1 /* this file is included from sam.c */
6 // FIXME: avoid this redirection?
9 VisCommandFunction
*func
;
13 static void cmdfree(CmdUser
*cmd
) {
16 free((char*)cmd
->def
.name
);
17 free(VIS_HELP_USE((char*)cmd
->def
.help
));
21 bool vis_cmd_register(Vis
*vis
, const char *name
, const char *help
, void *data
, VisCommandFunction
*func
) {
24 if (!vis
->usercmds
&& !(vis
->usercmds
= map_new()))
26 CmdUser
*cmd
= calloc(1, sizeof *cmd
);
29 if (!(cmd
->def
.name
= strdup(name
)))
32 if (help
&& !(cmd
->def
.help
= strdup(help
)))
35 cmd
->def
.flags
= CMD_ARGV
|CMD_FORCE
|CMD_ONCE
|CMD_ADDRESS_ALL
;
36 cmd
->def
.func
= cmd_user
;
39 if (!map_put(vis
->cmds
, name
, &cmd
->def
))
41 if (!map_put(vis
->usercmds
, name
, cmd
)) {
42 map_delete(vis
->cmds
, name
);
51 bool vis_cmd_unregister(Vis
*vis
, const char *name
) {
54 CmdUser
*cmd
= map_get(vis
->usercmds
, name
);
57 if (!map_delete(vis
->cmds
, name
))
59 if (!map_delete(vis
->usercmds
, name
))
65 static void option_free(OptionDef
*opt
) {
68 for (size_t i
= 0; i
< LENGTH(options
); i
++) {
69 if (opt
== &options
[i
])
73 for (const char **name
= opt
->names
; *name
; name
++)
75 free(VIS_HELP_USE((char*)opt
->help
));
79 bool vis_option_register(Vis
*vis
, const char *names
[], enum VisOption flags
,
80 VisOptionFunction
*func
, void *context
, const char *help
) {
82 if (!names
|| !names
[0])
85 for (const char **name
= names
; *name
; name
++) {
86 if (map_get(vis
->options
, *name
))
89 OptionDef
*opt
= calloc(1, sizeof *opt
);
92 for (size_t i
= 0; i
< LENGTH(opt
->names
)-1 && names
[i
]; i
++) {
93 if (!(opt
->names
[i
] = strdup(names
[i
])))
98 opt
->context
= context
;
100 if (help
&& !(opt
->help
= strdup(help
)))
103 for (const char **name
= names
; *name
; name
++)
104 map_put(vis
->options
, *name
, opt
);
111 bool vis_option_unregister(Vis
*vis
, const char *name
) {
112 OptionDef
*opt
= map_get(vis
->options
, name
);
115 for (const char **alias
= opt
->names
; *alias
; alias
++) {
116 if (!map_delete(vis
->options
, *alias
))
123 static bool cmd_user(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
124 CmdUser
*user
= map_get(vis
->usercmds
, argv
[0]);
125 return user
&& user
->func(vis
, win
, user
->data
, cmd
->flags
== '!', argv
, sel
, range
);
128 static void windows_arrange(Vis
*vis
, enum UiLayout layout
) {
129 vis
->ui
->arrange(vis
->ui
, layout
);
132 static void tabwidth_set(Vis
*vis
, int tabwidth
) {
133 if (tabwidth
< 1 || tabwidth
> 8)
135 for (Win
*win
= vis
->windows
; win
; win
= win
->next
)
136 view_tabwidth_set(win
->view
, tabwidth
);
137 vis
->tabwidth
= tabwidth
;
140 /* parse human-readable boolean value in s. If successful, store the result in
141 * outval and return true. Else return false and leave outval alone. */
142 static bool parse_bool(const char *s
, bool *outval
) {
143 for (const char **t
= (const char*[]){"1", "true", "yes", "on", NULL
}; *t
; t
++) {
144 if (!strcasecmp(s
, *t
)) {
149 for (const char **f
= (const char*[]){"0", "false", "no", "off", NULL
}; *f
; f
++) {
150 if (!strcasecmp(s
, *f
)) {
158 static bool cmd_set(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
160 if (!argv
[1] || !argv
[1][0] || argv
[3]) {
161 vis_info_show(vis
, "Expecting: set option [value]");
166 strncpy(name
, argv
[1], sizeof(name
)-1);
167 char *lastchar
= &name
[strlen(name
)-1];
168 bool toggle
= (*lastchar
== '!');
172 OptionDef
*opt
= map_closest(vis
->options
, name
);
174 vis_info_show(vis
, "Unknown option: `%s'", name
);
178 if (!win
&& (opt
->flags
& VIS_OPTION_NEED_WINDOW
)) {
179 vis_info_show(vis
, "Need active window for `:set %s'", name
);
184 if (!(opt
->flags
& VIS_OPTION_TYPE_BOOL
)) {
185 vis_info_show(vis
, "Only boolean options can be toggled");
189 vis_info_show(vis
, "Can not specify option value when toggling");
195 if (opt
->flags
& VIS_OPTION_TYPE_STRING
) {
196 if (!(opt
->flags
& VIS_OPTION_VALUE_OPTIONAL
) && !argv
[2]) {
197 vis_info_show(vis
, "Expecting string option value");
201 } else if (opt
->flags
& VIS_OPTION_TYPE_BOOL
) {
204 } else if (!parse_bool(argv
[2], &arg
.b
)) {
205 vis_info_show(vis
, "Expecting boolean option value not: `%s'", argv
[2]);
208 } else if (opt
->flags
& VIS_OPTION_TYPE_NUMBER
) {
210 vis_info_show(vis
, "Expecting number");
215 long lval
= strtol(argv
[2], &ep
, 10);
216 if (argv
[2][0] == '\0' || *ep
!= '\0') {
217 vis_info_show(vis
, "Invalid number");
221 if ((errno
== ERANGE
&& (lval
== LONG_MAX
|| lval
== LONG_MIN
)) ||
222 (lval
> INT_MAX
|| lval
< INT_MIN
)) {
223 vis_info_show(vis
, "Number overflow");
228 vis_info_show(vis
, "Expecting positive number");
236 size_t opt_index
= 0;
237 for (; opt_index
< LENGTH(options
); opt_index
++) {
238 if (opt
== &options
[opt_index
])
245 char *shell
= strdup(arg
.s
);
247 vis_info_show(vis
, "Failed to change shell");
254 case OPTION_ESCDELAY
:
256 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
257 termkey_set_waittime(termkey
, arg
.i
);
260 case OPTION_EXPANDTAB
:
261 vis
->expandtab
= toggle
? !vis
->expandtab
: arg
.b
;
263 case OPTION_AUTOINDENT
:
264 vis
->autoindent
= toggle
? !vis
->autoindent
: arg
.b
;
266 case OPTION_TABWIDTH
:
267 tabwidth_set(vis
, arg
.i
);
269 case OPTION_SHOW_SPACES
:
270 case OPTION_SHOW_TABS
:
271 case OPTION_SHOW_NEWLINES
:
272 case OPTION_SHOW_EOF
:
274 const int values
[] = {
275 [OPTION_SHOW_SPACES
] = UI_OPTION_SYMBOL_SPACE
,
276 [OPTION_SHOW_TABS
] = UI_OPTION_SYMBOL_TAB
|UI_OPTION_SYMBOL_TAB_FILL
,
277 [OPTION_SHOW_NEWLINES
] = UI_OPTION_SYMBOL_EOL
,
278 [OPTION_SHOW_EOF
] = UI_OPTION_SYMBOL_EOF
,
280 int flags
= view_options_get(win
->view
);
281 if (arg
.b
|| (toggle
&& !(flags
& values
[opt_index
])))
282 flags
|= values
[opt_index
];
284 flags
&= ~values
[opt_index
];
285 view_options_set(win
->view
, flags
);
288 case OPTION_NUMBER
: {
289 enum UiOption opt
= view_options_get(win
->view
);
290 if (arg
.b
|| (toggle
&& !(opt
& UI_OPTION_LINE_NUMBERS_ABSOLUTE
))) {
291 opt
&= ~UI_OPTION_LINE_NUMBERS_RELATIVE
;
292 opt
|= UI_OPTION_LINE_NUMBERS_ABSOLUTE
;
294 opt
&= ~UI_OPTION_LINE_NUMBERS_ABSOLUTE
;
296 view_options_set(win
->view
, opt
);
299 case OPTION_NUMBER_RELATIVE
: {
300 enum UiOption opt
= view_options_get(win
->view
);
301 if (arg
.b
|| (toggle
&& !(opt
& UI_OPTION_LINE_NUMBERS_RELATIVE
))) {
302 opt
&= ~UI_OPTION_LINE_NUMBERS_ABSOLUTE
;
303 opt
|= UI_OPTION_LINE_NUMBERS_RELATIVE
;
305 opt
&= ~UI_OPTION_LINE_NUMBERS_RELATIVE
;
307 view_options_set(win
->view
, opt
);
310 case OPTION_CURSOR_LINE
: {
311 enum UiOption opt
= view_options_get(win
->view
);
312 if (arg
.b
|| (toggle
&& !(opt
& UI_OPTION_CURSOR_LINE
)))
313 opt
|= UI_OPTION_CURSOR_LINE
;
315 opt
&= ~UI_OPTION_CURSOR_LINE
;
316 view_options_set(win
->view
, opt
);
319 case OPTION_COLOR_COLUMN
:
320 view_colorcolumn_set(win
->view
, arg
.i
);
322 case OPTION_SAVE_METHOD
:
323 if (strcmp("auto", arg
.s
) == 0) {
324 win
->file
->save_method
= TEXT_SAVE_AUTO
;
325 } else if (strcmp("atomic", arg
.s
) == 0) {
326 win
->file
->save_method
= TEXT_SAVE_ATOMIC
;
327 } else if (strcmp("inplace", arg
.s
) == 0) {
328 win
->file
->save_method
= TEXT_SAVE_INPLACE
;
330 vis_info_show(vis
, "Invalid save method `%s', expected "
331 "'auto', 'atomic' or 'inplace'", arg
.s
);
335 case OPTION_LOAD_METHOD
:
336 if (strcmp("auto", arg
.s
) == 0) {
337 vis
->load_method
= TEXT_LOAD_AUTO
;
338 } else if (strcmp("read", arg
.s
) == 0) {
339 vis
->load_method
= TEXT_LOAD_READ
;
340 } else if (strcmp("mmap", arg
.s
) == 0) {
341 vis
->load_method
= TEXT_LOAD_MMAP
;
343 vis_info_show(vis
, "Invalid load method `%s', expected "
344 "'auto', 'read' or 'mmap'", arg
.s
);
348 case OPTION_CHANGE_256COLORS
:
349 vis
->change_colors
= toggle
? !vis
->change_colors
: arg
.b
;
351 case OPTION_LAYOUT
: {
352 enum UiLayout layout
;
353 if (strcmp("h", arg
.s
) == 0) {
354 layout
= UI_LAYOUT_HORIZONTAL
;
355 } else if (strcmp("v", arg
.s
) == 0) {
356 layout
= UI_LAYOUT_VERTICAL
;
358 vis_info_show(vis
, "Invalid layout `%s', expected 'h' or 'v'", arg
.s
);
361 windows_arrange(vis
, layout
);
364 case OPTION_IGNORECASE
:
365 vis
->ignorecase
= toggle
? !vis
->ignorecase
: arg
.b
;
370 return opt
->func(vis
, win
, opt
->context
, toggle
, opt
->flags
, name
, &arg
);
376 static bool is_file_pattern(const char *pattern
) {
380 if (stat(pattern
, &meta
) == 0 && S_ISDIR(meta
.st_mode
))
382 for (char special
[] = "*?[{$~", *s
= special
; *s
; s
++) {
383 if (strchr(pattern
, *s
))
389 static const char *file_open_dialog(Vis
*vis
, const char *pattern
) {
390 static char name
[PATH_MAX
];
393 if (!is_file_pattern(pattern
))
396 Buffer bufcmd
, bufout
, buferr
;
397 buffer_init(&bufcmd
);
398 buffer_init(&bufout
);
399 buffer_init(&buferr
);
401 if (!buffer_put0(&bufcmd
, VIS_OPEN
" ") || !buffer_append0(&bufcmd
, pattern
? pattern
: ""))
404 Filerange empty
= text_range_new(0,0);
405 int status
= vis_pipe(vis
, vis
->win
->file
, &empty
,
406 (const char*[]){ buffer_content0(&bufcmd
), NULL
},
407 &bufout
, read_buffer
, &buferr
, read_buffer
);
410 strncpy(name
, buffer_content0(&bufout
), sizeof(name
)-1);
411 else if (status
!= 1)
412 vis_info_show(vis
, "Command failed %s", buffer_content0(&buferr
));
414 buffer_release(&bufcmd
);
415 buffer_release(&bufout
);
416 buffer_release(&buferr
);
418 for (char *end
= name
+strlen(name
)-1; end
>= name
&& isspace((unsigned char)*end
); end
--)
421 return name
[0] ? name
: NULL
;
424 static bool openfiles(Vis
*vis
, const char **files
) {
425 for (; *files
; files
++) {
426 const char *file
= file_open_dialog(vis
, *files
);
430 if (!vis_window_new(vis
, file
)) {
431 vis_info_show(vis
, "Could not open `%s' %s", file
,
432 errno
? strerror(errno
) : "");
439 static bool cmd_open(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
441 return vis_window_new(vis
, NULL
);
442 return openfiles(vis
, &argv
[1]);
445 static void info_unsaved_changes(Vis
*vis
) {
446 vis_info_show(vis
, "No write since last change (add ! to override)");
449 static bool cmd_edit(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
451 vis_info_show(vis
, "Only 1 filename allowed");
457 if (cmd
->flags
!= '!' && !vis_window_closable(oldwin
)) {
458 info_unsaved_changes(vis
);
462 if (oldwin
->file
->refcount
> 1) {
463 vis_info_show(vis
, "Can not reload file being opened multiple times");
466 return vis_window_reload(oldwin
);
468 if (!openfiles(vis
, &argv
[1]))
470 if (vis
->win
!= oldwin
) {
471 Win
*newwin
= vis
->win
;
472 vis_window_swap(oldwin
, newwin
);
473 vis_window_close(oldwin
);
474 vis_window_focus(newwin
);
476 return vis
->win
!= oldwin
;
479 static bool cmd_read(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
481 const size_t first_file
= 3;
482 const char *args
[MAX_ARGV
] = { argv
[0], "cat", "--" };
483 const char **name
= argv
[1] ? &argv
[1] : (const char*[]){ ".", NULL
};
484 for (size_t i
= first_file
; *name
&& i
< LENGTH(args
)-1; name
++, i
++) {
485 const char *file
= file_open_dialog(vis
, *name
);
486 if (!file
|| !(args
[i
] = strdup(file
)))
489 args
[LENGTH(args
)-1] = NULL
;
490 ret
= cmd_pipein(vis
, win
, cmd
, args
, sel
, range
);
492 for (size_t i
= first_file
; i
< LENGTH(args
); i
++)
493 free((char*)args
[i
]);
497 static bool has_windows(Vis
*vis
) {
498 for (Win
*win
= vis
->windows
; win
; win
= win
->next
) {
499 if (!win
->file
->internal
)
505 static bool cmd_quit(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
506 if (cmd
->flags
!= '!' && !vis_window_closable(win
)) {
507 info_unsaved_changes(vis
);
510 vis_window_close(win
);
511 if (!has_windows(vis
))
512 vis_exit(vis
, argv
[1] ? atoi(argv
[1]) : EXIT_SUCCESS
);
516 static bool cmd_qall(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
517 for (Win
*next
, *win
= vis
->windows
; win
; win
= next
) {
519 if (!win
->file
->internal
&& (!text_modified(win
->file
->text
) || cmd
->flags
== '!'))
520 vis_window_close(win
);
522 if (!has_windows(vis
)) {
523 vis_exit(vis
, argv
[1] ? atoi(argv
[1]) : EXIT_SUCCESS
);
526 info_unsaved_changes(vis
);
531 static bool cmd_split(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
534 enum UiOption options
= view_options_get(win
->view
);
535 windows_arrange(vis
, UI_LAYOUT_HORIZONTAL
);
537 return vis_window_split(win
);
538 bool ret
= openfiles(vis
, &argv
[1]);
540 view_options_set(vis
->win
->view
, options
);
544 static bool cmd_vsplit(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
547 enum UiOption options
= view_options_get(win
->view
);
548 windows_arrange(vis
, UI_LAYOUT_VERTICAL
);
550 return vis_window_split(win
);
551 bool ret
= openfiles(vis
, &argv
[1]);
553 view_options_set(vis
->win
->view
, options
);
557 static bool cmd_new(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
558 windows_arrange(vis
, UI_LAYOUT_HORIZONTAL
);
559 return vis_window_new(vis
, NULL
);
562 static bool cmd_vnew(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
563 windows_arrange(vis
, UI_LAYOUT_VERTICAL
);
564 return vis_window_new(vis
, NULL
);
567 static bool cmd_wq(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
570 File
*file
= win
->file
;
571 bool unmodified
= file
->fd
== -1 && !file
->name
&& !text_modified(file
->text
);
572 if (unmodified
|| cmd_write(vis
, win
, cmd
, argv
, sel
, range
))
573 return cmd_quit(vis
, win
, cmd
, (const char*[]){argv
[0], NULL
}, sel
, range
);
577 static bool cmd_earlier_later(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
580 Text
*txt
= win
->file
->text
;
586 count
= strtol(argv
[1], &unit
, 10);
587 if (errno
|| unit
== argv
[1] || count
< 0) {
588 vis_info_show(vis
, "Invalid number");
593 while (*unit
&& isspace((unsigned char)*unit
))
596 case 'd': count
*= 24; /* fall through */
597 case 'h': count
*= 60; /* fall through */
598 case 'm': count
*= 60; /* fall through */
601 vis_info_show(vis
, "Unknown time specifier (use: s,m,h or d)");
605 if (argv
[0][0] == 'e')
606 count
= -count
; /* earlier, move back in time */
608 pos
= text_restore(txt
, text_state(txt
) + count
);
613 VisCountIterator it
= vis_count_iterator_init(vis
, count
);
614 while (vis_count_iterator_next(&it
)) {
615 if (argv
[0][0] == 'e')
616 pos
= text_earlier(txt
);
618 pos
= text_later(txt
);
623 time_t state
= text_state(txt
);
625 strftime(buf
, sizeof buf
, "State from %H:%M", localtime_r(&state
, &tm
));
626 vis_info_show(vis
, "%s", buf
);
631 static bool print_keylayout(const char *key
, void *value
, void *data
) {
632 return text_appendf(data
, " %-18s\t%s\n", key
[0] == ' ' ? "␣" : key
, (char*)value
);
635 static bool print_keybinding(const char *key
, void *value
, void *data
) {
636 KeyBinding
*binding
= value
;
637 const char *desc
= binding
->alias
;
638 if (!desc
&& binding
->action
)
639 desc
= VIS_HELP_USE(binding
->action
->help
);
640 return text_appendf(data
, " %-18s\t%s\n", key
[0] == ' ' ? "␣" : key
, desc
? desc
: "");
643 static void print_mode(Mode
*mode
, Text
*txt
) {
644 if (!map_empty(mode
->bindings
))
645 text_appendf(txt
, "\n %s\n\n", mode
->name
);
646 map_iterate(mode
->bindings
, print_keybinding
, txt
);
649 static bool print_action(const char *key
, void *value
, void *data
) {
650 const char *help
= VIS_HELP_USE(((KeyAction
*)value
)->help
);
651 return text_appendf(data
, " %-30s\t%s\n", key
, help
? help
: "");
654 static bool print_cmd(const char *key
, void *value
, void *data
) {
655 CommandDef
*cmd
= value
;
656 const char *help
= VIS_HELP_USE(cmd
->help
);
658 snprintf(usage
, sizeof usage
, "%s%s%s%s%s%s%s",
660 (cmd
->flags
& CMD_FORCE
) ? "[!]" : "",
661 (cmd
->flags
& CMD_TEXT
) ? "/text/" : "",
662 (cmd
->flags
& CMD_REGEX
) ? "/regexp/" : "",
663 (cmd
->flags
& CMD_CMD
) ? " command" : "",
664 (cmd
->flags
& CMD_SHELL
) ? (!strcmp(cmd
->name
, "s") ? "/regexp/text/" : " shell-command") : "",
665 (cmd
->flags
& CMD_ARGV
) ? " [args...]" : "");
666 return text_appendf(data
, " %-30s %s\n", usage
, help
? help
: "");
669 static bool print_option(const char *key
, void *value
, void *txt
) {
671 const OptionDef
*opt
= value
;
672 const char *help
= VIS_HELP_USE(opt
->help
);
673 if (strcmp(key
, opt
->names
[0]))
675 snprintf(desc
, sizeof desc
, "%s%s%s%s%s",
677 opt
->names
[1] ? "|" : "",
678 opt
->names
[1] ? opt
->names
[1] : "",
679 opt
->flags
& VIS_OPTION_TYPE_BOOL
? " on|off" : "",
680 opt
->flags
& VIS_OPTION_TYPE_NUMBER
? " nn" : "");
681 return text_appendf(txt
, " %-30s %s\n", desc
, help
? help
: "");
684 static void print_symbolic_keys(Vis
*vis
, Text
*txt
) {
685 static const int keys
[] = {
686 TERMKEY_SYM_BACKSPACE
,
702 TERMKEY_SYM_PAGEDOWN
,
719 TERMKEY_SYM_REFERENCE
,
743 TERMKEY_SYM_KPPERIOD
,
744 TERMKEY_SYM_KPEQUALS
,
747 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
748 text_appendf(txt
, " ␣ (a literal \" \" space symbol must be used to refer to <Space>)\n");
749 for (size_t i
= 0; i
< LENGTH(keys
); i
++) {
750 text_appendf(txt
, " <%s>\n", termkey_get_keyname(termkey
, keys
[i
]));
754 static bool cmd_help(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
755 if (!vis_window_new(vis
, NULL
))
758 Text
*txt
= vis
->win
->file
->text
;
760 text_appendf(txt
, "vis %s (PID: %ld)\n\n", VERSION
, (long)getpid());
762 text_appendf(txt
, " Modes\n\n");
763 for (int i
= 0; i
< LENGTH(vis_modes
); i
++) {
764 Mode
*mode
= &vis_modes
[i
];
766 text_appendf(txt
, " %-18s\t%s\n", mode
->name
, mode
->help
);
769 if (!map_empty(vis
->keymap
)) {
770 text_appendf(txt
, "\n Layout specific mappings (affects all modes except INSERT/REPLACE)\n\n");
771 map_iterate(vis
->keymap
, print_keylayout
, txt
);
774 print_mode(&vis_modes
[VIS_MODE_NORMAL
], txt
);
775 print_mode(&vis_modes
[VIS_MODE_OPERATOR_PENDING
], txt
);
776 print_mode(&vis_modes
[VIS_MODE_VISUAL
], txt
);
777 print_mode(&vis_modes
[VIS_MODE_INSERT
], txt
);
779 text_appendf(txt
, "\n :-Commands\n\n");
780 map_iterate(vis
->cmds
, print_cmd
, txt
);
782 text_appendf(txt
, "\n Marks\n\n");
783 text_appendf(txt
, " a-z General purpose marks\n");
784 for (size_t i
= 0; i
< LENGTH(vis_marks
); i
++) {
785 const char *help
= VIS_HELP_USE(vis_marks
[i
].help
);
786 text_appendf(txt
, " %c %s\n", vis_marks
[i
].name
, help
? help
: "");
789 text_appendf(txt
, "\n Registers\n\n");
790 text_appendf(txt
, " a-z General purpose registers\n");
791 text_appendf(txt
, " A-Z Append to corresponding general purpose register\n");
792 for (size_t i
= 0; i
< LENGTH(vis_registers
); i
++) {
793 const char *help
= VIS_HELP_USE(vis_registers
[i
].help
);
794 text_appendf(txt
, " %c %s\n", vis_registers
[i
].name
, help
? help
: "");
797 text_appendf(txt
, "\n :set command options\n\n");
798 map_iterate(vis
->options
, print_option
, txt
);
800 text_appendf(txt
, "\n Key binding actions\n\n");
801 map_iterate(vis
->actions
, print_action
, txt
);
803 text_appendf(txt
, "\n Symbolic keys usable for key bindings "
804 "(prefix with C-, S-, and M- for Ctrl, Shift and Alt respectively)\n\n");
805 print_symbolic_keys(vis
, txt
);
807 char *paths
[] = { NULL
, NULL
};
808 char *paths_description
[] = {
809 "Lua paths used to load runtime files (? will be replaced by filename):",
810 "Lua paths used to load C libraries (? will be replaced by filename):",
813 if (vis_lua_paths_get(vis
, &paths
[0], &paths
[1])) {
814 for (size_t i
= 0; i
< LENGTH(paths
); i
++) {
815 text_appendf(txt
, "\n %s\n\n", paths_description
[i
]);
816 for (char *elem
= paths
[i
], *next
; elem
; elem
= next
) {
817 if ((next
= strstr(elem
, ";")))
820 text_appendf(txt
, " %s\n", elem
);
826 text_appendf(txt
, "\n Compile time configuration\n\n");
832 { "Curses support: ", CONFIG_CURSES
},
833 { "Lua support: ", CONFIG_LUA
},
834 { "Lua LPeg statically built-in: ", CONFIG_LPEG
},
835 { "TRE based regex support: ", CONFIG_TRE
},
836 { "POSIX ACL support: ", CONFIG_ACL
},
837 { "SELinux support: ", CONFIG_SELINUX
},
840 for (size_t i
= 0; i
< LENGTH(configs
); i
++)
841 text_appendf(txt
, " %-32s\t%s\n", configs
[i
].name
, configs
[i
].enabled
? "yes" : "no");
843 text_save(txt
, NULL
);
844 view_cursor_to(vis
->win
->view
, 0);
847 vis_motion(vis
, VIS_MOVE_SEARCH_FORWARD
, argv
[1]);
851 static bool cmd_langmap(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
852 const char *nonlatin
= argv
[1];
853 const char *latin
= argv
[2];
856 if (!latin
|| !nonlatin
) {
857 vis_info_show(vis
, "usage: langmap <non-latin keys> <latin keys>");
861 while (*latin
&& *nonlatin
) {
863 char latin_key
[8], nonlatin_key
[8];
865 if (i
< sizeof(latin_key
)-1)
866 latin_key
[i
++] = *latin
;
868 } while (!ISUTF8(*latin
));
870 if (j
< sizeof(nonlatin_key
)-1)
871 nonlatin_key
[j
++] = *nonlatin
;
873 } while (!ISUTF8(*nonlatin
));
875 nonlatin_key
[j
] = '\0';
876 mapped
&= vis_keymap_add(vis
, nonlatin_key
, strdup(latin_key
));
882 static bool cmd_map(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
884 bool local
= strstr(argv
[0], "-") != NULL
;
885 enum VisMode mode
= vis_mode_from(vis
, argv
[1]);
888 vis_info_show(vis
, "Invalid window for :%s", argv
[0]);
892 if (mode
== VIS_MODE_INVALID
|| !argv
[2] || !argv
[3]) {
893 vis_info_show(vis
, "usage: %s mode lhs rhs", argv
[0]);
897 const char *lhs
= argv
[2];
898 KeyBinding
*binding
= vis_binding_new(vis
);
899 if (!binding
|| !(binding
->alias
= strdup(argv
[3])))
903 mapped
= vis_window_mode_map(win
, mode
, cmd
->flags
== '!', lhs
, binding
);
905 mapped
= vis_mode_map(vis
, mode
, cmd
->flags
== '!', lhs
, binding
);
909 vis_info_show(vis
, "Failed to map `%s' in %s mode%s", lhs
, argv
[1],
910 cmd
->flags
!= '!' ? ", mapping already exists, "
911 "override with `!'" : "");
912 vis_binding_free(vis
, binding
);
917 static bool cmd_unmap(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
918 bool unmapped
= false;
919 bool local
= strstr(argv
[0], "-") != NULL
;
920 enum VisMode mode
= vis_mode_from(vis
, argv
[1]);
921 const char *lhs
= argv
[2];
924 vis_info_show(vis
, "Invalid window for :%s", argv
[0]);
928 if (mode
== VIS_MODE_INVALID
|| !lhs
) {
929 vis_info_show(vis
, "usage: %s mode lhs", argv
[0]);
934 unmapped
= vis_window_mode_unmap(win
, mode
, lhs
);
936 unmapped
= vis_mode_unmap(vis
, mode
, lhs
);
938 vis_info_show(vis
, "Failed to unmap `%s' in %s mode", lhs
, argv
[1]);