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