0
|
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
|
|
2 // Distributed under an MIT license: https://codemirror.net/5/LICENSE
|
|
3
|
|
4 // A rough approximation of Sublime Text's keybindings
|
|
5 // Depends on addon/search/searchcursor.js and optionally addon/dialog/dialogs.js
|
|
6
|
|
7 (function(mod) {
|
|
8 if (typeof exports == "object" && typeof module == "object") // CommonJS
|
|
9 mod(require("../lib/codemirror"), require("../addon/search/searchcursor"), require("../addon/edit/matchbrackets"));
|
|
10 else if (typeof define == "function" && define.amd) // AMD
|
|
11 define(["../lib/codemirror", "../addon/search/searchcursor", "../addon/edit/matchbrackets"], mod);
|
|
12 else // Plain browser env
|
|
13 mod(CodeMirror);
|
|
14 })(function(CodeMirror) {
|
|
15 "use strict";
|
|
16
|
|
17 var cmds = CodeMirror.commands;
|
|
18 var Pos = CodeMirror.Pos;
|
|
19
|
|
20 // This is not exactly Sublime's algorithm. I couldn't make heads or tails of that.
|
|
21 function findPosSubword(doc, start, dir) {
|
|
22 if (dir < 0 && start.ch == 0) return doc.clipPos(Pos(start.line - 1));
|
|
23 var line = doc.getLine(start.line);
|
|
24 if (dir > 0 && start.ch >= line.length) return doc.clipPos(Pos(start.line + 1, 0));
|
|
25 var state = "start", type, startPos = start.ch;
|
|
26 for (var pos = startPos, e = dir < 0 ? 0 : line.length, i = 0; pos != e; pos += dir, i++) {
|
|
27 var next = line.charAt(dir < 0 ? pos - 1 : pos);
|
|
28 var cat = next != "_" && CodeMirror.isWordChar(next) ? "w" : "o";
|
|
29 if (cat == "w" && next.toUpperCase() == next) cat = "W";
|
|
30 if (state == "start") {
|
|
31 if (cat != "o") { state = "in"; type = cat; }
|
|
32 else startPos = pos + dir
|
|
33 } else if (state == "in") {
|
|
34 if (type != cat) {
|
|
35 if (type == "w" && cat == "W" && dir < 0) pos--;
|
|
36 if (type == "W" && cat == "w" && dir > 0) { // From uppercase to lowercase
|
|
37 if (pos == startPos + 1) { type = "w"; continue; }
|
|
38 else pos--;
|
|
39 }
|
|
40 break;
|
|
41 }
|
|
42 }
|
|
43 }
|
|
44 return Pos(start.line, pos);
|
|
45 }
|
|
46
|
|
47 function moveSubword(cm, dir) {
|
|
48 cm.extendSelectionsBy(function(range) {
|
|
49 if (cm.display.shift || cm.doc.extend || range.empty())
|
|
50 return findPosSubword(cm.doc, range.head, dir);
|
|
51 else
|
|
52 return dir < 0 ? range.from() : range.to();
|
|
53 });
|
|
54 }
|
|
55
|
|
56 cmds.goSubwordLeft = function(cm) { moveSubword(cm, -1); };
|
|
57 cmds.goSubwordRight = function(cm) { moveSubword(cm, 1); };
|
|
58
|
|
59 cmds.scrollLineUp = function(cm) {
|
|
60 var info = cm.getScrollInfo();
|
|
61 if (!cm.somethingSelected()) {
|
|
62 var visibleBottomLine = cm.lineAtHeight(info.top + info.clientHeight, "local");
|
|
63 if (cm.getCursor().line >= visibleBottomLine)
|
|
64 cm.execCommand("goLineUp");
|
|
65 }
|
|
66 cm.scrollTo(null, info.top - cm.defaultTextHeight());
|
|
67 };
|
|
68 cmds.scrollLineDown = function(cm) {
|
|
69 var info = cm.getScrollInfo();
|
|
70 if (!cm.somethingSelected()) {
|
|
71 var visibleTopLine = cm.lineAtHeight(info.top, "local")+1;
|
|
72 if (cm.getCursor().line <= visibleTopLine)
|
|
73 cm.execCommand("goLineDown");
|
|
74 }
|
|
75 cm.scrollTo(null, info.top + cm.defaultTextHeight());
|
|
76 };
|
|
77
|
|
78 cmds.splitSelectionByLine = function(cm) {
|
|
79 var ranges = cm.listSelections(), lineRanges = [];
|
|
80 for (var i = 0; i < ranges.length; i++) {
|
|
81 var from = ranges[i].from(), to = ranges[i].to();
|
|
82 for (var line = from.line; line <= to.line; ++line)
|
|
83 if (!(to.line > from.line && line == to.line && to.ch == 0))
|
|
84 lineRanges.push({anchor: line == from.line ? from : Pos(line, 0),
|
|
85 head: line == to.line ? to : Pos(line)});
|
|
86 }
|
|
87 cm.setSelections(lineRanges, 0);
|
|
88 };
|
|
89
|
|
90 cmds.singleSelectionTop = function(cm) {
|
|
91 var range = cm.listSelections()[0];
|
|
92 cm.setSelection(range.anchor, range.head, {scroll: false});
|
|
93 };
|
|
94
|
|
95 cmds.selectLine = function(cm) {
|
|
96 var ranges = cm.listSelections(), extended = [];
|
|
97 for (var i = 0; i < ranges.length; i++) {
|
|
98 var range = ranges[i];
|
|
99 extended.push({anchor: Pos(range.from().line, 0),
|
|
100 head: Pos(range.to().line + 1, 0)});
|
|
101 }
|
|
102 cm.setSelections(extended);
|
|
103 };
|
|
104
|
|
105 function insertLine(cm, above) {
|
|
106 if (cm.isReadOnly()) return CodeMirror.Pass
|
|
107 cm.operation(function() {
|
|
108 var len = cm.listSelections().length, newSelection = [], last = -1;
|
|
109 for (var i = 0; i < len; i++) {
|
|
110 var head = cm.listSelections()[i].head;
|
|
111 if (head.line <= last) continue;
|
|
112 var at = Pos(head.line + (above ? 0 : 1), 0);
|
|
113 cm.replaceRange("\n", at, null, "+insertLine");
|
|
114 cm.indentLine(at.line, null, true);
|
|
115 newSelection.push({head: at, anchor: at});
|
|
116 last = head.line + 1;
|
|
117 }
|
|
118 cm.setSelections(newSelection);
|
|
119 });
|
|
120 cm.execCommand("indentAuto");
|
|
121 }
|
|
122
|
|
123 cmds.insertLineAfter = function(cm) { return insertLine(cm, false); };
|
|
124
|
|
125 cmds.insertLineBefore = function(cm) { return insertLine(cm, true); };
|
|
126
|
|
127 function wordAt(cm, pos) {
|
|
128 var start = pos.ch, end = start, line = cm.getLine(pos.line);
|
|
129 while (start && CodeMirror.isWordChar(line.charAt(start - 1))) --start;
|
|
130 while (end < line.length && CodeMirror.isWordChar(line.charAt(end))) ++end;
|
|
131 return {from: Pos(pos.line, start), to: Pos(pos.line, end), word: line.slice(start, end)};
|
|
132 }
|
|
133
|
|
134 cmds.selectNextOccurrence = function(cm) {
|
|
135 var from = cm.getCursor("from"), to = cm.getCursor("to");
|
|
136 var fullWord = cm.state.sublimeFindFullWord == cm.doc.sel;
|
|
137 if (CodeMirror.cmpPos(from, to) == 0) {
|
|
138 var word = wordAt(cm, from);
|
|
139 if (!word.word) return;
|
|
140 cm.setSelection(word.from, word.to);
|
|
141 fullWord = true;
|
|
142 } else {
|
|
143 var text = cm.getRange(from, to);
|
|
144 var query = fullWord ? new RegExp("\\b" + text + "\\b") : text;
|
|
145 var cur = cm.getSearchCursor(query, to);
|
|
146 var found = cur.findNext();
|
|
147 if (!found) {
|
|
148 cur = cm.getSearchCursor(query, Pos(cm.firstLine(), 0));
|
|
149 found = cur.findNext();
|
|
150 }
|
|
151 if (!found || isSelectedRange(cm.listSelections(), cur.from(), cur.to())) return
|
|
152 cm.addSelection(cur.from(), cur.to());
|
|
153 }
|
|
154 if (fullWord)
|
|
155 cm.state.sublimeFindFullWord = cm.doc.sel;
|
|
156 };
|
|
157
|
|
158 cmds.skipAndSelectNextOccurrence = function(cm) {
|
|
159 var prevAnchor = cm.getCursor("anchor"), prevHead = cm.getCursor("head");
|
|
160 cmds.selectNextOccurrence(cm);
|
|
161 if (CodeMirror.cmpPos(prevAnchor, prevHead) != 0) {
|
|
162 cm.doc.setSelections(cm.doc.listSelections()
|
|
163 .filter(function (sel) {
|
|
164 return sel.anchor != prevAnchor || sel.head != prevHead;
|
|
165 }));
|
|
166 }
|
|
167 }
|
|
168
|
|
169 function addCursorToSelection(cm, dir) {
|
|
170 var ranges = cm.listSelections(), newRanges = [];
|
|
171 for (var i = 0; i < ranges.length; i++) {
|
|
172 var range = ranges[i];
|
|
173 var newAnchor = cm.findPosV(
|
|
174 range.anchor, dir, "line", range.anchor.goalColumn);
|
|
175 var newHead = cm.findPosV(
|
|
176 range.head, dir, "line", range.head.goalColumn);
|
|
177 newAnchor.goalColumn = range.anchor.goalColumn != null ?
|
|
178 range.anchor.goalColumn : cm.cursorCoords(range.anchor, "div").left;
|
|
179 newHead.goalColumn = range.head.goalColumn != null ?
|
|
180 range.head.goalColumn : cm.cursorCoords(range.head, "div").left;
|
|
181 var newRange = {anchor: newAnchor, head: newHead};
|
|
182 newRanges.push(range);
|
|
183 newRanges.push(newRange);
|
|
184 }
|
|
185 cm.setSelections(newRanges);
|
|
186 }
|
|
187 cmds.addCursorToPrevLine = function(cm) { addCursorToSelection(cm, -1); };
|
|
188 cmds.addCursorToNextLine = function(cm) { addCursorToSelection(cm, 1); };
|
|
189
|
|
190 function isSelectedRange(ranges, from, to) {
|
|
191 for (var i = 0; i < ranges.length; i++)
|
|
192 if (CodeMirror.cmpPos(ranges[i].from(), from) == 0 &&
|
|
193 CodeMirror.cmpPos(ranges[i].to(), to) == 0) return true
|
|
194 return false
|
|
195 }
|
|
196
|
|
197 var mirror = "(){}[]";
|
|
198 function selectBetweenBrackets(cm) {
|
|
199 var ranges = cm.listSelections(), newRanges = []
|
|
200 for (var i = 0; i < ranges.length; i++) {
|
|
201 var range = ranges[i], pos = range.head, opening = cm.scanForBracket(pos, -1);
|
|
202 if (!opening) return false;
|
|
203 for (;;) {
|
|
204 var closing = cm.scanForBracket(pos, 1);
|
|
205 if (!closing) return false;
|
|
206 if (closing.ch == mirror.charAt(mirror.indexOf(opening.ch) + 1)) {
|
|
207 var startPos = Pos(opening.pos.line, opening.pos.ch + 1);
|
|
208 if (CodeMirror.cmpPos(startPos, range.from()) == 0 &&
|
|
209 CodeMirror.cmpPos(closing.pos, range.to()) == 0) {
|
|
210 opening = cm.scanForBracket(opening.pos, -1);
|
|
211 if (!opening) return false;
|
|
212 } else {
|
|
213 newRanges.push({anchor: startPos, head: closing.pos});
|
|
214 break;
|
|
215 }
|
|
216 }
|
|
217 pos = Pos(closing.pos.line, closing.pos.ch + 1);
|
|
218 }
|
|
219 }
|
|
220 cm.setSelections(newRanges);
|
|
221 return true;
|
|
222 }
|
|
223
|
|
224 cmds.selectScope = function(cm) {
|
|
225 selectBetweenBrackets(cm) || cm.execCommand("selectAll");
|
|
226 };
|
|
227 cmds.selectBetweenBrackets = function(cm) {
|
|
228 if (!selectBetweenBrackets(cm)) return CodeMirror.Pass;
|
|
229 };
|
|
230
|
|
231 function puncType(type) {
|
|
232 return !type ? null : /\bpunctuation\b/.test(type) ? type : undefined
|
|
233 }
|
|
234
|
|
235 cmds.goToBracket = function(cm) {
|
|
236 cm.extendSelectionsBy(function(range) {
|
|
237 var next = cm.scanForBracket(range.head, 1, puncType(cm.getTokenTypeAt(range.head)));
|
|
238 if (next && CodeMirror.cmpPos(next.pos, range.head) != 0) return next.pos;
|
|
239 var prev = cm.scanForBracket(range.head, -1, puncType(cm.getTokenTypeAt(Pos(range.head.line, range.head.ch + 1))));
|
|
240 return prev && Pos(prev.pos.line, prev.pos.ch + 1) || range.head;
|
|
241 });
|
|
242 };
|
|
243
|
|
244 cmds.swapLineUp = function(cm) {
|
|
245 if (cm.isReadOnly()) return CodeMirror.Pass
|
|
246 var ranges = cm.listSelections(), linesToMove = [], at = cm.firstLine() - 1, newSels = [];
|
|
247 for (var i = 0; i < ranges.length; i++) {
|
|
248 var range = ranges[i], from = range.from().line - 1, to = range.to().line;
|
|
249 newSels.push({anchor: Pos(range.anchor.line - 1, range.anchor.ch),
|
|
250 head: Pos(range.head.line - 1, range.head.ch)});
|
|
251 if (range.to().ch == 0 && !range.empty()) --to;
|
|
252 if (from > at) linesToMove.push(from, to);
|
|
253 else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to;
|
|
254 at = to;
|
|
255 }
|
|
256 cm.operation(function() {
|
|
257 for (var i = 0; i < linesToMove.length; i += 2) {
|
|
258 var from = linesToMove[i], to = linesToMove[i + 1];
|
|
259 var line = cm.getLine(from);
|
|
260 cm.replaceRange("", Pos(from, 0), Pos(from + 1, 0), "+swapLine");
|
|
261 if (to > cm.lastLine())
|
|
262 cm.replaceRange("\n" + line, Pos(cm.lastLine()), null, "+swapLine");
|
|
263 else
|
|
264 cm.replaceRange(line + "\n", Pos(to, 0), null, "+swapLine");
|
|
265 }
|
|
266 cm.setSelections(newSels);
|
|
267 cm.scrollIntoView();
|
|
268 });
|
|
269 };
|
|
270
|
|
271 cmds.swapLineDown = function(cm) {
|
|
272 if (cm.isReadOnly()) return CodeMirror.Pass
|
|
273 var ranges = cm.listSelections(), linesToMove = [], at = cm.lastLine() + 1;
|
|
274 for (var i = ranges.length - 1; i >= 0; i--) {
|
|
275 var range = ranges[i], from = range.to().line + 1, to = range.from().line;
|
|
276 if (range.to().ch == 0 && !range.empty()) from--;
|
|
277 if (from < at) linesToMove.push(from, to);
|
|
278 else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to;
|
|
279 at = to;
|
|
280 }
|
|
281 cm.operation(function() {
|
|
282 for (var i = linesToMove.length - 2; i >= 0; i -= 2) {
|
|
283 var from = linesToMove[i], to = linesToMove[i + 1];
|
|
284 var line = cm.getLine(from);
|
|
285 if (from == cm.lastLine())
|
|
286 cm.replaceRange("", Pos(from - 1), Pos(from), "+swapLine");
|
|
287 else
|
|
288 cm.replaceRange("", Pos(from, 0), Pos(from + 1, 0), "+swapLine");
|
|
289 cm.replaceRange(line + "\n", Pos(to, 0), null, "+swapLine");
|
|
290 }
|
|
291 cm.scrollIntoView();
|
|
292 });
|
|
293 };
|
|
294
|
|
295 cmds.toggleCommentIndented = function(cm) {
|
|
296 cm.toggleComment({ indent: true });
|
|
297 }
|
|
298
|
|
299 cmds.joinLines = function(cm) {
|
|
300 var ranges = cm.listSelections(), joined = [];
|
|
301 for (var i = 0; i < ranges.length; i++) {
|
|
302 var range = ranges[i], from = range.from();
|
|
303 var start = from.line, end = range.to().line;
|
|
304 while (i < ranges.length - 1 && ranges[i + 1].from().line == end)
|
|
305 end = ranges[++i].to().line;
|
|
306 joined.push({start: start, end: end, anchor: !range.empty() && from});
|
|
307 }
|
|
308 cm.operation(function() {
|
|
309 var offset = 0, ranges = [];
|
|
310 for (var i = 0; i < joined.length; i++) {
|
|
311 var obj = joined[i];
|
|
312 var anchor = obj.anchor && Pos(obj.anchor.line - offset, obj.anchor.ch), head;
|
|
313 for (var line = obj.start; line <= obj.end; line++) {
|
|
314 var actual = line - offset;
|
|
315 if (line == obj.end) head = Pos(actual, cm.getLine(actual).length + 1);
|
|
316 if (actual < cm.lastLine()) {
|
|
317 cm.replaceRange(" ", Pos(actual), Pos(actual + 1, /^\s*/.exec(cm.getLine(actual + 1))[0].length));
|
|
318 ++offset;
|
|
319 }
|
|
320 }
|
|
321 ranges.push({anchor: anchor || head, head: head});
|
|
322 }
|
|
323 cm.setSelections(ranges, 0);
|
|
324 });
|
|
325 };
|
|
326
|
|
327 cmds.duplicateLine = function(cm) {
|
|
328 cm.operation(function() {
|
|
329 var rangeCount = cm.listSelections().length;
|
|
330 for (var i = 0; i < rangeCount; i++) {
|
|
331 var range = cm.listSelections()[i];
|
|
332 if (range.empty())
|
|
333 cm.replaceRange(cm.getLine(range.head.line) + "\n", Pos(range.head.line, 0));
|
|
334 else
|
|
335 cm.replaceRange(cm.getRange(range.from(), range.to()), range.from());
|
|
336 }
|
|
337 cm.scrollIntoView();
|
|
338 });
|
|
339 };
|
|
340
|
|
341
|
|
342 function sortLines(cm, caseSensitive, direction) {
|
|
343 if (cm.isReadOnly()) return CodeMirror.Pass
|
|
344 var ranges = cm.listSelections(), toSort = [], selected;
|
|
345 for (var i = 0; i < ranges.length; i++) {
|
|
346 var range = ranges[i];
|
|
347 if (range.empty()) continue;
|
|
348 var from = range.from().line, to = range.to().line;
|
|
349 while (i < ranges.length - 1 && ranges[i + 1].from().line == to)
|
|
350 to = ranges[++i].to().line;
|
|
351 if (!ranges[i].to().ch) to--;
|
|
352 toSort.push(from, to);
|
|
353 }
|
|
354 if (toSort.length) selected = true;
|
|
355 else toSort.push(cm.firstLine(), cm.lastLine());
|
|
356
|
|
357 cm.operation(function() {
|
|
358 var ranges = [];
|
|
359 for (var i = 0; i < toSort.length; i += 2) {
|
|
360 var from = toSort[i], to = toSort[i + 1];
|
|
361 var start = Pos(from, 0), end = Pos(to);
|
|
362 var lines = cm.getRange(start, end, false);
|
|
363 if (caseSensitive)
|
|
364 lines.sort(function(a, b) { return a < b ? -direction : a == b ? 0 : direction; });
|
|
365 else
|
|
366 lines.sort(function(a, b) {
|
|
367 var au = a.toUpperCase(), bu = b.toUpperCase();
|
|
368 if (au != bu) { a = au; b = bu; }
|
|
369 return a < b ? -direction : a == b ? 0 : direction;
|
|
370 });
|
|
371 cm.replaceRange(lines, start, end);
|
|
372 if (selected) ranges.push({anchor: start, head: Pos(to + 1, 0)});
|
|
373 }
|
|
374 if (selected) cm.setSelections(ranges, 0);
|
|
375 });
|
|
376 }
|
|
377
|
|
378 cmds.sortLines = function(cm) { sortLines(cm, true, 1); };
|
|
379 cmds.reverseSortLines = function(cm) { sortLines(cm, true, -1); };
|
|
380 cmds.sortLinesInsensitive = function(cm) { sortLines(cm, false, 1); };
|
|
381 cmds.reverseSortLinesInsensitive = function(cm) { sortLines(cm, false, -1); };
|
|
382
|
|
383 cmds.nextBookmark = function(cm) {
|
|
384 var marks = cm.state.sublimeBookmarks;
|
|
385 if (marks) while (marks.length) {
|
|
386 var current = marks.shift();
|
|
387 var found = current.find();
|
|
388 if (found) {
|
|
389 marks.push(current);
|
|
390 return cm.setSelection(found.from, found.to);
|
|
391 }
|
|
392 }
|
|
393 };
|
|
394
|
|
395 cmds.prevBookmark = function(cm) {
|
|
396 var marks = cm.state.sublimeBookmarks;
|
|
397 if (marks) while (marks.length) {
|
|
398 marks.unshift(marks.pop());
|
|
399 var found = marks[marks.length - 1].find();
|
|
400 if (!found)
|
|
401 marks.pop();
|
|
402 else
|
|
403 return cm.setSelection(found.from, found.to);
|
|
404 }
|
|
405 };
|
|
406
|
|
407 cmds.toggleBookmark = function(cm) {
|
|
408 var ranges = cm.listSelections();
|
|
409 var marks = cm.state.sublimeBookmarks || (cm.state.sublimeBookmarks = []);
|
|
410 for (var i = 0; i < ranges.length; i++) {
|
|
411 var from = ranges[i].from(), to = ranges[i].to();
|
|
412 var found = ranges[i].empty() ? cm.findMarksAt(from) : cm.findMarks(from, to);
|
|
413 for (var j = 0; j < found.length; j++) {
|
|
414 if (found[j].sublimeBookmark) {
|
|
415 found[j].clear();
|
|
416 for (var k = 0; k < marks.length; k++)
|
|
417 if (marks[k] == found[j])
|
|
418 marks.splice(k--, 1);
|
|
419 break;
|
|
420 }
|
|
421 }
|
|
422 if (j == found.length)
|
|
423 marks.push(cm.markText(from, to, {sublimeBookmark: true, clearWhenEmpty: false}));
|
|
424 }
|
|
425 };
|
|
426
|
|
427 cmds.clearBookmarks = function(cm) {
|
|
428 var marks = cm.state.sublimeBookmarks;
|
|
429 if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear();
|
|
430 marks.length = 0;
|
|
431 };
|
|
432
|
|
433 cmds.selectBookmarks = function(cm) {
|
|
434 var marks = cm.state.sublimeBookmarks, ranges = [];
|
|
435 if (marks) for (var i = 0; i < marks.length; i++) {
|
|
436 var found = marks[i].find();
|
|
437 if (!found)
|
|
438 marks.splice(i--, 0);
|
|
439 else
|
|
440 ranges.push({anchor: found.from, head: found.to});
|
|
441 }
|
|
442 if (ranges.length)
|
|
443 cm.setSelections(ranges, 0);
|
|
444 };
|
|
445
|
|
446 function modifyWordOrSelection(cm, mod) {
|
|
447 cm.operation(function() {
|
|
448 var ranges = cm.listSelections(), indices = [], replacements = [];
|
|
449 for (var i = 0; i < ranges.length; i++) {
|
|
450 var range = ranges[i];
|
|
451 if (range.empty()) { indices.push(i); replacements.push(""); }
|
|
452 else replacements.push(mod(cm.getRange(range.from(), range.to())));
|
|
453 }
|
|
454 cm.replaceSelections(replacements, "around", "case");
|
|
455 for (var i = indices.length - 1, at; i >= 0; i--) {
|
|
456 var range = ranges[indices[i]];
|
|
457 if (at && CodeMirror.cmpPos(range.head, at) > 0) continue;
|
|
458 var word = wordAt(cm, range.head);
|
|
459 at = word.from;
|
|
460 cm.replaceRange(mod(word.word), word.from, word.to);
|
|
461 }
|
|
462 });
|
|
463 }
|
|
464
|
|
465 cmds.smartBackspace = function(cm) {
|
|
466 if (cm.somethingSelected()) return CodeMirror.Pass;
|
|
467
|
|
468 cm.operation(function() {
|
|
469 var cursors = cm.listSelections();
|
|
470 var indentUnit = cm.getOption("indentUnit");
|
|
471
|
|
472 for (var i = cursors.length - 1; i >= 0; i--) {
|
|
473 var cursor = cursors[i].head;
|
|
474 var toStartOfLine = cm.getRange({line: cursor.line, ch: 0}, cursor);
|
|
475 var column = CodeMirror.countColumn(toStartOfLine, null, cm.getOption("tabSize"));
|
|
476
|
|
477 // Delete by one character by default
|
|
478 var deletePos = cm.findPosH(cursor, -1, "char", false);
|
|
479
|
|
480 if (toStartOfLine && !/\S/.test(toStartOfLine) && column % indentUnit == 0) {
|
|
481 var prevIndent = new Pos(cursor.line,
|
|
482 CodeMirror.findColumn(toStartOfLine, column - indentUnit, indentUnit));
|
|
483
|
|
484 // Smart delete only if we found a valid prevIndent location
|
|
485 if (prevIndent.ch != cursor.ch) deletePos = prevIndent;
|
|
486 }
|
|
487
|
|
488 cm.replaceRange("", deletePos, cursor, "+delete");
|
|
489 }
|
|
490 });
|
|
491 };
|
|
492
|
|
493 cmds.delLineRight = function(cm) {
|
|
494 cm.operation(function() {
|
|
495 var ranges = cm.listSelections();
|
|
496 for (var i = ranges.length - 1; i >= 0; i--)
|
|
497 cm.replaceRange("", ranges[i].anchor, Pos(ranges[i].to().line), "+delete");
|
|
498 cm.scrollIntoView();
|
|
499 });
|
|
500 };
|
|
501
|
|
502 cmds.upcaseAtCursor = function(cm) {
|
|
503 modifyWordOrSelection(cm, function(str) { return str.toUpperCase(); });
|
|
504 };
|
|
505 cmds.downcaseAtCursor = function(cm) {
|
|
506 modifyWordOrSelection(cm, function(str) { return str.toLowerCase(); });
|
|
507 };
|
|
508
|
|
509 cmds.setSublimeMark = function(cm) {
|
|
510 if (cm.state.sublimeMark) cm.state.sublimeMark.clear();
|
|
511 cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
|
|
512 };
|
|
513 cmds.selectToSublimeMark = function(cm) {
|
|
514 var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
|
|
515 if (found) cm.setSelection(cm.getCursor(), found);
|
|
516 };
|
|
517 cmds.deleteToSublimeMark = function(cm) {
|
|
518 var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
|
|
519 if (found) {
|
|
520 var from = cm.getCursor(), to = found;
|
|
521 if (CodeMirror.cmpPos(from, to) > 0) { var tmp = to; to = from; from = tmp; }
|
|
522 cm.state.sublimeKilled = cm.getRange(from, to);
|
|
523 cm.replaceRange("", from, to);
|
|
524 }
|
|
525 };
|
|
526 cmds.swapWithSublimeMark = function(cm) {
|
|
527 var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
|
|
528 if (found) {
|
|
529 cm.state.sublimeMark.clear();
|
|
530 cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
|
|
531 cm.setCursor(found);
|
|
532 }
|
|
533 };
|
|
534 cmds.sublimeYank = function(cm) {
|
|
535 if (cm.state.sublimeKilled != null)
|
|
536 cm.replaceSelection(cm.state.sublimeKilled, null, "paste");
|
|
537 };
|
|
538
|
|
539 cmds.showInCenter = function(cm) {
|
|
540 var pos = cm.cursorCoords(null, "local");
|
|
541 cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2);
|
|
542 };
|
|
543
|
|
544 function getTarget(cm) {
|
|
545 var from = cm.getCursor("from"), to = cm.getCursor("to");
|
|
546 if (CodeMirror.cmpPos(from, to) == 0) {
|
|
547 var word = wordAt(cm, from);
|
|
548 if (!word.word) return;
|
|
549 from = word.from;
|
|
550 to = word.to;
|
|
551 }
|
|
552 return {from: from, to: to, query: cm.getRange(from, to), word: word};
|
|
553 }
|
|
554
|
|
555 function findAndGoTo(cm, forward) {
|
|
556 var target = getTarget(cm);
|
|
557 if (!target) return;
|
|
558 var query = target.query;
|
|
559 var cur = cm.getSearchCursor(query, forward ? target.to : target.from);
|
|
560
|
|
561 if (forward ? cur.findNext() : cur.findPrevious()) {
|
|
562 cm.setSelection(cur.from(), cur.to());
|
|
563 } else {
|
|
564 cur = cm.getSearchCursor(query, forward ? Pos(cm.firstLine(), 0)
|
|
565 : cm.clipPos(Pos(cm.lastLine())));
|
|
566 if (forward ? cur.findNext() : cur.findPrevious())
|
|
567 cm.setSelection(cur.from(), cur.to());
|
|
568 else if (target.word)
|
|
569 cm.setSelection(target.from, target.to);
|
|
570 }
|
|
571 };
|
|
572 cmds.findUnder = function(cm) { findAndGoTo(cm, true); };
|
|
573 cmds.findUnderPrevious = function(cm) { findAndGoTo(cm,false); };
|
|
574 cmds.findAllUnder = function(cm) {
|
|
575 var target = getTarget(cm);
|
|
576 if (!target) return;
|
|
577 var cur = cm.getSearchCursor(target.query);
|
|
578 var matches = [];
|
|
579 var primaryIndex = -1;
|
|
580 while (cur.findNext()) {
|
|
581 matches.push({anchor: cur.from(), head: cur.to()});
|
|
582 if (cur.from().line <= target.from.line && cur.from().ch <= target.from.ch)
|
|
583 primaryIndex++;
|
|
584 }
|
|
585 cm.setSelections(matches, primaryIndex);
|
|
586 };
|
|
587
|
|
588
|
|
589 var keyMap = CodeMirror.keyMap;
|
|
590 keyMap.macSublime = {
|
|
591 "Cmd-Left": "goLineStartSmart",
|
|
592 "Shift-Tab": "indentLess",
|
|
593 "Shift-Ctrl-K": "deleteLine",
|
|
594 "Alt-Q": "wrapLines",
|
|
595 "Ctrl-Left": "goSubwordLeft",
|
|
596 "Ctrl-Right": "goSubwordRight",
|
|
597 "Ctrl-Alt-Up": "scrollLineUp",
|
|
598 "Ctrl-Alt-Down": "scrollLineDown",
|
|
599 "Cmd-L": "selectLine",
|
|
600 "Shift-Cmd-L": "splitSelectionByLine",
|
|
601 "Esc": "singleSelectionTop",
|
|
602 "Cmd-Enter": "insertLineAfter",
|
|
603 "Shift-Cmd-Enter": "insertLineBefore",
|
|
604 "Cmd-D": "selectNextOccurrence",
|
|
605 "Shift-Cmd-Space": "selectScope",
|
|
606 "Shift-Cmd-M": "selectBetweenBrackets",
|
|
607 "Cmd-M": "goToBracket",
|
|
608 "Cmd-Ctrl-Up": "swapLineUp",
|
|
609 "Cmd-Ctrl-Down": "swapLineDown",
|
|
610 "Cmd-/": "toggleCommentIndented",
|
|
611 "Cmd-J": "joinLines",
|
|
612 "Shift-Cmd-D": "duplicateLine",
|
|
613 "F5": "sortLines",
|
|
614 "Shift-F5": "reverseSortLines",
|
|
615 "Cmd-F5": "sortLinesInsensitive",
|
|
616 "Shift-Cmd-F5": "reverseSortLinesInsensitive",
|
|
617 "F2": "nextBookmark",
|
|
618 "Shift-F2": "prevBookmark",
|
|
619 "Cmd-F2": "toggleBookmark",
|
|
620 "Shift-Cmd-F2": "clearBookmarks",
|
|
621 "Alt-F2": "selectBookmarks",
|
|
622 "Backspace": "smartBackspace",
|
|
623 "Cmd-K Cmd-D": "skipAndSelectNextOccurrence",
|
|
624 "Cmd-K Cmd-K": "delLineRight",
|
|
625 "Cmd-K Cmd-U": "upcaseAtCursor",
|
|
626 "Cmd-K Cmd-L": "downcaseAtCursor",
|
|
627 "Cmd-K Cmd-Space": "setSublimeMark",
|
|
628 "Cmd-K Cmd-A": "selectToSublimeMark",
|
|
629 "Cmd-K Cmd-W": "deleteToSublimeMark",
|
|
630 "Cmd-K Cmd-X": "swapWithSublimeMark",
|
|
631 "Cmd-K Cmd-Y": "sublimeYank",
|
|
632 "Cmd-K Cmd-C": "showInCenter",
|
|
633 "Cmd-K Cmd-G": "clearBookmarks",
|
|
634 "Cmd-K Cmd-Backspace": "delLineLeft",
|
|
635 "Cmd-K Cmd-1": "foldAll",
|
|
636 "Cmd-K Cmd-0": "unfoldAll",
|
|
637 "Cmd-K Cmd-J": "unfoldAll",
|
|
638 "Ctrl-Shift-Up": "addCursorToPrevLine",
|
|
639 "Ctrl-Shift-Down": "addCursorToNextLine",
|
|
640 "Cmd-F3": "findUnder",
|
|
641 "Shift-Cmd-F3": "findUnderPrevious",
|
|
642 "Alt-F3": "findAllUnder",
|
|
643 "Shift-Cmd-[": "fold",
|
|
644 "Shift-Cmd-]": "unfold",
|
|
645 "Cmd-I": "findIncremental",
|
|
646 "Shift-Cmd-I": "findIncrementalReverse",
|
|
647 "Cmd-H": "replace",
|
|
648 "F3": "findNext",
|
|
649 "Shift-F3": "findPrev",
|
|
650 "fallthrough": "macDefault"
|
|
651 };
|
|
652 CodeMirror.normalizeKeyMap(keyMap.macSublime);
|
|
653
|
|
654 keyMap.pcSublime = {
|
|
655 "Shift-Tab": "indentLess",
|
|
656 "Shift-Ctrl-K": "deleteLine",
|
|
657 "Alt-Q": "wrapLines",
|
|
658 "Ctrl-T": "transposeChars",
|
|
659 "Alt-Left": "goSubwordLeft",
|
|
660 "Alt-Right": "goSubwordRight",
|
|
661 "Ctrl-Up": "scrollLineUp",
|
|
662 "Ctrl-Down": "scrollLineDown",
|
|
663 "Ctrl-L": "selectLine",
|
|
664 "Shift-Ctrl-L": "splitSelectionByLine",
|
|
665 "Esc": "singleSelectionTop",
|
|
666 "Ctrl-Enter": "insertLineAfter",
|
|
667 "Shift-Ctrl-Enter": "insertLineBefore",
|
|
668 "Ctrl-D": "selectNextOccurrence",
|
|
669 "Shift-Ctrl-Space": "selectScope",
|
|
670 "Shift-Ctrl-M": "selectBetweenBrackets",
|
|
671 "Ctrl-M": "goToBracket",
|
|
672 "Shift-Ctrl-Up": "swapLineUp",
|
|
673 "Shift-Ctrl-Down": "swapLineDown",
|
|
674 "Ctrl-/": "toggleCommentIndented",
|
|
675 "Ctrl-J": "joinLines",
|
|
676 "Shift-Ctrl-D": "duplicateLine",
|
|
677 "F9": "sortLines",
|
|
678 "Shift-F9": "reverseSortLines",
|
|
679 "Ctrl-F9": "sortLinesInsensitive",
|
|
680 "Shift-Ctrl-F9": "reverseSortLinesInsensitive",
|
|
681 "F2": "nextBookmark",
|
|
682 "Shift-F2": "prevBookmark",
|
|
683 "Ctrl-F2": "toggleBookmark",
|
|
684 "Shift-Ctrl-F2": "clearBookmarks",
|
|
685 "Alt-F2": "selectBookmarks",
|
|
686 "Backspace": "smartBackspace",
|
|
687 "Ctrl-K Ctrl-D": "skipAndSelectNextOccurrence",
|
|
688 "Ctrl-K Ctrl-K": "delLineRight",
|
|
689 "Ctrl-K Ctrl-U": "upcaseAtCursor",
|
|
690 "Ctrl-K Ctrl-L": "downcaseAtCursor",
|
|
691 "Ctrl-K Ctrl-Space": "setSublimeMark",
|
|
692 "Ctrl-K Ctrl-A": "selectToSublimeMark",
|
|
693 "Ctrl-K Ctrl-W": "deleteToSublimeMark",
|
|
694 "Ctrl-K Ctrl-X": "swapWithSublimeMark",
|
|
695 "Ctrl-K Ctrl-Y": "sublimeYank",
|
|
696 "Ctrl-K Ctrl-C": "showInCenter",
|
|
697 "Ctrl-K Ctrl-G": "clearBookmarks",
|
|
698 "Ctrl-K Ctrl-Backspace": "delLineLeft",
|
|
699 "Ctrl-K Ctrl-1": "foldAll",
|
|
700 "Ctrl-K Ctrl-0": "unfoldAll",
|
|
701 "Ctrl-K Ctrl-J": "unfoldAll",
|
|
702 "Ctrl-Alt-Up": "addCursorToPrevLine",
|
|
703 "Ctrl-Alt-Down": "addCursorToNextLine",
|
|
704 "Ctrl-F3": "findUnder",
|
|
705 "Shift-Ctrl-F3": "findUnderPrevious",
|
|
706 "Alt-F3": "findAllUnder",
|
|
707 "Shift-Ctrl-[": "fold",
|
|
708 "Shift-Ctrl-]": "unfold",
|
|
709 "Ctrl-I": "findIncremental",
|
|
710 "Shift-Ctrl-I": "findIncrementalReverse",
|
|
711 "Ctrl-H": "replace",
|
|
712 "F3": "findNext",
|
|
713 "Shift-F3": "findPrev",
|
|
714 "fallthrough": "pcDefault"
|
|
715 };
|
|
716 CodeMirror.normalizeKeyMap(keyMap.pcSublime);
|
|
717
|
|
718 var mac = keyMap.default == keyMap.macDefault;
|
|
719 keyMap.sublime = mac ? keyMap.macSublime : keyMap.pcSublime;
|
|
720 });
|