Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/phpmyadmin/phpmyadmin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichal Čihař <mcihar@suse.cz>2013-08-06 15:25:39 +0400
committerMichal Čihař <mcihar@suse.cz>2013-08-06 15:27:09 +0400
commit94855c2137bab6fe6e07f6fa5ed11ae1912229e5 (patch)
tree41947f5d88574e84d43df192e4b1218dddc851bc /js/codemirror
parentcc3645592fad0f185a797e559be5c8f431435417 (diff)
Update CodeMirror to 3.15
Diffstat (limited to 'js/codemirror')
-rw-r--r--js/codemirror/lib/codemirror.js786
-rw-r--r--js/codemirror/mode/sql/sql.js112
2 files changed, 571 insertions, 327 deletions
diff --git a/js/codemirror/lib/codemirror.js b/js/codemirror/lib/codemirror.js
index ffe015ce66..1d0d996310 100644
--- a/js/codemirror/lib/codemirror.js
+++ b/js/codemirror/lib/codemirror.js
@@ -1,4 +1,4 @@
-// CodeMirror version 3.13
+// CodeMirror version 3.15
//
// CodeMirror is the only global var we claim
window.CodeMirror = (function() {
@@ -30,6 +30,7 @@ window.CodeMirror = (function() {
var opera_version = opera && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
if (opera_version) opera_version = Number(opera_version[1]);
+ if (opera_version && opera_version >= 15) { opera = false; webkit = true; }
// Some browsers use the wrong event properties to signal cmd/ctrl on OS X
var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11));
var captureMiddleClick = gecko || (ie && !ie_lt9);
@@ -238,9 +239,10 @@ window.CodeMirror = (function() {
}
function keyMapChanged(cm) {
- var style = keyMap[cm.options.keyMap].style;
+ var map = keyMap[cm.options.keyMap], style = map.style;
cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
(style ? " cm-keymap-" + style : "");
+ cm.state.disableInput = map.disableInput;
}
function themeChanged(cm) {
@@ -325,8 +327,8 @@ window.CodeMirror = (function() {
d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
d.gutters.style.height = Math.max(totalHeight, d.scroller.clientHeight - scrollerCutOff) + "px";
var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
- var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
- var needsV = scrollHeight > d.scroller.clientHeight;
+ var needsH = d.scroller.scrollWidth > (d.scroller.clientWidth + 1);
+ var needsV = scrollHeight > (d.scroller.clientHeight + 1);
if (needsV) {
d.scrollbarV.style.display = "block";
d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
@@ -402,11 +404,12 @@ window.CodeMirror = (function() {
// DISPLAY DRAWING
- function updateDisplay(cm, changes, viewPort) {
+ 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 (;;) {
- if (!updateDisplayInner(cm, changes, visible)) break;
+ if (!updateDisplayInner(cm, changes, visible, forced)) break;
+ forced = false;
updated = true;
updateSelection(cm);
updateScrollbars(cm);
@@ -432,7 +435,7 @@ window.CodeMirror = (function() {
// Uses a set of changes plus the current scroll position to
// determine which DOM updates have to be made, and makes the
// updates.
- function updateDisplayInner(cm, changes, visible) {
+ function updateDisplayInner(cm, changes, visible, forced) {
var display = cm.display, doc = cm.doc;
if (!display.wrapper.clientWidth) {
display.showingFrom = display.showingTo = doc.first;
@@ -441,7 +444,7 @@ window.CodeMirror = (function() {
}
// Bail out if the visible area is already rendered and nothing changed.
- if (changes.length == 0 &&
+ if (!forced && changes.length == 0 &&
visible.from > display.showingFrom && visible.to < display.showingTo)
return;
@@ -494,7 +497,7 @@ window.CodeMirror = (function() {
if (range.from >= range.to) intact.splice(i--, 1);
else intactLines += range.to - range.from;
}
- if (intactLines == to - from && from == display.showingFrom && to == display.showingTo) {
+ if (!forced && intactLines == to - from && from == display.showingFrom && to == display.showingTo) {
updateViewOffset(cm);
return;
}
@@ -513,10 +516,20 @@ window.CodeMirror = (function() {
display.lastSizeC != display.wrapper.clientHeight;
// This is just a bogus formula that detects when the editor is
// resized or the font size changes.
- if (different) display.lastSizeC = display.wrapper.clientHeight;
+ if (different) {
+ display.lastSizeC = display.wrapper.clientHeight;
+ startWorker(cm, 400);
+ }
display.showingFrom = from; display.showingTo = to;
- startWorker(cm, 100);
+ updateHeightsInViewport(cm);
+ updateViewOffset(cm);
+
+ return true;
+ }
+
+ function updateHeightsInViewport(cm) {
+ var display = cm.display;
var prevBottom = display.lineDiv.offsetTop;
for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
if (ie_lt8) {
@@ -536,9 +549,6 @@ window.CodeMirror = (function() {
widgets[i].height = widgets[i].node.offsetHeight;
}
}
- updateViewOffset(cm);
-
- return true;
}
function updateViewOffset(cm) {
@@ -604,8 +614,9 @@ 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[i].showIfHidden) {
+ if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i) {
+ var w = line.widgets[i];
+ if (w.showIfHidden) {
var prev = cur.previousSibling;
if (/pre/i.test(prev.nodeName)) {
var wrap = elt("div", null, null, "position: relative");
@@ -613,9 +624,11 @@ window.CodeMirror = (function() {
wrap.appendChild(prev);
prev = wrap;
}
- var wnode = prev.appendChild(elt("div", [line.widgets[i].node], "CodeMirror-linewidget"));
- positionLineWidget(line.widgets[i], wnode, prev, dims);
+ var wnode = prev.appendChild(elt("div", [w.node], "CodeMirror-linewidget"));
+ if (!w.handleMouseEvents) wnode.ignoreEvents = true;
+ positionLineWidget(w, wnode, prev, dims);
}
+ }
} else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) {
// This line is intact. Skip to the actual node. Update its
// line number if needed.
@@ -658,25 +671,25 @@ window.CodeMirror = (function() {
if (reuse) {
reuse.alignable = null;
- var isOk = true, widgetsSeen = 0;
+ var isOk = true, widgetsSeen = 0, insertBefore = null;
for (var n = reuse.firstChild, next; n; n = next) {
next = n.nextSibling;
if (!/\bCodeMirror-linewidget\b/.test(n.className)) {
reuse.removeChild(n);
} else {
- for (var i = 0, first = true; i < line.widgets.length; ++i) {
- var widget = line.widgets[i], isFirst = false;
- if (!widget.above) { isFirst = first; first = false; }
+ for (var i = 0; i < line.widgets.length; ++i) {
+ var widget = line.widgets[i];
if (widget.node == n.firstChild) {
+ if (!widget.above && !insertBefore) insertBefore = n;
positionLineWidget(widget, n, reuse, dims);
++widgetsSeen;
- if (isFirst) reuse.insertBefore(lineElement, n);
break;
}
}
if (i == line.widgets.length) { isOk = false; break; }
}
}
+ reuse.insertBefore(lineElement, insertBefore);
if (isOk && widgetsSeen == line.widgets.length) {
wrap = reuse;
reuse.className = line.wrapClass || "";
@@ -711,6 +724,7 @@ window.CodeMirror = (function() {
if (ie_lt8) wrap.style.zIndex = 2;
if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
+ if (!widget.handleMouseEvents) node.ignoreEvents = true;
positionLineWidget(widget, node, wrap, dims);
if (widget.above)
wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
@@ -793,74 +807,59 @@ window.CodeMirror = (function() {
"px; height: " + (bottom - top) + "px"));
}
- function drawForLine(line, fromArg, toArg, retTop) {
+ function drawForLine(line, fromArg, toArg) {
var lineObj = getLine(doc, line);
- var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity;
- function coords(ch) {
- return charCoords(cm, Pos(line, ch), "div", lineObj);
+ var lineLen = lineObj.text.length;
+ var start, end;
+ function coords(ch, bias) {
+ return charCoords(cm, Pos(line, ch), "div", lineObj, bias);
}
iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
- var leftPos = coords(from), rightPos, left, right;
+ var leftPos = coords(from, "left"), rightPos, left, right;
if (from == to) {
rightPos = leftPos;
left = right = leftPos.left;
} else {
- rightPos = coords(to - 1);
+ rightPos = coords(to - 1, "right");
if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
left = leftPos.left;
right = rightPos.right;
}
+ if (fromArg == null && from == 0) left = pl;
if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
add(left, leftPos.top, null, leftPos.bottom);
left = pl;
if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
}
if (toArg == null && to == lineLen) right = clientWidth;
- if (fromArg == null && from == 0) left = pl;
- rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal);
+ if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
+ start = leftPos;
+ if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
+ end = rightPos;
if (left < pl + 1) left = pl;
add(left, rightPos.top, right - left, rightPos.bottom);
});
- return rVal;
+ return {start: start, end: end};
}
if (sel.from.line == sel.to.line) {
drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
} else {
- var fromObj = getLine(doc, sel.from.line);
- var cur = fromObj, merged, path = [sel.from.line, sel.from.ch], singleLine;
- while (merged = collapsedSpanAtEnd(cur)) {
- var found = merged.find();
- path.push(found.from.ch, found.to.line, found.to.ch);
- if (found.to.line == sel.to.line) {
- path.push(sel.to.ch);
- singleLine = true;
- break;
+ var fromLine = getLine(doc, sel.from.line), toLine = getLine(doc, sel.to.line);
+ var singleVLine = visualLine(doc, fromLine) == visualLine(doc, toLine);
+ var leftEnd = drawForLine(sel.from.line, sel.from.ch, singleVLine ? fromLine.text.length : null).end;
+ var rightStart = drawForLine(sel.to.line, singleVLine ? 0 : null, sel.to.ch).start;
+ if (singleVLine) {
+ if (leftEnd.top < rightStart.top - 2) {
+ add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
+ add(pl, rightStart.top, rightStart.left, rightStart.bottom);
+ } else {
+ add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
}
- cur = getLine(doc, found.to.line);
- }
-
- // This is a single, merged line
- if (singleLine) {
- for (var i = 0; i < path.length; i += 3)
- drawForLine(path[i], path[i+1], path[i+2]);
- } else {
- var middleTop, middleBot, toObj = getLine(doc, sel.to.line);
- if (sel.from.ch)
- // Draw the first line of selection.
- middleTop = drawForLine(sel.from.line, sel.from.ch, null, false);
- else
- // Simply include it in the middle block.
- middleTop = heightAtLine(cm, fromObj) - display.viewOffset;
-
- if (!sel.to.ch)
- middleBot = heightAtLine(cm, toObj) - display.viewOffset;
- else
- middleBot = drawForLine(sel.to.line, collapsedSpanAtStart(toObj) ? null : 0, sel.to.ch, true);
-
- if (middleTop < middleBot) add(pl, middleTop, null, middleBot);
}
+ if (leftEnd.bottom < rightStart.top)
+ add(pl, leftEnd.bottom, null, rightStart.top);
}
removeChildrenAndAdd(display.selectionDiv, fragment);
@@ -926,12 +925,12 @@ window.CodeMirror = (function() {
// valid state. If that fails, it returns the line with the
// smallest indentation, which tends to need the least context to
// parse correctly.
- function findStartLine(cm, n) {
+ function findStartLine(cm, n, precise) {
var minindent, minline, doc = cm.doc;
for (var search = n, lim = n - 100; search > lim; --search) {
if (search <= doc.first) return doc.first;
var line = getLine(doc, search - 1);
- if (line.stateAfter) return search;
+ if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
var indented = countColumn(line.text, null, cm.options.tabSize);
if (minline == null || minindent > indented) {
minline = search - 1;
@@ -941,10 +940,10 @@ window.CodeMirror = (function() {
return minline;
}
- function getStateBefore(cm, n) {
+ function getStateBefore(cm, n, precise) {
var doc = cm.doc, display = cm.display;
if (!doc.mode.startState) return true;
- var pos = findStartLine(cm, n), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
+ 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) {
@@ -965,7 +964,7 @@ window.CodeMirror = (function() {
return e.offsetLeft;
}
- function measureChar(cm, line, ch, data) {
+ function measureChar(cm, line, ch, data, bias) {
var dir = -1;
data = data || measureLine(cm, line);
@@ -974,9 +973,13 @@ window.CodeMirror = (function() {
if (r) break;
if (dir < 0 && pos == 0) dir = 1;
}
+ bias = pos > ch ? "left" : pos < ch ? "right" : bias;
+ if (bias == "left" && r.leftSide) r = r.leftSide;
+ else if (bias == "right" && r.rightSide) r = r.rightSide;
return {left: pos < ch ? r.right : r.left,
right: pos > ch ? r.left : r.right,
- top: r.top, bottom: r.bottom};
+ top: r.top,
+ bottom: r.bottom};
}
function findCachedMeasurement(cm, line) {
@@ -1013,7 +1016,7 @@ window.CodeMirror = (function() {
function measureLineInner(cm, line) {
var display = cm.display, measure = emptyArray(line.text.length);
- var pre = lineContent(cm, line, measure);
+ var pre = lineContent(cm, line, measure, true);
// IE does not cache element positions of inline elements between
// calls to getBoundingClientRect. This makes the loop below,
@@ -1049,30 +1052,51 @@ window.CodeMirror = (function() {
if (ie_lt9 && display.measure.first != pre)
removeChildrenAndAdd(display.measure, pre);
- for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
- var size = getRect(cur);
- var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot);
- for (var j = 0; j < vranges.length; j += 2) {
- var rtop = vranges[j], rbot = vranges[j+1];
+ function measureRect(rect) {
+ var top = rect.top - outer.top, bot = rect.bottom - outer.top;
+ if (bot > maxBot) bot = maxBot;
+ if (top < 0) top = 0;
+ for (var i = vranges.length - 2; i >= 0; i -= 2) {
+ var rtop = vranges[i], rbot = vranges[i+1];
if (rtop > bot || rbot < top) continue;
if (rtop <= top && rbot >= bot ||
top <= rtop && bot >= rbot ||
Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
- vranges[j] = Math.min(top, rtop);
- vranges[j+1] = Math.max(bot, rbot);
+ vranges[i] = Math.min(top, rtop);
+ vranges[i+1] = Math.max(bot, rbot);
break;
}
}
- if (j == vranges.length) vranges.push(top, bot);
- var right = size.right;
- if (cur.measureRight) right = getRect(cur.measureRight).left;
- data[i] = {left: size.left - outer.left, right: right - outer.left, top: j};
+ if (i < 0) { i = vranges.length; vranges.push(top, bot); }
+ return {left: rect.left - outer.left,
+ right: rect.right - outer.left,
+ top: i, bottom: null};
}
- for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
- var vr = cur.top;
- cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
+ function finishRect(rect) {
+ rect.bottom = vranges[rect.top+1];
+ rect.top = vranges[rect.top];
}
+ for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
+ var node = cur, rect = null;
+ // A widget might wrap, needs special care
+ if (/\bCodeMirror-widget\b/.test(cur.className) && cur.getClientRects) {
+ if (cur.firstChild.nodeType == 1) node = cur.firstChild;
+ var rects = node.getClientRects();
+ if (rects.length > 1) {
+ rect = data[i] = measureRect(rects[0]);
+ rect.rightSide = measureRect(rects[rects.length - 1]);
+ }
+ }
+ if (!rect) rect = data[i] = measureRect(getRect(node));
+ if (cur.measureRight) rect.right = getRect(cur.measureRight).left;
+ if (cur.leftSide) rect.leftSide = measureRect(getRect(cur.leftSide));
+ }
+ for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
+ finishRect(cur);
+ if (cur.leftSide) finishRect(cur.leftSide);
+ if (cur.rightSide) finishRect(cur.rightSide);
+ }
return data;
}
@@ -1083,9 +1107,9 @@ 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;
+ if (cached) return measureChar(cm, line, line.text.length, cached.measure, "right").right;
- var pre = lineContent(cm, line);
+ var pre = lineContent(cm, line, null, true);
var end = pre.appendChild(zeroWidthElement(cm.display.measure));
removeChildrenAndAdd(cm.display.measure, pre);
return getRect(end).right - getRect(cm.display.lineDiv).left;
@@ -1098,6 +1122,9 @@ window.CodeMirror = (function() {
cm.display.lineNumChars = null;
}
+ function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
+ function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
+
// Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
function intoCoordSystem(cm, lineObj, rect, context) {
if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
@@ -1107,11 +1134,12 @@ window.CodeMirror = (function() {
if (context == "line") return rect;
if (!context) context = "local";
var yOff = heightAtLine(cm, lineObj);
- if (context != "local") yOff -= cm.display.viewOffset;
- if (context == "page") {
+ if (context == "local") yOff += paddingTop(cm.display);
+ else yOff -= cm.display.viewOffset;
+ if (context == "page" || context == "window") {
var lOff = getRect(cm.display.lineSpace);
- yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
- var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
+ yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
+ var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
rect.left += xOff; rect.right += xOff;
}
rect.top += yOff; rect.bottom += yOff;
@@ -1123,31 +1151,30 @@ window.CodeMirror = (function() {
function fromCoordSystem(cm, coords, context) {
if (context == "div") return coords;
var left = coords.left, top = coords.top;
+ // First move into "page" coordinate system
if (context == "page") {
- left -= window.pageXOffset || (document.documentElement || document.body).scrollLeft;
- top -= window.pageYOffset || (document.documentElement || document.body).scrollTop;
+ left -= pageScrollX();
+ top -= pageScrollY();
+ } else if (context == "local" || !context) {
+ var localBox = getRect(cm.display.sizer);
+ left += localBox.left;
+ top += localBox.top;
}
+
var lineSpaceBox = getRect(cm.display.lineSpace);
- left -= lineSpaceBox.left;
- top -= lineSpaceBox.top;
- if (context == "local" || !context) {
- var editorBox = getRect(cm.display.wrapper);
- left += editorBox.left;
- top += editorBox.top;
- }
- return {left: left, top: top};
+ return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
}
- function charCoords(cm, pos, context, lineObj) {
+ function charCoords(cm, pos, context, lineObj, bias) {
if (!lineObj) lineObj = getLine(cm.doc, pos.line);
- return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
+ return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, null, bias), context);
}
function cursorCoords(cm, pos, context, lineObj, measurement) {
lineObj = lineObj || getLine(cm.doc, pos.line);
if (!measurement) measurement = measureLine(cm, lineObj);
function get(ch, right) {
- var m = measureChar(cm, lineObj, ch, measurement);
+ var m = measureChar(cm, lineObj, ch, measurement, right ? "right" : "left");
if (right) m.left = m.right; else m.right = m.left;
return intoCoordSystem(cm, lineObj, m, context);
}
@@ -1173,8 +1200,9 @@ window.CodeMirror = (function() {
return val;
}
- function PosMaybeOutside(line, ch, outside) {
+ function PosWithInfo(line, ch, outside, xRel) {
var pos = new Pos(line, ch);
+ pos.xRel = xRel;
if (outside) pos.outside = true;
return pos;
}
@@ -1183,10 +1211,10 @@ window.CodeMirror = (function() {
function coordsChar(cm, x, y) {
var doc = cm.doc;
y += cm.display.viewOffset;
- if (y < 0) return PosMaybeOutside(doc.first, 0, true);
+ if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
if (lineNo > last)
- return PosMaybeOutside(doc.first + doc.size - 1, getLine(doc, last).text.length, true);
+ return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
if (x < 0) x = 0;
for (;;) {
@@ -1194,7 +1222,7 @@ window.CodeMirror = (function() {
var found = coordsCharInner(cm, lineObj, lineNo, x, y);
var merged = collapsedSpanAtEnd(lineObj);
var mergedPos = merged && merged.find();
- if (merged && found.ch >= mergedPos.from.ch)
+ if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
lineNo = mergedPos.to.line;
else
return found;
@@ -1220,14 +1248,15 @@ window.CodeMirror = (function() {
var from = lineLeft(lineObj), to = lineRight(lineObj);
var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
- if (x > toX) return PosMaybeOutside(lineNo, to, toOutside);
+ if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
// Do a binary search between these bounds.
for (;;) {
if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
- var after = x - fromX < toX - x, ch = after ? from : to;
+ var ch = x < fromX || x - fromX <= toX - x ? from : to;
+ var xDiff = x - (ch == from ? fromX : toX);
while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
- var pos = PosMaybeOutside(lineNo, ch, after ? fromOutside : toOutside);
- pos.after = after;
+ var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
+ xDiff < 0 ? -1 : xDiff ? 1 : 0);
return pos;
}
var step = Math.ceil(dist / 2), middle = from + step;
@@ -1284,6 +1313,7 @@ window.CodeMirror = (function() {
// An array of ranges of lines that have to be updated. See
// updateDisplay.
changes: [],
+ forceUpdate: false,
updateInput: null,
userSelChange: null,
textChanged: null,
@@ -1316,8 +1346,8 @@ window.CodeMirror = (function() {
var coords = cursorCoords(cm, doc.sel.head);
newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
}
- if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null) {
- updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop);
+ if (op.changes.length || op.forceUpdate || newScrollPos && newScrollPos.scrollTop != null) {
+ updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop, op.forceUpdate);
if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop;
}
if (!updated && op.selectionChanged) updateSelection(cm);
@@ -1413,7 +1443,7 @@ window.CodeMirror = (function() {
// supported or compatible enough yet to rely on.)
function readInput(cm) {
var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
- if (!cm.state.focused || hasSelection(input) || isReadOnly(cm)) return false;
+ if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.state.disableInput) return false;
var text = input.value;
if (text == prevInput && posEq(sel.from, sel.to)) return false;
if (ie && !ie_lt9 && cm.display.inputHasSelection === text) {
@@ -1431,11 +1461,14 @@ window.CodeMirror = (function() {
from = Pos(from.line, from.ch - (prevInput.length - same));
else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming)
to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
- var updateInput = cm.curOp.updateInput;
- makeChange(cm.doc, {from: from, to: to, text: splitLines(text.slice(same)),
- origin: cm.state.pasteIncoming ? "paste" : "+input"}, "end");
+ var updateInput = cm.curOp.updateInput;
+ var changeEvent = {from: from, to: to, text: splitLines(text.slice(same)),
+ origin: cm.state.pasteIncoming ? "paste" : "+input"};
+ makeChange(cm.doc, changeEvent, "end");
cm.curOp.updateInput = updateInput;
+ signalLater(cm, "inputRead", cm, changeEvent);
+
if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
else cm.display.prevInput = text;
if (withOp) endOperation(cm);
@@ -1476,6 +1509,7 @@ window.CodeMirror = (function() {
on(d.scroller, "mousedown", operation(cm, onMouseDown));
if (ie)
on(d.scroller, "dblclick", operation(cm, function(e) {
+ if (signalDOMEvent(cm, e)) return;
var pos = posFromMouse(cm, e);
if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
e_preventDefault(e);
@@ -1483,7 +1517,7 @@ window.CodeMirror = (function() {
extendSelection(cm.doc, word.from, word.to);
}));
else
- on(d.scroller, "dblclick", e_preventDefault);
+ on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
on(d.lineSpace, "selectstart", function(e) {
if (!eventInWidget(d, e)) e_preventDefault(e);
});
@@ -1515,11 +1549,15 @@ window.CodeMirror = (function() {
// Prevent wrapper from ever scrolling
on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
+ var resizeTimer;
function onResize() {
- // Might be a text scaling operation, clear size caches.
- d.cachedCharWidth = d.cachedTextHeight = null;
- clearCaches(cm);
- runInOp(cm, bind(regChange, cm));
+ if (resizeTimer == null) resizeTimer = setTimeout(function() {
+ resizeTimer = null;
+ // Might be a text scaling operation, clear size caches.
+ d.cachedCharWidth = d.cachedTextHeight = knownScrollbarWidth = null;
+ clearCaches(cm);
+ runInOp(cm, bind(regChange, cm));
+ }, 100);
}
on(window, "resize", onResize);
// Above handler holds on to the editor and its data structures.
@@ -1533,7 +1571,7 @@ window.CodeMirror = (function() {
setTimeout(unregister, 5000);
on(d.input, "keyup", operation(cm, function(e) {
- if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
+ 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));
@@ -1543,7 +1581,7 @@ window.CodeMirror = (function() {
on(d.input, "blur", bind(onBlur, cm));
function drag_(e) {
- if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
+ if (signalDOMEvent(cm, e) || cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
e_stop(e);
}
if (cm.options.dragDrop) {
@@ -1582,9 +1620,7 @@ window.CodeMirror = (function() {
function eventInWidget(display, e) {
for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
- if (!n) return true;
- if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
- n.parentNode == display.sizer && n != display.mover) return true;
+ if (!n || n.ignoreEvents || n.parentNode == display.sizer && n != display.mover) return true;
}
}
@@ -1604,6 +1640,7 @@ window.CodeMirror = (function() {
var lastClick, lastDoubleClick;
function onMouseDown(e) {
+ if (signalDOMEvent(this, e)) return;
var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel;
sel.shift = e.shiftKey;
@@ -1727,8 +1764,6 @@ window.CodeMirror = (function() {
function done(e) {
counter = Infinity;
- var cur = posFromMouse(cm, e);
- if (cur) doSelect(cur);
e_preventDefault(e);
focusInput(cm);
off(document, "mousemove", move);
@@ -1744,11 +1779,41 @@ window.CodeMirror = (function() {
on(document, "mouseup", up);
}
+ function clickInGutter(cm, e) {
+ var display = cm.display;
+ try { var mX = e.clientX, mY = e.clientY; }
+ catch(e) { return false; }
+
+ if (mX >= Math.floor(getRect(display.gutters).right)) return false;
+ e_preventDefault(e);
+ if (!hasHandler(cm, "gutterClick")) return true;
+
+ var lineBox = getRect(display.lineDiv);
+ if (mY > lineBox.bottom) return true;
+ mY -= lineBox.top - display.viewOffset;
+
+ for (var i = 0; i < cm.options.gutters.length; ++i) {
+ var g = display.gutters.childNodes[i];
+ 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;
+ }
+ }
+ return true;
+ }
+
+ // Kludge to work around strange IE behavior where it'll sometimes
+ // re-fire a series of drag-related events right after the drop (#1551)
+ var lastDrop = 0;
+
function onDrop(e) {
var cm = this;
- if (eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
+ if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
return;
e_preventDefault(e);
+ if (ie) lastDrop = +new Date;
var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
if (!pos || isReadOnly(cm)) return;
if (files && files.length && window.FileReader && window.File) {
@@ -1788,41 +1853,16 @@ window.CodeMirror = (function() {
}
}
- function clickInGutter(cm, e) {
- var display = cm.display;
- try { var mX = e.clientX, mY = e.clientY; }
- catch(e) { return false; }
-
- if (mX >= Math.floor(getRect(display.gutters).right)) return false;
- e_preventDefault(e);
- if (!hasHandler(cm, "gutterClick")) return true;
-
- var lineBox = getRect(display.lineDiv);
- if (mY > lineBox.bottom) return true;
- mY -= lineBox.top - display.viewOffset;
-
- for (var i = 0; i < cm.options.gutters.length; ++i) {
- var g = display.gutters.childNodes[i];
- 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;
- }
- }
- return true;
- }
-
function onDragStart(cm, e) {
- if (ie && !cm.state.draggingText) { e_stop(e); return; }
- if (eventInWidget(cm.display, e)) return;
+ if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
+ if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
var txt = cm.getSelection();
e.dataTransfer.setData("Text", txt);
// Use dummy image instead of default browsers image.
// Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
- if (e.dataTransfer.setDragImage) {
+ if (e.dataTransfer.setDragImage && !safari) {
var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
if (opera) {
img.width = img.height = 1;
@@ -1830,15 +1870,6 @@ window.CodeMirror = (function() {
// Force a relayout, or Opera won't use our image for some obscure reason
img._top = img.offsetTop;
}
- if (safari) {
- if (cm.display.dragImg) {
- img = cm.display.dragImg;
- } else {
- cm.display.dragImg = img;
- img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
- cm.display.wrapper.appendChild(img);
- }
- }
e.dataTransfer.setDragImage(img, 0, 0);
if (opera) img.parentNode.removeChild(img);
}
@@ -1851,6 +1882,7 @@ window.CodeMirror = (function() {
if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
if (gecko) updateDisplay(cm, []);
+ startWorker(cm, 100);
}
function setScrollLeft(cm, val, isScroller) {
if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
@@ -1983,8 +2015,10 @@ window.CodeMirror = (function() {
var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
clearTimeout(maybeTransition);
if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
- if (getKeyMap(cm.options.keyMap) == startMap)
+ if (getKeyMap(cm.options.keyMap) == startMap) {
cm.options.keyMap = (next.call ? next.call(null, cm) : next);
+ keyMapChanged(cm);
+ }
}, 50);
var name = keyName(e, true), handled = false;
@@ -1997,17 +2031,18 @@ window.CodeMirror = (function() {
// 'go') bound to the keyname without 'Shift-'.
handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);})
|| lookupKey(name, keymaps, function(b) {
- if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b);
+ if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
+ return doHandleBinding(cm, b);
});
} else {
handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); });
}
- if (handled == "stop") handled = false;
if (handled) {
e_preventDefault(e);
restartBlink(cm);
if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
+ signalLater(cm, "keyHandled", cm, name, e);
}
return handled;
}
@@ -2018,6 +2053,7 @@ window.CodeMirror = (function() {
if (handled) {
e_preventDefault(e);
restartBlink(cm);
+ signalLater(cm, "keyHandled", cm, "'" + ch + "'", e);
}
return handled;
}
@@ -2027,7 +2063,7 @@ window.CodeMirror = (function() {
var cm = this;
if (!cm.state.focused) onFocus(cm);
if (ie && e.keyCode == 27) { e.returnValue = false; }
- if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
+ if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
var code = e.keyCode;
// IE does strange things with escape.
cm.doc.sel.shift = code == 16 || e.shiftKey;
@@ -2043,7 +2079,7 @@ window.CodeMirror = (function() {
function onKeyPress(e) {
var cm = this;
- if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
+ if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
var keyCode = e.keyCode, charCode = e.charCode;
if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
@@ -2081,6 +2117,7 @@ window.CodeMirror = (function() {
var detectingSelectAll;
function onContextMenu(cm, e) {
+ if (signalDOMEvent(cm, e, "contextmenu")) return;
var display = cm.display, sel = cm.doc.sel;
if (eventInWidget(display, e)) return;
@@ -2141,11 +2178,11 @@ window.CodeMirror = (function() {
// UPDATING
- function changeEnd(change) {
+ var changeEnd = CodeMirror.changeEnd = function(change) {
if (!change.text) return change.to;
return Pos(change.from.line + change.text.length - 1,
lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
- }
+ };
// Make sure a position will be valid after the given change.
function clipPostChange(doc, change, pos) {
@@ -2187,21 +2224,21 @@ window.CodeMirror = (function() {
return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)};
}
- function filterChange(doc, change) {
+ function filterChange(doc, change, update) {
var obj = {
canceled: false,
from: change.from,
to: change.to,
text: change.text,
origin: change.origin,
- update: function(from, to, text, origin) {
- if (from) this.from = clipPos(doc, from);
- if (to) this.to = clipPos(doc, to);
- if (text) this.text = text;
- if (origin !== undefined) this.origin = origin;
- },
cancel: function() { this.canceled = true; }
};
+ if (update) obj.update = function(from, to, text, origin) {
+ if (from) this.from = clipPos(doc, from);
+ if (to) this.to = clipPos(doc, to);
+ if (text) this.text = text;
+ if (origin !== undefined) this.origin = origin;
+ };
signal(doc, "beforeChange", doc, obj);
if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
@@ -2218,7 +2255,7 @@ window.CodeMirror = (function() {
}
if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
- change = filterChange(doc, change);
+ change = filterChange(doc, change, true);
if (!change) return;
}
@@ -2257,15 +2294,23 @@ window.CodeMirror = (function() {
var hist = doc.history;
var event = (type == "undo" ? hist.done : hist.undone).pop();
if (!event) return;
- hist.dirtyCounter += type == "undo" ? -1 : 1;
var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter,
- anchorAfter: event.anchorBefore, headAfter: event.headBefore};
+ anchorAfter: event.anchorBefore, headAfter: event.headBefore,
+ generation: hist.generation};
(type == "undo" ? hist.undone : hist.done).push(anti);
+ hist.generation = event.generation || ++hist.maxGeneration;
+
+ var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
for (var i = event.changes.length - 1; i >= 0; --i) {
var change = event.changes[i];
change.origin = type;
+ if (filter && !filterChange(doc, change, false)) {
+ (type == "undo" ? hist.done : hist.undone).length = 0;
+ return;
+ }
+
anti.changes.push(historyChangeFromChange(doc, change));
var after = i ? computeSelAfterChange(doc, change, null)
@@ -2529,9 +2574,9 @@ window.CodeMirror = (function() {
function scrollCursorIntoView(cm) {
var coords = scrollPosIntoView(cm, cm.doc.sel.head, cm.options.cursorScrollMargin);
if (!cm.state.focused) return;
- var display = cm.display, box = getRect(display.sizer), doScroll = null, pTop = paddingTop(cm.display);
- if (coords.top + pTop + box.top < 0) doScroll = true;
- else if (coords.bottom + pTop + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
+ var display = cm.display, box = getRect(display.sizer), doScroll = null;
+ if (coords.top + box.top < 0) doScroll = true;
+ else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
if (doScroll != null && !phantom) {
var hidden = display.cursor.style.display == "none";
if (hidden) {
@@ -2569,12 +2614,11 @@ window.CodeMirror = (function() {
}
function calculateScrollPos(cm, x1, y1, x2, y2) {
- var display = cm.display, pt = paddingTop(display);
- y1 += pt; y2 += pt;
+ var display = cm.display, snapMargin = textHeight(cm.display);
if (y1 < 0) y1 = 0;
var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
var docBottom = cm.doc.height + paddingVert(display);
- var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
+ var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
if (y1 < screentop) {
result.scrollTop = atTop ? 0 : y1;
} else if (y2 > screentop + screen) {
@@ -2611,7 +2655,7 @@ window.CodeMirror = (function() {
function indentLine(cm, n, how, aggressive) {
var doc = cm.doc;
- if (!how) how = "add";
+ if (how == null) how = "add";
if (how == "smart") {
if (!cm.doc.mode.indent) how = "prev";
else var state = getStateBefore(cm, n);
@@ -2634,6 +2678,8 @@ window.CodeMirror = (function() {
indentation = curSpace + cm.options.indentUnit;
} else if (how == "subtract") {
indentation = curSpace - cm.options.indentUnit;
+ } else if (typeof how == "number") {
+ indentation = curSpace + how;
}
indentation = Math.max(0, indentation);
@@ -2722,7 +2768,7 @@ window.CodeMirror = (function() {
function findWordAt(line, pos) {
var start = pos.ch, end = pos.ch;
if (line) {
- if (pos.after === false || end == line.length) --start; else ++end;
+ if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
var startChar = line.charAt(start);
var check = isWordChar(startChar) ? isWordChar
: /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
@@ -2763,7 +2809,7 @@ window.CodeMirror = (function() {
removeKeyMap: function(map) {
var maps = this.state.keyMaps;
for (var i = 0; i < maps.length; ++i)
- if ((typeof map == "string" ? maps[i].name : maps[i]) == map) {
+ if (maps[i] == map || (typeof maps[i] != "string" && maps[i].name == map)) {
maps.splice(i, 1);
return true;
}
@@ -2779,7 +2825,8 @@ window.CodeMirror = (function() {
removeOverlay: operation(null, function(spec) {
var overlays = this.state.overlays;
for (var i = 0; i < overlays.length; ++i) {
- if (overlays[i].modeSpec == spec) {
+ var cur = overlays[i].modeSpec;
+ if (cur == spec || typeof spec == "string" && cur.name == spec) {
overlays.splice(i, 1);
this.state.modeGen++;
regChange(this);
@@ -2789,7 +2836,7 @@ window.CodeMirror = (function() {
}),
indentLine: operation(null, function(n, dir, aggressive) {
- if (typeof dir != "string") {
+ if (typeof dir != "string" && typeof dir != "number") {
if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
else dir = dir ? "add" : "subtract";
}
@@ -2804,10 +2851,10 @@ window.CodeMirror = (function() {
// Fetch the parser token for a given character. Useful for hacks
// that want to inspect the mode state (say, for completion).
- getTokenAt: function(pos) {
+ getTokenAt: function(pos, precise) {
var doc = this.doc;
pos = clipPos(doc, pos);
- var state = getStateBefore(this, pos.line), mode = this.doc.mode;
+ var state = getStateBefore(this, pos.line, precise), mode = this.doc.mode;
var line = getLine(doc, pos.line);
var stream = new StringStream(line.text, this.options.tabSize);
while (stream.pos < pos.ch && !stream.eol()) {
@@ -2822,10 +2869,37 @@ window.CodeMirror = (function() {
state: state};
},
- getStateAfter: function(line) {
+ getTokenTypeAt: function(pos) {
+ pos = clipPos(this.doc, pos);
+ var styles = getLineStyles(this, getLine(this.doc, pos.line));
+ var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
+ if (ch == 0) return styles[2];
+ for (;;) {
+ var mid = (before + after) >> 1;
+ if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
+ else if (styles[mid * 2 + 1] < ch) before = mid + 1;
+ else return styles[mid * 2 + 2];
+ }
+ },
+
+ getModeAt: function(pos) {
+ var mode = this.doc.mode;
+ if (!mode.innerMode) return mode;
+ return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode;
+ },
+
+ getHelper: function(pos, type) {
+ if (!helpers.hasOwnProperty(type)) return;
+ var help = helpers[type], mode = this.getModeAt(pos);
+ return mode[type] && help[mode[type]] ||
+ mode.helperType && help[mode.helperType] ||
+ help[mode.name];
+ },
+
+ getStateAfter: function(line, precise) {
var doc = this.doc;
line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
- return getStateBefore(this, line + 1);
+ return getStateBefore(this, line + 1, precise);
},
cursorCoords: function(start, mode) {
@@ -2845,6 +2919,19 @@ window.CodeMirror = (function() {
return coordsChar(this, coords.left, coords.top);
},
+ lineAtHeight: function(height, mode) {
+ height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
+ return lineAtHeight(this.doc, height + this.display.viewOffset);
+ },
+ heightAtLine: function(line, mode) {
+ var end = false, last = this.doc.first + this.doc.size - 1;
+ if (line < this.doc.first) line = this.doc.first;
+ else if (line > last) { line = last; end = true; }
+ var lineObj = getLine(this.doc, line);
+ return intoCoordSystem(this, getLine(this.doc, line), {top: 0, left: 0}, mode || "page").top +
+ (end ? lineObj.height : 0);
+ },
+
defaultTextHeight: function() { return textHeight(this.display); },
defaultCharWidth: function() { return charWidth(this.display); },
@@ -2873,7 +2960,7 @@ window.CodeMirror = (function() {
return changeLine(this, handle, function(line) {
var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
if (!line[prop]) line[prop] = cls;
- else if (new RegExp("\\b" + cls + "\\b").test(line[prop])) return false;
+ else if (new RegExp("(?:^|\\s)" + cls + "(?:$|\\s)").test(line[prop])) return false;
else line[prop] += " " + cls;
return true;
});
@@ -2886,9 +2973,10 @@ window.CodeMirror = (function() {
if (!cur) return false;
else if (cls == null) line[prop] = null;
else {
- var upd = cur.replace(new RegExp("^" + cls + "\\b\\s*|\\s*\\b" + cls + "\\b"), "");
- if (upd == cur) return false;
- line[prop] = upd || null;
+ var found = cur.match(new RegExp("(?:^|\\s+)" + cls + "(?:$|\\s+)"));
+ if (!found) return false;
+ var end = found.index + found[0].length;
+ line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
}
return true;
});
@@ -2936,7 +3024,7 @@ window.CodeMirror = (function() {
if (left + node.offsetWidth > hspace)
left = hspace - node.offsetWidth;
}
- node.style.top = (top + paddingTop(display)) + "px";
+ node.style.top = top + "px";
node.style.left = node.style.right = "";
if (horiz == "right") {
left = display.sizer.clientWidth - node.offsetWidth;
@@ -3037,17 +3125,16 @@ window.CodeMirror = (function() {
updateScrollPos(this, sPos.scrollLeft, sPos.scrollTop);
}),
- setSize: function(width, height) {
+ setSize: operation(null, function(width, height) {
function interpret(val) {
return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
}
if (width != null) this.display.wrapper.style.width = interpret(width);
if (height != null) this.display.wrapper.style.height = interpret(height);
- this.refresh();
- },
-
- on: function(type, f) {on(this, type, f);},
- off: function(type, f) {off(this, type, f);},
+ if (this.options.lineWrapping)
+ this.display.measureLineCache.length = this.display.measureLineCachePos = 0;
+ this.curOp.forceUpdate = true;
+ }),
operation: function(f){return runInOp(this, f);},
@@ -3072,6 +3159,7 @@ window.CodeMirror = (function() {
getScrollerElement: function(){return this.display.scroller;},
getGutterElement: function(){return this.display.gutters;}
};
+ eventMixin(CodeMirror);
// OPTION DEFAULTS
@@ -3196,7 +3284,7 @@ window.CodeMirror = (function() {
};
CodeMirror.getMode = function(options, spec) {
- spec = CodeMirror.resolveMode(spec);
+ var spec = CodeMirror.resolveMode(spec);
var mfactory = modes[spec.name];
if (!mfactory) return CodeMirror.getMode(options, "text/plain");
var modeObj = mfactory(options, spec);
@@ -3209,6 +3297,7 @@ window.CodeMirror = (function() {
}
}
modeObj.name = spec.name;
+
return modeObj;
};
@@ -3236,6 +3325,16 @@ window.CodeMirror = (function() {
var initHooks = [];
CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
+ var helpers = CodeMirror.helpers = {};
+ CodeMirror.registerHelper = function(type, name, value) {
+ if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {};
+ helpers[type][name] = value;
+ };
+
+ // UTILITIES
+
+ CodeMirror.isWordChar = isWordChar;
+
// MODE STATE HANDLING
// Utility functions for working with state. Exported because modes
@@ -3261,6 +3360,7 @@ window.CodeMirror = (function() {
CodeMirror.innerMode = function(mode, state) {
while (mode.innerMode) {
var info = mode.innerMode(state);
+ if (!info || info.mode == mode) break;
state = info.state;
mode = info.mode;
}
@@ -3281,6 +3381,10 @@ window.CodeMirror = (function() {
var l = cm.getCursor().line;
cm.replaceRange("", Pos(l, 0), Pos(l), "+delete");
},
+ delLineLeft: function(cm) {
+ var cur = cm.getCursor();
+ cm.replaceRange("", Pos(cur.line, 0), cur, "+delete");
+ },
undo: function(cm) {cm.undo();},
redo: function(cm) {cm.redo();},
goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
@@ -3376,7 +3480,7 @@ window.CodeMirror = (function() {
"Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
"Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
"Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
- "Cmd-[": "indentLess", "Cmd-]": "indentMore",
+ "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delLineLeft",
fallthrough: ["basic", "emacsy"]
};
keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
@@ -3415,7 +3519,7 @@ window.CodeMirror = (function() {
for (var i = 0; i < maps.length; ++i) {
var done = lookup(maps[i]);
- if (done) return done;
+ if (done) return done != "stop";
}
}
function isModifierKey(event) {
@@ -3569,11 +3673,16 @@ window.CodeMirror = (function() {
this.doc = doc;
}
CodeMirror.TextMarker = TextMarker;
+ eventMixin(TextMarker);
TextMarker.prototype.clear = function() {
if (this.explicitlyCleared) return;
var cm = this.doc.cm, withOp = cm && !cm.curOp;
if (withOp) startOperation(cm);
+ if (hasHandler(this, "clear")) {
+ var found = this.find();
+ if (found) signalLater(this, "clear", found.from, found.to);
+ }
var min = null, max = null;
for (var i = 0; i < this.lines.length; ++i) {
var line = this.lines[i];
@@ -3597,12 +3706,11 @@ window.CodeMirror = (function() {
if (min != null && cm) regChange(cm, min, max + 1);
this.lines.length = 0;
this.explicitlyCleared = true;
- if (this.collapsed && this.doc.cantEdit) {
+ if (this.atomic && this.doc.cantEdit) {
this.doc.cantEdit = false;
if (cm) reCheckSelection(cm);
}
if (withOp) endOperation(cm);
- signalLater(this, "clear");
};
TextMarker.prototype.find = function() {
@@ -3630,7 +3738,9 @@ window.CodeMirror = (function() {
if (node.offsetHeight != line.height) updateLineHeight(line, node.offsetHeight);
break;
}
- runInOp(cm, function() { cm.curOp.selectionChanged = true; });
+ runInOp(cm, function() {
+ cm.curOp.selectionChanged = cm.curOp.forceUpdate = cm.curOp.updateMaxLine = true;
+ });
}
};
@@ -3660,6 +3770,7 @@ window.CodeMirror = (function() {
if (marker.replacedWith) {
marker.collapsed = true;
marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
+ if (!options.handleMouseEvents) marker.replacedWith.ignoreEvents = true;
}
if (marker.collapsed) sawCollapsedSpans = true;
@@ -3702,7 +3813,7 @@ window.CodeMirror = (function() {
}
if (cm) {
if (updateMaxLine) cm.curOp.updateMaxLine = true;
- if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed)
+ if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.collapsed)
regChange(cm, from.line, to.line + 1);
if (marker.atomic) reCheckSelection(cm);
}
@@ -3720,6 +3831,7 @@ window.CodeMirror = (function() {
}
}
CodeMirror.SharedTextMarker = SharedTextMarker;
+ eventMixin(SharedTextMarker);
SharedTextMarker.prototype.clear = function() {
if (this.explicitlyCleared) return;
@@ -3833,6 +3945,13 @@ window.CodeMirror = (function() {
}
}
}
+ if (sameLine && first) {
+ // Make sure we didn't create any zero-length spans
+ for (var i = 0; i < first.length; ++i)
+ if (first[i].from != null && first[i].from == first[i].to && first[i].marker.type != "bookmark")
+ first.splice(i--, 1);
+ if (!first.length) first = null;
+ }
var newMarkers = [first];
if (!sameLine) {
@@ -3927,6 +4046,7 @@ window.CodeMirror = (function() {
sp = sps[i];
if (!sp.marker.collapsed) continue;
if (sp.from == null) return true;
+ if (sp.marker.replacedWith) continue;
if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
return true;
}
@@ -3940,7 +4060,7 @@ window.CodeMirror = (function() {
return true;
for (var sp, i = 0; i < line.markedSpans.length; ++i) {
sp = line.markedSpans[i];
- if (sp.marker.collapsed && sp.from == span.to &&
+ if (sp.marker.collapsed && !sp.marker.replacedWith && sp.from == span.to &&
(sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
lineIsHiddenInner(doc, line, sp)) return true;
}
@@ -3964,11 +4084,12 @@ window.CodeMirror = (function() {
// LINE WIDGETS
var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
- for (var opt in options) if (options.hasOwnProperty(opt))
+ if (options) for (var opt in options) if (options.hasOwnProperty(opt))
this[opt] = options[opt];
this.cm = cm;
this.node = node;
};
+ eventMixin(LineWidget);
function widgetOperation(f) {
return function() {
var withOp = !this.cm.curOp;
@@ -3983,7 +4104,9 @@ window.CodeMirror = (function() {
if (no == null || !ws) return;
for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
if (!ws.length) this.line.widgets = null;
+ var aboveVisible = heightAtLine(this.cm, this.line) < this.cm.doc.scrollTop;
updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this)));
+ if (aboveVisible) addToScrollPos(this.cm, 0, -this.height);
regChange(this.cm, no, no + 1);
});
LineWidget.prototype.changed = widgetOperation(function() {
@@ -4007,10 +4130,12 @@ window.CodeMirror = (function() {
var widget = new LineWidget(cm, node, options);
if (widget.noHScroll) cm.display.alignWidgets = true;
changeLine(cm, handle, function(line) {
- (line.widgets || (line.widgets = [])).push(widget);
+ var widgets = line.widgets || (line.widgets = []);
+ if (widget.insertAt == null) widgets.push(widget);
+ else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget);
widget.line = line;
if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) {
- var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop;
+ var aboveVisible = heightAtLine(cm, line) < cm.doc.scrollTop;
updateLineHeight(line, line.height + widgetHeight(widget));
if (aboveVisible) addToScrollPos(cm, 0, widget.height);
}
@@ -4023,12 +4148,12 @@ window.CodeMirror = (function() {
// Line objects. These hold state related to a line, including
// highlighting info (the styles array).
- function makeLine(text, markedSpans, estimateHeight) {
- var line = {text: text};
- attachMarkedSpans(line, markedSpans);
- line.height = estimateHeight ? estimateHeight(line) : 1;
- return line;
- }
+ var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) {
+ this.text = text;
+ attachMarkedSpans(this, markedSpans);
+ this.height = estimateHeight ? estimateHeight(this) : 1;
+ };
+ eventMixin(Line);
function updateLine(line, text, markedSpans, estimateHeight) {
line.text = text;
@@ -4052,7 +4177,7 @@ window.CodeMirror = (function() {
function runMode(cm, text, mode, state, f) {
var flattenSpans = mode.flattenSpans;
if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
- var curText = "", curStyle = null;
+ var curStart = 0, curStyle = null;
var stream = new StringStream(text, cm.options.tabSize), style;
if (text == "" && mode.blankLine) mode.blankLine(state);
while (!stream.eol()) {
@@ -4064,14 +4189,13 @@ window.CodeMirror = (function() {
} else {
style = mode.token(stream, state);
}
- var substr = stream.current();
- stream.start = stream.pos;
if (!flattenSpans || curStyle != style) {
- if (curText) f(curText, curStyle);
- curText = substr; curStyle = style;
- } else curText = curText + substr;
+ if (curStart < stream.start) f(stream.start, curStyle);
+ curStart = stream.start; curStyle = style;
+ }
+ stream.start = stream.pos;
}
- if (curText) f(curText, curStyle);
+ if (curStart < stream.pos) f(stream.pos, curStyle);
}
function highlightLine(cm, line, state) {
@@ -4079,27 +4203,24 @@ window.CodeMirror = (function() {
// 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(txt, style) {st.push(txt, style);});
+ runMode(cm, line.text, cm.doc.mode, state, function(end, style) {st.push(end, style);});
// Run overlays, adjust style array.
for (var o = 0; o < cm.state.overlays.length; ++o) {
- var overlay = cm.state.overlays[o], i = 1;
- runMode(cm, line.text, overlay.mode, true, function(txt, style) {
- var start = i, len = txt.length;
+ var overlay = cm.state.overlays[o], i = 1, at = 0;
+ runMode(cm, line.text, overlay.mode, true, function(end, style) {
+ var start = i;
// Ensure there's a token end at the current position, and that i points at it
- while (len) {
- var cur = st[i], len_ = cur.length;
- if (len_ <= len) {
- len -= len_;
- } else {
- st.splice(i, 1, cur.slice(0, len), st[i+1], cur.slice(len));
- len = 0;
- }
+ while (at < end) {
+ var i_end = st[i];
+ if (i_end > end)
+ st.splice(i, 1, end, st[i+1], i_end);
i += 2;
+ at = Math.min(end, i_end);
}
if (!style) return;
if (overlay.opaque) {
- st.splice(start, i - start, txt, style);
+ st.splice(start, i - start, end, style);
i = start + 2;
} else {
for (; start < i; start += 2) {
@@ -4138,38 +4259,33 @@ window.CodeMirror = (function() {
(styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-"));
}
- function lineContent(cm, realLine, measure) {
- var merged, line = realLine, lineBefore, sawBefore, simple = true;
- while (merged = collapsedSpanAtStart(line)) {
- simple = false;
+ function lineContent(cm, realLine, measure, copyWidgets) {
+ var merged, line = realLine, empty = true;
+ while (merged = collapsedSpanAtStart(line))
line = getLine(cm.doc, merged.find().from.line);
- if (!lineBefore) lineBefore = line;
- }
- var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure,
- measure: null, addedOne: false, cm: cm};
+ 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;
builder.measure = line == realLine && measure;
builder.pos = 0;
builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
if ((ie || webkit) && cm.getOption("lineWrapping"))
builder.addToken = buildTokenSplitSpaces(builder.addToken);
- if (measure && sawBefore && line != realLine && !builder.addedOne) {
- measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
- builder.addedOne = true;
- }
var next = insertLineContent(line, builder, getLineStyles(cm, line));
- sawBefore = line == lineBefore;
- if (next) {
- line = getLine(cm.doc, next.to.line);
- simple = false;
+ if (measure && line == realLine && !builder.measuredSomething) {
+ measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
+ builder.measuredSomething = true;
}
+ if (next) line = getLine(cm.doc, next.to.line);
} while (next);
- if (measure && !builder.addedOne)
- measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
+ if (measure && !builder.measuredSomething && !measure[0])
+ measure[0] = builder.pre.appendChild(empty ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
if (!builder.pre.firstChild && !lineIsHidden(cm.doc, realLine))
builder.pre.appendChild(document.createTextNode("\u00a0"));
@@ -4193,7 +4309,7 @@ window.CodeMirror = (function() {
}
var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g;
- function buildToken(builder, text, style, startStyle, endStyle) {
+ function buildToken(builder, text, style, startStyle, endStyle, title) {
if (!text) return;
if (!tokenSpecialChars.test(text)) {
builder.col += text.length;
@@ -4226,7 +4342,9 @@ window.CodeMirror = (function() {
var fullStyle = style || "";
if (startStyle) fullStyle += startStyle;
if (endStyle) fullStyle += endStyle;
- return builder.pre.appendChild(elt("span", [content], fullStyle));
+ var token = elt("span", [content], fullStyle);
+ if (title) token.title = title;
+ return builder.pre.appendChild(token);
}
builder.pre.appendChild(content);
}
@@ -4241,9 +4359,11 @@ window.CodeMirror = (function() {
} else if (i && wrapping && spanAffectsWrapping(text, i)) {
builder.pre.appendChild(elt("wbr"));
}
+ var old = builder.measure[builder.pos];
var span = builder.measure[builder.pos] =
buildToken(builder, ch, style,
start && startStyle, i == text.length - 1 && endStyle);
+ if (old) span.leftSide = old.leftSide || old;
// In IE single-space nodes wrap differently than spaces
// embedded in larger text nodes, except when set to
// white-space: normal (issue #1268).
@@ -4252,7 +4372,7 @@ window.CodeMirror = (function() {
span.style.whiteSpace = "normal";
builder.pos += ch.length;
}
- if (text.length) builder.addedOne = true;
+ if (text.length) builder.measuredSomething = true;
}
function buildTokenSplitSpaces(inner) {
@@ -4262,18 +4382,27 @@ window.CodeMirror = (function() {
out += " ";
return out;
}
- return function(builder, text, style, startStyle, endStyle) {
- return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle);
+ return function(builder, text, style, startStyle, endStyle, title) {
+ return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle, title);
};
}
- function buildCollapsedSpan(builder, size, widget) {
+ function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
+ var widget = !ignoreWidget && marker.replacedWith;
if (widget) {
- if (!builder.display) widget = widget.cloneNode(true);
+ if (builder.copyWidgets) widget = widget.cloneNode(true);
builder.pre.appendChild(widget);
- if (builder.measure && size) {
- builder.measure[builder.pos] = widget;
- builder.addedOne = true;
+ if (builder.measure) {
+ 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);
+ else
+ builder.pre.appendChild(elt);
+ }
+ builder.measuredSomething = true;
}
}
builder.pos += size;
@@ -4282,19 +4411,18 @@ window.CodeMirror = (function() {
// Outputs a number of spans to make up a line, taking highlighting
// and marked text into account.
function insertLineContent(line, builder, styles) {
- var spans = line.markedSpans;
+ var spans = line.markedSpans, allText = line.text, at = 0;
if (!spans) {
for (var i = 1; i < styles.length; i+=2)
- builder.addToken(builder, styles[i], styleToClass(styles[i+1]));
+ builder.addToken(builder, allText.slice(at, at = styles[i]), styleToClass(styles[i+1]));
return;
}
- var allText = line.text, len = allText.length;
- var pos = 0, i = 1, text = "", style;
- var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
+ var len = allText.length, pos = 0, i = 1, text = "", style;
+ var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;
for (;;) {
if (nextChange == pos) { // Update current marker set
- spanStyle = spanEndStyle = spanStartStyle = "";
+ spanStyle = spanEndStyle = spanStartStyle = title = "";
collapsed = null; nextChange = Infinity;
var foundBookmark = null;
for (var j = 0; j < spans.length; ++j) {
@@ -4304,17 +4432,17 @@ window.CodeMirror = (function() {
if (m.className) spanStyle += " " + m.className;
if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
- if (m.collapsed && (!collapsed || collapsed.marker.width < m.width))
+ if (m.title && !title) title = m.title;
+ if (m.collapsed && (!collapsed || collapsed.marker.size < m.size))
collapsed = sp;
} else if (sp.from > pos && nextChange > sp.from) {
nextChange = sp.from;
}
- if (m.type == "bookmark" && sp.from == pos && m.replacedWith)
- foundBookmark = m.replacedWith;
+ if (m.type == "bookmark" && sp.from == pos && m.replacedWith) foundBookmark = m;
}
if (collapsed && (collapsed.from || 0) == pos) {
buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos,
- collapsed.from != null && collapsed.marker.replacedWith);
+ collapsed.marker, collapsed.from == null);
if (collapsed.to == null) return collapsed.marker.find();
}
if (foundBookmark && !collapsed) buildCollapsedSpan(builder, 0, foundBookmark);
@@ -4328,13 +4456,14 @@ window.CodeMirror = (function() {
if (!collapsed) {
var tokenText = end > upto ? text.slice(0, upto - pos) : text;
builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
- spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "");
+ spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title);
}
if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
pos = end;
spanStartStyle = "";
}
- text = styles[i++]; style = styleToClass(styles[i++]);
+ text = allText.slice(at, at = styles[i++]);
+ style = styleToClass(styles[i++]);
}
}
}
@@ -4357,7 +4486,7 @@ window.CodeMirror = (function() {
// 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)
- added.push(makeLine(text[i], spansFor(i), estimateHeight));
+ added.push(new Line(text[i], spansFor(i), estimateHeight));
update(lastLine, lastLine.text, lastSpans);
if (nlines) doc.remove(from.line, nlines);
if (added.length) doc.insert(from.line, added);
@@ -4366,8 +4495,8 @@ window.CodeMirror = (function() {
update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
} else {
for (var added = [], i = 1, e = text.length - 1; i < e; ++i)
- added.push(makeLine(text[i], spansFor(i), estimateHeight));
- added.push(makeLine(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
+ added.push(new Line(text[i], spansFor(i), estimateHeight));
+ added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
doc.insert(from.line + 1, added);
}
@@ -4378,7 +4507,7 @@ window.CodeMirror = (function() {
update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
for (var i = 1, e = text.length - 1, added = []; i < e; ++i)
- added.push(makeLine(text[i], spansFor(i), estimateHeight));
+ added.push(new Line(text[i], spansFor(i), estimateHeight));
if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
doc.insert(from.line + 1, added);
}
@@ -4521,11 +4650,12 @@ window.CodeMirror = (function() {
if (!(this instanceof Doc)) return new Doc(text, mode, firstLine);
if (firstLine == null) firstLine = 0;
- BranchChunk.call(this, [new LeafChunk([makeLine("", null)])]);
+ BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
this.first = firstLine;
this.scrollTop = this.scrollLeft = 0;
this.cantEdit = false;
this.history = makeHistory();
+ this.cleanGeneration = 1;
this.frontier = firstLine;
var start = Pos(firstLine, 0);
this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null};
@@ -4585,6 +4715,11 @@ window.CodeMirror = (function() {
getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
getLineNumber: function(line) {return lineNo(line);},
+ getLineHandleVisualStart: function(line) {
+ if (typeof line == "number") line = getLine(this, line);
+ return visualLine(this, line);
+ },
+
lineCount: function() {return this.size;},
firstLine: function() {return this.first;},
lastLine: function() {return this.first + this.size - 1;},
@@ -4626,20 +4761,25 @@ window.CodeMirror = (function() {
var hist = this.history;
return {undo: hist.done.length, redo: hist.undone.length};
},
- clearHistory: function() {this.history = makeHistory();},
+ clearHistory: function() {this.history = makeHistory(this.history.maxGeneration);},
markClean: function() {
- this.history.dirtyCounter = 0;
+ this.cleanGeneration = this.changeGeneration();
+ },
+ changeGeneration: function() {
this.history.lastOp = this.history.lastOrigin = null;
+ return this.history.generation;
+ },
+ isClean: function (gen) {
+ return this.history.generation == (gen || this.cleanGeneration);
},
- isClean: function () {return this.history.dirtyCounter == 0;},
getHistory: function() {
return {done: copyHistoryArray(this.history.done),
undone: copyHistoryArray(this.history.undone)};
},
setHistory: function(histData) {
- var hist = this.history = makeHistory();
+ var hist = this.history = makeHistory(this.history.maxGeneration);
hist.done = histData.done.slice(0);
hist.undone = histData.undone.slice(0);
},
@@ -4750,6 +4890,8 @@ window.CodeMirror = (function() {
return function() {return method.apply(this.doc, arguments);};
})(Doc.prototype[prop]);
+ eventMixin(Doc);
+
function linkedDocs(doc, f, sharedHistOnly) {
function propagate(doc, skip, sharedHist) {
if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
@@ -4869,7 +5011,7 @@ window.CodeMirror = (function() {
// HISTORY
- function makeHistory() {
+ function makeHistory(startGen) {
return {
// Arrays of history events. Doing something adds an event to
// done and clears undo. Undoing moves events from done to
@@ -4879,7 +5021,7 @@ window.CodeMirror = (function() {
// event
lastTime: 0, lastOp: null, lastOrigin: null,
// Used by the isClean() method
- dirtyCounter: 0
+ generation: startGen || 1, maxGeneration: startGen || 1
};
}
@@ -4893,7 +5035,8 @@ window.CodeMirror = (function() {
}
function historyChangeFromChange(doc, change) {
- var histChange = {from: change.from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
+ var from = { line: change.from.line, ch: change.from.ch };
+ var histChange = {from: from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
return histChange;
@@ -4923,17 +5066,13 @@ window.CodeMirror = (function() {
} else {
// Can not be merged, start a new event.
cur = {changes: [historyChangeFromChange(doc, change)],
+ generation: hist.generation,
anchorBefore: doc.sel.anchor, headBefore: doc.sel.head,
anchorAfter: selAfter.anchor, headAfter: selAfter.head};
hist.done.push(cur);
+ hist.generation = ++hist.maxGeneration;
while (hist.done.length > hist.undoDepth)
hist.done.shift();
- if (hist.dirtyCounter < 0)
- // The user has made a change after undoing past the last clean state.
- // We can never get back to a clean state now until markClean() is called.
- hist.dirtyCounter = NaN;
- else
- hist.dirtyCounter++;
}
hist.lastTime = time;
hist.lastOp = opId;
@@ -5048,6 +5187,9 @@ window.CodeMirror = (function() {
if (e.stopPropagation) e.stopPropagation();
else e.cancelBubble = true;
}
+ function e_defaultPrevented(e) {
+ return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
+ }
function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
CodeMirror.e_stop = e_stop;
CodeMirror.e_preventDefault = e_preventDefault;
@@ -5114,6 +5256,11 @@ window.CodeMirror = (function() {
delayedCallbacks.push(bnd(arr[i]));
}
+ function signalDOMEvent(cm, e, override) {
+ signal(cm, override || e.type, cm, e);
+ return e_defaultPrevented(e) || e.codemirrorIgnore;
+ }
+
function fireDelayed() {
--delayedCallbackDepth;
var delayed = delayedCallbacks;
@@ -5128,6 +5275,11 @@ window.CodeMirror = (function() {
CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
+ function eventMixin(ctor) {
+ ctor.prototype.on = function(type, f) {on(this, type, f);};
+ ctor.prototype.off = function(type, f) {off(this, type, f);};
+ }
+
// MISC UTILITIES
// Number of pixels added to scroller and sizer to hide scrollbar
@@ -5168,7 +5320,11 @@ window.CodeMirror = (function() {
if (ios) { // Mobile Safari apparently has a bug where select() is broken.
node.selectionStart = 0;
node.selectionEnd = node.value.length;
- } else node.select();
+ } else {
+ // Suppress mysterious IE10 errors
+ try { node.select(); }
+ catch(_e) {}
+ }
}
function indexOf(collection, elt) {
@@ -5275,11 +5431,13 @@ window.CodeMirror = (function() {
spanAffectsWrapping = function(str, i) {
return /\-[^ \-?]|\?[^ !\'\"\),.\-\/:;\?\]\}]/.test(str.slice(i - 1, i + 1));
};
- else if (webkit)
+ else if (webkit && !/Chrome\/(?:29|[3-9]\d|\d\d\d)\./.test(navigator.userAgent))
spanAffectsWrapping = function(str, i) {
- if (i > 1 && str.charCodeAt(i - 1) == 45 && /\w/.test(str.charAt(i - 2)) && /[^\-?\.]/.test(str.charAt(i)))
- return true;
- return /[~!#%&*)=+}\]|\"\.>,:;][({[<]|\?[\w~`@#$%\^&*(_=+{[|><]/.test(str.slice(i - 1, i + 1));
+ 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));
};
var knownScrollbarWidth;
@@ -5365,11 +5523,15 @@ window.CodeMirror = (function() {
function iterateBidiSections(order, from, to, f) {
if (!order) return f(from, to, "ltr");
+ var found = false;
for (var i = 0; i < order.length; ++i) {
var part = order[i];
- if (part.from < to && part.to > from || from == to && part.to == from)
+ if (part.from < to && part.to > from || from == to && part.to == from) {
f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
+ found = true;
+ }
}
+ if (!found) f(from, to, "ltr");
}
function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
@@ -5631,7 +5793,7 @@ window.CodeMirror = (function() {
// THE END
- CodeMirror.version = "3.13";
+ CodeMirror.version = "3.15.0";
return CodeMirror;
})();
diff --git a/js/codemirror/mode/sql/sql.js b/js/codemirror/mode/sql/sql.js
index 066db97a82..9016cc7aae 100644
--- a/js/codemirror/mode/sql/sql.js
+++ b/js/codemirror/mode/sql/sql.js
@@ -19,43 +19,64 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
if (result !== false) return result;
}
- if ((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/))
- || (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/)) {
+ if (support.hexNumber == true &&
+ ((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/))
+ || (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/))) {
// hex
+ // ref: http://dev.mysql.com/doc/refman/5.5/en/hexadecimal-literals.html
return "number";
- } else if (((ch == "b" || ch == "B") && stream.match(/^'[01]+'/))
- || (ch == "0" && stream.match(/^b[01]+/))) {
+ } else if (support.binaryNumber == true &&
+ (((ch == "b" || ch == "B") && stream.match(/^'[01]+'/))
+ || (ch == "0" && stream.match(/^b[01]+/)))) {
// bitstring
+ // ref: http://dev.mysql.com/doc/refman/5.5/en/bit-field-literals.html
return "number";
} else if (ch.charCodeAt(0) > 47 && ch.charCodeAt(0) < 58) {
// numbers
- stream.match(/^[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/);
+ // ref: http://dev.mysql.com/doc/refman/5.5/en/number-literals.html
+ stream.match(/^[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/);
+ support.decimallessFloat == true && stream.eat('.');
return "number";
} else if (ch == "?" && (stream.eatSpace() || stream.eol() || stream.eat(";"))) {
// placeholders
return "variable-3";
- } else if (ch == '"' || ch == "'") {
+ } else if (ch == "'" || (ch == '"' && support.doubleQuote)) {
// strings
+ // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
state.tokenize = tokenLiteral(ch);
return state.tokenize(stream, state);
+ } else if ((((support.nCharCast == true && (ch == "n" || ch == "N"))
+ || (support.charsetCast == true && ch == "_" && stream.match(/[a-z][a-z0-9]*/i)))
+ && (stream.peek() == "'" || stream.peek() == '"'))) {
+ // charset casting: _utf8'str', N'str', n'str'
+ // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
+ return "keyword";
} else if (/^[\(\),\;\[\]]/.test(ch)) {
// no highlightning
return null;
- } else if (ch == "#" || (ch == "-" && stream.eat("-") && stream.eat(" "))) {
+ } else if (support.commentSlashSlash && ch == "/" && stream.eat("/")) {
+ // 1-line comment
+ stream.skipToEnd();
+ return "comment";
+ } else if ((support.commentHash && ch == "#")
+ || (ch == "-" && stream.eat("-") && (!support.commentSpaceRequired || stream.eat(" ")))) {
// 1-line comments
+ // ref: https://kb.askmonty.org/en/comment-syntax/
stream.skipToEnd();
return "comment";
} else if (ch == "/" && stream.eat("*")) {
// multi-line comments
+ // ref: https://kb.askmonty.org/en/comment-syntax/
state.tokenize = tokenComment;
return state.tokenize(stream, state);
} else if (ch == ".") {
// .1 for 0.1
- if (support.zerolessFloat == true && stream.match(/^(?:\d+(?:e\d*)?|\d*e\d+)/i)) {
+ if (support.zerolessFloat == true && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i)) {
return "number";
}
// .table_name (ODBC)
- if (stream.match(/^[a-zA-Z_]+/) && support.ODBCdotTable == true) {
+ // // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
+ if (support.ODBCdotTable == true && stream.match(/^[a-zA-Z_]+/)) {
return "variable-2";
}
} else if (operatorChars.test(ch)) {
@@ -65,11 +86,13 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
} else if (ch == '{' &&
(stream.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/) || stream.match(/^( )*(d|D|t|T|ts|TS)( )*"[^"]*"( )*}/))) {
// dates (weird ODBC syntax)
+ // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html
return "number";
} else {
stream.eatWhile(/^[_\w\d]/);
var word = stream.current().toLowerCase();
// dates (standard SQL syntax)
+ // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html
if (dateSQL.hasOwnProperty(word) && (stream.match(/^( )+'[^']*'/) || stream.match(/^( )+"[^"]*"/)))
return "number";
if (atoms.hasOwnProperty(word)) return "atom";
@@ -166,6 +189,8 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
// `identifier`
function hookIdentifier(stream) {
+ // MySQL/MariaDB identifiers
+ // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
var ch;
while ((ch = stream.next()) != null) {
if (ch == "`" && !stream.eat("`")) return "variable-2";
@@ -176,7 +201,9 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
// variable token
function hookVar(stream) {
// variables
- // @@ and prefix
+ // @@prefix.varName @varName
+ // varName can be quoted with ` or ' or "
+ // ref: http://dev.mysql.com/doc/refman/5.5/en/user-variables.html
if (stream.eat("@")) {
stream.match(/^session\./);
stream.match(/^local\./);
@@ -200,18 +227,27 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
// short client keyword token
function hookClient(stream) {
+ // \N means NULL
+ // ref: http://dev.mysql.com/doc/refman/5.5/en/null-values.html
+ if (stream.eat("N")) {
+ return "atom";
+ }
// \g, etc
- return stream.match(/^[a-zA-Z]\b/) ? "variable-2" : null;
+ // ref: http://dev.mysql.com/doc/refman/5.5/en/mysql-commands.html
+ return stream.match(/^[a-zA-Z.#!?]/) ? "variable-2" : null;
}
+ // these keywords are used by all SQL dialects (however, a mode can still overwrite it)
var sqlKeywords = "alter and as asc between by count create delete desc distinct drop from having in insert into is join like not on or order select set table union update values where ";
+ // turn a space-separated list into an array
function set(str) {
var obj = {}, words = str.split(" ");
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
return obj;
}
+ // A generic SQL Mode. It's not a standard, it just try to support what is generally supported
CodeMirror.defineMIME("text/x-sql", {
name: "sql",
keywords: set(sqlKeywords + "begin"),
@@ -219,7 +255,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
atoms: set("false true null unknown"),
operatorChars: /^[*+\-%<>!=]/,
dateSQL: set("date time timestamp"),
- support: set("ODBCdotTable")
+ support: set("ODBCdotTable doubleQuote binaryNumber hexNumber")
});
CodeMirror.defineMIME("text/x-mysql", {
@@ -230,7 +266,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
atoms: set("false true null unknown"),
operatorChars: /^[*+\-%<>!=&|^]/,
dateSQL: set("date time timestamp"),
- support: set("ODBCdotTable zerolessFloat"),
+ support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),
hooks: {
"@": hookVar,
"`": hookIdentifier,
@@ -246,7 +282,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
atoms: set("false true null unknown"),
operatorChars: /^[*+\-%<>!=&|^]/,
dateSQL: set("date time timestamp"),
- support: set("ODBCdotTable zerolessFloat"),
+ support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),
hooks: {
"@": hookVar,
"`": hookIdentifier,
@@ -254,6 +290,20 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
}
});
+ // the query language used by Apache Cassandra is called CQL, but this mime type
+ // is called Cassandra to avoid confusion with Contextual Query Language
+ CodeMirror.defineMIME("text/x-cassandra", {
+ name: "sql",
+ client: { },
+ keywords: set("use select from using consistency where limit first reversed first and in insert into values using consistency ttl update set delete truncate begin batch apply create keyspace with columnfamily primary key index on drop alter type add any one quorum all local_quorum each_quorum"),
+ builtin: set("ascii bigint blob boolean counter decimal double float int text timestamp uuid varchar varint"),
+ atoms: set("false true"),
+ operatorChars: /^[<>=]/,
+ dateSQL: { },
+ support: set("commentSlashSlash decimallessFloat"),
+ hooks: { }
+ });
+
// this is based on Peter Raganitsch's 'plsql' mode
CodeMirror.defineMIME("text/x-plsql", {
name: "sql",
@@ -262,6 +312,38 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
functions: set("abs acos add_months ascii asin atan atan2 average bfilename ceil chartorowid chr concat convert cos cosh count decode deref dual dump dup_val_on_index empty error exp false floor found glb greatest hextoraw initcap instr instrb isopen last_day least lenght lenghtb ln lower lpad ltrim lub make_ref max min mod months_between new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null nvl others power rawtohex reftohex round rowcount rowidtochar rpad rtrim sign sin sinh soundex sqlcode sqlerrm sqrt stddev substr substrb sum sysdate tan tanh to_char to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid upper user userenv variance vsize"),
builtin: set("bfile blob character clob dec float int integer mlslabel natural naturaln nchar nclob number numeric nvarchar2 real rowtype signtype smallint string varchar varchar2"),
operatorChars: /^[*+\-%<>!=~]/,
- dateSQL: set("date time timestamp")
+ dateSQL: set("date time timestamp"),
+ support: set("doubleQuote nCharCast zerolessFloat binaryNumber hexNumber")
});
}());
+
+/*
+ How Properties of Mime Types are used by SQL Mode
+ =================================================
+
+ keywords:
+ A list of keywords you want to be highlighted.
+ functions:
+ A list of function names you want to be highlighted.
+ builtin:
+ A list of builtin types you want to be highlighted (if you want types to be of class "builtin" instead of "keyword").
+ operatorChars:
+ All characters that must be handled as operators.
+ client:
+ Commands parsed and executed by the client (not the server).
+ support:
+ A list of supported syntaxes which are not common, but are supported by more than 1 DBMS.
+ * ODBCdotTable: .tableName
+ * zerolessFloat: .1
+ * doubleQuote
+ * nCharCast: N'string'
+ * charsetCast: _utf8'string'
+ * commentHash: use # char for comments
+ * commentSlashSlash: use // for comments
+ * commentSpaceRequired: require a space after -- for comments
+ atoms:
+ Keywords that must be highlighted as atoms,. Some DBMS's support more atoms than others:
+ UNKNOWN, INFINITY, UNDERFLOW, NaN...
+ dateSQL:
+ Used for date/time SQL standard syntax, because not all DBMS's support same temporal types.
+*/