@codemirror/view 0.19.21 → 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 +16 -0
- package/dist/index.cjs +189 -104
- package/dist/index.d.ts +44 -2
- package/dist/index.js +189 -104
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## 0.19.22 (2021-11-30)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix an issue where editors with large vertical padding (for example via `scrollPastEnd`) could sometimes lose their scroll position on Chrome.
|
|
6
|
+
|
|
7
|
+
Avoid some unnecessary DOM measuring work by more carefully checking whether it is needed.
|
|
8
|
+
|
|
9
|
+
### New features
|
|
10
|
+
|
|
11
|
+
The new `elementAtHeight`, `lineBlockAtHeight`, and `lineBlockAt` methods provide a simpler and more efficient replacement for the (now deprecated) `blockAtHeight`, `visualLineAtHeight`, and `visualLineAt` methods.
|
|
12
|
+
|
|
13
|
+
The editor view now exports a `documentTop` getter that gives you the vertical position of the top of the document. All height info is queried and reported relative to this top.
|
|
14
|
+
|
|
15
|
+
The editor view's new `viewportLineBlocks` property provides an array of in-viewport line blocks, and replaces the (now deprecated) `viewportLines` method.
|
|
16
|
+
|
|
1
17
|
## 0.19.21 (2021-11-26)
|
|
2
18
|
|
|
3
19
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -1988,10 +1988,7 @@ class DocView extends ContentView {
|
|
|
1988
1988
|
let prevDeco = this.decorations, deco = this.updateDeco();
|
|
1989
1989
|
let decoDiff = findChangedDeco(prevDeco, deco, update.changes);
|
|
1990
1990
|
changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
|
|
1991
|
-
if (this.dirty == 0 /* Not */ && changedRanges.length == 0
|
|
1992
|
-
!(update.flags & 4 /* Viewport */) &&
|
|
1993
|
-
update.state.selection.main.from >= this.view.viewport.from &&
|
|
1994
|
-
update.state.selection.main.to <= this.view.viewport.to) {
|
|
1991
|
+
if (this.dirty == 0 /* Not */ && changedRanges.length == 0) {
|
|
1995
1992
|
return false;
|
|
1996
1993
|
}
|
|
1997
1994
|
else {
|
|
@@ -2014,6 +2011,7 @@ class DocView extends ContentView {
|
|
|
2014
2011
|
// Used both by update and checkLayout do perform the actual DOM
|
|
2015
2012
|
// update
|
|
2016
2013
|
updateInner(changes, deco, oldLength) {
|
|
2014
|
+
this.view.viewState.mustMeasureContent = true;
|
|
2017
2015
|
this.updateChildren(changes, deco, oldLength);
|
|
2018
2016
|
let { observer } = this.view;
|
|
2019
2017
|
observer.ignore(() => {
|
|
@@ -2021,7 +2019,7 @@ class DocView extends ContentView {
|
|
|
2021
2019
|
// messes with the scroll position during DOM mutation (though
|
|
2022
2020
|
// no relayout is triggered and I cannot imagine how it can
|
|
2023
2021
|
// recompute the scroll position without a layout)
|
|
2024
|
-
this.dom.style.height = this.view.viewState.
|
|
2022
|
+
this.dom.style.height = this.view.viewState.contentHeight + "px";
|
|
2025
2023
|
this.dom.style.minWidth = this.minWidth ? this.minWidth + "px" : "";
|
|
2026
2024
|
// Chrome will sometimes, when DOM mutations occur directly
|
|
2027
2025
|
// around the selection, get confused and report a different
|
|
@@ -2306,7 +2304,7 @@ class DocView extends ContentView {
|
|
|
2306
2304
|
let next = i == vs.viewports.length ? null : vs.viewports[i];
|
|
2307
2305
|
let end = next ? next.from - 1 : this.length;
|
|
2308
2306
|
if (end > pos) {
|
|
2309
|
-
let height = vs.
|
|
2307
|
+
let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
|
|
2310
2308
|
deco.push(Decoration.replace({ widget: new BlockGapWidget(height), block: true, inclusive: true }).range(pos, end));
|
|
2311
2309
|
}
|
|
2312
2310
|
if (!next)
|
|
@@ -2913,13 +2911,14 @@ function domPosInText(node, x, y) {
|
|
|
2913
2911
|
}
|
|
2914
2912
|
function posAtCoords(view, { x, y }, precise, bias = -1) {
|
|
2915
2913
|
var _a;
|
|
2916
|
-
let content = view.contentDOM.getBoundingClientRect(),
|
|
2914
|
+
let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop;
|
|
2917
2915
|
let halfLine = view.defaultLineHeight / 2;
|
|
2916
|
+
let block, yOffset = y - docTop;
|
|
2918
2917
|
for (let bounced = false;;) {
|
|
2919
|
-
block = view.
|
|
2920
|
-
if (block.top >
|
|
2921
|
-
bias = block.top >
|
|
2922
|
-
|
|
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));
|
|
2923
2922
|
if (bounced)
|
|
2924
2923
|
return precise ? null : 0;
|
|
2925
2924
|
else
|
|
@@ -2927,8 +2926,9 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
|
|
|
2927
2926
|
}
|
|
2928
2927
|
if (block.type == exports.BlockType.Text)
|
|
2929
2928
|
break;
|
|
2930
|
-
|
|
2929
|
+
yOffset = bias > 0 ? block.bottom + halfLine : block.top - halfLine;
|
|
2931
2930
|
}
|
|
2931
|
+
y = docTop + yOffset;
|
|
2932
2932
|
let lineStart = block.from;
|
|
2933
2933
|
// Clip x to the viewport sides
|
|
2934
2934
|
x = Math.max(content.left + 1, Math.min(content.right - 1, x));
|
|
@@ -3041,17 +3041,17 @@ function moveVertically(view, start, forward, distance) {
|
|
|
3041
3041
|
return state.EditorSelection.cursor(startPos);
|
|
3042
3042
|
let goal = start.goalColumn, startY;
|
|
3043
3043
|
let rect = view.contentDOM.getBoundingClientRect();
|
|
3044
|
-
let startCoords = view.coordsAtPos(startPos);
|
|
3044
|
+
let startCoords = view.coordsAtPos(startPos), docTop = view.documentTop;
|
|
3045
3045
|
if (startCoords) {
|
|
3046
3046
|
if (goal == null)
|
|
3047
3047
|
goal = startCoords.left - rect.left;
|
|
3048
3048
|
startY = dir < 0 ? startCoords.top : startCoords.bottom;
|
|
3049
3049
|
}
|
|
3050
3050
|
else {
|
|
3051
|
-
let line = view.viewState.
|
|
3051
|
+
let line = view.viewState.lineBlockAt(startPos - docTop);
|
|
3052
3052
|
if (goal == null)
|
|
3053
3053
|
goal = Math.min(rect.right - rect.left, view.defaultCharacterWidth * (startPos - line.from));
|
|
3054
|
-
startY = dir < 0 ? line.top : line.bottom;
|
|
3054
|
+
startY = (dir < 0 ? line.top : line.bottom) + docTop;
|
|
3055
3055
|
}
|
|
3056
3056
|
let resolvedGoal = rect.left + goal;
|
|
3057
3057
|
let dist = distance !== null && distance !== void 0 ? distance : (view.defaultLineHeight >> 1);
|
|
@@ -3302,7 +3302,7 @@ class MouseSelection {
|
|
|
3302
3302
|
this.extend = startEvent.shiftKey;
|
|
3303
3303
|
this.multiple = view.state.facet(state.EditorState.allowMultipleSelections) && addsSelectionRange(view, startEvent);
|
|
3304
3304
|
this.dragMove = dragMovesSelection(view, startEvent);
|
|
3305
|
-
this.dragging = isInPrimarySelection(view, startEvent) ? null : false;
|
|
3305
|
+
this.dragging = isInPrimarySelection(view, startEvent) && getClickType(startEvent) == 1 ? null : false;
|
|
3306
3306
|
// When clicking outside of the selection, immediately apply the
|
|
3307
3307
|
// effect of starting the selection
|
|
3308
3308
|
if (this.dragging === false) {
|
|
@@ -3523,7 +3523,7 @@ function basicMouseSelection(view, event) {
|
|
|
3523
3523
|
let last = start, lastEvent = event;
|
|
3524
3524
|
return {
|
|
3525
3525
|
update(update) {
|
|
3526
|
-
if (update.
|
|
3526
|
+
if (update.docChanged) {
|
|
3527
3527
|
if (start)
|
|
3528
3528
|
start.pos = update.changes.mapPos(start.pos);
|
|
3529
3529
|
startSel = startSel.map(update.changes);
|
|
@@ -3787,7 +3787,10 @@ class HeightOracle {
|
|
|
3787
3787
|
return lines * this.lineHeight;
|
|
3788
3788
|
}
|
|
3789
3789
|
setDoc(doc) { this.doc = doc; return this; }
|
|
3790
|
-
|
|
3790
|
+
mustRefreshForStyle(whiteSpace, direction) {
|
|
3791
|
+
return (wrappingWhiteSpace.indexOf(whiteSpace) > -1) != this.lineWrapping || this.direction != direction;
|
|
3792
|
+
}
|
|
3793
|
+
mustRefreshForHeights(lineHeights) {
|
|
3791
3794
|
let newHeight = false;
|
|
3792
3795
|
for (let i = 0; i < lineHeights.length; i++) {
|
|
3793
3796
|
let h = lineHeights[i];
|
|
@@ -3799,7 +3802,7 @@ class HeightOracle {
|
|
|
3799
3802
|
this.heightSamples[Math.floor(h * 10)] = true;
|
|
3800
3803
|
}
|
|
3801
3804
|
}
|
|
3802
|
-
return newHeight
|
|
3805
|
+
return newHeight;
|
|
3803
3806
|
}
|
|
3804
3807
|
refresh(whiteSpace, direction, lineHeight, charWidth, lineLength, knownHeights) {
|
|
3805
3808
|
let lineWrapping = wrappingWhiteSpace.indexOf(whiteSpace) > -1;
|
|
@@ -3853,7 +3856,8 @@ class BlockInfo {
|
|
|
3853
3856
|
*/
|
|
3854
3857
|
length,
|
|
3855
3858
|
/**
|
|
3856
|
-
The top position of the element
|
|
3859
|
+
The top position of the element (relative to the top of the
|
|
3860
|
+
document).
|
|
3857
3861
|
*/
|
|
3858
3862
|
top,
|
|
3859
3863
|
/**
|
|
@@ -3887,6 +3891,12 @@ class BlockInfo {
|
|
|
3887
3891
|
.concat(Array.isArray(other.type) ? other.type : [other]);
|
|
3888
3892
|
return new BlockInfo(this.from, this.length + other.length, this.top, this.height + other.height, detail);
|
|
3889
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
|
+
}
|
|
3890
3900
|
}
|
|
3891
3901
|
var QueryType;
|
|
3892
3902
|
(function (QueryType) {
|
|
@@ -3894,7 +3904,7 @@ var QueryType;
|
|
|
3894
3904
|
QueryType[QueryType["ByHeight"] = 1] = "ByHeight";
|
|
3895
3905
|
QueryType[QueryType["ByPosNoHeight"] = 2] = "ByPosNoHeight";
|
|
3896
3906
|
})(QueryType || (QueryType = {}));
|
|
3897
|
-
const Epsilon = 1e-
|
|
3907
|
+
const Epsilon = 1e-3;
|
|
3898
3908
|
class HeightMap {
|
|
3899
3909
|
constructor(length, // The number of characters covered
|
|
3900
3910
|
height, // Height of this part of the document
|
|
@@ -4121,22 +4131,30 @@ class HeightMapGap extends HeightMap {
|
|
|
4121
4131
|
// can't be widgets or collapsed ranges in those lines, because
|
|
4122
4132
|
// they would already have been added to the heightmap (gaps
|
|
4123
4133
|
// only contain plain text).
|
|
4124
|
-
let nodes = [], pos = Math.max(offset, measured.from);
|
|
4134
|
+
let nodes = [], pos = Math.max(offset, measured.from), singleHeight = -1;
|
|
4135
|
+
let wasChanged = oracle.heightChanged;
|
|
4125
4136
|
if (measured.from > offset)
|
|
4126
4137
|
nodes.push(new HeightMapGap(measured.from - offset - 1).updateHeight(oracle, offset));
|
|
4127
4138
|
while (pos <= end && measured.more) {
|
|
4128
4139
|
let len = oracle.doc.lineAt(pos).length;
|
|
4129
4140
|
if (nodes.length)
|
|
4130
4141
|
nodes.push(null);
|
|
4131
|
-
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);
|
|
4132
4148
|
line.outdated = false;
|
|
4133
4149
|
nodes.push(line);
|
|
4134
4150
|
pos += len + 1;
|
|
4135
4151
|
}
|
|
4136
4152
|
if (pos <= end)
|
|
4137
4153
|
nodes.push(null, new HeightMapGap(end - pos).updateHeight(oracle, pos));
|
|
4138
|
-
|
|
4139
|
-
|
|
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;
|
|
4140
4158
|
}
|
|
4141
4159
|
else if (force || this.outdated) {
|
|
4142
4160
|
this.setHeight(oracle, oracle.heightForGap(offset, offset + this.length));
|
|
@@ -4494,7 +4512,8 @@ class ViewState {
|
|
|
4494
4512
|
this.inView = true;
|
|
4495
4513
|
this.paddingTop = 0;
|
|
4496
4514
|
this.paddingBottom = 0;
|
|
4497
|
-
this.
|
|
4515
|
+
this.contentDOMWidth = 0;
|
|
4516
|
+
this.contentDOMHeight = 0;
|
|
4498
4517
|
this.editorHeight = 0;
|
|
4499
4518
|
this.heightOracle = new HeightOracle;
|
|
4500
4519
|
// See VP.MaxDOMHeight
|
|
@@ -4502,6 +4521,9 @@ class ViewState {
|
|
|
4502
4521
|
this.scrollTarget = null;
|
|
4503
4522
|
// Briefly set to true when printing, to disable viewport limiting
|
|
4504
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;
|
|
4505
4527
|
this.visibleRanges = [];
|
|
4506
4528
|
// Cursor 'assoc' is only significant when the cursor is on a line
|
|
4507
4529
|
// wrap point, where it must stick to the character that it is
|
|
@@ -4514,6 +4536,7 @@ class ViewState {
|
|
|
4514
4536
|
this.mustEnforceCursorAssoc = false;
|
|
4515
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)]);
|
|
4516
4538
|
this.viewport = this.getViewport(0, null);
|
|
4539
|
+
this.updateViewportLines();
|
|
4517
4540
|
this.updateForViewport();
|
|
4518
4541
|
this.lineGaps = this.ensureLineGaps([]);
|
|
4519
4542
|
this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(false)));
|
|
@@ -4524,7 +4547,7 @@ class ViewState {
|
|
|
4524
4547
|
for (let i = 0; i <= 1; i++) {
|
|
4525
4548
|
let pos = i ? main.head : main.anchor;
|
|
4526
4549
|
if (!viewports.some(({ from, to }) => pos >= from && pos <= to)) {
|
|
4527
|
-
let { from, to } = this.
|
|
4550
|
+
let { from, to } = this.lineBlockAt(pos);
|
|
4528
4551
|
viewports.push(new Viewport(from, to));
|
|
4529
4552
|
}
|
|
4530
4553
|
}
|
|
@@ -4532,6 +4555,12 @@ class ViewState {
|
|
|
4532
4555
|
this.scaler = this.heightMap.height <= 7000000 /* MaxDOMHeight */ ? IdScaler :
|
|
4533
4556
|
new BigScaler(this.heightOracle.doc, this.heightMap, this.viewports);
|
|
4534
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
|
+
}
|
|
4535
4564
|
update(update, scrollTarget = null) {
|
|
4536
4565
|
let prev = this.state;
|
|
4537
4566
|
this.state = update.state;
|
|
@@ -4546,6 +4575,9 @@ class ViewState {
|
|
|
4546
4575
|
if (scrollTarget && (scrollTarget.range.head < viewport.from || scrollTarget.range.head > viewport.to) ||
|
|
4547
4576
|
!this.viewportIsAppropriate(viewport))
|
|
4548
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();
|
|
4549
4581
|
this.viewport = viewport;
|
|
4550
4582
|
this.updateForViewport();
|
|
4551
4583
|
if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
|
|
@@ -4557,13 +4589,17 @@ class ViewState {
|
|
|
4557
4589
|
update.state.selection.main.empty && update.state.selection.main.assoc)
|
|
4558
4590
|
this.mustEnforceCursorAssoc = true;
|
|
4559
4591
|
}
|
|
4560
|
-
measure(
|
|
4561
|
-
let dom =
|
|
4562
|
-
let
|
|
4563
|
-
|
|
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;
|
|
4564
4602
|
// Vertical padding
|
|
4565
|
-
let style = window.getComputedStyle(dom);
|
|
4566
|
-
whiteSpace = style.whiteSpace, direction = (style.direction == "rtl" ? exports.Direction.RTL : exports.Direction.LTR);
|
|
4567
4603
|
let paddingTop = parseInt(style.paddingTop) || 0, paddingBottom = parseInt(style.paddingBottom) || 0;
|
|
4568
4604
|
if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) {
|
|
4569
4605
|
result |= 8 /* Geometry */;
|
|
@@ -4578,39 +4614,42 @@ class ViewState {
|
|
|
4578
4614
|
this.inView = this.pixelViewport.bottom > this.pixelViewport.top && this.pixelViewport.right > this.pixelViewport.left;
|
|
4579
4615
|
if (!this.inView)
|
|
4580
4616
|
return 0;
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
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();
|
|
4588
4624
|
refresh = oracle.refresh(whiteSpace, direction, lineHeight, charWidth, contentWidth / charWidth, lineHeights);
|
|
4589
4625
|
if (refresh) {
|
|
4590
|
-
docView.minWidth = 0;
|
|
4626
|
+
view.docView.minWidth = 0;
|
|
4591
4627
|
result |= 8 /* Geometry */;
|
|
4592
4628
|
}
|
|
4593
4629
|
}
|
|
4594
|
-
if (this.
|
|
4595
|
-
this.
|
|
4630
|
+
if (this.contentDOMWidth != contentWidth) {
|
|
4631
|
+
this.contentDOMWidth = contentWidth;
|
|
4596
4632
|
result |= 8 /* Geometry */;
|
|
4597
4633
|
}
|
|
4598
|
-
if (this.editorHeight !=
|
|
4599
|
-
this.editorHeight =
|
|
4634
|
+
if (this.editorHeight != view.scrollDOM.clientHeight) {
|
|
4635
|
+
this.editorHeight = view.scrollDOM.clientHeight;
|
|
4600
4636
|
result |= 8 /* Geometry */;
|
|
4601
4637
|
}
|
|
4602
4638
|
if (dTop > 0 && dBottom > 0)
|
|
4603
4639
|
bias = Math.max(dTop, dBottom);
|
|
4604
4640
|
else if (dTop < 0 && dBottom < 0)
|
|
4605
4641
|
bias = Math.min(dTop, dBottom);
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
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)
|
|
4613
4650
|
this.viewport = this.getViewport(bias, this.scrollTarget);
|
|
4651
|
+
if ((result & 2 /* Height */) || viewportChange)
|
|
4652
|
+
this.updateViewportLines();
|
|
4614
4653
|
this.updateForViewport();
|
|
4615
4654
|
if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
|
|
4616
4655
|
this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps));
|
|
@@ -4621,12 +4660,12 @@ class ViewState {
|
|
|
4621
4660
|
// to a line end is going to trigger a layout anyway, so it
|
|
4622
4661
|
// can't be a pure write. It should be rare that it does any
|
|
4623
4662
|
// writing.
|
|
4624
|
-
docView.enforceCursorAssoc();
|
|
4663
|
+
view.docView.enforceCursorAssoc();
|
|
4625
4664
|
}
|
|
4626
4665
|
return result;
|
|
4627
4666
|
}
|
|
4628
|
-
get visibleTop() { return this.scaler.fromDOM(this.pixelViewport.top
|
|
4629
|
-
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); }
|
|
4630
4669
|
getViewport(bias, scrollTarget) {
|
|
4631
4670
|
// This will divide VP.Margin between the top and the
|
|
4632
4671
|
// bottom, depending on the bias (the change in viewport position
|
|
@@ -4686,12 +4725,12 @@ class ViewState {
|
|
|
4686
4725
|
// This won't work at all in predominantly right-to-left text.
|
|
4687
4726
|
if (this.heightOracle.direction != exports.Direction.LTR)
|
|
4688
4727
|
return gaps;
|
|
4689
|
-
|
|
4728
|
+
for (let line of this.viewportLines) {
|
|
4690
4729
|
if (line.length < 4000 /* DoubleMargin */)
|
|
4691
|
-
|
|
4730
|
+
continue;
|
|
4692
4731
|
let structure = lineStructure(line.from, line.to, this.state);
|
|
4693
4732
|
if (structure.total < 4000 /* DoubleMargin */)
|
|
4694
|
-
|
|
4733
|
+
continue;
|
|
4695
4734
|
let viewFrom, viewTo;
|
|
4696
4735
|
if (this.heightOracle.lineWrapping) {
|
|
4697
4736
|
let marginHeight = (2000 /* Margin */ / this.heightOracle.lineLength) * this.heightOracle.lineHeight;
|
|
@@ -4721,7 +4760,7 @@ class ViewState {
|
|
|
4721
4760
|
Math.abs(gap.from - from) < 1000 /* HalfMargin */ && Math.abs(gap.to - to) < 1000 /* HalfMargin */) ||
|
|
4722
4761
|
new LineGap(from, to, this.gapSize(line, from, to, structure)));
|
|
4723
4762
|
}
|
|
4724
|
-
}
|
|
4763
|
+
}
|
|
4725
4764
|
return gaps;
|
|
4726
4765
|
}
|
|
4727
4766
|
gapSize(line, from, to, structure) {
|
|
@@ -4753,27 +4792,18 @@ class ViewState {
|
|
|
4753
4792
|
this.visibleRanges = ranges;
|
|
4754
4793
|
return changed ? 4 /* Viewport */ : 0;
|
|
4755
4794
|
}
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
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);
|
|
4759
4798
|
}
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height, editorTop), QueryType.ByHeight, 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);
|
|
4763
4801
|
}
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height, editorTop), this.state.doc, editorTop, 0), this.scaler, editorTop);
|
|
4767
|
-
}
|
|
4768
|
-
forEachLine(from, to, f, editorTop) {
|
|
4769
|
-
editorTop += this.paddingTop;
|
|
4770
|
-
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);
|
|
4771
4804
|
}
|
|
4772
4805
|
get contentHeight() {
|
|
4773
|
-
return this.
|
|
4774
|
-
}
|
|
4775
|
-
get domHeight() {
|
|
4776
|
-
return this.scaler.toDOM(this.heightMap.height, this.paddingTop);
|
|
4806
|
+
return this.scaler.toDOM(this.heightMap.height) + this.paddingTop + this.paddingBottom;
|
|
4777
4807
|
}
|
|
4778
4808
|
}
|
|
4779
4809
|
class Viewport {
|
|
@@ -4870,36 +4900,34 @@ class BigScaler {
|
|
|
4870
4900
|
base = obj.bottom;
|
|
4871
4901
|
}
|
|
4872
4902
|
}
|
|
4873
|
-
toDOM(n
|
|
4874
|
-
n -= top;
|
|
4903
|
+
toDOM(n) {
|
|
4875
4904
|
for (let i = 0, base = 0, domBase = 0;; i++) {
|
|
4876
4905
|
let vp = i < this.viewports.length ? this.viewports[i] : null;
|
|
4877
4906
|
if (!vp || n < vp.top)
|
|
4878
|
-
return domBase + (n - base) * this.scale
|
|
4907
|
+
return domBase + (n - base) * this.scale;
|
|
4879
4908
|
if (n <= vp.bottom)
|
|
4880
|
-
return vp.domTop + (n - vp.top)
|
|
4909
|
+
return vp.domTop + (n - vp.top);
|
|
4881
4910
|
base = vp.bottom;
|
|
4882
4911
|
domBase = vp.domBottom;
|
|
4883
4912
|
}
|
|
4884
4913
|
}
|
|
4885
|
-
fromDOM(n
|
|
4886
|
-
n -= top;
|
|
4914
|
+
fromDOM(n) {
|
|
4887
4915
|
for (let i = 0, base = 0, domBase = 0;; i++) {
|
|
4888
4916
|
let vp = i < this.viewports.length ? this.viewports[i] : null;
|
|
4889
4917
|
if (!vp || n < vp.domTop)
|
|
4890
|
-
return base + (n - domBase) / this.scale
|
|
4918
|
+
return base + (n - domBase) / this.scale;
|
|
4891
4919
|
if (n <= vp.domBottom)
|
|
4892
|
-
return vp.top + (n - vp.domTop)
|
|
4920
|
+
return vp.top + (n - vp.domTop);
|
|
4893
4921
|
base = vp.bottom;
|
|
4894
4922
|
domBase = vp.domBottom;
|
|
4895
4923
|
}
|
|
4896
4924
|
}
|
|
4897
4925
|
}
|
|
4898
|
-
function scaleBlock(block, scaler
|
|
4926
|
+
function scaleBlock(block, scaler) {
|
|
4899
4927
|
if (scaler.scale == 1)
|
|
4900
4928
|
return block;
|
|
4901
|
-
let bTop = scaler.toDOM(block.top
|
|
4902
|
-
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);
|
|
4903
4931
|
}
|
|
4904
4932
|
|
|
4905
4933
|
const theme = state.Facet.define({ combine: strs => strs.join(" ") });
|
|
@@ -5878,11 +5906,11 @@ class EditorView {
|
|
|
5878
5906
|
for (let i = 0;; i++) {
|
|
5879
5907
|
this.updateState = 1 /* Measuring */;
|
|
5880
5908
|
let oldViewport = this.viewport;
|
|
5881
|
-
let changed = this.viewState.measure(this
|
|
5909
|
+
let changed = this.viewState.measure(this);
|
|
5882
5910
|
if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
|
|
5883
5911
|
break;
|
|
5884
5912
|
if (i > 5) {
|
|
5885
|
-
console.warn("Viewport failed to stabilize");
|
|
5913
|
+
console.warn(this.measureRequests.length ? "Measure loop restarted more than 5 times" : "Viewport failed to stabilize");
|
|
5886
5914
|
break;
|
|
5887
5915
|
}
|
|
5888
5916
|
let measuring = [];
|
|
@@ -5898,7 +5926,7 @@ class EditorView {
|
|
|
5898
5926
|
return BadMeasure;
|
|
5899
5927
|
}
|
|
5900
5928
|
});
|
|
5901
|
-
let update = new ViewUpdate(this, this.state);
|
|
5929
|
+
let update = new ViewUpdate(this, this.state), redrawn = false;
|
|
5902
5930
|
update.flags |= changed;
|
|
5903
5931
|
if (!updated)
|
|
5904
5932
|
updated = update;
|
|
@@ -5908,13 +5936,15 @@ class EditorView {
|
|
|
5908
5936
|
if (!update.empty) {
|
|
5909
5937
|
this.updatePlugins(update);
|
|
5910
5938
|
this.inputState.update(update);
|
|
5939
|
+
this.updateAttrs();
|
|
5940
|
+
redrawn = this.docView.update(update);
|
|
5911
5941
|
}
|
|
5912
|
-
this.updateAttrs();
|
|
5913
|
-
let redrawn = changed > 0 && this.docView.update(update);
|
|
5914
5942
|
for (let i = 0; i < measuring.length; i++)
|
|
5915
5943
|
if (measured[i] != BadMeasure) {
|
|
5916
5944
|
try {
|
|
5917
|
-
measuring[i]
|
|
5945
|
+
let m = measuring[i];
|
|
5946
|
+
if (m.write)
|
|
5947
|
+
m.write(measured[i], this);
|
|
5918
5948
|
}
|
|
5919
5949
|
catch (e) {
|
|
5920
5950
|
logException(this.state, e);
|
|
@@ -5924,8 +5954,8 @@ class EditorView {
|
|
|
5924
5954
|
this.docView.scrollIntoView(this.viewState.scrollTarget);
|
|
5925
5955
|
this.viewState.scrollTarget = null;
|
|
5926
5956
|
}
|
|
5927
|
-
if (
|
|
5928
|
-
this.docView.updateSelection(
|
|
5957
|
+
if (redrawn)
|
|
5958
|
+
this.docView.updateSelection(true);
|
|
5929
5959
|
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to && this.measureRequests.length == 0)
|
|
5930
5960
|
break;
|
|
5931
5961
|
}
|
|
@@ -6038,6 +6068,13 @@ class EditorView {
|
|
|
6038
6068
|
return null;
|
|
6039
6069
|
}
|
|
6040
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
|
+
/**
|
|
6041
6078
|
Find the line or block widget at the given vertical position.
|
|
6042
6079
|
|
|
6043
6080
|
By default, this position is interpreted as a screen position,
|
|
@@ -6047,10 +6084,21 @@ class EditorView {
|
|
|
6047
6084
|
position, or a precomputed document top
|
|
6048
6085
|
(`view.contentDOM.getBoundingClientRect().top`) to limit layout
|
|
6049
6086
|
queries.
|
|
6087
|
+
|
|
6088
|
+
*Deprecated: use `blockAtHeight` instead.*
|
|
6050
6089
|
*/
|
|
6051
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) {
|
|
6052
6100
|
this.readMeasured();
|
|
6053
|
-
return this.viewState.
|
|
6101
|
+
return this.viewState.elementAtHeight(height);
|
|
6054
6102
|
}
|
|
6055
6103
|
/**
|
|
6056
6104
|
Find information for the visual line (see
|
|
@@ -6062,20 +6110,43 @@ class EditorView {
|
|
|
6062
6110
|
Defaults to treating `height` as a screen position. See
|
|
6063
6111
|
[`blockAtHeight`](https://codemirror.net/6/docs/ref/#view.EditorView.blockAtHeight) for the
|
|
6064
6112
|
interpretation of the `docTop` parameter.
|
|
6113
|
+
|
|
6114
|
+
*Deprecated: use `lineBlockAtHeight` instead.*
|
|
6065
6115
|
*/
|
|
6066
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) {
|
|
6067
6126
|
this.readMeasured();
|
|
6068
|
-
return this.viewState.
|
|
6127
|
+
return this.viewState.lineBlockAtHeight(height);
|
|
6069
6128
|
}
|
|
6070
6129
|
/**
|
|
6071
6130
|
Iterate over the height information of the visual lines in the
|
|
6072
6131
|
viewport. The heights of lines are reported relative to the
|
|
6073
6132
|
given document top, which defaults to the screen position of the
|
|
6074
6133
|
document (forcing a layout).
|
|
6134
|
+
|
|
6135
|
+
*Deprecated: use `viewportLineBlocks` instead.*
|
|
6075
6136
|
*/
|
|
6076
6137
|
viewportLines(f, docTop) {
|
|
6077
|
-
let
|
|
6078
|
-
|
|
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;
|
|
6079
6150
|
}
|
|
6080
6151
|
/**
|
|
6081
6152
|
Find the extent and height of the visual line (a range delimited
|
|
@@ -6086,9 +6157,22 @@ class EditorView {
|
|
|
6086
6157
|
argument, which defaults to 0 for this method. You can pass
|
|
6087
6158
|
`view.contentDOM.getBoundingClientRect().top` here to get screen
|
|
6088
6159
|
coordinates.
|
|
6160
|
+
|
|
6161
|
+
*Deprecated: use `lineBlockAt` instead.*
|
|
6089
6162
|
*/
|
|
6090
6163
|
visualLineAt(pos, docTop = 0) {
|
|
6091
|
-
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);
|
|
6092
6176
|
}
|
|
6093
6177
|
/**
|
|
6094
6178
|
The editor's total content height.
|
|
@@ -6421,8 +6505,9 @@ search match).
|
|
|
6421
6505
|
EditorView.announce = state.StateEffect.define();
|
|
6422
6506
|
// Maximum line length for which we compute accurate bidi info
|
|
6423
6507
|
const MaxBidiLine = 4096;
|
|
6424
|
-
|
|
6425
|
-
|
|
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;
|
|
6426
6511
|
}
|
|
6427
6512
|
let resizeDebounce = -1;
|
|
6428
6513
|
function ensureGlobalHandler() {
|
|
@@ -6773,7 +6858,7 @@ function wrappedLine(view, pos, inside) {
|
|
|
6773
6858
|
type: exports.BlockType.Text };
|
|
6774
6859
|
}
|
|
6775
6860
|
function blockAt(view, pos) {
|
|
6776
|
-
let line = view.
|
|
6861
|
+
let line = view.lineBlockAt(pos);
|
|
6777
6862
|
if (Array.isArray(line.type))
|
|
6778
6863
|
for (let l of line.type) {
|
|
6779
6864
|
if (l.to > pos || l.to == pos && (l.to == line.to || l.type == exports.BlockType.Text))
|
|
@@ -7164,7 +7249,7 @@ const activeLineHighlighter = ViewPlugin.fromClass(class {
|
|
|
7164
7249
|
for (let r of view.state.selection.ranges) {
|
|
7165
7250
|
if (!r.empty)
|
|
7166
7251
|
return Decoration.none;
|
|
7167
|
-
let line = view.
|
|
7252
|
+
let line = view.lineBlockAt(r.head);
|
|
7168
7253
|
if (line.from > lastLineStart) {
|
|
7169
7254
|
deco.push(lineDeco.range(line.from));
|
|
7170
7255
|
lastLineStart = line.from;
|