@codemirror/view 0.19.18 → 0.19.22
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 +34 -0
- package/dist/index.cjs +289 -163
- package/dist/index.d.ts +44 -2
- package/dist/index.js +289 -163
- 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;
|
|
@@ -1767,11 +1770,17 @@ class PluginInstance {
|
|
|
1767
1770
|
}
|
|
1768
1771
|
}
|
|
1769
1772
|
PluginInstance.dummy = /*@__PURE__*/new PluginInstance(/*@__PURE__*/ViewPlugin.define(() => ({})));
|
|
1773
|
+
function combineFacetAttrs(values) {
|
|
1774
|
+
let result = {};
|
|
1775
|
+
for (let i = values.length - 1; i >= 0; i--)
|
|
1776
|
+
combineAttrs(values[i], result);
|
|
1777
|
+
return result;
|
|
1778
|
+
}
|
|
1770
1779
|
const editorAttributes = /*@__PURE__*/Facet.define({
|
|
1771
|
-
combine:
|
|
1780
|
+
combine: combineFacetAttrs
|
|
1772
1781
|
});
|
|
1773
1782
|
const contentAttributes = /*@__PURE__*/Facet.define({
|
|
1774
|
-
combine:
|
|
1783
|
+
combine: combineFacetAttrs
|
|
1775
1784
|
});
|
|
1776
1785
|
// Provide decorations
|
|
1777
1786
|
const decorations = /*@__PURE__*/Facet.define();
|
|
@@ -1933,6 +1942,10 @@ class DocView extends ContentView {
|
|
|
1933
1942
|
// we don't mess it up when reading it back it
|
|
1934
1943
|
this.impreciseAnchor = null;
|
|
1935
1944
|
this.impreciseHead = null;
|
|
1945
|
+
this.forceSelection = false;
|
|
1946
|
+
// Used by the resize observer to ignore resizes that we caused
|
|
1947
|
+
// ourselves
|
|
1948
|
+
this.lastUpdate = Date.now();
|
|
1936
1949
|
this.setDOM(view.contentDOM);
|
|
1937
1950
|
this.children = [new LineView];
|
|
1938
1951
|
this.children[0].setParent(this);
|
|
@@ -1965,21 +1978,19 @@ class DocView extends ContentView {
|
|
|
1965
1978
|
// getSelection than the one that it actually shows to the user.
|
|
1966
1979
|
// This forces a selection update when lines are joined to work
|
|
1967
1980
|
// around that. Issue #54
|
|
1968
|
-
|
|
1969
|
-
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;
|
|
1970
1984
|
let prevDeco = this.decorations, deco = this.updateDeco();
|
|
1971
1985
|
let decoDiff = findChangedDeco(prevDeco, deco, update.changes);
|
|
1972
1986
|
changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
|
|
1973
|
-
|
|
1974
|
-
if (this.dirty == 0 /* Not */ && changedRanges.length == 0 &&
|
|
1975
|
-
!(update.flags & 4 /* Viewport */) &&
|
|
1976
|
-
update.state.selection.main.from >= this.view.viewport.from &&
|
|
1977
|
-
update.state.selection.main.to <= this.view.viewport.to) {
|
|
1978
|
-
this.updateSelection(forceSelection, pointerSel);
|
|
1987
|
+
if (this.dirty == 0 /* Not */ && changedRanges.length == 0) {
|
|
1979
1988
|
return false;
|
|
1980
1989
|
}
|
|
1981
1990
|
else {
|
|
1982
|
-
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();
|
|
1983
1994
|
return true;
|
|
1984
1995
|
}
|
|
1985
1996
|
}
|
|
@@ -1987,13 +1998,16 @@ class DocView extends ContentView {
|
|
|
1987
1998
|
if (this.dirty) {
|
|
1988
1999
|
this.view.observer.ignore(() => this.view.docView.sync());
|
|
1989
2000
|
this.dirty = 0 /* Not */;
|
|
2001
|
+
this.updateSelection(true);
|
|
1990
2002
|
}
|
|
1991
|
-
|
|
2003
|
+
else {
|
|
1992
2004
|
this.updateSelection();
|
|
2005
|
+
}
|
|
1993
2006
|
}
|
|
1994
2007
|
// Used both by update and checkLayout do perform the actual DOM
|
|
1995
2008
|
// update
|
|
1996
|
-
updateInner(changes, deco, oldLength
|
|
2009
|
+
updateInner(changes, deco, oldLength) {
|
|
2010
|
+
this.view.viewState.mustMeasureContent = true;
|
|
1997
2011
|
this.updateChildren(changes, deco, oldLength);
|
|
1998
2012
|
let { observer } = this.view;
|
|
1999
2013
|
observer.ignore(() => {
|
|
@@ -2001,7 +2015,7 @@ class DocView extends ContentView {
|
|
|
2001
2015
|
// messes with the scroll position during DOM mutation (though
|
|
2002
2016
|
// no relayout is triggered and I cannot imagine how it can
|
|
2003
2017
|
// recompute the scroll position without a layout)
|
|
2004
|
-
this.dom.style.height = this.view.viewState.
|
|
2018
|
+
this.dom.style.height = this.view.viewState.contentHeight + "px";
|
|
2005
2019
|
this.dom.style.minWidth = this.minWidth ? this.minWidth + "px" : "";
|
|
2006
2020
|
// Chrome will sometimes, when DOM mutations occur directly
|
|
2007
2021
|
// around the selection, get confused and report a different
|
|
@@ -2011,8 +2025,7 @@ class DocView extends ContentView {
|
|
|
2011
2025
|
this.sync(track);
|
|
2012
2026
|
this.dirty = 0 /* Not */;
|
|
2013
2027
|
if (track && (track.written || observer.selectionRange.focusNode != track.node))
|
|
2014
|
-
forceSelection = true;
|
|
2015
|
-
this.updateSelection(forceSelection, pointerSel);
|
|
2028
|
+
this.forceSelection = true;
|
|
2016
2029
|
this.dom.style.height = "";
|
|
2017
2030
|
});
|
|
2018
2031
|
let gaps = [];
|
|
@@ -2098,10 +2111,14 @@ class DocView extends ContentView {
|
|
|
2098
2111
|
this.replaceChildren(fromI, toI, content);
|
|
2099
2112
|
}
|
|
2100
2113
|
// Sync the DOM selection to this.state.selection
|
|
2101
|
-
updateSelection(
|
|
2114
|
+
updateSelection(mustRead = false, fromPointer = false) {
|
|
2115
|
+
if (mustRead)
|
|
2116
|
+
this.view.observer.readSelectionRange();
|
|
2102
2117
|
if (!(fromPointer || this.mayControlSelection()) ||
|
|
2103
2118
|
browser.ios && this.view.inputState.rapidCompositionStart)
|
|
2104
2119
|
return;
|
|
2120
|
+
let force = this.forceSelection;
|
|
2121
|
+
this.forceSelection = false;
|
|
2105
2122
|
let main = this.view.state.selection.main;
|
|
2106
2123
|
// FIXME need to handle the case where the selection falls inside a block range
|
|
2107
2124
|
let anchor = this.domAtPos(main.anchor);
|
|
@@ -2283,7 +2300,7 @@ class DocView extends ContentView {
|
|
|
2283
2300
|
let next = i == vs.viewports.length ? null : vs.viewports[i];
|
|
2284
2301
|
let end = next ? next.from - 1 : this.length;
|
|
2285
2302
|
if (end > pos) {
|
|
2286
|
-
let height = vs.
|
|
2303
|
+
let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
|
|
2287
2304
|
deco.push(Decoration.replace({ widget: new BlockGapWidget(height), block: true, inclusive: true }).range(pos, end));
|
|
2288
2305
|
}
|
|
2289
2306
|
if (!next)
|
|
@@ -2889,13 +2906,14 @@ function domPosInText(node, x, y) {
|
|
|
2889
2906
|
}
|
|
2890
2907
|
function posAtCoords(view, { x, y }, precise, bias = -1) {
|
|
2891
2908
|
var _a;
|
|
2892
|
-
let content = view.contentDOM.getBoundingClientRect(),
|
|
2909
|
+
let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop;
|
|
2893
2910
|
let halfLine = view.defaultLineHeight / 2;
|
|
2911
|
+
let block, yOffset = y - docTop;
|
|
2894
2912
|
for (let bounced = false;;) {
|
|
2895
|
-
block = view.
|
|
2896
|
-
if (block.top >
|
|
2897
|
-
bias = block.top >
|
|
2898
|
-
|
|
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));
|
|
2899
2917
|
if (bounced)
|
|
2900
2918
|
return precise ? null : 0;
|
|
2901
2919
|
else
|
|
@@ -2903,8 +2921,9 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
|
|
|
2903
2921
|
}
|
|
2904
2922
|
if (block.type == BlockType.Text)
|
|
2905
2923
|
break;
|
|
2906
|
-
|
|
2924
|
+
yOffset = bias > 0 ? block.bottom + halfLine : block.top - halfLine;
|
|
2907
2925
|
}
|
|
2926
|
+
y = docTop + yOffset;
|
|
2908
2927
|
let lineStart = block.from;
|
|
2909
2928
|
// Clip x to the viewport sides
|
|
2910
2929
|
x = Math.max(content.left + 1, Math.min(content.right - 1, x));
|
|
@@ -3017,17 +3036,17 @@ function moveVertically(view, start, forward, distance) {
|
|
|
3017
3036
|
return EditorSelection.cursor(startPos);
|
|
3018
3037
|
let goal = start.goalColumn, startY;
|
|
3019
3038
|
let rect = view.contentDOM.getBoundingClientRect();
|
|
3020
|
-
let startCoords = view.coordsAtPos(startPos);
|
|
3039
|
+
let startCoords = view.coordsAtPos(startPos), docTop = view.documentTop;
|
|
3021
3040
|
if (startCoords) {
|
|
3022
3041
|
if (goal == null)
|
|
3023
3042
|
goal = startCoords.left - rect.left;
|
|
3024
3043
|
startY = dir < 0 ? startCoords.top : startCoords.bottom;
|
|
3025
3044
|
}
|
|
3026
3045
|
else {
|
|
3027
|
-
let line = view.viewState.
|
|
3046
|
+
let line = view.viewState.lineBlockAt(startPos - docTop);
|
|
3028
3047
|
if (goal == null)
|
|
3029
3048
|
goal = Math.min(rect.right - rect.left, view.defaultCharacterWidth * (startPos - line.from));
|
|
3030
|
-
startY = dir < 0 ? line.top : line.bottom;
|
|
3049
|
+
startY = (dir < 0 ? line.top : line.bottom) + docTop;
|
|
3031
3050
|
}
|
|
3032
3051
|
let resolvedGoal = rect.left + goal;
|
|
3033
3052
|
let dist = distance !== null && distance !== void 0 ? distance : (view.defaultLineHeight >> 1);
|
|
@@ -3278,7 +3297,7 @@ class MouseSelection {
|
|
|
3278
3297
|
this.extend = startEvent.shiftKey;
|
|
3279
3298
|
this.multiple = view.state.facet(EditorState.allowMultipleSelections) && addsSelectionRange(view, startEvent);
|
|
3280
3299
|
this.dragMove = dragMovesSelection(view, startEvent);
|
|
3281
|
-
this.dragging = isInPrimarySelection(view, startEvent) ? null : false;
|
|
3300
|
+
this.dragging = isInPrimarySelection(view, startEvent) && getClickType(startEvent) == 1 ? null : false;
|
|
3282
3301
|
// When clicking outside of the selection, immediately apply the
|
|
3283
3302
|
// effect of starting the selection
|
|
3284
3303
|
if (this.dragging === false) {
|
|
@@ -3499,7 +3518,7 @@ function basicMouseSelection(view, event) {
|
|
|
3499
3518
|
let last = start, lastEvent = event;
|
|
3500
3519
|
return {
|
|
3501
3520
|
update(update) {
|
|
3502
|
-
if (update.
|
|
3521
|
+
if (update.docChanged) {
|
|
3503
3522
|
if (start)
|
|
3504
3523
|
start.pos = update.changes.mapPos(start.pos);
|
|
3505
3524
|
startSel = startSel.map(update.changes);
|
|
@@ -3763,7 +3782,10 @@ class HeightOracle {
|
|
|
3763
3782
|
return lines * this.lineHeight;
|
|
3764
3783
|
}
|
|
3765
3784
|
setDoc(doc) { this.doc = doc; return this; }
|
|
3766
|
-
|
|
3785
|
+
mustRefreshForStyle(whiteSpace, direction) {
|
|
3786
|
+
return (wrappingWhiteSpace.indexOf(whiteSpace) > -1) != this.lineWrapping || this.direction != direction;
|
|
3787
|
+
}
|
|
3788
|
+
mustRefreshForHeights(lineHeights) {
|
|
3767
3789
|
let newHeight = false;
|
|
3768
3790
|
for (let i = 0; i < lineHeights.length; i++) {
|
|
3769
3791
|
let h = lineHeights[i];
|
|
@@ -3775,7 +3797,7 @@ class HeightOracle {
|
|
|
3775
3797
|
this.heightSamples[Math.floor(h * 10)] = true;
|
|
3776
3798
|
}
|
|
3777
3799
|
}
|
|
3778
|
-
return newHeight
|
|
3800
|
+
return newHeight;
|
|
3779
3801
|
}
|
|
3780
3802
|
refresh(whiteSpace, direction, lineHeight, charWidth, lineLength, knownHeights) {
|
|
3781
3803
|
let lineWrapping = wrappingWhiteSpace.indexOf(whiteSpace) > -1;
|
|
@@ -3829,7 +3851,8 @@ class BlockInfo {
|
|
|
3829
3851
|
*/
|
|
3830
3852
|
length,
|
|
3831
3853
|
/**
|
|
3832
|
-
The top position of the element
|
|
3854
|
+
The top position of the element (relative to the top of the
|
|
3855
|
+
document).
|
|
3833
3856
|
*/
|
|
3834
3857
|
top,
|
|
3835
3858
|
/**
|
|
@@ -3863,13 +3886,19 @@ class BlockInfo {
|
|
|
3863
3886
|
.concat(Array.isArray(other.type) ? other.type : [other]);
|
|
3864
3887
|
return new BlockInfo(this.from, this.length + other.length, this.top, this.height + other.height, detail);
|
|
3865
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
|
+
}
|
|
3866
3895
|
}
|
|
3867
3896
|
var QueryType = /*@__PURE__*/(function (QueryType) {
|
|
3868
3897
|
QueryType[QueryType["ByPos"] = 0] = "ByPos";
|
|
3869
3898
|
QueryType[QueryType["ByHeight"] = 1] = "ByHeight";
|
|
3870
3899
|
QueryType[QueryType["ByPosNoHeight"] = 2] = "ByPosNoHeight";
|
|
3871
3900
|
return QueryType})(QueryType || (QueryType = {}));
|
|
3872
|
-
const Epsilon = 1e-
|
|
3901
|
+
const Epsilon = 1e-3;
|
|
3873
3902
|
class HeightMap {
|
|
3874
3903
|
constructor(length, // The number of characters covered
|
|
3875
3904
|
height, // Height of this part of the document
|
|
@@ -4096,22 +4125,30 @@ class HeightMapGap extends HeightMap {
|
|
|
4096
4125
|
// can't be widgets or collapsed ranges in those lines, because
|
|
4097
4126
|
// they would already have been added to the heightmap (gaps
|
|
4098
4127
|
// only contain plain text).
|
|
4099
|
-
let nodes = [], pos = Math.max(offset, measured.from);
|
|
4128
|
+
let nodes = [], pos = Math.max(offset, measured.from), singleHeight = -1;
|
|
4129
|
+
let wasChanged = oracle.heightChanged;
|
|
4100
4130
|
if (measured.from > offset)
|
|
4101
4131
|
nodes.push(new HeightMapGap(measured.from - offset - 1).updateHeight(oracle, offset));
|
|
4102
4132
|
while (pos <= end && measured.more) {
|
|
4103
4133
|
let len = oracle.doc.lineAt(pos).length;
|
|
4104
4134
|
if (nodes.length)
|
|
4105
4135
|
nodes.push(null);
|
|
4106
|
-
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);
|
|
4107
4142
|
line.outdated = false;
|
|
4108
4143
|
nodes.push(line);
|
|
4109
4144
|
pos += len + 1;
|
|
4110
4145
|
}
|
|
4111
4146
|
if (pos <= end)
|
|
4112
4147
|
nodes.push(null, new HeightMapGap(end - pos).updateHeight(oracle, pos));
|
|
4113
|
-
|
|
4114
|
-
|
|
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;
|
|
4115
4152
|
}
|
|
4116
4153
|
else if (force || this.outdated) {
|
|
4117
4154
|
this.setHeight(oracle, oracle.heightForGap(offset, offset + this.length));
|
|
@@ -4469,13 +4506,18 @@ class ViewState {
|
|
|
4469
4506
|
this.inView = true;
|
|
4470
4507
|
this.paddingTop = 0;
|
|
4471
4508
|
this.paddingBottom = 0;
|
|
4472
|
-
this.
|
|
4509
|
+
this.contentDOMWidth = 0;
|
|
4510
|
+
this.contentDOMHeight = 0;
|
|
4511
|
+
this.editorHeight = 0;
|
|
4473
4512
|
this.heightOracle = new HeightOracle;
|
|
4474
4513
|
// See VP.MaxDOMHeight
|
|
4475
4514
|
this.scaler = IdScaler;
|
|
4476
4515
|
this.scrollTarget = null;
|
|
4477
4516
|
// Briefly set to true when printing, to disable viewport limiting
|
|
4478
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;
|
|
4479
4521
|
this.visibleRanges = [];
|
|
4480
4522
|
// Cursor 'assoc' is only significant when the cursor is on a line
|
|
4481
4523
|
// wrap point, where it must stick to the character that it is
|
|
@@ -4488,6 +4530,7 @@ class ViewState {
|
|
|
4488
4530
|
this.mustEnforceCursorAssoc = false;
|
|
4489
4531
|
this.heightMap = HeightMap.empty().applyChanges(state.facet(decorations), Text.empty, this.heightOracle.setDoc(state.doc), [new ChangedRange(0, 0, 0, state.doc.length)]);
|
|
4490
4532
|
this.viewport = this.getViewport(0, null);
|
|
4533
|
+
this.updateViewportLines();
|
|
4491
4534
|
this.updateForViewport();
|
|
4492
4535
|
this.lineGaps = this.ensureLineGaps([]);
|
|
4493
4536
|
this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(false)));
|
|
@@ -4498,7 +4541,7 @@ class ViewState {
|
|
|
4498
4541
|
for (let i = 0; i <= 1; i++) {
|
|
4499
4542
|
let pos = i ? main.head : main.anchor;
|
|
4500
4543
|
if (!viewports.some(({ from, to }) => pos >= from && pos <= to)) {
|
|
4501
|
-
let { from, to } = this.
|
|
4544
|
+
let { from, to } = this.lineBlockAt(pos);
|
|
4502
4545
|
viewports.push(new Viewport(from, to));
|
|
4503
4546
|
}
|
|
4504
4547
|
}
|
|
@@ -4506,6 +4549,12 @@ class ViewState {
|
|
|
4506
4549
|
this.scaler = this.heightMap.height <= 7000000 /* MaxDOMHeight */ ? IdScaler :
|
|
4507
4550
|
new BigScaler(this.heightOracle.doc, this.heightMap, this.viewports);
|
|
4508
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
|
+
}
|
|
4509
4558
|
update(update, scrollTarget = null) {
|
|
4510
4559
|
let prev = this.state;
|
|
4511
4560
|
this.state = update.state;
|
|
@@ -4520,6 +4569,9 @@ class ViewState {
|
|
|
4520
4569
|
if (scrollTarget && (scrollTarget.range.head < viewport.from || scrollTarget.range.head > viewport.to) ||
|
|
4521
4570
|
!this.viewportIsAppropriate(viewport))
|
|
4522
4571
|
viewport = this.getViewport(0, scrollTarget);
|
|
4572
|
+
if (!update.changes.empty || (update.flags & 2 /* Height */) ||
|
|
4573
|
+
viewport.from != this.viewport.from || viewport.to != this.viewport.to)
|
|
4574
|
+
this.updateViewportLines();
|
|
4523
4575
|
this.viewport = viewport;
|
|
4524
4576
|
this.updateForViewport();
|
|
4525
4577
|
if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
|
|
@@ -4531,13 +4583,17 @@ class ViewState {
|
|
|
4531
4583
|
update.state.selection.main.empty && update.state.selection.main.assoc)
|
|
4532
4584
|
this.mustEnforceCursorAssoc = true;
|
|
4533
4585
|
}
|
|
4534
|
-
measure(
|
|
4535
|
-
let dom =
|
|
4536
|
-
let
|
|
4537
|
-
|
|
4586
|
+
measure(view) {
|
|
4587
|
+
let dom = view.contentDOM, style = window.getComputedStyle(dom);
|
|
4588
|
+
let oracle = this.heightOracle;
|
|
4589
|
+
let whiteSpace = style.whiteSpace, direction = style.direction == "rtl" ? Direction.RTL : Direction.LTR;
|
|
4590
|
+
let refresh = this.heightOracle.mustRefreshForStyle(whiteSpace, direction);
|
|
4591
|
+
let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != dom.clientHeight;
|
|
4592
|
+
let result = 0, bias = 0;
|
|
4593
|
+
if (measureContent) {
|
|
4594
|
+
this.mustMeasureContent = false;
|
|
4595
|
+
this.contentDOMHeight = dom.clientHeight;
|
|
4538
4596
|
// Vertical padding
|
|
4539
|
-
let style = window.getComputedStyle(dom);
|
|
4540
|
-
whiteSpace = style.whiteSpace, direction = (style.direction == "rtl" ? Direction.RTL : Direction.LTR);
|
|
4541
4597
|
let paddingTop = parseInt(style.paddingTop) || 0, paddingBottom = parseInt(style.paddingBottom) || 0;
|
|
4542
4598
|
if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) {
|
|
4543
4599
|
result |= 8 /* Geometry */;
|
|
@@ -4552,35 +4608,42 @@ class ViewState {
|
|
|
4552
4608
|
this.inView = this.pixelViewport.bottom > this.pixelViewport.top && this.pixelViewport.right > this.pixelViewport.left;
|
|
4553
4609
|
if (!this.inView)
|
|
4554
4610
|
return 0;
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
let { lineHeight, charWidth } = docView.measureTextSize();
|
|
4611
|
+
if (measureContent) {
|
|
4612
|
+
let lineHeights = view.docView.measureVisibleLineHeights();
|
|
4613
|
+
if (oracle.mustRefreshForHeights(lineHeights))
|
|
4614
|
+
refresh = true;
|
|
4615
|
+
let contentWidth = dom.clientWidth;
|
|
4616
|
+
if (refresh || oracle.lineWrapping && Math.abs(contentWidth - this.contentDOMWidth) > oracle.charWidth) {
|
|
4617
|
+
let { lineHeight, charWidth } = view.docView.measureTextSize();
|
|
4562
4618
|
refresh = oracle.refresh(whiteSpace, direction, lineHeight, charWidth, contentWidth / charWidth, lineHeights);
|
|
4563
4619
|
if (refresh) {
|
|
4564
|
-
docView.minWidth = 0;
|
|
4620
|
+
view.docView.minWidth = 0;
|
|
4565
4621
|
result |= 8 /* Geometry */;
|
|
4566
4622
|
}
|
|
4567
4623
|
}
|
|
4568
|
-
if (this.
|
|
4569
|
-
this.
|
|
4624
|
+
if (this.contentDOMWidth != contentWidth) {
|
|
4625
|
+
this.contentDOMWidth = contentWidth;
|
|
4626
|
+
result |= 8 /* Geometry */;
|
|
4627
|
+
}
|
|
4628
|
+
if (this.editorHeight != view.scrollDOM.clientHeight) {
|
|
4629
|
+
this.editorHeight = view.scrollDOM.clientHeight;
|
|
4570
4630
|
result |= 8 /* Geometry */;
|
|
4571
4631
|
}
|
|
4572
4632
|
if (dTop > 0 && dBottom > 0)
|
|
4573
4633
|
bias = Math.max(dTop, dBottom);
|
|
4574
4634
|
else if (dTop < 0 && dBottom < 0)
|
|
4575
4635
|
bias = Math.min(dTop, dBottom);
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to)
|
|
4636
|
+
oracle.heightChanged = false;
|
|
4637
|
+
this.heightMap = this.heightMap.updateHeight(oracle, 0, refresh, new MeasuredHeights(this.viewport.from, lineHeights));
|
|
4638
|
+
if (oracle.heightChanged)
|
|
4639
|
+
result |= 2 /* Height */;
|
|
4640
|
+
}
|
|
4641
|
+
let viewportChange = !this.viewportIsAppropriate(this.viewport, bias) ||
|
|
4642
|
+
this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to);
|
|
4643
|
+
if (viewportChange)
|
|
4583
4644
|
this.viewport = this.getViewport(bias, this.scrollTarget);
|
|
4645
|
+
if ((result & 2 /* Height */) || viewportChange)
|
|
4646
|
+
this.updateViewportLines();
|
|
4584
4647
|
this.updateForViewport();
|
|
4585
4648
|
if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
|
|
4586
4649
|
this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps));
|
|
@@ -4591,12 +4654,12 @@ class ViewState {
|
|
|
4591
4654
|
// to a line end is going to trigger a layout anyway, so it
|
|
4592
4655
|
// can't be a pure write. It should be rare that it does any
|
|
4593
4656
|
// writing.
|
|
4594
|
-
docView.enforceCursorAssoc();
|
|
4657
|
+
view.docView.enforceCursorAssoc();
|
|
4595
4658
|
}
|
|
4596
4659
|
return result;
|
|
4597
4660
|
}
|
|
4598
|
-
get visibleTop() { return this.scaler.fromDOM(this.pixelViewport.top
|
|
4599
|
-
get visibleBottom() { return this.scaler.fromDOM(this.pixelViewport.bottom
|
|
4661
|
+
get visibleTop() { return this.scaler.fromDOM(this.pixelViewport.top); }
|
|
4662
|
+
get visibleBottom() { return this.scaler.fromDOM(this.pixelViewport.bottom); }
|
|
4600
4663
|
getViewport(bias, scrollTarget) {
|
|
4601
4664
|
// This will divide VP.Margin between the top and the
|
|
4602
4665
|
// bottom, depending on the bias (the change in viewport position
|
|
@@ -4656,12 +4719,12 @@ class ViewState {
|
|
|
4656
4719
|
// This won't work at all in predominantly right-to-left text.
|
|
4657
4720
|
if (this.heightOracle.direction != Direction.LTR)
|
|
4658
4721
|
return gaps;
|
|
4659
|
-
|
|
4722
|
+
for (let line of this.viewportLines) {
|
|
4660
4723
|
if (line.length < 4000 /* DoubleMargin */)
|
|
4661
|
-
|
|
4724
|
+
continue;
|
|
4662
4725
|
let structure = lineStructure(line.from, line.to, this.state);
|
|
4663
4726
|
if (structure.total < 4000 /* DoubleMargin */)
|
|
4664
|
-
|
|
4727
|
+
continue;
|
|
4665
4728
|
let viewFrom, viewTo;
|
|
4666
4729
|
if (this.heightOracle.lineWrapping) {
|
|
4667
4730
|
let marginHeight = (2000 /* Margin */ / this.heightOracle.lineLength) * this.heightOracle.lineHeight;
|
|
@@ -4691,7 +4754,7 @@ class ViewState {
|
|
|
4691
4754
|
Math.abs(gap.from - from) < 1000 /* HalfMargin */ && Math.abs(gap.to - to) < 1000 /* HalfMargin */) ||
|
|
4692
4755
|
new LineGap(from, to, this.gapSize(line, from, to, structure)));
|
|
4693
4756
|
}
|
|
4694
|
-
}
|
|
4757
|
+
}
|
|
4695
4758
|
return gaps;
|
|
4696
4759
|
}
|
|
4697
4760
|
gapSize(line, from, to, structure) {
|
|
@@ -4723,27 +4786,18 @@ class ViewState {
|
|
|
4723
4786
|
this.visibleRanges = ranges;
|
|
4724
4787
|
return changed ? 4 /* Viewport */ : 0;
|
|
4725
4788
|
}
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
}
|
|
4730
|
-
lineAtHeight(height, editorTop) {
|
|
4731
|
-
editorTop += this.paddingTop;
|
|
4732
|
-
return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height, editorTop), QueryType.ByHeight, this.state.doc, editorTop, 0), this.scaler, editorTop);
|
|
4789
|
+
lineBlockAt(pos) {
|
|
4790
|
+
return (pos >= this.viewport.from && pos <= this.viewport.to && this.viewportLines.find(b => b.from <= pos && b.to <= pos)) ||
|
|
4791
|
+
scaleBlock(this.heightMap.lineAt(pos, QueryType.ByPos, this.state.doc, 0, 0), this.scaler);
|
|
4733
4792
|
}
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height, editorTop), this.state.doc, editorTop, 0), this.scaler, editorTop);
|
|
4793
|
+
lineBlockAtHeight(height) {
|
|
4794
|
+
return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.state.doc, 0, 0), this.scaler);
|
|
4737
4795
|
}
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
return this.heightMap.forEachLine(from, to, this.state.doc, editorTop, 0, this.scaler.scale == 1 ? f : b => f(scaleBlock(b, this.scaler, editorTop)));
|
|
4796
|
+
elementAtHeight(height) {
|
|
4797
|
+
return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.state.doc, 0, 0), this.scaler);
|
|
4741
4798
|
}
|
|
4742
4799
|
get contentHeight() {
|
|
4743
|
-
return this.
|
|
4744
|
-
}
|
|
4745
|
-
get domHeight() {
|
|
4746
|
-
return this.scaler.toDOM(this.heightMap.height, this.paddingTop);
|
|
4800
|
+
return this.scaler.toDOM(this.heightMap.height) + this.paddingTop + this.paddingBottom;
|
|
4747
4801
|
}
|
|
4748
4802
|
}
|
|
4749
4803
|
class Viewport {
|
|
@@ -4840,36 +4894,34 @@ class BigScaler {
|
|
|
4840
4894
|
base = obj.bottom;
|
|
4841
4895
|
}
|
|
4842
4896
|
}
|
|
4843
|
-
toDOM(n
|
|
4844
|
-
n -= top;
|
|
4897
|
+
toDOM(n) {
|
|
4845
4898
|
for (let i = 0, base = 0, domBase = 0;; i++) {
|
|
4846
4899
|
let vp = i < this.viewports.length ? this.viewports[i] : null;
|
|
4847
4900
|
if (!vp || n < vp.top)
|
|
4848
|
-
return domBase + (n - base) * this.scale
|
|
4901
|
+
return domBase + (n - base) * this.scale;
|
|
4849
4902
|
if (n <= vp.bottom)
|
|
4850
|
-
return vp.domTop + (n - vp.top)
|
|
4903
|
+
return vp.domTop + (n - vp.top);
|
|
4851
4904
|
base = vp.bottom;
|
|
4852
4905
|
domBase = vp.domBottom;
|
|
4853
4906
|
}
|
|
4854
4907
|
}
|
|
4855
|
-
fromDOM(n
|
|
4856
|
-
n -= top;
|
|
4908
|
+
fromDOM(n) {
|
|
4857
4909
|
for (let i = 0, base = 0, domBase = 0;; i++) {
|
|
4858
4910
|
let vp = i < this.viewports.length ? this.viewports[i] : null;
|
|
4859
4911
|
if (!vp || n < vp.domTop)
|
|
4860
|
-
return base + (n - domBase) / this.scale
|
|
4912
|
+
return base + (n - domBase) / this.scale;
|
|
4861
4913
|
if (n <= vp.domBottom)
|
|
4862
|
-
return vp.top + (n - vp.domTop)
|
|
4914
|
+
return vp.top + (n - vp.domTop);
|
|
4863
4915
|
base = vp.bottom;
|
|
4864
4916
|
domBase = vp.domBottom;
|
|
4865
4917
|
}
|
|
4866
4918
|
}
|
|
4867
4919
|
}
|
|
4868
|
-
function scaleBlock(block, scaler
|
|
4920
|
+
function scaleBlock(block, scaler) {
|
|
4869
4921
|
if (scaler.scale == 1)
|
|
4870
4922
|
return block;
|
|
4871
|
-
let bTop = scaler.toDOM(block.top
|
|
4872
|
-
return new BlockInfo(block.from, block.length, bTop, bBottom - bTop, Array.isArray(block.type) ? block.type.map(b => scaleBlock(b, scaler
|
|
4923
|
+
let bTop = scaler.toDOM(block.top), bBottom = scaler.toDOM(block.bottom);
|
|
4924
|
+
return new BlockInfo(block.from, block.length, bTop, bBottom - bTop, Array.isArray(block.type) ? block.type.map(b => scaleBlock(b, scaler)) : block.type);
|
|
4873
4925
|
}
|
|
4874
4926
|
|
|
4875
4927
|
const theme = /*@__PURE__*/Facet.define({ combine: strs => strs.join(" ") });
|
|
@@ -5054,24 +5106,30 @@ class DOMObserver {
|
|
|
5054
5106
|
this.onChange = onChange;
|
|
5055
5107
|
this.onScrollChanged = onScrollChanged;
|
|
5056
5108
|
this.active = false;
|
|
5057
|
-
|
|
5109
|
+
// The known selection. Kept in our own object, as opposed to just
|
|
5110
|
+
// directly accessing the selection because:
|
|
5111
|
+
// - Safari doesn't report the right selection in shadow DOM
|
|
5112
|
+
// - Reading from the selection forces a DOM layout
|
|
5113
|
+
// - This way, we can ignore selectionchange events if we have
|
|
5114
|
+
// already seen the 'new' selection
|
|
5115
|
+
this.selectionRange = new DOMSelectionState;
|
|
5116
|
+
// Set when a selection change is detected, cleared on flush
|
|
5117
|
+
this.selectionChanged = false;
|
|
5058
5118
|
this.delayedFlush = -1;
|
|
5119
|
+
this.resizeTimeout = -1;
|
|
5059
5120
|
this.queue = [];
|
|
5060
|
-
this.lastFlush = 0;
|
|
5061
5121
|
this.scrollTargets = [];
|
|
5062
5122
|
this.intersection = null;
|
|
5123
|
+
this.resize = null;
|
|
5063
5124
|
this.intersecting = false;
|
|
5064
5125
|
this.gapIntersection = null;
|
|
5065
5126
|
this.gaps = [];
|
|
5066
|
-
// Used to work around a Safari Selection/shadow DOM bug (#414)
|
|
5067
|
-
this._selectionRange = null;
|
|
5068
5127
|
// Timeout for scheduling check of the parents that need scroll handlers
|
|
5069
5128
|
this.parentCheck = -1;
|
|
5070
5129
|
this.dom = view.contentDOM;
|
|
5071
5130
|
this.observer = new MutationObserver(mutations => {
|
|
5072
5131
|
for (let mut of mutations)
|
|
5073
5132
|
this.queue.push(mut);
|
|
5074
|
-
this._selectionRange = null;
|
|
5075
5133
|
// IE11 will sometimes (on typing over a selection or
|
|
5076
5134
|
// backspacing out a single character text node) call the
|
|
5077
5135
|
// observer callback before actually updating the DOM.
|
|
@@ -5096,6 +5154,16 @@ class DOMObserver {
|
|
|
5096
5154
|
this.flushSoon();
|
|
5097
5155
|
};
|
|
5098
5156
|
this.onSelectionChange = this.onSelectionChange.bind(this);
|
|
5157
|
+
if (typeof ResizeObserver == "function") {
|
|
5158
|
+
this.resize = new ResizeObserver(() => {
|
|
5159
|
+
if (this.view.docView.lastUpdate < Date.now() - 75 && this.resizeTimeout < 0)
|
|
5160
|
+
this.resizeTimeout = setTimeout(() => {
|
|
5161
|
+
this.resizeTimeout = -1;
|
|
5162
|
+
this.view.requestMeasure();
|
|
5163
|
+
}, 50);
|
|
5164
|
+
});
|
|
5165
|
+
this.resize.observe(view.scrollDOM);
|
|
5166
|
+
}
|
|
5099
5167
|
this.start();
|
|
5100
5168
|
this.onScroll = this.onScroll.bind(this);
|
|
5101
5169
|
window.addEventListener("scroll", this.onScroll);
|
|
@@ -5116,10 +5184,12 @@ class DOMObserver {
|
|
|
5116
5184
|
}, {});
|
|
5117
5185
|
}
|
|
5118
5186
|
this.listenForScroll();
|
|
5187
|
+
this.readSelectionRange();
|
|
5188
|
+
this.dom.ownerDocument.addEventListener("selectionchange", this.onSelectionChange);
|
|
5119
5189
|
}
|
|
5120
5190
|
onScroll(e) {
|
|
5121
5191
|
if (this.intersecting)
|
|
5122
|
-
this.flush();
|
|
5192
|
+
this.flush(false);
|
|
5123
5193
|
this.onScrollChanged(e);
|
|
5124
5194
|
}
|
|
5125
5195
|
updateGaps(gaps) {
|
|
@@ -5131,8 +5201,8 @@ class DOMObserver {
|
|
|
5131
5201
|
}
|
|
5132
5202
|
}
|
|
5133
5203
|
onSelectionChange(event) {
|
|
5134
|
-
if (this.
|
|
5135
|
-
|
|
5204
|
+
if (!this.readSelectionRange())
|
|
5205
|
+
return;
|
|
5136
5206
|
let { view } = this, sel = this.selectionRange;
|
|
5137
5207
|
if (view.state.facet(editable) ? view.root.activeElement != this.dom : !hasSelection(view.dom, sel))
|
|
5138
5208
|
return;
|
|
@@ -5147,24 +5217,22 @@ class DOMObserver {
|
|
|
5147
5217
|
sel.focusNode && isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset))
|
|
5148
5218
|
this.flushSoon();
|
|
5149
5219
|
else
|
|
5150
|
-
this.flush();
|
|
5151
|
-
}
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
return this.
|
|
5220
|
+
this.flush(false);
|
|
5221
|
+
}
|
|
5222
|
+
readSelectionRange() {
|
|
5223
|
+
let { root } = this.view, domSel = getSelection(root);
|
|
5224
|
+
// The Selection object is broken in shadow roots in Safari. See
|
|
5225
|
+
// https://github.com/codemirror/codemirror.next/issues/414
|
|
5226
|
+
let range = browser.safari && root.nodeType == 11 && deepActiveElement() == this.view.contentDOM &&
|
|
5227
|
+
safariSelectionRangeHack(this.view) || domSel;
|
|
5228
|
+
if (this.selectionRange.eq(range))
|
|
5229
|
+
return false;
|
|
5230
|
+
this.selectionRange.setRange(range);
|
|
5231
|
+
return this.selectionChanged = true;
|
|
5162
5232
|
}
|
|
5163
5233
|
setSelectionRange(anchor, head) {
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
this._selectionRange = { anchorNode: anchor.node, anchorOffset: anchor.offset,
|
|
5167
|
-
focusNode: head.node, focusOffset: head.offset };
|
|
5234
|
+
this.selectionRange.set(anchor.node, anchor.offset, head.node, head.offset);
|
|
5235
|
+
this.selectionChanged = false;
|
|
5168
5236
|
}
|
|
5169
5237
|
listenForScroll() {
|
|
5170
5238
|
this.parentCheck = -1;
|
|
@@ -5211,7 +5279,6 @@ class DOMObserver {
|
|
|
5211
5279
|
if (this.active)
|
|
5212
5280
|
return;
|
|
5213
5281
|
this.observer.observe(this.dom, observeOptions);
|
|
5214
|
-
this.dom.ownerDocument.addEventListener("selectionchange", this.onSelectionChange);
|
|
5215
5282
|
if (useCharData)
|
|
5216
5283
|
this.dom.addEventListener("DOMCharacterDataModified", this.onCharData);
|
|
5217
5284
|
this.active = true;
|
|
@@ -5221,18 +5288,14 @@ class DOMObserver {
|
|
|
5221
5288
|
return;
|
|
5222
5289
|
this.active = false;
|
|
5223
5290
|
this.observer.disconnect();
|
|
5224
|
-
this.dom.ownerDocument.removeEventListener("selectionchange", this.onSelectionChange);
|
|
5225
5291
|
if (useCharData)
|
|
5226
5292
|
this.dom.removeEventListener("DOMCharacterDataModified", this.onCharData);
|
|
5227
5293
|
}
|
|
5228
|
-
clearSelection() {
|
|
5229
|
-
this.ignoreSelection.set(this.selectionRange);
|
|
5230
|
-
}
|
|
5231
5294
|
// Throw away any pending changes
|
|
5232
5295
|
clear() {
|
|
5233
5296
|
this.observer.takeRecords();
|
|
5234
5297
|
this.queue.length = 0;
|
|
5235
|
-
this.
|
|
5298
|
+
this.selectionChanged = false;
|
|
5236
5299
|
}
|
|
5237
5300
|
flushSoon() {
|
|
5238
5301
|
if (this.delayedFlush < 0)
|
|
@@ -5269,24 +5332,24 @@ class DOMObserver {
|
|
|
5269
5332
|
return { from, to, typeOver };
|
|
5270
5333
|
}
|
|
5271
5334
|
// Apply pending changes, if any
|
|
5272
|
-
flush() {
|
|
5335
|
+
flush(readSelection = true) {
|
|
5336
|
+
if (readSelection)
|
|
5337
|
+
this.readSelectionRange();
|
|
5273
5338
|
// Completely hold off flushing when pending keys are set—the code
|
|
5274
5339
|
// managing those will make sure processRecords is called and the
|
|
5275
5340
|
// view is resynchronized after
|
|
5276
5341
|
if (this.delayedFlush >= 0 || this.view.inputState.pendingAndroidKey)
|
|
5277
5342
|
return;
|
|
5278
|
-
this.lastFlush = Date.now();
|
|
5279
5343
|
let { from, to, typeOver } = this.processRecords();
|
|
5280
|
-
let
|
|
5281
|
-
let newSel = !this.ignoreSelection.eq(selection) && hasSelection(this.dom, selection);
|
|
5344
|
+
let newSel = this.selectionChanged && hasSelection(this.dom, this.selectionRange);
|
|
5282
5345
|
if (from < 0 && !newSel)
|
|
5283
5346
|
return;
|
|
5347
|
+
this.selectionChanged = false;
|
|
5284
5348
|
let startState = this.view.state;
|
|
5285
5349
|
this.onChange(from, to, typeOver);
|
|
5286
5350
|
// The view wasn't updated
|
|
5287
5351
|
if (this.view.state == startState)
|
|
5288
5352
|
this.view.docView.reset(newSel);
|
|
5289
|
-
this.clearSelection();
|
|
5290
5353
|
}
|
|
5291
5354
|
readMutation(rec) {
|
|
5292
5355
|
let cView = this.view.docView.nearest(rec.target);
|
|
@@ -5309,15 +5372,16 @@ class DOMObserver {
|
|
|
5309
5372
|
}
|
|
5310
5373
|
}
|
|
5311
5374
|
destroy() {
|
|
5375
|
+
var _a, _b, _c;
|
|
5312
5376
|
this.stop();
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
this.gapIntersection.disconnect();
|
|
5377
|
+
(_a = this.intersection) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
5378
|
+
(_b = this.gapIntersection) === null || _b === void 0 ? void 0 : _b.disconnect();
|
|
5379
|
+
(_c = this.resize) === null || _c === void 0 ? void 0 : _c.disconnect();
|
|
5317
5380
|
for (let dom of this.scrollTargets)
|
|
5318
5381
|
dom.removeEventListener("scroll", this.onScroll);
|
|
5319
5382
|
window.removeEventListener("scroll", this.onScroll);
|
|
5320
5383
|
clearTimeout(this.parentCheck);
|
|
5384
|
+
clearTimeout(this.resizeTimeout);
|
|
5321
5385
|
}
|
|
5322
5386
|
}
|
|
5323
5387
|
function findChild(cView, dom, dir) {
|
|
@@ -5330,6 +5394,7 @@ function findChild(cView, dom, dir) {
|
|
|
5330
5394
|
}
|
|
5331
5395
|
return null;
|
|
5332
5396
|
}
|
|
5397
|
+
// Used to work around a Safari Selection/shadow DOM bug (#414)
|
|
5333
5398
|
function safariSelectionRangeHack(view) {
|
|
5334
5399
|
let found = null;
|
|
5335
5400
|
// Because Safari (at least in 2018-2021) doesn't provide regular
|
|
@@ -5749,6 +5814,7 @@ class EditorView {
|
|
|
5749
5814
|
this.mountStyles();
|
|
5750
5815
|
this.updateAttrs();
|
|
5751
5816
|
this.showAnnouncements(transactions);
|
|
5817
|
+
this.docView.updateSelection(redrawn, transactions.some(tr => tr.isUserEvent("select.pointer")));
|
|
5752
5818
|
}
|
|
5753
5819
|
finally {
|
|
5754
5820
|
this.updateState = 0 /* Idle */;
|
|
@@ -5826,7 +5892,7 @@ class EditorView {
|
|
|
5826
5892
|
return;
|
|
5827
5893
|
if (this.measureScheduled > -1)
|
|
5828
5894
|
cancelAnimationFrame(this.measureScheduled);
|
|
5829
|
-
this.measureScheduled =
|
|
5895
|
+
this.measureScheduled = 0; // Prevent requestMeasure calls from scheduling another animation frame
|
|
5830
5896
|
if (flush)
|
|
5831
5897
|
this.observer.flush();
|
|
5832
5898
|
let updated = null;
|
|
@@ -5834,11 +5900,11 @@ class EditorView {
|
|
|
5834
5900
|
for (let i = 0;; i++) {
|
|
5835
5901
|
this.updateState = 1 /* Measuring */;
|
|
5836
5902
|
let oldViewport = this.viewport;
|
|
5837
|
-
let changed = this.viewState.measure(this
|
|
5903
|
+
let changed = this.viewState.measure(this);
|
|
5838
5904
|
if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
|
|
5839
5905
|
break;
|
|
5840
5906
|
if (i > 5) {
|
|
5841
|
-
console.warn("Viewport failed to stabilize");
|
|
5907
|
+
console.warn(this.measureRequests.length ? "Measure loop restarted more than 5 times" : "Viewport failed to stabilize");
|
|
5842
5908
|
break;
|
|
5843
5909
|
}
|
|
5844
5910
|
let measuring = [];
|
|
@@ -5854,7 +5920,7 @@ class EditorView {
|
|
|
5854
5920
|
return BadMeasure;
|
|
5855
5921
|
}
|
|
5856
5922
|
});
|
|
5857
|
-
let update = new ViewUpdate(this, this.state);
|
|
5923
|
+
let update = new ViewUpdate(this, this.state), redrawn = false;
|
|
5858
5924
|
update.flags |= changed;
|
|
5859
5925
|
if (!updated)
|
|
5860
5926
|
updated = update;
|
|
@@ -5864,14 +5930,15 @@ class EditorView {
|
|
|
5864
5930
|
if (!update.empty) {
|
|
5865
5931
|
this.updatePlugins(update);
|
|
5866
5932
|
this.inputState.update(update);
|
|
5933
|
+
this.updateAttrs();
|
|
5934
|
+
redrawn = this.docView.update(update);
|
|
5867
5935
|
}
|
|
5868
|
-
this.updateAttrs();
|
|
5869
|
-
if (changed)
|
|
5870
|
-
this.docView.update(update);
|
|
5871
5936
|
for (let i = 0; i < measuring.length; i++)
|
|
5872
5937
|
if (measured[i] != BadMeasure) {
|
|
5873
5938
|
try {
|
|
5874
|
-
measuring[i]
|
|
5939
|
+
let m = measuring[i];
|
|
5940
|
+
if (m.write)
|
|
5941
|
+
m.write(measured[i], this);
|
|
5875
5942
|
}
|
|
5876
5943
|
catch (e) {
|
|
5877
5944
|
logException(this.state, e);
|
|
@@ -5881,14 +5948,16 @@ class EditorView {
|
|
|
5881
5948
|
this.docView.scrollIntoView(this.viewState.scrollTarget);
|
|
5882
5949
|
this.viewState.scrollTarget = null;
|
|
5883
5950
|
}
|
|
5951
|
+
if (redrawn)
|
|
5952
|
+
this.docView.updateSelection(true);
|
|
5884
5953
|
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to && this.measureRequests.length == 0)
|
|
5885
5954
|
break;
|
|
5886
5955
|
}
|
|
5887
5956
|
}
|
|
5888
5957
|
finally {
|
|
5889
5958
|
this.updateState = 0 /* Idle */;
|
|
5959
|
+
this.measureScheduled = -1;
|
|
5890
5960
|
}
|
|
5891
|
-
this.measureScheduled = -1;
|
|
5892
5961
|
if (updated && !updated.empty)
|
|
5893
5962
|
for (let listener of this.state.facet(updateListener))
|
|
5894
5963
|
listener(updated);
|
|
@@ -5905,8 +5974,6 @@ class EditorView {
|
|
|
5905
5974
|
let editorAttrs = combineAttrs(this.state.facet(editorAttributes), {
|
|
5906
5975
|
class: "cm-editor" + (this.hasFocus ? " cm-focused " : " ") + this.themeClasses
|
|
5907
5976
|
});
|
|
5908
|
-
updateAttrs(this.dom, this.editorAttrs, editorAttrs);
|
|
5909
|
-
this.editorAttrs = editorAttrs;
|
|
5910
5977
|
let contentAttrs = {
|
|
5911
5978
|
spellcheck: "false",
|
|
5912
5979
|
autocorrect: "off",
|
|
@@ -5921,7 +5988,11 @@ class EditorView {
|
|
|
5921
5988
|
if (this.state.readOnly)
|
|
5922
5989
|
contentAttrs["aria-readonly"] = "true";
|
|
5923
5990
|
combineAttrs(this.state.facet(contentAttributes), contentAttrs);
|
|
5924
|
-
|
|
5991
|
+
this.observer.ignore(() => {
|
|
5992
|
+
updateAttrs(this.contentDOM, this.contentAttrs, contentAttrs);
|
|
5993
|
+
updateAttrs(this.dom, this.editorAttrs, editorAttrs);
|
|
5994
|
+
});
|
|
5995
|
+
this.editorAttrs = editorAttrs;
|
|
5925
5996
|
this.contentAttrs = contentAttrs;
|
|
5926
5997
|
}
|
|
5927
5998
|
showAnnouncements(trs) {
|
|
@@ -5991,6 +6062,13 @@ class EditorView {
|
|
|
5991
6062
|
return null;
|
|
5992
6063
|
}
|
|
5993
6064
|
/**
|
|
6065
|
+
The top position of the document, in screen coordinates. This
|
|
6066
|
+
may be negative when the editor is scrolled down.
|
|
6067
|
+
*/
|
|
6068
|
+
get documentTop() {
|
|
6069
|
+
return this.contentDOM.getBoundingClientRect().top + this.viewState.paddingTop;
|
|
6070
|
+
}
|
|
6071
|
+
/**
|
|
5994
6072
|
Find the line or block widget at the given vertical position.
|
|
5995
6073
|
|
|
5996
6074
|
By default, this position is interpreted as a screen position,
|
|
@@ -6000,10 +6078,21 @@ class EditorView {
|
|
|
6000
6078
|
position, or a precomputed document top
|
|
6001
6079
|
(`view.contentDOM.getBoundingClientRect().top`) to limit layout
|
|
6002
6080
|
queries.
|
|
6081
|
+
|
|
6082
|
+
*Deprecated: use `blockAtHeight` instead.*
|
|
6003
6083
|
*/
|
|
6004
6084
|
blockAtHeight(height, docTop) {
|
|
6085
|
+
let top = ensureTop(docTop, this);
|
|
6086
|
+
return this.elementAtHeight(height - top).moveY(top);
|
|
6087
|
+
}
|
|
6088
|
+
/**
|
|
6089
|
+
Find the text line or block widget at the given vertical
|
|
6090
|
+
position (which is interpreted as relative to the [top of the
|
|
6091
|
+
document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)
|
|
6092
|
+
*/
|
|
6093
|
+
elementAtHeight(height) {
|
|
6005
6094
|
this.readMeasured();
|
|
6006
|
-
return this.viewState.
|
|
6095
|
+
return this.viewState.elementAtHeight(height);
|
|
6007
6096
|
}
|
|
6008
6097
|
/**
|
|
6009
6098
|
Find information for the visual line (see
|
|
@@ -6015,20 +6104,43 @@ class EditorView {
|
|
|
6015
6104
|
Defaults to treating `height` as a screen position. See
|
|
6016
6105
|
[`blockAtHeight`](https://codemirror.net/6/docs/ref/#view.EditorView.blockAtHeight) for the
|
|
6017
6106
|
interpretation of the `docTop` parameter.
|
|
6107
|
+
|
|
6108
|
+
*Deprecated: use `lineBlockAtHeight` instead.*
|
|
6018
6109
|
*/
|
|
6019
6110
|
visualLineAtHeight(height, docTop) {
|
|
6111
|
+
let top = ensureTop(docTop, this);
|
|
6112
|
+
return this.lineBlockAtHeight(height - top).moveY(top);
|
|
6113
|
+
}
|
|
6114
|
+
/**
|
|
6115
|
+
Find the line block (see
|
|
6116
|
+
[`lineBlockAt`](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) at the given
|
|
6117
|
+
height.
|
|
6118
|
+
*/
|
|
6119
|
+
lineBlockAtHeight(height) {
|
|
6020
6120
|
this.readMeasured();
|
|
6021
|
-
return this.viewState.
|
|
6121
|
+
return this.viewState.lineBlockAtHeight(height);
|
|
6022
6122
|
}
|
|
6023
6123
|
/**
|
|
6024
6124
|
Iterate over the height information of the visual lines in the
|
|
6025
6125
|
viewport. The heights of lines are reported relative to the
|
|
6026
6126
|
given document top, which defaults to the screen position of the
|
|
6027
6127
|
document (forcing a layout).
|
|
6128
|
+
|
|
6129
|
+
*Deprecated: use `viewportLineBlocks` instead.*
|
|
6028
6130
|
*/
|
|
6029
6131
|
viewportLines(f, docTop) {
|
|
6030
|
-
let
|
|
6031
|
-
|
|
6132
|
+
let top = ensureTop(docTop, this);
|
|
6133
|
+
for (let line of this.viewportLineBlocks)
|
|
6134
|
+
f(line.moveY(top));
|
|
6135
|
+
}
|
|
6136
|
+
/**
|
|
6137
|
+
Get the extent and vertical position of all [line
|
|
6138
|
+
blocks](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) in the viewport. Positions
|
|
6139
|
+
are relative to the [top of the
|
|
6140
|
+
document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop);
|
|
6141
|
+
*/
|
|
6142
|
+
get viewportLineBlocks() {
|
|
6143
|
+
return this.viewState.viewportLines;
|
|
6032
6144
|
}
|
|
6033
6145
|
/**
|
|
6034
6146
|
Find the extent and height of the visual line (a range delimited
|
|
@@ -6039,9 +6151,22 @@ class EditorView {
|
|
|
6039
6151
|
argument, which defaults to 0 for this method. You can pass
|
|
6040
6152
|
`view.contentDOM.getBoundingClientRect().top` here to get screen
|
|
6041
6153
|
coordinates.
|
|
6154
|
+
|
|
6155
|
+
*Deprecated: use `lineBlockAt` instead.*
|
|
6042
6156
|
*/
|
|
6043
6157
|
visualLineAt(pos, docTop = 0) {
|
|
6044
|
-
return this.
|
|
6158
|
+
return this.lineBlockAt(pos).moveY(docTop + this.viewState.paddingTop);
|
|
6159
|
+
}
|
|
6160
|
+
/**
|
|
6161
|
+
Find the line block around the given document position. A line
|
|
6162
|
+
block is a range delimited on both sides by either a
|
|
6163
|
+
non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^range) line breaks, or the
|
|
6164
|
+
start/end of the document. It will usually just hold a line of
|
|
6165
|
+
text, but may be broken into multiple textblocks by block
|
|
6166
|
+
widgets.
|
|
6167
|
+
*/
|
|
6168
|
+
lineBlockAt(pos) {
|
|
6169
|
+
return this.viewState.lineBlockAt(pos);
|
|
6045
6170
|
}
|
|
6046
6171
|
/**
|
|
6047
6172
|
The editor's total content height.
|
|
@@ -6374,8 +6499,9 @@ search match).
|
|
|
6374
6499
|
EditorView.announce = /*@__PURE__*/StateEffect.define();
|
|
6375
6500
|
// Maximum line length for which we compute accurate bidi info
|
|
6376
6501
|
const MaxBidiLine = 4096;
|
|
6377
|
-
|
|
6378
|
-
|
|
6502
|
+
// FIXME remove this and its callers on next breaking release
|
|
6503
|
+
function ensureTop(given, view) {
|
|
6504
|
+
return (given == null ? view.contentDOM.getBoundingClientRect().top : given) + view.viewState.paddingTop;
|
|
6379
6505
|
}
|
|
6380
6506
|
let resizeDebounce = -1;
|
|
6381
6507
|
function ensureGlobalHandler() {
|
|
@@ -6726,7 +6852,7 @@ function wrappedLine(view, pos, inside) {
|
|
|
6726
6852
|
type: BlockType.Text };
|
|
6727
6853
|
}
|
|
6728
6854
|
function blockAt(view, pos) {
|
|
6729
|
-
let line = view.
|
|
6855
|
+
let line = view.lineBlockAt(pos);
|
|
6730
6856
|
if (Array.isArray(line.type))
|
|
6731
6857
|
for (let l of line.type) {
|
|
6732
6858
|
if (l.to > pos || l.to == pos && (l.to == line.to || l.type == BlockType.Text))
|
|
@@ -7117,7 +7243,7 @@ const activeLineHighlighter = /*@__PURE__*/ViewPlugin.fromClass(class {
|
|
|
7117
7243
|
for (let r of view.state.selection.ranges) {
|
|
7118
7244
|
if (!r.empty)
|
|
7119
7245
|
return Decoration.none;
|
|
7120
|
-
let line = view.
|
|
7246
|
+
let line = view.lineBlockAt(r.head);
|
|
7121
7247
|
if (line.from > lastLineStart) {
|
|
7122
7248
|
deco.push(lineDeco.range(line.from));
|
|
7123
7249
|
lastLineStart = line.from;
|