README: add a paragraph about distribution packages
[vis.git] / main.c
bloba5fc2d38fdb0bb9b964e83c2c3a7a90db2a2348e
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_ABOVE_FIRST,
241 VIS_ACTION_CURSORS_NEW_LINE_BELOW,
242 VIS_ACTION_CURSORS_NEW_LINE_BELOW_LAST,
243 VIS_ACTION_CURSORS_NEW_LINES_BEGIN,
244 VIS_ACTION_CURSORS_NEW_LINES_END,
245 VIS_ACTION_CURSORS_NEW_MATCH_NEXT,
246 VIS_ACTION_CURSORS_NEW_MATCH_SKIP,
247 VIS_ACTION_CURSORS_ALIGN,
248 VIS_ACTION_CURSORS_ALIGN_INDENT_LEFT,
249 VIS_ACTION_CURSORS_ALIGN_INDENT_RIGHT,
250 VIS_ACTION_CURSORS_REMOVE_ALL,
251 VIS_ACTION_CURSORS_REMOVE_LAST,
252 VIS_ACTION_CURSORS_PREV,
253 VIS_ACTION_CURSORS_NEXT,
254 VIS_ACTION_TEXT_OBJECT_WORD_OUTER,
255 VIS_ACTION_TEXT_OBJECT_WORD_INNER,
256 VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER,
257 VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER,
258 VIS_ACTION_TEXT_OBJECT_SENTENCE,
259 VIS_ACTION_TEXT_OBJECT_PARAGRAPH,
260 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER,
261 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER,
262 VIS_ACTION_TEXT_OBJECT_PARANTHESE_OUTER,
263 VIS_ACTION_TEXT_OBJECT_PARANTHESE_INNER,
264 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER,
265 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER,
266 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER,
267 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER,
268 VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER,
269 VIS_ACTION_TEXT_OBJECT_QUOTE_INNER,
270 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER,
271 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER,
272 VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER,
273 VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER,
274 VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER,
275 VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER,
276 VIS_ACTION_TEXT_OBJECT_FUNCTION_OUTER,
277 VIS_ACTION_TEXT_OBJECT_FUNCTION_INNER,
278 VIS_ACTION_TEXT_OBJECT_LINE_OUTER,
279 VIS_ACTION_TEXT_OBJECT_LINE_INNER,
280 VIS_ACTION_TEXT_OBJECT_INDENTATION,
281 VIS_ACTION_TEXT_OBJECT_SEARCH_FORWARD,
282 VIS_ACTION_TEXT_OBJECT_SEARCH_BACKWARD,
283 VIS_ACTION_MOTION_CHARWISE,
284 VIS_ACTION_MOTION_LINEWISE,
285 VIS_ACTION_UNICODE_INFO,
286 VIS_ACTION_NUMBER_INCREMENT,
287 VIS_ACTION_NUMBER_DECREMENT,
288 VIS_ACTION_OPEN_FILE_UNDER_CURSOR,
289 VIS_ACTION_OPEN_FILE_UNDER_CURSOR_NEW_WINDOW,
290 VIS_ACTION_NOP,
293 static const KeyAction vis_action[] = {
294 [VIS_ACTION_EDITOR_SUSPEND] = {
295 "editor-suspend",
296 "Suspend the editor",
297 suspend,
299 [VIS_ACTION_CURSOR_CHAR_PREV] = {
300 "cursor-char-prev",
301 "Move cursor left, to the previous character",
302 movement, { .i = VIS_MOVE_CHAR_PREV }
304 [VIS_ACTION_CURSOR_CHAR_NEXT] = {
305 "cursor-char-next",
306 "Move cursor right, to the next character",
307 movement, { .i = VIS_MOVE_CHAR_NEXT }
309 [VIS_ACTION_CURSOR_WORD_START_PREV] = {
310 "cursor-word-start-prev",
311 "Move cursor words backwards",
312 movement, { .i = VIS_MOVE_WORD_START_PREV }
314 [VIS_ACTION_CURSOR_WORD_START_NEXT] = {
315 "cursor-word-start-next",
316 "Move cursor words forwards",
317 movement, { .i = VIS_MOVE_WORD_START_NEXT }
319 [VIS_ACTION_CURSOR_WORD_END_PREV] = {
320 "cursor-word-end-prev",
321 "Move cursor backwards to the end of word",
322 movement, { .i = VIS_MOVE_WORD_END_PREV }
324 [VIS_ACTION_CURSOR_WORD_END_NEXT] = {
325 "cursor-word-end-next",
326 "Move cursor forward to the end of word",
327 movement, { .i = VIS_MOVE_WORD_END_NEXT }
329 [VIS_ACTION_CURSOR_LONGWORD_START_PREV] = {
330 "cursor-longword-start-prev",
331 "Move cursor WORDS backwards",
332 movement, { .i = VIS_MOVE_LONGWORD_START_PREV }
334 [VIS_ACTION_CURSOR_LONGWORD_START_NEXT] = {
335 "cursor-longword-start-next",
336 "Move cursor WORDS forwards",
337 movement, { .i = VIS_MOVE_LONGWORD_START_NEXT }
339 [VIS_ACTION_CURSOR_LONGWORD_END_PREV] = {
340 "cursor-longword-end-prev",
341 "Move cursor backwards to the end of WORD",
342 movement, { .i = VIS_MOVE_LONGWORD_END_PREV }
344 [VIS_ACTION_CURSOR_LONGWORD_END_NEXT] = {
345 "cursor-longword-end-next",
346 "Move cursor forward to the end of WORD",
347 movement, { .i = VIS_MOVE_LONGWORD_END_NEXT }
349 [VIS_ACTION_CURSOR_LINE_UP] = {
350 "cursor-line-up",
351 "Move cursor line upwards",
352 movement, { .i = VIS_MOVE_LINE_UP }
354 [VIS_ACTION_CURSOR_LINE_DOWN] = {
355 "cursor-line-down",
356 "Move cursor line downwards",
357 movement, { .i = VIS_MOVE_LINE_DOWN }
359 [VIS_ACTION_CURSOR_LINE_START] = {
360 "cursor-line-start",
361 "Move cursor to first non-blank character of the line",
362 movement, { .i = VIS_MOVE_LINE_START }
364 [VIS_ACTION_CURSOR_LINE_FINISH] = {
365 "cursor-line-finish",
366 "Move cursor to last non-blank character of the line",
367 movement, { .i = VIS_MOVE_LINE_FINISH }
369 [VIS_ACTION_CURSOR_LINE_BEGIN] = {
370 "cursor-line-begin",
371 "Move cursor to first character of the line",
372 movement, { .i = VIS_MOVE_LINE_BEGIN }
374 [VIS_ACTION_CURSOR_LINE_END] = {
375 "cursor-line-end",
376 "Move cursor to end of the line",
377 movement, { .i = VIS_MOVE_LINE_END }
379 [VIS_ACTION_CURSOR_LINE_LASTCHAR] = {
380 "cursor-line-lastchar",
381 "Move cursor to last character of the line",
382 movement, { .i = VIS_MOVE_LINE_LASTCHAR }
384 [VIS_ACTION_CURSOR_SCREEN_LINE_UP] = {
385 "cursor-sceenline-up",
386 "Move cursor screen/display line upwards",
387 movement, { .i = VIS_MOVE_SCREEN_LINE_UP }
389 [VIS_ACTION_CURSOR_SCREEN_LINE_DOWN] = {
390 "cursor-screenline-down",
391 "Move cursor screen/display line downwards",
392 movement, { .i = VIS_MOVE_SCREEN_LINE_DOWN }
394 [VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN] = {
395 "cursor-screenline-begin",
396 "Move cursor to beginning of screen/display line",
397 movement, { .i = VIS_MOVE_SCREEN_LINE_BEGIN }
399 [VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE] = {
400 "cursor-screenline-middle",
401 "Move cursor to middle of screen/display line",
402 movement, { .i = VIS_MOVE_SCREEN_LINE_MIDDLE }
404 [VIS_ACTION_CURSOR_SCREEN_LINE_END] = {
405 "cursor-screenline-end",
406 "Move cursor to end of screen/display line",
407 movement, { .i = VIS_MOVE_SCREEN_LINE_END }
409 [VIS_ACTION_CURSOR_PERCENT] = {
410 "cursor-percent",
411 "Move to count % of file or matching item",
412 percent
414 [VIS_ACTION_CURSOR_PARAGRAPH_PREV] = {
415 "cursor-paragraph-prev",
416 "Move cursor paragraph backward",
417 movement, { .i = VIS_MOVE_PARAGRAPH_PREV }
419 [VIS_ACTION_CURSOR_PARAGRAPH_NEXT] = {
420 "cursor-paragraph-next",
421 "Move cursor paragraph forward",
422 movement, { .i = VIS_MOVE_PARAGRAPH_NEXT }
424 [VIS_ACTION_CURSOR_SENTENCE_PREV] = {
425 "cursor-sentence-prev",
426 "Move cursor sentence backward",
427 movement, { .i = VIS_MOVE_SENTENCE_PREV }
429 [VIS_ACTION_CURSOR_SENTENCE_NEXT] = {
430 "cursor-sentence-next",
431 "Move cursor sentence forward",
432 movement, { .i = VIS_MOVE_SENTENCE_NEXT }
434 [VIS_ACTION_CURSOR_FUNCTION_START_PREV] = {
435 "cursor-function-start-prev",
436 "Move cursor backwards to start of function",
437 movement, { .i = VIS_MOVE_FUNCTION_START_PREV }
439 [VIS_ACTION_CURSOR_FUNCTION_START_NEXT] = {
440 "cursor-function-start-next",
441 "Move cursor forwards to start of function",
442 movement, { .i = VIS_MOVE_FUNCTION_START_NEXT }
444 [VIS_ACTION_CURSOR_FUNCTION_END_PREV] = {
445 "cursor-function-end-prev",
446 "Move cursor backwards to end of function",
447 movement, { .i = VIS_MOVE_FUNCTION_END_PREV }
449 [VIS_ACTION_CURSOR_FUNCTION_END_NEXT] = {
450 "cursor-function-end-next",
451 "Move cursor forwards to end of function",
452 movement, { .i = VIS_MOVE_FUNCTION_END_NEXT }
454 [VIS_ACTION_CURSOR_COLUMN] = {
455 "cursor-column",
456 "Move cursor to given column of current line",
457 movement, { .i = VIS_MOVE_COLUMN }
459 [VIS_ACTION_CURSOR_LINE_FIRST] = {
460 "cursor-line-first",
461 "Move cursor to given line (defaults to first)",
462 gotoline, { .i = -1 }
464 [VIS_ACTION_CURSOR_LINE_LAST] = {
465 "cursor-line-last",
466 "Move cursor to given line (defaults to last)",
467 gotoline, { .i = +1 }
469 [VIS_ACTION_CURSOR_WINDOW_LINE_TOP] = {
470 "cursor-window-line-top",
471 "Move cursor to top line of the window",
472 movement, { .i = VIS_MOVE_WINDOW_LINE_TOP }
474 [VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE] = {
475 "cursor-window-line-middle",
476 "Move cursor to middle line of the window",
477 movement, { .i = VIS_MOVE_WINDOW_LINE_MIDDLE }
479 [VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM] = {
480 "cursor-window-line-bottom",
481 "Move cursor to bottom line of the window",
482 movement, { .i = VIS_MOVE_WINDOW_LINE_BOTTOM }
484 [VIS_ACTION_CURSOR_SEARCH_NEXT] = {
485 "cursor-search-forward",
486 "Move cursor to bottom line of the window",
487 movement, { .i = VIS_MOVE_SEARCH_NEXT }
489 [VIS_ACTION_CURSOR_SEARCH_PREV] = {
490 "cursor-search-backward",
491 "Move cursor to bottom line of the window",
492 movement, { .i = VIS_MOVE_SEARCH_PREV }
494 [VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD] = {
495 "cursor-search-word-forward",
496 "Move cursor to next occurence of the word under cursor",
497 movement, { .i = VIS_MOVE_SEARCH_WORD_FORWARD }
499 [VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD] = {
500 "cursor-search-word-backward",
501 "Move cursor to previous occurence of the word under cursor",
502 movement, { .i = VIS_MOVE_SEARCH_WORD_BACKWARD }
504 [VIS_ACTION_WINDOW_PAGE_UP] = {
505 "window-page-up",
506 "Scroll window pages backwards (upwards)",
507 wscroll, { .i = -PAGE }
509 [VIS_ACTION_WINDOW_HALFPAGE_UP] = {
510 "window-halfpage-up",
511 "Scroll window half pages backwards (upwards)",
512 wscroll, { .i = -PAGE_HALF }
514 [VIS_ACTION_WINDOW_PAGE_DOWN] = {
515 "window-page-down",
516 "Scroll window pages forwards (downwards)",
517 wscroll, { .i = +PAGE }
519 [VIS_ACTION_WINDOW_HALFPAGE_DOWN] = {
520 "window-halfpage-down",
521 "Scroll window half pages forwards (downwards)",
522 wscroll, { .i = +PAGE_HALF }
524 [VIS_ACTION_MODE_NORMAL] = {
525 "vis-mode-normal",
526 "Enter normal mode",
527 switchmode, { .i = VIS_MODE_NORMAL }
529 [VIS_ACTION_MODE_VISUAL] = {
530 "vis-mode-visual-charwise",
531 "Enter characterwise visual mode",
532 switchmode, { .i = VIS_MODE_VISUAL }
534 [VIS_ACTION_MODE_VISUAL_LINE] = {
535 "vis-mode-visual-linewise",
536 "Enter linewise visual mode",
537 switchmode, { .i = VIS_MODE_VISUAL_LINE }
539 [VIS_ACTION_MODE_INSERT] = {
540 "vis-mode-insert",
541 "Enter insert mode",
542 switchmode, { .i = VIS_MODE_INSERT }
544 [VIS_ACTION_MODE_REPLACE] = {
545 "vis-mode-replace",
546 "Enter replace mode",
547 switchmode, { .i = VIS_MODE_REPLACE }
549 [VIS_ACTION_MODE_OPERATOR_PENDING] = {
550 "vis-mode-operator-pending",
551 "Enter to operator pending mode",
552 switchmode, { .i = VIS_MODE_OPERATOR_PENDING }
554 [VIS_ACTION_DELETE_CHAR_PREV] = {
555 "delete-char-prev",
556 "Delete the previous character",
557 delete, { .i = VIS_MOVE_CHAR_PREV }
559 [VIS_ACTION_DELETE_CHAR_NEXT] = {
560 "delete-char-next",
561 "Delete the next character",
562 delete, { .i = VIS_MOVE_CHAR_NEXT }
564 [VIS_ACTION_DELETE_LINE_BEGIN] = {
565 "delete-line-begin",
566 "Delete until the start of the current line",
567 delete, { .i = VIS_MOVE_LINE_BEGIN }
569 [VIS_ACTION_DELETE_WORD_PREV] = {
570 "delete-word-prev",
571 "Delete the previous WORD",
572 delete, { .i = VIS_MOVE_LONGWORD_START_PREV }
574 [VIS_ACTION_JUMPLIST_PREV] = {
575 "jumplist-prev",
576 "Go to older cursor position in jump list",
577 movement, { .i = VIS_MOVE_JUMPLIST_PREV }
579 [VIS_ACTION_JUMPLIST_NEXT] = {
580 "jumplist-next",
581 "Go to newer cursor position in jump list",
582 movement, { .i = VIS_MOVE_JUMPLIST_NEXT }
584 [VIS_ACTION_CHANGELIST_PREV] = {
585 "changelist-prev",
586 "Go to older cursor position in change list",
587 movement, { .i = VIS_MOVE_CHANGELIST_PREV }
589 [VIS_ACTION_CHANGELIST_NEXT] = {
590 "changelist-next",
591 "Go to newer cursor position in change list",
592 movement, { .i = VIS_MOVE_CHANGELIST_NEXT }
594 [VIS_ACTION_UNDO] = {
595 "editor-undo",
596 "Undo last change",
597 undo,
599 [VIS_ACTION_REDO] = {
600 "editor-redo",
601 "Redo last change",
602 redo,
604 [VIS_ACTION_EARLIER] = {
605 "editor-earlier",
606 "Goto older text state",
607 earlier,
609 [VIS_ACTION_LATER] = {
610 "editor-later",
611 "Goto newer text state",
612 later,
614 [VIS_ACTION_MACRO_RECORD] = {
615 "macro-record",
616 "Record macro into given register",
617 macro_record,
619 [VIS_ACTION_MACRO_REPLAY] = {
620 "macro-replay",
621 "Replay macro, execute the content of the given register",
622 macro_replay,
624 [VIS_ACTION_MARK_SET] = {
625 "mark-set",
626 "Set given mark at current cursor position",
627 mark_set,
629 [VIS_ACTION_MARK_GOTO] = {
630 "mark-goto",
631 "Goto the position of the given mark",
632 mark_motion, { .i = VIS_MOVE_MARK }
634 [VIS_ACTION_MARK_GOTO_LINE] = {
635 "mark-goto-line",
636 "Goto first non-blank character of the line containing the given mark",
637 mark_motion, { .i = VIS_MOVE_MARK_LINE }
639 [VIS_ACTION_REDRAW] = {
640 "editor-redraw",
641 "Redraw current editor content",
642 call, { .f = vis_redraw }
644 [VIS_ACTION_REPLACE_CHAR] = {
645 "replace-char",
646 "Replace the character under the cursor",
647 replace,
649 [VIS_ACTION_TOTILL_REPEAT] = {
650 "totill-repeat",
651 "Repeat latest to/till motion",
652 movement, { .i = VIS_MOVE_TOTILL_REPEAT }
654 [VIS_ACTION_TOTILL_REVERSE] = {
655 "totill-reverse",
656 "Repeat latest to/till motion but in opposite direction",
657 movement, { .i = VIS_MOVE_TOTILL_REVERSE }
659 [VIS_ACTION_PROMPT_SEARCH_FORWARD] = {
660 "search-forward",
661 "Search forward",
662 prompt_show, { .s = "/" }
664 [VIS_ACTION_PROMPT_SEARCH_BACKWARD] = {
665 "search-backward",
666 "Search backward",
667 prompt_show, { .s = "?" }
669 [VIS_ACTION_TILL_LEFT] = {
670 "till-left",
671 "Till after the occurrence of character to the left",
672 movement_key, { .i = VIS_MOVE_LEFT_TILL }
674 [VIS_ACTION_TILL_RIGHT] = {
675 "till-right",
676 "Till before the occurrence of character to the right",
677 movement_key, { .i = VIS_MOVE_RIGHT_TILL }
679 [VIS_ACTION_TO_LEFT] = {
680 "to-left",
681 "To the first occurrence of character to the left",
682 movement_key, { .i = VIS_MOVE_LEFT_TO }
684 [VIS_ACTION_TO_RIGHT] = {
685 "to-right",
686 "To the first occurrence of character to the right",
687 movement_key, { .i = VIS_MOVE_RIGHT_TO }
689 [VIS_ACTION_REGISTER] = {
690 "register",
691 "Use given register for next operator",
692 reg,
694 [VIS_ACTION_OPERATOR_CHANGE] = {
695 "vis-operator-change",
696 "Change operator",
697 operator, { .i = VIS_OP_CHANGE }
699 [VIS_ACTION_OPERATOR_DELETE] = {
700 "vis-operator-delete",
701 "Delete operator",
702 operator, { .i = VIS_OP_DELETE }
704 [VIS_ACTION_OPERATOR_YANK] = {
705 "vis-operator-yank",
706 "Yank operator",
707 operator, { .i = VIS_OP_YANK }
709 [VIS_ACTION_OPERATOR_SHIFT_LEFT] = {
710 "vis-operator-shift-left",
711 "Shift left operator",
712 operator, { .i = VIS_OP_SHIFT_LEFT }
714 [VIS_ACTION_OPERATOR_SHIFT_RIGHT] = {
715 "vis-operator-shift-right",
716 "Shift right operator",
717 operator, { .i = VIS_OP_SHIFT_RIGHT }
719 [VIS_ACTION_OPERATOR_CASE_LOWER] = {
720 "vis-operator-case-lower",
721 "Lowercase operator",
722 operator, { .i = VIS_OP_CASE_LOWER }
724 [VIS_ACTION_OPERATOR_CASE_UPPER] = {
725 "vis-operator-case-upper",
726 "Uppercase operator",
727 operator, { .i = VIS_OP_CASE_UPPER }
729 [VIS_ACTION_OPERATOR_CASE_SWAP] = {
730 "vis-operator-case-swap",
731 "Swap case operator",
732 operator, { .i = VIS_OP_CASE_SWAP }
734 [VIS_ACTION_OPERATOR_FILTER] = {
735 "vis-operator-filter",
736 "Filter operator",
737 operator_filter,
739 [VIS_ACTION_OPERATOR_FILTER_FMT] = {
740 "vis-operator-filter-format",
741 "Formating operator, filter range through fmt(1)",
742 operator_filter, { .s = "'<,'>!fmt" }
744 [VIS_ACTION_COUNT] = {
745 "vis-count",
746 "Count specifier",
747 count,
749 [VIS_ACTION_INSERT_NEWLINE] = {
750 "insert-newline",
751 "Insert a line break (depending on file type)",
752 call, { .f = vis_insert_nl }
754 [VIS_ACTION_INSERT_TAB] = {
755 "insert-tab",
756 "Insert a tab (might be converted to spaces)",
757 call, { .f = vis_insert_tab }
759 [VIS_ACTION_INSERT_VERBATIM] = {
760 "insert-verbatim",
761 "Insert Unicode character based on code point",
762 insert_verbatim,
764 [VIS_ACTION_INSERT_REGISTER] = {
765 "insert-register",
766 "Insert specified register content",
767 insert_register,
769 [VIS_ACTION_WINDOW_NEXT] = {
770 "window-next",
771 "Focus next window",
772 call, { .f = vis_window_next }
774 [VIS_ACTION_WINDOW_PREV] = {
775 "window-prev",
776 "Focus previous window",
777 call, { .f = vis_window_prev }
779 [VIS_ACTION_APPEND_CHAR_NEXT] = {
780 "append-char-next",
781 "Append text after the cursor",
782 insertmode, { .i = VIS_MOVE_CHAR_NEXT }
784 [VIS_ACTION_APPEND_LINE_END] = {
785 "append-line-end",
786 "Append text after the end of the line",
787 insertmode, { .i = VIS_MOVE_LINE_END },
789 [VIS_ACTION_INSERT_LINE_START] = {
790 "insert-line-start",
791 "Insert text before the first non-blank in the line",
792 insertmode, { .i = VIS_MOVE_LINE_START },
794 [VIS_ACTION_OPEN_LINE_ABOVE] = {
795 "open-line-above",
796 "Begin a new line above the cursor",
797 openline, { .i = -1 }
799 [VIS_ACTION_OPEN_LINE_BELOW] = {
800 "open-line-below",
801 "Begin a new line below the cursor",
802 openline, { .i = +1 }
804 [VIS_ACTION_JOIN_LINE_BELOW] = {
805 "join-line-below",
806 "Join line(s)",
807 join, { .i = VIS_MOVE_LINE_NEXT },
809 [VIS_ACTION_JOIN_LINES] = {
810 "join-lines",
811 "Join selected lines",
812 operator, { .i = VIS_OP_JOIN }
814 [VIS_ACTION_PROMPT_SHOW] = {
815 "prompt-show",
816 "Show editor command line prompt",
817 prompt_show, { .s = ":" }
819 [VIS_ACTION_PROMPT_SHOW_VISUAL] = {
820 "prompt-show-visual",
821 "Show editor command line prompt in visual mode",
822 prompt_show, { .s = ":'<,'>" }
824 [VIS_ACTION_REPEAT] = {
825 "editor-repeat",
826 "Repeat latest editor command",
827 repeat
829 [VIS_ACTION_SELECTION_FLIP] = {
830 "selection-flip",
831 "Flip selection, move cursor to other end",
832 selection_end,
834 [VIS_ACTION_SELECTION_RESTORE] = {
835 "selection-restore",
836 "Restore last selection",
837 selection_restore,
839 [VIS_ACTION_WINDOW_REDRAW_TOP] = {
840 "window-redraw-top",
841 "Redraw cursor line at the top of the window",
842 window, { .w = view_redraw_top }
844 [VIS_ACTION_WINDOW_REDRAW_CENTER] = {
845 "window-redraw-center",
846 "Redraw cursor line at the center of the window",
847 window, { .w = view_redraw_center }
849 [VIS_ACTION_WINDOW_REDRAW_BOTTOM] = {
850 "window-redraw-bottom",
851 "Redraw cursor line at the bottom of the window",
852 window, { .w = view_redraw_bottom }
854 [VIS_ACTION_WINDOW_SLIDE_UP] = {
855 "window-slide-up",
856 "Slide window content upwards",
857 wslide, { .i = -1 }
859 [VIS_ACTION_WINDOW_SLIDE_DOWN] = {
860 "window-slide-down",
861 "Slide window content downwards",
862 wslide, { .i = +1 }
864 [VIS_ACTION_PUT_AFTER] = {
865 "put-after",
866 "Put text after the cursor",
867 operator, { .i = VIS_OP_PUT_AFTER }
869 [VIS_ACTION_PUT_BEFORE] = {
870 "put-before",
871 "Put text before the cursor",
872 operator, { .i = VIS_OP_PUT_BEFORE }
874 [VIS_ACTION_PUT_AFTER_END] = {
875 "put-after-end",
876 "Put text after the cursor, place cursor after new text",
877 operator, { .i = VIS_OP_PUT_AFTER_END }
879 [VIS_ACTION_PUT_BEFORE_END] = {
880 "put-before-end",
881 "Put text before the cursor, place cursor after new text",
882 operator, { .i = VIS_OP_PUT_BEFORE_END }
884 [VIS_ACTION_CURSOR_SELECT_WORD] = {
885 "cursors-select-word",
886 "Select word under cursor",
887 cursors_select,
889 [VIS_ACTION_CURSORS_NEW_LINE_ABOVE] = {
890 "cursors-new-lines-above",
891 "Create a new cursor on the line above",
892 cursors_new, { .i = -1 }
894 [VIS_ACTION_CURSORS_NEW_LINE_ABOVE_FIRST] = {
895 "cursors-new-lines-above-first",
896 "Create a new cursor on the line above the first cursor",
897 cursors_new, { .i = INT_MIN }
899 [VIS_ACTION_CURSORS_NEW_LINE_BELOW] = {
900 "cursor-new-lines-below",
901 "Create a new cursor on the line below",
902 cursors_new, { .i = +1 }
904 [VIS_ACTION_CURSORS_NEW_LINE_BELOW_LAST] = {
905 "cursor-new-lines-below-last",
906 "Create a new cursor on the line below the last cursor",
907 cursors_new, { .i = INT_MAX }
909 [VIS_ACTION_CURSORS_NEW_LINES_BEGIN] = {
910 "cursors-new-lines-begin",
911 "Create a new cursor at the start of every line covered by selection",
912 operator, { .i = VIS_OP_CURSOR_SOL }
914 [VIS_ACTION_CURSORS_NEW_LINES_END] = {
915 "cursors-new-lines-end",
916 "Create a new cursor at the end of every line covered by selection",
917 operator, { .i = VIS_OP_CURSOR_EOL }
919 [VIS_ACTION_CURSORS_NEW_MATCH_NEXT] = {
920 "cursors-new-match-next",
921 "Select the next region matching the current selection",
922 cursors_select_next
924 [VIS_ACTION_CURSORS_NEW_MATCH_SKIP] = {
925 "cursors-new-match-skip",
926 "Clear current selection, but select next match",
927 cursors_select_skip,
929 [VIS_ACTION_CURSORS_ALIGN] = {
930 "cursors-align",
931 "Try to align all cursors on the same column",
932 cursors_align,
934 [VIS_ACTION_CURSORS_ALIGN_INDENT_LEFT] = {
935 "cursors-align-indent-left",
936 "Left align all cursors/selections by inserting spaces",
937 cursors_align_indent, { .i = -1 }
939 [VIS_ACTION_CURSORS_ALIGN_INDENT_RIGHT] = {
940 "cursors-align-indent-right",
941 "Right align all cursors/selections by inserting spaces",
942 cursors_align_indent, { .i = +1 }
944 [VIS_ACTION_CURSORS_REMOVE_ALL] = {
945 "cursors-remove-all",
946 "Remove all but the primary cursor",
947 cursors_clear,
949 [VIS_ACTION_CURSORS_REMOVE_LAST] = {
950 "cursors-remove-last",
951 "Remove least recently created cursor",
952 cursors_remove,
954 [VIS_ACTION_CURSORS_PREV] = {
955 "cursors-prev",
956 "Move to the previous cursor",
957 cursors_navigate, { .i = -PAGE_HALF }
959 [VIS_ACTION_CURSORS_NEXT] = {
960 "cursors-next",
961 "Move to the next cursor",
962 cursors_navigate, { .i = +PAGE_HALF }
964 [VIS_ACTION_TEXT_OBJECT_WORD_OUTER] = {
965 "text-object-word-outer",
966 "A word leading and trailing whitespace included",
967 textobj, { .i = VIS_TEXTOBJECT_OUTER_WORD }
969 [VIS_ACTION_TEXT_OBJECT_WORD_INNER] = {
970 "text-object-word-inner",
971 "A word leading and trailing whitespace excluded",
972 textobj, { .i = VIS_TEXTOBJECT_INNER_WORD }
974 [VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER] = {
975 "text-object-longword-outer",
976 "A WORD leading and trailing whitespace included",
977 textobj, { .i = VIS_TEXTOBJECT_OUTER_LONGWORD }
979 [VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER] = {
980 "text-object-longword-inner",
981 "A WORD leading and trailing whitespace excluded",
982 textobj, { .i = VIS_TEXTOBJECT_INNER_LONGWORD }
984 [VIS_ACTION_TEXT_OBJECT_SENTENCE] = {
985 "text-object-sentence",
986 "A sentence",
987 textobj, { .i = VIS_TEXTOBJECT_SENTENCE }
989 [VIS_ACTION_TEXT_OBJECT_PARAGRAPH] = {
990 "text-object-paragraph",
991 "A paragraph",
992 textobj, { .i = VIS_TEXTOBJECT_PARAGRAPH }
994 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER] = {
995 "text-object-square-bracket-outer",
996 "[] block (outer variant)",
997 textobj, { .i = VIS_TEXTOBJECT_OUTER_SQUARE_BRACKET }
999 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER] = {
1000 "text-object-square-bracket-inner",
1001 "[] block (inner variant)",
1002 textobj, { .i = VIS_TEXTOBJECT_INNER_SQUARE_BRACKET }
1004 [VIS_ACTION_TEXT_OBJECT_PARANTHESE_OUTER] = {
1005 "text-object-parentheses-outer",
1006 "() block (outer variant)",
1007 textobj, { .i = VIS_TEXTOBJECT_OUTER_PARANTHESE }
1009 [VIS_ACTION_TEXT_OBJECT_PARANTHESE_INNER] = {
1010 "text-object-parentheses-inner",
1011 "() block (inner variant)",
1012 textobj, { .i = VIS_TEXTOBJECT_INNER_PARANTHESE }
1014 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER] = {
1015 "text-object-angle-bracket-outer",
1016 "<> block (outer variant)",
1017 textobj, { .i = VIS_TEXTOBJECT_OUTER_ANGLE_BRACKET }
1019 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER] = {
1020 "text-object-angle-bracket-inner",
1021 "<> block (inner variant)",
1022 textobj, { .i = VIS_TEXTOBJECT_INNER_ANGLE_BRACKET }
1024 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER] = {
1025 "text-object-curly-bracket-outer",
1026 "{} block (outer variant)",
1027 textobj, { .i = VIS_TEXTOBJECT_OUTER_CURLY_BRACKET }
1029 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER] = {
1030 "text-object-curly-bracket-inner",
1031 "{} block (inner variant)",
1032 textobj, { .i = VIS_TEXTOBJECT_INNER_CURLY_BRACKET }
1034 [VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER] = {
1035 "text-object-quote-outer",
1036 "A quoted string, including the quotation marks",
1037 textobj, { .i = VIS_TEXTOBJECT_OUTER_QUOTE }
1039 [VIS_ACTION_TEXT_OBJECT_QUOTE_INNER] = {
1040 "text-object-quote-inner",
1041 "A quoted string, excluding the quotation marks",
1042 textobj, { .i = VIS_TEXTOBJECT_INNER_QUOTE }
1044 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER] = {
1045 "text-object-single-quote-outer",
1046 "A single quoted string, including the quotation marks",
1047 textobj, { .i = VIS_TEXTOBJECT_OUTER_SINGLE_QUOTE }
1049 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER] = {
1050 "text-object-single-quote-inner",
1051 "A single quoted string, excluding the quotation marks",
1052 textobj, { .i = VIS_TEXTOBJECT_INNER_SINGLE_QUOTE }
1054 [VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER] = {
1055 "text-object-backtick-outer",
1056 "A backtick delimited string (outer variant)",
1057 textobj, { .i = VIS_TEXTOBJECT_OUTER_BACKTICK }
1059 [VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER] = {
1060 "text-object-backtick-inner",
1061 "A backtick delimited string (inner variant)",
1062 textobj, { .i = VIS_TEXTOBJECT_INNER_BACKTICK }
1064 [VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER] = {
1065 "text-object-entire-outer",
1066 "The whole text content",
1067 textobj, { .i = VIS_TEXTOBJECT_OUTER_ENTIRE }
1069 [VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER] = {
1070 "text-object-entire-inner",
1071 "The whole text content, except for leading and trailing empty lines",
1072 textobj, { .i = VIS_TEXTOBJECT_INNER_ENTIRE }
1074 [VIS_ACTION_TEXT_OBJECT_FUNCTION_OUTER] = {
1075 "text-object-function-outer",
1076 "A whole C-like function",
1077 textobj, { .i = VIS_TEXTOBJECT_OUTER_FUNCTION }
1079 [VIS_ACTION_TEXT_OBJECT_FUNCTION_INNER] = {
1080 "text-object-function-inner",
1081 "A whole C-like function body",
1082 textobj, { .i = VIS_TEXTOBJECT_INNER_FUNCTION }
1084 [VIS_ACTION_TEXT_OBJECT_LINE_OUTER] = {
1085 "text-object-line-outer",
1086 "The whole line",
1087 textobj, { .i = VIS_TEXTOBJECT_OUTER_LINE }
1089 [VIS_ACTION_TEXT_OBJECT_LINE_INNER] = {
1090 "text-object-line-inner",
1091 "The whole line, excluding leading and trailing whitespace",
1092 textobj, { .i = VIS_TEXTOBJECT_INNER_LINE }
1094 [VIS_ACTION_TEXT_OBJECT_INDENTATION] = {
1095 "text-object-indentation",
1096 "All adjacent lines with the same indentation level as the current one",
1097 textobj, { .i = VIS_TEXTOBJECT_INDENTATION }
1099 [VIS_ACTION_TEXT_OBJECT_SEARCH_FORWARD] = {
1100 "text-object-search-forward",
1101 "The next search match in forward direction",
1102 textobj, { .i = VIS_TEXTOBJECT_SEARCH_FORWARD }
1104 [VIS_ACTION_TEXT_OBJECT_SEARCH_BACKWARD] = {
1105 "text-object-search-backward",
1106 "The next search match in backward direction",
1107 textobj, { .i = VIS_TEXTOBJECT_SEARCH_BACKWARD }
1109 [VIS_ACTION_MOTION_CHARWISE] = {
1110 "motion-charwise",
1111 "Force motion to be charwise",
1112 motiontype, { .i = VIS_MOTIONTYPE_CHARWISE }
1114 [VIS_ACTION_MOTION_LINEWISE] = {
1115 "motion-linewise",
1116 "Force motion to be linewise",
1117 motiontype, { .i = VIS_MOTIONTYPE_LINEWISE }
1119 [VIS_ACTION_UNICODE_INFO] = {
1120 "unicode-info",
1121 "Show Unicode codepoint(s) of character under cursor",
1122 unicode_info,
1124 [VIS_ACTION_NUMBER_INCREMENT] = {
1125 "number-increment",
1126 "Increment number under cursor",
1127 number_increment_decrement, { .i = +1 }
1129 [VIS_ACTION_NUMBER_DECREMENT] = {
1130 "number-decrement",
1131 "Decrement number under cursor",
1132 number_increment_decrement, { .i = -1 }
1134 [VIS_ACTION_OPEN_FILE_UNDER_CURSOR] = {
1135 "open-file-under-cursor",
1136 "Open file under the cursor",
1137 open_file_under_cursor, { .b = false }
1139 [VIS_ACTION_OPEN_FILE_UNDER_CURSOR_NEW_WINDOW] = {
1140 "open-file-under-cursor-new-cursor",
1141 "Open file under the cursor in a new window",
1142 open_file_under_cursor, { .b = true }
1144 [VIS_ACTION_NOP] = {
1145 "nop",
1146 "Ignore key, do nothing",
1147 nop,
1151 #include "config.h"
1153 /** key bindings functions */
1155 static const char *nop(Vis *vis, const char *keys, const Arg *arg) {
1156 return keys;
1159 static const char *key2register(Vis *vis, const char *keys, enum VisRegister *reg) {
1160 *reg = VIS_REG_INVALID;
1161 if (!keys[0])
1162 return NULL;
1163 if ('a' <= keys[0] && keys[0] <= 'z')
1164 *reg = keys[0] - 'a';
1165 else if ('A' <= keys[0] && keys[0] <= 'Z')
1166 *reg = VIS_REG_A + keys[0] - 'A';
1167 else if (keys[0] == '*' || keys[0] == '+')
1168 *reg = VIS_REG_CLIPBOARD;
1169 else if (keys[0] == '_')
1170 *reg = VIS_REG_BLACKHOLE;
1171 else if (keys[0] == '0')
1172 *reg = VIS_REG_ZERO;
1173 else if (keys[0] == '@')
1174 *reg = VIS_MACRO_LAST_RECORDED;
1175 else if (keys[0] == '/')
1176 *reg = VIS_REG_SEARCH;
1177 else if (keys[0] == ':')
1178 *reg = VIS_REG_COMMAND;
1179 return keys+1;
1182 static const char *macro_record(Vis *vis, const char *keys, const Arg *arg) {
1183 if (!vis_macro_record_stop(vis)) {
1184 enum VisRegister reg;
1185 keys = key2register(vis, keys, &reg);
1186 vis_macro_record(vis, reg);
1188 vis_draw(vis);
1189 return keys;
1192 static const char *macro_replay(Vis *vis, const char *keys, const Arg *arg) {
1193 enum VisRegister reg;
1194 keys = key2register(vis, keys, &reg);
1195 vis_macro_replay(vis, reg);
1196 return keys;
1199 static const char *suspend(Vis *vis, const char *keys, const Arg *arg) {
1200 vis_suspend(vis);
1201 return keys;
1204 static const char *repeat(Vis *vis, const char *keys, const Arg *arg) {
1205 vis_repeat(vis);
1206 return keys;
1209 static const char *cursors_new(Vis *vis, const char *keys, const Arg *arg) {
1210 View *view = vis_view(vis);
1211 for (int count = vis_count_get_default(vis, 1); count > 0; count--) {
1212 Cursor *cursor;
1213 switch (arg->i) {
1214 case -1:
1215 case +1:
1216 cursor = view_cursors_primary_get(view);
1217 break;
1218 case INT_MIN:
1219 cursor = view_cursors(view);
1220 break;
1221 case INT_MAX:
1222 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c))
1223 cursor = c;
1224 break;
1225 default:
1226 return keys;
1228 size_t oldpos = view_cursors_pos(cursor);
1229 if (arg->i > 0)
1230 view_line_down(cursor);
1231 else if (arg->i < 0)
1232 view_line_up(cursor);
1233 size_t newpos = view_cursors_pos(cursor);
1234 view_cursors_to(cursor, oldpos);
1235 if (!view_cursors_new(view, newpos)) {
1236 if (arg->i == -1) {
1237 cursor = view_cursors_prev(cursor);
1238 } else if (arg->i == +1) {
1239 cursor = view_cursors_next(cursor);
1241 view_cursors_primary_set(cursor);
1244 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1245 return keys;
1248 static const char *cursors_align(Vis *vis, const char *keys, const Arg *arg) {
1249 View *view = vis_view(vis);
1250 Text *txt = vis_text(vis);
1251 int mincol = INT_MAX;
1252 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
1253 int col = view_cursors_cell_get(c);
1254 if (col >= 0 && col < mincol)
1255 mincol = col;
1257 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
1258 if (view_cursors_cell_set(c, mincol) == -1) {
1259 size_t pos = view_cursors_pos(c);
1260 size_t col = text_line_width_set(txt, pos, mincol);
1261 view_cursors_to(c, col);
1264 return keys;
1267 static const char *cursors_align_indent(Vis *vis, const char *keys, const Arg *arg) {
1268 View *view = vis_view(vis);
1269 Text *txt = vis_text(vis);
1270 bool left_align = arg->i < 0;
1271 int columns = view_cursors_column_count(view);
1273 for (int i = 0; i < columns; i++) {
1274 int mincol = INT_MAX, maxcol = 0;
1275 for (Cursor *c = view_cursors_column(view, i); c; c = view_cursors_column_next(c, i)) {
1276 size_t pos;
1277 Filerange sel = view_cursors_selection_get(c);
1278 if (text_range_valid(&sel))
1279 pos = left_align ? sel.start : sel.end;
1280 else
1281 pos = view_cursors_pos(c);
1282 int col = text_line_width_get(txt, pos);
1283 if (col < mincol)
1284 mincol = col;
1285 if (col > maxcol)
1286 maxcol = col;
1289 size_t len = maxcol - mincol;
1290 char *buf = malloc(len+1);
1291 if (!buf)
1292 return keys;
1293 memset(buf, ' ', len);
1295 for (Cursor *c = view_cursors_column(view, i); c; c = view_cursors_column_next(c, i)) {
1296 size_t pos, ipos;
1297 Filerange sel = view_cursors_selection_get(c);
1298 if (text_range_valid(&sel)) {
1299 pos = left_align ? sel.start : sel.end;
1300 ipos = sel.start;
1301 } else {
1302 pos = view_cursors_pos(c);
1303 ipos = pos;
1305 int col = text_line_width_get(txt, pos);
1306 if (col < maxcol) {
1307 size_t off = maxcol - col;
1308 if (off <= len)
1309 text_insert(txt, ipos, buf, off);
1313 free(buf);
1316 view_draw(view);
1317 return keys;
1320 static const char *cursors_clear(Vis *vis, const char *keys, const Arg *arg) {
1321 View *view = vis_view(vis);
1322 if (view_cursors_multiple(view))
1323 view_cursors_clear(view);
1324 else
1325 view_cursors_selection_clear(view_cursors_primary_get(view));
1326 return keys;
1329 static const char *cursors_select(Vis *vis, const char *keys, const Arg *arg) {
1330 Text *txt = vis_text(vis);
1331 View *view = vis_view(vis);
1332 for (Cursor *cursor = view_cursors(view); cursor; cursor = view_cursors_next(cursor)) {
1333 Filerange sel = view_cursors_selection_get(cursor);
1334 Filerange word = text_object_word(txt, view_cursors_pos(cursor));
1335 if (!text_range_valid(&sel) && text_range_valid(&word)) {
1336 view_cursors_selection_set(cursor, &word);
1337 view_cursors_to(cursor, text_char_prev(txt, word.end));
1340 vis_mode_switch(vis, VIS_MODE_VISUAL);
1341 return keys;
1344 static const char *cursors_select_next(Vis *vis, const char *keys, const Arg *arg) {
1345 Text *txt = vis_text(vis);
1346 View *view = vis_view(vis);
1347 Cursor *cursor = view_cursors_primary_get(view);
1348 Filerange sel = view_cursors_selection_get(cursor);
1349 if (!text_range_valid(&sel))
1350 return keys;
1352 char *buf = text_bytes_alloc0(txt, sel.start, text_range_size(&sel));
1353 if (!buf)
1354 return keys;
1355 Filerange word = text_object_word_find_next(txt, sel.end, buf);
1356 free(buf);
1358 if (text_range_valid(&word)) {
1359 size_t pos = text_char_prev(txt, word.end);
1360 cursor = view_cursors_new(view, pos);
1361 if (!cursor)
1362 return keys;
1363 view_cursors_selection_set(cursor, &word);
1365 return keys;
1368 static const char *cursors_select_skip(Vis *vis, const char *keys, const Arg *arg) {
1369 View *view = vis_view(vis);
1370 Cursor *cursor = view_cursors_primary_get(view);
1371 keys = cursors_select_next(vis, keys, arg);
1372 if (cursor != view_cursors_primary_get(view))
1373 view_cursors_dispose(cursor);
1374 return keys;
1377 static const char *cursors_remove(Vis *vis, const char *keys, const Arg *arg) {
1378 View *view = vis_view(vis);
1379 view_cursors_dispose(view_cursors_primary_get(view));
1380 view_cursor_to(view, view_cursor_get(view));
1381 return keys;
1384 static const char *cursors_navigate(Vis *vis, const char *keys, const Arg *arg) {
1385 View *view = vis_view(vis);
1386 if (!view_cursors_multiple(view))
1387 return wscroll(vis, keys, arg);
1388 Cursor *c = view_cursors_primary_get(view);
1389 for (int count = vis_count_get_default(vis, 1); count > 0; count--) {
1390 if (arg->i > 0) {
1391 c = view_cursors_next(c);
1392 if (!c)
1393 c = view_cursors(view);
1394 } else {
1395 c = view_cursors_prev(c);
1396 if (!c) {
1397 c = view_cursors(view);
1398 for (Cursor *n = c; n; n = view_cursors_next(n))
1399 c = n;
1403 view_cursors_primary_set(c);
1404 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1405 return keys;
1408 static const char *replace(Vis *vis, const char *keys, const Arg *arg) {
1409 if (!keys[0])
1410 return NULL;
1411 const char *next = vis_keys_next(vis, keys);
1412 if (!next)
1413 return NULL;
1414 size_t len = next - keys;
1415 char key[len+1];
1416 memcpy(key, keys, len);
1417 key[len] = '\0';
1418 vis_operator(vis, VIS_OP_REPLACE);
1419 vis_motion(vis, VIS_MOVE_NOP);
1420 vis_keys_inject(vis, next, key);
1421 vis_keys_inject(vis, next+len, "<Escape>");
1422 return next;
1425 static const char *count(Vis *vis, const char *keys, const Arg *arg) {
1426 int digit = keys[-1] - '0';
1427 int count = vis_count_get_default(vis, 0);
1428 if (0 <= digit && digit <= 9) {
1429 if (digit == 0 && count == 0)
1430 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
1431 vis_count_set(vis, count * 10 + digit);
1433 return keys;
1436 static const char *gotoline(Vis *vis, const char *keys, const Arg *arg) {
1437 if (vis_count_get(vis) != VIS_COUNT_UNKNOWN)
1438 vis_motion(vis, VIS_MOVE_LINE);
1439 else if (arg->i < 0)
1440 vis_motion(vis, VIS_MOVE_FILE_BEGIN);
1441 else
1442 vis_motion(vis, VIS_MOVE_FILE_END);
1443 return keys;
1446 static const char *motiontype(Vis *vis, const char *keys, const Arg *arg) {
1447 vis_motion_type(vis, arg->i);
1448 return keys;
1451 static const char *operator(Vis *vis, const char *keys, const Arg *arg) {
1452 vis_operator(vis, arg->i);
1453 return keys;
1456 static const char *operator_filter(Vis *vis, const char *keys, const Arg *arg) {
1457 vis_operator(vis, VIS_OP_FILTER, arg->s);
1458 return keys;
1461 static const char *movement_key(Vis *vis, const char *keys, const Arg *arg) {
1462 char key[32];
1463 const char *next;
1464 if (!keys[0] || !(next = vis_keys_next(vis, keys)))
1465 return NULL;
1466 size_t len = next - keys;
1467 if (len < sizeof key) {
1468 strncpy(key, keys, len);
1469 key[len] = '\0';
1470 vis_motion(vis, arg->i, key);
1472 return next;
1475 static const char *movement(Vis *vis, const char *keys, const Arg *arg) {
1476 vis_motion(vis, arg->i);
1477 return keys;
1480 static const char *textobj(Vis *vis, const char *keys, const Arg *arg) {
1481 vis_textobject(vis, arg->i);
1482 return keys;
1485 static const char *selection_end(Vis *vis, const char *keys, const Arg *arg) {
1486 for (Cursor *c = view_cursors(vis_view(vis)); c; c = view_cursors_next(c))
1487 view_cursors_selection_swap(c);
1488 return keys;
1491 static const char *selection_restore(Vis *vis, const char *keys, const Arg *arg) {
1492 Text *txt = vis_text(vis);
1493 View *view = vis_view(vis);
1494 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c))
1495 view_cursors_selection_restore(c);
1496 Filerange sel = view_selection_get(view);
1497 if (text_range_is_linewise(txt, &sel))
1498 vis_mode_switch(vis, VIS_MODE_VISUAL_LINE);
1499 else
1500 vis_mode_switch(vis, VIS_MODE_VISUAL);
1501 return keys;
1504 static const char *reg(Vis *vis, const char *keys, const Arg *arg) {
1505 enum VisRegister reg;
1506 keys = key2register(vis, keys, &reg);
1507 vis_register_set(vis, reg);
1508 return keys;
1511 static const char *key2mark(Vis *vis, const char *keys, int *mark) {
1512 *mark = VIS_MARK_INVALID;
1513 if (!keys[0])
1514 return NULL;
1515 if (keys[0] >= 'a' && keys[0] <= 'z')
1516 *mark = keys[0] - 'a';
1517 else if (keys[0] == '<')
1518 *mark = VIS_MARK_SELECTION_START;
1519 else if (keys[0] == '>')
1520 *mark = VIS_MARK_SELECTION_END;
1521 return keys+1;
1524 static const char *mark_set(Vis *vis, const char *keys, const Arg *arg) {
1525 int mark;
1526 keys = key2mark(vis, keys, &mark);
1527 vis_mark_set(vis, mark, view_cursor_get(vis_view(vis)));
1528 return keys;
1531 static const char *mark_motion(Vis *vis, const char *keys, const Arg *arg) {
1532 int mark;
1533 keys = key2mark(vis, keys, &mark);
1534 vis_motion(vis, arg->i, mark);
1535 return keys;
1538 static const char *undo(Vis *vis, const char *keys, const Arg *arg) {
1539 size_t pos = text_undo(vis_text(vis));
1540 if (pos != EPOS) {
1541 View *view = vis_view(vis);
1542 if (!view_cursors_multiple(view))
1543 view_cursor_to(view, pos);
1544 /* redraw all windows in case some display the same file */
1545 vis_draw(vis);
1547 return keys;
1550 static const char *redo(Vis *vis, const char *keys, const Arg *arg) {
1551 size_t pos = text_redo(vis_text(vis));
1552 if (pos != EPOS) {
1553 View *view = vis_view(vis);
1554 if (!view_cursors_multiple(view))
1555 view_cursor_to(view, pos);
1556 /* redraw all windows in case some display the same file */
1557 vis_draw(vis);
1559 return keys;
1562 static const char *earlier(Vis *vis, const char *keys, const Arg *arg) {
1563 size_t pos = text_earlier(vis_text(vis), vis_count_get_default(vis, 1));
1564 if (pos != EPOS) {
1565 view_cursor_to(vis_view(vis), pos);
1566 /* redraw all windows in case some display the same file */
1567 vis_draw(vis);
1569 return keys;
1572 static const char *later(Vis *vis, const char *keys, const Arg *arg) {
1573 size_t pos = text_later(vis_text(vis), vis_count_get_default(vis, 1));
1574 if (pos != EPOS) {
1575 view_cursor_to(vis_view(vis), pos);
1576 /* redraw all windows in case some display the same file */
1577 vis_draw(vis);
1579 return keys;
1582 static const char *delete(Vis *vis, const char *keys, const Arg *arg) {
1583 vis_operator(vis, VIS_OP_DELETE);
1584 vis_motion(vis, arg->i);
1585 return keys;
1588 static const char *insert_register(Vis *vis, const char *keys, const Arg *arg) {
1589 enum VisRegister regid;
1590 keys = key2register(vis, keys, &regid);
1591 size_t len;
1592 const char *data = vis_register_get(vis, regid, &len);
1593 vis_insert_key(vis, data, len);
1594 return keys;
1597 static const char *prompt_show(Vis *vis, const char *keys, const Arg *arg) {
1598 vis_prompt_show(vis, arg->s);
1599 return keys;
1602 static const char *insert_verbatim(Vis *vis, const char *keys, const Arg *arg) {
1603 Rune rune = 0;
1604 char buf[4], type = keys[0];
1605 const char *data = NULL;
1606 int len = 0, count = 0, base = 0;
1607 switch (type) {
1608 case '\0':
1609 return NULL;
1610 case 'o':
1611 case 'O':
1612 count = 3;
1613 base = 8;
1614 break;
1615 case 'U':
1616 count = 4;
1617 /* fall through */
1618 case 'u':
1619 count += 4;
1620 base = 16;
1621 break;
1622 case 'x':
1623 case 'X':
1624 count = 2;
1625 base = 16;
1626 break;
1627 default:
1628 if ('0' <= type && type <= '9') {
1629 rune = type - '0';
1630 count = 2;
1631 base = 10;
1633 break;
1636 if (base) {
1637 for (keys++; keys[0] && count > 0; keys++, count--) {
1638 int v = 0;
1639 if (base == 8 && '0' <= keys[0] && keys[0] <= '7') {
1640 v = keys[0] - '0';
1641 } else if ((base == 10 || base == 16) && '0' <= keys[0] && keys[0] <= '9') {
1642 v = keys[0] - '0';
1643 } else if (base == 16 && 'a' <= keys[0] && keys[0] <= 'f') {
1644 v = 10 + keys[0] - 'a';
1645 } else if (base == 16 && 'A' <= keys[0] && keys[0] <= 'F') {
1646 v = 10 + keys[0] - 'A';
1647 } else {
1648 count = 0;
1649 break;
1651 rune = rune * base + v;
1654 if (count > 0)
1655 return NULL;
1656 if (type == 'u' || type == 'U') {
1657 len = runetochar(buf, &rune);
1658 } else {
1659 buf[0] = rune;
1660 len = 1;
1663 data = buf;
1664 } else {
1665 const char *next = vis_keys_next(vis, keys);
1666 if (!next)
1667 return NULL;
1668 size_t keylen = next - keys;
1669 char key[keylen+1];
1670 memcpy(key, keys, keylen);
1671 key[keylen] = '\0';
1673 static const char *keysym[] = {
1674 "<Enter>", "\n",
1675 "<Tab>", "\t",
1676 "<Backspace>", "\b",
1677 "<Escape>", "\x1b",
1678 "<DEL>", "\x7f",
1679 NULL,
1682 for (const char **k = keysym; k[0]; k += 2) {
1683 if (strcmp(k[0], key) == 0) {
1684 data = k[1];
1685 len = strlen(data);
1686 keys = next;
1687 break;
1692 if (len > 0)
1693 vis_insert_key(vis, data, len);
1694 return keys;
1697 static int argi2lines(Vis *vis, const Arg *arg) {
1698 int count = vis_count_get(vis);
1699 switch (arg->i) {
1700 case -PAGE:
1701 case +PAGE:
1702 return view_height_get(vis_view(vis));
1703 case -PAGE_HALF:
1704 case +PAGE_HALF:
1705 return view_height_get(vis_view(vis))/2;
1706 default:
1707 if (count != VIS_COUNT_UNKNOWN)
1708 return count;
1709 return arg->i < 0 ? -arg->i : arg->i;
1713 static const char *wscroll(Vis *vis, const char *keys, const Arg *arg) {
1714 if (arg->i >= 0)
1715 view_scroll_down(vis_view(vis), argi2lines(vis, arg));
1716 else
1717 view_scroll_up(vis_view(vis), argi2lines(vis, arg));
1718 return keys;
1721 static const char *wslide(Vis *vis, const char *keys, const Arg *arg) {
1722 if (arg->i >= 0)
1723 view_slide_down(vis_view(vis), argi2lines(vis, arg));
1724 else
1725 view_slide_up(vis_view(vis), argi2lines(vis, arg));
1726 return keys;
1729 static const char *call(Vis *vis, const char *keys, const Arg *arg) {
1730 arg->f(vis);
1731 return keys;
1734 static const char *window(Vis *vis, const char *keys, const Arg *arg) {
1735 arg->w(vis_view(vis));
1736 return keys;
1739 static const char *openline(Vis *vis, const char *keys, const Arg *arg) {
1740 vis_operator(vis, VIS_OP_INSERT);
1741 if (arg->i > 0) {
1742 vis_motion(vis, VIS_MOVE_LINE_END);
1743 vis_keys_inject(vis, keys, "<insert-newline>");
1744 } else {
1745 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
1746 vis_keys_inject(vis, keys, "<insert-newline><Up>");
1748 return keys;
1751 static const char *join(Vis *vis, const char *keys, const Arg *arg) {
1752 int count = vis_count_get_default(vis, 0);
1753 if (count)
1754 vis_count_set(vis, count-1);
1755 vis_operator(vis, VIS_OP_JOIN);
1756 vis_motion(vis, arg->i);
1757 return keys;
1760 static const char *switchmode(Vis *vis, const char *keys, const Arg *arg) {
1761 vis_mode_switch(vis, arg->i);
1762 return keys;
1765 static const char *insertmode(Vis *vis, const char *keys, const Arg *arg) {
1766 vis_operator(vis, VIS_OP_INSERT);
1767 vis_motion(vis, arg->i);
1768 return keys;
1771 static const char *unicode_info(Vis *vis, const char *keys, const Arg *arg) {
1772 View *view = vis_view(vis);
1773 Text *txt = vis_text(vis);
1774 size_t start = view_cursor_get(view);
1775 size_t end = text_char_next(txt, start);
1776 char data[end-start], *data_cur = data;
1777 text_bytes_get(txt, start, end - start, data);
1778 Iterator it = text_iterator_get(txt, start);
1779 char info[255] = "", *info_cur = info;
1780 for (size_t pos = start; it.pos < end; pos = it.pos) {
1781 text_iterator_codepoint_next(&it, NULL);
1782 size_t len = it.pos - pos;
1783 wchar_t wc = 0xFFFD;
1784 mbtowc(&wc, data_cur, len);
1785 int width = wcwidth(wc);
1786 info_cur += snprintf(info_cur, sizeof(info) - (info_cur - info) - 1,
1787 "<%s%.*s> U+%04x ", width == 0 ? " " : "", (int)len, data_cur, wc);
1788 data_cur += len;
1790 vis_info_show(vis, "%s", info);
1791 return keys;
1794 static const char *percent(Vis *vis, const char *keys, const Arg *arg) {
1795 if (vis_count_get(vis) == VIS_COUNT_UNKNOWN)
1796 vis_motion(vis, VIS_MOVE_BRACKET_MATCH);
1797 else
1798 vis_motion(vis, VIS_MOVE_PERCENT);
1799 return keys;
1802 static const char *number_increment_decrement(Vis *vis, const char *keys, const Arg *arg) {
1803 View *view = vis_view(vis);
1804 Text *txt = vis_text(vis);
1806 int delta = arg->i;
1807 int count = vis_count_get(vis);
1808 if (count != 0 && count != VIS_COUNT_UNKNOWN)
1809 delta *= count;
1811 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
1812 Filerange r = text_object_number(txt, view_cursors_pos(c));
1813 if (!text_range_valid(&r))
1814 continue;
1815 char *buf = text_bytes_alloc0(txt, r.start, text_range_size(&r));
1816 if (buf) {
1817 char *number = buf, fmt[255];
1818 if (number[0] == '-')
1819 number++;
1820 bool octal = number[0] == '0' && ('0' <= number[1] && number[1] <= '7');
1821 bool hex = number[0] == '0' && (number[1] == 'x' || number[1] == 'X');
1822 bool dec = !hex && !octal;
1824 long long value = strtoll(buf, NULL, 0);
1825 value += delta;
1826 if (dec) {
1827 snprintf(fmt, sizeof fmt, "%lld", value);
1828 } else if (hex) {
1829 size_t len = strlen(number) - 2;
1830 snprintf(fmt, sizeof fmt, "0x%0*llx", (int)len, value);
1831 } else {
1832 size_t len = strlen(number) - 1;
1833 snprintf(fmt, sizeof fmt, "0%0*llo", (int)len, value);
1835 text_delete_range(txt, &r);
1836 text_insert(txt, r.start, fmt, strlen(fmt));
1837 view_cursors_to(c, r.start);
1839 free(buf);
1842 vis_cancel(vis);
1844 return keys;
1847 static const char *open_file_under_cursor(Vis *vis, const char *keys, const Arg *arg) {
1848 Win *win = vis_window(vis);
1849 View *view = vis_view(vis);
1850 Text *txt = vis_text(vis);
1852 if (!arg->b && !vis_window_closable(win)) {
1853 vis_info_show(vis, "No write since last change");
1854 return keys;
1857 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
1858 Filerange r = text_object_filename(txt, view_cursors_pos(c));
1859 if (!text_range_valid(&r))
1860 continue;
1861 char *name = text_bytes_alloc0(txt, r.start, text_range_size(&r));
1862 if (!name)
1863 continue;
1865 struct stat st;
1866 if (stat(name, &st) == -1) {
1867 vis_info_show(vis, "File `%s' not found", name);
1868 free(name);
1869 continue;
1872 if (!vis_window_new(vis, name)) {
1873 vis_info_show(vis, "Failed to open `%s': %s", name, strerror(errno));
1874 free(name);
1875 continue;
1876 } else if (!arg->b) {
1877 vis_window_close(win);
1878 free(name);
1879 return keys;
1882 free(name);
1885 return keys;
1888 static Vis *vis;
1890 static void signal_handler(int signum, siginfo_t *siginfo, void *context) {
1891 vis_signal_handler(vis, signum, siginfo, context);
1894 int main(int argc, char *argv[]) {
1896 VisEvent event = {
1897 .vis_start = vis_lua_start,
1898 .vis_quit = vis_lua_quit,
1899 .file_open = vis_lua_file_open,
1900 .file_save = vis_lua_file_save,
1901 .file_close = vis_lua_file_close,
1902 .win_open = vis_lua_win_open,
1903 .win_close = vis_lua_win_close,
1906 vis = vis_new(ui_curses_new(), &event);
1907 if (!vis)
1908 return EXIT_FAILURE;
1910 for (int i = 0; i < LENGTH(vis_action); i++) {
1911 const KeyAction *action = &vis_action[i];
1912 if (!vis_action_register(vis, action))
1913 vis_die(vis, "Could not register action: %s\n", action->name);
1916 for (int i = 0; i < LENGTH(default_bindings); i++) {
1917 for (const KeyBinding **binding = default_bindings[i]; binding && *binding; binding++) {
1918 for (const KeyBinding *kb = *binding; kb->key; kb++) {
1919 vis_mode_map(vis, i, kb->key, kb);
1924 for (const char **k = keymaps; k[0]; k += 2)
1925 vis_keymap_add(vis, k[0], k[1]);
1927 /* install signal handlers etc. */
1928 struct sigaction sa;
1929 memset(&sa, 0, sizeof sa);
1930 sa.sa_flags = SA_SIGINFO;
1931 sa.sa_sigaction = signal_handler;
1932 if (sigaction(SIGBUS, &sa, NULL) || sigaction(SIGINT, &sa, NULL))
1933 vis_die(vis, "sigaction: %s", strerror(errno));
1935 sigset_t blockset;
1936 sigemptyset(&blockset);
1937 sigaddset(&blockset, SIGWINCH);
1938 sigprocmask(SIG_BLOCK, &blockset, NULL);
1939 signal(SIGPIPE, SIG_IGN);
1941 int status = vis_run(vis, argc, argv);
1942 vis_free(vis);
1943 return status;