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_CHANGE_256COLORS
:
336 vis
->change_colors
= toggle
? !vis
->change_colors
: arg
.b
;
341 return opt
->func(vis
, win
, opt
->context
, toggle
, opt
->flags
, name
, &arg
);
347 static bool is_file_pattern(const char *pattern
) {
351 if (stat(pattern
, &meta
) == 0 && S_ISDIR(meta
.st_mode
))
353 for (char special
[] = "*?[{$~", *s
= special
; *s
; s
++) {
354 if (strchr(pattern
, *s
))
360 static const char *file_open_dialog(Vis
*vis
, const char *pattern
) {
361 static char name
[PATH_MAX
];
364 if (!is_file_pattern(pattern
))
367 Buffer bufcmd
, bufout
, buferr
;
368 buffer_init(&bufcmd
);
369 buffer_init(&bufout
);
370 buffer_init(&buferr
);
372 if (!buffer_put0(&bufcmd
, VIS_OPEN
" ") || !buffer_append0(&bufcmd
, pattern
? pattern
: ""))
375 Filerange empty
= text_range_new(0,0);
376 int status
= vis_pipe(vis
, vis
->win
->file
, &empty
,
377 (const char*[]){ buffer_content0(&bufcmd
), NULL
},
378 &bufout
, read_buffer
, &buferr
, read_buffer
);
381 strncpy(name
, buffer_content0(&bufout
), sizeof(name
)-1);
383 vis_info_show(vis
, "Command failed %s", buffer_content0(&buferr
));
385 buffer_release(&bufcmd
);
386 buffer_release(&bufout
);
387 buffer_release(&buferr
);
389 for (char *end
= name
+strlen(name
)-1; end
>= name
&& isspace((unsigned char)*end
); end
--)
392 return name
[0] ? name
: NULL
;
395 static bool openfiles(Vis
*vis
, const char **files
) {
396 for (; *files
; files
++) {
397 const char *file
= file_open_dialog(vis
, *files
);
401 if (!vis_window_new(vis
, file
)) {
402 vis_info_show(vis
, "Could not open `%s' %s", file
,
403 errno
? strerror(errno
) : "");
410 static bool cmd_open(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
412 return vis_window_new(vis
, NULL
);
413 return openfiles(vis
, &argv
[1]);
416 static void info_unsaved_changes(Vis
*vis
) {
417 vis_info_show(vis
, "No write since last change (add ! to override)");
420 static bool cmd_edit(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
422 vis_info_show(vis
, "Only 1 filename allowed");
428 if (cmd
->flags
!= '!' && !vis_window_closable(oldwin
)) {
429 info_unsaved_changes(vis
);
433 if (oldwin
->file
->refcount
> 1) {
434 vis_info_show(vis
, "Can not reload file being opened multiple times");
437 return vis_window_reload(oldwin
);
439 if (!openfiles(vis
, &argv
[1]))
441 if (vis
->win
!= oldwin
) {
442 Win
*newwin
= vis
->win
;
443 vis_window_swap(oldwin
, newwin
);
444 vis_window_close(oldwin
);
445 vis_window_focus(newwin
);
447 return vis
->win
!= oldwin
;
450 static bool cmd_read(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
452 const size_t first_file
= 3;
453 const char *args
[MAX_ARGV
] = { argv
[0], "cat", "--" };
454 const char **name
= argv
[1] ? &argv
[1] : (const char*[]){ ".", NULL
};
455 for (size_t i
= first_file
; *name
&& i
< LENGTH(args
)-1; name
++, i
++) {
456 const char *file
= file_open_dialog(vis
, *name
);
457 if (!file
|| !(args
[i
] = strdup(file
)))
460 args
[LENGTH(args
)-1] = NULL
;
461 ret
= cmd_pipein(vis
, win
, cmd
, args
, sel
, range
);
463 for (size_t i
= first_file
; i
< LENGTH(args
); i
++)
464 free((char*)args
[i
]);
468 static bool has_windows(Vis
*vis
) {
469 for (Win
*win
= vis
->windows
; win
; win
= win
->next
) {
470 if (!win
->file
->internal
)
476 static bool cmd_quit(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
477 if (cmd
->flags
!= '!' && !vis_window_closable(win
)) {
478 info_unsaved_changes(vis
);
481 vis_window_close(win
);
482 if (!has_windows(vis
))
483 vis_exit(vis
, EXIT_SUCCESS
);
487 static bool cmd_qall(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
488 for (Win
*next
, *win
= vis
->windows
; win
; win
= next
) {
490 if (!win
->file
->internal
&& (!text_modified(win
->file
->text
) || cmd
->flags
== '!'))
491 vis_window_close(win
);
493 if (!has_windows(vis
)) {
494 vis_exit(vis
, EXIT_SUCCESS
);
497 info_unsaved_changes(vis
);
502 static bool cmd_split(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
505 enum UiOption options
= view_options_get(win
->view
);
506 windows_arrange(vis
, UI_LAYOUT_HORIZONTAL
);
508 return vis_window_split(win
);
509 bool ret
= openfiles(vis
, &argv
[1]);
511 view_options_set(vis
->win
->view
, options
);
515 static bool cmd_vsplit(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
518 enum UiOption options
= view_options_get(win
->view
);
519 windows_arrange(vis
, UI_LAYOUT_VERTICAL
);
521 return vis_window_split(win
);
522 bool ret
= openfiles(vis
, &argv
[1]);
524 view_options_set(vis
->win
->view
, options
);
528 static bool cmd_new(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
529 windows_arrange(vis
, UI_LAYOUT_HORIZONTAL
);
530 return vis_window_new(vis
, NULL
);
533 static bool cmd_vnew(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
534 windows_arrange(vis
, UI_LAYOUT_VERTICAL
);
535 return vis_window_new(vis
, NULL
);
538 static bool cmd_wq(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
541 File
*file
= win
->file
;
542 bool unmodified
= file
->fd
== -1 && !file
->name
&& !text_modified(file
->text
);
543 if (unmodified
|| cmd_write(vis
, win
, cmd
, argv
, sel
, range
))
544 return cmd_quit(vis
, win
, cmd
, argv
, sel
, range
);
548 static bool cmd_earlier_later(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
551 Text
*txt
= win
->file
->text
;
557 count
= strtol(argv
[1], &unit
, 10);
558 if (errno
|| unit
== argv
[1] || count
< 0) {
559 vis_info_show(vis
, "Invalid number");
564 while (*unit
&& isspace((unsigned char)*unit
))
567 case 'd': count
*= 24; /* fall through */
568 case 'h': count
*= 60; /* fall through */
569 case 'm': count
*= 60; /* fall through */
572 vis_info_show(vis
, "Unknown time specifier (use: s,m,h or d)");
576 if (argv
[0][0] == 'e')
577 count
= -count
; /* earlier, move back in time */
579 pos
= text_restore(txt
, text_state(txt
) + count
);
584 VisCountIterator it
= vis_count_iterator_init(vis
, count
);
585 while (vis_count_iterator_next(&it
)) {
586 if (argv
[0][0] == 'e')
587 pos
= text_earlier(txt
);
589 pos
= text_later(txt
);
593 time_t state
= text_state(txt
);
595 strftime(buf
, sizeof buf
, "State from %H:%M", localtime(&state
));
596 vis_info_show(vis
, "%s", buf
);
601 static bool print_keylayout(const char *key
, void *value
, void *data
) {
602 return text_appendf(data
, " %-18s\t%s\n", key
[0] == ' ' ? "␣" : key
, (char*)value
);
605 static bool print_keybinding(const char *key
, void *value
, void *data
) {
606 KeyBinding
*binding
= value
;
607 const char *desc
= binding
->alias
;
608 if (!desc
&& binding
->action
)
609 desc
= VIS_HELP_USE(binding
->action
->help
);
610 return text_appendf(data
, " %-18s\t%s\n", key
[0] == ' ' ? "␣" : key
, desc
? desc
: "");
613 static void print_mode(Mode
*mode
, Text
*txt
) {
614 if (!map_empty(mode
->bindings
))
615 text_appendf(txt
, "\n %s\n\n", mode
->name
);
616 map_iterate(mode
->bindings
, print_keybinding
, txt
);
619 static bool print_action(const char *key
, void *value
, void *data
) {
620 const char *help
= VIS_HELP_USE(((KeyAction
*)value
)->help
);
621 return text_appendf(data
, " %-30s\t%s\n", key
, help
? help
: "");
624 static bool print_cmd(const char *key
, void *value
, void *data
) {
625 CommandDef
*cmd
= value
;
626 const char *help
= VIS_HELP_USE(cmd
->help
);
628 snprintf(usage
, sizeof usage
, "%s%s%s%s%s%s%s",
630 (cmd
->flags
& CMD_FORCE
) ? "[!]" : "",
631 (cmd
->flags
& CMD_TEXT
) ? "/text/" : "",
632 (cmd
->flags
& CMD_REGEX
) ? "/regexp/" : "",
633 (cmd
->flags
& CMD_CMD
) ? " command" : "",
634 (cmd
->flags
& CMD_SHELL
) ? (!strcmp(cmd
->name
, "s") ? "/regexp/text/" : " shell-command") : "",
635 (cmd
->flags
& CMD_ARGV
) ? " [args...]" : "");
636 return text_appendf(data
, " %-30s %s\n", usage
, help
? help
: "");
639 static bool print_option(const char *key
, void *value
, void *txt
) {
641 const OptionDef
*opt
= value
;
642 const char *help
= VIS_HELP_USE(opt
->help
);
643 if (strcmp(key
, opt
->names
[0]))
645 snprintf(desc
, sizeof desc
, "%s%s%s%s%s",
647 opt
->names
[1] ? "|" : "",
648 opt
->names
[1] ? opt
->names
[1] : "",
649 opt
->flags
& VIS_OPTION_TYPE_BOOL
? " on|off" : "",
650 opt
->flags
& VIS_OPTION_TYPE_NUMBER
? " nn" : "");
651 return text_appendf(txt
, " %-30s %s\n", desc
, help
? help
: "");
654 static void print_symbolic_keys(Vis
*vis
, Text
*txt
) {
655 static const int keys
[] = {
656 TERMKEY_SYM_BACKSPACE
,
672 TERMKEY_SYM_PAGEDOWN
,
689 TERMKEY_SYM_REFERENCE
,
713 TERMKEY_SYM_KPPERIOD
,
714 TERMKEY_SYM_KPEQUALS
,
717 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
718 text_appendf(txt
, " ␣ (a literal \" \" space symbol must be used to refer to <Space>)\n");
719 for (size_t i
= 0; i
< LENGTH(keys
); i
++) {
720 text_appendf(txt
, " <%s>\n", termkey_get_keyname(termkey
, keys
[i
]));
724 static bool cmd_help(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
725 if (!vis_window_new(vis
, NULL
))
728 Text
*txt
= vis
->win
->file
->text
;
730 text_appendf(txt
, "vis %s (PID: %ld)\n\n", VERSION
, (long)getpid());
732 text_appendf(txt
, " Modes\n\n");
733 for (int i
= 0; i
< LENGTH(vis_modes
); i
++) {
734 Mode
*mode
= &vis_modes
[i
];
736 text_appendf(txt
, " %-18s\t%s\n", mode
->name
, mode
->help
);
739 if (!map_empty(vis
->keymap
)) {
740 text_appendf(txt
, "\n Layout specific mappings (affects all modes except INSERT/REPLACE)\n\n");
741 map_iterate(vis
->keymap
, print_keylayout
, txt
);
744 print_mode(&vis_modes
[VIS_MODE_NORMAL
], txt
);
745 print_mode(&vis_modes
[VIS_MODE_OPERATOR_PENDING
], txt
);
746 print_mode(&vis_modes
[VIS_MODE_VISUAL
], txt
);
747 print_mode(&vis_modes
[VIS_MODE_INSERT
], txt
);
749 text_appendf(txt
, "\n :-Commands\n\n");
750 map_iterate(vis
->cmds
, print_cmd
, txt
);
752 text_appendf(txt
, "\n Marks\n\n");
753 text_appendf(txt
, " a-z General purpose marks\n");
754 for (size_t i
= 0; i
< LENGTH(vis_marks
); i
++) {
755 const char *help
= VIS_HELP_USE(vis_marks
[i
].help
);
756 text_appendf(txt
, " %c %s\n", vis_marks
[i
].name
, help
? help
: "");
759 text_appendf(txt
, "\n Registers\n\n");
760 text_appendf(txt
, " a-z General purpose registers\n");
761 text_appendf(txt
, " A-Z Append to corresponding general purpose register\n");
762 for (size_t i
= 0; i
< LENGTH(vis_registers
); i
++) {
763 const char *help
= VIS_HELP_USE(vis_registers
[i
].help
);
764 text_appendf(txt
, " %c %s\n", vis_registers
[i
].name
, help
? help
: "");
767 text_appendf(txt
, "\n :set command options\n\n");
768 map_iterate(vis
->options
, print_option
, txt
);
770 text_appendf(txt
, "\n Key binding actions\n\n");
771 map_iterate(vis
->actions
, print_action
, txt
);
773 text_appendf(txt
, "\n Symbolic keys usable for key bindings "
774 "(prefix with C-, S-, and M- for Ctrl, Shift and Alt respectively)\n\n");
775 print_symbolic_keys(vis
, txt
);
777 char *paths
[] = { NULL
, NULL
};
778 char *paths_description
[] = {
779 "Lua paths used to load runtime files (? will be replaced by filename):",
780 "Lua paths used to load C libraries (? will be replaced by filename):",
783 if (vis_lua_paths_get(vis
, &paths
[0], &paths
[1])) {
784 for (size_t i
= 0; i
< LENGTH(paths
); i
++) {
785 text_appendf(txt
, "\n %s\n\n", paths_description
[i
]);
786 for (char *elem
= paths
[i
], *next
; elem
; elem
= next
) {
787 if ((next
= strstr(elem
, ";")))
790 text_appendf(txt
, " %s\n", elem
);
796 text_appendf(txt
, "\n Compile time configuration\n\n");
802 { "Curses support: ", CONFIG_CURSES
},
803 { "Lua support: ", CONFIG_LUA
},
804 { "Lua LPeg statically built-in: ", CONFIG_LPEG
},
805 { "TRE based regex support: ", CONFIG_TRE
},
806 { "POSIX ACL support: ", CONFIG_ACL
},
807 { "SELinux support: ", CONFIG_SELINUX
},
810 for (size_t i
= 0; i
< LENGTH(configs
); i
++)
811 text_appendf(txt
, " %-32s\t%s\n", configs
[i
].name
, configs
[i
].enabled
? "yes" : "no");
813 text_save(txt
, NULL
);
814 view_cursor_to(vis
->win
->view
, 0);
817 vis_motion(vis
, VIS_MOVE_SEARCH_FORWARD
, argv
[1]);
821 static bool cmd_langmap(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
822 const char *nonlatin
= argv
[1];
823 const char *latin
= argv
[2];
826 if (!latin
|| !nonlatin
) {
827 vis_info_show(vis
, "usage: langmap <non-latin keys> <latin keys>");
831 while (*latin
&& *nonlatin
) {
833 char latin_key
[8], nonlatin_key
[8];
835 if (i
< sizeof(latin_key
)-1)
836 latin_key
[i
++] = *latin
;
838 } while (!ISUTF8(*latin
));
840 if (j
< sizeof(nonlatin_key
)-1)
841 nonlatin_key
[j
++] = *nonlatin
;
843 } while (!ISUTF8(*nonlatin
));
845 nonlatin_key
[j
] = '\0';
846 mapped
&= vis_keymap_add(vis
, nonlatin_key
, strdup(latin_key
));
852 static bool cmd_map(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
854 bool local
= strstr(argv
[0], "-") != NULL
;
855 enum VisMode mode
= vis_mode_from(vis
, argv
[1]);
858 vis_info_show(vis
, "Invalid window for :%s", argv
[0]);
862 if (mode
== VIS_MODE_INVALID
|| !argv
[2] || !argv
[3]) {
863 vis_info_show(vis
, "usage: %s mode lhs rhs", argv
[0]);
867 const char *lhs
= argv
[2];
868 KeyBinding
*binding
= vis_binding_new(vis
);
869 if (!binding
|| !(binding
->alias
= strdup(argv
[3])))
873 mapped
= vis_window_mode_map(win
, mode
, cmd
->flags
== '!', lhs
, binding
);
875 mapped
= vis_mode_map(vis
, mode
, cmd
->flags
== '!', lhs
, binding
);
879 vis_info_show(vis
, "Failed to map `%s' in %s mode%s", lhs
, argv
[1],
880 cmd
->flags
!= '!' ? ", mapping already exists, "
881 "override with `!'" : "");
882 vis_binding_free(vis
, binding
);
887 static bool cmd_unmap(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Selection
*sel
, Filerange
*range
) {
888 bool unmapped
= false;
889 bool local
= strstr(argv
[0], "-") != NULL
;
890 enum VisMode mode
= vis_mode_from(vis
, argv
[1]);
891 const char *lhs
= argv
[2];
894 vis_info_show(vis
, "Invalid window for :%s", argv
[0]);
898 if (mode
== VIS_MODE_INVALID
|| !lhs
) {
899 vis_info_show(vis
, "usage: %s mode lhs", argv
[0]);
904 unmapped
= vis_window_mode_unmap(win
, mode
, lhs
);
906 unmapped
= vis_mode_unmap(vis
, mode
, lhs
);
908 vis_info_show(vis
, "Failed to unmap `%s' in %s mode", lhs
, argv
[1]);