2 * Copyright Neil Brown ©2016-2020 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
7 * A history pane supports selection of lines from a separate
8 * document. The underlying document is assumed to be one line
9 * and this line can be replaced by various lines from the history document.
10 * When a line is replaced, if it had been modified, it is saved first.
11 * :M-p - replace current line with previous line from history, if there is one
12 * :M-n - replace current line with next line from history. If none, restore
14 * :M-r - incremental search - later
15 * When a selection is committed, it is added to end of history.
31 struct lookup_cmd handle;
34 static struct map *history_map;
35 DEF_LOOKUP_CMD(history_handle, history_map);
37 DEF_CMD(history_close)
39 struct history_info *hi = ci->home->data;
42 pane_close(hi->history);
48 struct history_info *hi = ci->home->data;
53 /* handle was in 'hi' */
54 ci->home->handle = NULL;
58 DEF_CMD(history_notify_close)
60 struct history_info *hi = ci->home->data;
62 if (ci->focus == hi->history)
63 /* The history document is going away!!! */
70 struct history_info *hi = ci->home->data;
72 const char *line = ci->str;
75 if (!hi->history || !ci->str)
76 /* history document was destroyed */
78 /* Must never include a newline in a history entry! */
79 eol = strchr(ci->str, '\n');
81 line = strnsave(ci->home, ci->str, eol - ci->str);
83 prev = call_ret(strsave, "history:get-last", ci->focus);
84 if (prev && line && strcmp(prev, line) == 0)
87 call("Move-File", hi->history, 1);
88 call("Replace", hi->history, 1, NULL, line);
89 call("Replace", hi->history, 1, NULL, "\n", 1);
95 history_save_func(ci);
100 DEF_CMD(history_notify_replace)
102 struct history_info *hi = ci->home->data;
109 DEF_CMD(history_move)
111 struct history_info *hi = ci->home->data;
114 const char *suffix = ksuffix(ci, "K:M-");
116 if (!hi->history || !ci->mark)
118 if (*suffix == 'p') {
119 m = mark_at_point(hi->history, NULL, MARK_UNGROUPED);
120 call("Move-EOL", hi->history, -2);
122 call("Move-EOL", hi->history, 1);
123 call("Move-Char", hi->history, 1);
124 m = mark_at_point(hi->history, NULL, MARK_UNGROUPED);
125 call("Move-EOL", hi->history, 1, m);
126 call("Move-Char", hi->history, 1, m);
128 l = call_ret(str, "doc:get-str", hi->history, 0, NULL, NULL, 0, m);
130 /* No more history */
132 if (*suffix == 'p') {
143 call("Move-EOL", ci->focus, -1, ci->mark);
144 m = mark_dup(ci->mark);
145 call("Move-EOL", ci->focus, 1, m);
149 hi->saved = call_ret(str, "doc:get-str", ci->focus,
153 call("Replace", ci->focus, 1, m, l);
162 DEF_CMD(history_attach)
164 struct history_info *hi;
167 if (!ci->str || !ci->str2)
171 hi->done_map = key_alloc();
172 hi->handle = history_handle;
173 hi->handle.m = &hi->done_map;
174 key_add_chain(hi->done_map, history_map);
175 key_add(hi->done_map, ci->str2, &history_done);
176 p = call_ret(pane, "docs:byname", ci->focus, 0, NULL, ci->str);
178 p = call_ret(pane, "doc:from-text", ci->focus, 0, NULL, ci->str);
184 hi->history = call_ret(pane, "doc:attach-view", p, -1, NULL, "invisible");
187 call("Move-File", hi->history, 1);
188 buf_init(&hi->search);
189 p = pane_register(ci->focus, 0, &hi->handle.c, hi);
192 pane_add_notify(p, hi->history, "Notify:Close");
193 call("doc:request:doc:replaced", p);
194 return comm_call(ci->comm2, "callback:attach", p);
197 DEF_CMD(history_hlast)
199 struct history_info *hi = ci->home->data;
200 struct pane *doc = hi->history;
207 m = vmark_new(doc, MARK_UNGROUPED, NULL);
210 call("doc:set-ref", doc, 0, m);
211 call("doc:set", doc, 0, m, NULL, 1);
214 while (doc_prior(doc, m) != '\n')
215 if (doc_prev(doc,m) == WEOF)
217 rv = call_comm("doc:get-str", doc, ci->comm2, 0, m, NULL, 0, m2);
223 static bool has_name(struct pane *doc safe, struct mark *m safe,
224 const char *name safe)
228 a = call_ret(strsave, "doc:get-attr", doc, 0, m, "history:name");
229 return a && strcmp(a, name) == 0;
232 DEF_CMD(history_last)
234 /* Get last line from the given history document
235 * If ci->num > 1 get nth last line
236 * else if ci->str, get the line with given name
237 * If both set, assign str to the nth last line
238 * Names are assign with attribute "history:name"
243 const char *name = ci->str2;
246 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL, ci->str);
249 m = vmark_new(doc, MARK_UNGROUPED, NULL);
252 call("doc:set-ref", doc, 0, m);
253 call("doc:set", doc, 0, m, NULL, 1);
257 while (doc_prior(doc, m) != '\n')
258 if (doc_prev(doc,m) == WEOF)
260 } while (!mark_same(m, m2) && num > 1 &&
261 (name == NULL || has_name(doc, m, name)));
262 if (mark_same(m, m2) || num > 1)
265 if (num == 1 && name)
266 call("doc:set-attr", doc, 0, m, "history:name",
268 rv = call_comm("doc:get-str", doc, ci->comm2,
278 const char *docname = ci->str;
279 const char *line = ci->str2;
282 if (!docname || !line || strchr(line, '\n'))
284 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL, ci->str);
286 doc = call_ret(pane, "doc:from-text", ci->focus,
289 call("global-multicall-doc:appeared-", doc);
293 call("doc:replace", doc, 1, NULL, line, 1);
294 call("doc:replace", doc, 1, NULL, "\n", 1);
298 void edlib_init(struct pane *ed safe)
300 call_comm("global-set-command", ed, &history_attach, 0, NULL, "attach-history");
301 call_comm("global-set-command", ed, &history_last, 0, NULL, "history:get-last");
302 call_comm("global-set-command", ed, &history_add, 0, NULL, "history:add");
307 history_map = key_alloc();
308 key_add(history_map, "Close", &history_close);
309 key_add(history_map, "Free", &history_free);
310 key_add(history_map, "Notify:Close", &history_notify_close);
311 key_add(history_map, "doc:replaced", &history_notify_replace);
312 key_add(history_map, "K:M-p", &history_move);
313 key_add(history_map, "K:M-n", &history_move);
314 key_add(history_map, "history:save", &history_save);
315 key_add(history_map, "history:get-last", &history_hlast);