@codemirror/view 6.39.14 → 6.39.16
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 +20 -0
- package/dist/index.cjs +89 -55
- package/dist/index.js +89 -55
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
## 6.39.16 (2026-03-02)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Perform scroll stabilization on the document or wrapping scrollable elements, when the user scrolls the editor.
|
|
6
|
+
|
|
7
|
+
Fix an issue where changing decorations right before a composition could end up corrupting the visible DOM.
|
|
8
|
+
|
|
9
|
+
Fix an issue where some types of text input over a selection would be read as happening in wrong position.
|
|
10
|
+
|
|
11
|
+
## 6.39.15 (2026-02-20)
|
|
12
|
+
|
|
13
|
+
### Bug fixes
|
|
14
|
+
|
|
15
|
+
Fix a regression where the editor would forget previously measured line heights without good reason.
|
|
16
|
+
|
|
17
|
+
Fix an issue where scrolling the cursor into view sometimes wouldn't work on Chrome Android.
|
|
18
|
+
|
|
19
|
+
Fix a bug that broke composition inside of block wrappers.
|
|
20
|
+
|
|
1
21
|
## 6.39.14 (2026-02-12)
|
|
2
22
|
|
|
3
23
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -624,16 +624,16 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
|
|
|
624
624
|
}
|
|
625
625
|
}
|
|
626
626
|
}
|
|
627
|
-
function scrollableParents(dom) {
|
|
628
|
-
let doc = dom.ownerDocument, x, y;
|
|
627
|
+
function scrollableParents(dom, getX = true) {
|
|
628
|
+
let doc = dom.ownerDocument, x = null, y = null;
|
|
629
629
|
for (let cur = dom.parentNode; cur;) {
|
|
630
|
-
if (cur == doc.body || (x && y)) {
|
|
630
|
+
if (cur == doc.body || ((!getX || x) && y)) {
|
|
631
631
|
break;
|
|
632
632
|
}
|
|
633
633
|
else if (cur.nodeType == 1) {
|
|
634
634
|
if (!y && cur.scrollHeight > cur.clientHeight)
|
|
635
635
|
y = cur;
|
|
636
|
-
if (!x && cur.scrollWidth > cur.clientWidth)
|
|
636
|
+
if (getX && !x && cur.scrollWidth > cur.clientWidth)
|
|
637
637
|
x = cur;
|
|
638
638
|
cur = cur.assignedSlot || cur.parentNode;
|
|
639
639
|
}
|
|
@@ -758,6 +758,8 @@ function atElementStart(doc, selection) {
|
|
|
758
758
|
}
|
|
759
759
|
}
|
|
760
760
|
function isScrolledToBottom(elt) {
|
|
761
|
+
if (elt instanceof Window)
|
|
762
|
+
return elt.pageYOffset > Math.max(0, elt.document.documentElement.scrollHeight - elt.innerHeight - 4);
|
|
761
763
|
return elt.scrollTop > Math.max(1, elt.scrollHeight - elt.clientHeight - 4);
|
|
762
764
|
}
|
|
763
765
|
function textNodeBefore(startNode, startOffset) {
|
|
@@ -2656,7 +2658,7 @@ class TileUpdate {
|
|
|
2656
2658
|
}
|
|
2657
2659
|
else if (tile.isText()) {
|
|
2658
2660
|
this.builder.ensureLine(null);
|
|
2659
|
-
if (!from && to == tile.length) {
|
|
2661
|
+
if (!from && to == tile.length && !this.cache.reused.has(tile)) {
|
|
2660
2662
|
this.builder.addText(tile.text, activeMarks, openMarks, this.cache.reuse(tile));
|
|
2661
2663
|
}
|
|
2662
2664
|
else {
|
|
@@ -2791,9 +2793,10 @@ class TileUpdate {
|
|
|
2791
2793
|
marks.push(tile);
|
|
2792
2794
|
else if (tile === null || tile === void 0 ? void 0 : tile.isLine())
|
|
2793
2795
|
line = tile;
|
|
2796
|
+
else if (tile instanceof BlockWrapperTile) ; // Ignore
|
|
2794
2797
|
else if (parent.nodeName == "DIV" && !line && parent != this.view.contentDOM)
|
|
2795
2798
|
line = new LineTile(parent, lineBaseAttrs);
|
|
2796
|
-
else
|
|
2799
|
+
else if (!line)
|
|
2797
2800
|
marks.push(MarkTile.of(new MarkDecoration({ tagName: parent.nodeName.toLowerCase(), attributes: getAttrs(parent) }), parent));
|
|
2798
2801
|
}
|
|
2799
2802
|
return { line: line, marks };
|
|
@@ -2959,6 +2962,8 @@ class DocView {
|
|
|
2959
2962
|
if (composition || changes.length) {
|
|
2960
2963
|
let oldTile = this.tile;
|
|
2961
2964
|
let builder = new TileUpdate(this.view, oldTile, this.blockWrappers, this.decorations, this.dynamicDecorationMap);
|
|
2965
|
+
if (composition && Tile.get(composition.text))
|
|
2966
|
+
builder.cache.reused.set(Tile.get(composition.text), 2 /* Reused.DOM */);
|
|
2962
2967
|
this.tile = builder.run(changes, composition);
|
|
2963
2968
|
destroyDropped(oldTile, builder.cache.reused);
|
|
2964
2969
|
}
|
|
@@ -3419,6 +3424,19 @@ class DocView {
|
|
|
3419
3424
|
};
|
|
3420
3425
|
let { offsetWidth, offsetHeight } = this.view.scrollDOM;
|
|
3421
3426
|
scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, Math.max(Math.min(target.xMargin, offsetWidth), -offsetWidth), Math.max(Math.min(target.yMargin, offsetHeight), -offsetHeight), this.view.textDirection == exports.Direction.LTR);
|
|
3427
|
+
// On mobile browsers, the visual viewport may be smaller than the
|
|
3428
|
+
// actual reported viewport, causing scrollRectIntoView to fail to
|
|
3429
|
+
// scroll properly. Unfortunately, this visual viewport cannot be
|
|
3430
|
+
// updated directly, and scrollIntoView is the only way a script
|
|
3431
|
+
// can affect it. So this tries to kludge around the problem by
|
|
3432
|
+
// calling scrollIntoView on the scroll target's line.
|
|
3433
|
+
if (window.visualViewport && window.innerHeight - window.visualViewport.height > 1 &&
|
|
3434
|
+
(rect.top > window.pageYOffset + window.visualViewport.offsetTop + window.visualViewport.height ||
|
|
3435
|
+
rect.bottom < window.pageYOffset + window.visualViewport.offsetTop)) {
|
|
3436
|
+
let line = this.view.docView.lineAt(range.head, 1);
|
|
3437
|
+
if (line)
|
|
3438
|
+
line.dom.scrollIntoView({ block: "nearest" });
|
|
3439
|
+
}
|
|
3422
3440
|
}
|
|
3423
3441
|
lineHasWidget(pos) {
|
|
3424
3442
|
let scan = (child) => child.isWidget() || child.children.some(scan);
|
|
@@ -4143,7 +4161,7 @@ function domBoundsAround(tile, from, to, offset) {
|
|
|
4143
4161
|
}
|
|
4144
4162
|
function applyDOMChange(view, domChange) {
|
|
4145
4163
|
let change;
|
|
4146
|
-
let { newSel } = domChange, sel =
|
|
4164
|
+
let { newSel } = domChange, { state: state$1 } = view, sel = state$1.selection.main;
|
|
4147
4165
|
let lastKey = view.inputState.lastKeyTime > Date.now() - 100 ? view.inputState.lastKeyCode : -1;
|
|
4148
4166
|
if (domChange.bounds) {
|
|
4149
4167
|
let { from, to } = domChange.bounds;
|
|
@@ -4154,8 +4172,15 @@ function applyDOMChange(view, domChange) {
|
|
|
4154
4172
|
preferredPos = sel.to;
|
|
4155
4173
|
preferredSide = "end";
|
|
4156
4174
|
}
|
|
4157
|
-
let
|
|
4158
|
-
if (
|
|
4175
|
+
let cmp = state$1.doc.sliceString(from, to, LineBreakPlaceholder), selEnd, diff;
|
|
4176
|
+
if (!sel.empty && sel.from >= from && sel.to <= to && (domChange.typeOver || cmp != domChange.text) &&
|
|
4177
|
+
cmp.slice(0, sel.from - from) == domChange.text.slice(0, sel.from - from) &&
|
|
4178
|
+
cmp.slice(sel.to - from) == domChange.text.slice(selEnd = domChange.text.length - (cmp.length - (sel.to - from)))) {
|
|
4179
|
+
// This looks like a selection replacement
|
|
4180
|
+
change = { from: sel.from, to: sel.to,
|
|
4181
|
+
insert: state.Text.of(domChange.text.slice(sel.from - from, selEnd).split(LineBreakPlaceholder)) };
|
|
4182
|
+
}
|
|
4183
|
+
else if (diff = findDiff(cmp, domChange.text, preferredPos - from, preferredSide)) {
|
|
4159
4184
|
// Chrome inserts two newlines when pressing shift-enter at the
|
|
4160
4185
|
// end of a line. DomChange drops one of those.
|
|
4161
4186
|
if (browser.chrome && lastKey == 13 &&
|
|
@@ -4165,16 +4190,12 @@ function applyDOMChange(view, domChange) {
|
|
|
4165
4190
|
insert: state.Text.of(domChange.text.slice(diff.from, diff.toB).split(LineBreakPlaceholder)) };
|
|
4166
4191
|
}
|
|
4167
4192
|
}
|
|
4168
|
-
else if (newSel && (!view.hasFocus &&
|
|
4193
|
+
else if (newSel && (!view.hasFocus && state$1.facet(editable) || sameSelPos(newSel, sel))) {
|
|
4169
4194
|
newSel = null;
|
|
4170
4195
|
}
|
|
4171
4196
|
if (!change && !newSel)
|
|
4172
4197
|
return false;
|
|
4173
|
-
if (
|
|
4174
|
-
// Heuristic to notice typing over a selected character
|
|
4175
|
-
change = { from: sel.from, to: sel.to, insert: view.state.doc.slice(sel.from, sel.to) };
|
|
4176
|
-
}
|
|
4177
|
-
else if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 &&
|
|
4198
|
+
if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 &&
|
|
4178
4199
|
/^\. ?$/.test(change.insert.toString()) && view.contentDOM.getAttribute("autocorrect") == "off") {
|
|
4179
4200
|
// Detect insert-period-on-double-space Mac and Android behavior,
|
|
4180
4201
|
// and transform it into a regular space insert.
|
|
@@ -4182,18 +4203,7 @@ function applyDOMChange(view, domChange) {
|
|
|
4182
4203
|
newSel = state.EditorSelection.single(newSel.main.anchor - 1, newSel.main.head - 1);
|
|
4183
4204
|
change = { from: change.from, to: change.to, insert: state.Text.of([change.insert.toString().replace(".", " ")]) };
|
|
4184
4205
|
}
|
|
4185
|
-
else if (
|
|
4186
|
-
(change.from != sel.from || change.to != sel.to) &&
|
|
4187
|
-
(sel.to - sel.from) - (change.to - change.from) <= 4) {
|
|
4188
|
-
// If the change is inside the selection and covers most of it,
|
|
4189
|
-
// assume it is a selection replace (with identical characters at
|
|
4190
|
-
// the start/end not included in the diff)
|
|
4191
|
-
change = {
|
|
4192
|
-
from: sel.from, to: sel.to,
|
|
4193
|
-
insert: view.state.doc.slice(sel.from, change.from).append(change.insert).append(view.state.doc.slice(change.to, sel.to))
|
|
4194
|
-
};
|
|
4195
|
-
}
|
|
4196
|
-
else if (view.state.doc.lineAt(sel.from).to < sel.to && view.docView.lineHasWidget(sel.to) &&
|
|
4206
|
+
else if (state$1.doc.lineAt(sel.from).to < sel.to && view.docView.lineHasWidget(sel.to) &&
|
|
4197
4207
|
view.inputState.insertingTextAt > Date.now() - 50) {
|
|
4198
4208
|
// For a cross-line insertion, Chrome and Safari will crudely take
|
|
4199
4209
|
// the text of the line after the selection, flattening any
|
|
@@ -4202,7 +4212,7 @@ function applyDOMChange(view, domChange) {
|
|
|
4202
4212
|
// replace of the text provided by the beforeinput event.
|
|
4203
4213
|
change = {
|
|
4204
4214
|
from: sel.from, to: sel.to,
|
|
4205
|
-
insert:
|
|
4215
|
+
insert: state$1.toText(view.inputState.insertingText)
|
|
4206
4216
|
};
|
|
4207
4217
|
}
|
|
4208
4218
|
else if (browser.chrome && change && change.from == change.to && change.from == sel.head &&
|
|
@@ -4224,7 +4234,7 @@ function applyDOMChange(view, domChange) {
|
|
|
4224
4234
|
scrollIntoView = true;
|
|
4225
4235
|
userEvent = view.inputState.lastSelectionOrigin;
|
|
4226
4236
|
if (userEvent == "select.pointer")
|
|
4227
|
-
newSel = skipAtomsForSelection(
|
|
4237
|
+
newSel = skipAtomsForSelection(state$1.facet(atomicRanges).map(f => f(view)), newSel);
|
|
4228
4238
|
}
|
|
4229
4239
|
view.dispatch({ selection: newSel, scrollIntoView, userEvent });
|
|
4230
4240
|
return true;
|
|
@@ -4405,6 +4415,7 @@ class InputState {
|
|
|
4405
4415
|
this.lastFocusTime = 0;
|
|
4406
4416
|
this.lastScrollTop = 0;
|
|
4407
4417
|
this.lastScrollLeft = 0;
|
|
4418
|
+
this.lastWheelEvent = 0;
|
|
4408
4419
|
// On iOS, some keys need to have their default behavior happen
|
|
4409
4420
|
// (after which we retroactively handle them and reset the DOM) to
|
|
4410
4421
|
// avoid messing up the virtual keyboard state.
|
|
@@ -4834,6 +4845,9 @@ observers.scroll = view => {
|
|
|
4834
4845
|
view.inputState.lastScrollTop = view.scrollDOM.scrollTop;
|
|
4835
4846
|
view.inputState.lastScrollLeft = view.scrollDOM.scrollLeft;
|
|
4836
4847
|
};
|
|
4848
|
+
observers.wheel = observers.mousewheel = view => {
|
|
4849
|
+
view.inputState.lastWheelEvent = Date.now();
|
|
4850
|
+
};
|
|
4837
4851
|
handlers.keydown = (view, event) => {
|
|
4838
4852
|
view.inputState.setSelectionOrigin("select");
|
|
4839
4853
|
if (event.keyCode == 27 && view.inputState.tabFocusMode != 0)
|
|
@@ -6078,7 +6092,8 @@ class LineGapWidget extends WidgetType {
|
|
|
6078
6092
|
get estimatedHeight() { return this.vertical ? this.size : -1; }
|
|
6079
6093
|
}
|
|
6080
6094
|
class ViewState {
|
|
6081
|
-
constructor(state$1) {
|
|
6095
|
+
constructor(view, state$1) {
|
|
6096
|
+
this.view = view;
|
|
6082
6097
|
this.state = state$1;
|
|
6083
6098
|
// These are contentDOM-local coordinates
|
|
6084
6099
|
this.pixelViewport = { left: 0, right: window.innerWidth, top: 0, bottom: 0 };
|
|
@@ -6089,12 +6104,14 @@ class ViewState {
|
|
|
6089
6104
|
this.contentDOMHeight = 0; // contentDOM.getBoundingClientRect().height
|
|
6090
6105
|
this.editorHeight = 0; // scrollDOM.clientHeight, unscaled
|
|
6091
6106
|
this.editorWidth = 0; // scrollDOM.clientWidth, unscaled
|
|
6092
|
-
this.scrollTop = 0; // Last seen scrollDOM.scrollTop, scaled
|
|
6093
|
-
this.scrolledToBottom = false;
|
|
6094
6107
|
// The CSS-transformation scale of the editor (transformed size /
|
|
6095
6108
|
// concrete size)
|
|
6096
6109
|
this.scaleX = 1;
|
|
6097
6110
|
this.scaleY = 1;
|
|
6111
|
+
// Last seen vertical offset of the element at the top of the scroll
|
|
6112
|
+
// container, or top of the window if there's no wrapping scroller
|
|
6113
|
+
this.scrollOffset = 0;
|
|
6114
|
+
this.scrolledToBottom = false;
|
|
6098
6115
|
// The vertical position (document-relative) to which to anchor the
|
|
6099
6116
|
// scroll position. -1 means anchor to the end of the document.
|
|
6100
6117
|
this.scrollAnchorPos = 0;
|
|
@@ -6132,6 +6149,7 @@ class ViewState {
|
|
|
6132
6149
|
this.updateViewportLines();
|
|
6133
6150
|
this.lineGaps = this.ensureLineGaps([]);
|
|
6134
6151
|
this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(this, false)));
|
|
6152
|
+
this.scrollParent = view.scrollDOM;
|
|
6135
6153
|
this.computeVisibleRanges();
|
|
6136
6154
|
}
|
|
6137
6155
|
updateForViewport() {
|
|
@@ -6165,7 +6183,7 @@ class ViewState {
|
|
|
6165
6183
|
let contentChanges = update.changedRanges;
|
|
6166
6184
|
let heightChanges = ChangedRange.extendWithRanges(contentChanges, heightRelevantDecoChanges(prevDeco, this.stateDeco, update ? update.changes : state.ChangeSet.empty(this.state.doc.length)));
|
|
6167
6185
|
let prevHeight = this.heightMap.height;
|
|
6168
|
-
let scrollAnchor = this.scrolledToBottom ? null : this.scrollAnchorAt(this.
|
|
6186
|
+
let scrollAnchor = this.scrolledToBottom ? null : this.scrollAnchorAt(this.scrollOffset);
|
|
6169
6187
|
clearHeightChangeFlag();
|
|
6170
6188
|
this.heightMap = this.heightMap.applyChanges(this.stateDeco, update.startState.doc, this.heightOracle.setDoc(this.state.doc), heightChanges);
|
|
6171
6189
|
if (this.heightMap.height != prevHeight || heightChangeFlag)
|
|
@@ -6197,12 +6215,12 @@ class ViewState {
|
|
|
6197
6215
|
!update.state.facet(nativeSelectionHidden))
|
|
6198
6216
|
this.mustEnforceCursorAssoc = true;
|
|
6199
6217
|
}
|
|
6200
|
-
measure(
|
|
6201
|
-
let dom = view.contentDOM, style = window.getComputedStyle(dom);
|
|
6218
|
+
measure() {
|
|
6219
|
+
let { view } = this, dom = view.contentDOM, style = window.getComputedStyle(dom);
|
|
6202
6220
|
let oracle = this.heightOracle;
|
|
6203
6221
|
let whiteSpace = style.whiteSpace;
|
|
6204
6222
|
this.defaultTextDirection = style.direction == "rtl" ? exports.Direction.RTL : exports.Direction.LTR;
|
|
6205
|
-
let refresh = this.heightOracle.mustRefreshForWrapping(whiteSpace) || this.mustMeasureContent;
|
|
6223
|
+
let refresh = this.heightOracle.mustRefreshForWrapping(whiteSpace) || this.mustMeasureContent === "refresh";
|
|
6206
6224
|
let domRect = dom.getBoundingClientRect();
|
|
6207
6225
|
let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != domRect.height;
|
|
6208
6226
|
this.contentDOMHeight = domRect.height;
|
|
@@ -6232,12 +6250,18 @@ class ViewState {
|
|
|
6232
6250
|
this.editorWidth = view.scrollDOM.clientWidth;
|
|
6233
6251
|
result |= 16 /* UpdateFlag.Geometry */;
|
|
6234
6252
|
}
|
|
6235
|
-
let
|
|
6236
|
-
if (
|
|
6253
|
+
let scrollParent = scrollableParents(this.view.contentDOM, false).y;
|
|
6254
|
+
if (scrollParent != this.scrollParent) {
|
|
6255
|
+
this.scrollParent = scrollParent;
|
|
6256
|
+
this.scrollAnchorHeight = -1;
|
|
6257
|
+
this.scrollOffset = 0;
|
|
6258
|
+
}
|
|
6259
|
+
let scrollOffset = this.getScrollOffset();
|
|
6260
|
+
if (this.scrollOffset != scrollOffset) {
|
|
6237
6261
|
this.scrollAnchorHeight = -1;
|
|
6238
|
-
this.
|
|
6262
|
+
this.scrollOffset = scrollOffset;
|
|
6239
6263
|
}
|
|
6240
|
-
this.scrolledToBottom = isScrolledToBottom(view.
|
|
6264
|
+
this.scrolledToBottom = isScrolledToBottom(this.scrollParent || view.win);
|
|
6241
6265
|
// Pixel viewport
|
|
6242
6266
|
let pixelViewport = (this.printing ? fullPixelRange : visiblePixelRange)(dom, this.paddingTop);
|
|
6243
6267
|
let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
|
|
@@ -6514,9 +6538,14 @@ class ViewState {
|
|
|
6514
6538
|
this.viewportLines.find(l => l.top <= height && l.bottom >= height)) ||
|
|
6515
6539
|
scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.heightOracle, 0, 0), this.scaler);
|
|
6516
6540
|
}
|
|
6517
|
-
|
|
6518
|
-
let
|
|
6519
|
-
|
|
6541
|
+
getScrollOffset() {
|
|
6542
|
+
let base = this.scrollParent == this.view.scrollDOM ? this.scrollParent.scrollTop
|
|
6543
|
+
: (this.scrollParent ? this.scrollParent.getBoundingClientRect().top : 0) - this.view.contentDOM.getBoundingClientRect().top;
|
|
6544
|
+
return base * this.scaleY;
|
|
6545
|
+
}
|
|
6546
|
+
scrollAnchorAt(scrollOffset) {
|
|
6547
|
+
let block = this.lineBlockAtHeight(scrollOffset + 8);
|
|
6548
|
+
return block.from >= this.viewport.from || this.viewportLines[0].top - scrollOffset > 200 ? block : this.viewportLines[0];
|
|
6520
6549
|
}
|
|
6521
6550
|
elementAtHeight(height) {
|
|
6522
6551
|
return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.heightOracle, 0, 0), this.scaler);
|
|
@@ -7763,7 +7792,7 @@ class EditorView {
|
|
|
7763
7792
|
((trs) => this.update(trs));
|
|
7764
7793
|
this.dispatch = this.dispatch.bind(this);
|
|
7765
7794
|
this._root = (config.root || getRoot(config.parent) || document);
|
|
7766
|
-
this.viewState = new ViewState(config.state || state.EditorState.create(config));
|
|
7795
|
+
this.viewState = new ViewState(this, config.state || state.EditorState.create(config));
|
|
7767
7796
|
if (config.scrollTo && config.scrollTo.is(scrollIntoView))
|
|
7768
7797
|
this.viewState.scrollTarget = config.scrollTo.value.clip(this.viewState.state);
|
|
7769
7798
|
this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec));
|
|
@@ -7779,7 +7808,7 @@ class EditorView {
|
|
|
7779
7808
|
this.requestMeasure();
|
|
7780
7809
|
if ((_a = document.fonts) === null || _a === void 0 ? void 0 : _a.ready)
|
|
7781
7810
|
document.fonts.ready.then(() => {
|
|
7782
|
-
this.viewState.mustMeasureContent =
|
|
7811
|
+
this.viewState.mustMeasureContent = "refresh";
|
|
7783
7812
|
this.requestMeasure();
|
|
7784
7813
|
});
|
|
7785
7814
|
}
|
|
@@ -7918,7 +7947,7 @@ class EditorView {
|
|
|
7918
7947
|
try {
|
|
7919
7948
|
for (let plugin of this.plugins)
|
|
7920
7949
|
plugin.destroy(this);
|
|
7921
|
-
this.viewState = new ViewState(newState);
|
|
7950
|
+
this.viewState = new ViewState(this, newState);
|
|
7922
7951
|
this.plugins = newState.facet(viewPlugin).map(spec => new PluginInstance(spec));
|
|
7923
7952
|
this.pluginMap.clear();
|
|
7924
7953
|
for (let plugin of this.plugins)
|
|
@@ -7997,26 +8026,26 @@ class EditorView {
|
|
|
7997
8026
|
if (flush)
|
|
7998
8027
|
this.observer.forceFlush();
|
|
7999
8028
|
let updated = null;
|
|
8000
|
-
let
|
|
8029
|
+
let scroll = this.viewState.scrollParent, scrollOffset = this.viewState.getScrollOffset();
|
|
8001
8030
|
let { scrollAnchorPos, scrollAnchorHeight } = this.viewState;
|
|
8002
|
-
if (Math.abs(
|
|
8031
|
+
if (Math.abs(scrollOffset - this.viewState.scrollOffset) > 1)
|
|
8003
8032
|
scrollAnchorHeight = -1;
|
|
8004
8033
|
this.viewState.scrollAnchorHeight = -1;
|
|
8005
8034
|
try {
|
|
8006
8035
|
for (let i = 0;; i++) {
|
|
8007
8036
|
if (scrollAnchorHeight < 0) {
|
|
8008
|
-
if (isScrolledToBottom(
|
|
8037
|
+
if (isScrolledToBottom(scroll || this.win)) {
|
|
8009
8038
|
scrollAnchorPos = -1;
|
|
8010
8039
|
scrollAnchorHeight = this.viewState.heightMap.height;
|
|
8011
8040
|
}
|
|
8012
8041
|
else {
|
|
8013
|
-
let block = this.viewState.scrollAnchorAt(
|
|
8042
|
+
let block = this.viewState.scrollAnchorAt(scrollOffset);
|
|
8014
8043
|
scrollAnchorPos = block.from;
|
|
8015
8044
|
scrollAnchorHeight = block.top;
|
|
8016
8045
|
}
|
|
8017
8046
|
}
|
|
8018
8047
|
this.updateState = 1 /* UpdateState.Measuring */;
|
|
8019
|
-
let changed = this.viewState.measure(
|
|
8048
|
+
let changed = this.viewState.measure();
|
|
8020
8049
|
if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
|
|
8021
8050
|
break;
|
|
8022
8051
|
if (i > 5) {
|
|
@@ -8077,10 +8106,15 @@ class EditorView {
|
|
|
8077
8106
|
else {
|
|
8078
8107
|
let newAnchorHeight = scrollAnchorPos < 0 ? this.viewState.heightMap.height :
|
|
8079
8108
|
this.viewState.lineBlockAt(scrollAnchorPos).top;
|
|
8080
|
-
let diff = newAnchorHeight - scrollAnchorHeight;
|
|
8081
|
-
if (diff > 1 || diff < -1)
|
|
8082
|
-
|
|
8083
|
-
|
|
8109
|
+
let diff = (newAnchorHeight - scrollAnchorHeight) / this.scaleY;
|
|
8110
|
+
if ((diff > 1 || diff < -1) &&
|
|
8111
|
+
(scroll == this.scrollDOM || this.hasFocus ||
|
|
8112
|
+
Math.max(this.inputState.lastWheelEvent, this.inputState.lastTouchTime) > Date.now() - 100)) {
|
|
8113
|
+
scrollOffset = scrollOffset + diff;
|
|
8114
|
+
if (scroll)
|
|
8115
|
+
scroll.scrollTop += diff;
|
|
8116
|
+
else
|
|
8117
|
+
this.win.scrollBy(0, diff);
|
|
8084
8118
|
scrollAnchorHeight = -1;
|
|
8085
8119
|
continue;
|
|
8086
8120
|
}
|
package/dist/index.js
CHANGED
|
@@ -621,16 +621,16 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
|
|
|
621
621
|
}
|
|
622
622
|
}
|
|
623
623
|
}
|
|
624
|
-
function scrollableParents(dom) {
|
|
625
|
-
let doc = dom.ownerDocument, x, y;
|
|
624
|
+
function scrollableParents(dom, getX = true) {
|
|
625
|
+
let doc = dom.ownerDocument, x = null, y = null;
|
|
626
626
|
for (let cur = dom.parentNode; cur;) {
|
|
627
|
-
if (cur == doc.body || (x && y)) {
|
|
627
|
+
if (cur == doc.body || ((!getX || x) && y)) {
|
|
628
628
|
break;
|
|
629
629
|
}
|
|
630
630
|
else if (cur.nodeType == 1) {
|
|
631
631
|
if (!y && cur.scrollHeight > cur.clientHeight)
|
|
632
632
|
y = cur;
|
|
633
|
-
if (!x && cur.scrollWidth > cur.clientWidth)
|
|
633
|
+
if (getX && !x && cur.scrollWidth > cur.clientWidth)
|
|
634
634
|
x = cur;
|
|
635
635
|
cur = cur.assignedSlot || cur.parentNode;
|
|
636
636
|
}
|
|
@@ -755,6 +755,8 @@ function atElementStart(doc, selection) {
|
|
|
755
755
|
}
|
|
756
756
|
}
|
|
757
757
|
function isScrolledToBottom(elt) {
|
|
758
|
+
if (elt instanceof Window)
|
|
759
|
+
return elt.pageYOffset > Math.max(0, elt.document.documentElement.scrollHeight - elt.innerHeight - 4);
|
|
758
760
|
return elt.scrollTop > Math.max(1, elt.scrollHeight - elt.clientHeight - 4);
|
|
759
761
|
}
|
|
760
762
|
function textNodeBefore(startNode, startOffset) {
|
|
@@ -2652,7 +2654,7 @@ class TileUpdate {
|
|
|
2652
2654
|
}
|
|
2653
2655
|
else if (tile.isText()) {
|
|
2654
2656
|
this.builder.ensureLine(null);
|
|
2655
|
-
if (!from && to == tile.length) {
|
|
2657
|
+
if (!from && to == tile.length && !this.cache.reused.has(tile)) {
|
|
2656
2658
|
this.builder.addText(tile.text, activeMarks, openMarks, this.cache.reuse(tile));
|
|
2657
2659
|
}
|
|
2658
2660
|
else {
|
|
@@ -2787,9 +2789,10 @@ class TileUpdate {
|
|
|
2787
2789
|
marks.push(tile);
|
|
2788
2790
|
else if (tile === null || tile === void 0 ? void 0 : tile.isLine())
|
|
2789
2791
|
line = tile;
|
|
2792
|
+
else if (tile instanceof BlockWrapperTile) ; // Ignore
|
|
2790
2793
|
else if (parent.nodeName == "DIV" && !line && parent != this.view.contentDOM)
|
|
2791
2794
|
line = new LineTile(parent, lineBaseAttrs);
|
|
2792
|
-
else
|
|
2795
|
+
else if (!line)
|
|
2793
2796
|
marks.push(MarkTile.of(new MarkDecoration({ tagName: parent.nodeName.toLowerCase(), attributes: getAttrs(parent) }), parent));
|
|
2794
2797
|
}
|
|
2795
2798
|
return { line: line, marks };
|
|
@@ -2955,6 +2958,8 @@ class DocView {
|
|
|
2955
2958
|
if (composition || changes.length) {
|
|
2956
2959
|
let oldTile = this.tile;
|
|
2957
2960
|
let builder = new TileUpdate(this.view, oldTile, this.blockWrappers, this.decorations, this.dynamicDecorationMap);
|
|
2961
|
+
if (composition && Tile.get(composition.text))
|
|
2962
|
+
builder.cache.reused.set(Tile.get(composition.text), 2 /* Reused.DOM */);
|
|
2958
2963
|
this.tile = builder.run(changes, composition);
|
|
2959
2964
|
destroyDropped(oldTile, builder.cache.reused);
|
|
2960
2965
|
}
|
|
@@ -3415,6 +3420,19 @@ class DocView {
|
|
|
3415
3420
|
};
|
|
3416
3421
|
let { offsetWidth, offsetHeight } = this.view.scrollDOM;
|
|
3417
3422
|
scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, Math.max(Math.min(target.xMargin, offsetWidth), -offsetWidth), Math.max(Math.min(target.yMargin, offsetHeight), -offsetHeight), this.view.textDirection == Direction.LTR);
|
|
3423
|
+
// On mobile browsers, the visual viewport may be smaller than the
|
|
3424
|
+
// actual reported viewport, causing scrollRectIntoView to fail to
|
|
3425
|
+
// scroll properly. Unfortunately, this visual viewport cannot be
|
|
3426
|
+
// updated directly, and scrollIntoView is the only way a script
|
|
3427
|
+
// can affect it. So this tries to kludge around the problem by
|
|
3428
|
+
// calling scrollIntoView on the scroll target's line.
|
|
3429
|
+
if (window.visualViewport && window.innerHeight - window.visualViewport.height > 1 &&
|
|
3430
|
+
(rect.top > window.pageYOffset + window.visualViewport.offsetTop + window.visualViewport.height ||
|
|
3431
|
+
rect.bottom < window.pageYOffset + window.visualViewport.offsetTop)) {
|
|
3432
|
+
let line = this.view.docView.lineAt(range.head, 1);
|
|
3433
|
+
if (line)
|
|
3434
|
+
line.dom.scrollIntoView({ block: "nearest" });
|
|
3435
|
+
}
|
|
3418
3436
|
}
|
|
3419
3437
|
lineHasWidget(pos) {
|
|
3420
3438
|
let scan = (child) => child.isWidget() || child.children.some(scan);
|
|
@@ -4139,7 +4157,7 @@ function domBoundsAround(tile, from, to, offset) {
|
|
|
4139
4157
|
}
|
|
4140
4158
|
function applyDOMChange(view, domChange) {
|
|
4141
4159
|
let change;
|
|
4142
|
-
let { newSel } = domChange, sel =
|
|
4160
|
+
let { newSel } = domChange, { state } = view, sel = state.selection.main;
|
|
4143
4161
|
let lastKey = view.inputState.lastKeyTime > Date.now() - 100 ? view.inputState.lastKeyCode : -1;
|
|
4144
4162
|
if (domChange.bounds) {
|
|
4145
4163
|
let { from, to } = domChange.bounds;
|
|
@@ -4150,8 +4168,15 @@ function applyDOMChange(view, domChange) {
|
|
|
4150
4168
|
preferredPos = sel.to;
|
|
4151
4169
|
preferredSide = "end";
|
|
4152
4170
|
}
|
|
4153
|
-
let
|
|
4154
|
-
if (
|
|
4171
|
+
let cmp = state.doc.sliceString(from, to, LineBreakPlaceholder), selEnd, diff;
|
|
4172
|
+
if (!sel.empty && sel.from >= from && sel.to <= to && (domChange.typeOver || cmp != domChange.text) &&
|
|
4173
|
+
cmp.slice(0, sel.from - from) == domChange.text.slice(0, sel.from - from) &&
|
|
4174
|
+
cmp.slice(sel.to - from) == domChange.text.slice(selEnd = domChange.text.length - (cmp.length - (sel.to - from)))) {
|
|
4175
|
+
// This looks like a selection replacement
|
|
4176
|
+
change = { from: sel.from, to: sel.to,
|
|
4177
|
+
insert: Text.of(domChange.text.slice(sel.from - from, selEnd).split(LineBreakPlaceholder)) };
|
|
4178
|
+
}
|
|
4179
|
+
else if (diff = findDiff(cmp, domChange.text, preferredPos - from, preferredSide)) {
|
|
4155
4180
|
// Chrome inserts two newlines when pressing shift-enter at the
|
|
4156
4181
|
// end of a line. DomChange drops one of those.
|
|
4157
4182
|
if (browser.chrome && lastKey == 13 &&
|
|
@@ -4161,16 +4186,12 @@ function applyDOMChange(view, domChange) {
|
|
|
4161
4186
|
insert: Text.of(domChange.text.slice(diff.from, diff.toB).split(LineBreakPlaceholder)) };
|
|
4162
4187
|
}
|
|
4163
4188
|
}
|
|
4164
|
-
else if (newSel && (!view.hasFocus &&
|
|
4189
|
+
else if (newSel && (!view.hasFocus && state.facet(editable) || sameSelPos(newSel, sel))) {
|
|
4165
4190
|
newSel = null;
|
|
4166
4191
|
}
|
|
4167
4192
|
if (!change && !newSel)
|
|
4168
4193
|
return false;
|
|
4169
|
-
if (
|
|
4170
|
-
// Heuristic to notice typing over a selected character
|
|
4171
|
-
change = { from: sel.from, to: sel.to, insert: view.state.doc.slice(sel.from, sel.to) };
|
|
4172
|
-
}
|
|
4173
|
-
else if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 &&
|
|
4194
|
+
if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 &&
|
|
4174
4195
|
/^\. ?$/.test(change.insert.toString()) && view.contentDOM.getAttribute("autocorrect") == "off") {
|
|
4175
4196
|
// Detect insert-period-on-double-space Mac and Android behavior,
|
|
4176
4197
|
// and transform it into a regular space insert.
|
|
@@ -4178,18 +4199,7 @@ function applyDOMChange(view, domChange) {
|
|
|
4178
4199
|
newSel = EditorSelection.single(newSel.main.anchor - 1, newSel.main.head - 1);
|
|
4179
4200
|
change = { from: change.from, to: change.to, insert: Text.of([change.insert.toString().replace(".", " ")]) };
|
|
4180
4201
|
}
|
|
4181
|
-
else if (
|
|
4182
|
-
(change.from != sel.from || change.to != sel.to) &&
|
|
4183
|
-
(sel.to - sel.from) - (change.to - change.from) <= 4) {
|
|
4184
|
-
// If the change is inside the selection and covers most of it,
|
|
4185
|
-
// assume it is a selection replace (with identical characters at
|
|
4186
|
-
// the start/end not included in the diff)
|
|
4187
|
-
change = {
|
|
4188
|
-
from: sel.from, to: sel.to,
|
|
4189
|
-
insert: view.state.doc.slice(sel.from, change.from).append(change.insert).append(view.state.doc.slice(change.to, sel.to))
|
|
4190
|
-
};
|
|
4191
|
-
}
|
|
4192
|
-
else if (view.state.doc.lineAt(sel.from).to < sel.to && view.docView.lineHasWidget(sel.to) &&
|
|
4202
|
+
else if (state.doc.lineAt(sel.from).to < sel.to && view.docView.lineHasWidget(sel.to) &&
|
|
4193
4203
|
view.inputState.insertingTextAt > Date.now() - 50) {
|
|
4194
4204
|
// For a cross-line insertion, Chrome and Safari will crudely take
|
|
4195
4205
|
// the text of the line after the selection, flattening any
|
|
@@ -4198,7 +4208,7 @@ function applyDOMChange(view, domChange) {
|
|
|
4198
4208
|
// replace of the text provided by the beforeinput event.
|
|
4199
4209
|
change = {
|
|
4200
4210
|
from: sel.from, to: sel.to,
|
|
4201
|
-
insert:
|
|
4211
|
+
insert: state.toText(view.inputState.insertingText)
|
|
4202
4212
|
};
|
|
4203
4213
|
}
|
|
4204
4214
|
else if (browser.chrome && change && change.from == change.to && change.from == sel.head &&
|
|
@@ -4220,7 +4230,7 @@ function applyDOMChange(view, domChange) {
|
|
|
4220
4230
|
scrollIntoView = true;
|
|
4221
4231
|
userEvent = view.inputState.lastSelectionOrigin;
|
|
4222
4232
|
if (userEvent == "select.pointer")
|
|
4223
|
-
newSel = skipAtomsForSelection(
|
|
4233
|
+
newSel = skipAtomsForSelection(state.facet(atomicRanges).map(f => f(view)), newSel);
|
|
4224
4234
|
}
|
|
4225
4235
|
view.dispatch({ selection: newSel, scrollIntoView, userEvent });
|
|
4226
4236
|
return true;
|
|
@@ -4401,6 +4411,7 @@ class InputState {
|
|
|
4401
4411
|
this.lastFocusTime = 0;
|
|
4402
4412
|
this.lastScrollTop = 0;
|
|
4403
4413
|
this.lastScrollLeft = 0;
|
|
4414
|
+
this.lastWheelEvent = 0;
|
|
4404
4415
|
// On iOS, some keys need to have their default behavior happen
|
|
4405
4416
|
// (after which we retroactively handle them and reset the DOM) to
|
|
4406
4417
|
// avoid messing up the virtual keyboard state.
|
|
@@ -4830,6 +4841,9 @@ observers.scroll = view => {
|
|
|
4830
4841
|
view.inputState.lastScrollTop = view.scrollDOM.scrollTop;
|
|
4831
4842
|
view.inputState.lastScrollLeft = view.scrollDOM.scrollLeft;
|
|
4832
4843
|
};
|
|
4844
|
+
observers.wheel = observers.mousewheel = view => {
|
|
4845
|
+
view.inputState.lastWheelEvent = Date.now();
|
|
4846
|
+
};
|
|
4833
4847
|
handlers.keydown = (view, event) => {
|
|
4834
4848
|
view.inputState.setSelectionOrigin("select");
|
|
4835
4849
|
if (event.keyCode == 27 && view.inputState.tabFocusMode != 0)
|
|
@@ -6073,7 +6087,8 @@ class LineGapWidget extends WidgetType {
|
|
|
6073
6087
|
get estimatedHeight() { return this.vertical ? this.size : -1; }
|
|
6074
6088
|
}
|
|
6075
6089
|
class ViewState {
|
|
6076
|
-
constructor(state) {
|
|
6090
|
+
constructor(view, state) {
|
|
6091
|
+
this.view = view;
|
|
6077
6092
|
this.state = state;
|
|
6078
6093
|
// These are contentDOM-local coordinates
|
|
6079
6094
|
this.pixelViewport = { left: 0, right: window.innerWidth, top: 0, bottom: 0 };
|
|
@@ -6084,12 +6099,14 @@ class ViewState {
|
|
|
6084
6099
|
this.contentDOMHeight = 0; // contentDOM.getBoundingClientRect().height
|
|
6085
6100
|
this.editorHeight = 0; // scrollDOM.clientHeight, unscaled
|
|
6086
6101
|
this.editorWidth = 0; // scrollDOM.clientWidth, unscaled
|
|
6087
|
-
this.scrollTop = 0; // Last seen scrollDOM.scrollTop, scaled
|
|
6088
|
-
this.scrolledToBottom = false;
|
|
6089
6102
|
// The CSS-transformation scale of the editor (transformed size /
|
|
6090
6103
|
// concrete size)
|
|
6091
6104
|
this.scaleX = 1;
|
|
6092
6105
|
this.scaleY = 1;
|
|
6106
|
+
// Last seen vertical offset of the element at the top of the scroll
|
|
6107
|
+
// container, or top of the window if there's no wrapping scroller
|
|
6108
|
+
this.scrollOffset = 0;
|
|
6109
|
+
this.scrolledToBottom = false;
|
|
6093
6110
|
// The vertical position (document-relative) to which to anchor the
|
|
6094
6111
|
// scroll position. -1 means anchor to the end of the document.
|
|
6095
6112
|
this.scrollAnchorPos = 0;
|
|
@@ -6127,6 +6144,7 @@ class ViewState {
|
|
|
6127
6144
|
this.updateViewportLines();
|
|
6128
6145
|
this.lineGaps = this.ensureLineGaps([]);
|
|
6129
6146
|
this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(this, false)));
|
|
6147
|
+
this.scrollParent = view.scrollDOM;
|
|
6130
6148
|
this.computeVisibleRanges();
|
|
6131
6149
|
}
|
|
6132
6150
|
updateForViewport() {
|
|
@@ -6160,7 +6178,7 @@ class ViewState {
|
|
|
6160
6178
|
let contentChanges = update.changedRanges;
|
|
6161
6179
|
let heightChanges = ChangedRange.extendWithRanges(contentChanges, heightRelevantDecoChanges(prevDeco, this.stateDeco, update ? update.changes : ChangeSet.empty(this.state.doc.length)));
|
|
6162
6180
|
let prevHeight = this.heightMap.height;
|
|
6163
|
-
let scrollAnchor = this.scrolledToBottom ? null : this.scrollAnchorAt(this.
|
|
6181
|
+
let scrollAnchor = this.scrolledToBottom ? null : this.scrollAnchorAt(this.scrollOffset);
|
|
6164
6182
|
clearHeightChangeFlag();
|
|
6165
6183
|
this.heightMap = this.heightMap.applyChanges(this.stateDeco, update.startState.doc, this.heightOracle.setDoc(this.state.doc), heightChanges);
|
|
6166
6184
|
if (this.heightMap.height != prevHeight || heightChangeFlag)
|
|
@@ -6192,12 +6210,12 @@ class ViewState {
|
|
|
6192
6210
|
!update.state.facet(nativeSelectionHidden))
|
|
6193
6211
|
this.mustEnforceCursorAssoc = true;
|
|
6194
6212
|
}
|
|
6195
|
-
measure(
|
|
6196
|
-
let dom = view.contentDOM, style = window.getComputedStyle(dom);
|
|
6213
|
+
measure() {
|
|
6214
|
+
let { view } = this, dom = view.contentDOM, style = window.getComputedStyle(dom);
|
|
6197
6215
|
let oracle = this.heightOracle;
|
|
6198
6216
|
let whiteSpace = style.whiteSpace;
|
|
6199
6217
|
this.defaultTextDirection = style.direction == "rtl" ? Direction.RTL : Direction.LTR;
|
|
6200
|
-
let refresh = this.heightOracle.mustRefreshForWrapping(whiteSpace) || this.mustMeasureContent;
|
|
6218
|
+
let refresh = this.heightOracle.mustRefreshForWrapping(whiteSpace) || this.mustMeasureContent === "refresh";
|
|
6201
6219
|
let domRect = dom.getBoundingClientRect();
|
|
6202
6220
|
let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != domRect.height;
|
|
6203
6221
|
this.contentDOMHeight = domRect.height;
|
|
@@ -6227,12 +6245,18 @@ class ViewState {
|
|
|
6227
6245
|
this.editorWidth = view.scrollDOM.clientWidth;
|
|
6228
6246
|
result |= 16 /* UpdateFlag.Geometry */;
|
|
6229
6247
|
}
|
|
6230
|
-
let
|
|
6231
|
-
if (
|
|
6248
|
+
let scrollParent = scrollableParents(this.view.contentDOM, false).y;
|
|
6249
|
+
if (scrollParent != this.scrollParent) {
|
|
6250
|
+
this.scrollParent = scrollParent;
|
|
6251
|
+
this.scrollAnchorHeight = -1;
|
|
6252
|
+
this.scrollOffset = 0;
|
|
6253
|
+
}
|
|
6254
|
+
let scrollOffset = this.getScrollOffset();
|
|
6255
|
+
if (this.scrollOffset != scrollOffset) {
|
|
6232
6256
|
this.scrollAnchorHeight = -1;
|
|
6233
|
-
this.
|
|
6257
|
+
this.scrollOffset = scrollOffset;
|
|
6234
6258
|
}
|
|
6235
|
-
this.scrolledToBottom = isScrolledToBottom(view.
|
|
6259
|
+
this.scrolledToBottom = isScrolledToBottom(this.scrollParent || view.win);
|
|
6236
6260
|
// Pixel viewport
|
|
6237
6261
|
let pixelViewport = (this.printing ? fullPixelRange : visiblePixelRange)(dom, this.paddingTop);
|
|
6238
6262
|
let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
|
|
@@ -6509,9 +6533,14 @@ class ViewState {
|
|
|
6509
6533
|
this.viewportLines.find(l => l.top <= height && l.bottom >= height)) ||
|
|
6510
6534
|
scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.heightOracle, 0, 0), this.scaler);
|
|
6511
6535
|
}
|
|
6512
|
-
|
|
6513
|
-
let
|
|
6514
|
-
|
|
6536
|
+
getScrollOffset() {
|
|
6537
|
+
let base = this.scrollParent == this.view.scrollDOM ? this.scrollParent.scrollTop
|
|
6538
|
+
: (this.scrollParent ? this.scrollParent.getBoundingClientRect().top : 0) - this.view.contentDOM.getBoundingClientRect().top;
|
|
6539
|
+
return base * this.scaleY;
|
|
6540
|
+
}
|
|
6541
|
+
scrollAnchorAt(scrollOffset) {
|
|
6542
|
+
let block = this.lineBlockAtHeight(scrollOffset + 8);
|
|
6543
|
+
return block.from >= this.viewport.from || this.viewportLines[0].top - scrollOffset > 200 ? block : this.viewportLines[0];
|
|
6515
6544
|
}
|
|
6516
6545
|
elementAtHeight(height) {
|
|
6517
6546
|
return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.heightOracle, 0, 0), this.scaler);
|
|
@@ -7758,7 +7787,7 @@ class EditorView {
|
|
|
7758
7787
|
((trs) => this.update(trs));
|
|
7759
7788
|
this.dispatch = this.dispatch.bind(this);
|
|
7760
7789
|
this._root = (config.root || getRoot(config.parent) || document);
|
|
7761
|
-
this.viewState = new ViewState(config.state || EditorState.create(config));
|
|
7790
|
+
this.viewState = new ViewState(this, config.state || EditorState.create(config));
|
|
7762
7791
|
if (config.scrollTo && config.scrollTo.is(scrollIntoView))
|
|
7763
7792
|
this.viewState.scrollTarget = config.scrollTo.value.clip(this.viewState.state);
|
|
7764
7793
|
this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec));
|
|
@@ -7774,7 +7803,7 @@ class EditorView {
|
|
|
7774
7803
|
this.requestMeasure();
|
|
7775
7804
|
if ((_a = document.fonts) === null || _a === void 0 ? void 0 : _a.ready)
|
|
7776
7805
|
document.fonts.ready.then(() => {
|
|
7777
|
-
this.viewState.mustMeasureContent =
|
|
7806
|
+
this.viewState.mustMeasureContent = "refresh";
|
|
7778
7807
|
this.requestMeasure();
|
|
7779
7808
|
});
|
|
7780
7809
|
}
|
|
@@ -7913,7 +7942,7 @@ class EditorView {
|
|
|
7913
7942
|
try {
|
|
7914
7943
|
for (let plugin of this.plugins)
|
|
7915
7944
|
plugin.destroy(this);
|
|
7916
|
-
this.viewState = new ViewState(newState);
|
|
7945
|
+
this.viewState = new ViewState(this, newState);
|
|
7917
7946
|
this.plugins = newState.facet(viewPlugin).map(spec => new PluginInstance(spec));
|
|
7918
7947
|
this.pluginMap.clear();
|
|
7919
7948
|
for (let plugin of this.plugins)
|
|
@@ -7992,26 +8021,26 @@ class EditorView {
|
|
|
7992
8021
|
if (flush)
|
|
7993
8022
|
this.observer.forceFlush();
|
|
7994
8023
|
let updated = null;
|
|
7995
|
-
let
|
|
8024
|
+
let scroll = this.viewState.scrollParent, scrollOffset = this.viewState.getScrollOffset();
|
|
7996
8025
|
let { scrollAnchorPos, scrollAnchorHeight } = this.viewState;
|
|
7997
|
-
if (Math.abs(
|
|
8026
|
+
if (Math.abs(scrollOffset - this.viewState.scrollOffset) > 1)
|
|
7998
8027
|
scrollAnchorHeight = -1;
|
|
7999
8028
|
this.viewState.scrollAnchorHeight = -1;
|
|
8000
8029
|
try {
|
|
8001
8030
|
for (let i = 0;; i++) {
|
|
8002
8031
|
if (scrollAnchorHeight < 0) {
|
|
8003
|
-
if (isScrolledToBottom(
|
|
8032
|
+
if (isScrolledToBottom(scroll || this.win)) {
|
|
8004
8033
|
scrollAnchorPos = -1;
|
|
8005
8034
|
scrollAnchorHeight = this.viewState.heightMap.height;
|
|
8006
8035
|
}
|
|
8007
8036
|
else {
|
|
8008
|
-
let block = this.viewState.scrollAnchorAt(
|
|
8037
|
+
let block = this.viewState.scrollAnchorAt(scrollOffset);
|
|
8009
8038
|
scrollAnchorPos = block.from;
|
|
8010
8039
|
scrollAnchorHeight = block.top;
|
|
8011
8040
|
}
|
|
8012
8041
|
}
|
|
8013
8042
|
this.updateState = 1 /* UpdateState.Measuring */;
|
|
8014
|
-
let changed = this.viewState.measure(
|
|
8043
|
+
let changed = this.viewState.measure();
|
|
8015
8044
|
if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
|
|
8016
8045
|
break;
|
|
8017
8046
|
if (i > 5) {
|
|
@@ -8072,10 +8101,15 @@ class EditorView {
|
|
|
8072
8101
|
else {
|
|
8073
8102
|
let newAnchorHeight = scrollAnchorPos < 0 ? this.viewState.heightMap.height :
|
|
8074
8103
|
this.viewState.lineBlockAt(scrollAnchorPos).top;
|
|
8075
|
-
let diff = newAnchorHeight - scrollAnchorHeight;
|
|
8076
|
-
if (diff > 1 || diff < -1)
|
|
8077
|
-
|
|
8078
|
-
|
|
8104
|
+
let diff = (newAnchorHeight - scrollAnchorHeight) / this.scaleY;
|
|
8105
|
+
if ((diff > 1 || diff < -1) &&
|
|
8106
|
+
(scroll == this.scrollDOM || this.hasFocus ||
|
|
8107
|
+
Math.max(this.inputState.lastWheelEvent, this.inputState.lastTouchTime) > Date.now() - 100)) {
|
|
8108
|
+
scrollOffset = scrollOffset + diff;
|
|
8109
|
+
if (scroll)
|
|
8110
|
+
scroll.scrollTop += diff;
|
|
8111
|
+
else
|
|
8112
|
+
this.win.scrollBy(0, diff);
|
|
8079
8113
|
scrollAnchorHeight = -1;
|
|
8080
8114
|
continue;
|
|
8081
8115
|
}
|