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);
402 struct mark *start = NULL;
403 int cnt = mv->direction * RPT_NUM(ci);
416 start = mark_dup(ci->mark);
420 struct mark *as, *ae, *bs, *be;
423 call(mv->type, ci->focus, -dir, ci->mark);
424 as = mark_dup(ci->mark);
425 call(mv->type, ci->focus, dir, ci->mark);
426 ae = mark_dup(ci->mark);
427 /* as to ae is the object to be moved. */
429 call(mv->type, ci->focus, dir, ci->mark);
430 be = mark_dup(ci->mark);
431 call(mv->type, ci->focus, -dir, ci->mark);
432 bs = mark_dup(ci->mark);
433 /* bs to be is the object to be swapped with.
434 * bs must be after ae in the direction
436 if (mark_same(as, ae) ||
438 bs->seq - ae->seq * dir < 0) {
439 mark_to_mark(ci->mark, ae);
446 astr = call_ret(str, "doc:get-str", ci->focus,
449 bstr = call_ret(str, "doc:get-str", ci->focus,
452 mark_to_mark(ci->mark, ae);
453 call("Replace", ci->focus, 1, as, bstr);
454 mark_to_mark(ci->mark, be);
455 call("Replace", ci->focus, 1, bs, astr, 1);
457 call(mv->type, ci->focus, dir, ci->mark);
466 /* When moving forward, move point. When backward, leave point alone */
468 mark_to_mark(ci->mark, start);
474 DEF_CMD(emacs_move_view_other)
476 /* If there is an 'other' pane', Send "K:Next" there */
479 /* '512' means 'fail if no other pane' */
480 p = call_ret(pane, "OtherPane", ci->focus, 512);
483 call("Mode:set-num", p, ci->num);
484 call("Keystroke", p, 0, NULL, "emacs-move-large-other");
488 DEF_CMD(emacs_recenter)
491 if (ci->num == NO_NUMERIC && N2(ci) == N2_recentre) {
492 /* Repeated command - go to top, or bottom, or middle in order */
495 case 0: /* was center, go to top */
496 call("Move-View-Line", ci->focus, 1, ci->mark);
499 case 1: /* was top, go to bottom */
500 call("Move-View-Line", ci->focus, -1, ci->mark);
503 case 2: /* was bottom, go to middle */
504 call("Move-View-Line", ci->focus, 0, ci->mark);
508 } else if (ci->num == -NO_NUMERIC) {
509 /* Move point to bottom */
510 call("Move-View-Line", ci->focus, -1, ci->mark);
512 } else if (ci->num != NO_NUMERIC) {
513 /* Move point to display line N */
514 call("Move-View-Line", ci->focus, ci->num, ci->mark);
516 /* Move point to middle and refresh */
517 call("Move-View-Line", ci->focus, 0, ci->mark);
518 call("window:refresh", ci->focus);
520 call("Mode:set-num2", ci->focus, N2_recentre | (step << 16));
524 REDEF_CMD(emacs_simple);
525 REDEF_CMD(emacs_simple_num);
526 REDEF_CMD(emacs_simple_str);
527 static struct simple_command {
531 } simple_commands[] = {
532 {CMD(emacs_simple), "Window:next", "K:CX-o"},
533 {CMD(emacs_simple), "Window:prev", "K:CX-O"},
534 {CMD(emacs_simple), "Window:y+", "K:CX-^"},
535 {CMD(emacs_simple), "Window:split-y", "K:CX-2"},
536 {CMD(emacs_simple), "Window:split-x", "K:CX-3"},
537 {CMD(emacs_simple), "Window:close", "K:CX-0"},
538 {CMD(emacs_simple), "Window:bury", "K:A-B"},
539 {CMD(emacs_simple), "window:new", "K:CX5-2"},
540 {CMD(emacs_simple), "window:close", "K:CX5-0"},
541 {CMD(emacs_simple), "lib-server:done", "K:CX-#"},
542 {CMD(emacs_simple), "mode-swap-mark", "K:CX:C-X"},
543 {CMD(emacs_simple), "Abort", "K:C-G"},
544 {CMD(emacs_simple), "Cancel", "K:ESC"},
545 {CMD(emacs_simple), "NOP", "K:A-G"},
546 {CMD(emacs_simple), "NOP", "K:CX:C-G"},
547 {CMD(emacs_simple), "NOP", "K:CX4:C-G"},
548 {CMD(emacs_simple), "doc:save-file", "K:CX:C-S"},
549 {CMD(emacs_simple), "Commit", "K:CC:C-C"},
550 /* one day, this will be "find definition", now it is same as "find any" */
551 {CMD(emacs_simple_num), "interactive-cmd-git-grep", "K:CX:A-."},
552 {CMD(emacs_simple_str), "interactive-cmd-git-grep", "K:A-."},
553 {CMD(emacs_simple), "interactive-cmd-merge-mode", "K:A-m"},
554 {CMD(emacs_simple_str), "interactive-cmd-calc-replace", "K:A-#"},
557 REDEF_CMD(emacs_simple)
559 struct simple_command *sc = container_of(ci->comm, struct simple_command, cmd);
564 return call(sc->type, ci->focus, ci->num, ci->mark);
567 REDEF_CMD(emacs_simple_num)
569 struct simple_command *sc = container_of(ci->comm, struct simple_command, cmd);
574 return call(sc->type, ci->focus, RPT_NUM(ci), ci->mark);
577 REDEF_CMD(emacs_simple_str)
579 struct simple_command *sc = container_of(ci->comm, struct simple_command, cmd);
580 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
585 if (clear_selection(ci->focus, NULL, mk, 0))
586 str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, mk);
588 return call(sc->type, ci->focus, ci->num, ci->mark, str, 0, mk);
591 REDEF_CMD(emacs_insert);
593 DEF_CMD(emacs_close_others)
595 if (strcmp(ci->key, "K-1") == 0 && N2(ci) != N2_close_others)
596 return emacs_insert_func(ci);
598 if (call("Window:close-others", ci->focus) <= 0)
600 call("Mode:set-num2", ci->focus, N2_close_others);
601 call("Message:modal", ci->focus, 0, NULL, "Type 1 to close more");
607 struct call_return *cr = container_of(ci->comm, struct call_return, c);
613 DEF_CMD(emacs_deactivate)
615 /* close-all popup has closed, see if it aborted */
617 call("event:deactivate", ci->focus);
623 struct call_return cr;
627 if (ci->num == NO_NUMERIC) {
630 /* If this is not only display, then refuse to exit */
631 call_comm("editor:notify:all-displays", ci->focus, &cr.c);
633 call("Message", ci->focus, 0, NULL,
634 "Cannot exit when there are multiple windows.");
638 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM");
640 /* Probably called from a pop-up. Abort the popup
641 * and let the user try again.
643 call("Abort", ci->focus);
646 attr_set_str(&p->attrs, "done-key", "emacs:deactivate");
647 call("docs:show-modified", p);
650 call("event:deactivate", ci->focus);
654 DEF_CMD(emacs_insert)
658 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
660 bool first = N2(ci) != N2_undo_insert;
665 if (clear_selection(ci->focus, NULL, mk, 3)) {
666 call("Replace", ci->focus, 1, mk, NULL, !first);
669 clear_selection(ci->focus, NULL, mk, 2);
671 str = ksuffix(ci, "K-");
672 /* Resubmit as doc:char-$str. By default this will be inserted
673 * but panes like lib-viewer might have other plans.
674 * lib-viewer could catch the original "K-", but sometimes
675 * the major mode might not want that.
677 strcat(strcpy(dc, "doc:char-"), str);
678 ret = call(dc, ci->focus, ci->num, ci->mark, NULL, !first);
679 call("Mode:set-num2", ci->focus, N2_undo_insert);
681 return ret < 0 ? ret : 1;
684 DEF_CMD(emacs_quote_insert)
689 bool first = N2(ci) != N2_undo_insert;
694 if (clear_selection(ci->focus, NULL, ci->mark, 3)) {
695 call("Replace", ci->focus, 1, ci->mark, NULL, !first);
698 clear_selection(ci->focus, NULL, ci->mark, 2);
700 str = ksuffix(ci, "K:CQ-");
702 str = ksuffix(ci, "K:CQ:C-");
704 buf[0] = str[0] & 0x1f;
709 ret = call("Replace", ci->focus, 1, ci->mark, str, !first);
710 call("Mode:set-num2", ci->focus, N2_undo_insert);
712 return ret < 0 ? ret : 1;
718 } other_inserts[] = {
726 DEF_CMD(emacs_insert_other)
730 struct mark *m = NULL;
731 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
732 bool first = N2(ci) != N2_undo_insert;
738 for (i = 0; other_inserts[i].key; i++)
739 if (strcmp(safe_cast other_inserts[i].key, ci->key) == 0)
741 ins = other_inserts[i].insert;
745 if (clear_selection(ci->focus, NULL, mk, 3)) {
746 call("Replace", ci->focus, 1, mk, NULL, !first);
749 clear_selection(ci->focus, NULL, mk, 2);
753 m = mark_dup(ci->mark);
754 /* Move m before ci->mark, so it doesn't move when we insert */
758 ret = call("Replace", ci->focus, 1, m, ins, !first, ci->mark);
760 mark_to_mark(ci->mark, m);
763 /* A newline starts a new undo */
764 call("Mode:set-num2", ci->focus, (*ins == '\n') ? 0 : N2_undo_insert);
765 return ret < 0 ? ret : 1;
768 DEF_CMD(emacs_interactive_insert)
770 /* If some pane want to insert text just like it was typed,
771 * it calls this, and we set up for proper undo
774 bool first = N2(ci) != N2_undo_insert;
779 if (clear_selection(ci->focus, NULL, ci->mark, 3)) {
780 call("Replace", ci->focus, 1, ci->mark, NULL, !first);
783 clear_selection(ci->focus, NULL, ci->mark, 2);
784 ret = call("Replace", ci->focus, 1, ci->mark, ci->str,
786 call("Mode:set-num2", ci->focus,
787 strchr(ci->str, '\n') ? 0 : N2_undo_insert);
788 return ret < 0 ? ret : 1;
791 DEF_CMD(emacs_interactive_delete)
793 /* If some pane want to delete text just like backspace was typed,
794 * it calls this, and we set up for proper undo
800 ret = call("Replace", ci->focus, 1, ci->mark, "",
801 N2(ci) == N2_undo_insert, ci->mark2);
802 call("Mode:set-num2", ci->focus,
803 strchr(ci->str, '\n') ? 0 : N2_undo_delete);
804 return ret < 0 ? ret : 1;
811 ret = call("doc:reundo", ci->focus, N2(ci) == N2_undo);
812 call("Mode:set-num2", ci->focus, N2_undo);
814 call("Message", ci->focus, 0, NULL,
815 "No further undo information");
820 REDEF_CMD(emacs_file_complete);
821 REDEF_CMD(emacs_doc_complete);
822 REDEF_CMD(emacs_cmd_complete);
824 DEF_CMD(find_complete)
826 char *type = ci->home->data;
828 if (strstarts(type, "file"))
829 return emacs_file_complete_func(ci);
830 if (strcmp(type, "shellcmd") == 0)
831 return emacs_file_complete_func(ci);
832 if (strcmp(type, "doc") == 0)
833 return emacs_doc_complete_func(ci);
834 if (strcmp(type, "cmd") == 0)
835 return emacs_cmd_complete_func(ci);
842 char *type = ci->home->data;
843 char *str = call_ret(strsave, "doc:get-str", ci->focus);
844 const char *norm = NULL;
848 /* close with no string */
849 ret = call("popup:close", ci->focus);
850 if (strcmp(type, "doc") == 0 &&
851 call_ret(pane, "docs:byname", ci->focus, 0, NULL, str) == NULL) {
852 call("Message:modal", ci->focus, 0, NULL, "Document not found");
855 if (strstarts(type, "file")) {
857 bool can_create = True;
858 bool can_create_dir = True;
860 norm = file_normalize(ci->focus, str,
861 attr_find(ci->home->attrs,
863 sl = strrchr(norm, '/');
864 while (sl && sl > norm) {
865 /* Need to check directories exist. */
869 if ((stb.st_mode & S_IFMT) == S_IFDIR)
872 can_create_dir = False;
874 sl = strrchr(norm, '/');
878 if (!can_create_dir) {
879 call("Message:modal", ci->focus, 0, NULL,
880 strconcat(ci->focus, norm, " is not a directory!!"));
884 /* Need to create directory first */
885 if (strcmp(ci->key, "K:A:Enter") == 0) {
886 char *m = "Cannot create directory: ";
887 if (mkdir(norm, 0777) == 0) {
888 m = "Created directory: ";
889 attr_set_str(&ci->home->attrs,
891 /* Trigger recalc on replace */
892 call("doc:replace", ci->focus, 0, NULL, "");
894 call("Message:modal", ci->focus, 0, NULL,
895 strconcat(ci->focus, m, norm));
898 call("Message:modal", ci->focus, 0, NULL,
900 "Use Alt-Enter to create directory ", norm));
904 if (strcmp(type, "file") == 0 && norm &&
905 strcmp(ci->key, "K:Enter") == 0 &&
906 stat(norm, &stb) != 0) {
907 call("Message:modal", ci->focus, 0, NULL,
908 "File not found - use Alt-Enter to create");
911 if (strcmp(type, "file write") == 0 && norm &&
912 strcmp(ci->key, "K:Enter") == 0 &&
913 stat(norm, &stb) == 0) {
914 call("Message:modal", ci->focus, 0, NULL,
915 "File exists - use Alt-Enter to overwrite");
918 ret = call("popup:close", ci->focus, 0, NULL, norm ?: str);
919 return ret < 0 ? ret : 1;
931 struct find_helper *h = container_of(ci->comm, struct find_helper, c);
932 struct pane *p = ci->focus;
939 /* want the pane before nothing, so the last.
940 * So return this one and keep looking.
945 /* Want the pane that is after nothing, so
946 * the first. This one. All done.
952 name = pane_attr_get(ci->focus, "doc-name");
955 if (strcmp(name, h->name) == 0) {
957 /* Want the previous one, which is
962 /* Want the next one, so clear name
970 /* This might be what I want - keep it in case */
974 /* Don't want this - just keep going */
980 DEF_CMD(find_prevnext)
982 /* Find the previous document lru or, which is "next" as we
983 * walk the list in mru order.
984 * When we find it, insert the name into ci->focus document
986 char *type = ci->home->data;
987 struct find_helper h;
989 if (strcmp(type, "doc") != 0)
991 h.name = attr_find(ci->home->attrs, "find-doc");
994 h.want_prev = strcmp(ci->key, "K:A-n") == 0;
996 call_comm("docs:byeach", ci->focus, &h.c);
998 char *name = pane_attr_get(h.ret, "doc-name");
1001 attr_set_str(&ci->home->attrs, "find-doc", name);
1002 m = mark_new(ci->focus);
1003 m2 = m ? mark_dup(m) : NULL;
1004 call("doc:file", ci->focus, -1, m);
1005 call("doc:file", ci->focus, 1, m2);
1006 call("Replace", ci->focus, 1, m, name, 0, m2);
1015 char *type = ci->home->data;
1020 if (strcmp(type, "file") != 0)
1021 return Efallthrough;
1023 if (strcmp(ci->str, "start-of-line") == 0) {
1024 char *lens = attr_find(ci->home->attrs, "path_lengths");
1025 int dir_start = 0, dir_end = 0, nondir_end = 0,
1028 sscanf(lens, "%d %d %d %d", &dir_start, &dir_end,
1029 &nondir_end, &basename_start);
1032 comm_call(ci->comm2, "cb", ci->focus, dir_start,
1033 ci->mark, "fg:grey+20,nobold,noinverse", 115);
1034 if (dir_end > dir_start)
1035 comm_call(ci->comm2, "cb", ci->focus, dir_end,
1036 ci->mark, "fg:black,nobold,noinverse", 114);
1037 if (nondir_end > dir_end)
1038 comm_call(ci->comm2, "cb", ci->focus, nondir_end,
1039 ci->mark, "fg:red-80,bold,inverse", 113);
1040 if (basename_start > nondir_end)
1041 comm_call(ci->comm2, "cb", ci->focus, basename_start,
1042 ci->mark, "fg:magenta", 112);
1043 comm_call(ci->comm2, "cb", ci->focus, 10000, ci->mark,
1049 DEF_CMD(find_check_replace)
1051 char *str, *cp, *sl;
1052 char *type = ci->home->data;
1056 int ipl; // Initial Path Len
1057 int dir_start, dir_end, nondir_end, basename_start;
1058 char nbuf[4 * (10+1) + 1], *lens;
1060 if (strcmp(type, "file") != 0)
1061 return Efallthrough;
1063 home_call(ci->home->parent, ci->key, ci->focus,
1064 ci->num, ci->mark, ci->str,
1065 ci->num2, ci->mark2, ci->str2);
1067 /* The doc content can have 5 different sections that might
1068 * be different colours.
1069 * - ignored prefix: grey - This inital_path followed by something
1070 * that looks like another path. "/" or "~/"
1071 * - True directories: black - directory part of the path that
1072 * exists and is a directory
1073 * - non-directory-in-path: red - directory part that exists but
1074 * is not a directory. At most one component.
1075 * - non-existant name: magenta - directory path that doesn't exist.
1076 * - basename: black, whether it exists or not.
1077 * These are found as:
1082 * These are all lengths from start of path. They are all stored
1083 * in a single attribute: "path_lengths".
1086 str = call_ret(str, "doc:get-str", ci->focus);
1089 sl = strrchr(str, '/');
1093 prev_dir = attr_find(ci->home->attrs, "prev_dir");
1094 if (prev_dir && strlen(prev_dir) == (size_t)(sl - str + 1) &&
1095 strncmp(prev_dir, str, sl - str + 1) == 0)
1096 /* No change before last '/' */
1099 initial_path = attr_find(ci->home->attrs, "initial_path");
1103 ipl = strlen(initial_path);
1105 if (strncmp(str, initial_path, ipl) == 0 &&
1106 (str[ipl] == '/' || (str[ipl] == '~' &&
1107 (str[ipl+1] == '/' ||
1111 dir_start = utf8_strnlen(str, cp - str);
1113 basename_start = utf8_strnlen(str, sl - str + 1);
1120 path = file_normalize(ci->focus, str, initial_path);
1126 while (sl > cp && *sl != '/')
1129 nondir_end = utf8_strnlen(str, sl - str + 1);
1130 dir_end = nondir_end;
1131 if (stb.st_mode != 0 &&
1132 (stb.st_mode & S_IFMT) != S_IFDIR) {
1133 /* There is a non-dir on the path */
1134 while (sl > cp && sl[-1] != '/')
1136 /* This must actually be a dir */
1137 dir_end = utf8_strnlen(str, sl - str);
1139 snprintf(nbuf, sizeof(nbuf), "%d %d %d %d",
1140 dir_start, dir_end, nondir_end, basename_start);
1141 lens = attr_find(ci->home->attrs, "path_lengths");
1142 if (!lens || strcmp(lens, nbuf) != 0)
1143 attr_set_str(&ci->home->attrs, "path_lengths", nbuf);
1144 sl = strrchr(str, '/');
1147 attr_set_str(&ci->home->attrs, "prev_dir", str);
1152 static struct map *fh_map;
1153 DEF_LOOKUP_CMD(find_handle, fh_map);
1155 static void findmap_init(void)
1159 fh_map = key_alloc();
1160 key_add(fh_map, "K:Tab", &find_complete);
1161 key_add(fh_map, "K:Enter", &find_done);
1162 key_add(fh_map, "K:A:Enter", &find_done);
1163 key_add(fh_map, "K:A-p", &find_prevnext);
1164 key_add(fh_map, "K:A-n", &find_prevnext);
1165 key_add(fh_map, "map-attr", &find_attr);
1166 key_add(fh_map, "doc:replace", &find_check_replace);
1169 static const char * safe file_normalize(struct pane *p safe,
1171 const char *initial_path)
1173 int len = strlen(initial_path?:"");
1178 if (initial_path && strncmp(path, initial_path, len) == 0) {
1179 if (path[len] == '/' || (path[len] == '~' &&
1180 (path[len+1] == '/' || path[len+1] == 0)))
1185 if (path[0] == '~' && (path[1] == '/' || path[1] == 0)) {
1186 char *home = getenv("HOME");
1189 if (home[strlen(home) - 1] == '/' && path[1] == '/')
1191 return strconcat(p, home, path+1);
1193 dir = pane_attr_get(p, "dirname");
1196 if (dir[strlen(dir)-1] == '/')
1197 return strconcat(p, dir, path);
1198 return strconcat(p, dir, "/", path);
1201 DEF_CMD(emacs_findfile)
1204 struct pane *p, *par;
1209 path = pane_attr_get(ci->focus, "dirname");
1218 e = buf + strlen(buf);
1219 if (e < buf + sizeof(buf)-1 && e > buf && e[-1] != '/') {
1224 if (ksuffix(ci, "File Found:")[0] == 0) {
1225 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
1230 if (ksuffix(ci, "K:CX44")[0]) {
1231 attr_set_str(&p->attrs, "prompt",
1233 attr_set_str(&p->attrs, "done-key",
1234 "File Found:Popup");
1235 } else if (ksuffix(ci, "K:CX4")[0]) {
1236 attr_set_str(&p->attrs, "prompt",
1237 "Find File Other Window");
1238 attr_set_str(&p->attrs, "done-key",
1239 "File Found:Other Window");
1241 attr_set_str(&p->attrs, "prompt", "Find File");
1242 attr_set_str(&p->attrs, "done-key",
1243 "File Found:This Window");
1245 call("doc:set-name", p, 0, NULL, "Find File");
1247 p = pane_register(p, 0, &find_handle.c, "file");
1250 attr_set_str(&p->attrs, "initial_path", path);
1251 call("attach-history", p, 0, NULL, "*File History*");
1258 path = file_normalize(ci->focus, ci->str, path);
1263 fd = open(path, O_RDONLY);
1265 p = call_ret(pane, "doc:open", ci->focus, fd, NULL, path);
1268 /* '4' says 'allow create' */
1269 p = call_ret(pane, "doc:open", ci->focus, -1, NULL, path, 4);
1272 asprintf(&m, "Failed to open file: %s", path);
1273 call("Message", ci->focus, 0, NULL, m);
1277 if (strcmp(ci->key, "File Found:Other Window") == 0) {
1278 struct pane *this = call_ret(pane, "ThisPane", ci->focus);
1279 par = home_call_ret(pane, ci->focus, "DocPane", p);
1280 if (par && par != this) {
1281 pane_take_focus(par);
1284 par = call_ret(pane, "OtherPane", ci->focus);
1285 } else if (strcmp(ci->key, "File Found:Popup") == 0) {
1286 par = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "MD3tsa");
1288 par = call_ret(pane, "ThisPane", ci->focus);
1295 p = home_call_ret(pane, p, "doc:attach-view", par, 1);
1299 return p ? 1 : Efail;
1302 DEF_CMD(emacs_writefile)
1304 /* Request to write to a different file */
1310 if (call("doc:write-file", ci->focus, NO_NUMERIC) >= 0) {
1311 /* It should have been an error ! */
1312 const char *doc = pane_attr_get(ci->focus, "doc-name");
1314 call("Message", ci->focus, 0, NULL,
1315 strconcat(ci->focus, "Document ", doc,
1316 " cannot be written"));
1318 call("Message", ci->focus, 0, NULL,
1319 "This document cannot be written");
1322 path = pane_attr_get(ci->focus, "dirname");
1331 e = buf + strlen(buf);
1332 if (e < buf + sizeof(buf)-1 && e > buf && e[-1] != '/') {
1337 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
1341 attr_set_str(&p->attrs, "prompt", "Write File");
1342 attr_set_str(&p->attrs, "done-key",
1343 strconcat(p, "emacs:write_file:", path));
1344 call("doc:set-name", p, 0, NULL, "Write File");
1345 p = pane_register(p, 0, &find_handle.c, "file write");
1348 attr_set_str(&p->attrs, "initial_path", path);
1349 call("attach-history", p, 0, NULL, "*File History*");
1353 DEF_CMD(emacs_do_writefile)
1355 const char *path = ksuffix(ci, "emacs:write_file:");
1356 if (!ci->str || !path)
1359 path = file_normalize(ci->focus, ci->str, path);
1362 if (call("doc:write-file", ci->focus, NO_NUMERIC, NULL, path) <= 0) {
1363 call("Message", ci->focus, 0, NULL,
1364 strconcat(ci->focus, "Failed to write to ", path));
1370 DEF_CMD(emacs_insertfile)
1372 /* Request to insert content of a file at point */
1379 ret = call("doc:insert-file", ci->focus, NO_NUMERIC);
1380 if (ret != Enoarg) {
1383 /* Message already given */
1385 doc = pane_attr_get(ci->focus, "doc-name");
1387 call("Message", ci->focus, 0, NULL,
1388 strconcat(ci->focus, "Document ", doc,
1389 " cannot receive insertions"));
1391 call("Message", ci->focus, 0, NULL,
1392 "This document cannot receive insertions");
1395 path = pane_attr_get(ci->focus, "dirname");
1404 e = buf + strlen(buf);
1405 if (e < buf + sizeof(buf)-1 && e > buf && e[-1] != '/') {
1410 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
1414 attr_set_str(&p->attrs, "prompt", "Insert File");
1415 attr_set_str(&p->attrs, "done-key",
1416 strconcat(p, "emacs:insert_file:", path));
1417 call("doc:set-name", p, 0, NULL, "Insert File");
1418 p = pane_register(p, 0, &find_handle.c, "file");
1421 attr_set_str(&p->attrs, "initial_path", path);
1422 call("attach-history", p, 0, NULL, "*File History*");
1426 DEF_CMD(emacs_do_insertfile)
1428 const char *path = ksuffix(ci, "emacs:insert_file:");
1431 if (!ci->str || !path)
1434 path = file_normalize(ci->focus, ci->str, path);
1437 fd = open(path, O_RDONLY);
1439 if (call("doc:insert-file", ci->focus, fd) <= 0) {
1441 call("Message", ci->focus, 0, NULL,
1442 strconcat(ci->focus, "Failed to insert ", path));
1448 char *m = strconcat(ci->focus,
1449 "Failed to inser file: ", path);
1450 call("Message", ci->focus, 0, NULL, m);
1455 REDEF_CMD(emacs_file_complete)
1457 /* Extract a directory name and a basename from the document.
1458 * Find a document for the directory and attach as a completing
1464 struct pane *par, *pop, *docp, *p;
1465 struct call_return cr;
1466 char *type = ci->home->data;
1467 char *initial = attr_find(ci->home->attrs, "initial_path");
1468 int wholebuf = strcmp(type, "file") == 0;
1474 st = mark_dup(ci->mark);
1475 call("doc:file", ci->focus, -1, st);
1477 str = call_ret(strsave, "doc:get-str", ci->focus, 0, st, NULL,
1484 /* Need guess which part of the buf is the file name.
1485 * This probably needs to be configurable, but lets
1486 * decide the file name starts immediately after a
1487 * space, or a '=' or ':' which is followed by a
1491 d = str + strlen(str);
1494 (strchr(":=", d[-1]) && d[0] == '/')))
1497 d = file_normalize(ci->focus, d, initial);
1498 b = strrchr(d, '/');
1501 d = strnsave(ci->focus, d, b-d);
1504 d = strsave(ci->focus, ".");
1506 fd = open(d, O_DIRECTORY|O_RDONLY);
1508 call("Message:modal", ci->focus, 0, NULL,
1509 strconcat(ci->focus, "Cannot open directory \"", d, "\""));
1512 /* 32 means quiet */
1513 docp = call_ret(pane, "doc:open", ci->focus, fd, NULL, d, 32);
1517 pop = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM1r");
1520 par = home_call_ret(pane, docp, "doc:attach-view", pop,
1525 attr_set_str(&par->attrs, "line-format", "%name%suffix");
1526 attr_set_str(&par->attrs, "heading", "");
1527 attr_set_str(&par->attrs, "done-key", "Replace");
1528 p = call_ret(pane, "attach-render-complete", par, 0, NULL, "format");
1531 cr = call_ret(all, "Complete:prefix", p, 1, NULL, b,
1532 0, NULL, "format:plain");
1533 if (cr.s && (strlen(cr.s) <= strlen(b) && cr.ret-1 > 1)) {
1534 /* We need the dropdown - delete prefix and drop-down will
1539 start = mark_dup(ci->mark);
1540 call("doc:char", ci->focus, -strlen(b), start);
1541 call("Replace", ci->focus, 1, start);
1547 /* Replace 'b' with the result. */
1550 start = mark_dup(ci->mark);
1551 call("doc:char", ci->focus, -strlen(b), start);
1552 call("Replace", ci->focus, 1, start, cr.s);
1555 call("Message:modal", ci->focus, 0, NULL,
1556 strconcat(ci->focus, "No completion found for \"", b, "\"",
1557 " in \"", d, "\""));
1559 /* Now need to close the popup */
1564 DEF_CMD(emacs_finddoc)
1566 struct pane *p, *par;
1568 if (ksuffix(ci, "Doc Found:")[0] == 0) {
1570 char *defname = NULL;
1572 dflt = call_ret(pane, "docs:choose", ci->focus);
1574 defname = pane_attr_get(dflt, "doc-name");
1576 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
1582 attr_set_str(&p->attrs, "default", defname);
1583 if (ksuffix(ci, "K:CX44")[0]) {
1584 attr_set_str(&p->attrs, "prompt",
1585 "Find Document Popup");
1586 attr_set_str(&p->attrs, "done-key",
1588 } else if (ksuffix(ci, "K:CX4")[0]) {
1589 attr_set_str(&p->attrs, "prompt",
1590 "Find Document Other Window");
1591 attr_set_str(&p->attrs, "done-key",
1592 "Doc Found:Other Window");
1594 attr_set_str(&p->attrs, "prompt", "Find Document");
1595 attr_set_str(&p->attrs, "done-key",
1596 "Doc Found:This Window");
1598 call("doc:set-name", p, 0, NULL, "Find Document", -1);
1600 pane_register(p, 0, &find_handle.c, "doc");
1608 p = call_ret(pane, "docs:byname", ci->focus, 0, NULL, ci->str);
1612 if (strcmp(ci->key, "Doc Found:Other Window") == 0) {
1613 struct pane *this = call_ret(pane, "ThisPane", ci->focus);
1614 par = home_call_ret(pane, ci->focus, "DocPane", p);
1615 if (par && par != this) {
1616 pane_take_focus(par);
1619 par = call_ret(pane, "OtherPane", ci->focus);
1620 } else if (strcmp(ci->key, "Doc Found:Popup") == 0) {
1621 par = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "MD3tsa");
1623 par = call_ret(pane, "ThisPane", ci->focus);
1627 p = home_call_ret(pane, p, "doc:attach-view", par, 1);
1630 return p ? 1 : Efail;
1633 REDEF_CMD(emacs_doc_complete)
1635 /* Extract a document name from the document.
1636 * Attach the 'docs' document as a completing popup menu
1639 struct pane *pop, *p = NULL;
1640 struct call_return cr;
1645 str = call_ret(strsave, "doc:get-str", ci->focus);
1648 pop = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM1r");
1651 p = call_ret(pane, "docs:complete", pop);
1654 cr = call_ret(all, "Complete:prefix", p, 1, NULL, str);
1655 if (cr.s && (strlen(cr.s) <= strlen(str) && cr.ret - 1 > 1)) {
1656 /* We need the dropdown */
1659 start = mark_dup(ci->mark);
1660 call("doc:set-ref", ci->focus, 1, start);
1662 call("Replace", ci->focus, 1, start);
1667 /* Replace the prefix with the new value */
1670 start = mark_dup(ci->mark);
1671 call("doc:set-ref", ci->focus, 1, start);
1673 call("Replace", ci->focus, 1, start, cr.s);
1676 /* Now need to close the popup */
1681 DEF_CMD(emacs_viewdocs)
1683 struct pane *p, *par;
1686 docs = call_ret(pane, "docs:byname", ci->focus, 0, NULL, "*Documents*");
1690 if (ksuffix(ci, "K:CX44")[0]) {
1691 par = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "MD3tsa");
1692 } else if (ksuffix(ci, "K:CX4")[0]) {
1693 struct pane *this = call_ret(pane, "ThisPane", ci->focus);
1694 par = home_call_ret(pane, ci->focus, "DocPane", docs);
1695 if (par && par != this) {
1696 pane_take_focus(par);
1699 par = call_ret(pane, "OtherPane", ci->focus);
1701 par = call_ret(pane, "ThisPane", ci->focus);
1707 p = home_call_ret(pane, docs, "doc:attach-view", par, 1);
1713 struct pane *doc safe;
1717 DEF_CMD(choose_pane)
1719 struct pcb *cb = container_of(ci->comm, struct pcb, c);
1722 if (cb->p || !ci->str)
1724 d = call_ret(pane, "docs:byname", ci->focus, 0, NULL, ci->str);
1726 cb->p = home_call_ret(pane, ci->focus, "DocPane", d);
1728 home_call(cb->doc, "doc:attach-view", cb->p, 1);
1736 if (strcmp(ci->key, "cb:timer") == 0) {
1737 /* If focus has moved, don't show shell window,
1738 * probably a popup was requested by command.
1740 if (!pane_has_focus(ci->home))
1741 /* call back when lines or eof */
1744 if (strcmp(ci->key, "cb:eof") != 0) {
1746 if (ci->str && strchr(ci->str, 'P'))
1747 par = call_ret(pane, "PopupTile", ci->home, 0, NULL, "MD3tsa");
1749 par = call_ret(pane, "OtherPane", ci->home);
1751 home_call(ci->focus, "doc:attach-view", par, 1);
1754 str = call_ret(str, "doc:get-str", ci->focus);
1755 if (!str || !*str) {
1758 asprintf(&str, "(shell command completed with no output)");
1759 else if (ci->num > 0)
1760 asprintf(&str, "(shell command completed with no output (exit code %d))", ci->num);
1762 asprintf(&str, "(shell command completed with no output (signalled %d))", -ci->num);
1764 call("Message", ci->home, 0, NULL, str);
1769 DEF_CB(shell_insert_cb)
1771 char *str = call_ret(str, "doc:get-str", ci->focus);
1772 struct mark *mk = call_ret(mark2, "doc:point", ci->home);
1774 if (clear_selection(ci->home, NULL, mk, 3))
1775 call("Replace", ci->home, 1, mk);
1776 call("Replace", ci->home, 0, NULL, str);
1781 DEF_CMD(emacs_shell)
1783 char *name = "*Shell Command Output*";
1784 struct pane *p, *doc, *par, *sc;
1785 char *path, *input = NULL;
1787 bool interpolate, pipe, popup;
1789 if (strcmp(ci->key, "Shell Command") != 0) {
1793 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2", 0, NULL,
1797 dirname = call_ret(strsave, "get-attr", ci->focus, 0, NULL, "dirname");
1798 attr_set_str(&p->attrs, "dirname", dirname ?: ".");
1799 attr_set_str(&p->attrs, "prompt", "Shell command");
1800 if (ci->num == 0 || ci->num == 1)
1801 strcat(aux, "i"); // interpolate
1803 strcat(aux, "P"); // popup
1804 if (strcmp(ci->key, "K:A-|") == 0)
1805 strcat(aux, "p"); // pipe from selection
1806 attr_set_str(&p->attrs, "popup-aux", aux);
1807 attr_set_str(&p->attrs, "done-key", "Shell Command");
1808 call("doc:set-name", p, 0, NULL, "Shell Command", -1);
1809 p = call_ret(pane, "attach-history", p, 0, NULL, "*Shell History*");
1811 p = pane_register(p, 0, &find_handle.c, "shellcmd");
1815 comm_call(ci->comm2, "cb", p);
1819 call("Message", ci->focus, 0, NULL, "Shell command aborted");
1823 interpolate = ci->str2 && strchr(ci->str2, 'i');
1824 pipe = ci->str2 && strchr(ci->str2, 'p');
1825 popup = ci->str2 && strchr(ci->str2, 'P');
1828 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
1830 input = call_ret(str, "doc:get-str", ci->focus,
1831 0, NULL, NULL, 0, mk);
1832 /* make the selection replacable */
1833 attr_set_int(&mk->attrs, "emacs:active", 3);
1836 doc = call_ret(pane, "doc:from-text", ci->focus, 0, NULL, name, 0, NULL,
1842 path = pane_attr_get(ci->focus, "dirname");
1843 attr_set_str(&doc->attrs, "dirname", path);
1845 /* shellcmd is attached directly to the document, not in the view
1846 * stack. It is go-between for document and external command.
1847 * We don't need a doc attachment as no point is needed - we
1848 * always insert at the end.
1850 sc = call_ret(pane, "attach-shellcmd", doc, pipe ? 22 : 4,
1851 NULL, ci->str, 0, NULL, path);
1853 call("doc:replace", doc, 0, NULL,
1854 "Failed to run command - sorry\n");
1855 if (call("text-search", doc, 0, NULL, "diff|(stg|git).*show",
1856 0, NULL, ci->str) > 0)
1857 attr_set_str(&doc->attrs, "view-default", "diff");
1859 /* Close old shell docs, but if one is visible in current frame, replace
1865 call_comm("editor:notify:shell-reuse", ci->focus, &cb.c);
1867 /* choose_pane didn't attach, so set a callback to do
1868 * it when there is enough content.
1871 /* If it take more than 500msec, or includes 2
1872 * or more lines, we'll show in a pane, else
1873 * just show as a message.
1876 home_call_comm(sc, "shellcmd:set-callback",
1877 ci->focus, &shellcb,
1878 500, NULL, popup ? "P": "",
1882 par = call_ret(pane, "PopupTile", ci->focus,
1885 par = call_ret(pane, "OtherPane", ci->focus);
1888 home_call(doc, "doc:attach-view", par, 1);
1891 if (sc && interpolate)
1892 /* Need a callback when the pipe command finished */
1893 home_call_comm(sc, "shellcmd:set-callback", ci->focus,
1902 const char *last = ksuffix(ci, "K:A-");
1909 if (rpt == NO_NUMERIC)
1912 rpt = rpt * 10 + *last - '0';
1914 call("Mode:set-num", ci->focus, neg ? -rpt : rpt);
1915 call("Mode:set-num2", ci->focus, ci->num2);
1921 call("Mode:set-num", ci->focus, - ci->num);
1922 call("Mode:set-num2", ci->focus, ci->num2);
1926 DEF_CMD(emacs_prefix)
1928 /* as a generic arg (ctrl-U) which is positive and
1929 * as as a repeat-count of 4, but is different to 4.
1930 * I should probably allow digits to alter the number.
1932 call("Mode:set-num", ci->focus, 4);
1936 DEF_CMD(emacs_kill_doc)
1938 if (ci->num <= 0 || ci->num == NO_NUMERIC) {
1939 /* Check if modified. */
1940 char *m = pane_attr_get(ci->focus, "doc-modified");
1942 if (m && strcmp(m, "yes") == 0)
1943 f = pane_attr_get(ci->focus, "filename");
1945 call("Message:modal", ci->focus, 0, NULL,
1946 "Document is modified - please save or give prefix arg");
1950 return call("doc:destroy", ci->focus);
1953 DEF_CMD(emacs_revisit)
1955 return call("doc:load-file", ci->focus, 2, NULL, NULL, -1);
1958 DEF_CMD(emacs_save_all)
1960 int ret = call("docs:save-all", ci->focus, 0, NULL, NULL, 1);
1963 call("Message", ci->focus, 0, NULL, "No files need to be saved.");
1966 if (ci->num == NO_NUMERIC) {
1967 struct pane *p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM");
1969 return call("docs:show-modified", p);
1971 return call("docs:save-all", ci->focus);
1974 DEF_CMD(emacs_reposition)
1977 int repoint = attr_find_int(ci->focus->attrs, "emacs-repoint");
1979 if (repoint != -1) {
1980 /* Move point to end of display, if that is in
1981 * the right direction. That will mean it has moved
1984 m = mark_at_point(ci->focus, NULL, MARK_UNGROUPED);
1986 struct mark *m2 = mark_dup(m);
1987 call("Move-CursorXY", ci->focus, 0, m, NULL,
1989 INT_MAX, repoint < 0 ? ci->focus->h-1 : 0);
1991 /* can only move point backwards */
1992 if (m->seq < m2->seq)
1993 call("Move-to", ci->focus, 0, m);
1995 /* can only move point forwards */
1996 if (m->seq > m2->seq)
1997 call("Move-to", ci->focus, 0, m);
2001 attr_set_str(&ci->focus->attrs, "emacs-repoint", NULL);
2003 return Efallthrough;
2006 DEF_CMD(emacs_start_search)
2008 struct pane *p = NULL, *hp;
2011 hp = call_ret(pane, "attach-emacs-search-highlight", ci->focus);
2014 p = call_ret(pane, "PopupTile", hp, 0, NULL, "TR2",
2019 home_call(hp, "highlight:set-popup", p);
2021 attr_set_str(&p->attrs, "prompt", "Search");
2022 attr_set_str(&p->attrs, "done-key", "Search String");
2024 hp = call_ret(pane, "attach-history", p, 0, NULL, "*Search History*");
2028 call("doc:set-name", p, 0, NULL, "Search", -1);
2029 if (strcmp(ci->key, "K:C-R") == 0)
2031 if (strcmp(ci->key, "K:A-%") == 0)
2033 call_ret(pane, "attach-emacs-search", p, mode);
2038 DEF_CMD(emacs_command)
2042 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2", 0, NULL, "");
2045 attr_set_str(&p->attrs, "prompt", "Cmd");
2046 attr_set_str(&p->attrs, "done-key", "emacs:command");
2047 call("doc:set-name", p, 0, NULL, "K:Ax command", -1);
2048 p = call_ret(pane, "attach-history", p, 0, NULL, "*Command History*");
2050 pane_register(p, 0, &find_handle.c, "cmd");
2054 DEF_CMD(emacs_do_command)
2062 asprintf(&cmd, "interactive-cmd-%s", ci->str);
2065 ret = call(cmd, ci->focus, 0, ci->mark);
2066 free(cmd); cmd = NULL;
2068 asprintf(&cmd, "Command %s not found", ci->str);
2069 call("Message", ci->focus, 0, NULL, cmd);
2070 } else if (ret < 0) {
2071 asprintf(&cmd, "Command %s Failed", ci->str);
2072 call("Message", ci->focus, 0, NULL, cmd);
2080 struct call_return *cr = container_of(ci->comm, struct call_return, c);
2087 struct mark *m = vmark_new(cr->p, MARK_UNGROUPED, NULL);
2088 call("doc:list-add", cr->p, 0, m);
2089 call("doc:set-attr", cr->p, 0, m, "cmd", 0, NULL, cmd);
2094 REDEF_CMD(emacs_cmd_complete)
2097 struct pane *doc = NULL, *pop = NULL, *p;
2098 struct call_return cr;
2102 s = call_ret(strsave, "doc:get-str", ci->focus);
2105 doc = call_ret(pane, "attach-doc-list", ci->focus);
2108 call("doc:set-name", doc, 0, NULL, "*Command List*");
2109 call("doc:set:autoclose", doc, 1);
2110 attr_set_str(&doc->attrs, "render-simple", "format");
2111 attr_set_str(&doc->attrs, "heading", "");
2112 attr_set_str(&doc->attrs, "line-format", "%cmd");
2115 call_comm("keymap:list", ci->focus, &cr.c,
2116 0, NULL, "interactive-cmd-");
2117 call("doc:list-sort", doc, 0, NULL, "cmd");
2118 pop = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM1r");
2121 p = home_call_ret(pane, cr.p, "doc:attach-view", pop, -1, NULL, "simple");
2124 attr_set_str(&p->attrs, "done-key", "Replace");
2125 p = call_ret(pane, "attach-render-complete", p);
2128 cr = call_ret(all, "Complete:prefix", p, 1, NULL, s);
2129 if (cr.s && strlen(cr.s) <= strlen(s) && cr.ret-1 > 1) {
2130 /* We need the dropdown - delete prefix and drop-down will
2133 struct mark *start = mark_dup(ci->mark);
2134 call("Move-Char", ci->focus, -strlen(s), start);
2135 call("Replace", ci->focus, 1, start);
2140 /* Replace 's' with the result */
2141 struct mark *start = mark_dup(ci->mark);
2142 call("Move-Char", ci->focus, -strlen(s), start);
2143 call("Replace", ci->focus, 1, start, cr.s);
2146 /* Now need to close the popup */
2158 DEF_CMD(emacs_version)
2162 asprintf(&v, "%s ; emacs-mode - v" VERSION " - " VERS_DATE, edlib_version);
2164 call("Message", ci->focus, 0, NULL, v);
2171 /* View the debug log */
2172 struct pane *p, *doc;
2174 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL, "*Debug Log*");
2176 call("interactive-cmd-view-log", ci->focus);
2177 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL,
2182 p = call_ret(pane, "ThisPane", ci->focus);
2184 home_call(doc, "doc:attach-view", p, 1);
2190 struct mark *m = call_ret(mark2, "doc:point", ci->focus);
2192 clear_selection(ci->focus, NULL, m, 0);
2194 call("Move-to", ci->focus, 1);
2195 m = call_ret(mark2, "doc:point", ci->focus);
2197 /* ci->num == 1 means replacable */
2198 set_selection(ci->focus, NULL, m, ci->num == 1 ? 3 : 1);
2202 DEF_CMD(emacs_abort)
2204 /* On abort, forget mark */
2205 struct mark *m = call_ret(mark2, "doc:point", ci->focus);
2207 clear_selection(ci->focus, NULL, m, 0);
2208 return Efallthrough;
2211 DEF_CMD(emacs_swap_mark)
2213 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2219 call("Move-to", ci->focus, 1); /* Move mark to point */
2220 call("Move-to", ci->focus, 0, m); /* Move point to old mark */
2221 set_selection(ci->focus, NULL, mk, ci->num == 1 ? 3 : 1);
2228 /* Delete text from point to mark */
2229 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2235 /* Remove any selection so it cannot be claimed and so replace this copy */
2236 call("selection:claim", ci->focus);
2237 call("selection:discard", ci->focus);
2239 str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, mk);
2241 call("copy:save", ci->focus, 0, NULL, str);
2242 ret = call("Replace", ci->focus, 1, mk);
2244 clear_selection(ci->focus, NULL, mk, 0);
2251 /* copy text from point to mark */
2252 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2257 /* Remove any selection so it cannot be claimed and so replace this copy */
2258 call("selection:claim", ci->focus);
2259 call("selection:discard", ci->focus);
2261 str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, mk);
2263 call("copy:save", ci->focus, 0, NULL, str);
2264 /* Clear current highlight */
2265 clear_selection(ci->focus, NULL, mk, 0);
2271 int n = RPT_NUM(ci);
2274 struct mark *m = NULL;
2276 /* If there is a selection elsewhere, we want to commit it */
2277 call("selection:discard", ci->focus);
2278 call("selection:commit", ci->focus);
2280 str = call_ret(strsave, "copy:get", ci->focus, n - 1);
2283 /* If mark exists and is active, replace marked regions */
2284 mk = call_ret(mark2, "doc:point", ci->focus);
2285 if (mk && clear_selection(ci->focus, NULL, mk, 0)) {
2286 char *str2 = call_ret(strsave, "doc:get-str", ci->focus,
2287 0, NULL, NULL, 0, mk);
2289 call("copy:save", ci->focus, 0, NULL, str2);
2293 call("Move-to", ci->focus, 1);
2294 call("Replace", ci->focus, 1, m, str);
2296 mk = call_ret(mark2, "doc:point", ci->focus);
2297 call("Mode:set-num2", ci->focus, N2_yank);
2301 DEF_CMD(emacs_yank_pop)
2303 struct mark *mk, *m;
2307 if (N2(ci) != N2_yank)
2309 mk = call_ret(mark2, "doc:point", ci->focus);
2313 str = call_ret(strsave, "copy:get", ci->focus, num);
2316 str = call_ret(strsave, "copy:get", ci->focus, num);
2322 call("Replace", ci->focus, 1, mk, str);
2323 call("Move-to", ci->focus, 1, m);
2325 call("Mode:set-num2", ci->focus, (num << 16) | N2_yank);
2329 DEF_CMD(emacs_attrs)
2331 struct call_return cr;
2333 char *selection = "bg:white-80,vis-nl,menu-at-mouse,action-menu:emacs:selection-menu"; // grey
2338 cr = call_ret(all, "doc:point", ci->focus);
2339 if (cr.ret <= 0 || !cr.m || !cr.m2 || !ci->mark)
2341 active = attr_find_int(cr.m2->attrs, "emacs:active");
2344 if (active >= 3) /* replacable */
2345 selection = "bg:red+80,vis-nl"; // pink
2346 if (mark_same(cr.m, cr.m2))
2348 if (strcmp(ci->str, "render:interactive-mark") == 0) {
2349 if (ci->mark == cr.m2 && cr.m2->seq < cr.m->seq)
2350 return comm_call(ci->comm2, "attr:callback", ci->focus, 0,
2351 ci->mark, selection, 210);
2352 if (ci->mark == cr.m2)
2353 return comm_call(ci->comm2, "attr:callback", ci->focus, -1,
2354 ci->mark, selection, 210);
2356 if (strcmp(ci->str, "render:interactive-point") == 0) {
2357 if (cr.m == ci->mark && cr.m->seq < cr.m2->seq)
2358 return comm_call(ci->comm2, "attr:cb", ci->focus, 0,
2359 ci->mark, selection, 210);
2360 if (cr.m == ci->mark)
2361 return comm_call(ci->comm2, "attr:callback", ci->focus, -1,
2362 ci->mark, selection, 210);
2364 if (strcmp(ci->str, "start-of-line") == 0) {
2365 if ((cr.m->seq < ci->mark->seq && ci->mark->seq < cr.m2->seq &&
2366 !mark_same(ci->mark, cr.m2)) ||
2367 (cr.m2->seq < ci->mark->seq && ci->mark->seq < cr.m->seq &&
2368 !mark_same(ci->mark, cr.m)))
2369 return comm_call(ci->comm2, "attr:cb", ci->focus, 0,
2370 ci->mark, selection, 210);
2372 return Efallthrough;
2375 DEF_CMD(emacs_selection_menu)
2379 p = call_ret(pane, "attach-menu", ci->focus, 0, NULL, "V", 0, NULL,
2380 "emacs:selection-menu-action", ci->x, ci->y+1);
2383 call("global-multicall-selection-menu:add-", p);
2384 call("menu-add", p, 0, NULL, "de-select", 0, NULL, ":ESC");
2388 DEF_CMD(emacs_selection_menu_action)
2390 struct pane *home = ci->home;
2391 const char *c = ci->str;
2396 /* command for focus */
2397 call(c+1, ci->focus, 0, ci->mark);
2401 call("Keystroke-sequence", home, 0, NULL, c);
2405 DEF_CMD(emacs_selection_menu_add)
2407 struct pane *p = ci->focus;
2408 call("menu-add", p, 0, NULL, "Cut", 0, NULL, ":C-W");
2409 call("menu-add", p, 0, NULL, "Copy", 0, NULL, ":A-w");
2410 call("menu-add", p, 0, NULL, "Paste-in", 0, NULL, ":C-Y");
2411 return Efallthrough;
2414 DEF_CMD(emacs_goto_line)
2416 if (ci->num == NO_NUMERIC)
2418 call("CountLines", ci->focus, ci->num, ci->mark, "goto:line");
2419 call("Move-View-Pos", ci->focus, 0, ci->mark);
2423 DEF_CMD(emacs_next_match)
2425 call("Mode:set-num2", ci->focus, N2_match);
2426 call("Message:modal", ci->focus, 0, NULL, "Type ` to search again");
2427 return call("interactive-cmd-next-match", ci->focus, ci->num, NULL, NULL,
2428 strcmp(ci->key, "K-`") == 0);
2431 DEF_CMD(emacs_match_again)
2433 if (N2(ci) != N2_match)
2434 return emacs_insert_func(ci);
2436 return emacs_next_match_func(ci);
2441 call("interactive-cmd-make", ci->focus,
2442 ci->num, ci->mark, NULL,
2443 strcmp(ci->key, "K:CC:C-M") == 0);
2447 static void update_sel(struct pane *p safe,
2448 struct mark *pt safe, struct mark *m2 safe,
2451 struct mark *mfirst, *mlast;
2454 call("Move-to", p, 1, m2);
2455 mk = call_ret(mark2, "doc:point", p);
2459 type = attr_find(m2->attrs, "emacs:selection-type");
2461 attr_set_str(&m2->attrs, "emacs:selection-type", type);
2463 if (type && strcmp(type, "char") != 0) {
2465 if (pt->seq < mk->seq) {
2472 if (strcmp(type, "word") == 0) {
2473 wint_t wch = doc_prior(p, mfirst);
2474 /* never move back over spaces */
2475 if (wch != WEOF && !iswspace(wch))
2476 call("doc:word", p, -1, mfirst);
2477 wch = doc_following(p, mlast);
2478 /* For forward over a single space is OK */
2479 if (wch != WEOF && iswspace(wch))
2482 call("doc:word", p, 1, mlast);
2484 call("doc:EOL", p, -1, mfirst);
2485 /* Include trailing newline */
2486 call("doc:EOL", p, 1, mlast, NULL, 1);
2490 /* Don't set selection until range is non-empty, else we
2491 * might clear some other selection too early.
2493 if (!mark_same(pt, mk)) {
2494 /* Must call 'claim' first as it might be claiming from us */
2495 call("selection:claim", p);
2496 set_selection(p, pt, mk, 2);
2500 DEF_CMD(emacs_press)
2502 /* The second mark (managed by core-doc) is used to record the
2503 * selected starting point. When double- or triple- click
2504 * asks for word or line selection, the actually start, which
2505 * is stored in the first mark, may be different.
2506 * That selected starting point will record the current unit
2507 * siez in the emacs:selection-type attribute.
2509 struct mark *pt = call_ret(mark, "doc:point", ci->focus);
2510 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2511 struct mark *m2 = call_ret(mark2, "doc:point", ci->focus, 2);
2512 struct mark *m = mark_new(ci->focus);
2516 /* Not in document, not my problem */
2518 return Efallthrough;
2520 /* NOTE must find new location before view changes. */
2521 call("Move-CursorXY", ci->focus, 0, m, NULL, 0, NULL, NULL, ci->x, ci->y);
2523 clear_selection(ci->focus, pt, mk, 0);
2524 call("Move-to", ci->focus, 0, m);
2525 pane_take_focus(ci->focus);
2527 if (m2 && strcmp(ci->key, "M:DPress-1") == 0) {
2528 type = attr_find(m2->attrs, "emacs:selection-type");
2531 else if (strcmp(type, "char") == 0)
2533 else if (strcmp(type, "word") == 0)
2539 /* Record start of selection */
2540 call("Move-to", ci->focus, 2, m);
2541 m2 = call_ret(mark2, "doc:point", ci->focus, 2);
2543 attr_set_str(&m2->attrs, "emacs:selection-type", type);
2546 /* Record co-ordinate of start so we can tell if the mouse moved. */
2547 attr_set_int(&m2->attrs, "emacs:track-selection",
2548 1 + ci->x * 10000 + ci->y);
2549 update_sel(ci->focus, pt, m2, type);
2556 DEF_CMD(emacs_release)
2558 struct mark *p = call_ret(mark, "doc:point", ci->focus);
2559 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2560 struct mark *m2 = call_ret(mark2, "doc:point", ci->focus, 2);
2561 struct mark *m = mark_new(ci->focus);
2566 if (!p || !m2 || !m) {
2567 /* Not in a document or no selection start - not my problem */
2569 return Efallthrough;
2572 prev_pos = attr_find_int(m2->attrs, "emacs:track-selection");
2573 type = attr_find(m2->attrs, "emacs:selection-type");
2574 moved = prev_pos != (1 + ci->x * 10000 + ci->y);
2575 attr_set_int(&m2->attrs, "emacs:track-selection", 0);
2577 call("Move-CursorXY", ci->focus,
2578 0, m, "activate", 0, NULL, NULL, ci->x, ci->y);
2579 /* That action might have closed a pane. Better check... */
2580 if (ci->focus->damaged & DAMAGED_CLOSED) {
2583 /* Moved the mouse, so new location is point */
2584 call("Move-to", ci->focus, 0, m);
2585 update_sel(ci->focus, p, m2, NULL);
2586 } else if (type && strcmp(type, "char") != 0) {
2587 /* Otherwise use the old location. Point might not
2588 * be there exactly if it was moved to end of word/line
2590 call("Move-to", ci->focus, 0, m2);
2591 update_sel(ci->focus, p, m2, NULL);
2593 clear_selection(ci->focus, p, mk, 0);
2600 DEF_CMD(emacs_menu_open)
2602 /* If there is a menu action here, activate it. */
2603 /* Don't move the cursor though */
2604 struct mark *m = mark_new(ci->focus);
2607 ret = call("Move-CursorXY", ci->focus, 0, m, "menu",
2608 0, NULL, NULL, ci->x, ci->y);
2613 DEF_CMD(emacs_menu_select)
2615 /* If a menu was opened it should have claimed the mouse focus
2616 * so ci->focus is now the menu. We want to activate the entry
2619 struct mark *m = mark_new(ci->focus);
2622 ret = call("Move-CursorXY", ci->focus, 0, m, "activate",
2623 0, NULL, NULL, ci->x, ci->y);
2628 DEF_CMD(emacs_motion)
2630 struct mark *p = call_ret(mark, "doc:point", ci->focus);
2631 struct mark *m2 = call_ret(mark2, "doc:point", ci->focus, 2);
2636 if (attr_find_int(m2->attrs, "emacs:track-selection") <= 0)
2637 return Efallthrough;
2639 call("Move-CursorXY", ci->focus,
2640 0, NULL, NULL, 0, NULL, NULL, ci->x, ci->y);
2642 update_sel(ci->focus, p, m2, NULL);
2646 DEF_CMD(emacs_paste)
2650 /* First commit the selection, then collect it */
2651 call("selection:commit", ci->focus);
2652 str = call_ret(strsave, "copy:get", ci->focus);
2654 call("Move-CursorXY", ci->focus,
2655 0, NULL, NULL, 0, NULL, NULL, ci->x, ci->y);
2660 call("Replace", ci->focus, 0, NULL, str);
2662 pane_take_focus(ci->focus);
2667 DEF_CMD(emacs_paste_direct)
2669 /* This command is an explicit paste command and the content
2670 * is available via "Paste:get".
2671 * It might come via the mouse (with x,y) or via a keystroke.
2674 if (ci->key[0] == 'M') {
2675 call("Move-CursorXY", ci->focus,
2676 0, NULL, NULL, 0, NULL, NULL, ci->x, ci->y);
2677 pane_take_focus(ci->focus);
2680 s = call_ret(str, "Paste:get", ci->focus);
2682 struct mark *pt = call_ret(mark, "doc:point", ci->focus);
2684 call("Move-to", ci->focus, 1);
2685 mk = call_ret(mark2, "doc:point", ci->focus);
2686 call("Replace", ci->focus, 0, mk, s, 0, pt);
2687 set_selection(ci->focus, pt, mk, 2);
2693 DEF_CMD(emacs_sel_claimed)
2695 /* Should possibly just change the color of our selection */
2696 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2698 clear_selection(ci->focus, NULL, mk, 0);
2702 DEF_CMD(emacs_sel_commit)
2704 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2705 struct mark *p = call_ret(mark, "doc:point", ci->focus);
2707 if (p && mk && !mark_same(p, mk)) {
2710 str = call_ret(strsave, "doc:get-str", ci->focus,
2714 call("copy:save", ci->focus, 0, NULL, str);
2720 DEF_CMD(emacs_readonly)
2724 ro = pane_attr_get(ci->focus, "doc-readonly");
2726 if (ro && strcmp(ro,"yes") == 0)
2727 call("doc:set:readonly", ci->focus, 0);
2729 call("doc:set:readonly", ci->focus, 1);
2733 DEF_CMD(emacs_shift)
2738 shift = pane_attr_get_int(ci->focus, "render-wrap", -1);
2739 if (strcmp(ci->key, "K:CX->") == 0 || strcmp(ci->key, "K->") == 0)
2741 if (rpt == NO_NUMERIC) {
2746 } else if (rpt == -NO_NUMERIC) {
2753 } else if (rpt >= 0) {
2758 if (shift > 0 && shift + rpt < 0)
2764 attr_set_str(&ci->focus->attrs, "render-wrap", "yes");
2766 attr_set_int(&ci->focus->attrs, "render-wrap", shift);
2768 attr_set_str(&ci->focus->attrs, "render-wrap", "0 auto");
2770 /* When reducing shift to zero, don't enable auto */
2771 attr_set_int(&ci->focus->attrs, "render-wrap", 0);
2772 call("view:changed", ci->focus);
2773 call("Mode:set-num2", ci->focus, N2_shift);
2774 call("Message:modal", ci->focus, 0, NULL, "Type < or > to shift again");
2778 DEF_CMD(emacs_shift_again)
2780 if (N2(ci) != N2_shift)
2781 return emacs_insert_func(ci);
2783 return emacs_shift_func(ci);
2786 DEF_CMD(emacs_growx)
2788 if (ci->key[strlen(ci->key)-1] == '}')
2789 call("Window:x+", ci->focus, ci->num);
2791 call("Window:x-", ci->focus, ci->num);
2792 call("Mode:set-num2", ci->focus, N2_growx);
2793 call("Message:modal", ci->focus, 0, NULL, "Type { or } to grow again");
2797 DEF_CMD(emacs_growx_again)
2799 if (N2(ci) != N2_growx)
2800 return emacs_insert_func(ci);
2802 return emacs_growx_func(ci);
2805 DEF_CMD(emacs_scale_relative)
2807 struct pane *p = ci->focus;
2808 char *sc = pane_attr_get(p, "scale:M");
2813 call("Message:modal", p, 0, NULL,
2814 "Cannot zoom display with fixed-sized font");
2817 sc = pane_attr_get(p, "scale");
2818 if (sc && strchr(sc, 'x')) {
2819 call("Message:modal", p, 0, NULL,
2820 "Cannot zoom display with fixed layout");
2828 if (ci->key[strlen(ci->key)-1] == '-')
2829 scale = 10 * scale / 12;
2831 scale = 12 * scale / 10;
2832 snprintf(num, sizeof(num)-1, "%d", scale);
2833 call("window:set:scale", p, 0, NULL, num);
2837 DEF_CMD(emacs_curs_pos)
2847 c = mark_dup(ci->mark);
2848 nxt = doc_following(ci->focus, c);
2850 while ((ch = doc_prev(ci->focus, c)) != WEOF && !is_eol(ch))
2852 while (mark_ordered_not_same(c, ci->mark)) {
2853 ch = doc_next(ci->focus, c);
2857 } else if (ch == '\t') {
2866 asprintf(&msg, "Cursor at column %d (%d chars), char is %d (0x%x)",
2867 col, chars, nxt, nxt);
2868 call("Message", ci->focus, 0, NULL, msg);
2873 DEF_CMD(emacs_word_count)
2876 struct pane *p = ci->focus;
2883 mk = call_ret(mark2, "doc:point", p);
2884 if (mk && attr_find_int(mk->attrs, "emacs:active") <= 0)
2886 call("CountLines", p, 0, ci->mark);
2887 wp = attr_find_int(ci->mark->attrs, "word");
2890 call("CountLines", p, 0, mk);
2891 wm = attr_find_int(mk->attrs, "word");
2892 asprintf(&msg, "%d words in region", abs(wp-wm));
2894 int wd = pane_attr_get_int(p, "words", 0);
2895 asprintf(&msg, "After word%s %d of %d", wp==2?"":"s", wp-1, wd);
2897 call("Message", p, 0, NULL, msg);
2904 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2905 struct mark *p = call_ret(mark, "doc:point", ci->focus);
2908 if (!clear_selection(ci->focus, p, mk, 0))
2911 if (strcmp(ci->key, "K:A-q") == 0) {
2912 if (call("fill-paragraph", ci->focus, ci->num, p, NULL,
2913 0, mk) == Efallthrough) {
2914 p2 = call_ret(pane, "attach-textfill", ci->focus);
2916 call("fill-paragraph", p2, ci->num, p, NULL,
2920 /* Don't try to load anything, the file-type should
2921 * have loaded something if relevant
2923 if (call("reindent-paragraph", ci->focus, ci->num, p, NULL,
2925 call("Message", ci->focus, 0, NULL,
2926 "Reindent not supported on the document.");
2931 DEF_CMD(emacs_abbrev)
2933 call("attach-abbrev", ci->focus);
2937 DEF_CMD(emacs_showinput)
2939 struct pane *p, *doc;
2941 if (call("input:log", ci->focus) <= 0) {
2942 call("Message", ci->focus, 0, NULL,
2943 "Cannot get log of recent input.");
2947 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL, "*Debug Log*");
2949 call("interactive-cmd-view-log", ci->focus);
2950 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL,
2955 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM");
2957 p = home_call_ret(pane, doc, "doc:attach-view", p, 1);
2959 call("doc:file", p, 1);
2964 DEF_CMD(emacs_macro_start)
2968 ret = call("macro:capture", ci->focus);
2970 call("Message", ci->focus, 0, NULL,
2971 "Macro capture already happening");
2973 call("Message", ci->focus, 0, NULL,
2974 "Macro facility not available");
2978 DEF_CMD(emacs_macro_stop)
2982 ret = call("macro:finished", ci->focus, 2);
2984 call("Message", ci->focus, 0, NULL,
2985 "Macro successfully created.");
2986 else if (ret == Efalse)
2987 call("Message", ci->focus, 0, NULL,
2988 "No macro being created.");
2990 call("Message", ci->focus, 0, NULL,
2991 "Failure creating macro.");
2995 DEF_CMD(emacs_macro_run)
2997 int cnt = RPT_NUM(ci);
2999 if (strcmp(ci->key, "K-e") == 0 && N2(ci) != N2_runmacro)
3000 return emacs_insert_func(ci);
3005 call("macro:replay", ci->focus, 1) > 0)
3008 call("Mode:set-num2", ci->focus, N2_runmacro);
3009 call("Message:modal", ci->focus, 0, NULL, "Type 'e' to repeat macro");
3010 return cnt < 1 ? 1 : Efail;
3019 static const char spell_choices[] =
3020 "0123456789-=;,/ABCDEFGHIJKLMNOPQRSTUVWXYZ";
3021 DEF_CB(get_suggestion)
3023 struct bb *b = container_of(ci->comm, struct bb, c);
3029 buf_concat(&b->b, " - (a)ccept, (i)nsert - ");
3031 buf_concat(&b->b, ", ");
3033 if (b->count < (int)sizeof(spell_choices)-1) {
3034 buf_append(&b->b, '(');
3035 buf_append(&b->b, spell_choices[b->count]);
3036 buf_append(&b->b, ')');
3039 buf_concat(&b->b, ci->str);
3043 DEF_CMD(emacs_spell)
3048 int rpt = RPT_NUM(ci);
3053 /* We always find a word that is partly *after* the given
3054 * make, but we want to find the word before point, so step
3057 doc_prev(ci->focus, ci->mark);
3059 if (ci->num != NO_NUMERIC)
3060 /* As a repeat-count was given, only look at intersting words */
3061 call("Spell:NextWord", ci->focus, 0, ci->mark);
3062 st = mark_dup(ci->mark);
3063 word = call_ret(str, "Spell:ThisWord", ci->focus, 0, ci->mark, NULL, 0, st);
3064 if (!word || !*word) {
3066 call("Message", ci->focus, 0, NULL,
3067 "Spell check reached end-of-file");
3068 call("Spell:Save", ci->focus);
3073 ret = call("Spell:Check", ci->focus, 0, NULL, word);
3079 call("Message", ci->focus, 0, NULL,
3080 strconcat(ci->focus, "\"", word,
3081 "\" is a correct spelling."));
3082 } else if (ret == Efalse) {
3085 buf_concat(&b.b, "\"");
3086 buf_concat(&b.b, word);
3087 buf_concat(&b.b, "\" is NOT correct");
3090 b.c = get_suggestion;
3091 call_comm("Spell:Suggest", ci->focus, &b.c,
3094 buf_concat(&b.b, " ... no suggestions");
3096 attr_set_str(&ci->focus->attrs, "spell:last-error",
3098 attr_set_str(&ci->focus->attrs, "spell:suggestions",
3100 attr_set_int(&ci->focus->attrs, "spell:offset", 0);
3102 call("Mode:set-all", ci->focus, rpt-1, NULL, ":Spell");
3103 call("Message:modal", ci->focus, 0, NULL, buf_final(&b.b));
3104 free(buf_final(&b.b));
3105 } else if (ret == Efail) {
3110 call("Message", ci->focus, 0, NULL,
3111 strconcat(ci->focus, "\"", word,
3112 "\" is not a word."));
3114 call("Message", ci->focus, 0, NULL,
3115 strconcat(ci->focus, "Spell check failed for \"", word,
3121 DEF_CMD(emacs_spell_choose)
3123 const char *k = ksuffix(ci, "K:Spell-");
3124 char match[4] = "( )";
3125 char *suggest = attr_find(ci->focus->attrs,
3126 "spell:suggestions");
3127 char *last = attr_find(ci->focus->attrs,
3128 "spell:last-error");
3133 if (!*k || !suggest || !last || !ci->mark)
3136 cp = strstr(suggest, match);
3140 ep = strchr(cp, ',');
3142 cp = strnsave(ci->focus, cp, ep-cp);
3144 m = mark_dup(ci->mark);
3145 i = utf8_strlen(last);
3147 doc_prev(ci->focus, m);
3150 call("doc:replace", ci->focus, 0, m, cp, 0, ci->mark);
3151 attr_set_str(&ci->focus->attrs, "spell:suggestions", NULL);
3152 attr_set_str(&ci->focus->attrs, "spell:last-error", NULL);
3155 doc_next(ci->focus, ci->mark);
3156 home_call(ci->home, "emacs:respell", ci->focus,
3157 ci->num, ci->mark, NULL,
3158 ci->num2, ci->mark2);
3164 DEF_CMD(emacs_spell_abort)
3169 DEF_CMD(emacs_spell_skip)
3172 doc_next(ci->focus, ci->mark);
3173 home_call(ci->home, "emacs:respell", ci->focus,
3174 ci->num, ci->mark, NULL,
3175 ci->num2, ci->mark2);
3180 DEF_CMD(emacs_spell_insert)
3182 char *last = attr_find(ci->focus->attrs,
3183 "spell:last-error");
3185 attr_set_str(&ci->focus->attrs, "spell:suggestions", NULL);
3186 attr_set_str(&ci->focus->attrs, "spell:last-error", NULL);
3187 call("Spell:AddWord", ci->focus, 1, NULL, last);
3191 doc_next(ci->focus, ci->mark);
3192 home_call(ci->home, "emacs:respell", ci->focus,
3193 ci->num, ci->mark, NULL,
3194 ci->num2, ci->mark2);
3199 DEF_CMD(emacs_spell_accept)
3201 char *last = attr_find(ci->focus->attrs,
3202 "spell:last-error");
3204 attr_set_str(&ci->focus->attrs, "spell:suggestions", NULL);
3205 attr_set_str(&ci->focus->attrs, "spell:last-error", NULL);
3206 call("Spell:AddWord", ci->focus, 0, NULL, last);
3210 doc_next(ci->focus, ci->mark);
3211 home_call(ci->home, "emacs:respell", ci->focus,
3212 ci->num, ci->mark, NULL,
3213 ci->num2, ci->mark2);
3218 static int spell_shift(struct pane *focus safe, int num, int inc)
3220 int o = pane_attr_get_int(focus, "spell:offset", 0);
3226 msg = pane_attr_get(focus, "spell:suggestions");
3229 for (i = 0; i < o && (c = strchr(msg, ',')) != NULL; i++)
3231 attr_set_int(&focus->attrs, "spell:offset", i);
3233 call("Mode:set-all", focus, num, NULL, ":Spell");
3234 call("Message:modal", focus, 0, NULL, msg);
3238 DEF_CMD(emacs_spell_left)
3240 return spell_shift(ci->focus, ci->num, -1);
3243 DEF_CMD(emacs_spell_right)
3245 return spell_shift(ci->focus, ci->num, 1);
3248 DEF_CMD(emacs_quote)
3253 struct mark *mk = NULL;
3254 bool free_mark = False;
3256 if (ci->num >= 0 && ci->num < NO_NUMERIC)
3258 else if (N2(ci) == N2_uniquote && ci->mark &&
3259 (str = attr_find(ci->mark->attrs, "emacs:unicode_char")) != NULL) {
3260 struct call_return cr;
3262 if (ci->num < 0 && i > 1)
3266 cr = call_ret(all, "Unicode-names", ci->focus, i, NULL, str);
3267 if (cr.s && cr.i && (wint_t)cr.i != WEOF) {
3269 call("Message", ci->focus, 0, NULL,
3270 strconcat(ci->focus,
3271 "Unicode char <", cr.s, ">"));
3272 call("Mode:set-num2", ci->focus,
3273 N2_uniquote | (i << 16));
3274 mk = mark_dup(ci->mark);
3275 doc_prev(ci->focus, mk);
3278 call("Message", ci->focus, 0, NULL,
3279 strconcat(ci->focus,
3280 "Cannot find another character <", str, ">"));
3283 } else if (wch == WEOF && ci->mark &&
3284 (mk = call_ret(mark2, "doc:point", ci->focus)) != NULL &&
3285 clear_selection(ci->focus, NULL, mk, 0) &&
3286 (str = call_ret(strsave, "doc:get-str", ci->focus,
3287 0, NULL, NULL, 0, mk)) != NULL) {
3292 x = strtoul(str, &ep, 16);
3293 if (ep && *ep == 0) {
3295 call("Message", ci->focus, 0, NULL,
3296 strconcat(ci->focus, "Hex code 0x", str));
3298 struct call_return cr;
3299 cr = call_ret(all, "Unicode-names", ci->focus,
3303 call("Message", ci->focus, 0, NULL,
3304 strconcat(ci->focus,
3305 "Unicode char <", cr.s, ">"));
3307 attr_set_str(&ci->mark->attrs,
3308 "emacs:unicode_char",
3310 call("Mode:set-num2", ci->focus,
3311 N2_uniquote | (1 << 16));
3314 call("Message", ci->focus, 0, NULL,
3315 strconcat(ci->focus,
3316 "Cannot find character <", str, ">"));
3322 call("Mode:set-all", ci->focus, ci->num, NULL, ":CQ", ci->num2);
3325 call("Replace", ci->focus, 0, mk, put_utf8(b, wch));
3331 struct docs_helper {
3334 struct pane *p safe;
3337 DEF_CMD(emacs_doc_menu)
3339 const char *d = ksuffix(ci, "emacs:doc-menu:");
3340 struct pane *p = call_ret(pane, "docs:byname", ci->focus,
3344 struct pane *t = call_ret(pane, "ThisPane", ci->focus);
3346 home_call(p, "doc:attach-view", t, 1);
3351 DEF_CB(emacs_menu_add_doc)
3353 struct docs_helper *dh = container_of(ci->comm, struct docs_helper, c);
3354 char *name = pane_attr_get(ci->focus, "doc-name");
3357 return Efallthrough;
3361 call("menu:add", dh->p, 0, NULL, name, 0, NULL,
3362 strconcat(ci->home, " emacs:doc-menu:", name));
3363 return Efallthrough;
3366 DEF_CMD(emacs_menu_refresh)
3368 struct pane *p = ci->focus;
3369 char *n = pane_attr_get(p, "doc-name");
3370 struct docs_helper dh;
3372 if (!n || strcmp(n, "Documents") != 0)
3375 call("menu:clear", p);
3376 dh.c = emacs_menu_add_doc;
3379 call_comm("docs:byeach", p, &dh.c);
3380 call("menu:add", p, 0, NULL, "List all", 0, NULL, ":C-X :C-B");
3384 DEF_PFX_CMD(cx_cmd, ":CX");
3385 DEF_PFX_CMD(cx4_cmd, ":CX4");
3386 DEF_PFX_CMD(cx5_cmd, ":CX5");
3387 DEF_PFX_CMD(cx44_cmd, ":CX44");
3388 DEF_PFX_CMD(cc_cmd, ":CC");
3389 DEF_PFX_CMD(help_cmd, ":Help");
3391 static void emacs_init(void)
3399 key_add(m, "K:C-X", &cx_cmd.c);
3400 key_add(m, "K:CX-4", &cx4_cmd.c);
3401 /* C-\ is generated by C-4. Weird... */
3402 key_add(m, "K:CX:C-\\", &cx4_cmd.c);
3403 key_add(m, "K:CX-5", &cx5_cmd.c);
3404 key_add(m, "K:CX4-4", &cx44_cmd.c);
3405 key_add(m, "K:CX4:C-\\", &cx44_cmd.c);
3406 key_add(m, "K:C-C", &cc_cmd.c);
3407 key_add(m, "K:F1", &help_cmd.c);
3409 key_add(m, "K:C-Q", &emacs_quote);
3411 for (i = 0; i < ARRAY_SIZE(move_commands); i++) {
3412 struct move_command *mc = &move_commands[i];
3413 key_add(m, mc->k1, &mc->cmd);
3415 key_add(m, mc->k2, &mc->cmd);
3417 key_add(m, mc->k3, &mc->cmd);
3420 for (i = 0; i < ARRAY_SIZE(simple_commands); i++) {
3421 struct simple_command *sc = &simple_commands[i];
3422 key_add(m, sc->k, &sc->cmd);
3425 key_add_range(m, "K- ", "K-~", &emacs_insert);
3426 key_add_range(m, "K-\200", "K-\377\377\377\377", &emacs_insert);
3427 key_add(m, "K:Tab", &emacs_insert_other);
3428 //key_add(m, "K:LF", &emacs_insert_other);
3429 key_add(m, "K:Enter", &emacs_insert_other);
3430 key_add(m, "K:C-O", &emacs_insert_other);
3431 key_add(m, "Interactive:insert", &emacs_interactive_insert);
3432 key_add(m, "Interactive:delete", &emacs_interactive_delete);
3434 key_add(m, "K:C-_", &emacs_undo);
3435 key_add(m, "K:CX-u", &emacs_undo);
3436 key_add(m, "K:C-/", &emacs_undo);
3437 key_add(m, "K:C-Z", &emacs_undo);
3439 key_add(m, "K:C-L", &emacs_recenter);
3441 key_add(m, "K:CX:C-F", &emacs_findfile);
3442 key_add(m, "K:CX4:C-F", &emacs_findfile);
3443 key_add(m, "K:CX4-f", &emacs_findfile);
3444 key_add(m, "K:CX44-f", &emacs_findfile);
3445 key_add_prefix(m, "File Found:", &emacs_findfile);
3447 key_add(m, "K:CX:C-W", &emacs_writefile);
3448 key_add_prefix(m, "emacs:write_file:", &emacs_do_writefile);
3450 key_add(m, "K:CX-i", &emacs_insertfile);
3451 key_add_prefix(m, "emacs:insert_file:", &emacs_do_insertfile);
3453 key_add(m, "K:CX-b", &emacs_finddoc);
3454 key_add(m, "K:CX4-b", &emacs_finddoc);
3455 key_add(m, "K:CX44-b", &emacs_finddoc);
3456 key_add_prefix(m, "Doc Found:", &emacs_finddoc);
3458 key_add(m, "K:CX:C-B", &emacs_viewdocs);
3459 key_add(m, "K:CX4:C-B", &emacs_viewdocs);
3460 key_add(m, "K:CX44:C-B", &emacs_viewdocs);
3462 key_add(m, "K:CX-k", &emacs_kill_doc);
3464 key_add(m, "K:CX-s", &emacs_save_all);
3466 key_add(m, "K:CX:C-V", &emacs_revisit);
3468 key_add(m, "K:CX-=", &emacs_curs_pos);
3469 key_add(m, "K:A-=", &emacs_word_count);
3471 key_add(m, "K:CX-<", &emacs_shift);
3472 key_add(m, "K:CX->", &emacs_shift);
3473 key_add(m, "K-<", &emacs_shift_again);
3474 key_add(m, "K->", &emacs_shift_again);
3476 key_add(m, "K:CX-{", &emacs_growx);
3477 key_add(m, "K:CX-}", &emacs_growx);
3478 key_add(m, "K-{", &emacs_growx_again);
3479 key_add(m, "K-}", &emacs_growx_again);
3481 key_add(m, "K:CX:C-=", &emacs_scale_relative);
3482 key_add(m, "K:CX:C--", &emacs_scale_relative);
3484 key_add(m, "K:C-S", &emacs_start_search);
3485 key_add(m, "K:C-R", &emacs_start_search);
3486 key_add(m, "K:A-%", &emacs_start_search);
3487 key_add(m, "render:reposition", &emacs_reposition);
3489 key_add(m, "K:CX:C-C", &emacs_exit);
3490 key_add(m, "emacs:deactivate", &emacs_deactivate);
3492 key_add(m, "K:C-U", &emacs_prefix);
3494 key_add(m, "K:A-!", &emacs_shell);
3495 key_add(m, "K:A-|", &emacs_shell);
3496 key_add(m, "Shell Command", &emacs_shell);
3498 key_add(m, "K:CX-`", &emacs_next_match);
3499 key_add(m, "K-`", &emacs_match_again);
3501 key_add(m, "K:CX-1", &emacs_close_others);
3502 key_add(m, "K-1", &emacs_close_others);
3504 key_add_range(m, "K:A-0", "K:A-9", &emacs_num);
3505 key_add(m, "K:A--", &emacs_neg);
3506 key_add(m, "K:C--", &emacs_neg);
3507 key_add(m, "K:C- ", &emacs_mark);
3508 key_add(m, "mode-set-mark", &emacs_mark);
3509 key_add(m, "mode-swap-mark", &emacs_swap_mark);
3510 key_add(m, "Abort", &emacs_abort);
3511 key_add(m, "Cancel", &emacs_abort);
3512 key_add(m, "K:C-W", &emacs_wipe);
3513 key_add(m, "K:A-w", &emacs_copy);
3514 key_add(m, "K:C-Y", &emacs_yank);
3515 key_add(m, "K:A-y", &emacs_yank_pop);
3516 key_add(m, "map-attr", &emacs_attrs);
3517 key_add(m, "emacs:selection-menu", &emacs_selection_menu);
3518 key_add(m, "emacs:selection-menu-action", &emacs_selection_menu_action);
3520 key_add(m, "K:A-g", &emacs_goto_line);
3521 key_add(m, "K:A-x", &emacs_command);
3522 key_add(m, "K:A-X", &emacs_command);
3523 key_add(m, "K:CC-m", &emacs_make);
3524 key_add(m, "K:CC:C-M", &emacs_make);
3526 key_add(m, "K:A:C-V", &emacs_move_view_other);
3528 key_add(m, "K:CX:C-Q", &emacs_readonly);
3530 key_add_prefix(m, "K:CQ-", &emacs_quote_insert);
3531 key_add_prefix(m, "K:CQ:C-", &emacs_quote_insert);
3533 key_add(m, "K:A-q", &emacs_fill);
3534 key_add(m, "K:A:C-Q", &emacs_fill);
3535 key_add(m, "K:A-/", &emacs_abbrev);
3536 key_add(m, "K:A-;", &emacs_spell);
3537 key_add(m, "emacs:respell", &emacs_spell);
3538 key_add_prefix(m, "K:Spell-", &emacs_spell_choose);
3539 key_add(m, "K:Spell-a", &emacs_spell_accept);
3540 key_add(m, "K:Spell-i", &emacs_spell_insert);
3541 key_add(m, "K:Spell- ", &emacs_spell_skip);
3542 key_add(m, "K:Spell:Enter", &emacs_spell_abort);
3543 key_add(m, "K:Spell:ESC", &emacs_spell_abort);
3544 key_add(m, "K:Spell:Left", &emacs_spell_left);
3545 key_add(m, "K:Spell:C-B", &emacs_spell_left);
3546 key_add(m, "K:Spell:Right", &emacs_spell_right);
3547 key_add(m, "K:Spell:C-F", &emacs_spell_right);
3549 key_add(m, "K:Help-l", &emacs_showinput);
3551 key_add(m, "emacs:command", &emacs_do_command);
3552 key_add(m, "interactive-cmd-version", &emacs_version);
3553 key_add(m, "interactive-cmd-log", &emacs_log);
3555 key_add(m, "M:Press-1", &emacs_press);
3556 key_add(m, "M:Release-1", &emacs_release);
3557 key_add(m, "M:Press-3", &emacs_menu_open);
3558 key_add(m, "M:Release-3", &emacs_menu_select);
3559 key_add(m, "M:DPress-1", &emacs_press);
3560 key_add(m, "M:Click-2", &emacs_paste);
3561 key_add(m, "M:C:Click-1", &emacs_paste);
3562 key_add(m, "M:Motion", &emacs_motion);
3563 key_add(m, "K:Paste", &emacs_paste_direct);
3564 key_add(m, "M:Paste", &emacs_paste_direct);
3566 key_add(m, "Notify:selection:claimed", &emacs_sel_claimed);
3567 key_add(m, "Notify:selection:commit", &emacs_sel_commit);
3569 key_add(m, "K:CX-(", &emacs_macro_start);
3570 key_add(m, "K:CX-)", &emacs_macro_stop);
3571 key_add(m, "K:CX-e", &emacs_macro_run);
3572 key_add(m, "K-e", &emacs_macro_run);
3574 key_add(m, "menu:refresh", &emacs_menu_refresh);
3575 key_add_prefix(m, "emacs:doc-menu:", &emacs_doc_menu);
3580 DEF_LOOKUP_CMD(mode_emacs, emacs_map);
3582 static char *menus[][3] = {
3583 { "Help/Recent", ":F1 l", "R" },
3584 { "Documents/List all", ":C-X :C-B", "R"},
3585 { "File/Open", ":C-X :C-F", "L"},
3586 { "File/Save", ":C-X :C-S", "L"},
3587 { "File/Exit", ":C-X :C-C", "L"},
3588 { "Edit/Copy", ":A-w", "L"},
3591 DEF_CMD(attach_mode_emacs)
3594 call_comm("global-set-keymap", ci->focus, &mode_emacs.c);
3595 for (i = 0; i < ARRAY_SIZE(menus); i++)
3596 call("menubar-add", ci->focus,
3597 menus[i][2][0] == 'R' ? 2 : 0,
3599 0, NULL, menus[i][1]);
3603 DEF_CMD(attach_file_entry)
3605 /* The 'type' passed must be static, not allocated */
3606 char *type = "shellcmd";
3608 if (ci->str && strcmp(ci->str, "file") == 0)
3610 else if (ci->str && strcmp(ci->str, "doc") == 0)
3612 pane_register(ci->focus, 0, &find_handle.c, type);
3617 void edlib_init(struct pane *ed safe)
3621 call_comm("global-set-command", ed, &attach_mode_emacs, 0, NULL, "attach-mode-emacs");
3622 call_comm("global-set-command", ed, &attach_file_entry, 0, NULL, "attach-file-entry");
3623 call_comm("global-set-command", ed, &emacs_shell, 0, NULL, "attach-shell-prompt");
3624 call_comm("global-set-command", ed, &emacs_selection_menu_add,
3625 0, NULL, "selection-menu:add-00-emacs");