aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/edit_buf.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/edit_buf.erl')
-rw-r--r--src/edit_buf.erl320
1 files changed, 320 insertions, 0 deletions
diff --git a/src/edit_buf.erl b/src/edit_buf.erl
new file mode 100644
index 0000000..7d89741
--- /dev/null
+++ b/src/edit_buf.erl
@@ -0,0 +1,320 @@
+%%%----------------------------------------------------------------------
+%%% File : edit_buf.erl
+%%% Author : Luke Gorrie <luke@bluetail.com>
+%%% Purpose : Buffer process
+%%% Created : 14 Sep 2000 by Luke Gorrie <luke@bluetail.com>
+%%%----------------------------------------------------------------------
+
+-module(edit_buf).
+-author('luke@bluetail.com').
+
+-compile(export_all).
+%%-export([Function/Arity, ...]).
+
+-record(state, {name,
+ filename, % optional
+ text, % text()
+ mode,
+ borrower=nobody % for a lock pid()
+ }).
+
+%% ----------------------------------------------------------------------
+%% API
+%% ----------------------------------------------------------------------
+
+new(Name) ->
+ case start_link(Name) of
+ {ok, B} ->
+ add_mark(Name, point, 1, forward),
+ add_mark(Name, mark, 1, backward),
+ {ok, Name};
+ Err ->
+ Err
+ end.
+
+start_link(Name) when atom(Name) ->
+ case whereis(Name) of
+ undefined ->
+ Pid = proc_lib:spawn_link(?MODULE, init, [Name]),
+ register(Name, Pid),
+ {ok, Pid};
+ Pid ->
+ {error, {already_started, Pid}}
+ end.
+
+kill(Buf) ->
+ call(Buf, kill).
+
+set_filename(Buf, Filename) ->
+ call(Buf, {set_filename, Filename}).
+
+get_filename(Buf) ->
+ call(Buf, get_filename).
+
+set_mode(Buf, Mode) ->
+ call(Buf, {set_mode, Mode}).
+
+get_mode(Buf) ->
+ call(Buf, get_mode).
+
+get_text(Buf) ->
+ call(Buf, get_text).
+
+set_text(Buf, Text) ->
+ call(Buf, {set_text, Text}).
+
+get_cord(Buf) ->
+ call(Buf, get_cord).
+
+get_size(Buf) ->
+ call(Buf, get_size).
+
+replace(Buf, New, Start, End) when Start > End ->
+ replace(Buf, New, End, Start);
+replace(Buf, New, Start, End) ->
+ call(Buf, {replace, New, Start, End}).
+
+get_region(Buf, Start, End) ->
+ cord:to_list(get_region_cord(Buf, Start, End)).
+
+get_region_cord(Buf, Start, End) when Start > End ->
+ get_region_cord(Buf, End, Start);
+get_region_cord(Buf, Start, End) ->
+ call(Buf, {get_region_cord, Start, End}).
+
+undo(Buf, Continuing) ->
+ call(Buf, {undo, Continuing}).
+
+delete(Buf, Start, End) ->
+ replace(Buf, "", Start, End).
+
+insert(Buf, New, Position) ->
+ replace(Buf, New, Position, Position).
+
+add_mark(Buf, Name, Pos, Direction) ->
+ call(Buf, {add_mark, Name, Pos, Direction}).
+
+move_mark(Buf, Name, Pos) ->
+ call(Buf, {move_mark, Name, Pos}).
+
+mark_pos(Buf, Name) ->
+ call(Buf, {mark_pos, Name}).
+
+point_max(Buf) ->
+ call(Buf, point_max).
+
+point_min(Buf) ->
+ 1.
+
+walk_backward(Buf, Fun, Pos) ->
+ call(Buf, {walk_backward, Fun, Pos}).
+
+walk_forward(Buf, Fun, Pos) ->
+ call(Buf, {walk_forward, Fun, Pos}).
+
+walk(Buf, Direction, Fun, Pos) ->
+ call(Buf, {walk, Direction, Fun, Pos}).
+
+wait_available(Buf) ->
+ call(Buf, wait_available).
+
+borrow(Buf) ->
+ call(Buf, {borrow, self()}).
+
+return(Buf) ->
+ call(Buf, {return, self()}).
+
+revoke(Buf) ->
+ call(Buf, revoke).
+
+regexp_search(Buf, RE, Pos, Direction) ->
+ call(Buf, {regexp_search, RE, Pos, Direction}).
+
+%% Asynchronous locking
+
+%% Returns: ref()
+async_borrow(Buf) ->
+ Ref = make_ref(),
+ cast(Buf, {borrow, self()}).
+
+async_return(Buf) ->
+ cast(Buf, {return, self()}).
+
+%% Internals
+
+call(Buf, Msg) ->
+ Buf ! {call, self(), Msg},
+ receive {reply, R} -> R end.
+
+cast(Buf, Msg) ->
+ Buf ! {cast, Msg},
+ ok.
+
+init(Name) ->
+ {'EXIT', Reason} = (loop(#state{name=Name,
+ mode=edit_lib:fundamental_mode_rec(),
+ text=edit_text:new()})),
+ io:format("Buffer ~p crashed: ~p~n", [Name, Reason]),
+ exit(Reason).
+
+loop(State) ->
+ receive
+ %% get_text
+ {call, From, get_text} ->
+ From ! {reply, cord:to_list(edit_text:cord(State#state.text))},
+ edit_buf:loop(State);
+ %% set_text
+ {call, From, {set_text, Text}} ->
+ Cord = edit_text:cord(State#state.text),
+ NewCmd = {replace, Text, 1, cord:cord_size(Cord) + 1},
+ self() ! {call, From, NewCmd},
+ edit_buf:loop(State);
+ %% get_cord
+ {call, From, get_cord} ->
+ From ! {reply, edit_text:cord(State#state.text)},
+ edit_buf:loop(State);
+ %% get_size
+ {call, From, get_size} ->
+ From ! {reply, cord:cord_size(edit_text:cord(State#state.text))},
+ edit_buf:loop(State);
+ %% set_filename
+ {call, From, {set_filename, Filename}} ->
+ NewState = State#state{filename=Filename},
+ From ! {reply, ok},
+ edit_buf:loop(NewState);
+ %% get_filename
+ {call, From, get_filename} ->
+ From ! {reply, State#state.filename},
+ edit_buf:loop(State);
+ %% set_mode
+ {call, From, {set_mode, Mode}} ->
+ From ! {reply, ok},
+ edit_buf:loop(State#state{mode=Mode});
+ %% get_mode
+ {call, From, get_mode} ->
+ From ! {reply, State#state.mode},
+ edit_buf:loop(State);
+ %% replace
+ {call, From, {replace, Text, Start, End}} ->
+ NewText = edit_text:replace(State#state.text,Text,Start,End-Start),
+ From ! {reply, ok},
+ edit_buf:loop(State#state{text=NewText});
+
+% NewText = cord:replace(State#state.text, Text, Start, End-Start),
+% Marks1 = update_marks(State#state.marks, Text, Start, End),
+% State2 = State#state{text=NewText, marks=Marks1},
+% Undo = [{State#state.text, State#state.marks}|State2#state.undo],
+% State3 = State2#state{undo=Undo},
+% From ! {reply, ok},
+% edit_buf:loop(State3);
+ %% undo
+ {call, From, {undo, Continuing}} ->
+ NewText = edit_text:undo(State#state.text, Continuing),
+ From ! {reply, ok},
+ edit_buf:loop(State#state{text=NewText});
+
+% NewState = do_undo(State, Continuing),
+% From ! {reply, ok},
+% edit_buf:loop(NewState);
+ %% get_region
+ {call, From, {get_region_cord, Start, End}} ->
+ Cord = edit_text:cord(State#state.text),
+ Text = cord:region(Cord, Start, End-Start),
+ From ! {reply, Text},
+ edit_buf:loop(State);
+ %% add_mark
+ {call, From, {add_mark, Name, Pos, Direction}} ->
+ NewText = edit_text:add_mark(State#state.text,Name,Pos,Direction),
+ From ! {reply, ok},
+ edit_buf:loop(State#state{text=NewText});
+ %% move_mark
+ {call, From, {move_mark, Name, Pos}} ->
+ NewText = edit_text:move_mark(State#state.text, Name, Pos),
+ From ! {reply, ok},
+ edit_buf:loop(State#state{text=NewText});
+ %% mark_pos
+ {call, From, {mark_pos, Name}} ->
+ From ! {reply, edit_text:mark_pos(State#state.text, Name)},
+ edit_buf:loop(State);
+ %% point_max
+ {call, From, point_max} ->
+ Cord = edit_text:cord(State#state.text),
+ From ! {reply, cord:cord_size(Cord) + 1},
+ edit_buf:loop(State);
+ %% walk_backward
+ {call, From, {walk_backward, Fun, Pos}} ->
+ From ! {reply, edit_text:walk_backward(State#state.text,Fun,Pos)},
+ edit_buf:loop(State);
+ %% walk_forward
+ {call, From, {walk_forward, Fun, Pos}} ->
+ From ! {reply, edit_text:walk_forward(State#state.text,Fun,Pos)},
+ edit_buf:loop(State);
+ %% walk
+ {call, From, {walk, Direction, Fun, Pos}} ->
+ Result = case Direction of
+ forward ->
+ edit_text:walk_forward(State#state.text,Fun,Pos);
+ backward ->
+ edit_text:walk_backward(State#state.text,Fun,Pos)
+ end,
+ From ! {reply, Result},
+ edit_buf:loop(State);
+ %% wait_available
+ {call, From, wait_available} when State#state.borrower == nobody ->
+ From ! {reply, true},
+ edit_buf:loop(State);
+ %% borrow
+ {call, From, {borrow, Borrower}} when State#state.borrower == nobody ->
+ From ! {reply, true},
+ io:format("Borrowing buffer for ~p at ~s~n", [Borrower,
+ os:cmd("date")]),
+ %% monitor the borrower so we know when to release
+ erlang:monitor(process, Borrower),
+ edit_buf:loop(State#state{borrower=Borrower});
+ %% return
+ {call, From, {return, Who}} when Who == State#state.borrower ->
+ edit_buf:loop(State#state{borrower=nobody});
+ {'DOWN', _, process, Who, _} when Who == State#state.borrower ->
+ edit_buf:loop(State#state{borrower=nobody});
+ %% revoke
+ {call, From, revoke} ->
+ case State#state.borrower of
+ nobody ->
+ From ! {reply, false},
+ true;
+ Who ->
+ From ! {reply, true},
+ exit(Who, buffer_revoked)
+ end,
+ edit_buf:loop(State#state{borrower=nobody});
+ %% regexp_search
+ {call, From, {regexp_search, RE, Pos, Direction}} ->
+ Cord = edit_text:cord(State#state.text),
+ From ! {reply, cord_regexp:first_match(RE, Cord, Pos, Direction)},
+ edit_buf:loop(State);
+ %% kill
+ {call, From, kill} ->
+ From ! {reply, ok};
+ %% ---------------------------------------------------------------
+ %% Casts
+
+ %% {borrow, Who} --> {loan, Buffer}
+ {cast, {borrow, Who}} when State#state.borrower == nobody ->
+ Who ! {loan, State#state.name},
+ erlang:monitor(process, Who),
+ loop(State#state{borrower=Who});
+ %% {return, ByWho} --> void
+ {cast, {return, Who}} when Who == State#state.borrower ->
+ loop(State#state{borrower=nobody})
+ end.
+
+%% Debug
+
+log(FmtString, Args) ->
+ log(io_lib:format(FmtString, Args)).
+
+log(String) ->
+ {ok, Fd} = file:open("edit_buf.log", [append, raw]),
+ file:write(Fd, String),
+ file:close(Fd).
+