build: add a make docker target
[vis.git] / main.c
blob3fec7351ed72b5d9138634f7c6440e543bee45e9
1 #include <signal.h>
2 #include <limits.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <wchar.h>
6 #include <ctype.h>
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <sys/stat.h>
10 #include <sys/types.h>
12 #include "ui-terminal.h"
13 #include "vis.h"
14 #include "vis-lua.h"
15 #include "text-util.h"
16 #include "text-motions.h"
17 #include "text-objects.h"
18 #include "util.h"
19 #include "libutf.h"
20 #include "array.h"
21 #include "buffer.h"
23 #define PAGE INT_MAX
24 #define PAGE_HALF (INT_MAX-1)
26 /** functions to be called from keybindings */
27 /* ignore key, do nothing */
28 static const char *nop(Vis*, const char *keys, const Arg *arg);
29 /* record/replay macro indicated by keys */
30 static const char *macro_record(Vis*, const char *keys, const Arg *arg);
31 static const char *macro_replay(Vis*, const char *keys, const Arg *arg);
32 /* temporarily suspend the editor and return to the shell, type 'fg' to get back */
33 static const char *suspend(Vis*, const char *keys, const Arg *arg);
34 /* switch to mode indicated by arg->i */
35 static const char *switchmode(Vis*, const char *keys, const Arg *arg);
36 /* switch to insert mode after performing movement indicated by arg->i */
37 static const char *insertmode(Vis*, const char *keys, const Arg *arg);
38 /* switch to replace mode after performing movement indicated by arg->i */
39 static const char *replacemode(Vis*, const char *keys, const Arg *arg);
40 /* set mark indicated by keys to current cursor position */
41 static const char *mark_set(Vis*, const char *keys, const Arg *arg);
42 /* add a new line either before or after the one where the cursor currently is */
43 static const char *openline(Vis*, const char *keys, const Arg *arg);
44 /* join lines from current cursor position to movement indicated by arg */
45 static const char *join(Vis*, const char *keys, const Arg *arg);
46 /* perform last action i.e. action_prev again */
47 static const char *repeat(Vis*, const char *keys, const Arg *arg);
48 /* replace character at cursor with one from keys */
49 static const char *replace(Vis*, const char *keys, const Arg *arg);
50 /* create a new cursor on the previous (arg->i < 0) or next (arg->i > 0) line */
51 static const char *cursors_new(Vis*, const char *keys, const Arg *arg);
52 /* try to align all cursors on the same column */
53 static const char *cursors_align(Vis*, const char *keys, const Arg *arg);
54 /* try to align all cursors by inserting the correct amount of white spaces */
55 static const char *cursors_align_indent(Vis*, const char *keys, const Arg *arg);
56 /* remove all but the primary cursor and their selections */
57 static const char *cursors_clear(Vis*, const char *keys, const Arg *arg);
58 /* remove the least recently added cursor */
59 static const char *cursors_remove(Vis*, const char *keys, const Arg *arg);
60 /* remove count (or arg->i)-th cursor column */
61 static const char *cursors_remove_column(Vis*, const char *keys, const Arg *arg);
62 /* remove all but the count (or arg->i)-th cursor column */
63 static const char *cursors_remove_column_except(Vis*, const char *keys, const Arg *arg);
64 /* move to the previous (arg->i < 0) or next (arg->i > 0) cursor */
65 static const char *cursors_navigate(Vis*, const char *keys, const Arg *arg);
66 /* select the word the cursor is currently over */
67 static const char *cursors_select(Vis*, const char *keys, const Arg *arg);
68 /* select the next region matching the current selection */
69 static const char *cursors_select_next(Vis*, const char *keys, const Arg *arg);
70 /* clear current selection but select next match */
71 static const char *cursors_select_skip(Vis*, const char *keys, const Arg *arg);
72 /* rotate selection content count times left (arg->i < 0) or right (arg->i > 0) */
73 static const char *selections_rotate(Vis*, const char *keys, const Arg *arg);
74 /* remove leading and trailing white spaces from selections */
75 static const char *selections_trim(Vis*, const char *keys, const Arg *arg);
76 /* adjust current used count according to keys */
77 static const char *count(Vis*, const char *keys, const Arg *arg);
78 /* move to the count-th line or if not given either to the first (arg->i < 0)
79 * or last (arg->i > 0) line of file */
80 static const char *gotoline(Vis*, const char *keys, const Arg *arg);
81 /* set motion type either LINEWISE or CHARWISE via arg->i */
82 static const char *motiontype(Vis*, const char *keys, const Arg *arg);
83 /* make the current action use the operator indicated by arg->i */
84 static const char *operator(Vis*, const char *keys, const Arg *arg);
85 /* use arg->s as command for the filter operator */
86 static const char *operator_filter(Vis*, const char *keys, const Arg *arg);
87 /* blocks to read a key and performs movement indicated by arg->i which
88 * should be one of VIS_MOVE_{RIGHT,LEFT}_{TO,TILL} */
89 static const char *movement_key(Vis*, const char *keys, const Arg *arg);
90 /* perform the movement as indicated by arg->i */
91 static const char *movement(Vis*, const char *keys, const Arg *arg);
92 /* let the current operator affect the range indicated by the text object arg->i */
93 static const char *textobj(Vis*, const char *keys, const Arg *arg);
94 /* move to the other end of selected text */
95 static const char *selection_end(Vis*, const char *keys, const Arg *arg);
96 /* restore least recently used selection */
97 static const char *selection_restore(Vis*, const char *keys, const Arg *arg);
98 /* use register indicated by keys for the current operator */
99 static const char *reg(Vis*, const char *keys, const Arg *arg);
100 /* perform arg->i motion with a mark indicated by keys as argument */
101 static const char *mark_motion(Vis*, const char *keys, const Arg *arg);
102 /* {un,re}do last action, redraw window */
103 static const char *undo(Vis*, const char *keys, const Arg *arg);
104 static const char *redo(Vis*, const char *keys, const Arg *arg);
105 /* earlier, later action chronologically, redraw window */
106 static const char *earlier(Vis*, const char *keys, const Arg *arg);
107 static const char *later(Vis*, const char *keys, const Arg *arg);
108 /* delete from the current cursor position to the end of
109 * movement as indicated by arg->i */
110 static const char *delete(Vis*, const char *keys, const Arg *arg);
111 /* insert register content indicated by keys at current cursor position */
112 static const char *insert_register(Vis*, const char *keys, const Arg *arg);
113 /* show a user prompt to get input with title arg->s */
114 static const char *prompt_show(Vis*, const char *keys, const Arg *arg);
115 /* blocks to read 3 consecutive digits and inserts the corresponding byte value */
116 static const char *insert_verbatim(Vis*, const char *keys, const Arg *arg);
117 /* scroll window content according to arg->i which can be either PAGE, PAGE_HALF,
118 * or an arbitrary number of lines. a multiplier overrides what is given in arg->i.
119 * negative values scroll back, positive forward. */
120 static const char *wscroll(Vis*, const char *keys, const Arg *arg);
121 /* similar to scroll, but do only move window content not cursor position */
122 static const char *wslide(Vis*, const char *keys, const Arg *arg);
123 /* call editor function as indicated by arg->f */
124 static const char *call(Vis*, const char *keys, const Arg *arg);
125 /* call window function as indicated by arg->w */
126 static const char *window(Vis*, const char *keys, const Arg *arg);
127 /* show info about Unicode character at cursor position */
128 static const char *unicode_info(Vis*, const char *keys, const Arg *arg);
129 /* either go to count % of ile or to matching item */
130 static const char *percent(Vis*, const char *keys, const Arg *arg);
132 enum {
133 VIS_ACTION_EDITOR_SUSPEND,
134 VIS_ACTION_CURSOR_CHAR_PREV,
135 VIS_ACTION_CURSOR_CHAR_NEXT,
136 VIS_ACTION_CURSOR_LINE_CHAR_PREV,
137 VIS_ACTION_CURSOR_LINE_CHAR_NEXT,
138 VIS_ACTION_CURSOR_CODEPOINT_PREV,
139 VIS_ACTION_CURSOR_CODEPOINT_NEXT,
140 VIS_ACTION_CURSOR_WORD_START_PREV,
141 VIS_ACTION_CURSOR_WORD_START_NEXT,
142 VIS_ACTION_CURSOR_WORD_END_PREV,
143 VIS_ACTION_CURSOR_WORD_END_NEXT,
144 VIS_ACTION_CURSOR_LONGWORD_START_PREV,
145 VIS_ACTION_CURSOR_LONGWORD_START_NEXT,
146 VIS_ACTION_CURSOR_LONGWORD_END_PREV,
147 VIS_ACTION_CURSOR_LONGWORD_END_NEXT,
148 VIS_ACTION_CURSOR_LINE_UP,
149 VIS_ACTION_CURSOR_LINE_DOWN,
150 VIS_ACTION_CURSOR_LINE_START,
151 VIS_ACTION_CURSOR_LINE_FINISH,
152 VIS_ACTION_CURSOR_LINE_BEGIN,
153 VIS_ACTION_CURSOR_LINE_END,
154 VIS_ACTION_CURSOR_SCREEN_LINE_UP,
155 VIS_ACTION_CURSOR_SCREEN_LINE_DOWN,
156 VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN,
157 VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE,
158 VIS_ACTION_CURSOR_SCREEN_LINE_END,
159 VIS_ACTION_CURSOR_PERCENT,
160 VIS_ACTION_CURSOR_BYTE,
161 VIS_ACTION_CURSOR_BYTE_LEFT,
162 VIS_ACTION_CURSOR_BYTE_RIGHT,
163 VIS_ACTION_CURSOR_PARAGRAPH_PREV,
164 VIS_ACTION_CURSOR_PARAGRAPH_NEXT,
165 VIS_ACTION_CURSOR_SENTENCE_PREV,
166 VIS_ACTION_CURSOR_SENTENCE_NEXT,
167 VIS_ACTION_CURSOR_BLOCK_START,
168 VIS_ACTION_CURSOR_BLOCK_END,
169 VIS_ACTION_CURSOR_PARENTHESE_START,
170 VIS_ACTION_CURSOR_PARENTHESE_END,
171 VIS_ACTION_CURSOR_COLUMN,
172 VIS_ACTION_CURSOR_LINE_FIRST,
173 VIS_ACTION_CURSOR_LINE_LAST,
174 VIS_ACTION_CURSOR_WINDOW_LINE_TOP,
175 VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE,
176 VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM,
177 VIS_ACTION_CURSOR_SEARCH_REPEAT_FORWARD,
178 VIS_ACTION_CURSOR_SEARCH_REPEAT_BACKWARD,
179 VIS_ACTION_CURSOR_SEARCH_REPEAT,
180 VIS_ACTION_CURSOR_SEARCH_REPEAT_REVERSE,
181 VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD,
182 VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD,
183 VIS_ACTION_WINDOW_PAGE_UP,
184 VIS_ACTION_WINDOW_PAGE_DOWN,
185 VIS_ACTION_WINDOW_HALFPAGE_UP,
186 VIS_ACTION_WINDOW_HALFPAGE_DOWN,
187 VIS_ACTION_MODE_NORMAL,
188 VIS_ACTION_MODE_VISUAL,
189 VIS_ACTION_MODE_VISUAL_LINE,
190 VIS_ACTION_MODE_INSERT,
191 VIS_ACTION_MODE_REPLACE,
192 VIS_ACTION_DELETE_CHAR_PREV,
193 VIS_ACTION_DELETE_CHAR_NEXT,
194 VIS_ACTION_DELETE_LINE_BEGIN,
195 VIS_ACTION_DELETE_WORD_PREV,
196 VIS_ACTION_JUMPLIST_PREV,
197 VIS_ACTION_JUMPLIST_NEXT,
198 VIS_ACTION_CHANGELIST_PREV,
199 VIS_ACTION_CHANGELIST_NEXT,
200 VIS_ACTION_UNDO,
201 VIS_ACTION_REDO,
202 VIS_ACTION_EARLIER,
203 VIS_ACTION_LATER,
204 VIS_ACTION_MACRO_RECORD,
205 VIS_ACTION_MACRO_REPLAY,
206 VIS_ACTION_MARK_SET,
207 VIS_ACTION_MARK_GOTO,
208 VIS_ACTION_MARK_GOTO_LINE,
209 VIS_ACTION_REDRAW,
210 VIS_ACTION_REPLACE_CHAR,
211 VIS_ACTION_TOTILL_REPEAT,
212 VIS_ACTION_TOTILL_REVERSE,
213 VIS_ACTION_PROMPT_SEARCH_FORWARD,
214 VIS_ACTION_PROMPT_SEARCH_BACKWARD,
215 VIS_ACTION_TILL_LEFT,
216 VIS_ACTION_TILL_RIGHT,
217 VIS_ACTION_TO_LEFT,
218 VIS_ACTION_TO_RIGHT,
219 VIS_ACTION_REGISTER,
220 VIS_ACTION_OPERATOR_CHANGE,
221 VIS_ACTION_OPERATOR_DELETE,
222 VIS_ACTION_OPERATOR_YANK,
223 VIS_ACTION_OPERATOR_SHIFT_LEFT,
224 VIS_ACTION_OPERATOR_SHIFT_RIGHT,
225 VIS_ACTION_OPERATOR_CASE_LOWER,
226 VIS_ACTION_OPERATOR_CASE_UPPER,
227 VIS_ACTION_OPERATOR_CASE_SWAP,
228 VIS_ACTION_OPERATOR_FILTER,
229 VIS_ACTION_OPERATOR_FILTER_FMT,
230 VIS_ACTION_COUNT,
231 VIS_ACTION_INSERT_NEWLINE,
232 VIS_ACTION_INSERT_TAB,
233 VIS_ACTION_INSERT_VERBATIM,
234 VIS_ACTION_INSERT_REGISTER,
235 VIS_ACTION_WINDOW_NEXT,
236 VIS_ACTION_WINDOW_PREV,
237 VIS_ACTION_APPEND_CHAR_NEXT,
238 VIS_ACTION_APPEND_LINE_END,
239 VIS_ACTION_INSERT_LINE_START,
240 VIS_ACTION_OPEN_LINE_ABOVE,
241 VIS_ACTION_OPEN_LINE_BELOW,
242 VIS_ACTION_JOIN_LINES,
243 VIS_ACTION_JOIN_LINES_TRIM,
244 VIS_ACTION_PROMPT_SHOW,
245 VIS_ACTION_REPEAT,
246 VIS_ACTION_SELECTION_FLIP,
247 VIS_ACTION_SELECTION_RESTORE,
248 VIS_ACTION_WINDOW_REDRAW_TOP,
249 VIS_ACTION_WINDOW_REDRAW_CENTER,
250 VIS_ACTION_WINDOW_REDRAW_BOTTOM,
251 VIS_ACTION_WINDOW_SLIDE_UP,
252 VIS_ACTION_WINDOW_SLIDE_DOWN,
253 VIS_ACTION_PUT_AFTER,
254 VIS_ACTION_PUT_BEFORE,
255 VIS_ACTION_PUT_AFTER_END,
256 VIS_ACTION_PUT_BEFORE_END,
257 VIS_ACTION_CURSOR_SELECT_WORD,
258 VIS_ACTION_CURSORS_NEW_LINE_ABOVE,
259 VIS_ACTION_CURSORS_NEW_LINE_ABOVE_FIRST,
260 VIS_ACTION_CURSORS_NEW_LINE_BELOW,
261 VIS_ACTION_CURSORS_NEW_LINE_BELOW_LAST,
262 VIS_ACTION_CURSORS_NEW_LINES_BEGIN,
263 VIS_ACTION_CURSORS_NEW_LINES_END,
264 VIS_ACTION_CURSORS_NEW_MATCH_NEXT,
265 VIS_ACTION_CURSORS_NEW_MATCH_SKIP,
266 VIS_ACTION_CURSORS_ALIGN,
267 VIS_ACTION_CURSORS_ALIGN_INDENT_LEFT,
268 VIS_ACTION_CURSORS_ALIGN_INDENT_RIGHT,
269 VIS_ACTION_CURSORS_REMOVE_ALL,
270 VIS_ACTION_CURSORS_REMOVE_LAST,
271 VIS_ACTION_CURSORS_REMOVE_COLUMN,
272 VIS_ACTION_CURSORS_REMOVE_COLUMN_EXCEPT,
273 VIS_ACTION_CURSORS_PREV,
274 VIS_ACTION_CURSORS_NEXT,
275 VIS_ACTION_SELECTIONS_ROTATE_LEFT,
276 VIS_ACTION_SELECTIONS_ROTATE_RIGHT,
277 VIS_ACTION_SELECTIONS_TRIM,
278 VIS_ACTION_TEXT_OBJECT_WORD_OUTER,
279 VIS_ACTION_TEXT_OBJECT_WORD_INNER,
280 VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER,
281 VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER,
282 VIS_ACTION_TEXT_OBJECT_SENTENCE,
283 VIS_ACTION_TEXT_OBJECT_PARAGRAPH,
284 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER,
285 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER,
286 VIS_ACTION_TEXT_OBJECT_PARANTHESE_OUTER,
287 VIS_ACTION_TEXT_OBJECT_PARANTHESE_INNER,
288 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER,
289 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER,
290 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER,
291 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER,
292 VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER,
293 VIS_ACTION_TEXT_OBJECT_QUOTE_INNER,
294 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER,
295 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER,
296 VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER,
297 VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER,
298 VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER,
299 VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER,
300 VIS_ACTION_TEXT_OBJECT_LINE_OUTER,
301 VIS_ACTION_TEXT_OBJECT_LINE_INNER,
302 VIS_ACTION_TEXT_OBJECT_INDENTATION,
303 VIS_ACTION_TEXT_OBJECT_SEARCH_FORWARD,
304 VIS_ACTION_TEXT_OBJECT_SEARCH_BACKWARD,
305 VIS_ACTION_MOTION_CHARWISE,
306 VIS_ACTION_MOTION_LINEWISE,
307 VIS_ACTION_UNICODE_INFO,
308 VIS_ACTION_UTF8_INFO,
309 VIS_ACTION_NOP,
312 static const KeyAction vis_action[] = {
313 [VIS_ACTION_EDITOR_SUSPEND] = {
314 "vis-suspend",
315 VIS_HELP("Suspend the editor")
316 suspend,
318 [VIS_ACTION_CURSOR_CHAR_PREV] = {
319 "vis-motion-char-prev",
320 VIS_HELP("Move cursor left, to the previous character")
321 movement, { .i = VIS_MOVE_CHAR_PREV }
323 [VIS_ACTION_CURSOR_CHAR_NEXT] = {
324 "vis-motion-char-next",
325 VIS_HELP("Move cursor right, to the next character")
326 movement, { .i = VIS_MOVE_CHAR_NEXT }
328 [VIS_ACTION_CURSOR_LINE_CHAR_PREV] = {
329 "vis-motion-line-char-prev",
330 VIS_HELP("Move cursor left, to the previous character on the same line")
331 movement, { .i = VIS_MOVE_LINE_CHAR_PREV }
333 [VIS_ACTION_CURSOR_LINE_CHAR_NEXT] = {
334 "vis-motion-line-char-next",
335 VIS_HELP("Move cursor right, to the next character on the same line")
336 movement, { .i = VIS_MOVE_LINE_CHAR_NEXT }
338 [VIS_ACTION_CURSOR_CODEPOINT_PREV] = {
339 "vis-motion-codepoint-prev",
340 VIS_HELP("Move to the previous Unicode codepoint")
341 movement, { .i = VIS_MOVE_CODEPOINT_PREV }
343 [VIS_ACTION_CURSOR_CODEPOINT_NEXT] = {
344 "vis-motion-codepoint-next",
345 VIS_HELP("Move to the next Unicode codepoint")
346 movement, { .i = VIS_MOVE_CODEPOINT_NEXT }
348 [VIS_ACTION_CURSOR_WORD_START_PREV] = {
349 "vis-motion-word-start-prev",
350 VIS_HELP("Move cursor words backwards")
351 movement, { .i = VIS_MOVE_WORD_START_PREV }
353 [VIS_ACTION_CURSOR_WORD_START_NEXT] = {
354 "vis-motion-word-start-next",
355 VIS_HELP("Move cursor words forwards")
356 movement, { .i = VIS_MOVE_WORD_START_NEXT }
358 [VIS_ACTION_CURSOR_WORD_END_PREV] = {
359 "vis-motion-word-end-prev",
360 VIS_HELP("Move cursor backwards to the end of word")
361 movement, { .i = VIS_MOVE_WORD_END_PREV }
363 [VIS_ACTION_CURSOR_WORD_END_NEXT] = {
364 "vis-motion-word-end-next",
365 VIS_HELP("Move cursor forward to the end of word")
366 movement, { .i = VIS_MOVE_WORD_END_NEXT }
368 [VIS_ACTION_CURSOR_LONGWORD_START_PREV] = {
369 "vis-motion-bigword-start-prev",
370 VIS_HELP("Move cursor WORDS backwards")
371 movement, { .i = VIS_MOVE_LONGWORD_START_PREV }
373 [VIS_ACTION_CURSOR_LONGWORD_START_NEXT] = {
374 "vis-motion-bigword-start-next",
375 VIS_HELP("Move cursor WORDS forwards")
376 movement, { .i = VIS_MOVE_LONGWORD_START_NEXT }
378 [VIS_ACTION_CURSOR_LONGWORD_END_PREV] = {
379 "vis-motion-bigword-end-prev",
380 VIS_HELP("Move cursor backwards to the end of WORD")
381 movement, { .i = VIS_MOVE_LONGWORD_END_PREV }
383 [VIS_ACTION_CURSOR_LONGWORD_END_NEXT] = {
384 "vis-motion-bigword-end-next",
385 VIS_HELP("Move cursor forward to the end of WORD")
386 movement, { .i = VIS_MOVE_LONGWORD_END_NEXT }
388 [VIS_ACTION_CURSOR_LINE_UP] = {
389 "vis-motion-line-up",
390 VIS_HELP("Move cursor line upwards")
391 movement, { .i = VIS_MOVE_LINE_UP }
393 [VIS_ACTION_CURSOR_LINE_DOWN] = {
394 "vis-motion-line-down",
395 VIS_HELP("Move cursor line downwards")
396 movement, { .i = VIS_MOVE_LINE_DOWN }
398 [VIS_ACTION_CURSOR_LINE_START] = {
399 "vis-motion-line-start",
400 VIS_HELP("Move cursor to first non-blank character of the line")
401 movement, { .i = VIS_MOVE_LINE_START }
403 [VIS_ACTION_CURSOR_LINE_FINISH] = {
404 "vis-motion-line-finish",
405 VIS_HELP("Move cursor to last non-blank character of the line")
406 movement, { .i = VIS_MOVE_LINE_FINISH }
408 [VIS_ACTION_CURSOR_LINE_BEGIN] = {
409 "vis-motion-line-begin",
410 VIS_HELP("Move cursor to first character of the line")
411 movement, { .i = VIS_MOVE_LINE_BEGIN }
413 [VIS_ACTION_CURSOR_LINE_END] = {
414 "vis-motion-line-end",
415 VIS_HELP("Move cursor to end of the line")
416 movement, { .i = VIS_MOVE_LINE_END }
418 [VIS_ACTION_CURSOR_SCREEN_LINE_UP] = {
419 "vis-motion-screenline-up",
420 VIS_HELP("Move cursor screen/display line upwards")
421 movement, { .i = VIS_MOVE_SCREEN_LINE_UP }
423 [VIS_ACTION_CURSOR_SCREEN_LINE_DOWN] = {
424 "vis-motion-screenline-down",
425 VIS_HELP("Move cursor screen/display line downwards")
426 movement, { .i = VIS_MOVE_SCREEN_LINE_DOWN }
428 [VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN] = {
429 "vis-motion-screenline-begin",
430 VIS_HELP("Move cursor to beginning of screen/display line")
431 movement, { .i = VIS_MOVE_SCREEN_LINE_BEGIN }
433 [VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE] = {
434 "vis-motion-screenline-middle",
435 VIS_HELP("Move cursor to middle of screen/display line")
436 movement, { .i = VIS_MOVE_SCREEN_LINE_MIDDLE }
438 [VIS_ACTION_CURSOR_SCREEN_LINE_END] = {
439 "vis-motion-screenline-end",
440 VIS_HELP("Move cursor to end of screen/display line")
441 movement, { .i = VIS_MOVE_SCREEN_LINE_END }
443 [VIS_ACTION_CURSOR_PERCENT] = {
444 "vis-motion-percent",
445 VIS_HELP("Move to count % of file or matching item")
446 percent
448 [VIS_ACTION_CURSOR_BYTE] = {
449 "vis-motion-byte",
450 VIS_HELP("Move to absolute byte position")
451 movement, { .i = VIS_MOVE_BYTE }
453 [VIS_ACTION_CURSOR_BYTE_LEFT] = {
454 "vis-motion-byte-left",
455 VIS_HELP("Move count bytes to the left")
456 movement, { .i = VIS_MOVE_BYTE_LEFT }
458 [VIS_ACTION_CURSOR_BYTE_RIGHT] = {
459 "vis-motion-byte-right",
460 VIS_HELP("Move count bytes to the right")
461 movement, { .i = VIS_MOVE_BYTE_RIGHT }
463 [VIS_ACTION_CURSOR_PARAGRAPH_PREV] = {
464 "vis-motion-paragraph-prev",
465 VIS_HELP("Move cursor paragraph backward")
466 movement, { .i = VIS_MOVE_PARAGRAPH_PREV }
468 [VIS_ACTION_CURSOR_PARAGRAPH_NEXT] = {
469 "vis-motion-paragraph-next",
470 VIS_HELP("Move cursor paragraph forward")
471 movement, { .i = VIS_MOVE_PARAGRAPH_NEXT }
473 [VIS_ACTION_CURSOR_SENTENCE_PREV] = {
474 "vis-motion-sentence-prev",
475 VIS_HELP("Move cursor sentence backward")
476 movement, { .i = VIS_MOVE_SENTENCE_PREV }
478 [VIS_ACTION_CURSOR_SENTENCE_NEXT] = {
479 "vis-motion-sentence-next",
480 VIS_HELP("Move cursor sentence forward")
481 movement, { .i = VIS_MOVE_SENTENCE_NEXT }
483 [VIS_ACTION_CURSOR_BLOCK_START] = {
484 "vis-motion-block-start",
485 VIS_HELP("Move cursor to the opening curly brace in a block")
486 movement, { .i = VIS_MOVE_BLOCK_START }
488 [VIS_ACTION_CURSOR_BLOCK_END] = {
489 "vis-motion-block-end",
490 VIS_HELP("Move cursor to the closing curly brace in a block")
491 movement, { .i = VIS_MOVE_BLOCK_END }
493 [VIS_ACTION_CURSOR_PARENTHESE_START] = {
494 "vis-motion-parenthese-start",
495 VIS_HELP("Move cursor to the opening parenthese inside a pair of parentheses")
496 movement, { .i = VIS_MOVE_PARENTHESE_START }
498 [VIS_ACTION_CURSOR_PARENTHESE_END] = {
499 "vis-motion-parenthese-end",
500 VIS_HELP("Move cursor to the closing parenthese inside a pair of parentheses")
501 movement, { .i = VIS_MOVE_PARENTHESE_END }
503 [VIS_ACTION_CURSOR_COLUMN] = {
504 "vis-motion-column",
505 VIS_HELP("Move cursor to given column of current line")
506 movement, { .i = VIS_MOVE_COLUMN }
508 [VIS_ACTION_CURSOR_LINE_FIRST] = {
509 "vis-motion-line-first",
510 VIS_HELP("Move cursor to given line (defaults to first)")
511 gotoline, { .i = -1 }
513 [VIS_ACTION_CURSOR_LINE_LAST] = {
514 "vis-motion-line-last",
515 VIS_HELP("Move cursor to given line (defaults to last)")
516 gotoline, { .i = +1 }
518 [VIS_ACTION_CURSOR_WINDOW_LINE_TOP] = {
519 "vis-motion-window-line-top",
520 VIS_HELP("Move cursor to top line of the window")
521 movement, { .i = VIS_MOVE_WINDOW_LINE_TOP }
523 [VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE] = {
524 "vis-motion-window-line-middle",
525 VIS_HELP("Move cursor to middle line of the window")
526 movement, { .i = VIS_MOVE_WINDOW_LINE_MIDDLE }
528 [VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM] = {
529 "vis-motion-window-line-bottom",
530 VIS_HELP("Move cursor to bottom line of the window")
531 movement, { .i = VIS_MOVE_WINDOW_LINE_BOTTOM }
533 [VIS_ACTION_CURSOR_SEARCH_REPEAT_FORWARD] = {
534 "vis-motion-search-repeat-forward",
535 VIS_HELP("Move cursor to next match in forward direction")
536 movement, { .i = VIS_MOVE_SEARCH_REPEAT_FORWARD }
538 [VIS_ACTION_CURSOR_SEARCH_REPEAT_BACKWARD] = {
539 "vis-motion-search-repeat-backward",
540 VIS_HELP("Move cursor to previous match in backward direction")
541 movement, { .i = VIS_MOVE_SEARCH_REPEAT_BACKWARD }
543 [VIS_ACTION_CURSOR_SEARCH_REPEAT] = {
544 "vis-motion-search-repeat",
545 VIS_HELP("Move cursor to next match")
546 movement, { .i = VIS_MOVE_SEARCH_REPEAT }
548 [VIS_ACTION_CURSOR_SEARCH_REPEAT_REVERSE] = {
549 "vis-motion-search-repeat-reverse",
550 VIS_HELP("Move cursor to next match in opposite direction")
551 movement, { .i = VIS_MOVE_SEARCH_REPEAT_REVERSE }
553 [VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD] = {
554 "vis-motion-search-word-forward",
555 VIS_HELP("Move cursor to next occurrence of the word under cursor")
556 movement, { .i = VIS_MOVE_SEARCH_WORD_FORWARD }
558 [VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD] = {
559 "vis-motion-search-word-backward",
560 VIS_HELP("Move cursor to previous occurrence of the word under cursor")
561 movement, { .i = VIS_MOVE_SEARCH_WORD_BACKWARD }
563 [VIS_ACTION_WINDOW_PAGE_UP] = {
564 "vis-window-page-up",
565 VIS_HELP("Scroll window pages backwards (upwards)")
566 wscroll, { .i = -PAGE }
568 [VIS_ACTION_WINDOW_HALFPAGE_UP] = {
569 "vis-window-halfpage-up",
570 VIS_HELP("Scroll window half pages backwards (upwards)")
571 wscroll, { .i = -PAGE_HALF }
573 [VIS_ACTION_WINDOW_PAGE_DOWN] = {
574 "vis-window-page-down",
575 VIS_HELP("Scroll window pages forwards (downwards)")
576 wscroll, { .i = +PAGE }
578 [VIS_ACTION_WINDOW_HALFPAGE_DOWN] = {
579 "vis-window-halfpage-down",
580 VIS_HELP("Scroll window half pages forwards (downwards)")
581 wscroll, { .i = +PAGE_HALF }
583 [VIS_ACTION_MODE_NORMAL] = {
584 "vis-mode-normal",
585 VIS_HELP("Enter normal mode")
586 switchmode, { .i = VIS_MODE_NORMAL }
588 [VIS_ACTION_MODE_VISUAL] = {
589 "vis-mode-visual-charwise",
590 VIS_HELP("Enter characterwise visual mode")
591 switchmode, { .i = VIS_MODE_VISUAL }
593 [VIS_ACTION_MODE_VISUAL_LINE] = {
594 "vis-mode-visual-linewise",
595 VIS_HELP("Enter linewise visual mode")
596 switchmode, { .i = VIS_MODE_VISUAL_LINE }
598 [VIS_ACTION_MODE_INSERT] = {
599 "vis-mode-insert",
600 VIS_HELP("Enter insert mode")
601 insertmode, { .i = VIS_MOVE_NOP }
603 [VIS_ACTION_MODE_REPLACE] = {
604 "vis-mode-replace",
605 VIS_HELP("Enter replace mode")
606 replacemode, { .i = VIS_MOVE_NOP }
608 [VIS_ACTION_DELETE_CHAR_PREV] = {
609 "vis-delete-char-prev",
610 VIS_HELP("Delete the previous character")
611 delete, { .i = VIS_MOVE_CHAR_PREV }
613 [VIS_ACTION_DELETE_CHAR_NEXT] = {
614 "vis-delete-char-next",
615 VIS_HELP("Delete the next character")
616 delete, { .i = VIS_MOVE_CHAR_NEXT }
618 [VIS_ACTION_DELETE_LINE_BEGIN] = {
619 "vis-delete-line-begin",
620 VIS_HELP("Delete until the start of the current line")
621 delete, { .i = VIS_MOVE_LINE_BEGIN }
623 [VIS_ACTION_DELETE_WORD_PREV] = {
624 "vis-delete-word-prev",
625 VIS_HELP("Delete the previous WORD")
626 delete, { .i = VIS_MOVE_WORD_START_PREV }
628 [VIS_ACTION_JUMPLIST_PREV] = {
629 "vis-jumplist-prev",
630 VIS_HELP("Go to older cursor position in jump list")
631 movement, { .i = VIS_MOVE_JUMPLIST_PREV }
633 [VIS_ACTION_JUMPLIST_NEXT] = {
634 "vis-jumplist-next",
635 VIS_HELP("Go to newer cursor position in jump list")
636 movement, { .i = VIS_MOVE_JUMPLIST_NEXT }
638 [VIS_ACTION_CHANGELIST_PREV] = {
639 "vis-changelist-prev",
640 VIS_HELP("Go to older cursor position in change list")
641 movement, { .i = VIS_MOVE_CHANGELIST_PREV }
643 [VIS_ACTION_CHANGELIST_NEXT] = {
644 "vis-changelist-next",
645 VIS_HELP("Go to newer cursor position in change list")
646 movement, { .i = VIS_MOVE_CHANGELIST_NEXT }
648 [VIS_ACTION_UNDO] = {
649 "vis-undo",
650 VIS_HELP("Undo last change")
651 undo,
653 [VIS_ACTION_REDO] = {
654 "vis-redo",
655 VIS_HELP("Redo last change")
656 redo,
658 [VIS_ACTION_EARLIER] = {
659 "vis-earlier",
660 VIS_HELP("Goto older text state")
661 earlier,
663 [VIS_ACTION_LATER] = {
664 "vis-later",
665 VIS_HELP("Goto newer text state")
666 later,
668 [VIS_ACTION_MACRO_RECORD] = {
669 "vis-macro-record",
670 VIS_HELP("Record macro into given register")
671 macro_record,
673 [VIS_ACTION_MACRO_REPLAY] = {
674 "vis-macro-replay",
675 VIS_HELP("Replay macro, execute the content of the given register")
676 macro_replay,
678 [VIS_ACTION_MARK_SET] = {
679 "vis-mark-set",
680 VIS_HELP("Set given mark at current cursor position")
681 mark_set,
683 [VIS_ACTION_MARK_GOTO] = {
684 "vis-mark-goto",
685 VIS_HELP("Goto the position of the given mark")
686 mark_motion, { .i = VIS_MOVE_MARK }
688 [VIS_ACTION_MARK_GOTO_LINE] = {
689 "vis-mark-goto-line",
690 VIS_HELP("Goto first non-blank character of the line containing the given mark")
691 mark_motion, { .i = VIS_MOVE_MARK_LINE }
693 [VIS_ACTION_REDRAW] = {
694 "vis-redraw",
695 VIS_HELP("Redraw current editor content")
696 call, { .f = vis_redraw }
698 [VIS_ACTION_REPLACE_CHAR] = {
699 "vis-replace-char",
700 VIS_HELP("Replace the character under the cursor")
701 replace,
703 [VIS_ACTION_TOTILL_REPEAT] = {
704 "vis-motion-totill-repeat",
705 VIS_HELP("Repeat latest to/till motion")
706 movement, { .i = VIS_MOVE_TOTILL_REPEAT }
708 [VIS_ACTION_TOTILL_REVERSE] = {
709 "vis-motion-totill-reverse",
710 VIS_HELP("Repeat latest to/till motion but in opposite direction")
711 movement, { .i = VIS_MOVE_TOTILL_REVERSE }
713 [VIS_ACTION_PROMPT_SEARCH_FORWARD] = {
714 "vis-search-forward",
715 VIS_HELP("Search forward")
716 prompt_show, { .s = "/" }
718 [VIS_ACTION_PROMPT_SEARCH_BACKWARD] = {
719 "vis-search-backward",
720 VIS_HELP("Search backward")
721 prompt_show, { .s = "?" }
723 [VIS_ACTION_TILL_LEFT] = {
724 "vis-motion-till-left",
725 VIS_HELP("Till after the occurrence of character to the left")
726 movement_key, { .i = VIS_MOVE_LEFT_TILL }
728 [VIS_ACTION_TILL_RIGHT] = {
729 "vis-motion-till-right",
730 VIS_HELP("Till before the occurrence of character to the right")
731 movement_key, { .i = VIS_MOVE_RIGHT_TILL }
733 [VIS_ACTION_TO_LEFT] = {
734 "vis-motion-to-left",
735 VIS_HELP("To the first occurrence of character to the left")
736 movement_key, { .i = VIS_MOVE_LEFT_TO }
738 [VIS_ACTION_TO_RIGHT] = {
739 "vis-motion-to-right",
740 VIS_HELP("To the first occurrence of character to the right")
741 movement_key, { .i = VIS_MOVE_RIGHT_TO }
743 [VIS_ACTION_REGISTER] = {
744 "vis-register",
745 VIS_HELP("Use given register for next operator")
746 reg,
748 [VIS_ACTION_OPERATOR_CHANGE] = {
749 "vis-operator-change",
750 VIS_HELP("Change operator")
751 operator, { .i = VIS_OP_CHANGE }
753 [VIS_ACTION_OPERATOR_DELETE] = {
754 "vis-operator-delete",
755 VIS_HELP("Delete operator")
756 operator, { .i = VIS_OP_DELETE }
758 [VIS_ACTION_OPERATOR_YANK] = {
759 "vis-operator-yank",
760 VIS_HELP("Yank operator")
761 operator, { .i = VIS_OP_YANK }
763 [VIS_ACTION_OPERATOR_SHIFT_LEFT] = {
764 "vis-operator-shift-left",
765 VIS_HELP("Shift left operator")
766 operator, { .i = VIS_OP_SHIFT_LEFT }
768 [VIS_ACTION_OPERATOR_SHIFT_RIGHT] = {
769 "vis-operator-shift-right",
770 VIS_HELP("Shift right operator")
771 operator, { .i = VIS_OP_SHIFT_RIGHT }
773 [VIS_ACTION_OPERATOR_CASE_LOWER] = {
774 "vis-operator-case-lower",
775 VIS_HELP("Lowercase operator")
776 operator, { .i = VIS_OP_CASE_LOWER }
778 [VIS_ACTION_OPERATOR_CASE_UPPER] = {
779 "vis-operator-case-upper",
780 VIS_HELP("Uppercase operator")
781 operator, { .i = VIS_OP_CASE_UPPER }
783 [VIS_ACTION_OPERATOR_CASE_SWAP] = {
784 "vis-operator-case-swap",
785 VIS_HELP("Swap case operator")
786 operator, { .i = VIS_OP_CASE_SWAP }
788 [VIS_ACTION_OPERATOR_FILTER] = {
789 "vis-operator-filter",
790 VIS_HELP("Filter operator")
791 operator_filter,
793 [VIS_ACTION_OPERATOR_FILTER_FMT] = {
794 "vis-operator-filter-format",
795 VIS_HELP("Formatting operator, filter range through fmt(1)")
796 operator_filter, { .s = "|fmt" }
798 [VIS_ACTION_COUNT] = {
799 "vis-count",
800 VIS_HELP("Count specifier")
801 count,
803 [VIS_ACTION_INSERT_NEWLINE] = {
804 "vis-insert-newline",
805 VIS_HELP("Insert a line break (depending on file type)")
806 call, { .f = vis_insert_nl }
808 [VIS_ACTION_INSERT_TAB] = {
809 "vis-insert-tab",
810 VIS_HELP("Insert a tab (might be converted to spaces)")
811 call, { .f = vis_insert_tab }
813 [VIS_ACTION_INSERT_VERBATIM] = {
814 "vis-insert-verbatim",
815 VIS_HELP("Insert Unicode character based on code point")
816 insert_verbatim,
818 [VIS_ACTION_INSERT_REGISTER] = {
819 "vis-insert-register",
820 VIS_HELP("Insert specified register content")
821 insert_register,
823 [VIS_ACTION_WINDOW_NEXT] = {
824 "vis-window-next",
825 VIS_HELP("Focus next window")
826 call, { .f = vis_window_next }
828 [VIS_ACTION_WINDOW_PREV] = {
829 "vis-window-prev",
830 VIS_HELP("Focus previous window")
831 call, { .f = vis_window_prev }
833 [VIS_ACTION_APPEND_CHAR_NEXT] = {
834 "vis-append-char-next",
835 VIS_HELP("Append text after the cursor")
836 insertmode, { .i = VIS_MOVE_LINE_CHAR_NEXT }
838 [VIS_ACTION_APPEND_LINE_END] = {
839 "vis-append-line-end",
840 VIS_HELP("Append text after the end of the line")
841 insertmode, { .i = VIS_MOVE_LINE_END },
843 [VIS_ACTION_INSERT_LINE_START] = {
844 "vis-insert-line-start",
845 VIS_HELP("Insert text before the first non-blank in the line")
846 insertmode, { .i = VIS_MOVE_LINE_START },
848 [VIS_ACTION_OPEN_LINE_ABOVE] = {
849 "vis-open-line-above",
850 VIS_HELP("Begin a new line above the cursor")
851 openline, { .i = -1 }
853 [VIS_ACTION_OPEN_LINE_BELOW] = {
854 "vis-open-line-below",
855 VIS_HELP("Begin a new line below the cursor")
856 openline, { .i = +1 }
858 [VIS_ACTION_JOIN_LINES] = {
859 "vis-join-lines",
860 VIS_HELP("Join selected lines")
861 join, { .s = " " }
863 [VIS_ACTION_JOIN_LINES_TRIM] = {
864 "vis-join-lines-trim",
865 VIS_HELP("Join selected lines, remove white space")
866 join, { .s = "" }
868 [VIS_ACTION_PROMPT_SHOW] = {
869 "vis-prompt-show",
870 VIS_HELP("Show editor command line prompt")
871 prompt_show, { .s = ":" }
873 [VIS_ACTION_REPEAT] = {
874 "vis-repeat",
875 VIS_HELP("Repeat latest editor command")
876 repeat
878 [VIS_ACTION_SELECTION_FLIP] = {
879 "vis-selection-flip",
880 VIS_HELP("Flip selection, move cursor to other end")
881 selection_end,
883 [VIS_ACTION_SELECTION_RESTORE] = {
884 "vis-selection-restore",
885 VIS_HELP("Restore last selection")
886 selection_restore,
888 [VIS_ACTION_WINDOW_REDRAW_TOP] = {
889 "vis-window-redraw-top",
890 VIS_HELP("Redraw cursor line at the top of the window")
891 window, { .w = view_redraw_top }
893 [VIS_ACTION_WINDOW_REDRAW_CENTER] = {
894 "vis-window-redraw-center",
895 VIS_HELP("Redraw cursor line at the center of the window")
896 window, { .w = view_redraw_center }
898 [VIS_ACTION_WINDOW_REDRAW_BOTTOM] = {
899 "vis-window-redraw-bottom",
900 VIS_HELP("Redraw cursor line at the bottom of the window")
901 window, { .w = view_redraw_bottom }
903 [VIS_ACTION_WINDOW_SLIDE_UP] = {
904 "vis-window-slide-up",
905 VIS_HELP("Slide window content upwards")
906 wslide, { .i = -1 }
908 [VIS_ACTION_WINDOW_SLIDE_DOWN] = {
909 "vis-window-slide-down",
910 VIS_HELP("Slide window content downwards")
911 wslide, { .i = +1 }
913 [VIS_ACTION_PUT_AFTER] = {
914 "vis-put-after",
915 VIS_HELP("Put text after the cursor")
916 operator, { .i = VIS_OP_PUT_AFTER }
918 [VIS_ACTION_PUT_BEFORE] = {
919 "vis-put-before",
920 VIS_HELP("Put text before the cursor")
921 operator, { .i = VIS_OP_PUT_BEFORE }
923 [VIS_ACTION_PUT_AFTER_END] = {
924 "vis-put-after-end",
925 VIS_HELP("Put text after the cursor, place cursor after new text")
926 operator, { .i = VIS_OP_PUT_AFTER_END }
928 [VIS_ACTION_PUT_BEFORE_END] = {
929 "vis-put-before-end",
930 VIS_HELP("Put text before the cursor, place cursor after new text")
931 operator, { .i = VIS_OP_PUT_BEFORE_END }
933 [VIS_ACTION_CURSOR_SELECT_WORD] = {
934 "vis-cursor-select-word",
935 VIS_HELP("Select word under cursor")
936 cursors_select,
938 [VIS_ACTION_CURSORS_NEW_LINE_ABOVE] = {
939 "vis-cursor-new-lines-above",
940 VIS_HELP("Create a new cursor on the line above")
941 cursors_new, { .i = -1 }
943 [VIS_ACTION_CURSORS_NEW_LINE_ABOVE_FIRST] = {
944 "vis-cursor-new-lines-above-first",
945 VIS_HELP("Create a new cursor on the line above the first cursor")
946 cursors_new, { .i = INT_MIN }
948 [VIS_ACTION_CURSORS_NEW_LINE_BELOW] = {
949 "vis-cursor-new-lines-below",
950 VIS_HELP("Create a new cursor on the line below")
951 cursors_new, { .i = +1 }
953 [VIS_ACTION_CURSORS_NEW_LINE_BELOW_LAST] = {
954 "vis-cursor-new-lines-below-last",
955 VIS_HELP("Create a new cursor on the line below the last cursor")
956 cursors_new, { .i = INT_MAX }
958 [VIS_ACTION_CURSORS_NEW_LINES_BEGIN] = {
959 "vis-cursor-new-lines-begin",
960 VIS_HELP("Create a new cursor at the start of every line covered by selection")
961 operator, { .i = VIS_OP_CURSOR_SOL }
963 [VIS_ACTION_CURSORS_NEW_LINES_END] = {
964 "vis-cursor-new-lines-end",
965 VIS_HELP("Create a new cursor at the end of every line covered by selection")
966 operator, { .i = VIS_OP_CURSOR_EOL }
968 [VIS_ACTION_CURSORS_NEW_MATCH_NEXT] = {
969 "vis-cursor-new-match-next",
970 VIS_HELP("Select the next region matching the current selection")
971 cursors_select_next
973 [VIS_ACTION_CURSORS_NEW_MATCH_SKIP] = {
974 "vis-cursor-new-match-skip",
975 VIS_HELP("Clear current selection, but select next match")
976 cursors_select_skip,
978 [VIS_ACTION_CURSORS_ALIGN] = {
979 "vis-cursors-align",
980 VIS_HELP("Try to align all cursors on the same column")
981 cursors_align,
983 [VIS_ACTION_CURSORS_ALIGN_INDENT_LEFT] = {
984 "vis-cursors-align-indent-left",
985 VIS_HELP("Left align all cursors/selections by inserting spaces")
986 cursors_align_indent, { .i = -1 }
988 [VIS_ACTION_CURSORS_ALIGN_INDENT_RIGHT] = {
989 "vis-cursors-align-indent-right",
990 VIS_HELP("Right align all cursors/selections by inserting spaces")
991 cursors_align_indent, { .i = +1 }
993 [VIS_ACTION_CURSORS_REMOVE_ALL] = {
994 "vis-cursors-remove-all",
995 VIS_HELP("Remove all but the primary cursor")
996 cursors_clear,
998 [VIS_ACTION_CURSORS_REMOVE_LAST] = {
999 "vis-cursors-remove-last",
1000 VIS_HELP("Remove least recently created cursor")
1001 cursors_remove,
1003 [VIS_ACTION_CURSORS_REMOVE_COLUMN] = {
1004 "vis-cursors-remove-column",
1005 VIS_HELP("Remove count cursor column")
1006 cursors_remove_column, { .i = 1 }
1008 [VIS_ACTION_CURSORS_REMOVE_COLUMN_EXCEPT] = {
1009 "vis-cursors-remove-column-except",
1010 VIS_HELP("Remove all but the count cursor column")
1011 cursors_remove_column_except, { .i = 1 }
1013 [VIS_ACTION_CURSORS_PREV] = {
1014 "vis-cursor-prev",
1015 VIS_HELP("Move to the previous cursor")
1016 cursors_navigate, { .i = -PAGE_HALF }
1018 [VIS_ACTION_CURSORS_NEXT] = {
1019 "vis-cursor-next",
1020 VIS_HELP("Move to the next cursor")
1021 cursors_navigate, { .i = +PAGE_HALF }
1023 [VIS_ACTION_SELECTIONS_ROTATE_LEFT] = {
1024 "vis-selections-rotate-left",
1025 VIS_HELP("Rotate selections left")
1026 selections_rotate, { .i = -1 }
1028 [VIS_ACTION_SELECTIONS_ROTATE_RIGHT] = {
1029 "vis-selections-rotate-right",
1030 VIS_HELP("Rotate selections right")
1031 selections_rotate, { .i = +1 }
1033 [VIS_ACTION_SELECTIONS_TRIM] = {
1034 "vis-selections-trim",
1035 VIS_HELP("Remove leading and trailing white space from selections")
1036 selections_trim
1038 [VIS_ACTION_TEXT_OBJECT_WORD_OUTER] = {
1039 "vis-textobject-word-outer",
1040 VIS_HELP("A word leading and trailing whitespace included")
1041 textobj, { .i = VIS_TEXTOBJECT_OUTER_WORD }
1043 [VIS_ACTION_TEXT_OBJECT_WORD_INNER] = {
1044 "vis-textobject-word-inner",
1045 VIS_HELP("A word leading and trailing whitespace excluded")
1046 textobj, { .i = VIS_TEXTOBJECT_INNER_WORD }
1048 [VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER] = {
1049 "vis-textobject-bigword-outer",
1050 VIS_HELP("A WORD leading and trailing whitespace included")
1051 textobj, { .i = VIS_TEXTOBJECT_OUTER_LONGWORD }
1053 [VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER] = {
1054 "vis-textobject-bigword-inner",
1055 VIS_HELP("A WORD leading and trailing whitespace excluded")
1056 textobj, { .i = VIS_TEXTOBJECT_INNER_LONGWORD }
1058 [VIS_ACTION_TEXT_OBJECT_SENTENCE] = {
1059 "vis-textobject-sentence",
1060 VIS_HELP("A sentence")
1061 textobj, { .i = VIS_TEXTOBJECT_SENTENCE }
1063 [VIS_ACTION_TEXT_OBJECT_PARAGRAPH] = {
1064 "vis-textobject-paragraph",
1065 VIS_HELP("A paragraph")
1066 textobj, { .i = VIS_TEXTOBJECT_PARAGRAPH }
1068 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER] = {
1069 "vis-textobject-square-bracket-outer",
1070 VIS_HELP("[] block (outer variant)")
1071 textobj, { .i = VIS_TEXTOBJECT_OUTER_SQUARE_BRACKET }
1073 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER] = {
1074 "vis-textobject-square-bracket-inner",
1075 VIS_HELP("[] block (inner variant)")
1076 textobj, { .i = VIS_TEXTOBJECT_INNER_SQUARE_BRACKET }
1078 [VIS_ACTION_TEXT_OBJECT_PARANTHESE_OUTER] = {
1079 "vis-textobject-parentheses-outer",
1080 VIS_HELP("() block (outer variant)")
1081 textobj, { .i = VIS_TEXTOBJECT_OUTER_PARANTHESE }
1083 [VIS_ACTION_TEXT_OBJECT_PARANTHESE_INNER] = {
1084 "vis-textobject-parentheses-inner",
1085 VIS_HELP("() block (inner variant)")
1086 textobj, { .i = VIS_TEXTOBJECT_INNER_PARANTHESE }
1088 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER] = {
1089 "vis-textobject-angle-bracket-outer",
1090 VIS_HELP("<> block (outer variant)")
1091 textobj, { .i = VIS_TEXTOBJECT_OUTER_ANGLE_BRACKET }
1093 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER] = {
1094 "vis-textobject-angle-bracket-inner",
1095 VIS_HELP("<> block (inner variant)")
1096 textobj, { .i = VIS_TEXTOBJECT_INNER_ANGLE_BRACKET }
1098 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER] = {
1099 "vis-textobject-curly-bracket-outer",
1100 VIS_HELP("{} block (outer variant)")
1101 textobj, { .i = VIS_TEXTOBJECT_OUTER_CURLY_BRACKET }
1103 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER] = {
1104 "vis-textobject-curly-bracket-inner",
1105 VIS_HELP("{} block (inner variant)")
1106 textobj, { .i = VIS_TEXTOBJECT_INNER_CURLY_BRACKET }
1108 [VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER] = {
1109 "vis-textobject-quote-outer",
1110 VIS_HELP("A quoted string, including the quotation marks")
1111 textobj, { .i = VIS_TEXTOBJECT_OUTER_QUOTE }
1113 [VIS_ACTION_TEXT_OBJECT_QUOTE_INNER] = {
1114 "vis-textobject-quote-inner",
1115 VIS_HELP("A quoted string, excluding the quotation marks")
1116 textobj, { .i = VIS_TEXTOBJECT_INNER_QUOTE }
1118 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER] = {
1119 "vis-textobject-single-quote-outer",
1120 VIS_HELP("A single quoted string, including the quotation marks")
1121 textobj, { .i = VIS_TEXTOBJECT_OUTER_SINGLE_QUOTE }
1123 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER] = {
1124 "vis-textobject-single-quote-inner",
1125 VIS_HELP("A single quoted string, excluding the quotation marks")
1126 textobj, { .i = VIS_TEXTOBJECT_INNER_SINGLE_QUOTE }
1128 [VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER] = {
1129 "vis-textobject-backtick-outer",
1130 VIS_HELP("A backtick delimited string (outer variant)")
1131 textobj, { .i = VIS_TEXTOBJECT_OUTER_BACKTICK }
1133 [VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER] = {
1134 "vis-textobject-backtick-inner",
1135 VIS_HELP("A backtick delimited string (inner variant)")
1136 textobj, { .i = VIS_TEXTOBJECT_INNER_BACKTICK }
1138 [VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER] = {
1139 "vis-textobject-entire-outer",
1140 VIS_HELP("The whole text content")
1141 textobj, { .i = VIS_TEXTOBJECT_OUTER_ENTIRE }
1143 [VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER] = {
1144 "vis-textobject-entire-inner",
1145 VIS_HELP("The whole text content, except for leading and trailing empty lines")
1146 textobj, { .i = VIS_TEXTOBJECT_INNER_ENTIRE }
1148 [VIS_ACTION_TEXT_OBJECT_LINE_OUTER] = {
1149 "vis-textobject-line-outer",
1150 VIS_HELP("The whole line")
1151 textobj, { .i = VIS_TEXTOBJECT_OUTER_LINE }
1153 [VIS_ACTION_TEXT_OBJECT_LINE_INNER] = {
1154 "vis-textobject-line-inner",
1155 VIS_HELP("The whole line, excluding leading and trailing whitespace")
1156 textobj, { .i = VIS_TEXTOBJECT_INNER_LINE }
1158 [VIS_ACTION_TEXT_OBJECT_INDENTATION] = {
1159 "vis-textobject-indentation",
1160 VIS_HELP("All adjacent lines with the same indentation level as the current one")
1161 textobj, { .i = VIS_TEXTOBJECT_INDENTATION }
1163 [VIS_ACTION_TEXT_OBJECT_SEARCH_FORWARD] = {
1164 "vis-textobject-search-forward",
1165 VIS_HELP("The next search match in forward direction")
1166 textobj, { .i = VIS_TEXTOBJECT_SEARCH_FORWARD }
1168 [VIS_ACTION_TEXT_OBJECT_SEARCH_BACKWARD] = {
1169 "vis-textobject-search-backward",
1170 VIS_HELP("The next search match in backward direction")
1171 textobj, { .i = VIS_TEXTOBJECT_SEARCH_BACKWARD }
1173 [VIS_ACTION_MOTION_CHARWISE] = {
1174 "vis-motion-charwise",
1175 VIS_HELP("Force motion to be charwise")
1176 motiontype, { .i = VIS_MOTIONTYPE_CHARWISE }
1178 [VIS_ACTION_MOTION_LINEWISE] = {
1179 "vis-motion-linewise",
1180 VIS_HELP("Force motion to be linewise")
1181 motiontype, { .i = VIS_MOTIONTYPE_LINEWISE }
1183 [VIS_ACTION_UNICODE_INFO] = {
1184 "vis-unicode-info",
1185 VIS_HELP("Show Unicode codepoint(s) of character under cursor")
1186 unicode_info, { .i = VIS_ACTION_UNICODE_INFO }
1188 [VIS_ACTION_UTF8_INFO] = {
1189 "vis-utf8-info",
1190 VIS_HELP("Show UTF-8 encoded codepoint(s) of character under cursor")
1191 unicode_info, { .i = VIS_ACTION_UTF8_INFO }
1193 [VIS_ACTION_NOP] = {
1194 "vis-nop",
1195 VIS_HELP("Ignore key, do nothing")
1196 nop,
1200 #include "config.h"
1202 /** key bindings functions */
1204 static const char *nop(Vis *vis, const char *keys, const Arg *arg) {
1205 return keys;
1208 static const char *macro_record(Vis *vis, const char *keys, const Arg *arg) {
1209 if (!vis_macro_record_stop(vis)) {
1210 if (!keys[0])
1211 return NULL;
1212 if (keys[1])
1213 return vis_keys_next(vis, keys);
1214 enum VisRegister reg = vis_register_from(vis, keys[0]);
1215 vis_macro_record(vis, reg);
1216 keys++;
1218 vis_draw(vis);
1219 return keys;
1222 static const char *macro_replay(Vis *vis, const char *keys, const Arg *arg) {
1223 if (!keys[0])
1224 return NULL;
1225 if (keys[1])
1226 return vis_keys_next(vis, keys);
1227 enum VisRegister reg = vis_register_from(vis, keys[0]);
1228 vis_macro_replay(vis, reg);
1229 return keys+1;
1232 static const char *suspend(Vis *vis, const char *keys, const Arg *arg) {
1233 vis_suspend(vis);
1234 return keys;
1237 static const char *repeat(Vis *vis, const char *keys, const Arg *arg) {
1238 vis_repeat(vis);
1239 return keys;
1242 static const char *cursors_new(Vis *vis, const char *keys, const Arg *arg) {
1243 View *view = vis_view(vis);
1244 VisCountIterator it = vis_count_iterator_get(vis, 1);
1245 while (vis_count_iterator_next(&it)) {
1246 Cursor *cursor = NULL;
1247 switch (arg->i) {
1248 case -1:
1249 case +1:
1250 cursor = view_cursors_primary_get(view);
1251 break;
1252 case INT_MIN:
1253 cursor = view_cursors(view);
1254 break;
1255 case INT_MAX:
1256 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c))
1257 cursor = c;
1258 break;
1259 default:
1260 return keys;
1262 size_t oldpos = view_cursors_pos(cursor);
1263 if (arg->i > 0)
1264 view_line_down(cursor);
1265 else if (arg->i < 0)
1266 view_line_up(cursor);
1267 size_t newpos = view_cursors_pos(cursor);
1268 view_cursors_to(cursor, oldpos);
1269 Cursor *cursor_new = view_cursors_new(view, newpos);
1270 if (!cursor_new) {
1271 if (arg->i == -1)
1272 cursor_new = view_cursors_prev(cursor);
1273 else if (arg->i == +1)
1274 cursor_new = view_cursors_next(cursor);
1276 if (cursor_new)
1277 view_cursors_primary_set(cursor_new);
1279 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1280 return keys;
1283 static const char *cursors_align(Vis *vis, const char *keys, const Arg *arg) {
1284 View *view = vis_view(vis);
1285 Text *txt = vis_text(vis);
1286 int mincol = INT_MAX;
1287 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
1288 int col = view_cursors_cell_get(c);
1289 if (col >= 0 && col < mincol)
1290 mincol = col;
1292 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
1293 if (view_cursors_cell_set(c, mincol) == -1) {
1294 size_t pos = view_cursors_pos(c);
1295 size_t col = text_line_width_set(txt, pos, mincol);
1296 view_cursors_to(c, col);
1299 return keys;
1302 static const char *cursors_align_indent(Vis *vis, const char *keys, const Arg *arg) {
1303 View *view = vis_view(vis);
1304 Text *txt = vis_text(vis);
1305 bool left_align = arg->i < 0;
1306 int columns = view_cursors_column_count(view);
1308 for (int i = 0; i < columns; i++) {
1309 int mincol = INT_MAX, maxcol = 0;
1310 for (Cursor *c = view_cursors_column(view, i); c; c = view_cursors_column_next(c, i)) {
1311 size_t pos;
1312 Filerange sel = view_cursors_selection_get(c);
1313 if (text_range_valid(&sel))
1314 pos = left_align ? sel.start : sel.end;
1315 else
1316 pos = view_cursors_pos(c);
1317 int col = text_line_width_get(txt, pos);
1318 if (col < mincol)
1319 mincol = col;
1320 if (col > maxcol)
1321 maxcol = col;
1324 size_t len = maxcol - mincol;
1325 char *buf = malloc(len+1);
1326 if (!buf)
1327 return keys;
1328 memset(buf, ' ', len);
1330 for (Cursor *c = view_cursors_column(view, i); c; c = view_cursors_column_next(c, i)) {
1331 size_t pos, ipos;
1332 Filerange sel = view_cursors_selection_get(c);
1333 if (text_range_valid(&sel)) {
1334 pos = left_align ? sel.start : sel.end;
1335 ipos = sel.start;
1336 } else {
1337 pos = view_cursors_pos(c);
1338 ipos = pos;
1340 int col = text_line_width_get(txt, pos);
1341 if (col < maxcol) {
1342 size_t off = maxcol - col;
1343 if (off <= len)
1344 text_insert(txt, ipos, buf, off);
1348 free(buf);
1351 view_draw(view);
1352 return keys;
1355 static const char *cursors_clear(Vis *vis, const char *keys, const Arg *arg) {
1356 View *view = vis_view(vis);
1357 if (view_cursors_multiple(view))
1358 view_cursors_clear(view);
1359 else
1360 view_cursors_selection_clear(view_cursors_primary_get(view));
1361 return keys;
1364 static const char *cursors_select(Vis *vis, const char *keys, const Arg *arg) {
1365 Text *txt = vis_text(vis);
1366 View *view = vis_view(vis);
1367 for (Cursor *cursor = view_cursors(view); cursor; cursor = view_cursors_next(cursor)) {
1368 Filerange sel = view_cursors_selection_get(cursor);
1369 Filerange word = text_object_word(txt, view_cursors_pos(cursor));
1370 if (!text_range_valid(&sel) && text_range_valid(&word)) {
1371 view_cursors_selection_set(cursor, &word);
1372 view_cursors_to(cursor, text_char_prev(txt, word.end));
1375 vis_mode_switch(vis, VIS_MODE_VISUAL);
1376 return keys;
1379 static const char *cursors_select_next(Vis *vis, const char *keys, const Arg *arg) {
1380 Text *txt = vis_text(vis);
1381 View *view = vis_view(vis);
1382 Cursor *cursor = view_cursors_primary_get(view);
1383 Filerange sel = view_cursors_selection_get(cursor);
1384 if (!text_range_valid(&sel))
1385 return keys;
1387 char *buf = text_bytes_alloc0(txt, sel.start, text_range_size(&sel));
1388 if (!buf)
1389 return keys;
1390 Filerange word = text_object_word_find_next(txt, sel.end, buf);
1391 if (text_range_valid(&word)) {
1392 size_t pos = text_char_prev(txt, word.end);
1393 if ((cursor = view_cursors_new(view, pos))) {
1394 view_cursors_selection_set(cursor, &word);
1395 view_cursors_primary_set(cursor);
1396 goto out;
1400 sel = view_cursors_selection_get(view_cursors(view));
1401 word = text_object_word_find_prev(txt, sel.start, buf);
1402 if (!text_range_valid(&word))
1403 goto out;
1404 size_t pos = text_char_prev(txt, word.end);
1405 if ((cursor = view_cursors_new(view, pos))) {
1406 view_cursors_selection_set(cursor, &word);
1407 view_cursors_primary_set(cursor);
1410 out:
1411 free(buf);
1412 return keys;
1415 static const char *cursors_select_skip(Vis *vis, const char *keys, const Arg *arg) {
1416 View *view = vis_view(vis);
1417 Cursor *cursor = view_cursors_primary_get(view);
1418 keys = cursors_select_next(vis, keys, arg);
1419 if (cursor != view_cursors_primary_get(view))
1420 view_cursors_dispose(cursor);
1421 return keys;
1424 static const char *cursors_remove(Vis *vis, const char *keys, const Arg *arg) {
1425 View *view = vis_view(vis);
1426 view_cursors_dispose(view_cursors_primary_get(view));
1427 view_cursor_to(view, view_cursor_get(view));
1428 return keys;
1431 static const char *cursors_remove_column(Vis *vis, const char *keys, const Arg *arg) {
1432 View *view = vis_view(vis);
1433 int max = view_cursors_column_count(view);
1434 int column = vis_count_get_default(vis, arg->i) - 1;
1435 if (column >= max)
1436 column = max - 1;
1437 if (!view_cursors_multiple(view)) {
1438 vis_mode_switch(vis, VIS_MODE_NORMAL);
1439 return keys;
1442 for (Cursor *c = view_cursors_column(view, column), *next; c; c = next) {
1443 next = view_cursors_column_next(c, column);
1444 view_cursors_dispose(c);
1447 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1448 return keys;
1451 static const char *cursors_remove_column_except(Vis *vis, const char *keys, const Arg *arg) {
1452 View *view = vis_view(vis);
1453 int max = view_cursors_column_count(view);
1454 int column = vis_count_get_default(vis, arg->i) - 1;
1455 if (column >= max)
1456 column = max - 1;
1457 if (!view_cursors_multiple(view)) {
1458 vis_redraw(vis);
1459 return keys;
1462 Cursor *cur = view_cursors(view);
1463 Cursor *col = view_cursors_column(view, column);
1464 for (Cursor *next; cur; cur = next) {
1465 next = view_cursors_next(cur);
1466 if (cur == col)
1467 col = view_cursors_column_next(col, column);
1468 else
1469 view_cursors_dispose(cur);
1472 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1473 return keys;
1476 static const char *cursors_navigate(Vis *vis, const char *keys, const Arg *arg) {
1477 View *view = vis_view(vis);
1478 if (!view_cursors_multiple(view)) {
1479 Filerange sel = view_selection_get(view);
1480 if (!text_range_valid(&sel))
1481 return wscroll(vis, keys, arg);
1482 return keys;
1484 Cursor *c = view_cursors_primary_get(view);
1485 VisCountIterator it = vis_count_iterator_get(vis, 1);
1486 while (vis_count_iterator_next(&it)) {
1487 if (arg->i > 0) {
1488 c = view_cursors_next(c);
1489 if (!c)
1490 c = view_cursors(view);
1491 } else {
1492 c = view_cursors_prev(c);
1493 if (!c) {
1494 c = view_cursors(view);
1495 for (Cursor *n = c; n; n = view_cursors_next(n))
1496 c = n;
1500 view_cursors_primary_set(c);
1501 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1502 return keys;
1505 static const char *selections_rotate(Vis *vis, const char *keys, const Arg *arg) {
1507 typedef struct {
1508 Cursor *cursor;
1509 char *data;
1510 size_t len;
1511 } Rotate;
1513 Array arr;
1514 Text *txt = vis_text(vis);
1515 View *view = vis_view(vis);
1516 int columns = view_cursors_column_count(view);
1517 int selections = columns == 1 ? view_cursors_count(view) : columns;
1518 int count = vis_count_get_default(vis, 1);
1519 array_init_sized(&arr, sizeof(Rotate));
1520 if (!array_reserve(&arr, selections))
1521 return keys;
1522 size_t line = 0;
1524 for (Cursor *c = view_cursors(view), *next; c; c = next) {
1525 next = view_cursors_next(c);
1526 size_t line_next = 0;
1528 Filerange sel = view_cursors_selection_get(c);
1529 Rotate rot;
1530 rot.cursor = c;
1531 rot.len = text_range_size(&sel);
1532 if ((rot.data = malloc(rot.len)))
1533 rot.len = text_bytes_get(txt, sel.start, rot.len, rot.data);
1534 else
1535 rot.len = 0;
1536 array_add(&arr, &rot);
1538 if (!line)
1539 line = text_lineno_by_pos(txt, view_cursors_pos(c));
1540 if (next)
1541 line_next = text_lineno_by_pos(txt, view_cursors_pos(next));
1542 if (!next || (columns > 1 && line != line_next)) {
1543 size_t len = array_length(&arr);
1544 size_t off = arg->i > 0 ? count % len : len - (count % len);
1545 for (size_t i = 0; i < len; i++) {
1546 size_t j = (i + off) % len;
1547 Rotate *oldrot = array_get(&arr, i);
1548 Rotate *newrot = array_get(&arr, j);
1549 if (!oldrot || !newrot || oldrot == newrot)
1550 continue;
1551 Filerange newsel = view_cursors_selection_get(newrot->cursor);
1552 if (!text_range_valid(&newsel))
1553 continue;
1554 if (!text_delete_range(txt, &newsel))
1555 continue;
1556 if (!text_insert(txt, newsel.start, oldrot->data, oldrot->len))
1557 continue;
1558 newsel.end = newsel.start + oldrot->len;
1559 view_cursors_selection_set(newrot->cursor, &newsel);
1560 view_cursors_selection_sync(newrot->cursor);
1561 free(oldrot->data);
1563 array_clear(&arr);
1565 line = line_next;
1568 array_release(&arr);
1569 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1570 return keys;
1573 static const char *selections_trim(Vis *vis, const char *keys, const Arg *arg) {
1574 Text *txt = vis_text(vis);
1575 View *view = vis_view(vis);
1576 for (Cursor *c = view_cursors(view), *next; c; c = next) {
1577 next = view_cursors_next(c);
1578 Filerange sel = view_cursors_selection_get(c);
1579 if (!text_range_valid(&sel))
1580 continue;
1581 for (char b; sel.start < sel.end && text_byte_get(txt, sel.end-1, &b)
1582 && isspace((unsigned char)b); sel.end--);
1583 for (char b; sel.start <= sel.end && text_byte_get(txt, sel.start, &b)
1584 && isspace((unsigned char)b); sel.start++);
1585 if (sel.start < sel.end) {
1586 view_cursors_selection_set(c, &sel);
1587 view_cursors_selection_sync(c);
1588 } else if (!view_cursors_dispose(c)) {
1589 vis_mode_switch(vis, VIS_MODE_NORMAL);
1592 return keys;
1595 static const char *replace(Vis *vis, const char *keys, const Arg *arg) {
1596 if (!keys[0]) {
1597 vis_keymap_disable(vis);
1598 return NULL;
1601 const char *next = vis_keys_next(vis, keys);
1602 if (!next)
1603 return NULL;
1605 char replacement[UTFmax+1];
1606 if (!vis_keys_utf8(vis, keys, replacement))
1607 return next;
1609 if (replacement[0] == 0x1b) /* <Escape> */
1610 return next;
1612 vis_operator(vis, VIS_OP_REPLACE, replacement);
1613 if (vis_mode_get(vis) == VIS_MODE_OPERATOR_PENDING)
1614 vis_motion(vis, VIS_MOVE_CHAR_NEXT);
1615 return next;
1618 static const char *count(Vis *vis, const char *keys, const Arg *arg) {
1619 int digit = keys[-1] - '0';
1620 int count = vis_count_get_default(vis, 0);
1621 if (0 <= digit && digit <= 9) {
1622 if (digit == 0 && count == 0)
1623 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
1624 else
1625 vis_count_set(vis, count * 10 + digit);
1627 return keys;
1630 static const char *gotoline(Vis *vis, const char *keys, const Arg *arg) {
1631 if (vis_count_get(vis) != VIS_COUNT_UNKNOWN)
1632 vis_motion(vis, VIS_MOVE_LINE);
1633 else if (arg->i < 0)
1634 vis_motion(vis, VIS_MOVE_FILE_BEGIN);
1635 else
1636 vis_motion(vis, VIS_MOVE_FILE_END);
1637 return keys;
1640 static const char *motiontype(Vis *vis, const char *keys, const Arg *arg) {
1641 vis_motion_type(vis, arg->i);
1642 return keys;
1645 static const char *operator(Vis *vis, const char *keys, const Arg *arg) {
1646 vis_operator(vis, arg->i);
1647 return keys;
1650 static const char *operator_filter(Vis *vis, const char *keys, const Arg *arg) {
1651 vis_operator(vis, VIS_OP_FILTER, arg->s);
1652 return keys;
1655 static const char *movement_key(Vis *vis, const char *keys, const Arg *arg) {
1656 if (!keys[0]) {
1657 vis_keymap_disable(vis);
1658 return NULL;
1661 const char *next = vis_keys_next(vis, keys);
1662 if (!next)
1663 return NULL;
1664 char utf8[UTFmax+1];
1665 if (vis_keys_utf8(vis, keys, utf8))
1666 vis_motion(vis, arg->i, utf8);
1667 return next;
1670 static const char *movement(Vis *vis, const char *keys, const Arg *arg) {
1671 vis_motion(vis, arg->i);
1672 return keys;
1675 static const char *textobj(Vis *vis, const char *keys, const Arg *arg) {
1676 vis_textobject(vis, arg->i);
1677 return keys;
1680 static const char *selection_end(Vis *vis, const char *keys, const Arg *arg) {
1681 for (Cursor *c = view_cursors(vis_view(vis)); c; c = view_cursors_next(c))
1682 view_cursors_selection_swap(c);
1683 return keys;
1686 static const char *selection_restore(Vis *vis, const char *keys, const Arg *arg) {
1687 Text *txt = vis_text(vis);
1688 View *view = vis_view(vis);
1689 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c))
1690 view_cursors_selection_restore(c);
1691 Filerange sel = view_selection_get(view);
1692 if (text_range_is_linewise(txt, &sel))
1693 vis_mode_switch(vis, VIS_MODE_VISUAL_LINE);
1694 else
1695 vis_mode_switch(vis, VIS_MODE_VISUAL);
1696 return keys;
1699 static const char *reg(Vis *vis, const char *keys, const Arg *arg) {
1700 if (!keys[0])
1701 return NULL;
1702 if (keys[1])
1703 return vis_keys_next(vis, keys);
1704 enum VisRegister reg = vis_register_from(vis, keys[0]);
1705 vis_register(vis, reg);
1706 return keys+1;
1709 static const char *mark_set(Vis *vis, const char *keys, const Arg *arg) {
1710 if (!keys[0])
1711 return NULL;
1712 if (keys[1])
1713 return vis_keys_next(vis, keys);
1714 vis_mark_set(vis, vis_mark_from(vis, keys[0]), view_cursor_get(vis_view(vis)));
1715 return keys+1;
1718 static const char *mark_motion(Vis *vis, const char *keys, const Arg *arg) {
1719 if (!keys[0])
1720 return NULL;
1721 if (keys[1])
1722 return vis_keys_next(vis, keys);
1723 vis_motion(vis, arg->i, vis_mark_from(vis, keys[0]));
1724 return keys+1;
1727 static const char *undo(Vis *vis, const char *keys, const Arg *arg) {
1728 size_t pos = text_undo(vis_text(vis));
1729 if (pos != EPOS) {
1730 View *view = vis_view(vis);
1731 if (!view_cursors_multiple(view))
1732 view_cursor_to(view, pos);
1733 /* redraw all windows in case some display the same file */
1734 vis_draw(vis);
1736 return keys;
1739 static const char *redo(Vis *vis, const char *keys, const Arg *arg) {
1740 size_t pos = text_redo(vis_text(vis));
1741 if (pos != EPOS) {
1742 View *view = vis_view(vis);
1743 if (!view_cursors_multiple(view))
1744 view_cursor_to(view, pos);
1745 /* redraw all windows in case some display the same file */
1746 vis_draw(vis);
1748 return keys;
1751 static const char *earlier(Vis *vis, const char *keys, const Arg *arg) {
1752 size_t pos = EPOS;
1753 VisCountIterator it = vis_count_iterator_get(vis, 1);
1754 while (vis_count_iterator_next(&it))
1755 pos = text_earlier(vis_text(vis));
1756 if (pos != EPOS) {
1757 view_cursor_to(vis_view(vis), pos);
1758 /* redraw all windows in case some display the same file */
1759 vis_draw(vis);
1761 return keys;
1764 static const char *later(Vis *vis, const char *keys, const Arg *arg) {
1765 size_t pos = EPOS;
1766 VisCountIterator it = vis_count_iterator_get(vis, 1);
1767 while (vis_count_iterator_next(&it))
1768 pos = text_later(vis_text(vis));
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 *delete(Vis *vis, const char *keys, const Arg *arg) {
1778 vis_operator(vis, VIS_OP_DELETE);
1779 vis_motion(vis, arg->i);
1780 return keys;
1783 static const char *insert_register(Vis *vis, const char *keys, const Arg *arg) {
1784 if (!keys[0])
1785 return NULL;
1786 if (keys[1])
1787 return vis_keys_next(vis, keys);
1788 enum VisRegister reg = vis_register_from(vis, keys[0]);
1789 if (reg != VIS_REG_INVALID) {
1790 vis_register(vis, reg);
1791 vis_operator(vis, VIS_OP_PUT_BEFORE_END);
1793 return keys+1;
1796 static const char *prompt_show(Vis *vis, const char *keys, const Arg *arg) {
1797 vis_prompt_show(vis, arg->s);
1798 return keys;
1801 static const char *insert_verbatim(Vis *vis, const char *keys, const Arg *arg) {
1802 Rune rune = 0;
1803 char buf[4], type = keys[0];
1804 const char *data = NULL;
1805 int len = 0, count = 0, base = 0;
1806 switch (type) {
1807 case '\0':
1808 return NULL;
1809 case 'o':
1810 case 'O':
1811 count = 3;
1812 base = 8;
1813 break;
1814 case 'U':
1815 count = 4;
1816 /* fall through */
1817 case 'u':
1818 count += 4;
1819 base = 16;
1820 break;
1821 case 'x':
1822 case 'X':
1823 count = 2;
1824 base = 16;
1825 break;
1826 default:
1827 if ('0' <= type && type <= '9') {
1828 rune = type - '0';
1829 count = 2;
1830 base = 10;
1832 break;
1835 if (base) {
1836 for (keys++; keys[0] && count > 0; keys++, count--) {
1837 int v = 0;
1838 if (base == 8 && '0' <= keys[0] && keys[0] <= '7') {
1839 v = keys[0] - '0';
1840 } else if ((base == 10 || base == 16) && '0' <= keys[0] && keys[0] <= '9') {
1841 v = keys[0] - '0';
1842 } else if (base == 16 && 'a' <= keys[0] && keys[0] <= 'f') {
1843 v = 10 + keys[0] - 'a';
1844 } else if (base == 16 && 'A' <= keys[0] && keys[0] <= 'F') {
1845 v = 10 + keys[0] - 'A';
1846 } else {
1847 count = 0;
1848 break;
1850 rune = rune * base + v;
1853 if (count > 0)
1854 return NULL;
1855 if (type == 'u' || type == 'U') {
1856 len = runetochar(buf, &rune);
1857 } else {
1858 buf[0] = rune;
1859 len = 1;
1862 data = buf;
1863 } else {
1864 const char *next = vis_keys_next(vis, keys);
1865 if (!next)
1866 return NULL;
1867 if ((rune = vis_keys_codepoint(vis, keys)) != (Rune)-1) {
1868 len = runetochar(buf, &rune);
1869 data = buf;
1870 } else {
1871 vis_info_show(vis, "Unknown key");
1873 keys = next;
1876 if (len > 0)
1877 vis_insert_key(vis, data, len);
1878 return keys;
1881 static const char *wscroll(Vis *vis, const char *keys, const Arg *arg) {
1882 View *view = vis_view(vis);
1883 int count = vis_count_get(vis);
1884 switch (arg->i) {
1885 case -PAGE:
1886 view_scroll_page_up(view);
1887 break;
1888 case +PAGE:
1889 view_scroll_page_down(view);
1890 break;
1891 case -PAGE_HALF:
1892 view_scroll_halfpage_up(view);
1893 break;
1894 case +PAGE_HALF:
1895 view_scroll_halfpage_down(view);
1896 break;
1897 default:
1898 if (count == VIS_COUNT_UNKNOWN)
1899 count = arg->i < 0 ? -arg->i : arg->i;
1900 if (arg->i < 0)
1901 view_scroll_up(view, count);
1902 else
1903 view_scroll_down(view, count);
1904 break;
1906 return keys;
1909 static const char *wslide(Vis *vis, const char *keys, const Arg *arg) {
1910 View *view = vis_view(vis);
1911 int count = vis_count_get(vis);
1912 if (count == VIS_COUNT_UNKNOWN)
1913 count = arg->i < 0 ? -arg->i : arg->i;
1914 if (arg->i >= 0)
1915 view_slide_down(view, count);
1916 else
1917 view_slide_up(view, count);
1918 return keys;
1921 static const char *call(Vis *vis, const char *keys, const Arg *arg) {
1922 arg->f(vis);
1923 return keys;
1926 static const char *window(Vis *vis, const char *keys, const Arg *arg) {
1927 arg->w(vis_view(vis));
1928 return keys;
1931 static const char *openline(Vis *vis, const char *keys, const Arg *arg) {
1932 vis_operator(vis, VIS_OP_MODESWITCH, VIS_MODE_INSERT);
1933 if (arg->i > 0) {
1934 vis_motion(vis, VIS_MOVE_LINE_END);
1935 vis_keys_feed(vis, "<Enter>");
1936 } else {
1937 if (vis_get_autoindent(vis)) {
1938 vis_motion(vis, VIS_MOVE_LINE_START);
1939 vis_keys_feed(vis, "<vis-motion-line-start>");
1940 } else {
1941 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
1942 vis_keys_feed(vis, "<vis-motion-line-begin>");
1944 vis_keys_feed(vis, "<Enter><Up>");
1946 return keys;
1949 static const char *join(Vis *vis, const char *keys, const Arg *arg) {
1950 bool normal = (vis_mode_get(vis) == VIS_MODE_NORMAL);
1951 vis_operator(vis, VIS_OP_JOIN, arg->s);
1952 if (normal) {
1953 int count = vis_count_get_default(vis, 0);
1954 if (count)
1955 vis_count_set(vis, count-1);
1956 vis_motion(vis, VIS_MOVE_LINE_NEXT);
1958 return keys;
1961 static const char *switchmode(Vis *vis, const char *keys, const Arg *arg) {
1962 vis_mode_switch(vis, arg->i);
1963 return keys;
1966 static const char *insertmode(Vis *vis, const char *keys, const Arg *arg) {
1967 vis_operator(vis, VIS_OP_MODESWITCH, VIS_MODE_INSERT);
1968 vis_motion(vis, arg->i);
1969 return keys;
1972 static const char *replacemode(Vis *vis, const char *keys, const Arg *arg) {
1973 vis_operator(vis, VIS_OP_MODESWITCH, VIS_MODE_REPLACE);
1974 vis_motion(vis, arg->i);
1975 return keys;
1978 static const char *unicode_info(Vis *vis, const char *keys, const Arg *arg) {
1979 View *view = vis_view(vis);
1980 Text *txt = vis_text(vis);
1981 size_t start = view_cursor_get(view);
1982 size_t end = text_char_next(txt, start);
1983 char *grapheme = text_bytes_alloc0(txt, start, end-start), *codepoint = grapheme;
1984 if (!grapheme)
1985 return keys;
1986 Buffer info;
1987 buffer_init(&info);
1988 mbstate_t ps = { 0 };
1989 Iterator it = text_iterator_get(txt, start);
1990 for (size_t pos = start; it.pos < end; pos = it.pos) {
1991 if (!text_iterator_codepoint_next(&it, NULL)) {
1992 vis_info_show(vis, "Failed to parse code point");
1993 goto err;
1995 size_t len = it.pos - pos;
1996 wchar_t wc = 0xFFFD;
1997 size_t res = mbrtowc(&wc, codepoint, len, &ps);
1998 bool combining = false;
1999 if (res != (size_t)-1 && res != (size_t)-2)
2000 combining = (wc != L'\0' && wcwidth(wc) == 0);
2001 unsigned char ch = *codepoint;
2002 if (ch < 128 && !isprint(ch))
2003 buffer_appendf(&info, "<^%c> ", ch == 127 ? '?' : ch + 64);
2004 else
2005 buffer_appendf(&info, "<%s%.*s> ", combining ? " " : "", (int)len, codepoint);
2006 if (arg->i == VIS_ACTION_UNICODE_INFO) {
2007 buffer_appendf(&info, "U+%04x ", wc);
2008 } else {
2009 for (size_t i = 0; i < len; i++)
2010 buffer_appendf(&info, "%02x ", (uint8_t)codepoint[i]);
2012 codepoint += len;
2014 vis_info_show(vis, "%s", buffer_content0(&info));
2015 err:
2016 free(grapheme);
2017 buffer_release(&info);
2018 return keys;
2021 static const char *percent(Vis *vis, const char *keys, const Arg *arg) {
2022 if (vis_count_get(vis) == VIS_COUNT_UNKNOWN)
2023 vis_motion(vis, VIS_MOVE_BRACKET_MATCH);
2024 else
2025 vis_motion(vis, VIS_MOVE_PERCENT);
2026 return keys;
2029 static Vis *vis;
2031 static void signal_handler(int signum, siginfo_t *siginfo, void *context) {
2032 vis_signal_handler(vis, signum, siginfo, context);
2035 int main(int argc, char *argv[]) {
2037 VisEvent event = {
2038 .init = vis_lua_init,
2039 .start = vis_lua_start,
2040 .quit = vis_lua_quit,
2041 .mode_insert_input = vis_lua_mode_insert_input,
2042 .mode_replace_input = vis_lua_mode_replace_input,
2043 .file_open = vis_lua_file_open,
2044 .file_save_pre = vis_lua_file_save_pre,
2045 .file_save_post = vis_lua_file_save_post,
2046 .file_close = vis_lua_file_close,
2047 .win_open = vis_lua_win_open,
2048 .win_close = vis_lua_win_close,
2049 .win_highlight = vis_lua_win_highlight,
2050 .win_status = vis_lua_win_status,
2053 vis = vis_new(ui_term_new(), &event);
2054 if (!vis)
2055 return EXIT_FAILURE;
2057 for (int i = 0; i < LENGTH(vis_action); i++) {
2058 const KeyAction *action = &vis_action[i];
2059 if (!vis_action_register(vis, action))
2060 vis_die(vis, "Could not register action: %s\n", action->name);
2063 for (int i = 0; i < LENGTH(default_bindings); i++) {
2064 for (const KeyBinding **binding = default_bindings[i]; binding && *binding; binding++) {
2065 for (const KeyBinding *kb = *binding; kb->key; kb++) {
2066 vis_mode_map(vis, i, false, kb->key, kb);
2071 for (const char **k = keymaps; k[0]; k += 2)
2072 vis_keymap_add(vis, k[0], k[1]);
2074 /* install signal handlers etc. */
2075 struct sigaction sa;
2076 memset(&sa, 0, sizeof sa);
2077 sigfillset(&sa.sa_mask);
2078 sa.sa_flags = SA_SIGINFO;
2079 sa.sa_sigaction = signal_handler;
2080 if (sigaction(SIGBUS, &sa, NULL) == -1 ||
2081 sigaction(SIGINT, &sa, NULL) == -1 ||
2082 sigaction(SIGCONT, &sa, NULL) == -1 ||
2083 sigaction(SIGWINCH, &sa, NULL) == -1 ||
2084 sigaction(SIGTERM, &sa, NULL) == -1 ||
2085 sigaction(SIGHUP, &sa, NULL) == -1) {
2086 vis_die(vis, "Failed to set signal handler: %s\n", strerror(errno));
2089 sa.sa_handler = SIG_IGN;
2090 if (sigaction(SIGPIPE, &sa, NULL) == -1)
2091 vis_die(vis, "Failed to ignore SIGPIPE\n");
2093 sigset_t blockset;
2094 sigemptyset(&blockset);
2095 sigaddset(&blockset, SIGBUS);
2096 sigaddset(&blockset, SIGCONT);
2097 sigaddset(&blockset, SIGWINCH);
2098 sigaddset(&blockset, SIGTERM);
2099 sigaddset(&blockset, SIGHUP);
2100 if (sigprocmask(SIG_BLOCK, &blockset, NULL) == -1)
2101 vis_die(vis, "Failed to block signals\n");
2103 for (int i = 1; i < argc; i++) {
2104 if (argv[i][0] != '-') {
2105 continue;
2106 } else if (strcmp(argv[i], "-") == 0) {
2107 continue;
2108 } else if (strcmp(argv[i], "--") == 0) {
2109 break;
2110 } else if (strcmp(argv[i], "-v") == 0) {
2111 printf("vis %s%s%s%s%s%s%s\n", VERSION,
2112 CONFIG_CURSES ? " +curses" : "",
2113 CONFIG_LUA ? " +lua" : "",
2114 CONFIG_LPEG ? " +lpeg" : "",
2115 CONFIG_TRE ? " +tre" : "",
2116 CONFIG_ACL ? " +acl" : "",
2117 CONFIG_SELINUX ? " +selinux" : "");
2118 return 0;
2119 } else {
2120 fprintf(stderr, "Unknown command option: %s\n", argv[i]);
2121 return 1;
2125 char *cmd = NULL;
2126 bool end_of_options = false, win_created = false;
2128 for (int i = 1; i < argc; i++) {
2129 if (argv[i][0] == '-' && !end_of_options) {
2130 if (strcmp(argv[i], "-") == 0) {
2131 if (!vis_window_new_fd(vis, STDOUT_FILENO))
2132 vis_die(vis, "Can not create empty buffer\n");
2133 ssize_t len = 0;
2134 char buf[PIPE_BUF];
2135 Text *txt = vis_text(vis);
2136 while ((len = read(STDIN_FILENO, buf, sizeof buf)) > 0)
2137 text_insert(txt, text_size(txt), buf, len);
2138 if (len == -1)
2139 vis_die(vis, "Can not read from stdin\n");
2140 text_snapshot(txt);
2141 int fd = open("/dev/tty", O_RDWR);
2142 if (fd == -1)
2143 vis_die(vis, "Can not reopen stdin\n");
2144 dup2(fd, STDIN_FILENO);
2145 close(fd);
2146 } else if (strcmp(argv[i], "--") == 0) {
2147 end_of_options = true;
2148 continue;
2150 } else if (argv[i][0] == '+' && !end_of_options) {
2151 cmd = argv[i] + (argv[i][1] == '/' || argv[i][1] == '?');
2152 continue;
2153 } else if (!vis_window_new(vis, argv[i])) {
2154 vis_die(vis, "Can not load `%s': %s\n", argv[i], strerror(errno));
2156 win_created = true;
2157 if (cmd) {
2158 vis_prompt_cmd(vis, cmd);
2159 cmd = NULL;
2163 if (!vis_window(vis) && !win_created) {
2164 if (!vis_window_new(vis, NULL))
2165 vis_die(vis, "Can not create empty buffer\n");
2166 if (cmd)
2167 vis_prompt_cmd(vis, cmd);
2170 int status = vis_run(vis);
2171 vis_free(vis);
2172 return status;