vis: implement :set cursorline
[vis.git] / main.c
blobfc3877aec46053bd5c15ea3e92be8b5832ce94c4
1 #include <signal.h>
2 #include <limits.h>
3 #include <string.h>
4 #include <errno.h>
6 #include "ui-curses.h"
7 #include "vis.h"
8 #include "text-util.h"
9 #include "text-motions.h"
10 #include "text-objects.h"
11 #include "util.h"
12 #include "libutf.h"
14 #define PAGE INT_MAX
15 #define PAGE_HALF (INT_MAX-1)
17 /** functions to be called from keybindings */
18 /* ignore key, do nothing */
19 static const char *nop(Vis*, const char *keys, const Arg *arg);
20 /* record/replay macro indicated by keys */
21 static const char *macro_record(Vis*, const char *keys, const Arg *arg);
22 static const char *macro_replay(Vis*, const char *keys, const Arg *arg);
23 /* temporarily suspend the editor and return to the shell, type 'fg' to get back */
24 static const char *suspend(Vis*, const char *keys, const Arg *arg);
25 /* switch to mode indicated by arg->i */
26 static const char *switchmode(Vis*, const char *keys, const Arg *arg);
27 /* switch to insert mode after performing movement indicated by arg->i */
28 static const char *insertmode(Vis*, const char *keys, const Arg *arg);
29 /* set mark indicated by keys to current cursor position */
30 static const char *mark_set(Vis*, const char *keys, const Arg *arg);
31 /* add a new line either before or after the one where the cursor currently is */
32 static const char *openline(Vis*, const char *keys, const Arg *arg);
33 /* join lines from current cursor position to movement indicated by arg */
34 static const char *join(Vis*, const char *keys, const Arg *arg);
35 /* execute arg->s as if it was typed on command prompt */
36 static const char *cmd(Vis*, const char *keys, const Arg *arg);
37 /* perform last action i.e. action_prev again */
38 static const char *repeat(Vis*, const char *keys, const Arg *arg);
39 /* replace character at cursor with one from keys */
40 static const char *replace(Vis*, const char *keys, const Arg *arg);
41 /* create a new cursor on the previous (arg->i < 0) or next (arg->i > 0) line */
42 static const char *cursors_new(Vis*, const char *keys, const Arg *arg);
43 /* try to align all cursors on the same column */
44 static const char *cursors_align(Vis*, const char *keys, const Arg *arg);
45 /* remove all but the primary cursor and their selections */
46 static const char *cursors_clear(Vis*, const char *keys, const Arg *arg);
47 /* remove the least recently added cursor */
48 static const char *cursors_remove(Vis*, const char *keys, const Arg *arg);
49 /* select the word the cursor is currently over */
50 static const char *cursors_select(Vis*, const char *keys, const Arg *arg);
51 /* select the next region matching the current selection */
52 static const char *cursors_select_next(Vis*, const char *keys, const Arg *arg);
53 /* clear current selection but select next match */
54 static const char *cursors_select_skip(Vis*, const char *keys, const Arg *arg);
55 /* adjust current used count according to keys */
56 static const char *count(Vis*, const char *keys, const Arg *arg);
57 /* move to the count-th line or if not given either to the first (arg->i < 0)
58 * or last (arg->i > 0) line of file */
59 static const char *gotoline(Vis*, const char *keys, const Arg *arg);
60 /* set motion type either LINEWISE or CHARWISE via arg->i */
61 static const char *motiontype(Vis*, const char *keys, const Arg *arg);
62 /* make the current action use the operator indicated by arg->i */
63 static const char *operator(Vis*, const char *keys, const Arg *arg);
64 /* blocks to read a key and performs movement indicated by arg->i which
65 * should be one of VIS_MOVE_{RIGHT,LEFT}_{TO,TILL} */
66 static const char *movement_key(Vis*, const char *keys, const Arg *arg);
67 /* perform the movement as indicated by arg->i */
68 static const char *movement(Vis*, const char *keys, const Arg *arg);
69 /* let the current operator affect the range indicated by the text object arg->i */
70 static const char *textobj(Vis*, const char *keys, const Arg *arg);
71 /* move to the other end of selected text */
72 static const char *selection_end(Vis*, const char *keys, const Arg *arg);
73 /* restore least recently used selection */
74 static const char *selection_restore(Vis*, const char *keys, const Arg *arg);
75 /* use register indicated by keys for the current operator */
76 static const char *reg(Vis*, const char *keys, const Arg *arg);
77 /* perform arg->i motion with a mark indicated by keys as argument */
78 static const char *mark_motion(Vis*, const char *keys, const Arg *arg);
79 /* {un,re}do last action, redraw window */
80 static const char *undo(Vis*, const char *keys, const Arg *arg);
81 static const char *redo(Vis*, const char *keys, const Arg *arg);
82 /* earlier, later action chronologically, redraw window */
83 static const char *earlier(Vis*, const char *keys, const Arg *arg);
84 static const char *later(Vis*, const char *keys, const Arg *arg);
85 /* delete from the current cursor position to the end of
86 * movement as indicated by arg->i */
87 static const char *delete(Vis*, const char *keys, const Arg *arg);
88 /* insert register content indicated by keys at current cursor position */
89 static const char *insert_register(Vis*, const char *keys, const Arg *arg);
90 /* show a user prompt to get input with title arg->s */
91 static const char *prompt_search(Vis*, const char *keys, const Arg *arg);
92 static const char *prompt_cmd(Vis*, const char *keys, const Arg *arg);
93 /* exit command mode if the last char is deleted */
94 static const char *prompt_backspace(Vis*, const char *keys, const Arg *arg);
95 /* blocks to read 3 consecutive digits and inserts the corresponding byte value */
96 static const char *insert_verbatim(Vis*, const char *keys, const Arg *arg);
97 /* scroll window content according to arg->i which can be either PAGE, PAGE_HALF,
98 * or an arbitrary number of lines. a multiplier overrides what is given in arg->i.
99 * negative values scroll back, positive forward. */
100 static const char *wscroll(Vis*, const char *keys, const Arg *arg);
101 /* similar to scroll, but do only move window content not cursor position */
102 static const char *wslide(Vis*, const char *keys, const Arg *arg);
103 /* call editor function as indicated by arg->f */
104 static const char *call(Vis*, const char *keys, const Arg *arg);
105 /* call window function as indicated by arg->w */
106 static const char *window(Vis*, const char *keys, const Arg *arg);
108 enum {
109 VIS_ACTION_EDITOR_SUSPEND,
110 VIS_ACTION_CURSOR_CHAR_PREV,
111 VIS_ACTION_CURSOR_CHAR_NEXT,
112 VIS_ACTION_CURSOR_WORD_START_PREV,
113 VIS_ACTION_CURSOR_WORD_START_NEXT,
114 VIS_ACTION_CURSOR_WORD_END_PREV,
115 VIS_ACTION_CURSOR_WORD_END_NEXT,
116 VIS_ACTION_CURSOR_LONGWORD_START_PREV,
117 VIS_ACTION_CURSOR_LONGWORD_START_NEXT,
118 VIS_ACTION_CURSOR_LONGWORD_END_PREV,
119 VIS_ACTION_CURSOR_LONGWORD_END_NEXT,
120 VIS_ACTION_CURSOR_LINE_UP,
121 VIS_ACTION_CURSOR_LINE_DOWN,
122 VIS_ACTION_CURSOR_LINE_START,
123 VIS_ACTION_CURSOR_LINE_FINISH,
124 VIS_ACTION_CURSOR_LINE_BEGIN,
125 VIS_ACTION_CURSOR_LINE_END,
126 VIS_ACTION_CURSOR_SCREEN_LINE_UP,
127 VIS_ACTION_CURSOR_SCREEN_LINE_DOWN,
128 VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN,
129 VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE,
130 VIS_ACTION_CURSOR_SCREEN_LINE_END,
131 VIS_ACTION_CURSOR_BRACKET_MATCH,
132 VIS_ACTION_CURSOR_PARAGRAPH_PREV,
133 VIS_ACTION_CURSOR_PARAGRAPH_NEXT,
134 VIS_ACTION_CURSOR_SENTENCE_PREV,
135 VIS_ACTION_CURSOR_SENTENCE_NEXT,
136 VIS_ACTION_CURSOR_FUNCTION_START_PREV,
137 VIS_ACTION_CURSOR_FUNCTION_END_PREV,
138 VIS_ACTION_CURSOR_FUNCTION_START_NEXT,
139 VIS_ACTION_CURSOR_FUNCTION_END_NEXT,
140 VIS_ACTION_CURSOR_COLUMN,
141 VIS_ACTION_CURSOR_LINE_FIRST,
142 VIS_ACTION_CURSOR_LINE_LAST,
143 VIS_ACTION_CURSOR_WINDOW_LINE_TOP,
144 VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE,
145 VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM,
146 VIS_ACTION_CURSOR_SEARCH_NEXT,
147 VIS_ACTION_CURSOR_SEARCH_PREV,
148 VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD,
149 VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD,
150 VIS_ACTION_WINDOW_PAGE_UP,
151 VIS_ACTION_WINDOW_PAGE_DOWN,
152 VIS_ACTION_WINDOW_HALFPAGE_UP,
153 VIS_ACTION_WINDOW_HALFPAGE_DOWN,
154 VIS_ACTION_MODE_NORMAL,
155 VIS_ACTION_MODE_VISUAL,
156 VIS_ACTION_MODE_VISUAL_LINE,
157 VIS_ACTION_MODE_INSERT,
158 VIS_ACTION_MODE_REPLACE,
159 VIS_ACTION_MODE_OPERATOR_PENDING,
160 VIS_ACTION_DELETE_CHAR_PREV,
161 VIS_ACTION_DELETE_CHAR_NEXT,
162 VIS_ACTION_DELETE_LINE_BEGIN,
163 VIS_ACTION_DELETE_WORD_PREV,
164 VIS_ACTION_JUMPLIST_PREV,
165 VIS_ACTION_JUMPLIST_NEXT,
166 VIS_ACTION_CHANGELIST_PREV,
167 VIS_ACTION_CHANGELIST_NEXT,
168 VIS_ACTION_UNDO,
169 VIS_ACTION_REDO,
170 VIS_ACTION_EARLIER,
171 VIS_ACTION_LATER,
172 VIS_ACTION_MACRO_RECORD,
173 VIS_ACTION_MACRO_REPLAY,
174 VIS_ACTION_MARK_SET,
175 VIS_ACTION_MARK_GOTO,
176 VIS_ACTION_MARK_GOTO_LINE,
177 VIS_ACTION_REDRAW,
178 VIS_ACTION_REPLACE_CHAR,
179 VIS_ACTION_TOTILL_REPEAT,
180 VIS_ACTION_TOTILL_REVERSE,
181 VIS_ACTION_PROMPT_SEARCH_FORWARD,
182 VIS_ACTION_PROMPT_SEARCH_BACKWARD,
183 VIS_ACTION_TILL_LEFT,
184 VIS_ACTION_TILL_RIGHT,
185 VIS_ACTION_TO_LEFT,
186 VIS_ACTION_TO_RIGHT,
187 VIS_ACTION_REGISTER,
188 VIS_ACTION_OPERATOR_CHANGE,
189 VIS_ACTION_OPERATOR_DELETE,
190 VIS_ACTION_OPERATOR_YANK,
191 VIS_ACTION_OPERATOR_SHIFT_LEFT,
192 VIS_ACTION_OPERATOR_SHIFT_RIGHT,
193 VIS_ACTION_OPERATOR_CASE_LOWER,
194 VIS_ACTION_OPERATOR_CASE_UPPER,
195 VIS_ACTION_OPERATOR_CASE_SWAP,
196 VIS_ACTION_COUNT,
197 VIS_ACTION_INSERT_NEWLINE,
198 VIS_ACTION_INSERT_TAB,
199 VIS_ACTION_INSERT_VERBATIM,
200 VIS_ACTION_INSERT_REGISTER,
201 VIS_ACTION_WINDOW_NEXT,
202 VIS_ACTION_WINDOW_PREV,
203 VIS_ACTION_APPEND_CHAR_NEXT,
204 VIS_ACTION_APPEND_LINE_END,
205 VIS_ACTION_INSERT_LINE_START,
206 VIS_ACTION_OPEN_LINE_ABOVE,
207 VIS_ACTION_OPEN_LINE_BELOW,
208 VIS_ACTION_JOIN_LINE_BELOW,
209 VIS_ACTION_JOIN_LINES,
210 VIS_ACTION_PROMPT_SHOW,
211 VIS_ACTION_PROMPT_BACKSPACE,
212 VIS_ACTION_PROMPT_ENTER,
213 VIS_ACTION_PROMPT_SHOW_VISUAL,
214 VIS_ACTION_REPEAT,
215 VIS_ACTION_SELECTION_FLIP,
216 VIS_ACTION_SELECTION_RESTORE,
217 VIS_ACTION_WINDOW_REDRAW_TOP,
218 VIS_ACTION_WINDOW_REDRAW_CENTER,
219 VIS_ACTION_WINDOW_REDRAW_BOTTOM,
220 VIS_ACTION_WINDOW_SLIDE_UP,
221 VIS_ACTION_WINDOW_SLIDE_DOWN,
222 VIS_ACTION_PUT_AFTER,
223 VIS_ACTION_PUT_BEFORE,
224 VIS_ACTION_PUT_AFTER_END,
225 VIS_ACTION_PUT_BEFORE_END,
226 VIS_ACTION_CURSOR_SELECT_WORD,
227 VIS_ACTION_CURSORS_NEW_LINE_ABOVE,
228 VIS_ACTION_CURSORS_NEW_LINE_BELOW,
229 VIS_ACTION_CURSORS_NEW_LINES_BEGIN,
230 VIS_ACTION_CURSORS_NEW_LINES_END,
231 VIS_ACTION_CURSORS_NEW_MATCH_NEXT,
232 VIS_ACTION_CURSORS_NEW_MATCH_SKIP,
233 VIS_ACTION_CURSORS_ALIGN,
234 VIS_ACTION_CURSORS_REMOVE_ALL,
235 VIS_ACTION_CURSORS_REMOVE_LAST,
236 VIS_ACTION_TEXT_OBJECT_WORD_OUTER,
237 VIS_ACTION_TEXT_OBJECT_WORD_INNER,
238 VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER,
239 VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER,
240 VIS_ACTION_TEXT_OBJECT_SENTENCE,
241 VIS_ACTION_TEXT_OBJECT_PARAGRAPH,
242 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER,
243 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER,
244 VIS_ACTION_TEXT_OBJECT_PARANTHESE_OUTER,
245 VIS_ACTION_TEXT_OBJECT_PARANTHESE_INNER,
246 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER,
247 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER,
248 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER,
249 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER,
250 VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER,
251 VIS_ACTION_TEXT_OBJECT_QUOTE_INNER,
252 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER,
253 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER,
254 VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER,
255 VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER,
256 VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER,
257 VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER,
258 VIS_ACTION_TEXT_OBJECT_FUNCTION_OUTER,
259 VIS_ACTION_TEXT_OBJECT_FUNCTION_INNER,
260 VIS_ACTION_TEXT_OBJECT_LINE_OUTER,
261 VIS_ACTION_TEXT_OBJECT_LINE_INNER,
262 VIS_ACTION_MOTION_CHARWISE,
263 VIS_ACTION_MOTION_LINEWISE,
264 VIS_ACTION_NOP,
267 static KeyAction vis_action[] = {
268 [VIS_ACTION_EDITOR_SUSPEND] = {
269 "editor-suspend",
270 "Suspend the editor",
271 suspend,
273 [VIS_ACTION_CURSOR_CHAR_PREV] = {
274 "cursor-char-prev",
275 "Move cursor left, to the previous character",
276 movement, { .i = VIS_MOVE_CHAR_PREV }
278 [VIS_ACTION_CURSOR_CHAR_NEXT] = {
279 "cursor-char-next",
280 "Move cursor right, to the next character",
281 movement, { .i = VIS_MOVE_CHAR_NEXT }
283 [VIS_ACTION_CURSOR_WORD_START_PREV] = {
284 "cursor-word-start-prev",
285 "Move cursor words backwards",
286 movement, { .i = VIS_MOVE_WORD_START_PREV }
288 [VIS_ACTION_CURSOR_WORD_START_NEXT] = {
289 "cursor-word-start-next",
290 "Move cursor words forwards",
291 movement, { .i = VIS_MOVE_WORD_START_NEXT }
293 [VIS_ACTION_CURSOR_WORD_END_PREV] = {
294 "cursor-word-end-prev",
295 "Move cursor backwards to the end of word",
296 movement, { .i = VIS_MOVE_WORD_END_PREV }
298 [VIS_ACTION_CURSOR_WORD_END_NEXT] = {
299 "cursor-word-end-next",
300 "Move cursor forward to the end of word",
301 movement, { .i = VIS_MOVE_WORD_END_NEXT }
303 [VIS_ACTION_CURSOR_LONGWORD_START_PREV] = {
304 "cursor-longword-start-prev",
305 "Move cursor WORDS backwards",
306 movement, { .i = VIS_MOVE_LONGWORD_START_PREV }
308 [VIS_ACTION_CURSOR_LONGWORD_START_NEXT] = {
309 "cursor-longword-start-next",
310 "Move cursor WORDS forwards",
311 movement, { .i = VIS_MOVE_LONGWORD_START_NEXT }
313 [VIS_ACTION_CURSOR_LONGWORD_END_PREV] = {
314 "cursor-longword-end-prev",
315 "Move cursor backwards to the end of WORD",
316 movement, { .i = VIS_MOVE_LONGWORD_END_PREV }
318 [VIS_ACTION_CURSOR_LONGWORD_END_NEXT] = {
319 "cursor-longword-end-next",
320 "Move cursor forward to the end of WORD",
321 movement, { .i = VIS_MOVE_LONGWORD_END_NEXT }
323 [VIS_ACTION_CURSOR_LINE_UP] = {
324 "cursor-line-up",
325 "Move cursor line upwards",
326 movement, { .i = VIS_MOVE_LINE_UP }
328 [VIS_ACTION_CURSOR_LINE_DOWN] = {
329 "cursor-line-down",
330 "Move cursor line downwards",
331 movement, { .i = VIS_MOVE_LINE_DOWN }
333 [VIS_ACTION_CURSOR_LINE_START] = {
334 "cursor-line-start",
335 "Move cursor to first non-blank character of the line",
336 movement, { .i = VIS_MOVE_LINE_START }
338 [VIS_ACTION_CURSOR_LINE_FINISH] = {
339 "cursor-line-finish",
340 "Move cursor to last non-blank character of the line",
341 movement, { .i = VIS_MOVE_LINE_FINISH }
343 [VIS_ACTION_CURSOR_LINE_BEGIN] = {
344 "cursor-line-begin",
345 "Move cursor to first character of the line",
346 movement, { .i = VIS_MOVE_LINE_BEGIN }
348 [VIS_ACTION_CURSOR_LINE_END] = {
349 "cursor-line-end",
350 "Move cursor to end of the line",
351 movement, { .i = VIS_MOVE_LINE_LASTCHAR }
353 [VIS_ACTION_CURSOR_SCREEN_LINE_UP] = {
354 "cursor-sceenline-up",
355 "Move cursor screen/display line upwards",
356 movement, { .i = VIS_MOVE_SCREEN_LINE_UP }
358 [VIS_ACTION_CURSOR_SCREEN_LINE_DOWN] = {
359 "cursor-screenline-down",
360 "Move cursor screen/display line downwards",
361 movement, { .i = VIS_MOVE_SCREEN_LINE_DOWN }
363 [VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN] = {
364 "cursor-screenline-begin",
365 "Move cursor to beginning of screen/display line",
366 movement, { .i = VIS_MOVE_SCREEN_LINE_BEGIN }
368 [VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE] = {
369 "cursor-screenline-middle",
370 "Move cursor to middle of screen/display line",
371 movement, { .i = VIS_MOVE_SCREEN_LINE_MIDDLE }
373 [VIS_ACTION_CURSOR_SCREEN_LINE_END] = {
374 "cursor-screenline-end",
375 "Move cursor to end of screen/display line",
376 movement, { .i = VIS_MOVE_SCREEN_LINE_END }
378 [VIS_ACTION_CURSOR_BRACKET_MATCH] = {
379 "cursor-match-bracket",
380 "Match corresponding symbol if cursor is on a bracket character",
381 movement, { .i = VIS_MOVE_BRACKET_MATCH }
383 [VIS_ACTION_CURSOR_PARAGRAPH_PREV] = {
384 "cursor-paragraph-prev",
385 "Move cursor paragraph backward",
386 movement, { .i = VIS_MOVE_PARAGRAPH_PREV }
388 [VIS_ACTION_CURSOR_PARAGRAPH_NEXT] = {
389 "cursor-paragraph-next",
390 "Move cursor paragraph forward",
391 movement, { .i = VIS_MOVE_PARAGRAPH_NEXT }
393 [VIS_ACTION_CURSOR_SENTENCE_PREV] = {
394 "cursor-sentence-prev",
395 "Move cursor sentence backward",
396 movement, { .i = VIS_MOVE_SENTENCE_PREV }
398 [VIS_ACTION_CURSOR_SENTENCE_NEXT] = {
399 "cursor-sentence-next",
400 "Move cursor sentence forward",
401 movement, { .i = VIS_MOVE_SENTENCE_NEXT }
403 [VIS_ACTION_CURSOR_FUNCTION_START_PREV] = {
404 "cursor-function-start-prev",
405 "Move cursor backwards to start of function",
406 movement, { .i = VIS_MOVE_FUNCTION_START_PREV }
408 [VIS_ACTION_CURSOR_FUNCTION_START_NEXT] = {
409 "cursor-function-start-next",
410 "Move cursor forwards to start of function",
411 movement, { .i = VIS_MOVE_FUNCTION_START_NEXT }
413 [VIS_ACTION_CURSOR_FUNCTION_END_PREV] = {
414 "cursor-function-end-prev",
415 "Move cursor backwards to end of function",
416 movement, { .i = VIS_MOVE_FUNCTION_END_PREV }
418 [VIS_ACTION_CURSOR_FUNCTION_END_NEXT] = {
419 "cursor-function-end-next",
420 "Move cursor forwards to end of function",
421 movement, { .i = VIS_MOVE_FUNCTION_END_NEXT }
423 [VIS_ACTION_CURSOR_COLUMN] = {
424 "cursor-column",
425 "Move cursor to given column of current line",
426 movement, { .i = VIS_MOVE_COLUMN }
428 [VIS_ACTION_CURSOR_LINE_FIRST] = {
429 "cursor-line-first",
430 "Move cursor to given line (defaults to first)",
431 gotoline, { .i = -1 }
433 [VIS_ACTION_CURSOR_LINE_LAST] = {
434 "cursor-line-last",
435 "Move cursor to given line (defaults to last)",
436 gotoline, { .i = +1 }
438 [VIS_ACTION_CURSOR_WINDOW_LINE_TOP] = {
439 "cursor-window-line-top",
440 "Move cursor to top line of the window",
441 movement, { .i = VIS_MOVE_WINDOW_LINE_TOP }
443 [VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE] = {
444 "cursor-window-line-middle",
445 "Move cursor to middle line of the window",
446 movement, { .i = VIS_MOVE_WINDOW_LINE_MIDDLE }
448 [VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM] = {
449 "cursor-window-line-bottom",
450 "Move cursor to bottom line of the window",
451 movement, { .i = VIS_MOVE_WINDOW_LINE_BOTTOM }
453 [VIS_ACTION_CURSOR_SEARCH_NEXT] = {
454 "cursor-search-forward",
455 "Move cursor to bottom line of the window",
456 movement, { .i = VIS_MOVE_SEARCH_NEXT }
458 [VIS_ACTION_CURSOR_SEARCH_PREV] = {
459 "cursor-search-backward",
460 "Move cursor to bottom line of the window",
461 movement, { .i = VIS_MOVE_SEARCH_PREV }
463 [VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD] = {
464 "cursor-search-word-forward",
465 "Move cursor to next occurence of the word under cursor",
466 movement, { .i = VIS_MOVE_SEARCH_WORD_FORWARD }
468 [VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD] = {
469 "cursor-search-word-backward",
470 "Move cursor to previous occurence of the word under cursor",
471 movement, { .i = VIS_MOVE_SEARCH_WORD_BACKWARD }
473 [VIS_ACTION_WINDOW_PAGE_UP] = {
474 "window-page-up",
475 "Scroll window pages backwards (upwards)",
476 wscroll, { .i = -PAGE }
478 [VIS_ACTION_WINDOW_HALFPAGE_UP] = {
479 "window-halfpage-up",
480 "Scroll window half pages backwards (upwards)",
481 wscroll, { .i = -PAGE_HALF }
483 [VIS_ACTION_WINDOW_PAGE_DOWN] = {
484 "window-page-down",
485 "Scroll window pages forwards (downwards)",
486 wscroll, { .i = +PAGE }
488 [VIS_ACTION_WINDOW_HALFPAGE_DOWN] = {
489 "window-halfpage-down",
490 "Scroll window half pages forwards (downwards)",
491 wscroll, { .i = +PAGE_HALF }
493 [VIS_ACTION_MODE_NORMAL] = {
494 "vis-mode-normal",
495 "Enter normal mode",
496 switchmode, { .i = VIS_MODE_NORMAL }
498 [VIS_ACTION_MODE_VISUAL] = {
499 "vis-mode-visual-charwise",
500 "Enter characterwise visual mode",
501 switchmode, { .i = VIS_MODE_VISUAL }
503 [VIS_ACTION_MODE_VISUAL_LINE] = {
504 "vis-mode-visual-linewise",
505 "Enter linewise visual mode",
506 switchmode, { .i = VIS_MODE_VISUAL_LINE }
508 [VIS_ACTION_MODE_INSERT] = {
509 "vis-mode-insert",
510 "Enter insert mode",
511 switchmode, { .i = VIS_MODE_INSERT }
513 [VIS_ACTION_MODE_REPLACE] = {
514 "vis-mode-replace",
515 "Enter replace mode",
516 switchmode, { .i = VIS_MODE_REPLACE }
518 [VIS_ACTION_MODE_OPERATOR_PENDING] = {
519 "vis-mode-operator-pending",
520 "Enter to operator pending mode",
521 switchmode, { .i = VIS_MODE_OPERATOR }
523 [VIS_ACTION_DELETE_CHAR_PREV] = {
524 "delete-char-prev",
525 "Delete the previous character",
526 delete, { .i = VIS_MOVE_CHAR_PREV }
528 [VIS_ACTION_DELETE_CHAR_NEXT] = {
529 "delete-char-next",
530 "Delete the next character",
531 delete, { .i = VIS_MOVE_CHAR_NEXT }
533 [VIS_ACTION_DELETE_LINE_BEGIN] = {
534 "delete-line-begin",
535 "Delete until the start of the current line",
536 delete, { .i = VIS_MOVE_LINE_BEGIN }
538 [VIS_ACTION_DELETE_WORD_PREV] = {
539 "delete-word-prev",
540 "Delete the previous WORD",
541 delete, { .i = VIS_MOVE_LONGWORD_START_PREV }
543 [VIS_ACTION_JUMPLIST_PREV] = {
544 "jumplist-prev",
545 "Go to older cursor position in jump list",
546 movement, { .i = VIS_MOVE_JUMPLIST_PREV }
548 [VIS_ACTION_JUMPLIST_NEXT] = {
549 "jumplist-next",
550 "Go to newer cursor position in jump list",
551 movement, { .i = VIS_MOVE_JUMPLIST_NEXT }
553 [VIS_ACTION_CHANGELIST_PREV] = {
554 "changelist-prev",
555 "Go to older cursor position in change list",
556 movement, { .i = VIS_MOVE_CHANGELIST_PREV }
558 [VIS_ACTION_CHANGELIST_NEXT] = {
559 "changelist-next",
560 "Go to newer cursor position in change list",
561 movement, { .i = VIS_MOVE_CHANGELIST_NEXT }
563 [VIS_ACTION_UNDO] = {
564 "editor-undo",
565 "Undo last change",
566 undo,
568 [VIS_ACTION_REDO] = {
569 "editor-redo",
570 "Redo last change",
571 redo,
573 [VIS_ACTION_EARLIER] = {
574 "editor-earlier",
575 "Goto older text state",
576 earlier,
578 [VIS_ACTION_LATER] = {
579 "editor-later",
580 "Goto newer text state",
581 later,
583 [VIS_ACTION_MACRO_RECORD] = {
584 "macro-record",
585 "Record macro into given register",
586 macro_record,
588 [VIS_ACTION_MACRO_REPLAY] = {
589 "macro-replay",
590 "Replay macro, execute the content of the given register",
591 macro_replay,
593 [VIS_ACTION_MARK_SET] = {
594 "mark-set",
595 "Set given mark at current cursor position",
596 mark_set,
598 [VIS_ACTION_MARK_GOTO] = {
599 "mark-goto",
600 "Goto the position of the given mark",
601 mark_motion, { .i = VIS_MOVE_MARK }
603 [VIS_ACTION_MARK_GOTO_LINE] = {
604 "mark-goto-line",
605 "Goto first non-blank character of the line containing the given mark",
606 mark_motion, { .i = VIS_MOVE_MARK_LINE }
608 [VIS_ACTION_REDRAW] = {
609 "editor-redraw",
610 "Redraw current editor content",
611 call, { .f = vis_draw }
613 [VIS_ACTION_REPLACE_CHAR] = {
614 "replace-char",
615 "Replace the character under the cursor",
616 replace,
618 [VIS_ACTION_TOTILL_REPEAT] = {
619 "totill-repeat",
620 "Repeat latest to/till motion",
621 movement, { .i = VIS_MOVE_TOTILL_REPEAT }
623 [VIS_ACTION_TOTILL_REVERSE] = {
624 "totill-reverse",
625 "Repeat latest to/till motion but in opposite direction",
626 movement, { .i = VIS_MOVE_TOTILL_REVERSE }
628 [VIS_ACTION_PROMPT_SEARCH_FORWARD] = {
629 "search-forward",
630 "Search forward",
631 prompt_search, { .s = "/" }
633 [VIS_ACTION_PROMPT_SEARCH_BACKWARD] = {
634 "search-backward",
635 "Search backward",
636 prompt_search, { .s = "?" }
638 [VIS_ACTION_TILL_LEFT] = {
639 "till-left",
640 "Till after the occurrence of character to the left",
641 movement_key, { .i = VIS_MOVE_LEFT_TILL }
643 [VIS_ACTION_TILL_RIGHT] = {
644 "till-right",
645 "Till before the occurrence of character to the right",
646 movement_key, { .i = VIS_MOVE_RIGHT_TILL }
648 [VIS_ACTION_TO_LEFT] = {
649 "to-left",
650 "To the first occurrence of character to the left",
651 movement_key, { .i = VIS_MOVE_LEFT_TO }
653 [VIS_ACTION_TO_RIGHT] = {
654 "to-right",
655 "To the first occurrence of character to the right",
656 movement_key, { .i = VIS_MOVE_RIGHT_TO }
658 [VIS_ACTION_REGISTER] = {
659 "register",
660 "Use given register for next operator",
661 reg,
663 [VIS_ACTION_OPERATOR_CHANGE] = {
664 "vis-operator-change",
665 "Change operator",
666 operator, { .i = VIS_OP_CHANGE }
668 [VIS_ACTION_OPERATOR_DELETE] = {
669 "vis-operator-delete",
670 "Delete operator",
671 operator, { .i = VIS_OP_DELETE }
673 [VIS_ACTION_OPERATOR_YANK] = {
674 "vis-operator-yank",
675 "Yank operator",
676 operator, { .i = VIS_OP_YANK }
678 [VIS_ACTION_OPERATOR_SHIFT_LEFT] = {
679 "vis-operator-shift-left",
680 "Shift left operator",
681 operator, { .i = VIS_OP_SHIFT_LEFT }
683 [VIS_ACTION_OPERATOR_SHIFT_RIGHT] = {
684 "vis-operator-shift-right",
685 "Shift right operator",
686 operator, { .i = VIS_OP_SHIFT_RIGHT }
688 [VIS_ACTION_OPERATOR_CASE_LOWER] = {
689 "vis-operator-case-lower",
690 "Lowercase operator",
691 operator, { .i = VIS_OP_CASE_LOWER }
693 [VIS_ACTION_OPERATOR_CASE_UPPER] = {
694 "vis-operator-case-upper",
695 "Uppercase operator",
696 operator, { .i = VIS_OP_CASE_UPPER }
698 [VIS_ACTION_OPERATOR_CASE_SWAP] = {
699 "vis-operator-case-swap",
700 "Swap case operator",
701 operator, { .i = VIS_OP_CASE_SWAP }
703 [VIS_ACTION_COUNT] = {
704 "vis-count",
705 "Count specifier",
706 count,
708 [VIS_ACTION_INSERT_NEWLINE] = {
709 "insert-newline",
710 "Insert a line break (depending on file type)",
711 call, { .f = vis_insert_nl }
713 [VIS_ACTION_INSERT_TAB] = {
714 "insert-tab",
715 "Insert a tab (might be converted to spaces)",
716 call, { .f = vis_insert_tab }
718 [VIS_ACTION_INSERT_VERBATIM] = {
719 "insert-verbatim",
720 "Insert Unicode character based on code point",
721 insert_verbatim,
723 [VIS_ACTION_INSERT_REGISTER] = {
724 "insert-register",
725 "Insert specified register content",
726 insert_register,
728 [VIS_ACTION_WINDOW_NEXT] = {
729 "window-next",
730 "Focus next window",
731 call, { .f = vis_window_next }
733 [VIS_ACTION_WINDOW_PREV] = {
734 "window-prev",
735 "Focus previous window",
736 call, { .f = vis_window_prev }
738 [VIS_ACTION_APPEND_CHAR_NEXT] = {
739 "append-char-next",
740 "Append text after the cursor",
741 insertmode, { .i = VIS_MOVE_CHAR_NEXT }
743 [VIS_ACTION_APPEND_LINE_END] = {
744 "append-line-end",
745 "Append text after the end of the line",
746 insertmode, { .i = VIS_MOVE_LINE_END },
748 [VIS_ACTION_INSERT_LINE_START] = {
749 "insert-line-start",
750 "Insert text before the first non-blank in the line",
751 insertmode, { .i = VIS_MOVE_LINE_START },
753 [VIS_ACTION_OPEN_LINE_ABOVE] = {
754 "open-line-above",
755 "Begin a new line above the cursor",
756 openline, { .i = -1 }
758 [VIS_ACTION_OPEN_LINE_BELOW] = {
759 "open-line-below",
760 "Begin a new line below the cursor",
761 openline, { .i = +1 }
763 [VIS_ACTION_JOIN_LINE_BELOW] = {
764 "join-line-below",
765 "Join line(s)",
766 join, { .i = VIS_MOVE_LINE_NEXT },
768 [VIS_ACTION_JOIN_LINES] = {
769 "join-lines",
770 "Join selected lines",
771 operator, { .i = VIS_OP_JOIN }
773 [VIS_ACTION_PROMPT_SHOW] = {
774 "prompt-show",
775 "Show editor command line prompt",
776 prompt_cmd, { .s = "" }
778 [VIS_ACTION_PROMPT_BACKSPACE] = {
779 "prompt-backspace",
780 "Delete previous character in prompt",
781 prompt_backspace
783 [VIS_ACTION_PROMPT_ENTER] = {
784 "prompt-enter",
785 "Execute current prompt content",
786 call, { .f = vis_prompt_enter }
788 [VIS_ACTION_PROMPT_SHOW_VISUAL] = {
789 "prompt-show-visual",
790 "Show editor command line prompt in visual mode",
791 prompt_cmd, { .s = "'<,'>" }
793 [VIS_ACTION_REPEAT] = {
794 "editor-repeat",
795 "Repeat latest editor command",
796 repeat
798 [VIS_ACTION_SELECTION_FLIP] = {
799 "selection-flip",
800 "Flip selection, move cursor to other end",
801 selection_end,
803 [VIS_ACTION_SELECTION_RESTORE] = {
804 "selection-restore",
805 "Restore last selection",
806 selection_restore,
808 [VIS_ACTION_WINDOW_REDRAW_TOP] = {
809 "window-redraw-top",
810 "Redraw cursor line at the top of the window",
811 window, { .w = view_redraw_top }
813 [VIS_ACTION_WINDOW_REDRAW_CENTER] = {
814 "window-redraw-center",
815 "Redraw cursor line at the center of the window",
816 window, { .w = view_redraw_center }
818 [VIS_ACTION_WINDOW_REDRAW_BOTTOM] = {
819 "window-redraw-bottom",
820 "Redraw cursor line at the bottom of the window",
821 window, { .w = view_redraw_bottom }
823 [VIS_ACTION_WINDOW_SLIDE_UP] = {
824 "window-slide-up",
825 "Slide window content upwards",
826 wslide, { .i = -1 }
828 [VIS_ACTION_WINDOW_SLIDE_DOWN] = {
829 "window-slide-down",
830 "Slide window content downwards",
831 wslide, { .i = +1 }
833 [VIS_ACTION_PUT_AFTER] = {
834 "put-after",
835 "Put text after the cursor",
836 operator, { .i = VIS_OP_PUT_AFTER }
838 [VIS_ACTION_PUT_BEFORE] = {
839 "put-before",
840 "Put text before the cursor",
841 operator, { .i = VIS_OP_PUT_BEFORE }
843 [VIS_ACTION_PUT_AFTER_END] = {
844 "put-after-end",
845 "Put text after the cursor, place cursor after new text",
846 operator, { .i = VIS_OP_PUT_AFTER_END }
848 [VIS_ACTION_PUT_BEFORE_END] = {
849 "put-before-end",
850 "Put text before the cursor, place cursor after new text",
851 operator, { .i = VIS_OP_PUT_BEFORE_END }
853 [VIS_ACTION_CURSOR_SELECT_WORD] = {
854 "cursors-select-word",
855 "Select word under cursor",
856 cursors_select,
858 [VIS_ACTION_CURSORS_NEW_LINE_ABOVE] = {
859 "cursors-new-lines-above",
860 "Create a new cursor on the line above",
861 cursors_new, { .i = -1 }
863 [VIS_ACTION_CURSORS_NEW_LINE_BELOW] = {
864 "cursor-new-lines-below",
865 "Create a new cursor on the line below",
866 cursors_new, { .i = +1 }
868 [VIS_ACTION_CURSORS_NEW_LINES_BEGIN] = {
869 "cursors-new-lines-begin",
870 "Create a new cursor at the start of every line covered by selection",
871 operator, { .i = VIS_OP_CURSOR_SOL }
873 [VIS_ACTION_CURSORS_NEW_LINES_END] = {
874 "cursors-new-lines-end",
875 "Create a new cursor at the end of every line covered by selection",
876 operator, { .i = VIS_OP_CURSOR_EOL }
878 [VIS_ACTION_CURSORS_NEW_MATCH_NEXT] = {
879 "cursors-new-match-next",
880 "Select the next region matching the current selection",
881 cursors_select_next
883 [VIS_ACTION_CURSORS_NEW_MATCH_SKIP] = {
884 "cursors-new-match-skip",
885 "Clear current selection, but select next match",
886 cursors_select_skip,
888 [VIS_ACTION_CURSORS_ALIGN] = {
889 "cursors-align",
890 "Try to align all cursors on the same column",
891 cursors_align,
893 [VIS_ACTION_CURSORS_REMOVE_ALL] = {
894 "cursors-remove-all",
895 "Remove all but the primary cursor",
896 cursors_clear,
898 [VIS_ACTION_CURSORS_REMOVE_LAST] = {
899 "cursors-remove-last",
900 "Remove least recently created cursor",
901 cursors_remove,
903 [VIS_ACTION_TEXT_OBJECT_WORD_OUTER] = {
904 "text-object-word-outer",
905 "A word leading and trailing whitespace included",
906 textobj, { .i = VIS_TEXTOBJECT_OUTER_WORD }
908 [VIS_ACTION_TEXT_OBJECT_WORD_INNER] = {
909 "text-object-word-inner",
910 "A word leading and trailing whitespace excluded",
911 textobj, { .i = VIS_TEXTOBJECT_INNER_WORD }
913 [VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER] = {
914 "text-object-longword-outer",
915 "A WORD leading and trailing whitespace included",
916 textobj, { .i = VIS_TEXTOBJECT_OUTER_LONGWORD }
918 [VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER] = {
919 "text-object-longword-inner",
920 "A WORD leading and trailing whitespace excluded",
921 textobj, { .i = VIS_TEXTOBJECT_INNER_LONGWORD }
923 [VIS_ACTION_TEXT_OBJECT_SENTENCE] = {
924 "text-object-sentence",
925 "A sentence",
926 textobj, { .i = VIS_TEXTOBJECT_SENTENCE }
928 [VIS_ACTION_TEXT_OBJECT_PARAGRAPH] = {
929 "text-object-paragraph",
930 "A paragraph",
931 textobj, { .i = VIS_TEXTOBJECT_PARAGRAPH }
933 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER] = {
934 "text-object-square-bracket-outer",
935 "[] block (outer variant)",
936 textobj, { .i = VIS_TEXTOBJECT_OUTER_SQUARE_BRACKET }
938 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER] = {
939 "text-object-square-bracket-inner",
940 "[] block (inner variant)",
941 textobj, { .i = VIS_TEXTOBJECT_INNER_SQUARE_BRACKET }
943 [VIS_ACTION_TEXT_OBJECT_PARANTHESE_OUTER] = {
944 "text-object-parentheses-outer",
945 "() block (outer variant)",
946 textobj, { .i = VIS_TEXTOBJECT_OUTER_PARANTHESE }
948 [VIS_ACTION_TEXT_OBJECT_PARANTHESE_INNER] = {
949 "text-object-parentheses-inner",
950 "() block (inner variant)",
951 textobj, { .i = VIS_TEXTOBJECT_INNER_PARANTHESE }
953 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER] = {
954 "text-object-angle-bracket-outer",
955 "<> block (outer variant)",
956 textobj, { .i = VIS_TEXTOBJECT_OUTER_ANGLE_BRACKET }
958 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER] = {
959 "text-object-angle-bracket-inner",
960 "<> block (inner variant)",
961 textobj, { .i = VIS_TEXTOBJECT_INNER_ANGLE_BRACKET }
963 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER] = {
964 "text-object-curly-bracket-outer",
965 "{} block (outer variant)",
966 textobj, { .i = VIS_TEXTOBJECT_OUTER_CURLY_BRACKET }
968 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER] = {
969 "text-object-curly-bracket-inner",
970 "{} block (inner variant)",
971 textobj, { .i = VIS_TEXTOBJECT_INNER_CURLY_BRACKET }
973 [VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER] = {
974 "text-object-quote-outer",
975 "A quoted string, including the quotation marks",
976 textobj, { .i = VIS_TEXTOBJECT_OUTER_QUOTE }
978 [VIS_ACTION_TEXT_OBJECT_QUOTE_INNER] = {
979 "text-object-quote-inner",
980 "A quoted string, excluding the quotation marks",
981 textobj, { .i = VIS_TEXTOBJECT_INNER_QUOTE }
983 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER] = {
984 "text-object-single-quote-outer",
985 "A single quoted string, including the quotation marks",
986 textobj, { .i = VIS_TEXTOBJECT_OUTER_SINGLE_QUOTE }
988 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER] = {
989 "text-object-single-quote-inner",
990 "A single quoted string, excluding the quotation marks",
991 textobj, { .i = VIS_TEXTOBJECT_INNER_SINGLE_QUOTE }
993 [VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER] = {
994 "text-object-backtick-outer",
995 "A backtick delimited string (outer variant)",
996 textobj, { .i = VIS_TEXTOBJECT_OUTER_BACKTICK }
998 [VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER] = {
999 "text-object-backtick-inner",
1000 "A backtick delimited string (inner variant)",
1001 textobj, { .i = VIS_TEXTOBJECT_INNER_BACKTICK }
1003 [VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER] = {
1004 "text-object-entire-outer",
1005 "The whole text content",
1006 textobj, { .i = VIS_TEXTOBJECT_OUTER_ENTIRE }
1008 [VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER] = {
1009 "text-object-entire-inner",
1010 "The whole text content, except for leading and trailing empty lines",
1011 textobj, { .i = VIS_TEXTOBJECT_INNER_ENTIRE }
1013 [VIS_ACTION_TEXT_OBJECT_FUNCTION_OUTER] = {
1014 "text-object-function-outer",
1015 "A whole C-like function",
1016 textobj, { .i = VIS_TEXTOBJECT_OUTER_FUNCTION }
1018 [VIS_ACTION_TEXT_OBJECT_FUNCTION_INNER] = {
1019 "text-object-function-inner",
1020 "A whole C-like function body",
1021 textobj, { .i = VIS_TEXTOBJECT_INNER_FUNCTION }
1023 [VIS_ACTION_TEXT_OBJECT_LINE_OUTER] = {
1024 "text-object-line-outer",
1025 "The whole line",
1026 textobj, { .i = VIS_TEXTOBJECT_OUTER_LINE }
1028 [VIS_ACTION_TEXT_OBJECT_LINE_INNER] = {
1029 "text-object-line-inner",
1030 "The whole line, excluding leading and trailing whitespace",
1031 textobj, { .i = VIS_TEXTOBJECT_INNER_LINE }
1033 [VIS_ACTION_MOTION_CHARWISE] = {
1034 "motion-charwise",
1035 "Force motion to be charwise",
1036 motiontype, { .i = VIS_MOTIONTYPE_CHARWISE }
1038 [VIS_ACTION_MOTION_LINEWISE] = {
1039 "motion-linewise",
1040 "Force motion to be linewise",
1041 motiontype, { .i = VIS_MOTIONTYPE_LINEWISE }
1043 [VIS_ACTION_NOP] = {
1044 "nop",
1045 "Ignore key, do nothing",
1046 nop,
1050 #include "config.h"
1052 /** key bindings functions */
1054 static const char *nop(Vis *vis, const char *keys, const Arg *arg) {
1055 return keys;
1058 static const char *key2macro(Vis *vis, const char *keys, enum VisMacro *macro) {
1059 *macro = VIS_MACRO_INVALID;
1060 if (keys[0] >= 'a' && keys[0] <= 'z')
1061 *macro = keys[0] - 'a';
1062 else if (keys[0] == '@')
1063 *macro = VIS_MACRO_LAST_RECORDED;
1064 else if (keys[0] == '\0')
1065 return NULL;
1066 return keys+1;
1069 static const char *macro_record(Vis *vis, const char *keys, const Arg *arg) {
1070 if (vis_macro_record_stop(vis))
1071 return keys;
1072 enum VisMacro macro;
1073 keys = key2macro(vis, keys, &macro);
1074 vis_macro_record(vis, macro);
1075 vis_draw(vis);
1076 return keys;
1079 static const char *macro_replay(Vis *vis, const char *keys, const Arg *arg) {
1080 enum VisMacro macro;
1081 keys = key2macro(vis, keys, &macro);
1082 vis_macro_replay(vis, macro);
1083 return keys;
1086 static const char *suspend(Vis *vis, const char *keys, const Arg *arg) {
1087 vis_suspend(vis);
1088 return keys;
1091 static const char *repeat(Vis *vis, const char *keys, const Arg *arg) {
1092 vis_repeat(vis);
1093 return keys;
1096 static const char *cursors_new(Vis *vis, const char *keys, const Arg *arg) {
1097 View *view = vis_view(vis);
1098 Text *txt = vis_text(vis);
1099 size_t pos = view_cursor_get(view);
1100 if (arg->i > 0)
1101 pos = text_line_down(txt, pos);
1102 else if (arg->i < 0)
1103 pos = text_line_up(txt, pos);
1104 Cursor *cursor = view_cursors_new(view);
1105 if (cursor)
1106 view_cursors_to(cursor, pos);
1107 return keys;
1110 static const char *cursors_align(Vis *vis, const char *keys, const Arg *arg) {
1111 View *view = vis_view(vis);
1112 Text *txt = vis_text(vis);
1113 int mincol = INT_MAX;
1114 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
1115 size_t pos = view_cursors_pos(c);
1116 int col = text_line_char_get(txt, pos);
1117 if (col < mincol)
1118 mincol = col;
1120 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
1121 size_t pos = view_cursors_pos(c);
1122 size_t col = text_line_char_set(txt, pos, mincol);
1123 view_cursors_to(c, col);
1125 return keys;
1128 static const char *cursors_clear(Vis *vis, const char *keys, const Arg *arg) {
1129 View *view = vis_view(vis);
1130 if (view_cursors_count(view) > 1)
1131 view_cursors_clear(view);
1132 else
1133 view_cursors_selection_clear(view_cursor(view));
1134 return keys;
1137 static const char *cursors_select(Vis *vis, const char *keys, const Arg *arg) {
1138 Text *txt = vis_text(vis);
1139 View *view = vis_view(vis);
1140 for (Cursor *cursor = view_cursors(view); cursor; cursor = view_cursors_next(cursor)) {
1141 Filerange sel = view_cursors_selection_get(cursor);
1142 Filerange word = text_object_word(txt, view_cursors_pos(cursor));
1143 if (!text_range_valid(&sel) && text_range_valid(&word)) {
1144 view_cursors_selection_set(cursor, &word);
1145 view_cursors_to(cursor, text_char_prev(txt, word.end));
1148 vis_mode_switch(vis, VIS_MODE_VISUAL);
1149 return keys;
1152 static const char *cursors_select_next(Vis *vis, const char *keys, const Arg *arg) {
1153 Text *txt = vis_text(vis);
1154 View *view = vis_view(vis);
1155 Cursor *cursor = view_cursor(view);
1156 Filerange sel = view_cursors_selection_get(cursor);
1157 if (!text_range_valid(&sel))
1158 return keys;
1160 size_t len = text_range_size(&sel);
1161 char *buf = malloc(len+1);
1162 if (!buf)
1163 return keys;
1164 len = text_bytes_get(txt, sel.start, len, buf);
1165 buf[len] = '\0';
1166 Filerange word = text_object_word_find_next(txt, sel.end, buf);
1167 free(buf);
1169 if (text_range_valid(&word)) {
1170 cursor = view_cursors_new(view);
1171 if (!cursor)
1172 return keys;
1173 view_cursors_selection_set(cursor, &word);
1174 view_cursors_to(cursor, text_char_prev(txt, word.end));
1176 return keys;
1179 static const char *cursors_select_skip(Vis *vis, const char *keys, const Arg *arg) {
1180 View *view = vis_view(vis);
1181 Cursor *cursor = view_cursor(view);
1182 keys = cursors_select_next(vis, keys, arg);
1183 if (cursor != view_cursor(view))
1184 view_cursors_dispose(cursor);
1185 return keys;
1188 static const char *cursors_remove(Vis *vis, const char *keys, const Arg *arg) {
1189 View *view = vis_view(vis);
1190 view_cursors_dispose(view_cursor(view));
1191 return keys;
1194 static const char *replace(Vis *vis, const char *keys, const Arg *arg) {
1195 if (!keys[0])
1196 return NULL;
1197 const char *next = vis_keys_next(vis, keys);
1198 if (!next)
1199 return NULL;
1200 size_t len = next - keys;
1201 char key[len+1];
1202 memcpy(key, keys, len);
1203 key[len] = '\0';
1204 vis_operator(vis, VIS_OP_REPLACE);
1205 vis_motion(vis, VIS_MOVE_NOP);
1206 vis_keys_inject(vis, next, key);
1207 vis_keys_inject(vis, next+len, "<Escape>");
1208 return next;
1211 static const char *count(Vis *vis, const char *keys, const Arg *arg) {
1212 int digit = keys[-1] - '0';
1213 int count = vis_count_get(vis);
1214 if (0 <= digit && digit <= 9) {
1215 if (digit == 0 && count == 0)
1216 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
1217 vis_count_set(vis, count * 10 + digit);
1219 return keys;
1222 static const char *gotoline(Vis *vis, const char *keys, const Arg *arg) {
1223 if (vis_count_get(vis))
1224 vis_motion(vis, VIS_MOVE_LINE);
1225 else if (arg->i < 0)
1226 vis_motion(vis, VIS_MOVE_FILE_BEGIN);
1227 else
1228 vis_motion(vis, VIS_MOVE_FILE_END);
1229 return keys;
1232 static const char *motiontype(Vis *vis, const char *keys, const Arg *arg) {
1233 vis_motion_type(vis, arg->i);
1234 return keys;
1237 static const char *operator(Vis *vis, const char *keys, const Arg *arg) {
1238 vis_operator(vis, arg->i);
1239 return keys;
1242 static const char *movement_key(Vis *vis, const char *keys, const Arg *arg) {
1243 if (!keys[0])
1244 return NULL;
1245 char key[32];
1246 const char *next = vis_keys_next(vis, keys);
1247 strncpy(key, keys, next - keys + 1);
1248 key[sizeof(key)-1] = '\0';
1249 vis_motion(vis, arg->i, key);
1250 return next;
1253 static const char *movement(Vis *vis, const char *keys, const Arg *arg) {
1254 vis_motion(vis, arg->i);
1255 return keys;
1258 static const char *textobj(Vis *vis, const char *keys, const Arg *arg) {
1259 vis_textobject(vis, arg->i);
1260 return keys;
1263 static const char *selection_end(Vis *vis, const char *keys, const Arg *arg) {
1264 for (Cursor *c = view_cursors(vis_view(vis)); c; c = view_cursors_next(c))
1265 view_cursors_selection_swap(c);
1266 return keys;
1269 static const char *selection_restore(Vis *vis, const char *keys, const Arg *arg) {
1270 for (Cursor *c = view_cursors(vis_view(vis)); c; c = view_cursors_next(c))
1271 view_cursors_selection_restore(c);
1272 vis_mode_switch(vis, VIS_MODE_VISUAL);
1273 return keys;
1276 static const char *key2register(Vis *vis, const char *keys, enum VisRegister *reg) {
1277 *reg = VIS_REG_INVALID;
1278 if (!keys[0])
1279 return NULL;
1280 if (keys[0] >= 'a' && keys[0] <= 'z')
1281 *reg = keys[0] - 'a';
1282 return keys+1;
1285 static const char *reg(Vis *vis, const char *keys, const Arg *arg) {
1286 enum VisRegister reg;
1287 keys = key2register(vis, keys, &reg);
1288 vis_register_set(vis, reg);
1289 return keys;
1292 static const char *key2mark(Vis *vis, const char *keys, int *mark) {
1293 *mark = VIS_MARK_INVALID;
1294 if (!keys[0])
1295 return NULL;
1296 if (keys[0] >= 'a' && keys[0] <= 'z')
1297 *mark = keys[0] - 'a';
1298 else if (keys[0] == '<')
1299 *mark = MARK_SELECTION_START;
1300 else if (keys[0] == '>')
1301 *mark = MARK_SELECTION_END;
1302 return keys+1;
1305 static const char *mark_set(Vis *vis, const char *keys, const Arg *arg) {
1306 int mark;
1307 keys = key2mark(vis, keys, &mark);
1308 vis_mark_set(vis, mark, view_cursor_get(vis_view(vis)));
1309 return keys;
1312 static const char *mark_motion(Vis *vis, const char *keys, const Arg *arg) {
1313 int mark;
1314 keys = key2mark(vis, keys, &mark);
1315 vis_motion(vis, arg->i, mark);
1316 return keys;
1319 static const char *undo(Vis *vis, const char *keys, const Arg *arg) {
1320 size_t pos = text_undo(vis_text(vis));
1321 if (pos != EPOS) {
1322 View *view = vis_view(vis);
1323 if (view_cursors_count(view) == 1)
1324 view_cursor_to(view, pos);
1325 /* redraw all windows in case some display the same file */
1326 vis_draw(vis);
1328 return keys;
1331 static const char *redo(Vis *vis, const char *keys, const Arg *arg) {
1332 size_t pos = text_redo(vis_text(vis));
1333 if (pos != EPOS) {
1334 View *view = vis_view(vis);
1335 if (view_cursors_count(view) == 1)
1336 view_cursor_to(view, pos);
1337 /* redraw all windows in case some display the same file */
1338 vis_draw(vis);
1340 return keys;
1343 static const char *earlier(Vis *vis, const char *keys, const Arg *arg) {
1344 size_t pos = text_earlier(vis_text(vis), MAX(vis_count_get(vis), 1));
1345 if (pos != EPOS) {
1346 view_cursor_to(vis_view(vis), pos);
1347 /* redraw all windows in case some display the same file */
1348 vis_draw(vis);
1350 return keys;
1353 static const char *later(Vis *vis, const char *keys, const Arg *arg) {
1354 size_t pos = text_later(vis_text(vis), MAX(vis_count_get(vis), 1));
1355 if (pos != EPOS) {
1356 view_cursor_to(vis_view(vis), pos);
1357 /* redraw all windows in case some display the same file */
1358 vis_draw(vis);
1360 return keys;
1363 static const char *delete(Vis *vis, const char *keys, const Arg *arg) {
1364 vis_operator(vis, VIS_OP_DELETE);
1365 vis_motion(vis, arg->i);
1366 return keys;
1369 static const char *insert_register(Vis *vis, const char *keys, const Arg *arg) {
1370 enum VisRegister regid;
1371 keys = key2register(vis, keys, &regid);
1372 Register *reg = vis_register_get(vis, regid);
1373 if (reg) {
1374 int pos = view_cursor_get(vis_view(vis));
1375 vis_insert(vis, pos, reg->data, reg->len);
1376 view_cursor_to(vis_view(vis), pos + reg->len);
1378 return keys;
1381 static const char *prompt_search(Vis *vis, const char *keys, const Arg *arg) {
1382 vis_prompt_show(vis, arg->s, "");
1383 vis_mode_switch(vis, VIS_MODE_PROMPT);
1384 return keys;
1387 static const char *prompt_cmd(Vis *vis, const char *keys, const Arg *arg) {
1388 vis_prompt_show(vis, ":", arg->s);
1389 vis_mode_switch(vis, VIS_MODE_PROMPT);
1390 return keys;
1393 static const char *prompt_backspace(Vis *vis, const char *keys, const Arg *arg) {
1394 char *cmd = vis_prompt_get(vis);
1395 if (!cmd || !*cmd)
1396 vis_mode_switch(vis, VIS_MODE_NORMAL);
1397 else
1398 delete(vis, keys, &(const Arg){ .i = VIS_MOVE_CHAR_PREV });
1399 free(cmd);
1400 return keys;
1403 static const char *insert_verbatim(Vis *vis, const char *keys, const Arg *arg) {
1404 Rune rune = 0;
1405 char buf[4], type = keys[0];
1406 int len = 0, count = 0, base;
1407 switch (type) {
1408 case '\0':
1409 return NULL;
1410 case 'o':
1411 case 'O':
1412 count = 3;
1413 base = 8;
1414 break;
1415 case 'U':
1416 count = 4;
1417 /* fall through */
1418 case 'u':
1419 count += 4;
1420 base = 16;
1421 break;
1422 case 'x':
1423 case 'X':
1424 count = 2;
1425 base = 16;
1426 break;
1427 default:
1428 if (type < '0' || type > '9')
1429 return keys;
1430 rune = type - '0';
1431 count = 2;
1432 base = 10;
1433 break;
1436 for (keys++; keys[0] && count > 0; keys++, count--) {
1437 int v = 0;
1438 if (base == 8 && '0' <= keys[0] && keys[0] <= '7') {
1439 v = keys[0] - '0';
1440 } else if ((base == 10 || base == 16) && '0' <= keys[0] && keys[0] <= '9') {
1441 v = keys[0] - '0';
1442 } else if (base == 16 && 'a' <= keys[0] && keys[0] <= 'f') {
1443 v = 10 + keys[0] - 'a';
1444 } else if (base == 16 && 'A' <= keys[0] && keys[0] <= 'F') {
1445 v = 10 + keys[0] - 'A';
1446 } else {
1447 count = 0;
1448 break;
1450 rune = rune * base + v;
1453 if (count > 0)
1454 return NULL;
1456 if (type == 'u' || type == 'U') {
1457 len = runetochar(buf, &rune);
1458 } else {
1459 buf[0] = rune;
1460 len = 1;
1463 if (len > 0) {
1464 size_t pos = view_cursor_get(vis_view(vis));
1465 vis_insert(vis, pos, buf, len);
1466 view_cursor_to(vis_view(vis), pos + len);
1468 return keys;
1471 static const char *cmd(Vis *vis, const char *keys, const Arg *arg) {
1472 vis_cmd(vis, arg->s);
1473 return keys;
1476 static int argi2lines(Vis *vis, const Arg *arg) {
1477 switch (arg->i) {
1478 case -PAGE:
1479 case +PAGE:
1480 return view_height_get(vis_view(vis));
1481 case -PAGE_HALF:
1482 case +PAGE_HALF:
1483 return view_height_get(vis_view(vis))/2;
1484 default:
1485 if (vis_count_get(vis) > 0)
1486 return vis_count_get(vis);
1487 return arg->i < 0 ? -arg->i : arg->i;
1491 static const char *wscroll(Vis *vis, const char *keys, const Arg *arg) {
1492 if (arg->i >= 0)
1493 view_scroll_down(vis_view(vis), argi2lines(vis, arg));
1494 else
1495 view_scroll_up(vis_view(vis), argi2lines(vis, arg));
1496 return keys;
1499 static const char *wslide(Vis *vis, const char *keys, const Arg *arg) {
1500 if (arg->i >= 0)
1501 view_slide_down(vis_view(vis), argi2lines(vis, arg));
1502 else
1503 view_slide_up(vis_view(vis), argi2lines(vis, arg));
1504 return keys;
1507 static const char *call(Vis *vis, const char *keys, const Arg *arg) {
1508 arg->f(vis);
1509 return keys;
1512 static const char *window(Vis *vis, const char *keys, const Arg *arg) {
1513 arg->w(vis_view(vis));
1514 return keys;
1517 static const char *openline(Vis *vis, const char *keys, const Arg *arg) {
1518 vis_operator(vis, VIS_OP_INSERT);
1519 if (arg->i > 0) {
1520 vis_motion(vis, VIS_MOVE_LINE_END);
1521 vis_keys_inject(vis, keys, "<Enter>");
1522 } else {
1523 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
1524 vis_keys_inject(vis, keys, "<Enter><Up>");
1526 return keys;
1529 static const char *join(Vis *vis, const char *keys, const Arg *arg) {
1530 int count = vis_count_get(vis);
1531 if (count)
1532 vis_count_set(vis, count-1);
1533 vis_operator(vis, VIS_OP_JOIN);
1534 vis_motion(vis, arg->i);
1535 return keys;
1538 static const char *switchmode(Vis *vis, const char *keys, const Arg *arg) {
1539 vis_mode_switch(vis, arg->i);
1540 return keys;
1543 static const char *insertmode(Vis *vis, const char *keys, const Arg *arg) {
1544 vis_operator(vis, VIS_OP_INSERT);
1545 vis_motion(vis, arg->i);
1546 return keys;
1549 static Vis *vis;
1551 static KeyBinding *default_bindings[] = {
1552 [VIS_MODE_BASIC] = basic_movement,
1553 [VIS_MODE_MOVE] = vis_movements,
1554 [VIS_MODE_TEXTOBJ] = vis_textobjs,
1555 [VIS_MODE_OPERATOR_OPTION] = vis_operator_options,
1556 [VIS_MODE_OPERATOR] = vis_operators,
1557 [VIS_MODE_NORMAL] = vis_mode_normal,
1558 [VIS_MODE_VISUAL] = vis_mode_visual,
1559 [VIS_MODE_VISUAL_LINE] = vis_mode_visual_line,
1560 [VIS_MODE_READLINE] = vis_mode_readline,
1561 [VIS_MODE_PROMPT] = vis_mode_prompt,
1562 [VIS_MODE_INSERT] = vis_mode_insert,
1563 [VIS_MODE_REPLACE] = vis_mode_replace,
1566 static void signal_handler(int signum, siginfo_t *siginfo, void *context) {
1567 vis_signal_handler(vis, signum, siginfo, context);
1570 int main(int argc, char *argv[]) {
1572 vis = vis_new(ui_curses_new());
1573 if (!vis)
1574 return EXIT_FAILURE;
1576 for (int i = 0; i < LENGTH(vis_action); i++) {
1577 KeyAction *action = &vis_action[i];
1578 if (!vis_action_register(vis, action))
1579 vis_die(vis, "Could not register action: %s\n", action->name);
1582 for (int i = 0; i < LENGTH(default_bindings); i++) {
1583 if (!vis_mode_bindings(vis, i, &default_bindings[i]))
1584 vis_die(vis, "Could not load default bindings\n");
1587 /* install signal handlers etc. */
1588 struct sigaction sa;
1589 memset(&sa, 0, sizeof sa);
1590 sa.sa_flags = SA_SIGINFO;
1591 sa.sa_sigaction = signal_handler;
1592 if (sigaction(SIGBUS, &sa, NULL) || sigaction(SIGINT, &sa, NULL))
1593 vis_die(vis, "sigaction: %s", strerror(errno));
1595 sigset_t blockset;
1596 sigemptyset(&blockset);
1597 sigaddset(&blockset, SIGWINCH);
1598 sigprocmask(SIG_BLOCK, &blockset, NULL);
1599 signal(SIGPIPE, SIG_IGN);
1601 int status = vis_run(vis, argc, argv);
1602 vis_free(vis);
1603 return status;