2 * Copyright Neil Brown ©2015-2023 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
5 * Define some keystrokes to create an editor with an
8 * We register an 'emacs' mode and associate keys with that
9 * in the global keymap.
11 #define _GNU_SOURCE /* for asprintf */
22 #define PANE_DATA_PTR_TYPE char *
24 #include "core-pane.h"
26 static struct map *emacs_map;
27 static const char * safe file_normalize(struct pane *p safe, const char *path,
28 const char *initial_path);
30 /* num2 is used to track if successive commands are related.
31 * Only low 16 bits identify command, other bits are free.
35 N2_undo_insert, /* adjacent commands form a single undo set */
36 N2_undo_delete, /* adjacent commands form a single undo set */
37 N2_undo_change, /* adjacent commands form a single undo set */
38 N2_recentre, /* repeated recentre goes to different places */
39 N2_yank, /* repeated yank-pop takes different result */
40 N2_match, /* repeated ` after Cx` repeats the search */
41 N2_undo, /* Last command was 'undo' too */
42 N2_close_others,/* Last command was close-other, just a 1 can repeat */
43 N2_runmacro, /* Last command was CX-e, just an 'e' can repeat */
44 N2_shift, /* Last command was CX-< or CX-> */
45 N2_growx, /* Last command was CX-{ or CX-} */
46 N2_uniquote, /* Last command was :C-q inserting a unicode from name */
48 static inline int N2(const struct cmd_info *ci safe)
50 return ci->num2 & 0xffff;
53 static inline int N2a(const struct cmd_info *ci safe)
55 return ci->num2 >> 16;
58 REDEF_CMD(emacs_move);
59 REDEF_CMD(emacs_delete);
60 REDEF_CMD(emacs_kill);
61 REDEF_CMD(emacs_case);
62 REDEF_CMD(emacs_swap);
64 /* emacs:active encodes 4 different states for the selection
65 * 0 - inactive. The other-end might exist but it is passive, not displayed
66 * and not used unless explicitly asked for (Cx Cx)
67 * 1 - active. Selection is active and will remain active if cursor
68 * moves or text is changed. Is used in various ways.
69 * 2 - transient. Is active, but will be canceled on movement or change.
70 * Will be used for copy/cut, but little else
71 * 3 - replacable Transient plus text entry will delete selected content.
74 static void set_selection(struct pane *p safe, struct mark *pt,
75 struct mark *mk, int type)
80 active = attr_find_int(mk->attrs, "emacs:active");
83 attr_set_int(&mk->attrs, "emacs:active", type);
85 pt = call_ret(mark, "doc:point", p);
89 attr_set_int(&pt->attrs, "selection:active", 1);
90 if (!mark_same(pt, mk))
91 call("view:changed", p, 0, pt, NULL, 0, mk);
94 static bool clear_selection(struct pane *p safe, struct mark *pt,
95 struct mark *mk, int type)
100 active = attr_find_int(mk->attrs, "emacs:active");
103 if (type && active < type)
105 attr_set_int(&mk->attrs, "emacs:active", 0);
107 pt = call_ret(mark, "doc:point", p);
110 attr_set_int(&pt->attrs, "selection:active", 0);
111 if (!mark_same(pt, mk))
112 call("view:changed", p, 0, pt, NULL, 0, mk);
116 static struct move_command {
119 int direction, extra;
120 char *k1 safe, *k2, *k3;
121 } move_commands[] = {
122 {CMD(emacs_move), "Move-Char", 1, 0,
123 "K:C-F", "K:Right", NULL},
124 {CMD(emacs_move), "Move-Char", -1, 0,
125 "K:C-B", "K:Left", NULL},
126 {CMD(emacs_move), "doc:word", 1, 0,
127 "K:A-f", "K:A:Right", NULL},
128 {CMD(emacs_move), "doc:word", -1, 0,
129 "K:A-b", "K:A:Left", NULL},
130 {CMD(emacs_move), "doc:expr", 1, 0,
131 "K:A:C-F", NULL, NULL},
132 {CMD(emacs_move), "doc:expr", -1, 0,
133 "K:A:C-B", NULL, NULL},
134 {CMD(emacs_move), "doc:expr", -1, 1,
135 "K:A:C-U", NULL, NULL},
136 {CMD(emacs_move), "doc:expr", 1, 1,
137 "K:A:C-D", NULL, NULL},
138 {CMD(emacs_move), "doc:WORD", 1, 0,
139 "K:A-F", NULL, NULL},
140 {CMD(emacs_move), "doc:WORD", -1, 0,
141 "K:A-B", NULL, NULL},
142 {CMD(emacs_move), "doc:EOL", 1, 0,
143 "K:C-E", "K:End", NULL},
144 {CMD(emacs_move), "doc:EOL", -1, 0,
145 "K:C-A", "K:Home", NULL},
146 {CMD(emacs_move), "Move-Line", -1, 0,
147 "K:C-P", "K:Up", NULL},
148 {CMD(emacs_move), "Move-Line", 1, 0,
149 "K:C-N", "K:Down", NULL},
150 {CMD(emacs_move), "doc:file", 1, 0,
151 "K:A->", "K:S:End", NULL},
152 {CMD(emacs_move), "doc:file", -1, 0,
153 "K:A-<", "K:S:Home", NULL},
154 {CMD(emacs_move), "Move-View", 900, 0,
155 "K:Next", "K:C-V", "emacs-move-large-other"},
156 {CMD(emacs_move), "Move-View", -900, 0,
157 "K:Prior", "K:A-v", NULL},
159 {CMD(emacs_move), "doc:paragraph", -1, 0,
160 "K:A:C-A", NULL, NULL},
161 {CMD(emacs_move), "doc:paragraph", 1, 0,
162 "K:A:C-E", NULL, NULL},
164 {CMD(emacs_delete), "Move-Char", 1, 0,
165 "K:C-D", "K:Del", "del"},
166 {CMD(emacs_delete), "Move-Char", -1, 0,
167 "K:C-H", "K:Backspace", "K:Delete"},
168 {CMD(emacs_delete), "doc:word", 1, 0,
169 "K:A-d", NULL, NULL},
170 {CMD(emacs_delete), "doc:word", -1, 0,
171 "K:A:C-H", "K:A:Backspace", NULL},
172 {CMD(emacs_kill), "doc:EOL", 1, 0,
173 "K:C-K", NULL, NULL},
174 {CMD(emacs_kill), "doc:expr", 1, 0,
175 "K:A:C-K", NULL, NULL},
177 {CMD(emacs_case), "Ldoc:word", 1, 0,
178 "K:A-l", "K:A-L", NULL},
179 {CMD(emacs_case), "Udoc:word", 1, 0,
180 "K:A-u", "K:A-U", NULL},
181 {CMD(emacs_case), "Cdoc:word", 1, 0,
182 "K:A-c", "K:A-C", NULL},
183 {CMD(emacs_case), "TMove-Char", 1, 0,
184 "K:A-`", NULL, NULL},
186 {CMD(emacs_swap), "Move-Char", 1, 0,
187 "K:C-T", NULL, NULL},
188 {CMD(emacs_swap), "doc:word", 1, 0,
189 "K:A-t", NULL, NULL},
190 {CMD(emacs_swap), "doc:WORD", 1, 0,
191 "K:A-T", NULL, NULL},
194 REDEF_CMD(emacs_move)
196 struct move_command *mv = container_of(ci->comm, struct move_command, cmd);
197 struct pane *cursor_pane = ci->focus;
204 /* if doc:file, leave inactive mark behind */
205 if (strcmp(mv->type, "doc:file") == 0) {
206 mk = call_ret(mark2, "doc:point", ci->focus);
208 /* Don't change emacs:active */
209 mark_to_mark(mk, ci->mark);
211 call("Move-to", ci->focus, 1, ci->mark);
212 mk = call_ret(mark2, "doc:point", ci->focus);
216 ret = call(mv->type, ci->focus, mv->direction * RPT_NUM(ci), ci->mark,
221 if (strcmp(mv->type, "Move-View") == 0)
222 attr_set_int(&cursor_pane->attrs, "emacs-repoint",
225 mk = call_ret(mark2, "doc:point", ci->focus);
226 /* Discard a transient selection */
227 clear_selection(ci->focus, NULL, mk, 2);
232 REDEF_CMD(emacs_delete)
234 struct move_command *mv = container_of(ci->comm, struct move_command, cmd);
241 mk = call_ret(mark2, "doc:point", ci->focus);
242 /* If selection is replacable, clear it and use mk */
243 if (!clear_selection(ci->focus, NULL, mk, 3)) {
244 /* else clear any transient selection */
245 clear_selection(ci->focus, NULL, mk, 2);
249 m = mark_dup(ci->mark);
251 ret = call(mv->type, ci->focus, mv->direction * RPT_NUM(ci), m);
258 call("Replace", ci->focus, 1, mk, NULL, 0, mk);
260 ret = call("Replace", ci->focus, 1, m, NULL, N2(ci) == N2_undo_delete);
262 call("Mode:set-num2", ci->focus, N2_undo_delete);
267 REDEF_CMD(emacs_kill)
269 /* Like delete, but copy to copy-buffer */
270 struct move_command *mv = container_of(ci->comm, struct move_command, cmd);
278 m = mark_dup(ci->mark);
280 if (strcmp(mv->type, "doc:EOL") == 0 &&
281 mv->direction == 1 && RPT_NUM(ci) == 1 &&
282 is_eol(doc_following(ci->focus, m)))
283 ret = call("Move-Char", ci->focus, mv->direction * RPT_NUM(ci), m);
285 ret = call(mv->type, ci->focus, mv->direction * RPT_NUM(ci), m);
291 /* Remove any selection so it cannot be claimed and so replace this copy */
292 call("selection:claim", ci->focus);
293 call("selection:discard", ci->focus);
295 str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, m);
297 call("copy:save", ci->focus, N2(ci) == N2_undo_delete, NULL, str);
298 ret = call("Replace", ci->focus, 1, m, NULL, N2(ci) == N2_undo_delete);
300 call("Mode:set-num2", ci->focus, N2_undo_delete);
305 REDEF_CMD(emacs_case)
307 struct move_command *mv = container_of(ci->comm, struct move_command, cmd);
309 struct mark *start = NULL;
310 int cnt = mv->direction * RPT_NUM(ci);
323 start = mark_dup(ci->mark);
327 struct mark *m = mark_dup(ci->mark);
329 ret = call(mv->type+1, ci->focus, dir, ci->mark);
330 if (ret <= 0 || mark_same(ci->mark, m))
331 /* Hit end of file */
334 char *str = call_ret(str, "doc:get-str", ci->focus,
341 for (c = str; c && *c; c += 1) {
342 char type = mv->type[0];
344 type = found ? 'L' : 'U';
349 default: /* silence compiler */
350 case 'U': /* Uppercase */
356 case 'L': /* Lowercase */
362 case 'T': /* Toggle */
366 } else if (islower(*c)) {
374 ret = call("Replace", ci->focus, 1, m, str,
375 N2(ci) == N2_undo_change);
377 call(mv->type+1, ci->focus, dir, ci->mark);
380 if (changed || N2(ci) == N2_undo_change)
381 call("Mode:set-num2", ci->focus, N2_undo_change);
386 /* When moving forward, move point. When backward, leave point alone */
388 mark_to_mark(ci->mark, start);
394 REDEF_CMD(emacs_swap)
396 /* collect the object behind point and insert it after the object
397 * after point. With a +ve repeat count, insert after n objects.
398 * With -ve repeast, collect object after and insert behind n
399 * previous objects. Object is determined by mv->type.
401 struct move_command *mv = container_of(ci->comm, struct move_command, cmd);
403 struct mark *start = NULL;
404 int cnt = mv->direction * RPT_NUM(ci);
417 start = mark_dup(ci->mark);
421 struct mark *as, *ae, *bs, *be;
424 call(mv->type, ci->focus, -dir, ci->mark);
425 as = mark_dup(ci->mark);
426 call(mv->type, ci->focus, dir, ci->mark);
427 ae = mark_dup(ci->mark);
428 /* as to ae is the object to be moved. */
430 call(mv->type, ci->focus, dir, ci->mark);
431 be = mark_dup(ci->mark);
432 call(mv->type, ci->focus, -dir, ci->mark);
433 bs = mark_dup(ci->mark);
434 /* bs to be is the object to be swapped with.
435 * bs must be after ae in the direction
437 if (mark_same(as, ae) ||
439 bs->seq - ae->seq * dir < 0) {
440 mark_to_mark(ci->mark, ae);
447 astr = call_ret(str, "doc:get-str", ci->focus,
450 bstr = call_ret(str, "doc:get-str", ci->focus,
453 mark_to_mark(ci->mark, ae);
454 call("Replace", ci->focus, 1, as, bstr);
455 mark_to_mark(ci->mark, be);
456 call("Replace", ci->focus, 1, bs, astr, 1);
458 call(mv->type, ci->focus, dir, ci->mark);
467 /* When moving forward, move point. When backward, leave point alone */
469 mark_to_mark(ci->mark, start);
475 DEF_CMD(emacs_move_view_other)
477 /* If there is an 'other' pane', Send "K:Next" there */
480 /* '512' means 'fail if no other pane' */
481 p = call_ret(pane, "OtherPane", ci->focus, 512);
484 call("Mode:set-num", p, ci->num);
485 call("Keystroke", p, 0, NULL, "emacs-move-large-other");
489 DEF_CMD(emacs_recenter)
492 if (ci->num == NO_NUMERIC && N2(ci) == N2_recentre) {
493 /* Repeated command - go to top, or bottom, or middle in order */
496 case 0: /* was center, go to top */
497 call("Move-View-Line", ci->focus, 1, ci->mark);
500 case 1: /* was top, go to bottom */
501 call("Move-View-Line", ci->focus, -1, ci->mark);
504 case 2: /* was bottom, go to middle */
505 call("Move-View-Line", ci->focus, 0, ci->mark);
509 } else if (ci->num == -NO_NUMERIC) {
510 /* Move point to bottom */
511 call("Move-View-Line", ci->focus, -1, ci->mark);
513 } else if (ci->num != NO_NUMERIC) {
514 /* Move point to display line N */
515 call("Move-View-Line", ci->focus, ci->num, ci->mark);
517 /* Move point to middle and refresh */
518 call("Move-View-Line", ci->focus, 0, ci->mark);
519 call("window:refresh", ci->focus);
521 call("Mode:set-num2", ci->focus, N2_recentre | (step << 16));
525 REDEF_CMD(emacs_simple);
526 REDEF_CMD(emacs_simple_num);
527 REDEF_CMD(emacs_simple_str);
528 static struct simple_command {
532 } simple_commands[] = {
533 {CMD(emacs_simple), "Window:next", "K:CX-o"},
534 {CMD(emacs_simple), "Window:prev", "K:CX-O"},
535 {CMD(emacs_simple), "Window:y+", "K:CX-^"},
536 {CMD(emacs_simple), "Window:split-y", "K:CX-2"},
537 {CMD(emacs_simple), "Window:split-x", "K:CX-3"},
538 {CMD(emacs_simple), "Window:close", "K:CX-0"},
539 {CMD(emacs_simple), "Window:bury", "K:A-B"},
540 {CMD(emacs_simple), "window:new", "K:CX5-2"},
541 {CMD(emacs_simple), "window:close", "K:CX5-0"},
542 {CMD(emacs_simple), "lib-server:done", "K:CX-#"},
543 {CMD(emacs_simple), "mode-swap-mark", "K:CX:C-X"},
544 {CMD(emacs_simple), "Abort", "K:C-G"},
545 {CMD(emacs_simple), "Cancel", "K:ESC"},
546 {CMD(emacs_simple), "NOP", "K:A-G"},
547 {CMD(emacs_simple), "NOP", "K:CX:C-G"},
548 {CMD(emacs_simple), "NOP", "K:CX4:C-G"},
549 {CMD(emacs_simple), "doc:save-file", "K:CX:C-S"},
550 {CMD(emacs_simple), "Commit", "K:CC:C-C"},
551 /* one day, this will be "find definition", now it is same as "find any" */
552 {CMD(emacs_simple_num), "interactive-cmd-git-grep", "K:CX:A-."},
553 {CMD(emacs_simple_str), "interactive-cmd-git-grep", "K:A-."},
554 {CMD(emacs_simple), "interactive-cmd-merge-mode", "K:A-m"},
555 {CMD(emacs_simple_str), "interactive-cmd-calc-replace", "K:A-#"},
558 REDEF_CMD(emacs_simple)
560 struct simple_command *sc = container_of(ci->comm, struct simple_command, cmd);
565 return call(sc->type, ci->focus, ci->num, ci->mark);
568 REDEF_CMD(emacs_simple_num)
570 struct simple_command *sc = container_of(ci->comm, struct simple_command, cmd);
575 return call(sc->type, ci->focus, RPT_NUM(ci), ci->mark);
578 REDEF_CMD(emacs_simple_str)
580 struct simple_command *sc = container_of(ci->comm, struct simple_command, cmd);
581 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
586 if (clear_selection(ci->focus, NULL, mk, 0))
587 str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, mk);
589 return call(sc->type, ci->focus, ci->num, ci->mark, str, 0, mk);
592 REDEF_CMD(emacs_insert);
594 DEF_CMD(emacs_close_others)
596 if (strcmp(ci->key, "K-1") == 0 && N2(ci) != N2_close_others)
597 return emacs_insert_func(ci);
599 if (call("Window:close-others", ci->focus) <= 0)
601 call("Mode:set-num2", ci->focus, N2_close_others);
602 call("Message:modal", ci->focus, 0, NULL, "Type 1 to close more");
608 struct call_return *cr = container_of(ci->comm, struct call_return, c);
614 DEF_CMD(emacs_deactivate)
616 /* close-all popup has closed, see if it aborted */
618 call("event:deactivate", ci->focus);
624 struct call_return cr;
628 if (ci->num == NO_NUMERIC) {
631 /* If this is not only display, then refuse to exit */
632 call_comm("editor:notify:all-displays", ci->focus, &cr.c);
634 call("Message", ci->focus, 0, NULL,
635 "Cannot exit when there are multiple windows.");
639 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM");
640 // FIXME if called from a popup, this fails.
643 attr_set_str(&p->attrs, "done-key", "emacs:deactivate");
644 return call("docs:show-modified", p);
646 call("event:deactivate", ci->focus);
650 DEF_CMD(emacs_insert)
654 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
656 bool first = N2(ci) != N2_undo_insert;
661 if (clear_selection(ci->focus, NULL, mk, 3)) {
662 call("Replace", ci->focus, 1, mk, NULL, !first);
665 clear_selection(ci->focus, NULL, mk, 2);
667 str = ksuffix(ci, "K-");
668 /* Resubmit as doc:char-$str. By default this will be inserted
669 * but panes like lib-viewer might have other plans.
670 * lib-viewer could catch the original "K-", but sometimes
671 * the major mode might not want that.
673 strcat(strcpy(dc, "doc:char-"), str);
674 ret = call(dc, ci->focus, ci->num, ci->mark, NULL, !first);
675 call("Mode:set-num2", ci->focus, N2_undo_insert);
680 DEF_CMD(emacs_quote_insert)
685 bool first = N2(ci) != N2_undo_insert;
690 if (clear_selection(ci->focus, NULL, ci->mark, 3)) {
691 call("Replace", ci->focus, 1, ci->mark, NULL, !first);
694 clear_selection(ci->focus, NULL, ci->mark, 2);
696 str = ksuffix(ci, "K:CQ-");
698 str = ksuffix(ci, "K:CQ:C-");
700 buf[0] = str[0] & 0x1f;
705 ret = call("Replace", ci->focus, 1, ci->mark, str, !first);
706 call("Mode:set-num2", ci->focus, N2_undo_insert);
714 } other_inserts[] = {
722 DEF_CMD(emacs_insert_other)
726 struct mark *m = NULL;
727 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
728 bool first = N2(ci) != N2_undo_insert;
734 for (i = 0; other_inserts[i].key; i++)
735 if (strcmp(safe_cast other_inserts[i].key, ci->key) == 0)
737 ins = other_inserts[i].insert;
741 if (clear_selection(ci->focus, NULL, mk, 3)) {
742 call("Replace", ci->focus, 1, mk, NULL, !first);
745 clear_selection(ci->focus, NULL, mk, 2);
749 m = mark_dup(ci->mark);
750 /* Move m before ci->mark, so it doesn't move when we insert */
754 ret = call("Replace", ci->focus, 1, m, ins, !first, ci->mark);
756 mark_to_mark(ci->mark, m);
759 /* A newline starts a new undo */
760 call("Mode:set-num2", ci->focus, (*ins == '\n') ? 0 : N2_undo_insert);
764 DEF_CMD(emacs_interactive_insert)
766 /* If some pane want to insert text just like it was typed,
767 * it calls this, and we set up for proper undo
770 bool first = N2(ci) != N2_undo_insert;
775 if (clear_selection(ci->focus, NULL, ci->mark, 3)) {
776 call("Replace", ci->focus, 1, ci->mark, NULL, !first);
779 clear_selection(ci->focus, NULL, ci->mark, 2);
780 ret = call("Replace", ci->focus, 1, ci->mark, ci->str,
782 call("Mode:set-num2", ci->focus,
783 strchr(ci->str, '\n') ? 0 : N2_undo_insert);
787 DEF_CMD(emacs_interactive_delete)
789 /* If some pane want to delete text just like backspace was typed,
790 * it calls this, and we set up for proper undo
796 ret = call("Replace", ci->focus, 1, ci->mark, "",
797 N2(ci) == N2_undo_insert, ci->mark2);
798 call("Mode:set-num2", ci->focus,
799 strchr(ci->str, '\n') ? 0 : N2_undo_delete);
807 ret = call("doc:reundo", ci->focus, N2(ci) == N2_undo);
808 call("Mode:set-num2", ci->focus, N2_undo);
810 call("Message", ci->focus, 0, NULL,
811 "No further undo information");
816 REDEF_CMD(emacs_file_complete);
817 REDEF_CMD(emacs_doc_complete);
818 REDEF_CMD(emacs_cmd_complete);
820 DEF_CMD(find_complete)
822 char *type = ci->home->data;
824 if (strstarts(type, "file"))
825 return emacs_file_complete_func(ci);
826 if (strcmp(type, "shellcmd") == 0)
827 return emacs_file_complete_func(ci);
828 if (strcmp(type, "doc") == 0)
829 return emacs_doc_complete_func(ci);
830 if (strcmp(type, "cmd") == 0)
831 return emacs_cmd_complete_func(ci);
838 char *type = ci->home->data;
839 char *str = call_ret(strsave, "doc:get-str", ci->focus);
840 const char *norm = NULL;
844 /* close with no string */
845 ret = call("popup:close", ci->focus);
846 if (strcmp(type, "doc") == 0 &&
847 call_ret(pane, "docs:byname", ci->focus, 0, NULL, str) == NULL) {
848 call("Message:modal", ci->focus, 0, NULL, "Document not found");
851 if (strstarts(type, "file")) {
853 bool can_create = True;
854 bool can_create_dir = True;
856 norm = file_normalize(ci->focus, str,
857 attr_find(ci->home->attrs,
859 sl = strrchr(norm, '/');
860 while (sl && sl > norm) {
861 /* Need to check directories exist. */
865 if ((stb.st_mode & S_IFMT) == S_IFDIR)
868 can_create_dir = False;
870 sl = strrchr(norm, '/');
874 if (!can_create_dir) {
875 call("Message:modal", ci->focus, 0, NULL,
876 strconcat(ci->focus, norm, " is not a directory!!"));
880 /* Need to create directory first */
881 if (strcmp(ci->key, "K:A:Enter") == 0) {
882 char *m = "Cannot create directory: ";
883 if (mkdir(norm, 0777) == 0) {
884 m = "Created directory: ";
885 attr_set_str(&ci->home->attrs,
887 /* Trigger recalc on replace */
888 call("doc:replace", ci->focus, 0, NULL, "");
890 call("Message:modal", ci->focus, 0, NULL,
891 strconcat(ci->focus, m, norm));
894 call("Message:modal", ci->focus, 0, NULL,
896 "Use Alt-Enter to create directory ", norm));
900 if (strcmp(type, "file") == 0 && norm &&
901 strcmp(ci->key, "K:Enter") == 0 &&
902 stat(norm, &stb) != 0) {
903 call("Message:modal", ci->focus, 0, NULL,
904 "File not found - use Alt-Enter to create");
907 if (strcmp(type, "file write") == 0 && norm &&
908 strcmp(ci->key, "K:Enter") == 0 &&
909 stat(norm, &stb) == 0) {
910 call("Message:modal", ci->focus, 0, NULL,
911 "File exists - use Alt-Enter to overwrite");
914 ret = call("popup:close", ci->focus, 0, NULL, norm ?: str);
927 struct find_helper *h = container_of(ci->comm, struct find_helper, c);
928 struct pane *p = ci->focus;
935 /* want the pane before nothing, so the last.
936 * So return this one and keep looking.
941 /* Want the pane that is after nothing, so
942 * the first. This one. All done.
948 name = pane_attr_get(ci->focus, "doc-name");
951 if (strcmp(name, h->name) == 0) {
953 /* Want the previous one, which is
958 /* Want the next one, so clear name
966 /* This might be what I want - keep it in case */
970 /* Don't want this - just keep going */
976 DEF_CMD(find_prevnext)
978 /* Find the previous document lru or, which is "next" as we
979 * walk the list in mru order.
980 * When we find it, insert the name into ci->focus document
982 char *type = ci->home->data;
983 struct find_helper h;
985 if (strcmp(type, "doc") != 0)
987 h.name = attr_find(ci->home->attrs, "find-doc");
990 h.want_prev = strcmp(ci->key, "K:A-n") == 0;
992 call_comm("docs:byeach", ci->focus, &h.c);
994 char *name = pane_attr_get(h.ret, "doc-name");
997 attr_set_str(&ci->home->attrs, "find-doc", name);
998 m = mark_new(ci->focus);
999 m2 = m ? mark_dup(m) : NULL;
1000 call("doc:file", ci->focus, -1, m);
1001 call("doc:file", ci->focus, 1, m2);
1002 call("Replace", ci->focus, 1, m, name, 0, m2);
1011 char *type = ci->home->data;
1016 if (strcmp(type, "file") != 0)
1017 return Efallthrough;
1019 if (strcmp(ci->str, "start-of-line") == 0) {
1020 char *lens = attr_find(ci->home->attrs, "path_lengths");
1021 int dir_start = 0, dir_end = 0, nondir_end = 0,
1024 sscanf(lens, "%d %d %d %d", &dir_start, &dir_end,
1025 &nondir_end, &basename_start);
1028 comm_call(ci->comm2, "cb", ci->focus, dir_start,
1029 ci->mark, "fg:grey+20,nobold,noinverse", 115);
1030 if (dir_end > dir_start)
1031 comm_call(ci->comm2, "cb", ci->focus, dir_end,
1032 ci->mark, "fg:black,nobold,noinverse", 114);
1033 if (nondir_end > dir_end)
1034 comm_call(ci->comm2, "cb", ci->focus, nondir_end,
1035 ci->mark, "fg:red-80,bold,inverse", 113);
1036 if (basename_start > nondir_end)
1037 comm_call(ci->comm2, "cb", ci->focus, basename_start,
1038 ci->mark, "fg:magenta", 112);
1039 comm_call(ci->comm2, "cb", ci->focus, 10000, ci->mark,
1045 DEF_CMD(find_check_replace)
1047 char *str, *cp, *sl;
1048 char *type = ci->home->data;
1052 int ipl; // Initial Path Len
1053 int dir_start, dir_end, nondir_end, basename_start;
1054 char nbuf[4 * (10+1) + 1], *lens;
1056 if (strcmp(type, "file") != 0)
1057 return Efallthrough;
1059 home_call(ci->home->parent, ci->key, ci->focus,
1060 ci->num, ci->mark, ci->str,
1061 ci->num2, ci->mark2, ci->str2);
1063 /* The doc content can have 5 different sections that might
1064 * be different colours.
1065 * - ignored prefix: grey - This inital_path followed by something
1066 * that looks like another path. "/" or "~/"
1067 * - True directories: black - directory part of the path that
1068 * exists and is a directory
1069 * - non-directory-in-path: red - directory part that exists but
1070 * is not a directory. At most one component.
1071 * - non-existant name: magenta - directory path that doesn't exist.
1072 * - basename: black, whether it exists or not.
1073 * These are found as:
1078 * These are all lengths from start of path. They are all stored
1079 * in a single attribute: "path_lengths".
1082 str = call_ret(str, "doc:get-str", ci->focus);
1085 sl = strrchr(str, '/');
1089 prev_dir = attr_find(ci->home->attrs, "prev_dir");
1090 if (prev_dir && strlen(prev_dir) == (size_t)(sl - str + 1) &&
1091 strncmp(prev_dir, str, sl - str + 1) == 0)
1092 /* No change before last '/' */
1095 initial_path = attr_find(ci->home->attrs, "initial_path");
1099 ipl = strlen(initial_path);
1101 if (strncmp(str, initial_path, ipl) == 0 &&
1102 (str[ipl] == '/' || (str[ipl] == '~' &&
1103 (str[ipl+1] == '/' ||
1107 dir_start = utf8_strnlen(str, cp - str);
1109 basename_start = utf8_strnlen(str, sl - str + 1);
1116 path = file_normalize(ci->focus, str, initial_path);
1122 while (sl > cp && *sl != '/')
1125 nondir_end = utf8_strnlen(str, sl - str + 1);
1126 dir_end = nondir_end;
1127 if (stb.st_mode != 0 &&
1128 (stb.st_mode & S_IFMT) != S_IFDIR) {
1129 /* There is a non-dir on the path */
1130 while (sl > cp && sl[-1] != '/')
1132 /* This must actually be a dir */
1133 dir_end = utf8_strnlen(str, sl - str);
1135 snprintf(nbuf, sizeof(nbuf), "%d %d %d %d",
1136 dir_start, dir_end, nondir_end, basename_start);
1137 lens = attr_find(ci->home->attrs, "path_lengths");
1138 if (!lens || strcmp(lens, nbuf) != 0)
1139 attr_set_str(&ci->home->attrs, "path_lengths", nbuf);
1140 sl = strrchr(str, '/');
1143 attr_set_str(&ci->home->attrs, "prev_dir", str);
1148 static struct map *fh_map;
1149 DEF_LOOKUP_CMD(find_handle, fh_map);
1151 static void findmap_init(void)
1155 fh_map = key_alloc();
1156 key_add(fh_map, "K:Tab", &find_complete);
1157 key_add(fh_map, "K:Enter", &find_done);
1158 key_add(fh_map, "K:A:Enter", &find_done);
1159 key_add(fh_map, "K:A-p", &find_prevnext);
1160 key_add(fh_map, "K:A-n", &find_prevnext);
1161 key_add(fh_map, "map-attr", &find_attr);
1162 key_add(fh_map, "doc:replace", &find_check_replace);
1165 static const char * safe file_normalize(struct pane *p safe,
1167 const char *initial_path)
1169 int len = strlen(initial_path?:"");
1174 if (initial_path && strncmp(path, initial_path, len) == 0) {
1175 if (path[len] == '/' || (path[len] == '~' &&
1176 (path[len+1] == '/' || path[len+1] == 0)))
1181 if (path[0] == '~' && (path[1] == '/' || path[1] == 0)) {
1182 char *home = getenv("HOME");
1185 if (home[strlen(home) - 1] == '/' && path[1] == '/')
1187 return strconcat(p, home, path+1);
1189 dir = pane_attr_get(p, "dirname");
1192 if (dir[strlen(dir)-1] == '/')
1193 return strconcat(p, dir, path);
1194 return strconcat(p, dir, "/", path);
1197 DEF_CMD(emacs_findfile)
1200 struct pane *p, *par;
1205 path = pane_attr_get(ci->focus, "dirname");
1214 e = buf + strlen(buf);
1215 if (e < buf + sizeof(buf)-1 && e > buf && e[-1] != '/') {
1220 if (ksuffix(ci, "File Found:")[0] == 0) {
1221 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
1226 if (ksuffix(ci, "K:CX44")[0]) {
1227 attr_set_str(&p->attrs, "prompt",
1229 attr_set_str(&p->attrs, "done-key",
1230 "File Found:Popup");
1231 } else if (ksuffix(ci, "K:CX4")[0]) {
1232 attr_set_str(&p->attrs, "prompt",
1233 "Find File Other Window");
1234 attr_set_str(&p->attrs, "done-key",
1235 "File Found:Other Window");
1237 attr_set_str(&p->attrs, "prompt", "Find File");
1238 attr_set_str(&p->attrs, "done-key",
1239 "File Found:This Window");
1241 call("doc:set-name", p, 0, NULL, "Find File");
1243 p = pane_register(p, 0, &find_handle.c, "file");
1246 attr_set_str(&p->attrs, "initial_path", path);
1247 call("attach-history", p, 0, NULL, "*File History*");
1254 path = file_normalize(ci->focus, ci->str, path);
1259 fd = open(path, O_RDONLY);
1261 p = call_ret(pane, "doc:open", ci->focus, fd, NULL, path);
1264 /* '4' says 'allow create' */
1265 p = call_ret(pane, "doc:open", ci->focus, -1, NULL, path, 4);
1268 asprintf(&m, "Failed to open file: %s", path);
1269 call("Message", ci->focus, 0, NULL, m);
1273 if (strcmp(ci->key, "File Found:Other Window") == 0) {
1274 struct pane *this = call_ret(pane, "ThisPane", ci->focus);
1275 par = home_call_ret(pane, ci->focus, "DocPane", p);
1276 if (par && par != this) {
1277 pane_take_focus(par);
1280 par = call_ret(pane, "OtherPane", ci->focus);
1281 } else if (strcmp(ci->key, "File Found:Popup") == 0) {
1282 par = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "MD3tsa");
1284 par = call_ret(pane, "ThisPane", ci->focus);
1291 p = home_call_ret(pane, p, "doc:attach-view", par, 1);
1295 return p ? 1 : Efail;
1298 DEF_CMD(emacs_writefile)
1300 /* Request to write to a different file */
1306 if (call("doc:write-file", ci->focus, NO_NUMERIC) >= 0) {
1307 /* It should have been an error ! */
1308 const char *doc = pane_attr_get(ci->focus, "doc-name");
1310 call("Message", ci->focus, 0, NULL,
1311 strconcat(ci->focus, "Document ", doc,
1312 " cannot be written"));
1314 call("Message", ci->focus, 0, NULL,
1315 "This document cannot be written");
1318 path = pane_attr_get(ci->focus, "dirname");
1327 e = buf + strlen(buf);
1328 if (e < buf + sizeof(buf)-1 && e > buf && e[-1] != '/') {
1333 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
1337 attr_set_str(&p->attrs, "prompt", "Write File");
1338 attr_set_str(&p->attrs, "done-key",
1339 strconcat(p, "emacs:write_file:", path));
1340 call("doc:set-name", p, 0, NULL, "Write File");
1341 p = pane_register(p, 0, &find_handle.c, "file write");
1344 attr_set_str(&p->attrs, "initial_path", path);
1345 call("attach-history", p, 0, NULL, "*File History*");
1349 DEF_CMD(emacs_do_writefile)
1351 const char *path = ksuffix(ci, "emacs:write_file:");
1352 if (!ci->str || !path)
1355 path = file_normalize(ci->focus, ci->str, path);
1358 if (call("doc:write-file", ci->focus, NO_NUMERIC, NULL, path) <= 0) {
1359 call("Message", ci->focus, 0, NULL,
1360 strconcat(ci->focus, "Failed to write to ", path));
1366 DEF_CMD(emacs_insertfile)
1368 /* Request to insert content of a file at point */
1375 ret = call("doc:insert-file", ci->focus, NO_NUMERIC);
1376 if (ret != Enoarg) {
1379 /* Message already given */
1381 doc = pane_attr_get(ci->focus, "doc-name");
1383 call("Message", ci->focus, 0, NULL,
1384 strconcat(ci->focus, "Document ", doc,
1385 " cannot receive insertions"));
1387 call("Message", ci->focus, 0, NULL,
1388 "This document cannot receive insertions");
1391 path = pane_attr_get(ci->focus, "dirname");
1400 e = buf + strlen(buf);
1401 if (e < buf + sizeof(buf)-1 && e > buf && e[-1] != '/') {
1406 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
1410 attr_set_str(&p->attrs, "prompt", "Insert File");
1411 attr_set_str(&p->attrs, "done-key",
1412 strconcat(p, "emacs:insert_file:", path));
1413 call("doc:set-name", p, 0, NULL, "Insert File");
1414 p = pane_register(p, 0, &find_handle.c, "file");
1417 attr_set_str(&p->attrs, "initial_path", path);
1418 call("attach-history", p, 0, NULL, "*File History*");
1422 DEF_CMD(emacs_do_insertfile)
1424 const char *path = ksuffix(ci, "emacs:insert_file:");
1427 if (!ci->str || !path)
1430 path = file_normalize(ci->focus, ci->str, path);
1433 fd = open(path, O_RDONLY);
1435 if (call("doc:insert-file", ci->focus, fd) <= 0) {
1437 call("Message", ci->focus, 0, NULL,
1438 strconcat(ci->focus, "Failed to insert ", path));
1444 char *m = strconcat(ci->focus,
1445 "Failed to inser file: ", path);
1446 call("Message", ci->focus, 0, NULL, m);
1451 REDEF_CMD(emacs_file_complete)
1453 /* Extract a directory name and a basename from the document.
1454 * Find a document for the directory and attach as a completing
1460 struct pane *par, *pop, *docp, *p;
1461 struct call_return cr;
1462 char *type = ci->home->data;
1463 char *initial = attr_find(ci->home->attrs, "initial_path");
1464 int wholebuf = strcmp(type, "file") == 0;
1470 st = mark_dup(ci->mark);
1471 call("doc:file", ci->focus, -1, st);
1473 str = call_ret(strsave, "doc:get-str", ci->focus, 0, st, NULL,
1480 /* Need guess which part of the buf is the file name.
1481 * This probably needs to be configurable, but lets
1482 * decide the file name starts immediately after a
1483 * space, or a '=' or ':' which is followed by a
1487 d = str + strlen(str);
1490 (strchr(":=", d[-1]) && d[0] == '/')))
1493 d = file_normalize(ci->focus, d, initial);
1494 b = strrchr(d, '/');
1497 d = strnsave(ci->focus, d, b-d);
1500 d = strsave(ci->focus, ".");
1502 fd = open(d, O_DIRECTORY|O_RDONLY);
1504 call("Message:modal", ci->focus, 0, NULL,
1505 strconcat(ci->focus, "Cannot open directory \"", d, "\""));
1508 /* 32 means quiet */
1509 docp = call_ret(pane, "doc:open", ci->focus, fd, NULL, d, 32);
1513 pop = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM1r");
1516 par = home_call_ret(pane, docp, "doc:attach-view", pop,
1521 attr_set_str(&par->attrs, "line-format", "%name%suffix");
1522 attr_set_str(&par->attrs, "heading", "");
1523 attr_set_str(&par->attrs, "done-key", "Replace");
1524 p = call_ret(pane, "attach-render-complete", par, 0, NULL, "format");
1527 cr = call_ret(all, "Complete:prefix", p, 1, NULL, b,
1528 0, NULL, "format:plain");
1529 if (cr.s && (strlen(cr.s) <= strlen(b) && cr.ret-1 > 1)) {
1530 /* We need the dropdown - delete prefix and drop-down will
1535 start = mark_dup(ci->mark);
1536 call("doc:char", ci->focus, -strlen(b), start);
1537 call("Replace", ci->focus, 1, start);
1543 /* Replace 'b' with the result. */
1546 start = mark_dup(ci->mark);
1547 call("doc:char", ci->focus, -strlen(b), start);
1548 call("Replace", ci->focus, 1, start, cr.s);
1551 call("Message:modal", ci->focus, 0, NULL,
1552 strconcat(ci->focus, "No completion found for \"", b, "\"",
1553 " in \"", d, "\""));
1555 /* Now need to close the popup */
1560 DEF_CMD(emacs_finddoc)
1562 struct pane *p, *par;
1564 if (ksuffix(ci, "Doc Found:")[0] == 0) {
1566 char *defname = NULL;
1568 dflt = call_ret(pane, "docs:choose", ci->focus);
1570 defname = pane_attr_get(dflt, "doc-name");
1572 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
1578 attr_set_str(&p->attrs, "default", defname);
1579 if (ksuffix(ci, "K:CX44")[0]) {
1580 attr_set_str(&p->attrs, "prompt",
1581 "Find Document Popup");
1582 attr_set_str(&p->attrs, "done-key",
1584 } else if (ksuffix(ci, "K:CX4")[0]) {
1585 attr_set_str(&p->attrs, "prompt",
1586 "Find Document Other Window");
1587 attr_set_str(&p->attrs, "done-key",
1588 "Doc Found:Other Window");
1590 attr_set_str(&p->attrs, "prompt", "Find Document");
1591 attr_set_str(&p->attrs, "done-key",
1592 "Doc Found:This Window");
1594 call("doc:set-name", p, 0, NULL, "Find Document", -1);
1596 pane_register(p, 0, &find_handle.c, "doc");
1604 p = call_ret(pane, "docs:byname", ci->focus, 0, NULL, ci->str);
1608 if (strcmp(ci->key, "Doc Found:Other Window") == 0) {
1609 struct pane *this = call_ret(pane, "ThisPane", ci->focus);
1610 par = home_call_ret(pane, ci->focus, "DocPane", p);
1611 if (par && par != this) {
1612 pane_take_focus(par);
1615 par = call_ret(pane, "OtherPane", ci->focus);
1616 } else if (strcmp(ci->key, "Doc Found:Popup") == 0) {
1617 par = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "MD3tsa");
1619 par = call_ret(pane, "ThisPane", ci->focus);
1623 p = home_call_ret(pane, p, "doc:attach-view", par, 1);
1626 return p ? 1 : Efail;
1629 REDEF_CMD(emacs_doc_complete)
1631 /* Extract a document name from the document.
1632 * Attach the 'docs' document as a completing popup menu
1635 struct pane *pop, *p = NULL;
1636 struct call_return cr;
1641 str = call_ret(strsave, "doc:get-str", ci->focus);
1644 pop = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM1r");
1647 p = call_ret(pane, "docs:complete", pop);
1650 cr = call_ret(all, "Complete:prefix", p, 1, NULL, str);
1651 if (cr.s && (strlen(cr.s) <= strlen(str) && cr.ret - 1 > 1)) {
1652 /* We need the dropdown */
1655 start = mark_dup(ci->mark);
1656 call("doc:set-ref", ci->focus, 1, start);
1658 call("Replace", ci->focus, 1, start);
1663 /* Replace the prefix with the new value */
1666 start = mark_dup(ci->mark);
1667 call("doc:set-ref", ci->focus, 1, start);
1669 call("Replace", ci->focus, 1, start, cr.s);
1672 /* Now need to close the popup */
1677 DEF_CMD(emacs_viewdocs)
1679 struct pane *p, *par;
1682 docs = call_ret(pane, "docs:byname", ci->focus, 0, NULL, "*Documents*");
1686 if (ksuffix(ci, "K:CX44")[0]) {
1687 par = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "MD3tsa");
1688 } else if (ksuffix(ci, "K:CX4")[0]) {
1689 struct pane *this = call_ret(pane, "ThisPane", ci->focus);
1690 par = home_call_ret(pane, ci->focus, "DocPane", docs);
1691 if (par && par != this) {
1692 pane_take_focus(par);
1695 par = call_ret(pane, "OtherPane", ci->focus);
1697 par = call_ret(pane, "ThisPane", ci->focus);
1703 p = home_call_ret(pane, docs, "doc:attach-view", par, 1);
1709 struct pane *doc safe;
1713 DEF_CMD(choose_pane)
1715 struct pcb *cb = container_of(ci->comm, struct pcb, c);
1718 if (cb->p || !ci->str)
1720 d = call_ret(pane, "docs:byname", ci->focus, 0, NULL, ci->str);
1722 cb->p = home_call_ret(pane, ci->focus, "DocPane", d);
1724 home_call(cb->doc, "doc:attach-view", cb->p, 1);
1732 if (strcmp(ci->key, "cb:timer") == 0) {
1733 /* If focus has moved, don't show shell window,
1734 * probably a popup was requested by command.
1736 if (!pane_has_focus(ci->home))
1737 /* call back when lines or eof */
1740 if (strcmp(ci->key, "cb:eof") != 0) {
1742 if (ci->str && strchr(ci->str, 'P'))
1743 par = call_ret(pane, "PopupTile", ci->home, 0, NULL, "MD3tsa");
1745 par = call_ret(pane, "OtherPane", ci->home);
1747 home_call(ci->focus, "doc:attach-view", par, 1);
1750 str = call_ret(str, "doc:get-str", ci->focus);
1751 if (!str || !*str) {
1754 asprintf(&str, "(shell command completed with no output)");
1755 else if (ci->num > 0)
1756 asprintf(&str, "(shell command completed with no output (exit code %d))", ci->num);
1758 asprintf(&str, "(shell command completed with no output (signalled %d))", -ci->num);
1760 call("Message", ci->home, 0, NULL, str);
1765 DEF_CB(shell_insert_cb)
1767 char *str = call_ret(str, "doc:get-str", ci->focus);
1768 struct mark *mk = call_ret(mark2, "doc:point", ci->home);
1770 if (clear_selection(ci->home, NULL, mk, 3))
1771 call("Replace", ci->home, 1, mk);
1772 call("Replace", ci->home, 0, NULL, str);
1777 DEF_CMD(emacs_shell)
1779 char *name = "*Shell Command Output*";
1780 struct pane *p, *doc, *par, *sc;
1781 char *path, *input = NULL;
1783 bool interpolate, pipe, popup;
1785 if (strcmp(ci->key, "Shell Command") != 0) {
1789 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2", 0, NULL,
1793 dirname = call_ret(strsave, "get-attr", ci->focus, 0, NULL, "dirname");
1794 attr_set_str(&p->attrs, "dirname", dirname ?: ".");
1795 attr_set_str(&p->attrs, "prompt", "Shell command");
1796 if (ci->num == 0 || ci->num == 1)
1797 strcat(aux, "i"); // interpolate
1799 strcat(aux, "P"); // popup
1800 if (strcmp(ci->key, "K:A-|") == 0)
1801 strcat(aux, "p"); // pipe from selection
1802 attr_set_str(&p->attrs, "popup-aux", aux);
1803 attr_set_str(&p->attrs, "done-key", "Shell Command");
1804 call("doc:set-name", p, 0, NULL, "Shell Command", -1);
1805 p = call_ret(pane, "attach-history", p, 0, NULL, "*Shell History*");
1807 p = pane_register(p, 0, &find_handle.c, "shellcmd");
1811 comm_call(ci->comm2, "cb", p);
1815 call("Message", ci->focus, 0, NULL, "Shell command aborted");
1819 interpolate = ci->str2 && strchr(ci->str2, 'i');
1820 pipe = ci->str2 && strchr(ci->str2, 'p');
1821 popup = ci->str2 && strchr(ci->str2, 'P');
1824 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
1826 input = call_ret(str, "doc:get-str", ci->focus,
1827 0, NULL, NULL, 0, mk);
1828 /* make the selection replacable */
1829 attr_set_int(&mk->attrs, "emacs:active", 3);
1832 doc = call_ret(pane, "doc:from-text", ci->focus, 0, NULL, name, 0, NULL,
1838 path = pane_attr_get(ci->focus, "dirname");
1839 attr_set_str(&doc->attrs, "dirname", path);
1841 /* shellcmd is attached directly to the document, not in the view
1842 * stack. It is go-between for document and external command.
1843 * We don't need a doc attachment as no point is needed - we
1844 * always insert at the end.
1846 sc = call_ret(pane, "attach-shellcmd", doc, pipe ? 22 : 4,
1847 NULL, ci->str, 0, NULL, path);
1849 call("doc:replace", doc, 0, NULL,
1850 "Failed to run command - sorry\n");
1851 if (call("text-search", doc, 0, NULL, "diff|(stg|git).*show",
1852 0, NULL, ci->str) > 0)
1853 attr_set_str(&doc->attrs, "view-default", "diff");
1855 /* Close old shell docs, but if one is visible in current frame, replace
1861 call_comm("editor:notify:shell-reuse", ci->focus, &cb.c);
1863 /* choose_pane didn't attach, so set a callback to do
1864 * it when there is enough content.
1867 /* If it take more than 500msec, or includes 2
1868 * or more lines, we'll show in a pane, else
1869 * just show as a message.
1872 home_call_comm(sc, "shellcmd:set-callback",
1873 ci->focus, &shellcb,
1874 500, NULL, popup ? "P": "",
1878 par = call_ret(pane, "PopupTile", ci->focus,
1881 par = call_ret(pane, "OtherPane", ci->focus);
1884 home_call(doc, "doc:attach-view", par, 1);
1887 if (sc && interpolate)
1888 /* Need a callback when the pipe command finished */
1889 home_call_comm(sc, "shellcmd:set-callback", ci->focus,
1898 const char *last = ksuffix(ci, "K:A-");
1905 if (rpt == NO_NUMERIC)
1908 rpt = rpt * 10 + *last - '0';
1910 call("Mode:set-num", ci->focus, neg ? -rpt : rpt);
1911 call("Mode:set-num2", ci->focus, ci->num2);
1917 call("Mode:set-num", ci->focus, - ci->num);
1918 call("Mode:set-num2", ci->focus, ci->num2);
1922 DEF_CMD(emacs_prefix)
1924 /* as a generic arg (ctrl-U) which is positive and
1925 * as as a repeat-count of 4, but is different to 4.
1926 * I should probably allow digits to alter the number.
1928 call("Mode:set-num", ci->focus, 4);
1932 DEF_CMD(emacs_kill_doc)
1934 if (ci->num <= 0 || ci->num == NO_NUMERIC) {
1935 /* Check if modified. */
1936 char *m = pane_attr_get(ci->focus, "doc-modified");
1938 if (m && strcmp(m, "yes") == 0)
1939 f = pane_attr_get(ci->focus, "filename");
1941 call("Message:modal", ci->focus, 0, NULL,
1942 "Document is modified - please save or give prefix arg");
1946 return call("doc:destroy", ci->focus);
1949 DEF_CMD(emacs_revisit)
1951 return call("doc:load-file", ci->focus, 2, NULL, NULL, -1);
1954 DEF_CMD(emacs_save_all)
1956 int ret = call("docs:save-all", ci->focus, 0, NULL, NULL, 1);
1959 call("Message", ci->focus, 0, NULL, "No files need to be saved.");
1962 if (ci->num == NO_NUMERIC) {
1963 struct pane *p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM");
1965 return call("docs:show-modified", p);
1967 return call("docs:save-all", ci->focus);
1970 DEF_CMD(emacs_reposition)
1973 int repoint = attr_find_int(ci->focus->attrs, "emacs-repoint");
1975 if (repoint != -1) {
1976 /* Move point to end of display, if that is in
1977 * the right direction. That will mean it has moved
1980 m = mark_at_point(ci->focus, NULL, MARK_UNGROUPED);
1982 struct mark *m2 = mark_dup(m);
1983 call("Move-CursorXY", ci->focus, 0, m, NULL,
1985 INT_MAX, repoint < 0 ? ci->focus->h-1 : 0);
1987 /* can only move point backwards */
1988 if (m->seq < m2->seq)
1989 call("Move-to", ci->focus, 0, m);
1991 /* can only move point forwards */
1992 if (m->seq > m2->seq)
1993 call("Move-to", ci->focus, 0, m);
1997 attr_set_str(&ci->focus->attrs, "emacs-repoint", NULL);
1999 return Efallthrough;
2002 DEF_CMD(emacs_start_search)
2004 struct pane *p = NULL, *hp;
2007 hp = call_ret(pane, "attach-emacs-search-highlight", ci->focus);
2010 p = call_ret(pane, "PopupTile", hp, 0, NULL, "TR2",
2015 home_call(hp, "highlight:set-popup", p);
2017 attr_set_str(&p->attrs, "prompt", "Search");
2018 attr_set_str(&p->attrs, "done-key", "Search String");
2020 hp = call_ret(pane, "attach-history", p, 0, NULL, "*Search History*");
2024 call("doc:set-name", p, 0, NULL, "Search", -1);
2025 if (strcmp(ci->key, "K:C-R") == 0)
2027 if (strcmp(ci->key, "K:A-%") == 0)
2029 call_ret(pane, "attach-emacs-search", p, mode);
2034 DEF_CMD(emacs_command)
2038 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2", 0, NULL, "");
2041 attr_set_str(&p->attrs, "prompt", "Cmd");
2042 attr_set_str(&p->attrs, "done-key", "emacs:command");
2043 call("doc:set-name", p, 0, NULL, "K:Ax command", -1);
2044 p = call_ret(pane, "attach-history", p, 0, NULL, "*Command History*");
2046 pane_register(p, 0, &find_handle.c, "cmd");
2050 DEF_CMD(emacs_do_command)
2058 asprintf(&cmd, "interactive-cmd-%s", ci->str);
2061 ret = call(cmd, ci->focus, 0, ci->mark);
2062 free(cmd); cmd = NULL;
2064 asprintf(&cmd, "Command %s not found", ci->str);
2065 call("Message", ci->focus, 0, NULL, cmd);
2066 } else if (ret < 0) {
2067 asprintf(&cmd, "Command %s Failed", ci->str);
2068 call("Message", ci->focus, 0, NULL, cmd);
2076 struct call_return *cr = container_of(ci->comm, struct call_return, c);
2083 struct mark *m = vmark_new(cr->p, MARK_UNGROUPED, NULL);
2084 call("doc:list-add", cr->p, 0, m);
2085 call("doc:set-attr", cr->p, 0, m, "cmd", 0, NULL, cmd);
2090 REDEF_CMD(emacs_cmd_complete)
2093 struct pane *doc = NULL, *pop = NULL, *p;
2094 struct call_return cr;
2098 s = call_ret(strsave, "doc:get-str", ci->focus);
2101 doc = call_ret(pane, "attach-doc-list", ci->focus);
2104 call("doc:set-name", doc, 0, NULL, "*Command List*");
2105 call("doc:set:autoclose", doc, 1);
2106 attr_set_str(&doc->attrs, "render-simple", "format");
2107 attr_set_str(&doc->attrs, "heading", "");
2108 attr_set_str(&doc->attrs, "line-format", "%cmd");
2111 call_comm("keymap:list", ci->focus, &cr.c,
2112 0, NULL, "interactive-cmd-");
2113 call("doc:list-sort", doc, 0, NULL, "cmd");
2114 pop = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM1r");
2117 p = home_call_ret(pane, cr.p, "doc:attach-view", pop, -1, NULL, "simple");
2120 attr_set_str(&p->attrs, "done-key", "Replace");
2121 p = call_ret(pane, "attach-render-complete", p);
2124 cr = call_ret(all, "Complete:prefix", p, 1, NULL, s);
2125 if (cr.s && strlen(cr.s) <= strlen(s) && cr.ret-1 > 1) {
2126 /* We need the dropdown - delete prefix and drop-down will
2129 struct mark *start = mark_dup(ci->mark);
2130 call("Move-Char", ci->focus, -strlen(s), start);
2131 call("Replace", ci->focus, 1, start);
2136 /* Replace 's' with the result */
2137 struct mark *start = mark_dup(ci->mark);
2138 call("Move-Char", ci->focus, -strlen(s), start);
2139 call("Replace", ci->focus, 1, start, cr.s);
2142 /* Now need to close the popup */
2154 DEF_CMD(emacs_version)
2158 asprintf(&v, "%s ; emacs-mode - v" VERSION " - " VERS_DATE, edlib_version);
2160 call("Message", ci->focus, 0, NULL, v);
2167 /* View the debug log */
2168 struct pane *p, *doc;
2170 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL, "*Debug Log*");
2172 call("interactive-cmd-view-log", ci->focus);
2173 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL,
2178 p = call_ret(pane, "ThisPane", ci->focus);
2180 home_call(doc, "doc:attach-view", p, 1);
2186 struct mark *m = call_ret(mark2, "doc:point", ci->focus);
2188 clear_selection(ci->focus, NULL, m, 0);
2190 call("Move-to", ci->focus, 1);
2191 m = call_ret(mark2, "doc:point", ci->focus);
2193 /* ci->num == 1 means replacable */
2194 set_selection(ci->focus, NULL, m, ci->num == 1 ? 3 : 1);
2198 DEF_CMD(emacs_abort)
2200 /* On abort, forget mark */
2201 struct mark *m = call_ret(mark2, "doc:point", ci->focus);
2203 clear_selection(ci->focus, NULL, m, 0);
2204 return Efallthrough;
2207 DEF_CMD(emacs_swap_mark)
2209 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2215 call("Move-to", ci->focus, 1); /* Move mark to point */
2216 call("Move-to", ci->focus, 0, m); /* Move point to old mark */
2217 set_selection(ci->focus, NULL, mk, ci->num == 1 ? 3 : 1);
2224 /* Delete text from point to mark */
2225 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2231 /* Remove any selection so it cannot be claimed and so replace this copy */
2232 call("selection:claim", ci->focus);
2233 call("selection:discard", ci->focus);
2235 str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, mk);
2237 call("copy:save", ci->focus, 0, NULL, str);
2238 ret = call("Replace", ci->focus, 1, mk);
2240 clear_selection(ci->focus, NULL, mk, 0);
2247 /* copy text from point to mark */
2248 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2253 /* Remove any selection so it cannot be claimed and so replace this copy */
2254 call("selection:claim", ci->focus);
2255 call("selection:discard", ci->focus);
2257 str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, mk);
2259 call("copy:save", ci->focus, 0, NULL, str);
2260 /* Clear current highlight */
2261 clear_selection(ci->focus, NULL, mk, 0);
2267 int n = RPT_NUM(ci);
2270 struct mark *m = NULL;
2272 /* If there is a selection elsewhere, we want to commit it */
2273 call("selection:discard", ci->focus);
2274 call("selection:commit", ci->focus);
2276 str = call_ret(strsave, "copy:get", ci->focus, n - 1);
2279 /* If mark exists and is active, replace marked regions */
2280 mk = call_ret(mark2, "doc:point", ci->focus);
2281 if (mk && clear_selection(ci->focus, NULL, mk, 0)) {
2282 char *str2 = call_ret(strsave, "doc:get-str", ci->focus,
2283 0, NULL, NULL, 0, mk);
2285 call("copy:save", ci->focus, 0, NULL, str2);
2289 call("Move-to", ci->focus, 1);
2290 call("Replace", ci->focus, 1, m, str);
2292 mk = call_ret(mark2, "doc:point", ci->focus);
2293 call("Mode:set-num2", ci->focus, N2_yank);
2297 DEF_CMD(emacs_yank_pop)
2299 struct mark *mk, *m;
2303 if (N2(ci) != N2_yank)
2305 mk = call_ret(mark2, "doc:point", ci->focus);
2309 str = call_ret(strsave, "copy:get", ci->focus, num);
2312 str = call_ret(strsave, "copy:get", ci->focus, num);
2318 call("Replace", ci->focus, 1, mk, str);
2319 call("Move-to", ci->focus, 1, m);
2321 call("Mode:set-num2", ci->focus, (num << 16) | N2_yank);
2325 DEF_CMD(emacs_attrs)
2327 struct call_return cr;
2329 char *selection = "bg:white-80,vis-nl,menu-at-mouse,action-menu:emacs:selection-menu"; // grey
2334 cr = call_ret(all, "doc:point", ci->focus);
2335 if (cr.ret <= 0 || !cr.m || !cr.m2 || !ci->mark)
2337 active = attr_find_int(cr.m2->attrs, "emacs:active");
2340 if (active >= 3) /* replacable */
2341 selection = "bg:red+80,vis-nl"; // pink
2342 if (mark_same(cr.m, cr.m2))
2344 if (strcmp(ci->str, "render:interactive-mark") == 0) {
2345 if (ci->mark == cr.m2 && cr.m2->seq < cr.m->seq)
2346 return comm_call(ci->comm2, "attr:callback", ci->focus, 0,
2347 ci->mark, selection, 210);
2348 if (ci->mark == cr.m2)
2349 return comm_call(ci->comm2, "attr:callback", ci->focus, -1,
2350 ci->mark, selection, 210);
2352 if (strcmp(ci->str, "render:interactive-point") == 0) {
2353 if (cr.m == ci->mark && cr.m->seq < cr.m2->seq)
2354 return comm_call(ci->comm2, "attr:cb", ci->focus, 0,
2355 ci->mark, selection, 210);
2356 if (cr.m == ci->mark)
2357 return comm_call(ci->comm2, "attr:callback", ci->focus, -1,
2358 ci->mark, selection, 210);
2360 if (strcmp(ci->str, "start-of-line") == 0) {
2361 if ((cr.m->seq < ci->mark->seq && ci->mark->seq < cr.m2->seq &&
2362 !mark_same(ci->mark, cr.m2)) ||
2363 (cr.m2->seq < ci->mark->seq && ci->mark->seq < cr.m->seq &&
2364 !mark_same(ci->mark, cr.m)))
2365 return comm_call(ci->comm2, "attr:cb", ci->focus, 0,
2366 ci->mark, selection, 210);
2368 return Efallthrough;
2371 DEF_CMD(emacs_selection_menu)
2375 call("Message", ci->focus, 0, NULL, "So .... you want a menu do you?");
2376 p = call_ret(pane, "attach-menu", ci->focus, 0, NULL, "V", 0, NULL,
2377 "emacs:selection-menu-action", ci->x, ci->y+1);
2380 call("global-multicall-selection-menu:add-", p);
2381 call("menu-add", p, 0, NULL, "de-select", 0, NULL, ":ESC");
2385 DEF_CMD(emacs_selection_menu_action)
2387 struct pane *home = ci->home;
2388 const char *c = ci->str;
2393 /* command for focus */
2394 call(c+1, ci->focus, 0, ci->mark);
2398 call("Keystroke-sequence", home, 0, NULL, c);
2402 DEF_CMD(emacs_selection_menu_add)
2404 struct pane *p = ci->focus;
2405 call("menu-add", p, 0, NULL, "Cut", 0, NULL, ":C-W");
2406 call("menu-add", p, 0, NULL, "Copy", 0, NULL, ":A-w");
2407 call("menu-add", p, 0, NULL, "Paste-in", 0, NULL, ":C-Y");
2411 DEF_CMD(emacs_goto_line)
2413 if (ci->num == NO_NUMERIC)
2415 call("CountLines", ci->focus, ci->num, ci->mark, "goto:line");
2416 call("Move-View-Pos", ci->focus, 0, ci->mark);
2420 DEF_CMD(emacs_next_match)
2422 call("Mode:set-num2", ci->focus, N2_match);
2423 call("Message:modal", ci->focus, 0, NULL, "Type ` to search again");
2424 return call("interactive-cmd-next-match", ci->focus, ci->num, NULL, NULL,
2425 strcmp(ci->key, "K-`") == 0);
2428 DEF_CMD(emacs_match_again)
2430 if (N2(ci) != N2_match)
2431 return emacs_insert_func(ci);
2433 return emacs_next_match_func(ci);
2438 call("interactive-cmd-make", ci->focus,
2439 ci->num, ci->mark, NULL,
2440 strcmp(ci->key, "K:CC:C-M") == 0);
2444 static void update_sel(struct pane *p safe,
2445 struct mark *pt safe, struct mark *m2 safe,
2448 struct mark *mfirst, *mlast;
2451 call("Move-to", p, 1, m2);
2452 mk = call_ret(mark2, "doc:point", p);
2456 type = attr_find(m2->attrs, "emacs:selection-type");
2458 attr_set_str(&m2->attrs, "emacs:selection-type", type);
2460 if (type && strcmp(type, "char") != 0) {
2462 if (pt->seq < mk->seq) {
2469 if (strcmp(type, "word") == 0) {
2470 wint_t wch = doc_prior(p, mfirst);
2471 /* never move back over spaces */
2472 if (wch != WEOF && !iswspace(wch))
2473 call("doc:word", p, -1, mfirst);
2474 wch = doc_following(p, mlast);
2475 /* For forward over a single space is OK */
2476 if (wch != WEOF && iswspace(wch))
2479 call("doc:word", p, 1, mlast);
2481 call("doc:EOL", p, -1, mfirst);
2482 /* Include trailing newline */
2483 call("doc:EOL", p, 1, mlast, NULL, 1);
2487 /* Don't set selection until range is non-empty, else we
2488 * might clear some other selection too early.
2490 if (!mark_same(pt, mk)) {
2491 /* Must call 'claim' first as it might be claiming from us */
2492 call("selection:claim", p);
2493 set_selection(p, pt, mk, 2);
2497 DEF_CMD(emacs_press)
2499 /* The second mark (managed by core-doc) is used to record the
2500 * selected starting point. When double- or triple- click
2501 * asks for word or line selection, the actually start, which
2502 * is stored in the first mark, may be different.
2503 * That selected starting point will record the current unit
2504 * siez in the emacs:selection-type attribute.
2506 struct mark *pt = call_ret(mark, "doc:point", ci->focus);
2507 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2508 struct mark *m2 = call_ret(mark2, "doc:point", ci->focus, 2);
2509 struct mark *m = mark_new(ci->focus);
2513 /* Not in document, not my problem */
2515 return Efallthrough;
2517 /* NOTE must find new location before view changes. */
2518 call("Move-CursorXY", ci->focus, 0, m, NULL, 0, NULL, NULL, ci->x, ci->y);
2520 clear_selection(ci->focus, pt, mk, 0);
2521 call("Move-to", ci->focus, 0, m);
2522 pane_take_focus(ci->focus);
2524 if (m2 && strcmp(ci->key, "M:DPress-1") == 0) {
2525 type = attr_find(m2->attrs, "emacs:selection-type");
2528 else if (strcmp(type, "char") == 0)
2530 else if (strcmp(type, "word") == 0)
2536 /* Record start of selection */
2537 call("Move-to", ci->focus, 2, m);
2538 m2 = call_ret(mark2, "doc:point", ci->focus, 2);
2540 attr_set_str(&m2->attrs, "emacs:selection-type", type);
2543 /* Record co-ordinate of start so we can tell if the mouse moved. */
2544 attr_set_int(&m2->attrs, "emacs:track-selection",
2545 1 + ci->x * 10000 + ci->y);
2546 update_sel(ci->focus, pt, m2, type);
2553 DEF_CMD(emacs_release)
2555 struct mark *p = call_ret(mark, "doc:point", ci->focus);
2556 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2557 struct mark *m2 = call_ret(mark2, "doc:point", ci->focus, 2);
2558 struct mark *m = mark_new(ci->focus);
2563 if (!p || !m2 || !m) {
2564 /* Not in a document or no selection start - not my problem */
2566 return Efallthrough;
2569 prev_pos = attr_find_int(m2->attrs, "emacs:track-selection");
2570 type = attr_find(m2->attrs, "emacs:selection-type");
2571 moved = prev_pos != (1 + ci->x * 10000 + ci->y);
2572 attr_set_int(&m2->attrs, "emacs:track-selection", 0);
2574 call("Move-CursorXY", ci->focus,
2575 0, m, "activate", 0, NULL, NULL, ci->x, ci->y);
2576 /* That action might have closed a pane. Better check... */
2577 if (ci->focus->damaged & DAMAGED_CLOSED) {
2580 /* Moved the mouse, so new location is point */
2581 call("Move-to", ci->focus, 0, m);
2582 update_sel(ci->focus, p, m2, NULL);
2583 } else if (type && strcmp(type, "char") != 0) {
2584 /* Otherwise use the old location. Point might not
2585 * be there exactly if it was moved to end of word/line
2587 call("Move-to", ci->focus, 0, m2);
2588 update_sel(ci->focus, p, m2, NULL);
2590 clear_selection(ci->focus, p, mk, 0);
2597 DEF_CMD(emacs_menu_open)
2599 /* If there is a menu action here, activate it. */
2600 /* Don't move the cursor though */
2601 struct mark *m = mark_new(ci->focus);
2604 ret = call("Move-CursorXY", ci->focus, 0, m, "menu",
2605 0, NULL, NULL, ci->x, ci->y);
2610 DEF_CMD(emacs_menu_select)
2612 /* If a menu was opened it should have claimed the mouse focus
2613 * so ci->focus is now the menu. We want to activate the entry
2616 struct mark *m = mark_new(ci->focus);
2619 ret = call("Move-CursorXY", ci->focus, 0, m, "activate",
2620 0, NULL, NULL, ci->x, ci->y);
2625 DEF_CMD(emacs_motion)
2627 struct mark *p = call_ret(mark, "doc:point", ci->focus);
2628 struct mark *m2 = call_ret(mark2, "doc:point", ci->focus, 2);
2633 if (attr_find_int(m2->attrs, "emacs:track-selection") <= 0)
2634 return Efallthrough;
2636 call("Move-CursorXY", ci->focus,
2637 0, NULL, NULL, 0, NULL, NULL, ci->x, ci->y);
2639 update_sel(ci->focus, p, m2, NULL);
2643 DEF_CMD(emacs_paste)
2647 /* First commit the selection, then collect it */
2648 call("selection:commit", ci->focus);
2649 str = call_ret(strsave, "copy:get", ci->focus);
2651 call("Move-CursorXY", ci->focus,
2652 0, NULL, NULL, 0, NULL, NULL, ci->x, ci->y);
2657 call("Replace", ci->focus, 0, NULL, str);
2659 pane_take_focus(ci->focus);
2664 DEF_CMD(emacs_paste_direct)
2666 /* This command is an explicit paste command and the content
2667 * is available via "Paste:get".
2668 * It might come via the mouse (with x,y) or via a keystroke.
2671 if (ci->key[0] == 'M') {
2672 call("Move-CursorXY", ci->focus,
2673 0, NULL, NULL, 0, NULL, NULL, ci->x, ci->y);
2674 pane_take_focus(ci->focus);
2677 s = call_ret(str, "Paste:get", ci->focus);
2679 struct mark *pt = call_ret(mark, "doc:point", ci->focus);
2681 call("Move-to", ci->focus, 1);
2682 mk = call_ret(mark2, "doc:point", ci->focus);
2683 call("Replace", ci->focus, 0, mk, s, 0, pt);
2684 set_selection(ci->focus, pt, mk, 2);
2690 DEF_CMD(emacs_sel_claimed)
2692 /* Should possibly just change the color of our selection */
2693 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2695 clear_selection(ci->focus, NULL, mk, 0);
2699 DEF_CMD(emacs_sel_commit)
2701 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2702 struct mark *p = call_ret(mark, "doc:point", ci->focus);
2704 if (p && mk && !mark_same(p, mk)) {
2707 str = call_ret(strsave, "doc:get-str", ci->focus,
2711 call("copy:save", ci->focus, 0, NULL, str);
2717 DEF_CMD(emacs_readonly)
2721 ro = pane_attr_get(ci->focus, "doc-readonly");
2723 if (ro && strcmp(ro,"yes") == 0)
2724 call("doc:set:readonly", ci->focus, 0);
2726 call("doc:set:readonly", ci->focus, 1);
2730 DEF_CMD(emacs_shift)
2735 shift = pane_attr_get_int(ci->focus, "render-wrap", -1);
2736 if (strcmp(ci->key, "K:CX->") == 0 || strcmp(ci->key, "K->") == 0)
2738 if (rpt == NO_NUMERIC) {
2743 } else if (rpt == -NO_NUMERIC) {
2750 } else if (rpt >= 0) {
2755 if (shift > 0 && shift + rpt < 0)
2761 attr_set_str(&ci->focus->attrs, "render-wrap", "yes");
2763 attr_set_int(&ci->focus->attrs, "render-wrap", shift);
2765 attr_set_str(&ci->focus->attrs, "render-wrap", "0 auto");
2767 /* When reducing shift to zero, don't enable auto */
2768 attr_set_int(&ci->focus->attrs, "render-wrap", 0);
2769 call("view:changed", ci->focus);
2770 call("Mode:set-num2", ci->focus, N2_shift);
2771 call("Message:modal", ci->focus, 0, NULL, "Type < or > to shift again");
2775 DEF_CMD(emacs_shift_again)
2777 if (N2(ci) != N2_shift)
2778 return emacs_insert_func(ci);
2780 return emacs_shift_func(ci);
2783 DEF_CMD(emacs_growx)
2785 if (ci->key[strlen(ci->key)-1] == '}')
2786 call("Window:x+", ci->focus, ci->num);
2788 call("Window:x-", ci->focus, ci->num);
2789 call("Mode:set-num2", ci->focus, N2_growx);
2790 call("Message:modal", ci->focus, 0, NULL, "Type { or } to grow again");
2794 DEF_CMD(emacs_growx_again)
2796 if (N2(ci) != N2_growx)
2797 return emacs_insert_func(ci);
2799 return emacs_growx_func(ci);
2802 DEF_CMD(emacs_scale_relative)
2804 struct pane *p = ci->focus;
2805 char *sc = pane_attr_get(p, "scale:M");
2810 call("Message:modal", p, 0, NULL,
2811 "Cannot zoom display with fixed-sized font");
2814 sc = pane_attr_get(p, "scale");
2815 if (sc && strchr(sc, 'x')) {
2816 call("Message:modal", p, 0, NULL,
2817 "Cannot zoom display with fixed layout");
2825 if (ci->key[strlen(ci->key)-1] == '-')
2826 scale = 10 * scale / 12;
2828 scale = 12 * scale / 10;
2829 snprintf(num, sizeof(num)-1, "%d", scale);
2830 call("window:set:scale", p, 0, NULL, num);
2834 DEF_CMD(emacs_curs_pos)
2844 c = mark_dup(ci->mark);
2845 nxt = doc_following(ci->focus, c);
2847 while ((ch = doc_prev(ci->focus, c)) != WEOF && !is_eol(ch))
2849 while (mark_ordered_not_same(c, ci->mark)) {
2850 ch = doc_next(ci->focus, c);
2854 } else if (ch == '\t') {
2863 asprintf(&msg, "Cursor at column %d (%d chars), char is %d (0x%x)",
2864 col, chars, nxt, nxt);
2865 call("Message", ci->focus, 0, NULL, msg);
2870 DEF_CMD(emacs_word_count)
2873 struct pane *p = ci->focus;
2880 mk = call_ret(mark2, "doc:point", p);
2881 if (mk && attr_find_int(mk->attrs, "emacs:active") <= 0)
2883 call("CountLines", p, 0, ci->mark);
2884 wp = attr_find_int(ci->mark->attrs, "word");
2887 call("CountLines", p, 0, mk);
2888 wm = attr_find_int(mk->attrs, "word");
2889 asprintf(&msg, "%d words in region", abs(wp-wm));
2891 int wd = pane_attr_get_int(p, "words", 0);
2892 asprintf(&msg, "After word%s %d of %d", wp==2?"":"s", wp-1, wd);
2894 call("Message", p, 0, NULL, msg);
2901 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2902 struct mark *p = call_ret(mark, "doc:point", ci->focus);
2905 if (!clear_selection(ci->focus, p, mk, 0))
2908 if (strcmp(ci->key, "K:A-q") == 0) {
2909 if (call("fill-paragraph", ci->focus, ci->num, p, NULL,
2910 0, mk) == Efallthrough) {
2911 p2 = call_ret(pane, "attach-textfill", ci->focus);
2913 call("fill-paragraph", p2, ci->num, p, NULL,
2917 /* Don't try to load anything, the file-type should
2918 * have loaded something if relevant
2920 if (call("reindent-paragraph", ci->focus, ci->num, p, NULL,
2922 call("Message", ci->focus, 0, NULL,
2923 "Reindent not supported on the document.");
2928 DEF_CMD(emacs_abbrev)
2930 call("attach-abbrev", ci->focus);
2934 DEF_CMD(emacs_showinput)
2936 struct pane *p, *doc;
2938 if (call("input:log", ci->focus) <= 0) {
2939 call("Message", ci->focus, 0, NULL,
2940 "Cannot get log of recent input.");
2944 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL, "*Debug Log*");
2946 call("interactive-cmd-view-log", ci->focus);
2947 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL,
2952 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM");
2954 p = home_call_ret(pane, doc, "doc:attach-view", p, 1);
2956 call("doc:file", p, 1);
2961 DEF_CMD(emacs_macro_start)
2965 ret = call("macro:capture", ci->focus);
2967 call("Message", ci->focus, 0, NULL,
2968 "Macro capture already happening");
2970 call("Message", ci->focus, 0, NULL,
2971 "Macro facility not available");
2975 DEF_CMD(emacs_macro_stop)
2979 ret = call("macro:finished", ci->focus, 2);
2981 call("Message", ci->focus, 0, NULL,
2982 "Macro successfully created.");
2983 else if (ret == Efalse)
2984 call("Message", ci->focus, 0, NULL,
2985 "No macro being created.");
2987 call("Message", ci->focus, 0, NULL,
2988 "Failure creating macro.");
2992 DEF_CMD(emacs_macro_run)
2994 int cnt = RPT_NUM(ci);
2996 if (strcmp(ci->key, "K-e") == 0 && N2(ci) != N2_runmacro)
2997 return emacs_insert_func(ci);
3002 call("macro:replay", ci->focus, 1) > 0)
3005 call("Mode:set-num2", ci->focus, N2_runmacro);
3006 call("Message:modal", ci->focus, 0, NULL, "Type 'e' to repeat macro");
3007 return cnt < 1 ? 1 : Efail;
3016 static const char spell_choices[] =
3017 "0123456789-=;,/ABCDEFGHIJKLMNOPQRSTUVWXYZ";
3018 DEF_CB(get_suggestion)
3020 struct bb *b = container_of(ci->comm, struct bb, c);
3026 buf_concat(&b->b, " - (a)ccept, (i)nsert - ");
3028 buf_concat(&b->b, ", ");
3030 if (b->count < (int)sizeof(spell_choices)-1) {
3031 buf_append(&b->b, '(');
3032 buf_append(&b->b, spell_choices[b->count]);
3033 buf_append(&b->b, ')');
3036 buf_concat(&b->b, ci->str);
3040 DEF_CMD(emacs_spell)
3045 int rpt = RPT_NUM(ci);
3050 /* We always find a word that is partly *after* the given
3051 * make, but we want to find the word before point, so step
3054 doc_prev(ci->focus, ci->mark);
3056 if (ci->num != NO_NUMERIC)
3057 /* As a repeat-count was given, only look at intersting words */
3058 call("Spell:NextWord", ci->focus, 0, ci->mark);
3059 st = mark_dup(ci->mark);
3060 word = call_ret(str, "Spell:ThisWord", ci->focus, 0, ci->mark, NULL, 0, st);
3061 if (!word || !*word) {
3063 call("Message", ci->focus, 0, NULL,
3064 "Spell check reached end-of-file");
3065 call("Spell:Save", ci->focus);
3070 ret = call("Spell:Check", ci->focus, 0, NULL, word);
3076 call("Message", ci->focus, 0, NULL,
3077 strconcat(ci->focus, "\"", word,
3078 "\" is a correct spelling."));
3079 } else if (ret == Efalse) {
3082 buf_concat(&b.b, "\"");
3083 buf_concat(&b.b, word);
3084 buf_concat(&b.b, "\" is NOT correct");
3087 b.c = get_suggestion;
3088 call_comm("Spell:Suggest", ci->focus, &b.c,
3091 buf_concat(&b.b, " ... no suggestions");
3093 attr_set_str(&ci->focus->attrs, "spell:last-error",
3095 attr_set_str(&ci->focus->attrs, "spell:suggestions",
3097 attr_set_int(&ci->focus->attrs, "spell:offset", 0);
3099 call("Mode:set-all", ci->focus, rpt-1, NULL, ":Spell");
3100 call("Message:modal", ci->focus, 0, NULL, buf_final(&b.b));
3101 free(buf_final(&b.b));
3102 } else if (ret == Efail) {
3107 call("Message", ci->focus, 0, NULL,
3108 strconcat(ci->focus, "\"", word,
3109 "\" is not a word."));
3111 call("Message", ci->focus, 0, NULL,
3112 strconcat(ci->focus, "Spell check failed for \"", word,
3118 DEF_CMD(emacs_spell_choose)
3120 const char *k = ksuffix(ci, "K:Spell-");
3121 char match[4] = "( )";
3122 char *suggest = attr_find(ci->focus->attrs,
3123 "spell:suggestions");
3124 char *last = attr_find(ci->focus->attrs,
3125 "spell:last-error");
3130 if (!*k || !suggest || !last || !ci->mark)
3133 cp = strstr(suggest, match);
3137 ep = strchr(cp, ',');
3139 cp = strnsave(ci->focus, cp, ep-cp);
3141 m = mark_dup(ci->mark);
3142 i = utf8_strlen(last);
3144 doc_prev(ci->focus, m);
3147 call("doc:replace", ci->focus, 0, m, cp, 0, ci->mark);
3148 attr_set_str(&ci->focus->attrs, "spell:suggestions", NULL);
3149 attr_set_str(&ci->focus->attrs, "spell:last-error", NULL);
3152 doc_next(ci->focus, ci->mark);
3153 home_call(ci->home, "emacs:respell", ci->focus,
3154 ci->num, ci->mark, NULL,
3155 ci->num2, ci->mark2);
3161 DEF_CMD(emacs_spell_abort)
3166 DEF_CMD(emacs_spell_skip)
3169 doc_next(ci->focus, ci->mark);
3170 home_call(ci->home, "emacs:respell", ci->focus,
3171 ci->num, ci->mark, NULL,
3172 ci->num2, ci->mark2);
3177 DEF_CMD(emacs_spell_insert)
3179 char *last = attr_find(ci->focus->attrs,
3180 "spell:last-error");
3182 attr_set_str(&ci->focus->attrs, "spell:suggestions", NULL);
3183 attr_set_str(&ci->focus->attrs, "spell:last-error", NULL);
3184 call("Spell:AddWord", ci->focus, 1, NULL, last);
3188 doc_next(ci->focus, ci->mark);
3189 home_call(ci->home, "emacs:respell", ci->focus,
3190 ci->num, ci->mark, NULL,
3191 ci->num2, ci->mark2);
3196 DEF_CMD(emacs_spell_accept)
3198 char *last = attr_find(ci->focus->attrs,
3199 "spell:last-error");
3201 attr_set_str(&ci->focus->attrs, "spell:suggestions", NULL);
3202 attr_set_str(&ci->focus->attrs, "spell:last-error", NULL);
3203 call("Spell:AddWord", ci->focus, 0, NULL, last);
3207 doc_next(ci->focus, ci->mark);
3208 home_call(ci->home, "emacs:respell", ci->focus,
3209 ci->num, ci->mark, NULL,
3210 ci->num2, ci->mark2);
3215 static int spell_shift(struct pane *focus safe, int num, int inc)
3217 int o = pane_attr_get_int(focus, "spell:offset", 0);
3223 msg = pane_attr_get(focus, "spell:suggestions");
3226 for (i = 0; i < o && (c = strchr(msg, ',')) != NULL; i++)
3228 attr_set_int(&focus->attrs, "spell:offset", i);
3230 call("Mode:set-all", focus, num, NULL, ":Spell");
3231 call("Message:modal", focus, 0, NULL, msg);
3235 DEF_CMD(emacs_spell_left)
3237 return spell_shift(ci->focus, ci->num, -1);
3240 DEF_CMD(emacs_spell_right)
3242 return spell_shift(ci->focus, ci->num, 1);
3245 DEF_CMD(emacs_quote)
3250 struct mark *mk = NULL;
3251 bool free_mark = False;
3253 if (ci->num >= 0 && ci->num < NO_NUMERIC)
3255 else if (N2(ci) == N2_uniquote && ci->mark &&
3256 (str = attr_find(ci->mark->attrs, "emacs:unicode_char")) != NULL) {
3257 struct call_return cr;
3259 if (ci->num < 0 && i > 1)
3263 cr = call_ret(all, "Unicode-names", ci->focus, i, NULL, str);
3264 if (cr.s && cr.i && (wint_t)cr.i != WEOF) {
3266 call("Message", ci->focus, 0, NULL,
3267 strconcat(ci->focus,
3268 "Unicode char <", cr.s, ">"));
3269 call("Mode:set-num2", ci->focus,
3270 N2_uniquote | (i << 16));
3271 mk = mark_dup(ci->mark);
3272 doc_prev(ci->focus, mk);
3275 call("Message", ci->focus, 0, NULL,
3276 strconcat(ci->focus,
3277 "Cannot find another character <", str, ">"));
3280 } else if (wch == WEOF && ci->mark &&
3281 (mk = call_ret(mark2, "doc:point", ci->focus)) != NULL &&
3282 clear_selection(ci->focus, NULL, mk, 0) &&
3283 (str = call_ret(strsave, "doc:get-str", ci->focus,
3284 0, NULL, NULL, 0, mk)) != NULL) {
3289 x = strtoul(str, &ep, 16);
3290 if (ep && *ep == 0) {
3292 call("Message", ci->focus, 0, NULL,
3293 strconcat(ci->focus, "Hex code 0x", str));
3295 struct call_return cr;
3296 cr = call_ret(all, "Unicode-names", ci->focus,
3300 call("Message", ci->focus, 0, NULL,
3301 strconcat(ci->focus,
3302 "Unicode char <", cr.s, ">"));
3304 attr_set_str(&ci->mark->attrs,
3305 "emacs:unicode_char",
3307 call("Mode:set-num2", ci->focus,
3308 N2_uniquote | (1 << 16));
3311 call("Message", ci->focus, 0, NULL,
3312 strconcat(ci->focus,
3313 "Cannot find character <", str, ">"));
3319 call("Mode:set-all", ci->focus, ci->num, NULL, ":CQ", ci->num2);
3322 call("Replace", ci->focus, 0, mk, put_utf8(b, wch));
3328 DEF_PFX_CMD(cx_cmd, ":CX");
3329 DEF_PFX_CMD(cx4_cmd, ":CX4");
3330 DEF_PFX_CMD(cx5_cmd, ":CX5");
3331 DEF_PFX_CMD(cx44_cmd, ":CX44");
3332 DEF_PFX_CMD(cc_cmd, ":CC");
3333 DEF_PFX_CMD(help_cmd, ":Help");
3335 static void emacs_init(void)
3343 key_add(m, "K:C-X", &cx_cmd.c);
3344 key_add(m, "K:CX-4", &cx4_cmd.c);
3345 /* C-\ is generated by C-4. Weird... */
3346 key_add(m, "K:CX:C-\\", &cx4_cmd.c);
3347 key_add(m, "K:CX-5", &cx5_cmd.c);
3348 key_add(m, "K:CX4-4", &cx44_cmd.c);
3349 key_add(m, "K:CX4:C-\\", &cx44_cmd.c);
3350 key_add(m, "K:C-C", &cc_cmd.c);
3351 key_add(m, "K:F1", &help_cmd.c);
3353 key_add(m, "K:C-Q", &emacs_quote);
3355 for (i = 0; i < ARRAY_SIZE(move_commands); i++) {
3356 struct move_command *mc = &move_commands[i];
3357 key_add(m, mc->k1, &mc->cmd);
3359 key_add(m, mc->k2, &mc->cmd);
3361 key_add(m, mc->k3, &mc->cmd);
3364 for (i = 0; i < ARRAY_SIZE(simple_commands); i++) {
3365 struct simple_command *sc = &simple_commands[i];
3366 key_add(m, sc->k, &sc->cmd);
3369 key_add_range(m, "K- ", "K-~", &emacs_insert);
3370 key_add_range(m, "K-\200", "K-\377\377\377\377", &emacs_insert);
3371 key_add(m, "K:Tab", &emacs_insert_other);
3372 //key_add(m, "K:LF", &emacs_insert_other);
3373 key_add(m, "K:Enter", &emacs_insert_other);
3374 key_add(m, "K:C-O", &emacs_insert_other);
3375 key_add(m, "Interactive:insert", &emacs_interactive_insert);
3376 key_add(m, "Interactive:delete", &emacs_interactive_delete);
3378 key_add(m, "K:C-_", &emacs_undo);
3379 key_add(m, "K:CX-u", &emacs_undo);
3380 key_add(m, "K:C-/", &emacs_undo);
3381 key_add(m, "K:C-Z", &emacs_undo);
3383 key_add(m, "K:C-L", &emacs_recenter);
3385 key_add(m, "K:CX:C-F", &emacs_findfile);
3386 key_add(m, "K:CX4:C-F", &emacs_findfile);
3387 key_add(m, "K:CX4-f", &emacs_findfile);
3388 key_add(m, "K:CX44-f", &emacs_findfile);
3389 key_add_prefix(m, "File Found:", &emacs_findfile);
3391 key_add(m, "K:CX:C-W", &emacs_writefile);
3392 key_add_prefix(m, "emacs:write_file:", &emacs_do_writefile);
3394 key_add(m, "K:CX-i", &emacs_insertfile);
3395 key_add_prefix(m, "emacs:insert_file:", &emacs_do_insertfile);
3397 key_add(m, "K:CX-b", &emacs_finddoc);
3398 key_add(m, "K:CX4-b", &emacs_finddoc);
3399 key_add(m, "K:CX44-b", &emacs_finddoc);
3400 key_add_prefix(m, "Doc Found:", &emacs_finddoc);
3402 key_add(m, "K:CX:C-B", &emacs_viewdocs);
3403 key_add(m, "K:CX4:C-B", &emacs_viewdocs);
3404 key_add(m, "K:CX44:C-B", &emacs_viewdocs);
3406 key_add(m, "K:CX-k", &emacs_kill_doc);
3408 key_add(m, "K:CX-s", &emacs_save_all);
3410 key_add(m, "K:CX:C-V", &emacs_revisit);
3412 key_add(m, "K:CX-=", &emacs_curs_pos);
3413 key_add(m, "K:A-=", &emacs_word_count);
3415 key_add(m, "K:CX-<", &emacs_shift);
3416 key_add(m, "K:CX->", &emacs_shift);
3417 key_add(m, "K-<", &emacs_shift_again);
3418 key_add(m, "K->", &emacs_shift_again);
3420 key_add(m, "K:CX-{", &emacs_growx);
3421 key_add(m, "K:CX-}", &emacs_growx);
3422 key_add(m, "K-{", &emacs_growx_again);
3423 key_add(m, "K-}", &emacs_growx_again);
3425 key_add(m, "K:CX:C-=", &emacs_scale_relative);
3426 key_add(m, "K:CX:C--", &emacs_scale_relative);
3428 key_add(m, "K:C-S", &emacs_start_search);
3429 key_add(m, "K:C-R", &emacs_start_search);
3430 key_add(m, "K:A-%", &emacs_start_search);
3431 key_add(m, "render:reposition", &emacs_reposition);
3433 key_add(m, "K:CX:C-C", &emacs_exit);
3434 key_add(m, "emacs:deactivate", &emacs_deactivate);
3436 key_add(m, "K:C-U", &emacs_prefix);
3438 key_add(m, "K:A-!", &emacs_shell);
3439 key_add(m, "K:A-|", &emacs_shell);
3440 key_add(m, "Shell Command", &emacs_shell);
3442 key_add(m, "K:CX-`", &emacs_next_match);
3443 key_add(m, "K-`", &emacs_match_again);
3445 key_add(m, "K:CX-1", &emacs_close_others);
3446 key_add(m, "K-1", &emacs_close_others);
3448 key_add_range(m, "K:A-0", "K:A-9", &emacs_num);
3449 key_add(m, "K:A--", &emacs_neg);
3450 key_add(m, "K:C--", &emacs_neg);
3451 key_add(m, "K:C- ", &emacs_mark);
3452 key_add(m, "mode-set-mark", &emacs_mark);
3453 key_add(m, "mode-swap-mark", &emacs_swap_mark);
3454 key_add(m, "Abort", &emacs_abort);
3455 key_add(m, "Cancel", &emacs_abort);
3456 key_add(m, "K:C-W", &emacs_wipe);
3457 key_add(m, "K:A-w", &emacs_copy);
3458 key_add(m, "K:C-Y", &emacs_yank);
3459 key_add(m, "K:A-y", &emacs_yank_pop);
3460 key_add(m, "map-attr", &emacs_attrs);
3461 key_add(m, "emacs:selection-menu", &emacs_selection_menu);
3462 key_add(m, "emacs:selection-menu-action", &emacs_selection_menu_action);
3464 key_add(m, "K:A-g", &emacs_goto_line);
3465 key_add(m, "K:A-x", &emacs_command);
3466 key_add(m, "K:A-X", &emacs_command);
3467 key_add(m, "K:CC-m", &emacs_make);
3468 key_add(m, "K:CC:C-M", &emacs_make);
3470 key_add(m, "K:A:C-V", &emacs_move_view_other);
3472 key_add(m, "K:CX:C-Q", &emacs_readonly);
3474 key_add_prefix(m, "K:CQ-", &emacs_quote_insert);
3475 key_add_prefix(m, "K:CQ:C-", &emacs_quote_insert);
3477 key_add(m, "K:A-q", &emacs_fill);
3478 key_add(m, "K:A:C-Q", &emacs_fill);
3479 key_add(m, "K:A-/", &emacs_abbrev);
3480 key_add(m, "K:A-;", &emacs_spell);
3481 key_add(m, "emacs:respell", &emacs_spell);
3482 key_add_prefix(m, "K:Spell-", &emacs_spell_choose);
3483 key_add(m, "K:Spell-a", &emacs_spell_accept);
3484 key_add(m, "K:Spell-i", &emacs_spell_insert);
3485 key_add(m, "K:Spell- ", &emacs_spell_skip);
3486 key_add(m, "K:Spell:Enter", &emacs_spell_abort);
3487 key_add(m, "K:Spell:ESC", &emacs_spell_abort);
3488 key_add(m, "K:Spell:Left", &emacs_spell_left);
3489 key_add(m, "K:Spell:C-B", &emacs_spell_left);
3490 key_add(m, "K:Spell:Right", &emacs_spell_right);
3491 key_add(m, "K:Spell:C-F", &emacs_spell_right);
3493 key_add(m, "K:Help-l", &emacs_showinput);
3495 key_add(m, "emacs:command", &emacs_do_command);
3496 key_add(m, "interactive-cmd-version", &emacs_version);
3497 key_add(m, "interactive-cmd-log", &emacs_log);
3499 key_add(m, "M:Press-1", &emacs_press);
3500 key_add(m, "M:Release-1", &emacs_release);
3501 key_add(m, "M:Press-3", &emacs_menu_open);
3502 key_add(m, "M:Release-3", &emacs_menu_select);
3503 key_add(m, "M:DPress-1", &emacs_press);
3504 key_add(m, "M:Click-2", &emacs_paste);
3505 key_add(m, "M:C:Click-1", &emacs_paste);
3506 key_add(m, "M:Motion", &emacs_motion);
3507 key_add(m, "K:Paste", &emacs_paste_direct);
3508 key_add(m, "M:Paste", &emacs_paste_direct);
3510 key_add(m, "Notify:selection:claimed", &emacs_sel_claimed);
3511 key_add(m, "Notify:selection:commit", &emacs_sel_commit);
3513 key_add(m, "K:CX-(", &emacs_macro_start);
3514 key_add(m, "K:CX-)", &emacs_macro_stop);
3515 key_add(m, "K:CX-e", &emacs_macro_run);
3516 key_add(m, "K-e", &emacs_macro_run);
3521 DEF_LOOKUP_CMD(mode_emacs, emacs_map);
3523 static char *menus[][3] = {
3524 { "Help/Recent", ":F1 l", "R" },
3525 { "File/Open", ":C-X :C-F", "L"},
3526 { "File/Save", ":C-X :C-S", "L"},
3527 { "File/Exit", ":C-X :C-C", "L"},
3528 { "Edit/Copy", ":A-w", "L"},
3531 DEF_CMD(attach_mode_emacs)
3534 call_comm("global-set-keymap", ci->focus, &mode_emacs.c);
3535 for (i = 0; i < ARRAY_SIZE(menus); i++)
3536 call("menubar-add", ci->focus,
3537 menus[i][2][0] == 'R' ? 2 : 0,
3539 0, NULL, menus[i][1]);
3543 DEF_CMD(attach_file_entry)
3545 /* The 'type' passed must be static, not allocated */
3546 char *type = "shellcmd";
3548 if (ci->str && strcmp(ci->str, "file") == 0)
3550 else if (ci->str && strcmp(ci->str, "doc") == 0)
3552 pane_register(ci->focus, 0, &find_handle.c, type);
3557 void edlib_init(struct pane *ed safe)
3561 call_comm("global-set-command", ed, &attach_mode_emacs, 0, NULL, "attach-mode-emacs");
3562 call_comm("global-set-command", ed, &attach_file_entry, 0, NULL, "attach-file-entry");
3563 call_comm("global-set-command", ed, &emacs_shell, 0, NULL, "attach-shell-prompt");
3564 call_comm("global-set-command", ed, &emacs_selection_menu_add,
3565 0, NULL, "selection-menu:add-00-emacs");