single: include tools vis-{clipboard,complete,menu,open}
[vis.git] / main.c
blobf1e32fac53755db990cd62d1ea16a789b14b4763
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 <fcntl.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
11 #include "ui-curses.h"
12 #include "vis.h"
13 #include "vis-lua.h"
14 #include "text-util.h"
15 #include "text-motions.h"
16 #include "text-objects.h"
17 #include "util.h"
18 #include "libutf.h"
19 #include "array.h"
21 #define PAGE INT_MAX
22 #define PAGE_HALF (INT_MAX-1)
24 /** functions to be called from keybindings */
25 /* ignore key, do nothing */
26 static const char *nop(Vis*, const char *keys, const Arg *arg);
27 /* record/replay macro indicated by keys */
28 static const char *macro_record(Vis*, const char *keys, const Arg *arg);
29 static const char *macro_replay(Vis*, const char *keys, const Arg *arg);
30 /* temporarily suspend the editor and return to the shell, type 'fg' to get back */
31 static const char *suspend(Vis*, const char *keys, const Arg *arg);
32 /* switch to mode indicated by arg->i */
33 static const char *switchmode(Vis*, const char *keys, const Arg *arg);
34 /* switch to insert mode after performing movement indicated by arg->i */
35 static const char *insertmode(Vis*, const char *keys, const Arg *arg);
36 /* set mark indicated by keys to current cursor position */
37 static const char *mark_set(Vis*, const char *keys, const Arg *arg);
38 /* add a new line either before or after the one where the cursor currently is */
39 static const char *openline(Vis*, const char *keys, const Arg *arg);
40 /* join lines from current cursor position to movement indicated by arg */
41 static const char *join(Vis*, const char *keys, const Arg *arg);
42 /* perform last action i.e. action_prev again */
43 static const char *repeat(Vis*, const char *keys, const Arg *arg);
44 /* replace character at cursor with one from keys */
45 static const char *replace(Vis*, const char *keys, const Arg *arg);
46 /* create a new cursor on the previous (arg->i < 0) or next (arg->i > 0) line */
47 static const char *cursors_new(Vis*, const char *keys, const Arg *arg);
48 /* try to align all cursors on the same column */
49 static const char *cursors_align(Vis*, const char *keys, const Arg *arg);
50 /* try to align all cursors by inserting the correct amount of white spaces */
51 static const char *cursors_align_indent(Vis*, const char *keys, const Arg *arg);
52 /* remove all but the primary cursor and their selections */
53 static const char *cursors_clear(Vis*, const char *keys, const Arg *arg);
54 /* remove the least recently added cursor */
55 static const char *cursors_remove(Vis*, const char *keys, const Arg *arg);
56 /* remove count (or arg->i)-th cursor column */
57 static const char *cursors_remove_column(Vis*, const char *keys, const Arg *arg);
58 /* remove all but the count (or arg->i)-th cursor column */
59 static const char *cursors_remove_column_except(Vis*, const char *keys, const Arg *arg);
60 /* move to the previous (arg->i < 0) or next (arg->i > 0) cursor */
61 static const char *cursors_navigate(Vis*, const char *keys, const Arg *arg);
62 /* select the word the cursor is currently over */
63 static const char *cursors_select(Vis*, const char *keys, const Arg *arg);
64 /* select the next region matching the current selection */
65 static const char *cursors_select_next(Vis*, const char *keys, const Arg *arg);
66 /* clear current selection but select next match */
67 static const char *cursors_select_skip(Vis*, const char *keys, const Arg *arg);
68 /* rotate selection content count times left (arg->i < 0) or right (arg->i > 0) */
69 static const char *selections_rotate(Vis*, const char *keys, const Arg *arg);
70 /* remove leading and trailing white spaces from selections */
71 static const char *selections_trim(Vis*, const char *keys, const Arg *arg);
72 /* adjust current used count according to keys */
73 static const char *count(Vis*, const char *keys, const Arg *arg);
74 /* move to the count-th line or if not given either to the first (arg->i < 0)
75 * or last (arg->i > 0) line of file */
76 static const char *gotoline(Vis*, const char *keys, const Arg *arg);
77 /* set motion type either LINEWISE or CHARWISE via arg->i */
78 static const char *motiontype(Vis*, const char *keys, const Arg *arg);
79 /* make the current action use the operator indicated by arg->i */
80 static const char *operator(Vis*, const char *keys, const Arg *arg);
81 /* use arg->s as command for the filter operator */
82 static const char *operator_filter(Vis*, const char *keys, const Arg *arg);
83 /* blocks to read a key and performs movement indicated by arg->i which
84 * should be one of VIS_MOVE_{RIGHT,LEFT}_{TO,TILL} */
85 static const char *movement_key(Vis*, const char *keys, const Arg *arg);
86 /* perform the movement as indicated by arg->i */
87 static const char *movement(Vis*, const char *keys, const Arg *arg);
88 /* let the current operator affect the range indicated by the text object arg->i */
89 static const char *textobj(Vis*, const char *keys, const Arg *arg);
90 /* move to the other end of selected text */
91 static const char *selection_end(Vis*, const char *keys, const Arg *arg);
92 /* restore least recently used selection */
93 static const char *selection_restore(Vis*, const char *keys, const Arg *arg);
94 /* use register indicated by keys for the current operator */
95 static const char *reg(Vis*, const char *keys, const Arg *arg);
96 /* perform arg->i motion with a mark indicated by keys as argument */
97 static const char *mark_motion(Vis*, const char *keys, const Arg *arg);
98 /* {un,re}do last action, redraw window */
99 static const char *undo(Vis*, const char *keys, const Arg *arg);
100 static const char *redo(Vis*, const char *keys, const Arg *arg);
101 /* earlier, later action chronologically, redraw window */
102 static const char *earlier(Vis*, const char *keys, const Arg *arg);
103 static const char *later(Vis*, const char *keys, const Arg *arg);
104 /* delete from the current cursor position to the end of
105 * movement as indicated by arg->i */
106 static const char *delete(Vis*, const char *keys, const Arg *arg);
107 /* insert register content indicated by keys at current cursor position */
108 static const char *insert_register(Vis*, const char *keys, const Arg *arg);
109 /* show a user prompt to get input with title arg->s */
110 static const char *prompt_show(Vis*, const char *keys, const Arg *arg);
111 /* blocks to read 3 consecutive digits and inserts the corresponding byte value */
112 static const char *insert_verbatim(Vis*, const char *keys, const Arg *arg);
113 /* scroll window content according to arg->i which can be either PAGE, PAGE_HALF,
114 * or an arbitrary number of lines. a multiplier overrides what is given in arg->i.
115 * negative values scroll back, positive forward. */
116 static const char *wscroll(Vis*, const char *keys, const Arg *arg);
117 /* similar to scroll, but do only move window content not cursor position */
118 static const char *wslide(Vis*, const char *keys, const Arg *arg);
119 /* call editor function as indicated by arg->f */
120 static const char *call(Vis*, const char *keys, const Arg *arg);
121 /* call window function as indicated by arg->w */
122 static const char *window(Vis*, const char *keys, const Arg *arg);
123 /* show info about Unicode character at cursor position */
124 static const char *unicode_info(Vis*, const char *keys, const Arg *arg);
125 /* either go to count % of ile or to matching item */
126 static const char *percent(Vis*, const char *keys, const Arg *arg);
127 /* either increment (arg->i > 0) or decrement (arg->i < 0) number under cursor */
128 static const char *number_increment_decrement(Vis*, const char *keys, const Arg *arg);
129 /* open a filename under cursor in same (!arg->b) or new (arg->b) window */
130 static const char *open_file_under_cursor(Vis*, const char *keys, const Arg *arg);
131 /* complete input text at cursor based on the words in the current file */
132 static const char *complete_word(Vis*, const char *keys, const Arg *arg);
133 /* complete input text at cursor based on file names of the current directory */
134 static const char *complete_filename(Vis*, const char *keys, const Arg *arg);
136 enum {
137 VIS_ACTION_EDITOR_SUSPEND,
138 VIS_ACTION_CURSOR_CHAR_PREV,
139 VIS_ACTION_CURSOR_CHAR_NEXT,
140 VIS_ACTION_CURSOR_LINE_CHAR_PREV,
141 VIS_ACTION_CURSOR_LINE_CHAR_NEXT,
142 VIS_ACTION_CURSOR_WORD_START_PREV,
143 VIS_ACTION_CURSOR_WORD_START_NEXT,
144 VIS_ACTION_CURSOR_WORD_END_PREV,
145 VIS_ACTION_CURSOR_WORD_END_NEXT,
146 VIS_ACTION_CURSOR_LONGWORD_START_PREV,
147 VIS_ACTION_CURSOR_LONGWORD_START_NEXT,
148 VIS_ACTION_CURSOR_LONGWORD_END_PREV,
149 VIS_ACTION_CURSOR_LONGWORD_END_NEXT,
150 VIS_ACTION_CURSOR_LINE_UP,
151 VIS_ACTION_CURSOR_LINE_DOWN,
152 VIS_ACTION_CURSOR_LINE_START,
153 VIS_ACTION_CURSOR_LINE_FINISH,
154 VIS_ACTION_CURSOR_LINE_BEGIN,
155 VIS_ACTION_CURSOR_LINE_END,
156 VIS_ACTION_CURSOR_LINE_LASTCHAR,
157 VIS_ACTION_CURSOR_SCREEN_LINE_UP,
158 VIS_ACTION_CURSOR_SCREEN_LINE_DOWN,
159 VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN,
160 VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE,
161 VIS_ACTION_CURSOR_SCREEN_LINE_END,
162 VIS_ACTION_CURSOR_PERCENT,
163 VIS_ACTION_CURSOR_BYTE,
164 VIS_ACTION_CURSOR_BYTE_LEFT,
165 VIS_ACTION_CURSOR_BYTE_RIGHT,
166 VIS_ACTION_CURSOR_PARAGRAPH_PREV,
167 VIS_ACTION_CURSOR_PARAGRAPH_NEXT,
168 VIS_ACTION_CURSOR_SENTENCE_PREV,
169 VIS_ACTION_CURSOR_SENTENCE_NEXT,
170 VIS_ACTION_CURSOR_FUNCTION_START_PREV,
171 VIS_ACTION_CURSOR_FUNCTION_END_PREV,
172 VIS_ACTION_CURSOR_FUNCTION_START_NEXT,
173 VIS_ACTION_CURSOR_FUNCTION_END_NEXT,
174 VIS_ACTION_CURSOR_BLOCK_START,
175 VIS_ACTION_CURSOR_BLOCK_END,
176 VIS_ACTION_CURSOR_PARENTHESE_START,
177 VIS_ACTION_CURSOR_PARENTHESE_END,
178 VIS_ACTION_CURSOR_COLUMN,
179 VIS_ACTION_CURSOR_LINE_FIRST,
180 VIS_ACTION_CURSOR_LINE_LAST,
181 VIS_ACTION_CURSOR_WINDOW_LINE_TOP,
182 VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE,
183 VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM,
184 VIS_ACTION_CURSOR_SEARCH_NEXT,
185 VIS_ACTION_CURSOR_SEARCH_PREV,
186 VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD,
187 VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD,
188 VIS_ACTION_WINDOW_PAGE_UP,
189 VIS_ACTION_WINDOW_PAGE_DOWN,
190 VIS_ACTION_WINDOW_HALFPAGE_UP,
191 VIS_ACTION_WINDOW_HALFPAGE_DOWN,
192 VIS_ACTION_MODE_NORMAL,
193 VIS_ACTION_MODE_VISUAL,
194 VIS_ACTION_MODE_VISUAL_LINE,
195 VIS_ACTION_MODE_INSERT,
196 VIS_ACTION_MODE_REPLACE,
197 VIS_ACTION_MODE_OPERATOR_PENDING,
198 VIS_ACTION_DELETE_CHAR_PREV,
199 VIS_ACTION_DELETE_CHAR_NEXT,
200 VIS_ACTION_DELETE_LINE_BEGIN,
201 VIS_ACTION_DELETE_WORD_PREV,
202 VIS_ACTION_JUMPLIST_PREV,
203 VIS_ACTION_JUMPLIST_NEXT,
204 VIS_ACTION_CHANGELIST_PREV,
205 VIS_ACTION_CHANGELIST_NEXT,
206 VIS_ACTION_UNDO,
207 VIS_ACTION_REDO,
208 VIS_ACTION_EARLIER,
209 VIS_ACTION_LATER,
210 VIS_ACTION_MACRO_RECORD,
211 VIS_ACTION_MACRO_REPLAY,
212 VIS_ACTION_MARK_SET,
213 VIS_ACTION_MARK_GOTO,
214 VIS_ACTION_MARK_GOTO_LINE,
215 VIS_ACTION_REDRAW,
216 VIS_ACTION_REPLACE_CHAR,
217 VIS_ACTION_TOTILL_REPEAT,
218 VIS_ACTION_TOTILL_REVERSE,
219 VIS_ACTION_PROMPT_SEARCH_FORWARD,
220 VIS_ACTION_PROMPT_SEARCH_BACKWARD,
221 VIS_ACTION_TILL_LEFT,
222 VIS_ACTION_TILL_RIGHT,
223 VIS_ACTION_TO_LEFT,
224 VIS_ACTION_TO_RIGHT,
225 VIS_ACTION_REGISTER,
226 VIS_ACTION_OPERATOR_CHANGE,
227 VIS_ACTION_OPERATOR_DELETE,
228 VIS_ACTION_OPERATOR_YANK,
229 VIS_ACTION_OPERATOR_SHIFT_LEFT,
230 VIS_ACTION_OPERATOR_SHIFT_RIGHT,
231 VIS_ACTION_OPERATOR_CASE_LOWER,
232 VIS_ACTION_OPERATOR_CASE_UPPER,
233 VIS_ACTION_OPERATOR_CASE_SWAP,
234 VIS_ACTION_OPERATOR_FILTER,
235 VIS_ACTION_OPERATOR_FILTER_FMT,
236 VIS_ACTION_COUNT,
237 VIS_ACTION_INSERT_NEWLINE,
238 VIS_ACTION_INSERT_TAB,
239 VIS_ACTION_INSERT_VERBATIM,
240 VIS_ACTION_INSERT_REGISTER,
241 VIS_ACTION_WINDOW_NEXT,
242 VIS_ACTION_WINDOW_PREV,
243 VIS_ACTION_APPEND_CHAR_NEXT,
244 VIS_ACTION_APPEND_LINE_END,
245 VIS_ACTION_INSERT_LINE_START,
246 VIS_ACTION_OPEN_LINE_ABOVE,
247 VIS_ACTION_OPEN_LINE_BELOW,
248 VIS_ACTION_JOIN_LINES,
249 VIS_ACTION_JOIN_LINES_TRIM,
250 VIS_ACTION_PROMPT_SHOW,
251 VIS_ACTION_REPEAT,
252 VIS_ACTION_SELECTION_FLIP,
253 VIS_ACTION_SELECTION_RESTORE,
254 VIS_ACTION_WINDOW_REDRAW_TOP,
255 VIS_ACTION_WINDOW_REDRAW_CENTER,
256 VIS_ACTION_WINDOW_REDRAW_BOTTOM,
257 VIS_ACTION_WINDOW_SLIDE_UP,
258 VIS_ACTION_WINDOW_SLIDE_DOWN,
259 VIS_ACTION_PUT_AFTER,
260 VIS_ACTION_PUT_BEFORE,
261 VIS_ACTION_PUT_AFTER_END,
262 VIS_ACTION_PUT_BEFORE_END,
263 VIS_ACTION_CURSOR_SELECT_WORD,
264 VIS_ACTION_CURSORS_NEW_LINE_ABOVE,
265 VIS_ACTION_CURSORS_NEW_LINE_ABOVE_FIRST,
266 VIS_ACTION_CURSORS_NEW_LINE_BELOW,
267 VIS_ACTION_CURSORS_NEW_LINE_BELOW_LAST,
268 VIS_ACTION_CURSORS_NEW_LINES_BEGIN,
269 VIS_ACTION_CURSORS_NEW_LINES_END,
270 VIS_ACTION_CURSORS_NEW_MATCH_NEXT,
271 VIS_ACTION_CURSORS_NEW_MATCH_SKIP,
272 VIS_ACTION_CURSORS_ALIGN,
273 VIS_ACTION_CURSORS_ALIGN_INDENT_LEFT,
274 VIS_ACTION_CURSORS_ALIGN_INDENT_RIGHT,
275 VIS_ACTION_CURSORS_REMOVE_ALL,
276 VIS_ACTION_CURSORS_REMOVE_LAST,
277 VIS_ACTION_CURSORS_REMOVE_COLUMN,
278 VIS_ACTION_CURSORS_REMOVE_COLUMN_EXCEPT,
279 VIS_ACTION_CURSORS_PREV,
280 VIS_ACTION_CURSORS_NEXT,
281 VIS_ACTION_SELECTIONS_ROTATE_LEFT,
282 VIS_ACTION_SELECTIONS_ROTATE_RIGHT,
283 VIS_ACTION_SELECTIONS_TRIM,
284 VIS_ACTION_TEXT_OBJECT_WORD_OUTER,
285 VIS_ACTION_TEXT_OBJECT_WORD_INNER,
286 VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER,
287 VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER,
288 VIS_ACTION_TEXT_OBJECT_SENTENCE,
289 VIS_ACTION_TEXT_OBJECT_PARAGRAPH,
290 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER,
291 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER,
292 VIS_ACTION_TEXT_OBJECT_PARANTHESE_OUTER,
293 VIS_ACTION_TEXT_OBJECT_PARANTHESE_INNER,
294 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER,
295 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER,
296 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER,
297 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER,
298 VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER,
299 VIS_ACTION_TEXT_OBJECT_QUOTE_INNER,
300 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER,
301 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER,
302 VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER,
303 VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER,
304 VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER,
305 VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER,
306 VIS_ACTION_TEXT_OBJECT_FUNCTION_OUTER,
307 VIS_ACTION_TEXT_OBJECT_FUNCTION_INNER,
308 VIS_ACTION_TEXT_OBJECT_LINE_OUTER,
309 VIS_ACTION_TEXT_OBJECT_LINE_INNER,
310 VIS_ACTION_TEXT_OBJECT_INDENTATION,
311 VIS_ACTION_TEXT_OBJECT_SEARCH_FORWARD,
312 VIS_ACTION_TEXT_OBJECT_SEARCH_BACKWARD,
313 VIS_ACTION_MOTION_CHARWISE,
314 VIS_ACTION_MOTION_LINEWISE,
315 VIS_ACTION_UNICODE_INFO,
316 VIS_ACTION_UTF8_INFO,
317 VIS_ACTION_NUMBER_INCREMENT,
318 VIS_ACTION_NUMBER_DECREMENT,
319 VIS_ACTION_OPEN_FILE_UNDER_CURSOR,
320 VIS_ACTION_OPEN_FILE_UNDER_CURSOR_NEW_WINDOW,
321 VIS_ACTION_COMPLETE_WORD,
322 VIS_ACTION_COMPLETE_FILENAME,
323 VIS_ACTION_NOP,
326 static const KeyAction vis_action[] = {
327 [VIS_ACTION_EDITOR_SUSPEND] = {
328 "editor-suspend",
329 "Suspend the editor",
330 suspend,
332 [VIS_ACTION_CURSOR_CHAR_PREV] = {
333 "cursor-char-prev",
334 "Move cursor left, to the previous character",
335 movement, { .i = VIS_MOVE_CHAR_PREV }
337 [VIS_ACTION_CURSOR_CHAR_NEXT] = {
338 "cursor-char-next",
339 "Move cursor right, to the next character",
340 movement, { .i = VIS_MOVE_CHAR_NEXT }
342 [VIS_ACTION_CURSOR_LINE_CHAR_PREV] = {
343 "cursor-line-char-prev",
344 "Move cursor left, to the previous character on the same line",
345 movement, { .i = VIS_MOVE_LINE_CHAR_PREV }
347 [VIS_ACTION_CURSOR_LINE_CHAR_NEXT] = {
348 "cursor-line-char-next",
349 "Move cursor right, to the next character on the same line",
350 movement, { .i = VIS_MOVE_LINE_CHAR_NEXT }
352 [VIS_ACTION_CURSOR_WORD_START_PREV] = {
353 "cursor-word-start-prev",
354 "Move cursor words backwards",
355 movement, { .i = VIS_MOVE_WORD_START_PREV }
357 [VIS_ACTION_CURSOR_WORD_START_NEXT] = {
358 "cursor-word-start-next",
359 "Move cursor words forwards",
360 movement, { .i = VIS_MOVE_WORD_START_NEXT }
362 [VIS_ACTION_CURSOR_WORD_END_PREV] = {
363 "cursor-word-end-prev",
364 "Move cursor backwards to the end of word",
365 movement, { .i = VIS_MOVE_WORD_END_PREV }
367 [VIS_ACTION_CURSOR_WORD_END_NEXT] = {
368 "cursor-word-end-next",
369 "Move cursor forward to the end of word",
370 movement, { .i = VIS_MOVE_WORD_END_NEXT }
372 [VIS_ACTION_CURSOR_LONGWORD_START_PREV] = {
373 "cursor-longword-start-prev",
374 "Move cursor WORDS backwards",
375 movement, { .i = VIS_MOVE_LONGWORD_START_PREV }
377 [VIS_ACTION_CURSOR_LONGWORD_START_NEXT] = {
378 "cursor-longword-start-next",
379 "Move cursor WORDS forwards",
380 movement, { .i = VIS_MOVE_LONGWORD_START_NEXT }
382 [VIS_ACTION_CURSOR_LONGWORD_END_PREV] = {
383 "cursor-longword-end-prev",
384 "Move cursor backwards to the end of WORD",
385 movement, { .i = VIS_MOVE_LONGWORD_END_PREV }
387 [VIS_ACTION_CURSOR_LONGWORD_END_NEXT] = {
388 "cursor-longword-end-next",
389 "Move cursor forward to the end of WORD",
390 movement, { .i = VIS_MOVE_LONGWORD_END_NEXT }
392 [VIS_ACTION_CURSOR_LINE_UP] = {
393 "cursor-line-up",
394 "Move cursor line upwards",
395 movement, { .i = VIS_MOVE_LINE_UP }
397 [VIS_ACTION_CURSOR_LINE_DOWN] = {
398 "cursor-line-down",
399 "Move cursor line downwards",
400 movement, { .i = VIS_MOVE_LINE_DOWN }
402 [VIS_ACTION_CURSOR_LINE_START] = {
403 "cursor-line-start",
404 "Move cursor to first non-blank character of the line",
405 movement, { .i = VIS_MOVE_LINE_START }
407 [VIS_ACTION_CURSOR_LINE_FINISH] = {
408 "cursor-line-finish",
409 "Move cursor to last non-blank character of the line",
410 movement, { .i = VIS_MOVE_LINE_FINISH }
412 [VIS_ACTION_CURSOR_LINE_BEGIN] = {
413 "cursor-line-begin",
414 "Move cursor to first character of the line",
415 movement, { .i = VIS_MOVE_LINE_BEGIN }
417 [VIS_ACTION_CURSOR_LINE_END] = {
418 "cursor-line-end",
419 "Move cursor to end of the line",
420 movement, { .i = VIS_MOVE_LINE_END }
422 [VIS_ACTION_CURSOR_LINE_LASTCHAR] = {
423 "cursor-line-lastchar",
424 "Move cursor to last character of the line",
425 movement, { .i = VIS_MOVE_LINE_LASTCHAR }
427 [VIS_ACTION_CURSOR_SCREEN_LINE_UP] = {
428 "cursor-screenline-up",
429 "Move cursor screen/display line upwards",
430 movement, { .i = VIS_MOVE_SCREEN_LINE_UP }
432 [VIS_ACTION_CURSOR_SCREEN_LINE_DOWN] = {
433 "cursor-screenline-down",
434 "Move cursor screen/display line downwards",
435 movement, { .i = VIS_MOVE_SCREEN_LINE_DOWN }
437 [VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN] = {
438 "cursor-screenline-begin",
439 "Move cursor to beginning of screen/display line",
440 movement, { .i = VIS_MOVE_SCREEN_LINE_BEGIN }
442 [VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE] = {
443 "cursor-screenline-middle",
444 "Move cursor to middle of screen/display line",
445 movement, { .i = VIS_MOVE_SCREEN_LINE_MIDDLE }
447 [VIS_ACTION_CURSOR_SCREEN_LINE_END] = {
448 "cursor-screenline-end",
449 "Move cursor to end of screen/display line",
450 movement, { .i = VIS_MOVE_SCREEN_LINE_END }
452 [VIS_ACTION_CURSOR_PERCENT] = {
453 "cursor-percent",
454 "Move to count % of file or matching item",
455 percent
457 [VIS_ACTION_CURSOR_BYTE] = {
458 "cursor-byte",
459 "Move to absolute byte position",
460 movement, { .i = VIS_MOVE_BYTE }
462 [VIS_ACTION_CURSOR_BYTE_LEFT] = {
463 "cursor-byte-left",
464 "Move count bytes to the left",
465 movement, { .i = VIS_MOVE_BYTE_LEFT }
467 [VIS_ACTION_CURSOR_BYTE_RIGHT] = {
468 "cursor-byte-right",
469 "Move count bytes to the right",
470 movement, { .i = VIS_MOVE_BYTE_RIGHT }
472 [VIS_ACTION_CURSOR_PARAGRAPH_PREV] = {
473 "cursor-paragraph-prev",
474 "Move cursor paragraph backward",
475 movement, { .i = VIS_MOVE_PARAGRAPH_PREV }
477 [VIS_ACTION_CURSOR_PARAGRAPH_NEXT] = {
478 "cursor-paragraph-next",
479 "Move cursor paragraph forward",
480 movement, { .i = VIS_MOVE_PARAGRAPH_NEXT }
482 [VIS_ACTION_CURSOR_SENTENCE_PREV] = {
483 "cursor-sentence-prev",
484 "Move cursor sentence backward",
485 movement, { .i = VIS_MOVE_SENTENCE_PREV }
487 [VIS_ACTION_CURSOR_SENTENCE_NEXT] = {
488 "cursor-sentence-next",
489 "Move cursor sentence forward",
490 movement, { .i = VIS_MOVE_SENTENCE_NEXT }
492 [VIS_ACTION_CURSOR_FUNCTION_START_PREV] = {
493 "cursor-function-start-prev",
494 "Move cursor backwards to start of function",
495 movement, { .i = VIS_MOVE_FUNCTION_START_PREV }
497 [VIS_ACTION_CURSOR_FUNCTION_START_NEXT] = {
498 "cursor-function-start-next",
499 "Move cursor forwards to start of function",
500 movement, { .i = VIS_MOVE_FUNCTION_START_NEXT }
502 [VIS_ACTION_CURSOR_FUNCTION_END_PREV] = {
503 "cursor-function-end-prev",
504 "Move cursor backwards to end of function",
505 movement, { .i = VIS_MOVE_FUNCTION_END_PREV }
507 [VIS_ACTION_CURSOR_FUNCTION_END_NEXT] = {
508 "cursor-function-end-next",
509 "Move cursor forwards to end of function",
510 movement, { .i = VIS_MOVE_FUNCTION_END_NEXT }
512 [VIS_ACTION_CURSOR_BLOCK_START] = {
513 "cursor-block-start",
514 "Move cursor to the opening curly brace in a block",
515 movement, { .i = VIS_MOVE_BLOCK_START }
517 [VIS_ACTION_CURSOR_BLOCK_END] = {
518 "cursor-block-end",
519 "Move cursor to the closing curly brace in a block",
520 movement, { .i = VIS_MOVE_BLOCK_END }
522 [VIS_ACTION_CURSOR_PARENTHESE_START] = {
523 "cursor-parenthese-start",
524 "Move cursor to the opening parenthese inside a pair of parentheses",
525 movement, { .i = VIS_MOVE_PARENTHESE_START }
527 [VIS_ACTION_CURSOR_PARENTHESE_END] = {
528 "cursor-parenthese-end",
529 "Move cursor to the closing parenthese inside a pair of parentheses",
530 movement, { .i = VIS_MOVE_PARENTHESE_END }
532 [VIS_ACTION_CURSOR_COLUMN] = {
533 "cursor-column",
534 "Move cursor to given column of current line",
535 movement, { .i = VIS_MOVE_COLUMN }
537 [VIS_ACTION_CURSOR_LINE_FIRST] = {
538 "cursor-line-first",
539 "Move cursor to given line (defaults to first)",
540 gotoline, { .i = -1 }
542 [VIS_ACTION_CURSOR_LINE_LAST] = {
543 "cursor-line-last",
544 "Move cursor to given line (defaults to last)",
545 gotoline, { .i = +1 }
547 [VIS_ACTION_CURSOR_WINDOW_LINE_TOP] = {
548 "cursor-window-line-top",
549 "Move cursor to top line of the window",
550 movement, { .i = VIS_MOVE_WINDOW_LINE_TOP }
552 [VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE] = {
553 "cursor-window-line-middle",
554 "Move cursor to middle line of the window",
555 movement, { .i = VIS_MOVE_WINDOW_LINE_MIDDLE }
557 [VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM] = {
558 "cursor-window-line-bottom",
559 "Move cursor to bottom line of the window",
560 movement, { .i = VIS_MOVE_WINDOW_LINE_BOTTOM }
562 [VIS_ACTION_CURSOR_SEARCH_NEXT] = {
563 "cursor-search-forward",
564 "Move cursor to next match",
565 movement, { .i = VIS_MOVE_SEARCH_NEXT }
567 [VIS_ACTION_CURSOR_SEARCH_PREV] = {
568 "cursor-search-backward",
569 "Move cursor to previous match",
570 movement, { .i = VIS_MOVE_SEARCH_PREV }
572 [VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD] = {
573 "cursor-search-word-forward",
574 "Move cursor to next occurence of the word under cursor",
575 movement, { .i = VIS_MOVE_SEARCH_WORD_FORWARD }
577 [VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD] = {
578 "cursor-search-word-backward",
579 "Move cursor to previous occurence of the word under cursor",
580 movement, { .i = VIS_MOVE_SEARCH_WORD_BACKWARD }
582 [VIS_ACTION_WINDOW_PAGE_UP] = {
583 "window-page-up",
584 "Scroll window pages backwards (upwards)",
585 wscroll, { .i = -PAGE }
587 [VIS_ACTION_WINDOW_HALFPAGE_UP] = {
588 "window-halfpage-up",
589 "Scroll window half pages backwards (upwards)",
590 wscroll, { .i = -PAGE_HALF }
592 [VIS_ACTION_WINDOW_PAGE_DOWN] = {
593 "window-page-down",
594 "Scroll window pages forwards (downwards)",
595 wscroll, { .i = +PAGE }
597 [VIS_ACTION_WINDOW_HALFPAGE_DOWN] = {
598 "window-halfpage-down",
599 "Scroll window half pages forwards (downwards)",
600 wscroll, { .i = +PAGE_HALF }
602 [VIS_ACTION_MODE_NORMAL] = {
603 "vis-mode-normal",
604 "Enter normal mode",
605 switchmode, { .i = VIS_MODE_NORMAL }
607 [VIS_ACTION_MODE_VISUAL] = {
608 "vis-mode-visual-charwise",
609 "Enter characterwise visual mode",
610 switchmode, { .i = VIS_MODE_VISUAL }
612 [VIS_ACTION_MODE_VISUAL_LINE] = {
613 "vis-mode-visual-linewise",
614 "Enter linewise visual mode",
615 switchmode, { .i = VIS_MODE_VISUAL_LINE }
617 [VIS_ACTION_MODE_INSERT] = {
618 "vis-mode-insert",
619 "Enter insert mode",
620 switchmode, { .i = VIS_MODE_INSERT }
622 [VIS_ACTION_MODE_REPLACE] = {
623 "vis-mode-replace",
624 "Enter replace mode",
625 switchmode, { .i = VIS_MODE_REPLACE }
627 [VIS_ACTION_MODE_OPERATOR_PENDING] = {
628 "vis-mode-operator-pending",
629 "Enter to operator pending mode",
630 switchmode, { .i = VIS_MODE_OPERATOR_PENDING }
632 [VIS_ACTION_DELETE_CHAR_PREV] = {
633 "delete-char-prev",
634 "Delete the previous character",
635 delete, { .i = VIS_MOVE_CHAR_PREV }
637 [VIS_ACTION_DELETE_CHAR_NEXT] = {
638 "delete-char-next",
639 "Delete the next character",
640 delete, { .i = VIS_MOVE_CHAR_NEXT }
642 [VIS_ACTION_DELETE_LINE_BEGIN] = {
643 "delete-line-begin",
644 "Delete until the start of the current line",
645 delete, { .i = VIS_MOVE_LINE_BEGIN }
647 [VIS_ACTION_DELETE_WORD_PREV] = {
648 "delete-word-prev",
649 "Delete the previous WORD",
650 delete, { .i = VIS_MOVE_WORD_START_PREV }
652 [VIS_ACTION_JUMPLIST_PREV] = {
653 "jumplist-prev",
654 "Go to older cursor position in jump list",
655 movement, { .i = VIS_MOVE_JUMPLIST_PREV }
657 [VIS_ACTION_JUMPLIST_NEXT] = {
658 "jumplist-next",
659 "Go to newer cursor position in jump list",
660 movement, { .i = VIS_MOVE_JUMPLIST_NEXT }
662 [VIS_ACTION_CHANGELIST_PREV] = {
663 "changelist-prev",
664 "Go to older cursor position in change list",
665 movement, { .i = VIS_MOVE_CHANGELIST_PREV }
667 [VIS_ACTION_CHANGELIST_NEXT] = {
668 "changelist-next",
669 "Go to newer cursor position in change list",
670 movement, { .i = VIS_MOVE_CHANGELIST_NEXT }
672 [VIS_ACTION_UNDO] = {
673 "editor-undo",
674 "Undo last change",
675 undo,
677 [VIS_ACTION_REDO] = {
678 "editor-redo",
679 "Redo last change",
680 redo,
682 [VIS_ACTION_EARLIER] = {
683 "editor-earlier",
684 "Goto older text state",
685 earlier,
687 [VIS_ACTION_LATER] = {
688 "editor-later",
689 "Goto newer text state",
690 later,
692 [VIS_ACTION_MACRO_RECORD] = {
693 "macro-record",
694 "Record macro into given register",
695 macro_record,
697 [VIS_ACTION_MACRO_REPLAY] = {
698 "macro-replay",
699 "Replay macro, execute the content of the given register",
700 macro_replay,
702 [VIS_ACTION_MARK_SET] = {
703 "mark-set",
704 "Set given mark at current cursor position",
705 mark_set,
707 [VIS_ACTION_MARK_GOTO] = {
708 "mark-goto",
709 "Goto the position of the given mark",
710 mark_motion, { .i = VIS_MOVE_MARK }
712 [VIS_ACTION_MARK_GOTO_LINE] = {
713 "mark-goto-line",
714 "Goto first non-blank character of the line containing the given mark",
715 mark_motion, { .i = VIS_MOVE_MARK_LINE }
717 [VIS_ACTION_REDRAW] = {
718 "editor-redraw",
719 "Redraw current editor content",
720 call, { .f = vis_redraw }
722 [VIS_ACTION_REPLACE_CHAR] = {
723 "replace-char",
724 "Replace the character under the cursor",
725 replace,
727 [VIS_ACTION_TOTILL_REPEAT] = {
728 "totill-repeat",
729 "Repeat latest to/till motion",
730 movement, { .i = VIS_MOVE_TOTILL_REPEAT }
732 [VIS_ACTION_TOTILL_REVERSE] = {
733 "totill-reverse",
734 "Repeat latest to/till motion but in opposite direction",
735 movement, { .i = VIS_MOVE_TOTILL_REVERSE }
737 [VIS_ACTION_PROMPT_SEARCH_FORWARD] = {
738 "search-forward",
739 "Search forward",
740 prompt_show, { .s = "/" }
742 [VIS_ACTION_PROMPT_SEARCH_BACKWARD] = {
743 "search-backward",
744 "Search backward",
745 prompt_show, { .s = "?" }
747 [VIS_ACTION_TILL_LEFT] = {
748 "till-left",
749 "Till after the occurrence of character to the left",
750 movement_key, { .i = VIS_MOVE_LEFT_TILL }
752 [VIS_ACTION_TILL_RIGHT] = {
753 "till-right",
754 "Till before the occurrence of character to the right",
755 movement_key, { .i = VIS_MOVE_RIGHT_TILL }
757 [VIS_ACTION_TO_LEFT] = {
758 "to-left",
759 "To the first occurrence of character to the left",
760 movement_key, { .i = VIS_MOVE_LEFT_TO }
762 [VIS_ACTION_TO_RIGHT] = {
763 "to-right",
764 "To the first occurrence of character to the right",
765 movement_key, { .i = VIS_MOVE_RIGHT_TO }
767 [VIS_ACTION_REGISTER] = {
768 "register",
769 "Use given register for next operator",
770 reg,
772 [VIS_ACTION_OPERATOR_CHANGE] = {
773 "vis-operator-change",
774 "Change operator",
775 operator, { .i = VIS_OP_CHANGE }
777 [VIS_ACTION_OPERATOR_DELETE] = {
778 "vis-operator-delete",
779 "Delete operator",
780 operator, { .i = VIS_OP_DELETE }
782 [VIS_ACTION_OPERATOR_YANK] = {
783 "vis-operator-yank",
784 "Yank operator",
785 operator, { .i = VIS_OP_YANK }
787 [VIS_ACTION_OPERATOR_SHIFT_LEFT] = {
788 "vis-operator-shift-left",
789 "Shift left operator",
790 operator, { .i = VIS_OP_SHIFT_LEFT }
792 [VIS_ACTION_OPERATOR_SHIFT_RIGHT] = {
793 "vis-operator-shift-right",
794 "Shift right operator",
795 operator, { .i = VIS_OP_SHIFT_RIGHT }
797 [VIS_ACTION_OPERATOR_CASE_LOWER] = {
798 "vis-operator-case-lower",
799 "Lowercase operator",
800 operator, { .i = VIS_OP_CASE_LOWER }
802 [VIS_ACTION_OPERATOR_CASE_UPPER] = {
803 "vis-operator-case-upper",
804 "Uppercase operator",
805 operator, { .i = VIS_OP_CASE_UPPER }
807 [VIS_ACTION_OPERATOR_CASE_SWAP] = {
808 "vis-operator-case-swap",
809 "Swap case operator",
810 operator, { .i = VIS_OP_CASE_SWAP }
812 [VIS_ACTION_OPERATOR_FILTER] = {
813 "vis-operator-filter",
814 "Filter operator",
815 operator_filter,
817 [VIS_ACTION_OPERATOR_FILTER_FMT] = {
818 "vis-operator-filter-format",
819 "Formating operator, filter range through fmt(1)",
820 operator_filter, { .s = "|fmt" }
822 [VIS_ACTION_COUNT] = {
823 "vis-count",
824 "Count specifier",
825 count,
827 [VIS_ACTION_INSERT_NEWLINE] = {
828 "insert-newline",
829 "Insert a line break (depending on file type)",
830 call, { .f = vis_insert_nl }
832 [VIS_ACTION_INSERT_TAB] = {
833 "insert-tab",
834 "Insert a tab (might be converted to spaces)",
835 call, { .f = vis_insert_tab }
837 [VIS_ACTION_INSERT_VERBATIM] = {
838 "insert-verbatim",
839 "Insert Unicode character based on code point",
840 insert_verbatim,
842 [VIS_ACTION_INSERT_REGISTER] = {
843 "insert-register",
844 "Insert specified register content",
845 insert_register,
847 [VIS_ACTION_WINDOW_NEXT] = {
848 "window-next",
849 "Focus next window",
850 call, { .f = vis_window_next }
852 [VIS_ACTION_WINDOW_PREV] = {
853 "window-prev",
854 "Focus previous window",
855 call, { .f = vis_window_prev }
857 [VIS_ACTION_APPEND_CHAR_NEXT] = {
858 "append-char-next",
859 "Append text after the cursor",
860 insertmode, { .i = VIS_MOVE_LINE_CHAR_NEXT }
862 [VIS_ACTION_APPEND_LINE_END] = {
863 "append-line-end",
864 "Append text after the end of the line",
865 insertmode, { .i = VIS_MOVE_LINE_END },
867 [VIS_ACTION_INSERT_LINE_START] = {
868 "insert-line-start",
869 "Insert text before the first non-blank in the line",
870 insertmode, { .i = VIS_MOVE_LINE_START },
872 [VIS_ACTION_OPEN_LINE_ABOVE] = {
873 "open-line-above",
874 "Begin a new line above the cursor",
875 openline, { .i = -1 }
877 [VIS_ACTION_OPEN_LINE_BELOW] = {
878 "open-line-below",
879 "Begin a new line below the cursor",
880 openline, { .i = +1 }
882 [VIS_ACTION_JOIN_LINES] = {
883 "join-lines",
884 "Join selected lines",
885 join, { .s = " " }
887 [VIS_ACTION_JOIN_LINES_TRIM] = {
888 "join-lines-trim",
889 "Join selected lines, remove white space",
890 join, { .s = "" }
892 [VIS_ACTION_PROMPT_SHOW] = {
893 "prompt-show",
894 "Show editor command line prompt",
895 prompt_show, { .s = ":" }
897 [VIS_ACTION_REPEAT] = {
898 "editor-repeat",
899 "Repeat latest editor command",
900 repeat
902 [VIS_ACTION_SELECTION_FLIP] = {
903 "selection-flip",
904 "Flip selection, move cursor to other end",
905 selection_end,
907 [VIS_ACTION_SELECTION_RESTORE] = {
908 "selection-restore",
909 "Restore last selection",
910 selection_restore,
912 [VIS_ACTION_WINDOW_REDRAW_TOP] = {
913 "window-redraw-top",
914 "Redraw cursor line at the top of the window",
915 window, { .w = view_redraw_top }
917 [VIS_ACTION_WINDOW_REDRAW_CENTER] = {
918 "window-redraw-center",
919 "Redraw cursor line at the center of the window",
920 window, { .w = view_redraw_center }
922 [VIS_ACTION_WINDOW_REDRAW_BOTTOM] = {
923 "window-redraw-bottom",
924 "Redraw cursor line at the bottom of the window",
925 window, { .w = view_redraw_bottom }
927 [VIS_ACTION_WINDOW_SLIDE_UP] = {
928 "window-slide-up",
929 "Slide window content upwards",
930 wslide, { .i = -1 }
932 [VIS_ACTION_WINDOW_SLIDE_DOWN] = {
933 "window-slide-down",
934 "Slide window content downwards",
935 wslide, { .i = +1 }
937 [VIS_ACTION_PUT_AFTER] = {
938 "put-after",
939 "Put text after the cursor",
940 operator, { .i = VIS_OP_PUT_AFTER }
942 [VIS_ACTION_PUT_BEFORE] = {
943 "put-before",
944 "Put text before the cursor",
945 operator, { .i = VIS_OP_PUT_BEFORE }
947 [VIS_ACTION_PUT_AFTER_END] = {
948 "put-after-end",
949 "Put text after the cursor, place cursor after new text",
950 operator, { .i = VIS_OP_PUT_AFTER_END }
952 [VIS_ACTION_PUT_BEFORE_END] = {
953 "put-before-end",
954 "Put text before the cursor, place cursor after new text",
955 operator, { .i = VIS_OP_PUT_BEFORE_END }
957 [VIS_ACTION_CURSOR_SELECT_WORD] = {
958 "cursors-select-word",
959 "Select word under cursor",
960 cursors_select,
962 [VIS_ACTION_CURSORS_NEW_LINE_ABOVE] = {
963 "cursors-new-lines-above",
964 "Create a new cursor on the line above",
965 cursors_new, { .i = -1 }
967 [VIS_ACTION_CURSORS_NEW_LINE_ABOVE_FIRST] = {
968 "cursors-new-lines-above-first",
969 "Create a new cursor on the line above the first cursor",
970 cursors_new, { .i = INT_MIN }
972 [VIS_ACTION_CURSORS_NEW_LINE_BELOW] = {
973 "cursor-new-lines-below",
974 "Create a new cursor on the line below",
975 cursors_new, { .i = +1 }
977 [VIS_ACTION_CURSORS_NEW_LINE_BELOW_LAST] = {
978 "cursor-new-lines-below-last",
979 "Create a new cursor on the line below the last cursor",
980 cursors_new, { .i = INT_MAX }
982 [VIS_ACTION_CURSORS_NEW_LINES_BEGIN] = {
983 "cursors-new-lines-begin",
984 "Create a new cursor at the start of every line covered by selection",
985 operator, { .i = VIS_OP_CURSOR_SOL }
987 [VIS_ACTION_CURSORS_NEW_LINES_END] = {
988 "cursors-new-lines-end",
989 "Create a new cursor at the end of every line covered by selection",
990 operator, { .i = VIS_OP_CURSOR_EOL }
992 [VIS_ACTION_CURSORS_NEW_MATCH_NEXT] = {
993 "cursors-new-match-next",
994 "Select the next region matching the current selection",
995 cursors_select_next
997 [VIS_ACTION_CURSORS_NEW_MATCH_SKIP] = {
998 "cursors-new-match-skip",
999 "Clear current selection, but select next match",
1000 cursors_select_skip,
1002 [VIS_ACTION_CURSORS_ALIGN] = {
1003 "cursors-align",
1004 "Try to align all cursors on the same column",
1005 cursors_align,
1007 [VIS_ACTION_CURSORS_ALIGN_INDENT_LEFT] = {
1008 "cursors-align-indent-left",
1009 "Left align all cursors/selections by inserting spaces",
1010 cursors_align_indent, { .i = -1 }
1012 [VIS_ACTION_CURSORS_ALIGN_INDENT_RIGHT] = {
1013 "cursors-align-indent-right",
1014 "Right align all cursors/selections by inserting spaces",
1015 cursors_align_indent, { .i = +1 }
1017 [VIS_ACTION_CURSORS_REMOVE_ALL] = {
1018 "cursors-remove-all",
1019 "Remove all but the primary cursor",
1020 cursors_clear,
1022 [VIS_ACTION_CURSORS_REMOVE_LAST] = {
1023 "cursors-remove-last",
1024 "Remove least recently created cursor",
1025 cursors_remove,
1027 [VIS_ACTION_CURSORS_REMOVE_COLUMN] = {
1028 "cursors-remove-column",
1029 "Remove count cursor column",
1030 cursors_remove_column, { .i = 1 }
1032 [VIS_ACTION_CURSORS_REMOVE_COLUMN_EXCEPT] = {
1033 "cursors-remove-column-except",
1034 "Remove all but the count cursor column",
1035 cursors_remove_column_except, { .i = 1 }
1037 [VIS_ACTION_CURSORS_PREV] = {
1038 "cursors-prev",
1039 "Move to the previous cursor",
1040 cursors_navigate, { .i = -PAGE_HALF }
1042 [VIS_ACTION_CURSORS_NEXT] = {
1043 "cursors-next",
1044 "Move to the next cursor",
1045 cursors_navigate, { .i = +PAGE_HALF }
1047 [VIS_ACTION_SELECTIONS_ROTATE_LEFT] = {
1048 "selections-rotate-left",
1049 "Rotate selections left",
1050 selections_rotate, { .i = -1 }
1052 [VIS_ACTION_SELECTIONS_ROTATE_RIGHT] = {
1053 "selections-rotate-right",
1054 "Rotate selections right",
1055 selections_rotate, { .i = +1 }
1057 [VIS_ACTION_SELECTIONS_TRIM] = {
1058 "selections-trim",
1059 "Remove leading and trailing white space from selections",
1060 selections_trim
1062 [VIS_ACTION_TEXT_OBJECT_WORD_OUTER] = {
1063 "text-object-word-outer",
1064 "A word leading and trailing whitespace included",
1065 textobj, { .i = VIS_TEXTOBJECT_OUTER_WORD }
1067 [VIS_ACTION_TEXT_OBJECT_WORD_INNER] = {
1068 "text-object-word-inner",
1069 "A word leading and trailing whitespace excluded",
1070 textobj, { .i = VIS_TEXTOBJECT_INNER_WORD }
1072 [VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER] = {
1073 "text-object-longword-outer",
1074 "A WORD leading and trailing whitespace included",
1075 textobj, { .i = VIS_TEXTOBJECT_OUTER_LONGWORD }
1077 [VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER] = {
1078 "text-object-longword-inner",
1079 "A WORD leading and trailing whitespace excluded",
1080 textobj, { .i = VIS_TEXTOBJECT_INNER_LONGWORD }
1082 [VIS_ACTION_TEXT_OBJECT_SENTENCE] = {
1083 "text-object-sentence",
1084 "A sentence",
1085 textobj, { .i = VIS_TEXTOBJECT_SENTENCE }
1087 [VIS_ACTION_TEXT_OBJECT_PARAGRAPH] = {
1088 "text-object-paragraph",
1089 "A paragraph",
1090 textobj, { .i = VIS_TEXTOBJECT_PARAGRAPH }
1092 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER] = {
1093 "text-object-square-bracket-outer",
1094 "[] block (outer variant)",
1095 textobj, { .i = VIS_TEXTOBJECT_OUTER_SQUARE_BRACKET }
1097 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER] = {
1098 "text-object-square-bracket-inner",
1099 "[] block (inner variant)",
1100 textobj, { .i = VIS_TEXTOBJECT_INNER_SQUARE_BRACKET }
1102 [VIS_ACTION_TEXT_OBJECT_PARANTHESE_OUTER] = {
1103 "text-object-parentheses-outer",
1104 "() block (outer variant)",
1105 textobj, { .i = VIS_TEXTOBJECT_OUTER_PARANTHESE }
1107 [VIS_ACTION_TEXT_OBJECT_PARANTHESE_INNER] = {
1108 "text-object-parentheses-inner",
1109 "() block (inner variant)",
1110 textobj, { .i = VIS_TEXTOBJECT_INNER_PARANTHESE }
1112 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER] = {
1113 "text-object-angle-bracket-outer",
1114 "<> block (outer variant)",
1115 textobj, { .i = VIS_TEXTOBJECT_OUTER_ANGLE_BRACKET }
1117 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER] = {
1118 "text-object-angle-bracket-inner",
1119 "<> block (inner variant)",
1120 textobj, { .i = VIS_TEXTOBJECT_INNER_ANGLE_BRACKET }
1122 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER] = {
1123 "text-object-curly-bracket-outer",
1124 "{} block (outer variant)",
1125 textobj, { .i = VIS_TEXTOBJECT_OUTER_CURLY_BRACKET }
1127 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER] = {
1128 "text-object-curly-bracket-inner",
1129 "{} block (inner variant)",
1130 textobj, { .i = VIS_TEXTOBJECT_INNER_CURLY_BRACKET }
1132 [VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER] = {
1133 "text-object-quote-outer",
1134 "A quoted string, including the quotation marks",
1135 textobj, { .i = VIS_TEXTOBJECT_OUTER_QUOTE }
1137 [VIS_ACTION_TEXT_OBJECT_QUOTE_INNER] = {
1138 "text-object-quote-inner",
1139 "A quoted string, excluding the quotation marks",
1140 textobj, { .i = VIS_TEXTOBJECT_INNER_QUOTE }
1142 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER] = {
1143 "text-object-single-quote-outer",
1144 "A single quoted string, including the quotation marks",
1145 textobj, { .i = VIS_TEXTOBJECT_OUTER_SINGLE_QUOTE }
1147 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER] = {
1148 "text-object-single-quote-inner",
1149 "A single quoted string, excluding the quotation marks",
1150 textobj, { .i = VIS_TEXTOBJECT_INNER_SINGLE_QUOTE }
1152 [VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER] = {
1153 "text-object-backtick-outer",
1154 "A backtick delimited string (outer variant)",
1155 textobj, { .i = VIS_TEXTOBJECT_OUTER_BACKTICK }
1157 [VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER] = {
1158 "text-object-backtick-inner",
1159 "A backtick delimited string (inner variant)",
1160 textobj, { .i = VIS_TEXTOBJECT_INNER_BACKTICK }
1162 [VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER] = {
1163 "text-object-entire-outer",
1164 "The whole text content",
1165 textobj, { .i = VIS_TEXTOBJECT_OUTER_ENTIRE }
1167 [VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER] = {
1168 "text-object-entire-inner",
1169 "The whole text content, except for leading and trailing empty lines",
1170 textobj, { .i = VIS_TEXTOBJECT_INNER_ENTIRE }
1172 [VIS_ACTION_TEXT_OBJECT_FUNCTION_OUTER] = {
1173 "text-object-function-outer",
1174 "A whole C-like function",
1175 textobj, { .i = VIS_TEXTOBJECT_OUTER_FUNCTION }
1177 [VIS_ACTION_TEXT_OBJECT_FUNCTION_INNER] = {
1178 "text-object-function-inner",
1179 "A whole C-like function body",
1180 textobj, { .i = VIS_TEXTOBJECT_INNER_FUNCTION }
1182 [VIS_ACTION_TEXT_OBJECT_LINE_OUTER] = {
1183 "text-object-line-outer",
1184 "The whole line",
1185 textobj, { .i = VIS_TEXTOBJECT_OUTER_LINE }
1187 [VIS_ACTION_TEXT_OBJECT_LINE_INNER] = {
1188 "text-object-line-inner",
1189 "The whole line, excluding leading and trailing whitespace",
1190 textobj, { .i = VIS_TEXTOBJECT_INNER_LINE }
1192 [VIS_ACTION_TEXT_OBJECT_INDENTATION] = {
1193 "text-object-indentation",
1194 "All adjacent lines with the same indentation level as the current one",
1195 textobj, { .i = VIS_TEXTOBJECT_INDENTATION }
1197 [VIS_ACTION_TEXT_OBJECT_SEARCH_FORWARD] = {
1198 "text-object-search-forward",
1199 "The next search match in forward direction",
1200 textobj, { .i = VIS_TEXTOBJECT_SEARCH_FORWARD }
1202 [VIS_ACTION_TEXT_OBJECT_SEARCH_BACKWARD] = {
1203 "text-object-search-backward",
1204 "The next search match in backward direction",
1205 textobj, { .i = VIS_TEXTOBJECT_SEARCH_BACKWARD }
1207 [VIS_ACTION_MOTION_CHARWISE] = {
1208 "motion-charwise",
1209 "Force motion to be charwise",
1210 motiontype, { .i = VIS_MOTIONTYPE_CHARWISE }
1212 [VIS_ACTION_MOTION_LINEWISE] = {
1213 "motion-linewise",
1214 "Force motion to be linewise",
1215 motiontype, { .i = VIS_MOTIONTYPE_LINEWISE }
1217 [VIS_ACTION_UNICODE_INFO] = {
1218 "unicode-info",
1219 "Show Unicode codepoint(s) of character under cursor",
1220 unicode_info, { .i = VIS_ACTION_UNICODE_INFO }
1222 [VIS_ACTION_UTF8_INFO] = {
1223 "utf8-info",
1224 "Show UTF-8 encoded codepoint(s) of character under cursor",
1225 unicode_info, { .i = VIS_ACTION_UTF8_INFO }
1227 [VIS_ACTION_NUMBER_INCREMENT] = {
1228 "number-increment",
1229 "Increment number under cursor",
1230 number_increment_decrement, { .i = +1 }
1232 [VIS_ACTION_NUMBER_DECREMENT] = {
1233 "number-decrement",
1234 "Decrement number under cursor",
1235 number_increment_decrement, { .i = -1 }
1237 [VIS_ACTION_OPEN_FILE_UNDER_CURSOR] = {
1238 "open-file-under-cursor",
1239 "Open file under the cursor",
1240 open_file_under_cursor, { .b = false }
1242 [VIS_ACTION_OPEN_FILE_UNDER_CURSOR_NEW_WINDOW] = {
1243 "open-file-under-cursor-new-window",
1244 "Open file under the cursor in a new window",
1245 open_file_under_cursor, { .b = true }
1247 [VIS_ACTION_COMPLETE_WORD] = {
1248 "complete-word",
1249 "Complete word in file",
1250 complete_word,
1252 [VIS_ACTION_COMPLETE_FILENAME] = {
1253 "complete-filename",
1254 "Complete file name",
1255 complete_filename,
1257 [VIS_ACTION_NOP] = {
1258 "nop",
1259 "Ignore key, do nothing",
1260 nop,
1264 #include "config.h"
1266 /** key bindings functions */
1268 static const char *nop(Vis *vis, const char *keys, const Arg *arg) {
1269 return keys;
1272 static const char *macro_record(Vis *vis, const char *keys, const Arg *arg) {
1273 if (!vis_macro_record_stop(vis)) {
1274 if (!keys[0])
1275 return NULL;
1276 enum VisRegister reg = vis_register_from(vis, keys[0]);
1277 vis_macro_record(vis, reg);
1278 keys++;
1280 vis_draw(vis);
1281 return keys;
1284 static const char *macro_replay(Vis *vis, const char *keys, const Arg *arg) {
1285 if (!keys[0])
1286 return NULL;
1287 enum VisRegister reg = vis_register_from(vis, keys[0]);
1288 vis_macro_replay(vis, reg);
1289 return keys+1;
1292 static const char *suspend(Vis *vis, const char *keys, const Arg *arg) {
1293 vis_suspend(vis);
1294 return keys;
1297 static const char *repeat(Vis *vis, const char *keys, const Arg *arg) {
1298 vis_repeat(vis);
1299 return keys;
1302 static const char *cursors_new(Vis *vis, const char *keys, const Arg *arg) {
1303 View *view = vis_view(vis);
1304 for (int count = vis_count_get_default(vis, 1); count > 0; count--) {
1305 Cursor *cursor = NULL;
1306 switch (arg->i) {
1307 case -1:
1308 case +1:
1309 cursor = view_cursors_primary_get(view);
1310 break;
1311 case INT_MIN:
1312 cursor = view_cursors(view);
1313 break;
1314 case INT_MAX:
1315 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c))
1316 cursor = c;
1317 break;
1318 default:
1319 return keys;
1321 size_t oldpos = view_cursors_pos(cursor);
1322 if (arg->i > 0)
1323 view_line_down(cursor);
1324 else if (arg->i < 0)
1325 view_line_up(cursor);
1326 size_t newpos = view_cursors_pos(cursor);
1327 view_cursors_to(cursor, oldpos);
1328 if (!view_cursors_new(view, newpos)) {
1329 if (arg->i == -1) {
1330 cursor = view_cursors_prev(cursor);
1331 } else if (arg->i == +1) {
1332 cursor = view_cursors_next(cursor);
1334 view_cursors_primary_set(cursor);
1337 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1338 return keys;
1341 static const char *cursors_align(Vis *vis, const char *keys, const Arg *arg) {
1342 View *view = vis_view(vis);
1343 Text *txt = vis_text(vis);
1344 int mincol = INT_MAX;
1345 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
1346 int col = view_cursors_cell_get(c);
1347 if (col >= 0 && col < mincol)
1348 mincol = col;
1350 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
1351 if (view_cursors_cell_set(c, mincol) == -1) {
1352 size_t pos = view_cursors_pos(c);
1353 size_t col = text_line_width_set(txt, pos, mincol);
1354 view_cursors_to(c, col);
1357 return keys;
1360 static const char *cursors_align_indent(Vis *vis, const char *keys, const Arg *arg) {
1361 View *view = vis_view(vis);
1362 Text *txt = vis_text(vis);
1363 bool left_align = arg->i < 0;
1364 int columns = view_cursors_column_count(view);
1366 for (int i = 0; i < columns; i++) {
1367 int mincol = INT_MAX, maxcol = 0;
1368 for (Cursor *c = view_cursors_column(view, i); c; c = view_cursors_column_next(c, i)) {
1369 size_t pos;
1370 Filerange sel = view_cursors_selection_get(c);
1371 if (text_range_valid(&sel))
1372 pos = left_align ? sel.start : sel.end;
1373 else
1374 pos = view_cursors_pos(c);
1375 int col = text_line_width_get(txt, pos);
1376 if (col < mincol)
1377 mincol = col;
1378 if (col > maxcol)
1379 maxcol = col;
1382 size_t len = maxcol - mincol;
1383 char *buf = malloc(len+1);
1384 if (!buf)
1385 return keys;
1386 memset(buf, ' ', len);
1388 for (Cursor *c = view_cursors_column(view, i); c; c = view_cursors_column_next(c, i)) {
1389 size_t pos, ipos;
1390 Filerange sel = view_cursors_selection_get(c);
1391 if (text_range_valid(&sel)) {
1392 pos = left_align ? sel.start : sel.end;
1393 ipos = sel.start;
1394 } else {
1395 pos = view_cursors_pos(c);
1396 ipos = pos;
1398 int col = text_line_width_get(txt, pos);
1399 if (col < maxcol) {
1400 size_t off = maxcol - col;
1401 if (off <= len)
1402 text_insert(txt, ipos, buf, off);
1406 free(buf);
1409 view_draw(view);
1410 return keys;
1413 static const char *cursors_clear(Vis *vis, const char *keys, const Arg *arg) {
1414 View *view = vis_view(vis);
1415 if (view_cursors_multiple(view))
1416 view_cursors_clear(view);
1417 else
1418 view_cursors_selection_clear(view_cursors_primary_get(view));
1419 return keys;
1422 static const char *cursors_select(Vis *vis, const char *keys, const Arg *arg) {
1423 Text *txt = vis_text(vis);
1424 View *view = vis_view(vis);
1425 for (Cursor *cursor = view_cursors(view); cursor; cursor = view_cursors_next(cursor)) {
1426 Filerange sel = view_cursors_selection_get(cursor);
1427 Filerange word = text_object_word(txt, view_cursors_pos(cursor));
1428 if (!text_range_valid(&sel) && text_range_valid(&word)) {
1429 view_cursors_selection_set(cursor, &word);
1430 view_cursors_to(cursor, text_char_prev(txt, word.end));
1433 vis_mode_switch(vis, VIS_MODE_VISUAL);
1434 return keys;
1437 static const char *cursors_select_next(Vis *vis, const char *keys, const Arg *arg) {
1438 Text *txt = vis_text(vis);
1439 View *view = vis_view(vis);
1440 Cursor *cursor = view_cursors_primary_get(view);
1441 Filerange sel = view_cursors_selection_get(cursor);
1442 if (!text_range_valid(&sel))
1443 return keys;
1445 char *buf = text_bytes_alloc0(txt, sel.start, text_range_size(&sel));
1446 if (!buf)
1447 return keys;
1448 Filerange word = text_object_word_find_next(txt, sel.end, buf);
1449 free(buf);
1451 if (text_range_valid(&word)) {
1452 size_t pos = text_char_prev(txt, word.end);
1453 cursor = view_cursors_new(view, pos);
1454 if (!cursor)
1455 return keys;
1456 view_cursors_selection_set(cursor, &word);
1458 return keys;
1461 static const char *cursors_select_skip(Vis *vis, const char *keys, const Arg *arg) {
1462 View *view = vis_view(vis);
1463 Cursor *cursor = view_cursors_primary_get(view);
1464 keys = cursors_select_next(vis, keys, arg);
1465 if (cursor != view_cursors_primary_get(view))
1466 view_cursors_dispose(cursor);
1467 return keys;
1470 static const char *cursors_remove(Vis *vis, const char *keys, const Arg *arg) {
1471 View *view = vis_view(vis);
1472 view_cursors_dispose(view_cursors_primary_get(view));
1473 view_cursor_to(view, view_cursor_get(view));
1474 return keys;
1477 static const char *cursors_remove_column(Vis *vis, const char *keys, const Arg *arg) {
1478 View *view = vis_view(vis);
1479 int max = view_cursors_column_count(view);
1480 int column = vis_count_get_default(vis, arg->i) - 1;
1481 if (column >= max)
1482 column = max - 1;
1483 if (!view_cursors_multiple(view)) {
1484 vis_mode_switch(vis, VIS_MODE_NORMAL);
1485 return keys;
1488 for (Cursor *c = view_cursors_column(view, column), *next; c; c = next) {
1489 next = view_cursors_column_next(c, column);
1490 view_cursors_dispose(c);
1493 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1494 return keys;
1497 static const char *cursors_remove_column_except(Vis *vis, const char *keys, const Arg *arg) {
1498 View *view = vis_view(vis);
1499 int max = view_cursors_column_count(view);
1500 int column = vis_count_get_default(vis, arg->i) - 1;
1501 if (column >= max)
1502 column = max - 1;
1503 if (!view_cursors_multiple(view)) {
1504 vis_redraw(vis);
1505 return keys;
1508 Cursor *cur = view_cursors(view);
1509 Cursor *col = view_cursors_column(view, column);
1510 for (Cursor *next; cur; cur = next) {
1511 next = view_cursors_next(cur);
1512 if (cur == col)
1513 col = view_cursors_column_next(col, column);
1514 else
1515 view_cursors_dispose(cur);
1518 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1519 return keys;
1522 static const char *cursors_navigate(Vis *vis, const char *keys, const Arg *arg) {
1523 View *view = vis_view(vis);
1524 if (!view_cursors_multiple(view)) {
1525 Filerange sel = view_selection_get(view);
1526 if (!text_range_valid(&sel))
1527 return wscroll(vis, keys, arg);
1528 return keys;
1530 Cursor *c = view_cursors_primary_get(view);
1531 for (int count = vis_count_get_default(vis, 1); count > 0; count--) {
1532 if (arg->i > 0) {
1533 c = view_cursors_next(c);
1534 if (!c)
1535 c = view_cursors(view);
1536 } else {
1537 c = view_cursors_prev(c);
1538 if (!c) {
1539 c = view_cursors(view);
1540 for (Cursor *n = c; n; n = view_cursors_next(n))
1541 c = n;
1545 view_cursors_primary_set(c);
1546 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1547 return keys;
1550 static const char *selections_rotate(Vis *vis, const char *keys, const Arg *arg) {
1552 typedef struct {
1553 Cursor *cursor;
1554 char *data;
1555 size_t len;
1556 } Rotate;
1558 Array arr;
1559 Text *txt = vis_text(vis);
1560 View *view = vis_view(vis);
1561 int columns = view_cursors_column_count(view);
1562 int selections = columns == 1 ? view_cursors_count(view) : columns;
1563 int count = vis_count_get_default(vis, 1);
1564 array_init_sized(&arr, sizeof(Rotate));
1565 if (!array_reserve(&arr, selections))
1566 return keys;
1567 size_t line = 0;
1569 for (Cursor *c = view_cursors(view), *next; c; c = next) {
1570 next = view_cursors_next(c);
1571 size_t line_next = 0;
1573 Filerange sel = view_cursors_selection_get(c);
1574 Rotate rot;
1575 rot.cursor = c;
1576 rot.len = text_range_size(&sel);
1577 if ((rot.data = malloc(rot.len)))
1578 rot.len = text_bytes_get(txt, sel.start, rot.len, rot.data);
1579 else
1580 rot.len = 0;
1581 array_add(&arr, &rot);
1583 if (!line)
1584 line = text_lineno_by_pos(txt, view_cursors_pos(c));
1585 if (next)
1586 line_next = text_lineno_by_pos(txt, view_cursors_pos(next));
1587 if (!next || (columns > 1 && line != line_next)) {
1588 size_t len = array_length(&arr);
1589 size_t off = arg->i > 0 ? count % len : len - (count % len);
1590 for (size_t i = 0; i < len; i++) {
1591 size_t j = (i + off) % len;
1592 Rotate *oldrot = array_get(&arr, i);
1593 Rotate *newrot = array_get(&arr, j);
1594 if (!oldrot || !newrot || oldrot == newrot)
1595 continue;
1596 Filerange newsel = view_cursors_selection_get(newrot->cursor);
1597 if (!text_range_valid(&newsel))
1598 continue;
1599 if (!text_delete_range(txt, &newsel))
1600 continue;
1601 if (!text_insert(txt, newsel.start, oldrot->data, oldrot->len))
1602 continue;
1603 newsel.end = newsel.start + oldrot->len;
1604 view_cursors_selection_set(newrot->cursor, &newsel);
1605 view_cursors_selection_sync(newrot->cursor);
1606 free(oldrot->data);
1608 array_clear(&arr);
1610 line = line_next;
1613 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1614 return keys;
1617 static const char *selections_trim(Vis *vis, const char *keys, const Arg *arg) {
1618 Text *txt = vis_text(vis);
1619 View *view = vis_view(vis);
1620 for (Cursor *c = view_cursors(view), *next; c; c = next) {
1621 next = view_cursors_next(c);
1622 Filerange sel = view_cursors_selection_get(c);
1623 if (!text_range_valid(&sel))
1624 continue;
1625 for (char b; sel.start < sel.end && text_byte_get(txt, sel.end-1, &b)
1626 && isspace((unsigned char)b); sel.end--);
1627 for (char b; sel.start <= sel.end && text_byte_get(txt, sel.start, &b)
1628 && isspace((unsigned char)b); sel.start++);
1629 if (sel.start < sel.end) {
1630 view_cursors_selection_set(c, &sel);
1631 view_cursors_selection_sync(c);
1632 } else if (!view_cursors_dispose(c)) {
1633 vis_mode_switch(vis, VIS_MODE_NORMAL);
1636 return keys;
1639 static const char *replace(Vis *vis, const char *keys, const Arg *arg) {
1640 if (!keys[0]) {
1641 vis_keymap_disable(vis);
1642 return NULL;
1645 const char *next = vis_keys_next(vis, keys);
1646 if (!next)
1647 return NULL;
1649 char replacement[64];
1650 size_t len = next - keys;
1651 if (len >= sizeof(replacement))
1652 return next;
1654 memcpy(replacement, keys, len);
1655 replacement[len] = '\0';
1657 if (strcmp("<Escape>", replacement) == 0)
1658 return next;
1660 if (vis_mode_get(vis) == VIS_MODE_NORMAL) {
1661 int count = vis_count_get_default(vis, 1);
1662 vis_operator(vis, VIS_OP_CHANGE);
1663 vis_motion(vis, VIS_MOVE_CHAR_NEXT);
1664 for (; count > 0; count--)
1665 vis_keys_feed(vis, replacement);
1666 } else {
1667 vis_operator(vis, VIS_OP_REPLACE, replacement);
1670 vis_keys_feed(vis, "<Escape>");
1671 return next;
1674 static const char *count(Vis *vis, const char *keys, const Arg *arg) {
1675 int digit = keys[-1] - '0';
1676 int count = vis_count_get_default(vis, 0);
1677 if (0 <= digit && digit <= 9) {
1678 if (digit == 0 && count == 0)
1679 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
1680 vis_count_set(vis, count * 10 + digit);
1682 return keys;
1685 static const char *gotoline(Vis *vis, const char *keys, const Arg *arg) {
1686 if (vis_count_get(vis) != VIS_COUNT_UNKNOWN)
1687 vis_motion(vis, VIS_MOVE_LINE);
1688 else if (arg->i < 0)
1689 vis_motion(vis, VIS_MOVE_FILE_BEGIN);
1690 else
1691 vis_motion(vis, VIS_MOVE_FILE_END);
1692 return keys;
1695 static const char *motiontype(Vis *vis, const char *keys, const Arg *arg) {
1696 vis_motion_type(vis, arg->i);
1697 return keys;
1700 static const char *operator(Vis *vis, const char *keys, const Arg *arg) {
1701 vis_operator(vis, arg->i);
1702 return keys;
1705 static const char *operator_filter(Vis *vis, const char *keys, const Arg *arg) {
1706 vis_operator(vis, VIS_OP_FILTER, arg->s);
1707 return keys;
1710 static const char *movement_key(Vis *vis, const char *keys, const Arg *arg) {
1711 char key[32];
1712 const char *next;
1713 if (!keys[0] || !(next = vis_keys_next(vis, keys)))
1714 return NULL;
1715 size_t len = next - keys;
1716 if (len < sizeof key) {
1717 strncpy(key, keys, len);
1718 key[len] = '\0';
1719 vis_motion(vis, arg->i, key);
1721 return next;
1724 static const char *movement(Vis *vis, const char *keys, const Arg *arg) {
1725 vis_motion(vis, arg->i);
1726 return keys;
1729 static const char *textobj(Vis *vis, const char *keys, const Arg *arg) {
1730 vis_textobject(vis, arg->i);
1731 return keys;
1734 static const char *selection_end(Vis *vis, const char *keys, const Arg *arg) {
1735 for (Cursor *c = view_cursors(vis_view(vis)); c; c = view_cursors_next(c))
1736 view_cursors_selection_swap(c);
1737 return keys;
1740 static const char *selection_restore(Vis *vis, const char *keys, const Arg *arg) {
1741 Text *txt = vis_text(vis);
1742 View *view = vis_view(vis);
1743 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c))
1744 view_cursors_selection_restore(c);
1745 Filerange sel = view_selection_get(view);
1746 if (text_range_is_linewise(txt, &sel))
1747 vis_mode_switch(vis, VIS_MODE_VISUAL_LINE);
1748 else
1749 vis_mode_switch(vis, VIS_MODE_VISUAL);
1750 return keys;
1753 static const char *reg(Vis *vis, const char *keys, const Arg *arg) {
1754 if (!keys[0])
1755 return NULL;
1756 enum VisRegister reg = vis_register_from(vis, keys[0]);
1757 vis_register_set(vis, reg);
1758 return keys+1;
1761 static const char *mark_set(Vis *vis, const char *keys, const Arg *arg) {
1762 if (!keys[0])
1763 return NULL;
1764 vis_mark_set(vis, vis_mark_from(vis, keys[0]), view_cursor_get(vis_view(vis)));
1765 return keys+1;
1768 static const char *mark_motion(Vis *vis, const char *keys, const Arg *arg) {
1769 if (!keys[0])
1770 return NULL;
1771 vis_motion(vis, arg->i, vis_mark_from(vis, keys[0]));
1772 return keys+1;
1775 static const char *undo(Vis *vis, const char *keys, const Arg *arg) {
1776 size_t pos = text_undo(vis_text(vis));
1777 if (pos != EPOS) {
1778 View *view = vis_view(vis);
1779 if (!view_cursors_multiple(view))
1780 view_cursor_to(view, pos);
1781 /* redraw all windows in case some display the same file */
1782 vis_draw(vis);
1784 return keys;
1787 static const char *redo(Vis *vis, const char *keys, const Arg *arg) {
1788 size_t pos = text_redo(vis_text(vis));
1789 if (pos != EPOS) {
1790 View *view = vis_view(vis);
1791 if (!view_cursors_multiple(view))
1792 view_cursor_to(view, pos);
1793 /* redraw all windows in case some display the same file */
1794 vis_draw(vis);
1796 return keys;
1799 static const char *earlier(Vis *vis, const char *keys, const Arg *arg) {
1800 size_t pos = text_earlier(vis_text(vis), vis_count_get_default(vis, 1));
1801 if (pos != EPOS) {
1802 view_cursor_to(vis_view(vis), pos);
1803 /* redraw all windows in case some display the same file */
1804 vis_draw(vis);
1806 return keys;
1809 static const char *later(Vis *vis, const char *keys, const Arg *arg) {
1810 size_t pos = text_later(vis_text(vis), vis_count_get_default(vis, 1));
1811 if (pos != EPOS) {
1812 view_cursor_to(vis_view(vis), pos);
1813 /* redraw all windows in case some display the same file */
1814 vis_draw(vis);
1816 return keys;
1819 static const char *delete(Vis *vis, const char *keys, const Arg *arg) {
1820 vis_operator(vis, VIS_OP_DELETE);
1821 vis_motion(vis, arg->i);
1822 return keys;
1825 static const char *insert_register(Vis *vis, const char *keys, const Arg *arg) {
1826 if (!keys[0])
1827 return NULL;
1828 enum VisRegister reg = vis_register_from(vis, keys[0]);
1829 size_t len;
1830 const char *data = vis_register_get(vis, reg, &len);
1831 vis_insert_key(vis, data, len);
1832 return keys+1;
1835 static const char *prompt_show(Vis *vis, const char *keys, const Arg *arg) {
1836 vis_prompt_show(vis, arg->s);
1837 return keys;
1840 static const char *insert_verbatim(Vis *vis, const char *keys, const Arg *arg) {
1841 Rune rune = 0;
1842 char buf[4], type = keys[0];
1843 const char *data = NULL;
1844 int len = 0, count = 0, base = 0;
1845 switch (type) {
1846 case '\0':
1847 return NULL;
1848 case 'o':
1849 case 'O':
1850 count = 3;
1851 base = 8;
1852 break;
1853 case 'U':
1854 count = 4;
1855 /* fall through */
1856 case 'u':
1857 count += 4;
1858 base = 16;
1859 break;
1860 case 'x':
1861 case 'X':
1862 count = 2;
1863 base = 16;
1864 break;
1865 default:
1866 if ('0' <= type && type <= '9') {
1867 rune = type - '0';
1868 count = 2;
1869 base = 10;
1871 break;
1874 if (base) {
1875 for (keys++; keys[0] && count > 0; keys++, count--) {
1876 int v = 0;
1877 if (base == 8 && '0' <= keys[0] && keys[0] <= '7') {
1878 v = keys[0] - '0';
1879 } else if ((base == 10 || base == 16) && '0' <= keys[0] && keys[0] <= '9') {
1880 v = keys[0] - '0';
1881 } else if (base == 16 && 'a' <= keys[0] && keys[0] <= 'f') {
1882 v = 10 + keys[0] - 'a';
1883 } else if (base == 16 && 'A' <= keys[0] && keys[0] <= 'F') {
1884 v = 10 + keys[0] - 'A';
1885 } else {
1886 count = 0;
1887 break;
1889 rune = rune * base + v;
1892 if (count > 0)
1893 return NULL;
1894 if (type == 'u' || type == 'U') {
1895 len = runetochar(buf, &rune);
1896 } else {
1897 buf[0] = rune;
1898 len = 1;
1901 data = buf;
1902 } else {
1903 const char *next = vis_keys_next(vis, keys);
1904 if (!next)
1905 return NULL;
1906 if ((rune = vis_keys_codepoint(vis, keys)) != (Rune)-1) {
1907 len = runetochar(buf, &rune);
1908 data = buf;
1909 } else {
1910 vis_info_show(vis, "Unknown key");
1912 keys = next;
1915 if (len > 0)
1916 vis_insert_key(vis, data, len);
1917 return keys;
1920 static const char *wscroll(Vis *vis, const char *keys, const Arg *arg) {
1921 View *view = vis_view(vis);
1922 int count = vis_count_get(vis);
1923 switch (arg->i) {
1924 case -PAGE:
1925 view_scroll_page_up(view);
1926 break;
1927 case +PAGE:
1928 view_scroll_page_down(view);
1929 break;
1930 case -PAGE_HALF:
1931 view_scroll_halfpage_up(view);
1932 break;
1933 case +PAGE_HALF:
1934 view_scroll_halfpage_down(view);
1935 break;
1936 default:
1937 if (count == VIS_COUNT_UNKNOWN)
1938 count = arg->i < 0 ? -arg->i : arg->i;
1939 if (arg->i < 0)
1940 view_scroll_up(view, count);
1941 else
1942 view_scroll_down(view, count);
1943 break;
1945 return keys;
1948 static const char *wslide(Vis *vis, const char *keys, const Arg *arg) {
1949 View *view = vis_view(vis);
1950 int count = vis_count_get(vis);
1951 if (count == VIS_COUNT_UNKNOWN)
1952 count = arg->i < 0 ? -arg->i : arg->i;
1953 if (arg->i >= 0)
1954 view_slide_down(view, count);
1955 else
1956 view_slide_up(view, count);
1957 return keys;
1960 static const char *call(Vis *vis, const char *keys, const Arg *arg) {
1961 arg->f(vis);
1962 return keys;
1965 static const char *window(Vis *vis, const char *keys, const Arg *arg) {
1966 arg->w(vis_view(vis));
1967 return keys;
1970 static const char *openline(Vis *vis, const char *keys, const Arg *arg) {
1971 vis_operator(vis, VIS_OP_MODESWITCH, VIS_MODE_INSERT);
1972 if (arg->i > 0) {
1973 vis_motion(vis, VIS_MOVE_LINE_END);
1974 vis_keys_feed(vis, "<insert-newline>");
1975 } else {
1976 if (vis_get_autoindent(vis)) {
1977 vis_motion(vis, VIS_MOVE_LINE_START);
1978 } else {
1979 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
1981 vis_keys_feed(vis, "<insert-newline><Up>");
1983 return keys;
1986 static const char *join(Vis *vis, const char *keys, const Arg *arg) {
1987 bool normal = (vis_mode_get(vis) == VIS_MODE_NORMAL);
1988 vis_operator(vis, VIS_OP_JOIN, arg->s);
1989 if (normal) {
1990 int count = vis_count_get_default(vis, 0);
1991 if (count)
1992 vis_count_set(vis, count-1);
1993 vis_motion(vis, VIS_MOVE_LINE_NEXT);
1995 return keys;
1998 static const char *switchmode(Vis *vis, const char *keys, const Arg *arg) {
1999 vis_mode_switch(vis, arg->i);
2000 return keys;
2003 static const char *insertmode(Vis *vis, const char *keys, const Arg *arg) {
2004 vis_operator(vis, VIS_OP_MODESWITCH, VIS_MODE_INSERT);
2005 vis_motion(vis, arg->i);
2006 return keys;
2009 static const char *unicode_info(Vis *vis, const char *keys, const Arg *arg) {
2010 View *view = vis_view(vis);
2011 Text *txt = vis_text(vis);
2012 size_t start = view_cursor_get(view);
2013 size_t end = text_char_next(txt, start);
2014 char *grapheme = text_bytes_alloc0(txt, start, end-start), *codepoint = grapheme;
2015 if (!grapheme)
2016 return keys;
2017 Buffer info;
2018 buffer_init(&info);
2019 mbstate_t ps = { 0 };
2020 Iterator it = text_iterator_get(txt, start);
2021 for (size_t pos = start; it.pos < end; pos = it.pos) {
2022 text_iterator_codepoint_next(&it, NULL);
2023 size_t len = it.pos - pos;
2024 wchar_t wc = 0xFFFD;
2025 size_t res = mbrtowc(&wc, codepoint, len, &ps);
2026 bool combining = false;
2027 if (res != (size_t)-1 && res != (size_t)-2)
2028 combining = (wc != L'\0' && wcwidth(wc) == 0);
2029 unsigned char ch = *codepoint;
2030 if (ch < 128 && !isprint(ch))
2031 buffer_appendf(&info, "<^%c> ", ch == 127 ? '?' : ch + 64);
2032 else
2033 buffer_appendf(&info, "<%s%.*s> ", combining ? " " : "", (int)len, codepoint);
2034 if (arg->i == VIS_ACTION_UNICODE_INFO) {
2035 buffer_appendf(&info, "U+%04x ", wc);
2036 } else {
2037 for (size_t i = 0; i < len; i++)
2038 buffer_appendf(&info, "%02x ", (uint8_t)codepoint[i]);
2040 codepoint += len;
2042 vis_info_show(vis, "%s", buffer_content0(&info));
2043 free(grapheme);
2044 buffer_release(&info);
2045 return keys;
2048 static const char *percent(Vis *vis, const char *keys, const Arg *arg) {
2049 if (vis_count_get(vis) == VIS_COUNT_UNKNOWN)
2050 vis_motion(vis, VIS_MOVE_BRACKET_MATCH);
2051 else
2052 vis_motion(vis, VIS_MOVE_PERCENT);
2053 return keys;
2056 static const char *number_increment_decrement(Vis *vis, const char *keys, const Arg *arg) {
2057 View *view = vis_view(vis);
2058 Text *txt = vis_text(vis);
2060 int delta = arg->i;
2061 int count = vis_count_get(vis);
2062 if (count != 0 && count != VIS_COUNT_UNKNOWN)
2063 delta *= count;
2065 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
2066 Filerange r = text_object_number(txt, view_cursors_pos(c));
2067 if (!text_range_valid(&r))
2068 continue;
2069 char *buf = text_bytes_alloc0(txt, r.start, text_range_size(&r));
2070 if (buf) {
2071 char *number = buf, fmt[255];
2072 if (number[0] == '-')
2073 number++;
2074 bool octal = number[0] == '0' && ('0' <= number[1] && number[1] <= '7');
2075 bool hex = number[0] == '0' && (number[1] == 'x' || number[1] == 'X');
2076 bool dec = !hex && !octal;
2078 long long value = strtoll(buf, NULL, 0);
2079 value += delta;
2080 if (dec) {
2081 snprintf(fmt, sizeof fmt, "%lld", value);
2082 } else if (hex) {
2083 size_t len = strlen(number) - 2;
2084 snprintf(fmt, sizeof fmt, "0x%0*llx", (int)len, value);
2085 } else {
2086 size_t len = strlen(number) - 1;
2087 snprintf(fmt, sizeof fmt, "0%0*llo", (int)len, value);
2089 text_delete_range(txt, &r);
2090 text_insert(txt, r.start, fmt, strlen(fmt));
2091 view_cursors_to(c, r.start);
2093 free(buf);
2096 vis_cancel(vis);
2098 return keys;
2101 static const char *open_file_under_cursor(Vis *vis, const char *keys, const Arg *arg) {
2102 View *view = vis_view(vis);
2103 Text *txt = vis_text(vis);
2104 char cmd[PATH_MAX], name[PATH_MAX];
2106 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
2107 Filerange r = text_object_filename(txt, view_cursors_pos(c));
2108 if (!text_range_valid(&r))
2109 continue;
2110 size_t len = text_range_size(&r);
2111 if (len >= sizeof(cmd)-10)
2112 continue;
2113 len = text_bytes_get(txt, r.start, text_range_size(&r), name);
2114 name[len] = '\0';
2115 snprintf(cmd, sizeof cmd, "%s '%s'", arg->b ? "o" : "e", name);
2116 if (vis_cmd(vis, cmd) && !arg->b)
2117 break;
2120 return keys;
2123 static char *get_completion_prefix(Vis *vis, Filerange (*text_object)(Text *, size_t)) {
2124 View *view = vis_view(vis);
2125 Text *txt = vis_text(vis);
2126 size_t pos = view_cursor_get(view);
2127 Filerange r = text_object(txt, pos-1);
2128 r = text_range_inner(txt, &r);
2129 if (r.end > pos)
2130 r.end = pos;
2131 size_t size = text_range_size(&r);
2132 if (size == 0) {
2133 vis_info_show(vis, "No valid prefix found for completion");
2134 return NULL;
2137 return text_bytes_alloc0(txt, r.start, size);
2140 static void insert_dialog_selection(Vis *vis, Filerange *range, const char *argv[]) {
2141 char *out = NULL, *err = NULL;
2142 if (vis_pipe_collect(vis, range, argv, &out, &err) == 0) {
2143 View *view = vis_view(vis);
2144 size_t len = out ? strlen(out) : 0;
2145 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
2146 size_t pos = view_cursors_pos(c);
2147 vis_insert(vis, pos, out, len);
2148 view_cursors_scroll_to(c, pos + len);
2150 } else {
2151 vis_info_show(vis, "Completion command failed, is vis-menu in $PATH?");
2153 vis_draw(vis);
2154 free(out);
2155 free(err);
2158 static const char *complete_word(Vis *vis, const char *keys, const Arg *arg) {
2159 Text *txt = vis_text(vis);
2160 Buffer cmd;
2161 buffer_init(&cmd);
2162 char *prefix = get_completion_prefix(vis, text_object_word);
2163 if (prefix && buffer_printf(&cmd, VIS_COMPLETE " --word '%s'", prefix)) {
2164 Filerange all = text_range_new(0, text_size(txt));
2165 insert_dialog_selection(vis, &all, (const char*[]){ buffer_content0(&cmd), NULL });
2167 buffer_release(&cmd);
2168 free(prefix);
2169 return keys;
2172 static const char *complete_filename(Vis *vis, const char *keys, const Arg *arg) {
2173 Buffer cmd;
2174 buffer_init(&cmd);
2175 char *prefix = get_completion_prefix(vis, text_object_filename);
2176 if (prefix && buffer_printf(&cmd, VIS_COMPLETE " --file '%s'", prefix)) {
2177 Filerange empty = text_range_new(0, 0);
2178 insert_dialog_selection(vis, &empty, (const char*[]){ buffer_content0(&cmd), NULL });
2180 buffer_release(&cmd);
2181 free(prefix);
2182 return keys;
2185 static Vis *vis;
2187 static void signal_handler(int signum, siginfo_t *siginfo, void *context) {
2188 vis_signal_handler(vis, signum, siginfo, context);
2191 int main(int argc, char *argv[]) {
2193 VisEvent event = {
2194 .init = vis_lua_init,
2195 .start = vis_lua_start,
2196 .quit = vis_lua_quit,
2197 .mode_insert_input = vis_lua_mode_insert_input,
2198 .mode_replace_input = vis_lua_mode_replace_input,
2199 .file_open = vis_lua_file_open,
2200 .file_save_pre = vis_lua_file_save_pre,
2201 .file_save_post = vis_lua_file_save_post,
2202 .file_close = vis_lua_file_close,
2203 .win_open = vis_lua_win_open,
2204 .win_close = vis_lua_win_close,
2205 .win_highlight = vis_lua_win_highlight,
2206 .win_syntax = vis_lua_win_syntax,
2207 .win_status = vis_lua_win_status,
2210 vis = vis_new(ui_curses_new(), &event);
2211 if (!vis)
2212 return EXIT_FAILURE;
2214 for (int i = 0; i < LENGTH(vis_action); i++) {
2215 const KeyAction *action = &vis_action[i];
2216 if (!vis_action_register(vis, action))
2217 vis_die(vis, "Could not register action: %s\n", action->name);
2220 for (int i = 0; i < LENGTH(default_bindings); i++) {
2221 for (const KeyBinding **binding = default_bindings[i]; binding && *binding; binding++) {
2222 for (const KeyBinding *kb = *binding; kb->key; kb++) {
2223 vis_mode_map(vis, i, false, kb->key, kb);
2228 for (const char **k = keymaps; k[0]; k += 2)
2229 vis_keymap_add(vis, k[0], k[1]);
2231 /* install signal handlers etc. */
2232 struct sigaction sa;
2233 memset(&sa, 0, sizeof sa);
2234 sigfillset(&sa.sa_mask);
2235 sa.sa_flags = SA_SIGINFO;
2236 sa.sa_sigaction = signal_handler;
2237 if (sigaction(SIGBUS, &sa, NULL) == -1 ||
2238 sigaction(SIGINT, &sa, NULL) == -1 ||
2239 sigaction(SIGCONT, &sa, NULL) == -1 ||
2240 sigaction(SIGWINCH, &sa, NULL) == -1 ||
2241 sigaction(SIGTERM, &sa, NULL) == -1 ||
2242 sigaction(SIGHUP, &sa, NULL) == -1) {
2243 vis_die(vis, "Failed to set signal handler: %s\n", strerror(errno));
2246 sa.sa_handler = SIG_IGN;
2247 if (sigaction(SIGPIPE, &sa, NULL) == -1)
2248 vis_die(vis, "Failed to ignore SIGPIPE\n");
2250 sigset_t blockset;
2251 sigemptyset(&blockset);
2252 sigaddset(&blockset, SIGBUS);
2253 sigaddset(&blockset, SIGINT);
2254 sigaddset(&blockset, SIGCONT);
2255 sigaddset(&blockset, SIGWINCH);
2256 sigaddset(&blockset, SIGTERM);
2257 sigaddset(&blockset, SIGHUP);
2258 if (sigprocmask(SIG_BLOCK, &blockset, NULL) == -1)
2259 vis_die(vis, "Failed to block signals\n");
2261 for (int i = 1; i < argc; i++) {
2262 if (argv[i][0] != '-') {
2263 continue;
2264 } else if (strcmp(argv[i], "-") == 0) {
2265 continue;
2266 } else if (strcmp(argv[i], "--") == 0) {
2267 break;
2268 } else if (strcmp(argv[i], "-v") == 0) {
2269 puts("vis " VERSION);
2270 return 0;
2271 } else {
2272 fprintf(stderr, "Unknown command option: %s\n", argv[i]);
2273 return 1;
2277 char *cmd = NULL;
2278 bool end_of_options = false, win_created = false;
2280 for (int i = 1; i < argc; i++) {
2281 if (argv[i][0] == '-' && !end_of_options) {
2282 if (strcmp(argv[i], "-") == 0) {
2283 if (!vis_window_new_fd(vis, STDOUT_FILENO))
2284 vis_die(vis, "Can not create empty buffer\n");
2285 ssize_t len = 0;
2286 char buf[PIPE_BUF];
2287 Text *txt = vis_text(vis);
2288 while ((len = read(STDIN_FILENO, buf, sizeof buf)) > 0)
2289 text_insert(txt, text_size(txt), buf, len);
2290 if (len == -1)
2291 vis_die(vis, "Can not read from stdin\n");
2292 text_snapshot(txt);
2293 int fd = open("/dev/tty", O_RDWR);
2294 if (fd == -1)
2295 vis_die(vis, "Can not reopen stdin\n");
2296 dup2(fd, STDIN_FILENO);
2297 close(fd);
2298 } else if (strcmp(argv[i], "--") == 0) {
2299 end_of_options = true;
2301 } else if (argv[i][0] == '+' && !end_of_options) {
2302 cmd = argv[i] + (argv[i][1] == '/' || argv[i][1] == '?');
2303 } else if (!vis_window_new(vis, argv[i])) {
2304 vis_die(vis, "Can not load `%s': %s\n", argv[i], strerror(errno));
2305 } else {
2306 win_created = true;
2307 if (cmd) {
2308 vis_prompt_cmd(vis, cmd);
2309 cmd = NULL;
2314 if (!vis_window(vis) && !win_created) {
2315 if (!vis_window_new(vis, NULL))
2316 vis_die(vis, "Can not create empty buffer\n");
2317 if (cmd)
2318 vis_prompt_cmd(vis, cmd);
2321 int status = vis_run(vis, argc, argv);
2322 vis_free(vis);
2323 return status;