2 * Copyright Neil Brown ©2015 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
5 * render-complete - support string completion.
7 * This should be attached between render-lines and the pane which
8 * provides the lines. It is given a prefix and it suppresses all
9 * lines which start with the prefix.
10 * All events are redirected to the controlling window (where the text
11 * to be completed is being entered)
13 * This module doesn't hold any marks on any document. The marks
14 * held by the rendered should be sufficient.
22 struct complete_data {
32 static char *add_highlight_prefix(char *orig, int plen, char *attr)
39 buf_concat(&ret, attr);
40 while (plen > 0 && *orig) {
42 buf_append_byte(&ret, *orig++);
43 buf_append_byte(&ret, *orig++);
46 buf_concat(&ret, "</>");
47 buf_concat(&ret, orig);
48 return buf_final(&ret);
51 DEF_CMD(save_highlighted)
53 struct call_return *cr = container_of(ci->comm, struct call_return, c);
54 cr->s = add_highlight_prefix(ci->str, cr->i, "<fg:red>");
60 struct rlcb *cb = container_of(ci->comm, struct rlcb, c);
64 cb->cmp = strncmp(ci->str, cb->prefix, cb->plen);
67 DEF_CMD(render_complete_line)
69 /* The first line *must* match the prefix.
70 * skip over any following lines that don't
72 struct cmd_info ci2 = {};
73 struct complete_data *cd = ci->home->data;
74 int plen = strlen(cd->prefix);
75 struct call_return cr;
84 ci2.mark2 = ci->mark2;
85 ci2.focus = ci->home->parent;
86 ci2.numeric = ci->numeric;
87 cr.c = save_highlighted;
91 if (key_handle(&ci2) == 0)
94 ret = comm_call(ci->comm2, "callback:render", ci->focus, 0, NULL, cr.s, 0);
95 if (ci->numeric != NO_NUMERIC)
97 /* Need to continue over other matching lines */
98 ci2.mark = mark_dup(ci->mark, 1);
100 ci2.numeric = ci->numeric;
101 ci2.focus = ci->home->parent;
104 cb.prefix = cd->prefix;
111 /* have a non-match, so move the mark over it. */
112 mark_to_mark(ci->mark, ci2.mark);
120 struct rlcb *cb = container_of(ci->comm, struct rlcb, c);
124 cb->cmp = strncmp(ci->str, cb->prefix, cb->plen);
125 if (cb->cmp == 0 && cb->keep && ci->str)
126 cb->str = strdup(ci->str);
129 DEF_CMD(render_complete_prev)
131 /* If ->numeric is 0 we just need 'start of line' so use
132 * underlying function.
133 * otherwise call repeatedly and then render the line and see if
134 * it matches the prefix.
136 struct cmd_info ci2 = {}, ci3 = {};
138 struct complete_data *cd = ci->home->data;
143 ci2.focus = ci->home->parent;
146 ci3.key = "render-line";
147 ci3.focus = ci->home->parent;
150 cb.prefix = cd->prefix;
151 cb.plen = strlen(cb.prefix);
155 ret = key_handle(&ci2);
156 if (ret <= 0 || ci->numeric == 0)
157 /* Either hit start-of-file, or have what we need */
159 /* we must be looking at a possible option for the previous
162 if (ci2.mark == ci->mark)
163 ci2.mark = mark_dup(ci->mark, 1);
164 ci3.mark = mark_dup(ci2.mark, 1);
165 ci3.numeric = NO_NUMERIC;
166 cb.keep = ci2.numeric == 1 && ci->extra == 42;
168 if (key_handle(&ci3) != 1) {
173 /* This is a horrible hack, but as it is entirely internal
174 * to this module it can say for now.
175 * Cast ci to remove any 'const' tag that I hope to add soon.
177 ((struct cmd_info*)ci)->str2 = cb.str;
179 if (cb.cmp == 0 && ci2.numeric == 1)
180 /* This is a valid new start-of-line */
182 /* step back once more */
185 if (ci2.mark != ci->mark) {
187 /* move ci->mark back to ci2.mark */
188 mark_to_mark(ci->mark, ci2.mark);
194 DEF_CMD(complete_close)
196 struct pane *p = ci->home;
197 struct complete_data *cd = p->data;
203 DEF_CMD(complete_attach);
204 DEF_CMD(complete_clone)
206 struct pane *parent = ci->focus;
208 complete_attach.func(ci);
209 pane_clone_children(ci->home, parent->focus);
213 DEF_CMD(complete_nomove)
215 if (strcmp(ci->key, "Move-File") == 0)
217 if (strcmp(ci->key, "Move-to") == 0)
219 if (strcmp(ci->key, "Move-Line") == 0)
226 /* don't save anything */
230 DEF_CMD(complete_eol)
232 int rpt = RPT_NUM(ci);
234 if (rpt >= -1 && rpt <= 1)
235 /* movement within the line */
238 struct cmd_info ci2 = {};
239 ci2.key = "render-line-prev";
242 ci2.focus = ci->focus;
244 if (render_complete_prev_func(&ci2) < 0)
249 struct cmd_info ci2 = {};
250 struct call_return cr;
251 ci2.key = "render-line";
252 ci2.numeric = NO_NUMERIC;
254 ci2.focus = ci->focus;
258 if (render_complete_line_func(&ci2) <= 0)
265 static int common_len(char *a, char *b)
268 while (*a && *a == *b) {
276 DEF_CMD(complete_set_prefix)
278 /* Set the prefix, force a full refresh, and move point
279 * to the first match.
280 * If there is no match, return -1.
281 * Otherwise return number of matches in ->extra and
282 * the longest common prefix in ->str.
284 struct pane *p = ci->home;
285 struct complete_data *cd = p->data;
286 struct cmd_info ci2 = {};
292 cd->prefix = strdup(ci->str);
294 m = mark_at_point(ci->focus, NULL, MARK_UNGROUPED);
295 call3("Move-File", ci->focus, 1, m);
297 ci2.key = "render-line-prev";
302 ci2.extra = 42; /* request copy of line in str2 */
303 while (render_complete_prev_func(&ci2) > 0) {
309 common = strndup(c, l);
311 common[common_len(c, common)] = 0;
314 comm_call(ci->comm2, "callback:prefix", ci->focus, 0, NULL, common, 0);
316 call3("Move-to", ci->focus, 0, m);
318 pane_damaged(ci->focus, DAMAGED_VIEW);
324 struct call_return *cr = container_of(ci->comm, struct call_return, c);
325 cr->s = ci->str ? strdup(ci->str) : NULL;
329 DEF_CMD(complete_return)
331 /* submit the selected entry to the popup */
332 struct pane *p = ci->home;
333 struct complete_data *cd = p->data;
334 struct cmd_info ci2 = {};
335 struct call_return cr;
339 ci2.key = "render-line";
340 ci2.focus = ci->home;
343 ci2.numeric = NO_NUMERIC;
347 render_complete_line_func(&ci2);
351 if (l && cr.s[l-1] == '\n')
364 while (*c2 && c2[-1] != '>')
369 call5("popup:close", ci->home->parent, NO_NUMERIC, NULL,
370 cr.s + strlen(cd->prefix), 0);
375 static struct map *rc_map;
377 DEF_LOOKUP_CMD(complete_handle, rc_map)
379 static void register_map(void)
381 rc_map = key_alloc();
383 key_add(rc_map, "render-line", &render_complete_line);
384 key_add(rc_map, "render-line-prev", &render_complete_prev);
385 key_add(rc_map, "Close", &complete_close);
386 key_add(rc_map, "Clone", &complete_clone);
388 key_add_range(rc_map, "Move-", "Move-\377", &complete_nomove);
389 key_add(rc_map, "Move-EOL", &complete_eol);
391 key_add(rc_map, "Return", &complete_return);
393 key_add(rc_map, "Complete:prefix", &complete_set_prefix);
396 REDEF_CMD(complete_attach)
398 struct pane *complete;
399 struct complete_data *cd;
404 cd = calloc(1, sizeof(*cd));
405 complete = pane_register(ci->focus, 0, &complete_handle.c, cd, NULL);
410 cd->prefix = strdup("");
412 return comm_call(ci->comm2, "callback:attach", complete, 0, NULL, NULL, 0);
415 void edlib_init(struct pane *ed)
417 call_comm("global-set-command", ed, 0, NULL, "attach-render-complete",
418 0, &complete_attach);