diff options
author | lukeg <lukeg> | 2003-02-21 19:01:14 +0000 |
---|---|---|
committer | lukeg <lukeg> | 2003-02-21 19:01:14 +0000 |
commit | e7d48fe500f6ed676ee1b212ebd61408bced1c5b (patch) | |
tree | 11a756c7bb4906f3e186c1cb8331cb7ed27bc69c /src/edit_lib.erl | |
download | ermacs-fork-e7d48fe500f6ed676ee1b212ebd61408bced1c5b.tar.gz |
*** empty log message ***
Diffstat (limited to 'src/edit_lib.erl')
-rw-r--r-- | src/edit_lib.erl | 801 |
1 files changed, 801 insertions, 0 deletions
diff --git a/src/edit_lib.erl b/src/edit_lib.erl new file mode 100644 index 0000000..73372db --- /dev/null +++ b/src/edit_lib.erl @@ -0,0 +1,801 @@ +-module(edit_lib). +-author('tobbe@serc.rmit.edu.au'). +%%---------------------------------------------------------------------- +%% Created : 15 Jun 1998 by tobbe@serc.rmit.edu.au +%% Function: Core library routines for the Edit editor. +%%---------------------------------------------------------------------- +-vc('$Id$ '). + +-include_lib("ermacs/include/edit.hrl"). + +-compile(export_all). +-compile({parse_transform, edit_transform}). + +self_insert_command(S,Ch) when record(S,state) -> + insert_char(S,Ch). + +delete_char_backward(S) -> + B = buffer(S), + case edit_buf:mark_pos(B, point) of + 1 -> + S; + N -> + edit_buf:delete(B, N-1, N) + end. + +delete_char_forward(S) -> + B = buffer(S), + Max = edit_buf:point_max(B), + case edit_buf:mark_pos(B, point) of + N when N < Max -> + edit_buf:delete(B, N, N+1); + _ -> + true + end, + S. + +%% Note: delete_word, not kill_word. Should single words really go on +%% the kill ring, like they do in emacs? +delete_word_forward(S) -> + Buf = buffer(S), + Point = edit_buf:mark_pos(Buf, point), + P1 = find_char_forward(Buf, alphanumeric_P(), Point, Point), + P2 = find_char_forward(Buf, not_P(alphanumeric_P()), P1, P1), + edit_buf:delete(Buf, Point, P2). + +insert_char(S, Ch) -> + Buf = buffer(S), + edit_buf:insert(Buf, [Ch], edit_buf:mark_pos(Buf, point)). + +%% "open_line" is the emacs name for what C-o does +open_line(S) -> + Buf = buffer(S), + OrigPoint = edit_buf:mark_pos(Buf, point), + insert_char(S, $\n), + edit_buf:move_mark(Buf, point, OrigPoint). + +%% Abort - interpreted to abort any asynchronous command which is +%% borrowing the current buffer (or nothing if there isn't one). +abort(S) -> + case edit_buf:revoke((S#state.curwin)#window.buffer) of + false -> + S; + true -> + edit_util:status_msg(S, "Buffer revoked") + end. + +%% ----------------------------------- +%% Move cursor vertically down 1 line. + +next_line(S) when record(S,state) -> + Buf = buffer(S), + NewWindow = update_goal(S, Buf), + Goal = NewWindow#window.goal_column, + Max = edit_buf:point_max(Buf), + case end_of_line_pos(Buf) of + X when X == Max -> + ok; + X -> + F = fun(Ch) -> walk_goal(Ch, Goal, X+1) end, + NewP = edit_buf:walk_forward(Buf, F, X+1), + edit_buf:move_mark(Buf, point, NewP), + S#state{curwin=NewWindow} + end. + +previous_line(S) when record(S,state) -> + Buf = buffer(S), + NewWindow = update_goal(S, Buf), + Goal = NewWindow#window.goal_column, + case beginning_of_line_pos(Buf) of + 1 -> + move_to(Buf, 1); + X -> + From = beginning_of_line_pos(Buf, X-1), + F = fun(Ch) -> walk_goal(Ch, Goal, From) end, + NewP = edit_buf:walk_forward(Buf, + F, + beginning_of_line_pos(Buf, From)), + edit_buf:move_mark(Buf, point, NewP), + S#state{curwin=NewWindow} + end. + +update_goal(State, Buf) -> + Win = State#state.curwin, + case goal_preserving(State#state.lastcmd) of + true -> + Win; + false -> + Region = edit_buf:get_region(Buf, + point(Buf), + beginning_of_line_pos(Buf)), + Win#window{goal_column=goal_value(Region)} + end. + +goal_value(Region) -> + lists:foldl(fun($\t, Acc) -> Acc + 8; + (_, Acc) -> Acc + 1 + end, + 0, + Region). + +goal_preserving({?MODULE, next_line, []}) -> true; +goal_preserving({?MODULE, previous_line, []}) -> true; +goal_preserving(_) -> false. + +%% Go forward until we reach the goal column, or the end of file +walk_goal(C, Z, P) when Z =< 0 -> + {result, P}; +walk_goal(C, Remaining, P) when C == finish; + C == $\n -> + {result, P}; +walk_goal($\t, Remaining, P) when Remaining < 8 -> + {result, P}; +walk_goal($\t, Remaining, P) -> + {more, fun(C) -> walk_goal(C, Remaining-8, P+1) end}; +walk_goal(_, Remaining, P) -> + {more, fun(C) -> walk_goal(C, Remaining-1, P+1) end}. + +point(Buf) -> + edit_buf:mark_pos(Buf, point). + +%% ---------------------------- +%% Move point left 1 character. + +backward_char(S) when record(S,state) -> + Buf = buffer(S), + Pos = edit_buf:mark_pos(Buf, point) - 1, + case Pos < 1 of + true -> + S; + false -> + edit_buf:move_mark(Buf, point, Pos) + end. + +%% ----------------------------- +%% Move point right 1 character. +%% + +forward_char(S) when record(S,state) -> + Buf = buffer(S), + Pos = edit_buf:mark_pos(Buf, point) + 1, + Max = edit_buf:point_max(Buf), + case Pos > Max of + true -> + S; + false -> + edit_buf:move_mark(Buf, point, Pos) + end. + +%% ---------------------------------------- +%% Move point to beginning of current line. + +beginning_of_line(S) -> + Pos = beginning_of_line_pos(buffer(S)), + edit_buf:move_mark(buffer(S), point, Pos), + S. + +%% ugly function +backward_word(S) -> + Buf = buffer(S), + Point = edit_buf:mark_pos(Buf, point), + if Point == 1 -> + S; + true -> + X = find_char_backward(Buf, alphanumeric_P(), Point-1, 1), + if X == 1 -> + move_to(Buf, 1), + S; + true -> + NewP = 1 + find_char_backward(Buf, + not_P(alphanumeric_P()), + X, + 0), + move_to(Buf, NewP), + S + end + end. + +beginning_of_line_pos(Buf) -> + Pos = edit_buf:mark_pos(Buf, point), + beginning_of_line_pos(Buf, Pos). +beginning_of_line_pos(Buf, Pos) -> + %% We start before the point, incase we're on a newline + case find_char_backward(Buf, const_P($\n), Pos - 1) of + not_found -> 1; % point_min + N when integer(N) -> N + 1 % we want to be just after the newline + end. + +move_to_char_backward(Buf, Pred) -> + Pos = edit_buf:mark_pos(Buf, point), + edit_buf:move_mark(Buf, point, find_char_backward(Buf, Pred, Pos, 1)). + +%% Returns CharPos | not_found +find_char_backward(Buf, Pred) -> + find_char_backward(Buf, Pred, edit_buf:mark_pos(Buf, point)). +find_char_backward(Buf, Pred, Pos) -> + find_char_backward(Buf, Pred, Pos, not_found). +find_char_backward(Buf, Pred, Pos, Default) -> + case edit_buf:walk_backward(Buf, + fun(C) -> find_char_backward1(C,Pred,Pos) end, + Pos) of + not_found -> + Default; + P when integer(P) -> + P + end. + +%% This clause is a kludge; see edit_buf.erl for details +find_char_backward1(not_there, Pred, Pos) -> + {more, fun(New) -> find_char_backward1(New, Pred, Pos-1) end}; +find_char_backward1(finish, Pred, Pos) -> + {result, not_found}; +find_char_backward1(C, Pred, Pos) -> + %%edit_buf:log("Char: ~p~n", [C]), + case Pred(C) of + true -> + {result, Pos}; + false -> + {more, fun(New) -> find_char_backward1(New, Pred, Pos-1) end} + end. + +%% ---------------------------------- +%% Move point to end of current line. + +end_of_line(S) -> + move_to_char_forward(buffer(S), fun(C) -> C == $\n end), + S. + +forward_word(S) -> + move_to_char_forward(buffer(S), alphanumeric_P()), + move_to_char_forward(buffer(S), not_P(alphanumeric_P())), + S. + +end_of_line_pos(Buf) -> + Pos = edit_buf:mark_pos(Buf, point), + end_of_line_pos(Buf, Pos). +end_of_line_pos(Buf, Pos) -> + P = case find_char_forward(Buf, fun(C) -> C == $\n end, Pos) of + not_found -> + edit_buf:point_max(Buf); + N when integer(N) -> + N + end. + +move_to_char_forward(Buf, Pred) -> + Pos = edit_buf:mark_pos(Buf, point), + NewPos = find_char_forward(Buf, Pred, Pos, edit_buf:point_max(Buf)), + edit_buf:move_mark(Buf, point, NewPos). + +%% Returns CharPos | not_found +find_char_forward(Buf, Pred, Pos) -> + find_char_forward(Buf, Pred, Pos, not_found). +%% Returns CharPos | Default (if not found) +find_char_forward(Buf, Pred, Pos, Default) -> + case edit_buf:walk_forward(Buf, + fun(C) -> find_char_forward1(C, Pred, Pos) end, + Pos) of + not_found -> + Default; + P when integer(P) -> + P + end. + +find_char_forward1(finish, Pred, Pos) -> + {result, not_found}; +find_char_forward1(C, Pred, Pos) -> + case Pred(C) of + true -> + {result, Pos}; + false -> + {more, fun(New) -> find_char_forward1(New, Pred, Pos+1) end} + end. + +recenter(State) -> + Buf = buffer(State), + Win = State#state.curwin, + Lines = trunc(Win#window.height/2), + Point = edit_buf:mark_pos(Buf, point), + NewDStart = case find_nth(Buf, backward, Point, $\n, Lines) of + not_found -> + 1; + Pos -> + Pos + 1 + end, + edit_buf:move_mark(Buf, Win#window.start_mark, NewDStart), + ?EDIT_TERMINAL:invalidate(), + State. + +%% These scrolling functions are ugly + +scroll_up(State) -> + Win = State#state.curwin, + Height = edit_window:text_lines(Win), + Buf = buffer(State), + DStart = edit_buf:mark_pos(Buf, Win#window.start_mark), + NewDStart = + case find_nth(Buf, backward, DStart, $\n, Height - 1) of + not_found -> + 1; + X when integer(X) -> + X + 1 + end, + DEnd = case find_nth(Buf, forward, NewDStart, $\n, Height) of + not_found -> + edit_buf:point_max(Buf); + N -> + N + end, + Point = edit_buf:mark_pos(Buf, point), + if Point > DEnd -> + move_to(Buf, beginning_of_line_pos(Buf, DEnd)); + true -> + ok + end, + edit_buf:move_mark(Buf, Win#window.start_mark, NewDStart), + State. + +scroll_down(State) -> + Win = State#state.curwin, + Buf = buffer(State), + DStart = edit_buf:mark_pos(Buf, Win#window.start_mark), + PMax = edit_buf:point_max(Buf), + case find_nth(Buf, forward, DStart, $\n, edit_window:text_lines(Win)-2) of + X when integer(X), + X < PMax -> + %% We want to be just after the newline - i.e. start of next line + Pos = X + 1, + Point = edit_buf:mark_pos(Buf, point), + if Point < Pos -> + %% Scrolled the point off the screen, move the + %% point to the old display start (up near the + %% top). + edit_buf:move_mark(Buf, point, Pos); + true -> + ok + end, + edit_buf:move_mark(Buf, Win#window.start_mark, Pos), + State; + _ -> + State + end. + +scroll_down_wrap(Win) -> + Buf = Win#window.buffer, + DStart = edit_buf:mark_pos(Buf, Win#window.start_mark), + PMax = edit_buf:point_max(Buf), + Lines = edit_window:text_lines(Win), + Pos = case find_nth(Buf, forward, DStart, $\n, Lines) of + X when integer(X), + X < PMax -> + %% We want to be just after the newline - i.e. start + %% of next line + X + 1; + _ -> + 1 + end, + edit_buf:move_mark(Buf, Win#window.start_mark, Pos), + edit_buf:move_mark(Buf, point, Pos), + Win. + +start_of_buffer(State) -> + B = buffer(State), + edit_buf:move_mark(B, mark, edit_buf:mark_pos(B, point)), + edit_buf:move_mark(B, point, 1), + State. + +end_of_buffer(State) -> + B = buffer(State), + edit_buf:move_mark(B, mark, edit_buf:mark_pos(B, point)), + edit_buf:move_mark(B, point, edit_buf:point_max(B)), + State. + +mark_whole_buffer(State) -> + Buf = buffer(State), + edit_buf:move_mark(Buf, point, 1), + edit_buf:move_mark(Buf, mark, edit_buf:point_max(Buf)), + State. + +find_nth(Buf, Direction, Pos, Char, N) -> + case edit_buf:walk(Buf, Direction, find_nth_fun(Char, N), Pos) of + not_found -> + not_found; + Offset when Direction == forward -> + Pos + Offset; + Offset when Direction == backward -> + Pos - Offset + end. + +find_nth_fun(Char, N) -> find_nth_fun(Char, N, 0). +find_nth_fun(Char, N, Offset) -> find_nth_fun(Char, N, Offset, not_found). + +find_nth_fun(Char, N, Offset, Default) -> + fun(X) when X == Char -> + case N of + 1 -> + {result, Offset}; + _ -> + {more, find_nth_fun(Char, N-1, Offset+1, Default)} + end; + (finish) -> + {result, Default}; + (_) -> + {more, find_nth_fun(Char, N, Offset+1, Default)} + end. + +set_mark(State) -> + Buf = buffer(State), + Point = edit_buf:mark_pos(Buf, point), + edit_buf:move_mark(Buf, mark, Point), + State. + +exchange_point_and_mark(State) -> + Buf = buffer(State), + Point = edit_buf:mark_pos(Buf, point), + Mark = edit_buf:mark_pos(Buf, mark), + edit_buf:move_mark(Buf, point, Mark), + edit_buf:move_mark(Buf, mark, Point), + State. + +kill_region(State, KillFlag) -> + Buf = buffer(State), + Point = edit_buf:mark_pos(Buf, point), + Mark = edit_buf:mark_pos(Buf, mark), + Text = edit_buf:get_region_cord(Buf, Point, Mark), + State1 = case KillFlag of + true -> + edit_buf:delete(Buf, Point, Mark), + State; + false -> + edit_util:status_msg(State, "Copied") + end, + KR = edit_var:lookup(killring), + edit_var:set(killring, [Text|KR]). +%%State1#state{killring=[Text|State#state.killring]}. + +kill_line(State) -> + Buf = buffer(State), + P = edit_buf:mark_pos(Buf, point), + EOL = end_of_line_pos(Buf), + End = if P == EOL -> + min(P + 1, edit_buf:point_max(Buf)); + true -> + EOL + end, + Text = edit_buf:get_region_cord(Buf, P, End), + edit_buf:delete(Buf, P, End), + %%OldKillring = State#state.killring, + OldKillring = edit_var:lookup(killring), + NewKillring = case State#state.lastcmd of + %% Multiple kill_line's accumulate at the front + %% of the kill ring + {?MODULE, kill_line, _} -> + [cord:join(hd(OldKillring), Text) | + tl(OldKillring)]; + _ -> + [Text|OldKillring] + end, + edit_var:set(killring, NewKillring). + %%State#state{killring=NewKillring}. + +yank(State) -> + case edit_var:lookup(killring) of + [] -> + State; + [Text|_] -> + Buf = buffer(State), + Point = edit_buf:mark_pos(Buf, point), + edit_buf:insert(Buf, Text, Point) + end. + +undo(State) -> + Continuing = case State#state.lastcmd of + {?MODULE, undo, []} -> + true; + _ -> + false + end, + edit_buf:undo(buffer(State), Continuing). + +move_to(Buf, Pos) -> + edit_buf:move_mark(Buf, point, Pos). + +next_window(State) when State#state.windows == [] -> + State; +next_window(State) -> + next_window1(State). + +next_window1(State) -> + [Next | Rest] = State#state.windows, + NewState = State#state{curwin=Next, + windows = Rest ++ [State#state.curwin]}, + if (NewState#state.curwin)#window.active == false -> + next_window1(NewState); + true -> + NewState + end. + +split_window_vertically(State) -> + case edit_window:physical_lines(State#state.curwin) of + N when N < 4 -> % too small to split + State; + N -> + split_window_vertically1(State) + end. + +split_window_vertically1(State) -> + Orig = State#state.curwin, + OrigHeight = edit_window:physical_lines(Orig), + TopHeight = trunc(OrigHeight / 2), + BottomHeight = OrigHeight - TopHeight, + BottomY = Orig#window.y + TopHeight, + BottomW = edit_window:make_window(buffer(State), + BottomY, + Orig#window.width, + BottomHeight), + State#state{curwin=Orig#window{height=TopHeight}, + windows=[BottomW | State#state.windows]}. + +delete_other_windows(State) + when (State#state.curwin)#window.minibuffer == true -> + edit_util:status_msg(State, "Minibuffer can't expand to full frame"); +delete_other_windows(State) -> + Others = lists:filter(fun(W) -> not W#window.minibuffer end, + State#state.windows), + ExtraSpace = lists:foldl(fun(W, Sum) -> Sum + W#window.height end, + 0, + Others), + [Minibuffer] = State#state.windows -- Others, + OldWin = State#state.curwin, + NewWin = OldWin#window{y=0, height = OldWin#window.height + ExtraSpace}, + State#state{curwin=NewWin, + windows=[Minibuffer]}. + +delete_window(State) when (State#state.curwin)#window.minibuffer == true -> + State; +delete_window(State) when length(State#state.windows) =< 1 -> + State; +delete_window(State) -> + Cur = State#state.curwin, + Wins = State#state.windows, + [Prev | OthersReversed] = lists:reverse(Wins), + Others = lists:reverse(OthersReversed), + io:format("Prev.y = ~p~nCur.y = ~p~n", [Prev#window.y, Cur#window.y]), + if Prev#window.y > Cur#window.y -> + %% Topmost window is being deleted. In this special case, + %% its size is allocated to the next (rather than + %% previous, i.e. bottom) window. + Kin = hd(Wins), + Kin1 = Kin#window{height=Kin#window.height + Cur#window.height, + y=Cur#window.y}, + State#state{curwin=Kin1, + windows=tl(Wins)}; + true -> + Kin = Prev, + Kin1 = Kin#window{height=Kin#window.height + Cur#window.height}, + Others1 = Others ++ [Kin1], + case Others1 of + [Next = #window{minibuffer=false} | Rest] -> + State#state{curwin=Next, + windows=Rest}; + [MB = #window{minibuffer=true}, Next | Rest] -> + State#state{curwin=Next, + windows=(Rest ++ [MB])} + end + end. + +-command({quit, [], "Exit the editor process"}). +quit(State) -> + ?EDIT_TERMINAL:teardown(), + halt(). + +-command({printf, [{string, "String:"}], + "Print a string to standard output (the file edit.out)"}). + +printf(State, String) -> + io:format("*** PRINTF: ~s~n", [String]), + State. + +-command({switch_to_buffer, [{string, "Switch to buffer:"}]}). + +switch_to_buffer(State, "") -> + VisibleBuffers = [W#window.buffer || W <- edit_util:windows(State)], + case State#state.buffers -- VisibleBuffers of + [] -> + State; + [Next|_] -> + switch_to_buffer(State, atom_to_list(Next)) + end; +switch_to_buffer(State, Name) -> + Buffer = list_to_atom(Name), + case whereis(Buffer) of % FIXME: how do I know its a buffer? + undefined -> + edit_util:status_msg(State, "No such buffer: ~s", [Name]); + Pid -> + Buffers = State#state.buffers, + NewBuffers = [Buffer | (Buffers -- [Buffer])], + Win = edit_window:attach(State#state.curwin, Buffer), + State#state{curwin=Win, buffers=NewBuffers} + end. + +-command({kill_buffer, [{string, "Kill buffer:"}]}). + +kill_buffer(State, "") -> + kill_buffer(State, atom_to_list((State#state.curwin)#window.buffer)); +kill_buffer(State, Name) -> + BufferName = list_to_atom(Name), + case State#state.buffers of + [OnlyBuffer] -> + State; + Buffers -> + case whereis(BufferName) of + Pid when pid(Pid) -> + edit_buf:kill(BufferName), + NewBuffers = Buffers -- [BufferName], + F = fun(Win) -> + case Win#window.buffer of + BufferName -> + edit_window:attach(Win, + hd(NewBuffers)); + _ -> + Win + end + end, + State1 = edit_util:window_map(State, F), + io:format("Killing ~p~n", [BufferName]), + State1#state{buffers=NewBuffers}; + undefined -> + edit_util:status_msg(State, "No such buffer: ~s", [Name]) + end + end. + +min(X,Y) when X<Y -> X; +min(_,Y) -> Y. + +max(X,Y) when X>Y -> X; +max(_,Y) -> Y. + +%% Get the buffer from state - blocks if it's being borrowed by someone else. +buffer(State) -> + Buf = (State#state.curwin)#window.buffer, + edit_buf:wait_available(Buf), + Buf. + +find(Pred, []) -> + not_found; +find(Pred, [H|T]) -> + case Pred(H) of + true -> + H; + false -> + find(Pred, T) + end. + +%% FIXME: Expects the *end* of the string to be before the point +find_string_backward(Buf, String) -> + find_string_backward(Buf, String, edit_buf:mark_pos(Buf, point)). +find_string_backward(Buf, String, Point) -> + find_string_backward(Buf, String, Point, not_found). +find_string_backward(Buf, String, Point, Default) -> + find_string_backward1(Buf, String, Point, Default). + +find_string_backward1(Buf, String, Point, Default) -> + PointMax = edit_buf:point_max(Buf), + P = fun(C) -> C == hd(String) end, + case min(Point, PointMax - length(String)) of + Start when Start =< 0 -> + Default; + Start -> + case find_char_backward(Buf, P, Start) of + not_found -> + Default; + Pos -> + case edit_buf:get_region(Buf, Pos, Pos + length(String)) of + String -> + Pos; + _Other when Pos == 1 -> + Default; + _Other -> + find_string_backward1(Buf, String, Pos-1, Default) + end + end + end. + +%% Predicate functions on characters. +%% Functions ending in _P return predicate fun's. +whitespace_P() -> fun(C) -> lists:member(C, "\n\t ") end. +alphabet_P() -> fun(C) when C >= $A, C =< $Z -> true; + (C) when C >= $a, C =< $z -> true; + (_) -> false + end. +numeric_P() -> fun(C) -> (C >= $0) and (C =< $9) end. +not_whitespace_P() -> not_P(whitespace_P()). + +either_P(A, B) -> fun(C) -> A(C) or B(C) end. +const_P(Char) -> fun(C) -> C == Char end. +not_P(Pred) -> fun(C) -> not Pred(C) end. + +alphanumeric_P() -> either_P(alphabet_P(), numeric_P()). + +fundamental_mode(State) -> + Buf = buffer(State), + edit_buf:set_mode(Buf, fundamental_mode_rec()). + +fundamental_mode_rec() -> + #mode{name="Fundamental", + id=fundamental, + keymaps=[]}. + +-command({unix_command, [{shell_command, "Shell command:"}]}). + +unix_command(State, Cmd) -> + %% FIXME: This looks suspiciously system dependent. + %% Infact running the command as a port would be much nicer + Text = os:cmd("sh -c '(cd "++edit_util:pwd(State)++" ; "++Cmd++")'"), + edit_util:popup_message(State, '*Shell Command Output*', Text). + +lines(Text) -> + length(lists:filter(fun(C) -> C == $\n end, Text)). + +-command({search, [{search_string, "Search:"}]}). + +search(State, Dir, "") -> + Str = edit_var:lookup(prev_search_string, ""), + regexp_search1(State, Dir, cord_regexp:escape(Str)); +search(State, Dir, Str) -> + edit_var:set(prev_search_string, Str), + regexp_search1(State, Dir, cord_regexp:escape(Str)). + +-command({regexp_search, [{search_regexp, "Regexp:"}]}). + +regexp_search(State, Dir, "") -> + %% Blank search string - reuse previous + RE = edit_var:lookup(prev_search_regexp, ""), + regexp_search1(State, Dir, RE); +regexp_search(State, Dir, RE) -> + edit_var:set(prev_search_regexp, RE), + regexp_search1(State, Dir, RE). + +regexp_search1(State, Direction, RE) -> + B = buffer(State), + P = edit_buf:mark_pos(B, point), + case edit_buf:regexp_search(B, RE, clip(B, P), Direction) of + nomatch -> + edit_util:status_msg(State, "Not found: " ++ RE); + {match, Start, Len} -> + NewPos = case Direction of + forward -> + Start + Len; + backward -> + Start - (Len - 1) + end, + ?debug("Start = ~p Len = ~p~n", [Start, Len]), + mark_to_point(B), + edit_buf:move_mark(B, point, NewPos) + end. + +%% Returns: Point' < point_max +clip(Buf, Point) -> + max(1, min(Point, edit_buf:point_max(Buf) - 1)). + +mark_to_point(Buf) -> + edit_buf:move_mark(Buf, mark, edit_buf:mark_pos(Buf, point)). + +%% Null command. Invoked by async processes to get the editor to +%% redraw. +nop(State) -> + State. + +%% ---------------------------------------------------------------------- +%% Debug info + +buffer_cord_info(State) -> + B = buffer(State), + C = edit_buf:get_cord(B), + Fmt = "Byte size: ~p ; Max depth: ~p ; Mean leaf size: ~p ; # nodes: ~p", + edit_util:status_msg(State, + Fmt, + [cord:cord_size(C), + cord:max_depth(C), + cord:mean_leaf_size(C), + cord:nr_nodes(C)]). + |