| 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 390cb3786647dce8b05f641f40e7d85bba00291a..c8f48092f44572a844ccc3502fb86668f43fe5ef 100644
|
| --- a/Source/devtools/front_end/cm/codemirror.js
|
| +++ b/Source/devtools/front_end/cm/codemirror.js
|
| @@ -108,6 +108,7 @@
|
|
|
| for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))
|
| optionHandlers[opt](cm, options[opt], Init);
|
| + maybeUpdateLineNumberWidth(cm);
|
| for (var i = 0; i < initHooks.length; ++i) initHooks[i](cm);
|
| });
|
| }
|
| @@ -467,18 +468,18 @@
|
| }
|
|
|
| // Compute the lines that are visible in a given viewport (defaults
|
| - // the the current scroll position). viewPort may contain top,
|
| + // 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 ? Math.max(0, viewPort.top) : display.scroller.scrollTop;
|
| + function visibleLines(display, doc, viewport) {
|
| + 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;
|
| + var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
|
|
|
| var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
|
| // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
|
| // forces those lines into the viewport (if possible).
|
| - if (viewPort && viewPort.ensure) {
|
| - var ensureFrom = viewPort.ensure.from.line, ensureTo = viewPort.ensure.to.line;
|
| + if (viewport && viewport.ensure) {
|
| + var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
|
| if (ensureFrom < from)
|
| return {from: ensureFrom,
|
| to: lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight)};
|
| @@ -543,83 +544,46 @@
|
|
|
| // DISPLAY DRAWING
|
|
|
| - // Updates the display, selection, and scrollbars, using the
|
| - // information in display.view to find out which nodes are no longer
|
| - // up-to-date. Tries to bail out early when no changes are needed,
|
| - // unless forced is true.
|
| - // Returns true if an actual update happened, false otherwise.
|
| - function updateDisplay(cm, viewPort, forced) {
|
| - var oldFrom = cm.display.viewFrom, oldTo = cm.display.viewTo, updated;
|
| - var visible = visibleLines(cm.display, cm.doc, viewPort);
|
| - for (var first = true;; first = false) {
|
| - var oldWidth = cm.display.scroller.clientWidth;
|
| - if (!updateDisplayInner(cm, visible, forced)) break;
|
| - updated = true;
|
| -
|
| - // If the max line changed since it was last measured, measure it,
|
| - // and ensure the document's width matches it.
|
| - if (cm.display.maxLineChanged && !cm.options.lineWrapping)
|
| - adjustContentWidth(cm);
|
| -
|
| - var barMeasure = measureForScrollbars(cm);
|
| - updateSelection(cm);
|
| - setDocumentHeight(cm, barMeasure);
|
| - updateScrollbars(cm, barMeasure);
|
| - if (webkit && cm.options.lineWrapping)
|
| - checkForWebkitWidthBug(cm, barMeasure); // (Issue #2420)
|
| - if (webkit && barMeasure.scrollWidth > barMeasure.clientWidth &&
|
| - barMeasure.scrollWidth < barMeasure.clientWidth + 1 &&
|
| - !hScrollbarTakesSpace(cm))
|
| - updateScrollbars(cm); // (Issue #2562)
|
| - if (first && cm.options.lineWrapping && oldWidth != cm.display.scroller.clientWidth) {
|
| - forced = true;
|
| - continue;
|
| - }
|
| - forced = false;
|
| -
|
| - // Clip forced viewport to actual scrollable area.
|
| - if (viewPort && viewPort.top != null)
|
| - viewPort = {top: Math.min(barMeasure.docHeight - scrollerCutOff - barMeasure.clientHeight, viewPort.top)};
|
| - // Updated line heights might result in the drawn area not
|
| - // actually covering the viewport. Keep looping until it does.
|
| - visible = visibleLines(cm.display, cm.doc, viewPort);
|
| - if (visible.from >= cm.display.viewFrom && visible.to <= cm.display.viewTo)
|
| - break;
|
| - }
|
| + function DisplayUpdate(cm, viewport, force) {
|
| + var display = cm.display;
|
|
|
| - cm.display.updateLineNumbers = null;
|
| - if (updated) {
|
| - signalLater(cm, "update", cm);
|
| - if (cm.display.viewFrom != oldFrom || cm.display.viewTo != oldTo)
|
| - signalLater(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
|
| - }
|
| - return updated;
|
| + this.viewport = viewport;
|
| + // Store some values that we'll need later (but don't want to force a relayout for)
|
| + this.visible = visibleLines(display, cm.doc, viewport);
|
| + this.editorIsHidden = !display.wrapper.offsetWidth;
|
| + this.wrapperHeight = display.wrapper.clientHeight;
|
| + this.oldViewFrom = display.viewFrom; this.oldViewTo = display.viewTo;
|
| + this.oldScrollerWidth = display.scroller.clientWidth;
|
| + this.force = force;
|
| + this.dims = getDimensions(cm);
|
| }
|
|
|
| // Does the actual updating of the line display. Bails out
|
| // (returning false) when there is nothing to be done and forced is
|
| // false.
|
| - function updateDisplayInner(cm, visible, forced) {
|
| + function updateDisplayIfNeeded(cm, update) {
|
| var display = cm.display, doc = cm.doc;
|
| - if (!display.wrapper.offsetWidth) {
|
| + if (update.editorIsHidden) {
|
| resetView(cm);
|
| - return;
|
| + return false;
|
| }
|
|
|
| // Bail out if the visible area is already rendered and nothing changed.
|
| - if (!forced && visible.from >= display.viewFrom && visible.to <= display.viewTo &&
|
| + if (!update.force &&
|
| + update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
|
| (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
|
| countDirtyView(cm) == 0)
|
| - return;
|
| + return false;
|
|
|
| - if (maybeUpdateLineNumberWidth(cm))
|
| + if (maybeUpdateLineNumberWidth(cm)) {
|
| resetView(cm);
|
| - var dims = getDimensions(cm);
|
| + update.dims = getDimensions(cm);
|
| + }
|
|
|
| // Compute a suitable new viewport (from & to)
|
| var end = doc.first + doc.size;
|
| - var from = Math.max(visible.from - cm.options.viewportMargin, doc.first);
|
| - var to = Math.min(end, visible.to + cm.options.viewportMargin);
|
| + var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
|
| + var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
|
| if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom);
|
| if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo);
|
| if (sawCollapsedSpans) {
|
| @@ -628,7 +592,7 @@
|
| }
|
|
|
| var different = from != display.viewFrom || to != display.viewTo ||
|
| - display.lastSizeC != display.wrapper.clientHeight;
|
| + display.lastSizeC != update.wrapperHeight;
|
| adjustView(cm, from, to);
|
|
|
| display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
|
| @@ -636,13 +600,15 @@
|
| cm.display.mover.style.top = display.viewOffset + "px";
|
|
|
| var toUpdate = countDirtyView(cm);
|
| - if (!different && toUpdate == 0 && !forced) return;
|
| + if (!different && toUpdate == 0 && !update.force &&
|
| + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
|
| + return false;
|
|
|
| // For big changes, we hide the enclosing element during the
|
| // update, since that speeds up the operations on most browsers.
|
| var focused = activeElt();
|
| if (toUpdate > 4) display.lineDiv.style.display = "none";
|
| - patchDisplay(cm, display.updateLineNumbers, dims);
|
| + patchDisplay(cm, display.updateLineNumbers, update.dims);
|
| if (toUpdate > 4) display.lineDiv.style.display = "";
|
| // There might have been a widget with a focused element that got
|
| // hidden or updated, if so re-focus it.
|
| @@ -654,24 +620,55 @@
|
| removeChildren(display.selectionDiv);
|
|
|
| if (different) {
|
| - display.lastSizeC = display.wrapper.clientHeight;
|
| + display.lastSizeC = update.wrapperHeight;
|
| startWorker(cm, 400);
|
| }
|
|
|
| - updateHeightsInViewport(cm);
|
| + display.updateLineNumbers = null;
|
|
|
| return true;
|
| }
|
|
|
| - function adjustContentWidth(cm) {
|
| - var display = cm.display;
|
| - var width = measureChar(cm, display.maxLine, display.maxLine.text.length).left;
|
| - display.maxLineChanged = false;
|
| - var minWidth = Math.max(0, width + 3);
|
| - var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + minWidth + scrollerCutOff - display.scroller.clientWidth);
|
| - display.sizer.style.minWidth = minWidth + "px";
|
| - if (maxScrollLeft < cm.doc.scrollLeft)
|
| - setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
|
| + function postUpdateDisplay(cm, update) {
|
| + var force = update.force, viewport = update.viewport;
|
| + for (var first = true;; first = false) {
|
| + if (first && cm.options.lineWrapping && update.oldScrollerWidth != cm.display.scroller.clientWidth) {
|
| + force = true;
|
| + } else {
|
| + force = false;
|
| + // Clip forced viewport to actual scrollable area.
|
| + if (viewport && viewport.top != null)
|
| + viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - scrollerCutOff -
|
| + cm.display.scroller.clientHeight, viewport.top)};
|
| + // Updated line heights might result in the drawn area not
|
| + // actually covering the viewport. Keep looping until it does.
|
| + update.visible = visibleLines(cm.display, cm.doc, viewport);
|
| + if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
|
| + break;
|
| + }
|
| + if (!updateDisplayIfNeeded(cm, update)) break;
|
| + updateHeightsInViewport(cm);
|
| + var barMeasure = measureForScrollbars(cm);
|
| + updateSelection(cm);
|
| + setDocumentHeight(cm, barMeasure);
|
| + updateScrollbars(cm, barMeasure);
|
| + }
|
| +
|
| + signalLater(cm, "update", cm);
|
| + if (cm.display.viewFrom != update.oldViewFrom || cm.display.viewTo != update.oldViewTo)
|
| + signalLater(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
|
| + }
|
| +
|
| + function updateDisplaySimple(cm, viewport) {
|
| + var update = new DisplayUpdate(cm, viewport);
|
| + if (updateDisplayIfNeeded(cm, update)) {
|
| + updateHeightsInViewport(cm);
|
| + postUpdateDisplay(cm, update);
|
| + var barMeasure = measureForScrollbars(cm);
|
| + updateSelection(cm);
|
| + setDocumentHeight(cm, barMeasure);
|
| + updateScrollbars(cm, barMeasure);
|
| + }
|
| }
|
|
|
| function setDocumentHeight(cm, measure) {
|
| @@ -1257,10 +1254,10 @@
|
| // SELECTION DRAWING
|
|
|
| // Redraw the selection and/or cursor
|
| - function updateSelection(cm) {
|
| - var display = cm.display, doc = cm.doc;
|
| - var curFragment = document.createDocumentFragment();
|
| - var selFragment = document.createDocumentFragment();
|
| + function drawSelection(cm) {
|
| + var display = cm.display, doc = cm.doc, result = {};
|
| + var curFragment = result.cursors = document.createDocumentFragment();
|
| + var selFragment = result.selection = document.createDocumentFragment();
|
|
|
| for (var i = 0; i < doc.sel.ranges.length; i++) {
|
| var range = doc.sel.ranges[i];
|
| @@ -1275,16 +1272,26 @@
|
| if (cm.options.moveInputWithCursor) {
|
| var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
|
| var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
|
| - var top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
|
| - headPos.top + lineOff.top - wrapOff.top));
|
| - var left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
|
| - headPos.left + lineOff.left - wrapOff.left));
|
| - display.inputDiv.style.top = top + "px";
|
| - display.inputDiv.style.left = left + "px";
|
| + result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
|
| + headPos.top + lineOff.top - wrapOff.top));
|
| + result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
|
| + headPos.left + lineOff.left - wrapOff.left));
|
| + }
|
| +
|
| + return result;
|
| + }
|
| +
|
| + function showSelection(cm, drawn) {
|
| + removeChildrenAndAdd(cm.display.cursorDiv, drawn.cursors);
|
| + removeChildrenAndAdd(cm.display.selectionDiv, drawn.selection);
|
| + if (drawn.teTop != null) {
|
| + cm.display.inputDiv.style.top = drawn.teTop + "px";
|
| + cm.display.inputDiv.style.left = drawn.teLeft + "px";
|
| }
|
| + }
|
|
|
| - removeChildrenAndAdd(display.cursorDiv, curFragment);
|
| - removeChildrenAndAdd(display.selectionDiv, selFragment);
|
| + function updateSelection(cm) {
|
| + showSelection(cm, drawSelection(cm));
|
| }
|
|
|
| // Draws a cursor for the given range
|
| @@ -1408,8 +1415,8 @@
|
| if (doc.frontier >= cm.display.viewTo) return;
|
| var end = +new Date + cm.options.workTime;
|
| var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
|
| + var changedLines = [];
|
|
|
| - runInOp(cm, function() {
|
| doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) {
|
| if (doc.frontier >= cm.display.viewFrom) { // Visible
|
| var oldStyles = line.styles;
|
| @@ -1421,7 +1428,7 @@
|
| var ischange = !oldStyles || oldStyles.length != line.styles.length ||
|
| oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
|
| for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
|
| - if (ischange) regLineChange(cm, doc.frontier, "text");
|
| + if (ischange) changedLines.push(doc.frontier);
|
| line.stateAfter = copyState(doc.mode, state);
|
| } else {
|
| processLine(cm, line.text, state);
|
| @@ -1433,6 +1440,9 @@
|
| return true;
|
| }
|
| });
|
| + if (changedLines.length) runInOp(cm, function() {
|
| + for (var i = 0; i < changedLines.length; i++)
|
| + regLineChange(cm, changedLines[i], "text");
|
| });
|
| }
|
|
|
| @@ -1665,6 +1675,8 @@
|
| rect = nullRect;
|
| }
|
|
|
| + if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect);
|
| +
|
| var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
|
| var mid = (rtop + rbot) / 2;
|
| var heights = prepared.view.measure.heights;
|
| @@ -1676,9 +1688,22 @@
|
| top: top, bottom: bot};
|
| if (!rect.left && !rect.right) result.bogus = true;
|
| if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
|
| +
|
| return result;
|
| }
|
|
|
| + // Work around problem with bounding client rects on ranges being
|
| + // returned incorrectly when zoomed on IE10 and below.
|
| + function maybeUpdateRectForZooming(measure, rect) {
|
| + if (!window.screen || screen.logicalXDPI == null ||
|
| + screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
|
| + return rect;
|
| + var scaleX = screen.logicalXDPI / screen.deviceXDPI;
|
| + var scaleY = screen.logicalYDPI / screen.deviceYDPI;
|
| + return {left: rect.left * scaleX, right: rect.right * scaleX,
|
| + top: rect.top * scaleY, bottom: rect.bottom * scaleY};
|
| + }
|
| +
|
| function clearLineMeasurementCacheFor(lineView) {
|
| if (lineView.measure) {
|
| lineView.measure.cache = {};
|
| @@ -1911,10 +1936,13 @@
|
| // error-prone). Instead, display updates are batched and then all
|
| // combined and executed at once.
|
|
|
| + var operationGroup = null;
|
| +
|
| var nextOpId = 0;
|
| // Start a new operation.
|
| function startOperation(cm) {
|
| cm.curOp = {
|
| + cm: cm,
|
| viewChanged: false, // Flag that indicates that lines might need to be redrawn
|
| startHeight: cm.doc.height, // Used to detect need to update scrollbar
|
| forceUpdate: false, // Used to force a redraw
|
| @@ -1922,33 +1950,134 @@
|
| typing: false, // Whether this reset should be careful to leave existing text (for compositing)
|
| changeObjs: null, // Accumulated changes, for firing change events
|
| cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
|
| + cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
|
| selectionChanged: false, // Whether the selection needs to be redrawn
|
| updateMaxLine: false, // Set when the widest line needs to be determined anew
|
| scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
|
| scrollToPos: null, // Used to scroll to a specific position
|
| id: ++nextOpId // Unique ID
|
| };
|
| - if (!delayedCallbackDepth++) delayedCallbacks = [];
|
| + if (operationGroup) {
|
| + operationGroup.ops.push(cm.curOp);
|
| + } else {
|
| + cm.curOp.ownsGroup = operationGroup = {
|
| + ops: [cm.curOp],
|
| + delayedCallbacks: []
|
| + };
|
| + }
|
| + }
|
| +
|
| + function fireCallbacksForOps(group) {
|
| + // Calls delayed callbacks and cursorActivity handlers until no
|
| + // new ones appear
|
| + var callbacks = group.delayedCallbacks, i = 0;
|
| + do {
|
| + for (; i < callbacks.length; i++)
|
| + callbacks[i]();
|
| + for (var j = 0; j < group.ops.length; j++) {
|
| + var op = group.ops[j];
|
| + if (op.cursorActivityHandlers)
|
| + while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
|
| + op.cursorActivityHandlers[op.cursorActivityCalled++](op.cm);
|
| + }
|
| + } while (i < callbacks.length);
|
| }
|
|
|
| // Finish an operation, updating the display and signalling delayed events
|
| function endOperation(cm) {
|
| - var op = cm.curOp, doc = cm.doc, display = cm.display;
|
| - cm.curOp = null;
|
| -
|
| + var op = cm.curOp, group = op.ownsGroup;
|
| + if (!group) return;
|
| +
|
| + try { fireCallbacksForOps(group); }
|
| + finally {
|
| + operationGroup = null;
|
| + for (var i = 0; i < group.ops.length; i++)
|
| + group.ops[i].cm.curOp = null;
|
| + endOperations(group);
|
| + }
|
| + }
|
| +
|
| + // The DOM updates done when an operation finishes are batched so
|
| + // that the minimum number of relayouts are required.
|
| + function endOperations(group) {
|
| + var ops = group.ops;
|
| + for (var i = 0; i < ops.length; i++) // Read DOM
|
| + endOperation_R1(ops[i]);
|
| + for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
|
| + endOperation_W1(ops[i]);
|
| + for (var i = 0; i < ops.length; i++) // Read DOM
|
| + endOperation_R2(ops[i]);
|
| + for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
|
| + endOperation_W2(ops[i]);
|
| + for (var i = 0; i < ops.length; i++) // Read DOM
|
| + endOperation_finish(ops[i]);
|
| + }
|
| +
|
| + function endOperation_R1(op) {
|
| + var cm = op.cm, display = cm.display;
|
| if (op.updateMaxLine) findMaxLine(cm);
|
|
|
| - // If it looks like an update might be needed, call updateDisplay
|
| - if (op.viewChanged || op.forceUpdate || op.scrollTop != null ||
|
| - op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
|
| - op.scrollToPos.to.line >= display.viewTo) ||
|
| - display.maxLineChanged && cm.options.lineWrapping) {
|
| - var updated = updateDisplay(cm, {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
|
| - if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop;
|
| + op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
|
| + op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
|
| + op.scrollToPos.to.line >= display.viewTo) ||
|
| + display.maxLineChanged && cm.options.lineWrapping;
|
| + op.update = op.mustUpdate &&
|
| + new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
|
| + }
|
| +
|
| + function endOperation_W1(op) {
|
| + op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
|
| + }
|
| +
|
| + function endOperation_R2(op) {
|
| + var cm = op.cm, display = cm.display;
|
| + if (op.updatedDisplay) updateHeightsInViewport(cm);
|
| +
|
| + op.barMeasure = measureForScrollbars(cm);
|
| +
|
| + // If the max line changed since it was last measured, measure it,
|
| + // and ensure the document's width matches it.
|
| + // updateDisplay_W2 will use these properties to do the actual resizing
|
| + if (display.maxLineChanged && !cm.options.lineWrapping) {
|
| + op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
|
| + op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo +
|
| + scrollerCutOff - display.scroller.clientWidth);
|
| }
|
| - // If no update was run, but the selection changed, redraw that.
|
| - if (!updated && op.selectionChanged) updateSelection(cm);
|
| - if (!updated && op.startHeight != cm.doc.height) updateScrollbars(cm);
|
| +
|
| + if (op.updatedDisplay || op.selectionChanged)
|
| + op.newSelectionNodes = drawSelection(cm);
|
| + }
|
| +
|
| + function endOperation_W2(op) {
|
| + var cm = op.cm;
|
| +
|
| + if (op.adjustWidthTo != null) {
|
| + cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
|
| + if (op.maxScrollLeft < cm.doc.scrollLeft)
|
| + setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true);
|
| + cm.display.maxLineChanged = false;
|
| + }
|
| +
|
| + if (op.newSelectionNodes)
|
| + showSelection(cm, op.newSelectionNodes);
|
| + if (op.updatedDisplay)
|
| + setDocumentHeight(cm, op.barMeasure);
|
| + if (op.updatedDisplay || op.startHeight != cm.doc.height)
|
| + updateScrollbars(cm, op.barMeasure);
|
| +
|
| + if (op.selectionChanged) restartBlink(cm);
|
| +
|
| + if (cm.state.focused && op.updateInput)
|
| + resetInput(cm, op.typing);
|
| + }
|
| +
|
| + function endOperation_finish(op) {
|
| + var cm = op.cm, display = cm.display, doc = cm.doc;
|
| +
|
| + if (op.adjustWidthTo != null && Math.abs(op.barMeasure.scrollWidth - cm.display.scroller.scrollWidth) > 1)
|
| + updateScrollbars(cm);
|
| +
|
| + if (op.updatedDisplay) postUpdateDisplay(cm, op.update);
|
|
|
| // Abort mouse wheel delta measurement, when scrolling explicitly
|
| if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
|
| @@ -1966,16 +2095,11 @@
|
| }
|
| // If we need to scroll a specific position into view, do so.
|
| if (op.scrollToPos) {
|
| - var coords = scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos.from),
|
| - clipPos(cm.doc, op.scrollToPos.to), op.scrollToPos.margin);
|
| + var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
|
| + clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
|
| if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords);
|
| }
|
|
|
| - if (op.selectionChanged) restartBlink(cm);
|
| -
|
| - if (cm.state.focused && op.updateInput)
|
| - resetInput(cm, op.typing);
|
| -
|
| // Fire events for markers that are hidden/unidden by editing or
|
| // undoing
|
| var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
|
| @@ -1984,18 +2108,22 @@
|
| if (unhidden) for (var i = 0; i < unhidden.length; ++i)
|
| if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
|
|
|
| - var delayed;
|
| - if (!--delayedCallbackDepth) {
|
| - delayed = delayedCallbacks;
|
| - delayedCallbacks = null;
|
| + if (display.wrapper.offsetHeight)
|
| + doc.scrollTop = cm.display.scroller.scrollTop;
|
| +
|
| + // Apply workaround for two webkit bugs
|
| + if (op.updatedDisplay && webkit) {
|
| + if (cm.options.lineWrapping)
|
| + checkForWebkitWidthBug(cm, op.barMeasure); // (Issue #2420)
|
| + if (op.barMeasure.scrollWidth > op.barMeasure.clientWidth &&
|
| + op.barMeasure.scrollWidth < op.barMeasure.clientWidth + 1 &&
|
| + !hScrollbarTakesSpace(cm))
|
| + updateScrollbars(cm); // (Issue #2562)
|
| }
|
| +
|
| // Fire change events, and delayed event handlers
|
| if (op.changeObjs)
|
| signal(cm, "changes", cm, op.changeObjs);
|
| - if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i]();
|
| - if (op.cursorActivityHandlers)
|
| - for (var i = 0; i < op.cursorActivityHandlers.length; i++)
|
| - op.cursorActivityHandlers[i](cm);
|
| }
|
|
|
| // Run the given function in an operation
|
| @@ -2247,6 +2375,11 @@
|
| cm.display.poll.set(20, p);
|
| }
|
|
|
| + // This will be set to an array of strings when copying, so that,
|
| + // when pasting, we know what kind of selections the copied text
|
| + // was made out of.
|
| + var lastCopied = null;
|
| +
|
| // Read input from the textarea, and update the document to match.
|
| // When something is selected, it is present in the textarea, and
|
| // selected (unless it is huge, in which case a placeholder is
|
| @@ -2269,8 +2402,11 @@
|
| var text = input.value;
|
| // If nothing changed, bail.
|
| if (text == prevInput && !cm.somethingSelected()) return false;
|
| - // Work around nonsensical selection resetting in IE9/10
|
| - if (ie && ie_version >= 9 && cm.display.inputHasSelection === text) {
|
| + // Work around nonsensical selection resetting in IE9/10, and
|
| + // inexplicable appearance of private area unicode characters on
|
| + // some key combos in Mac (#2689).
|
| + if (ie && ie_version >= 9 && cm.display.inputHasSelection === text ||
|
| + mac && /[\uf700-\uf7ff]/.test(text)) {
|
| resetInput(cm);
|
| return false;
|
| }
|
| @@ -2287,7 +2423,13 @@
|
| var inserted = text.slice(same), textLines = splitLines(inserted);
|
|
|
| // When pasing N lines into N selections, insert one line per selection
|
| - var multiPaste = cm.state.pasteIncoming && textLines.length > 1 && doc.sel.ranges.length == textLines.length;
|
| + var multiPaste = null;
|
| + if (cm.state.pasteIncoming && doc.sel.ranges.length > 1) {
|
| + if (lastCopied && lastCopied.join("\n") == inserted)
|
| + multiPaste = doc.sel.ranges.length % lastCopied.length == 0 && map(lastCopied, splitLines);
|
| + else if (textLines.length == doc.sel.ranges.length)
|
| + multiPaste = map(textLines, function(l) { return [l]; });
|
| + }
|
|
|
| // Normal behavior is to insert the new text into every selection
|
| for (var i = doc.sel.ranges.length - 1; i >= 0; i--) {
|
| @@ -2300,7 +2442,7 @@
|
| else if (cm.state.overwrite && range.empty() && !cm.state.pasteIncoming)
|
| to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length));
|
| var updateInput = cm.curOp.updateInput;
|
| - var changeEvent = {from: from, to: to, text: multiPaste ? [textLines[i]] : textLines,
|
| + var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
|
| origin: cm.state.pasteIncoming ? "paste" : cm.state.cutIncoming ? "cut" : "+input"};
|
| makeChange(cm.doc, changeEvent);
|
| signalLater(cm, "inputRead", cm, changeEvent);
|
| @@ -2421,7 +2563,7 @@
|
| // Prevent wrapper from ever scrolling
|
| on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
|
|
|
| - on(d.input, "keyup", operation(cm, onKeyUp));
|
| + on(d.input, "keyup", function(e) { onKeyUp.call(cm, e); });
|
| on(d.input, "input", function() {
|
| if (ie && ie_version >= 9 && cm.display.inputHasSelection) cm.display.inputHasSelection = null;
|
| fastPoll(cm);
|
| @@ -2467,27 +2609,29 @@
|
|
|
| function prepareCopyCut(e) {
|
| if (cm.somethingSelected()) {
|
| + lastCopied = cm.getSelections();
|
| if (d.inaccurateSelection) {
|
| d.prevInput = "";
|
| d.inaccurateSelection = false;
|
| - d.input.value = cm.getSelection();
|
| + d.input.value = lastCopied.join("\n");
|
| selectInput(d.input);
|
| }
|
| } else {
|
| - var text = "", ranges = [];
|
| + var text = [], ranges = [];
|
| for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
|
| var line = cm.doc.sel.ranges[i].head.line;
|
| var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
|
| ranges.push(lineRange);
|
| - text += cm.getRange(lineRange.anchor, lineRange.head);
|
| + text.push(cm.getRange(lineRange.anchor, lineRange.head));
|
| }
|
| if (e.type == "cut") {
|
| cm.setSelections(ranges, null, sel_dontScroll);
|
| } else {
|
| d.prevInput = "";
|
| - d.input.value = text;
|
| + d.input.value = text.join("\n");
|
| selectInput(d.input);
|
| }
|
| + lastCopied = text;
|
| }
|
| if (e.type == "cut") cm.state.cutIncoming = true;
|
| }
|
| @@ -2885,10 +3029,10 @@
|
| function setScrollTop(cm, val) {
|
| if (Math.abs(cm.doc.scrollTop - val) < 2) return;
|
| cm.doc.scrollTop = val;
|
| - if (!gecko) updateDisplay(cm, {top: val});
|
| + if (!gecko) updateDisplaySimple(cm, {top: val});
|
| 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);
|
| + if (gecko) updateDisplaySimple(cm);
|
| startWorker(cm, 100);
|
| }
|
| // Sync scroller and scrollbar, ensure the gutter elements are
|
| @@ -2971,7 +3115,7 @@
|
| var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
|
| if (pixels < 0) top = Math.max(0, top + pixels - 50);
|
| else bot = Math.min(cm.doc.height, bot + pixels + 50);
|
| - updateDisplay(cm, {top: top, bottom: bot});
|
| + updateDisplaySimple(cm, {top: top, bottom: bot});
|
| }
|
|
|
| if (wheelSamples < 20) {
|
| @@ -3114,13 +3258,13 @@
|
| }
|
|
|
| function onKeyUp(e) {
|
| - if (signalDOMEvent(this, e)) return;
|
| if (e.keyCode == 16) this.doc.sel.shift = false;
|
| + signalDOMEvent(this, e);
|
| }
|
|
|
| function onKeyPress(e) {
|
| var cm = this;
|
| - if (signalDOMEvent(cm, e) || e.ctrlKey || mac && e.metaKey) return;
|
| + if (signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return;
|
| var keyCode = e.keyCode, charCode = e.charCode;
|
| if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
|
| if (((presto && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
|
| @@ -3184,7 +3328,9 @@
|
| "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " +
|
| (ie ? "rgba(255, 255, 255, .05)" : "transparent") +
|
| "; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
|
| + if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2712)
|
| focusInput(cm);
|
| + if (webkit) window.scrollTo(null, oldScrollY);
|
| resetInput(cm);
|
| // Adds "Select all" to context menu in FF
|
| if (!cm.somethingSelected()) display.input.value = display.prevInput = " ";
|
| @@ -3414,9 +3560,9 @@
|
|
|
| antiChanges.push(historyChangeFromChange(doc, change));
|
|
|
| - var after = i ? computeSelAfterChange(doc, change, null) : lst(source);
|
| + var after = i ? computeSelAfterChange(doc, change) : lst(source);
|
| makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
|
| - if (!i && doc.cm) doc.cm.scrollIntoView(change);
|
| + if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)});
|
| var rebased = [];
|
|
|
| // Propagate to the linked documents
|
| @@ -3473,7 +3619,7 @@
|
|
|
| change.removed = getBetween(doc, change.from, change.to);
|
|
|
| - if (!selAfter) selAfter = computeSelAfterChange(doc, change, null);
|
| + if (!selAfter) selAfter = computeSelAfterChange(doc, change);
|
| if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans);
|
| else updateDoc(doc, change, spans);
|
| setSelectionNoUndo(doc, selAfter, sel_dontScroll);
|
| @@ -3605,6 +3751,7 @@
|
| if (y1 < 0) y1 = 0;
|
| var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
|
| var screen = display.scroller.clientHeight - scrollerCutOff, result = {};
|
| + if (y2 - y1 > screen) y2 = y1 + screen;
|
| var docBottom = cm.doc.height + paddingVert(display);
|
| var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
|
| if (y1 < screentop) {
|
| @@ -3615,16 +3762,16 @@
|
| }
|
|
|
| var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
|
| - var screenw = display.scroller.clientWidth - scrollerCutOff;
|
| - x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
|
| - var gutterw = display.gutters.offsetWidth;
|
| - var atLeft = x1 < gutterw + 10;
|
| - if (x1 < screenleft + gutterw || atLeft) {
|
| - if (atLeft) x1 = 0;
|
| - result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
|
| - } else if (x2 > screenw + screenleft - 3) {
|
| - result.scrollLeft = x2 + 10 - screenw;
|
| - }
|
| + var screenw = display.scroller.clientWidth - scrollerCutOff - display.gutters.offsetWidth;
|
| + var tooWide = x2 - x1 > screenw;
|
| + if (tooWide) x2 = y1 + screen;
|
| + if (x1 < 10)
|
| + result.scrollLeft = 0;
|
| + else if (x1 < screenleft)
|
| + result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10));
|
| + else if (x2 > screenw + screenleft - 3)
|
| + result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw;
|
| +
|
| return result;
|
| }
|
|
|
| @@ -3680,7 +3827,7 @@
|
| if (how == "smart") {
|
| // Fall back to "prev" when the mode doesn't have an indentation
|
| // method.
|
| - if (!cm.doc.mode.indent) how = "prev";
|
| + if (!doc.mode.indent) how = "prev";
|
| else state = getStateBefore(cm, n);
|
| }
|
|
|
| @@ -3692,8 +3839,8 @@
|
| indentation = 0;
|
| how = "not";
|
| } else if (how == "smart") {
|
| - indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
|
| - if (indentation == Pass) {
|
| + indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
|
| + if (indentation == Pass || indentation > 150) {
|
| if (!aggressive) return;
|
| how = "prev";
|
| }
|
| @@ -3716,7 +3863,7 @@
|
| if (pos < indentation) indentString += spaceStr(indentation - pos);
|
|
|
| if (indentString != curSpaceString) {
|
| - replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
|
| + replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
|
| } else {
|
| // Ensure that, if the cursor was in the whitespace at the start
|
| // of the line, it is moved to the end of that space.
|
| @@ -3933,11 +4080,14 @@
|
| for (var i = 0; i < ranges.length; i++) {
|
| var range = ranges[i];
|
| if (!range.empty()) {
|
| - var start = Math.max(end, range.from().line);
|
| - var to = range.to();
|
| + var from = range.from(), to = range.to();
|
| + var start = Math.max(end, from.line);
|
| end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
|
| for (var j = start; j < end; ++j)
|
| indentLine(this, j, how);
|
| + var newRanges = this.doc.sel.ranges;
|
| + if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
|
| + replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()));
|
| } else if (range.head.line > end) {
|
| indentLine(this, range.head.line, how, true);
|
| end = range.head.line;
|
| @@ -4133,7 +4283,7 @@
|
|
|
| triggerOnKeyDown: methodOp(onKeyDown),
|
| triggerOnKeyPress: methodOp(onKeyPress),
|
| - triggerOnKeyUp: methodOp(onKeyUp),
|
| + triggerOnKeyUp: onKeyUp,
|
|
|
| execCommand: function(cmd) {
|
| if (commands.hasOwnProperty(cmd))
|
| @@ -4333,7 +4483,7 @@
|
| clearCaches(cm);
|
| regChange(cm);
|
| }, true);
|
| - option("specialChars", /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\ufeff]/g, function(cm, val) {
|
| + option("specialChars", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val) {
|
| cm.options.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
|
| cm.refresh();
|
| }, true);
|
| @@ -4568,6 +4718,20 @@
|
| return {from: Pos(range.from().line, 0), to: range.from()};
|
| });
|
| },
|
| + delWrappedLineLeft: function(cm) {
|
| + deleteNearSelection(cm, function(range) {
|
| + var top = cm.charCoords(range.head, "div").top + 5;
|
| + var leftPos = cm.coordsChar({left: 0, top: top}, "div");
|
| + return {from: leftPos, to: range.from()};
|
| + });
|
| + },
|
| + delWrappedLineRight: function(cm) {
|
| + deleteNearSelection(cm, function(range) {
|
| + var top = cm.charCoords(range.head, "div").top + 5;
|
| + var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
|
| + return {from: range.from(), to: rightPos };
|
| + });
|
| + },
|
| undo: function(cm) {cm.undo();},
|
| redo: function(cm) {cm.redo();},
|
| undoSelection: function(cm) {cm.undoSelection();},
|
| @@ -4580,15 +4744,7 @@
|
| },
|
| goLineStartSmart: function(cm) {
|
| cm.extendSelectionsBy(function(range) {
|
| - var start = lineStart(cm, range.head.line);
|
| - var line = cm.getLineHandle(start.line);
|
| - var order = getOrder(line);
|
| - if (!order || order[0].level == 0) {
|
| - var firstNonWS = Math.max(0, line.text.search(/\S/));
|
| - var inWS = range.head.line == start.line && range.head.ch <= firstNonWS && range.head.ch;
|
| - return Pos(start.line, inWS ? 0 : firstNonWS);
|
| - }
|
| - return start;
|
| + return lineStartSmart(cm, range.head);
|
| }, {origin: "+move", bias: 1});
|
| },
|
| goLineEnd: function(cm) {
|
| @@ -4607,6 +4763,14 @@
|
| return cm.coordsChar({left: 0, top: top}, "div");
|
| }, sel_move);
|
| },
|
| + goLineLeftSmart: function(cm) {
|
| + cm.extendSelectionsBy(function(range) {
|
| + var top = cm.charCoords(range.head, "div").top + 5;
|
| + var pos = cm.coordsChar({left: 0, top: top}, "div");
|
| + if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head);
|
| + return pos;
|
| + }, sel_move);
|
| + },
|
| goLineUp: function(cm) {cm.moveV(-1, "line");},
|
| goLineDown: function(cm) {cm.moveV(1, "line");},
|
| goPageUp: function(cm) {cm.moveV(-1, "page");},
|
| @@ -4705,11 +4869,11 @@
|
| };
|
| keyMap.macDefault = {
|
| "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
|
| - "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
|
| - "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
|
| + "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
|
| + "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "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-Backspace": "delLineLeft",
|
| + "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
|
| "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection",
|
| fallthrough: ["basic", "emacsy"]
|
| };
|
| @@ -6344,7 +6508,7 @@
|
| },
|
| changeGeneration: function(forceSplit) {
|
| if (forceSplit)
|
| - this.history.lastOp = this.history.lastOrigin = null;
|
| + this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null;
|
| return this.history.generation;
|
| },
|
| isClean: function (gen) {
|
| @@ -6661,7 +6825,7 @@
|
| // Used to track when changes can be merged into a single undo
|
| // event
|
| this.lastModTime = this.lastSelTime = 0;
|
| - this.lastOp = null;
|
| + this.lastOp = this.lastSelOp = null;
|
| this.lastOrigin = this.lastSelOrigin = null;
|
| // Used by the isClean() method
|
| this.generation = this.maxGeneration = startGen || 1;
|
| @@ -6739,7 +6903,7 @@
|
| hist.done.push(selAfter);
|
| hist.generation = ++hist.maxGeneration;
|
| hist.lastModTime = hist.lastSelTime = time;
|
| - hist.lastOp = opId;
|
| + hist.lastOp = hist.lastSelOp = opId;
|
| hist.lastOrigin = hist.lastSelOrigin = change.origin;
|
|
|
| if (!last) signal(doc, "historyAdded");
|
| @@ -6765,7 +6929,7 @@
|
| // the current, or the origins don't allow matching. Origins
|
| // starting with * are always merged, those starting with + are
|
| // merged when similar and close together in time.
|
| - if (opId == hist.lastOp ||
|
| + if (opId == hist.lastSelOp ||
|
| (origin && hist.lastSelOrigin == origin &&
|
| (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
|
| selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
|
| @@ -6775,7 +6939,7 @@
|
|
|
| hist.lastSelTime = +new Date;
|
| hist.lastSelOrigin = origin;
|
| - hist.lastOp = opId;
|
| + hist.lastSelOp = opId;
|
| if (options && options.clearRedo !== false)
|
| clearSelectionEvents(hist.undone);
|
| }
|
| @@ -6960,6 +7124,8 @@
|
| for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
|
| };
|
|
|
| + var orphanDelayedCallbacks = null;
|
| +
|
| // Often, we want to signal events at a point where we are in the
|
| // middle of some work, but don't want the handler to start calling
|
| // other methods on the editor, which might be in an inconsistent
|
| @@ -6967,25 +7133,26 @@
|
| // signalLater looks whether there are any handlers, and schedules
|
| // them to be executed when the last operation ends, or, if no
|
| // operation is active, when a timeout fires.
|
| - var delayedCallbacks, delayedCallbackDepth = 0;
|
| function signalLater(emitter, type /*, values...*/) {
|
| var arr = emitter._handlers && emitter._handlers[type];
|
| if (!arr) return;
|
| - var args = Array.prototype.slice.call(arguments, 2);
|
| - if (!delayedCallbacks) {
|
| - ++delayedCallbackDepth;
|
| - delayedCallbacks = [];
|
| - setTimeout(fireDelayed, 0);
|
| + var args = Array.prototype.slice.call(arguments, 2), list;
|
| + if (operationGroup) {
|
| + list = operationGroup.delayedCallbacks;
|
| + } else if (orphanDelayedCallbacks) {
|
| + list = orphanDelayedCallbacks;
|
| + } else {
|
| + list = orphanDelayedCallbacks = [];
|
| + setTimeout(fireOrphanDelayed, 0);
|
| }
|
| function bnd(f) {return function(){f.apply(null, args);};};
|
| for (var i = 0; i < arr.length; ++i)
|
| - delayedCallbacks.push(bnd(arr[i]));
|
| + list.push(bnd(arr[i]));
|
| }
|
|
|
| - function fireDelayed() {
|
| - --delayedCallbackDepth;
|
| - var delayed = delayedCallbacks;
|
| - delayedCallbacks = null;
|
| + function fireOrphanDelayed() {
|
| + var delayed = orphanDelayedCallbacks;
|
| + orphanDelayedCallbacks = null;
|
| for (var i = 0; i < delayed.length; ++i) delayed[i]();
|
| }
|
|
|
| @@ -7331,6 +7498,15 @@
|
| return typeof e.oncopy == "function";
|
| })();
|
|
|
| + var badZoomedRects = null;
|
| + function hasBadZoomedRects(measure) {
|
| + if (badZoomedRects != null) return badZoomedRects;
|
| + var node = removeChildrenAndAdd(measure, elt("span", "x"));
|
| + var normal = node.getBoundingClientRect();
|
| + var fromRange = range(node, 0, 1).getBoundingClientRect();
|
| + return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1;
|
| + }
|
| +
|
| // KEY NAMES
|
|
|
| var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
|
| @@ -7393,6 +7569,17 @@
|
| var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
|
| return Pos(lineN == null ? lineNo(line) : lineN, ch);
|
| }
|
| + function lineStartSmart(cm, pos) {
|
| + var start = lineStart(cm, pos.line);
|
| + var line = getLine(cm.doc, start.line);
|
| + var order = getOrder(line);
|
| + if (!order || order[0].level == 0) {
|
| + var firstNonWS = Math.max(0, line.text.search(/\S/));
|
| + var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
|
| + return Pos(start.line, inWS ? 0 : firstNonWS);
|
| + }
|
| + return start;
|
| + }
|
|
|
| function compareBidiLevel(order, a, b) {
|
| var linedir = order[0].level;
|
| @@ -7632,7 +7819,7 @@
|
|
|
| // THE END
|
|
|
| - CodeMirror.version = "4.3.1";
|
| + CodeMirror.version = "4.4.1";
|
|
|
| return CodeMirror;
|
| });
|
|
|