]> git.neil.brown.name Git - edlib.git/blob - render-complete.c
complete: fix mark placement after failed search
[edlib.git] / render-complete.c
1 /*
2  * Copyright Neil Brown ©2015-2020 <neil@brown.name>
3  * May be distributed under terms of GPLv2 - see file:COPYING
4  *
5  * render-complete - support string completion.
6  *
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 don't start with the prefix.
10  * All events are redirected to the controlling window (where the text
11  * to be completed is being entered)
12  *
13  * This module doesn't hold any marks on any document.  The marks
14  * held by the rendered should be sufficient.
15  */
16
17 #define _GNU_SOURCE for strcasestr
18 #include <stdlib.h>
19 #include <string.h>
20 #include <ctype.h>
21 #include "core.h"
22 #include "misc.h"
23
24 struct complete_data {
25         char *orig;
26         struct stk {
27                 struct stk *prev;
28                 const char *substr safe;
29         } *stk safe;
30         int prefix_only;
31 };
32
33 static struct map *rc_map;
34
35 DEF_LOOKUP_CMD(complete_handle, rc_map);
36
37 struct rlcb {
38         struct command c;
39         int plen;
40         const char *prefix safe, *str;
41 };
42
43 static const char *add_highlight_prefix(const char *orig, int start, int plen,
44                                         const char *attr safe)
45 {
46         struct buf ret;
47         const char *c safe;
48
49         if (orig == NULL)
50                 return orig;
51         buf_init(&ret);
52         c = orig;
53         while (start > 0 && *c) {
54                 if (*c == '<')
55                         buf_append_byte(&ret, *c++);
56                 buf_append_byte(&ret, *c++);
57                 start -= 1;
58         }
59         buf_concat(&ret, attr);
60         while (plen > 0 && *c) {
61                 if (*c == '<')
62                         buf_append_byte(&ret, *c++);
63                 buf_append_byte(&ret, *c++);
64                 plen -= 1;
65         }
66         buf_concat(&ret, "</>");
67         buf_concat(&ret, c);
68         return buf_final(&ret);
69 }
70
71 DEF_CMD(save_highlighted)
72 {
73         struct rlcb *cb = container_of(ci->comm, struct rlcb, c);
74         const char *start;
75
76         if (!ci->str)
77                 return 1;
78
79         start = strcasestr(ci->str, cb->prefix);
80         if (!start)
81                 start = ci->str;
82         cb->str = add_highlight_prefix(ci->str, start - ci->str, cb->plen, "<fg:red>");
83         return 1;
84 }
85
86 DEF_CMD(render_complete_line)
87 {
88         struct complete_data *cd = ci->home->data;
89         struct rlcb cb;
90         int ret;
91
92         if (!ci->mark)
93                 return Enoarg;
94
95         cb.prefix = cd->stk->substr;
96         cb.plen = strlen(cd->stk->substr);
97         cb.str = NULL;
98         cb.c = save_highlighted;
99         ret = call_comm(ci->key, ci->home->parent, &cb.c, ci->num, ci->mark,
100                         NULL, 0, ci->mark2);
101         if (ret < 0 || !cb.str)
102                 return ret;
103
104         ret = comm_call(ci->comm2, "callback:render", ci->focus, 0, NULL, cb.str);
105         free((void*)cb.str);
106         return ret;
107 }
108
109 DEF_CMD(complete_free)
110 {
111         struct complete_data *cd = ci->home->data;
112         struct stk *stk = cd->stk;
113
114         while (stk) {
115                 struct stk *t = stk;
116                 stk = stk->prev;
117                 free((void*)t->substr);
118                 free(t);
119         }
120
121         unalloc(cd, pane);
122         return 1;
123 }
124
125
126 static struct pane *complete_pane(struct pane *focus)
127 {
128         struct pane *complete;
129         struct complete_data *cd;
130
131         alloc(cd, pane);
132         complete = pane_register(focus, 0, &complete_handle.c, cd);
133         if (!complete) {
134                 unalloc(cd, pane);
135                 return NULL;
136         }
137         cd->stk = malloc(sizeof(cd->stk));
138         cd->stk->prev = NULL;
139         cd->stk->substr = strdup("");
140         cd->prefix_only = 1;
141         return complete;
142 }
143
144 DEF_CMD(complete_clone)
145 {
146         struct pane *parent = ci->focus;
147         struct pane *complete;
148
149         complete = complete_pane(parent);
150         if (complete)
151                 pane_clone_children(ci->home, complete);
152         return 1;
153 }
154
155 DEF_CMD(complete_ignore_replace)
156 {
157         return 1;
158 }
159
160 DEF_CMD(complete_escape)
161 {
162         /* submit the original prefix back*/
163         struct complete_data *cd = ci->home->data;
164
165         /* This pane might be closed before the reply string is used,
166          * so we need to save it.
167          */
168         call("popup:close", ci->home->parent, NO_NUMERIC, NULL,
169              strsave(ci->home, cd->orig));
170         return 1;
171 }
172
173 DEF_CMD(complete_char)
174 {
175         struct complete_data *cd = ci->home->data;
176         char *np;
177         int pl = strlen(cd->stk->substr);
178         const char *suffix = ksuffix(ci, "doc:char-");
179
180         np = malloc(pl + strlen(suffix) + 1);
181         strcpy(np, cd->stk->substr);
182         strcpy(np+pl, suffix);
183         call("Complete:prefix", ci->focus, !cd->prefix_only, NULL, np);
184         return 1;
185 }
186
187 DEF_CMD(complete_bs)
188 {
189         struct complete_data *cd = ci->home->data;
190         struct stk *stk = cd->stk;
191
192         if (!stk->prev)
193                 return 1;
194         cd->stk = stk->prev;
195         free((void*)stk->substr);
196         free(stk);
197         call("Complete:prefix", ci->home);
198         return 1;
199 }
200
201 static int csame(char a, char b)
202 {
203         if (isupper(a))
204                 a = tolower(a);
205         if (isupper(b))
206                 b = tolower(b);
207         return a == b;
208 }
209
210 static int common_len(const char *a safe, const char *b safe)
211 {
212         int len = 0;
213         while (*a && csame(*a, *b)) {
214                 a += 1;
215                 b += 1;
216                 len += 1;
217         }
218         return len;
219 }
220
221 static void adjust_pre(char *common safe, const char *new safe, int len)
222 {
223         int l = strlen(common);
224         int newlen = 0;
225
226         while (l && len && csame(common[l-1], new[len-1])) {
227                 l -= 1;
228                 len -= 1;
229                 newlen += 1;
230         }
231         if (l)
232                 memmove(common, common+l, newlen+1);
233 }
234
235 struct setcb {
236         struct command c;
237         struct complete_data *cd safe;
238         const char *ss safe;
239         int best_match;
240         char *common;
241         /* common_pre is the longest common prefix to 'common' that
242          * appears in all matches in which 'common' appears.  It is
243          * allocated with enough space to append 'common' after the
244          * prefix.
245          */
246         char *common_pre;
247         struct mark *bestm;
248         int cnt;
249 };
250
251 DEF_CMD(set_cb)
252 {
253         struct setcb *cb = container_of(ci->comm, struct setcb, c);
254         struct complete_data *cd = cb->cd;
255         const char *ss = cb->ss;
256         int len = strlen(ss);
257         const char *c = ci->str;
258         const char *match;
259         int this_match = 0;
260         int l;
261
262         if (!c)
263                 return Enoarg;
264         if (cd->prefix_only) {
265                 match = c;
266                 if (strncmp(match, ss, len) == 0)
267                         this_match += 1;
268         } else {
269                 match = strcasestr(c, ss);
270                 if (strncasecmp(c, ss, len) == 0) {
271                         this_match += 1;
272                         if (strncmp(c, ss, len) == 0)
273                                 this_match += 1;
274                 } else if (strstr(c, ss))
275                         this_match += 1;
276         }
277
278         if (!match)
279                 /* should be impossible */
280                 return 1;
281
282         l = strlen(match);
283         if (l && match[l-1] == '\n')
284                 l -= 1;
285
286         if (this_match > cb->best_match) {
287                 /* Only use matches at least this good to calculate
288                  * 'common'
289                  */
290                 cb->best_match = this_match;
291                 free(cb->common);
292                 cb->common = NULL;
293                 free(cb->common_pre);
294                 cb->common_pre = NULL;
295         }
296
297         if (this_match == cb->best_match) {
298                 /* This match can be used for 'common' and
299                  * initial cursor
300                  */
301                 mark_free(cb->bestm);
302                 if (ci->mark)
303                         cb->bestm = mark_dup(ci->mark);
304
305                 if (!cb->common) {
306                         cb->common = strndup(match, l);
307                 } else {
308                         cb->common[common_len(match, cb->common)] = 0;
309                         /* If 'match' and 'common' disagree on case of
310                          * 'prefix', use that of 'prefix'
311                          */
312                         if (memcmp(cb->common, match, len) != 0)
313                                 memcpy(cb->common, ss, len);
314                 }
315                 if (!cb->common_pre) {
316                         cb->common_pre = strndup(c, l + match-c);
317                         strncpy(cb->common_pre, c, match-c);
318                         cb->common_pre[match-c] = 0;
319                 } else
320                         adjust_pre(cb->common_pre, c, match-c);
321         }
322         cb->cnt += 1;
323         return 1;
324 }
325
326 DEF_CMD(complete_set_prefix)
327 {
328         /* Set the prefix, force a full refresh, and move point
329          * to the first match at start-of-line, or first match
330          * If there is no match, return -1.
331          * Otherwise return number of matches in ->num2 and
332          * the longest common prefix in ->str.
333          */
334         struct pane *p = ci->home;
335         struct complete_data *cd = p->data;
336         struct setcb cb;
337         struct stk *stk;
338         struct mark *m;
339
340         /* Save a copy of the point so we can restore it if needed */
341         m = call_ret(mark, "doc:point", ci->focus);
342         if (m)
343                 m = mark_dup(m);
344
345         cb.c = set_cb;
346         cb.cd = cd;
347         cb.best_match = 0;
348         cb.common = NULL;
349         cb.common_pre = NULL;
350         cb.bestm = NULL;
351         cb.cnt = 0;
352         if (ci->str) {
353                 cb.ss = ci->str;
354                 cd->prefix_only = !ci->num;
355         } else {
356                 cb.ss = cd->stk->substr;
357         }
358
359         call_comm("Filter:set", ci->focus, &cb.c,
360                   cd->prefix_only ? 3 : 2, NULL, cb.ss);
361
362         if (cb.cnt <= 0) {
363                 /* Revert */
364                 call("Filter:set", ci->focus,
365                      cd->prefix_only ? 3 : 2, NULL, cd->stk->substr);
366                 if (m)
367                         call("Move-to", ci->focus, 0, m);
368         }
369         mark_free(m);
370
371         if (cb.common_pre && cb.common && cb.cnt && ci->str) {
372                 strcat(cb.common_pre, cb.common);
373                 stk = malloc(sizeof(*stk));
374                 stk->substr = cb.common_pre;
375                 stk->prev = cd->stk;
376                 cd->stk = stk;
377                 cb.common_pre = NULL;
378                 call("Filter:set", ci->focus,
379                      cd->prefix_only ? 3 : 2, NULL, cd->stk->substr);
380                 comm_call(ci->comm2, "callback:prefix", ci->focus, cb.cnt,
381                           NULL, cd->stk->substr);
382                 if (!cd->orig)
383                         cd->orig = strdup(ci->str);
384         } else {
385                 comm_call(ci->comm2, "callback:prefix", ci->focus, 0);
386         }
387         free(cb.common);
388         free(cb.common_pre);
389         if (cb.bestm) {
390                 call("Move-to", ci->focus, 0, cb.bestm);
391                 mark_free(cb.bestm);
392         }
393
394         call("view:changed", ci->focus);
395         pane_damaged(ci->home, DAMAGED_VIEW);
396
397         return cb.cnt + 1;
398 }
399
400 DEF_CMD(save_str)
401 {
402         struct call_return *cr = container_of(ci->comm, struct call_return, c);
403         cr->s = ci->str ? strdup(ci->str) : NULL;
404         return 1;
405 }
406
407 DEF_CMD(complete_return)
408 {
409         /* submit the selected entry to the popup */
410         struct call_return cr;
411         int l;
412         char *c1, *c2;
413
414         if (!ci->mark)
415                 return Enoarg;
416
417         cr.c = save_str;
418         cr.s = NULL;
419         home_call(ci->home, "doc:render-line",
420                   ci->home, NO_NUMERIC, ci->mark, NULL, 0, NULL,
421                   NULL, 0,0, &cr.c);
422         if (!cr.s)
423                 return 1;
424         l = strlen(cr.s);
425         if (l && cr.s[l-1] == '\n')
426                 cr.s[l-1] = 0;
427         c1 = c2 = cr.s;
428         while (*c2) {
429                 if (*c2 != '<') {
430                         *c1++ = *c2++;
431                         continue;
432                 }
433                 c2 += 1;
434                 if (*c2 == '<') {
435                         *c1++ = *c2++;
436                         continue;
437                 }
438                 while (*c2 && c2[-1] != '>')
439                         c2++;
440         }
441         *c1 = 0;
442
443         call("popup:close", ci->home->parent, NO_NUMERIC, NULL,
444              cr.s, 0);
445         free(cr.s);
446         return 1;
447 }
448
449 static void register_map(void)
450 {
451         rc_map = key_alloc();
452
453         key_add(rc_map, "doc:render-line", &render_complete_line);
454         key_add(rc_map, "Free", &complete_free);
455         key_add(rc_map, "Clone", &complete_clone);
456
457         key_add(rc_map, "Replace", &complete_ignore_replace);
458         key_add(rc_map, "K:ESC", &complete_escape);
459         key_add_range(rc_map, "doc:char- ", "doc:char-~", &complete_char);
460         key_add(rc_map, "K:Backspace", &complete_bs);
461
462         key_add(rc_map, "K:Enter", &complete_return);
463
464         key_add(rc_map, "Complete:prefix", &complete_set_prefix);
465 }
466
467 DEF_CMD(complete_attach)
468 {
469         struct pane *p = ci->focus;
470         struct pane *complete;
471
472         if (!rc_map)
473                 register_map();
474
475         p = call_ret(pane, "attach-linefilter", p);
476         if (!p)
477                 return Efail;
478         complete = complete_pane(p);
479         if (!complete) {
480                 pane_close(p);
481                 return Efail;
482         }
483
484         return comm_call(ci->comm2, "callback:attach", complete);
485 }
486
487 void edlib_init(struct pane *ed safe)
488 {
489         call_comm("global-set-command", ed, &complete_attach,
490                   0, NULL, "attach-render-complete");
491 }