]> git.neil.brown.name Git - edlib.git/blob - mode-emacs.c
TODO: clean out done items.
[edlib.git] / mode-emacs.c
1 /*
2  * Copyright Neil Brown ©2015-2023 <neil@brown.name>
3  * May be distributed under terms of GPLv2 - see file:COPYING
4  *
5  * Define some keystrokes to create an editor with an
6  * "emacs" feel.
7  *
8  */
9 #define _GNU_SOURCE /*  for asprintf */
10 #include <unistd.h>
11 #include <stdlib.h>
12 #include <wchar.h>
13 #include <fcntl.h>
14 #include <string.h>
15 #include <ctype.h>
16 #include <wctype.h>
17
18 #include <stdio.h>
19
20 #define PANE_DATA_PTR_TYPE char *
21 #include "core.h"
22 #include "core-pane.h"
23
24 static const char * safe file_normalize(struct pane *p safe, const char *path,
25                                         const char *initial_path);
26
27 /* num2 is used to track if successive commands are related.
28  * Only low 16 bits identify command, other bits are free.
29  */
30 enum {
31         N2_zero = 0,
32         N2_undo_insert, /* adjacent commands form a single undo set */
33         N2_undo_delete, /* adjacent commands form a single undo set */
34         N2_undo_change, /* adjacent commands form a single undo set */
35         N2_recentre,    /* repeated recentre goes to different places */
36         N2_yank,        /* repeated yank-pop takes different result */
37         N2_match,       /* repeated ` after Cx` repeats the search */
38         N2_undo,        /* Last command was 'undo' too */
39         N2_close_others,/* Last command was close-other, just a 1 can repeat */
40         N2_runmacro,    /* Last command was CX-e, just an 'e' can repeat */
41         N2_shift,       /* Last command was CX-< or CX-> */
42         N2_growx,       /* Last command was CX-{ or CX-} */
43         N2_uniquote,    /* Last command was :C-q inserting a unicode from name */
44 };
45 static inline int N2(const struct cmd_info *ci safe)
46 {
47         return ci->num2 & 0xffff;
48 }
49
50 static inline int N2a(const struct cmd_info *ci safe)
51 {
52         return ci->num2 >> 16;
53 }
54
55 REDEF_CMD(emacs_move);
56 REDEF_CMD(emacs_delete);
57 REDEF_CMD(emacs_kill);
58 REDEF_CMD(emacs_case);
59 REDEF_CMD(emacs_swap);
60
61 static struct move_command {
62         struct command  cmd;
63         char            *type safe;
64         int             direction, extra;
65         char            *k1 safe, *k2, *k3;
66 } move_commands[] = {
67         {CMD(emacs_move), "Move-Char", 1, 0,
68          "K:C-F", "K:Right", NULL},
69         {CMD(emacs_move), "Move-Char", -1, 0,
70          "K:C-B", "K:Left", NULL},
71         {CMD(emacs_move), "doc:word", 1, 0,
72          "K:A-f", "K:A:Right", NULL},
73         {CMD(emacs_move), "doc:word", -1, 0,
74          "K:A-b", "K:A:Left", NULL},
75         {CMD(emacs_move), "doc:expr", 1, 0,
76          "K:A:C-F", NULL, NULL},
77         {CMD(emacs_move), "doc:expr", -1, 0,
78          "K:A:C-B", NULL, NULL},
79         {CMD(emacs_move), "doc:expr", -1, 1,
80          "K:A:C-U", NULL, NULL},
81         {CMD(emacs_move), "doc:expr", 1, 1,
82          "K:A:C-D", NULL, NULL},
83         {CMD(emacs_move), "doc:WORD", 1, 0,
84          "K:A-F", NULL, NULL},
85         {CMD(emacs_move), "doc:WORD", -1, 0,
86          "K:A-B", NULL, NULL},
87         {CMD(emacs_move), "doc:EOL", 1, 0,
88          "K:C-E", "K:End", NULL},
89         {CMD(emacs_move), "doc:EOL", -1, 0,
90          "K:C-A", "K:Home", NULL},
91         {CMD(emacs_move), "Move-Line", -1, 0,
92          "K:C-P", "K:Up", NULL},
93         {CMD(emacs_move), "Move-Line", 1, 0,
94          "K:C-N", "K:Down", NULL},
95         {CMD(emacs_move), "doc:file", 1, 0,
96          "K:A->", "K:S:End", NULL},
97         {CMD(emacs_move), "doc:file", -1, 0,
98          "K:A-<", "K:S:Home", NULL},
99         {CMD(emacs_move), "Move-View", 900, 0,
100          "K:Next", "K:C-V", "emacs-move-large-other"},
101         {CMD(emacs_move), "Move-View", -900, 0,
102          "K:Prior", "K:A-v", NULL},
103
104         {CMD(emacs_move), "doc:paragraph", -1, 0,
105          "K:A:C-A", NULL, NULL},
106         {CMD(emacs_move), "doc:paragraph", 1, 0,
107          "K:A:C-E", NULL, NULL},
108
109         {CMD(emacs_delete), "Move-Char", 1, 0,
110          "K:C-D", "K:Del", "del"},
111         {CMD(emacs_delete), "Move-Char", -1, 0,
112          "K:C-H", "K:Backspace", "K:Delete"},
113         {CMD(emacs_delete), "doc:word", 1, 0,
114          "K:A-d", NULL, NULL},
115         {CMD(emacs_delete), "doc:word", -1, 0,
116          "K:A:C-H", "K:A:Backspace", NULL},
117         {CMD(emacs_kill), "doc:EOL", 1, 0,
118          "K:C-K", NULL, NULL},
119         {CMD(emacs_kill), "doc:expr", 1, 0,
120          "K:A:C-K", NULL, NULL},
121
122         {CMD(emacs_case), "Ldoc:word", 1, 0,
123          "K:A-l", "K:A-L", NULL},
124         {CMD(emacs_case), "Udoc:word", 1, 0,
125          "K:A-u", "K:A-U", NULL},
126         {CMD(emacs_case), "Cdoc:word", 1, 0,
127          "K:A-c", "K:A-C", NULL},
128         {CMD(emacs_case), "TMove-Char", 1, 0,
129          "K:A-`", NULL, NULL},
130
131         {CMD(emacs_swap), "Move-Char", 1, 0,
132          "K:C-T", NULL, NULL},
133         {CMD(emacs_swap), "doc:word", 1, 0,
134          "K:A-t", NULL, NULL},
135         {CMD(emacs_swap), "doc:WORD", 1, 0,
136          "K:A-T", NULL, NULL},
137 };
138
139 REDEF_CMD(emacs_move)
140 {
141         struct move_command *mv = container_of(ci->comm, struct move_command, cmd);
142         struct pane *cursor_pane = ci->focus;
143         int ret = 0;
144         struct mark *mk;
145
146         if (!ci->mark)
147                 return Enoarg;
148
149         /* if doc:file, leave inactive mark behind */
150         if (strcmp(mv->type, "doc:file") == 0) {
151                 mk = call_ret(mark2, "doc:point", ci->focus);
152                 if (mk)
153                         /* Don't change selection:active */
154                         mark_to_mark(mk, ci->mark);
155                 else {
156                         call("Move-to", ci->focus, 1, ci->mark);
157                         mk = call_ret(mark2, "doc:point", ci->focus);
158                 }
159         }
160
161         ret = call(mv->type, ci->focus, mv->direction * RPT_NUM(ci), ci->mark,
162                    NULL, mv->extra);
163         if (ret < 0)
164                 return ret;
165
166         if (strcmp(mv->type, "Move-View") == 0)
167                 attr_set_int(&cursor_pane->attrs, "emacs-repoint",
168                              mv->direction*2);
169
170         mk = call_ret(mark2, "doc:point", ci->focus);
171         /* Discard a transient selection */
172         call("selection:clear", ci->focus, 2, mk);
173
174         return 1;
175 }
176
177 REDEF_CMD(emacs_delete)
178 {
179         struct move_command *mv = container_of(ci->comm, struct move_command, cmd);
180         int ret;
181         struct mark *m, *mk;
182
183         if (!ci->mark)
184                 return Enoarg;
185
186         mk = call_ret(mark2, "doc:point", ci->focus);
187         /* If selection is replacable, clear it and use mk */
188         if (call("selection:clear", ci->focus, 3, mk) == Efalse) {
189                 /* else clear any transient selection */
190                 call("selection:clear", ci->focus, 2, mk);
191                 mk = NULL;
192         }
193
194         m = mark_dup(ci->mark);
195
196         ret = call(mv->type, ci->focus, mv->direction * RPT_NUM(ci), m);
197
198         if (ret < 0) {
199                 mark_free(m);
200                 return ret;
201         }
202         if (mk)
203                 call("Replace", ci->focus, 1, mk, NULL, 0, mk);
204
205         ret = call("Replace", ci->focus, 1, m, NULL, N2(ci) == N2_undo_delete);
206         mark_free(m);
207         call("Mode:set-num2", ci->focus, N2_undo_delete);
208
209         return 1;
210 }
211
212 REDEF_CMD(emacs_kill)
213 {
214         /* Like delete, but copy to copy-buffer */
215         struct move_command *mv = container_of(ci->comm, struct move_command, cmd);
216         int ret;
217         struct mark *m;
218         char *str;
219
220         if (!ci->mark)
221                 return Enoarg;
222
223         m = mark_dup(ci->mark);
224
225         if (strcmp(mv->type, "doc:EOL") == 0 &&
226             mv->direction == 1 && RPT_NUM(ci) == 1 &&
227             is_eol(doc_following(ci->focus, m)))
228                 ret = call("Move-Char", ci->focus, mv->direction * RPT_NUM(ci), m);
229         else
230                 ret = call(mv->type, ci->focus, mv->direction * RPT_NUM(ci), m);
231
232         if (ret < 0) {
233                 mark_free(m);
234                 return ret;
235         }
236         /* Remove any selection so it cannot be claimed and so replace this copy */
237         call("selection:claim", ci->focus);
238         call("selection:discard", ci->focus);
239
240         str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, m);
241         if (str && *str)
242                 call("copy:save", ci->focus, N2(ci) == N2_undo_delete, NULL, str);
243         ret = call("Replace", ci->focus, 1, m, NULL, N2(ci) == N2_undo_delete);
244         mark_free(m);
245         call("Mode:set-num2", ci->focus, N2_undo_delete);
246
247         return 1;
248 }
249
250 REDEF_CMD(emacs_case)
251 {
252         struct move_command *mv = container_of(ci->comm, struct move_command, cmd);
253         int ret;
254         struct mark *start = NULL;
255         int cnt = mv->direction * RPT_NUM(ci);
256         int dir;
257
258         if (!ci->mark)
259                 return Enoarg;
260
261         if (cnt == 0)
262                 return 1;
263         if (cnt > 0) {
264                 dir = 1;
265         } else {
266                 dir = -1;
267                 cnt = -cnt;
268                 start = mark_dup(ci->mark);
269         }
270
271         while (cnt > 0) {
272                 struct mark *m = mark_dup(ci->mark);
273
274                 ret = call(mv->type+1, ci->focus, dir, ci->mark);
275                 if (ret <= 0 || mark_same(ci->mark, m))
276                         /* Hit end of file */
277                         cnt = 1;
278                 else {
279                         char *str = call_ret(str, "doc:get-str", ci->focus,
280                                              0, ci->mark, NULL,
281                                              0, m);
282                         char *c;
283                         int changed = 0;
284                         int found = 0;
285
286                         for (c = str; c && *c; c += 1) {
287                                 char type = mv->type[0];
288                                 if (type == 'C') {
289                                         type = found ? 'L' : 'U';
290                                         if (isalpha(*c))
291                                                 found = 1;
292                                 }
293                                 switch(type) {
294                                 default: /* silence compiler */
295                                 case 'U': /* Uppercase */
296                                         if (islower(*c)) {
297                                                 changed = 1;
298                                                 *c = toupper(*c);
299                                         }
300                                         break;
301                                 case 'L': /* Lowercase */
302                                         if (isupper(*c)) {
303                                                 changed = 1;
304                                                 *c = tolower(*c);
305                                         }
306                                         break;
307                                 case 'T': /* Toggle */
308                                         if (isupper(*c)) {
309                                                 changed = 1;
310                                                 *c = tolower(*c);
311                                         } else if (islower(*c)) {
312                                                 changed = 1;
313                                                 *c = toupper(*c);
314                                         }
315                                         break;
316                                 }
317                         }
318                         if (changed) {
319                                 ret = call("Replace", ci->focus, 1, m, str,
320                                            N2(ci) == N2_undo_change);
321                                 if (dir < 0)
322                                         call(mv->type+1, ci->focus, dir, ci->mark);
323                         }
324                         free(str);
325                         if (changed || N2(ci) == N2_undo_change)
326                                 call("Mode:set-num2", ci->focus, N2_undo_change);
327                 }
328                 mark_free(m);
329                 cnt -= 1;
330         }
331         /* When moving forward, move point.  When backward, leave point alone */
332         if (start) {
333                 mark_to_mark(ci->mark, start);
334                 mark_free(start);
335         }
336         return 1;
337 }
338
339 REDEF_CMD(emacs_swap)
340 {
341         /* collect the object behind point and insert it after the object
342          * after point.  With a +ve repeat count, insert after n objects.
343          * With -ve repeast, collect object after and insert behind n
344          * previous objects.  Object is determined by mv->type.
345          */
346         struct move_command *mv = container_of(ci->comm, struct move_command, cmd);
347         struct mark *start = NULL;
348         int cnt = mv->direction * RPT_NUM(ci);
349         int dir;
350
351         if (!ci->mark)
352                 return Enoarg;
353
354         if (cnt == 0)
355                 return 1;
356         if (cnt > 0) {
357                 dir = 1;
358         } else {
359                 dir = -1;
360                 cnt = -cnt;
361                 start = mark_dup(ci->mark);
362         }
363
364         while (cnt > 0) {
365                 struct mark *as, *ae, *bs, *be;
366                 char *astr, *bstr;
367
368                 call(mv->type, ci->focus, -dir, ci->mark);
369                 as = mark_dup(ci->mark);
370                 call(mv->type, ci->focus, dir, ci->mark);
371                 ae = mark_dup(ci->mark);
372                 /* as to ae is the object to be moved. */
373
374                 call(mv->type, ci->focus, dir, ci->mark);
375                 be = mark_dup(ci->mark);
376                 call(mv->type, ci->focus, -dir, ci->mark);
377                 bs = mark_dup(ci->mark);
378                 /* bs to be is the object to be swapped with.
379                  * bs must be after ae in the direction
380                  */
381                 if (mark_same(as, ae) ||
382                     mark_same(bs, be) ||
383                     bs->seq - ae->seq * dir < 0) {
384                         mark_to_mark(ci->mark, ae);
385                         mark_free(as);
386                         mark_free(ae);
387                         mark_free(bs);
388                         mark_free(be);
389                         break;
390                 }
391                 astr = call_ret(str, "doc:get-str", ci->focus,
392                                 0, as, NULL,
393                                 0, ae);
394                 bstr = call_ret(str, "doc:get-str", ci->focus,
395                                 0, bs, NULL,
396                                 0, be);
397                 mark_to_mark(ci->mark, ae);
398                 call("Replace", ci->focus, 1, as, bstr);
399                 mark_to_mark(ci->mark, be);
400                 call("Replace", ci->focus, 1, bs, astr, 1);
401                 if (dir < 0)
402                         call(mv->type, ci->focus, dir, ci->mark);
403                 free(astr);
404                 free(bstr);
405                 mark_free(as);
406                 mark_free(ae);
407                 mark_free(bs);
408                 mark_free(be);
409                 cnt -= 1;
410         }
411         /* When moving forward, move point.  When backward, leave point alone */
412         if (start) {
413                 mark_to_mark(ci->mark, start);
414                 mark_free(start);
415         }
416         return 1;
417 }
418
419 DEF_CMD(emacs_move_view_other)
420 {
421         /* If there is an 'other' pane', Send "K:Next" there */
422         struct pane *p;
423
424         /* '512' means 'fail if no other pane' */
425         p = call_ret(pane, "OtherPane", ci->focus, 512);
426         if (!p)
427                 return 1;
428         call("Mode:set-num", p, ci->num);
429         call("Keystroke", p, 0, NULL, "emacs-move-large-other");
430         return 1;
431 }
432
433 DEF_CMD(emacs_recenter)
434 {
435         int step = 0;
436         if (ci->num == NO_NUMERIC && N2(ci) == N2_recentre) {
437                 /* Repeated command - go to top, or bottom, or middle in order */
438                 switch (N2a(ci)) {
439                 default:
440                 case 0: /* was center, go to top */
441                         call("Move-View-Line", ci->focus, 1, ci->mark);
442                         step = 1;
443                         break;
444                 case 1: /* was top, go to bottom */
445                         call("Move-View-Line", ci->focus, -1, ci->mark);
446                         step = 2;
447                         break;
448                 case 2: /* was bottom, go to middle */
449                         call("Move-View-Line", ci->focus, 0, ci->mark);
450                         step = 0;
451                         break;
452                 }
453         } else if (ci->num == -NO_NUMERIC) {
454                 /* Move point to bottom */
455                 call("Move-View-Line", ci->focus, -1, ci->mark);
456                 step = 2;
457         } else if (ci->num != NO_NUMERIC) {
458                 /* Move point to display line N */
459                 call("Move-View-Line", ci->focus, ci->num, ci->mark);
460         } else {
461                 /* Move point to middle and refresh */
462                 call("Move-View-Line", ci->focus, 0, ci->mark);
463                 call("Window:refresh", ci->focus);
464         }
465         call("Mode:set-num2", ci->focus, N2_recentre | (step << 16));
466         return 1;
467 }
468
469 REDEF_CMD(emacs_simple);
470 REDEF_CMD(emacs_simple_num);
471 REDEF_CMD(emacs_simple_str);
472 static struct simple_command {
473         struct command  cmd;
474         char            *type safe;
475         char            *k safe;
476 } simple_commands[] = {
477         {CMD(emacs_simple), "Tile:next", "K:CX-o"},
478         {CMD(emacs_simple), "Tile:prev", "K:CX-O"},
479         {CMD(emacs_simple), "Tile:y+", "K:CX-^"},
480         {CMD(emacs_simple), "Tile:split-y", "K:CX-2"},
481         {CMD(emacs_simple), "Tile:split-x", "K:CX-3"},
482         {CMD(emacs_simple), "Tile:close", "K:CX-0"},
483         {CMD(emacs_simple), "Tile:bury", "K:A-B"},
484         {CMD(emacs_simple), "Window:new", "K:CX5-2"},
485         {CMD(emacs_simple), "Window:close", "K:CX5-0"},
486         {CMD(emacs_simple), "lib-server:done", "K:CX-#"},
487         {CMD(emacs_simple), "mode-swap-mark", "K:CX:C-X"},
488         {CMD(emacs_simple), "Abort", "K:C-G"},
489         {CMD(emacs_simple), "Cancel", "K:ESC"},
490         {CMD(emacs_simple), "NOP", "K:A-G"},
491         {CMD(emacs_simple), "NOP", "K:CX:C-G"},
492         {CMD(emacs_simple), "NOP", "K:CX4:C-G"},
493         {CMD(emacs_simple), "doc:save-file", "K:CX:C-S"},
494         {CMD(emacs_simple), "Commit", "K:CC:C-C"},
495         /* one day, this will be "find definition", now it is same as "find any" */
496         {CMD(emacs_simple_num), "interactive-cmd-git-grep", "K:CX:A-."},
497         {CMD(emacs_simple_str), "interactive-cmd-git-grep", "K:A-."},
498         {CMD(emacs_simple), "interactive-cmd-merge-mode", "K:A-m"},
499         {CMD(emacs_simple_str), "interactive-cmd-calc-replace", "K:A-#"},
500 };
501
502 REDEF_CMD(emacs_simple)
503 {
504         struct simple_command *sc = container_of(ci->comm, struct simple_command, cmd);
505
506         if (!ci->mark)
507                 return Enoarg;
508
509         return call(sc->type, ci->focus, ci->num, ci->mark);
510 }
511
512 REDEF_CMD(emacs_simple_num)
513 {
514         struct simple_command *sc = container_of(ci->comm, struct simple_command, cmd);
515
516         if (!ci->mark)
517                 return Enoarg;
518
519         return call(sc->type, ci->focus, RPT_NUM(ci), ci->mark);
520 }
521
522 REDEF_CMD(emacs_simple_str)
523 {
524         struct simple_command *sc = container_of(ci->comm, struct simple_command, cmd);
525         struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
526         char *str = NULL;
527
528         if (!ci->mark)
529                 return Enoarg;
530         if (call("selection:clear", ci->focus, 0, mk) >= 1)
531                 str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, mk);
532
533         return call(sc->type, ci->focus, ci->num, ci->mark, str, 0, mk);
534 }
535
536 DEF_CMD(emacs_close_others)
537 {
538         if (strcmp(ci->key, "K-1") == 0 && N2(ci) != N2_close_others)
539                 return Efallthrough;
540
541         if (call("Tile:close-others", ci->focus) <= 0)
542                 return Efalse;
543         call("Mode:set-num2", ci->focus, N2_close_others);
544         call("Message:modal", ci->focus, 0, NULL, "Type 1 to close more");
545         return 1;
546 }
547
548 DEF_CB(cnt_disp)
549 {
550         struct call_return *cr = container_of(ci->comm, struct call_return, c);
551
552         cr->i += 1;
553         return 1;
554 }
555
556 DEF_CMD(emacs_deactivate)
557 {
558         /* close-all popup has closed, see if it aborted */
559         if (ci->str)
560                 call("event:deactivate", ci->focus);
561         return 1;
562 }
563
564 DEF_CMD(emacs_exit)
565 {
566         struct call_return cr;
567
568         cr.c = cnt_disp;
569         cr.i = 0;
570         if (ci->num == NO_NUMERIC) {
571                 struct pane *p;
572
573                 /* If this is not only display, then refuse to exit */
574                 call_comm("editor:notify:all-displays", ci->focus, &cr.c);
575                 if (cr.i > 1) {
576                         call("Message", ci->focus, 0, NULL,
577                              "Cannot exit when there are multiple windows.");
578                         return 1;
579                 }
580
581                 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM");
582                 if (!p) {
583                         /* Probably called from a pop-up.  Abort the popup
584                          * and let the user try again.
585                          */
586                         call("Abort", ci->focus);
587                         return Efail;
588                 }
589                 attr_set_str(&p->attrs, "done-key", "emacs:deactivate");
590                 call("docs:show-modified", p);
591                 return 1;
592         } else
593                 call("event:deactivate", ci->focus);
594         return 1;
595 }
596
597 DEF_CMD(emacs_quote_insert)
598 {
599         int ret;
600         char buf[2] = ".";
601         const char *str;
602         bool first = N2(ci) != N2_undo_insert;
603
604         if (!ci->mark)
605                 return Enoarg;
606
607         if (call("selection:clear", ci->focus, 3, ci->mark) >= 1) {
608                 call("Replace", ci->focus, 1, ci->mark, NULL, !first);
609                 first = False;
610         } else
611                 call("selection:clear", ci->focus, 2, ci->mark);
612
613         str = ksuffix(ci, "K:CQ-");
614         if (!str[0]) {
615                 str = ksuffix(ci, "K:CQ:C-");
616                 if (str[0]) {
617                         buf[0] = str[0] & 0x1f;
618                         str = buf;
619                 } else
620                         str = "??";
621         }
622         ret = call("Replace", ci->focus, 1, ci->mark, str, !first);
623         call("Mode:set-num2", ci->focus, N2_undo_insert);
624
625         return ret < 0 ? ret : 1;
626 }
627
628 DEF_CMD(emacs_open_line)
629 {
630         if (call("Interactive:insert", ci->focus, 0, ci->mark, "\n") > 0)
631                 call("Move-Char", ci->focus, -1, ci->mark);
632         return 1;
633 }
634
635 DEF_CMD(emacs_undo)
636 {
637         int ret;
638
639         ret = call("doc:reundo", ci->focus, N2(ci) == N2_undo);
640         call("Mode:set-num2", ci->focus, N2_undo);
641         if (ret == Efalse)
642                 call("Message", ci->focus, 0, NULL,
643                      "No further undo information");
644
645         return 1;
646 }
647
648 REDEF_CMD(emacs_file_complete);
649 REDEF_CMD(emacs_doc_complete);
650 REDEF_CMD(emacs_cmd_complete);
651
652 DEF_CMD(find_complete)
653 {
654         char *type = ci->home->data;
655
656         if (strstarts(type, "file"))
657                 return emacs_file_complete_func(ci);
658         if (strcmp(type, "shellcmd") == 0)
659                 return emacs_file_complete_func(ci);
660         if (strcmp(type, "doc") == 0)
661                 return emacs_doc_complete_func(ci);
662         if (strcmp(type, "cmd") == 0)
663                 return emacs_cmd_complete_func(ci);
664         return Efallthrough;
665 }
666
667 DEF_CMD(find_done)
668 {
669         int ret;
670         char *type = ci->home->data;
671         char *str = call_ret(strsave, "doc:get-str", ci->focus);
672         const char *norm = NULL;
673         struct stat stb;
674
675         if (!str || !str[0])
676                 /* close with no string */
677                 ret = call("popup:close", ci->focus);
678         if (strcmp(type, "doc") == 0 &&
679             call_ret(pane, "docs:byname", ci->focus, 0, NULL, str) == NULL) {
680                 call("Message:modal", ci->focus, 0, NULL, "Document not found");
681                 return 1;
682         }
683         if (strstarts(type, "file")) {
684                 char *sl;
685                 bool can_create = True;
686                 bool can_create_dir = True;
687
688                 norm = file_normalize(ci->focus, str,
689                                       attr_find(ci->home->attrs,
690                                                 "initial_path"));
691                 sl = strrchr(norm, '/');
692                 while (sl && sl > norm) {
693                         /* Need to check directories exist. */
694                         *sl = 0;
695                         stb.st_mode = 0;
696                         stat(norm, &stb);
697                         if ((stb.st_mode & S_IFMT) == S_IFDIR)
698                                 break;
699                         if (stb.st_mode)
700                                 can_create_dir = False;
701                         can_create = False;
702                         sl = strrchr(norm, '/');
703                 }
704                 if (sl)
705                         *sl = '/';
706                 if (!can_create_dir) {
707                         call("Message:modal", ci->focus, 0, NULL,
708                              strconcat(ci->focus, norm, " is not a directory!!"));
709                         return 1;
710                 }
711                 if (!can_create) {
712                         /* Need to create directory first */
713                         if (strcmp(ci->key, "K:A:Enter") == 0) {
714                                 char *m = "Cannot create directory: ";
715                                 if (mkdir(norm, 0777) == 0) {
716                                         m = "Created directory: ";
717                                         attr_set_str(&ci->home->attrs,
718                                                      "prev_dir", NULL);
719                                         /* Trigger recalc on replace */
720                                         call("doc:replace", ci->focus, 0, NULL, "");
721                                 }
722                                 call("Message:modal", ci->focus, 0, NULL,
723                                      strconcat(ci->focus, m, norm));
724                                 return 1;
725                         }
726                         call("Message:modal", ci->focus, 0, NULL,
727                              strconcat(ci->focus,
728                                        "Use Alt-Enter to create directory ", norm));
729                         return 1;
730                 }
731         }
732         if (strcmp(type, "file") == 0 && norm &&
733             strcmp(ci->key, "K:Enter") == 0 &&
734             stat(norm, &stb) != 0) {
735                 call("Message:modal", ci->focus, 0, NULL,
736                      "File not found - use Alt-Enter to create");
737                 return 1;
738         }
739         if (strcmp(type, "file write") == 0 && norm &&
740             strcmp(ci->key, "K:Enter") == 0 &&
741             stat(norm, &stb) == 0) {
742                 call("Message:modal", ci->focus, 0, NULL,
743                      "File exists - use Alt-Enter to overwrite");
744                 return 1;
745         }
746         ret = call("popup:close", ci->focus, 0, NULL, norm ?: str);
747         return ret < 0 ? ret : 1;
748 }
749
750 struct find_helper {
751         char *name;
752         int want_prev;
753         struct pane *ret;
754         struct command c;
755 };
756
757 DEF_CB(find_helper)
758 {
759         struct find_helper *h = container_of(ci->comm, struct find_helper, c);
760         struct pane *p = ci->focus;
761         char *name;
762
763         if (!p)
764                 return 1;
765         if (!h->name) {
766                 if (h->want_prev) {
767                         /* want the pane before nothing, so the last.
768                          * So return this one and keep looking.
769                          */
770                         h->ret = p;
771                         return 1;
772                 } else {
773                         /* Want the pane that is after nothing, so
774                          * the first.  This one. All done.
775                          */
776                         h->ret = p;
777                         return Efalse;
778                 }
779         }
780         name = pane_attr_get(ci->focus, "doc-name");
781         if (!name)
782                 return 1;
783         if (strcmp(name, h->name) == 0) {
784                 if (h->want_prev) {
785                         /* Want the previous one, which is
786                          * already in ->ret
787                          */
788                         return Efalse;
789                 } else {
790                         /* Want the next one, so clear name
791                          * and keep going.
792                          */
793                         h->name = NULL;
794                         return 1;
795                 }
796         } else {
797                 if (h->want_prev) {
798                         /* This might be what I want - keep it in case */
799                         h->ret = p;
800                         return 1;
801                 } else {
802                         /* Don't want this - just keep going */
803                         return 1;
804                 }
805         }
806 }
807
808 DEF_CMD(find_prevnext)
809 {
810         /* Find the previous document lru or, which is "next" as we
811          * walk the list in mru order.
812          * When we find it, insert the name into ci->focus document
813          */
814         char *type = ci->home->data;
815         struct find_helper h;
816
817         if (strcmp(type, "doc") != 0)
818                 return Efallthrough;
819         h.name = attr_find(ci->home->attrs, "find-doc");
820         h.ret = NULL;
821         h.c = find_helper;
822         h.want_prev = strcmp(ci->key, "K:A-n") == 0;
823
824         call_comm("docs:byeach", ci->focus, &h.c);
825         if (h.ret) {
826                 char *name = pane_attr_get(h.ret, "doc-name");
827                 struct mark *m, *m2;
828
829                 attr_set_str(&ci->home->attrs, "find-doc", name);
830                 m = mark_new(ci->focus);
831                 m2 = m ? mark_dup(m) : NULL;
832                 call("doc:file", ci->focus, -1, m);
833                 call("doc:file", ci->focus, 1, m2);
834                 call("Replace", ci->focus, 1, m, name, 0, m2);
835                 mark_free(m);
836                 mark_free(m2);
837         }
838         return 1;
839 }
840
841 DEF_CMD(find_attr)
842 {
843         char *type = ci->home->data;
844
845         if (!ci->str)
846                 return Enoarg;
847
848         if (strcmp(type, "file") != 0)
849                 return Efallthrough;
850
851         if (strcmp(ci->str, "start-of-line") == 0) {
852                 char *lens = attr_find(ci->home->attrs, "path_lengths");
853                 int dir_start = 0, dir_end = 0, nondir_end = 0,
854                         basename_start = 0;
855                 if (lens)
856                         sscanf(lens, "%d %d %d %d", &dir_start, &dir_end,
857                                &nondir_end, &basename_start);
858
859                 if (dir_start > 0)
860                         comm_call(ci->comm2, "cb", ci->focus, dir_start,
861                                   ci->mark, "fg:grey+20,nobold,noinverse", 115);
862                 if (dir_end > dir_start)
863                         comm_call(ci->comm2, "cb", ci->focus, dir_end,
864                                   ci->mark, "fg:black,nobold,noinverse", 114);
865                 if (nondir_end > dir_end)
866                         comm_call(ci->comm2, "cb", ci->focus, nondir_end,
867                                   ci->mark, "fg:red-80,bold,inverse", 113);
868                 if (basename_start > nondir_end)
869                         comm_call(ci->comm2, "cb", ci->focus, basename_start,
870                                   ci->mark, "fg:magenta", 112);
871                 comm_call(ci->comm2, "cb", ci->focus, 10000, ci->mark,
872                           "fg:black", 111);
873         }
874         return 1;
875 }
876
877 DEF_CMD(find_check_replace)
878 {
879         char *str, *cp, *sl;
880         char *type = ci->home->data;
881         char *initial_path;
882         char *prev_dir;
883         struct stat stb;
884         int ipl; // Initial Path Len
885         int dir_start, dir_end, nondir_end, basename_start;
886         char nbuf[4 * (10+1) + 1], *lens;
887
888         if (strcmp(type, "file") != 0)
889                 return Efallthrough;
890
891         home_call(ci->home->parent, ci->key, ci->focus,
892                   ci->num, ci->mark, ci->str,
893                   ci->num2, ci->mark2, ci->str2);
894
895         /* The doc content can have 5 different sections that might
896          * be different colours.
897          *  - ignored prefix: grey - This inital_path followed by something
898          *          that looks like another path. "/" or "~/"
899          *  - True directories: black - directory part of the path that
900          *          exists and is a directory
901          *  - non-directory-in-path: red - directory part that exists but
902          *          is not a directory.  At most one component.
903          *  - non-existant name: magenta - directory path that doesn't exist.
904          *  - basename: black, whether it exists or not.
905          * These are found as:
906          *  - dir_start
907          *  - dir_end
908          *  - nondir_end
909          *  - basename_start
910          * These are all lengths from start of path.  They are all stored
911          * in a single attribute: "path_lengths".
912          */
913
914         str = call_ret(str, "doc:get-str", ci->focus);
915         if (!str)
916                 return 1;
917         sl = strrchr(str, '/');
918         if (!sl)
919                 sl = str;
920
921         prev_dir = attr_find(ci->home->attrs, "prev_dir");
922         if (prev_dir && strlen(prev_dir) == (size_t)(sl - str + 1) &&
923             strncmp(prev_dir, str, sl - str + 1) == 0)
924                 /* No change before last '/' */
925                 return 1;
926
927         initial_path = attr_find(ci->home->attrs, "initial_path");
928
929         if (!initial_path)
930                 return 1;
931         ipl = strlen(initial_path);
932         cp = str;
933         if (strncmp(str, initial_path, ipl) == 0 &&
934             (str[ipl] == '/' || (str[ipl] == '~' &&
935                                 (str[ipl+1] == '/' ||
936                                  str[ipl+1] == 0))))
937                 cp = str + ipl;
938
939         dir_start = utf8_strnlen(str, cp - str);
940
941         basename_start = utf8_strnlen(str, sl - str + 1);
942         stb.st_mode = 0;
943         if (sl < cp)
944                 sl = cp;
945         while (sl > cp) {
946                 const char *path;
947                 *sl = 0;
948                 path = file_normalize(ci->focus, str, initial_path);
949                 stat(path, &stb);
950                 *sl = '/';
951                 if (stb.st_mode)
952                         break;
953                 sl -= 1;
954                 while (sl > cp && *sl != '/')
955                         sl -= 1;
956         }
957         nondir_end = utf8_strnlen(str, sl - str + 1);
958         dir_end = nondir_end;
959         if (stb.st_mode != 0 &&
960             (stb.st_mode & S_IFMT) != S_IFDIR) {
961                 /* There is a non-dir on the path */
962                 while (sl > cp && sl[-1] != '/')
963                         sl -= 1;
964                 /* This must actually be a dir */
965                 dir_end = utf8_strnlen(str, sl - str);
966         }
967         snprintf(nbuf, sizeof(nbuf), "%d %d %d %d",
968                  dir_start, dir_end, nondir_end, basename_start);
969         lens = attr_find(ci->home->attrs, "path_lengths");
970         if (!lens || strcmp(lens, nbuf) != 0)
971                 attr_set_str(&ci->home->attrs, "path_lengths", nbuf);
972         sl = strrchr(str, '/');
973         if (sl) {
974                 sl[1] = 0;
975                 attr_set_str(&ci->home->attrs, "prev_dir", str);
976         }
977         return 1;
978 }
979
980 static struct map *fh_map;
981 DEF_LOOKUP_CMD(find_handle, fh_map);
982
983 static void findmap_init(void)
984 {
985         if (fh_map)
986                 return;
987         fh_map = key_alloc();
988         key_add(fh_map, "K:Tab", &find_complete);
989         key_add(fh_map, "K:Enter", &find_done);
990         key_add(fh_map, "K:A:Enter", &find_done);
991         key_add(fh_map, "K:A-p", &find_prevnext);
992         key_add(fh_map, "K:A-n", &find_prevnext);
993         key_add(fh_map, "map-attr", &find_attr);
994         key_add(fh_map, "doc:replace", &find_check_replace);
995 }
996
997 static const char * safe file_normalize(struct pane *p safe,
998                                         const char *path,
999                                         const char *initial_path)
1000 {
1001         int len = strlen(initial_path?:"");
1002         char *dir;
1003
1004         if (!path)
1005                 return ".";
1006         if (initial_path && strncmp(path, initial_path, len) == 0) {
1007                 if (path[len] == '/' || (path[len] == '~' &&
1008                                          (path[len+1] == '/' || path[len+1] == 0)))
1009                         path = path + len;
1010         }
1011         if (path[0] == '/')
1012                 return path;
1013         if (path[0] == '~' && (path[1] == '/' || path[1] == 0)) {
1014                 char *home = getenv("HOME");
1015                 if (!home)
1016                         home = "/";
1017                 if (home[strlen(home) - 1] == '/' && path[1] == '/')
1018                         path += 1;
1019                 return strconcat(p, home, path+1);
1020         }
1021         dir = pane_attr_get(p, "dirname");
1022         if (!dir)
1023                 dir = "/";
1024         if (dir[strlen(dir)-1] == '/')
1025                 return strconcat(p, dir, path);
1026         return strconcat(p, dir, "/", path);
1027 }
1028
1029 DEF_CMD(emacs_findfile)
1030 {
1031         int fd;
1032         struct pane *p, *par;
1033         const char *path;
1034         char buf[PATH_MAX];
1035         char *e;
1036
1037         path = pane_attr_get(ci->focus, "dirname");
1038         if (path) {
1039                 strcpy(buf, path);
1040                 path = buf;
1041         }
1042         if (!path) {
1043                 strcpy(buf, "/");
1044                 path = buf;
1045         }
1046         e = buf + strlen(buf);
1047         if (e < buf + sizeof(buf)-1 && e > buf && e[-1] != '/') {
1048                 *e++ = '/';
1049                 *e++ = '\0';
1050         }
1051
1052         if (ksuffix(ci, "File Found:")[0] == 0) {
1053                 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
1054                              0, NULL, path);
1055                 if (!p)
1056                         return Efail;
1057
1058                 if (ksuffix(ci, "K:CX44")[0]) {
1059                         attr_set_str(&p->attrs, "prompt",
1060                                      "Find File Popup");
1061                         attr_set_str(&p->attrs, "done-key",
1062                                      "File Found:Popup");
1063                 } else if (ksuffix(ci, "K:CX4")[0]) {
1064                         attr_set_str(&p->attrs, "prompt",
1065                                      "Find File Other Window");
1066                         attr_set_str(&p->attrs, "done-key",
1067                                      "File Found:Other Window");
1068                 } else {
1069                         attr_set_str(&p->attrs, "prompt", "Find File");
1070                         attr_set_str(&p->attrs, "done-key",
1071                                      "File Found:This Window");
1072                 }
1073                 call("doc:set-name", p, 0, NULL, "Find File");
1074
1075                 p = pane_register(p, 0, &find_handle.c, "file");
1076                 if (!p)
1077                         return Efail;
1078                 attr_set_str(&p->attrs, "initial_path", path);
1079                 call("attach-history", p, 0, NULL, "*File History*");
1080                 return 1;
1081         }
1082         if (!ci->str)
1083                 /* Aborted */
1084                 return Efail;
1085
1086         path = file_normalize(ci->focus, ci->str, path);
1087
1088         if (!path)
1089                 return Efail;
1090
1091         fd = open(path, O_RDONLY);
1092         if (fd >= 0) {
1093                 p = call_ret(pane, "doc:open", ci->focus, fd, NULL, path);
1094                 close(fd);
1095         } else
1096                 /* '4' says 'allow create' */
1097                 p = call_ret(pane, "doc:open", ci->focus, -1, NULL, path, 4);
1098         if (!p) {
1099                 char *m = NULL;
1100                 asprintf(&m, "Failed to open file: %s", path);
1101                 call("Message", ci->focus, 0, NULL, m);
1102                 free(m);
1103                 return Efail;
1104         }
1105         if (strcmp(ci->key, "File Found:Other Window") == 0) {
1106                 struct pane *this = call_ret(pane, "ThisPane", ci->focus);
1107                 par = home_call_ret(pane, ci->focus, "DocPane", p);
1108                 if (par && par != this) {
1109                         pane_take_focus(par);
1110                         return 1;
1111                 }
1112                 par = call_ret(pane, "OtherPane", ci->focus);
1113         } else if (strcmp(ci->key, "File Found:Popup") == 0) {
1114                 par = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "MD3tsa");
1115         } else
1116                 par = call_ret(pane, "ThisPane", ci->focus);
1117
1118         if (!par) {
1119                 pane_close(p);
1120                 return Efail;
1121         }
1122
1123         p = home_call_ret(pane, p, "doc:attach-view", par, 1);
1124         if (p)
1125                 pane_take_focus(p);
1126
1127         return p ? 1 : Efail;
1128 }
1129
1130 DEF_CMD(emacs_writefile)
1131 {
1132         /* Request to write to a different file */
1133         struct pane *p;
1134         const char *path;
1135         char buf[PATH_MAX];
1136         char *e;
1137
1138         if (call("doc:write-file", ci->focus, NO_NUMERIC) >= 0) {
1139                 /* It should have been an error ! */
1140                 const char *doc = pane_attr_get(ci->focus, "doc-name");
1141                 if (doc)
1142                         call("Message", ci->focus, 0, NULL,
1143                              strconcat(ci->focus, "Document ", doc,
1144                                        " cannot be written"));
1145                 else
1146                         call("Message", ci->focus, 0, NULL,
1147                              "This document cannot be written");
1148                 return Efail;
1149         }
1150         path = pane_attr_get(ci->focus, "dirname");
1151         if (path) {
1152                 strcpy(buf, path);
1153                 path = buf;
1154         }
1155         if (!path) {
1156                 strcpy(buf, "/");
1157                 path = buf;
1158         }
1159         e = buf + strlen(buf);
1160         if (e < buf + sizeof(buf)-1 && e > buf && e[-1] != '/') {
1161                 *e++ = '/';
1162                 *e++ = '\0';
1163         }
1164
1165         p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
1166                      0, NULL, path);
1167         if (!p)
1168                 return Efail;
1169         attr_set_str(&p->attrs, "prompt", "Write File");
1170         attr_set_str(&p->attrs, "done-key",
1171                      strconcat(p, "emacs:write_file:", path));
1172         call("doc:set-name", p, 0, NULL, "Write File");
1173         p = pane_register(p, 0, &find_handle.c, "file write");
1174         if (!p)
1175                 return Efail;
1176         attr_set_str(&p->attrs, "initial_path", path);
1177         call("attach-history", p, 0, NULL, "*File History*");
1178         return 1;
1179 }
1180
1181 DEF_CMD(emacs_do_writefile)
1182 {
1183         const char *path = ksuffix(ci, "emacs:write_file:");
1184         if (!ci->str || !path)
1185                 return Efail;
1186
1187         path = file_normalize(ci->focus, ci->str, path);
1188         if (!path)
1189                 return Efail;
1190         if (call("doc:write-file", ci->focus, NO_NUMERIC, NULL, path) <= 0) {
1191                 call("Message", ci->focus, 0, NULL,
1192                      strconcat(ci->focus, "Failed to write to ", path));
1193                 return Efail;
1194         }
1195         return 1;
1196 }
1197
1198 DEF_CMD(emacs_insertfile)
1199 {
1200         /* Request to insert content of a file at point */
1201         struct pane *p;
1202         const char *path;
1203         char buf[PATH_MAX];
1204         char *e;
1205         int ret;
1206
1207         ret = call("doc:insert-file", ci->focus, NO_NUMERIC);
1208         if (ret != Enoarg) {
1209                 const char *doc;
1210                 if (ret == Efail)
1211                         /* Message already given */
1212                         return ret;
1213                 doc = pane_attr_get(ci->focus, "doc-name");
1214                 if (doc)
1215                         call("Message", ci->focus, 0, NULL,
1216                              strconcat(ci->focus, "Document ", doc,
1217                                        " cannot receive insertions"));
1218                 else
1219                         call("Message", ci->focus, 0, NULL,
1220                              "This document cannot receive insertions");
1221                 return Efail;
1222         }
1223         path = pane_attr_get(ci->focus, "dirname");
1224         if (path) {
1225                 strcpy(buf, path);
1226                 path = buf;
1227         }
1228         if (!path) {
1229                 strcpy(buf, "/");
1230                 path = buf;
1231         }
1232         e = buf + strlen(buf);
1233         if (e < buf + sizeof(buf)-1 && e > buf && e[-1] != '/') {
1234                 *e++ = '/';
1235                 *e++ = '\0';
1236         }
1237
1238         p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
1239                      0, NULL, path);
1240         if (!p)
1241                 return Efail;
1242         attr_set_str(&p->attrs, "prompt", "Insert File");
1243         attr_set_str(&p->attrs, "done-key",
1244                      strconcat(p, "emacs:insert_file:", path));
1245         call("doc:set-name", p, 0, NULL, "Insert File");
1246         p = pane_register(p, 0, &find_handle.c, "file");
1247         if (!p)
1248                 return Efail;
1249         attr_set_str(&p->attrs, "initial_path", path);
1250         call("attach-history", p, 0, NULL, "*File History*");
1251         return 1;
1252 }
1253
1254 DEF_CMD(emacs_do_insertfile)
1255 {
1256         const char *path = ksuffix(ci, "emacs:insert_file:");
1257         int fd;
1258
1259         if (!ci->str || !path)
1260                 return Efail;
1261
1262         path = file_normalize(ci->focus, ci->str, path);
1263         if (!path)
1264                 return Efail;
1265         fd = open(path, O_RDONLY);
1266         if (fd >= 0) {
1267                 if (call("doc:insert-file", ci->focus, fd) <= 0) {
1268                         close(fd);
1269                         call("Message", ci->focus, 0, NULL,
1270                              strconcat(ci->focus, "Failed to insert ", path));
1271                         return Efail;
1272                 }
1273                 close(fd);
1274                 return 1;
1275         } else {
1276                 char *m = strconcat(ci->focus,
1277                                     "Failed to inser file: ", path);
1278                 call("Message", ci->focus, 0, NULL, m);
1279                 return Efail;
1280         }
1281 }
1282
1283 REDEF_CMD(emacs_file_complete)
1284 {
1285         /* Extract a directory name and a basename from the document.
1286          * Find a document for the directory and attach as a completing
1287          * popup menu
1288          */
1289         const char *str;
1290         const char *d, *b;
1291         int fd;
1292         struct pane *par, *pop, *docp, *p;
1293         struct call_return cr;
1294         char *type = ci->home->data;
1295         char *initial = attr_find(ci->home->attrs, "initial_path");
1296         int wholebuf = strcmp(type, "file") == 0;
1297         struct mark *st;
1298
1299         if (!ci->mark)
1300                 return Enoarg;
1301
1302         st = mark_dup(ci->mark);
1303         call("doc:file", ci->focus, -1, st);
1304
1305         str = call_ret(strsave, "doc:get-str", ci->focus, 0, st, NULL,
1306                        0, ci->mark);
1307         if (!str)
1308                 return Einval;
1309         if (wholebuf) {
1310                 d = str;
1311         } else {
1312                 /* Need guess which part of the buf is the file name.
1313                  * This probably needs to be configurable, but lets
1314                  * decide the file name starts immediately after a
1315                  * space, or a '=' or ':' which is followed by a
1316                  * '/'.
1317                  */
1318                 initial = "";
1319                 d = str + strlen(str);
1320                 while (d > str &&
1321                        !(d[-1] == ' ' ||
1322                          (strchr(":=", d[-1]) && d[0] == '/')))
1323                         d -= 1;
1324         }
1325         d = file_normalize(ci->focus, d, initial);
1326         b = strrchr(d, '/');
1327         if (b) {
1328                 b += 1;
1329                 d = strnsave(ci->focus, d, b-d);
1330         } else {
1331                 b = d;
1332                 d = strsave(ci->focus, ".");
1333         }
1334         fd = open(d, O_DIRECTORY|O_RDONLY);
1335         if (fd < 0) {
1336                 call("Message:modal", ci->focus, 0, NULL,
1337                      strconcat(ci->focus, "Cannot open directory \"", d, "\""));
1338                 return Efail;
1339         }
1340         /* 32 means quiet */
1341         docp = call_ret(pane, "doc:open", ci->focus, fd, NULL, d, 32);
1342         close(fd);
1343         if (!docp)
1344                 return Efail;
1345         pop = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM1r");
1346         if (!pop)
1347                 return Efail;
1348         par = home_call_ret(pane, docp, "doc:attach-view", pop,
1349                             0, NULL, "simple");
1350         if (!par)
1351                 return Efail;
1352
1353         attr_set_str(&par->attrs, "line-format", "%name%suffix");
1354         attr_set_str(&par->attrs, "heading", "");
1355         attr_set_str(&par->attrs, "done-key", "Replace");
1356         p = call_ret(pane, "attach-render-complete", par, 0, NULL, "format");
1357         if (!p)
1358                 return Efail;
1359         cr = call_ret(all, "Complete:prefix", p, 1, NULL, b,
1360                       0, NULL, "format:plain");
1361         if (cr.s && cr.ret-1 > 1 && cr.s[strlen(cr.s)-1] == '/')
1362                 /* Trailing slash on non-unique name must be removed */
1363                 cr.s[strlen(cr.s)-1] = '\0';
1364         if (cr.s && (strlen(cr.s) <= strlen(b) && cr.ret-1 > 1)) {
1365                 /* We need the dropdown - delete prefix and drop-down will
1366                  * insert result.
1367                  */
1368                 struct mark *start;
1369
1370                 start = mark_dup(ci->mark);
1371                 call("doc:char", ci->focus, -strlen(b), start);
1372                 call("Replace", ci->focus, 1, start);
1373                 mark_free(start);
1374
1375                 return 1;
1376         }
1377         if (cr.s) {
1378                 /* Replace 'b' with the result. */
1379                 struct mark *start;
1380
1381                 start = mark_dup(ci->mark);
1382                 call("doc:char", ci->focus, -strlen(b), start);
1383                 call("Replace", ci->focus, 1, start, cr.s);
1384                 mark_free(start);
1385         } else {
1386                 call("Message:modal", ci->focus, 0, NULL,
1387                      strconcat(ci->focus, "No completion found for \"", b, "\"",
1388                                " in \"", d, "\""));
1389         }
1390         /* Now need to close the popup */
1391         pane_close(pop);
1392         return 1;
1393 }
1394
1395 DEF_CMD(emacs_finddoc)
1396 {
1397         struct pane *p, *par;
1398
1399         if (ksuffix(ci, "Doc Found:")[0] == 0) {
1400                 struct pane *dflt;
1401                 char *defname = NULL;
1402
1403                 dflt = call_ret(pane, "docs:choose", ci->focus);
1404                 if (dflt)
1405                         defname = pane_attr_get(dflt, "doc-name");
1406
1407                 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
1408                              0, NULL, "");
1409                 if (!p)
1410                         return Efail;
1411
1412                 if (defname)
1413                         attr_set_str(&p->attrs, "default", defname);
1414                 if (ksuffix(ci, "K:CX44")[0]) {
1415                         attr_set_str(&p->attrs, "prompt",
1416                                      "Find Document Popup");
1417                         attr_set_str(&p->attrs, "done-key",
1418                                      "Doc Found:Popup");
1419                 } else if (ksuffix(ci, "K:CX4")[0]) {
1420                         attr_set_str(&p->attrs, "prompt",
1421                                      "Find Document Other Window");
1422                         attr_set_str(&p->attrs, "done-key",
1423                                      "Doc Found:Other Window");
1424                 } else {
1425                         attr_set_str(&p->attrs, "prompt", "Find Document");
1426                         attr_set_str(&p->attrs, "done-key",
1427                                      "Doc Found:This Window");
1428                 }
1429                 call("doc:set-name", p, 0, NULL, "Find Document", -1);
1430
1431                 pane_register(p, 0, &find_handle.c, "doc");
1432                 return 1;
1433         }
1434
1435         if (!ci->str)
1436                 /* Aborted */
1437                 return Efail;
1438
1439         p = call_ret(pane, "docs:byname", ci->focus, 0, NULL, ci->str);
1440         if (!p)
1441                 return Efail;
1442
1443         if (strcmp(ci->key, "Doc Found:Other Window") == 0) {
1444                 struct pane *this = call_ret(pane, "ThisPane", ci->focus);
1445                 par = home_call_ret(pane, ci->focus, "DocPane", p);
1446                 if (par && par != this) {
1447                         pane_take_focus(par);
1448                         return 1;
1449                 }
1450                 par = call_ret(pane, "OtherPane", ci->focus);
1451         } else if (strcmp(ci->key, "Doc Found:Popup") == 0) {
1452                 par = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "MD3tsa");
1453         } else
1454                 par = call_ret(pane, "ThisPane", ci->focus);
1455         if (!p || !par)
1456                 return Efail;
1457
1458         p = home_call_ret(pane, p, "doc:attach-view", par, 1);
1459         if (p)
1460                 pane_take_focus(p);
1461         return p ? 1 : Efail;
1462 }
1463
1464 REDEF_CMD(emacs_doc_complete)
1465 {
1466         /* Extract a document name from the document.
1467          * Attach the 'docs' document as a completing popup menu
1468          */
1469         char *str;
1470         struct pane *pop, *p = NULL;
1471         struct call_return cr;
1472
1473         if (!ci->mark)
1474                 return Enoarg;
1475
1476         str = call_ret(strsave, "doc:get-str", ci->focus);
1477         if (!str)
1478                 return Einval;
1479         pop = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM1r");
1480         if (!pop)
1481                 return Efail;
1482         p = call_ret(pane, "docs:complete", pop);
1483         if (!p)
1484                 return Efail;
1485         cr = call_ret(all, "Complete:prefix", p, 1, NULL, str);
1486         if (cr.s && (strlen(cr.s) <= strlen(str) && cr.ret - 1 > 1)) {
1487                 /* We need the dropdown */
1488                 struct mark *start;
1489
1490                 start = mark_dup(ci->mark);
1491                 call("doc:set-ref", ci->focus, 1, start);
1492
1493                 call("Replace", ci->focus, 1, start);
1494                 mark_free(start);
1495                 return 1;
1496         }
1497         if (cr.s) {
1498                 /* Replace the prefix with the new value */
1499                 struct mark *start;
1500
1501                 start = mark_dup(ci->mark);
1502                 call("doc:set-ref", ci->focus, 1, start);
1503
1504                 call("Replace", ci->focus, 1, start, cr.s);
1505                 mark_free(start);
1506         }
1507         /* Now need to close the popup */
1508         pane_close(pop);
1509         return 1;
1510 }
1511
1512 DEF_CMD(emacs_viewdocs)
1513 {
1514         struct pane *p, *par;
1515         struct pane *docs;
1516
1517         docs = call_ret(pane, "docs:byname", ci->focus, 0, NULL, "*Documents*");
1518         if (!docs)
1519                 return Efail;
1520
1521         if (ksuffix(ci, "K:CX44")[0]) {
1522                 par = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "MD3tsa");
1523         } else if (ksuffix(ci, "K:CX4")[0]) {
1524                 struct pane *this = call_ret(pane, "ThisPane", ci->focus);
1525                 par = home_call_ret(pane, ci->focus, "DocPane", docs);
1526                 if (par && par != this) {
1527                         pane_take_focus(par);
1528                         return 1;
1529                 }
1530                 par = call_ret(pane, "OtherPane", ci->focus);
1531         } else {
1532                 par = call_ret(pane, "ThisPane", ci->focus);
1533         }
1534
1535         if (!par)
1536                 return Efail;
1537
1538         p = home_call_ret(pane, docs, "doc:attach-view", par, 1);
1539         return !!p;
1540 }
1541
1542 struct pcb {
1543         struct command c;
1544         struct pane *doc safe;
1545         struct pane *p;
1546 };
1547
1548 DEF_CMD(choose_pane)
1549 {
1550         struct pcb *cb = container_of(ci->comm, struct pcb, c);
1551         struct pane *d;
1552
1553         if (cb->p || !ci->str)
1554                 return 1;
1555         d = call_ret(pane, "docs:byname", ci->focus, 0, NULL, ci->str);
1556         if (d)
1557                 cb->p = home_call_ret(pane, ci->focus, "DocPane", d);
1558         if (cb->p)
1559                 home_call(cb->doc, "doc:attach-view", cb->p, 1);
1560         return 1;
1561 }
1562
1563 DEF_CB(shellcb)
1564 {
1565         char *str;
1566
1567         if (strcmp(ci->key, "cb:timer") == 0) {
1568                 /* If focus has moved, don't show shell window,
1569                  * probably a popup was requested by command.
1570                  */
1571                 if (!pane_has_focus(ci->home))
1572                         /* call back when lines or eof */
1573                         return 2;
1574         }
1575         if (strcmp(ci->key, "cb:eof") != 0) {
1576                 struct pane *par;
1577                 if (ci->str && strchr(ci->str, 'P'))
1578                         par = call_ret(pane, "PopupTile", ci->home, 0, NULL, "MD3tsa");
1579                 else
1580                         par = call_ret(pane, "OtherPane", ci->home);
1581                 if (par)
1582                         home_call(ci->focus, "doc:attach-view", par, 1);
1583                 return 1;
1584         }
1585         str = call_ret(str, "doc:get-str", ci->focus);
1586         if (!str || !*str) {
1587                 free(str);
1588                 if (ci->num == 0)
1589                         asprintf(&str, "(shell command completed with no output)");
1590                 else if (ci->num > 0)
1591                         asprintf(&str, "(shell command completed with no output (exit code %d))", ci->num);
1592                 else
1593                         asprintf(&str, "(shell command completed with no output (signalled %d))", -ci->num);
1594         }
1595         call("Message", ci->home, 0, NULL, str);
1596         free(str);
1597         return 1;
1598 }
1599
1600 DEF_CB(shell_insert_cb)
1601 {
1602         char *str = call_ret(str, "doc:get-str", ci->focus);
1603         struct mark *mk = call_ret(mark2, "doc:point", ci->home);
1604
1605         if (call("selection:clear", ci->home, 3, mk) >= 1)
1606                 call("Replace", ci->home, 1, mk);
1607         call("Replace", ci->home, 0, NULL, str);
1608         free(str);
1609         return 1;
1610 }
1611
1612 DEF_CMD(emacs_shell)
1613 {
1614         char *name = "*Shell Command Output*";
1615         struct pane *p, *doc, *par, *sc;
1616         char *path, *input = NULL;
1617         struct pcb cb;
1618         bool interpolate, pipe, popup;
1619
1620         if (strcmp(ci->key, "Shell Command") != 0) {
1621                 char *dirname;
1622                 char aux[4] = "";
1623
1624                 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2", 0, NULL,
1625                              ci->str ?: "");
1626                 if (!p)
1627                         return Efail;
1628                 dirname = call_ret(strsave, "get-attr", ci->focus, 0, NULL, "dirname");
1629                 attr_set_str(&p->attrs, "dirname", dirname ?: ".");
1630                 attr_set_str(&p->attrs, "prompt", "Shell command");
1631                 if (ci->num == 0 || ci->num == 1)
1632                         strcat(aux, "i"); // interpolate
1633                 if (ci->num == 4)
1634                         strcat(aux, "P"); // popup
1635                 if (strcmp(ci->key, "K:A-|") == 0)
1636                         strcat(aux, "p"); // pipe from selection
1637                 attr_set_str(&p->attrs, "popup-aux", aux);
1638                 attr_set_str(&p->attrs, "done-key", "Shell Command");
1639                 call("doc:set-name", p, 0, NULL, "Shell Command", -1);
1640                 p = call_ret(pane, "attach-history", p, 0, NULL, "*Shell History*");
1641                 if (p)
1642                         p = pane_register(p, 0, &find_handle.c, "shellcmd");
1643                 if (!p)
1644                         return Efail;
1645                 if (ci->comm2)
1646                         comm_call(ci->comm2, "cb", p);
1647                 return 1;
1648         }
1649         if (!ci->str) {
1650                 call("Message", ci->focus, 0, NULL, "Shell command aborted");
1651                 return 1;
1652         }
1653
1654         interpolate = ci->str2 && strchr(ci->str2, 'i');
1655         pipe = ci->str2 && strchr(ci->str2, 'p');
1656         popup = ci->str2 && strchr(ci->str2, 'P');
1657
1658         if (pipe) {
1659                 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
1660                 if (mk) {
1661                         input = call_ret(str, "doc:get-str", ci->focus,
1662                                          0, NULL, NULL, 0, mk);
1663                         /* make the selection replacable */
1664                         attr_set_int(&mk->attrs, "selection:active", 3);
1665                 }
1666         }
1667         doc = call_ret(pane, "doc:from-text", ci->focus, 0, NULL, name, 0, NULL,
1668                        input ?: "");
1669         free(input);
1670         if (!doc)
1671                 return Efail;
1672
1673         path = pane_attr_get(ci->focus, "dirname");
1674         attr_set_str(&doc->attrs, "dirname", path);
1675
1676         /* shellcmd is attached directly to the document, not in the view
1677          * stack.  It is go-between for document and external command.
1678          * We don't need a doc attachment as no point is needed - we
1679          * always insert at the end.
1680          */
1681         sc = call_ret(pane, "attach-shellcmd", doc, pipe ? 22 : 4,
1682                       NULL, ci->str, 0, NULL, path);
1683         if (!sc)
1684                 call("doc:replace", doc, 0, NULL,
1685                      "Failed to run command - sorry\n");
1686         if (call("text-search", doc, 0, NULL, "diff|(stg|git).*show",
1687                  0, NULL, ci->str) > 0)
1688                 attr_set_str(&doc->attrs, "view-default", "diff");
1689
1690         /* Close old shell docs, but if one is visible in current frame, replace
1691          * it with doc
1692          */
1693         cb.c = choose_pane;
1694         cb.doc = doc;
1695         cb.p = NULL;
1696         call_comm("editor:notify:shell-reuse", ci->focus, &cb.c);
1697         if (!cb.p) {
1698                 /* choose_pane didn't attach, so set a callback to do
1699                  * it when there is enough content.
1700                  */
1701                 if (sc) {
1702                         /* If it take more than 500msec, or includes 2
1703                          * or more lines, we'll show in a pane, else
1704                          * just show as a message.
1705                          */
1706                         if (!interpolate)
1707                                 home_call_comm(sc, "shellcmd:set-callback",
1708                                                ci->focus, &shellcb,
1709                                                500, NULL, popup ? "P": "",
1710                                                2);
1711                 } else {
1712                         if (popup)
1713                                 par = call_ret(pane, "PopupTile", ci->focus,
1714                                                0, NULL, "MD3tsa");
1715                         else
1716                                 par = call_ret(pane, "OtherPane", ci->focus);
1717                         if (!par)
1718                                 return Efail;
1719                         home_call(doc, "doc:attach-view", par, 1);
1720                 }
1721         }
1722         if (sc && interpolate)
1723                 /* Need a callback when the pipe command finished */
1724                 home_call_comm(sc, "shellcmd:set-callback", ci->focus,
1725                                &shell_insert_cb);
1726
1727         return 1;
1728 }
1729
1730 DEF_CMD(emacs_num)
1731 {
1732         int rpt = ci->num;
1733         const char *last = ksuffix(ci, "K:A-");
1734         int neg = 0;
1735
1736         if (rpt < 0) {
1737                 neg = 1;
1738                 rpt = -rpt;
1739         }
1740         if (rpt == NO_NUMERIC)
1741                 rpt = 0;
1742
1743         rpt = rpt * 10 + *last - '0';
1744
1745         call("Mode:set-num", ci->focus, neg ? -rpt : rpt);
1746         call("Mode:set-num2", ci->focus, ci->num2);
1747         return 1;
1748 }
1749
1750 DEF_CMD(emacs_neg)
1751 {
1752         call("Mode:set-num", ci->focus, - ci->num);
1753         call("Mode:set-num2", ci->focus, ci->num2);
1754         return 1;
1755 }
1756
1757 DEF_CMD(emacs_prefix)
1758 {
1759         /* as a generic arg (ctrl-U) which is positive and
1760          * as as a repeat-count of 4, but is different to 4.
1761          * I should probably allow digits to alter the number.
1762          */
1763         call("Mode:set-num", ci->focus, 4);
1764         return 1;
1765 }
1766
1767 DEF_CMD(emacs_kill_doc)
1768 {
1769         if (ci->num <= 0 || ci->num == NO_NUMERIC) {
1770                 /* Check if modified. */
1771                 char *m = pane_attr_get(ci->focus, "doc-modified");
1772                 char *f = NULL;
1773                 if (m && strcmp(m, "yes") == 0)
1774                         f = pane_attr_get(ci->focus, "filename");
1775                 if (f) {
1776                         call("Message:modal", ci->focus, 0, NULL,
1777                              "Document is modified - please save or give prefix arg");
1778                         return 1;
1779                 }
1780         }
1781         return call("doc:destroy", ci->focus);
1782 }
1783
1784 DEF_CMD(emacs_revisit)
1785 {
1786         return call("doc:load-file", ci->focus, 2, NULL, NULL, -1);
1787 }
1788
1789 DEF_CMD(emacs_save_all)
1790 {
1791         int ret = call("docs:save-all", ci->focus, 0, NULL, NULL, 1);
1792
1793         if (ret == 1) {
1794                 call("Message", ci->focus, 0, NULL, "No files need to be saved.");
1795                 return 1;
1796         }
1797         if (ci->num == NO_NUMERIC) {
1798                 struct pane *p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM");
1799                 if (p)
1800                         return call("docs:show-modified", p);
1801         }
1802         return call("docs:save-all", ci->focus);
1803 }
1804
1805 DEF_CMD(emacs_reposition)
1806 {
1807         struct mark *m;
1808         int repoint = attr_find_int(ci->focus->attrs, "emacs-repoint");
1809
1810         if (repoint != -1) {
1811                 /* Move point to end of display, if that is in
1812                  * the right direction.  That will mean it has moved
1813                  * off the display.
1814                  */
1815                 m = mark_at_point(ci->focus, NULL, MARK_UNGROUPED);
1816                 if (m) {
1817                         struct mark *m2 = mark_dup(m);
1818                         call("Move-CursorXY", ci->focus, 0, m, NULL,
1819                              0, NULL, NULL,
1820                              INT_MAX, repoint < 0 ? ci->focus->h-1 : 0);
1821                         if (repoint < 0)
1822                                 /* can only move point backwards */
1823                                 if (m->seq < m2->seq)
1824                                         call("Move-to", ci->focus, 0, m);
1825                         if (repoint > 0)
1826                                 /* can only move point forwards */
1827                                 if (m->seq > m2->seq)
1828                                         call("Move-to", ci->focus, 0, m);
1829                         mark_free(m);
1830                         mark_free(m2);
1831                 }
1832                 attr_set_str(&ci->focus->attrs, "emacs-repoint", NULL);
1833         }
1834         return Efallthrough;
1835 }
1836
1837 DEF_CMD(emacs_start_search)
1838 {
1839         struct pane *p = NULL, *hp;
1840         int mode = 0;
1841
1842         hp = call_ret(pane, "attach-emacs-search-highlight", ci->focus);
1843
1844         if (hp)
1845                 p = call_ret(pane, "PopupTile", hp, 0, NULL, "TR2",
1846                              0, NULL, "");
1847
1848         if (!p)
1849                 return Efail;
1850         home_call(hp, "highlight:set-popup", p);
1851
1852         attr_set_str(&p->attrs, "prompt", "Search");
1853         attr_set_str(&p->attrs, "done-key", "Search String");
1854
1855         hp = call_ret(pane, "attach-history", p, 0, NULL, "*Search History*");
1856         if (hp)
1857                 p = hp;
1858
1859         call("doc:set-name", p, 0, NULL, "Search", -1);
1860         if (strcmp(ci->key, "K:C-R") == 0)
1861                 mode |= 1;
1862         if (strcmp(ci->key, "K:A-%") == 0)
1863                 mode |= 2;
1864         call_ret(pane, "attach-emacs-search", p, mode);
1865
1866         return 1;
1867 }
1868
1869 DEF_CMD(emacs_command)
1870 {
1871         struct pane *p;
1872
1873         p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2", 0, NULL, "");
1874         if (!p)
1875                 return Efail;
1876         attr_set_str(&p->attrs, "prompt", "Cmd");
1877         attr_set_str(&p->attrs, "done-key", "emacs:command");
1878         call("doc:set-name", p, 0, NULL, "K:Ax command", -1);
1879         p = call_ret(pane, "attach-history", p, 0, NULL, "*Command History*");
1880         if (p)
1881                 pane_register(p, 0, &find_handle.c, "cmd");
1882         return 1;
1883 }
1884
1885 DEF_CMD(emacs_do_command)
1886 {
1887         char *cmd = NULL;
1888         int ret;
1889
1890         if (!ci->str)
1891                 /* Aborted */
1892                 return Efail;
1893         asprintf(&cmd, "interactive-cmd-%s", ci->str);
1894         if (!cmd)
1895                 return Efail;
1896         ret = call(cmd, ci->focus, 0, ci->mark);
1897         free(cmd); cmd = NULL;
1898         if (ret == 0) {
1899                 asprintf(&cmd, "Command %s not found", ci->str);
1900                 call("Message", ci->focus, 0, NULL, cmd);
1901         } else if (ret < 0) {
1902                 asprintf(&cmd, "Command %s Failed", ci->str);
1903                 call("Message", ci->focus, 0, NULL, cmd);
1904         }
1905         free(cmd);
1906         return 1;
1907 }
1908
1909 DEF_CB(take_cmd)
1910 {
1911         struct call_return *cr = container_of(ci->comm, struct call_return, c);
1912         const char *cmd;
1913
1914         if (!ci->str)
1915                 return Enoarg;
1916         cmd = ci->str + 16;
1917         if (cr->p) {
1918                 struct mark *m = vmark_new(cr->p, MARK_UNGROUPED, NULL);
1919                 call("doc:list-add", cr->p, 0, m);
1920                 call("doc:set-attr", cr->p, 0, m, "cmd", 0, NULL, cmd);
1921         }
1922         return 1;
1923 }
1924
1925 REDEF_CMD(emacs_cmd_complete)
1926 {
1927         char *s;
1928         struct pane *doc = NULL, *pop = NULL, *p;
1929         struct call_return cr;
1930
1931         if (!ci->mark)
1932                 return Enoarg;
1933         s = call_ret(strsave, "doc:get-str", ci->focus);
1934         if (!s)
1935                 s = "";
1936         doc = call_ret(pane, "attach-doc-list", ci->focus);
1937         if (!doc)
1938                 return Efail;
1939         call("doc:set-name", doc, 0, NULL, "*Command List*");
1940         call("doc:set:autoclose", doc, 1);
1941         attr_set_str(&doc->attrs, "render-simple", "format");
1942         attr_set_str(&doc->attrs, "heading", "");
1943         attr_set_str(&doc->attrs, "line-format", "%cmd");
1944         cr.c = take_cmd;
1945         cr.p = doc;
1946         call_comm("keymap:list", ci->focus, &cr.c,
1947                   0, NULL, "interactive-cmd-");
1948         call("doc:list-sort", doc, 0, NULL, "cmd");
1949         pop = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM1r");
1950         if (!pop)
1951                 goto fail;
1952         p = home_call_ret(pane, cr.p, "doc:attach-view", pop, -1, NULL, "simple");
1953         if (!p)
1954                 goto fail_pop;
1955         attr_set_str(&p->attrs, "done-key", "Replace");
1956         p = call_ret(pane, "attach-render-complete", p);
1957         if (!p)
1958                 goto fail_pop;
1959         cr = call_ret(all, "Complete:prefix", p, 1, NULL, s);
1960         if (cr.s && strlen(cr.s) <= strlen(s) && cr.ret-1 > 1) {
1961                 /* We need the dropdown - delete prefix and drop-down will
1962                  * insert result.
1963                  */
1964                 struct mark *start = mark_dup(ci->mark);
1965                 call("Move-Char", ci->focus, -strlen(s), start);
1966                 call("Replace", ci->focus, 1, start);
1967                 mark_free(start);
1968                 return 1;
1969         }
1970         if (cr.s) {
1971                 /* Replace 's' with the result */
1972                 struct mark *start = mark_dup(ci->mark);
1973                 call("Move-Char", ci->focus, -strlen(s), start);
1974                 call("Replace", ci->focus, 1, start, cr.s);
1975                 mark_free(start);
1976         }
1977         /* Now need to close the popup */
1978         pane_close(pop);
1979         pane_close(doc);
1980         return 1;
1981
1982 fail_pop:
1983         pane_close(pop);
1984 fail:
1985         pane_close(doc);
1986         return Efail;
1987 }
1988
1989 DEF_CMD(emacs_version)
1990 {
1991         char *v = NULL;
1992
1993         asprintf(&v, "%s ; emacs-mode - v" VERSION " - " VERS_DATE, edlib_version);
1994         if (v)
1995                 call("Message", ci->focus, 0, NULL, v);
1996         free(v);
1997         return 1;
1998 }
1999
2000 DEF_CMD(emacs_log)
2001 {
2002         /* View the debug log */
2003         struct pane *p, *doc;
2004
2005         doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL, "*Debug Log*");
2006         if (!doc) {
2007                 call("interactive-cmd-view-log", ci->focus);
2008                 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL,
2009                              "*Debug Log*");
2010         }
2011         if (!doc)
2012                 return Efail;
2013         p = call_ret(pane, "ThisPane", ci->focus);
2014         if (p)
2015                 home_call(doc, "doc:attach-view", p, 1);
2016         return 1;
2017 }
2018
2019 DEF_CMD(emacs_mark)
2020 {
2021         struct mark *m = call_ret(mark2, "doc:point", ci->focus);
2022
2023         call("selection:clear", ci->focus, 0, m);
2024
2025         call("Move-to", ci->focus, 1);
2026         m = call_ret(mark2, "doc:point", ci->focus);
2027         if (m)
2028                 /* ci->num == 1 means replacable */
2029                 call("selection:set", ci->focus,
2030                      ci->num == 1 ? 3 : 1, m);
2031         return 1;
2032 }
2033
2034 DEF_CMD(emacs_swap_mark)
2035 {
2036         struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2037         struct mark *m;
2038
2039         if (!mk)
2040                 return 1;
2041         m = mark_dup(mk);
2042         call("Move-to", ci->focus, 1); /* Move mark to point */
2043         call("Move-to", ci->focus, 0, m); /* Move point to old mark */
2044         call("selection:set", ci->focus,
2045              ci->num == 1 ? 3 : 1, mk);
2046         mark_free(m);
2047         return 1;
2048 }
2049
2050 DEF_CMD(emacs_wipe)
2051 {
2052         /* Delete text from point to mark */
2053         struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2054         char *str;
2055         int ret;
2056
2057         if (!mk)
2058                 return 1;
2059         /* Remove any selection so it cannot be claimed and so replace this copy */
2060         call("selection:claim", ci->focus);
2061         call("selection:discard", ci->focus);
2062
2063         str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, mk);
2064         if (str && *str)
2065                 call("copy:save", ci->focus, 0, NULL, str);
2066         ret = call("Replace", ci->focus, 1, mk);
2067         /* Clear mark */
2068         call("selection:clear", ci->focus, 0, mk);
2069
2070         return ret;
2071 }
2072
2073 DEF_CMD(emacs_copy)
2074 {
2075         /* copy text from point to mark */
2076         struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2077         char *str;
2078
2079         if (!mk)
2080                 return 1;
2081         /* Remove any selection so it cannot be claimed and so replace this copy */
2082         call("selection:claim", ci->focus);
2083         call("selection:discard", ci->focus);
2084
2085         str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, mk);
2086         if (str && *str)
2087                 call("copy:save", ci->focus, 0, NULL, str);
2088         /* Clear current highlight */
2089         call("selection:clear", ci->focus, 0, mk);
2090         return 1;
2091 }
2092
2093 DEF_CMD(emacs_yank)
2094 {
2095         int n = RPT_NUM(ci);
2096         char *str;
2097         struct mark *mk;
2098         struct mark *m = NULL;
2099
2100         /* If there is a selection elsewhere, we want to commit it */
2101         call("selection:discard", ci->focus);
2102         call("selection:commit", ci->focus);
2103
2104         str = call_ret(strsave, "copy:get", ci->focus, n - 1);
2105         if (!str || !*str)
2106                 return 1;
2107         /* If mark exists and is active, replace marked regions */
2108         mk = call_ret(mark2, "doc:point", ci->focus);
2109         if (mk && call("selection:clear", ci->focus, 0, mk) >= 1) {
2110                 char *str2 = call_ret(strsave, "doc:get-str", ci->focus,
2111                                       0, NULL, NULL, 0, mk);
2112                 if (str2 && *str2)
2113                         call("copy:save", ci->focus, 0, NULL, str2);
2114                 m = mark_dup(mk);
2115         }
2116
2117         call("Move-to", ci->focus, 1);
2118         call("Replace", ci->focus, 1, m, str);
2119         mark_free(m);
2120         mk = call_ret(mark2, "doc:point", ci->focus);
2121         call("Mode:set-num2", ci->focus, N2_yank);
2122         return 1;
2123 }
2124
2125 DEF_CMD(emacs_yank_pop)
2126 {
2127         struct mark *mk, *m;
2128         char *str;
2129         int num = N2a(ci);
2130
2131         if (N2(ci) != N2_yank)
2132                 return Einval;
2133         mk = call_ret(mark2, "doc:point", ci->focus);
2134         if (!mk)
2135                 return 1;
2136         num += 1;
2137         str = call_ret(strsave, "copy:get", ci->focus, num);
2138         if (!str) {
2139                 num = 0;
2140                 str = call_ret(strsave, "copy:get", ci->focus, num);
2141         }
2142         if (!str)
2143                 return Efail;
2144         m = mark_dup(mk);
2145         mark_step(m, 0);
2146         call("Replace", ci->focus, 1, mk, str);
2147         call("Move-to", ci->focus, 1, m);
2148         mark_free(m);
2149         call("Mode:set-num2", ci->focus, (num << 16) | N2_yank);
2150         return 1;
2151 }
2152
2153 DEF_CMD(emacs_selection_menu_add)
2154 {
2155         struct pane *p = ci->focus;
2156         call("menu-add", p, 0, NULL, "Cut", 0, NULL, ":C-W");
2157         call("menu-add", p, 0, NULL, "Copy", 0, NULL, ":A-w");
2158         call("menu-add", p, 0, NULL, "Paste-in", 0, NULL, ":C-Y");
2159         return Efallthrough;
2160 }
2161
2162 DEF_CMD(emacs_goto_line)
2163 {
2164         if (ci->num == NO_NUMERIC)
2165                 return 1;
2166         call("CountLines", ci->focus, ci->num, ci->mark, "goto:line");
2167         call("Move-View-Pos", ci->focus, 0, ci->mark);
2168         return 1;
2169 }
2170
2171 DEF_CMD(emacs_next_match)
2172 {
2173         call("Mode:set-num2", ci->focus, N2_match);
2174         call("Message:modal", ci->focus, 0, NULL, "Type ` to search again");
2175         return call("interactive-cmd-next-match", ci->focus, ci->num, NULL, NULL,
2176                     strcmp(ci->key, "K-`") == 0);
2177 }
2178
2179 DEF_CMD(emacs_match_again)
2180 {
2181         if (N2(ci) != N2_match)
2182                 return Efallthrough;
2183         else
2184                 return emacs_next_match_func(ci);
2185 }
2186
2187 DEF_CMD(emacs_make)
2188 {
2189         call("interactive-cmd-make", ci->focus,
2190              ci->num, ci->mark, NULL,
2191              strcmp(ci->key, "K:CC:C-M") == 0);
2192         return 1;
2193 }
2194
2195 DEF_CMD(emacs_paste)
2196 {
2197         char *str;
2198
2199         /* First commit the selection, then collect it */
2200         call("selection:commit", ci->focus);
2201         str = call_ret(strsave, "copy:get", ci->focus);
2202
2203         call("Move-CursorXY", ci->focus,
2204              0, NULL, NULL, 0, NULL, NULL, ci->x, ci->y);
2205
2206         if (!str || !*str)
2207                 return 1;
2208
2209         call("Replace", ci->focus, 0, NULL, str);
2210
2211         pane_take_focus(ci->focus);
2212
2213         return 1;
2214 }
2215
2216 DEF_CMD(emacs_readonly)
2217 {
2218         char *ro;
2219
2220         ro = pane_attr_get(ci->focus, "doc-readonly");
2221
2222         if (ro && strcmp(ro,"yes") == 0)
2223                 call("doc:set:readonly", ci->focus, 0);
2224         else
2225                 call("doc:set:readonly", ci->focus, 1);
2226         return 1;
2227 }
2228
2229 DEF_CMD(emacs_shift)
2230 {
2231         int rpt = ci->num;
2232         int shift;
2233
2234         shift = pane_attr_get_int(ci->focus, "render-wrap", -1);
2235         if (strcmp(ci->key, "K:CX->") == 0 || strcmp(ci->key, "K->") == 0)
2236                 rpt = -rpt;
2237         if (rpt == NO_NUMERIC) {
2238                 if (shift < 0)
2239                         shift = 0;
2240                 else
2241                         shift += 8;
2242         } else if (rpt == -NO_NUMERIC) {
2243                 if (shift >= 8)
2244                         shift -= 8;
2245                 else if (shift > 0)
2246                         shift = 0;
2247                 else
2248                         shift = -1;
2249         } else if (rpt >= 0) {
2250                 if (shift < 0)
2251                         shift = 0;
2252                 shift += rpt;
2253         } else {
2254                 if (shift > 0 && shift + rpt < 0)
2255                         shift = 0;
2256                 else
2257                         shift += rpt;
2258         }
2259         if (shift < 0)
2260                 attr_set_str(&ci->focus->attrs, "render-wrap", "yes");
2261         else if (shift > 0)
2262                 attr_set_int(&ci->focus->attrs, "render-wrap", shift);
2263         else if (rpt > 0)
2264                 attr_set_str(&ci->focus->attrs, "render-wrap", "0 auto");
2265         else
2266                 /* When reducing shift to zero, don't enable auto */
2267                 attr_set_int(&ci->focus->attrs, "render-wrap", 0);
2268         call("view:changed", ci->focus);
2269         call("Mode:set-num2", ci->focus, N2_shift);
2270         call("Message:modal", ci->focus, 0, NULL, "Type < or > to shift again");
2271         return 1;
2272 }
2273
2274 DEF_CMD(emacs_shift_again)
2275 {
2276         if (N2(ci) != N2_shift)
2277                 return Efallthrough;
2278         else
2279                 return emacs_shift_func(ci);
2280 }
2281
2282 DEF_CMD(emacs_growx)
2283 {
2284         if (ci->key[strlen(ci->key)-1] == '}')
2285                 call("Tile:x+", ci->focus, ci->num);
2286         else
2287                 call("Tile:x-", ci->focus, ci->num);
2288         call("Mode:set-num2", ci->focus, N2_growx);
2289         call("Message:modal", ci->focus, 0, NULL, "Type { or } to grow again");
2290         return 1;
2291 }
2292
2293 DEF_CMD(emacs_growx_again)
2294 {
2295         if (N2(ci) != N2_growx)
2296                 return Efallthrough;
2297         else
2298                 return emacs_growx_func(ci);
2299 }
2300
2301 DEF_CMD(emacs_scale_relative)
2302 {
2303         struct pane *p = ci->focus;
2304         char *sc = pane_attr_get(p, "scale:M");
2305         int scale = 0;
2306         char num[20];
2307
2308         if (!sc) {
2309                 call("Message:modal", p, 0, NULL,
2310                      "Cannot zoom display with fixed-sized font");
2311                 return 1;
2312         }
2313         sc = pane_attr_get(p, "scale");
2314         if (sc && strchr(sc, 'x')) {
2315                 call("Message:modal", p, 0, NULL,
2316                      "Cannot zoom display with fixed layout");
2317                 return 1;
2318         }
2319
2320         if (sc)
2321                 scale = atoi(sc);
2322         if (scale <= 0)
2323                 scale = 1000;
2324         if (ci->key[strlen(ci->key)-1] == '-')
2325                 scale = 10 * scale / 12;
2326         else
2327                 scale = 12 * scale / 10;
2328         snprintf(num, sizeof(num)-1, "%d", scale);
2329         call("Window:set:scale", p, 0, NULL, num);
2330         return 1;
2331 }
2332
2333 DEF_CMD(emacs_curs_pos)
2334 {
2335         struct mark *c;
2336         int col = 0;
2337         int chars = 0;
2338         wint_t ch, nxt;
2339         char *msg = NULL;
2340
2341         if (!ci->mark)
2342                 return Enoarg;
2343         c = mark_dup(ci->mark);
2344         nxt = doc_following(ci->focus, c);
2345
2346         while ((ch = doc_prev(ci->focus, c)) != WEOF && !is_eol(ch))
2347                 ;
2348         while (mark_ordered_not_same(c, ci->mark)) {
2349                 ch = doc_next(ci->focus, c);
2350                 if (is_eol(ch)) {
2351                         col = 0;
2352                         chars = 0;
2353                 } else if (ch == '\t') {
2354                         col = (col|7)+1;
2355                         chars += 1;
2356                 } else {
2357                         col += 1;
2358                         chars += 1;
2359                 }
2360         }
2361         mark_free(c);
2362         asprintf(&msg, "Cursor at column %d (%d chars), char is %d (0x%x)",
2363                  col, chars, nxt, nxt);
2364         call("Message", ci->focus, 0, NULL, msg);
2365         free(msg);
2366         return 1;
2367 }
2368
2369 DEF_CMD(emacs_word_count)
2370 {
2371         struct mark *mk;
2372         struct pane *p = ci->focus;
2373         char *msg = NULL;
2374         int wp;
2375
2376         if (!ci->mark)
2377                 return Enoarg;
2378
2379         mk = call_ret(mark2, "doc:point", p);
2380         if (mk && attr_find_int(mk->attrs, "selection:active") <= 0)
2381                 mk = NULL;
2382         call("CountLines", p, 0, ci->mark);
2383         wp = attr_find_int(ci->mark->attrs, "word");
2384         if (mk) {
2385                 int wm;
2386                 call("CountLines", p, 0, mk);
2387                 wm = attr_find_int(mk->attrs, "word");
2388                 asprintf(&msg, "%d words in region", abs(wp-wm));
2389         } else {
2390                 int wd = pane_attr_get_int(p, "words", 0);
2391                 asprintf(&msg, "After word%s %d of %d", wp==2?"":"s", wp-1, wd);
2392         }
2393         call("Message", p, 0, NULL, msg);
2394         free(msg);
2395         return 1;
2396 }
2397
2398 DEF_CMD(emacs_fill)
2399 {
2400         struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2401         struct mark *p = call_ret(mark, "doc:point", ci->focus);
2402         struct pane *p2;
2403
2404         if (call("selection:clear", ci->focus, 0, mk, NULL, 0, p) == Efalse)
2405                 mk = NULL;
2406
2407         if (strcmp(ci->key, "K:A-q") == 0) {
2408                 if (call("fill-paragraph", ci->focus, ci->num, p, NULL,
2409                          0, mk) == Efallthrough) {
2410                         p2 = call_ret(pane, "attach-textfill", ci->focus);
2411                         if (p2)
2412                                 call("fill-paragraph", p2, ci->num, p, NULL,
2413                                      0, mk);
2414                 }
2415         } else {
2416                 /* Don't try to load anything, the file-type should
2417                  * have loaded something if relevant
2418                  */
2419                 if (call("reindent-paragraph", ci->focus, ci->num, p, NULL,
2420                          0, mk) == 0)
2421                         call("Message", ci->focus, 0, NULL,
2422                              "Reindent not supported on the document.");
2423         }
2424         return 1;
2425 }
2426
2427 DEF_CMD(emacs_abbrev)
2428 {
2429         call("attach-abbrev", ci->focus);
2430         return 1;
2431 }
2432
2433 DEF_CMD(emacs_showinput)
2434 {
2435         struct pane *p, *doc;
2436
2437         if (call("input:log", ci->focus) <= 0) {
2438                 call("Message", ci->focus, 0, NULL,
2439                      "Cannot get log of recent input.");
2440                 return Efail;
2441         }
2442
2443         doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL, "*Debug Log*");
2444         if (!doc) {
2445                 call("interactive-cmd-view-log", ci->focus);
2446                 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL,
2447                              "*Debug Log*");
2448         }
2449         if (!doc)
2450                 return Efail;
2451         p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM");
2452         if (p) {
2453                 p = home_call_ret(pane, doc, "doc:attach-view", p, 1);
2454                 if (p)
2455                         call("doc:file", p, 1);
2456         }
2457         return 1;
2458 }
2459
2460 DEF_CMD(emacs_macro_start)
2461 {
2462         int ret;
2463
2464         ret = call("macro:capture", ci->focus);
2465         if (ret == Efalse)
2466                 call("Message", ci->focus, 0, NULL,
2467                      "Macro capture already happening");
2468         else if (ret <= 0)
2469                 call("Message", ci->focus, 0, NULL,
2470                      "Macro facility not available");
2471         return ret;
2472 }
2473
2474 DEF_CMD(emacs_macro_stop)
2475 {
2476         int ret;
2477
2478         ret = call("macro:finished", ci->focus, 2);
2479         if (ret > 0)
2480                 call("Message", ci->focus, 0, NULL,
2481                      "Macro successfully created.");
2482         else if (ret == Efalse)
2483                 call("Message", ci->focus, 0, NULL,
2484                      "No macro being created.");
2485         else
2486                 call("Message", ci->focus, 0, NULL,
2487                      "Failure creating macro.");
2488         return ret;
2489 }
2490
2491 DEF_CMD(emacs_macro_run)
2492 {
2493         int cnt = RPT_NUM(ci);
2494
2495         if (strcmp(ci->key, "K-e") == 0 && N2(ci) != N2_runmacro)
2496                 return Efallthrough;
2497
2498         if (cnt < 1)
2499                 cnt = 1;
2500         while (cnt >= 1 &&
2501                call("macro:replay", ci->focus, 1) > 0)
2502                 cnt -= 1;
2503
2504         call("Mode:set-num2", ci->focus, N2_runmacro);
2505         call("Message:modal", ci->focus, 0, NULL, "Type 'e' to repeat macro");
2506         return cnt < 1 ? 1 : Efail;
2507 }
2508
2509 struct bb {
2510         struct buf b;
2511         struct command c;
2512         bool first;
2513         int count;
2514 };
2515 static const char spell_choices[] =
2516         "0123456789-=;,/ABCDEFGHIJKLMNOPQRSTUVWXYZ";
2517 DEF_CB(get_suggestion)
2518 {
2519         struct bb *b = container_of(ci->comm, struct bb, c);
2520
2521         if (!ci->str)
2522                 return Enoarg;
2523
2524         if (b->first)
2525                 buf_concat(&b->b, " - (a)ccept, (i)nsert -     ");
2526         else
2527                 buf_concat(&b->b, ", ");
2528         b->first = False;
2529         if (b->count < (int)sizeof(spell_choices)-1) {
2530                 buf_append(&b->b, '(');
2531                 buf_append(&b->b, spell_choices[b->count]);
2532                 buf_append(&b->b, ')');
2533         }
2534         b->count += 1;
2535         buf_concat(&b->b, ci->str);
2536         return 1;
2537 }
2538
2539 DEF_CMD(emacs_spell)
2540 {
2541         struct mark *st;
2542         char *word;
2543         int ret;
2544         int rpt = RPT_NUM(ci);
2545
2546         if (!ci->mark)
2547                 return Enoarg;
2548
2549         /* We always find a word that is partly *after* the given
2550          * make, but we want to find the word before point, so step
2551          * back.
2552          */
2553         doc_prev(ci->focus, ci->mark);
2554 again:
2555         if (ci->num != NO_NUMERIC)
2556                 /* As a repeat-count was given, only look at intersting words */
2557                 call("Spell:NextWord", ci->focus, 0, ci->mark);
2558         st = mark_dup(ci->mark);
2559         word = call_ret(str, "Spell:ThisWord", ci->focus, 0, ci->mark, NULL, 0, st);
2560         if (!word || !*word) {
2561                 /* No word found */
2562                 call("Message", ci->focus, 0, NULL,
2563                      "Spell check reached end-of-file");
2564                 call("Spell:Save", ci->focus);
2565                 free(word);
2566                 mark_free(st);
2567                 return 1;
2568         }
2569         ret = call("Spell:Check", ci->focus, 0, NULL, word);
2570         if (ret > 0) {
2571                 rpt -= 1;
2572                 mark_free(st);
2573                 if (rpt)
2574                         goto again;
2575                 call("Message", ci->focus, 0, NULL,
2576                      strconcat(ci->focus, "\"", word,
2577                                "\" is a correct spelling."));
2578         } else if (ret == Efalse) {
2579                 struct bb b;
2580                 buf_init(&b.b);
2581                 buf_concat(&b.b, "\"");
2582                 buf_concat(&b.b, word);
2583                 buf_concat(&b.b, "\" is NOT correct");
2584                 b.count = 0;
2585                 b.first = True;
2586                 b.c = get_suggestion;
2587                 call_comm("Spell:Suggest", ci->focus, &b.c,
2588                           0, NULL, word);
2589                 if (b.first) {
2590                         buf_concat(&b.b, " ... no suggestions");
2591                 } else {
2592                         attr_set_str(&ci->focus->attrs, "spell:last-error",
2593                                      word);
2594                         attr_set_str(&ci->focus->attrs, "spell:suggestions",
2595                                      buf_final(&b.b));
2596                         attr_set_int(&ci->focus->attrs, "spell:offset", 0);
2597                 }
2598                 call("Mode:set-all", ci->focus, rpt-1, NULL, ":Spell");
2599                 call("Message:modal", ci->focus, 0, NULL, buf_final(&b.b));
2600                 free(buf_final(&b.b));
2601         } else if (ret == Efail) {
2602                 rpt -= 1;
2603                 if (rpt)
2604                         goto again;
2605
2606                 call("Message", ci->focus, 0, NULL,
2607                      strconcat(ci->focus, "\"", word,
2608                                "\" is not a word."));
2609         } else
2610                 call("Message", ci->focus, 0, NULL,
2611                      strconcat(ci->focus, "Spell check failed for \"", word,
2612                                "\""));
2613         mark_free(st);
2614         return 1;
2615 }
2616
2617 DEF_CMD(emacs_spell_choose)
2618 {
2619         const char *k = ksuffix(ci, "K:Spell-");
2620         char match[4] = "( )";
2621         char *suggest = attr_find(ci->focus->attrs,
2622                                   "spell:suggestions");
2623         char *last = attr_find(ci->focus->attrs,
2624                                "spell:last-error");
2625         char *cp, *ep;
2626         struct mark *m;
2627         int i;
2628
2629         if (!*k || !suggest || !last || !ci->mark)
2630                 return 1;
2631         match[1] = *k;
2632         cp = strstr(suggest, match);
2633         if (!cp)
2634                 return 1;
2635         cp += 3;
2636         ep = strchr(cp, ',');
2637         if (ep)
2638                 cp = strnsave(ci->focus, cp, ep-cp);
2639
2640         m = mark_dup(ci->mark);
2641         i = utf8_strlen(last);
2642         while (i > 0) {
2643                 doc_prev(ci->focus, m);
2644                 i -= 1;
2645         }
2646         call("doc:replace", ci->focus, 0, m, cp, 0, ci->mark);
2647         attr_set_str(&ci->focus->attrs, "spell:suggestions", NULL);
2648         attr_set_str(&ci->focus->attrs, "spell:last-error", NULL);
2649
2650         if (ci->num) {
2651                 doc_next(ci->focus, ci->mark);
2652                 home_call(ci->home, "emacs:respell", ci->focus,
2653                           ci->num, ci->mark, NULL,
2654                           ci->num2, ci->mark2);
2655         }
2656
2657         return 1;
2658 }
2659
2660 DEF_CMD(emacs_spell_abort)
2661 {
2662         return 1;
2663 }
2664
2665 DEF_CMD(emacs_spell_skip)
2666 {
2667         if (ci->num) {
2668                 doc_next(ci->focus, ci->mark);
2669                 home_call(ci->home, "emacs:respell", ci->focus,
2670                           ci->num, ci->mark, NULL,
2671                           ci->num2, ci->mark2);
2672         }
2673         return 1;
2674 }
2675
2676 DEF_CMD(emacs_spell_insert)
2677 {
2678         char *last = attr_find(ci->focus->attrs,
2679                                "spell:last-error");
2680         if (last) {
2681                 attr_set_str(&ci->focus->attrs, "spell:suggestions", NULL);
2682                 attr_set_str(&ci->focus->attrs, "spell:last-error", NULL);
2683                 call("Spell:AddWord", ci->focus, 1, NULL, last);
2684         }
2685
2686         if (ci->num) {
2687                 doc_next(ci->focus, ci->mark);
2688                 home_call(ci->home, "emacs:respell", ci->focus,
2689                           ci->num, ci->mark, NULL,
2690                           ci->num2, ci->mark2);
2691         }
2692         return 1;
2693 }
2694
2695 DEF_CMD(emacs_spell_accept)
2696 {
2697         char *last = attr_find(ci->focus->attrs,
2698                                "spell:last-error");
2699         if (last) {
2700                 attr_set_str(&ci->focus->attrs, "spell:suggestions", NULL);
2701                 attr_set_str(&ci->focus->attrs, "spell:last-error", NULL);
2702                 call("Spell:AddWord", ci->focus, 0, NULL, last);
2703         }
2704
2705         if (ci->num) {
2706                 doc_next(ci->focus, ci->mark);
2707                 home_call(ci->home, "emacs:respell", ci->focus,
2708                           ci->num, ci->mark, NULL,
2709                           ci->num2, ci->mark2);
2710         }
2711         return 1;
2712 }
2713
2714 static int spell_shift(struct pane *focus safe, int num, int inc)
2715 {
2716         int o = pane_attr_get_int(focus, "spell:offset", 0);
2717         int i;
2718         char *msg;
2719         char *c;
2720
2721         o += inc;
2722         msg = pane_attr_get(focus, "spell:suggestions");
2723         if (!msg)
2724                 return 1;
2725         for (i = 0; i < o && (c = strchr(msg, ',')) != NULL; i++)
2726                 msg = c+1;
2727         attr_set_int(&focus->attrs, "spell:offset", i);
2728
2729         call("Mode:set-all", focus, num, NULL, ":Spell");
2730         call("Message:modal", focus, 0, NULL, msg);
2731         return 1;
2732 }
2733
2734 DEF_CMD(emacs_spell_left)
2735 {
2736         return spell_shift(ci->focus, ci->num, -1);
2737 }
2738
2739 DEF_CMD(emacs_spell_right)
2740 {
2741         return spell_shift(ci->focus, ci->num, 1);
2742 }
2743
2744 DEF_CMD(emacs_quote)
2745 {
2746         char b[6];
2747         wint_t wch = WEOF;
2748         char *str = NULL;
2749         struct mark *mk = NULL;
2750         bool free_mark = False;
2751
2752         if (ci->num >= 0 && ci->num < NO_NUMERIC)
2753                 wch = ci->num;
2754         else if (N2(ci) == N2_uniquote && ci->mark &&
2755                  (str = attr_find(ci->mark->attrs, "emacs:unicode_char")) != NULL) {
2756                 struct call_return cr;
2757                 int i = N2a(ci);
2758                 if (ci->num < 0 && i > 1)
2759                         i -= 1;
2760                 else
2761                         i += 1;
2762                 cr = call_ret(all, "Unicode-names", ci->focus, i, NULL, str);
2763                 if (cr.s && cr.i && (wint_t)cr.i != WEOF) {
2764                         wch = cr.i;
2765                         call("Message", ci->focus, 0, NULL,
2766                              strconcat(ci->focus,
2767                                        "Unicode char <", cr.s, ">"));
2768                         call("Mode:set-num2", ci->focus,
2769                              N2_uniquote | (i << 16));
2770                         mk = mark_dup(ci->mark);
2771                         doc_prev(ci->focus, mk);
2772                         free_mark = True;
2773                 } else {
2774                         call("Message", ci->focus, 0, NULL,
2775                              strconcat(ci->focus,
2776                                        "Cannot find another character <", str, ">"));
2777                         return Efail;
2778                 }
2779         } else if (wch == WEOF && ci->mark &&
2780                    (mk = call_ret(mark2, "doc:point", ci->focus)) != NULL &&
2781                    call("selection:clear", ci->focus, 0, mk) >= 1 &&
2782                    (str = call_ret(strsave, "doc:get-str", ci->focus,
2783                                    0, NULL, NULL, 0, mk)) != NULL) {
2784                 int x;
2785                 char *ep;
2786                 if (*str == '#')
2787                         str ++;
2788                 x = strtoul(str, &ep, 16);
2789                 if (ep && *ep == 0) {
2790                         wch = x;
2791                         call("Message", ci->focus, 0, NULL,
2792                              strconcat(ci->focus, "Hex code 0x", str));
2793                 } else {
2794                         struct call_return cr;
2795                         cr = call_ret(all, "Unicode-names", ci->focus,
2796                                       1, NULL, str);
2797                         if (cr.s && cr.i) {
2798                                 wch = cr.i;
2799                                 call("Message", ci->focus, 0, NULL,
2800                                      strconcat(ci->focus,
2801                                                "Unicode char <", cr.s, ">"));
2802                                 if (ci->mark) {
2803                                         attr_set_str(&ci->mark->attrs,
2804                                                      "emacs:unicode_char",
2805                                                      str);
2806                                         call("Mode:set-num2", ci->focus,
2807                                              N2_uniquote | (1 << 16));
2808                                 }
2809                         } else {
2810                                 call("Message", ci->focus, 0, NULL,
2811                                      strconcat(ci->focus,
2812                                                "Cannot find character <", str, ">"));
2813                                 return Efail;
2814                         }
2815                 }
2816         }
2817         if (wch == WEOF) {
2818                 call("Mode:set-all", ci->focus, ci->num, NULL, ":CQ", ci->num2);
2819                 return 1;
2820         }
2821         call("Replace", ci->focus, 0, mk, put_utf8(b, wch));
2822         if (free_mark)
2823                 mark_free(mk);
2824         return 1;
2825 }
2826
2827 struct docs_helper {
2828         struct command c;
2829         int cnt;
2830         struct pane *p safe;
2831 };
2832
2833 DEF_CMD(emacs_doc_menu)
2834 {
2835         const char *d = ksuffix(ci, "emacs:doc-menu:");
2836         struct pane *p = call_ret(pane, "docs:byname", ci->focus,
2837                                   0, NULL, d);
2838
2839         if (p) {
2840                 struct pane *t = call_ret(pane, "ThisPane", ci->focus);
2841                 if (t)
2842                         home_call(p, "doc:attach-view", t, 1);
2843         }
2844         return 1;
2845 }
2846
2847 DEF_CB(emacs_menu_add_doc)
2848 {
2849         struct docs_helper *dh = container_of(ci->comm, struct docs_helper, c);
2850         char *name = pane_attr_get(ci->focus, "doc-name");
2851
2852         if (!name)
2853                 return Efallthrough;
2854         if (dh->cnt >= 10)
2855                 return 1;
2856         dh->cnt += 1;
2857         call("menu:add", dh->p, 0, NULL, name, 0, NULL,
2858              strconcat(ci->home, " emacs:doc-menu:", name));
2859         return Efallthrough;
2860 }
2861
2862 DEF_CMD(emacs_menu_refresh)
2863 {
2864         struct pane *p = ci->focus;
2865         char *n = pane_attr_get(p, "doc-name");
2866         struct docs_helper dh;
2867
2868         if (!n || strcmp(n, "Documents") != 0)
2869                 return 1;
2870
2871         call("menu:clear", p);
2872         dh.c = emacs_menu_add_doc;
2873         dh.cnt = 0;
2874         dh.p = p;
2875         call_comm("docs:byeach", p, &dh.c);
2876         call("menu:add", p, 0, NULL, "List all", 0, NULL, ":C-X :C-B");
2877         return 1;
2878 }
2879
2880 static char *menus[][3] = {
2881         { "Help/Recent", ":F1 l", "R" },
2882         { "Documents/List all", ":C-X :C-B", "R"},
2883         { "File/Open", ":C-X :C-F", "L"},
2884         { "File/Save", ":C-X :C-S", "L"},
2885         { "File/Exit", ":C-X :C-C", "L"},
2886         { "Edit/Copy", ":A-w", "L"},
2887 };
2888
2889 DEF_CMD(emacs_menubar_configure)
2890 {
2891         unsigned int i;
2892
2893         for (i = 0; i < ARRAY_SIZE(menus); i++)
2894                 call("menubar-add", ci->focus,
2895                      menus[i][2][0] == 'R' ? 2 : 0,
2896                      NULL, menus[i][0],
2897                      0, NULL, menus[i][1]);
2898         /* Allow other ancestor to configure */
2899         return Efallthrough;
2900 }
2901
2902 DEF_PFX_CMD(cx_cmd, ":CX");
2903 DEF_PFX_CMD(cx4_cmd, ":CX4");
2904 DEF_PFX_CMD(cx5_cmd, ":CX5");
2905 DEF_PFX_CMD(cx44_cmd, ":CX44");
2906 DEF_PFX_CMD(cc_cmd, ":CC");
2907
2908 static struct map *emacs_map;
2909 DEF_LOOKUP_CMD(mode_emacs, emacs_map);
2910
2911 static void emacs_init(void)
2912 {
2913         unsigned i;
2914         struct map *m;
2915
2916         if (emacs_map)
2917                 return;
2918         m = key_alloc();
2919         key_add(m, "K:C-X", &cx_cmd.c);
2920         key_add(m, "K:CX-4", &cx4_cmd.c);
2921         /* C-\ is generated by C-4.  Weird... */
2922         key_add(m, "K:CX:C-\\", &cx4_cmd.c);
2923         key_add(m, "K:CX-5", &cx5_cmd.c);
2924         key_add(m, "K:CX4-4", &cx44_cmd.c);
2925         key_add(m, "K:CX4:C-\\", &cx44_cmd.c);
2926         key_add(m, "K:C-C", &cc_cmd.c);
2927
2928         key_add(m, "K:C-Q", &emacs_quote);
2929
2930         for (i = 0; i < ARRAY_SIZE(move_commands); i++) {
2931                 struct move_command *mc = &move_commands[i];
2932                 key_add(m, mc->k1, &mc->cmd);
2933                 if (mc->k2)
2934                         key_add(m, mc->k2, &mc->cmd);
2935                 if (mc->k3)
2936                         key_add(m, mc->k3, &mc->cmd);
2937         }
2938
2939         for (i = 0; i < ARRAY_SIZE(simple_commands); i++) {
2940                 struct simple_command *sc = &simple_commands[i];
2941                 key_add(m, sc->k, &sc->cmd);
2942         }
2943
2944         key_add(m, "K:C-O", &emacs_open_line);
2945
2946         key_add(m, "K:C-_", &emacs_undo);
2947         key_add(m, "K:CX-u", &emacs_undo);
2948         key_add(m, "K:C-/", &emacs_undo);
2949         key_add(m, "K:C-Z", &emacs_undo);
2950
2951         key_add(m, "K:C-L", &emacs_recenter);
2952
2953         key_add(m, "K:CX:C-F", &emacs_findfile);
2954         key_add(m, "K:CX4:C-F", &emacs_findfile);
2955         key_add(m, "K:CX4-f", &emacs_findfile);
2956         key_add(m, "K:CX44-f", &emacs_findfile);
2957         key_add_prefix(m, "File Found:", &emacs_findfile);
2958
2959         key_add(m, "K:CX:C-W", &emacs_writefile);
2960         key_add_prefix(m, "emacs:write_file:", &emacs_do_writefile);
2961
2962         key_add(m, "K:CX-i", &emacs_insertfile);
2963         key_add_prefix(m, "emacs:insert_file:", &emacs_do_insertfile);
2964
2965         key_add(m, "K:CX-b", &emacs_finddoc);
2966         key_add(m, "K:CX4-b", &emacs_finddoc);
2967         key_add(m, "K:CX44-b", &emacs_finddoc);
2968         key_add_prefix(m, "Doc Found:", &emacs_finddoc);
2969
2970         key_add(m, "K:CX:C-B", &emacs_viewdocs);
2971         key_add(m, "K:CX4:C-B", &emacs_viewdocs);
2972         key_add(m, "K:CX44:C-B", &emacs_viewdocs);
2973
2974         key_add(m, "K:CX-k", &emacs_kill_doc);
2975
2976         key_add(m, "K:CX-s", &emacs_save_all);
2977
2978         key_add(m, "K:CX:C-V", &emacs_revisit);
2979
2980         key_add(m, "K:CX-=", &emacs_curs_pos);
2981         key_add(m, "K:A-=", &emacs_word_count);
2982
2983         key_add(m, "K:CX-<", &emacs_shift);
2984         key_add(m, "K:CX->", &emacs_shift);
2985         key_add(m, "K-<", &emacs_shift_again);
2986         key_add(m, "K->", &emacs_shift_again);
2987
2988         key_add(m, "K:CX-{", &emacs_growx);
2989         key_add(m, "K:CX-}", &emacs_growx);
2990         key_add(m, "K-{", &emacs_growx_again);
2991         key_add(m, "K-}", &emacs_growx_again);
2992
2993         key_add(m, "K:CX:C-=", &emacs_scale_relative);
2994         key_add(m, "K:CX:C--", &emacs_scale_relative);
2995
2996         key_add(m, "K:C-S", &emacs_start_search);
2997         key_add(m, "K:C-R", &emacs_start_search);
2998         key_add(m, "K:A-%", &emacs_start_search);
2999         key_add(m, "render:reposition", &emacs_reposition);
3000
3001         key_add(m, "K:CX:C-C", &emacs_exit);
3002         key_add(m, "emacs:deactivate", &emacs_deactivate);
3003
3004         key_add(m, "K:C-U", &emacs_prefix);
3005
3006         key_add(m, "K:A-!", &emacs_shell);
3007         key_add(m, "K:A-|", &emacs_shell);
3008         key_add(m, "Shell Command", &emacs_shell);
3009
3010         key_add(m, "K:CX-`", &emacs_next_match);
3011         key_add(m, "K-`", &emacs_match_again);
3012
3013         key_add(m, "K:CX-1", &emacs_close_others);
3014         key_add(m, "K-1", &emacs_close_others);
3015
3016         key_add_range(m, "K:A-0", "K:A-9", &emacs_num);
3017         key_add(m, "K:A--", &emacs_neg);
3018         key_add(m, "K:C--", &emacs_neg);
3019         key_add(m, "K:C- ", &emacs_mark);
3020         key_add(m, "mode-set-mark", &emacs_mark);
3021         key_add(m, "mode-swap-mark", &emacs_swap_mark);
3022         key_add(m, "K:C-W", &emacs_wipe);
3023         key_add(m, "K:A-w", &emacs_copy);
3024         key_add(m, "K:C-Y", &emacs_yank);
3025         key_add(m, "K:A-y", &emacs_yank_pop);
3026
3027         key_add(m, "K:A-g", &emacs_goto_line);
3028         key_add(m, "K:A-x", &emacs_command);
3029         key_add(m, "K:A-X", &emacs_command);
3030         key_add(m, "K:CC-m", &emacs_make);
3031         key_add(m, "K:CC:C-M", &emacs_make);
3032
3033         key_add(m, "K:A:C-V", &emacs_move_view_other);
3034
3035         key_add(m, "K:CX:C-Q", &emacs_readonly);
3036
3037         key_add_prefix(m, "K:CQ-", &emacs_quote_insert);
3038         key_add_prefix(m, "K:CQ:C-", &emacs_quote_insert);
3039
3040         key_add(m, "K:A-q", &emacs_fill);
3041         key_add(m, "K:A:C-Q", &emacs_fill);
3042         key_add(m, "K:A-/", &emacs_abbrev);
3043         key_add(m, "K:A-;", &emacs_spell);
3044         key_add(m, "emacs:respell", &emacs_spell);
3045         key_add_prefix(m, "K:Spell-", &emacs_spell_choose);
3046         key_add(m, "K:Spell-a", &emacs_spell_accept);
3047         key_add(m, "K:Spell-i", &emacs_spell_insert);
3048         key_add(m, "K:Spell- ", &emacs_spell_skip);
3049         key_add(m, "K:Spell:Enter", &emacs_spell_abort);
3050         key_add(m, "K:Spell:ESC", &emacs_spell_abort);
3051         key_add(m, "K:Spell:Left", &emacs_spell_left);
3052         key_add(m, "K:Spell:C-B", &emacs_spell_left);
3053         key_add(m, "K:Spell:Right", &emacs_spell_right);
3054         key_add(m, "K:Spell:C-F", &emacs_spell_right);
3055
3056         key_add(m, "K:Help-l", &emacs_showinput);
3057
3058         key_add(m, "emacs:command", &emacs_do_command);
3059         key_add(m, "interactive-cmd-version", &emacs_version);
3060         key_add(m, "interactive-cmd-log", &emacs_log);
3061
3062         key_add(m, "M:Click-2", &emacs_paste);
3063         key_add(m, "M:C:Click-1", &emacs_paste);
3064
3065         key_add(m, "K:CX-(", &emacs_macro_start);
3066         key_add(m, "K:CX-)", &emacs_macro_stop);
3067         key_add(m, "K:CX-e", &emacs_macro_run);
3068         key_add(m, "K-e", &emacs_macro_run);
3069
3070         key_add(m, "menu:refresh", &emacs_menu_refresh);
3071         key_add_prefix(m, "emacs:doc-menu:", &emacs_doc_menu);
3072
3073         key_add(m, "menubar:ready", &emacs_menubar_configure);
3074
3075         emacs_map = m;
3076 }
3077
3078 DEF_CMD(attach_mode_emacs)
3079 {
3080         struct pane *p = pane_register(ci->focus, 0, &mode_emacs.c, NULL);
3081
3082         if (!p)
3083                 return Efail;
3084         comm_call(ci->comm2, "cb", p);
3085         return 1;
3086 }
3087
3088 DEF_CMD(attach_file_entry)
3089 {
3090         /* The 'type' passed must be static, not allocated */
3091         char *type = "shellcmd";
3092
3093         if (ci->str && strcmp(ci->str, "file") == 0)
3094                 type = "file";
3095         else if (ci->str && strcmp(ci->str, "doc") == 0)
3096                 type = "doc";
3097         pane_register(ci->focus, 0, &find_handle.c, type);
3098
3099         return 1;
3100 }
3101
3102 void edlib_init(struct pane *ed safe)
3103 {
3104         emacs_init();
3105         findmap_init();
3106         call_comm("global-set-command", ed, &attach_mode_emacs, 0, NULL, "attach-mode-emacs");
3107         call_comm("global-set-command", ed, &attach_file_entry, 0, NULL, "attach-file-entry");
3108         call_comm("global-set-command", ed, &emacs_shell, 0, NULL, "attach-shell-prompt");
3109         call_comm("global-set-command", ed, &emacs_selection_menu_add,
3110                   0, NULL, "selection-menu:add-00-emacs");
3111 }