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 */
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);
28 /* num2 is used to track if successive commands are related.
29 * Only low 16 bits identify command, other bits are free.
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 */
46 static inline int N2(const struct cmd_info *ci safe)
48 return ci->num2 & 0xffff;
51 static inline int N2a(const struct cmd_info *ci safe)
53 return ci->num2 >> 16;
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);
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.
72 static void set_selection(struct pane *p safe, struct mark *pt,
73 struct mark *mk, int type)
78 active = attr_find_int(mk->attrs, "emacs:active");
81 attr_set_int(&mk->attrs, "emacs:active", type);
83 pt = call_ret(mark, "doc:point", p);
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);
92 static bool clear_selection(struct pane *p safe, struct mark *pt,
93 struct mark *mk, int type)
98 active = attr_find_int(mk->attrs, "emacs:active");
101 if (type && active < type)
103 attr_set_int(&mk->attrs, "emacs:active", 0);
105 pt = call_ret(mark, "doc:point", p);
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);
114 static struct move_command {
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},
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},
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},
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},
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},
192 REDEF_CMD(emacs_move)
194 struct move_command *mv = container_of(ci->comm, struct move_command, cmd);
195 struct pane *cursor_pane = ci->focus;
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);
206 /* Don't change emacs:active */
207 mark_to_mark(mk, ci->mark);
209 call("Move-to", ci->focus, 1, ci->mark);
210 mk = call_ret(mark2, "doc:point", ci->focus);
214 ret = call(mv->type, ci->focus, mv->direction * RPT_NUM(ci), ci->mark,
219 if (strcmp(mv->type, "Move-View") == 0)
220 attr_set_int(&cursor_pane->attrs, "emacs-repoint",
223 mk = call_ret(mark2, "doc:point", ci->focus);
224 /* Discard a transient selection */
225 clear_selection(ci->focus, NULL, mk, 2);
230 REDEF_CMD(emacs_delete)
232 struct move_command *mv = container_of(ci->comm, struct move_command, cmd);
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);
247 m = mark_dup(ci->mark);
249 ret = call(mv->type, ci->focus, mv->direction * RPT_NUM(ci), m);
256 call("Replace", ci->focus, 1, mk, NULL, 0, mk);
258 ret = call("Replace", ci->focus, 1, m, NULL, N2(ci) == N2_undo_delete);
260 call("Mode:set-num2", ci->focus, N2_undo_delete);
265 REDEF_CMD(emacs_kill)
267 /* Like delete, but copy to copy-buffer */
268 struct move_command *mv = container_of(ci->comm, struct move_command, cmd);
276 m = mark_dup(ci->mark);
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);
283 ret = call(mv->type, ci->focus, mv->direction * RPT_NUM(ci), m);
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);
293 str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, m);
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);
298 call("Mode:set-num2", ci->focus, N2_undo_delete);
303 REDEF_CMD(emacs_case)
305 struct move_command *mv = container_of(ci->comm, struct move_command, cmd);
307 struct mark *start = NULL;
308 int cnt = mv->direction * RPT_NUM(ci);
321 start = mark_dup(ci->mark);
325 struct mark *m = mark_dup(ci->mark);
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 */
332 char *str = call_ret(str, "doc:get-str", ci->focus,
339 for (c = str; c && *c; c += 1) {
340 char type = mv->type[0];
342 type = found ? 'L' : 'U';
347 default: /* silence compiler */
348 case 'U': /* Uppercase */
354 case 'L': /* Lowercase */
360 case 'T': /* Toggle */
364 } else if (islower(*c)) {
372 ret = call("Replace", ci->focus, 1, m, str,
373 N2(ci) == N2_undo_change);
375 call(mv->type+1, ci->focus, dir, ci->mark);
378 if (changed || N2(ci) == N2_undo_change)
379 call("Mode:set-num2", ci->focus, N2_undo_change);
384 /* When moving forward, move point. When backward, leave point alone */
386 mark_to_mark(ci->mark, start);
392 REDEF_CMD(emacs_swap)
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.
399 struct move_command *mv = container_of(ci->comm, struct move_command, cmd);
401 struct mark *start = NULL;
402 int cnt = mv->direction * RPT_NUM(ci);
415 start = mark_dup(ci->mark);
419 struct mark *as, *ae, *bs, *be;
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. */
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
435 if (mark_same(as, ae) ||
437 bs->seq - ae->seq * dir < 0) {
438 mark_to_mark(ci->mark, ae);
445 astr = call_ret(str, "doc:get-str", ci->focus,
448 bstr = call_ret(str, "doc:get-str", ci->focus,
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);
456 call(mv->type, ci->focus, dir, ci->mark);
465 /* When moving forward, move point. When backward, leave point alone */
467 mark_to_mark(ci->mark, start);
473 DEF_CMD(emacs_move_view_other)
475 /* If there is an 'other' pane', Send "K:Next" there */
478 /* '512' means 'fail if no other pane' */
479 p = call_ret(pane, "OtherPane", ci->focus, 512);
482 call("Mode:set-num", p, ci->num);
483 call("Keystroke", p, 0, NULL, "emacs-move-large-other");
487 DEF_CMD(emacs_recenter)
490 if (ci->num == NO_NUMERIC && N2(ci) == N2_recentre) {
491 /* Repeated command - go to top, or bottom, or middle in order */
494 case 0: /* was center, go to top */
495 call("Move-View-Line", ci->focus, 1, ci->mark);
498 case 1: /* was top, go to bottom */
499 call("Move-View-Line", ci->focus, -1, ci->mark);
502 case 2: /* was bottom, go to middle */
503 call("Move-View-Line", ci->focus, 0, ci->mark);
507 } else if (ci->num == -NO_NUMERIC) {
508 /* Move point to bottom */
509 call("Move-View-Line", ci->focus, -1, ci->mark);
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);
515 /* Move point to middle and refresh */
516 call("Move-View-Line", ci->focus, 0, ci->mark);
517 call("Display:refresh", ci->focus);
519 call("Mode:set-num2", ci->focus, N2_recentre | (step << 16));
523 REDEF_CMD(emacs_simple);
524 REDEF_CMD(emacs_simple_num);
525 REDEF_CMD(emacs_simple_str);
526 static struct simple_command {
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-#"},
556 REDEF_CMD(emacs_simple)
558 struct simple_command *sc = container_of(ci->comm, struct simple_command, cmd);
563 return call(sc->type, ci->focus, ci->num, ci->mark);
566 REDEF_CMD(emacs_simple_num)
568 struct simple_command *sc = container_of(ci->comm, struct simple_command, cmd);
573 return call(sc->type, ci->focus, RPT_NUM(ci), ci->mark);
576 REDEF_CMD(emacs_simple_str)
578 struct simple_command *sc = container_of(ci->comm, struct simple_command, cmd);
579 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
584 if (clear_selection(ci->focus, NULL, mk, 0))
585 str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, mk);
587 return call(sc->type, ci->focus, ci->num, ci->mark, str, 0, mk);
590 REDEF_CMD(emacs_insert);
592 DEF_CMD(emacs_close_others)
594 if (strcmp(ci->key, "K-1") == 0 && N2(ci) != N2_close_others)
595 return emacs_insert_func(ci);
597 if (call("Window:close-others", ci->focus) <= 0)
599 call("Mode:set-num2", ci->focus, N2_close_others);
600 call("Message:modal", ci->focus, 0, NULL, "Type 1 to close more");
606 struct call_return *cr = container_of(ci->comm, struct call_return, c);
612 DEF_CMD(emacs_deactivate)
614 /* close-all popup has closed, see if it aborted */
616 call("event:deactivate", ci->focus);
622 struct call_return cr;
626 if (ci->num == NO_NUMERIC) {
629 /* If this is not only display, then refuse to exit */
630 call_comm("editor:notify:all-displays", ci->focus, &cr.c);
632 call("Message", ci->focus, 0, NULL,
633 "Cannot exit when there are multiple windows.");
637 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM");
638 // FIXME if called from a popup, this fails.
641 attr_set_str(&p->attrs, "done-key", "emacs:deactivate");
642 return call("docs:show-modified", p);
644 call("event:deactivate", ci->focus);
648 DEF_CMD(emacs_insert)
652 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
654 bool first = N2(ci) != N2_undo_insert;
659 if (clear_selection(ci->focus, NULL, mk, 3)) {
660 call("Replace", ci->focus, 1, mk, NULL, !first);
663 clear_selection(ci->focus, NULL, mk, 2);
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.
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);
678 DEF_CMD(emacs_quote_insert)
683 bool first = N2(ci) != N2_undo_insert;
688 if (clear_selection(ci->focus, NULL, ci->mark, 3)) {
689 call("Replace", ci->focus, 1, ci->mark, NULL, !first);
692 clear_selection(ci->focus, NULL, ci->mark, 2);
694 str = ksuffix(ci, "K:CQ-");
696 str = ksuffix(ci, "K:CQ:C-");
698 buf[0] = str[0] & 0x1f;
703 ret = call("Replace", ci->focus, 1, ci->mark, str, !first);
704 call("Mode:set-num2", ci->focus, N2_undo_insert);
712 } other_inserts[] = {
720 DEF_CMD(emacs_insert_other)
724 struct mark *m = NULL;
725 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
726 bool first = N2(ci) != N2_undo_insert;
732 for (i = 0; other_inserts[i].key; i++)
733 if (strcmp(safe_cast other_inserts[i].key, ci->key) == 0)
735 ins = other_inserts[i].insert;
739 if (clear_selection(ci->focus, NULL, mk, 3)) {
740 call("Replace", ci->focus, 1, mk, NULL, !first);
743 clear_selection(ci->focus, NULL, mk, 2);
747 m = mark_dup(ci->mark);
748 /* Move m before ci->mark, so it doesn't move when we insert */
752 ret = call("Replace", ci->focus, 1, m, ins, !first, ci->mark);
754 mark_to_mark(ci->mark, m);
757 /* A newline starts a new undo */
758 call("Mode:set-num2", ci->focus, (*ins == '\n') ? 0 : N2_undo_insert);
762 DEF_CMD(emacs_interactive_insert)
764 /* If some pane want to insert text just like it was typed,
765 * it calls this, and we set up for proper undo
768 bool first = N2(ci) != N2_undo_insert;
773 if (clear_selection(ci->focus, NULL, ci->mark, 3)) {
774 call("Replace", ci->focus, 1, ci->mark, NULL, !first);
777 clear_selection(ci->focus, NULL, ci->mark, 2);
778 ret = call("Replace", ci->focus, 1, ci->mark, ci->str,
780 call("Mode:set-num2", ci->focus,
781 strchr(ci->str, '\n') ? 0 : N2_undo_insert);
785 DEF_CMD(emacs_interactive_delete)
787 /* If some pane want to delete text just like backspace was typed,
788 * it calls this, and we set up for proper undo
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);
805 ret = call("doc:reundo", ci->focus, N2(ci) == N2_undo);
806 call("Mode:set-num2", ci->focus, N2_undo);
808 call("Message", ci->focus, 0, NULL,
809 "No further undo information");
814 REDEF_CMD(emacs_file_complete);
815 REDEF_CMD(emacs_doc_complete);
816 REDEF_CMD(emacs_cmd_complete);
818 DEF_CMD(find_complete)
820 char *type = ci->home->data;
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);
836 char *type = ci->home->data;
837 char *str = call_ret(strsave, "doc:get-str", ci->focus);
838 const char *norm = NULL;
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");
849 if (strstarts(type, "file")) {
851 bool can_create = True;
852 bool can_create_dir = True;
854 norm = file_normalize(ci->focus, str,
855 attr_find(ci->home->attrs,
857 sl = strrchr(norm, '/');
858 while (sl && sl > norm) {
859 /* Need to check directories exist. */
863 if ((stb.st_mode & S_IFMT) == S_IFDIR)
866 can_create_dir = False;
868 sl = strrchr(norm, '/');
872 if (!can_create_dir) {
873 call("Message:modal", ci->focus, 0, NULL,
874 strconcat(ci->focus, norm, " is not a directory!!"));
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,
885 /* Trigger recalc on replace */
886 call("doc:replace", ci->focus, 0, NULL, "");
888 call("Message:modal", ci->focus, 0, NULL,
889 strconcat(ci->focus, m, norm));
892 call("Message:modal", ci->focus, 0, NULL,
894 "Use Alt-Enter to create directory ", norm));
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");
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");
912 ret = call("popup:close", ci->focus, 0, NULL, norm ?: str);
925 struct find_helper *h = container_of(ci->comm, struct find_helper, c);
926 struct pane *p = ci->focus;
933 /* want the pane before nothing, so the last.
934 * So return this one and keep looking.
939 /* Want the pane that is after nothing, so
940 * the first. This one. All done.
946 name = pane_attr_get(ci->focus, "doc-name");
949 if (strcmp(name, h->name) == 0) {
951 /* Want the previous one, which is
956 /* Want the next one, so clear name
964 /* This might be what I want - keep it in case */
968 /* Don't want this - just keep going */
974 DEF_CMD(find_prevnext)
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
980 char *type = ci->home->data;
981 struct find_helper h;
983 if (strcmp(type, "doc") != 0)
985 h.name = attr_find(ci->home->attrs, "find-doc");
988 h.want_prev = strcmp(ci->key, "K:A-n") == 0;
990 call_comm("docs:byeach", ci->focus, &h.c);
992 char *name = pane_attr_get(h.ret, "doc-name");
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);
1009 char *type = ci->home->data;
1014 if (strcmp(type, "file") != 0)
1015 return Efallthrough;
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,
1022 sscanf(lens, "%d %d %d %d", &dir_start, &dir_end,
1023 &nondir_end, &basename_start);
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,
1043 DEF_CMD(find_check_replace)
1045 char *str, *cp, *sl;
1046 char *type = ci->home->data;
1050 int ipl; // Initial Path Len
1051 int dir_start, dir_end, nondir_end, basename_start;
1052 char nbuf[4 * (10+1) + 1], *lens;
1054 if (strcmp(type, "file") != 0)
1055 return Efallthrough;
1057 home_call(ci->home->parent, ci->key, ci->focus,
1058 ci->num, ci->mark, ci->str,
1059 ci->num2, ci->mark2, ci->str2);
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:
1076 * These are all lengths from start of path. They are all stored
1077 * in a single attribute: "path_lengths".
1080 str = call_ret(str, "doc:get-str", ci->focus);
1083 sl = strrchr(str, '/');
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 '/' */
1093 initial_path = attr_find(ci->home->attrs, "initial_path");
1097 ipl = strlen(initial_path);
1099 if (strncmp(str, initial_path, ipl) == 0 &&
1100 (str[ipl] == '/' || (str[ipl] == '~' &&
1101 (str[ipl+1] == '/' ||
1105 dir_start = utf8_strnlen(str, cp - str);
1107 basename_start = utf8_strnlen(str, sl - str + 1);
1114 path = file_normalize(ci->focus, str, initial_path);
1120 while (sl > cp && *sl != '/')
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] != '/')
1130 /* This must actually be a dir */
1131 dir_end = utf8_strnlen(str, sl - str);
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, '/');
1141 attr_set_str(&ci->home->attrs, "prev_dir", str);
1146 static struct map *fh_map;
1147 DEF_LOOKUP_CMD(find_handle, fh_map);
1149 static void findmap_init(void)
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);
1163 static const char * safe file_normalize(struct pane *p safe,
1165 const char *initial_path)
1167 int len = strlen(initial_path?:"");
1172 if (initial_path && strncmp(path, initial_path, len) == 0) {
1173 if (path[len] == '/' || (path[len] == '~' &&
1174 (path[len+1] == '/' || path[len+1] == 0)))
1179 if (path[0] == '~' && (path[1] == '/' || path[1] == 0)) {
1180 char *home = getenv("HOME");
1183 if (home[strlen(home) - 1] == '/' && path[1] == '/')
1185 return strconcat(p, home, path+1);
1187 dir = pane_attr_get(p, "dirname");
1190 if (dir[strlen(dir)-1] == '/')
1191 return strconcat(p, dir, path);
1192 return strconcat(p, dir, "/", path);
1195 DEF_CMD(emacs_findfile)
1198 struct pane *p, *par;
1203 path = pane_attr_get(ci->focus, "dirname");
1212 e = buf + strlen(buf);
1213 if (e < buf + sizeof(buf)-1 && e > buf && e[-1] != '/') {
1218 if (ksuffix(ci, "File Found:")[0] == 0) {
1219 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
1224 if (ksuffix(ci, "K:CX44")[0]) {
1225 attr_set_str(&p->attrs, "prompt",
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");
1235 attr_set_str(&p->attrs, "prompt", "Find File");
1236 attr_set_str(&p->attrs, "done-key",
1237 "File Found:This Window");
1239 call("doc:set-name", p, 0, NULL, "Find File");
1241 p = pane_register(p, 0, &find_handle.c, "file");
1244 attr_set_str(&p->attrs, "initial_path", path);
1245 call("attach-history", p, 0, NULL, "*File History*");
1252 path = file_normalize(ci->focus, ci->str, path);
1257 fd = open(path, O_RDONLY);
1259 p = call_ret(pane, "doc:open", ci->focus, fd, NULL, path);
1262 /* '4' says 'allow create' */
1263 p = call_ret(pane, "doc:open", ci->focus, -1, NULL, path, 4);
1266 asprintf(&m, "Failed to open file: %s", path);
1267 call("Message", ci->focus, 0, NULL, m);
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) {
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");
1282 par = call_ret(pane, "ThisPane", ci->focus);
1289 p = home_call_ret(pane, p, "doc:attach-view", par, 1);
1293 return p ? 1 : Efail;
1296 DEF_CMD(emacs_writefile)
1298 /* Request to write to a different file */
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");
1308 call("Message", ci->focus, 0, NULL,
1309 strconcat(ci->focus, "Document ", doc,
1310 " cannot be written"));
1312 call("Message", ci->focus, 0, NULL,
1313 "This document cannot be written");
1316 path = pane_attr_get(ci->focus, "dirname");
1325 e = buf + strlen(buf);
1326 if (e < buf + sizeof(buf)-1 && e > buf && e[-1] != '/') {
1331 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
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");
1342 attr_set_str(&p->attrs, "initial_path", path);
1343 call("attach-history", p, 0, NULL, "*File History*");
1347 DEF_CMD(emacs_do_writefile)
1349 const char *path = ksuffix(ci, "emacs:write_file:");
1350 if (!ci->str || !path)
1353 path = file_normalize(ci->focus, ci->str, path);
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));
1364 DEF_CMD(emacs_insertfile)
1366 /* Request to insert content of a file at point */
1373 ret = call("doc:insert-file", ci->focus, NO_NUMERIC);
1374 if (ret != Enoarg) {
1377 /* Message already given */
1379 doc = pane_attr_get(ci->focus, "doc-name");
1381 call("Message", ci->focus, 0, NULL,
1382 strconcat(ci->focus, "Document ", doc,
1383 " cannot receive insertions"));
1385 call("Message", ci->focus, 0, NULL,
1386 "This document cannot receive insertions");
1389 path = pane_attr_get(ci->focus, "dirname");
1398 e = buf + strlen(buf);
1399 if (e < buf + sizeof(buf)-1 && e > buf && e[-1] != '/') {
1404 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
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");
1415 attr_set_str(&p->attrs, "initial_path", path);
1416 call("attach-history", p, 0, NULL, "*File History*");
1420 DEF_CMD(emacs_do_insertfile)
1422 const char *path = ksuffix(ci, "emacs:insert_file:");
1425 if (!ci->str || !path)
1428 path = file_normalize(ci->focus, ci->str, path);
1431 fd = open(path, O_RDONLY);
1433 if (call("doc:insert-file", ci->focus, fd) <= 0) {
1435 call("Message", ci->focus, 0, NULL,
1436 strconcat(ci->focus, "Failed to insert ", path));
1442 char *m = strconcat(ci->focus,
1443 "Failed to inser file: ", path);
1444 call("Message", ci->focus, 0, NULL, m);
1449 REDEF_CMD(emacs_file_complete)
1451 /* Extract a directory name and a basename from the document.
1452 * Find a document for the directory and attach as a completing
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;
1468 st = mark_dup(ci->mark);
1469 call("doc:file", ci->focus, -1, st);
1471 str = call_ret(strsave, "doc:get-str", ci->focus, 0, st, NULL,
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
1485 d = str + strlen(str);
1488 (strchr(":=", d[-1]) && d[0] == '/')))
1491 d = file_normalize(ci->focus, d, initial);
1492 b = strrchr(d, '/');
1495 d = strnsave(ci->focus, d, b-d);
1498 d = strsave(ci->focus, ".");
1500 fd = open(d, O_DIRECTORY|O_RDONLY);
1502 call("Message:modal", ci->focus, 0, NULL,
1503 strconcat(ci->focus, "Cannot open directory \"", d, "\""));
1506 /* 32 means quiet */
1507 docp = call_ret(pane, "doc:open", ci->focus, fd, NULL, d, 32);
1511 pop = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM1r");
1514 par = home_call_ret(pane, docp, "doc:attach-view", pop,
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");
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
1533 start = mark_dup(ci->mark);
1534 call("doc:char", ci->focus, -strlen(b), start);
1535 call("Replace", ci->focus, 1, start);
1541 /* Replace 'b' with the result. */
1544 start = mark_dup(ci->mark);
1545 call("doc:char", ci->focus, -strlen(b), start);
1546 call("Replace", ci->focus, 1, start, cr.s);
1549 call("Message:modal", ci->focus, 0, NULL,
1550 strconcat(ci->focus, "No completion found for \"", b, "\"",
1551 " in \"", d, "\""));
1553 /* Now need to close the popup */
1558 DEF_CMD(emacs_finddoc)
1560 struct pane *p, *par;
1562 if (ksuffix(ci, "Doc Found:")[0] == 0) {
1564 char *defname = NULL;
1566 dflt = call_ret(pane, "docs:choose", ci->focus);
1568 defname = pane_attr_get(dflt, "doc-name");
1570 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
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",
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");
1588 attr_set_str(&p->attrs, "prompt", "Find Document");
1589 attr_set_str(&p->attrs, "done-key",
1590 "Doc Found:This Window");
1592 call("doc:set-name", p, 0, NULL, "Find Document", -1);
1594 pane_register(p, 0, &find_handle.c, "doc");
1602 p = call_ret(pane, "docs:byname", ci->focus, 0, NULL, ci->str);
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) {
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");
1617 par = call_ret(pane, "ThisPane", ci->focus);
1621 p = home_call_ret(pane, p, "doc:attach-view", par, 1);
1624 return p ? 1 : Efail;
1627 REDEF_CMD(emacs_doc_complete)
1629 /* Extract a document name from the document.
1630 * Attach the 'docs' document as a completing popup menu
1633 struct pane *pop, *p = NULL;
1634 struct call_return cr;
1639 str = call_ret(strsave, "doc:get-str", ci->focus);
1642 pop = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM1r");
1645 p = call_ret(pane, "docs:complete", pop);
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 */
1653 start = mark_dup(ci->mark);
1654 call("doc:set-ref", ci->focus, 1, start);
1656 call("Replace", ci->focus, 1, start);
1661 /* Replace the prefix with the new value */
1664 start = mark_dup(ci->mark);
1665 call("doc:set-ref", ci->focus, 1, start);
1667 call("Replace", ci->focus, 1, start, cr.s);
1670 /* Now need to close the popup */
1675 DEF_CMD(emacs_viewdocs)
1677 struct pane *p, *par;
1680 docs = call_ret(pane, "docs:byname", ci->focus, 0, NULL, "*Documents*");
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) {
1693 par = call_ret(pane, "OtherPane", ci->focus);
1695 par = call_ret(pane, "ThisPane", ci->focus);
1701 p = home_call_ret(pane, docs, "doc:attach-view", par, 1);
1707 struct pane *doc safe;
1711 DEF_CMD(choose_pane)
1713 struct pcb *cb = container_of(ci->comm, struct pcb, c);
1716 if (cb->p || !ci->str)
1718 d = call_ret(pane, "docs:byname", ci->focus, 0, NULL, ci->str);
1720 cb->p = home_call_ret(pane, ci->focus, "DocPane", d);
1722 home_call(cb->doc, "doc:attach-view", cb->p, 1);
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.
1734 if (!pane_has_focus(ci->home))
1735 /* call back when lines or eof */
1738 if (strcmp(ci->key, "cb:eof") != 0) {
1740 if (ci->str && strchr(ci->str, 'P'))
1741 par = call_ret(pane, "PopupTile", ci->home, 0, NULL, "MD3tsa");
1743 par = call_ret(pane, "OtherPane", ci->home);
1745 home_call(ci->focus, "doc:attach-view", par, 1);
1748 str = call_ret(str, "doc:get-str", ci->focus);
1749 if (!str || !*str) {
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);
1756 asprintf(&str, "(shell command completed with no output (signalled %d))", -ci->num);
1758 call("Message", ci->home, 0, NULL, str);
1763 DEF_CB(shell_insert_cb)
1765 char *str = call_ret(str, "doc:get-str", ci->focus);
1766 struct mark *mk = call_ret(mark2, "doc:point", ci->home);
1768 if (clear_selection(ci->home, NULL, mk, 3))
1769 call("Replace", ci->home, 1, mk);
1770 call("Replace", ci->home, 0, NULL, str);
1775 DEF_CMD(emacs_shell)
1777 char *name = "*Shell Command Output*";
1778 struct pane *p, *doc, *par, *sc;
1779 char *path, *input = NULL;
1781 bool interpolate, pipe, popup;
1783 if (strcmp(ci->key, "Shell Command") != 0) {
1787 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2", 0, NULL,
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
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*");
1805 p = pane_register(p, 0, &find_handle.c, "shellcmd");
1809 comm_call(ci->comm2, "cb", p);
1813 call("Message", ci->focus, 0, NULL, "Shell command aborted");
1817 interpolate = ci->str2 && strchr(ci->str2, 'i');
1818 pipe = ci->str2 && strchr(ci->str2, 'p');
1819 popup = ci->str2 && strchr(ci->str2, 'P');
1822 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
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);
1830 doc = call_ret(pane, "doc:from-text", ci->focus, 0, NULL, name, 0, NULL,
1836 path = pane_attr_get(ci->focus, "dirname");
1837 attr_set_str(&doc->attrs, "dirname", path);
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.
1844 sc = call_ret(pane, "attach-shellcmd", doc, pipe ? 22 : 4,
1845 NULL, ci->str, 0, NULL, path);
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");
1853 /* Close old shell docs, but if one is visible in current frame, replace
1859 call_comm("editor:notify:shell-reuse", ci->focus, &cb.c);
1861 /* choose_pane didn't attach, so set a callback to do
1862 * it when there is enough content.
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.
1870 home_call_comm(sc, "shellcmd:set-callback",
1871 ci->focus, &shellcb,
1872 500, NULL, popup ? "P": "",
1876 par = call_ret(pane, "PopupTile", ci->focus,
1879 par = call_ret(pane, "OtherPane", ci->focus);
1882 home_call(doc, "doc:attach-view", par, 1);
1885 if (sc && interpolate)
1886 /* Need a callback when the pipe command finished */
1887 home_call_comm(sc, "shellcmd:set-callback", ci->focus,
1896 const char *last = ksuffix(ci, "K:A-");
1903 if (rpt == NO_NUMERIC)
1906 rpt = rpt * 10 + *last - '0';
1908 call("Mode:set-num", ci->focus, neg ? -rpt : rpt);
1909 call("Mode:set-num2", ci->focus, ci->num2);
1915 call("Mode:set-num", ci->focus, - ci->num);
1916 call("Mode:set-num2", ci->focus, ci->num2);
1920 DEF_CMD(emacs_prefix)
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.
1926 call("Mode:set-num", ci->focus, 4);
1930 DEF_CMD(emacs_kill_doc)
1932 if (ci->num <= 0 || ci->num == NO_NUMERIC) {
1933 /* Check if modified. */
1934 char *m = pane_attr_get(ci->focus, "doc-modified");
1936 if (m && strcmp(m, "yes") == 0)
1937 f = pane_attr_get(ci->focus, "filename");
1939 call("Message:modal", ci->focus, 0, NULL,
1940 "Document is modified - please save or give prefix arg");
1944 return call("doc:destroy", ci->focus);
1947 DEF_CMD(emacs_revisit)
1949 return call("doc:load-file", ci->focus, 2, NULL, NULL, -1);
1952 DEF_CMD(emacs_save_all)
1954 int ret = call("docs:save-all", ci->focus, 0, NULL, NULL, 1);
1957 call("Message", ci->focus, 0, NULL, "No files need to be saved.");
1960 if (ci->num == NO_NUMERIC) {
1961 struct pane *p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM");
1963 return call("docs:show-modified", p);
1965 return call("docs:save-all", ci->focus);
1968 DEF_CMD(emacs_reposition)
1971 int repoint = attr_find_int(ci->focus->attrs, "emacs-repoint");
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
1978 m = mark_at_point(ci->focus, NULL, MARK_UNGROUPED);
1980 struct mark *m2 = mark_dup(m);
1981 call("Move-CursorXY", ci->focus, 0, m, NULL,
1983 INT_MAX, repoint < 0 ? ci->focus->h-1 : 0);
1985 /* can only move point backwards */
1986 if (m->seq < m2->seq)
1987 call("Move-to", ci->focus, 0, m);
1989 /* can only move point forwards */
1990 if (m->seq > m2->seq)
1991 call("Move-to", ci->focus, 0, m);
1995 attr_set_str(&ci->focus->attrs, "emacs-repoint", NULL);
1997 return Efallthrough;
2000 DEF_CMD(emacs_start_search)
2002 struct pane *p = NULL, *hp;
2005 hp = call_ret(pane, "attach-emacs-search-highlight", ci->focus);
2008 p = call_ret(pane, "PopupTile", hp, 0, NULL, "TR2",
2013 home_call(hp, "highlight:set-popup", p);
2015 attr_set_str(&p->attrs, "prompt", "Search");
2016 attr_set_str(&p->attrs, "done-key", "Search String");
2018 hp = call_ret(pane, "attach-history", p, 0, NULL, "*Search History*");
2022 call("doc:set-name", p, 0, NULL, "Search", -1);
2023 if (strcmp(ci->key, "K:C-R") == 0)
2025 if (strcmp(ci->key, "K:A-%") == 0)
2027 call_ret(pane, "attach-emacs-search", p, mode);
2032 DEF_CMD(emacs_command)
2036 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2", 0, NULL, "");
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*");
2044 pane_register(p, 0, &find_handle.c, "cmd");
2048 DEF_CMD(emacs_do_command)
2056 asprintf(&cmd, "interactive-cmd-%s", ci->str);
2059 ret = call(cmd, ci->focus, 0, ci->mark);
2060 free(cmd); cmd = NULL;
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);
2074 struct call_return *cr = container_of(ci->comm, struct call_return, c);
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);
2088 REDEF_CMD(emacs_cmd_complete)
2091 struct pane *doc = NULL, *pop = NULL, *p;
2092 struct call_return cr;
2096 s = call_ret(strsave, "doc:get-str", ci->focus);
2099 doc = call_ret(pane, "attach-doc-list", ci->focus);
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");
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");
2115 p = home_call_ret(pane, cr.p, "doc:attach-view", pop, -1, NULL, "simple");
2118 attr_set_str(&p->attrs, "done-key", "Replace");
2119 p = call_ret(pane, "attach-render-complete", p);
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
2127 struct mark *start = mark_dup(ci->mark);
2128 call("Move-Char", ci->focus, -strlen(s), start);
2129 call("Replace", ci->focus, 1, start);
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);
2140 /* Now need to close the popup */
2152 DEF_CMD(emacs_version)
2156 asprintf(&v, "%s ; emacs-mode - v" VERSION " - " VERS_DATE, edlib_version);
2158 call("Message", ci->focus, 0, NULL, v);
2165 /* View the debug log */
2166 struct pane *p, *doc;
2168 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL, "*Debug Log*");
2170 call("interactive-cmd-view-log", ci->focus);
2171 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL,
2176 p = call_ret(pane, "ThisPane", ci->focus);
2178 home_call(doc, "doc:attach-view", p, 1);
2184 struct mark *m = call_ret(mark2, "doc:point", ci->focus);
2186 clear_selection(ci->focus, NULL, m, 0);
2188 call("Move-to", ci->focus, 1);
2189 m = call_ret(mark2, "doc:point", ci->focus);
2191 /* ci->num == 1 means replacable */
2192 set_selection(ci->focus, NULL, m, ci->num == 1 ? 3 : 1);
2196 DEF_CMD(emacs_abort)
2198 /* On abort, forget mark */
2199 struct mark *m = call_ret(mark2, "doc:point", ci->focus);
2201 clear_selection(ci->focus, NULL, m, 0);
2202 return Efallthrough;
2205 DEF_CMD(emacs_swap_mark)
2207 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
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);
2222 /* Delete text from point to mark */
2223 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
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);
2233 str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, mk);
2235 call("copy:save", ci->focus, 0, NULL, str);
2236 ret = call("Replace", ci->focus, 1, mk);
2238 clear_selection(ci->focus, NULL, mk, 0);
2245 /* copy text from point to mark */
2246 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
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);
2255 str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, mk);
2257 call("copy:save", ci->focus, 0, NULL, str);
2258 /* Clear current highlight */
2259 clear_selection(ci->focus, NULL, mk, 0);
2265 int n = RPT_NUM(ci);
2268 struct mark *m = NULL;
2270 /* If there is a selection elsewhere, we want to commit it */
2271 call("selection:discard", ci->focus);
2272 call("selection:commit", ci->focus);
2274 str = call_ret(strsave, "copy:get", ci->focus, n - 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);
2283 call("copy:save", ci->focus, 0, NULL, str2);
2287 call("Move-to", ci->focus, 1);
2288 call("Replace", ci->focus, 1, m, str);
2290 mk = call_ret(mark2, "doc:point", ci->focus);
2291 call("Mode:set-num2", ci->focus, N2_yank);
2295 DEF_CMD(emacs_yank_pop)
2297 struct mark *mk, *m;
2301 if (N2(ci) != N2_yank)
2303 mk = call_ret(mark2, "doc:point", ci->focus);
2307 str = call_ret(strsave, "copy:get", ci->focus, num);
2310 str = call_ret(strsave, "copy:get", ci->focus, num);
2316 call("Replace", ci->focus, 1, mk, str);
2317 call("Move-to", ci->focus, 1, m);
2319 call("Mode:set-num2", ci->focus, (num << 16) | N2_yank);
2323 DEF_CMD(emacs_attrs)
2325 struct call_return cr;
2327 char *selection = "bg:white-80,vis-nl,menu-at-mouse,action-menu:emacs:selection-menu"; // grey
2332 cr = call_ret(all, "doc:point", ci->focus);
2333 if (cr.ret <= 0 || !cr.m || !cr.m2 || !ci->mark)
2335 active = attr_find_int(cr.m2->attrs, "emacs:active");
2338 if (active >= 3) /* replacable */
2339 selection = "bg:red+80,vis-nl"; // pink
2340 if (mark_same(cr.m, cr.m2))
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);
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);
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);
2366 return Efallthrough;
2369 DEF_CMD(emacs_selection_menu)
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);
2378 call("global-multicall-selection-menu:add-", p);
2379 call("menu-add", p, 0, NULL, "de-select", 0, NULL, ":ESC");
2383 DEF_CMD(emacs_selection_menu_action)
2385 struct pane *home = ci->home;
2386 const char *c = ci->str;
2391 /* command for focus */
2392 call(c+1, ci->focus, 0, ci->mark);
2396 call("Keystroke-sequence", home, 0, NULL, c);
2400 DEF_CMD(emacs_selection_menu_add)
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");
2409 DEF_CMD(emacs_goto_line)
2411 if (ci->num == NO_NUMERIC)
2413 call("CountLines", ci->focus, ci->num, ci->mark, "goto:line");
2414 call("Move-View-Pos", ci->focus, 0, ci->mark);
2418 DEF_CMD(emacs_next_match)
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);
2426 DEF_CMD(emacs_match_again)
2428 if (N2(ci) != N2_match)
2429 return emacs_insert_func(ci);
2431 return emacs_next_match_func(ci);
2436 call("interactive-cmd-make", ci->focus,
2437 ci->num, ci->mark, NULL,
2438 strcmp(ci->key, "K:CC:C-M") == 0);
2442 static void update_sel(struct pane *p safe,
2443 struct mark *pt safe, struct mark *m2 safe,
2446 struct mark *mfirst, *mlast;
2449 call("Move-to", p, 1, m2);
2450 mk = call_ret(mark2, "doc:point", p);
2454 type = attr_find(m2->attrs, "emacs:selection-type");
2456 attr_set_str(&m2->attrs, "emacs:selection-type", type);
2458 if (type && strcmp(type, "char") != 0) {
2460 if (pt->seq < mk->seq) {
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))
2477 call("doc:word", p, 1, mlast);
2479 call("doc:EOL", p, -1, mfirst);
2480 /* Include trailing newline */
2481 call("doc:EOL", p, 1, mlast, NULL, 1);
2485 /* Don't set selection until range is non-empty, else we
2486 * might clear some other selection too early.
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);
2495 DEF_CMD(emacs_press)
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.
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);
2511 /* Not in document, not my problem */
2513 return Efallthrough;
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);
2518 clear_selection(ci->focus, pt, mk, 0);
2519 call("Move-to", ci->focus, 0, m);
2520 pane_focus(ci->focus);
2522 if (m2 && strcmp(ci->key, "M:DPress-1") == 0) {
2523 type = attr_find(m2->attrs, "emacs:selection-type");
2526 else if (strcmp(type, "char") == 0)
2528 else if (strcmp(type, "word") == 0)
2534 /* Record start of selection */
2535 call("Move-to", ci->focus, 2, m);
2536 m2 = call_ret(mark2, "doc:point", ci->focus, 2);
2538 attr_set_str(&m2->attrs, "emacs:selection-type", type);
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);
2551 DEF_CMD(emacs_release)
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);
2561 if (!p || !m2 || !m) {
2562 /* Not in a document or no selection start - not my problem */
2564 return Efallthrough;
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);
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) {
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
2585 call("Move-to", ci->focus, 0, m2);
2586 update_sel(ci->focus, p, m2, NULL);
2588 clear_selection(ci->focus, p, mk, 0);
2595 DEF_CMD(emacs_menu_open)
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);
2602 ret = call("Move-CursorXY", ci->focus, 0, m, "menu",
2603 0, NULL, NULL, ci->x, ci->y);
2608 DEF_CMD(emacs_menu_select)
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
2614 struct mark *m = mark_new(ci->focus);
2617 ret = call("Move-CursorXY", ci->focus, 0, m, "activate",
2618 0, NULL, NULL, ci->x, ci->y);
2623 DEF_CMD(emacs_motion)
2625 struct mark *p = call_ret(mark, "doc:point", ci->focus);
2626 struct mark *m2 = call_ret(mark2, "doc:point", ci->focus, 2);
2631 if (attr_find_int(m2->attrs, "emacs:track-selection") <= 0)
2632 return Efallthrough;
2634 call("Move-CursorXY", ci->focus,
2635 0, NULL, NULL, 0, NULL, NULL, ci->x, ci->y);
2637 update_sel(ci->focus, p, m2, NULL);
2641 DEF_CMD(emacs_paste)
2645 /* First commit the selection, then collect it */
2646 call("selection:commit", ci->focus);
2647 str = call_ret(strsave, "copy:get", ci->focus);
2649 call("Move-CursorXY", ci->focus,
2650 0, NULL, NULL, 0, NULL, NULL, ci->x, ci->y);
2655 call("Replace", ci->focus, 0, NULL, str);
2657 pane_focus(ci->focus);
2662 DEF_CMD(emacs_paste_direct)
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.
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);
2675 s = call_ret(str, "Paste:get", ci->focus);
2677 struct mark *pt = call_ret(mark, "doc:point", ci->focus);
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);
2688 DEF_CMD(emacs_sel_claimed)
2690 /* Should possibly just change the color of our selection */
2691 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2693 clear_selection(ci->focus, NULL, mk, 0);
2697 DEF_CMD(emacs_sel_commit)
2699 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2700 struct mark *p = call_ret(mark, "doc:point", ci->focus);
2702 if (p && mk && !mark_same(p, mk)) {
2705 str = call_ret(strsave, "doc:get-str", ci->focus,
2709 call("copy:save", ci->focus, 0, NULL, str);
2715 DEF_CMD(emacs_readonly)
2719 ro = pane_attr_get(ci->focus, "doc-readonly");
2721 if (ro && strcmp(ro,"yes") == 0)
2722 call("doc:set:readonly", ci->focus, 0);
2724 call("doc:set:readonly", ci->focus, 1);
2728 DEF_CMD(emacs_shift)
2733 shift = pane_attr_get_int(ci->focus, "render-wrap", -1);
2734 if (strcmp(ci->key, "K:CX->") == 0 || strcmp(ci->key, "K->") == 0)
2736 if (rpt == NO_NUMERIC) {
2741 } else if (rpt == -NO_NUMERIC) {
2748 } else if (rpt >= 0) {
2753 if (shift > 0 && shift + rpt < 0)
2759 attr_set_str(&ci->focus->attrs, "render-wrap", "yes");
2761 attr_set_int(&ci->focus->attrs, "render-wrap", shift);
2763 attr_set_str(&ci->focus->attrs, "render-wrap", "0 auto");
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");
2773 DEF_CMD(emacs_shift_again)
2775 if (N2(ci) != N2_shift)
2776 return emacs_insert_func(ci);
2778 return emacs_shift_func(ci);
2781 DEF_CMD(emacs_growx)
2783 if (ci->key[strlen(ci->key)-1] == '}')
2784 call("Window:x+", ci->focus, ci->num);
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");
2792 DEF_CMD(emacs_growx_again)
2794 if (N2(ci) != N2_growx)
2795 return emacs_insert_func(ci);
2797 return emacs_growx_func(ci);
2800 DEF_CMD(emacs_scale_relative)
2802 struct pane *p = ci->focus;
2803 char *sc = pane_attr_get(p, "scale:M");
2808 call("Message:modal", p, 0, NULL,
2809 "Cannot zoom display with fixed-sized font");
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");
2823 if (ci->key[strlen(ci->key)-1] == '-')
2824 scale = 10 * scale / 12;
2826 scale = 12 * scale / 10;
2827 snprintf(num, sizeof(num)-1, "%d", scale);
2828 call("Display:set:scale", p, 0, NULL, num);
2832 DEF_CMD(emacs_curs_pos)
2842 c = mark_dup(ci->mark);
2843 nxt = doc_following(ci->focus, c);
2845 while ((ch = doc_prev(ci->focus, c)) != WEOF && !is_eol(ch))
2847 while (mark_ordered_not_same(c, ci->mark)) {
2848 ch = doc_next(ci->focus, c);
2852 } else if (ch == '\t') {
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);
2868 DEF_CMD(emacs_word_count)
2871 struct pane *p = ci->focus;
2878 mk = call_ret(mark2, "doc:point", p);
2879 if (mk && attr_find_int(mk->attrs, "emacs:active") <= 0)
2881 call("CountLines", p, 0, ci->mark);
2882 wp = attr_find_int(ci->mark->attrs, "word");
2885 call("CountLines", p, 0, mk);
2886 wm = attr_find_int(mk->attrs, "word");
2887 asprintf(&msg, "%d words in region", abs(wp-wm));
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);
2892 call("Message", p, 0, NULL, msg);
2899 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2900 struct mark *p = call_ret(mark, "doc:point", ci->focus);
2903 if (!clear_selection(ci->focus, p, mk, 0))
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);
2911 call("fill-paragraph", p2, ci->num, p, NULL,
2915 /* Don't try to load anything, the file-type should
2916 * have loaded something if relevant
2918 if (call("reindent-paragraph", ci->focus, ci->num, p, NULL,
2920 call("Message", ci->focus, 0, NULL,
2921 "Reindent not supported on the document.");
2926 DEF_CMD(emacs_abbrev)
2928 call("attach-abbrev", ci->focus);
2932 DEF_CMD(emacs_showinput)
2934 struct pane *p, *doc;
2936 if (call("input:log", ci->focus) <= 0) {
2937 call("Message", ci->focus, 0, NULL,
2938 "Cannot get log of recent input.");
2942 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL, "*Debug Log*");
2944 call("interactive-cmd-view-log", ci->focus);
2945 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL,
2950 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM");
2952 p = home_call_ret(pane, doc, "doc:attach-view", p, 1);
2954 call("doc:file", p, 1);
2959 DEF_CMD(emacs_macro_start)
2963 ret = call("macro:capture", ci->focus);
2965 call("Message", ci->focus, 0, NULL,
2966 "Macro capture already happening");
2968 call("Message", ci->focus, 0, NULL,
2969 "Macro facility not available");
2973 DEF_CMD(emacs_macro_stop)
2977 ret = call("macro:finished", ci->focus, 2);
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.");
2985 call("Message", ci->focus, 0, NULL,
2986 "Failure creating macro.");
2990 DEF_CMD(emacs_macro_run)
2992 int cnt = RPT_NUM(ci);
2994 if (strcmp(ci->key, "K-e") == 0 && N2(ci) != N2_runmacro)
2995 return emacs_insert_func(ci);
3000 call("macro:replay", ci->focus, 1) > 0)
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;
3014 static const char spell_choices[] =
3015 "0123456789-=;,/ABCDEFGHIJKLMNOPQRSTUVWXYZ";
3016 DEF_CB(get_suggestion)
3018 struct bb *b = container_of(ci->comm, struct bb, c);
3024 buf_concat(&b->b, " - (a)ccept, (i)nsert - ");
3026 buf_concat(&b->b, ", ");
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, ')');
3034 buf_concat(&b->b, ci->str);
3038 DEF_CMD(emacs_spell)
3043 int rpt = RPT_NUM(ci);
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
3052 doc_prev(ci->focus, ci->mark);
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) {
3061 call("Message", ci->focus, 0, NULL,
3062 "Spell check reached end-of-file");
3063 call("Spell:Save", ci->focus);
3068 ret = call("Spell:Check", ci->focus, 0, NULL, word);
3074 call("Message", ci->focus, 0, NULL,
3075 strconcat(ci->focus, "\"", word,
3076 "\" is a correct spelling."));
3077 } else if (ret == Efalse) {
3080 buf_concat(&b.b, "\"");
3081 buf_concat(&b.b, word);
3082 buf_concat(&b.b, "\" is NOT correct");
3085 b.c = get_suggestion;
3086 call_comm("Spell:Suggest", ci->focus, &b.c,
3089 buf_concat(&b.b, " ... no suggestions");
3091 attr_set_str(&ci->focus->attrs, "spell:last-error",
3093 attr_set_str(&ci->focus->attrs, "spell:suggestions",
3095 attr_set_int(&ci->focus->attrs, "spell:offset", 0);
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) {
3105 call("Message", ci->focus, 0, NULL,
3106 strconcat(ci->focus, "\"", word,
3107 "\" is not a word."));
3109 call("Message", ci->focus, 0, NULL,
3110 strconcat(ci->focus, "Spell check failed for \"", word,
3116 DEF_CMD(emacs_spell_choose)
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");
3128 if (!*k || !suggest || !last || !ci->mark)
3131 cp = strstr(suggest, match);
3135 ep = strchr(cp, ',');
3137 cp = strnsave(ci->focus, cp, ep-cp);
3139 m = mark_dup(ci->mark);
3140 i = utf8_strlen(last);
3142 doc_prev(ci->focus, m);
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);
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);
3159 DEF_CMD(emacs_spell_abort)
3164 DEF_CMD(emacs_spell_skip)
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);
3175 DEF_CMD(emacs_spell_insert)
3177 char *last = attr_find(ci->focus->attrs,
3178 "spell:last-error");
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);
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);
3194 DEF_CMD(emacs_spell_accept)
3196 char *last = attr_find(ci->focus->attrs,
3197 "spell:last-error");
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);
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);
3213 static int spell_shift(struct pane *focus safe, int num, int inc)
3215 int o = pane_attr_get_int(focus, "spell:offset", 0);
3221 msg = pane_attr_get(focus, "spell:suggestions");
3224 for (i = 0; i < o && (c = strchr(msg, ',')) != NULL; i++)
3226 attr_set_int(&focus->attrs, "spell:offset", i);
3228 call("Mode:set-all", focus, num, NULL, ":Spell");
3229 call("Message:modal", focus, 0, NULL, msg);
3233 DEF_CMD(emacs_spell_left)
3235 return spell_shift(ci->focus, ci->num, -1);
3238 DEF_CMD(emacs_spell_right)
3240 return spell_shift(ci->focus, ci->num, 1);
3243 DEF_CMD(emacs_quote)
3248 struct mark *mk = NULL;
3249 bool free_mark = False;
3251 if (ci->num >= 0 && ci->num < NO_NUMERIC)
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;
3257 if (ci->num < 0 && 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) {
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);
3273 call("Message", ci->focus, 0, NULL,
3274 strconcat(ci->focus,
3275 "Cannot find another character <", str, ">"));
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) {
3287 x = strtoul(str, &ep, 16);
3288 if (ep && *ep == 0) {
3290 call("Message", ci->focus, 0, NULL,
3291 strconcat(ci->focus, "Hex code 0x", str));
3293 struct call_return cr;
3294 cr = call_ret(all, "Unicode-names", ci->focus,
3298 call("Message", ci->focus, 0, NULL,
3299 strconcat(ci->focus,
3300 "Unicode char <", cr.s, ">"));
3302 attr_set_str(&ci->mark->attrs,
3303 "emacs:unicode_char",
3305 call("Mode:set-num2", ci->focus,
3306 N2_uniquote | (1 << 16));
3309 call("Message", ci->focus, 0, NULL,
3310 strconcat(ci->focus,
3311 "Cannot find character <", str, ">"));
3317 call("Mode:set-all", ci->focus, ci->num, NULL, ":CQ", ci->num2);
3320 call("Replace", ci->focus, 0, mk, put_utf8(b, wch));
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");
3333 static void emacs_init(void)
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);
3351 key_add(m, "K:C-Q", &emacs_quote);
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);
3357 key_add(m, mc->k2, &mc->cmd);
3359 key_add(m, mc->k3, &mc->cmd);
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);
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);
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);
3381 key_add(m, "K:C-L", &emacs_recenter);
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);
3389 key_add(m, "K:CX:C-W", &emacs_writefile);
3390 key_add_prefix(m, "emacs:write_file:", &emacs_do_writefile);
3392 key_add(m, "K:CX-i", &emacs_insertfile);
3393 key_add_prefix(m, "emacs:insert_file:", &emacs_do_insertfile);
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);
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);
3404 key_add(m, "K:CX-k", &emacs_kill_doc);
3406 key_add(m, "K:CX-s", &emacs_save_all);
3408 key_add(m, "K:CX:C-V", &emacs_revisit);
3410 key_add(m, "K:CX-=", &emacs_curs_pos);
3411 key_add(m, "K:A-=", &emacs_word_count);
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);
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);
3423 key_add(m, "K:CX:C-=", &emacs_scale_relative);
3424 key_add(m, "K:CX:C--", &emacs_scale_relative);
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);
3431 key_add(m, "K:CX:C-C", &emacs_exit);
3432 key_add(m, "emacs:deactivate", &emacs_deactivate);
3434 key_add(m, "K:C-U", &emacs_prefix);
3436 key_add(m, "K:A-!", &emacs_shell);
3437 key_add(m, "K:A-|", &emacs_shell);
3438 key_add(m, "Shell Command", &emacs_shell);
3440 key_add(m, "K:CX-`", &emacs_next_match);
3441 key_add(m, "K-`", &emacs_match_again);
3443 key_add(m, "K:CX-1", &emacs_close_others);
3444 key_add(m, "K-1", &emacs_close_others);
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);
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);
3468 key_add(m, "K:A:C-V", &emacs_move_view_other);
3470 key_add(m, "K:CX:C-Q", &emacs_readonly);
3472 key_add_prefix(m, "K:CQ-", &emacs_quote_insert);
3473 key_add_prefix(m, "K:CQ:C-", &emacs_quote_insert);
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);
3491 key_add(m, "K:Help-l", &emacs_showinput);
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);
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);
3508 key_add(m, "Notify:selection:claimed", &emacs_sel_claimed);
3509 key_add(m, "Notify:selection:commit", &emacs_sel_commit);
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);
3519 DEF_LOOKUP_CMD(mode_emacs, emacs_map);
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"},
3529 DEF_CMD(attach_mode_emacs)
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,
3537 0, NULL, menus[i][1]);
3541 DEF_CMD(attach_file_entry)
3543 /* The 'type' passed must be static, not allocated */
3544 char *type = "shellcmd";
3546 if (ci->str && strcmp(ci->str, "file") == 0)
3548 else if (ci->str && strcmp(ci->str, "doc") == 0)
3550 pane_register(ci->focus, 0, &find_handle.c, type);
3555 void edlib_init(struct pane *ed safe)
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");