codemirror upgrade

This commit is contained in:
Van 2014-08-30 11:26:14 +08:00
parent 8f28d1e308
commit 037159e39e
282 changed files with 1719 additions and 493 deletions

View File

@ -1,117 +0,0 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function() {
var mode = CodeMirror.getMode({indentUnit: 4}, "verilog");
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
MT("Binary literals",
"[number 1'b0]",
"[number 1'b1]",
"[number 1'bx]",
"[number 1'bz]",
"[number 1'bX]",
"[number 1'bZ]",
"[number 1'B0]",
"[number 1'B1]",
"[number 1'Bx]",
"[number 1'Bz]",
"[number 1'BX]",
"[number 1'BZ]",
"[number 1'b0]",
"[number 1'b1]",
"[number 2'b01]",
"[number 2'bxz]",
"[number 2'b11]",
"[number 2'b10]",
"[number 2'b1Z]",
"[number 12'b0101_0101_0101]",
"[number 1'b 0]",
"[number 'b0101]"
);
MT("Octal literals",
"[number 3'o7]",
"[number 3'O7]",
"[number 3'so7]",
"[number 3'SO7]"
);
MT("Decimal literals",
"[number 0]",
"[number 1]",
"[number 7]",
"[number 123_456]",
"[number 'd33]",
"[number 8'd255]",
"[number 8'D255]",
"[number 8'sd255]",
"[number 8'SD255]",
"[number 32'd123]",
"[number 32 'd123]",
"[number 32 'd 123]"
);
MT("Hex literals",
"[number 4'h0]",
"[number 4'ha]",
"[number 4'hF]",
"[number 4'hx]",
"[number 4'hz]",
"[number 4'hX]",
"[number 4'hZ]",
"[number 32'hdc78]",
"[number 32'hDC78]",
"[number 32 'hDC78]",
"[number 32'h DC78]",
"[number 32 'h DC78]",
"[number 32'h44x7]",
"[number 32'hFFF?]"
);
MT("Real number literals",
"[number 1.2]",
"[number 0.1]",
"[number 2394.26331]",
"[number 1.2E12]",
"[number 1.2e12]",
"[number 1.30e-2]",
"[number 0.1e-0]",
"[number 23E10]",
"[number 29E-2]",
"[number 236.123_763_e-12]"
);
MT("Operators",
"[meta ^]"
);
MT("Keywords",
"[keyword logic]",
"[keyword logic] [variable foo]",
"[keyword reg] [variable abc]"
);
MT("Variables",
"[variable _leading_underscore]",
"[variable _if]",
"[number 12] [variable foo]",
"[variable foo] [number 14]"
);
MT("Tick defines",
"[def `FOO]",
"[def `foo]",
"[def `FOO_bar]"
);
MT("System calls",
"[meta $display]",
"[meta $vpi_printf]"
);
MT("Line comment", "[comment // Hello world]");
})();

View File

@ -157,12 +157,12 @@
// Positions of the last startString before the start of the selection, and the first endString after it. // Positions of the last startString before the start of the selection, and the first endString after it.
var lastStart = startLine.lastIndexOf(startString, from.ch); var lastStart = startLine.lastIndexOf(startString, from.ch);
var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length); var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length);
if (lastStart != -1 && firstEnd != -1) return false; if (lastStart != -1 && firstEnd != -1 && firstEnd + endString.length != from.ch) return false;
// Positions of the first endString after the end of the selection, and the last startString before it. // Positions of the first endString after the end of the selection, and the last startString before it.
firstEnd = endLine.indexOf(endString, to.ch); firstEnd = endLine.indexOf(endString, to.ch);
var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch); var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch);
lastStart = (firstEnd == -1 || almostLastStart == -1) ? -1 : to.ch + almostLastStart; lastStart = (firstEnd == -1 || almostLastStart == -1) ? -1 : to.ch + almostLastStart;
if (firstEnd != -1 && lastStart != -1) return false; if (firstEnd != -1 && lastStart != -1 && lastStart != to.ch) return false;
self.operation(function() { self.operation(function() {
self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)), self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)),

View File

@ -15,11 +15,11 @@
var wrap = cm.getWrapperElement(); var wrap = cm.getWrapperElement();
var dialog; var dialog;
dialog = wrap.appendChild(document.createElement("div")); dialog = wrap.appendChild(document.createElement("div"));
if (bottom) { if (bottom)
dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom"; dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom";
} else { else
dialog.className = "CodeMirror-dialog CodeMirror-dialog-top"; dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";
}
if (typeof template == "string") { if (typeof template == "string") {
dialog.innerHTML = template; dialog.innerHTML = template;
} else { // Assuming it's a detached DOM element. } else { // Assuming it's a detached DOM element.
@ -35,8 +35,11 @@
} }
CodeMirror.defineExtension("openDialog", function(template, callback, options) { CodeMirror.defineExtension("openDialog", function(template, callback, options) {
if (!options) options = {};
closeNotification(this, null); closeNotification(this, null);
var dialog = dialogDiv(this, template, options && options.bottom);
var dialog = dialogDiv(this, template, options.bottom);
var closed = false, me = this; var closed = false, me = this;
function close(newVal) { function close(newVal) {
if (typeof newVal == 'string') { if (typeof newVal == 'string') {
@ -45,34 +48,43 @@
if (closed) return; if (closed) return;
closed = true; closed = true;
dialog.parentNode.removeChild(dialog); dialog.parentNode.removeChild(dialog);
me.focus();
if (options.onClose) options.onClose(dialog);
} }
} }
var inp = dialog.getElementsByTagName("input")[0], button; var inp = dialog.getElementsByTagName("input")[0], button;
if (inp) { if (inp) {
if (options && options.value) inp.value = options.value; if (options.value) inp.value = options.value;
if (options.onInput)
CodeMirror.on(inp, "input", function(e) { options.onInput(e, inp.value, close);});
if (options.onKeyUp)
CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);});
CodeMirror.on(inp, "keydown", function(e) { CodeMirror.on(inp, "keydown", function(e) {
if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; } if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }
if (e.keyCode == 13 || e.keyCode == 27) { if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) {
inp.blur(); inp.blur();
CodeMirror.e_stop(e); CodeMirror.e_stop(e);
close(); close();
me.focus(); }
if (e.keyCode == 13) callback(inp.value); if (e.keyCode == 13) callback(inp.value);
}
}); });
if (options && options.onKeyUp) {
CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);}); if (options.closeOnBlur !== false) CodeMirror.on(inp, "blur", close);
}
if (options && options.value) inp.value = options.value;
inp.focus(); inp.focus();
CodeMirror.on(inp, "blur", close);
} else if (button = dialog.getElementsByTagName("button")[0]) { } else if (button = dialog.getElementsByTagName("button")[0]) {
CodeMirror.on(button, "click", function() { CodeMirror.on(button, "click", function() {
close(); close();
me.focus(); me.focus();
}); });
if (options.closeOnBlur !== false) CodeMirror.on(button, "blur", close);
button.focus(); button.focus();
CodeMirror.on(button, "blur", close);
} }
return close; return close;
}); });

View File

@ -109,6 +109,10 @@
replacements[i] = "/" + state.context.tagName + ">"; replacements[i] = "/" + state.context.tagName + ">";
} }
cm.replaceSelections(replacements); cm.replaceSelections(replacements);
ranges = cm.listSelections();
for (var i = 0; i < ranges.length; i++)
if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
cm.indentLine(ranges[i].head.line);
} }
function indexOf(collection, elt) { function indexOf(collection, elt) {

View File

@ -151,8 +151,9 @@
if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return; if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return;
var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch); var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch);
var start = end && toTagStart(iter); var start = end && toTagStart(iter);
if (!end || end == "selfClose" || !start || cmp(iter, pos) > 0) return; if (!end || !start || cmp(iter, pos) > 0) return;
var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]}; var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]};
if (end == "selfClose") return {open: here, close: null, at: "open"};
if (start[1]) { // closing tag if (start[1]) { // closing tag
return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"}; return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"};

View File

