vis
a vi-like editor based on Plan 9's structural regular expressions
git clone https://9o.is/git/vis.git
main.c
(71802B)
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>
12
13 #include "ui.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"
23
24 #define PAGE INT_MAX
25 #define PAGE_HALF (INT_MAX-1)
26
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 /* reset count if set, otherwise remove all but the primary selection */
36 static const char *normalmode_escape(Vis*, const char *keys, const Arg *arg);
37 /* reset count if set, otherwise switch to normal mode */
38 static const char *visualmode_escape(Vis*, const char *keys, const Arg *arg);
39 /* switch to mode indicated by arg->i */
40 static const char *switchmode(Vis*, const char *keys, const Arg *arg);
41 /* switch to insert mode after performing movement indicated by arg->i */
42 static const char *insertmode(Vis*, const char *keys, const Arg *arg);
43 /* switch to replace mode after performing movement indicated by arg->i */
44 static const char *replacemode(Vis*, const char *keys, const Arg *arg);
45 /* add a new line either before or after the one where the cursor currently is */
46 static const char *openline(Vis*, const char *keys, const Arg *arg);
47 /* join lines from current cursor position to movement indicated by arg */
48 static const char *join(Vis*, const char *keys, const Arg *arg);
49 /* perform last action i.e. action_prev again */
50 static const char *repeat(Vis*, const char *keys, const Arg *arg);
51 /* replace character at cursor with one from keys */
52 static const char *replace(Vis*, const char *keys, const Arg *arg);
53 /* create a new cursor on the previous (arg->i < 0) or next (arg->i > 0) line */
54 static const char *selections_new(Vis*, const char *keys, const Arg *arg);
55 /* try to align all selections on the same column */
56 static const char *selections_align(Vis*, const char *keys, const Arg *arg);
57 /* try to align all selections by inserting the correct amount of white spaces */
58 static const char *selections_align_indent(Vis*, const char *keys, const Arg *arg);
59 /* remove all but the primary cursor and their selections */
60 static const char *selections_clear(Vis*, const char *keys, const Arg *arg);
61 /* remove the least recently added selection */
62 static const char *selections_remove(Vis*, const char *keys, const Arg *arg);
63 /* remove count (or arg->i)-th selection column */
64 static const char *selections_remove_column(Vis*, const char *keys, const Arg *arg);
65 /* remove all but the count (or arg->i)-th selection column */
66 static const char *selections_remove_column_except(Vis*, const char *keys, const Arg *arg);
67 /* move to the previous (arg->i < 0) or next (arg->i > 0) selection */
68 static const char *selections_navigate(Vis*, const char *keys, const Arg *arg);
69 /* select the next region matching the current selection */
70 static const char *selections_match_next(Vis*, const char *keys, const Arg *arg);
71 /* clear current selection but select next match */
72 static const char *selections_match_skip(Vis*, const char *keys, const Arg *arg);
73 /* rotate selection content count times left (arg->i < 0) or right (arg->i > 0) */
74 static const char *selections_rotate(Vis*, const char *keys, const Arg *arg);
75 /* remove leading and trailing white spaces from selections */
76 static const char *selections_trim(Vis*, const char *keys, const Arg *arg);
77 /* save active selections to mark */
78 static const char *selections_save(Vis*, const char *keys, const Arg *arg);
79 /* restore selections from mark */
80 static const char *selections_restore(Vis*, const char *keys, const Arg *arg);
81 /* union selections from mark */
82 static const char *selections_union(Vis*, const char *keys, const Arg *arg);
83 /* intersect selections from mark */
84 static const char *selections_intersect(Vis*, const char *keys, const Arg *arg);
85 /* perform complement of current active selections */
86 static const char *selections_complement(Vis*, const char *keys, const Arg *arg);
87 /* subtract selections from mark */
88 static const char *selections_minus(Vis*, const char *keys, const Arg *arg);
89 /* adjust current used count according to keys */
90 static const char *count(Vis*, const char *keys, const Arg *arg);
91 /* move to the count-th line or if not given either to the first (arg->i < 0)
92 * or last (arg->i > 0) line of file */
93 static const char *gotoline(Vis*, const char *keys, const Arg *arg);
94 /* make the current action use the operator indicated by arg->i */
95 static const char *operator(Vis*, const char *keys, const Arg *arg);
96 /* blocks to read a key and performs movement indicated by arg->i which
97 * should be one of VIS_MOVE_{TO,TILL}_{,LINE}_{RIGHT,LEFT}*/
98 static const char *movement_key(Vis*, const char *keys, const Arg *arg);
99 /* perform the movement as indicated by arg->i */
100 static const char *movement(Vis*, const char *keys, const Arg *arg);
101 /* let the current operator affect the range indicated by the text object arg->i */
102 static const char *textobj(Vis*, const char *keys, const Arg *arg);
103 /* move to the other end of selected text */
104 static const char *selection_end(Vis*, const char *keys, const Arg *arg);
105 /* use register indicated by keys for the current operator */
106 static const char *reg(Vis*, const char *keys, const Arg *arg);
107 /* use mark indicated by keys for the current action */
108 static const char *mark(Vis*, const char *keys, const Arg *arg);
109 /* {un,re}do last action, redraw window */
110 static const char *undo(Vis*, const char *keys, const Arg *arg);
111 static const char *redo(Vis*, const char *keys, const Arg *arg);
112 /* earlier, later action chronologically, redraw window */
113 static const char *earlier(Vis*, const char *keys, const Arg *arg);
114 static const char *later(Vis*, const char *keys, const Arg *arg);
115 /* delete from the current cursor position to the end of
116 * movement as indicated by arg->i */
117 static const char *delete(Vis*, const char *keys, const Arg *arg);
118 /* insert register content indicated by keys at current cursor position */
119 static const char *insert_register(Vis*, const char *keys, const Arg *arg);
120 /* show a user prompt to get input with title arg->s */
121 static const char *prompt_show(Vis*, const char *keys, const Arg *arg);
122 /* blocks to read 3 consecutive digits and inserts the corresponding byte value */
123 static const char *insert_verbatim(Vis*, const char *keys, const Arg *arg);
124 /* scroll window content according to arg->i which can be either PAGE, PAGE_HALF,
125 * or an arbitrary number of lines. a multiplier overrides what is given in arg->i.
126 * negative values scroll back, positive forward. */
127 static const char *wscroll(Vis*, const char *keys, const Arg *arg);
128 /* similar to scroll, but do only move window content not cursor position */
129 static const char *wslide(Vis*, const char *keys, const Arg *arg);
130 /* call editor function as indicated by arg->f */
131 static const char *call(Vis*, const char *keys, const Arg *arg);
132 /* call window function as indicated by arg->w */
133 static const char *window(Vis*, const char *keys, const Arg *arg);
134 /* show info about Unicode character at cursor position */
135 static const char *unicode_info(Vis*, const char *keys, const Arg *arg);
136 /* either go to count % of file or to matching item */
137 static const char *percent(Vis*, const char *keys, const Arg *arg);
138 /* navigate jumplist next (arg->i > 0), prev (arg->i < 0), save (arg->i = 0) */
139 static const char *jumplist(Vis*, const char *keys, const Arg *arg);
140
141 enum {
142 VIS_ACTION_EDITOR_SUSPEND,
143 VIS_ACTION_CURSOR_CHAR_PREV,
144 VIS_ACTION_CURSOR_CHAR_NEXT,
145 VIS_ACTION_CURSOR_LINE_CHAR_PREV,
146 VIS_ACTION_CURSOR_LINE_CHAR_NEXT,
147 VIS_ACTION_CURSOR_CODEPOINT_PREV,
148 VIS_ACTION_CURSOR_CODEPOINT_NEXT,
149 VIS_ACTION_CURSOR_WORD_START_PREV,
150 VIS_ACTION_CURSOR_WORD_START_NEXT,
151 VIS_ACTION_CURSOR_WORD_END_PREV,
152 VIS_ACTION_CURSOR_WORD_END_NEXT,
153 VIS_ACTION_CURSOR_LONGWORD_START_PREV,
154 VIS_ACTION_CURSOR_LONGWORD_START_NEXT,
155 VIS_ACTION_CURSOR_LONGWORD_END_PREV,
156 VIS_ACTION_CURSOR_LONGWORD_END_NEXT,
157 VIS_ACTION_CURSOR_LINE_UP,
158 VIS_ACTION_CURSOR_LINE_DOWN,
159 VIS_ACTION_CURSOR_LINE_START,
160 VIS_ACTION_CURSOR_LINE_FINISH,
161 VIS_ACTION_CURSOR_LINE_BEGIN,
162 VIS_ACTION_CURSOR_LINE_END,
163 VIS_ACTION_CURSOR_SCREEN_LINE_UP,
164 VIS_ACTION_CURSOR_SCREEN_LINE_DOWN,
165 VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN,
166 VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE,
167 VIS_ACTION_CURSOR_SCREEN_LINE_END,
168 VIS_ACTION_CURSOR_PERCENT,
169 VIS_ACTION_CURSOR_BYTE,
170 VIS_ACTION_CURSOR_BYTE_LEFT,
171 VIS_ACTION_CURSOR_BYTE_RIGHT,
172 VIS_ACTION_CURSOR_PARAGRAPH_PREV,
173 VIS_ACTION_CURSOR_PARAGRAPH_NEXT,
174 VIS_ACTION_CURSOR_SENTENCE_PREV,
175 VIS_ACTION_CURSOR_SENTENCE_NEXT,
176 VIS_ACTION_CURSOR_BLOCK_START,
177 VIS_ACTION_CURSOR_BLOCK_END,
178 VIS_ACTION_CURSOR_PARENTHESIS_START,
179 VIS_ACTION_CURSOR_PARENTHESIS_END,
180 VIS_ACTION_CURSOR_COLUMN,
181 VIS_ACTION_CURSOR_LINE_FIRST,
182 VIS_ACTION_CURSOR_LINE_LAST,
183 VIS_ACTION_CURSOR_WINDOW_LINE_TOP,
184 VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE,
185 VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM,
186 VIS_ACTION_CURSOR_SEARCH_REPEAT_FORWARD,
187 VIS_ACTION_CURSOR_SEARCH_REPEAT_BACKWARD,
188 VIS_ACTION_CURSOR_SEARCH_REPEAT,
189 VIS_ACTION_CURSOR_SEARCH_REPEAT_REVERSE,
190 VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD,
191 VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD,
192 VIS_ACTION_WINDOW_PAGE_UP,
193 VIS_ACTION_WINDOW_PAGE_DOWN,
194 VIS_ACTION_WINDOW_HALFPAGE_UP,
195 VIS_ACTION_WINDOW_HALFPAGE_DOWN,
196 VIS_ACTION_MODE_NORMAL,
197 VIS_ACTION_MODE_NORMAL_ESCAPE,
198 VIS_ACTION_MODE_VISUAL,
199 VIS_ACTION_MODE_VISUAL_ESCAPE,
200 VIS_ACTION_MODE_VISUAL_LINE,
201 VIS_ACTION_MODE_INSERT,
202 VIS_ACTION_MODE_REPLACE,
203 VIS_ACTION_DELETE_CHAR_PREV,
204 VIS_ACTION_DELETE_CHAR_NEXT,
205 VIS_ACTION_DELETE_LINE_BEGIN,
206 VIS_ACTION_DELETE_WORD_PREV,
207 VIS_ACTION_JUMPLIST_PREV,
208 VIS_ACTION_JUMPLIST_NEXT,
209 VIS_ACTION_JUMPLIST_SAVE,
210 VIS_ACTION_UNDO,
211 VIS_ACTION_REDO,
212 VIS_ACTION_EARLIER,
213 VIS_ACTION_LATER,
214 VIS_ACTION_MACRO_RECORD,
215 VIS_ACTION_MACRO_REPLAY,
216 VIS_ACTION_MARK,
217 VIS_ACTION_REDRAW,
218 VIS_ACTION_REPLACE_CHAR,
219 VIS_ACTION_TOTILL_REPEAT,
220 VIS_ACTION_TOTILL_REVERSE,
221 VIS_ACTION_PROMPT_SEARCH_FORWARD,
222 VIS_ACTION_PROMPT_SEARCH_BACKWARD,
223 VIS_ACTION_TILL_LEFT,
224 VIS_ACTION_TILL_RIGHT,
225 VIS_ACTION_TILL_LINE_LEFT,
226 VIS_ACTION_TILL_LINE_RIGHT,
227 VIS_ACTION_TO_LEFT,
228 VIS_ACTION_TO_RIGHT,
229 VIS_ACTION_TO_LINE_LEFT,
230 VIS_ACTION_TO_LINE_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_NEW_LINE_ABOVE,
262 VIS_ACTION_SELECTIONS_NEW_LINE_ABOVE_FIRST,
263 VIS_ACTION_SELECTIONS_NEW_LINE_BELOW,
264 VIS_ACTION_SELECTIONS_NEW_LINE_BELOW_LAST,
265 VIS_ACTION_SELECTIONS_NEW_LINES_BEGIN,
266 VIS_ACTION_SELECTIONS_NEW_LINES_END,
267 VIS_ACTION_SELECTIONS_NEW_MATCH_ALL,
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_TEXT_OBJECT_WORD_OUTER,
289 VIS_ACTION_TEXT_OBJECT_WORD_INNER,
290 VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER,
291 VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER,
292 VIS_ACTION_TEXT_OBJECT_SENTENCE,
293 VIS_ACTION_TEXT_OBJECT_PARAGRAPH,
294 VIS_ACTION_TEXT_OBJECT_PARAGRAPH_OUTER,
295 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER,
296 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER,
297 VIS_ACTION_TEXT_OBJECT_PARENTHESIS_OUTER,
298 VIS_ACTION_TEXT_OBJECT_PARENTHESIS_INNER,
299 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER,
300 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER,
301 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER,
302 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER,
303 VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER,
304 VIS_ACTION_TEXT_OBJECT_QUOTE_INNER,
305 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER,
306 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER,
307 VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER,
308 VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER,
309 VIS_ACTION_TEXT_OBJECT_LINE_OUTER,
310 VIS_ACTION_TEXT_OBJECT_LINE_INNER,
311 VIS_ACTION_TEXT_OBJECT_INDENTATION,
312 VIS_ACTION_TEXT_OBJECT_SEARCH_FORWARD,
313 VIS_ACTION_TEXT_OBJECT_SEARCH_BACKWARD,
314 VIS_ACTION_UNICODE_INFO,
315 VIS_ACTION_UTF8_INFO,
316 VIS_ACTION_NOP,
317 };
318
319 static const KeyAction vis_action[] = {
320 [VIS_ACTION_EDITOR_SUSPEND] = {
321 "vis-suspend",
322 VIS_HELP("Suspend the editor")
323 suspend,
324 },
325 [VIS_ACTION_CURSOR_CHAR_PREV] = {
326 "vis-motion-char-prev",
327 VIS_HELP("Move cursor left, to the previous character")
328 movement, { .i = VIS_MOVE_CHAR_PREV }
329 },
330 [VIS_ACTION_CURSOR_CHAR_NEXT] = {
331 "vis-motion-char-next",
332 VIS_HELP("Move cursor right, to the next character")
333 movement, { .i = VIS_MOVE_CHAR_NEXT }
334 },
335 [VIS_ACTION_CURSOR_LINE_CHAR_PREV] = {
336 "vis-motion-line-char-prev",
337 VIS_HELP("Move cursor left, to the previous character on the same line")
338 movement, { .i = VIS_MOVE_LINE_CHAR_PREV }
339 },
340 [VIS_ACTION_CURSOR_LINE_CHAR_NEXT] = {
341 "vis-motion-line-char-next",
342 VIS_HELP("Move cursor right, to the next character on the same line")
343 movement, { .i = VIS_MOVE_LINE_CHAR_NEXT }
344 },
345 [VIS_ACTION_CURSOR_CODEPOINT_PREV] = {
346 "vis-motion-codepoint-prev",
347 VIS_HELP("Move to the previous Unicode codepoint")
348 movement, { .i = VIS_MOVE_CODEPOINT_PREV }
349 },
350 [VIS_ACTION_CURSOR_CODEPOINT_NEXT] = {
351 "vis-motion-codepoint-next",
352 VIS_HELP("Move to the next Unicode codepoint")
353 movement, { .i = VIS_MOVE_CODEPOINT_NEXT }
354 },
355 [VIS_ACTION_CURSOR_WORD_START_PREV] = {
356 "vis-motion-word-start-prev",
357 VIS_HELP("Move cursor words backwards")
358 movement, { .i = VIS_MOVE_WORD_START_PREV }
359 },
360 [VIS_ACTION_CURSOR_WORD_START_NEXT] = {
361 "vis-motion-word-start-next",
362 VIS_HELP("Move cursor words forwards")
363 movement, { .i = VIS_MOVE_WORD_START_NEXT }
364 },
365 [VIS_ACTION_CURSOR_WORD_END_PREV] = {
366 "vis-motion-word-end-prev",
367 VIS_HELP("Move cursor backwards to the end of word")
368 movement, { .i = VIS_MOVE_WORD_END_PREV }
369 },
370 [VIS_ACTION_CURSOR_WORD_END_NEXT] = {
371 "vis-motion-word-end-next",
372 VIS_HELP("Move cursor forward to the end of word")
373 movement, { .i = VIS_MOVE_WORD_END_NEXT }
374 },
375 [VIS_ACTION_CURSOR_LONGWORD_START_PREV] = {
376 "vis-motion-bigword-start-prev",
377 VIS_HELP("Move cursor WORDS backwards")
378 movement, { .i = VIS_MOVE_LONGWORD_START_PREV }
379 },
380 [VIS_ACTION_CURSOR_LONGWORD_START_NEXT] = {
381 "vis-motion-bigword-start-next",
382 VIS_HELP("Move cursor WORDS forwards")
383 movement, { .i = VIS_MOVE_LONGWORD_START_NEXT }
384 },
385 [VIS_ACTION_CURSOR_LONGWORD_END_PREV] = {
386 "vis-motion-bigword-end-prev",
387 VIS_HELP("Move cursor backwards to the end of WORD")
388 movement, { .i = VIS_MOVE_LONGWORD_END_PREV }
389 },
390 [VIS_ACTION_CURSOR_LONGWORD_END_NEXT] = {
391 "vis-motion-bigword-end-next",
392 VIS_HELP("Move cursor forward to the end of WORD")
393 movement, { .i = VIS_MOVE_LONGWORD_END_NEXT }
394 },
395 [VIS_ACTION_CURSOR_LINE_UP] = {
396 "vis-motion-line-up",
397 VIS_HELP("Move cursor line upwards")
398 movement, { .i = VIS_MOVE_LINE_UP }
399 },
400 [VIS_ACTION_CURSOR_LINE_DOWN] = {
401 "vis-motion-line-down",
402 VIS_HELP("Move cursor line downwards")
403 movement, { .i = VIS_MOVE_LINE_DOWN }
404 },
405 [VIS_ACTION_CURSOR_LINE_START] = {
406 "vis-motion-line-start",
407 VIS_HELP("Move cursor to first non-blank character of the line")
408 movement, { .i = VIS_MOVE_LINE_START }
409 },
410 [VIS_ACTION_CURSOR_LINE_FINISH] = {
411 "vis-motion-line-finish",
412 VIS_HELP("Move cursor to last non-blank character of the line")
413 movement, { .i = VIS_MOVE_LINE_FINISH }
414 },
415 [VIS_ACTION_CURSOR_LINE_BEGIN] = {
416 "vis-motion-line-begin",
417 VIS_HELP("Move cursor to first character of the line")
418 movement, { .i = VIS_MOVE_LINE_BEGIN }
419 },
420 [VIS_ACTION_CURSOR_LINE_END] = {
421 "vis-motion-line-end",
422 VIS_HELP("Move cursor to end of the line")
423 movement, { .i = VIS_MOVE_LINE_END }
424 },
425 [VIS_ACTION_CURSOR_SCREEN_LINE_UP] = {
426 "vis-motion-screenline-up",
427 VIS_HELP("Move cursor screen/display line upwards")
428 movement, { .i = VIS_MOVE_SCREEN_LINE_UP }
429 },
430 [VIS_ACTION_CURSOR_SCREEN_LINE_DOWN] = {
431 "vis-motion-screenline-down",
432 VIS_HELP("Move cursor screen/display line downwards")
433 movement, { .i = VIS_MOVE_SCREEN_LINE_DOWN }
434 },
435 [VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN] = {
436 "vis-motion-screenline-begin",
437 VIS_HELP("Move cursor to beginning of screen/display line")
438 movement, { .i = VIS_MOVE_SCREEN_LINE_BEGIN }
439 },
440 [VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE] = {
441 "vis-motion-screenline-middle",
442 VIS_HELP("Move cursor to middle of screen/display line")
443 movement, { .i = VIS_MOVE_SCREEN_LINE_MIDDLE }
444 },
445 [VIS_ACTION_CURSOR_SCREEN_LINE_END] = {
446 "vis-motion-screenline-end",
447 VIS_HELP("Move cursor to end of screen/display line")
448 movement, { .i = VIS_MOVE_SCREEN_LINE_END }
449 },
450 [VIS_ACTION_CURSOR_PERCENT] = {
451 "vis-motion-percent",
452 VIS_HELP("Move to count % of file or matching item")
453 percent
454 },
455 [VIS_ACTION_CURSOR_BYTE] = {
456 "vis-motion-byte",
457 VIS_HELP("Move to absolute byte position")
458 movement, { .i = VIS_MOVE_BYTE }
459 },
460 [VIS_ACTION_CURSOR_BYTE_LEFT] = {
461 "vis-motion-byte-left",
462 VIS_HELP("Move count bytes to the left")
463 movement, { .i = VIS_MOVE_BYTE_LEFT }
464 },
465 [VIS_ACTION_CURSOR_BYTE_RIGHT] = {
466 "vis-motion-byte-right",
467 VIS_HELP("Move count bytes to the right")
468 movement, { .i = VIS_MOVE_BYTE_RIGHT }
469 },
470 [VIS_ACTION_CURSOR_PARAGRAPH_PREV] = {
471 "vis-motion-paragraph-prev",
472 VIS_HELP("Move cursor paragraph backward")
473 movement, { .i = VIS_MOVE_PARAGRAPH_PREV }
474 },
475 [VIS_ACTION_CURSOR_PARAGRAPH_NEXT] = {
476 "vis-motion-paragraph-next",
477 VIS_HELP("Move cursor paragraph forward")
478 movement, { .i = VIS_MOVE_PARAGRAPH_NEXT }
479 },
480 [VIS_ACTION_CURSOR_SENTENCE_PREV] = {
481 "vis-motion-sentence-prev",
482 VIS_HELP("Move cursor sentence backward")
483 movement, { .i = VIS_MOVE_SENTENCE_PREV }
484 },
485 [VIS_ACTION_CURSOR_SENTENCE_NEXT] = {
486 "vis-motion-sentence-next",
487 VIS_HELP("Move cursor sentence forward")
488 movement, { .i = VIS_MOVE_SENTENCE_NEXT }
489 },
490 [VIS_ACTION_CURSOR_BLOCK_START] = {
491 "vis-motion-block-start",
492 VIS_HELP("Move cursor to the opening curly brace in a block")
493 movement, { .i = VIS_MOVE_BLOCK_START }
494 },
495 [VIS_ACTION_CURSOR_BLOCK_END] = {
496 "vis-motion-block-end",
497 VIS_HELP("Move cursor to the closing curly brace in a block")
498 movement, { .i = VIS_MOVE_BLOCK_END }
499 },
500 [VIS_ACTION_CURSOR_PARENTHESIS_START] = {
501 "vis-motion-parenthesis-start",
502 VIS_HELP("Move cursor to the opening parenthesis inside a pair of parentheses")
503 movement, { .i = VIS_MOVE_PARENTHESIS_START }
504 },
505 [VIS_ACTION_CURSOR_PARENTHESIS_END] = {
506 "vis-motion-parenthesis-end",
507 VIS_HELP("Move cursor to the closing parenthesis inside a pair of parentheses")
508 movement, { .i = VIS_MOVE_PARENTHESIS_END }
509 },
510 [VIS_ACTION_CURSOR_COLUMN] = {
511 "vis-motion-column",
512 VIS_HELP("Move cursor to given column of current line")
513 movement, { .i = VIS_MOVE_COLUMN }
514 },
515 [VIS_ACTION_CURSOR_LINE_FIRST] = {
516 "vis-motion-line-first",
517 VIS_HELP("Move cursor to given line (defaults to first)")
518 gotoline, { .i = -1 }
519 },
520 [VIS_ACTION_CURSOR_LINE_LAST] = {
521 "vis-motion-line-last",
522 VIS_HELP("Move cursor to given line (defaults to last)")
523 gotoline, { .i = +1 }
524 },
525 [VIS_ACTION_CURSOR_WINDOW_LINE_TOP] = {
526 "vis-motion-window-line-top",
527 VIS_HELP("Move cursor to top line of the window")
528 movement, { .i = VIS_MOVE_WINDOW_LINE_TOP }
529 },
530 [VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE] = {
531 "vis-motion-window-line-middle",
532 VIS_HELP("Move cursor to middle line of the window")
533 movement, { .i = VIS_MOVE_WINDOW_LINE_MIDDLE }
534 },
535 [VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM] = {
536 "vis-motion-window-line-bottom",
537 VIS_HELP("Move cursor to bottom line of the window")
538 movement, { .i = VIS_MOVE_WINDOW_LINE_BOTTOM }
539 },
540 [VIS_ACTION_CURSOR_SEARCH_REPEAT_FORWARD] = {
541 "vis-motion-search-repeat-forward",
542 VIS_HELP("Move cursor to next match in forward direction")
543 movement, { .i = VIS_MOVE_SEARCH_REPEAT_FORWARD }
544 },
545 [VIS_ACTION_CURSOR_SEARCH_REPEAT_BACKWARD] = {
546 "vis-motion-search-repeat-backward",
547 VIS_HELP("Move cursor to previous match in backward direction")
548 movement, { .i = VIS_MOVE_SEARCH_REPEAT_BACKWARD }
549 },
550 [VIS_ACTION_CURSOR_SEARCH_REPEAT] = {
551 "vis-motion-search-repeat",
552 VIS_HELP("Move cursor to next match")
553 movement, { .i = VIS_MOVE_SEARCH_REPEAT }
554 },
555 [VIS_ACTION_CURSOR_SEARCH_REPEAT_REVERSE] = {
556 "vis-motion-search-repeat-reverse",
557 VIS_HELP("Move cursor to next match in opposite direction")
558 movement, { .i = VIS_MOVE_SEARCH_REPEAT_REVERSE }
559 },
560 [VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD] = {
561 "vis-motion-search-word-forward",
562 VIS_HELP("Move cursor to next occurrence of the word under cursor")
563 movement, { .i = VIS_MOVE_SEARCH_WORD_FORWARD }
564 },
565 [VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD] = {
566 "vis-motion-search-word-backward",
567 VIS_HELP("Move cursor to previous occurrence of the word under cursor")
568 movement, { .i = VIS_MOVE_SEARCH_WORD_BACKWARD }
569 },
570 [VIS_ACTION_WINDOW_PAGE_UP] = {
571 "vis-window-page-up",
572 VIS_HELP("Scroll window pages backwards (upwards)")
573 wscroll, { .i = -PAGE }
574 },
575 [VIS_ACTION_WINDOW_HALFPAGE_UP] = {
576 "vis-window-halfpage-up",
577 VIS_HELP("Scroll window half pages backwards (upwards)")
578 wscroll, { .i = -PAGE_HALF }
579 },
580 [VIS_ACTION_WINDOW_PAGE_DOWN] = {
581 "vis-window-page-down",
582 VIS_HELP("Scroll window pages forwards (downwards)")
583 wscroll, { .i = +PAGE }
584 },
585 [VIS_ACTION_WINDOW_HALFPAGE_DOWN] = {
586 "vis-window-halfpage-down",
587 VIS_HELP("Scroll window half pages forwards (downwards)")
588 wscroll, { .i = +PAGE_HALF }
589 },
590 [VIS_ACTION_MODE_NORMAL] = {
591 "vis-mode-normal",
592 VIS_HELP("Enter normal mode")
593 switchmode, { .i = VIS_MODE_NORMAL }
594 },
595 [VIS_ACTION_MODE_NORMAL_ESCAPE] = {
596 "vis-mode-normal-escape",
597 VIS_HELP("Reset count or remove all non-primary selections")
598 normalmode_escape,
599 },
600 [VIS_ACTION_MODE_VISUAL] = {
601 "vis-mode-visual-charwise",
602 VIS_HELP("Enter characterwise visual mode")
603 switchmode, { .i = VIS_MODE_VISUAL }
604 },
605 [VIS_ACTION_MODE_VISUAL_ESCAPE] = {
606 "vis-mode-visual-escape",
607 VIS_HELP("Reset count or switch to normal mode")
608 visualmode_escape,
609 },
610 [VIS_ACTION_MODE_VISUAL_LINE] = {
611 "vis-mode-visual-linewise",
612 VIS_HELP("Enter linewise visual mode")
613 switchmode, { .i = VIS_MODE_VISUAL_LINE }
614 },
615 [VIS_ACTION_MODE_INSERT] = {
616 "vis-mode-insert",
617 VIS_HELP("Enter insert mode")
618 insertmode, { .i = VIS_MOVE_NOP }
619 },
620 [VIS_ACTION_MODE_REPLACE] = {
621 "vis-mode-replace",
622 VIS_HELP("Enter replace mode")
623 replacemode, { .i = VIS_MOVE_NOP }
624 },
625 [VIS_ACTION_DELETE_CHAR_PREV] = {
626 "vis-delete-char-prev",
627 VIS_HELP("Delete the previous character")
628 delete, { .i = VIS_MOVE_CHAR_PREV }
629 },
630 [VIS_ACTION_DELETE_CHAR_NEXT] = {
631 "vis-delete-char-next",
632 VIS_HELP("Delete the next character")
633 delete, { .i = VIS_MOVE_CHAR_NEXT }
634 },
635 [VIS_ACTION_DELETE_LINE_BEGIN] = {
636 "vis-delete-line-begin",
637 VIS_HELP("Delete until the start of the current line")
638 delete, { .i = VIS_MOVE_LINE_BEGIN }
639 },
640 [VIS_ACTION_DELETE_WORD_PREV] = {
641 "vis-delete-word-prev",
642 VIS_HELP("Delete the previous WORD")
643 delete, { .i = VIS_MOVE_WORD_START_PREV }
644 },
645 [VIS_ACTION_JUMPLIST_PREV] = {
646 "vis-jumplist-prev",
647 VIS_HELP("Go to older cursor position in jump list")
648 jumplist, { .i = -1 }
649 },
650 [VIS_ACTION_JUMPLIST_NEXT] = {
651 "vis-jumplist-next",
652 VIS_HELP("Go to newer cursor position in jump list")
653 jumplist, { .i = +1 }
654 },
655 [VIS_ACTION_JUMPLIST_SAVE] = {
656 "vis-jumplist-save",
657 VIS_HELP("Save current selections in jump list")
658 jumplist, { .i = 0 }
659 },
660 [VIS_ACTION_UNDO] = {
661 "vis-undo",
662 VIS_HELP("Undo last change")
663 undo,
664 },
665 [VIS_ACTION_REDO] = {
666 "vis-redo",
667 VIS_HELP("Redo last change")
668 redo,
669 },
670 [VIS_ACTION_EARLIER] = {
671 "vis-earlier",
672 VIS_HELP("Goto older text state")
673 earlier,
674 },
675 [VIS_ACTION_LATER] = {
676 "vis-later",
677 VIS_HELP("Goto newer text state")
678 later,
679 },
680 [VIS_ACTION_MACRO_RECORD] = {
681 "vis-macro-record",
682 VIS_HELP("Record macro into given register")
683 macro_record,
684 },
685 [VIS_ACTION_MACRO_REPLAY] = {
686 "vis-macro-replay",
687 VIS_HELP("Replay macro, execute the content of the given register")
688 macro_replay,
689 },
690 [VIS_ACTION_MARK] = {
691 "vis-mark",
692 VIS_HELP("Use given mark for next action")
693 mark,
694 },
695 [VIS_ACTION_REDRAW] = {
696 "vis-redraw",
697 VIS_HELP("Redraw current editor content")
698 call, { .f = vis_redraw }
699 },
700 [VIS_ACTION_REPLACE_CHAR] = {
701 "vis-replace-char",
702 VIS_HELP("Replace the character under the cursor")
703 replace,
704 },
705 [VIS_ACTION_TOTILL_REPEAT] = {
706 "vis-motion-totill-repeat",
707 VIS_HELP("Repeat latest to/till motion")
708 movement, { .i = VIS_MOVE_TOTILL_REPEAT }
709 },
710 [VIS_ACTION_TOTILL_REVERSE] = {
711 "vis-motion-totill-reverse",
712 VIS_HELP("Repeat latest to/till motion but in opposite direction")
713 movement, { .i = VIS_MOVE_TOTILL_REVERSE }
714 },
715 [VIS_ACTION_PROMPT_SEARCH_FORWARD] = {
716 "vis-search-forward",
717 VIS_HELP("Search forward")
718 prompt_show, { .s = "/" }
719 },
720 [VIS_ACTION_PROMPT_SEARCH_BACKWARD] = {
721 "vis-search-backward",
722 VIS_HELP("Search backward")
723 prompt_show, { .s = "?" }
724 },
725 [VIS_ACTION_TILL_LEFT] = {
726 "vis-motion-till-left",
727 VIS_HELP("Till after the occurrence of character to the left")
728 movement_key, { .i = VIS_MOVE_TILL_LEFT }
729 },
730 [VIS_ACTION_TILL_RIGHT] = {
731 "vis-motion-till-right",
732 VIS_HELP("Till before the occurrence of character to the right")
733 movement_key, { .i = VIS_MOVE_TILL_RIGHT }
734 },
735 [VIS_ACTION_TILL_LINE_LEFT] = {
736 "vis-motion-till-line-left",
737 VIS_HELP("Till after the occurrence of character to the left on the current line")
738 movement_key, { .i = VIS_MOVE_TILL_LINE_LEFT }
739 },
740 [VIS_ACTION_TILL_LINE_RIGHT] = {
741 "vis-motion-till-line-right",
742 VIS_HELP("Till before the occurrence of character to the right on the current line")
743 movement_key, { .i = VIS_MOVE_TILL_LINE_RIGHT }
744 },
745 [VIS_ACTION_TO_LEFT] = {
746 "vis-motion-to-left",
747 VIS_HELP("To the first occurrence of character to the left")
748 movement_key, { .i = VIS_MOVE_TO_LEFT }
749 },
750 [VIS_ACTION_TO_RIGHT] = {
751 "vis-motion-to-right",
752 VIS_HELP("To the first occurrence of character to the right")
753 movement_key, { .i = VIS_MOVE_TO_RIGHT }
754 },
755 [VIS_ACTION_TO_LINE_LEFT] = {
756 "vis-motion-to-line-left",
757 VIS_HELP("To the first occurrence of character to the left on the current line")
758 movement_key, { .i = VIS_MOVE_TO_LINE_LEFT }
759 },
760 [VIS_ACTION_TO_LINE_RIGHT] = {
761 "vis-motion-to-line-right",
762 VIS_HELP("To the first occurrence of character to the right on the current line")
763 movement_key, { .i = VIS_MOVE_TO_LINE_RIGHT }
764 },
765 [VIS_ACTION_REGISTER] = {
766 "vis-register",
767 VIS_HELP("Use given register for next operator")
768 reg,
769 },
770 [VIS_ACTION_OPERATOR_CHANGE] = {
771 "vis-operator-change",
772 VIS_HELP("Change operator")
773 operator, { .i = VIS_OP_CHANGE }
774 },
775 [VIS_ACTION_OPERATOR_DELETE] = {
776 "vis-operator-delete",
777 VIS_HELP("Delete operator")
778 operator, { .i = VIS_OP_DELETE }
779 },
780 [VIS_ACTION_OPERATOR_YANK] = {
781 "vis-operator-yank",
782 VIS_HELP("Yank operator")
783 operator, { .i = VIS_OP_YANK }
784 },
785 [VIS_ACTION_OPERATOR_SHIFT_LEFT] = {
786 "vis-operator-shift-left",
787 VIS_HELP("Shift left operator")
788 operator, { .i = VIS_OP_SHIFT_LEFT }
789 },
790 [VIS_ACTION_OPERATOR_SHIFT_RIGHT] = {
791 "vis-operator-shift-right",
792 VIS_HELP("Shift right operator")
793 operator, { .i = VIS_OP_SHIFT_RIGHT }
794 },
795 [VIS_ACTION_COUNT] = {
796 "vis-count",
797 VIS_HELP("Count specifier")
798 count,
799 },
800 [VIS_ACTION_INSERT_NEWLINE] = {
801 "vis-insert-newline",
802 VIS_HELP("Insert a line break (depending on file type)")
803 call, { .f = vis_insert_nl }
804 },
805 [VIS_ACTION_INSERT_TAB] = {
806 "vis-insert-tab",
807 VIS_HELP("Insert a tab (might be converted to spaces)")
808 call, { .f = vis_insert_tab }
809 },
810 [VIS_ACTION_INSERT_VERBATIM] = {
811 "vis-insert-verbatim",
812 VIS_HELP("Insert Unicode character based on code point")
813 insert_verbatim,
814 },
815 [VIS_ACTION_INSERT_REGISTER] = {
816 "vis-insert-register",
817 VIS_HELP("Insert specified register content")
818 insert_register,
819 },
820 [VIS_ACTION_WINDOW_NEXT] = {
821 "vis-window-next",
822 VIS_HELP("Focus next window")
823 call, { .f = vis_window_next }
824 },
825 [VIS_ACTION_WINDOW_PREV] = {
826 "vis-window-prev",
827 VIS_HELP("Focus previous window")
828 call, { .f = vis_window_prev }
829 },
830 [VIS_ACTION_APPEND_CHAR_NEXT] = {
831 "vis-append-char-next",
832 VIS_HELP("Append text after the cursor")
833 insertmode, { .i = VIS_MOVE_LINE_CHAR_NEXT }
834 },
835 [VIS_ACTION_APPEND_LINE_END] = {
836 "vis-append-line-end",
837 VIS_HELP("Append text after the end of the line")
838 insertmode, { .i = VIS_MOVE_LINE_END },
839 },
840 [VIS_ACTION_INSERT_LINE_START] = {
841 "vis-insert-line-start",
842 VIS_HELP("Insert text before the first non-blank in the line")
843 insertmode, { .i = VIS_MOVE_LINE_START },
844 },
845 [VIS_ACTION_OPEN_LINE_ABOVE] = {
846 "vis-open-line-above",
847 VIS_HELP("Begin a new line above the cursor")
848 openline, { .i = -1 }
849 },
850 [VIS_ACTION_OPEN_LINE_BELOW] = {
851 "vis-open-line-below",
852 VIS_HELP("Begin a new line below the cursor")
853 openline, { .i = +1 }
854 },
855 [VIS_ACTION_JOIN_LINES] = {
856 "vis-join-lines",
857 VIS_HELP("Join selected lines")
858 join, { .s = " " }
859 },
860 [VIS_ACTION_JOIN_LINES_TRIM] = {
861 "vis-join-lines-trim",
862 VIS_HELP("Join selected lines, remove white space")
863 join, { .s = "" }
864 },
865 [VIS_ACTION_PROMPT_SHOW] = {
866 "vis-prompt-show",
867 VIS_HELP("Show editor command line prompt")
868 prompt_show, { .s = ":" }
869 },
870 [VIS_ACTION_REPEAT] = {
871 "vis-repeat",
872 VIS_HELP("Repeat latest editor command")
873 repeat
874 },
875 [VIS_ACTION_SELECTION_FLIP] = {
876 "vis-selection-flip",
877 VIS_HELP("Flip selection, move cursor to other end")
878 selection_end,
879 },
880 [VIS_ACTION_WINDOW_REDRAW_TOP] = {
881 "vis-window-redraw-top",
882 VIS_HELP("Redraw cursor line at the top of the window")
883 window, { .w = view_redraw_top }
884 },
885 [VIS_ACTION_WINDOW_REDRAW_CENTER] = {
886 "vis-window-redraw-center",
887 VIS_HELP("Redraw cursor line at the center of the window")
888 window, { .w = view_redraw_center }
889 },
890 [VIS_ACTION_WINDOW_REDRAW_BOTTOM] = {
891 "vis-window-redraw-bottom",
892 VIS_HELP("Redraw cursor line at the bottom of the window")
893 window, { .w = view_redraw_bottom }
894 },
895 [VIS_ACTION_WINDOW_SLIDE_UP] = {
896 "vis-window-slide-up",
897 VIS_HELP("Slide window content upwards")
898 wslide, { .i = -1 }
899 },
900 [VIS_ACTION_WINDOW_SLIDE_DOWN] = {
901 "vis-window-slide-down",
902 VIS_HELP("Slide window content downwards")
903 wslide, { .i = +1 }
904 },
905 [VIS_ACTION_PUT_AFTER] = {
906 "vis-put-after",
907 VIS_HELP("Put text after the cursor")
908 operator, { .i = VIS_OP_PUT_AFTER }
909 },
910 [VIS_ACTION_PUT_BEFORE] = {
911 "vis-put-before",
912 VIS_HELP("Put text before the cursor")
913 operator, { .i = VIS_OP_PUT_BEFORE }
914 },
915 [VIS_ACTION_SELECTIONS_NEW_LINE_ABOVE] = {
916 "vis-selection-new-lines-above",
917 VIS_HELP("Create a new selection on the line above")
918 selections_new, { .i = -1 }
919 },
920 [VIS_ACTION_SELECTIONS_NEW_LINE_ABOVE_FIRST] = {
921 "vis-selection-new-lines-above-first",
922 VIS_HELP("Create a new selection on the line above the first selection")
923 selections_new, { .i = INT_MIN }
924 },
925 [VIS_ACTION_SELECTIONS_NEW_LINE_BELOW] = {
926 "vis-selection-new-lines-below",
927 VIS_HELP("Create a new selection on the line below")
928 selections_new, { .i = +1 }
929 },
930 [VIS_ACTION_SELECTIONS_NEW_LINE_BELOW_LAST] = {
931 "vis-selection-new-lines-below-last",
932 VIS_HELP("Create a new selection on the line below the last selection")
933 selections_new, { .i = INT_MAX }
934 },
935 [VIS_ACTION_SELECTIONS_NEW_LINES_BEGIN] = {
936 "vis-selection-new-lines-begin",
937 VIS_HELP("Create a new selection at the start of every line covered by selection")
938 operator, { .i = VIS_OP_CURSOR_SOL }
939 },
940 [VIS_ACTION_SELECTIONS_NEW_LINES_END] = {
941 "vis-selection-new-lines-end",
942 VIS_HELP("Create a new selection at the end of every line covered by selection")
943 operator, { .i = VIS_OP_CURSOR_EOL }
944 },
945 [VIS_ACTION_SELECTIONS_NEW_MATCH_ALL] = {
946 "vis-selection-new-match-all",
947 VIS_HELP("Select all regions matching the current selection")
948 selections_match_next, { .b = true }
949 },
950 [VIS_ACTION_SELECTIONS_NEW_MATCH_NEXT] = {
951 "vis-selection-new-match-next",
952 VIS_HELP("Select the next region matching the current selection")
953 selections_match_next,
954 },
955 [VIS_ACTION_SELECTIONS_NEW_MATCH_SKIP] = {
956 "vis-selection-new-match-skip",
957 VIS_HELP("Clear current selection, but select next match")
958 selections_match_skip,
959 },
960 [VIS_ACTION_SELECTIONS_ALIGN] = {
961 "vis-selections-align",
962 VIS_HELP("Try to align all selections on the same column")
963 selections_align,
964 },
965 [VIS_ACTION_SELECTIONS_ALIGN_INDENT_LEFT] = {
966 "vis-selections-align-indent-left",
967 VIS_HELP("Left-align all selections by inserting spaces")
968 selections_align_indent, { .i = -1 }
969 },
970 [VIS_ACTION_SELECTIONS_ALIGN_INDENT_RIGHT] = {
971 "vis-selections-align-indent-right",
972 VIS_HELP("Right-align all selections by inserting spaces")
973 selections_align_indent, { .i = +1 }
974 },
975 [VIS_ACTION_SELECTIONS_REMOVE_ALL] = {
976 "vis-selections-remove-all",
977 VIS_HELP("Remove all but the primary selection")
978 selections_clear,
979 },
980 [VIS_ACTION_SELECTIONS_REMOVE_LAST] = {
981 "vis-selections-remove-last",
982 VIS_HELP("Remove primary selection")
983 selections_remove,
984 },
985 [VIS_ACTION_SELECTIONS_REMOVE_COLUMN] = {
986 "vis-selections-remove-column",
987 VIS_HELP("Remove count selection column")
988 selections_remove_column, { .i = 1 }
989 },
990 [VIS_ACTION_SELECTIONS_REMOVE_COLUMN_EXCEPT] = {
991 "vis-selections-remove-column-except",
992 VIS_HELP("Remove all but the count selection column")
993 selections_remove_column_except, { .i = 1 }
994 },
995 [VIS_ACTION_SELECTIONS_PREV] = {
996 "vis-selection-prev",
997 VIS_HELP("Move to the previous selection")
998 selections_navigate, { .i = -PAGE_HALF }
999 },
1000 [VIS_ACTION_SELECTIONS_NEXT] = {
1001 "vis-selection-next",
1002 VIS_HELP("Move to the next selection")
1003 selections_navigate, { .i = +PAGE_HALF }
1004 },
1005 [VIS_ACTION_SELECTIONS_ROTATE_LEFT] = {
1006 "vis-selections-rotate-left",
1007 VIS_HELP("Rotate selections left")
1008 selections_rotate, { .i = -1 }
1009 },
1010 [VIS_ACTION_SELECTIONS_ROTATE_RIGHT] = {
1011 "vis-selections-rotate-right",
1012 VIS_HELP("Rotate selections right")
1013 selections_rotate, { .i = +1 }
1014 },
1015 [VIS_ACTION_SELECTIONS_TRIM] = {
1016 "vis-selections-trim",
1017 VIS_HELP("Remove leading and trailing white space from selections")
1018 selections_trim
1019 },
1020 [VIS_ACTION_SELECTIONS_SAVE] = {
1021 "vis-selections-save",
1022 VIS_HELP("Save currently active selections to mark")
1023 selections_save
1024 },
1025 [VIS_ACTION_SELECTIONS_RESTORE] = {
1026 "vis-selections-restore",
1027 VIS_HELP("Restore selections from mark")
1028 selections_restore
1029 },
1030 [VIS_ACTION_SELECTIONS_UNION] = {
1031 "vis-selections-union",
1032 VIS_HELP("Add selections from mark")
1033 selections_union
1034 },
1035 [VIS_ACTION_SELECTIONS_INTERSECT] = {
1036 "vis-selections-intersect",
1037 VIS_HELP("Intersect with selections from mark")
1038 selections_intersect
1039 },
1040 [VIS_ACTION_SELECTIONS_COMPLEMENT] = {
1041 "vis-selections-complement",
1042 VIS_HELP("Complement selections")
1043 selections_complement
1044 },
1045 [VIS_ACTION_SELECTIONS_MINUS] = {
1046 "vis-selections-minus",
1047 VIS_HELP("Subtract selections from mark")
1048 selections_minus
1049 },
1050 [VIS_ACTION_TEXT_OBJECT_WORD_OUTER] = {
1051 "vis-textobject-word-outer",
1052 VIS_HELP("A word leading and trailing whitespace included")
1053 textobj, { .i = VIS_TEXTOBJECT_OUTER_WORD }
1054 },
1055 [VIS_ACTION_TEXT_OBJECT_WORD_INNER] = {
1056 "vis-textobject-word-inner",
1057 VIS_HELP("A word leading and trailing whitespace excluded")
1058 textobj, { .i = VIS_TEXTOBJECT_INNER_WORD }
1059 },
1060 [VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER] = {
1061 "vis-textobject-bigword-outer",
1062 VIS_HELP("A WORD leading and trailing whitespace included")
1063 textobj, { .i = VIS_TEXTOBJECT_OUTER_LONGWORD }
1064 },
1065 [VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER] = {
1066 "vis-textobject-bigword-inner",
1067 VIS_HELP("A WORD leading and trailing whitespace excluded")
1068 textobj, { .i = VIS_TEXTOBJECT_INNER_LONGWORD }
1069 },
1070 [VIS_ACTION_TEXT_OBJECT_SENTENCE] = {
1071 "vis-textobject-sentence",
1072 VIS_HELP("A sentence")
1073 textobj, { .i = VIS_TEXTOBJECT_SENTENCE }
1074 },
1075 [VIS_ACTION_TEXT_OBJECT_PARAGRAPH] = {
1076 "vis-textobject-paragraph",
1077 VIS_HELP("A paragraph")
1078 textobj, { .i = VIS_TEXTOBJECT_PARAGRAPH }
1079 },
1080 [VIS_ACTION_TEXT_OBJECT_PARAGRAPH_OUTER] = {
1081 "vis-textobject-paragraph-outer",
1082 VIS_HELP("A paragraph (outer variant)")
1083 textobj, { .i = VIS_TEXTOBJECT_PARAGRAPH_OUTER }
1084 },
1085 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER] = {
1086 "vis-textobject-square-bracket-outer",
1087 VIS_HELP("[] block (outer variant)")
1088 textobj, { .i = VIS_TEXTOBJECT_OUTER_SQUARE_BRACKET }
1089 },
1090 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER] = {
1091 "vis-textobject-square-bracket-inner",
1092 VIS_HELP("[] block (inner variant)")
1093 textobj, { .i = VIS_TEXTOBJECT_INNER_SQUARE_BRACKET }
1094 },
1095 [VIS_ACTION_TEXT_OBJECT_PARENTHESIS_OUTER] = {
1096 "vis-textobject-parenthesis-outer",
1097 VIS_HELP("() block (outer variant)")
1098 textobj, { .i = VIS_TEXTOBJECT_OUTER_PARENTHESIS }
1099 },
1100 [VIS_ACTION_TEXT_OBJECT_PARENTHESIS_INNER] = {
1101 "vis-textobject-parenthesis-inner",
1102 VIS_HELP("() block (inner variant)")
1103 textobj, { .i = VIS_TEXTOBJECT_INNER_PARENTHESIS }
1104 },
1105 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER] = {
1106 "vis-textobject-angle-bracket-outer",
1107 VIS_HELP("<> block (outer variant)")
1108 textobj, { .i = VIS_TEXTOBJECT_OUTER_ANGLE_BRACKET }
1109 },
1110 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER] = {
1111 "vis-textobject-angle-bracket-inner",
1112 VIS_HELP("<> block (inner variant)")
1113 textobj, { .i = VIS_TEXTOBJECT_INNER_ANGLE_BRACKET }
1114 },
1115 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER] = {
1116 "vis-textobject-curly-bracket-outer",
1117 VIS_HELP("{} block (outer variant)")
1118 textobj, { .i = VIS_TEXTOBJECT_OUTER_CURLY_BRACKET }
1119 },
1120 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER] = {
1121 "vis-textobject-curly-bracket-inner",
1122 VIS_HELP("{} block (inner variant)")
1123 textobj, { .i = VIS_TEXTOBJECT_INNER_CURLY_BRACKET }
1124 },
1125 [VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER] = {
1126 "vis-textobject-quote-outer",
1127 VIS_HELP("A quoted string, including the quotation marks")
1128 textobj, { .i = VIS_TEXTOBJECT_OUTER_QUOTE }
1129 },
1130 [VIS_ACTION_TEXT_OBJECT_QUOTE_INNER] = {
1131 "vis-textobject-quote-inner",
1132 VIS_HELP("A quoted string, excluding the quotation marks")
1133 textobj, { .i = VIS_TEXTOBJECT_INNER_QUOTE }
1134 },
1135 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER] = {
1136 "vis-textobject-single-quote-outer",
1137 VIS_HELP("A single quoted string, including the quotation marks")
1138 textobj, { .i = VIS_TEXTOBJECT_OUTER_SINGLE_QUOTE }
1139 },
1140 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER] = {
1141 "vis-textobject-single-quote-inner",
1142 VIS_HELP("A single quoted string, excluding the quotation marks")
1143 textobj, { .i = VIS_TEXTOBJECT_INNER_SINGLE_QUOTE }
1144 },
1145 [VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER] = {
1146 "vis-textobject-backtick-outer",
1147 VIS_HELP("A backtick delimited string (outer variant)")
1148 textobj, { .i = VIS_TEXTOBJECT_OUTER_BACKTICK }
1149 },
1150 [VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER] = {
1151 "vis-textobject-backtick-inner",
1152 VIS_HELP("A backtick delimited string (inner variant)")
1153 textobj, { .i = VIS_TEXTOBJECT_INNER_BACKTICK }
1154 },
1155 [VIS_ACTION_TEXT_OBJECT_LINE_OUTER] = {
1156 "vis-textobject-line-outer",
1157 VIS_HELP("The whole line")
1158 textobj, { .i = VIS_TEXTOBJECT_OUTER_LINE }
1159 },
1160 [VIS_ACTION_TEXT_OBJECT_LINE_INNER] = {
1161 "vis-textobject-line-inner",
1162 VIS_HELP("The whole line, excluding leading and trailing whitespace")
1163 textobj, { .i = VIS_TEXTOBJECT_INNER_LINE }
1164 },
1165 [VIS_ACTION_TEXT_OBJECT_INDENTATION] = {
1166 "vis-textobject-indentation",
1167 VIS_HELP("All adjacent lines with the same indentation level as the current one")
1168 textobj, { .i = VIS_TEXTOBJECT_INDENTATION }
1169 },
1170 [VIS_ACTION_TEXT_OBJECT_SEARCH_FORWARD] = {
1171 "vis-textobject-search-forward",
1172 VIS_HELP("The next search match in forward direction")
1173 textobj, { .i = VIS_TEXTOBJECT_SEARCH_FORWARD }
1174 },
1175 [VIS_ACTION_TEXT_OBJECT_SEARCH_BACKWARD] = {
1176 "vis-textobject-search-backward",
1177 VIS_HELP("The next search match in backward direction")
1178 textobj, { .i = VIS_TEXTOBJECT_SEARCH_BACKWARD }
1179 },
1180 [VIS_ACTION_UNICODE_INFO] = {
1181 "vis-unicode-info",
1182 VIS_HELP("Show Unicode codepoint(s) of character under cursor")
1183 unicode_info, { .i = VIS_ACTION_UNICODE_INFO }
1184 },
1185 [VIS_ACTION_UTF8_INFO] = {
1186 "vis-utf8-info",
1187 VIS_HELP("Show UTF-8 encoded codepoint(s) of character under cursor")
1188 unicode_info, { .i = VIS_ACTION_UTF8_INFO }
1189 },
1190 [VIS_ACTION_NOP] = {
1191 "vis-nop",
1192 VIS_HELP("Ignore key, do nothing")
1193 nop,
1194 },
1195 };
1196
1197 #include "config.h"
1198
1199 /** key bindings functions */
1200
1201 static const char *nop(Vis *vis, const char *keys, const Arg *arg) {
1202 return keys;
1203 }
1204
1205 static const char *macro_record(Vis *vis, const char *keys, const Arg *arg) {
1206 if (!vis_macro_record_stop(vis)) {
1207 if (!keys[0])
1208 return NULL;
1209 const char *next = vis_keys_next(vis, keys);
1210 if (next - keys > 1)
1211 return next;
1212 enum VisRegister reg = vis_register_from(vis, keys[0]);
1213 vis_macro_record(vis, reg);
1214 keys++;
1215 }
1216 vis_draw(vis);
1217 return keys;
1218 }
1219
1220 static const char *macro_replay(Vis *vis, const char *keys, const Arg *arg) {
1221 if (!keys[0])
1222 return NULL;
1223 const char *next = vis_keys_next(vis, keys);
1224 if (next - keys > 1)
1225 return next;
1226 enum VisRegister reg = vis_register_from(vis, keys[0]);
1227 vis_macro_replay(vis, reg);
1228 return keys+1;
1229 }
1230
1231 static const char *suspend(Vis *vis, const char *keys, const Arg *arg) {
1232 ui_terminal_suspend(&vis->ui);
1233 return keys;
1234 }
1235
1236 static const char *repeat(Vis *vis, const char *keys, const Arg *arg) {
1237 vis_repeat(vis);
1238 return keys;
1239 }
1240
1241 static const char *selections_new(Vis *vis, const char *keys, const Arg *arg) {
1242 View *view = vis_view(vis);
1243 bool anchored = view_selections_primary_get(view)->anchored;
1244 VisCountIterator it = vis_count_iterator_get(vis, 1);
1245 while (vis_count_iterator_next(&it)) {
1246 Selection *sel = NULL;
1247 switch (arg->i) {
1248 case -1:
1249 case +1:
1250 sel = view_selections_primary_get(view);
1251 break;
1252 case INT_MIN:
1253 sel = view_selections(view);
1254 break;
1255 case INT_MAX:
1256 for (Selection *s = view_selections(view); s; s = view_selections_next(s))
1257 sel = s;
1258 break;
1259 }
1260
1261 if (!sel)
1262 return keys;
1263
1264 size_t oldpos = view_cursors_pos(sel);
1265 if (arg->i > 0)
1266 view_line_down(sel);
1267 else if (arg->i < 0)
1268 view_line_up(sel);
1269 size_t newpos = view_cursors_pos(sel);
1270 view_cursors_to(sel, oldpos);
1271 Selection *sel_new = view_selections_new(view, newpos);
1272 if (!sel_new) {
1273 if (arg->i == -1)
1274 sel_new = view_selections_prev(sel);
1275 else if (arg->i == +1)
1276 sel_new = view_selections_next(sel);
1277 }
1278 if (sel_new) {
1279 view_selections_primary_set(sel_new);
1280 sel_new->anchored = anchored;
1281 }
1282 }
1283 vis->action.count = VIS_COUNT_UNKNOWN;
1284 return keys;
1285 }
1286
1287 static const char *selections_align(Vis *vis, const char *keys, const Arg *arg) {
1288 View *view = vis_view(vis);
1289 Text *txt = vis_text(vis);
1290 int mincol = INT_MAX;
1291 for (Selection *s = view_selections(view); s; s = view_selections_next(s)) {
1292 if (!s->line)
1293 continue;
1294 if (s->col >= 0 && s->col < mincol)
1295 mincol = s->col;
1296 }
1297 for (Selection *s = view_selections(view); s; s = view_selections_next(s)) {
1298 if (view_cursors_cell_set(s, mincol) == -1) {
1299 size_t pos = view_cursors_pos(s);
1300 size_t col = text_line_width_set(txt, pos, mincol);
1301 view_cursors_to(s, col);
1302 }
1303 }
1304 return keys;
1305 }
1306
1307 static const char *selections_align_indent(Vis *vis, const char *keys, const Arg *arg) {
1308 View *view = vis_view(vis);
1309 Text *txt = vis_text(vis);
1310 bool left_align = arg->i < 0;
1311 int columns = view_selections_column_count(view);
1312
1313 for (int i = 0; i < columns; i++) {
1314 int mincol = INT_MAX, maxcol = 0;
1315 for (Selection *s = view_selections_column(view, i); s; s = view_selections_column_next(s, i)) {
1316 Filerange sel = view_selections_get(s);
1317 size_t pos = left_align ? sel.start : sel.end;
1318 int col = text_line_width_get(txt, pos);
1319 if (col < mincol)
1320 mincol = col;
1321 if (col > maxcol)
1322 maxcol = col;
1323 }
1324
1325 size_t len = maxcol - mincol;
1326 char *buf = malloc(len+1);
1327 if (!buf)
1328 return keys;
1329 memset(buf, ' ', len);
1330
1331 for (Selection *s = view_selections_column(view, i); s; s = view_selections_column_next(s, i)) {
1332 Filerange sel = view_selections_get(s);
1333 size_t pos = left_align ? sel.start : sel.end;
1334 size_t ipos = sel.start;
1335 int col = text_line_width_get(txt, pos);
1336 if (col < maxcol) {
1337 size_t off = maxcol - col;
1338 if (off <= len)
1339 text_insert(txt, ipos, buf, off);
1340 }
1341 }
1342
1343 free(buf);
1344 }
1345
1346 view_draw(view);
1347 return keys;
1348 }
1349
1350 static const char *selections_clear(Vis *vis, const char *keys, const Arg *arg) {
1351 View *view = vis_view(vis);
1352 if (view->selection_count > 1)
1353 view_selections_dispose_all(view);
1354 else
1355 view_selection_clear(view_selections_primary_get(view));
1356 return keys;
1357 }
1358
1359 static Selection *selection_new(View *view, Filerange *r, bool isprimary) {
1360 Text *txt = view->text;
1361 size_t pos = text_char_prev(txt, r->end);
1362 Selection *s = view_selections_new(view, pos);
1363 if (!s)
1364 return NULL;
1365 view_selections_set(s, r);
1366 s->anchored = true;
1367 if (isprimary)
1368 view_selections_primary_set(s);
1369 return s;
1370 }
1371
1372 static const char *selections_match_next(Vis *vis, const char *keys, const Arg *arg) {
1373 Text *txt = vis_text(vis);
1374 View *view = vis_view(vis);
1375 Selection *s = view_selections_primary_get(view);
1376 Filerange sel = view_selections_get(s);
1377 if (!text_range_valid(&sel))
1378 return keys;
1379
1380 static bool match_word;
1381
1382 if (view->selection_count == 1) {
1383 Filerange word = text_object_word(txt, view_cursors_pos(s));
1384 match_word = text_range_equal(&sel, &word);
1385 }
1386
1387 Filerange (*find_next)(Text *, size_t, const char *) = text_object_word_find_next;
1388 Filerange (*find_prev)(Text *, size_t, const char *) = text_object_word_find_prev;
1389 if (!match_word) {
1390 find_next = text_object_find_next;
1391 find_prev = text_object_find_prev;
1392 }
1393
1394 char *buf = text_bytes_alloc0(txt, sel.start, text_range_size(&sel));
1395 if (!buf)
1396 return keys;
1397
1398 bool match_all = arg->b;
1399 Filerange primary = sel;
1400
1401 for (;;) {
1402 sel = find_next(txt, sel.end, buf);
1403 if (!text_range_valid(&sel))
1404 break;
1405 if (selection_new(view, &sel, !match_all) && !match_all)
1406 goto out;
1407 }
1408
1409 sel = primary;
1410
1411 for (;;) {
1412 sel = find_prev(txt, sel.start, buf);
1413 if (!text_range_valid(&sel))
1414 break;
1415 if (selection_new(view, &sel, !match_all) && !match_all)
1416 break;
1417 }
1418
1419 out:
1420 free(buf);
1421 return keys;
1422 }
1423
1424 static const char *selections_match_skip(Vis *vis, const char *keys, const Arg *arg) {
1425 View *view = vis_view(vis);
1426 Selection *sel = view_selections_primary_get(view);
1427 keys = selections_match_next(vis, keys, arg);
1428 if (sel != view_selections_primary_get(view))
1429 view_selections_dispose(sel);
1430 return keys;
1431 }
1432
1433 static const char *selections_remove(Vis *vis, const char *keys, const Arg *arg) {
1434 View *view = vis_view(vis);
1435 view_selections_dispose(view_selections_primary_get(view));
1436 view_cursors_to(view->selection, view_cursor_get(view));
1437 return keys;
1438 }
1439
1440 static const char *selections_remove_column(Vis *vis, const char *keys, const Arg *arg) {
1441 View *view = vis_view(vis);
1442 int max = view_selections_column_count(view);
1443 int column = VIS_COUNT_DEFAULT(vis->action.count, arg->i) - 1;
1444 if (column >= max)
1445 column = max - 1;
1446 if (view->selection_count == 1) {
1447 vis_keys_feed(vis, "<Escape>");
1448 return keys;
1449 }
1450
1451 for (Selection *s = view_selections_column(view, column), *next; s; s = next) {
1452 next = view_selections_column_next(s, column);
1453 view_selections_dispose(s);
1454 }
1455
1456 vis->action.count = VIS_COUNT_UNKNOWN;
1457 return keys;
1458 }
1459
1460 static const char *selections_remove_column_except(Vis *vis, const char *keys, const Arg *arg) {
1461 View *view = vis_view(vis);
1462 int max = view_selections_column_count(view);
1463 int column = VIS_COUNT_DEFAULT(vis->action.count, arg->i) - 1;
1464 if (column >= max)
1465 column = max - 1;
1466 if (view->selection_count == 1) {
1467 vis_redraw(vis);
1468 return keys;
1469 }
1470
1471 Selection *sel = view_selections(view);
1472 Selection *col = view_selections_column(view, column);
1473 for (Selection *next; sel; sel = next) {
1474 next = view_selections_next(sel);
1475 if (sel == col)
1476 col = view_selections_column_next(col, column);
1477 else
1478 view_selections_dispose(sel);
1479 }
1480
1481 vis->action.count = VIS_COUNT_UNKNOWN;
1482 return keys;
1483 }
1484
1485 static const char *selections_navigate(Vis *vis, const char *keys, const Arg *arg) {
1486 View *view = vis_view(vis);
1487 if (view->selection_count == 1)
1488 return wscroll(vis, keys, arg);
1489 Selection *s = view_selections_primary_get(view);
1490 VisCountIterator it = vis_count_iterator_get(vis, 1);
1491 while (vis_count_iterator_next(&it)) {
1492 if (arg->i > 0) {
1493 s = view_selections_next(s);
1494 if (!s)
1495 s = view_selections(view);
1496 } else {
1497 s = view_selections_prev(s);
1498 if (!s) {
1499 s = view_selections(view);
1500 for (Selection *n = s; n; n = view_selections_next(n))
1501 s = n;
1502 }
1503 }
1504 }
1505 view_selections_primary_set(s);
1506 vis->action.count = VIS_COUNT_UNKNOWN;
1507 return keys;
1508 }
1509
1510 static const char *selections_rotate(Vis *vis, const char *keys, const Arg *arg) {
1511
1512 typedef struct {
1513 Selection *sel;
1514 char *data;
1515 size_t len;
1516 } Rotate;
1517
1518 Array arr;
1519 Text *txt = vis_text(vis);
1520 View *view = vis_view(vis);
1521 int columns = view_selections_column_count(view);
1522 int selections = columns == 1 ? view->selection_count : columns;
1523 int count = VIS_COUNT_DEFAULT(vis->action.count, 1);
1524 array_init_sized(&arr, sizeof(Rotate));
1525 if (!array_reserve(&arr, selections))
1526 return keys;
1527 size_t line = 0;
1528
1529 for (Selection *s = view_selections(view), *next; s; s = next) {
1530 next = view_selections_next(s);
1531 size_t line_next = 0;
1532
1533 Filerange sel = view_selections_get(s);
1534 Rotate rot;
1535 rot.sel = s;
1536 rot.len = text_range_size(&sel);
1537 if ((rot.data = malloc(rot.len)))
1538 rot.len = text_bytes_get(txt, sel.start, rot.len, rot.data);
1539 else
1540 rot.len = 0;
1541 array_add(&arr, &rot);
1542
1543 if (!line)
1544 line = text_lineno_by_pos(txt, view_cursors_pos(s));
1545 if (next)
1546 line_next = text_lineno_by_pos(txt, view_cursors_pos(next));
1547 if (!next || (columns > 1 && line != line_next)) {
1548 size_t len = arr.len;
1549 size_t off = arg->i > 0 ? count % len : len - (count % len);
1550 for (size_t i = 0; i < len; i++) {
1551 size_t j = (i + off) % len;
1552 Rotate *oldrot = array_get(&arr, i);
1553 Rotate *newrot = array_get(&arr, j);
1554 if (!oldrot || !newrot || oldrot == newrot)
1555 continue;
1556 Filerange newsel = view_selections_get(newrot->sel);
1557 if (!text_range_valid(&newsel))
1558 continue;
1559 if (!text_delete_range(txt, &newsel))
1560 continue;
1561 if (!text_insert(txt, newsel.start, oldrot->data, oldrot->len))
1562 continue;
1563 newsel.end = newsel.start + oldrot->len;
1564 view_selections_set(newrot->sel, &newsel);
1565 free(oldrot->data);
1566 }
1567 array_clear(&arr);
1568 }
1569 line = line_next;
1570 }
1571
1572 array_release(&arr);
1573 vis->action.count = VIS_COUNT_UNKNOWN;
1574 return keys;
1575 }
1576
1577 static const char *selections_trim(Vis *vis, const char *keys, const Arg *arg) {
1578 Text *txt = vis_text(vis);
1579 View *view = vis_view(vis);
1580 for (Selection *s = view_selections(view), *next; s; s = next) {
1581 next = view_selections_next(s);
1582 Filerange sel = view_selections_get(s);
1583 if (!text_range_valid(&sel))
1584 continue;
1585 for (char b; sel.start < sel.end && text_byte_get(txt, sel.end-1, &b)
1586 && isspace((unsigned char)b); sel.end--);
1587 for (char b; sel.start <= sel.end && text_byte_get(txt, sel.start, &b)
1588 && isspace((unsigned char)b); sel.start++);
1589 if (sel.start < sel.end) {
1590 view_selections_set(s, &sel);
1591 } else if (!view_selections_dispose(s)) {
1592 vis_mode_switch(vis, VIS_MODE_NORMAL);
1593 }
1594 }
1595 return keys;
1596 }
1597
1598 static void selections_set(Vis *vis, View *view, Array *sel) {
1599 enum VisMode mode = vis->mode->id;
1600 bool anchored = mode == VIS_MODE_VISUAL || mode == VIS_MODE_VISUAL_LINE;
1601 view_selections_set_all(view, sel, anchored);
1602 if (!anchored)
1603 view_selections_clear_all(view);
1604 }
1605
1606 static const char *selections_save(Vis *vis, const char *keys, const Arg *arg) {
1607 Win *win = vis->win;
1608 View *view = vis_view(vis);
1609 enum VisMark mark = vis_mark_used(vis);
1610 Array sel = view_selections_get_all(view);
1611 vis_mark_set(win, mark, &sel);
1612 array_release(&sel);
1613 vis_cancel(vis);
1614 return keys;
1615 }
1616
1617 static const char *selections_restore(Vis *vis, const char *keys, const Arg *arg) {
1618 Win *win = vis->win;
1619 View *view = vis_view(vis);
1620 enum VisMark mark = vis_mark_used(vis);
1621 Array sel = vis_mark_get(win, mark);
1622 selections_set(vis, view, &sel);
1623 array_release(&sel);
1624 vis_cancel(vis);
1625 return keys;
1626 }
1627
1628 static const char *selections_union(Vis *vis, const char *keys, const Arg *arg) {
1629 Win *win = vis->win;
1630 View *view = vis_view(vis);
1631 enum VisMark mark = vis_mark_used(vis);
1632 Array a = vis_mark_get(win, mark);
1633 Array b = view_selections_get_all(view);
1634 Array sel;
1635 array_init_from(&sel, &a);
1636
1637 size_t i = 0, j = 0;
1638 Filerange *r1 = array_get(&a, i), *r2 = array_get(&b, j), cur = text_range_empty();
1639 while (r1 || r2) {
1640 if (r1 && text_range_overlap(r1, &cur)) {
1641 cur = text_range_union(r1, &cur);
1642 r1 = array_get(&a, ++i);
1643 } else if (r2 && text_range_overlap(r2, &cur)) {
1644 cur = text_range_union(r2, &cur);
1645 r2 = array_get(&b, ++j);
1646 } else {
1647 if (text_range_valid(&cur))
1648 array_add(&sel, &cur);
1649 if (!r1) {
1650 cur = *r2;
1651 r2 = array_get(&b, ++j);
1652 } else if (!r2) {
1653 cur = *r1;
1654 r1 = array_get(&a, ++i);
1655 } else {
1656 if (r1->start < r2->start) {
1657 cur = *r1;
1658 r1 = array_get(&a, ++i);
1659 } else {
1660 cur = *r2;
1661 r2 = array_get(&b, ++j);
1662 }
1663 }
1664 }
1665 }
1666
1667 if (text_range_valid(&cur))
1668 array_add(&sel, &cur);
1669
1670 selections_set(vis, view, &sel);
1671 vis_cancel(vis);
1672
1673 array_release(&a);
1674 array_release(&b);
1675 array_release(&sel);
1676
1677 return keys;
1678 }
1679
1680 static void intersect(Array *ret, Array *a, Array *b) {
1681 size_t i = 0, j = 0;
1682 Filerange *r1 = array_get(a, i), *r2 = array_get(b, j);
1683 while (r1 && r2) {
1684 if (text_range_overlap(r1, r2)) {
1685 Filerange new = text_range_intersect(r1, r2);
1686 array_add(ret, &new);
1687 }
1688 if (r1->end < r2->end)
1689 r1 = array_get(a, ++i);
1690 else
1691 r2 = array_get(b, ++j);
1692 }
1693 }
1694
1695 static const char *selections_intersect(Vis *vis, const char *keys, const Arg *arg) {
1696 Win *win = vis->win;
1697 View *view = vis_view(vis);
1698 enum VisMark mark = vis_mark_used(vis);
1699 Array a = vis_mark_get(win, mark);
1700 Array b = view_selections_get_all(view);
1701 Array sel;
1702 array_init_from(&sel, &a);
1703
1704 intersect(&sel, &a, &b);
1705 selections_set(vis, view, &sel);
1706 vis_cancel(vis);
1707
1708 array_release(&a);
1709 array_release(&b);
1710 array_release(&sel);
1711
1712 return keys;
1713 }
1714
1715 static void complement(Array *ret, Array *a, Filerange *universe) {
1716 size_t pos = universe->start;
1717 for (size_t i = 0, len = a->len; i < len; i++) {
1718 Filerange *r = array_get(a, i);
1719 if (pos < r->start) {
1720 Filerange new = text_range_new(pos, r->start);
1721 array_add(ret, &new);
1722 }
1723 pos = r->end;
1724 }
1725 if (pos < universe->end) {
1726 Filerange new = text_range_new(pos, universe->end);
1727 array_add(ret, &new);
1728 }
1729 }
1730
1731 static const char *selections_complement(Vis *vis, const char *keys, const Arg *arg) {
1732 Text *txt = vis_text(vis);
1733 View *view = vis_view(vis);
1734 Filerange universe = text_object_entire(txt, 0);
1735 Array a = view_selections_get_all(view);
1736 Array sel;
1737 array_init_from(&sel, &a);
1738
1739 complement(&sel, &a, &universe);
1740
1741 selections_set(vis, view, &sel);
1742 array_release(&a);
1743 array_release(&sel);
1744 return keys;
1745 }
1746
1747 static const char *selections_minus(Vis *vis, const char *keys, const Arg *arg) {
1748 Text *txt = vis_text(vis);
1749 Win *win = vis->win;
1750 View *view = vis_view(vis);
1751 enum VisMark mark = vis_mark_used(vis);
1752 Array a = view_selections_get_all(view);
1753 Array b = vis_mark_get(win, mark);
1754 Array sel;
1755 array_init_from(&sel, &a);
1756 Array b_complement;
1757 array_init_from(&b_complement, &b);
1758
1759 Filerange universe = text_object_entire(txt, 0);
1760 complement(&b_complement, &b, &universe);
1761 intersect(&sel, &a, &b_complement);
1762
1763 selections_set(vis, view, &sel);
1764 vis_cancel(vis);
1765
1766 array_release(&a);
1767 array_release(&b);
1768 array_release(&b_complement);
1769 array_release(&sel);
1770
1771 return keys;
1772 }
1773
1774 static const char *replace(Vis *vis, const char *keys, const Arg *arg) {
1775 if (!keys[0]) {
1776 vis_keymap_disable(vis);
1777 return NULL;
1778 }
1779
1780 const char *next = vis_keys_next(vis, keys);
1781 if (!next)
1782 return NULL;
1783
1784 char replacement[UTFmax+1];
1785 if (!vis_keys_utf8(vis, keys, replacement))
1786 return next;
1787
1788 if (replacement[0] == 0x1b) /* <Escape> */
1789 return next;
1790
1791 vis_operator(vis, VIS_OP_REPLACE, replacement);
1792 if (vis->mode->id == VIS_MODE_OPERATOR_PENDING)
1793 vis_motion(vis, VIS_MOVE_CHAR_NEXT);
1794 return next;
1795 }
1796
1797 static const char *count(Vis *vis, const char *keys, const Arg *arg) {
1798 int digit = keys[-1] - '0';
1799 int count = VIS_COUNT_DEFAULT(vis->action.count, 0);
1800 if (0 <= digit && digit <= 9) {
1801 if (digit == 0 && count == 0)
1802 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
1803 else
1804 vis->action.count = VIS_COUNT_NORMALIZE(count * 10 + digit);
1805 }
1806 return keys;
1807 }
1808
1809 static const char *gotoline(Vis *vis, const char *keys, const Arg *arg) {
1810 if (vis->action.count != VIS_COUNT_UNKNOWN)
1811 vis_motion(vis, VIS_MOVE_LINE);
1812 else if (arg->i < 0)
1813 vis_motion(vis, VIS_MOVE_FILE_BEGIN);
1814 else
1815 vis_motion(vis, VIS_MOVE_FILE_END);
1816 return keys;
1817 }
1818
1819 static const char *operator(Vis *vis, const char *keys, const Arg *arg) {
1820 vis_operator(vis, arg->i);
1821 return keys;
1822 }
1823
1824 static const char *movement_key(Vis *vis, const char *keys, const Arg *arg) {
1825 if (!keys[0]) {
1826 vis_keymap_disable(vis);
1827 return NULL;
1828 }
1829
1830 const char *next = vis_keys_next(vis, keys);
1831 if (!next)
1832 return NULL;
1833 char utf8[UTFmax+1];
1834 if (vis_keys_utf8(vis, keys, utf8))
1835 vis_motion(vis, arg->i, utf8);
1836 return next;
1837 }
1838
1839 static const char *movement(Vis *vis, const char *keys, const Arg *arg) {
1840 vis_motion(vis, arg->i);
1841 return keys;
1842 }
1843
1844 static const char *textobj(Vis *vis, const char *keys, const Arg *arg) {
1845 vis_textobject(vis, arg->i);
1846 return keys;
1847 }
1848
1849 static const char *selection_end(Vis *vis, const char *keys, const Arg *arg) {
1850 for (Selection *s = view_selections(vis_view(vis)); s; s = view_selections_next(s))
1851 view_selections_flip(s);
1852 return keys;
1853 }
1854
1855 static const char *reg(Vis *vis, const char *keys, const Arg *arg) {
1856 if (!keys[0])
1857 return NULL;
1858 const char *next = vis_keys_next(vis, keys);
1859 if (next - keys > 1)
1860 return next;
1861 enum VisRegister reg = vis_register_from(vis, keys[0]);
1862 vis_register(vis, reg);
1863 return keys+1;
1864 }
1865
1866 static const char *mark(Vis *vis, const char *keys, const Arg *arg) {
1867 if (!keys[0])
1868 return NULL;
1869 const char *next = vis_keys_next(vis, keys);
1870 if (next - keys > 1)
1871 return next;
1872 enum VisMark mark = vis_mark_from(vis, keys[0]);
1873 vis_mark(vis, mark);
1874 return keys+1;
1875 }
1876
1877 static const char *undo(Vis *vis, const char *keys, const Arg *arg) {
1878 size_t pos = text_undo(vis_text(vis));
1879 if (pos != EPOS) {
1880 View *view = vis_view(vis);
1881 if (view->selection_count == 1)
1882 view_cursors_to(view->selection, pos);
1883 /* redraw all windows in case some display the same file */
1884 vis_draw(vis);
1885 }
1886 return keys;
1887 }
1888
1889 static const char *redo(Vis *vis, const char *keys, const Arg *arg) {
1890 size_t pos = text_redo(vis_text(vis));
1891 if (pos != EPOS) {
1892 View *view = vis_view(vis);
1893 if (view->selection_count == 1)
1894 view_cursors_to(view->selection, pos);
1895 /* redraw all windows in case some display the same file */
1896 vis_draw(vis);
1897 }
1898 return keys;
1899 }
1900
1901 static const char *earlier(Vis *vis, const char *keys, const Arg *arg) {
1902 size_t pos = EPOS;
1903 VisCountIterator it = vis_count_iterator_get(vis, 1);
1904 while (vis_count_iterator_next(&it))
1905 pos = text_earlier(vis_text(vis));
1906 if (pos != EPOS) {
1907 view_cursors_to(vis_view(vis)->selection, pos);
1908 /* redraw all windows in case some display the same file */
1909 vis_draw(vis);
1910 }
1911 return keys;
1912 }
1913
1914 static const char *later(Vis *vis, const char *keys, const Arg *arg) {
1915 size_t pos = EPOS;
1916 VisCountIterator it = vis_count_iterator_get(vis, 1);
1917 while (vis_count_iterator_next(&it))
1918 pos = text_later(vis_text(vis));
1919 if (pos != EPOS) {
1920 view_cursors_to(vis_view(vis)->selection, pos);
1921 /* redraw all windows in case some display the same file */
1922 vis_draw(vis);
1923 }
1924 return keys;
1925 }
1926
1927 static const char *delete(Vis *vis, const char *keys, const Arg *arg) {
1928 vis_operator(vis, VIS_OP_DELETE);
1929 vis_motion(vis, arg->i);
1930 return keys;
1931 }
1932
1933 static const char *insert_register(Vis *vis, const char *keys, const Arg *arg) {
1934 if (!keys[0])
1935 return NULL;
1936 const char *next = vis_keys_next(vis, keys);
1937 if (next - keys > 1)
1938 return next;
1939 enum VisRegister reg = vis_register_from(vis, keys[0]);
1940 if (reg != VIS_REG_INVALID) {
1941 vis_register(vis, reg);
1942 vis_operator(vis, VIS_OP_PUT_BEFORE_END);
1943 }
1944 return keys+1;
1945 }
1946
1947 static const char *prompt_show(Vis *vis, const char *keys, const Arg *arg) {
1948 vis_prompt_show(vis, arg->s);
1949 return keys;
1950 }
1951
1952 static const char *insert_verbatim(Vis *vis, const char *keys, const Arg *arg) {
1953 Rune rune = 0;
1954 char buf[4], type = keys[0];
1955 const char *data = NULL;
1956 int len = 0, count = 0, base = 0;
1957 switch (type) {
1958 case '\0':
1959 return NULL;
1960 case 'o':
1961 case 'O':
1962 count = 3;
1963 base = 8;
1964 break;
1965 case 'U':
1966 count = 4;
1967 /* fall through */
1968 case 'u':
1969 count += 4;
1970 base = 16;
1971 break;
1972 case 'x':
1973 case 'X':
1974 count = 2;
1975 base = 16;
1976 break;
1977 default:
1978 if ('0' <= type && type <= '9') {
1979 rune = type - '0';
1980 count = 2;
1981 base = 10;
1982 }
1983 break;
1984 }
1985
1986 if (base) {
1987 for (keys++; keys[0] && count > 0; keys++, count--) {
1988 int v = 0;
1989 if (base == 8 && '0' <= keys[0] && keys[0] <= '7') {
1990 v = keys[0] - '0';
1991 } else if ((base == 10 || base == 16) && '0' <= keys[0] && keys[0] <= '9') {
1992 v = keys[0] - '0';
1993 } else if (base == 16 && 'a' <= keys[0] && keys[0] <= 'f') {
1994 v = 10 + keys[0] - 'a';
1995 } else if (base == 16 && 'A' <= keys[0] && keys[0] <= 'F') {
1996 v = 10 + keys[0] - 'A';
1997 } else {
1998 count = 0;
1999 break;
2000 }
2001 rune = rune * base + v;
2002 }
2003
2004 if (count > 0)
2005 return NULL;
2006 if (type == 'u' || type == 'U') {
2007 len = runetochar(buf, &rune);
2008 } else {
2009 buf[0] = rune;
2010 len = 1;
2011 }
2012
2013 data = buf;
2014 } else {
2015 const char *next = vis_keys_next(vis, keys);
2016 if (!next)
2017 return NULL;
2018 if ((rune = vis_keys_codepoint(vis, keys)) != (Rune)-1) {
2019 len = runetochar(buf, &rune);
2020 if (buf[0] == '\n')
2021 buf[0] = '\r';
2022 data = buf;
2023 } else {
2024 vis_info_show(vis, "Unknown key");
2025 }
2026 keys = next;
2027 }
2028
2029 if (len > 0)
2030 vis_insert_key(vis, data, len);
2031 return keys;
2032 }
2033
2034 static const char *wscroll(Vis *vis, const char *keys, const Arg *arg) {
2035 View *view = vis_view(vis);
2036 int count = vis->action.count;
2037 switch (arg->i) {
2038 case -PAGE:
2039 view_scroll_page_up(view);
2040 break;
2041 case +PAGE:
2042 view_scroll_page_down(view);
2043 break;
2044 case -PAGE_HALF:
2045 view_scroll_halfpage_up(view);
2046 break;
2047 case +PAGE_HALF:
2048 view_scroll_halfpage_down(view);
2049 break;
2050 default:
2051 if (count == VIS_COUNT_UNKNOWN)
2052 count = arg->i < 0 ? -arg->i : arg->i;
2053 if (arg->i < 0)
2054 view_scroll_up(view, count);
2055 else
2056 view_scroll_down(view, count);
2057 break;
2058 }
2059 vis->action.count = VIS_COUNT_UNKNOWN;
2060 return keys;
2061 }
2062
2063 static const char *wslide(Vis *vis, const char *keys, const Arg *arg) {
2064 View *view = vis_view(vis);
2065 int count = vis->action.count;
2066 if (count == VIS_COUNT_UNKNOWN)
2067 count = arg->i < 0 ? -arg->i : arg->i;
2068 if (arg->i >= 0)
2069 view_slide_down(view, count);
2070 else
2071 view_slide_up(view, count);
2072 vis->action.count = VIS_COUNT_UNKNOWN;
2073 return keys;
2074 }
2075
2076 static const char *call(Vis *vis, const char *keys, const Arg *arg) {
2077 arg->f(vis);
2078 return keys;
2079 }
2080
2081 static const char *window(Vis *vis, const char *keys, const Arg *arg) {
2082 arg->w(vis_view(vis));
2083 return keys;
2084 }
2085
2086 static const char *openline(Vis *vis, const char *keys, const Arg *arg) {
2087 vis_operator(vis, VIS_OP_MODESWITCH, VIS_MODE_INSERT);
2088 if (arg->i > 0) {
2089 vis_motion(vis, VIS_MOVE_LINE_END);
2090 vis_keys_feed(vis, "<Enter>");
2091 } else {
2092 if (vis->autoindent) {
2093 vis_motion(vis, VIS_MOVE_LINE_START);
2094 vis_keys_feed(vis, "<vis-motion-line-start>");
2095 } else {
2096 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
2097 vis_keys_feed(vis, "<vis-motion-line-begin>");
2098 }
2099 vis_keys_feed(vis, "<Enter><vis-motion-line-up>");
2100 }
2101 return keys;
2102 }
2103
2104 static const char *join(Vis *vis, const char *keys, const Arg *arg) {
2105 bool normal = (vis->mode->id == VIS_MODE_NORMAL);
2106 vis_operator(vis, VIS_OP_JOIN, arg->s);
2107 if (normal) {
2108 vis->action.count = VIS_COUNT_DEFAULT(vis->action.count, 0);
2109 if (vis->action.count > 0)
2110 vis->action.count -= 1;
2111 vis_motion(vis, VIS_MOVE_LINE_NEXT);
2112 }
2113 return keys;
2114 }
2115
2116 static const char *normalmode_escape(Vis *vis, const char *keys, const Arg *arg) {
2117 if (vis->action.count == VIS_COUNT_UNKNOWN)
2118 selections_clear(vis, keys, arg);
2119 else
2120 vis->action.count = VIS_COUNT_UNKNOWN;
2121 return keys;
2122 }
2123
2124 static const char *visualmode_escape(Vis *vis, const char *keys, const Arg *arg) {
2125 if (vis->action.count == VIS_COUNT_UNKNOWN)
2126 vis_mode_switch(vis, VIS_MODE_NORMAL);
2127 else
2128 vis->action.count = VIS_COUNT_UNKNOWN;
2129 return keys;
2130 }
2131
2132 static const char *switchmode(Vis *vis, const char *keys, const Arg *arg) {
2133 vis_mode_switch(vis, arg->i);
2134 return keys;
2135 }
2136
2137 static const char *insertmode(Vis *vis, const char *keys, const Arg *arg) {
2138 vis_operator(vis, VIS_OP_MODESWITCH, VIS_MODE_INSERT);
2139 vis_motion(vis, arg->i);
2140 return keys;
2141 }
2142
2143 static const char *replacemode(Vis *vis, const char *keys, const Arg *arg) {
2144 vis_operator(vis, VIS_OP_MODESWITCH, VIS_MODE_REPLACE);
2145 vis_motion(vis, arg->i);
2146 return keys;
2147 }
2148
2149 static const char *unicode_info(Vis *vis, const char *keys, const Arg *arg) {
2150 View *view = vis_view(vis);
2151 Text *txt = vis_text(vis);
2152 size_t start = view_cursor_get(view);
2153 size_t end = text_char_next(txt, start);
2154 char *grapheme = text_bytes_alloc0(txt, start, end-start), *codepoint = grapheme;
2155 if (!grapheme)
2156 return keys;
2157 Buffer info = {0};
2158 mbstate_t ps = {0};
2159 Iterator it = text_iterator_get(txt, start);
2160 for (size_t pos = start; it.pos < end; pos = it.pos) {
2161 if (!text_iterator_codepoint_next(&it, NULL)) {
2162 vis_info_show(vis, "Failed to parse code point");
2163 goto err;
2164 }
2165 size_t len = it.pos - pos;
2166 wchar_t wc = 0xFFFD;
2167 size_t res = mbrtowc(&wc, codepoint, len, &ps);
2168 bool combining = false;
2169 if (res != (size_t)-1 && res != (size_t)-2)
2170 combining = (wc != L'\0' && wcwidth(wc) == 0);
2171 unsigned char ch = *codepoint;
2172 if (ch < 128 && !isprint(ch))
2173 buffer_appendf(&info, "<^%c> ", ch == 127 ? '?' : ch + 64);
2174 else
2175 buffer_appendf(&info, "<%s%.*s> ", combining ? " " : "", (int)len, codepoint);
2176 if (arg->i == VIS_ACTION_UNICODE_INFO) {
2177 buffer_appendf(&info, "U+%04"PRIX32" ", (uint32_t)wc);
2178 } else {
2179 for (size_t i = 0; i < len; i++)
2180 buffer_appendf(&info, "%02x ", (uint8_t)codepoint[i]);
2181 }
2182 codepoint += len;
2183 }
2184 vis_info_show(vis, "%s", buffer_content0(&info));
2185 err:
2186 free(grapheme);
2187 buffer_release(&info);
2188 return keys;
2189 }
2190
2191 static const char *percent(Vis *vis, const char *keys, const Arg *arg) {
2192 if (vis->action.count == VIS_COUNT_UNKNOWN)
2193 vis_motion(vis, VIS_MOVE_BRACKET_MATCH);
2194 else
2195 vis_motion(vis, VIS_MOVE_PERCENT);
2196 return keys;
2197 }
2198
2199 static const char *jumplist(Vis *vis, const char *keys, const Arg *arg) {
2200 if (arg->i < 0)
2201 vis_jumplist_prev(vis);
2202 else if (arg->i > 0)
2203 vis_jumplist_next(vis);
2204 else
2205 vis_jumplist_save(vis);
2206 return keys;
2207 }
2208
2209 static Vis *vis;
2210
2211 static void signal_handler(int signum, siginfo_t *siginfo, void *context) {
2212 vis_signal_handler(vis, signum, siginfo, context);
2213 }
2214
2215 int main(int argc, char *argv[]) {
2216 for (int i = 1; i < argc; i++) {
2217 if (argv[i][0] != '-') {
2218 continue;
2219 } else if (strcmp(argv[i], "-") == 0) {
2220 continue;
2221 } else if (strcmp(argv[i], "--") == 0) {
2222 break;
2223 } else if (strcmp(argv[i], "-v") == 0) {
2224 printf("vis %s%s%s%s%s%s%s\n", VERSION,
2225 CONFIG_CURSES ? " +curses" : "",
2226 CONFIG_LUA ? " +lua" : "",
2227 CONFIG_LPEG ? " +lpeg" : "",
2228 CONFIG_TRE ? " +tre" : "",
2229 CONFIG_ACL ? " +acl" : "",
2230 CONFIG_SELINUX ? " +selinux" : "");
2231 return 0;
2232 } else {
2233 fprintf(stderr, "Unknown command option: %s\n", argv[i]);
2234 return 1;
2235 }
2236 }
2237
2238 vis = vis_new();
2239 if (!vis)
2240 return EXIT_FAILURE;
2241
2242 vis_event_emit(vis, VIS_EVENT_INIT);
2243
2244 for (int i = 0; i < LENGTH(vis_action); i++) {
2245 const KeyAction *action = &vis_action[i];
2246 if (!vis_action_register(vis, action))
2247 vis_die(vis, "Could not register action: %s\n", action->name);
2248 }
2249
2250 for (int i = 0; i < LENGTH(default_bindings); i++) {
2251 for (const KeyBinding **binding = default_bindings[i]; binding && *binding; binding++) {
2252 for (const KeyBinding *kb = *binding; kb->key; kb++) {
2253 vis_mode_map(vis, i, false, kb->key, kb);
2254 }
2255 }
2256 }
2257
2258 for (const char **k = keymaps; k[0]; k += 2)
2259 vis_keymap_add(vis, k[0], k[1]);
2260
2261 /* install signal handlers etc. */
2262 struct sigaction sa;
2263 memset(&sa, 0, sizeof sa);
2264 sigfillset(&sa.sa_mask);
2265 sa.sa_flags = SA_SIGINFO;
2266 sa.sa_sigaction = signal_handler;
2267 if (sigaction(SIGBUS, &sa, NULL) == -1 ||
2268 sigaction(SIGINT, &sa, NULL) == -1 ||
2269 sigaction(SIGCONT, &sa, NULL) == -1 ||
2270 sigaction(SIGWINCH, &sa, NULL) == -1 ||
2271 sigaction(SIGTERM, &sa, NULL) == -1 ||
2272 sigaction(SIGHUP, &sa, NULL) == -1) {
2273 vis_die(vis, "Failed to set signal handler: %s\n", strerror(errno));
2274 }
2275
2276 sa.sa_handler = SIG_IGN;
2277 if (sigaction(SIGPIPE, &sa, NULL) == -1 || sigaction(SIGQUIT, &sa, NULL) == -1)
2278 vis_die(vis, "Failed to ignore signals\n");
2279
2280 sigset_t blockset;
2281 sigemptyset(&blockset);
2282 sigaddset(&blockset, SIGBUS);
2283 sigaddset(&blockset, SIGCONT);
2284 sigaddset(&blockset, SIGWINCH);
2285 sigaddset(&blockset, SIGTERM);
2286 sigaddset(&blockset, SIGHUP);
2287 if (sigprocmask(SIG_BLOCK, &blockset, NULL) == -1)
2288 vis_die(vis, "Failed to block signals\n");
2289
2290 char *cmd = NULL;
2291 bool end_of_options = false, win_created = false;
2292
2293 for (int i = 1; i < argc; i++) {
2294 if (argv[i][0] == '-' && !end_of_options) {
2295 if (strcmp(argv[i], "-") == 0) {
2296 if (!vis_window_new_fd(vis, STDOUT_FILENO))
2297 vis_die(vis, "Can not create empty buffer\n");
2298 ssize_t len = 0;
2299 char buf[PIPE_BUF];
2300 Text *txt = vis_text(vis);
2301 while ((len = read(STDIN_FILENO, buf, sizeof buf)) > 0)
2302 text_insert(txt, text_size(txt), buf, len);
2303 if (len == -1)
2304 vis_die(vis, "Can not read from stdin\n");
2305 text_snapshot(txt);
2306 int fd = open("/dev/tty", O_RDWR);
2307 if (fd == -1)
2308 vis_die(vis, "Can not reopen stdin\n");
2309 dup2(fd, STDIN_FILENO);
2310 close(fd);
2311 } else if (strcmp(argv[i], "--") == 0) {
2312 end_of_options = true;
2313 continue;
2314 }
2315 } else if (argv[i][0] == '+' && !end_of_options) {
2316 cmd = argv[i] + (argv[i][1] == '/' || argv[i][1] == '?');
2317 continue;
2318 } else if (!vis_window_new(vis, argv[i])) {
2319 vis_die(vis, "Can not load '%s': %s\n", argv[i], strerror(errno));
2320 }
2321 win_created = true;
2322 if (cmd) {
2323 vis_prompt_cmd(vis, cmd);
2324 cmd = NULL;
2325 }
2326 }
2327
2328 if (!vis->win && !win_created) {
2329 if (!vis_window_new(vis, NULL))
2330 vis_die(vis, "Can not create empty buffer\n");
2331 if (cmd)
2332 vis_prompt_cmd(vis, cmd);
2333 }
2334
2335 int status = vis_run(vis);
2336 vis_free(vis);
2337 return status;
2338 }