Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

initial import

  • Loading branch information...
commit 25d96e1273ad27faa8dc2dde4ae1aab00ca2516a 0 parents
@jskinner jskinner authored
3  Base File (OSX).sublime-settings
@@ -0,0 +1,3 @@
+{
+ "vintage_ctrl_keys": true
+}
80 Default (Linux).sublime-keymap
@@ -0,0 +1,80 @@
+[
+ { "keys": ["left"], "command": "set_motion", "args": {
+ "motion": "vi_move_by_characters_in_line",
+ "motion_args": {"forward": false, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["right"], "command": "set_motion", "args": {
+ "motion": "vi_move_by_characters_in_line",
+ "motion_args": {"forward": true, "extend": true, "visual": false }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["up"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "lines", "forward": false, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["down"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "lines", "forward": true, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["ctrl+left"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_begin": true, "punct_begin": true, "empty_line": true, "forward": false, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["ctrl+right"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_begin": true, "punct_begin": true, "empty_line": true, "forward": true, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["alt+left"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_begin": true, "sub_word_begin": true, "punct_begin": true, "empty_line": true, "forward": false, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["alt+right"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_begin": true, "sub_word_begin": true, "punct_begin": true, "empty_line": true, "forward": true, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["pageup"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "pages", "forward": false, "extend": true }},
+ "context": [{"key": "setting.command_mode"}, {"key": "setting.vintage_ctrl_keys"}]
+ },
+ { "keys": ["pagedown"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "pages", "forward": true, "extend": true }},
+ "context": [{"key": "setting.command_mode"}, {"key": "setting.vintage_ctrl_keys"}]
+ },
+
+ { "keys": ["home"], "command": "set_motion", "args": {
+ "motion": "vi_move_to_first_non_white_space_character",
+ "motion_args": {"extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["end"], "command": "set_motion", "args": {
+ "motion": "vi_move_to_hard_eol",
+ "motion_args": {"repeat": 1, "extend": true},
+ "inclusive": true },
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["ctrl+home"], "command": "set_motion", "args": {
+ "motion": "move_to",
+ "motion_args": {"to": "bof", "extend": true},
+ "inclusive": true },
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["ctrl+end"], "command": "set_motion", "args": {
+ "motion": "move_to",
+ "motion_args": {"to": "eof", "extend": true},
+ "inclusive": true },
+ "context": [{"key": "setting.command_mode"}]
+ }
+]
91 Default (OSX).sublime-keymap
@@ -0,0 +1,91 @@
+[
+ { "keys": ["left"], "command": "set_motion", "args": {
+ "motion": "vi_move_by_characters_in_line",
+ "motion_args": {"forward": false, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["right"], "command": "set_motion", "args": {
+ "motion": "vi_move_by_characters_in_line",
+ "motion_args": {"forward": true, "extend": true, "visual": false }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["up"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "lines", "forward": false, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["down"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "lines", "forward": true, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["alt+left"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_begin": true, "punct_begin": true, "empty_line": true, "forward": false, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["alt+right"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_begin": true, "punct_begin": true, "empty_line": true, "forward": true, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["ctrl+left"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_begin": true, "sub_word_begin": true, "punct_begin": true, "empty_line": true, "forward": false, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["ctrl+right"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_begin": true, "sub_word_begin": true, "punct_begin": true, "empty_line": true, "forward": true, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["pageup"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "pages", "forward": false, "extend": true }},
+ "context": [{"key": "setting.command_mode"}, {"key": "setting.vintage_ctrl_keys"}]
+ },
+ { "keys": ["pagedown"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "pages", "forward": true, "extend": true }},
+ "context": [{"key": "setting.command_mode"}, {"key": "setting.vintage_ctrl_keys"}]
+ },
+
+ { "keys": ["super+left"], "command": "set_motion", "args": {
+ "motion": "vi_move_to_first_non_white_space_character",
+ "motion_args": {"extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["home"], "command": "set_motion", "args": {
+ "motion": "vi_move_to_first_non_white_space_character",
+ "motion_args": {"extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["super+right"], "command": "set_motion", "args": {
+ "motion": "vi_move_to_hard_eol",
+ "motion_args": {"repeat": 1, "extend": true},
+ "inclusive": true },
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["end"], "command": "set_motion", "args": {
+ "motion": "vi_move_to_hard_eol",
+ "motion_args": {"repeat": 1, "extend": true},
+ "inclusive": true },
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["super+up"], "command": "set_motion", "args": {
+ "motion": "move_to",
+ "motion_args": {"to": "bof", "extend": true},
+ "inclusive": true },
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["super+down"], "command": "set_motion", "args": {
+ "motion": "move_to",
+ "motion_args": {"to": "eof", "extend": true},
+ "inclusive": true },
+ "context": [{"key": "setting.command_mode"}]
+ }
+]
80 Default (Windows).sublime-keymap
@@ -0,0 +1,80 @@
+[
+ { "keys": ["left"], "command": "set_motion", "args": {
+ "motion": "vi_move_by_characters_in_line",
+ "motion_args": {"forward": false, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["right"], "command": "set_motion", "args": {
+ "motion": "vi_move_by_characters_in_line",
+ "motion_args": {"forward": true, "extend": true, "visual": false }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["up"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "lines", "forward": false, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["down"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "lines", "forward": true, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["ctrl+left"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_begin": true, "punct_begin": true, "empty_line": true, "forward": false, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["ctrl+right"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_begin": true, "punct_begin": true, "empty_line": true, "forward": true, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["alt+left"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_begin": true, "sub_word_begin": true, "punct_begin": true, "empty_line": true, "forward": false, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["alt+right"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_begin": true, "sub_word_begin": true, "punct_begin": true, "empty_line": true, "forward": true, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["pageup"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "pages", "forward": false, "extend": true }},
+ "context": [{"key": "setting.command_mode"}, {"key": "setting.vintage_ctrl_keys"}]
+ },
+ { "keys": ["pagedown"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "pages", "forward": true, "extend": true }},
+ "context": [{"key": "setting.command_mode"}, {"key": "setting.vintage_ctrl_keys"}]
+ },
+
+ { "keys": ["home"], "command": "set_motion", "args": {
+ "motion": "vi_move_to_first_non_white_space_character",
+ "motion_args": {"extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["end"], "command": "set_motion", "args": {
+ "motion": "vi_move_to_hard_eol",
+ "motion_args": {"repeat": 1, "extend": true},
+ "inclusive": true },
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["ctrl+home"], "command": "set_motion", "args": {
+ "motion": "move_to",
+ "motion_args": {"to": "bof", "extend": true},
+ "inclusive": true },
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["ctrl+end"], "command": "set_motion", "args": {
+ "motion": "move_to",
+ "motion_args": {"to": "eof", "extend": true},
+ "inclusive": true },
+ "context": [{"key": "setting.command_mode"}]
+ }
+]
762 Default.sublime-keymap
@@ -0,0 +1,762 @@
+[
+ { "keys": ["escape"], "command": "exit_insert_mode",
+ "context":
+ [
+ { "key": "setting.command_mode", "operand": false },
+ { "key": "setting.is_widget", "operand": false }
+ ]
+ },
+
+ { "keys": ["escape"], "command": "exit_visual_mode",
+ "context":
+ [
+ { "key": "setting.command_mode"},
+ { "key": "num_selections", "operand": 1},
+ { "key": "selection_empty", "operator": "equal", "operand": false, "match_all": false }
+ ]
+ },
+
+ { "keys": ["ctrl+["], "command": "exit_insert_mode",
+ "context":
+ [
+ { "key": "setting.command_mode", "operand": false },
+ { "key": "setting.is_widget", "operand": false },
+ { "key": "setting.vintage_ctrl_keys" }
+ ]
+ },
+
+ { "keys": ["ctrl+["], "command": "exit_visual_mode",
+ "context":
+ [
+ { "key": "setting.command_mode"},
+ { "key": "num_selections", "operand": 1},
+ { "key": "selection_empty", "operator": "equal", "operand": false, "match_all": false },
+ { "key": "setting.vintage_ctrl_keys" }
+ ]
+ },
+
+ { "keys": ["i"], "command": "enter_insert_mode", "context": [{"key": "setting.command_mode"}] },
+ { "keys": ["I"], "command": "enter_insert_mode", "args":
+ {"insert_command": "vi_move_to_first_non_white_space_character"},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["a"], "command": "enter_insert_mode", "args":
+ {"insert_command": "move", "insert_args": {"by": "characters", "forward": true} },
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["A"], "command": "enter_insert_mode", "args":
+ {"insert_command": "move_to", "insert_args": {"to": "hardeol"} },
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["o"], "command": "enter_insert_mode", "args":
+ {"insert_command": "run_macro_file", "insert_args": {"file": "Packages/Default/Add Line.sublime-macro"} },
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["o"], "command": "vi_reverse_selections_direction",
+ "context":
+ [
+ {"key": "setting.command_mode"},
+ {"key": "selection_empty", "operator": "equal", "operand": false}
+ ]
+ },
+
+ { "keys": ["O"], "command": "enter_insert_mode", "args":
+ {"insert_command": "run_macro_file", "insert_args": {"file": "Packages/Default/Add Line Before.sublime-macro"} },
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["u"], "command": "undo", "context": [{"key": "setting.command_mode"}] },
+ {
+ "keys": ["ctrl+r"], "command": "redo",
+ "context": [{"key": "setting.command_mode"}, {"key": "setting.vintage_ctrl_keys"}]
+ },
+
+ { "keys": ["v"], "command": "enter_visual_mode",
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["v"], "command": "set_motion_mode", "args": {"mode": "normal"},
+ "context":
+ [
+ {"key": "setting.command_mode"},
+ {"key": "vi_has_action" }
+ ]
+ },
+ { "keys": ["v"], "command": "exit_visual_mode", "args": {"toggle": true},
+ "context":
+ [
+ {"key": "setting.command_mode"},
+ {"key": "selection_empty", "operator": "equal", "operand": false, "match_all": false}
+ ]
+ },
+
+ { "keys": ["V"], "command": "enter_visual_line_mode",
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["V"], "command": "enter_visual_line_mode",
+ "context":
+ [
+ {"key": "setting.command_mode"},
+ {"key": "vi_motion_mode", "operand": "line"}
+ ]
+ },
+
+ { "keys": ["\"", "<character>"], "command": "set_register",
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["P"], "command": "vi_paste_left",
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["p"], "command": "vi_paste_right",
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["/"], "command": "show_panel", "args":
+ {
+ "panel": "incremental_find",
+ "select_text": false,
+ "reverse": false
+ },
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["?"], "command": "show_panel", "args":
+ {
+ "panel": "incremental_find",
+ "select_text": false,
+ "reverse": true
+ },
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": [":"], "command": "show_overlay", "args": {"overlay": "command_palette", "text": ":"},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["*"], "command": "find_under",
+ "args": {"select_text": false},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["#"], "command": "find_under_prev",
+ "args": {"select_text": false},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ {
+ "keys": ["n"], "command": "find_next",
+ "args": {"select_text": false},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ {
+ "keys": ["N"],
+ "command": "find_prev",
+ "args": {"select_text": false},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["J"], "command": "join_lines", "context": [{"key": "setting.command_mode"}] },
+
+ { "keys": ["."], "command": "repeat", "context": [{"key": "setting.command_mode"}] },
+
+ { "keys": ["r", "<character>"], "command": "replace_character",
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["x"], "command": "set_action_motion", "args": {
+ "action": "vi_right_delete",
+ "motion": null },
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["m", "<character>"], "command": "vi_set_bookmark",
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["`", "<character>"], "command": "vi_select_bookmark",
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["'", "<character>"], "command": "vi_select_bookmark",
+ "args": {"select_bol": true},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["q", "<character>"], "command": "vi_begin_record_macro",
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["q"], "command": "vi_end_record_macro",
+ "context": [{"key": "setting.command_mode"}, {"key": "is_recording_macro"}]
+ },
+
+ { "keys": ["@", "<character>"], "command": "vi_replay_macro",
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["ctrl+y"], "command": "scroll_lines", "args": {"amount": 1.0 },
+ "context": [{"key": "setting.command_mode"}, {"key": "setting.vintage_ctrl_keys"}]
+ },
+ { "keys": ["ctrl+e"], "command": "scroll_lines", "args": {"amount": -1.0 },
+ "context": [{"key": "setting.command_mode"}, {"key": "setting.vintage_ctrl_keys"}]
+ },
+
+ //
+ // Actions
+ //
+
+ { "keys": ["d"], "command": "set_action", "args": {
+ "action": "vi_delete",
+ "description": "Delete",
+ "motion_mode": "auto_line"},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["y"], "command": "set_action", "args": {
+ "action": "vi_copy",
+ "description": "Yank",
+ "motion_mode": "auto_line"},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["c"], "command": "set_action", "args": {
+ "action": "enter_insert_mode",
+ "description": "Change",
+ "action_args": {"insert_command": "left_delete"}},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["g", "u"], "command": "set_action", "args": {"action": "lower_case", "description": "Lower Case"},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["g", "U"], "command": "set_action", "args": {"action": "upper_case", "description": "Upper Case"},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["g", "~"], "command": "set_action", "args": {"action": "swap_case", "description": "Swap Case"},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["g", "?"], "command": "set_action", "args": {"action": "rot13", "description": "Rot13"},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["g", "a"], "command": "show_ascii_info",
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": [">"], "command": "set_action", "args": {"action": "vi_indent", "description": "Indent", "motion_mode": "auto_line"},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["<"], "command": "set_action", "args": {"action": "vi_unindent", "description": "Unindent"},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["="], "command": "set_action", "args": {"action": "reindent", "description": "Reindent"},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ //
+ // Motions
+ //
+
+ { "keys": ["W"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_begin": true, "empty_line": true, "separators": "", "forward": true, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["w"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_begin": true, "punct_begin": true, "empty_line": true, "forward": true, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["b"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_begin": true, "punct_begin": true, "empty_line": true, "forward": false, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["B"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_begin": true, "empty_line": true, "separators": "", "forward": false, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["e"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_end": true, "punct_end": true, "empty_line": true, "forward": true, "extend": true },
+ "inclusive": true },
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["E"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_end": true, "empty_line": true, "separators": "", "forward": true, "extend": true },
+ "inclusive": true },
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ // Make cw act like ce
+ { "keys": ["w"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_end": true, "punct_end": true, "empty_line": true, "forward": true, "extend": true },
+ "inclusive": true },
+ "context":
+ [
+ {"key": "setting.command_mode"},
+ {"key": "vi_action", "operand": "enter_insert_mode"}
+ ]
+ },
+
+ // Make cW act like cE
+ { "keys": ["W"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_end": true, "punct_end": true, "empty_line": true, "separators": "", "forward": true, "extend": true },
+ "inclusive": true },
+ "context":
+ [
+ {"key": "setting.command_mode"},
+ {"key": "vi_action", "operand": "enter_insert_mode"}
+ ]
+ },
+
+ // Bonus: alt+w and alt+b move by sub-words
+ { "keys": ["alt+w"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_begin": true, "sub_word_begin": true, "punct_begin": true, "empty_line": true, "forward": true, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["alt+w"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_end": true, "sub_word_end": true, "punct_end": true, "empty_line": true, "forward": true, "extend": true },
+ "inclusive": true },
+ "context":
+ [
+ {"key": "setting.command_mode"},
+ {"key": "vi_action", "operand": "enter_insert_mode"}
+ ]
+ },
+ { "keys": ["alt+b"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "stops", "word_begin": true, "sub_word_begin": true, "punct_begin": true, "empty_line": true, "forward": false, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["$"], "command": "set_motion", "args": {
+ "motion": "vi_move_to_hard_eol",
+ "motion_args": {"repeat": 1, "extend": true},
+ "inclusive": true },
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["^"], "command": "set_motion", "args": {
+ "motion": "vi_move_to_first_non_white_space_character",
+ "motion_args": {"extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": [" "], "command": "set_motion", "args": {
+ "motion": "vi_move_by_characters",
+ "motion_args": {"forward": true, "extend": true, "visual": false }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["enter"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "lines", "forward": true, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["shift+enter"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "lines", "forward": true, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["l"], "command": "set_motion", "args": {
+ "motion": "vi_move_by_characters_in_line",
+ "motion_args": {"forward": true, "extend": true, "visual": false }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["h"], "command": "set_motion", "args": {
+ "motion": "vi_move_by_characters_in_line",
+ "motion_args": {"forward": false, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["j"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "lines", "forward": true, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["k"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "lines", "forward": false, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["G"], "command": "set_motion", "args": {
+ "motion": "vi_goto_line",
+ "motion_args": {"repeat": 1, "explicit_repeat": true, "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["g", "g"], "command": "set_motion", "args": {
+ "motion": "move_to",
+ "motion_args": {"to": "bof", "extend": true }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["f", "<character>"], "command": "set_motion", "args": {
+ "motion": "vi_move_to_character",
+ "motion_args": {"extend": true },
+ "inclusive": true},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["F", "<character>"], "command": "set_motion", "args": {
+ "motion": "vi_move_to_character",
+ "motion_args": {"extend": true, "forward": false },
+ "inclusive": true},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["t", "<character>"], "command": "set_motion", "args": {
+ "motion": "vi_move_to_character",
+ "motion_args": {"extend": true, "before": true },
+ "inclusive": true},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["T", "<character>"], "command": "set_motion", "args": {
+ "motion": "vi_move_to_character",
+ "motion_args": {"extend": true, "forward": false, "before": true },
+ "inclusive": true},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": [";"], "command": "set_repeat_move_to_character_motion",
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": [","], "command": "set_repeat_move_to_character_motion",
+ "args": {"reverse": true},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["%"], "command": "set_motion", "args": {
+ "motion": "vi_move_to_brackets",
+ "motion_args": {"repeat": 1},
+ "inclusive": true },
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["ctrl+f"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "pages", "forward": true, "extend": true }},
+ "context": [{"key": "setting.command_mode"}, {"key": "setting.vintage_ctrl_keys"}]
+ },
+
+ { "keys": ["ctrl+b"], "command": "set_motion", "args": {
+ "motion": "move",
+ "motion_args": {"by": "pages", "forward": false, "extend": true }},
+ "context": [{"key": "setting.command_mode"}, {"key": "setting.vintage_ctrl_keys"}]
+ },
+
+ { "keys": ["H"], "command": "set_motion", "args": {
+ "motion": "move_caret_to_screen_top",
+ "motion_args": {"repeat": 1}},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["M"], "command": "set_motion", "args": {
+ "motion": "move_caret_to_screen_center"},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["L"], "command": "set_motion", "args": {
+ "motion": "move_caret_to_screen_bottom",
+ "motion_args": {"repeat": 1}},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ // Motions to allow double press to mean entire line
+
+ { "keys": ["c"], "command": "set_motion", "args": {
+ "motion": "expand_selection",
+ "motion_args": {"to": "line_without_eol" }},
+ "context":
+ [
+ {"key": "setting.command_mode"},
+ {"key": "vi_action", "operand": "enter_insert_mode"}
+ ]
+ },
+
+ { "keys": ["d"], "command": "set_motion", "args": {
+ "motion": "expand_selection",
+ "motion_args": {"to": "line" },
+ "mode": "normal"},
+ "context":
+ [
+ {"key": "setting.command_mode"},
+ {"key": "vi_action", "operand": "vi_delete"}
+ ]
+ },
+
+ { "keys": ["y"], "command": "set_motion", "args": {
+ "motion": "expand_selection",
+ "motion_args": {"to": "line" },
+ "mode": "normal"},
+ "context":
+ [
+ {"key": "setting.command_mode"},
+ {"key": "vi_action", "operand": "vi_copy"}
+ ]
+ },
+
+ { "keys": [">"], "command": "set_motion", "args": {"motion": null},
+ "context":
+ [
+ {"key": "setting.command_mode"},
+ {"key": "vi_action", "operand": "vi_indent"}
+ ]
+ },
+
+ { "keys": ["<"], "command": "set_motion", "args": {"motion": null},
+ "context":
+ [
+ {"key": "setting.command_mode"},
+ {"key": "vi_action", "operand": "vi_unindent"}
+ ]
+ },
+
+ // Single key, combined action-motions
+
+ { "keys": ["D"], "command": "set_action_motion", "args": {
+ "action": "vi_delete",
+ "motion": "vi_move_to_hard_eol",
+ "motion_args": {"repeat": 1, "extend": true},
+ "motion_inclusive": true },
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["C"], "command": "set_action_motion", "args": {
+ "action": "enter_insert_mode",
+ "action_args": {"insert_command": "left_delete"},
+ "motion": "vi_move_to_hard_eol",
+ "motion_args": {"repeat": 1, "extend": true},
+ "motion_inclusive": true },
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["Y"], "command": "set_action_motion", "args": {
+ "action": "vi_copy",
+ "motion": "expand_selection",
+ "motion_args": {"to": "line" }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["s"], "command": "set_action_motion", "args": {
+ "action": "enter_insert_mode",
+ "action_args": {"insert_command": "left_delete"},
+ "motion": "vi_move_by_characters_in_line",
+ "motion_args": {"forward": true, "extend": true, "visual": false }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ { "keys": ["S"], "command": "set_action_motion", "args": {
+ "action": "enter_insert_mode",
+ "action_args": {"insert_command": "left_delete"},
+ "motion": "expand_selection",
+ "motion_args": {"to": "line_without_eol" }},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ // Text Object motions
+
+ { "keys": ["i", "w"], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_words",
+ "motion_args": {"repeat": 1}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+ { "keys": ["a", "w"], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_words",
+ "motion_args": {"repeat": 1, "outer": true}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+
+ { "keys": ["i", "W"], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_big_words",
+ "motion_args": {"repeat": 1}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+ { "keys": ["a", "W"], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_big_words",
+ "motion_args": {"repeat": 1, "outer": true}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+
+ { "keys": ["i", "\""], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_quotes",
+ "motion_args": {"character": "\""}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+ { "keys": ["a", "\""], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_quotes",
+ "motion_args": {"character": "\"", "outer": true}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+ { "keys": ["i", "'"], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_quotes",
+ "motion_args": {"character": "'"}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+ { "keys": ["a", "'"], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_quotes",
+ "motion_args": {"character": "'", "outer": true}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+
+ { "keys": ["i", "t"], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_tag"},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+ { "keys": ["a", "t"], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_tag",
+ "motion_args": {"outer": true}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+
+ { "keys": ["i", "("], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_brackets",
+ "motion_args": {"character": "("}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+ { "keys": ["a", "("], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_brackets",
+ "motion_args": {"character": "(", "outer": true}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+ { "keys": ["i", ")"], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_brackets",
+ "motion_args": {"character": "("}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+ { "keys": ["a", ")"], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_brackets",
+ "motion_args": {"character": "(", "outer": true}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+ { "keys": ["i", "b"], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_brackets",
+ "motion_args": {"character": "("}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+ { "keys": ["a", "b"], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_brackets",
+ "motion_args": {"character": "(", "outer": true}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+
+ { "keys": ["i", "["], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_brackets",
+ "motion_args": {"character": "["}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+ { "keys": ["a", "["], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_brackets",
+ "motion_args": {"character": "[", "outer": true}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+ { "keys": ["i", "]"], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_brackets",
+ "motion_args": {"character": "["}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+ { "keys": ["a", "]"], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_brackets",
+ "motion_args": {"character": "[", "outer": true}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+
+ { "keys": ["i", "{"], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_brackets",
+ "motion_args": {"character": "{"}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+ { "keys": ["a", "{"], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_brackets",
+ "motion_args": {"character": "{", "outer": true}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+ { "keys": ["i", "}"], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_brackets",
+ "motion_args": {"character": "{"}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+ { "keys": ["a", "}"], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_brackets",
+ "motion_args": {"character": "{", "outer": true}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+ { "keys": ["i", "B"], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_brackets",
+ "motion_args": {"character": "{"}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+ { "keys": ["a", "B"], "command": "set_motion", "args": {
+ "motion": "vi_expand_to_brackets",
+ "motion_args": {"character": "{", "outer": true}},
+ "context": [{"key": "setting.command_mode"}, {"key": "vi_can_enter_text_object"}]
+ },
+
+ //
+ // Repeat digits
+ //
+
+ { "keys": ["1"], "command": "push_repeat_digit", "args": {"digit": 1},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["2"], "command": "push_repeat_digit", "args": {"digit": 2},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["3"], "command": "push_repeat_digit", "args": {"digit": 3},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["4"], "command": "push_repeat_digit", "args": {"digit": 4},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["5"], "command": "push_repeat_digit", "args": {"digit": 5},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["6"], "command": "push_repeat_digit", "args": {"digit": 6},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["7"], "command": "push_repeat_digit", "args": {"digit": 7},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["8"], "command": "push_repeat_digit", "args": {"digit": 8},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["9"], "command": "push_repeat_digit", "args": {"digit": 9},
+ "context": [{"key": "setting.command_mode"}]
+ },
+ { "keys": ["0"], "command": "push_repeat_digit", "args": {"digit": 0},
+ "context": [{"key": "setting.command_mode"}]
+ },
+
+ // This is a motion, but must come after the above binding
+ { "keys": ["0"], "command": "set_motion", "args": {
+ "motion": "move_to",
+ "motion_args": {"to": "hardbol", "extend": true }},
+ "context":
+ [
+ {"key": "setting.command_mode"},
+ {"key": "vi_has_repeat_digit", "operand": false}
+ ]
+ }
+]
32 README.TXT
@@ -0,0 +1,32 @@
+Overview:
+--------
+
+Vintage is a vi editing package for Sublime Text 2. It's not quite a faithful recreation, and not all details match up. On the other hand, you do get multiple selections.
+
+
+Enabling Vintage:
+--------
+
+Vintage is disabled by default, via the ignored_packages global setting. If you remove "Vintage" from the list of ignored packages, you'll be able to edit with vi keys.
+
+Vintage starts in insert mode by default. This can be changed by adding:
+
+ "vintage_start_in_command_mode": true
+
+to your User File Settings.
+
+
+Major Differences From vi:
+--------
+
+Insert mode is plain Sublime Text 2 editing, with the usual Sublime Text 2 key bindings: vi insert mode key bindings are not emulated.
+
+Ex commands are not implemented, apart from :w and :e, which work via the command palette.
+
+
+Extending Vintage:
+--------
+
+Vintage is implemented entirely in Python. Extending it, for example, to add additional motions, is a matter of writing the relevant plugin (see vintage_motions.py for the existing ones), and adding a key binding for it.
+
+Motions are normal commands that work by selecting the range of text that they move over. The end of the selection (.b), is considered the active end. Motions are either inclusive, or exclusive (the default). Exclusive motions will move the caret to the right of the last selected character, inclusive motions will move it to the left. Motions are considered inclusive if the inclusive flag is passed to the set_motion command.
20 Vintage.sublime-commands
@@ -0,0 +1,20 @@
+[
+ {
+ "caption": ":w - Save",
+ "command": "save"
+ },
+ {
+ "caption": ":e - Revert",
+ "command": "revert"
+ },
+ {
+ "caption": ":0 - BOF",
+ "command": "move_to",
+ "args": {"to": "bof"}
+ },
+ {
+ "caption": ":$ - EOF",
+ "command": "move_to",
+ "args": {"to": "eof"}
+ }
+]
962 vintage.py
@@ -0,0 +1,962 @@
+import sublime, sublime_plugin
+import os.path
+
+# Normal: Motions apply to all the characters they select
+MOTION_MODE_NORMAL = 0
+# Used in visual line mode: Motions are extended to BOL and EOL.
+MOTION_MODE_LINE = 2
+# Used by some actions, just as 'd'. If a motion crosses line boundaries,
+# it'll be extended to BOL and EOL
+MOTION_MODE_AUTO_LINE = 1
+
+# Registers are used for clipboards and macro storage
+g_registers = {}
+
+# Represents the current input state. The primary commands that interact with
+# this are:
+# * set_action
+# * set_motion
+# * push_repeat_digit
+
+class InputState:
+ prefix_repeat_digits = []
+ action_command = None
+ action_command_args = None
+ action_description = None
+ motion_repeat_digits = []
+ motion_command = None
+ motion_command_args = None
+ motion_mode = MOTION_MODE_NORMAL
+ motion_inclusive = False
+ register = None
+
+g_input_state = InputState()
+
+# Updates the status bar to reflect the current mode and input state
+def update_status_line(view):
+ cmd_mode = view.settings().get('command_mode')
+
+ if cmd_mode and g_input_state.motion_mode == MOTION_MODE_LINE:
+ view.set_status('mode', 'VISUAL LINE MODE')
+ elif cmd_mode and view.has_non_empty_selection_region():
+ view.set_status('mode', 'VISUAL MODE')
+ elif cmd_mode:
+ desc = None
+ if g_input_state.register:
+ desc = 'Register "' + g_input_state.register + '" - '
+
+ repeat = (digits_to_number(g_input_state.prefix_repeat_digits)
+ * digits_to_number(g_input_state.motion_repeat_digits))
+ if g_input_state.action_command != None or repeat != 1:
+ cmd_desc = g_input_state.action_command
+ if g_input_state.action_description:
+ cmd_desc = g_input_state.action_description
+
+ if cmd_desc and desc:
+ desc += " "
+ desc += cmd_desc
+
+ if repeat != 1 and desc:
+ desc = desc + " * " + str(repeat)
+ elif repeat != 1:
+ desc = "* " + str(repeat)
+
+ if desc:
+ view.set_status('mode', 'COMMAND MODE - ' + desc)
+ else:
+ view.set_status('mode', 'COMMAND MODE')
+ else:
+ view.set_status('mode', 'INSERT MODE')
+
+def set_motion_mode(view, mode):
+ g_input_state.motion_mode = mode
+ update_status_line(view)
+
+def reset_input_state(view, reset_motion_mode = True):
+ global g_input_state
+ g_input_state.prefix_repeat_digits = []
+ g_input_state.action_command = None
+ g_input_state.action_command_args = None
+ g_input_state.action_description = None
+ g_input_state.motion_repeat_digits = []
+ g_input_state.motion_command = None
+ g_input_state.motion_command_args = None
+ g_input_state.motion_inclusive = False
+ g_input_state.register = None
+ if reset_motion_mode:
+ set_motion_mode(view, MOTION_MODE_NORMAL)
+
+def string_to_motion_mode(mode):
+ if mode == 'normal':
+ return MOTION_MODE_NORMAL
+ elif mode == 'line':
+ return MOTION_MODE_LINE
+ elif mode == 'auto_line':
+ return MOTION_MODE_AUTO_LINE
+ else:
+ return -1
+
+# Called when the plugin is unloaded (e.g., perhaps it just got added to
+# ignored_packages). Ensure files aren't left in command mode.
+def unload_handler():
+ for w in sublime.windows():
+ for v in w.views():
+ v.settings().set('command_mode', False)
+ v.settings().set('inverse_caret_state', False)
+ v.erase_status('mode')
+
+# Ensures the input state is reset when the view changes, or the user selects
+# with the mouse or non-vintage key bindings
+class InputStateTracker(sublime_plugin.EventListener):
+ def __init__(self):
+ for w in sublime.windows():
+ for v in w.views():
+ if v.settings().get("vintage_start_in_command_mode"):
+ v.settings().set('command_mode', True)
+ v.settings().set('inverse_caret_state', True)
+ update_status_line(v)
+
+ def on_activated(self, view):
+ reset_input_state(view)
+
+ def on_deactivated(self, view):
+ reset_input_state(view)
+
+ # Ensure that insert mode actions will no longer be grouped, otherwise
+ # it can lead to the impression that too much is undone at once
+ view.run_command('unmark_undo_groups_for_gluing')
+
+ def on_post_save(self, view):
+ # Ensure that insert mode actions will no longer be grouped, so it's
+ # always possible to undo back to the last saved state
+ view.run_command('unmark_undo_groups_for_gluing')
+
+ def on_selection_modified(self, view):
+ reset_input_state(view, False)
+ # Get out of visual line mode if the selection has changed, e.g., due
+ # to clicking with the mouse
+ if (g_input_state.motion_mode == MOTION_MODE_LINE and
+ not view.has_non_empty_selection_region()):
+ g_input_state.motion_mode = MOTION_MODE_NORMAL
+ update_status_line(view)
+
+ def on_load(self, view):
+ if view.settings().get("vintage_start_in_command_mode"):
+ view.run_command('exit_insert_mode')
+
+ def on_new(self, view):
+ self.on_load(view)
+
+ def on_clone(self, view):
+ self.on_load(view)
+
+ def on_query_context(self, view, key, operator, operand, match_all):
+ if key == "vi_action" and g_input_state.action_command:
+ if operator == sublime.OP_EQUAL:
+ return operand == g_input_state.action_command
+ if operator == sublime.OP_NOT_EQUAL:
+ return operand != g_input_state.action_command
+ elif key == "vi_has_action":
+ v = g_input_state.action_command != None
+ if operator == sublime.OP_EQUAL: return v == operand
+ if operator == sublime.OP_NOT_EQUAL: return v != operand
+ elif key == "vi_motion_mode":
+ m = string_to_motion_mode(operand)
+ if operator == sublime.OP_EQUAL:
+ return m == g_input_state.motion_mode
+ if operator == sublime.OP_NOT_EQUAL:
+ return m != g_input_state.motion_mode
+ elif key == "vi_has_repeat_digit":
+ if g_input_state.motion_command:
+ v = len(g_input_state.motion_repeat_digits) > 0
+ else:
+ v = len(g_input_state.prefix_repeat_digits) > 0
+ if operator == sublime.OP_EQUAL: return v == operand
+ if operator == sublime.OP_NOT_EQUAL: return v != operand
+ elif key == "vi_can_enter_text_object":
+ v = (g_input_state.action_command != None) or view.has_non_empty_selection_region()
+ if operator == sublime.OP_EQUAL: return v == operand
+ if operator == sublime.OP_NOT_EQUAL: return v != operand
+
+ return None
+
+# Called when g_input_state represents a fully formed command. Generates a
+# call to vi_eval, which is what will be left on the undo/redo stack.
+def eval_input(view):
+ global g_input_state
+
+ cmd_args = {
+ 'action_command': g_input_state.action_command,
+ 'action_args': g_input_state.action_command_args,
+ 'motion_command': g_input_state.motion_command,
+ 'motion_args': g_input_state.motion_command_args,
+ 'motion_mode': g_input_state.motion_mode,
+ 'motion_inclusive': g_input_state.motion_inclusive }
+
+ if len(g_input_state.prefix_repeat_digits) > 0:
+ cmd_args['prefix_repeat'] = digits_to_number(g_input_state.prefix_repeat_digits)
+
+ if len(g_input_state.motion_repeat_digits) > 0:
+ cmd_args['motion_repeat'] = digits_to_number(g_input_state.motion_repeat_digits)
+
+ if g_input_state.register != None:
+ if not cmd_args['action_args']:
+ cmd_args['action_args'] = {}
+ cmd_args['action_args']['register'] = g_input_state.register
+
+ reset_motion_mode = (g_input_state.action_command != None)
+
+ reset_input_state(view, reset_motion_mode)
+
+ view.run_command('vi_eval', cmd_args)
+
+# Adds a repeat digit to the input state.
+# Repeat digits may come before the action, after the action, or both. For
+# example:
+# 4dw
+# d4w
+# 2d2w
+# These commands will all delete 4 words.
+class PushRepeatDigit(sublime_plugin.TextCommand):
+ def run(self, edit, digit):
+ global g_input_state
+ if g_input_state.action_command:
+ g_input_state.motion_repeat_digits.append(digit)
+ else:
+ g_input_state.prefix_repeat_digits.append(digit)
+ update_status_line(self.view)
+
+# Set the current action in the input state. Note that this won't create an
+# entry on the undo stack: only eval_input does this.
+class SetAction(sublime_plugin.TextCommand):
+ # Custom version of run_, so an edit object isn't created. This allows
+ # eval_input() to add the desired command to the undo stack
+ def run_(self, args):
+ if 'event' in args:
+ del args['event']
+
+ return self.run(**args)
+
+ def run(self, action, action_args = {}, motion_mode = None, description = None):
+ global g_input_state
+ g_input_state.action_command = action
+ g_input_state.action_command_args = action_args
+ g_input_state.action_description = description
+
+ if motion_mode != None:
+ m = string_to_motion_mode(motion_mode)
+ if m != -1:
+ if g_input_state.motion_mode == MOTION_MODE_LINE and m == MOTION_MODE_AUTO_LINE:
+ # e.g., 'Vjd', MOTION_MODE_LINE should be maintained
+ pass
+ else:
+ set_motion_mode(self.view, m)
+ else:
+ print "invalid motion mode:", motion_mode
+
+ if self.view.has_non_empty_selection_region():
+ # Currently in visual mode, so no following motion is expected:
+ # eval the current input
+ eval_input(self.view)
+ else:
+ update_status_line(self.view)
+
+def digits_to_number(digits):
+ if len(digits) == 0:
+ return 1
+
+ number = 0
+ place = 1
+ for d in reversed(digits):
+ number += place * int(d)
+ place *= 10
+ return number
+
+# Set the current motion in the input state. Note that this won't create an
+# entry on the undo stack: only eval_input does this.
+class SetMotion(sublime_plugin.TextCommand):
+ # Custom version of run_, so an edit object isn't created. This allows
+ # eval_input() to add the desired command to the undo stack
+ def run_(self, args):
+ return self.run(**args)
+
+ def run(self, motion, motion_args = {}, inclusive = False, character = None, mode = None):
+ global g_input_state
+
+ # Pass the character, if any, onto the motion command.
+ # This is required for 'f', 't', etc
+ if character != None:
+ motion_args['character'] = character
+
+ g_input_state.motion_command = motion
+ g_input_state.motion_command_args = motion_args
+ g_input_state.motion_inclusive = inclusive
+
+ if mode != None:
+ m = string_to_motion_mode(mode)
+ if m != -1:
+ set_motion_mode(self.view, m)
+ else:
+ print "invalid motion mode:", mode
+
+ eval_input(self.view)
+
+# Run a single, combined action and motion. Examples are 'D' (delete to EOL)
+# and 'C' (change to EOL).
+class SetActionMotion(sublime_plugin.TextCommand):
+ # Custom version of run_, so an edit object isn't created. This allows
+ # eval_input() to add the desired command to the undo stack
+ def run_(self, args):
+ return self.run(**args)
+
+ def run(self, motion, action, motion_args = {}, motion_inclusive = False, action_args = {}):
+ global g_input_state
+
+ g_input_state.motion_command = motion
+ g_input_state.motion_command_args = motion_args
+ g_input_state.motion_inclusive = motion_inclusive
+ g_input_state.action_command = action
+ g_input_state.action_command_args = action_args
+
+ eval_input(self.view)
+
+# Update the current motion mode. e.g., 'dvj'
+class SetMotionMode(sublime_plugin.TextCommand):
+ def run_(self, args):
+ if 'event' in args:
+ del args['event']
+
+ return self.run(**args)
+
+ def run(self, mode):
+ global g_input_state
+ m = string_to_motion_mode(mode)
+
+ if m != -1:
+ set_motion_mode(self.view, m)
+ else:
+ print "invalid motion mode"
+
+# Sets the target register for the next command
+class SetRegister(sublime_plugin.TextCommand):
+ def run_(self, args):
+ return self.run(**args)
+
+ def run(self, character):
+ g_input_state.register = character
+ update_status_line(self.view)
+
+def clip_point_to_line(view, f, pt):
+ l = view.line(pt)
+ if l.a == l.b:
+ return l.a
+
+ new_pt = f(pt)
+ if new_pt < l.a:
+ return l.a
+ elif new_pt >= l.b:
+ return l.b
+ else:
+ return new_pt
+
+def transform_selection(view, f, extend = False, clip_to_line = False):
+ new_sel = []
+ sel = view.sel()
+ size = view.size()
+
+ for r in sel:
+ if clip_to_line:
+ new_pt = clip_point_to_line(view, f, r.b)
+ else:
+ new_pt = f(r.b)
+
+ if new_pt < 0: new_pt = 0
+ elif new_pt > size: new_pt = size
+
+ if extend:
+ new_sel.append(sublime.Region(r.a, new_pt))
+ else:
+ new_sel.append(sublime.Region(new_pt))
+
+ sel.clear()
+ for r in new_sel:
+ sel.add(r)
+
+def transform_selection_regions(view, f):
+ new_sel = []
+ sel = view.sel()
+
+ for r in sel:
+ nr = f(r)
+ if nr != None:
+ new_sel.append(nr)
+
+ sel.clear()
+ for r in new_sel:
+ sel.add(r)
+
+def expand_to_full_line(view):
+ new_sel = []
+ for s in view.sel():
+ if s.a == s.b:
+ new_sel.append(view.full_line(s.a))
+ else:
+ la = view.full_line(s.begin())
+ lb = view.full_line(s.end())
+
+ a = la.a
+
+ if s.end() == lb.a:
+ # s.end() is already at EOL, don't go down to the next line
+ b = s.end()
+ else:
+ b = lb.b
+
+ if s.a < s.b:
+ new_sel.append(sublime.Region(a, b, 0))
+ else:
+ new_sel.append(sublime.Region(b, a, 0))
+
+ view.sel().clear()
+ for s in new_sel:
+ view.sel().add(s)
+
+def orient_single_line_region(view, forward, r):
+ l = view.full_line(r.begin())
+ if l.a == r.begin() and l.end() == r.end():
+ if forward:
+ return l
+ else:
+ return sublime.Region(l.b, l.a)
+ else:
+ return r
+
+def set_single_line_selection_direction(view, forward):
+ transform_selection_regions(view,
+ lambda r: orient_single_line_region(view, forward, r))
+
+def orient_single_character_region(view, forward, r):
+ if r.begin() + 1 == r.end():
+ if forward:
+ return sublime.Region(r.begin(), r.end())
+ else:
+ return sublime.Region(r.end(), r.begin())
+ else:
+ return r
+
+def set_single_character_selection_direction(view, forward):
+ transform_selection_regions(view,
+ lambda r: orient_single_character_region(view, forward, r))
+
+def expand_line_spanning_selections_to_line(view):
+ new_sel = []
+ for s in view.sel():
+ if s.a == s.b:
+ new_sel.append(s)
+ continue
+
+ la = view.full_line(s.a)
+ lb = view.full_line(s.b)
+
+ if la == lb:
+ new_sel.append(s)
+ elif s.a < s.b:
+ a = la.a
+ b = lb.b
+ new_sel.append(sublime.Region(a, b))
+ else:
+ a = la.b
+ b = lb.a
+ new_sel.append(sublime.Region(a, b))
+
+ view.sel().clear()
+ for s in new_sel:
+ view.sel().add(s)
+
+def clip_empty_selection_to_line_contents(view):
+ new_sel = []
+ for s in view.sel():
+ if s.empty():
+ l = view.line(s.b)
+ if s.b == l.b and not l.empty():
+ s = sublime.Region(l.b - 1)
+
+ new_sel.append(s)
+
+ view.sel().clear()
+ for s in new_sel:
+ view.sel().add(s)
+
+def shrink_inclusive(r):
+ if r.a < r.b:
+ return sublime.Region(r.b - 1)
+ else:
+ return sublime.Region(r.b)
+
+def shrink_exclusive(r):
+ return sublime.Region(r.b)
+
+# This is the core: it takes a motion command, action command, and repeat
+# counts, and runs them all.
+#
+# Note that this doesn't touch g_input_state, and doesn't maintain any state
+# other than what's passed on its arguments. This allows it to operate correctly
+# in macros, and when running via repeat.
+class ViEval(sublime_plugin.TextCommand):
+ def run_(self, args):
+ was_visual = self.view.has_non_empty_selection_region()
+
+ edit = self.view.begin_edit(self.name(), args)
+ try:
+ self.run(edit, **args)
+ finally:
+ self.view.end_edit(edit)
+
+ # Glue the marked undo groups if visual mode was exited (e.g., by
+ # running an action while in visual mode). This ensures that
+ # v+motions+action can be repeated as a single unit.
+ if self.view.settings().get('command_mode') == True:
+ is_visual = self.view.has_non_empty_selection_region()
+ if was_visual and not is_visual:
+ self.view.run_command('glue_marked_undo_groups')
+ elif not is_visual:
+ self.view.run_command('unmark_undo_groups_for_gluing')
+
+ def run(self, edit, action_command, action_args,
+ motion_command, motion_args, motion_mode,
+ motion_inclusive, prefix_repeat = None, motion_repeat = None):
+
+ explicit_repeat = (prefix_repeat != None or motion_repeat != None)
+
+ if prefix_repeat == None:
+ prefix_repeat = 1
+ if motion_repeat == None:
+ motion_repeat = 1
+
+ # Arguments are always passed as floats (thanks to JSON encoding),
+ # convert them back to integers
+ prefix_repeat = int(prefix_repeat)
+ motion_repeat = int(motion_repeat)
+ motion_mode = int(motion_mode)
+
+ # Combine the prefix_repeat and motion_repeat into motion_repeat, to
+ # allow commands like 2yy to work by first doing the motion twice,
+ # then operating once
+ if motion_command and prefix_repeat > 1:
+ motion_repeat *= prefix_repeat
+ prefix_repeat = 1
+
+ # Check if the motion command would like to handle the repeat itself
+ if motion_args and 'repeat' in motion_args:
+ motion_args['repeat'] = motion_repeat * prefix_repeat
+ motion_repeat = 1
+ prefix_repeat = 1
+
+ # Some commands behave differently if a repeat is given. e.g., 1G goes
+ # to line one, but G without a repeat goes to EOF. Let the command
+ # know if a repeat was specified.
+ if motion_args and 'explicit_repeat' in motion_args:
+ motion_args['explicit_repeat'] = explicit_repeat
+
+ visual_mode = self.view.has_non_empty_selection_region()
+
+ # Let the motion know if we're in visual mode, if it wants to know
+ if motion_args and 'visual' in motion_args:
+ motion_args['visual'] = visual_mode
+
+ for i in xrange(prefix_repeat):
+ # Run the motion command, extending the selection to the range of
+ # characters covered by the motion
+ if motion_command:
+ direction = 0
+ if motion_args and 'forward' in motion_args:
+ forward = motion_args['forward']
+ if forward:
+ direction = 1
+ else:
+ direction = -1
+
+ for j in xrange(motion_repeat):
+ if direction != 0 and motion_mode == MOTION_MODE_LINE:
+ # Ensure selections encompassing a single line are
+ # oriented in the same way as the motion, so they'll
+ # remain selected. This is needed so that Vk will work
+ # as expected
+ set_single_line_selection_direction(self.view, direction == 1)
+ elif direction != 0:
+ set_single_character_selection_direction(self.view, direction == 1)
+
+ if motion_mode == MOTION_MODE_LINE:
+ # Don't do either of the below things: this is
+ # important so that Vk on an empty line would select
+ # the following line.
+ pass
+ elif direction == 1 and motion_inclusive:
+ # Expand empty selections include the character
+ # they're on, and to start from the RHS of the
+ # character
+ transform_selection_regions(self.view,
+ lambda r: sublime.Region(r.b, r.b + 1) if r.empty() else r)
+
+ self.view.run_command(motion_command, motion_args)
+
+ if motion_mode == MOTION_MODE_LINE:
+ expand_to_full_line(self.view)
+ elif motion_mode == MOTION_MODE_AUTO_LINE:
+ expand_line_spanning_selections_to_line(self.view)
+
+ if action_command:
+ # Apply the action to the selection
+ self.view.run_command(action_command, action_args)
+
+ if not visual_mode:
+ # Shrink the selection down to a point
+ if motion_inclusive:
+ transform_selection_regions(self.view, shrink_inclusive)
+ else:
+ transform_selection_regions(self.view, shrink_exclusive)
+
+ # Clip the selections to the line contents
+ if self.view.settings().get('command_mode'):
+ clip_empty_selection_to_line_contents(self.view)
+
+ # Ensure the selection is visible
+ self.view.show(self.view.sel())
+
+
+class EnterInsertMode(sublime_plugin.TextCommand):
+ # Ensure no undo group is created: the only entry on the undo stack should
+ # be the insert_command, if any
+ def run_(self, args):
+ if args:
+ return self.run(**args)
+ else:
+ return self.run()
+
+ def run(self, insert_command = None, insert_args = None):
+ # mark_undo_groups_for_gluing allows all commands run while in insert
+ # mode to comprise a single undo group, which is important for '.' to
+ # work as desired.
+ self.view.run_command('maybe_mark_undo_groups_for_gluing')
+ if insert_command:
+ self.view.run_command(insert_command, insert_args)
+
+ self.view.settings().set('command_mode', False)
+ self.view.settings().set('inverse_caret_state', False)
+ update_status_line(self.view)
+
+class ExitInsertMode(sublime_plugin.TextCommand):
+ def run_(self, args):
+ edit = self.view.begin_edit(self.name(), args)
+ try:
+ self.run(edit)
+ finally:
+ self.view.end_edit(edit)
+
+ # Call after end_edit(), to ensure the final entry in the glued undo
+ # group is 'exit_insert_mode'.
+ self.view.run_command('glue_marked_undo_groups')
+
+ def run(self, edit):
+ self.view.settings().set('command_mode', True)
+ self.view.settings().set('inverse_caret_state', True)
+
+ if not self.view.has_non_empty_selection_region():
+ self.view.run_command('vi_move_by_characters_in_line', {'forward': False})
+
+ update_status_line(self.view)
+
+class EnterVisualMode(sublime_plugin.TextCommand):
+ def run(self, edit):
+ self.view.run_command('mark_undo_groups_for_gluing')
+ if g_input_state.motion_mode != MOTION_MODE_NORMAL:
+ set_motion_mode(self.view, MOTION_MODE_NORMAL)
+
+ transform_selection_regions(self.view, lambda r: sublime.Region(r.b, r.b + 1) if r.empty() else r)
+
+class ExitVisualMode(sublime_plugin.TextCommand):
+ def run(self, edit, toggle = False):
+ if toggle:
+ if g_input_state.motion_mode != MOTION_MODE_NORMAL:
+ set_motion_mode(self.view, MOTION_MODE_NORMAL)
+ else:
+ self.view.run_command('shrink_selections')
+ else:
+ set_motion_mode(self.view, MOTION_MODE_NORMAL)
+ self.view.run_command('shrink_selections')
+
+ self.view.run_command('unmark_undo_groups_for_gluing')
+
+class EnterVisualLineMode(sublime_plugin.TextCommand):
+ def run(self, edit):
+ set_motion_mode(self.view, MOTION_MODE_LINE)
+ expand_to_full_line(self.view)
+ self.view.run_command('maybe_mark_undo_groups_for_gluing')
+
+class ShrinkSelections(sublime_plugin.TextCommand):
+ def shrink(self, r):
+ if r.empty():
+ return r
+ elif r.a < r.b:
+ return sublime.Region(r.b - 1)
+ else:
+ return sublime.Region(r.b)
+
+ def run(self, edit):
+ transform_selection_regions(self.view, self.shrink)
+
+# Sequence is used as part of glue_marked_undo_groups: the marked undo groups
+# are rewritten into a single sequence command, that accepts all the previous
+# commands
+class Sequence(sublime_plugin.TextCommand):
+ def run(self, edit, commands):
+ for cmd, args in commands:
+ self.view.run_command(cmd, args)
+
+class ViDelete(sublime_plugin.TextCommand):
+ def run(self, edit, register = '"'):
+ set_register(self.view, register, forward=False)
+ set_register(self.view, '1', forward=False)
+ self.view.run_command('left_delete')
+
+class ViRightDelete(sublime_plugin.TextCommand):
+ def run(self, edit, register = '"'):
+ set_register(self.view, register, forward=True)
+ set_register(self.view, '1', forward=True)
+ self.view.run_command('right_delete')
+ clip_empty_selection_to_line_contents(self.view)
+
+class ViCopy(sublime_plugin.TextCommand):
+ def run(self, edit, register = '"'):
+ set_register(self.view, register, forward=True)
+ set_register(self.view, '0', forward=True)
+ transform_selection_regions(self.view, lambda r: sublime.Region(r.a))
+
+class ViPasteRight(sublime_plugin.TextCommand):
+ # Ensure the register is picked up from g_input_state, and that it'll be
+ # recorded on the undo stack
+ def run_(self, args):
+ if not args:
+ args = {}
+
+ if g_input_state.register:
+ args['register'] = g_input_state.register
+ g_input_state.register = None
+
+ if 'event' in args:
+ del args['event']
+
+ edit = self.view.begin_edit(self.name(), args)
+ try:
+ return self.run(edit, **args)
+ finally:
+ self.view.end_edit(edit)
+
+ def advance(self, pt):
+ if self.view.substr(pt) == '\n' or pt >= self.view.size():
+ return pt
+ else:
+ return pt + 1
+
+ def run(self, edit, register = '"'):
+ transform_selection(self.view, lambda pt: self.advance(pt))
+ self.view.run_command('paste_from_register', {'forward': True, 'register': register})
+
+class ViPasteLeft(sublime_plugin.TextCommand):
+ # Ensure the register is picked up from g_input_state, and that it'll be
+ # recorded on the undo stack
+ def run_(self, args):
+ if not args:
+ args = {}
+
+ if g_input_state.register:
+ args['register'] = g_input_state.register
+ g_input_state.register = None
+
+ if 'event' in args:
+ del args['event']
+
+ edit = self.view.begin_edit(self.name(), args)
+ try:
+ return self.run(edit, **args)
+ finally:
+ self.view.end_edit(edit)
+
+ def run(self, edit, register = '"'):
+ self.view.run_command('paste_from_register', {'forward': False, 'register': register})
+
+def set_register(view, register, forward):
+ delta = 1
+ if not forward:
+ delta = -