diff --git a/src/display/update_display.js b/src/display/update_display.js index 17d5a069e5d258a6d70b0d166d103a3ccfa44563..affa2a759ade6a7f125db019d180966600fac0b4 100644 --- a/src/display/update_display.js +++ b/src/display/update_display.js @@ -3,7 +3,7 @@ import { heightAtLine, visualLineEndNo, visualLineNo } from "../line/spans" import { getLine, lineNumberFor } from "../line/utils_line" import { displayHeight, displayWidth, getDimensions, paddingVert, scrollGap } from "../measurement/position_measurement" import { mac, webkit } from "../util/browser" -import { activeElt, removeChildren } from "../util/dom" +import { activeElt, removeChildren, contains } from "../util/dom" import { hasHandler, signal } from "../util/event" import { indexOf } from "../util/misc" @@ -54,6 +54,36 @@ export function maybeClipScrollbars(cm) { } } +function selectionSnapshot(cm) { + if (cm.hasFocus()) return null + let active = activeElt() + if (!active || !contains(cm.display.lineDiv, active)) return null + let result = {activeElt: active} + if (window.getSelection) { + let sel = window.getSelection() + if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) { + result.anchorNode = sel.anchorNode + result.anchorOffset = sel.anchorOffset + result.focusNode = sel.focusNode + result.focusOffset = sel.focusOffset + } + } + return result +} + +function restoreSelection(snapshot) { + if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) return + snapshot.activeElt.focus() + if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { + let sel = window.getSelection(), range = document.createRange() + range.setEnd(snapshot.anchorNode, snapshot.anchorOffset) + range.collapse(false) + sel.removeAllRanges() + sel.addRange(range) + sel.extend(snapshot.focusNode, snapshot.focusOffset) + } +} + // Does the actual updating of the line display. Bails out // (returning false) when there is nothing to be done and forced is // false. @@ -103,14 +133,14 @@ export function updateDisplayIfNeeded(cm, update) { // For big changes, we hide the enclosing element during the // update, since that speeds up the operations on most browsers. - let focused = activeElt() + let selSnapshot = selectionSnapshot(cm) if (toUpdate > 4) display.lineDiv.style.display = "none" patchDisplay(cm, display.updateLineNumbers, update.dims) if (toUpdate > 4) display.lineDiv.style.display = "" display.renderedView = display.view // There might have been a widget with a focused element that got // hidden or updated, if so re-focus it. - if (focused && activeElt() != focused && focused.offsetHeight) focused.focus() + restoreSelection(selSnapshot) // Prevent selection and cursors from interfering with the scroll // width and height.