]> git.neil.brown.name Git - edlib.git/blob - render-complete.c
reorder final arguments of pane_call()
[edlib.git] / render-complete.c
1 /*
2  * Copyright Neil Brown ©2015 <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 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 #include <stdlib.h>
18 #include <string.h>
19 #include "core.h"
20 #include "misc.h"
21
22 struct complete_data {
23         char *prefix safe;
24 };
25
26 struct rlcb {
27         struct command c;
28         int keep, plen, cmp;
29         char *prefix safe, *str;
30 };
31
32 static char *add_highlight_prefix(char *orig, int plen, char *attr safe)
33 {
34         struct buf ret;
35         char *c safe;
36
37         if (orig == NULL)
38                 return orig;
39         c = orig;
40         buf_init(&ret);
41         buf_concat(&ret, attr);
42         while (plen > 0 && *c) {
43                 if (*c == '<')
44                         buf_append_byte(&ret, *c++);
45                 buf_append_byte(&ret, *c++);
46                 plen -= 1;
47         }
48         buf_concat(&ret, "</>");
49         buf_concat(&ret, c);
50         return buf_final(&ret);
51 }
52
53 DEF_CMD(save_highlighted)
54 {
55         struct call_return *cr = container_of(ci->comm, struct call_return, c);
56         cr->s = add_highlight_prefix(ci->str, cr->i, "<fg:red>");
57         return 1;
58 }
59
60 DEF_CMD(rcl_cb)
61 {
62         struct rlcb *cb = container_of(ci->comm, struct rlcb, c);
63         if (ci->str == NULL)
64                 cb->cmp = 0;
65         else
66                 cb->cmp = strncmp(ci->str, cb->prefix, cb->plen);
67         return 1;
68 }
69 DEF_CMD(render_complete_line)
70 {
71         /* The first line *must* match the prefix.
72          * skip over any following lines that don't
73          */
74         struct complete_data *cd = ci->home->data;
75         int plen = strlen(cd->prefix);
76         struct call_return cr;
77         struct rlcb cb;
78         int ret;
79         struct mark *m;
80
81         if (!ci->mark || !ci->home->parent)
82                 return -1;
83
84         cr.c = save_highlighted;
85         cr.i = plen;
86         cr.s = NULL;
87         if (call_comm(ci->key, ci->home->parent, &cr.c, ci->num, ci->mark, NULL,
88                       0, ci->mark2) == 0)
89                 return 0;
90
91         ret = comm_call(ci->comm2, "callback:render", ci->focus, 0, NULL, cr.s);
92         free(cr.s);
93         if (ci->num != NO_NUMERIC)
94                 return ret;
95         /* Need to continue over other matching lines */
96         m = mark_dup(ci->mark, 1);
97         while (1) {
98                 cb.c = rcl_cb;
99                 cb.plen = plen;
100                 cb.prefix = cd->prefix;
101                 cb.cmp = 0;
102                 call_comm(ci->key, ci->home->parent, &cb.c, ci->num, m, NULL,
103                           0, ci->mark2);
104
105                 if (cb.cmp == 0)
106                         break;
107
108                 /* have a non-match, so move the mark over it. */
109                 mark_to_mark(ci->mark, m);
110         }
111         mark_free(m);
112         return ret;
113 }
114
115 DEF_CMD(rlcb)
116 {
117         struct rlcb *cb = container_of(ci->comm, struct rlcb, c);
118         if (ci->str == NULL)
119                 cb->cmp = -1;
120         else
121                 cb->cmp = strncmp(ci->str, cb->prefix, cb->plen);
122         if (cb->cmp == 0 && cb->keep && ci->str)
123                 cb->str = strdup(ci->str);
124         return 1;
125 }
126
127 static int do_render_complete_prev(struct complete_data *cd safe, struct mark *m safe,
128                                    struct pane *focus safe, int n, char **savestr)
129 {
130         /* If 'n' is 0 we just need 'start of line' so use
131          * underlying function.
132          * otherwise call repeatedly and then render the line and see if
133          * it matches the prefix.
134          */
135         struct rlcb cb;
136         int ret;
137         struct mark *m2, *m3;
138         int n2;
139
140         if (savestr)
141                 *savestr = NULL;
142         m2 = m;
143         n2 = 0;
144
145         cb.c = rlcb;
146         cb.str = NULL;
147         cb.prefix = cd->prefix;
148         cb.plen = strlen(cb.prefix);
149         cb.cmp = 0;
150         while (1) {
151                 ret = call("render-line-prev", focus, n2, m2);
152                 if (ret <= 0 || n == 0)
153                         /* Either hit start-of-file, or have what we need */
154                         break;
155                 /* we must be looking at a possible option for the previous
156                  * line
157                  */
158                 if (m2 == m)
159                         m2 = mark_dup(m, 1);
160                 m3 = mark_dup(m2, 1);
161                 cb.keep = n2 == 1 && savestr;
162                 cb.str = NULL;
163                 if (call_comm("render-line", focus, &cb.c, NO_NUMERIC, m3)
164                     != 1) {
165                         mark_free(m3);
166                         break;
167                 }
168                 mark_free(m3);
169
170                 if (savestr)
171                         *savestr = cb.str;
172
173                 if (cb.cmp == 0 && n2 == 1)
174                         /* This is a valid new start-of-line */
175                         break;
176                 /* step back once more */
177                 n2 = 1;
178         }
179         if (m2 != m) {
180                 if (ret > 0)
181                         /* move m back to m2 */
182                         mark_to_mark(m, m2);
183                 mark_free(m2);
184         }
185         return ret;
186 }
187
188 DEF_CMD(render_complete_prev)
189 {
190         /* If ->num is 0 we just need 'start of line' so use
191          * underlying function.
192          * otherwise call repeatedly and then render the line and see if
193          * it matches the prefix.
194          */
195         struct complete_data *cd = ci->home->data;
196         if (!ci->mark || !ci->home->parent)
197                 return -1;
198         return do_render_complete_prev(cd, ci->mark, ci->home->parent, ci->num, NULL);
199 }
200
201 DEF_CMD(complete_close)
202 {
203         struct pane *p = ci->home;
204         struct complete_data *cd = p->data;
205         free(cd->prefix);
206         free(cd);
207         return 1;
208 }
209
210 DEF_CMD(complete_attach);
211 DEF_CMD(complete_clone)
212 {
213         struct pane *parent = ci->focus;
214
215         complete_attach.func(ci);
216         pane_clone_children(ci->home, parent->focus);
217         return 1;
218 }
219
220 DEF_CMD(complete_nomove)
221 {
222         if (strcmp(ci->key, "Move-File") == 0)
223                 return 0;
224         if (strcmp(ci->key, "Move-to") == 0)
225                 return 0;
226         if (strcmp(ci->key, "Move-Line") == 0)
227                 return 0;
228         return 1;
229 }
230
231 DEF_CMD(eol_cb)
232 {
233         /* don't save anything */
234         return 1;
235 }
236
237 DEF_CMD(complete_eol)
238 {
239         int rpt = RPT_NUM(ci);
240
241         if (!ci->mark || !ci->focus->parent)
242                 return -1;
243         if (rpt >= -1 && rpt <= 1)
244                 /* movement within the line */
245                 return 1;
246         while (rpt < -1) {
247                 if (do_render_complete_prev(ci->home->data, ci->mark,
248                                             ci->focus->parent, 1, NULL) < 0)
249                         rpt = -1;
250                 rpt += 1;
251         }
252         while (rpt > 1) {
253                 struct call_return cr;
254                 cr.c = eol_cb;
255                 if (pane_call(ci->home, "render-line",
256                               ci->focus, NO_NUMERIC, ci->mark, NULL,
257                               0, NULL, NULL, 0,0, &cr.c) <= 0)
258                         rpt = 1;
259                 rpt -= 1;
260         }
261         return 1;
262 }
263
264 static int common_len(char *a safe, char *b safe)
265 {
266         int len = 0;
267         while (*a && *a == *b) {
268                 a += 1;
269                 b += 1;
270                 len += 1;
271         }
272         return len;
273 }
274
275 DEF_CMD(complete_set_prefix)
276 {
277         /* Set the prefix, force a full refresh, and move point
278          * to the first match.
279          * If there is no match, return -1.
280          * Otherwise return number of matches in ->num2 and
281          * the longest common prefix in ->str.
282          */
283         struct pane *p = ci->home;
284         struct complete_data *cd = p->data;
285         struct mark *m;
286         char *c;
287         int cnt = 0;
288         char *common = NULL;
289
290         if (!ci->str)
291                 return -1;
292         free(cd->prefix);
293         cd->prefix = strdup(ci->str);
294
295         m = mark_at_point(ci->focus, NULL, MARK_UNGROUPED);
296         if (!m || !p->parent)
297                 return -1;
298         call("Move-File", ci->focus, 1, m);
299
300         while (do_render_complete_prev(cd, m, p->parent, 1, &c) > 0 && c) {
301                 int l = strlen(c);
302                 if (c[l-1] == '\n')
303                         l -= 1;
304                 if (common == NULL)
305                         common = strndup(c, l);
306                 else
307                         common[common_len(c, common)] = 0;
308                 cnt += 1;
309         }
310         comm_call(ci->comm2, "callback:prefix", ci->focus, 0, NULL, common);
311         free(common);
312         call("Move-to", ci->focus, 0, m);
313         mark_free(m);
314         pane_damaged(ci->focus, DAMAGED_VIEW);
315         return cnt + 1;
316 }
317
318 DEF_CMD(save_str)
319 {
320         struct call_return *cr = container_of(ci->comm, struct call_return, c);
321         cr->s = ci->str ? strdup(ci->str) : NULL;
322         return 1;
323 }
324
325 DEF_CMD(complete_return)
326 {
327         /* submit the selected entry to the popup */
328         struct pane *p = ci->home;
329         struct complete_data *cd = p->data;
330         struct call_return cr;
331         int l;
332         char *c1, *c2;
333
334         if (!ci->mark || !ci->home->parent)
335                 return -1;
336
337         cr.c = save_str;
338         cr.s = NULL;
339         pane_call(ci->home, "render-line",
340                   ci->home, NO_NUMERIC, ci->mark, NULL, 0, NULL,
341                   NULL, 0,0, &cr.c);
342         if (!cr.s)
343                 return 1;
344         l = strlen(cr.s);
345         if (l && cr.s[l-1] == '\n')
346                 cr.s[l-1] = 0;
347         c1 = c2 = cr.s;
348         while (*c2) {
349                 if (*c2 != '<') {
350                         *c1++ = *c2++;
351                         continue;
352                 }
353                 c2 += 1;
354                 if (*c2 == '<') {
355                         *c1++ = *c2++;
356                         continue;
357                 }
358                 while (*c2 && c2[-1] != '>')
359                         c2++;
360         }
361         *c1 = 0;
362
363         call("popup:close", ci->home->parent, NO_NUMERIC, NULL,
364               cr.s + strlen(cd->prefix), 0);
365         free(cr.s);
366         return 1;
367 }
368
369 static struct map *rc_map;
370
371 DEF_LOOKUP_CMD(complete_handle, rc_map)
372
373 static void register_map(void)
374 {
375         rc_map = key_alloc();
376
377         key_add(rc_map, "render-line", &render_complete_line);
378         key_add(rc_map, "render-line-prev", &render_complete_prev);
379         key_add(rc_map, "Close", &complete_close);
380         key_add(rc_map, "Clone", &complete_clone);
381
382         key_add_range(rc_map, "Move-", "Move-\377", &complete_nomove);
383         key_add(rc_map, "Move-EOL", &complete_eol);
384
385         key_add(rc_map, "Return", &complete_return);
386
387         key_add(rc_map, "Complete:prefix", &complete_set_prefix);
388 }
389
390 REDEF_CMD(complete_attach)
391 {
392         struct pane *complete;
393         struct complete_data *cd;
394
395         if (!rc_map)
396                 register_map();
397
398         cd = calloc(1, sizeof(*cd));
399         complete = pane_register(ci->focus, 0, &complete_handle.c, cd, NULL);
400         if (!complete) {
401                 free(cd);
402                 return -1;
403         }
404         cd->prefix = strdup("");
405
406         return comm_call(ci->comm2, "callback:attach", complete);
407 }
408
409 void edlib_init(struct pane *ed safe)
410 {
411         call_comm("global-set-command", ed, &complete_attach, 0, NULL, "attach-render-complete");
412 }