Merge branch 'paranthese-typo' of https://github.com/Two-Finger/vis
[vis.git] / main.c
blob8a06bc24f30a0e1e500f1f701a6f334b42a1ce36
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 register */
76 static const char *selections_save(Vis*, const char *keys, const Arg *arg);
77 /* restore selections from register */
78 static const char *selections_restore(Vis*, const char *keys, const Arg *arg);
79 /* union selections */
80 static const char *selections_union(Vis*, const char *keys, const Arg *arg);
81 /* intersect selections */
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 register */
86 static const char *selections_minus(Vis*, const char *keys, const Arg *arg);
87 /* pariwise combine selections */
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 /* set motion type either LINEWISE or CHARWISE via arg->i */
101 static const char *motiontype(Vis*, const char *keys, const Arg *arg);
102 /* make the current action use the operator indicated by arg->i */
103 static const char *operator(Vis*, const char *keys, const Arg *arg);
104 /* blocks to read a key and performs movement indicated by arg->i which
105 * should be one of VIS_MOVE_{RIGHT,LEFT}_{TO,TILL} */
106 static const char *movement_key(Vis*, const char *keys, const Arg *arg);
107 /* perform the movement as indicated by arg->i */
108 static const char *movement(Vis*, const char *keys, const Arg *arg);
109 /* let the current operator affect the range indicated by the text object arg->i */
110 static const char *textobj(Vis*, const char *keys, const Arg *arg);
111 /* move to the other end of selected text */
112 static const char *selection_end(Vis*, const char *keys, const Arg *arg);
113 /* use register indicated by keys for the current operator */
114 static const char *reg(Vis*, const char *keys, const Arg *arg);
115 /* use mark indicated by keys for the current action */
116 static const char *mark(Vis*, const char *keys, const Arg *arg);
117 /* {un,re}do last action, redraw window */
118 static const char *undo(Vis*, const char *keys, const Arg *arg);
119 static const char *redo(Vis*, const char *keys, const Arg *arg);
120 /* earlier, later action chronologically, redraw window */
121 static const char *earlier(Vis*, const char *keys, const Arg *arg);
122 static const char *later(Vis*, const char *keys, const Arg *arg);
123 /* delete from the current cursor position to the end of
124 * movement as indicated by arg->i */
125 static const char *delete(Vis*, const char *keys, const Arg *arg);
126 /* insert register content indicated by keys at current cursor position */
127 static const char *insert_register(Vis*, const char *keys, const Arg *arg);
128 /* show a user prompt to get input with title arg->s */
129 static const char *prompt_show(Vis*, const char *keys, const Arg *arg);
130 /* blocks to read 3 consecutive digits and inserts the corresponding byte value */
131 static const char *insert_verbatim(Vis*, const char *keys, const Arg *arg);
132 /* scroll window content according to arg->i which can be either PAGE, PAGE_HALF,
133 * or an arbitrary number of lines. a multiplier overrides what is given in arg->i.
134 * negative values scroll back, positive forward. */
135 static const char *wscroll(Vis*, const char *keys, const Arg *arg);
136 /* similar to scroll, but do only move window content not cursor position */
137 static const char *wslide(Vis*, const char *keys, const Arg *arg);
138 /* call editor function as indicated by arg->f */
139 static const char *call(Vis*, const char *keys, const Arg *arg);
140 /* call window function as indicated by arg->w */
141 static const char *window(Vis*, const char *keys, const Arg *arg);
142 /* show info about Unicode character at cursor position */
143 static const char *unicode_info(Vis*, const char *keys, const Arg *arg);
144 /* either go to count % of ile or to matching item */
145 static const char *percent(Vis*, const char *keys, const Arg *arg);
146 /* navigate jumplist next (arg->i > 0), prev (arg->i < 0), save (arg->i = 0) */
147 static const char *jumplist(Vis*, const char *keys, const Arg *arg);
149 enum {
150 VIS_ACTION_EDITOR_SUSPEND,
151 VIS_ACTION_CURSOR_CHAR_PREV,
152 VIS_ACTION_CURSOR_CHAR_NEXT,
153 VIS_ACTION_CURSOR_LINE_CHAR_PREV,
154 VIS_ACTION_CURSOR_LINE_CHAR_NEXT,
155 VIS_ACTION_CURSOR_CODEPOINT_PREV,
156 VIS_ACTION_CURSOR_CODEPOINT_NEXT,
157 VIS_ACTION_CURSOR_WORD_START_PREV,
158 VIS_ACTION_CURSOR_WORD_START_NEXT,
159 VIS_ACTION_CURSOR_WORD_END_PREV,
160 VIS_ACTION_CURSOR_WORD_END_NEXT,
161 VIS_ACTION_CURSOR_LONGWORD_START_PREV,
162 VIS_ACTION_CURSOR_LONGWORD_START_NEXT,
163 VIS_ACTION_CURSOR_LONGWORD_END_PREV,
164 VIS_ACTION_CURSOR_LONGWORD_END_NEXT,
165 VIS_ACTION_CURSOR_LINE_UP,
166 VIS_ACTION_CURSOR_LINE_DOWN,
167 VIS_ACTION_CURSOR_LINE_START,
168 VIS_ACTION_CURSOR_LINE_FINISH,
169 VIS_ACTION_CURSOR_LINE_BEGIN,
170 VIS_ACTION_CURSOR_LINE_END,
171 VIS_ACTION_CURSOR_SCREEN_LINE_UP,
172 VIS_ACTION_CURSOR_SCREEN_LINE_DOWN,
173 VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN,
174 VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE,
175 VIS_ACTION_CURSOR_SCREEN_LINE_END,
176 VIS_ACTION_CURSOR_PERCENT,
177 VIS_ACTION_CURSOR_BYTE,
178 VIS_ACTION_CURSOR_BYTE_LEFT,
179 VIS_ACTION_CURSOR_BYTE_RIGHT,
180 VIS_ACTION_CURSOR_PARAGRAPH_PREV,
181 VIS_ACTION_CURSOR_PARAGRAPH_NEXT,
182 VIS_ACTION_CURSOR_SENTENCE_PREV,
183 VIS_ACTION_CURSOR_SENTENCE_NEXT,
184 VIS_ACTION_CURSOR_BLOCK_START,
185 VIS_ACTION_CURSOR_BLOCK_END,
186 VIS_ACTION_CURSOR_PARENTHESE_START,
187 VIS_ACTION_CURSOR_PARENTHESE_END,
188 VIS_ACTION_CURSOR_COLUMN,
189 VIS_ACTION_CURSOR_LINE_FIRST,
190 VIS_ACTION_CURSOR_LINE_LAST,
191 VIS_ACTION_CURSOR_WINDOW_LINE_TOP,
192 VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE,
193 VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM,
194 VIS_ACTION_CURSOR_SEARCH_REPEAT_FORWARD,
195 VIS_ACTION_CURSOR_SEARCH_REPEAT_BACKWARD,
196 VIS_ACTION_CURSOR_SEARCH_REPEAT,
197 VIS_ACTION_CURSOR_SEARCH_REPEAT_REVERSE,
198 VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD,
199 VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD,
200 VIS_ACTION_WINDOW_PAGE_UP,
201 VIS_ACTION_WINDOW_PAGE_DOWN,
202 VIS_ACTION_WINDOW_HALFPAGE_UP,
203 VIS_ACTION_WINDOW_HALFPAGE_DOWN,
204 VIS_ACTION_MODE_NORMAL,
205 VIS_ACTION_MODE_VISUAL,
206 VIS_ACTION_MODE_VISUAL_LINE,
207 VIS_ACTION_MODE_INSERT,
208 VIS_ACTION_MODE_REPLACE,
209 VIS_ACTION_DELETE_CHAR_PREV,
210 VIS_ACTION_DELETE_CHAR_NEXT,
211 VIS_ACTION_DELETE_LINE_BEGIN,
212 VIS_ACTION_DELETE_WORD_PREV,
213 VIS_ACTION_JUMPLIST_PREV,
214 VIS_ACTION_JUMPLIST_NEXT,
215 VIS_ACTION_JUMPLIST_SAVE,
216 VIS_ACTION_UNDO,
217 VIS_ACTION_REDO,
218 VIS_ACTION_EARLIER,
219 VIS_ACTION_LATER,
220 VIS_ACTION_MACRO_RECORD,
221 VIS_ACTION_MACRO_REPLAY,
222 VIS_ACTION_MARK,
223 VIS_ACTION_REDRAW,
224 VIS_ACTION_REPLACE_CHAR,
225 VIS_ACTION_TOTILL_REPEAT,
226 VIS_ACTION_TOTILL_REVERSE,
227 VIS_ACTION_PROMPT_SEARCH_FORWARD,
228 VIS_ACTION_PROMPT_SEARCH_BACKWARD,
229 VIS_ACTION_TILL_LEFT,
230 VIS_ACTION_TILL_RIGHT,
231 VIS_ACTION_TO_LEFT,
232 VIS_ACTION_TO_RIGHT,
233 VIS_ACTION_REGISTER,
234 VIS_ACTION_OPERATOR_CHANGE,
235 VIS_ACTION_OPERATOR_DELETE,
236 VIS_ACTION_OPERATOR_YANK,
237 VIS_ACTION_OPERATOR_SHIFT_LEFT,
238 VIS_ACTION_OPERATOR_SHIFT_RIGHT,
239 VIS_ACTION_OPERATOR_CASE_LOWER,
240 VIS_ACTION_OPERATOR_CASE_UPPER,
241 VIS_ACTION_OPERATOR_CASE_SWAP,
242 VIS_ACTION_COUNT,
243 VIS_ACTION_INSERT_NEWLINE,
244 VIS_ACTION_INSERT_TAB,
245 VIS_ACTION_INSERT_VERBATIM,
246 VIS_ACTION_INSERT_REGISTER,
247 VIS_ACTION_WINDOW_NEXT,
248 VIS_ACTION_WINDOW_PREV,
249 VIS_ACTION_APPEND_CHAR_NEXT,
250 VIS_ACTION_APPEND_LINE_END,
251 VIS_ACTION_INSERT_LINE_START,
252 VIS_ACTION_OPEN_LINE_ABOVE,
253 VIS_ACTION_OPEN_LINE_BELOW,
254 VIS_ACTION_JOIN_LINES,
255 VIS_ACTION_JOIN_LINES_TRIM,
256 VIS_ACTION_PROMPT_SHOW,
257 VIS_ACTION_REPEAT,
258 VIS_ACTION_SELECTION_FLIP,
259 VIS_ACTION_WINDOW_REDRAW_TOP,
260 VIS_ACTION_WINDOW_REDRAW_CENTER,
261 VIS_ACTION_WINDOW_REDRAW_BOTTOM,
262 VIS_ACTION_WINDOW_SLIDE_UP,
263 VIS_ACTION_WINDOW_SLIDE_DOWN,
264 VIS_ACTION_PUT_AFTER,
265 VIS_ACTION_PUT_BEFORE,
266 VIS_ACTION_PUT_AFTER_END,
267 VIS_ACTION_PUT_BEFORE_END,
268 VIS_ACTION_SELECTIONS_MATCH_WORD,
269 VIS_ACTION_SELECTIONS_NEW_LINE_ABOVE,
270 VIS_ACTION_SELECTIONS_NEW_LINE_ABOVE_FIRST,
271 VIS_ACTION_SELECTIONS_NEW_LINE_BELOW,
272 VIS_ACTION_SELECTIONS_NEW_LINE_BELOW_LAST,
273 VIS_ACTION_SELECTIONS_NEW_LINES_BEGIN,
274 VIS_ACTION_SELECTIONS_NEW_LINES_END,
275 VIS_ACTION_SELECTIONS_NEW_MATCH_NEXT,
276 VIS_ACTION_SELECTIONS_NEW_MATCH_SKIP,
277 VIS_ACTION_SELECTIONS_ALIGN,
278 VIS_ACTION_SELECTIONS_ALIGN_INDENT_LEFT,
279 VIS_ACTION_SELECTIONS_ALIGN_INDENT_RIGHT,
280 VIS_ACTION_SELECTIONS_REMOVE_ALL,
281 VIS_ACTION_SELECTIONS_REMOVE_LAST,
282 VIS_ACTION_SELECTIONS_REMOVE_COLUMN,
283 VIS_ACTION_SELECTIONS_REMOVE_COLUMN_EXCEPT,
284 VIS_ACTION_SELECTIONS_PREV,
285 VIS_ACTION_SELECTIONS_NEXT,
286 VIS_ACTION_SELECTIONS_ROTATE_LEFT,
287 VIS_ACTION_SELECTIONS_ROTATE_RIGHT,
288 VIS_ACTION_SELECTIONS_TRIM,
289 VIS_ACTION_SELECTIONS_SAVE,
290 VIS_ACTION_SELECTIONS_RESTORE,
291 VIS_ACTION_SELECTIONS_UNION,
292 VIS_ACTION_SELECTIONS_INTERSECT,
293 VIS_ACTION_SELECTIONS_COMPLEMENT,
294 VIS_ACTION_SELECTIONS_MINUS,
295 VIS_ACTION_SELECTIONS_COMBINE_UNION,
296 VIS_ACTION_SELECTIONS_COMBINE_INTERSECT,
297 VIS_ACTION_SELECTIONS_COMBINE_LONGER,
298 VIS_ACTION_SELECTIONS_COMBINE_SHORTER,
299 VIS_ACTION_SELECTIONS_COMBINE_LEFTMOST,
300 VIS_ACTION_SELECTIONS_COMBINE_RIGHTMOST,
301 VIS_ACTION_TEXT_OBJECT_WORD_OUTER,
302 VIS_ACTION_TEXT_OBJECT_WORD_INNER,
303 VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER,
304 VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER,
305 VIS_ACTION_TEXT_OBJECT_SENTENCE,
306 VIS_ACTION_TEXT_OBJECT_PARAGRAPH,
307 VIS_ACTION_TEXT_OBJECT_PARAGRAPH_OUTER,
308 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER,
309 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER,
310 VIS_ACTION_TEXT_OBJECT_PARENTHESIS_OUTER,
311 VIS_ACTION_TEXT_OBJECT_PARENTHESIS_INNER,
312 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER,
313 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER,
314 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER,
315 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER,
316 VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER,
317 VIS_ACTION_TEXT_OBJECT_QUOTE_INNER,
318 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER,
319 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER,
320 VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER,
321 VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER,
322 VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER,
323 VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER,
324 VIS_ACTION_TEXT_OBJECT_LINE_OUTER,
325 VIS_ACTION_TEXT_OBJECT_LINE_INNER,
326 VIS_ACTION_TEXT_OBJECT_INDENTATION,
327 VIS_ACTION_TEXT_OBJECT_SEARCH_FORWARD,
328 VIS_ACTION_TEXT_OBJECT_SEARCH_BACKWARD,
329 VIS_ACTION_MOTION_CHARWISE,
330 VIS_ACTION_MOTION_LINEWISE,
331 VIS_ACTION_UNICODE_INFO,
332 VIS_ACTION_UTF8_INFO,
333 VIS_ACTION_NOP,
336 static const KeyAction vis_action[] = {
337 [VIS_ACTION_EDITOR_SUSPEND] = {
338 "vis-suspend",
339 VIS_HELP("Suspend the editor")
340 suspend,
342 [VIS_ACTION_CURSOR_CHAR_PREV] = {
343 "vis-motion-char-prev",
344 VIS_HELP("Move cursor left, to the previous character")
345 movement, { .i = VIS_MOVE_CHAR_PREV }
347 [VIS_ACTION_CURSOR_CHAR_NEXT] = {
348 "vis-motion-char-next",
349 VIS_HELP("Move cursor right, to the next character")
350 movement, { .i = VIS_MOVE_CHAR_NEXT }
352 [VIS_ACTION_CURSOR_LINE_CHAR_PREV] = {
353 "vis-motion-line-char-prev",
354 VIS_HELP("Move cursor left, to the previous character on the same line")
355 movement, { .i = VIS_MOVE_LINE_CHAR_PREV }
357 [VIS_ACTION_CURSOR_LINE_CHAR_NEXT] = {
358 "vis-motion-line-char-next",
359 VIS_HELP("Move cursor right, to the next character on the same line")
360 movement, { .i = VIS_MOVE_LINE_CHAR_NEXT }
362 [VIS_ACTION_CURSOR_CODEPOINT_PREV] = {
363 "vis-motion-codepoint-prev",
364 VIS_HELP("Move to the previous Unicode codepoint")
365 movement, { .i = VIS_MOVE_CODEPOINT_PREV }
367 [VIS_ACTION_CURSOR_CODEPOINT_NEXT] = {
368 "vis-motion-codepoint-next",
369 VIS_HELP("Move to the next Unicode codepoint")
370 movement, { .i = VIS_MOVE_CODEPOINT_NEXT }
372 [VIS_ACTION_CURSOR_WORD_START_PREV] = {
373 "vis-motion-word-start-prev",
374 VIS_HELP("Move cursor words backwards")
375 movement, { .i = VIS_MOVE_WORD_START_PREV }
377 [VIS_ACTION_CURSOR_WORD_START_NEXT] = {
378 "vis-motion-word-start-next",
379 VIS_HELP("Move cursor words forwards")
380 movement, { .i = VIS_MOVE_WORD_START_NEXT }
382 [VIS_ACTION_CURSOR_WORD_END_PREV] = {
383 "vis-motion-word-end-prev",
384 VIS_HELP("Move cursor backwards to the end of word")
385 movement, { .i = VIS_MOVE_WORD_END_PREV }
387 [VIS_ACTION_CURSOR_WORD_END_NEXT] = {
388 "vis-motion-word-end-next",
389 VIS_HELP("Move cursor forward to the end of word")
390 movement, { .i = VIS_MOVE_WORD_END_NEXT }
392 [VIS_ACTION_CURSOR_LONGWORD_START_PREV] = {
393 "vis-motion-bigword-start-prev",
394 VIS_HELP("Move cursor WORDS backwards")
395 movement, { .i = VIS_MOVE_LONGWORD_START_PREV }
397 [VIS_ACTION_CURSOR_LONGWORD_START_NEXT] = {
398 "vis-motion-bigword-start-next",
399 VIS_HELP("Move cursor WORDS forwards")
400 movement, { .i = VIS_MOVE_LONGWORD_START_NEXT }
402 [VIS_ACTION_CURSOR_LONGWORD_END_PREV] = {
403 "vis-motion-bigword-end-prev",
404 VIS_HELP("Move cursor backwards to the end of WORD")
405 movement, { .i = VIS_MOVE_LONGWORD_END_PREV }
407 [VIS_ACTION_CURSOR_LONGWORD_END_NEXT] = {
408 "vis-motion-bigword-end-next",
409 VIS_HELP("Move cursor forward to the end of WORD")
410 movement, { .i = VIS_MOVE_LONGWORD_END_NEXT }
412 [VIS_ACTION_CURSOR_LINE_UP] = {
413 "vis-motion-line-up",
414 VIS_HELP("Move cursor line upwards")
415 movement, { .i = VIS_MOVE_LINE_UP }
417 [VIS_ACTION_CURSOR_LINE_DOWN] = {
418 "vis-motion-line-down",
419 VIS_HELP("Move cursor line downwards")
420 movement, { .i = VIS_MOVE_LINE_DOWN }
422 [VIS_ACTION_CURSOR_LINE_START] = {
423 "vis-motion-line-start",
424 VIS_HELP("Move cursor to first non-blank character of the line")
425 movement, { .i = VIS_MOVE_LINE_START }
427 [VIS_ACTION_CURSOR_LINE_FINISH] = {
428 "vis-motion-line-finish",
429 VIS_HELP("Move cursor to last non-blank character of the line")
430 movement, { .i = VIS_MOVE_LINE_FINISH }
432 [VIS_ACTION_CURSOR_LINE_BEGIN] = {
433 "vis-motion-line-begin",
434 VIS_HELP("Move cursor to first character of the line")
435 movement, { .i = VIS_MOVE_LINE_BEGIN }
437 [VIS_ACTION_CURSOR_LINE_END] = {
438 "vis-motion-line-end",
439 VIS_HELP("Move cursor to end of the line")
440 movement, { .i = VIS_MOVE_LINE_END }
442 [VIS_ACTION_CURSOR_SCREEN_LINE_UP] = {
443 "vis-motion-screenline-up",
444 VIS_HELP("Move cursor screen/display line upwards")
445 movement, { .i = VIS_MOVE_SCREEN_LINE_UP }
447 [VIS_ACTION_CURSOR_SCREEN_LINE_DOWN] = {
448 "vis-motion-screenline-down",
449 VIS_HELP("Move cursor screen/display line downwards")
450 movement, { .i = VIS_MOVE_SCREEN_LINE_DOWN }
452 [VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN] = {
453 "vis-motion-screenline-begin",
454 VIS_HELP("Move cursor to beginning of screen/display line")
455 movement, { .i = VIS_MOVE_SCREEN_LINE_BEGIN }
457 [VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE] = {
458 "vis-motion-screenline-middle",
459 VIS_HELP("Move cursor to middle of screen/display line")
460 movement, { .i = VIS_MOVE_SCREEN_LINE_MIDDLE }
462 [VIS_ACTION_CURSOR_SCREEN_LINE_END] = {
463 "vis-motion-screenline-end",
464 VIS_HELP("Move cursor to end of screen/display line")
465 movement, { .i = VIS_MOVE_SCREEN_LINE_END }
467 [VIS_ACTION_CURSOR_PERCENT] = {
468 "vis-motion-percent",
469 VIS_HELP("Move to count % of file or matching item")
470 percent
472 [VIS_ACTION_CURSOR_BYTE] = {
473 "vis-motion-byte",
474 VIS_HELP("Move to absolute byte position")
475 movement, { .i = VIS_MOVE_BYTE }
477 [VIS_ACTION_CURSOR_BYTE_LEFT] = {
478 "vis-motion-byte-left",
479 VIS_HELP("Move count bytes to the left")
480 movement, { .i = VIS_MOVE_BYTE_LEFT }
482 [VIS_ACTION_CURSOR_BYTE_RIGHT] = {
483 "vis-motion-byte-right",
484 VIS_HELP("Move count bytes to the right")
485 movement, { .i = VIS_MOVE_BYTE_RIGHT }
487 [VIS_ACTION_CURSOR_PARAGRAPH_PREV] = {
488 "vis-motion-paragraph-prev",
489 VIS_HELP("Move cursor paragraph backward")
490 movement, { .i = VIS_MOVE_PARAGRAPH_PREV }
492 [VIS_ACTION_CURSOR_PARAGRAPH_NEXT] = {
493 "vis-motion-paragraph-next",
494 VIS_HELP("Move cursor paragraph forward")
495 movement, { .i = VIS_MOVE_PARAGRAPH_NEXT }
497 [VIS_ACTION_CURSOR_SENTENCE_PREV] = {
498 "vis-motion-sentence-prev",
499 VIS_HELP("Move cursor sentence backward")
500 movement, { .i = VIS_MOVE_SENTENCE_PREV }
502 [VIS_ACTION_CURSOR_SENTENCE_NEXT] = {
503 "vis-motion-sentence-next",
504 VIS_HELP("Move cursor sentence forward")
505 movement, { .i = VIS_MOVE_SENTENCE_NEXT }
507 [VIS_ACTION_CURSOR_BLOCK_START] = {
508 "vis-motion-block-start",
509 VIS_HELP("Move cursor to the opening curly brace in a block")
510 movement, { .i = VIS_MOVE_BLOCK_START }
512 [VIS_ACTION_CURSOR_BLOCK_END] = {
513 "vis-motion-block-end",
514 VIS_HELP("Move cursor to the closing curly brace in a block")
515 movement, { .i = VIS_MOVE_BLOCK_END }
517 [VIS_ACTION_CURSOR_PARENTHESE_START] = {
518 "vis-motion-parenthese-start",
519 VIS_HELP("Move cursor to the opening parenthese inside a pair of parentheses")
520 movement, { .i = VIS_MOVE_PARENTHESE_START }
522 [VIS_ACTION_CURSOR_PARENTHESE_END] = {
523 "vis-motion-parenthese-end",
524 VIS_HELP("Move cursor to the closing parenthese inside a pair of parentheses")
525 movement, { .i = VIS_MOVE_PARENTHESE_END }
527 [VIS_ACTION_CURSOR_COLUMN] = {
528 "vis-motion-column",
529 VIS_HELP("Move cursor to given column of current line")
530 movement, { .i = VIS_MOVE_COLUMN }
532 [VIS_ACTION_CURSOR_LINE_FIRST] = {
533 "vis-motion-line-first",
534 VIS_HELP("Move cursor to given line (defaults to first)")
535 gotoline, { .i = -1 }
537 [VIS_ACTION_CURSOR_LINE_LAST] = {
538 "vis-motion-line-last",
539 VIS_HELP("Move cursor to given line (defaults to last)")
540 gotoline, { .i = +1 }
542 [VIS_ACTION_CURSOR_WINDOW_LINE_TOP] = {
543 "vis-motion-window-line-top",
544 VIS_HELP("Move cursor to top line of the window")
545 movement, { .i = VIS_MOVE_WINDOW_LINE_TOP }
547 [VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE] = {
548 "vis-motion-window-line-middle",
549 VIS_HELP("Move cursor to middle line of the window")
550 movement, { .i = VIS_MOVE_WINDOW_LINE_MIDDLE }
552 [VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM] = {
553 "vis-motion-window-line-bottom",
554 VIS_HELP("Move cursor to bottom line of the window")
555 movement, { .i = VIS_MOVE_WINDOW_LINE_BOTTOM }
557 [VIS_ACTION_CURSOR_SEARCH_REPEAT_FORWARD] = {
558 "vis-motion-search-repeat-forward",
559 VIS_HELP("Move cursor to next match in forward direction")
560 movement, { .i = VIS_MOVE_SEARCH_REPEAT_FORWARD }
562 [VIS_ACTION_CURSOR_SEARCH_REPEAT_BACKWARD] = {
563 "vis-motion-search-repeat-backward",
564 VIS_HELP("Move cursor to previous match in backward direction")
565 movement, { .i = VIS_MOVE_SEARCH_REPEAT_BACKWARD }
567 [VIS_ACTION_CURSOR_SEARCH_REPEAT] = {
568 "vis-motion-search-repeat",
569 VIS_HELP("Move cursor to next match")
570 movement, { .i = VIS_MOVE_SEARCH_REPEAT }
572 [VIS_ACTION_CURSOR_SEARCH_REPEAT_REVERSE] = {
573 "vis-motion-search-repeat-reverse",
574 VIS_HELP("Move cursor to next match in opposite direction")
575 movement, { .i = VIS_MOVE_SEARCH_REPEAT_REVERSE }
577 [VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD] = {
578 "vis-motion-search-word-forward",
579 VIS_HELP("Move cursor to next occurrence of the word under cursor")
580 movement, { .i = VIS_MOVE_SEARCH_WORD_FORWARD }
582 [VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD] = {
583 "vis-motion-search-word-backward",
584 VIS_HELP("Move cursor to previous occurrence of the word under cursor")
585 movement, { .i = VIS_MOVE_SEARCH_WORD_BACKWARD }
587 [VIS_ACTION_WINDOW_PAGE_UP] = {
588 "vis-window-page-up",
589 VIS_HELP("Scroll window pages backwards (upwards)")
590 wscroll, { .i = -PAGE }
592 [VIS_ACTION_WINDOW_HALFPAGE_UP] = {
593 "vis-window-halfpage-up",
594 VIS_HELP("Scroll window half pages backwards (upwards)")
595 wscroll, { .i = -PAGE_HALF }
597 [VIS_ACTION_WINDOW_PAGE_DOWN] = {
598 "vis-window-page-down",
599 VIS_HELP("Scroll window pages forwards (downwards)")
600 wscroll, { .i = +PAGE }
602 [VIS_ACTION_WINDOW_HALFPAGE_DOWN] = {
603 "vis-window-halfpage-down",
604 VIS_HELP("Scroll window half pages forwards (downwards)")
605 wscroll, { .i = +PAGE_HALF }
607 [VIS_ACTION_MODE_NORMAL] = {
608 "vis-mode-normal",
609 VIS_HELP("Enter normal mode")
610 switchmode, { .i = VIS_MODE_NORMAL }
612 [VIS_ACTION_MODE_VISUAL] = {
613 "vis-mode-visual-charwise",
614 VIS_HELP("Enter characterwise visual mode")
615 switchmode, { .i = VIS_MODE_VISUAL }
617 [VIS_ACTION_MODE_VISUAL_LINE] = {
618 "vis-mode-visual-linewise",
619 VIS_HELP("Enter linewise visual mode")
620 switchmode, { .i = VIS_MODE_VISUAL_LINE }
622 [VIS_ACTION_MODE_INSERT] = {
623 "vis-mode-insert",
624 VIS_HELP("Enter insert mode")
625 insertmode, { .i = VIS_MOVE_NOP }
627 [VIS_ACTION_MODE_REPLACE] = {
628 "vis-mode-replace",
629 VIS_HELP("Enter replace mode")
630 replacemode, { .i = VIS_MOVE_NOP }
632 [VIS_ACTION_DELETE_CHAR_PREV] = {
633 "vis-delete-char-prev",
634 VIS_HELP("Delete the previous character")
635 delete, { .i = VIS_MOVE_CHAR_PREV }
637 [VIS_ACTION_DELETE_CHAR_NEXT] = {
638 "vis-delete-char-next",
639 VIS_HELP("Delete the next character")
640 delete, { .i = VIS_MOVE_CHAR_NEXT }
642 [VIS_ACTION_DELETE_LINE_BEGIN] = {
643 "vis-delete-line-begin",
644 VIS_HELP("Delete until the start of the current line")
645 delete, { .i = VIS_MOVE_LINE_BEGIN }
647 [VIS_ACTION_DELETE_WORD_PREV] = {
648 "vis-delete-word-prev",
649 VIS_HELP("Delete the previous WORD")
650 delete, { .i = VIS_MOVE_WORD_START_PREV }
652 [VIS_ACTION_JUMPLIST_PREV] = {
653 "vis-jumplist-prev",
654 VIS_HELP("Go to older cursor position in jump list")
655 jumplist, { .i = -1 }
657 [VIS_ACTION_JUMPLIST_NEXT] = {
658 "vis-jumplist-next",
659 VIS_HELP("Go to newer cursor position in jump list")
660 jumplist, { .i = +1 }
662 [VIS_ACTION_JUMPLIST_SAVE] = {
663 "vis-jumplist-save",
664 VIS_HELP("Save current selections in jump list")
665 jumplist, { .i = 0 }
667 [VIS_ACTION_UNDO] = {
668 "vis-undo",
669 VIS_HELP("Undo last change")
670 undo,
672 [VIS_ACTION_REDO] = {
673 "vis-redo",
674 VIS_HELP("Redo last change")
675 redo,
677 [VIS_ACTION_EARLIER] = {
678 "vis-earlier",
679 VIS_HELP("Goto older text state")
680 earlier,
682 [VIS_ACTION_LATER] = {
683 "vis-later",
684 VIS_HELP("Goto newer text state")
685 later,
687 [VIS_ACTION_MACRO_RECORD] = {
688 "vis-macro-record",
689 VIS_HELP("Record macro into given register")
690 macro_record,
692 [VIS_ACTION_MACRO_REPLAY] = {
693 "vis-macro-replay",
694 VIS_HELP("Replay macro, execute the content of the given register")
695 macro_replay,
697 [VIS_ACTION_MARK] = {
698 "vis-mark",
699 VIS_HELP("Use given mark for next action")
700 mark,
702 [VIS_ACTION_REDRAW] = {
703 "vis-redraw",
704 VIS_HELP("Redraw current editor content")
705 call, { .f = vis_redraw }
707 [VIS_ACTION_REPLACE_CHAR] = {
708 "vis-replace-char",
709 VIS_HELP("Replace the character under the cursor")
710 replace,
712 [VIS_ACTION_TOTILL_REPEAT] = {
713 "vis-motion-totill-repeat",
714 VIS_HELP("Repeat latest to/till motion")
715 movement, { .i = VIS_MOVE_TOTILL_REPEAT }
717 [VIS_ACTION_TOTILL_REVERSE] = {
718 "vis-motion-totill-reverse",
719 VIS_HELP("Repeat latest to/till motion but in opposite direction")
720 movement, { .i = VIS_MOVE_TOTILL_REVERSE }
722 [VIS_ACTION_PROMPT_SEARCH_FORWARD] = {
723 "vis-search-forward",
724 VIS_HELP("Search forward")
725 prompt_show, { .s = "/" }
727 [VIS_ACTION_PROMPT_SEARCH_BACKWARD] = {
728 "vis-search-backward",
729 VIS_HELP("Search backward")
730 prompt_show, { .s = "?" }
732 [VIS_ACTION_TILL_LEFT] = {
733 "vis-motion-till-left",
734 VIS_HELP("Till after the occurrence of character to the left")
735 movement_key, { .i = VIS_MOVE_LEFT_TILL }
737 [VIS_ACTION_TILL_RIGHT] = {
738 "vis-motion-till-right",
739 VIS_HELP("Till before the occurrence of character to the right")
740 movement_key, { .i = VIS_MOVE_RIGHT_TILL }
742 [VIS_ACTION_TO_LEFT] = {
743 "vis-motion-to-left",
744 VIS_HELP("To the first occurrence of character to the left")
745 movement_key, { .i = VIS_MOVE_LEFT_TO }
747 [VIS_ACTION_TO_RIGHT] = {
748 "vis-motion-to-right",
749 VIS_HELP("To the first occurrence of character to the right")
750 movement_key, { .i = VIS_MOVE_RIGHT_TO }
752 [VIS_ACTION_REGISTER] = {
753 "vis-register",
754 VIS_HELP("Use given register for next operator")
755 reg,
757 [VIS_ACTION_OPERATOR_CHANGE] = {
758 "vis-operator-change",
759 VIS_HELP("Change operator")
760 operator, { .i = VIS_OP_CHANGE }
762 [VIS_ACTION_OPERATOR_DELETE] = {
763 "vis-operator-delete",
764 VIS_HELP("Delete operator")
765 operator, { .i = VIS_OP_DELETE }
767 [VIS_ACTION_OPERATOR_YANK] = {
768 "vis-operator-yank",
769 VIS_HELP("Yank operator")
770 operator, { .i = VIS_OP_YANK }
772 [VIS_ACTION_OPERATOR_SHIFT_LEFT] = {
773 "vis-operator-shift-left",
774 VIS_HELP("Shift left operator")
775 operator, { .i = VIS_OP_SHIFT_LEFT }
777 [VIS_ACTION_OPERATOR_SHIFT_RIGHT] = {
778 "vis-operator-shift-right",
779 VIS_HELP("Shift right operator")
780 operator, { .i = VIS_OP_SHIFT_RIGHT }
782 [VIS_ACTION_OPERATOR_CASE_LOWER] = {
783 "vis-operator-case-lower",
784 VIS_HELP("Lowercase operator")
785 operator, { .i = VIS_OP_CASE_LOWER }
787 [VIS_ACTION_OPERATOR_CASE_UPPER] = {
788 "vis-operator-case-upper",
789 VIS_HELP("Uppercase operator")
790 operator, { .i = VIS_OP_CASE_UPPER }
792 [VIS_ACTION_OPERATOR_CASE_SWAP] = {
793 "vis-operator-case-swap",
794 VIS_HELP("Swap case operator")
795 operator, { .i = VIS_OP_CASE_SWAP }
797 [VIS_ACTION_COUNT] = {
798 "vis-count",
799 VIS_HELP("Count specifier")
800 count,
802 [VIS_ACTION_INSERT_NEWLINE] = {
803 "vis-insert-newline",
804 VIS_HELP("Insert a line break (depending on file type)")
805 call, { .f = vis_insert_nl }
807 [VIS_ACTION_INSERT_TAB] = {
808 "vis-insert-tab",
809 VIS_HELP("Insert a tab (might be converted to spaces)")
810 call, { .f = vis_insert_tab }
812 [VIS_ACTION_INSERT_VERBATIM] = {
813 "vis-insert-verbatim",
814 VIS_HELP("Insert Unicode character based on code point")
815 insert_verbatim,
817 [VIS_ACTION_INSERT_REGISTER] = {
818 "vis-insert-register",
819 VIS_HELP("Insert specified register content")
820 insert_register,
822 [VIS_ACTION_WINDOW_NEXT] = {
823 "vis-window-next",
824 VIS_HELP("Focus next window")
825 call, { .f = vis_window_next }
827 [VIS_ACTION_WINDOW_PREV] = {
828 "vis-window-prev",
829 VIS_HELP("Focus previous window")
830 call, { .f = vis_window_prev }
832 [VIS_ACTION_APPEND_CHAR_NEXT] = {
833 "vis-append-char-next",
834 VIS_HELP("Append text after the cursor")
835 insertmode, { .i = VIS_MOVE_LINE_CHAR_NEXT }
837 [VIS_ACTION_APPEND_LINE_END] = {
838 "vis-append-line-end",
839 VIS_HELP("Append text after the end of the line")
840 insertmode, { .i = VIS_MOVE_LINE_END },
842 [VIS_ACTION_INSERT_LINE_START] = {
843 "vis-insert-line-start",
844 VIS_HELP("Insert text before the first non-blank in the line")
845 insertmode, { .i = VIS_MOVE_LINE_START },
847 [VIS_ACTION_OPEN_LINE_ABOVE] = {
848 "vis-open-line-above",
849 VIS_HELP("Begin a new line above the cursor")
850 openline, { .i = -1 }
852 [VIS_ACTION_OPEN_LINE_BELOW] = {
853 "vis-open-line-below",
854 VIS_HELP("Begin a new line below the cursor")
855 openline, { .i = +1 }
857 [VIS_ACTION_JOIN_LINES] = {
858 "vis-join-lines",
859 VIS_HELP("Join selected lines")
860 join, { .s = " " }
862 [VIS_ACTION_JOIN_LINES_TRIM] = {
863 "vis-join-lines-trim",
864 VIS_HELP("Join selected lines, remove white space")
865 join, { .s = "" }
867 [VIS_ACTION_PROMPT_SHOW] = {
868 "vis-prompt-show",
869 VIS_HELP("Show editor command line prompt")
870 prompt_show, { .s = ":" }
872 [VIS_ACTION_REPEAT] = {
873 "vis-repeat",
874 VIS_HELP("Repeat latest editor command")
875 repeat
877 [VIS_ACTION_SELECTION_FLIP] = {
878 "vis-selection-flip",
879 VIS_HELP("Flip selection, move cursor to other end")
880 selection_end,
882 [VIS_ACTION_WINDOW_REDRAW_TOP] = {
883 "vis-window-redraw-top",
884 VIS_HELP("Redraw cursor line at the top of the window")
885 window, { .w = view_redraw_top }
887 [VIS_ACTION_WINDOW_REDRAW_CENTER] = {
888 "vis-window-redraw-center",
889 VIS_HELP("Redraw cursor line at the center of the window")
890 window, { .w = view_redraw_center }
892 [VIS_ACTION_WINDOW_REDRAW_BOTTOM] = {
893 "vis-window-redraw-bottom",
894 VIS_HELP("Redraw cursor line at the bottom of the window")
895 window, { .w = view_redraw_bottom }
897 [VIS_ACTION_WINDOW_SLIDE_UP] = {
898 "vis-window-slide-up",
899 VIS_HELP("Slide window content upwards")
900 wslide, { .i = -1 }
902 [VIS_ACTION_WINDOW_SLIDE_DOWN] = {
903 "vis-window-slide-down",
904 VIS_HELP("Slide window content downwards")
905 wslide, { .i = +1 }
907 [VIS_ACTION_PUT_AFTER] = {
908 "vis-put-after",
909 VIS_HELP("Put text after the cursor")
910 operator, { .i = VIS_OP_PUT_AFTER }
912 [VIS_ACTION_PUT_BEFORE] = {
913 "vis-put-before",
914 VIS_HELP("Put text before the cursor")
915 operator, { .i = VIS_OP_PUT_BEFORE }
917 [VIS_ACTION_PUT_AFTER_END] = {
918 "vis-put-after-end",
919 VIS_HELP("Put text after the cursor, place cursor after new text")
920 operator, { .i = VIS_OP_PUT_AFTER_END }
922 [VIS_ACTION_PUT_BEFORE_END] = {
923 "vis-put-before-end",
924 VIS_HELP("Put text before the cursor, place cursor after new text")
925 operator, { .i = VIS_OP_PUT_BEFORE_END }
927 [VIS_ACTION_SELECTIONS_MATCH_WORD] = {
928 "vis-selections-select-word",
929 VIS_HELP("Select word under cursor")
930 selections_match_word,
932 [VIS_ACTION_SELECTIONS_NEW_LINE_ABOVE] = {
933 "vis-selection-new-lines-above",
934 VIS_HELP("Create a new selection on the line above")
935 selections_new, { .i = -1 }
937 [VIS_ACTION_SELECTIONS_NEW_LINE_ABOVE_FIRST] = {
938 "vis-selection-new-lines-above-first",
939 VIS_HELP("Create a new selection on the line above the first selection")
940 selections_new, { .i = INT_MIN }
942 [VIS_ACTION_SELECTIONS_NEW_LINE_BELOW] = {
943 "vis-selection-new-lines-below",
944 VIS_HELP("Create a new selection on the line below")
945 selections_new, { .i = +1 }
947 [VIS_ACTION_SELECTIONS_NEW_LINE_BELOW_LAST] = {
948 "vis-selection-new-lines-below-last",
949 VIS_HELP("Create a new selection on the line below the last selection")
950 selections_new, { .i = INT_MAX }
952 [VIS_ACTION_SELECTIONS_NEW_LINES_BEGIN] = {
953 "vis-selection-new-lines-begin",
954 VIS_HELP("Create a new selection at the start of every line covered by selection")
955 operator, { .i = VIS_OP_CURSOR_SOL }
957 [VIS_ACTION_SELECTIONS_NEW_LINES_END] = {
958 "vis-selection-new-lines-end",
959 VIS_HELP("Create a new selection at the end of every line covered by selection")
960 operator, { .i = VIS_OP_CURSOR_EOL }
962 [VIS_ACTION_SELECTIONS_NEW_MATCH_NEXT] = {
963 "vis-selection-new-match-next",
964 VIS_HELP("Select the next region matching the current selection")
965 selections_match_next,
967 [VIS_ACTION_SELECTIONS_NEW_MATCH_SKIP] = {
968 "vis-selection-new-match-skip",
969 VIS_HELP("Clear current selection, but select next match")
970 selections_match_skip,
972 [VIS_ACTION_SELECTIONS_ALIGN] = {
973 "vis-selections-align",
974 VIS_HELP("Try to align all selections on the same column")
975 selections_align,
977 [VIS_ACTION_SELECTIONS_ALIGN_INDENT_LEFT] = {
978 "vis-selections-align-indent-left",
979 VIS_HELP("Left align all selections/selections by inserting spaces")
980 selections_align_indent, { .i = -1 }
982 [VIS_ACTION_SELECTIONS_ALIGN_INDENT_RIGHT] = {
983 "vis-selections-align-indent-right",
984 VIS_HELP("Right align all selections/selections by inserting spaces")
985 selections_align_indent, { .i = +1 }
987 [VIS_ACTION_SELECTIONS_REMOVE_ALL] = {
988 "vis-selections-remove-all",
989 VIS_HELP("Remove all but the primary selection")
990 selections_clear,
992 [VIS_ACTION_SELECTIONS_REMOVE_LAST] = {
993 "vis-selections-remove-last",
994 VIS_HELP("Remove least recently created selection")
995 selections_remove,
997 [VIS_ACTION_SELECTIONS_REMOVE_COLUMN] = {
998 "vis-selections-remove-column",
999 VIS_HELP("Remove count selection column")
1000 selections_remove_column, { .i = 1 }
1002 [VIS_ACTION_SELECTIONS_REMOVE_COLUMN_EXCEPT] = {
1003 "vis-selections-remove-column-except",
1004 VIS_HELP("Remove all but the count selection column")
1005 selections_remove_column_except, { .i = 1 }
1007 [VIS_ACTION_SELECTIONS_PREV] = {
1008 "vis-selection-prev",
1009 VIS_HELP("Move to the previous selection")
1010 selections_navigate, { .i = -PAGE_HALF }
1012 [VIS_ACTION_SELECTIONS_NEXT] = {
1013 "vis-selection-next",
1014 VIS_HELP("Move to the next selection")
1015 selections_navigate, { .i = +PAGE_HALF }
1017 [VIS_ACTION_SELECTIONS_ROTATE_LEFT] = {
1018 "vis-selections-rotate-left",
1019 VIS_HELP("Rotate selections left")
1020 selections_rotate, { .i = -1 }
1022 [VIS_ACTION_SELECTIONS_ROTATE_RIGHT] = {
1023 "vis-selections-rotate-right",
1024 VIS_HELP("Rotate selections right")
1025 selections_rotate, { .i = +1 }
1027 [VIS_ACTION_SELECTIONS_TRIM] = {
1028 "vis-selections-trim",
1029 VIS_HELP("Remove leading and trailing white space from selections")
1030 selections_trim
1032 [VIS_ACTION_SELECTIONS_SAVE] = {
1033 "vis-selections-save",
1034 VIS_HELP("Save currently active selections to register")
1035 selections_save
1037 [VIS_ACTION_SELECTIONS_RESTORE] = {
1038 "vis-selections-restore",
1039 VIS_HELP("Restore selections from register")
1040 selections_restore
1042 [VIS_ACTION_SELECTIONS_UNION] = {
1043 "vis-selections-union",
1044 VIS_HELP("Add selections from register")
1045 selections_union
1047 [VIS_ACTION_SELECTIONS_INTERSECT] = {
1048 "vis-selections-intersect",
1049 VIS_HELP("Intersect selections with register")
1050 selections_intersect
1052 [VIS_ACTION_SELECTIONS_COMPLEMENT] = {
1053 "vis-selections-complement",
1054 VIS_HELP("Complement selections")
1055 selections_complement
1057 [VIS_ACTION_SELECTIONS_MINUS] = {
1058 "vis-selections-minus",
1059 VIS_HELP("Subtract selections from register")
1060 selections_minus
1062 [VIS_ACTION_SELECTIONS_COMBINE_UNION] = {
1063 "vis-selections-combine-union",
1064 VIS_HELP("Pairwise union with selection from register")
1065 selections_combine, { .combine = combine_union }
1067 [VIS_ACTION_SELECTIONS_COMBINE_INTERSECT] = {
1068 "vis-selections-combine-intersect",
1069 VIS_HELP("Pairwise intersect with selections from register")
1070 selections_combine, { .combine = combine_intersect }
1072 [VIS_ACTION_SELECTIONS_COMBINE_LONGER] = {
1073 "vis-selections-combine-longer",
1074 VIS_HELP("Pairwise combine: take longer")
1075 selections_combine, { .combine = combine_longer }
1077 [VIS_ACTION_SELECTIONS_COMBINE_SHORTER] = {
1078 "vis-selections-combine-shorter",
1079 VIS_HELP("Pairwise combine: take shorter")
1080 selections_combine, { .combine = combine_shorter }
1082 [VIS_ACTION_SELECTIONS_COMBINE_LEFTMOST] = {
1083 "vis-selections-combine-leftmost",
1084 VIS_HELP("Pairwise combine: leftmost")
1085 selections_combine, { .combine = combine_leftmost }
1087 [VIS_ACTION_SELECTIONS_COMBINE_RIGHTMOST] = {
1088 "vis-selections-combine-rightmost",
1089 VIS_HELP("Pairwise combine: rightmost")
1090 selections_combine, { .combine = combine_rightmost }
1092 [VIS_ACTION_TEXT_OBJECT_WORD_OUTER] = {
1093 "vis-textobject-word-outer",
1094 VIS_HELP("A word leading and trailing whitespace included")
1095 textobj, { .i = VIS_TEXTOBJECT_OUTER_WORD }
1097 [VIS_ACTION_TEXT_OBJECT_WORD_INNER] = {
1098 "vis-textobject-word-inner",
1099 VIS_HELP("A word leading and trailing whitespace excluded")
1100 textobj, { .i = VIS_TEXTOBJECT_INNER_WORD }
1102 [VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER] = {
1103 "vis-textobject-bigword-outer",
1104 VIS_HELP("A WORD leading and trailing whitespace included")
1105 textobj, { .i = VIS_TEXTOBJECT_OUTER_LONGWORD }
1107 [VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER] = {
1108 "vis-textobject-bigword-inner",
1109 VIS_HELP("A WORD leading and trailing whitespace excluded")
1110 textobj, { .i = VIS_TEXTOBJECT_INNER_LONGWORD }
1112 [VIS_ACTION_TEXT_OBJECT_SENTENCE] = {
1113 "vis-textobject-sentence",
1114 VIS_HELP("A sentence")
1115 textobj, { .i = VIS_TEXTOBJECT_SENTENCE }
1117 [VIS_ACTION_TEXT_OBJECT_PARAGRAPH] = {
1118 "vis-textobject-paragraph",
1119 VIS_HELP("A paragraph")
1120 textobj, { .i = VIS_TEXTOBJECT_PARAGRAPH }
1122 [VIS_ACTION_TEXT_OBJECT_PARAGRAPH_OUTER] = {
1123 "vis-textobject-paragraph-outer",
1124 VIS_HELP("A paragraph (outer variant)")
1125 textobj, { .i = VIS_TEXTOBJECT_PARAGRAPH_OUTER }
1127 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER] = {
1128 "vis-textobject-square-bracket-outer",
1129 VIS_HELP("[] block (outer variant)")
1130 textobj, { .i = VIS_TEXTOBJECT_OUTER_SQUARE_BRACKET }
1132 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER] = {
1133 "vis-textobject-square-bracket-inner",
1134 VIS_HELP("[] block (inner variant)")
1135 textobj, { .i = VIS_TEXTOBJECT_INNER_SQUARE_BRACKET }
1137 [VIS_ACTION_TEXT_OBJECT_PARENTHESIS_OUTER] = {
1138 "vis-textobject-parentheses-outer",
1139 VIS_HELP("() block (outer variant)")
1140 textobj, { .i = VIS_TEXTOBJECT_OUTER_PARENTHESIS }
1142 [VIS_ACTION_TEXT_OBJECT_PARENTHESIS_INNER] = {
1143 "vis-textobject-parentheses-inner",
1144 VIS_HELP("() block (inner variant)")
1145 textobj, { .i = VIS_TEXTOBJECT_INNER_PARENTHESIS }
1147 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER] = {
1148 "vis-textobject-angle-bracket-outer",
1149 VIS_HELP("<> block (outer variant)")
1150 textobj, { .i = VIS_TEXTOBJECT_OUTER_ANGLE_BRACKET }
1152 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER] = {
1153 "vis-textobject-angle-bracket-inner",
1154 VIS_HELP("<> block (inner variant)")
1155 textobj, { .i = VIS_TEXTOBJECT_INNER_ANGLE_BRACKET }
1157 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER] = {
1158 "vis-textobject-curly-bracket-outer",
1159 VIS_HELP("{} block (outer variant)")
1160 textobj, { .i = VIS_TEXTOBJECT_OUTER_CURLY_BRACKET }
1162 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER] = {
1163 "vis-textobject-curly-bracket-inner",
1164 VIS_HELP("{} block (inner variant)")
1165 textobj, { .i = VIS_TEXTOBJECT_INNER_CURLY_BRACKET }
1167 [VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER] = {
1168 "vis-textobject-quote-outer",
1169 VIS_HELP("A quoted string, including the quotation marks")
1170 textobj, { .i = VIS_TEXTOBJECT_OUTER_QUOTE }
1172 [VIS_ACTION_TEXT_OBJECT_QUOTE_INNER] = {
1173 "vis-textobject-quote-inner",
1174 VIS_HELP("A quoted string, excluding the quotation marks")
1175 textobj, { .i = VIS_TEXTOBJECT_INNER_QUOTE }
1177 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER] = {
1178 "vis-textobject-single-quote-outer",
1179 VIS_HELP("A single quoted string, including the quotation marks")
1180 textobj, { .i = VIS_TEXTOBJECT_OUTER_SINGLE_QUOTE }
1182 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER] = {
1183 "vis-textobject-single-quote-inner",
1184 VIS_HELP("A single quoted string, excluding the quotation marks")
1185 textobj, { .i = VIS_TEXTOBJECT_INNER_SINGLE_QUOTE }
1187 [VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER] = {
1188 "vis-textobject-backtick-outer",
1189 VIS_HELP("A backtick delimited string (outer variant)")
1190 textobj, { .i = VIS_TEXTOBJECT_OUTER_BACKTICK }
1192 [VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER] = {
1193 "vis-textobject-backtick-inner",
1194 VIS_HELP("A backtick delimited string (inner variant)")
1195 textobj, { .i = VIS_TEXTOBJECT_INNER_BACKTICK }
1197 [VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER] = {
1198 "vis-textobject-entire-outer",
1199 VIS_HELP("The whole text content")
1200 textobj, { .i = VIS_TEXTOBJECT_OUTER_ENTIRE }
1202 [VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER] = {
1203 "vis-textobject-entire-inner",
1204 VIS_HELP("The whole text content, except for leading and trailing empty lines")
1205 textobj, { .i = VIS_TEXTOBJECT_INNER_ENTIRE }
1207 [VIS_ACTION_TEXT_OBJECT_LINE_OUTER] = {
1208 "vis-textobject-line-outer",
1209 VIS_HELP("The whole line")
1210 textobj, { .i = VIS_TEXTOBJECT_OUTER_LINE }
1212 [VIS_ACTION_TEXT_OBJECT_LINE_INNER] = {
1213 "vis-textobject-line-inner",
1214 VIS_HELP("The whole line, excluding leading and trailing whitespace")
1215 textobj, { .i = VIS_TEXTOBJECT_INNER_LINE }
1217 [VIS_ACTION_TEXT_OBJECT_INDENTATION] = {
1218 "vis-textobject-indentation",
1219 VIS_HELP("All adjacent lines with the same indentation level as the current one")
1220 textobj, { .i = VIS_TEXTOBJECT_INDENTATION }
1222 [VIS_ACTION_TEXT_OBJECT_SEARCH_FORWARD] = {
1223 "vis-textobject-search-forward",
1224 VIS_HELP("The next search match in forward direction")
1225 textobj, { .i = VIS_TEXTOBJECT_SEARCH_FORWARD }
1227 [VIS_ACTION_TEXT_OBJECT_SEARCH_BACKWARD] = {
1228 "vis-textobject-search-backward",
1229 VIS_HELP("The next search match in backward direction")
1230 textobj, { .i = VIS_TEXTOBJECT_SEARCH_BACKWARD }
1232 [VIS_ACTION_MOTION_CHARWISE] = {
1233 "vis-motion-charwise",
1234 VIS_HELP("Force motion to be charwise")
1235 motiontype, { .i = VIS_MOTIONTYPE_CHARWISE }
1237 [VIS_ACTION_MOTION_LINEWISE] = {
1238 "vis-motion-linewise",
1239 VIS_HELP("Force motion to be linewise")
1240 motiontype, { .i = VIS_MOTIONTYPE_LINEWISE }
1242 [VIS_ACTION_UNICODE_INFO] = {
1243 "vis-unicode-info",
1244 VIS_HELP("Show Unicode codepoint(s) of character under cursor")
1245 unicode_info, { .i = VIS_ACTION_UNICODE_INFO }
1247 [VIS_ACTION_UTF8_INFO] = {
1248 "vis-utf8-info",
1249 VIS_HELP("Show UTF-8 encoded codepoint(s) of character under cursor")
1250 unicode_info, { .i = VIS_ACTION_UTF8_INFO }
1252 [VIS_ACTION_NOP] = {
1253 "vis-nop",
1254 VIS_HELP("Ignore key, do nothing")
1255 nop,
1259 #include "config.h"
1261 /** key bindings functions */
1263 static const char *nop(Vis *vis, const char *keys, const Arg *arg) {
1264 return keys;
1267 static const char *macro_record(Vis *vis, const char *keys, const Arg *arg) {
1268 if (!vis_macro_record_stop(vis)) {
1269 if (!keys[0])
1270 return NULL;
1271 const char *next = vis_keys_next(vis, keys);
1272 if (next - keys > 1)
1273 return next;
1274 enum VisRegister reg = vis_register_from(vis, keys[0]);
1275 vis_macro_record(vis, reg);
1276 keys++;
1278 vis_draw(vis);
1279 return keys;
1282 static const char *macro_replay(Vis *vis, const char *keys, const Arg *arg) {
1283 if (!keys[0])
1284 return NULL;
1285 const char *next = vis_keys_next(vis, keys);
1286 if (next - keys > 1)
1287 return next;
1288 enum VisRegister reg = vis_register_from(vis, keys[0]);
1289 vis_macro_replay(vis, reg);
1290 return keys+1;
1293 static const char *suspend(Vis *vis, const char *keys, const Arg *arg) {
1294 vis_suspend(vis);
1295 return keys;
1298 static const char *repeat(Vis *vis, const char *keys, const Arg *arg) {
1299 vis_repeat(vis);
1300 return keys;
1303 static const char *selections_new(Vis *vis, const char *keys, const Arg *arg) {
1304 View *view = vis_view(vis);
1305 bool anchored = view_selections_anchored(view_selections_primary_get(view));
1306 VisCountIterator it = vis_count_iterator_get(vis, 1);
1307 while (vis_count_iterator_next(&it)) {
1308 Selection *sel = NULL;
1309 switch (arg->i) {
1310 case -1:
1311 case +1:
1312 sel = view_selections_primary_get(view);
1313 break;
1314 case INT_MIN:
1315 sel = view_selections(view);
1316 break;
1317 case INT_MAX:
1318 for (Selection *s = view_selections(view); s; s = view_selections_next(s))
1319 sel = s;
1320 break;
1323 if (!sel)
1324 return keys;
1326 size_t oldpos = view_cursors_pos(sel);
1327 if (arg->i > 0)
1328 view_line_down(sel);
1329 else if (arg->i < 0)
1330 view_line_up(sel);
1331 size_t newpos = view_cursors_pos(sel);
1332 view_cursors_to(sel, oldpos);
1333 Selection *sel_new = view_selections_new(view, newpos);
1334 if (!sel_new) {
1335 if (arg->i == -1)
1336 sel_new = view_selections_prev(sel);
1337 else if (arg->i == +1)
1338 sel_new = view_selections_next(sel);
1340 if (sel_new) {
1341 view_selections_primary_set(sel_new);
1342 view_selections_anchor(sel_new, anchored);
1345 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1346 return keys;
1349 static const char *selections_align(Vis *vis, const char *keys, const Arg *arg) {
1350 View *view = vis_view(vis);
1351 Text *txt = vis_text(vis);
1352 int mincol = INT_MAX;
1353 for (Selection *s = view_selections(view); s; s = view_selections_next(s)) {
1354 int col = view_cursors_cell_get(s);
1355 if (col >= 0 && col < mincol)
1356 mincol = col;
1358 for (Selection *s = view_selections(view); s; s = view_selections_next(s)) {
1359 if (view_cursors_cell_set(s, mincol) == -1) {
1360 size_t pos = view_cursors_pos(s);
1361 size_t col = text_line_width_set(txt, pos, mincol);
1362 view_cursors_to(s, col);
1365 return keys;
1368 static const char *selections_align_indent(Vis *vis, const char *keys, const Arg *arg) {
1369 View *view = vis_view(vis);
1370 Text *txt = vis_text(vis);
1371 bool left_align = arg->i < 0;
1372 int columns = view_selections_column_count(view);
1374 for (int i = 0; i < columns; i++) {
1375 int mincol = INT_MAX, maxcol = 0;
1376 for (Selection *s = view_selections_column(view, i); s; s = view_selections_column_next(s, i)) {
1377 Filerange sel = view_selections_get(s);
1378 size_t pos = left_align ? sel.start : sel.end;
1379 int col = text_line_width_get(txt, pos);
1380 if (col < mincol)
1381 mincol = col;
1382 if (col > maxcol)
1383 maxcol = col;
1386 size_t len = maxcol - mincol;
1387 char *buf = malloc(len+1);
1388 if (!buf)
1389 return keys;
1390 memset(buf, ' ', len);
1392 for (Selection *s = view_selections_column(view, i); s; s = view_selections_column_next(s, i)) {
1393 Filerange sel = view_selections_get(s);
1394 size_t pos = left_align ? sel.start : sel.end;
1395 size_t ipos = sel.start;
1396 int col = text_line_width_get(txt, pos);
1397 if (col < maxcol) {
1398 size_t off = maxcol - col;
1399 if (off <= len)
1400 text_insert(txt, ipos, buf, off);
1404 free(buf);
1407 view_draw(view);
1408 return keys;
1411 static const char *selections_clear(Vis *vis, const char *keys, const Arg *arg) {
1412 View *view = vis_view(vis);
1413 if (view_selections_count(view) > 1)
1414 view_selections_dispose_all(view);
1415 else
1416 view_selection_clear(view_selections_primary_get(view));
1417 return keys;
1420 static const char *selections_match_word(Vis *vis, const char *keys, const Arg *arg) {
1421 Text *txt = vis_text(vis);
1422 View *view = vis_view(vis);
1423 for (Selection *s = view_selections(view); s; s = view_selections_next(s)) {
1424 Filerange word = text_object_word(txt, view_cursors_pos(s));
1425 if (text_range_valid(&word))
1426 view_selections_set(s, &word);
1428 vis_mode_switch(vis, VIS_MODE_VISUAL);
1429 return keys;
1432 static const char *selections_match_next(Vis *vis, const char *keys, const Arg *arg) {
1433 Text *txt = vis_text(vis);
1434 View *view = vis_view(vis);
1435 Selection *s = view_selections_primary_get(view);
1436 Filerange sel = view_selections_get(s);
1437 if (!text_range_valid(&sel))
1438 return keys;
1440 char *buf = text_bytes_alloc0(txt, sel.start, text_range_size(&sel));
1441 if (!buf)
1442 return keys;
1443 Filerange word = text_object_word_find_next(txt, sel.end, buf);
1444 if (text_range_valid(&word)) {
1445 size_t pos = text_char_prev(txt, word.end);
1446 if ((s = view_selections_new(view, pos))) {
1447 view_selections_set(s, &word);
1448 view_selections_anchor(s, true);
1449 view_selections_primary_set(s);
1450 goto out;
1454 sel = view_selections_get(view_selections(view));
1455 word = text_object_word_find_prev(txt, sel.start, buf);
1456 if (!text_range_valid(&word))
1457 goto out;
1458 size_t pos = text_char_prev(txt, word.end);
1459 if ((s = view_selections_new(view, pos))) {
1460 view_selections_set(s, &word);
1461 view_selections_anchor(s, true);
1462 view_selections_primary_set(s);
1465 out:
1466 free(buf);
1467 return keys;
1470 static const char *selections_match_skip(Vis *vis, const char *keys, const Arg *arg) {
1471 View *view = vis_view(vis);
1472 Selection *sel = view_selections_primary_get(view);
1473 keys = selections_match_next(vis, keys, arg);
1474 if (sel != view_selections_primary_get(view))
1475 view_selections_dispose(sel);
1476 return keys;
1479 static const char *selections_remove(Vis *vis, const char *keys, const Arg *arg) {
1480 View *view = vis_view(vis);
1481 view_selections_dispose(view_selections_primary_get(view));
1482 view_cursor_to(view, view_cursor_get(view));
1483 return keys;
1486 static const char *selections_remove_column(Vis *vis, const char *keys, const Arg *arg) {
1487 View *view = vis_view(vis);
1488 int max = view_selections_column_count(view);
1489 int column = vis_count_get_default(vis, arg->i) - 1;
1490 if (column >= max)
1491 column = max - 1;
1492 if (view_selections_count(view) == 1) {
1493 vis_keys_feed(vis, "<Escape>");
1494 return keys;
1497 for (Selection *s = view_selections_column(view, column), *next; s; s = next) {
1498 next = view_selections_column_next(s, column);
1499 view_selections_dispose(s);
1502 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1503 return keys;
1506 static const char *selections_remove_column_except(Vis *vis, const char *keys, const Arg *arg) {
1507 View *view = vis_view(vis);
1508 int max = view_selections_column_count(view);
1509 int column = vis_count_get_default(vis, arg->i) - 1;
1510 if (column >= max)
1511 column = max - 1;
1512 if (view_selections_count(view) == 1) {
1513 vis_redraw(vis);
1514 return keys;
1517 Selection *sel = view_selections(view);
1518 Selection *col = view_selections_column(view, column);
1519 for (Selection *next; sel; sel = next) {
1520 next = view_selections_next(sel);
1521 if (sel == col)
1522 col = view_selections_column_next(col, column);
1523 else
1524 view_selections_dispose(sel);
1527 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1528 return keys;
1531 static const char *selections_navigate(Vis *vis, const char *keys, const Arg *arg) {
1532 View *view = vis_view(vis);
1533 if (view_selections_count(view) == 1)
1534 return wscroll(vis, keys, arg);
1535 Selection *s = view_selections_primary_get(view);
1536 VisCountIterator it = vis_count_iterator_get(vis, 1);
1537 while (vis_count_iterator_next(&it)) {
1538 if (arg->i > 0) {
1539 s = view_selections_next(s);
1540 if (!s)
1541 s = view_selections(view);
1542 } else {
1543 s = view_selections_prev(s);
1544 if (!s) {
1545 s = view_selections(view);
1546 for (Selection *n = s; n; n = view_selections_next(n))
1547 s = n;
1551 view_selections_primary_set(s);
1552 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1553 return keys;
1556 static const char *selections_rotate(Vis *vis, const char *keys, const Arg *arg) {
1558 typedef struct {
1559 Selection *sel;
1560 char *data;
1561 size_t len;
1562 } Rotate;
1564 Array arr;
1565 Text *txt = vis_text(vis);
1566 View *view = vis_view(vis);
1567 int columns = view_selections_column_count(view);
1568 int selections = columns == 1 ? view_selections_count(view) : columns;
1569 int count = vis_count_get_default(vis, 1);
1570 array_init_sized(&arr, sizeof(Rotate));
1571 if (!array_reserve(&arr, selections))
1572 return keys;
1573 size_t line = 0;
1575 for (Selection *s = view_selections(view), *next; s; s = next) {
1576 next = view_selections_next(s);
1577 size_t line_next = 0;
1579 Filerange sel = view_selections_get(s);
1580 Rotate rot;
1581 rot.sel = s;
1582 rot.len = text_range_size(&sel);
1583 if ((rot.data = malloc(rot.len)))
1584 rot.len = text_bytes_get(txt, sel.start, rot.len, rot.data);
1585 else
1586 rot.len = 0;
1587 array_add(&arr, &rot);
1589 if (!line)
1590 line = text_lineno_by_pos(txt, view_cursors_pos(s));
1591 if (next)
1592 line_next = text_lineno_by_pos(txt, view_cursors_pos(next));
1593 if (!next || (columns > 1 && line != line_next)) {
1594 size_t len = array_length(&arr);
1595 size_t off = arg->i > 0 ? count % len : len - (count % len);
1596 for (size_t i = 0; i < len; i++) {
1597 size_t j = (i + off) % len;
1598 Rotate *oldrot = array_get(&arr, i);
1599 Rotate *newrot = array_get(&arr, j);
1600 if (!oldrot || !newrot || oldrot == newrot)
1601 continue;
1602 Filerange newsel = view_selections_get(newrot->sel);
1603 if (!text_range_valid(&newsel))
1604 continue;
1605 if (!text_delete_range(txt, &newsel))
1606 continue;
1607 if (!text_insert(txt, newsel.start, oldrot->data, oldrot->len))
1608 continue;
1609 newsel.end = newsel.start + oldrot->len;
1610 view_selections_set(newrot->sel, &newsel);
1611 free(oldrot->data);
1613 array_clear(&arr);
1615 line = line_next;
1618 array_release(&arr);
1619 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1620 return keys;
1623 static const char *selections_trim(Vis *vis, const char *keys, const Arg *arg) {
1624 Text *txt = vis_text(vis);
1625 View *view = vis_view(vis);
1626 for (Selection *s = view_selections(view), *next; s; s = next) {
1627 next = view_selections_next(s);
1628 Filerange sel = view_selections_get(s);
1629 if (!text_range_valid(&sel))
1630 continue;
1631 for (char b; sel.start < sel.end && text_byte_get(txt, sel.end-1, &b)
1632 && isspace((unsigned char)b); sel.end--);
1633 for (char b; sel.start <= sel.end && text_byte_get(txt, sel.start, &b)
1634 && isspace((unsigned char)b); sel.start++);
1635 if (sel.start < sel.end) {
1636 view_selections_set(s, &sel);
1637 } else if (!view_selections_dispose(s)) {
1638 vis_mode_switch(vis, VIS_MODE_NORMAL);
1641 return keys;
1644 static void selections_set(Vis *vis, View *view, Array *sel) {
1645 enum VisMode mode = vis_mode_get(vis);
1646 bool anchored = mode == VIS_MODE_VISUAL || mode == VIS_MODE_VISUAL_LINE;
1647 view_selections_set_all(view, sel, anchored);
1648 if (!anchored)
1649 view_selections_clear_all(view);
1652 static const char *selections_save(Vis *vis, const char *keys, const Arg *arg) {
1653 Win *win = vis_window(vis);
1654 View *view = vis_view(vis);
1655 enum VisMark mark = vis_mark_used(vis);
1656 Array sel = view_selections_get_all(view);
1657 vis_mark_set(win, mark, &sel);
1658 array_release(&sel);
1659 vis_cancel(vis);
1660 return keys;
1663 static const char *selections_restore(Vis *vis, const char *keys, const Arg *arg) {
1664 Win *win = vis_window(vis);
1665 View *view = vis_view(vis);
1666 enum VisMark mark = vis_mark_used(vis);
1667 Array sel = vis_mark_get(win, mark);
1668 selections_set(vis, view, &sel);
1669 array_release(&sel);
1670 vis_cancel(vis);
1671 return keys;
1674 static const char *selections_union(Vis *vis, const char *keys, const Arg *arg) {
1675 Win *win = vis_window(vis);
1676 View *view = vis_view(vis);
1677 enum VisMark mark = vis_mark_used(vis);
1678 Array a = vis_mark_get(win, mark);
1679 Array b = view_selections_get_all(view);
1680 Array sel;
1681 array_init_from(&sel, &a);
1683 size_t i = 0, j = 0;
1684 Filerange *r1 = array_get(&a, i), *r2 = array_get(&b, j), cur = text_range_empty();
1685 while (r1 || r2) {
1686 if (r1 && text_range_overlap(r1, &cur)) {
1687 cur = text_range_union(r1, &cur);
1688 r1 = array_get(&a, ++i);
1689 } else if (r2 && text_range_overlap(r2, &cur)) {
1690 cur = text_range_union(r2, &cur);
1691 r2 = array_get(&b, ++j);
1692 } else {
1693 if (text_range_valid(&cur))
1694 array_add(&sel, &cur);
1695 if (!r1) {
1696 cur = *r2;
1697 r2 = array_get(&b, ++j);
1698 } else if (!r2) {
1699 cur = *r1;
1700 r1 = array_get(&a, ++i);
1701 } else {
1702 if (r1->start < r2->start) {
1703 cur = *r1;
1704 r1 = array_get(&a, ++i);
1705 } else {
1706 cur = *r2;
1707 r2 = array_get(&b, ++j);
1713 if (text_range_valid(&cur))
1714 array_add(&sel, &cur);
1716 selections_set(vis, view, &sel);
1717 vis_cancel(vis);
1719 array_release(&a);
1720 array_release(&b);
1721 array_release(&sel);
1723 return keys;
1726 static void intersect(Array *ret, Array *a, Array *b) {
1727 size_t i = 0, j = 0;
1728 Filerange *r1 = array_get(a, i), *r2 = array_get(b, j);
1729 while (r1 && r2) {
1730 if (text_range_overlap(r1, r2)) {
1731 Filerange new = text_range_intersect(r1, r2);
1732 array_add(ret, &new);
1734 if (r1->end < r2->end)
1735 r1 = array_get(a, ++i);
1736 else
1737 r2 = array_get(b, ++j);
1741 static const char *selections_intersect(Vis *vis, const char *keys, const Arg *arg) {
1742 Win *win = vis_window(vis);
1743 View *view = vis_view(vis);
1744 enum VisMark mark = vis_mark_used(vis);
1745 Array a = vis_mark_get(win, mark);
1746 Array b = view_selections_get_all(view);
1747 Array sel;
1748 array_init_from(&sel, &a);
1750 intersect(&sel, &a, &b);
1751 selections_set(vis, view, &sel);
1752 vis_cancel(vis);
1754 array_release(&a);
1755 array_release(&b);
1756 array_release(&sel);
1758 return keys;
1761 static void complement(Array *ret, Array *a, Filerange *universe) {
1762 size_t pos = universe->start;
1763 for (size_t i = 0, len = array_length(a); i < len; i++) {
1764 Filerange *r = array_get(a, i);
1765 if (pos < r->start) {
1766 Filerange new = text_range_new(pos, r->start);
1767 array_add(ret, &new);
1769 pos = r->end;
1771 if (pos < universe->end) {
1772 Filerange new = text_range_new(pos, universe->end);
1773 array_add(ret, &new);
1777 static const char *selections_complement(Vis *vis, const char *keys, const Arg *arg) {
1778 Text *txt = vis_text(vis);
1779 View *view = vis_view(vis);
1780 Filerange universe = text_object_entire(txt, 0);
1781 Array a = view_selections_get_all(view);
1782 Array sel;
1783 array_init_from(&sel, &a);
1785 complement(&sel, &a, &universe);
1787 selections_set(vis, view, &sel);
1788 array_release(&a);
1789 array_release(&sel);
1790 return keys;
1793 static const char *selections_minus(Vis *vis, const char *keys, const Arg *arg) {
1794 Text *txt = vis_text(vis);
1795 Win *win = vis_window(vis);
1796 View *view = vis_view(vis);
1797 enum VisMark mark = vis_mark_used(vis);
1798 Array a = view_selections_get_all(view);
1799 Array b = vis_mark_get(win, mark);
1800 Array sel;
1801 array_init_from(&sel, &a);
1802 Array b_complement;
1803 array_init_from(&b_complement, &b);
1805 Filerange universe = text_object_entire(txt, 0);
1806 complement(&b_complement, &b, &universe);
1807 intersect(&sel, &a, &b_complement);
1809 selections_set(vis, view, &sel);
1810 vis_cancel(vis);
1812 array_release(&a);
1813 array_release(&b);
1814 array_release(&b_complement);
1815 array_release(&sel);
1817 return keys;
1820 static Filerange combine_union(const Filerange *r1, const Filerange *r2) {
1821 if (!r1)
1822 return *r2;
1823 if (!r2)
1824 return *r1;
1825 return text_range_union(r1, r2);
1828 static Filerange combine_intersect(const Filerange *r1, const Filerange *r2) {
1829 if (!r1 || !r2)
1830 return text_range_empty();
1831 return text_range_intersect(r1, r2);
1834 static Filerange combine_longer(const Filerange *r1, const Filerange *r2) {
1835 if (!r1)
1836 return *r2;
1837 if (!r2)
1838 return *r1;
1839 size_t l1 = text_range_size(r1);
1840 size_t l2 = text_range_size(r2);
1841 return l1 < l2 ? *r2 : *r1;
1844 static Filerange combine_shorter(const Filerange *r1, const Filerange *r2) {
1845 if (!r1)
1846 return *r2;
1847 if (!r2)
1848 return *r1;
1849 size_t l1 = text_range_size(r1);
1850 size_t l2 = text_range_size(r2);
1851 return l1 < l2 ? *r1 : *r2;
1854 static Filerange combine_leftmost(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) ? *r1 : *r2;
1862 static Filerange combine_rightmost(const Filerange *r1, const Filerange *r2) {
1863 if (!r1)
1864 return *r2;
1865 if (!r2)
1866 return *r1;
1867 return r1->start < r2->start || (r1->start == r2->start && r1->end < r2->end) ? *r2 : *r1;
1870 static const char *selections_combine(Vis *vis, const char *keys, const Arg *arg) {
1871 Win *win = vis_window(vis);
1872 View *view = vis_view(vis);
1873 enum VisMark mark = vis_mark_used(vis);
1874 Array a = view_selections_get_all(view);
1875 Array b = vis_mark_get(win, mark);
1876 Array sel;
1877 array_init_from(&sel, &a);
1879 Filerange *r1 = array_get(&a, 0), *r2 = array_get(&b, 0);
1880 for (size_t i = 0, j = 0; r1 || r2; r1 = array_get(&a, ++i), r2 = array_get(&b, ++j)) {
1881 Filerange new = arg->combine(r1, r2);
1882 if (text_range_valid(&new))
1883 array_add(&sel, &new);
1886 vis_mark_normalize(&sel);
1887 selections_set(vis, view, &sel);
1888 vis_cancel(vis);
1890 array_release(&a);
1891 array_release(&b);
1892 array_release(&sel);
1894 return keys;
1897 static const char *replace(Vis *vis, const char *keys, const Arg *arg) {
1898 if (!keys[0]) {
1899 vis_keymap_disable(vis);
1900 return NULL;
1903 const char *next = vis_keys_next(vis, keys);
1904 if (!next)
1905 return NULL;
1907 char replacement[UTFmax+1];
1908 if (!vis_keys_utf8(vis, keys, replacement))
1909 return next;
1911 if (replacement[0] == 0x1b) /* <Escape> */
1912 return next;
1914 vis_operator(vis, VIS_OP_REPLACE, replacement);
1915 if (vis_mode_get(vis) == VIS_MODE_OPERATOR_PENDING)
1916 vis_motion(vis, VIS_MOVE_CHAR_NEXT);
1917 return next;
1920 static const char *count(Vis *vis, const char *keys, const Arg *arg) {
1921 int digit = keys[-1] - '0';
1922 int count = vis_count_get_default(vis, 0);
1923 if (0 <= digit && digit <= 9) {
1924 if (digit == 0 && count == 0)
1925 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
1926 else
1927 vis_count_set(vis, count * 10 + digit);
1929 return keys;
1932 static const char *gotoline(Vis *vis, const char *keys, const Arg *arg) {
1933 if (vis_count_get(vis) != VIS_COUNT_UNKNOWN)
1934 vis_motion(vis, VIS_MOVE_LINE);
1935 else if (arg->i < 0)
1936 vis_motion(vis, VIS_MOVE_FILE_BEGIN);
1937 else
1938 vis_motion(vis, VIS_MOVE_FILE_END);
1939 return keys;
1942 static const char *motiontype(Vis *vis, const char *keys, const Arg *arg) {
1943 vis_motion_type(vis, arg->i);
1944 return keys;
1947 static const char *operator(Vis *vis, const char *keys, const Arg *arg) {
1948 vis_operator(vis, arg->i);
1949 return keys;
1952 static const char *movement_key(Vis *vis, const char *keys, const Arg *arg) {
1953 if (!keys[0]) {
1954 vis_keymap_disable(vis);
1955 return NULL;
1958 const char *next = vis_keys_next(vis, keys);
1959 if (!next)
1960 return NULL;
1961 char utf8[UTFmax+1];
1962 if (vis_keys_utf8(vis, keys, utf8))
1963 vis_motion(vis, arg->i, utf8);
1964 return next;
1967 static const char *movement(Vis *vis, const char *keys, const Arg *arg) {
1968 vis_motion(vis, arg->i);
1969 return keys;
1972 static const char *textobj(Vis *vis, const char *keys, const Arg *arg) {
1973 vis_textobject(vis, arg->i);
1974 return keys;
1977 static const char *selection_end(Vis *vis, const char *keys, const Arg *arg) {
1978 for (Selection *s = view_selections(vis_view(vis)); s; s = view_selections_next(s))
1979 view_selections_flip(s);
1980 return keys;
1983 static const char *reg(Vis *vis, const char *keys, const Arg *arg) {
1984 if (!keys[0])
1985 return NULL;
1986 const char *next = vis_keys_next(vis, keys);
1987 if (next - keys > 1)
1988 return next;
1989 enum VisRegister reg = vis_register_from(vis, keys[0]);
1990 vis_register(vis, reg);
1991 return keys+1;
1994 static const char *mark(Vis *vis, const char *keys, const Arg *arg) {
1995 if (!keys[0])
1996 return NULL;
1997 const char *next = vis_keys_next(vis, keys);
1998 if (next - keys > 1)
1999 return next;
2000 enum VisMark mark = vis_mark_from(vis, keys[0]);
2001 vis_mark(vis, mark);
2002 return keys+1;
2005 static const char *undo(Vis *vis, const char *keys, const Arg *arg) {
2006 size_t pos = text_undo(vis_text(vis));
2007 if (pos != EPOS) {
2008 View *view = vis_view(vis);
2009 if (view_selections_count(view) == 1)
2010 view_cursor_to(view, pos);
2011 /* redraw all windows in case some display the same file */
2012 vis_draw(vis);
2014 return keys;
2017 static const char *redo(Vis *vis, const char *keys, const Arg *arg) {
2018 size_t pos = text_redo(vis_text(vis));
2019 if (pos != EPOS) {
2020 View *view = vis_view(vis);
2021 if (view_selections_count(view) == 1)
2022 view_cursor_to(view, pos);
2023 /* redraw all windows in case some display the same file */
2024 vis_draw(vis);
2026 return keys;
2029 static const char *earlier(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_earlier(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 *later(Vis *vis, const char *keys, const Arg *arg) {
2043 size_t pos = EPOS;
2044 VisCountIterator it = vis_count_iterator_get(vis, 1);
2045 while (vis_count_iterator_next(&it))
2046 pos = text_later(vis_text(vis));
2047 if (pos != EPOS) {
2048 view_cursor_to(vis_view(vis), pos);
2049 /* redraw all windows in case some display the same file */
2050 vis_draw(vis);
2052 return keys;
2055 static const char *delete(Vis *vis, const char *keys, const Arg *arg) {
2056 vis_operator(vis, VIS_OP_DELETE);
2057 vis_motion(vis, arg->i);
2058 return keys;
2061 static const char *insert_register(Vis *vis, const char *keys, const Arg *arg) {
2062 if (!keys[0])
2063 return NULL;
2064 const char *next = vis_keys_next(vis, keys);
2065 if (next - keys > 1)
2066 return next;
2067 enum VisRegister reg = vis_register_from(vis, keys[0]);
2068 if (reg != VIS_REG_INVALID) {
2069 vis_register(vis, reg);
2070 vis_operator(vis, VIS_OP_PUT_BEFORE_END);
2072 return keys+1;
2075 static const char *prompt_show(Vis *vis, const char *keys, const Arg *arg) {
2076 vis_prompt_show(vis, arg->s);
2077 return keys;
2080 static const char *insert_verbatim(Vis *vis, const char *keys, const Arg *arg) {
2081 Rune rune = 0;
2082 char buf[4], type = keys[0];
2083 const char *data = NULL;
2084 int len = 0, count = 0, base = 0;
2085 switch (type) {
2086 case '\0':
2087 return NULL;
2088 case 'o':
2089 case 'O':
2090 count = 3;
2091 base = 8;
2092 break;
2093 case 'U':
2094 count = 4;
2095 /* fall through */
2096 case 'u':
2097 count += 4;
2098 base = 16;
2099 break;
2100 case 'x':
2101 case 'X':
2102 count = 2;
2103 base = 16;
2104 break;
2105 default:
2106 if ('0' <= type && type <= '9') {
2107 rune = type - '0';
2108 count = 2;
2109 base = 10;
2111 break;
2114 if (base) {
2115 for (keys++; keys[0] && count > 0; keys++, count--) {
2116 int v = 0;
2117 if (base == 8 && '0' <= keys[0] && keys[0] <= '7') {
2118 v = keys[0] - '0';
2119 } else if ((base == 10 || base == 16) && '0' <= keys[0] && keys[0] <= '9') {
2120 v = keys[0] - '0';
2121 } else if (base == 16 && 'a' <= keys[0] && keys[0] <= 'f') {
2122 v = 10 + keys[0] - 'a';
2123 } else if (base == 16 && 'A' <= keys[0] && keys[0] <= 'F') {
2124 v = 10 + keys[0] - 'A';
2125 } else {
2126 count = 0;
2127 break;
2129 rune = rune * base + v;
2132 if (count > 0)
2133 return NULL;
2134 if (type == 'u' || type == 'U') {
2135 len = runetochar(buf, &rune);
2136 } else {
2137 buf[0] = rune;
2138 len = 1;
2141 data = buf;
2142 } else {
2143 const char *next = vis_keys_next(vis, keys);
2144 if (!next)
2145 return NULL;
2146 if ((rune = vis_keys_codepoint(vis, keys)) != (Rune)-1) {
2147 len = runetochar(buf, &rune);
2148 data = buf;
2149 } else {
2150 vis_info_show(vis, "Unknown key");
2152 keys = next;
2155 if (len > 0)
2156 vis_insert_key(vis, data, len);
2157 return keys;
2160 static const char *wscroll(Vis *vis, const char *keys, const Arg *arg) {
2161 View *view = vis_view(vis);
2162 int count = vis_count_get(vis);
2163 switch (arg->i) {
2164 case -PAGE:
2165 view_scroll_page_up(view);
2166 break;
2167 case +PAGE:
2168 view_scroll_page_down(view);
2169 break;
2170 case -PAGE_HALF:
2171 view_scroll_halfpage_up(view);
2172 break;
2173 case +PAGE_HALF:
2174 view_scroll_halfpage_down(view);
2175 break;
2176 default:
2177 if (count == VIS_COUNT_UNKNOWN)
2178 count = arg->i < 0 ? -arg->i : arg->i;
2179 if (arg->i < 0)
2180 view_scroll_up(view, count);
2181 else
2182 view_scroll_down(view, count);
2183 break;
2185 vis_count_set(vis, VIS_COUNT_UNKNOWN);
2186 return keys;
2189 static const char *wslide(Vis *vis, const char *keys, const Arg *arg) {
2190 View *view = vis_view(vis);
2191 int count = vis_count_get(vis);
2192 if (count == VIS_COUNT_UNKNOWN)
2193 count = arg->i < 0 ? -arg->i : arg->i;
2194 if (arg->i >= 0)
2195 view_slide_down(view, count);
2196 else
2197 view_slide_up(view, count);
2198 vis_count_set(vis, VIS_COUNT_UNKNOWN);
2199 return keys;
2202 static const char *call(Vis *vis, const char *keys, const Arg *arg) {
2203 arg->f(vis);
2204 return keys;
2207 static const char *window(Vis *vis, const char *keys, const Arg *arg) {
2208 arg->w(vis_view(vis));
2209 return keys;
2212 static const char *openline(Vis *vis, const char *keys, const Arg *arg) {
2213 vis_operator(vis, VIS_OP_MODESWITCH, VIS_MODE_INSERT);
2214 if (arg->i > 0) {
2215 vis_motion(vis, VIS_MOVE_LINE_END);
2216 vis_keys_feed(vis, "<Enter>");
2217 } else {
2218 if (vis_get_autoindent(vis)) {
2219 vis_motion(vis, VIS_MOVE_LINE_START);
2220 vis_keys_feed(vis, "<vis-motion-line-start>");
2221 } else {
2222 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
2223 vis_keys_feed(vis, "<vis-motion-line-begin>");
2225 vis_keys_feed(vis, "<Enter><Up>");
2227 return keys;
2230 static const char *join(Vis *vis, const char *keys, const Arg *arg) {
2231 bool normal = (vis_mode_get(vis) == VIS_MODE_NORMAL);
2232 vis_operator(vis, VIS_OP_JOIN, arg->s);
2233 if (normal) {
2234 int count = vis_count_get_default(vis, 0);
2235 if (count)
2236 vis_count_set(vis, count-1);
2237 vis_motion(vis, VIS_MOVE_LINE_NEXT);
2239 return keys;
2242 static const char *switchmode(Vis *vis, const char *keys, const Arg *arg) {
2243 vis_mode_switch(vis, arg->i);
2244 return keys;
2247 static const char *insertmode(Vis *vis, const char *keys, const Arg *arg) {
2248 vis_operator(vis, VIS_OP_MODESWITCH, VIS_MODE_INSERT);
2249 vis_motion(vis, arg->i);
2250 return keys;
2253 static const char *replacemode(Vis *vis, const char *keys, const Arg *arg) {
2254 vis_operator(vis, VIS_OP_MODESWITCH, VIS_MODE_REPLACE);
2255 vis_motion(vis, arg->i);
2256 return keys;
2259 static const char *unicode_info(Vis *vis, const char *keys, const Arg *arg) {
2260 View *view = vis_view(vis);
2261 Text *txt = vis_text(vis);
2262 size_t start = view_cursor_get(view);
2263 size_t end = text_char_next(txt, start);
2264 char *grapheme = text_bytes_alloc0(txt, start, end-start), *codepoint = grapheme;
2265 if (!grapheme)
2266 return keys;
2267 Buffer info;
2268 buffer_init(&info);
2269 mbstate_t ps = { 0 };
2270 Iterator it = text_iterator_get(txt, start);
2271 for (size_t pos = start; it.pos < end; pos = it.pos) {
2272 if (!text_iterator_codepoint_next(&it, NULL)) {
2273 vis_info_show(vis, "Failed to parse code point");
2274 goto err;
2276 size_t len = it.pos - pos;
2277 wchar_t wc = 0xFFFD;
2278 size_t res = mbrtowc(&wc, codepoint, len, &ps);
2279 bool combining = false;
2280 if (res != (size_t)-1 && res != (size_t)-2)
2281 combining = (wc != L'\0' && wcwidth(wc) == 0);
2282 unsigned char ch = *codepoint;
2283 if (ch < 128 && !isprint(ch))
2284 buffer_appendf(&info, "<^%c> ", ch == 127 ? '?' : ch + 64);
2285 else
2286 buffer_appendf(&info, "<%s%.*s> ", combining ? " " : "", (int)len, codepoint);
2287 if (arg->i == VIS_ACTION_UNICODE_INFO) {
2288 buffer_appendf(&info, "U+%04"PRIX32" ", (uint32_t)wc);
2289 } else {
2290 for (size_t i = 0; i < len; i++)
2291 buffer_appendf(&info, "%02x ", (uint8_t)codepoint[i]);
2293 codepoint += len;
2295 vis_info_show(vis, "%s", buffer_content0(&info));
2296 err:
2297 free(grapheme);
2298 buffer_release(&info);
2299 return keys;
2302 static const char *percent(Vis *vis, const char *keys, const Arg *arg) {
2303 if (vis_count_get(vis) == VIS_COUNT_UNKNOWN)
2304 vis_motion(vis, VIS_MOVE_BRACKET_MATCH);
2305 else
2306 vis_motion(vis, VIS_MOVE_PERCENT);
2307 return keys;
2310 static const char *jumplist(Vis *vis, const char *keys, const Arg *arg) {
2311 if (arg->i < 0)
2312 vis_jumplist_prev(vis);
2313 else if (arg->i > 0)
2314 vis_jumplist_next(vis);
2315 else
2316 vis_jumplist_save(vis);
2317 return keys;
2320 static Vis *vis;
2322 static void signal_handler(int signum, siginfo_t *siginfo, void *context) {
2323 vis_signal_handler(vis, signum, siginfo, context);
2326 int main(int argc, char *argv[]) {
2328 VisEvent event = {
2329 .init = vis_lua_init,
2330 .start = vis_lua_start,
2331 .quit = vis_lua_quit,
2332 .mode_insert_input = vis_lua_mode_insert_input,
2333 .mode_replace_input = vis_lua_mode_replace_input,
2334 .file_open = vis_lua_file_open,
2335 .file_save_pre = vis_lua_file_save_pre,
2336 .file_save_post = vis_lua_file_save_post,
2337 .file_close = vis_lua_file_close,
2338 .win_open = vis_lua_win_open,
2339 .win_close = vis_lua_win_close,
2340 .win_highlight = vis_lua_win_highlight,
2341 .win_status = vis_lua_win_status,
2344 vis = vis_new(ui_term_new(), &event);
2345 if (!vis)
2346 return EXIT_FAILURE;
2348 for (int i = 0; i < LENGTH(vis_action); i++) {
2349 const KeyAction *action = &vis_action[i];
2350 if (!vis_action_register(vis, action))
2351 vis_die(vis, "Could not register action: %s\n", action->name);
2354 for (int i = 0; i < LENGTH(default_bindings); i++) {
2355 for (const KeyBinding **binding = default_bindings[i]; binding && *binding; binding++) {
2356 for (const KeyBinding *kb = *binding; kb->key; kb++) {
2357 vis_mode_map(vis, i, false, kb->key, kb);
2362 for (const char **k = keymaps; k[0]; k += 2)
2363 vis_keymap_add(vis, k[0], k[1]);
2365 /* install signal handlers etc. */
2366 struct sigaction sa;
2367 memset(&sa, 0, sizeof sa);
2368 sigfillset(&sa.sa_mask);
2369 sa.sa_flags = SA_SIGINFO;
2370 sa.sa_sigaction = signal_handler;
2371 if (sigaction(SIGBUS, &sa, NULL) == -1 ||
2372 sigaction(SIGINT, &sa, NULL) == -1 ||
2373 sigaction(SIGCONT, &sa, NULL) == -1 ||
2374 sigaction(SIGWINCH, &sa, NULL) == -1 ||
2375 sigaction(SIGTERM, &sa, NULL) == -1 ||
2376 sigaction(SIGHUP, &sa, NULL) == -1) {
2377 vis_die(vis, "Failed to set signal handler: %s\n", strerror(errno));
2380 sa.sa_handler = SIG_IGN;
2381 if (sigaction(SIGPIPE, &sa, NULL) == -1 || sigaction(SIGQUIT, &sa, NULL) == -1)
2382 vis_die(vis, "Failed to ignore signals\n");
2384 sigset_t blockset;
2385 sigemptyset(&blockset);
2386 sigaddset(&blockset, SIGBUS);
2387 sigaddset(&blockset, SIGCONT);
2388 sigaddset(&blockset, SIGWINCH);
2389 sigaddset(&blockset, SIGTERM);
2390 sigaddset(&blockset, SIGHUP);
2391 if (sigprocmask(SIG_BLOCK, &blockset, NULL) == -1)
2392 vis_die(vis, "Failed to block signals\n");
2394 for (int i = 1; i < argc; i++) {
2395 if (argv[i][0] != '-') {
2396 continue;
2397 } else if (strcmp(argv[i], "-") == 0) {
2398 continue;
2399 } else if (strcmp(argv[i], "--") == 0) {
2400 break;
2401 } else if (strcmp(argv[i], "-v") == 0) {
2402 printf("vis %s%s%s%s%s%s%s\n", VERSION,
2403 CONFIG_CURSES ? " +curses" : "",
2404 CONFIG_LUA ? " +lua" : "",
2405 CONFIG_LPEG ? " +lpeg" : "",
2406 CONFIG_TRE ? " +tre" : "",
2407 CONFIG_ACL ? " +acl" : "",
2408 CONFIG_SELINUX ? " +selinux" : "");
2409 return 0;
2410 } else {
2411 fprintf(stderr, "Unknown command option: %s\n", argv[i]);
2412 return 1;
2416 char *cmd = NULL;
2417 bool end_of_options = false, win_created = false;
2419 for (int i = 1; i < argc; i++) {
2420 if (argv[i][0] == '-' && !end_of_options) {
2421 if (strcmp(argv[i], "-") == 0) {
2422 if (!vis_window_new_fd(vis, STDOUT_FILENO))
2423 vis_die(vis, "Can not create empty buffer\n");
2424 ssize_t len = 0;
2425 char buf[PIPE_BUF];
2426 Text *txt = vis_text(vis);
2427 while ((len = read(STDIN_FILENO, buf, sizeof buf)) > 0)
2428 text_insert(txt, text_size(txt), buf, len);
2429 if (len == -1)
2430 vis_die(vis, "Can not read from stdin\n");
2431 text_snapshot(txt);
2432 int fd = open("/dev/tty", O_RDWR);
2433 if (fd == -1)
2434 vis_die(vis, "Can not reopen stdin\n");
2435 dup2(fd, STDIN_FILENO);
2436 close(fd);
2437 } else if (strcmp(argv[i], "--") == 0) {
2438 end_of_options = true;
2439 continue;
2441 } else if (argv[i][0] == '+' && !end_of_options) {
2442 cmd = argv[i] + (argv[i][1] == '/' || argv[i][1] == '?');
2443 continue;
2444 } else if (!vis_window_new(vis, argv[i])) {
2445 vis_die(vis, "Can not load `%s': %s\n", argv[i], strerror(errno));
2447 win_created = true;
2448 if (cmd) {
2449 vis_prompt_cmd(vis, cmd);
2450 cmd = NULL;
2454 if (!vis_window(vis) && !win_created) {
2455 if (!vis_window_new(vis, NULL))
2456 vis_die(vis, "Can not create empty buffer\n");
2457 if (cmd)
2458 vis_prompt_cmd(vis, cmd);
2461 int status = vis_run(vis);
2462 vis_free(vis);
2463 return status;