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.
var lastStart = startLine.lastIndexOf(startString, from.ch);
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.
firstEnd = endLine.indexOf(endString, to.ch);
var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch);
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.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 dialog;
dialog = wrap.appendChild(document.createElement("div"));
if (bottom) {
if (bottom)
dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom";
} else {
else
dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";
}
if (typeof template == "string") {
dialog.innerHTML = template;
} else { // Assuming it's a detached DOM element.
@ -35,8 +35,11 @@
}
CodeMirror.defineExtension("openDialog", function(template, callback, options) {
if (!options) options = {};
closeNotification(this, null);
var dialog = dialogDiv(this, template, options && options.bottom);
var dialog = dialogDiv(this, template, options.bottom);
var closed = false, me = this;
function close(newVal) {
if (typeof newVal == 'string') {
@ -45,34 +48,43 @@
if (closed) return;
closed = true;
dialog.parentNode.removeChild(dialog);
me.focus();
if (options.onClose) options.onClose(dialog);
}
}
var inp = dialog.getElementsByTagName("input")[0], button;
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) {
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();
CodeMirror.e_stop(e);
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 && options.value) inp.value = options.value;
if (options.closeOnBlur !== false) CodeMirror.on(inp, "blur", close);
inp.focus();
CodeMirror.on(inp, "blur", close);
} else if (button = dialog.getElementsByTagName("button")[0]) {
CodeMirror.on(button, "click", function() {
close();
me.focus();
});
if (options.closeOnBlur !== false) CodeMirror.on(button, "blur", close);
button.focus();
CodeMirror.on(button, "blur", close);
}
return close;
});

View File

