@codemirror/view 0.19.20 → 0.19.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/dist/index.cjs +273 -162
- package/dist/index.d.ts +52 -2
- package/dist/index.js +273 -162
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -183,7 +183,7 @@ function scrollRectIntoView(dom, rect, side, center) {
|
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
|
-
class
|
|
186
|
+
class DOMSelectionState {
|
|
187
187
|
constructor() {
|
|
188
188
|
this.anchorNode = null;
|
|
189
189
|
this.anchorOffset = 0;
|
|
@@ -194,11 +194,14 @@ class DOMSelection {
|
|
|
194
194
|
return this.anchorNode == domSel.anchorNode && this.anchorOffset == domSel.anchorOffset &&
|
|
195
195
|
this.focusNode == domSel.focusNode && this.focusOffset == domSel.focusOffset;
|
|
196
196
|
}
|
|
197
|
-
|
|
198
|
-
this.anchorNode
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
this.
|
|
197
|
+
setRange(range) {
|
|
198
|
+
this.set(range.anchorNode, range.anchorOffset, range.focusNode, range.focusOffset);
|
|
199
|
+
}
|
|
200
|
+
set(anchorNode, anchorOffset, focusNode, focusOffset) {
|
|
201
|
+
this.anchorNode = anchorNode;
|
|
202
|
+
this.anchorOffset = anchorOffset;
|
|
203
|
+
this.focusNode = focusNode;
|
|
204
|
+
this.focusOffset = focusOffset;
|
|
202
205
|
}
|
|
203
206
|
}
|
|
204
207
|
let preventScrollSupported = null;
|
|
@@ -1905,7 +1908,7 @@ class ViewUpdate {
|
|
|
1905
1908
|
Whether the document changed in this update.
|
|
1906
1909
|
*/
|
|
1907
1910
|
get docChanged() {
|
|
1908
|
-
return this.
|
|
1911
|
+
return !this.changes.empty;
|
|
1909
1912
|
}
|
|
1910
1913
|
/**
|
|
1911
1914
|
Whether the selection was explicitly set in this update.
|
|
@@ -1939,9 +1942,10 @@ class DocView extends ContentView {
|
|
|
1939
1942
|
// we don't mess it up when reading it back it
|
|
1940
1943
|
this.impreciseAnchor = null;
|
|
1941
1944
|
this.impreciseHead = null;
|
|
1945
|
+
this.forceSelection = false;
|
|
1942
1946
|
// Used by the resize observer to ignore resizes that we caused
|
|
1943
1947
|
// ourselves
|
|
1944
|
-
this.lastUpdate =
|
|
1948
|
+
this.lastUpdate = Date.now();
|
|
1945
1949
|
this.setDOM(view.contentDOM);
|
|
1946
1950
|
this.children = [new LineView];
|
|
1947
1951
|
this.children[0].setParent(this);
|
|
@@ -1955,7 +1959,6 @@ class DocView extends ContentView {
|
|
|
1955
1959
|
// position, if we know the editor is going to scroll that position
|
|
1956
1960
|
// into view.
|
|
1957
1961
|
update(update) {
|
|
1958
|
-
this.lastUpdate = Date.now();
|
|
1959
1962
|
let changedRanges = update.changedRanges;
|
|
1960
1963
|
if (this.minWidth > 0 && changedRanges.length) {
|
|
1961
1964
|
if (!changedRanges.every(({ fromA, toA }) => toA < this.minWidthFrom || fromA > this.minWidthTo)) {
|
|
@@ -1975,21 +1978,19 @@ class DocView extends ContentView {
|
|
|
1975
1978
|
// getSelection than the one that it actually shows to the user.
|
|
1976
1979
|
// This forces a selection update when lines are joined to work
|
|
1977
1980
|
// around that. Issue #54
|
|
1978
|
-
|
|
1979
|
-
update.state.doc.lines != update.startState.doc.lines
|
|
1981
|
+
if ((browser.ie || browser.chrome) && !this.compositionDeco.size && update &&
|
|
1982
|
+
update.state.doc.lines != update.startState.doc.lines)
|
|
1983
|
+
this.forceSelection = true;
|
|
1980
1984
|
let prevDeco = this.decorations, deco = this.updateDeco();
|
|
1981
1985
|
let decoDiff = findChangedDeco(prevDeco, deco, update.changes);
|
|
1982
1986
|
changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
|
|
1983
|
-
|
|
1984
|
-
if (this.dirty == 0 /* Not */ && changedRanges.length == 0 &&
|
|
1985
|
-
!(update.flags & 4 /* Viewport */) &&
|
|
1986
|
-
update.state.selection.main.from >= this.view.viewport.from &&
|
|
1987
|
-
update.state.selection.main.to <= this.view.viewport.to) {
|
|
1988
|
-
this.updateSelection(forceSelection, pointerSel);
|
|
1987
|
+
if (this.dirty == 0 /* Not */ && changedRanges.length == 0) {
|
|
1989
1988
|
return false;
|
|
1990
1989
|
}
|
|
1991
1990
|
else {
|
|
1992
|
-
this.updateInner(changedRanges, deco, update.startState.doc.length
|
|
1991
|
+
this.updateInner(changedRanges, deco, update.startState.doc.length);
|
|
1992
|
+
if (update.transactions.length)
|
|
1993
|
+
this.lastUpdate = Date.now();
|
|
1993
1994
|
return true;
|
|
1994
1995
|
}
|
|
1995
1996
|
}
|
|
@@ -1997,13 +1998,16 @@ class DocView extends ContentView {
|
|
|
1997
1998
|
if (this.dirty) {
|
|
1998
1999
|
this.view.observer.ignore(() => this.view.docView.sync());
|
|
1999
2000
|
this.dirty = 0 /* Not */;
|
|
2001
|
+
this.updateSelection(true);
|
|
2000
2002
|
}
|
|
2001
|
-
|
|
2003
|
+
else {
|
|
2002
2004
|
this.updateSelection();
|
|
2005
|
+
}
|
|
2003
2006
|
}
|
|
2004
2007
|
// Used both by update and checkLayout do perform the actual DOM
|
|
2005
2008
|
// update
|
|
2006
|
-
updateInner(changes, deco, oldLength
|
|
2009
|
+
updateInner(changes, deco, oldLength) {
|
|
2010
|
+
this.view.viewState.mustMeasureContent = true;
|
|
2007
2011
|
this.updateChildren(changes, deco, oldLength);
|
|
2008
2012
|
let { observer } = this.view;
|
|
2009
2013
|
observer.ignore(() => {
|
|
@@ -2011,7 +2015,7 @@ class DocView extends ContentView {
|
|
|
2011
2015
|
// messes with the scroll position during DOM mutation (though
|
|
2012
2016
|
// no relayout is triggered and I cannot imagine how it can
|
|
2013
2017
|
// recompute the scroll position without a layout)
|
|
2014
|
-
this.dom.style.height = this.view.viewState.
|
|
2018
|
+
this.dom.style.height = this.view.viewState.contentHeight + "px";
|
|
2015
2019
|
this.dom.style.minWidth = this.minWidth ? this.minWidth + "px" : "";
|
|
2016
2020
|
// Chrome will sometimes, when DOM mutations occur directly
|
|
2017
2021
|
// around the selection, get confused and report a different
|
|
@@ -2021,8 +2025,7 @@ class DocView extends ContentView {
|
|
|
2021
2025
|
this.sync(track);
|
|
2022
2026
|
this.dirty = 0 /* Not */;
|
|
2023
2027
|
if (track && (track.written || observer.selectionRange.focusNode != track.node))
|
|
2024
|
-
forceSelection = true;
|
|
2025
|
-
this.updateSelection(forceSelection, pointerSel);
|
|
2028
|
+
this.forceSelection = true;
|
|
2026
2029
|
this.dom.style.height = "";
|
|
2027
2030
|
});
|
|
2028
2031
|
let gaps = [];
|
|
@@ -2108,10 +2111,14 @@ class DocView extends ContentView {
|
|
|
2108
2111
|
this.replaceChildren(fromI, toI, content);
|
|
2109
2112
|
}
|
|
2110
2113
|
// Sync the DOM selection to this.state.selection
|
|
2111
|
-
updateSelection(
|
|
2114
|
+
updateSelection(mustRead = false, fromPointer = false) {
|
|
2115
|
+
if (mustRead)
|
|
2116
|
+
this.view.observer.readSelectionRange();
|
|
2112
2117
|
if (!(fromPointer || this.mayControlSelection()) ||
|
|
2113
2118
|
browser.ios && this.view.inputState.rapidCompositionStart)
|
|
2114
2119
|
return;
|
|
2120
|
+
let force = this.forceSelection;
|
|
2121
|
+
this.forceSelection = false;
|
|
2115
2122
|
let main = this.view.state.selection.main;
|
|
2116
2123
|
// FIXME need to handle the case where the selection falls inside a block range
|
|
2117
2124
|
let anchor = this.domAtPos(main.anchor);
|
|
@@ -2293,7 +2300,7 @@ class DocView extends ContentView {
|
|
|
2293
2300
|
let next = i == vs.viewports.length ? null : vs.viewports[i];
|
|
2294
2301
|
let end = next ? next.from - 1 : this.length;
|
|
2295
2302
|
if (end > pos) {
|
|
2296
|
-
let height = vs.
|
|
2303
|
+
let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
|
|
2297
2304
|
deco.push(Decoration.replace({ widget: new BlockGapWidget(height), block: true, inclusive: true }).range(pos, end));
|
|
2298
2305
|
}
|
|
2299
2306
|
if (!next)
|
|
@@ -2899,13 +2906,14 @@ function domPosInText(node, x, y) {
|
|
|
2899
2906
|
}
|
|
2900
2907
|
function posAtCoords(view, { x, y }, precise, bias = -1) {
|
|
2901
2908
|
var _a;
|
|
2902
|
-
let content = view.contentDOM.getBoundingClientRect(),
|
|
2909
|
+
let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop;
|
|
2903
2910
|
let halfLine = view.defaultLineHeight / 2;
|
|
2911
|
+
let block, yOffset = y - docTop;
|
|
2904
2912
|
for (let bounced = false;;) {
|
|
2905
|
-
block = view.
|
|
2906
|
-
if (block.top >
|
|
2907
|
-
bias = block.top >
|
|
2908
|
-
|
|
2913
|
+
block = view.elementAtHeight(yOffset);
|
|
2914
|
+
if (block.top > yOffset || block.bottom < yOffset) {
|
|
2915
|
+
bias = block.top > yOffset ? -1 : 1;
|
|
2916
|
+
yOffset = Math.min(block.bottom - halfLine, Math.max(block.top + halfLine, yOffset));
|
|
2909
2917
|
if (bounced)
|
|
2910
2918
|
return precise ? null : 0;
|
|
2911
2919
|
else
|
|
@@ -2913,8 +2921,9 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
|
|
|
2913
2921
|
}
|
|
2914
2922
|
if (block.type == BlockType.Text)
|
|
2915
2923
|
break;
|
|
2916
|
-
|
|
2924
|
+
yOffset = bias > 0 ? block.bottom + halfLine : block.top - halfLine;
|
|
2917
2925
|
}
|
|
2926
|
+
y = docTop + yOffset;
|
|
2918
2927
|
let lineStart = block.from;
|
|
2919
2928
|
// Clip x to the viewport sides
|
|
2920
2929
|
x = Math.max(content.left + 1, Math.min(content.right - 1, x));
|
|
@@ -3027,17 +3036,17 @@ function moveVertically(view, start, forward, distance) {
|
|
|
3027
3036
|
return EditorSelection.cursor(startPos);
|
|
3028
3037
|
let goal = start.goalColumn, startY;
|
|
3029
3038
|
let rect = view.contentDOM.getBoundingClientRect();
|
|
3030
|
-
let startCoords = view.coordsAtPos(startPos);
|
|
3039
|
+
let startCoords = view.coordsAtPos(startPos), docTop = view.documentTop;
|
|
3031
3040
|
if (startCoords) {
|
|
3032
3041
|
if (goal == null)
|
|
3033
3042
|
goal = startCoords.left - rect.left;
|
|
3034
3043
|
startY = dir < 0 ? startCoords.top : startCoords.bottom;
|
|
3035
3044
|
}
|
|
3036
3045
|
else {
|
|
3037
|
-
let line = view.viewState.
|
|
3046
|
+
let line = view.viewState.lineBlockAt(startPos - docTop);
|
|
3038
3047
|
if (goal == null)
|
|
3039
3048
|
goal = Math.min(rect.right - rect.left, view.defaultCharacterWidth * (startPos - line.from));
|
|
3040
|
-
startY = dir < 0 ? line.top : line.bottom;
|
|
3049
|
+
startY = (dir < 0 ? line.top : line.bottom) + docTop;
|
|
3041
3050
|
}
|
|
3042
3051
|
let resolvedGoal = rect.left + goal;
|
|
3043
3052
|
let dist = distance !== null && distance !== void 0 ? distance : (view.defaultLineHeight >> 1);
|
|
@@ -3288,7 +3297,7 @@ class MouseSelection {
|
|
|
3288
3297
|
this.extend = startEvent.shiftKey;
|
|
3289
3298
|
this.multiple = view.state.facet(EditorState.allowMultipleSelections) && addsSelectionRange(view, startEvent);
|
|
3290
3299
|
this.dragMove = dragMovesSelection(view, startEvent);
|
|
3291
|
-
this.dragging = isInPrimarySelection(view, startEvent) ? null : false;
|
|
3300
|
+
this.dragging = isInPrimarySelection(view, startEvent) && getClickType(startEvent) == 1 ? null : false;
|
|
3292
3301
|
// When clicking outside of the selection, immediately apply the
|
|
3293
3302
|
// effect of starting the selection
|
|
3294
3303
|
if (this.dragging === false) {
|
|
@@ -3509,7 +3518,7 @@ function basicMouseSelection(view, event) {
|
|
|
3509
3518
|
let last = start, lastEvent = event;
|
|
3510
3519
|
return {
|
|
3511
3520
|
update(update) {
|
|
3512
|
-
if (update.
|
|
3521
|
+
if (update.docChanged) {
|
|
3513
3522
|
if (start)
|
|
3514
3523
|
start.pos = update.changes.mapPos(start.pos);
|
|
3515
3524
|
startSel = startSel.map(update.changes);
|
|
@@ -3773,7 +3782,10 @@ class HeightOracle {
|
|
|
3773
3782
|
return lines * this.lineHeight;
|
|
3774
3783
|
}
|
|
3775
3784
|
setDoc(doc) { this.doc = doc; return this; }
|
|
3776
|
-
|
|
3785
|
+
mustRefreshForStyle(whiteSpace, direction) {
|
|
3786
|
+
return (wrappingWhiteSpace.indexOf(whiteSpace) > -1) != this.lineWrapping || this.direction != direction;
|
|
3787
|
+
}
|
|
3788
|
+
mustRefreshForHeights(lineHeights) {
|
|
3777
3789
|
let newHeight = false;
|
|
3778
3790
|
for (let i = 0; i < lineHeights.length; i++) {
|
|
3779
3791
|
let h = lineHeights[i];
|
|
@@ -3785,7 +3797,7 @@ class HeightOracle {
|
|
|
3785
3797
|
this.heightSamples[Math.floor(h * 10)] = true;
|
|
3786
3798
|
}
|
|
3787
3799
|
}
|
|
3788
|
-
return newHeight
|
|
3800
|
+
return newHeight;
|
|
3789
3801
|
}
|
|
3790
3802
|
refresh(whiteSpace, direction, lineHeight, charWidth, lineLength, knownHeights) {
|
|
3791
3803
|
let lineWrapping = wrappingWhiteSpace.indexOf(whiteSpace) > -1;
|
|
@@ -3839,7 +3851,8 @@ class BlockInfo {
|
|
|
3839
3851
|
*/
|
|
3840
3852
|
length,
|
|
3841
3853
|
/**
|
|
3842
|
-
The top position of the element
|
|
3854
|
+
The top position of the element (relative to the top of the
|
|
3855
|
+
document).
|
|
3843
3856
|
*/
|
|
3844
3857
|
top,
|
|
3845
3858
|
/**
|
|
@@ -3873,13 +3886,19 @@ class BlockInfo {
|
|
|
3873
3886
|
.concat(Array.isArray(other.type) ? other.type : [other]);
|
|
3874
3887
|
return new BlockInfo(this.from, this.length + other.length, this.top, this.height + other.height, detail);
|
|
3875
3888
|
}
|
|
3889
|
+
/**
|
|
3890
|
+
FIXME remove on next breaking release @internal
|
|
3891
|
+
*/
|
|
3892
|
+
moveY(offset) {
|
|
3893
|
+
return !offset ? this : new BlockInfo(this.from, this.length, this.top + offset, this.height, Array.isArray(this.type) ? this.type.map(b => b.moveY(offset)) : this.type);
|
|
3894
|
+
}
|
|
3876
3895
|
}
|
|
3877
3896
|
var QueryType = /*@__PURE__*/(function (QueryType) {
|
|
3878
3897
|
QueryType[QueryType["ByPos"] = 0] = "ByPos";
|
|
3879
3898
|
QueryType[QueryType["ByHeight"] = 1] = "ByHeight";
|
|
3880
3899
|
QueryType[QueryType["ByPosNoHeight"] = 2] = "ByPosNoHeight";
|
|
3881
3900
|
return QueryType})(QueryType || (QueryType = {}));
|
|
3882
|
-
const Epsilon = 1e-
|
|
3901
|
+
const Epsilon = 1e-3;
|
|
3883
3902
|
class HeightMap {
|
|
3884
3903
|
constructor(length, // The number of characters covered
|
|
3885
3904
|
height, // Height of this part of the document
|
|
@@ -4106,22 +4125,30 @@ class HeightMapGap extends HeightMap {
|
|
|
4106
4125
|
// can't be widgets or collapsed ranges in those lines, because
|
|
4107
4126
|
// they would already have been added to the heightmap (gaps
|
|
4108
4127
|
// only contain plain text).
|
|
4109
|
-
let nodes = [], pos = Math.max(offset, measured.from);
|
|
4128
|
+
let nodes = [], pos = Math.max(offset, measured.from), singleHeight = -1;
|
|
4129
|
+
let wasChanged = oracle.heightChanged;
|
|
4110
4130
|
if (measured.from > offset)
|
|
4111
4131
|
nodes.push(new HeightMapGap(measured.from - offset - 1).updateHeight(oracle, offset));
|
|
4112
4132
|
while (pos <= end && measured.more) {
|
|
4113
4133
|
let len = oracle.doc.lineAt(pos).length;
|
|
4114
4134
|
if (nodes.length)
|
|
4115
4135
|
nodes.push(null);
|
|
4116
|
-
let
|
|
4136
|
+
let height = measured.heights[measured.index++];
|
|
4137
|
+
if (singleHeight == -1)
|
|
4138
|
+
singleHeight = height;
|
|
4139
|
+
else if (Math.abs(height - singleHeight) >= Epsilon)
|
|
4140
|
+
singleHeight = -2;
|
|
4141
|
+
let line = new HeightMapText(len, height);
|
|
4117
4142
|
line.outdated = false;
|
|
4118
4143
|
nodes.push(line);
|
|
4119
4144
|
pos += len + 1;
|
|
4120
4145
|
}
|
|
4121
4146
|
if (pos <= end)
|
|
4122
4147
|
nodes.push(null, new HeightMapGap(end - pos).updateHeight(oracle, pos));
|
|
4123
|
-
|
|
4124
|
-
|
|
4148
|
+
let result = HeightMap.of(nodes);
|
|
4149
|
+
oracle.heightChanged = wasChanged || singleHeight < 0 || Math.abs(result.height - this.height) >= Epsilon ||
|
|
4150
|
+
Math.abs(singleHeight - this.lines(oracle.doc, offset).lineHeight) >= Epsilon;
|
|
4151
|
+
return result;
|
|
4125
4152
|
}
|
|
4126
4153
|
else if (force || this.outdated) {
|
|
4127
4154
|
this.setHeight(oracle, oracle.heightForGap(offset, offset + this.length));
|
|
@@ -4479,7 +4506,8 @@ class ViewState {
|
|
|
4479
4506
|
this.inView = true;
|
|
4480
4507
|
this.paddingTop = 0;
|
|
4481
4508
|
this.paddingBottom = 0;
|
|
4482
|
-
this.
|
|
4509
|
+
this.contentDOMWidth = 0;
|
|
4510
|
+
this.contentDOMHeight = 0;
|
|
4483
4511
|
this.editorHeight = 0;
|
|
4484
4512
|
this.heightOracle = new HeightOracle;
|
|
4485
4513
|
// See VP.MaxDOMHeight
|
|
@@ -4487,6 +4515,9 @@ class ViewState {
|
|
|
4487
4515
|
this.scrollTarget = null;
|
|
4488
4516
|
// Briefly set to true when printing, to disable viewport limiting
|
|
4489
4517
|
this.printing = false;
|
|
4518
|
+
// Flag set when editor content was redrawn, so that the next
|
|
4519
|
+
// measure stage knows it must read DOM layout
|
|
4520
|
+
this.mustMeasureContent = true;
|
|
4490
4521
|
this.visibleRanges = [];
|
|
4491
4522
|
// Cursor 'assoc' is only significant when the cursor is on a line
|
|
4492
4523
|
// wrap point, where it must stick to the character that it is
|
|
@@ -4499,6 +4530,7 @@ class ViewState {
|
|
|
4499
4530
|
this.mustEnforceCursorAssoc = false;
|
|
4500
4531
|
this.heightMap = HeightMap.empty().applyChanges(state.facet(decorations), Text.empty, this.heightOracle.setDoc(state.doc), [new ChangedRange(0, 0, 0, state.doc.length)]);
|
|
4501
4532
|
this.viewport = this.getViewport(0, null);
|
|
4533
|
+
this.updateViewportLines();
|
|
4502
4534
|
this.updateForViewport();
|
|
4503
4535
|
this.lineGaps = this.ensureLineGaps([]);
|
|
4504
4536
|
this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(false)));
|
|
@@ -4509,7 +4541,7 @@ class ViewState {
|
|
|
4509
4541
|
for (let i = 0; i <= 1; i++) {
|
|
4510
4542
|
let pos = i ? main.head : main.anchor;
|
|
4511
4543
|
if (!viewports.some(({ from, to }) => pos >= from && pos <= to)) {
|
|
4512
|
-
let { from, to } = this.
|
|
4544
|
+
let { from, to } = this.lineBlockAt(pos);
|
|
4513
4545
|
viewports.push(new Viewport(from, to));
|
|
4514
4546
|
}
|
|
4515
4547
|
}
|
|
@@ -4517,6 +4549,12 @@ class ViewState {
|
|
|
4517
4549
|
this.scaler = this.heightMap.height <= 7000000 /* MaxDOMHeight */ ? IdScaler :
|
|
4518
4550
|
new BigScaler(this.heightOracle.doc, this.heightMap, this.viewports);
|
|
4519
4551
|
}
|
|
4552
|
+
updateViewportLines() {
|
|
4553
|
+
this.viewportLines = [];
|
|
4554
|
+
this.heightMap.forEachLine(this.viewport.from, this.viewport.to, this.state.doc, 0, 0, block => {
|
|
4555
|
+
this.viewportLines.push(this.scaler.scale == 1 ? block : scaleBlock(block, this.scaler));
|
|
4556
|
+
});
|
|
4557
|
+
}
|
|
4520
4558
|
update(update, scrollTarget = null) {
|
|
4521
4559
|
let prev = this.state;
|
|
4522
4560
|
this.state = update.state;
|
|
@@ -4531,7 +4569,11 @@ class ViewState {
|
|
|
4531
4569
|
if (scrollTarget && (scrollTarget.range.head < viewport.from || scrollTarget.range.head > viewport.to) ||
|
|
4532
4570
|
!this.viewportIsAppropriate(viewport))
|
|
4533
4571
|
viewport = this.getViewport(0, scrollTarget);
|
|
4572
|
+
let updateLines = !update.changes.empty || (update.flags & 2 /* Height */) ||
|
|
4573
|
+
viewport.from != this.viewport.from || viewport.to != this.viewport.to;
|
|
4534
4574
|
this.viewport = viewport;
|
|
4575
|
+
if (updateLines)
|
|
4576
|
+
this.updateViewportLines();
|
|
4535
4577
|
this.updateForViewport();
|
|
4536
4578
|
if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
|
|
4537
4579
|
this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps, update.changes)));
|
|
@@ -4542,13 +4584,17 @@ class ViewState {
|
|
|
4542
4584
|
update.state.selection.main.empty && update.state.selection.main.assoc)
|
|
4543
4585
|
this.mustEnforceCursorAssoc = true;
|
|
4544
4586
|
}
|
|
4545
|
-
measure(
|
|
4546
|
-
let dom =
|
|
4547
|
-
let
|
|
4548
|
-
|
|
4587
|
+
measure(view) {
|
|
4588
|
+
let dom = view.contentDOM, style = window.getComputedStyle(dom);
|
|
4589
|
+
let oracle = this.heightOracle;
|
|
4590
|
+
let whiteSpace = style.whiteSpace, direction = style.direction == "rtl" ? Direction.RTL : Direction.LTR;
|
|
4591
|
+
let refresh = this.heightOracle.mustRefreshForStyle(whiteSpace, direction);
|
|
4592
|
+
let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != dom.clientHeight;
|
|
4593
|
+
let result = 0, bias = 0;
|
|
4594
|
+
if (measureContent) {
|
|
4595
|
+
this.mustMeasureContent = false;
|
|
4596
|
+
this.contentDOMHeight = dom.clientHeight;
|
|
4549
4597
|
// Vertical padding
|
|
4550
|
-
let style = window.getComputedStyle(dom);
|
|
4551
|
-
whiteSpace = style.whiteSpace, direction = (style.direction == "rtl" ? Direction.RTL : Direction.LTR);
|
|
4552
4598
|
let paddingTop = parseInt(style.paddingTop) || 0, paddingBottom = parseInt(style.paddingBottom) || 0;
|
|
4553
4599
|
if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) {
|
|
4554
4600
|
result |= 8 /* Geometry */;
|
|
@@ -4563,39 +4609,42 @@ class ViewState {
|
|
|
4563
4609
|
this.inView = this.pixelViewport.bottom > this.pixelViewport.top && this.pixelViewport.right > this.pixelViewport.left;
|
|
4564
4610
|
if (!this.inView)
|
|
4565
4611
|
return 0;
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
let { lineHeight, charWidth } = docView.measureTextSize();
|
|
4612
|
+
if (measureContent) {
|
|
4613
|
+
let lineHeights = view.docView.measureVisibleLineHeights();
|
|
4614
|
+
if (oracle.mustRefreshForHeights(lineHeights))
|
|
4615
|
+
refresh = true;
|
|
4616
|
+
let contentWidth = dom.clientWidth;
|
|
4617
|
+
if (refresh || oracle.lineWrapping && Math.abs(contentWidth - this.contentDOMWidth) > oracle.charWidth) {
|
|
4618
|
+
let { lineHeight, charWidth } = view.docView.measureTextSize();
|
|
4573
4619
|
refresh = oracle.refresh(whiteSpace, direction, lineHeight, charWidth, contentWidth / charWidth, lineHeights);
|
|
4574
4620
|
if (refresh) {
|
|
4575
|
-
docView.minWidth = 0;
|
|
4621
|
+
view.docView.minWidth = 0;
|
|
4576
4622
|
result |= 8 /* Geometry */;
|
|
4577
4623
|
}
|
|
4578
4624
|
}
|
|
4579
|
-
if (this.
|
|
4580
|
-
this.
|
|
4625
|
+
if (this.contentDOMWidth != contentWidth) {
|
|
4626
|
+
this.contentDOMWidth = contentWidth;
|
|
4581
4627
|
result |= 8 /* Geometry */;
|
|
4582
4628
|
}
|
|
4583
|
-
if (this.editorHeight !=
|
|
4584
|
-
this.editorHeight =
|
|
4629
|
+
if (this.editorHeight != view.scrollDOM.clientHeight) {
|
|
4630
|
+
this.editorHeight = view.scrollDOM.clientHeight;
|
|
4585
4631
|
result |= 8 /* Geometry */;
|
|
4586
4632
|
}
|
|
4587
4633
|
if (dTop > 0 && dBottom > 0)
|
|
4588
4634
|
bias = Math.max(dTop, dBottom);
|
|
4589
4635
|
else if (dTop < 0 && dBottom < 0)
|
|
4590
4636
|
bias = Math.min(dTop, dBottom);
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to)
|
|
4637
|
+
oracle.heightChanged = false;
|
|
4638
|
+
this.heightMap = this.heightMap.updateHeight(oracle, 0, refresh, new MeasuredHeights(this.viewport.from, lineHeights));
|
|
4639
|
+
if (oracle.heightChanged)
|
|
4640
|
+
result |= 2 /* Height */;
|
|
4641
|
+
}
|
|
4642
|
+
let viewportChange = !this.viewportIsAppropriate(this.viewport, bias) ||
|
|
4643
|
+
this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to);
|
|
4644
|
+
if (viewportChange)
|
|
4598
4645
|
this.viewport = this.getViewport(bias, this.scrollTarget);
|
|
4646
|
+
if ((result & 2 /* Height */) || viewportChange)
|
|
4647
|
+
this.updateViewportLines();
|
|
4599
4648
|
this.updateForViewport();
|
|
4600
4649
|
if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
|
|
4601
4650
|
this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps));
|
|
@@ -4606,12 +4655,12 @@ class ViewState {
|
|
|
4606
4655
|
// to a line end is going to trigger a layout anyway, so it
|
|
4607
4656
|
// can't be a pure write. It should be rare that it does any
|
|
4608
4657
|
// writing.
|
|
4609
|
-
docView.enforceCursorAssoc();
|
|
4658
|
+
view.docView.enforceCursorAssoc();
|
|
4610
4659
|
}
|
|
4611
4660
|
return result;
|
|
4612
4661
|
}
|
|
4613
|
-
get visibleTop() { return this.scaler.fromDOM(this.pixelViewport.top
|
|
4614
|
-
get visibleBottom() { return this.scaler.fromDOM(this.pixelViewport.bottom
|
|
4662
|
+
get visibleTop() { return this.scaler.fromDOM(this.pixelViewport.top); }
|
|
4663
|
+
get visibleBottom() { return this.scaler.fromDOM(this.pixelViewport.bottom); }
|
|
4615
4664
|
getViewport(bias, scrollTarget) {
|
|
4616
4665
|
// This will divide VP.Margin between the top and the
|
|
4617
4666
|
// bottom, depending on the bias (the change in viewport position
|
|
@@ -4671,12 +4720,12 @@ class ViewState {
|
|
|
4671
4720
|
// This won't work at all in predominantly right-to-left text.
|
|
4672
4721
|
if (this.heightOracle.direction != Direction.LTR)
|
|
4673
4722
|
return gaps;
|
|
4674
|
-
|
|
4723
|
+
for (let line of this.viewportLines) {
|
|
4675
4724
|
if (line.length < 4000 /* DoubleMargin */)
|
|
4676
|
-
|
|
4725
|
+
continue;
|
|
4677
4726
|
let structure = lineStructure(line.from, line.to, this.state);
|
|
4678
4727
|
if (structure.total < 4000 /* DoubleMargin */)
|
|
4679
|
-
|
|
4728
|
+
continue;
|
|
4680
4729
|
let viewFrom, viewTo;
|
|
4681
4730
|
if (this.heightOracle.lineWrapping) {
|
|
4682
4731
|
let marginHeight = (2000 /* Margin */ / this.heightOracle.lineLength) * this.heightOracle.lineHeight;
|
|
@@ -4706,7 +4755,7 @@ class ViewState {
|
|
|
4706
4755
|
Math.abs(gap.from - from) < 1000 /* HalfMargin */ && Math.abs(gap.to - to) < 1000 /* HalfMargin */) ||
|
|
4707
4756
|
new LineGap(from, to, this.gapSize(line, from, to, structure)));
|
|
4708
4757
|
}
|
|
4709
|
-
}
|
|
4758
|
+
}
|
|
4710
4759
|
return gaps;
|
|
4711
4760
|
}
|
|
4712
4761
|
gapSize(line, from, to, structure) {
|
|
@@ -4738,27 +4787,18 @@ class ViewState {
|
|
|
4738
4787
|
this.visibleRanges = ranges;
|
|
4739
4788
|
return changed ? 4 /* Viewport */ : 0;
|
|
4740
4789
|
}
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4790
|
+
lineBlockAt(pos) {
|
|
4791
|
+
return (pos >= this.viewport.from && pos <= this.viewport.to && this.viewportLines.find(b => b.from <= pos && b.to >= pos)) ||
|
|
4792
|
+
scaleBlock(this.heightMap.lineAt(pos, QueryType.ByPos, this.state.doc, 0, 0), this.scaler);
|
|
4744
4793
|
}
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height, editorTop), QueryType.ByHeight, this.state.doc, editorTop, 0), this.scaler, editorTop);
|
|
4794
|
+
lineBlockAtHeight(height) {
|
|
4795
|
+
return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.state.doc, 0, 0), this.scaler);
|
|
4748
4796
|
}
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height, editorTop), this.state.doc, editorTop, 0), this.scaler, editorTop);
|
|
4752
|
-
}
|
|
4753
|
-
forEachLine(from, to, f, editorTop) {
|
|
4754
|
-
editorTop += this.paddingTop;
|
|
4755
|
-
return this.heightMap.forEachLine(from, to, this.state.doc, editorTop, 0, this.scaler.scale == 1 ? f : b => f(scaleBlock(b, this.scaler, editorTop)));
|
|
4797
|
+
elementAtHeight(height) {
|
|
4798
|
+
return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.state.doc, 0, 0), this.scaler);
|
|
4756
4799
|
}
|
|
4757
4800
|
get contentHeight() {
|
|
4758
|
-
return this.
|
|
4759
|
-
}
|
|
4760
|
-
get domHeight() {
|
|
4761
|
-
return this.scaler.toDOM(this.heightMap.height, this.paddingTop);
|
|
4801
|
+
return this.scaler.toDOM(this.heightMap.height) + this.paddingTop + this.paddingBottom;
|
|
4762
4802
|
}
|
|
4763
4803
|
}
|
|
4764
4804
|
class Viewport {
|
|
@@ -4855,36 +4895,34 @@ class BigScaler {
|
|
|
4855
4895
|
base = obj.bottom;
|
|
4856
4896
|
}
|
|
4857
4897
|
}
|
|
4858
|
-
toDOM(n
|
|
4859
|
-
n -= top;
|
|
4898
|
+
toDOM(n) {
|
|
4860
4899
|
for (let i = 0, base = 0, domBase = 0;; i++) {
|
|
4861
4900
|
let vp = i < this.viewports.length ? this.viewports[i] : null;
|
|
4862
4901
|
if (!vp || n < vp.top)
|
|
4863
|
-
return domBase + (n - base) * this.scale
|
|
4902
|
+
return domBase + (n - base) * this.scale;
|
|
4864
4903
|
if (n <= vp.bottom)
|
|
4865
|
-
return vp.domTop + (n - vp.top)
|
|
4904
|
+
return vp.domTop + (n - vp.top);
|
|
4866
4905
|
base = vp.bottom;
|
|
4867
4906
|
domBase = vp.domBottom;
|
|
4868
4907
|
}
|
|
4869
4908
|
}
|
|
4870
|
-
fromDOM(n
|
|
4871
|
-
n -= top;
|
|
4909
|
+
fromDOM(n) {
|
|
4872
4910
|
for (let i = 0, base = 0, domBase = 0;; i++) {
|
|
4873
4911
|
let vp = i < this.viewports.length ? this.viewports[i] : null;
|
|
4874
4912
|
if (!vp || n < vp.domTop)
|
|
4875
|
-
return base + (n - domBase) / this.scale
|
|
4913
|
+
return base + (n - domBase) / this.scale;
|
|
4876
4914
|
if (n <= vp.domBottom)
|
|
4877
|
-
return vp.top + (n - vp.domTop)
|
|
4915
|
+
return vp.top + (n - vp.domTop);
|
|
4878
4916
|
base = vp.bottom;
|
|
4879
4917
|
domBase = vp.domBottom;
|
|
4880
4918
|
}
|
|
4881
4919
|
}
|
|
4882
4920
|
}
|
|
4883
|
-
function scaleBlock(block, scaler
|
|
4921
|
+
function scaleBlock(block, scaler) {
|
|
4884
4922
|
if (scaler.scale == 1)
|
|
4885
4923
|
return block;
|
|
4886
|
-
let bTop = scaler.toDOM(block.top
|
|
4887
|
-
return new BlockInfo(block.from, block.length, bTop, bBottom - bTop, Array.isArray(block.type) ? block.type.map(b => scaleBlock(b, scaler
|
|
4924
|
+
let bTop = scaler.toDOM(block.top), bBottom = scaler.toDOM(block.bottom);
|
|
4925
|
+
return new BlockInfo(block.from, block.length, bTop, bBottom - bTop, Array.isArray(block.type) ? block.type.map(b => scaleBlock(b, scaler)) : block.type);
|
|
4888
4926
|
}
|
|
4889
4927
|
|
|
4890
4928
|
const theme = /*@__PURE__*/Facet.define({ combine: strs => strs.join(" ") });
|
|
@@ -5069,25 +5107,30 @@ class DOMObserver {
|
|
|
5069
5107
|
this.onChange = onChange;
|
|
5070
5108
|
this.onScrollChanged = onScrollChanged;
|
|
5071
5109
|
this.active = false;
|
|
5072
|
-
|
|
5110
|
+
// The known selection. Kept in our own object, as opposed to just
|
|
5111
|
+
// directly accessing the selection because:
|
|
5112
|
+
// - Safari doesn't report the right selection in shadow DOM
|
|
5113
|
+
// - Reading from the selection forces a DOM layout
|
|
5114
|
+
// - This way, we can ignore selectionchange events if we have
|
|
5115
|
+
// already seen the 'new' selection
|
|
5116
|
+
this.selectionRange = new DOMSelectionState;
|
|
5117
|
+
// Set when a selection change is detected, cleared on flush
|
|
5118
|
+
this.selectionChanged = false;
|
|
5073
5119
|
this.delayedFlush = -1;
|
|
5120
|
+
this.resizeTimeout = -1;
|
|
5074
5121
|
this.queue = [];
|
|
5075
|
-
this.lastFlush = 0;
|
|
5076
5122
|
this.scrollTargets = [];
|
|
5077
5123
|
this.intersection = null;
|
|
5078
5124
|
this.resize = null;
|
|
5079
5125
|
this.intersecting = false;
|
|
5080
5126
|
this.gapIntersection = null;
|
|
5081
5127
|
this.gaps = [];
|
|
5082
|
-
// Used to work around a Safari Selection/shadow DOM bug (#414)
|
|
5083
|
-
this._selectionRange = null;
|
|
5084
5128
|
// Timeout for scheduling check of the parents that need scroll handlers
|
|
5085
5129
|
this.parentCheck = -1;
|
|
5086
5130
|
this.dom = view.contentDOM;
|
|
5087
5131
|
this.observer = new MutationObserver(mutations => {
|
|
5088
5132
|
for (let mut of mutations)
|
|
5089
5133
|
this.queue.push(mut);
|
|
5090
|
-
this._selectionRange = null;
|
|
5091
5134
|
// IE11 will sometimes (on typing over a selection or
|
|
5092
5135
|
// backspacing out a single character text node) call the
|
|
5093
5136
|
// observer callback before actually updating the DOM.
|
|
@@ -5114,8 +5157,11 @@ class DOMObserver {
|
|
|
5114
5157
|
this.onSelectionChange = this.onSelectionChange.bind(this);
|
|
5115
5158
|
if (typeof ResizeObserver == "function") {
|
|
5116
5159
|
this.resize = new ResizeObserver(() => {
|
|
5117
|
-
if (this.view.docView.lastUpdate < Date.now() -
|
|
5118
|
-
this.
|
|
5160
|
+
if (this.view.docView.lastUpdate < Date.now() - 75 && this.resizeTimeout < 0)
|
|
5161
|
+
this.resizeTimeout = setTimeout(() => {
|
|
5162
|
+
this.resizeTimeout = -1;
|
|
5163
|
+
this.view.requestMeasure();
|
|
5164
|
+
}, 50);
|
|
5119
5165
|
});
|
|
5120
5166
|
this.resize.observe(view.scrollDOM);
|
|
5121
5167
|
}
|
|
@@ -5139,10 +5185,12 @@ class DOMObserver {
|
|
|
5139
5185
|
}, {});
|
|
5140
5186
|
}
|
|
5141
5187
|
this.listenForScroll();
|
|
5188
|
+
this.readSelectionRange();
|
|
5189
|
+
this.dom.ownerDocument.addEventListener("selectionchange", this.onSelectionChange);
|
|
5142
5190
|
}
|
|
5143
5191
|
onScroll(e) {
|
|
5144
5192
|
if (this.intersecting)
|
|
5145
|
-
this.flush();
|
|
5193
|
+
this.flush(false);
|
|
5146
5194
|
this.onScrollChanged(e);
|
|
5147
5195
|
}
|
|
5148
5196
|
updateGaps(gaps) {
|
|
@@ -5154,8 +5202,8 @@ class DOMObserver {
|
|
|
5154
5202
|
}
|
|
5155
5203
|
}
|
|
5156
5204
|
onSelectionChange(event) {
|
|
5157
|
-
if (this.
|
|
5158
|
-
|
|
5205
|
+
if (!this.readSelectionRange())
|
|
5206
|
+
return;
|
|
5159
5207
|
let { view } = this, sel = this.selectionRange;
|
|
5160
5208
|
if (view.state.facet(editable) ? view.root.activeElement != this.dom : !hasSelection(view.dom, sel))
|
|
5161
5209
|
return;
|
|
@@ -5170,24 +5218,22 @@ class DOMObserver {
|
|
|
5170
5218
|
sel.focusNode && isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset))
|
|
5171
5219
|
this.flushSoon();
|
|
5172
5220
|
else
|
|
5173
|
-
this.flush();
|
|
5174
|
-
}
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
return this.
|
|
5221
|
+
this.flush(false);
|
|
5222
|
+
}
|
|
5223
|
+
readSelectionRange() {
|
|
5224
|
+
let { root } = this.view, domSel = getSelection(root);
|
|
5225
|
+
// The Selection object is broken in shadow roots in Safari. See
|
|
5226
|
+
// https://github.com/codemirror/codemirror.next/issues/414
|
|
5227
|
+
let range = browser.safari && root.nodeType == 11 && deepActiveElement() == this.view.contentDOM &&
|
|
5228
|
+
safariSelectionRangeHack(this.view) || domSel;
|
|
5229
|
+
if (this.selectionRange.eq(range))
|
|
5230
|
+
return false;
|
|
5231
|
+
this.selectionRange.setRange(range);
|
|
5232
|
+
return this.selectionChanged = true;
|
|
5185
5233
|
}
|
|
5186
5234
|
setSelectionRange(anchor, head) {
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
this._selectionRange = { anchorNode: anchor.node, anchorOffset: anchor.offset,
|
|
5190
|
-
focusNode: head.node, focusOffset: head.offset };
|
|
5235
|
+
this.selectionRange.set(anchor.node, anchor.offset, head.node, head.offset);
|
|
5236
|
+
this.selectionChanged = false;
|
|
5191
5237
|
}
|
|
5192
5238
|
listenForScroll() {
|
|
5193
5239
|
this.parentCheck = -1;
|
|
@@ -5234,7 +5280,6 @@ class DOMObserver {
|
|
|
5234
5280
|
if (this.active)
|
|
5235
5281
|
return;
|
|
5236
5282
|
this.observer.observe(this.dom, observeOptions);
|
|
5237
|
-
this.dom.ownerDocument.addEventListener("selectionchange", this.onSelectionChange);
|
|
5238
5283
|
if (useCharData)
|
|
5239
5284
|
this.dom.addEventListener("DOMCharacterDataModified", this.onCharData);
|
|
5240
5285
|
this.active = true;
|
|
@@ -5244,18 +5289,14 @@ class DOMObserver {
|
|
|
5244
5289
|
return;
|
|
5245
5290
|
this.active = false;
|
|
5246
5291
|
this.observer.disconnect();
|
|
5247
|
-
this.dom.ownerDocument.removeEventListener("selectionchange", this.onSelectionChange);
|
|
5248
5292
|
if (useCharData)
|
|
5249
5293
|
this.dom.removeEventListener("DOMCharacterDataModified", this.onCharData);
|
|
5250
5294
|
}
|
|
5251
|
-
clearSelection() {
|
|
5252
|
-
this.ignoreSelection.set(this.selectionRange);
|
|
5253
|
-
}
|
|
5254
5295
|
// Throw away any pending changes
|
|
5255
5296
|
clear() {
|
|
5256
5297
|
this.observer.takeRecords();
|
|
5257
5298
|
this.queue.length = 0;
|
|
5258
|
-
this.
|
|
5299
|
+
this.selectionChanged = false;
|
|
5259
5300
|
}
|
|
5260
5301
|
flushSoon() {
|
|
5261
5302
|
if (this.delayedFlush < 0)
|
|
@@ -5292,24 +5333,24 @@ class DOMObserver {
|
|
|
5292
5333
|
return { from, to, typeOver };
|
|
5293
5334
|
}
|
|
5294
5335
|
// Apply pending changes, if any
|
|
5295
|
-
flush() {
|
|
5336
|
+
flush(readSelection = true) {
|
|
5337
|
+
if (readSelection)
|
|
5338
|
+
this.readSelectionRange();
|
|
5296
5339
|
// Completely hold off flushing when pending keys are set—the code
|
|
5297
5340
|
// managing those will make sure processRecords is called and the
|
|
5298
5341
|
// view is resynchronized after
|
|
5299
5342
|
if (this.delayedFlush >= 0 || this.view.inputState.pendingAndroidKey)
|
|
5300
5343
|
return;
|
|
5301
|
-
this.lastFlush = Date.now();
|
|
5302
5344
|
let { from, to, typeOver } = this.processRecords();
|
|
5303
|
-
let
|
|
5304
|
-
let newSel = !this.ignoreSelection.eq(selection) && hasSelection(this.dom, selection);
|
|
5345
|
+
let newSel = this.selectionChanged && hasSelection(this.dom, this.selectionRange);
|
|
5305
5346
|
if (from < 0 && !newSel)
|
|
5306
5347
|
return;
|
|
5348
|
+
this.selectionChanged = false;
|
|
5307
5349
|
let startState = this.view.state;
|
|
5308
5350
|
this.onChange(from, to, typeOver);
|
|
5309
5351
|
// The view wasn't updated
|
|
5310
5352
|
if (this.view.state == startState)
|
|
5311
5353
|
this.view.docView.reset(newSel);
|
|
5312
|
-
this.clearSelection();
|
|
5313
5354
|
}
|
|
5314
5355
|
readMutation(rec) {
|
|
5315
5356
|
let cView = this.view.docView.nearest(rec.target);
|
|
@@ -5341,6 +5382,7 @@ class DOMObserver {
|
|
|
5341
5382
|
dom.removeEventListener("scroll", this.onScroll);
|
|
5342
5383
|
window.removeEventListener("scroll", this.onScroll);
|
|
5343
5384
|
clearTimeout(this.parentCheck);
|
|
5385
|
+
clearTimeout(this.resizeTimeout);
|
|
5344
5386
|
}
|
|
5345
5387
|
}
|
|
5346
5388
|
function findChild(cView, dom, dir) {
|
|
@@ -5353,6 +5395,7 @@ function findChild(cView, dom, dir) {
|
|
|
5353
5395
|
}
|
|
5354
5396
|
return null;
|
|
5355
5397
|
}
|
|
5398
|
+
// Used to work around a Safari Selection/shadow DOM bug (#414)
|
|
5356
5399
|
function safariSelectionRangeHack(view) {
|
|
5357
5400
|
let found = null;
|
|
5358
5401
|
// Because Safari (at least in 2018-2021) doesn't provide regular
|
|
@@ -5772,6 +5815,7 @@ class EditorView {
|
|
|
5772
5815
|
this.mountStyles();
|
|
5773
5816
|
this.updateAttrs();
|
|
5774
5817
|
this.showAnnouncements(transactions);
|
|
5818
|
+
this.docView.updateSelection(redrawn, transactions.some(tr => tr.isUserEvent("select.pointer")));
|
|
5775
5819
|
}
|
|
5776
5820
|
finally {
|
|
5777
5821
|
this.updateState = 0 /* Idle */;
|
|
@@ -5857,11 +5901,11 @@ class EditorView {
|
|
|
5857
5901
|
for (let i = 0;; i++) {
|
|
5858
5902
|
this.updateState = 1 /* Measuring */;
|
|
5859
5903
|
let oldViewport = this.viewport;
|
|
5860
|
-
let changed = this.viewState.measure(this
|
|
5904
|
+
let changed = this.viewState.measure(this);
|
|
5861
5905
|
if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
|
|
5862
5906
|
break;
|
|
5863
5907
|
if (i > 5) {
|
|
5864
|
-
console.warn("Viewport failed to stabilize");
|
|
5908
|
+
console.warn(this.measureRequests.length ? "Measure loop restarted more than 5 times" : "Viewport failed to stabilize");
|
|
5865
5909
|
break;
|
|
5866
5910
|
}
|
|
5867
5911
|
let measuring = [];
|
|
@@ -5877,7 +5921,7 @@ class EditorView {
|
|
|
5877
5921
|
return BadMeasure;
|
|
5878
5922
|
}
|
|
5879
5923
|
});
|
|
5880
|
-
let update = new ViewUpdate(this, this.state);
|
|
5924
|
+
let update = new ViewUpdate(this, this.state), redrawn = false;
|
|
5881
5925
|
update.flags |= changed;
|
|
5882
5926
|
if (!updated)
|
|
5883
5927
|
updated = update;
|
|
@@ -5887,14 +5931,15 @@ class EditorView {
|
|
|
5887
5931
|
if (!update.empty) {
|
|
5888
5932
|
this.updatePlugins(update);
|
|
5889
5933
|
this.inputState.update(update);
|
|
5934
|
+
this.updateAttrs();
|
|
5935
|
+
redrawn = this.docView.update(update);
|
|
5890
5936
|
}
|
|
5891
|
-
this.updateAttrs();
|
|
5892
|
-
if (changed)
|
|
5893
|
-
this.docView.update(update);
|
|
5894
5937
|
for (let i = 0; i < measuring.length; i++)
|
|
5895
5938
|
if (measured[i] != BadMeasure) {
|
|
5896
5939
|
try {
|
|
5897
|
-
measuring[i]
|
|
5940
|
+
let m = measuring[i];
|
|
5941
|
+
if (m.write)
|
|
5942
|
+
m.write(measured[i], this);
|
|
5898
5943
|
}
|
|
5899
5944
|
catch (e) {
|
|
5900
5945
|
logException(this.state, e);
|
|
@@ -5904,6 +5949,8 @@ class EditorView {
|
|
|
5904
5949
|
this.docView.scrollIntoView(this.viewState.scrollTarget);
|
|
5905
5950
|
this.viewState.scrollTarget = null;
|
|
5906
5951
|
}
|
|
5952
|
+
if (redrawn)
|
|
5953
|
+
this.docView.updateSelection(true);
|
|
5907
5954
|
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to && this.measureRequests.length == 0)
|
|
5908
5955
|
break;
|
|
5909
5956
|
}
|
|
@@ -5928,8 +5975,6 @@ class EditorView {
|
|
|
5928
5975
|
let editorAttrs = combineAttrs(this.state.facet(editorAttributes), {
|
|
5929
5976
|
class: "cm-editor" + (this.hasFocus ? " cm-focused " : " ") + this.themeClasses
|
|
5930
5977
|
});
|
|
5931
|
-
updateAttrs(this.dom, this.editorAttrs, editorAttrs);
|
|
5932
|
-
this.editorAttrs = editorAttrs;
|
|
5933
5978
|
let contentAttrs = {
|
|
5934
5979
|
spellcheck: "false",
|
|
5935
5980
|
autocorrect: "off",
|
|
@@ -5944,7 +5989,11 @@ class EditorView {
|
|
|
5944
5989
|
if (this.state.readOnly)
|
|
5945
5990
|
contentAttrs["aria-readonly"] = "true";
|
|
5946
5991
|
combineAttrs(this.state.facet(contentAttributes), contentAttrs);
|
|
5947
|
-
|
|
5992
|
+
this.observer.ignore(() => {
|
|
5993
|
+
updateAttrs(this.contentDOM, this.contentAttrs, contentAttrs);
|
|
5994
|
+
updateAttrs(this.dom, this.editorAttrs, editorAttrs);
|
|
5995
|
+
});
|
|
5996
|
+
this.editorAttrs = editorAttrs;
|
|
5948
5997
|
this.contentAttrs = contentAttrs;
|
|
5949
5998
|
}
|
|
5950
5999
|
showAnnouncements(trs) {
|
|
@@ -6014,6 +6063,20 @@ class EditorView {
|
|
|
6014
6063
|
return null;
|
|
6015
6064
|
}
|
|
6016
6065
|
/**
|
|
6066
|
+
The top position of the document, in screen coordinates. This
|
|
6067
|
+
may be negative when the editor is scrolled down. Points
|
|
6068
|
+
directly to the top of the first line, not above the padding.
|
|
6069
|
+
*/
|
|
6070
|
+
get documentTop() {
|
|
6071
|
+
return this.contentDOM.getBoundingClientRect().top + this.viewState.paddingTop;
|
|
6072
|
+
}
|
|
6073
|
+
/**
|
|
6074
|
+
Reports the padding above and below the document.
|
|
6075
|
+
*/
|
|
6076
|
+
get documentPadding() {
|
|
6077
|
+
return { top: this.viewState.paddingTop, bottom: this.viewState.paddingBottom };
|
|
6078
|
+
}
|
|
6079
|
+
/**
|
|
6017
6080
|
Find the line or block widget at the given vertical position.
|
|
6018
6081
|
|
|
6019
6082
|
By default, this position is interpreted as a screen position,
|
|
@@ -6023,10 +6086,21 @@ class EditorView {
|
|
|
6023
6086
|
position, or a precomputed document top
|
|
6024
6087
|
(`view.contentDOM.getBoundingClientRect().top`) to limit layout
|
|
6025
6088
|
queries.
|
|
6089
|
+
|
|
6090
|
+
*Deprecated: use `blockAtHeight` instead.*
|
|
6026
6091
|
*/
|
|
6027
6092
|
blockAtHeight(height, docTop) {
|
|
6093
|
+
let top = ensureTop(docTop, this);
|
|
6094
|
+
return this.elementAtHeight(height - top).moveY(top);
|
|
6095
|
+
}
|
|
6096
|
+
/**
|
|
6097
|
+
Find the text line or block widget at the given vertical
|
|
6098
|
+
position (which is interpreted as relative to the [top of the
|
|
6099
|
+
document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)
|
|
6100
|
+
*/
|
|
6101
|
+
elementAtHeight(height) {
|
|
6028
6102
|
this.readMeasured();
|
|
6029
|
-
return this.viewState.
|
|
6103
|
+
return this.viewState.elementAtHeight(height);
|
|
6030
6104
|
}
|
|
6031
6105
|
/**
|
|
6032
6106
|
Find information for the visual line (see
|
|
@@ -6038,20 +6112,43 @@ class EditorView {
|
|
|
6038
6112
|
Defaults to treating `height` as a screen position. See
|
|
6039
6113
|
[`blockAtHeight`](https://codemirror.net/6/docs/ref/#view.EditorView.blockAtHeight) for the
|
|
6040
6114
|
interpretation of the `docTop` parameter.
|
|
6115
|
+
|
|
6116
|
+
*Deprecated: use `lineBlockAtHeight` instead.*
|
|
6041
6117
|
*/
|
|
6042
6118
|
visualLineAtHeight(height, docTop) {
|
|
6119
|
+
let top = ensureTop(docTop, this);
|
|
6120
|
+
return this.lineBlockAtHeight(height - top).moveY(top);
|
|
6121
|
+
}
|
|
6122
|
+
/**
|
|
6123
|
+
Find the line block (see
|
|
6124
|
+
[`lineBlockAt`](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) at the given
|
|
6125
|
+
height.
|
|
6126
|
+
*/
|
|
6127
|
+
lineBlockAtHeight(height) {
|
|
6043
6128
|
this.readMeasured();
|
|
6044
|
-
return this.viewState.
|
|
6129
|
+
return this.viewState.lineBlockAtHeight(height);
|
|
6045
6130
|
}
|
|
6046
6131
|
/**
|
|
6047
6132
|
Iterate over the height information of the visual lines in the
|
|
6048
6133
|
viewport. The heights of lines are reported relative to the
|
|
6049
6134
|
given document top, which defaults to the screen position of the
|
|
6050
6135
|
document (forcing a layout).
|
|
6136
|
+
|
|
6137
|
+
*Deprecated: use `viewportLineBlocks` instead.*
|
|
6051
6138
|
*/
|
|
6052
6139
|
viewportLines(f, docTop) {
|
|
6053
|
-
let
|
|
6054
|
-
|
|
6140
|
+
let top = ensureTop(docTop, this);
|
|
6141
|
+
for (let line of this.viewportLineBlocks)
|
|
6142
|
+
f(line.moveY(top));
|
|
6143
|
+
}
|
|
6144
|
+
/**
|
|
6145
|
+
Get the extent and vertical position of all [line
|
|
6146
|
+
blocks](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) in the viewport. Positions
|
|
6147
|
+
are relative to the [top of the
|
|
6148
|
+
document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop);
|
|
6149
|
+
*/
|
|
6150
|
+
get viewportLineBlocks() {
|
|
6151
|
+
return this.viewState.viewportLines;
|
|
6055
6152
|
}
|
|
6056
6153
|
/**
|
|
6057
6154
|
Find the extent and height of the visual line (a range delimited
|
|
@@ -6062,9 +6159,22 @@ class EditorView {
|
|
|
6062
6159
|
argument, which defaults to 0 for this method. You can pass
|
|
6063
6160
|
`view.contentDOM.getBoundingClientRect().top` here to get screen
|
|
6064
6161
|
coordinates.
|
|
6162
|
+
|
|
6163
|
+
*Deprecated: use `lineBlockAt` instead.*
|
|
6065
6164
|
*/
|
|
6066
6165
|
visualLineAt(pos, docTop = 0) {
|
|
6067
|
-
return this.
|
|
6166
|
+
return this.lineBlockAt(pos).moveY(docTop + this.viewState.paddingTop);
|
|
6167
|
+
}
|
|
6168
|
+
/**
|
|
6169
|
+
Find the line block around the given document position. A line
|
|
6170
|
+
block is a range delimited on both sides by either a
|
|
6171
|
+
non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^range) line breaks, or the
|
|
6172
|
+
start/end of the document. It will usually just hold a line of
|
|
6173
|
+
text, but may be broken into multiple textblocks by block
|
|
6174
|
+
widgets.
|
|
6175
|
+
*/
|
|
6176
|
+
lineBlockAt(pos) {
|
|
6177
|
+
return this.viewState.lineBlockAt(pos);
|
|
6068
6178
|
}
|
|
6069
6179
|
/**
|
|
6070
6180
|
The editor's total content height.
|
|
@@ -6397,8 +6507,9 @@ search match).
|
|
|
6397
6507
|
EditorView.announce = /*@__PURE__*/StateEffect.define();
|
|
6398
6508
|
// Maximum line length for which we compute accurate bidi info
|
|
6399
6509
|
const MaxBidiLine = 4096;
|
|
6400
|
-
|
|
6401
|
-
|
|
6510
|
+
// FIXME remove this and its callers on next breaking release
|
|
6511
|
+
function ensureTop(given, view) {
|
|
6512
|
+
return (given == null ? view.contentDOM.getBoundingClientRect().top : given) + view.viewState.paddingTop;
|
|
6402
6513
|
}
|
|
6403
6514
|
let resizeDebounce = -1;
|
|
6404
6515
|
function ensureGlobalHandler() {
|
|
@@ -6749,7 +6860,7 @@ function wrappedLine(view, pos, inside) {
|
|
|
6749
6860
|
type: BlockType.Text };
|
|
6750
6861
|
}
|
|
6751
6862
|
function blockAt(view, pos) {
|
|
6752
|
-
let line = view.
|
|
6863
|
+
let line = view.lineBlockAt(pos);
|
|
6753
6864
|
if (Array.isArray(line.type))
|
|
6754
6865
|
for (let l of line.type) {
|
|
6755
6866
|
if (l.to > pos || l.to == pos && (l.to == line.to || l.type == BlockType.Text))
|
|
@@ -7140,7 +7251,7 @@ const activeLineHighlighter = /*@__PURE__*/ViewPlugin.fromClass(class {
|
|
|
7140
7251
|
for (let r of view.state.selection.ranges) {
|
|
7141
7252
|
if (!r.empty)
|
|
7142
7253
|
return Decoration.none;
|
|
7143
|
-
let line = view.
|
|
7254
|
+
let line = view.lineBlockAt(r.head);
|
|
7144
7255
|
if (line.from > lastLineStart) {
|
|
7145
7256
|
deco.push(lineDeco.range(line.from));
|
|
7146
7257
|
lastLineStart = line.from;
|