vis: move text object definitions to separate file
[vis.git] / main.c
blob9834dbfa46e8742aa79040facd5ac9c212c40993
1 #include <signal.h>
2 #include <limits.h>
3 #include <string.h>
4 #include <wchar.h>
5 #include <errno.h>
7 #include "ui-curses.h"
8 #include "vis.h"
9 #include "vis-lua.h"
10 #include "text-util.h"
11 #include "text-motions.h"
12 #include "text-objects.h"
13 #include "util.h"
14 #include "libutf.h"
16 #define PAGE INT_MAX
17 #define PAGE_HALF (INT_MAX-1)
19 /** functions to be called from keybindings */
20 /* ignore key, do nothing */
21 static const char *nop(Vis*, const char *keys, const Arg *arg);
22 /* record/replay macro indicated by keys */
23 static const char *macro_record(Vis*, const char *keys, const Arg *arg);
24 static const char *macro_replay(Vis*, const char *keys, const Arg *arg);
25 /* temporarily suspend the editor and return to the shell, type 'fg' to get back */
26 static const char *suspend(Vis*, const char *keys, const Arg *arg);
27 /* switch to mode indicated by arg->i */
28 static const char *switchmode(Vis*, const char *keys, const Arg *arg);
29 /* switch to insert mode after performing movement indicated by arg->i */
30 static const char *insertmode(Vis*, const char *keys, const Arg *arg);
31 /* set mark indicated by keys to current cursor position */
32 static const char *mark_set(Vis*, const char *keys, const Arg *arg);
33 /* add a new line either before or after the one where the cursor currently is */
34 static const char *openline(Vis*, const char *keys, const Arg *arg);
35 /* join lines from current cursor position to movement indicated by arg */
36 static const char *join(Vis*, const char *keys, const Arg *arg);
37 /* execute arg->s as if it was typed on command prompt */
38 static const char *cmd(Vis*, const char *keys, const Arg *arg);
39 /* perform last action i.e. action_prev again */
40 static const char *repeat(Vis*, const char *keys, const Arg *arg);
41 /* replace character at cursor with one from keys */
42 static const char *replace(Vis*, const char *keys, const Arg *arg);
43 /* create a new cursor on the previous (arg->i < 0) or next (arg->i > 0) line */
44 static const char *cursors_new(Vis*, const char *keys, const Arg *arg);
45 /* try to align all cursors on the same column */
46 static const char *cursors_align(Vis*, const char *keys, const Arg *arg);
47 /* remove all but the primary cursor and their selections */
48 static const char *cursors_clear(Vis*, const char *keys, const Arg *arg);
49 /* remove the least recently added cursor */
50 static const char *cursors_remove(Vis*, const char *keys, const Arg *arg);
51 /* select the word the cursor is currently over */
52 static const char *cursors_select(Vis*, const char *keys, const Arg *arg);
53 /* select the next region matching the current selection */
54 static const char *cursors_select_next(Vis*, const char *keys, const Arg *arg);
55 /* clear current selection but select next match */
56 static const char *cursors_select_skip(Vis*, const char *keys, const Arg *arg);
57 /* adjust current used count according to keys */
58 static const char *count(Vis*, const char *keys, const Arg *arg);
59 /* move to the count-th line or if not given either to the first (arg->i < 0)
60 * or last (arg->i > 0) line of file */
61 static const char *gotoline(Vis*, const char *keys, const Arg *arg);
62 /* set motion type either LINEWISE or CHARWISE via arg->i */
63 static const char *motiontype(Vis*, const char *keys, const Arg *arg);
64 /* make the current action use the operator indicated by arg->i */
65 static const char *operator(Vis*, const char *keys, const Arg *arg);
66 /* use arg->s as command for the filter operator */
67 static const char *operator_filter(Vis*, const char *keys, const Arg *arg);
68 /* blocks to read a key and performs movement indicated by arg->i which
69 * should be one of VIS_MOVE_{RIGHT,LEFT}_{TO,TILL} */
70 static const char *movement_key(Vis*, const char *keys, const Arg *arg);
71 /* perform the movement as indicated by arg->i */
72 static const char *movement(Vis*, const char *keys, const Arg *arg);
73 /* let the current operator affect the range indicated by the text object arg->i */
74 static const char *textobj(Vis*, const char *keys, const Arg *arg);
75 /* move to the other end of selected text */
76 static const char *selection_end(Vis*, const char *keys, const Arg *arg);
77 /* restore least recently used selection */
78 static const char *selection_restore(Vis*, const char *keys, const Arg *arg);
79 /* use register indicated by keys for the current operator */
80 static const char *reg(Vis*, const char *keys, const Arg *arg);
81 /* perform arg->i motion with a mark indicated by keys as argument */
82 static const char *mark_motion(Vis*, const char *keys, const Arg *arg);
83 /* {un,re}do last action, redraw window */
84 static const char *undo(Vis*, const char *keys, const Arg *arg);
85 static const char *redo(Vis*, const char *keys, const Arg *arg);
86 /* earlier, later action chronologically, redraw window */
87 static const char *earlier(Vis*, const char *keys, const Arg *arg);
88 static const char *later(Vis*, const char *keys, const Arg *arg);
89 /* delete from the current cursor position to the end of
90 * movement as indicated by arg->i */
91 static const char *delete(Vis*, const char *keys, const Arg *arg);
92 /* insert register content indicated by keys at current cursor position */
93 static const char *insert_register(Vis*, const char *keys, const Arg *arg);
94 /* show a user prompt to get input with title arg->s */
95 static const char *prompt_show(Vis*, const char *keys, const Arg *arg);
96 /* blocks to read 3 consecutive digits and inserts the corresponding byte value */
97 static const char *insert_verbatim(Vis*, const char *keys, const Arg *arg);
98 /* scroll window content according to arg->i which can be either PAGE, PAGE_HALF,
99 * or an arbitrary number of lines. a multiplier overrides what is given in arg->i.
100 * negative values scroll back, positive forward. */
101 static const char *wscroll(Vis*, const char *keys, const Arg *arg);
102 /* similar to scroll, but do only move window content not cursor position */
103 static const char *wslide(Vis*, const char *keys, const Arg *arg);
104 /* call editor function as indicated by arg->f */
105 static const char *call(Vis*, const char *keys, const Arg *arg);
106 /* call window function as indicated by arg->w */
107 static const char *window(Vis*, const char *keys, const Arg *arg);
108 /* show info about Unicode character at cursor position */
109 static const char *unicode_info(Vis*, const char *keys, const Arg *arg);
111 enum {
112 VIS_ACTION_EDITOR_SUSPEND,
113 VIS_ACTION_CURSOR_CHAR_PREV,
114 VIS_ACTION_CURSOR_CHAR_NEXT,
115 VIS_ACTION_CURSOR_WORD_START_PREV,
116 VIS_ACTION_CURSOR_WORD_START_NEXT,
117 VIS_ACTION_CURSOR_WORD_END_PREV,
118 VIS_ACTION_CURSOR_WORD_END_NEXT,
119 VIS_ACTION_CURSOR_LONGWORD_START_PREV,
120 VIS_ACTION_CURSOR_LONGWORD_START_NEXT,
121 VIS_ACTION_CURSOR_LONGWORD_END_PREV,
122 VIS_ACTION_CURSOR_LONGWORD_END_NEXT,
123 VIS_ACTION_CURSOR_LINE_UP,
124 VIS_ACTION_CURSOR_LINE_DOWN,
125 VIS_ACTION_CURSOR_LINE_START,
126 VIS_ACTION_CURSOR_LINE_FINISH,
127 VIS_ACTION_CURSOR_LINE_BEGIN,
128 VIS_ACTION_CURSOR_LINE_END,
129 VIS_ACTION_CURSOR_SCREEN_LINE_UP,
130 VIS_ACTION_CURSOR_SCREEN_LINE_DOWN,
131 VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN,
132 VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE,
133 VIS_ACTION_CURSOR_SCREEN_LINE_END,
134 VIS_ACTION_CURSOR_BRACKET_MATCH,
135 VIS_ACTION_CURSOR_PARAGRAPH_PREV,
136 VIS_ACTION_CURSOR_PARAGRAPH_NEXT,
137 VIS_ACTION_CURSOR_SENTENCE_PREV,
138 VIS_ACTION_CURSOR_SENTENCE_NEXT,
139 VIS_ACTION_CURSOR_FUNCTION_START_PREV,
140 VIS_ACTION_CURSOR_FUNCTION_END_PREV,
141 VIS_ACTION_CURSOR_FUNCTION_START_NEXT,
142 VIS_ACTION_CURSOR_FUNCTION_END_NEXT,
143 VIS_ACTION_CURSOR_COLUMN,
144 VIS_ACTION_CURSOR_LINE_FIRST,
145 VIS_ACTION_CURSOR_LINE_LAST,
146 VIS_ACTION_CURSOR_WINDOW_LINE_TOP,
147 VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE,
148 VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM,
149 VIS_ACTION_CURSOR_SEARCH_NEXT,
150 VIS_ACTION_CURSOR_SEARCH_PREV,
151 VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD,
152 VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD,
153 VIS_ACTION_WINDOW_PAGE_UP,
154 VIS_ACTION_WINDOW_PAGE_DOWN,
155 VIS_ACTION_WINDOW_HALFPAGE_UP,
156 VIS_ACTION_WINDOW_HALFPAGE_DOWN,
157 VIS_ACTION_MODE_NORMAL,
158 VIS_ACTION_MODE_VISUAL,
159 VIS_ACTION_MODE_VISUAL_LINE,
160 VIS_ACTION_MODE_INSERT,
161 VIS_ACTION_MODE_REPLACE,
162 VIS_ACTION_MODE_OPERATOR_PENDING,
163 VIS_ACTION_DELETE_CHAR_PREV,
164 VIS_ACTION_DELETE_CHAR_NEXT,
165 VIS_ACTION_DELETE_LINE_BEGIN,
166 VIS_ACTION_DELETE_WORD_PREV,
167 VIS_ACTION_JUMPLIST_PREV,
168 VIS_ACTION_JUMPLIST_NEXT,
169 VIS_ACTION_CHANGELIST_PREV,
170 VIS_ACTION_CHANGELIST_NEXT,
171 VIS_ACTION_UNDO,
172 VIS_ACTION_REDO,
173 VIS_ACTION_EARLIER,
174 VIS_ACTION_LATER,
175 VIS_ACTION_MACRO_RECORD,
176 VIS_ACTION_MACRO_REPLAY,
177 VIS_ACTION_MARK_SET,
178 VIS_ACTION_MARK_GOTO,
179 VIS_ACTION_MARK_GOTO_LINE,
180 VIS_ACTION_REDRAW,
181 VIS_ACTION_REPLACE_CHAR,
182 VIS_ACTION_TOTILL_REPEAT,
183 VIS_ACTION_TOTILL_REVERSE,
184 VIS_ACTION_PROMPT_SEARCH_FORWARD,
185 VIS_ACTION_PROMPT_SEARCH_BACKWARD,
186 VIS_ACTION_TILL_LEFT,
187 VIS_ACTION_TILL_RIGHT,
188 VIS_ACTION_TO_LEFT,
189 VIS_ACTION_TO_RIGHT,
190 VIS_ACTION_REGISTER,
191 VIS_ACTION_OPERATOR_CHANGE,
192 VIS_ACTION_OPERATOR_DELETE,
193 VIS_ACTION_OPERATOR_YANK,
194 VIS_ACTION_OPERATOR_SHIFT_LEFT,
195 VIS_ACTION_OPERATOR_SHIFT_RIGHT,
196 VIS_ACTION_OPERATOR_CASE_LOWER,
197 VIS_ACTION_OPERATOR_CASE_UPPER,
198 VIS_ACTION_OPERATOR_CASE_SWAP,
199 VIS_ACTION_OPERATOR_FILTER,
200 VIS_ACTION_OPERATOR_FILTER_FMT,
201 VIS_ACTION_COUNT,
202 VIS_ACTION_INSERT_NEWLINE,
203 VIS_ACTION_INSERT_TAB,
204 VIS_ACTION_INSERT_VERBATIM,
205 VIS_ACTION_INSERT_REGISTER,
206 VIS_ACTION_WINDOW_NEXT,
207 VIS_ACTION_WINDOW_PREV,
208 VIS_ACTION_APPEND_CHAR_NEXT,
209 VIS_ACTION_APPEND_LINE_END,
210 VIS_ACTION_INSERT_LINE_START,
211 VIS_ACTION_OPEN_LINE_ABOVE,
212 VIS_ACTION_OPEN_LINE_BELOW,
213 VIS_ACTION_JOIN_LINE_BELOW,
214 VIS_ACTION_JOIN_LINES,
215 VIS_ACTION_PROMPT_SHOW,
216 VIS_ACTION_PROMPT_SHOW_VISUAL,
217 VIS_ACTION_REPEAT,
218 VIS_ACTION_SELECTION_FLIP,
219 VIS_ACTION_SELECTION_RESTORE,
220 VIS_ACTION_WINDOW_REDRAW_TOP,
221 VIS_ACTION_WINDOW_REDRAW_CENTER,
222 VIS_ACTION_WINDOW_REDRAW_BOTTOM,
223 VIS_ACTION_WINDOW_SLIDE_UP,
224 VIS_ACTION_WINDOW_SLIDE_DOWN,
225 VIS_ACTION_PUT_AFTER,
226 VIS_ACTION_PUT_BEFORE,
227 VIS_ACTION_PUT_AFTER_END,
228 VIS_ACTION_PUT_BEFORE_END,
229 VIS_ACTION_CURSOR_SELECT_WORD,
230 VIS_ACTION_CURSORS_NEW_LINE_ABOVE,
231 VIS_ACTION_CURSORS_NEW_LINE_BELOW,
232 VIS_ACTION_CURSORS_NEW_LINES_BEGIN,
233 VIS_ACTION_CURSORS_NEW_LINES_END,
234 VIS_ACTION_CURSORS_NEW_MATCH_NEXT,
235 VIS_ACTION_CURSORS_NEW_MATCH_SKIP,
236 VIS_ACTION_CURSORS_ALIGN,
237 VIS_ACTION_CURSORS_REMOVE_ALL,
238 VIS_ACTION_CURSORS_REMOVE_LAST,
239 VIS_ACTION_TEXT_OBJECT_WORD_OUTER,
240 VIS_ACTION_TEXT_OBJECT_WORD_INNER,
241 VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER,
242 VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER,
243 VIS_ACTION_TEXT_OBJECT_SENTENCE,
244 VIS_ACTION_TEXT_OBJECT_PARAGRAPH,
245 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER,
246 VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER,
247 VIS_ACTION_TEXT_OBJECT_PARANTHESE_OUTER,
248 VIS_ACTION_TEXT_OBJECT_PARANTHESE_INNER,
249 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER,
250 VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER,
251 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER,
252 VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER,
253 VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER,
254 VIS_ACTION_TEXT_OBJECT_QUOTE_INNER,
255 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER,
256 VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER,
257 VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER,
258 VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER,
259 VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER,
260 VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER,
261 VIS_ACTION_TEXT_OBJECT_FUNCTION_OUTER,
262 VIS_ACTION_TEXT_OBJECT_FUNCTION_INNER,
263 VIS_ACTION_TEXT_OBJECT_LINE_OUTER,
264 VIS_ACTION_TEXT_OBJECT_LINE_INNER,
265 VIS_ACTION_MOTION_CHARWISE,
266 VIS_ACTION_MOTION_LINEWISE,
267 VIS_ACTION_UNICODE_INFO,
268 VIS_ACTION_NOP,
271 static KeyAction vis_action[] = {
272 [VIS_ACTION_EDITOR_SUSPEND] = {
273 "editor-suspend",
274 "Suspend the editor",
275 suspend,
277 [VIS_ACTION_CURSOR_CHAR_PREV] = {
278 "cursor-char-prev",
279 "Move cursor left, to the previous character",
280 movement, { .i = VIS_MOVE_CHAR_PREV }
282 [VIS_ACTION_CURSOR_CHAR_NEXT] = {
283 "cursor-char-next",
284 "Move cursor right, to the next character",
285 movement, { .i = VIS_MOVE_CHAR_NEXT }
287 [VIS_ACTION_CURSOR_WORD_START_PREV] = {
288 "cursor-word-start-prev",
289 "Move cursor words backwards",
290 movement, { .i = VIS_MOVE_WORD_START_PREV }
292 [VIS_ACTION_CURSOR_WORD_START_NEXT] = {
293 "cursor-word-start-next",
294 "Move cursor words forwards",
295 movement, { .i = VIS_MOVE_WORD_START_NEXT }
297 [VIS_ACTION_CURSOR_WORD_END_PREV] = {
298 "cursor-word-end-prev",
299 "Move cursor backwards to the end of word",
300 movement, { .i = VIS_MOVE_WORD_END_PREV }
302 [VIS_ACTION_CURSOR_WORD_END_NEXT] = {
303 "cursor-word-end-next",
304 "Move cursor forward to the end of word",
305 movement, { .i = VIS_MOVE_WORD_END_NEXT }
307 [VIS_ACTION_CURSOR_LONGWORD_START_PREV] = {
308 "cursor-longword-start-prev",
309 "Move cursor WORDS backwards",
310 movement, { .i = VIS_MOVE_LONGWORD_START_PREV }
312 [VIS_ACTION_CURSOR_LONGWORD_START_NEXT] = {
313 "cursor-longword-start-next",
314 "Move cursor WORDS forwards",
315 movement, { .i = VIS_MOVE_LONGWORD_START_NEXT }
317 [VIS_ACTION_CURSOR_LONGWORD_END_PREV] = {
318 "cursor-longword-end-prev",
319 "Move cursor backwards to the end of WORD",
320 movement, { .i = VIS_MOVE_LONGWORD_END_PREV }
322 [VIS_ACTION_CURSOR_LONGWORD_END_NEXT] = {
323 "cursor-longword-end-next",
324 "Move cursor forward to the end of WORD",
325 movement, { .i = VIS_MOVE_LONGWORD_END_NEXT }
327 [VIS_ACTION_CURSOR_LINE_UP] = {
328 "cursor-line-up",
329 "Move cursor line upwards",
330 movement, { .i = VIS_MOVE_LINE_UP }
332 [VIS_ACTION_CURSOR_LINE_DOWN] = {
333 "cursor-line-down",
334 "Move cursor line downwards",
335 movement, { .i = VIS_MOVE_LINE_DOWN }
337 [VIS_ACTION_CURSOR_LINE_START] = {
338 "cursor-line-start",
339 "Move cursor to first non-blank character of the line",
340 movement, { .i = VIS_MOVE_LINE_START }
342 [VIS_ACTION_CURSOR_LINE_FINISH] = {
343 "cursor-line-finish",
344 "Move cursor to last non-blank character of the line",
345 movement, { .i = VIS_MOVE_LINE_FINISH }
347 [VIS_ACTION_CURSOR_LINE_BEGIN] = {
348 "cursor-line-begin",
349 "Move cursor to first character of the line",
350 movement, { .i = VIS_MOVE_LINE_BEGIN }
352 [VIS_ACTION_CURSOR_LINE_END] = {
353 "cursor-line-end",
354 "Move cursor to end of the line",
355 movement, { .i = VIS_MOVE_LINE_LASTCHAR }
357 [VIS_ACTION_CURSOR_SCREEN_LINE_UP] = {
358 "cursor-sceenline-up",
359 "Move cursor screen/display line upwards",
360 movement, { .i = VIS_MOVE_SCREEN_LINE_UP }
362 [VIS_ACTION_CURSOR_SCREEN_LINE_DOWN] = {
363 "cursor-screenline-down",
364 "Move cursor screen/display line downwards",
365 movement, { .i = VIS_MOVE_SCREEN_LINE_DOWN }
367 [VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN] = {
368 "cursor-screenline-begin",
369 "Move cursor to beginning of screen/display line",
370 movement, { .i = VIS_MOVE_SCREEN_LINE_BEGIN }
372 [VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE] = {
373 "cursor-screenline-middle",
374 "Move cursor to middle of screen/display line",
375 movement, { .i = VIS_MOVE_SCREEN_LINE_MIDDLE }
377 [VIS_ACTION_CURSOR_SCREEN_LINE_END] = {
378 "cursor-screenline-end",
379 "Move cursor to end of screen/display line",
380 movement, { .i = VIS_MOVE_SCREEN_LINE_END }
382 [VIS_ACTION_CURSOR_BRACKET_MATCH] = {
383 "cursor-match-bracket",
384 "Match corresponding symbol if cursor is on a bracket character",
385 movement, { .i = VIS_MOVE_BRACKET_MATCH }
387 [VIS_ACTION_CURSOR_PARAGRAPH_PREV] = {
388 "cursor-paragraph-prev",
389 "Move cursor paragraph backward",
390 movement, { .i = VIS_MOVE_PARAGRAPH_PREV }
392 [VIS_ACTION_CURSOR_PARAGRAPH_NEXT] = {
393 "cursor-paragraph-next",
394 "Move cursor paragraph forward",
395 movement, { .i = VIS_MOVE_PARAGRAPH_NEXT }
397 [VIS_ACTION_CURSOR_SENTENCE_PREV] = {
398 "cursor-sentence-prev",
399 "Move cursor sentence backward",
400 movement, { .i = VIS_MOVE_SENTENCE_PREV }
402 [VIS_ACTION_CURSOR_SENTENCE_NEXT] = {
403 "cursor-sentence-next",
404 "Move cursor sentence forward",
405 movement, { .i = VIS_MOVE_SENTENCE_NEXT }
407 [VIS_ACTION_CURSOR_FUNCTION_START_PREV] = {
408 "cursor-function-start-prev",
409 "Move cursor backwards to start of function",
410 movement, { .i = VIS_MOVE_FUNCTION_START_PREV }
412 [VIS_ACTION_CURSOR_FUNCTION_START_NEXT] = {
413 "cursor-function-start-next",
414 "Move cursor forwards to start of function",
415 movement, { .i = VIS_MOVE_FUNCTION_START_NEXT }
417 [VIS_ACTION_CURSOR_FUNCTION_END_PREV] = {
418 "cursor-function-end-prev",
419 "Move cursor backwards to end of function",
420 movement, { .i = VIS_MOVE_FUNCTION_END_PREV }
422 [VIS_ACTION_CURSOR_FUNCTION_END_NEXT] = {
423 "cursor-function-end-next",
424 "Move cursor forwards to end of function",
425 movement, { .i = VIS_MOVE_FUNCTION_END_NEXT }
427 [VIS_ACTION_CURSOR_COLUMN] = {
428 "cursor-column",
429 "Move cursor to given column of current line",
430 movement, { .i = VIS_MOVE_COLUMN }
432 [VIS_ACTION_CURSOR_LINE_FIRST] = {
433 "cursor-line-first",
434 "Move cursor to given line (defaults to first)",
435 gotoline, { .i = -1 }
437 [VIS_ACTION_CURSOR_LINE_LAST] = {
438 "cursor-line-last",
439 "Move cursor to given line (defaults to last)",
440 gotoline, { .i = +1 }
442 [VIS_ACTION_CURSOR_WINDOW_LINE_TOP] = {
443 "cursor-window-line-top",
444 "Move cursor to top line of the window",
445 movement, { .i = VIS_MOVE_WINDOW_LINE_TOP }
447 [VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE] = {
448 "cursor-window-line-middle",
449 "Move cursor to middle line of the window",
450 movement, { .i = VIS_MOVE_WINDOW_LINE_MIDDLE }
452 [VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM] = {
453 "cursor-window-line-bottom",
454 "Move cursor to bottom line of the window",
455 movement, { .i = VIS_MOVE_WINDOW_LINE_BOTTOM }
457 [VIS_ACTION_CURSOR_SEARCH_NEXT] = {
458 "cursor-search-forward",
459 "Move cursor to bottom line of the window",
460 movement, { .i = VIS_MOVE_SEARCH_NEXT }
462 [VIS_ACTION_CURSOR_SEARCH_PREV] = {
463 "cursor-search-backward",
464 "Move cursor to bottom line of the window",
465 movement, { .i = VIS_MOVE_SEARCH_PREV }
467 [VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD] = {
468 "cursor-search-word-forward",
469 "Move cursor to next occurence of the word under cursor",
470 movement, { .i = VIS_MOVE_SEARCH_WORD_FORWARD }
472 [VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD] = {
473 "cursor-search-word-backward",
474 "Move cursor to previous occurence of the word under cursor",
475 movement, { .i = VIS_MOVE_SEARCH_WORD_BACKWARD }
477 [VIS_ACTION_WINDOW_PAGE_UP] = {
478 "window-page-up",
479 "Scroll window pages backwards (upwards)",
480 wscroll, { .i = -PAGE }
482 [VIS_ACTION_WINDOW_HALFPAGE_UP] = {
483 "window-halfpage-up",
484 "Scroll window half pages backwards (upwards)",
485 wscroll, { .i = -PAGE_HALF }
487 [VIS_ACTION_WINDOW_PAGE_DOWN] = {
488 "window-page-down",
489 "Scroll window pages forwards (downwards)",
490 wscroll, { .i = +PAGE }
492 [VIS_ACTION_WINDOW_HALFPAGE_DOWN] = {
493 "window-halfpage-down",
494 "Scroll window half pages forwards (downwards)",
495 wscroll, { .i = +PAGE_HALF }
497 [VIS_ACTION_MODE_NORMAL] = {
498 "vis-mode-normal",
499 "Enter normal mode",
500 switchmode, { .i = VIS_MODE_NORMAL }
502 [VIS_ACTION_MODE_VISUAL] = {
503 "vis-mode-visual-charwise",
504 "Enter characterwise visual mode",
505 switchmode, { .i = VIS_MODE_VISUAL }
507 [VIS_ACTION_MODE_VISUAL_LINE] = {
508 "vis-mode-visual-linewise",
509 "Enter linewise visual mode",
510 switchmode, { .i = VIS_MODE_VISUAL_LINE }
512 [VIS_ACTION_MODE_INSERT] = {
513 "vis-mode-insert",
514 "Enter insert mode",
515 switchmode, { .i = VIS_MODE_INSERT }
517 [VIS_ACTION_MODE_REPLACE] = {
518 "vis-mode-replace",
519 "Enter replace mode",
520 switchmode, { .i = VIS_MODE_REPLACE }
522 [VIS_ACTION_MODE_OPERATOR_PENDING] = {
523 "vis-mode-operator-pending",
524 "Enter to operator pending mode",
525 switchmode, { .i = VIS_MODE_OPERATOR_PENDING }
527 [VIS_ACTION_DELETE_CHAR_PREV] = {
528 "delete-char-prev",
529 "Delete the previous character",
530 delete, { .i = VIS_MOVE_CHAR_PREV }
532 [VIS_ACTION_DELETE_CHAR_NEXT] = {
533 "delete-char-next",
534 "Delete the next character",
535 delete, { .i = VIS_MOVE_CHAR_NEXT }
537 [VIS_ACTION_DELETE_LINE_BEGIN] = {
538 "delete-line-begin",
539 "Delete until the start of the current line",
540 delete, { .i = VIS_MOVE_LINE_BEGIN }
542 [VIS_ACTION_DELETE_WORD_PREV] = {
543 "delete-word-prev",
544 "Delete the previous WORD",
545 delete, { .i = VIS_MOVE_LONGWORD_START_PREV }
547 [VIS_ACTION_JUMPLIST_PREV] = {
548 "jumplist-prev",
549 "Go to older cursor position in jump list",
550 movement, { .i = VIS_MOVE_JUMPLIST_PREV }
552 [VIS_ACTION_JUMPLIST_NEXT] = {
553 "jumplist-next",
554 "Go to newer cursor position in jump list",
555 movement, { .i = VIS_MOVE_JUMPLIST_NEXT }
557 [VIS_ACTION_CHANGELIST_PREV] = {
558 "changelist-prev",
559 "Go to older cursor position in change list",
560 movement, { .i = VIS_MOVE_CHANGELIST_PREV }
562 [VIS_ACTION_CHANGELIST_NEXT] = {
563 "changelist-next",
564 "Go to newer cursor position in change list",
565 movement, { .i = VIS_MOVE_CHANGELIST_NEXT }
567 [VIS_ACTION_UNDO] = {
568 "editor-undo",
569 "Undo last change",
570 undo,
572 [VIS_ACTION_REDO] = {
573 "editor-redo",
574 "Redo last change",
575 redo,
577 [VIS_ACTION_EARLIER] = {
578 "editor-earlier",
579 "Goto older text state",
580 earlier,
582 [VIS_ACTION_LATER] = {
583 "editor-later",
584 "Goto newer text state",
585 later,
587 [VIS_ACTION_MACRO_RECORD] = {
588 "macro-record",
589 "Record macro into given register",
590 macro_record,
592 [VIS_ACTION_MACRO_REPLAY] = {
593 "macro-replay",
594 "Replay macro, execute the content of the given register",
595 macro_replay,
597 [VIS_ACTION_MARK_SET] = {
598 "mark-set",
599 "Set given mark at current cursor position",
600 mark_set,
602 [VIS_ACTION_MARK_GOTO] = {
603 "mark-goto",
604 "Goto the position of the given mark",
605 mark_motion, { .i = VIS_MOVE_MARK }
607 [VIS_ACTION_MARK_GOTO_LINE] = {
608 "mark-goto-line",
609 "Goto first non-blank character of the line containing the given mark",
610 mark_motion, { .i = VIS_MOVE_MARK_LINE }
612 [VIS_ACTION_REDRAW] = {
613 "editor-redraw",
614 "Redraw current editor content",
615 call, { .f = vis_redraw }
617 [VIS_ACTION_REPLACE_CHAR] = {
618 "replace-char",
619 "Replace the character under the cursor",
620 replace,
622 [VIS_ACTION_TOTILL_REPEAT] = {
623 "totill-repeat",
624 "Repeat latest to/till motion",
625 movement, { .i = VIS_MOVE_TOTILL_REPEAT }
627 [VIS_ACTION_TOTILL_REVERSE] = {
628 "totill-reverse",
629 "Repeat latest to/till motion but in opposite direction",
630 movement, { .i = VIS_MOVE_TOTILL_REVERSE }
632 [VIS_ACTION_PROMPT_SEARCH_FORWARD] = {
633 "search-forward",
634 "Search forward",
635 prompt_show, { .s = "/" }
637 [VIS_ACTION_PROMPT_SEARCH_BACKWARD] = {
638 "search-backward",
639 "Search backward",
640 prompt_show, { .s = "?" }
642 [VIS_ACTION_TILL_LEFT] = {
643 "till-left",
644 "Till after the occurrence of character to the left",
645 movement_key, { .i = VIS_MOVE_LEFT_TILL }
647 [VIS_ACTION_TILL_RIGHT] = {
648 "till-right",
649 "Till before the occurrence of character to the right",
650 movement_key, { .i = VIS_MOVE_RIGHT_TILL }
652 [VIS_ACTION_TO_LEFT] = {
653 "to-left",
654 "To the first occurrence of character to the left",
655 movement_key, { .i = VIS_MOVE_LEFT_TO }
657 [VIS_ACTION_TO_RIGHT] = {
658 "to-right",
659 "To the first occurrence of character to the right",
660 movement_key, { .i = VIS_MOVE_RIGHT_TO }
662 [VIS_ACTION_REGISTER] = {
663 "register",
664 "Use given register for next operator",
665 reg,
667 [VIS_ACTION_OPERATOR_CHANGE] = {
668 "vis-operator-change",
669 "Change operator",
670 operator, { .i = VIS_OP_CHANGE }
672 [VIS_ACTION_OPERATOR_DELETE] = {
673 "vis-operator-delete",
674 "Delete operator",
675 operator, { .i = VIS_OP_DELETE }
677 [VIS_ACTION_OPERATOR_YANK] = {
678 "vis-operator-yank",
679 "Yank operator",
680 operator, { .i = VIS_OP_YANK }
682 [VIS_ACTION_OPERATOR_SHIFT_LEFT] = {
683 "vis-operator-shift-left",
684 "Shift left operator",
685 operator, { .i = VIS_OP_SHIFT_LEFT }
687 [VIS_ACTION_OPERATOR_SHIFT_RIGHT] = {
688 "vis-operator-shift-right",
689 "Shift right operator",
690 operator, { .i = VIS_OP_SHIFT_RIGHT }
692 [VIS_ACTION_OPERATOR_CASE_LOWER] = {
693 "vis-operator-case-lower",
694 "Lowercase operator",
695 operator, { .i = VIS_OP_CASE_LOWER }
697 [VIS_ACTION_OPERATOR_CASE_UPPER] = {
698 "vis-operator-case-upper",
699 "Uppercase operator",
700 operator, { .i = VIS_OP_CASE_UPPER }
702 [VIS_ACTION_OPERATOR_CASE_SWAP] = {
703 "vis-operator-case-swap",
704 "Swap case operator",
705 operator, { .i = VIS_OP_CASE_SWAP }
707 [VIS_ACTION_OPERATOR_FILTER] = {
708 "vis-operator-filter",
709 "Filter operator",
710 operator_filter,
712 [VIS_ACTION_OPERATOR_FILTER_FMT] = {
713 "vis-operator-filter-format",
714 "Formating operator, filter range through fmt(1)",
715 operator_filter, { .s = "'<,'>!fmt" }
717 [VIS_ACTION_COUNT] = {
718 "vis-count",
719 "Count specifier",
720 count,
722 [VIS_ACTION_INSERT_NEWLINE] = {
723 "insert-newline",
724 "Insert a line break (depending on file type)",
725 call, { .f = vis_insert_nl }
727 [VIS_ACTION_INSERT_TAB] = {
728 "insert-tab",
729 "Insert a tab (might be converted to spaces)",
730 call, { .f = vis_insert_tab }
732 [VIS_ACTION_INSERT_VERBATIM] = {
733 "insert-verbatim",
734 "Insert Unicode character based on code point",
735 insert_verbatim,
737 [VIS_ACTION_INSERT_REGISTER] = {
738 "insert-register",
739 "Insert specified register content",
740 insert_register,
742 [VIS_ACTION_WINDOW_NEXT] = {
743 "window-next",
744 "Focus next window",
745 call, { .f = vis_window_next }
747 [VIS_ACTION_WINDOW_PREV] = {
748 "window-prev",
749 "Focus previous window",
750 call, { .f = vis_window_prev }
752 [VIS_ACTION_APPEND_CHAR_NEXT] = {
753 "append-char-next",
754 "Append text after the cursor",
755 insertmode, { .i = VIS_MOVE_CHAR_NEXT }
757 [VIS_ACTION_APPEND_LINE_END] = {
758 "append-line-end",
759 "Append text after the end of the line",
760 insertmode, { .i = VIS_MOVE_LINE_END },
762 [VIS_ACTION_INSERT_LINE_START] = {
763 "insert-line-start",
764 "Insert text before the first non-blank in the line",
765 insertmode, { .i = VIS_MOVE_LINE_START },
767 [VIS_ACTION_OPEN_LINE_ABOVE] = {
768 "open-line-above",
769 "Begin a new line above the cursor",
770 openline, { .i = -1 }
772 [VIS_ACTION_OPEN_LINE_BELOW] = {
773 "open-line-below",
774 "Begin a new line below the cursor",
775 openline, { .i = +1 }
777 [VIS_ACTION_JOIN_LINE_BELOW] = {
778 "join-line-below",
779 "Join line(s)",
780 join, { .i = VIS_MOVE_LINE_NEXT },
782 [VIS_ACTION_JOIN_LINES] = {
783 "join-lines",
784 "Join selected lines",
785 operator, { .i = VIS_OP_JOIN }
787 [VIS_ACTION_PROMPT_SHOW] = {
788 "prompt-show",
789 "Show editor command line prompt",
790 prompt_show, { .s = ":" }
792 [VIS_ACTION_PROMPT_SHOW_VISUAL] = {
793 "prompt-show-visual",
794 "Show editor command line prompt in visual mode",
795 prompt_show, { .s = "'<,'>" }
797 [VIS_ACTION_REPEAT] = {
798 "editor-repeat",
799 "Repeat latest editor command",
800 repeat
802 [VIS_ACTION_SELECTION_FLIP] = {
803 "selection-flip",
804 "Flip selection, move cursor to other end",
805 selection_end,
807 [VIS_ACTION_SELECTION_RESTORE] = {
808 "selection-restore",
809 "Restore last selection",
810 selection_restore,
812 [VIS_ACTION_WINDOW_REDRAW_TOP] = {
813 "window-redraw-top",
814 "Redraw cursor line at the top of the window",
815 window, { .w = view_redraw_top }
817 [VIS_ACTION_WINDOW_REDRAW_CENTER] = {
818 "window-redraw-center",
819 "Redraw cursor line at the center of the window",
820 window, { .w = view_redraw_center }
822 [VIS_ACTION_WINDOW_REDRAW_BOTTOM] = {
823 "window-redraw-bottom",
824 "Redraw cursor line at the bottom of the window",
825 window, { .w = view_redraw_bottom }
827 [VIS_ACTION_WINDOW_SLIDE_UP] = {
828 "window-slide-up",
829 "Slide window content upwards",
830 wslide, { .i = -1 }
832 [VIS_ACTION_WINDOW_SLIDE_DOWN] = {
833 "window-slide-down",
834 "Slide window content downwards",
835 wslide, { .i = +1 }
837 [VIS_ACTION_PUT_AFTER] = {
838 "put-after",
839 "Put text after the cursor",
840 operator, { .i = VIS_OP_PUT_AFTER }
842 [VIS_ACTION_PUT_BEFORE] = {
843 "put-before",
844 "Put text before the cursor",
845 operator, { .i = VIS_OP_PUT_BEFORE }
847 [VIS_ACTION_PUT_AFTER_END] = {
848 "put-after-end",
849 "Put text after the cursor, place cursor after new text",
850 operator, { .i = VIS_OP_PUT_AFTER_END }
852 [VIS_ACTION_PUT_BEFORE_END] = {
853 "put-before-end",
854 "Put text before the cursor, place cursor after new text",
855 operator, { .i = VIS_OP_PUT_BEFORE_END }
857 [VIS_ACTION_CURSOR_SELECT_WORD] = {
858 "cursors-select-word",
859 "Select word under cursor",
860 cursors_select,
862 [VIS_ACTION_CURSORS_NEW_LINE_ABOVE] = {
863 "cursors-new-lines-above",
864 "Create a new cursor on the line above",
865 cursors_new, { .i = -1 }
867 [VIS_ACTION_CURSORS_NEW_LINE_BELOW] = {
868 "cursor-new-lines-below",
869 "Create a new cursor on the line below",
870 cursors_new, { .i = +1 }
872 [VIS_ACTION_CURSORS_NEW_LINES_BEGIN] = {
873 "cursors-new-lines-begin",
874 "Create a new cursor at the start of every line covered by selection",
875 operator, { .i = VIS_OP_CURSOR_SOL }
877 [VIS_ACTION_CURSORS_NEW_LINES_END] = {
878 "cursors-new-lines-end",
879 "Create a new cursor at the end of every line covered by selection",
880 operator, { .i = VIS_OP_CURSOR_EOL }
882 [VIS_ACTION_CURSORS_NEW_MATCH_NEXT] = {
883 "cursors-new-match-next",
884 "Select the next region matching the current selection",
885 cursors_select_next
887 [VIS_ACTION_CURSORS_NEW_MATCH_SKIP] = {
888 "cursors-new-match-skip",
889 "Clear current selection, but select next match",
890 cursors_select_skip,
892 [VIS_ACTION_CURSORS_ALIGN] = {
893 "cursors-align",
894 "Try to align all cursors on the same column",
895 cursors_align,
897 [VIS_ACTION_CURSORS_REMOVE_ALL] = {
898 "cursors-remove-all",
899 "Remove all but the primary cursor",
900 cursors_clear,
902 [VIS_ACTION_CURSORS_REMOVE_LAST] = {
903 "cursors-remove-last",
904 "Remove least recently created cursor",
905 cursors_remove,
907 [VIS_ACTION_TEXT_OBJECT_WORD_OUTER] = {
908 "text-object-word-outer",
909 "A word leading and trailing whitespace included",
910 textobj, { .i = VIS_TEXTOBJECT_OUTER_WORD }
912 [VIS_ACTION_TEXT_OBJECT_WORD_INNER] = {
913 "text-object-word-inner",
914 "A word leading and trailing whitespace excluded",
915 textobj, { .i = VIS_TEXTOBJECT_INNER_WORD }
917 [VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER] = {
918 "text-object-longword-outer",
919 "A WORD leading and trailing whitespace included",
920 textobj, { .i = VIS_TEXTOBJECT_OUTER_LONGWORD }
922 [VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER] = {
923 "text-object-longword-inner",
924 "A WORD leading and trailing whitespace excluded",
925 textobj, { .i = VIS_TEXTOBJECT_INNER_LONGWORD }
927 [VIS_ACTION_TEXT_OBJECT_SENTENCE] = {
928 "text-object-sentence",
929 "A sentence",
930 textobj, { .i = VIS_TEXTOBJECT_SENTENCE }
932 [VIS_ACTION_TEXT_OBJECT_PARAGRAPH] = {
933 "text-object-paragraph",
934 "A paragraph",
935 textobj, { .i = VIS_TEXTOBJECT_PARAGRAPH }
937 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER] = {
938 "text-object-square-bracket-outer",
939 "[] block (outer variant)",
940 textobj, { .i = VIS_TEXTOBJECT_OUTER_SQUARE_BRACKET }
942 [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER] = {
943 "text-object-square-bracket-inner",
944 "[] block (inner variant)",
945 textobj, { .i = VIS_TEXTOBJECT_INNER_SQUARE_BRACKET }
947 [VIS_ACTION_TEXT_OBJECT_PARANTHESE_OUTER] = {
948 "text-object-parentheses-outer",
949 "() block (outer variant)",
950 textobj, { .i = VIS_TEXTOBJECT_OUTER_PARANTHESE }
952 [VIS_ACTION_TEXT_OBJECT_PARANTHESE_INNER] = {
953 "text-object-parentheses-inner",
954 "() block (inner variant)",
955 textobj, { .i = VIS_TEXTOBJECT_INNER_PARANTHESE }
957 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER] = {
958 "text-object-angle-bracket-outer",
959 "<> block (outer variant)",
960 textobj, { .i = VIS_TEXTOBJECT_OUTER_ANGLE_BRACKET }
962 [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER] = {
963 "text-object-angle-bracket-inner",
964 "<> block (inner variant)",
965 textobj, { .i = VIS_TEXTOBJECT_INNER_ANGLE_BRACKET }
967 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER] = {
968 "text-object-curly-bracket-outer",
969 "{} block (outer variant)",
970 textobj, { .i = VIS_TEXTOBJECT_OUTER_CURLY_BRACKET }
972 [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER] = {
973 "text-object-curly-bracket-inner",
974 "{} block (inner variant)",
975 textobj, { .i = VIS_TEXTOBJECT_INNER_CURLY_BRACKET }
977 [VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER] = {
978 "text-object-quote-outer",
979 "A quoted string, including the quotation marks",
980 textobj, { .i = VIS_TEXTOBJECT_OUTER_QUOTE }
982 [VIS_ACTION_TEXT_OBJECT_QUOTE_INNER] = {
983 "text-object-quote-inner",
984 "A quoted string, excluding the quotation marks",
985 textobj, { .i = VIS_TEXTOBJECT_INNER_QUOTE }
987 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER] = {
988 "text-object-single-quote-outer",
989 "A single quoted string, including the quotation marks",
990 textobj, { .i = VIS_TEXTOBJECT_OUTER_SINGLE_QUOTE }
992 [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER] = {
993 "text-object-single-quote-inner",
994 "A single quoted string, excluding the quotation marks",
995 textobj, { .i = VIS_TEXTOBJECT_INNER_SINGLE_QUOTE }
997 [VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER] = {
998 "text-object-backtick-outer",
999 "A backtick delimited string (outer variant)",
1000 textobj, { .i = VIS_TEXTOBJECT_OUTER_BACKTICK }
1002 [VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER] = {
1003 "text-object-backtick-inner",
1004 "A backtick delimited string (inner variant)",
1005 textobj, { .i = VIS_TEXTOBJECT_INNER_BACKTICK }
1007 [VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER] = {
1008 "text-object-entire-outer",
1009 "The whole text content",
1010 textobj, { .i = VIS_TEXTOBJECT_OUTER_ENTIRE }
1012 [VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER] = {
1013 "text-object-entire-inner",
1014 "The whole text content, except for leading and trailing empty lines",
1015 textobj, { .i = VIS_TEXTOBJECT_INNER_ENTIRE }
1017 [VIS_ACTION_TEXT_OBJECT_FUNCTION_OUTER] = {
1018 "text-object-function-outer",
1019 "A whole C-like function",
1020 textobj, { .i = VIS_TEXTOBJECT_OUTER_FUNCTION }
1022 [VIS_ACTION_TEXT_OBJECT_FUNCTION_INNER] = {
1023 "text-object-function-inner",
1024 "A whole C-like function body",
1025 textobj, { .i = VIS_TEXTOBJECT_INNER_FUNCTION }
1027 [VIS_ACTION_TEXT_OBJECT_LINE_OUTER] = {
1028 "text-object-line-outer",
1029 "The whole line",
1030 textobj, { .i = VIS_TEXTOBJECT_OUTER_LINE }
1032 [VIS_ACTION_TEXT_OBJECT_LINE_INNER] = {
1033 "text-object-line-inner",
1034 "The whole line, excluding leading and trailing whitespace",
1035 textobj, { .i = VIS_TEXTOBJECT_INNER_LINE }
1037 [VIS_ACTION_MOTION_CHARWISE] = {
1038 "motion-charwise",
1039 "Force motion to be charwise",
1040 motiontype, { .i = VIS_MOTIONTYPE_CHARWISE }
1042 [VIS_ACTION_MOTION_LINEWISE] = {
1043 "motion-linewise",
1044 "Force motion to be linewise",
1045 motiontype, { .i = VIS_MOTIONTYPE_LINEWISE }
1047 [VIS_ACTION_UNICODE_INFO] = {
1048 "unicode-info",
1049 "Show Unicode codepoint(s) of character under cursor",
1050 unicode_info,
1052 [VIS_ACTION_NOP] = {
1053 "nop",
1054 "Ignore key, do nothing",
1055 nop,
1059 #include "config.h"
1061 /** key bindings functions */
1063 static const char *nop(Vis *vis, const char *keys, const Arg *arg) {
1064 return keys;
1067 static const char *key2macro(Vis *vis, const char *keys, enum VisMacro *macro) {
1068 *macro = VIS_MACRO_INVALID;
1069 if (keys[0] >= 'a' && keys[0] <= 'z')
1070 *macro = keys[0] - 'a';
1071 else if (keys[0] == '@')
1072 *macro = VIS_MACRO_LAST_RECORDED;
1073 else if (keys[0] == '\0')
1074 return NULL;
1075 return keys+1;
1078 static const char *macro_record(Vis *vis, const char *keys, const Arg *arg) {
1079 if (vis_macro_record_stop(vis))
1080 return keys;
1081 enum VisMacro macro;
1082 keys = key2macro(vis, keys, &macro);
1083 vis_macro_record(vis, macro);
1084 vis_draw(vis);
1085 return keys;
1088 static const char *macro_replay(Vis *vis, const char *keys, const Arg *arg) {
1089 enum VisMacro macro;
1090 keys = key2macro(vis, keys, &macro);
1091 vis_macro_replay(vis, macro);
1092 return keys;
1095 static const char *suspend(Vis *vis, const char *keys, const Arg *arg) {
1096 vis_suspend(vis);
1097 return keys;
1100 static const char *repeat(Vis *vis, const char *keys, const Arg *arg) {
1101 vis_repeat(vis);
1102 return keys;
1105 static const char *cursors_new(Vis *vis, const char *keys, const Arg *arg) {
1106 View *view = vis_view(vis);
1107 size_t pos = view_cursor_get(view);
1108 Cursor *cursor = view_cursors_new(view);
1109 if (cursor) {
1110 view_cursors_to(cursor, pos);
1111 if (arg->i > 0)
1112 view_line_down(cursor);
1113 else if (arg->i < 0)
1114 view_line_up(cursor);
1116 return keys;
1119 static const char *cursors_align(Vis *vis, const char *keys, const Arg *arg) {
1120 View *view = vis_view(vis);
1121 Text *txt = vis_text(vis);
1122 int mincol = INT_MAX;
1123 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
1124 int col = view_cursors_cell_get(c);
1125 if (col >= 0 && col < mincol)
1126 mincol = col;
1128 for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
1129 if (view_cursors_cell_set(c, mincol) == -1) {
1130 size_t pos = view_cursors_pos(c);
1131 size_t col = text_line_char_set(txt, pos, mincol);
1132 view_cursors_to(c, col);
1135 return keys;
1138 static const char *cursors_clear(Vis *vis, const char *keys, const Arg *arg) {
1139 View *view = vis_view(vis);
1140 if (view_cursors_count(view) > 1)
1141 view_cursors_clear(view);
1142 else
1143 view_cursors_selection_clear(view_cursor(view));
1144 return keys;
1147 static const char *cursors_select(Vis *vis, const char *keys, const Arg *arg) {
1148 Text *txt = vis_text(vis);
1149 View *view = vis_view(vis);
1150 for (Cursor *cursor = view_cursors(view); cursor; cursor = view_cursors_next(cursor)) {
1151 Filerange sel = view_cursors_selection_get(cursor);
1152 Filerange word = text_object_word(txt, view_cursors_pos(cursor));
1153 if (!text_range_valid(&sel) && text_range_valid(&word)) {
1154 view_cursors_selection_set(cursor, &word);
1155 view_cursors_to(cursor, text_char_prev(txt, word.end));
1158 vis_mode_switch(vis, VIS_MODE_VISUAL);
1159 return keys;
1162 static const char *cursors_select_next(Vis *vis, const char *keys, const Arg *arg) {
1163 Text *txt = vis_text(vis);
1164 View *view = vis_view(vis);
1165 Cursor *cursor = view_cursor(view);
1166 Filerange sel = view_cursors_selection_get(cursor);
1167 if (!text_range_valid(&sel))
1168 return keys;
1170 size_t len = text_range_size(&sel);
1171 char *buf = malloc(len+1);
1172 if (!buf)
1173 return keys;
1174 len = text_bytes_get(txt, sel.start, len, buf);
1175 buf[len] = '\0';
1176 Filerange word = text_object_word_find_next(txt, sel.end, buf);
1177 free(buf);
1179 if (text_range_valid(&word)) {
1180 cursor = view_cursors_new(view);
1181 if (!cursor)
1182 return keys;
1183 view_cursors_selection_set(cursor, &word);
1184 view_cursors_to(cursor, text_char_prev(txt, word.end));
1186 return keys;
1189 static const char *cursors_select_skip(Vis *vis, const char *keys, const Arg *arg) {
1190 View *view = vis_view(vis);
1191 Cursor *cursor = view_cursor(view);
1192 keys = cursors_select_next(vis, keys, arg);
1193 if (cursor != view_cursor(view))
1194 view_cursors_dispose(cursor);
1195 return keys;
1198 static const char *cursors_remove(Vis *vis, const char *keys, const Arg *arg) {
1199 View *view = vis_view(vis);
1200 view_cursors_dispose(view_cursor(view));
1201 view_cursor_to(view, view_cursor_get(view));
1202 return keys;
1205 static const char *replace(Vis *vis, const char *keys, const Arg *arg) {
1206 if (!keys[0])
1207 return NULL;
1208 const char *next = vis_keys_next(vis, keys);
1209 if (!next)
1210 return NULL;
1211 size_t len = next - keys;
1212 char key[len+1];
1213 memcpy(key, keys, len);
1214 key[len] = '\0';
1215 vis_operator(vis, VIS_OP_REPLACE);
1216 vis_motion(vis, VIS_MOVE_NOP);
1217 vis_keys_inject(vis, next, key);
1218 vis_keys_inject(vis, next+len, "<Escape>");
1219 return next;
1222 static const char *count(Vis *vis, const char *keys, const Arg *arg) {
1223 int digit = keys[-1] - '0';
1224 int count = vis_count_get(vis);
1225 if (0 <= digit && digit <= 9) {
1226 if (digit == 0 && count == 0)
1227 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
1228 vis_count_set(vis, count * 10 + digit);
1230 return keys;
1233 static const char *gotoline(Vis *vis, const char *keys, const Arg *arg) {
1234 if (vis_count_get(vis))
1235 vis_motion(vis, VIS_MOVE_LINE);
1236 else if (arg->i < 0)
1237 vis_motion(vis, VIS_MOVE_FILE_BEGIN);
1238 else
1239 vis_motion(vis, VIS_MOVE_FILE_END);
1240 return keys;
1243 static const char *motiontype(Vis *vis, const char *keys, const Arg *arg) {
1244 vis_motion_type(vis, arg->i);
1245 return keys;
1248 static const char *operator(Vis *vis, const char *keys, const Arg *arg) {
1249 vis_operator(vis, arg->i);
1250 return keys;
1253 static const char *operator_filter(Vis *vis, const char *keys, const Arg *arg) {
1254 vis_operator(vis, VIS_OP_FILTER, arg->s);
1255 return keys;
1258 static const char *movement_key(Vis *vis, const char *keys, const Arg *arg) {
1259 if (!keys[0])
1260 return NULL;
1261 char key[32];
1262 const char *next = vis_keys_next(vis, keys);
1263 strncpy(key, keys, next - keys + 1);
1264 key[sizeof(key)-1] = '\0';
1265 vis_motion(vis, arg->i, key);
1266 return next;
1269 static const char *movement(Vis *vis, const char *keys, const Arg *arg) {
1270 vis_motion(vis, arg->i);
1271 return keys;
1274 static const char *textobj(Vis *vis, const char *keys, const Arg *arg) {
1275 vis_textobject(vis, arg->i);
1276 return keys;
1279 static const char *selection_end(Vis *vis, const char *keys, const Arg *arg) {
1280 for (Cursor *c = view_cursors(vis_view(vis)); c; c = view_cursors_next(c))
1281 view_cursors_selection_swap(c);
1282 return keys;
1285 static const char *selection_restore(Vis *vis, const char *keys, const Arg *arg) {
1286 for (Cursor *c = view_cursors(vis_view(vis)); c; c = view_cursors_next(c))
1287 view_cursors_selection_restore(c);
1288 vis_mode_switch(vis, VIS_MODE_VISUAL);
1289 return keys;
1292 static const char *key2register(Vis *vis, const char *keys, enum VisRegister *reg) {
1293 *reg = VIS_REG_INVALID;
1294 if (!keys[0])
1295 return NULL;
1296 if (keys[0] >= 'a' && keys[0] <= 'z')
1297 *reg = keys[0] - 'a';
1298 return keys+1;
1301 static const char *reg(Vis *vis, const char *keys, const Arg *arg) {
1302 enum VisRegister reg;
1303 keys = key2register(vis, keys, &reg);
1304 vis_register_set(vis, reg);
1305 return keys;
1308 static const char *key2mark(Vis *vis, const char *keys, int *mark) {
1309 *mark = VIS_MARK_INVALID;
1310 if (!keys[0])
1311 return NULL;
1312 if (keys[0] >= 'a' && keys[0] <= 'z')
1313 *mark = keys[0] - 'a';
1314 else if (keys[0] == '<')
1315 *mark = VIS_MARK_SELECTION_START;
1316 else if (keys[0] == '>')
1317 *mark = VIS_MARK_SELECTION_END;
1318 return keys+1;
1321 static const char *mark_set(Vis *vis, const char *keys, const Arg *arg) {
1322 int mark;
1323 keys = key2mark(vis, keys, &mark);
1324 vis_mark_set(vis, mark, view_cursor_get(vis_view(vis)));
1325 return keys;
1328 static const char *mark_motion(Vis *vis, const char *keys, const Arg *arg) {
1329 int mark;
1330 keys = key2mark(vis, keys, &mark);
1331 vis_motion(vis, arg->i, mark);
1332 return keys;
1335 static const char *undo(Vis *vis, const char *keys, const Arg *arg) {
1336 size_t pos = text_undo(vis_text(vis));
1337 if (pos != EPOS) {
1338 View *view = vis_view(vis);
1339 if (view_cursors_count(view) == 1)
1340 view_cursor_to(view, pos);
1341 /* redraw all windows in case some display the same file */
1342 vis_draw(vis);
1344 return keys;
1347 static const char *redo(Vis *vis, const char *keys, const Arg *arg) {
1348 size_t pos = text_redo(vis_text(vis));
1349 if (pos != EPOS) {
1350 View *view = vis_view(vis);
1351 if (view_cursors_count(view) == 1)
1352 view_cursor_to(view, pos);
1353 /* redraw all windows in case some display the same file */
1354 vis_draw(vis);
1356 return keys;
1359 static const char *earlier(Vis *vis, const char *keys, const Arg *arg) {
1360 size_t pos = text_earlier(vis_text(vis), MAX(vis_count_get(vis), 1));
1361 if (pos != EPOS) {
1362 view_cursor_to(vis_view(vis), pos);
1363 /* redraw all windows in case some display the same file */
1364 vis_draw(vis);
1366 return keys;
1369 static const char *later(Vis *vis, const char *keys, const Arg *arg) {
1370 size_t pos = text_later(vis_text(vis), MAX(vis_count_get(vis), 1));
1371 if (pos != EPOS) {
1372 view_cursor_to(vis_view(vis), pos);
1373 /* redraw all windows in case some display the same file */
1374 vis_draw(vis);
1376 return keys;
1379 static const char *delete(Vis *vis, const char *keys, const Arg *arg) {
1380 vis_operator(vis, VIS_OP_DELETE);
1381 vis_motion(vis, arg->i);
1382 return keys;
1385 static const char *insert_register(Vis *vis, const char *keys, const Arg *arg) {
1386 enum VisRegister regid;
1387 keys = key2register(vis, keys, &regid);
1388 Register *reg = vis_register_get(vis, regid);
1389 if (reg) {
1390 int pos = view_cursor_get(vis_view(vis));
1391 vis_insert(vis, pos, reg->data, reg->len);
1392 view_cursor_to(vis_view(vis), pos + reg->len);
1394 return keys;
1397 static const char *prompt_show(Vis *vis, const char *keys, const Arg *arg) {
1398 vis_prompt_show(vis, arg->s);
1399 return keys;
1402 static const char *insert_verbatim(Vis *vis, const char *keys, const Arg *arg) {
1403 Rune rune = 0;
1404 char buf[4], type = keys[0];
1405 const char *data = NULL;
1406 int len = 0, count = 0, base = 0;
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 ('0' <= type && type <= '9') {
1429 rune = type - '0';
1430 count = 2;
1431 base = 10;
1433 break;
1436 if (base) {
1437 for (keys++; keys[0] && count > 0; keys++, count--) {
1438 int v = 0;
1439 if (base == 8 && '0' <= keys[0] && keys[0] <= '7') {
1440 v = keys[0] - '0';
1441 } else if ((base == 10 || base == 16) && '0' <= keys[0] && keys[0] <= '9') {
1442 v = keys[0] - '0';
1443 } else if (base == 16 && 'a' <= keys[0] && keys[0] <= 'f') {
1444 v = 10 + keys[0] - 'a';
1445 } else if (base == 16 && 'A' <= keys[0] && keys[0] <= 'F') {
1446 v = 10 + keys[0] - 'A';
1447 } else {
1448 count = 0;
1449 break;
1451 rune = rune * base + v;
1454 if (count > 0)
1455 return NULL;
1456 if (type == 'u' || type == 'U') {
1457 len = runetochar(buf, &rune);
1458 } else {
1459 buf[0] = rune;
1460 len = 1;
1463 data = buf;
1464 } else {
1465 const char *next = vis_keys_next(vis, keys);
1466 if (!next)
1467 return NULL;
1468 size_t keylen = next - keys;
1469 char key[keylen+1];
1470 memcpy(key, keys, keylen);
1471 key[keylen] = '\0';
1473 static const char *keysym[] = {
1474 "<Enter>", "\n",
1475 "<Tab>", "\t",
1476 "<Backspace>", "\b",
1477 "<Escape>", "\x1b",
1478 "<DEL>", "\x7f",
1479 NULL,
1482 for (const char **k = keysym; k[0]; k += 2) {
1483 if (strcmp(k[0], key) == 0) {
1484 data = k[1];
1485 len = strlen(data);
1486 keys = next;
1487 break;
1492 if (len > 0)
1493 vis_insert_key(vis, data, len);
1494 return keys;
1497 static const char *cmd(Vis *vis, const char *keys, const Arg *arg) {
1498 vis_cmd(vis, arg->s);
1499 return keys;
1502 static int argi2lines(Vis *vis, const Arg *arg) {
1503 switch (arg->i) {
1504 case -PAGE:
1505 case +PAGE:
1506 return view_height_get(vis_view(vis));
1507 case -PAGE_HALF:
1508 case +PAGE_HALF:
1509 return view_height_get(vis_view(vis))/2;
1510 default:
1511 if (vis_count_get(vis) > 0)
1512 return vis_count_get(vis);
1513 return arg->i < 0 ? -arg->i : arg->i;
1517 static const char *wscroll(Vis *vis, const char *keys, const Arg *arg) {
1518 if (arg->i >= 0)
1519 view_scroll_down(vis_view(vis), argi2lines(vis, arg));
1520 else
1521 view_scroll_up(vis_view(vis), argi2lines(vis, arg));
1522 return keys;
1525 static const char *wslide(Vis *vis, const char *keys, const Arg *arg) {
1526 if (arg->i >= 0)
1527 view_slide_down(vis_view(vis), argi2lines(vis, arg));
1528 else
1529 view_slide_up(vis_view(vis), argi2lines(vis, arg));
1530 return keys;
1533 static const char *call(Vis *vis, const char *keys, const Arg *arg) {
1534 arg->f(vis);
1535 return keys;
1538 static const char *window(Vis *vis, const char *keys, const Arg *arg) {
1539 arg->w(vis_view(vis));
1540 return keys;
1543 static const char *openline(Vis *vis, const char *keys, const Arg *arg) {
1544 vis_operator(vis, VIS_OP_INSERT);
1545 if (arg->i > 0) {
1546 vis_motion(vis, VIS_MOVE_LINE_END);
1547 vis_keys_inject(vis, keys, "<Enter>");
1548 } else {
1549 vis_motion(vis, VIS_MOVE_LINE_BEGIN);
1550 vis_keys_inject(vis, keys, "<Enter><Up>");
1552 return keys;
1555 static const char *join(Vis *vis, const char *keys, const Arg *arg) {
1556 int count = vis_count_get(vis);
1557 if (count)
1558 vis_count_set(vis, count-1);
1559 vis_operator(vis, VIS_OP_JOIN);
1560 vis_motion(vis, arg->i);
1561 return keys;
1564 static const char *switchmode(Vis *vis, const char *keys, const Arg *arg) {
1565 vis_mode_switch(vis, arg->i);
1566 return keys;
1569 static const char *insertmode(Vis *vis, const char *keys, const Arg *arg) {
1570 vis_operator(vis, VIS_OP_INSERT);
1571 vis_motion(vis, arg->i);
1572 return keys;
1575 static const char *unicode_info(Vis *vis, const char *keys, const Arg *arg) {
1576 View *view = vis_view(vis);
1577 Text *txt = vis_text(vis);
1578 size_t start = view_cursor_get(view);
1579 size_t end = text_char_next(txt, start);
1580 char data[end-start], *data_cur = data;
1581 text_bytes_get(txt, start, end - start, data);
1582 Iterator it = text_iterator_get(txt, start);
1583 char info[255] = "", *info_cur = info;
1584 for (size_t pos = start; it.pos < end; pos = it.pos) {
1585 text_iterator_codepoint_next(&it, NULL);
1586 size_t len = it.pos - pos;
1587 wchar_t wc = 0xFFFD;
1588 mbtowc(&wc, data_cur, len);
1589 int width = wcwidth(wc);
1590 info_cur += snprintf(info_cur, sizeof(info) - (info_cur - info) - 1,
1591 "<%s%.*s> U+%04x ", width == 0 ? " " : "", (int)len, data_cur, wc);
1592 data_cur += len;
1594 vis_info_show(vis, "%s", info);
1595 return keys;
1598 static Vis *vis;
1600 static void signal_handler(int signum, siginfo_t *siginfo, void *context) {
1601 vis_signal_handler(vis, signum, siginfo, context);
1604 int main(int argc, char *argv[]) {
1606 VisEvent event = {
1607 .vis_start = vis_lua_start,
1608 .vis_quit = vis_lua_quit,
1609 .file_open = vis_lua_file_open,
1610 .file_save = vis_lua_file_save,
1611 .file_close = vis_lua_file_close,
1612 .win_open = vis_lua_win_open,
1613 .win_close = vis_lua_win_close,
1616 vis = vis_new(ui_curses_new(), &event);
1617 if (!vis)
1618 return EXIT_FAILURE;
1620 for (int i = 0; i < LENGTH(vis_action); i++) {
1621 KeyAction *action = &vis_action[i];
1622 if (!vis_action_register(vis, action))
1623 vis_die(vis, "Could not register action: %s\n", action->name);
1626 for (int i = 0; i < LENGTH(default_bindings); i++) {
1627 for (const KeyBinding **binding = default_bindings[i]; binding && *binding; binding++) {
1628 for (const KeyBinding *kb = *binding; kb->key; kb++) {
1629 vis_mode_map(vis, i, kb->key, kb);
1634 /* install signal handlers etc. */
1635 struct sigaction sa;
1636 memset(&sa, 0, sizeof sa);
1637 sa.sa_flags = SA_SIGINFO;
1638 sa.sa_sigaction = signal_handler;
1639 if (sigaction(SIGBUS, &sa, NULL) || sigaction(SIGINT, &sa, NULL))
1640 vis_die(vis, "sigaction: %s", strerror(errno));
1642 sigset_t blockset;
1643 sigemptyset(&blockset);
1644 sigaddset(&blockset, SIGWINCH);
1645 sigprocmask(SIG_BLOCK, &blockset, NULL);
1646 signal(SIGPIPE, SIG_IGN);
1648 int status = vis_run(vis, argc, argv);
1649 vis_free(vis);
1650 return status;