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), "Tile:next", "K:CX-o"},
478 {CMD(emacs_simple), "Tile:prev", "K:CX-O"},
479 {CMD(emacs_simple), "Tile:y+", "K:CX-^"},
480 {CMD(emacs_simple), "Tile:split-y", "K:CX-2"},
481 {CMD(emacs_simple), "Tile:split-x", "K:CX-3"},
482 {CMD(emacs_simple), "Tile:close", "K:CX-0"},
483 {CMD(emacs_simple), "Tile: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("Tile: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 && cr.ret-1 > 1 && cr.s[strlen(cr.s)-1] == '/')
1362 /* Trailing slash on non-unique name must be removed */
1363 cr.s[strlen(cr.s)-1] = '\0';
1364 if (cr.s && (strlen(cr.s) <= strlen(b) && cr.ret-1 > 1)) {
1365 /* We need the dropdown - delete prefix and drop-down will
1370 start = mark_dup(ci->mark);
1371 call("doc:char", ci->focus, -strlen(b), start);
1372 call("Replace", ci->focus, 1, start);
1378 /* Replace 'b' with the result. */
1381 start = mark_dup(ci->mark);
1382 call("doc:char", ci->focus, -strlen(b), start);
1383 call("Replace", ci->focus, 1, start, cr.s);
1386 call("Message:modal", ci->focus, 0, NULL,
1387 strconcat(ci->focus, "No completion found for \"", b, "\"",
1388 " in \"", d, "\""));
1390 /* Now need to close the popup */
1395 DEF_CMD(emacs_finddoc)
1397 struct pane *p, *par;
1399 if (ksuffix(ci, "Doc Found:")[0] == 0) {
1401 char *defname = NULL;
1403 dflt = call_ret(pane, "docs:choose", ci->focus);
1405 defname = pane_attr_get(dflt, "doc-name");
1407 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2",
1413 attr_set_str(&p->attrs, "default", defname);
1414 if (ksuffix(ci, "K:CX44")[0]) {
1415 attr_set_str(&p->attrs, "prompt",
1416 "Find Document Popup");
1417 attr_set_str(&p->attrs, "done-key",
1419 } else if (ksuffix(ci, "K:CX4")[0]) {
1420 attr_set_str(&p->attrs, "prompt",
1421 "Find Document Other Window");
1422 attr_set_str(&p->attrs, "done-key",
1423 "Doc Found:Other Window");
1425 attr_set_str(&p->attrs, "prompt", "Find Document");
1426 attr_set_str(&p->attrs, "done-key",
1427 "Doc Found:This Window");
1429 call("doc:set-name", p, 0, NULL, "Find Document", -1);
1431 pane_register(p, 0, &find_handle.c, "doc");
1439 p = call_ret(pane, "docs:byname", ci->focus, 0, NULL, ci->str);
1443 if (strcmp(ci->key, "Doc Found:Other Window") == 0) {
1444 struct pane *this = call_ret(pane, "ThisPane", ci->focus);
1445 par = home_call_ret(pane, ci->focus, "DocPane", p);
1446 if (par && par != this) {
1447 pane_take_focus(par);
1450 par = call_ret(pane, "OtherPane", ci->focus);
1451 } else if (strcmp(ci->key, "Doc Found:Popup") == 0) {
1452 par = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "MD3tsa");
1454 par = call_ret(pane, "ThisPane", ci->focus);
1458 p = home_call_ret(pane, p, "doc:attach-view", par, 1);
1461 return p ? 1 : Efail;
1464 REDEF_CMD(emacs_doc_complete)
1466 /* Extract a document name from the document.
1467 * Attach the 'docs' document as a completing popup menu
1470 struct pane *pop, *p = NULL;
1471 struct call_return cr;
1476 str = call_ret(strsave, "doc:get-str", ci->focus);
1479 pop = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM1r");
1482 p = call_ret(pane, "docs:complete", pop);
1485 cr = call_ret(all, "Complete:prefix", p, 1, NULL, str);
1486 if (cr.s && (strlen(cr.s) <= strlen(str) && cr.ret - 1 > 1)) {
1487 /* We need the dropdown */
1490 start = mark_dup(ci->mark);
1491 call("doc:set-ref", ci->focus, 1, start);
1493 call("Replace", ci->focus, 1, start);
1498 /* Replace the prefix with the new value */
1501 start = mark_dup(ci->mark);
1502 call("doc:set-ref", ci->focus, 1, start);
1504 call("Replace", ci->focus, 1, start, cr.s);
1507 /* Now need to close the popup */
1512 DEF_CMD(emacs_viewdocs)
1514 struct pane *p, *par;
1517 docs = call_ret(pane, "docs:byname", ci->focus, 0, NULL, "*Documents*");
1521 if (ksuffix(ci, "K:CX44")[0]) {
1522 par = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "MD3tsa");
1523 } else if (ksuffix(ci, "K:CX4")[0]) {
1524 struct pane *this = call_ret(pane, "ThisPane", ci->focus);
1525 par = home_call_ret(pane, ci->focus, "DocPane", docs);
1526 if (par && par != this) {
1527 pane_take_focus(par);
1530 par = call_ret(pane, "OtherPane", ci->focus);
1532 par = call_ret(pane, "ThisPane", ci->focus);
1538 p = home_call_ret(pane, docs, "doc:attach-view", par, 1);
1544 struct pane *doc safe;
1548 DEF_CMD(choose_pane)
1550 struct pcb *cb = container_of(ci->comm, struct pcb, c);
1553 if (cb->p || !ci->str)
1555 d = call_ret(pane, "docs:byname", ci->focus, 0, NULL, ci->str);
1557 cb->p = home_call_ret(pane, ci->focus, "DocPane", d);
1559 home_call(cb->doc, "doc:attach-view", cb->p, 1);
1567 if (strcmp(ci->key, "cb:timer") == 0) {
1568 /* If focus has moved, don't show shell window,
1569 * probably a popup was requested by command.
1571 if (!pane_has_focus(ci->home))
1572 /* call back when lines or eof */
1575 if (strcmp(ci->key, "cb:eof") != 0) {
1577 if (ci->str && strchr(ci->str, 'P'))
1578 par = call_ret(pane, "PopupTile", ci->home, 0, NULL, "MD3tsa");
1580 par = call_ret(pane, "OtherPane", ci->home);
1582 home_call(ci->focus, "doc:attach-view", par, 1);
1585 str = call_ret(str, "doc:get-str", ci->focus);
1586 if (!str || !*str) {
1589 asprintf(&str, "(shell command completed with no output)");
1590 else if (ci->num > 0)
1591 asprintf(&str, "(shell command completed with no output (exit code %d))", ci->num);
1593 asprintf(&str, "(shell command completed with no output (signalled %d))", -ci->num);
1595 call("Message", ci->home, 0, NULL, str);
1600 DEF_CB(shell_insert_cb)
1602 char *str = call_ret(str, "doc:get-str", ci->focus);
1603 struct mark *mk = call_ret(mark2, "doc:point", ci->home);
1605 if (call("selection:clear", ci->home, 3, mk) >= 1)
1606 call("Replace", ci->home, 1, mk);
1607 call("Replace", ci->home, 0, NULL, str);
1612 DEF_CMD(emacs_shell)
1614 char *name = "*Shell Command Output*";
1615 struct pane *p, *doc, *par, *sc;
1616 char *path, *input = NULL;
1618 bool interpolate, pipe, popup;
1620 if (strcmp(ci->key, "Shell Command") != 0) {
1624 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2", 0, NULL,
1628 dirname = call_ret(strsave, "get-attr", ci->focus, 0, NULL, "dirname");
1629 attr_set_str(&p->attrs, "dirname", dirname ?: ".");
1630 attr_set_str(&p->attrs, "prompt", "Shell command");
1631 if (ci->num == 0 || ci->num == 1)
1632 strcat(aux, "i"); // interpolate
1634 strcat(aux, "P"); // popup
1635 if (strcmp(ci->key, "K:A-|") == 0)
1636 strcat(aux, "p"); // pipe from selection
1637 attr_set_str(&p->attrs, "popup-aux", aux);
1638 attr_set_str(&p->attrs, "done-key", "Shell Command");
1639 call("doc:set-name", p, 0, NULL, "Shell Command", -1);
1640 p = call_ret(pane, "attach-history", p, 0, NULL, "*Shell History*");
1642 p = pane_register(p, 0, &find_handle.c, "shellcmd");
1646 comm_call(ci->comm2, "cb", p);
1650 call("Message", ci->focus, 0, NULL, "Shell command aborted");
1654 interpolate = ci->str2 && strchr(ci->str2, 'i');
1655 pipe = ci->str2 && strchr(ci->str2, 'p');
1656 popup = ci->str2 && strchr(ci->str2, 'P');
1659 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
1661 input = call_ret(str, "doc:get-str", ci->focus,
1662 0, NULL, NULL, 0, mk);
1663 /* make the selection replacable */
1664 attr_set_int(&mk->attrs, "selection:active", 3);
1667 doc = call_ret(pane, "doc:from-text", ci->focus, 0, NULL, name, 0, NULL,
1673 path = pane_attr_get(ci->focus, "dirname");
1674 attr_set_str(&doc->attrs, "dirname", path);
1676 /* shellcmd is attached directly to the document, not in the view
1677 * stack. It is go-between for document and external command.
1678 * We don't need a doc attachment as no point is needed - we
1679 * always insert at the end.
1681 sc = call_ret(pane, "attach-shellcmd", doc, pipe ? 22 : 4,
1682 NULL, ci->str, 0, NULL, path);
1684 call("doc:replace", doc, 0, NULL,
1685 "Failed to run command - sorry\n");
1686 if (call("text-search", doc, 0, NULL, "diff|(stg|git).*show",
1687 0, NULL, ci->str) > 0)
1688 attr_set_str(&doc->attrs, "view-default", "diff");
1690 /* Close old shell docs, but if one is visible in current frame, replace
1696 call_comm("editor:notify:shell-reuse", ci->focus, &cb.c);
1698 /* choose_pane didn't attach, so set a callback to do
1699 * it when there is enough content.
1702 /* If it take more than 500msec, or includes 2
1703 * or more lines, we'll show in a pane, else
1704 * just show as a message.
1707 home_call_comm(sc, "shellcmd:set-callback",
1708 ci->focus, &shellcb,
1709 500, NULL, popup ? "P": "",
1713 par = call_ret(pane, "PopupTile", ci->focus,
1716 par = call_ret(pane, "OtherPane", ci->focus);
1719 home_call(doc, "doc:attach-view", par, 1);
1722 if (sc && interpolate)
1723 /* Need a callback when the pipe command finished */
1724 home_call_comm(sc, "shellcmd:set-callback", ci->focus,
1733 const char *last = ksuffix(ci, "K:A-");
1740 if (rpt == NO_NUMERIC)
1743 rpt = rpt * 10 + *last - '0';
1745 call("Mode:set-num", ci->focus, neg ? -rpt : rpt);
1746 call("Mode:set-num2", ci->focus, ci->num2);
1752 call("Mode:set-num", ci->focus, - ci->num);
1753 call("Mode:set-num2", ci->focus, ci->num2);
1757 DEF_CMD(emacs_prefix)
1759 /* as a generic arg (ctrl-U) which is positive and
1760 * as as a repeat-count of 4, but is different to 4.
1761 * I should probably allow digits to alter the number.
1763 call("Mode:set-num", ci->focus, 4);
1767 DEF_CMD(emacs_kill_doc)
1769 if (ci->num <= 0 || ci->num == NO_NUMERIC) {
1770 /* Check if modified. */
1771 char *m = pane_attr_get(ci->focus, "doc-modified");
1773 if (m && strcmp(m, "yes") == 0)
1774 f = pane_attr_get(ci->focus, "filename");
1776 call("Message:modal", ci->focus, 0, NULL,
1777 "Document is modified - please save or give prefix arg");
1781 return call("doc:destroy", ci->focus);
1784 DEF_CMD(emacs_revisit)
1786 return call("doc:load-file", ci->focus, 2, NULL, NULL, -1);
1789 DEF_CMD(emacs_save_all)
1791 int ret = call("docs:save-all", ci->focus, 0, NULL, NULL, 1);
1794 call("Message", ci->focus, 0, NULL, "No files need to be saved.");
1797 if (ci->num == NO_NUMERIC) {
1798 struct pane *p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM");
1800 return call("docs:show-modified", p);
1802 return call("docs:save-all", ci->focus);
1805 DEF_CMD(emacs_reposition)
1808 int repoint = attr_find_int(ci->focus->attrs, "emacs-repoint");
1810 if (repoint != -1) {
1811 /* Move point to end of display, if that is in
1812 * the right direction. That will mean it has moved
1815 m = mark_at_point(ci->focus, NULL, MARK_UNGROUPED);
1817 struct mark *m2 = mark_dup(m);
1818 call("Move-CursorXY", ci->focus, 0, m, NULL,
1820 INT_MAX, repoint < 0 ? ci->focus->h-1 : 0);
1822 /* can only move point backwards */
1823 if (m->seq < m2->seq)
1824 call("Move-to", ci->focus, 0, m);
1826 /* can only move point forwards */
1827 if (m->seq > m2->seq)
1828 call("Move-to", ci->focus, 0, m);
1832 attr_set_str(&ci->focus->attrs, "emacs-repoint", NULL);
1834 return Efallthrough;
1837 DEF_CMD(emacs_start_search)
1839 struct pane *p = NULL, *hp;
1842 hp = call_ret(pane, "attach-emacs-search-highlight", ci->focus);
1845 p = call_ret(pane, "PopupTile", hp, 0, NULL, "TR2",
1850 home_call(hp, "highlight:set-popup", p);
1852 attr_set_str(&p->attrs, "prompt", "Search");
1853 attr_set_str(&p->attrs, "done-key", "Search String");
1855 hp = call_ret(pane, "attach-history", p, 0, NULL, "*Search History*");
1859 call("doc:set-name", p, 0, NULL, "Search", -1);
1860 if (strcmp(ci->key, "K:C-R") == 0)
1862 if (strcmp(ci->key, "K:A-%") == 0)
1864 call_ret(pane, "attach-emacs-search", p, mode);
1869 DEF_CMD(emacs_command)
1873 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2", 0, NULL, "");
1876 attr_set_str(&p->attrs, "prompt", "Cmd");
1877 attr_set_str(&p->attrs, "done-key", "emacs:command");
1878 call("doc:set-name", p, 0, NULL, "K:Ax command", -1);
1879 p = call_ret(pane, "attach-history", p, 0, NULL, "*Command History*");
1881 pane_register(p, 0, &find_handle.c, "cmd");
1885 DEF_CMD(emacs_do_command)
1893 asprintf(&cmd, "interactive-cmd-%s", ci->str);
1896 ret = call(cmd, ci->focus, 0, ci->mark);
1897 free(cmd); cmd = NULL;
1899 asprintf(&cmd, "Command %s not found", ci->str);
1900 call("Message", ci->focus, 0, NULL, cmd);
1901 } else if (ret < 0) {
1902 asprintf(&cmd, "Command %s Failed", ci->str);
1903 call("Message", ci->focus, 0, NULL, cmd);
1911 struct call_return *cr = container_of(ci->comm, struct call_return, c);
1918 struct mark *m = vmark_new(cr->p, MARK_UNGROUPED, NULL);
1919 call("doc:list-add", cr->p, 0, m);
1920 call("doc:set-attr", cr->p, 0, m, "cmd", 0, NULL, cmd);
1925 REDEF_CMD(emacs_cmd_complete)
1928 struct pane *doc = NULL, *pop = NULL, *p;
1929 struct call_return cr;
1933 s = call_ret(strsave, "doc:get-str", ci->focus);
1936 doc = call_ret(pane, "attach-doc-list", ci->focus);
1939 call("doc:set-name", doc, 0, NULL, "*Command List*");
1940 call("doc:set:autoclose", doc, 1);
1941 attr_set_str(&doc->attrs, "render-simple", "format");
1942 attr_set_str(&doc->attrs, "heading", "");
1943 attr_set_str(&doc->attrs, "line-format", "%cmd");
1946 call_comm("keymap:list", ci->focus, &cr.c,
1947 0, NULL, "interactive-cmd-");
1948 call("doc:list-sort", doc, 0, NULL, "cmd");
1949 pop = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM1r");
1952 p = home_call_ret(pane, cr.p, "doc:attach-view", pop, -1, NULL, "simple");
1955 attr_set_str(&p->attrs, "done-key", "Replace");
1956 p = call_ret(pane, "attach-render-complete", p);
1959 cr = call_ret(all, "Complete:prefix", p, 1, NULL, s);
1960 if (cr.s && strlen(cr.s) <= strlen(s) && cr.ret-1 > 1) {
1961 /* We need the dropdown - delete prefix and drop-down will
1964 struct mark *start = mark_dup(ci->mark);
1965 call("Move-Char", ci->focus, -strlen(s), start);
1966 call("Replace", ci->focus, 1, start);
1971 /* Replace 's' with the result */
1972 struct mark *start = mark_dup(ci->mark);
1973 call("Move-Char", ci->focus, -strlen(s), start);
1974 call("Replace", ci->focus, 1, start, cr.s);
1977 /* Now need to close the popup */
1989 DEF_CMD(emacs_version)
1993 asprintf(&v, "%s ; emacs-mode - v" VERSION " - " VERS_DATE, edlib_version);
1995 call("Message", ci->focus, 0, NULL, v);
2002 /* View the debug log */
2003 struct pane *p, *doc;
2005 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL, "*Debug Log*");
2007 call("interactive-cmd-view-log", ci->focus);
2008 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL,
2013 p = call_ret(pane, "ThisPane", ci->focus);
2015 home_call(doc, "doc:attach-view", p, 1);
2021 struct mark *m = call_ret(mark2, "doc:point", ci->focus);
2023 call("selection:clear", ci->focus, 0, m);
2025 call("Move-to", ci->focus, 1);
2026 m = call_ret(mark2, "doc:point", ci->focus);
2028 /* ci->num == 1 means replacable */
2029 call("selection:set", ci->focus,
2030 ci->num == 1 ? 3 : 1, m);
2034 DEF_CMD(emacs_swap_mark)
2036 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2042 call("Move-to", ci->focus, 1); /* Move mark to point */
2043 call("Move-to", ci->focus, 0, m); /* Move point to old mark */
2044 call("selection:set", ci->focus,
2045 ci->num == 1 ? 3 : 1, mk);
2052 /* Delete text from point to mark */
2053 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2059 /* Remove any selection so it cannot be claimed and so replace this copy */
2060 call("selection:claim", ci->focus);
2061 call("selection:discard", ci->focus);
2063 str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, mk);
2065 call("copy:save", ci->focus, 0, NULL, str);
2066 ret = call("Replace", ci->focus, 1, mk);
2068 call("selection:clear", ci->focus, 0, mk);
2075 /* copy text from point to mark */
2076 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2081 /* Remove any selection so it cannot be claimed and so replace this copy */
2082 call("selection:claim", ci->focus);
2083 call("selection:discard", ci->focus);
2085 str = call_ret(strsave, "doc:get-str", ci->focus, 0, NULL, NULL, 0, mk);
2087 call("copy:save", ci->focus, 0, NULL, str);
2088 /* Clear current highlight */
2089 call("selection:clear", ci->focus, 0, mk);
2095 int n = RPT_NUM(ci);
2098 struct mark *m = NULL;
2100 /* If there is a selection elsewhere, we want to commit it */
2101 call("selection:discard", ci->focus);
2102 call("selection:commit", ci->focus);
2104 str = call_ret(strsave, "copy:get", ci->focus, n - 1);
2107 /* If mark exists and is active, replace marked regions */
2108 mk = call_ret(mark2, "doc:point", ci->focus);
2109 if (mk && call("selection:clear", ci->focus, 0, mk) >= 1) {
2110 char *str2 = call_ret(strsave, "doc:get-str", ci->focus,
2111 0, NULL, NULL, 0, mk);
2113 call("copy:save", ci->focus, 0, NULL, str2);
2117 call("Move-to", ci->focus, 1);
2118 call("Replace", ci->focus, 1, m, str);
2120 mk = call_ret(mark2, "doc:point", ci->focus);
2121 call("Mode:set-num2", ci->focus, N2_yank);
2125 DEF_CMD(emacs_yank_pop)
2127 struct mark *mk, *m;
2131 if (N2(ci) != N2_yank)
2133 mk = call_ret(mark2, "doc:point", ci->focus);
2137 str = call_ret(strsave, "copy:get", ci->focus, num);
2140 str = call_ret(strsave, "copy:get", ci->focus, num);
2146 call("Replace", ci->focus, 1, mk, str);
2147 call("Move-to", ci->focus, 1, m);
2149 call("Mode:set-num2", ci->focus, (num << 16) | N2_yank);
2153 DEF_CMD(emacs_selection_menu_add)
2155 struct pane *p = ci->focus;
2156 call("menu-add", p, 0, NULL, "Cut", 0, NULL, ":C-W");
2157 call("menu-add", p, 0, NULL, "Copy", 0, NULL, ":A-w");
2158 call("menu-add", p, 0, NULL, "Paste-in", 0, NULL, ":C-Y");
2159 return Efallthrough;
2162 DEF_CMD(emacs_goto_line)
2164 if (ci->num == NO_NUMERIC)
2166 call("CountLines", ci->focus, ci->num, ci->mark, "goto:line");
2167 call("Move-View-Pos", ci->focus, 0, ci->mark);
2171 DEF_CMD(emacs_next_match)
2173 call("Mode:set-num2", ci->focus, N2_match);
2174 call("Message:modal", ci->focus, 0, NULL, "Type ` to search again");
2175 return call("interactive-cmd-next-match", ci->focus, ci->num, NULL, NULL,
2176 strcmp(ci->key, "K-`") == 0);
2179 DEF_CMD(emacs_match_again)
2181 if (N2(ci) != N2_match)
2182 return Efallthrough;
2184 return emacs_next_match_func(ci);
2189 call("interactive-cmd-make", ci->focus,
2190 ci->num, ci->mark, NULL,
2191 strcmp(ci->key, "K:CC:C-M") == 0);
2195 DEF_CMD(emacs_paste)
2199 /* First commit the selection, then collect it */
2200 call("selection:commit", ci->focus);
2201 str = call_ret(strsave, "copy:get", ci->focus);
2203 call("Move-CursorXY", ci->focus,
2204 0, NULL, NULL, 0, NULL, NULL, ci->x, ci->y);
2209 call("Replace", ci->focus, 0, NULL, str);
2211 pane_take_focus(ci->focus);
2216 DEF_CMD(emacs_readonly)
2220 ro = pane_attr_get(ci->focus, "doc-readonly");
2222 if (ro && strcmp(ro,"yes") == 0)
2223 call("doc:set:readonly", ci->focus, 0);
2225 call("doc:set:readonly", ci->focus, 1);
2229 DEF_CMD(emacs_shift)
2234 shift = pane_attr_get_int(ci->focus, "render-wrap", -1);
2235 if (strcmp(ci->key, "K:CX->") == 0 || strcmp(ci->key, "K->") == 0)
2237 if (rpt == NO_NUMERIC) {
2242 } else if (rpt == -NO_NUMERIC) {
2249 } else if (rpt >= 0) {
2254 if (shift > 0 && shift + rpt < 0)
2260 attr_set_str(&ci->focus->attrs, "render-wrap", "yes");
2262 attr_set_int(&ci->focus->attrs, "render-wrap", shift);
2264 attr_set_str(&ci->focus->attrs, "render-wrap", "0 auto");
2266 /* When reducing shift to zero, don't enable auto */
2267 attr_set_int(&ci->focus->attrs, "render-wrap", 0);
2268 call("view:changed", ci->focus);
2269 call("Mode:set-num2", ci->focus, N2_shift);
2270 call("Message:modal", ci->focus, 0, NULL, "Type < or > to shift again");
2274 DEF_CMD(emacs_shift_again)
2276 if (N2(ci) != N2_shift)
2277 return Efallthrough;
2279 return emacs_shift_func(ci);
2282 DEF_CMD(emacs_growx)
2284 if (ci->key[strlen(ci->key)-1] == '}')
2285 call("Tile:x+", ci->focus, ci->num);
2287 call("Tile:x-", ci->focus, ci->num);
2288 call("Mode:set-num2", ci->focus, N2_growx);
2289 call("Message:modal", ci->focus, 0, NULL, "Type { or } to grow again");
2293 DEF_CMD(emacs_growx_again)
2295 if (N2(ci) != N2_growx)
2296 return Efallthrough;
2298 return emacs_growx_func(ci);
2301 DEF_CMD(emacs_scale_relative)
2303 struct pane *p = ci->focus;
2304 char *sc = pane_attr_get(p, "scale:M");
2309 call("Message:modal", p, 0, NULL,
2310 "Cannot zoom display with fixed-sized font");
2313 sc = pane_attr_get(p, "scale");
2314 if (sc && strchr(sc, 'x')) {
2315 call("Message:modal", p, 0, NULL,
2316 "Cannot zoom display with fixed layout");
2324 if (ci->key[strlen(ci->key)-1] == '-')
2325 scale = 10 * scale / 12;
2327 scale = 12 * scale / 10;
2328 snprintf(num, sizeof(num)-1, "%d", scale);
2329 call("Window:set:scale", p, 0, NULL, num);
2333 DEF_CMD(emacs_curs_pos)
2343 c = mark_dup(ci->mark);
2344 nxt = doc_following(ci->focus, c);
2346 while ((ch = doc_prev(ci->focus, c)) != WEOF && !is_eol(ch))
2348 while (mark_ordered_not_same(c, ci->mark)) {
2349 ch = doc_next(ci->focus, c);
2353 } else if (ch == '\t') {
2362 asprintf(&msg, "Cursor at column %d (%d chars), char is %d (0x%x)",
2363 col, chars, nxt, nxt);
2364 call("Message", ci->focus, 0, NULL, msg);
2369 DEF_CMD(emacs_word_count)
2372 struct pane *p = ci->focus;
2379 mk = call_ret(mark2, "doc:point", p);
2380 if (mk && attr_find_int(mk->attrs, "selection:active") <= 0)
2382 call("CountLines", p, 0, ci->mark);
2383 wp = attr_find_int(ci->mark->attrs, "word");
2386 call("CountLines", p, 0, mk);
2387 wm = attr_find_int(mk->attrs, "word");
2388 asprintf(&msg, "%d words in region", abs(wp-wm));
2390 int wd = pane_attr_get_int(p, "words", 0);
2391 asprintf(&msg, "After word%s %d of %d", wp==2?"":"s", wp-1, wd);
2393 call("Message", p, 0, NULL, msg);
2400 struct mark *mk = call_ret(mark2, "doc:point", ci->focus);
2401 struct mark *p = call_ret(mark, "doc:point", ci->focus);
2404 if (call("selection:clear", ci->focus, 0, mk, NULL, 0, p) == Efalse)
2407 if (strcmp(ci->key, "K:A-q") == 0) {
2408 if (call("fill-paragraph", ci->focus, ci->num, p, NULL,
2409 0, mk) == Efallthrough) {
2410 p2 = call_ret(pane, "attach-textfill", ci->focus);
2412 call("fill-paragraph", p2, ci->num, p, NULL,
2416 /* Don't try to load anything, the file-type should
2417 * have loaded something if relevant
2419 if (call("reindent-paragraph", ci->focus, ci->num, p, NULL,
2421 call("Message", ci->focus, 0, NULL,
2422 "Reindent not supported on the document.");
2427 DEF_CMD(emacs_abbrev)
2429 call("attach-abbrev", ci->focus);
2433 DEF_CMD(emacs_showinput)
2435 struct pane *p, *doc;
2437 if (call("input:log", ci->focus) <= 0) {
2438 call("Message", ci->focus, 0, NULL,
2439 "Cannot get log of recent input.");
2443 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL, "*Debug Log*");
2445 call("interactive-cmd-view-log", ci->focus);
2446 doc = call_ret(pane, "docs:byname", ci->focus, 0, NULL,
2451 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM");
2453 p = home_call_ret(pane, doc, "doc:attach-view", p, 1);
2455 call("doc:file", p, 1);
2460 DEF_CMD(emacs_macro_start)
2464 ret = call("macro:capture", ci->focus);
2466 call("Message", ci->focus, 0, NULL,
2467 "Macro capture already happening");
2469 call("Message", ci->focus, 0, NULL,
2470 "Macro facility not available");
2474 DEF_CMD(emacs_macro_stop)
2478 ret = call("macro:finished", ci->focus, 2);
2480 call("Message", ci->focus, 0, NULL,
2481 "Macro successfully created.");
2482 else if (ret == Efalse)
2483 call("Message", ci->focus, 0, NULL,
2484 "No macro being created.");
2486 call("Message", ci->focus, 0, NULL,
2487 "Failure creating macro.");
2491 DEF_CMD(emacs_macro_run)
2493 int cnt = RPT_NUM(ci);
2495 if (strcmp(ci->key, "K-e") == 0 && N2(ci) != N2_runmacro)
2496 return Efallthrough;
2501 call("macro:replay", ci->focus, 1) > 0)
2504 call("Mode:set-num2", ci->focus, N2_runmacro);
2505 call("Message:modal", ci->focus, 0, NULL, "Type 'e' to repeat macro");
2506 return cnt < 1 ? 1 : Efail;
2515 static const char spell_choices[] =
2516 "0123456789-=;,/ABCDEFGHIJKLMNOPQRSTUVWXYZ";
2517 DEF_CB(get_suggestion)
2519 struct bb *b = container_of(ci->comm, struct bb, c);
2525 buf_concat(&b->b, " - (a)ccept, (i)nsert - ");
2527 buf_concat(&b->b, ", ");
2529 if (b->count < (int)sizeof(spell_choices)-1) {
2530 buf_append(&b->b, '(');
2531 buf_append(&b->b, spell_choices[b->count]);
2532 buf_append(&b->b, ')');
2535 buf_concat(&b->b, ci->str);
2539 DEF_CMD(emacs_spell)
2544 int rpt = RPT_NUM(ci);
2549 /* We always find a word that is partly *after* the given
2550 * make, but we want to find the word before point, so step
2553 doc_prev(ci->focus, ci->mark);
2555 if (ci->num != NO_NUMERIC)
2556 /* As a repeat-count was given, only look at intersting words */
2557 call("Spell:NextWord", ci->focus, 0, ci->mark);
2558 st = mark_dup(ci->mark);
2559 word = call_ret(str, "Spell:ThisWord", ci->focus, 0, ci->mark, NULL, 0, st);
2560 if (!word || !*word) {
2562 call("Message", ci->focus, 0, NULL,
2563 "Spell check reached end-of-file");
2564 call("Spell:Save", ci->focus);
2569 ret = call("Spell:Check", ci->focus, 0, NULL, word);
2575 call("Message", ci->focus, 0, NULL,
2576 strconcat(ci->focus, "\"", word,
2577 "\" is a correct spelling."));
2578 } else if (ret == Efalse) {
2581 buf_concat(&b.b, "\"");
2582 buf_concat(&b.b, word);
2583 buf_concat(&b.b, "\" is NOT correct");
2586 b.c = get_suggestion;
2587 call_comm("Spell:Suggest", ci->focus, &b.c,
2590 buf_concat(&b.b, " ... no suggestions");
2592 attr_set_str(&ci->focus->attrs, "spell:last-error",
2594 attr_set_str(&ci->focus->attrs, "spell:suggestions",
2596 attr_set_int(&ci->focus->attrs, "spell:offset", 0);
2598 call("Mode:set-all", ci->focus, rpt-1, NULL, ":Spell");
2599 call("Message:modal", ci->focus, 0, NULL, buf_final(&b.b));
2600 free(buf_final(&b.b));
2601 } else if (ret == Efail) {
2606 call("Message", ci->focus, 0, NULL,
2607 strconcat(ci->focus, "\"", word,
2608 "\" is not a word."));
2610 call("Message", ci->focus, 0, NULL,
2611 strconcat(ci->focus, "Spell check failed for \"", word,
2617 DEF_CMD(emacs_spell_choose)
2619 const char *k = ksuffix(ci, "K:Spell-");
2620 char match[4] = "( )";
2621 char *suggest = attr_find(ci->focus->attrs,
2622 "spell:suggestions");
2623 char *last = attr_find(ci->focus->attrs,
2624 "spell:last-error");
2629 if (!*k || !suggest || !last || !ci->mark)
2632 cp = strstr(suggest, match);
2636 ep = strchr(cp, ',');
2638 cp = strnsave(ci->focus, cp, ep-cp);
2640 m = mark_dup(ci->mark);
2641 i = utf8_strlen(last);
2643 doc_prev(ci->focus, m);
2646 call("doc:replace", ci->focus, 0, m, cp, 0, ci->mark);
2647 attr_set_str(&ci->focus->attrs, "spell:suggestions", NULL);
2648 attr_set_str(&ci->focus->attrs, "spell:last-error", NULL);
2651 doc_next(ci->focus, ci->mark);
2652 home_call(ci->home, "emacs:respell", ci->focus,
2653 ci->num, ci->mark, NULL,
2654 ci->num2, ci->mark2);
2660 DEF_CMD(emacs_spell_abort)
2665 DEF_CMD(emacs_spell_skip)
2668 doc_next(ci->focus, ci->mark);
2669 home_call(ci->home, "emacs:respell", ci->focus,
2670 ci->num, ci->mark, NULL,
2671 ci->num2, ci->mark2);
2676 DEF_CMD(emacs_spell_insert)
2678 char *last = attr_find(ci->focus->attrs,
2679 "spell:last-error");
2681 attr_set_str(&ci->focus->attrs, "spell:suggestions", NULL);
2682 attr_set_str(&ci->focus->attrs, "spell:last-error", NULL);
2683 call("Spell:AddWord", ci->focus, 1, NULL, last);
2687 doc_next(ci->focus, ci->mark);
2688 home_call(ci->home, "emacs:respell", ci->focus,
2689 ci->num, ci->mark, NULL,
2690 ci->num2, ci->mark2);
2695 DEF_CMD(emacs_spell_accept)
2697 char *last = attr_find(ci->focus->attrs,
2698 "spell:last-error");
2700 attr_set_str(&ci->focus->attrs, "spell:suggestions", NULL);
2701 attr_set_str(&ci->focus->attrs, "spell:last-error", NULL);
2702 call("Spell:AddWord", ci->focus, 0, NULL, last);
2706 doc_next(ci->focus, ci->mark);
2707 home_call(ci->home, "emacs:respell", ci->focus,
2708 ci->num, ci->mark, NULL,
2709 ci->num2, ci->mark2);
2714 static int spell_shift(struct pane *focus safe, int num, int inc)
2716 int o = pane_attr_get_int(focus, "spell:offset", 0);
2722 msg = pane_attr_get(focus, "spell:suggestions");
2725 for (i = 0; i < o && (c = strchr(msg, ',')) != NULL; i++)
2727 attr_set_int(&focus->attrs, "spell:offset", i);
2729 call("Mode:set-all", focus, num, NULL, ":Spell");
2730 call("Message:modal", focus, 0, NULL, msg);
2734 DEF_CMD(emacs_spell_left)
2736 return spell_shift(ci->focus, ci->num, -1);
2739 DEF_CMD(emacs_spell_right)
2741 return spell_shift(ci->focus, ci->num, 1);
2744 DEF_CMD(emacs_quote)
2749 struct mark *mk = NULL;
2750 bool free_mark = False;
2752 if (ci->num >= 0 && ci->num < NO_NUMERIC)
2754 else if (N2(ci) == N2_uniquote && ci->mark &&
2755 (str = attr_find(ci->mark->attrs, "emacs:unicode_char")) != NULL) {
2756 struct call_return cr;
2758 if (ci->num < 0 && i > 1)
2762 cr = call_ret(all, "Unicode-names", ci->focus, i, NULL, str);
2763 if (cr.s && cr.i && (wint_t)cr.i != WEOF) {
2765 call("Message", ci->focus, 0, NULL,
2766 strconcat(ci->focus,
2767 "Unicode char <", cr.s, ">"));
2768 call("Mode:set-num2", ci->focus,
2769 N2_uniquote | (i << 16));
2770 mk = mark_dup(ci->mark);
2771 doc_prev(ci->focus, mk);
2774 call("Message", ci->focus, 0, NULL,
2775 strconcat(ci->focus,
2776 "Cannot find another character <", str, ">"));
2779 } else if (wch == WEOF && ci->mark &&
2780 (mk = call_ret(mark2, "doc:point", ci->focus)) != NULL &&
2781 call("selection:clear", ci->focus, 0, mk) >= 1 &&
2782 (str = call_ret(strsave, "doc:get-str", ci->focus,
2783 0, NULL, NULL, 0, mk)) != NULL) {
2788 x = strtoul(str, &ep, 16);
2789 if (ep && *ep == 0) {
2791 call("Message", ci->focus, 0, NULL,
2792 strconcat(ci->focus, "Hex code 0x", str));
2794 struct call_return cr;
2795 cr = call_ret(all, "Unicode-names", ci->focus,
2799 call("Message", ci->focus, 0, NULL,
2800 strconcat(ci->focus,
2801 "Unicode char <", cr.s, ">"));
2803 attr_set_str(&ci->mark->attrs,
2804 "emacs:unicode_char",
2806 call("Mode:set-num2", ci->focus,
2807 N2_uniquote | (1 << 16));
2810 call("Message", ci->focus, 0, NULL,
2811 strconcat(ci->focus,
2812 "Cannot find character <", str, ">"));
2818 call("Mode:set-all", ci->focus, ci->num, NULL, ":CQ", ci->num2);
2821 call("Replace", ci->focus, 0, mk, put_utf8(b, wch));
2827 struct docs_helper {
2830 struct pane *p safe;
2833 DEF_CMD(emacs_doc_menu)
2835 const char *d = ksuffix(ci, "emacs:doc-menu:");
2836 struct pane *p = call_ret(pane, "docs:byname", ci->focus,
2840 struct pane *t = call_ret(pane, "ThisPane", ci->focus);
2842 home_call(p, "doc:attach-view", t, 1);
2847 DEF_CB(emacs_menu_add_doc)
2849 struct docs_helper *dh = container_of(ci->comm, struct docs_helper, c);
2850 char *name = pane_attr_get(ci->focus, "doc-name");
2853 return Efallthrough;
2857 call("menu:add", dh->p, 0, NULL, name, 0, NULL,
2858 strconcat(ci->home, " emacs:doc-menu:", name));
2859 return Efallthrough;
2862 DEF_CMD(emacs_menu_refresh)
2864 struct pane *p = ci->focus;
2865 char *n = pane_attr_get(p, "doc-name");
2866 struct docs_helper dh;
2868 if (!n || strcmp(n, "Documents") != 0)
2871 call("menu:clear", p);
2872 dh.c = emacs_menu_add_doc;
2875 call_comm("docs:byeach", p, &dh.c);
2876 call("menu:add", p, 0, NULL, "List all", 0, NULL, ":C-X :C-B");
2880 static char *menus[][3] = {
2881 { "Help/Recent", ":F1 l", "R" },
2882 { "Documents/List all", ":C-X :C-B", "R"},
2883 { "File/Open", ":C-X :C-F", "L"},
2884 { "File/Save", ":C-X :C-S", "L"},
2885 { "File/Exit", ":C-X :C-C", "L"},
2886 { "Edit/Copy", ":A-w", "L"},
2889 DEF_CMD(emacs_menubar_configure)
2893 for (i = 0; i < ARRAY_SIZE(menus); i++)
2894 call("menubar-add", ci->focus,
2895 menus[i][2][0] == 'R' ? 2 : 0,
2897 0, NULL, menus[i][1]);
2898 /* Allow other ancestor to configure */
2899 return Efallthrough;
2902 DEF_PFX_CMD(cx_cmd, ":CX");
2903 DEF_PFX_CMD(cx4_cmd, ":CX4");
2904 DEF_PFX_CMD(cx5_cmd, ":CX5");
2905 DEF_PFX_CMD(cx44_cmd, ":CX44");
2906 DEF_PFX_CMD(cc_cmd, ":CC");
2908 static struct map *emacs_map;
2909 DEF_LOOKUP_CMD(mode_emacs, emacs_map);
2911 static void emacs_init(void)
2919 key_add(m, "K:C-X", &cx_cmd.c);
2920 key_add(m, "K:CX-4", &cx4_cmd.c);
2921 /* C-\ is generated by C-4. Weird... */
2922 key_add(m, "K:CX:C-\\", &cx4_cmd.c);
2923 key_add(m, "K:CX-5", &cx5_cmd.c);
2924 key_add(m, "K:CX4-4", &cx44_cmd.c);
2925 key_add(m, "K:CX4:C-\\", &cx44_cmd.c);
2926 key_add(m, "K:C-C", &cc_cmd.c);
2928 key_add(m, "K:C-Q", &emacs_quote);
2930 for (i = 0; i < ARRAY_SIZE(move_commands); i++) {
2931 struct move_command *mc = &move_commands[i];
2932 key_add(m, mc->k1, &mc->cmd);
2934 key_add(m, mc->k2, &mc->cmd);
2936 key_add(m, mc->k3, &mc->cmd);
2939 for (i = 0; i < ARRAY_SIZE(simple_commands); i++) {
2940 struct simple_command *sc = &simple_commands[i];
2941 key_add(m, sc->k, &sc->cmd);
2944 key_add(m, "K:C-O", &emacs_open_line);
2946 key_add(m, "K:C-_", &emacs_undo);
2947 key_add(m, "K:CX-u", &emacs_undo);
2948 key_add(m, "K:C-/", &emacs_undo);
2949 key_add(m, "K:C-Z", &emacs_undo);
2951 key_add(m, "K:C-L", &emacs_recenter);
2953 key_add(m, "K:CX:C-F", &emacs_findfile);
2954 key_add(m, "K:CX4:C-F", &emacs_findfile);
2955 key_add(m, "K:CX4-f", &emacs_findfile);
2956 key_add(m, "K:CX44-f", &emacs_findfile);
2957 key_add_prefix(m, "File Found:", &emacs_findfile);
2959 key_add(m, "K:CX:C-W", &emacs_writefile);
2960 key_add_prefix(m, "emacs:write_file:", &emacs_do_writefile);
2962 key_add(m, "K:CX-i", &emacs_insertfile);
2963 key_add_prefix(m, "emacs:insert_file:", &emacs_do_insertfile);
2965 key_add(m, "K:CX-b", &emacs_finddoc);
2966 key_add(m, "K:CX4-b", &emacs_finddoc);
2967 key_add(m, "K:CX44-b", &emacs_finddoc);
2968 key_add_prefix(m, "Doc Found:", &emacs_finddoc);
2970 key_add(m, "K:CX:C-B", &emacs_viewdocs);
2971 key_add(m, "K:CX4:C-B", &emacs_viewdocs);
2972 key_add(m, "K:CX44:C-B", &emacs_viewdocs);
2974 key_add(m, "K:CX-k", &emacs_kill_doc);
2976 key_add(m, "K:CX-s", &emacs_save_all);
2978 key_add(m, "K:CX:C-V", &emacs_revisit);
2980 key_add(m, "K:CX-=", &emacs_curs_pos);
2981 key_add(m, "K:A-=", &emacs_word_count);
2983 key_add(m, "K:CX-<", &emacs_shift);
2984 key_add(m, "K:CX->", &emacs_shift);
2985 key_add(m, "K-<", &emacs_shift_again);
2986 key_add(m, "K->", &emacs_shift_again);
2988 key_add(m, "K:CX-{", &emacs_growx);
2989 key_add(m, "K:CX-}", &emacs_growx);
2990 key_add(m, "K-{", &emacs_growx_again);
2991 key_add(m, "K-}", &emacs_growx_again);
2993 key_add(m, "K:CX:C-=", &emacs_scale_relative);
2994 key_add(m, "K:CX:C--", &emacs_scale_relative);
2996 key_add(m, "K:C-S", &emacs_start_search);
2997 key_add(m, "K:C-R", &emacs_start_search);
2998 key_add(m, "K:A-%", &emacs_start_search);
2999 key_add(m, "render:reposition", &emacs_reposition);
3001 key_add(m, "K:CX:C-C", &emacs_exit);
3002 key_add(m, "emacs:deactivate", &emacs_deactivate);
3004 key_add(m, "K:C-U", &emacs_prefix);
3006 key_add(m, "K:A-!", &emacs_shell);
3007 key_add(m, "K:A-|", &emacs_shell);
3008 key_add(m, "Shell Command", &emacs_shell);
3010 key_add(m, "K:CX-`", &emacs_next_match);
3011 key_add(m, "K-`", &emacs_match_again);
3013 key_add(m, "K:CX-1", &emacs_close_others);
3014 key_add(m, "K-1", &emacs_close_others);
3016 key_add_range(m, "K:A-0", "K:A-9", &emacs_num);
3017 key_add(m, "K:A--", &emacs_neg);
3018 key_add(m, "K:C--", &emacs_neg);
3019 key_add(m, "K:C- ", &emacs_mark);
3020 key_add(m, "mode-set-mark", &emacs_mark);
3021 key_add(m, "mode-swap-mark", &emacs_swap_mark);
3022 key_add(m, "K:C-W", &emacs_wipe);
3023 key_add(m, "K:A-w", &emacs_copy);
3024 key_add(m, "K:C-Y", &emacs_yank);
3025 key_add(m, "K:A-y", &emacs_yank_pop);
3027 key_add(m, "K:A-g", &emacs_goto_line);
3028 key_add(m, "K:A-x", &emacs_command);
3029 key_add(m, "K:A-X", &emacs_command);
3030 key_add(m, "K:CC-m", &emacs_make);
3031 key_add(m, "K:CC:C-M", &emacs_make);
3033 key_add(m, "K:A:C-V", &emacs_move_view_other);
3035 key_add(m, "K:CX:C-Q", &emacs_readonly);
3037 key_add_prefix(m, "K:CQ-", &emacs_quote_insert);
3038 key_add_prefix(m, "K:CQ:C-", &emacs_quote_insert);
3040 key_add(m, "K:A-q", &emacs_fill);
3041 key_add(m, "K:A:C-Q", &emacs_fill);
3042 key_add(m, "K:A-/", &emacs_abbrev);
3043 key_add(m, "K:A-;", &emacs_spell);
3044 key_add(m, "emacs:respell", &emacs_spell);
3045 key_add_prefix(m, "K:Spell-", &emacs_spell_choose);
3046 key_add(m, "K:Spell-a", &emacs_spell_accept);
3047 key_add(m, "K:Spell-i", &emacs_spell_insert);
3048 key_add(m, "K:Spell- ", &emacs_spell_skip);
3049 key_add(m, "K:Spell:Enter", &emacs_spell_abort);
3050 key_add(m, "K:Spell:ESC", &emacs_spell_abort);
3051 key_add(m, "K:Spell:Left", &emacs_spell_left);
3052 key_add(m, "K:Spell:C-B", &emacs_spell_left);
3053 key_add(m, "K:Spell:Right", &emacs_spell_right);
3054 key_add(m, "K:Spell:C-F", &emacs_spell_right);
3056 key_add(m, "K:Help-l", &emacs_showinput);
3058 key_add(m, "emacs:command", &emacs_do_command);
3059 key_add(m, "interactive-cmd-version", &emacs_version);
3060 key_add(m, "interactive-cmd-log", &emacs_log);
3062 key_add(m, "M:Click-2", &emacs_paste);
3063 key_add(m, "M:C:Click-1", &emacs_paste);
3065 key_add(m, "K:CX-(", &emacs_macro_start);
3066 key_add(m, "K:CX-)", &emacs_macro_stop);
3067 key_add(m, "K:CX-e", &emacs_macro_run);
3068 key_add(m, "K-e", &emacs_macro_run);
3070 key_add(m, "menu:refresh", &emacs_menu_refresh);
3071 key_add_prefix(m, "emacs:doc-menu:", &emacs_doc_menu);
3073 key_add(m, "menubar:ready", &emacs_menubar_configure);
3078 DEF_CMD(attach_mode_emacs)
3080 struct pane *p = pane_register(ci->focus, 0, &mode_emacs.c, NULL);
3084 comm_call(ci->comm2, "cb", p);
3088 DEF_CMD(attach_file_entry)
3090 /* The 'type' passed must be static, not allocated */
3091 char *type = "shellcmd";
3093 if (ci->str && strcmp(ci->str, "file") == 0)
3095 else if (ci->str && strcmp(ci->str, "doc") == 0)
3097 pane_register(ci->focus, 0, &find_handle.c, type);
3102 void edlib_init(struct pane *ed safe)
3106 call_comm("global-set-command", ed, &attach_mode_emacs, 0, NULL, "attach-mode-emacs");
3107 call_comm("global-set-command", ed, &attach_file_entry, 0, NULL, "attach-file-entry");
3108 call_comm("global-set-command", ed, &emacs_shell, 0, NULL, "attach-shell-prompt");
3109 call_comm("global-set-command", ed, &emacs_selection_menu_add,
3110 0, NULL, "selection-menu:add-00-emacs");