]> git.neil.brown.name Git - edlib.git/blob - mode-emacs.c
d5b37d0badab970b344e763fc1a89c0ee16160d9
[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 && (strlen(cr.s) <= strlen(b) && cr.ret-1 > 1)) {
1362                 /* We need the dropdown - delete prefix and drop-down will
1363                  * insert result.
1364                  */
1365                 struct mark *start;
1366
1367                 start = mark_dup(ci->mark);
1368                 call("doc:char", ci->focus, -strlen(b), start);
1369                 call("Replace", ci->focus, 1, start);
1370                 mark_free(start);
1371
1372                 return 1;
1373         }
1374         if (cr.s) {
1375                 /* Replace 'b' with the result. */
1376                 struct mark *start;
1377
1378                 start = mark_dup(ci->mark);
1379                 call("doc:char", ci->focus, -strlen(b), start);
1380                 call("Replace", ci->focus, 1, start, cr.s);
1381                 mark_free(start);
1382         } else {
1383                 call("Message:modal", ci->focus, 0, NULL,
1384                      strconcat(ci->focus, "No completion found for \"", b, "\"",
1385                                " in \"", d, "\""));
1386         }
1387         /* Now need to close the popup */
1388         pane_close(pop);
1389         return 1;
1390 }
1391
1392 DEF_CMD(emacs_finddoc)
1393 {
1394         struct pane *p, *par;
1395
1396         if (ksuffix(ci, "Doc Found:")[0] == 0) {
1397                 struct pane *dflt;
1398                 char *defname = NULL;
1399
1400                 dflt = call_ret(pane, "docs:choose", ci->focus);
1401                 if (dflt)
1402                         defname = pane_attr_get(dflt, "doc-name");
1403
1404                 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
1405                              0, NULL, "");
1406                 if (!p)
1407                         return Efail;
1408
1409                 if (defname)
1410                         attr_set_str(&p->attrs, "default", defname);
1411                 if (ksuffix(ci, "K:CX44")[0]) {
1412                         attr_set_str(&p->attrs, "prompt",
1413                                      "Find Document Popup");
1414                         attr_set_str(&p->attrs, "done-key",
1415                                      "Doc Found:Popup");
1416                 } else if (ksuffix(ci, "K:CX4")[0]) {
1417                         attr_set_str(&p->attrs, "prompt",
1418                                      "Find Document Other Window");
1419                         attr_set_str(&p->attrs, "done-key",
1420                                      "Doc Found:Other Window");
1421                 } else {
1422                         attr_set_str(&p->attrs, "prompt", "Find Document");
1423                         attr_set_str(&p->attrs, "done-key",
1424                                      "Doc Found:This Window");
1425                 }
1426                 call("doc:set-name", p, 0, NULL, "Find Document", -1);
1427
1428                 pane_register(p, 0, &find_handle.c, "doc");
1429                 return 1;
1430         }
1431
1432         if (!ci->str)
1433                 /* Aborted */
1434                 return Efail;
1435
1436         p = call_ret(pane, "docs:byname", ci->focus, 0, NULL, ci->str);
1437         if (!p)
1438                 return Efail;
1439
1440         if (strcmp(ci->key, "Doc Found:Other Window") == 0) {
1441                 struct pane *this = call_ret(pane, "ThisPane", ci->focus);
1442                 par = home_call_ret(pane, ci->focus, "DocPane", p);
1443                 if (par && par != this) {
1444                         pane_take_focus(par);
1445                         return 1;
1446                 }
1447                 par = call_ret(pane, "OtherPane", ci->focus);
1448         } else if (strcmp(ci->key, "Doc Found:Popup") == 0) {
1449                 par = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "MD3tsa");
1450         } else
1451                 par = call_ret(pane, "ThisPane", ci->focus);
1452         if (!p || !par)
1453                 return Efail;
1454
1455         p = home_call_ret(pane, p, "doc:attach-view", par, 1);
1456         if (p)
1457                 pane_take_focus(p);
1458         return p ? 1 : Efail;
1459 }
1460
1461 REDEF_CMD(emacs_doc_complete)
1462 {
1463         /* Extract a document name from the document.
1464          * Attach the 'docs' document as a completing popup menu
1465          */
1466         char *str;
1467         struct pane *pop, *p = NULL;
1468         struct call_return cr;
1469
1470         if (!ci->mark)
1471                 return Enoarg;
1472
1473         str = call_ret(strsave, "doc:get-str", ci->focus);
1474         if (!str)
1475                 return Einval;
1476         pop = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM1r");
1477         if (!pop)
1478                 return Efail;
1479         p = call_ret(pane, "docs:complete", pop);
1480         if (!p)
1481                 return Efail;
1482         cr = call_ret(all, "Complete:prefix", p, 1, NULL, str);
1483         if (cr.s && (strlen(cr.s) <= strlen(str) && cr.ret - 1 > 1)) {
1484                 /* We need the dropdown */
1485                 struct mark *start;
1486
1487                 start = mark_dup(ci->mark);
1488                 call("doc:set-ref", ci->focus, 1, start);
1489
1490                 call("Replace", ci->focus, 1, start);
1491                 mark_free(start);
1492                 return 1;
1493         }
1494         if (cr.s) {
1495                 /* Replace the prefix with the new value */
1496                 struct mark *start;
1497
1498                 start = mark_dup(ci->mark);
1499                 call("doc:set-ref", ci->focus, 1, start);
1500
1501                 call("Replace", ci->focus, 1, start, cr.s);
1502                 mark_free(start);
1503         }
1504         /* Now need to close the popup */
1505         pane_close(pop);
1506         return 1;
1507 }
1508
1509 DEF_CMD(emacs_viewdocs)
1510 {
1511         struct pane *p, *par;
1512         struct pane *docs;
1513
1514         docs = call_ret(pane, "docs:byname", ci->focus, 0, NULL, "*Documents*");
1515         if (!docs)
1516                 return Efail;
1517
1518         if (ksuffix(ci, "K:CX44")[0]) {
1519                 par = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "MD3tsa");
1520         } else if (ksuffix(ci, "K:CX4")[0]) {
1521                 struct pane *this = call_ret(pane, "ThisPane", ci->focus);
1522                 par = home_call_ret(pane, ci->focus, "DocPane", docs);
1523                 if (par && par != this) {
1524                         pane_take_focus(par);
1525                         return 1;
1526                 }
1527                 par = call_ret(pane, "OtherPane", ci->focus);
1528         } else {
1529                 par = call_ret(pane, "ThisPane", ci->focus);
1530         }
1531
1532         if (!par)
1533                 return Efail;
1534
1535         p = home_call_ret(pane, docs, "doc:attach-view", par, 1);
1536         return !!p;
1537 }
1538
1539 struct pcb {
1540         struct command c;
1541         struct pane *doc safe;
1542         struct pane *p;
1543 };
1544
1545 DEF_CMD(choose_pane)
1546 {
1547         struct pcb *cb = container_of(ci->comm, struct pcb, c);
1548         struct pane *d;
1549
1550         if (cb->p || !ci->str)
1551                 return 1;
1552         d = call_ret(pane, "docs:byname", ci->focus, 0, NULL, ci->str);
1553         if (d)
1554                 cb->p = home_call_ret(pane, ci->focus, "DocPane", d);
1555         if (cb->p)
1556                 home_call(cb->doc, "doc:attach-view", cb->p, 1);
1557         return 1;
1558 }
1559
1560 DEF_CB(shellcb)
1561 {
1562         char *str;
1563
1564         if (strcmp(ci->key, "cb:timer") == 0) {
1565                 /* If focus has moved, don't show shell window,
1566                  * probably a popup was requested by command.
1567                  */
1568                 if (!pane_has_focus(ci->home))
1569                         /* call back when lines or eof */
1570                         return 2;
1571         }
1572         if (strcmp(ci->key, "cb:eof") != 0) {
1573                 struct pane *par;
1574                 if (ci->str && strchr(ci->str, 'P'))
1575                         par = call_ret(pane, "PopupTile", ci->home, 0, NULL, "MD3tsa");
1576                 else
1577                         par = call_ret(pane, "OtherPane", ci->home);
1578                 if (par)
1579                         home_call(ci->focus, "doc:attach-view", par, 1);
1580                 return 1;
1581         }
1582         str = call_ret(str, "doc:get-str", ci->focus);
1583         if (!str || !*str) {
1584                 free(str);
1585                 if (ci->num == 0)
1586                         asprintf(&str, "(shell command completed with no output)");
1587                 else if (ci->num > 0)
1588                         asprintf(&str, "(shell command completed with no output (exit code %d))", ci->num);
1589                 else
1590                         asprintf(&str, "(shell command completed with no output (signalled %d))", -ci->num);
1591         }
1592         call("Message", ci->home, 0, NULL, str);
1593         free(str);
1594         return 1;
1595 }
1596
1597 DEF_CB(shell_insert_cb)
1598 {
1599         char *str = call_ret(str, "doc:get-str", ci->focus);
1600         struct mark *mk = call_ret(mark2, "doc:point", ci->home);
1601
1602         if (call("selection:clear", ci->home, 3, mk) >= 1)
1603                 call("Replace", ci->home, 1, mk);
1604         call("Replace", ci->home, 0, NULL, str);
1605         free(str);
1606         return 1;
1607 }
1608
1609 DEF_CMD(emacs_shell)
1610 {
1611         char *name = "*Shell Command Output*";
1612         struct pane *p, *doc, *par, *sc;
1613         char *path, *input = NULL;
1614         struct pcb cb;
1615         bool interpolate, pipe, popup;
1616
1617         if (strcmp(ci->key, "Shell Command") != 0) {
1618                 char *dirname;
1619                 char aux[4] = "";
1620
1621                 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2", 0, NULL,
1622                              ci->str ?: "");
1623                 if (!p)
1624                         return Efail;
1625                 dirname = call_ret(strsave, "get-attr", ci->focus, 0, NULL, "dirname");
1626                 attr_set_str(&p->attrs, "dirname", dirname ?: ".");
1627                 attr_set_str(&p->attrs, "prompt", "Shell command");
1628                 if (ci->num == 0 || ci->num == 1)
1629                         strcat(aux, "i"); // interpolate
1630                 if (ci->num == 4)
1631                         strcat(aux, "P"); // popup
1632                 if (strcmp(ci->key, "K:A-|") == 0)
1633                         strcat(aux, "p"); // pipe from selection
1634                 attr_set_str(&p->attrs, "popup-aux", aux);
1635                 attr_set_str(&p->attrs, "done-key", "Shell Command");
1636                 call("doc:set-name", p, 0, NULL, "Shell Command", -1);
1637                 p = call_ret(pane, "attach-history", p, 0, NULL, "*Shell History*");
1638                 if (p)
1639                         p = pane_register(p, 0, &find_handle.c, "shellcmd");
1640                 if (!p)
1641                         return Efail;
1642                 if (ci->comm2)
1643                         comm_call(ci->comm2, "cb", p);
1644                 return 1;
1645         }
1646         if (!ci->str) {
1647                 call("Message", ci->focus, 0, NULL, "Shell command aborted");
1648                 return 1;
1649         }
1650
1651         interpolate = ci->str2 && strchr(ci->str2, 'i');
1652         pipe = ci->str2 && strchr(ci->str2, 'p');
1653         popup = ci->str2 && strchr(ci->str2, 'P');
1654
1655         if (pipe) {
1656                 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
1657                 if (mk) {
1658                         input = call_ret(str, "doc:get-str", ci->focus,
1659                                          0, NULL, NULL, 0, mk);
1660                         /* make the selection replacable */
1661                         attr_set_int(&mk->attrs, "selection:active", 3);
1662                 }
1663         }
1664         doc = call_ret(pane, "doc:from-text", ci->focus, 0, NULL, name, 0, NULL,
1665                        input ?: "");
1666         free(input);
1667         if (!doc)
1668                 return Efail;
1669
1670         path = pane_attr_get(ci->focus, "dirname");
1671         attr_set_str(&doc->attrs, "dirname", path);
1672
1673         /* shellcmd is attached directly to the document, not in the view
1674          * stack.  It is go-between for document and external command.
1675          * We don't need a doc attachment as no point is needed - we
1676          * always insert at the end.
1677          */
1678         sc = call_ret(pane, "attach-shellcmd", doc, pipe ? 22 : 4,
1679                       NULL, ci->str, 0, NULL, path);
1680         if (!sc)
1681                 call("doc:replace", doc, 0, NULL,
1682                      "Failed to run command - sorry\n");
1683         if (call("text-search", doc, 0, NULL, "diff|(stg|git).*show",
1684                  0, NULL, ci->str) > 0)
1685                 attr_set_str(&doc->attrs, "view-default", "diff");
1686
1687         /* Close old shell docs, but if one is visible in current frame, replace
1688          * it with doc
1689          */
1690         cb.c = choose_pane;
1691         cb.doc = doc;
1692         cb.p = NULL;
1693         call_comm("editor:notify:shell-reuse", ci->focus, &cb.c);
1694         if (!cb.p) {
1695                 /* choose_pane didn't attach, so set a callback to do
1696                  * it when there is enough content.
1697                  */
1698                 if (sc) {
1699                         /* If it take more than 500msec, or includes 2
1700                          * or more lines, we'll show in a pane, else
1701                          * just show as a message.
1702                          */
1703                         if (!interpolate)
1704                                 home_call_comm(sc, "shellcmd:set-callback",
1705                                                ci->focus, &shellcb,
1706                                                500, NULL, popup ? "P": "",
1707                                                2);
1708                 } else {
1709                         if (popup)
1710                                 par = call_ret(pane, "PopupTile", ci->focus,
1711                                                0, NULL, "MD3tsa");
1712                         else
1713                                 par = call_ret(pane, "OtherPane", ci->focus);
1714                         if (!par)
1715                                 return Efail;
1716                         home_call(doc, "doc:attach-view", par, 1);
1717                 }
1718         }
1719         if (sc && interpolate)
1720                 /* Need a callback when the pipe command finished */
1721                 home_call_comm(sc, "shellcmd:set-callback", ci->focus,
1722                                &shell_insert_cb);
1723
1724         return 1;
1725 }
1726
1727 DEF_CMD(emacs_num)
1728 {
1729         int rpt = ci->num;
1730         const char *last = ksuffix(ci, "K:A-");
1731         int neg = 0;
1732
1733         if (rpt < 0) {
1734                 neg = 1;
1735                 rpt = -rpt;
1736         }
1737         if (rpt == NO_NUMERIC)
1738                 rpt = 0;
1739
1740         rpt = rpt * 10 + *last - '0';
1741
1742         call("Mode:set-num", ci->focus, neg ? -rpt : rpt);
1743         call("Mode:set-num2", ci->focus, ci->num2);
1744         return 1;
1745 }
1746
1747 DEF_CMD(emacs_neg)
1748 {
1749         call("Mode:set-num", ci->focus, - ci->num);
1750         call("Mode:set-num2", ci->focus, ci->num2);
1751         return 1;
1752 }
1753
1754 DEF_CMD(emacs_prefix)
1755 {
1756         /* as a generic arg (ctrl-U) which is positive and
1757          * as as a repeat-count of 4, but is different to 4.
1758          * I should probably allow digits to alter the number.
1759          */
1760         call("Mode:set-num", ci->focus, 4);
1761         return 1;
1762 }
1763
1764 DEF_CMD(emacs_kill_doc)
1765 {
1766         if (ci->num <= 0 || ci->num == NO_NUMERIC) {
1767                 /* Check if modified. */
1768                 char *m = pane_attr_get(ci->focus, "doc-modified");
1769                 char *f = NULL;
1770                 if (m && strcmp(m, "yes") == 0)
1771                         f = pane_attr_get(ci->focus, "filename");
1772                 if (f) {
1773                         call("Message:modal", ci->focus, 0, NULL,
1774                              "Document is modified - please save or give prefix arg");
1775                         return 1;
1776                 }
1777         }
1778         return call("doc:destroy", ci->focus);
1779 }
1780
1781 DEF_CMD(emacs_revisit)
1782 {
1783         return call("doc:load-file", ci->focus, 2, NULL, NULL, -1);
1784 }
1785
1786 DEF_CMD(emacs_save_all)
1787 {
1788         int ret = call("docs:save-all", ci->focus, 0, NULL, NULL, 1);
1789
1790         if (ret == 1) {
1791                 call("Message", ci->focus, 0, NULL, "No files need to be saved.");
1792                 return 1;
1793         }
1794         if (ci->num == NO_NUMERIC) {
1795                 struct pane *p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM");
1796                 if (p)
1797                         return call("docs:show-modified", p);
1798         }
1799         return call("docs:save-all", ci->focus);
1800 }
1801
1802 DEF_CMD(emacs_reposition)
1803 {
1804         struct mark *m;
1805         int repoint = attr_find_int(ci->focus->attrs, "emacs-repoint");
1806
1807         if (repoint != -1) {
1808                 /* Move point to end of display, if that is in
1809                  * the right direction.  That will mean it has moved
1810                  * off the display.
1811                  */
1812                 m = mark_at_point(ci->focus, NULL, MARK_UNGROUPED);
1813                 if (m) {
1814                         struct mark *m2 = mark_dup(m);
1815                         call("Move-CursorXY", ci->focus, 0, m, NULL,
1816                              0, NULL, NULL,
1817                              INT_MAX, repoint < 0 ? ci->focus->h-1 : 0);
1818                         if (repoint < 0)
1819                                 /* can only move point backwards */
1820                                 if (m->seq < m2->seq)
1821                                         call("Move-to", ci->focus, 0, m);
1822                         if (repoint > 0)
1823                                 /* can only move point forwards */
1824                                 if (m->seq > m2->seq)
1825                                         call("Move-to", ci->focus, 0, m);
1826                         mark_free(m);
1827                         mark_free(m2);
1828                 }
1829                 attr_set_str(&ci->focus->attrs, "emacs-repoint", NULL);
1830         }
1831         return Efallthrough;
1832 }
1833
1834 DEF_CMD(emacs_start_search)
1835 {
1836         struct pane *p = NULL, *hp;
1837         int mode = 0;
1838
1839         hp = call_ret(pane, "attach-emacs-search-highlight", ci->focus);
1840
1841         if (hp)
1842                 p = call_ret(pane, "PopupTile", hp, 0, NULL, "TR2",
1843                              0, NULL, "");
1844
1845         if (!p)
1846                 return Efail;
1847         home_call(hp, "highlight:set-popup", p);
1848
1849         attr_set_str(&p->attrs, "prompt", "Search");
1850         attr_set_str(&p->attrs, "done-key", "Search String");
1851
1852         hp = call_ret(pane, "attach-history", p, 0, NULL, "*Search History*");
1853         if (hp)
1854                 p = hp;
1855
1856         call("doc:set-name", p, 0, NULL, "Search", -1);
1857         if (strcmp(ci->key, "K:C-R") == 0)
1858                 mode |= 1;
1859         if (strcmp(ci->key, "K:A-%") == 0)
1860                 mode |= 2;
1861         call_ret(pane, "attach-emacs-search", p, mode);
1862
1863         return 1;
1864 }
1865
1866 DEF_CMD(emacs_command)
1867 {
1868         struct pane *p;
1869
1870         p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2", 0, NULL, "");
1871         if (!p)
1872                 return Efail;
1873         attr_set_str(&p->attrs, "prompt", "Cmd");
1874         attr_set_str(&p->attrs, "done-key", "emacs:command");
1875         call("doc:set-name", p, 0, NULL, "K:Ax command", -1);
1876         p = call_ret(pane, "attach-history", p, 0, NULL, "*Command History*");
1877         if (p)
1878                 pane_register(p, 0, &find_handle.c, "cmd");
1879         return 1;
1880 }
1881
1882 DEF_CMD(emacs_do_command)
1883 {
1884         char *cmd = NULL;
1885         int ret;
1886
1887         if (!ci->str)
1888                 /* Aborted */
1889                 return Efail;
1890         asprintf(&cmd, "interactive-cmd-%s", ci->str);
1891         if (!cmd)
1892                 return Efail;
1893         ret = call(cmd, ci->focus, 0, ci->mark);
1894         free(cmd); cmd = NULL;
1895         if (ret == 0) {
1896                 asprintf(&cmd, "Command %s not found", ci->str);
1897                 call("Message", ci->focus, 0, NULL, cmd);
1898         } else if (ret < 0) {
1899                 asprintf(&cmd, "Command %s Failed", ci->str);
1900                 call("Message", ci->focus, 0, NULL, cmd);
1901         }
1902         free(cmd);
1903         return 1;
1904 }
1905
1906 DEF_CB(take_cmd)
1907 {
1908         struct call_return *cr = container_of(ci->comm, struct call_return, c);
1909         const char *cmd;
1910
1911         if (!ci->str)
1912                 return Enoarg;
1913         cmd = ci->str + 16;
1914         if (cr->p) {
1915                 struct mark *m = vmark_new(cr->p, MARK_UNGROUPED, NULL);
1916                 call("doc:list-add", cr->p, 0, m);
1917                 call("doc:set-attr", cr->p, 0, m, "cmd", 0, NULL, cmd);
1918         }
1919         return 1;
1920 }
1921
1922 REDEF_CMD(emacs_cmd_complete)
1923 {
1924         char *s;
1925         struct pane *doc = NULL, *pop = NULL, *p;
1926         struct call_return cr;
1927
1928         if (!ci->mark)
1929                 return Enoarg;
1930         s = call_ret(strsave, "doc:get-str", ci->focus);
1931         if (!s)
1932                 s = "";
1933         doc = call_ret(pane, "attach-doc-list", ci->focus);
1934         if (!doc)
1935                 return Efail;
1936         call("doc:set-name", doc, 0, NULL, "*Command List*");
1937         call("doc:set:autoclose", doc, 1);
1938         attr_set_str(&doc->attrs, "render-simple", "format");
1939         attr_set_str(&doc->attrs, "heading", "");
1940         attr_set_str(&doc->attrs, "line-format", "%cmd");
1941         cr.c = take_cmd;
1942         cr.p = doc;
1943         call_comm("keymap:list", ci->focus, &cr.c,
1944                   0, NULL, "interactive-cmd-");
1945         call("doc:list-sort", doc, 0, NULL, "cmd");
1946         pop = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM1r");
1947         if (!pop)
1948                 goto fail;
1949         p = home_call_ret(pane, cr.p, "doc:attach-view", pop, -1, NULL, "simple");
1950         if (!p)
1951                 goto fail_pop;
1952         attr_set_str(&p->attrs, "done-key", "Replace");
1953         p = call_ret(pane, "attach-render-complete", p);
1954         if (!p)
1955                 goto fail_pop;
1956         cr = call_ret(all, "Complete:prefix", p, 1, NULL, s);
1957         if (cr.s && strlen(cr.s) <= strlen(s) && cr.ret-1 > 1) {
1958                 /* We need the dropdown - delete prefix and drop-down will
1959                  * insert result.
1960                  */
1961                 struct mark *start = mark_dup(ci->mark);
1962                 call("Move-Char", ci->focus, -strlen(s), start);
1963                 call("Replace", ci->focus, 1, start);
1964                 mark_free(start);
1965                 return 1;
1966         }
1967         if (cr.s) {
1968                 /* Replace 's' with the result */
1969                 struct mark *start = mark_dup(ci->mark);
1970                 call("Move-Char", ci->focus, -strlen(s), start);
1971                 call("Replace", ci->focus, 1, start, cr.s);
1972                 mark_free(start);
1973         }
1974         /* Now need to close the popup */
1975         pane_close(pop);
1976         pane_close(doc);
1977         return 1;
1978
1979 fail_pop:
1980         pane_close(pop);
1981 fail:
1982         pane_close(doc);
1983         return Efail;
1984 }
1985
1986 DEF_CMD(emacs_version)
1987 {
1988         char *v = NULL;
1989
1990         asprintf(&v, "%s ; emacs-mode - v" VERSION " - " VERS_DATE, edlib_version);
1991         if (v)
1992                 call("Message", ci->focus, 0, NULL, v);
1993         free(v);
1994         return 1;
1995 }
1996
1997 DEF_CMD(emacs_log)
1998 {
1999         /* View the debug log */
2000         struct pane *p, *doc;
2001
2002         doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL, "*Debug Log*");
2003         if (!doc) {
2004                 call("interactive-cmd-view-log", ci->focus);
2005                 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL,
2006                              "*Debug Log*");
2007         }
2008         if (!doc)
2009                 return Efail;
2010         p = call_ret(pane, "ThisPane", ci->focus);
2011         if (p)
2012                 home_call(doc, "doc:attach-view", p, 1);
2013         return 1;
2014 }
2015
2016 DEF_CMD(emacs_mark)
2017 {
2018         struct mark *m = call_ret(mark2, "doc:point", ci->focus);
2019
2020         call("selection:clear", ci->focus, 0, m);
2021
2022         call("Move-to", ci->focus, 1);
2023         m = call_ret(mark2, "doc:point", ci->focus);
2024         if (m)
2025                 /* ci->num == 1 means replacable */
2026                 call("selection:set", ci->focus,
2027                      ci->num == 1 ? 3 : 1, m);
2028         return 1;
2029 }
2030
2031 DEF_CMD(emacs_swap_mark)
2032 {
2033         struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2034         struct mark *m;
2035
2036         if (!mk)
2037                 return 1;
2038         m = mark_dup(mk);
2039         call("Move-to", ci->focus, 1); /* Move mark to point */
2040         call("Move-to", ci->focus, 0, m); /* Move point to old mark */
2041         call("selection:set", ci->focus,
2042              ci->num == 1 ? 3 : 1, mk);
2043         mark_free(m);
2044         return 1;
2045 }
2046
2047 DEF_CMD(emacs_wipe)
2048 {
2049         /* Delete text from point to mark */
2050         struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2051         char *str;
2052         int ret;
2053
2054         if (!mk)
2055                 return 1;
2056         /* Remove any selection so it cannot be claimed and so replace this copy */
2057         call("selection:claim", ci->focus);
2058         call("selection:discard", ci->focus);
2059
2060         str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, mk);
2061         if (str && *str)
2062                 call("copy:save", ci->focus, 0, NULL, str);
2063         ret = call("Replace", ci->focus, 1, mk);
2064         /* Clear mark */
2065         call("selection:clear", ci->focus, 0, mk);
2066
2067         return ret;
2068 }
2069
2070 DEF_CMD(emacs_copy)
2071 {
2072         /* copy text from point to mark */
2073         struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2074         char *str;
2075
2076         if (!mk)
2077                 return 1;
2078         /* Remove any selection so it cannot be claimed and so replace this copy */
2079         call("selection:claim", ci->focus);
2080         call("selection:discard", ci->focus);
2081
2082         str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, mk);
2083         if (str && *str)
2084                 call("copy:save", ci->focus, 0, NULL, str);
2085         /* Clear current highlight */
2086         call("selection:clear", ci->focus, 0, mk);
2087         return 1;
2088 }
2089
2090 DEF_CMD(emacs_yank)
2091 {
2092         int n = RPT_NUM(ci);
2093         char *str;
2094         struct mark *mk;
2095         struct mark *m = NULL;
2096
2097         /* If there is a selection elsewhere, we want to commit it */
2098         call("selection:discard", ci->focus);
2099         call("selection:commit", ci->focus);
2100
2101         str = call_ret(strsave, "copy:get", ci->focus, n - 1);
2102         if (!str || !*str)
2103                 return 1;
2104         /* If mark exists and is active, replace marked regions */
2105         mk = call_ret(mark2, "doc:point", ci->focus);
2106         if (mk && call("selection:clear", ci->focus, 0, mk) >= 1) {
2107                 char *str2 = call_ret(strsave, "doc:get-str", ci->focus,
2108                                       0, NULL, NULL, 0, mk);
2109                 if (str2 && *str2)
2110                         call("copy:save", ci->focus, 0, NULL, str2);
2111                 m = mark_dup(mk);
2112         }
2113
2114         call("Move-to", ci->focus, 1);
2115         call("Replace", ci->focus, 1, m, str);
2116         mark_free(m);
2117         mk = call_ret(mark2, "doc:point", ci->focus);
2118         call("Mode:set-num2", ci->focus, N2_yank);
2119         return 1;
2120 }
2121
2122 DEF_CMD(emacs_yank_pop)
2123 {
2124         struct mark *mk, *m;
2125         char *str;
2126         int num = N2a(ci);
2127
2128         if (N2(ci) != N2_yank)
2129                 return Einval;
2130         mk = call_ret(mark2, "doc:point", ci->focus);
2131         if (!mk)
2132                 return 1;
2133         num += 1;
2134         str = call_ret(strsave, "copy:get", ci->focus, num);
2135         if (!str) {
2136                 num = 0;
2137                 str = call_ret(strsave, "copy:get", ci->focus, num);
2138         }
2139         if (!str)
2140                 return Efail;
2141         m = mark_dup(mk);
2142         mark_step(m, 0);
2143         call("Replace", ci->focus, 1, mk, str);
2144         call("Move-to", ci->focus, 1, m);
2145         mark_free(m);
2146         call("Mode:set-num2", ci->focus, (num << 16) | N2_yank);
2147         return 1;
2148 }
2149
2150 DEF_CMD(emacs_selection_menu_add)
2151 {
2152         struct pane *p = ci->focus;
2153         call("menu-add", p, 0, NULL, "Cut", 0, NULL, ":C-W");
2154         call("menu-add", p, 0, NULL, "Copy", 0, NULL, ":A-w");
2155         call("menu-add", p, 0, NULL, "Paste-in", 0, NULL, ":C-Y");
2156         return Efallthrough;
2157 }
2158
2159 DEF_CMD(emacs_goto_line)
2160 {
2161         if (ci->num == NO_NUMERIC)
2162                 return 1;
2163         call("CountLines", ci->focus, ci->num, ci->mark, "goto:line");
2164         call("Move-View-Pos", ci->focus, 0, ci->mark);
2165         return 1;
2166 }
2167
2168 DEF_CMD(emacs_next_match)
2169 {
2170         call("Mode:set-num2", ci->focus, N2_match);
2171         call("Message:modal", ci->focus, 0, NULL, "Type ` to search again");
2172         return call("interactive-cmd-next-match", ci->focus, ci->num, NULL, NULL,
2173                     strcmp(ci->key, "K-`") == 0);
2174 }
2175
2176 DEF_CMD(emacs_match_again)
2177 {
2178         if (N2(ci) != N2_match)
2179                 return Efallthrough;
2180         else
2181                 return emacs_next_match_func(ci);
2182 }
2183
2184 DEF_CMD(emacs_make)
2185 {
2186         call("interactive-cmd-make", ci->focus,
2187              ci->num, ci->mark, NULL,
2188              strcmp(ci->key, "K:CC:C-M") == 0);
2189         return 1;
2190 }
2191
2192 DEF_CMD(emacs_paste)
2193 {
2194         char *str;
2195
2196         /* First commit the selection, then collect it */
2197         call("selection:commit", ci->focus);
2198         str = call_ret(strsave, "copy:get", ci->focus);
2199
2200         call("Move-CursorXY", ci->focus,
2201              0, NULL, NULL, 0, NULL, NULL, ci->x, ci->y);
2202
2203         if (!str || !*str)
2204                 return 1;
2205
2206         call("Replace", ci->focus, 0, NULL, str);
2207
2208         pane_take_focus(ci->focus);
2209
2210         return 1;
2211 }
2212
2213 DEF_CMD(emacs_readonly)
2214 {
2215         char *ro;
2216
2217         ro = pane_attr_get(ci->focus, "doc-readonly");
2218
2219         if (ro && strcmp(ro,"yes") == 0)
2220                 call("doc:set:readonly", ci->focus, 0);
2221         else
2222                 call("doc:set:readonly", ci->focus, 1);
2223         return 1;
2224 }
2225
2226 DEF_CMD(emacs_shift)
2227 {
2228         int rpt = ci->num;
2229         int shift;
2230
2231         shift = pane_attr_get_int(ci->focus, "render-wrap", -1);
2232         if (strcmp(ci->key, "K:CX->") == 0 || strcmp(ci->key, "K->") == 0)
2233                 rpt = -rpt;
2234         if (rpt == NO_NUMERIC) {
2235                 if (shift < 0)
2236                         shift = 0;
2237                 else
2238                         shift += 8;
2239         } else if (rpt == -NO_NUMERIC) {
2240                 if (shift >= 8)
2241                         shift -= 8;
2242                 else if (shift > 0)
2243                         shift = 0;
2244                 else
2245                         shift = -1;
2246         } else if (rpt >= 0) {
2247                 if (shift < 0)
2248                         shift = 0;
2249                 shift += rpt;
2250         } else {
2251                 if (shift > 0 && shift + rpt < 0)
2252                         shift = 0;
2253                 else
2254                         shift += rpt;
2255         }
2256         if (shift < 0)
2257                 attr_set_str(&ci->focus->attrs, "render-wrap", "yes");
2258         else if (shift > 0)
2259                 attr_set_int(&ci->focus->attrs, "render-wrap", shift);
2260         else if (rpt > 0)
2261                 attr_set_str(&ci->focus->attrs, "render-wrap", "0 auto");
2262         else
2263                 /* When reducing shift to zero, don't enable auto */
2264                 attr_set_int(&ci->focus->attrs, "render-wrap", 0);
2265         call("view:changed", ci->focus);
2266         call("Mode:set-num2", ci->focus, N2_shift);
2267         call("Message:modal", ci->focus, 0, NULL, "Type < or > to shift again");
2268         return 1;
2269 }
2270
2271 DEF_CMD(emacs_shift_again)
2272 {
2273         if (N2(ci) != N2_shift)
2274                 return Efallthrough;
2275         else
2276                 return emacs_shift_func(ci);
2277 }
2278
2279 DEF_CMD(emacs_growx)
2280 {
2281         if (ci->key[strlen(ci->key)-1] == '}')
2282                 call("Tile:x+", ci->focus, ci->num);
2283         else
2284                 call("Tile:x-", ci->focus, ci->num);
2285         call("Mode:set-num2", ci->focus, N2_growx);
2286         call("Message:modal", ci->focus, 0, NULL, "Type { or } to grow again");
2287         return 1;
2288 }
2289
2290 DEF_CMD(emacs_growx_again)
2291 {
2292         if (N2(ci) != N2_growx)
2293                 return Efallthrough;
2294         else
2295                 return emacs_growx_func(ci);
2296 }
2297
2298 DEF_CMD(emacs_scale_relative)
2299 {
2300         struct pane *p = ci->focus;
2301         char *sc = pane_attr_get(p, "scale:M");
2302         int scale = 0;
2303         char num[20];
2304
2305         if (!sc) {
2306                 call("Message:modal", p, 0, NULL,
2307                      "Cannot zoom display with fixed-sized font");
2308                 return 1;
2309         }
2310         sc = pane_attr_get(p, "scale");
2311         if (sc && strchr(sc, 'x')) {
2312                 call("Message:modal", p, 0, NULL,
2313                      "Cannot zoom display with fixed layout");
2314                 return 1;
2315         }
2316
2317         if (sc)
2318                 scale = atoi(sc);
2319         if (scale <= 0)
2320                 scale = 1000;
2321         if (ci->key[strlen(ci->key)-1] == '-')
2322                 scale = 10 * scale / 12;
2323         else
2324                 scale = 12 * scale / 10;
2325         snprintf(num, sizeof(num)-1, "%d", scale);
2326         call("window:set:scale", p, 0, NULL, num);
2327         return 1;
2328 }
2329
2330 DEF_CMD(emacs_curs_pos)
2331 {
2332         struct mark *c;
2333         int col = 0;
2334         int chars = 0;
2335         wint_t ch, nxt;
2336         char *msg = NULL;
2337
2338         if (!ci->mark)
2339                 return Enoarg;
2340         c = mark_dup(ci->mark);
2341         nxt = doc_following(ci->focus, c);
2342
2343         while ((ch = doc_prev(ci->focus, c)) != WEOF && !is_eol(ch))
2344                 ;
2345         while (mark_ordered_not_same(c, ci->mark)) {
2346                 ch = doc_next(ci->focus, c);
2347                 if (is_eol(ch)) {
2348                         col = 0;
2349                         chars = 0;
2350                 } else if (ch == '\t') {
2351                         col = (col|7)+1;
2352                         chars += 1;
2353                 } else {
2354                         col += 1;
2355                         chars += 1;
2356                 }
2357         }
2358         mark_free(c);
2359         asprintf(&msg, "Cursor at column %d (%d chars), char is %d (0x%x)",
2360                  col, chars, nxt, nxt);
2361         call("Message", ci->focus, 0, NULL, msg);
2362         free(msg);
2363         return 1;
2364 }
2365
2366 DEF_CMD(emacs_word_count)
2367 {
2368         struct mark *mk;
2369         struct pane *p = ci->focus;
2370         char *msg = NULL;
2371         int wp;
2372
2373         if (!ci->mark)
2374                 return Enoarg;
2375
2376         mk = call_ret(mark2, "doc:point", p);
2377         if (mk && attr_find_int(mk->attrs, "selection:active") <= 0)
2378                 mk = NULL;
2379         call("CountLines", p, 0, ci->mark);
2380         wp = attr_find_int(ci->mark->attrs, "word");
2381         if (mk) {
2382                 int wm;
2383                 call("CountLines", p, 0, mk);
2384                 wm = attr_find_int(mk->attrs, "word");
2385                 asprintf(&msg, "%d words in region", abs(wp-wm));
2386         } else {
2387                 int wd = pane_attr_get_int(p, "words", 0);
2388                 asprintf(&msg, "After word%s %d of %d", wp==2?"":"s", wp-1, wd);
2389         }
2390         call("Message", p, 0, NULL, msg);
2391         free(msg);
2392         return 1;
2393 }
2394
2395 DEF_CMD(emacs_fill)
2396 {
2397         struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2398         struct mark *p = call_ret(mark, "doc:point", ci->focus);
2399         struct pane *p2;
2400
2401         if (call("selection:clear", ci->focus, 0, mk, NULL, 0, p) == Efalse)
2402                 mk = NULL;
2403
2404         if (strcmp(ci->key, "K:A-q") == 0) {
2405                 if (call("fill-paragraph", ci->focus, ci->num, p, NULL,
2406                          0, mk) == Efallthrough) {
2407                         p2 = call_ret(pane, "attach-textfill", ci->focus);
2408                         if (p2)
2409                                 call("fill-paragraph", p2, ci->num, p, NULL,
2410                                      0, mk);
2411                 }
2412         } else {
2413                 /* Don't try to load anything, the file-type should
2414                  * have loaded something if relevant
2415                  */
2416                 if (call("reindent-paragraph", ci->focus, ci->num, p, NULL,
2417                          0, mk) == 0)
2418                         call("Message", ci->focus, 0, NULL,
2419                              "Reindent not supported on the document.");
2420         }
2421         return 1;
2422 }
2423
2424 DEF_CMD(emacs_abbrev)
2425 {
2426         call("attach-abbrev", ci->focus);
2427         return 1;
2428 }
2429
2430 DEF_CMD(emacs_showinput)
2431 {
2432         struct pane *p, *doc;
2433
2434         if (call("input:log", ci->focus) <= 0) {
2435                 call("Message", ci->focus, 0, NULL,
2436                      "Cannot get log of recent input.");
2437                 return Efail;
2438         }
2439
2440         doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL, "*Debug Log*");
2441         if (!doc) {
2442                 call("interactive-cmd-view-log", ci->focus);
2443                 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL,
2444                              "*Debug Log*");
2445         }
2446         if (!doc)
2447                 return Efail;
2448         p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM");
2449         if (p) {
2450                 p = home_call_ret(pane, doc, "doc:attach-view", p, 1);
2451                 if (p)
2452                         call("doc:file", p, 1);
2453         }
2454         return 1;
2455 }
2456
2457 DEF_CMD(emacs_macro_start)
2458 {
2459         int ret;
2460
2461         ret = call("macro:capture", ci->focus);
2462         if (ret == Efalse)
2463                 call("Message", ci->focus, 0, NULL,
2464                      "Macro capture already happening");
2465         else if (ret <= 0)
2466                 call("Message", ci->focus, 0, NULL,
2467                      "Macro facility not available");
2468         return ret;
2469 }
2470
2471 DEF_CMD(emacs_macro_stop)
2472 {
2473         int ret;
2474
2475         ret = call("macro:finished", ci->focus, 2);
2476         if (ret > 0)
2477                 call("Message", ci->focus, 0, NULL,
2478                      "Macro successfully created.");
2479         else if (ret == Efalse)
2480                 call("Message", ci->focus, 0, NULL,
2481                      "No macro being created.");
2482         else
2483                 call("Message", ci->focus, 0, NULL,
2484                      "Failure creating macro.");
2485         return ret;
2486 }
2487
2488 DEF_CMD(emacs_macro_run)
2489 {
2490         int cnt = RPT_NUM(ci);
2491
2492         if (strcmp(ci->key, "K-e") == 0 && N2(ci) != N2_runmacro)
2493                 return Efallthrough;
2494
2495         if (cnt < 1)
2496                 cnt = 1;
2497         while (cnt >= 1 &&
2498                call("macro:replay", ci->focus, 1) > 0)
2499                 cnt -= 1;
2500
2501         call("Mode:set-num2", ci->focus, N2_runmacro);
2502         call("Message:modal", ci->focus, 0, NULL, "Type 'e' to repeat macro");
2503         return cnt < 1 ? 1 : Efail;
2504 }
2505
2506 struct bb {
2507         struct buf b;
2508         struct command c;
2509         bool first;
2510         int count;
2511 };
2512 static const char spell_choices[] =
2513         "0123456789-=;,/ABCDEFGHIJKLMNOPQRSTUVWXYZ";
2514 DEF_CB(get_suggestion)
2515 {
2516         struct bb *b = container_of(ci->comm, struct bb, c);
2517
2518         if (!ci->str)
2519                 return Enoarg;
2520
2521         if (b->first)
2522                 buf_concat(&b->b, " - (a)ccept, (i)nsert -     ");
2523         else
2524                 buf_concat(&b->b, ", ");
2525         b->first = False;
2526         if (b->count < (int)sizeof(spell_choices)-1) {
2527                 buf_append(&b->b, '(');
2528                 buf_append(&b->b, spell_choices[b->count]);
2529                 buf_append(&b->b, ')');
2530         }
2531         b->count += 1;
2532         buf_concat(&b->b, ci->str);
2533         return 1;
2534 }
2535
2536 DEF_CMD(emacs_spell)
2537 {
2538         struct mark *st;
2539         char *word;
2540         int ret;
2541         int rpt = RPT_NUM(ci);
2542
2543         if (!ci->mark)
2544                 return Enoarg;
2545
2546         /* We always find a word that is partly *after* the given
2547          * make, but we want to find the word before point, so step
2548          * back.
2549          */
2550         doc_prev(ci->focus, ci->mark);
2551 again:
2552         if (ci->num != NO_NUMERIC)
2553                 /* As a repeat-count was given, only look at intersting words */
2554                 call("Spell:NextWord", ci->focus, 0, ci->mark);
2555         st = mark_dup(ci->mark);
2556         word = call_ret(str, "Spell:ThisWord", ci->focus, 0, ci->mark, NULL, 0, st);
2557         if (!word || !*word) {
2558                 /* No word found */
2559                 call("Message", ci->focus, 0, NULL,
2560                      "Spell check reached end-of-file");
2561                 call("Spell:Save", ci->focus);
2562                 free(word);
2563                 mark_free(st);
2564                 return 1;
2565         }
2566         ret = call("Spell:Check", ci->focus, 0, NULL, word);
2567         if (ret > 0) {
2568                 rpt -= 1;
2569                 mark_free(st);
2570                 if (rpt)
2571                         goto again;
2572                 call("Message", ci->focus, 0, NULL,
2573                      strconcat(ci->focus, "\"", word,
2574                                "\" is a correct spelling."));
2575         } else if (ret == Efalse) {
2576                 struct bb b;
2577                 buf_init(&b.b);
2578                 buf_concat(&b.b, "\"");
2579                 buf_concat(&b.b, word);
2580                 buf_concat(&b.b, "\" is NOT correct");
2581                 b.count = 0;
2582                 b.first = True;
2583                 b.c = get_suggestion;
2584                 call_comm("Spell:Suggest", ci->focus, &b.c,
2585                           0, NULL, word);
2586                 if (b.first) {
2587                         buf_concat(&b.b, " ... no suggestions");
2588                 } else {
2589                         attr_set_str(&ci->focus->attrs, "spell:last-error",
2590                                      word);
2591                         attr_set_str(&ci->focus->attrs, "spell:suggestions",
2592                                      buf_final(&b.b));
2593                         attr_set_int(&ci->focus->attrs, "spell:offset", 0);
2594                 }
2595                 call("Mode:set-all", ci->focus, rpt-1, NULL, ":Spell");
2596                 call("Message:modal", ci->focus, 0, NULL, buf_final(&b.b));
2597                 free(buf_final(&b.b));
2598         } else if (ret == Efail) {
2599                 rpt -= 1;
2600                 if (rpt)
2601                         goto again;
2602
2603                 call("Message", ci->focus, 0, NULL,
2604                      strconcat(ci->focus, "\"", word,
2605                                "\" is not a word."));
2606         } else
2607                 call("Message", ci->focus, 0, NULL,
2608                      strconcat(ci->focus, "Spell check failed for \"", word,
2609                                "\""));
2610         mark_free(st);
2611         return 1;
2612 }
2613
2614 DEF_CMD(emacs_spell_choose)
2615 {
2616         const char *k = ksuffix(ci, "K:Spell-");
2617         char match[4] = "( )";
2618         char *suggest = attr_find(ci->focus->attrs,
2619                                   "spell:suggestions");
2620         char *last = attr_find(ci->focus->attrs,
2621                                "spell:last-error");
2622         char *cp, *ep;
2623         struct mark *m;
2624         int i;
2625
2626         if (!*k || !suggest || !last || !ci->mark)
2627                 return 1;
2628         match[1] = *k;
2629         cp = strstr(suggest, match);
2630         if (!cp)
2631                 return 1;
2632         cp += 3;
2633         ep = strchr(cp, ',');
2634         if (ep)
2635                 cp = strnsave(ci->focus, cp, ep-cp);
2636
2637         m = mark_dup(ci->mark);
2638         i = utf8_strlen(last);
2639         while (i > 0) {
2640                 doc_prev(ci->focus, m);
2641                 i -= 1;
2642         }
2643         call("doc:replace", ci->focus, 0, m, cp, 0, ci->mark);
2644         attr_set_str(&ci->focus->attrs, "spell:suggestions", NULL);
2645         attr_set_str(&ci->focus->attrs, "spell:last-error", NULL);
2646
2647         if (ci->num) {
2648                 doc_next(ci->focus, ci->mark);
2649                 home_call(ci->home, "emacs:respell", ci->focus,
2650                           ci->num, ci->mark, NULL,
2651                           ci->num2, ci->mark2);
2652         }
2653
2654         return 1;
2655 }
2656
2657 DEF_CMD(emacs_spell_abort)
2658 {
2659         return 1;
2660 }
2661
2662 DEF_CMD(emacs_spell_skip)
2663 {
2664         if (ci->num) {
2665                 doc_next(ci->focus, ci->mark);
2666                 home_call(ci->home, "emacs:respell", ci->focus,
2667                           ci->num, ci->mark, NULL,
2668                           ci->num2, ci->mark2);
2669         }
2670         return 1;
2671 }
2672
2673 DEF_CMD(emacs_spell_insert)
2674 {
2675         char *last = attr_find(ci->focus->attrs,
2676                                "spell:last-error");
2677         if (last) {
2678                 attr_set_str(&ci->focus->attrs, "spell:suggestions", NULL);
2679                 attr_set_str(&ci->focus->attrs, "spell:last-error", NULL);
2680                 call("Spell:AddWord", ci->focus, 1, NULL, last);
2681         }
2682
2683         if (ci->num) {
2684                 doc_next(ci->focus, ci->mark);
2685                 home_call(ci->home, "emacs:respell", ci->focus,
2686                           ci->num, ci->mark, NULL,
2687                           ci->num2, ci->mark2);
2688         }
2689         return 1;
2690 }
2691
2692 DEF_CMD(emacs_spell_accept)
2693 {
2694         char *last = attr_find(ci->focus->attrs,
2695                                "spell:last-error");
2696         if (last) {
2697                 attr_set_str(&ci->focus->attrs, "spell:suggestions", NULL);
2698                 attr_set_str(&ci->focus->attrs, "spell:last-error", NULL);
2699                 call("Spell:AddWord", ci->focus, 0, NULL, last);
2700         }
2701
2702         if (ci->num) {
2703                 doc_next(ci->focus, ci->mark);
2704                 home_call(ci->home, "emacs:respell", ci->focus,
2705                           ci->num, ci->mark, NULL,
2706                           ci->num2, ci->mark2);
2707         }
2708         return 1;
2709 }
2710
2711 static int spell_shift(struct pane *focus safe, int num, int inc)
2712 {
2713         int o = pane_attr_get_int(focus, "spell:offset", 0);
2714         int i;
2715         char *msg;
2716         char *c;
2717
2718         o += inc;
2719         msg = pane_attr_get(focus, "spell:suggestions");
2720         if (!msg)
2721                 return 1;
2722         for (i = 0; i < o && (c = strchr(msg, ',')) != NULL; i++)
2723                 msg = c+1;
2724         attr_set_int(&focus->attrs, "spell:offset", i);
2725
2726         call("Mode:set-all", focus, num, NULL, ":Spell");
2727         call("Message:modal", focus, 0, NULL, msg);
2728         return 1;
2729 }
2730
2731 DEF_CMD(emacs_spell_left)
2732 {
2733         return spell_shift(ci->focus, ci->num, -1);
2734 }
2735
2736 DEF_CMD(emacs_spell_right)
2737 {
2738         return spell_shift(ci->focus, ci->num, 1);
2739 }
2740
2741 DEF_CMD(emacs_quote)
2742 {
2743         char b[6];
2744         wint_t wch = WEOF;
2745         char *str = NULL;
2746         struct mark *mk = NULL;
2747         bool free_mark = False;
2748
2749         if (ci->num >= 0 && ci->num < NO_NUMERIC)
2750                 wch = ci->num;
2751         else if (N2(ci) == N2_uniquote && ci->mark &&
2752                  (str = attr_find(ci->mark->attrs, "emacs:unicode_char")) != NULL) {
2753                 struct call_return cr;
2754                 int i = N2a(ci);
2755                 if (ci->num < 0 && i > 1)
2756                         i -= 1;
2757                 else
2758                         i += 1;
2759                 cr = call_ret(all, "Unicode-names", ci->focus, i, NULL, str);
2760                 if (cr.s && cr.i && (wint_t)cr.i != WEOF) {
2761                         wch = cr.i;
2762                         call("Message", ci->focus, 0, NULL,
2763                              strconcat(ci->focus,
2764                                        "Unicode char <", cr.s, ">"));
2765                         call("Mode:set-num2", ci->focus,
2766                              N2_uniquote | (i << 16));
2767                         mk = mark_dup(ci->mark);
2768                         doc_prev(ci->focus, mk);
2769                         free_mark = True;
2770                 } else {
2771                         call("Message", ci->focus, 0, NULL,
2772                              strconcat(ci->focus,
2773                                        "Cannot find another character <", str, ">"));
2774                         return Efail;
2775                 }
2776         } else if (wch == WEOF && ci->mark &&
2777                    (mk = call_ret(mark2, "doc:point", ci->focus)) != NULL &&
2778                    call("selection:clear", ci->focus, 0, mk) >= 1 &&
2779                    (str = call_ret(strsave, "doc:get-str", ci->focus,
2780                                    0, NULL, NULL, 0, mk)) != NULL) {
2781                 int x;
2782                 char *ep;
2783                 if (*str == '#')
2784                         str ++;
2785                 x = strtoul(str, &ep, 16);
2786                 if (ep && *ep == 0) {
2787                         wch = x;
2788                         call("Message", ci->focus, 0, NULL,
2789                              strconcat(ci->focus, "Hex code 0x", str));
2790                 } else {
2791                         struct call_return cr;
2792                         cr = call_ret(all, "Unicode-names", ci->focus,
2793                                       1, NULL, str);
2794                         if (cr.s && cr.i) {
2795                                 wch = cr.i;
2796                                 call("Message", ci->focus, 0, NULL,
2797                                      strconcat(ci->focus,
2798                                                "Unicode char <", cr.s, ">"));
2799                                 if (ci->mark) {
2800                                         attr_set_str(&ci->mark->attrs,
2801                                                      "emacs:unicode_char",
2802                                                      str);
2803                                         call("Mode:set-num2", ci->focus,
2804                                              N2_uniquote | (1 << 16));
2805                                 }
2806                         } else {
2807                                 call("Message", ci->focus, 0, NULL,
2808                                      strconcat(ci->focus,
2809                                                "Cannot find character <", str, ">"));
2810                                 return Efail;
2811                         }
2812                 }
2813         }
2814         if (wch == WEOF) {
2815                 call("Mode:set-all", ci->focus, ci->num, NULL, ":CQ", ci->num2);
2816                 return 1;
2817         }
2818         call("Replace", ci->focus, 0, mk, put_utf8(b, wch));
2819         if (free_mark)
2820                 mark_free(mk);
2821         return 1;
2822 }
2823
2824 struct docs_helper {
2825         struct command c;
2826         int cnt;
2827         struct pane *p safe;
2828 };
2829
2830 DEF_CMD(emacs_doc_menu)
2831 {
2832         const char *d = ksuffix(ci, "emacs:doc-menu:");
2833         struct pane *p = call_ret(pane, "docs:byname", ci->focus,
2834                                   0, NULL, d);
2835
2836         if (p) {
2837                 struct pane *t = call_ret(pane, "ThisPane", ci->focus);
2838                 if (t)
2839                         home_call(p, "doc:attach-view", t, 1);
2840         }
2841         return 1;
2842 }
2843
2844 DEF_CB(emacs_menu_add_doc)
2845 {
2846         struct docs_helper *dh = container_of(ci->comm, struct docs_helper, c);
2847         char *name = pane_attr_get(ci->focus, "doc-name");
2848
2849         if (!name)
2850                 return Efallthrough;
2851         if (dh->cnt >= 10)
2852                 return 1;
2853         dh->cnt += 1;
2854         call("menu:add", dh->p, 0, NULL, name, 0, NULL,
2855              strconcat(ci->home, " emacs:doc-menu:", name));
2856         return Efallthrough;
2857 }
2858
2859 DEF_CMD(emacs_menu_refresh)
2860 {
2861         struct pane *p = ci->focus;
2862         char *n = pane_attr_get(p, "doc-name");
2863         struct docs_helper dh;
2864
2865         if (!n || strcmp(n, "Documents") != 0)
2866                 return 1;
2867
2868         call("menu:clear", p);
2869         dh.c = emacs_menu_add_doc;
2870         dh.cnt = 0;
2871         dh.p = p;
2872         call_comm("docs:byeach", p, &dh.c);
2873         call("menu:add", p, 0, NULL, "List all", 0, NULL, ":C-X :C-B");
2874         return 1;
2875 }
2876
2877 static char *menus[][3] = {
2878         { "Help/Recent", ":F1 l", "R" },
2879         { "Documents/List all", ":C-X :C-B", "R"},
2880         { "File/Open", ":C-X :C-F", "L"},
2881         { "File/Save", ":C-X :C-S", "L"},
2882         { "File/Exit", ":C-X :C-C", "L"},
2883         { "Edit/Copy", ":A-w", "L"},
2884 };
2885
2886 DEF_CMD(emacs_menubar_configure)
2887 {
2888         unsigned int i;
2889
2890         for (i = 0; i < ARRAY_SIZE(menus); i++)
2891                 call("menubar-add", ci->focus,
2892                      menus[i][2][0] == 'R' ? 2 : 0,
2893                      NULL, menus[i][0],
2894                      0, NULL, menus[i][1]);
2895         /* Allow other ancestor to configure */
2896         return Efallthrough;
2897 }
2898
2899 DEF_PFX_CMD(cx_cmd, ":CX");
2900 DEF_PFX_CMD(cx4_cmd, ":CX4");
2901 DEF_PFX_CMD(cx5_cmd, ":CX5");
2902 DEF_PFX_CMD(cx44_cmd, ":CX44");
2903 DEF_PFX_CMD(cc_cmd, ":CC");
2904
2905 static struct map *emacs_map;
2906 DEF_LOOKUP_CMD(mode_emacs, emacs_map);
2907
2908 static void emacs_init(void)
2909 {
2910         unsigned i;
2911         struct map *m;
2912
2913         if (emacs_map)
2914                 return;
2915         m = key_alloc();
2916         key_add(m, "K:C-X", &cx_cmd.c);
2917         key_add(m, "K:CX-4", &cx4_cmd.c);
2918         /* C-\ is generated by C-4.  Weird... */
2919         key_add(m, "K:CX:C-\\", &cx4_cmd.c);
2920         key_add(m, "K:CX-5", &cx5_cmd.c);
2921         key_add(m, "K:CX4-4", &cx44_cmd.c);
2922         key_add(m, "K:CX4:C-\\", &cx44_cmd.c);
2923         key_add(m, "K:C-C", &cc_cmd.c);
2924
2925         key_add(m, "K:C-Q", &emacs_quote);
2926
2927         for (i = 0; i < ARRAY_SIZE(move_commands); i++) {
2928                 struct move_command *mc = &move_commands[i];
2929                 key_add(m, mc->k1, &mc->cmd);
2930                 if (mc->k2)
2931                         key_add(m, mc->k2, &mc->cmd);
2932                 if (mc->k3)
2933                         key_add(m, mc->k3, &mc->cmd);
2934         }
2935
2936         for (i = 0; i < ARRAY_SIZE(simple_commands); i++) {
2937                 struct simple_command *sc = &simple_commands[i];
2938                 key_add(m, sc->k, &sc->cmd);
2939         }
2940
2941         key_add(m, "K:C-O", &emacs_open_line);
2942
2943         key_add(m, "K:C-_", &emacs_undo);
2944         key_add(m, "K:CX-u", &emacs_undo);
2945         key_add(m, "K:C-/", &emacs_undo);
2946         key_add(m, "K:C-Z", &emacs_undo);
2947
2948         key_add(m, "K:C-L", &emacs_recenter);
2949
2950         key_add(m, "K:CX:C-F", &emacs_findfile);
2951         key_add(m, "K:CX4:C-F", &emacs_findfile);
2952         key_add(m, "K:CX4-f", &emacs_findfile);
2953         key_add(m, "K:CX44-f", &emacs_findfile);
2954         key_add_prefix(m, "File Found:", &emacs_findfile);
2955
2956         key_add(m, "K:CX:C-W", &emacs_writefile);
2957         key_add_prefix(m, "emacs:write_file:", &emacs_do_writefile);
2958
2959         key_add(m, "K:CX-i", &emacs_insertfile);
2960         key_add_prefix(m, "emacs:insert_file:", &emacs_do_insertfile);
2961
2962         key_add(m, "K:CX-b", &emacs_finddoc);
2963         key_add(m, "K:CX4-b", &emacs_finddoc);
2964         key_add(m, "K:CX44-b", &emacs_finddoc);
2965         key_add_prefix(m, "Doc Found:", &emacs_finddoc);
2966
2967         key_add(m, "K:CX:C-B", &emacs_viewdocs);
2968         key_add(m, "K:CX4:C-B", &emacs_viewdocs);
2969         key_add(m, "K:CX44:C-B", &emacs_viewdocs);
2970
2971         key_add(m, "K:CX-k", &emacs_kill_doc);
2972
2973         key_add(m, "K:CX-s", &emacs_save_all);
2974
2975         key_add(m, "K:CX:C-V", &emacs_revisit);
2976
2977         key_add(m, "K:CX-=", &emacs_curs_pos);
2978         key_add(m, "K:A-=", &emacs_word_count);
2979
2980         key_add(m, "K:CX-<", &emacs_shift);
2981         key_add(m, "K:CX->", &emacs_shift);
2982         key_add(m, "K-<", &emacs_shift_again);
2983         key_add(m, "K->", &emacs_shift_again);
2984
2985         key_add(m, "K:CX-{", &emacs_growx);
2986         key_add(m, "K:CX-}", &emacs_growx);
2987         key_add(m, "K-{", &emacs_growx_again);
2988         key_add(m, "K-}", &emacs_growx_again);
2989
2990         key_add(m, "K:CX:C-=", &emacs_scale_relative);
2991         key_add(m, "K:CX:C--", &emacs_scale_relative);
2992
2993         key_add(m, "K:C-S", &emacs_start_search);
2994         key_add(m, "K:C-R", &emacs_start_search);
2995         key_add(m, "K:A-%", &emacs_start_search);
2996         key_add(m, "render:reposition", &emacs_reposition);
2997
2998         key_add(m, "K:CX:C-C", &emacs_exit);
2999         key_add(m, "emacs:deactivate", &emacs_deactivate);
3000
3001         key_add(m, "K:C-U", &emacs_prefix);
3002
3003         key_add(m, "K:A-!", &emacs_shell);
3004         key_add(m, "K:A-|", &emacs_shell);
3005         key_add(m, "Shell Command", &emacs_shell);
3006
3007         key_add(m, "K:CX-`", &emacs_next_match);
3008         key_add(m, "K-`", &emacs_match_again);
3009
3010         key_add(m, "K:CX-1", &emacs_close_others);
3011         key_add(m, "K-1", &emacs_close_others);
3012
3013         key_add_range(m, "K:A-0", "K:A-9", &emacs_num);
3014         key_add(m, "K:A--", &emacs_neg);
3015         key_add(m, "K:C--", &emacs_neg);
3016         key_add(m, "K:C- ", &emacs_mark);
3017         key_add(m, "mode-set-mark", &emacs_mark);
3018         key_add(m, "mode-swap-mark", &emacs_swap_mark);
3019         key_add(m, "K:C-W", &emacs_wipe);
3020         key_add(m, "K:A-w", &emacs_copy);
3021         key_add(m, "K:C-Y", &emacs_yank);
3022         key_add(m, "K:A-y", &emacs_yank_pop);
3023
3024         key_add(m, "K:A-g", &emacs_goto_line);
3025         key_add(m, "K:A-x", &emacs_command);
3026         key_add(m, "K:A-X", &emacs_command);
3027         key_add(m, "K:CC-m", &emacs_make);
3028         key_add(m, "K:CC:C-M", &emacs_make);
3029
3030         key_add(m, "K:A:C-V", &emacs_move_view_other);
3031
3032         key_add(m, "K:CX:C-Q", &emacs_readonly);
3033
3034         key_add_prefix(m, "K:CQ-", &emacs_quote_insert);
3035         key_add_prefix(m, "K:CQ:C-", &emacs_quote_insert);
3036
3037         key_add(m, "K:A-q", &emacs_fill);
3038         key_add(m, "K:A:C-Q", &emacs_fill);
3039         key_add(m, "K:A-/", &emacs_abbrev);
3040         key_add(m, "K:A-;", &emacs_spell);
3041         key_add(m, "emacs:respell", &emacs_spell);
3042         key_add_prefix(m, "K:Spell-", &emacs_spell_choose);
3043         key_add(m, "K:Spell-a", &emacs_spell_accept);
3044         key_add(m, "K:Spell-i", &emacs_spell_insert);
3045         key_add(m, "K:Spell- ", &emacs_spell_skip);
3046         key_add(m, "K:Spell:Enter", &emacs_spell_abort);
3047         key_add(m, "K:Spell:ESC", &emacs_spell_abort);
3048         key_add(m, "K:Spell:Left", &emacs_spell_left);
3049         key_add(m, "K:Spell:C-B", &emacs_spell_left);
3050         key_add(m, "K:Spell:Right", &emacs_spell_right);
3051         key_add(m, "K:Spell:C-F", &emacs_spell_right);
3052
3053         key_add(m, "K:Help-l", &emacs_showinput);
3054
3055         key_add(m, "emacs:command", &emacs_do_command);
3056         key_add(m, "interactive-cmd-version", &emacs_version);
3057         key_add(m, "interactive-cmd-log", &emacs_log);
3058
3059         key_add(m, "M:Click-2", &emacs_paste);
3060         key_add(m, "M:C:Click-1", &emacs_paste);
3061
3062         key_add(m, "K:CX-(", &emacs_macro_start);
3063         key_add(m, "K:CX-)", &emacs_macro_stop);
3064         key_add(m, "K:CX-e", &emacs_macro_run);
3065         key_add(m, "K-e", &emacs_macro_run);
3066
3067         key_add(m, "menu:refresh", &emacs_menu_refresh);
3068         key_add_prefix(m, "emacs:doc-menu:", &emacs_doc_menu);
3069
3070         key_add(m, "menubar:ready", &emacs_menubar_configure);
3071
3072         emacs_map = m;
3073 }
3074
3075 DEF_CMD(attach_mode_emacs)
3076 {
3077         struct pane *p = pane_register(ci->focus, 0, &mode_emacs.c, NULL);
3078
3079         if (!p)
3080                 return Efail;
3081         comm_call(ci->comm2, "cb", p);
3082         return 1;
3083 }
3084
3085 DEF_CMD(attach_file_entry)
3086 {
3087         /* The 'type' passed must be static, not allocated */
3088         char *type = "shellcmd";
3089
3090         if (ci->str && strcmp(ci->str, "file") == 0)
3091                 type = "file";
3092         else if (ci->str && strcmp(ci->str, "doc") == 0)
3093                 type = "doc";
3094         pane_register(ci->focus, 0, &find_handle.c, type);
3095
3096         return 1;
3097 }
3098
3099 void edlib_init(struct pane *ed safe)
3100 {
3101         emacs_init();
3102         findmap_init();
3103         call_comm("global-set-command", ed, &attach_mode_emacs, 0, NULL, "attach-mode-emacs");
3104         call_comm("global-set-command", ed, &attach_file_entry, 0, NULL, "attach-file-entry");
3105         call_comm("global-set-command", ed, &emacs_shell, 0, NULL, "attach-shell-prompt");
3106         call_comm("global-set-command", ed, &emacs_selection_menu_add,
3107                   0, NULL, "selection-menu:add-00-emacs");
3108 }