aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/edit_history.erl
blob: eaf89358d05c07b0ba9d69b36d1221ea572960e4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
-module(edit_history).

-include("edit.hrl").
-compile({parse_transform, edit_transform}).
-import(edit_lib, [buffer/1]).

-define(HISTORY_MAX_LENGTH, 500).

%% API
-export([bindings/2, add/2]).

%% Commands
-export([move/4, search/4]).

%% BaseVar = atom() name of edit_var variable to put history
%% RegionFn = fun(Buffer) -> {Start, End} of history region
%%
%% Returns a set of (standard) keybindings.
bindings(BaseVar, RegionFn) ->
    [{"M-p", {?MODULE, move,   [BaseVar, RegionFn, up]}},
     {"M-n", {?MODULE, move,   [BaseVar, RegionFn, down]}},
     {"M-r", {?MODULE, search, [BaseVar, RegionFn]}}
    ].

add(BaseVar, "") ->
    %% No blanks in history.
    skipped;
add(BaseVar, Item) ->
    Hist = edit_var:lookup(BaseVar, []),
    if
	hd(Hist) == Item ->
	    %% duplicate, leave history alone
	    ok;
	true ->
	    edit_var:set(BaseVar, trim_history([Item|Hist]))
    end.

move(State, BaseVar, RegionFn, Direction) ->
    IdxVar = index_var(BaseVar),
    HVar = list_var(BaseVar),
    History = ["" | edit_var:lookup(HVar, [])],
    Idx = case continuing_p(State) of
	      true ->
		  Prev = edit_var:lookup(IdxVar, 1),
		  case Direction of
		      up ->
			  Prev + 1;
		      down ->
			  Prev - 1
		  end;
	      false ->
		  2			% 1 is a new empty string
	  end,
    if
	Idx >= 1, length(History) >= Idx ->
	    Buf = buffer(State),
	    New = lists:nth(Idx, History),
	    kill_old(Buf, RegionFn),
	    edit_buf:insert(Buf, New, edit_buf:mark_pos(Buf, point)),
	    edit_var:set(IdxVar, Idx),
	    State;
	true ->
	    edit_util:status_msg(State, "No more history")
    end.

-command({search, [{string, "History regexp:"}]}).
search(State, BaseVar, RegionFn, Regexp) ->
    History = edit_var:lookup(BaseVar, []),
    case find_match(History, Regexp) of
	{match, Cmd} ->
	    Buf = buffer(State),
	    kill_old(Buf, RegionFn),
	    edit_buf:insert(Buf, Cmd, edit_buf:mark_pos(Buf, point)),
	    State;
	{error, {ErrString, Position}} ->
	    edit_util:status_msg(State, "Regex Error at " ++ integer_to_list(Position) ++ ": " ++ ErrString);
	nomatch ->
	    edit_util:status_msg(State, "Not found")
    end.

continuing_p(State) ->
    case State#state.lastcmd of
	{?MODULE, move, _} ->
	    true;
	{?MODULE, search, _} ->
	    true;
	{_, history_move, _} ->
	    true;
	{_, history_search, _} ->
	    true;
	_ ->
	    false
    end.

kill_old(Buf, RegionFn) ->
    {Start, End} = RegionFn(Buf),
    edit_buf:delete(Buf, Start, End).

trim_history(Hist) when length(Hist) > ?HISTORY_MAX_LENGTH ->
    lists:sublist(Hist, ?HISTORY_MAX_LENGTH);
trim_history(Hist) ->
    Hist.

find_match(Strings, Regexp) ->
    case re:compile(Regexp) of
        {ok, MP} ->
            find_match1(Strings, MP);
        {error, Error} ->
            {error, Error}
    end.

find_match1([], _Regexp) ->
    nomatch;
find_match1([H|T], Regexp) ->
    case re:run(H, Regexp) of
        nomatch ->
            find_match(T, Regexp);
        match ->
            {match, H}
    end.

list_var(Base)  -> Base.
index_var(Base) -> list_to_atom(atom_to_list(Base) ++ "_idx").