vis: add support for command register ":
[vis.git] / main.c
blob95786beb4e161d34e283788f3435a82c5fac6cd0
1 #include <signal.h>
2 #include <limits.h>
3 #include <string.h>
4 #include <wchar.h>
5 #include <errno.h>
7 #include "ui-curses.h"
8 #include "vis.h"
9 #include "vis-lua.h"
10 #include "text-util.h"
11 #include "text-motions.h"
12 #include "text-objects.h"
13 #include "util.h"
14 #include "libutf.h"
16 #define PAGE INT_MAX
17 #define PAGE_HALF (INT_MAX-1)
19 /** functions to be called from keybindings */
20 /* ignore key, do nothing */
21 static const char *nop(Vis*, const char *keys, const Arg *arg);
22 /* record/replay macro indicated by keys */
23 static const char *macro_record(Vis*, const char *keys, const Arg *arg);
24 static const char *macro_replay(Vis*, const char *keys, const Arg *arg);
25 /* temporarily suspend the editor and return to the shell, type 'fg' to get back */
26 static const char *suspend(Vis*, const char *keys, const Arg *arg);
27 /* switch to mode indicated by arg->i */
28 static const char *switchmode(Vis*, const char *keys, const Arg *arg);
29 /* switch to insert mode after performing movement indicated by arg->i */
30 static const char *insertmode(Vis*, const char *keys, const Arg *arg);
31 /* set mark indicated by keys to current cursor position */
32 static const char *mark_set(Vis*, const char *keys, const Arg *arg);
33 /* add a new line either before or after the one where the cursor currently is */
34 static const char *openline(Vis*, const char *keys, const Arg *arg);
35 /* join lines from current cursor position to movement indicated by arg */
36 static const char *join(Vis*, const char *keys, const Arg *arg);
37 /* perform last action i.e. action_prev again */
38 static const char *repeat(Vis*, const char *keys, const Arg *arg);
39 /* replace character at cursor with one from keys */
40 static const char *replace(Vis*, const char *keys, const Arg *arg);
41 /* create a new cursor on the previous (arg->i < 0) or next (arg->i > 0) line */
42 static const char *cursors_new(Vis*, const char *keys, const Arg *arg);
43 /* try to align all cursors on the same column */
44 static const char *cursors_align(Vis*, const char *keys, const Arg *arg);
45 /* try to align all cursors by inserting the correct amount of white spaces */
46 static const char *cursors_align_indent(Vis*, const char *keys, const Arg *arg);
47 /* remove all but the primary cursor and their selections */
48 static const char *cursors_clear(Vis*, const char *keys, const Arg *arg);
49 /* remove the least recently added cursor */
50 static const char *cursors_remove(Vis*, const char *keys, const Arg *arg);
51 /* move to the previous (arg->i < 0) or next (arg->i > 0) cursor */
52 static const char *cursors_navigate(Vis*, const char *keys, const Arg *arg);
53 /* select the word the cursor is currently over */
54 static const char *cursors_select(Vis*, const char *keys, const Arg *arg);
55 /* select the next region matching the current selection */
56 static const char *cursors_select_next(Vis*, const char *keys, const Arg *arg);
57 /* clear current selection but select next match */
58 static const char *cursors_select_skip(Vis*, const char *keys, const Arg *arg);
59 /* adjust current used count according to keys */
60 static const char *count(Vis*, const char *keys, const Arg *arg);
61 /* move to the count-th line or if not given either to the first (arg->i < 0)
62 * or last (arg->i > 0) line of file */
63 static const char *gotoline(Vis*, const char *keys, const Arg *arg);
64 /* set motion type either LINEWISE or CHARWISE via arg->i */
65 static const char *motiontype(Vis*, const char *keys, const Arg *arg);
66 /* make the current action use the operator indicated by arg->i */
67 static const char *operator(Vis*, const char *keys, const Arg *arg);
68 /* use arg->s as command for the filter operator */
69 static const char *operator_filter(Vis*, const char *keys, const Arg *arg);
70 /* blocks to read a key and performs movement indicated by arg->i which
71 * should be one of VIS_MOVE_{RIGHT,LEFT}_{TO,TILL} */
72 static const char *movement_key(Vis*, const char *keys, const Arg *arg);
73 /* perform the movement as indicated by arg->i */
74 static const char *movement(Vis*, const char *keys, const Arg *arg);
75 /* let the current operator affect the range indicated by the text object arg->i */
76 static const char *textobj(Vis*, const char *keys, const Arg *arg);
77 /* move to the other end of selected text */
78 static const char *selection_end(Vis*, const char *keys, const Arg *arg);
79 /* restore least recently used selection */
80 static const char *selection_restore(Vis*, const char *keys, const Arg *arg);
81 /* use register indicated by keys for the current operator */
82 static const char *reg(Vis*, const char *keys, const Arg *arg);
83 /* perform arg->i motion with a mark indicated by keys as argument */
84 static const char *mark_motion(Vis*, const char *keys, const Arg *arg);
85 /* {un,re}do last action, redraw window */
86 static const char *undo(Vis*, const char *keys, const Arg *arg);
87 static const char *redo(Vis*, const char *keys, const Arg *arg);
88 /* earlier, later action chronologically, redraw window */
89 static const char *earlier(Vis*, const char *keys, const Arg *arg);
90 static const char *later(Vis*, const char *keys, const Arg *arg);
91 /* delete from the current cursor position to the end of
92 * movement as indicated by arg->i */
93 static const char *delete(Vis*, const char *keys, const Arg *arg);
94 /* insert register content indicated by keys at current cursor position */
95 static const char *insert_register(Vis*, const char *keys, const Arg *arg);
96 /* show a user prompt to get input with title arg->s */
97 static const char *prompt_show(Vis*, const char *keys, const Arg *arg);
98 /* blocks to read 3 consecutive digits and inserts the corresponding byte value */
99 static const char *insert_verbatim(Vis*, const char *keys, const Arg *arg);
100 /* scroll window content according to arg->i which can be either PAGE, PAGE_HALF,
101 * or an arbitrary number of lines. a multiplier overrides what is given in arg->i.
102 * negative values scroll back, positive forward. */
103 static const char *wscroll(Vis*, const char *keys, const Arg *arg);
104 /* similar to scroll, but do only move window content not cursor position */
105 static const char *wslide(Vis*, const char *keys, const Arg *arg);
106 /* call editor function as indicated by arg->f */
107 static const char *call(Vis*, const char *keys, const Arg *arg);
108 /* call window function as indicated by arg->w */
109 static const char *window(Vis*, const char *keys, const Arg *arg);
110 /* show info about Unicode character at cursor position */
111 static const char *unicode_info(Vis*, const char *keys, const Arg *arg);
112 /* either go to count % of ile or to matching item */
113 static const char *percent(Vis*, const char *keys, const Arg *arg);
114 /* either increment (arg->i > 0) or decrement (arg->i < 0) number under cursor */
115 static const char *number_increment_decrement(Vis*, const char *keys, const Arg *arg);
116 /* open a filename under cursor in same (!arg->b) or new (arg->b) window */
117 static const char *open_file_under_cursor(Vis*, const char *keys, const Arg *arg);
119 enum {
120 VIS_ACTION_EDITOR_SUSPEND,
121 VIS_ACTION_CURSOR_CHAR_PREV,
122 VIS_ACTION_CURSOR_CHAR_NEXT,
123 VIS_ACTION_CURSOR_WORD_START_PREV,
124 VIS_ACTION_CURSOR_WORD_START_NEXT,
125 VIS_ACTION_CURSOR_WORD_END_PREV,
126 VIS_ACTION_CURSOR_WORD_END_NEXT,
127 VIS_ACTION_CURSOR_LONGWORD_START_PREV,
128 VIS_ACTION_CURSOR_LONGWORD_START_NEXT,
129 VIS_ACTION_CURSOR_LONGWORD_END_PREV,
130 VIS_ACTION_CURSOR_LONGWORD_END_NEXT,
131 VIS_ACTION_CURSOR_LINE_UP,
132 VIS_ACTION_CURSOR_LINE_DOWN,
133 VIS_ACTION_CURSOR_LINE_START,
134 VIS_ACTION_CURSOR_LINE_FINISH,
135 VIS_ACTION_CURSOR_LINE_BEGIN,
136 VIS_ACTION_CURSOR_LINE_END,
137 VIS_ACTION_CURSOR_LINE_LASTCHAR,
138 VIS_ACTION_CURSOR_SCREEN_LINE_UP,
139 VIS_ACTION_CURSOR_SCREEN_LINE_DOWN,
140 VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN,
141 VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE,
142 VIS_ACTION_CURSOR_SCREEN_LINE_END,
143 VIS_ACTION_CURSOR_PERCENT,
144 VIS_ACTION_CURSOR_PARAGRAPH_PREV,
145 VIS_ACTION_CURSOR_PARAGRAPH_NEXT,
146 VIS_ACTION_CURSOR_SENTENCE_PREV,
147 VIS_ACTION_CURSOR_SENTENCE_NEXT,
148 VIS_ACTION_CURSOR_FUNCTION_START_PREV,
149 VIS_ACTION_CURSOR_FUNCTION_END_PREV,
150 VIS_ACTION_CURSOR_FUNCTION_START_NEXT,
151 VIS_ACTION_CURSOR_FUNCTION_END_NEXT,
152 VIS_ACTION_CURSOR_COLUMN,
153 VIS_ACTION_CURSOR_LINE_FIRST,
154 VIS_ACTION_CURSOR_LINE_LAST,
155 VIS_ACTION_CURSOR_WINDOW_LINE_TOP,
156 VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE,
157 VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM,
158 VIS_ACTION_CURSOR_SEARCH_NEXT,
159 VIS_ACTION_CURSOR_SEARCH_PREV,
160 VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD,
161 VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD,
162 VIS_ACTION_WINDOW_PAGE_UP,
163 VIS_ACTION_WINDOW_PAGE_DOWN,
164 VIS_ACTION_WINDOW_HALFPAGE_UP,
165 VIS_ACTION_WINDOW_HALFPAGE_DOWN,
166 VIS_ACTION_MODE_NORMAL,
167 VIS_ACTION_MODE_VISUAL,
168 VIS_ACTION_MODE_VISUAL_LINE,
169 VIS_ACTION_MODE_INSERT,
170 VIS_ACTION_MODE_REPLACE,
171 VIS_ACTION_MODE_OPERATOR_PENDING,
172 VIS_ACTION_DELETE_CHAR_PREV,
173 VIS_ACTION_DELETE_CHAR_NEXT,
174 VIS_ACTION_DELETE_LINE_BEGIN,
175 VIS_ACTION_DELETE_WORD_PREV,
176 VIS_ACTION_JUMPLIST_PREV,
177 VIS_ACTION_JUMPLIST_NEXT,
178 VIS_ACTION_CHANGELIST_PREV,
179 VIS_ACTION_CHANGELIST_NEXT,
180 VIS_ACTION_UNDO,
181 VIS_ACTION_REDO,
182 VIS_ACTION_EARLIER,
183 VIS_ACTION_LATER,
184 VIS_ACTION_MACRO_RECORD,
185 VIS_ACTION_MACRO_REPLAY,
186 VIS_ACTION_MARK_SET,
187 VIS_ACTION_MARK_GOTO,
188 VIS_ACTION_MARK_GOTO_LINE,
189 VIS_ACTION_REDRAW,
190 VIS_ACTION_REPLACE_CHAR,
191 VIS_ACTION_TOTILL_REPEAT,
192 VIS_ACTION_TOTILL_REVERSE,
193 VIS_ACTION_PROMPT_SEARCH_FORWARD,
194 VIS_ACTION_PROMPT_SEARCH_BACKWARD,
195 VIS_ACTION_TILL_LEFT,
196 VIS_ACTION_TILL_RIGHT,
197 VIS_ACTION_TO_LEFT,
198 VIS_ACTION_TO_RIGHT,
199 VIS_ACTION_REGISTER,
200 VIS_ACTION_OPERATOR_CHANGE,
201 VIS_ACTION_OPERATOR_DELETE,
202 VIS_ACTION_OPERATOR_YANK,
203 VIS_ACTION_OPERATOR_SHIFT_LEFT,
204 VIS_ACTION_OPERATOR_SHIFT_RIGHT,
205 VIS_ACTION_OPERATOR_CASE_LOWER,
206 VIS_ACTION_OPERATOR_CASE_UPPER,
207 VIS_ACTION_OPERATOR_CASE_SWAP,
208 VIS_ACTION_OPERATOR_FILTER,
209 VIS_ACTION_OPERATOR_FILTER_FMT,
210 VIS_ACTION_COUNT,
211 VIS_ACTION_INSERT_NEWLINE,
212 VIS_ACTION_INSERT_TAB,
213 VIS_ACTION_INSERT_VERBATIM,
214 VIS_ACTION_INSERT_REGISTER,
215 VIS_ACTION_WINDOW_NEXT,
216 VIS_ACTION_WINDOW_PREV,
217 VIS_ACTION_APPEND_CHAR_NEXT,
218 VIS_ACTION_APPEND_LINE_END,
219 VIS_ACTION_INSERT_LINE_START,
220 VIS_ACTION_OPEN_LINE_ABOVE,
221 VIS_ACTION_OPEN_LINE_BELOW,
222 VIS_ACTION_JOIN_LINE_BELOW,
223 VIS_ACTION_JOIN_LINES,
224 VIS_ACTION_PROMPT_SHOW,
225 VIS_ACTION_PROMPT_SHOW_VISUAL,
226 VIS_ACTION_REPEAT,
227 VIS_ACTION_SELECTION_FLIP,
228 VIS_ACTION_SELECTION_RESTORE,
229 VIS_ACTION_WINDOW_REDRAW_TOP,
230 VIS_ACTION_WINDOW_REDRAW_CENTER,
231 VIS_ACTION_WINDOW_REDRAW_BOTTOM,
232 VIS_ACTION_WINDOW_SLIDE_UP,
233 VIS_ACTION_WINDOW_SLIDE_DOWN,
234 VIS_ACTION_PUT_AFTER,
235 VIS_ACTION_PUT_BEFORE,
236 VIS_ACTION_PUT_AFTER_END,
237 VIS_ACTION_PUT_BEFORE_END,
238 VIS_ACTION_CURSOR_SELECT_WORD,
239 VIS_ACTION_CURSORS_NEW_LINE_ABOVE,
240 VIS_ACTION_CURSORS_NEW_LINE_BELOW,
241 VIS_ACTION_CURSORS_NEW_LINES_BEGIN,
242 VIS_ACTION_CURSORS_NEW_LINES_END,
243 VIS_ACTION_CURSORS_NEW_MATCH_NEXT,
244 VIS_ACTION_CURSORS_NEW_MATCH_SKIP,
245 VIS_ACTION_CURSORS_ALIGN,
246 VIS_ACTION_CURSORS_ALIGN_INDENT,
247 VIS_ACTION_CURSORS_REMOVE_ALL,
248 VIS_ACTION_CURSORS_REMOVE_LAST,
249 VIS_ACTION_CURSORS_PREV,
250 VIS_ACTION_CURSORS_NEXT,
251 VIS_ACTION_TEXT_OBJECT_WORD_OUTER,
252 VIS_ACTION_TEXT_OBJECT_WORD_INNER,
253 VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER,
254 VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER,
255 VIS_ACTION_TEXT_OBJECT_SENTENCE,
256 VIS_ACTION_TEXT_OBJECT_PARAGRAPH,
257 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER,
258 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER,
259 VIS_ACTION_TEXT_OBJECT_PARANTHESE_OUTER,
260 VIS_ACTION_TEXT_OBJECT_PARANTHESE_INNER,
261 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER,
262 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER,
263 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER,
264 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER,
265 VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER,
266 VIS_ACTION_TEXT_OBJECT_QUOTE_INNER,
267 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER,
268 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER,
269 VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER,
270 VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER,
271 VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER,
272 VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER,
273 VIS_ACTION_TEXT_OBJECT_FUNCTION_OUTER,
274 VIS_ACTION_TEXT_OBJECT_FUNCTION_INNER,
275 VIS_ACTION_TEXT_OBJECT_LINE_OUTER,
276 VIS_ACTION_TEXT_OBJECT_LINE_INNER,
277 VIS_ACTION_TEXT_OBJECT_INDENTATION,
278 VIS_ACTION_TEXT_OBJECT_SEARCH_FORWARD,
279 VIS_ACTION_TEXT_OBJECT_SEARCH_BACKWARD,
280 VIS_ACTION_MOTION_CHARWISE,
281 VIS_ACTION_MOTION_LINEWISE,
282 VIS_ACTION_UNICODE_INFO,
283 VIS_ACTION_NUMBER_INCREMENT,
284 VIS_ACTION_NUMBER_DECREMENT,
285 VIS_ACTION_OPEN_FILE_UNDER_CURSOR,
286 VIS_ACTION_OPEN_FILE_UNDER_CURSOR_NEW_WINDOW,
287 VIS_ACTION_NOP,
290 static const KeyAction vis_action[] = {
291 [VIS_ACTION_EDITOR_SUSPEND] = {
292 "editor-suspend",
293 "Suspend the editor",
294 suspend,
296 [VIS_ACTION_CURSOR_CHAR_PREV] = {
297 "cursor-char-prev",
298 "Move cursor left, to the previous character",
299 movement, { .i = VIS_MOVE_CHAR_PREV }
301 [VIS_ACTION_CURSOR_CHAR_NEXT] = {
302 "cursor-char-next",
303 "Move cursor right, to the next character",
304 movement, { .i = VIS_MOVE_CHAR_NEXT }
306 [VIS_ACTION_CURSOR_WORD_START_PREV] = {
307 "cursor-word-start-prev",
308 "Move cursor words backwards",
309 movement, { .i = VIS_MOVE_WORD_START_PREV }
311 [VIS_ACTION_CURSOR_WORD_START_NEXT] = {
312 "cursor-word-start-next",
313 "Move cursor words forwards",
314 movement, { .i = VIS_MOVE_WORD_START_NEXT }
316 [VIS_ACTION_CURSOR_WORD_END_PREV] = {
317 "cursor-word-end-prev",
318 "Move cursor backwards to the end of word",
319 movement, { .i = VIS_MOVE_WORD_END_PREV }
321 [VIS_ACTION_CURSOR_WORD_END_NEXT] = {
322 "cursor-word-end-next",
323 "Move cursor forward to the end of word",
324 movement, { .i = VIS_MOVE_WORD_END_NEXT }
326 [VIS_ACTION_CURSOR_LONGWORD_START_PREV] = {
327 "cursor-longword-start-prev",
328 "Move cursor WORDS backwards",
329 movement, { .i = VIS_MOVE_LONGWORD_START_PREV }
331 [VIS_ACTION_CURSOR_LONGWORD_START_NEXT] = {
332 "cursor-longword-start-next",
333 "Move cursor WORDS forwards",
334 movement, { .i = VIS_MOVE_LONGWORD_START_NEXT }
336 [VIS_ACTION_CURSOR_LONGWORD_END_PREV] = {
337 "cursor-longword-end-prev",
338 "Move cursor backwards to the end of WORD",
339 movement, { .i = VIS_MOVE_LONGWORD_END_PREV }
341 [VIS_ACTION_CURSOR_LONGWORD_END_NEXT] = {
342 "cursor-longword-end-next",
343 "Move cursor forward to the end of WORD",
344 movement, { .i = VIS_MOVE_LONGWORD_END_NEXT }
346 [VIS_ACTION_CURSOR_LINE_UP] = {
347 "cursor-line-up",
348 "Move cursor line upwards",
349 movement, { .i = VIS_MOVE_LINE_UP }
351 [VIS_ACTION_CURSOR_LINE_DOWN] = {
352 "cursor-line-down",
353 "Move cursor line downwards",
354 movement, { .i = VIS_MOVE_LINE_DOWN }
356 [VIS_ACTION_CURSOR_LINE_START] = {
357 "cursor-line-start",
358 "Move cursor to first non-blank character of the line",
359 movement, { .i = VIS_MOVE_LINE_START }
361 [VIS_ACTION_CURSOR_LINE_FINISH] = {
362 "cursor-line-finish",
363 "Move cursor to last non-blank character of the line",
364 movement, { .i = VIS_MOVE_LINE_FINISH }
366 [VIS_ACTION_CURSOR_LINE_BEGIN] = {
367 "cursor-line-begin",
368 "Move cursor to first character of the line",
369 movement, { .i = VIS_MOVE_LINE_BEGIN }
371 [VIS_ACTION_CURSOR_LINE_END] = {
372 "cursor-line-end",
373 "Move cursor to end of the line",
374 movement, { .i = VIS_MOVE_LINE_END }
376 [VIS_ACTION_CURSOR_LINE_LASTCHAR] = {
377 "cursor-line-lastchar",
378 "Move cursor to last character of the line",
379 movement, { .i = VIS_MOVE_LINE_LASTCHAR }
381 [VIS_ACTION_CURSOR_SCREEN_LINE_UP] = {
382 "cursor-sceenline-up",
383 "Move cursor screen/display line upwards",
384 movement, { .i = VIS_MOVE_SCREEN_LINE_UP }
386 [VIS_ACTION_CURSOR_SCREEN_LINE_DOWN] = {
387 "cursor-screenline-down",
388 "Move cursor screen/display line downwards",
389 movement, { .i = VIS_MOVE_SCREEN_LINE_DOWN }
391 [VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN] = {
392 "cursor-screenline-begin",
393 "Move cursor to beginning of screen/display line",
394 movement, { .i = VIS_MOVE_SCREEN_LINE_BEGIN }
396 [VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE] = {
397 "cursor-screenline-middle",
398 "Move cursor to middle of screen/display line",
399 movement, { .i = VIS_MOVE_SCREEN_LINE_MIDDLE }
401 [VIS_ACTION_CURSOR_SCREEN_LINE_END] = {
402 "cursor-screenline-end",
403 "Move cursor to end of screen/display line",
404 movement, { .i = VIS_MOVE_SCREEN_LINE_END }
406 [VIS_ACTION_CURSOR_PERCENT] = {
407 "cursor-percent",
408 "Move to count % of file or matching item",
409 percent
411 [VIS_ACTION_CURSOR_PARAGRAPH_PREV] = {
412 "cursor-paragraph-prev",
413 "Move cursor paragraph backward",
414 movement, { .i = VIS_MOVE_PARAGRAPH_PREV }
416 [VIS_ACTION_CURSOR_PARAGRAPH_NEXT] = {
417 "cursor-paragraph-next",
418 "Move cursor paragraph forward",
419 movement, { .i = VIS_MOVE_PARAGRAPH_NEXT }
421 [VIS_ACTION_CURSOR_SENTENCE_PREV] = {
422 "cursor-sentence-prev",
423 "Move cursor sentence backward",
424 movement, { .i = VIS_MOVE_SENTENCE_PREV }
426 [VIS_ACTION_CURSOR_SENTENCE_NEXT] = {
427 "cursor-sentence-next",
428 "Move cursor sentence forward",
429 movement, { .i = VIS_MOVE_SENTENCE_NEXT }
431 [VIS_ACTION_CURSOR_FUNCTION_START_PREV] = {
432 "cursor-function-start-prev",
433 "Move cursor backwards to start of function",
434 movement, { .i = VIS_MOVE_FUNCTION_START_PREV }
436 [VIS_ACTION_CURSOR_FUNCTION_START_NEXT] = {
437 "cursor-function-start-next",
438 "Move cursor forwards to start of function",
439 movement, { .i = VIS_MOVE_FUNCTION_START_NEXT }
441 [VIS_ACTION_CURSOR_FUNCTION_END_PREV] = {
442 "cursor-function-end-prev",
443 "Move cursor backwards to end of function",
444 movement, { .i = VIS_MOVE_FUNCTION_END_PREV }
446 [VIS_ACTION_CURSOR_FUNCTION_END_NEXT] = {
447 "cursor-function-end-next",
448 "Move cursor forwards to end of function",
449 movement, { .i = VIS_MOVE_FUNCTION_END_NEXT }
451 [VIS_ACTION_CURSOR_COLUMN] = {
452 "cursor-column",
453 "Move cursor to given column of current line",
454 movement, { .i = VIS_MOVE_COLUMN }
456 [VIS_ACTION_CURSOR_LINE_FIRST] = {
457 "cursor-line-first",
458 "Move cursor to given line (defaults to first)",
459 gotoline, { .i = -1 }
461 [VIS_ACTION_CURSOR_LINE_LAST] = {
462 "cursor-line-last",
463 "Move cursor to given line (defaults to last)",
464 gotoline, { .i = +1 }
466 [VIS_ACTION_CURSOR_WINDOW_LINE_TOP] = {
467 "cursor-window-line-top",
468 "Move cursor to top line of the window",
469 movement, { .i = VIS_MOVE_WINDOW_LINE_TOP }
471 [VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE] = {
472 "cursor-window-line-middle",
473 "Move cursor to middle line of the window",
474 movement, { .i = VIS_MOVE_WINDOW_LINE_MIDDLE }
476 [VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM] = {
477 "cursor-window-line-bottom",
478 "Move cursor to bottom line of the window",
479 movement, { .i = VIS_MOVE_WINDOW_LINE_BOTTOM }
481 [VIS_ACTION_CURSOR_SEARCH_NEXT] = {
482 "cursor-search-forward",
483 "Move cursor to bottom line of the window",
484 movement, { .i = VIS_MOVE_SEARCH_NEXT }
486 [VIS_ACTION_CURSOR_SEARCH_PREV] = {
487 "cursor-search-backward",
488 "Move cursor to bottom line of the window",
489 movement, { .i = VIS_MOVE_SEARCH_PREV }
491 [VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD] = {
492 "cursor-search-word-forward",
493 "Move cursor to next occurence of the word under cursor",
494 movement, { .i = VIS_MOVE_SEARCH_WORD_FORWARD }
496 [VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD] = {
497 "cursor-search-word-backward",
498 "Move cursor to previous occurence of the word under cursor",
499 movement, { .i = VIS_MOVE_SEARCH_WORD_BACKWARD }
501 [VIS_ACTION_WINDOW_PAGE_UP] = {
502 "window-page-up",
503 "Scroll window pages backwards (upwards)",
504 wscroll, { .i = -PAGE }
506 [VIS_ACTION_WINDOW_HALFPAGE_UP] = {
507 "window-halfpage-up",
508 "Scroll window half pages backwards (upwards)",
509 wscroll, { .i = -PAGE_HALF }
511 [VIS_ACTION_WINDOW_PAGE_DOWN] = {
512 "window-page-down",
513 "Scroll window pages forwards (downwards)",
514 wscroll, { .i = +PAGE }
516 [VIS_ACTION_WINDOW_HALFPAGE_DOWN] = {
517 "window-halfpage-down",
518 "Scroll window half pages forwards (downwards)",
519 wscroll, { .i = +PAGE_HALF }
521 [VIS_ACTION_MODE_NORMAL] = {
522 "vis-mode-normal",
523 "Enter normal mode",
524 switchmode, { .i = VIS_MODE_NORMAL }
526 [VIS_ACTION_MODE_VISUAL] = {
527 "vis-mode-visual-charwise",
528 "Enter characterwise visual mode",
529 switchmode, { .i = VIS_MODE_VISUAL }
531 [VIS_ACTION_MODE_VISUAL_LINE] = {
532 "vis-mode-visual-linewise",
533 "Enter linewise visual mode",
534 switchmode, { .i = VIS_MODE_VISUAL_LINE }
536 [VIS_ACTION_MODE_INSERT] = {
537 "vis-mode-insert",
538 "Enter insert mode",
539 switchmode, { .i = VIS_MODE_INSERT }
541 [VIS_ACTION_MODE_REPLACE] = {
542 "vis-mode-replace",
543 "Enter replace mode",
544 switchmode, { .i = VIS_MODE_REPLACE }
546 [VIS_ACTION_MODE_OPERATOR_PENDING] = {
547 "vis-mode-operator-pending",
548 "Enter to operator pending mode",
549 switchmode, { .i = VIS_MODE_OPERATOR_PENDING }
551 [VIS_ACTION_DELETE_CHAR_PREV] = {
552 "delete-char-prev",
553 "Delete the previous character",
554 delete, { .i = VIS_MOVE_CHAR_PREV }
556 [VIS_ACTION_DELETE_CHAR_NEXT] = {
557 "delete-char-next",
558 "Delete the next character",
559 delete, { .i = VIS_MOVE_CHAR_NEXT }
561 [VIS_ACTION_DELETE_LINE_BEGIN] = {
562 "delete-line-begin",
563 "Delete until the start of the current line",
564 delete, { .i = VIS_MOVE_LINE_BEGIN }
566 [VIS_ACTION_DELETE_WORD_PREV] = {
567 "delete-word-prev",
568 "Delete the previous WORD",
569 delete, { .i = VIS_MOVE_LONGWORD_START_PREV }
571 [VIS_ACTION_JUMPLIST_PREV] = {
572 "jumplist-prev",
573 "Go to older cursor position in jump list",
574 movement, { .i = VIS_MOVE_JUMPLIST_PREV }
576 [VIS_ACTION_JUMPLIST_NEXT] = {
577 "jumplist-next",
578 "Go to newer cursor position in jump list",
579 movement, { .i = VIS_MOVE_JUMPLIST_NEXT }
581 [VIS_ACTION_CHANGELIST_PREV] = {
582 "changelist-prev",
583 "Go to older cursor position in change list",
584 movement, { .i = VIS_MOVE_CHANGELIST_PREV }
586 [VIS_ACTION_CHANGELIST_NEXT] = {
587 "changelist-next",
588 "Go to newer cursor position in change list",
589 movement, { .i = VIS_MOVE_CHANGELIST_NEXT }
591 [VIS_ACTION_UNDO] = {
592 "editor-undo",
593 "Undo last change",
594 undo,
596 [VIS_ACTION_REDO] = {
597 "editor-redo",
598 "Redo last change",
599 redo,
601 [VIS_ACTION_EARLIER] = {
602 "editor-earlier",
603 "Goto older text state",
604 earlier,
606 [VIS_ACTION_LATER] = {
607 "editor-later",
608 "Goto newer text state",
609 later,
611 [VIS_ACTION_MACRO_RECORD] = {
612 "macro-record",
613 "Record macro into given register",
614 macro_record,
616 [VIS_ACTION_MACRO_REPLAY] = {
617 "macro-replay",
618 "Replay macro, execute the content of the given register",
619 macro_replay,
621 [VIS_ACTION_MARK_SET] = {
622 "mark-set",
623 "Set given mark at current cursor position",
624 mark_set,
626 [VIS_ACTION_MARK_GOTO] = {
627 "mark-goto",
628 "Goto the position of the given mark",
629 mark_motion, { .i = VIS_MOVE_MARK }
631 [VIS_ACTION_MARK_GOTO_LINE] = {
632 "mark-goto-line",
633 "Goto first non-blank character of the line containing the given mark",
634 mark_motion, { .i = VIS_MOVE_MARK_LINE }
636 [VIS_ACTION_REDRAW] = {
637 "editor-redraw",
638 "Redraw current editor content",
639 call, { .f = vis_redraw }
641 [VIS_ACTION_REPLACE_CHAR] = {
642 "replace-char",
643 "Replace the character under the cursor",
644 replace,
646 [VIS_ACTION_TOTILL_REPEAT] = {
647 "totill-repeat",
648 "Repeat latest to/till motion",
649 movement, { .i = VIS_MOVE_TOTILL_REPEAT }
651 [VIS_ACTION_TOTILL_REVERSE] = {
652 "totill-reverse",
653 "Repeat latest to/till motion but in opposite direction",
654 movement, { .i = VIS_MOVE_TOTILL_REVERSE }
656 [VIS_ACTION_PROMPT_SEARCH_FORWARD] = {
657 "search-forward",
658 "Search forward",
659 prompt_show, { .s = "/" }
661 [VIS_ACTION_PROMPT_SEARCH_BACKWARD] = {
662 "search-backward",
663 "Search backward",
664 prompt_show, { .s = "?" }
666 [VIS_ACTION_TILL_LEFT] = {
667 "till-left",
668 "Till after the occurrence of character to the left",
669 movement_key, { .i = VIS_MOVE_LEFT_TILL }
671 [VIS_ACTION_TILL_RIGHT] = {
672 "till-right",
673 "Till before the occurrence of character to the right",
674 movement_key, { .i = VIS_MOVE_RIGHT_TILL }
676 [VIS_ACTION_TO_LEFT] = {
677 "to-left",
678 "To the first occurrence of character to the left",
679 movement_key, { .i = VIS_MOVE_LEFT_TO }
681 [VIS_ACTION_TO_RIGHT] = {
682 "to-right",
683 "To the first occurrence of character to the right",
684 movement_key, { .i = VIS_MOVE_RIGHT_TO }
686 [VIS_ACTION_REGISTER] = {
687 "register",
688 "Use given register for next operator",
689 reg,
691 [VIS_ACTION_OPERATOR_CHANGE] = {
692 "vis-operator-change",
693 "Change operator",
694 operator, { .i = VIS_OP_CHANGE }
696 [VIS_ACTION_OPERATOR_DELETE] = {
697 "vis-operator-delete",
698 "Delete operator",
699 operator, { .i = VIS_OP_DELETE }
701 [VIS_ACTION_OPERATOR_YANK] = {
702 "vis-operator-yank",
703 "Yank operator",
704 operator, { .i = VIS_OP_YANK }
706 [VIS_ACTION_OPERATOR_SHIFT_LEFT] = {
707 "vis-operator-shift-left",
708 "Shift left operator",
709 operator, { .i = VIS_OP_SHIFT_LEFT }
711 [VIS_ACTION_OPERATOR_SHIFT_RIGHT] = {
712 "vis-operator-shift-right",
713 "Shift right operator",
714 operator, { .i = VIS_OP_SHIFT_RIGHT }
716 [VIS_ACTION_OPERATOR_CASE_LOWER] = {
717 "vis-operator-case-lower",
718 "Lowercase operator",
719 operator, { .i = VIS_OP_CASE_LOWER }
721 [VIS_ACTION_OPERATOR_CASE_UPPER] = {
722 "vis-operator-case-upper",
723 "Uppercase operator",
724 operator, { .i = VIS_OP_CASE_UPPER }
726 [VIS_ACTION_OPERATOR_CASE_SWAP] = {
727 "vis-operator-case-swap",
728 "Swap case operator",
729 operator, { .i = VIS_OP_CASE_SWAP }
731 [VIS_ACTION_OPERATOR_FILTER] = {
732 "vis-operator-filter",
733 "Filter operator",
734 operator_filter,
736 [VIS_ACTION_OPERATOR_FILTER_FMT] = {
737 "vis-operator-filter-format",
738 "Formating operator, filter range through fmt(1)",
739 operator_filter, { .s = "'<,'>!fmt" }
741 [VIS_ACTION_COUNT] = {
742 "vis-count",
743 "Count specifier",
744 count,
746 [VIS_ACTION_INSERT_NEWLINE] = {
747 "insert-newline",
748 "Insert a line break (depending on file type)",
749 call, { .f = vis_insert_nl }
751 [VIS_ACTION_INSERT_TAB] = {
752 "insert-tab",
753 "Insert a tab (might be converted to spaces)",
754 call, { .f = vis_insert_tab }
756 [VIS_ACTION_INSERT_VERBATIM] = {
757 "insert-verbatim",
758 "Insert Unicode character based on code point",
759 insert_verbatim,
761 [VIS_ACTION_INSERT_REGISTER] = {
762 "insert-register",
763 "Insert specified register content",
764 insert_register,
766 [VIS_ACTION_WINDOW_NEXT] = {
767 "window-next",
768 "Focus next window",
769 call, { .f = vis_window_next }
771 [VIS_ACTION_WINDOW_PREV] = {
772 "window-prev",
773 "Focus previous window",
774 call, { .f = vis_window_prev }
776 [VIS_ACTION_APPEND_CHAR_NEXT] = {
777 "append-char-next",
778 "Append text after the cursor",
779 insertmode, { .i = VIS_MOVE_CHAR_NEXT }
781 [VIS_ACTION_APPEND_LINE_END] = {
782 "append-line-end",
783 "Append text after the end of the line",
784 insertmode, { .i = VIS_MOVE_LINE_END },
786 [VIS_ACTION_INSERT_LINE_START] = {
787 "insert-line-start",
788 "Insert text before the first non-blank in the line",
789 insertmode, { .i = VIS_MOVE_LINE_START },
791 [VIS_ACTION_OPEN_LINE_ABOVE] = {
792 "open-line-above",
793 "Begin a new line above the cursor",
794 openline, { .i = -1 }
796 [VIS_ACTION_OPEN_LINE_BELOW] = {
797 "open-line-below",
798 "Begin a new line below the cursor",
799 openline, { .i = +1 }
801 [VIS_ACTION_JOIN_LINE_BELOW] = {
802 "join-line-below",
803 "Join line(s)",
804 join, { .i = VIS_MOVE_LINE_NEXT },
806 [VIS_ACTION_JOIN_LINES] = {
807 "join-lines",
808 "Join selected lines",
809 operator, { .i = VIS_OP_JOIN }
811 [VIS_ACTION_PROMPT_SHOW] = {
812 "prompt-show",
813 "Show editor command line prompt",
814 prompt_show, { .s = ":" }
816 [VIS_ACTION_PROMPT_SHOW_VISUAL] = {
817 "prompt-show-visual",
818 "Show editor command line prompt in visual mode",
819 prompt_show, { .s = ":'<,'>" }
821 [VIS_ACTION_REPEAT] = {
822 "editor-repeat",
823 "Repeat latest editor command",
824 repeat
826 [VIS_ACTION_SELECTION_FLIP] = {
827 "selection-flip",
828 "Flip selection, move cursor to other end",
829 selection_end,
831 [VIS_ACTION_SELECTION_RESTORE] = {
832 "selection-restore",
833 "Restore last selection",
834 selection_restore,
836 [VIS_ACTION_WINDOW_REDRAW_TOP] = {
837 "window-redraw-top",
838 "Redraw cursor line at the top of the window",
839 window, { .w = view_redraw_top }
841 [VIS_ACTION_WINDOW_REDRAW_CENTER] = {
842 "window-redraw-center",
843 "Redraw cursor line at the center of the window",
844 window, { .w = view_redraw_center }
846 [VIS_ACTION_WINDOW_REDRAW_BOTTOM] = {
847 "window-redraw-bottom",
848 "Redraw cursor line at the bottom of the window",
849 window, { .w = view_redraw_bottom }
851 [VIS_ACTION_WINDOW_SLIDE_UP] = {
852 "window-slide-up",
853 "Slide window content upwards",
854 wslide, { .i = -1 }
856 [VIS_ACTION_WINDOW_SLIDE_DOWN] = {
857 "window-slide-down",
858 "Slide window content downwards",
859 wslide, { .i = +1 }
861 [VIS_ACTION_PUT_AFTER] = {
862 "put-after",
863 "Put text after the cursor",
864 operator, { .i = VIS_OP_PUT_AFTER }
866 [VIS_ACTION_PUT_BEFORE] = {
867 "put-before",
868 "Put text before the cursor",
869 operator, { .i = VIS_OP_PUT_BEFORE }
871 [VIS_ACTION_PUT_AFTER_END] = {
872 "put-after-end",
873 "Put text after the cursor, place cursor after new text",
874 operator, { .i = VIS_OP_PUT_AFTER_END }
876 [VIS_ACTION_PUT_BEFORE_END] = {
877 "put-before-end",
878 "Put text before the cursor, place cursor after new text",
879 operator, { .i = VIS_OP_PUT_BEFORE_END }
881 [VIS_ACTION_CURSOR_SELECT_WORD] = {
882 "cursors-select-word",
883 "Select word under cursor",
884 cursors_select,
886 [VIS_ACTION_CURSORS_NEW_LINE_ABOVE] = {
887 "cursors-new-lines-above",
888 "Create a new cursor on the line above",
889 cursors_new, { .i = -1 }
891 [VIS_ACTION_CURSORS_NEW_LINE_BELOW] = {
892 "cursor-new-lines-below",
893 "Create a new cursor on the line below",
894 cursors_new, { .i = +1 }
896 [VIS_ACTION_CURSORS_NEW_LINES_BEGIN] = {
897 "cursors-new-lines-begin",
898 "Create a new cursor at the start of every line covered by selection",
899 operator, { .i = VIS_OP_CURSOR_SOL }
901 [VIS_ACTION_CURSORS_NEW_LINES_END] = {
902 "cursors-new-lines-end",
903 "Create a new cursor at the end of every line covered by selection",
904 operator, { .i = VIS_OP_CURSOR_EOL }
906 [VIS_ACTION_CURSORS_NEW_MATCH_NEXT] = {
907 "cursors-new-match-next",
908 "Select the next region matching the current selection",
909 cursors_select_next
911 [VIS_ACTION_CURSORS_NEW_MATCH_SKIP] = {
912 "cursors-new-match-skip",
913 "Clear current selection, but select next match",
914 cursors_select_skip,
916 [VIS_ACTION_CURSORS_ALIGN] = {
917 "cursors-align",
918 "Try to align all cursors on the same column",
919 cursors_align,
921 [VIS_ACTION_CURSORS_ALIGN_INDENT] = {
922 "cursors-align-indent",
923 "Try to align all cursors by inserting spaces",
924 cursors_align_indent,
926 [VIS_ACTION_CURSORS_REMOVE_ALL] = {
927 "cursors-remove-all",
928 "Remove all but the primary cursor",
929 cursors_clear,
931 [VIS_ACTION_CURSORS_REMOVE_LAST] = {
932 "cursors-remove-last",
933 "Remove least recently created cursor",
934 cursors_remove,
936 [VIS_ACTION_CURSORS_PREV] = {
937 "cursors-prev",
938 "Move to the previous cursor",
939 cursors_navigate, { .i = -PAGE_HALF }
941 [VIS_ACTION_CURSORS_NEXT] = {
942 "cursors-next",
943 "Move to the next cursor",
944 cursors_navigate, { .i = +PAGE_HALF }
946 [VIS_ACTION_TEXT_OBJECT_WORD_OUTER] = {
947 "text-object-word-outer",
948 "A word leading and trailing whitespace included",
949 textobj, { .i = VIS_TEXTOBJECT_OUTER_WORD }
951 [VIS_ACTION_TEXT_OBJECT_WORD_INNER] = {
952 "text-object-word-inner",
953 "A word leading and trailing whitespace excluded",
954 textobj, { .i = VIS_TEXTOBJECT_INNER_WORD }
956 [VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER] = {
957 "text-object-longword-outer",
958 "A WORD leading and trailing whitespace included",
959 textobj, { .i = VIS_TEXTOBJECT_OUTER_LONGWORD }
961 [VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER] = {
962 "text-object-longword-inner",
963 "A WORD leading and trailing whitespace excluded",
964 textobj, { .i = VIS_TEXTOBJECT_INNER_LONGWORD }
966 [VIS_ACTION_TEXT_OBJECT_SENTENCE] = {
967 "text-object-sentence",
968 "A sentence",
969 textobj, { .i = VIS_TEXTOBJECT_SENTENCE }
971 [VIS_ACTION_TEXT_OBJECT_PARAGRAPH] = {
972 "text-object-paragraph",
973 "A paragraph",
974 textobj, { .i = VIS_TEXTOBJECT_PARAGRAPH }
976 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER] = {
977 "text-object-square-bracket-outer",
978 "[] block (outer variant)",
979 textobj, { .i = VIS_TEXTOBJECT_OUTER_SQUARE_BRACKET }
981 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER] = {
982 "text-object-square-bracket-inner",
983 "[] block (inner variant)",
984 textobj, { .i = VIS_TEXTOBJECT_INNER_SQUARE_BRACKET }
986 [VIS_ACTION_TEXT_OBJECT_PARANTHESE_OUTER] = {
987 "text-object-parentheses-outer",
988 "() block (outer variant)",
989 textobj, { .i = VIS_TEXTOBJECT_OUTER_PARANTHESE }
991 [VIS_ACTION_TEXT_OBJECT_PARANTHESE_INNER] = {
992 "text-object-parentheses-inner",
993 "() block (inner variant)",
994 textobj, { .i = VIS_TEXTOBJECT_INNER_PARANTHESE }
996 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER] = {
997 "text-object-angle-bracket-outer",
998 "<> block (outer variant)",
999 textobj, { .i = VIS_TEXTOBJECT_OUTER_ANGLE_BRACKET }
1001 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER] = {
1002 "text-object-angle-bracket-inner",
1003 "<> block (inner variant)",
1004 textobj, { .i = VIS_TEXTOBJECT_INNER_ANGLE_BRACKET }
1006 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER] = {
1007 "text-object-curly-bracket-outer",
1008 "{} block (outer variant)",
1009 textobj, { .i = VIS_TEXTOBJECT_OUTER_CURLY_BRACKET }
1011 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER] = {
1012 "text-object-curly-bracket-inner",
1013 "{} block (inner variant)",
1014 textobj, { .i = VIS_TEXTOBJECT_INNER_CURLY_BRACKET }
1016 [VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER] = {
1017 "text-object-quote-outer",
1018 "A quoted string, including the quotation marks",
1019 textobj, { .i = VIS_TEXTOBJECT_OUTER_QUOTE }
1021 [VIS_ACTION_TEXT_OBJECT_QUOTE_INNER] = {
1022 "text-object-quote-inner",
1023 "A quoted string, excluding the quotation marks",
1024 textobj, { .i = VIS_TEXTOBJECT_INNER_QUOTE }
1026 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER] = {
1027 "text-object-single-quote-outer",
1028 "A single quoted string, including the quotation marks",
1029 textobj, { .i = VIS_TEXTOBJECT_OUTER_SINGLE_QUOTE }
1031 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER] = {
1032 "text-object-single-quote-inner",
1033 "A single quoted string, excluding the quotation marks",
1034 textobj, { .i = VIS_TEXTOBJECT_INNER_SINGLE_QUOTE }
1036 [VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER] = {
1037 "text-object-backtick-outer",
1038 "A backtick delimited string (outer variant)",
1039 textobj, { .i = VIS_TEXTOBJECT_OUTER_BACKTICK }
1041 [VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER] = {
1042 "text-object-backtick-inner",
1043 "A backtick delimited string (inner variant)",
1044 textobj, { .i = VIS_TEXTOBJECT_INNER_BACKTICK }
1046 [VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER] = {
1047 "text-object-entire-outer",
1048 "The whole text content",
1049 textobj, { .i = VIS_TEXTOBJECT_OUTER_ENTIRE }
1051 [VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER] = {
1052 "text-object-entire-inner",
1053 "The whole text content, except for leading and trailing empty lines",
1054 textobj, { .i = VIS_TEXTOBJECT_INNER_ENTIRE }
1056 [VIS_ACTION_TEXT_OBJECT_FUNCTION_OUTER] = {
1057 "text-object-function-outer",
1058 "A whole C-like function",
1059 textobj, { .i = VIS_TEXTOBJECT_OUTER_FUNCTION }
1061 [VIS_ACTION_TEXT_OBJECT_FUNCTION_INNER] = {
1062 "text-object-function-inner",
1063 "A whole C-like function body",
1064 textobj, { .i = VIS_TEXTOBJECT_INNER_FUNCTION }
1066 [VIS_ACTION_TEXT_OBJECT_LINE_OUTER] = {
1067 "text-object-line-outer",
1068 "The whole line",
1069 textobj, { .i = VIS_TEXTOBJECT_OUTER_LINE }
1071 [VIS_ACTION_TEXT_OBJECT_LINE_INNER] = {
1072 "text-object-line-inner",
1073 "The whole line, excluding leading and trailing whitespace",
1074 textobj, { .i = VIS_TEXTOBJECT_INNER_LINE }
1076 [VIS_ACTION_TEXT_OBJECT_INDENTATION] = {
1077 "text-object-indentation",
1078 "All adjacent lines with the same indentation level as the current one",
1079 textobj, { .i = VIS_TEXTOBJECT_INDENTATION }
1081 [VIS_ACTION_TEXT_OBJECT_SEARCH_FORWARD] = {
1082 "text-object-search-forward",
1083 "The next search match in forward direction",
1084 textobj, { .i = VIS_TEXTOBJECT_SEARCH_FORWARD }
1086 [VIS_ACTION_TEXT_OBJECT_SEARCH_BACKWARD] = {
1087 "text-object-search-backward",
1088 "The next search match in backward direction",
1089 textobj, { .i = VIS_TEXTOBJECT_SEARCH_BACKWARD }
1091 [VIS_ACTION_MOTION_CHARWISE] = {
1092 "motion-charwise",
1093 "Force motion to be charwise",
1094 motiontype, { .i = VIS_MOTIONTYPE_CHARWISE }
1096 [VIS_ACTION_MOTION_LINEWISE] = {
1097 "motion-linewise",
1098 "Force motion to be linewise",
1099 motiontype, { .i = VIS_MOTIONTYPE_LINEWISE }
1101 [VIS_ACTION_UNICODE_INFO] = {
1102 "unicode-info",
1103 "Show Unicode codepoint(s) of character under cursor",
1104 unicode_info,
1106 [VIS_ACTION_NUMBER_INCREMENT] = {
1107 "number-increment",
1108 "Increment number under cursor",
1109 number_increment_decrement, { .i = +1 }
1111 [VIS_ACTION_NUMBER_DECREMENT] = {
1112 "number-decrement",
1113 "Decrement number under cursor",
1114 number_increment_decrement, { .i = -1 }
1116 [VIS_ACTION_OPEN_FILE_UNDER_CURSOR] = {
1117 "open-file-under-cursor",
1118 "Open file under the cursor",
1119 open_file_under_cursor, { .b = false }
1121 [VIS_ACTION_OPEN_FILE_UNDER_CURSOR_NEW_WINDOW] = {
1122 "open-file-under-cursor-new-cursor",
1123 "Open file under the cursor in a new window",
1124 open_file_under_cursor, { .b = true }
1126 [VIS_ACTION_NOP] = {
1127 "nop",
1128 "Ignore key, do nothing",
1129 nop,
1133 #include "config.h"
1135 /** key bindings functions */
1137 static const char *nop(Vis *vis, const char *keys, const Arg *arg) {
1138 return keys;
1141 static const char *key2register(Vis *vis, const char *keys, enum VisRegister *reg) {
1142 *reg = VIS_REG_INVALID;
1143 if (!keys[0])
1144 return NULL;
1145 if ('a' <= keys[0] && keys[0] <= 'z')
1146 *reg = keys[0] - 'a';
1147 else if ('A' <= keys[0] && keys[0] <= 'Z')
1148 *reg = VIS_REG_A + keys[0] - 'A';
1149 else if (keys[0] == '*' || keys[0] == '+')
1150 *reg = VIS_REG_CLIPBOARD;
1151 else if (keys[0] == '_')
1152 *reg = VIS_REG_BLACKHOLE;
1153 else if (keys[0] == '0')
1154 *reg = VIS_REG_ZERO;
1155 else if (keys[0] == '@')
1156 *reg = VIS_MACRO_LAST_RECORDED;
1157 else if (keys[0] == '/')
1158 *reg = VIS_REG_SEARCH;
1159 else if (keys[0] == ':')
1160 *reg = VIS_REG_COMMAND;
1161 return keys+1;
1164 static const char *macro_record(Vis *vis, const char *keys, const Arg *arg) {
1165 if (!vis_macro_record_stop(vis)) {
1166 enum VisRegister reg;
1167 keys = key2register(vis, keys, &reg);
1168 vis_macro_record(vis, reg);
1170 vis_draw(vis);
1171 return keys;
1174 static const char *macro_replay(Vis *vis, const char *keys, const Arg *arg) {
1175 enum VisRegister reg;
1176 keys = key2register(vis, keys, &reg);
1177 vis_macro_replay(vis, reg);
1178 return keys;
1181 static const char *suspend(Vis *vis, const char *keys, const Arg *arg) {
1182 vis_suspend(vis);
1183 return keys;
1186 static const char *repeat(Vis *vis, const char *keys, const Arg *arg) {
1187 vis_repeat(vis);
1188 return keys;
1191 static const char *cursors_new(Vis *vis, const char *keys, const Arg *arg) {
1192 View *view = vis_view(vis);
1193 size_t pos = view_cursor_get(view);
1194 Cursor *cursor = view_cursors_new(view);
1195 if (cursor) {
1196 view_cursors_to(cursor, pos);
1197 if (arg->i > 0)
1198 view_line_down(cursor);
1199 else if (arg->i < 0)
1200 view_line_up(cursor);
1202 return keys;
1205 static const char *cursors_align(Vis *vis, const char *keys, const Arg *arg) {
1206 View *view = vis_view(vis);
1207 Text *txt = vis_text(vis);
1208 int mincol = INT_MAX;
1209 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
1210 int col = view_cursors_cell_get(c);
1211 if (col >= 0 && col < mincol)
1212 mincol = col;
1214 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
1215 if (view_cursors_cell_set(c, mincol) == -1) {
1216 size_t pos = view_cursors_pos(c);
1217 size_t col = text_line_width_set(txt, pos, mincol);
1218 view_cursors_to(c, col);
1221 return keys;
1224 static const char *cursors_align_indent(Vis *vis, const char *keys, const Arg *arg) {
1225 View *view = vis_view(vis);
1226 Text *txt = vis_text(vis);
1227 int mincol = INT_MAX, maxcol = 0;
1229 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
1230 int col = text_line_width_get(txt, view_cursors_pos(c));
1231 if (col < mincol)
1232 mincol = col;
1233 if (col > maxcol)
1234 maxcol = col;
1237 size_t len = maxcol - mincol;
1238 char *buf = malloc(len+1);
1239 if (!buf)
1240 return keys;
1241 memset(buf, ' ', len);
1243 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
1244 size_t pos = view_cursors_pos(c);
1245 int col = text_line_width_get(txt, pos);
1246 if (col < maxcol) {
1247 size_t off = maxcol - col;
1248 if (off <= len)
1249 text_insert(txt, pos, buf, off);
1253 view_draw(view);
1254 free(buf);
1255 return keys;
1258 static const char *cursors_clear(Vis *vis, const char *keys, const Arg *arg) {
1259 View *view = vis_view(vis);
1260 if (view_cursors_multiple(view))
1261 view_cursors_clear(view);
1262 else
1263 view_cursors_selection_clear(view_cursors_primary_get(view));
1264 return keys;
1267 static const char *cursors_select(Vis *vis, const char *keys, const Arg *arg) {
1268 Text *txt = vis_text(vis);
1269 View *view = vis_view(vis);
1270 for (Cursor *cursor = view_cursors(view); cursor; cursor = view_cursors_next(cursor)) {
1271 Filerange sel = view_cursors_selection_get(cursor);
1272 Filerange word = text_object_word(txt, view_cursors_pos(cursor));
1273 if (!text_range_valid(&sel) && text_range_valid(&word)) {
1274 view_cursors_selection_set(cursor, &word);
1275 view_cursors_to(cursor, text_char_prev(txt, word.end));
1278 vis_mode_switch(vis, VIS_MODE_VISUAL);
1279 return keys;
1282 static const char *cursors_select_next(Vis *vis, const char *keys, const Arg *arg) {
1283 Text *txt = vis_text(vis);
1284 View *view = vis_view(vis);
1285 Cursor *cursor = view_cursors_primary_get(view);
1286 Filerange sel = view_cursors_selection_get(cursor);
1287 if (!text_range_valid(&sel))
1288 return keys;
1290 char *buf = text_bytes_alloc0(txt, sel.start, text_range_size(&sel));
1291 if (!buf)
1292 return keys;
1293 Filerange word = text_object_word_find_next(txt, sel.end, buf);
1294 free(buf);
1296 if (text_range_valid(&word)) {
1297 cursor = view_cursors_new(view);
1298 if (!cursor)
1299 return keys;
1300 view_cursors_selection_set(cursor, &word);
1301 view_cursors_to(cursor, text_char_prev(txt, word.end));
1303 return keys;
1306 static const char *cursors_select_skip(Vis *vis, const char *keys, const Arg *arg) {
1307 View *view = vis_view(vis);
1308 Cursor *cursor = view_cursors_primary_get(view);
1309 keys = cursors_select_next(vis, keys, arg);
1310 if (cursor != view_cursors_primary_get(view))
1311 view_cursors_dispose(cursor);
1312 return keys;
1315 static const char *cursors_remove(Vis *vis, const char *keys, const Arg *arg) {
1316 View *view = vis_view(vis);
1317 view_cursors_dispose(view_cursors_primary_get(view));
1318 view_cursor_to(view, view_cursor_get(view));
1319 return keys;
1322 static const char *cursors_navigate(Vis *vis, const char *keys, const Arg *arg) {
1323 View *view = vis_view(vis);
1324 if (!view_cursors_multiple(view))
1325 return wscroll(vis, keys, arg);
1326 Cursor *c = view_cursors_primary_get(view);
1327 for (int count = vis_count_get_default(vis, 1); count > 0; count--) {
1328 if (arg->i < 0) {
1329 c = view_cursors_next(c);
1330 if (!c)
1331 c = view_cursors(view);
1332 } else {
1333 c = view_cursors_prev(c);
1334 if (!c) {
1335 c = view_cursors(view);
1336 for (Cursor *n = c; n; n = view_cursors_next(n))
1337 c = n;
1341 view_cursors_primary_set(c);
1342 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1343 return keys;
1346 static const char *replace(Vis *vis, const char *keys, const Arg *arg) {
1347 if (!keys[0])
1348 return NULL;
1349 const char *next = vis_keys_next(vis, keys);
1350 if (!next)
1351 return NULL;
1352 size_t len = next - keys;
1353 char key[len+1];
1354 memcpy(key, keys, len);
1355 key[len] = '\0';
1356 vis_operator(vis, VIS_OP_REPLACE);
1357 vis_motion(vis, VIS_MOVE_NOP);
1358 vis_keys_inject(vis, next, key);
1359 vis_keys_inject(vis, next+len, "<Escape>");
1360 return next;
1363 static const char *count(Vis *vis, const char *keys, const Arg *arg) {
1364 int digit = keys[-1] - '0';
1365 int count = vis_count_get_default(vis, 0);
1366 if (0 <= digit && digit <= 9) {
1367 if (digit == 0 && count == 0)
1368 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
1369 vis_count_set(vis, count * 10 + digit);
1371 return keys;
1374 static const char *gotoline(Vis *vis, const char *keys, const Arg *arg) {
1375 if (vis_count_get(vis) != VIS_COUNT_UNKNOWN)
1376 vis_motion(vis, VIS_MOVE_LINE);
1377 else if (arg->i < 0)
1378 vis_motion(vis, VIS_MOVE_FILE_BEGIN);
1379 else
1380 vis_motion(vis, VIS_MOVE_FILE_END);
1381 return keys;
1384 static const char *motiontype(Vis *vis, const char *keys, const Arg *arg) {
1385 vis_motion_type(vis, arg->i);
1386 return keys;
1389 static const char *operator(Vis *vis, const char *keys, const Arg *arg) {
1390 vis_operator(vis, arg->i);
1391 return keys;
1394 static const char *operator_filter(Vis *vis, const char *keys, const Arg *arg) {
1395 vis_operator(vis, VIS_OP_FILTER, arg->s);
1396 return keys;
1399 static const char *movement_key(Vis *vis, const char *keys, const Arg *arg) {
1400 char key[32];
1401 const char *next;
1402 if (!keys[0] || !(next = vis_keys_next(vis, keys)))
1403 return NULL;
1404 size_t len = next - keys;
1405 if (len < sizeof key) {
1406 strncpy(key, keys, len);
1407 key[len] = '\0';
1408 vis_motion(vis, arg->i, key);
1410 return next;
1413 static const char *movement(Vis *vis, const char *keys, const Arg *arg) {
1414 vis_motion(vis, arg->i);
1415 return keys;
1418 static const char *textobj(Vis *vis, const char *keys, const Arg *arg) {
1419 vis_textobject(vis, arg->i);
1420 return keys;
1423 static const char *selection_end(Vis *vis, const char *keys, const Arg *arg) {
1424 for (Cursor *c = view_cursors(vis_view(vis)); c; c = view_cursors_next(c))
1425 view_cursors_selection_swap(c);
1426 return keys;
1429 static const char *selection_restore(Vis *vis, const char *keys, const Arg *arg) {
1430 Text *txt = vis_text(vis);
1431 View *view = vis_view(vis);
1432 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c))
1433 view_cursors_selection_restore(c);
1434 Filerange sel = view_selection_get(view);
1435 if (text_range_is_linewise(txt, &sel))
1436 vis_mode_switch(vis, VIS_MODE_VISUAL_LINE);
1437 else
1438 vis_mode_switch(vis, VIS_MODE_VISUAL);
1439 return keys;
1442 static const char *reg(Vis *vis, const char *keys, const Arg *arg) {
1443 enum VisRegister reg;
1444 keys = key2register(vis, keys, &reg);
1445 vis_register_set(vis, reg);
1446 return keys;
1449 static const char *key2mark(Vis *vis, const char *keys, int *mark) {
1450 *mark = VIS_MARK_INVALID;
1451 if (!keys[0])
1452 return NULL;
1453 if (keys[0] >= 'a' && keys[0] <= 'z')
1454 *mark = keys[0] - 'a';
1455 else if (keys[0] == '<')
1456 *mark = VIS_MARK_SELECTION_START;
1457 else if (keys[0] == '>')
1458 *mark = VIS_MARK_SELECTION_END;
1459 return keys+1;
1462 static const char *mark_set(Vis *vis, const char *keys, const Arg *arg) {
1463 int mark;
1464 keys = key2mark(vis, keys, &mark);
1465 vis_mark_set(vis, mark, view_cursor_get(vis_view(vis)));
1466 return keys;
1469 static const char *mark_motion(Vis *vis, const char *keys, const Arg *arg) {
1470 int mark;
1471 keys = key2mark(vis, keys, &mark);
1472 vis_motion(vis, arg->i, mark);
1473 return keys;
1476 static const char *undo(Vis *vis, const char *keys, const Arg *arg) {
1477 size_t pos = text_undo(vis_text(vis));
1478 if (pos != EPOS) {
1479 View *view = vis_view(vis);
1480 if (!view_cursors_multiple(view))
1481 view_cursor_to(view, pos);
1482 /* redraw all windows in case some display the same file */
1483 vis_draw(vis);
1485 return keys;
1488 static const char *redo(Vis *vis, const char *keys, const Arg *arg) {
1489 size_t pos = text_redo(vis_text(vis));
1490 if (pos != EPOS) {
1491 View *view = vis_view(vis);
1492 if (!view_cursors_multiple(view))
1493 view_cursor_to(view, pos);
1494 /* redraw all windows in case some display the same file */
1495 vis_draw(vis);
1497 return keys;
1500 static const char *earlier(Vis *vis, const char *keys, const Arg *arg) {
1501 size_t pos = text_earlier(vis_text(vis), vis_count_get_default(vis, 1));
1502 if (pos != EPOS) {
1503 view_cursor_to(vis_view(vis), pos);
1504 /* redraw all windows in case some display the same file */
1505 vis_draw(vis);
1507 return keys;
1510 static const char *later(Vis *vis, const char *keys, const Arg *arg) {
1511 size_t pos = text_later(vis_text(vis), vis_count_get_default(vis, 1));
1512 if (pos != EPOS) {
1513 view_cursor_to(vis_view(vis), pos);
1514 /* redraw all windows in case some display the same file */
1515 vis_draw(vis);
1517 return keys;
1520 static const char *delete(Vis *vis, const char *keys, const Arg *arg) {
1521 vis_operator(vis, VIS_OP_DELETE);
1522 vis_motion(vis, arg->i);
1523 return keys;
1526 static const char *insert_register(Vis *vis, const char *keys, const Arg *arg) {
1527 enum VisRegister regid;
1528 keys = key2register(vis, keys, &regid);
1529 size_t len;
1530 const char *data = vis_register_get(vis, regid, &len);
1531 vis_insert_key(vis, data, len);
1532 return keys;
1535 static const char *prompt_show(Vis *vis, const char *keys, const Arg *arg) {
1536 vis_prompt_show(vis, arg->s);
1537 return keys;
1540 static const char *insert_verbatim(Vis *vis, const char *keys, const Arg *arg) {
1541 Rune rune = 0;
1542 char buf[4], type = keys[0];
1543 const char *data = NULL;
1544 int len = 0, count = 0, base = 0;
1545 switch (type) {
1546 case '\0':
1547 return NULL;
1548 case 'o':
1549 case 'O':
1550 count = 3;
1551 base = 8;
1552 break;
1553 case 'U':
1554 count = 4;
1555 /* fall through */
1556 case 'u':
1557 count += 4;
1558 base = 16;
1559 break;
1560 case 'x':
1561 case 'X':
1562 count = 2;
1563 base = 16;
1564 break;
1565 default:
1566 if ('0' <= type && type <= '9') {
1567 rune = type - '0';
1568 count = 2;
1569 base = 10;
1571 break;
1574 if (base) {
1575 for (keys++; keys[0] && count > 0; keys++, count--) {
1576 int v = 0;
1577 if (base == 8 && '0' <= keys[0] && keys[0] <= '7') {
1578 v = keys[0] - '0';
1579 } else if ((base == 10 || base == 16) && '0' <= keys[0] && keys[0] <= '9') {
1580 v = keys[0] - '0';
1581 } else if (base == 16 && 'a' <= keys[0] && keys[0] <= 'f') {
1582 v = 10 + keys[0] - 'a';
1583 } else if (base == 16 && 'A' <= keys[0] && keys[0] <= 'F') {
1584 v = 10 + keys[0] - 'A';
1585 } else {
1586 count = 0;
1587 break;
1589 rune = rune * base + v;
1592 if (count > 0)
1593 return NULL;
1594 if (type == 'u' || type == 'U') {
1595 len = runetochar(buf, &rune);
1596 } else {
1597 buf[0] = rune;
1598 len = 1;
1601 data = buf;
1602 } else {
1603 const char *next = vis_keys_next(vis, keys);
1604 if (!next)
1605 return NULL;
1606 size_t keylen = next - keys;
1607 char key[keylen+1];
1608 memcpy(key, keys, keylen);
1609 key[keylen] = '\0';
1611 static const char *keysym[] = {
1612 "<Enter>", "\n",
1613 "<Tab>", "\t",
1614 "<Backspace>", "\b",
1615 "<Escape>", "\x1b",
1616 "<DEL>", "\x7f",
1617 NULL,
1620 for (const char **k = keysym; k[0]; k += 2) {
1621 if (strcmp(k[0], key) == 0) {
1622 data = k[1];
1623 len = strlen(data);
1624 keys = next;
1625 break;
1630 if (len > 0)
1631 vis_insert_key(vis, data, len);
1632 return keys;
1635 static int argi2lines(Vis *vis, const Arg *arg) {
1636 int count = vis_count_get(vis);
1637 switch (arg->i) {
1638 case -PAGE:
1639 case +PAGE:
1640 return view_height_get(vis_view(vis));
1641 case -PAGE_HALF:
1642 case +PAGE_HALF:
1643 return view_height_get(vis_view(vis))/2;
1644 default:
1645 if (count != VIS_COUNT_UNKNOWN)
1646 return count;
1647 return arg->i < 0 ? -arg->i : arg->i;
1651 static const char *wscroll(Vis *vis, const char *keys, const Arg *arg) {
1652 if (arg->i >= 0)
1653 view_scroll_down(vis_view(vis), argi2lines(vis, arg));
1654 else
1655 view_scroll_up(vis_view(vis), argi2lines(vis, arg));
1656 return keys;
1659 static const char *wslide(Vis *vis, const char *keys, const Arg *arg) {
1660 if (arg->i >= 0)
1661 view_slide_down(vis_view(vis), argi2lines(vis, arg));
1662 else
1663 view_slide_up(vis_view(vis), argi2lines(vis, arg));
1664 return keys;
1667 static const char *call(Vis *vis, const char *keys, const Arg *arg) {
1668 arg->f(vis);
1669 return keys;
1672 static const char *window(Vis *vis, const char *keys, const Arg *arg) {
1673 arg->w(vis_view(vis));
1674 return keys;
1677 static const char *openline(Vis *vis, const char *keys, const Arg *arg) {
1678 vis_operator(vis, VIS_OP_INSERT);
1679 if (arg->i > 0) {
1680 vis_motion(vis, VIS_MOVE_LINE_END);
1681 vis_keys_inject(vis, keys, "<insert-newline>");
1682 } else {
1683 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
1684 vis_keys_inject(vis, keys, "<insert-newline><Up>");
1686 return keys;
1689 static const char *join(Vis *vis, const char *keys, const Arg *arg) {
1690 int count = vis_count_get_default(vis, 0);
1691 if (count)
1692 vis_count_set(vis, count-1);
1693 vis_operator(vis, VIS_OP_JOIN);
1694 vis_motion(vis, arg->i);
1695 return keys;
1698 static const char *switchmode(Vis *vis, const char *keys, const Arg *arg) {
1699 vis_mode_switch(vis, arg->i);
1700 return keys;
1703 static const char *insertmode(Vis *vis, const char *keys, const Arg *arg) {
1704 vis_operator(vis, VIS_OP_INSERT);
1705 vis_motion(vis, arg->i);
1706 return keys;
1709 static const char *unicode_info(Vis *vis, const char *keys, const Arg *arg) {
1710 View *view = vis_view(vis);
1711 Text *txt = vis_text(vis);
1712 size_t start = view_cursor_get(view);
1713 size_t end = text_char_next(txt, start);
1714 char data[end-start], *data_cur = data;
1715 text_bytes_get(txt, start, end - start, data);
1716 Iterator it = text_iterator_get(txt, start);
1717 char info[255] = "", *info_cur = info;
1718 for (size_t pos = start; it.pos < end; pos = it.pos) {
1719 text_iterator_codepoint_next(&it, NULL);
1720 size_t len = it.pos - pos;
1721 wchar_t wc = 0xFFFD;
1722 mbtowc(&wc, data_cur, len);
1723 int width = wcwidth(wc);
1724 info_cur += snprintf(info_cur, sizeof(info) - (info_cur - info) - 1,
1725 "<%s%.*s> U+%04x ", width == 0 ? " " : "", (int)len, data_cur, wc);
1726 data_cur += len;
1728 vis_info_show(vis, "%s", info);
1729 return keys;
1732 static const char *percent(Vis *vis, const char *keys, const Arg *arg) {
1733 if (vis_count_get(vis) == VIS_COUNT_UNKNOWN)
1734 vis_motion(vis, VIS_MOVE_BRACKET_MATCH);
1735 else
1736 vis_motion(vis, VIS_MOVE_PERCENT);
1737 return keys;
1740 static const char *number_increment_decrement(Vis *vis, const char *keys, const Arg *arg) {
1741 View *view = vis_view(vis);
1742 Text *txt = vis_text(vis);
1744 int delta = arg->i;
1745 int count = vis_count_get(vis);
1746 if (count != 0 && count != VIS_COUNT_UNKNOWN)
1747 delta *= count;
1749 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
1750 Filerange r = text_object_number(txt, view_cursors_pos(c));
1751 if (!text_range_valid(&r))
1752 continue;
1753 char *buf = text_bytes_alloc0(txt, r.start, text_range_size(&r));
1754 if (buf) {
1755 char *number = buf, fmt[255];
1756 if (number[0] == '-')
1757 number++;
1758 bool octal = number[0] == '0' && ('0' <= number[1] && number[1] <= '7');
1759 bool hex = number[0] == '0' && (number[1] == 'x' || number[1] == 'X');
1760 bool dec = !hex && !octal;
1762 long long value = strtoll(buf, NULL, 0);
1763 value += delta;
1764 if (dec) {
1765 snprintf(fmt, sizeof fmt, "%lld", value);
1766 } else if (hex) {
1767 size_t len = strlen(number) - 2;
1768 snprintf(fmt, sizeof fmt, "0x%0*llx", (int)len, value);
1769 } else {
1770 size_t len = strlen(number) - 1;
1771 snprintf(fmt, sizeof fmt, "0%0*llo", (int)len, value);
1773 text_delete_range(txt, &r);
1774 text_insert(txt, r.start, fmt, strlen(fmt));
1775 view_cursors_to(c, r.start);
1777 free(buf);
1780 vis_cancel(vis);
1782 return keys;
1785 static const char *open_file_under_cursor(Vis *vis, const char *keys, const Arg *arg) {
1786 Win *win = vis_window(vis);
1787 View *view = vis_view(vis);
1788 Text *txt = vis_text(vis);
1790 if (!arg->b && !vis_window_closable(win)) {
1791 vis_info_show(vis, "No write since last change");
1792 return keys;
1795 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
1796 Filerange r = text_object_filename(txt, view_cursors_pos(c));
1797 if (!text_range_valid(&r))
1798 continue;
1799 char *name = text_bytes_alloc0(txt, r.start, text_range_size(&r));
1800 if (!name)
1801 continue;
1803 struct stat st;
1804 if (stat(name, &st) == -1) {
1805 vis_info_show(vis, "File `%s' not found", name);
1806 free(name);
1807 continue;
1810 if (!vis_window_new(vis, name)) {
1811 vis_info_show(vis, "Failed to open `%s': %s", name, strerror(errno));
1812 free(name);
1813 continue;
1814 } else if (!arg->b) {
1815 vis_window_close(win);
1816 free(name);
1817 return keys;
1820 free(name);
1823 return keys;
1826 static Vis *vis;
1828 static void signal_handler(int signum, siginfo_t *siginfo, void *context) {
1829 vis_signal_handler(vis, signum, siginfo, context);
1832 int main(int argc, char *argv[]) {
1834 VisEvent event = {
1835 .vis_start = vis_lua_start,
1836 .vis_quit = vis_lua_quit,
1837 .file_open = vis_lua_file_open,
1838 .file_save = vis_lua_file_save,
1839 .file_close = vis_lua_file_close,
1840 .win_open = vis_lua_win_open,
1841 .win_close = vis_lua_win_close,
1844 vis = vis_new(ui_curses_new(), &event);
1845 if (!vis)
1846 return EXIT_FAILURE;
1848 for (int i = 0; i < LENGTH(vis_action); i++) {
1849 const KeyAction *action = &vis_action[i];
1850 if (!vis_action_register(vis, action))
1851 vis_die(vis, "Could not register action: %s\n", action->name);
1854 for (int i = 0; i < LENGTH(default_bindings); i++) {
1855 for (const KeyBinding **binding = default_bindings[i]; binding && *binding; binding++) {
1856 for (const KeyBinding *kb = *binding; kb->key; kb++) {
1857 vis_mode_map(vis, i, kb->key, kb);
1862 for (const char **k = keymaps; k[0]; k += 2)
1863 vis_keymap_add(vis, k[0], k[1]);
1865 /* install signal handlers etc. */
1866 struct sigaction sa;
1867 memset(&sa, 0, sizeof sa);
1868 sa.sa_flags = SA_SIGINFO;
1869 sa.sa_sigaction = signal_handler;
1870 if (sigaction(SIGBUS, &sa, NULL) || sigaction(SIGINT, &sa, NULL))
1871 vis_die(vis, "sigaction: %s", strerror(errno));
1873 sigset_t blockset;
1874 sigemptyset(&blockset);
1875 sigaddset(&blockset, SIGWINCH);
1876 sigprocmask(SIG_BLOCK, &blockset, NULL);
1877 signal(SIGPIPE, SIG_IGN);
1879 int status = vis_run(vis, argc, argv);
1880 vis_free(vis);
1881 return status;