Merge fx-team to m-c

This commit is contained in:
Wes Kocher 2013-11-22 15:41:13 -08:00
commit 86e4fa5ab2
18 changed files with 835 additions and 262 deletions

View File

@ -145,11 +145,10 @@
<body><![CDATA[
// we always set the src on click if it has not been set for this tab,
// but we only want to open the panel if it was previously annotated.
let openPanel = this.isMarked || aOpenPanel;
let openPanel = this.isMarked || aOpenPanel || !this.provider.haveLoggedInUser();
let src = this.getAttribute("src");
if (!src || src == "about:blank") {
this.loadPanel();
this.isMarked = true;
}
if (openPanel)
this.panel.openPopup(this, "bottomcenter topright", 0, 0, false, false);
@ -170,7 +169,6 @@
// previous url when it is closed.
this.setAttribute("src", "about:blank");
this.loadPanel({ url: aUrl });
this.isMarked = true;
this.panel.openPopup(this, "bottomcenter topright", 0, 0, false, false);
this.panel.addEventListener("popuphidden", function _hidden() {
this.panel.removeEventListener("popuphidden", _hidden);

View File

@ -199,6 +199,7 @@ var tests = {
EventUtils.synthesizeMouseAtCenter(btn, {});
// wait for the button to be marked, click to open panel
waitForCondition(function() btn.isMarked, function() {
is(btn.panel.state, "closed", "panel should not be visible yet");
EventUtils.synthesizeMouseAtCenter(btn, {});
}, "button is marked");
break;
@ -214,7 +215,69 @@ var tests = {
// page should no longer be marked
port.close();
waitForCondition(function() !btn.isMarked, function() {
// after closing the addons tab, verify provider is still installed
// cleanup after the page has been unmarked
gBrowser.tabContainer.addEventListener("TabClose", function onTabClose() {
gBrowser.tabContainer.removeEventListener("TabClose", onTabClose);
executeSoon(function () {
ok(btn.disabled, "button is disabled");
next();
});
});
gBrowser.removeTab(tab);
}, "button unmarked");
}
break;
}
};
port.postMessage({topic: "test-init"});
});
},
testMarkPanelLoggedOut: function(next) {
// click on panel to open and wait for visibility
let provider = Social._getProviderFromOrigin(manifest2.origin);
ok(provider.enabled, "provider is enabled");
let id = "social-mark-button-" + provider.origin;
let btn = document.getElementById(id)
ok(btn, "got a mark button");
let port = provider.getWorkerPort();
ok(port, "got a port");
// verify markbutton is disabled when there is no browser url
ok(btn.disabled, "button is disabled");
let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
addTab(activationURL, function(tab) {
ok(!btn.disabled, "button is enabled");
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "test-init-done":
ok(true, "test-init-done received");
ok(provider.profile.userName, "profile was set by test worker");
port.postMessage({topic: "test-logout"});
waitForCondition(function() !provider.profile.userName,
function() {
// when the provider has not indicated to us that a user is
// logged in, the first click opens the page.
EventUtils.synthesizeMouseAtCenter(btn, {});
},
"profile was unset by test worker");
break;
case "got-social-panel-visibility":
ok(true, "got the panel message " + e.data.result);
if (e.data.result == "shown") {
// our test marks the page during the load event (see
// social_mark.html) regardless of login state, unmark the page
// via the button in the page
let doc = btn.contentDocument;
let unmarkBtn = doc.getElementById("unmark");
ok(unmarkBtn, "got the panel unmark button");
EventUtils.sendMouseEvent({type: "click"}, unmarkBtn, btn.contentWindow);
} else {
// page should no longer be marked
port.close();
waitForCondition(function() !btn.isMarked, function() {
// cleanup after the page has been unmarked
gBrowser.tabContainer.addEventListener("TabClose", function onTabClose() {
gBrowser.tabContainer.removeEventListener("TabClose", onTabClose);
executeSoon(function () {

View File

@ -59,6 +59,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils",
"resource://gre/modules/DownloadUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS",
"resource://gre/modules/osfile.jsm")
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
@ -668,6 +670,27 @@ DownloadsDataCtor.prototype = {
Cu.reportError(ex);
}
}
// This state transition code should actually be located in a Downloads
// API module (bug 941009). Moreover, the fact that state is stored as
// annotations should be ideally hidden behind methods of
// nsIDownloadHistory (bug 830415).
if (!this._isPrivate && !aDataItem.inProgress) {
try {
let downloadMetaData = { state: aDataItem.state,
endTime: aDataItem.endTime };
if (aDataItem.done) {
downloadMetaData.fileSize = aDataItem.maxBytes;
}
PlacesUtils.annotations.setPageAnnotation(
NetUtil.newURI(aDataItem.uri), "downloads/metaData",
JSON.stringify(downloadMetaData), 0,
PlacesUtils.annotations.EXPIRE_WITH_HISTORY);
} catch (ex) {
Cu.reportError(ex);
}
}
}
if (!aDataItem.newDownloadNotified) {
@ -858,10 +881,26 @@ DownloadsDataItem.prototype = {
this.referrer = this._download.source.referrer;
this.startTime = this._download.startTime;
this.currBytes = this._download.currentBytes;
this.maxBytes = this._download.totalBytes;
this.resumable = this._download.hasPartialData;
this.speed = this._download.speed;
this.percentComplete = this._download.progress;
if (this._download.succeeded) {
// If the download succeeded, show the final size if available, otherwise
// use the last known number of bytes transferred. The final size on disk
// will be available when bug 941063 is resolved.
this.maxBytes = this._download.hasProgress ?
this._download.totalBytes :
this._download.currentBytes;
this.percentComplete = 100;
} else if (this._download.hasProgress) {
// If the final size and progress are known, use them.
this.maxBytes = this._download.totalBytes;
this.percentComplete = this._download.progress;
} else {
// The download final size and progress percentage is unknown.
this.maxBytes = -1;
this.percentComplete = -1;
}
},
/**

View File

@ -5,7 +5,7 @@ code, and optionally help with indentation.
# Upgrade
Currently used version is 3.15. To upgrade, download a new version of
Currently used version is 3.20. To upgrade, download a new version of
CodeMirror from the project's page [1] and replace all JavaScript and
CSS files inside the codemirror directory [2].

View File

@ -74,7 +74,6 @@
.cm-s-default .cm-string {color: #a11;}
.cm-s-default .cm-string-2 {color: #f50;}
.cm-s-default .cm-meta {color: #555;}
.cm-s-default .cm-error {color: #f00;}
.cm-s-default .cm-qualifier {color: #555;}
.cm-s-default .cm-builtin {color: #30a;}
.cm-s-default .cm-bracket {color: #997;}
@ -91,10 +90,12 @@
.cm-em {font-style: italic;}
.cm-link {text-decoration: underline;}
.cm-s-default .cm-error {color: #f00;}
.cm-invalidchar {color: #f00;}
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
.CodeMirror-activeline-background {background: #e8f2ff;}
/* STOP */
@ -117,6 +118,8 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
height: 100%;
outline: none; /* Prevent dragging from highlighting the element */
position: relative;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
.CodeMirror-sizer {
position: relative;
@ -155,6 +158,8 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
.CodeMirror-gutter {
white-space: normal;
height: 100%;
-moz-box-sizing: content-box;
box-sizing: content-box;
padding-bottom: 30px;
margin-bottom: -32px;
display: inline-block;
@ -214,8 +219,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
overflow: auto;
}
.CodeMirror-widget {
}
.CodeMirror-widget {}
.CodeMirror-wrap .CodeMirror-scroll {
overflow-x: hidden;
@ -223,7 +227,8 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
.CodeMirror-measure {
position: absolute;
width: 100%; height: 0px;
width: 100%;
height: 0;
overflow: hidden;
visibility: hidden;
}

View File

@ -1,5 +1,6 @@
// CodeMirror version 3.15
// CodeMirror version 3.20
//
// CodeMirror is the only global var we claim
window.CodeMirror = (function() {
"use strict";
@ -8,9 +9,13 @@ window.CodeMirror = (function() {
// Crude, but necessary to handle a number of hard-to-feature-detect
// bugs and behavior differences.
var gecko = /gecko\/\d/i.test(navigator.userAgent);
// 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 ie_gt10 = /Trident\/([7-9]|\d{2,})\./.test(navigator.userAgent);
var webkit = /WebKit\//.test(navigator.userAgent);
var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
var chrome = /Chrome\//.test(navigator.userAgent);
@ -25,7 +30,7 @@ window.CodeMirror = (function() {
// This is woefully incomplete. Suggestions for alternative methods welcome.
var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
var mac = ios || /Mac/.test(navigator.platform);
var windows = /windows/i.test(navigator.platform);
var windows = /win/i.test(navigator.platform);
var opera_version = opera && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
if (opera_version) opera_version = Number(opera_version[1]);
@ -305,15 +310,13 @@ window.CodeMirror = (function() {
// Make sure the gutters options contains the element
// "CodeMirror-linenumbers" when the lineNumbers option is true.
function setGuttersForLineNumbers(options) {
var found = false;
for (var i = 0; i < options.gutters.length; ++i) {
if (options.gutters[i] == "CodeMirror-linenumbers") {
if (options.lineNumbers) found = true;
else options.gutters.splice(i--, 1);
}
var found = indexOf(options.gutters, "CodeMirror-linenumbers");
if (found == -1 && options.lineNumbers) {
options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
} else if (found > -1 && !options.lineNumbers) {
options.gutters = options.gutters.slice(0);
options.gutters.splice(found, 1);
}
if (!found && options.lineNumbers)
options.gutters.push("CodeMirror-linenumbers");
}
// SCROLLBARS
@ -333,13 +336,19 @@ window.CodeMirror = (function() {
d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
d.scrollbarV.firstChild.style.height =
(scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
} else d.scrollbarV.style.display = "";
} else {
d.scrollbarV.style.display = "";
d.scrollbarV.firstChild.style.height = "0";
}
if (needsH) {
d.scrollbarH.style.display = "block";
d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0";
d.scrollbarH.firstChild.style.width =
(d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px";
} else d.scrollbarH.style.display = "";
} else {
d.scrollbarH.style.display = "";
d.scrollbarH.firstChild.style.width = "0";
}
if (needsH && needsV) {
d.scrollbarFiller.style.display = "block";
d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
@ -350,8 +359,10 @@ window.CodeMirror = (function() {
d.gutterFiller.style.width = d.gutters.offsetWidth + "px";
} else d.gutterFiller.style.display = "";
if (mac_geLion && scrollbarWidth(d.measure) === 0)
if (mac_geLion && scrollbarWidth(d.measure) === 0) {
d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
d.scrollbarV.style.pointerEvents = d.scrollbarH.style.pointerEvents = "none";
}
}
function visibleLines(display, doc, viewPort) {
@ -406,12 +417,18 @@ window.CodeMirror = (function() {
function updateDisplay(cm, changes, viewPort, forced) {
var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated;
var visible = visibleLines(cm.display, cm.doc, viewPort);
for (;;) {
for (var first = true;; first = false) {
var oldWidth = cm.display.scroller.clientWidth;
if (!updateDisplayInner(cm, changes, visible, forced)) break;
forced = false;
updated = true;
changes = [];
updateSelection(cm);
updateScrollbars(cm);
if (first && cm.options.lineWrapping && oldWidth != cm.display.scroller.clientWidth) {
forced = true;
continue;
}
forced = false;
// Clip forced viewport to actual scrollable area
if (viewPort)
@ -420,7 +437,6 @@ window.CodeMirror = (function() {
visible = visibleLines(cm.display, cm.doc, viewPort);
if (visible.from >= cm.display.showingFrom && visible.to <= cm.display.showingTo)
break;
changes = [];
}
if (updated) {
@ -456,7 +472,7 @@ window.CodeMirror = (function() {
var positionsChangedFrom = Infinity;
if (cm.options.lineNumbers)
for (var i = 0; i < changes.length; ++i)
if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; }
if (changes[i].diff && changes[i].from < positionsChangedFrom) { positionsChangedFrom = changes[i].from; }
var end = doc.first + doc.size;
var from = Math.max(visible.from - cm.options.viewportMargin, doc.first);
@ -613,7 +629,7 @@ window.CodeMirror = (function() {
if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift();
if (lineIsHidden(cm.doc, line)) {
if (line.height != 0) updateLineHeight(line, 0);
if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i) {
if (line.widgets && cur && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i) {
var w = line.widgets[i];
if (w.showIfHidden) {
var prev = cur.previousSibling;
@ -658,10 +674,11 @@ window.CodeMirror = (function() {
}
function buildLineElement(cm, line, lineNo, dims, reuse) {
var lineElement = lineContent(cm, line);
var built = buildLineContent(cm, line), lineElement = built.pre;
var markers = line.gutterMarkers, display = cm.display, wrap;
if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass && !line.widgets)
var bgClass = built.bgClass ? built.bgClass + " " + (line.bgClass || "") : line.bgClass;
if (!cm.options.lineNumbers && !markers && !bgClass && !line.wrapClass && !line.widgets)
return lineElement;
// Lines with gutter elements, widgets or a background class need
@ -699,8 +716,8 @@ window.CodeMirror = (function() {
wrap.appendChild(lineElement);
}
// Kludge to make sure the styled element lies behind the selection (by z-index)
if (line.bgClass)
wrap.insertBefore(elt("div", null, line.bgClass + " CodeMirror-linebackground"), wrap.firstChild);
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: " +
(cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
@ -872,9 +889,10 @@ window.CodeMirror = (function() {
clearInterval(display.blinker);
var on = true;
display.cursor.style.visibility = display.otherCursor.style.visibility = "";
display.blinker = setInterval(function() {
display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
}, cm.options.cursorBlinkRate);
if (cm.options.cursorBlinkRate > 0)
display.blinker = setInterval(function() {
display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
}, cm.options.cursorBlinkRate);
}
// HIGHLIGHT WORKER
@ -894,7 +912,7 @@ window.CodeMirror = (function() {
doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.showingTo + 500), function(line) {
if (doc.frontier >= cm.display.showingFrom) { // Visible
var oldStyles = line.styles;
line.styles = highlightLine(cm, line, state);
line.styles = highlightLine(cm, line, state, true);
var ischange = !oldStyles || oldStyles.length != line.styles.length;
for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
if (ischange) {
@ -903,7 +921,7 @@ window.CodeMirror = (function() {
}
line.stateAfter = copyState(doc.mode, state);
} else {
processLine(cm, line, state);
processLine(cm, line.text, state);
line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
}
++doc.frontier;
@ -926,7 +944,8 @@ window.CodeMirror = (function() {
// parse correctly.
function findStartLine(cm, n, precise) {
var minindent, minline, doc = cm.doc;
for (var search = n, lim = n - 100; search > lim; --search) {
var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
for (var search = n; search > lim; --search) {
if (search <= doc.first) return doc.first;
var line = getLine(doc, search - 1);
if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
@ -941,16 +960,17 @@ window.CodeMirror = (function() {
function getStateBefore(cm, n, precise) {
var doc = cm.doc, display = cm.display;
if (!doc.mode.startState) return true;
if (!doc.mode.startState) return true;
var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
if (!state) state = startState(doc.mode);
else state = copyState(doc.mode, state);
doc.iter(pos, n, function(line) {
processLine(cm, line, state);
processLine(cm, line.text, state);
var save = pos == n - 1 || pos % 5 == 0 || pos >= display.showingFrom && pos < display.showingTo;
line.stateAfter = save ? copyState(doc.mode, state) : null;
++pos;
});
if (precise) doc.frontier = pos;
return state;
}
@ -966,6 +986,10 @@ window.CodeMirror = (function() {
function measureChar(cm, line, ch, data, bias) {
var dir = -1;
data = data || measureLine(cm, line);
if (data.crude) {
var left = data.left + ch * data.width;
return {left: left, right: left + data.width, top: data.top, bottom: data.bottom};
}
for (var pos = ch;; pos += dir) {
var r = data[pos];
@ -987,7 +1011,7 @@ window.CodeMirror = (function() {
var memo = cache[i];
if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
cm.display.scroller.clientWidth == memo.width &&
memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapClass)
memo.classes == line.textClass + "|" + line.wrapClass)
return memo;
}
}
@ -1007,15 +1031,18 @@ window.CodeMirror = (function() {
var cache = cm.display.measureLineCache;
var memo = {text: line.text, width: cm.display.scroller.clientWidth,
markedSpans: line.markedSpans, measure: measure,
classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass};
classes: line.textClass + "|" + line.wrapClass};
if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
else cache.push(memo);
return measure;
}
function measureLineInner(cm, line) {
if (!cm.options.lineWrapping && line.text.length >= cm.options.crudeMeasuringFrom)
return crudelyMeasureLine(cm, line);
var display = cm.display, measure = emptyArray(line.text.length);
var pre = lineContent(cm, line, measure, true);
var pre = buildLineContent(cm, line, measure, true).pre;
// IE does not cache element positions of inline elements between
// calls to getBoundingClientRect. This makes the loop below,
@ -1091,6 +1118,7 @@ window.CodeMirror = (function() {
if (cur.measureRight) rect.right = getRect(cur.measureRight).left;
if (cur.leftSide) rect.leftSide = measureRect(getRect(cur.leftSide));
}
removeChildren(cm.display.measure);
for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
finishRect(cur);
if (cur.leftSide) finishRect(cur.leftSide);
@ -1099,6 +1127,15 @@ window.CodeMirror = (function() {
return data;
}
function crudelyMeasureLine(cm, line) {
var copy = new Line(line.text.slice(0, 100), null);
if (line.textClass) copy.textClass = line.textClass;
var measure = measureLineInner(cm, copy);
var left = measureChar(cm, copy, 0, measure, "left");
var right = measureChar(cm, copy, 99, measure, "right");
return {crude: true, top: left.top, left: left.left, bottom: left.bottom, width: (right.right - left.left) / 100};
}
function measureLineWidth(cm, line) {
var hasBadSpan = false;
if (line.markedSpans) for (var i = 0; i < line.markedSpans; ++i) {
@ -1106,9 +1143,10 @@ window.CodeMirror = (function() {
if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
}
var cached = !hasBadSpan && findCachedMeasurement(cm, line);
if (cached) return measureChar(cm, line, line.text.length, cached.measure, "right").right;
if (cached || line.text.length >= cm.options.crudeMeasuringFrom)
return measureChar(cm, line, line.text.length, cached && cached.measure, "right").right;
var pre = lineContent(cm, line, null, true);
var pre = buildLineContent(cm, line, null, true).pre;
var end = pre.appendChild(zeroWidthElement(cm.display.measure));
removeChildrenAndAdd(cm.display.measure, pre);
return getRect(end).right - getRect(cm.display.lineDiv).left;
@ -1351,11 +1389,14 @@ window.CodeMirror = (function() {
}
if (!updated && op.selectionChanged) updateSelection(cm);
if (op.updateScrollPos) {
display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop;
display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft;
var top = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, newScrollPos.scrollTop));
var left = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, newScrollPos.scrollLeft));
display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = top;
display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = left;
alignHorizontally(cm);
if (op.scrollToPos)
scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos), op.scrollToPosMargin);
scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos.from),
clipPos(cm.doc, op.scrollToPos.to), op.scrollToPos.margin);
} else if (newScrollPos) {
scrollCursorIntoView(cm);
}
@ -1443,6 +1484,10 @@ window.CodeMirror = (function() {
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.pasteIncoming && cm.state.fakedLastChar) {
input.value = input.value.substring(0, input.value.length - 1);
cm.state.fakedLastChar = false;
}
var text = input.value;
if (text == prevInput && posEq(sel.from, sel.to)) return false;
if (ie && !ie_lt9 && cm.display.inputHasSelection === text) {
@ -1573,7 +1618,10 @@ window.CodeMirror = (function() {
if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
if (e.keyCode == 16) cm.doc.sel.shift = false;
}));
on(d.input, "input", bind(fastPoll, cm));
on(d.input, "input", function() {
if (ie && !ie_lt9 && cm.display.inputHasSelection) cm.display.inputHasSelection = null;
fastPoll(cm);
});
on(d.input, "keydown", operation(cm, onKeyDown));
on(d.input, "keypress", operation(cm, onKeyPress));
on(d.input, "focus", bind(onFocus, cm));
@ -1589,12 +1637,22 @@ window.CodeMirror = (function() {
on(d.scroller, "dragover", drag_);
on(d.scroller, "drop", operation(cm, onDrop));
}
on(d.scroller, "paste", function(e){
on(d.scroller, "paste", function(e) {
if (eventInWidget(d, e)) return;
focusInput(cm);
fastPoll(cm);
});
on(d.input, "paste", function() {
// Workaround for webkit bug https://bugs.webkit.org/show_bug.cgi?id=90206
// Add a char to the end of textarea before paste occur so that
// selection doesn't span to the end of textarea.
if (webkit && !cm.state.fakedLastChar && !(new Date - cm.state.lastMiddleDown < 200)) {
var start = d.input.selectionStart, end = d.input.selectionEnd;
d.input.value += "$";
d.input.selectionStart = start;
d.input.selectionEnd = end;
cm.state.fakedLastChar = true;
}
cm.state.pasteIncoming = true;
fastPoll(cm);
});
@ -1658,6 +1716,7 @@ window.CodeMirror = (function() {
if (captureMiddleClick) onContextMenu.call(cm, cm, e);
return;
case 2:
if (webkit) cm.state.lastMiddleDown = +new Date;
if (start) extendSelection(cm.doc, start);
setTimeout(bind(focusInput, cm), 20);
e_preventDefault(e);
@ -1778,17 +1837,16 @@ window.CodeMirror = (function() {
on(document, "mouseup", up);
}
function clickInGutter(cm, e) {
var display = cm.display;
function gutterEvent(cm, e, type, prevent, signalfn) {
try { var mX = e.clientX, mY = e.clientY; }
catch(e) { return false; }
if (mX >= Math.floor(getRect(cm.display.gutters).right)) return false;
if (prevent) e_preventDefault(e);
if (mX >= Math.floor(getRect(display.gutters).right)) return false;
e_preventDefault(e);
if (!hasHandler(cm, "gutterClick")) return true;
var display = cm.display;
var lineBox = getRect(display.lineDiv);
if (mY > lineBox.bottom) return true;
if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e);
mY -= lineBox.top - display.viewOffset;
for (var i = 0; i < cm.options.gutters.length; ++i) {
@ -1796,11 +1854,19 @@ window.CodeMirror = (function() {
if (g && getRect(g).right >= mX) {
var line = lineAtHeight(cm.doc, mY);
var gutter = cm.options.gutters[i];
signalLater(cm, "gutterClick", cm, line, gutter, e);
break;
signalfn(cm, type, cm, line, gutter, e);
return e_defaultPrevented(e);
}
}
return true;
}
function contextMenuInGutter(cm, e) {
if (!hasHandler(cm, "gutterContextMenu")) return false;
return gutterEvent(cm, e, "gutterContextMenu", false, signal);
}
function clickInGutter(cm, e) {
return gutterEvent(cm, e, "gutterClick", true, signalLater);
}
// Kludge to work around strange IE behavior where it'll sometimes
@ -1845,7 +1911,6 @@ window.CodeMirror = (function() {
if (cm.state.draggingText) replaceRange(cm.doc, "", curFrom, curTo, "paste");
cm.replaceSelection(text, null, "paste");
focusInput(cm);
onFocus(cm);
}
}
catch(e){}
@ -1863,6 +1928,7 @@ window.CodeMirror = (function() {
// Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
if (e.dataTransfer.setDragImage && !safari) {
var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
if (opera) {
img.width = img.height = 1;
cm.display.wrapper.appendChild(img);
@ -2061,8 +2127,8 @@ window.CodeMirror = (function() {
function onKeyDown(e) {
var cm = this;
if (!cm.state.focused) onFocus(cm);
if (ie && e.keyCode == 27) { e.returnValue = false; }
if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
if (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;
@ -2099,7 +2165,10 @@ window.CodeMirror = (function() {
cm.state.focused = true;
if (cm.display.wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
cm.display.wrapper.className += " CodeMirror-focused";
resetInput(cm, true);
if (!cm.curOp) {
resetInput(cm, true);
if (webkit) setTimeout(bind(resetInput, cm, true), 0); // Issue #1730
}
}
slowPoll(cm);
restartBlink(cm);
@ -2118,11 +2187,15 @@ window.CodeMirror = (function() {
function onContextMenu(cm, e) {
if (signalDOMEvent(cm, e, "contextmenu")) return;
var display = cm.display, sel = cm.doc.sel;
if (eventInWidget(display, e)) return;
if (eventInWidget(display, e) || contextMenuInGutter(cm, e)) return;
var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
if (!pos || opera) return; // Opera is difficult.
if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
// Reset the current text selection only if the click is done outside of the selection
// and 'resetSelectionOnContextMenu' option is true.
var reset = cm.options.resetSelectionOnContextMenu;
if (reset && (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to)))
operation(cm, setSelection)(cm.doc, pos, pos);
var oldCSS = display.input.style.cssText;
@ -2137,8 +2210,8 @@ window.CodeMirror = (function() {
function prepareSelectAllHack() {
if (display.input.selectionStart != null) {
var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value);
display.prevInput = " ";
var extval = display.input.value = "\u200b" + (posEq(sel.from, sel.to) ? "" : display.input.value);
display.prevInput = "\u200b";
display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
}
}
@ -2272,6 +2345,7 @@ window.CodeMirror = (function() {
}
function makeChangeNoReadonly(doc, change, selUpdate) {
if (change.text.length == 1 && change.text[0] == "" && posEq(change.from, change.to)) return;
var selAfter = computeSelAfterChange(doc, change, selUpdate);
addToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
@ -2494,6 +2568,7 @@ window.CodeMirror = (function() {
var sel = doc.sel;
sel.goalColumn = null;
if (bias == null) bias = posLess(head, sel.head) ? -1 : 1;
// Skip over atomic spans.
if (checkAtomic || !posEq(anchor, sel.anchor))
anchor = skipAtomic(doc, anchor, bias, checkAtomic != "push");
@ -2571,7 +2646,7 @@ window.CodeMirror = (function() {
// SCROLLING
function scrollCursorIntoView(cm) {
var coords = scrollPosIntoView(cm, cm.doc.sel.head, cm.options.cursorScrollMargin);
var coords = scrollPosIntoView(cm, cm.doc.sel.head, null, cm.options.cursorScrollMargin);
if (!cm.state.focused) return;
var display = cm.display, box = getRect(display.sizer), doScroll = null;
if (coords.top + box.top < 0) doScroll = true;
@ -2588,11 +2663,15 @@ window.CodeMirror = (function() {
}
}
function scrollPosIntoView(cm, pos, margin) {
function scrollPosIntoView(cm, pos, end, margin) {
if (margin == null) margin = 0;
for (;;) {
var changed = false, coords = cursorCoords(cm, pos);
var scrollPos = calculateScrollPos(cm, coords.left, coords.top - margin, coords.left, coords.bottom + margin);
var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),
Math.min(coords.top, endCoords.top) - margin,
Math.max(coords.left, endCoords.left),
Math.max(coords.bottom, endCoords.bottom) + margin);
var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
if (scrollPos.scrollTop != null) {
setScrollTop(cm, scrollPos.scrollTop);
@ -2689,6 +2768,8 @@ window.CodeMirror = (function() {
if (indentString != curSpaceString)
replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
else if (doc.sel.head.line == n && doc.sel.head.ch < curSpaceString.length)
setSelection(doc, Pos(n, curSpaceString.length), Pos(n, curSpaceString.length), 1);
line.stateAfter = null;
}
@ -2789,7 +2870,7 @@ window.CodeMirror = (function() {
CodeMirror.prototype = {
constructor: CodeMirror,
focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
focus: function(){window.focus(); focusInput(this); fastPoll(this);},
setOption: function(option, value) {
var options = this.options, old = options[option];
@ -3110,17 +3191,23 @@ window.CodeMirror = (function() {
clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
},
scrollIntoView: operation(null, function(pos, margin) {
if (typeof pos == "number") pos = Pos(pos, 0);
scrollIntoView: operation(null, function(range, margin) {
if (range == null) range = {from: this.doc.sel.head, to: null};
else if (typeof range == "number") range = {from: Pos(range, 0), to: null};
else if (range.from == null) range = {from: range, to: null};
if (!range.to) range.to = range.from;
if (!margin) margin = 0;
var coords = pos;
if (!pos || pos.line != null) {
this.curOp.scrollToPos = pos ? clipPos(this.doc, pos) : this.doc.sel.head;
this.curOp.scrollToPosMargin = margin;
coords = cursorCoords(this, this.curOp.scrollToPos);
var coords = range;
if (range.from.line != null) {
this.curOp.scrollToPos = {from: range.from, to: range.to, margin: margin};
coords = {from: cursorCoords(this, range.from),
to: cursorCoords(this, range.to)};
}
var sPos = calculateScrollPos(this, coords.left, coords.top - margin, coords.right, coords.bottom + margin);
var sPos = calculateScrollPos(this, Math.min(coords.from.left, coords.to.left),
Math.min(coords.from.top, coords.to.top) - margin,
Math.max(coords.from.right, coords.to.right),
Math.max(coords.from.bottom, coords.to.bottom) + margin);
updateScrollPos(this, sPos.scrollLeft, sPos.scrollTop);
}),
@ -3138,9 +3225,11 @@ window.CodeMirror = (function() {
operation: function(f){return runInOp(this, f);},
refresh: operation(null, function() {
var badHeight = this.display.cachedTextHeight == null;
clearCaches(this);
updateScrollPos(this, this.doc.scrollLeft, this.doc.scrollTop);
regChange(this);
if (badHeight) estimateLineHeights(this);
}),
swapDoc: operation(null, function(doc) {
@ -3150,6 +3239,7 @@ window.CodeMirror = (function() {
clearCaches(this);
resetInput(this, true);
updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
signalLater(this, "swapDoc", this, old);
return old;
}),
@ -3193,8 +3283,14 @@ window.CodeMirror = (function() {
clearCaches(cm);
regChange(cm);
}, true);
option("specialChars", /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\ufeff]/g, function(cm, val) {
cm.options.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
cm.refresh();
}, true);
option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true);
option("electricChars", true);
option("rtlMoveVisually", !windows);
option("wholeLineUpdateBefore", true);
option("theme", "default", function(cm) {
themeChanged(cm);
@ -3224,9 +3320,17 @@ window.CodeMirror = (function() {
option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
option("showCursorWhenSelecting", false, updateSelection, true);
option("resetSelectionOnContextMenu", true);
option("readOnly", false, function(cm, val) {
if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
else if (!val) resetInput(cm, true);
if (val == "nocursor") {
onBlur(cm);
cm.display.input.blur();
cm.display.disabled = true;
} else {
cm.display.disabled = false;
if (!val) resetInput(cm, true);
}
});
option("dragDrop", true);
@ -3241,6 +3345,7 @@ window.CodeMirror = (function() {
option("historyEventDelay", 500);
option("viewportMargin", 10, function(cm){cm.refresh();}, true);
option("maxHighlightLength", 10000, function(cm){loadMode(cm); cm.refresh();}, true);
option("crudeMeasuringFrom", 10000);
option("moveInputWithCursor", true, function(cm, val) {
if (!val) cm.display.inputDiv.style.top = cm.display.inputDiv.style.left = 0;
});
@ -3459,7 +3564,8 @@ window.CodeMirror = (function() {
keyMap.basic = {
"Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
"End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
"Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
"Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
"Tab": "defaultTab", "Shift-Tab": "indentAuto",
"Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
};
// Note that the save and find-related commands aren't defined by
@ -3730,9 +3836,10 @@ window.CodeMirror = (function() {
TextMarker.prototype.changed = function() {
var pos = this.find(), cm = this.doc.cm;
if (!pos || !cm) return;
var line = getLine(this.doc, pos.from.line);
if (this.type != "bookmark") pos = pos.from;
var line = getLine(this.doc, pos.line);
clearCachedMeasurement(cm, line);
if (pos.from.line >= cm.display.showingFrom && pos.from.line < cm.display.showingTo) {
if (pos.line >= cm.display.showingFrom && pos.line < cm.display.showingTo) {
for (var node = cm.display.lineDiv.firstChild; node; node = node.nextSibling) if (node.lineObj == line) {
if (node.offsetHeight != line.height) updateLineHeight(line, node.offsetHeight);
break;
@ -3764,7 +3871,9 @@ window.CodeMirror = (function() {
if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
var marker = new TextMarker(doc, type);
if (type == "range" && !posLess(from, to)) return marker;
if (posLess(to, from) || posEq(from, to) && type == "range" &&
!(options.inclusiveLeft && options.inclusiveRight))
return marker;
if (options) copyObj(options, marker);
if (marker.replacedWith) {
marker.collapsed = true;
@ -3880,7 +3989,9 @@ 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.type == "bookmark" && span.from == startCh && (!isInsert || !span.marker.insertLeft)) {
if (startsBefore ||
(marker.inclusiveLeft && marker.inclusiveRight || marker.type == "bookmark") &&
span.from == startCh && (!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,
@ -4153,6 +4264,7 @@ window.CodeMirror = (function() {
this.height = estimateHeight ? estimateHeight(this) : 1;
};
eventMixin(Line);
Line.prototype.lineNo = function() { return lineNo(this); };
function updateLine(line, text, markedSpans, estimateHeight) {
line.text = text;
@ -4173,7 +4285,7 @@ window.CodeMirror = (function() {
// Run the given mode's parser over a line, update the styles
// array, which contains alternating fragments of text and CSS
// classes.
function runMode(cm, text, mode, state, f) {
function runMode(cm, text, mode, state, f, forceToEnd) {
var flattenSpans = mode.flattenSpans;
if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
var curStart = 0, curStyle = null;
@ -4182,8 +4294,8 @@ window.CodeMirror = (function() {
while (!stream.eol()) {
if (stream.pos > cm.options.maxHighlightLength) {
flattenSpans = false;
// Webkit seems to refuse to render text nodes longer than 57444 characters
stream.pos = Math.min(text.length, stream.start + 50000);
if (forceToEnd) processLine(cm, text, state, stream.pos);
stream.pos = text.length;
style = null;
} else {
style = mode.token(stream, state);
@ -4194,15 +4306,22 @@ window.CodeMirror = (function() {
}
stream.start = stream.pos;
}
if (curStart < stream.pos) f(stream.pos, curStyle);
while (curStart < stream.pos) {
// Webkit seems to refuse to render text nodes longer than 57444 characters
var pos = Math.min(stream.pos, curStart + 50000);
f(pos, curStyle);
curStart = pos;
}
}
function highlightLine(cm, line, state) {
function highlightLine(cm, line, state, forceToEnd) {
// A styles array always starts with a number identifying the
// mode/overlays that it is based on (for easy invalidation).
var st = [cm.state.modeGen];
// Compute the base array of styles
runMode(cm, line.text, cm.doc.mode, state, function(end, style) {st.push(end, style);});
runMode(cm, line.text, cm.doc.mode, state, function(end, style) {
st.push(end, style);
}, forceToEnd);
// Run overlays, adjust style array.
for (var o = 0; o < cm.state.overlays.length; ++o) {
@ -4241,10 +4360,11 @@ window.CodeMirror = (function() {
// Lightweight form of highlight -- proceed over this line and
// update state, but don't save a style array.
function processLine(cm, line, state) {
function processLine(cm, text, state, startAt) {
var mode = cm.doc.mode;
var stream = new StringStream(line.text, cm.options.tabSize);
if (line.text == "" && mode.blankLine) mode.blankLine(state);
var stream = new StringStream(text, cm.options.tabSize);
stream.start = stream.pos = startAt || 0;
if (text == "" && mode.blankLine) mode.blankLine(state);
while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
mode.token(stream, state);
stream.start = stream.pos;
@ -4252,13 +4372,23 @@ window.CodeMirror = (function() {
}
var styleToClassCache = {};
function styleToClass(style) {
function interpretTokenStyle(style, builder) {
if (!style) return null;
for (;;) {
var lineClass = style.match(/(?:^|\s)line-(background-)?(\S+)/);
if (!lineClass) break;
style = style.slice(0, lineClass.index) + style.slice(lineClass.index + lineClass[0].length);
var prop = lineClass[1] ? "bgClass" : "textClass";
if (builder[prop] == null)
builder[prop] = lineClass[2];
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-"));
}
function lineContent(cm, realLine, measure, copyWidgets) {
function buildLineContent(cm, realLine, measure, copyWidgets) {
var merged, line = realLine, empty = true;
while (merged = collapsedSpanAtStart(line))
line = getLine(cm.doc, merged.find().from.line);
@ -4266,7 +4396,6 @@ window.CodeMirror = (function() {
var builder = {pre: elt("pre"), col: 0, pos: 0,
measure: null, measuredSomething: false, cm: cm,
copyWidgets: copyWidgets};
if (line.textClass) builder.pre.className = line.textClass;
do {
if (line.text) empty = false;
@ -4292,7 +4421,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 && (order = getOrder(line))) {
if (measure && (ie || ie_gt10) && (order = getOrder(line))) {
var l = order.length - 1;
if (order[l].from == order[l].to) --l;
var last = order[l], prev = order[l - 1];
@ -4303,21 +4432,30 @@ window.CodeMirror = (function() {
}
}
var textClass = builder.textClass ? builder.textClass + " " + (realLine.textClass || "") : realLine.textClass;
if (textClass) builder.pre.className = textClass;
signal(cm, "renderLine", cm, realLine, builder.pre);
return builder.pre;
return builder;
}
function defaultSpecialCharPlaceholder(ch) {
var token = elt("span", "\u2022", "cm-invalidchar");
token.title = "\\u" + ch.charCodeAt(0).toString(16);
return token;
}
var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g;
function buildToken(builder, text, style, startStyle, endStyle, title) {
if (!text) return;
if (!tokenSpecialChars.test(text)) {
var special = builder.cm.options.specialChars;
if (!special.test(text)) {
builder.col += text.length;
var content = document.createTextNode(text);
} else {
var content = document.createDocumentFragment(), pos = 0;
while (true) {
tokenSpecialChars.lastIndex = pos;
var m = tokenSpecialChars.exec(text);
special.lastIndex = pos;
var m = special.exec(text);
var skipped = m ? m.index - pos : text.length - pos;
if (skipped) {
content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
@ -4330,8 +4468,7 @@ window.CodeMirror = (function() {
content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
builder.col += tabWidth;
} else {
var token = elt("span", "\u2022", "cm-invalidchar");
token.title = "\\u" + m[0].charCodeAt(0).toString(16);
var token = builder.cm.options.specialCharPlaceholder(m[0]);
content.appendChild(token);
builder.col += 1;
}
@ -4382,7 +4519,7 @@ window.CodeMirror = (function() {
return out;
}
return function(builder, text, style, startStyle, endStyle, title) {
return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle, title);
return inner(builder, text.replace(/ {3,}/g, split), style, startStyle, endStyle, title);
};
}
@ -4395,11 +4532,13 @@ window.CodeMirror = (function() {
if (size) {
builder.measure[builder.pos] = widget;
} else {
var elt = builder.measure[builder.pos] = zeroWidthElement(builder.cm.display.measure);
if (marker.type != "bookmark" || marker.insertLeft)
builder.pre.insertBefore(elt, widget);
var elt = zeroWidthElement(builder.cm.display.measure);
if (marker.type == "bookmark" && !marker.insertLeft)
builder.measure[builder.pos] = builder.pre.appendChild(elt);
else if (builder.measure[builder.pos])
return;
else
builder.pre.appendChild(elt);
builder.measure[builder.pos] = builder.pre.insertBefore(elt, widget);
}
builder.measuredSomething = true;
}
@ -4413,7 +4552,7 @@ window.CodeMirror = (function() {
var spans = line.markedSpans, allText = line.text, at = 0;
if (!spans) {
for (var i = 1; i < styles.length; i+=2)
builder.addToken(builder, allText.slice(at, at = styles[i]), styleToClass(styles[i+1]));
builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder));
return;
}
@ -4423,7 +4562,7 @@ window.CodeMirror = (function() {
if (nextChange == pos) { // Update current marker set
spanStyle = spanEndStyle = spanStartStyle = title = "";
collapsed = null; nextChange = Infinity;
var foundBookmark = null;
var foundBookmarks = [];
for (var j = 0; j < spans.length; ++j) {
var sp = spans[j], m = sp.marker;
if (sp.from <= pos && (sp.to == null || sp.to > pos)) {
@ -4437,14 +4576,15 @@ window.CodeMirror = (function() {
} else if (sp.from > pos && nextChange > sp.from) {
nextChange = sp.from;
}
if (m.type == "bookmark" && sp.from == pos && m.replacedWith) foundBookmark = m;
if (m.type == "bookmark" && sp.from == pos && m.replacedWith) foundBookmarks.push(m);
}
if (collapsed && (collapsed.from || 0) == pos) {
buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos,
collapsed.marker, collapsed.from == null);
if (collapsed.to == null) return collapsed.marker.find();
}
if (foundBookmark && !collapsed) buildCollapsedSpan(builder, 0, foundBookmark);
if (!collapsed && foundBookmarks.length) for (var j = 0; j < foundBookmarks.length; ++j)
buildCollapsedSpan(builder, 0, foundBookmarks[j]);
}
if (pos >= len) break;
@ -4462,7 +4602,7 @@ window.CodeMirror = (function() {
spanStartStyle = "";
}
text = allText.slice(at, at = styles[i++]);
style = styleToClass(styles[i++]);
style = interpretTokenStyle(styles[i++], builder);
}
}
}
@ -4481,7 +4621,8 @@ window.CodeMirror = (function() {
var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
// First adjust the line structure
if (from.ch == 0 && to.ch == 0 && lastText == "") {
if (from.ch == 0 && to.ch == 0 && lastText == "" &&
(!doc.cm || doc.cm.options.wholeLineUpdateBefore)) {
// This is a whole-line replace. Treated specially to make
// sure line objects move the way they are supposed to.
for (var i = 0, e = text.length - 1, added = []; i < e; ++i)
@ -4740,11 +4881,11 @@ window.CodeMirror = (function() {
if (extend) extendSelection(this, pos);
else setSelection(this, pos, pos);
}),
setSelection: docOperation(function(anchor, head) {
setSelection(this, clipPos(this, anchor), clipPos(this, head || anchor));
setSelection: docOperation(function(anchor, head, bias) {
setSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), bias);
}),
extendSelection: docOperation(function(from, to) {
extendSelection(this, clipPos(this, from), to && clipPos(this, to));
extendSelection: docOperation(function(from, to, bias) {
extendSelection(this, clipPos(this, from), to && clipPos(this, to), bias);
}),
getSelection: function(lineSep) {return this.getRange(this.sel.from, this.sel.to, lineSep);},
@ -5368,7 +5509,7 @@ 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\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff]/;
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]/;
// DOM UTILITIES
@ -5430,13 +5571,18 @@ window.CodeMirror = (function() {
spanAffectsWrapping = function(str, i) {
return /\-[^ \-?]|\?[^ !\'\"\),.\-\/:;\?\]\}]/.test(str.slice(i - 1, i + 1));
};
else if (webkit && !/Chrome\/(?:29|[3-9]\d|\d\d\d)\./.test(navigator.userAgent))
else if (webkit && /Chrome\/(?:29|[3-9]\d|\d\d\d)\./.test(navigator.userAgent))
spanAffectsWrapping = function(str, i) {
var code = str.charCodeAt(i - 1);
return code >= 8208 && code <= 8212;
};
else if (webkit)
spanAffectsWrapping = function(str, i) {
if (i > 1 && str.charCodeAt(i - 1) == 45) {
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~`@#$%\^&*(_=+{[|><]|…[\w~`@#$%\^&*(_=+{[><]/.test(str.slice(i - 1, i + 1));
};
var knownScrollbarWidth;
@ -5792,7 +5938,7 @@ window.CodeMirror = (function() {
// THE END
CodeMirror.version = "3.15.0";
CodeMirror.version = "3.20.0";
return CodeMirror;
})();

View File

@ -1,11 +1,9 @@
CodeMirror.defineMode("css", function(config) {
return CodeMirror.getMode(config, "text/css");
});
CodeMirror.defineMode("css-base", function(config, parserConfig) {
CodeMirror.defineMode("css", function(config, parserConfig) {
"use strict";
var indentUnit = config.indentUnit,
if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
var indentUnit = config.indentUnit || config.tabSize || 2,
hooks = parserConfig.hooks || {},
atMediaTypes = parserConfig.atMediaTypes || {},
atMediaFeatures = parserConfig.atMediaFeatures || {},
@ -39,7 +37,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
stream.match(/^\s*\w*/);
return ret("keyword", "important");
}
else if (/\d/.test(ch)) {
else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
stream.eatWhile(/[\w.%]/);
return ret("number", "unit");
}
@ -261,8 +259,13 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
}
else if (type == "}") {
if (context == "interpolation") style = "operator";
state.stack.pop();
if (context == "propertyValue") state.stack.pop();
// 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");
@ -277,15 +280,15 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
state.stack[state.stack.length-1] = "@mediaType";
state.stack.push("@mediaType(");
}
else state.stack.push("(");
}
else if (type == ")") {
if (context == "propertyValue" && state.stack[state.stack.length-2] == "@mediaType(") {
// In @mediaType( without closing ; after propertyValue
state.stack.pop();
state.stack.pop();
}
else if (context == "@mediaType(") {
state.stack.pop();
// 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");
@ -366,8 +369,8 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
"drop-initial-before-align", "drop-initial-size", "drop-initial-value",
"elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
"flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
"float", "float-offset", "font", "font-feature-settings", "font-family",
"font-kerning", "font-language-override", "font-size", "font-size-adjust",
"float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings",
"font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust",
"font-stretch", "font-style", "font-synthesis", "font-variant",
"font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
"font-variant-ligatures", "font-variant-numeric", "font-variant-position",
@ -391,10 +394,12 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
"page", "page-break-after", "page-break-before", "page-break-inside",
"page-policy", "pause", "pause-after", "pause-before", "perspective",
"perspective-origin", "pitch", "pitch-range", "play-during", "position",
"presentation-level", "punctuation-trim", "quotes", "rendering-intent",
"resize", "rest", "rest-after", "rest-before", "richness", "right",
"rotation", "rotation-point", "ruby-align", "ruby-overhang",
"ruby-position", "ruby-span", "size", "speak", "speak-as", "speak-header",
"presentation-level", "punctuation-trim", "quotes", "region-break-after",
"region-break-before", "region-break-inside", "region-fragment",
"rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
"right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
"ruby-position", "ruby-span", "shape-inside", "shape-outside", "size",
"speak", "speak-as", "speak-header",
"speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
"tab-size", "table-layout", "target", "target-name", "target-new",
"target-position", "text-align", "text-align-last", "text-decoration",
@ -432,7 +437,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
"darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
"deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
"floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
"gold", "goldenrod", "gray", "green", "greenyellow", "honeydew",
"gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
"hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
"lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
"lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
@ -455,22 +460,22 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
"above", "absolute", "activeborder", "activecaption", "afar",
"after-white-space", "ahead", "alias", "all", "all-scroll", "alternate",
"always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
"arabic-indic", "armenian", "asterisks", "auto", "avoid", "background",
"backwards", "baseline", "below", "bidi-override", "binary", "bengali",
"blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
"both", "bottom", "break-all", "break-word", "button", "button-bevel",
"arabic-indic", "armenian", "asterisks", "auto", "avoid", "avoid-column", "avoid-page",
"avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
"bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
"both", "bottom", "break", "break-all", "break-word", "button", "button-bevel",
"buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian",
"capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
"cell", "center", "checkbox", "circle", "cjk-earthly-branch",
"cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
"col-resize", "collapse", "compact", "condensed", "contain", "content",
"col-resize", "collapse", "column", "compact", "condensed", "contain", "content",
"content-box", "context-menu", "continuous", "copy", "cover", "crop",
"cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal",
"decimal-leading-zero", "default", "default-button", "destination-atop",
"destination-in", "destination-out", "destination-over", "devanagari",
"disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted",
"double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
"element", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
"element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
"ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
"ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
"ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
@ -486,7 +491,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
"inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
"infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
"inline-block", "inline-table", "inset", "inside", "intrinsic", "invert",
"italic", "justify", "kannada", "katakana", "katakana-iroha", "khmer",
"italic", "justify", "kannada", "katakana", "katakana-iroha", "keep-all", "khmer",
"landscape", "lao", "large", "larger", "left", "level", "lighter",
"line-through", "linear", "lines", "list-item", "listbox", "listitem",
"local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
@ -505,11 +510,11 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
"no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
"ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
"optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
"outside", "overlay", "overline", "padding", "padding-box", "painted",
"paused", "persian", "plus-darker", "plus-lighter", "pointer", "portrait",
"pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button",
"radio", "read-only", "read-write", "read-write-plaintext-only", "relative",
"repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba",
"outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
"painted", "page", "paused", "persian", "plus-darker", "plus-lighter", "pointer",
"polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button",
"radio", "read-only", "read-write", "read-write-plaintext-only", "rectangle", "region",
"relative", "repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba",
"ridge", "right", "round", "row-resize", "rtl", "run-in", "running",
"s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield",
"searchfield-cancel-button", "searchfield-decoration",
@ -580,7 +585,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
return false;
}
},
name: "css-base"
name: "css"
});
CodeMirror.defineMIME("text/x-scss", {
@ -591,6 +596,12 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
valueKeywords: valueKeywords,
allowNested: true,
hooks: {
":": function(stream) {
if (stream.match(/\s*{/)) {
return [null, "{"];
}
return false;
},
"$": function(stream) {
stream.match(/^[\w-]+/);
if (stream.peek() == ":") {
@ -598,6 +609,11 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
}
return ["variable", "variable"];
},
",": function(stream, state) {
if (state.stack[state.stack.length - 1] == "propertyValue" && stream.match(/^ *\$/, false)) {
return ["operator", ";"];
}
},
"/": function(stream, state) {
if (stream.eat("/")) {
stream.skipToEnd();
@ -618,6 +634,6 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
}
}
},
name: "css-base"
name: "css"
});
})();

View File

@ -12,13 +12,20 @@
}
if (typeof template == "string") {
dialog.innerHTML = template;
} else {
} else { // Assuming it's a detached DOM element.
dialog.appendChild(template);
}
return dialog;
}
function closeNotification(cm, newVal) {
if (cm.state.currentNotificationClose)
cm.state.currentNotificationClose();
cm.state.currentNotificationClose = newVal;
}
CodeMirror.defineExtension("openDialog", function(template, callback, options) {
closeNotification(this, null);
var dialog = dialogDiv(this, template, options && options.bottom);
var closed = false, me = this;
function close() {
@ -55,6 +62,7 @@
});
CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) {
closeNotification(this, null);
var dialog = dialogDiv(this, template, options && options.bottom);
var buttons = dialog.getElementsByTagName("button");
var closed = false, me = this, blurring = 1;
@ -81,4 +89,33 @@
CodeMirror.on(b, "focus", function() { ++blurring; });
}
});
/*
* openNotification
* Opens a notification, that can be closed with an optional timer
* (default 5000ms timer) and always closes on click.
*
* If a notification is opened while another is opened, it will close the
* currently opened one and open the new one immediately.
*/
CodeMirror.defineExtension("openNotification", function(template, options) {
closeNotification(this, close);
var dialog = dialogDiv(this, template, options && options.bottom);
var duration = options && (options.duration === undefined ? 5000 : options.duration);
var closed = false, doneTimer;
function close() {
if (closed) return;
closed = true;
clearTimeout(doneTimer);
dialog.parentNode.removeChild(dialog);
}
CodeMirror.on(dialog, 'click', function(e) {
CodeMirror.e_preventDefault(e);
close();
});
if (duration)
doneTimer = setTimeout(close, options.duration);
});
})();

View File

@ -44,7 +44,7 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
if (close > -1) stream.backUp(cur.length - close);
else if (m = cur.match(/<\/?$/)) {
stream.backUp(cur.length);
if (!stream.match(pat, false)) stream.match(cur[0]);
if (!stream.match(pat, false)) stream.match(cur);
}
return style;
}

View File

@ -21,7 +21,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
"in": operator, "typeof": operator, "instanceof": operator,
"true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
"this": kw("this")
"this": kw("this"), "module": kw("module"), "class": kw("class"), "super": kw("atom"),
"yield": C, "export": kw("export"), "import": kw("import"), "extends": C
};
// Extend the 'normal' keywords with the TypeScript language extensions
@ -30,7 +31,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
var tsKeywords = {
// object-like things
"interface": kw("interface"),
"class": kw("class"),
"extends": kw("extends"),
"constructor": kw("constructor"),
@ -40,8 +40,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
"protected": kw("protected"),
"static": kw("static"),
"super": kw("super"),
// types
"string": type, "number": type, "bool": type, "any": type
};
@ -56,11 +54,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
var isOperatorChar = /[+\-*&%=<>!?|~^]/;
function chain(stream, state, f) {
state.tokenize = f;
return f(stream, state);
}
function nextUntilUnescaped(stream, end) {
var escaped = false, next;
while ((next = stream.next()) != null) {
@ -78,49 +71,51 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
type = tp; content = cont;
return style;
}
function jsTokenBase(stream, state) {
function tokenBase(stream, state) {
var ch = stream.next();
if (ch == '"' || ch == "'")
return chain(stream, state, jsTokenString(ch));
else if (/[\[\]{}\(\),;\:\.]/.test(ch))
if (ch == '"' || ch == "'") {
state.tokenize = tokenString(ch);
return state.tokenize(stream, state);
} else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
return ret("number", "number");
} else if (ch == "." && stream.match("..")) {
return ret("spread", "meta");
} else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
return ret(ch);
else if (ch == "0" && stream.eat(/x/i)) {
} else if (ch == "=" && stream.eat(">")) {
return ret("=>");
} else if (ch == "0" && stream.eat(/x/i)) {
stream.eatWhile(/[\da-f]/i);
return ret("number", "number");
}
else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) {
} else if (/\d/.test(ch)) {
stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
return ret("number", "number");
}
else if (ch == "/") {
} else if (ch == "/") {
if (stream.eat("*")) {
return chain(stream, state, jsTokenComment);
}
else if (stream.eat("/")) {
state.tokenize = tokenComment;
return tokenComment(stream, state);
} else if (stream.eat("/")) {
stream.skipToEnd();
return ret("comment", "comment");
}
else if (state.lastType == "operator" || state.lastType == "keyword c" ||
/^[\[{}\(,;:]$/.test(state.lastType)) {
} else if (state.lastType == "operator" || state.lastType == "keyword c" ||
state.lastType == "sof" || /^[\[{}\(,;:]$/.test(state.lastType)) {
nextUntilUnescaped(stream, "/");
stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
return ret("regexp", "string-2");
}
else {
} else {
stream.eatWhile(isOperatorChar);
return ret("operator", null, stream.current());
}
}
else if (ch == "#") {
} else if (ch == "`") {
state.tokenize = tokenQuasi;
return tokenQuasi(stream, state);
} else if (ch == "#") {
stream.skipToEnd();
return ret("error", "error");
}
else if (isOperatorChar.test(ch)) {
} else if (isOperatorChar.test(ch)) {
stream.eatWhile(isOperatorChar);
return ret("operator", null, stream.current());
}
else {
} else {
stream.eatWhile(/[\w\$_]/);
var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
@ -128,19 +123,19 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
}
}
function jsTokenString(quote) {
function tokenString(quote) {
return function(stream, state) {
if (!nextUntilUnescaped(stream, quote))
state.tokenize = jsTokenBase;
state.tokenize = tokenBase;
return ret("string", "string");
};
}
function jsTokenComment(stream, state) {
function tokenComment(stream, state) {
var maybeEnd = false, ch;
while (ch = stream.next()) {
if (ch == "/" && maybeEnd) {
state.tokenize = jsTokenBase;
state.tokenize = tokenBase;
break;
}
maybeEnd = (ch == "*");
@ -148,6 +143,50 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
return ret("comment", "comment");
}
function tokenQuasi(stream, state) {
var escaped = false, next;
while ((next = stream.next()) != null) {
if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
state.tokenize = tokenBase;
break;
}
escaped = !escaped && next == "\\";
}
return ret("quasi", "string-2", stream.current());
}
var brackets = "([{}])";
// This is a crude lookahead trick to try and notice that we're
// parsing the argument patterns for a fat-arrow function before we
// actually hit the arrow token. It only works if the arrow is on
// the same line as the arguments and there's no strange noise
// (comments) in between. Fallback is to only notice when we hit the
// arrow, and not declare the arguments as locals for the arrow
// body.
function findFatArrow(stream, state) {
if (state.fatArrowAt) state.fatArrowAt = null;
var arrow = stream.string.indexOf("=>", stream.start);
if (arrow < 0) return;
var depth = 0, sawSomething = false;
for (var pos = arrow - 1; pos >= 0; --pos) {
var ch = stream.string.charAt(pos);
var bracket = brackets.indexOf(ch);
if (bracket >= 0 && bracket < 3) {
if (!depth) { ++pos; break; }
if (--depth == 0) break;
} else if (bracket >= 3 && bracket < 6) {
++depth;
} else if (/[$\w]/.test(ch)) {
sawSomething = true;
} else if (sawSomething && !depth) {
++pos;
break;
}
}
if (sawSomething && !depth) state.fatArrowAt = pos;
}
// Parser
var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true};
@ -164,6 +203,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
function inScope(state, varname) {
for (var v = state.localVars; v; v = v.next)
if (v.name == varname) return true;
for (var cx = state.context; cx; cx = cx.prev) {
for (var v = cx.vars; v; v = v.next)
if (v.name == varname) return true;
}
}
function parseJS(state, style, type, content, stream) {
@ -210,7 +253,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
state.localVars = {name: varname, next: state.localVars};
} else {
if (inList(state.globalVars)) return;
state.globalVars = {name: varname, next: state.globalVars};
if (parserConfig.globalVars)
state.globalVars = {name: varname, next: state.globalVars};
}
}
@ -252,16 +296,15 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
};
}
function statement(type) {
if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
function statement(type, value) {
if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
if (type == "{") return cont(pushlex("}"), block, poplex);
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"), expect("("), pushlex(")"), forspec1, expect(")"),
poplex, statement, poplex);
if (type == "for") return cont(pushlex("form"), forspec, poplex, statement, poplex);
if (type == "variable") return cont(pushlex("stat"), maybelabel);
if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
block, poplex, poplex);
@ -269,6 +312,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "default") return cont(expect(":"));
if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
statement, poplex, popcontext);
if (type == "module") return cont(pushlex("form"), pushcontext, afterModule, popcontext, poplex);
if (type == "class") return cont(pushlex("form"), className, objlit, poplex);
if (type == "export") return cont(pushlex("form"), afterExport, poplex);
if (type == "import") return cont(pushlex("form"), afterImport, poplex);
return pass(pushlex("stat"), expression, expect(";"), poplex);
}
function expression(type) {
@ -278,14 +325,20 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
return expressionInner(type, true);
}
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);
else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
}
var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
if (type == "function") return cont(functiondef);
if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
if (type == "operator") return cont(noComma ? expressionNoComma : expression);
if (type == "[") return cont(pushlex("]"), commasep(expressionNoComma, "]"), poplex, maybeop);
if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeop);
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);
return cont();
}
function maybeexpression(type) {
@ -304,16 +357,40 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
function maybeoperatorNoComma(type, value, noComma) {
var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
var expr = noComma == false ? expression : expressionNoComma;
if (value == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
if (type == "operator") {
if (/\+\+|--/.test(value)) return cont(me);
if (value == "?") return cont(expression, expect(":"), expr);
return cont(expr);
}
if (type == "quasi") { cx.cc.push(me); return quasi(value); }
if (type == ";") return;
if (type == "(") return cont(pushlex(")", "call"), commasep(expressionNoComma, ")"), poplex, me);
if (type == "(") return cont(commasep(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);
}
function continueQuasi(type) {
if (type == "}") {
cx.marked = "string-2";
cx.state.tokenize = tokenQuasi;
return cont();
}
}
function arrowBody(type) {
findFatArrow(cx.stream, cx.state);
if (type == "{") return pass(statement);
return pass(expression);
}
function arrowBodyNoComma(type) {
findFatArrow(cx.stream, cx.state);
if (type == "{") return pass(statement);
return pass(expressionNoComma);
}
function maybelabel(type) {
if (type == ":") return cont(poplex, statement);
return pass(maybeoperatorComma, expect(";"), poplex);
@ -327,16 +404,21 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (value == "get" || value == "set") return cont(getterSetter);
} else if (type == "number" || type == "string") {
cx.marked = type + " property";
} else if (type == "[") {
return cont(expression, expect("]"), afterprop);
}
if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expressionNoComma);
if (atomicTypes.hasOwnProperty(type)) return cont(afterprop);
}
function getterSetter(type) {
if (type == ":") return cont(expression);
if (type != "variable") return cont(expect(":"), expression);
if (type != "variable") return pass(afterprop);
cx.marked = "property";
return cont(functiondef);
}
function commasep(what, end) {
function afterprop(type) {
if (type == ":") return cont(expressionNoComma);
if (type == "(") return pass(functiondef);
}
function commasep(what, end, info) {
function proceed(type) {
if (type == ",") {
var lex = cx.state.lexical;
@ -348,7 +430,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
}
return function(type) {
if (type == end) return cont();
else return pass(what, proceed);
if (info === false) return pass(what, proceed);
return pass(pushlex(end, info), what, proceed, poplex);
};
}
function block(type) {
@ -356,67 +439,121 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
return pass(statement, block);
}
function maybetype(type) {
if (type == ":") return cont(typedef);
return pass();
if (isTS && type == ":") return cont(typedef);
}
function typedef(type) {
if (type == "variable"){cx.marked = "variable-3"; return cont();}
return pass();
}
function vardef1(type, value) {
if (type == "variable") {
function vardef() {
return pass(pattern, maybetype, maybeAssign, vardefCont);
}
function pattern(type, value) {
if (type == "variable") { register(value); return cont(); }
if (type == "[") return cont(commasep(pattern, "]"));
if (type == "{") return cont(commasep(proppattern, "}"));
}
function proppattern(type, value) {
if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
register(value);
return isTS ? cont(maybetype, vardef2) : cont(vardef2);
return cont(maybeAssign);
}
return pass();
if (type == "variable") cx.marked = "property";
return cont(expect(":"), pattern, maybeAssign);
}
function vardef2(type, value) {
if (value == "=") return cont(expressionNoComma, vardef2);
if (type == ",") return cont(vardef1);
function maybeAssign(_type, value) {
if (value == "=") return cont(expressionNoComma);
}
function vardefCont(type) {
if (type == ",") return cont(vardef);
}
function maybeelse(type, value) {
if (type == "keyword b" && value == "else") return cont(pushlex("form"), statement, poplex);
}
function forspec(type) {
if (type == "(") return cont(pushlex(")"), forspec1, expect(")"));
}
function forspec1(type) {
if (type == "var") return cont(vardef1, expect(";"), forspec2);
if (type == "var") return cont(vardef, expect(";"), forspec2);
if (type == ";") return cont(forspec2);
if (type == "variable") return cont(formaybein);
if (type == "variable") return cont(formaybeinof);
return pass(expression, expect(";"), forspec2);
}
function formaybein(_type, value) {
if (value == "in") return cont(expression);
function formaybeinof(_type, value) {
if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
return cont(maybeoperatorComma, forspec2);
}
function forspec2(type, value) {
if (type == ";") return cont(forspec3);
if (value == "in") return cont(expression);
if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
return pass(expression, expect(";"), forspec3);
}
function forspec3(type) {
if (type != ")") cont(expression);
}
function functiondef(type, value) {
if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
if (type == "variable") {register(value); return cont(functiondef);}
if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
if (type == "(") return cont(pushcontext, commasep(funarg, ")"), statement, popcontext);
}
function funarg(type, value) {
if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();}
function funarg(type) {
if (type == "spread") return cont(funarg);
return pass(pattern, maybetype);
}
function className(type, value) {
if (type == "variable") {register(value); return cont(classNameAfter);}
}
function classNameAfter(_type, value) {
if (value == "extends") return cont(expression);
}
function objlit(type) {
if (type == "{") return cont(commasep(objprop, "}"));
}
function afterModule(type, value) {
if (type == "string") return cont(statement);
if (type == "variable") { register(value); return cont(maybeFrom); }
}
function afterExport(_type, value) {
if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
return pass(statement);
}
function afterImport(type) {
if (type == "string") return cont();
return pass(importSpec, maybeFrom);
}
function importSpec(type, value) {
if (type == "{") return cont(commasep(importSpec, "}"));
if (type == "variable") register(value);
return cont();
}
function maybeFrom(_type, value) {
if (value == "from") { cx.marked = "keyword"; return cont(expression); }
}
function maybeArrayComprehension(type) {
if (type == "for") return pass(comprehension);
if (type == ",") return cont(commasep(expressionNoComma, "]", false));
return pass(commasep(expressionNoComma, "]", false));
}
function comprehension(type) {
if (type == "for") return cont(forspec, comprehension);
if (type == "if") return cont(expression, comprehension);
}
// Interface
return {
startState: function(basecolumn) {
return {
tokenize: jsTokenBase,
lastType: null,
var state = {
tokenize: tokenBase,
lastType: "sof",
cc: [],
lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
localVars: parserConfig.localVars,
globalVars: parserConfig.globalVars,
context: parserConfig.localVars && {vars: parserConfig.localVars},
indented: 0
};
if (parserConfig.globalVars) state.globalVars = parserConfig.globalVars;
return state;
},
token: function(stream, state) {
@ -424,8 +561,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (!state.lexical.hasOwnProperty("align"))
state.lexical.align = false;
state.indented = stream.indentation();
findFatArrow(stream, state);
}
if (state.tokenize != jsTokenComment && stream.eatSpace()) return null;
if (state.tokenize != tokenComment && stream.eatSpace()) return null;
var style = state.tokenize(stream, state);
if (type == "comment") return style;
state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
@ -433,21 +571,21 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
},
indent: function(state, textAfter) {
if (state.tokenize == jsTokenComment) return CodeMirror.Pass;
if (state.tokenize != jsTokenBase) return 0;
if (state.tokenize == tokenComment) return CodeMirror.Pass;
if (state.tokenize != tokenBase) return 0;
var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
// Kludge to prevent 'maybelse' from blocking lexical scope pops
for (var i = state.cc.length - 1; i >= 0; --i) {
var c = state.cc[i];
if (c == poplex) lexical = lexical.prev;
else if (c != maybeelse || /^else\b/.test(textAfter)) break;
else if (c != maybeelse) break;
}
if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
lexical = lexical.prev;
var type = lexical.type, closing = firstChar == type;
if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0);
if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
else if (type == "form" && firstChar == "{") return lexical.indented;
else if (type == "form") return lexical.indented + indentUnit;
else if (type == "stat")

View File

@ -8,6 +8,7 @@
function findMatchingBracket(cm, where, strict) {
var state = cm.state.matchBrackets;
var maxScanLen = (state && state.maxScanLineLength) || 10000;
var maxScanLines = (state && state.maxScanLines) || 100;
var cur = where || cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
@ -32,7 +33,7 @@
}
}
}
for (var i = cur.line, found, e = forward ? Math.min(i + 100, cm.lineCount()) : Math.max(-1, i - 100); i != e; i+=d) {
for (var i = cur.line, found, e = forward ? Math.min(i + maxScanLines, cm.lineCount()) : Math.max(-1, i - maxScanLines); i != e; i+=d) {
if (i == cur.line) found = scan(line, i, pos);
else found = scan(cm.getLineHandle(i), i);
if (found) break;

View File

@ -15,15 +15,18 @@
(function() {
var DEFAULT_MIN_CHARS = 2;
var DEFAULT_TOKEN_STYLE = "matchhighlight";
var DEFAULT_DELAY = 100;
function State(options) {
if (typeof options == "object") {
this.minChars = options.minChars;
this.style = options.style;
this.showToken = options.showToken;
this.delay = options.delay;
}
if (this.style == null) this.style = DEFAULT_TOKEN_STYLE;
if (this.minChars == null) this.minChars = DEFAULT_MIN_CHARS;
if (this.delay == null) this.delay = DEFAULT_DELAY;
this.overlay = this.timeout = null;
}
@ -45,7 +48,7 @@
function cursorActivity(cm) {
var state = cm.state.matchHighlighter;
clearTimeout(state.timeout);
state.timeout = setTimeout(function() {highlightMatches(cm);}, 100);
state.timeout = setTimeout(function() {highlightMatches(cm);}, state.delay);
}
function highlightMatches(cm) {

View File

@ -83,6 +83,7 @@
if (!cursor.find(rev)) return;
}
cm.setSelection(cursor.from(), cursor.to());
cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
state.posFrom = cursor.from(); state.posTo = cursor.to();
});}
function clearSearch(cm) {cm.operation(function() {
@ -121,6 +122,7 @@
(start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
}
cm.setSelection(cursor.from(), cursor.to());
cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
confirmDialog(cm, doReplaceConfirm, "Replace?",
[function() {doReplace(match);}, advance]);
};

View File

@ -69,8 +69,8 @@
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)
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;

View File

@ -76,7 +76,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
tagName = "";
var c;
while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
if (!tagName) return "error";
if (!tagName) return "tag error";
type = isClose ? "closeTag" : "openTag";
state.tokenize = inTag;
return "tag";
@ -109,7 +109,9 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
type = "equals";
return null;
} else if (ch == "<") {
return "error";
state.tokenize = inText;
var next = state.tokenize(stream, state);
return next ? next + " error" : "error";
} else if (/[\'\"]/.test(ch)) {
state.tokenize = inAttribute(ch);
state.stringStartCol = stream.column();
@ -261,7 +263,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
function attribute(type) {
if (type == "equals") return cont(attvalue, attributes);
if (!Kludges.allowMissing) setStyle = "error";
else if (type == "word") setStyle = "attribute";
else if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);}
return (type == "endTag" || type == "selfcloseTag") ? pass() : cont();
}
function attvalue(type) {
@ -298,7 +300,9 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
}
}
state.startOfLine = false;
return setStyle || style;
if (setStyle)
style = setStyle == "error" ? style + " error" : setStyle;
return style;
},
indent: function(state, textAfter, fullLine) {

View File

@ -7,4 +7,66 @@
MT("comma-and-binop",
"[keyword function](){ [keyword var] [def x] = [number 1] + [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];",
"})();");
MT("class",
"[keyword class] [variable Point] [keyword extends] [variable SuperThing] {",
" [[ [string-2 /expr/] ]]: [number 24],",
" [property constructor]([def x], [def y]) {",
" [keyword super]([string 'something']);",
" [keyword this].[property x] = [variable-2 x];",
" }",
"}");
MT("module",
"[keyword module] [string 'foo'] {",
" [keyword export] [keyword let] [def x] = [number 42];",
" [keyword export] [keyword *] [keyword from] [string 'somewhere'];",
"}");
MT("import",
"[keyword function] [variable foo]() {",
" [keyword import] [def $] [keyword from] [string 'jquery'];",
" [keyword module] [def crypto] [keyword from] [string 'crypto'];",
" [keyword import] { [def encrypt], [def decrypt] } [keyword from] [string 'crypto'];",
"}");
MT("const",
"[keyword function] [variable f]() {",
" [keyword const] [[ [def a], [def b] ]] = [[ [number 1], [number 2] ]];",
"}");
MT("for/of",
"[keyword for]([keyword let] [variable of] [keyword of] [variable something]) {}");
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 yield] [variable-2 i];",
"}");
MT("fatArrow",
"[variable array].[property filter]([def a] => [variable-2 a] + [number 1]);",
"[variable a];", // No longer in scope
"[keyword let] [variable f] = ([[ [def a], [def b] ]], [def c]) => [variable-2 a] + [variable-2 c];",
"[variable c];");
MT("spread",
"[keyword function] [variable f]([def a], [meta ...][def b]) {",
" [variable something]([variable-2 a], [meta ...][variable-2 b]);",
"}");
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']));",
"}");
MT("quasi",
"[variable re][string-2 `fofdlakj${][variable x] + ([variable re][string-2 `foo`]) + [number 1][string-2 }fdsa`] + [number 2]");
})();

View File

@ -59,6 +59,13 @@
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() {
@ -66,7 +73,7 @@
});
};
function compare(text, expected, mode) {
function compare(text, expected, mode, compareIndentation) {
var expectedOutput = [];
for (var i = 0; i < expected.length; i += 2) {
@ -75,7 +82,7 @@
expectedOutput.push(sty, expected[i + 1]);
}
var observedOutput = highlight(text, mode);
var observedOutput = highlight(text, mode, compareIndentation);
var pass, passStyle = "";
pass = highlightOutputsEqual(expectedOutput, observedOutput);
@ -114,7 +121,7 @@
*
* @return array of [style, token] pairs
*/
function highlight(string, mode) {
function highlight(string, mode, compareIndentation) {
var state = mode.startState()
var lines = string.replace(/\r\n/g,'\n').split('\n');
@ -125,14 +132,15 @@
if (line == "" && mode.blankLine) mode.blankLine(state);
/* Start copied code from CodeMirror.highlight */
while (!stream.eol()) {
var style = mode.token(stream, state), substr = stream.current();
if (style && style.indexOf(" ") > -1) style = style.split(' ').sort().join(' ');
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;
if (pos && st[pos-2] == style && !newLine) {
stream.start = stream.pos;
if (pos && st[pos-2] == compare && !newLine) {
st[pos-1] += substr;
} else if (substr) {
st[pos++] = style; st[pos++] = substr;
st[pos++] = compare; st[pos++] = substr;
}
// Give up when line is ridiculously long
if (stream.pos > 5000) {

View File

@ -1,5 +1,7 @@
var Pos = CodeMirror.Pos;
CodeMirror.defaults.rtlMoveVisually = true;
function forEach(arr, f) {
for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
}
@ -517,6 +519,24 @@ testCM("bookmarkCursor", function(cm) {
is(cm.cursorCoords(Pos(4, 1)).left > pos41.left, "single-char bug");
}, {value: "foo\nbar\n\n\nx\ny"});
testCM("multiBookmarkCursor", function(cm) {
if (phantom) return;
var ms = [], m;
function add(insertLeft) {
for (var i = 0; i < 3; ++i) {
var node = document.createElement("span");
node.innerHTML = "X";
ms.push(cm.setBookmark(Pos(0, 1), {widget: node, insertLeft: insertLeft}));
}
}
var base1 = cm.cursorCoords(Pos(0, 1)).left, base4 = cm.cursorCoords(Pos(0, 4)).left;
add(true);
is(Math.abs(base1 - cm.cursorCoords(Pos(0, 1)).left) < .1);
while (m = ms.pop()) m.clear();
add(false);
is(Math.abs(base4 - cm.cursorCoords(Pos(0, 1)).left) < .1);
}, {value: "abcdefg"});
testCM("getAllMarks", function(cm) {
addDoc(cm, 10, 10);
var m1 = cm.setBookmark(Pos(0, 2));
@ -852,6 +872,17 @@ testCM("changedInlineWidget", function(cm) {
is(hScroll.scrollWidth > hScroll.clientWidth);
}, {value: "hello there"});
testCM("changedBookmark", function(cm) {
cm.setSize("10em");
var w = document.createElement("span");
w.innerHTML = "x";
var m = cm.setBookmark(Pos(0, 4), {widget: w});
w.innerHTML = "and now the widget is really really long all of a sudden and a scrollbar is needed";
m.changed();
var hScroll = byClassName(cm.getWrapperElement(), "CodeMirror-hscrollbar")[0];
is(hScroll.scrollWidth > hScroll.clientWidth);
}, {value: "abcdefg"});
testCM("inlineWidget", function(cm) {
var w = cm.setBookmark(Pos(0, 2), {widget: document.createTextNode("uu")});
cm.setCursor(0, 2);
@ -1130,7 +1161,7 @@ testCM("rtlMovement", function(cm) {
prevX = cursor.offsetLeft;
}
});
}, {rtlMoveVisually: true});
});
// Verify that updating a line clears its bidi ordering
testCM("bidiUpdate", function(cm) {
@ -1311,6 +1342,7 @@ testCM("atomicMarker", function(cm) {
eqPos(cm.getCursor(), Pos(8, 3));
m.clear();
m = atom(1, 1, 3, 8);
cm.setCursor(Pos(0, 0));
cm.setCursor(Pos(2, 0));
eqPos(cm.getCursor(), Pos(3, 8));
cm.execCommand("goCharLeft");
@ -1509,3 +1541,22 @@ testCM("change_removedText", function(cm) {
eq(removedText[0].join("\n"), "abc\nd");
eq(removedText[1].join("\n"), "");
});
testCM("lineStyleFromMode", function(cm) {
CodeMirror.defineMode("test_mode", function() {
return {token: function(stream) {
if (stream.match(/^\[[^\]]*\]/)) return "line-brackets";
if (stream.match(/^\([^\]]*\)/)) return "line-background-parens";
stream.match(/^\s+|^\S+/);
}};
});
cm.setOption("mode", "test_mode");
var bracketElts = byClassName(cm.getWrapperElement(), "brackets");
eq(bracketElts.length, 1);
eq(bracketElts[0].nodeName, "PRE");
is(!/brackets.*brackets/.test(bracketElts[0].className));
var parenElts = byClassName(cm.getWrapperElement(), "parens");
eq(parenElts.length, 1);
eq(parenElts[0].nodeName, "DIV");
is(!/parens.*parens/.test(parenElts[0].className));
}, {value: "line1: [br] [br]\nline2: (par) (par)\nline3: nothing"});