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
9 #define _GNU_SOURCE /* for asprintf */
20 #define PANE_DATA_PTR_TYPE char *
22 #include "core-pane.h"
24 static const char * safe file_normalize(struct pane *p safe, const char *path,
25 const char *initial_path);
27 /* num2 is used to track if successive commands are related.
28 * Only low 16 bits identify command, other bits are free.
32 N2_undo_insert, /* adjacent commands form a single undo set */
33 N2_undo_delete, /* adjacent commands form a single undo set */
34 N2_undo_change, /* adjacent commands form a single undo set */
35 N2_recentre, /* repeated recentre goes to different places */
36 N2_yank, /* repeated yank-pop takes different result */
37 N2_match, /* repeated ` after Cx` repeats the search */
38 N2_undo, /* Last command was 'undo' too */
39 N2_close_others,/* Last command was close-other, just a 1 can repeat */
40 N2_runmacro, /* Last command was CX-e, just an 'e' can repeat */
41 N2_shift, /* Last command was CX-< or CX-> */
42 N2_growx, /* Last command was CX-{ or CX-} */
43 N2_uniquote, /* Last command was :C-q inserting a unicode from name */
45 static inline int N2(const struct cmd_info *ci safe)
47 return ci->num2 & 0xffff;
50 static inline int N2a(const struct cmd_info *ci safe)
52 return ci->num2 >> 16;
55 REDEF_CMD(emacs_move);
56 REDEF_CMD(emacs_delete);
57 REDEF_CMD(emacs_kill);
58 REDEF_CMD(emacs_case);
59 REDEF_CMD(emacs_swap);
61 static struct move_command {
65 char *k1 safe, *k2, *k3;
67 {CMD(emacs_move), "Move-Char", 1, 0,
68 "K:C-F", "K:Right", NULL},
69 {CMD(emacs_move), "Move-Char", -1, 0,
70 "K:C-B", "K:Left", NULL},
71 {CMD(emacs_move), "doc:word", 1, 0,
72 "K:A-f", "K:A:Right", NULL},
73 {CMD(emacs_move), "doc:word", -1, 0,
74 "K:A-b", "K:A:Left", NULL},
75 {CMD(emacs_move), "doc:expr", 1, 0,
76 "K:A:C-F", NULL, NULL},
77 {CMD(emacs_move), "doc:expr", -1, 0,
78 "K:A:C-B", NULL, NULL},
79 {CMD(emacs_move), "doc:expr", -1, 1,
80 "K:A:C-U", NULL, NULL},
81 {CMD(emacs_move), "doc:expr", 1, 1,
82 "K:A:C-D", NULL, NULL},
83 {CMD(emacs_move), "doc:WORD", 1, 0,
85 {CMD(emacs_move), "doc:WORD", -1, 0,
87 {CMD(emacs_move), "doc:EOL", 1, 0,
88 "K:C-E", "K:End", NULL},
89 {CMD(emacs_move), "doc:EOL", -1, 0,
90 "K:C-A", "K:Home", NULL},
91 {CMD(emacs_move), "Move-Line", -1, 0,
92 "K:C-P", "K:Up", NULL},
93 {CMD(emacs_move), "Move-Line", 1, 0,
94 "K:C-N", "K:Down", NULL},
95 {CMD(emacs_move), "doc:file", 1, 0,
96 "K:A->", "K:S:End", NULL},
97 {CMD(emacs_move), "doc:file", -1, 0,
98 "K:A-<", "K:S:Home", NULL},
99 {CMD(emacs_move), "Move-View", 900, 0,
100 "K:Next", "K:C-V", "emacs-move-large-other"},
101 {CMD(emacs_move), "Move-View", -900, 0,
102 "K:Prior", "K:A-v", NULL},
104 {CMD(emacs_move), "doc:paragraph", -1, 0,
105 "K:A:C-A", NULL, NULL},
106 {CMD(emacs_move), "doc:paragraph", 1, 0,
107 "K:A:C-E", NULL, NULL},
109 {CMD(emacs_delete), "Move-Char", 1, 0,
110 "K:C-D", "K:Del", "del"},
111 {CMD(emacs_delete), "Move-Char", -1, 0,
112 "K:C-H", "K:Backspace", "K:Delete"},
113 {CMD(emacs_delete), "doc:word", 1, 0,
114 "K:A-d", NULL, NULL},
115 {CMD(emacs_delete), "doc:word", -1, 0,
116 "K:A:C-H", "K:A:Backspace", NULL},
117 {CMD(emacs_kill), "doc:EOL", 1, 0,
118 "K:C-K", NULL, NULL},
119 {CMD(emacs_kill), "doc:expr", 1, 0,
120 "K:A:C-K", NULL, NULL},
122 {CMD(emacs_case), "Ldoc:word", 1, 0,
123 "K:A-l", "K:A-L", NULL},
124 {CMD(emacs_case), "Udoc:word", 1, 0,
125 "K:A-u", "K:A-U", NULL},
126 {CMD(emacs_case), "Cdoc:word", 1, 0,
127 "K:A-c", "K:A-C", NULL},
128 {CMD(emacs_case), "TMove-Char", 1, 0,
129 "K:A-`", NULL, NULL},
131 {CMD(emacs_swap), "Move-Char", 1, 0,
132 "K:C-T", NULL, NULL},
133 {CMD(emacs_swap), "doc:word", 1, 0,
134 "K:A-t", NULL, NULL},
135 {CMD(emacs_swap), "doc:WORD", 1, 0,
136 "K:A-T", NULL, NULL},
139 REDEF_CMD(emacs_move)
141 struct move_command *mv = container_of(ci->comm, struct move_command, cmd);
142 struct pane *cursor_pane = ci->focus;
149 /* if doc:file, leave inactive mark behind */
150 if (strcmp(mv->type, "doc:file") == 0) {
151 mk = call_ret(mark2, "doc:point", ci->focus);
153 /* Don't change selection:active */
154 mark_to_mark(mk, ci->mark);
156 call("Move-to", ci->focus, 1, ci->mark);
157 mk = call_ret(mark2, "doc:point", ci->focus);
161 ret = call(mv->type, ci->focus, mv->direction * RPT_NUM(ci), ci->mark,
166 if (strcmp(mv->type, "Move-View") == 0)
167 attr_set_int(&cursor_pane->attrs, "emacs-repoint",
170 mk = call_ret(mark2, "doc:point", ci->focus);
171 /* Discard a transient selection */
172 call("selection:clear", ci->focus, 2, mk);
177 REDEF_CMD(emacs_delete)
179 struct move_command *mv = container_of(ci->comm, struct move_command, cmd);
186 mk = call_ret(mark2, "doc:point", ci->focus);
187 /* If selection is replacable, clear it and use mk */
188 if (call("selection:clear", ci->focus, 3, mk) == Efalse) {
189 /* else clear any transient selection */
190 call("selection:clear", ci->focus, 2, mk);
194 m = mark_dup(ci->mark);
196 ret = call(mv->type, ci->focus, mv->direction * RPT_NUM(ci), m);
203 call("Replace", ci->focus, 1, mk, NULL, 0, mk);
205 ret = call("Replace", ci->focus, 1, m, NULL, N2(ci) == N2_undo_delete);
207 call("Mode:set-num2", ci->focus, N2_undo_delete);
212 REDEF_CMD(emacs_kill)
214 /* Like delete, but copy to copy-buffer */
215 struct move_command *mv = container_of(ci->comm, struct move_command, cmd);
223 m = mark_dup(ci->mark);
225 if (strcmp(mv->type, "doc:EOL") == 0 &&
226 mv->direction == 1 && RPT_NUM(ci) == 1 &&
227 is_eol(doc_following(ci->focus, m)))
228 ret = call("Move-Char", ci->focus, mv->direction * RPT_NUM(ci), m);
230 ret = call(mv->type, ci->focus, mv->direction * RPT_NUM(ci), m);
236 /* Remove any selection so it cannot be claimed and so replace this copy */
237 call("selection:claim", ci->focus);
238 call("selection:discard", ci->focus);
240 str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, m);
242 call("copy:save", ci->focus, N2(ci) == N2_undo_delete, NULL, str);
243 ret = call("Replace", ci->focus, 1, m, NULL, N2(ci) == N2_undo_delete);
245 call("Mode:set-num2", ci->focus, N2_undo_delete);
250 REDEF_CMD(emacs_case)
252 struct move_command *mv = container_of(ci->comm, struct move_command, cmd);
254 struct mark *start = NULL;
255 int cnt = mv->direction * RPT_NUM(ci);
268 start = mark_dup(ci->mark);
272 struct mark *m = mark_dup(ci->mark);
274 ret = call(mv->type+1, ci->focus, dir, ci->mark);
275 if (ret <= 0 || mark_same(ci->mark, m))
276 /* Hit end of file */
279 char *str = call_ret(str, "doc:get-str", ci->focus,
286 for (c = str; c && *c; c += 1) {
287 char type = mv->type[0];
289 type = found ? 'L' : 'U';
294 default: /* silence compiler */
295 case 'U': /* Uppercase */
301 case 'L': /* Lowercase */
307 case 'T': /* Toggle */
311 } else if (islower(*c)) {
319 ret = call("Replace", ci->focus, 1, m, str,
320 N2(ci) == N2_undo_change);
322 call(mv->type+1, ci->focus, dir, ci->mark);
325 if (changed || N2(ci) == N2_undo_change)
326 call("Mode:set-num2", ci->focus, N2_undo_change);
331 /* When moving forward, move point. When backward, leave point alone */
333 mark_to_mark(ci->mark, start);
339 REDEF_CMD(emacs_swap)
341 /* collect the object behind point and insert it after the object
342 * after point. With a +ve repeat count, insert after n objects.
343 * With -ve repeast, collect object after and insert behind n
344 * previous objects. Object is determined by mv->type.
346 struct move_command *mv = container_of(ci->comm, struct move_command, cmd);
347 struct mark *start = NULL;
348 int cnt = mv->direction * RPT_NUM(ci);
361 start = mark_dup(ci->mark);
365 struct mark *as, *ae, *bs, *be;
368 call(mv->type, ci->focus, -dir, ci->mark);
369 as = mark_dup(ci->mark);
370 call(mv->type, ci->focus, dir, ci->mark);
371 ae = mark_dup(ci->mark);
372 /* as to ae is the object to be moved. */
374 call(mv->type, ci->focus, dir, ci->mark);
375 be = mark_dup(ci->mark);
376 call(mv->type, ci->focus, -dir, ci->mark);
377 bs = mark_dup(ci->mark);
378 /* bs to be is the object to be swapped with.
379 * bs must be after ae in the direction
381 if (mark_same(as, ae) ||
383 bs->seq - ae->seq * dir < 0) {
384 mark_to_mark(ci->mark, ae);
391 astr = call_ret(str, "doc:get-str", ci->focus,
394 bstr = call_ret(str, "doc:get-str", ci->focus,
397 mark_to_mark(ci->mark, ae);
398 call("Replace", ci->focus, 1, as, bstr);
399 mark_to_mark(ci->mark, be);
400 call("Replace", ci->focus, 1, bs, astr, 1);
402 call(mv->type, ci->focus, dir, ci->mark);
411 /* When moving forward, move point. When backward, leave point alone */
413 mark_to_mark(ci->mark, start);
419 DEF_CMD(emacs_move_view_other)
421 /* If there is an 'other' pane', Send "K:Next" there */
424 /* '512' means 'fail if no other pane' */
425 p = call_ret(pane, "OtherPane", ci->focus, 512);
428 call("Mode:set-num", p, ci->num);
429 call("Keystroke", p, 0, NULL, "emacs-move-large-other");
433 DEF_CMD(emacs_recenter)
436 if (ci->num == NO_NUMERIC && N2(ci) == N2_recentre) {
437 /* Repeated command - go to top, or bottom, or middle in order */
440 case 0: /* was center, go to top */
441 call("Move-View-Line", ci->focus, 1, ci->mark);
444 case 1: /* was top, go to bottom */
445 call("Move-View-Line", ci->focus, -1, ci->mark);
448 case 2: /* was bottom, go to middle */
449 call("Move-View-Line", ci->focus, 0, ci->mark);
453 } else if (ci->num == -NO_NUMERIC) {
454 /* Move point to bottom */
455 call("Move-View-Line", ci->focus, -1, ci->mark);
457 } else if (ci->num != NO_NUMERIC) {
458 /* Move point to display line N */
459 call("Move-View-Line", ci->focus, ci->num, ci->mark);
461 /* Move point to middle and refresh */
462 call("Move-View-Line", ci->focus, 0, ci->mark);
463 call("window:refresh", ci->focus);
465 call("Mode:set-num2", ci->focus, N2_recentre | (step << 16));
469 REDEF_CMD(emacs_simple);
470 REDEF_CMD(emacs_simple_num);
471 REDEF_CMD(emacs_simple_str);
472 static struct simple_command {
476 } simple_commands[] = {
477 {CMD(emacs_simple), "Window:next", "K:CX-o"},
478 {CMD(emacs_simple), "Window:prev", "K:CX-O"},
479 {CMD(emacs_simple), "Window:y+", "K:CX-^"},
480 {CMD(emacs_simple), "Window:split-y", "K:CX-2"},
481 {CMD(emacs_simple), "Window:split-x", "K:CX-3"},
482 {CMD(emacs_simple), "Window:close", "K:CX-0"},
483 {CMD(emacs_simple), "Window:bury", "K:A-B"},
484 {CMD(emacs_simple), "window:new", "K:CX5-2"},
485 {CMD(emacs_simple), "window:close", "K:CX5-0"},
486 {CMD(emacs_simple), "lib-server:done", "K:CX-#"},
487 {CMD(emacs_simple), "mode-swap-mark", "K:CX:C-X"},
488 {CMD(emacs_simple), "Abort", "K:C-G"},
489 {CMD(emacs_simple), "Cancel", "K:ESC"},
490 {CMD(emacs_simple), "NOP", "K:A-G"},
491 {CMD(emacs_simple), "NOP", "K:CX:C-G"},
492 {CMD(emacs_simple), "NOP", "K:CX4:C-G"},
493 {CMD(emacs_simple), "doc:save-file", "K:CX:C-S"},
494 {CMD(emacs_simple), "Commit", "K:CC:C-C"},
495 /* one day, this will be "find definition", now it is same as "find any" */
496 {CMD(emacs_simple_num), "interactive-cmd-git-grep", "K:CX:A-."},
497 {CMD(emacs_simple_str), "interactive-cmd-git-grep", "K:A-."},
498 {CMD(emacs_simple), "interactive-cmd-merge-mode", "K:A-m"},
499 {CMD(emacs_simple_str), "interactive-cmd-calc-replace", "K:A-#"},
502 REDEF_CMD(emacs_simple)
504 struct simple_command *sc = container_of(ci->comm, struct simple_command, cmd);
509 return call(sc->type, ci->focus, ci->num, ci->mark);
512 REDEF_CMD(emacs_simple_num)
514 struct simple_command *sc = container_of(ci->comm, struct simple_command, cmd);
519 return call(sc->type, ci->focus, RPT_NUM(ci), ci->mark);
522 REDEF_CMD(emacs_simple_str)
524 struct simple_command *sc = container_of(ci->comm, struct simple_command, cmd);
525 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
530 if (call("selection:clear", ci->focus, 0, mk) >= 1)
531 str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, mk);
533 return call(sc->type, ci->focus, ci->num, ci->mark, str, 0, mk);
536 DEF_CMD(emacs_close_others)
538 if (strcmp(ci->key, "K-1") == 0 && N2(ci) != N2_close_others)
541 if (call("Window:close-others", ci->focus) <= 0)
543 call("Mode:set-num2", ci->focus, N2_close_others);
544 call("Message:modal", ci->focus, 0, NULL, "Type 1 to close more");
550 struct call_return *cr = container_of(ci->comm, struct call_return, c);
556 DEF_CMD(emacs_deactivate)
558 /* close-all popup has closed, see if it aborted */
560 call("event:deactivate", ci->focus);
566 struct call_return cr;
570 if (ci->num == NO_NUMERIC) {
573 /* If this is not only display, then refuse to exit */
574 call_comm("editor:notify:all-displays", ci->focus, &cr.c);
576 call("Message", ci->focus, 0, NULL,
577 "Cannot exit when there are multiple windows.");
581 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM");
583 /* Probably called from a pop-up. Abort the popup
584 * and let the user try again.
586 call("Abort", ci->focus);
589 attr_set_str(&p->attrs, "done-key", "emacs:deactivate");
590 call("docs:show-modified", p);
593 call("event:deactivate", ci->focus);
597 DEF_CMD(emacs_quote_insert)
602 bool first = N2(ci) != N2_undo_insert;
607 if (call("selection:clear", ci->focus, 3, ci->mark) >= 1) {
608 call("Replace", ci->focus, 1, ci->mark, NULL, !first);
611 call("selection:clear", ci->focus, 2, ci->mark);
613 str = ksuffix(ci, "K:CQ-");
615 str = ksuffix(ci, "K:CQ:C-");
617 buf[0] = str[0] & 0x1f;
622 ret = call("Replace", ci->focus, 1, ci->mark, str, !first);
623 call("Mode:set-num2", ci->focus, N2_undo_insert);
625 return ret < 0 ? ret : 1;
628 DEF_CMD(emacs_open_line)
630 if (call("Interactive:insert", ci->focus, 0, ci->mark, "\n") > 0)
631 call("Move-Char", ci->focus, -1, ci->mark);
639 ret = call("doc:reundo", ci->focus, N2(ci) == N2_undo);
640 call("Mode:set-num2", ci->focus, N2_undo);
642 call("Message", ci->focus, 0, NULL,
643 "No further undo information");
648 REDEF_CMD(emacs_file_complete);
649 REDEF_CMD(emacs_doc_complete);
650 REDEF_CMD(emacs_cmd_complete);
652 DEF_CMD(find_complete)
654 char *type = ci->home->data;
656 if (strstarts(type, "file"))
657 return emacs_file_complete_func(ci);
658 if (strcmp(type, "shellcmd") == 0)
659 return emacs_file_complete_func(ci);
660 if (strcmp(type, "doc") == 0)
661 return emacs_doc_complete_func(ci);
662 if (strcmp(type, "cmd") == 0)
663 return emacs_cmd_complete_func(ci);
670 char *type = ci->home->data;
671 char *str = call_ret(strsave, "doc:get-str", ci->focus);
672 const char *norm = NULL;
676 /* close with no string */
677 ret = call("popup:close", ci->focus);
678 if (strcmp(type, "doc") == 0 &&
679 call_ret(pane, "docs:byname", ci->focus, 0, NULL, str) == NULL) {
680 call("Message:modal", ci->focus, 0, NULL, "Document not found");
683 if (strstarts(type, "file")) {
685 bool can_create = True;
686 bool can_create_dir = True;
688 norm = file_normalize(ci->focus, str,
689 attr_find(ci->home->attrs,
691 sl = strrchr(norm, '/');
692 while (sl && sl > norm) {
693 /* Need to check directories exist. */
697 if ((stb.st_mode & S_IFMT) == S_IFDIR)
700 can_create_dir = False;
702 sl = strrchr(norm, '/');
706 if (!can_create_dir) {
707 call("Message:modal", ci->focus, 0, NULL,
708 strconcat(ci->focus, norm, " is not a directory!!"));
712 /* Need to create directory first */
713 if (strcmp(ci->key, "K:A:Enter") == 0) {
714 char *m = "Cannot create directory: ";
715 if (mkdir(norm, 0777) == 0) {
716 m = "Created directory: ";
717 attr_set_str(&ci->home->attrs,
719 /* Trigger recalc on replace */
720 call("doc:replace", ci->focus, 0, NULL, "");
722 call("Message:modal", ci->focus, 0, NULL,
723 strconcat(ci->focus, m, norm));
726 call("Message:modal", ci->focus, 0, NULL,
728 "Use Alt-Enter to create directory ", norm));
732 if (strcmp(type, "file") == 0 && norm &&
733 strcmp(ci->key, "K:Enter") == 0 &&
734 stat(norm, &stb) != 0) {
735 call("Message:modal", ci->focus, 0, NULL,
736 "File not found - use Alt-Enter to create");
739 if (strcmp(type, "file write") == 0 && norm &&
740 strcmp(ci->key, "K:Enter") == 0 &&
741 stat(norm, &stb) == 0) {
742 call("Message:modal", ci->focus, 0, NULL,
743 "File exists - use Alt-Enter to overwrite");
746 ret = call("popup:close", ci->focus, 0, NULL, norm ?: str);
747 return ret < 0 ? ret : 1;
759 struct find_helper *h = container_of(ci->comm, struct find_helper, c);
760 struct pane *p = ci->focus;
767 /* want the pane before nothing, so the last.
768 * So return this one and keep looking.
773 /* Want the pane that is after nothing, so
774 * the first. This one. All done.
780 name = pane_attr_get(ci->focus, "doc-name");
783 if (strcmp(name, h->name) == 0) {
785 /* Want the previous one, which is
790 /* Want the next one, so clear name
798 /* This might be what I want - keep it in case */
802 /* Don't want this - just keep going */
808 DEF_CMD(find_prevnext)
810 /* Find the previous document lru or, which is "next" as we
811 * walk the list in mru order.
812 * When we find it, insert the name into ci->focus document
814 char *type = ci->home->data;
815 struct find_helper h;
817 if (strcmp(type, "doc") != 0)
819 h.name = attr_find(ci->home->attrs, "find-doc");
822 h.want_prev = strcmp(ci->key, "K:A-n") == 0;
824 call_comm("docs:byeach", ci->focus, &h.c);
826 char *name = pane_attr_get(h.ret, "doc-name");
829 attr_set_str(&ci->home->attrs, "find-doc", name);
830 m = mark_new(ci->focus);
831 m2 = m ? mark_dup(m) : NULL;
832 call("doc:file", ci->focus, -1, m);
833 call("doc:file", ci->focus, 1, m2);
834 call("Replace", ci->focus, 1, m, name, 0, m2);
843 char *type = ci->home->data;
848 if (strcmp(type, "file") != 0)
851 if (strcmp(ci->str, "start-of-line") == 0) {
852 char *lens = attr_find(ci->home->attrs, "path_lengths");
853 int dir_start = 0, dir_end = 0, nondir_end = 0,
856 sscanf(lens, "%d %d %d %d", &dir_start, &dir_end,
857 &nondir_end, &basename_start);
860 comm_call(ci->comm2, "cb", ci->focus, dir_start,
861 ci->mark, "fg:grey+20,nobold,noinverse", 115);
862 if (dir_end > dir_start)
863 comm_call(ci->comm2, "cb", ci->focus, dir_end,
864 ci->mark, "fg:black,nobold,noinverse", 114);
865 if (nondir_end > dir_end)
866 comm_call(ci->comm2, "cb", ci->focus, nondir_end,
867 ci->mark, "fg:red-80,bold,inverse", 113);
868 if (basename_start > nondir_end)
869 comm_call(ci->comm2, "cb", ci->focus, basename_start,
870 ci->mark, "fg:magenta", 112);
871 comm_call(ci->comm2, "cb", ci->focus, 10000, ci->mark,
877 DEF_CMD(find_check_replace)
880 char *type = ci->home->data;
884 int ipl; // Initial Path Len
885 int dir_start, dir_end, nondir_end, basename_start;
886 char nbuf[4 * (10+1) + 1], *lens;
888 if (strcmp(type, "file") != 0)
891 home_call(ci->home->parent, ci->key, ci->focus,
892 ci->num, ci->mark, ci->str,
893 ci->num2, ci->mark2, ci->str2);
895 /* The doc content can have 5 different sections that might
896 * be different colours.
897 * - ignored prefix: grey - This inital_path followed by something
898 * that looks like another path. "/" or "~/"
899 * - True directories: black - directory part of the path that
900 * exists and is a directory
901 * - non-directory-in-path: red - directory part that exists but
902 * is not a directory. At most one component.
903 * - non-existant name: magenta - directory path that doesn't exist.
904 * - basename: black, whether it exists or not.
905 * These are found as:
910 * These are all lengths from start of path. They are all stored
911 * in a single attribute: "path_lengths".
914 str = call_ret(str, "doc:get-str", ci->focus);
917 sl = strrchr(str, '/');
921 prev_dir = attr_find(ci->home->attrs, "prev_dir");
922 if (prev_dir && strlen(prev_dir) == (size_t)(sl - str + 1) &&
923 strncmp(prev_dir, str, sl - str + 1) == 0)
924 /* No change before last '/' */
927 initial_path = attr_find(ci->home->attrs, "initial_path");
931 ipl = strlen(initial_path);
933 if (strncmp(str, initial_path, ipl) == 0 &&
934 (str[ipl] == '/' || (str[ipl] == '~' &&
935 (str[ipl+1] == '/' ||
939 dir_start = utf8_strnlen(str, cp - str);
941 basename_start = utf8_strnlen(str, sl - str + 1);
948 path = file_normalize(ci->focus, str, initial_path);
954 while (sl > cp && *sl != '/')
957 nondir_end = utf8_strnlen(str, sl - str + 1);
958 dir_end = nondir_end;
959 if (stb.st_mode != 0 &&
960 (stb.st_mode & S_IFMT) != S_IFDIR) {
961 /* There is a non-dir on the path */
962 while (sl > cp && sl[-1] != '/')
964 /* This must actually be a dir */
965 dir_end = utf8_strnlen(str, sl - str);
967 snprintf(nbuf, sizeof(nbuf), "%d %d %d %d",
968 dir_start, dir_end, nondir_end, basename_start);
969 lens = attr_find(ci->home->attrs, "path_lengths");
970 if (!lens || strcmp(lens, nbuf) != 0)
971 attr_set_str(&ci->home->attrs, "path_lengths", nbuf);
972 sl = strrchr(str, '/');
975 attr_set_str(&ci->home->attrs, "prev_dir", str);
980 static struct map *fh_map;
981 DEF_LOOKUP_CMD(find_handle, fh_map);
983 static void findmap_init(void)
987 fh_map = key_alloc();
988 key_add(fh_map, "K:Tab", &find_complete);
989 key_add(fh_map, "K:Enter", &find_done);
990 key_add(fh_map, "K:A:Enter", &find_done);
991 key_add(fh_map, "K:A-p", &find_prevnext);
992 key_add(fh_map, "K:A-n", &find_prevnext);
993 key_add(fh_map, "map-attr", &find_attr);
994 key_add(fh_map, "doc:replace", &find_check_replace);
997 static const char * safe file_normalize(struct pane *p safe,
999 const char *initial_path)
1001 int len = strlen(initial_path?:"");
1006 if (initial_path && strncmp(path, initial_path, len) == 0) {
1007 if (path[len] == '/' || (path[len] == '~' &&
1008 (path[len+1] == '/' || path[len+1] == 0)))
1013 if (path[0] == '~' && (path[1] == '/' || path[1] == 0)) {
1014 char *home = getenv("HOME");
1017 if (home[strlen(home) - 1] == '/' && path[1] == '/')
1019 return strconcat(p, home, path+1);
1021 dir = pane_attr_get(p, "dirname");
1024 if (dir[strlen(dir)-1] == '/')
1025 return strconcat(p, dir, path);
1026 return strconcat(p, dir, "/", path);
1029 DEF_CMD(emacs_findfile)
1032 struct pane *p, *par;
1037 path = pane_attr_get(ci->focus, "dirname");
1046 e = buf + strlen(buf);
1047 if (e < buf + sizeof(buf)-1 && e > buf && e[-1] != '/') {
1052 if (ksuffix(ci, "File Found:")[0] == 0) {
1053 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
1058 if (ksuffix(ci, "K:CX44")[0]) {
1059 attr_set_str(&p->attrs, "prompt",
1061 attr_set_str(&p->attrs, "done-key",
1062 "File Found:Popup");
1063 } else if (ksuffix(ci, "K:CX4")[0]) {
1064 attr_set_str(&p->attrs, "prompt",
1065 "Find File Other Window");
1066 attr_set_str(&p->attrs, "done-key",
1067 "File Found:Other Window");
1069 attr_set_str(&p->attrs, "prompt", "Find File");
1070 attr_set_str(&p->attrs, "done-key",
1071 "File Found:This Window");
1073 call("doc:set-name", p, 0, NULL, "Find File");
1075 p = pane_register(p, 0, &find_handle.c, "file");
1078 attr_set_str(&p->attrs, "initial_path", path);
1079 call("attach-history", p, 0, NULL, "*File History*");
1086 path = file_normalize(ci->focus, ci->str, path);
1091 fd = open(path, O_RDONLY);
1093 p = call_ret(pane, "doc:open", ci->focus, fd, NULL, path);
1096 /* '4' says 'allow create' */
1097 p = call_ret(pane, "doc:open", ci->focus, -1, NULL, path, 4);
1100 asprintf(&m, "Failed to open file: %s", path);
1101 call("Message", ci->focus, 0, NULL, m);
1105 if (strcmp(ci->key, "File Found:Other Window") == 0) {
1106 struct pane *this = call_ret(pane, "ThisPane", ci->focus);
1107 par = home_call_ret(pane, ci->focus, "DocPane", p);
1108 if (par && par != this) {
1109 pane_take_focus(par);
1112 par = call_ret(pane, "OtherPane", ci->focus);
1113 } else if (strcmp(ci->key, "File Found:Popup") == 0) {
1114 par = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "MD3tsa");
1116 par = call_ret(pane, "ThisPane", ci->focus);
1123 p = home_call_ret(pane, p, "doc:attach-view", par, 1);
1127 return p ? 1 : Efail;
1130 DEF_CMD(emacs_writefile)
1132 /* Request to write to a different file */
1138 if (call("doc:write-file", ci->focus, NO_NUMERIC) >= 0) {
1139 /* It should have been an error ! */
1140 const char *doc = pane_attr_get(ci->focus, "doc-name");
1142 call("Message", ci->focus, 0, NULL,
1143 strconcat(ci->focus, "Document ", doc,
1144 " cannot be written"));
1146 call("Message", ci->focus, 0, NULL,
1147 "This document cannot be written");
1150 path = pane_attr_get(ci->focus, "dirname");
1159 e = buf + strlen(buf);
1160 if (e < buf + sizeof(buf)-1 && e > buf && e[-1] != '/') {
1165 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
1169 attr_set_str(&p->attrs, "prompt", "Write File");
1170 attr_set_str(&p->attrs, "done-key",
1171 strconcat(p, "emacs:write_file:", path));
1172 call("doc:set-name", p, 0, NULL, "Write File");
1173 p = pane_register(p, 0, &find_handle.c, "file write");
1176 attr_set_str(&p->attrs, "initial_path", path);
1177 call("attach-history", p, 0, NULL, "*File History*");
1181 DEF_CMD(emacs_do_writefile)
1183 const char *path = ksuffix(ci, "emacs:write_file:");
1184 if (!ci->str || !path)
1187 path = file_normalize(ci->focus, ci->str, path);
1190 if (call("doc:write-file", ci->focus, NO_NUMERIC, NULL, path) <= 0) {
1191 call("Message", ci->focus, 0, NULL,
1192 strconcat(ci->focus, "Failed to write to ", path));
1198 DEF_CMD(emacs_insertfile)
1200 /* Request to insert content of a file at point */
1207 ret = call("doc:insert-file", ci->focus, NO_NUMERIC);
1208 if (ret != Enoarg) {
1211 /* Message already given */
1213 doc = pane_attr_get(ci->focus, "doc-name");
1215 call("Message", ci->focus, 0, NULL,
1216 strconcat(ci->focus, "Document ", doc,
1217 " cannot receive insertions"));
1219 call("Message", ci->focus, 0, NULL,
1220 "This document cannot receive insertions");
1223 path = pane_attr_get(ci->focus, "dirname");
1232 e = buf + strlen(buf);
1233 if (e < buf + sizeof(buf)-1 && e > buf && e[-1] != '/') {
1238 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
1242 attr_set_str(&p->attrs, "prompt", "Insert File");
1243 attr_set_str(&p->attrs, "done-key",
1244 strconcat(p, "emacs:insert_file:", path));
1245 call("doc:set-name", p, 0, NULL, "Insert File");
1246 p = pane_register(p, 0, &find_handle.c, "file");
1249 attr_set_str(&p->attrs, "initial_path", path);
1250 call("attach-history", p, 0, NULL, "*File History*");
1254 DEF_CMD(emacs_do_insertfile)
1256 const char *path = ksuffix(ci, "emacs:insert_file:");
1259 if (!ci->str || !path)
1262 path = file_normalize(ci->focus, ci->str, path);
1265 fd = open(path, O_RDONLY);
1267 if (call("doc:insert-file", ci->focus, fd) <= 0) {
1269 call("Message", ci->focus, 0, NULL,
1270 strconcat(ci->focus, "Failed to insert ", path));
1276 char *m = strconcat(ci->focus,
1277 "Failed to inser file: ", path);
1278 call("Message", ci->focus, 0, NULL, m);
1283 REDEF_CMD(emacs_file_complete)
1285 /* Extract a directory name and a basename from the document.
1286 * Find a document for the directory and attach as a completing
1292 struct pane *par, *pop, *docp, *p;
1293 struct call_return cr;
1294 char *type = ci->home->data;
1295 char *initial = attr_find(ci->home->attrs, "initial_path");
1296 int wholebuf = strcmp(type, "file") == 0;
1302 st = mark_dup(ci->mark);
1303 call("doc:file", ci->focus, -1, st);
1305 str = call_ret(strsave, "doc:get-str", ci->focus, 0, st, NULL,
1312 /* Need guess which part of the buf is the file name.
1313 * This probably needs to be configurable, but lets
1314 * decide the file name starts immediately after a
1315 * space, or a '=' or ':' which is followed by a
1319 d = str + strlen(str);
1322 (strchr(":=", d[-1]) && d[0] == '/')))
1325 d = file_normalize(ci->focus, d, initial);
1326 b = strrchr(d, '/');
1329 d = strnsave(ci->focus, d, b-d);
1332 d = strsave(ci->focus, ".");
1334 fd = open(d, O_DIRECTORY|O_RDONLY);
1336 call("Message:modal", ci->focus, 0, NULL,
1337 strconcat(ci->focus, "Cannot open directory \"", d, "\""));
1340 /* 32 means quiet */
1341 docp = call_ret(pane, "doc:open", ci->focus, fd, NULL, d, 32);
1345 pop = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM1r");
1348 par = home_call_ret(pane, docp, "doc:attach-view", pop,
1353 attr_set_str(&par->attrs, "line-format", "%name%suffix");
1354 attr_set_str(&par->attrs, "heading", "");
1355 attr_set_str(&par->attrs, "done-key", "Replace");
1356 p = call_ret(pane, "attach-render-complete", par, 0, NULL, "format");
1359 cr = call_ret(all, "Complete:prefix", p, 1, NULL, b,
1360 0, NULL, "format:plain");
1361 if (cr.s && (strlen(cr.s) <= strlen(b) && cr.ret-1 > 1)) {
1362 /* We need the dropdown - delete prefix and drop-down will
1367 start = mark_dup(ci->mark);
1368 call("doc:char", ci->focus, -strlen(b), start);
1369 call("Replace", ci->focus, 1, start);
1375 /* Replace 'b' with the result. */
1378 start = mark_dup(ci->mark);
1379 call("doc:char", ci->focus, -strlen(b), start);
1380 call("Replace", ci->focus, 1, start, cr.s);
1383 call("Message:modal", ci->focus, 0, NULL,
1384 strconcat(ci->focus, "No completion found for \"", b, "\"",
1385 " in \"", d, "\""));
1387 /* Now need to close the popup */
1392 DEF_CMD(emacs_finddoc)
1394 struct pane *p, *par;
1396 if (ksuffix(ci, "Doc Found:")[0] == 0) {
1398 char *defname = NULL;
1400 dflt = call_ret(pane, "docs:choose", ci->focus);
1402 defname = pane_attr_get(dflt, "doc-name");
1404 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
1410 attr_set_str(&p->attrs, "default", defname);
1411 if (ksuffix(ci, "K:CX44")[0]) {
1412 attr_set_str(&p->attrs, "prompt",
1413 "Find Document Popup");
1414 attr_set_str(&p->attrs, "done-key",
1416 } else if (ksuffix(ci, "K:CX4")[0]) {
1417 attr_set_str(&p->attrs, "prompt",
1418 "Find Document Other Window");
1419 attr_set_str(&p->attrs, "done-key",
1420 "Doc Found:Other Window");
1422 attr_set_str(&p->attrs, "prompt", "Find Document");
1423 attr_set_str(&p->attrs, "done-key",
1424 "Doc Found:This Window");
1426 call("doc:set-name", p, 0, NULL, "Find Document", -1);
1428 pane_register(p, 0, &find_handle.c, "doc");
1436 p = call_ret(pane, "docs:byname", ci->focus, 0, NULL, ci->str);
1440 if (strcmp(ci->key, "Doc Found:Other Window") == 0) {
1441 struct pane *this = call_ret(pane, "ThisPane", ci->focus);
1442 par = home_call_ret(pane, ci->focus, "DocPane", p);
1443 if (par && par != this) {
1444 pane_take_focus(par);
1447 par = call_ret(pane, "OtherPane", ci->focus);
1448 } else if (strcmp(ci->key, "Doc Found:Popup") == 0) {
1449 par = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "MD3tsa");
1451 par = call_ret(pane, "ThisPane", ci->focus);
1455 p = home_call_ret(pane, p, "doc:attach-view", par, 1);
1458 return p ? 1 : Efail;
1461 REDEF_CMD(emacs_doc_complete)
1463 /* Extract a document name from the document.
1464 * Attach the 'docs' document as a completing popup menu
1467 struct pane *pop, *p = NULL;
1468 struct call_return cr;
1473 str = call_ret(strsave, "doc:get-str", ci->focus);
1476 pop = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM1r");
1479 p = call_ret(pane, "docs:complete", pop);
1482 cr = call_ret(all, "Complete:prefix", p, 1, NULL, str);
1483 if (cr.s && (strlen(cr.s) <= strlen(str) && cr.ret - 1 > 1)) {
1484 /* We need the dropdown */
1487 start = mark_dup(ci->mark);
1488 call("doc:set-ref", ci->focus, 1, start);
1490 call("Replace", ci->focus, 1, start);
1495 /* Replace the prefix with the new value */
1498 start = mark_dup(ci->mark);
1499 call("doc:set-ref", ci->focus, 1, start);
1501 call("Replace", ci->focus, 1, start, cr.s);
1504 /* Now need to close the popup */
1509 DEF_CMD(emacs_viewdocs)
1511 struct pane *p, *par;
1514 docs = call_ret(pane, "docs:byname", ci->focus, 0, NULL, "*Documents*");
1518 if (ksuffix(ci, "K:CX44")[0]) {
1519 par = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "MD3tsa");
1520 } else if (ksuffix(ci, "K:CX4")[0]) {
1521 struct pane *this = call_ret(pane, "ThisPane", ci->focus);
1522 par = home_call_ret(pane, ci->focus, "DocPane", docs);
1523 if (par && par != this) {
1524 pane_take_focus(par);
1527 par = call_ret(pane, "OtherPane", ci->focus);
1529 par = call_ret(pane, "ThisPane", ci->focus);
1535 p = home_call_ret(pane, docs, "doc:attach-view", par, 1);
1541 struct pane *doc safe;
1545 DEF_CMD(choose_pane)
1547 struct pcb *cb = container_of(ci->comm, struct pcb, c);
1550 if (cb->p || !ci->str)
1552 d = call_ret(pane, "docs:byname", ci->focus, 0, NULL, ci->str);
1554 cb->p = home_call_ret(pane, ci->focus, "DocPane", d);
1556 home_call(cb->doc, "doc:attach-view", cb->p, 1);
1564 if (strcmp(ci->key, "cb:timer") == 0) {
1565 /* If focus has moved, don't show shell window,
1566 * probably a popup was requested by command.
1568 if (!pane_has_focus(ci->home))
1569 /* call back when lines or eof */
1572 if (strcmp(ci->key, "cb:eof") != 0) {
1574 if (ci->str && strchr(ci->str, 'P'))
1575 par = call_ret(pane, "PopupTile", ci->home, 0, NULL, "MD3tsa");
1577 par = call_ret(pane, "OtherPane", ci->home);
1579 home_call(ci->focus, "doc:attach-view", par, 1);
1582 str = call_ret(str, "doc:get-str", ci->focus);
1583 if (!str || !*str) {
1586 asprintf(&str, "(shell command completed with no output)");
1587 else if (ci->num > 0)
1588 asprintf(&str, "(shell command completed with no output (exit code %d))", ci->num);
1590 asprintf(&str, "(shell command completed with no output (signalled %d))", -ci->num);
1592 call("Message", ci->home, 0, NULL, str);
1597 DEF_CB(shell_insert_cb)
1599 char *str = call_ret(str, "doc:get-str", ci->focus);
1600 struct mark *mk = call_ret(mark2, "doc:point", ci->home);
1602 if (call("selection:clear", ci->home, 3, mk) >= 1)
1603 call("Replace", ci->home, 1, mk);
1604 call("Replace", ci->home, 0, NULL, str);
1609 DEF_CMD(emacs_shell)
1611 char *name = "*Shell Command Output*";
1612 struct pane *p, *doc, *par, *sc;
1613 char *path, *input = NULL;
1615 bool interpolate, pipe, popup;
1617 if (strcmp(ci->key, "Shell Command") != 0) {
1621 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2", 0, NULL,
1625 dirname = call_ret(strsave, "get-attr", ci->focus, 0, NULL, "dirname");
1626 attr_set_str(&p->attrs, "dirname", dirname ?: ".");
1627 attr_set_str(&p->attrs, "prompt", "Shell command");
1628 if (ci->num == 0 || ci->num == 1)
1629 strcat(aux, "i"); // interpolate
1631 strcat(aux, "P"); // popup
1632 if (strcmp(ci->key, "K:A-|") == 0)
1633 strcat(aux, "p"); // pipe from selection
1634 attr_set_str(&p->attrs, "popup-aux", aux);
1635 attr_set_str(&p->attrs, "done-key", "Shell Command");
1636 call("doc:set-name", p, 0, NULL, "Shell Command", -1);
1637 p = call_ret(pane, "attach-history", p, 0, NULL, "*Shell History*");
1639 p = pane_register(p, 0, &find_handle.c, "shellcmd");
1643 comm_call(ci->comm2, "cb", p);
1647 call("Message", ci->focus, 0, NULL, "Shell command aborted");
1651 interpolate = ci->str2 && strchr(ci->str2, 'i');
1652 pipe = ci->str2 && strchr(ci->str2, 'p');
1653 popup = ci->str2 && strchr(ci->str2, 'P');
1656 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
1658 input = call_ret(str, "doc:get-str", ci->focus,
1659 0, NULL, NULL, 0, mk);
1660 /* make the selection replacable */
1661 attr_set_int(&mk->attrs, "selection:active", 3);
1664 doc = call_ret(pane, "doc:from-text", ci->focus, 0, NULL, name, 0, NULL,
1670 path = pane_attr_get(ci->focus, "dirname");
1671 attr_set_str(&doc->attrs, "dirname", path);
1673 /* shellcmd is attached directly to the document, not in the view
1674 * stack. It is go-between for document and external command.
1675 * We don't need a doc attachment as no point is needed - we
1676 * always insert at the end.
1678 sc = call_ret(pane, "attach-shellcmd", doc, pipe ? 22 : 4,
1679 NULL, ci->str, 0, NULL, path);
1681 call("doc:replace", doc, 0, NULL,
1682 "Failed to run command - sorry\n");
1683 if (call("text-search", doc, 0, NULL, "diff|(stg|git).*show",
1684 0, NULL, ci->str) > 0)
1685 attr_set_str(&doc->attrs, "view-default", "diff");
1687 /* Close old shell docs, but if one is visible in current frame, replace
1693 call_comm("editor:notify:shell-reuse", ci->focus, &cb.c);
1695 /* choose_pane didn't attach, so set a callback to do
1696 * it when there is enough content.
1699 /* If it take more than 500msec, or includes 2
1700 * or more lines, we'll show in a pane, else
1701 * just show as a message.
1704 home_call_comm(sc, "shellcmd:set-callback",
1705 ci->focus, &shellcb,
1706 500, NULL, popup ? "P": "",
1710 par = call_ret(pane, "PopupTile", ci->focus,
1713 par = call_ret(pane, "OtherPane", ci->focus);
1716 home_call(doc, "doc:attach-view", par, 1);
1719 if (sc && interpolate)
1720 /* Need a callback when the pipe command finished */
1721 home_call_comm(sc, "shellcmd:set-callback", ci->focus,
1730 const char *last = ksuffix(ci, "K:A-");
1737 if (rpt == NO_NUMERIC)
1740 rpt = rpt * 10 + *last - '0';
1742 call("Mode:set-num", ci->focus, neg ? -rpt : rpt);
1743 call("Mode:set-num2", ci->focus, ci->num2);
1749 call("Mode:set-num", ci->focus, - ci->num);
1750 call("Mode:set-num2", ci->focus, ci->num2);
1754 DEF_CMD(emacs_prefix)
1756 /* as a generic arg (ctrl-U) which is positive and
1757 * as as a repeat-count of 4, but is different to 4.
1758 * I should probably allow digits to alter the number.
1760 call("Mode:set-num", ci->focus, 4);
1764 DEF_CMD(emacs_kill_doc)
1766 if (ci->num <= 0 || ci->num == NO_NUMERIC) {
1767 /* Check if modified. */
1768 char *m = pane_attr_get(ci->focus, "doc-modified");
1770 if (m && strcmp(m, "yes") == 0)
1771 f = pane_attr_get(ci->focus, "filename");
1773 call("Message:modal", ci->focus, 0, NULL,
1774 "Document is modified - please save or give prefix arg");
1778 return call("doc:destroy", ci->focus);
1781 DEF_CMD(emacs_revisit)
1783 return call("doc:load-file", ci->focus, 2, NULL, NULL, -1);
1786 DEF_CMD(emacs_save_all)
1788 int ret = call("docs:save-all", ci->focus, 0, NULL, NULL, 1);
1791 call("Message", ci->focus, 0, NULL, "No files need to be saved.");
1794 if (ci->num == NO_NUMERIC) {
1795 struct pane *p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM");
1797 return call("docs:show-modified", p);
1799 return call("docs:save-all", ci->focus);
1802 DEF_CMD(emacs_reposition)
1805 int repoint = attr_find_int(ci->focus->attrs, "emacs-repoint");
1807 if (repoint != -1) {
1808 /* Move point to end of display, if that is in
1809 * the right direction. That will mean it has moved
1812 m = mark_at_point(ci->focus, NULL, MARK_UNGROUPED);
1814 struct mark *m2 = mark_dup(m);
1815 call("Move-CursorXY", ci->focus, 0, m, NULL,
1817 INT_MAX, repoint < 0 ? ci->focus->h-1 : 0);
1819 /* can only move point backwards */
1820 if (m->seq < m2->seq)
1821 call("Move-to", ci->focus, 0, m);
1823 /* can only move point forwards */
1824 if (m->seq > m2->seq)
1825 call("Move-to", ci->focus, 0, m);
1829 attr_set_str(&ci->focus->attrs, "emacs-repoint", NULL);
1831 return Efallthrough;
1834 DEF_CMD(emacs_start_search)
1836 struct pane *p = NULL, *hp;
1839 hp = call_ret(pane, "attach-emacs-search-highlight", ci->focus);
1842 p = call_ret(pane, "PopupTile", hp, 0, NULL, "TR2",
1847 home_call(hp, "highlight:set-popup", p);
1849 attr_set_str(&p->attrs, "prompt", "Search");
1850 attr_set_str(&p->attrs, "done-key", "Search String");
1852 hp = call_ret(pane, "attach-history", p, 0, NULL, "*Search History*");
1856 call("doc:set-name", p, 0, NULL, "Search", -1);
1857 if (strcmp(ci->key, "K:C-R") == 0)
1859 if (strcmp(ci->key, "K:A-%") == 0)
1861 call_ret(pane, "attach-emacs-search", p, mode);
1866 DEF_CMD(emacs_command)
1870 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2", 0, NULL, "");
1873 attr_set_str(&p->attrs, "prompt", "Cmd");
1874 attr_set_str(&p->attrs, "done-key", "emacs:command");
1875 call("doc:set-name", p, 0, NULL, "K:Ax command", -1);
1876 p = call_ret(pane, "attach-history", p, 0, NULL, "*Command History*");
1878 pane_register(p, 0, &find_handle.c, "cmd");
1882 DEF_CMD(emacs_do_command)
1890 asprintf(&cmd, "interactive-cmd-%s", ci->str);
1893 ret = call(cmd, ci->focus, 0, ci->mark);
1894 free(cmd); cmd = NULL;
1896 asprintf(&cmd, "Command %s not found", ci->str);
1897 call("Message", ci->focus, 0, NULL, cmd);
1898 } else if (ret < 0) {
1899 asprintf(&cmd, "Command %s Failed", ci->str);
1900 call("Message", ci->focus, 0, NULL, cmd);
1908 struct call_return *cr = container_of(ci->comm, struct call_return, c);
1915 struct mark *m = vmark_new(cr->p, MARK_UNGROUPED, NULL);
1916 call("doc:list-add", cr->p, 0, m);
1917 call("doc:set-attr", cr->p, 0, m, "cmd", 0, NULL, cmd);
1922 REDEF_CMD(emacs_cmd_complete)
1925 struct pane *doc = NULL, *pop = NULL, *p;
1926 struct call_return cr;
1930 s = call_ret(strsave, "doc:get-str", ci->focus);
1933 doc = call_ret(pane, "attach-doc-list", ci->focus);
1936 call("doc:set-name", doc, 0, NULL, "*Command List*");
1937 call("doc:set:autoclose", doc, 1);
1938 attr_set_str(&doc->attrs, "render-simple", "format");
1939 attr_set_str(&doc->attrs, "heading", "");
1940 attr_set_str(&doc->attrs, "line-format", "%cmd");
1943 call_comm("keymap:list", ci->focus, &cr.c,
1944 0, NULL, "interactive-cmd-");
1945 call("doc:list-sort", doc, 0, NULL, "cmd");
1946 pop = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM1r");
1949 p = home_call_ret(pane, cr.p, "doc:attach-view", pop, -1, NULL, "simple");
1952 attr_set_str(&p->attrs, "done-key", "Replace");
1953 p = call_ret(pane, "attach-render-complete", p);
1956 cr = call_ret(all, "Complete:prefix", p, 1, NULL, s);
1957 if (cr.s && strlen(cr.s) <= strlen(s) && cr.ret-1 > 1) {
1958 /* We need the dropdown - delete prefix and drop-down will
1961 struct mark *start = mark_dup(ci->mark);
1962 call("Move-Char", ci->focus, -strlen(s), start);
1963 call("Replace", ci->focus, 1, start);
1968 /* Replace 's' with the result */
1969 struct mark *start = mark_dup(ci->mark);
1970 call("Move-Char", ci->focus, -strlen(s), start);
1971 call("Replace", ci->focus, 1, start, cr.s);
1974 /* Now need to close the popup */
1986 DEF_CMD(emacs_version)
1990 asprintf(&v, "%s ; emacs-mode - v" VERSION " - " VERS_DATE, edlib_version);
1992 call("Message", ci->focus, 0, NULL, v);
1999 /* View the debug log */
2000 struct pane *p, *doc;
2002 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL, "*Debug Log*");
2004 call("interactive-cmd-view-log", ci->focus);
2005 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL,
2010 p = call_ret(pane, "ThisPane", ci->focus);
2012 home_call(doc, "doc:attach-view", p, 1);
2018 struct mark *m = call_ret(mark2, "doc:point", ci->focus);
2020 call("selection:clear", ci->focus, 0, m);
2022 call("Move-to", ci->focus, 1);
2023 m = call_ret(mark2, "doc:point", ci->focus);
2025 /* ci->num == 1 means replacable */
2026 call("selection:set", ci->focus,
2027 ci->num == 1 ? 3 : 1, m);
2031 DEF_CMD(emacs_swap_mark)
2033 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2039 call("Move-to", ci->focus, 1); /* Move mark to point */
2040 call("Move-to", ci->focus, 0, m); /* Move point to old mark */
2041 call("selection:set", ci->focus,
2042 ci->num == 1 ? 3 : 1, mk);
2049 /* Delete text from point to mark */
2050 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2056 /* Remove any selection so it cannot be claimed and so replace this copy */
2057 call("selection:claim", ci->focus);
2058 call("selection:discard", ci->focus);
2060 str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, mk);
2062 call("copy:save", ci->focus, 0, NULL, str);
2063 ret = call("Replace", ci->focus, 1, mk);
2065 call("selection:clear", ci->focus, 0, mk);
2072 /* copy text from point to mark */
2073 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2078 /* Remove any selection so it cannot be claimed and so replace this copy */
2079 call("selection:claim", ci->focus);
2080 call("selection:discard", ci->focus);
2082 str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, mk);
2084 call("copy:save", ci->focus, 0, NULL, str);
2085 /* Clear current highlight */
2086 call("selection:clear", ci->focus, 0, mk);
2092 int n = RPT_NUM(ci);
2095 struct mark *m = NULL;
2097 /* If there is a selection elsewhere, we want to commit it */
2098 call("selection:discard", ci->focus);
2099 call("selection:commit", ci->focus);
2101 str = call_ret(strsave, "copy:get", ci->focus, n - 1);
2104 /* If mark exists and is active, replace marked regions */
2105 mk = call_ret(mark2, "doc:point", ci->focus);
2106 if (mk && call("selection:clear", ci->focus, 0, mk) >= 1) {
2107 char *str2 = call_ret(strsave, "doc:get-str", ci->focus,
2108 0, NULL, NULL, 0, mk);
2110 call("copy:save", ci->focus, 0, NULL, str2);
2114 call("Move-to", ci->focus, 1);
2115 call("Replace", ci->focus, 1, m, str);
2117 mk = call_ret(mark2, "doc:point", ci->focus);
2118 call("Mode:set-num2", ci->focus, N2_yank);
2122 DEF_CMD(emacs_yank_pop)
2124 struct mark *mk, *m;
2128 if (N2(ci) != N2_yank)
2130 mk = call_ret(mark2, "doc:point", ci->focus);
2134 str = call_ret(strsave, "copy:get", ci->focus, num);
2137 str = call_ret(strsave, "copy:get", ci->focus, num);
2143 call("Replace", ci->focus, 1, mk, str);
2144 call("Move-to", ci->focus, 1, m);
2146 call("Mode:set-num2", ci->focus, (num << 16) | N2_yank);
2150 DEF_CMD(emacs_selection_menu_add)
2152 struct pane *p = ci->focus;
2153 call("menu-add", p, 0, NULL, "Cut", 0, NULL, ":C-W");
2154 call("menu-add", p, 0, NULL, "Copy", 0, NULL, ":A-w");
2155 call("menu-add", p, 0, NULL, "Paste-in", 0, NULL, ":C-Y");
2156 return Efallthrough;
2159 DEF_CMD(emacs_goto_line)
2161 if (ci->num == NO_NUMERIC)
2163 call("CountLines", ci->focus, ci->num, ci->mark, "goto:line");
2164 call("Move-View-Pos", ci->focus, 0, ci->mark);
2168 DEF_CMD(emacs_next_match)
2170 call("Mode:set-num2", ci->focus, N2_match);
2171 call("Message:modal", ci->focus, 0, NULL, "Type ` to search again");
2172 return call("interactive-cmd-next-match", ci->focus, ci->num, NULL, NULL,
2173 strcmp(ci->key, "K-`") == 0);
2176 DEF_CMD(emacs_match_again)
2178 if (N2(ci) != N2_match)
2179 return Efallthrough;
2181 return emacs_next_match_func(ci);
2186 call("interactive-cmd-make", ci->focus,
2187 ci->num, ci->mark, NULL,
2188 strcmp(ci->key, "K:CC:C-M") == 0);
2192 DEF_CMD(emacs_paste)
2196 /* First commit the selection, then collect it */
2197 call("selection:commit", ci->focus);
2198 str = call_ret(strsave, "copy:get", ci->focus);
2200 call("Move-CursorXY", ci->focus,
2201 0, NULL, NULL, 0, NULL, NULL, ci->x, ci->y);
2206 call("Replace", ci->focus, 0, NULL, str);
2208 pane_take_focus(ci->focus);
2213 DEF_CMD(emacs_readonly)
2217 ro = pane_attr_get(ci->focus, "doc-readonly");
2219 if (ro && strcmp(ro,"yes") == 0)
2220 call("doc:set:readonly", ci->focus, 0);
2222 call("doc:set:readonly", ci->focus, 1);
2226 DEF_CMD(emacs_shift)
2231 shift = pane_attr_get_int(ci->focus, "render-wrap", -1);
2232 if (strcmp(ci->key, "K:CX->") == 0 || strcmp(ci->key, "K->") == 0)
2234 if (rpt == NO_NUMERIC) {
2239 } else if (rpt == -NO_NUMERIC) {
2246 } else if (rpt >= 0) {
2251 if (shift > 0 && shift + rpt < 0)
2257 attr_set_str(&ci->focus->attrs, "render-wrap", "yes");
2259 attr_set_int(&ci->focus->attrs, "render-wrap", shift);
2261 attr_set_str(&ci->focus->attrs, "render-wrap", "0 auto");
2263 /* When reducing shift to zero, don't enable auto */
2264 attr_set_int(&ci->focus->attrs, "render-wrap", 0);
2265 call("view:changed", ci->focus);
2266 call("Mode:set-num2", ci->focus, N2_shift);
2267 call("Message:modal", ci->focus, 0, NULL, "Type < or > to shift again");
2271 DEF_CMD(emacs_shift_again)
2273 if (N2(ci) != N2_shift)
2274 return Efallthrough;
2276 return emacs_shift_func(ci);
2279 DEF_CMD(emacs_growx)
2281 if (ci->key[strlen(ci->key)-1] == '}')
2282 call("Window:x+", ci->focus, ci->num);
2284 call("Window:x-", ci->focus, ci->num);
2285 call("Mode:set-num2", ci->focus, N2_growx);
2286 call("Message:modal", ci->focus, 0, NULL, "Type { or } to grow again");
2290 DEF_CMD(emacs_growx_again)
2292 if (N2(ci) != N2_growx)
2293 return Efallthrough;
2295 return emacs_growx_func(ci);
2298 DEF_CMD(emacs_scale_relative)
2300 struct pane *p = ci->focus;
2301 char *sc = pane_attr_get(p, "scale:M");
2306 call("Message:modal", p, 0, NULL,
2307 "Cannot zoom display with fixed-sized font");
2310 sc = pane_attr_get(p, "scale");
2311 if (sc && strchr(sc, 'x')) {
2312 call("Message:modal", p, 0, NULL,
2313 "Cannot zoom display with fixed layout");
2321 if (ci->key[strlen(ci->key)-1] == '-')
2322 scale = 10 * scale / 12;
2324 scale = 12 * scale / 10;
2325 snprintf(num, sizeof(num)-1, "%d", scale);
2326 call("window:set:scale", p, 0, NULL, num);
2330 DEF_CMD(emacs_curs_pos)
2340 c = mark_dup(ci->mark);
2341 nxt = doc_following(ci->focus, c);
2343 while ((ch = doc_prev(ci->focus, c)) != WEOF && !is_eol(ch))
2345 while (mark_ordered_not_same(c, ci->mark)) {
2346 ch = doc_next(ci->focus, c);
2350 } else if (ch == '\t') {
2359 asprintf(&msg, "Cursor at column %d (%d chars), char is %d (0x%x)",
2360 col, chars, nxt, nxt);
2361 call("Message", ci->focus, 0, NULL, msg);
2366 DEF_CMD(emacs_word_count)
2369 struct pane *p = ci->focus;
2376 mk = call_ret(mark2, "doc:point", p);
2377 if (mk && attr_find_int(mk->attrs, "selection:active") <= 0)
2379 call("CountLines", p, 0, ci->mark);
2380 wp = attr_find_int(ci->mark->attrs, "word");
2383 call("CountLines", p, 0, mk);
2384 wm = attr_find_int(mk->attrs, "word");
2385 asprintf(&msg, "%d words in region", abs(wp-wm));
2387 int wd = pane_attr_get_int(p, "words", 0);
2388 asprintf(&msg, "After word%s %d of %d", wp==2?"":"s", wp-1, wd);
2390 call("Message", p, 0, NULL, msg);
2397 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2398 struct mark *p = call_ret(mark, "doc:point", ci->focus);
2401 if (call("selection:clear", ci->focus, 0, mk, NULL, 0, p) == Efalse)
2404 if (strcmp(ci->key, "K:A-q") == 0) {
2405 if (call("fill-paragraph", ci->focus, ci->num, p, NULL,
2406 0, mk) == Efallthrough) {
2407 p2 = call_ret(pane, "attach-textfill", ci->focus);
2409 call("fill-paragraph", p2, ci->num, p, NULL,
2413 /* Don't try to load anything, the file-type should
2414 * have loaded something if relevant
2416 if (call("reindent-paragraph", ci->focus, ci->num, p, NULL,
2418 call("Message", ci->focus, 0, NULL,
2419 "Reindent not supported on the document.");
2424 DEF_CMD(emacs_abbrev)
2426 call("attach-abbrev", ci->focus);
2430 DEF_CMD(emacs_showinput)
2432 struct pane *p, *doc;
2434 if (call("input:log", ci->focus) <= 0) {
2435 call("Message", ci->focus, 0, NULL,
2436 "Cannot get log of recent input.");
2440 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL, "*Debug Log*");
2442 call("interactive-cmd-view-log", ci->focus);
2443 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL,
2448 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM");
2450 p = home_call_ret(pane, doc, "doc:attach-view", p, 1);
2452 call("doc:file", p, 1);
2457 DEF_CMD(emacs_macro_start)
2461 ret = call("macro:capture", ci->focus);
2463 call("Message", ci->focus, 0, NULL,
2464 "Macro capture already happening");
2466 call("Message", ci->focus, 0, NULL,
2467 "Macro facility not available");
2471 DEF_CMD(emacs_macro_stop)
2475 ret = call("macro:finished", ci->focus, 2);
2477 call("Message", ci->focus, 0, NULL,
2478 "Macro successfully created.");
2479 else if (ret == Efalse)
2480 call("Message", ci->focus, 0, NULL,
2481 "No macro being created.");
2483 call("Message", ci->focus, 0, NULL,
2484 "Failure creating macro.");
2488 DEF_CMD(emacs_macro_run)
2490 int cnt = RPT_NUM(ci);
2492 if (strcmp(ci->key, "K-e") == 0 && N2(ci) != N2_runmacro)
2493 return Efallthrough;
2498 call("macro:replay", ci->focus, 1) > 0)
2501 call("Mode:set-num2", ci->focus, N2_runmacro);
2502 call("Message:modal", ci->focus, 0, NULL, "Type 'e' to repeat macro");
2503 return cnt < 1 ? 1 : Efail;
2512 static const char spell_choices[] =
2513 "0123456789-=;,/ABCDEFGHIJKLMNOPQRSTUVWXYZ";
2514 DEF_CB(get_suggestion)
2516 struct bb *b = container_of(ci->comm, struct bb, c);
2522 buf_concat(&b->b, " - (a)ccept, (i)nsert - ");
2524 buf_concat(&b->b, ", ");
2526 if (b->count < (int)sizeof(spell_choices)-1) {
2527 buf_append(&b->b, '(');
2528 buf_append(&b->b, spell_choices[b->count]);
2529 buf_append(&b->b, ')');
2532 buf_concat(&b->b, ci->str);
2536 DEF_CMD(emacs_spell)
2541 int rpt = RPT_NUM(ci);
2546 /* We always find a word that is partly *after* the given
2547 * make, but we want to find the word before point, so step
2550 doc_prev(ci->focus, ci->mark);
2552 if (ci->num != NO_NUMERIC)
2553 /* As a repeat-count was given, only look at intersting words */
2554 call("Spell:NextWord", ci->focus, 0, ci->mark);
2555 st = mark_dup(ci->mark);
2556 word = call_ret(str, "Spell:ThisWord", ci->focus, 0, ci->mark, NULL, 0, st);
2557 if (!word || !*word) {
2559 call("Message", ci->focus, 0, NULL,
2560 "Spell check reached end-of-file");
2561 call("Spell:Save", ci->focus);
2566 ret = call("Spell:Check", ci->focus, 0, NULL, word);
2572 call("Message", ci->focus, 0, NULL,
2573 strconcat(ci->focus, "\"", word,
2574 "\" is a correct spelling."));
2575 } else if (ret == Efalse) {
2578 buf_concat(&b.b, "\"");
2579 buf_concat(&b.b, word);
2580 buf_concat(&b.b, "\" is NOT correct");
2583 b.c = get_suggestion;
2584 call_comm("Spell:Suggest", ci->focus, &b.c,
2587 buf_concat(&b.b, " ... no suggestions");
2589 attr_set_str(&ci->focus->attrs, "spell:last-error",
2591 attr_set_str(&ci->focus->attrs, "spell:suggestions",
2593 attr_set_int(&ci->focus->attrs, "spell:offset", 0);
2595 call("Mode:set-all", ci->focus, rpt-1, NULL, ":Spell");
2596 call("Message:modal", ci->focus, 0, NULL, buf_final(&b.b));
2597 free(buf_final(&b.b));
2598 } else if (ret == Efail) {
2603 call("Message", ci->focus, 0, NULL,
2604 strconcat(ci->focus, "\"", word,
2605 "\" is not a word."));
2607 call("Message", ci->focus, 0, NULL,
2608 strconcat(ci->focus, "Spell check failed for \"", word,
2614 DEF_CMD(emacs_spell_choose)
2616 const char *k = ksuffix(ci, "K:Spell-");
2617 char match[4] = "( )";
2618 char *suggest = attr_find(ci->focus->attrs,
2619 "spell:suggestions");
2620 char *last = attr_find(ci->focus->attrs,
2621 "spell:last-error");
2626 if (!*k || !suggest || !last || !ci->mark)
2629 cp = strstr(suggest, match);
2633 ep = strchr(cp, ',');
2635 cp = strnsave(ci->focus, cp, ep-cp);
2637 m = mark_dup(ci->mark);
2638 i = utf8_strlen(last);
2640 doc_prev(ci->focus, m);
2643 call("doc:replace", ci->focus, 0, m, cp, 0, ci->mark);
2644 attr_set_str(&ci->focus->attrs, "spell:suggestions", NULL);
2645 attr_set_str(&ci->focus->attrs, "spell:last-error", NULL);
2648 doc_next(ci->focus, ci->mark);
2649 home_call(ci->home, "emacs:respell", ci->focus,
2650 ci->num, ci->mark, NULL,
2651 ci->num2, ci->mark2);
2657 DEF_CMD(emacs_spell_abort)
2662 DEF_CMD(emacs_spell_skip)
2665 doc_next(ci->focus, ci->mark);
2666 home_call(ci->home, "emacs:respell", ci->focus,
2667 ci->num, ci->mark, NULL,
2668 ci->num2, ci->mark2);
2673 DEF_CMD(emacs_spell_insert)
2675 char *last = attr_find(ci->focus->attrs,
2676 "spell:last-error");
2678 attr_set_str(&ci->focus->attrs, "spell:suggestions", NULL);
2679 attr_set_str(&ci->focus->attrs, "spell:last-error", NULL);
2680 call("Spell:AddWord", ci->focus, 1, NULL, last);
2684 doc_next(ci->focus, ci->mark);
2685 home_call(ci->home, "emacs:respell", ci->focus,
2686 ci->num, ci->mark, NULL,
2687 ci->num2, ci->mark2);
2692 DEF_CMD(emacs_spell_accept)
2694 char *last = attr_find(ci->focus->attrs,
2695 "spell:last-error");
2697 attr_set_str(&ci->focus->attrs, "spell:suggestions", NULL);
2698 attr_set_str(&ci->focus->attrs, "spell:last-error", NULL);
2699 call("Spell:AddWord", ci->focus, 0, NULL, last);
2703 doc_next(ci->focus, ci->mark);
2704 home_call(ci->home, "emacs:respell", ci->focus,
2705 ci->num, ci->mark, NULL,
2706 ci->num2, ci->mark2);
2711 static int spell_shift(struct pane *focus safe, int num, int inc)
2713 int o = pane_attr_get_int(focus, "spell:offset", 0);
2719 msg = pane_attr_get(focus, "spell:suggestions");
2722 for (i = 0; i < o && (c = strchr(msg, ',')) != NULL; i++)
2724 attr_set_int(&focus->attrs, "spell:offset", i);
2726 call("Mode:set-all", focus, num, NULL, ":Spell");
2727 call("Message:modal", focus, 0, NULL, msg);
2731 DEF_CMD(emacs_spell_left)
2733 return spell_shift(ci->focus, ci->num, -1);
2736 DEF_CMD(emacs_spell_right)
2738 return spell_shift(ci->focus, ci->num, 1);
2741 DEF_CMD(emacs_quote)
2746 struct mark *mk = NULL;
2747 bool free_mark = False;
2749 if (ci->num >= 0 && ci->num < NO_NUMERIC)
2751 else if (N2(ci) == N2_uniquote && ci->mark &&
2752 (str = attr_find(ci->mark->attrs, "emacs:unicode_char")) != NULL) {
2753 struct call_return cr;
2755 if (ci->num < 0 && i > 1)
2759 cr = call_ret(all, "Unicode-names", ci->focus, i, NULL, str);
2760 if (cr.s && cr.i && (wint_t)cr.i != WEOF) {
2762 call("Message", ci->focus, 0, NULL,
2763 strconcat(ci->focus,
2764 "Unicode char <", cr.s, ">"));
2765 call("Mode:set-num2", ci->focus,
2766 N2_uniquote | (i << 16));
2767 mk = mark_dup(ci->mark);
2768 doc_prev(ci->focus, mk);
2771 call("Message", ci->focus, 0, NULL,
2772 strconcat(ci->focus,
2773 "Cannot find another character <", str, ">"));
2776 } else if (wch == WEOF && ci->mark &&
2777 (mk = call_ret(mark2, "doc:point", ci->focus)) != NULL &&
2778 call("selection:clear", ci->focus, 0, mk) >= 1 &&
2779 (str = call_ret(strsave, "doc:get-str", ci->focus,
2780 0, NULL, NULL, 0, mk)) != NULL) {
2785 x = strtoul(str, &ep, 16);
2786 if (ep && *ep == 0) {
2788 call("Message", ci->focus, 0, NULL,
2789 strconcat(ci->focus, "Hex code 0x", str));
2791 struct call_return cr;
2792 cr = call_ret(all, "Unicode-names", ci->focus,
2796 call("Message", ci->focus, 0, NULL,
2797 strconcat(ci->focus,
2798 "Unicode char <", cr.s, ">"));
2800 attr_set_str(&ci->mark->attrs,
2801 "emacs:unicode_char",
2803 call("Mode:set-num2", ci->focus,
2804 N2_uniquote | (1 << 16));
2807 call("Message", ci->focus, 0, NULL,
2808 strconcat(ci->focus,
2809 "Cannot find character <", str, ">"));
2815 call("Mode:set-all", ci->focus, ci->num, NULL, ":CQ", ci->num2);
2818 call("Replace", ci->focus, 0, mk, put_utf8(b, wch));
2824 struct docs_helper {
2827 struct pane *p safe;
2830 DEF_CMD(emacs_doc_menu)
2832 const char *d = ksuffix(ci, "emacs:doc-menu:");
2833 struct pane *p = call_ret(pane, "docs:byname", ci->focus,
2837 struct pane *t = call_ret(pane, "ThisPane", ci->focus);
2839 home_call(p, "doc:attach-view", t, 1);
2844 DEF_CB(emacs_menu_add_doc)
2846 struct docs_helper *dh = container_of(ci->comm, struct docs_helper, c);
2847 char *name = pane_attr_get(ci->focus, "doc-name");
2850 return Efallthrough;
2854 call("menu:add", dh->p, 0, NULL, name, 0, NULL,
2855 strconcat(ci->home, " emacs:doc-menu:", name));
2856 return Efallthrough;
2859 DEF_CMD(emacs_menu_refresh)
2861 struct pane *p = ci->focus;
2862 char *n = pane_attr_get(p, "doc-name");
2863 struct docs_helper dh;
2865 if (!n || strcmp(n, "Documents") != 0)
2868 call("menu:clear", p);
2869 dh.c = emacs_menu_add_doc;
2872 call_comm("docs:byeach", p, &dh.c);
2873 call("menu:add", p, 0, NULL, "List all", 0, NULL, ":C-X :C-B");
2877 static char *menus[][3] = {
2878 { "Help/Recent", ":F1 l", "R" },
2879 { "Documents/List all", ":C-X :C-B", "R"},
2880 { "File/Open", ":C-X :C-F", "L"},
2881 { "File/Save", ":C-X :C-S", "L"},
2882 { "File/Exit", ":C-X :C-C", "L"},
2883 { "Edit/Copy", ":A-w", "L"},
2886 DEF_CMD(emacs_menubar_configure)
2890 for (i = 0; i < ARRAY_SIZE(menus); i++)
2891 call("menubar-add", ci->focus,
2892 menus[i][2][0] == 'R' ? 2 : 0,
2894 0, NULL, menus[i][1]);
2895 /* Allow other ancestor to configure */
2896 return Efallthrough;
2899 DEF_PFX_CMD(cx_cmd, ":CX");
2900 DEF_PFX_CMD(cx4_cmd, ":CX4");
2901 DEF_PFX_CMD(cx5_cmd, ":CX5");
2902 DEF_PFX_CMD(cx44_cmd, ":CX44");
2903 DEF_PFX_CMD(cc_cmd, ":CC");
2905 static struct map *emacs_map;
2906 DEF_LOOKUP_CMD(mode_emacs, emacs_map);
2908 static void emacs_init(void)
2916 key_add(m, "K:C-X", &cx_cmd.c);
2917 key_add(m, "K:CX-4", &cx4_cmd.c);
2918 /* C-\ is generated by C-4. Weird... */
2919 key_add(m, "K:CX:C-\\", &cx4_cmd.c);
2920 key_add(m, "K:CX-5", &cx5_cmd.c);
2921 key_add(m, "K:CX4-4", &cx44_cmd.c);
2922 key_add(m, "K:CX4:C-\\", &cx44_cmd.c);
2923 key_add(m, "K:C-C", &cc_cmd.c);
2925 key_add(m, "K:C-Q", &emacs_quote);
2927 for (i = 0; i < ARRAY_SIZE(move_commands); i++) {
2928 struct move_command *mc = &move_commands[i];
2929 key_add(m, mc->k1, &mc->cmd);
2931 key_add(m, mc->k2, &mc->cmd);
2933 key_add(m, mc->k3, &mc->cmd);
2936 for (i = 0; i < ARRAY_SIZE(simple_commands); i++) {
2937 struct simple_command *sc = &simple_commands[i];
2938 key_add(m, sc->k, &sc->cmd);
2941 key_add(m, "K:C-O", &emacs_open_line);
2943 key_add(m, "K:C-_", &emacs_undo);
2944 key_add(m, "K:CX-u", &emacs_undo);
2945 key_add(m, "K:C-/", &emacs_undo);
2946 key_add(m, "K:C-Z", &emacs_undo);
2948 key_add(m, "K:C-L", &emacs_recenter);
2950 key_add(m, "K:CX:C-F", &emacs_findfile);
2951 key_add(m, "K:CX4:C-F", &emacs_findfile);
2952 key_add(m, "K:CX4-f", &emacs_findfile);
2953 key_add(m, "K:CX44-f", &emacs_findfile);
2954 key_add_prefix(m, "File Found:", &emacs_findfile);
2956 key_add(m, "K:CX:C-W", &emacs_writefile);
2957 key_add_prefix(m, "emacs:write_file:", &emacs_do_writefile);
2959 key_add(m, "K:CX-i", &emacs_insertfile);
2960 key_add_prefix(m, "emacs:insert_file:", &emacs_do_insertfile);
2962 key_add(m, "K:CX-b", &emacs_finddoc);
2963 key_add(m, "K:CX4-b", &emacs_finddoc);
2964 key_add(m, "K:CX44-b", &emacs_finddoc);
2965 key_add_prefix(m, "Doc Found:", &emacs_finddoc);
2967 key_add(m, "K:CX:C-B", &emacs_viewdocs);
2968 key_add(m, "K:CX4:C-B", &emacs_viewdocs);
2969 key_add(m, "K:CX44:C-B", &emacs_viewdocs);
2971 key_add(m, "K:CX-k", &emacs_kill_doc);
2973 key_add(m, "K:CX-s", &emacs_save_all);
2975 key_add(m, "K:CX:C-V", &emacs_revisit);
2977 key_add(m, "K:CX-=", &emacs_curs_pos);
2978 key_add(m, "K:A-=", &emacs_word_count);
2980 key_add(m, "K:CX-<", &emacs_shift);
2981 key_add(m, "K:CX->", &emacs_shift);
2982 key_add(m, "K-<", &emacs_shift_again);
2983 key_add(m, "K->", &emacs_shift_again);
2985 key_add(m, "K:CX-{", &emacs_growx);
2986 key_add(m, "K:CX-}", &emacs_growx);
2987 key_add(m, "K-{", &emacs_growx_again);
2988 key_add(m, "K-}", &emacs_growx_again);
2990 key_add(m, "K:CX:C-=", &emacs_scale_relative);
2991 key_add(m, "K:CX:C--", &emacs_scale_relative);
2993 key_add(m, "K:C-S", &emacs_start_search);
2994 key_add(m, "K:C-R", &emacs_start_search);
2995 key_add(m, "K:A-%", &emacs_start_search);
2996 key_add(m, "render:reposition", &emacs_reposition);
2998 key_add(m, "K:CX:C-C", &emacs_exit);
2999 key_add(m, "emacs:deactivate", &emacs_deactivate);
3001 key_add(m, "K:C-U", &emacs_prefix);
3003 key_add(m, "K:A-!", &emacs_shell);
3004 key_add(m, "K:A-|", &emacs_shell);
3005 key_add(m, "Shell Command", &emacs_shell);
3007 key_add(m, "K:CX-`", &emacs_next_match);
3008 key_add(m, "K-`", &emacs_match_again);
3010 key_add(m, "K:CX-1", &emacs_close_others);
3011 key_add(m, "K-1", &emacs_close_others);
3013 key_add_range(m, "K:A-0", "K:A-9", &emacs_num);
3014 key_add(m, "K:A--", &emacs_neg);
3015 key_add(m, "K:C--", &emacs_neg);
3016 key_add(m, "K:C- ", &emacs_mark);
3017 key_add(m, "mode-set-mark", &emacs_mark);
3018 key_add(m, "mode-swap-mark", &emacs_swap_mark);
3019 key_add(m, "K:C-W", &emacs_wipe);
3020 key_add(m, "K:A-w", &emacs_copy);
3021 key_add(m, "K:C-Y", &emacs_yank);
3022 key_add(m, "K:A-y", &emacs_yank_pop);
3024 key_add(m, "K:A-g", &emacs_goto_line);
3025 key_add(m, "K:A-x", &emacs_command);
3026 key_add(m, "K:A-X", &emacs_command);
3027 key_add(m, "K:CC-m", &emacs_make);
3028 key_add(m, "K:CC:C-M", &emacs_make);
3030 key_add(m, "K:A:C-V", &emacs_move_view_other);
3032 key_add(m, "K:CX:C-Q", &emacs_readonly);
3034 key_add_prefix(m, "K:CQ-", &emacs_quote_insert);
3035 key_add_prefix(m, "K:CQ:C-", &emacs_quote_insert);
3037 key_add(m, "K:A-q", &emacs_fill);
3038 key_add(m, "K:A:C-Q", &emacs_fill);
3039 key_add(m, "K:A-/", &emacs_abbrev);
3040 key_add(m, "K:A-;", &emacs_spell);
3041 key_add(m, "emacs:respell", &emacs_spell);
3042 key_add_prefix(m, "K:Spell-", &emacs_spell_choose);
3043 key_add(m, "K:Spell-a", &emacs_spell_accept);
3044 key_add(m, "K:Spell-i", &emacs_spell_insert);
3045 key_add(m, "K:Spell- ", &emacs_spell_skip);
3046 key_add(m, "K:Spell:Enter", &emacs_spell_abort);
3047 key_add(m, "K:Spell:ESC", &emacs_spell_abort);
3048 key_add(m, "K:Spell:Left", &emacs_spell_left);
3049 key_add(m, "K:Spell:C-B", &emacs_spell_left);
3050 key_add(m, "K:Spell:Right", &emacs_spell_right);
3051 key_add(m, "K:Spell:C-F", &emacs_spell_right);
3053 key_add(m, "K:Help-l", &emacs_showinput);
3055 key_add(m, "emacs:command", &emacs_do_command);
3056 key_add(m, "interactive-cmd-version", &emacs_version);
3057 key_add(m, "interactive-cmd-log", &emacs_log);
3059 key_add(m, "M:Click-2", &emacs_paste);
3060 key_add(m, "M:C:Click-1", &emacs_paste);
3062 key_add(m, "K:CX-(", &emacs_macro_start);
3063 key_add(m, "K:CX-)", &emacs_macro_stop);
3064 key_add(m, "K:CX-e", &emacs_macro_run);
3065 key_add(m, "K-e", &emacs_macro_run);
3067 key_add(m, "menu:refresh", &emacs_menu_refresh);
3068 key_add_prefix(m, "emacs:doc-menu:", &emacs_doc_menu);
3070 key_add(m, "menubar:ready", &emacs_menubar_configure);
3075 DEF_CMD(attach_mode_emacs)
3077 struct pane *p = pane_register(ci->focus, 0, &mode_emacs.c, NULL);
3081 comm_call(ci->comm2, "cb", p);
3085 DEF_CMD(attach_file_entry)
3087 /* The 'type' passed must be static, not allocated */
3088 char *type = "shellcmd";
3090 if (ci->str && strcmp(ci->str, "file") == 0)
3092 else if (ci->str && strcmp(ci->str, "doc") == 0)
3094 pane_register(ci->focus, 0, &find_handle.c, type);
3099 void edlib_init(struct pane *ed safe)
3103 call_comm("global-set-command", ed, &attach_mode_emacs, 0, NULL, "attach-mode-emacs");
3104 call_comm("global-set-command", ed, &attach_file_entry, 0, NULL, "attach-file-entry");
3105 call_comm("global-set-command", ed, &emacs_shell, 0, NULL, "attach-shell-prompt");
3106 call_comm("global-set-command", ed, &emacs_selection_menu_add,
3107 0, NULL, "selection-menu:add-00-emacs");