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").
|