@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.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;
|
|
@@ -1771,11 +1774,17 @@ class PluginInstance {
|
|
|
1771
1774
|
}
|
|
1772
1775
|
}
|
|
1773
1776
|
PluginInstance.dummy = new PluginInstance(ViewPlugin.define(() => ({})));
|
|
1777
|
+
function combineFacetAttrs(values) {
|
|
1778
|
+
let result = {};
|
|
1779
|
+
for (let i = values.length - 1; i >= 0; i--)
|
|
1780
|
+
combineAttrs(values[i], result);
|
|
1781
|
+
return result;
|
|
1782
|
+
}
|
|
1774
1783
|
const editorAttributes = state.Facet.define({
|
|
1775
|
-
combine:
|
|
1784
|
+
combine: combineFacetAttrs
|
|
1776
1785
|
});
|
|
1777
1786
|
const contentAttributes = state.Facet.define({
|
|
1778
|
-
combine:
|
|
1787
|
+
combine: combineFacetAttrs
|
|
1779
1788
|
});
|
|
1780
1789
|
// Provide decorations
|
|
1781
1790
|
const decorations = state.Facet.define();
|
|
@@ -1937,6 +1946,10 @@ class DocView extends ContentView {
|
|
|
1937
1946
|
// we don't mess it up when reading it back it
|
|
1938
1947
|
this.impreciseAnchor = null;
|
|
1939
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();
|
|
1940
1953
|
this.setDOM(view.contentDOM);
|
|
1941
1954
|
this.children = [new LineView];
|
|
1942
1955
|
this.children[0].setParent(this);
|
|
@@ -1969,21 +1982,19 @@ class DocView extends ContentView {
|
|
|
1969
1982
|
// getSelection than the one that it actually shows to the user.
|
|
1970
1983
|
// This forces a selection update when lines are joined to work
|
|
1971
1984
|
// around that. Issue #54
|
|
1972
|
-
|
|
1973
|
-
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;
|
|
1974
1988
|
let prevDeco = this.decorations, deco = this.updateDeco();
|
|
1975
1989
|
let decoDiff = findChangedDeco(prevDeco, deco, update.changes);
|
|
1976
1990
|
changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
|
|
1977
|
-
|
|
1978
|
-
if (this.dirty == 0 /* Not */ && changedRanges.length == 0 &&
|
|
1979
|
-
!(update.flags & 4 /* Viewport */) &&
|
|
1980
|
-
update.state.selection.main.from >= this.view.viewport.from &&
|
|
1981
|
-
update.state.selection.main.to <= this.view.viewport.to) {
|
|
1982
|
-
this.updateSelection(forceSelection, pointerSel);
|
|
1991
|
+
if (this.dirty == 0 /* Not */ && changedRanges.length == 0) {
|
|
1983
1992
|
return false;
|
|
1984
1993
|
}
|
|
1985
1994
|
else {
|
|
1986
|
-
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();
|
|
1987
1998
|
return true;
|
|
1988
1999
|
}
|
|
1989
2000
|
}
|
|
@@ -1991,13 +2002,16 @@ class DocView extends ContentView {
|
|
|
1991
2002
|
if (this.dirty) {
|
|
1992
2003
|
this.view.observer.ignore(() => this.view.docView.sync());
|
|
1993
2004
|
this.dirty = 0 /* Not */;
|
|
2005
|
+
this.updateSelection(true);
|
|
1994
2006
|
}
|
|
1995
|
-
|
|
2007
|
+
else {
|
|
1996
2008
|
this.updateSelection();
|
|
2009
|
+
}
|
|
1997
2010
|
}
|
|
1998
2011
|
// Used both by update and checkLayout do perform the actual DOM
|
|
1999
2012
|
// update
|
|
2000
|
-
updateInner(changes, deco, oldLength
|
|
2013
|
+
updateInner(changes, deco, oldLength) {
|
|
2014
|
+
this.view.viewState.mustMeasureContent = true;
|
|
2001
2015
|
this.updateChildren(changes, deco, oldLength);
|
|
2002
2016
|
let { observer } = this.view;
|
|
2003
2017
|
observer.ignore(() => {
|
|
@@ -2005,7 +2019,7 @@ class DocView extends ContentView {
|
|
|
2005
2019
|
// messes with the scroll position during DOM mutation (though
|
|
2006
2020
|
// no relayout is triggered and I cannot imagine how it can
|
|
2007
2021
|
// recompute the scroll position without a layout)
|
|
2008
|
-
this.dom.style.height = this.view.viewState.
|
|
2022
|
+
this.dom.style.height = this.view.viewState.contentHeight + "px";
|
|
2009
2023
|
this.dom.style.minWidth = this.minWidth ? this.minWidth + "px" : "";
|
|
2010
2024
|
// Chrome will sometimes, when DOM mutations occur directly
|
|
2011
2025
|
// around the selection, get confused and report a different
|
|
@@ -2015,8 +2029,7 @@ class DocView extends ContentView {
|
|
|
2015
2029
|
this.sync(track);
|
|
2016
2030
|
this.dirty = 0 /* Not */;
|
|
2017
2031
|
if (track && (track.written || observer.selectionRange.focusNode != track.node))
|
|
2018
|
-
forceSelection = true;
|
|
2019
|
-
this.updateSelection(forceSelection, pointerSel);
|
|
2032
|
+
this.forceSelection = true;
|
|
2020
2033
|
this.dom.style.height = "";
|
|
2021
2034
|
});
|
|
2022
2035
|
let gaps = [];
|
|
@@ -2102,10 +2115,14 @@ class DocView extends ContentView {
|
|
|
2102
2115
|
this.replaceChildren(fromI, toI, content);
|
|
2103
2116
|
}
|
|
2104
2117
|
// Sync the DOM selection to this.state.selection
|
|
2105
|
-
updateSelection(
|
|
2118
|
+
updateSelection(mustRead = false, fromPointer = false) {
|
|
2119
|
+
if (mustRead)
|
|
2120
|
+
this.view.observer.readSelectionRange();
|
|
2106
2121
|
if (!(fromPointer || this.mayControlSelection()) ||
|
|
2107
2122
|
browser.ios && this.view.inputState.rapidCompositionStart)
|
|
2108
2123
|
return;
|
|
2124
|
+
let force = this.forceSelection;
|
|
2125
|
+
this.forceSelection = false;
|
|
2109
2126
|
let main = this.view.state.selection.main;
|
|
2110
2127
|
// FIXME need to handle the case where the selection falls inside a block range
|
|
2111
2128
|
let anchor = this.domAtPos(main.anchor);
|
|
@@ -2287,7 +2304,7 @@ class DocView extends ContentView {
|
|
|
2287
2304
|
let next = i == vs.viewports.length ? null : vs.viewports[i];
|
|
2288
2305
|
let end = next ? next.from - 1 : this.length;
|
|
2289
2306
|
if (end > pos) {
|
|
2290
|
-
let height = vs.
|
|
2307
|
+
let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
|
|
2291
2308
|
deco.push(Decoration.replace({ widget: new BlockGapWidget(height), block: true, inclusive: true }).range(pos, end));
|
|
2292
2309
|
}
|
|
2293
2310
|
if (!next)
|
|
@@ -2894,13 +2911,14 @@ function domPosInText(node, x, y) {
|
|
|
2894
2911
|
}
|
|
2895
2912
|
function posAtCoords(view, { x, y }, precise, bias = -1) {
|
|
2896
2913
|
var _a;
|
|
2897
|
-
let content = view.contentDOM.getBoundingClientRect(),
|
|
2914
|
+
let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop;
|
|
2898
2915
|
let halfLine = view.defaultLineHeight / 2;
|
|
2916
|
+
let block, yOffset = y - docTop;
|
|
2899
2917
|
for (let bounced = false;;) {
|
|
2900
|
-
block = view.
|
|
2901
|
-
if (block.top >
|
|
2902
|
-
bias = block.top >
|
|
2903
|
-
|
|
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));
|
|
2904
2922
|
if (bounced)
|
|
2905
2923
|
return precise ? null : 0;
|
|
2906
2924
|
else
|
|
@@ -2908,8 +2926,9 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
|
|
|
2908
2926
|
}
|
|
2909
2927
|
if (block.type == exports.BlockType.Text)
|
|
2910
2928
|
break;
|
|
2911
|
-
|
|
2929
|
+
yOffset = bias > 0 ? block.bottom + halfLine : block.top - halfLine;
|
|
2912
2930
|
}
|
|
2931
|
+
y = docTop + yOffset;
|
|
2913
2932
|
let lineStart = block.from;
|
|
2914
2933
|
// Clip x to the viewport sides
|
|
2915
2934
|
x = Math.max(content.left + 1, Math.min(content.right - 1, x));
|
|
@@ -3022,17 +3041,17 @@ function moveVertically(view, start, forward, distance) {
|
|
|
3022
3041
|
return state.EditorSelection.cursor(startPos);
|
|
3023
3042
|
let goal = start.goalColumn, startY;
|
|
3024
3043
|
let rect = view.contentDOM.getBoundingClientRect();
|
|
3025
|
-
let startCoords = view.coordsAtPos(startPos);
|
|
3044
|
+
let startCoords = view.coordsAtPos(startPos), docTop = view.documentTop;
|
|
3026
3045
|
if (startCoords) {
|
|
3027
3046
|
if (goal == null)
|
|
3028
3047
|
goal = startCoords.left - rect.left;
|
|
3029
3048
|
startY = dir < 0 ? startCoords.top : startCoords.bottom;
|
|
3030
3049
|
}
|
|
3031
3050
|
else {
|
|
3032
|
-
let line = view.viewState.
|
|
3051
|
+
let line = view.viewState.lineBlockAt(startPos - docTop);
|
|
3033
3052
|
if (goal == null)
|
|
3034
3053
|
goal = Math.min(rect.right - rect.left, view.defaultCharacterWidth * (startPos - line.from));
|
|
3035
|
-
startY = dir < 0 ? line.top : line.bottom;
|
|
3054
|
+
startY = (dir < 0 ? line.top : line.bottom) + docTop;
|
|
3036
3055
|
}
|
|
3037
3056
|
let resolvedGoal = rect.left + goal;
|
|
3038
3057
|
let dist = distance !== null && distance !== void 0 ? distance : (view.defaultLineHeight >> 1);
|
|
@@ -3283,7 +3302,7 @@ class MouseSelection {
|
|
|
3283
3302
|
this.extend = startEvent.shiftKey;
|
|
3284
3303
|
this.multiple = view.state.facet(state.EditorState.allowMultipleSelections) && addsSelectionRange(view, startEvent);
|
|
3285
3304
|
this.dragMove = dragMovesSelection(view, startEvent);
|
|
3286
|
-
this.dragging = isInPrimarySelection(view, startEvent) ? null : false;
|
|
3305
|
+
this.dragging = isInPrimarySelection(view, startEvent) && getClickType(startEvent) == 1 ? null : false;
|
|
3287
3306
|
// When clicking outside of the selection, immediately apply the
|
|
3288
3307
|
// effect of starting the selection
|
|
3289
3308
|
if (this.dragging === false) {
|
|
@@ -3504,7 +3523,7 @@ function basicMouseSelection(view, event) {
|
|
|
3504
3523
|
let last = start, lastEvent = event;
|
|
3505
3524
|
return {
|
|
3506
3525
|
update(update) {
|
|
3507
|
-
if (update.
|
|
3526
|
+
if (update.docChanged) {
|
|
3508
3527
|
if (start)
|
|
3509
3528
|
start.pos = update.changes.mapPos(start.pos);
|
|
3510
3529
|
startSel = startSel.map(update.changes);
|
|
@@ -3768,7 +3787,10 @@ class HeightOracle {
|
|
|
3768
3787
|
return lines * this.lineHeight;
|
|
3769
3788
|
}
|
|
3770
3789
|
setDoc(doc) { this.doc = doc; return this; }
|
|
3771
|
-
|
|
3790
|
+
mustRefreshForStyle(whiteSpace, direction) {
|
|
3791
|
+
return (wrappingWhiteSpace.indexOf(whiteSpace) > -1) != this.lineWrapping || this.direction != direction;
|
|
3792
|
+
}
|
|
3793
|
+
mustRefreshForHeights(lineHeights) {
|
|
3772
3794
|
let newHeight = false;
|
|
3773
3795
|
for (let i = 0; i < lineHeights.length; i++) {
|
|
3774
3796
|
let h = lineHeights[i];
|
|
@@ -3780,7 +3802,7 @@ class HeightOracle {
|
|
|
3780
3802
|
this.heightSamples[Math.floor(h * 10)] = true;
|
|
3781
3803
|
}
|
|
3782
3804
|
}
|
|
3783
|
-
return newHeight
|
|
3805
|
+
return newHeight;
|
|
3784
3806
|
}
|
|
3785
3807
|
refresh(whiteSpace, direction, lineHeight, charWidth, lineLength, knownHeights) {
|
|
3786
3808
|
let lineWrapping = wrappingWhiteSpace.indexOf(whiteSpace) > -1;
|
|
@@ -3834,7 +3856,8 @@ class BlockInfo {
|
|
|
3834
3856
|
*/
|
|
3835
3857
|
length,
|
|
3836
3858
|
/**
|
|
3837
|
-
The top position of the element
|
|
3859
|
+
The top position of the element (relative to the top of the
|
|
3860
|
+
document).
|
|
3838
3861
|
*/
|
|
3839
3862
|
top,
|
|
3840
3863
|
/**
|
|
@@ -3868,6 +3891,12 @@ class BlockInfo {
|
|
|
3868
3891
|
.concat(Array.isArray(other.type) ? other.type : [other]);
|
|
3869
3892
|
return new BlockInfo(this.from, this.length + other.length, this.top, this.height + other.height, detail);
|
|
3870
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
|
+
}
|
|
3871
3900
|
}
|
|
3872
3901
|
var QueryType;
|
|
3873
3902
|
(function (QueryType) {
|
|
@@ -3875,7 +3904,7 @@ var QueryType;
|
|
|
3875
3904
|
QueryType[QueryType["ByHeight"] = 1] = "ByHeight";
|
|
3876
3905
|
QueryType[QueryType["ByPosNoHeight"] = 2] = "ByPosNoHeight";
|
|
3877
3906
|
})(QueryType || (QueryType = {}));
|
|
3878
|
-
const Epsilon = 1e-
|
|
3907
|
+
const Epsilon = 1e-3;
|
|
3879
3908
|
class HeightMap {
|
|
3880
3909
|
constructor(length, // The number of characters covered
|
|
3881
3910
|
height, // Height of this part of the document
|
|
@@ -4102,22 +4131,30 @@ class HeightMapGap extends HeightMap {
|
|
|
4102
4131
|
// can't be widgets or collapsed ranges in those lines, because
|
|
4103
4132
|
// they would already have been added to the heightmap (gaps
|
|
4104
4133
|
// only contain plain text).
|
|
4105
|
-
let nodes = [], pos = Math.max(offset, measured.from);
|
|
4134
|
+
let nodes = [], pos = Math.max(offset, measured.from), singleHeight = -1;
|
|
4135
|
+
let wasChanged = oracle.heightChanged;
|
|
4106
4136
|
if (measured.from > offset)
|
|
4107
4137
|
nodes.push(new HeightMapGap(measured.from - offset - 1).updateHeight(oracle, offset));
|
|
4108
4138
|
while (pos <= end && measured.more) {
|
|
4109
4139
|
let len = oracle.doc.lineAt(pos).length;
|
|
4110
4140
|
if (nodes.length)
|
|
4111
4141
|
nodes.push(null);
|
|
4112
|
-
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);
|
|
4113
4148
|
line.outdated = false;
|
|
4114
4149
|
nodes.push(line);
|
|
4115
4150
|
pos += len + 1;
|
|
4116
4151
|
}
|
|
4117
4152
|
if (pos <= end)
|
|
4118
4153
|
nodes.push(null, new HeightMapGap(end - pos).updateHeight(oracle, pos));
|
|
4119
|
-
|
|
4120
|
-
|
|
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;
|
|
4121
4158
|
}
|
|
4122
4159
|
else if (force || this.outdated) {
|
|
4123
4160
|
this.setHeight(oracle, oracle.heightForGap(offset, offset + this.length));
|
|
@@ -4475,13 +4512,18 @@ class ViewState {
|
|
|
4475
4512
|
this.inView = true;
|
|
4476
4513
|
this.paddingTop = 0;
|
|
4477
4514
|
this.paddingBottom = 0;
|
|
4478
|
-
this.
|
|
4515
|
+
this.contentDOMWidth = 0;
|
|
4516
|
+
this.contentDOMHeight = 0;
|
|
4517
|
+
this.editorHeight = 0;
|
|
4479
4518
|
this.heightOracle = new HeightOracle;
|
|
4480
4519
|
// See VP.MaxDOMHeight
|
|
4481
4520
|
this.scaler = IdScaler;
|
|
4482
4521
|
this.scrollTarget = null;
|
|
4483
4522
|
// Briefly set to true when printing, to disable viewport limiting
|
|
4484
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;
|
|
4485
4527
|
this.visibleRanges = [];
|
|
4486
4528
|
// Cursor 'assoc' is only significant when the cursor is on a line
|
|
4487
4529
|
// wrap point, where it must stick to the character that it is
|
|
@@ -4494,6 +4536,7 @@ class ViewState {
|
|
|
4494
4536
|
this.mustEnforceCursorAssoc = false;
|
|
4495
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)]);
|
|
4496
4538
|
this.viewport = this.getViewport(0, null);
|
|
4539
|
+
this.updateViewportLines();
|
|
4497
4540
|
this.updateForViewport();
|
|
4498
4541
|
this.lineGaps = this.ensureLineGaps([]);
|
|
4499
4542
|
this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(false)));
|
|
@@ -4504,7 +4547,7 @@ class ViewState {
|
|
|
4504
4547
|
for (let i = 0; i <= 1; i++) {
|
|
4505
4548
|
let pos = i ? main.head : main.anchor;
|
|
4506
4549
|
if (!viewports.some(({ from, to }) => pos >= from && pos <= to)) {
|
|
4507
|
-
let { from, to } = this.
|
|
4550
|
+
let { from, to } = this.lineBlockAt(pos);
|
|
4508
4551
|
viewports.push(new Viewport(from, to));
|
|
4509
4552
|
}
|
|
4510
4553
|
}
|
|
@@ -4512,6 +4555,12 @@ class ViewState {
|
|
|
4512
4555
|
this.scaler = this.heightMap.height <= 7000000 /* MaxDOMHeight */ ? IdScaler :
|
|
4513
4556
|
new BigScaler(this.heightOracle.doc, this.heightMap, this.viewports);
|
|
4514
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
|
+
}
|
|
4515
4564
|
update(update, scrollTarget = null) {
|
|
4516
4565
|
let prev = this.state;
|
|
4517
4566
|
this.state = update.state;
|
|
@@ -4526,6 +4575,9 @@ class ViewState {
|
|
|
4526
4575
|
if (scrollTarget && (scrollTarget.range.head < viewport.from || scrollTarget.range.head > viewport.to) ||
|
|
4527
4576
|
!this.viewportIsAppropriate(viewport))
|
|
4528
4577
|
viewport = this.getViewport(0, scrollTarget);
|
|
4578
|
+
if (!update.changes.empty || (update.flags & 2 /* Height */) ||
|
|
4579
|
+
viewport.from != this.viewport.from || viewport.to != this.viewport.to)
|
|
4580
|
+
this.updateViewportLines();
|
|
4529
4581
|
this.viewport = viewport;
|
|
4530
4582
|
this.updateForViewport();
|
|
4531
4583
|
if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
|
|
@@ -4537,13 +4589,17 @@ class ViewState {
|
|
|
4537
4589
|
update.state.selection.main.empty && update.state.selection.main.assoc)
|
|
4538
4590
|
this.mustEnforceCursorAssoc = true;
|
|
4539
4591
|
}
|
|
4540
|
-
measure(
|
|
4541
|
-
let dom =
|
|
4542
|
-
let
|
|
4543
|
-
|
|
4592
|
+
measure(view) {
|
|
4593
|
+
let dom = view.contentDOM, style = window.getComputedStyle(dom);
|
|
4594
|
+
let oracle = this.heightOracle;
|
|
4595
|
+
let whiteSpace = style.whiteSpace, direction = style.direction == "rtl" ? exports.Direction.RTL : exports.Direction.LTR;
|
|
4596
|
+
let refresh = this.heightOracle.mustRefreshForStyle(whiteSpace, direction);
|
|
4597
|
+
let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != dom.clientHeight;
|
|
4598
|
+
let result = 0, bias = 0;
|
|
4599
|
+
if (measureContent) {
|
|
4600
|
+
this.mustMeasureContent = false;
|
|
4601
|
+
this.contentDOMHeight = dom.clientHeight;
|
|
4544
4602
|
// Vertical padding
|
|
4545
|
-
let style = window.getComputedStyle(dom);
|
|
4546
|
-
whiteSpace = style.whiteSpace, direction = (style.direction == "rtl" ? exports.Direction.RTL : exports.Direction.LTR);
|
|
4547
4603
|
let paddingTop = parseInt(style.paddingTop) || 0, paddingBottom = parseInt(style.paddingBottom) || 0;
|
|
4548
4604
|
if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) {
|
|
4549
4605
|
result |= 8 /* Geometry */;
|
|
@@ -4558,35 +4614,42 @@ class ViewState {
|
|
|
4558
4614
|
this.inView = this.pixelViewport.bottom > this.pixelViewport.top && this.pixelViewport.right > this.pixelViewport.left;
|
|
4559
4615
|
if (!this.inView)
|
|
4560
4616
|
return 0;
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
let { lineHeight, charWidth } = docView.measureTextSize();
|
|
4617
|
+
if (measureContent) {
|
|
4618
|
+
let lineHeights = view.docView.measureVisibleLineHeights();
|
|
4619
|
+
if (oracle.mustRefreshForHeights(lineHeights))
|
|
4620
|
+
refresh = true;
|
|
4621
|
+
let contentWidth = dom.clientWidth;
|
|
4622
|
+
if (refresh || oracle.lineWrapping && Math.abs(contentWidth - this.contentDOMWidth) > oracle.charWidth) {
|
|
4623
|
+
let { lineHeight, charWidth } = view.docView.measureTextSize();
|
|
4568
4624
|
refresh = oracle.refresh(whiteSpace, direction, lineHeight, charWidth, contentWidth / charWidth, lineHeights);
|
|
4569
4625
|
if (refresh) {
|
|
4570
|
-
docView.minWidth = 0;
|
|
4626
|
+
view.docView.minWidth = 0;
|
|
4571
4627
|
result |= 8 /* Geometry */;
|
|
4572
4628
|
}
|
|
4573
4629
|
}
|
|
4574
|
-
if (this.
|
|
4575
|
-
this.
|
|
4630
|
+
if (this.contentDOMWidth != contentWidth) {
|
|
4631
|
+
this.contentDOMWidth = contentWidth;
|
|
4632
|
+
result |= 8 /* Geometry */;
|
|
4633
|
+
}
|
|
4634
|
+
if (this.editorHeight != view.scrollDOM.clientHeight) {
|
|
4635
|
+
this.editorHeight = view.scrollDOM.clientHeight;
|
|
4576
4636
|
result |= 8 /* Geometry */;
|
|
4577
4637
|
}
|
|
4578
4638
|
if (dTop > 0 && dBottom > 0)
|
|
4579
4639
|
bias = Math.max(dTop, dBottom);
|
|
4580
4640
|
else if (dTop < 0 && dBottom < 0)
|
|
4581
4641
|
bias = Math.min(dTop, dBottom);
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to)
|
|
4642
|
+
oracle.heightChanged = false;
|
|
4643
|
+
this.heightMap = this.heightMap.updateHeight(oracle, 0, refresh, new MeasuredHeights(this.viewport.from, lineHeights));
|
|
4644
|
+
if (oracle.heightChanged)
|
|
4645
|
+
result |= 2 /* Height */;
|
|
4646
|
+
}
|
|
4647
|
+
let viewportChange = !this.viewportIsAppropriate(this.viewport, bias) ||
|
|
4648
|
+
this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to);
|
|
4649
|
+
if (viewportChange)
|
|
4589
4650
|
this.viewport = this.getViewport(bias, this.scrollTarget);
|
|
4651
|
+
if ((result & 2 /* Height */) || viewportChange)
|
|
4652
|
+
this.updateViewportLines();
|
|
4590
4653
|
this.updateForViewport();
|
|
4591
4654
|
if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
|
|
4592
4655
|
this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps));
|
|
@@ -4597,12 +4660,12 @@ class ViewState {
|
|
|
4597
4660
|
// to a line end is going to trigger a layout anyway, so it
|
|
4598
4661
|
// can't be a pure write. It should be rare that it does any
|
|
4599
4662
|
// writing.
|
|
4600
|
-
docView.enforceCursorAssoc();
|
|
4663
|
+
view.docView.enforceCursorAssoc();
|
|
4601
4664
|
}
|
|
4602
4665
|
return result;
|
|
4603
4666
|
}
|
|
4604
|
-
get visibleTop() { return this.scaler.fromDOM(this.pixelViewport.top
|
|
4605
|
-
get visibleBottom() { return this.scaler.fromDOM(this.pixelViewport.bottom
|
|
4667
|
+
get visibleTop() { return this.scaler.fromDOM(this.pixelViewport.top); }
|
|
4668
|
+
get visibleBottom() { return this.scaler.fromDOM(this.pixelViewport.bottom); }
|
|
4606
4669
|
getViewport(bias, scrollTarget) {
|
|
4607
4670
|
// This will divide VP.Margin between the top and the
|
|
4608
4671
|
// bottom, depending on the bias (the change in viewport position
|
|
@@ -4662,12 +4725,12 @@ class ViewState {
|
|
|
4662
4725
|
// This won't work at all in predominantly right-to-left text.
|
|
4663
4726
|
if (this.heightOracle.direction != exports.Direction.LTR)
|
|
4664
4727
|
return gaps;
|
|
4665
|
-
|
|
4728
|
+
for (let line of this.viewportLines) {
|
|
4666
4729
|
if (line.length < 4000 /* DoubleMargin */)
|
|
4667
|
-
|
|
4730
|
+
continue;
|
|
4668
4731
|
let structure = lineStructure(line.from, line.to, this.state);
|
|
4669
4732
|
if (structure.total < 4000 /* DoubleMargin */)
|
|
4670
|
-
|
|
4733
|
+
continue;
|
|
4671
4734
|
let viewFrom, viewTo;
|
|
4672
4735
|
if (this.heightOracle.lineWrapping) {
|
|
4673
4736
|
let marginHeight = (2000 /* Margin */ / this.heightOracle.lineLength) * this.heightOracle.lineHeight;
|
|
@@ -4697,7 +4760,7 @@ class ViewState {
|
|
|
4697
4760
|
Math.abs(gap.from - from) < 1000 /* HalfMargin */ && Math.abs(gap.to - to) < 1000 /* HalfMargin */) ||
|
|
4698
4761
|
new LineGap(from, to, this.gapSize(line, from, to, structure)));
|
|
4699
4762
|
}
|
|
4700
|
-
}
|
|
4763
|
+
}
|
|
4701
4764
|
return gaps;
|
|
4702
4765
|
}
|
|
4703
4766
|
gapSize(line, from, to, structure) {
|
|
@@ -4729,27 +4792,18 @@ class ViewState {
|
|
|
4729
4792
|
this.visibleRanges = ranges;
|
|
4730
4793
|
return changed ? 4 /* Viewport */ : 0;
|
|
4731
4794
|
}
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
}
|
|
4736
|
-
lineAtHeight(height, editorTop) {
|
|
4737
|
-
editorTop += this.paddingTop;
|
|
4738
|
-
return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height, editorTop), QueryType.ByHeight, this.state.doc, editorTop, 0), this.scaler, editorTop);
|
|
4795
|
+
lineBlockAt(pos) {
|
|
4796
|
+
return (pos >= this.viewport.from && pos <= this.viewport.to && this.viewportLines.find(b => b.from <= pos && b.to <= pos)) ||
|
|
4797
|
+
scaleBlock(this.heightMap.lineAt(pos, QueryType.ByPos, this.state.doc, 0, 0), this.scaler);
|
|
4739
4798
|
}
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height, editorTop), this.state.doc, editorTop, 0), this.scaler, editorTop);
|
|
4799
|
+
lineBlockAtHeight(height) {
|
|
4800
|
+
return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.state.doc, 0, 0), this.scaler);
|
|
4743
4801
|
}
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
return this.heightMap.forEachLine(from, to, this.state.doc, editorTop, 0, this.scaler.scale == 1 ? f : b => f(scaleBlock(b, this.scaler, editorTop)));
|
|
4802
|
+
elementAtHeight(height) {
|
|
4803
|
+
return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.state.doc, 0, 0), this.scaler);
|
|
4747
4804
|
}
|
|
4748
4805
|
get contentHeight() {
|
|
4749
|
-
return this.
|
|
4750
|
-
}
|
|
4751
|
-
get domHeight() {
|
|
4752
|
-
return this.scaler.toDOM(this.heightMap.height, this.paddingTop);
|
|
4806
|
+
return this.scaler.toDOM(this.heightMap.height) + this.paddingTop + this.paddingBottom;
|
|
4753
4807
|
}
|
|
4754
4808
|
}
|
|
4755
4809
|
class Viewport {
|
|
@@ -4846,36 +4900,34 @@ class BigScaler {
|
|
|
4846
4900
|
base = obj.bottom;
|
|
4847
4901
|
}
|
|
4848
4902
|
}
|
|
4849
|
-
toDOM(n
|
|
4850
|
-
n -= top;
|
|
4903
|
+
toDOM(n) {
|
|
4851
4904
|
for (let i = 0, base = 0, domBase = 0;; i++) {
|
|
4852
4905
|
let vp = i < this.viewports.length ? this.viewports[i] : null;
|
|
4853
4906
|
if (!vp || n < vp.top)
|
|
4854
|
-
return domBase + (n - base) * this.scale
|
|
4907
|
+
return domBase + (n - base) * this.scale;
|
|
4855
4908
|
if (n <= vp.bottom)
|
|
4856
|
-
return vp.domTop + (n - vp.top)
|
|
4909
|
+
return vp.domTop + (n - vp.top);
|
|
4857
4910
|
base = vp.bottom;
|
|
4858
4911
|
domBase = vp.domBottom;
|
|
4859
4912
|
}
|
|
4860
4913
|
}
|
|
4861
|
-
fromDOM(n
|
|
4862
|
-
n -= top;
|
|
4914
|
+
fromDOM(n) {
|
|
4863
4915
|
for (let i = 0, base = 0, domBase = 0;; i++) {
|
|
4864
4916
|
let vp = i < this.viewports.length ? this.viewports[i] : null;
|
|
4865
4917
|
if (!vp || n < vp.domTop)
|
|
4866
|
-
return base + (n - domBase) / this.scale
|
|
4918
|
+
return base + (n - domBase) / this.scale;
|
|
4867
4919
|
if (n <= vp.domBottom)
|
|
4868
|
-
return vp.top + (n - vp.domTop)
|
|
4920
|
+
return vp.top + (n - vp.domTop);
|
|
4869
4921
|
base = vp.bottom;
|
|
4870
4922
|
domBase = vp.domBottom;
|
|
4871
4923
|
}
|
|
4872
4924
|
}
|
|
4873
4925
|
}
|
|
4874
|
-
function scaleBlock(block, scaler
|
|
4926
|
+
function scaleBlock(block, scaler) {
|
|
4875
4927
|
if (scaler.scale == 1)
|
|
4876
4928
|
return block;
|
|
4877
|
-
let bTop = scaler.toDOM(block.top
|
|
4878
|
-
return new BlockInfo(block.from, block.length, bTop, bBottom - bTop, Array.isArray(block.type) ? block.type.map(b => scaleBlock(b, scaler
|
|
4929
|
+
let bTop = scaler.toDOM(block.top), bBottom = scaler.toDOM(block.bottom);
|
|
4930
|
+
return new BlockInfo(block.from, block.length, bTop, bBottom - bTop, Array.isArray(block.type) ? block.type.map(b => scaleBlock(b, scaler)) : block.type);
|
|
4879
4931
|
}
|
|
4880
4932
|
|
|
4881
4933
|
const theme = state.Facet.define({ combine: strs => strs.join(" ") });
|
|
@@ -5060,24 +5112,30 @@ class DOMObserver {
|
|
|
5060
5112
|
this.onChange = onChange;
|
|
5061
5113
|
this.onScrollChanged = onScrollChanged;
|
|
5062
5114
|
this.active = false;
|
|
5063
|
-
|
|
5115
|
+
// The known selection. Kept in our own object, as opposed to just
|
|
5116
|
+
// directly accessing the selection because:
|
|
5117
|
+
// - Safari doesn't report the right selection in shadow DOM
|
|
5118
|
+
// - Reading from the selection forces a DOM layout
|
|
5119
|
+
// - This way, we can ignore selectionchange events if we have
|
|
5120
|
+
// already seen the 'new' selection
|
|
5121
|
+
this.selectionRange = new DOMSelectionState;
|
|
5122
|
+
// Set when a selection change is detected, cleared on flush
|
|
5123
|
+
this.selectionChanged = false;
|
|
5064
5124
|
this.delayedFlush = -1;
|
|
5125
|
+
this.resizeTimeout = -1;
|
|
5065
5126
|
this.queue = [];
|
|
5066
|
-
this.lastFlush = 0;
|
|
5067
5127
|
this.scrollTargets = [];
|
|
5068
5128
|
this.intersection = null;
|
|
5129
|
+
this.resize = null;
|
|
5069
5130
|
this.intersecting = false;
|
|
5070
5131
|
this.gapIntersection = null;
|
|
5071
5132
|
this.gaps = [];
|
|
5072
|
-
// Used to work around a Safari Selection/shadow DOM bug (#414)
|
|
5073
|
-
this._selectionRange = null;
|
|
5074
5133
|
// Timeout for scheduling check of the parents that need scroll handlers
|
|
5075
5134
|
this.parentCheck = -1;
|
|
5076
5135
|
this.dom = view.contentDOM;
|
|
5077
5136
|
this.observer = new MutationObserver(mutations => {
|
|
5078
5137
|
for (let mut of mutations)
|
|
5079
5138
|
this.queue.push(mut);
|
|
5080
|
-
this._selectionRange = null;
|
|
5081
5139
|
// IE11 will sometimes (on typing over a selection or
|
|
5082
5140
|
// backspacing out a single character text node) call the
|
|
5083
5141
|
// observer callback before actually updating the DOM.
|
|
@@ -5102,6 +5160,16 @@ class DOMObserver {
|
|
|
5102
5160
|
this.flushSoon();
|
|
5103
5161
|
};
|
|
5104
5162
|
this.onSelectionChange = this.onSelectionChange.bind(this);
|
|
5163
|
+
if (typeof ResizeObserver == "function") {
|
|
5164
|
+
this.resize = new ResizeObserver(() => {
|
|
5165
|
+
if (this.view.docView.lastUpdate < Date.now() - 75 && this.resizeTimeout < 0)
|
|
5166
|
+
this.resizeTimeout = setTimeout(() => {
|
|
5167
|
+
this.resizeTimeout = -1;
|
|
5168
|
+
this.view.requestMeasure();
|
|
5169
|
+
}, 50);
|
|
5170
|
+
});
|
|
5171
|
+
this.resize.observe(view.scrollDOM);
|
|
5172
|
+
}
|
|
5105
5173
|
this.start();
|
|
5106
5174
|
this.onScroll = this.onScroll.bind(this);
|
|
5107
5175
|
window.addEventListener("scroll", this.onScroll);
|
|
@@ -5122,10 +5190,12 @@ class DOMObserver {
|
|
|
5122
5190
|
}, {});
|
|
5123
5191
|
}
|
|
5124
5192
|
this.listenForScroll();
|
|
5193
|
+
this.readSelectionRange();
|
|
5194
|
+
this.dom.ownerDocument.addEventListener("selectionchange", this.onSelectionChange);
|
|
5125
5195
|
}
|
|
5126
5196
|
onScroll(e) {
|
|
5127
5197
|
if (this.intersecting)
|
|
5128
|
-
this.flush();
|
|
5198
|
+
this.flush(false);
|
|
5129
5199
|
this.onScrollChanged(e);
|
|
5130
5200
|
}
|
|
5131
5201
|
updateGaps(gaps) {
|
|
@@ -5137,8 +5207,8 @@ class DOMObserver {
|
|
|
5137
5207
|
}
|
|
5138
5208
|
}
|
|
5139
5209
|
onSelectionChange(event) {
|
|
5140
|
-
if (this.
|
|
5141
|
-
|
|
5210
|
+
if (!this.readSelectionRange())
|
|
5211
|
+
return;
|
|
5142
5212
|
let { view } = this, sel = this.selectionRange;
|
|
5143
5213
|
if (view.state.facet(editable) ? view.root.activeElement != this.dom : !hasSelection(view.dom, sel))
|
|
5144
5214
|
return;
|
|
@@ -5153,24 +5223,22 @@ class DOMObserver {
|
|
|
5153
5223
|
sel.focusNode && isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset))
|
|
5154
5224
|
this.flushSoon();
|
|
5155
5225
|
else
|
|
5156
|
-
this.flush();
|
|
5157
|
-
}
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
return this.
|
|
5226
|
+
this.flush(false);
|
|
5227
|
+
}
|
|
5228
|
+
readSelectionRange() {
|
|
5229
|
+
let { root } = this.view, domSel = getSelection(root);
|
|
5230
|
+
// The Selection object is broken in shadow roots in Safari. See
|
|
5231
|
+
// https://github.com/codemirror/codemirror.next/issues/414
|
|
5232
|
+
let range = browser.safari && root.nodeType == 11 && deepActiveElement() == this.view.contentDOM &&
|
|
5233
|
+
safariSelectionRangeHack(this.view) || domSel;
|
|
5234
|
+
if (this.selectionRange.eq(range))
|
|
5235
|
+
return false;
|
|
5236
|
+
this.selectionRange.setRange(range);
|
|
5237
|
+
return this.selectionChanged = true;
|
|
5168
5238
|
}
|
|
5169
5239
|
setSelectionRange(anchor, head) {
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
this._selectionRange = { anchorNode: anchor.node, anchorOffset: anchor.offset,
|
|
5173
|
-
focusNode: head.node, focusOffset: head.offset };
|
|
5240
|
+
this.selectionRange.set(anchor.node, anchor.offset, head.node, head.offset);
|
|
5241
|
+
this.selectionChanged = false;
|
|
5174
5242
|
}
|
|
5175
5243
|
listenForScroll() {
|
|
5176
5244
|
this.parentCheck = -1;
|
|
@@ -5217,7 +5285,6 @@ class DOMObserver {
|
|
|
5217
5285
|
if (this.active)
|
|
5218
5286
|
return;
|
|
5219
5287
|
this.observer.observe(this.dom, observeOptions);
|
|
5220
|
-
this.dom.ownerDocument.addEventListener("selectionchange", this.onSelectionChange);
|
|
5221
5288
|
if (useCharData)
|
|
5222
5289
|
this.dom.addEventListener("DOMCharacterDataModified", this.onCharData);
|
|
5223
5290
|
this.active = true;
|
|
@@ -5227,18 +5294,14 @@ class DOMObserver {
|
|
|
5227
5294
|
return;
|
|
5228
5295
|
this.active = false;
|
|
5229
5296
|
this.observer.disconnect();
|
|
5230
|
-
this.dom.ownerDocument.removeEventListener("selectionchange", this.onSelectionChange);
|
|
5231
5297
|
if (useCharData)
|
|
5232
5298
|
this.dom.removeEventListener("DOMCharacterDataModified", this.onCharData);
|
|
5233
5299
|
}
|
|
5234
|
-
clearSelection() {
|
|
5235
|
-
this.ignoreSelection.set(this.selectionRange);
|
|
5236
|
-
}
|
|
5237
5300
|
// Throw away any pending changes
|
|
5238
5301
|
clear() {
|
|
5239
5302
|
this.observer.takeRecords();
|
|
5240
5303
|
this.queue.length = 0;
|
|
5241
|
-
this.
|
|
5304
|
+
this.selectionChanged = false;
|
|
5242
5305
|
}
|
|
5243
5306
|
flushSoon() {
|
|
5244
5307
|
if (this.delayedFlush < 0)
|
|
@@ -5275,24 +5338,24 @@ class DOMObserver {
|
|
|
5275
5338
|
return { from, to, typeOver };
|
|
5276
5339
|
}
|
|
5277
5340
|
// Apply pending changes, if any
|
|
5278
|
-
flush() {
|
|
5341
|
+
flush(readSelection = true) {
|
|
5342
|
+
if (readSelection)
|
|
5343
|
+
this.readSelectionRange();
|
|
5279
5344
|
// Completely hold off flushing when pending keys are set—the code
|
|
5280
5345
|
// managing those will make sure processRecords is called and the
|
|
5281
5346
|
// view is resynchronized after
|
|
5282
5347
|
if (this.delayedFlush >= 0 || this.view.inputState.pendingAndroidKey)
|
|
5283
5348
|
return;
|
|
5284
|
-
this.lastFlush = Date.now();
|
|
5285
5349
|
let { from, to, typeOver } = this.processRecords();
|
|
5286
|
-
let
|
|
5287
|
-
let newSel = !this.ignoreSelection.eq(selection) && hasSelection(this.dom, selection);
|
|
5350
|
+
let newSel = this.selectionChanged && hasSelection(this.dom, this.selectionRange);
|
|
5288
5351
|
if (from < 0 && !newSel)
|
|
5289
5352
|
return;
|
|
5353
|
+
this.selectionChanged = false;
|
|
5290
5354
|
let startState = this.view.state;
|
|
5291
5355
|
this.onChange(from, to, typeOver);
|
|
5292
5356
|
// The view wasn't updated
|
|
5293
5357
|
if (this.view.state == startState)
|
|
5294
5358
|
this.view.docView.reset(newSel);
|
|
5295
|
-
this.clearSelection();
|
|
5296
5359
|
}
|
|
5297
5360
|
readMutation(rec) {
|
|
5298
5361
|
let cView = this.view.docView.nearest(rec.target);
|
|
@@ -5315,15 +5378,16 @@ class DOMObserver {
|
|
|
5315
5378
|
}
|
|
5316
5379
|
}
|
|
5317
5380
|
destroy() {
|
|
5381
|
+
var _a, _b, _c;
|
|
5318
5382
|
this.stop();
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
this.gapIntersection.disconnect();
|
|
5383
|
+
(_a = this.intersection) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
5384
|
+
(_b = this.gapIntersection) === null || _b === void 0 ? void 0 : _b.disconnect();
|
|
5385
|
+
(_c = this.resize) === null || _c === void 0 ? void 0 : _c.disconnect();
|
|
5323
5386
|
for (let dom of this.scrollTargets)
|
|
5324
5387
|
dom.removeEventListener("scroll", this.onScroll);
|
|
5325
5388
|
window.removeEventListener("scroll", this.onScroll);
|
|
5326
5389
|
clearTimeout(this.parentCheck);
|
|
5390
|
+
clearTimeout(this.resizeTimeout);
|
|
5327
5391
|
}
|
|
5328
5392
|
}
|
|
5329
5393
|
function findChild(cView, dom, dir) {
|
|
@@ -5336,6 +5400,7 @@ function findChild(cView, dom, dir) {
|
|
|
5336
5400
|
}
|
|
5337
5401
|
return null;
|
|
5338
5402
|
}
|
|
5403
|
+
// Used to work around a Safari Selection/shadow DOM bug (#414)
|
|
5339
5404
|
function safariSelectionRangeHack(view) {
|
|
5340
5405
|
let found = null;
|
|
5341
5406
|
// Because Safari (at least in 2018-2021) doesn't provide regular
|
|
@@ -5755,6 +5820,7 @@ class EditorView {
|
|
|
5755
5820
|
this.mountStyles();
|
|
5756
5821
|
this.updateAttrs();
|
|
5757
5822
|
this.showAnnouncements(transactions);
|
|
5823
|
+
this.docView.updateSelection(redrawn, transactions.some(tr => tr.isUserEvent("select.pointer")));
|
|
5758
5824
|
}
|
|
5759
5825
|
finally {
|
|
5760
5826
|
this.updateState = 0 /* Idle */;
|
|
@@ -5832,7 +5898,7 @@ class EditorView {
|
|
|
5832
5898
|
return;
|
|
5833
5899
|
if (this.measureScheduled > -1)
|
|
5834
5900
|
cancelAnimationFrame(this.measureScheduled);
|
|
5835
|
-
this.measureScheduled =
|
|
5901
|
+
this.measureScheduled = 0; // Prevent requestMeasure calls from scheduling another animation frame
|
|
5836
5902
|
if (flush)
|
|
5837
5903
|
this.observer.flush();
|
|
5838
5904
|
let updated = null;
|
|
@@ -5840,11 +5906,11 @@ class EditorView {
|
|
|
5840
5906
|
for (let i = 0;; i++) {
|
|
5841
5907
|
this.updateState = 1 /* Measuring */;
|
|
5842
5908
|
let oldViewport = this.viewport;
|
|
5843
|
-
let changed = this.viewState.measure(this
|
|
5909
|
+
let changed = this.viewState.measure(this);
|
|
5844
5910
|
if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
|
|
5845
5911
|
break;
|
|
5846
5912
|
if (i > 5) {
|
|
5847
|
-
console.warn("Viewport failed to stabilize");
|
|
5913
|
+
console.warn(this.measureRequests.length ? "Measure loop restarted more than 5 times" : "Viewport failed to stabilize");
|
|
5848
5914
|
break;
|
|
5849
5915
|
}
|
|
5850
5916
|
let measuring = [];
|
|
@@ -5860,7 +5926,7 @@ class EditorView {
|
|
|
5860
5926
|
return BadMeasure;
|
|
5861
5927
|
}
|
|
5862
5928
|
});
|
|
5863
|
-
let update = new ViewUpdate(this, this.state);
|
|
5929
|
+
let update = new ViewUpdate(this, this.state), redrawn = false;
|
|
5864
5930
|
update.flags |= changed;
|
|
5865
5931
|
if (!updated)
|
|
5866
5932
|
updated = update;
|
|
@@ -5870,14 +5936,15 @@ class EditorView {
|
|
|
5870
5936
|
if (!update.empty) {
|
|
5871
5937
|
this.updatePlugins(update);
|
|
5872
5938
|
this.inputState.update(update);
|
|
5939
|
+
this.updateAttrs();
|
|
5940
|
+
redrawn = this.docView.update(update);
|
|
5873
5941
|
}
|
|
5874
|
-
this.updateAttrs();
|
|
5875
|
-
if (changed)
|
|
5876
|
-
this.docView.update(update);
|
|
5877
5942
|
for (let i = 0; i < measuring.length; i++)
|
|
5878
5943
|
if (measured[i] != BadMeasure) {
|
|
5879
5944
|
try {
|
|
5880
|
-
measuring[i]
|
|
5945
|
+
let m = measuring[i];
|
|
5946
|
+
if (m.write)
|
|
5947
|
+
m.write(measured[i], this);
|
|
5881
5948
|
}
|
|
5882
5949
|
catch (e) {
|
|
5883
5950
|
logException(this.state, e);
|
|
@@ -5887,14 +5954,16 @@ class EditorView {
|
|
|
5887
5954
|
this.docView.scrollIntoView(this.viewState.scrollTarget);
|
|
5888
5955
|
this.viewState.scrollTarget = null;
|
|
5889
5956
|
}
|
|
5957
|
+
if (redrawn)
|
|
5958
|
+
this.docView.updateSelection(true);
|
|
5890
5959
|
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to && this.measureRequests.length == 0)
|
|
5891
5960
|
break;
|
|
5892
5961
|
}
|
|
5893
5962
|
}
|
|
5894
5963
|
finally {
|
|
5895
5964
|
this.updateState = 0 /* Idle */;
|
|
5965
|
+
this.measureScheduled = -1;
|
|
5896
5966
|
}
|
|
5897
|
-
this.measureScheduled = -1;
|
|
5898
5967
|
if (updated && !updated.empty)
|
|
5899
5968
|
for (let listener of this.state.facet(updateListener))
|
|
5900
5969
|
listener(updated);
|
|
@@ -5911,8 +5980,6 @@ class EditorView {
|
|
|
5911
5980
|
let editorAttrs = combineAttrs(this.state.facet(editorAttributes), {
|
|
5912
5981
|
class: "cm-editor" + (this.hasFocus ? " cm-focused " : " ") + this.themeClasses
|
|
5913
5982
|
});
|
|
5914
|
-
updateAttrs(this.dom, this.editorAttrs, editorAttrs);
|
|
5915
|
-
this.editorAttrs = editorAttrs;
|
|
5916
5983
|
let contentAttrs = {
|
|
5917
5984
|
spellcheck: "false",
|
|
5918
5985
|
autocorrect: "off",
|
|
@@ -5927,7 +5994,11 @@ class EditorView {
|
|
|
5927
5994
|
if (this.state.readOnly)
|
|
5928
5995
|
contentAttrs["aria-readonly"] = "true";
|
|
5929
5996
|
combineAttrs(this.state.facet(contentAttributes), contentAttrs);
|
|
5930
|
-
|
|
5997
|
+
this.observer.ignore(() => {
|
|
5998
|
+
updateAttrs(this.contentDOM, this.contentAttrs, contentAttrs);
|
|
5999
|
+
updateAttrs(this.dom, this.editorAttrs, editorAttrs);
|
|
6000
|
+
});
|
|
6001
|
+
this.editorAttrs = editorAttrs;
|
|
5931
6002
|
this.contentAttrs = contentAttrs;
|
|
5932
6003
|
}
|
|
5933
6004
|
showAnnouncements(trs) {
|
|
@@ -5997,6 +6068,13 @@ class EditorView {
|
|
|
5997
6068
|
return null;
|
|
5998
6069
|
}
|
|
5999
6070
|
/**
|
|
6071
|
+
The top position of the document, in screen coordinates. This
|
|
6072
|
+
may be negative when the editor is scrolled down.
|
|
6073
|
+
*/
|
|
6074
|
+
get documentTop() {
|
|
6075
|
+
return this.contentDOM.getBoundingClientRect().top + this.viewState.paddingTop;
|
|
6076
|
+
}
|
|
6077
|
+
/**
|
|
6000
6078
|
Find the line or block widget at the given vertical position.
|
|
6001
6079
|
|
|
6002
6080
|
By default, this position is interpreted as a screen position,
|
|
@@ -6006,10 +6084,21 @@ class EditorView {
|
|
|
6006
6084
|
position, or a precomputed document top
|
|
6007
6085
|
(`view.contentDOM.getBoundingClientRect().top`) to limit layout
|
|
6008
6086
|
queries.
|
|
6087
|
+
|
|
6088
|
+
*Deprecated: use `blockAtHeight` instead.*
|
|
6009
6089
|
*/
|
|
6010
6090
|
blockAtHeight(height, docTop) {
|
|
6091
|
+
let top = ensureTop(docTop, this);
|
|
6092
|
+
return this.elementAtHeight(height - top).moveY(top);
|
|
6093
|
+
}
|
|
6094
|
+
/**
|
|
6095
|
+
Find the text line or block widget at the given vertical
|
|
6096
|
+
position (which is interpreted as relative to the [top of the
|
|
6097
|
+
document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)
|
|
6098
|
+
*/
|
|
6099
|
+
elementAtHeight(height) {
|
|
6011
6100
|
this.readMeasured();
|
|
6012
|
-
return this.viewState.
|
|
6101
|
+
return this.viewState.elementAtHeight(height);
|
|
6013
6102
|
}
|
|
6014
6103
|
/**
|
|
6015
6104
|
Find information for the visual line (see
|
|
@@ -6021,20 +6110,43 @@ class EditorView {
|
|
|
6021
6110
|
Defaults to treating `height` as a screen position. See
|
|
6022
6111
|
[`blockAtHeight`](https://codemirror.net/6/docs/ref/#view.EditorView.blockAtHeight) for the
|
|
6023
6112
|
interpretation of the `docTop` parameter.
|
|
6113
|
+
|
|
6114
|
+
*Deprecated: use `lineBlockAtHeight` instead.*
|
|
6024
6115
|
*/
|
|
6025
6116
|
visualLineAtHeight(height, docTop) {
|
|
6117
|
+
let top = ensureTop(docTop, this);
|
|
6118
|
+
return this.lineBlockAtHeight(height - top).moveY(top);
|
|
6119
|
+
}
|
|
6120
|
+
/**
|
|
6121
|
+
Find the line block (see
|
|
6122
|
+
[`lineBlockAt`](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) at the given
|
|
6123
|
+
height.
|
|
6124
|
+
*/
|
|
6125
|
+
lineBlockAtHeight(height) {
|
|
6026
6126
|
this.readMeasured();
|
|
6027
|
-
return this.viewState.
|
|
6127
|
+
return this.viewState.lineBlockAtHeight(height);
|
|
6028
6128
|
}
|
|
6029
6129
|
/**
|
|
6030
6130
|
Iterate over the height information of the visual lines in the
|
|
6031
6131
|
viewport. The heights of lines are reported relative to the
|
|
6032
6132
|
given document top, which defaults to the screen position of the
|
|
6033
6133
|
document (forcing a layout).
|
|
6134
|
+
|
|
6135
|
+
*Deprecated: use `viewportLineBlocks` instead.*
|
|
6034
6136
|
*/
|
|
6035
6137
|
viewportLines(f, docTop) {
|
|
6036
|
-
let
|
|
6037
|
-
|
|
6138
|
+
let top = ensureTop(docTop, this);
|
|
6139
|
+
for (let line of this.viewportLineBlocks)
|
|
6140
|
+
f(line.moveY(top));
|
|
6141
|
+
}
|
|
6142
|
+
/**
|
|
6143
|
+
Get the extent and vertical position of all [line
|
|
6144
|
+
blocks](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) in the viewport. Positions
|
|
6145
|
+
are relative to the [top of the
|
|
6146
|
+
document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop);
|
|
6147
|
+
*/
|
|
6148
|
+
get viewportLineBlocks() {
|
|
6149
|
+
return this.viewState.viewportLines;
|
|
6038
6150
|
}
|
|
6039
6151
|
/**
|
|
6040
6152
|
Find the extent and height of the visual line (a range delimited
|
|
@@ -6045,9 +6157,22 @@ class EditorView {
|
|
|
6045
6157
|
argument, which defaults to 0 for this method. You can pass
|
|
6046
6158
|
`view.contentDOM.getBoundingClientRect().top` here to get screen
|
|
6047
6159
|
coordinates.
|
|
6160
|
+
|
|
6161
|
+
*Deprecated: use `lineBlockAt` instead.*
|
|
6048
6162
|
*/
|
|
6049
6163
|
visualLineAt(pos, docTop = 0) {
|
|
6050
|
-
return this.
|
|
6164
|
+
return this.lineBlockAt(pos).moveY(docTop + this.viewState.paddingTop);
|
|
6165
|
+
}
|
|
6166
|
+
/**
|
|
6167
|
+
Find the line block around the given document position. A line
|
|
6168
|
+
block is a range delimited on both sides by either a
|
|
6169
|
+
non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^range) line breaks, or the
|
|
6170
|
+
start/end of the document. It will usually just hold a line of
|
|
6171
|
+
text, but may be broken into multiple textblocks by block
|
|
6172
|
+
widgets.
|
|
6173
|
+
*/
|
|
6174
|
+
lineBlockAt(pos) {
|
|
6175
|
+
return this.viewState.lineBlockAt(pos);
|
|
6051
6176
|
}
|
|
6052
6177
|
/**
|
|
6053
6178
|
The editor's total content height.
|
|
@@ -6380,8 +6505,9 @@ search match).
|
|
|
6380
6505
|
EditorView.announce = state.StateEffect.define();
|
|
6381
6506
|
// Maximum line length for which we compute accurate bidi info
|
|
6382
6507
|
const MaxBidiLine = 4096;
|
|
6383
|
-
|
|
6384
|
-
|
|
6508
|
+
// FIXME remove this and its callers on next breaking release
|
|
6509
|
+
function ensureTop(given, view) {
|
|
6510
|
+
return (given == null ? view.contentDOM.getBoundingClientRect().top : given) + view.viewState.paddingTop;
|
|
6385
6511
|
}
|
|
6386
6512
|
let resizeDebounce = -1;
|
|
6387
6513
|
function ensureGlobalHandler() {
|
|
@@ -6732,7 +6858,7 @@ function wrappedLine(view, pos, inside) {
|
|
|
6732
6858
|
type: exports.BlockType.Text };
|
|
6733
6859
|
}
|
|
6734
6860
|
function blockAt(view, pos) {
|
|
6735
|
-
let line = view.
|
|
6861
|
+
let line = view.lineBlockAt(pos);
|
|
6736
6862
|
if (Array.isArray(line.type))
|
|
6737
6863
|
for (let l of line.type) {
|
|
6738
6864
|
if (l.to > pos || l.to == pos && (l.to == line.to || l.type == exports.BlockType.Text))
|
|
@@ -7123,7 +7249,7 @@ const activeLineHighlighter = ViewPlugin.fromClass(class {
|
|
|
7123
7249
|
for (let r of view.state.selection.ranges) {
|
|
7124
7250
|
if (!r.empty)
|
|
7125
7251
|
return Decoration.none;
|
|
7126
|
-
let line = view.
|
|
7252
|
+
let line = view.lineBlockAt(r.head);
|
|
7127
7253
|
if (line.from > lastLineStart) {
|
|
7128
7254
|
deco.push(lineDeco.range(line.from));
|
|
7129
7255
|
lastLineStart = line.from;
|