@ -21,7 +21,7 @@
function getKeywords(editor) { function getKeywords(editor) {
var mode = editor.doc.modeOption; var mode = editor.doc.modeOption;
if(mode === "sql") mode = "text/x-sql"; if (mode === "sql") mode = "text/x-sql";
return CodeMirror.resolveMode(mode).keywords; return CodeMirror.resolveMode(mode).keywords;
} }
@ -32,12 +32,12 @@
} }
function addMatches(result, search, wordlist, formatter) { function addMatches(result, search, wordlist, formatter) {
for(var word in wordlist) { for (var word in wordlist) {
if(!wordlist.hasOwnProperty(word)) continue; if (!wordlist.hasOwnProperty(word)) continue;
if(Array.isArray(wordlist)) { if (Array.isArray(wordlist)) {
word = wordlist[word]; word = wordlist[word];
} }
if(match(search, word)) { if (match(search, word)) {
result.push(formatter(word)); result.push(formatter(word));
} }
} }
@ -49,33 +49,30 @@
var string = token.string.substr(1); var string = token.string.substr(1);
var prevCur = Pos(cur.line, token.start); var prevCur = Pos(cur.line, token.start);
var table = editor.getTokenAt(prevCur).string; var table = editor.getTokenAt(prevCur).string;
if( !tables.hasOwnProperty( table ) ){ if (!tables.hasOwnProperty(table))
table = findTableByAlias(table, editor); table = findTableByAlias(table, editor);
}
var columns = tables[table]; var columns = tables[table];
if(!columns) { if (!columns) return;
return;
} addMatches(result, string, columns, function(w) {return "." + w;});
addMatches(result, string, columns,
function(w) {return "." + w;});
} }
function eachWord(lineText, f) { function eachWord(lineText, f) {
if( !lineText ){return;} if (!lineText) return;
var excepted = /[,;]/g; var excepted = /[,;]/g;
var words = lineText.split( " " ); var words = lineText.split(" ");
for( var i = 0; i < words.length; i++ ){ for (var i = 0; i < words.length; i++) {
f( words[i]?words[i].replace( excepted, '' ) : '' ); f(words[i]?words[i].replace(excepted, '') : '');
} }
} }
function convertCurToNumber( cur ){ function convertCurToNumber(cur) {
// max characters of a line is 999,999. // max characters of a line is 999,999.
return cur.line + cur.ch / Math.pow( 10, 6 ); return cur.line + cur.ch / Math.pow(10, 6);
} }
function convertNumberToCur( num ){ function convertNumberToCur(num) {
return Pos(Math.floor( num ), +num.toString().split( '.' ).pop()); return Pos(Math.floor(num), +num.toString().split('.').pop());
} }
function findTableByAlias(alias, editor) { function findTableByAlias(alias, editor) {
@ -86,26 +83,26 @@
var table = ""; var table = "";
var separator = []; var separator = [];
var validRange = { var validRange = {
start: Pos( 0, 0 ), start: Pos(0, 0),
end: Pos( editor.lastLine(), editor.getLineHandle( editor.lastLine() ).length ) end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length)
}; };
//add separator //add separator
var indexOfSeparator = fullQuery.indexOf( CONS.QUERY_DIV ); var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV);
while( indexOfSeparator != -1 ){ while(indexOfSeparator != -1) {
separator.push( doc.posFromIndex(indexOfSeparator)); separator.push(doc.posFromIndex(indexOfSeparator));
indexOfSeparator = fullQuery.indexOf( CONS.QUERY_DIV, indexOfSeparator+1); indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1);
} }
separator.unshift( Pos( 0, 0 ) ); separator.unshift(Pos(0, 0));
separator.push( Pos( editor.lastLine(), editor.getLineHandle( editor.lastLine() ).text.length ) ); separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length));
//find valieRange //find valid range
var prevItem = 0; var prevItem = 0;
var current = convertCurToNumber( editor.getCursor() ); var current = convertCurToNumber(editor.getCursor());
for( var i=0; i< separator.length; i++){ for (var i=0; i< separator.length; i++) {
var _v = convertCurToNumber( separator[i] ); var _v = convertCurToNumber(separator[i]);
if( current > prevItem && current <= _v ){ if (current > prevItem && current <= _v) {
validRange = { start: convertNumberToCur( prevItem ), end: convertNumberToCur( _v ) }; validRange = { start: convertNumberToCur(prevItem), end: convertNumberToCur(_v) };
break; break;
} }
prevItem = _v; prevItem = _v;
@ -113,52 +110,51 @@
var query = doc.getRange(validRange.start, validRange.end, false); var query = doc.getRange(validRange.start, validRange.end, false);
for(var i=0; i < query.length; i++){ for (var i = 0; i < query.length; i++) {
var lineText = query[i]; var lineText = query[i];
eachWord( lineText, function( word ){ eachWord(lineText, function(word) {
var wordUpperCase = word.toUpperCase(); var wordUpperCase = word.toUpperCase();
if( wordUpperCase === aliasUpperCase && tables.hasOwnProperty( previousWord ) ){ if (wordUpperCase === aliasUpperCase && tables.hasOwnProperty(previousWord)) {
table = previousWord; table = previousWord;
} }
if( wordUpperCase !== CONS.ALIAS_KEYWORD ){ if (wordUpperCase !== CONS.ALIAS_KEYWORD) {
previousWord = word; previousWord = word;
} }
}); });
if( table ){ break; } if (table) break;
} }
return table; return table;
} }
function sqlHint(editor, options) { CodeMirror.registerHelper("hint", "sql", function(editor, options) {
tables = (options && options.tables) || {}; tables = (options && options.tables) || {};
keywords = keywords || getKeywords(editor); keywords = keywords || getKeywords(editor);
var cur = editor.getCursor(); var cur = editor.getCursor();
var token = editor.getTokenAt(cur), end = token.end;
var result = []; var result = [];
var search = token.string.trim(); var token = editor.getTokenAt(cur), start, end, search;
if (token.string.match(/^[.\w@]\w*$/)) {
search = token.string;
start = token.start;
end = token.end;
} else {
start = end = cur.ch;
search = "";
}
if (search.charAt(0) == ".") { if (search.charAt(0) == ".") {
columnCompletion(result, editor); columnCompletion(result, editor);
if (!result.length) { if (!result.length) {
while (token.start && search.charAt(0) == ".") { while (start && search.charAt(0) == ".") {
token = editor.getTokenAt(Pos(cur.line, token.start - 1)); token = editor.getTokenAt(Pos(cur.line, token.start - 1));
start = token.start;
search = token.string + search; search = token.string + search;
} }
addMatches(result, search, tables, addMatches(result, search, tables, function(w) {return w;});
function(w) {return w;});
} }
} else { } else {
addMatches(result, search, keywords, addMatches(result, search, tables, function(w) {return w;});
function(w) {return w.toUpperCase();}); addMatches(result, search, keywords, function(w) {return w.toUpperCase();});
addMatches(result, search, tables,
function(w) {return w;});
} }
return { return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)};
list: result, });
from: Pos(cur.line, token.start),
to: Pos(cur.line, end)
};
}
CodeMirror.registerHelper("hint", "sql", sqlHint);
}); });

View File

@ -62,6 +62,12 @@
color: #44c; color: #44c;
} }
.CodeMirror-merge-copy-reverse {
position: absolute;
cursor: pointer;
color: #44c;
}
.CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy { left: 2px; } .CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy { left: 2px; }
.CodeMirror-merge-copybuttons-right .CodeMirror-merge-copy { right: 2px; } .CodeMirror-merge-copybuttons-right .CodeMirror-merge-copy { right: 2px; }

View File

