diff options
Diffstat (limited to 'imports/codemirror/keymap/vim.js')
-rw-r--r-- | imports/codemirror/keymap/vim.js | 927 |
1 files changed, 673 insertions, 254 deletions
diff --git a/imports/codemirror/keymap/vim.js b/imports/codemirror/keymap/vim.js index f8fa5e07..2aa6e0f7 100644 --- a/imports/codemirror/keymap/vim.js +++ b/imports/codemirror/keymap/vim.js | |||
@@ -1,21 +1,96 @@ | |||
1 | // Supported keybindings: | ||
2 | // | ||
3 | // Cursor movement: | ||
4 | // h, j, k, l | ||
5 | // e, E, w, W, b, B | ||
6 | // Ctrl-f, Ctrl-b | ||
7 | // Ctrl-n, Ctrl-p | ||
8 | // $, ^, 0 | ||
9 | // G | ||
10 | // ge, gE | ||
11 | // gg | ||
12 | // f<char>, F<char>, t<char>, T<char> | ||
13 | // Ctrl-o, Ctrl-i TODO (FIXME - Ctrl-O wont work in Chrome) | ||
14 | // /, ?, n, N TODO (does not work) | ||
15 | // #, * TODO | ||
16 | // | ||
17 | // Entering insert mode: | ||
18 | // i, I, a, A, o, O | ||
19 | // s | ||
20 | // ce, cb (without support for number of actions like c3e - TODO) | ||
21 | // cc | ||
22 | // S, C TODO | ||
23 | // cf<char>, cF<char>, ct<char>, cT<char> | ||
24 | // | ||
25 | // Deleting text: | ||
26 | // x, X | ||
27 | // J | ||
28 | // dd, D | ||
29 | // de, db (without support for number of actions like d3e - TODO) | ||
30 | // df<char>, dF<char>, dt<char>, dT<char> | ||
31 | // | ||
32 | // Yanking and pasting: | ||
33 | // yy, Y | ||
34 | // p, P | ||
35 | // p'<char> TODO - test | ||
36 | // y'<char> TODO - test | ||
37 | // m<char> TODO - test | ||
38 | // | ||
39 | // Changing text in place: | ||
40 | // ~ | ||
41 | // r<char> | ||
42 | // | ||
43 | // Visual mode: | ||
44 | // v, V TODO | ||
45 | // | ||
46 | // Misc: | ||
47 | // . TODO | ||
48 | // | ||
49 | |||
1 | (function() { | 50 | (function() { |
2 | var count = ""; | 51 | var count = ""; |
3 | var sdir = "f"; | 52 | var sdir = "f"; |
4 | var buf = ""; | 53 | var buf = ""; |
5 | var yank = 0; | 54 | var yank = 0; |
6 | var mark = []; | 55 | var mark = []; |
56 | var reptTimes = 0; | ||
7 | function emptyBuffer() { buf = ""; } | 57 | function emptyBuffer() { buf = ""; } |
8 | function pushInBuffer(str) { buf += str; }; | 58 | function pushInBuffer(str) { buf += str; } |
9 | function pushCountDigit(digit) { return function(cm) {count += digit;} } | 59 | function pushCountDigit(digit) { return function(cm) {count += digit;}; } |
10 | function popCount() { var i = parseInt(count); count = ""; return i || 1; } | 60 | function popCount() { var i = parseInt(count, 10); count = ""; return i || 1; } |
61 | function iterTimes(func) { | ||
62 | for (var i = 0, c = popCount(); i < c; ++i) func(i, i == c - 1); | ||
63 | } | ||
11 | function countTimes(func) { | 64 | function countTimes(func) { |
12 | if (typeof func == "string") func = CodeMirror.commands[func]; | 65 | if (typeof func == "string") func = CodeMirror.commands[func]; |
13 | return function(cm) { for (var i = 0, c = popCount(); i < c; ++i) func(cm); } | 66 | return function(cm) { iterTimes(function () { func(cm); }); }; |
14 | } | 67 | } |
15 | 68 | ||
16 | function iterObj(o, f) { | 69 | function iterObj(o, f) { |
17 | for (var prop in o) if (o.hasOwnProperty(prop)) f(prop, o[prop]); | 70 | for (var prop in o) if (o.hasOwnProperty(prop)) f(prop, o[prop]); |
18 | } | 71 | } |
72 | function iterList(l, f) { | ||
73 | for (var i in l) f(l[i]); | ||
74 | } | ||
75 | function toLetter(ch) { | ||
76 | // T -> t, Shift-T -> T, '*' -> *, "Space" -> " " | ||
77 | if (ch.slice(0, 6) == "Shift-") { | ||
78 | return ch.slice(0, 1); | ||
79 | } else { | ||
80 | if (ch == "Space") return " "; | ||
81 | if (ch.length == 3 && ch[0] == "'" && ch[2] == "'") return ch[1]; | ||
82 | return ch.toLowerCase(); | ||
83 | } | ||
84 | } | ||
85 | var SPECIAL_SYMBOLS = "~`!@#$%^&*()_-+=[{}]\\|/?.,<>:;\"\'1234567890"; | ||
86 | function toCombo(ch) { | ||
87 | // t -> T, T -> Shift-T, * -> '*', " " -> "Space" | ||
88 | if (ch == " ") return "Space"; | ||
89 | var specialIdx = SPECIAL_SYMBOLS.indexOf(ch); | ||
90 | if (specialIdx != -1) return "'" + ch + "'"; | ||
91 | if (ch.toLowerCase() == ch) return ch.toUpperCase(); | ||
92 | return "Shift-" + ch.toUpperCase(); | ||
93 | } | ||
19 | 94 | ||
20 | var word = [/\w/, /[^\w\s]/], bigWord = [/\S/]; | 95 | var word = [/\w/, /[^\w\s]/], bigWord = [/\S/]; |
21 | function findWord(line, pos, dir, regexps) { | 96 | function findWord(line, pos, dir, regexps) { |
@@ -37,62 +112,172 @@ | |||
37 | } | 112 | } |
38 | return {from: Math.min(start, end), to: Math.max(start, end)}; | 113 | return {from: Math.min(start, end), to: Math.max(start, end)}; |
39 | } | 114 | } |
40 | function moveToWord(cm, regexps, dir, where) { | 115 | function moveToWord(cm, regexps, dir, times, where) { |
41 | var cur = cm.getCursor(), ch = cur.ch, line = cm.getLine(cur.line), word; | 116 | var cur = cm.getCursor(); |
42 | while (true) { | 117 | |
43 | word = findWord(line, ch, dir, regexps); | 118 | for (var i = 0; i < times; i++) { |
44 | ch = word[where == "end" ? "to" : "from"]; | 119 | var line = cm.getLine(cur.line), startCh = cur.ch, word; |
45 | if (ch == cur.ch && word.from != word.to) ch = word[dir < 0 ? "from" : "to"]; | 120 | while (true) { |
46 | else break; | 121 | // If we're at start/end of line, start on prev/next respectivly |
122 | if (cur.ch == line.length && dir > 0) { | ||
123 | cur.line++; | ||
124 | cur.ch = 0; | ||
125 | line = cm.getLine(cur.line); | ||
126 | } else if (cur.ch == 0 && dir < 0) { | ||
127 | cur.line--; | ||
128 | cur.ch = line.length; | ||
129 | line = cm.getLine(cur.line); | ||
130 | } | ||
131 | if (!line) break; | ||
132 | |||
133 | // On to the actual searching | ||
134 | word = findWord(line, cur.ch, dir, regexps); | ||
135 | cur.ch = word[where == "end" ? "to" : "from"]; | ||
136 | if (startCh == cur.ch && word.from != word.to) cur.ch = word[dir < 0 ? "from" : "to"]; | ||
137 | else break; | ||
138 | } | ||
47 | } | 139 | } |
48 | cm.setCursor(cur.line, word[where == "end" ? "to" : "from"], true); | 140 | return cur; |
49 | } | 141 | } |
50 | function joinLineNext(cm) { | 142 | function joinLineNext(cm) { |
51 | var cur = cm.getCursor(), ch = cur.ch, line = cm.getLine(cur.line); | 143 | var cur = cm.getCursor(), ch = cur.ch, line = cm.getLine(cur.line); |
52 | CodeMirror.commands.goLineEnd(cm); | 144 | CodeMirror.commands.goLineEnd(cm); |
53 | if (cur.line != cm.lineCount()) { | 145 | if (cur.line != cm.lineCount()) { |
54 | CodeMirror.commands.goLineEnd(cm); | 146 | CodeMirror.commands.goLineEnd(cm); |
55 | cm.replaceSelection(" ", "end"); | 147 | cm.replaceSelection(" ", "end"); |
56 | CodeMirror.commands.delCharRight(cm); | 148 | CodeMirror.commands.delCharRight(cm); |
57 | } | ||
58 | } | ||
59 | function editCursor(mode) { | ||
60 | if (mode == "vim-insert") { | ||
61 | // put in your cursor css changing code | ||
62 | } else if (mode == "vim") { | ||
63 | // put in your cursor css changing code | ||
64 | } | 149 | } |
65 | } | 150 | } |
66 | function delTillMark(cm, cHar) { | 151 | function delTillMark(cm, cHar) { |
67 | var i = mark[cHar], l = cm.getCursor().line, start = i > l ? l : i, end = i > l ? i : l; | 152 | var i = mark[cHar]; |
153 | if (i === undefined) { | ||
154 | // console.log("Mark not set"); // TODO - show in status bar | ||
155 | return; | ||
156 | } | ||
157 | var l = cm.getCursor().line, start = i > l ? l : i, end = i > l ? i : l; | ||
68 | cm.setCursor(start); | 158 | cm.setCursor(start); |
69 | for (var c = start; c <= end; c++) { | 159 | for (var c = start; c <= end; c++) { |
70 | pushInBuffer("\n"+cm.getLine(start)); | 160 | pushInBuffer("\n"+cm.getLine(start)); |
71 | cm.removeLine(start); | 161 | cm.removeLine(start); |
72 | } | 162 | } |
73 | } | 163 | } |
74 | function yankTillMark(cm, cHar) { | 164 | function yankTillMark(cm, cHar) { |
75 | var i = mark[cHar], l = cm.getCursor().line, start = i > l ? l : i, end = i > l ? i : l; | 165 | var i = mark[cHar]; |
166 | if (i === undefined) { | ||
167 | // console.log("Mark not set"); // TODO - show in status bar | ||
168 | return; | ||
169 | } | ||
170 | var l = cm.getCursor().line, start = i > l ? l : i, end = i > l ? i : l; | ||
76 | for (var c = start; c <= end; c++) { | 171 | for (var c = start; c <= end; c++) { |
77 | pushInBuffer("\n"+cm.getLine(c)); | 172 | pushInBuffer("\n"+cm.getLine(c)); |
78 | } | 173 | } |
79 | cm.setCursor(start); | 174 | cm.setCursor(start); |
80 | } | 175 | } |
176 | function goLineStartText(cm) { | ||
177 | // Go to the start of the line where the text begins, or the end for whitespace-only lines | ||
178 | var cur = cm.getCursor(), firstNonWS = cm.getLine(cur.line).search(/\S/); | ||
179 | cm.setCursor(cur.line, firstNonWS == -1 ? line.length : firstNonWS, true); | ||
180 | } | ||
81 | 181 | ||
182 | function charIdxInLine(cm, cHar, motion_options) { | ||
183 | // Search for cHar in line. | ||
184 | // motion_options: {forward, inclusive} | ||
185 | // If inclusive = true, include it too. | ||
186 | // If forward = true, search forward, else search backwards. | ||
187 | // If char is not found on this line, do nothing | ||
188 | var cur = cm.getCursor(), line = cm.getLine(cur.line), idx; | ||
189 | var ch = toLetter(cHar), mo = motion_options; | ||
190 | if (mo.forward) { | ||
191 | idx = line.indexOf(ch, cur.ch + 1); | ||
192 | if (idx != -1 && mo.inclusive) idx += 1; | ||
193 | } else { | ||
194 | idx = line.lastIndexOf(ch, cur.ch); | ||
195 | if (idx != -1 && !mo.inclusive) idx += 1; | ||
196 | } | ||
197 | return idx; | ||
198 | } | ||
199 | |||
200 | function moveTillChar(cm, cHar, motion_options) { | ||
201 | // Move to cHar in line, as found by charIdxInLine. | ||
202 | var idx = charIdxInLine(cm, cHar, motion_options), cur = cm.getCursor(); | ||
203 | if (idx != -1) cm.setCursor({line: cur.line, ch: idx}); | ||
204 | } | ||
205 | |||
206 | function delTillChar(cm, cHar, motion_options) { | ||
207 | // delete text in this line, untill cHar is met, | ||
208 | // as found by charIdxInLine. | ||
209 | // If char is not found on this line, do nothing | ||