@codemirror/view 0.19.19 → 0.19.23
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 +290 -162
- package/dist/index.d.ts +52 -2
- package/dist/index.js +290 -162
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -186,7 +186,7 @@ function scrollRectIntoView(dom, rect, side, center) {
|
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
|
-
class
|
|
189
|
+
class DOMSelectionState {
|
|
190
190
|
constructor() {
|
|
191
191
|
this.anchorNode = null;
|
|
192
192
|
this.anchorOffset = 0;
|
|
@@ -197,11 +197,14 @@ class DOMSelection {
|
|
|
197
197
|
return this.anchorNode == domSel.anchorNode && this.anchorOffset == domSel.anchorOffset &&
|
|
198
198
|
this.focusNode == domSel.focusNode && this.focusOffset == domSel.focusOffset;
|
|
199
199
|
}
|
|
200
|
-
|
|
201
|
-
this.anchorNode
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
this.
|
|
200
|
+
setRange(range) {
|
|
201
|
+
this.set(range.anchorNode, range.anchorOffset, range.focusNode, range.focusOffset);
|
|
202
|
+
}
|
|
203
|
+
set(anchorNode, anchorOffset, focusNode, focusOffset) {
|
|
204
|
+
this.anchorNode = anchorNode;
|
|
205
|
+
this.anchorOffset = anchorOffset;
|
|
206
|
+
this.focusNode = focusNode;
|
|
207
|
+
this.focusOffset = focusOffset;
|
|
205
208
|
}
|
|
206
209
|
}
|
|
207
210
|
let preventScrollSupported = null;
|
|
@@ -1909,7 +1912,7 @@ class ViewUpdate {
|
|
|
1909
1912
|
Whether the document changed in this update.
|
|
1910
1913
|
*/
|
|
1911
1914
|
get docChanged() {
|
|
1912
|
-
return this.
|
|
1915
|
+
return !this.changes.empty;
|
|
1913
1916
|
}
|
|
1914
1917
|
/**
|
|
1915
1918
|
Whether the selection was explicitly set in this update.
|
|
@@ -1943,6 +1946,10 @@ class DocView extends ContentView {
|
|
|
1943
1946
|
// we don't mess it up when reading it back it
|
|
1944
1947
|
this.impreciseAnchor = null;
|
|
1945
1948
|
this.impreciseHead = null;
|
|
1949
|
+
this.forceSelection = false;
|
|
1950
|
+
// Used by the resize observer to ignore resizes that we caused
|
|
1951
|
+
// ourselves
|
|
1952
|
+
this.lastUpdate = Date.now();
|
|
1946
1953
|
this.setDOM(view.contentDOM);
|
|
1947
1954
|
this.children = [new LineView];
|
|
1948
1955
|
this.children[0].setParent(this);
|
|
@@ -1975,21 +1982,19 @@ class DocView extends ContentView {
|
|
|
1975
1982
|
// getSelection than the one that it actually shows to the user.
|
|
1976
1983
|
// This forces a selection update when lines are joined to work
|
|
1977
1984
|
// around that. Issue #54
|
|
1978
|
-
|
|
1979
|
-
update.state.doc.lines != update.startState.doc.lines
|
|
1985
|
+
if ((browser.ie || browser.chrome) && !this.compositionDeco.size && update &&
|
|
1986
|
+
update.state.doc.lines != update.startState.doc.lines)
|
|
1987
|
+
this.forceSelection = true;
|
|
1980
1988
|
let prevDeco = this.decorations, deco = this.updateDeco();
|
|
1981
1989
|
let decoDiff = findChangedDeco(prevDeco, deco, update.changes);
|
|
1982
1990
|
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);
|
|
1991
|
+
if (this.dirty == 0 /* Not */ && changedRanges.length == 0) {
|
|
1989
1992
|
return false;
|
|
1990
1993
|
}
|
|
1991
1994
|
else {
|
|
1992
|
-
this.updateInner(changedRanges, deco, update.startState.doc.length
|
|
1995
|
+
this.updateInner(changedRanges, deco, update.startState.doc.length);
|
|
1996
|
+
if (update.transactions.length)
|
|
1997
|
+
this.lastUpdate = Date.now();
|
|
1993
1998
|
return true;
|
|
1994
1999
|
}
|
|
1995
2000
|
}
|
|
@@ -1997,13 +2002,16 @@ class DocView extends ContentView {
|
|
|
1997
2002
|
if (this.dirty) {
|
|
1998
2003
|
this.view.observer.ignore(() => this.view.docView.sync());
|
|
1999
2004
|
this.dirty = 0 /* Not */;
|
|
2005
|
+
this.updateSelection(true);
|
|
2000
2006
|
}
|
|
2001
|
-
|
|
2007
|
+
else {
|
|
2002
2008
|
this.updateSelection();
|
|
2009
|
+
}
|
|
2003
2010
|
}
|
|
2004
2011
|
// Used both by update and checkLayout do perform the actual DOM
|
|
2005
2012
|
// update
|
|
2006
|
-
updateInner(changes, deco, oldLength
|
|
2013
|
+
updateInner(changes, deco, oldLength) {
|
|
2014
|
+
this.view.viewState.mustMeasureContent = true;
|
|
2007
2015
|
this.updateChildren(changes, deco, oldLength);
|
|
2008
2016
|
let { observer } = this.view;
|
|
2009
2017
|
observer.ignore(() => {
|
|
@@ -2011,7 +2019,7 @@ class DocView extends ContentView {
|
|
|
2011
2019
|
// messes with the scroll position during DOM mutation (though
|
|
2012
2020
|
// no relayout is triggered and I cannot imagine how it can
|
|
2013
2021
|
// recompute the scroll position without a layout)
|
|
2014
|
-
this.dom.style.height = this.view.viewState.
|
|
2022
|
+
this.dom.style.height = this.view.viewState.contentHeight + "px";
|
|
2015
2023
|
this.dom.style.minWidth = this.minWidth ? this.minWidth + "px" : "";
|
|
2016
2024
|
// Chrome will sometimes, when DOM mutations occur directly
|
|
2017
2025
|
// around the selection, get confused and report a different
|
|
@@ -2021,8 +2029,7 @@ class DocView extends ContentView {
|
|
|
2021
2029
|
this.sync(track);
|
|
2022
2030
|
this.dirty = 0 /* Not */;
|
|
2023
2031
|
if (track && (track.written || observer.selectionRange.focusNode != track.node))
|
|
2024
|
-
forceSelection = true;
|
|
2025
|
-
this.updateSelection(forceSelection, pointerSel);
|
|
2032
|
+
this.forceSelection = true;
|
|
2026
2033
|
this.dom.style.height = "";
|
|
2027
2034
|
});
|
|
2028
2035
|
let gaps = [];
|
|
@@ -2108,10 +2115,14 @@ class DocView extends ContentView {
|
|
|
2108
2115
|
this.replaceChildren(fromI, toI, content);
|
|
2109
2116
|
}
|
|
2110
2117
|
// Sync the DOM selection to this.state.selection
|
|
2111
|
-
updateSelection(
|
|
2118
|
+
updateSelection(mustRead = false, fromPointer = false) {
|
|
2119
|
+
if (mustRead)
|
|
2120
|
+
this.view.observer.readSelectionRange();
|
|
2112
2121
|
if (!(fromPointer || this.mayControlSelection()) ||
|
|
2113
2122
|
browser.ios && this.view.inputState.rapidCompositionStart)
|
|
2114
2123
|
return;
|
|
2124
|
+
let force = this.forceSelection;
|
|
2125
|
+
this.forceSelection = false;
|
|
2115
2126
|
let main = this.view.state.selection.main;
|
|
2116
2127
|
// FIXME need to handle the case where the selection falls inside a block range
|
|
2117
2128
|
let anchor = this.domAtPos(main.anchor);
|
|
@@ -2293,7 +2304,7 @@ class DocView extends ContentView {
|
|
|
2293
2304
|
let next = i == vs.viewports.length ? null : vs.viewports[i];
|
|
2294
2305
|
let end = next ? next.from - 1 : this.length;
|
|
2295
2306
|
if (end > pos) {
|
|
2296
|
-
let height = vs.
|
|
2307
|
+
let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
|
|
2297
2308
|
deco.push(Decoration.replace({ widget: new BlockGapWidget(height), block: true, inclusive: true }).range(pos, end));
|
|
2298
2309
|
}
|
|
2299
2310
|
if (!next)
|
|
@@ -2900,13 +2911,14 @@ function domPosInText(node, x, y) {
|
|
|
2900
2911
|
}
|
|
2901
2912
|
function posAtCoords(view, { x, y }, precise, bias = -1) {
|
|
2902
2913
|
var _a;
|
|
2903
|
-
let content = view.contentDOM.getBoundingClientRect(),
|
|
2914
|
+
let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop;
|
|
2904
2915
|
let halfLine = view.defaultLineHeight / 2;
|
|
2916
|
+
let block, yOffset = y - docTop;
|
|
2905
2917
|
for (let bounced = false;;) {
|
|
2906
|
-
block = view.
|
|
2907
|
-
if (block.top >
|
|
2908
|
-
bias = block.top >
|
|
2909
|
-
|
|
2918
|
+
block = view.elementAtHeight(yOffset);
|
|
2919
|
+
if (block.top > yOffset || block.bottom < yOffset) {
|
|
2920
|
+
bias = block.top > yOffset ? -1 : 1;
|
|
2921
|
+
yOffset = Math.min(block.bottom - halfLine, Math.max(block.top + halfLine, yOffset));
|
|
2910
2922
|
if (bounced)
|
|
2911
2923
|
return precise ? null : 0;
|
|
2912
2924
|
else
|
|
@@ -2914,8 +2926,9 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
|
|
|
2914
2926
|
}
|
|
2915
2927
|
if (block.type == exports.BlockType.Text)
|
|
2916
2928
|
break;
|
|
2917
|
-
|
|
2929
|
+
yOffset = bias > 0 ? block.bottom + halfLine : block.top - halfLine;
|
|
2918
2930
|
}
|
|
2931
|
+
y = docTop + yOffset;
|
|
2919
2932
|
let lineStart = block.from;
|
|
2920
2933
|
// Clip x to the viewport sides
|
|
2921
2934
|
x = Math.max(content.left + 1, Math.min(content.right - 1, x));
|
|
@@ -3028,17 +3041,17 @@ function moveVertically(view, start, forward, distance) {
|
|
|
3028
3041
|
return state.EditorSelection.cursor(startPos);
|
|
3029
3042
|
let goal = start.goalColumn, startY;
|
|
3030
3043
|
let rect = view.contentDOM.getBoundingClientRect();
|
|
3031
|
-
let startCoords = view.coordsAtPos(startPos);
|
|
3044
|
+
let startCoords = view.coordsAtPos(startPos), docTop = view.documentTop;
|
|
3032
3045
|
if (startCoords) {
|
|
3033
3046
|
if (goal == null)
|
|
3034
3047
|
goal = startCoords.left - rect.left;
|
|
3035
3048
|
startY = dir < 0 ? startCoords.top : startCoords.bottom;
|
|
3036
3049
|
}
|
|
3037
3050
|
else {
|
|
3038
|
-
let line = view.viewState.
|
|
3051
|
+
let line = view.viewState.lineBlockAt(startPos - docTop);
|
|
3039
3052
|
if (goal == null)
|
|
3040
3053
|
goal = Math.min(rect.right - rect.left, view.defaultCharacterWidth * (startPos - line.from));
|
|
3041
|
-
startY = dir < 0 ? line.top : line.bottom;
|
|
3054
|
+
startY = (dir < 0 ? line.top : line.bottom) + docTop;
|
|
3042
3055
|
}
|
|
3043
3056
|
let resolvedGoal = rect.left + goal;
|
|
3044
3057
|
let dist = distance !== null && distance !== void 0 ? distance : (view.defaultLineHeight >> 1);
|
|
@@ -3289,7 +3302,7 @@ class MouseSelection {
|
|
|
3289
3302
|
this.extend = startEvent.shiftKey;
|
|
3290
3303
|
this.multiple = view.state.facet(state.EditorState.allowMultipleSelections) && addsSelectionRange(view, startEvent);
|
|
3291
3304
|
this.dragMove = dragMovesSelection(view, startEvent);
|
|
3292
|
-
this.dragging = isInPrimarySelection(view, startEvent) ? null : false;
|
|
3305
|
+
this.dragging = isInPrimarySelection(view, startEvent) && getClickType(startEvent) == 1 ? null : false;
|
|
3293
3306
|
// When clicking outside of the selection, immediately apply the
|
|
3294
3307
|
// effect of starting the selection
|
|
3295
3308
|
if (this.dragging === false) {
|
|
@@ -3510,7 +3523,7 @@ function basicMouseSelection(view, event) {
|
|
|
3510
3523
|
let last = start, lastEvent = event;
|
|
3511
3524
|
return {
|
|
3512
3525
|
update(update) {
|
|
3513
|
-
if (update.
|
|
3526
|
+
if (update.docChanged) {
|
|
3514
3527
|
if (start)
|
|
3515
3528
|
start.pos = update.changes.mapPos(start.pos);
|
|
3516
3529
|
startSel = startSel.map(update.changes);
|
|
@@ -3774,7 +3787,10 @@ class HeightOracle {
|
|
|
3774
3787
|
return lines * this.lineHeight;
|
|
3775
3788
|
}
|
|
3776
3789
|
setDoc(doc) { this.doc = doc; return this; }
|
|
3777
|
-
|
|
3790
|
+
mustRefreshForStyle(whiteSpace, direction) {
|
|
3791
|
+
return (wrappingWhiteSpace.indexOf(whiteSpace) > -1) != this.lineWrapping || this.direction != direction;
|
|
3792
|
+
}
|
|
3793
|
+
mustRefreshForHeights(lineHeights) {
|
|
3778
3794
|
let newHeight = false;
|
|
3779
3795
|
for (let i = 0; i < lineHeights.length; i++) {
|
|
3780
3796
|
let h = lineHeights[i];
|
|
@@ -3786,7 +3802,7 @@ class HeightOracle {
|
|
|
3786
3802
|
this.heightSamples[Math.floor(h * 10)] = true;
|
|
3787
3803
|
}
|
|
3788
3804
|
}
|
|
3789
|
-
return newHeight
|
|
3805
|
+
return newHeight;
|
|
3790
3806
|
}
|
|
3791
3807
|
refresh(whiteSpace, direction, lineHeight, charWidth, lineLength, knownHeights) {
|
|
3792
3808
|
let lineWrapping = wrappingWhiteSpace.indexOf(whiteSpace) > -1;
|
|
@@ -3840,7 +3856,8 @@ class BlockInfo {
|
|
|
3840
3856
|
*/
|
|
3841
3857
|
length,
|
|
3842
3858
|
/**
|
|
3843
|
-
The top position of the element
|
|
3859
|
+
The top position of the element (relative to the top of the
|
|
3860
|
+
document).
|
|
3844
3861
|
*/
|
|
3845
3862
|
top,
|
|
3846
3863
|
/**
|
|
@@ -3874,6 +3891,12 @@ class BlockInfo {
|
|
|
3874
3891
|
.concat(Array.isArray(other.type) ? other.type : [other]);
|
|
3875
3892
|
return new BlockInfo(this.from, this.length + other.length, this.top, this.height + other.height, detail);
|
|
3876
3893
|
}
|
|
3894
|
+
/**
|
|
3895
|
+
FIXME remove on next breaking release @internal
|
|
3896
|
+
*/
|
|
3897
|
+
moveY(offset) {
|
|
3898
|
+
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);
|
|
3899
|
+
}
|
|
3877
3900
|
}
|
|
3878
3901
|
var QueryType;
|
|
3879
3902
|
(function (QueryType) {
|
|
@@ -3881,7 +3904,7 @@ var QueryType;
|
|
|
3881
3904
|
QueryType[QueryType["ByHeight"] = 1] = "ByHeight";
|
|
3882
3905
|
QueryType[QueryType["ByPosNoHeight"] = 2] = "ByPosNoHeight";
|
|
3883
3906
|
})(QueryType || (QueryType = {}));
|
|
3884
|
-
const Epsilon = 1e-
|
|
3907
|
+
const Epsilon = 1e-3;
|
|
3885
3908
|
class HeightMap {
|
|
3886
3909
|
constructor(length, // The number of characters covered
|
|
3887
3910
|
height, // Height of this part of the document
|
|
@@ -4108,22 +4131,30 @@ class HeightMapGap extends HeightMap {
|
|
|
4108
4131
|
// can't be widgets or collapsed ranges in those lines, because
|
|
4109
4132
|
// they would already have been added to the heightmap (gaps
|
|
4110
4133
|
// only contain plain text).
|
|
4111
|
-
let nodes = [], pos = Math.max(offset, measured.from);
|
|
4134
|
+
let nodes = [], pos = Math.max(offset, measured.from), singleHeight = -1;
|
|
4135
|
+
let wasChanged = oracle.heightChanged;
|
|
4112
4136
|
if (measured.from > offset)
|
|
4113
4137
|
nodes.push(new HeightMapGap(measured.from - offset - 1).updateHeight(oracle, offset));
|
|
4114
4138
|
while (pos <= end && measured.more) {
|
|
4115
4139
|
let len = oracle.doc.lineAt(pos).length;
|
|
4116
4140
|
if (nodes.length)
|
|
4117
4141
|
nodes.push(null);
|
|
4118
|
-
let
|
|
4142
|
+
let height = measured.heights[measured.index++];
|
|
4143
|
+
if (singleHeight == -1)
|
|
4144
|
+
singleHeight = height;
|
|
4145
|
+
else if (Math.abs(height - singleHeight) >= Epsilon)
|
|
4146
|
+
singleHeight = -2;
|
|
4147
|
+
let line = new HeightMapText(len, height);
|
|
4119
4148
|
line.outdated = false;
|
|
4120
4149
|
nodes.push(line);
|
|
4121
4150
|
pos += len + 1;
|
|
4122
4151
|
}
|
|
4123
4152
|
if (pos <= end)
|
|
4124
4153
|
nodes.push(null, new HeightMapGap(end - pos).updateHeight(oracle, pos));
|
|
4125
|
-
|
|
4126
|
-
|
|
4154
|
+
let result = HeightMap.of(nodes);
|
|
4155
|
+
oracle.heightChanged = wasChanged || singleHeight < 0 || Math.abs(result.height - this.height) >= Epsilon ||
|
|
4156
|
+
Math.abs(singleHeight - this.lines(oracle.doc, offset).lineHeight) >= Epsilon;
|
|
4157
|
+
return result;
|
|
4127
4158
|
}
|
|
4128
4159
|
else if (force || this.outdated) {
|
|
4129
4160
|
this.setHeight(oracle, oracle.heightForGap(offset, offset + this.length));
|
|
@@ -4481,13 +4512,18 @@ class ViewState {
|
|
|
4481
4512
|
this.inView = true;
|
|
4482
4513
|
this.paddingTop = 0;
|
|
4483
4514
|
this.paddingBottom = 0;
|
|
4484
|
-
this.
|
|
4515
|
+
this.contentDOMWidth = 0;
|
|
4516
|
+
this.contentDOMHeight = 0;
|
|
4517
|
+
this.editorHeight = 0;
|
|
4485
4518
|
this.heightOracle = new HeightOracle;
|
|
4486
4519
|
// See VP.MaxDOMHeight
|
|
4487
4520
|
this.scaler = IdScaler;
|
|
4488
4521
|
this.scrollTarget = null;
|
|
4489
4522
|
// Briefly set to true when printing, to disable viewport limiting
|
|
4490
4523
|
this.printing = false;
|
|
4524
|
+
// Flag set when editor content was redrawn, so that the next
|
|
4525
|
+
// measure stage knows it must read DOM layout
|
|
4526
|
+
this.mustMeasureContent = true;
|
|
4491
4527
|
this.visibleRanges = [];
|
|
4492
4528
|
// Cursor 'assoc' is only significant when the cursor is on a line
|
|
4493
4529
|
// wrap point, where it must stick to the character that it is
|
|
@@ -4500,6 +4536,7 @@ class ViewState {
|
|
|
4500
4536
|
this.mustEnforceCursorAssoc = false;
|
|
4501
4537
|
this.heightMap = HeightMap.empty().applyChanges(state.facet(decorations), text.Text.empty, this.heightOracle.setDoc(state.doc), [new ChangedRange(0, 0, 0, state.doc.length)]);
|
|
4502
4538
|
this.viewport = this.getViewport(0, null);
|
|
4539
|
+
this.updateViewportLines();
|
|
4503
4540
|
this.updateForViewport();
|
|
4504
4541
|
this.lineGaps = this.ensureLineGaps([]);
|
|
4505
4542
|
this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(false)));
|
|
@@ -4510,7 +4547,7 @@ class ViewState {
|
|
|
4510
4547
|
for (let i = 0; i <= 1; i++) {
|
|
4511
4548
|
let pos = i ? main.head : main.anchor;
|
|
4512
4549
|
if (!viewports.some(({ from, to }) => pos >= from && pos <= to)) {
|
|
4513
|
-
let { from, to } = this.
|
|
4550
|
+
let { from, to } = this.lineBlockAt(pos);
|
|
4514
4551
|
viewports.push(new Viewport(from, to));
|
|
4515
4552
|
}
|
|
4516
4553
|
}
|
|
@@ -4518,6 +4555,12 @@ class ViewState {
|
|
|
4518
4555
|
this.scaler = this.heightMap.height <= 7000000 /* MaxDOMHeight */ ? IdScaler :
|
|
4519
4556
|
new BigScaler(this.heightOracle.doc, this.heightMap, this.viewports);
|
|
4520
4557
|
}
|
|
4558
|
+
updateViewportLines() {
|
|
4559
|
+
this.viewportLines = [];
|
|
4560
|
+
this.heightMap.forEachLine(this.viewport.from, this.viewport.to, this.state.doc, 0, 0, block => {
|
|
4561
|
+
this.viewportLines.push(this.scaler.scale == 1 ? block : scaleBlock(block, this.scaler));
|
|
4562
|
+
});
|
|
4563
|
+
}
|
|
4521
4564
|
update(update, scrollTarget = null) {
|
|
4522
4565
|
let prev = this.state;
|
|
4523
4566
|
this.state = update.state;
|
|
@@ -4532,7 +4575,11 @@ class ViewState {
|
|
|
4532
4575
|
if (scrollTarget && (scrollTarget.range.head < viewport.from || scrollTarget.range.head > viewport.to) ||
|
|
4533
4576
|
!this.viewportIsAppropriate(viewport))
|
|
4534
4577
|
viewport = this.getViewport(0, scrollTarget);
|
|
4578
|
+
let updateLines = !update.changes.empty || (update.flags & 2 /* Height */) ||
|
|
4579
|
+
viewport.from != this.viewport.from || viewport.to != this.viewport.to;
|
|
4535
4580
|
this.viewport = viewport;
|
|
4581
|
+
if (updateLines)
|
|
4582
|
+
this.updateViewportLines();
|
|
4536
4583
|
this.updateForViewport();
|
|
4537
4584
|
if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
|
|
4538
4585
|
this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps, update.changes)));
|
|
@@ -4543,13 +4590,17 @@ class ViewState {
|
|
|
4543
4590
|
update.state.selection.main.empty && update.state.selection.main.assoc)
|
|
4544
4591
|
this.mustEnforceCursorAssoc = true;
|
|
4545
4592
|
}
|
|
4546
|
-
measure(
|
|
4547
|
-
let dom =
|
|
4548
|
-
let
|
|
4549
|
-
|
|
4593
|
+
measure(view) {
|
|
4594
|
+
let dom = view.contentDOM, style = window.getComputedStyle(dom);
|
|
4595
|
+
let oracle = this.heightOracle;
|
|
4596
|
+
let whiteSpace = style.whiteSpace, direction = style.direction == "rtl" ? exports.Direction.RTL : exports.Direction.LTR;
|
|
4597
|
+
let refresh = this.heightOracle.mustRefreshForStyle(whiteSpace, direction);
|
|
4598
|
+
let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != dom.clientHeight;
|
|
4599
|
+
let result = 0, bias = 0;
|
|
4600
|
+
if (measureContent) {
|
|
4601
|
+
this.mustMeasureContent = false;
|
|
4602
|
+
this.contentDOMHeight = dom.clientHeight;
|
|
4550
4603
|
// Vertical padding
|
|
4551
|
-
let style = window.getComputedStyle(dom);
|
|
4552
|
-
whiteSpace = style.whiteSpace, direction = (style.direction == "rtl" ? exports.Direction.RTL : exports.Direction.LTR);
|
|
4553
4604
|
let paddingTop = parseInt(style.paddingTop) || 0, paddingBottom = parseInt(style.paddingBottom) || 0;
|
|
4554
4605
|
if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) {
|
|
4555
4606
|
result |= 8 /* Geometry */;
|
|
@@ -4564,35 +4615,42 @@ class ViewState {
|
|
|
4564
4615
|
this.inView = this.pixelViewport.bottom > this.pixelViewport.top && this.pixelViewport.right > this.pixelViewport.left;
|
|
4565
4616
|
if (!this.inView)
|
|
4566
4617
|
return 0;
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
let { lineHeight, charWidth } = docView.measureTextSize();
|
|
4618
|
+
if (measureContent) {
|
|
4619
|
+
let lineHeights = view.docView.measureVisibleLineHeights();
|
|
4620
|
+
if (oracle.mustRefreshForHeights(lineHeights))
|
|
4621
|
+
refresh = true;
|
|
4622
|
+
let contentWidth = dom.clientWidth;
|
|
4623
|
+
if (refresh || oracle.lineWrapping && Math.abs(contentWidth - this.contentDOMWidth) > oracle.charWidth) {
|
|
4624
|
+
let { lineHeight, charWidth } = view.docView.measureTextSize();
|
|
4574
4625
|
refresh = oracle.refresh(whiteSpace, direction, lineHeight, charWidth, contentWidth / charWidth, lineHeights);
|
|
4575
4626
|
if (refresh) {
|
|
4576
|
-
docView.minWidth = 0;
|
|
4627
|
+
view.docView.minWidth = 0;
|
|
4577
4628
|
result |= 8 /* Geometry */;
|
|
4578
4629
|
}
|
|
4579
4630
|
}
|
|
4580
|
-
if (this.
|
|
4581
|
-
this.
|
|
4631
|
+
if (this.contentDOMWidth != contentWidth) {
|
|
4632
|
+
this.contentDOMWidth = contentWidth;
|
|
4633
|
+
result |= 8 /* Geometry */;
|
|
4634
|
+
}
|
|
4635
|
+
if (this.editorHeight != view.scrollDOM.clientHeight) {
|
|
4636
|
+
this.editorHeight = view.scrollDOM.clientHeight;
|
|
4582
4637
|
result |= 8 /* Geometry */;
|
|
4583
4638
|
}
|
|
4584
4639
|
if (dTop > 0 && dBottom > 0)
|
|
4585
4640
|
bias = Math.max(dTop, dBottom);
|
|
4586
4641
|
else if (dTop < 0 && dBottom < 0)
|
|
4587
4642
|
bias = Math.min(dTop, dBottom);
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to)
|
|
4643
|
+
oracle.heightChanged = false;
|
|
4644
|
+
this.heightMap = this.heightMap.updateHeight(oracle, 0, refresh, new MeasuredHeights(this.viewport.from, lineHeights));
|
|
4645
|
+
if (oracle.heightChanged)
|
|
4646
|
+
result |= 2 /* Height */;
|
|
4647
|
+
}
|
|
4648
|
+
let viewportChange = !this.viewportIsAppropriate(this.viewport, bias) ||
|
|
4649
|
+
this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to);
|
|
4650
|
+
if (viewportChange)
|
|
4595
4651
|
this.viewport = this.getViewport(bias, this.scrollTarget);
|
|
4652
|
+
if ((result & 2 /* Height */) || viewportChange)
|
|
4653
|
+
this.updateViewportLines();
|
|
4596
4654
|
this.updateForViewport();
|
|
4597
4655
|
if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
|
|
4598
4656
|
this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps));
|
|
@@ -4603,12 +4661,12 @@ class ViewState {
|
|
|
4603
4661
|
// to a line end is going to trigger a layout anyway, so it
|
|
4604
4662
|
// can't be a pure write. It should be rare that it does any
|
|
4605
4663
|
// writing.
|
|
4606
|
-
docView.enforceCursorAssoc();
|
|
4664
|
+
view.docView.enforceCursorAssoc();
|
|
4607
4665
|
}
|
|
4608
4666
|
return result;
|
|
4609
4667
|
}
|
|
4610
|
-
get visibleTop() { return this.scaler.fromDOM(this.pixelViewport.top
|
|
4611
|
-
get visibleBottom() { return this.scaler.fromDOM(this.pixelViewport.bottom
|
|
4668
|
+
get visibleTop() { return this.scaler.fromDOM(this.pixelViewport.top); }
|
|
4669
|
+
get visibleBottom() { return this.scaler.fromDOM(this.pixelViewport.bottom); }
|
|
4612
4670
|
getViewport(bias, scrollTarget) {
|
|
4613
4671
|
// This will divide VP.Margin between the top and the
|
|
4614
4672
|
// bottom, depending on the bias (the change in viewport position
|
|
@@ -4668,12 +4726,12 @@ class ViewState {
|
|
|
4668
4726
|
// This won't work at all in predominantly right-to-left text.
|
|
4669
4727
|
if (this.heightOracle.direction != exports.Direction.LTR)
|
|
4670
4728
|
return gaps;
|
|
4671
|
-
|
|
4729
|
+
for (let line of this.viewportLines) {
|
|
4672
4730
|
if (line.length < 4000 /* DoubleMargin */)
|
|
4673
|
-
|
|
4731
|
+
continue;
|
|
4674
4732
|
let structure = lineStructure(line.from, line.to, this.state);
|
|
4675
4733
|
if (structure.total < 4000 /* DoubleMargin */)
|
|
4676
|
-
|
|
4734
|
+
continue;
|
|
4677
4735
|
let viewFrom, viewTo;
|
|
4678
4736
|
if (this.heightOracle.lineWrapping) {
|
|
4679
4737
|
let marginHeight = (2000 /* Margin */ / this.heightOracle.lineLength) * this.heightOracle.lineHeight;
|
|
@@ -4703,7 +4761,7 @@ class ViewState {
|
|
|
4703
4761
|
Math.abs(gap.from - from) < 1000 /* HalfMargin */ && Math.abs(gap.to - to) < 1000 /* HalfMargin */) ||
|
|
4704
4762
|
new LineGap(from, to, this.gapSize(line, from, to, structure)));
|
|
4705
4763
|
}
|
|
4706
|
-
}
|
|
4764
|
+
}
|
|
4707
4765
|
return gaps;
|
|
4708
4766
|
}
|
|
4709
4767
|
gapSize(line, from, to, structure) {
|
|
@@ -4735,27 +4793,18 @@ class ViewState {
|
|
|
4735
4793
|
this.visibleRanges = ranges;
|
|
4736
4794
|
return changed ? 4 /* Viewport */ : 0;
|
|
4737
4795
|
}
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
}
|
|
4742
|
-
lineAtHeight(height, editorTop) {
|
|
4743
|
-
editorTop += this.paddingTop;
|
|
4744
|
-
return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height, editorTop), QueryType.ByHeight, this.state.doc, editorTop, 0), this.scaler, editorTop);
|
|
4796
|
+
lineBlockAt(pos) {
|
|
4797
|
+
return (pos >= this.viewport.from && pos <= this.viewport.to && this.viewportLines.find(b => b.from <= pos && b.to <= pos)) ||
|
|
4798
|
+
scaleBlock(this.heightMap.lineAt(pos, QueryType.ByPos, this.state.doc, 0, 0), this.scaler);
|
|
4745
4799
|
}
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height, editorTop), this.state.doc, editorTop, 0), this.scaler, editorTop);
|
|
4800
|
+
lineBlockAtHeight(height) {
|
|
4801
|
+
return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.state.doc, 0, 0), this.scaler);
|
|
4749
4802
|
}
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
return this.heightMap.forEachLine(from, to, this.state.doc, editorTop, 0, this.scaler.scale == 1 ? f : b => f(scaleBlock(b, this.scaler, editorTop)));
|
|
4803
|
+
elementAtHeight(height) {
|
|
4804
|
+
return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.state.doc, 0, 0), this.scaler);
|
|
4753
4805
|
}
|
|
4754
4806
|
get contentHeight() {
|
|
4755
|
-
return this.
|
|
4756
|
-
}
|
|
4757
|
-
get domHeight() {
|
|
4758
|
-
return this.scaler.toDOM(this.heightMap.height, this.paddingTop);
|
|
4807
|
+
return this.scaler.toDOM(this.heightMap.height) + this.paddingTop + this.paddingBottom;
|
|
4759
4808
|
}
|
|
4760
4809
|
}
|
|
4761
4810
|
class Viewport {
|
|
@@ -4852,36 +4901,34 @@ class BigScaler {
|
|
|
4852
4901
|
base = obj.bottom;
|
|
4853
4902
|
}
|
|
4854
4903
|
}
|
|
4855
|
-
toDOM(n
|
|
4856
|
-
n -= top;
|
|
4904
|
+
toDOM(n) {
|
|
4857
4905
|
for (let i = 0, base = 0, domBase = 0;; i++) {
|
|
4858
4906
|
let vp = i < this.viewports.length ? this.viewports[i] : null;
|
|
4859
4907
|
if (!vp || n < vp.top)
|
|
4860
|
-
return domBase + (n - base) * this.scale
|
|
4908
|
+
return domBase + (n - base) * this.scale;
|
|
4861
4909
|
if (n <= vp.bottom)
|
|
4862
|
-
return vp.domTop + (n - vp.top)
|
|
4910
|
+
return vp.domTop + (n - vp.top);
|
|
4863
4911
|
base = vp.bottom;
|
|
4864
4912
|
domBase = vp.domBottom;
|
|
4865
4913
|
}
|
|
4866
4914
|
}
|
|
4867
|
-
fromDOM(n
|
|
4868
|
-
n -= top;
|
|
4915
|
+
fromDOM(n) {
|
|
4869
4916
|
for (let i = 0, base = 0, domBase = 0;; i++) {
|
|
4870
4917
|
let vp = i < this.viewports.length ? this.viewports[i] : null;
|
|
4871
4918
|
if (!vp || n < vp.domTop)
|
|
4872
|
-
return base + (n - domBase) / this.scale
|
|
4919
|
+
return base + (n - domBase) / this.scale;
|
|
4873
4920
|
if (n <= vp.domBottom)
|
|
4874
|
-
return vp.top + (n - vp.domTop)
|
|
4921
|
+
return vp.top + (n - vp.domTop);
|
|
4875
4922
|
base = vp.bottom;
|
|
4876
4923
|
domBase = vp.domBottom;
|
|
4877
4924
|
}
|
|
4878
4925
|
}
|
|
4879
4926
|
}
|
|
4880
|
-
function scaleBlock(block, scaler
|
|
4927
|
+
function scaleBlock(block, scaler) {
|
|
4881
4928
|
if (scaler.scale == 1)
|
|
4882
4929
|
return block;
|
|
4883
|
-
let bTop = scaler.toDOM(block.top
|
|
4884
|
-
return new BlockInfo(block.from, block.length, bTop, bBottom - bTop, Array.isArray(block.type) ? block.type.map(b => scaleBlock(b, scaler
|
|
4930
|
+
let bTop = scaler.toDOM(block.top), bBottom = scaler.toDOM(block.bottom);
|
|
4931
|
+
return new BlockInfo(block.from, block.length, bTop, bBottom - bTop, Array.isArray(block.type) ? block.type.map(b => scaleBlock(b, scaler)) : block.type);
|
|
4885
4932
|
}
|
|
4886
4933
|
|
|
4887
4934
|
const theme = state.Facet.define({ combine: strs => strs.join(" ") });
|
|
@@ -5066,24 +5113,30 @@ class DOMObserver {
|
|
|
5066
5113
|
this.onChange = onChange;
|
|
5067
5114
|
this.onScrollChanged = onScrollChanged;
|
|
5068
5115
|
this.active = false;
|
|
5069
|
-
|
|
5116
|
+
// The known selection. Kept in our own object, as opposed to just
|
|
5117
|
+
// directly accessing the selection because:
|
|
5118
|
+
// - Safari doesn't report the right selection in shadow DOM
|
|
5119
|
+
// - Reading from the selection forces a DOM layout
|
|
5120
|
+
// - This way, we can ignore selectionchange events if we have
|
|
5121
|
+
// already seen the 'new' selection
|
|
5122
|
+
this.selectionRange = new DOMSelectionState;
|
|
5123
|
+
// Set when a selection change is detected, cleared on flush
|
|
5124
|
+
this.selectionChanged = false;
|
|
5070
5125
|
this.delayedFlush = -1;
|
|
5126
|
+
this.resizeTimeout = -1;
|
|
5071
5127
|
this.queue = [];
|
|
5072
|
-
this.lastFlush = 0;
|
|
5073
5128
|
this.scrollTargets = [];
|
|
5074
5129
|
this.intersection = null;
|
|
5130
|
+
this.resize = null;
|
|
5075
5131
|
this.intersecting = false;
|
|
5076
5132
|
this.gapIntersection = null;
|
|
5077
5133
|
this.gaps = [];
|
|
5078
|
-
// Used to work around a Safari Selection/shadow DOM bug (#414)
|
|
5079
|
-
this._selectionRange = null;
|
|
5080
5134
|
// Timeout for scheduling check of the parents that need scroll handlers
|
|
5081
5135
|
this.parentCheck = -1;
|
|
5082
5136
|
this.dom = view.contentDOM;
|
|
5083
5137
|
this.observer = new MutationObserver(mutations => {
|
|
5084
5138
|
for (let mut of mutations)
|
|
5085
5139
|
this.queue.push(mut);
|
|
5086
|
-
this._selectionRange = null;
|
|
5087
5140
|
// IE11 will sometimes (on typing over a selection or
|
|
5088
5141
|
// backspacing out a single character text node) call the
|
|
5089
5142
|
// observer callback before actually updating the DOM.
|
|
@@ -5108,6 +5161,16 @@ class DOMObserver {
|
|
|
5108
5161
|
this.flushSoon();
|
|
5109
5162
|
};
|
|
5110
5163
|
this.onSelectionChange = this.onSelectionChange.bind(this);
|
|
5164
|
+
if (typeof ResizeObserver == "function") {
|
|
5165
|
+
this.resize = new ResizeObserver(() => {
|
|
5166
|
+
if (this.view.docView.lastUpdate < Date.now() - 75 && this.resizeTimeout < 0)
|
|
5167
|
+
this.resizeTimeout = setTimeout(() => {
|
|
5168
|
+
this.resizeTimeout = -1;
|
|
5169
|
+
this.view.requestMeasure();
|
|
5170
|
+
}, 50);
|
|
5171
|
+
});
|
|
5172
|
+
this.resize.observe(view.scrollDOM);
|
|
5173
|
+
}
|
|
5111
5174
|
this.start();
|
|
5112
5175
|
this.onScroll = this.onScroll.bind(this);
|
|
5113
5176
|
window.addEventListener("scroll", this.onScroll);
|
|
@@ -5128,10 +5191,12 @@ class DOMObserver {
|
|
|
5128
5191
|
}, {});
|
|
5129
5192
|
}
|
|
5130
5193
|
this.listenForScroll();
|
|
5194
|
+
this.readSelectionRange();
|
|
5195
|
+
this.dom.ownerDocument.addEventListener("selectionchange", this.onSelectionChange);
|
|
5131
5196
|
}
|
|
5132
5197
|
onScroll(e) {
|
|
5133
5198
|
if (this.intersecting)
|
|
5134
|
-
this.flush();
|
|
5199
|
+
this.flush(false);
|
|
5135
5200
|
this.onScrollChanged(e);
|
|
5136
5201
|
}
|
|
5137
5202
|
updateGaps(gaps) {
|
|
@@ -5143,8 +5208,8 @@ class DOMObserver {
|
|
|
5143
5208
|
}
|
|
5144
5209
|
}
|
|
5145
5210
|
onSelectionChange(event) {
|
|
5146
|
-
if (this.
|
|
5147
|
-
|
|
5211
|
+
if (!this.readSelectionRange())
|
|
5212
|
+
return;
|
|
5148
5213
|
let { view } = this, sel = this.selectionRange;
|
|
5149
5214
|
if (view.state.facet(editable) ? view.root.activeElement != this.dom : !hasSelection(view.dom, sel))
|
|
5150
5215
|
return;
|
|
@@ -5159,24 +5224,22 @@ class DOMObserver {
|
|
|
5159
5224
|
sel.focusNode && isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset))
|
|
5160
5225
|
this.flushSoon();
|
|
5161
5226
|
else
|
|
5162
|
-
this.flush();
|
|
5163
|
-
}
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
return this.
|
|
5227
|
+
this.flush(false);
|
|
5228
|
+
}
|
|
5229
|
+
readSelectionRange() {
|
|
5230
|
+
let { root } = this.view, domSel = getSelection(root);
|
|
5231
|
+
// The Selection object is broken in shadow roots in Safari. See
|
|
5232
|
+
// https://github.com/codemirror/codemirror.next/issues/414
|
|
5233
|
+
let range = browser.safari && root.nodeType == 11 && deepActiveElement() == this.view.contentDOM &&
|
|
5234
|
+
safariSelectionRangeHack(this.view) || domSel;
|
|
5235
|
+
if (this.selectionRange.eq(range))
|
|
5236
|
+
return false;
|
|
5237
|
+
this.selectionRange.setRange(range);
|
|
5238
|
+
return this.selectionChanged = true;
|
|
5174
5239
|
}
|
|
5175
5240
|
setSelectionRange(anchor, head) {
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
this._selectionRange = { anchorNode: anchor.node, anchorOffset: anchor.offset,
|
|
5179
|
-
focusNode: head.node, focusOffset: head.offset };
|
|
5241
|
+
this.selectionRange.set(anchor.node, anchor.offset, head.node, head.offset);
|
|
5242
|
+
this.selectionChanged = false;
|
|
5180
5243
|
}
|
|
5181
5244
|
listenForScroll() {
|
|
5182
5245
|
this.parentCheck = -1;
|
|
@@ -5223,7 +5286,6 @@ class DOMObserver {
|
|
|
5223
5286
|
if (this.active)
|
|
5224
5287
|
return;
|
|
5225
5288
|
this.observer.observe(this.dom, observeOptions);
|
|
5226
|
-
this.dom.ownerDocument.addEventListener("selectionchange", this.onSelectionChange);
|
|
5227
5289
|
if (useCharData)
|
|
5228
5290
|
this.dom.addEventListener("DOMCharacterDataModified", this.onCharData);
|
|
5229
5291
|
this.active = true;
|
|
@@ -5233,18 +5295,14 @@ class DOMObserver {
|
|
|
5233
5295
|
return;
|
|
5234
5296
|
this.active = false;
|
|
5235
5297
|
this.observer.disconnect();
|
|
5236
|
-
this.dom.ownerDocument.removeEventListener("selectionchange", this.onSelectionChange);
|
|
5237
5298
|
if (useCharData)
|
|
5238
5299
|
this.dom.removeEventListener("DOMCharacterDataModified", this.onCharData);
|
|
5239
5300
|
}
|
|
5240
|
-
clearSelection() {
|
|
5241
|
-
this.ignoreSelection.set(this.selectionRange);
|
|
5242
|
-
}
|
|
5243
5301
|
// Throw away any pending changes
|
|
5244
5302
|
clear() {
|
|
5245
5303
|
this.observer.takeRecords();
|
|
5246
5304
|
this.queue.length = 0;
|
|
5247
|
-
this.
|
|
5305
|
+
this.selectionChanged = false;
|
|
5248
5306
|
}
|
|
5249
5307
|
flushSoon() {
|
|
5250
5308
|
if (this.delayedFlush < 0)
|
|
@@ -5281,24 +5339,24 @@ class DOMObserver {
|
|
|
5281
5339
|
return { from, to, typeOver };
|
|
5282
5340
|
}
|
|
5283
5341
|
// Apply pending changes, if any
|
|
5284
|
-
flush() {
|
|
5342
|
+
flush(readSelection = true) {
|
|
5343
|
+
if (readSelection)
|
|
5344
|
+
this.readSelectionRange();
|
|
5285
5345
|
// Completely hold off flushing when pending keys are set—the code
|
|
5286
5346
|
// managing those will make sure processRecords is called and the
|
|
5287
5347
|
// view is resynchronized after
|
|
5288
5348
|
if (this.delayedFlush >= 0 || this.view.inputState.pendingAndroidKey)
|
|
5289
5349
|
return;
|
|
5290
|
-
this.lastFlush = Date.now();
|
|
5291
5350
|
let { from, to, typeOver } = this.processRecords();
|
|
5292
|
-
let
|
|
5293
|
-
let newSel = !this.ignoreSelection.eq(selection) && hasSelection(this.dom, selection);
|
|
5351
|
+
let newSel = this.selectionChanged && hasSelection(this.dom, this.selectionRange);
|
|
5294
5352
|
if (from < 0 && !newSel)
|
|
5295
5353
|
return;
|
|
5354
|
+
this.selectionChanged = false;
|
|
5296
5355
|
let startState = this.view.state;
|
|
5297
5356
|
this.onChange(from, to, typeOver);
|
|
5298
5357
|
// The view wasn't updated
|
|
5299
5358
|
if (this.view.state == startState)
|
|
5300
5359
|
this.view.docView.reset(newSel);
|
|
5301
|
-
this.clearSelection();
|
|
5302
5360
|
}
|
|
5303
5361
|
readMutation(rec) {
|
|
5304
5362
|
let cView = this.view.docView.nearest(rec.target);
|
|
@@ -5321,15 +5379,16 @@ class DOMObserver {
|
|
|
5321
5379
|
}
|
|
5322
5380
|
}
|
|
5323
5381
|
destroy() {
|
|
5382
|
+
var _a, _b, _c;
|
|
5324
5383
|
this.stop();
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
this.gapIntersection.disconnect();
|
|
5384
|
+
(_a = this.intersection) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
5385
|
+
(_b = this.gapIntersection) === null || _b === void 0 ? void 0 : _b.disconnect();
|
|
5386
|
+
(_c = this.resize) === null || _c === void 0 ? void 0 : _c.disconnect();
|
|
5329
5387
|
for (let dom of this.scrollTargets)
|
|
5330
5388
|
dom.removeEventListener("scroll", this.onScroll);
|
|
5331
5389
|
window.removeEventListener("scroll", this.onScroll);
|
|
5332
5390
|
clearTimeout(this.parentCheck);
|
|
5391
|
+
clearTimeout(this.resizeTimeout);
|
|
5333
5392
|
}
|
|
5334
5393
|
}
|
|
5335
5394
|
function findChild(cView, dom, dir) {
|
|
@@ -5342,6 +5401,7 @@ function findChild(cView, dom, dir) {
|
|
|
5342
5401
|
}
|
|
5343
5402
|
return null;
|
|
5344
5403
|
}
|
|
5404
|
+
// Used to work around a Safari Selection/shadow DOM bug (#414)
|
|
5345
5405
|
function safariSelectionRangeHack(view) {
|
|
5346
5406
|
let found = null;
|
|
5347
5407
|
// Because Safari (at least in 2018-2021) doesn't provide regular
|
|
@@ -5761,6 +5821,7 @@ class EditorView {
|
|
|
5761
5821
|
this.mountStyles();
|
|
5762
5822
|
this.updateAttrs();
|
|
5763
5823
|
this.showAnnouncements(transactions);
|
|
5824
|
+
this.docView.updateSelection(redrawn, transactions.some(tr => tr.isUserEvent("select.pointer")));
|
|
5764
5825
|
}
|
|
5765
5826
|
finally {
|
|
5766
5827
|
this.updateState = 0 /* Idle */;
|
|
@@ -5838,7 +5899,7 @@ class EditorView {
|
|
|
5838
5899
|
return;
|
|
5839
5900
|
if (this.measureScheduled > -1)
|
|
5840
5901
|
cancelAnimationFrame(this.measureScheduled);
|
|
5841
|
-
this.measureScheduled =
|
|
5902
|
+
this.measureScheduled = 0; // Prevent requestMeasure calls from scheduling another animation frame
|
|
5842
5903
|
if (flush)
|
|
5843
5904
|
this.observer.flush();
|
|
5844
5905
|
let updated = null;
|
|
@@ -5846,11 +5907,11 @@ class EditorView {
|
|
|
5846
5907
|
for (let i = 0;; i++) {
|
|
5847
5908
|
this.updateState = 1 /* Measuring */;
|
|
5848
5909
|
let oldViewport = this.viewport;
|
|
5849
|
-
let changed = this.viewState.measure(this
|
|
5910
|
+
let changed = this.viewState.measure(this);
|
|
5850
5911
|
if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
|
|
5851
5912
|
break;
|
|
5852
5913
|
if (i > 5) {
|
|
5853
|
-
console.warn("Viewport failed to stabilize");
|
|
5914
|
+
console.warn(this.measureRequests.length ? "Measure loop restarted more than 5 times" : "Viewport failed to stabilize");
|
|
5854
5915
|
break;
|
|
5855
5916
|
}
|
|
5856
5917
|
let measuring = [];
|
|
@@ -5866,7 +5927,7 @@ class EditorView {
|
|
|
5866
5927
|
return BadMeasure;
|
|
5867
5928
|
}
|
|
5868
5929
|
});
|
|
5869
|
-
let update = new ViewUpdate(this, this.state);
|
|
5930
|
+
let update = new ViewUpdate(this, this.state), redrawn = false;
|
|
5870
5931
|
update.flags |= changed;
|
|
5871
5932
|
if (!updated)
|
|
5872
5933
|
updated = update;
|
|
@@ -5876,14 +5937,15 @@ class EditorView {
|
|
|
5876
5937
|
if (!update.empty) {
|
|
5877
5938
|
this.updatePlugins(update);
|
|
5878
5939
|
this.inputState.update(update);
|
|
5940
|
+
this.updateAttrs();
|
|
5941
|
+
redrawn = this.docView.update(update);
|
|
5879
5942
|
}
|
|
5880
|
-
this.updateAttrs();
|
|
5881
|
-
if (changed)
|
|
5882
|
-
this.docView.update(update);
|
|
5883
5943
|
for (let i = 0; i < measuring.length; i++)
|
|
5884
5944
|
if (measured[i] != BadMeasure) {
|
|
5885
5945
|
try {
|
|
5886
|
-
measuring[i]
|
|
5946
|
+
let m = measuring[i];
|
|
5947
|
+
if (m.write)
|
|
5948
|
+
m.write(measured[i], this);
|
|
5887
5949
|
}
|
|
5888
5950
|
catch (e) {
|
|
5889
5951
|
logException(this.state, e);
|
|
@@ -5893,14 +5955,16 @@ class EditorView {
|
|
|
5893
5955
|
this.docView.scrollIntoView(this.viewState.scrollTarget);
|
|
5894
5956
|
this.viewState.scrollTarget = null;
|
|
5895
5957
|
}
|
|
5958
|
+
if (redrawn)
|
|
5959
|
+
this.docView.updateSelection(true);
|
|
5896
5960
|
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to && this.measureRequests.length == 0)
|
|
5897
5961
|
break;
|
|
5898
5962
|
}
|
|
5899
5963
|
}
|
|
5900
5964
|
finally {
|
|
5901
5965
|
this.updateState = 0 /* Idle */;
|
|
5966
|
+
this.measureScheduled = -1;
|
|
5902
5967
|
}
|
|
5903
|
-
this.measureScheduled = -1;
|
|
5904
5968
|
if (updated && !updated.empty)
|
|
5905
5969
|
for (let listener of this.state.facet(updateListener))
|
|
5906
5970
|
listener(updated);
|
|
@@ -5917,8 +5981,6 @@ class EditorView {
|
|
|
5917
5981
|
let editorAttrs = combineAttrs(this.state.facet(editorAttributes), {
|
|
5918
5982
|
class: "cm-editor" + (this.hasFocus ? " cm-focused " : " ") + this.themeClasses
|
|
5919
5983
|
});
|
|
5920
|
-
updateAttrs(this.dom, this.editorAttrs, editorAttrs);
|
|
5921
|
-
this.editorAttrs = editorAttrs;
|
|
5922
5984
|
let contentAttrs = {
|
|
5923
5985
|
spellcheck: "false",
|
|
5924
5986
|
autocorrect: "off",
|
|
@@ -5933,7 +5995,11 @@ class EditorView {
|
|
|
5933
5995
|
if (this.state.readOnly)
|
|
5934
5996
|
contentAttrs["aria-readonly"] = "true";
|
|
5935
5997
|
combineAttrs(this.state.facet(contentAttributes), contentAttrs);
|
|
5936
|
-
|
|
5998
|
+
this.observer.ignore(() => {
|
|
5999
|
+
updateAttrs(this.contentDOM, this.contentAttrs, contentAttrs);
|
|
6000
|
+
updateAttrs(this.dom, this.editorAttrs, editorAttrs);
|
|
6001
|
+
});
|
|
6002
|
+
this.editorAttrs = editorAttrs;
|
|
5937
6003
|
this.contentAttrs = contentAttrs;
|
|
5938
6004
|
}
|
|
5939
6005
|
showAnnouncements(trs) {
|
|
@@ -6003,6 +6069,20 @@ class EditorView {
|
|
|
6003
6069
|
return null;
|
|
6004
6070
|
}
|
|
6005
6071
|
/**
|
|
6072
|
+
The top position of the document, in screen coordinates. This
|
|
6073
|
+
may be negative when the editor is scrolled down. Points
|
|
6074
|
+
directly to the top of the first line, not above the padding.
|
|
6075
|
+
*/
|
|
6076
|
+
get documentTop() {
|
|
6077
|
+
return this.contentDOM.getBoundingClientRect().top + this.viewState.paddingTop;
|
|
6078
|
+
}
|
|
6079
|
+
/**
|
|
6080
|
+
Reports the padding above and below the document.
|
|
6081
|
+
*/
|
|
6082
|
+
get documentPadding() {
|
|
6083
|
+
return { top: this.viewState.paddingTop, bottom: this.viewState.paddingBottom };
|
|
6084
|
+
}
|
|
6085
|
+
/**
|
|
6006
6086
|
Find the line or block widget at the given vertical position.
|
|
6007
6087
|
|
|
6008
6088
|
By default, this position is interpreted as a screen position,
|
|
@@ -6012,10 +6092,21 @@ class EditorView {
|
|
|
6012
6092
|
position, or a precomputed document top
|
|
6013
6093
|
(`view.contentDOM.getBoundingClientRect().top`) to limit layout
|
|
6014
6094
|
queries.
|
|
6095
|
+
|
|
6096
|
+
*Deprecated: use `blockAtHeight` instead.*
|
|
6015
6097
|
*/
|
|
6016
6098
|
blockAtHeight(height, docTop) {
|
|
6099
|
+
let top = ensureTop(docTop, this);
|
|
6100
|
+
return this.elementAtHeight(height - top).moveY(top);
|
|
6101
|
+
}
|
|
6102
|
+
/**
|
|
6103
|
+
Find the text line or block widget at the given vertical
|
|
6104
|
+
position (which is interpreted as relative to the [top of the
|
|
6105
|
+
document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)
|
|
6106
|
+
*/
|
|
6107
|
+
elementAtHeight(height) {
|
|
6017
6108
|
this.readMeasured();
|
|
6018
|
-
return this.viewState.
|
|
6109
|
+
return this.viewState.elementAtHeight(height);
|
|
6019
6110
|
}
|
|
6020
6111
|
/**
|
|
6021
6112
|
Find information for the visual line (see
|
|
@@ -6027,20 +6118,43 @@ class EditorView {
|
|
|
6027
6118
|
Defaults to treating `height` as a screen position. See
|
|
6028
6119
|
[`blockAtHeight`](https://codemirror.net/6/docs/ref/#view.EditorView.blockAtHeight) for the
|
|
6029
6120
|
interpretation of the `docTop` parameter.
|
|
6121
|
+
|
|
6122
|
+
*Deprecated: use `lineBlockAtHeight` instead.*
|
|
6030
6123
|
*/
|
|
6031
6124
|
visualLineAtHeight(height, docTop) {
|
|
6125
|
+
let top = ensureTop(docTop, this);
|
|
6126
|
+
return this.lineBlockAtHeight(height - top).moveY(top);
|
|
6127
|
+
}
|
|
6128
|
+
/**
|
|
6129
|
+
Find the line block (see
|
|
6130
|
+
[`lineBlockAt`](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) at the given
|
|
6131
|
+
height.
|
|
6132
|
+
*/
|
|
6133
|
+
lineBlockAtHeight(height) {
|
|
6032
6134
|
this.readMeasured();
|
|
6033
|
-
return this.viewState.
|
|
6135
|
+
return this.viewState.lineBlockAtHeight(height);
|
|
6034
6136
|
}
|
|
6035
6137
|
/**
|
|
6036
6138
|
Iterate over the height information of the visual lines in the
|
|
6037
6139
|
viewport. The heights of lines are reported relative to the
|
|
6038
6140
|
given document top, which defaults to the screen position of the
|
|
6039
6141
|
document (forcing a layout).
|
|
6142
|
+
|
|
6143
|
+
*Deprecated: use `viewportLineBlocks` instead.*
|
|
6040
6144
|
*/
|
|
6041
6145
|
viewportLines(f, docTop) {
|
|
6042
|
-
let
|
|
6043
|
-
|
|
6146
|
+
let top = ensureTop(docTop, this);
|
|
6147
|
+
for (let line of this.viewportLineBlocks)
|
|
6148
|
+
f(line.moveY(top));
|
|
6149
|
+
}
|
|
6150
|
+
/**
|
|
6151
|
+
Get the extent and vertical position of all [line
|
|
6152
|
+
blocks](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) in the viewport. Positions
|
|
6153
|
+
are relative to the [top of the
|
|
6154
|
+
document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop);
|
|
6155
|
+
*/
|
|
6156
|
+
get viewportLineBlocks() {
|
|
6157
|
+
return this.viewState.viewportLines;
|
|
6044
6158
|
}
|
|
6045
6159
|
/**
|
|
6046
6160
|
Find the extent and height of the visual line (a range delimited
|
|
@@ -6051,9 +6165,22 @@ class EditorView {
|
|
|
6051
6165
|
argument, which defaults to 0 for this method. You can pass
|
|
6052
6166
|
`view.contentDOM.getBoundingClientRect().top` here to get screen
|
|
6053
6167
|
coordinates.
|
|
6168
|
+
|
|
6169
|
+
*Deprecated: use `lineBlockAt` instead.*
|
|
6054
6170
|
*/
|
|
6055
6171
|
visualLineAt(pos, docTop = 0) {
|
|
6056
|
-
return this.
|
|
6172
|
+
return this.lineBlockAt(pos).moveY(docTop + this.viewState.paddingTop);
|
|
6173
|
+
}
|
|
6174
|
+
/**
|
|
6175
|
+
Find the line block around the given document position. A line
|
|
6176
|
+
block is a range delimited on both sides by either a
|
|
6177
|
+
non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^range) line breaks, or the
|
|
6178
|
+
start/end of the document. It will usually just hold a line of
|
|
6179
|
+
text, but may be broken into multiple textblocks by block
|
|
6180
|
+
widgets.
|
|
6181
|
+
*/
|
|
6182
|
+
lineBlockAt(pos) {
|
|
6183
|
+
return this.viewState.lineBlockAt(pos);
|
|
6057
6184
|
}
|
|
6058
6185
|
/**
|
|
6059
6186
|
The editor's total content height.
|
|
@@ -6386,8 +6513,9 @@ search match).
|
|
|
6386
6513
|
EditorView.announce = state.StateEffect.define();
|
|
6387
6514
|
// Maximum line length for which we compute accurate bidi info
|
|
6388
6515
|
const MaxBidiLine = 4096;
|
|
6389
|
-
|
|
6390
|
-
|
|
6516
|
+
// FIXME remove this and its callers on next breaking release
|
|
6517
|
+
function ensureTop(given, view) {
|
|
6518
|
+
return (given == null ? view.contentDOM.getBoundingClientRect().top : given) + view.viewState.paddingTop;
|
|
6391
6519
|
}
|
|
6392
6520
|
let resizeDebounce = -1;
|
|
6393
6521
|
function ensureGlobalHandler() {
|
|
@@ -6738,7 +6866,7 @@ function wrappedLine(view, pos, inside) {
|
|
|
6738
6866
|
type: exports.BlockType.Text };
|
|
6739
6867
|
}
|
|
6740
6868
|
function blockAt(view, pos) {
|
|
6741
|
-
let line = view.
|
|
6869
|
+
let line = view.lineBlockAt(pos);
|
|
6742
6870
|
if (Array.isArray(line.type))
|
|
6743
6871
|
for (let l of line.type) {
|
|
6744
6872
|
if (l.to > pos || l.to == pos && (l.to == line.to || l.type == exports.BlockType.Text))
|
|
@@ -7129,7 +7257,7 @@ const activeLineHighlighter = ViewPlugin.fromClass(class {
|
|
|
7129
7257
|
for (let r of view.state.selection.ranges) {
|
|
7130
7258
|
if (!r.empty)
|
|
7131
7259
|
return Decoration.none;
|
|
7132
|
-
let line = view.
|
|
7260
|
+
let line = view.lineBlockAt(r.head);
|
|
7133
7261
|
if (line.from > lastLineStart) {
|
|
7134
7262
|
deco.push(lineDeco.range(line.from));
|
|
7135
7263
|
lastLineStart = line.from;
|