@ -37,7 +37,7 @@
constructor: DiffView, constructor: DiffView,
init: function(pane, orig, options) { init: function(pane, orig, options) {
this.edit = this.mv.edit; this.edit = this.mv.edit;
this.orig = CodeMirror(pane, copyObj({value: orig, readOnly: true}, copyObj(options))); this.orig = CodeMirror(pane, copyObj({value: orig, readOnly: !this.mv.options.allowEditingOriginals}, copyObj(options)));
this.diff = getDiff(asString(orig), asString(options.value)); this.diff = getDiff(asString(orig), asString(options.value));
this.diffOutOfDate = false; this.diffOutOfDate = false;
@ -71,7 +71,7 @@
function update(mode) { function update(mode) {
if (mode == "full") { if (mode == "full") {
if (dv.svg) clear(dv.svg); if (dv.svg) clear(dv.svg);
clear(dv.copyButtons); if (dv.copyButtons) clear(dv.copyButtons);
clearMarks(dv.edit, edit.marked, dv.classes); clearMarks(dv.edit, edit.marked, dv.classes);
clearMarks(dv.orig, orig.marked, dv.classes); clearMarks(dv.orig, orig.marked, dv.classes);
edit.from = edit.to = orig.from = orig.to = 0; edit.from = edit.to = orig.from = orig.to = 0;
@ -257,7 +257,7 @@
var w = dv.gap.offsetWidth; var w = dv.gap.offsetWidth;
attrs(dv.svg, "width", w, "height", dv.gap.offsetHeight); attrs(dv.svg, "width", w, "height", dv.gap.offsetHeight);
} }
clear(dv.copyButtons); if (dv.copyButtons) clear(dv.copyButtons);
var flip = dv.type == "left"; var flip = dv.type == "left";
var vpEdit = dv.edit.getViewport(), vpOrig = dv.orig.getViewport(); var vpEdit = dv.edit.getViewport(), vpOrig = dv.orig.getViewport();
@ -279,17 +279,30 @@
"d", "M -1 " + topRpx + curveTop + " L " + (w + 2) + " " + botLpx + curveBot + " z", "d", "M -1 " + topRpx + curveTop + " L " + (w + 2) + " " + botLpx + curveBot + " z",
"class", dv.classes.connect); "class", dv.classes.connect);
} }
if (dv.copyButtons) {
var copy = dv.copyButtons.appendChild(elt("div", dv.type == "left" ? "\u21dd" : "\u21dc", var copy = dv.copyButtons.appendChild(elt("div", dv.type == "left" ? "\u21dd" : "\u21dc",
"CodeMirror-merge-copy")); "CodeMirror-merge-copy"));
copy.title = "Revert chunk"; var editOriginals = dv.mv.options.allowEditingOriginals;
copy.title = editOriginals ? "Push to left" : "Revert chunk";
copy.chunk = {topEdit: topEdit, botEdit: botEdit, topOrig: topOrig, botOrig: botOrig}; copy.chunk = {topEdit: topEdit, botEdit: botEdit, topOrig: topOrig, botOrig: botOrig};
copy.style.top = top + "px"; copy.style.top = top + "px";
if (editOriginals) {
var topReverse = dv.orig.heightAtLine(topEdit, "local") - sTopEdit;
var copyReverse = dv.copyButtons.appendChild(elt("div", dv.type == "right" ? "\u21dd" : "\u21dc",
"CodeMirror-merge-copy-reverse"));
copyReverse.title = "Push to right";
copyReverse.chunk = {topEdit: topOrig, botEdit: botOrig, topOrig: topEdit, botOrig: botEdit};
copyReverse.style.top = topReverse + "px";
dv.type == "right" ? copyReverse.style.left = "2px" : copyReverse.style.right = "2px";
}
}
}); });
} }
function copyChunk(dv, chunk) { function copyChunk(dv, to, from, chunk) {
if (dv.diffOutOfDate) return; if (dv.diffOutOfDate) return;
dv.edit.replaceRange(dv.orig.getRange(Pos(chunk.topOrig, 0), Pos(chunk.botOrig, 0)), to.replaceRange(from.getRange(Pos(chunk.topOrig, 0), Pos(chunk.botOrig, 0)),
Pos(chunk.topEdit, 0), Pos(chunk.botEdit, 0)); Pos(chunk.topEdit, 0), Pos(chunk.botEdit, 0));
} }
@ -298,6 +311,7 @@
var MergeView = CodeMirror.MergeView = function(node, options) { var MergeView = CodeMirror.MergeView = function(node, options) {
if (!(this instanceof MergeView)) return new MergeView(node, options); if (!(this instanceof MergeView)) return new MergeView(node, options);
this.options = options;
var origLeft = options.origLeft, origRight = options.origRight == null ? options.orig : options.origRight; var origLeft = options.origLeft, origRight = options.origRight == null ? options.orig : options.origRight;
var hasLeft = origLeft != null, hasRight = origRight != null; var hasLeft = origLeft != null, hasRight = origRight != null;
var panes = 1 + (hasLeft ? 1 : 0) + (hasRight ? 1 : 0); var panes = 1 + (hasLeft ? 1 : 0) + (hasRight ? 1 : 0);
@ -323,6 +337,7 @@
(hasRight ? rightPane : editPane).className += " CodeMirror-merge-pane-rightmost"; (hasRight ? rightPane : editPane).className += " CodeMirror-merge-pane-rightmost";
wrap.push(elt("div", null, null, "height: 0; clear: both;")); wrap.push(elt("div", null, null, "height: 0; clear: both;"));
var wrapElt = this.wrap = node.appendChild(elt("div", wrap, "CodeMirror-merge CodeMirror-merge-" + panes + "pane")); var wrapElt = this.wrap = node.appendChild(elt("div", wrap, "CodeMirror-merge CodeMirror-merge-" + panes + "pane"));
this.edit = CodeMirror(editPane, copyObj(options)); this.edit = CodeMirror(editPane, copyObj(options));
@ -345,12 +360,20 @@
lock.title = "Toggle locked scrolling"; lock.title = "Toggle locked scrolling";
var lockWrap = elt("div", [lock], "CodeMirror-merge-scrolllock-wrap"); var lockWrap = elt("div", [lock], "CodeMirror-merge-scrolllock-wrap");
CodeMirror.on(lock, "click", function() { setScrollLock(dv, !dv.lockScroll); }); CodeMirror.on(lock, "click", function() { setScrollLock(dv, !dv.lockScroll); });
var gapElts = [lockWrap];
if (dv.mv.options.revertButtons !== false) {
dv.copyButtons = elt("div", null, "CodeMirror-merge-copybuttons-" + dv.type); dv.copyButtons = elt("div", null, "CodeMirror-merge-copybuttons-" + dv.type);
CodeMirror.on(dv.copyButtons, "click", function(e) { CodeMirror.on(dv.copyButtons, "click", function(e) {
var node = e.target || e.srcElement; var node = e.target || e.srcElement;
if (node.chunk) copyChunk(dv, node.chunk); if (!node.chunk) return;
if (node.className == "CodeMirror-merge-copy-reverse") {
copyChunk(dv, dv.orig, dv.edit, node.chunk);
return;
}
copyChunk(dv, dv.edit, dv.orig, node.chunk);
}); });
var gapElts = [dv.copyButtons, lockWrap]; gapElts.unshift(dv.copyButtons);
}
var svg = document.createElementNS && document.createElementNS(svgNS, "svg"); var svg = document.createElementNS && document.createElementNS(svgNS, "svg");
if (svg && !svg.createSVGRect) svg = null; if (svg && !svg.createSVGRect) svg = null;
dv.svg = svg; dv.svg = svg;

View File

@ -71,7 +71,7 @@
return query; return query;
} }
var queryDialog = var queryDialog =
'Search: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>'; 'Search: <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">(Use /re/ syntax for regexp search)</span>';
function doSearch(cm, rev) { function doSearch(cm, rev) {
var state = getSearchState(cm); var state = getSearchState(cm);
if (state.query) return findNext(cm, rev); if (state.query) return findNext(cm, rev);
@ -106,8 +106,8 @@
});} });}
var replaceQueryDialog = var replaceQueryDialog =
'Replace: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>'; 'Replace: <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">(Use /re/ syntax for regexp search)</span>';
var replacementQueryDialog = 'With: <input type="text" style="width: 10em"/>'; var replacementQueryDialog = 'With: <input type="text" style="width: 10em" class="CodeMirror-search-field"/>';
var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>"; var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>";
function replace(cm, all) { function replace(cm, all) {
if (cm.getOption("readOnly")) return; if (cm.getOption("readOnly")) return;

View File

@ -662,6 +662,7 @@
function updateDisplaySimple(cm, viewport) { function updateDisplaySimple(cm, viewport) {
var update = new DisplayUpdate(cm, viewport); var update = new DisplayUpdate(cm, viewport);
if (updateDisplayIfNeeded(cm, update)) { if (updateDisplayIfNeeded(cm, update)) {
updateHeightsInViewport(cm);
postUpdateDisplay(cm, update); postUpdateDisplay(cm, update);
var barMeasure = measureForScrollbars(cm); var barMeasure = measureForScrollbars(cm);
updateSelection(cm); updateSelection(cm);
@ -1280,8 +1281,7 @@
return result; return result;
} }
function updateSelection(cm, drawn) { function showSelection(cm, drawn) {
if (!drawn) drawn = drawSelection(cm);
removeChildrenAndAdd(cm.display.cursorDiv, drawn.cursors); removeChildrenAndAdd(cm.display.cursorDiv, drawn.cursors);
removeChildrenAndAdd(cm.display.selectionDiv, drawn.selection); removeChildrenAndAdd(cm.display.selectionDiv, drawn.selection);
if (drawn.teTop != null) { if (drawn.teTop != null) {
@ -1290,6 +1290,10 @@
} }
} }
function updateSelection(cm) {
showSelection(cm, drawSelection(cm));
}
// Draws a cursor for the given range // Draws a cursor for the given range
function drawSelectionCursor(cm, range, output) { function drawSelectionCursor(cm, range, output) {
var pos = cursorCoords(cm, range.head, "div", null, null, !cm.options.singleCursorHeightPerLine); var pos = cursorCoords(cm, range.head, "div", null, null, !cm.options.singleCursorHeightPerLine);
@ -1642,6 +1646,7 @@
var rect; var rect;
if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
for (;;) {
while (start && isExtendingChar(prepared.line.text.charAt(mStart + start))) --start; while (start && isExtendingChar(prepared.line.text.charAt(mStart + start))) --start;
while (mStart + end < mEnd && isExtendingChar(prepared.line.text.charAt(mStart + end))) ++end; while (mStart + end < mEnd && isExtendingChar(prepared.line.text.charAt(mStart + end))) ++end;
if (ie && ie_version < 9 && start == 0 && end == mEnd - mStart) { if (ie && ie_version < 9 && start == 0 && end == mEnd - mStart) {
@ -1655,6 +1660,11 @@
} else { } else {
rect = range(node, start, end).getBoundingClientRect() || nullRect; rect = range(node, start, end).getBoundingClientRect() || nullRect;
} }
if (rect.left || rect.right || start == 0) break;
end = start;
start = start - 1;
collapse = "right";
}
} else { // If it is a widget, simply get the box for the whole widget. } else { // If it is a widget, simply get the box for the whole widget.
if (start > 0) collapse = bias = "right"; if (start > 0) collapse = bias = "right";
var rects; var rects;
@ -2029,16 +2039,17 @@
var cm = op.cm, display = cm.display; var cm = op.cm, display = cm.display;
if (op.updatedDisplay) updateHeightsInViewport(cm); if (op.updatedDisplay) updateHeightsInViewport(cm);
op.barMeasure = measureForScrollbars(cm);
// If the max line changed since it was last measured, measure it, // If the max line changed since it was last measured, measure it,
// and ensure the document's width matches it. // and ensure the document's width matches it.
// updateDisplayIfNeeded will use these properties to do the actual resizing // updateDisplay_W2 will use these properties to do the actual resizing
if (display.maxLineChanged && !cm.options.lineWrapping) { if (display.maxLineChanged && !cm.options.lineWrapping) {
op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left; op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo + op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo +
scrollerCutOff - display.scroller.clientWidth); scrollerCutOff - display.scroller.clientWidth);
} }
op.barMeasure = measureForScrollbars(cm);
if (op.updatedDisplay || op.selectionChanged) if (op.updatedDisplay || op.selectionChanged)
op.newSelectionNodes = drawSelection(cm); op.newSelectionNodes = drawSelection(cm);
} }
@ -2050,10 +2061,11 @@
cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
if (op.maxScrollLeft < cm.doc.scrollLeft) if (op.maxScrollLeft < cm.doc.scrollLeft)
setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true);
cm.display.maxLineChanged = false;
} }
if (op.newSelectionNodes) if (op.newSelectionNodes)
updateSelection(cm, op.newSelectionNodes); showSelection(cm, op.newSelectionNodes);
if (op.updatedDisplay) if (op.updatedDisplay)
setDocumentHeight(cm, op.barMeasure); setDocumentHeight(cm, op.barMeasure);
if (op.updatedDisplay || op.startHeight != cm.doc.height) if (op.updatedDisplay || op.startHeight != cm.doc.height)
@ -2068,6 +2080,9 @@
function endOperation_finish(op) { function endOperation_finish(op) {
var cm = op.cm, display = cm.display, doc = cm.doc; var cm = op.cm, display = cm.display, doc = cm.doc;
if (op.adjustWidthTo != null && Math.abs(op.barMeasure.scrollWidth - cm.display.scroller.scrollWidth) > 1)
updateScrollbars(cm);
if (op.updatedDisplay) postUpdateDisplay(cm, op.update); if (op.updatedDisplay) postUpdateDisplay(cm, op.update);
// Abort mouse wheel delta measurement, when scrolling explicitly // Abort mouse wheel delta measurement, when scrolling explicitly
@ -3553,7 +3568,7 @@
var after = i ? computeSelAfterChange(doc, change) : lst(source); var after = i ? computeSelAfterChange(doc, change) : lst(source);
makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
if (!i && doc.cm) doc.cm.scrollIntoView(change); if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)});
var rebased = []; var rebased = [];
// Propagate to the linked documents // Propagate to the linked documents
@ -3742,6 +3757,7 @@
if (y1 < 0) y1 = 0; if (y1 < 0) y1 = 0;
var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop; var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
var screen = display.scroller.clientHeight - scrollerCutOff, result = {}; var screen = display.scroller.clientHeight - scrollerCutOff, result = {};
if (y2 - y1 > screen) y2 = y1 + screen;
var docBottom = cm.doc.height + paddingVert(display); var docBottom = cm.doc.height + paddingVert(display);
var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin; var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
if (y1 < screentop) { if (y1 < screentop) {
@ -3752,16 +3768,16 @@
} }
var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft; var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
var screenw = display.scroller.clientWidth - scrollerCutOff; var screenw = display.scroller.clientWidth - scrollerCutOff - display.gutters.offsetWidth;
x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth; var tooWide = x2 - x1 > screenw;
var gutterw = display.gutters.offsetWidth; if (tooWide) x2 = y1 + screen;
var atLeft = x1 < gutterw + 10; if (x1 < 10)
if (x1 < screenleft + gutterw || atLeft) { result.scrollLeft = 0;
if (atLeft) x1 = 0; else if (x1 < screenleft)
result.scrollLeft = Math.max(0, x1 - 10 - gutterw); result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10));
} else if (x2 > screenw + screenleft - 3) { else if (x2 > screenw + screenleft - 3)
result.scrollLeft = x2 + 10 - screenw; result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw;
}
return result; return result;
} }
@ -4070,11 +4086,14 @@
for (var i = 0; i < ranges.length; i++) { for (var i = 0; i < ranges.length; i++) {
var range = ranges[i]; var range = ranges[i];
if (!range.empty()) { if (!range.empty()) {
var start = Math.max(end, range.from().line); var from = range.from(), to = range.to();
var to = range.to(); var start = Math.max(end, from.line);
end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
for (var j = start; j < end; ++j) for (var j = start; j < end; ++j)
indentLine(this, j, how); indentLine(this, j, how);
var newRanges = this.doc.sel.ranges;
if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll);
} else if (range.head.line > end) { } else if (range.head.line > end) {
indentLine(this, range.head.line, how, true); indentLine(this, range.head.line, how, true);
end = range.head.line; end = range.head.line;
@ -4470,7 +4489,7 @@
clearCaches(cm); clearCaches(cm);
regChange(cm); regChange(cm);
}, true); }, true);
option("specialChars", /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\ufeff]/g, function(cm, val) { option("specialChars", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val) {
cm.options.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); cm.options.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
cm.refresh(); cm.refresh();
}, true); }, true);
@ -4731,15 +4750,7 @@
}, },
goLineStartSmart: function(cm) { goLineStartSmart: function(cm) {
cm.extendSelectionsBy(function(range) { cm.extendSelectionsBy(function(range) {
var start = lineStart(cm, range.head.line); return lineStartSmart(cm, range.head);
var line = cm.getLineHandle(start.line);
var order = getOrder(line);
if (!order || order[0].level == 0) {
var firstNonWS = Math.max(0, line.text.search(/\S/));
var inWS = range.head.line == start.line && range.head.ch <= firstNonWS && range.head.ch;
return Pos(start.line, inWS ? 0 : firstNonWS);
}
return start;
}, {origin: "+move", bias: 1}); }, {origin: "+move", bias: 1});
}, },
goLineEnd: function(cm) { goLineEnd: function(cm) {
@ -4758,6 +4769,14 @@
return cm.coordsChar({left: 0, top: top}, "div"); return cm.coordsChar({left: 0, top: top}, "div");
}, sel_move); }, sel_move);
}, },
goLineLeftSmart: function(cm) {
cm.extendSelectionsBy(function(range) {
var top = cm.charCoords(range.head, "div").top + 5;
var pos = cm.coordsChar({left: 0, top: top}, "div");
if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head);
return pos;
}, sel_move);
},
goLineUp: function(cm) {cm.moveV(-1, "line");}, goLineUp: function(cm) {cm.moveV(-1, "line");},
goLineDown: function(cm) {cm.moveV(1, "line");}, goLineDown: function(cm) {cm.moveV(1, "line");},
goPageUp: function(cm) {cm.moveV(-1, "page");}, goPageUp: function(cm) {cm.moveV(-1, "page");},
@ -6495,7 +6514,7 @@
}, },
changeGeneration: function(forceSplit) { changeGeneration: function(forceSplit) {
if (forceSplit) if (forceSplit)
this.history.lastOp = this.history.lastOrigin = null; this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null;
return this.history.generation; return this.history.generation;
}, },
isClean: function (gen) { isClean: function (gen) {
@ -6812,7 +6831,7 @@
// Used to track when changes can be merged into a single undo // Used to track when changes can be merged into a single undo
// event // event
this.lastModTime = this.lastSelTime = 0; this.lastModTime = this.lastSelTime = 0;
this.lastOp = null; this.lastOp = this.lastSelOp = null;
this.lastOrigin = this.lastSelOrigin = null; this.lastOrigin = this.lastSelOrigin = null;
// Used by the isClean() method // Used by the isClean() method
this.generation = this.maxGeneration = startGen || 1; this.generation = this.maxGeneration = startGen || 1;
@ -6890,7 +6909,7 @@
hist.done.push(selAfter); hist.done.push(selAfter);
hist.generation = ++hist.maxGeneration; hist.generation = ++hist.maxGeneration;
hist.lastModTime = hist.lastSelTime = time; hist.lastModTime = hist.lastSelTime = time;
hist.lastOp = opId; hist.lastOp = hist.lastSelOp = opId;
hist.lastOrigin = hist.lastSelOrigin = change.origin; hist.lastOrigin = hist.lastSelOrigin = change.origin;
if (!last) signal(doc, "historyAdded"); if (!last) signal(doc, "historyAdded");
@ -6916,7 +6935,7 @@
// the current, or the origins don't allow matching. Origins // the current, or the origins don't allow matching. Origins
// starting with * are always merged, those starting with + are // starting with * are always merged, those starting with + are
// merged when similar and close together in time. // merged when similar and close together in time.
if (opId == hist.lastOp || if (opId == hist.lastSelOp ||
(origin && hist.lastSelOrigin == origin && (origin && hist.lastSelOrigin == origin &&
(hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
@ -6926,7 +6945,7 @@
hist.lastSelTime = +new Date; hist.lastSelTime = +new Date;
hist.lastSelOrigin = origin; hist.lastSelOrigin = origin;
hist.lastOp = opId; hist.lastSelOp = opId;
if (options && options.clearRedo !== false) if (options && options.clearRedo !== false)
clearSelectionEvents(hist.undone); clearSelectionEvents(hist.undone);
} }
@ -7556,6 +7575,17 @@
var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line); var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
return Pos(lineN == null ? lineNo(line) : lineN, ch); return Pos(lineN == null ? lineNo(line) : lineN, ch);
} }
function lineStartSmart(cm, pos) {
var start = lineStart(cm, pos.line);
var line = getLine(cm.doc, start.line);
var order = getOrder(line);
if (!order || order[0].level == 0) {
var firstNonWS = Math.max(0, line.text.search(/\S/));
var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
return Pos(start.line, inWS ? 0 : firstNonWS);
}
return start;
}
function compareBidiLevel(order, a, b) { function compareBidiLevel(order, a, b) {
var linedir = order[0].level; var linedir = order[0].level;
@ -7795,7 +7825,7 @@
// THE END // THE END
CodeMirror.version = "4.4.0"; CodeMirror.version = "4.5.0";
return CodeMirror; return CodeMirror;
}); });

