Merge branch 'lexer-c' of https://github.com/zsugabubus/vis
[vis.git] / main.c
blob80724d9c4dc0b2381f414d09d744909efb268ae5
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 <inttypes.h>
10 #include <sys/stat.h>
11 #include <sys/types.h>
13 #include "ui-terminal.h"
14 #include "vis.h"
15 #include "vis-lua.h"
16 #include "text-util.h"
17 #include "text-motions.h"
18 #include "text-objects.h"
19 #include "util.h"
20 #include "libutf.h"
21 #include "array.h"
22 #include "buffer.h"
24 #define PAGE INT_MAX
25 #define PAGE_HALF (INT_MAX-1)
27 /** functions to be called from keybindings */
28 /* ignore key, do nothing */
29 static const char *nop(Vis*, const char *keys, const Arg *arg);
30 /* record/replay macro indicated by keys */
31 static const char *macro_record(Vis*, const char *keys, const Arg *arg);
32 static const char *macro_replay(Vis*, const char *keys, const Arg *arg);
33 /* temporarily suspend the editor and return to the shell, type 'fg' to get back */
34 static const char *suspend(Vis*, const char *keys, const Arg *arg);
35 /* switch to mode indicated by arg->i */
36 static const char *switchmode(Vis*, const char *keys, const Arg *arg);
37 /* switch to insert mode after performing movement indicated by arg->i */
38 static const char *insertmode(Vis*, const char *keys, const Arg *arg);
39 /* switch to replace mode after performing movement indicated by arg->i */
40 static const char *replacemode(Vis*, const char *keys, const Arg *arg);
41 /* add a new line either before or after the one where the cursor currently is */
42 static const char *openline(Vis*, const char *keys, const Arg *arg);
43 /* join lines from current cursor position to movement indicated by arg */
44 static const char *join(Vis*, const char *keys, const Arg *arg);
45 /* perform last action i.e. action_prev again */
46 static const char *repeat(Vis*, const char *keys, const Arg *arg);
47 /* replace character at cursor with one from keys */
48 static const char *replace(Vis*, const char *keys, const Arg *arg);
49 /* create a new cursor on the previous (arg->i < 0) or next (arg->i > 0) line */
50 static const char *selections_new(Vis*, const char *keys, const Arg *arg);
51 /* try to align all selections on the same column */
52 static const char *selections_align(Vis*, const char *keys, const Arg *arg);
53 /* try to align all selections by inserting the correct amount of white spaces */
54 static const char *selections_align_indent(Vis*, const char *keys, const Arg *arg);
55 /* remove all but the primary cursor and their selections */
56 static const char *selections_clear(Vis*, const char *keys, const Arg *arg);
57 /* remove the least recently added selection */
58 static const char *selections_remove(Vis*, const char *keys, const Arg *arg);
59 /* remove count (or arg->i)-th selection column */
60 static const char *selections_remove_column(Vis*, const char *keys, const Arg *arg);
61 /* remove all but the count (or arg->i)-th selection column */
62 static const char *selections_remove_column_except(Vis*, const char *keys, const Arg *arg);
63 /* move to the previous (arg->i < 0) or next (arg->i > 0) selection */
64 static const char *selections_navigate(Vis*, const char *keys, const Arg *arg);
65 /* select the word the selection is currently over */
66 static const char *selections_match_word(Vis*, const char *keys, const Arg *arg);
67 /* select the next region matching the current selection */
68 static const char *selections_match_next(Vis*, const char *keys, const Arg *arg);
69 /* clear current selection but select next match */
70 static const char *selections_match_skip(Vis*, const char *keys, const Arg *arg);
71 /* rotate selection content count times left (arg->i < 0) or right (arg->i > 0) */
72 static const char *selections_rotate(Vis*, const char *keys, const Arg *arg);
73 /* remove leading and trailing white spaces from selections */
74 static const char *selections_trim(Vis*, const char *keys, const Arg *arg);
75 /* save active selections to mark */
76 static const char *selections_save(Vis*, const char *keys, const Arg *arg);
77 /* restore selections from mark */
78 static const char *selections_restore(Vis*, const char *keys, const Arg *arg);
79 /* union selections from mark */
80 static const char *selections_union(Vis*, const char *keys, const Arg *arg);
81 /* intersect selections from mark */
82 static const char *selections_intersect(Vis*, const char *keys, const Arg *arg);
83 /* perform complement of current active selections */
84 static const char *selections_complement(Vis*, const char *keys, const Arg *arg);
85 /* subtract selections from mark */
86 static const char *selections_minus(Vis*, const char *keys, const Arg *arg);
87 /* pairwise combine selections from mark */
88 static const char *selections_combine(Vis*, const char *keys, const Arg *arg);
89 static Filerange combine_union(const Filerange*, const Filerange*);
90 static Filerange combine_intersect(const Filerange*, const Filerange*);
91 static Filerange combine_longer(const Filerange*, const Filerange*);
92 static Filerange combine_shorter(const Filerange*, const Filerange*);
93 static Filerange combine_leftmost(const Filerange*, const Filerange*);
94 static Filerange combine_rightmost(const Filerange*, const Filerange*);
95 /* adjust current used count according to keys */
96 static const char *count(Vis*, const char *keys, const Arg *arg);
97 /* move to the count-th line or if not given either to the first (arg->i < 0)
98 * or last (arg->i > 0) line of file */
99 static const char *gotoline(Vis*, const char *keys, const Arg *arg);
100 /* make the current action use the operator indicated by arg->i */
101 static const char *operator(Vis*, const char *keys, const Arg *arg);
102 /* blocks to read a key and performs movement indicated by arg->i which
103 * should be one of VIS_MOVE_{RIGHT,LEFT}_{TO,TILL} */
104 static const char *movement_key(Vis*, const char *keys, const Arg *arg);
105 /* perform the movement as indicated by arg->i */
106 static const char *movement(Vis*, const char *keys, const Arg *arg);
107 /* let the current operator affect the range indicated by the text object arg->i */
108 static const char *textobj(Vis*, const char *keys, const Arg *arg);
109 /* move to the other end of selected text */
110 static const char *selection_end(Vis*, const char *keys, const Arg *arg);
111 /* use register indicated by keys for the current operator */
112 static const char *reg(Vis*, const char *keys, const Arg *arg);
113 /* use mark indicated by keys for the current action */
114 static const char *mark(Vis*, const char *keys, const Arg *arg);
115 /* {un,re}do last action, redraw window */
116 static const char *undo(Vis*, const char *keys, const Arg *arg);
117 static const char *redo(Vis*, const char *keys, const Arg *arg);
118 /* earlier, later action chronologically, redraw window */
119 static const char *earlier(Vis*, const char *keys, const Arg *arg);
120 static const char *later(Vis*, const char *keys, const Arg *arg);
121 /* delete from the current cursor position to the end of
122 * movement as indicated by arg->i */
123 static const char *delete(Vis*, const char *keys, const Arg *arg);
124 /* insert register content indicated by keys at current cursor position */
125 static const char *insert_register(Vis*, const char *keys, const Arg *arg);
126 /* show a user prompt to get input with title arg->s */
127 static const char *prompt_show(Vis*, const char *keys, const Arg *arg);
128 /* blocks to read 3 consecutive digits and inserts the corresponding byte value */
129 static const char *insert_verbatim(Vis*, const char *keys, const Arg *arg);
130 /* scroll window content according to arg->i which can be either PAGE, PAGE_HALF,
131 * or an arbitrary number of lines. a multiplier overrides what is given in arg->i.
132 * negative values scroll back, positive forward. */
133 static const char *wscroll(Vis*, const char *keys, const Arg *arg);
134 /* similar to scroll, but do only move window content not cursor position */
135 static const char *wslide(Vis*, const char *keys, const Arg *arg);
136 /* call editor function as indicated by arg->f */
137 static const char *call(Vis*, const char *keys, const Arg *arg);
138 /* call window function as indicated by arg->w */
139 static const char *window(Vis*, const char *keys, const Arg *arg);
140 /* show info about Unicode character at cursor position */
141 static const char *unicode_info(Vis*, const char *keys, const Arg *arg);
142 /* either go to count % of file or to matching item */
143 static const char *percent(Vis*, const char *keys, const Arg *arg);
144 /* navigate jumplist next (arg->i > 0), prev (arg->i < 0), save (arg->i = 0) */
145 static const char *jumplist(Vis*, const char *keys, const Arg *arg);
147 enum {
148 VIS_ACTION_EDITOR_SUSPEND,
149 VIS_ACTION_CURSOR_CHAR_PREV,
150 VIS_ACTION_CURSOR_CHAR_NEXT,
151 VIS_ACTION_CURSOR_LINE_CHAR_PREV,
152 VIS_ACTION_CURSOR_LINE_CHAR_NEXT,
153 VIS_ACTION_CURSOR_CODEPOINT_PREV,
154 VIS_ACTION_CURSOR_CODEPOINT_NEXT,
155 VIS_ACTION_CURSOR_WORD_START_PREV,
156 VIS_ACTION_CURSOR_WORD_START_NEXT,
157 VIS_ACTION_CURSOR_WORD_END_PREV,
158 VIS_ACTION_CURSOR_WORD_END_NEXT,
159 VIS_ACTION_CURSOR_LONGWORD_START_PREV,
160 VIS_ACTION_CURSOR_LONGWORD_START_NEXT,
161 VIS_ACTION_CURSOR_LONGWORD_END_PREV,
162 VIS_ACTION_CURSOR_LONGWORD_END_NEXT,
163 VIS_ACTION_CURSOR_LINE_UP,
164 VIS_ACTION_CURSOR_LINE_DOWN,
165 VIS_ACTION_CURSOR_LINE_START,
166 VIS_ACTION_CURSOR_LINE_FINISH,
167 VIS_ACTION_CURSOR_LINE_BEGIN,
168 VIS_ACTION_CURSOR_LINE_END,
169 VIS_ACTION_CURSOR_SCREEN_LINE_UP,
170 VIS_ACTION_CURSOR_SCREEN_LINE_DOWN,
171 VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN,
172 VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE,
173 VIS_ACTION_CURSOR_SCREEN_LINE_END,
174 VIS_ACTION_CURSOR_PERCENT,
175 VIS_ACTION_CURSOR_BYTE,
176 VIS_ACTION_CURSOR_BYTE_LEFT,
177 VIS_ACTION_CURSOR_BYTE_RIGHT,
178 VIS_ACTION_CURSOR_PARAGRAPH_PREV,
179 VIS_ACTION_CURSOR_PARAGRAPH_NEXT,
180 VIS_ACTION_CURSOR_SENTENCE_PREV,
181 VIS_ACTION_CURSOR_SENTENCE_NEXT,
182 VIS_ACTION_CURSOR_BLOCK_START,
183 VIS_ACTION_CURSOR_BLOCK_END,
184 VIS_ACTION_CURSOR_PARENTHESIS_START,
185 VIS_ACTION_CURSOR_PARENTHESIS_END,
186 VIS_ACTION_CURSOR_COLUMN,
187 VIS_ACTION_CURSOR_LINE_FIRST,
188 VIS_ACTION_CURSOR_LINE_LAST,
189 VIS_ACTION_CURSOR_WINDOW_LINE_TOP,
190 VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE,
191 VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM,
192 VIS_ACTION_CURSOR_SEARCH_REPEAT_FORWARD,
193 VIS_ACTION_CURSOR_SEARCH_REPEAT_BACKWARD,
194 VIS_ACTION_CURSOR_SEARCH_REPEAT,
195 VIS_ACTION_CURSOR_SEARCH_REPEAT_REVERSE,
196 VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD,
197 VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD,
198 VIS_ACTION_WINDOW_PAGE_UP,
199 VIS_ACTION_WINDOW_PAGE_DOWN,
200 VIS_ACTION_WINDOW_HALFPAGE_UP,
201 VIS_ACTION_WINDOW_HALFPAGE_DOWN,
202 VIS_ACTION_MODE_NORMAL,
203 VIS_ACTION_MODE_VISUAL,
204 VIS_ACTION_MODE_VISUAL_LINE,
205 VIS_ACTION_MODE_INSERT,
206 VIS_ACTION_MODE_REPLACE,
207 VIS_ACTION_DELETE_CHAR_PREV,
208 VIS_ACTION_DELETE_CHAR_NEXT,
209 VIS_ACTION_DELETE_LINE_BEGIN,
210 VIS_ACTION_DELETE_WORD_PREV,
211 VIS_ACTION_JUMPLIST_PREV,
212 VIS_ACTION_JUMPLIST_NEXT,
213 VIS_ACTION_JUMPLIST_SAVE,
214 VIS_ACTION_UNDO,
215 VIS_ACTION_REDO,
216 VIS_ACTION_EARLIER,
217 VIS_ACTION_LATER,
218 VIS_ACTION_MACRO_RECORD,
219 VIS_ACTION_MACRO_REPLAY,
220 VIS_ACTION_MARK,
221 VIS_ACTION_REDRAW,
222 VIS_ACTION_REPLACE_CHAR,
223 VIS_ACTION_TOTILL_REPEAT,
224 VIS_ACTION_TOTILL_REVERSE,
225 VIS_ACTION_PROMPT_SEARCH_FORWARD,
226 VIS_ACTION_PROMPT_SEARCH_BACKWARD,
227 VIS_ACTION_TILL_LEFT,
228 VIS_ACTION_TILL_RIGHT,
229 VIS_ACTION_TO_LEFT,
230 VIS_ACTION_TO_RIGHT,
231 VIS_ACTION_REGISTER,
232 VIS_ACTION_OPERATOR_CHANGE,
233 VIS_ACTION_OPERATOR_DELETE,
234 VIS_ACTION_OPERATOR_YANK,
235 VIS_ACTION_OPERATOR_SHIFT_LEFT,
236 VIS_ACTION_OPERATOR_SHIFT_RIGHT,
237 VIS_ACTION_COUNT,
238 VIS_ACTION_INSERT_NEWLINE,
239 VIS_ACTION_INSERT_TAB,
240 VIS_ACTION_INSERT_VERBATIM,
241 VIS_ACTION_INSERT_REGISTER,
242 VIS_ACTION_WINDOW_NEXT,
243 VIS_ACTION_WINDOW_PREV,
244 VIS_ACTION_APPEND_CHAR_NEXT,
245 VIS_ACTION_APPEND_LINE_END,
246 VIS_ACTION_INSERT_LINE_START,
247 VIS_ACTION_OPEN_LINE_ABOVE,
248 VIS_ACTION_OPEN_LINE_BELOW,
249 VIS_ACTION_JOIN_LINES,
250 VIS_ACTION_JOIN_LINES_TRIM,
251 VIS_ACTION_PROMPT_SHOW,
252 VIS_ACTION_REPEAT,
253 VIS_ACTION_SELECTION_FLIP,
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_SELECTIONS_MATCH_WORD,
262 VIS_ACTION_SELECTIONS_NEW_LINE_ABOVE,
263 VIS_ACTION_SELECTIONS_NEW_LINE_ABOVE_FIRST,
264 VIS_ACTION_SELECTIONS_NEW_LINE_BELOW,
265 VIS_ACTION_SELECTIONS_NEW_LINE_BELOW_LAST,
266 VIS_ACTION_SELECTIONS_NEW_LINES_BEGIN,
267 VIS_ACTION_SELECTIONS_NEW_LINES_END,
268 VIS_ACTION_SELECTIONS_NEW_MATCH_NEXT,
269 VIS_ACTION_SELECTIONS_NEW_MATCH_SKIP,
270 VIS_ACTION_SELECTIONS_ALIGN,
271 VIS_ACTION_SELECTIONS_ALIGN_INDENT_LEFT,
272 VIS_ACTION_SELECTIONS_ALIGN_INDENT_RIGHT,
273 VIS_ACTION_SELECTIONS_REMOVE_ALL,
274 VIS_ACTION_SELECTIONS_REMOVE_LAST,
275 VIS_ACTION_SELECTIONS_REMOVE_COLUMN,
276 VIS_ACTION_SELECTIONS_REMOVE_COLUMN_EXCEPT,
277 VIS_ACTION_SELECTIONS_PREV,
278 VIS_ACTION_SELECTIONS_NEXT,
279 VIS_ACTION_SELECTIONS_ROTATE_LEFT,
280 VIS_ACTION_SELECTIONS_ROTATE_RIGHT,
281 VIS_ACTION_SELECTIONS_TRIM,
282 VIS_ACTION_SELECTIONS_SAVE,
283 VIS_ACTION_SELECTIONS_RESTORE,
284 VIS_ACTION_SELECTIONS_UNION,
285 VIS_ACTION_SELECTIONS_INTERSECT,
286 VIS_ACTION_SELECTIONS_COMPLEMENT,
287 VIS_ACTION_SELECTIONS_MINUS,
288 VIS_ACTION_SELECTIONS_COMBINE_UNION,
289 VIS_ACTION_SELECTIONS_COMBINE_INTERSECT,
290 VIS_ACTION_SELECTIONS_COMBINE_LONGER,
291 VIS_ACTION_SELECTIONS_COMBINE_SHORTER,
292 VIS_ACTION_SELECTIONS_COMBINE_LEFTMOST,
293 VIS_ACTION_SELECTIONS_COMBINE_RIGHTMOST,
294 VIS_ACTION_TEXT_OBJECT_WORD_OUTER,
295 VIS_ACTION_TEXT_OBJECT_WORD_INNER,
296 VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER,
297 VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER,
298 VIS_ACTION_TEXT_OBJECT_SENTENCE,
299 VIS_ACTION_TEXT_OBJECT_PARAGRAPH,
300 VIS_ACTION_TEXT_OBJECT_PARAGRAPH_OUTER,
301 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER,
302 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER,
303 VIS_ACTION_TEXT_OBJECT_PARENTHESIS_OUTER,
304 VIS_ACTION_TEXT_OBJECT_PARENTHESIS_INNER,
305 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER,
306 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER,
307 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER,
308 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER,
309 VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER,
310 VIS_ACTION_TEXT_OBJECT_QUOTE_INNER,
311 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER,
312 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER,
313 VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER,
314 VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER,
315 VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER,
316 VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER,
317 VIS_ACTION_TEXT_OBJECT_LINE_OUTER,
318 VIS_ACTION_TEXT_OBJECT_LINE_INNER,
319 VIS_ACTION_TEXT_OBJECT_INDENTATION,
320 VIS_ACTION_TEXT_OBJECT_SEARCH_FORWARD,
321 VIS_ACTION_TEXT_OBJECT_SEARCH_BACKWARD,
322 VIS_ACTION_UNICODE_INFO,
323 VIS_ACTION_UTF8_INFO,
324 VIS_ACTION_NOP,
327 static const KeyAction vis_action[] = {
328 [VIS_ACTION_EDITOR_SUSPEND] = {
329 "vis-suspend",
330 VIS_HELP("Suspend the editor")
331 suspend,
333 [VIS_ACTION_CURSOR_CHAR_PREV] = {
334 "vis-motion-char-prev",
335 VIS_HELP("Move cursor left, to the previous character")
336 movement, { .i = VIS_MOVE_CHAR_PREV }
338 [VIS_ACTION_CURSOR_CHAR_NEXT] = {
339 "vis-motion-char-next",
340 VIS_HELP("Move cursor right, to the next character")
341 movement, { .i = VIS_MOVE_CHAR_NEXT }
343 [VIS_ACTION_CURSOR_LINE_CHAR_PREV] = {
344 "vis-motion-line-char-prev",
345 VIS_HELP("Move cursor left, to the previous character on the same line")
346 movement, { .i = VIS_MOVE_LINE_CHAR_PREV }
348 [VIS_ACTION_CURSOR_LINE_CHAR_NEXT] = {
349 "vis-motion-line-char-next",
350 VIS_HELP("Move cursor right, to the next character on the same line")
351 movement, { .i = VIS_MOVE_LINE_CHAR_NEXT }
353 [VIS_ACTION_CURSOR_CODEPOINT_PREV] = {
354 "vis-motion-codepoint-prev",
355 VIS_HELP("Move to the previous Unicode codepoint")
356 movement, { .i = VIS_MOVE_CODEPOINT_PREV }
358 [VIS_ACTION_CURSOR_CODEPOINT_NEXT] = {
359 "vis-motion-codepoint-next",
360 VIS_HELP("Move to the next Unicode codepoint")
361 movement, { .i = VIS_MOVE_CODEPOINT_NEXT }
363 [VIS_ACTION_CURSOR_WORD_START_PREV] = {
364 "vis-motion-word-start-prev",
365 VIS_HELP("Move cursor words backwards")
366 movement, { .i = VIS_MOVE_WORD_START_PREV }
368 [VIS_ACTION_CURSOR_WORD_START_NEXT] = {
369 "vis-motion-word-start-next",
370 VIS_HELP("Move cursor words forwards")
371 movement, { .i = VIS_MOVE_WORD_START_NEXT }
373 [VIS_ACTION_CURSOR_WORD_END_PREV] = {
374 "vis-motion-word-end-prev",
375 VIS_HELP("Move cursor backwards to the end of word")
376 movement, { .i = VIS_MOVE_WORD_END_PREV }
378 [VIS_ACTION_CURSOR_WORD_END_NEXT] = {
379 "vis-motion-word-end-next",
380 VIS_HELP("Move cursor forward to the end of word")
381 movement, { .i = VIS_MOVE_WORD_END_NEXT }
383 [VIS_ACTION_CURSOR_LONGWORD_START_PREV] = {
384 "vis-motion-bigword-start-prev",
385 VIS_HELP("Move cursor WORDS backwards")
386 movement, { .i = VIS_MOVE_LONGWORD_START_PREV }
388 [VIS_ACTION_CURSOR_LONGWORD_START_NEXT] = {
389 "vis-motion-bigword-start-next",
390 VIS_HELP("Move cursor WORDS forwards")
391 movement, { .i = VIS_MOVE_LONGWORD_START_NEXT }
393 [VIS_ACTION_CURSOR_LONGWORD_END_PREV] = {
394 "vis-motion-bigword-end-prev",
395 VIS_HELP("Move cursor backwards to the end of WORD")
396 movement, { .i = VIS_MOVE_LONGWORD_END_PREV }
398 [VIS_ACTION_CURSOR_LONGWORD_END_NEXT] = {
399 "vis-motion-bigword-end-next",
400 VIS_HELP("Move cursor forward to the end of WORD")
401 movement, { .i = VIS_MOVE_LONGWORD_END_NEXT }
403 [VIS_ACTION_CURSOR_LINE_UP] = {
404 "vis-motion-line-up",
405 VIS_HELP("Move cursor line upwards")
406 movement, { .i = VIS_MOVE_LINE_UP }
408 [VIS_ACTION_CURSOR_LINE_DOWN] = {
409 "vis-motion-line-down",
410 VIS_HELP("Move cursor line downwards")
411 movement, { .i = VIS_MOVE_LINE_DOWN }
413 [VIS_ACTION_CURSOR_LINE_START] = {
414 "vis-motion-line-start",
415 VIS_HELP("Move cursor to first non-blank character of the line")
416 movement, { .i = VIS_MOVE_LINE_START }
418 [VIS_ACTION_CURSOR_LINE_FINISH] = {
419 "vis-motion-line-finish",
420 VIS_HELP("Move cursor to last non-blank character of the line")
421 movement, { .i = VIS_MOVE_LINE_FINISH }
423 [VIS_ACTION_CURSOR_LINE_BEGIN] = {
424 "vis-motion-line-begin",
425 VIS_HELP("Move cursor to first character of the line")
426 movement, { .i = VIS_MOVE_LINE_BEGIN }
428 [VIS_ACTION_CURSOR_LINE_END] = {
429 "vis-motion-line-end",
430 VIS_HELP("Move cursor to end of the line")
431 movement, { .i = VIS_MOVE_LINE_END }
433 [VIS_ACTION_CURSOR_SCREEN_LINE_UP] = {
434 "vis-motion-screenline-up",
435 VIS_HELP("Move cursor screen/display line upwards")
436 movement, { .i = VIS_MOVE_SCREEN_LINE_UP }
438 [VIS_ACTION_CURSOR_SCREEN_LINE_DOWN] = {
439 "vis-motion-screenline-down",
440 VIS_HELP("Move cursor screen/display line downwards")
441 movement, { .i = VIS_MOVE_SCREEN_LINE_DOWN }
443 [VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN] = {
444 "vis-motion-screenline-begin",
445 VIS_HELP("Move cursor to beginning of screen/display line")
446 movement, { .i = VIS_MOVE_SCREEN_LINE_BEGIN }
448 [VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE] = {
449 "vis-motion-screenline-middle",
450 VIS_HELP("Move cursor to middle of screen/display line")
451 movement, { .i = VIS_MOVE_SCREEN_LINE_MIDDLE }
453 [VIS_ACTION_CURSOR_SCREEN_LINE_END] = {
454 "vis-motion-screenline-end",
455 VIS_HELP("Move cursor to end of screen/display line")
456 movement, { .i = VIS_MOVE_SCREEN_LINE_END }
458 [VIS_ACTION_CURSOR_PERCENT] = {
459 "vis-motion-percent",
460 VIS_HELP("Move to count % of file or matching item")
461 percent
463 [VIS_ACTION_CURSOR_BYTE] = {
464 "vis-motion-byte",
465 VIS_HELP("Move to absolute byte position")
466 movement, { .i = VIS_MOVE_BYTE }
468 [VIS_ACTION_CURSOR_BYTE_LEFT] = {
469 "vis-motion-byte-left",
470 VIS_HELP("Move count bytes to the left")
471 movement, { .i = VIS_MOVE_BYTE_LEFT }
473 [VIS_ACTION_CURSOR_BYTE_RIGHT] = {
474 "vis-motion-byte-right",
475 VIS_HELP("Move count bytes to the right")
476 movement, { .i = VIS_MOVE_BYTE_RIGHT }
478 [VIS_ACTION_CURSOR_PARAGRAPH_PREV] = {
479 "vis-motion-paragraph-prev",
480 VIS_HELP("Move cursor paragraph backward")
481 movement, { .i = VIS_MOVE_PARAGRAPH_PREV }
483 [VIS_ACTION_CURSOR_PARAGRAPH_NEXT] = {
484 "vis-motion-paragraph-next",
485 VIS_HELP("Move cursor paragraph forward")
486 movement, { .i = VIS_MOVE_PARAGRAPH_NEXT }
488 [VIS_ACTION_CURSOR_SENTENCE_PREV] = {
489 "vis-motion-sentence-prev",
490 VIS_HELP("Move cursor sentence backward")
491 movement, { .i = VIS_MOVE_SENTENCE_PREV }
493 [VIS_ACTION_CURSOR_SENTENCE_NEXT] = {
494 "vis-motion-sentence-next",
495 VIS_HELP("Move cursor sentence forward")
496 movement, { .i = VIS_MOVE_SENTENCE_NEXT }
498 [VIS_ACTION_CURSOR_BLOCK_START] = {
499 "vis-motion-block-start",
500 VIS_HELP("Move cursor to the opening curly brace in a block")
501 movement, { .i = VIS_MOVE_BLOCK_START }
503 [VIS_ACTION_CURSOR_BLOCK_END] = {
504 "vis-motion-block-end",
505 VIS_HELP("Move cursor to the closing curly brace in a block")
506 movement, { .i = VIS_MOVE_BLOCK_END }
508 [VIS_ACTION_CURSOR_PARENTHESIS_START] = {
509 "vis-motion-parenthesis-start",
510 VIS_HELP("Move cursor to the opening parenthesis inside a pair of parentheses")
511 movement, { .i = VIS_MOVE_PARENTHESIS_START }
513 [VIS_ACTION_CURSOR_PARENTHESIS_END] = {
514 "vis-motion-parenthesis-end",
515 VIS_HELP("Move cursor to the closing parenthesis inside a pair of parentheses")
516 movement, { .i = VIS_MOVE_PARENTHESIS_END }
518 [VIS_ACTION_CURSOR_COLUMN] = {
519 "vis-motion-column",
520 VIS_HELP("Move cursor to given column of current line")
521 movement, { .i = VIS_MOVE_COLUMN }
523 [VIS_ACTION_CURSOR_LINE_FIRST] = {
524 "vis-motion-line-first",
525 VIS_HELP("Move cursor to given line (defaults to first)")
526 gotoline, { .i = -1 }
528 [VIS_ACTION_CURSOR_LINE_LAST] = {
529 "vis-motion-line-last",
530 VIS_HELP("Move cursor to given line (defaults to last)")
531 gotoline, { .i = +1 }
533 [VIS_ACTION_CURSOR_WINDOW_LINE_TOP] = {
534 "vis-motion-window-line-top",
535 VIS_HELP("Move cursor to top line of the window")
536 movement, { .i = VIS_MOVE_WINDOW_LINE_TOP }
538 [VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE] = {
539 "vis-motion-window-line-middle",
540 VIS_HELP("Move cursor to middle line of the window")
541 movement, { .i = VIS_MOVE_WINDOW_LINE_MIDDLE }
543 [VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM] = {
544 "vis-motion-window-line-bottom",
545 VIS_HELP("Move cursor to bottom line of the window")
546 movement, { .i = VIS_MOVE_WINDOW_LINE_BOTTOM }
548 [VIS_ACTION_CURSOR_SEARCH_REPEAT_FORWARD] = {
549 "vis-motion-search-repeat-forward",
550 VIS_HELP("Move cursor to next match in forward direction")
551 movement, { .i = VIS_MOVE_SEARCH_REPEAT_FORWARD }
553 [VIS_ACTION_CURSOR_SEARCH_REPEAT_BACKWARD] = {
554 "vis-motion-search-repeat-backward",
555 VIS_HELP("Move cursor to previous match in backward direction")
556 movement, { .i = VIS_MOVE_SEARCH_REPEAT_BACKWARD }
558 [VIS_ACTION_CURSOR_SEARCH_REPEAT] = {
559 "vis-motion-search-repeat",
560 VIS_HELP("Move cursor to next match")
561 movement, { .i = VIS_MOVE_SEARCH_REPEAT }
563 [VIS_ACTION_CURSOR_SEARCH_REPEAT_REVERSE] = {
564 "vis-motion-search-repeat-reverse",
565 VIS_HELP("Move cursor to next match in opposite direction")
566 movement, { .i = VIS_MOVE_SEARCH_REPEAT_REVERSE }
568 [VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD] = {
569 "vis-motion-search-word-forward",
570 VIS_HELP("Move cursor to next occurrence of the word under cursor")
571 movement, { .i = VIS_MOVE_SEARCH_WORD_FORWARD }
573 [VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD] = {
574 "vis-motion-search-word-backward",
575 VIS_HELP("Move cursor to previous occurrence of the word under cursor")
576 movement, { .i = VIS_MOVE_SEARCH_WORD_BACKWARD }
578 [VIS_ACTION_WINDOW_PAGE_UP] = {
579 "vis-window-page-up",
580 VIS_HELP("Scroll window pages backwards (upwards)")
581 wscroll, { .i = -PAGE }
583 [VIS_ACTION_WINDOW_HALFPAGE_UP] = {
584 "vis-window-halfpage-up",
585 VIS_HELP("Scroll window half pages backwards (upwards)")
586 wscroll, { .i = -PAGE_HALF }
588 [VIS_ACTION_WINDOW_PAGE_DOWN] = {
589 "vis-window-page-down",
590 VIS_HELP("Scroll window pages forwards (downwards)")
591 wscroll, { .i = +PAGE }
593 [VIS_ACTION_WINDOW_HALFPAGE_DOWN] = {
594 "vis-window-halfpage-down",
595 VIS_HELP("Scroll window half pages forwards (downwards)")
596 wscroll, { .i = +PAGE_HALF }
598 [VIS_ACTION_MODE_NORMAL] = {
599 "vis-mode-normal",
600 VIS_HELP("Enter normal mode")
601 switchmode, { .i = VIS_MODE_NORMAL }
603 [VIS_ACTION_MODE_VISUAL] = {
604 "vis-mode-visual-charwise",
605 VIS_HELP("Enter characterwise visual mode")
606 switchmode, { .i = VIS_MODE_VISUAL }
608 [VIS_ACTION_MODE_VISUAL_LINE] = {
609 "vis-mode-visual-linewise",
610 VIS_HELP("Enter linewise visual mode")
611 switchmode, { .i = VIS_MODE_VISUAL_LINE }
613 [VIS_ACTION_MODE_INSERT] = {
614 "vis-mode-insert",
615 VIS_HELP("Enter insert mode")
616 insertmode, { .i = VIS_MOVE_NOP }
618 [VIS_ACTION_MODE_REPLACE] = {
619 "vis-mode-replace",
620 VIS_HELP("Enter replace mode")
621 replacemode, { .i = VIS_MOVE_NOP }
623 [VIS_ACTION_DELETE_CHAR_PREV] = {
624 "vis-delete-char-prev",
625 VIS_HELP("Delete the previous character")
626 delete, { .i = VIS_MOVE_CHAR_PREV }
628 [VIS_ACTION_DELETE_CHAR_NEXT] = {
629 "vis-delete-char-next",
630 VIS_HELP("Delete the next character")
631 delete, { .i = VIS_MOVE_CHAR_NEXT }
633 [VIS_ACTION_DELETE_LINE_BEGIN] = {
634 "vis-delete-line-begin",
635 VIS_HELP("Delete until the start of the current line")
636 delete, { .i = VIS_MOVE_LINE_BEGIN }
638 [VIS_ACTION_DELETE_WORD_PREV] = {
639 "vis-delete-word-prev",
640 VIS_HELP("Delete the previous WORD")
641 delete, { .i = VIS_MOVE_WORD_START_PREV }
643 [VIS_ACTION_JUMPLIST_PREV] = {
644 "vis-jumplist-prev",
645 VIS_HELP("Go to older cursor position in jump list")
646 jumplist, { .i = -1 }
648 [VIS_ACTION_JUMPLIST_NEXT] = {
649 "vis-jumplist-next",
650 VIS_HELP("Go to newer cursor position in jump list")
651 jumplist, { .i = +1 }
653 [VIS_ACTION_JUMPLIST_SAVE] = {
654 "vis-jumplist-save",
655 VIS_HELP("Save current selections in jump list")
656 jumplist, { .i = 0 }
658 [VIS_ACTION_UNDO] = {
659 "vis-undo",
660 VIS_HELP("Undo last change")
661 undo,
663 [VIS_ACTION_REDO] = {
664 "vis-redo",
665 VIS_HELP("Redo last change")
666 redo,
668 [VIS_ACTION_EARLIER] = {
669 "vis-earlier",
670 VIS_HELP("Goto older text state")
671 earlier,
673 [VIS_ACTION_LATER] = {
674 "vis-later",
675 VIS_HELP("Goto newer text state")
676 later,
678 [VIS_ACTION_MACRO_RECORD] = {
679 "vis-macro-record",
680 VIS_HELP("Record macro into given register")
681 macro_record,
683 [VIS_ACTION_MACRO_REPLAY] = {
684 "vis-macro-replay",
685 VIS_HELP("Replay macro, execute the content of the given register")
686 macro_replay,
688 [VIS_ACTION_MARK] = {
689 "vis-mark",
690 VIS_HELP("Use given mark for next action")
691 mark,
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_COUNT] = {
774 "vis-count",
775 VIS_HELP("Count specifier")
776 count,
778 [VIS_ACTION_INSERT_NEWLINE] = {
779 "vis-insert-newline",
780 VIS_HELP("Insert a line break (depending on file type)")
781 call, { .f = vis_insert_nl }
783 [VIS_ACTION_INSERT_TAB] = {
784 "vis-insert-tab",
785 VIS_HELP("Insert a tab (might be converted to spaces)")
786 call, { .f = vis_insert_tab }
788 [VIS_ACTION_INSERT_VERBATIM] = {
789 "vis-insert-verbatim",
790 VIS_HELP("Insert Unicode character based on code point")
791 insert_verbatim,
793 [VIS_ACTION_INSERT_REGISTER] = {
794 "vis-insert-register",
795 VIS_HELP("Insert specified register content")
796 insert_register,
798 [VIS_ACTION_WINDOW_NEXT] = {
799 "vis-window-next",
800 VIS_HELP("Focus next window")
801 call, { .f = vis_window_next }
803 [VIS_ACTION_WINDOW_PREV] = {
804 "vis-window-prev",
805 VIS_HELP("Focus previous window")
806 call, { .f = vis_window_prev }
808 [VIS_ACTION_APPEND_CHAR_NEXT] = {
809 "vis-append-char-next",
810 VIS_HELP("Append text after the cursor")
811 insertmode, { .i = VIS_MOVE_LINE_CHAR_NEXT }
813 [VIS_ACTION_APPEND_LINE_END] = {
814 "vis-append-line-end",
815 VIS_HELP("Append text after the end of the line")
816 insertmode, { .i = VIS_MOVE_LINE_END },
818 [VIS_ACTION_INSERT_LINE_START] = {
819 "vis-insert-line-start",
820 VIS_HELP("Insert text before the first non-blank in the line")
821 insertmode, { .i = VIS_MOVE_LINE_START },
823 [VIS_ACTION_OPEN_LINE_ABOVE] = {
824 "vis-open-line-above",
825 VIS_HELP("Begin a new line above the cursor")
826 openline, { .i = -1 }
828 [VIS_ACTION_OPEN_LINE_BELOW] = {
829 "vis-open-line-below",
830 VIS_HELP("Begin a new line below the cursor")
831 openline, { .i = +1 }
833 [VIS_ACTION_JOIN_LINES] = {
834 "vis-join-lines",
835 VIS_HELP("Join selected lines")
836 join, { .s = " " }
838 [VIS_ACTION_JOIN_LINES_TRIM] = {
839 "vis-join-lines-trim",
840 VIS_HELP("Join selected lines, remove white space")
841 join, { .s = "" }
843 [VIS_ACTION_PROMPT_SHOW] = {
844 "vis-prompt-show",
845 VIS_HELP("Show editor command line prompt")
846 prompt_show, { .s = ":" }
848 [VIS_ACTION_REPEAT] = {
849 "vis-repeat",
850 VIS_HELP("Repeat latest editor command")
851 repeat
853 [VIS_ACTION_SELECTION_FLIP] = {
854 "vis-selection-flip",
855 VIS_HELP("Flip selection, move cursor to other end")
856 selection_end,
858 [VIS_ACTION_WINDOW_REDRAW_TOP] = {
859 "vis-window-redraw-top",
860 VIS_HELP("Redraw cursor line at the top of the window")
861 window, { .w = view_redraw_top }
863 [VIS_ACTION_WINDOW_REDRAW_CENTER] = {
864 "vis-window-redraw-center",
865 VIS_HELP("Redraw cursor line at the center of the window")
866 window, { .w = view_redraw_center }
868 [VIS_ACTION_WINDOW_REDRAW_BOTTOM] = {
869 "vis-window-redraw-bottom",
870 VIS_HELP("Redraw cursor line at the bottom of the window")
871 window, { .w = view_redraw_bottom }
873 [VIS_ACTION_WINDOW_SLIDE_UP] = {
874 "vis-window-slide-up",
875 VIS_HELP("Slide window content upwards")
876 wslide, { .i = -1 }
878 [VIS_ACTION_WINDOW_SLIDE_DOWN] = {
879 "vis-window-slide-down",
880 VIS_HELP("Slide window content downwards")
881 wslide, { .i = +1 }
883 [VIS_ACTION_PUT_AFTER] = {
884 "vis-put-after",
885 VIS_HELP("Put text after the cursor")
886 operator, { .i = VIS_OP_PUT_AFTER }
888 [VIS_ACTION_PUT_BEFORE] = {
889 "vis-put-before",
890 VIS_HELP("Put text before the cursor")
891 operator, { .i = VIS_OP_PUT_BEFORE }
893 [VIS_ACTION_SELECTIONS_MATCH_WORD] = {
894 "vis-selections-select-word",
895 VIS_HELP("Select word under cursor")
896 selections_match_word,
898 [VIS_ACTION_SELECTIONS_NEW_LINE_ABOVE] = {
899 "vis-selection-new-lines-above",
900 VIS_HELP("Create a new selection on the line above")
901 selections_new, { .i = -1 }
903 [VIS_ACTION_SELECTIONS_NEW_LINE_ABOVE_FIRST] = {
904 "vis-selection-new-lines-above-first",
905 VIS_HELP("Create a new selection on the line above the first selection")
906 selections_new, { .i = INT_MIN }
908 [VIS_ACTION_SELECTIONS_NEW_LINE_BELOW] = {
909 "vis-selection-new-lines-below",
910 VIS_HELP("Create a new selection on the line below")
911 selections_new, { .i = +1 }
913 [VIS_ACTION_SELECTIONS_NEW_LINE_BELOW_LAST] = {
914 "vis-selection-new-lines-below-last",
915 VIS_HELP("Create a new selection on the line below the last selection")
916 selections_new, { .i = INT_MAX }
918 [VIS_ACTION_SELECTIONS_NEW_LINES_BEGIN] = {
919 "vis-selection-new-lines-begin",
920 VIS_HELP("Create a new selection at the start of every line covered by selection")
921 operator, { .i = VIS_OP_CURSOR_SOL }
923 [VIS_ACTION_SELECTIONS_NEW_LINES_END] = {
924 "vis-selection-new-lines-end",
925 VIS_HELP("Create a new selection at the end of every line covered by selection")
926 operator, { .i = VIS_OP_CURSOR_EOL }
928 [VIS_ACTION_SELECTIONS_NEW_MATCH_NEXT] = {
929 "vis-selection-new-match-next",
930 VIS_HELP("Select the next region matching the current selection")
931 selections_match_next,
933 [VIS_ACTION_SELECTIONS_NEW_MATCH_SKIP] = {
934 "vis-selection-new-match-skip",
935 VIS_HELP("Clear current selection, but select next match")
936 selections_match_skip,
938 [VIS_ACTION_SELECTIONS_ALIGN] = {
939 "vis-selections-align",
940 VIS_HELP("Try to align all selections on the same column")
941 selections_align,
943 [VIS_ACTION_SELECTIONS_ALIGN_INDENT_LEFT] = {
944 "vis-selections-align-indent-left",
945 VIS_HELP("Left-align all selections by inserting spaces")
946 selections_align_indent, { .i = -1 }
948 [VIS_ACTION_SELECTIONS_ALIGN_INDENT_RIGHT] = {
949 "vis-selections-align-indent-right",
950 VIS_HELP("Right-align all selections by inserting spaces")
951 selections_align_indent, { .i = +1 }
953 [VIS_ACTION_SELECTIONS_REMOVE_ALL] = {
954 "vis-selections-remove-all",
955 VIS_HELP("Remove all but the primary selection")
956 selections_clear,
958 [VIS_ACTION_SELECTIONS_REMOVE_LAST] = {
959 "vis-selections-remove-last",
960 VIS_HELP("Remove primary selection")
961 selections_remove,
963 [VIS_ACTION_SELECTIONS_REMOVE_COLUMN] = {
964 "vis-selections-remove-column",
965 VIS_HELP("Remove count selection column")
966 selections_remove_column, { .i = 1 }
968 [VIS_ACTION_SELECTIONS_REMOVE_COLUMN_EXCEPT] = {
969 "vis-selections-remove-column-except",
970 VIS_HELP("Remove all but the count selection column")
971 selections_remove_column_except, { .i = 1 }
973 [VIS_ACTION_SELECTIONS_PREV] = {
974 "vis-selection-prev",
975 VIS_HELP("Move to the previous selection")
976 selections_navigate, { .i = -PAGE_HALF }
978 [VIS_ACTION_SELECTIONS_NEXT] = {
979 "vis-selection-next",
980 VIS_HELP("Move to the next selection")
981 selections_navigate, { .i = +PAGE_HALF }
983 [VIS_ACTION_SELECTIONS_ROTATE_LEFT] = {
984 "vis-selections-rotate-left",
985 VIS_HELP("Rotate selections left")
986 selections_rotate, { .i = -1 }
988 [VIS_ACTION_SELECTIONS_ROTATE_RIGHT] = {
989 "vis-selections-rotate-right",
990 VIS_HELP("Rotate selections right")
991 selections_rotate, { .i = +1 }
993 [VIS_ACTION_SELECTIONS_TRIM] = {
994 "vis-selections-trim",
995 VIS_HELP("Remove leading and trailing white space from selections")
996 selections_trim
998 [VIS_ACTION_SELECTIONS_SAVE] = {
999 "vis-selections-save",
1000 VIS_HELP("Save currently active selections to mark")
1001 selections_save
1003 [VIS_ACTION_SELECTIONS_RESTORE] = {
1004 "vis-selections-restore",
1005 VIS_HELP("Restore selections from mark")
1006 selections_restore
1008 [VIS_ACTION_SELECTIONS_UNION] = {
1009 "vis-selections-union",
1010 VIS_HELP("Add selections from mark")
1011 selections_union
1013 [VIS_ACTION_SELECTIONS_INTERSECT] = {
1014 "vis-selections-intersect",
1015 VIS_HELP("Intersect with selections from mark")
1016 selections_intersect
1018 [VIS_ACTION_SELECTIONS_COMPLEMENT] = {
1019 "vis-selections-complement",
1020 VIS_HELP("Complement selections")
1021 selections_complement
1023 [VIS_ACTION_SELECTIONS_MINUS] = {
1024 "vis-selections-minus",
1025 VIS_HELP("Subtract selections from mark")
1026 selections_minus
1028 [VIS_ACTION_SELECTIONS_COMBINE_UNION] = {
1029 "vis-selections-combine-union",
1030 VIS_HELP("Pairwise union with selections from mark")
1031 selections_combine, { .combine = combine_union }
1033 [VIS_ACTION_SELECTIONS_COMBINE_INTERSECT] = {
1034 "vis-selections-combine-intersect",
1035 VIS_HELP("Pairwise intersect with selections from mark")
1036 selections_combine, { .combine = combine_intersect }
1038 [VIS_ACTION_SELECTIONS_COMBINE_LONGER] = {
1039 "vis-selections-combine-longer",
1040 VIS_HELP("Pairwise combine: take longer")
1041 selections_combine, { .combine = combine_longer }
1043 [VIS_ACTION_SELECTIONS_COMBINE_SHORTER] = {
1044 "vis-selections-combine-shorter",
1045 VIS_HELP("Pairwise combine: take shorter")
1046 selections_combine, { .combine = combine_shorter }
1048 [VIS_ACTION_SELECTIONS_COMBINE_LEFTMOST] = {
1049 "vis-selections-combine-leftmost",
1050 VIS_HELP("Pairwise combine: leftmost")
1051 selections_combine, { .combine = combine_leftmost }
1053 [VIS_ACTION_SELECTIONS_COMBINE_RIGHTMOST] = {
1054 "vis-selections-combine-rightmost",
1055 VIS_HELP("Pairwise combine: rightmost")
1056 selections_combine, { .combine = combine_rightmost }
1058 [VIS_ACTION_TEXT_OBJECT_WORD_OUTER] = {
1059 "vis-textobject-word-outer",
1060 VIS_HELP("A word leading and trailing whitespace included")
1061 textobj, { .i = VIS_TEXTOBJECT_OUTER_WORD }
1063 [VIS_ACTION_TEXT_OBJECT_WORD_INNER] = {
1064 "vis-textobject-word-inner",
1065 VIS_HELP("A word leading and trailing whitespace excluded")
1066 textobj, { .i = VIS_TEXTOBJECT_INNER_WORD }
1068 [VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER] = {
1069 "vis-textobject-bigword-outer",
1070 VIS_HELP("A WORD leading and trailing whitespace included")
1071 textobj, { .i = VIS_TEXTOBJECT_OUTER_LONGWORD }
1073 [VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER] = {
1074 "vis-textobject-bigword-inner",
1075 VIS_HELP("A WORD leading and trailing whitespace excluded")
1076 textobj, { .i = VIS_TEXTOBJECT_INNER_LONGWORD }
1078 [VIS_ACTION_TEXT_OBJECT_SENTENCE] = {
1079 "vis-textobject-sentence",
1080 VIS_HELP("A sentence")
1081 textobj, { .i = VIS_TEXTOBJECT_SENTENCE }
1083 [VIS_ACTION_TEXT_OBJECT_PARAGRAPH] = {
1084 "vis-textobject-paragraph",
1085 VIS_HELP("A paragraph")
1086 textobj, { .i = VIS_TEXTOBJECT_PARAGRAPH }
1088 [VIS_ACTION_TEXT_OBJECT_PARAGRAPH_OUTER] = {
1089 "vis-textobject-paragraph-outer",
1090 VIS_HELP("A paragraph (outer variant)")
1091 textobj, { .i = VIS_TEXTOBJECT_PARAGRAPH_OUTER }
1093 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER] = {
1094 "vis-textobject-square-bracket-outer",
1095 VIS_HELP("[] block (outer variant)")
1096 textobj, { .i = VIS_TEXTOBJECT_OUTER_SQUARE_BRACKET }
1098 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER] = {
1099 "vis-textobject-square-bracket-inner",
1100 VIS_HELP("[] block (inner variant)")
1101 textobj, { .i = VIS_TEXTOBJECT_INNER_SQUARE_BRACKET }
1103 [VIS_ACTION_TEXT_OBJECT_PARENTHESIS_OUTER] = {
1104 "vis-textobject-parenthesis-outer",
1105 VIS_HELP("() block (outer variant)")
1106 textobj, { .i = VIS_TEXTOBJECT_OUTER_PARENTHESIS }
1108 [VIS_ACTION_TEXT_OBJECT_PARENTHESIS_INNER] = {
1109 "vis-textobject-parenthesis-inner",
1110 VIS_HELP("() block (inner variant)")
1111 textobj, { .i = VIS_TEXTOBJECT_INNER_PARENTHESIS }
1113 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER] = {
1114 "vis-textobject-angle-bracket-outer",
1115 VIS_HELP("<> block (outer variant)")
1116 textobj, { .i = VIS_TEXTOBJECT_OUTER_ANGLE_BRACKET }
1118 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER] = {
1119 "vis-textobject-angle-bracket-inner",
1120 VIS_HELP("<> block (inner variant)")
1121 textobj, { .i = VIS_TEXTOBJECT_INNER_ANGLE_BRACKET }
1123 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER] = {
1124 "vis-textobject-curly-bracket-outer",
1125 VIS_HELP("{} block (outer variant)")
1126 textobj, { .i = VIS_TEXTOBJECT_OUTER_CURLY_BRACKET }
1128 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER] = {
1129 "vis-textobject-curly-bracket-inner",
1130 VIS_HELP("{} block (inner variant)")
1131 textobj, { .i = VIS_TEXTOBJECT_INNER_CURLY_BRACKET }
1133 [VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER] = {
1134 "vis-textobject-quote-outer",
1135 VIS_HELP("A quoted string, including the quotation marks")
1136 textobj, { .i = VIS_TEXTOBJECT_OUTER_QUOTE }
1138 [VIS_ACTION_TEXT_OBJECT_QUOTE_INNER] = {
1139 "vis-textobject-quote-inner",
1140 VIS_HELP("A quoted string, excluding the quotation marks")
1141 textobj, { .i = VIS_TEXTOBJECT_INNER_QUOTE }
1143 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER] = {
1144 "vis-textobject-single-quote-outer",
1145 VIS_HELP("A single quoted string, including the quotation marks")
1146 textobj, { .i = VIS_TEXTOBJECT_OUTER_SINGLE_QUOTE }
1148 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER] = {
1149 "vis-textobject-single-quote-inner",
1150 VIS_HELP("A single quoted string, excluding the quotation marks")
1151 textobj, { .i = VIS_TEXTOBJECT_INNER_SINGLE_QUOTE }
1153 [VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER] = {
1154 "vis-textobject-backtick-outer",
1155 VIS_HELP("A backtick delimited string (outer variant)")
1156 textobj, { .i = VIS_TEXTOBJECT_OUTER_BACKTICK }
1158 [VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER] = {
1159 "vis-textobject-backtick-inner",
1160 VIS_HELP("A backtick delimited string (inner variant)")
1161 textobj, { .i = VIS_TEXTOBJECT_INNER_BACKTICK }
1163 [VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER] = {
1164 "vis-textobject-entire-outer",
1165 VIS_HELP("The whole text content")
1166 textobj, { .i = VIS_TEXTOBJECT_OUTER_ENTIRE }
1168 [VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER] = {
1169 "vis-textobject-entire-inner",
1170 VIS_HELP("The whole text content, except for leading and trailing empty lines")
1171 textobj, { .i = VIS_TEXTOBJECT_INNER_ENTIRE }
1173 [VIS_ACTION_TEXT_OBJECT_LINE_OUTER] = {
1174 "vis-textobject-line-outer",
1175 VIS_HELP("The whole line")
1176 textobj, { .i = VIS_TEXTOBJECT_OUTER_LINE }
1178 [VIS_ACTION_TEXT_OBJECT_LINE_INNER] = {
1179 "vis-textobject-line-inner",
1180 VIS_HELP("The whole line, excluding leading and trailing whitespace")
1181 textobj, { .i = VIS_TEXTOBJECT_INNER_LINE }
1183 [VIS_ACTION_TEXT_OBJECT_INDENTATION] = {
1184 "vis-textobject-indentation",
1185 VIS_HELP("All adjacent lines with the same indentation level as the current one")
1186 textobj, { .i = VIS_TEXTOBJECT_INDENTATION }
1188 [VIS_ACTION_TEXT_OBJECT_SEARCH_FORWARD] = {
1189 "vis-textobject-search-forward",
1190 VIS_HELP("The next search match in forward direction")
1191 textobj, { .i = VIS_TEXTOBJECT_SEARCH_FORWARD }
1193 [VIS_ACTION_TEXT_OBJECT_SEARCH_BACKWARD] = {
1194 "vis-textobject-search-backward",
1195 VIS_HELP("The next search match in backward direction")
1196 textobj, { .i = VIS_TEXTOBJECT_SEARCH_BACKWARD }
1198 [VIS_ACTION_UNICODE_INFO] = {
1199 "vis-unicode-info",
1200 VIS_HELP("Show Unicode codepoint(s) of character under cursor")
1201 unicode_info, { .i = VIS_ACTION_UNICODE_INFO }
1203 [VIS_ACTION_UTF8_INFO] = {
1204 "vis-utf8-info",
1205 VIS_HELP("Show UTF-8 encoded codepoint(s) of character under cursor")
1206 unicode_info, { .i = VIS_ACTION_UTF8_INFO }
1208 [VIS_ACTION_NOP] = {
1209 "vis-nop",
1210 VIS_HELP("Ignore key, do nothing")
1211 nop,
1215 #include "config.h"
1217 /** key bindings functions */
1219 static const char *nop(Vis *vis, const char *keys, const Arg *arg) {
1220 return keys;
1223 static const char *macro_record(Vis *vis, const char *keys, const Arg *arg) {
1224 if (!vis_macro_record_stop(vis)) {
1225 if (!keys[0])
1226 return NULL;
1227 const char *next = vis_keys_next(vis, keys);
1228 if (next - keys > 1)
1229 return next;
1230 enum VisRegister reg = vis_register_from(vis, keys[0]);
1231 vis_macro_record(vis, reg);
1232 keys++;
1234 vis_draw(vis);
1235 return keys;
1238 static const char *macro_replay(Vis *vis, const char *keys, const Arg *arg) {
1239 if (!keys[0])
1240 return NULL;
1241 const char *next = vis_keys_next(vis, keys);
1242 if (next - keys > 1)
1243 return next;
1244 enum VisRegister reg = vis_register_from(vis, keys[0]);
1245 vis_macro_replay(vis, reg);
1246 return keys+1;
1249 static const char *suspend(Vis *vis, const char *keys, const Arg *arg) {
1250 vis_suspend(vis);
1251 return keys;
1254 static const char *repeat(Vis *vis, const char *keys, const Arg *arg) {
1255 vis_repeat(vis);
1256 return keys;
1259 static const char *selections_new(Vis *vis, const char *keys, const Arg *arg) {
1260 View *view = vis_view(vis);
1261 bool anchored = view_selections_anchored(view_selections_primary_get(view));
1262 VisCountIterator it = vis_count_iterator_get(vis, 1);
1263 while (vis_count_iterator_next(&it)) {
1264 Selection *sel = NULL;
1265 switch (arg->i) {
1266 case -1:
1267 case +1:
1268 sel = view_selections_primary_get(view);
1269 break;
1270 case INT_MIN:
1271 sel = view_selections(view);
1272 break;
1273 case INT_MAX:
1274 for (Selection *s = view_selections(view); s; s = view_selections_next(s))
1275 sel = s;
1276 break;
1279 if (!sel)
1280 return keys;
1282 size_t oldpos = view_cursors_pos(sel);
1283 if (arg->i > 0)
1284 view_line_down(sel);
1285 else if (arg->i < 0)
1286 view_line_up(sel);
1287 size_t newpos = view_cursors_pos(sel);
1288 view_cursors_to(sel, oldpos);
1289 Selection *sel_new = view_selections_new(view, newpos);
1290 if (!sel_new) {
1291 if (arg->i == -1)
1292 sel_new = view_selections_prev(sel);
1293 else if (arg->i == +1)
1294 sel_new = view_selections_next(sel);
1296 if (sel_new) {
1297 view_selections_primary_set(sel_new);
1298 view_selections_anchor(sel_new, anchored);
1301 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1302 return keys;
1305 static const char *selections_align(Vis *vis, const char *keys, const Arg *arg) {
1306 View *view = vis_view(vis);
1307 Text *txt = vis_text(vis);
1308 int mincol = INT_MAX;
1309 for (Selection *s = view_selections(view); s; s = view_selections_next(s)) {
1310 int col = view_cursors_cell_get(s);
1311 if (col >= 0 && col < mincol)
1312 mincol = col;
1314 for (Selection *s = view_selections(view); s; s = view_selections_next(s)) {
1315 if (view_cursors_cell_set(s, mincol) == -1) {
1316 size_t pos = view_cursors_pos(s);
1317 size_t col = text_line_width_set(txt, pos, mincol);
1318 view_cursors_to(s, col);
1321 return keys;
1324 static const char *selections_align_indent(Vis *vis, const char *keys, const Arg *arg) {
1325 View *view = vis_view(vis);
1326 Text *txt = vis_text(vis);
1327 bool left_align = arg->i < 0;
1328 int columns = view_selections_column_count(view);
1330 for (int i = 0; i < columns; i++) {
1331 int mincol = INT_MAX, maxcol = 0;
1332 for (Selection *s = view_selections_column(view, i); s; s = view_selections_column_next(s, i)) {
1333 Filerange sel = view_selections_get(s);
1334 size_t pos = left_align ? sel.start : sel.end;
1335 int col = text_line_width_get(txt, pos);
1336 if (col < mincol)
1337 mincol = col;
1338 if (col > maxcol)
1339 maxcol = col;
1342 size_t len = maxcol - mincol;
1343 char *buf = malloc(len+1);
1344 if (!buf)
1345 return keys;
1346 memset(buf, ' ', len);
1348 for (Selection *s = view_selections_column(view, i); s; s = view_selections_column_next(s, i)) {
1349 Filerange sel = view_selections_get(s);
1350 size_t pos = left_align ? sel.start : sel.end;
1351 size_t ipos = sel.start;
1352 int col = text_line_width_get(txt, pos);
1353 if (col < maxcol) {
1354 size_t off = maxcol - col;
1355 if (off <= len)
1356 text_insert(txt, ipos, buf, off);
1360 free(buf);
1363 view_draw(view);
1364 return keys;
1367 static const char *selections_clear(Vis *vis, const char *keys, const Arg *arg) {
1368 View *view = vis_view(vis);
1369 if (view_selections_count(view) > 1)
1370 view_selections_dispose_all(view);
1371 else
1372 view_selection_clear(view_selections_primary_get(view));
1373 return keys;
1376 static const char *selections_match_word(Vis *vis, const char *keys, const Arg *arg) {
1377 Text *txt = vis_text(vis);
1378 View *view = vis_view(vis);
1379 for (Selection *s = view_selections(view); s; s = view_selections_next(s)) {
1380 Filerange word = text_object_word(txt, view_cursors_pos(s));
1381 if (text_range_valid(&word))
1382 view_selections_set(s, &word);
1384 vis_mode_switch(vis, VIS_MODE_VISUAL);
1385 return keys;
1388 static const Selection *selection_new_primary(View *view, Filerange *r) {
1389 Text *txt = view_text(view);
1390 size_t pos = text_char_prev(txt, r->end);
1391 Selection *s = view_selections_new(view, pos);
1392 if (!s)
1393 return NULL;
1394 view_selections_set(s, r);
1395 view_selections_anchor(s, true);
1396 view_selections_primary_set(s);
1397 return s;
1400 static const char *selections_match_next_literal(Vis *vis, const char *keys, const Arg *arg) {
1401 Text *txt = vis_text(vis);
1402 View *view = vis_view(vis);
1403 Selection *s = view_selections_primary_get(view);
1404 Filerange sel = view_selections_get(s);
1405 size_t len = text_range_size(&sel);
1406 if (!len)
1407 return keys;
1409 char *buf = text_bytes_alloc0(txt, sel.start, len);
1410 if (!buf)
1411 return keys;
1413 size_t start = text_find_next(txt, sel.end, buf);
1414 Filerange match = text_range_new(start, start+len);
1415 if (start != sel.end && selection_new_primary(view, &match))
1416 goto out;
1418 sel = view_selections_get(view_selections(view));
1419 start = text_find_prev(txt, sel.start, buf);
1420 if (start == sel.start)
1421 goto out;
1423 match = text_range_new(start, start+len);
1424 selection_new_primary(view, &match);
1426 out:
1427 free(buf);
1428 return keys;
1431 static const char *selections_match_next(Vis *vis, const char *keys, const Arg *arg) {
1432 Text *txt = vis_text(vis);
1433 View *view = vis_view(vis);
1434 Selection *s = view_selections_primary_get(view);
1435 Filerange sel = view_selections_get(s);
1436 if (!text_range_valid(&sel))
1437 return keys;
1439 Filerange word = text_object_word(txt, view_cursors_pos(s));
1440 if (!text_range_equal(&sel, &word))
1441 return selections_match_next_literal(vis, keys, arg);
1443 char *buf = text_bytes_alloc0(txt, sel.start, text_range_size(&sel));
1444 if (!buf)
1445 return keys;
1447 word = text_object_word_find_next(txt, sel.end, buf);
1448 if (text_range_valid(&word) && selection_new_primary(view, &word))
1449 goto out;
1451 sel = view_selections_get(view_selections(view));
1452 word = text_object_word_find_prev(txt, sel.start, buf);
1453 if (!text_range_valid(&word))
1454 goto out;
1455 selection_new_primary(view, &word);
1457 out:
1458 free(buf);
1459 return keys;
1462 static const char *selections_match_skip(Vis *vis, const char *keys, const Arg *arg) {
1463 View *view = vis_view(vis);
1464 Selection *sel = view_selections_primary_get(view);
1465 keys = selections_match_next(vis, keys, arg);
1466 if (sel != view_selections_primary_get(view))
1467 view_selections_dispose(sel);
1468 return keys;
1471 static const char *selections_remove(Vis *vis, const char *keys, const Arg *arg) {
1472 View *view = vis_view(vis);
1473 view_selections_dispose(view_selections_primary_get(view));
1474 view_cursor_to(view, view_cursor_get(view));
1475 return keys;
1478 static const char *selections_remove_column(Vis *vis, const char *keys, const Arg *arg) {
1479 View *view = vis_view(vis);
1480 int max = view_selections_column_count(view);
1481 int column = vis_count_get_default(vis, arg->i) - 1;
1482 if (column >= max)
1483 column = max - 1;
1484 if (view_selections_count(view) == 1) {
1485 vis_keys_feed(vis, "<Escape>");
1486 return keys;
1489 for (Selection *s = view_selections_column(view, column), *next; s; s = next) {
1490 next = view_selections_column_next(s, column);
1491 view_selections_dispose(s);
1494 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1495 return keys;
1498 static const char *selections_remove_column_except(Vis *vis, const char *keys, const Arg *arg) {
1499 View *view = vis_view(vis);
1500 int max = view_selections_column_count(view);
1501 int column = vis_count_get_default(vis, arg->i) - 1;
1502 if (column >= max)
1503 column = max - 1;
1504 if (view_selections_count(view) == 1) {
1505 vis_redraw(vis);
1506 return keys;
1509 Selection *sel = view_selections(view);
1510 Selection *col = view_selections_column(view, column);
1511 for (Selection *next; sel; sel = next) {
1512 next = view_selections_next(sel);
1513 if (sel == col)
1514 col = view_selections_column_next(col, column);
1515 else
1516 view_selections_dispose(sel);
1519 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1520 return keys;
1523 static const char *selections_navigate(Vis *vis, const char *keys, const Arg *arg) {
1524 View *view = vis_view(vis);
1525 if (view_selections_count(view) == 1)
1526 return wscroll(vis, keys, arg);
1527 Selection *s = view_selections_primary_get(view);
1528 VisCountIterator it = vis_count_iterator_get(vis, 1);
1529 while (vis_count_iterator_next(&it)) {
1530 if (arg->i > 0) {
1531 s = view_selections_next(s);
1532 if (!s)
1533 s = view_selections(view);
1534 } else {
1535 s = view_selections_prev(s);
1536 if (!s) {
1537 s = view_selections(view);
1538 for (Selection *n = s; n; n = view_selections_next(n))
1539 s = n;
1543 view_selections_primary_set(s);
1544 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1545 return keys;
1548 static const char *selections_rotate(Vis *vis, const char *keys, const Arg *arg) {
1550 typedef struct {
1551 Selection *sel;
1552 char *data;
1553 size_t len;
1554 } Rotate;
1556 Array arr;
1557 Text *txt = vis_text(vis);
1558 View *view = vis_view(vis);
1559 int columns = view_selections_column_count(view);
1560 int selections = columns == 1 ? view_selections_count(view) : columns;
1561 int count = vis_count_get_default(vis, 1);
1562 array_init_sized(&arr, sizeof(Rotate));
1563 if (!array_reserve(&arr, selections))
1564 return keys;
1565 size_t line = 0;
1567 for (Selection *s = view_selections(view), *next; s; s = next) {
1568 next = view_selections_next(s);
1569 size_t line_next = 0;
1571 Filerange sel = view_selections_get(s);
1572 Rotate rot;
1573 rot.sel = s;
1574 rot.len = text_range_size(&sel);
1575 if ((rot.data = malloc(rot.len)))
1576 rot.len = text_bytes_get(txt, sel.start, rot.len, rot.data);
1577 else
1578 rot.len = 0;
1579 array_add(&arr, &rot);
1581 if (!line)
1582 line = text_lineno_by_pos(txt, view_cursors_pos(s));
1583 if (next)
1584 line_next = text_lineno_by_pos(txt, view_cursors_pos(next));
1585 if (!next || (columns > 1 && line != line_next)) {
1586 size_t len = array_length(&arr);
1587 size_t off = arg->i > 0 ? count % len : len - (count % len);
1588 for (size_t i = 0; i < len; i++) {
1589 size_t j = (i + off) % len;
1590 Rotate *oldrot = array_get(&arr, i);
1591 Rotate *newrot = array_get(&arr, j);
1592 if (!oldrot || !newrot || oldrot == newrot)
1593 continue;
1594 Filerange newsel = view_selections_get(newrot->sel);
1595 if (!text_range_valid(&newsel))
1596 continue;
1597 if (!text_delete_range(txt, &newsel))
1598 continue;
1599 if (!text_insert(txt, newsel.start, oldrot->data, oldrot->len))
1600 continue;
1601 newsel.end = newsel.start + oldrot->len;
1602 view_selections_set(newrot->sel, &newsel);
1603 free(oldrot->data);
1605 array_clear(&arr);
1607 line = line_next;
1610 array_release(&arr);
1611 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1612 return keys;
1615 static const char *selections_trim(Vis *vis, const char *keys, const Arg *arg) {
1616 Text *txt = vis_text(vis);
1617 View *view = vis_view(vis);
1618 for (Selection *s = view_selections(view), *next; s; s = next) {
1619 next = view_selections_next(s);
1620 Filerange sel = view_selections_get(s);
1621 if (!text_range_valid(&sel))
1622 continue;
1623 for (char b; sel.start < sel.end && text_byte_get(txt, sel.end-1, &b)
1624 && isspace((unsigned char)b); sel.end--);
1625 for (char b; sel.start <= sel.end && text_byte_get(txt, sel.start, &b)
1626 && isspace((unsigned char)b); sel.start++);
1627 if (sel.start < sel.end) {
1628 view_selections_set(s, &sel);
1629 } else if (!view_selections_dispose(s)) {
1630 vis_mode_switch(vis, VIS_MODE_NORMAL);
1633 return keys;
1636 static void selections_set(Vis *vis, View *view, Array *sel) {
1637 enum VisMode mode = vis_mode_get(vis);
1638 bool anchored = mode == VIS_MODE_VISUAL || mode == VIS_MODE_VISUAL_LINE;
1639 view_selections_set_all(view, sel, anchored);
1640 if (!anchored)
1641 view_selections_clear_all(view);
1644 static const char *selections_save(Vis *vis, const char *keys, const Arg *arg) {
1645 Win *win = vis_window(vis);
1646 View *view = vis_view(vis);
1647 enum VisMark mark = vis_mark_used(vis);
1648 Array sel = view_selections_get_all(view);
1649 vis_mark_set(win, mark, &sel);
1650 array_release(&sel);
1651 vis_cancel(vis);
1652 return keys;
1655 static const char *selections_restore(Vis *vis, const char *keys, const Arg *arg) {
1656 Win *win = vis_window(vis);
1657 View *view = vis_view(vis);
1658 enum VisMark mark = vis_mark_used(vis);
1659 Array sel = vis_mark_get(win, mark);
1660 selections_set(vis, view, &sel);
1661 array_release(&sel);
1662 vis_cancel(vis);
1663 return keys;
1666 static const char *selections_union(Vis *vis, const char *keys, const Arg *arg) {
1667 Win *win = vis_window(vis);
1668 View *view = vis_view(vis);
1669 enum VisMark mark = vis_mark_used(vis);
1670 Array a = vis_mark_get(win, mark);
1671 Array b = view_selections_get_all(view);
1672 Array sel;
1673 array_init_from(&sel, &a);
1675 size_t i = 0, j = 0;
1676 Filerange *r1 = array_get(&a, i), *r2 = array_get(&b, j), cur = text_range_empty();
1677 while (r1 || r2) {
1678 if (r1 && text_range_overlap(r1, &cur)) {
1679 cur = text_range_union(r1, &cur);
1680 r1 = array_get(&a, ++i);
1681 } else if (r2 && text_range_overlap(r2, &cur)) {
1682 cur = text_range_union(r2, &cur);
1683 r2 = array_get(&b, ++j);
1684 } else {
1685 if (text_range_valid(&cur))
1686 array_add(&sel, &cur);
1687 if (!r1) {
1688 cur = *r2;
1689 r2 = array_get(&b, ++j);
1690 } else if (!r2) {
1691 cur = *r1;
1692 r1 = array_get(&a, ++i);
1693 } else {
1694 if (r1->start < r2->start) {
1695 cur = *r1;
1696 r1 = array_get(&a, ++i);
1697 } else {
1698 cur = *r2;
1699 r2 = array_get(&b, ++j);
1705 if (text_range_valid(&cur))
1706 array_add(&sel, &cur);
1708 selections_set(vis, view, &sel);
1709 vis_cancel(vis);
1711 array_release(&a);
1712 array_release(&b);
1713 array_release(&sel);
1715 return keys;
1718 static void intersect(Array *ret, Array *a, Array *b) {
1719 size_t i = 0, j = 0;
1720 Filerange *r1 = array_get(a, i), *r2 = array_get(b, j);
1721 while (r1 && r2) {
1722 if (text_range_overlap(r1, r2)) {
1723 Filerange new = text_range_intersect(r1, r2);
1724 array_add(ret, &new);
1726 if (r1->end < r2->end)
1727 r1 = array_get(a, ++i);
1728 else
1729 r2 = array_get(b, ++j);
1733 static const char *selections_intersect(Vis *vis, const char *keys, const Arg *arg) {
1734 Win *win = vis_window(vis);
1735 View *view = vis_view(vis);
1736 enum VisMark mark = vis_mark_used(vis);
1737 Array a = vis_mark_get(win, mark);
1738 Array b = view_selections_get_all(view);
1739 Array sel;
1740 array_init_from(&sel, &a);
1742 intersect(&sel, &a, &b);
1743 selections_set(vis, view, &sel);
1744 vis_cancel(vis);
1746 array_release(&a);
1747 array_release(&b);
1748 array_release(&sel);
1750 return keys;
1753 static void complement(Array *ret, Array *a, Filerange *universe) {
1754 size_t pos = universe->start;
1755 for (size_t i = 0, len = array_length(a); i < len; i++) {
1756 Filerange *r = array_get(a, i);
1757 if (pos < r->start) {
1758 Filerange new = text_range_new(pos, r->start);
1759 array_add(ret, &new);
1761 pos = r->end;
1763 if (pos < universe->end) {
1764 Filerange new = text_range_new(pos, universe->end);
1765 array_add(ret, &new);
1769 static const char *selections_complement(Vis *vis, const char *keys, const Arg *arg) {
1770 Text *txt = vis_text(vis);
1771 View *view = vis_view(vis);
1772 Filerange universe = text_object_entire(txt, 0);
1773 Array a = view_selections_get_all(view);
1774 Array sel;
1775 array_init_from(&sel, &a);
1777 complement(&sel, &a, &universe);
1779 selections_set(vis, view, &sel);
1780 array_release(&a);
1781 array_release(&sel);
1782 return keys;
1785 static const char *selections_minus(Vis *vis, const char *keys, const Arg *arg) {
1786 Text *txt = vis_text(vis);
1787 Win *win = vis_window(vis);
1788 View *view = vis_view(vis);
1789 enum VisMark mark = vis_mark_used(vis);
1790 Array a = view_selections_get_all(view);
1791 Array b = vis_mark_get(win, mark);
1792 Array sel;
1793 array_init_from(&sel, &a);
1794 Array b_complement;
1795 array_init_from(&b_complement, &b);
1797 Filerange universe = text_object_entire(txt, 0);
1798 complement(&b_complement, &b, &universe);
1799 intersect(&sel, &a, &b_complement);
1801 selections_set(vis, view, &sel);
1802 vis_cancel(vis);
1804 array_release(&a);
1805 array_release(&b);
1806 array_release(&b_complement);
1807 array_release(&sel);
1809 return keys;
1812 static Filerange combine_union(const Filerange *r1, const Filerange *r2) {
1813 if (!r1)
1814 return *r2;
1815 if (!r2)
1816 return *r1;
1817 return text_range_union(r1, r2);
1820 static Filerange combine_intersect(const Filerange *r1, const Filerange *r2) {
1821 if (!r1 || !r2)
1822 return text_range_empty();
1823 return text_range_intersect(r1, r2);
1826 static Filerange combine_longer(const Filerange *r1, const Filerange *r2) {
1827 if (!r1)
1828 return *r2;
1829 if (!r2)
1830 return *r1;
1831 size_t l1 = text_range_size(r1);
1832 size_t l2 = text_range_size(r2);
1833 return l1 < l2 ? *r2 : *r1;
1836 static Filerange combine_shorter(const Filerange *r1, const Filerange *r2) {
1837 if (!r1)
1838 return *r2;
1839 if (!r2)
1840 return *r1;
1841 size_t l1 = text_range_size(r1);
1842 size_t l2 = text_range_size(r2);
1843 return l1 < l2 ? *r1 : *r2;
1846 static Filerange combine_leftmost(const Filerange *r1, const Filerange *r2) {
1847 if (!r1)
1848 return *r2;
1849 if (!r2)
1850 return *r1;
1851 return r1->start < r2->start || (r1->start == r2->start && r1->end < r2->end) ? *r1 : *r2;
1854 static Filerange combine_rightmost(const Filerange *r1, const Filerange *r2) {
1855 if (!r1)
1856 return *r2;
1857 if (!r2)
1858 return *r1;
1859 return r1->start < r2->start || (r1->start == r2->start && r1->end < r2->end) ? *r2 : *r1;
1862 static const char *selections_combine(Vis *vis, const char *keys, const Arg *arg) {
1863 Win *win = vis_window(vis);
1864 View *view = vis_view(vis);
1865 enum VisMark mark = vis_mark_used(vis);
1866 Array a = view_selections_get_all(view);
1867 Array b = vis_mark_get(win, mark);
1868 Array sel;
1869 array_init_from(&sel, &a);
1871 Filerange *r1 = array_get(&a, 0), *r2 = array_get(&b, 0);
1872 for (size_t i = 0, j = 0; r1 || r2; r1 = array_get(&a, ++i), r2 = array_get(&b, ++j)) {
1873 Filerange new = arg->combine(r1, r2);
1874 if (text_range_valid(&new))
1875 array_add(&sel, &new);
1878 vis_mark_normalize(&sel);
1879 selections_set(vis, view, &sel);
1880 vis_cancel(vis);
1882 array_release(&a);
1883 array_release(&b);
1884 array_release(&sel);
1886 return keys;
1889 static const char *replace(Vis *vis, const char *keys, const Arg *arg) {
1890 if (!keys[0]) {
1891 vis_keymap_disable(vis);
1892 return NULL;
1895 const char *next = vis_keys_next(vis, keys);
1896 if (!next)
1897 return NULL;
1899 char replacement[UTFmax+1];
1900 if (!vis_keys_utf8(vis, keys, replacement))
1901 return next;
1903 if (replacement[0] == 0x1b) /* <Escape> */
1904 return next;
1906 vis_operator(vis, VIS_OP_REPLACE, replacement);
1907 if (vis_mode_get(vis) == VIS_MODE_OPERATOR_PENDING)
1908 vis_motion(vis, VIS_MOVE_CHAR_NEXT);
1909 return next;
1912 static const char *count(Vis *vis, const char *keys, const Arg *arg) {
1913 int digit = keys[-1] - '0';
1914 int count = vis_count_get_default(vis, 0);
1915 if (0 <= digit && digit <= 9) {
1916 if (digit == 0 && count == 0)
1917 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
1918 else
1919 vis_count_set(vis, count * 10 + digit);
1921 return keys;
1924 static const char *gotoline(Vis *vis, const char *keys, const Arg *arg) {
1925 if (vis_count_get(vis) != VIS_COUNT_UNKNOWN)
1926 vis_motion(vis, VIS_MOVE_LINE);
1927 else if (arg->i < 0)
1928 vis_motion(vis, VIS_MOVE_FILE_BEGIN);
1929 else
1930 vis_motion(vis, VIS_MOVE_FILE_END);
1931 return keys;
1934 static const char *operator(Vis *vis, const char *keys, const Arg *arg) {
1935 vis_operator(vis, arg->i);
1936 return keys;
1939 static const char *movement_key(Vis *vis, const char *keys, const Arg *arg) {
1940 if (!keys[0]) {
1941 vis_keymap_disable(vis);
1942 return NULL;
1945 const char *next = vis_keys_next(vis, keys);
1946 if (!next)
1947 return NULL;
1948 char utf8[UTFmax+1];
1949 if (vis_keys_utf8(vis, keys, utf8))
1950 vis_motion(vis, arg->i, utf8);
1951 return next;
1954 static const char *movement(Vis *vis, const char *keys, const Arg *arg) {
1955 vis_motion(vis, arg->i);
1956 return keys;
1959 static const char *textobj(Vis *vis, const char *keys, const Arg *arg) {
1960 vis_textobject(vis, arg->i);
1961 return keys;
1964 static const char *selection_end(Vis *vis, const char *keys, const Arg *arg) {
1965 for (Selection *s = view_selections(vis_view(vis)); s; s = view_selections_next(s))
1966 view_selections_flip(s);
1967 return keys;
1970 static const char *reg(Vis *vis, const char *keys, const Arg *arg) {
1971 if (!keys[0])
1972 return NULL;
1973 const char *next = vis_keys_next(vis, keys);
1974 if (next - keys > 1)
1975 return next;
1976 enum VisRegister reg = vis_register_from(vis, keys[0]);
1977 vis_register(vis, reg);
1978 return keys+1;
1981 static const char *mark(Vis *vis, const char *keys, const Arg *arg) {
1982 if (!keys[0])
1983 return NULL;
1984 const char *next = vis_keys_next(vis, keys);
1985 if (next - keys > 1)
1986 return next;
1987 enum VisMark mark = vis_mark_from(vis, keys[0]);
1988 vis_mark(vis, mark);
1989 return keys+1;
1992 static const char *undo(Vis *vis, const char *keys, const Arg *arg) {
1993 size_t pos = text_undo(vis_text(vis));
1994 if (pos != EPOS) {
1995 View *view = vis_view(vis);
1996 if (view_selections_count(view) == 1)
1997 view_cursor_to(view, pos);
1998 /* redraw all windows in case some display the same file */
1999 vis_draw(vis);
2001 return keys;
2004 static const char *redo(Vis *vis, const char *keys, const Arg *arg) {
2005 size_t pos = text_redo(vis_text(vis));
2006 if (pos != EPOS) {
2007 View *view = vis_view(vis);
2008 if (view_selections_count(view) == 1)
2009 view_cursor_to(view, pos);
2010 /* redraw all windows in case some display the same file */
2011 vis_draw(vis);
2013 return keys;
2016 static const char *earlier(Vis *vis, const char *keys, const Arg *arg) {
2017 size_t pos = EPOS;
2018 VisCountIterator it = vis_count_iterator_get(vis, 1);
2019 while (vis_count_iterator_next(&it))
2020 pos = text_earlier(vis_text(vis));
2021 if (pos != EPOS) {
2022 view_cursor_to(vis_view(vis), pos);
2023 /* redraw all windows in case some display the same file */
2024 vis_draw(vis);
2026 return keys;
2029 static const char *later(Vis *vis, const char *keys, const Arg *arg) {
2030 size_t pos = EPOS;
2031 VisCountIterator it = vis_count_iterator_get(vis, 1);
2032 while (vis_count_iterator_next(&it))
2033 pos = text_later(vis_text(vis));
2034 if (pos != EPOS) {
2035 view_cursor_to(vis_view(vis), pos);
2036 /* redraw all windows in case some display the same file */
2037 vis_draw(vis);
2039 return keys;
2042 static const char *delete(Vis *vis, const char *keys, const Arg *arg) {
2043 vis_operator(vis, VIS_OP_DELETE);
2044 vis_motion(vis, arg->i);
2045 return keys;
2048 static const char *insert_register(Vis *vis, const char *keys, const Arg *arg) {
2049 if (!keys[0])
2050 return NULL;
2051 const char *next = vis_keys_next(vis, keys);
2052 if (next - keys > 1)
2053 return next;
2054 enum VisRegister reg = vis_register_from(vis, keys[0]);
2055 if (reg != VIS_REG_INVALID) {
2056 vis_register(vis, reg);
2057 vis_operator(vis, VIS_OP_PUT_BEFORE_END);
2059 return keys+1;
2062 static const char *prompt_show(Vis *vis, const char *keys, const Arg *arg) {
2063 vis_prompt_show(vis, arg->s);
2064 return keys;
2067 static const char *insert_verbatim(Vis *vis, const char *keys, const Arg *arg) {
2068 Rune rune = 0;
2069 char buf[4], type = keys[0];
2070 const char *data = NULL;
2071 int len = 0, count = 0, base = 0;
2072 switch (type) {
2073 case '\0':
2074 return NULL;
2075 case 'o':
2076 case 'O':
2077 count = 3;
2078 base = 8;
2079 break;
2080 case 'U':
2081 count = 4;
2082 /* fall through */
2083 case 'u':
2084 count += 4;
2085 base = 16;
2086 break;
2087 case 'x':
2088 case 'X':
2089 count = 2;
2090 base = 16;
2091 break;
2092 default:
2093 if ('0' <= type && type <= '9') {
2094 rune = type - '0';
2095 count = 2;
2096 base = 10;
2098 break;
2101 if (base) {
2102 for (keys++; keys[0] && count > 0; keys++, count--) {
2103 int v = 0;
2104 if (base == 8 && '0' <= keys[0] && keys[0] <= '7') {
2105 v = keys[0] - '0';
2106 } else if ((base == 10 || base == 16) && '0' <= keys[0] && keys[0] <= '9') {
2107 v = keys[0] - '0';
2108 } else if (base == 16 && 'a' <= keys[0] && keys[0] <= 'f') {
2109 v = 10 + keys[0] - 'a';
2110 } else if (base == 16 && 'A' <= keys[0] && keys[0] <= 'F') {
2111 v = 10 + keys[0] - 'A';
2112 } else {
2113 count = 0;
2114 break;
2116 rune = rune * base + v;
2119 if (count > 0)
2120 return NULL;
2121 if (type == 'u' || type == 'U') {
2122 len = runetochar(buf, &rune);
2123 } else {
2124 buf[0] = rune;
2125 len = 1;
2128 data = buf;
2129 } else {
2130 const char *next = vis_keys_next(vis, keys);
2131 if (!next)
2132 return NULL;
2133 if ((rune = vis_keys_codepoint(vis, keys)) != (Rune)-1) {
2134 len = runetochar(buf, &rune);
2135 if (buf[0] == '\n')
2136 buf[0] = '\r';
2137 data = buf;
2138 } else {
2139 vis_info_show(vis, "Unknown key");
2141 keys = next;
2144 if (len > 0)
2145 vis_insert_key(vis, data, len);
2146 return keys;
2149 static const char *wscroll(Vis *vis, const char *keys, const Arg *arg) {
2150 View *view = vis_view(vis);
2151 int count = vis_count_get(vis);
2152 switch (arg->i) {
2153 case -PAGE:
2154 view_scroll_page_up(view);
2155 break;
2156 case +PAGE:
2157 view_scroll_page_down(view);
2158 break;
2159 case -PAGE_HALF:
2160 view_scroll_halfpage_up(view);
2161 break;
2162 case +PAGE_HALF:
2163 view_scroll_halfpage_down(view);
2164 break;
2165 default:
2166 if (count == VIS_COUNT_UNKNOWN)
2167 count = arg->i < 0 ? -arg->i : arg->i;
2168 if (arg->i < 0)
2169 view_scroll_up(view, count);
2170 else
2171 view_scroll_down(view, count);
2172 break;
2174 vis_count_set(vis, VIS_COUNT_UNKNOWN);
2175 return keys;
2178 static const char *wslide(Vis *vis, const char *keys, const Arg *arg) {
2179 View *view = vis_view(vis);
2180 int count = vis_count_get(vis);
2181 if (count == VIS_COUNT_UNKNOWN)
2182 count = arg->i < 0 ? -arg->i : arg->i;
2183 if (arg->i >= 0)
2184 view_slide_down(view, count);
2185 else
2186 view_slide_up(view, count);
2187 vis_count_set(vis, VIS_COUNT_UNKNOWN);
2188 return keys;
2191 static const char *call(Vis *vis, const char *keys, const Arg *arg) {
2192 arg->f(vis);
2193 return keys;
2196 static const char *window(Vis *vis, const char *keys, const Arg *arg) {
2197 arg->w(vis_view(vis));
2198 return keys;
2201 static const char *openline(Vis *vis, const char *keys, const Arg *arg) {
2202 vis_operator(vis, VIS_OP_MODESWITCH, VIS_MODE_INSERT);
2203 if (arg->i > 0) {
2204 vis_motion(vis, VIS_MOVE_LINE_END);
2205 vis_keys_feed(vis, "<Enter>");
2206 } else {
2207 if (vis_get_autoindent(vis)) {
2208 vis_motion(vis, VIS_MOVE_LINE_START);
2209 vis_keys_feed(vis, "<vis-motion-line-start>");
2210 } else {
2211 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
2212 vis_keys_feed(vis, "<vis-motion-line-begin>");
2214 vis_keys_feed(vis, "<Enter><Up>");
2216 return keys;
2219 static const char *join(Vis *vis, const char *keys, const Arg *arg) {
2220 bool normal = (vis_mode_get(vis) == VIS_MODE_NORMAL);
2221 vis_operator(vis, VIS_OP_JOIN, arg->s);
2222 if (normal) {
2223 int count = vis_count_get_default(vis, 0);
2224 if (count)
2225 vis_count_set(vis, count-1);
2226 vis_motion(vis, VIS_MOVE_LINE_NEXT);
2228 return keys;
2231 static const char *switchmode(Vis *vis, const char *keys, const Arg *arg) {
2232 vis_mode_switch(vis, arg->i);
2233 return keys;
2236 static const char *insertmode(Vis *vis, const char *keys, const Arg *arg) {
2237 vis_operator(vis, VIS_OP_MODESWITCH, VIS_MODE_INSERT);
2238 vis_motion(vis, arg->i);
2239 return keys;
2242 static const char *replacemode(Vis *vis, const char *keys, const Arg *arg) {
2243 vis_operator(vis, VIS_OP_MODESWITCH, VIS_MODE_REPLACE);
2244 vis_motion(vis, arg->i);
2245 return keys;
2248 static const char *unicode_info(Vis *vis, const char *keys, const Arg *arg) {
2249 View *view = vis_view(vis);
2250 Text *txt = vis_text(vis);
2251 size_t start = view_cursor_get(view);
2252 size_t end = text_char_next(txt, start);
2253 char *grapheme = text_bytes_alloc0(txt, start, end-start), *codepoint = grapheme;
2254 if (!grapheme)
2255 return keys;
2256 Buffer info;
2257 buffer_init(&info);
2258 mbstate_t ps = { 0 };
2259 Iterator it = text_iterator_get(txt, start);
2260 for (size_t pos = start; it.pos < end; pos = it.pos) {
2261 if (!text_iterator_codepoint_next(&it, NULL)) {
2262 vis_info_show(vis, "Failed to parse code point");
2263 goto err;
2265 size_t len = it.pos - pos;
2266 wchar_t wc = 0xFFFD;
2267 size_t res = mbrtowc(&wc, codepoint, len, &ps);
2268 bool combining = false;
2269 if (res != (size_t)-1 && res != (size_t)-2)
2270 combining = (wc != L'\0' && wcwidth(wc) == 0);
2271 unsigned char ch = *codepoint;
2272 if (ch < 128 && !isprint(ch))
2273 buffer_appendf(&info, "<^%c> ", ch == 127 ? '?' : ch + 64);
2274 else
2275 buffer_appendf(&info, "<%s%.*s> ", combining ? " " : "", (int)len, codepoint);
2276 if (arg->i == VIS_ACTION_UNICODE_INFO) {
2277 buffer_appendf(&info, "U+%04"PRIX32" ", (uint32_t)wc);
2278 } else {
2279 for (size_t i = 0; i < len; i++)
2280 buffer_appendf(&info, "%02x ", (uint8_t)codepoint[i]);
2282 codepoint += len;
2284 vis_info_show(vis, "%s", buffer_content0(&info));
2285 err:
2286 free(grapheme);
2287 buffer_release(&info);
2288 return keys;
2291 static const char *percent(Vis *vis, const char *keys, const Arg *arg) {
2292 if (vis_count_get(vis) == VIS_COUNT_UNKNOWN)
2293 vis_motion(vis, VIS_MOVE_BRACKET_MATCH);
2294 else
2295 vis_motion(vis, VIS_MOVE_PERCENT);
2296 return keys;
2299 static const char *jumplist(Vis *vis, const char *keys, const Arg *arg) {
2300 if (arg->i < 0)
2301 vis_jumplist_prev(vis);
2302 else if (arg->i > 0)
2303 vis_jumplist_next(vis);
2304 else
2305 vis_jumplist_save(vis);
2306 return keys;
2309 static Vis *vis;
2311 static void signal_handler(int signum, siginfo_t *siginfo, void *context) {
2312 vis_signal_handler(vis, signum, siginfo, context);
2315 int main(int argc, char *argv[]) {
2317 VisEvent event = {
2318 .init = vis_lua_init,
2319 .start = vis_lua_start,
2320 .quit = vis_lua_quit,
2321 .mode_insert_input = vis_lua_mode_insert_input,
2322 .mode_replace_input = vis_lua_mode_replace_input,
2323 .file_open = vis_lua_file_open,
2324 .file_save_pre = vis_lua_file_save_pre,
2325 .file_save_post = vis_lua_file_save_post,
2326 .file_close = vis_lua_file_close,
2327 .win_open = vis_lua_win_open,
2328 .win_close = vis_lua_win_close,
2329 .win_highlight = vis_lua_win_highlight,
2330 .win_status = vis_lua_win_status,
2333 vis = vis_new(ui_term_new(), &event);
2334 if (!vis)
2335 return EXIT_FAILURE;
2337 for (int i = 0; i < LENGTH(vis_action); i++) {
2338 const KeyAction *action = &vis_action[i];
2339 if (!vis_action_register(vis, action))
2340 vis_die(vis, "Could not register action: %s\n", action->name);
2343 for (int i = 0; i < LENGTH(default_bindings); i++) {
2344 for (const KeyBinding **binding = default_bindings[i]; binding && *binding; binding++) {
2345 for (const KeyBinding *kb = *binding; kb->key; kb++) {
2346 vis_mode_map(vis, i, false, kb->key, kb);
2351 for (const char **k = keymaps; k[0]; k += 2)
2352 vis_keymap_add(vis, k[0], k[1]);
2354 /* install signal handlers etc. */
2355 struct sigaction sa;
2356 memset(&sa, 0, sizeof sa);
2357 sigfillset(&sa.sa_mask);
2358 sa.sa_flags = SA_SIGINFO;
2359 sa.sa_sigaction = signal_handler;
2360 if (sigaction(SIGBUS, &sa, NULL) == -1 ||
2361 sigaction(SIGINT, &sa, NULL) == -1 ||
2362 sigaction(SIGCONT, &sa, NULL) == -1 ||
2363 sigaction(SIGWINCH, &sa, NULL) == -1 ||
2364 sigaction(SIGTERM, &sa, NULL) == -1 ||
2365 sigaction(SIGHUP, &sa, NULL) == -1) {
2366 vis_die(vis, "Failed to set signal handler: %s\n", strerror(errno));
2369 sa.sa_handler = SIG_IGN;
2370 if (sigaction(SIGPIPE, &sa, NULL) == -1 || sigaction(SIGQUIT, &sa, NULL) == -1)
2371 vis_die(vis, "Failed to ignore signals\n");
2373 sigset_t blockset;
2374 sigemptyset(&blockset);
2375 sigaddset(&blockset, SIGBUS);
2376 sigaddset(&blockset, SIGCONT);
2377 sigaddset(&blockset, SIGWINCH);
2378 sigaddset(&blockset, SIGTERM);
2379 sigaddset(&blockset, SIGHUP);
2380 if (sigprocmask(SIG_BLOCK, &blockset, NULL) == -1)
2381 vis_die(vis, "Failed to block signals\n");
2383 for (int i = 1; i < argc; i++) {
2384 if (argv[i][0] != '-') {
2385 continue;
2386 } else if (strcmp(argv[i], "-") == 0) {
2387 continue;
2388 } else if (strcmp(argv[i], "--") == 0) {
2389 break;
2390 } else if (strcmp(argv[i], "-v") == 0) {
2391 printf("vis %s%s%s%s%s%s%s\n", VERSION,
2392 CONFIG_CURSES ? " +curses" : "",
2393 CONFIG_LUA ? " +lua" : "",
2394 CONFIG_LPEG ? " +lpeg" : "",
2395 CONFIG_TRE ? " +tre" : "",
2396 CONFIG_ACL ? " +acl" : "",
2397 CONFIG_SELINUX ? " +selinux" : "");
2398 return 0;
2399 } else {
2400 fprintf(stderr, "Unknown command option: %s\n", argv[i]);
2401 return 1;
2405 char *cmd = NULL;
2406 bool end_of_options = false, win_created = false;
2408 for (int i = 1; i < argc; i++) {
2409 if (argv[i][0] == '-' && !end_of_options) {
2410 if (strcmp(argv[i], "-") == 0) {
2411 if (!vis_window_new_fd(vis, STDOUT_FILENO))
2412 vis_die(vis, "Can not create empty buffer\n");
2413 ssize_t len = 0;
2414 char buf[PIPE_BUF];
2415 Text *txt = vis_text(vis);
2416 while ((len = read(STDIN_FILENO, buf, sizeof buf)) > 0)
2417 text_insert(txt, text_size(txt), buf, len);
2418 if (len == -1)
2419 vis_die(vis, "Can not read from stdin\n");
2420 text_snapshot(txt);
2421 int fd = open("/dev/tty", O_RDWR);
2422 if (fd == -1)
2423 vis_die(vis, "Can not reopen stdin\n");
2424 dup2(fd, STDIN_FILENO);
2425 close(fd);
2426 } else if (strcmp(argv[i], "--") == 0) {
2427 end_of_options = true;
2428 continue;
2430 } else if (argv[i][0] == '+' && !end_of_options) {
2431 cmd = argv[i] + (argv[i][1] == '/' || argv[i][1] == '?');
2432 continue;
2433 } else if (!vis_window_new(vis, argv[i])) {
2434 vis_die(vis, "Can not load `%s': %s\n", argv[i], strerror(errno));
2436 win_created = true;
2437 if (cmd) {
2438 vis_prompt_cmd(vis, cmd);
2439 cmd = NULL;
2443 if (!vis_window(vis) && !win_created) {
2444 if (!vis_window_new(vis, NULL))
2445 vis_die(vis, "Can not create empty buffer\n");
2446 if (cmd)
2447 vis_prompt_cmd(vis, cmd);
2450 int status = vis_run(vis);
2451 vis_free(vis);
2452 return status;