1 /* this file is included from sam.c */
6 // FIXME: avoid this redirection?
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
, CmdFunc
*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 bool cmd_user(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
66 CmdUser
*user
= map_get(vis
->usercmds
, argv
[0]);
67 return user
&& user
->func(vis
, win
, user
->data
, cmd
->flags
== '!', argv
, cur
, range
);
70 static void windows_arrange(Vis
*vis
, enum UiLayout layout
) {
71 vis
->ui
->arrange(vis
->ui
, layout
);
74 static void tabwidth_set(Vis
*vis
, int tabwidth
) {
75 if (tabwidth
< 1 || tabwidth
> 8)
77 for (Win
*win
= vis
->windows
; win
; win
= win
->next
)
78 view_tabwidth_set(win
->view
, tabwidth
);
79 vis
->tabwidth
= tabwidth
;
82 /* parse human-readable boolean value in s. If successful, store the result in
83 * outval and return true. Else return false and leave outval alone. */
84 static bool parse_bool(const char *s
, bool *outval
) {
85 for (const char **t
= (const char*[]){"1", "true", "yes", "on", NULL
}; *t
; t
++) {
86 if (!strcasecmp(s
, *t
)) {
91 for (const char **f
= (const char*[]){"0", "false", "no", "off", NULL
}; *f
; f
++) {
92 if (!strcasecmp(s
, *f
)) {
100 static bool cmd_set(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
102 if (!argv
[1] || !argv
[1][0] || argv
[3]) {
103 vis_info_show(vis
, "Expecting: set option [value]");
108 strncpy(name
, argv
[1], sizeof(name
)-1);
109 char *lastchar
= &name
[strlen(name
)-1];
110 bool toggle
= (*lastchar
== '!');
114 OptionDef
*opt
= map_closest(vis
->options
, name
);
116 vis_info_show(vis
, "Unknown option: `%s'", name
);
120 if (!win
&& (opt
->flags
& OPTION_FLAG_WINDOW
)) {
121 vis_info_show(vis
, "Need active window for `:set %s'", name
);
126 if (opt
->type
!= OPTION_TYPE_BOOL
) {
127 vis_info_show(vis
, "Only boolean options can be toggled");
131 vis_info_show(vis
, "Can not specify option value when toggling");
138 case OPTION_TYPE_STRING
:
139 if (!(opt
->flags
& OPTION_FLAG_OPTIONAL
) && !argv
[2]) {
140 vis_info_show(vis
, "Expecting string option value");
145 case OPTION_TYPE_BOOL
:
148 } else if (!parse_bool(argv
[2], &arg
.b
)) {
149 vis_info_show(vis
, "Expecting boolean option value not: `%s'", argv
[2]);
153 case OPTION_TYPE_NUMBER
:
155 vis_info_show(vis
, "Expecting number");
160 long lval
= strtol(argv
[2], &ep
, 10);
161 if (argv
[2][0] == '\0' || *ep
!= '\0') {
162 vis_info_show(vis
, "Invalid number");
166 if ((errno
== ERANGE
&& (lval
== LONG_MAX
|| lval
== LONG_MIN
)) ||
167 (lval
> INT_MAX
|| lval
< INT_MIN
)) {
168 vis_info_show(vis
, "Number overflow");
173 vis_info_show(vis
, "Expecting positive number");
182 size_t opt_index
= opt
- options
;
186 char *shell
= strdup(arg
.s
);
188 vis_info_show(vis
, "Failed to change shell");
195 case OPTION_ESCDELAY
:
197 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
198 termkey_set_waittime(termkey
, arg
.i
);
201 case OPTION_EXPANDTAB
:
202 vis
->expandtab
= toggle
? !vis
->expandtab
: arg
.b
;
204 case OPTION_AUTOINDENT
:
205 vis
->autoindent
= toggle
? !vis
->autoindent
: arg
.b
;
207 case OPTION_TABWIDTH
:
208 tabwidth_set(vis
, arg
.i
);
212 const char *syntax
= vis_window_syntax_get(win
);
214 vis_info_show(vis
, "Syntax definition in use: `%s'", syntax
);
216 vis_info_show(vis
, "No syntax definition in use");
220 if (parse_bool(argv
[2], &arg
.b
) && !arg
.b
)
221 return vis_window_syntax_set(win
, NULL
);
222 if (!vis_window_syntax_set(win
, argv
[2])) {
223 vis_info_show(vis
, "Unknown syntax definition: `%s'", argv
[2]);
227 case OPTION_SHOW_SPACES
:
228 case OPTION_SHOW_TABS
:
229 case OPTION_SHOW_NEWLINES
:
231 const int values
[] = {
232 [OPTION_SHOW_SPACES
] = UI_OPTION_SYMBOL_SPACE
,
233 [OPTION_SHOW_TABS
] = UI_OPTION_SYMBOL_TAB
|UI_OPTION_SYMBOL_TAB_FILL
,
234 [OPTION_SHOW_NEWLINES
] = UI_OPTION_SYMBOL_EOL
,
236 int flags
= view_options_get(win
->view
);
237 if (arg
.b
|| (toggle
&& !(flags
& values
[opt_index
])))
238 flags
|= values
[opt_index
];
240 flags
&= ~values
[opt_index
];
241 view_options_set(win
->view
, flags
);
244 case OPTION_NUMBER
: {
245 enum UiOption opt
= view_options_get(win
->view
);
246 if (arg
.b
|| (toggle
&& !(opt
& UI_OPTION_LINE_NUMBERS_ABSOLUTE
))) {
247 opt
&= ~UI_OPTION_LINE_NUMBERS_RELATIVE
;
248 opt
|= UI_OPTION_LINE_NUMBERS_ABSOLUTE
;
250 opt
&= ~UI_OPTION_LINE_NUMBERS_ABSOLUTE
;
252 view_options_set(win
->view
, opt
);
255 case OPTION_NUMBER_RELATIVE
: {
256 enum UiOption opt
= view_options_get(win
->view
);
257 if (arg
.b
|| (toggle
&& !(opt
& UI_OPTION_LINE_NUMBERS_RELATIVE
))) {
258 opt
&= ~UI_OPTION_LINE_NUMBERS_ABSOLUTE
;
259 opt
|= UI_OPTION_LINE_NUMBERS_RELATIVE
;
261 opt
&= ~UI_OPTION_LINE_NUMBERS_RELATIVE
;
263 view_options_set(win
->view
, opt
);
266 case OPTION_CURSOR_LINE
: {
267 enum UiOption opt
= view_options_get(win
->view
);
268 if (arg
.b
|| (toggle
&& !(opt
& UI_OPTION_CURSOR_LINE
)))
269 opt
|= UI_OPTION_CURSOR_LINE
;
271 opt
&= ~UI_OPTION_CURSOR_LINE
;
272 view_options_set(win
->view
, opt
);
276 if (!vis_theme_load(vis
, arg
.s
)) {
277 vis_info_show(vis
, "Failed to load theme: `%s'", arg
.s
);
281 case OPTION_COLOR_COLUMN
:
282 view_colorcolumn_set(win
->view
, arg
.i
);
285 win
->horizon
= arg
.i
;
287 case OPTION_SAVE_METHOD
:
288 if (strcmp("auto", arg
.s
) == 0) {
289 win
->file
->save_method
= TEXT_SAVE_AUTO
;
290 } else if (strcmp("atomic", arg
.s
) == 0) {
291 win
->file
->save_method
= TEXT_SAVE_ATOMIC
;
292 } else if (strcmp("inplace", arg
.s
) == 0) {
293 win
->file
->save_method
= TEXT_SAVE_INPLACE
;
295 vis_info_show(vis
, "Invalid save method `%s', expected "
296 "'auto', 'atomic' or 'inplace'", arg
.s
);
300 case OPTION_CHANGE_256COLORS
:
301 vis
->change_colors
= toggle
? !vis
->change_colors
: arg
.b
;
310 static bool is_file_pattern(const char *pattern
) {
314 if (stat(pattern
, &meta
) == 0 && S_ISDIR(meta
.st_mode
))
316 for (char special
[] = "*?[{$~", *s
= special
; *s
; s
++) {
317 if (strchr(pattern
, *s
))
323 static const char *file_open_dialog(Vis
*vis
, const char *pattern
) {
324 static char name
[PATH_MAX
];
327 if (!is_file_pattern(pattern
))
330 Buffer bufcmd
, bufout
, buferr
;
331 buffer_init(&bufcmd
);
332 buffer_init(&bufout
);
333 buffer_init(&buferr
);
335 if (!buffer_put0(&bufcmd
, VIS_OPEN
" ") || !buffer_append0(&bufcmd
, pattern
? pattern
: ""))
338 Filerange empty
= text_range_new(0,0);
339 int status
= vis_pipe(vis
, vis
->win
->file
, &empty
,
340 (const char*[]){ buffer_content0(&bufcmd
), NULL
},
341 &bufout
, read_buffer
, &buferr
, read_buffer
);
344 strncpy(name
, buffer_content0(&bufout
), sizeof(name
)-1);
346 vis_info_show(vis
, "Command failed %s", buffer_content0(&buferr
));
348 buffer_release(&bufcmd
);
349 buffer_release(&bufout
);
350 buffer_release(&buferr
);
352 for (char *end
= name
+strlen(name
)-1; end
>= name
&& isspace((unsigned char)*end
); end
--)
355 return name
[0] ? name
: NULL
;
358 static bool openfiles(Vis
*vis
, const char **files
) {
359 for (; *files
; files
++) {
360 const char *file
= file_open_dialog(vis
, *files
);
364 if (!vis_window_new(vis
, file
)) {
365 vis_info_show(vis
, "Could not open `%s' %s", file
,
366 errno
? strerror(errno
) : "");
373 static bool cmd_open(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
375 return vis_window_new(vis
, NULL
);
376 return openfiles(vis
, &argv
[1]);
379 static void info_unsaved_changes(Vis
*vis
) {
380 vis_info_show(vis
, "No write since last change (add ! to override)");
383 static bool cmd_edit(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
385 vis_info_show(vis
, "Only 1 filename allowed");
391 if (cmd
->flags
!= '!' && !vis_window_closable(oldwin
)) {
392 info_unsaved_changes(vis
);
396 if (oldwin
->file
->refcount
> 1) {
397 vis_info_show(vis
, "Can not reload file being opened multiple times");
400 return vis_window_reload(oldwin
);
402 if (!openfiles(vis
, &argv
[1]))
404 if (vis
->win
!= oldwin
) {
405 Win
*newwin
= vis
->win
;
406 vis_window_swap(oldwin
, newwin
);
407 vis_window_close(oldwin
);
408 vis_window_focus(newwin
);
410 return vis
->win
!= oldwin
;
413 static bool cmd_read(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
415 const size_t first_file
= 3;
416 const char *args
[MAX_ARGV
] = { argv
[0], "cat", "--" };
417 const char **name
= argv
[1] ? &argv
[1] : (const char*[]){ ".", NULL
};
418 for (size_t i
= first_file
; *name
&& i
< LENGTH(args
)-1; name
++, i
++) {
419 const char *file
= file_open_dialog(vis
, *name
);
420 if (!file
|| !(args
[i
] = strdup(file
)))
423 args
[LENGTH(args
)-1] = NULL
;
424 ret
= cmd_pipein(vis
, win
, cmd
, args
, cur
, range
);
426 for (size_t i
= first_file
; i
< LENGTH(args
); i
++)
427 free((char*)args
[i
]);
431 static bool has_windows(Vis
*vis
) {
432 for (Win
*win
= vis
->windows
; win
; win
= win
->next
) {
433 if (!win
->file
->internal
)
439 static bool cmd_quit(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
440 if (cmd
->flags
!= '!' && !vis_window_closable(win
)) {
441 info_unsaved_changes(vis
);
444 vis_window_close(win
);
445 if (!has_windows(vis
))
446 vis_exit(vis
, EXIT_SUCCESS
);
450 static bool cmd_qall(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
451 for (Win
*next
, *win
= vis
->windows
; win
; win
= next
) {
453 if (!win
->file
->internal
&& (!text_modified(win
->file
->text
) || cmd
->flags
== '!'))
454 vis_window_close(win
);
456 if (!has_windows(vis
)) {
457 vis_exit(vis
, EXIT_SUCCESS
);
460 info_unsaved_changes(vis
);
465 static bool cmd_split(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
468 enum UiOption options
= view_options_get(win
->view
);
469 windows_arrange(vis
, UI_LAYOUT_HORIZONTAL
);
471 return vis_window_split(win
);
472 bool ret
= openfiles(vis
, &argv
[1]);
474 view_options_set(vis
->win
->view
, options
);
478 static bool cmd_vsplit(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
481 enum UiOption options
= view_options_get(win
->view
);
482 windows_arrange(vis
, UI_LAYOUT_VERTICAL
);
484 return vis_window_split(win
);
485 bool ret
= openfiles(vis
, &argv
[1]);
487 view_options_set(vis
->win
->view
, options
);
491 static bool cmd_new(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
492 windows_arrange(vis
, UI_LAYOUT_HORIZONTAL
);
493 return vis_window_new(vis
, NULL
);
496 static bool cmd_vnew(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
497 windows_arrange(vis
, UI_LAYOUT_VERTICAL
);
498 return vis_window_new(vis
, NULL
);
501 static bool cmd_wq(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
504 File
*file
= win
->file
;
505 bool unmodified
= file
->fd
== -1 && !file
->name
&& !text_modified(file
->text
);
506 if (unmodified
|| cmd_write(vis
, win
, cmd
, argv
, cur
, range
))
507 return cmd_quit(vis
, win
, cmd
, argv
, cur
, range
);
511 static bool cmd_earlier_later(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
514 Text
*txt
= win
->file
->text
;
520 count
= strtol(argv
[1], &unit
, 10);
521 if (errno
|| unit
== argv
[1] || count
< 0) {
522 vis_info_show(vis
, "Invalid number");
527 while (*unit
&& isspace((unsigned char)*unit
))
530 case 'd': count
*= 24; /* fall through */
531 case 'h': count
*= 60; /* fall through */
532 case 'm': count
*= 60; /* fall through */
535 vis_info_show(vis
, "Unknown time specifier (use: s,m,h or d)");
539 if (argv
[0][0] == 'e')
540 count
= -count
; /* earlier, move back in time */
542 pos
= text_restore(txt
, text_state(txt
) + count
);
547 if (argv
[0][0] == 'e')
548 pos
= text_earlier(txt
, count
);
550 pos
= text_later(txt
, count
);
553 time_t state
= text_state(txt
);
555 strftime(buf
, sizeof buf
, "State from %H:%M", localtime(&state
));
556 vis_info_show(vis
, "%s", buf
);
561 static bool print_keylayout(const char *key
, void *value
, void *data
) {
562 return text_appendf(data
, " %-18s\t%s\n", key
[0] == ' ' ? "␣" : key
, (char*)value
);
565 static bool print_keybinding(const char *key
, void *value
, void *data
) {
566 KeyBinding
*binding
= value
;
567 const char *desc
= binding
->alias
;
568 if (!desc
&& binding
->action
)
569 desc
= VIS_HELP_USE(binding
->action
->help
);
570 return text_appendf(data
, " %-18s\t%s\n", key
[0] == ' ' ? "␣" : key
, desc
? desc
: "");
573 static void print_mode(Mode
*mode
, Text
*txt
) {
574 if (!map_empty(mode
->bindings
))
575 text_appendf(txt
, "\n %s\n\n", mode
->name
);
576 map_iterate(mode
->bindings
, print_keybinding
, txt
);
579 static bool print_action(const char *key
, void *value
, void *data
) {
580 const char *help
= VIS_HELP_USE(((KeyAction
*)value
)->help
);
581 return text_appendf(data
, " %-30s\t%s\n", key
, help
? help
: "");
584 static bool print_cmd(const char *key
, void *value
, void *data
) {
585 CommandDef
*cmd
= value
;
586 const char *help
= VIS_HELP_USE(cmd
->help
);
588 snprintf(usage
, sizeof usage
, "%s%s%s%s%s%s%s",
590 (cmd
->flags
& CMD_FORCE
) ? "[!]" : "",
591 (cmd
->flags
& CMD_TEXT
) ? "/text/" : "",
592 (cmd
->flags
& CMD_REGEX
) ? "/regexp/" : "",
593 (cmd
->flags
& CMD_CMD
) ? " command" : "",
594 (cmd
->flags
& CMD_SHELL
) ? (!strcmp(cmd
->name
, "s") ? "/regexp/text/" : " shell-command") : "",
595 (cmd
->flags
& CMD_ARGV
) ? " [args...]" : "");
596 return text_appendf(data
, " %-30s %s\n", usage
, help
? help
: "");
599 static void print_symbolic_keys(Vis
*vis
, Text
*txt
) {
600 static const int keys
[] = {
601 TERMKEY_SYM_BACKSPACE
,
617 TERMKEY_SYM_PAGEDOWN
,
634 TERMKEY_SYM_REFERENCE
,
658 TERMKEY_SYM_KPPERIOD
,
659 TERMKEY_SYM_KPEQUALS
,
662 TermKey
*termkey
= vis
->ui
->termkey_get(vis
->ui
);
663 text_appendf(txt
, " ␣ (a literal \" \" space symbol must be used to refer to <Space>)\n");
664 for (size_t i
= 0; i
< LENGTH(keys
); i
++) {
665 text_appendf(txt
, " <%s>\n", termkey_get_keyname(termkey
, keys
[i
]));
669 static bool cmd_help(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
670 if (!vis_window_new(vis
, NULL
))
673 Text
*txt
= vis
->win
->file
->text
;
675 text_appendf(txt
, "vis %s (PID: %ld)\n\n", VERSION
, (long)getpid());
677 text_appendf(txt
, " Modes\n\n");
678 for (int i
= 0; i
< LENGTH(vis_modes
); i
++) {
679 Mode
*mode
= &vis_modes
[i
];
681 text_appendf(txt
, " %-18s\t%s\n", mode
->name
, mode
->help
);
684 if (!map_empty(vis
->keymap
)) {
685 text_appendf(txt
, "\n Layout specific mappings (affects all modes except INSERT/REPLACE)\n\n");
686 map_iterate(vis
->keymap
, print_keylayout
, txt
);
689 print_mode(&vis_modes
[VIS_MODE_NORMAL
], txt
);
690 print_mode(&vis_modes
[VIS_MODE_OPERATOR_PENDING
], txt
);
691 print_mode(&vis_modes
[VIS_MODE_VISUAL
], txt
);
692 print_mode(&vis_modes
[VIS_MODE_INSERT
], txt
);
694 text_appendf(txt
, "\n :-Commands\n\n");
695 map_iterate(vis
->cmds
, print_cmd
, txt
);
697 text_appendf(txt
, "\n Marks\n\n");
698 text_appendf(txt
, " a-z General purpose marks\n");
699 for (size_t i
= 0; i
< LENGTH(vis_marks
); i
++) {
700 const char *help
= VIS_HELP_USE(vis_marks
[i
].help
);
701 text_appendf(txt
, " %c %s\n", vis_marks
[i
].name
, help
? help
: "");
704 text_appendf(txt
, "\n Registers\n\n");
705 text_appendf(txt
, " a-z General purpose registers\n");
706 text_appendf(txt
, " A-Z Append to corresponding general purpose register\n");
707 for (size_t i
= 0; i
< LENGTH(vis_registers
); i
++) {
708 const char *help
= VIS_HELP_USE(vis_registers
[i
].help
);
709 text_appendf(txt
, " %c %s\n", vis_registers
[i
].name
, help
? help
: "");
712 text_appendf(txt
, "\n :set command options\n\n");
713 for (int i
= 0; i
< LENGTH(options
); i
++) {
715 const OptionDef
*opt
= &options
[i
];
716 const char *help
= VIS_HELP_USE(opt
->help
);
717 snprintf(names
, sizeof names
, "%s%s%s%s%s",
719 opt
->names
[1] ? "|" : "",
720 opt
->names
[1] ? opt
->names
[1] : "",
721 opt
->type
== OPTION_TYPE_BOOL
? " on|off" : "",
722 opt
->type
== OPTION_TYPE_NUMBER
? " nn" : "");
723 text_appendf(txt
, " %-30s %s\n", names
, help
? help
: "");
726 text_appendf(txt
, "\n Key binding actions\n\n");
727 map_iterate(vis
->actions
, print_action
, txt
);
729 text_appendf(txt
, "\n Symbolic keys usable for key bindings "
730 "(prefix with C-, S-, and M- for Ctrl, Shift and Alt respectively)\n\n");
731 print_symbolic_keys(vis
, txt
);
733 char *paths
[] = { NULL
, NULL
};
734 char *paths_description
[] = {
735 "Lua paths used to load runtime files (? will be replaced by filename):",
736 "Lua paths used to load C libraries (? will be replaced by filename):",
739 if (vis_lua_paths_get(vis
, &paths
[0], &paths
[1])) {
740 for (size_t i
= 0; i
< LENGTH(paths
); i
++) {
741 text_appendf(txt
, "\n %s\n\n", paths_description
[i
]);
742 for (char *elem
= paths
[i
], *next
; elem
; elem
= next
) {
743 if ((next
= strstr(elem
, ";")))
746 text_appendf(txt
, " %s\n", elem
);
752 text_appendf(txt
, "\n Compile time configuration\n\n");
758 { "Curses support: ", CONFIG_CURSES
},
759 { "Lua support: ", CONFIG_LUA
},
760 { "Lua LPeg statically built-in: ", CONFIG_LPEG
},
761 { "TRE based regex support: ", CONFIG_TRE
},
762 { "POSIX ACL support: ", CONFIG_ACL
},
763 { "SELinux support: ", CONFIG_SELINUX
},
766 for (size_t i
= 0; i
< LENGTH(configs
); i
++)
767 text_appendf(txt
, " %-32s\t%s\n", configs
[i
].name
, configs
[i
].enabled
? "yes" : "no");
769 text_save(txt
, NULL
);
770 view_cursor_to(vis
->win
->view
, 0);
773 vis_motion(vis
, VIS_MOVE_SEARCH_FORWARD
, argv
[1]);
777 static bool cmd_langmap(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
778 const char *nonlatin
= argv
[1];
779 const char *latin
= argv
[2];
782 if (!latin
|| !nonlatin
) {
783 vis_info_show(vis
, "usage: langmap <non-latin keys> <latin keys>");
787 while (*latin
&& *nonlatin
) {
789 char latin_key
[8], nonlatin_key
[8];
791 if (i
< sizeof(latin_key
)-1)
792 latin_key
[i
++] = *latin
;
794 } while (!ISUTF8(*latin
));
796 if (j
< sizeof(nonlatin_key
)-1)
797 nonlatin_key
[j
++] = *nonlatin
;
799 } while (!ISUTF8(*nonlatin
));
801 nonlatin_key
[j
] = '\0';
802 mapped
&= vis_keymap_add(vis
, nonlatin_key
, strdup(latin_key
));
808 static bool cmd_map(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
810 bool local
= strstr(argv
[0], "-") != NULL
;
811 enum VisMode mode
= vis_mode_from(vis
, argv
[1]);
814 vis_info_show(vis
, "Invalid window for :%s", argv
[0]);
818 if (mode
== VIS_MODE_INVALID
|| !argv
[2] || !argv
[3]) {
819 vis_info_show(vis
, "usage: %s mode lhs rhs", argv
[0]);
823 const char *lhs
= argv
[2];
824 KeyBinding
*binding
= vis_binding_new(vis
);
825 if (!binding
|| !(binding
->alias
= strdup(argv
[3])))
829 mapped
= vis_window_mode_map(win
, mode
, cmd
->flags
== '!', lhs
, binding
);
831 mapped
= vis_mode_map(vis
, mode
, cmd
->flags
== '!', lhs
, binding
);
835 vis_info_show(vis
, "Failed to map `%s' in %s mode%s", lhs
, argv
[1],
836 cmd
->flags
!= '!' ? ", mapping already exists, "
837 "override with `!'" : "");
838 vis_binding_free(vis
, binding
);
843 static bool cmd_unmap(Vis
*vis
, Win
*win
, Command
*cmd
, const char *argv
[], Cursor
*cur
, Filerange
*range
) {
844 bool unmapped
= false;
845 bool local
= strstr(argv
[0], "-") != NULL
;
846 enum VisMode mode
= vis_mode_from(vis
, argv
[1]);
847 const char *lhs
= argv
[2];
850 vis_info_show(vis
, "Invalid window for :%s", argv
[0]);
854 if (mode
== VIS_MODE_INVALID
|| !lhs
) {
855 vis_info_show(vis
, "usage: %s mode lhs", argv
[0]);
860 unmapped
= vis_window_mode_unmap(win
, mode
, lhs
);
862 unmapped
= vis_mode_unmap(vis
, mode
, lhs
);
864 vis_info_show(vis
, "Failed to unmap `%s' in %s mode", lhs
, argv
[1]);