View File

@ -17,7 +17,8 @@
var map = CodeMirror.keyMap.sublime = {fallthrough: "default"}; var map = CodeMirror.keyMap.sublime = {fallthrough: "default"};
var cmds = CodeMirror.commands; var cmds = CodeMirror.commands;
var Pos = CodeMirror.Pos; var Pos = CodeMirror.Pos;
var ctrl = CodeMirror.keyMap["default"] == CodeMirror.keyMap.pcDefault ? "Ctrl-" : "Cmd-"; var mac = CodeMirror.keyMap["default"] == CodeMirror.keyMap.macDefault;
var ctrl = mac ? "Cmd-" : "Ctrl-";
// This is not exactly Sublime's algorithm. I couldn't make heads or tails of that. // This is not exactly Sublime's algorithm. I couldn't make heads or tails of that.
function findPosSubword(doc, start, dir) { function findPosSubword(doc, start, dir) {
@ -186,7 +187,9 @@
}); });
}; };
cmds[map["Shift-" + ctrl + "Up"] = "swapLineUp"] = function(cm) { var swapLineCombo = mac ? "Cmd-Ctrl-" : "Shift-Ctrl-";
cmds[map[swapLineCombo + "Up"] = "swapLineUp"] = function(cm) {
var ranges = cm.listSelections(), linesToMove = [], at = cm.firstLine() - 1, newSels = []; var ranges = cm.listSelections(), linesToMove = [], at = cm.firstLine() - 1, newSels = [];
for (var i = 0; i < ranges.length; i++) { for (var i = 0; i < ranges.length; i++) {
var range = ranges[i], from = range.from().line - 1, to = range.to().line; var range = ranges[i], from = range.from().line - 1, to = range.to().line;
@ -212,7 +215,7 @@
}); });
}; };
cmds[map["Shift-" + ctrl + "Down"] = "swapLineDown"] = function(cm) { cmds[map[swapLineCombo + "Down"] = "swapLineDown"] = function(cm) {
var ranges = cm.listSelections(), linesToMove = [], at = cm.lastLine() + 1; var ranges = cm.listSelections(), linesToMove = [], at = cm.lastLine() + 1;
for (var i = ranges.length - 1; i >= 0; i--) { for (var i = ranges.length - 1; i >= 0; i--) {
var range = ranges[i], from = range.to().line + 1, to = range.from().line; var range = ranges[i], from = range.to().line + 1, to = range.from().line;

View File

@ -616,7 +616,9 @@
visualLine: false, visualLine: false,
visualBlock: false, visualBlock: false,
lastSelection: null, lastSelection: null,
lastPastedText: null lastPastedText: null,
// Used by two-character ESC keymap routines. Should not be changed from false here.
awaitingEscapeSecondCharacter: false
}; };
} }
return cm.state.vim; return cm.state.vim;
@ -785,17 +787,19 @@
* pasted, should it insert itself into a new line, or should the text be * pasted, should it insert itself into a new line, or should the text be
* inserted at the cursor position.) * inserted at the cursor position.)
*/ */
function Register(text, linewise) { function Register(text, linewise, blockwise) {
this.clear(); this.clear();
this.keyBuffer = [text || '']; this.keyBuffer = [text || ''];
this.insertModeChanges = []; this.insertModeChanges = [];
this.searchQueries = []; this.searchQueries = [];
this.linewise = !!linewise; this.linewise = !!linewise;
this.blockwise = !!blockwise;
} }
Register.prototype = { Register.prototype = {
setText: function(text, linewise) { setText: function(text, linewise, blockwise) {
this.keyBuffer = [text || '']; this.keyBuffer = [text || ''];
this.linewise = !!linewise; this.linewise = !!linewise;
this.blockwise = !!blockwise;
}, },
pushText: function(text, linewise) { pushText: function(text, linewise) {
// if this register has ever been set to linewise, use linewise. // if this register has ever been set to linewise, use linewise.
@ -840,7 +844,7 @@
registers['/'] = new Register(); registers['/'] = new Register();
} }
RegisterController.prototype = { RegisterController.prototype = {
pushText: function(registerName, operator, text, linewise) { pushText: function(registerName, operator, text, linewise, blockwise) {
if (linewise && text.charAt(0) == '\n') { if (linewise && text.charAt(0) == '\n') {
text = text.slice(1) + '\n'; text = text.slice(1) + '\n';
} }
@ -857,7 +861,7 @@
switch (operator) { switch (operator) {
case 'yank': case 'yank':
// The 0 register contains the text from the most recent yank. // The 0 register contains the text from the most recent yank.
this.registers['0'] = new Register(text, linewise); this.registers['0'] = new Register(text, linewise, blockwise);
break; break;
case 'delete': case 'delete':
case 'change': case 'change':
@ -873,7 +877,7 @@
break; break;
} }
// Make sure the unnamed register is set to what just happened // Make sure the unnamed register is set to what just happened
this.unnamedRegister.setText(text, linewise); this.unnamedRegister.setText(text, linewise, blockwise);
return; return;
} }
@ -882,7 +886,7 @@
if (append) { if (append) {
register.pushText(text, linewise); register.pushText(text, linewise);
} else { } else {
register.setText(text, linewise); register.setText(text, linewise, blockwise);
} }
// The unnamed register always has the same value as the last used // The unnamed register always has the same value as the last used
// register. // register.
@ -1860,6 +1864,12 @@
var curStart = cursorIsBefore(start.anchor, start.head) ? start.anchor : start.head; var curStart = cursorIsBefore(start.anchor, start.head) ? start.anchor : start.head;
var curEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor; var curEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor;
var text = cm.getSelection(); var text = cm.getSelection();
var visualBlock = vim.visualBlock;
if (vim.lastSelection && !vim.visualMode) {
visualBlock = vim.lastSelection.visualBlock ? true : visualBlock;
}
var lastInsertModeChanges = vimGlobalState.macroModeState.lastInsertModeChanges;
lastInsertModeChanges.inVisualBlock = visualBlock;
var replacement = new Array(selections.length).join('1').split('1'); var replacement = new Array(selections.length).join('1').split('1');
// save the selectionEnd mark // save the selectionEnd mark
var selectionEnd = vim.marks['>'] ? vim.marks['>'].find() : cm.getCursor('head'); var selectionEnd = vim.marks['>'] ? vim.marks['>'].find() : cm.getCursor('head');
@ -1868,7 +1878,7 @@
operatorArgs.linewise); operatorArgs.linewise);
if (operatorArgs.linewise) { if (operatorArgs.linewise) {
// 'C' in visual block extends the block till eol for all lines // 'C' in visual block extends the block till eol for all lines
if (vim.visualBlock){ if (visualBlock){
var startLine = curStart.line; var startLine = curStart.line;
while (startLine <= curEnd.line) { while (startLine <= curEnd.line) {
var endCh = lineLength(cm, startLine); var endCh = lineLength(cm, startLine);
@ -1898,7 +1908,7 @@
curEnd = offsetCursor(curEnd, 0, - match[0].length); curEnd = offsetCursor(curEnd, 0, - match[0].length);
} }
} }
if (vim.visualBlock) { if (visualBlock) {
cm.replaceSelections(replacement); cm.replaceSelections(replacement);
} else { } else {
cm.setCursor(curStart); cm.setCursor(curStart);
@ -1916,17 +1926,19 @@
var curEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor; var curEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor;
// Save the '>' mark before cm.replaceRange clears it. // Save the '>' mark before cm.replaceRange clears it.
var selectionEnd, selectionStart; var selectionEnd, selectionStart;
var blockwise = vim.visualBlock;
if (vim.visualMode) { if (vim.visualMode) {
selectionEnd = vim.marks['>'].find(); selectionEnd = vim.marks['>'].find();
selectionStart = vim.marks['<'].find(); selectionStart = vim.marks['<'].find();
} else if (vim.lastSelection) { } else if (vim.lastSelection) {
selectionEnd = vim.lastSelection.curStartMark.find(); selectionEnd = vim.lastSelection.curStartMark.find();
selectionStart = vim.lastSelection.curEndMark.find(); selectionStart = vim.lastSelection.curEndMark.find();
blockwise = vim.lastSelection.visualBlock;
} }
var text = cm.getSelection(); var text = cm.getSelection();
vimGlobalState.registerController.pushText( vimGlobalState.registerController.pushText(
operatorArgs.registerName, 'delete', text, operatorArgs.registerName, 'delete', text,
operatorArgs.linewise); operatorArgs.linewise, blockwise);
var replacement = new Array(selections.length).join('1').split('1'); var replacement = new Array(selections.length).join('1').split('1');
// If the ending line is past the last line, inclusive, instead of // If the ending line is past the last line, inclusive, instead of
// including the trailing \n, include the \n before the starting line // including the trailing \n, include the \n before the starting line
@ -1999,11 +2011,11 @@
cm.setCursor(cursorIsBefore(curStart, curEnd) ? curStart : curEnd); cm.setCursor(cursorIsBefore(curStart, curEnd) ? curStart : curEnd);
} }
}, },
yank: function(cm, operatorArgs, _vim, _curStart, _curEnd, curOriginal) { yank: function(cm, operatorArgs, vim, _curStart, _curEnd, curOriginal) {
var text = cm.getSelection(); var text = cm.getSelection();
vimGlobalState.registerController.pushText( vimGlobalState.registerController.pushText(
operatorArgs.registerName, 'yank', operatorArgs.registerName, 'yank',
text, operatorArgs.linewise); text, operatorArgs.linewise, vim.visualBlock);
cm.setCursor(curOriginal); cm.setCursor(curOriginal);
} }
}; };
@ -2096,6 +2108,11 @@
vim.insertMode = true; vim.insertMode = true;
vim.insertModeRepeat = actionArgs && actionArgs.repeat || 1; vim.insertModeRepeat = actionArgs && actionArgs.repeat || 1;
var insertAt = (actionArgs) ? actionArgs.insertAt : null; var insertAt = (actionArgs) ? actionArgs.insertAt : null;
if (vim.visualMode) {
var selections = getSelectedAreaRange(cm, vim);
var selectionStart = selections[0];
var selectionEnd = selections[1];
}
if (insertAt == 'eol') { if (insertAt == 'eol') {
var cursor = cm.getCursor(); var cursor = cm.getCursor();
cursor = Pos(cursor.line, lineLength(cm, cursor.line)); cursor = Pos(cursor.line, lineLength(cm, cursor.line));
@ -2103,15 +2120,34 @@
} else if (insertAt == 'charAfter') { } else if (insertAt == 'charAfter') {
cm.setCursor(offsetCursor(cm.getCursor(), 0, 1)); cm.setCursor(offsetCursor(cm.getCursor(), 0, 1));
} else if (insertAt == 'firstNonBlank') { } else if (insertAt == 'firstNonBlank') {
cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm)); if (vim.visualMode && !vim.visualBlock) {
} else if (insertAt == 'endOfSelectedArea') {
var selectionEnd = cm.getCursor('head');
var selectionStart = cm.getCursor('anchor');
if (selectionEnd.line < selectionStart.line) { if (selectionEnd.line < selectionStart.line) {
selectionEnd = Pos(selectionStart.line, 0);
}
cm.setCursor(selectionEnd); cm.setCursor(selectionEnd);
exitVisualMode(cm); } else {
selectionStart = Pos(selectionStart.line, 0);
cm.setCursor(selectionStart);
}
cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm));
} else if (vim.visualBlock) {
selectionEnd = Pos(selectionEnd.line, selectionStart.ch);
cm.setCursor(selectionStart);
selectBlock(cm, selectionEnd);
} else {
cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm));
}
} else if (insertAt == 'endOfSelectedArea') {
if (vim.visualBlock) {
selectionStart = Pos(selectionStart.line, selectionEnd.ch);
cm.setCursor(selectionStart);
selectBlock(cm, selectionEnd);
} else if (selectionEnd.line < selectionStart.line) {
selectionEnd = Pos(selectionStart.line, 0);
cm.setCursor(selectionEnd);
}
} else if (insertAt == 'inplace') {
if (vim.visualMode){
return;
}
} }
cm.setOption('keyMap', 'vim-insert'); cm.setOption('keyMap', 'vim-insert');
cm.setOption('disableInput', false); cm.setOption('disableInput', false);
@ -2129,6 +2165,9 @@
cm.on('change', onChange); cm.on('change', onChange);
CodeMirror.on(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown); CodeMirror.on(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown);
} }
if (vim.visualMode) {
exitVisualMode(cm);
}
}, },
toggleVisualMode: function(cm, actionArgs, vim) { toggleVisualMode: function(cm, actionArgs, vim) {
var repeat = actionArgs.repeat; var repeat = actionArgs.repeat;
@ -2339,6 +2378,7 @@
var text = Array(actionArgs.repeat + 1).join(text); var text = Array(actionArgs.repeat + 1).join(text);
} }
var linewise = register.linewise; var linewise = register.linewise;
var blockwise = register.blockwise;
if (linewise) { if (linewise) {
if(vim.visualMode) { if(vim.visualMode) {
text = vim.visualLine ? text.slice(0, -1) : '\n' + text.slice(0, text.length - 1) + '\n'; text = vim.visualLine ? text.slice(0, -1) : '\n' + text.slice(0, text.length - 1) + '\n';
@ -2351,6 +2391,12 @@
cur.ch = 0; cur.ch = 0;
} }
} else { } else {
if (blockwise) {
text = text.split('\n');
for (var i = 0; i < text.length; i++) {
text[i] = (text[i] == '') ? ' ' : text[i];
}
}
cur.ch += actionArgs.after ? 1 : 0; cur.ch += actionArgs.after ? 1 : 0;
} }
var curPosFinal; var curPosFinal;
@ -2362,15 +2408,57 @@
var selectedArea = getSelectedAreaRange(cm, vim); var selectedArea = getSelectedAreaRange(cm, vim);
var selectionStart = selectedArea[0]; var selectionStart = selectedArea[0];
var selectionEnd = selectedArea[1]; var selectionEnd = selectedArea[1];
var selectedText = cm.getSelection();
var selections = cm.listSelections();
var emptyStrings = new Array(selections.length).join('1').split('1');
// save the curEnd marker before it get cleared due to cm.replaceRange. // save the curEnd marker before it get cleared due to cm.replaceRange.
if (vim.lastSelection) lastSelectionCurEnd = vim.lastSelection.curEndMark.find(); if (vim.lastSelection) {
lastSelectionCurEnd = vim.lastSelection.curEndMark.find();
}
// push the previously selected text to unnamed register // push the previously selected text to unnamed register
vimGlobalState.registerController.unnamedRegister.setText(cm.getRange(selectionStart, selectionEnd)); vimGlobalState.registerController.unnamedRegister.setText(selectedText);
if (blockwise) {
// first delete the selected text
cm.replaceSelections(emptyStrings);
// Set new selections as per the block length of the yanked text
selectionEnd = Pos(selectionStart.line + text.length-1, selectionStart.ch);
cm.setCursor(selectionStart);
selectBlock(cm, selectionEnd);
cm.replaceSelections(text);
curPosFinal = selectionStart;
} else if (vim.visualBlock) {
cm.replaceSelections(emptyStrings);
cm.setCursor(selectionStart);
cm.replaceRange(text, selectionStart, selectionStart);
curPosFinal = selectionStart;
} else {
cm.replaceRange(text, selectionStart, selectionEnd); cm.replaceRange(text, selectionStart, selectionEnd);
// restore the the curEnd marker
if(lastSelectionCurEnd) vim.lastSelection.curEndMark = cm.setBookmark(lastSelectionCurEnd);
curPosFinal = cm.posFromIndex(cm.indexFromPos(selectionStart) + text.length - 1); curPosFinal = cm.posFromIndex(cm.indexFromPos(selectionStart) + text.length - 1);
if(linewise)curPosFinal.ch=0; }
// restore the the curEnd marker
if(lastSelectionCurEnd) {
vim.lastSelection.curEndMark = cm.setBookmark(lastSelectionCurEnd);
}
if (linewise) {
curPosFinal.ch=0;
}
} else {
if (blockwise) {
cm.setCursor(cur);
for (var i = 0; i < text.length; i++) {
var line = cur.line+i;
if (line > cm.lastLine()) {
cm.replaceRange('\n', Pos(line, 0));
}
var lastCh = lineLength(cm, line);
if (lastCh < cur.ch) {
extendLineToColumn(cm, line, cur.ch);
}
}
cm.setCursor(cur);
selectBlock(cm, Pos(cur.line + text.length-1, cur.ch));
cm.replaceSelections(text);
curPosFinal = cur;
} else { } else {
cm.replaceRange(text, cur); cm.replaceRange(text, cur);
// Now fine tune the cursor to where we want it. // Now fine tune the cursor to where we want it.
@ -2390,6 +2478,7 @@
curPosFinal = cm.posFromIndex(idx + text.length); curPosFinal = cm.posFromIndex(idx + text.length);
} }
} }
}
cm.setCursor(curPosFinal); cm.setCursor(curPosFinal);
if (vim.visualMode) { if (vim.visualMode) {
exitVisualMode(cm); exitVisualMode(cm);
@ -2597,6 +2686,12 @@
function escapeRegex(s) { function escapeRegex(s) {
return s.replace(/([.?*+$\[\]\/\\(){}|\-])/g, '\\$1'); return s.replace(/([.?*+$\[\]\/\\(){}|\-])/g, '\\$1');
} }
function extendLineToColumn(cm, lineNum, column) {
var endCh = lineLength(cm, lineNum);
var spaces = new Array(column-endCh+1).join(' ');
cm.setCursor(Pos(lineNum, endCh));
cm.replaceRange(spaces, cm.getCursor());
}
// This functions selects a rectangular block // This functions selects a rectangular block
// of text with selectionEnd as any of its corner // of text with selectionEnd as any of its corner
// Height of block: // Height of block:
@ -2606,55 +2701,86 @@
function selectBlock(cm, selectionEnd) { function selectBlock(cm, selectionEnd) {
var selections = [], ranges = cm.listSelections(); var selections = [], ranges = cm.listSelections();
var firstRange = ranges[0].anchor, lastRange = ranges[ranges.length-1].anchor; var firstRange = ranges[0].anchor, lastRange = ranges[ranges.length-1].anchor;
var start, end, selectionStart; var start, end, direction, selectionStart;
var curEnd = cm.getCursor('head'); var curEnd = cm.getCursor('head');
var originalSelectionEnd = copyCursor(selectionEnd);
start = firstRange.line;
end = lastRange.line;
if (selectionEnd.line < curEnd.line) {
direction = 'up';
} else if (selectionEnd.line > curEnd.line) {
direction = 'down';
} else {
if (selectionEnd.ch != curEnd.ch) {
direction = selectionEnd.ch > curEnd.ch ? 'right' : 'left';
}
selectionStart = cm.getCursor('anchor');
}
var primIndex = getIndex(ranges, curEnd); var primIndex = getIndex(ranges, curEnd);
// sets to true when selectionEnd already lies inside the existing selections // sets to true when selectionEnd already lies inside the existing selections
var contains = getIndex(ranges, selectionEnd) < 0 ? false : true;
selectionEnd = cm.clipPos(selectionEnd); selectionEnd = cm.clipPos(selectionEnd);
// difference in distance of selectionEnd from each end of the block. var contains = getIndex(ranges, selectionEnd) < 0 ? false : true;
var near = Math.abs(firstRange.line - selectionEnd.line) - Math.abs(lastRange.line - selectionEnd.line); var isClipped = !cursorEqual(originalSelectionEnd, selectionEnd);
if (near > 0) { // This function helps to check selection crossing
end = selectionEnd.line; // in case of short lines.
start = firstRange.line; var processSelectionCrossing = function() {
if (lastRange.line == selectionEnd.line && contains) { if (isClipped) {
start = end; if (curEnd.ch >= selectionStart.ch) {
selectionStart.ch++;
} }
} else if (near < 0) { } else if (curEnd.ch == lineLength(cm, curEnd.line)) {
start = selectionEnd.line; if (cursorEqual(ranges[primIndex].anchor, ranges[primIndex].head) && ranges.length>1) {
end = lastRange.line; if (direction == 'up') {
if (firstRange.line == selectionEnd.line && contains) { if (contains || primIndex>0) {
end = start;
}
} else {
// Case where selectionEnd line is halfway between the 2 ends.
// We remove the primary selection in this case
if (primIndex == 0) {
start = selectionEnd.line;
end = lastRange.line;
} else {
start = firstRange.line; start = firstRange.line;
end = selectionEnd.line; end = selectionEnd.line;
selectionStart = ranges[primIndex-1].anchor;
}
} else {
if (contains || primIndex == 0) {
end = lastRange.line;
start = selectionEnd.line;
selectionStart = ranges[primIndex+1].anchor;
} }
} }
if (start > end) { if (selectionEnd.ch >= selectionStart.ch) {
var tmp = start; selectionStart.ch--;
start = end; }
end = tmp; }
}
};
switch(direction) {
case 'up':
start = contains ? firstRange.line : selectionEnd.line;
end = contains ? selectionEnd.line : lastRange.line;
selectionStart = lastRange;
processSelectionCrossing();
break;
case 'down':
start = contains ? selectionEnd.line : firstRange.line;
end = contains ? lastRange.line : selectionEnd.line;
selectionStart = firstRange;
processSelectionCrossing();
break;
case 'left':
if ((selectionEnd.ch <= selectionStart.ch) && (curEnd.ch > selectionStart.ch)) {
selectionStart.ch++;
selectionEnd.ch--;
}
break;
case 'right':
if ((selectionStart.ch <= selectionEnd.ch) && (curEnd.ch < selectionStart.ch)) {
selectionStart.ch--;
selectionEnd.ch++;
}
break;
default:
start = selectionStart.line;
end = selectionEnd.line;
} }
selectionStart = (near > 0) ? firstRange : lastRange;
while (start <= end) { while (start <= end) {
var anchor = {line: start, ch: selectionStart.ch}; var anchor = {line: start, ch: selectionStart.ch};
var head = {line: start, ch: selectionEnd.ch}; var head = {line: start, ch: selectionEnd.ch};
// Shift the anchor right or left
// as each selection crosses itself.
if ((anchor.ch < curEnd.ch) && ((head.ch == anchor.ch) || (anchor.ch - head.ch == 1))) {
anchor.ch++;
head.ch--;
} else if ((anchor.ch > curEnd.ch) && ((head.ch == anchor.ch) || (anchor.ch - head.ch == -1))) {
anchor.ch--;
head.ch++;
}
var range = {anchor: anchor, head: head}; var range = {anchor: anchor, head: head};
selections.push(range); selections.push(range);
if (cursorEqual(head, selectionEnd)) { if (cursorEqual(head, selectionEnd)) {
@ -2666,16 +2792,29 @@
// after selection crossing // after selection crossing
selectionEnd.ch = selections[0].head.ch; selectionEnd.ch = selections[0].head.ch;
selectionStart.ch = selections[0].anchor.ch; selectionStart.ch = selections[0].anchor.ch;
if (cursorEqual(selectionEnd, selections[0].head)) {
selectionStart.line = selections[selections.length-1].anchor.line;
} else {
selectionStart.line = selections[0].anchor.line;
}
cm.setSelections(selections, primIndex); cm.setSelections(selections, primIndex);
return selectionStart; return selectionStart;
} }
function getIndex(ranges, head) { // getIndex returns the index of the cursor in the selections.
function getIndex(ranges, cursor, end) {
var pos = -1;
for (var i = 0; i < ranges.length; i++) { for (var i = 0; i < ranges.length; i++) {
if (cursorEqual(ranges[i].head, head)) { var atAnchor = cursorEqual(ranges[i].anchor, cursor);
return i; var atHead = cursorEqual(ranges[i].head, cursor);
if (end == 'head') {
pos = atHead ? i : pos;
} else if (end == 'anchor') {
pos = atAnchor ? i : pos;
} else {
pos = (atAnchor || atHead) ? i : pos;
} }
} }
return -1; return pos;
} }
function getSelectedAreaRange(cm, vim) { function getSelectedAreaRange(cm, vim) {
var lastSelection = vim.lastSelection; var lastSelection = vim.lastSelection;
@ -2739,7 +2878,7 @@
var ranges = cm.listSelections(); var ranges = cm.listSelections();
// This check ensures to set the cursor // This check ensures to set the cursor
// position where we left off in previous selection // position where we left off in previous selection
var swap = getIndex(ranges, selectionStart) > -1; var swap = getIndex(ranges, selectionStart, 'head') > -1;
if (vim.visualBlock) { if (vim.visualBlock) {
var height = Math.abs(selectionStart.line - selectionEnd.line)+1; var height = Math.abs(selectionStart.line - selectionEnd.line)+1;
var width = Math.abs(selectionStart.ch - selectionEnd.ch); var width = Math.abs(selectionStart.ch - selectionEnd.ch);
@ -2759,14 +2898,16 @@
var vim = cm.state.vim; var vim = cm.state.vim;
var selectionStart = cm.getCursor('anchor'); var selectionStart = cm.getCursor('anchor');
var selectionEnd = cm.getCursor('head'); var selectionEnd = cm.getCursor('head');
// hack to place the cursor at the right place
// in case of visual block
if (vim.visualBlock && (cursorIsBefore(selectionStart, selectionEnd))) {
selectionEnd.ch--;
}
updateLastSelection(cm, vim); updateLastSelection(cm, vim);
vim.visualMode = false; vim.visualMode = false;
vim.visualLine = false; vim.visualLine = false;
vim.visualBlock = false; vim.visualBlock = false;
if (!cursorEqual(selectionStart, selectionEnd)) { if (!cursorEqual(selectionStart, selectionEnd)) {
// Clear the selection and set the cursor only if the selection has not
// already been cleared. Otherwise we risk moving the cursor somewhere
// it's not supposed to be.
cm.setCursor(clipCursorToContent(cm, selectionEnd)); cm.setCursor(clipCursorToContent(cm, selectionEnd));
} }
CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"}); CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
@ -4498,7 +4639,32 @@
var macroModeState = vimGlobalState.macroModeState; var macroModeState = vimGlobalState.macroModeState;
var insertModeChangeRegister = vimGlobalState.registerController.getRegister('.'); var insertModeChangeRegister = vimGlobalState.registerController.getRegister('.');
var isPlaying = macroModeState.isPlaying; var isPlaying = macroModeState.isPlaying;
var lastChange = macroModeState.lastInsertModeChanges;
// In case of visual block, the insertModeChanges are not saved as a
// single word, so we convert them to a single word
// so as to update the ". register as expected in real vim.
var text = [];
if (!isPlaying) { if (!isPlaying) {
var selLength = lastChange.inVisualBlock ? vim.lastSelection.visualBlock.height : 1;
var changes = lastChange.changes;
var text = [];
var i = 0;
// In case of multiple selections in blockwise visual,
// the inserted text, for example: 'f<Backspace>oo', is stored as
// 'f', 'f', InsertModeKey 'o', 'o', 'o', 'o'. (if you have a block with 2 lines).
// We push the contents of the changes array as per the following:
// 1. In case of InsertModeKey, just increment by 1.
// 2. In case of a character, jump by selLength (2 in the example).
while (i < changes.length) {
// This loop will convert 'ff<bs>oooo' to 'f<bs>oo'.
text.push(changes[i]);
if (changes[i] instanceof InsertModeKey) {
i++;
} else {
i+= selLength;
}
}
lastChange.changes = text;
cm.off('change', onChange); cm.off('change', onChange);
CodeMirror.off(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown); CodeMirror.off(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown);
} }
@ -4515,13 +4681,64 @@
cm.setOption('disableInput', true); cm.setOption('disableInput', true);
cm.toggleOverwrite(false); // exit replace mode if we were in it. cm.toggleOverwrite(false); // exit replace mode if we were in it.
// update the ". register before exiting insert mode // update the ". register before exiting insert mode
insertModeChangeRegister.setText(macroModeState.lastInsertModeChanges.changes.join('')); insertModeChangeRegister.setText(lastChange.changes.join(''));
CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"}); CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
if (macroModeState.isRecording) { if (macroModeState.isRecording) {
logInsertModeChange(macroModeState); logInsertModeChange(macroModeState);
} }
} }
defineOption('enableInsertModeEscKeys', false, 'boolean');
// Use this option to customize the two-character ESC keymap.
// If you want to use characters other than i j or k you'll have to add
// lines to the vim-insert and await-second keymaps later in this file.
defineOption('insertModeEscKeys', 'kj', 'string');
// The timeout in milliseconds for the two-character ESC keymap should be
// adjusted according to your typing speed to prevent false positives.
defineOption('insertModeEscKeysTimeout', 200, 'number');
function firstEscCharacterHandler(ch) {
return function(cm){
var keys = getOption('insertModeEscKeys');
var firstEscCharacter = keys && keys.length > 1 && keys.charAt(0);
if (!getOption('enableInsertModeEscKeys')|| firstEscCharacter !== ch) {
return CodeMirror.Pass;
} else {
cm.replaceRange(ch, cm.getCursor(), cm.getCursor(), "+input");
cm.setOption('keyMap', 'await-second');
cm.state.vim.awaitingEscapeSecondCharacter = true;
setTimeout(
function(){
if(cm.state.vim.awaitingEscapeSecondCharacter) {
cm.state.vim.awaitingEscapeSecondCharacter = false;
cm.setOption('keyMap', 'vim-insert');
}
},
getOption('insertModeEscKeysTimeout'));
}
};
}
function secondEscCharacterHandler(ch){
return function(cm) {
var keys = getOption('insertModeEscKeys');
var secondEscCharacter = keys && keys.length > 1 && keys.charAt(1);
if (!getOption('enableInsertModeEscKeys')|| secondEscCharacter !== ch) {
return CodeMirror.Pass;
// This is not the handler you're looking for. Just insert as usual.
} else {
if (cm.state.vim.insertMode) {
var lastChange = vimGlobalState.macroModeState.lastInsertModeChanges;
if (lastChange && lastChange.changes.length) {
lastChange.changes.pop();
}
}
cm.state.vim.awaitingEscapeSecondCharacter = false;
cm.replaceRange('', {ch: cm.getCursor().ch - 1, line: cm.getCursor().line},
cm.getCursor(), "+input");
exitInsertMode(cm);
}
};
}
CodeMirror.keyMap['vim-insert'] = { CodeMirror.keyMap['vim-insert'] = {
// TODO: override navigation keys so that Esc will cancel automatic // TODO: override navigation keys so that Esc will cancel automatic
// indentation from o, O, i_<CR> // indentation from o, O, i_<CR>
@ -4535,9 +4752,23 @@
CodeMirror.commands.newlineAndIndent; CodeMirror.commands.newlineAndIndent;
fn(cm); fn(cm);
}, },
// The next few lines are where you'd add additional handlers if
// you wanted to use keys other than i j and k for two-character
// escape sequences. Don't forget to add them in the await-second
// section as well.
"'i'": firstEscCharacterHandler('i'),
"'j'": firstEscCharacterHandler('j'),
"'k'": firstEscCharacterHandler('k'),
fallthrough: ['default'] fallthrough: ['default']
}; };
CodeMirror.keyMap['await-second'] = {
"'i'": secondEscCharacterHandler('i'),
"'j'": secondEscCharacterHandler('j'),
"'k'": secondEscCharacterHandler('k'),
fallthrough: ['vim-insert']
};
CodeMirror.keyMap['vim-replace'] = { CodeMirror.keyMap['vim-replace'] = {
'Backspace': 'goCharLeft', 'Backspace': 'goCharLeft',
fallthrough: ['vim-insert'] fallthrough: ['vim-insert']
@ -4710,11 +4941,7 @@
// insert mode changes. Will conform to that behavior. // insert mode changes. Will conform to that behavior.
repeat = !vim.lastEditActionCommand ? 1 : repeat; repeat = !vim.lastEditActionCommand ? 1 : repeat;
var changeObject = macroModeState.lastInsertModeChanges; var changeObject = macroModeState.lastInsertModeChanges;
// This isn't strictly necessary, but since lastInsertModeChanges is
// supposed to be immutable during replay, this helps catch bugs.
macroModeState.lastInsertModeChanges = {};
repeatInsertModeChanges(cm, changeObject.changes, repeat); repeatInsertModeChanges(cm, changeObject.changes, repeat);
macroModeState.lastInsertModeChanges = changeObject;
} }
} }
vim.inputState = vim.lastEditInputState; vim.inputState = vim.lastEditInputState;
@ -4752,6 +4979,18 @@
} }
return true; return true;
} }
var curStart = cm.getCursor();
var inVisualBlock = vimGlobalState.macroModeState.lastInsertModeChanges.inVisualBlock;
if (inVisualBlock) {
// Set up block selection again for repeating the changes.
var vim = cm.state.vim;
var block = vim.lastSelection.visualBlock;
var curEnd = Pos(curStart.line + block.height-1, curStart.ch);
cm.setCursor(curStart);
selectBlock(cm, curEnd);
repeat = cm.listSelections().length;
cm.setCursor(curStart);
}
for (var i = 0; i < repeat; i++) { for (var i = 0; i < repeat; i++) {
for (var j = 0; j < changes.length; j++) { for (var j = 0; j < changes.length; j++) {
var change = changes[j]; var change = changes[j];
@ -4762,6 +5001,10 @@
cm.replaceRange(change, cur, cur); cm.replaceRange(change, cur, cur);
} }
} }
if (inVisualBlock) {
curStart.line++;
cm.setCursor(curStart);
}
} }
} }

View File

@ -437,4 +437,15 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
modeProps: {fold: ["brace", "include"]} modeProps: {fold: ["brace", "include"]}
}); });
def("text/x-nesc", {
name: "clike",
keywords: words(cKeywords + "as atomic async call command component components configuration event generic " +
"implementation includes interface module new norace nx_struct nx_union post provides " +
"signal task uses abstract extends"),
blockKeywords: words("case do else for if switch while struct"),
atoms: words("null"),
hooks: {"#": cppHook},
modeProps: {fold: ["brace", "include"]}
});
}); });

View File

@ -114,7 +114,7 @@ CodeMirror.defineMode("clojure", function (options) {
var first = stream.next(); var first = stream.next();
// Read special literals: backspace, newline, space, return. // Read special literals: backspace, newline, space, return.
// Just read all lowercase letters. // Just read all lowercase letters.
if (first.match(/[a-z]/) && stream.match(/[a-z]+/, true)) { if (first && first.match(/[a-z]/) && stream.match(/[a-z]+/, true)) {
return; return;
} }
// Read unicode character: \u1000 \uA0a1 // Read unicode character: \u1000 \uA0a1

Some files were not shown because too many files have changed in this diff Show More