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