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