aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config.def.h1199
-rw-r--r--main.c1643
-rw-r--r--ui-curses.c3
-rw-r--r--vis.c1095
-rw-r--r--vis.h68
5 files changed, 2030 insertions, 1978 deletions
diff --git a/config.def.h b/config.def.h
index 722dc07..f0c6508 100644
--- a/config.def.h
+++ b/config.def.h
@@ -1,963 +1,7 @@
-/** start by reading from the top of vis.c up until config.h is included */
-
-static Mode vis_modes[VIS_MODE_LAST];
-
-/* command recognized at the ':'-prompt. commands are found using a unique
- * prefix match. that is if a command should be available under an abbreviation
- * which is a prefix for another command it has to be added as an alias. the
- * long human readable name should always come first */
-static Command cmds[] = {
- /* command name / optional alias, function, options */
- { { "bdelete" }, cmd_bdelete, CMD_OPT_FORCE },
- { { "edit" }, cmd_edit, CMD_OPT_FORCE },
- { { "help" }, cmd_help, CMD_OPT_NONE },
- { { "new" }, cmd_new, CMD_OPT_NONE },
- { { "open" }, cmd_open, CMD_OPT_NONE },
- { { "qall" }, cmd_qall, CMD_OPT_FORCE },
- { { "quit", "q" }, cmd_quit, CMD_OPT_FORCE },
- { { "read", }, cmd_read, CMD_OPT_FORCE },
- { { "saveas" }, cmd_saveas, CMD_OPT_FORCE },
- { { "set", }, cmd_set, CMD_OPT_ARGS },
- { { "split" }, cmd_split, CMD_OPT_NONE },
- { { "substitute", "s" }, cmd_substitute, CMD_OPT_NONE },
- { { "vnew" }, cmd_vnew, CMD_OPT_NONE },
- { { "vsplit", }, cmd_vsplit, CMD_OPT_NONE },
- { { "wq", }, cmd_wq, CMD_OPT_FORCE },
- { { "write", "w" }, cmd_write, CMD_OPT_FORCE },
- { { "xit", }, cmd_xit, CMD_OPT_FORCE },
- { { "earlier" }, cmd_earlier_later, CMD_OPT_NONE },
- { { "later" }, cmd_earlier_later, CMD_OPT_NONE },
- { { "!", }, cmd_filter, CMD_OPT_NONE },
- { /* array terminator */ },
-};
-
+/** configure your desired default key bindings */
#define ALIAS(name) .alias = name,
#define ACTION(id) .action = &vis_action[VIS_ACTION_##id],
-enum {
- VIS_ACTION_EDITOR_SUSPEND,
- VIS_ACTION_CURSOR_CHAR_PREV,
- VIS_ACTION_CURSOR_CHAR_NEXT,
- VIS_ACTION_CURSOR_WORD_START_PREV,
- VIS_ACTION_CURSOR_WORD_START_NEXT,
- VIS_ACTION_CURSOR_WORD_END_PREV,
- VIS_ACTION_CURSOR_WORD_END_NEXT,
- VIS_ACTION_CURSOR_LONGWORD_START_PREV,
- VIS_ACTION_CURSOR_LONGWORD_START_NEXT,
- VIS_ACTION_CURSOR_LONGWORD_END_PREV,
- VIS_ACTION_CURSOR_LONGWORD_END_NEXT,
- VIS_ACTION_CURSOR_LINE_UP,
- VIS_ACTION_CURSOR_LINE_DOWN,
- VIS_ACTION_CURSOR_LINE_START,
- VIS_ACTION_CURSOR_LINE_FINISH,
- VIS_ACTION_CURSOR_LINE_BEGIN,
- VIS_ACTION_CURSOR_LINE_END,
- VIS_ACTION_CURSOR_SCREEN_LINE_UP,
- VIS_ACTION_CURSOR_SCREEN_LINE_DOWN,
- VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN,
- VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE,
- VIS_ACTION_CURSOR_SCREEN_LINE_END,
- VIS_ACTION_CURSOR_BRACKET_MATCH,
- VIS_ACTION_CURSOR_PARAGRAPH_PREV,
- VIS_ACTION_CURSOR_PARAGRAPH_NEXT,
- VIS_ACTION_CURSOR_SENTENCE_PREV,
- VIS_ACTION_CURSOR_SENTENCE_NEXT,
- VIS_ACTION_CURSOR_FUNCTION_START_PREV,
- VIS_ACTION_CURSOR_FUNCTION_END_PREV,
- VIS_ACTION_CURSOR_FUNCTION_START_NEXT,
- VIS_ACTION_CURSOR_FUNCTION_END_NEXT,
- VIS_ACTION_CURSOR_COLUMN,
- VIS_ACTION_CURSOR_LINE_FIRST,
- VIS_ACTION_CURSOR_LINE_LAST,
- VIS_ACTION_CURSOR_WINDOW_LINE_TOP,
- VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE,
- VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM,
- VIS_ACTION_CURSOR_SEARCH_FORWARD,
- VIS_ACTION_CURSOR_SEARCH_BACKWARD,
- VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD,
- VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD,
- VIS_ACTION_WINDOW_PAGE_UP,
- VIS_ACTION_WINDOW_PAGE_DOWN,
- VIS_ACTION_WINDOW_HALFPAGE_UP,
- VIS_ACTION_WINDOW_HALFPAGE_DOWN,
- VIS_ACTION_MODE_NORMAL,
- VIS_ACTION_MODE_VISUAL,
- VIS_ACTION_MODE_VISUAL_LINE,
- VIS_ACTION_MODE_INSERT,
- VIS_ACTION_MODE_REPLACE,
- VIS_ACTION_MODE_OPERATOR_PENDING,
- VIS_ACTION_DELETE_CHAR_PREV,
- VIS_ACTION_DELETE_CHAR_NEXT,
- VIS_ACTION_DELETE_LINE_BEGIN,
- VIS_ACTION_DELETE_WORD_PREV,
- VIS_ACTION_JUMPLIST_PREV,
- VIS_ACTION_JUMPLIST_NEXT,
- VIS_ACTION_CHANGELIST_PREV,
- VIS_ACTION_CHANGELIST_NEXT,
- VIS_ACTION_UNDO,
- VIS_ACTION_REDO,
- VIS_ACTION_EARLIER,
- VIS_ACTION_LATER,
- VIS_ACTION_MACRO_RECORD,
- VIS_ACTION_MACRO_REPLAY,
- VIS_ACTION_MARK_SET,
- VIS_ACTION_MARK_GOTO,
- VIS_ACTION_MARK_GOTO_LINE,
- VIS_ACTION_REDRAW,
- VIS_ACTION_REPLACE_CHAR,
- VIS_ACTION_TOTILL_REPEAT,
- VIS_ACTION_TOTILL_REVERSE,
- VIS_ACTION_SEARCH_FORWARD,
- VIS_ACTION_SEARCH_BACKWARD,
- VIS_ACTION_TILL_LEFT,
- VIS_ACTION_TILL_RIGHT,
- VIS_ACTION_TO_LEFT,
- VIS_ACTION_TO_RIGHT,
- VIS_ACTION_REGISTER,
- VIS_ACTION_OPERATOR_CHANGE,
- VIS_ACTION_OPERATOR_DELETE,
- VIS_ACTION_OPERATOR_YANK,
- VIS_ACTION_OPERATOR_SHIFT_LEFT,
- VIS_ACTION_OPERATOR_SHIFT_RIGHT,
- VIS_ACTION_OPERATOR_CASE_LOWER,
- VIS_ACTION_OPERATOR_CASE_UPPER,
- VIS_ACTION_OPERATOR_CASE_SWAP,
- VIS_ACTION_COUNT,
- VIS_ACTION_INSERT_NEWLINE,
- VIS_ACTION_INSERT_TAB,
- VIS_ACTION_INSERT_VERBATIM,
- VIS_ACTION_INSERT_REGISTER,
- VIS_ACTION_WINDOW_NEXT,
- VIS_ACTION_WINDOW_PREV,
- VIS_ACTION_OPEN_LINE_ABOVE,
- VIS_ACTION_OPEN_LINE_BELOW,
- VIS_ACTION_JOIN_LINE_BELOW,
- VIS_ACTION_JOIN_LINES,
- VIS_ACTION_PROMPT_SHOW,
- VIS_ACTION_PROMPT_BACKSPACE,
- VIS_ACTION_PROMPT_ENTER,
- VIS_ACTION_PROMPT_SHOW_VISUAL,
- VIS_ACTION_REPEAT,
- VIS_ACTION_SELECTION_FLIP,
- VIS_ACTION_SELECTION_RESTORE,
- VIS_ACTION_WINDOW_REDRAW_TOP,
- VIS_ACTION_WINDOW_REDRAW_CENTER,
- VIS_ACTION_WINDOW_REDRAW_BOTTOM,
- VIS_ACTION_WINDOW_SLIDE_UP,
- VIS_ACTION_WINDOW_SLIDE_DOWN,
- VIS_ACTION_PUT_AFTER,
- VIS_ACTION_PUT_BEFORE,
- VIS_ACTION_PUT_AFTER_END,
- VIS_ACTION_PUT_BEFORE_END,
- VIS_ACTION_CURSOR_SELECT_WORD,
- VIS_ACTION_CURSORS_NEW_LINE_ABOVE,
- VIS_ACTION_CURSORS_NEW_LINE_BELOW,
- VIS_ACTION_CURSORS_NEW_LINES_BEGIN,
- VIS_ACTION_CURSORS_NEW_LINES_END,
- VIS_ACTION_CURSORS_NEW_MATCH_NEXT,
- VIS_ACTION_CURSORS_NEW_MATCH_SKIP,
- VIS_ACTION_CURSORS_ALIGN,
- VIS_ACTION_CURSORS_REMOVE_ALL,
- VIS_ACTION_CURSORS_REMOVE_LAST,
- VIS_ACTION_TEXT_OBJECT_WORD_OUTER,
- VIS_ACTION_TEXT_OBJECT_WORD_INNER,
- VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER,
- VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER,
- VIS_ACTION_TEXT_OBJECT_SENTENCE,
- VIS_ACTION_TEXT_OBJECT_PARAGRAPH,
- VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER,
- VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER,
- VIS_ACTION_TEXT_OBJECT_PARANTHESE_OUTER,
- VIS_ACTION_TEXT_OBJECT_PARANTHESE_INNER,
- VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER,
- VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER,
- VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER,
- VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER,
- VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER,
- VIS_ACTION_TEXT_OBJECT_QUOTE_INNER,
- VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER,
- VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER,
- VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER,
- VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER,
- VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER,
- VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER,
- VIS_ACTION_TEXT_OBJECT_FUNCTION_OUTER,
- VIS_ACTION_TEXT_OBJECT_FUNCTION_INNER,
- VIS_ACTION_TEXT_OBJECT_LINE_OUTER,
- VIS_ACTION_TEXT_OBJECT_LINE_INNER,
- VIS_ACTION_MOTION_CHARWISE,
- VIS_ACTION_MOTION_LINEWISE,
- VIS_ACTION_NOP,
-};
-
-static KeyAction vis_action[] = {
- [VIS_ACTION_EDITOR_SUSPEND] = {
- "editor-suspend",
- "Suspend the editor",
- suspend,
- },
- [VIS_ACTION_CURSOR_CHAR_PREV] = {
- "cursor-char-prev",
- "Move cursor left, to the previous character",
- movement, { .i = MOVE_CHAR_PREV }
- },
- [VIS_ACTION_CURSOR_CHAR_NEXT] = {
- "cursor-char-next",
- "Move cursor right, to the next character",
- movement, { .i = MOVE_CHAR_NEXT }
- },
- [VIS_ACTION_CURSOR_WORD_START_PREV] = {
- "cursor-word-start-prev",
- "Move cursor words backwards",
- movement, { .i = MOVE_WORD_START_PREV }
- },
- [VIS_ACTION_CURSOR_WORD_START_NEXT] = {
- "cursor-word-start-next",
- "Move cursor words forwards",
- movement, { .i = MOVE_WORD_START_NEXT }
- },
- [VIS_ACTION_CURSOR_WORD_END_PREV] = {
- "cursor-word-end-prev",
- "Move cursor backwards to the end of word",
- movement, { .i = MOVE_WORD_END_PREV }
- },
- [VIS_ACTION_CURSOR_WORD_END_NEXT] = {
- "cursor-word-end-next",
- "Move cursor forward to the end of word",
- movement, { .i = MOVE_WORD_END_NEXT }
- },
- [VIS_ACTION_CURSOR_LONGWORD_START_PREV] = {
- "cursor-longword-start-prev",
- "Move cursor WORDS backwards",
- movement, { .i = MOVE_LONGWORD_START_PREV }
- },
- [VIS_ACTION_CURSOR_LONGWORD_START_NEXT] = {
- "cursor-longword-start-next",
- "Move cursor WORDS forwards",
- movement, { .i = MOVE_LONGWORD_START_NEXT }
- },
- [VIS_ACTION_CURSOR_LONGWORD_END_PREV] = {
- "cursor-longword-end-prev",
- "Move cursor backwards to the end of WORD",
- movement, { .i = MOVE_LONGWORD_END_PREV }
- },
- [VIS_ACTION_CURSOR_LONGWORD_END_NEXT] = {
- "cursor-longword-end-next",
- "Move cursor forward to the end of WORD",
- movement, { .i = MOVE_LONGWORD_END_NEXT }
- },
- [VIS_ACTION_CURSOR_LINE_UP] = {
- "cursor-line-up",
- "Move cursor line upwards",
- movement, { .i = MOVE_LINE_UP }
- },
- [VIS_ACTION_CURSOR_LINE_DOWN] = {
- "cursor-line-down",
- "Move cursor line downwards",
- movement, { .i = MOVE_LINE_DOWN }
- },
- [VIS_ACTION_CURSOR_LINE_START] = {
- "cursor-line-start",
- "Move cursor to first non-blank character of the line",
- movement, { .i = MOVE_LINE_START }
- },
- [VIS_ACTION_CURSOR_LINE_FINISH] = {
- "cursor-line-finish",
- "Move cursor to last non-blank character of the line",
- movement, { .i = MOVE_LINE_FINISH }
- },
- [VIS_ACTION_CURSOR_LINE_BEGIN] = {
- "cursor-line-begin",
- "Move cursor to first character of the line",
- movement, { .i = MOVE_LINE_BEGIN }
- },
- [VIS_ACTION_CURSOR_LINE_END] = {
- "cursor-line-end",
- "Move cursor to end of the line",
- movement, { .i = MOVE_LINE_LASTCHAR }
- },
- [VIS_ACTION_CURSOR_SCREEN_LINE_UP] = {
- "cursor-sceenline-up",
- "Move cursor screen/display line upwards",
- movement, { .i = MOVE_SCREEN_LINE_UP }
- },
- [VIS_ACTION_CURSOR_SCREEN_LINE_DOWN] = {
- "cursor-screenline-down",
- "Move cursor screen/display line downwards",
- movement, { .i = MOVE_SCREEN_LINE_DOWN }
- },
- [VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN] = {
- "cursor-screenline-begin",
- "Move cursor to beginning of screen/display line",
- movement, { .i = MOVE_SCREEN_LINE_BEGIN }
- },
- [VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE] = {
- "cursor-screenline-middle",
- "Move cursor to middle of screen/display line",
- movement, { .i = MOVE_SCREEN_LINE_MIDDLE }
- },
- [VIS_ACTION_CURSOR_SCREEN_LINE_END] = {
- "cursor-screenline-end",
- "Move cursor to end of screen/display line",
- movement, { .i = MOVE_SCREEN_LINE_END }
- },
- [VIS_ACTION_CURSOR_BRACKET_MATCH] = {
- "cursor-match-bracket",
- "Match corresponding symbol if cursor is on a bracket character",
- movement, { .i = MOVE_BRACKET_MATCH }
- },
- [VIS_ACTION_CURSOR_PARAGRAPH_PREV] = {
- "cursor-paragraph-prev",
- "Move cursor paragraph backward",
- movement, { .i = MOVE_PARAGRAPH_PREV }
- },
- [VIS_ACTION_CURSOR_PARAGRAPH_NEXT] = {
- "cursor-paragraph-next",
- "Move cursor paragraph forward",
- movement, { .i = MOVE_PARAGRAPH_NEXT }
- },
- [VIS_ACTION_CURSOR_SENTENCE_PREV] = {
- "cursor-sentence-prev",
- "Move cursor sentence backward",
- movement, { .i = MOVE_SENTENCE_PREV }
- },
- [VIS_ACTION_CURSOR_SENTENCE_NEXT] = {
- "cursor-sentence-next",
- "Move cursor sentence forward",
- movement, { .i = MOVE_SENTENCE_NEXT }
- },
- [VIS_ACTION_CURSOR_FUNCTION_START_PREV] = {
- "cursor-function-start-prev",
- "Move cursor backwards to start of function",
- movement, { .i = MOVE_FUNCTION_START_PREV }
- },
- [VIS_ACTION_CURSOR_FUNCTION_START_NEXT] = {
- "cursor-function-start-next",
- "Move cursor forwards to start of function",
- movement, { .i = MOVE_FUNCTION_START_NEXT }
- },
- [VIS_ACTION_CURSOR_FUNCTION_END_PREV] = {
- "cursor-function-end-prev",
- "Move cursor backwards to end of function",
- movement, { .i = MOVE_FUNCTION_END_PREV }
- },
- [VIS_ACTION_CURSOR_FUNCTION_END_NEXT] = {
- "cursor-function-end-next",
- "Move cursor forwards to end of function",
- movement, { .i = MOVE_FUNCTION_END_NEXT }
- },
- [VIS_ACTION_CURSOR_COLUMN] = {
- "cursor-column",
- "Move cursor to given column of current line",
- movement, { .i = MOVE_COLUMN }
- },
- [VIS_ACTION_CURSOR_LINE_FIRST] = {
- "cursor-line-first",
- "Move cursor to given line (defaults to first)",
- gotoline, { .i = -1 }
- },
- [VIS_ACTION_CURSOR_LINE_LAST] = {
- "cursor-line-last",
- "Move cursor to given line (defaults to last)",
- gotoline, { .i = +1 }
- },
- [VIS_ACTION_CURSOR_WINDOW_LINE_TOP] = {
- "cursor-window-line-top",
- "Move cursor to top line of the window",
- movement, { .i = MOVE_WINDOW_LINE_TOP }
- },
- [VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE] = {
- "cursor-window-line-middle",
- "Move cursor to middle line of the window",
- movement, { .i = MOVE_WINDOW_LINE_MIDDLE }
- },
- [VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM] = {
- "cursor-window-line-bottom",
- "Move cursor to bottom line of the window",
- movement, { .i = MOVE_WINDOW_LINE_BOTTOM }
- },
- [VIS_ACTION_CURSOR_SEARCH_FORWARD] = {
- "cursor-search-forward",
- "Move cursor to bottom line of the window",
- movement, { .i = MOVE_SEARCH_FORWARD }
- },
- [VIS_ACTION_CURSOR_SEARCH_BACKWARD] = {
- "cursor-search-backward",
- "Move cursor to bottom line of the window",
- movement, { .i = MOVE_SEARCH_BACKWARD }
- },
- [VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD] = {
- "cursor-search-word-forward",
- "Move cursor to next occurence of the word under cursor",
- movement, { .i = MOVE_SEARCH_WORD_FORWARD }
- },
- [VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD] = {
- "cursor-search-word-backward",
- "Move cursor to previous occurence of the word under cursor",
- movement, { .i = MOVE_SEARCH_WORD_BACKWARD }
- },
- [VIS_ACTION_WINDOW_PAGE_UP] = {
- "window-page-up",
- "Scroll window pages backwards (upwards)",
- wscroll, { .i = -PAGE }
- },
- [VIS_ACTION_WINDOW_HALFPAGE_UP] = {
- "window-halfpage-up",
- "Scroll window half pages backwards (upwards)",
- wscroll, { .i = -PAGE_HALF }
- },
- [VIS_ACTION_WINDOW_PAGE_DOWN] = {
- "window-page-down",
- "Scroll window pages forwards (downwards)",
- wscroll, { .i = +PAGE }
- },
- [VIS_ACTION_WINDOW_HALFPAGE_DOWN] = {
- "window-halfpage-down",
- "Scroll window half pages forwards (downwards)",
- wscroll, { .i = +PAGE_HALF }
- },
- [VIS_ACTION_MODE_NORMAL] = {
- "vis-mode-normal",
- "Enter normal mode",
- switchmode, { .i = VIS_MODE_NORMAL }
- },
- [VIS_ACTION_MODE_VISUAL] = {
- "vis-mode-visual-charwise",
- "Enter characterwise visual mode",
- switchmode, { .i = VIS_MODE_VISUAL }
- },
- [VIS_ACTION_MODE_VISUAL_LINE] = {
- "vis-mode-visual-linewise",
- "Enter linewise visual mode",
- switchmode, { .i = VIS_MODE_VISUAL_LINE }
- },
- [VIS_ACTION_MODE_INSERT] = {
- "vis-mode-insert",
- "Enter insert mode",
- switchmode, { .i = VIS_MODE_INSERT }
- },
- [VIS_ACTION_MODE_REPLACE] = {
- "vis-mode-replace",
- "Enter replace mode",
- switchmode, { .i = VIS_MODE_REPLACE }
- },
- [VIS_ACTION_MODE_OPERATOR_PENDING] = {
- "vis-mode-operator-pending",
- "Enter to operator pending mode",
- switchmode, { .i = VIS_MODE_OPERATOR }
- },
- [VIS_ACTION_DELETE_CHAR_PREV] = {
- "delete-char-prev",
- "Delete the previous character",
- delete, { .i = MOVE_CHAR_PREV }
- },
- [VIS_ACTION_DELETE_CHAR_NEXT] = {
- "delete-char-next",
- "Delete the next character",
- delete, { .i = MOVE_CHAR_NEXT }
- },
- [VIS_ACTION_DELETE_LINE_BEGIN] = {
- "delete-line-begin",
- "Delete until the start of the current line",
- delete, { .i = MOVE_LINE_BEGIN }
- },
- [VIS_ACTION_DELETE_WORD_PREV] = {
- "delete-word-prev",
- "Delete the previous WORD",
- delete, { .i = MOVE_LONGWORD_START_PREV }
- },
- [VIS_ACTION_JUMPLIST_PREV] = {
- "jumplist-prev",
- "Go to older cursor position in jump list",
- movement, { .i = MOVE_JUMPLIST_PREV }
- },
- [VIS_ACTION_JUMPLIST_NEXT] = {
- "jumplist-next",
- "Go to newer cursor position in jump list",
- movement, { .i = MOVE_JUMPLIST_NEXT }
- },
- [VIS_ACTION_CHANGELIST_PREV] = {
- "changelist-prev",
- "Go to older cursor position in change list",
- movement, { .i = MOVE_CHANGELIST_PREV }
- },
- [VIS_ACTION_CHANGELIST_NEXT] = {
- "changelist-next",
- "Go to newer cursor position in change list",
- movement, { .i = MOVE_CHANGELIST_NEXT }
- },
- [VIS_ACTION_UNDO] = {
- "editor-undo",
- "Undo last change",
- undo,
- },
- [VIS_ACTION_REDO] = {
- "editor-redo",
- "Redo last change",
- redo,
- },
- [VIS_ACTION_EARLIER] = {
- "editor-earlier",
- "Goto older text state",
- earlier,
- },
- [VIS_ACTION_LATER] = {
- "editor-later",
- "Goto newer text state",
- later,
- },
- [VIS_ACTION_MACRO_RECORD] = {
- "macro-record",
- "Record macro into given register",
- macro_record,
- },
- [VIS_ACTION_MACRO_REPLAY] = {
- "macro-replay",
- "Replay macro, execute the content of the given register",
- macro_replay,
- },
- [VIS_ACTION_MARK_SET] = {
- "mark-set",
- "Set given mark at current cursor position",
- mark_set,
- },
- [VIS_ACTION_MARK_GOTO] = {
- "mark-goto",
- "Goto the position of the given mark",
- mark_motion, { .i = MOVE_MARK }
- },
- [VIS_ACTION_MARK_GOTO_LINE] = {
- "mark-goto-line",
- "Goto first non-blank character of the line containing the given mark",
- mark_motion, { .i = MOVE_MARK_LINE }
- },
- [VIS_ACTION_REDRAW] = {
- "editor-redraw",
- "Redraw current editor content",
- call, { .f = vis_draw }
- },
- [VIS_ACTION_REPLACE_CHAR] = {
- "replace-char",
- "Replace the character under the cursor",
- replace,
- },
- [VIS_ACTION_TOTILL_REPEAT] = {
- "totill-repeat",
- "Repeat latest to/till motion",
- movement, { .i = MOVE_TOTILL_REPEAT }
- },
- [VIS_ACTION_TOTILL_REVERSE] = {
- "totill-reverse",
- "Repeat latest to/till motion but in opposite direction",
- movement, { .i = MOVE_TOTILL_REVERSE }
- },
- [VIS_ACTION_SEARCH_FORWARD] = {
- "search-forward",
- "Search forward",
- prompt_search, { .s = "/" }
- },
- [VIS_ACTION_SEARCH_BACKWARD] = {
- "search-backward",
- "Search backward",
- prompt_search, { .s = "?" }
- },
- [VIS_ACTION_TILL_LEFT] = {
- "till-left",
- "Till after the occurrence of character to the left",
- movement_key, { .i = MOVE_LEFT_TILL }
- },
- [VIS_ACTION_TILL_RIGHT] = {
- "till-right",
- "Till before the occurrence of character to the right",
- movement_key, { .i = MOVE_RIGHT_TILL }
- },
- [VIS_ACTION_TO_LEFT] = {
- "to-left",
- "To the first occurrence of character to the left",
- movement_key, { .i = MOVE_LEFT_TO }
- },
- [VIS_ACTION_TO_RIGHT] = {
- "to-right",
- "To the first occurrence of character to the right",
- movement_key, { .i = MOVE_RIGHT_TO }
- },
- [VIS_ACTION_REGISTER] = {
- "register",
- "Use given register for next operator",
- reg,
- },
- [VIS_ACTION_OPERATOR_CHANGE] = {
- "vis-operator-change",
- "Change operator",
- operator, { .i = OP_CHANGE }
- },
- [VIS_ACTION_OPERATOR_DELETE] = {
- "vis-operator-delete",
- "Delete operator",
- operator, { .i = OP_DELETE }
- },
- [VIS_ACTION_OPERATOR_YANK] = {
- "vis-operator-yank",
- "Yank operator",
- operator, { .i = OP_YANK }
- },
- [VIS_ACTION_OPERATOR_SHIFT_LEFT] = {
- "vis-operator-shift-left",
- "Shift left operator",
- operator, { .i = OP_SHIFT_LEFT }
- },
- [VIS_ACTION_OPERATOR_SHIFT_RIGHT] = {
- "vis-operator-shift-right",
- "Shift right operator",
- operator, { .i = OP_SHIFT_RIGHT }
- },
- [VIS_ACTION_OPERATOR_CASE_LOWER] = {
- "vis-operator-case-lower",
- "Lowercase operator",
- changecase, { .i = -1 }
- },
- [VIS_ACTION_OPERATOR_CASE_UPPER] = {
- "vis-operator-case-upper",
- "Uppercase operator",
- changecase, { .i = +1 }
- },
- [VIS_ACTION_OPERATOR_CASE_SWAP] = {
- "vis-operator-case-swap",
- "Swap case operator",
- changecase, { .i = 0 }
- },
- [VIS_ACTION_COUNT] = {
- "vis-count",
- "Count specifier",
- count,
- },
- [VIS_ACTION_INSERT_NEWLINE] = {
- "insert-newline",
- "Insert a line break (depending on file type)",
- insert_newline,
- },
- [VIS_ACTION_INSERT_TAB] = {
- "insert-tab",
- "Insert a tab (might be converted to spaces)",
- insert_tab,
- },
- [VIS_ACTION_INSERT_VERBATIM] = {
- "insert-verbatim",
- "Insert Unicode character based on code point",
- insert_verbatim,
- },
- [VIS_ACTION_INSERT_REGISTER] = {
- "insert-register",
- "Insert specified register content",
- insert_register,
- },
- [VIS_ACTION_WINDOW_NEXT] = {
- "window-next",
- "Focus next window",
- call, { .f = vis_window_next }
- },
- [VIS_ACTION_WINDOW_PREV] = {
- "window-prev",
- "Focus previous window",
- call, { .f = vis_window_prev }
- },
- [VIS_ACTION_OPEN_LINE_ABOVE] = {
- "open-line-above",
- "Begin a new line above the cursor",
- openline, { .i = MOVE_LINE_PREV }
- },
- [VIS_ACTION_OPEN_LINE_BELOW] = {
- "open-line-below",
- "Begin a new line below the cursor",
- openline, { .i = MOVE_LINE_NEXT }
- },
- [VIS_ACTION_JOIN_LINE_BELOW] = {
- "join-line-below",
- "Join line(s)",
- join, { .i = MOVE_LINE_NEXT },
- },
- [VIS_ACTION_JOIN_LINES] = {
- "join-lines",
- "Join selected lines",
- operator, { .i = OP_JOIN }
- },
- [VIS_ACTION_PROMPT_SHOW] = {
- "prompt-show",
- "Show editor command line prompt",
- prompt_cmd, { .s = "" }
- },
- [VIS_ACTION_PROMPT_BACKSPACE] = {
- "prompt-backspace",
- "Delete previous character in prompt",
- prompt_backspace
- },
- [VIS_ACTION_PROMPT_ENTER] = {
- "prompt-enter",
- "Execute current prompt content",
- prompt_enter
- },
- [VIS_ACTION_PROMPT_SHOW_VISUAL] = {
- "prompt-show-visual",
- "Show editor command line prompt in visual mode",
- prompt_cmd, { .s = "'<,'>" }
- },
- [VIS_ACTION_REPEAT] = {
- "editor-repeat",
- "Repeat latest editor command",
- repeat
- },
- [VIS_ACTION_SELECTION_FLIP] = {
- "selection-flip",
- "Flip selection, move cursor to other end",
- selection_end,
- },
- [VIS_ACTION_SELECTION_RESTORE] = {
- "selection-restore",
- "Restore last selection",
- selection_restore,
- },
- [VIS_ACTION_WINDOW_REDRAW_TOP] = {
- "window-redraw-top",
- "Redraw cursor line at the top of the window",
- window, { .w = view_redraw_top }
- },
- [VIS_ACTION_WINDOW_REDRAW_CENTER] = {
- "window-redraw-center",
- "Redraw cursor line at the center of the window",
- window, { .w = view_redraw_center }
- },
- [VIS_ACTION_WINDOW_REDRAW_BOTTOM] = {
- "window-redraw-bottom",
- "Redraw cursor line at the bottom of the window",
- window, { .w = view_redraw_bottom }
- },
- [VIS_ACTION_WINDOW_SLIDE_UP] = {
- "window-slide-up",
- "Slide window content upwards",
- wslide, { .i = -1 }
- },
- [VIS_ACTION_WINDOW_SLIDE_DOWN] = {
- "window-slide-down",
- "Slide window content downwards",
- wslide, { .i = +1 }
- },
- [VIS_ACTION_PUT_AFTER] = {
- "put-after",
- "Put text after the cursor",
- put, { .i = PUT_AFTER }
- },
- [VIS_ACTION_PUT_BEFORE] = {
- "put-before",
- "Put text before the cursor",
- put, { .i = PUT_BEFORE }
- },
- [VIS_ACTION_PUT_AFTER_END] = {
- "put-after-end",
- "Put text after the cursor, place cursor after new text",
- put, { .i = PUT_AFTER_END }
- },
- [VIS_ACTION_PUT_BEFORE_END] = {
- "put-before-end",
- "Put text before the cursor, place cursor after new text",
- put, { .i = PUT_BEFORE_END }
- },
- [VIS_ACTION_CURSOR_SELECT_WORD] = {
- "cursors-select-word",
- "Select word under cursor",
- cursors_select,
- },
- [VIS_ACTION_CURSORS_NEW_LINE_ABOVE] = {
- "cursors-new-lines-above",
- "Create a new cursor on the line above",
- cursors_new, { .i = -1 }
- },
- [VIS_ACTION_CURSORS_NEW_LINE_BELOW] = {
- "cursor-new-lines-below",
- "Create a new cursor on the line below",
- cursors_new, { .i = +1 }
- },
- [VIS_ACTION_CURSORS_NEW_LINES_BEGIN] = {
- "cursors-new-lines-begin",
- "Create a new cursor at the start of every line covered by selection",
- cursors_split, { .i = -1 }
- },
- [VIS_ACTION_CURSORS_NEW_LINES_END] = {
- "cursors-new-lines-end",
- "Create a new cursor at the end of every line covered by selection",
- cursors_split, { .i = +1 }
- },
- [VIS_ACTION_CURSORS_NEW_MATCH_NEXT] = {
- "cursors-new-match-next",
- "Select the next region matching the current selection",
- cursors_select_next
- },
- [VIS_ACTION_CURSORS_NEW_MATCH_SKIP] = {
- "cursors-new-match-skip",
- "Clear current selection, but select next match",
- cursors_select_skip,
- },
- [VIS_ACTION_CURSORS_ALIGN] = {
- "cursors-align",
- "Try to align all cursors on the same column",
- cursors_align,
- },
- [VIS_ACTION_CURSORS_REMOVE_ALL] = {
- "cursors-remove-all",
- "Remove all but the primary cursor",
- cursors_clear,
- },
- [VIS_ACTION_CURSORS_REMOVE_LAST] = {
- "cursors-remove-last",
- "Remove least recently created cursor",
- cursors_remove,
- },
- [VIS_ACTION_TEXT_OBJECT_WORD_OUTER] = {
- "text-object-word-outer",
- "A word leading and trailing whitespace included",
- textobj, { .i = TEXT_OBJ_OUTER_WORD }
- },
- [VIS_ACTION_TEXT_OBJECT_WORD_INNER] = {
- "text-object-word-inner",
- "A word leading and trailing whitespace excluded",
- textobj, { .i = TEXT_OBJ_INNER_WORD }
- },
- [VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER] = {
- "text-object-longword-outer",
- "A WORD leading and trailing whitespace included",
- textobj, { .i = TEXT_OBJ_OUTER_LONGWORD }
- },
- [VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER] = {
- "text-object-longword-inner",
- "A WORD leading and trailing whitespace excluded",
- textobj, { .i = TEXT_OBJ_INNER_LONGWORD }
- },
- [VIS_ACTION_TEXT_OBJECT_SENTENCE] = {
- "text-object-sentence",
- "A sentence",
- textobj, { .i = TEXT_OBJ_SENTENCE }
- },
- [VIS_ACTION_TEXT_OBJECT_PARAGRAPH] = {
- "text-object-paragraph",
- "A paragraph",
- textobj, { .i = TEXT_OBJ_PARAGRAPH }
- },
- [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER] = {
- "text-object-square-bracket-outer",
- "[] block (outer variant)",
- textobj, { .i = TEXT_OBJ_OUTER_SQUARE_BRACKET }
- },
- [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER] = {
- "text-object-square-bracket-inner",
- "[] block (inner variant)",
- textobj, { .i = TEXT_OBJ_INNER_SQUARE_BRACKET }
- },
- [VIS_ACTION_TEXT_OBJECT_PARANTHESE_OUTER] = {
- "text-object-parentheses-outer",
- "() block (outer variant)",
- textobj, { .i = TEXT_OBJ_OUTER_PARANTHESE }
- },
- [VIS_ACTION_TEXT_OBJECT_PARANTHESE_INNER] = {
- "text-object-parentheses-inner",
- "() block (inner variant)",
- textobj, { .i = TEXT_OBJ_INNER_PARANTHESE }
- },
- [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER] = {
- "text-object-angle-bracket-outer",
- "<> block (outer variant)",
- textobj, { .i = TEXT_OBJ_OUTER_ANGLE_BRACKET }
- },
- [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER] = {
- "text-object-angle-bracket-inner",
- "<> block (inner variant)",
- textobj, { .i = TEXT_OBJ_INNER_ANGLE_BRACKET }
- },
- [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER] = {
- "text-object-curly-bracket-outer",
- "{} block (outer variant)",
- textobj, { .i = TEXT_OBJ_OUTER_CURLY_BRACKET }
- },
- [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER] = {
- "text-object-curly-bracket-inner",
- "{} block (inner variant)",
- textobj, { .i = TEXT_OBJ_INNER_CURLY_BRACKET }
- },
- [VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER] = {
- "text-object-quote-outer",
- "A quoted string, including the quotation marks",
- textobj, { .i = TEXT_OBJ_OUTER_QUOTE }
- },
- [VIS_ACTION_TEXT_OBJECT_QUOTE_INNER] = {
- "text-object-quote-inner",
- "A quoted string, excluding the quotation marks",
- textobj, { .i = TEXT_OBJ_INNER_QUOTE }
- },
- [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER] = {
- "text-object-single-quote-outer",
- "A single quoted string, including the quotation marks",
- textobj, { .i = TEXT_OBJ_OUTER_SINGLE_QUOTE }
- },
- [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER] = {
- "text-object-single-quote-inner",
- "A single quoted string, excluding the quotation marks",
- textobj, { .i = TEXT_OBJ_INNER_SINGLE_QUOTE }
- },
- [VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER] = {
- "text-object-backtick-outer",
- "A backtick delimited string (outer variant)",
- textobj, { .i = TEXT_OBJ_OUTER_BACKTICK }
- },
- [VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER] = {
- "text-object-backtick-inner",
- "A backtick delimited string (inner variant)",
- textobj, { .i = TEXT_OBJ_INNER_BACKTICK }
- },
- [VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER] = {
- "text-object-entire-outer",
- "The whole text content",
- textobj, { .i = TEXT_OBJ_OUTER_ENTIRE }
- },
- [VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER] = {
- "text-object-entire-inner",
- "The whole text content, except for leading and trailing empty lines",
- textobj, { .i = TEXT_OBJ_INNER_ENTIRE }
- },
- [VIS_ACTION_TEXT_OBJECT_FUNCTION_OUTER] = {
- "text-object-function-outer",
- "A whole C-like function",
- textobj, { .i = TEXT_OBJ_OUTER_FUNCTION }
- },
- [VIS_ACTION_TEXT_OBJECT_FUNCTION_INNER] = {
- "text-object-function-inner",
- "A whole C-like function body",
- textobj, { .i = TEXT_OBJ_INNER_FUNCTION }
- },
- [VIS_ACTION_TEXT_OBJECT_LINE_OUTER] = {
- "text-object-line-outer",
- "The whole line",
- textobj, { .i = TEXT_OBJ_OUTER_LINE }
- },
- [VIS_ACTION_TEXT_OBJECT_LINE_INNER] = {
- "text-object-line-inner",
- "The whole line, excluding leading and trailing whitespace",
- textobj, { .i = TEXT_OBJ_INNER_LINE }
- },
- [VIS_ACTION_MOTION_CHARWISE] = {
- "motion-charwise",
- "Force motion to be charwise",
- motiontype, { .i = CHARWISE }
- },
- [VIS_ACTION_MOTION_LINEWISE] = {
- "motion-linewise",
- "Force motion to be linewise",
- motiontype, { .i = LINEWISE }
- },
- [VIS_ACTION_NOP] = {
- "nop",
- "Ignore key, do nothing",
- nop,
- },
-};
-
static KeyBinding basic_movement[] = {
{ "<C-z>", ACTION(EDITOR_SUSPEND) },
{ "<Left>", ACTION(CURSOR_CHAR_PREV) },
@@ -1017,8 +61,8 @@ static KeyBinding vis_movements[] = {
{ "g$", ACTION(CURSOR_SCREEN_LINE_END) },
{ "G", ACTION(CURSOR_LINE_LAST) },
{ "|", ACTION(CURSOR_COLUMN) },
- { "n", ACTION(CURSOR_SEARCH_FORWARD) },
- { "N", ACTION(CURSOR_SEARCH_BACKWARD) },
+ { "n", ACTION(CURSOR_SEARCH_NEXT) },
+ { "N", ACTION(CURSOR_SEARCH_PREV) },
{ "H", ACTION(CURSOR_WINDOW_LINE_TOP) },
{ "M", ACTION(CURSOR_WINDOW_LINE_MIDDLE) },
{ "L", ACTION(CURSOR_WINDOW_LINE_BOTTOM) },
@@ -1030,8 +74,8 @@ static KeyBinding vis_movements[] = {
{ "T", ACTION(TILL_LEFT) },
{ ";", ACTION(TOTILL_REPEAT) },
{ ",", ACTION(TOTILL_REVERSE) },
- { "/", ACTION(SEARCH_FORWARD) },
- { "?", ACTION(SEARCH_BACKWARD) },
+ { "/", ACTION(PROMPT_SEARCH_FORWARD) },
+ { "?", ACTION(PROMPT_SEARCH_BACKWARD) },
{ "`", ACTION(MARK_GOTO) },
{ "'", ACTION(MARK_GOTO_LINE) },
{ /* empty last element, array terminator */ },
@@ -1109,20 +153,6 @@ static KeyBinding vis_operators[] = {
{ /* empty last element, array terminator */ },
};
-static void vis_mode_operator_enter(Vis *vis, Mode *old) {
- vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_OPERATOR_OPTION];
-}
-
-static void vis_mode_operator_leave(Vis *vis, Mode *new) {
- vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_MOVE];
-}
-
-static void vis_mode_operator_input(Vis *vis, const char *str, size_t len) {
- /* invalid operator */
- action_reset(vis, &vis->action);
- vis_mode_set(vis, vis->mode_prev);
-}
-
static KeyBinding vis_operator_options[] = {
{ "v", ACTION(MOTION_CHARWISE) },
{ "V", ACTION(MOTION_LINEWISE) },
@@ -1219,45 +249,12 @@ static KeyBinding vis_mode_visual[] = {
{ /* empty last element, array terminator */ },
};
-static void vis_mode_visual_enter(Vis *vis, Mode *old) {
- if (!old->visual) {
- for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c))
- view_cursors_selection_start(c);
- vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_TEXTOBJ];
- }
-}
-
-static void vis_mode_visual_leave(Vis *vis, Mode *new) {
- if (!new->visual) {
- view_selections_clear(vis->win->view);
- vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_MOVE];
- }
-}
-
static KeyBinding vis_mode_visual_line[] = {
{ "v", ACTION(MODE_VISUAL) },
{ "V", ACTION(MODE_NORMAL) },
{ /* empty last element, array terminator */ },
};
-static void vis_mode_visual_line_enter(Vis *vis, Mode *old) {
- if (!old->visual) {
- for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c))
- view_cursors_selection_start(c);
- vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_TEXTOBJ];
- }
- movement(vis, NULL, &(const Arg){ .i = MOVE_LINE_END });
-}
-
-static void vis_mode_visual_line_leave(Vis *vis, Mode *new) {
- if (!new->visual) {
- view_selections_clear(vis->win->view);
- vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_MOVE];
- } else {
- view_cursor_to(vis->win->view, view_cursor_get(vis->win->view));
- }
-}
-
static KeyBinding vis_mode_readline[] = {
{ "<Backspace>", ACTION(DELETE_CHAR_PREV) },
{ "<C-h>", ALIAS("<Backspace>") },
@@ -1278,20 +275,6 @@ static KeyBinding vis_mode_prompt[] = {
{ /* empty last element, array terminator */ },
};
-static void vis_mode_prompt_input(Vis *vis, const char *str, size_t len) {
- vis_insert_key(vis, str, len);
-}
-
-static void vis_mode_prompt_enter(Vis *vis, Mode *old) {
- if (old->isuser && old != &vis_modes[VIS_MODE_PROMPT])
- vis->mode_before_prompt = old;
-}
-
-static void vis_mode_prompt_leave(Vis *vis, Mode *new) {
- if (new->isuser)
- vis_prompt_hide(vis);
-}
-
static KeyBinding vis_mode_insert[] = {
{ "<Escape>", ACTION(MODE_NORMAL) },
{ "<C-l>", ALIAS("<Escape>") },
@@ -1310,182 +293,10 @@ static KeyBinding vis_mode_insert[] = {
{ /* empty last element, array terminator */ },
};
-static void vis_mode_insert_leave(Vis *vis, Mode *old) {
- /* make sure we can recover the current state after an editing operation */
- text_snapshot(vis->win->file->text);
-}
-
-static void vis_mode_insert_idle(Vis *vis) {
- text_snapshot(vis->win->file->text);
-}
-
-static void vis_mode_insert_input(Vis *vis, const char *str, size_t len) {
- static size_t oldpos = EPOS;
- size_t pos = view_cursor_get(vis->win->view);
- if (pos != oldpos)
- buffer_truncate(&vis->buffer_repeat);
- buffer_append(&vis->buffer_repeat, str, len);
- oldpos = pos + len;
- action_reset(vis, &vis->action_prev);
- vis->action_prev.op = &ops[OP_REPEAT_INSERT];
- vis_insert_key(vis, str, len);
-}
-
static KeyBinding vis_mode_replace[] = {
{ /* empty last element, array terminator */ },
};
-static void vis_mode_replace_leave(Vis *vis, Mode *old) {
- /* make sure we can recover the current state after an editing operation */
- text_snapshot(vis->win->file->text);
-}
-
-static void vis_mode_replace_input(Vis *vis, const char *str, size_t len) {
- static size_t oldpos = EPOS;
- size_t pos = view_cursor_get(vis->win->view);
- if (pos != oldpos)
- buffer_truncate(&vis->buffer_repeat);
- buffer_append(&vis->buffer_repeat, str, len);
- oldpos = pos + len;
- action_reset(vis, &vis->action_prev);
- vis->action_prev.op = &ops[OP_REPEAT_REPLACE];
- vis_replace_key(vis, str, len);
-}
-
-/*
- * the tree of modes currently looks like this. the double line between OPERATOR-OPTION
- * and OPERATOR is only in effect once an operator is detected. that is when entering the
- * OPERATOR mode its parent is set to OPERATOR-OPTION which makes {INNER-,}TEXTOBJ
- * reachable. once the operator is processed (i.e. the OPERATOR mode is left) its parent
- * mode is reset back to MOVE.
- *
- * Similarly the +-ed line between OPERATOR and TEXTOBJ is only active within the visual
- * modes.
- *
- *
- * BASIC
- * (arrow keys etc.)
- * / |
- * /-------------------/ |
- * READLINE MOVE
- * / \ (h,j,k,l ...)
- * / \ | \-----------------\
- * / \ | |
- * INSERT PROMPT OPERATOR ++++ INNER-TEXTOBJ
- * | (history etc) (d,c,y,p ..) + (i [wsp[]()b<>{}B"'`] )
- * | | \\ + |
- * | | \\ + |
- * REPLACE NORMAL \\ + TEXTOBJ
- * | \\ + (a [wsp[]()b<>{}B"'`] )
- * | \\ + + |
- * | \\ + + |
- * VISUAL \\ OPERATOR-OPTION
- * | \\ (v,V)
- * | \\ //
- * | \\======//
- * VISUAL-LINE
- */
-
-static Mode vis_modes[] = {
- [VIS_MODE_BASIC] = {
- .name = "BASIC",
- .parent = NULL,
- .default_bindings = basic_movement,
- },
- [VIS_MODE_MOVE] = {
- .name = "MOVE",
- .parent = &vis_modes[VIS_MODE_BASIC],
- .default_bindings = vis_movements,
- },
- [VIS_MODE_TEXTOBJ] = {
- .name = "TEXT-OBJECTS",
- .parent = &vis_modes[VIS_MODE_MOVE],
- .default_bindings = vis_textobjs,
- },
- [VIS_MODE_OPERATOR_OPTION] = {
- .name = "OPERATOR-OPTION",
- .parent = &vis_modes[VIS_MODE_TEXTOBJ],
- .default_bindings = vis_operator_options,
- },
- [VIS_MODE_OPERATOR] = {
- .name = "OPERATOR",
- .parent = &vis_modes[VIS_MODE_MOVE],
- .default_bindings = vis_operators,
- .enter = vis_mode_operator_enter,
- .leave = vis_mode_operator_leave,
- .input = vis_mode_operator_input,
- },
- [VIS_MODE_NORMAL] = {
- .name = "NORMAL",
- .status = "",
- .help = "",
- .isuser = true,
- .parent = &vis_modes[VIS_MODE_OPERATOR],
- .default_bindings = vis_mode_normal,
- },
- [VIS_MODE_VISUAL] = {
- .name = "VISUAL",
- .status = "--VISUAL--",
- .help = "",
- .isuser = true,
- .parent = &vis_modes[VIS_MODE_OPERATOR],
- .default_bindings = vis_mode_visual,
- .enter = vis_mode_visual_enter,
- .leave = vis_mode_visual_leave,
- .visual = true,
- },
- [VIS_MODE_VISUAL_LINE] = {
- .name = "VISUAL LINE",
- .status = "--VISUAL LINE--",
- .help = "",
- .isuser = true,
- .parent = &vis_modes[VIS_MODE_VISUAL],
- .default_bindings = vis_mode_visual_line,
- .enter = vis_mode_visual_line_enter,
- .leave = vis_mode_visual_line_leave,
- .visual = true,
- },
- [VIS_MODE_READLINE] = {
- .name = "READLINE",
- .parent = &vis_modes[VIS_MODE_BASIC],
- .default_bindings = vis_mode_readline,
- },
- [VIS_MODE_PROMPT] = {
- .name = "PROMPT",
- .help = "",
- .isuser = true,
- .parent = &vis_modes[VIS_MODE_READLINE],
- .default_bindings = vis_mode_prompt,
- .input = vis_mode_prompt_input,
- .enter = vis_mode_prompt_enter,
- .leave = vis_mode_prompt_leave,
- },
- [VIS_MODE_INSERT] = {
- .name = "INSERT",
- .status = "--INSERT--",
- .help = "",
- .isuser = true,
- .parent = &vis_modes[VIS_MODE_READLINE],
- .default_bindings = vis_mode_insert,
- .leave = vis_mode_insert_leave,
- .input = vis_mode_insert_input,
- .idle = vis_mode_insert_idle,
- .idle_timeout = 3,
- },
- [VIS_MODE_REPLACE] = {
- .name = "REPLACE",
- .status = "--REPLACE--",
- .help = "",
- .isuser = true,
- .parent = &vis_modes[VIS_MODE_INSERT],
- .default_bindings = vis_mode_replace,
- .leave = vis_mode_replace_leave,
- .input = vis_mode_replace_input,
- .idle = vis_mode_insert_idle,
- .idle_timeout = 3,
- },
-};
-
/* Color definitions for use in the sytax highlighting rules below. A fore
* or background color of -1 specifies the default terminal color. */
enum {
diff --git a/main.c b/main.c
index a66c21c..9be0525 100644
--- a/main.c
+++ b/main.c
@@ -1,12 +1,1635 @@
#include <signal.h>
+#include <limits.h>
#include <string.h>
#include <errno.h>
#include "ui-curses.h"
#include "vis.h"
+#include "text-util.h"
+#include "text-motions.h"
+#include "text-objects.h"
+#include "util.h"
+#include "libutf.h"
+
+#define PAGE INT_MAX
+#define PAGE_HALF (INT_MAX-1)
+
+/** functions to be called from keybindings */
+/* ignore key, do nothing */
+static const char *nop(Vis*, const char *keys, const Arg *arg);
+static const char *macro_record(Vis*, const char *keys, const Arg *arg);
+static const char *macro_replay(Vis*, const char *keys, const Arg *arg);
+/* temporarily suspend the editor and return to the shell, type 'fg' to get back */
+static const char *suspend(Vis*, const char *keys, const Arg *arg);
+/* switch to mode indicated by arg->i */
+static const char *switchmode(Vis*, const char *keys, const Arg *arg);
+/* set mark indicated by arg->i to current cursor position */
+static const char *mark_set(Vis*, const char *keys, const Arg *arg);
+/* insert arg->s at the current cursor position */
+static const char *insert(Vis*, const char *keys, const Arg *arg);
+/* insert a tab or the needed amount of spaces at the current cursor position */
+static const char *insert_tab(Vis*, const char *keys, const Arg *arg);
+/* inserts a newline (either \n or \r\n depending on file type) */
+static const char *insert_newline(Vis*, const char *keys, const Arg *arg);
+/* put register content according to arg->i */
+static const char *put(Vis*, const char *keys, const Arg *arg);
+/* add a new line either before or after the one where the cursor currently is */
+static const char *openline(Vis*, const char *keys, const Arg *arg);
+/* join lines from current cursor position to movement indicated by arg */
+static const char *join(Vis*, const char *keys, const Arg *arg);
+/* execute arg->s as if it was typed on command prompt */
+static const char *cmd(Vis*, const char *keys, const Arg *arg);
+/* perform last action i.e. action_prev again */
+static const char *repeat(Vis*, const char *keys, const Arg *arg);
+/* replace character at cursor with one read form keyboard */
+static const char *replace(Vis*, const char *keys, const Arg *arg);
+/* create a new cursor on the previous (arg->i < 0) or next (arg->i > 0) line */
+static const char *cursors_new(Vis*, const char *keys, const Arg *arg);
+/* create new cursors in visual mode either at the start (arg-i < 0)
+ * or end (arg->i > 0) of the selected lines */
+static const char *cursors_split(Vis*, const char *keys, const Arg *arg);
+/* try to align all cursors on the same column */
+static const char *cursors_align(Vis*, const char *keys, const Arg *arg);
+/* remove all but the primary cursor and their selections */
+static const char *cursors_clear(Vis*, const char *keys, const Arg *arg);
+/* remove the least recently added cursor */
+static const char *cursors_remove(Vis*, const char *keys, const Arg *arg);
+/* select the word the cursor is currently over */
+static const char *cursors_select(Vis*, const char *keys, const Arg *arg);
+/* select the next region matching the current selection */
+static const char *cursors_select_next(Vis*, const char *keys, const Arg *arg);
+/* clear current selection but select next match */
+static const char *cursors_select_skip(Vis*, const char *keys, const Arg *arg);
+/* adjust action.count by arg->i */
+static const char *count(Vis*, const char *keys, const Arg *arg);
+/* move to the action.count-th line or if not given either to the first (arg->i < 0)
+ * or last (arg->i > 0) line of file */
+static const char *gotoline(Vis*, const char *keys, const Arg *arg);
+/* set motion type either LINEWISE or CHARWISE via arg->i */
+static const char *motiontype(Vis*, const char *keys, const Arg *arg);
+/* make the current action use the operator indicated by arg->i */
+static const char *operator(Vis*, const char *keys, const Arg *arg);
+/* change case of a file range to upper (arg->i > 0) or lowercase (arg->i < 0) */
+static const char *changecase(Vis*, const char *keys, const Arg *arg);
+/* blocks to read a key and performs movement indicated by arg->i which
+ * should be one of MOVE_{RIGHT,LEFT}_{TO,TILL} */
+static const char *movement_key(Vis*, const char *keys, const Arg *arg);
+/* perform the movement as indicated by arg->i */
+static const char *movement(Vis*, const char *keys, const Arg *arg);
+/* let the current operator affect the range indicated by the text object arg->i */
+static const char *textobj(Vis*, const char *keys, const Arg *arg);
+/* move to the other end of selected text */
+static const char *selection_end(Vis*, const char *keys, const Arg *arg);
+/* restore least recently used selection */
+static const char *selection_restore(Vis*, const char *keys, const Arg *arg);
+/* use register indicated by arg->i for the current operator */
+static const char *reg(Vis*, const char *keys, const Arg *arg);
+/* perform arg->i motion with a mark as argument */
+static const char *mark_motion(Vis*, const char *keys, const Arg *arg);
+/* {un,re}do last action, redraw window */
+static const char *undo(Vis*, const char *keys, const Arg *arg);
+static const char *redo(Vis*, const char *keys, const Arg *arg);
+/* earlier, later action chronologically, redraw window */
+static const char *earlier(Vis*, const char *keys, const Arg *arg);
+static const char *later(Vis*, const char *keys, const Arg *arg);
+/* hange/delete from the current cursor position to the end of
+ * movement as indicated by arg->i */
+static const char *delete(Vis*, const char *keys, const Arg *arg);
+/* insert register content indicated by arg->i at current cursor position */
+static const char *insert_register(Vis*, const char *keys, const Arg *arg);
+/* show a user prompt to get input with title arg->s */
+static const char *prompt_search(Vis*, const char *keys, const Arg *arg);
+static const char *prompt_cmd(Vis*, const char *keys, const Arg *arg);
+/* evaluate user input at prompt, perform search or execute a command */
+static const char *prompt_enter(Vis*, const char *keys, const Arg *arg);
+/* exit command mode if the last char is deleted */
+static const char *prompt_backspace(Vis*, const char *keys, const Arg *arg);
+/* blocks to read 3 consecutive digits and inserts the corresponding byte value */
+static const char *insert_verbatim(Vis*, const char *keys, const Arg *arg);
+/* scroll window content according to arg->i which can be either PAGE, PAGE_HALF,
+ * or an arbitrary number of lines. a multiplier overrides what is given in arg->i.
+ * negative values scroll back, positive forward. */
+static const char *wscroll(Vis*, const char *keys, const Arg *arg);
+/* similar to scroll, but do only move window content not cursor position */
+static const char *wslide(Vis*, const char *keys, const Arg *arg);
+/* call editor function as indicated by arg->f */
+static const char *call(Vis*, const char *keys, const Arg *arg);
+/* call window function as indicated by arg->w */
+static const char *window(Vis*, const char *keys, const Arg *arg);
+
+enum {
+ VIS_ACTION_EDITOR_SUSPEND,
+ VIS_ACTION_CURSOR_CHAR_PREV,
+ VIS_ACTION_CURSOR_CHAR_NEXT,
+ VIS_ACTION_CURSOR_WORD_START_PREV,
+ VIS_ACTION_CURSOR_WORD_START_NEXT,
+ VIS_ACTION_CURSOR_WORD_END_PREV,
+ VIS_ACTION_CURSOR_WORD_END_NEXT,
+ VIS_ACTION_CURSOR_LONGWORD_START_PREV,
+ VIS_ACTION_CURSOR_LONGWORD_START_NEXT,
+ VIS_ACTION_CURSOR_LONGWORD_END_PREV,
+ VIS_ACTION_CURSOR_LONGWORD_END_NEXT,
+ VIS_ACTION_CURSOR_LINE_UP,
+ VIS_ACTION_CURSOR_LINE_DOWN,
+ VIS_ACTION_CURSOR_LINE_START,
+ VIS_ACTION_CURSOR_LINE_FINISH,
+ VIS_ACTION_CURSOR_LINE_BEGIN,
+ VIS_ACTION_CURSOR_LINE_END,
+ VIS_ACTION_CURSOR_SCREEN_LINE_UP,
+ VIS_ACTION_CURSOR_SCREEN_LINE_DOWN,
+ VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN,
+ VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE,
+ VIS_ACTION_CURSOR_SCREEN_LINE_END,
+ VIS_ACTION_CURSOR_BRACKET_MATCH,
+ VIS_ACTION_CURSOR_PARAGRAPH_PREV,
+ VIS_ACTION_CURSOR_PARAGRAPH_NEXT,
+ VIS_ACTION_CURSOR_SENTENCE_PREV,
+ VIS_ACTION_CURSOR_SENTENCE_NEXT,
+ VIS_ACTION_CURSOR_FUNCTION_START_PREV,
+ VIS_ACTION_CURSOR_FUNCTION_END_PREV,
+ VIS_ACTION_CURSOR_FUNCTION_START_NEXT,
+ VIS_ACTION_CURSOR_FUNCTION_END_NEXT,
+ VIS_ACTION_CURSOR_COLUMN,
+ VIS_ACTION_CURSOR_LINE_FIRST,
+ VIS_ACTION_CURSOR_LINE_LAST,
+ VIS_ACTION_CURSOR_WINDOW_LINE_TOP,
+ VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE,
+ VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM,
+ VIS_ACTION_CURSOR_SEARCH_NEXT,
+ VIS_ACTION_CURSOR_SEARCH_PREV,
+ VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD,
+ VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD,
+ VIS_ACTION_WINDOW_PAGE_UP,
+ VIS_ACTION_WINDOW_PAGE_DOWN,
+ VIS_ACTION_WINDOW_HALFPAGE_UP,
+ VIS_ACTION_WINDOW_HALFPAGE_DOWN,
+ VIS_ACTION_MODE_NORMAL,
+ VIS_ACTION_MODE_VISUAL,
+ VIS_ACTION_MODE_VISUAL_LINE,
+ VIS_ACTION_MODE_INSERT,
+ VIS_ACTION_MODE_REPLACE,
+ VIS_ACTION_MODE_OPERATOR_PENDING,
+ VIS_ACTION_DELETE_CHAR_PREV,
+ VIS_ACTION_DELETE_CHAR_NEXT,
+ VIS_ACTION_DELETE_LINE_BEGIN,
+ VIS_ACTION_DELETE_WORD_PREV,
+ VIS_ACTION_JUMPLIST_PREV,
+ VIS_ACTION_JUMPLIST_NEXT,
+ VIS_ACTION_CHANGELIST_PREV,
+ VIS_ACTION_CHANGELIST_NEXT,
+ VIS_ACTION_UNDO,
+ VIS_ACTION_REDO,
+ VIS_ACTION_EARLIER,
+ VIS_ACTION_LATER,
+ VIS_ACTION_MACRO_RECORD,
+ VIS_ACTION_MACRO_REPLAY,
+ VIS_ACTION_MARK_SET,
+ VIS_ACTION_MARK_GOTO,
+ VIS_ACTION_MARK_GOTO_LINE,
+ VIS_ACTION_REDRAW,
+ VIS_ACTION_REPLACE_CHAR,
+ VIS_ACTION_TOTILL_REPEAT,
+ VIS_ACTION_TOTILL_REVERSE,
+ VIS_ACTION_PROMPT_SEARCH_FORWARD,
+ VIS_ACTION_PROMPT_SEARCH_BACKWARD,
+ VIS_ACTION_TILL_LEFT,
+ VIS_ACTION_TILL_RIGHT,
+ VIS_ACTION_TO_LEFT,
+ VIS_ACTION_TO_RIGHT,
+ VIS_ACTION_REGISTER,
+ VIS_ACTION_OPERATOR_CHANGE,
+ VIS_ACTION_OPERATOR_DELETE,
+ VIS_ACTION_OPERATOR_YANK,
+ VIS_ACTION_OPERATOR_SHIFT_LEFT,
+ VIS_ACTION_OPERATOR_SHIFT_RIGHT,
+ VIS_ACTION_OPERATOR_CASE_LOWER,
+ VIS_ACTION_OPERATOR_CASE_UPPER,
+ VIS_ACTION_OPERATOR_CASE_SWAP,
+ VIS_ACTION_COUNT,
+ VIS_ACTION_INSERT_NEWLINE,
+ VIS_ACTION_INSERT_TAB,
+ VIS_ACTION_INSERT_VERBATIM,
+ VIS_ACTION_INSERT_REGISTER,
+ VIS_ACTION_WINDOW_NEXT,
+ VIS_ACTION_WINDOW_PREV,
+ VIS_ACTION_OPEN_LINE_ABOVE,
+ VIS_ACTION_OPEN_LINE_BELOW,
+ VIS_ACTION_JOIN_LINE_BELOW,
+ VIS_ACTION_JOIN_LINES,
+ VIS_ACTION_PROMPT_SHOW,
+ VIS_ACTION_PROMPT_BACKSPACE,
+ VIS_ACTION_PROMPT_ENTER,
+ VIS_ACTION_PROMPT_SHOW_VISUAL,
+ VIS_ACTION_REPEAT,
+ VIS_ACTION_SELECTION_FLIP,
+ VIS_ACTION_SELECTION_RESTORE,
+ VIS_ACTION_WINDOW_REDRAW_TOP,
+ VIS_ACTION_WINDOW_REDRAW_CENTER,
+ VIS_ACTION_WINDOW_REDRAW_BOTTOM,
+ VIS_ACTION_WINDOW_SLIDE_UP,
+ VIS_ACTION_WINDOW_SLIDE_DOWN,
+ VIS_ACTION_PUT_AFTER,
+ VIS_ACTION_PUT_BEFORE,
+ VIS_ACTION_PUT_AFTER_END,
+ VIS_ACTION_PUT_BEFORE_END,
+ VIS_ACTION_CURSOR_SELECT_WORD,
+ VIS_ACTION_CURSORS_NEW_LINE_ABOVE,
+ VIS_ACTION_CURSORS_NEW_LINE_BELOW,
+ VIS_ACTION_CURSORS_NEW_LINES_BEGIN,
+ VIS_ACTION_CURSORS_NEW_LINES_END,
+ VIS_ACTION_CURSORS_NEW_MATCH_NEXT,
+ VIS_ACTION_CURSORS_NEW_MATCH_SKIP,
+ VIS_ACTION_CURSORS_ALIGN,
+ VIS_ACTION_CURSORS_REMOVE_ALL,
+ VIS_ACTION_CURSORS_REMOVE_LAST,
+ VIS_ACTION_TEXT_OBJECT_WORD_OUTER,
+ VIS_ACTION_TEXT_OBJECT_WORD_INNER,
+ VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER,
+ VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER,
+ VIS_ACTION_TEXT_OBJECT_SENTENCE,
+ VIS_ACTION_TEXT_OBJECT_PARAGRAPH,
+ VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER,
+ VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER,
+ VIS_ACTION_TEXT_OBJECT_PARANTHESE_OUTER,
+ VIS_ACTION_TEXT_OBJECT_PARANTHESE_INNER,
+ VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER,
+ VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER,
+ VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER,
+ VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER,
+ VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER,
+ VIS_ACTION_TEXT_OBJECT_QUOTE_INNER,
+ VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER,
+ VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER,
+ VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER,
+ VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER,
+ VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER,
+ VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER,
+ VIS_ACTION_TEXT_OBJECT_FUNCTION_OUTER,
+ VIS_ACTION_TEXT_OBJECT_FUNCTION_INNER,
+ VIS_ACTION_TEXT_OBJECT_LINE_OUTER,
+ VIS_ACTION_TEXT_OBJECT_LINE_INNER,
+ VIS_ACTION_MOTION_CHARWISE,
+ VIS_ACTION_MOTION_LINEWISE,
+ VIS_ACTION_NOP,
+};
+
+static KeyAction vis_action[] = {
+ [VIS_ACTION_EDITOR_SUSPEND] = {
+ "editor-suspend",
+ "Suspend the editor",
+ suspend,
+ },
+ [VIS_ACTION_CURSOR_CHAR_PREV] = {
+ "cursor-char-prev",
+ "Move cursor left, to the previous character",
+ movement, { .i = MOVE_CHAR_PREV }
+ },
+ [VIS_ACTION_CURSOR_CHAR_NEXT] = {
+ "cursor-char-next",
+ "Move cursor right, to the next character",
+ movement, { .i = MOVE_CHAR_NEXT }
+ },
+ [VIS_ACTION_CURSOR_WORD_START_PREV] = {
+ "cursor-word-start-prev",
+ "Move cursor words backwards",
+ movement, { .i = MOVE_WORD_START_PREV }
+ },
+ [VIS_ACTION_CURSOR_WORD_START_NEXT] = {
+ "cursor-word-start-next",
+ "Move cursor words forwards",
+ movement, { .i = MOVE_WORD_START_NEXT }
+ },
+ [VIS_ACTION_CURSOR_WORD_END_PREV] = {
+ "cursor-word-end-prev",
+ "Move cursor backwards to the end of word",
+ movement, { .i = MOVE_WORD_END_PREV }
+ },
+ [VIS_ACTION_CURSOR_WORD_END_NEXT] = {
+ "cursor-word-end-next",
+ "Move cursor forward to the end of word",
+ movement, { .i = MOVE_WORD_END_NEXT }
+ },
+ [VIS_ACTION_CURSOR_LONGWORD_START_PREV] = {
+ "cursor-longword-start-prev",
+ "Move cursor WORDS backwards",
+ movement, { .i = MOVE_LONGWORD_START_PREV }
+ },
+ [VIS_ACTION_CURSOR_LONGWORD_START_NEXT] = {
+ "cursor-longword-start-next",
+ "Move cursor WORDS forwards",
+ movement, { .i = MOVE_LONGWORD_START_NEXT }
+ },
+ [VIS_ACTION_CURSOR_LONGWORD_END_PREV] = {
+ "cursor-longword-end-prev",
+ "Move cursor backwards to the end of WORD",
+ movement, { .i = MOVE_LONGWORD_END_PREV }
+ },
+ [VIS_ACTION_CURSOR_LONGWORD_END_NEXT] = {
+ "cursor-longword-end-next",
+ "Move cursor forward to the end of WORD",
+ movement, { .i = MOVE_LONGWORD_END_NEXT }
+ },
+ [VIS_ACTION_CURSOR_LINE_UP] = {
+ "cursor-line-up",
+ "Move cursor line upwards",
+ movement, { .i = MOVE_LINE_UP }
+ },
+ [VIS_ACTION_CURSOR_LINE_DOWN] = {
+ "cursor-line-down",
+ "Move cursor line downwards",
+ movement, { .i = MOVE_LINE_DOWN }
+ },
+ [VIS_ACTION_CURSOR_LINE_START] = {
+ "cursor-line-start",
+ "Move cursor to first non-blank character of the line",
+ movement, { .i = MOVE_LINE_START }
+ },
+ [VIS_ACTION_CURSOR_LINE_FINISH] = {
+ "cursor-line-finish",
+ "Move cursor to last non-blank character of the line",
+ movement, { .i = MOVE_LINE_FINISH }
+ },
+ [VIS_ACTION_CURSOR_LINE_BEGIN] = {
+ "cursor-line-begin",
+ "Move cursor to first character of the line",
+ movement, { .i = MOVE_LINE_BEGIN }
+ },
+ [VIS_ACTION_CURSOR_LINE_END] = {
+ "cursor-line-end",
+ "Move cursor to end of the line",
+ movement, { .i = MOVE_LINE_LASTCHAR }
+ },
+ [VIS_ACTION_CURSOR_SCREEN_LINE_UP] = {
+ "cursor-sceenline-up",
+ "Move cursor screen/display line upwards",
+ movement, { .i = MOVE_SCREEN_LINE_UP }
+ },
+ [VIS_ACTION_CURSOR_SCREEN_LINE_DOWN] = {
+ "cursor-screenline-down",
+ "Move cursor screen/display line downwards",
+ movement, { .i = MOVE_SCREEN_LINE_DOWN }
+ },
+ [VIS_ACTION_CURSOR_SCREEN_LINE_BEGIN] = {
+ "cursor-screenline-begin",
+ "Move cursor to beginning of screen/display line",
+ movement, { .i = MOVE_SCREEN_LINE_BEGIN }
+ },
+ [VIS_ACTION_CURSOR_SCREEN_LINE_MIDDLE] = {
+ "cursor-screenline-middle",
+ "Move cursor to middle of screen/display line",
+ movement, { .i = MOVE_SCREEN_LINE_MIDDLE }
+ },
+ [VIS_ACTION_CURSOR_SCREEN_LINE_END] = {
+ "cursor-screenline-end",
+ "Move cursor to end of screen/display line",
+ movement, { .i = MOVE_SCREEN_LINE_END }
+ },
+ [VIS_ACTION_CURSOR_BRACKET_MATCH] = {
+ "cursor-match-bracket",
+ "Match corresponding symbol if cursor is on a bracket character",
+ movement, { .i = MOVE_BRACKET_MATCH }
+ },
+ [VIS_ACTION_CURSOR_PARAGRAPH_PREV] = {
+ "cursor-paragraph-prev",
+ "Move cursor paragraph backward",
+ movement, { .i = MOVE_PARAGRAPH_PREV }
+ },
+ [VIS_ACTION_CURSOR_PARAGRAPH_NEXT] = {
+ "cursor-paragraph-next",
+ "Move cursor paragraph forward",
+ movement, { .i = MOVE_PARAGRAPH_NEXT }
+ },
+ [VIS_ACTION_CURSOR_SENTENCE_PREV] = {
+ "cursor-sentence-prev",
+ "Move cursor sentence backward",
+ movement, { .i = MOVE_SENTENCE_PREV }
+ },
+ [VIS_ACTION_CURSOR_SENTENCE_NEXT] = {
+ "cursor-sentence-next",
+ "Move cursor sentence forward",
+ movement, { .i = MOVE_SENTENCE_NEXT }
+ },
+ [VIS_ACTION_CURSOR_FUNCTION_START_PREV] = {
+ "cursor-function-start-prev",
+ "Move cursor backwards to start of function",
+ movement, { .i = MOVE_FUNCTION_START_PREV }
+ },
+ [VIS_ACTION_CURSOR_FUNCTION_START_NEXT] = {
+ "cursor-function-start-next",
+ "Move cursor forwards to start of function",
+ movement, { .i = MOVE_FUNCTION_START_NEXT }
+ },
+ [VIS_ACTION_CURSOR_FUNCTION_END_PREV] = {
+ "cursor-function-end-prev",
+ "Move cursor backwards to end of function",
+ movement, { .i = MOVE_FUNCTION_END_PREV }
+ },
+ [VIS_ACTION_CURSOR_FUNCTION_END_NEXT] = {
+ "cursor-function-end-next",
+ "Move cursor forwards to end of function",
+ movement, { .i = MOVE_FUNCTION_END_NEXT }
+ },
+ [VIS_ACTION_CURSOR_COLUMN] = {
+ "cursor-column",
+ "Move cursor to given column of current line",
+ movement, { .i = MOVE_COLUMN }
+ },
+ [VIS_ACTION_CURSOR_LINE_FIRST] = {
+ "cursor-line-first",
+ "Move cursor to given line (defaults to first)",
+ gotoline, { .i = -1 }
+ },
+ [VIS_ACTION_CURSOR_LINE_LAST] = {
+ "cursor-line-last",
+ "Move cursor to given line (defaults to last)",
+ gotoline, { .i = +1 }
+ },
+ [VIS_ACTION_CURSOR_WINDOW_LINE_TOP] = {
+ "cursor-window-line-top",
+ "Move cursor to top line of the window",
+ movement, { .i = MOVE_WINDOW_LINE_TOP }
+ },
+ [VIS_ACTION_CURSOR_WINDOW_LINE_MIDDLE] = {
+ "cursor-window-line-middle",
+ "Move cursor to middle line of the window",
+ movement, { .i = MOVE_WINDOW_LINE_MIDDLE }
+ },
+ [VIS_ACTION_CURSOR_WINDOW_LINE_BOTTOM] = {
+ "cursor-window-line-bottom",
+ "Move cursor to bottom line of the window",
+ movement, { .i = MOVE_WINDOW_LINE_BOTTOM }
+ },
+ [VIS_ACTION_CURSOR_SEARCH_NEXT] = {
+ "cursor-search-forward",
+ "Move cursor to bottom line of the window",
+ movement, { .i = MOVE_SEARCH_NEXT }
+ },
+ [VIS_ACTION_CURSOR_SEARCH_PREV] = {
+ "cursor-search-backward",
+ "Move cursor to bottom line of the window",
+ movement, { .i = MOVE_SEARCH_PREV }
+ },
+ [VIS_ACTION_CURSOR_SEARCH_WORD_FORWARD] = {
+ "cursor-search-word-forward",
+ "Move cursor to next occurence of the word under cursor",
+ movement, { .i = MOVE_SEARCH_WORD_FORWARD }
+ },
+ [VIS_ACTION_CURSOR_SEARCH_WORD_BACKWARD] = {
+ "cursor-search-word-backward",
+ "Move cursor to previous occurence of the word under cursor",
+ movement, { .i = MOVE_SEARCH_WORD_BACKWARD }
+ },
+ [VIS_ACTION_WINDOW_PAGE_UP] = {
+ "window-page-up",
+ "Scroll window pages backwards (upwards)",
+ wscroll, { .i = -PAGE }
+ },
+ [VIS_ACTION_WINDOW_HALFPAGE_UP] = {
+ "window-halfpage-up",
+ "Scroll window half pages backwards (upwards)",
+ wscroll, { .i = -PAGE_HALF }
+ },
+ [VIS_ACTION_WINDOW_PAGE_DOWN] = {
+ "window-page-down",
+ "Scroll window pages forwards (downwards)",
+ wscroll, { .i = +PAGE }
+ },
+ [VIS_ACTION_WINDOW_HALFPAGE_DOWN] = {
+ "window-halfpage-down",
+ "Scroll window half pages forwards (downwards)",
+ wscroll, { .i = +PAGE_HALF }
+ },
+ [VIS_ACTION_MODE_NORMAL] = {
+ "vis-mode-normal",
+ "Enter normal mode",
+ switchmode, { .i = VIS_MODE_NORMAL }
+ },
+ [VIS_ACTION_MODE_VISUAL] = {
+ "vis-mode-visual-charwise",
+ "Enter characterwise visual mode",
+ switchmode, { .i = VIS_MODE_VISUAL }
+ },
+ [VIS_ACTION_MODE_VISUAL_LINE] = {
+ "vis-mode-visual-linewise",
+ "Enter linewise visual mode",
+ switchmode, { .i = VIS_MODE_VISUAL_LINE }
+ },
+ [VIS_ACTION_MODE_INSERT] = {
+ "vis-mode-insert",
+ "Enter insert mode",
+ switchmode, { .i = VIS_MODE_INSERT }
+ },
+ [VIS_ACTION_MODE_REPLACE] = {
+ "vis-mode-replace",
+ "Enter replace mode",
+ switchmode, { .i = VIS_MODE_REPLACE }
+ },
+ [VIS_ACTION_MODE_OPERATOR_PENDING] = {
+ "vis-mode-operator-pending",
+ "Enter to operator pending mode",
+ switchmode, { .i = VIS_MODE_OPERATOR }
+ },
+ [VIS_ACTION_DELETE_CHAR_PREV] = {
+ "delete-char-prev",
+ "Delete the previous character",
+ delete, { .i = MOVE_CHAR_PREV }
+ },
+ [VIS_ACTION_DELETE_CHAR_NEXT] = {
+ "delete-char-next",
+ "Delete the next character",
+ delete, { .i = MOVE_CHAR_NEXT }
+ },
+ [VIS_ACTION_DELETE_LINE_BEGIN] = {
+ "delete-line-begin",
+ "Delete until the start of the current line",
+ delete, { .i = MOVE_LINE_BEGIN }
+ },
+ [VIS_ACTION_DELETE_WORD_PREV] = {
+ "delete-word-prev",
+ "Delete the previous WORD",
+ delete, { .i = MOVE_LONGWORD_START_PREV }
+ },
+ [VIS_ACTION_JUMPLIST_PREV] = {
+ "jumplist-prev",
+ "Go to older cursor position in jump list",
+ movement, { .i = MOVE_JUMPLIST_PREV }
+ },
+ [VIS_ACTION_JUMPLIST_NEXT] = {
+ "jumplist-next",
+ "Go to newer cursor position in jump list",
+ movement, { .i = MOVE_JUMPLIST_NEXT }
+ },
+ [VIS_ACTION_CHANGELIST_PREV] = {
+ "changelist-prev",
+ "Go to older cursor position in change list",
+ movement, { .i = MOVE_CHANGELIST_PREV }
+ },
+ [VIS_ACTION_CHANGELIST_NEXT] = {
+ "changelist-next",
+ "Go to newer cursor position in change list",
+ movement, { .i = MOVE_CHANGELIST_NEXT }
+ },
+ [VIS_ACTION_UNDO] = {
+ "editor-undo",
+ "Undo last change",
+ undo,
+ },
+ [VIS_ACTION_REDO] = {
+ "editor-redo",
+ "Redo last change",
+ redo,
+ },
+ [VIS_ACTION_EARLIER] = {
+ "editor-earlier",
+ "Goto older text state",
+ earlier,
+ },
+ [VIS_ACTION_LATER] = {
+ "editor-later",
+ "Goto newer text state",
+ later,
+ },
+ [VIS_ACTION_MACRO_RECORD] = {
+ "macro-record",
+ "Record macro into given register",
+ macro_record,
+ },
+ [VIS_ACTION_MACRO_REPLAY] = {
+ "macro-replay",
+ "Replay macro, execute the content of the given register",
+ macro_replay,
+ },
+ [VIS_ACTION_MARK_SET] = {
+ "mark-set",
+ "Set given mark at current cursor position",
+ mark_set,
+ },
+ [VIS_ACTION_MARK_GOTO] = {
+ "mark-goto",
+ "Goto the position of the given mark",
+ mark_motion, { .i = MOVE_MARK }
+ },
+ [VIS_ACTION_MARK_GOTO_LINE] = {
+ "mark-goto-line",
+ "Goto first non-blank character of the line containing the given mark",
+ mark_motion, { .i = MOVE_MARK_LINE }
+ },
+ [VIS_ACTION_REDRAW] = {
+ "editor-redraw",
+ "Redraw current editor content",
+ call, { .f = vis_draw }
+ },
+ [VIS_ACTION_REPLACE_CHAR] = {
+ "replace-char",
+ "Replace the character under the cursor",
+ replace,
+ },
+ [VIS_ACTION_TOTILL_REPEAT] = {
+ "totill-repeat",
+ "Repeat latest to/till motion",
+ movement, { .i = MOVE_TOTILL_REPEAT }
+ },
+ [VIS_ACTION_TOTILL_REVERSE] = {
+ "totill-reverse",
+ "Repeat latest to/till motion but in opposite direction",
+ movement, { .i = MOVE_TOTILL_REVERSE }
+ },
+ [VIS_ACTION_PROMPT_SEARCH_FORWARD] = {
+ "search-forward",
+ "Search forward",
+ prompt_search, { .s = "/" }
+ },
+ [VIS_ACTION_PROMPT_SEARCH_BACKWARD] = {
+ "search-backward",
+ "Search backward",
+ prompt_search, { .s = "?" }
+ },
+ [VIS_ACTION_TILL_LEFT] = {
+ "till-left",
+ "Till after the occurrence of character to the left",
+ movement_key, { .i = MOVE_LEFT_TILL }
+ },
+ [VIS_ACTION_TILL_RIGHT] = {
+ "till-right",
+ "Till before the occurrence of character to the right",
+ movement_key, { .i = MOVE_RIGHT_TILL }
+ },
+ [VIS_ACTION_TO_LEFT] = {
+ "to-left",
+ "To the first occurrence of character to the left",
+ movement_key, { .i = MOVE_LEFT_TO }
+ },
+ [VIS_ACTION_TO_RIGHT] = {
+ "to-right",
+ "To the first occurrence of character to the right",
+ movement_key, { .i = MOVE_RIGHT_TO }
+ },
+ [VIS_ACTION_REGISTER] = {
+ "register",
+ "Use given register for next operator",
+ reg,
+ },
+ [VIS_ACTION_OPERATOR_CHANGE] = {
+ "vis-operator-change",
+ "Change operator",
+ operator, { .i = OP_CHANGE }
+ },
+ [VIS_ACTION_OPERATOR_DELETE] = {
+ "vis-operator-delete",
+ "Delete operator",
+ operator, { .i = OP_DELETE }
+ },
+ [VIS_ACTION_OPERATOR_YANK] = {
+ "vis-operator-yank",
+ "Yank operator",
+ operator, { .i = OP_YANK }
+ },
+ [VIS_ACTION_OPERATOR_SHIFT_LEFT] = {
+ "vis-operator-shift-left",
+ "Shift left operator",
+ operator, { .i = OP_SHIFT_LEFT }
+ },
+ [VIS_ACTION_OPERATOR_SHIFT_RIGHT] = {
+ "vis-operator-shift-right",
+ "Shift right operator",
+ operator, { .i = OP_SHIFT_RIGHT }
+ },
+ [VIS_ACTION_OPERATOR_CASE_LOWER] = {
+ "vis-operator-case-lower",
+ "Lowercase operator",
+ changecase, { .i = -1 }
+ },
+ [VIS_ACTION_OPERATOR_CASE_UPPER] = {
+ "vis-operator-case-upper",
+ "Uppercase operator",
+ changecase, { .i = +1 }
+ },
+ [VIS_ACTION_OPERATOR_CASE_SWAP] = {
+ "vis-operator-case-swap",
+ "Swap case operator",
+ changecase, { .i = 0 }
+ },
+ [VIS_ACTION_COUNT] = {
+ "vis-count",
+ "Count specifier",
+ count,
+ },
+ [VIS_ACTION_INSERT_NEWLINE] = {
+ "insert-newline",
+ "Insert a line break (depending on file type)",
+ insert_newline,
+ },
+ [VIS_ACTION_INSERT_TAB] = {
+ "insert-tab",
+ "Insert a tab (might be converted to spaces)",
+ insert_tab,
+ },
+ [VIS_ACTION_INSERT_VERBATIM] = {
+ "insert-verbatim",
+ "Insert Unicode character based on code point",
+ insert_verbatim,
+ },
+ [VIS_ACTION_INSERT_REGISTER] = {
+ "insert-register",
+ "Insert specified register content",
+ insert_register,
+ },
+ [VIS_ACTION_WINDOW_NEXT] = {
+ "window-next",
+ "Focus next window",
+ call, { .f = vis_window_next }
+ },
+ [VIS_ACTION_WINDOW_PREV] = {
+ "window-prev",
+ "Focus previous window",
+ call, { .f = vis_window_prev }
+ },
+ [VIS_ACTION_OPEN_LINE_ABOVE] = {
+ "open-line-above",
+ "Begin a new line above the cursor",
+ openline, { .i = MOVE_LINE_PREV }
+ },
+ [VIS_ACTION_OPEN_LINE_BELOW] = {
+ "open-line-below",
+ "Begin a new line below the cursor",
+ openline, { .i = MOVE_LINE_NEXT }
+ },
+ [VIS_ACTION_JOIN_LINE_BELOW] = {
+ "join-line-below",
+ "Join line(s)",
+ join, { .i = MOVE_LINE_NEXT },
+ },
+ [VIS_ACTION_JOIN_LINES] = {
+ "join-lines",
+ "Join selected lines",
+ operator, { .i = OP_JOIN }
+ },
+ [VIS_ACTION_PROMPT_SHOW] = {
+ "prompt-show",
+ "Show editor command line prompt",
+ prompt_cmd, { .s = "" }
+ },
+ [VIS_ACTION_PROMPT_BACKSPACE] = {
+ "prompt-backspace",
+ "Delete previous character in prompt",
+ prompt_backspace
+ },
+ [VIS_ACTION_PROMPT_ENTER] = {
+ "prompt-enter",
+ "Execute current prompt content",
+ prompt_enter
+ },
+ [VIS_ACTION_PROMPT_SHOW_VISUAL] = {
+ "prompt-show-visual",
+ "Show editor command line prompt in visual mode",
+ prompt_cmd, { .s = "'<,'>" }
+ },
+ [VIS_ACTION_REPEAT] = {
+ "editor-repeat",
+ "Repeat latest editor command",
+ repeat
+ },
+ [VIS_ACTION_SELECTION_FLIP] = {
+ "selection-flip",
+ "Flip selection, move cursor to other end",
+ selection_end,
+ },
+ [VIS_ACTION_SELECTION_RESTORE] = {
+ "selection-restore",
+ "Restore last selection",
+ selection_restore,
+ },
+ [VIS_ACTION_WINDOW_REDRAW_TOP] = {
+ "window-redraw-top",
+ "Redraw cursor line at the top of the window",
+ window, { .w = view_redraw_top }
+ },
+ [VIS_ACTION_WINDOW_REDRAW_CENTER] = {
+ "window-redraw-center",
+ "Redraw cursor line at the center of the window",
+ window, { .w = view_redraw_center }
+ },
+ [VIS_ACTION_WINDOW_REDRAW_BOTTOM] = {
+ "window-redraw-bottom",
+ "Redraw cursor line at the bottom of the window",
+ window, { .w = view_redraw_bottom }
+ },
+ [VIS_ACTION_WINDOW_SLIDE_UP] = {
+ "window-slide-up",
+ "Slide window content upwards",
+ wslide, { .i = -1 }
+ },
+ [VIS_ACTION_WINDOW_SLIDE_DOWN] = {
+ "window-slide-down",
+ "Slide window content downwards",
+ wslide, { .i = +1 }
+ },
+ [VIS_ACTION_PUT_AFTER] = {
+ "put-after",
+ "Put text after the cursor",
+ put, { .i = PUT_AFTER }
+ },
+ [VIS_ACTION_PUT_BEFORE] = {
+ "put-before",
+ "Put text before the cursor",
+ put, { .i = PUT_BEFORE }
+ },
+ [VIS_ACTION_PUT_AFTER_END] = {
+ "put-after-end",
+ "Put text after the cursor, place cursor after new text",
+ put, { .i = PUT_AFTER_END }
+ },
+ [VIS_ACTION_PUT_BEFORE_END] = {
+ "put-before-end",
+ "Put text before the cursor, place cursor after new text",
+ put, { .i = PUT_BEFORE_END }
+ },
+ [VIS_ACTION_CURSOR_SELECT_WORD] = {
+ "cursors-select-word",
+ "Select word under cursor",
+ cursors_select,
+ },
+ [VIS_ACTION_CURSORS_NEW_LINE_ABOVE] = {
+ "cursors-new-lines-above",
+ "Create a new cursor on the line above",
+ cursors_new, { .i = -1 }
+ },
+ [VIS_ACTION_CURSORS_NEW_LINE_BELOW] = {
+ "cursor-new-lines-below",
+ "Create a new cursor on the line below",
+ cursors_new, { .i = +1 }
+ },
+ [VIS_ACTION_CURSORS_NEW_LINES_BEGIN] = {
+ "cursors-new-lines-begin",
+ "Create a new cursor at the start of every line covered by selection",
+ cursors_split, { .i = -1 }
+ },
+ [VIS_ACTION_CURSORS_NEW_LINES_END] = {
+ "cursors-new-lines-end",
+ "Create a new cursor at the end of every line covered by selection",
+ cursors_split, { .i = +1 }
+ },
+ [VIS_ACTION_CURSORS_NEW_MATCH_NEXT] = {
+ "cursors-new-match-next",
+ "Select the next region matching the current selection",
+ cursors_select_next
+ },
+ [VIS_ACTION_CURSORS_NEW_MATCH_SKIP] = {
+ "cursors-new-match-skip",
+ "Clear current selection, but select next match",
+ cursors_select_skip,
+ },
+ [VIS_ACTION_CURSORS_ALIGN] = {
+ "cursors-align",
+ "Try to align all cursors on the same column",
+ cursors_align,
+ },
+ [VIS_ACTION_CURSORS_REMOVE_ALL] = {
+ "cursors-remove-all",
+ "Remove all but the primary cursor",
+ cursors_clear,
+ },
+ [VIS_ACTION_CURSORS_REMOVE_LAST] = {
+ "cursors-remove-last",
+ "Remove least recently created cursor",
+ cursors_remove,
+ },
+ [VIS_ACTION_TEXT_OBJECT_WORD_OUTER] = {
+ "text-object-word-outer",
+ "A word leading and trailing whitespace included",
+ textobj, { .i = TEXT_OBJ_OUTER_WORD }
+ },
+ [VIS_ACTION_TEXT_OBJECT_WORD_INNER] = {
+ "text-object-word-inner",
+ "A word leading and trailing whitespace excluded",
+ textobj, { .i = TEXT_OBJ_INNER_WORD }
+ },
+ [VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER] = {
+ "text-object-longword-outer",
+ "A WORD leading and trailing whitespace included",
+ textobj, { .i = TEXT_OBJ_OUTER_LONGWORD }
+ },
+ [VIS_ACTION_TEXT_OBJECT_LONGWORD_INNER] = {
+ "text-object-longword-inner",
+ "A WORD leading and trailing whitespace excluded",
+ textobj, { .i = TEXT_OBJ_INNER_LONGWORD }
+ },
+ [VIS_ACTION_TEXT_OBJECT_SENTENCE] = {
+ "text-object-sentence",
+ "A sentence",
+ textobj, { .i = TEXT_OBJ_SENTENCE }
+ },
+ [VIS_ACTION_TEXT_OBJECT_PARAGRAPH] = {
+ "text-object-paragraph",
+ "A paragraph",
+ textobj, { .i = TEXT_OBJ_PARAGRAPH }
+ },
+ [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_OUTER] = {
+ "text-object-square-bracket-outer",
+ "[] block (outer variant)",
+ textobj, { .i = TEXT_OBJ_OUTER_SQUARE_BRACKET }
+ },
+ [VIS_ACTION_TEXT_OBJECT_SQUARE_BRACKET_INNER] = {
+ "text-object-square-bracket-inner",
+ "[] block (inner variant)",
+ textobj, { .i = TEXT_OBJ_INNER_SQUARE_BRACKET }
+ },
+ [VIS_ACTION_TEXT_OBJECT_PARANTHESE_OUTER] = {
+ "text-object-parentheses-outer",
+ "() block (outer variant)",
+ textobj, { .i = TEXT_OBJ_OUTER_PARANTHESE }
+ },
+ [VIS_ACTION_TEXT_OBJECT_PARANTHESE_INNER] = {
+ "text-object-parentheses-inner",
+ "() block (inner variant)",
+ textobj, { .i = TEXT_OBJ_INNER_PARANTHESE }
+ },
+ [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_OUTER] = {
+ "text-object-angle-bracket-outer",
+ "<> block (outer variant)",
+ textobj, { .i = TEXT_OBJ_OUTER_ANGLE_BRACKET }
+ },
+ [VIS_ACTION_TEXT_OBJECT_ANGLE_BRACKET_INNER] = {
+ "text-object-angle-bracket-inner",
+ "<> block (inner variant)",
+ textobj, { .i = TEXT_OBJ_INNER_ANGLE_BRACKET }
+ },
+ [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_OUTER] = {
+ "text-object-curly-bracket-outer",
+ "{} block (outer variant)",
+ textobj, { .i = TEXT_OBJ_OUTER_CURLY_BRACKET }
+ },
+ [VIS_ACTION_TEXT_OBJECT_CURLY_BRACKET_INNER] = {
+ "text-object-curly-bracket-inner",
+ "{} block (inner variant)",
+ textobj, { .i = TEXT_OBJ_INNER_CURLY_BRACKET }
+ },
+ [VIS_ACTION_TEXT_OBJECT_QUOTE_OUTER] = {
+ "text-object-quote-outer",
+ "A quoted string, including the quotation marks",
+ textobj, { .i = TEXT_OBJ_OUTER_QUOTE }
+ },
+ [VIS_ACTION_TEXT_OBJECT_QUOTE_INNER] = {
+ "text-object-quote-inner",
+ "A quoted string, excluding the quotation marks",
+ textobj, { .i = TEXT_OBJ_INNER_QUOTE }
+ },
+ [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_OUTER] = {
+ "text-object-single-quote-outer",
+ "A single quoted string, including the quotation marks",
+ textobj, { .i = TEXT_OBJ_OUTER_SINGLE_QUOTE }
+ },
+ [VIS_ACTION_TEXT_OBJECT_SINGLE_QUOTE_INNER] = {
+ "text-object-single-quote-inner",
+ "A single quoted string, excluding the quotation marks",
+ textobj, { .i = TEXT_OBJ_INNER_SINGLE_QUOTE }
+ },
+ [VIS_ACTION_TEXT_OBJECT_BACKTICK_OUTER] = {
+ "text-object-backtick-outer",
+ "A backtick delimited string (outer variant)",
+ textobj, { .i = TEXT_OBJ_OUTER_BACKTICK }
+ },
+ [VIS_ACTION_TEXT_OBJECT_BACKTICK_INNER] = {
+ "text-object-backtick-inner",
+ "A backtick delimited string (inner variant)",
+ textobj, { .i = TEXT_OBJ_INNER_BACKTICK }
+ },
+ [VIS_ACTION_TEXT_OBJECT_ENTIRE_OUTER] = {
+ "text-object-entire-outer",
+ "The whole text content",
+ textobj, { .i = TEXT_OBJ_OUTER_ENTIRE }
+ },
+ [VIS_ACTION_TEXT_OBJECT_ENTIRE_INNER] = {
+ "text-object-entire-inner",
+ "The whole text content, except for leading and trailing empty lines",
+ textobj, { .i = TEXT_OBJ_INNER_ENTIRE }
+ },
+ [VIS_ACTION_TEXT_OBJECT_FUNCTION_OUTER] = {
+ "text-object-function-outer",
+ "A whole C-like function",
+ textobj, { .i = TEXT_OBJ_OUTER_FUNCTION }
+ },
+ [VIS_ACTION_TEXT_OBJECT_FUNCTION_INNER] = {
+ "text-object-function-inner",
+ "A whole C-like function body",
+ textobj, { .i = TEXT_OBJ_INNER_FUNCTION }
+ },
+ [VIS_ACTION_TEXT_OBJECT_LINE_OUTER] = {
+ "text-object-line-outer",
+ "The whole line",
+ textobj, { .i = TEXT_OBJ_OUTER_LINE }
+ },
+ [VIS_ACTION_TEXT_OBJECT_LINE_INNER] = {
+ "text-object-line-inner",
+ "The whole line, excluding leading and trailing whitespace",
+ textobj, { .i = TEXT_OBJ_INNER_LINE }
+ },
+ [VIS_ACTION_MOTION_CHARWISE] = {
+ "motion-charwise",
+ "Force motion to be charwise",
+ motiontype, { .i = VIS_MOTIONTYPE_CHARWISE }
+ },
+ [VIS_ACTION_MOTION_LINEWISE] = {
+ "motion-linewise",
+ "Force motion to be linewise",
+ motiontype, { .i = VIS_MOTIONTYPE_LINEWISE }
+ },
+ [VIS_ACTION_NOP] = {
+ "nop",
+ "Ignore key, do nothing",
+ nop,
+ },
+};
+
+#include "config.h"
+
+/** key bindings functions */
+
+static const char *nop(Vis *vis, const char *keys, const Arg *arg) {
+ return keys;
+}
+
+static const char *key2macro(Vis *vis, const char *keys, enum VisMacro *macro) {
+ *macro = VIS_MACRO_INVALID;
+ if (keys[0] >= 'a' && keys[0] <= 'z')
+ *macro = keys[0] - 'a';
+ else if (keys[0] == '@')
+ *macro = VIS_MACRO_LAST_RECORDED;
+ else if (keys[0] == '\0')
+ return NULL;
+ return keys+1;
+}
+
+static const char *macro_record(Vis *vis, const char *keys, const Arg *arg) {
+ if (vis_macro_record_stop(vis))
+ return keys;
+ enum VisMacro macro;
+ keys = key2macro(vis, keys, &macro);
+ vis_macro_record(vis, macro);
+ vis_draw(vis);
+ return keys;
+}
+
+static const char *macro_replay(Vis *vis, const char *keys, const Arg *arg) {
+ enum VisMacro macro;
+ keys = key2macro(vis, keys, &macro);
+ vis_macro_replay(vis, macro);
+ return keys;
+}
+
+static const char *suspend(Vis *vis, const char *keys, const Arg *arg) {
+ vis_suspend(vis);
+ return keys;
+}
+
+static const char *repeat(Vis *vis, const char *keys, const Arg *arg) {
+ vis_repeat(vis);
+ return keys;
+}
+
+static const char *cursors_new(Vis *vis, const char *keys, const Arg *arg) {
+ View *view = vis->win->view;
+ Text *txt = vis->win->file->text;
+ size_t pos = view_cursor_get(view);
+ if (arg->i > 0)
+ pos = text_line_down(txt, pos);
+ else if (arg->i < 0)
+ pos = text_line_up(txt, pos);
+ Cursor *cursor = view_cursors_new(view);
+ if (cursor)
+ view_cursors_to(cursor, pos);
+ return keys;
+}
+
+static const char *cursors_split(Vis *vis, const char *keys, const Arg *arg) {
+ vis->action.arg = *arg;
+ vis_operator(vis, OP_CURSOR);
+ return keys;
+}
+
+static const char *cursors_align(Vis *vis, const char *keys, const Arg *arg) {
+ View *view = vis->win->view;
+ Text *txt = vis->win->file->text;
+ int mincol = INT_MAX;
+ for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
+ size_t pos = view_cursors_pos(c);
+ int col = text_line_char_get(txt, pos);
+ if (col < mincol)
+ mincol = col;
+ }
+ for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
+ size_t pos = view_cursors_pos(c);
+ size_t col = text_line_char_set(txt, pos, mincol);
+ view_cursors_to(c, col);
+ }
+ return keys;
+}
+
+static const char *cursors_clear(Vis *vis, const char *keys, const Arg *arg) {
+ View *view = vis->win->view;
+ if (view_cursors_count(view) > 1)
+ view_cursors_clear(view);
+ else
+ view_cursors_selection_clear(view_cursor(view));
+ return keys;
+}
+
+static const char *cursors_select(Vis *vis, const char *keys, const Arg *arg) {
+ Text *txt = vis->win->file->text;
+ View *view = vis->win->view;
+ for (Cursor *cursor = view_cursors(view); cursor; cursor = view_cursors_next(cursor)) {
+ Filerange sel = view_cursors_selection_get(cursor);
+ Filerange word = text_object_word(txt, view_cursors_pos(cursor));
+ if (!text_range_valid(&sel) && text_range_valid(&word)) {
+ view_cursors_selection_set(cursor, &word);
+ view_cursors_to(cursor, text_char_prev(txt, word.end));
+ }
+ }
+ vis_mode_switch(vis, VIS_MODE_VISUAL);
+ return keys;
+}
+
+static const char *cursors_select_next(Vis *vis, const char *keys, const Arg *arg) {
+ Text *txt = vis->win->file->text;
+ View *view = vis->win->view;
+ Cursor *cursor = view_cursor(view);
+ Filerange sel = view_cursors_selection_get(cursor);
+ if (!text_range_valid(&sel))
+ return keys;
+
+ size_t len = text_range_size(&sel);
+ char *buf = malloc(len+1);
+ if (!buf)
+ return keys;
+ len = text_bytes_get(txt, sel.start, len, buf);
+ buf[len] = '\0';
+ Filerange word = text_object_word_find_next(txt, sel.end, buf);
+ free(buf);
+
+ if (text_range_valid(&word)) {
+ cursor = view_cursors_new(view);
+ if (!cursor)
+ return keys;
+ view_cursors_selection_set(cursor, &word);
+ view_cursors_to(cursor, text_char_prev(txt, word.end));
+ }
+ return keys;
+}
+
+static const char *cursors_select_skip(Vis *vis, const char *keys, const Arg *arg) {
+ View *view = vis->win->view;
+ Cursor *cursor = view_cursor(view);
+ keys = cursors_select_next(vis, keys, arg);
+ if (cursor != view_cursor(view))
+ view_cursors_dispose(cursor);
+ return keys;
+}
+
+static const char *cursors_remove(Vis *vis, const char *keys, const Arg *arg) {
+ View *view = vis->win->view;
+ view_cursors_dispose(view_cursor(view));
+ return keys;
+}
+
+static const char *replace(Vis *vis, const char *keys, const Arg *arg) {
+ if (!keys[0])
+ return NULL;
+ const char *next = vis_key_next(vis, keys);
+ size_t len = next - keys;
+ /* TODO: fix
+ action_reset(vis, &vis->action_prev);
+ vis->action_prev.op = &ops[OP_REPEAT_REPLACE];
+ buffer_put(&vis->buffer_repeat, keys, len);
+ */
+ vis_replace_key(vis, keys, len);
+ text_snapshot(vis->win->file->text);
+ return next;
+}
+
+static const char *count(Vis *vis, const char *keys, const Arg *arg) {
+ int digit = keys[-1] - '0';
+ int count = vis_count_get(vis);
+ if (0 <= digit && digit <= 9) {
+ if (digit == 0 && count == 0)
+ vis_motion(vis, MOVE_LINE_BEGIN);
+ vis_count_set(vis, count * 10 + digit);
+ }
+ return keys;
+}
+
+static const char *gotoline(Vis *vis, const char *keys, const Arg *arg) {
+ if (vis_count_get(vis))
+ vis_motion(vis, MOVE_LINE);
+ else if (arg->i < 0)
+ vis_motion(vis, MOVE_FILE_BEGIN);
+ else
+ vis_motion(vis, MOVE_FILE_END);
+ return keys;
+}
+
+static const char *motiontype(Vis *vis, const char *keys, const Arg *arg) {
+ vis_motion_type(vis, arg->i);
+ return keys;
+}
+
+static const char *operator(Vis *vis, const char *keys, const Arg *arg) {
+ vis_operator(vis, arg->i);
+ return keys;
+}
+
+static const char *changecase(Vis *vis, const char *keys, const Arg *arg) {
+ vis->action.arg = *arg;
+ vis_operator(vis, OP_CASE_CHANGE);
+ return keys;
+}
+
+static const char *movement_key(Vis *vis, const char *keys, const Arg *arg) {
+ if (!keys[0])
+ return NULL;
+ char key[32];
+ const char *next = vis_key_next(vis, keys);
+ strncpy(key, keys, next - keys + 1);
+ key[sizeof(key)-1] = '\0';
+ vis_motion(vis, arg->i, key);
+ return next;
+}
+
+static const char *movement(Vis *vis, const char *keys, const Arg *arg) {
+ vis_motion(vis, arg->i);
+ return keys;
+}
+
+static const char *textobj(Vis *vis, const char *keys, const Arg *arg) {
+ vis_textobject(vis, arg->i);
+ return keys;
+}
+
+static const char *selection_end(Vis *vis, const char *keys, const Arg *arg) {
+ for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c))
+ view_cursors_selection_swap(c);
+ return keys;
+}
+
+static const char *selection_restore(Vis *vis, const char *keys, const Arg *arg) {
+ for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c))
+ view_cursors_selection_restore(c);
+ vis_mode_switch(vis, VIS_MODE_VISUAL);
+ return keys;
+}
+
+static const char *key2register(Vis *vis, const char *keys, enum VisRegister *reg) {
+ *reg = VIS_REGISTER_INVALID;
+ if (!keys[0])
+ return NULL;
+ if (keys[0] >= 'a' && keys[0] <= 'z')
+ *reg = keys[0] - 'a';
+ return keys+1;
+}
+
+static const char *reg(Vis *vis, const char *keys, const Arg *arg) {
+ enum VisRegister reg;
+ keys = key2register(vis, keys, &reg);
+ vis_register_set(vis, reg);
+ return keys;
+}
+
+static const char *key2mark(Vis *vis, const char *keys, int *mark) {
+ *mark = VIS_MARK_INVALID;
+ if (!keys[0])
+ return NULL;
+ if (keys[0] >= 'a' && keys[0] <= 'z')
+ *mark = keys[0] - 'a';
+ else if (keys[0] == '<')
+ *mark = MARK_SELECTION_START;
+ else if (keys[0] == '>')
+ *mark = MARK_SELECTION_END;
+ return keys+1;
+}
+
+static const char *mark_set(Vis *vis, const char *keys, const Arg *arg) {
+ int mark;
+ keys = key2mark(vis, keys, &mark);
+ vis_mark_set(vis, mark, view_cursor_get(vis->win->view));
+ return keys;
+}
+
+static const char *mark_motion(Vis *vis, const char *keys, const Arg *arg) {
+ int mark;
+ keys = key2mark(vis, keys, &mark);
+ vis_motion(vis, arg->i, mark);
+ return keys;
+}
+
+static const char *undo(Vis *vis, const char *keys, const Arg *arg) {
+ size_t pos = text_undo(vis->win->file->text);
+ if (pos != EPOS) {
+ View *view = vis->win->view;
+ if (view_cursors_count(view) == 1)
+ view_cursor_to(view, pos);
+ /* redraw all windows in case some display the same file */
+ vis_draw(vis);
+ }
+ return keys;
+}
+
+static const char *redo(Vis *vis, const char *keys, const Arg *arg) {
+ size_t pos = text_redo(vis->win->file->text);
+ if (pos != EPOS) {
+ View *view = vis->win->view;
+ if (view_cursors_count(view) == 1)
+ view_cursor_to(view, pos);
+ /* redraw all windows in case some display the same file */
+ vis_draw(vis);
+ }
+ return keys;
+}
+
+static const char *earlier(Vis *vis, const char *keys, const Arg *arg) {
+ size_t pos = text_earlier(vis->win->file->text, MAX(vis_count_get(vis), 1));
+ if (pos != EPOS) {
+ view_cursor_to(vis->win->view, pos);
+ /* redraw all windows in case some display the same file */
+ vis_draw(vis);
+ }
+ return keys;
+}
+
+static const char *later(Vis *vis, const char *keys, const Arg *arg) {
+ size_t pos = text_later(vis->win->file->text, MAX(vis_count_get(vis), 1));
+ if (pos != EPOS) {
+ view_cursor_to(vis->win->view, pos);
+ /* redraw all windows in case some display the same file */
+ vis_draw(vis);
+ }
+ return keys;
+}
+
+static const char *delete(Vis *vis, const char *keys, const Arg *arg) {
+ vis_operator(vis, OP_DELETE);
+ vis_motion(vis, arg->i);
+ return keys;
+}
+
+static const char *insert_register(Vis *vis, const char *keys, const Arg *arg) {
+ enum VisRegister regid;
+ keys = key2register(vis, keys, &regid);
+ Register *reg = vis_register_get(vis, regid);
+ if (reg) {
+ int pos = view_cursor_get(vis->win->view);
+ vis_insert(vis, pos, reg->data, reg->len);
+ view_cursor_to(vis->win->view, pos + reg->len);
+ }
+ return keys;
+}
+
+static const char *prompt_search(Vis *vis, const char *keys, const Arg *arg) {
+ vis_prompt_show(vis, arg->s, "");
+ vis_mode_switch(vis, VIS_MODE_PROMPT);
+ return keys;
+}
+
+static const char *prompt_cmd(Vis *vis, const char *keys, const Arg *arg) {
+ vis_prompt_show(vis, ":", arg->s);
+ vis_mode_switch(vis, VIS_MODE_PROMPT);
+ return keys;
+}
+
+static const char *prompt_enter(Vis *vis, const char *keys, const Arg *arg) {
+ char *s = vis_prompt_get(vis);
+ /* it is important to switch back to the previous mode, which hides
+ * the prompt and more importantly resets vis->win to the currently
+ * focused editor window *before* anything is executed which depends
+ * on vis->win.
+ */
+ vis_mode_set(vis, vis->mode_before_prompt);
+ if (s && *s && vis_prompt_cmd(vis, vis->prompt_type, s) && vis->running)
+ vis_mode_switch(vis, VIS_MODE_NORMAL);
+ free(s);
+ vis_draw(vis);
+ return keys;
+}
+
+static const char *prompt_backspace(Vis *vis, const char *keys, const Arg *arg) {
+ char *cmd = vis_prompt_get(vis);
+ if (!cmd || !*cmd)
+ prompt_enter(vis, keys, NULL);
+ else
+ delete(vis, keys, &(const Arg){ .i = MOVE_CHAR_PREV });
+ free(cmd);
+ return keys;
+}
+
+static const char *insert_verbatim(Vis *vis, const char *keys, const Arg *arg) {
+ Rune rune = 0;
+ char buf[4], type = keys[0];
+ int len = 0, count = 0, base;
+ switch (type) {
+ case '\0':
+ return NULL;
+ case 'o':
+ case 'O':
+ count = 3;
+ base = 8;
+ break;
+ case 'U':
+ count = 4;
+ /* fall through */
+ case 'u':
+ count += 4;
+ base = 16;
+ break;
+ case 'x':
+ case 'X':
+ count = 2;
+ base = 16;
+ break;
+ default:
+ if (type < '0' || type > '9')
+ return keys;
+ rune = type - '0';
+ count = 2;
+ base = 10;
+ break;
+ }
+
+ for (keys++; keys[0] && count > 0; keys++, count--) {
+ int v = 0;
+ if (base == 8 && '0' <= keys[0] && keys[0] <= '7') {
+ v = keys[0] - '0';
+ } else if ((base == 10 || base == 16) && '0' <= keys[0] && keys[0] <= '9') {
+ v = keys[0] - '0';
+ } else if (base == 16 && 'a' <= keys[0] && keys[0] <= 'f') {
+ v = 10 + keys[0] - 'a';
+ } else if (base == 16 && 'A' <= keys[0] && keys[0] <= 'F') {
+ v = 10 + keys[0] - 'A';
+ } else {
+ count = 0;
+ break;
+ }
+ rune = rune * base + v;
+ }
+
+ if (count > 0)
+ return NULL;
+
+ if (type == 'u' || type == 'U') {
+ len = runetochar(buf, &rune);
+ } else {
+ buf[0] = rune;
+ len = 1;
+ }
+
+ if (len > 0) {
+ size_t pos = view_cursor_get(vis->win->view);
+ vis_insert(vis, pos, buf, len);
+ view_cursor_to(vis->win->view, pos + len);
+ }
+ return keys;
+}
+
+static const char *cmd(Vis *vis, const char *keys, const Arg *arg) {
+ vis_cmd(vis, arg->s);
+ return keys;
+}
+
+static int argi2lines(Vis *vis, const Arg *arg) {
+ switch (arg->i) {
+ case -PAGE:
+ case +PAGE:
+ return view_height_get(vis->win->view);
+ case -PAGE_HALF:
+ case +PAGE_HALF:
+ return view_height_get(vis->win->view)/2;
+ default:
+ if (vis_count_get(vis) > 0)
+ return vis_count_get(vis);
+ return arg->i < 0 ? -arg->i : arg->i;
+ }
+}
+
+static const char *wscroll(Vis *vis, const char *keys, const Arg *arg) {
+ if (arg->i >= 0)
+ view_scroll_down(vis->win->view, argi2lines(vis, arg));
+ else
+ view_scroll_up(vis->win->view, argi2lines(vis, arg));
+ return keys;
+}
+
+static const char *wslide(Vis *vis, const char *keys, const Arg *arg) {
+ if (arg->i >= 0)
+ view_slide_down(vis->win->view, argi2lines(vis, arg));
+ else
+ view_slide_up(vis->win->view, argi2lines(vis, arg));
+ return keys;
+}
+
+static const char *call(Vis *vis, const char *keys, const Arg *arg) {
+ arg->f(vis);
+ return keys;
+}
+
+static const char *window(Vis *vis, const char *keys, const Arg *arg) {
+ arg->w(vis->win->view);
+ return keys;
+}
+
+static const char *insert(Vis *vis, const char *keys, const Arg *arg) {
+ vis_insert_key(vis, arg->s, arg->s ? strlen(arg->s) : 0);
+ return keys;
+}
+
+static const char *insert_tab(Vis *vis, const char *keys, const Arg *arg) {
+ insert(vis, keys, &(const Arg){ .s = vis_expandtab(vis) });
+ return keys;
+}
+
+static void copy_indent_from_previous_line(Win *win) {
+ View *view = win->view;
+ Text *text = win->file->text;
+ size_t pos = view_cursor_get(view);
+ size_t prev_line = text_line_prev(text, pos);
+ if (pos == prev_line)
+ return;
+ size_t begin = text_line_begin(text, prev_line);
+ size_t start = text_line_start(text, begin);
+ size_t len = start-begin;
+ char *buf = malloc(len);
+ if (!buf)
+ return;
+ len = text_bytes_get(text, begin, len, buf);
+ vis_insert_key(win->editor, buf, len);
+ free(buf);
+}
+
+static const char *insert_newline(Vis *vis, const char *keys, const Arg *arg) {
+ const char *nl;
+ switch (text_newline_type(vis->win->file->text)) {
+ case TEXT_NEWLINE_CRNL:
+ nl = "\r\n";
+ break;
+ default:
+ nl = "\n";
+ break;
+ }
+
+ insert(vis, keys, &(const Arg){ .s = nl });
+
+ if (vis->autoindent)
+ copy_indent_from_previous_line(vis->win);
+ return keys;
+}
+
+static const char *put(Vis *vis, const char *keys, const Arg *arg) {
+ vis->action.arg = *arg;
+ vis_operator(vis, OP_PUT);
+ vis_motion(vis, MOVE_NOP);
+ return keys;
+}
+
+static const char *openline(Vis *vis, const char *keys, const Arg *arg) {
+ if (arg->i == MOVE_LINE_NEXT) {
+ vis_motion(vis, MOVE_LINE_END);
+ insert_newline(vis, keys, NULL);
+ } else {
+ vis_motion(vis, MOVE_LINE_BEGIN);
+ insert_newline(vis, keys, NULL);
+ vis_motion(vis, MOVE_LINE_PREV);
+ }
+ vis_mode_switch(vis, VIS_MODE_INSERT);
+ return keys;
+}
+
+static const char *join(Vis *vis, const char *keys, const Arg *arg) {
+ int count = vis_count_get(vis);
+ if (count)
+ vis_count_set(vis, count-1);
+ vis_operator(vis, OP_JOIN);
+ vis_motion(vis, arg->i);
+ return keys;
+}
+
+static const char *switchmode(Vis *vis, const char *keys, const Arg *arg) {
+ vis_mode_switch(vis, arg->i);
+ return keys;
+}
static Vis *vis;
+static KeyBinding *default_bindings[] = {
+ [VIS_MODE_BASIC] = basic_movement,
+ [VIS_MODE_MOVE] = vis_movements,
+ [VIS_MODE_TEXTOBJ] = vis_textobjs,
+ [VIS_MODE_OPERATOR_OPTION] = vis_operator_options,
+ [VIS_MODE_OPERATOR] = vis_operators,
+ [VIS_MODE_NORMAL] = vis_mode_normal,
+ [VIS_MODE_VISUAL] = vis_mode_visual,
+ [VIS_MODE_VISUAL_LINE] = vis_mode_visual_line,
+ [VIS_MODE_READLINE] = vis_mode_readline,
+ [VIS_MODE_PROMPT] = vis_mode_prompt,
+ [VIS_MODE_INSERT] = vis_mode_insert,
+ [VIS_MODE_REPLACE] = vis_mode_replace,
+};
+
static void signal_handler(int signum, siginfo_t *siginfo, void *context) {
vis_signal_handler(vis, signum, siginfo, context);
}
@@ -14,6 +1637,22 @@ static void signal_handler(int signum, siginfo_t *siginfo, void *context) {
int main(int argc, char *argv[]) {
vis = vis_new(ui_curses_new());
+ if (!vis)
+ return EXIT_FAILURE;
+
+ for (int i = 0; i < LENGTH(vis_action); i++) {
+ KeyAction *action = &vis_action[i];
+ if (!vis_action_register(vis, action))
+ vis_die(vis, "Could not register action: %s\n", action->name);
+ }
+
+ for (int i = 0; i < LENGTH(default_bindings); i++) {
+ if (!vis_mode_bindings(vis, i, &default_bindings[i]))
+ vis_die(vis, "Could not load default bindings\n");
+ }
+
+ if (!vis_syntax_load(vis, syntaxes))
+ vis_die(vis, "Could not load syntax highlighting definitions\n");
/* install signal handlers etc. */
struct sigaction sa;
@@ -29,7 +1668,7 @@ int main(int argc, char *argv[]) {
sigprocmask(SIG_BLOCK, &blockset, NULL);
signal(SIGPIPE, SIG_IGN);
- vis_run(vis, argc, argv);
+ int status = vis_run(vis, argc, argv);
vis_free(vis);
- return 0;
+ return status;
}
diff --git a/ui-curses.c b/ui-curses.c
index 1e3c925..42809bd 100644
--- a/ui-curses.c
+++ b/ui-curses.c
@@ -609,11 +609,12 @@ static void ui_window_draw_status(UiWin *w) {
Vis *vis = uic->vis;
bool focused = uic->selwin == win;
const char *filename = win->file->name;
+ const char *status = vis_mode_status(vis);
CursorPos pos = view_cursor_getpos(win->view);
wattrset(win->winstatus, focused ? A_REVERSE|A_BOLD : A_REVERSE);
mvwhline(win->winstatus, 0, 0, ' ', win->width);
mvwprintw(win->winstatus, 0, 0, "%s %s %s %s",
- focused && vis->mode->status ? vis->mode->status : "",
+ focused && status ? status : "",
filename ? filename : "[No Name]",
text_modified(win->file->text) ? "[+]" : "",
vis->recording ? "recording": "");
diff --git a/vis.c b/vis.c
index d88c980..2e24fe9 100644
--- a/vis.c
+++ b/vis.c
@@ -40,7 +40,35 @@
#include "text-objects.h"
#include "util.h"
#include "map.h"
-#include "libutf.h"
+
+/* a mode contains a set of key bindings which are currently valid.
+ *
+ * each mode can specify one parent mode which is consultated if a given key
+ * is not found in the current mode. hence the modes form a tree which is
+ * searched from the current mode up towards the root mode until a valid binding
+ * is found.
+ *
+ * if no binding is found, mode->input(...) is called and the user entered
+ * keys are passed as argument. this is used to change the document content.
+ */
+struct Mode {
+ Mode *parent; /* if no match is found in this mode, search will continue there */
+ Map *bindings;
+ const char *name; /* descriptive, user facing name of the mode */
+ const char *status; /* name displayed in the window status bar */
+ const char *help; /* short description used by :help */
+ bool isuser; /* whether this is a user or internal mode */
+ void (*enter)(Vis*, Mode *old); /* called right before the mode becomes active */
+ void (*leave)(Vis*, Mode *new); /* called right before the mode becomes inactive */
+ void (*input)(Vis*, const char*, size_t); /* called whenever a key is not found in this mode and all its parent modes */
+ void (*idle)(Vis*); /* called whenever a certain idle time i.e. without any user input elapsed */
+ time_t idle_timeout; /* idle time in seconds after which the registered function will be called */
+ bool visual; /* whether text selection is possible in this mode */
+};
+
+
+/* TODO make part of struct Vis? */
+static Mode vis_modes[VIS_MODE_LAST];
typedef struct {
int count; /* how many times should the command be executed? */
@@ -391,7 +419,7 @@ void vis_window_close(Win *win) {
vis_draw(vis);
}
-Vis *vis_new0(Ui *ui) {
+Vis *vis_new(Ui *ui) {
if (!ui)
return NULL;
Vis *vis = calloc(1, sizeof(Vis));
@@ -401,6 +429,11 @@ Vis *vis_new0(Ui *ui) {
vis->ui->init(vis->ui, vis);
vis->tabwidth = 8;
vis->expandtab = false;
+ for (int i = 0; i < VIS_MODE_LAST; i++) {
+ Mode *mode = &vis_modes[i];
+ if (!(mode->bindings = map_new()))
+ goto err;
+ }
if (!(vis->prompt = calloc(1, sizeof(Win))))
goto err;
if (!(vis->prompt->file = calloc(1, sizeof(File))))
@@ -413,6 +446,7 @@ Vis *vis_new0(Ui *ui) {
goto err;
if (!(vis->search_pattern = text_regex_new()))
goto err;
+ vis->mode_prev = vis->mode = &vis_modes[VIS_MODE_NORMAL];
return vis;
err:
vis_free(vis);
@@ -437,6 +471,10 @@ void vis_free(Vis *vis) {
map_free(vis->options);
map_free(vis->actions);
buffer_release(&vis->buffer_repeat);
+ for (int i = 0; i < VIS_MODE_LAST; i++) {
+ Mode *mode = &vis_modes[i];
+ map_free(mode->bindings);
+ }
free(vis);
}
@@ -541,16 +579,6 @@ static Operator ops[] = {
[OP_CURSOR] = { op_cursor },
};
-#define PAGE INT_MAX
-#define PAGE_HALF (INT_MAX-1)
-
-enum {
- PUT_AFTER,
- PUT_AFTER_END,
- PUT_BEFORE,
- PUT_BEFORE_END,
-};
-
/** movements which can be used besides the one in text-motions.h and view.h */
/* search in forward direction for the word under the cursor */
@@ -588,6 +616,7 @@ static size_t window_changelist_prev(Vis*, Win*, size_t pos);
/* navigate the jump list */
static size_t window_jumplist_next(Vis*, Win*, size_t pos);
static size_t window_jumplist_prev(Vis*, Win*, size_t pos);
+static size_t window_nop(Vis*, Win*, size_t pos);
static Movement moves[] = {
[MOVE_LINE_UP] = { .cur = view_line_up, .type = LINEWISE },
@@ -637,8 +666,8 @@ static Movement moves[] = {
[MOVE_MARK_LINE] = { .file = mark_line_goto, .type = LINEWISE|JUMP|IDEMPOTENT},
[MOVE_SEARCH_WORD_FORWARD] = { .vis = search_word_forward, .type = JUMP },
[MOVE_SEARCH_WORD_BACKWARD]= { .vis = search_word_backward, .type = JUMP },
- [MOVE_SEARCH_FORWARD] = { .vis = search_forward, .type = JUMP },
- [MOVE_SEARCH_BACKWARD] = { .vis = search_backward, .type = JUMP },
+ [MOVE_SEARCH_NEXT] = { .vis = search_forward, .type = JUMP },
+ [MOVE_SEARCH_PREV] = { .vis = search_backward, .type = JUMP },
[MOVE_WINDOW_LINE_TOP] = { .view = view_lines_top, .type = LINEWISE|JUMP|IDEMPOTENT },
[MOVE_WINDOW_LINE_MIDDLE] = { .view = view_lines_middle, .type = LINEWISE|JUMP|IDEMPOTENT },
[MOVE_WINDOW_LINE_BOTTOM] = { .view = view_lines_bottom, .type = LINEWISE|JUMP|IDEMPOTENT },
@@ -646,6 +675,7 @@ static Movement moves[] = {
[MOVE_CHANGELIST_PREV] = { .win = window_changelist_prev, .type = INCLUSIVE },
[MOVE_JUMPLIST_NEXT] = { .win = window_jumplist_next, .type = INCLUSIVE },
[MOVE_JUMPLIST_PREV] = { .win = window_jumplist_prev, .type = INCLUSIVE },
+ [MOVE_NOP] = { .win = window_nop, .type = IDEMPOTENT },
};
static TextObject textobjs[] = {
@@ -677,110 +707,6 @@ static TextObject textobjs[] = {
[TEXT_OBJ_INNER_LINE] = { text_object_line_inner, },
};
-/** functions to be called from keybindings */
-/* ignore key, do nothing */
-static const char *nop(Vis*, const char *keys, const Arg *arg);
-static const char *macro_record(Vis*, const char *keys, const Arg *arg);
-static const char *macro_replay(Vis*, const char *keys, const Arg *arg);
-/* temporarily suspend the editor and return to the shell, type 'fg' to get back */
-static const char *suspend(Vis*, const char *keys, const Arg *arg);
-/* switch to mode indicated by arg->i */
-static const char *switchmode(Vis*, const char *keys, const Arg *arg);
-/* set mark indicated by arg->i to current cursor position */
-static const char *mark_set(Vis*, const char *keys, const Arg *arg);
-/* insert arg->s at the current cursor position */
-static const char *insert(Vis*, const char *keys, const Arg *arg);
-/* insert a tab or the needed amount of spaces at the current cursor position */
-static const char *insert_tab(Vis*, const char *keys, const Arg *arg);
-/* inserts a newline (either \n or \r\n depending on file type) */
-static const char *insert_newline(Vis*, const char *keys, const Arg *arg);
-/* put register content according to arg->i */
-static const char *put(Vis*, const char *keys, const Arg *arg);
-/* add a new line either before or after the one where the cursor currently is */
-static const char *openline(Vis*, const char *keys, const Arg *arg);
-/* join lines from current cursor position to movement indicated by arg */
-static const char *join(Vis*, const char *keys, const Arg *arg);
-/* execute arg->s as if it was typed on command prompt */
-static const char *cmd(Vis*, const char *keys, const Arg *arg);
-/* perform last action i.e. action_prev again */
-static const char *repeat(Vis*, const char *keys, const Arg *arg);
-/* replace character at cursor with one read form keyboard */
-static const char *replace(Vis*, const char *keys, const Arg *arg);
-/* create a new cursor on the previous (arg->i < 0) or next (arg->i > 0) line */
-static const char *cursors_new(Vis*, const char *keys, const Arg *arg);
-/* create new cursors in visual mode either at the start (arg-i < 0)
- * or end (arg->i > 0) of the selected lines */
-static const char *cursors_split(Vis*, const char *keys, const Arg *arg);
-/* try to align all cursors on the same column */
-static const char *cursors_align(Vis*, const char *keys, const Arg *arg);
-/* remove all but the primary cursor and their selections */
-static const char *cursors_clear(Vis*, const char *keys, const Arg *arg);
-/* remove the least recently added cursor */
-static const char *cursors_remove(Vis*, const char *keys, const Arg *arg);
-/* select the word the cursor is currently over */
-static const char *cursors_select(Vis*, const char *keys, const Arg *arg);
-/* select the next region matching the current selection */
-static const char *cursors_select_next(Vis*, const char *keys, const Arg *arg);
-/* clear current selection but select next match */
-static const char *cursors_select_skip(Vis*, const char *keys, const Arg *arg);
-/* adjust action.count by arg->i */
-static const char *count(Vis*, const char *keys, const Arg *arg);
-/* move to the action.count-th line or if not given either to the first (arg->i < 0)
- * or last (arg->i > 0) line of file */
-static const char *gotoline(Vis*, const char *keys, const Arg *arg);
-/* set motion type either LINEWISE or CHARWISE via arg->i */
-static const char *motiontype(Vis*, const char *keys, const Arg *arg);
-/* make the current action use the operator indicated by arg->i */
-static const char *operator(Vis*, const char *keys, const Arg *arg);
-/* change case of a file range to upper (arg->i > 0) or lowercase (arg->i < 0) */
-static const char *changecase(Vis*, const char *keys, const Arg *arg);
-/* blocks to read a key and performs movement indicated by arg->i which
- * should be one of MOVE_{RIGHT,LEFT}_{TO,TILL} */
-static const char *movement_key(Vis*, const char *keys, const Arg *arg);
-/* perform the movement as indicated by arg->i */
-static const char *movement(Vis*, const char *keys, const Arg *arg);
-/* let the current operator affect the range indicated by the text object arg->i */
-static const char *textobj(Vis*, const char *keys, const Arg *arg);
-/* move to the other end of selected text */
-static const char *selection_end(Vis*, const char *keys, const Arg *arg);
-/* restore least recently used selection */
-static const char *selection_restore(Vis*, const char *keys, const Arg *arg);
-/* use register indicated by arg->i for the current operator */
-static const char *reg(Vis*, const char *keys, const Arg *arg);
-/* perform arg->i motion with a mark as argument */
-static const char *mark_motion(Vis*, const char *keys, const Arg *arg);
-/* {un,re}do last action, redraw window */
-static const char *undo(Vis*, const char *keys, const Arg *arg);
-static const char *redo(Vis*, const char *keys, const Arg *arg);
-/* earlier, later action chronologically, redraw window */
-static const char *earlier(Vis*, const char *keys, const Arg *arg);
-static const char *later(Vis*, const char *keys, const Arg *arg);
-/* hange/delete from the current cursor position to the end of
- * movement as indicated by arg->i */
-static const char *delete(Vis*, const char *keys, const Arg *arg);
-/* insert register content indicated by arg->i at current cursor position */
-static const char *insert_register(Vis*, const char *keys, const Arg *arg);
-/* show a user prompt to get input with title arg->s */
-static const char *prompt_search(Vis*, const char *keys, const Arg *arg);
-static const char *prompt_cmd(Vis*, const char *keys, const Arg *arg);
-/* evaluate user input at prompt, perform search or execute a command */
-static const char *prompt_enter(Vis*, const char *keys, const Arg *arg);
-/* exit command mode if the last char is deleted */
-static const char *prompt_backspace(Vis*, const char *keys, const Arg *arg);
-/* blocks to read 3 consecutive digits and inserts the corresponding byte value */
-static const char *insert_verbatim(Vis*, const char *keys, const Arg *arg);
-/* scroll window content according to arg->i which can be either PAGE, PAGE_HALF,
- * or an arbitrary number of lines. a multiplier overrides what is given in arg->i.
- * negative values scroll back, positive forward. */
-static const char *wscroll(Vis*, const char *keys, const Arg *arg);
-/* similar to scroll, but do only move window content not cursor position */
-static const char *wslide(Vis*, const char *keys, const Arg *arg);
-/* call editor function as indicated by arg->f */
-static const char *call(Vis*, const char *keys, const Arg *arg);
-/* call window function as indicated by arg->w */
-static const char *window(Vis*, const char *keys, const Arg *arg);
-/* quit editor, discard all changes */
-static const char *quit(Vis*, const char *keys, const Arg *arg);
/** commands to enter at the ':'-prompt */
/* set various runtime options */
@@ -828,9 +754,228 @@ static bool cmd_earlier_later(Vis*, Filerange*, enum CmdOpt, const char *argv[])
static bool cmd_help(Vis*, Filerange*, enum CmdOpt, const char *argv[]);
static void action_reset(Vis*, Action *a);
-static void vis_mode_set(Vis*, Mode *new_mode);
-#include "config.h"
+static void vis_mode_operator_enter(Vis *vis, Mode *old) {
+ vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_OPERATOR_OPTION];
+}
+
+static void vis_mode_operator_leave(Vis *vis, Mode *new) {
+ vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_MOVE];
+}
+
+static void vis_mode_operator_input(Vis *vis, const char *str, size_t len) {
+ /* invalid operator */
+ action_reset(vis, &vis->action);
+ vis_mode_set(vis, vis->mode_prev);
+}
+
+static void vis_mode_visual_enter(Vis *vis, Mode *old) {
+ if (!old->visual) {
+ for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c))
+ view_cursors_selection_start(c);
+ vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_TEXTOBJ];
+ }
+}
+
+static void vis_mode_visual_line_enter(Vis *vis, Mode *old) {
+ if (!old->visual) {
+ for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c))
+ view_cursors_selection_start(c);
+ vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_TEXTOBJ];
+ }
+ vis_motion(vis, MOVE_LINE_END);
+}
+
+static void vis_mode_visual_line_leave(Vis *vis, Mode *new) {
+ if (!new->visual) {
+ view_selections_clear(vis->win->view);
+ vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_MOVE];
+ } else {
+ view_cursor_to(vis->win->view, view_cursor_get(vis->win->view));
+ }
+}
+
+static void vis_mode_visual_leave(Vis *vis, Mode *new) {
+ if (!new->visual) {
+ view_selections_clear(vis->win->view);
+ vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_MOVE];
+ }
+}
+
+static void vis_mode_prompt_input(Vis *vis, const char *str, size_t len) {
+ vis_insert_key(vis, str, len);
+}
+
+static void vis_mode_prompt_enter(Vis *vis, Mode *old) {
+ if (old->isuser && old != &vis_modes[VIS_MODE_PROMPT])
+ vis->mode_before_prompt = old;
+}
+
+static void vis_mode_prompt_leave(Vis *vis, Mode *new) {
+ if (new->isuser)
+ vis_prompt_hide(vis);
+}
+
+static void vis_mode_insert_leave(Vis *vis, Mode *old) {
+ /* make sure we can recover the current state after an editing operation */
+ text_snapshot(vis->win->file->text);
+}
+
+static void vis_mode_insert_idle(Vis *vis) {
+ text_snapshot(vis->win->file->text);
+}
+
+static void vis_mode_insert_input(Vis *vis, const char *str, size_t len) {
+ static size_t oldpos = EPOS;
+ size_t pos = view_cursor_get(vis->win->view);
+ if (pos != oldpos)
+ buffer_truncate(&vis->buffer_repeat);
+ buffer_append(&vis->buffer_repeat, str, len);
+ oldpos = pos + len;
+ action_reset(vis, &vis->action_prev);
+ vis->action_prev.op = &ops[OP_REPEAT_INSERT];
+ vis_insert_key(vis, str, len);
+}
+
+static void vis_mode_replace_leave(Vis *vis, Mode *old) {
+ /* make sure we can recover the current state after an editing operation */
+ text_snapshot(vis->win->file->text);
+}
+
+static void vis_mode_replace_input(Vis *vis, const char *str, size_t len) {
+ static size_t oldpos = EPOS;
+ size_t pos = view_cursor_get(vis->win->view);
+ if (pos != oldpos)
+ buffer_truncate(&vis->buffer_repeat);
+ buffer_append(&vis->buffer_repeat, str, len);
+ oldpos = pos + len;
+ action_reset(vis, &vis->action_prev);
+ vis->action_prev.op = &ops[OP_REPEAT_REPLACE];
+ vis_replace_key(vis, str, len);
+}
+
+/*
+ * the tree of modes currently looks like this. the double line between OPERATOR-OPTION
+ * and OPERATOR is only in effect once an operator is detected. that is when entering the
+ * OPERATOR mode its parent is set to OPERATOR-OPTION which makes {INNER-,}TEXTOBJ
+ * reachable. once the operator is processed (i.e. the OPERATOR mode is left) its parent
+ * mode is reset back to MOVE.
+ *
+ * Similarly the +-ed line between OPERATOR and TEXTOBJ is only active within the visual
+ * modes.
+ *
+ *
+ * BASIC
+ * (arrow keys etc.)
+ * / |
+ * /-------------------/ |
+ * READLINE MOVE
+ * / \ (h,j,k,l ...)
+ * / \ | \-----------------\
+ * / \ | |
+ * INSERT PROMPT OPERATOR ++++ INNER-TEXTOBJ
+ * | (history etc) (d,c,y,p ..) + (i [wsp[]()b<>{}B"'`] )
+ * | | \\ + |
+ * | | \\ + |
+ * REPLACE NORMAL \\ + TEXTOBJ
+ * | \\ + (a [wsp[]()b<>{}B"'`] )
+ * | \\ + + |
+ * | \\ + + |
+ * VISUAL \\ OPERATOR-OPTION
+ * | \\ (v,V)
+ * | \\ //
+ * | \\======//
+ * VISUAL-LINE
+ */
+
+static Mode vis_modes[] = {
+ [VIS_MODE_BASIC] = {
+ .name = "BASIC",
+ .parent = NULL,
+ },
+ [VIS_MODE_MOVE] = {
+ .name = "MOVE",
+ .parent = &vis_modes[VIS_MODE_BASIC],
+ },
+ [VIS_MODE_TEXTOBJ] = {
+ .name = "TEXT-OBJECTS",
+ .parent = &vis_modes[VIS_MODE_MOVE],
+ },
+ [VIS_MODE_OPERATOR_OPTION] = {
+ .name = "OPERATOR-OPTION",
+ .parent = &vis_modes[VIS_MODE_TEXTOBJ],
+ },
+ [VIS_MODE_OPERATOR] = {
+ .name = "OPERATOR",
+ .parent = &vis_modes[VIS_MODE_MOVE],
+ .enter = vis_mode_operator_enter,
+ .leave = vis_mode_operator_leave,
+ .input = vis_mode_operator_input,
+ },
+ [VIS_MODE_NORMAL] = {
+ .name = "NORMAL",
+ .status = "",
+ .help = "",
+ .isuser = true,
+ .parent = &vis_modes[VIS_MODE_OPERATOR],
+ },
+ [VIS_MODE_VISUAL] = {
+ .name = "VISUAL",
+ .status = "--VISUAL--",
+ .help = "",
+ .isuser = true,
+ .parent = &vis_modes[VIS_MODE_OPERATOR],
+ .enter = vis_mode_visual_enter,
+ .leave = vis_mode_visual_leave,
+ .visual = true,
+ },
+ [VIS_MODE_VISUAL_LINE] = {
+ .name = "VISUAL LINE",
+ .status = "--VISUAL LINE--",
+ .help = "",
+ .isuser = true,
+ .parent = &vis_modes[VIS_MODE_VISUAL],
+ .enter = vis_mode_visual_line_enter,
+ .leave = vis_mode_visual_line_leave,
+ .visual = true,
+ },
+ [VIS_MODE_READLINE] = {
+ .name = "READLINE",
+ .parent = &vis_modes[VIS_MODE_BASIC],
+ },
+ [VIS_MODE_PROMPT] = {
+ .name = "PROMPT",
+ .help = "",
+ .isuser = true,
+ .parent = &vis_modes[VIS_MODE_READLINE],
+ .input = vis_mode_prompt_input,
+ .enter = vis_mode_prompt_enter,
+ .leave = vis_mode_prompt_leave,
+ },
+ [VIS_MODE_INSERT] = {
+ .name = "INSERT",
+ .status = "--INSERT--",
+ .help = "",
+ .isuser = true,
+ .parent = &vis_modes[VIS_MODE_READLINE],
+ .leave = vis_mode_insert_leave,
+ .input = vis_mode_insert_input,
+ .idle = vis_mode_insert_idle,
+ .idle_timeout = 3,
+ },
+ [VIS_MODE_REPLACE] = {
+ .name = "REPLACE",
+ .status = "--REPLACE--",
+ .help = "",
+ .isuser = true,
+ .parent = &vis_modes[VIS_MODE_INSERT],
+ .leave = vis_mode_replace_leave,
+ .input = vis_mode_replace_input,
+ .idle = vis_mode_insert_idle,
+ .idle_timeout = 3,
+ },
+};
+
static Mode *mode_get(Vis *vis, enum VisMode mode) {
if (mode < LENGTH(vis_modes))
@@ -847,10 +992,9 @@ bool vis_mode_map(Vis *vis, enum VisMode modeid, const char *name, KeyBinding *b
return mode && map_put(mode->bindings, name, binding);
}
-static bool mode_bindings(Mode *mode, KeyBinding **bindings) {
- if (!mode->bindings)
- mode->bindings = map_new();
- if (!mode->bindings)
+bool vis_mode_bindings(Vis *vis, enum VisMode modeid, KeyBinding **bindings) {
+ Mode *mode = mode_get(vis, modeid);
+ if (!mode)
return false;
bool success = true;
for (KeyBinding *kb = *bindings; kb->key; kb++) {
@@ -875,7 +1019,6 @@ bool vis_action_register(Vis *vis, KeyAction *action) {
static const char *getkey(Vis*);
static void action_do(Vis*, Action *a);
-static bool exec_command(Vis *vis, char type, const char *cmdline);
/** operator implementations of type: void (*op)(OperatorContext*) */
@@ -940,7 +1083,7 @@ static size_t op_put(Vis *vis, Text *txt, OperatorContext *c) {
return pos;
}
-static const char *expand_tab(Vis *vis) {
+const char *vis_expandtab(Vis *vis) {
static char spaces[9];
int tabwidth = tabwidth_get(vis);
tabwidth = MIN(tabwidth, LENGTH(spaces) - 1);
@@ -952,7 +1095,7 @@ static const char *expand_tab(Vis *vis) {
static size_t op_shift_right(Vis *vis, Text *txt, OperatorContext *c) {
size_t pos = text_line_begin(txt, c->range.end), prev_pos;
- const char *tab = expand_tab(vis);
+ const char *tab = vis_expandtab(vis);
size_t tablen = strlen(tab);
/* if range ends at the begin of a line, skip line break */
@@ -1236,577 +1379,9 @@ static size_t window_jumplist_prev(Vis *vis, Win *win, size_t cur) {
}
return cur;
}
-/** key bindings functions */
-
-static const char *nop(Vis *vis, const char *keys, const Arg *arg) {
- return keys;
-}
-
-static const char *key2macro(Vis *vis, const char *keys, enum VisMacro *macro) {
- *macro = VIS_MACRO_INVALID;
- if (keys[0] >= 'a' && keys[0] <= 'z')
- *macro = keys[0] - 'a';
- else if (keys[0] == '@')
- *macro = VIS_MACRO_LAST_RECORDED;
- else if (keys[0] == '\0')
- return NULL;
- return keys+1;
-}
-
-static const char *macro_record(Vis *vis, const char *keys, const Arg *arg) {
- if (vis_macro_record_stop(vis))
- return keys;
- enum VisMacro macro;
- keys = key2macro(vis, keys, &macro);
- vis_macro_record(vis, macro);
- vis_draw(vis);
- return keys;
-}
-
-static const char *macro_replay(Vis *vis, const char *keys, const Arg *arg) {
- enum VisMacro macro;
- keys = key2macro(vis, keys, &macro);
- vis_macro_replay(vis, macro);
- return keys;
-}
-
-static const char *suspend(Vis *vis, const char *keys, const Arg *arg) {
- vis_suspend(vis);
- return keys;
-}
-
-static const char *repeat(Vis *vis, const char *keys, const Arg *arg) {
- vis_repeat(vis);
- return keys;
-}
-
-static const char *cursors_new(Vis *vis, const char *keys, const Arg *arg) {
- View *view = vis->win->view;
- Text *txt = vis->win->file->text;
- size_t pos = view_cursor_get(view);
- if (arg->i > 0)
- pos = text_line_down(txt, pos);
- else if (arg->i < 0)
- pos = text_line_up(txt, pos);
- Cursor *cursor = view_cursors_new(view);
- if (cursor)
- view_cursors_to(cursor, pos);
- return keys;
-}
-
-static const char *cursors_split(Vis *vis, const char *keys, const Arg *arg) {
- vis->action.arg = *arg;
- vis_operator(vis, OP_CURSOR);
- return keys;
-}
-
-static const char *cursors_align(Vis *vis, const char *keys, const Arg *arg) {
- View *view = vis->win->view;
- Text *txt = vis->win->file->text;
- int mincol = INT_MAX;
- for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
- size_t pos = view_cursors_pos(c);
- int col = text_line_char_get(txt, pos);
- if (col < mincol)
- mincol = col;
- }
- for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) {
- size_t pos = view_cursors_pos(c);
- size_t col = text_line_char_set(txt, pos, mincol);
- view_cursors_to(c, col);
- }
- return keys;
-}
-
-static const char *cursors_clear(Vis *vis, const char *keys, const Arg *arg) {
- View *view = vis->win->view;
- if (view_cursors_count(view) > 1)
- view_cursors_clear(view);
- else
- view_cursors_selection_clear(view_cursor(view));
- return keys;
-}
-
-static const char *cursors_select(Vis *vis, const char *keys, const Arg *arg) {
- Text *txt = vis->win->file->text;
- View *view = vis->win->view;
- for (Cursor *cursor = view_cursors(view); cursor; cursor = view_cursors_next(cursor)) {
- Filerange sel = view_cursors_selection_get(cursor);
- Filerange word = text_object_word(txt, view_cursors_pos(cursor));
- if (!text_range_valid(&sel) && text_range_valid(&word)) {
- view_cursors_selection_set(cursor, &word);
- view_cursors_to(cursor, text_char_prev(txt, word.end));
- }
- }
- vis_mode_switch(vis, VIS_MODE_VISUAL);
- return keys;
-}
-
-static const char *cursors_select_next(Vis *vis, const char *keys, const Arg *arg) {
- Text *txt = vis->win->file->text;
- View *view = vis->win->view;
- Cursor *cursor = view_cursor(view);
- Filerange sel = view_cursors_selection_get(cursor);
- if (!text_range_valid(&sel))
- return keys;
-
- size_t len = text_range_size(&sel);
- char *buf = malloc(len+1);
- if (!buf)
- return keys;
- len = text_bytes_get(txt, sel.start, len, buf);
- buf[len] = '\0';
- Filerange word = text_object_word_find_next(txt, sel.end, buf);
- free(buf);
-
- if (text_range_valid(&word)) {
- cursor = view_cursors_new(view);
- if (!cursor)
- return keys;
- view_cursors_selection_set(cursor, &word);
- view_cursors_to(cursor, text_char_prev(txt, word.end));
- }
- return keys;
-}
-
-static const char *cursors_select_skip(Vis *vis, const char *keys, const Arg *arg) {
- View *view = vis->win->view;
- Cursor *cursor = view_cursor(view);
- keys = cursors_select_next(vis, keys, arg);
- if (cursor != view_cursor(view))
- view_cursors_dispose(cursor);
- return keys;
-}
-
-static const char *cursors_remove(Vis *vis, const char *keys, const Arg *arg) {
- View *view = vis->win->view;
- view_cursors_dispose(view_cursor(view));
- return keys;
-}
-
-static const char *replace(Vis *vis, const char *keys, const Arg *arg) {
- if (!keys[0])
- return NULL;
- const char *next = vis_key_next(vis, keys);
- size_t len = next - keys;
- action_reset(vis, &vis->action_prev);
- vis->action_prev.op = &ops[OP_REPEAT_REPLACE];
- buffer_put(&vis->buffer_repeat, keys, len);
- vis_replace_key(vis, keys, len);
- text_snapshot(vis->win->file->text);
- return next;
-}
-
-static const char *count(Vis *vis, const char *keys, const Arg *arg) {
- int digit = keys[-1] - '0';
- int count = vis_count_get(vis);
- if (0 <= digit && digit <= 9) {
- if (digit == 0 && count == 0)
- vis_motion(vis, MOVE_LINE_BEGIN);
- vis_count_set(vis, count * 10 + digit);
- }
- return keys;
-}
-
-static const char *gotoline(Vis *vis, const char *keys, const Arg *arg) {
- if (vis_count_get(vis))
- vis_motion(vis, MOVE_LINE);
- else if (arg->i < 0)
- vis_motion(vis, MOVE_FILE_BEGIN);
- else
- vis_motion(vis, MOVE_FILE_END);
- return keys;
-}
-
-static const char *motiontype(Vis *vis, const char *keys, const Arg *arg) {
- vis_motion_type(vis, arg->i);
- return keys;
-}
-
-static const char *operator(Vis *vis, const char *keys, const Arg *arg) {
- vis_operator(vis, arg->i);
- return keys;
-}
-
-static const char *changecase(Vis *vis, const char *keys, const Arg *arg) {
- vis->action.arg = *arg;
- vis_operator(vis, OP_CASE_CHANGE);
- return keys;
-}
-
-static const char *movement_key(Vis *vis, const char *keys, const Arg *arg) {
- if (!keys[0])
- return NULL;
- char key[32];
- const char *next = vis_key_next(vis, keys);
- strncpy(key, keys, next - keys + 1);
- key[sizeof(key)-1] = '\0';
- vis_motion(vis, arg->i, key);
- return next;
-}
-
-static const char *movement(Vis *vis, const char *keys, const Arg *arg) {
- vis_motion(vis, arg->i);
- return keys;
-}
-
-static const char *textobj(Vis *vis, const char *keys, const Arg *arg) {
- vis_textobject(vis, arg->i);
- return keys;
-}
-
-static const char *selection_end(Vis *vis, const char *keys, const Arg *arg) {
- for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c))
- view_cursors_selection_swap(c);
- return keys;
-}
-
-static const char *selection_restore(Vis *vis, const char *keys, const Arg *arg) {
- for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c))
- view_cursors_selection_restore(c);
- vis_mode_switch(vis, VIS_MODE_VISUAL);
- return keys;
-}
-static const char *key2register(Vis *vis, const char *keys, enum VisRegister *reg) {
- *reg = VIS_REGISTER_INVALID;
- if (!keys[0])
- return NULL;
- if (keys[0] >= 'a' && keys[0] <= 'z')
- *reg = keys[0] - 'a';
- return keys+1;
-}
-
-static const char *reg(Vis *vis, const char *keys, const Arg *arg) {
- enum VisRegister reg;
- keys = key2register(vis, keys, &reg);
- vis_register_set(vis, reg);
- return keys;
-}
-
-static const char *key2mark(Vis *vis, const char *keys, int *mark) {
- *mark = VIS_MARK_INVALID;
- if (!keys[0])
- return NULL;
- if (keys[0] >= 'a' && keys[0] <= 'z')
- *mark = keys[0] - 'a';
- else if (keys[0] == '<')
- *mark = MARK_SELECTION_START;
- else if (keys[0] == '>')
- *mark = MARK_SELECTION_END;
- return keys+1;
-}
-
-static const char *mark_set(Vis *vis, const char *keys, const Arg *arg) {
- int mark;
- keys = key2mark(vis, keys, &mark);
- vis_mark_set(vis, mark, view_cursor_get(vis->win->view));
- return keys;
-}
-
-static const char *mark_motion(Vis *vis, const char *keys, const Arg *arg) {
- int mark;
- keys = key2mark(vis, keys, &mark);
- vis_motion(vis, arg->i, mark);
- return keys;
-}
-
-static const char *undo(Vis *vis, const char *keys, const Arg *arg) {
- size_t pos = text_undo(vis->win->file->text);
- if (pos != EPOS) {
- View *view = vis->win->view;
- if (view_cursors_count(view) == 1)
- view_cursor_to(view, pos);
- /* redraw all windows in case some display the same file */
- vis_draw(vis);
- }
- return keys;
-}
-
-static const char *redo(Vis *vis, const char *keys, const Arg *arg) {
- size_t pos = text_redo(vis->win->file->text);
- if (pos != EPOS) {
- View *view = vis->win->view;
- if (view_cursors_count(view) == 1)
- view_cursor_to(view, pos);
- /* redraw all windows in case some display the same file */
- vis_draw(vis);
- }
- return keys;
-}
-
-static const char *earlier(Vis *vis, const char *keys, const Arg *arg) {
- size_t pos = text_earlier(vis->win->file->text, MAX(vis_count_get(vis), 1));
- if (pos != EPOS) {
- view_cursor_to(vis->win->view, pos);
- /* redraw all windows in case some display the same file */
- vis_draw(vis);
- }
- return keys;
-}
-
-static const char *later(Vis *vis, const char *keys, const Arg *arg) {
- size_t pos = text_later(vis->win->file->text, MAX(vis_count_get(vis), 1));
- if (pos != EPOS) {
- view_cursor_to(vis->win->view, pos);
- /* redraw all windows in case some display the same file */
- vis_draw(vis);
- }
- return keys;
-}
-
-static const char *delete(Vis *vis, const char *keys, const Arg *arg) {
- vis_operator(vis, OP_DELETE);
- vis_motion(vis, arg->i);
- return keys;
-}
-
-static const char *insert_register(Vis *vis, const char *keys, const Arg *arg) {
- enum VisRegister regid;
- keys = key2register(vis, keys, &regid);
- Register *reg = vis_register_get(vis, regid);
- if (reg) {
- int pos = view_cursor_get(vis->win->view);
- vis_insert(vis, pos, reg->data, reg->len);
- view_cursor_to(vis->win->view, pos + reg->len);
- }
- return keys;
-}
-
-static const char *prompt_search(Vis *vis, const char *keys, const Arg *arg) {
- vis_prompt_show(vis, arg->s, "");
- vis_mode_switch(vis, VIS_MODE_PROMPT);
- return keys;
-}
-
-static const char *prompt_cmd(Vis *vis, const char *keys, const Arg *arg) {
- vis_prompt_show(vis, ":", arg->s);
- vis_mode_switch(vis, VIS_MODE_PROMPT);
- return keys;
-}
-
-static const char *prompt_enter(Vis *vis, const char *keys, const Arg *arg) {
- char *s = vis_prompt_get(vis);
- /* it is important to switch back to the previous mode, which hides
- * the prompt and more importantly resets vis->win to the currently
- * focused editor window *before* anything is executed which depends
- * on vis->win.
- */
- vis_mode_set(vis, vis->mode_before_prompt);
- if (s && *s && exec_command(vis, vis->prompt_type, s) && vis->running)
- vis_mode_switch(vis, VIS_MODE_NORMAL);
- free(s);
- vis_draw(vis);
- return keys;
-}
-
-static const char *prompt_backspace(Vis *vis, const char *keys, const Arg *arg) {
- char *cmd = vis_prompt_get(vis);
- if (!cmd || !*cmd)
- prompt_enter(vis, keys, NULL);
- else
- delete(vis, keys, &(const Arg){ .i = MOVE_CHAR_PREV });
- free(cmd);
- return keys;
-}
-
-static const char *insert_verbatim(Vis *vis, const char *keys, const Arg *arg) {
- Rune rune = 0;
- char buf[4], type = keys[0];
- int len = 0, count = 0, base;
- switch (type) {
- case '\0':
- return NULL;
- case 'o':
- case 'O':
- count = 3;
- base = 8;
- break;
- case 'U':
- count = 4;
- /* fall through */
- case 'u':
- count += 4;
- base = 16;
- break;
- case 'x':
- case 'X':
- count = 2;
- base = 16;
- break;
- default:
- if (type < '0' || type > '9')
- return keys;
- rune = type - '0';
- count = 2;
- base = 10;
- break;
- }
-
- for (keys++; keys[0] && count > 0; keys++, count--) {
- int v = 0;
- if (base == 8 && '0' <= keys[0] && keys[0] <= '7') {
- v = keys[0] - '0';
- } else if ((base == 10 || base == 16) && '0' <= keys[0] && keys[0] <= '9') {
- v = keys[0] - '0';
- } else if (base == 16 && 'a' <= keys[0] && keys[0] <= 'f') {
- v = 10 + keys[0] - 'a';
- } else if (base == 16 && 'A' <= keys[0] && keys[0] <= 'F') {
- v = 10 + keys[0] - 'A';
- } else {
- count = 0;
- break;
- }
- rune = rune * base + v;
- }
-
- if (count > 0)
- return NULL;
-
- if (type == 'u' || type == 'U') {
- len = runetochar(buf, &rune);
- } else {
- buf[0] = rune;
- len = 1;
- }
-
- if (len > 0) {
- size_t pos = view_cursor_get(vis->win->view);
- vis_insert(vis, pos, buf, len);
- view_cursor_to(vis->win->view, pos + len);
- }
- return keys;
-}
-
-static const char *quit(Vis *vis, const char *keys, const Arg *arg) {
- vis->running = false;
- return keys;
-}
-
-static const char *cmd(Vis *vis, const char *keys, const Arg *arg) {
- vis_cmd(vis, arg->s);
- return keys;
-}
-
-static int argi2lines(Vis *vis, const Arg *arg) {
- switch (arg->i) {
- case -PAGE:
- case +PAGE:
- return view_height_get(vis->win->view);
- case -PAGE_HALF:
- case +PAGE_HALF:
- return view_height_get(vis->win->view)/2;
- default:
- if (vis_count_get(vis) > 0)
- return vis_count_get(vis);
- return arg->i < 0 ? -arg->i : arg->i;
- }
-}
-
-static const char *wscroll(Vis *vis, const char *keys, const Arg *arg) {
- if (arg->i >= 0)
- view_scroll_down(vis->win->view, argi2lines(vis, arg));
- else
- view_scroll_up(vis->win->view, argi2lines(vis, arg));
- return keys;
-}
-
-static const char *wslide(Vis *vis, const char *keys, const Arg *arg) {
- if (arg->i >= 0)
- view_slide_down(vis->win->view, argi2lines(vis, arg));
- else
- view_slide_up(vis->win->view, argi2lines(vis, arg));
- return keys;
-}
-
-static const char *call(Vis *vis, const char *keys, const Arg *arg) {
- arg->f(vis);
- return keys;
-}
-
-static const char *window(Vis *vis, const char *keys, const Arg *arg) {
- arg->w(vis->win->view);
- return keys;
-}
-
-static const char *insert(Vis *vis, const char *keys, const Arg *arg) {
- vis_insert_key(vis, arg->s, arg->s ? strlen(arg->s) : 0);
- return keys;
-}
-
-static const char *insert_tab(Vis *vis, const char *keys, const Arg *arg) {
- insert(vis, keys, &(const Arg){ .s = expand_tab(vis) });
- return keys;
-}
-
-static void copy_indent_from_previous_line(Win *win) {
- View *view = win->view;
- Text *text = win->file->text;
- size_t pos = view_cursor_get(view);
- size_t prev_line = text_line_prev(text, pos);
- if (pos == prev_line)
- return;
- size_t begin = text_line_begin(text, prev_line);
- size_t start = text_line_start(text, begin);
- size_t len = start-begin;
- char *buf = malloc(len);
- if (!buf)
- return;
- len = text_bytes_get(text, begin, len, buf);
- vis_insert_key(win->editor, buf, len);
- free(buf);
-}
-
-static const char *insert_newline(Vis *vis, const char *keys, const Arg *arg) {
- const char *nl;
- switch (text_newline_type(vis->win->file->text)) {
- case TEXT_NEWLINE_CRNL:
- nl = "\r\n";
- break;
- default:
- nl = "\n";
- break;
- }
-
- insert(vis, keys, &(const Arg){ .s = nl });
-
- if (vis->autoindent)
- copy_indent_from_previous_line(vis->win);
- return keys;
-}
-
-static const char *put(Vis *vis, const char *keys, const Arg *arg) {
- vis->action.arg = *arg;
- vis_operator(vis, OP_PUT);
- action_do(vis, &vis->action);
- return keys;
-}
-
-static const char *openline(Vis *vis, const char *keys, const Arg *arg) {
- if (arg->i == MOVE_LINE_NEXT) {
- vis_motion(vis, MOVE_LINE_END);
- insert_newline(vis, keys, NULL);
- } else {
- vis_motion(vis, MOVE_LINE_BEGIN);
- insert_newline(vis, keys, NULL);
- vis_motion(vis, MOVE_LINE_PREV);
- }
- vis_mode_switch(vis, VIS_MODE_INSERT);
- return keys;
-}
-
-static const char *join(Vis *vis, const char *keys, const Arg *arg) {
- int count = vis_count_get(vis);
- if (count)
- vis_count_set(vis, count-1);
- vis_operator(vis, OP_JOIN);
- vis_motion(vis, arg->i);
- return keys;
-}
-
-static const char *switchmode(Vis *vis, const char *keys, const Arg *arg) {
- vis_mode_switch(vis, arg->i);
- return keys;
+static size_t window_nop(Vis *vis, Win *win, size_t pos) {
+ return pos;
}
/** action processing: execut the operator / movement / text object */
@@ -1961,7 +1536,7 @@ static void action_reset(Vis *vis, Action *a) {
a->reg = NULL;
}
-static void vis_mode_set(Vis *vis, Mode *new_mode) {
+void vis_mode_set(Vis *vis, Mode *new_mode) {
if (vis->mode == new_mode)
return;
if (vis->mode->leave)
@@ -1976,6 +1551,36 @@ static void vis_mode_set(Vis *vis, Mode *new_mode) {
/** ':'-command implementations */
+
+/* command recognized at the ':'-prompt. commands are found using a unique
+ * prefix match. that is if a command should be available under an abbreviation
+ * which is a prefix for another command it has to be added as an alias. the
+ * long human readable name should always come first */
+static Command cmds[] = {
+ /* command name / optional alias, function, options */
+ { { "bdelete" }, cmd_bdelete, CMD_OPT_FORCE },
+ { { "edit" }, cmd_edit, CMD_OPT_FORCE },
+ { { "help" }, cmd_help, CMD_OPT_NONE },
+ { { "new" }, cmd_new, CMD_OPT_NONE },
+ { { "open" }, cmd_open, CMD_OPT_NONE },
+ { { "qall" }, cmd_qall, CMD_OPT_FORCE },
+ { { "quit", "q" }, cmd_quit, CMD_OPT_FORCE },
+ { { "read", }, cmd_read, CMD_OPT_FORCE },
+ { { "saveas" }, cmd_saveas, CMD_OPT_FORCE },
+ { { "set", }, cmd_set, CMD_OPT_ARGS },
+ { { "split" }, cmd_split, CMD_OPT_NONE },
+ { { "substitute", "s" }, cmd_substitute, CMD_OPT_NONE },
+ { { "vnew" }, cmd_vnew, CMD_OPT_NONE },
+ { { "vsplit", }, cmd_vsplit, CMD_OPT_NONE },
+ { { "wq", }, cmd_wq, CMD_OPT_FORCE },
+ { { "write", "w" }, cmd_write, CMD_OPT_FORCE },
+ { { "xit", }, cmd_xit, CMD_OPT_FORCE },
+ { { "earlier" }, cmd_earlier_later, CMD_OPT_NONE },
+ { { "later" }, cmd_earlier_later, CMD_OPT_NONE },
+ { { "!", }, cmd_filter, CMD_OPT_NONE },
+ { /* array terminator */ },
+};
+
/* parse human-readable boolean value in s. If successful, store the result in
* outval and return true. Else return false and leave outval alone. */
static bool parse_bool(const char *s, bool *outval) {
@@ -2111,7 +1716,7 @@ static bool cmd_set(Vis *vis, Filerange *range, enum CmdOpt cmdopt, const char *
return true;
}
- for (Syntax *syntax = syntaxes; syntax && syntax->name; syntax++) {
+ for (Syntax *syntax = vis->syntaxes; syntax && syntax->name; syntax++) {
if (!strcasecmp(syntax->name, argv[2])) {
view_syntax_set(vis->win->view, syntax);
return true;
@@ -2277,7 +1882,7 @@ static bool cmd_quit(Vis *vis, Filerange *range, enum CmdOpt opt, const char *ar
}
vis_window_close(vis->win);
if (!vis->windows)
- quit(vis, NULL, NULL);
+ vis_exit(vis, EXIT_SUCCESS);
return true;
}
@@ -2301,7 +1906,7 @@ static bool cmd_bdelete(Vis *vis, Filerange *range, enum CmdOpt opt, const char
vis_window_close(win);
}
if (!vis->windows)
- quit(vis, NULL, NULL);
+ vis_exit(vis, EXIT_SUCCESS);
return true;
}
@@ -2312,7 +1917,7 @@ static bool cmd_qall(Vis *vis, Filerange *range, enum CmdOpt opt, const char *ar
vis_window_close(win);
}
if (!vis->windows)
- quit(vis, NULL, NULL);
+ vis_exit(vis, EXIT_SUCCESS);
else
info_unsaved_changes(vis);
return vis->windows == NULL;
@@ -2920,22 +2525,17 @@ bool vis_cmd(Vis *vis, const char *cmdline) {
return true;
}
-static bool exec_command(Vis *vis, char type, const char *cmd) {
+bool vis_prompt_cmd(Vis *vis, char type, const char *cmd) {
if (!cmd || !cmd[0])
return true;
switch (type) {
case '/':
+ return vis_motion(vis, MOVE_SEARCH_FORWARD, cmd);
case '?':
- if (text_regex_compile(vis->search_pattern, cmd, REG_EXTENDED)) {
- action_reset(vis, &vis->action);
- return false;
- }
- vis_motion(vis, type == '/' ? MOVE_SEARCH_FORWARD : MOVE_SEARCH_BACKWARD);
- return true;
+ return vis_motion(vis, MOVE_SEARCH_BACKWARD, cmd);
case '+':
case ':':
- if (vis_cmd(vis, cmd))
- return true;
+ return vis_cmd(vis, cmd);
}
return false;
}
@@ -3101,7 +2701,7 @@ static void vis_args(Vis *vis, int argc, char *argv[]) {
} else if (!vis_window_new(vis, argv[i])) {
vis_die(vis, "Can not load `%s': %s\n", argv[i], strerror(errno));
} else if (cmd) {
- exec_command(vis, cmd[0], cmd+1);
+ vis_prompt_cmd(vis, cmd[0], cmd+1);
cmd = NULL;
}
}
@@ -3129,11 +2729,11 @@ static void vis_args(Vis *vis, int argc, char *argv[]) {
vis_die(vis, "Can not create empty buffer\n");
}
if (cmd)
- exec_command(vis, cmd[0], cmd+1);
+ vis_prompt_cmd(vis, cmd[0], cmd+1);
}
}
-void vis_run(Vis *vis, int argc, char *argv[]) {
+int vis_run(Vis *vis, int argc, char *argv[]) {
vis_args(vis, argc, argv);
struct timespec idle = { .tv_nsec = 0 }, *timeout = NULL;
@@ -3142,6 +2742,7 @@ void vis_run(Vis *vis, int argc, char *argv[]) {
sigemptyset(&emptyset);
vis_draw(vis);
vis->running = true;
+ vis->exit_status = EXIT_SUCCESS;
sigsetjmp(vis->sigbus_jmpbuf, 1);
@@ -3196,31 +2797,7 @@ void vis_run(Vis *vis, int argc, char *argv[]) {
if (vis->mode->idle)
timeout = &idle;
}
-}
-
-Vis *vis_new(Ui *ui) {
- Vis *vis = vis_new0(ui);
- if (!vis)
- return NULL;
-
- for (int i = 0; i < LENGTH(vis_modes); i++) {
- Mode *mode = &vis_modes[i];
- if (!mode_bindings(mode, &mode->default_bindings))
- vis_die(vis, "Could not load bindings for mode: %s\n", mode->name);
- }
-
- vis->mode_prev = vis->mode = &vis_modes[VIS_MODE_NORMAL];
-
- if (!vis_syntax_load(vis, syntaxes))
- vis_die(vis, "Could not load syntax highlighting definitions\n");
-
- for (int i = 0; i < LENGTH(vis_action); i++) {
- KeyAction *action = &vis_action[i];
- if (!vis_action_register(vis, action))
- vis_die(vis, "Could not register action: %s\n", action->name);
- }
-
- return vis;
+ return vis->exit_status;
}
void vis_operator(Vis *vis, enum VisOperator opi) {
@@ -3247,7 +2824,7 @@ void vis_mode_switch(Vis *vis, enum VisMode mode) {
vis_mode_set(vis, &vis_modes[mode]);
}
-void vis_motion(Vis *vis, enum VisMotion motion, ...) {
+bool vis_motion(Vis *vis, enum VisMotion motion, ...) {
va_list ap;
va_start(ap, motion);
@@ -3260,6 +2837,20 @@ void vis_motion(Vis *vis, enum VisMotion motion, ...) {
if (vis->action.op == &ops[OP_CHANGE])
motion = MOVE_LONGWORD_END_NEXT;
break;
+ case MOVE_SEARCH_FORWARD:
+ case MOVE_SEARCH_BACKWARD:
+ {
+ const char *pattern = va_arg(ap, char*);
+ if (text_regex_compile(vis->search_pattern, pattern, REG_EXTENDED)) {
+ action_reset(vis, &vis->action);
+ goto err;
+ }
+ if (motion == MOVE_SEARCH_FORWARD)
+ motion = MOVE_SEARCH_NEXT;
+ else
+ motion = MOVE_SEARCH_PREV;
+ break;
+ }
case MOVE_RIGHT_TO:
case MOVE_LEFT_TO:
case MOVE_RIGHT_TILL:
@@ -3267,7 +2858,7 @@ void vis_motion(Vis *vis, enum VisMotion motion, ...) {
{
const char *key = va_arg(ap, char*);
if (!key)
- goto out;
+ goto err;
strncpy(vis->search_char, key, sizeof(vis->search_char));
vis->search_char[sizeof(vis->search_char)-1] = '\0';
vis->last_totill = motion;
@@ -3275,7 +2866,7 @@ void vis_motion(Vis *vis, enum VisMotion motion, ...) {
}
case MOVE_TOTILL_REPEAT:
if (!vis->last_totill)
- goto out;
+ goto err;
motion = vis->last_totill;
break;
case MOVE_TOTILL_REVERSE:
@@ -3293,7 +2884,7 @@ void vis_motion(Vis *vis, enum VisMotion motion, ...) {
motion = MOVE_RIGHT_TILL;
break;
default:
- goto out;
+ goto err;
}
break;
case MOVE_MARK:
@@ -3303,19 +2894,20 @@ void vis_motion(Vis *vis, enum VisMotion motion, ...) {
if (MARK_a <= mark && mark < VIS_MARK_INVALID)
vis->action.mark = mark;
else
- goto out;
+ goto err;
break;
}
default:
break;
}
-
vis->action.movement = &moves[motion];
+ va_end(ap);
action_do(vis, &vis->action);
-out:
+ return true;
+err:
va_end(ap);
-
+ return false;
}
void vis_textobject(Vis *vis, enum VisTextObject textobj) {
@@ -3404,3 +2996,12 @@ Register *vis_register_get(Vis *vis, enum VisRegister reg) {
return &vis->registers[reg];
return NULL;
}
+
+void vis_exit(Vis *vis, int status) {
+ vis->running = false;
+ vis->exit_status = status;
+}
+
+const char *vis_mode_status(Vis *vis) {
+ return vis->mode->status;
+}
diff --git a/vis.h b/vis.h
index 71a0b78..5e371e3 100644
--- a/vis.h
+++ b/vis.h
@@ -97,7 +97,8 @@ void vis_insert(Vis*, size_t pos, const char *data, size_t len);
void vis_delete(Vis*, size_t pos, size_t len);
void vis_replace(Vis*, size_t pos, const char *data, size_t len);
-void vis_run(Vis*, int argc, char *argv[]);
+int vis_run(Vis*, int argc, char *argv[]);
+void vis_exit(Vis*, int status);
void vis_die(Vis*, const char *msg, ...);
enum VisMode {
@@ -119,6 +120,11 @@ enum VisMode {
void vis_mode_switch(Vis*, enum VisMode);
bool vis_mode_map(Vis*, enum VisMode, const char *name, KeyBinding*);
bool vis_mode_unmap(Vis*, enum VisMode, const char *name);
+bool vis_mode_bindings(Vis*, enum VisMode, KeyBinding **bindings);
+const char *vis_mode_status(Vis*);
+/* TODO: temporary */
+typedef struct Mode Mode;
+void vis_mode_set(Vis*, Mode*);
bool vis_action_register(Vis*, KeyAction*);
@@ -136,6 +142,18 @@ enum VisOperator {
OP_CURSOR,
};
+/* TODO: overhaul repeatable infrastructure:
+ * - put is not really an operator, but should still be repeatable
+ * and respect count
+ " - review OP_REPEAT_{REPLACE,INSERT}
+ */
+enum {
+ PUT_AFTER,
+ PUT_AFTER_END,
+ PUT_BEFORE,
+ PUT_BEFORE_END,
+};
+
void vis_operator(Vis*, enum VisOperator);
enum VisMotion {
@@ -186,8 +204,8 @@ enum VisMotion {
MOVE_MARK_LINE,
MOVE_SEARCH_WORD_FORWARD,
MOVE_SEARCH_WORD_BACKWARD,
- MOVE_SEARCH_FORWARD,
- MOVE_SEARCH_BACKWARD,
+ MOVE_SEARCH_NEXT,
+ MOVE_SEARCH_PREV,
MOVE_WINDOW_LINE_TOP,
MOVE_WINDOW_LINE_MIDDLE,
MOVE_WINDOW_LINE_BOTTOM,
@@ -195,19 +213,22 @@ enum VisMotion {
MOVE_CHANGELIST_PREV,
MOVE_JUMPLIST_NEXT,
MOVE_JUMPLIST_PREV,
+ MOVE_NOP,
/* pseudo motions: keep them at the end to save space in array definition */
MOVE_TOTILL_REPEAT,
MOVE_TOTILL_REVERSE,
+ MOVE_SEARCH_FORWARD,
+ MOVE_SEARCH_BACKWARD,
};
-void vis_motion(Vis*, enum VisMotion, ...);
+bool vis_motion(Vis*, enum VisMotion, ...);
int vis_count_get(Vis*);
void vis_count_set(Vis*, int count);
enum VisMotionType {
- VIS_MOTION_LINEWISE = 1 << 0,
- VIS_MOTION_CHARWISE = 1 << 1,
+ VIS_MOTIONTYPE_LINEWISE = 1 << 0,
+ VIS_MOTIONTYPE_CHARWISE = 1 << 1,
};
void vis_motion_type(Vis *vis, enum VisMotionType);
@@ -300,11 +321,16 @@ Register *vis_register_get(Vis*, enum VisRegister);
void vis_repeat(Vis*);
-bool vis_cmd(Vis*, const char *cmdline);
+/* execute a :-command (call without without leading ':') */
+bool vis_cmd(Vis*, const char *cmd);
+/* TODO temporary. type is either '/', '?', '+', or ':' */
+bool vis_prompt_cmd(Vis *vis, char type, const char *cmd);
const char *vis_key_next(Vis*, const char *keys);
const char *vis_keys(Vis*, const char *input);
+const char *vis_expandtab(Vis*);
+
bool vis_signal_handler(Vis*, int signum, const siginfo_t *siginfo,
const void *context);
@@ -324,33 +350,6 @@ typedef struct { /** collects all information until an operator is e
Arg arg;
} Action;
-/* a mode contains a set of key bindings which are currently valid.
- *
- * each mode can specify one parent mode which is consultated if a given key
- * is not found in the current mode. hence the modes form a tree which is
- * searched from the current mode up towards the root mode until a valid binding
- * is found.
- *
- * if no binding is found, mode->input(...) is called and the user entered
- * keys are passed as argument. this is used to change the document content.
- */
-typedef struct Mode Mode;
-struct Mode {
- Mode *parent; /* if no match is found in this mode, search will continue there */
- Map *bindings;
- KeyBinding *default_bindings;
- const char *name; /* descriptive, user facing name of the mode */
- const char *status; /* name displayed in the window status bar */
- const char *help; /* short description used by :help */
- bool isuser; /* whether this is a user or internal mode */
- void (*enter)(Vis*, Mode *old); /* called right before the mode becomes active */
- void (*leave)(Vis*, Mode *new); /* called right before the mode becomes inactive */
- void (*input)(Vis*, const char*, size_t); /* called whenever a key is not found in this mode and all its parent modes */
- void (*idle)(Vis*); /* called whenever a certain idle time i.e. without any user input elapsed */
- time_t idle_timeout; /* idle time in seconds after which the registered function will be called */
- bool visual; /* whether text selection is possible in this mode */
-};
-
struct File {
Text *text;
const char *name;
@@ -408,6 +407,7 @@ struct Vis {
Mode *mode_prev; /* previsouly active user mode */
Mode *mode_before_prompt; /* user mode which was active before entering prompt */
volatile bool running; /* exit main loop once this becomes false */
+ int exit_status;
volatile sig_atomic_t cancel_filter; /* abort external command */
volatile sig_atomic_t sigbus;
sigjmp_buf sigbus_jmpbuf;