| 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
|
|
|