2 * Copyright Neil Brown ©2015-2023 <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 string and it suppresses all
9 * lines which don't match the string. Matching can be case-insensitive,
10 * and may require the string to be at the start of the line.
12 * The linefilter module is used manage the selective display of lines.
13 * This module examine the results provided by linefilter and extends the
14 * string to the maximum that still matches the same set of lines.
15 * Keystrokes can extend or contract the match, which will cause display
18 * This module doesn't hold any marks on any document. The marks
19 * held by the rendered should be sufficient.
22 #define _GNU_SOURCE for strcasestr
29 struct complete_data {
34 const char *substr safe;
39 static struct map *rc_map;
41 DEF_LOOKUP_CMD(complete_handle, rc_map);
46 const char *prefix safe, *str;
49 static void strip_attrs(char *c safe)
54 if (*c == ack || *c == etx)
65 if (*c == '<' && c[1] == '<') {
80 static char *add_highlight_prefix(const char *orig, int start, int plen,
81 const char *attr safe, int *offset)
90 while (start > 0 && *c && (!offset || ret.len < *offset)) {
92 buf_append_byte(&ret, *c++);
93 buf_append_byte(&ret, *c++);
96 if (!*c || (offset && ret.len >= *offset)) {
97 /* Nothing here to highlight */
100 return buf_final(&ret);
103 buf_concat(&ret, attr);
104 while (plen > 0 && *c && (!offset || ret.len < *offset)) {
106 buf_append_byte(&ret, *c++);
107 buf_append_byte(&ret, *c++);
110 buf_concat(&ret, "</>");
111 while (*c && (!offset || ret.len < *offset)) {
113 buf_append_byte(&ret, *c++);
114 buf_append_byte(&ret, *c++);
118 return buf_final(&ret);
121 DEF_CMD(render_complete_line)
123 struct complete_data *cd = ci->home->data;
124 char *line, *start, *hl;
132 m = mark_dup(ci->mark);
133 line = call_ret(str, ci->key, ci->home->parent, -1, m);
138 match = cd->stk->substr;
139 start = strcasestr(line, match);
143 startlen = start - line;
145 /* Only want 'num' bytes from start, with ->mark positioned.
146 * So need to find how many bytes of 'line' produce num bytes
147 * of highlighted line.
150 hl = add_highlight_prefix(line, startlen, strlen(match),
155 line = call_ret(str, ci->key, ci->home->parent,
157 } else if (ci->mark2) { //FIXME
158 /* Only want up-to the cursor, which might be in the middle of
159 * the highlighted region. Now we know where that is, we can
160 * highlight whatever part is still visible.
164 line = call_ret(str, ci->key, ci->home->parent,
165 ci->num, ci->mark, NULL,
168 mark_to_mark(ci->mark, m);
173 hl = add_highlight_prefix(line, startlen, strlen(match), "<fg:red>", NULL);
175 ret = comm_call(ci->comm2, "callback:render", ci->focus, 0, NULL, hl);
181 DEF_CMD(complete_free)
183 struct complete_data *cd = ci->home->data;
184 struct stk *stk = cd->stk;
189 free((void*)t->substr);
198 static struct pane *complete_pane(struct pane *focus safe)
200 struct pane *complete;
201 struct complete_data *cd;
204 complete = pane_register(focus, 0, &complete_handle.c, cd);
209 cd->stk = malloc(sizeof(cd->stk[0]));
210 cd->stk->prev = NULL;
211 cd->stk->substr = strdup("");
216 DEF_CMD(complete_clone)
218 struct pane *parent = ci->focus;
219 struct pane *complete;
221 complete = complete_pane(parent);
223 pane_clone_children(ci->home, complete);
227 DEF_CMD(complete_ignore_replace)
232 DEF_CMD(complete_escape)
234 /* submit the original prefix back*/
235 struct complete_data *cd = ci->home->data;
237 /* This pane might be closed before the reply string is used,
238 * so we need to save it.
240 call("popup:close", ci->home->parent, NO_NUMERIC, NULL,
241 strsave(ci->home, cd->orig));
245 DEF_CMD(complete_char)
247 struct complete_data *cd = ci->home->data;
249 int pl = strlen(cd->stk->substr);
250 const char *suffix = ksuffix(ci, "doc:char-");
252 np = malloc(pl + strlen(suffix) + 1);
253 strcpy(np, cd->stk->substr);
254 strcpy(np+pl, suffix);
255 call("Complete:prefix", ci->focus, !cd->prefix_only, NULL, np,
263 struct complete_data *cd = ci->home->data;
264 struct stk *stk = cd->stk;
269 if (stk->substr[0] && !stk->prev->substr[0]) {
270 old = (void*)stk->substr;
271 old[strlen(old)-1] = 0;
274 free((void*)stk->substr);
277 call("Complete:prefix", ci->home, 0, NULL, NULL, 1,
282 static int csame(char a, char b)
291 static int common_len(const char *a safe, const char *b safe)
294 while (*a && csame(*a, *b)) {
302 static void adjust_pre(char *common safe, const char *new safe, int len)
304 int l = strlen(common);
307 while (l && len && csame(common[l-1], new[len-1])) {
313 memmove(common, common+l, newlen+1);
318 struct complete_data *cd safe;
322 /* common_pre is the longest common prefix to 'common' that
323 * appears in all matches in which 'common' appears. It is
324 * allocated with enough space to append 'common' after the
334 struct setcb *cb = container_of(ci->comm, struct setcb, c);
335 struct complete_data *cd = cb->cd;
336 const char *ss = cb->ss;
337 int len = strlen(ss);
338 const char *c = ci->str;
345 if (cd->prefix_only) {
347 if (strncmp(match, ss, len) == 0)
350 match = strcasestr(c, ss);
351 if (strncasecmp(c, ss, len) == 0) {
353 if (strncmp(c, ss, len) == 0)
355 } else if (strstr(c, ss))
360 /* should be impossible */
364 if (l && match[l-1] == '\n')
367 if (this_match > cb->best_match) {
368 /* Only use matches at least this good to calculate
371 cb->best_match = this_match;
374 free(cb->common_pre);
375 cb->common_pre = NULL;
378 if (this_match == cb->best_match) {
379 /* This match can be used for 'common' and
382 mark_free(cb->bestm);
384 cb->bestm = mark_dup(ci->mark);
387 cb->common = strndup(match, l);
389 cb->common[common_len(match, cb->common)] = 0;
390 /* If 'match' and 'common' disagree on case of
391 * 'prefix', use that of 'prefix'
393 if (memcmp(cb->common, match, len) != 0)
394 memcpy(cb->common, ss, len);
396 if (!cb->common_pre) {
397 cb->common_pre = strndup(c, l + match-c);
398 strncpy(cb->common_pre, c, match-c);
399 cb->common_pre[match-c] = 0;
401 adjust_pre(cb->common_pre, c, match-c);
407 DEF_CMD(complete_set_prefix)
409 /* Set the prefix, force a full refresh, and move point
410 * to the first match at start-of-line, or first match
411 * If there is no match, return -1.
412 * Otherwise return number of matches in ->num2 and
413 * the longest common prefix in ->str.
414 * If ci->num with ->str, allow substrings, else prefix-only
415 * if ci->num2, don't autocomplete, just display matches
417 struct pane *p = ci->home;
418 struct complete_data *cd = p->data;
423 /* Save a copy of the point so we can restore it if needed */
424 m = call_ret(mark, "doc:point", ci->focus);
432 cb.common_pre = NULL;
437 cd->prefix_only = !ci->num;
439 cb.ss = cd->stk->substr;
441 if (ci->str2 && (!cd->attr || strcmp(cd->attr, ci->str2) != 0)) {
443 cd->attr = strdup(ci->str2);
446 call_comm("Filter:set", ci->focus, &cb.c,
447 cd->prefix_only ? 3 : 2, NULL, cb.ss, 0, NULL, cd->attr);
451 call("Filter:set", ci->focus,
452 cd->prefix_only ? 3 : 2, NULL, cd->stk->substr,
455 call("Move-to", ci->focus, 0, m);
459 if (cb.common_pre && cb.common && cb.cnt && ci->str) {
461 strcat(cb.common_pre, cb.common);
462 stk = malloc(sizeof(*stk));
463 stk->substr = cb.common_pre;
466 cb.common_pre = NULL;
467 call("Filter:set", ci->focus,
468 cd->prefix_only ? 3 : 2, NULL, cd->stk->substr,
470 comm_call(ci->comm2, "callback:prefix", ci->focus, cb.cnt,
471 NULL, cd->stk->substr);
473 cd->orig = strdup(ci->str);
475 comm_call(ci->comm2, "callback:prefix", ci->focus, 0);
480 call("Move-to", ci->focus, 0, cb.bestm);
484 call("view:changed", ci->focus);
491 struct call_return *cr = container_of(ci->comm, struct call_return, c);
492 cr->s = ci->str ? strdup(ci->str) : NULL;
496 DEF_CMD(complete_return)
498 /* submit the selected entry to the popup */
499 struct call_return cr;
507 /* Go to start of line */
508 home_call(ci->home, "doc:render-line-prev", ci->home, 0, ci->mark);
509 home_call(ci->home, "doc:render-line",
510 ci->home, -1, ci->mark, NULL, 0, NULL,
516 if (l && cr.s[l-1] == '\n')
519 call("popup:close", ci->home->parent, NO_NUMERIC, NULL,
525 static void register_map(void)
527 rc_map = key_alloc();
529 key_add(rc_map, "doc:render-line", &render_complete_line);
530 key_add(rc_map, "Free", &complete_free);
531 key_add(rc_map, "Clone", &complete_clone);
533 key_add(rc_map, "Replace", &complete_ignore_replace);
534 key_add(rc_map, "K:ESC", &complete_escape);
535 key_add_range(rc_map, "doc:char- ", "doc:char-~", &complete_char);
536 key_add(rc_map, "K:Backspace", &complete_bs);
538 key_add(rc_map, "K:Enter", &complete_return);
540 key_add(rc_map, "Complete:prefix", &complete_set_prefix);
543 DEF_CMD(complete_attach)
545 struct pane *p = ci->focus;
546 struct pane *complete;
551 p = call_ret(pane, "attach-linefilter", p);
554 complete = complete_pane(p);
560 return comm_call(ci->comm2, "callback:attach", complete);
563 void edlib_init(struct pane *ed safe)
565 call_comm("global-set-command", ed, &complete_attach,
566 0, NULL, "attach-render-complete");