Index: Source/devtools/front_end/cm/codemirror.js |
diff --git a/Source/devtools/front_end/cm/codemirror.js b/Source/devtools/front_end/cm/codemirror.js |
index fa31adc1619756c6fab654d5e6735d48c38889bb..ddb251461880fe7a0cc39d7192f385882df63bcc 100644 |
--- a/Source/devtools/front_end/cm/codemirror.js |
+++ b/Source/devtools/front_end/cm/codemirror.js |
@@ -1,3 +1,6 @@ |
+// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others |
+// Distributed under an MIT license: http://codemirror.net/LICENSE |
+ |
// This is CodeMirror (http://codemirror.net), a code editor |
// implemented in JavaScript on top of the browser's DOM. |
// |
@@ -93,6 +96,7 @@ |
if (ie_upto10) setTimeout(bind(resetInput, this, true), 20); |
registerEventHandlers(this); |
+ ensureGlobalHandlers(); |
var cm = this; |
runInOp(this, function() { |
@@ -230,6 +234,10 @@ |
// True when shift is held down. |
d.shift = false; |
+ |
+ // Used to track whether anything happened since the context menu |
+ // was opened. |
+ d.selForContextMenu = null; |
} |
// STATE UPDATES |
@@ -455,7 +463,7 @@ |
// the the current scroll position). viewPort may contain top, |
// height, and ensure (see op.scrollToPos) properties. |
function visibleLines(display, doc, viewPort) { |
- var top = viewPort && viewPort.top != null ? viewPort.top : display.scroller.scrollTop; |
+ var top = viewPort && viewPort.top != null ? Math.max(0, viewPort.top) : display.scroller.scrollTop; |
top = Math.floor(top - paddingTop(display)); |
var bottom = viewPort && viewPort.bottom != null ? viewPort.bottom : top + display.wrapper.clientHeight; |
@@ -659,7 +667,6 @@ |
cm.display.gutters.style.height = Math.max(measure.docHeight, measure.clientHeight - scrollerCutOff) + "px"; |
} |
- |
function checkForWebkitWidthBug(cm, measure) { |
// Work around Webkit bug where it sometimes reserves space for a |
// non-existing phantom scrollbar in the scroller (Issue #2420) |
@@ -1621,7 +1628,7 @@ |
else |
rect = nullRect; |
} else { |
- rect = range(node, start, end).getBoundingClientRect(); |
+ rect = range(node, start, end).getBoundingClientRect() || nullRect; |
} |
} else { // If it is a widget, simply get the box for the whole widget. |
if (start > 0) collapse = bias = "right"; |
@@ -1922,6 +1929,10 @@ |
if (!updated && op.selectionChanged) updateSelection(cm); |
if (!updated && op.startHeight != cm.doc.height) updateScrollbars(cm); |
+ // Abort mouse wheel delta measurement, when scrolling explicitly |
+ if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) |
+ display.wheelStartX = display.wheelStartY = null; |
+ |
// Propagate the scroll position to the actual DOM scroller |
if (op.scrollTop != null && display.scroller.scrollTop != op.scrollTop) { |
var top = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop)); |
@@ -2135,7 +2146,8 @@ |
function viewCuttingPoint(cm, oldN, newN, dir) { |
var index = findViewIndex(cm, oldN), diff, view = cm.display.view; |
- if (!sawCollapsedSpans) return {index: index, lineN: newN}; |
+ if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) |
+ return {index: index, lineN: newN}; |
for (var i = 0, n = cm.display.viewFrom; i < index; i++) |
n += view[i].size; |
if (n != oldN) { |
@@ -2246,6 +2258,8 @@ |
if (withOp) startOperation(cm); |
cm.display.shift = false; |
+ if (text.charCodeAt(0) == 0x200b && doc.sel == cm.display.selForContextMenu && !prevInput) |
+ prevInput = "\u200b"; |
// Find the part of the input that is actually new |
var same = 0, l = Math.min(prevInput.length, text.length); |
while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same; |
@@ -2345,7 +2359,7 @@ |
var pos = posFromMouse(cm, e); |
if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return; |
e_preventDefault(e); |
- var word = findWordAt(cm.doc, pos); |
+ var word = findWordAt(cm, pos); |
extendSelection(cm.doc, word.anchor, word.head); |
})); |
else |
@@ -2386,26 +2400,6 @@ |
// Prevent wrapper from ever scrolling |
on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); |
- // When the window resizes, we need to refresh active editors. |
- var resizeTimer; |
- function onResize() { |
- if (resizeTimer == null) resizeTimer = setTimeout(function() { |
- resizeTimer = null; |
- // Might be a text scaling operation, clear size caches. |
- d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = knownScrollbarWidth = null; |
- cm.setSize(); |
- }, 100); |
- } |
- on(window, "resize", onResize); |
- // The above handler holds on to the editor and its data |
- // structures. Here we poll to unregister it when the editor is no |
- // longer in the document, so that it can be garbage-collected. |
- function unregister() { |
- if (contains(document.body, d.wrapper)) setTimeout(unregister, 5000); |
- else off(window, "resize", onResize); |
- } |
- setTimeout(unregister, 5000); |
- |
on(d.input, "keyup", operation(cm, onKeyUp)); |
on(d.input, "input", function() { |
if (ie && !ie_upto8 && cm.display.inputHasSelection) cm.display.inputHasSelection = null; |
@@ -2482,6 +2476,14 @@ |
}); |
} |
+ // Called when the window resizes |
+ function onResize(cm) { |
+ // Might be a text scaling operation, clear size caches. |
+ var d = cm.display; |
+ d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; |
+ cm.setSize(); |
+ } |
+ |
// MOUSE EVENTS |
// Return true when the given mouse event happened in a widget |
@@ -2629,7 +2631,7 @@ |
start = posFromMouse(cm, e, true, true); |
ourIndex = -1; |
} else if (type == "double") { |
- var word = findWordAt(doc, start); |
+ var word = findWordAt(cm, start); |
if (cm.display.shift || doc.extend) |
ourRange = extendRange(doc, ourRange, word.anchor, word.head); |
else |
@@ -2675,13 +2677,15 @@ |
ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); |
} |
if (!ranges.length) ranges.push(new Range(start, start)); |
- setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), sel_mouse); |
+ setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), |
+ {origin: "*mouse", scroll: false}); |
+ cm.scrollIntoView(pos); |
} else { |
var oldRange = ourRange; |
var anchor = oldRange.anchor, head = pos; |
if (type != "single") { |
if (type == "double") |
- var range = findWordAt(doc, pos); |
+ var range = findWordAt(cm, pos); |
else |
var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0))); |
if (cmp(range.anchor, anchor) > 0) { |
@@ -3382,7 +3386,7 @@ |
var after = i ? computeSelAfterChange(doc, change, null) : lst(source); |
makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); |
- if (doc.cm) ensureCursorVisible(doc.cm); |
+ if (!i && doc.cm) doc.cm.scrollIntoView(change); |
var rebased = []; |
// Propagate to the linked documents |
@@ -3399,12 +3403,17 @@ |
// Sub-views need their line numbers shifted when text is added |
// above or below them in the parent document. |
function shiftDoc(doc, distance) { |
+ if (distance == 0) return; |
doc.first += distance; |
doc.sel = new Selection(map(doc.sel.ranges, function(range) { |
return new Range(Pos(range.anchor.line + distance, range.anchor.ch), |
Pos(range.head.line + distance, range.head.ch)); |
}), doc.sel.primIndex); |
- if (doc.cm) regChange(doc.cm, doc.first, doc.first - distance, distance); |
+ if (doc.cm) { |
+ regChange(doc.cm, doc.first, doc.first - distance, distance); |
+ for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) |
+ regLineChange(doc.cm, l, "gutter"); |
+ } |
} |
// More lower-level change function, handling only a single document |
@@ -3496,6 +3505,7 @@ |
if (changeHandler) signalLater(cm, "change", cm, obj); |
if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); |
} |
+ cm.display.selForContextMenu = null; |
} |
function replaceRange(doc, code, from, to, origin) { |
@@ -3763,10 +3773,11 @@ |
else if (unit == "column") moveOnce(true); |
else if (unit == "word" || unit == "group") { |
var sawType = null, group = unit == "group"; |
+ var helper = doc.cm && doc.cm.getHelper(pos, "wordChars"); |
for (var first = true;; first = false) { |
if (dir < 0 && !moveOnce(!first)) break; |
var cur = lineObj.text.charAt(ch) || "\n"; |
- var type = isWordChar(cur) ? "w" |
+ var type = isWordChar(cur, helper) ? "w" |
: group && cur == "\n" ? "n" |
: !group || /\s/.test(cur) ? null |
: "p"; |
@@ -3806,13 +3817,15 @@ |
} |
// Find the word at the given position (as returned by coordsChar). |
- function findWordAt(doc, pos) { |
- var line = getLine(doc, pos.line).text; |
+ function findWordAt(cm, pos) { |
+ var doc = cm.doc, line = getLine(doc, pos.line).text; |
var start = pos.ch, end = pos.ch; |
if (line) { |
+ var helper = cm.getHelper(pos, "wordChars"); |
if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end; |
var startChar = line.charAt(start); |
- var check = isWordChar(startChar) ? isWordChar |
+ var check = isWordChar(startChar, helper) |
+ ? function(ch) { return isWordChar(ch, helper); } |
: /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} |
: function(ch) {return !/\s/.test(ch) && !isWordChar(ch);}; |
while (start > 0 && check(line.charAt(start - 1))) --start; |
@@ -4617,13 +4630,25 @@ |
}, |
transposeChars: function(cm) { |
runInOp(cm, function() { |
- var ranges = cm.listSelections(); |
+ var ranges = cm.listSelections(), newSel = []; |
for (var i = 0; i < ranges.length; i++) { |
var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text; |
- if (cur.ch > 0 && cur.ch < line.length - 1) |
- cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1), |
- Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1)); |
+ if (line) { |
+ if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1); |
+ if (cur.ch > 0) { |
+ cur = new Pos(cur.line, cur.ch + 1); |
+ cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), |
+ Pos(cur.line, cur.ch - 2), cur, "+transpose"); |
+ } else if (cur.line > cm.doc.first) { |
+ var prev = getLine(cm.doc, cur.line - 1).text; |
+ if (prev) |
+ cm.replaceRange(line.charAt(0) + "\n" + prev.charAt(prev.length - 1), |
+ Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose"); |
+ } |
+ } |
+ newSel.push(new Range(cur, cur)); |
} |
+ cm.setSelections(newSel); |
}); |
}, |
newlineAndIndent: function(cm) { |
@@ -5583,10 +5608,11 @@ |
} |
function readToken(mode, stream, state) { |
- var style = mode.token(stream, state); |
- if (stream.pos <= stream.start) |
- throw new Error("Mode " + mode.name + " failed to advance stream."); |
- return style; |
+ for (var i = 0; i < 10; i++) { |
+ var style = mode.token(stream, state); |
+ if (stream.pos > stream.start) return style; |
+ } |
+ throw new Error("Mode " + mode.name + " failed to advance stream."); |
} |
// Run the given mode's parser over a line, calling f for each token. |
@@ -7052,10 +7078,15 @@ |
} |
var nonASCIISingleCaseWordChar = /[\u00df\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; |
- var isWordChar = CodeMirror.isWordChar = function(ch) { |
+ var isWordCharBasic = CodeMirror.isWordChar = function(ch) { |
return /\w/.test(ch) || ch > "\x80" && |
(ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)); |
}; |
+ function isWordChar(ch, helper) { |
+ if (!helper) return isWordCharBasic(ch); |
+ if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true; |
+ return helper.test(ch); |
+ } |
function isEmpty(obj) { |
for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false; |
@@ -7137,6 +7168,43 @@ |
return b; |
} |
+ // WINDOW-WIDE EVENTS |
+ |
+ // These must be handled carefully, because naively registering a |
+ // handler for each editor will cause the editors to never be |
+ // garbage collected. |
+ |
+ function forEachCodeMirror(f) { |
+ if (!document.body.getElementsByClassName) return; |
+ var byClass = document.body.getElementsByClassName("CodeMirror"); |
+ for (var i = 0; i < byClass.length; i++) { |
+ var cm = byClass[i].CodeMirror; |
+ if (cm) f(cm); |
+ } |
+ } |
+ |
+ var globalsRegistered = false; |
+ function ensureGlobalHandlers() { |
+ if (globalsRegistered) return; |
+ registerGlobalHandlers(); |
+ globalsRegistered = true; |
+ } |
+ function registerGlobalHandlers() { |
+ // When the window resizes, we need to refresh active editors. |
+ var resizeTimer; |
+ on(window, "resize", function() { |
+ if (resizeTimer == null) resizeTimer = setTimeout(function() { |
+ resizeTimer = null; |
+ knownScrollbarWidth = null; |
+ forEachCodeMirror(onResize); |
+ }, 100); |
+ }); |
+ // When the window loses focus, we want to show the editor as blurred |
+ on(window, "blur", function() { |
+ forEachCodeMirror(onBlur); |
+ }); |
+ } |
+ |
// FEATURE DETECTION |
// Detect drag-and-drop |