lexer: add networkd file LPeg lexer
[vis.git] / main.c
blob266ee1e2557165db71cb8dc8c0c18d4867f7697a
1 #include <signal.h>
2 #include <limits.h>
3 #include <string.h>
4 #include <wchar.h>
5 #include <ctype.h>
6 #include <errno.h>
7 #include <stdlib.h>
9 #include "ui-curses.h"
10 #include "vis.h"
11 #include "vis-lua.h"
12 #include "text-util.h"
13 #include "text-motions.h"
14 #include "text-objects.h"
15 #include "util.h"
16 #include "libutf.h"
17 #include "array.h"
19 #define PAGE INT_MAX
20 #define PAGE_HALF (INT_MAX-1)
22 /** functions to be called from keybindings */
23 /* ignore key, do nothing */
24 static const char *nop(Vis*, const char *keys, const Arg *arg);
25 /* record/replay macro indicated by keys */
26 static const char *macro_record(Vis*, const char *keys, const Arg *arg);
27 static const char *macro_replay(Vis*, const char *keys, const Arg *arg);
28 /* temporarily suspend the editor and return to the shell, type 'fg' to get back */
29 static const char *suspend(Vis*, const char *keys, const Arg *arg);
30 /* switch to mode indicated by arg->i */
31 static const char *switchmode(Vis*, const char *keys, const Arg *arg);
32 /* switch to insert mode after performing movement indicated by arg->i */
33 static const char *insertmode(Vis*, const char *keys, const Arg *arg);
34 /* set mark indicated by keys to current cursor position */
35 static const char *mark_set(Vis*, const char *keys, const Arg *arg);
36 /* add a new line either before or after the one where the cursor currently is */
37 static const char *openline(Vis*, const char *keys, const Arg *arg);
38 /* join lines from current cursor position to movement indicated by arg */
39 static const char *join(Vis*, const char *keys, const Arg *arg);
40 /* perform last action i.e. action_prev again */
41 static const char *repeat(Vis*, const char *keys, const Arg *arg);
42 /* replace character at cursor with one from keys */
43 static const char *replace(Vis*, const char *keys, const Arg *arg);
44 /* create a new cursor on the previous (arg->i < 0) or next (arg->i > 0) line */
45 static const char *cursors_new(Vis*, const char *keys, const Arg *arg);
46 /* try to align all cursors on the same column */
47 static const char *cursors_align(Vis*, const char *keys, const Arg *arg);
48 /* try to align all cursors by inserting the correct amount of white spaces */
49 static const char *cursors_align_indent(Vis*, const char *keys, const Arg *arg);
50 /* remove all but the primary cursor and their selections */
51 static const char *cursors_clear(Vis*, const char *keys, const Arg *arg);
52 /* remove the least recently added cursor */
53 static const char *cursors_remove(Vis*, const char *keys, const Arg *arg);
54 /* remove count (or arg->i)-th cursor column */
55 static const char *cursors_remove_column(Vis*, const char *keys, const Arg *arg);
56 /* remove all but the count (or arg->i)-th cursor column */
57 static const char *cursors_remove_column_except(Vis*, const char *keys, const Arg *arg);
58 /* move to the previous (arg->i < 0) or next (arg->i > 0) cursor */
59 static const char *cursors_navigate(Vis*, const char *keys, const Arg *arg);
60 /* select the word the cursor is currently over */
61 static const char *cursors_select(Vis*, const char *keys, const Arg *arg);
62 /* select the next region matching the current selection */
63 static const char *cursors_select_next(Vis*, const char *keys, const Arg *arg);
64 /* clear current selection but select next match */
65 static const char *cursors_select_skip(Vis*, const char *keys, const Arg *arg);
66 /* rotate selection content count times left (arg->i < 0) or right (arg->i > 0) */
67 static const char *selections_rotate(Vis*, const char *keys, const Arg *arg);
68 /* remove leading and trailing white spaces from selections */
69 static const char *selections_trim(Vis*, const char *keys, const Arg *arg);
70 /* adjust current used count according to keys */
71 static const char *count(Vis*, const char *keys, const Arg *arg);
72 /* move to the count-th line or if not given either to the first (arg->i < 0)
73 * or last (arg->i > 0) line of file */
74 static const char *gotoline(Vis*, const char *keys, const Arg *arg);
75 /* set motion type either LINEWISE or CHARWISE via arg->i */
76 static const char *motiontype(Vis*, const char *keys, const Arg *arg);
77 /* make the current action use the operator indicated by arg->i */
78 static const char *operator(Vis*, const char *keys, const Arg *arg);
79 /* use arg->s as command for the filter operator */
80 static const char *operator_filter(Vis*, const char *keys, const Arg *arg);
81 /* blocks to read a key and performs movement indicated by arg->i which
82 * should be one of VIS_MOVE_{RIGHT,LEFT}_{TO,TILL} */
83 static const char *movement_key(Vis*, const char *keys, const Arg *arg);
84 /* perform the movement as indicated by arg->i */
85 static const char *movement(Vis*, const char *keys, const Arg *arg);
86 /* let the current operator affect the range indicated by the text object arg->i */
87 static const char *textobj(Vis*, const char *keys, const Arg *arg);
88 /* move to the other end of selected text */
89 static const char *selection_end(Vis*, const char *keys, const Arg *arg);
90 /* restore least recently used selection */
91 static const char *selection_restore(Vis*, const char *keys, const Arg *arg);
92 /* use register indicated by keys for the current operator */
93 static const char *reg(Vis*, const char *keys, const Arg *arg);
94 /* perform arg->i motion with a mark indicated by keys as argument */
95 static const char *mark_motion(Vis*, const char *keys, const Arg *arg);
96 /* {un,re}do last action, redraw window */
97 static const char *undo(Vis*, const char *keys, const Arg *arg);
98 static const char *redo(Vis*, const char *keys, const Arg *arg);
99 /* earlier, later action chronologically, redraw window */
100 static const char *earlier(Vis*, const char *keys, const Arg *arg);
101 static const char *later(Vis*, const char *keys, const Arg *arg);
102 /* delete from the current cursor position to the end of
103 * movement as indicated by arg->i */
104 static const char *delete(Vis*, const char *keys, const Arg *arg);
105 /* insert register content indicated by keys at current cursor position */
106 static const char *insert_register(Vis*, const char *keys, const Arg *arg);
107 /* show a user prompt to get input with title arg->s */
108 static const char *prompt_show(Vis*, const char *keys, const Arg *arg);
109 /* blocks to read 3 consecutive digits and inserts the corresponding byte value */
110 static const char *insert_verbatim(Vis*, const char *keys, const Arg *arg);
111 /* scroll window content according to arg->i which can be either PAGE, PAGE_HALF,
112 * or an arbitrary number of lines. a multiplier overrides what is given in arg->i.
113 * negative values scroll back, positive forward. */
114 static const char *wscroll(Vis*, const char *keys, const Arg *arg);
115 /* similar to scroll, but do only move window content not cursor position */
116 static const char *wslide(Vis*, const char *keys, const Arg *arg);
117 /* call editor function as indicated by arg->f */
118 static const char *call(Vis*, const char *keys, const Arg *arg);
119 /* call window function as indicated by arg->w */
120 static const char *window(Vis*, const char *keys, const Arg *arg);
121 /* show info about Unicode character at cursor position */
122 static const char *unicode_info(Vis*, const char *keys, const Arg *arg);
123 /* either go to count % of ile or to matching item */
124 static const char *percent(Vis*, const char *keys, const Arg *arg);
125 /* either increment (arg->i > 0) or decrement (arg->i < 0) number under cursor */
126 static const char *number_increment_decrement(Vis*, const char *keys, const Arg *arg);
127 /* open a filename under cursor in same (!arg->b) or new (arg->b) window */
128 static const char *open_file_under_cursor(Vis*, const char *keys, const Arg *arg);
130 enum {
131 VIS_ACTION_EDITOR_SUSPEND,
132 VIS_ACTION_CURSOR_CHAR_PREV,
133 VIS_ACTION_CURSOR_CHAR_NEXT,
134 VIS_ACTION_CURSOR_LINE_CHAR_PREV,
135 VIS_ACTION_CURSOR_LINE_CHAR_NEXT,
136 VIS_ACTION_CURSOR_WORD_START_PREV,
137 VIS_ACTION_CURSOR_WORD_START_NEXT,
138 VIS_ACTION_CURSOR_WORD_END_PREV,
139 VIS_ACTION_CURSOR_WORD_END_NEXT,
140 VIS_ACTION_CURSOR_LONGWORD_START_PREV,
141 VIS_ACTION_CURSOR_LONGWORD_START_NEXT,
142 VIS_ACTION_CURSOR_LONGWORD_END_PREV,
143 VIS_ACTION_CURSOR_LONGWORD_END_NEXT,
144 VIS_ACTION_CURSOR_LINE_UP,
145 VIS_ACTION_CURSOR_LINE_DOWN,
146 VIS_ACTION_CURSOR_LINE_START,
147 VIS_ACTION_CURSOR_LINE_FINISH,
148 VIS_ACTION_CURSOR_LINE_BEGIN,
149 VIS_ACTION_CURSOR_LINE_END,
150 VIS_ACTION_CURSOR_LINE_LASTCHAR,
151 VIS_ACTION_CURSOR_SCREEN_LINE_UP,
152 VIS_ACTION_CURSOR_SCREEN_LINE_DOWN,
153 VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN,
154 VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE,
155 VIS_ACTION_CURSOR_SCREEN_LINE_END,
156 VIS_ACTION_CURSOR_PERCENT,
157 VIS_ACTION_CURSOR_PARAGRAPH_PREV,
158 VIS_ACTION_CURSOR_PARAGRAPH_NEXT,
159 VIS_ACTION_CURSOR_SENTENCE_PREV,
160 VIS_ACTION_CURSOR_SENTENCE_NEXT,
161 VIS_ACTION_CURSOR_FUNCTION_START_PREV,
162 VIS_ACTION_CURSOR_FUNCTION_END_PREV,
163 VIS_ACTION_CURSOR_FUNCTION_START_NEXT,
164 VIS_ACTION_CURSOR_FUNCTION_END_NEXT,
165 VIS_ACTION_CURSOR_BLOCK_START,
166 VIS_ACTION_CURSOR_BLOCK_END,
167 VIS_ACTION_CURSOR_PARENTHESE_START,
168 VIS_ACTION_CURSOR_PARENTHESE_END,
169 VIS_ACTION_CURSOR_COLUMN,
170 VIS_ACTION_CURSOR_LINE_FIRST,
171 VIS_ACTION_CURSOR_LINE_LAST,
172 VIS_ACTION_CURSOR_WINDOW_LINE_TOP,
173 VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE,
174 VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM,
175 VIS_ACTION_CURSOR_SEARCH_NEXT,
176 VIS_ACTION_CURSOR_SEARCH_PREV,
177 VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD,
178 VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD,
179 VIS_ACTION_WINDOW_PAGE_UP,
180 VIS_ACTION_WINDOW_PAGE_DOWN,
181 VIS_ACTION_WINDOW_HALFPAGE_UP,
182 VIS_ACTION_WINDOW_HALFPAGE_DOWN,
183 VIS_ACTION_MODE_NORMAL,
184 VIS_ACTION_MODE_VISUAL,
185 VIS_ACTION_MODE_VISUAL_LINE,
186 VIS_ACTION_MODE_INSERT,
187 VIS_ACTION_MODE_REPLACE,
188 VIS_ACTION_MODE_OPERATOR_PENDING,
189 VIS_ACTION_DELETE_CHAR_PREV,
190 VIS_ACTION_DELETE_CHAR_NEXT,
191 VIS_ACTION_DELETE_LINE_BEGIN,
192 VIS_ACTION_DELETE_WORD_PREV,
193 VIS_ACTION_JUMPLIST_PREV,
194 VIS_ACTION_JUMPLIST_NEXT,
195 VIS_ACTION_CHANGELIST_PREV,
196 VIS_ACTION_CHANGELIST_NEXT,
197 VIS_ACTION_UNDO,
198 VIS_ACTION_REDO,
199 VIS_ACTION_EARLIER,
200 VIS_ACTION_LATER,
201 VIS_ACTION_MACRO_RECORD,
202 VIS_ACTION_MACRO_REPLAY,
203 VIS_ACTION_MARK_SET,
204 VIS_ACTION_MARK_GOTO,
205 VIS_ACTION_MARK_GOTO_LINE,
206 VIS_ACTION_REDRAW,
207 VIS_ACTION_REPLACE_CHAR,
208 VIS_ACTION_TOTILL_REPEAT,
209 VIS_ACTION_TOTILL_REVERSE,
210 VIS_ACTION_PROMPT_SEARCH_FORWARD,
211 VIS_ACTION_PROMPT_SEARCH_BACKWARD,
212 VIS_ACTION_TILL_LEFT,
213 VIS_ACTION_TILL_RIGHT,
214 VIS_ACTION_TO_LEFT,
215 VIS_ACTION_TO_RIGHT,
216 VIS_ACTION_REGISTER,
217 VIS_ACTION_OPERATOR_CHANGE,
218 VIS_ACTION_OPERATOR_DELETE,
219 VIS_ACTION_OPERATOR_YANK,
220 VIS_ACTION_OPERATOR_SHIFT_LEFT,
221 VIS_ACTION_OPERATOR_SHIFT_RIGHT,
222 VIS_ACTION_OPERATOR_CASE_LOWER,
223 VIS_ACTION_OPERATOR_CASE_UPPER,
224 VIS_ACTION_OPERATOR_CASE_SWAP,
225 VIS_ACTION_OPERATOR_FILTER,
226 VIS_ACTION_OPERATOR_FILTER_FMT,
227 VIS_ACTION_COUNT,
228 VIS_ACTION_INSERT_NEWLINE,
229 VIS_ACTION_INSERT_TAB,
230 VIS_ACTION_INSERT_VERBATIM,
231 VIS_ACTION_INSERT_REGISTER,
232 VIS_ACTION_WINDOW_NEXT,
233 VIS_ACTION_WINDOW_PREV,
234 VIS_ACTION_APPEND_CHAR_NEXT,
235 VIS_ACTION_APPEND_LINE_END,
236 VIS_ACTION_INSERT_LINE_START,
237 VIS_ACTION_OPEN_LINE_ABOVE,
238 VIS_ACTION_OPEN_LINE_BELOW,
239 VIS_ACTION_JOIN_LINE_BELOW,
240 VIS_ACTION_JOIN_LINES,
241 VIS_ACTION_PROMPT_SHOW,
242 VIS_ACTION_REPEAT,
243 VIS_ACTION_SELECTION_FLIP,
244 VIS_ACTION_SELECTION_RESTORE,
245 VIS_ACTION_WINDOW_REDRAW_TOP,
246 VIS_ACTION_WINDOW_REDRAW_CENTER,
247 VIS_ACTION_WINDOW_REDRAW_BOTTOM,
248 VIS_ACTION_WINDOW_SLIDE_UP,
249 VIS_ACTION_WINDOW_SLIDE_DOWN,
250 VIS_ACTION_PUT_AFTER,
251 VIS_ACTION_PUT_BEFORE,
252 VIS_ACTION_PUT_AFTER_END,
253 VIS_ACTION_PUT_BEFORE_END,
254 VIS_ACTION_CURSOR_SELECT_WORD,
255 VIS_ACTION_CURSORS_NEW_LINE_ABOVE,
256 VIS_ACTION_CURSORS_NEW_LINE_ABOVE_FIRST,
257 VIS_ACTION_CURSORS_NEW_LINE_BELOW,
258 VIS_ACTION_CURSORS_NEW_LINE_BELOW_LAST,
259 VIS_ACTION_CURSORS_NEW_LINES_BEGIN,
260 VIS_ACTION_CURSORS_NEW_LINES_END,
261 VIS_ACTION_CURSORS_NEW_MATCH_NEXT,
262 VIS_ACTION_CURSORS_NEW_MATCH_SKIP,
263 VIS_ACTION_CURSORS_ALIGN,
264 VIS_ACTION_CURSORS_ALIGN_INDENT_LEFT,
265 VIS_ACTION_CURSORS_ALIGN_INDENT_RIGHT,
266 VIS_ACTION_CURSORS_REMOVE_ALL,
267 VIS_ACTION_CURSORS_REMOVE_LAST,
268 VIS_ACTION_CURSORS_REMOVE_COLUMN,
269 VIS_ACTION_CURSORS_REMOVE_COLUMN_EXCEPT,
270 VIS_ACTION_CURSORS_PREV,
271 VIS_ACTION_CURSORS_NEXT,
272 VIS_ACTION_SELECTIONS_ROTATE_LEFT,
273 VIS_ACTION_SELECTIONS_ROTATE_RIGHT,
274 VIS_ACTION_SELECTIONS_TRIM,
275 VIS_ACTION_TEXT_OBJECT_WORD_OUTER,
276 VIS_ACTION_TEXT_OBJECT_WORD_INNER,
277 VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER,
278 VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER,
279 VIS_ACTION_TEXT_OBJECT_SENTENCE,
280 VIS_ACTION_TEXT_OBJECT_PARAGRAPH,
281 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER,
282 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER,
283 VIS_ACTION_TEXT_OBJECT_PARANTHESE_OUTER,
284 VIS_ACTION_TEXT_OBJECT_PARANTHESE_INNER,
285 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER,
286 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER,
287 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER,
288 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER,
289 VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER,
290 VIS_ACTION_TEXT_OBJECT_QUOTE_INNER,
291 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER,
292 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER,
293 VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER,
294 VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER,
295 VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER,
296 VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER,
297 VIS_ACTION_TEXT_OBJECT_FUNCTION_OUTER,
298 VIS_ACTION_TEXT_OBJECT_FUNCTION_INNER,
299 VIS_ACTION_TEXT_OBJECT_LINE_OUTER,
300 VIS_ACTION_TEXT_OBJECT_LINE_INNER,
301 VIS_ACTION_TEXT_OBJECT_INDENTATION,
302 VIS_ACTION_TEXT_OBJECT_SEARCH_FORWARD,
303 VIS_ACTION_TEXT_OBJECT_SEARCH_BACKWARD,
304 VIS_ACTION_MOTION_CHARWISE,
305 VIS_ACTION_MOTION_LINEWISE,
306 VIS_ACTION_UNICODE_INFO,
307 VIS_ACTION_NUMBER_INCREMENT,
308 VIS_ACTION_NUMBER_DECREMENT,
309 VIS_ACTION_OPEN_FILE_UNDER_CURSOR,
310 VIS_ACTION_OPEN_FILE_UNDER_CURSOR_NEW_WINDOW,
311 VIS_ACTION_NOP,
314 static const KeyAction vis_action[] = {
315 [VIS_ACTION_EDITOR_SUSPEND] = {
316 "editor-suspend",
317 "Suspend the editor",
318 suspend,
320 [VIS_ACTION_CURSOR_CHAR_PREV] = {
321 "cursor-char-prev",
322 "Move cursor left, to the previous character",
323 movement, { .i = VIS_MOVE_CHAR_PREV }
325 [VIS_ACTION_CURSOR_CHAR_NEXT] = {
326 "cursor-char-next",
327 "Move cursor right, to the next character",
328 movement, { .i = VIS_MOVE_CHAR_NEXT }
330 [VIS_ACTION_CURSOR_LINE_CHAR_PREV] = {
331 "cursor-line-char-prev",
332 "Move cursor left, to the previous character on the same line",
333 movement, { .i = VIS_MOVE_LINE_CHAR_PREV }
335 [VIS_ACTION_CURSOR_LINE_CHAR_NEXT] = {
336 "cursor-line-char-next",
337 "Move cursor right, to the next character on the same line",
338 movement, { .i = VIS_MOVE_LINE_CHAR_NEXT }
340 [VIS_ACTION_CURSOR_WORD_START_PREV] = {
341 "cursor-word-start-prev",
342 "Move cursor words backwards",
343 movement, { .i = VIS_MOVE_WORD_START_PREV }
345 [VIS_ACTION_CURSOR_WORD_START_NEXT] = {
346 "cursor-word-start-next",
347 "Move cursor words forwards",
348 movement, { .i = VIS_MOVE_WORD_START_NEXT }
350 [VIS_ACTION_CURSOR_WORD_END_PREV] = {
351 "cursor-word-end-prev",
352 "Move cursor backwards to the end of word",
353 movement, { .i = VIS_MOVE_WORD_END_PREV }
355 [VIS_ACTION_CURSOR_WORD_END_NEXT] = {
356 "cursor-word-end-next",
357 "Move cursor forward to the end of word",
358 movement, { .i = VIS_MOVE_WORD_END_NEXT }
360 [VIS_ACTION_CURSOR_LONGWORD_START_PREV] = {
361 "cursor-longword-start-prev",
362 "Move cursor WORDS backwards",
363 movement, { .i = VIS_MOVE_LONGWORD_START_PREV }
365 [VIS_ACTION_CURSOR_LONGWORD_START_NEXT] = {
366 "cursor-longword-start-next",
367 "Move cursor WORDS forwards",
368 movement, { .i = VIS_MOVE_LONGWORD_START_NEXT }
370 [VIS_ACTION_CURSOR_LONGWORD_END_PREV] = {
371 "cursor-longword-end-prev",
372 "Move cursor backwards to the end of WORD",
373 movement, { .i = VIS_MOVE_LONGWORD_END_PREV }
375 [VIS_ACTION_CURSOR_LONGWORD_END_NEXT] = {
376 "cursor-longword-end-next",
377 "Move cursor forward to the end of WORD",
378 movement, { .i = VIS_MOVE_LONGWORD_END_NEXT }
380 [VIS_ACTION_CURSOR_LINE_UP] = {
381 "cursor-line-up",
382 "Move cursor line upwards",
383 movement, { .i = VIS_MOVE_LINE_UP }
385 [VIS_ACTION_CURSOR_LINE_DOWN] = {
386 "cursor-line-down",
387 "Move cursor line downwards",
388 movement, { .i = VIS_MOVE_LINE_DOWN }
390 [VIS_ACTION_CURSOR_LINE_START] = {
391 "cursor-line-start",
392 "Move cursor to first non-blank character of the line",
393 movement, { .i = VIS_MOVE_LINE_START }
395 [VIS_ACTION_CURSOR_LINE_FINISH] = {
396 "cursor-line-finish",
397 "Move cursor to last non-blank character of the line",
398 movement, { .i = VIS_MOVE_LINE_FINISH }
400 [VIS_ACTION_CURSOR_LINE_BEGIN] = {
401 "cursor-line-begin",
402 "Move cursor to first character of the line",
403 movement, { .i = VIS_MOVE_LINE_BEGIN }
405 [VIS_ACTION_CURSOR_LINE_END] = {
406 "cursor-line-end",
407 "Move cursor to end of the line",
408 movement, { .i = VIS_MOVE_LINE_END }
410 [VIS_ACTION_CURSOR_LINE_LASTCHAR] = {
411 "cursor-line-lastchar",
412 "Move cursor to last character of the line",
413 movement, { .i = VIS_MOVE_LINE_LASTCHAR }
415 [VIS_ACTION_CURSOR_SCREEN_LINE_UP] = {
416 "cursor-screenline-up",
417 "Move cursor screen/display line upwards",
418 movement, { .i = VIS_MOVE_SCREEN_LINE_UP }
420 [VIS_ACTION_CURSOR_SCREEN_LINE_DOWN] = {
421 "cursor-screenline-down",
422 "Move cursor screen/display line downwards",
423 movement, { .i = VIS_MOVE_SCREEN_LINE_DOWN }
425 [VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN] = {
426 "cursor-screenline-begin",
427 "Move cursor to beginning of screen/display line",
428 movement, { .i = VIS_MOVE_SCREEN_LINE_BEGIN }
430 [VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE] = {
431 "cursor-screenline-middle",
432 "Move cursor to middle of screen/display line",
433 movement, { .i = VIS_MOVE_SCREEN_LINE_MIDDLE }
435 [VIS_ACTION_CURSOR_SCREEN_LINE_END] = {
436 "cursor-screenline-end",
437 "Move cursor to end of screen/display line",
438 movement, { .i = VIS_MOVE_SCREEN_LINE_END }
440 [VIS_ACTION_CURSOR_PERCENT] = {
441 "cursor-percent",
442 "Move to count % of file or matching item",
443 percent
445 [VIS_ACTION_CURSOR_PARAGRAPH_PREV] = {
446 "cursor-paragraph-prev",
447 "Move cursor paragraph backward",
448 movement, { .i = VIS_MOVE_PARAGRAPH_PREV }
450 [VIS_ACTION_CURSOR_PARAGRAPH_NEXT] = {
451 "cursor-paragraph-next",
452 "Move cursor paragraph forward",
453 movement, { .i = VIS_MOVE_PARAGRAPH_NEXT }
455 [VIS_ACTION_CURSOR_SENTENCE_PREV] = {
456 "cursor-sentence-prev",
457 "Move cursor sentence backward",
458 movement, { .i = VIS_MOVE_SENTENCE_PREV }
460 [VIS_ACTION_CURSOR_SENTENCE_NEXT] = {
461 "cursor-sentence-next",
462 "Move cursor sentence forward",
463 movement, { .i = VIS_MOVE_SENTENCE_NEXT }
465 [VIS_ACTION_CURSOR_FUNCTION_START_PREV] = {
466 "cursor-function-start-prev",
467 "Move cursor backwards to start of function",
468 movement, { .i = VIS_MOVE_FUNCTION_START_PREV }
470 [VIS_ACTION_CURSOR_FUNCTION_START_NEXT] = {
471 "cursor-function-start-next",
472 "Move cursor forwards to start of function",
473 movement, { .i = VIS_MOVE_FUNCTION_START_NEXT }
475 [VIS_ACTION_CURSOR_FUNCTION_END_PREV] = {
476 "cursor-function-end-prev",
477 "Move cursor backwards to end of function",
478 movement, { .i = VIS_MOVE_FUNCTION_END_PREV }
480 [VIS_ACTION_CURSOR_FUNCTION_END_NEXT] = {
481 "cursor-function-end-next",
482 "Move cursor forwards to end of function",
483 movement, { .i = VIS_MOVE_FUNCTION_END_NEXT }
485 [VIS_ACTION_CURSOR_BLOCK_START] = {
486 "cursor-block-start",
487 "Move cursor to the opening curly brace in a block",
488 movement, { .i = VIS_MOVE_BLOCK_START }
490 [VIS_ACTION_CURSOR_BLOCK_END] = {
491 "cursor-block-end",
492 "Move cursor to the closing curly brace in a block",
493 movement, { .i = VIS_MOVE_BLOCK_END }
495 [VIS_ACTION_CURSOR_PARENTHESE_START] = {
496 "cursor-parenthese-start",
497 "Move cursor to the opening parenthese inside a pair of parentheses",
498 movement, { .i = VIS_MOVE_PARENTHESE_START }
500 [VIS_ACTION_CURSOR_PARENTHESE_END] = {
501 "cursor-parenthese-end",
502 "Move cursor to the closing parenthese inside a pair of parentheses",
503 movement, { .i = VIS_MOVE_PARENTHESE_END }
505 [VIS_ACTION_CURSOR_COLUMN] = {
506 "cursor-column",
507 "Move cursor to given column of current line",
508 movement, { .i = VIS_MOVE_COLUMN }
510 [VIS_ACTION_CURSOR_LINE_FIRST] = {
511 "cursor-line-first",
512 "Move cursor to given line (defaults to first)",
513 gotoline, { .i = -1 }
515 [VIS_ACTION_CURSOR_LINE_LAST] = {
516 "cursor-line-last",
517 "Move cursor to given line (defaults to last)",
518 gotoline, { .i = +1 }
520 [VIS_ACTION_CURSOR_WINDOW_LINE_TOP] = {
521 "cursor-window-line-top",
522 "Move cursor to top line of the window",
523 movement, { .i = VIS_MOVE_WINDOW_LINE_TOP }
525 [VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE] = {
526 "cursor-window-line-middle",
527 "Move cursor to middle line of the window",
528 movement, { .i = VIS_MOVE_WINDOW_LINE_MIDDLE }
530 [VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM] = {
531 "cursor-window-line-bottom",
532 "Move cursor to bottom line of the window",
533 movement, { .i = VIS_MOVE_WINDOW_LINE_BOTTOM }
535 [VIS_ACTION_CURSOR_SEARCH_NEXT] = {
536 "cursor-search-forward",
537 "Move cursor to bottom line of the window",
538 movement, { .i = VIS_MOVE_SEARCH_NEXT }
540 [VIS_ACTION_CURSOR_SEARCH_PREV] = {
541 "cursor-search-backward",
542 "Move cursor to bottom line of the window",
543 movement, { .i = VIS_MOVE_SEARCH_PREV }
545 [VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD] = {
546 "cursor-search-word-forward",
547 "Move cursor to next occurence of the word under cursor",
548 movement, { .i = VIS_MOVE_SEARCH_WORD_FORWARD }
550 [VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD] = {
551 "cursor-search-word-backward",
552 "Move cursor to previous occurence of the word under cursor",
553 movement, { .i = VIS_MOVE_SEARCH_WORD_BACKWARD }
555 [VIS_ACTION_WINDOW_PAGE_UP] = {
556 "window-page-up",
557 "Scroll window pages backwards (upwards)",
558 wscroll, { .i = -PAGE }
560 [VIS_ACTION_WINDOW_HALFPAGE_UP] = {
561 "window-halfpage-up",
562 "Scroll window half pages backwards (upwards)",
563 wscroll, { .i = -PAGE_HALF }
565 [VIS_ACTION_WINDOW_PAGE_DOWN] = {
566 "window-page-down",
567 "Scroll window pages forwards (downwards)",
568 wscroll, { .i = +PAGE }
570 [VIS_ACTION_WINDOW_HALFPAGE_DOWN] = {
571 "window-halfpage-down",
572 "Scroll window half pages forwards (downwards)",
573 wscroll, { .i = +PAGE_HALF }
575 [VIS_ACTION_MODE_NORMAL] = {
576 "vis-mode-normal",
577 "Enter normal mode",
578 switchmode, { .i = VIS_MODE_NORMAL }
580 [VIS_ACTION_MODE_VISUAL] = {
581 "vis-mode-visual-charwise",
582 "Enter characterwise visual mode",
583 switchmode, { .i = VIS_MODE_VISUAL }
585 [VIS_ACTION_MODE_VISUAL_LINE] = {
586 "vis-mode-visual-linewise",
587 "Enter linewise visual mode",
588 switchmode, { .i = VIS_MODE_VISUAL_LINE }
590 [VIS_ACTION_MODE_INSERT] = {
591 "vis-mode-insert",
592 "Enter insert mode",
593 switchmode, { .i = VIS_MODE_INSERT }
595 [VIS_ACTION_MODE_REPLACE] = {
596 "vis-mode-replace",
597 "Enter replace mode",
598 switchmode, { .i = VIS_MODE_REPLACE }
600 [VIS_ACTION_MODE_OPERATOR_PENDING] = {
601 "vis-mode-operator-pending",
602 "Enter to operator pending mode",
603 switchmode, { .i = VIS_MODE_OPERATOR_PENDING }
605 [VIS_ACTION_DELETE_CHAR_PREV] = {
606 "delete-char-prev",
607 "Delete the previous character",
608 delete, { .i = VIS_MOVE_CHAR_PREV }
610 [VIS_ACTION_DELETE_CHAR_NEXT] = {
611 "delete-char-next",
612 "Delete the next character",
613 delete, { .i = VIS_MOVE_CHAR_NEXT }
615 [VIS_ACTION_DELETE_LINE_BEGIN] = {
616 "delete-line-begin",
617 "Delete until the start of the current line",
618 delete, { .i = VIS_MOVE_LINE_BEGIN }
620 [VIS_ACTION_DELETE_WORD_PREV] = {
621 "delete-word-prev",
622 "Delete the previous WORD",
623 delete, { .i = VIS_MOVE_LONGWORD_START_PREV }
625 [VIS_ACTION_JUMPLIST_PREV] = {
626 "jumplist-prev",
627 "Go to older cursor position in jump list",
628 movement, { .i = VIS_MOVE_JUMPLIST_PREV }
630 [VIS_ACTION_JUMPLIST_NEXT] = {
631 "jumplist-next",
632 "Go to newer cursor position in jump list",
633 movement, { .i = VIS_MOVE_JUMPLIST_NEXT }
635 [VIS_ACTION_CHANGELIST_PREV] = {
636 "changelist-prev",
637 "Go to older cursor position in change list",
638 movement, { .i = VIS_MOVE_CHANGELIST_PREV }
640 [VIS_ACTION_CHANGELIST_NEXT] = {
641 "changelist-next",
642 "Go to newer cursor position in change list",
643 movement, { .i = VIS_MOVE_CHANGELIST_NEXT }
645 [VIS_ACTION_UNDO] = {
646 "editor-undo",
647 "Undo last change",
648 undo,
650 [VIS_ACTION_REDO] = {
651 "editor-redo",
652 "Redo last change",
653 redo,
655 [VIS_ACTION_EARLIER] = {
656 "editor-earlier",
657 "Goto older text state",
658 earlier,
660 [VIS_ACTION_LATER] = {
661 "editor-later",
662 "Goto newer text state",
663 later,
665 [VIS_ACTION_MACRO_RECORD] = {
666 "macro-record",
667 "Record macro into given register",
668 macro_record,
670 [VIS_ACTION_MACRO_REPLAY] = {
671 "macro-replay",
672 "Replay macro, execute the content of the given register",
673 macro_replay,
675 [VIS_ACTION_MARK_SET] = {
676 "mark-set",
677 "Set given mark at current cursor position",
678 mark_set,
680 [VIS_ACTION_MARK_GOTO] = {
681 "mark-goto",
682 "Goto the position of the given mark",
683 mark_motion, { .i = VIS_MOVE_MARK }
685 [VIS_ACTION_MARK_GOTO_LINE] = {
686 "mark-goto-line",
687 "Goto first non-blank character of the line containing the given mark",
688 mark_motion, { .i = VIS_MOVE_MARK_LINE }
690 [VIS_ACTION_REDRAW] = {
691 "editor-redraw",
692 "Redraw current editor content",
693 call, { .f = vis_redraw }
695 [VIS_ACTION_REPLACE_CHAR] = {
696 "replace-char",
697 "Replace the character under the cursor",
698 replace,
700 [VIS_ACTION_TOTILL_REPEAT] = {
701 "totill-repeat",
702 "Repeat latest to/till motion",
703 movement, { .i = VIS_MOVE_TOTILL_REPEAT }
705 [VIS_ACTION_TOTILL_REVERSE] = {
706 "totill-reverse",
707 "Repeat latest to/till motion but in opposite direction",
708 movement, { .i = VIS_MOVE_TOTILL_REVERSE }
710 [VIS_ACTION_PROMPT_SEARCH_FORWARD] = {
711 "search-forward",
712 "Search forward",
713 prompt_show, { .s = "/" }
715 [VIS_ACTION_PROMPT_SEARCH_BACKWARD] = {
716 "search-backward",
717 "Search backward",
718 prompt_show, { .s = "?" }
720 [VIS_ACTION_TILL_LEFT] = {
721 "till-left",
722 "Till after the occurrence of character to the left",
723 movement_key, { .i = VIS_MOVE_LEFT_TILL }
725 [VIS_ACTION_TILL_RIGHT] = {
726 "till-right",
727 "Till before the occurrence of character to the right",
728 movement_key, { .i = VIS_MOVE_RIGHT_TILL }
730 [VIS_ACTION_TO_LEFT] = {
731 "to-left",
732 "To the first occurrence of character to the left",
733 movement_key, { .i = VIS_MOVE_LEFT_TO }
735 [VIS_ACTION_TO_RIGHT] = {
736 "to-right",
737 "To the first occurrence of character to the right",
738 movement_key, { .i = VIS_MOVE_RIGHT_TO }
740 [VIS_ACTION_REGISTER] = {
741 "register",
742 "Use given register for next operator",
743 reg,
745 [VIS_ACTION_OPERATOR_CHANGE] = {
746 "vis-operator-change",
747 "Change operator",
748 operator, { .i = VIS_OP_CHANGE }
750 [VIS_ACTION_OPERATOR_DELETE] = {
751 "vis-operator-delete",
752 "Delete operator",
753 operator, { .i = VIS_OP_DELETE }
755 [VIS_ACTION_OPERATOR_YANK] = {
756 "vis-operator-yank",
757 "Yank operator",
758 operator, { .i = VIS_OP_YANK }
760 [VIS_ACTION_OPERATOR_SHIFT_LEFT] = {
761 "vis-operator-shift-left",
762 "Shift left operator",
763 operator, { .i = VIS_OP_SHIFT_LEFT }
765 [VIS_ACTION_OPERATOR_SHIFT_RIGHT] = {
766 "vis-operator-shift-right",
767 "Shift right operator",
768 operator, { .i = VIS_OP_SHIFT_RIGHT }
770 [VIS_ACTION_OPERATOR_CASE_LOWER] = {
771 "vis-operator-case-lower",
772 "Lowercase operator",
773 operator, { .i = VIS_OP_CASE_LOWER }
775 [VIS_ACTION_OPERATOR_CASE_UPPER] = {
776 "vis-operator-case-upper",
777 "Uppercase operator",
778 operator, { .i = VIS_OP_CASE_UPPER }
780 [VIS_ACTION_OPERATOR_CASE_SWAP] = {
781 "vis-operator-case-swap",
782 "Swap case operator",
783 operator, { .i = VIS_OP_CASE_SWAP }
785 [VIS_ACTION_OPERATOR_FILTER] = {
786 "vis-operator-filter",
787 "Filter operator",
788 operator_filter,
790 [VIS_ACTION_OPERATOR_FILTER_FMT] = {
791 "vis-operator-filter-format",
792 "Formating operator, filter range through fmt(1)",
793 operator_filter, { .s = "|fmt" }
795 [VIS_ACTION_COUNT] = {
796 "vis-count",
797 "Count specifier",
798 count,
800 [VIS_ACTION_INSERT_NEWLINE] = {
801 "insert-newline",
802 "Insert a line break (depending on file type)",
803 call, { .f = vis_insert_nl }
805 [VIS_ACTION_INSERT_TAB] = {
806 "insert-tab",
807 "Insert a tab (might be converted to spaces)",
808 call, { .f = vis_insert_tab }
810 [VIS_ACTION_INSERT_VERBATIM] = {
811 "insert-verbatim",
812 "Insert Unicode character based on code point",
813 insert_verbatim,
815 [VIS_ACTION_INSERT_REGISTER] = {
816 "insert-register",
817 "Insert specified register content",
818 insert_register,
820 [VIS_ACTION_WINDOW_NEXT] = {
821 "window-next",
822 "Focus next window",
823 call, { .f = vis_window_next }
825 [VIS_ACTION_WINDOW_PREV] = {
826 "window-prev",
827 "Focus previous window",
828 call, { .f = vis_window_prev }
830 [VIS_ACTION_APPEND_CHAR_NEXT] = {
831 "append-char-next",
832 "Append text after the cursor",
833 insertmode, { .i = VIS_MOVE_LINE_CHAR_NEXT }
835 [VIS_ACTION_APPEND_LINE_END] = {
836 "append-line-end",
837 "Append text after the end of the line",
838 insertmode, { .i = VIS_MOVE_LINE_END },
840 [VIS_ACTION_INSERT_LINE_START] = {
841 "insert-line-start",
842 "Insert text before the first non-blank in the line",
843 insertmode, { .i = VIS_MOVE_LINE_START },
845 [VIS_ACTION_OPEN_LINE_ABOVE] = {
846 "open-line-above",
847 "Begin a new line above the cursor",
848 openline, { .i = -1 }
850 [VIS_ACTION_OPEN_LINE_BELOW] = {
851 "open-line-below",
852 "Begin a new line below the cursor",
853 openline, { .i = +1 }
855 [VIS_ACTION_JOIN_LINE_BELOW] = {
856 "join-line-below",
857 "Join line(s)",
858 join, { .i = VIS_MOVE_LINE_NEXT },
860 [VIS_ACTION_JOIN_LINES] = {
861 "join-lines",
862 "Join selected lines",
863 operator, { .i = VIS_OP_JOIN }
865 [VIS_ACTION_PROMPT_SHOW] = {
866 "prompt-show",
867 "Show editor command line prompt",
868 prompt_show, { .s = ":" }
870 [VIS_ACTION_REPEAT] = {
871 "editor-repeat",
872 "Repeat latest editor command",
873 repeat
875 [VIS_ACTION_SELECTION_FLIP] = {
876 "selection-flip",
877 "Flip selection, move cursor to other end",
878 selection_end,
880 [VIS_ACTION_SELECTION_RESTORE] = {
881 "selection-restore",
882 "Restore last selection",
883 selection_restore,
885 [VIS_ACTION_WINDOW_REDRAW_TOP] = {
886 "window-redraw-top",
887 "Redraw cursor line at the top of the window",
888 window, { .w = view_redraw_top }
890 [VIS_ACTION_WINDOW_REDRAW_CENTER] = {
891 "window-redraw-center",
892 "Redraw cursor line at the center of the window",
893 window, { .w = view_redraw_center }
895 [VIS_ACTION_WINDOW_REDRAW_BOTTOM] = {
896 "window-redraw-bottom",
897 "Redraw cursor line at the bottom of the window",
898 window, { .w = view_redraw_bottom }
900 [VIS_ACTION_WINDOW_SLIDE_UP] = {
901 "window-slide-up",
902 "Slide window content upwards",
903 wslide, { .i = -1 }
905 [VIS_ACTION_WINDOW_SLIDE_DOWN] = {
906 "window-slide-down",
907 "Slide window content downwards",
908 wslide, { .i = +1 }
910 [VIS_ACTION_PUT_AFTER] = {
911 "put-after",
912 "Put text after the cursor",
913 operator, { .i = VIS_OP_PUT_AFTER }
915 [VIS_ACTION_PUT_BEFORE] = {
916 "put-before",
917 "Put text before the cursor",
918 operator, { .i = VIS_OP_PUT_BEFORE }
920 [VIS_ACTION_PUT_AFTER_END] = {
921 "put-after-end",
922 "Put text after the cursor, place cursor after new text",
923 operator, { .i = VIS_OP_PUT_AFTER_END }
925 [VIS_ACTION_PUT_BEFORE_END] = {
926 "put-before-end",
927 "Put text before the cursor, place cursor after new text",
928 operator, { .i = VIS_OP_PUT_BEFORE_END }
930 [VIS_ACTION_CURSOR_SELECT_WORD] = {
931 "cursors-select-word",
932 "Select word under cursor",
933 cursors_select,
935 [VIS_ACTION_CURSORS_NEW_LINE_ABOVE] = {
936 "cursors-new-lines-above",
937 "Create a new cursor on the line above",
938 cursors_new, { .i = -1 }
940 [VIS_ACTION_CURSORS_NEW_LINE_ABOVE_FIRST] = {
941 "cursors-new-lines-above-first",
942 "Create a new cursor on the line above the first cursor",
943 cursors_new, { .i = INT_MIN }
945 [VIS_ACTION_CURSORS_NEW_LINE_BELOW] = {
946 "cursor-new-lines-below",
947 "Create a new cursor on the line below",
948 cursors_new, { .i = +1 }
950 [VIS_ACTION_CURSORS_NEW_LINE_BELOW_LAST] = {
951 "cursor-new-lines-below-last",
952 "Create a new cursor on the line below the last cursor",
953 cursors_new, { .i = INT_MAX }
955 [VIS_ACTION_CURSORS_NEW_LINES_BEGIN] = {
956 "cursors-new-lines-begin",
957 "Create a new cursor at the start of every line covered by selection",
958 operator, { .i = VIS_OP_CURSOR_SOL }
960 [VIS_ACTION_CURSORS_NEW_LINES_END] = {
961 "cursors-new-lines-end",
962 "Create a new cursor at the end of every line covered by selection",
963 operator, { .i = VIS_OP_CURSOR_EOL }
965 [VIS_ACTION_CURSORS_NEW_MATCH_NEXT] = {
966 "cursors-new-match-next",
967 "Select the next region matching the current selection",
968 cursors_select_next
970 [VIS_ACTION_CURSORS_NEW_MATCH_SKIP] = {
971 "cursors-new-match-skip",
972 "Clear current selection, but select next match",
973 cursors_select_skip,
975 [VIS_ACTION_CURSORS_ALIGN] = {
976 "cursors-align",
977 "Try to align all cursors on the same column",
978 cursors_align,
980 [VIS_ACTION_CURSORS_ALIGN_INDENT_LEFT] = {
981 "cursors-align-indent-left",
982 "Left align all cursors/selections by inserting spaces",
983 cursors_align_indent, { .i = -1 }
985 [VIS_ACTION_CURSORS_ALIGN_INDENT_RIGHT] = {
986 "cursors-align-indent-right",
987 "Right align all cursors/selections by inserting spaces",
988 cursors_align_indent, { .i = +1 }
990 [VIS_ACTION_CURSORS_REMOVE_ALL] = {
991 "cursors-remove-all",
992 "Remove all but the primary cursor",
993 cursors_clear,
995 [VIS_ACTION_CURSORS_REMOVE_LAST] = {
996 "cursors-remove-last",
997 "Remove least recently created cursor",
998 cursors_remove,
1000 [VIS_ACTION_CURSORS_REMOVE_COLUMN] = {
1001 "cursors-remove-column",
1002 "Remove count cursor column",
1003 cursors_remove_column, { .i = 1 }
1005 [VIS_ACTION_CURSORS_REMOVE_COLUMN_EXCEPT] = {
1006 "cursors-remove-column-except",
1007 "Remove all but the count cursor column",
1008 cursors_remove_column_except, { .i = 1 }
1010 [VIS_ACTION_CURSORS_PREV] = {
1011 "cursors-prev",
1012 "Move to the previous cursor",
1013 cursors_navigate, { .i = -PAGE_HALF }
1015 [VIS_ACTION_CURSORS_NEXT] = {
1016 "cursors-next",
1017 "Move to the next cursor",
1018 cursors_navigate, { .i = +PAGE_HALF }
1020 [VIS_ACTION_SELECTIONS_ROTATE_LEFT] = {
1021 "selections-rotate-left",
1022 "Rotate selections left",
1023 selections_rotate, { .i = -1 }
1025 [VIS_ACTION_SELECTIONS_ROTATE_RIGHT] = {
1026 "selections-rotate-right",
1027 "Rotate selections right",
1028 selections_rotate, { .i = +1 }
1030 [VIS_ACTION_SELECTIONS_TRIM] = {
1031 "selections-trim",
1032 "Remove leading and trailing white space from selections",
1033 selections_trim
1035 [VIS_ACTION_TEXT_OBJECT_WORD_OUTER] = {
1036 "text-object-word-outer",
1037 "A word leading and trailing whitespace included",
1038 textobj, { .i = VIS_TEXTOBJECT_OUTER_WORD }
1040 [VIS_ACTION_TEXT_OBJECT_WORD_INNER] = {
1041 "text-object-word-inner",
1042 "A word leading and trailing whitespace excluded",
1043 textobj, { .i = VIS_TEXTOBJECT_INNER_WORD }
1045 [VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER] = {
1046 "text-object-longword-outer",
1047 "A WORD leading and trailing whitespace included",
1048 textobj, { .i = VIS_TEXTOBJECT_OUTER_LONGWORD }
1050 [VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER] = {
1051 "text-object-longword-inner",
1052 "A WORD leading and trailing whitespace excluded",
1053 textobj, { .i = VIS_TEXTOBJECT_INNER_LONGWORD }
1055 [VIS_ACTION_TEXT_OBJECT_SENTENCE] = {
1056 "text-object-sentence",
1057 "A sentence",
1058 textobj, { .i = VIS_TEXTOBJECT_SENTENCE }
1060 [VIS_ACTION_TEXT_OBJECT_PARAGRAPH] = {
1061 "text-object-paragraph",
1062 "A paragraph",
1063 textobj, { .i = VIS_TEXTOBJECT_PARAGRAPH }
1065 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER] = {
1066 "text-object-square-bracket-outer",
1067 "[] block (outer variant)",
1068 textobj, { .i = VIS_TEXTOBJECT_OUTER_SQUARE_BRACKET }
1070 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER] = {
1071 "text-object-square-bracket-inner",
1072 "[] block (inner variant)",
1073 textobj, { .i = VIS_TEXTOBJECT_INNER_SQUARE_BRACKET }
1075 [VIS_ACTION_TEXT_OBJECT_PARANTHESE_OUTER] = {
1076 "text-object-parentheses-outer",
1077 "() block (outer variant)",
1078 textobj, { .i = VIS_TEXTOBJECT_OUTER_PARANTHESE }
1080 [VIS_ACTION_TEXT_OBJECT_PARANTHESE_INNER] = {
1081 "text-object-parentheses-inner",
1082 "() block (inner variant)",
1083 textobj, { .i = VIS_TEXTOBJECT_INNER_PARANTHESE }
1085 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER] = {
1086 "text-object-angle-bracket-outer",
1087 "<> block (outer variant)",
1088 textobj, { .i = VIS_TEXTOBJECT_OUTER_ANGLE_BRACKET }
1090 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER] = {
1091 "text-object-angle-bracket-inner",
1092 "<> block (inner variant)",
1093 textobj, { .i = VIS_TEXTOBJECT_INNER_ANGLE_BRACKET }
1095 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER] = {
1096 "text-object-curly-bracket-outer",
1097 "{} block (outer variant)",
1098 textobj, { .i = VIS_TEXTOBJECT_OUTER_CURLY_BRACKET }
1100 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER] = {
1101 "text-object-curly-bracket-inner",
1102 "{} block (inner variant)",
1103 textobj, { .i = VIS_TEXTOBJECT_INNER_CURLY_BRACKET }
1105 [VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER] = {
1106 "text-object-quote-outer",
1107 "A quoted string, including the quotation marks",
1108 textobj, { .i = VIS_TEXTOBJECT_OUTER_QUOTE }
1110 [VIS_ACTION_TEXT_OBJECT_QUOTE_INNER] = {
1111 "text-object-quote-inner",
1112 "A quoted string, excluding the quotation marks",
1113 textobj, { .i = VIS_TEXTOBJECT_INNER_QUOTE }
1115 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER] = {
1116 "text-object-single-quote-outer",
1117 "A single quoted string, including the quotation marks",
1118 textobj, { .i = VIS_TEXTOBJECT_OUTER_SINGLE_QUOTE }
1120 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER] = {
1121 "text-object-single-quote-inner",
1122 "A single quoted string, excluding the quotation marks",
1123 textobj, { .i = VIS_TEXTOBJECT_INNER_SINGLE_QUOTE }
1125 [VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER] = {
1126 "text-object-backtick-outer",
1127 "A backtick delimited string (outer variant)",
1128 textobj, { .i = VIS_TEXTOBJECT_OUTER_BACKTICK }
1130 [VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER] = {
1131 "text-object-backtick-inner",
1132 "A backtick delimited string (inner variant)",
1133 textobj, { .i = VIS_TEXTOBJECT_INNER_BACKTICK }
1135 [VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER] = {
1136 "text-object-entire-outer",
1137 "The whole text content",
1138 textobj, { .i = VIS_TEXTOBJECT_OUTER_ENTIRE }
1140 [VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER] = {
1141 "text-object-entire-inner",
1142 "The whole text content, except for leading and trailing empty lines",
1143 textobj, { .i = VIS_TEXTOBJECT_INNER_ENTIRE }
1145 [VIS_ACTION_TEXT_OBJECT_FUNCTION_OUTER] = {
1146 "text-object-function-outer",
1147 "A whole C-like function",
1148 textobj, { .i = VIS_TEXTOBJECT_OUTER_FUNCTION }
1150 [VIS_ACTION_TEXT_OBJECT_FUNCTION_INNER] = {
1151 "text-object-function-inner",
1152 "A whole C-like function body",
1153 textobj, { .i = VIS_TEXTOBJECT_INNER_FUNCTION }
1155 [VIS_ACTION_TEXT_OBJECT_LINE_OUTER] = {
1156 "text-object-line-outer",
1157 "The whole line",
1158 textobj, { .i = VIS_TEXTOBJECT_OUTER_LINE }
1160 [VIS_ACTION_TEXT_OBJECT_LINE_INNER] = {
1161 "text-object-line-inner",
1162 "The whole line, excluding leading and trailing whitespace",
1163 textobj, { .i = VIS_TEXTOBJECT_INNER_LINE }
1165 [VIS_ACTION_TEXT_OBJECT_INDENTATION] = {
1166 "text-object-indentation",
1167 "All adjacent lines with the same indentation level as the current one",
1168 textobj, { .i = VIS_TEXTOBJECT_INDENTATION }
1170 [VIS_ACTION_TEXT_OBJECT_SEARCH_FORWARD] = {
1171 "text-object-search-forward",
1172 "The next search match in forward direction",
1173 textobj, { .i = VIS_TEXTOBJECT_SEARCH_FORWARD }
1175 [VIS_ACTION_TEXT_OBJECT_SEARCH_BACKWARD] = {
1176 "text-object-search-backward",
1177 "The next search match in backward direction",
1178 textobj, { .i = VIS_TEXTOBJECT_SEARCH_BACKWARD }
1180 [VIS_ACTION_MOTION_CHARWISE] = {
1181 "motion-charwise",
1182 "Force motion to be charwise",
1183 motiontype, { .i = VIS_MOTIONTYPE_CHARWISE }
1185 [VIS_ACTION_MOTION_LINEWISE] = {
1186 "motion-linewise",
1187 "Force motion to be linewise",
1188 motiontype, { .i = VIS_MOTIONTYPE_LINEWISE }
1190 [VIS_ACTION_UNICODE_INFO] = {
1191 "unicode-info",
1192 "Show Unicode codepoint(s) of character under cursor",
1193 unicode_info,
1195 [VIS_ACTION_NUMBER_INCREMENT] = {
1196 "number-increment",
1197 "Increment number under cursor",
1198 number_increment_decrement, { .i = +1 }
1200 [VIS_ACTION_NUMBER_DECREMENT] = {
1201 "number-decrement",
1202 "Decrement number under cursor",
1203 number_increment_decrement, { .i = -1 }
1205 [VIS_ACTION_OPEN_FILE_UNDER_CURSOR] = {
1206 "open-file-under-cursor",
1207 "Open file under the cursor",
1208 open_file_under_cursor, { .b = false }
1210 [VIS_ACTION_OPEN_FILE_UNDER_CURSOR_NEW_WINDOW] = {
1211 "open-file-under-cursor-new-window",
1212 "Open file under the cursor in a new window",
1213 open_file_under_cursor, { .b = true }
1215 [VIS_ACTION_NOP] = {
1216 "nop",
1217 "Ignore key, do nothing",
1218 nop,
1222 #include "config.h"
1224 /** key bindings functions */
1226 static const char *nop(Vis *vis, const char *keys, const Arg *arg) {
1227 return keys;
1230 static const char *key2register(Vis *vis, const char *keys, enum VisRegister *reg) {
1231 *reg = VIS_REG_INVALID;
1232 if (!keys[0])
1233 return NULL;
1234 if ('a' <= keys[0] && keys[0] <= 'z')
1235 *reg = keys[0] - 'a';
1236 else if ('A' <= keys[0] && keys[0] <= 'Z')
1237 *reg = VIS_REG_A + keys[0] - 'A';
1238 else if (keys[0] == '*' || keys[0] == '+')
1239 *reg = VIS_REG_CLIPBOARD;
1240 else if (keys[0] == '_')
1241 *reg = VIS_REG_BLACKHOLE;
1242 else if (keys[0] == '0')
1243 *reg = VIS_REG_ZERO;
1244 else if (keys[0] == '@')
1245 *reg = VIS_MACRO_LAST_RECORDED;
1246 else if (keys[0] == '/')
1247 *reg = VIS_REG_SEARCH;
1248 else if (keys[0] == ':')
1249 *reg = VIS_REG_COMMAND;
1250 return keys+1;
1253 static const char *macro_record(Vis *vis, const char *keys, const Arg *arg) {
1254 if (!vis_macro_record_stop(vis)) {
1255 enum VisRegister reg;
1256 keys = key2register(vis, keys, &reg);
1257 vis_macro_record(vis, reg);
1259 vis_draw(vis);
1260 return keys;
1263 static const char *macro_replay(Vis *vis, const char *keys, const Arg *arg) {
1264 enum VisRegister reg;
1265 keys = key2register(vis, keys, &reg);
1266 vis_macro_replay(vis, reg);
1267 return keys;
1270 static const char *suspend(Vis *vis, const char *keys, const Arg *arg) {
1271 vis_suspend(vis);
1272 return keys;
1275 static const char *repeat(Vis *vis, const char *keys, const Arg *arg) {
1276 vis_repeat(vis);
1277 return keys;
1280 static const char *cursors_new(Vis *vis, const char *keys, const Arg *arg) {
1281 View *view = vis_view(vis);
1282 for (int count = vis_count_get_default(vis, 1); count > 0; count--) {
1283 Cursor *cursor = NULL;
1284 switch (arg->i) {
1285 case -1:
1286 case +1:
1287 cursor = view_cursors_primary_get(view);
1288 break;
1289 case INT_MIN:
1290 cursor = view_cursors(view);
1291 break;
1292 case INT_MAX:
1293 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c))
1294 cursor = c;
1295 break;
1296 default:
1297 return keys;
1299 size_t oldpos = view_cursors_pos(cursor);
1300 if (arg->i > 0)
1301 view_line_down(cursor);
1302 else if (arg->i < 0)
1303 view_line_up(cursor);
1304 size_t newpos = view_cursors_pos(cursor);
1305 view_cursors_to(cursor, oldpos);
1306 if (!view_cursors_new(view, newpos)) {
1307 if (arg->i == -1) {
1308 cursor = view_cursors_prev(cursor);
1309 } else if (arg->i == +1) {
1310 cursor = view_cursors_next(cursor);
1312 view_cursors_primary_set(cursor);
1315 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1316 return keys;
1319 static const char *cursors_align(Vis *vis, const char *keys, const Arg *arg) {
1320 View *view = vis_view(vis);
1321 Text *txt = vis_text(vis);
1322 int mincol = INT_MAX;
1323 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
1324 int col = view_cursors_cell_get(c);
1325 if (col >= 0 && col < mincol)
1326 mincol = col;
1328 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
1329 if (view_cursors_cell_set(c, mincol) == -1) {
1330 size_t pos = view_cursors_pos(c);
1331 size_t col = text_line_width_set(txt, pos, mincol);
1332 view_cursors_to(c, col);
1335 return keys;
1338 static const char *cursors_align_indent(Vis *vis, const char *keys, const Arg *arg) {
1339 View *view = vis_view(vis);
1340 Text *txt = vis_text(vis);
1341 bool left_align = arg->i < 0;
1342 int columns = view_cursors_column_count(view);
1344 for (int i = 0; i < columns; i++) {
1345 int mincol = INT_MAX, maxcol = 0;
1346 for (Cursor *c = view_cursors_column(view, i); c; c = view_cursors_column_next(c, i)) {
1347 size_t pos;
1348 Filerange sel = view_cursors_selection_get(c);
1349 if (text_range_valid(&sel))
1350 pos = left_align ? sel.start : sel.end;
1351 else
1352 pos = view_cursors_pos(c);
1353 int col = text_line_width_get(txt, pos);
1354 if (col < mincol)
1355 mincol = col;
1356 if (col > maxcol)
1357 maxcol = col;
1360 size_t len = maxcol - mincol;
1361 char *buf = malloc(len+1);
1362 if (!buf)
1363 return keys;
1364 memset(buf, ' ', len);
1366 for (Cursor *c = view_cursors_column(view, i); c; c = view_cursors_column_next(c, i)) {
1367 size_t pos, ipos;
1368 Filerange sel = view_cursors_selection_get(c);
1369 if (text_range_valid(&sel)) {
1370 pos = left_align ? sel.start : sel.end;
1371 ipos = sel.start;
1372 } else {
1373 pos = view_cursors_pos(c);
1374 ipos = pos;
1376 int col = text_line_width_get(txt, pos);
1377 if (col < maxcol) {
1378 size_t off = maxcol - col;
1379 if (off <= len)
1380 text_insert(txt, ipos, buf, off);
1384 free(buf);
1387 view_draw(view);
1388 return keys;
1391 static const char *cursors_clear(Vis *vis, const char *keys, const Arg *arg) {
1392 View *view = vis_view(vis);
1393 if (view_cursors_multiple(view))
1394 view_cursors_clear(view);
1395 else
1396 view_cursors_selection_clear(view_cursors_primary_get(view));
1397 return keys;
1400 static const char *cursors_select(Vis *vis, const char *keys, const Arg *arg) {
1401 Text *txt = vis_text(vis);
1402 View *view = vis_view(vis);
1403 for (Cursor *cursor = view_cursors(view); cursor; cursor = view_cursors_next(cursor)) {
1404 Filerange sel = view_cursors_selection_get(cursor);
1405 Filerange word = text_object_word(txt, view_cursors_pos(cursor));
1406 if (!text_range_valid(&sel) && text_range_valid(&word)) {
1407 view_cursors_selection_set(cursor, &word);
1408 view_cursors_to(cursor, text_char_prev(txt, word.end));
1411 vis_mode_switch(vis, VIS_MODE_VISUAL);
1412 return keys;
1415 static const char *cursors_select_next(Vis *vis, const char *keys, const Arg *arg) {
1416 Text *txt = vis_text(vis);
1417 View *view = vis_view(vis);
1418 Cursor *cursor = view_cursors_primary_get(view);
1419 Filerange sel = view_cursors_selection_get(cursor);
1420 if (!text_range_valid(&sel))
1421 return keys;
1423 char *buf = text_bytes_alloc0(txt, sel.start, text_range_size(&sel));
1424 if (!buf)
1425 return keys;
1426 Filerange word = text_object_word_find_next(txt, sel.end, buf);
1427 free(buf);
1429 if (text_range_valid(&word)) {
1430 size_t pos = text_char_prev(txt, word.end);
1431 cursor = view_cursors_new(view, pos);
1432 if (!cursor)
1433 return keys;
1434 view_cursors_selection_set(cursor, &word);
1436 return keys;
1439 static const char *cursors_select_skip(Vis *vis, const char *keys, const Arg *arg) {
1440 View *view = vis_view(vis);
1441 Cursor *cursor = view_cursors_primary_get(view);
1442 keys = cursors_select_next(vis, keys, arg);
1443 if (cursor != view_cursors_primary_get(view))
1444 view_cursors_dispose(cursor);
1445 return keys;
1448 static const char *cursors_remove(Vis *vis, const char *keys, const Arg *arg) {
1449 View *view = vis_view(vis);
1450 view_cursors_dispose(view_cursors_primary_get(view));
1451 view_cursor_to(view, view_cursor_get(view));
1452 return keys;
1455 static const char *cursors_remove_column(Vis *vis, const char *keys, const Arg *arg) {
1456 View *view = vis_view(vis);
1457 int max = view_cursors_column_count(view);
1458 int column = vis_count_get_default(vis, arg->i) - 1;
1459 if (column >= max)
1460 column = max - 1;
1461 if (!view_cursors_multiple(view)) {
1462 vis_mode_switch(vis, VIS_MODE_NORMAL);
1463 return keys;
1466 for (Cursor *c = view_cursors_column(view, column), *next; c; c = next) {
1467 next = view_cursors_column_next(c, column);
1468 view_cursors_dispose(c);
1471 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1472 return keys;
1475 static const char *cursors_remove_column_except(Vis *vis, const char *keys, const Arg *arg) {
1476 View *view = vis_view(vis);
1477 int max = view_cursors_column_count(view);
1478 int column = vis_count_get_default(vis, arg->i) - 1;
1479 if (column >= max)
1480 column = max - 1;
1481 if (!view_cursors_multiple(view)) {
1482 vis_redraw(vis);
1483 return keys;
1486 Cursor *cur = view_cursors(view);
1487 Cursor *col = view_cursors_column(view, column);
1488 for (Cursor *next; cur; cur = next) {
1489 next = view_cursors_next(cur);
1490 if (cur == col)
1491 col = view_cursors_column_next(col, column);
1492 else
1493 view_cursors_dispose(cur);
1496 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1497 return keys;
1500 static const char *cursors_navigate(Vis *vis, const char *keys, const Arg *arg) {
1501 View *view = vis_view(vis);
1502 if (!view_cursors_multiple(view)) {
1503 Filerange sel = view_selection_get(view);
1504 if (!text_range_valid(&sel))
1505 return wscroll(vis, keys, arg);
1506 return keys;
1508 Cursor *c = view_cursors_primary_get(view);
1509 for (int count = vis_count_get_default(vis, 1); count > 0; count--) {
1510 if (arg->i > 0) {
1511 c = view_cursors_next(c);
1512 if (!c)
1513 c = view_cursors(view);
1514 } else {
1515 c = view_cursors_prev(c);
1516 if (!c) {
1517 c = view_cursors(view);
1518 for (Cursor *n = c; n; n = view_cursors_next(n))
1519 c = n;
1523 view_cursors_primary_set(c);
1524 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1525 return keys;
1528 static const char *selections_rotate(Vis *vis, const char *keys, const Arg *arg) {
1530 typedef struct {
1531 Cursor *cursor;
1532 char *data;
1533 size_t len;
1534 } Rotate;
1536 Array arr;
1537 Text *txt = vis_text(vis);
1538 View *view = vis_view(vis);
1539 int columns = view_cursors_column_count(view);
1540 int selections = columns == 1 ? view_cursors_count(view) : columns;
1541 int count = vis_count_get_default(vis, 1);
1542 array_init_sized(&arr, sizeof(Rotate));
1543 if (!array_reserve(&arr, selections))
1544 return keys;
1545 size_t line = 0;
1547 for (Cursor *c = view_cursors(view), *next; c; c = next) {
1548 next = view_cursors_next(c);
1549 size_t line_next = 0;
1551 Filerange sel = view_cursors_selection_get(c);
1552 Rotate rot;
1553 rot.cursor = c;
1554 rot.len = text_range_size(&sel);
1555 if ((rot.data = malloc(rot.len)))
1556 rot.len = text_bytes_get(txt, sel.start, rot.len, rot.data);
1557 else
1558 rot.len = 0;
1559 array_add(&arr, &rot);
1561 if (!line)
1562 line = text_lineno_by_pos(txt, view_cursors_pos(c));
1563 if (next)
1564 line_next = text_lineno_by_pos(txt, view_cursors_pos(next));
1565 if (!next || (columns > 1 && line != line_next)) {
1566 size_t len = array_length(&arr);
1567 size_t off = arg->i > 0 ? count % len : len - (count % len);
1568 for (size_t i = 0; i < len; i++) {
1569 size_t j = (i + off) % len;
1570 Rotate *oldrot = array_get(&arr, i);
1571 Rotate *newrot = array_get(&arr, j);
1572 if (!oldrot || !newrot || oldrot == newrot)
1573 continue;
1574 Filerange newsel = view_cursors_selection_get(newrot->cursor);
1575 if (!text_range_valid(&newsel))
1576 continue;
1577 if (!text_delete_range(txt, &newsel))
1578 continue;
1579 if (!text_insert(txt, newsel.start, oldrot->data, oldrot->len))
1580 continue;
1581 newsel.end = newsel.start + oldrot->len;
1582 view_cursors_selection_set(newrot->cursor, &newsel);
1583 view_cursors_selection_sync(newrot->cursor);
1584 free(oldrot->data);
1586 array_clear(&arr);
1588 line = line_next;
1591 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1592 return keys;
1595 static const char *selections_trim(Vis *vis, const char *keys, const Arg *arg) {
1596 Text *txt = vis_text(vis);
1597 View *view = vis_view(vis);
1598 for (Cursor *c = view_cursors(view), *next; c; c = next) {
1599 next = view_cursors_next(c);
1600 Filerange sel = view_cursors_selection_get(c);
1601 if (!text_range_valid(&sel))
1602 continue;
1603 for (char b; sel.start < sel.end && text_byte_get(txt, sel.end-1, &b)
1604 && isspace((unsigned char)b); sel.end--);
1605 for (char b; sel.start <= sel.end && text_byte_get(txt, sel.start, &b)
1606 && isspace((unsigned char)b); sel.start++);
1607 if (sel.start < sel.end) {
1608 view_cursors_selection_set(c, &sel);
1609 view_cursors_selection_sync(c);
1610 } else if (!view_cursors_dispose(c)) {
1611 vis_mode_switch(vis, VIS_MODE_NORMAL);
1614 return keys;
1617 static const char *replace(Vis *vis, const char *keys, const Arg *arg) {
1618 if (!keys[0])
1619 return NULL;
1620 const char *next = vis_keys_next(vis, keys);
1621 if (!next)
1622 return NULL;
1623 vis_operator(vis, VIS_OP_REPLACE);
1624 vis_motion(vis, VIS_MOVE_NOP);
1625 vis_keys_feed(vis, keys);
1626 vis_keys_feed(vis, "<Escape>");
1627 return next;
1630 static const char *count(Vis *vis, const char *keys, const Arg *arg) {
1631 int digit = keys[-1] - '0';
1632 int count = vis_count_get_default(vis, 0);
1633 if (0 <= digit && digit <= 9) {
1634 if (digit == 0 && count == 0)
1635 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
1636 vis_count_set(vis, count * 10 + digit);
1638 return keys;
1641 static const char *gotoline(Vis *vis, const char *keys, const Arg *arg) {
1642 if (vis_count_get(vis) != VIS_COUNT_UNKNOWN)
1643 vis_motion(vis, VIS_MOVE_LINE);
1644 else if (arg->i < 0)
1645 vis_motion(vis, VIS_MOVE_FILE_BEGIN);
1646 else
1647 vis_motion(vis, VIS_MOVE_FILE_END);
1648 return keys;
1651 static const char *motiontype(Vis *vis, const char *keys, const Arg *arg) {
1652 vis_motion_type(vis, arg->i);
1653 return keys;
1656 static const char *operator(Vis *vis, const char *keys, const Arg *arg) {
1657 vis_operator(vis, arg->i);
1658 return keys;
1661 static const char *operator_filter(Vis *vis, const char *keys, const Arg *arg) {
1662 vis_operator(vis, VIS_OP_FILTER, arg->s);
1663 return keys;
1666 static const char *movement_key(Vis *vis, const char *keys, const Arg *arg) {
1667 char key[32];
1668 const char *next;
1669 if (!keys[0] || !(next = vis_keys_next(vis, keys)))
1670 return NULL;
1671 size_t len = next - keys;
1672 if (len < sizeof key) {
1673 strncpy(key, keys, len);
1674 key[len] = '\0';
1675 vis_motion(vis, arg->i, key);
1677 return next;
1680 static const char *movement(Vis *vis, const char *keys, const Arg *arg) {
1681 vis_motion(vis, arg->i);
1682 return keys;
1685 static const char *textobj(Vis *vis, const char *keys, const Arg *arg) {
1686 vis_textobject(vis, arg->i);
1687 return keys;
1690 static const char *selection_end(Vis *vis, const char *keys, const Arg *arg) {
1691 for (Cursor *c = view_cursors(vis_view(vis)); c; c = view_cursors_next(c))
1692 view_cursors_selection_swap(c);
1693 return keys;
1696 static const char *selection_restore(Vis *vis, const char *keys, const Arg *arg) {
1697 Text *txt = vis_text(vis);
1698 View *view = vis_view(vis);
1699 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c))
1700 view_cursors_selection_restore(c);
1701 Filerange sel = view_selection_get(view);
1702 if (text_range_is_linewise(txt, &sel))
1703 vis_mode_switch(vis, VIS_MODE_VISUAL_LINE);
1704 else
1705 vis_mode_switch(vis, VIS_MODE_VISUAL);
1706 return keys;
1709 static const char *reg(Vis *vis, const char *keys, const Arg *arg) {
1710 enum VisRegister reg;
1711 keys = key2register(vis, keys, &reg);
1712 vis_register_set(vis, reg);
1713 return keys;
1716 static const char *key2mark(Vis *vis, const char *keys, int *mark) {
1717 *mark = VIS_MARK_INVALID;
1718 if (!keys[0])
1719 return NULL;
1720 if (keys[0] >= 'a' && keys[0] <= 'z')
1721 *mark = keys[0] - 'a';
1722 else if (keys[0] == '<')
1723 *mark = VIS_MARK_SELECTION_START;
1724 else if (keys[0] == '>')
1725 *mark = VIS_MARK_SELECTION_END;
1726 return keys+1;
1729 static const char *mark_set(Vis *vis, const char *keys, const Arg *arg) {
1730 int mark;
1731 keys = key2mark(vis, keys, &mark);
1732 vis_mark_set(vis, mark, view_cursor_get(vis_view(vis)));
1733 return keys;
1736 static const char *mark_motion(Vis *vis, const char *keys, const Arg *arg) {
1737 int mark;
1738 keys = key2mark(vis, keys, &mark);
1739 vis_motion(vis, arg->i, mark);
1740 return keys;
1743 static const char *undo(Vis *vis, const char *keys, const Arg *arg) {
1744 size_t pos = text_undo(vis_text(vis));
1745 if (pos != EPOS) {
1746 View *view = vis_view(vis);
1747 if (!view_cursors_multiple(view))
1748 view_cursor_to(view, pos);
1749 /* redraw all windows in case some display the same file */
1750 vis_draw(vis);
1752 return keys;
1755 static const char *redo(Vis *vis, const char *keys, const Arg *arg) {
1756 size_t pos = text_redo(vis_text(vis));
1757 if (pos != EPOS) {
1758 View *view = vis_view(vis);
1759 if (!view_cursors_multiple(view))
1760 view_cursor_to(view, pos);
1761 /* redraw all windows in case some display the same file */
1762 vis_draw(vis);
1764 return keys;
1767 static const char *earlier(Vis *vis, const char *keys, const Arg *arg) {
1768 size_t pos = text_earlier(vis_text(vis), vis_count_get_default(vis, 1));
1769 if (pos != EPOS) {
1770 view_cursor_to(vis_view(vis), pos);
1771 /* redraw all windows in case some display the same file */
1772 vis_draw(vis);
1774 return keys;
1777 static const char *later(Vis *vis, const char *keys, const Arg *arg) {
1778 size_t pos = text_later(vis_text(vis), vis_count_get_default(vis, 1));
1779 if (pos != EPOS) {
1780 view_cursor_to(vis_view(vis), pos);
1781 /* redraw all windows in case some display the same file */
1782 vis_draw(vis);
1784 return keys;
1787 static const char *delete(Vis *vis, const char *keys, const Arg *arg) {
1788 vis_operator(vis, VIS_OP_DELETE);
1789 vis_motion(vis, arg->i);
1790 return keys;
1793 static const char *insert_register(Vis *vis, const char *keys, const Arg *arg) {
1794 enum VisRegister regid;
1795 keys = key2register(vis, keys, &regid);
1796 size_t len;
1797 const char *data = vis_register_get(vis, regid, &len);
1798 vis_insert_key(vis, data, len);
1799 return keys;
1802 static const char *prompt_show(Vis *vis, const char *keys, const Arg *arg) {
1803 vis_prompt_show(vis, arg->s);
1804 return keys;
1807 static const char *insert_verbatim(Vis *vis, const char *keys, const Arg *arg) {
1808 Rune rune = 0;
1809 char buf[4], type = keys[0];
1810 const char *data = NULL;
1811 int len = 0, count = 0, base = 0;
1812 switch (type) {
1813 case '\0':
1814 return NULL;
1815 case 'o':
1816 case 'O':
1817 count = 3;
1818 base = 8;
1819 break;
1820 case 'U':
1821 count = 4;
1822 /* fall through */
1823 case 'u':
1824 count += 4;
1825 base = 16;
1826 break;
1827 case 'x':
1828 case 'X':
1829 count = 2;
1830 base = 16;
1831 break;
1832 default:
1833 if ('0' <= type && type <= '9') {
1834 rune = type - '0';
1835 count = 2;
1836 base = 10;
1838 break;
1841 if (base) {
1842 for (keys++; keys[0] && count > 0; keys++, count--) {
1843 int v = 0;
1844 if (base == 8 && '0' <= keys[0] && keys[0] <= '7') {
1845 v = keys[0] - '0';
1846 } else if ((base == 10 || base == 16) && '0' <= keys[0] && keys[0] <= '9') {
1847 v = keys[0] - '0';
1848 } else if (base == 16 && 'a' <= keys[0] && keys[0] <= 'f') {
1849 v = 10 + keys[0] - 'a';
1850 } else if (base == 16 && 'A' <= keys[0] && keys[0] <= 'F') {
1851 v = 10 + keys[0] - 'A';
1852 } else {
1853 count = 0;
1854 break;
1856 rune = rune * base + v;
1859 if (count > 0)
1860 return NULL;
1861 if (type == 'u' || type == 'U') {
1862 len = runetochar(buf, &rune);
1863 } else {
1864 buf[0] = rune;
1865 len = 1;
1868 data = buf;
1869 } else {
1870 const char *next = vis_keys_next(vis, keys);
1871 if (!next)
1872 return NULL;
1873 size_t keylen = next - keys;
1874 char key[keylen+1];
1875 memcpy(key, keys, keylen);
1876 key[keylen] = '\0';
1878 static const char *keysym[] = {
1879 "<Enter>", "\n",
1880 "<Tab>", "\t",
1881 "<Backspace>", "\b",
1882 "<Escape>", "\x1b",
1883 "<DEL>", "\x7f",
1884 NULL,
1887 for (const char **k = keysym; k[0]; k += 2) {
1888 if (strcmp(k[0], key) == 0) {
1889 data = k[1];
1890 len = strlen(data);
1891 keys = next;
1892 break;
1897 if (len > 0)
1898 vis_insert_key(vis, data, len);
1899 return keys;
1902 static int argi2lines(Vis *vis, const Arg *arg) {
1903 int count = vis_count_get(vis);
1904 switch (arg->i) {
1905 case -PAGE:
1906 case +PAGE:
1907 return view_height_get(vis_view(vis));
1908 case -PAGE_HALF:
1909 case +PAGE_HALF:
1910 return view_height_get(vis_view(vis))/2;
1911 default:
1912 if (count != VIS_COUNT_UNKNOWN)
1913 return count;
1914 return arg->i < 0 ? -arg->i : arg->i;
1918 static const char *wscroll(Vis *vis, const char *keys, const Arg *arg) {
1919 if (arg->i >= 0)
1920 view_scroll_down(vis_view(vis), argi2lines(vis, arg));
1921 else
1922 view_scroll_up(vis_view(vis), argi2lines(vis, arg));
1923 return keys;
1926 static const char *wslide(Vis *vis, const char *keys, const Arg *arg) {
1927 if (arg->i >= 0)
1928 view_slide_down(vis_view(vis), argi2lines(vis, arg));
1929 else
1930 view_slide_up(vis_view(vis), argi2lines(vis, arg));
1931 return keys;
1934 static const char *call(Vis *vis, const char *keys, const Arg *arg) {
1935 arg->f(vis);
1936 return keys;
1939 static const char *window(Vis *vis, const char *keys, const Arg *arg) {
1940 arg->w(vis_view(vis));
1941 return keys;
1944 static const char *openline(Vis *vis, const char *keys, const Arg *arg) {
1945 vis_operator(vis, VIS_OP_INSERT);
1946 if (arg->i > 0) {
1947 vis_motion(vis, VIS_MOVE_LINE_END);
1948 vis_keys_feed(vis, "<insert-newline>");
1949 } else {
1950 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
1951 vis_keys_feed(vis, "<insert-newline><Up>");
1953 return keys;
1956 static const char *join(Vis *vis, const char *keys, const Arg *arg) {
1957 int count = vis_count_get_default(vis, 0);
1958 if (count)
1959 vis_count_set(vis, count-1);
1960 vis_operator(vis, VIS_OP_JOIN);
1961 vis_motion(vis, arg->i);
1962 return keys;
1965 static const char *switchmode(Vis *vis, const char *keys, const Arg *arg) {
1966 vis_mode_switch(vis, arg->i);
1967 return keys;
1970 static const char *insertmode(Vis *vis, const char *keys, const Arg *arg) {
1971 vis_operator(vis, VIS_OP_INSERT);
1972 vis_motion(vis, arg->i);
1973 return keys;
1976 static const char *unicode_info(Vis *vis, const char *keys, const Arg *arg) {
1977 View *view = vis_view(vis);
1978 Text *txt = vis_text(vis);
1979 size_t start = view_cursor_get(view);
1980 size_t end = text_char_next(txt, start);
1981 char data[end-start], *data_cur = data;
1982 text_bytes_get(txt, start, end - start, data);
1983 Iterator it = text_iterator_get(txt, start);
1984 char info[255] = "", *info_cur = info;
1985 for (size_t pos = start; it.pos < end; pos = it.pos) {
1986 text_iterator_codepoint_next(&it, NULL);
1987 size_t len = it.pos - pos;
1988 wchar_t wc = 0xFFFD;
1989 mbtowc(&wc, data_cur, len);
1990 int width = wcwidth(wc);
1991 info_cur += snprintf(info_cur, sizeof(info) - (info_cur - info) - 1,
1992 "<%s%.*s> U+%04x ", width == 0 ? " " : "", (int)len, data_cur, wc);
1993 data_cur += len;
1995 vis_info_show(vis, "%s", info);
1996 return keys;
1999 static const char *percent(Vis *vis, const char *keys, const Arg *arg) {
2000 if (vis_count_get(vis) == VIS_COUNT_UNKNOWN)
2001 vis_motion(vis, VIS_MOVE_BRACKET_MATCH);
2002 else
2003 vis_motion(vis, VIS_MOVE_PERCENT);
2004 return keys;
2007 static const char *number_increment_decrement(Vis *vis, const char *keys, const Arg *arg) {
2008 View *view = vis_view(vis);
2009 Text *txt = vis_text(vis);
2011 int delta = arg->i;
2012 int count = vis_count_get(vis);
2013 if (count != 0 && count != VIS_COUNT_UNKNOWN)
2014 delta *= count;
2016 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
2017 Filerange r = text_object_number(txt, view_cursors_pos(c));
2018 if (!text_range_valid(&r))
2019 continue;
2020 char *buf = text_bytes_alloc0(txt, r.start, text_range_size(&r));
2021 if (buf) {
2022 char *number = buf, fmt[255];
2023 if (number[0] == '-')
2024 number++;
2025 bool octal = number[0] == '0' && ('0' <= number[1] && number[1] <= '7');
2026 bool hex = number[0] == '0' && (number[1] == 'x' || number[1] == 'X');
2027 bool dec = !hex && !octal;
2029 long long value = strtoll(buf, NULL, 0);
2030 value += delta;
2031 if (dec) {
2032 snprintf(fmt, sizeof fmt, "%lld", value);
2033 } else if (hex) {
2034 size_t len = strlen(number) - 2;
2035 snprintf(fmt, sizeof fmt, "0x%0*llx", (int)len, value);
2036 } else {
2037 size_t len = strlen(number) - 1;
2038 snprintf(fmt, sizeof fmt, "0%0*llo", (int)len, value);
2040 text_delete_range(txt, &r);
2041 text_insert(txt, r.start, fmt, strlen(fmt));
2042 view_cursors_to(c, r.start);
2044 free(buf);
2047 vis_cancel(vis);
2049 return keys;
2052 static const char *open_file_under_cursor(Vis *vis, const char *keys, const Arg *arg) {
2053 Win *win = vis_window(vis);
2054 View *view = vis_view(vis);
2055 Text *txt = vis_text(vis);
2057 if (!arg->b && !vis_window_closable(win)) {
2058 vis_info_show(vis, "No write since last change");
2059 return keys;
2062 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
2063 Filerange r = text_object_filename(txt, view_cursors_pos(c));
2064 if (!text_range_valid(&r))
2065 continue;
2066 char *name = text_bytes_alloc0(txt, r.start, text_range_size(&r));
2067 if (!name)
2068 continue;
2070 struct stat st;
2071 if (stat(name, &st) == -1) {
2072 vis_info_show(vis, "File `%s' not found", name);
2073 free(name);
2074 continue;
2077 if (!vis_window_new(vis, name)) {
2078 vis_info_show(vis, "Failed to open `%s': %s", name, strerror(errno));
2079 free(name);
2080 continue;
2081 } else if (!arg->b) {
2082 vis_window_close(win);
2083 free(name);
2084 return keys;
2087 free(name);
2090 return keys;
2093 static Vis *vis;
2095 static void signal_handler(int signum, siginfo_t *siginfo, void *context) {
2096 vis_signal_handler(vis, signum, siginfo, context);
2099 int main(int argc, char *argv[]) {
2101 VisEvent event = {
2102 .vis_init = vis_lua_init,
2103 .vis_start = vis_lua_start,
2104 .vis_quit = vis_lua_quit,
2105 .file_open = vis_lua_file_open,
2106 .file_save = vis_lua_file_save,
2107 .file_close = vis_lua_file_close,
2108 .win_open = vis_lua_win_open,
2109 .win_close = vis_lua_win_close,
2112 vis = vis_new(ui_curses_new(), &event);
2113 if (!vis)
2114 return EXIT_FAILURE;
2116 for (int i = 0; i < LENGTH(vis_action); i++) {
2117 const KeyAction *action = &vis_action[i];
2118 if (!vis_action_register(vis, action))
2119 vis_die(vis, "Could not register action: %s\n", action->name);
2122 for (int i = 0; i < LENGTH(default_bindings); i++) {
2123 for (const KeyBinding **binding = default_bindings[i]; binding && *binding; binding++) {
2124 for (const KeyBinding *kb = *binding; kb->key; kb++) {
2125 vis_mode_map(vis, i, kb->key, kb);
2130 for (const char **k = keymaps; k[0]; k += 2)
2131 vis_keymap_add(vis, k[0], k[1]);
2133 /* install signal handlers etc. */
2134 struct sigaction sa;
2135 memset(&sa, 0, sizeof sa);
2136 sa.sa_flags = SA_SIGINFO;
2137 sa.sa_sigaction = signal_handler;
2138 if (sigaction(SIGBUS, &sa, NULL) || sigaction(SIGINT, &sa, NULL))
2139 vis_die(vis, "sigaction: %s", strerror(errno));
2141 sigset_t blockset;
2142 sigemptyset(&blockset);
2143 sigaddset(&blockset, SIGWINCH);
2144 sigprocmask(SIG_BLOCK, &blockset, NULL);
2145 signal(SIGPIPE, SIG_IGN);
2147 int status = vis_run(vis, argc, argv);
2148 vis_free(vis);
2149 return status;