@ -109,6 +109,10 @@
replacements[i] = "/" + state.context.tagName + ">";
}
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) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,7 +17,8 @@
var map = CodeMirror.keyMap.sublime = {fallthrough: "default"};
var cmds = CodeMirror.commands;
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.
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 = [];
for (var i = 0; i < ranges.length; i++) {
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;
for (var i = ranges.length - 1; i >= 0; i--) {
var range = ranges[i], from = range.to().line + 1, to = range.from().line;

View File

@ -616,7 +616,9 @@
visualLine: false,
visualBlock: false,
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;
@ -785,17 +787,19 @@
* pasted, should it insert itself into a new line, or should the text be
* inserted at the cursor position.)
*/
function Register(text, linewise) {
function Register(text, linewise, blockwise) {
this.clear();
this.keyBuffer = [text || ''];
this.insertModeChanges = [];
this.searchQueries = [];
this.linewise = !!linewise;
this.blockwise = !!blockwise;
}
Register.prototype = {
setText: function(text, linewise) {
setText: function(text, linewise, blockwise) {
this.keyBuffer = [text || ''];
this.linewise = !!linewise;
this.blockwise = !!blockwise;
},
pushText: function(text, linewise) {
// if this register has ever been set to linewise, use linewise.
@ -840,7 +844,7 @@
registers['/'] = new Register();
}
RegisterController.prototype = {
pushText: function(registerName, operator, text, linewise) {
pushText: function(registerName, operator, text, linewise, blockwise) {
if (linewise && text.charAt(0) == '\n') {
text = text.slice(1) + '\n';
}
@ -857,7 +861,7 @@
switch (operator) {
case '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;
case 'delete':
case 'change':
@ -873,7 +877,7 @@
break;
}
// Make sure the unnamed register is set to what just happened
this.unnamedRegister.setText(text, linewise);
this.unnamedRegister.setText(text, linewise, blockwise);
return;
}
@ -882,7 +886,7 @@
if (append) {
register.pushText(text, linewise);
} else {
register.setText(text, linewise);
register.setText(text, linewise, blockwise);
}
// The unnamed register always has the same value as the last used
// register.
@ -1860,6 +1864,12 @@
var curStart = cursorIsBefore(start.anchor, start.head) ? start.anchor : start.head;
var curEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor;
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');
// save the selectionEnd mark
var selectionEnd = vim.marks['>'] ? vim.marks['>'].find() : cm.getCursor('head');
@ -1868,7 +1878,7 @@
operatorArgs.linewise);
if (operatorArgs.linewise) {
// 'C' in visual block extends the block till eol for all lines
if (vim.visualBlock){
if (visualBlock){
var startLine = curStart.line;
while (startLine <= curEnd.line) {
var endCh = lineLength(cm, startLine);
@ -1898,7 +1908,7 @@
curEnd = offsetCursor(curEnd, 0, - match[0].length);
}
}
if (vim.visualBlock) {
if (visualBlock) {
cm.replaceSelections(replacement);
} else {
cm.setCursor(curStart);
@ -1916,17 +1926,19 @@
var curEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor;
// Save the '>' mark before cm.replaceRange clears it.
var selectionEnd, selectionStart;
var blockwise = vim.visualBlock;
if (vim.visualMode) {
selectionEnd = vim.marks['>'].find();
selectionStart = vim.marks['<'].find();
} else if (vim.lastSelection) {
selectionEnd = vim.lastSelection.curStartMark.find();
selectionStart = vim.lastSelection.curEndMark.find();
blockwise = vim.lastSelection.visualBlock;
}
var text = cm.getSelection();
vimGlobalState.registerController.pushText(
operatorArgs.registerName, 'delete', text,
operatorArgs.linewise);
operatorArgs.linewise, blockwise);
var replacement = new Array(selections.length).join('1').split('1');
// If the ending line is past the last line, inclusive, instead of
// including the trailing \n, include the \n before the starting line
@ -1999,11 +2011,11 @@
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();
vimGlobalState.registerController.pushText(
operatorArgs.registerName, 'yank',
text, operatorArgs.linewise);
text, operatorArgs.linewise, vim.visualBlock);
cm.setCursor(curOriginal);
}
};
@ -2096,6 +2108,11 @@
vim.insertMode = true;
vim.insertModeRepeat = actionArgs && actionArgs.repeat || 1;
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') {
var cursor = cm.getCursor();
cursor = Pos(cursor.line, lineLength(cm, cursor.line));
@ -2103,15 +2120,34 @@
} else if (insertAt == 'charAfter') {
cm.setCursor(offsetCursor(cm.getCursor(), 0, 1));
} else if (insertAt == 'firstNonBlank') {
cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm));
} else if (insertAt == 'endOfSelectedArea') {
var selectionEnd = cm.getCursor('head');
var selectionStart = cm.getCursor('anchor');
if (selectionEnd.line < selectionStart.line) {
selectionEnd = Pos(selectionStart.line, 0);
if (vim.visualMode && !vim.visualBlock) {
if (selectionEnd.line < selectionStart.line) {
cm.setCursor(selectionEnd);
} 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.setCursor(selectionEnd);
exitVisualMode(cm);
}
cm.setOption('keyMap', 'vim-insert');
cm.setOption('disableInput', false);
@ -2129,6 +2165,9 @@
cm.on('change', onChange);
CodeMirror.on(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown);
}
if (vim.visualMode) {
exitVisualMode(cm);
}
},
toggleVisualMode: function(cm, actionArgs, vim) {
var repeat = actionArgs.repeat;
@ -2339,6 +2378,7 @@
var text = Array(actionArgs.repeat + 1).join(text);
}
var linewise = register.linewise;
var blockwise = register.blockwise;
if (linewise) {
if(vim.visualMode) {
text = vim.visualLine ? text.slice(0, -1) : '\n' + text.slice(0, text.length - 1) + '\n';
@ -2351,6 +2391,12 @@
cur.ch = 0;
}
} 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;
}
var curPosFinal;
@ -2362,32 +2408,75 @@
var selectedArea = getSelectedAreaRange(cm, vim);
var selectionStart = selectedArea[0];
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.
if (vim.lastSelection) lastSelectionCurEnd = vim.lastSelection.curEndMark.find();
if (vim.lastSelection) {
lastSelectionCurEnd = vim.lastSelection.curEndMark.find();
}
// push the previously selected text to unnamed register
vimGlobalState.registerController.unnamedRegister.setText(cm.getRange(selectionStart, selectionEnd));
cm.replaceRange(text, 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);
curPosFinal = cm.posFromIndex(cm.indexFromPos(selectionStart) + text.length - 1);
}
// restore the the curEnd marker
if(lastSelectionCurEnd) vim.lastSelection.curEndMark = cm.setBookmark(lastSelectionCurEnd);
curPosFinal = cm.posFromIndex(cm.indexFromPos(selectionStart) + text.length - 1);
if(linewise)curPosFinal.ch=0;
if(lastSelectionCurEnd) {
vim.lastSelection.curEndMark = cm.setBookmark(lastSelectionCurEnd);
}
if (linewise) {
curPosFinal.ch=0;
}
} else {
cm.replaceRange(text, cur);
// Now fine tune the cursor to where we want it.
if (linewise && actionArgs.after) {
curPosFinal = Pos(
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 {
cm.replaceRange(text, cur);
// Now fine tune the cursor to where we want it.
if (linewise && actionArgs.after) {
curPosFinal = Pos(
cur.line + 1,
findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line + 1)));
} else if (linewise && !actionArgs.after) {
curPosFinal = Pos(
cur.line,
findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line)));
} else if (!linewise && actionArgs.after) {
idx = cm.indexFromPos(cur);
curPosFinal = cm.posFromIndex(idx + text.length - 1);
} else {
idx = cm.indexFromPos(cur);
curPosFinal = cm.posFromIndex(idx + text.length);
} else if (linewise && !actionArgs.after) {
curPosFinal = Pos(
cur.line,
findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line)));
} else if (!linewise && actionArgs.after) {
idx = cm.indexFromPos(cur);
curPosFinal = cm.posFromIndex(idx + text.length - 1);
} else {
idx = cm.indexFromPos(cur);
curPosFinal = cm.posFromIndex(idx + text.length);
}
}
}
cm.setCursor(curPosFinal);
@ -2597,6 +2686,12 @@
function escapeRegex(s) {
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
// of text with selectionEnd as any of its corner
// Height of block:
@ -2606,55 +2701,86 @@
function selectBlock(cm, selectionEnd) {
var selections = [], ranges = cm.listSelections();
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 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);
// sets to true when selectionEnd already lies inside the existing selections
var contains = getIndex(ranges, selectionEnd) < 0 ? false : true;
selectionEnd = cm.clipPos(selectionEnd);
// difference in distance of selectionEnd from each end of the block.
var near = Math.abs(firstRange.line - selectionEnd.line) - Math.abs(lastRange.line - selectionEnd.line);
if (near > 0) {
end = selectionEnd.line;
start = firstRange.line;
if (lastRange.line == selectionEnd.line && contains) {
start = end;
var contains = getIndex(ranges, selectionEnd) < 0 ? false : true;
var isClipped = !cursorEqual(originalSelectionEnd, selectionEnd);
// This function helps to check selection crossing
// in case of short lines.
var processSelectionCrossing = function() {
if (isClipped) {
if (curEnd.ch >= selectionStart.ch) {
selectionStart.ch++;
}
} else if (curEnd.ch == lineLength(cm, curEnd.line)) {
if (cursorEqual(ranges[primIndex].anchor, ranges[primIndex].head) && ranges.length>1) {
if (direction == 'up') {
if (contains || primIndex>0) {
start = firstRange.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 (selectionEnd.ch >= selectionStart.ch) {
selectionStart.ch--;
}
}
}
} else if (near < 0) {
start = selectionEnd.line;
end = lastRange.line;
if (firstRange.line == selectionEnd.line && contains) {
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;
};
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;
}
}
if (start > end) {
var tmp = start;
start = end;
end = tmp;
}
selectionStart = (near > 0) ? firstRange : lastRange;
while (start <= end) {
var anchor = {line: start, ch: selectionStart.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};
selections.push(range);
if (cursorEqual(head, selectionEnd)) {
@ -2666,16 +2792,29 @@
// after selection crossing
selectionEnd.ch = selections[0].head.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);
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++) {
if (cursorEqual(ranges[i].head, head)) {
return i;
var atAnchor = cursorEqual(ranges[i].anchor, cursor);
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) {
var lastSelection = vim.lastSelection;
@ -2739,7 +2878,7 @@
var ranges = cm.listSelections();
// This check ensures to set the cursor
// position where we left off in previous selection
var swap = getIndex(ranges, selectionStart) > -1;
var swap = getIndex(ranges, selectionStart, 'head') > -1;
if (vim.visualBlock) {
var height = Math.abs(selectionStart.line - selectionEnd.line)+1;
var width = Math.abs(selectionStart.ch - selectionEnd.ch);
@ -2759,14 +2898,16 @@
var vim = cm.state.vim;
var selectionStart = cm.getCursor('anchor');
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);
vim.visualMode = false;
vim.visualLine = false;
vim.visualBlock = false;
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));
}
CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
@ -4498,7 +4639,32 @@
var macroModeState = vimGlobalState.macroModeState;
var insertModeChangeRegister = vimGlobalState.registerController.getRegister('.');
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) {
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);
CodeMirror.off(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown);
}
@ -4515,13 +4681,64 @@
cm.setOption('disableInput', true);
cm.toggleOverwrite(false); // exit replace mode if we were in it.
// 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"});
if (macroModeState.isRecording) {
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'] = {
// TODO: override navigation keys so that Esc will cancel automatic
// indentation from o, O, i_<CR>
@ -4535,9 +4752,23 @@
CodeMirror.commands.newlineAndIndent;
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']
};
CodeMirror.keyMap['await-second'] = {
"'i'": secondEscCharacterHandler('i'),
"'j'": secondEscCharacterHandler('j'),
"'k'": secondEscCharacterHandler('k'),
fallthrough: ['vim-insert']
};
CodeMirror.keyMap['vim-replace'] = {
'Backspace': 'goCharLeft',
fallthrough: ['vim-insert']
@ -4710,11 +4941,7 @@
// insert mode changes. Will conform to that behavior.
repeat = !vim.lastEditActionCommand ? 1 : repeat;
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);
macroModeState.lastInsertModeChanges = changeObject;
}
}
vim.inputState = vim.lastEditInputState;
@ -4752,6 +4979,18 @@
}
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 j = 0; j < changes.length; j++) {
var change = changes[j];
@ -4762,6 +5001,10 @@
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"]}
});
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();
// Read special literals: backspace, newline, space, return.
// 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;
}
// Read unicode character: \u1000 \uA0a1

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