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
[], Cursor
*cur
, Filerange
*range
) {
124 CmdUser
*user
= map_get(vis
->usercmds
, argv
[0]);
125 return user
&& user
->func(vis
, win
, user
->data
, cmd
->flags
== '!', argv
, cur
, 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
[], Cursor
*cur
, 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
:
273 const int values
[] = {
274 [OPTION_SHOW_SPACES
] = UI_OPTION_SYMBOL_SPACE
,
275 [OPTION_SHOW_TABS
] = UI_OPTION_SYMBOL_TAB
|UI_OPTION_SYMBOL_TAB_FILL
,
276 [OPTION_SHOW_NEWLINES
] = UI_OPTION_SYMBOL_EOL
,
278 int flags
= view_options_get(win
->view
);
279 if (arg
.b
|| (toggle
&& !(flags
& values
[opt_index
])))
280 flags
|= values
[opt_index
];
282 flags
&= ~values
[opt_index
];
283 view_options_set(win
->view
, flags
);
286 case OPTION_NUMBER
: {
287 enum UiOption opt
= view_options_get(win
->view
);
288 if (arg
.b
|| (toggle
&& !(opt
& UI_OPTION_LINE_NUMBERS_ABSOLUTE
))) {
289 opt
&= ~UI_OPTION_LINE_NUMBERS_RELATIVE
;
290 opt
|= UI_OPTION_LINE_NUMBERS_ABSOLUTE
;
292 opt
&= ~UI_OPTION_LINE_NUMBERS_ABSOLUTE
;
294 view_options_set(win
->view
, opt
);
297 case OPTION_NUMBER_RELATIVE
: {
298 enum UiOption opt
= view_options_get(win
->view
);
299 if (arg
.b
|| (toggle
&& !(opt
& UI_OPTION_LINE_NUMBERS_RELATIVE
))) {
300 opt
&= ~UI_OPTION_LINE_NUMBERS_ABSOLUTE
;
301 opt
|= UI_OPTION_LINE_NUMBERS_RELATIVE
;
303 opt
&= ~UI_OPTION_LINE_NUMBERS_RELATIVE
;
305 view_options_set(win
->view
, opt
);
308 case OPTION_CURSOR_LINE
: {
309 enum UiOption opt
= view_options_get(win
->view
);
310 if (arg
.b
|| (toggle
&& !(opt
& UI_OPTION_CURSOR_LINE
)))
311 opt
|= UI_OPTION_CURSOR_LINE
;
313 opt
&= ~UI_OPTION_CURSOR_LINE
;
314 view_options_set(win
->view
, opt
);
317 case OPTION_COLOR_COLUMN
:
318 view_colorcolumn_set(win
->view
, arg
.i
);
320 case OPTION_SAVE_METHOD
:
321 if (strcmp("auto", arg
.s
) == 0) {
322 win
->file
->save_method
= TEXT_SAVE_AUTO
;
323 } else if (strcmp("atomic", arg
.s
) == 0) {
324 win
->file
->save_method
= TEXT_SAVE_ATOMIC
;
325 } else if (strcmp("inplace", arg
.s
) == 0) {
326 win
->file
->save_method
= TEXT_SAVE_INPLACE
;
328 vis_info_show(vis
, "Invalid save method `%s', expected "
329 "'auto', 'atomic' or 'inplace'", arg
.s
);
333 case OPTION_CHANGE_256COLORS
:
334 vis
->change_colors
= toggle
? !vis
->change_colors
: arg
.b
;
339 return opt
->func(vis
, win
, opt
->context
, toggle
, opt
->flags
, name
, &arg
);
345 static bool is_file_pattern(const char *pattern
) {
349 if (stat(pattern
, &meta
) == 0 && S_ISDIR(meta
.st_mode
))
351 for (char special
[] = "*?[{$~", *s
= special
; *s
; s
++) {
352 if (strchr(pattern
, *s
))
358 static const char *file_open_dialog(Vis
*vis
, const char *pattern
) {
359 static char name
[PATH_MAX
];
362 if (!is_file_pattern(pattern
))
365 Buffer bufcmd
, bufout
, buferr
;
366 buffer_init(&bufcmd
);
367 buffer_init(&bufout
);
368 buffer_init(&buferr
);
370 if (!buffer_put0(&bufcmd
, VIS_OPEN
" ") || !buffer_append0(&bufcmd
, pattern
? pattern
: ""))
373 Filerange empty
= text_range_new(0,0);
374 int status
= vis_pipe(vis
, vis
->win
->file
, &empty
,
375 (const char*[]){ buffer_content0(&bufcmd
), NULL
},
376 &bufout
, read_buffer
, &buferr
, read_buffer
);
379 strncpy(name
, buffer_content0(&bufout
), sizeof(name
)-1);
381 vis_info_show(vis
, "Command failed %s", buffer_content0(&buferr
));
383 buffer_release(&bufcmd
);
384 buffer_release(&bufout
);
385 buffer_release(&buferr
);
387 for (char *end
= name
+strlen(name
)-1; end
>= name
&& isspace((unsigned char)*end
); end
--)
390 return name
[0] ? name
: NULL
;
393 static bool openfiles(Vis
*vis
, const char **files
) {
394 for (; *files
; files
++) {
395 const char *file
= file_open_dialog(vis
, *files
);
399 if (!vis_window_new(vis
, file
)) {
400 vis_info_show(vis
, "Could not open `%s' %s", file
,
401 errno
? strerror(errno
) : "");
408 static bool cmd_open(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
410 return vis_window_new(vis
, NULL
);
411 return openfiles(vis
, &argv
[1]);
414 static void info_unsaved_changes(Vis
*vis
) {
415 vis_info_show(vis
, "No write since last change (add ! to override)");
418 static bool cmd_edit(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
420 vis_info_show(vis
, "Only 1 filename allowed");
426 if (cmd
->flags
!= '!' && !vis_window_closable(oldwin
)) {
427 info_unsaved_changes(vis
);
431 if (oldwin
->file
->refcount
> 1) {
432 vis_info_show(vis
, "Can not reload file being opened multiple times");
435 return vis_window_reload(oldwin
);
437 if (!openfiles(vis
, &argv
[1]))
439 if (vis
->win
!= oldwin
) {
440 Win
*newwin
= vis
->win
;
441 vis_window_swap(oldwin
, newwin
);
442 vis_window_close(oldwin
);
443 vis_window_focus(newwin
);
445 return vis
->win
!= oldwin
;
448 static bool cmd_read(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
450 const size_t first_file
= 3;
451 const char *args
[MAX_ARGV
] = { argv
[0], "cat", "--" };
452 const char **name
= argv
[1] ? &argv
[1] : (const char*[]){ ".", NULL
};
453 for (size_t i
= first_file
; *name
&& i
< LENGTH(args
)-1; name
++, i
++) {
454 const char *file
= file_open_dialog(vis
, *name
);
455 if (!file
|| !(args
[i
] = strdup(file
)))
458 args
[LENGTH(args
)-1] = NULL
;
459 ret
= cmd_pipein(vis
, win
, cmd
, args
, cur
, range
);
461 for (size_t i
= first_file
; i
< LENGTH(args
); i
++)
462 free((char*)args
[i
]);
466 static bool has_windows(Vis
*vis
) {
467 for (Win
*win
= vis
->windows
; win
; win
= win
->next
) {
468 if (!win
->file
->internal
)
474 static bool cmd_quit(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
475 if (cmd
->flags
!= '!' && !vis_window_closable(win
)) {
476 info_unsaved_changes(vis
);
479 vis_window_close(win
);
480 if (!has_windows(vis
))
481 vis_exit(vis
, EXIT_SUCCESS
);
485 static bool cmd_qall(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
486 for (Win
*next
, *win
= vis
->windows
; win
; win
= next
) {
488 if (!win
->file
->internal
&& (!text_modified(win
->file
->text
) || cmd
->flags
== '!'))
489 vis_window_close(win
);
491 if (!has_windows(vis
)) {
492 vis_exit(vis
, EXIT_SUCCESS
);
495 info_unsaved_changes(vis
);
500 static bool cmd_split(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
503 enum UiOption options
= view_options_get(win
->view
);
504 windows_arrange(vis
, UI_LAYOUT_HORIZONTAL
);
506 return vis_window_split(win
);
507 bool ret
= openfiles(vis
, &argv
[1]);
509 view_options_set(vis
->win
->view
, options
);
513 static bool cmd_vsplit(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
516 enum UiOption options
= view_options_get(win
->view
);
517 windows_arrange(vis
, UI_LAYOUT_VERTICAL
);
519 return vis_window_split(win
);
520 bool ret
= openfiles(vis
, &argv
[1]);
522 view_options_set(vis
->win
->view
, options
);
526 static bool cmd_new(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
527 windows_arrange(vis
, UI_LAYOUT_HORIZONTAL
);
528 return vis_window_new(vis
, NULL
);
531 static bool cmd_vnew(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
532 windows_arrange(vis
, UI_LAYOUT_VERTICAL
);
533 return vis_window_new(vis
, NULL
);
536 static bool cmd_wq(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
539 File
*file
= win
->file
;
540 bool unmodified
= file
->fd
== -1 && !file
->name
&& !text_modified(file
->text
);
541 if (unmodified
|| cmd_write(vis
, win
, cmd
, argv
, cur
, range
))
542 return cmd_quit(vis
, win
, cmd
, argv
, cur
, range
);
546 static bool cmd_earlier_later(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
549 Text
*txt
= win
->file
->text
;
555 count
= strtol(argv
[1], &unit
, 10);
556 if (errno
|| unit
== argv
[1] || count
< 0) {
557 vis_info_show(vis
, "Invalid number");
562 while (*unit
&& isspace((unsigned char)*unit
))
565 case 'd': count
*= 24; /* fall through */
566 case 'h': count
*= 60; /* fall through */
567 case 'm': count
*= 60; /* fall through */
570 vis_info_show(vis
, "Unknown time specifier (use: s,m,h or d)");
574 if (argv
[0][0] == 'e')
575 count
= -count
; /* earlier, move back in time */
577 pos
= text_restore(txt
, text_state(txt
) + count
);
582 VisCountIterator it
= vis_count_iterator_init(vis
, count
);
583 while (vis_count_iterator_next(&it
)) {
584 if (argv
[0][0] == 'e')
585 pos
= text_earlier(txt
);
587 pos
= text_later(txt
);
591 time_t state
= text_state(txt
);
593 strftime(buf
, sizeof buf
, "State from %H:%M", localtime(&state
));
594 vis_info_show(vis
, "%s", buf
);
599 static bool print_keylayout(const char *key
, void *value
, void *data
) {
600 return text_appendf(data
, " %-18s\t%s\n", key
[0] == ' ' ? "␣" : key
, (char*)value
);
603 static bool print_keybinding(const char *key
, void *value
, void *data
) {
604 KeyBinding
*binding
= value
;
605 const char *desc
= binding
->alias
;
606 if (!desc
&& binding
->action
)
607 desc
= VIS_HELP_USE(binding
->action
->help
);
608 return text_appendf(data
, " %-18s\t%s\n", key
[0] == ' ' ? "␣" : key
, desc
? desc
: "");
611 static void print_mode(Mode
*mode
, Text
*txt
) {
612 if (!map_empty(mode
->bindings
))
613 text_appendf(txt
, "\n %s\n\n", mode
->name
);
614 map_iterate(mode
->bindings
, print_keybinding
, txt
);
617 static bool print_action(const char *key
, void *value
, void *data
) {
618 const char *help
= VIS_HELP_USE(((KeyAction
*)value
)->help
);
619 return text_appendf(data
, " %-30s\t%s\n", key
, help
? help
: "");
622 static bool print_cmd(const char *key
, void *value
, void *data
) {
623 CommandDef
*cmd
= value
;
624 const char *help
= VIS_HELP_USE(cmd
->help
);
626 snprintf(usage
, sizeof usage
, "%s%s%s%s%s%s%s",
628 (cmd
->flags
& CMD_FORCE
) ? "[!]" : "",
629 (cmd
->flags
& CMD_TEXT
) ? "/text/" : "",
630 (cmd
->flags
& CMD_REGEX
) ? "/regexp/" : "",
631 (cmd
->flags
& CMD_CMD
) ? " command" : "",
632 (cmd
->flags
& CMD_SHELL
) ? (!strcmp(cmd
->name
, "s") ? "/regexp/text/" : " shell-command") : "",
633 (cmd
->flags
& CMD_ARGV
) ? " [args...]" : "");
634 return text_appendf(data
, " %-30s %s\n", usage
, help
? help
: "");
637 static bool print_option(const char *key
, void *value
, void *txt
) {
639 const OptionDef
*opt
= value
;
640 const char *help
= VIS_HELP_USE(opt
->help
);
641 if (strcmp(key
, opt
->names
[0]))
643 snprintf(desc
, sizeof desc
, "%s%s%s%s%s",
645 opt
->names
[1] ? "|" : "",
646 opt
->names
[1] ? opt
->names
[1] : "",
647 opt
->flags
& VIS_OPTION_TYPE_BOOL
? " on|off" : "",
648 opt
->flags
& VIS_OPTION_TYPE_NUMBER
? " nn" : "");
649 return text_appendf(txt
, " %-30s %s\n", desc
, help
? help
: "");
652 static void print_symbolic_keys(Vis
*vis
, Text
*txt
) {
653 static const int keys
[] = {
654 TERMKEY_SYM_BACKSPACE
,
670 TERMKEY_SYM_PAGEDOWN
,
687 TERMKEY_SYM_REFERENCE
,
711 TERMKEY_SYM_KPPERIOD
,
712 TERMKEY_SYM_KPEQUALS
,
715 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
716 text_appendf(txt
, " ␣ (a literal \" \" space symbol must be used to refer to <Space>)\n");
717 for (size_t i
= 0; i
< LENGTH(keys
); i
++) {
718 text_appendf(txt
, " <%s>\n", termkey_get_keyname(termkey
, keys
[i
]));
722 static bool cmd_help(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
723 if (!vis_window_new(vis
, NULL
))
726 Text
*txt
= vis
->win
->file
->text
;
728 text_appendf(txt
, "vis %s (PID: %ld)\n\n", VERSION
, (long)getpid());
730 text_appendf(txt
, " Modes\n\n");
731 for (int i
= 0; i
< LENGTH(vis_modes
); i
++) {
732 Mode
*mode
= &vis_modes
[i
];
734 text_appendf(txt
, " %-18s\t%s\n", mode
->name
, mode
->help
);
737 if (!map_empty(vis
->keymap
)) {
738 text_appendf(txt
, "\n Layout specific mappings (affects all modes except INSERT/REPLACE)\n\n");
739 map_iterate(vis
->keymap
, print_keylayout
, txt
);
742 print_mode(&vis_modes
[VIS_MODE_NORMAL
], txt
);
743 print_mode(&vis_modes
[VIS_MODE_OPERATOR_PENDING
], txt
);
744 print_mode(&vis_modes
[VIS_MODE_VISUAL
], txt
);
745 print_mode(&vis_modes
[VIS_MODE_INSERT
], txt
);
747 text_appendf(txt
, "\n :-Commands\n\n");
748 map_iterate(vis
->cmds
, print_cmd
, txt
);
750 text_appendf(txt
, "\n Marks\n\n");
751 text_appendf(txt
, " a-z General purpose marks\n");
752 for (size_t i
= 0; i
< LENGTH(vis_marks
); i
++) {
753 const char *help
= VIS_HELP_USE(vis_marks
[i
].help
);
754 text_appendf(txt
, " %c %s\n", vis_marks
[i
].name
, help
? help
: "");
757 text_appendf(txt
, "\n Registers\n\n");
758 text_appendf(txt
, " a-z General purpose registers\n");
759 text_appendf(txt
, " A-Z Append to corresponding general purpose register\n");
760 for (size_t i
= 0; i
< LENGTH(vis_registers
); i
++) {
761 const char *help
= VIS_HELP_USE(vis_registers
[i
].help
);
762 text_appendf(txt
, " %c %s\n", vis_registers
[i
].name
, help
? help
: "");
765 text_appendf(txt
, "\n :set command options\n\n");
766 map_iterate(vis
->options
, print_option
, txt
);
768 text_appendf(txt
, "\n Key binding actions\n\n");
769 map_iterate(vis
->actions
, print_action
, txt
);
771 text_appendf(txt
, "\n Symbolic keys usable for key bindings "
772 "(prefix with C-, S-, and M- for Ctrl, Shift and Alt respectively)\n\n");
773 print_symbolic_keys(vis
, txt
);
775 char *paths
[] = { NULL
, NULL
};
776 char *paths_description
[] = {
777 "Lua paths used to load runtime files (? will be replaced by filename):",
778 "Lua paths used to load C libraries (? will be replaced by filename):",
781 if (vis_lua_paths_get(vis
, &paths
[0], &paths
[1])) {
782 for (size_t i
= 0; i
< LENGTH(paths
); i
++) {
783 text_appendf(txt
, "\n %s\n\n", paths_description
[i
]);
784 for (char *elem
= paths
[i
], *next
; elem
; elem
= next
) {
785 if ((next
= strstr(elem
, ";")))
788 text_appendf(txt
, " %s\n", elem
);
794 text_appendf(txt
, "\n Compile time configuration\n\n");
800 { "Curses support: ", CONFIG_CURSES
},
801 { "Lua support: ", CONFIG_LUA
},
802 { "Lua LPeg statically built-in: ", CONFIG_LPEG
},
803 { "TRE based regex support: ", CONFIG_TRE
},
804 { "POSIX ACL support: ", CONFIG_ACL
},
805 { "SELinux support: ", CONFIG_SELINUX
},
808 for (size_t i
= 0; i
< LENGTH(configs
); i
++)
809 text_appendf(txt
, " %-32s\t%s\n", configs
[i
].name
, configs
[i
].enabled
? "yes" : "no");
811 text_save(txt
, NULL
);
812 view_cursor_to(vis
->win
->view
, 0);
815 vis_motion(vis
, VIS_MOVE_SEARCH_FORWARD
, argv
[1]);
819 static bool cmd_langmap(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
820 const char *nonlatin
= argv
[1];
821 const char *latin
= argv
[2];
824 if (!latin
|| !nonlatin
) {
825 vis_info_show(vis
, "usage: langmap <non-latin keys> <latin keys>");
829 while (*latin
&& *nonlatin
) {
831 char latin_key
[8], nonlatin_key
[8];
833 if (i
< sizeof(latin_key
)-1)
834 latin_key
[i
++] = *latin
;
836 } while (!ISUTF8(*latin
));
838 if (j
< sizeof(nonlatin_key
)-1)
839 nonlatin_key
[j
++] = *nonlatin
;
841 } while (!ISUTF8(*nonlatin
));
843 nonlatin_key
[j
] = '\0';
844 mapped
&= vis_keymap_add(vis
, nonlatin_key
, strdup(latin_key
));
850 static bool cmd_map(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
852 bool local
= strstr(argv
[0], "-") != NULL
;
853 enum VisMode mode
= vis_mode_from(vis
, argv
[1]);
856 vis_info_show(vis
, "Invalid window for :%s", argv
[0]);
860 if (mode
== VIS_MODE_INVALID
|| !argv
[2] || !argv
[3]) {
861 vis_info_show(vis
, "usage: %s mode lhs rhs", argv
[0]);
865 const char *lhs
= argv
[2];
866 KeyBinding
*binding
= vis_binding_new(vis
);
867 if (!binding
|| !(binding
->alias
= strdup(argv
[3])))
871 mapped
= vis_window_mode_map(win
, mode
, cmd
->flags
== '!', lhs
, binding
);
873 mapped
= vis_mode_map(vis
, mode
, cmd
->flags
== '!', lhs
, binding
);
877 vis_info_show(vis
, "Failed to map `%s' in %s mode%s", lhs
, argv
[1],
878 cmd
->flags
!= '!' ? ", mapping already exists, "
879 "override with `!'" : "");
880 vis_binding_free(vis
, binding
);
885 static bool cmd_unmap(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
886 bool unmapped
= false;
887 bool local
= strstr(argv
[0], "-") != NULL
;
888 enum VisMode mode
= vis_mode_from(vis
, argv
[1]);
889 const char *lhs
= argv
[2];
892 vis_info_show(vis
, "Invalid window for :%s", argv
[0]);
896 if (mode
== VIS_MODE_INVALID
|| !lhs
) {
897 vis_info_show(vis
, "usage: %s mode lhs", argv
[0]);
902 unmapped
= vis_window_mode_unmap(win
, mode
, lhs
);
904 unmapped
= vis_mode_unmap(vis
, mode
, lhs
);
906 vis_info_show(vis
, "Failed to unmap `%s' in %s mode", lhs
, argv
[1]);