mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 960704 - Upgrade CodeMirror to 3.21. r=msucan
This commit is contained in:
parent
c66cff2b44
commit
e37bd6d5eb
@ -17,11 +17,19 @@ To confirm the functionality run mochitests for the following components:
|
||||
* styleditor
|
||||
* netmonitor
|
||||
|
||||
The sourceeditor component contains imported CodeMirror tests [3]. Some
|
||||
tests were commented out because we don't use that functionality within
|
||||
Firefox (for example Ruby editing mode). The search addon (search.js)
|
||||
was slightly modified to make search UI localizable. Other than that,
|
||||
we don't have any Mozilla-specific patches applied to CodeMirror itself.
|
||||
The sourceeditor component contains imported CodeMirror tests [3].
|
||||
|
||||
* Some tests were commented out because we don't use that functionality
|
||||
within Firefox (for example Ruby editing mode). Be careful when updating
|
||||
files test/codemirror.html and test/vimemacs.html; they were modified to
|
||||
co-exist with Mozilla's testing infrastructure.
|
||||
* In cm_comment_test.js comment out fallbackToBlock and fallbackToLine
|
||||
tests.
|
||||
* The search addon (search.js) was slightly modified to make search
|
||||
UI localizable.
|
||||
|
||||
Other than that, we don't have any Mozilla-specific patches applied to
|
||||
CodeMirror itself.
|
||||
|
||||
# Addons
|
||||
|
||||
@ -37,6 +45,7 @@ in the LICENSE file:
|
||||
* codemirror.css
|
||||
* codemirror.js
|
||||
* comment.js
|
||||
* activeline.js
|
||||
* dialog/dialog.css
|
||||
* dialog/dialog.js
|
||||
* keymap/emacs.js
|
||||
@ -50,6 +59,7 @@ in the LICENSE file:
|
||||
* css.js
|
||||
* javascript.js
|
||||
* clike.js
|
||||
* htmlmixed.js
|
||||
* matchbrackets.js
|
||||
* closebrackets.js
|
||||
* trailingspace.js
|
||||
@ -62,6 +72,8 @@ in the LICENSE file:
|
||||
* test/cm_mode_javascript_test.js
|
||||
* test/cm_mode_test.css
|
||||
* test/cm_mode_test.js
|
||||
* test/cm_vim_test.js
|
||||
* test/cm_emacs_test.js
|
||||
* test/cm_test.js
|
||||
|
||||
# Footnotes
|
||||
|
@ -1,22 +1,21 @@
|
||||
/* vim:set ts=2 sw=2 sts=2 et tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
// Because sometimes you need to style the cursor's line.
|
||||
//
|
||||
// Adds an option 'styleActiveLine' which, when enabled, gives the
|
||||
// active line's wrapping <div> the CSS class "CodeMirror-activeline",
|
||||
// and gives its background <div> the class "CodeMirror-activeline-background".
|
||||
|
||||
(function () {
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
const WRAP_CLASS = "CodeMirror-activeline";
|
||||
const BACK_CLASS = "CodeMirror-activeline-background";
|
||||
var WRAP_CLASS = "CodeMirror-activeline";
|
||||
var BACK_CLASS = "CodeMirror-activeline-background";
|
||||
|
||||
CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) {
|
||||
var prev = old && old != CodeMirror.Init;
|
||||
|
||||
if (val && !prev) {
|
||||
updateActiveLine(cm);
|
||||
cm.on("cursorActivity", updateActiveLine);
|
||||
updateActiveLine(cm, cm.getCursor().line);
|
||||
cm.on("beforeSelectionChange", selectionChange);
|
||||
} else if (!val && prev) {
|
||||
cm.off("cursorActivity", updateActiveLine);
|
||||
cm.off("beforeSelectionChange", selectionChange);
|
||||
clearActiveLine(cm);
|
||||
delete cm.state.activeLine;
|
||||
}
|
||||
@ -29,12 +28,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
function updateActiveLine(cm) {
|
||||
var line = cm.getLineHandleVisualStart(cm.getCursor().line);
|
||||
function updateActiveLine(cm, selectedLine) {
|
||||
var line = cm.getLineHandleVisualStart(selectedLine);
|
||||
if (cm.state.activeLine == line) return;
|
||||
clearActiveLine(cm);
|
||||
cm.addLineClass(line, "wrap", WRAP_CLASS);
|
||||
cm.addLineClass(line, "background", BACK_CLASS);
|
||||
cm.state.activeLine = line;
|
||||
cm.operation(function() {
|
||||
clearActiveLine(cm);
|
||||
cm.addLineClass(line, "wrap", WRAP_CLASS);
|
||||
cm.addLineClass(line, "background", BACK_CLASS);
|
||||
cm.state.activeLine = line;
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
function selectionChange(cm, sel) {
|
||||
updateActiveLine(cm, sel.head.line);
|
||||
}
|
||||
})();
|
||||
|
@ -203,18 +203,34 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
|
||||
return "string";
|
||||
}
|
||||
|
||||
function mimes(ms, mode) {
|
||||
for (var i = 0; i < ms.length; ++i) CodeMirror.defineMIME(ms[i], mode);
|
||||
function def(mimes, mode) {
|
||||
var words = [];
|
||||
function add(obj) {
|
||||
if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop))
|
||||
words.push(prop);
|
||||
}
|
||||
add(mode.keywords);
|
||||
add(mode.builtin);
|
||||
add(mode.atoms);
|
||||
if (words.length) {
|
||||
mode.helperType = mimes[0];
|
||||
CodeMirror.registerHelper("hintWords", mimes[0], words);
|
||||
}
|
||||
|
||||
for (var i = 0; i < mimes.length; ++i)
|
||||
CodeMirror.defineMIME(mimes[i], mode);
|
||||
}
|
||||
|
||||
mimes(["text/x-csrc", "text/x-c", "text/x-chdr"], {
|
||||
def(["text/x-csrc", "text/x-c", "text/x-chdr"], {
|
||||
name: "clike",
|
||||
keywords: words(cKeywords),
|
||||
blockKeywords: words("case do else for if switch while struct"),
|
||||
atoms: words("null"),
|
||||
hooks: {"#": cppHook}
|
||||
hooks: {"#": cppHook},
|
||||
modeProps: {fold: ["brace", "include"]}
|
||||
});
|
||||
mimes(["text/x-c++src", "text/x-c++hdr"], {
|
||||
|
||||
def(["text/x-c++src", "text/x-c++hdr"], {
|
||||
name: "clike",
|
||||
keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try bool explicit new " +
|
||||
"static_cast typeid catch operator template typename class friend private " +
|
||||
@ -222,7 +238,8 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
|
||||
"wchar_t"),
|
||||
blockKeywords: words("catch class do else finally for if struct switch try while"),
|
||||
atoms: words("true false null"),
|
||||
hooks: {"#": cppHook}
|
||||
hooks: {"#": cppHook},
|
||||
modeProps: {fold: ["brace", "include"]}
|
||||
});
|
||||
CodeMirror.defineMIME("text/x-java", {
|
||||
name: "clike",
|
||||
@ -238,7 +255,8 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
|
||||
stream.eatWhile(/[\w\$_]/);
|
||||
return "meta";
|
||||
}
|
||||
}
|
||||
},
|
||||
modeProps: {fold: ["brace", "import"]}
|
||||
});
|
||||
CodeMirror.defineMIME("text/x-csharp", {
|
||||
name: "clike",
|
||||
@ -303,7 +321,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
|
||||
}
|
||||
}
|
||||
});
|
||||
mimes(["x-shader/x-vertex", "x-shader/x-fragment"], {
|
||||
def(["x-shader/x-vertex", "x-shader/x-fragment"], {
|
||||
name: "clike",
|
||||
keywords: words("float int bool void " +
|
||||
"vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " +
|
||||
@ -357,6 +375,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
|
||||
"gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits " +
|
||||
"gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits " +
|
||||
"gl_MaxDrawBuffers"),
|
||||
hooks: {"#": cppHook}
|
||||
hooks: {"#": cppHook},
|
||||
modeProps: {fold: ["brace", "include"]}
|
||||
});
|
||||
}());
|
||||
|
@ -28,7 +28,7 @@
|
||||
var map = {
|
||||
name : "autoCloseBrackets",
|
||||
Backspace: function(cm) {
|
||||
if (cm.somethingSelected()) return CodeMirror.Pass;
|
||||
if (cm.somethingSelected() || cm.getOption("disableInput")) return CodeMirror.Pass;
|
||||
var cur = cm.getCursor(), around = charsAround(cm, cur);
|
||||
if (around && pairs.indexOf(around) % 2 == 0)
|
||||
cm.replaceRange("", CodeMirror.Pos(cur.line, cur.ch - 1), CodeMirror.Pos(cur.line, cur.ch + 1));
|
||||
@ -49,7 +49,8 @@
|
||||
else cm.execCommand("goCharRight");
|
||||
}
|
||||
map["'" + left + "'"] = function(cm) {
|
||||
if (left == "'" && cm.getTokenAt(cm.getCursor()).type == "comment")
|
||||
if (left == "'" && cm.getTokenAt(cm.getCursor()).type == "comment" ||
|
||||
cm.getOption("disableInput"))
|
||||
return CodeMirror.Pass;
|
||||
if (cm.somethingSelected()) return surround(cm);
|
||||
if (left == right && maybeOverwrite(cm) != CodeMirror.Pass) return;
|
||||
@ -70,7 +71,8 @@
|
||||
function buildExplodeHandler(pairs) {
|
||||
return function(cm) {
|
||||
var cur = cm.getCursor(), around = charsAround(cm, cur);
|
||||
if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
|
||||
if (!around || pairs.indexOf(around) % 2 != 0 || cm.getOption("disableInput"))
|
||||
return CodeMirror.Pass;
|
||||
cm.operation(function() {
|
||||
var newPos = CodeMirror.Pos(cur.line + 1, 0);
|
||||
cm.replaceSelection("\n\n", {anchor: newPos, head: newPos}, "+input");
|
||||
|
@ -61,7 +61,7 @@
|
||||
|
||||
/* DEFAULT THEME */
|
||||
|
||||
.cm-s-default .cm-keyword {color: #708;}
|
||||
.cm-s-default .cm-keyword {color: #708;}
|
||||
.cm-s-default .cm-atom {color: #219;}
|
||||
.cm-s-default .cm-number {color: #164;}
|
||||
.cm-s-default .cm-def {color: #00f;}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// CodeMirror version 3.20
|
||||
// CodeMirror version 3.21
|
||||
//
|
||||
// CodeMirror is the only global var we claim
|
||||
window.CodeMirror = (function() {
|
||||
@ -12,10 +12,11 @@ window.CodeMirror = (function() {
|
||||
// IE11 currently doesn't count as 'ie', since it has almost none of
|
||||
// the same bugs as earlier versions. Use ie_gt10 to handle
|
||||
// incompatibilities in that version.
|
||||
var ie = /MSIE \d/.test(navigator.userAgent);
|
||||
var ie_lt8 = ie && (document.documentMode == null || document.documentMode < 8);
|
||||
var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
|
||||
var old_ie = /MSIE \d/.test(navigator.userAgent);
|
||||
var ie_lt8 = old_ie && (document.documentMode == null || document.documentMode < 8);
|
||||
var ie_lt9 = old_ie && (document.documentMode == null || document.documentMode < 9);
|
||||
var ie_gt10 = /Trident\/([7-9]|\d{2,})\./.test(navigator.userAgent);
|
||||
var ie = old_ie || ie_gt10;
|
||||
var webkit = /WebKit\//.test(navigator.userAgent);
|
||||
var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
|
||||
var chrome = /Chrome\//.test(navigator.userAgent);
|
||||
@ -37,7 +38,7 @@ window.CodeMirror = (function() {
|
||||
if (opera_version && opera_version >= 15) { opera = false; webkit = true; }
|
||||
// Some browsers use the wrong event properties to signal cmd/ctrl on OS X
|
||||
var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11));
|
||||
var captureMiddleClick = gecko || (ie && !ie_lt9);
|
||||
var captureMiddleClick = gecko || (old_ie && !ie_lt9);
|
||||
|
||||
// Optimize some code when these features are not used
|
||||
var sawReadOnlySpans = false, sawCollapsedSpans = false;
|
||||
@ -63,7 +64,8 @@ window.CodeMirror = (function() {
|
||||
overlays: [],
|
||||
modeGen: 0,
|
||||
overwrite: false, focused: false,
|
||||
suppressEdits: false, pasteIncoming: false,
|
||||
suppressEdits: false,
|
||||
pasteIncoming: false, cutIncoming: false,
|
||||
draggingText: false,
|
||||
highlight: new Delayed()};
|
||||
|
||||
@ -77,7 +79,7 @@ window.CodeMirror = (function() {
|
||||
|
||||
// Override magic textarea content restore that IE sometimes does
|
||||
// on our hidden textarea on reload
|
||||
if (ie) setTimeout(bind(resetInput, this, true), 20);
|
||||
if (old_ie) setTimeout(bind(resetInput, this, true), 20);
|
||||
|
||||
registerEventHandlers(this);
|
||||
// IE throws unspecified error in certain cases, when
|
||||
@ -197,6 +199,10 @@ window.CodeMirror = (function() {
|
||||
|
||||
function loadMode(cm) {
|
||||
cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
|
||||
resetModeState(cm);
|
||||
}
|
||||
|
||||
function resetModeState(cm) {
|
||||
cm.doc.iter(function(line) {
|
||||
if (line.stateAfter) line.stateAfter = null;
|
||||
if (line.styles) line.styles = null;
|
||||
@ -246,7 +252,6 @@ window.CodeMirror = (function() {
|
||||
var map = keyMap[cm.options.keyMap], style = map.style;
|
||||
cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
|
||||
(style ? " cm-keymap-" + style : "");
|
||||
cm.state.disableInput = map.disableInput;
|
||||
}
|
||||
|
||||
function themeChanged(cm) {
|
||||
@ -452,7 +457,7 @@ window.CodeMirror = (function() {
|
||||
// updates.
|
||||
function updateDisplayInner(cm, changes, visible, forced) {
|
||||
var display = cm.display, doc = cm.doc;
|
||||
if (!display.wrapper.clientWidth) {
|
||||
if (!display.wrapper.offsetWidth) {
|
||||
display.showingFrom = display.showingTo = doc.first;
|
||||
display.viewOffset = 0;
|
||||
return;
|
||||
@ -537,6 +542,7 @@ window.CodeMirror = (function() {
|
||||
}
|
||||
display.showingFrom = from; display.showingTo = to;
|
||||
|
||||
display.gutters.style.height = "";
|
||||
updateHeightsInViewport(cm);
|
||||
updateViewOffset(cm);
|
||||
|
||||
@ -719,9 +725,9 @@ window.CodeMirror = (function() {
|
||||
if (bgClass)
|
||||
wrap.insertBefore(elt("div", null, bgClass + " CodeMirror-linebackground"), wrap.firstChild);
|
||||
if (cm.options.lineNumbers || markers) {
|
||||
var gutterWrap = wrap.insertBefore(elt("div", null, null, "position: absolute; left: " +
|
||||
var gutterWrap = wrap.insertBefore(elt("div", null, "CodeMirror-gutter-wrapper", "position: absolute; left: " +
|
||||
(cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
|
||||
wrap.firstChild);
|
||||
lineElement);
|
||||
if (cm.options.fixedGutter) (wrap.alignable || (wrap.alignable = [])).push(gutterWrap);
|
||||
if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
|
||||
wrap.lineNumber = gutterWrap.appendChild(
|
||||
@ -1055,7 +1061,7 @@ window.CodeMirror = (function() {
|
||||
// doesn't work when wrapping is on, but in that case the
|
||||
// situation is slightly better, since IE does cache line-wrapping
|
||||
// information and only recomputes per-line.
|
||||
if (ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length > 100) {
|
||||
if (old_ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length > 100) {
|
||||
var fragment = document.createDocumentFragment();
|
||||
var chunk = 10, n = pre.childNodes.length;
|
||||
for (var i = 0, chunks = Math.ceil(n / chunk); i < chunks; ++i) {
|
||||
@ -1115,7 +1121,7 @@ window.CodeMirror = (function() {
|
||||
}
|
||||
}
|
||||
if (!rect) rect = data[i] = measureRect(getRect(node));
|
||||
if (cur.measureRight) rect.right = getRect(cur.measureRight).left;
|
||||
if (cur.measureRight) rect.right = getRect(cur.measureRight).left - outer.left;
|
||||
if (cur.leftSide) rect.leftSide = measureRect(getRect(cur.leftSide));
|
||||
}
|
||||
removeChildren(cm.display.measure);
|
||||
@ -1291,7 +1297,7 @@ window.CodeMirror = (function() {
|
||||
if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
|
||||
var ch = x < fromX || x - fromX <= toX - x ? from : to;
|
||||
var xDiff = x - (ch == from ? fromX : toX);
|
||||
while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
|
||||
while (isExtendingChar(lineObj.text.charAt(ch))) ++ch;
|
||||
var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
|
||||
xDiff < 0 ? -1 : xDiff ? 1 : 0);
|
||||
return pos;
|
||||
@ -1483,7 +1489,7 @@ window.CodeMirror = (function() {
|
||||
// supported or compatible enough yet to rely on.)
|
||||
function readInput(cm) {
|
||||
var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
|
||||
if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.state.disableInput) return false;
|
||||
if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.options.disableInput) return false;
|
||||
if (cm.state.pasteIncoming && cm.state.fakedLastChar) {
|
||||
input.value = input.value.substring(0, input.value.length - 1);
|
||||
cm.state.fakedLastChar = false;
|
||||
@ -1501,22 +1507,32 @@ window.CodeMirror = (function() {
|
||||
var same = 0, l = Math.min(prevInput.length, text.length);
|
||||
while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
|
||||
var from = sel.from, to = sel.to;
|
||||
var inserted = text.slice(same);
|
||||
if (same < prevInput.length)
|
||||
from = Pos(from.line, from.ch - (prevInput.length - same));
|
||||
else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming)
|
||||
to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
|
||||
to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + inserted.length));
|
||||
|
||||
var updateInput = cm.curOp.updateInput;
|
||||
var changeEvent = {from: from, to: to, text: splitLines(text.slice(same)),
|
||||
origin: cm.state.pasteIncoming ? "paste" : "+input"};
|
||||
var changeEvent = {from: from, to: to, text: splitLines(inserted),
|
||||
origin: cm.state.pasteIncoming ? "paste" : cm.state.cutIncoming ? "cut" : "+input"};
|
||||
makeChange(cm.doc, changeEvent, "end");
|
||||
cm.curOp.updateInput = updateInput;
|
||||
signalLater(cm, "inputRead", cm, changeEvent);
|
||||
if (inserted && !cm.state.pasteIncoming && cm.options.electricChars &&
|
||||
cm.options.smartIndent && sel.head.ch < 100) {
|
||||
var electric = cm.getModeAt(sel.head).electricChars;
|
||||
if (electric) for (var i = 0; i < electric.length; i++)
|
||||
if (inserted.indexOf(electric.charAt(i)) > -1) {
|
||||
indentLine(cm, sel.head.line, "smart");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
|
||||
else cm.display.prevInput = text;
|
||||
if (withOp) endOperation(cm);
|
||||
cm.state.pasteIncoming = false;
|
||||
cm.state.pasteIncoming = cm.state.cutIncoming = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1551,7 +1567,7 @@ window.CodeMirror = (function() {
|
||||
function registerEventHandlers(cm) {
|
||||
var d = cm.display;
|
||||
on(d.scroller, "mousedown", operation(cm, onMouseDown));
|
||||
if (ie)
|
||||
if (old_ie)
|
||||
on(d.scroller, "dblclick", operation(cm, function(e) {
|
||||
if (signalDOMEvent(cm, e)) return;
|
||||
var pos = posFromMouse(cm, e);
|
||||
@ -1657,21 +1673,22 @@ window.CodeMirror = (function() {
|
||||
fastPoll(cm);
|
||||
});
|
||||
|
||||
function prepareCopy() {
|
||||
function prepareCopy(e) {
|
||||
if (d.inaccurateSelection) {
|
||||
d.prevInput = "";
|
||||
d.inaccurateSelection = false;
|
||||
d.input.value = cm.getSelection();
|
||||
selectInput(d.input);
|
||||
}
|
||||
if (e.type == "cut") cm.state.cutIncoming = true;
|
||||
}
|
||||
on(d.input, "cut", prepareCopy);
|
||||
on(d.input, "copy", prepareCopy);
|
||||
|
||||
// Needed to handle Tab key in KHTML
|
||||
if (khtml) on(d.sizer, "mouseup", function() {
|
||||
if (document.activeElement == d.input) d.input.blur();
|
||||
focusInput(cm);
|
||||
if (document.activeElement == d.input) d.input.blur();
|
||||
focusInput(cm);
|
||||
});
|
||||
}
|
||||
|
||||
@ -1755,6 +1772,9 @@ window.CodeMirror = (function() {
|
||||
e_preventDefault(e2);
|
||||
extendSelection(cm.doc, start);
|
||||
focusInput(cm);
|
||||
// Work around unexplainable focus problem in IE9 (#2127)
|
||||
if (old_ie && !ie_lt9)
|
||||
setTimeout(function() {document.body.focus(); focusInput(cm);}, 20);
|
||||
}
|
||||
});
|
||||
// Let the drag handler handle this.
|
||||
@ -1829,7 +1849,7 @@ window.CodeMirror = (function() {
|
||||
}
|
||||
|
||||
var move = operation(cm, function(e) {
|
||||
if (!ie && !e_button(e)) done(e);
|
||||
if (!old_ie && !e_button(e)) done(e);
|
||||
else extend(e);
|
||||
});
|
||||
var up = operation(cm, done);
|
||||
@ -1974,7 +1994,7 @@ window.CodeMirror = (function() {
|
||||
// know one. These don't have to be accurate -- the result of them
|
||||
// being wrong would just be a slight flicker on the first wheel
|
||||
// scroll (if it is large enough).
|
||||
if (ie) wheelPixelsPerUnit = -.53;
|
||||
if (old_ie) wheelPixelsPerUnit = -.53;
|
||||
else if (gecko) wheelPixelsPerUnit = 15;
|
||||
else if (chrome) wheelPixelsPerUnit = -.7;
|
||||
else if (safari) wheelPixelsPerUnit = -1/3;
|
||||
@ -2128,7 +2148,7 @@ window.CodeMirror = (function() {
|
||||
var cm = this;
|
||||
if (!cm.state.focused) onFocus(cm);
|
||||
if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
|
||||
if (ie && e.keyCode == 27) e.returnValue = false;
|
||||
if (old_ie && e.keyCode == 27) e.returnValue = false;
|
||||
var code = e.keyCode;
|
||||
// IE does strange things with escape.
|
||||
cm.doc.sel.shift = code == 16 || e.shiftKey;
|
||||
@ -2149,10 +2169,6 @@ window.CodeMirror = (function() {
|
||||
if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
|
||||
if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
|
||||
var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
|
||||
if (this.options.electricChars && this.doc.mode.electricChars &&
|
||||
this.options.smartIndent && !isReadOnly(this) &&
|
||||
this.doc.mode.electricChars.indexOf(ch) > -1)
|
||||
setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, "smart");}), 75);
|
||||
if (handleCharBinding(cm, e, ch)) return;
|
||||
if (ie && !ie_lt9) cm.display.inputHasSelection = null;
|
||||
fastPoll(cm);
|
||||
@ -2201,7 +2217,7 @@ window.CodeMirror = (function() {
|
||||
var oldCSS = display.input.style.cssText;
|
||||
display.inputDiv.style.position = "absolute";
|
||||
display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
|
||||
"px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
|
||||
"px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: transparent; outline: none;" +
|
||||
"border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);";
|
||||
focusInput(cm);
|
||||
resetInput(cm, true);
|
||||
@ -2223,10 +2239,10 @@ window.CodeMirror = (function() {
|
||||
|
||||
// Try to detect the user choosing select-all
|
||||
if (display.input.selectionStart != null) {
|
||||
if (!ie || ie_lt9) prepareSelectAllHack();
|
||||
if (!old_ie || ie_lt9) prepareSelectAllHack();
|
||||
clearTimeout(detectingSelectAll);
|
||||
var i = 0, poll = function(){
|
||||
if (display.prevInput == " " && display.input.selectionStart == 0)
|
||||
if (display.prevInput == "\u200b" && display.input.selectionStart == 0)
|
||||
operation(cm, commands.selectAll)(cm);
|
||||
else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
|
||||
else resetInput(cm);
|
||||
@ -2235,7 +2251,7 @@ window.CodeMirror = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
if (ie && !ie_lt9) prepareSelectAllHack();
|
||||
if (old_ie && !ie_lt9) prepareSelectAllHack();
|
||||
if (captureMiddleClick) {
|
||||
e_stop(e);
|
||||
var mouseup = function() {
|
||||
@ -2508,6 +2524,7 @@ window.CodeMirror = (function() {
|
||||
|
||||
function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
|
||||
function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
|
||||
function cmp(a, b) {return a.line - b.line || a.ch - b.ch;}
|
||||
function copyPos(x) {return Pos(x.line, x.ch);}
|
||||
|
||||
// SELECTION
|
||||
@ -2652,14 +2669,13 @@ window.CodeMirror = (function() {
|
||||
if (coords.top + box.top < 0) doScroll = true;
|
||||
else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
|
||||
if (doScroll != null && !phantom) {
|
||||
var hidden = display.cursor.style.display == "none";
|
||||
if (hidden) {
|
||||
display.cursor.style.display = "";
|
||||
display.cursor.style.left = coords.left + "px";
|
||||
display.cursor.style.top = (coords.top - display.viewOffset) + "px";
|
||||
}
|
||||
display.cursor.scrollIntoView(doScroll);
|
||||
if (hidden) display.cursor.style.display = "none";
|
||||
var scrollNode = elt("div", "\u200b", null, "position: absolute; top: " +
|
||||
(coords.top - display.viewOffset) + "px; height: " +
|
||||
(coords.bottom - coords.top + scrollerCutOff) + "px; left: " +
|
||||
coords.left + "px; width: 2px;");
|
||||
cm.display.lineSpace.appendChild(scrollNode);
|
||||
scrollNode.scrollIntoView(doScroll);
|
||||
cm.display.lineSpace.removeChild(scrollNode);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2742,7 +2758,10 @@ window.CodeMirror = (function() {
|
||||
var tabSize = cm.options.tabSize;
|
||||
var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
|
||||
var curSpaceString = line.text.match(/^\s*/)[0], indentation;
|
||||
if (how == "smart") {
|
||||
if (!aggressive && !/\S/.test(line.text)) {
|
||||
indentation = 0;
|
||||
how = "not";
|
||||
} else if (how == "smart") {
|
||||
indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
|
||||
if (indentation == Pass) {
|
||||
if (!aggressive) return;
|
||||
@ -2924,7 +2943,7 @@ window.CodeMirror = (function() {
|
||||
}),
|
||||
indentSelection: operation(null, function(how) {
|
||||
var sel = this.doc.sel;
|
||||
if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
|
||||
if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how, true);
|
||||
var e = sel.to.line - (sel.to.ch ? 0 : 1);
|
||||
for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
|
||||
}),
|
||||
@ -2969,11 +2988,31 @@ window.CodeMirror = (function() {
|
||||
},
|
||||
|
||||
getHelper: function(pos, type) {
|
||||
if (!helpers.hasOwnProperty(type)) return;
|
||||
return this.getHelpers(pos, type)[0];
|
||||
},
|
||||
|
||||
getHelpers: function(pos, type) {
|
||||
var found = [];
|
||||
if (!helpers.hasOwnProperty(type)) return helpers;
|
||||
var help = helpers[type], mode = this.getModeAt(pos);
|
||||
return mode[type] && help[mode[type]] ||
|
||||
mode.helperType && help[mode.helperType] ||
|
||||
help[mode.name];
|
||||
if (typeof mode[type] == "string") {
|
||||
if (help[mode[type]]) found.push(help[mode[type]]);
|
||||
} else if (mode[type]) {
|
||||
for (var i = 0; i < mode[type].length; i++) {
|
||||
var val = help[mode[type][i]];
|
||||
if (val) found.push(val);
|
||||
}
|
||||
} else if (mode.helperType && help[mode.helperType]) {
|
||||
found.push(help[mode.helperType]);
|
||||
} else if (help[mode.name]) {
|
||||
found.push(help[mode.name]);
|
||||
}
|
||||
for (var i = 0; i < help._global.length; i++) {
|
||||
var cur = help._global[i];
|
||||
if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)
|
||||
found.push(cur.val);
|
||||
}
|
||||
return found;
|
||||
},
|
||||
|
||||
getStateAfter: function(line, precise) {
|
||||
@ -3120,7 +3159,10 @@ window.CodeMirror = (function() {
|
||||
|
||||
triggerOnKeyDown: operation(null, onKeyDown),
|
||||
|
||||
execCommand: function(cmd) {return commands[cmd](this);},
|
||||
execCommand: function(cmd) {
|
||||
if (commands.hasOwnProperty(cmd))
|
||||
return commands[cmd](this);
|
||||
},
|
||||
|
||||
findPosH: function(from, amount, unit, visually) {
|
||||
var dir = 1;
|
||||
@ -3162,14 +3204,18 @@ window.CodeMirror = (function() {
|
||||
},
|
||||
|
||||
moveV: operation(null, function(dir, unit) {
|
||||
var sel = this.doc.sel;
|
||||
var pos = cursorCoords(this, sel.head, "div");
|
||||
if (sel.goalColumn != null) pos.left = sel.goalColumn;
|
||||
var target = findPosV(this, pos, dir, unit);
|
||||
|
||||
if (unit == "page") addToScrollPos(this, 0, charCoords(this, target, "div").top - pos.top);
|
||||
var sel = this.doc.sel, target, goal;
|
||||
if (sel.shift || sel.extend || posEq(sel.from, sel.to)) {
|
||||
var pos = cursorCoords(this, sel.head, "div");
|
||||
if (sel.goalColumn != null) pos.left = sel.goalColumn;
|
||||
target = findPosV(this, pos, dir, unit);
|
||||
if (unit == "page") addToScrollPos(this, 0, charCoords(this, target, "div").top - pos.top);
|
||||
goal = pos.left;
|
||||
} else {
|
||||
target = dir < 0 ? sel.from : sel.to;
|
||||
}
|
||||
extendSelection(this.doc, target, target, dir);
|
||||
sel.goalColumn = pos.left;
|
||||
if (goal != null) sel.goalColumn = goal;
|
||||
}),
|
||||
|
||||
toggleOverwrite: function(value) {
|
||||
@ -3279,7 +3325,7 @@ window.CodeMirror = (function() {
|
||||
option("indentWithTabs", false);
|
||||
option("smartIndent", true);
|
||||
option("tabSize", 4, function(cm) {
|
||||
loadMode(cm);
|
||||
resetModeState(cm);
|
||||
clearCaches(cm);
|
||||
regChange(cm);
|
||||
}, true);
|
||||
@ -3332,6 +3378,7 @@ window.CodeMirror = (function() {
|
||||
if (!val) resetInput(cm, true);
|
||||
}
|
||||
});
|
||||
option("disableInput", false, function(cm, val) {if (!val) resetInput(cm, true);}, true);
|
||||
option("dragDrop", true);
|
||||
|
||||
option("cursorBlinkRate", 530);
|
||||
@ -3339,12 +3386,13 @@ window.CodeMirror = (function() {
|
||||
option("cursorHeight", 1);
|
||||
option("workTime", 100);
|
||||
option("workDelay", 100);
|
||||
option("flattenSpans", true);
|
||||
option("flattenSpans", true, resetModeState, true);
|
||||
option("addModeClass", false, resetModeState, true);
|
||||
option("pollInterval", 100);
|
||||
option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;});
|
||||
option("historyEventDelay", 500);
|
||||
option("viewportMargin", 10, function(cm){cm.refresh();}, true);
|
||||
option("maxHighlightLength", 10000, function(cm){loadMode(cm); cm.refresh();}, true);
|
||||
option("maxHighlightLength", 10000, resetModeState, true);
|
||||
option("crudeMeasuringFrom", 10000);
|
||||
option("moveInputWithCursor", true, function(cm, val) {
|
||||
if (!val) cm.display.inputDiv.style.top = cm.display.inputDiv.style.left = 0;
|
||||
@ -3401,6 +3449,9 @@ window.CodeMirror = (function() {
|
||||
}
|
||||
}
|
||||
modeObj.name = spec.name;
|
||||
if (spec.helperType) modeObj.helperType = spec.helperType;
|
||||
if (spec.modeProps) for (var prop in spec.modeProps)
|
||||
modeObj[prop] = spec.modeProps[prop];
|
||||
|
||||
return modeObj;
|
||||
};
|
||||
@ -3431,9 +3482,13 @@ window.CodeMirror = (function() {
|
||||
|
||||
var helpers = CodeMirror.helpers = {};
|
||||
CodeMirror.registerHelper = function(type, name, value) {
|
||||
if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {};
|
||||
if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []};
|
||||
helpers[type][name] = value;
|
||||
};
|
||||
CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
|
||||
CodeMirror.registerHelper(type, name, value);
|
||||
helpers[type]._global.push({pred: predicate, val: value});
|
||||
};
|
||||
|
||||
// UTILITIES
|
||||
|
||||
@ -3538,7 +3593,9 @@ window.CodeMirror = (function() {
|
||||
indentAuto: function(cm) {cm.indentSelection("smart");},
|
||||
indentMore: function(cm) {cm.indentSelection("add");},
|
||||
indentLess: function(cm) {cm.indentSelection("subtract");},
|
||||
insertTab: function(cm) {cm.replaceSelection("\t", "end", "+input");},
|
||||
insertTab: function(cm) {
|
||||
cm.replaceSelection("\t", "end", "+input");
|
||||
},
|
||||
defaultTab: function(cm) {
|
||||
if (cm.somethingSelected()) cm.indentSelection("add");
|
||||
else cm.replaceSelection("\t", "end", "+input");
|
||||
@ -3711,11 +3768,12 @@ window.CodeMirror = (function() {
|
||||
this.string = string;
|
||||
this.tabSize = tabSize || 8;
|
||||
this.lastColumnPos = this.lastColumnValue = 0;
|
||||
this.lineStart = 0;
|
||||
}
|
||||
|
||||
StringStream.prototype = {
|
||||
eol: function() {return this.pos >= this.string.length;},
|
||||
sol: function() {return this.pos == 0;},
|
||||
sol: function() {return this.pos == this.lineStart;},
|
||||
peek: function() {return this.string.charAt(this.pos) || undefined;},
|
||||
next: function() {
|
||||
if (this.pos < this.string.length)
|
||||
@ -3748,9 +3806,12 @@ window.CodeMirror = (function() {
|
||||
this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
|
||||
this.lastColumnPos = this.start;
|
||||
}
|
||||
return this.lastColumnValue;
|
||||
return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
|
||||
},
|
||||
indentation: function() {
|
||||
return countColumn(this.string, null, this.tabSize) -
|
||||
(this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
|
||||
},
|
||||
indentation: function() {return countColumn(this.string, null, this.tabSize);},
|
||||
match: function(pattern, consume, caseInsensitive) {
|
||||
if (typeof pattern == "string") {
|
||||
var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
|
||||
@ -3766,7 +3827,12 @@ window.CodeMirror = (function() {
|
||||
return match;
|
||||
}
|
||||
},
|
||||
current: function(){return this.string.slice(this.start, this.pos);}
|
||||
current: function(){return this.string.slice(this.start, this.pos);},
|
||||
hideFirstChars: function(n, inner) {
|
||||
this.lineStart += n;
|
||||
try { return inner(); }
|
||||
finally { this.lineStart -= n; }
|
||||
}
|
||||
};
|
||||
CodeMirror.StringStream = StringStream;
|
||||
|
||||
@ -3818,7 +3884,7 @@ window.CodeMirror = (function() {
|
||||
if (withOp) endOperation(cm);
|
||||
};
|
||||
|
||||
TextMarker.prototype.find = function() {
|
||||
TextMarker.prototype.find = function(bothSides) {
|
||||
var from, to;
|
||||
for (var i = 0; i < this.lines.length; ++i) {
|
||||
var line = this.lines[i];
|
||||
@ -3829,7 +3895,7 @@ window.CodeMirror = (function() {
|
||||
if (span.to != null) to = Pos(found, span.to);
|
||||
}
|
||||
}
|
||||
if (this.type == "bookmark") return from;
|
||||
if (this.type == "bookmark" && !bothSides) return from;
|
||||
return from && {from: from, to: to};
|
||||
};
|
||||
|
||||
@ -3866,39 +3932,40 @@ window.CodeMirror = (function() {
|
||||
}
|
||||
};
|
||||
|
||||
var nextMarkerId = 0;
|
||||
|
||||
function markText(doc, from, to, options, type) {
|
||||
if (options && options.shared) return markTextShared(doc, from, to, options, type);
|
||||
if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
|
||||
|
||||
var marker = new TextMarker(doc, type);
|
||||
if (posLess(to, from) || posEq(from, to) && type == "range" &&
|
||||
!(options.inclusiveLeft && options.inclusiveRight))
|
||||
return marker;
|
||||
if (options) copyObj(options, marker);
|
||||
if (posLess(to, from) || posEq(from, to) && marker.clearWhenEmpty !== false)
|
||||
return marker;
|
||||
if (marker.replacedWith) {
|
||||
marker.collapsed = true;
|
||||
marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
|
||||
if (!options.handleMouseEvents) marker.replacedWith.ignoreEvents = true;
|
||||
}
|
||||
if (marker.collapsed) sawCollapsedSpans = true;
|
||||
if (marker.collapsed) {
|
||||
if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
|
||||
from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
|
||||
throw new Error("Inserting collapsed marker partially overlapping an existing one");
|
||||
sawCollapsedSpans = true;
|
||||
}
|
||||
|
||||
if (marker.addToHistory)
|
||||
addToHistory(doc, {from: from, to: to, origin: "markText"},
|
||||
{head: doc.sel.head, anchor: doc.sel.anchor}, NaN);
|
||||
|
||||
var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd, cm = doc.cm, updateMaxLine;
|
||||
var curLine = from.line, cm = doc.cm, updateMaxLine;
|
||||
doc.iter(curLine, to.line + 1, function(line) {
|
||||
if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.display.maxLine)
|
||||
updateMaxLine = true;
|
||||
var span = {from: null, to: null, marker: marker};
|
||||
size += line.text.length;
|
||||
if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
|
||||
if (curLine == to.line) {span.to = to.ch; size -= line.text.length - to.ch;}
|
||||
if (marker.collapsed) {
|
||||
if (curLine == to.line) collapsedAtEnd = collapsedSpanAt(line, to.ch);
|
||||
if (curLine == from.line) collapsedAtStart = collapsedSpanAt(line, from.ch);
|
||||
else updateLineHeight(line, 0);
|
||||
}
|
||||
if (curLine == from.line) span.from = from.ch;
|
||||
if (curLine == to.line) span.to = to.ch;
|
||||
if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0);
|
||||
addMarkedSpan(line, span);
|
||||
++curLine;
|
||||
});
|
||||
@ -3914,9 +3981,7 @@ window.CodeMirror = (function() {
|
||||
doc.clearHistory();
|
||||
}
|
||||
if (marker.collapsed) {
|
||||
if (collapsedAtStart != collapsedAtEnd)
|
||||
throw new Error("Inserting collapsed marker overlapping an existing one");
|
||||
marker.size = size;
|
||||
marker.id = ++nextMarkerId;
|
||||
marker.atomic = true;
|
||||
}
|
||||
if (cm) {
|
||||
@ -3989,9 +4054,7 @@ window.CodeMirror = (function() {
|
||||
if (old) for (var i = 0, nw; i < old.length; ++i) {
|
||||
var span = old[i], marker = span.marker;
|
||||
var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
|
||||
if (startsBefore ||
|
||||
(marker.inclusiveLeft && marker.inclusiveRight || marker.type == "bookmark") &&
|
||||
span.from == startCh && (!isInsert || !span.marker.insertLeft)) {
|
||||
if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
|
||||
var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
|
||||
(nw || (nw = [])).push({from: span.from,
|
||||
to: endsAfter ? null : span.to,
|
||||
@ -4005,7 +4068,7 @@ window.CodeMirror = (function() {
|
||||
if (old) for (var i = 0, nw; i < old.length; ++i) {
|
||||
var span = old[i], marker = span.marker;
|
||||
var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
|
||||
if (endsAfter || marker.type == "bookmark" && span.from == endCh && (!isInsert || span.marker.insertLeft)) {
|
||||
if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
|
||||
var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
|
||||
(nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
|
||||
to: span.to == null ? null : span.to - endCh,
|
||||
@ -4055,13 +4118,9 @@ window.CodeMirror = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sameLine && first) {
|
||||
// Make sure we didn't create any zero-length spans
|
||||
for (var i = 0; i < first.length; ++i)
|
||||
if (first[i].from != null && first[i].from == first[i].to && first[i].marker.type != "bookmark")
|
||||
first.splice(i--, 1);
|
||||
if (!first.length) first = null;
|
||||
}
|
||||
// Make sure we didn't create any zero-length spans
|
||||
if (first) first = clearEmptySpans(first);
|
||||
if (last && last != first) last = clearEmptySpans(last);
|
||||
|
||||
var newMarkers = [first];
|
||||
if (!sameLine) {
|
||||
@ -4078,6 +4137,16 @@ window.CodeMirror = (function() {
|
||||
return newMarkers;
|
||||
}
|
||||
|
||||
function clearEmptySpans(spans) {
|
||||
for (var i = 0; i < spans.length; ++i) {
|
||||
var span = spans[i];
|
||||
if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
|
||||
spans.splice(i--, 1);
|
||||
}
|
||||
if (!spans.length) return null;
|
||||
return spans;
|
||||
}
|
||||
|
||||
function mergeOldSpans(doc, change) {
|
||||
var old = getOldSpans(doc, change);
|
||||
var stretched = stretchSpansOverChange(doc, change);
|
||||
@ -4128,20 +4197,48 @@ window.CodeMirror = (function() {
|
||||
return parts;
|
||||
}
|
||||
|
||||
function collapsedSpanAt(line, ch) {
|
||||
function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; }
|
||||
function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; }
|
||||
|
||||
function compareCollapsedMarkers(a, b) {
|
||||
var lenDiff = a.lines.length - b.lines.length;
|
||||
if (lenDiff != 0) return lenDiff;
|
||||
var aPos = a.find(), bPos = b.find();
|
||||
var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
|
||||
if (fromCmp) return -fromCmp;
|
||||
var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
|
||||
if (toCmp) return toCmp;
|
||||
return b.id - a.id;
|
||||
}
|
||||
|
||||
function collapsedSpanAtSide(line, start) {
|
||||
var sps = sawCollapsedSpans && line.markedSpans, found;
|
||||
if (sps) for (var sp, i = 0; i < sps.length; ++i) {
|
||||
sp = sps[i];
|
||||
if (!sp.marker.collapsed) continue;
|
||||
if ((sp.from == null || sp.from < ch) &&
|
||||
(sp.to == null || sp.to > ch) &&
|
||||
(!found || found.width < sp.marker.width))
|
||||
if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
|
||||
(!found || compareCollapsedMarkers(found, sp.marker) < 0))
|
||||
found = sp.marker;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); }
|
||||
function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); }
|
||||
function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); }
|
||||
function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false); }
|
||||
|
||||
function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
|
||||
var line = getLine(doc, lineNo);
|
||||
var sps = sawCollapsedSpans && line.markedSpans;
|
||||
if (sps) for (var i = 0; i < sps.length; ++i) {
|
||||
var sp = sps[i];
|
||||
if (!sp.marker.collapsed) continue;
|
||||
var found = sp.marker.find(true);
|
||||
var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
|
||||
var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
|
||||
if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue;
|
||||
if (fromCmp <= 0 && (cmp(found.to, from) || extraRight(sp.marker) - extraLeft(marker)) > 0 ||
|
||||
fromCmp >= 0 && (cmp(found.from, to) || extraLeft(sp.marker) - extraRight(marker)) < 0)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function visualLine(doc, line) {
|
||||
var merged;
|
||||
@ -4171,6 +4268,7 @@ window.CodeMirror = (function() {
|
||||
for (var sp, i = 0; i < line.markedSpans.length; ++i) {
|
||||
sp = line.markedSpans[i];
|
||||
if (sp.marker.collapsed && !sp.marker.replacedWith && sp.from == span.to &&
|
||||
(sp.to == null || sp.to != span.from) &&
|
||||
(sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
|
||||
lineIsHiddenInner(doc, line, sp)) return true;
|
||||
}
|
||||
@ -4300,6 +4398,10 @@ window.CodeMirror = (function() {
|
||||
} else {
|
||||
style = mode.token(stream, state);
|
||||
}
|
||||
if (cm.options.addModeClass) {
|
||||
var mName = CodeMirror.innerMode(mode, state).mode.name;
|
||||
if (mName) style = "m-" + (style ? mName + " " + style : mName);
|
||||
}
|
||||
if (!flattenSpans || curStyle != style) {
|
||||
if (curStart < stream.start) f(stream.start, curStyle);
|
||||
curStart = stream.start; curStyle = style;
|
||||
@ -4371,7 +4473,7 @@ window.CodeMirror = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
var styleToClassCache = {};
|
||||
var styleToClassCache = {}, styleToClassCacheWithMode = {};
|
||||
function interpretTokenStyle(style, builder) {
|
||||
if (!style) return null;
|
||||
for (;;) {
|
||||
@ -4384,8 +4486,9 @@ window.CodeMirror = (function() {
|
||||
else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(builder[prop]))
|
||||
builder[prop] += " " + lineClass[2];
|
||||
}
|
||||
return styleToClassCache[style] ||
|
||||
(styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-"));
|
||||
var cache = builder.cm.options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
|
||||
return cache[style] ||
|
||||
(cache[style] = "cm-" + style.replace(/ +/g, " cm-"));
|
||||
}
|
||||
|
||||
function buildLineContent(cm, realLine, measure, copyWidgets) {
|
||||
@ -4402,7 +4505,7 @@ window.CodeMirror = (function() {
|
||||
builder.measure = line == realLine && measure;
|
||||
builder.pos = 0;
|
||||
builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
|
||||
if ((ie || webkit) && cm.getOption("lineWrapping"))
|
||||
if ((old_ie || webkit) && cm.getOption("lineWrapping"))
|
||||
builder.addToken = buildTokenSplitSpaces(builder.addToken);
|
||||
var next = insertLineContent(line, builder, getLineStyles(cm, line));
|
||||
if (measure && line == realLine && !builder.measuredSomething) {
|
||||
@ -4421,7 +4524,7 @@ window.CodeMirror = (function() {
|
||||
// Work around problem with the reported dimensions of single-char
|
||||
// direction spans on IE (issue #1129). See also the comment in
|
||||
// cursorCoords.
|
||||
if (measure && (ie || ie_gt10) && (order = getOrder(line))) {
|
||||
if (measure && ie && (order = getOrder(line))) {
|
||||
var l = order.length - 1;
|
||||
if (order[l].from == order[l].to) --l;
|
||||
var last = order[l], prev = order[l - 1];
|
||||
@ -4488,13 +4591,12 @@ window.CodeMirror = (function() {
|
||||
function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
|
||||
var wrapping = builder.cm.options.lineWrapping;
|
||||
for (var i = 0; i < text.length; ++i) {
|
||||
var ch = text.charAt(i), start = i == 0;
|
||||
if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) {
|
||||
ch = text.slice(i, i + 2);
|
||||
++i;
|
||||
} else if (i && wrapping && spanAffectsWrapping(text, i)) {
|
||||
var start = i == 0, to = i + 1;
|
||||
while (to < text.length && isExtendingChar(text.charAt(to))) ++to;
|
||||
var ch = text.slice(i, to);
|
||||
i = to - 1;
|
||||
if (i && wrapping && spanAffectsWrapping(text, i))
|
||||
builder.pre.appendChild(elt("wbr"));
|
||||
}
|
||||
var old = builder.measure[builder.pos];
|
||||
var span = builder.measure[builder.pos] =
|
||||
buildToken(builder, ch, style,
|
||||
@ -4503,7 +4605,7 @@ window.CodeMirror = (function() {
|
||||
// In IE single-space nodes wrap differently than spaces
|
||||
// embedded in larger text nodes, except when set to
|
||||
// white-space: normal (issue #1268).
|
||||
if (ie && wrapping && ch == " " && i && !/\s/.test(text.charAt(i - 1)) &&
|
||||
if (old_ie && wrapping && ch == " " && i && !/\s/.test(text.charAt(i - 1)) &&
|
||||
i < text.length - 1 && !/\s/.test(text.charAt(i + 1)))
|
||||
span.style.whiteSpace = "normal";
|
||||
builder.pos += ch.length;
|
||||
@ -4571,7 +4673,7 @@ window.CodeMirror = (function() {
|
||||
if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
|
||||
if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
|
||||
if (m.title && !title) title = m.title;
|
||||
if (m.collapsed && (!collapsed || collapsed.marker.size < m.size))
|
||||
if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
|
||||
collapsed = sp;
|
||||
} else if (sp.from > pos && nextChange > sp.from) {
|
||||
nextChange = sp.from;
|
||||
@ -4904,10 +5006,11 @@ window.CodeMirror = (function() {
|
||||
clearHistory: function() {this.history = makeHistory(this.history.maxGeneration);},
|
||||
|
||||
markClean: function() {
|
||||
this.cleanGeneration = this.changeGeneration();
|
||||
this.cleanGeneration = this.changeGeneration(true);
|
||||
},
|
||||
changeGeneration: function() {
|
||||
this.history.lastOp = this.history.lastOrigin = null;
|
||||
changeGeneration: function(forceSplit) {
|
||||
if (forceSplit)
|
||||
this.history.lastOp = this.history.lastOrigin = null;
|
||||
return this.history.generation;
|
||||
},
|
||||
isClean: function (gen) {
|
||||
@ -4929,7 +5032,8 @@ window.CodeMirror = (function() {
|
||||
},
|
||||
setBookmark: function(pos, options) {
|
||||
var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
|
||||
insertLeft: options && options.insertLeft};
|
||||
insertLeft: options && options.insertLeft,
|
||||
clearWhenEmpty: false};
|
||||
pos = clipPos(this, pos);
|
||||
return markText(this, pos, pos, realOpts, "bookmark");
|
||||
},
|
||||
@ -5210,10 +5314,10 @@ window.CodeMirror = (function() {
|
||||
anchorBefore: doc.sel.anchor, headBefore: doc.sel.head,
|
||||
anchorAfter: selAfter.anchor, headAfter: selAfter.head};
|
||||
hist.done.push(cur);
|
||||
hist.generation = ++hist.maxGeneration;
|
||||
while (hist.done.length > hist.undoDepth)
|
||||
hist.done.shift();
|
||||
}
|
||||
hist.generation = ++hist.maxGeneration;
|
||||
hist.lastTime = time;
|
||||
hist.lastOp = opId;
|
||||
hist.lastOrigin = change.origin;
|
||||
@ -5509,7 +5613,8 @@ window.CodeMirror = (function() {
|
||||
return true;
|
||||
}
|
||||
|
||||
var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\u1DC0–\u1DFF\u20D0–\u20FF\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff\uFE20–\uFE2F]/;
|
||||
var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;
|
||||
function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch); }
|
||||
|
||||
// DOM UTILITIES
|
||||
|
||||
@ -5582,7 +5687,7 @@ window.CodeMirror = (function() {
|
||||
if (/\w/.test(str.charAt(i - 2)) && /[^\-?\.]/.test(str.charAt(i))) return true;
|
||||
if (i > 2 && /[\d\.,]/.test(str.charAt(i - 2)) && /[\d\.,]/.test(str.charAt(i))) return false;
|
||||
}
|
||||
return /[~!#%&*)=+}\]\\|\"\.>,:;][({[<]|-[^\-?\.\u2010-\u201f\u2026]|\?[\w~`@#$%\^&*(_=+{[|><]|…[\w~`@#$%\^&*(_=+{[><]/.test(str.slice(i - 1, i + 1));
|
||||
return /[~!#%&*)=+}\]\\|\"\.>,:;][({[<]|-[^\-?\.\u2010-\u201f\u2026]|\?[\w~`@#$%\^&*(_=+{[|><]|\u2026[\w~`@#$%\^&*(_=+{[><]/.test(str.slice(i - 1, i + 1));
|
||||
};
|
||||
|
||||
var knownScrollbarWidth;
|
||||
@ -5650,14 +5755,14 @@ window.CodeMirror = (function() {
|
||||
var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
|
||||
19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
|
||||
36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
|
||||
46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
|
||||
186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
|
||||
221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
|
||||
63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
|
||||
46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", 107: "=", 109: "-", 127: "Delete",
|
||||
173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
|
||||
221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
|
||||
63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"};
|
||||
CodeMirror.keyNames = keyNames;
|
||||
(function() {
|
||||
// Number keys
|
||||
for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
|
||||
for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i);
|
||||
// Alphabetic keys
|
||||
for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
|
||||
// Function keys
|
||||
@ -5714,29 +5819,29 @@ window.CodeMirror = (function() {
|
||||
}
|
||||
var bidiOther;
|
||||
function getBidiPartAt(order, pos) {
|
||||
bidiOther = null;
|
||||
for (var i = 0, found; i < order.length; ++i) {
|
||||
var cur = order[i];
|
||||
if (cur.from < pos && cur.to > pos) { bidiOther = null; return i; }
|
||||
if (cur.from == pos || cur.to == pos) {
|
||||
if (cur.from < pos && cur.to > pos) return i;
|
||||
if ((cur.from == pos || cur.to == pos)) {
|
||||
if (found == null) {
|
||||
found = i;
|
||||
} else if (compareBidiLevel(order, cur.level, order[found].level)) {
|
||||
bidiOther = found;
|
||||
if (cur.from != cur.to) bidiOther = found;
|
||||
return i;
|
||||
} else {
|
||||
bidiOther = i;
|
||||
if (cur.from != cur.to) bidiOther = i;
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
bidiOther = null;
|
||||
return found;
|
||||
}
|
||||
|
||||
function moveInLine(line, pos, dir, byUnit) {
|
||||
if (!byUnit) return pos + dir;
|
||||
do pos += dir;
|
||||
while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
|
||||
while (pos > 0 && isExtendingChar(line.text.charAt(pos)));
|
||||
return pos;
|
||||
}
|
||||
|
||||
@ -5771,7 +5876,7 @@ window.CodeMirror = (function() {
|
||||
|
||||
function moveLogically(line, start, dir, byUnit) {
|
||||
var target = start + dir;
|
||||
if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(target))) target += dir;
|
||||
if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir;
|
||||
return target < 0 || target > line.text.length ? null : target;
|
||||
}
|
||||
|
||||
@ -5863,7 +5968,7 @@ window.CodeMirror = (function() {
|
||||
if (type == ",") types[i] = "N";
|
||||
else if (type == "%") {
|
||||
for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
|
||||
var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N";
|
||||
var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
|
||||
for (var j = i; j < end; ++j) types[j] = replace;
|
||||
i = end - 1;
|
||||
}
|
||||
@ -5888,7 +5993,7 @@ window.CodeMirror = (function() {
|
||||
if (isNeutral.test(types[i])) {
|
||||
for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
|
||||
var before = (i ? types[i-1] : outerType) == "L";
|
||||
var after = (end < len - 1 ? types[end] : outerType) == "L";
|
||||
var after = (end < len ? types[end] : outerType) == "L";
|
||||
var replace = before || after ? "L" : "R";
|
||||
for (var j = i; j < end; ++j) types[j] = replace;
|
||||
i = end - 1;
|
||||
@ -5938,7 +6043,7 @@ window.CodeMirror = (function() {
|
||||
|
||||
// THE END
|
||||
|
||||
CodeMirror.version = "3.20.0";
|
||||
CodeMirror.version = "3.21.0";
|
||||
|
||||
return CodeMirror;
|
||||
})();
|
||||
|
@ -96,8 +96,9 @@
|
||||
for (var i = start; i <= end; ++i) {
|
||||
var line = self.getLine(i);
|
||||
var found = line.indexOf(lineString);
|
||||
if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1;
|
||||
if (found == -1 && (i != end || i == start) && nonWS.test(line)) break lineComment;
|
||||
if (i != start && found > -1 && nonWS.test(line.slice(0, found))) break lineComment;
|
||||
if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment;
|
||||
lines.push(line);
|
||||
}
|
||||
self.operation(function() {
|
||||
@ -124,7 +125,10 @@
|
||||
endLine = self.getLine(--end);
|
||||
close = endLine.lastIndexOf(endString);
|
||||
}
|
||||
if (open == -1 || close == -1) return false;
|
||||
if (open == -1 || close == -1 ||
|
||||
!/comment/.test(self.getTokenTypeAt(Pos(start, open + 1))) ||
|
||||
!/comment/.test(self.getTokenTypeAt(Pos(end, close + 1))))
|
||||
return false;
|
||||
|
||||
self.operation(function() {
|
||||
self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)),
|
||||
|
@ -3,87 +3,80 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
|
||||
|
||||
if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
|
||||
|
||||
var indentUnit = config.indentUnit || config.tabSize || 2,
|
||||
hooks = parserConfig.hooks || {},
|
||||
atMediaTypes = parserConfig.atMediaTypes || {},
|
||||
atMediaFeatures = parserConfig.atMediaFeatures || {},
|
||||
var indentUnit = config.indentUnit,
|
||||
tokenHooks = parserConfig.tokenHooks,
|
||||
mediaTypes = parserConfig.mediaTypes || {},
|
||||
mediaFeatures = parserConfig.mediaFeatures || {},
|
||||
propertyKeywords = parserConfig.propertyKeywords || {},
|
||||
colorKeywords = parserConfig.colorKeywords || {},
|
||||
valueKeywords = parserConfig.valueKeywords || {},
|
||||
allowNested = !!parserConfig.allowNested,
|
||||
type = null;
|
||||
fontProperties = parserConfig.fontProperties || {},
|
||||
allowNested = parserConfig.allowNested;
|
||||
|
||||
var type, override;
|
||||
function ret(style, tp) { type = tp; return style; }
|
||||
|
||||
// Tokenizers
|
||||
|
||||
function tokenBase(stream, state) {
|
||||
var ch = stream.next();
|
||||
if (hooks[ch]) {
|
||||
// result[0] is style and result[1] is type
|
||||
var result = hooks[ch](stream, state);
|
||||
if (tokenHooks[ch]) {
|
||||
var result = tokenHooks[ch](stream, state);
|
||||
if (result !== false) return result;
|
||||
}
|
||||
if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());}
|
||||
else if (ch == "=") ret(null, "compare");
|
||||
else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare");
|
||||
else if (ch == "\"" || ch == "'") {
|
||||
if (ch == "@") {
|
||||
stream.eatWhile(/[\w\\\-]/);
|
||||
return ret("def", stream.current());
|
||||
} else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
|
||||
return ret(null, "compare");
|
||||
} else if (ch == "\"" || ch == "'") {
|
||||
state.tokenize = tokenString(ch);
|
||||
return state.tokenize(stream, state);
|
||||
}
|
||||
else if (ch == "#") {
|
||||
} else if (ch == "#") {
|
||||
stream.eatWhile(/[\w\\\-]/);
|
||||
return ret("atom", "hash");
|
||||
}
|
||||
else if (ch == "!") {
|
||||
} else if (ch == "!") {
|
||||
stream.match(/^\s*\w*/);
|
||||
return ret("keyword", "important");
|
||||
}
|
||||
else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
|
||||
} else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
|
||||
stream.eatWhile(/[\w.%]/);
|
||||
return ret("number", "unit");
|
||||
}
|
||||
else if (ch === "-") {
|
||||
if (/\d/.test(stream.peek())) {
|
||||
} else if (ch === "-") {
|
||||
if (/[\d.]/.test(stream.peek())) {
|
||||
stream.eatWhile(/[\w.%]/);
|
||||
return ret("number", "unit");
|
||||
} else if (stream.match(/^[^-]+-/)) {
|
||||
return ret("meta", "meta");
|
||||
}
|
||||
}
|
||||
else if (/[,+>*\/]/.test(ch)) {
|
||||
} else if (/[,+>*\/]/.test(ch)) {
|
||||
return ret(null, "select-op");
|
||||
}
|
||||
else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
|
||||
} else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
|
||||
return ret("qualifier", "qualifier");
|
||||
}
|
||||
else if (ch == ":") {
|
||||
return ret("operator", ch);
|
||||
}
|
||||
else if (/[;{}\[\]\(\)]/.test(ch)) {
|
||||
} else if (/[:;{}\[\]\(\)]/.test(ch)) {
|
||||
return ret(null, ch);
|
||||
}
|
||||
else if (ch == "u" && stream.match("rl(")) {
|
||||
} else if (ch == "u" && stream.match("rl(")) {
|
||||
stream.backUp(1);
|
||||
state.tokenize = tokenParenthesized;
|
||||
return ret("property", "variable");
|
||||
}
|
||||
else {
|
||||
return ret("property", "word");
|
||||
} else if (/[\w\\\-]/.test(ch)) {
|
||||
stream.eatWhile(/[\w\\\-]/);
|
||||
return ret("property", "variable");
|
||||
return ret("property", "word");
|
||||
} else {
|
||||
return ret(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
function tokenString(quote, nonInclusive) {
|
||||
function tokenString(quote) {
|
||||
return function(stream, state) {
|
||||
var escaped = false, ch;
|
||||
while ((ch = stream.next()) != null) {
|
||||
if (ch == quote && !escaped)
|
||||
if (ch == quote && !escaped) {
|
||||
if (quote == ")") stream.backUp(1);
|
||||
break;
|
||||
}
|
||||
escaped = !escaped && ch == "\\";
|
||||
}
|
||||
if (!escaped) {
|
||||
if (nonInclusive) stream.backUp(1);
|
||||
state.tokenize = tokenBase;
|
||||
}
|
||||
if (ch == quote || !escaped && quote != ")") state.tokenize = null;
|
||||
return ret("string", "string");
|
||||
};
|
||||
}
|
||||
@ -91,218 +84,238 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
|
||||
function tokenParenthesized(stream, state) {
|
||||
stream.next(); // Must be '('
|
||||
if (!stream.match(/\s*[\"\']/, false))
|
||||
state.tokenize = tokenString(")", true);
|
||||
state.tokenize = tokenString(")");
|
||||
else
|
||||
state.tokenize = tokenBase;
|
||||
state.tokenize = null;
|
||||
return ret(null, "(");
|
||||
}
|
||||
|
||||
// Context management
|
||||
|
||||
function Context(type, indent, prev) {
|
||||
this.type = type;
|
||||
this.indent = indent;
|
||||
this.prev = prev;
|
||||
}
|
||||
|
||||
function pushContext(state, stream, type) {
|
||||
state.context = new Context(type, stream.indentation() + indentUnit, state.context);
|
||||
return type;
|
||||
}
|
||||
|
||||
function popContext(state) {
|
||||
state.context = state.context.prev;
|
||||
return state.context.type;
|
||||
}
|
||||
|
||||
function pass(type, stream, state) {
|
||||
return states[state.context.type](type, stream, state);
|
||||
}
|
||||
function popAndPass(type, stream, state, n) {
|
||||
for (var i = n || 1; i > 0; i--)
|
||||
state.context = state.context.prev;
|
||||
return pass(type, stream, state);
|
||||
}
|
||||
|
||||
// Parser
|
||||
|
||||
function wordAsValue(stream) {
|
||||
var word = stream.current().toLowerCase();
|
||||
if (valueKeywords.hasOwnProperty(word))
|
||||
override = "atom";
|
||||
else if (colorKeywords.hasOwnProperty(word))
|
||||
override = "keyword";
|
||||
else
|
||||
override = "variable";
|
||||
}
|
||||
|
||||
var states = {};
|
||||
|
||||
states.top = function(type, stream, state) {
|
||||
if (type == "{") {
|
||||
return pushContext(state, stream, "block");
|
||||
} else if (type == "}" && state.context.prev) {
|
||||
return popContext(state);
|
||||
} else if (type == "@media") {
|
||||
return pushContext(state, stream, "media");
|
||||
} else if (type == "@font-face") {
|
||||
return "font_face_before";
|
||||
} else if (type && type.charAt(0) == "@") {
|
||||
return pushContext(state, stream, "at");
|
||||
} else if (type == "hash") {
|
||||
override = "builtin";
|
||||
} else if (type == "word") {
|
||||
override = "tag";
|
||||
} else if (type == "variable-definition") {
|
||||
return "maybeprop";
|
||||
} else if (type == "interpolation") {
|
||||
return pushContext(state, stream, "interpolation");
|
||||
} else if (type == ":") {
|
||||
return "pseudo";
|
||||
} else if (allowNested && type == "(") {
|
||||
return pushContext(state, stream, "params");
|
||||
}
|
||||
return state.context.type;
|
||||
};
|
||||
|
||||
states.block = function(type, stream, state) {
|
||||
if (type == "word") {
|
||||
if (propertyKeywords.hasOwnProperty(stream.current().toLowerCase())) {
|
||||
override = "property";
|
||||
return "maybeprop";
|
||||
} else if (allowNested) {
|
||||
override = stream.match(/^\s*:/, false) ? "property" : "tag";
|
||||
return "block";
|
||||
} else {
|
||||
override += " error";
|
||||
return "maybeprop";
|
||||
}
|
||||
} else if (type == "meta") {
|
||||
return "block";
|
||||
} else if (!allowNested && (type == "hash" || type == "qualifier")) {
|
||||
override = "error";
|
||||
return "block";
|
||||
} else {
|
||||
return states.top(type, stream, state);
|
||||
}
|
||||
};
|
||||
|
||||
states.maybeprop = function(type, stream, state) {
|
||||
if (type == ":") return pushContext(state, stream, "prop");
|
||||
return pass(type, stream, state);
|
||||
};
|
||||
|
||||
states.prop = function(type, stream, state) {
|
||||
if (type == ";") return popContext(state);
|
||||
if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
|
||||
if (type == "}" || type == "{") return popAndPass(type, stream, state);
|
||||
if (type == "(") return pushContext(state, stream, "parens");
|
||||
|
||||
if (type == "hash" && !/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) {
|
||||
override += " error";
|
||||
} else if (type == "word") {
|
||||
wordAsValue(stream);
|
||||
} else if (type == "interpolation") {
|
||||
return pushContext(state, stream, "interpolation");
|
||||
}
|
||||
return "prop";
|
||||
};
|
||||
|
||||
states.propBlock = function(type, _stream, state) {
|
||||
if (type == "}") return popContext(state);
|
||||
if (type == "word") { override = "property"; return "maybeprop"; }
|
||||
return state.context.type;
|
||||
};
|
||||
|
||||
states.parens = function(type, stream, state) {
|
||||
if (type == "{" || type == "}") return popAndPass(type, stream, state);
|
||||
if (type == ")") return popContext(state);
|
||||
return "parens";
|
||||
};
|
||||
|
||||
states.pseudo = function(type, stream, state) {
|
||||
if (type == "word") {
|
||||
override = "variable-3";
|
||||
return state.context.type;
|
||||
}
|
||||
return pass(type, stream, state);
|
||||
};
|
||||
|
||||
states.media = function(type, stream, state) {
|
||||
if (type == "(") return pushContext(state, stream, "media_parens");
|
||||
if (type == "}") return popAndPass(type, stream, state);
|
||||
if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
|
||||
|
||||
if (type == "word") {
|
||||
var word = stream.current().toLowerCase();
|
||||
if (word == "only" || word == "not" || word == "and")
|
||||
override = "keyword";
|
||||
else if (mediaTypes.hasOwnProperty(word))
|
||||
override = "attribute";
|
||||
else if (mediaFeatures.hasOwnProperty(word))
|
||||
override = "property";
|
||||
else
|
||||
override = "error";
|
||||
}
|
||||
return state.context.type;
|
||||
};
|
||||
|
||||
states.media_parens = function(type, stream, state) {
|
||||
if (type == ")") return popContext(state);
|
||||
if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
|
||||
return states.media(type, stream, state);
|
||||
};
|
||||
|
||||
states.font_face_before = function(type, stream, state) {
|
||||
if (type == "{")
|
||||
return pushContext(state, stream, "font_face");
|
||||
return pass(type, stream, state);
|
||||
};
|
||||
|
||||
states.font_face = function(type, stream, state) {
|
||||
if (type == "}") return popContext(state);
|
||||
if (type == "word") {
|
||||
if (!fontProperties.hasOwnProperty(stream.current().toLowerCase()))
|
||||
override = "error";
|
||||
else
|
||||
override = "property";
|
||||
return "maybeprop";
|
||||
}
|
||||
return "font_face";
|
||||
};
|
||||
|
||||
states.at = function(type, stream, state) {
|
||||
if (type == ";") return popContext(state);
|
||||
if (type == "{" || type == "}") return popAndPass(type, stream, state);
|
||||
if (type == "word") override = "tag";
|
||||
else if (type == "hash") override = "builtin";
|
||||
return "at";
|
||||
};
|
||||
|
||||
states.interpolation = function(type, stream, state) {
|
||||
if (type == "}") return popContext(state);
|
||||
if (type == "{" || type == ";") return popAndPass(type, stream, state);
|
||||
if (type != "variable") override = "error";
|
||||
return "interpolation";
|
||||
};
|
||||
|
||||
states.params = function(type, stream, state) {
|
||||
if (type == ")") return popContext(state);
|
||||
if (type == "{" || type == "}") return popAndPass(type, stream, state);
|
||||
if (type == "word") wordAsValue(stream);
|
||||
return "params";
|
||||
};
|
||||
|
||||
return {
|
||||
startState: function(base) {
|
||||
return {tokenize: tokenBase,
|
||||
baseIndent: base || 0,
|
||||
stack: [],
|
||||
lastToken: null};
|
||||
return {tokenize: null,
|
||||
state: "top",
|
||||
context: new Context("top", base || 0, null)};
|
||||
},
|
||||
|
||||
token: function(stream, state) {
|
||||
|
||||
// Use these terms when applicable (see http://www.xanthir.com/blog/b4E50)
|
||||
//
|
||||
// rule** or **ruleset:
|
||||
// A selector + braces combo, or an at-rule.
|
||||
//
|
||||
// declaration block:
|
||||
// A sequence of declarations.
|
||||
//
|
||||
// declaration:
|
||||
// A property + colon + value combo.
|
||||
//
|
||||
// property value:
|
||||
// The entire value of a property.
|
||||
//
|
||||
// component value:
|
||||
// A single piece of a property value. Like the 5px in
|
||||
// text-shadow: 0 0 5px blue;. Can also refer to things that are
|
||||
// multiple terms, like the 1-4 terms that make up the background-size
|
||||
// portion of the background shorthand.
|
||||
//
|
||||
// term:
|
||||
// The basic unit of author-facing CSS, like a single number (5),
|
||||
// dimension (5px), string ("foo"), or function. Officially defined
|
||||
// by the CSS 2.1 grammar (look for the 'term' production)
|
||||
//
|
||||
//
|
||||
// simple selector:
|
||||
// A single atomic selector, like a type selector, an attr selector, a
|
||||
// class selector, etc.
|
||||
//
|
||||
// compound selector:
|
||||
// One or more simple selectors without a combinator. div.example is
|
||||
// compound, div > .example is not.
|
||||
//
|
||||
// complex selector:
|
||||
// One or more compound selectors chained with combinators.
|
||||
//
|
||||
// combinator:
|
||||
// The parts of selectors that express relationships. There are four
|
||||
// currently - the space (descendant combinator), the greater-than
|
||||
// bracket (child combinator), the plus sign (next sibling combinator),
|
||||
// and the tilda (following sibling combinator).
|
||||
//
|
||||
// sequence of selectors:
|
||||
// One or more of the named type of selector chained with commas.
|
||||
|
||||
state.tokenize = state.tokenize || tokenBase;
|
||||
if (state.tokenize == tokenBase && stream.eatSpace()) return null;
|
||||
var style = state.tokenize(stream, state);
|
||||
if (style && typeof style != "string") style = ret(style[0], style[1]);
|
||||
|
||||
// Changing style returned based on context
|
||||
var context = state.stack[state.stack.length-1];
|
||||
if (style == "variable") {
|
||||
if (type == "variable-definition") state.stack.push("propertyValue");
|
||||
return state.lastToken = "variable-2";
|
||||
} else if (style == "property") {
|
||||
var word = stream.current().toLowerCase();
|
||||
if (context == "propertyValue") {
|
||||
if (valueKeywords.hasOwnProperty(word)) {
|
||||
style = "string-2";
|
||||
} else if (colorKeywords.hasOwnProperty(word)) {
|
||||
style = "keyword";
|
||||
} else {
|
||||
style = "variable-2";
|
||||
}
|
||||
} else if (context == "rule") {
|
||||
if (!propertyKeywords.hasOwnProperty(word)) {
|
||||
style += " error";
|
||||
}
|
||||
} else if (context == "block") {
|
||||
// if a value is present in both property, value, or color, the order
|
||||
// of preference is property -> color -> value
|
||||
if (propertyKeywords.hasOwnProperty(word)) {
|
||||
style = "property";
|
||||
} else if (colorKeywords.hasOwnProperty(word)) {
|
||||
style = "keyword";
|
||||
} else if (valueKeywords.hasOwnProperty(word)) {
|
||||
style = "string-2";
|
||||
} else {
|
||||
style = "tag";
|
||||
}
|
||||
} else if (!context || context == "@media{") {
|
||||
style = "tag";
|
||||
} else if (context == "@media") {
|
||||
if (atMediaTypes[stream.current()]) {
|
||||
style = "attribute"; // Known attribute
|
||||
} else if (/^(only|not)$/.test(word)) {
|
||||
style = "keyword";
|
||||
} else if (word == "and") {
|
||||
style = "error"; // "and" is only allowed in @mediaType
|
||||
} else if (atMediaFeatures.hasOwnProperty(word)) {
|
||||
style = "error"; // Known property, should be in @mediaType(
|
||||
} else {
|
||||
// Unknown, expecting keyword or attribute, assuming attribute
|
||||
style = "attribute error";
|
||||
}
|
||||
} else if (context == "@mediaType") {
|
||||
if (atMediaTypes.hasOwnProperty(word)) {
|
||||
style = "attribute";
|
||||
} else if (word == "and") {
|
||||
style = "operator";
|
||||
} else if (/^(only|not)$/.test(word)) {
|
||||
style = "error"; // Only allowed in @media
|
||||
} else {
|
||||
// Unknown attribute or property, but expecting property (preceded
|
||||
// by "and"). Should be in parentheses
|
||||
style = "error";
|
||||
}
|
||||
} else if (context == "@mediaType(") {
|
||||
if (propertyKeywords.hasOwnProperty(word)) {
|
||||
// do nothing, remains "property"
|
||||
} else if (atMediaTypes.hasOwnProperty(word)) {
|
||||
style = "error"; // Known property, should be in parentheses
|
||||
} else if (word == "and") {
|
||||
style = "operator";
|
||||
} else if (/^(only|not)$/.test(word)) {
|
||||
style = "error"; // Only allowed in @media
|
||||
} else {
|
||||
style += " error";
|
||||
}
|
||||
} else if (context == "@import") {
|
||||
style = "tag";
|
||||
} else {
|
||||
style = "error";
|
||||
}
|
||||
} else if (style == "atom") {
|
||||
if(!context || context == "@media{" || context == "block") {
|
||||
style = "builtin";
|
||||
} else if (context == "propertyValue") {
|
||||
if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) {
|
||||
style += " error";
|
||||
}
|
||||
} else {
|
||||
style = "error";
|
||||
}
|
||||
} else if (context == "@media" && type == "{") {
|
||||
style = "error";
|
||||
if (!state.tokenize && stream.eatSpace()) return null;
|
||||
var style = (state.tokenize || tokenBase)(stream, state);
|
||||
if (style && typeof style == "object") {
|
||||
type = style[1];
|
||||
style = style[0];
|
||||
}
|
||||
|
||||
// Push/pop context stack
|
||||
if (type == "{") {
|
||||
if (context == "@media" || context == "@mediaType") {
|
||||
state.stack[state.stack.length-1] = "@media{";
|
||||
}
|
||||
else {
|
||||
var newContext = allowNested ? "block" : "rule";
|
||||
state.stack.push(newContext);
|
||||
}
|
||||
}
|
||||
else if (type == "}") {
|
||||
if (context == "interpolation") style = "operator";
|
||||
// Pop off end of array until { is reached
|
||||
while(state.stack.length){
|
||||
var removed = state.stack.pop();
|
||||
if(removed.indexOf("{") > -1 || removed == "block" || removed == "rule"){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type == "interpolation") state.stack.push("interpolation");
|
||||
else if (type == "@media") state.stack.push("@media");
|
||||
else if (type == "@import") state.stack.push("@import");
|
||||
else if (context == "@media" && /\b(keyword|attribute)\b/.test(style))
|
||||
state.stack[state.stack.length-1] = "@mediaType";
|
||||
else if (context == "@mediaType" && stream.current() == ",")
|
||||
state.stack[state.stack.length-1] = "@media";
|
||||
else if (type == "(") {
|
||||
if (context == "@media" || context == "@mediaType") {
|
||||
// Make sure @mediaType is used to avoid error on {
|
||||
state.stack[state.stack.length-1] = "@mediaType";
|
||||
state.stack.push("@mediaType(");
|
||||
}
|
||||
else state.stack.push("(");
|
||||
}
|
||||
else if (type == ")") {
|
||||
// Pop off end of array until ( is reached
|
||||
while(state.stack.length){
|
||||
var removed = state.stack.pop();
|
||||
if(removed.indexOf("(") > -1){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type == ":" && state.lastToken == "property") state.stack.push("propertyValue");
|
||||
else if (context == "propertyValue" && type == ";") state.stack.pop();
|
||||
else if (context == "@import" && type == ";") state.stack.pop();
|
||||
|
||||
return state.lastToken = style;
|
||||
override = style;
|
||||
state.state = states[state.state](type, stream, state);
|
||||
return override;
|
||||
},
|
||||
|
||||
indent: function(state, textAfter) {
|
||||
var n = state.stack.length;
|
||||
if (/^\}/.test(textAfter))
|
||||
n -= state.stack[n-1] == "propertyValue" ? 2 : 1;
|
||||
return state.baseIndent + n * indentUnit;
|
||||
var cx = state.context, ch = textAfter && textAfter.charAt(0);
|
||||
var indent = cx.indent;
|
||||
if (cx.prev &&
|
||||
(ch == "}" && (cx.type == "block" || cx.type == "top" || cx.type == "interpolation" || cx.type == "font_face") ||
|
||||
ch == ")" && (cx.type == "parens" || cx.type == "params" || cx.type == "media_parens") ||
|
||||
ch == "{" && (cx.type == "at" || cx.type == "media"))) {
|
||||
indent = cx.indent - indentUnit;
|
||||
cx = cx.prev;
|
||||
}
|
||||
return indent;
|
||||
},
|
||||
|
||||
electricChars: "}",
|
||||
@ -321,12 +334,12 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
|
||||
return keys;
|
||||
}
|
||||
|
||||
var atMediaTypes = keySet([
|
||||
var mediaTypes_ = [
|
||||
"all", "aural", "braille", "handheld", "print", "projection", "screen",
|
||||
"tty", "tv", "embossed"
|
||||
]);
|
||||
], mediaTypes = keySet(mediaTypes_);
|
||||
|
||||
var atMediaFeatures = keySet([
|
||||
var mediaFeatures_ = [
|
||||
"width", "min-width", "max-width", "height", "min-height", "max-height",
|
||||
"device-width", "min-device-width", "max-device-width", "device-height",
|
||||
"min-device-height", "max-device-height", "aspect-ratio",
|
||||
@ -335,9 +348,9 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
|
||||
"max-color", "color-index", "min-color-index", "max-color-index",
|
||||
"monochrome", "min-monochrome", "max-monochrome", "resolution",
|
||||
"min-resolution", "max-resolution", "scan", "grid"
|
||||
]);
|
||||
], mediaFeatures = keySet(mediaFeatures_);
|
||||
|
||||
var propertyKeywords = keySet([
|
||||
var propertyKeywords_ = [
|
||||
"align-content", "align-items", "align-self", "alignment-adjust",
|
||||
"alignment-baseline", "anchor-point", "animation", "animation-delay",
|
||||
"animation-direction", "animation-duration", "animation-iteration-count",
|
||||
@ -425,9 +438,9 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
|
||||
"stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
|
||||
"baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
|
||||
"glyph-orientation-vertical", "kerning", "text-anchor", "writing-mode"
|
||||
]);
|
||||
], propertyKeywords = keySet(propertyKeywords_);
|
||||
|
||||
var colorKeywords = keySet([
|
||||
var colorKeywords_ = [
|
||||
"aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
|
||||
"bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
|
||||
"burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
|
||||
@ -454,9 +467,9 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
|
||||
"slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
|
||||
"teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
|
||||
"whitesmoke", "yellow", "yellowgreen"
|
||||
]);
|
||||
], colorKeywords = keySet(colorKeywords_);
|
||||
|
||||
var valueKeywords = keySet([
|
||||
var valueKeywords_ = [
|
||||
"above", "absolute", "activeborder", "activecaption", "afar",
|
||||
"after-white-space", "ahead", "alias", "all", "all-scroll", "alternate",
|
||||
"always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
|
||||
@ -539,7 +552,15 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
|
||||
"visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
|
||||
"window", "windowframe", "windowtext", "x-large", "x-small", "xor",
|
||||
"xx-large", "xx-small"
|
||||
]);
|
||||
], valueKeywords = keySet(valueKeywords_);
|
||||
|
||||
var fontProperties_ = [
|
||||
"font-family", "src", "unicode-range", "font-variant", "font-feature-settings",
|
||||
"font-stretch", "font-weight", "font-style"
|
||||
], fontProperties = keySet(fontProperties_);
|
||||
|
||||
var allWords = mediaTypes_.concat(mediaFeatures_).concat(propertyKeywords_).concat(colorKeywords_).concat(valueKeywords_);
|
||||
CodeMirror.registerHelper("hintWords", "css", allWords);
|
||||
|
||||
function tokenCComment(stream, state) {
|
||||
var maybeEnd = false, ch;
|
||||
@ -553,67 +574,47 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
|
||||
return ["comment", "comment"];
|
||||
}
|
||||
|
||||
function tokenSGMLComment(stream, state) {
|
||||
if (stream.skipTo("-->")) {
|
||||
stream.match("-->");
|
||||
state.tokenize = null;
|
||||
} else {
|
||||
stream.skipToEnd();
|
||||
}
|
||||
return ["comment", "comment"];
|
||||
}
|
||||
|
||||
CodeMirror.defineMIME("text/css", {
|
||||
atMediaTypes: atMediaTypes,
|
||||
atMediaFeatures: atMediaFeatures,
|
||||
mediaTypes: mediaTypes,
|
||||
mediaFeatures: mediaFeatures,
|
||||
propertyKeywords: propertyKeywords,
|
||||
colorKeywords: colorKeywords,
|
||||
valueKeywords: valueKeywords,
|
||||
hooks: {
|
||||
fontProperties: fontProperties,
|
||||
tokenHooks: {
|
||||
"<": function(stream, state) {
|
||||
function tokenSGMLComment(stream, state) {
|
||||
var dashes = 0, ch;
|
||||
while ((ch = stream.next()) != null) {
|
||||
if (dashes >= 2 && ch == ">") {
|
||||
state.tokenize = null;
|
||||
break;
|
||||
}
|
||||
dashes = (ch == "-") ? dashes + 1 : 0;
|
||||
}
|
||||
return ["comment", "comment"];
|
||||
}
|
||||
if (stream.eat("!")) {
|
||||
state.tokenize = tokenSGMLComment;
|
||||
return tokenSGMLComment(stream, state);
|
||||
}
|
||||
if (!stream.match("!--")) return false;
|
||||
state.tokenize = tokenSGMLComment;
|
||||
return tokenSGMLComment(stream, state);
|
||||
},
|
||||
"/": function(stream, state) {
|
||||
if (stream.eat("*")) {
|
||||
state.tokenize = tokenCComment;
|
||||
return tokenCComment(stream, state);
|
||||
}
|
||||
return false;
|
||||
if (!stream.eat("*")) return false;
|
||||
state.tokenize = tokenCComment;
|
||||
return tokenCComment(stream, state);
|
||||
}
|
||||
},
|
||||
name: "css"
|
||||
});
|
||||
|
||||
CodeMirror.defineMIME("text/x-scss", {
|
||||
atMediaTypes: atMediaTypes,
|
||||
atMediaFeatures: atMediaFeatures,
|
||||
mediaTypes: mediaTypes,
|
||||
mediaFeatures: mediaFeatures,
|
||||
propertyKeywords: propertyKeywords,
|
||||
colorKeywords: colorKeywords,
|
||||
valueKeywords: valueKeywords,
|
||||
fontProperties: fontProperties,
|
||||
allowNested: true,
|
||||
hooks: {
|
||||
":": function(stream) {
|
||||
if (stream.match(/\s*{/)) {
|
||||
return [null, "{"];
|
||||
}
|
||||
return false;
|
||||
},
|
||||
"$": function(stream) {
|
||||
stream.match(/^[\w-]+/);
|
||||
if (stream.peek() == ":") {
|
||||
return ["variable", "variable-definition"];
|
||||
}
|
||||
return ["variable", "variable"];
|
||||
},
|
||||
",": function(stream, state) {
|
||||
if (state.stack[state.stack.length - 1] == "propertyValue" && stream.match(/^ *\$/, false)) {
|
||||
return ["operator", ";"];
|
||||
}
|
||||
},
|
||||
tokenHooks: {
|
||||
"/": function(stream, state) {
|
||||
if (stream.eat("/")) {
|
||||
stream.skipToEnd();
|
||||
@ -625,15 +626,58 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
|
||||
return ["operator", "operator"];
|
||||
}
|
||||
},
|
||||
":": function(stream) {
|
||||
if (stream.match(/\s*{/))
|
||||
return [null, "{"];
|
||||
return false;
|
||||
},
|
||||
"$": function(stream) {
|
||||
stream.match(/^[\w-]+/);
|
||||
if (stream.match(/^\s*:/, false))
|
||||
return ["variable-2", "variable-definition"];
|
||||
return ["variable-2", "variable"];
|
||||
},
|
||||
"#": function(stream) {
|
||||
if (stream.eat("{")) {
|
||||
return ["operator", "interpolation"];
|
||||
} else {
|
||||
stream.eatWhile(/[\w\\\-]/);
|
||||
return ["atom", "hash"];
|
||||
}
|
||||
if (!stream.eat("{")) return false;
|
||||
return [null, "interpolation"];
|
||||
}
|
||||
},
|
||||
name: "css"
|
||||
name: "css",
|
||||
helperType: "scss"
|
||||
});
|
||||
|
||||
CodeMirror.defineMIME("text/x-less", {
|
||||
mediaTypes: mediaTypes,
|
||||
mediaFeatures: mediaFeatures,
|
||||
propertyKeywords: propertyKeywords,
|
||||
colorKeywords: colorKeywords,
|
||||
valueKeywords: valueKeywords,
|
||||
fontProperties: fontProperties,
|
||||
allowNested: true,
|
||||
tokenHooks: {
|
||||
"/": function(stream, state) {
|
||||
if (stream.eat("/")) {
|
||||
stream.skipToEnd();
|
||||
return ["comment", "comment"];
|
||||
} else if (stream.eat("*")) {
|
||||
state.tokenize = tokenCComment;
|
||||
return tokenCComment(stream, state);
|
||||
} else {
|
||||
return ["operator", "operator"];
|
||||
}
|
||||
},
|
||||
"@": function(stream) {
|
||||
if (stream.match(/^(charset|document|font-face|import|keyframes|media|namespace|page|supports)\b/, false)) return false;
|
||||
stream.eatWhile(/[\w\\\-]/);
|
||||
if (stream.match(/^\s*:/, false))
|
||||
return ["variable-2", "variable-definition"];
|
||||
return ["variable-2", "variable"];
|
||||
},
|
||||
"&": function() {
|
||||
return ["atom", "atom"];
|
||||
}
|
||||
},
|
||||
name: "css",
|
||||
helperType: "less"
|
||||
});
|
||||
})();
|
||||
|
@ -35,6 +35,7 @@
|
||||
}
|
||||
var inp = dialog.getElementsByTagName("input")[0], button;
|
||||
if (inp) {
|
||||
if (options && options.value) inp.value = options.value;
|
||||
CodeMirror.on(inp, "keydown", function(e) {
|
||||
if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }
|
||||
if (e.keyCode == 13 || e.keyCode == 27) {
|
||||
|
@ -1,4 +1,6 @@
|
||||
CodeMirror.registerHelper("fold", "comment", function(cm, start) {
|
||||
CodeMirror.registerGlobalHelper("fold", "comment", function(mode) {
|
||||
return mode.blockCommentStart && mode.blockCommentEnd;
|
||||
}, function(cm, start) {
|
||||
var mode = cm.getModeAt(start), startToken = mode.blockCommentStart, endToken = mode.blockCommentEnd;
|
||||
if (!startToken || !endToken) return;
|
||||
var line = start.line, lineText = cm.getLine(line);
|
||||
|
@ -3,8 +3,7 @@
|
||||
|
||||
function doFold(cm, pos, options, force) {
|
||||
var finder = options && (options.call ? options : options.rangeFinder);
|
||||
if (!finder) finder = cm.getHelper(pos, "fold");
|
||||
if (!finder) return;
|
||||
if (!finder) finder = CodeMirror.fold.auto;
|
||||
if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0);
|
||||
var minSize = options && options.minFoldSize || 0;
|
||||
|
||||
@ -63,6 +62,10 @@
|
||||
doFold(this, pos, options, force);
|
||||
});
|
||||
|
||||
CodeMirror.commands.fold = function(cm) {
|
||||
cm.foldCode(cm.getCursor());
|
||||
};
|
||||
|
||||
CodeMirror.registerHelper("fold", "combine", function() {
|
||||
var funcs = Array.prototype.slice.call(arguments, 0);
|
||||
return function(cm, start) {
|
||||
@ -72,4 +75,12 @@
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
CodeMirror.registerHelper("fold", "auto", function(cm, start) {
|
||||
var helpers = cm.getHelpers(start, "fold");
|
||||
for (var i = 0; i < helpers.length; i++) {
|
||||
var cur = helpers[i](cm, start);
|
||||
if (cur) return cur;
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
@ -62,7 +62,7 @@
|
||||
if (isFolded(cm, cur)) {
|
||||
mark = marker(opts.indicatorFolded);
|
||||
} else {
|
||||
var pos = Pos(cur, 0), func = opts.rangeFinder || cm.getHelper(pos, "fold");
|
||||
var pos = Pos(cur, 0), func = opts.rangeFinder || CodeMirror.fold.auto;
|
||||
var range = func && func(cm, pos);
|
||||
if (range && range.from.line + 1 < range.to.line)
|
||||
mark = marker(opts.indicatorOpen);
|
||||
|
@ -164,4 +164,10 @@
|
||||
if (close) return {open: open, close: close};
|
||||
}
|
||||
};
|
||||
|
||||
// Used by addon/edit/closetag.js
|
||||
CodeMirror.scanForClosingTag = function(cm, pos, name, end) {
|
||||
var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null);
|
||||
return !!findMatchingClose(iter, name);
|
||||
};
|
||||
})();
|
||||
|
@ -93,8 +93,6 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
|
||||
return CodeMirror.Pass;
|
||||
},
|
||||
|
||||
electricChars: "/{}:",
|
||||
|
||||
innerMode: function(state) {
|
||||
return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
|
||||
var jsKeywords = {
|
||||
"if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
|
||||
"return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
|
||||
"return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C, "debugger": C,
|
||||
"var": kw("var"), "const": kw("var"), "let": kw("var"),
|
||||
"function": kw("function"), "catch": kw("catch"),
|
||||
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
|
||||
@ -54,14 +54,16 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
|
||||
var isOperatorChar = /[+\-*&%=<>!?|~^]/;
|
||||
|
||||
function nextUntilUnescaped(stream, end) {
|
||||
var escaped = false, next;
|
||||
function readRegexp(stream) {
|
||||
var escaped = false, next, inSet = false;
|
||||
while ((next = stream.next()) != null) {
|
||||
if (next == end && !escaped)
|
||||
return false;
|
||||
if (!escaped) {
|
||||
if (next == "/" && !inSet) return;
|
||||
if (next == "[") inSet = true;
|
||||
else if (inSet && next == "]") inSet = false;
|
||||
}
|
||||
escaped = !escaped && next == "\\";
|
||||
}
|
||||
return escaped;
|
||||
}
|
||||
|
||||
// Used as scratch variables to communicate multiple values without
|
||||
@ -83,7 +85,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
} else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
|
||||
return ret(ch);
|
||||
} else if (ch == "=" && stream.eat(">")) {
|
||||
return ret("=>");
|
||||
return ret("=>", "operator");
|
||||
} else if (ch == "0" && stream.eat(/x/i)) {
|
||||
stream.eatWhile(/[\da-f]/i);
|
||||
return ret("number", "number");
|
||||
@ -99,12 +101,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
return ret("comment", "comment");
|
||||
} else if (state.lastType == "operator" || state.lastType == "keyword c" ||
|
||||
state.lastType == "sof" || /^[\[{}\(,;:]$/.test(state.lastType)) {
|
||||
nextUntilUnescaped(stream, "/");
|
||||
readRegexp(stream);
|
||||
stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
|
||||
return ret("regexp", "string-2");
|
||||
} else {
|
||||
stream.eatWhile(isOperatorChar);
|
||||
return ret("operator", null, stream.current());
|
||||
return ret("operator", "operator", stream.current());
|
||||
}
|
||||
} else if (ch == "`") {
|
||||
state.tokenize = tokenQuasi;
|
||||
@ -114,7 +116,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
return ret("error", "error");
|
||||
} else if (isOperatorChar.test(ch)) {
|
||||
stream.eatWhile(isOperatorChar);
|
||||
return ret("operator", null, stream.current());
|
||||
return ret("operator", "operator", stream.current());
|
||||
} else {
|
||||
stream.eatWhile(/[\w\$_]/);
|
||||
var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
|
||||
@ -125,8 +127,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
|
||||
function tokenString(quote) {
|
||||
return function(stream, state) {
|
||||
if (!nextUntilUnescaped(stream, quote))
|
||||
state.tokenize = tokenBase;
|
||||
var escaped = false, next;
|
||||
while ((next = stream.next()) != null) {
|
||||
if (next == quote && !escaped) break;
|
||||
escaped = !escaped && next == "\\";
|
||||
}
|
||||
if (!escaped) state.tokenize = tokenBase;
|
||||
return ret("string", "string");
|
||||
};
|
||||
}
|
||||
@ -304,7 +310,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
if (type == ";") return cont();
|
||||
if (type == "if") return cont(pushlex("form"), expression, statement, poplex, maybeelse);
|
||||
if (type == "function") return cont(functiondef);
|
||||
if (type == "for") return cont(pushlex("form"), forspec, poplex, statement, poplex);
|
||||
if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
|
||||
if (type == "variable") return cont(pushlex("stat"), maybelabel);
|
||||
if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
|
||||
block, poplex, poplex);
|
||||
@ -327,7 +333,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
function expressionInner(type, noComma) {
|
||||
if (cx.state.fatArrowAt == cx.stream.start) {
|
||||
var body = noComma ? arrowBodyNoComma : arrowBody;
|
||||
if (type == "(") return cont(pushcontext, commasep(pattern, ")"), expect("=>"), body, popcontext);
|
||||
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext);
|
||||
else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
|
||||
}
|
||||
|
||||
@ -337,8 +343,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
|
||||
if (type == "(") return cont(pushlex(")"), maybeexpression, comprehension, expect(")"), poplex, maybeop);
|
||||
if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
|
||||
if (type == "[") return cont(pushlex("]"), expressionNoComma, maybeArrayComprehension, poplex, maybeop);
|
||||
if (type == "{") return cont(commasep(objprop, "}"), maybeop);
|
||||
if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
|
||||
if (type == "{") return contCommasep(objprop, "}", null, maybeop);
|
||||
return cont();
|
||||
}
|
||||
function maybeexpression(type) {
|
||||
@ -365,12 +371,11 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
}
|
||||
if (type == "quasi") { cx.cc.push(me); return quasi(value); }
|
||||
if (type == ";") return;
|
||||
if (type == "(") return cont(commasep(expressionNoComma, ")", "call"), me);
|
||||
if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
|
||||
if (type == ".") return cont(property, me);
|
||||
if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
|
||||
}
|
||||
function quasi(value) {
|
||||
if (!value) debugger;
|
||||
if (value.slice(value.length - 2) != "${") return cont();
|
||||
return cont(expression, continueQuasi);
|
||||
}
|
||||
@ -418,7 +423,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
if (type == ":") return cont(expressionNoComma);
|
||||
if (type == "(") return pass(functiondef);
|
||||
}
|
||||
function commasep(what, end, info) {
|
||||
function commasep(what, end) {
|
||||
function proceed(type) {
|
||||
if (type == ",") {
|
||||
var lex = cx.state.lexical;
|
||||
@ -430,10 +435,14 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
}
|
||||
return function(type) {
|
||||
if (type == end) return cont();
|
||||
if (info === false) return pass(what, proceed);
|
||||
return pass(pushlex(end, info), what, proceed, poplex);
|
||||
return pass(what, proceed);
|
||||
};
|
||||
}
|
||||
function contCommasep(what, end, info) {
|
||||
for (var i = 3; i < arguments.length; i++)
|
||||
cx.cc.push(arguments[i]);
|
||||
return cont(pushlex(end, info), commasep(what, end), poplex);
|
||||
}
|
||||
function block(type) {
|
||||
if (type == "}") return cont();
|
||||
return pass(statement, block);
|
||||
@ -449,8 +458,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
}
|
||||
function pattern(type, value) {
|
||||
if (type == "variable") { register(value); return cont(); }
|
||||
if (type == "[") return cont(commasep(pattern, "]"));
|
||||
if (type == "{") return cont(commasep(proppattern, "}"));
|
||||
if (type == "[") return contCommasep(pattern, "]");
|
||||
if (type == "{") return contCommasep(proppattern, "}");
|
||||
}
|
||||
function proppattern(type, value) {
|
||||
if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
|
||||
@ -470,7 +479,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
if (type == "keyword b" && value == "else") return cont(pushlex("form"), statement, poplex);
|
||||
}
|
||||
function forspec(type) {
|
||||
if (type == "(") return cont(pushlex(")"), forspec1, expect(")"));
|
||||
if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
|
||||
}
|
||||
function forspec1(type) {
|
||||
if (type == "var") return cont(vardef, expect(";"), forspec2);
|
||||
@ -493,7 +502,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
function functiondef(type, value) {
|
||||
if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
|
||||
if (type == "variable") {register(value); return cont(functiondef);}
|
||||
if (type == "(") return cont(pushcontext, commasep(funarg, ")"), statement, popcontext);
|
||||
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, statement, popcontext);
|
||||
}
|
||||
function funarg(type) {
|
||||
if (type == "spread") return cont(funarg);
|
||||
@ -506,7 +515,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
if (value == "extends") return cont(expression);
|
||||
}
|
||||
function objlit(type) {
|
||||
if (type == "{") return cont(commasep(objprop, "}"));
|
||||
if (type == "{") return contCommasep(objprop, "}");
|
||||
}
|
||||
function afterModule(type, value) {
|
||||
if (type == "string") return cont(statement);
|
||||
@ -522,17 +531,21 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
return pass(importSpec, maybeFrom);
|
||||
}
|
||||
function importSpec(type, value) {
|
||||
if (type == "{") return cont(commasep(importSpec, "}"));
|
||||
if (type == "{") return contCommasep(importSpec, "}");
|
||||
if (type == "variable") register(value);
|
||||
return cont();
|
||||
}
|
||||
function maybeFrom(_type, value) {
|
||||
if (value == "from") { cx.marked = "keyword"; return cont(expression); }
|
||||
}
|
||||
function arrayLiteral(type) {
|
||||
if (type == "]") return cont();
|
||||
return pass(expressionNoComma, maybeArrayComprehension);
|
||||
}
|
||||
function maybeArrayComprehension(type) {
|
||||
if (type == "for") return pass(comprehension);
|
||||
if (type == ",") return cont(commasep(expressionNoComma, "]", false));
|
||||
return pass(commasep(expressionNoComma, "]", false));
|
||||
if (type == "for") return pass(comprehension, expect("]"));
|
||||
if (type == ",") return cont(commasep(expressionNoComma, "]"));
|
||||
return pass(commasep(expressionNoComma, "]"));
|
||||
}
|
||||
function comprehension(type) {
|
||||
if (type == "for") return cont(forspec, comprehension);
|
||||
|
@ -201,6 +201,11 @@
|
||||
cm.on("change", function() { cm.setExtending(false); });
|
||||
}
|
||||
|
||||
function clearMark(cm) {
|
||||
cm.setExtending(false);
|
||||
cm.setCursor(cm.getCursor());
|
||||
}
|
||||
|
||||
function getInput(cm, msg, f) {
|
||||
if (cm.openDialog)
|
||||
cm.openDialog(msg + ": <input type=\"text\" style=\"width: 10em\"/>", f, {bottom: true});
|
||||
@ -234,6 +239,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
function quit(cm) {
|
||||
cm.execCommand("clearSearch");
|
||||
clearMark(cm);
|
||||
}
|
||||
|
||||
// Actual keymap
|
||||
|
||||
var keyMap = CodeMirror.keyMap.emacs = {
|
||||
@ -249,6 +259,7 @@
|
||||
}),
|
||||
"Alt-W": function(cm) {
|
||||
addToRing(cm.getSelection());
|
||||
clearMark(cm);
|
||||
},
|
||||
"Ctrl-Y": function(cm) {
|
||||
var start = cm.getCursor();
|
||||
@ -334,7 +345,7 @@
|
||||
"Ctrl-/": repeated("undo"), "Shift-Ctrl--": repeated("undo"),
|
||||
"Ctrl-Z": repeated("undo"), "Cmd-Z": repeated("undo"),
|
||||
"Shift-Alt-,": "goDocStart", "Shift-Alt-.": "goDocEnd",
|
||||
"Ctrl-S": "findNext", "Ctrl-R": "findPrev", "Ctrl-G": "clearSearch", "Shift-Alt-5": "replace",
|
||||
"Ctrl-S": "findNext", "Ctrl-R": "findPrev", "Ctrl-G": quit, "Shift-Alt-5": "replace",
|
||||
"Alt-/": "autocomplete",
|
||||
"Ctrl-J": "newlineAndIndent", "Enter": false, "Tab": "indentAuto",
|
||||
|
||||
|
@ -84,6 +84,7 @@
|
||||
{ keys: ['<End>'], type: 'keyToKey', toKeys: ['$'] },
|
||||
{ keys: ['<PageUp>'], type: 'keyToKey', toKeys: ['<C-b>'] },
|
||||
{ keys: ['<PageDown>'], type: 'keyToKey', toKeys: ['<C-f>'] },
|
||||
{ keys: ['<CR>'], type: 'keyToKey', toKeys: ['j', '^'], context: 'normal' },
|
||||
// Motions
|
||||
{ keys: ['H'], type: 'motion',
|
||||
motion: 'moveToTopLine',
|
||||
@ -247,6 +248,12 @@
|
||||
actionArgs: { forward: true }},
|
||||
{ keys: ['<C-o>'], type: 'action', action: 'jumpListWalk',
|
||||
actionArgs: { forward: false }},
|
||||
{ keys: ['<C-e>'], type: 'action',
|
||||
action: 'scroll',
|
||||
actionArgs: { forward: true, linewise: true }},
|
||||
{ keys: ['<C-y>'], type: 'action',
|
||||
action: 'scroll',
|
||||
actionArgs: { forward: false, linewise: true }},
|
||||
{ keys: ['a'], type: 'action', action: 'enterInsertMode', isEdit: true,
|
||||
actionArgs: { insertAt: 'charAfter' }},
|
||||
{ keys: ['A'], type: 'action', action: 'enterInsertMode', isEdit: true,
|
||||
@ -324,12 +331,16 @@
|
||||
CodeMirror.defineOption('vimMode', false, function(cm, val) {
|
||||
if (val) {
|
||||
cm.setOption('keyMap', 'vim');
|
||||
cm.setOption('disableInput', true);
|
||||
CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
|
||||
cm.on('beforeSelectionChange', beforeSelectionChange);
|
||||
maybeInitVimState(cm);
|
||||
CodeMirror.on(cm.getInputField(), 'paste', getOnPasteFn(cm));
|
||||
} else if (cm.state.vim) {
|
||||
cm.setOption('keyMap', 'default');
|
||||
cm.setOption('disableInput', false);
|
||||
cm.off('beforeSelectionChange', beforeSelectionChange);
|
||||
CodeMirror.off(cm.getInputField(), 'paste', getOnPasteFn(cm));
|
||||
cm.state.vim = null;
|
||||
}
|
||||
});
|
||||
@ -342,6 +353,18 @@
|
||||
head.ch--;
|
||||
}
|
||||
}
|
||||
function getOnPasteFn(cm) {
|
||||
var vim = cm.state.vim;
|
||||
if (!vim.onPasteFn) {
|
||||
vim.onPasteFn = function() {
|
||||
if (!vim.insertMode) {
|
||||
cm.setCursor(offsetCursor(cm.getCursor(), 0, 1));
|
||||
actions.enterInsertMode(cm, {}, vim);
|
||||
}
|
||||
};
|
||||
}
|
||||
return vim.onPasteFn;
|
||||
}
|
||||
|
||||
var numberRegex = /[\d]/;
|
||||
var wordRegexp = [(/\w/), (/[^\w\s]/)], bigWordRegexp = [(/\S/)];
|
||||
@ -549,9 +572,9 @@
|
||||
maybeInitVimState_: maybeInitVimState,
|
||||
|
||||
InsertModeKey: InsertModeKey,
|
||||
map: function(lhs, rhs) {
|
||||
map: function(lhs, rhs, ctx) {
|
||||
// Add user defined key bindings.
|
||||
exCommandDispatcher.map(lhs, rhs);
|
||||
exCommandDispatcher.map(lhs, rhs, ctx);
|
||||
},
|
||||
defineEx: function(name, prefix, func){
|
||||
if (name.indexOf(prefix) !== 0) {
|
||||
@ -833,11 +856,17 @@
|
||||
} else {
|
||||
// Find the best match in the list of matchedCommands.
|
||||
var context = vim.visualMode ? 'visual' : 'normal';
|
||||
var bestMatch = matchedCommands[0]; // Default to first in the list.
|
||||
var bestMatch; // Default to first in the list.
|
||||
for (var i = 0; i < matchedCommands.length; i++) {
|
||||
if (matchedCommands[i].context == context) {
|
||||
bestMatch = matchedCommands[i];
|
||||
var current = matchedCommands[i];
|
||||
if (current.context == context) {
|
||||
bestMatch = current;
|
||||
break;
|
||||
} else if (!bestMatch && !current.context) {
|
||||
// Only set an imperfect match to best match if no best match is
|
||||
// set and the imperfect match is not restricted to another
|
||||
// context.
|
||||
bestMatch = current;
|
||||
}
|
||||
}
|
||||
return getFullyMatchedCommandOrNull(bestMatch);
|
||||
@ -1636,6 +1665,43 @@
|
||||
markPos = markPos ? markPos : cm.getCursor();
|
||||
cm.setCursor(markPos);
|
||||
},
|
||||
scroll: function(cm, actionArgs, vim) {
|
||||
if (vim.visualMode) {
|
||||
return;
|
||||
}
|
||||
var repeat = actionArgs.repeat || 1;
|
||||
var lineHeight = cm.defaultTextHeight();
|
||||
var top = cm.getScrollInfo().top;
|
||||
var delta = lineHeight * repeat;
|
||||
var newPos = actionArgs.forward ? top + delta : top - delta;
|
||||
var cursor = cm.getCursor();
|
||||
var cursorCoords = cm.charCoords(cursor, 'local');
|
||||
if (actionArgs.forward) {
|
||||
if (newPos > cursorCoords.top) {
|
||||
cursor.line += (newPos - cursorCoords.top) / lineHeight;
|
||||
cursor.line = Math.ceil(cursor.line);
|
||||
cm.setCursor(cursor);
|
||||
cursorCoords = cm.charCoords(cursor, 'local');
|
||||
cm.scrollTo(null, cursorCoords.top);
|
||||
} else {
|
||||
// Cursor stays within bounds. Just reposition the scroll window.
|
||||
cm.scrollTo(null, newPos);
|
||||
}
|
||||
} else {
|
||||
var newBottom = newPos + cm.getScrollInfo().clientHeight;
|
||||
if (newBottom < cursorCoords.bottom) {
|
||||
cursor.line -= (cursorCoords.bottom - newBottom) / lineHeight;
|
||||
cursor.line = Math.floor(cursor.line);
|
||||
cm.setCursor(cursor);
|
||||
cursorCoords = cm.charCoords(cursor, 'local');
|
||||
cm.scrollTo(
|
||||
null, cursorCoords.bottom - cm.getScrollInfo().clientHeight);
|
||||
} else {
|
||||
// Cursor stays within bounds. Just reposition the scroll window.
|
||||
cm.scrollTo(null, newPos);
|
||||
}
|
||||
}
|
||||
},
|
||||
scrollToCursor: function(cm, actionArgs) {
|
||||
var lineNum = cm.getCursor().line;
|
||||
var charCoords = cm.charCoords({line: lineNum, ch: 0}, 'local');
|
||||
@ -1691,6 +1757,7 @@
|
||||
cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm));
|
||||
}
|
||||
cm.setOption('keyMap', 'vim-insert');
|
||||
cm.setOption('disableInput', false);
|
||||
if (actionArgs && actionArgs.replace) {
|
||||
// Handle Replace-mode as a special case of insert mode.
|
||||
cm.toggleOverwrite(true);
|
||||
@ -2896,14 +2963,16 @@
|
||||
// pair of commands that have a shared prefix, at least one of their
|
||||
// shortNames must not match the prefix of the other command.
|
||||
var defaultExCommandMap = [
|
||||
{ name: 'map', type: 'builtIn' },
|
||||
{ name: 'write', shortName: 'w', type: 'builtIn' },
|
||||
{ name: 'undo', shortName: 'u', type: 'builtIn' },
|
||||
{ name: 'redo', shortName: 'red', type: 'builtIn' },
|
||||
{ name: 'sort', shortName: 'sor', type: 'builtIn'},
|
||||
{ name: 'substitute', shortName: 's', type: 'builtIn'},
|
||||
{ name: 'nohlsearch', shortName: 'noh', type: 'builtIn'},
|
||||
{ name: 'delmarks', shortName: 'delm', type: 'builtin'}
|
||||
{ name: 'map' },
|
||||
{ name: 'nmap', shortName: 'nm' },
|
||||
{ name: 'vmap', shortName: 'vm' },
|
||||
{ name: 'write', shortName: 'w' },
|
||||
{ name: 'undo', shortName: 'u' },
|
||||
{ name: 'redo', shortName: 'red' },
|
||||
{ name: 'sort', shortName: 'sor' },
|
||||
{ name: 'substitute', shortName: 's' },
|
||||
{ name: 'nohlsearch', shortName: 'noh' },
|
||||
{ name: 'delmarks', shortName: 'delm' }
|
||||
];
|
||||
Vim.ExCommandDispatcher = function() {
|
||||
this.buildCommandMap_();
|
||||
@ -2955,6 +3024,7 @@
|
||||
exCommands[commandName](cm, params);
|
||||
} catch(e) {
|
||||
showConfirm(cm, e);
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
parseInput_: function(cm, inputStream, result) {
|
||||
@ -3037,8 +3107,9 @@
|
||||
this.commandMap_[key] = command;
|
||||
}
|
||||
},
|
||||
map: function(lhs, rhs) {
|
||||
map: function(lhs, rhs, ctx) {
|
||||
if (lhs != ':' && lhs.charAt(0) == ':') {
|
||||
if (ctx) { throw Error('Mode not supported for ex mappings'); }
|
||||
var commandName = lhs.substring(1);
|
||||
if (rhs != ':' && rhs.charAt(0) == ':') {
|
||||
// Ex to Ex mapping
|
||||
@ -3058,17 +3129,21 @@
|
||||
} else {
|
||||
if (rhs != ':' && rhs.charAt(0) == ':') {
|
||||
// Key to Ex mapping.
|
||||
defaultKeymap.unshift({
|
||||
var mapping = {
|
||||
keys: parseKeyString(lhs),
|
||||
type: 'keyToEx',
|
||||
exArgs: { input: rhs.substring(1) }});
|
||||
exArgs: { input: rhs.substring(1) }};
|
||||
if (ctx) { mapping.context = ctx; }
|
||||
defaultKeymap.unshift(mapping);
|
||||
} else {
|
||||
// Key to key mapping
|
||||
defaultKeymap.unshift({
|
||||
var mapping = {
|
||||
keys: parseKeyString(lhs),
|
||||
type: 'keyToKey',
|
||||
toKeys: parseKeyString(rhs)
|
||||
});
|
||||
};
|
||||
if (ctx) { mapping.context = ctx; }
|
||||
defaultKeymap.unshift(mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3090,7 +3165,7 @@
|
||||
}
|
||||
|
||||
var exCommands = {
|
||||
map: function(cm, params) {
|
||||
map: function(cm, params, ctx) {
|
||||
var mapArgs = params.args;
|
||||
if (!mapArgs || mapArgs.length < 2) {
|
||||
if (cm) {
|
||||
@ -3098,8 +3173,10 @@
|
||||
}
|
||||
return;
|
||||
}
|
||||
exCommandDispatcher.map(mapArgs[0], mapArgs[1], cm);
|
||||
exCommandDispatcher.map(mapArgs[0], mapArgs[1], ctx);
|
||||
},
|
||||
nmap: function(cm, params) { this.map(cm, params, 'normal'); },
|
||||
vmap: function(cm, params) { this.map(cm, params, 'visual'); },
|
||||
move: function(cm, params) {
|
||||
commandDispatcher.processCommand(cm, cm.state.vim, {
|
||||
type: 'motion',
|
||||
@ -3115,7 +3192,7 @@
|
||||
var args = new CodeMirror.StringStream(params.argString);
|
||||
if (args.eat('!')) { reverse = true; }
|
||||
if (args.eol()) { return; }
|
||||
if (!args.eatSpace()) { throw new Error('invalid arguments ' + args.match(/.*/)[0]); }
|
||||
if (!args.eatSpace()) { return 'Invalid arguments'; }
|
||||
var opts = args.match(/[a-z]+/);
|
||||
if (opts) {
|
||||
opts = opts[0];
|
||||
@ -3124,13 +3201,17 @@
|
||||
var decimal = opts.indexOf('d') != -1 && 1;
|
||||
var hex = opts.indexOf('x') != -1 && 1;
|
||||
var octal = opts.indexOf('o') != -1 && 1;
|
||||
if (decimal + hex + octal > 1) { throw new Error('invalid arguments'); }
|
||||
if (decimal + hex + octal > 1) { return 'Invalid arguments'; }
|
||||
number = decimal && 'decimal' || hex && 'hex' || octal && 'octal';
|
||||
}
|
||||
if (args.eatSpace() && args.match(/\/.*\//)) { throw new Error('patterns not supported'); }
|
||||
if (args.eatSpace() && args.match(/\/.*\//)) { 'patterns not supported'; }
|
||||
}
|
||||
}
|
||||
parseArgs();
|
||||
var err = parseArgs();
|
||||
if (err) {
|
||||
showConfirm(cm, err + ': ' + params.argString);
|
||||
return;
|
||||
}
|
||||
var lineStart = params.line || cm.firstLine();
|
||||
var lineEnd = params.lineEnd || params.line || cm.lastLine();
|
||||
if (lineStart == lineEnd) { return; }
|
||||
@ -3251,13 +3332,13 @@
|
||||
clearSearchHighlight(cm);
|
||||
},
|
||||
delmarks: function(cm, params) {
|
||||
if (!params.argString || !params.argString.trim()) {
|
||||
if (!params.argString || !trim(params.argString)) {
|
||||
showConfirm(cm, 'Argument required');
|
||||
return;
|
||||
}
|
||||
|
||||
var state = cm.state.vim;
|
||||
var stream = new CodeMirror.StringStream(params.argString.trim());
|
||||
var stream = new CodeMirror.StringStream(trim(params.argString));
|
||||
while (!stream.eol()) {
|
||||
stream.eatSpace();
|
||||
|
||||
@ -3394,7 +3475,8 @@
|
||||
// Actually do replace.
|
||||
next();
|
||||
if (done) {
|
||||
throw new Error('No matches for ' + query.source);
|
||||
showConfirm(cm, 'No matches for ' + query.source);
|
||||
return;
|
||||
}
|
||||
if (!confirm) {
|
||||
replaceAll();
|
||||
@ -3445,7 +3527,6 @@
|
||||
|
||||
var cmToVimKeymap = {
|
||||
'nofallthrough': true,
|
||||
'disableInput': true,
|
||||
'style': 'fat-cursor'
|
||||
};
|
||||
function bindKeys(keys, modifier) {
|
||||
@ -3492,6 +3573,7 @@
|
||||
cm.setCursor(cm.getCursor().line, cm.getCursor().ch-1, true);
|
||||
vim.insertMode = false;
|
||||
cm.setOption('keyMap', 'vim');
|
||||
cm.setOption('disableInput', true);
|
||||
cm.toggleOverwrite(false); // exit replace mode if we were in it.
|
||||
CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
|
||||
}
|
||||
|
@ -54,7 +54,7 @@
|
||||
var one = cm.markText(found.from, Pos(found.from.line, found.from.ch + 1), {className: style});
|
||||
var two = found.to && cm.markText(found.to, Pos(found.to.line, found.to.ch + 1), {className: style});
|
||||
// Kludge to work around the IE bug from issue #1193, where text
|
||||
// input stops going to the textare whever this fires.
|
||||
// input stops going to the textarea whenever this fires.
|
||||
if (ie_lt8 && cm.state.focused) cm.display.input.focus();
|
||||
var clear = function() {
|
||||
cm.operation(function() { one.clear(); two && two.clear(); });
|
||||
|
@ -74,14 +74,6 @@ selector in floating-scrollbar-light.css across all platforms. */
|
||||
font: message-box;
|
||||
}
|
||||
|
||||
.CodeMirror-code > div > div:first-child {
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.CodeMirror-gutter-elt {
|
||||
transform: translate(0,-50%);
|
||||
}
|
||||
|
||||
.cm-trailingspace {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAYAAAB/qH1jAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QUXCToH00Y1UgAAACFJREFUCNdjPMDBUc/AwNDAAAFMTAwMDA0OP34wQgX/AQBYgwYEx4f9lQAAAABJRU5ErkJggg==");
|
||||
opacity: 0.75;
|
||||
|
@ -7,7 +7,15 @@
|
||||
// Ctrl-G.
|
||||
|
||||
(function() {
|
||||
function searchOverlay(query) {
|
||||
function searchOverlay(query, caseInsensitive) {
|
||||
var startChar;
|
||||
if (typeof query == "string") {
|
||||
startChar = query.charAt(0);
|
||||
query = new RegExp("^" + query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"),
|
||||
caseInsensitive ? "i" : "");
|
||||
} else {
|
||||
query = new RegExp("^(?:" + query.source + ")", query.ignoreCase ? "i" : "");
|
||||
}
|
||||
if (typeof query == "string") return {token: function(stream) {
|
||||
if (stream.match(query)) return "searching";
|
||||
stream.next();
|
||||
@ -17,6 +25,8 @@
|
||||
if (stream.match(query)) return "searching";
|
||||
while (!stream.eol()) {
|
||||
stream.next();
|
||||
if (startChar)
|
||||
stream.skipTo(startChar) || stream.skipToEnd();
|
||||
if (stream.match(query, false)) break;
|
||||
}
|
||||
}};
|
||||
@ -29,13 +39,16 @@
|
||||
function getSearchState(cm) {
|
||||
return cm.state.search || (cm.state.search = new SearchState());
|
||||
}
|
||||
function queryCaseInsensitive(query) {
|
||||
return typeof query == "string" && query == query.toLowerCase();
|
||||
}
|
||||
function getSearchCursor(cm, query, pos) {
|
||||
// Heuristic: if the query string is all lowercase, do a case insensitive search.
|
||||
return cm.getSearchCursor(query, pos, typeof query == "string" && query == query.toLowerCase());
|
||||
return cm.getSearchCursor(query, pos, queryCaseInsensitive(query));
|
||||
}
|
||||
function dialog(cm, text, shortText, f) {
|
||||
if (cm.openDialog) cm.openDialog(text, f);
|
||||
else f(prompt(shortText, ""));
|
||||
function dialog(cm, text, shortText, deflt, f) {
|
||||
if (cm.openDialog) cm.openDialog(text, f, {value: deflt});
|
||||
else f(prompt(shortText, deflt));
|
||||
}
|
||||
function confirmDialog(cm, text, shortText, fs) {
|
||||
if (cm.openConfirm) cm.openConfirm(text, fs);
|
||||
@ -45,29 +58,16 @@
|
||||
var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
|
||||
return isRE ? new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i") : query;
|
||||
}
|
||||
var queryDialog;
|
||||
var queryDialog =
|
||||
'Search: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
|
||||
function doSearch(cm, rev) {
|
||||
if (!queryDialog) {
|
||||
let doc = cm.getWrapperElement().ownerDocument;
|
||||
let inp = doc.createElement("input");
|
||||
let txt = doc.createTextNode(cm.l10n("findCmd.promptMessage"));
|
||||
|
||||
inp.type = "text";
|
||||
inp.style.width = "10em";
|
||||
inp.style.MozMarginStart = "1em";
|
||||
|
||||
queryDialog = doc.createElement("div");
|
||||
queryDialog.appendChild(txt);
|
||||
queryDialog.appendChild(inp);
|
||||
}
|
||||
|
||||
var state = getSearchState(cm);
|
||||
if (state.query) return findNext(cm, rev);
|
||||
dialog(cm, queryDialog, cm.l10n('findCmd.promptMessage'), function(query) {
|
||||
dialog(cm, queryDialog, "Search for:", cm.getSelection(), function(query) {
|
||||
cm.operation(function() {
|
||||
if (!query || state.query) return;
|
||||
state.query = parseQuery(query);
|
||||
cm.removeOverlay(state.overlay);
|
||||
cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
|
||||
state.overlay = searchOverlay(state.query);
|
||||
cm.addOverlay(state.overlay);
|
||||
state.posFrom = state.posTo = cm.getCursor();
|
||||
@ -98,10 +98,10 @@
|
||||
var replacementQueryDialog = 'With: <input type="text" style="width: 10em"/>';
|
||||
var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>";
|
||||
function replace(cm, all) {
|
||||
dialog(cm, replaceQueryDialog, "Replace:", function(query) {
|
||||
dialog(cm, replaceQueryDialog, "Replace:", cm.getSelection(), function(query) {
|
||||
if (!query) return;
|
||||
query = parseQuery(query);
|
||||
dialog(cm, replacementQueryDialog, "Replace with:", function(text) {
|
||||
dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) {
|
||||
if (all) {
|
||||
cm.operation(function() {
|
||||
for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
|
||||
|
@ -47,6 +47,7 @@
|
||||
match: match};
|
||||
};
|
||||
} else { // String query
|
||||
var origQuery = query;
|
||||
if (caseFold) query = query.toLowerCase();
|
||||
var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
|
||||
var target = query.split("\n");
|
||||
@ -58,33 +59,45 @@
|
||||
this.matches = function() {};
|
||||
} else {
|
||||
this.matches = function(reverse, pos) {
|
||||
var line = fold(doc.getLine(pos.line)), len = query.length, match;
|
||||
if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
|
||||
: (match = line.indexOf(query, pos.ch)) != -1)
|
||||
return {from: Pos(pos.line, match),
|
||||
to: Pos(pos.line, match + len)};
|
||||
if (reverse) {
|
||||
var orig = doc.getLine(pos.line).slice(0, pos.ch), line = fold(orig);
|
||||
var match = line.lastIndexOf(query);
|
||||
if (match > -1) {
|
||||
match = adjustPos(orig, line, match);
|
||||
return {from: Pos(pos.line, match), to: Pos(pos.line, match + origQuery.length)};
|
||||
}
|
||||
} else {
|
||||
var orig = doc.getLine(pos.line).slice(pos.ch), line = fold(orig);
|
||||
var match = line.indexOf(query);
|
||||
if (match > -1) {
|
||||
match = adjustPos(orig, line, match) + pos.ch;
|
||||
return {from: Pos(pos.line, match), to: Pos(pos.line, match + origQuery.length)};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
} else {
|
||||
var origTarget = origQuery.split("\n");
|
||||
this.matches = function(reverse, pos) {
|
||||
var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(doc.getLine(ln));
|
||||
var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
|
||||
if (reverse ? offsetA > pos.ch || offsetA != match.length
|
||||
: offsetA < pos.ch || offsetA != line.length - match.length)
|
||||
return;
|
||||
for (;;) {
|
||||
if (reverse ? !ln : ln == doc.lineCount() - 1) return;
|
||||
line = fold(doc.getLine(ln += reverse ? -1 : 1));
|
||||
match = target[reverse ? --idx : ++idx];
|
||||
if (idx > 0 && idx < target.length - 1) {
|
||||
if (line != match) return;
|
||||
else continue;
|
||||
}
|
||||
var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
|
||||
if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
|
||||
return;
|
||||
var start = Pos(pos.line, offsetA), end = Pos(ln, offsetB);
|
||||
return {from: reverse ? end : start, to: reverse ? start : end};
|
||||
var last = target.length - 1;
|
||||
if (reverse) {
|
||||
if (pos.line - (target.length - 1) < doc.firstLine()) return;
|
||||
if (fold(doc.getLine(pos.line).slice(0, origTarget[last].length)) != target[target.length - 1]) return;
|
||||
var to = Pos(pos.line, origTarget[last].length);
|
||||
for (var ln = pos.line - 1, i = last - 1; i >= 1; --i, --ln)
|
||||
if (target[i] != fold(doc.getLine(ln))) return;
|
||||
var line = doc.getLine(ln), cut = line.length - origTarget[0].length;
|
||||
if (fold(line.slice(cut)) != target[0]) return;
|
||||
return {from: Pos(ln, cut), to: to};
|
||||
} else {
|
||||
if (pos.line + (target.length - 1) > doc.lastLine()) return;
|
||||
var line = doc.getLine(pos.line), cut = line.length - origTarget[0].length;
|
||||
if (fold(line.slice(cut)) != target[0]) return;
|
||||
var from = Pos(pos.line, cut);
|
||||
for (var ln = pos.line + 1, i = 1; i < last; ++i, ++ln)
|
||||
if (target[i] != fold(doc.getLine(ln))) return;
|
||||
if (doc.getLine(ln).slice(0, origTarget[last].length) != target[last]) return;
|
||||
return {from: from, to: Pos(ln, origTarget[last].length)};
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -106,7 +119,6 @@
|
||||
|
||||
for (;;) {
|
||||
if (this.pos = this.matches(reverse, pos)) {
|
||||
if (!this.pos.from || !this.pos.to) { console.log(this.matches, this.pos); }
|
||||
this.atOccurrence = true;
|
||||
return this.pos.match || true;
|
||||
}
|
||||
@ -134,6 +146,18 @@
|
||||
}
|
||||
};
|
||||
|
||||
// Maps a position in a case-folded line back to a position in the original line
|
||||
// (compensating for codepoints increasing in number during folding)
|
||||
function adjustPos(orig, folded, pos) {
|
||||
if (orig.length == folded.length) return pos;
|
||||
for (var pos1 = Math.min(pos, orig.length);;) {
|
||||
var len1 = orig.slice(0, pos1).toLowerCase().length;
|
||||
if (len1 < pos) ++pos1;
|
||||
else if (len1 > pos) --pos1;
|
||||
else return pos1;
|
||||
}
|
||||
}
|
||||
|
||||
CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
|
||||
return new SearchCursor(this.doc, query, pos, caseFold);
|
||||
});
|
||||
|
@ -1,11 +1,8 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
CodeMirror.defineOption("showTrailingSpace", true, function(cm, val, prev) {
|
||||
if (prev == CodeMirror.Init) prev = false;
|
||||
if (prev && !val)
|
||||
CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) {
|
||||
if (prev == CodeMirror.Init) prev = false;
|
||||
if (prev && !val)
|
||||
cm.removeOverlay("trailingspace");
|
||||
else if (!prev && val)
|
||||
else if (!prev && val)
|
||||
cm.addOverlay({
|
||||
token: function(stream) {
|
||||
for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {}
|
||||
@ -15,5 +12,4 @@
|
||||
},
|
||||
name: "trailingspace"
|
||||
});
|
||||
});
|
||||
})();
|
||||
});
|
||||
|
@ -45,7 +45,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
|
||||
var alignCDATA = parserConfig.alignCDATA;
|
||||
|
||||
// Return variables for tokenizers
|
||||
var tagName, type;
|
||||
var tagName, type, setStyle;
|
||||
|
||||
function inText(stream, state) {
|
||||
function chain(parser) {
|
||||
@ -110,6 +110,8 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
|
||||
return null;
|
||||
} else if (ch == "<") {
|
||||
state.tokenize = inText;
|
||||
state.state = baseState;
|
||||
state.tagName = state.tagStart = null;
|
||||
var next = state.tokenize(stream, state);
|
||||
return next ? next + " error" : "error";
|
||||
} else if (/[\'\"]/.test(ch)) {
|
||||
@ -169,139 +171,124 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
|
||||
};
|
||||
}
|
||||
|
||||
var curState, curStream, setStyle;
|
||||
function pass() {
|
||||
for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
|
||||
function Context(state, tagName, startOfLine) {
|
||||
this.prev = state.context;
|
||||
this.tagName = tagName;
|
||||
this.indent = state.indented;
|
||||
this.startOfLine = startOfLine;
|
||||
if (Kludges.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
|
||||
this.noIndent = true;
|
||||
}
|
||||
function cont() {
|
||||
pass.apply(null, arguments);
|
||||
return true;
|
||||
function popContext(state) {
|
||||
if (state.context) state.context = state.context.prev;
|
||||
}
|
||||
function maybePopContext(state, nextTagName) {
|
||||
var parentTagName;
|
||||
while (true) {
|
||||
if (!state.context) {
|
||||
return;
|
||||
}
|
||||
parentTagName = state.context.tagName.toLowerCase();
|
||||
if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
|
||||
!Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
|
||||
return;
|
||||
}
|
||||
popContext(state);
|
||||
}
|
||||
}
|
||||
|
||||
function pushContext(tagName, startOfLine) {
|
||||
var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
|
||||
curState.context = {
|
||||
prev: curState.context,
|
||||
tagName: tagName,
|
||||
indent: curState.indented,
|
||||
startOfLine: startOfLine,
|
||||
noIndent: noIndent
|
||||
};
|
||||
}
|
||||
function popContext() {
|
||||
if (curState.context) curState.context = curState.context.prev;
|
||||
}
|
||||
|
||||
function element(type) {
|
||||
function baseState(type, stream, state) {
|
||||
if (type == "openTag") {
|
||||
curState.tagName = tagName;
|
||||
curState.tagStart = curStream.column();
|
||||
return cont(attributes, endtag(curState.startOfLine));
|
||||
state.tagName = tagName;
|
||||
state.tagStart = stream.column();
|
||||
return attrState;
|
||||
} else if (type == "closeTag") {
|
||||
var err = false;
|
||||
if (curState.context) {
|
||||
if (curState.context.tagName != tagName) {
|
||||
if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) {
|
||||
popContext();
|
||||
}
|
||||
err = !curState.context || curState.context.tagName != tagName;
|
||||
if (state.context) {
|
||||
if (state.context.tagName != tagName) {
|
||||
if (Kludges.implicitlyClosed.hasOwnProperty(state.context.tagName.toLowerCase()))
|
||||
popContext(state);
|
||||
err = !state.context || state.context.tagName != tagName;
|
||||
}
|
||||
} else {
|
||||
err = true;
|
||||
}
|
||||
if (err) setStyle = "error";
|
||||
return cont(endclosetag(err));
|
||||
return err ? closeStateErr : closeState;
|
||||
} else {
|
||||
return baseState;
|
||||
}
|
||||
return cont();
|
||||
}
|
||||
function endtag(startOfLine) {
|
||||
return function(type) {
|
||||
var tagName = curState.tagName;
|
||||
curState.tagName = curState.tagStart = null;
|
||||
if (type == "selfcloseTag" ||
|
||||
(type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) {
|
||||
maybePopContext(tagName.toLowerCase());
|
||||
return cont();
|
||||
}
|
||||
if (type == "endTag") {
|
||||
maybePopContext(tagName.toLowerCase());
|
||||
pushContext(tagName, startOfLine);
|
||||
return cont();
|
||||
}
|
||||
return cont();
|
||||
};
|
||||
}
|
||||
function endclosetag(err) {
|
||||
return function(type) {
|
||||
if (err) setStyle = "error";
|
||||
if (type == "endTag") { popContext(); return cont(); }
|
||||
function closeState(type, _stream, state) {
|
||||
if (type != "endTag") {
|
||||
setStyle = "error";
|
||||
return cont(arguments.callee);
|
||||
};
|
||||
}
|
||||
function maybePopContext(nextTagName) {
|
||||
var parentTagName;
|
||||
while (true) {
|
||||
if (!curState.context) {
|
||||
return;
|
||||
}
|
||||
parentTagName = curState.context.tagName.toLowerCase();
|
||||
if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
|
||||
!Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
|
||||
return;
|
||||
}
|
||||
popContext();
|
||||
return closeState;
|
||||
}
|
||||
popContext(state);
|
||||
return baseState;
|
||||
}
|
||||
function closeStateErr(type, stream, state) {
|
||||
setStyle = "error";
|
||||
return closeState(type, stream, state);
|
||||
}
|
||||
|
||||
function attributes(type) {
|
||||
if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);}
|
||||
if (type == "endTag" || type == "selfcloseTag") return pass();
|
||||
function attrState(type, _stream, state) {
|
||||
if (type == "word") {
|
||||
setStyle = "attribute";
|
||||
return attrEqState;
|
||||
} else if (type == "endTag" || type == "selfcloseTag") {
|
||||
var tagName = state.tagName, tagStart = state.tagStart;
|
||||
state.tagName = state.tagStart = null;
|
||||
if (type == "selfcloseTag" ||
|
||||
Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase())) {
|
||||
maybePopContext(state, tagName.toLowerCase());
|
||||
} else {
|
||||
maybePopContext(state, tagName.toLowerCase());
|
||||
state.context = new Context(state, tagName, tagStart == state.indented);
|
||||
}
|
||||
return baseState;
|
||||
}
|
||||
setStyle = "error";
|
||||
return cont(attributes);
|
||||
return attrState;
|
||||
}
|
||||
function attribute(type) {
|
||||
if (type == "equals") return cont(attvalue, attributes);
|
||||
function attrEqState(type, stream, state) {
|
||||
if (type == "equals") return attrValueState;
|
||||
if (!Kludges.allowMissing) setStyle = "error";
|
||||
else if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);}
|
||||
return (type == "endTag" || type == "selfcloseTag") ? pass() : cont();
|
||||
return attrState(type, stream, state);
|
||||
}
|
||||
function attvalue(type) {
|
||||
if (type == "string") return cont(attvaluemaybe);
|
||||
if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
|
||||
function attrValueState(type, stream, state) {
|
||||
if (type == "string") return attrContinuedState;
|
||||
if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return attrState;}
|
||||
setStyle = "error";
|
||||
return (type == "endTag" || type == "selfCloseTag") ? pass() : cont();
|
||||
return attrState(type, stream, state);
|
||||
}
|
||||
function attvaluemaybe(type) {
|
||||
if (type == "string") return cont(attvaluemaybe);
|
||||
else return pass();
|
||||
function attrContinuedState(type, stream, state) {
|
||||
if (type == "string") return attrContinuedState;
|
||||
return attrState(type, stream, state);
|
||||
}
|
||||
|
||||
return {
|
||||
startState: function() {
|
||||
return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, tagStart: null, context: null};
|
||||
return {tokenize: inText,
|
||||
state: baseState,
|
||||
indented: 0,
|
||||
tagName: null, tagStart: null,
|
||||
context: null};
|
||||
},
|
||||
|
||||
token: function(stream, state) {
|
||||
if (!state.tagName && stream.sol()) {
|
||||
state.startOfLine = true;
|
||||
if (!state.tagName && stream.sol())
|
||||
state.indented = stream.indentation();
|
||||
}
|
||||
if (stream.eatSpace()) return null;
|
||||
|
||||
setStyle = type = tagName = null;
|
||||
if (stream.eatSpace()) return null;
|
||||
tagName = type = null;
|
||||
var style = state.tokenize(stream, state);
|
||||
state.type = type;
|
||||
if ((style || type) && style != "comment") {
|
||||
curState = state; curStream = stream;
|
||||
while (true) {
|
||||
var comb = state.cc.pop() || element;
|
||||
if (comb(type || style)) break;
|
||||
}
|
||||
setStyle = null;
|
||||
state.state = state.state(type || style, stream, state);
|
||||
if (setStyle)
|
||||
style = setStyle == "error" ? style + " error" : setStyle;
|
||||
}
|
||||
state.startOfLine = false;
|
||||
if (setStyle)
|
||||
style = setStyle == "error" ? style + " error" : setStyle;
|
||||
return style;
|
||||
},
|
||||
|
||||
@ -311,8 +298,8 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
|
||||
if (state.tokenize.isInAttribute) {
|
||||
return state.stringStartCol + 1;
|
||||
}
|
||||
if ((state.tokenize != inTag && state.tokenize != inText) ||
|
||||
context && context.noIndent)
|
||||
if (context && context.noIndent) return CodeMirror.Pass;
|
||||
if (state.tokenize != inTag && state.tokenize != inText)
|
||||
return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
|
||||
// Indent the starts of attribute names.
|
||||
if (state.tagName) {
|
||||
|
@ -29,11 +29,11 @@ namespace = "comment_";
|
||||
}, simpleProg, simpleProg);
|
||||
|
||||
// test("fallbackToBlock", "css", function(cm) {
|
||||
// cm.lineComment(Pos(0, 0), Pos(2, 1));
|
||||
// cm.lineComment(Pos(0, 0), Pos(2, 1));
|
||||
// }, "html {\n border: none;\n}", "/* html {\n border: none;\n} */");
|
||||
|
||||
// test("fallbackToLine", "ruby", function(cm) {
|
||||
// cm.blockComment(Pos(0, 0), Pos(1));
|
||||
// cm.blockComment(Pos(0, 0), Pos(1));
|
||||
// }, "def blah()\n return hah\n", "# def blah()\n# return hah\n");
|
||||
|
||||
test("commentRange", "javascript", function(cm) {
|
||||
@ -48,4 +48,16 @@ namespace = "comment_";
|
||||
cm.setCursor(1);
|
||||
cm.execCommand("toggleComment");
|
||||
}, "a;\n\nb;", "a;\n// \nb;");
|
||||
|
||||
test("dontMessWithStrings", "javascript", function(cm) {
|
||||
cm.execCommand("toggleComment");
|
||||
}, "console.log(\"/*string*/\");", "// console.log(\"/*string*/\");");
|
||||
|
||||
test("dontMessWithStrings2", "javascript", function(cm) {
|
||||
cm.execCommand("toggleComment");
|
||||
}, "console.log(\"// string\");", "// console.log(\"// string\");");
|
||||
|
||||
test("dontMessWithStrings3", "javascript", function(cm) {
|
||||
cm.execCommand("toggleComment");
|
||||
}, "// console.log(\"// string\");", "console.log(\"// string\");");
|
||||
})();
|
||||
|
@ -1,4 +1,4 @@
|
||||
var tests = [], debug = null, debugUsed = new Array(), allNames = [];
|
||||
var tests = [], filters = [], allNames = [];
|
||||
|
||||
function Failure(why) {this.message = why;}
|
||||
Failure.prototype.toString = function() { return this.message; };
|
||||
@ -32,7 +32,7 @@ function testCM(name, run, opts, expectedFail) {
|
||||
run(cm);
|
||||
successful = true;
|
||||
} finally {
|
||||
if ((debug && !successful) || verbose) {
|
||||
if (!successful || verbose) {
|
||||
place.style.visibility = "visible";
|
||||
} else {
|
||||
place.removeChild(cm.getWrapperElement());
|
||||
@ -42,39 +42,23 @@ function testCM(name, run, opts, expectedFail) {
|
||||
}
|
||||
|
||||
function runTests(callback) {
|
||||
if (debug) {
|
||||
if (indexOf(debug, "verbose") === 0) {
|
||||
verbose = true;
|
||||
debug.splice(0, 1);
|
||||
}
|
||||
if (debug.length < 1) {
|
||||
debug = null;
|
||||
}
|
||||
}
|
||||
var totalTime = 0;
|
||||
function step(i) {
|
||||
if (i === tests.length){
|
||||
running = false;
|
||||
return callback("done");
|
||||
}
|
||||
}
|
||||
var test = tests[i], expFail = test.expectedFail, startTime = +new Date;
|
||||
if (debug !== null) {
|
||||
var debugIndex = indexOf(debug, test.name);
|
||||
if (debugIndex !== -1) {
|
||||
// Remove from array for reporting incorrect tests later
|
||||
debug.splice(debugIndex, 1);
|
||||
} else {
|
||||
var wildcardName = test.name.split("_")[0] + "_*";
|
||||
debugIndex = indexOf(debug, wildcardName);
|
||||
if (debugIndex !== -1) {
|
||||
// Remove from array for reporting incorrect tests later
|
||||
debug.splice(debugIndex, 1);
|
||||
debugUsed.push(wildcardName);
|
||||
} else {
|
||||
debugIndex = indexOf(debugUsed, wildcardName);
|
||||
if (debugIndex == -1) return step(i + 1);
|
||||
if (filters.length) {
|
||||
for (var j = 0; j < filters.length; j++) {
|
||||
if (test.name.match(filters[j])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == filters.length) {
|
||||
callback("skipped", test.name, message);
|
||||
return step(i + 1);
|
||||
}
|
||||
}
|
||||
var threw = false;
|
||||
try {
|
||||
@ -84,7 +68,7 @@ function runTests(callback) {
|
||||
if (expFail) callback("expected", test.name);
|
||||
else if (e instanceof Failure) callback("fail", test.name, e.message);
|
||||
else {
|
||||
var pos = /\bat .*?([^\/:]+):(\d+):/.exec(e.stack);
|
||||
var pos = /(?:\bat |@).*?([^\/:]+):(\d+)/.exec(e.stack);
|
||||
callback("error", test.name, e.toString() + (pos ? " (" + pos[1] + ":" + pos[2] + ")" : ""));
|
||||
}
|
||||
}
|
||||
@ -127,13 +111,21 @@ function is(a, msg) {
|
||||
}
|
||||
|
||||
function countTests() {
|
||||
if (!debug) return tests.length;
|
||||
if (!filters.length) return tests.length;
|
||||
var sum = 0;
|
||||
for (var i = 0; i < tests.length; ++i) {
|
||||
var name = tests[i].name;
|
||||
if (indexOf(debug, name) != -1 ||
|
||||
indexOf(debug, name.split("_")[0] + "_*") != -1)
|
||||
++sum;
|
||||
for (var j = 0; j < filters.length; j++) {
|
||||
if (name.match(filters[j])) {
|
||||
++sum;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
function parseTestFilter(s) {
|
||||
if (/_\*$/.test(s)) return new RegExp("^" + s.slice(0, s.length - 2), "i");
|
||||
else return new RegExp(s, "i");
|
||||
}
|
||||
|
@ -125,6 +125,9 @@
|
||||
sim("transposeExpr", "do foo[bar] dah",
|
||||
Pos(0, 6), "Ctrl-Alt-T", txt("do [bar]foo dah"));
|
||||
|
||||
sim("clearMark", "abcde", Pos(0, 2), "Ctrl-Space", "Ctrl-F", "Ctrl-F",
|
||||
"Ctrl-G", "Ctrl-W", txt("abcde"));
|
||||
|
||||
testCM("save", function(cm) {
|
||||
var saved = false;
|
||||
CodeMirror.commands.save = function(cm) { saved = cm.getValue(); };
|
||||
|
@ -3,15 +3,15 @@
|
||||
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
|
||||
|
||||
MT("locals",
|
||||
"[keyword function] [variable foo]([def a], [def b]) { [keyword var] [def c] = [number 10]; [keyword return] [variable-2 a] + [variable-2 c] + [variable d]; }");
|
||||
"[keyword function] [variable foo]([def a], [def b]) { [keyword var] [def c] [operator =] [number 10]; [keyword return] [variable-2 a] [operator +] [variable-2 c] [operator +] [variable d]; }");
|
||||
|
||||
MT("comma-and-binop",
|
||||
"[keyword function](){ [keyword var] [def x] = [number 1] + [number 2], [def y]; }");
|
||||
"[keyword function](){ [keyword var] [def x] [operator =] [number 1] [operator +] [number 2], [def y]; }");
|
||||
|
||||
MT("destructuring",
|
||||
"([keyword function]([def a], [[[def b], [def c] ]]) {",
|
||||
" [keyword let] {[def d], [property foo]: [def c]=[number 10], [def x]} = [variable foo]([variable-2 a]);",
|
||||
" [[[variable-2 c], [variable y] ]] = [variable-2 c];",
|
||||
" [keyword let] {[def d], [property foo]: [def c][operator =][number 10], [def x]} [operator =] [variable foo]([variable-2 a]);",
|
||||
" [[[variable-2 c], [variable y] ]] [operator =] [variable-2 c];",
|
||||
"})();");
|
||||
|
||||
MT("class",
|
||||
@ -19,13 +19,13 @@
|
||||
" [[ [string-2 /expr/] ]]: [number 24],",
|
||||
" [property constructor]([def x], [def y]) {",
|
||||
" [keyword super]([string 'something']);",
|
||||
" [keyword this].[property x] = [variable-2 x];",
|
||||
" [keyword this].[property x] [operator =] [variable-2 x];",
|
||||
" }",
|
||||
"}");
|
||||
|
||||
MT("module",
|
||||
"[keyword module] [string 'foo'] {",
|
||||
" [keyword export] [keyword let] [def x] = [number 42];",
|
||||
" [keyword export] [keyword let] [def x] [operator =] [number 42];",
|
||||
" [keyword export] [keyword *] [keyword from] [string 'somewhere'];",
|
||||
"}");
|
||||
|
||||
@ -38,7 +38,7 @@
|
||||
|
||||
MT("const",
|
||||
"[keyword function] [variable f]() {",
|
||||
" [keyword const] [[ [def a], [def b] ]] = [[ [number 1], [number 2] ]];",
|
||||
" [keyword const] [[ [def a], [def b] ]] [operator =] [[ [number 1], [number 2] ]];",
|
||||
"}");
|
||||
|
||||
MT("for/of",
|
||||
@ -46,14 +46,14 @@
|
||||
|
||||
MT("generator",
|
||||
"[keyword function*] [variable repeat]([def n]) {",
|
||||
" [keyword for]([keyword var] [def i] = [number 0]; [variable-2 i] < [variable-2 n]; ++[variable-2 i])",
|
||||
" [keyword for]([keyword var] [def i] [operator =] [number 0]; [variable-2 i] [operator <] [variable-2 n]; [operator ++][variable-2 i])",
|
||||
" [keyword yield] [variable-2 i];",
|
||||
"}");
|
||||
|
||||
MT("fatArrow",
|
||||
"[variable array].[property filter]([def a] => [variable-2 a] + [number 1]);",
|
||||
"[variable array].[property filter]([def a] [operator =>] [variable-2 a] [operator +] [number 1]);",
|
||||
"[variable a];", // No longer in scope
|
||||
"[keyword let] [variable f] = ([[ [def a], [def b] ]], [def c]) => [variable-2 a] + [variable-2 c];",
|
||||
"[keyword let] [variable f] [operator =] ([[ [def a], [def b] ]], [def c]) [operator =>] [variable-2 a] [operator +] [variable-2 c];",
|
||||
"[variable c];");
|
||||
|
||||
MT("spread",
|
||||
@ -63,10 +63,51 @@
|
||||
|
||||
MT("comprehension",
|
||||
"[keyword function] [variable f]() {",
|
||||
" [[ [variable x] + [number 1] [keyword for] ([keyword var] [def x] [keyword in] [variable y]) [keyword if] [variable pred]([variable-2 x]) ]];",
|
||||
" ([variable u] [keyword for] ([keyword var] [def u] [keyword of] [variable generateValues]()) [keyword if] ([variable-2 u].[property color] === [string 'blue']));",
|
||||
" [[([variable x] [operator +] [number 1]) [keyword for] ([keyword var] [def x] [keyword in] [variable y]) [keyword if] [variable pred]([variable-2 x]) ]];",
|
||||
" ([variable u] [keyword for] ([keyword var] [def u] [keyword of] [variable generateValues]()) [keyword if] ([variable-2 u].[property color] [operator ===] [string 'blue']));",
|
||||
"}");
|
||||
|
||||
MT("quasi",
|
||||
"[variable re][string-2 `fofdlakj${][variable x] + ([variable re][string-2 `foo`]) + [number 1][string-2 }fdsa`] + [number 2]");
|
||||
"[variable re][string-2 `fofdlakj${][variable x] [operator +] ([variable re][string-2 `foo`]) [operator +] [number 1][string-2 }fdsa`] [operator +] [number 2]");
|
||||
|
||||
MT("indent_statement",
|
||||
"[keyword var] [variable x] [operator =] [number 10]",
|
||||
"[variable x] [operator +=] [variable y] [operator +]",
|
||||
" [atom Infinity]",
|
||||
"[keyword debugger];");
|
||||
|
||||
MT("indent_if",
|
||||
"[keyword if] ([number 1])",
|
||||
" [keyword break];",
|
||||
"[keyword else] [keyword if] ([number 2])",
|
||||
" [keyword continue];",
|
||||
"[keyword else]",
|
||||
" [number 10];",
|
||||
"[keyword if] ([number 1]) {",
|
||||
" [keyword break];",
|
||||
"} [keyword else] [keyword if] ([number 2]) {",
|
||||
" [keyword continue];",
|
||||
"} [keyword else] {",
|
||||
" [number 10];",
|
||||
"}");
|
||||
|
||||
MT("indent_for",
|
||||
"[keyword for] ([keyword var] [variable i] [operator =] [number 0];",
|
||||
" [variable i] [operator <] [number 100];",
|
||||
" [variable i][operator ++])",
|
||||
" [variable doSomething]([variable i]);",
|
||||
"[keyword debugger];");
|
||||
|
||||
MT("indent_c_style",
|
||||
"[keyword function] [variable foo]()",
|
||||
"{",
|
||||
" [keyword debugger];",
|
||||
"}");
|
||||
|
||||
MT("multilinestring",
|
||||
"[keyword var] [variable x] [operator =] [string 'foo\\]",
|
||||
"[string bar'];");
|
||||
|
||||
MT("scary_regexp",
|
||||
"[string-2 /foo[[/]]bar/];");
|
||||
})();
|
||||
|
@ -59,13 +59,6 @@
|
||||
return {tokens: tokens, plain: plain};
|
||||
}
|
||||
|
||||
test.indentation = function(name, mode, tokens, modeName) {
|
||||
var data = parseTokens(tokens);
|
||||
return test((modeName || mode.name) + "_indent_" + name, function() {
|
||||
return compare(data.plain, data.tokens, mode, true);
|
||||
});
|
||||
};
|
||||
|
||||
test.mode = function(name, mode, tokens, modeName) {
|
||||
var data = parseTokens(tokens);
|
||||
return test((modeName || mode.name) + "_" + name, function() {
|
||||
@ -73,7 +66,11 @@
|
||||
});
|
||||
};
|
||||
|
||||
function compare(text, expected, mode, compareIndentation) {
|
||||
function esc(str) {
|
||||
return str.replace('&', '&').replace('<', '<');
|
||||
}
|
||||
|
||||
function compare(text, expected, mode) {
|
||||
|
||||
var expectedOutput = [];
|
||||
for (var i = 0; i < expected.length; i += 2) {
|
||||
@ -82,61 +79,49 @@
|
||||
expectedOutput.push(sty, expected[i + 1]);
|
||||
}
|
||||
|
||||
var observedOutput = highlight(text, mode, compareIndentation);
|
||||
var observedOutput = highlight(text, mode);
|
||||
|
||||
var pass, passStyle = "";
|
||||
pass = highlightOutputsEqual(expectedOutput, observedOutput);
|
||||
passStyle = pass ? 'mt-pass' : 'mt-fail';
|
||||
|
||||
var s = '';
|
||||
if (pass) {
|
||||
s += '<div class="mt-test ' + passStyle + '">';
|
||||
s += '<pre>' + text.replace('&', '&').replace('<', '<') + '</pre>';
|
||||
s += '<div class="cm-s-default">';
|
||||
s += prettyPrintOutputTable(observedOutput);
|
||||
s += '</div>';
|
||||
s += '</div>';
|
||||
return s;
|
||||
} else {
|
||||
s += '<div class="mt-test ' + passStyle + '">';
|
||||
s += '<pre>' + text.replace('&', '&').replace('<', '<') + '</pre>';
|
||||
var s = "";
|
||||
var diff = highlightOutputsDifferent(expectedOutput, observedOutput);
|
||||
if (diff != null) {
|
||||
s += '<div class="mt-test mt-fail">';
|
||||
s += '<pre>' + esc(text) + '</pre>';
|
||||
s += '<div class="cm-s-default">';
|
||||
s += 'expected:';
|
||||
s += prettyPrintOutputTable(expectedOutput);
|
||||
s += prettyPrintOutputTable(expectedOutput, diff);
|
||||
s += 'observed:';
|
||||
s += prettyPrintOutputTable(observedOutput);
|
||||
s += prettyPrintOutputTable(observedOutput, diff);
|
||||
s += '</div>';
|
||||
s += '</div>';
|
||||
throw s;
|
||||
}
|
||||
if (observedOutput.indentFailures) {
|
||||
for (var i = 0; i < observedOutput.indentFailures.length; i++)
|
||||
s += "<div class='mt-test mt-fail'>" + esc(observedOutput.indentFailures[i]) + "</div>";
|
||||
}
|
||||
if (s) throw new Failure(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emulation of CodeMirror's internal highlight routine for testing. Multi-line
|
||||
* input is supported.
|
||||
*
|
||||
* @param string to highlight
|
||||
*
|
||||
* @param mode the mode that will do the actual highlighting
|
||||
*
|
||||
* @return array of [style, token] pairs
|
||||
*/
|
||||
function highlight(string, mode, compareIndentation) {
|
||||
function highlight(string, mode) {
|
||||
var state = mode.startState()
|
||||
|
||||
var lines = string.replace(/\r\n/g,'\n').split('\n');
|
||||
var st = [], pos = 0;
|
||||
for (var i = 0; i < lines.length; ++i) {
|
||||
var line = lines[i], newLine = true;
|
||||
if (mode.indent) {
|
||||
var ws = line.match(/^\s*/)[0];
|
||||
var indent = mode.indent(state, line.slice(ws.length));
|
||||
if (indent != CodeMirror.Pass && indent != ws.length)
|
||||
(st.indentFailures || (st.indentFailures = [])).push(
|
||||
"Indentation of line " + (i + 1) + " is " + indent + " (expected " + ws.length + ")");
|
||||
}
|
||||
var stream = new CodeMirror.StringStream(line);
|
||||
if (line == "" && mode.blankLine) mode.blankLine(state);
|
||||
/* Start copied code from CodeMirror.highlight */
|
||||
while (!stream.eol()) {
|
||||
var compare = mode.token(stream, state), substr = stream.current();
|
||||
if(compareIndentation) compare = mode.indent(state) || null;
|
||||
else if (compare && compare.indexOf(" ") > -1) compare = compare.split(' ').sort().join(' ');
|
||||
|
||||
stream.start = stream.pos;
|
||||
var compare = mode.token(stream, state), substr = stream.current();
|
||||
if (compare && compare.indexOf(" ") > -1) compare = compare.split(' ').sort().join(' ');
|
||||
stream.start = stream.pos;
|
||||
if (pos && st[pos-2] == compare && !newLine) {
|
||||
st[pos-1] += substr;
|
||||
} else if (substr) {
|
||||
@ -154,39 +139,22 @@
|
||||
return st;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two arrays of output from highlight.
|
||||
*
|
||||
* @param o1 array of [style, token] pairs
|
||||
*
|
||||
* @param o2 array of [style, token] pairs
|
||||
*
|
||||
* @return boolean; true iff outputs equal
|
||||
*/
|
||||
function highlightOutputsEqual(o1, o2) {
|
||||
if (o1.length != o2.length) return false;
|
||||
for (var i = 0; i < o1.length; ++i)
|
||||
if (o1[i] != o2[i]) return false;
|
||||
return true;
|
||||
function highlightOutputsDifferent(o1, o2) {
|
||||
var minLen = Math.min(o1.length, o2.length);
|
||||
for (var i = 0; i < minLen; ++i)
|
||||
if (o1[i] != o2[i]) return i >> 1;
|
||||
if (o1.length > minLen || o2.length > minLen) return minLen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print tokens and corresponding styles in a table. Spaces in the token are
|
||||
* replaced with 'interpunct' dots (·).
|
||||
*
|
||||
* @param output array of [style, token] pairs
|
||||
*
|
||||
* @return html string
|
||||
*/
|
||||
function prettyPrintOutputTable(output) {
|
||||
function prettyPrintOutputTable(output, diffAt) {
|
||||
var s = '<table class="mt-output">';
|
||||
s += '<tr>';
|
||||
for (var i = 0; i < output.length; i += 2) {
|
||||
var style = output[i], val = output[i+1];
|
||||
s +=
|
||||
'<td class="mt-token">' +
|
||||
'<span class="cm-' + String(style).replace(/ +/g, " cm-") + '">' +
|
||||
val.replace(/ /g,'\xb7').replace('&', '&').replace('<', '<') +
|
||||
'<td class="mt-token"' + (i == diffAt * 2 ? " style='background: pink'" : "") + '>' +
|
||||
'<span class="cm-' + esc(String(style)) + '">' +
|
||||
esc(val.replace(/ /g,'\xb7')) +
|
||||
'</span>' +
|
||||
'</td>';
|
||||
}
|
||||
|
@ -436,6 +436,36 @@ testCM("markTextStayGone", function(cm) {
|
||||
eq(m1.find(), null);
|
||||
}, {value: "hello"});
|
||||
|
||||
testCM("markTextAllowEmpty", function(cm) {
|
||||
var m1 = cm.markText(Pos(0, 1), Pos(0, 2), {clearWhenEmpty: false});
|
||||
is(m1.find());
|
||||
cm.replaceRange("x", Pos(0, 0));
|
||||
is(m1.find());
|
||||
cm.replaceRange("y", Pos(0, 2));
|
||||
is(m1.find());
|
||||
cm.replaceRange("z", Pos(0, 3), Pos(0, 4));
|
||||
is(!m1.find());
|
||||
var m2 = cm.markText(Pos(0, 1), Pos(0, 2), {clearWhenEmpty: false,
|
||||
inclusiveLeft: true,
|
||||
inclusiveRight: true});
|
||||
cm.replaceRange("q", Pos(0, 1), Pos(0, 2));
|
||||
is(m2.find());
|
||||
cm.replaceRange("", Pos(0, 0), Pos(0, 3));
|
||||
is(!m2.find());
|
||||
var m3 = cm.markText(Pos(0, 1), Pos(0, 1), {clearWhenEmpty: false});
|
||||
cm.replaceRange("a", Pos(0, 3));
|
||||
is(m3.find());
|
||||
cm.replaceRange("b", Pos(0, 1));
|
||||
is(!m3.find());
|
||||
}, {value: "abcde"});
|
||||
|
||||
testCM("markTextStacked", function(cm) {
|
||||
var m1 = cm.markText(Pos(0, 0), Pos(0, 0), {clearWhenEmpty: false});
|
||||
var m2 = cm.markText(Pos(0, 0), Pos(0, 0), {clearWhenEmpty: false});
|
||||
cm.replaceRange("B", Pos(0, 1));
|
||||
is(m1.find() && m2.find());
|
||||
}, {value: "A"});
|
||||
|
||||
testCM("undoPreservesNewMarks", function(cm) {
|
||||
cm.markText(Pos(0, 3), Pos(0, 4));
|
||||
cm.markText(Pos(1, 1), Pos(1, 3));
|
||||
@ -712,8 +742,8 @@ testCM("collapsedRangeCoordsChar", function(cm) {
|
||||
var m1 = cm.markText(Pos(0, 0), Pos(2, 0), opts);
|
||||
eqPos(cm.coordsChar(pos_1_3), Pos(3, 3));
|
||||
m1.clear();
|
||||
var m1 = cm.markText(Pos(0, 0), Pos(1, 1), opts);
|
||||
var m2 = cm.markText(Pos(1, 1), Pos(2, 0), opts);
|
||||
var m1 = cm.markText(Pos(0, 0), Pos(1, 1), {collapsed: true, inclusiveLeft: true});
|
||||
var m2 = cm.markText(Pos(1, 1), Pos(2, 0), {collapsed: true, inclusiveRight: true});
|
||||
eqPos(cm.coordsChar(pos_1_3), Pos(3, 3));
|
||||
m1.clear(); m2.clear();
|
||||
var m1 = cm.markText(Pos(0, 0), Pos(1, 6), opts);
|
||||
@ -841,6 +871,23 @@ testCM("badNestedFold", function(cm) {
|
||||
is(/overlap/i.test(caught.message), "wrong error");
|
||||
});
|
||||
|
||||
testCM("nestedFoldOnSide", function(cm) {
|
||||
var m1 = cm.markText(Pos(0, 1), Pos(2, 1), {collapsed: true, inclusiveRight: true});
|
||||
var m2 = cm.markText(Pos(0, 1), Pos(0, 2), {collapsed: true});
|
||||
cm.markText(Pos(0, 1), Pos(0, 2), {collapsed: true}).clear();
|
||||
try { cm.markText(Pos(0, 1), Pos(0, 2), {collapsed: true, inclusiveLeft: true}); }
|
||||
catch(e) { var caught = e; }
|
||||
is(caught && /overlap/i.test(caught.message));
|
||||
var m3 = cm.markText(Pos(2, 0), Pos(2, 1), {collapsed: true});
|
||||
var m4 = cm.markText(Pos(2, 0), Pos(2, 1), {collapse: true, inclusiveRight: true});
|
||||
m1.clear(); m4.clear();
|
||||
m1 = cm.markText(Pos(0, 1), Pos(2, 1), {collapsed: true});
|
||||
cm.markText(Pos(2, 0), Pos(2, 1), {collapsed: true}).clear();
|
||||
try { cm.markText(Pos(2, 0), Pos(2, 1), {collapsed: true, inclusiveRight: true}); }
|
||||
catch(e) { var caught = e; }
|
||||
is(caught && /overlap/i.test(caught.message));
|
||||
}, {value: "ab\ncd\ef"});
|
||||
|
||||
testCM("wrappingInlineWidget", function(cm) {
|
||||
cm.setSize("11em");
|
||||
var w = document.createElement("span");
|
||||
@ -973,6 +1020,24 @@ testCM("moveVstuck", function(cm) {
|
||||
eqPos(cm.getCursor(), Pos(0, 26));
|
||||
}, {lineWrapping: true}, ie_lt8 || opera_lt10);
|
||||
|
||||
testCM("collapseOnMove", function(cm) {
|
||||
cm.setSelection(Pos(0, 1), Pos(2, 4));
|
||||
cm.execCommand("goLineUp");
|
||||
is(!cm.somethingSelected());
|
||||
eqPos(cm.getCursor(), Pos(0, 1));
|
||||
cm.setSelection(Pos(0, 1), Pos(2, 4));
|
||||
cm.execCommand("goPageDown");
|
||||
is(!cm.somethingSelected());
|
||||
eqPos(cm.getCursor(), Pos(2, 4));
|
||||
cm.execCommand("goLineUp");
|
||||
cm.execCommand("goLineUp");
|
||||
eqPos(cm.getCursor(), Pos(0, 4));
|
||||
cm.setSelection(Pos(0, 1), Pos(2, 4));
|
||||
cm.execCommand("goCharLeft");
|
||||
is(!cm.somethingSelected());
|
||||
eqPos(cm.getCursor(), Pos(0, 1));
|
||||
}, {value: "aaaaa\nb\nccccc"});
|
||||
|
||||
testCM("clickTab", function(cm) {
|
||||
var p0 = cm.charCoords(Pos(0, 0));
|
||||
eqPos(cm.coordsChar({left: p0.left + 5, top: p0.top + 5}), Pos(0, 0));
|
||||
@ -1141,7 +1206,7 @@ testCM("verticalMovementCommandsWrapping", function(cm) {
|
||||
|
||||
testCM("rtlMovement", function(cm) {
|
||||
forEach(["خحج", "خحabcخحج", "abخحخحجcd", "abخde", "abخح2342خ1حج", "خ1ح2خح3حxج",
|
||||
"خحcd", "1خحcd", "abcdeح1ج", "خمرحبها مها!", "foobarر",
|
||||
"خحcd", "1خحcd", "abcdeح1ج", "خمرحبها مها!", "foobarر", "خ ة ق",
|
||||
"<img src=\"/בדיקה3.jpg\">"], function(line) {
|
||||
var inv = line.charAt(0) == "خ";
|
||||
cm.setValue(line + "\n"); cm.execCommand(inv ? "goLineEnd" : "goLineStart");
|
||||
@ -1414,6 +1479,21 @@ testCM("dirtyBit", function(cm) {
|
||||
eq(cm.isClean(), true);
|
||||
});
|
||||
|
||||
testCM("changeGeneration", function(cm) {
|
||||
cm.replaceSelection("x", null, "+insert");
|
||||
var softGen = cm.changeGeneration();
|
||||
cm.replaceSelection("x", null, "+insert");
|
||||
cm.undo();
|
||||
eq(cm.getValue(), "");
|
||||
is(!cm.isClean(softGen));
|
||||
cm.replaceSelection("x", null, "+insert");
|
||||
var hardGen = cm.changeGeneration(true);
|
||||
cm.replaceSelection("x", null, "+insert");
|
||||
cm.undo();
|
||||
eq(cm.getValue(), "x");
|
||||
is(cm.isClean(hardGen));
|
||||
});
|
||||
|
||||
testCM("addKeyMap", function(cm) {
|
||||
function sendKey(code) {
|
||||
cm.triggerOnKeyDown({type: "keydown", keyCode: code,
|
||||
@ -1560,3 +1640,22 @@ testCM("lineStyleFromMode", function(cm) {
|
||||
eq(parenElts[0].nodeName, "DIV");
|
||||
is(!/parens.*parens/.test(parenElts[0].className));
|
||||
}, {value: "line1: [br] [br]\nline2: (par) (par)\nline3: nothing"});
|
||||
|
||||
CodeMirror.registerHelper("xxx", "a", "A");
|
||||
CodeMirror.registerHelper("xxx", "b", "B");
|
||||
CodeMirror.defineMode("yyy", function() {
|
||||
return {
|
||||
token: function(stream) { stream.skipToEnd(); },
|
||||
xxx: ["a", "b", "q"]
|
||||
};
|
||||
});
|
||||
CodeMirror.registerGlobalHelper("xxx", "c", function(m) { return m.enableC; }, "C");
|
||||
|
||||
testCM("helpers", function(cm) {
|
||||
cm.setOption("mode", "yyy");
|
||||
eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), "A/B");
|
||||
cm.setOption("mode", {name: "yyy", modeProps: {xxx: "b", enableC: true}});
|
||||
eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), "B/C");
|
||||
cm.setOption("mode", "javascript");
|
||||
eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), "");
|
||||
});
|
||||
|
@ -96,6 +96,12 @@ function copyCursor(cur) {
|
||||
return { ch: cur.ch, line: cur.line };
|
||||
}
|
||||
|
||||
function forEach(arr, func) {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
func(arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function testVim(name, run, opts, expectedFail) {
|
||||
var vimOpts = {
|
||||
lineNumbers: true,
|
||||
@ -189,7 +195,7 @@ function testVim(name, run, opts, expectedFail) {
|
||||
run(cm, vim, helpers);
|
||||
successful = true;
|
||||
} finally {
|
||||
if ((debug && !successful) || verbose) {
|
||||
if (!successful || verbose) {
|
||||
place.style.visibility = "visible";
|
||||
} else {
|
||||
place.removeChild(cm.getWrapperElement());
|
||||
@ -1047,7 +1053,7 @@ testVim('ctrl-x', function(cm, vim, helpers) {
|
||||
eq('-3', cm.getValue());
|
||||
}, {value: '0'});
|
||||
testVim('<C-x>/<C-a> search forward', function(cm, vim, helpers) {
|
||||
['<C-x>', '<C-a>'].forEach(function(key) {
|
||||
forEach(['<C-x>', '<C-a>'], function(key) {
|
||||
cm.setCursor(0, 0);
|
||||
helpers.doKeys(key);
|
||||
helpers.assertCursorAt(0, 5);
|
||||
@ -1997,6 +2003,33 @@ testVim('zt==z<CR>', function(cm, vim, helpers){
|
||||
eq(zVals[2], zVals[5]);
|
||||
});
|
||||
|
||||
var scrollMotionSandbox =
|
||||
'\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n'
|
||||
'\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n'
|
||||
'\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n'
|
||||
'\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n';
|
||||
testVim('scrollMotion', function(cm, vim, helpers){
|
||||
var prevCursor, prevScrollInfo;
|
||||
cm.setCursor(0, 0);
|
||||
// ctrl-y at the top of the file should have no effect.
|
||||
helpers.doKeys('<C-y>');
|
||||
eq(0, cm.getCursor().line);
|
||||
prevScrollInfo = cm.getScrollInfo();
|
||||
helpers.doKeys('<C-e>');
|
||||
eq(1, cm.getCursor().line);
|
||||
eq(true, prevScrollInfo.top < cm.getScrollInfo().top);
|
||||
// Jump to the end of the sandbox.
|
||||
cm.setCursor(1000, 0);
|
||||
prevCursor = cm.getCursor();
|
||||
// ctrl-e at the bottom of the file should have no effect.
|
||||
helpers.doKeys('<C-e>');
|
||||
eq(prevCursor.line, cm.getCursor().line);
|
||||
prevScrollInfo = cm.getScrollInfo();
|
||||
helpers.doKeys('<C-y>');
|
||||
eq(prevCursor.line - 1, cm.getCursor().line);
|
||||
eq(true, prevScrollInfo.top > cm.getScrollInfo().top);
|
||||
}, { value: scrollMotionSandbox});
|
||||
|
||||
var squareBracketMotionSandbox = ''+
|
||||
'({\n'+//0
|
||||
' ({\n'+//11
|
||||
@ -2080,7 +2113,7 @@ testVim('[(, ])', function(cm, vim, helpers) {
|
||||
helpers.assertCursorAt(8,0);
|
||||
}, { value: squareBracketMotionSandbox});
|
||||
testVim('[*, ]*, [/, ]/', function(cm, vim, helpers) {
|
||||
['*', '/'].forEach(function(key){
|
||||
forEach(['*', '/'], function(key){
|
||||
cm.setCursor(7, 0);
|
||||
helpers.doKeys('2', '[', key);
|
||||
helpers.assertCursorAt(2,2);
|
||||
@ -2368,6 +2401,26 @@ testVim('ex_map_key2ex', function(cm, vim, helpers) {
|
||||
eq(written, true);
|
||||
eq(actualCm, cm);
|
||||
});
|
||||
testVim('ex_map_key2key_visual_api', function(cm, vim, helpers) {
|
||||
CodeMirror.Vim.map('b', ':w', 'visual');
|
||||
var tmp = CodeMirror.commands.save;
|
||||
var written = false;
|
||||
var actualCm;
|
||||
CodeMirror.commands.save = function(cm) {
|
||||
written = true;
|
||||
actualCm = cm;
|
||||
};
|
||||
// Mapping should not work in normal mode.
|
||||
helpers.doKeys('b');
|
||||
eq(written, false);
|
||||
// Mapping should work in visual mode.
|
||||
helpers.doKeys('v', 'b');
|
||||
eq(written, true);
|
||||
eq(actualCm, cm);
|
||||
|
||||
CodeMirror.commands.save = tmp;
|
||||
});
|
||||
|
||||
// Testing registration of functions as ex-commands and mapping to <Key>-keys
|
||||
testVim('ex_api_test', function(cm, vim, helpers) {
|
||||
var res=false;
|
||||
|
@ -100,12 +100,15 @@
|
||||
progress = document.getElementById("progress"),
|
||||
progressRan = document.getElementById("progress_ran").childNodes[0],
|
||||
progressTotal = document.getElementById("progress_total").childNodes[0];
|
||||
|
||||
var count = 0,
|
||||
failed = 0,
|
||||
skipped = 0,
|
||||
bad = "",
|
||||
running = false, // Flag that states tests are running
|
||||
quit = false, // Flag to quit tests ASAP
|
||||
verbose = false; // Adds message for *every* test to output
|
||||
quit = false, // Flag to quit tests ASAP
|
||||
verbose = false, // Adds message for *every* test to output
|
||||
phantom = false;
|
||||
|
||||
function runHarness(){
|
||||
if (running) {
|
||||
@ -114,19 +117,25 @@
|
||||
setTimeout(function(){runHarness();}, 500);
|
||||
return;
|
||||
}
|
||||
filters = [];
|
||||
verbose = false;
|
||||
if (window.location.hash.substr(1)){
|
||||
debug = window.location.hash.substr(1).split(",");
|
||||
} else {
|
||||
debug = null;
|
||||
var strings = window.location.hash.substr(1).split(",");
|
||||
while (strings.length) {
|
||||
var s = strings.shift();
|
||||
if (s === "verbose")
|
||||
verbose = true;
|
||||
else
|
||||
filters.push(parseTestFilter(decodeURIComponent(s)));
|
||||
}
|
||||
}
|
||||
quit = false;
|
||||
running = true;
|
||||
setStatus("Loading tests...");
|
||||
count = 0;
|
||||
failed = 0;
|
||||
skipped = 0;
|
||||
bad = "";
|
||||
verbose = false;
|
||||
debugUsed = Array();
|
||||
totalTests = countTests();
|
||||
progressTotal.nodeValue = " of " + totalTests;
|
||||
progressRan.nodeValue = count;
|
||||
@ -159,12 +168,16 @@
|
||||
}
|
||||
function displayTest(type, name, customMessage) {
|
||||
var message = "???";
|
||||
if (type != "done") ++count;
|
||||
if (type != "done" && type != "skipped") ++count;
|
||||
progress.style.width = (count * (progress.parentNode.clientWidth - 2) / totalTests) + "px";
|
||||
progressRan.nodeValue = count;
|
||||
if (type == "ok") {
|
||||
message = "Test '" + name + "' succeeded";
|
||||
if (!verbose) customMessage = false;
|
||||
} else if (type == "skipped") {
|
||||
message = "Test '" + name + "' skipped";
|
||||
++skipped;
|
||||
if (!verbose) customMessage = false;
|
||||
} else if (type == "expected") {
|
||||
message = "Test '" + name + "' failed as expected";
|
||||
if (!verbose) customMessage = false;
|
||||
@ -182,15 +195,11 @@
|
||||
} else {
|
||||
type += " ok";
|
||||
message = "All passed";
|
||||
if (skipped) {
|
||||
message += " (" + skipped + " skipped)";
|
||||
}
|
||||
}
|
||||
if (debug && debug.length) {
|
||||
var bogusTests = totalTests - count;
|
||||
message += " — " + bogusTests + " nonexistent test" +
|
||||
(bogusTests > 1 ? "s" : "") + " requested by location.hash: " +
|
||||
"`" + debug.join("`, `") + "`";
|
||||
} else {
|
||||
progressTotal.nodeValue = '';
|
||||
}
|
||||
progressTotal.nodeValue = '';
|
||||
customMessage = true; // Hack to avoid adding to output
|
||||
}
|
||||
if (verbose && !customMessage) customMessage = message;
|
||||
|
@ -100,8 +100,10 @@
|
||||
progress = document.getElementById("progress"),
|
||||
progressRan = document.getElementById("progress_ran").childNodes[0],
|
||||
progressTotal = document.getElementById("progress_total").childNodes[0];
|
||||
|
||||
var count = 0,
|
||||
failed = 0,
|
||||
skipped = 0,
|
||||
bad = "",
|
||||
running = false, // Flag that states tests are running
|
||||
quit = false, // Flag to quit tests ASAP
|
||||
@ -115,19 +117,25 @@
|
||||
setTimeout(function(){runHarness();}, 500);
|
||||
return;
|
||||
}
|
||||
filters = [];
|
||||
verbose = false;
|
||||
if (window.location.hash.substr(1)){
|
||||
debug = window.location.hash.substr(1).split(",");
|
||||
} else {
|
||||
debug = null;
|
||||
var strings = window.location.hash.substr(1).split(",");
|
||||
while (strings.length) {
|
||||
var s = strings.shift();
|
||||
if (s === "verbose")
|
||||
verbose = true;
|
||||
else
|
||||
filters.push(parseTestFilter(decodeURIComponent(s)));
|
||||
}
|
||||
}
|
||||
quit = false;
|
||||
running = true;
|
||||
setStatus("Loading tests...");
|
||||
count = 0;
|
||||
failed = 0;
|
||||
skipped = 0;
|
||||
bad = "";
|
||||
verbose = false;
|
||||
debugUsed = Array();
|
||||
totalTests = countTests();
|
||||
progressTotal.nodeValue = " of " + totalTests;
|
||||
progressRan.nodeValue = count;
|
||||
@ -160,12 +168,16 @@
|
||||
}
|
||||
function displayTest(type, name, customMessage) {
|
||||
var message = "???";
|
||||
if (type != "done") ++count;
|
||||
if (type != "done" && type != "skipped") ++count;
|
||||
progress.style.width = (count * (progress.parentNode.clientWidth - 2) / totalTests) + "px";
|
||||
progressRan.nodeValue = count;
|
||||
if (type == "ok") {
|
||||
message = "Test '" + name + "' succeeded";
|
||||
if (!verbose) customMessage = false;
|
||||
} else if (type == "skipped") {
|
||||
message = "Test '" + name + "' skipped";
|
||||
++skipped;
|
||||
if (!verbose) customMessage = false;
|
||||
} else if (type == "expected") {
|
||||
message = "Test '" + name + "' failed as expected";
|
||||
if (!verbose) customMessage = false;
|
||||
@ -183,15 +195,11 @@
|
||||
} else {
|
||||
type += " ok";
|
||||
message = "All passed";
|
||||
if (skipped) {
|
||||
message += " (" + skipped + " skipped)";
|
||||
}
|
||||
}
|
||||
if (debug && debug.length) {
|
||||
var bogusTests = totalTests - count;
|
||||
message += " — " + bogusTests + " nonexistent test" +
|
||||
(bogusTests > 1 ? "s" : "") + " requested by location.hash: " +
|
||||
"`" + debug.join("`, `") + "`";
|
||||
} else {
|
||||
progressTotal.nodeValue = '';
|
||||
}
|
||||
progressTotal.nodeValue = '';
|
||||
customMessage = true; // Hack to avoid adding to output
|
||||
}
|
||||
if (verbose && !customMessage) customMessage = message;
|
||||
|
Loading…
Reference in New Issue
Block a user