test: update
[vis.git] / main.c
blobaf15ffb9f3c3aaa8a2dee98389f34033d3fae387
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 /* 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 word the selection is currently over */
70 static const char *selections_match_word(Vis*, const char *keys, const Arg *arg);
71 /* select the next region matching the current selection */
72 static const char *selections_match_next(Vis*, const char *keys, const Arg *arg);
73 /* clear current selection but select next match */
74 static const char *selections_match_skip(Vis*, const char *keys, const Arg *arg);
75 /* rotate selection content count times left (arg->i < 0) or right (arg->i > 0) */
76 static const char *selections_rotate(Vis*, const char *keys, const Arg *arg);
77 /* remove leading and trailing white spaces from selections */
78 static const char *selections_trim(Vis*, const char *keys, const Arg *arg);
79 /* save active selections to mark */
80 static const char *selections_save(Vis*, const char *keys, const Arg *arg);
81 /* restore selections from mark */
82 static const char *selections_restore(Vis*, const char *keys, const Arg *arg);
83 /* union selections from mark */
84 static const char *selections_union(Vis*, const char *keys, const Arg *arg);
85 /* intersect selections from mark */
86 static const char *selections_intersect(Vis*, const char *keys, const Arg *arg);
87 /* perform complement of current active selections */
88 static const char *selections_complement(Vis*, const char *keys, const Arg *arg);
89 /* subtract selections from mark */
90 static const char *selections_minus(Vis*, const char *keys, const Arg *arg);
91 /* pairwise combine selections from mark */
92 static const char *selections_combine(Vis*, const char *keys, const Arg *arg);
93 static Filerange combine_union(const Filerange*, const Filerange*);
94 static Filerange combine_intersect(const Filerange*, const Filerange*);
95 static Filerange combine_longer(const Filerange*, const Filerange*);
96 static Filerange combine_shorter(const Filerange*, const Filerange*);
97 static Filerange combine_leftmost(const Filerange*, const Filerange*);
98 static Filerange combine_rightmost(const Filerange*, const Filerange*);
99 /* adjust current used count according to keys */
100 static const char *count(Vis*, const char *keys, const Arg *arg);
101 /* move to the count-th line or if not given either to the first (arg->i < 0)
102 * or last (arg->i > 0) line of file */
103 static const char *gotoline(Vis*, const char *keys, const Arg *arg);
104 /* make the current action use the operator indicated by arg->i */
105 static const char *operator(Vis*, const char *keys, const Arg *arg);
106 /* blocks to read a key and performs movement indicated by arg->i which
107 * should be one of VIS_MOVE_{RIGHT,LEFT}_{TO,TILL} */
108 static const char *movement_key(Vis*, const char *keys, const Arg *arg);
109 /* perform the movement as indicated by arg->i */
110 static const char *movement(Vis*, const char *keys, const Arg *arg);
111 /* let the current operator affect the range indicated by the text object arg->i */
112 static const char *textobj(Vis*, const char *keys, const Arg *arg);
113 /* move to the other end of selected text */
114 static const char *selection_end(Vis*, const char *keys, const Arg *arg);
115 /* use register indicated by keys for the current operator */
116 static const char *reg(Vis*, const char *keys, const Arg *arg);
117 /* use mark indicated by keys for the current action */
118 static const char *mark(Vis*, const char *keys, const Arg *arg);
119 /* {un,re}do last action, redraw window */
120 static const char *undo(Vis*, const char *keys, const Arg *arg);
121 static const char *redo(Vis*, const char *keys, const Arg *arg);
122 /* earlier, later action chronologically, redraw window */
123 static const char *earlier(Vis*, const char *keys, const Arg *arg);
124 static const char *later(Vis*, const char *keys, const Arg *arg);
125 /* delete from the current cursor position to the end of
126 * movement as indicated by arg->i */
127 static const char *delete(Vis*, const char *keys, const Arg *arg);
128 /* insert register content indicated by keys at current cursor position */
129 static const char *insert_register(Vis*, const char *keys, const Arg *arg);
130 /* show a user prompt to get input with title arg->s */
131 static const char *prompt_show(Vis*, const char *keys, const Arg *arg);
132 /* blocks to read 3 consecutive digits and inserts the corresponding byte value */
133 static const char *insert_verbatim(Vis*, const char *keys, const Arg *arg);
134 /* scroll window content according to arg->i which can be either PAGE, PAGE_HALF,
135 * or an arbitrary number of lines. a multiplier overrides what is given in arg->i.
136 * negative values scroll back, positive forward. */
137 static const char *wscroll(Vis*, const char *keys, const Arg *arg);
138 /* similar to scroll, but do only move window content not cursor position */
139 static const char *wslide(Vis*, const char *keys, const Arg *arg);
140 /* call editor function as indicated by arg->f */
141 static const char *call(Vis*, const char *keys, const Arg *arg);
142 /* call window function as indicated by arg->w */
143 static const char *window(Vis*, const char *keys, const Arg *arg);
144 /* show info about Unicode character at cursor position */
145 static const char *unicode_info(Vis*, const char *keys, const Arg *arg);
146 /* either go to count % of file or to matching item */
147 static const char *percent(Vis*, const char *keys, const Arg *arg);
148 /* navigate jumplist next (arg->i > 0), prev (arg->i < 0), save (arg->i = 0) */
149 static const char *jumplist(Vis*, const char *keys, const Arg *arg);
151 enum {
152 VIS_ACTION_EDITOR_SUSPEND,
153 VIS_ACTION_CURSOR_CHAR_PREV,
154 VIS_ACTION_CURSOR_CHAR_NEXT,
155 VIS_ACTION_CURSOR_LINE_CHAR_PREV,
156 VIS_ACTION_CURSOR_LINE_CHAR_NEXT,
157 VIS_ACTION_CURSOR_CODEPOINT_PREV,
158 VIS_ACTION_CURSOR_CODEPOINT_NEXT,
159 VIS_ACTION_CURSOR_WORD_START_PREV,
160 VIS_ACTION_CURSOR_WORD_START_NEXT,
161 VIS_ACTION_CURSOR_WORD_END_PREV,
162 VIS_ACTION_CURSOR_WORD_END_NEXT,
163 VIS_ACTION_CURSOR_LONGWORD_START_PREV,
164 VIS_ACTION_CURSOR_LONGWORD_START_NEXT,
165 VIS_ACTION_CURSOR_LONGWORD_END_PREV,
166 VIS_ACTION_CURSOR_LONGWORD_END_NEXT,
167 VIS_ACTION_CURSOR_LINE_UP,
168 VIS_ACTION_CURSOR_LINE_DOWN,
169 VIS_ACTION_CURSOR_LINE_START,
170 VIS_ACTION_CURSOR_LINE_FINISH,
171 VIS_ACTION_CURSOR_LINE_BEGIN,
172 VIS_ACTION_CURSOR_LINE_END,
173 VIS_ACTION_CURSOR_SCREEN_LINE_UP,
174 VIS_ACTION_CURSOR_SCREEN_LINE_DOWN,
175 VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN,
176 VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE,
177 VIS_ACTION_CURSOR_SCREEN_LINE_END,
178 VIS_ACTION_CURSOR_PERCENT,
179 VIS_ACTION_CURSOR_BYTE,
180 VIS_ACTION_CURSOR_BYTE_LEFT,
181 VIS_ACTION_CURSOR_BYTE_RIGHT,
182 VIS_ACTION_CURSOR_PARAGRAPH_PREV,
183 VIS_ACTION_CURSOR_PARAGRAPH_NEXT,
184 VIS_ACTION_CURSOR_SENTENCE_PREV,
185 VIS_ACTION_CURSOR_SENTENCE_NEXT,
186 VIS_ACTION_CURSOR_BLOCK_START,
187 VIS_ACTION_CURSOR_BLOCK_END,
188 VIS_ACTION_CURSOR_PARENTHESIS_START,
189 VIS_ACTION_CURSOR_PARENTHESIS_END,
190 VIS_ACTION_CURSOR_COLUMN,
191 VIS_ACTION_CURSOR_LINE_FIRST,
192 VIS_ACTION_CURSOR_LINE_LAST,
193 VIS_ACTION_CURSOR_WINDOW_LINE_TOP,
194 VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE,
195 VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM,
196 VIS_ACTION_CURSOR_SEARCH_REPEAT_FORWARD,
197 VIS_ACTION_CURSOR_SEARCH_REPEAT_BACKWARD,
198 VIS_ACTION_CURSOR_SEARCH_REPEAT,
199 VIS_ACTION_CURSOR_SEARCH_REPEAT_REVERSE,
200 VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD,
201 VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD,
202 VIS_ACTION_WINDOW_PAGE_UP,
203 VIS_ACTION_WINDOW_PAGE_DOWN,
204 VIS_ACTION_WINDOW_HALFPAGE_UP,
205 VIS_ACTION_WINDOW_HALFPAGE_DOWN,
206 VIS_ACTION_MODE_NORMAL,
207 VIS_ACTION_MODE_NORMAL_ESCAPE,
208 VIS_ACTION_MODE_VISUAL,
209 VIS_ACTION_MODE_VISUAL_ESCAPE,
210 VIS_ACTION_MODE_VISUAL_LINE,
211 VIS_ACTION_MODE_INSERT,
212 VIS_ACTION_MODE_REPLACE,
213 VIS_ACTION_DELETE_CHAR_PREV,
214 VIS_ACTION_DELETE_CHAR_NEXT,
215 VIS_ACTION_DELETE_LINE_BEGIN,
216 VIS_ACTION_DELETE_WORD_PREV,
217 VIS_ACTION_JUMPLIST_PREV,
218 VIS_ACTION_JUMPLIST_NEXT,
219 VIS_ACTION_JUMPLIST_SAVE,
220 VIS_ACTION_UNDO,
221 VIS_ACTION_REDO,
222 VIS_ACTION_EARLIER,
223 VIS_ACTION_LATER,
224 VIS_ACTION_MACRO_RECORD,
225 VIS_ACTION_MACRO_REPLAY,
226 VIS_ACTION_MARK,
227 VIS_ACTION_REDRAW,
228 VIS_ACTION_REPLACE_CHAR,
229 VIS_ACTION_TOTILL_REPEAT,
230 VIS_ACTION_TOTILL_REVERSE,
231 VIS_ACTION_PROMPT_SEARCH_FORWARD,
232 VIS_ACTION_PROMPT_SEARCH_BACKWARD,
233 VIS_ACTION_TILL_LEFT,
234 VIS_ACTION_TILL_RIGHT,
235 VIS_ACTION_TO_LEFT,
236 VIS_ACTION_TO_RIGHT,
237 VIS_ACTION_REGISTER,
238 VIS_ACTION_OPERATOR_CHANGE,
239 VIS_ACTION_OPERATOR_DELETE,
240 VIS_ACTION_OPERATOR_YANK,
241 VIS_ACTION_OPERATOR_SHIFT_LEFT,
242 VIS_ACTION_OPERATOR_SHIFT_RIGHT,
243 VIS_ACTION_COUNT,
244 VIS_ACTION_INSERT_NEWLINE,
245 VIS_ACTION_INSERT_TAB,
246 VIS_ACTION_INSERT_VERBATIM,
247 VIS_ACTION_INSERT_REGISTER,
248 VIS_ACTION_WINDOW_NEXT,
249 VIS_ACTION_WINDOW_PREV,
250 VIS_ACTION_APPEND_CHAR_NEXT,
251 VIS_ACTION_APPEND_LINE_END,
252 VIS_ACTION_INSERT_LINE_START,
253 VIS_ACTION_OPEN_LINE_ABOVE,
254 VIS_ACTION_OPEN_LINE_BELOW,
255 VIS_ACTION_JOIN_LINES,
256 VIS_ACTION_JOIN_LINES_TRIM,
257 VIS_ACTION_PROMPT_SHOW,
258 VIS_ACTION_REPEAT,
259 VIS_ACTION_SELECTION_FLIP,
260 VIS_ACTION_WINDOW_REDRAW_TOP,
261 VIS_ACTION_WINDOW_REDRAW_CENTER,
262 VIS_ACTION_WINDOW_REDRAW_BOTTOM,
263 VIS_ACTION_WINDOW_SLIDE_UP,
264 VIS_ACTION_WINDOW_SLIDE_DOWN,
265 VIS_ACTION_PUT_AFTER,
266 VIS_ACTION_PUT_BEFORE,
267 VIS_ACTION_SELECTIONS_MATCH_WORD,
268 VIS_ACTION_SELECTIONS_NEW_LINE_ABOVE,
269 VIS_ACTION_SELECTIONS_NEW_LINE_ABOVE_FIRST,
270 VIS_ACTION_SELECTIONS_NEW_LINE_BELOW,
271 VIS_ACTION_SELECTIONS_NEW_LINE_BELOW_LAST,
272 VIS_ACTION_SELECTIONS_NEW_LINES_BEGIN,
273 VIS_ACTION_SELECTIONS_NEW_LINES_END,
274 VIS_ACTION_SELECTIONS_NEW_MATCH_NEXT,
275 VIS_ACTION_SELECTIONS_NEW_MATCH_SKIP,
276 VIS_ACTION_SELECTIONS_ALIGN,
277 VIS_ACTION_SELECTIONS_ALIGN_INDENT_LEFT,
278 VIS_ACTION_SELECTIONS_ALIGN_INDENT_RIGHT,
279 VIS_ACTION_SELECTIONS_REMOVE_ALL,
280 VIS_ACTION_SELECTIONS_REMOVE_LAST,
281 VIS_ACTION_SELECTIONS_REMOVE_COLUMN,
282 VIS_ACTION_SELECTIONS_REMOVE_COLUMN_EXCEPT,
283 VIS_ACTION_SELECTIONS_PREV,
284 VIS_ACTION_SELECTIONS_NEXT,
285 VIS_ACTION_SELECTIONS_ROTATE_LEFT,
286 VIS_ACTION_SELECTIONS_ROTATE_RIGHT,
287 VIS_ACTION_SELECTIONS_TRIM,
288 VIS_ACTION_SELECTIONS_SAVE,
289 VIS_ACTION_SELECTIONS_RESTORE,
290 VIS_ACTION_SELECTIONS_UNION,
291 VIS_ACTION_SELECTIONS_INTERSECT,
292 VIS_ACTION_SELECTIONS_COMPLEMENT,
293 VIS_ACTION_SELECTIONS_MINUS,
294 VIS_ACTION_SELECTIONS_COMBINE_UNION,
295 VIS_ACTION_SELECTIONS_COMBINE_INTERSECT,
296 VIS_ACTION_SELECTIONS_COMBINE_LONGER,
297 VIS_ACTION_SELECTIONS_COMBINE_SHORTER,
298 VIS_ACTION_SELECTIONS_COMBINE_LEFTMOST,
299 VIS_ACTION_SELECTIONS_COMBINE_RIGHTMOST,
300 VIS_ACTION_TEXT_OBJECT_WORD_OUTER,
301 VIS_ACTION_TEXT_OBJECT_WORD_INNER,
302 VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER,
303 VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER,
304 VIS_ACTION_TEXT_OBJECT_SENTENCE,
305 VIS_ACTION_TEXT_OBJECT_PARAGRAPH,
306 VIS_ACTION_TEXT_OBJECT_PARAGRAPH_OUTER,
307 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER,
308 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER,
309 VIS_ACTION_TEXT_OBJECT_PARENTHESIS_OUTER,
310 VIS_ACTION_TEXT_OBJECT_PARENTHESIS_INNER,
311 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER,
312 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER,
313 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER,
314 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER,
315 VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER,
316 VIS_ACTION_TEXT_OBJECT_QUOTE_INNER,
317 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER,
318 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER,
319 VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER,
320 VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER,
321 VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER,
322 VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER,
323 VIS_ACTION_TEXT_OBJECT_LINE_OUTER,
324 VIS_ACTION_TEXT_OBJECT_LINE_INNER,
325 VIS_ACTION_TEXT_OBJECT_INDENTATION,
326 VIS_ACTION_TEXT_OBJECT_SEARCH_FORWARD,
327 VIS_ACTION_TEXT_OBJECT_SEARCH_BACKWARD,
328 VIS_ACTION_UNICODE_INFO,
329 VIS_ACTION_UTF8_INFO,
330 VIS_ACTION_NOP,
333 static const KeyAction vis_action[] = {
334 [VIS_ACTION_EDITOR_SUSPEND] = {
335 "vis-suspend",
336 VIS_HELP("Suspend the editor")
337 suspend,
339 [VIS_ACTION_CURSOR_CHAR_PREV] = {
340 "vis-motion-char-prev",
341 VIS_HELP("Move cursor left, to the previous character")
342 movement, { .i = VIS_MOVE_CHAR_PREV }
344 [VIS_ACTION_CURSOR_CHAR_NEXT] = {
345 "vis-motion-char-next",
346 VIS_HELP("Move cursor right, to the next character")
347 movement, { .i = VIS_MOVE_CHAR_NEXT }
349 [VIS_ACTION_CURSOR_LINE_CHAR_PREV] = {
350 "vis-motion-line-char-prev",
351 VIS_HELP("Move cursor left, to the previous character on the same line")
352 movement, { .i = VIS_MOVE_LINE_CHAR_PREV }
354 [VIS_ACTION_CURSOR_LINE_CHAR_NEXT] = {
355 "vis-motion-line-char-next",
356 VIS_HELP("Move cursor right, to the next character on the same line")
357 movement, { .i = VIS_MOVE_LINE_CHAR_NEXT }
359 [VIS_ACTION_CURSOR_CODEPOINT_PREV] = {
360 "vis-motion-codepoint-prev",
361 VIS_HELP("Move to the previous Unicode codepoint")
362 movement, { .i = VIS_MOVE_CODEPOINT_PREV }
364 [VIS_ACTION_CURSOR_CODEPOINT_NEXT] = {
365 "vis-motion-codepoint-next",
366 VIS_HELP("Move to the next Unicode codepoint")
367 movement, { .i = VIS_MOVE_CODEPOINT_NEXT }
369 [VIS_ACTION_CURSOR_WORD_START_PREV] = {
370 "vis-motion-word-start-prev",
371 VIS_HELP("Move cursor words backwards")
372 movement, { .i = VIS_MOVE_WORD_START_PREV }
374 [VIS_ACTION_CURSOR_WORD_START_NEXT] = {
375 "vis-motion-word-start-next",
376 VIS_HELP("Move cursor words forwards")
377 movement, { .i = VIS_MOVE_WORD_START_NEXT }
379 [VIS_ACTION_CURSOR_WORD_END_PREV] = {
380 "vis-motion-word-end-prev",
381 VIS_HELP("Move cursor backwards to the end of word")
382 movement, { .i = VIS_MOVE_WORD_END_PREV }
384 [VIS_ACTION_CURSOR_WORD_END_NEXT] = {
385 "vis-motion-word-end-next",
386 VIS_HELP("Move cursor forward to the end of word")
387 movement, { .i = VIS_MOVE_WORD_END_NEXT }
389 [VIS_ACTION_CURSOR_LONGWORD_START_PREV] = {
390 "vis-motion-bigword-start-prev",
391 VIS_HELP("Move cursor WORDS backwards")
392 movement, { .i = VIS_MOVE_LONGWORD_START_PREV }
394 [VIS_ACTION_CURSOR_LONGWORD_START_NEXT] = {
395 "vis-motion-bigword-start-next",
396 VIS_HELP("Move cursor WORDS forwards")
397 movement, { .i = VIS_MOVE_LONGWORD_START_NEXT }
399 [VIS_ACTION_CURSOR_LONGWORD_END_PREV] = {
400 "vis-motion-bigword-end-prev",
401 VIS_HELP("Move cursor backwards to the end of WORD")
402 movement, { .i = VIS_MOVE_LONGWORD_END_PREV }
404 [VIS_ACTION_CURSOR_LONGWORD_END_NEXT] = {
405 "vis-motion-bigword-end-next",
406 VIS_HELP("Move cursor forward to the end of WORD")
407 movement, { .i = VIS_MOVE_LONGWORD_END_NEXT }
409 [VIS_ACTION_CURSOR_LINE_UP] = {
410 "vis-motion-line-up",
411 VIS_HELP("Move cursor line upwards")
412 movement, { .i = VIS_MOVE_LINE_UP }
414 [VIS_ACTION_CURSOR_LINE_DOWN] = {
415 "vis-motion-line-down",
416 VIS_HELP("Move cursor line downwards")
417 movement, { .i = VIS_MOVE_LINE_DOWN }
419 [VIS_ACTION_CURSOR_LINE_START] = {
420 "vis-motion-line-start",
421 VIS_HELP("Move cursor to first non-blank character of the line")
422 movement, { .i = VIS_MOVE_LINE_START }
424 [VIS_ACTION_CURSOR_LINE_FINISH] = {
425 "vis-motion-line-finish",
426 VIS_HELP("Move cursor to last non-blank character of the line")
427 movement, { .i = VIS_MOVE_LINE_FINISH }
429 [VIS_ACTION_CURSOR_LINE_BEGIN] = {
430 "vis-motion-line-begin",
431 VIS_HELP("Move cursor to first character of the line")
432 movement, { .i = VIS_MOVE_LINE_BEGIN }
434 [VIS_ACTION_CURSOR_LINE_END] = {
435 "vis-motion-line-end",
436 VIS_HELP("Move cursor to end of the line")
437 movement, { .i = VIS_MOVE_LINE_END }
439 [VIS_ACTION_CURSOR_SCREEN_LINE_UP] = {
440 "vis-motion-screenline-up",
441 VIS_HELP("Move cursor screen/display line upwards")
442 movement, { .i = VIS_MOVE_SCREEN_LINE_UP }
444 [VIS_ACTION_CURSOR_SCREEN_LINE_DOWN] = {
445 "vis-motion-screenline-down",
446 VIS_HELP("Move cursor screen/display line downwards")
447 movement, { .i = VIS_MOVE_SCREEN_LINE_DOWN }
449 [VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN] = {
450 "vis-motion-screenline-begin",
451 VIS_HELP("Move cursor to beginning of screen/display line")
452 movement, { .i = VIS_MOVE_SCREEN_LINE_BEGIN }
454 [VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE] = {
455 "vis-motion-screenline-middle",
456 VIS_HELP("Move cursor to middle of screen/display line")
457 movement, { .i = VIS_MOVE_SCREEN_LINE_MIDDLE }
459 [VIS_ACTION_CURSOR_SCREEN_LINE_END] = {
460 "vis-motion-screenline-end",
461 VIS_HELP("Move cursor to end of screen/display line")
462 movement, { .i = VIS_MOVE_SCREEN_LINE_END }
464 [VIS_ACTION_CURSOR_PERCENT] = {
465 "vis-motion-percent",
466 VIS_HELP("Move to count % of file or matching item")
467 percent
469 [VIS_ACTION_CURSOR_BYTE] = {
470 "vis-motion-byte",
471 VIS_HELP("Move to absolute byte position")
472 movement, { .i = VIS_MOVE_BYTE }
474 [VIS_ACTION_CURSOR_BYTE_LEFT] = {
475 "vis-motion-byte-left",
476 VIS_HELP("Move count bytes to the left")
477 movement, { .i = VIS_MOVE_BYTE_LEFT }
479 [VIS_ACTION_CURSOR_BYTE_RIGHT] = {
480 "vis-motion-byte-right",
481 VIS_HELP("Move count bytes to the right")
482 movement, { .i = VIS_MOVE_BYTE_RIGHT }
484 [VIS_ACTION_CURSOR_PARAGRAPH_PREV] = {
485 "vis-motion-paragraph-prev",
486 VIS_HELP("Move cursor paragraph backward")
487 movement, { .i = VIS_MOVE_PARAGRAPH_PREV }
489 [VIS_ACTION_CURSOR_PARAGRAPH_NEXT] = {
490 "vis-motion-paragraph-next",
491 VIS_HELP("Move cursor paragraph forward")
492 movement, { .i = VIS_MOVE_PARAGRAPH_NEXT }
494 [VIS_ACTION_CURSOR_SENTENCE_PREV] = {
495 "vis-motion-sentence-prev",
496 VIS_HELP("Move cursor sentence backward")
497 movement, { .i = VIS_MOVE_SENTENCE_PREV }
499 [VIS_ACTION_CURSOR_SENTENCE_NEXT] = {
500 "vis-motion-sentence-next",
501 VIS_HELP("Move cursor sentence forward")
502 movement, { .i = VIS_MOVE_SENTENCE_NEXT }
504 [VIS_ACTION_CURSOR_BLOCK_START] = {
505 "vis-motion-block-start",
506 VIS_HELP("Move cursor to the opening curly brace in a block")
507 movement, { .i = VIS_MOVE_BLOCK_START }
509 [VIS_ACTION_CURSOR_BLOCK_END] = {
510 "vis-motion-block-end",
511 VIS_HELP("Move cursor to the closing curly brace in a block")
512 movement, { .i = VIS_MOVE_BLOCK_END }
514 [VIS_ACTION_CURSOR_PARENTHESIS_START] = {
515 "vis-motion-parenthesis-start",
516 VIS_HELP("Move cursor to the opening parenthesis inside a pair of parentheses")
517 movement, { .i = VIS_MOVE_PARENTHESIS_START }
519 [VIS_ACTION_CURSOR_PARENTHESIS_END] = {
520 "vis-motion-parenthesis-end",
521 VIS_HELP("Move cursor to the closing parenthesis inside a pair of parentheses")
522 movement, { .i = VIS_MOVE_PARENTHESIS_END }
524 [VIS_ACTION_CURSOR_COLUMN] = {
525 "vis-motion-column",
526 VIS_HELP("Move cursor to given column of current line")
527 movement, { .i = VIS_MOVE_COLUMN }
529 [VIS_ACTION_CURSOR_LINE_FIRST] = {
530 "vis-motion-line-first",
531 VIS_HELP("Move cursor to given line (defaults to first)")
532 gotoline, { .i = -1 }
534 [VIS_ACTION_CURSOR_LINE_LAST] = {
535 "vis-motion-line-last",
536 VIS_HELP("Move cursor to given line (defaults to last)")
537 gotoline, { .i = +1 }
539 [VIS_ACTION_CURSOR_WINDOW_LINE_TOP] = {
540 "vis-motion-window-line-top",
541 VIS_HELP("Move cursor to top line of the window")
542 movement, { .i = VIS_MOVE_WINDOW_LINE_TOP }
544 [VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE] = {
545 "vis-motion-window-line-middle",
546 VIS_HELP("Move cursor to middle line of the window")
547 movement, { .i = VIS_MOVE_WINDOW_LINE_MIDDLE }
549 [VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM] = {
550 "vis-motion-window-line-bottom",
551 VIS_HELP("Move cursor to bottom line of the window")
552 movement, { .i = VIS_MOVE_WINDOW_LINE_BOTTOM }
554 [VIS_ACTION_CURSOR_SEARCH_REPEAT_FORWARD] = {
555 "vis-motion-search-repeat-forward",
556 VIS_HELP("Move cursor to next match in forward direction")
557 movement, { .i = VIS_MOVE_SEARCH_REPEAT_FORWARD }
559 [VIS_ACTION_CURSOR_SEARCH_REPEAT_BACKWARD] = {
560 "vis-motion-search-repeat-backward",
561 VIS_HELP("Move cursor to previous match in backward direction")
562 movement, { .i = VIS_MOVE_SEARCH_REPEAT_BACKWARD }
564 [VIS_ACTION_CURSOR_SEARCH_REPEAT] = {
565 "vis-motion-search-repeat",
566 VIS_HELP("Move cursor to next match")
567 movement, { .i = VIS_MOVE_SEARCH_REPEAT }
569 [VIS_ACTION_CURSOR_SEARCH_REPEAT_REVERSE] = {
570 "vis-motion-search-repeat-reverse",
571 VIS_HELP("Move cursor to next match in opposite direction")
572 movement, { .i = VIS_MOVE_SEARCH_REPEAT_REVERSE }
574 [VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD] = {
575 "vis-motion-search-word-forward",
576 VIS_HELP("Move cursor to next occurrence of the word under cursor")
577 movement, { .i = VIS_MOVE_SEARCH_WORD_FORWARD }
579 [VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD] = {
580 "vis-motion-search-word-backward",
581 VIS_HELP("Move cursor to previous occurrence of the word under cursor")
582 movement, { .i = VIS_MOVE_SEARCH_WORD_BACKWARD }
584 [VIS_ACTION_WINDOW_PAGE_UP] = {
585 "vis-window-page-up",
586 VIS_HELP("Scroll window pages backwards (upwards)")
587 wscroll, { .i = -PAGE }
589 [VIS_ACTION_WINDOW_HALFPAGE_UP] = {
590 "vis-window-halfpage-up",
591 VIS_HELP("Scroll window half pages backwards (upwards)")
592 wscroll, { .i = -PAGE_HALF }
594 [VIS_ACTION_WINDOW_PAGE_DOWN] = {
595 "vis-window-page-down",
596 VIS_HELP("Scroll window pages forwards (downwards)")
597 wscroll, { .i = +PAGE }
599 [VIS_ACTION_WINDOW_HALFPAGE_DOWN] = {
600 "vis-window-halfpage-down",
601 VIS_HELP("Scroll window half pages forwards (downwards)")
602 wscroll, { .i = +PAGE_HALF }
604 [VIS_ACTION_MODE_NORMAL] = {
605 "vis-mode-normal",
606 VIS_HELP("Enter normal mode")
607 switchmode, { .i = VIS_MODE_NORMAL }
609 [VIS_ACTION_MODE_NORMAL_ESCAPE] = {
610 "vis-mode-normal-escape",
611 VIS_HELP("Reset count or remove all non-primary selections")
612 normalmode_escape,
614 [VIS_ACTION_MODE_VISUAL] = {
615 "vis-mode-visual-charwise",
616 VIS_HELP("Enter characterwise visual mode")
617 switchmode, { .i = VIS_MODE_VISUAL }
619 [VIS_ACTION_MODE_VISUAL_ESCAPE] = {
620 "vis-mode-visual-escape",
621 VIS_HELP("Reset count or switch to normal mode")
622 visualmode_escape,
624 [VIS_ACTION_MODE_VISUAL_LINE] = {
625 "vis-mode-visual-linewise",
626 VIS_HELP("Enter linewise visual mode")
627 switchmode, { .i = VIS_MODE_VISUAL_LINE }
629 [VIS_ACTION_MODE_INSERT] = {
630 "vis-mode-insert",
631 VIS_HELP("Enter insert mode")
632 insertmode, { .i = VIS_MOVE_NOP }
634 [VIS_ACTION_MODE_REPLACE] = {
635 "vis-mode-replace",
636 VIS_HELP("Enter replace mode")
637 replacemode, { .i = VIS_MOVE_NOP }
639 [VIS_ACTION_DELETE_CHAR_PREV] = {
640 "vis-delete-char-prev",
641 VIS_HELP("Delete the previous character")
642 delete, { .i = VIS_MOVE_CHAR_PREV }
644 [VIS_ACTION_DELETE_CHAR_NEXT] = {
645 "vis-delete-char-next",
646 VIS_HELP("Delete the next character")
647 delete, { .i = VIS_MOVE_CHAR_NEXT }
649 [VIS_ACTION_DELETE_LINE_BEGIN] = {
650 "vis-delete-line-begin",
651 VIS_HELP("Delete until the start of the current line")
652 delete, { .i = VIS_MOVE_LINE_BEGIN }
654 [VIS_ACTION_DELETE_WORD_PREV] = {
655 "vis-delete-word-prev",
656 VIS_HELP("Delete the previous WORD")
657 delete, { .i = VIS_MOVE_WORD_START_PREV }
659 [VIS_ACTION_JUMPLIST_PREV] = {
660 "vis-jumplist-prev",
661 VIS_HELP("Go to older cursor position in jump list")
662 jumplist, { .i = -1 }
664 [VIS_ACTION_JUMPLIST_NEXT] = {
665 "vis-jumplist-next",
666 VIS_HELP("Go to newer cursor position in jump list")
667 jumplist, { .i = +1 }
669 [VIS_ACTION_JUMPLIST_SAVE] = {
670 "vis-jumplist-save",
671 VIS_HELP("Save current selections in jump list")
672 jumplist, { .i = 0 }
674 [VIS_ACTION_UNDO] = {
675 "vis-undo",
676 VIS_HELP("Undo last change")
677 undo,
679 [VIS_ACTION_REDO] = {
680 "vis-redo",
681 VIS_HELP("Redo last change")
682 redo,
684 [VIS_ACTION_EARLIER] = {
685 "vis-earlier",
686 VIS_HELP("Goto older text state")
687 earlier,
689 [VIS_ACTION_LATER] = {
690 "vis-later",
691 VIS_HELP("Goto newer text state")
692 later,
694 [VIS_ACTION_MACRO_RECORD] = {
695 "vis-macro-record",
696 VIS_HELP("Record macro into given register")
697 macro_record,
699 [VIS_ACTION_MACRO_REPLAY] = {
700 "vis-macro-replay",
701 VIS_HELP("Replay macro, execute the content of the given register")
702 macro_replay,
704 [VIS_ACTION_MARK] = {
705 "vis-mark",
706 VIS_HELP("Use given mark for next action")
707 mark,
709 [VIS_ACTION_REDRAW] = {
710 "vis-redraw",
711 VIS_HELP("Redraw current editor content")
712 call, { .f = vis_redraw }
714 [VIS_ACTION_REPLACE_CHAR] = {
715 "vis-replace-char",
716 VIS_HELP("Replace the character under the cursor")
717 replace,
719 [VIS_ACTION_TOTILL_REPEAT] = {
720 "vis-motion-totill-repeat",
721 VIS_HELP("Repeat latest to/till motion")
722 movement, { .i = VIS_MOVE_TOTILL_REPEAT }
724 [VIS_ACTION_TOTILL_REVERSE] = {
725 "vis-motion-totill-reverse",
726 VIS_HELP("Repeat latest to/till motion but in opposite direction")
727 movement, { .i = VIS_MOVE_TOTILL_REVERSE }
729 [VIS_ACTION_PROMPT_SEARCH_FORWARD] = {
730 "vis-search-forward",
731 VIS_HELP("Search forward")
732 prompt_show, { .s = "/" }
734 [VIS_ACTION_PROMPT_SEARCH_BACKWARD] = {
735 "vis-search-backward",
736 VIS_HELP("Search backward")
737 prompt_show, { .s = "?" }
739 [VIS_ACTION_TILL_LEFT] = {
740 "vis-motion-till-left",
741 VIS_HELP("Till after the occurrence of character to the left")
742 movement_key, { .i = VIS_MOVE_LEFT_TILL }
744 [VIS_ACTION_TILL_RIGHT] = {
745 "vis-motion-till-right",
746 VIS_HELP("Till before the occurrence of character to the right")
747 movement_key, { .i = VIS_MOVE_RIGHT_TILL }
749 [VIS_ACTION_TO_LEFT] = {
750 "vis-motion-to-left",
751 VIS_HELP("To the first occurrence of character to the left")
752 movement_key, { .i = VIS_MOVE_LEFT_TO }
754 [VIS_ACTION_TO_RIGHT] = {
755 "vis-motion-to-right",
756 VIS_HELP("To the first occurrence of character to the right")
757 movement_key, { .i = VIS_MOVE_RIGHT_TO }
759 [VIS_ACTION_REGISTER] = {
760 "vis-register",
761 VIS_HELP("Use given register for next operator")
762 reg,
764 [VIS_ACTION_OPERATOR_CHANGE] = {
765 "vis-operator-change",
766 VIS_HELP("Change operator")
767 operator, { .i = VIS_OP_CHANGE }
769 [VIS_ACTION_OPERATOR_DELETE] = {
770 "vis-operator-delete",
771 VIS_HELP("Delete operator")
772 operator, { .i = VIS_OP_DELETE }
774 [VIS_ACTION_OPERATOR_YANK] = {
775 "vis-operator-yank",
776 VIS_HELP("Yank operator")
777 operator, { .i = VIS_OP_YANK }
779 [VIS_ACTION_OPERATOR_SHIFT_LEFT] = {
780 "vis-operator-shift-left",
781 VIS_HELP("Shift left operator")
782 operator, { .i = VIS_OP_SHIFT_LEFT }
784 [VIS_ACTION_OPERATOR_SHIFT_RIGHT] = {
785 "vis-operator-shift-right",
786 VIS_HELP("Shift right operator")
787 operator, { .i = VIS_OP_SHIFT_RIGHT }
789 [VIS_ACTION_COUNT] = {
790 "vis-count",
791 VIS_HELP("Count specifier")
792 count,
794 [VIS_ACTION_INSERT_NEWLINE] = {
795 "vis-insert-newline",
796 VIS_HELP("Insert a line break (depending on file type)")
797 call, { .f = vis_insert_nl }
799 [VIS_ACTION_INSERT_TAB] = {
800 "vis-insert-tab",
801 VIS_HELP("Insert a tab (might be converted to spaces)")
802 call, { .f = vis_insert_tab }
804 [VIS_ACTION_INSERT_VERBATIM] = {
805 "vis-insert-verbatim",
806 VIS_HELP("Insert Unicode character based on code point")
807 insert_verbatim,
809 [VIS_ACTION_INSERT_REGISTER] = {
810 "vis-insert-register",
811 VIS_HELP("Insert specified register content")
812 insert_register,
814 [VIS_ACTION_WINDOW_NEXT] = {
815 "vis-window-next",
816 VIS_HELP("Focus next window")
817 call, { .f = vis_window_next }
819 [VIS_ACTION_WINDOW_PREV] = {
820 "vis-window-prev",
821 VIS_HELP("Focus previous window")
822 call, { .f = vis_window_prev }
824 [VIS_ACTION_APPEND_CHAR_NEXT] = {
825 "vis-append-char-next",
826 VIS_HELP("Append text after the cursor")
827 insertmode, { .i = VIS_MOVE_LINE_CHAR_NEXT }
829 [VIS_ACTION_APPEND_LINE_END] = {
830 "vis-append-line-end",
831 VIS_HELP("Append text after the end of the line")
832 insertmode, { .i = VIS_MOVE_LINE_END },
834 [VIS_ACTION_INSERT_LINE_START] = {
835 "vis-insert-line-start",
836 VIS_HELP("Insert text before the first non-blank in the line")
837 insertmode, { .i = VIS_MOVE_LINE_START },
839 [VIS_ACTION_OPEN_LINE_ABOVE] = {
840 "vis-open-line-above",
841 VIS_HELP("Begin a new line above the cursor")
842 openline, { .i = -1 }
844 [VIS_ACTION_OPEN_LINE_BELOW] = {
845 "vis-open-line-below",
846 VIS_HELP("Begin a new line below the cursor")
847 openline, { .i = +1 }
849 [VIS_ACTION_JOIN_LINES] = {
850 "vis-join-lines",
851 VIS_HELP("Join selected lines")
852 join, { .s = " " }
854 [VIS_ACTION_JOIN_LINES_TRIM] = {
855 "vis-join-lines-trim",
856 VIS_HELP("Join selected lines, remove white space")
857 join, { .s = "" }
859 [VIS_ACTION_PROMPT_SHOW] = {
860 "vis-prompt-show",
861 VIS_HELP("Show editor command line prompt")
862 prompt_show, { .s = ":" }
864 [VIS_ACTION_REPEAT] = {
865 "vis-repeat",
866 VIS_HELP("Repeat latest editor command")
867 repeat
869 [VIS_ACTION_SELECTION_FLIP] = {
870 "vis-selection-flip",
871 VIS_HELP("Flip selection, move cursor to other end")
872 selection_end,
874 [VIS_ACTION_WINDOW_REDRAW_TOP] = {
875 "vis-window-redraw-top",
876 VIS_HELP("Redraw cursor line at the top of the window")
877 window, { .w = view_redraw_top }
879 [VIS_ACTION_WINDOW_REDRAW_CENTER] = {
880 "vis-window-redraw-center",
881 VIS_HELP("Redraw cursor line at the center of the window")
882 window, { .w = view_redraw_center }
884 [VIS_ACTION_WINDOW_REDRAW_BOTTOM] = {
885 "vis-window-redraw-bottom",
886 VIS_HELP("Redraw cursor line at the bottom of the window")
887 window, { .w = view_redraw_bottom }
889 [VIS_ACTION_WINDOW_SLIDE_UP] = {
890 "vis-window-slide-up",
891 VIS_HELP("Slide window content upwards")
892 wslide, { .i = -1 }
894 [VIS_ACTION_WINDOW_SLIDE_DOWN] = {
895 "vis-window-slide-down",
896 VIS_HELP("Slide window content downwards")
897 wslide, { .i = +1 }
899 [VIS_ACTION_PUT_AFTER] = {
900 "vis-put-after",
901 VIS_HELP("Put text after the cursor")
902 operator, { .i = VIS_OP_PUT_AFTER }
904 [VIS_ACTION_PUT_BEFORE] = {
905 "vis-put-before",
906 VIS_HELP("Put text before the cursor")
907 operator, { .i = VIS_OP_PUT_BEFORE }
909 [VIS_ACTION_SELECTIONS_MATCH_WORD] = {
910 "vis-selections-select-word",
911 VIS_HELP("Select word under cursor")
912 selections_match_word,
914 [VIS_ACTION_SELECTIONS_NEW_LINE_ABOVE] = {
915 "vis-selection-new-lines-above",
916 VIS_HELP("Create a new selection on the line above")
917 selections_new, { .i = -1 }
919 [VIS_ACTION_SELECTIONS_NEW_LINE_ABOVE_FIRST] = {
920 "vis-selection-new-lines-above-first",
921 VIS_HELP("Create a new selection on the line above the first selection")
922 selections_new, { .i = INT_MIN }
924 [VIS_ACTION_SELECTIONS_NEW_LINE_BELOW] = {
925 "vis-selection-new-lines-below",
926 VIS_HELP("Create a new selection on the line below")
927 selections_new, { .i = +1 }
929 [VIS_ACTION_SELECTIONS_NEW_LINE_BELOW_LAST] = {
930 "vis-selection-new-lines-below-last",
931 VIS_HELP("Create a new selection on the line below the last selection")
932 selections_new, { .i = INT_MAX }
934 [VIS_ACTION_SELECTIONS_NEW_LINES_BEGIN] = {
935 "vis-selection-new-lines-begin",
936 VIS_HELP("Create a new selection at the start of every line covered by selection")
937 operator, { .i = VIS_OP_CURSOR_SOL }
939 [VIS_ACTION_SELECTIONS_NEW_LINES_END] = {
940 "vis-selection-new-lines-end",
941 VIS_HELP("Create a new selection at the end of every line covered by selection")
942 operator, { .i = VIS_OP_CURSOR_EOL }
944 [VIS_ACTION_SELECTIONS_NEW_MATCH_NEXT] = {
945 "vis-selection-new-match-next",
946 VIS_HELP("Select the next region matching the current selection")
947 selections_match_next,
949 [VIS_ACTION_SELECTIONS_NEW_MATCH_SKIP] = {
950 "vis-selection-new-match-skip",
951 VIS_HELP("Clear current selection, but select next match")
952 selections_match_skip,
954 [VIS_ACTION_SELECTIONS_ALIGN] = {
955 "vis-selections-align",
956 VIS_HELP("Try to align all selections on the same column")
957 selections_align,
959 [VIS_ACTION_SELECTIONS_ALIGN_INDENT_LEFT] = {
960 "vis-selections-align-indent-left",
961 VIS_HELP("Left-align all selections by inserting spaces")
962 selections_align_indent, { .i = -1 }
964 [VIS_ACTION_SELECTIONS_ALIGN_INDENT_RIGHT] = {
965 "vis-selections-align-indent-right",
966 VIS_HELP("Right-align all selections by inserting spaces")
967 selections_align_indent, { .i = +1 }
969 [VIS_ACTION_SELECTIONS_REMOVE_ALL] = {
970 "vis-selections-remove-all",
971 VIS_HELP("Remove all but the primary selection")
972 selections_clear,
974 [VIS_ACTION_SELECTIONS_REMOVE_LAST] = {
975 "vis-selections-remove-last",
976 VIS_HELP("Remove primary selection")
977 selections_remove,
979 [VIS_ACTION_SELECTIONS_REMOVE_COLUMN] = {
980 "vis-selections-remove-column",
981 VIS_HELP("Remove count selection column")
982 selections_remove_column, { .i = 1 }
984 [VIS_ACTION_SELECTIONS_REMOVE_COLUMN_EXCEPT] = {
985 "vis-selections-remove-column-except",
986 VIS_HELP("Remove all but the count selection column")
987 selections_remove_column_except, { .i = 1 }
989 [VIS_ACTION_SELECTIONS_PREV] = {
990 "vis-selection-prev",
991 VIS_HELP("Move to the previous selection")
992 selections_navigate, { .i = -PAGE_HALF }
994 [VIS_ACTION_SELECTIONS_NEXT] = {
995 "vis-selection-next",
996 VIS_HELP("Move to the next selection")
997 selections_navigate, { .i = +PAGE_HALF }
999 [VIS_ACTION_SELECTIONS_ROTATE_LEFT] = {
1000 "vis-selections-rotate-left",
1001 VIS_HELP("Rotate selections left")
1002 selections_rotate, { .i = -1 }
1004 [VIS_ACTION_SELECTIONS_ROTATE_RIGHT] = {
1005 "vis-selections-rotate-right",
1006 VIS_HELP("Rotate selections right")
1007 selections_rotate, { .i = +1 }
1009 [VIS_ACTION_SELECTIONS_TRIM] = {
1010 "vis-selections-trim",
1011 VIS_HELP("Remove leading and trailing white space from selections")
1012 selections_trim
1014 [VIS_ACTION_SELECTIONS_SAVE] = {
1015 "vis-selections-save",
1016 VIS_HELP("Save currently active selections to mark")
1017 selections_save
1019 [VIS_ACTION_SELECTIONS_RESTORE] = {
1020 "vis-selections-restore",
1021 VIS_HELP("Restore selections from mark")
1022 selections_restore
1024 [VIS_ACTION_SELECTIONS_UNION] = {
1025 "vis-selections-union",
1026 VIS_HELP("Add selections from mark")
1027 selections_union
1029 [VIS_ACTION_SELECTIONS_INTERSECT] = {
1030 "vis-selections-intersect",
1031 VIS_HELP("Intersect with selections from mark")
1032 selections_intersect
1034 [VIS_ACTION_SELECTIONS_COMPLEMENT] = {
1035 "vis-selections-complement",
1036 VIS_HELP("Complement selections")
1037 selections_complement
1039 [VIS_ACTION_SELECTIONS_MINUS] = {
1040 "vis-selections-minus",
1041 VIS_HELP("Subtract selections from mark")
1042 selections_minus
1044 [VIS_ACTION_SELECTIONS_COMBINE_UNION] = {
1045 "vis-selections-combine-union",
1046 VIS_HELP("Pairwise union with selections from mark")
1047 selections_combine, { .combine = combine_union }
1049 [VIS_ACTION_SELECTIONS_COMBINE_INTERSECT] = {
1050 "vis-selections-combine-intersect",
1051 VIS_HELP("Pairwise intersect with selections from mark")
1052 selections_combine, { .combine = combine_intersect }
1054 [VIS_ACTION_SELECTIONS_COMBINE_LONGER] = {
1055 "vis-selections-combine-longer",
1056 VIS_HELP("Pairwise combine: take longer")
1057 selections_combine, { .combine = combine_longer }
1059 [VIS_ACTION_SELECTIONS_COMBINE_SHORTER] = {
1060 "vis-selections-combine-shorter",
1061 VIS_HELP("Pairwise combine: take shorter")
1062 selections_combine, { .combine = combine_shorter }
1064 [VIS_ACTION_SELECTIONS_COMBINE_LEFTMOST] = {
1065 "vis-selections-combine-leftmost",
1066 VIS_HELP("Pairwise combine: leftmost")
1067 selections_combine, { .combine = combine_leftmost }
1069 [VIS_ACTION_SELECTIONS_COMBINE_RIGHTMOST] = {
1070 "vis-selections-combine-rightmost",
1071 VIS_HELP("Pairwise combine: rightmost")
1072 selections_combine, { .combine = combine_rightmost }
1074 [VIS_ACTION_TEXT_OBJECT_WORD_OUTER] = {
1075 "vis-textobject-word-outer",
1076 VIS_HELP("A word leading and trailing whitespace included")
1077 textobj, { .i = VIS_TEXTOBJECT_OUTER_WORD }
1079 [VIS_ACTION_TEXT_OBJECT_WORD_INNER] = {
1080 "vis-textobject-word-inner",
1081 VIS_HELP("A word leading and trailing whitespace excluded")
1082 textobj, { .i = VIS_TEXTOBJECT_INNER_WORD }
1084 [VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER] = {
1085 "vis-textobject-bigword-outer",
1086 VIS_HELP("A WORD leading and trailing whitespace included")
1087 textobj, { .i = VIS_TEXTOBJECT_OUTER_LONGWORD }
1089 [VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER] = {
1090 "vis-textobject-bigword-inner",
1091 VIS_HELP("A WORD leading and trailing whitespace excluded")
1092 textobj, { .i = VIS_TEXTOBJECT_INNER_LONGWORD }
1094 [VIS_ACTION_TEXT_OBJECT_SENTENCE] = {
1095 "vis-textobject-sentence",
1096 VIS_HELP("A sentence")
1097 textobj, { .i = VIS_TEXTOBJECT_SENTENCE }
1099 [VIS_ACTION_TEXT_OBJECT_PARAGRAPH] = {
1100 "vis-textobject-paragraph",
1101 VIS_HELP("A paragraph")
1102 textobj, { .i = VIS_TEXTOBJECT_PARAGRAPH }
1104 [VIS_ACTION_TEXT_OBJECT_PARAGRAPH_OUTER] = {
1105 "vis-textobject-paragraph-outer",
1106 VIS_HELP("A paragraph (outer variant)")
1107 textobj, { .i = VIS_TEXTOBJECT_PARAGRAPH_OUTER }
1109 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER] = {
1110 "vis-textobject-square-bracket-outer",
1111 VIS_HELP("[] block (outer variant)")
1112 textobj, { .i = VIS_TEXTOBJECT_OUTER_SQUARE_BRACKET }
1114 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER] = {
1115 "vis-textobject-square-bracket-inner",
1116 VIS_HELP("[] block (inner variant)")
1117 textobj, { .i = VIS_TEXTOBJECT_INNER_SQUARE_BRACKET }
1119 [VIS_ACTION_TEXT_OBJECT_PARENTHESIS_OUTER] = {
1120 "vis-textobject-parenthesis-outer",
1121 VIS_HELP("() block (outer variant)")
1122 textobj, { .i = VIS_TEXTOBJECT_OUTER_PARENTHESIS }
1124 [VIS_ACTION_TEXT_OBJECT_PARENTHESIS_INNER] = {
1125 "vis-textobject-parenthesis-inner",
1126 VIS_HELP("() block (inner variant)")
1127 textobj, { .i = VIS_TEXTOBJECT_INNER_PARENTHESIS }
1129 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER] = {
1130 "vis-textobject-angle-bracket-outer",
1131 VIS_HELP("<> block (outer variant)")
1132 textobj, { .i = VIS_TEXTOBJECT_OUTER_ANGLE_BRACKET }
1134 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER] = {
1135 "vis-textobject-angle-bracket-inner",
1136 VIS_HELP("<> block (inner variant)")
1137 textobj, { .i = VIS_TEXTOBJECT_INNER_ANGLE_BRACKET }
1139 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER] = {
1140 "vis-textobject-curly-bracket-outer",
1141 VIS_HELP("{} block (outer variant)")
1142 textobj, { .i = VIS_TEXTOBJECT_OUTER_CURLY_BRACKET }
1144 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER] = {
1145 "vis-textobject-curly-bracket-inner",
1146 VIS_HELP("{} block (inner variant)")
1147 textobj, { .i = VIS_TEXTOBJECT_INNER_CURLY_BRACKET }
1149 [VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER] = {
1150 "vis-textobject-quote-outer",
1151 VIS_HELP("A quoted string, including the quotation marks")
1152 textobj, { .i = VIS_TEXTOBJECT_OUTER_QUOTE }
1154 [VIS_ACTION_TEXT_OBJECT_QUOTE_INNER] = {
1155 "vis-textobject-quote-inner",
1156 VIS_HELP("A quoted string, excluding the quotation marks")
1157 textobj, { .i = VIS_TEXTOBJECT_INNER_QUOTE }
1159 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER] = {
1160 "vis-textobject-single-quote-outer",
1161 VIS_HELP("A single quoted string, including the quotation marks")
1162 textobj, { .i = VIS_TEXTOBJECT_OUTER_SINGLE_QUOTE }
1164 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER] = {
1165 "vis-textobject-single-quote-inner",
1166 VIS_HELP("A single quoted string, excluding the quotation marks")
1167 textobj, { .i = VIS_TEXTOBJECT_INNER_SINGLE_QUOTE }
1169 [VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER] = {
1170 "vis-textobject-backtick-outer",
1171 VIS_HELP("A backtick delimited string (outer variant)")
1172 textobj, { .i = VIS_TEXTOBJECT_OUTER_BACKTICK }
1174 [VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER] = {
1175 "vis-textobject-backtick-inner",
1176 VIS_HELP("A backtick delimited string (inner variant)")
1177 textobj, { .i = VIS_TEXTOBJECT_INNER_BACKTICK }
1179 [VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER] = {
1180 "vis-textobject-entire-outer",
1181 VIS_HELP("The whole text content")
1182 textobj, { .i = VIS_TEXTOBJECT_OUTER_ENTIRE }
1184 [VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER] = {
1185 "vis-textobject-entire-inner",
1186 VIS_HELP("The whole text content, except for leading and trailing empty lines")
1187 textobj, { .i = VIS_TEXTOBJECT_INNER_ENTIRE }
1189 [VIS_ACTION_TEXT_OBJECT_LINE_OUTER] = {
1190 "vis-textobject-line-outer",
1191 VIS_HELP("The whole line")
1192 textobj, { .i = VIS_TEXTOBJECT_OUTER_LINE }
1194 [VIS_ACTION_TEXT_OBJECT_LINE_INNER] = {
1195 "vis-textobject-line-inner",
1196 VIS_HELP("The whole line, excluding leading and trailing whitespace")
1197 textobj, { .i = VIS_TEXTOBJECT_INNER_LINE }
1199 [VIS_ACTION_TEXT_OBJECT_INDENTATION] = {
1200 "vis-textobject-indentation",
1201 VIS_HELP("All adjacent lines with the same indentation level as the current one")
1202 textobj, { .i = VIS_TEXTOBJECT_INDENTATION }
1204 [VIS_ACTION_TEXT_OBJECT_SEARCH_FORWARD] = {
1205 "vis-textobject-search-forward",
1206 VIS_HELP("The next search match in forward direction")
1207 textobj, { .i = VIS_TEXTOBJECT_SEARCH_FORWARD }
1209 [VIS_ACTION_TEXT_OBJECT_SEARCH_BACKWARD] = {
1210 "vis-textobject-search-backward",
1211 VIS_HELP("The next search match in backward direction")
1212 textobj, { .i = VIS_TEXTOBJECT_SEARCH_BACKWARD }
1214 [VIS_ACTION_UNICODE_INFO] = {
1215 "vis-unicode-info",
1216 VIS_HELP("Show Unicode codepoint(s) of character under cursor")
1217 unicode_info, { .i = VIS_ACTION_UNICODE_INFO }
1219 [VIS_ACTION_UTF8_INFO] = {
1220 "vis-utf8-info",
1221 VIS_HELP("Show UTF-8 encoded codepoint(s) of character under cursor")
1222 unicode_info, { .i = VIS_ACTION_UTF8_INFO }
1224 [VIS_ACTION_NOP] = {
1225 "vis-nop",
1226 VIS_HELP("Ignore key, do nothing")
1227 nop,
1231 #include "config.h"
1233 /** key bindings functions */
1235 static const char *nop(Vis *vis, const char *keys, const Arg *arg) {
1236 return keys;
1239 static const char *macro_record(Vis *vis, const char *keys, const Arg *arg) {
1240 if (!vis_macro_record_stop(vis)) {
1241 if (!keys[0])
1242 return NULL;
1243 const char *next = vis_keys_next(vis, keys);
1244 if (next - keys > 1)
1245 return next;
1246 enum VisRegister reg = vis_register_from(vis, keys[0]);
1247 vis_macro_record(vis, reg);
1248 keys++;
1250 vis_draw(vis);
1251 return keys;
1254 static const char *macro_replay(Vis *vis, const char *keys, const Arg *arg) {
1255 if (!keys[0])
1256 return NULL;
1257 const char *next = vis_keys_next(vis, keys);
1258 if (next - keys > 1)
1259 return next;
1260 enum VisRegister reg = vis_register_from(vis, keys[0]);
1261 vis_macro_replay(vis, reg);
1262 return keys+1;
1265 static const char *suspend(Vis *vis, const char *keys, const Arg *arg) {
1266 vis_suspend(vis);
1267 return keys;
1270 static const char *repeat(Vis *vis, const char *keys, const Arg *arg) {
1271 vis_repeat(vis);
1272 return keys;
1275 static const char *selections_new(Vis *vis, const char *keys, const Arg *arg) {
1276 View *view = vis_view(vis);
1277 bool anchored = view_selections_anchored(view_selections_primary_get(view));
1278 VisCountIterator it = vis_count_iterator_get(vis, 1);
1279 while (vis_count_iterator_next(&it)) {
1280 Selection *sel = NULL;
1281 switch (arg->i) {
1282 case -1:
1283 case +1:
1284 sel = view_selections_primary_get(view);
1285 break;
1286 case INT_MIN:
1287 sel = view_selections(view);
1288 break;
1289 case INT_MAX:
1290 for (Selection *s = view_selections(view); s; s = view_selections_next(s))
1291 sel = s;
1292 break;
1295 if (!sel)
1296 return keys;
1298 size_t oldpos = view_cursors_pos(sel);
1299 if (arg->i > 0)
1300 view_line_down(sel);
1301 else if (arg->i < 0)
1302 view_line_up(sel);
1303 size_t newpos = view_cursors_pos(sel);
1304 view_cursors_to(sel, oldpos);
1305 Selection *sel_new = view_selections_new(view, newpos);
1306 if (!sel_new) {
1307 if (arg->i == -1)
1308 sel_new = view_selections_prev(sel);
1309 else if (arg->i == +1)
1310 sel_new = view_selections_next(sel);
1312 if (sel_new) {
1313 view_selections_primary_set(sel_new);
1314 view_selections_anchor(sel_new, anchored);
1317 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1318 return keys;
1321 static const char *selections_align(Vis *vis, const char *keys, const Arg *arg) {
1322 View *view = vis_view(vis);
1323 Text *txt = vis_text(vis);
1324 int mincol = INT_MAX;
1325 for (Selection *s = view_selections(view); s; s = view_selections_next(s)) {
1326 int col = view_cursors_cell_get(s);
1327 if (col >= 0 && col < mincol)
1328 mincol = col;
1330 for (Selection *s = view_selections(view); s; s = view_selections_next(s)) {
1331 if (view_cursors_cell_set(s, mincol) == -1) {
1332 size_t pos = view_cursors_pos(s);
1333 size_t col = text_line_width_set(txt, pos, mincol);
1334 view_cursors_to(s, col);
1337 return keys;
1340 static const char *selections_align_indent(Vis *vis, const char *keys, const Arg *arg) {
1341 View *view = vis_view(vis);
1342 Text *txt = vis_text(vis);
1343 bool left_align = arg->i < 0;
1344 int columns = view_selections_column_count(view);
1346 for (int i = 0; i < columns; i++) {
1347 int mincol = INT_MAX, maxcol = 0;
1348 for (Selection *s = view_selections_column(view, i); s; s = view_selections_column_next(s, i)) {
1349 Filerange sel = view_selections_get(s);
1350 size_t pos = left_align ? sel.start : sel.end;
1351 int col = text_line_width_get(txt, pos);
1352 if (col < mincol)
1353 mincol = col;
1354 if (col > maxcol)
1355 maxcol = col;
1358 size_t len = maxcol - mincol;
1359 char *buf = malloc(len+1);
1360 if (!buf)
1361 return keys;
1362 memset(buf, ' ', len);
1364 for (Selection *s = view_selections_column(view, i); s; s = view_selections_column_next(s, i)) {
1365 Filerange sel = view_selections_get(s);
1366 size_t pos = left_align ? sel.start : sel.end;
1367 size_t ipos = sel.start;
1368 int col = text_line_width_get(txt, pos);
1369 if (col < maxcol) {
1370 size_t off = maxcol - col;
1371 if (off <= len)
1372 text_insert(txt, ipos, buf, off);
1376 free(buf);
1379 view_draw(view);
1380 return keys;
1383 static const char *selections_clear(Vis *vis, const char *keys, const Arg *arg) {
1384 View *view = vis_view(vis);
1385 if (view_selections_count(view) > 1)
1386 view_selections_dispose_all(view);
1387 else
1388 view_selection_clear(view_selections_primary_get(view));
1389 return keys;
1392 static const char *selections_match_word(Vis *vis, const char *keys, const Arg *arg) {
1393 Text *txt = vis_text(vis);
1394 View *view = vis_view(vis);
1395 for (Selection *s = view_selections(view); s; s = view_selections_next(s)) {
1396 Filerange word = text_object_word(txt, view_cursors_pos(s));
1397 if (text_range_valid(&word))
1398 view_selections_set(s, &word);
1400 vis_mode_switch(vis, VIS_MODE_VISUAL);
1401 return keys;
1404 static const Selection *selection_new_primary(View *view, Filerange *r) {
1405 Text *txt = view_text(view);
1406 size_t pos = text_char_prev(txt, r->end);
1407 Selection *s = view_selections_new(view, pos);
1408 if (!s)
1409 return NULL;
1410 view_selections_set(s, r);
1411 view_selections_anchor(s, true);
1412 view_selections_primary_set(s);
1413 return s;
1416 static const char *selections_match_next_literal(Vis *vis, const char *keys, const Arg *arg) {
1417 Text *txt = vis_text(vis);
1418 View *view = vis_view(vis);
1419 Selection *s = view_selections_primary_get(view);
1420 Filerange sel = view_selections_get(s);
1421 size_t len = text_range_size(&sel);
1422 if (!len)
1423 return keys;
1425 char *buf = text_bytes_alloc0(txt, sel.start, len);
1426 if (!buf)
1427 return keys;
1429 size_t start = text_find_next(txt, sel.end, buf);
1430 Filerange match = text_range_new(start, start+len);
1431 if (start != sel.end && selection_new_primary(view, &match))
1432 goto out;
1434 sel = view_selections_get(view_selections(view));
1435 start = text_find_prev(txt, sel.start, buf);
1436 if (start == sel.start)
1437 goto out;
1439 match = text_range_new(start, start+len);
1440 selection_new_primary(view, &match);
1442 out:
1443 free(buf);
1444 return keys;
1447 static const char *selections_match_next(Vis *vis, const char *keys, const Arg *arg) {
1448 Text *txt = vis_text(vis);
1449 View *view = vis_view(vis);
1450 Selection *s = view_selections_primary_get(view);
1451 Filerange sel = view_selections_get(s);
1452 if (!text_range_valid(&sel))
1453 return keys;
1455 Filerange word = text_object_word(txt, view_cursors_pos(s));
1456 if (!text_range_equal(&sel, &word))
1457 return selections_match_next_literal(vis, keys, arg);
1459 char *buf = text_bytes_alloc0(txt, sel.start, text_range_size(&sel));
1460 if (!buf)
1461 return keys;
1463 word = text_object_word_find_next(txt, sel.end, buf);
1464 if (text_range_valid(&word) && selection_new_primary(view, &word))
1465 goto out;
1467 sel = view_selections_get(view_selections(view));
1468 word = text_object_word_find_prev(txt, sel.start, buf);
1469 if (!text_range_valid(&word))
1470 goto out;
1471 selection_new_primary(view, &word);
1473 out:
1474 free(buf);
1475 return keys;
1478 static const char *selections_match_skip(Vis *vis, const char *keys, const Arg *arg) {
1479 View *view = vis_view(vis);
1480 Selection *sel = view_selections_primary_get(view);
1481 keys = selections_match_next(vis, keys, arg);
1482 if (sel != view_selections_primary_get(view))
1483 view_selections_dispose(sel);
1484 return keys;
1487 static const char *selections_remove(Vis *vis, const char *keys, const Arg *arg) {
1488 View *view = vis_view(vis);
1489 view_selections_dispose(view_selections_primary_get(view));
1490 view_cursor_to(view, view_cursor_get(view));
1491 return keys;
1494 static const char *selections_remove_column(Vis *vis, const char *keys, const Arg *arg) {
1495 View *view = vis_view(vis);
1496 int max = view_selections_column_count(view);
1497 int column = vis_count_get_default(vis, arg->i) - 1;
1498 if (column >= max)
1499 column = max - 1;
1500 if (view_selections_count(view) == 1) {
1501 vis_keys_feed(vis, "<Escape>");
1502 return keys;
1505 for (Selection *s = view_selections_column(view, column), *next; s; s = next) {
1506 next = view_selections_column_next(s, column);
1507 view_selections_dispose(s);
1510 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1511 return keys;
1514 static const char *selections_remove_column_except(Vis *vis, const char *keys, const Arg *arg) {
1515 View *view = vis_view(vis);
1516 int max = view_selections_column_count(view);
1517 int column = vis_count_get_default(vis, arg->i) - 1;
1518 if (column >= max)
1519 column = max - 1;
1520 if (view_selections_count(view) == 1) {
1521 vis_redraw(vis);
1522 return keys;
1525 Selection *sel = view_selections(view);
1526 Selection *col = view_selections_column(view, column);
1527 for (Selection *next; sel; sel = next) {
1528 next = view_selections_next(sel);
1529 if (sel == col)
1530 col = view_selections_column_next(col, column);
1531 else
1532 view_selections_dispose(sel);
1535 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1536 return keys;
1539 static const char *selections_navigate(Vis *vis, const char *keys, const Arg *arg) {
1540 View *view = vis_view(vis);
1541 if (view_selections_count(view) == 1)
1542 return wscroll(vis, keys, arg);
1543 Selection *s = view_selections_primary_get(view);
1544 VisCountIterator it = vis_count_iterator_get(vis, 1);
1545 while (vis_count_iterator_next(&it)) {
1546 if (arg->i > 0) {
1547 s = view_selections_next(s);
1548 if (!s)
1549 s = view_selections(view);
1550 } else {
1551 s = view_selections_prev(s);
1552 if (!s) {
1553 s = view_selections(view);
1554 for (Selection *n = s; n; n = view_selections_next(n))
1555 s = n;
1559 view_selections_primary_set(s);
1560 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1561 return keys;
1564 static const char *selections_rotate(Vis *vis, const char *keys, const Arg *arg) {
1566 typedef struct {
1567 Selection *sel;
1568 char *data;
1569 size_t len;
1570 } Rotate;
1572 Array arr;
1573 Text *txt = vis_text(vis);
1574 View *view = vis_view(vis);
1575 int columns = view_selections_column_count(view);
1576 int selections = columns == 1 ? view_selections_count(view) : columns;
1577 int count = vis_count_get_default(vis, 1);
1578 array_init_sized(&arr, sizeof(Rotate));
1579 if (!array_reserve(&arr, selections))
1580 return keys;
1581 size_t line = 0;
1583 for (Selection *s = view_selections(view), *next; s; s = next) {
1584 next = view_selections_next(s);
1585 size_t line_next = 0;
1587 Filerange sel = view_selections_get(s);
1588 Rotate rot;
1589 rot.sel = s;
1590 rot.len = text_range_size(&sel);
1591 if ((rot.data = malloc(rot.len)))
1592 rot.len = text_bytes_get(txt, sel.start, rot.len, rot.data);
1593 else
1594 rot.len = 0;
1595 array_add(&arr, &rot);
1597 if (!line)
1598 line = text_lineno_by_pos(txt, view_cursors_pos(s));
1599 if (next)
1600 line_next = text_lineno_by_pos(txt, view_cursors_pos(next));
1601 if (!next || (columns > 1 && line != line_next)) {
1602 size_t len = array_length(&arr);
1603 size_t off = arg->i > 0 ? count % len : len - (count % len);
1604 for (size_t i = 0; i < len; i++) {
1605 size_t j = (i + off) % len;
1606 Rotate *oldrot = array_get(&arr, i);
1607 Rotate *newrot = array_get(&arr, j);
1608 if (!oldrot || !newrot || oldrot == newrot)
1609 continue;
1610 Filerange newsel = view_selections_get(newrot->sel);
1611 if (!text_range_valid(&newsel))
1612 continue;
1613 if (!text_delete_range(txt, &newsel))
1614 continue;
1615 if (!text_insert(txt, newsel.start, oldrot->data, oldrot->len))
1616 continue;
1617 newsel.end = newsel.start + oldrot->len;
1618 view_selections_set(newrot->sel, &newsel);
1619 free(oldrot->data);
1621 array_clear(&arr);
1623 line = line_next;
1626 array_release(&arr);
1627 vis_count_set(vis, VIS_COUNT_UNKNOWN);
1628 return keys;
1631 static const char *selections_trim(Vis *vis, const char *keys, const Arg *arg) {
1632 Text *txt = vis_text(vis);
1633 View *view = vis_view(vis);
1634 for (Selection *s = view_selections(view), *next; s; s = next) {
1635 next = view_selections_next(s);
1636 Filerange sel = view_selections_get(s);
1637 if (!text_range_valid(&sel))
1638 continue;
1639 for (char b; sel.start < sel.end && text_byte_get(txt, sel.end-1, &b)
1640 && isspace((unsigned char)b); sel.end--);
1641 for (char b; sel.start <= sel.end && text_byte_get(txt, sel.start, &b)
1642 && isspace((unsigned char)b); sel.start++);
1643 if (sel.start < sel.end) {
1644 view_selections_set(s, &sel);
1645 } else if (!view_selections_dispose(s)) {
1646 vis_mode_switch(vis, VIS_MODE_NORMAL);
1649 return keys;
1652 static void selections_set(Vis *vis, View *view, Array *sel) {
1653 enum VisMode mode = vis_mode_get(vis);
1654 bool anchored = mode == VIS_MODE_VISUAL || mode == VIS_MODE_VISUAL_LINE;
1655 view_selections_set_all(view, sel, anchored);
1656 if (!anchored)
1657 view_selections_clear_all(view);
1660 static const char *selections_save(Vis *vis, const char *keys, const Arg *arg) {
1661 Win *win = vis_window(vis);
1662 View *view = vis_view(vis);
1663 enum VisMark mark = vis_mark_used(vis);
1664 Array sel = view_selections_get_all(view);
1665 vis_mark_set(win, mark, &sel);
1666 array_release(&sel);
1667 vis_cancel(vis);
1668 return keys;
1671 static const char *selections_restore(Vis *vis, const char *keys, const Arg *arg) {
1672 Win *win = vis_window(vis);
1673 View *view = vis_view(vis);
1674 enum VisMark mark = vis_mark_used(vis);
1675 Array sel = vis_mark_get(win, mark);
1676 selections_set(vis, view, &sel);
1677 array_release(&sel);
1678 vis_cancel(vis);
1679 return keys;
1682 static const char *selections_union(Vis *vis, const char *keys, const Arg *arg) {
1683 Win *win = vis_window(vis);
1684 View *view = vis_view(vis);
1685 enum VisMark mark = vis_mark_used(vis);
1686 Array a = vis_mark_get(win, mark);
1687 Array b = view_selections_get_all(view);
1688 Array sel;
1689 array_init_from(&sel, &a);
1691 size_t i = 0, j = 0;
1692 Filerange *r1 = array_get(&a, i), *r2 = array_get(&b, j), cur = text_range_empty();
1693 while (r1 || r2) {
1694 if (r1 && text_range_overlap(r1, &cur)) {
1695 cur = text_range_union(r1, &cur);
1696 r1 = array_get(&a, ++i);
1697 } else if (r2 && text_range_overlap(r2, &cur)) {
1698 cur = text_range_union(r2, &cur);
1699 r2 = array_get(&b, ++j);
1700 } else {
1701 if (text_range_valid(&cur))
1702 array_add(&sel, &cur);
1703 if (!r1) {
1704 cur = *r2;
1705 r2 = array_get(&b, ++j);
1706 } else if (!r2) {
1707 cur = *r1;
1708 r1 = array_get(&a, ++i);
1709 } else {
1710 if (r1->start < r2->start) {
1711 cur = *r1;
1712 r1 = array_get(&a, ++i);
1713 } else {
1714 cur = *r2;
1715 r2 = array_get(&b, ++j);
1721 if (text_range_valid(&cur))
1722 array_add(&sel, &cur);
1724 selections_set(vis, view, &sel);
1725 vis_cancel(vis);
1727 array_release(&a);
1728 array_release(&b);
1729 array_release(&sel);
1731 return keys;
1734 static void intersect(Array *ret, Array *a, Array *b) {
1735 size_t i = 0, j = 0;
1736 Filerange *r1 = array_get(a, i), *r2 = array_get(b, j);
1737 while (r1 && r2) {
1738 if (text_range_overlap(r1, r2)) {
1739 Filerange new = text_range_intersect(r1, r2);
1740 array_add(ret, &new);
1742 if (r1->end < r2->end)
1743 r1 = array_get(a, ++i);
1744 else
1745 r2 = array_get(b, ++j);
1749 static const char *selections_intersect(Vis *vis, const char *keys, const Arg *arg) {
1750 Win *win = vis_window(vis);
1751 View *view = vis_view(vis);
1752 enum VisMark mark = vis_mark_used(vis);
1753 Array a = vis_mark_get(win, mark);
1754 Array b = view_selections_get_all(view);
1755 Array sel;
1756 array_init_from(&sel, &a);
1758 intersect(&sel, &a, &b);
1759 selections_set(vis, view, &sel);
1760 vis_cancel(vis);
1762 array_release(&a);
1763 array_release(&b);
1764 array_release(&sel);
1766 return keys;
1769 static void complement(Array *ret, Array *a, Filerange *universe) {
1770 size_t pos = universe->start;
1771 for (size_t i = 0, len = array_length(a); i < len; i++) {
1772 Filerange *r = array_get(a, i);
1773 if (pos < r->start) {
1774 Filerange new = text_range_new(pos, r->start);
1775 array_add(ret, &new);
1777 pos = r->end;
1779 if (pos < universe->end) {
1780 Filerange new = text_range_new(pos, universe->end);
1781 array_add(ret, &new);
1785 static const char *selections_complement(Vis *vis, const char *keys, const Arg *arg) {
1786 Text *txt = vis_text(vis);
1787 View *view = vis_view(vis);
1788 Filerange universe = text_object_entire(txt, 0);
1789 Array a = view_selections_get_all(view);
1790 Array sel;
1791 array_init_from(&sel, &a);
1793 complement(&sel, &a, &universe);
1795 selections_set(vis, view, &sel);
1796 array_release(&a);
1797 array_release(&sel);
1798 return keys;
1801 static const char *selections_minus(Vis *vis, const char *keys, const Arg *arg) {
1802 Text *txt = vis_text(vis);
1803 Win *win = vis_window(vis);
1804 View *view = vis_view(vis);
1805 enum VisMark mark = vis_mark_used(vis);
1806 Array a = view_selections_get_all(view);
1807 Array b = vis_mark_get(win, mark);
1808 Array sel;
1809 array_init_from(&sel, &a);
1810 Array b_complement;
1811 array_init_from(&b_complement, &b);
1813 Filerange universe = text_object_entire(txt, 0);
1814 complement(&b_complement, &b, &universe);
1815 intersect(&sel, &a, &b_complement);
1817 selections_set(vis, view, &sel);
1818 vis_cancel(vis);
1820 array_release(&a);
1821 array_release(&b);
1822 array_release(&b_complement);
1823 array_release(&sel);
1825 return keys;
1828 static Filerange combine_union(const Filerange *r1, const Filerange *r2) {
1829 if (!r1)
1830 return *r2;
1831 if (!r2)
1832 return *r1;
1833 return text_range_union(r1, r2);
1836 static Filerange combine_intersect(const Filerange *r1, const Filerange *r2) {
1837 if (!r1 || !r2)
1838 return text_range_empty();
1839 return text_range_intersect(r1, r2);
1842 static Filerange combine_longer(const Filerange *r1, const Filerange *r2) {
1843 if (!r1)
1844 return *r2;
1845 if (!r2)
1846 return *r1;
1847 size_t l1 = text_range_size(r1);
1848 size_t l2 = text_range_size(r2);
1849 return l1 < l2 ? *r2 : *r1;
1852 static Filerange combine_shorter(const Filerange *r1, const Filerange *r2) {
1853 if (!r1)
1854 return *r2;
1855 if (!r2)
1856 return *r1;
1857 size_t l1 = text_range_size(r1);
1858 size_t l2 = text_range_size(r2);
1859 return l1 < l2 ? *r1 : *r2;
1862 static Filerange combine_leftmost(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) ? *r1 : *r2;
1870 static Filerange combine_rightmost(const Filerange *r1, const Filerange *r2) {
1871 if (!r1)
1872 return *r2;
1873 if (!r2)
1874 return *r1;
1875 return r1->start < r2->start || (r1->start == r2->start && r1->end < r2->end) ? *r2 : *r1;
1878 static const char *selections_combine(Vis *vis, const char *keys, const Arg *arg) {
1879 Win *win = vis_window(vis);
1880 View *view = vis_view(vis);
1881 enum VisMark mark = vis_mark_used(vis);
1882 Array a = view_selections_get_all(view);
1883 Array b = vis_mark_get(win, mark);
1884 Array sel;
1885 array_init_from(&sel, &a);
1887 Filerange *r1 = array_get(&a, 0), *r2 = array_get(&b, 0);
1888 for (size_t i = 0, j = 0; r1 || r2; r1 = array_get(&a, ++i), r2 = array_get(&b, ++j)) {
1889 Filerange new = arg->combine(r1, r2);
1890 if (text_range_valid(&new))
1891 array_add(&sel, &new);
1894 vis_mark_normalize(&sel);
1895 selections_set(vis, view, &sel);
1896 vis_cancel(vis);
1898 array_release(&a);
1899 array_release(&b);
1900 array_release(&sel);
1902 return keys;
1905 static const char *replace(Vis *vis, const char *keys, const Arg *arg) {
1906 if (!keys[0]) {
1907 vis_keymap_disable(vis);
1908 return NULL;
1911 const char *next = vis_keys_next(vis, keys);
1912 if (!next)
1913 return NULL;
1915 char replacement[UTFmax+1];
1916 if (!vis_keys_utf8(vis, keys, replacement))
1917 return next;
1919 if (replacement[0] == 0x1b) /* <Escape> */
1920 return next;
1922 vis_operator(vis, VIS_OP_REPLACE, replacement);
1923 if (vis_mode_get(vis) == VIS_MODE_OPERATOR_PENDING)
1924 vis_motion(vis, VIS_MOVE_CHAR_NEXT);
1925 return next;
1928 static const char *count(Vis *vis, const char *keys, const Arg *arg) {
1929 int digit = keys[-1] - '0';
1930 int count = vis_count_get_default(vis, 0);
1931 if (0 <= digit && digit <= 9) {
1932 if (digit == 0 && count == 0)
1933 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
1934 else
1935 vis_count_set(vis, count * 10 + digit);
1937 return keys;
1940 static const char *gotoline(Vis *vis, const char *keys, const Arg *arg) {
1941 if (vis_count_get(vis) != VIS_COUNT_UNKNOWN)
1942 vis_motion(vis, VIS_MOVE_LINE);
1943 else if (arg->i < 0)
1944 vis_motion(vis, VIS_MOVE_FILE_BEGIN);
1945 else
1946 vis_motion(vis, VIS_MOVE_FILE_END);
1947 return keys;
1950 static const char *operator(Vis *vis, const char *keys, const Arg *arg) {
1951 vis_operator(vis, arg->i);
1952 return keys;
1955 static const char *movement_key(Vis *vis, const char *keys, const Arg *arg) {
1956 if (!keys[0]) {
1957 vis_keymap_disable(vis);
1958 return NULL;
1961 const char *next = vis_keys_next(vis, keys);
1962 if (!next)
1963 return NULL;
1964 char utf8[UTFmax+1];
1965 if (vis_keys_utf8(vis, keys, utf8))
1966 vis_motion(vis, arg->i, utf8);
1967 return next;
1970 static const char *movement(Vis *vis, const char *keys, const Arg *arg) {
1971 vis_motion(vis, arg->i);
1972 return keys;
1975 static const char *textobj(Vis *vis, const char *keys, const Arg *arg) {
1976 vis_textobject(vis, arg->i);
1977 return keys;
1980 static const char *selection_end(Vis *vis, const char *keys, const Arg *arg) {
1981 for (Selection *s = view_selections(vis_view(vis)); s; s = view_selections_next(s))
1982 view_selections_flip(s);
1983 return keys;
1986 static const char *reg(Vis *vis, const char *keys, const Arg *arg) {
1987 if (!keys[0])
1988 return NULL;
1989 const char *next = vis_keys_next(vis, keys);
1990 if (next - keys > 1)
1991 return next;
1992 enum VisRegister reg = vis_register_from(vis, keys[0]);
1993 vis_register(vis, reg);
1994 return keys+1;
1997 static const char *mark(Vis *vis, const char *keys, const Arg *arg) {
1998 if (!keys[0])
1999 return NULL;
2000 const char *next = vis_keys_next(vis, keys);
2001 if (next - keys > 1)
2002 return next;
2003 enum VisMark mark = vis_mark_from(vis, keys[0]);
2004 vis_mark(vis, mark);
2005 return keys+1;
2008 static const char *undo(Vis *vis, const char *keys, const Arg *arg) {
2009 size_t pos = text_undo(vis_text(vis));
2010 if (pos != EPOS) {
2011 View *view = vis_view(vis);
2012 if (view_selections_count(view) == 1)
2013 view_cursor_to(view, pos);
2014 /* redraw all windows in case some display the same file */
2015 vis_draw(vis);
2017 return keys;
2020 static const char *redo(Vis *vis, const char *keys, const Arg *arg) {
2021 size_t pos = text_redo(vis_text(vis));
2022 if (pos != EPOS) {
2023 View *view = vis_view(vis);
2024 if (view_selections_count(view) == 1)
2025 view_cursor_to(view, pos);
2026 /* redraw all windows in case some display the same file */
2027 vis_draw(vis);
2029 return keys;
2032 static const char *earlier(Vis *vis, const char *keys, const Arg *arg) {
2033 size_t pos = EPOS;
2034 VisCountIterator it = vis_count_iterator_get(vis, 1);
2035 while (vis_count_iterator_next(&it))
2036 pos = text_earlier(vis_text(vis));
2037 if (pos != EPOS) {
2038 view_cursor_to(vis_view(vis), pos);
2039 /* redraw all windows in case some display the same file */
2040 vis_draw(vis);
2042 return keys;
2045 static const char *later(Vis *vis, const char *keys, const Arg *arg) {
2046 size_t pos = EPOS;
2047 VisCountIterator it = vis_count_iterator_get(vis, 1);
2048 while (vis_count_iterator_next(&it))
2049 pos = text_later(vis_text(vis));
2050 if (pos != EPOS) {
2051 view_cursor_to(vis_view(vis), pos);
2052 /* redraw all windows in case some display the same file */
2053 vis_draw(vis);
2055 return keys;
2058 static const char *delete(Vis *vis, const char *keys, const Arg *arg) {
2059 vis_operator(vis, VIS_OP_DELETE);
2060 vis_motion(vis, arg->i);
2061 return keys;
2064 static const char *insert_register(Vis *vis, const char *keys, const Arg *arg) {
2065 if (!keys[0])
2066 return NULL;
2067 const char *next = vis_keys_next(vis, keys);
2068 if (next - keys > 1)
2069 return next;
2070 enum VisRegister reg = vis_register_from(vis, keys[0]);
2071 if (reg != VIS_REG_INVALID) {
2072 vis_register(vis, reg);
2073 vis_operator(vis, VIS_OP_PUT_BEFORE_END);
2075 return keys+1;
2078 static const char *prompt_show(Vis *vis, const char *keys, const Arg *arg) {
2079 vis_prompt_show(vis, arg->s);
2080 return keys;
2083 static const char *insert_verbatim(Vis *vis, const char *keys, const Arg *arg) {
2084 Rune rune = 0;
2085 char buf[4], type = keys[0];
2086 const char *data = NULL;
2087 int len = 0, count = 0, base = 0;
2088 switch (type) {
2089 case '\0':
2090 return NULL;
2091 case 'o':
2092 case 'O':
2093 count = 3;
2094 base = 8;
2095 break;
2096 case 'U':
2097 count = 4;
2098 /* fall through */
2099 case 'u':
2100 count += 4;
2101 base = 16;
2102 break;
2103 case 'x':
2104 case 'X':
2105 count = 2;
2106 base = 16;
2107 break;
2108 default:
2109 if ('0' <= type && type <= '9') {
2110 rune = type - '0';
2111 count = 2;
2112 base = 10;
2114 break;
2117 if (base) {
2118 for (keys++; keys[0] && count > 0; keys++, count--) {
2119 int v = 0;
2120 if (base == 8 && '0' <= keys[0] && keys[0] <= '7') {
2121 v = keys[0] - '0';
2122 } else if ((base == 10 || base == 16) && '0' <= keys[0] && keys[0] <= '9') {
2123 v = keys[0] - '0';
2124 } else if (base == 16 && 'a' <= keys[0] && keys[0] <= 'f') {
2125 v = 10 + keys[0] - 'a';
2126 } else if (base == 16 && 'A' <= keys[0] && keys[0] <= 'F') {
2127 v = 10 + keys[0] - 'A';
2128 } else {
2129 count = 0;
2130 break;
2132 rune = rune * base + v;
2135 if (count > 0)
2136 return NULL;
2137 if (type == 'u' || type == 'U') {
2138 len = runetochar(buf, &rune);
2139 } else {
2140 buf[0] = rune;
2141 len = 1;
2144 data = buf;
2145 } else {
2146 const char *next = vis_keys_next(vis, keys);
2147 if (!next)
2148 return NULL;
2149 if ((rune = vis_keys_codepoint(vis, keys)) != (Rune)-1) {
2150 len = runetochar(buf, &rune);
2151 if (buf[0] == '\n')
2152 buf[0] = '\r';
2153 data = buf;
2154 } else {
2155 vis_info_show(vis, "Unknown key");
2157 keys = next;
2160 if (len > 0)
2161 vis_insert_key(vis, data, len);
2162 return keys;
2165 static const char *wscroll(Vis *vis, const char *keys, const Arg *arg) {
2166 View *view = vis_view(vis);
2167 int count = vis_count_get(vis);
2168 switch (arg->i) {
2169 case -PAGE:
2170 view_scroll_page_up(view);
2171 break;
2172 case +PAGE:
2173 view_scroll_page_down(view);
2174 break;
2175 case -PAGE_HALF:
2176 view_scroll_halfpage_up(view);
2177 break;
2178 case +PAGE_HALF:
2179 view_scroll_halfpage_down(view);
2180 break;
2181 default:
2182 if (count == VIS_COUNT_UNKNOWN)
2183 count = arg->i < 0 ? -arg->i : arg->i;
2184 if (arg->i < 0)
2185 view_scroll_up(view, count);
2186 else
2187 view_scroll_down(view, count);
2188 break;
2190 vis_count_set(vis, VIS_COUNT_UNKNOWN);
2191 return keys;
2194 static const char *wslide(Vis *vis, const char *keys, const Arg *arg) {
2195 View *view = vis_view(vis);
2196 int count = vis_count_get(vis);
2197 if (count == VIS_COUNT_UNKNOWN)
2198 count = arg->i < 0 ? -arg->i : arg->i;
2199 if (arg->i >= 0)
2200 view_slide_down(view, count);
2201 else
2202 view_slide_up(view, count);
2203 vis_count_set(vis, VIS_COUNT_UNKNOWN);
2204 return keys;
2207 static const char *call(Vis *vis, const char *keys, const Arg *arg) {
2208 arg->f(vis);
2209 return keys;
2212 static const char *window(Vis *vis, const char *keys, const Arg *arg) {
2213 arg->w(vis_view(vis));
2214 return keys;
2217 static const char *openline(Vis *vis, const char *keys, const Arg *arg) {
2218 vis_operator(vis, VIS_OP_MODESWITCH, VIS_MODE_INSERT);
2219 if (arg->i > 0) {
2220 vis_motion(vis, VIS_MOVE_LINE_END);
2221 vis_keys_feed(vis, "<Enter>");
2222 } else {
2223 if (vis_get_autoindent(vis)) {
2224 vis_motion(vis, VIS_MOVE_LINE_START);
2225 vis_keys_feed(vis, "<vis-motion-line-start>");
2226 } else {
2227 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
2228 vis_keys_feed(vis, "<vis-motion-line-begin>");
2230 vis_keys_feed(vis, "<Enter><Up>");
2232 return keys;
2235 static const char *join(Vis *vis, const char *keys, const Arg *arg) {
2236 bool normal = (vis_mode_get(vis) == VIS_MODE_NORMAL);
2237 vis_operator(vis, VIS_OP_JOIN, arg->s);
2238 if (normal) {
2239 int count = vis_count_get_default(vis, 0);
2240 if (count)
2241 vis_count_set(vis, count-1);
2242 vis_motion(vis, VIS_MOVE_LINE_NEXT);
2244 return keys;
2247 static const char *normalmode_escape(Vis *vis, const char *keys, const Arg *arg) {
2248 if (vis_count_get(vis) == VIS_COUNT_UNKNOWN)
2249 selections_clear(vis, keys, arg);
2250 else
2251 vis_count_set(vis, VIS_COUNT_UNKNOWN);
2252 return keys;
2255 static const char *visualmode_escape(Vis *vis, const char *keys, const Arg *arg) {
2256 if (vis_count_get(vis) == VIS_COUNT_UNKNOWN)
2257 vis_mode_switch(vis, VIS_MODE_NORMAL);
2258 else
2259 vis_count_set(vis, VIS_COUNT_UNKNOWN);
2260 return keys;
2263 static const char *switchmode(Vis *vis, const char *keys, const Arg *arg) {
2264 vis_mode_switch(vis, arg->i);
2265 return keys;
2268 static const char *insertmode(Vis *vis, const char *keys, const Arg *arg) {
2269 vis_operator(vis, VIS_OP_MODESWITCH, VIS_MODE_INSERT);
2270 vis_motion(vis, arg->i);
2271 return keys;
2274 static const char *replacemode(Vis *vis, const char *keys, const Arg *arg) {
2275 vis_operator(vis, VIS_OP_MODESWITCH, VIS_MODE_REPLACE);
2276 vis_motion(vis, arg->i);
2277 return keys;
2280 static const char *unicode_info(Vis *vis, const char *keys, const Arg *arg) {
2281 View *view = vis_view(vis);
2282 Text *txt = vis_text(vis);
2283 size_t start = view_cursor_get(view);
2284 size_t end = text_char_next(txt, start);
2285 char *grapheme = text_bytes_alloc0(txt, start, end-start), *codepoint = grapheme;
2286 if (!grapheme)
2287 return keys;
2288 Buffer info;
2289 buffer_init(&info);
2290 mbstate_t ps = { 0 };
2291 Iterator it = text_iterator_get(txt, start);
2292 for (size_t pos = start; it.pos < end; pos = it.pos) {
2293 if (!text_iterator_codepoint_next(&it, NULL)) {
2294 vis_info_show(vis, "Failed to parse code point");
2295 goto err;
2297 size_t len = it.pos - pos;
2298 wchar_t wc = 0xFFFD;
2299 size_t res = mbrtowc(&wc, codepoint, len, &ps);
2300 bool combining = false;
2301 if (res != (size_t)-1 && res != (size_t)-2)
2302 combining = (wc != L'\0' && wcwidth(wc) == 0);
2303 unsigned char ch = *codepoint;
2304 if (ch < 128 && !isprint(ch))
2305 buffer_appendf(&info, "<^%c> ", ch == 127 ? '?' : ch + 64);
2306 else
2307 buffer_appendf(&info, "<%s%.*s> ", combining ? " " : "", (int)len, codepoint);
2308 if (arg->i == VIS_ACTION_UNICODE_INFO) {
2309 buffer_appendf(&info, "U+%04"PRIX32" ", (uint32_t)wc);
2310 } else {
2311 for (size_t i = 0; i < len; i++)
2312 buffer_appendf(&info, "%02x ", (uint8_t)codepoint[i]);
2314 codepoint += len;
2316 vis_info_show(vis, "%s", buffer_content0(&info));
2317 err:
2318 free(grapheme);
2319 buffer_release(&info);
2320 return keys;
2323 static const char *percent(Vis *vis, const char *keys, const Arg *arg) {
2324 if (vis_count_get(vis) == VIS_COUNT_UNKNOWN)
2325 vis_motion(vis, VIS_MOVE_BRACKET_MATCH);
2326 else
2327 vis_motion(vis, VIS_MOVE_PERCENT);
2328 return keys;
2331 static const char *jumplist(Vis *vis, const char *keys, const Arg *arg) {
2332 if (arg->i < 0)
2333 vis_jumplist_prev(vis);
2334 else if (arg->i > 0)
2335 vis_jumplist_next(vis);
2336 else
2337 vis_jumplist_save(vis);
2338 return keys;
2341 static Vis *vis;
2343 static void signal_handler(int signum, siginfo_t *siginfo, void *context) {
2344 vis_signal_handler(vis, signum, siginfo, context);
2347 int main(int argc, char *argv[]) {
2349 VisEvent event = {
2350 .init = vis_lua_init,
2351 .start = vis_lua_start,
2352 .quit = vis_lua_quit,
2353 .mode_insert_input = vis_lua_mode_insert_input,
2354 .mode_replace_input = vis_lua_mode_replace_input,
2355 .file_open = vis_lua_file_open,
2356 .file_save_pre = vis_lua_file_save_pre,
2357 .file_save_post = vis_lua_file_save_post,
2358 .file_close = vis_lua_file_close,
2359 .win_open = vis_lua_win_open,
2360 .win_close = vis_lua_win_close,
2361 .win_highlight = vis_lua_win_highlight,
2362 .win_status = vis_lua_win_status,
2365 vis = vis_new(ui_term_new(), &event);
2366 if (!vis)
2367 return EXIT_FAILURE;
2369 for (int i = 0; i < LENGTH(vis_action); i++) {
2370 const KeyAction *action = &vis_action[i];
2371 if (!vis_action_register(vis, action))
2372 vis_die(vis, "Could not register action: %s\n", action->name);
2375 for (int i = 0; i < LENGTH(default_bindings); i++) {
2376 for (const KeyBinding **binding = default_bindings[i]; binding && *binding; binding++) {
2377 for (const KeyBinding *kb = *binding; kb->key; kb++) {
2378 vis_mode_map(vis, i, false, kb->key, kb);
2383 for (const char **k = keymaps; k[0]; k += 2)
2384 vis_keymap_add(vis, k[0], k[1]);
2386 /* install signal handlers etc. */
2387 struct sigaction sa;
2388 memset(&sa, 0, sizeof sa);
2389 sigfillset(&sa.sa_mask);
2390 sa.sa_flags = SA_SIGINFO;
2391 sa.sa_sigaction = signal_handler;
2392 if (sigaction(SIGBUS, &sa, NULL) == -1 ||
2393 sigaction(SIGINT, &sa, NULL) == -1 ||
2394 sigaction(SIGCONT, &sa, NULL) == -1 ||
2395 sigaction(SIGWINCH, &sa, NULL) == -1 ||
2396 sigaction(SIGTERM, &sa, NULL) == -1 ||
2397 sigaction(SIGHUP, &sa, NULL) == -1) {
2398 vis_die(vis, "Failed to set signal handler: %s\n", strerror(errno));
2401 sa.sa_handler = SIG_IGN;
2402 if (sigaction(SIGPIPE, &sa, NULL) == -1 || sigaction(SIGQUIT, &sa, NULL) == -1)
2403 vis_die(vis, "Failed to ignore signals\n");
2405 sigset_t blockset;
2406 sigemptyset(&blockset);
2407 sigaddset(&blockset, SIGBUS);
2408 sigaddset(&blockset, SIGCONT);
2409 sigaddset(&blockset, SIGWINCH);
2410 sigaddset(&blockset, SIGTERM);
2411 sigaddset(&blockset, SIGHUP);
2412 if (sigprocmask(SIG_BLOCK, &blockset, NULL) == -1)
2413 vis_die(vis, "Failed to block signals\n");
2415 for (int i = 1; i < argc; i++) {
2416 if (argv[i][0] != '-') {
2417 continue;
2418 } else if (strcmp(argv[i], "-") == 0) {
2419 continue;
2420 } else if (strcmp(argv[i], "--") == 0) {
2421 break;
2422 } else if (strcmp(argv[i], "-v") == 0) {
2423 printf("vis %s%s%s%s%s%s%s\n", VERSION,
2424 CONFIG_CURSES ? " +curses" : "",
2425 CONFIG_LUA ? " +lua" : "",
2426 CONFIG_LPEG ? " +lpeg" : "",
2427 CONFIG_TRE ? " +tre" : "",
2428 CONFIG_ACL ? " +acl" : "",
2429 CONFIG_SELINUX ? " +selinux" : "");
2430 return 0;
2431 } else {
2432 fprintf(stderr, "Unknown command option: %s\n", argv[i]);
2433 return 1;
2437 char *cmd = NULL;
2438 bool end_of_options = false, win_created = false;
2440 for (int i = 1; i < argc; i++) {
2441 if (argv[i][0] == '-' && !end_of_options) {
2442 if (strcmp(argv[i], "-") == 0) {
2443 if (!vis_window_new_fd(vis, STDOUT_FILENO))
2444 vis_die(vis, "Can not create empty buffer\n");
2445 ssize_t len = 0;
2446 char buf[PIPE_BUF];
2447 Text *txt = vis_text(vis);
2448 while ((len = read(STDIN_FILENO, buf, sizeof buf)) > 0)
2449 text_insert(txt, text_size(txt), buf, len);
2450 if (len == -1)
2451 vis_die(vis, "Can not read from stdin\n");
2452 text_snapshot(txt);
2453 int fd = open("/dev/tty", O_RDWR);
2454 if (fd == -1)
2455 vis_die(vis, "Can not reopen stdin\n");
2456 dup2(fd, STDIN_FILENO);
2457 close(fd);
2458 } else if (strcmp(argv[i], "--") == 0) {
2459 end_of_options = true;
2460 continue;
2462 } else if (argv[i][0] == '+' && !end_of_options) {
2463 cmd = argv[i] + (argv[i][1] == '/' || argv[i][1] == '?');
2464 continue;
2465 } else if (!vis_window_new(vis, argv[i])) {
2466 vis_die(vis, "Can not load `%s': %s\n", argv[i], strerror(errno));
2468 win_created = true;
2469 if (cmd) {
2470 vis_prompt_cmd(vis, cmd);
2471 cmd = NULL;
2475 if (!vis_window(vis) && !win_created) {
2476 if (!vis_window_new(vis, NULL))
2477 vis_die(vis, "Can not create empty buffer\n");
2478 if (cmd)
2479 vis_prompt_cmd(vis, cmd);
2482 int status = vis_run(vis);
2483 vis_free(vis);
2484 return status;