@codemirror/view 0.19.10 → 0.19.14
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 +32 -0
- package/dist/index.cjs +184 -136
- package/dist/index.d.ts +5 -3
- package/dist/index.js +184 -136
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,35 @@
|
|
|
1
|
+
## 0.19.14 (2021-11-07)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix an issue where typing into a read-only editor would move the selection.
|
|
6
|
+
|
|
7
|
+
Fix slowness when backspace is held down on iOS.
|
|
8
|
+
|
|
9
|
+
## 0.19.13 (2021-11-06)
|
|
10
|
+
|
|
11
|
+
### Bug fixes
|
|
12
|
+
|
|
13
|
+
Fix a bug where backspace, enter, and delete would get applied twice on iOS.
|
|
14
|
+
|
|
15
|
+
## 0.19.12 (2021-11-04)
|
|
16
|
+
|
|
17
|
+
### Bug fixes
|
|
18
|
+
|
|
19
|
+
Make sure the workaround for the lost virtual keyboard on Chrome Android also works on slower phones. Slight style change in beforeinput handler
|
|
20
|
+
|
|
21
|
+
Avoid failure cases in viewport-based rendering of very long lines.
|
|
22
|
+
|
|
23
|
+
## 0.19.11 (2021-11-03)
|
|
24
|
+
|
|
25
|
+
### Breaking changes
|
|
26
|
+
|
|
27
|
+
`EditorView.scrollPosIntoView` has been deprecated. Use the `EditorView.scrollTo` effect instead.
|
|
28
|
+
|
|
29
|
+
### New features
|
|
30
|
+
|
|
31
|
+
The new `EditorView.centerOn` effect can be used to scroll a given range to the center of the view.
|
|
32
|
+
|
|
1
33
|
## 0.19.10 (2021-11-02)
|
|
2
34
|
|
|
3
35
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -102,9 +102,9 @@ function windowRect(win) {
|
|
|
102
102
|
top: 0, bottom: win.innerHeight };
|
|
103
103
|
}
|
|
104
104
|
const ScrollSpace = 5;
|
|
105
|
-
function scrollRectIntoView(dom, rect, side) {
|
|
105
|
+
function scrollRectIntoView(dom, rect, side, center) {
|
|
106
106
|
let doc = dom.ownerDocument, win = doc.defaultView;
|
|
107
|
-
for (let cur = dom
|
|
107
|
+
for (let cur = dom; cur;) {
|
|
108
108
|
if (cur.nodeType == 1) { // Element
|
|
109
109
|
let bounding, top = cur == doc.body;
|
|
110
110
|
if (top) {
|
|
@@ -121,7 +121,20 @@ function scrollRectIntoView(dom, rect, side) {
|
|
|
121
121
|
top: rect.top, bottom: rect.top + cur.clientHeight };
|
|
122
122
|
}
|
|
123
123
|
let moveX = 0, moveY = 0;
|
|
124
|
-
if (
|
|
124
|
+
if (center) {
|
|
125
|
+
let rectHeight = rect.bottom - rect.top, boundingHeight = bounding.bottom - bounding.top;
|
|
126
|
+
let targetTop;
|
|
127
|
+
if (rectHeight <= boundingHeight)
|
|
128
|
+
targetTop = rect.top + rectHeight / 2 - boundingHeight / 2;
|
|
129
|
+
else if (side < 0)
|
|
130
|
+
targetTop = rect.top - ScrollSpace;
|
|
131
|
+
else
|
|
132
|
+
targetTop = rect.bottom + ScrollSpace - boundingHeight;
|
|
133
|
+
moveY = targetTop - bounding.top;
|
|
134
|
+
if (Math.abs(moveY) <= 1)
|
|
135
|
+
moveY = 0;
|
|
136
|
+
}
|
|
137
|
+
else if (rect.top < bounding.top) {
|
|
125
138
|
moveY = -(bounding.top - rect.top + ScrollSpace);
|
|
126
139
|
if (side > 0 && rect.bottom > bounding.bottom + moveY)
|
|
127
140
|
moveY = rect.bottom - bounding.bottom + moveY + ScrollSpace;
|
|
@@ -163,6 +176,7 @@ function scrollRectIntoView(dom, rect, side) {
|
|
|
163
176
|
if (top)
|
|
164
177
|
break;
|
|
165
178
|
cur = cur.assignedSlot || cur.parentNode;
|
|
179
|
+
center = false;
|
|
166
180
|
}
|
|
167
181
|
else if (cur.nodeType == 11) { // A shadow root
|
|
168
182
|
cur = cur.host;
|
|
@@ -253,9 +267,9 @@ function contentEditablePlainTextSupported() {
|
|
|
253
267
|
}
|
|
254
268
|
function getRoot(node) {
|
|
255
269
|
while (node) {
|
|
256
|
-
node = node.assignedSlot || node.parentNode;
|
|
257
270
|
if (node && (node.nodeType == 9 || node.nodeType == 11 && node.host))
|
|
258
271
|
return node;
|
|
272
|
+
node = node.assignedSlot || node.parentNode;
|
|
259
273
|
}
|
|
260
274
|
return null;
|
|
261
275
|
}
|
|
@@ -307,25 +321,30 @@ class ContentView {
|
|
|
307
321
|
sync(track) {
|
|
308
322
|
var _a;
|
|
309
323
|
if (this.dirty & 2 /* Node */) {
|
|
310
|
-
let parent = this.dom
|
|
324
|
+
let parent = this.dom;
|
|
325
|
+
let pos = parent.firstChild;
|
|
311
326
|
for (let child of this.children) {
|
|
312
327
|
if (child.dirty) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
child.reuseDOM(next);
|
|
328
|
+
if (!child.dom && pos && !((_a = ContentView.get(pos)) === null || _a === void 0 ? void 0 : _a.parent))
|
|
329
|
+
child.reuseDOM(pos);
|
|
316
330
|
child.sync(track);
|
|
317
331
|
child.dirty = 0 /* Not */;
|
|
318
332
|
}
|
|
319
|
-
if (track && track.node == parent && pos != child.dom)
|
|
333
|
+
if (track && !track.written && track.node == parent && pos != child.dom)
|
|
320
334
|
track.written = true;
|
|
321
|
-
|
|
322
|
-
|
|
335
|
+
if (child.dom.parentNode == parent) {
|
|
336
|
+
while (pos && pos != child.dom)
|
|
337
|
+
pos = rm(pos);
|
|
338
|
+
pos = child.dom.nextSibling;
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
parent.insertBefore(child.dom, pos);
|
|
342
|
+
}
|
|
323
343
|
}
|
|
324
|
-
|
|
325
|
-
if (next && track && track.node == parent)
|
|
344
|
+
if (pos && track && track.node == parent)
|
|
326
345
|
track.written = true;
|
|
327
|
-
while (
|
|
328
|
-
|
|
346
|
+
while (pos)
|
|
347
|
+
pos = rm(pos);
|
|
329
348
|
}
|
|
330
349
|
else if (this.dirty & 1 /* Child */) {
|
|
331
350
|
for (let child of this.children)
|
|
@@ -465,14 +484,6 @@ function rm(dom) {
|
|
|
465
484
|
dom.parentNode.removeChild(dom);
|
|
466
485
|
return next;
|
|
467
486
|
}
|
|
468
|
-
function syncNodeInto(parent, after, dom) {
|
|
469
|
-
let next = after ? after.nextSibling : parent.firstChild;
|
|
470
|
-
if (dom.parentNode == parent)
|
|
471
|
-
while (next != dom)
|
|
472
|
-
next = rm(next);
|
|
473
|
-
else
|
|
474
|
-
parent.insertBefore(dom, next);
|
|
475
|
-
}
|
|
476
487
|
class ChildCursor {
|
|
477
488
|
constructor(children, pos, i) {
|
|
478
489
|
this.children = children;
|
|
@@ -1560,6 +1571,9 @@ const inputHandler = state.Facet.define();
|
|
|
1560
1571
|
const scrollTo = state.StateEffect.define({
|
|
1561
1572
|
map: (range, changes) => range.map(changes)
|
|
1562
1573
|
});
|
|
1574
|
+
const centerOn = state.StateEffect.define({
|
|
1575
|
+
map: (range, changes) => range.map(changes)
|
|
1576
|
+
});
|
|
1563
1577
|
/**
|
|
1564
1578
|
Log or report an unhandled exception in client code. Should
|
|
1565
1579
|
probably only be used by extension code that allows client code to
|
|
@@ -2304,7 +2318,7 @@ class DocView extends ContentView {
|
|
|
2304
2318
|
this.view.viewState.lineGapDeco
|
|
2305
2319
|
];
|
|
2306
2320
|
}
|
|
2307
|
-
|
|
2321
|
+
scrollIntoView({ range, center }) {
|
|
2308
2322
|
let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
|
|
2309
2323
|
if (!rect)
|
|
2310
2324
|
return;
|
|
@@ -2324,10 +2338,10 @@ class DocView extends ContentView {
|
|
|
2324
2338
|
if (bottom != null)
|
|
2325
2339
|
mBottom = Math.max(mBottom, bottom);
|
|
2326
2340
|
}
|
|
2327
|
-
scrollRectIntoView(this.
|
|
2341
|
+
scrollRectIntoView(this.view.scrollDOM, {
|
|
2328
2342
|
left: rect.left - mLeft, top: rect.top - mTop,
|
|
2329
2343
|
right: rect.right + mRight, bottom: rect.bottom + mBottom
|
|
2330
|
-
}, range.head < range.anchor ? -1 : 1);
|
|
2344
|
+
}, range.head < range.anchor ? -1 : 1, center);
|
|
2331
2345
|
}
|
|
2332
2346
|
}
|
|
2333
2347
|
function betweenUneditable(pos) {
|
|
@@ -3064,10 +3078,6 @@ class InputState {
|
|
|
3064
3078
|
constructor(view) {
|
|
3065
3079
|
this.lastKeyCode = 0;
|
|
3066
3080
|
this.lastKeyTime = 0;
|
|
3067
|
-
// On iOS, some keys need to have their default behavior happen
|
|
3068
|
-
// (after which we retroactively handle them and reset the DOM) to
|
|
3069
|
-
// avoid messing up the virtual keyboard state.
|
|
3070
|
-
//
|
|
3071
3081
|
// On Chrome Android, backspace near widgets is just completely
|
|
3072
3082
|
// broken, and there are no key events, so we need to handle the
|
|
3073
3083
|
// beforeinput event. Deleting stuff will often create a flurry of
|
|
@@ -3075,12 +3085,11 @@ class InputState {
|
|
|
3075
3085
|
// subsequent events even more broken, so again, we hold off doing
|
|
3076
3086
|
// anything until the browser is finished with whatever it is trying
|
|
3077
3087
|
// to do.
|
|
3078
|
-
|
|
3079
|
-
//
|
|
3080
|
-
//
|
|
3081
|
-
//
|
|
3082
|
-
|
|
3083
|
-
this.pendingKey = undefined;
|
|
3088
|
+
this.pendingAndroidKey = undefined;
|
|
3089
|
+
// On iOS, some keys need to have their default behavior happen
|
|
3090
|
+
// (after which we retroactively handle them and reset the DOM) to
|
|
3091
|
+
// avoid messing up the virtual keyboard state.
|
|
3092
|
+
this.pendingIOSKey = undefined;
|
|
3084
3093
|
this.lastSelectionOrigin = null;
|
|
3085
3094
|
this.lastSelectionTime = 0;
|
|
3086
3095
|
this.lastEscPress = 0;
|
|
@@ -3192,18 +3201,30 @@ class InputState {
|
|
|
3192
3201
|
let pending;
|
|
3193
3202
|
if (browser.ios && (pending = PendingKeys.find(key => key.keyCode == event.keyCode)) &&
|
|
3194
3203
|
!(event.ctrlKey || event.altKey || event.metaKey) && !event.synthetic) {
|
|
3195
|
-
this.
|
|
3204
|
+
this.pendingIOSKey = pending;
|
|
3205
|
+
setTimeout(() => this.flushIOSKey(view), 250);
|
|
3196
3206
|
return true;
|
|
3197
3207
|
}
|
|
3198
3208
|
return false;
|
|
3199
3209
|
}
|
|
3200
|
-
|
|
3201
|
-
|
|
3210
|
+
flushIOSKey(view) {
|
|
3211
|
+
let key = this.pendingIOSKey;
|
|
3212
|
+
if (!key)
|
|
3213
|
+
return false;
|
|
3214
|
+
this.pendingIOSKey = undefined;
|
|
3215
|
+
return dispatchKey(view.contentDOM, key.key, key.keyCode);
|
|
3216
|
+
}
|
|
3217
|
+
// This causes the DOM observer to pause for a bit, and sets an
|
|
3218
|
+
// animation frame (which seems the most reliable way to detect
|
|
3219
|
+
// 'Chrome is done flailing about messing with the DOM') to fire a
|
|
3220
|
+
// fake key event and re-sync the view again.
|
|
3221
|
+
setPendingAndroidKey(view, pending) {
|
|
3222
|
+
this.pendingAndroidKey = pending;
|
|
3202
3223
|
requestAnimationFrame(() => {
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
this.
|
|
3224
|
+
let key = this.pendingAndroidKey;
|
|
3225
|
+
if (!key)
|
|
3226
|
+
return;
|
|
3227
|
+
this.pendingAndroidKey = undefined;
|
|
3207
3228
|
view.observer.processRecords();
|
|
3208
3229
|
let startState = view.state;
|
|
3209
3230
|
dispatchKey(view.contentDOM, key.key, key.keyCode);
|
|
@@ -3717,18 +3738,20 @@ handlers.beforeinput = (view, event) => {
|
|
|
3717
3738
|
// seems to do nothing at all on Chrome).
|
|
3718
3739
|
let pending;
|
|
3719
3740
|
if (browser.chrome && browser.android && (pending = PendingKeys.find(key => key.inputType == event.inputType))) {
|
|
3720
|
-
view.inputState.
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3741
|
+
view.inputState.setPendingAndroidKey(view, pending);
|
|
3742
|
+
if (pending.key == "Backspace" || pending.key == "Delete") {
|
|
3743
|
+
let startViewHeight = ((_a = window.visualViewport) === null || _a === void 0 ? void 0 : _a.height) || 0;
|
|
3744
|
+
setTimeout(() => {
|
|
3745
|
+
var _a;
|
|
3746
|
+
// Backspacing near uneditable nodes on Chrome Android sometimes
|
|
3747
|
+
// closes the virtual keyboard. This tries to crudely detect
|
|
3748
|
+
// that and refocus to get it back.
|
|
3749
|
+
if ((((_a = window.visualViewport) === null || _a === void 0 ? void 0 : _a.height) || 0) > startViewHeight + 10 && view.hasFocus) {
|
|
3750
|
+
view.contentDOM.blur();
|
|
3751
|
+
view.focus();
|
|
3752
|
+
}
|
|
3753
|
+
}, 100);
|
|
3754
|
+
}
|
|
3732
3755
|
}
|
|
3733
3756
|
};
|
|
3734
3757
|
|
|
@@ -4446,6 +4469,15 @@ class LineGapWidget extends WidgetType {
|
|
|
4446
4469
|
}
|
|
4447
4470
|
get estimatedHeight() { return this.vertical ? this.size : -1; }
|
|
4448
4471
|
}
|
|
4472
|
+
class ScrollTarget {
|
|
4473
|
+
constructor(range, center = false) {
|
|
4474
|
+
this.range = range;
|
|
4475
|
+
this.center = center;
|
|
4476
|
+
}
|
|
4477
|
+
map(changes) {
|
|
4478
|
+
return changes.empty ? this : new ScrollTarget(this.range.map(changes), this.center);
|
|
4479
|
+
}
|
|
4480
|
+
}
|
|
4449
4481
|
class ViewState {
|
|
4450
4482
|
constructor(state) {
|
|
4451
4483
|
this.state = state;
|
|
@@ -4458,7 +4490,7 @@ class ViewState {
|
|
|
4458
4490
|
this.heightOracle = new HeightOracle;
|
|
4459
4491
|
// See VP.MaxDOMHeight
|
|
4460
4492
|
this.scaler = IdScaler;
|
|
4461
|
-
this.
|
|
4493
|
+
this.scrollTarget = null;
|
|
4462
4494
|
// Briefly set to true when printing, to disable viewport limiting
|
|
4463
4495
|
this.printing = false;
|
|
4464
4496
|
this.visibleRanges = [];
|
|
@@ -4491,7 +4523,7 @@ class ViewState {
|
|
|
4491
4523
|
this.scaler = this.heightMap.height <= 7000000 /* MaxDOMHeight */ ? IdScaler :
|
|
4492
4524
|
new BigScaler(this.heightOracle.doc, this.heightMap, this.viewports);
|
|
4493
4525
|
}
|
|
4494
|
-
update(update,
|
|
4526
|
+
update(update, scrollTarget = null) {
|
|
4495
4527
|
let prev = this.state;
|
|
4496
4528
|
this.state = update.state;
|
|
4497
4529
|
let newDeco = this.state.facet(decorations);
|
|
@@ -4502,15 +4534,16 @@ class ViewState {
|
|
|
4502
4534
|
if (this.heightMap.height != prevHeight)
|
|
4503
4535
|
update.flags |= 2 /* Height */;
|
|
4504
4536
|
let viewport = heightChanges.length ? this.mapViewport(this.viewport, update.changes) : this.viewport;
|
|
4505
|
-
if (
|
|
4506
|
-
|
|
4537
|
+
if (scrollTarget && (scrollTarget.range.head < viewport.from || scrollTarget.range.head > viewport.to) ||
|
|
4538
|
+
!this.viewportIsAppropriate(viewport))
|
|
4539
|
+
viewport = this.getViewport(0, scrollTarget);
|
|
4507
4540
|
this.viewport = viewport;
|
|
4508
4541
|
this.updateForViewport();
|
|
4509
|
-
if (this.lineGaps.length || this.viewport.to - this.viewport.from >
|
|
4542
|
+
if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
|
|
4510
4543
|
this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps, update.changes)));
|
|
4511
4544
|
update.flags |= this.computeVisibleRanges();
|
|
4512
|
-
if (
|
|
4513
|
-
this.
|
|
4545
|
+
if (scrollTarget)
|
|
4546
|
+
this.scrollTarget = scrollTarget;
|
|
4514
4547
|
if (!this.mustEnforceCursorAssoc && update.selectionSet && update.view.lineWrapping &&
|
|
4515
4548
|
update.state.selection.main.empty && update.state.selection.main.assoc)
|
|
4516
4549
|
this.mustEnforceCursorAssoc = true;
|
|
@@ -4563,10 +4596,10 @@ class ViewState {
|
|
|
4563
4596
|
if (oracle.heightChanged)
|
|
4564
4597
|
result |= 2 /* Height */;
|
|
4565
4598
|
if (!this.viewportIsAppropriate(this.viewport, bias) ||
|
|
4566
|
-
this.
|
|
4567
|
-
this.viewport = this.getViewport(bias, this.
|
|
4599
|
+
this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to))
|
|
4600
|
+
this.viewport = this.getViewport(bias, this.scrollTarget);
|
|
4568
4601
|
this.updateForViewport();
|
|
4569
|
-
if (this.lineGaps.length || this.viewport.to - this.viewport.from >
|
|
4602
|
+
if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
|
|
4570
4603
|
this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps));
|
|
4571
4604
|
result |= this.computeVisibleRanges();
|
|
4572
4605
|
if (this.mustEnforceCursorAssoc) {
|
|
@@ -4581,22 +4614,25 @@ class ViewState {
|
|
|
4581
4614
|
}
|
|
4582
4615
|
get visibleTop() { return this.scaler.fromDOM(this.pixelViewport.top, 0); }
|
|
4583
4616
|
get visibleBottom() { return this.scaler.fromDOM(this.pixelViewport.bottom, 0); }
|
|
4584
|
-
getViewport(bias,
|
|
4617
|
+
getViewport(bias, scrollTarget) {
|
|
4585
4618
|
// This will divide VP.Margin between the top and the
|
|
4586
4619
|
// bottom, depending on the bias (the change in viewport position
|
|
4587
4620
|
// since the last update). It'll hold a number between 0 and 1
|
|
4588
4621
|
let marginTop = 0.5 - Math.max(-0.5, Math.min(0.5, bias / 1000 /* Margin */ / 2));
|
|
4589
4622
|
let map = this.heightMap, doc = this.state.doc, { visibleTop, visibleBottom } = this;
|
|
4590
4623
|
let viewport = new Viewport(map.lineAt(visibleTop - marginTop * 1000 /* Margin */, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(visibleBottom + (1 - marginTop) * 1000 /* Margin */, QueryType.ByHeight, doc, 0, 0).to);
|
|
4591
|
-
// If
|
|
4592
|
-
if (
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4624
|
+
// If scrollTarget is given, make sure the viewport includes that position
|
|
4625
|
+
if (scrollTarget) {
|
|
4626
|
+
let { head } = scrollTarget.range, viewHeight = visibleBottom - visibleTop;
|
|
4627
|
+
if (head < viewport.from || head > viewport.to) {
|
|
4628
|
+
let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
|
|
4629
|
+
if (scrollTarget.center)
|
|
4630
|
+
topPos = (block.top + block.bottom) / 2 - viewHeight / 2;
|
|
4631
|
+
else if (head < viewport.from)
|
|
4632
|
+
topPos = block.top;
|
|
4633
|
+
else
|
|
4634
|
+
topPos = block.bottom - viewHeight;
|
|
4635
|
+
viewport = new Viewport(map.lineAt(topPos - 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(topPos + viewHeight + 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).to);
|
|
4600
4636
|
}
|
|
4601
4637
|
}
|
|
4602
4638
|
return viewport;
|
|
@@ -4638,52 +4674,50 @@ class ViewState {
|
|
|
4638
4674
|
if (this.heightOracle.direction != exports.Direction.LTR)
|
|
4639
4675
|
return gaps;
|
|
4640
4676
|
this.heightMap.forEachLine(this.viewport.from, this.viewport.to, this.state.doc, 0, 0, line => {
|
|
4641
|
-
if (line.length <
|
|
4677
|
+
if (line.length < 4000 /* DoubleMargin */)
|
|
4642
4678
|
return;
|
|
4643
4679
|
let structure = lineStructure(line.from, line.to, this.state);
|
|
4644
|
-
if (structure.total <
|
|
4680
|
+
if (structure.total < 4000 /* DoubleMargin */)
|
|
4645
4681
|
return;
|
|
4646
4682
|
let viewFrom, viewTo;
|
|
4647
4683
|
if (this.heightOracle.lineWrapping) {
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
viewFrom = findPosition(structure, (this.visibleTop - line.top) / line.height);
|
|
4652
|
-
if (line.to != this.viewport.to)
|
|
4653
|
-
viewTo = line.to;
|
|
4654
|
-
else
|
|
4655
|
-
viewTo = findPosition(structure, (this.visibleBottom - line.top) / line.height);
|
|
4684
|
+
let marginHeight = (2000 /* Margin */ / this.heightOracle.lineLength) * this.heightOracle.lineHeight;
|
|
4685
|
+
viewFrom = findPosition(structure, (this.visibleTop - line.top - marginHeight) / line.height);
|
|
4686
|
+
viewTo = findPosition(structure, (this.visibleBottom - line.top + marginHeight) / line.height);
|
|
4656
4687
|
}
|
|
4657
4688
|
else {
|
|
4658
4689
|
let totalWidth = structure.total * this.heightOracle.charWidth;
|
|
4659
|
-
|
|
4660
|
-
|
|
4690
|
+
let marginWidth = 2000 /* Margin */ * this.heightOracle.charWidth;
|
|
4691
|
+
viewFrom = findPosition(structure, (this.pixelViewport.left - marginWidth) / totalWidth);
|
|
4692
|
+
viewTo = findPosition(structure, (this.pixelViewport.right + marginWidth) / totalWidth);
|
|
4661
4693
|
}
|
|
4694
|
+
let outside = [];
|
|
4695
|
+
if (viewFrom > line.from)
|
|
4696
|
+
outside.push({ from: line.from, to: viewFrom });
|
|
4697
|
+
if (viewTo < line.to)
|
|
4698
|
+
outside.push({ from: viewTo, to: line.to });
|
|
4662
4699
|
let sel = this.state.selection.main;
|
|
4663
|
-
// Make sure the
|
|
4664
|
-
if (sel.from
|
|
4665
|
-
|
|
4666
|
-
if (sel.
|
|
4667
|
-
|
|
4668
|
-
let
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
gap.from < gapFrom + 5000 /* HalfMargin */) ||
|
|
4675
|
-
new LineGap(gapFrom, line.to, this.gapSize(line, gapFrom, false, structure)));
|
|
4700
|
+
// Make sure the gaps don't cover a selection end
|
|
4701
|
+
if (sel.from >= line.from && sel.from <= line.to)
|
|
4702
|
+
cutRange(outside, sel.from - 10 /* SelectionMargin */, sel.from + 10 /* SelectionMargin */);
|
|
4703
|
+
if (!sel.empty && sel.to >= line.from && sel.to <= line.to)
|
|
4704
|
+
cutRange(outside, sel.to - 10 /* SelectionMargin */, sel.to + 10 /* SelectionMargin */);
|
|
4705
|
+
for (let { from, to } of outside)
|
|
4706
|
+
if (to - from > 1000 /* HalfMargin */) {
|
|
4707
|
+
gaps.push(find(current, gap => gap.from >= line.from && gap.to <= line.to &&
|
|
4708
|
+
Math.abs(gap.from - from) < 1000 /* HalfMargin */ && Math.abs(gap.to - to) < 1000 /* HalfMargin */) ||
|
|
4709
|
+
new LineGap(from, to, this.gapSize(line, from, to, structure)));
|
|
4710
|
+
}
|
|
4676
4711
|
});
|
|
4677
4712
|
return gaps;
|
|
4678
4713
|
}
|
|
4679
|
-
gapSize(line,
|
|
4714
|
+
gapSize(line, from, to, structure) {
|
|
4715
|
+
let fraction = findFraction(structure, to) - findFraction(structure, from);
|
|
4680
4716
|
if (this.heightOracle.lineWrapping) {
|
|
4681
|
-
|
|
4682
|
-
return start ? height : line.height - height;
|
|
4717
|
+
return line.height * fraction;
|
|
4683
4718
|
}
|
|
4684
4719
|
else {
|
|
4685
|
-
|
|
4686
|
-
return structure.total * this.heightOracle.charWidth * (start ? ratio : 1 - ratio);
|
|
4720
|
+
return structure.total * this.heightOracle.charWidth * fraction;
|
|
4687
4721
|
}
|
|
4688
4722
|
}
|
|
4689
4723
|
updateLineGaps(gaps) {
|
|
@@ -4777,6 +4811,20 @@ function findFraction(structure, pos) {
|
|
|
4777
4811
|
}
|
|
4778
4812
|
return counted / structure.total;
|
|
4779
4813
|
}
|
|
4814
|
+
function cutRange(ranges, from, to) {
|
|
4815
|
+
for (let i = 0; i < ranges.length; i++) {
|
|
4816
|
+
let r = ranges[i];
|
|
4817
|
+
if (r.from < to && r.to > from) {
|
|
4818
|
+
let pieces = [];
|
|
4819
|
+
if (r.from < from)
|
|
4820
|
+
pieces.push({ from: r.from, to: from });
|
|
4821
|
+
if (r.to > to)
|
|
4822
|
+
pieces.push({ from: to, to: r.to });
|
|
4823
|
+
ranges.splice(i, 1, ...pieces);
|
|
4824
|
+
i += pieces.length - 1;
|
|
4825
|
+
}
|
|
4826
|
+
}
|
|
4827
|
+
}
|
|
4780
4828
|
function find(array, f) {
|
|
4781
4829
|
for (let val of array)
|
|
4782
4830
|
if (f(val))
|
|
@@ -5238,7 +5286,7 @@ class DOMObserver {
|
|
|
5238
5286
|
// Completely hold off flushing when pending keys are set—the code
|
|
5239
5287
|
// managing those will make sure processRecords is called and the
|
|
5240
5288
|
// view is resynchronized after
|
|
5241
|
-
if (this.delayedFlush >= 0 || this.view.inputState.
|
|
5289
|
+
if (this.delayedFlush >= 0 || this.view.inputState.pendingAndroidKey)
|
|
5242
5290
|
return;
|
|
5243
5291
|
this.lastFlush = Date.now();
|
|
5244
5292
|
let { from, to, typeOver } = this.processRecords();
|
|
@@ -5325,8 +5373,11 @@ function safariSelectionRangeHack(view) {
|
|
|
5325
5373
|
|
|
5326
5374
|
function applyDOMChange(view, start, end, typeOver) {
|
|
5327
5375
|
let change, newSel;
|
|
5328
|
-
let sel = view.state.selection.main
|
|
5329
|
-
if (start > -1
|
|
5376
|
+
let sel = view.state.selection.main;
|
|
5377
|
+
if (start > -1) {
|
|
5378
|
+
let bounds = view.docView.domBoundsAround(start, end, 0);
|
|
5379
|
+
if (!bounds || view.state.readOnly)
|
|
5380
|
+
return;
|
|
5330
5381
|
let { from, to } = bounds;
|
|
5331
5382
|
let selPoints = view.docView.impreciseHead || view.docView.impreciseAnchor ? [] : selectionPoints(view);
|
|
5332
5383
|
let reader = new DOMReader(selPoints, view);
|
|
@@ -5380,16 +5431,8 @@ function applyDOMChange(view, start, end, typeOver) {
|
|
|
5380
5431
|
// backspace, or delete. So this detects changes that look like
|
|
5381
5432
|
// they're caused by those keys, and reinterprets them as key
|
|
5382
5433
|
// events.
|
|
5383
|
-
if (browser.
|
|
5384
|
-
((change.from == sel.from && change.to == sel.to &&
|
|
5385
|
-
change.insert.length == 1 && change.insert.lines == 2 &&
|
|
5386
|
-
dispatchKey(view.contentDOM, "Enter", 13)) ||
|
|
5387
|
-
(change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 &&
|
|
5388
|
-
dispatchKey(view.contentDOM, "Backspace", 8)) ||
|
|
5389
|
-
(change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
|
|
5390
|
-
dispatchKey(view.contentDOM, "Delete", 46)))) {
|
|
5434
|
+
if (browser.ios && view.inputState.flushIOSKey(view))
|
|
5391
5435
|
return;
|
|
5392
|
-
}
|
|
5393
5436
|
let text = change.insert.toString();
|
|
5394
5437
|
if (view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text)))
|
|
5395
5438
|
return;
|
|
@@ -5691,21 +5734,24 @@ class EditorView {
|
|
|
5691
5734
|
if (state$1.facet(state.EditorState.phrases) != this.state.facet(state.EditorState.phrases))
|
|
5692
5735
|
return this.setState(state$1);
|
|
5693
5736
|
update = new ViewUpdate(this, state$1, transactions);
|
|
5694
|
-
let
|
|
5737
|
+
let scrollTarget = null;
|
|
5695
5738
|
try {
|
|
5696
5739
|
this.updateState = 2 /* Updating */;
|
|
5697
5740
|
for (let tr of transactions) {
|
|
5698
|
-
if (
|
|
5699
|
-
|
|
5741
|
+
if (scrollTarget)
|
|
5742
|
+
scrollTarget = scrollTarget.map(tr.changes);
|
|
5700
5743
|
if (tr.scrollIntoView) {
|
|
5701
5744
|
let { main } = tr.state.selection;
|
|
5702
|
-
|
|
5745
|
+
scrollTarget = new ScrollTarget(main.empty ? main : state.EditorSelection.cursor(main.head, main.head > main.anchor ? -1 : 1));
|
|
5703
5746
|
}
|
|
5704
|
-
for (let e of tr.effects)
|
|
5747
|
+
for (let e of tr.effects) {
|
|
5705
5748
|
if (e.is(scrollTo))
|
|
5706
|
-
|
|
5749
|
+
scrollTarget = new ScrollTarget(e.value);
|
|
5750
|
+
else if (e.is(centerOn))
|
|
5751
|
+
scrollTarget = new ScrollTarget(e.value, true);
|
|
5752
|
+
}
|
|
5707
5753
|
}
|
|
5708
|
-
this.viewState.update(update,
|
|
5754
|
+
this.viewState.update(update, scrollTarget);
|
|
5709
5755
|
this.bidiCache = CachedOrder.update(this.bidiCache, update.changes);
|
|
5710
5756
|
if (!update.empty) {
|
|
5711
5757
|
this.updatePlugins(update);
|
|
@@ -5720,7 +5766,7 @@ class EditorView {
|
|
|
5720
5766
|
finally {
|
|
5721
5767
|
this.updateState = 0 /* Idle */;
|
|
5722
5768
|
}
|
|
5723
|
-
if (redrawn ||
|
|
5769
|
+
if (redrawn || scrollTarget || this.viewState.mustEnforceCursorAssoc)
|
|
5724
5770
|
this.requestMeasure();
|
|
5725
5771
|
if (!update.empty)
|
|
5726
5772
|
for (let listener of this.state.facet(updateListener))
|
|
@@ -5802,7 +5848,7 @@ class EditorView {
|
|
|
5802
5848
|
this.updateState = 1 /* Measuring */;
|
|
5803
5849
|
let oldViewport = this.viewport;
|
|
5804
5850
|
let changed = this.viewState.measure(this.docView, i > 0);
|
|
5805
|
-
if (!changed && !this.measureRequests.length && this.viewState.
|
|
5851
|
+
if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
|
|
5806
5852
|
break;
|
|
5807
5853
|
if (i > 5) {
|
|
5808
5854
|
console.warn("Viewport failed to stabilize");
|
|
@@ -5844,9 +5890,9 @@ class EditorView {
|
|
|
5844
5890
|
logException(this.state, e);
|
|
5845
5891
|
}
|
|
5846
5892
|
}
|
|
5847
|
-
if (this.viewState.
|
|
5848
|
-
this.docView.
|
|
5849
|
-
this.viewState.
|
|
5893
|
+
if (this.viewState.scrollTarget) {
|
|
5894
|
+
this.docView.scrollIntoView(this.viewState.scrollTarget);
|
|
5895
|
+
this.viewState.scrollTarget = null;
|
|
5850
5896
|
}
|
|
5851
5897
|
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to && this.measureRequests.length == 0)
|
|
5852
5898
|
break;
|
|
@@ -6069,12 +6115,9 @@ class EditorView {
|
|
|
6069
6115
|
moveVertically(start, forward, distance) {
|
|
6070
6116
|
return skipAtoms(this, start, moveVertically(this, start, forward, distance));
|
|
6071
6117
|
}
|
|
6072
|
-
|
|
6073
|
-
Scroll the given document position into view.
|
|
6074
|
-
*/
|
|
6118
|
+
// FIXME remove on next major version
|
|
6075
6119
|
scrollPosIntoView(pos) {
|
|
6076
|
-
this.
|
|
6077
|
-
this.requestMeasure();
|
|
6120
|
+
this.dispatch({ effects: scrollTo.of(state.EditorSelection.cursor(pos)) });
|
|
6078
6121
|
}
|
|
6079
6122
|
/**
|
|
6080
6123
|
Find the DOM parent node and offset (child offset if `node` is
|
|
@@ -6241,7 +6284,7 @@ class EditorView {
|
|
|
6241
6284
|
target editors with a dark or light theme.
|
|
6242
6285
|
*/
|
|
6243
6286
|
static baseTheme(spec) {
|
|
6244
|
-
return state.Prec.
|
|
6287
|
+
return state.Prec.lowest(styleModule.of(buildTheme("." + baseThemeID, spec, lightDarkIDs)));
|
|
6245
6288
|
}
|
|
6246
6289
|
}
|
|
6247
6290
|
/**
|
|
@@ -6250,6 +6293,11 @@ transaction to make it scroll the given range into view.
|
|
|
6250
6293
|
*/
|
|
6251
6294
|
EditorView.scrollTo = scrollTo;
|
|
6252
6295
|
/**
|
|
6296
|
+
Effect that makes the editor scroll the given range to the
|
|
6297
|
+
center of the visible view.
|
|
6298
|
+
*/
|
|
6299
|
+
EditorView.centerOn = centerOn;
|
|
6300
|
+
/**
|
|
6253
6301
|
Facet to add a [style
|
|
6254
6302
|
module](https://github.com/marijnh/style-mod#documentation) to
|
|
6255
6303
|
an editor view. The view will ensure that the module is
|
|
@@ -6678,7 +6726,7 @@ const themeSpec = {
|
|
|
6678
6726
|
};
|
|
6679
6727
|
if (CanHidePrimary)
|
|
6680
6728
|
themeSpec[".cm-line"].caretColor = "transparent !important";
|
|
6681
|
-
const hideNativeSelection = state.Prec.
|
|
6729
|
+
const hideNativeSelection = state.Prec.highest(EditorView.theme(themeSpec));
|
|
6682
6730
|
function getBase(view) {
|
|
6683
6731
|
let rect = view.scrollDOM.getBoundingClientRect();
|
|
6684
6732
|
let left = view.textDirection == exports.Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth;
|
package/dist/index.d.ts
CHANGED
|
@@ -856,9 +856,6 @@ declare class EditorView {
|
|
|
856
856
|
used.
|
|
857
857
|
*/
|
|
858
858
|
moveVertically(start: SelectionRange, forward: boolean, distance?: number): SelectionRange;
|
|
859
|
-
/**
|
|
860
|
-
Scroll the given document position into view.
|
|
861
|
-
*/
|
|
862
859
|
scrollPosIntoView(pos: number): void;
|
|
863
860
|
/**
|
|
864
861
|
Find the DOM parent node and offset (child offset if `node` is
|
|
@@ -949,6 +946,11 @@ declare class EditorView {
|
|
|
949
946
|
*/
|
|
950
947
|
static scrollTo: _codemirror_state.StateEffectType<SelectionRange>;
|
|
951
948
|
/**
|
|
949
|
+
Effect that makes the editor scroll the given range to the
|
|
950
|
+
center of the visible view.
|
|
951
|
+
*/
|
|
952
|
+
static centerOn: _codemirror_state.StateEffectType<SelectionRange>;
|
|
953
|
+
/**
|
|
952
954
|
Facet to add a [style
|
|
953
955
|
module](https://github.com/marijnh/style-mod#documentation) to
|
|
954
956
|
an editor view. The view will ensure that the module is
|
package/dist/index.js
CHANGED
|
@@ -99,9 +99,9 @@ function windowRect(win) {
|
|
|
99
99
|
top: 0, bottom: win.innerHeight };
|
|
100
100
|
}
|
|
101
101
|
const ScrollSpace = 5;
|
|
102
|
-
function scrollRectIntoView(dom, rect, side) {
|
|
102
|
+
function scrollRectIntoView(dom, rect, side, center) {
|
|
103
103
|
let doc = dom.ownerDocument, win = doc.defaultView;
|
|
104
|
-
for (let cur = dom
|
|
104
|
+
for (let cur = dom; cur;) {
|
|
105
105
|
if (cur.nodeType == 1) { // Element
|
|
106
106
|
let bounding, top = cur == doc.body;
|
|
107
107
|
if (top) {
|
|
@@ -118,7 +118,20 @@ function scrollRectIntoView(dom, rect, side) {
|
|
|
118
118
|
top: rect.top, bottom: rect.top + cur.clientHeight };
|
|
119
119
|
}
|
|
120
120
|
let moveX = 0, moveY = 0;
|
|
121
|
-
if (
|
|
121
|
+
if (center) {
|
|
122
|
+
let rectHeight = rect.bottom - rect.top, boundingHeight = bounding.bottom - bounding.top;
|
|
123
|
+
let targetTop;
|
|
124
|
+
if (rectHeight <= boundingHeight)
|
|
125
|
+
targetTop = rect.top + rectHeight / 2 - boundingHeight / 2;
|
|
126
|
+
else if (side < 0)
|
|
127
|
+
targetTop = rect.top - ScrollSpace;
|
|
128
|
+
else
|
|
129
|
+
targetTop = rect.bottom + ScrollSpace - boundingHeight;
|
|
130
|
+
moveY = targetTop - bounding.top;
|
|
131
|
+
if (Math.abs(moveY) <= 1)
|
|
132
|
+
moveY = 0;
|
|
133
|
+
}
|
|
134
|
+
else if (rect.top < bounding.top) {
|
|
122
135
|
moveY = -(bounding.top - rect.top + ScrollSpace);
|
|
123
136
|
if (side > 0 && rect.bottom > bounding.bottom + moveY)
|
|
124
137
|
moveY = rect.bottom - bounding.bottom + moveY + ScrollSpace;
|
|
@@ -160,6 +173,7 @@ function scrollRectIntoView(dom, rect, side) {
|
|
|
160
173
|
if (top)
|
|
161
174
|
break;
|
|
162
175
|
cur = cur.assignedSlot || cur.parentNode;
|
|
176
|
+
center = false;
|
|
163
177
|
}
|
|
164
178
|
else if (cur.nodeType == 11) { // A shadow root
|
|
165
179
|
cur = cur.host;
|
|
@@ -250,9 +264,9 @@ function contentEditablePlainTextSupported() {
|
|
|
250
264
|
}
|
|
251
265
|
function getRoot(node) {
|
|
252
266
|
while (node) {
|
|
253
|
-
node = node.assignedSlot || node.parentNode;
|
|
254
267
|
if (node && (node.nodeType == 9 || node.nodeType == 11 && node.host))
|
|
255
268
|
return node;
|
|
269
|
+
node = node.assignedSlot || node.parentNode;
|
|
256
270
|
}
|
|
257
271
|
return null;
|
|
258
272
|
}
|
|
@@ -304,25 +318,30 @@ class ContentView {
|
|
|
304
318
|
sync(track) {
|
|
305
319
|
var _a;
|
|
306
320
|
if (this.dirty & 2 /* Node */) {
|
|
307
|
-
let parent = this.dom
|
|
321
|
+
let parent = this.dom;
|
|
322
|
+
let pos = parent.firstChild;
|
|
308
323
|
for (let child of this.children) {
|
|
309
324
|
if (child.dirty) {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
child.reuseDOM(next);
|
|
325
|
+
if (!child.dom && pos && !((_a = ContentView.get(pos)) === null || _a === void 0 ? void 0 : _a.parent))
|
|
326
|
+
child.reuseDOM(pos);
|
|
313
327
|
child.sync(track);
|
|
314
328
|
child.dirty = 0 /* Not */;
|
|
315
329
|
}
|
|
316
|
-
if (track && track.node == parent && pos != child.dom)
|
|
330
|
+
if (track && !track.written && track.node == parent && pos != child.dom)
|
|
317
331
|
track.written = true;
|
|
318
|
-
|
|
319
|
-
|
|
332
|
+
if (child.dom.parentNode == parent) {
|
|
333
|
+
while (pos && pos != child.dom)
|
|
334
|
+
pos = rm(pos);
|
|
335
|
+
pos = child.dom.nextSibling;
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
parent.insertBefore(child.dom, pos);
|
|
339
|
+
}
|
|
320
340
|
}
|
|
321
|
-
|
|
322
|
-
if (next && track && track.node == parent)
|
|
341
|
+
if (pos && track && track.node == parent)
|
|
323
342
|
track.written = true;
|
|
324
|
-
while (
|
|
325
|
-
|
|
343
|
+
while (pos)
|
|
344
|
+
pos = rm(pos);
|
|
326
345
|
}
|
|
327
346
|
else if (this.dirty & 1 /* Child */) {
|
|
328
347
|
for (let child of this.children)
|
|
@@ -462,14 +481,6 @@ function rm(dom) {
|
|
|
462
481
|
dom.parentNode.removeChild(dom);
|
|
463
482
|
return next;
|
|
464
483
|
}
|
|
465
|
-
function syncNodeInto(parent, after, dom) {
|
|
466
|
-
let next = after ? after.nextSibling : parent.firstChild;
|
|
467
|
-
if (dom.parentNode == parent)
|
|
468
|
-
while (next != dom)
|
|
469
|
-
next = rm(next);
|
|
470
|
-
else
|
|
471
|
-
parent.insertBefore(dom, next);
|
|
472
|
-
}
|
|
473
484
|
class ChildCursor {
|
|
474
485
|
constructor(children, pos, i) {
|
|
475
486
|
this.children = children;
|
|
@@ -1556,6 +1567,9 @@ const inputHandler = /*@__PURE__*/Facet.define();
|
|
|
1556
1567
|
const scrollTo = /*@__PURE__*/StateEffect.define({
|
|
1557
1568
|
map: (range, changes) => range.map(changes)
|
|
1558
1569
|
});
|
|
1570
|
+
const centerOn = /*@__PURE__*/StateEffect.define({
|
|
1571
|
+
map: (range, changes) => range.map(changes)
|
|
1572
|
+
});
|
|
1559
1573
|
/**
|
|
1560
1574
|
Log or report an unhandled exception in client code. Should
|
|
1561
1575
|
probably only be used by extension code that allows client code to
|
|
@@ -2300,7 +2314,7 @@ class DocView extends ContentView {
|
|
|
2300
2314
|
this.view.viewState.lineGapDeco
|
|
2301
2315
|
];
|
|
2302
2316
|
}
|
|
2303
|
-
|
|
2317
|
+
scrollIntoView({ range, center }) {
|
|
2304
2318
|
let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
|
|
2305
2319
|
if (!rect)
|
|
2306
2320
|
return;
|
|
@@ -2320,10 +2334,10 @@ class DocView extends ContentView {
|
|
|
2320
2334
|
if (bottom != null)
|
|
2321
2335
|
mBottom = Math.max(mBottom, bottom);
|
|
2322
2336
|
}
|
|
2323
|
-
scrollRectIntoView(this.
|
|
2337
|
+
scrollRectIntoView(this.view.scrollDOM, {
|
|
2324
2338
|
left: rect.left - mLeft, top: rect.top - mTop,
|
|
2325
2339
|
right: rect.right + mRight, bottom: rect.bottom + mBottom
|
|
2326
|
-
}, range.head < range.anchor ? -1 : 1);
|
|
2340
|
+
}, range.head < range.anchor ? -1 : 1, center);
|
|
2327
2341
|
}
|
|
2328
2342
|
}
|
|
2329
2343
|
function betweenUneditable(pos) {
|
|
@@ -3059,10 +3073,6 @@ class InputState {
|
|
|
3059
3073
|
constructor(view) {
|
|
3060
3074
|
this.lastKeyCode = 0;
|
|
3061
3075
|
this.lastKeyTime = 0;
|
|
3062
|
-
// On iOS, some keys need to have their default behavior happen
|
|
3063
|
-
// (after which we retroactively handle them and reset the DOM) to
|
|
3064
|
-
// avoid messing up the virtual keyboard state.
|
|
3065
|
-
//
|
|
3066
3076
|
// On Chrome Android, backspace near widgets is just completely
|
|
3067
3077
|
// broken, and there are no key events, so we need to handle the
|
|
3068
3078
|
// beforeinput event. Deleting stuff will often create a flurry of
|
|
@@ -3070,12 +3080,11 @@ class InputState {
|
|
|
3070
3080
|
// subsequent events even more broken, so again, we hold off doing
|
|
3071
3081
|
// anything until the browser is finished with whatever it is trying
|
|
3072
3082
|
// to do.
|
|
3073
|
-
|
|
3074
|
-
//
|
|
3075
|
-
//
|
|
3076
|
-
//
|
|
3077
|
-
|
|
3078
|
-
this.pendingKey = undefined;
|
|
3083
|
+
this.pendingAndroidKey = undefined;
|
|
3084
|
+
// On iOS, some keys need to have their default behavior happen
|
|
3085
|
+
// (after which we retroactively handle them and reset the DOM) to
|
|
3086
|
+
// avoid messing up the virtual keyboard state.
|
|
3087
|
+
this.pendingIOSKey = undefined;
|
|
3079
3088
|
this.lastSelectionOrigin = null;
|
|
3080
3089
|
this.lastSelectionTime = 0;
|
|
3081
3090
|
this.lastEscPress = 0;
|
|
@@ -3187,18 +3196,30 @@ class InputState {
|
|
|
3187
3196
|
let pending;
|
|
3188
3197
|
if (browser.ios && (pending = PendingKeys.find(key => key.keyCode == event.keyCode)) &&
|
|
3189
3198
|
!(event.ctrlKey || event.altKey || event.metaKey) && !event.synthetic) {
|
|
3190
|
-
this.
|
|
3199
|
+
this.pendingIOSKey = pending;
|
|
3200
|
+
setTimeout(() => this.flushIOSKey(view), 250);
|
|
3191
3201
|
return true;
|
|
3192
3202
|
}
|
|
3193
3203
|
return false;
|
|
3194
3204
|
}
|
|
3195
|
-
|
|
3196
|
-
|
|
3205
|
+
flushIOSKey(view) {
|
|
3206
|
+
let key = this.pendingIOSKey;
|
|
3207
|
+
if (!key)
|
|
3208
|
+
return false;
|
|
3209
|
+
this.pendingIOSKey = undefined;
|
|
3210
|
+
return dispatchKey(view.contentDOM, key.key, key.keyCode);
|
|
3211
|
+
}
|
|
3212
|
+
// This causes the DOM observer to pause for a bit, and sets an
|
|
3213
|
+
// animation frame (which seems the most reliable way to detect
|
|
3214
|
+
// 'Chrome is done flailing about messing with the DOM') to fire a
|
|
3215
|
+
// fake key event and re-sync the view again.
|
|
3216
|
+
setPendingAndroidKey(view, pending) {
|
|
3217
|
+
this.pendingAndroidKey = pending;
|
|
3197
3218
|
requestAnimationFrame(() => {
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
this.
|
|
3219
|
+
let key = this.pendingAndroidKey;
|
|
3220
|
+
if (!key)
|
|
3221
|
+
return;
|
|
3222
|
+
this.pendingAndroidKey = undefined;
|
|
3202
3223
|
view.observer.processRecords();
|
|
3203
3224
|
let startState = view.state;
|
|
3204
3225
|
dispatchKey(view.contentDOM, key.key, key.keyCode);
|
|
@@ -3712,18 +3733,20 @@ handlers.beforeinput = (view, event) => {
|
|
|
3712
3733
|
// seems to do nothing at all on Chrome).
|
|
3713
3734
|
let pending;
|
|
3714
3735
|
if (browser.chrome && browser.android && (pending = PendingKeys.find(key => key.inputType == event.inputType))) {
|
|
3715
|
-
view.inputState.
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3736
|
+
view.inputState.setPendingAndroidKey(view, pending);
|
|
3737
|
+
if (pending.key == "Backspace" || pending.key == "Delete") {
|
|
3738
|
+
let startViewHeight = ((_a = window.visualViewport) === null || _a === void 0 ? void 0 : _a.height) || 0;
|
|
3739
|
+
setTimeout(() => {
|
|
3740
|
+
var _a;
|
|
3741
|
+
// Backspacing near uneditable nodes on Chrome Android sometimes
|
|
3742
|
+
// closes the virtual keyboard. This tries to crudely detect
|
|
3743
|
+
// that and refocus to get it back.
|
|
3744
|
+
if ((((_a = window.visualViewport) === null || _a === void 0 ? void 0 : _a.height) || 0) > startViewHeight + 10 && view.hasFocus) {
|
|
3745
|
+
view.contentDOM.blur();
|
|
3746
|
+
view.focus();
|
|
3747
|
+
}
|
|
3748
|
+
}, 100);
|
|
3749
|
+
}
|
|
3727
3750
|
}
|
|
3728
3751
|
};
|
|
3729
3752
|
|
|
@@ -4440,6 +4463,15 @@ class LineGapWidget extends WidgetType {
|
|
|
4440
4463
|
}
|
|
4441
4464
|
get estimatedHeight() { return this.vertical ? this.size : -1; }
|
|
4442
4465
|
}
|
|
4466
|
+
class ScrollTarget {
|
|
4467
|
+
constructor(range, center = false) {
|
|
4468
|
+
this.range = range;
|
|
4469
|
+
this.center = center;
|
|
4470
|
+
}
|
|
4471
|
+
map(changes) {
|
|
4472
|
+
return changes.empty ? this : new ScrollTarget(this.range.map(changes), this.center);
|
|
4473
|
+
}
|
|
4474
|
+
}
|
|
4443
4475
|
class ViewState {
|
|
4444
4476
|
constructor(state) {
|
|
4445
4477
|
this.state = state;
|
|
@@ -4452,7 +4484,7 @@ class ViewState {
|
|
|
4452
4484
|
this.heightOracle = new HeightOracle;
|
|
4453
4485
|
// See VP.MaxDOMHeight
|
|
4454
4486
|
this.scaler = IdScaler;
|
|
4455
|
-
this.
|
|
4487
|
+
this.scrollTarget = null;
|
|
4456
4488
|
// Briefly set to true when printing, to disable viewport limiting
|
|
4457
4489
|
this.printing = false;
|
|
4458
4490
|
this.visibleRanges = [];
|
|
@@ -4485,7 +4517,7 @@ class ViewState {
|
|
|
4485
4517
|
this.scaler = this.heightMap.height <= 7000000 /* MaxDOMHeight */ ? IdScaler :
|
|
4486
4518
|
new BigScaler(this.heightOracle.doc, this.heightMap, this.viewports);
|
|
4487
4519
|
}
|
|
4488
|
-
update(update,
|
|
4520
|
+
update(update, scrollTarget = null) {
|
|
4489
4521
|
let prev = this.state;
|
|
4490
4522
|
this.state = update.state;
|
|
4491
4523
|
let newDeco = this.state.facet(decorations);
|
|
@@ -4496,15 +4528,16 @@ class ViewState {
|
|
|
4496
4528
|
if (this.heightMap.height != prevHeight)
|
|
4497
4529
|
update.flags |= 2 /* Height */;
|
|
4498
4530
|
let viewport = heightChanges.length ? this.mapViewport(this.viewport, update.changes) : this.viewport;
|
|
4499
|
-
if (
|
|
4500
|
-
|
|
4531
|
+
if (scrollTarget && (scrollTarget.range.head < viewport.from || scrollTarget.range.head > viewport.to) ||
|
|
4532
|
+
!this.viewportIsAppropriate(viewport))
|
|
4533
|
+
viewport = this.getViewport(0, scrollTarget);
|
|
4501
4534
|
this.viewport = viewport;
|
|
4502
4535
|
this.updateForViewport();
|
|
4503
|
-
if (this.lineGaps.length || this.viewport.to - this.viewport.from >
|
|
4536
|
+
if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
|
|
4504
4537
|
this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps, update.changes)));
|
|
4505
4538
|
update.flags |= this.computeVisibleRanges();
|
|
4506
|
-
if (
|
|
4507
|
-
this.
|
|
4539
|
+
if (scrollTarget)
|
|
4540
|
+
this.scrollTarget = scrollTarget;
|
|
4508
4541
|
if (!this.mustEnforceCursorAssoc && update.selectionSet && update.view.lineWrapping &&
|
|
4509
4542
|
update.state.selection.main.empty && update.state.selection.main.assoc)
|
|
4510
4543
|
this.mustEnforceCursorAssoc = true;
|
|
@@ -4557,10 +4590,10 @@ class ViewState {
|
|
|
4557
4590
|
if (oracle.heightChanged)
|
|
4558
4591
|
result |= 2 /* Height */;
|
|
4559
4592
|
if (!this.viewportIsAppropriate(this.viewport, bias) ||
|
|
4560
|
-
this.
|
|
4561
|
-
this.viewport = this.getViewport(bias, this.
|
|
4593
|
+
this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to))
|
|
4594
|
+
this.viewport = this.getViewport(bias, this.scrollTarget);
|
|
4562
4595
|
this.updateForViewport();
|
|
4563
|
-
if (this.lineGaps.length || this.viewport.to - this.viewport.from >
|
|
4596
|
+
if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
|
|
4564
4597
|
this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps));
|
|
4565
4598
|
result |= this.computeVisibleRanges();
|
|
4566
4599
|
if (this.mustEnforceCursorAssoc) {
|
|
@@ -4575,22 +4608,25 @@ class ViewState {
|
|
|
4575
4608
|
}
|
|
4576
4609
|
get visibleTop() { return this.scaler.fromDOM(this.pixelViewport.top, 0); }
|
|
4577
4610
|
get visibleBottom() { return this.scaler.fromDOM(this.pixelViewport.bottom, 0); }
|
|
4578
|
-
getViewport(bias,
|
|
4611
|
+
getViewport(bias, scrollTarget) {
|
|
4579
4612
|
// This will divide VP.Margin between the top and the
|
|
4580
4613
|
// bottom, depending on the bias (the change in viewport position
|
|
4581
4614
|
// since the last update). It'll hold a number between 0 and 1
|
|
4582
4615
|
let marginTop = 0.5 - Math.max(-0.5, Math.min(0.5, bias / 1000 /* Margin */ / 2));
|
|
4583
4616
|
let map = this.heightMap, doc = this.state.doc, { visibleTop, visibleBottom } = this;
|
|
4584
4617
|
let viewport = new Viewport(map.lineAt(visibleTop - marginTop * 1000 /* Margin */, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(visibleBottom + (1 - marginTop) * 1000 /* Margin */, QueryType.ByHeight, doc, 0, 0).to);
|
|
4585
|
-
// If
|
|
4586
|
-
if (
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4618
|
+
// If scrollTarget is given, make sure the viewport includes that position
|
|
4619
|
+
if (scrollTarget) {
|
|
4620
|
+
let { head } = scrollTarget.range, viewHeight = visibleBottom - visibleTop;
|
|
4621
|
+
if (head < viewport.from || head > viewport.to) {
|
|
4622
|
+
let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
|
|
4623
|
+
if (scrollTarget.center)
|
|
4624
|
+
topPos = (block.top + block.bottom) / 2 - viewHeight / 2;
|
|
4625
|
+
else if (head < viewport.from)
|
|
4626
|
+
topPos = block.top;
|
|
4627
|
+
else
|
|
4628
|
+
topPos = block.bottom - viewHeight;
|
|
4629
|
+
viewport = new Viewport(map.lineAt(topPos - 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(topPos + viewHeight + 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).to);
|
|
4594
4630
|
}
|
|
4595
4631
|
}
|
|
4596
4632
|
return viewport;
|
|
@@ -4632,52 +4668,50 @@ class ViewState {
|
|
|
4632
4668
|
if (this.heightOracle.direction != Direction.LTR)
|
|
4633
4669
|
return gaps;
|
|
4634
4670
|
this.heightMap.forEachLine(this.viewport.from, this.viewport.to, this.state.doc, 0, 0, line => {
|
|
4635
|
-
if (line.length <
|
|
4671
|
+
if (line.length < 4000 /* DoubleMargin */)
|
|
4636
4672
|
return;
|
|
4637
4673
|
let structure = lineStructure(line.from, line.to, this.state);
|
|
4638
|
-
if (structure.total <
|
|
4674
|
+
if (structure.total < 4000 /* DoubleMargin */)
|
|
4639
4675
|
return;
|
|
4640
4676
|
let viewFrom, viewTo;
|
|
4641
4677
|
if (this.heightOracle.lineWrapping) {
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
viewFrom = findPosition(structure, (this.visibleTop - line.top) / line.height);
|
|
4646
|
-
if (line.to != this.viewport.to)
|
|
4647
|
-
viewTo = line.to;
|
|
4648
|
-
else
|
|
4649
|
-
viewTo = findPosition(structure, (this.visibleBottom - line.top) / line.height);
|
|
4678
|
+
let marginHeight = (2000 /* Margin */ / this.heightOracle.lineLength) * this.heightOracle.lineHeight;
|
|
4679
|
+
viewFrom = findPosition(structure, (this.visibleTop - line.top - marginHeight) / line.height);
|
|
4680
|
+
viewTo = findPosition(structure, (this.visibleBottom - line.top + marginHeight) / line.height);
|
|
4650
4681
|
}
|
|
4651
4682
|
else {
|
|
4652
4683
|
let totalWidth = structure.total * this.heightOracle.charWidth;
|
|
4653
|
-
|
|
4654
|
-
|
|
4684
|
+
let marginWidth = 2000 /* Margin */ * this.heightOracle.charWidth;
|
|
4685
|
+
viewFrom = findPosition(structure, (this.pixelViewport.left - marginWidth) / totalWidth);
|
|
4686
|
+
viewTo = findPosition(structure, (this.pixelViewport.right + marginWidth) / totalWidth);
|
|
4655
4687
|
}
|
|
4688
|
+
let outside = [];
|
|
4689
|
+
if (viewFrom > line.from)
|
|
4690
|
+
outside.push({ from: line.from, to: viewFrom });
|
|
4691
|
+
if (viewTo < line.to)
|
|
4692
|
+
outside.push({ from: viewTo, to: line.to });
|
|
4656
4693
|
let sel = this.state.selection.main;
|
|
4657
|
-
// Make sure the
|
|
4658
|
-
if (sel.from
|
|
4659
|
-
|
|
4660
|
-
if (sel.
|
|
4661
|
-
|
|
4662
|
-
let
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
gap.from < gapFrom + 5000 /* HalfMargin */) ||
|
|
4669
|
-
new LineGap(gapFrom, line.to, this.gapSize(line, gapFrom, false, structure)));
|
|
4694
|
+
// Make sure the gaps don't cover a selection end
|
|
4695
|
+
if (sel.from >= line.from && sel.from <= line.to)
|
|
4696
|
+
cutRange(outside, sel.from - 10 /* SelectionMargin */, sel.from + 10 /* SelectionMargin */);
|
|
4697
|
+
if (!sel.empty && sel.to >= line.from && sel.to <= line.to)
|
|
4698
|
+
cutRange(outside, sel.to - 10 /* SelectionMargin */, sel.to + 10 /* SelectionMargin */);
|
|
4699
|
+
for (let { from, to } of outside)
|
|
4700
|
+
if (to - from > 1000 /* HalfMargin */) {
|
|
4701
|
+
gaps.push(find(current, gap => gap.from >= line.from && gap.to <= line.to &&
|
|
4702
|
+
Math.abs(gap.from - from) < 1000 /* HalfMargin */ && Math.abs(gap.to - to) < 1000 /* HalfMargin */) ||
|
|
4703
|
+
new LineGap(from, to, this.gapSize(line, from, to, structure)));
|
|
4704
|
+
}
|
|
4670
4705
|
});
|
|
4671
4706
|
return gaps;
|
|
4672
4707
|
}
|
|
4673
|
-
gapSize(line,
|
|
4708
|
+
gapSize(line, from, to, structure) {
|
|
4709
|
+
let fraction = findFraction(structure, to) - findFraction(structure, from);
|
|
4674
4710
|
if (this.heightOracle.lineWrapping) {
|
|
4675
|
-
|
|
4676
|
-
return start ? height : line.height - height;
|
|
4711
|
+
return line.height * fraction;
|
|
4677
4712
|
}
|
|
4678
4713
|
else {
|
|
4679
|
-
|
|
4680
|
-
return structure.total * this.heightOracle.charWidth * (start ? ratio : 1 - ratio);
|
|
4714
|
+
return structure.total * this.heightOracle.charWidth * fraction;
|
|
4681
4715
|
}
|
|
4682
4716
|
}
|
|
4683
4717
|
updateLineGaps(gaps) {
|
|
@@ -4771,6 +4805,20 @@ function findFraction(structure, pos) {
|
|
|
4771
4805
|
}
|
|
4772
4806
|
return counted / structure.total;
|
|
4773
4807
|
}
|
|
4808
|
+
function cutRange(ranges, from, to) {
|
|
4809
|
+
for (let i = 0; i < ranges.length; i++) {
|
|
4810
|
+
let r = ranges[i];
|
|
4811
|
+
if (r.from < to && r.to > from) {
|
|
4812
|
+
let pieces = [];
|
|
4813
|
+
if (r.from < from)
|
|
4814
|
+
pieces.push({ from: r.from, to: from });
|
|
4815
|
+
if (r.to > to)
|
|
4816
|
+
pieces.push({ from: to, to: r.to });
|
|
4817
|
+
ranges.splice(i, 1, ...pieces);
|
|
4818
|
+
i += pieces.length - 1;
|
|
4819
|
+
}
|
|
4820
|
+
}
|
|
4821
|
+
}
|
|
4774
4822
|
function find(array, f) {
|
|
4775
4823
|
for (let val of array)
|
|
4776
4824
|
if (f(val))
|
|
@@ -5232,7 +5280,7 @@ class DOMObserver {
|
|
|
5232
5280
|
// Completely hold off flushing when pending keys are set—the code
|
|
5233
5281
|
// managing those will make sure processRecords is called and the
|
|
5234
5282
|
// view is resynchronized after
|
|
5235
|
-
if (this.delayedFlush >= 0 || this.view.inputState.
|
|
5283
|
+
if (this.delayedFlush >= 0 || this.view.inputState.pendingAndroidKey)
|
|
5236
5284
|
return;
|
|
5237
5285
|
this.lastFlush = Date.now();
|
|
5238
5286
|
let { from, to, typeOver } = this.processRecords();
|
|
@@ -5319,8 +5367,11 @@ function safariSelectionRangeHack(view) {
|
|
|
5319
5367
|
|
|
5320
5368
|
function applyDOMChange(view, start, end, typeOver) {
|
|
5321
5369
|
let change, newSel;
|
|
5322
|
-
let sel = view.state.selection.main
|
|
5323
|
-
if (start > -1
|
|
5370
|
+
let sel = view.state.selection.main;
|
|
5371
|
+
if (start > -1) {
|
|
5372
|
+
let bounds = view.docView.domBoundsAround(start, end, 0);
|
|
5373
|
+
if (!bounds || view.state.readOnly)
|
|
5374
|
+
return;
|
|
5324
5375
|
let { from, to } = bounds;
|
|
5325
5376
|
let selPoints = view.docView.impreciseHead || view.docView.impreciseAnchor ? [] : selectionPoints(view);
|
|
5326
5377
|
let reader = new DOMReader(selPoints, view);
|
|
@@ -5374,16 +5425,8 @@ function applyDOMChange(view, start, end, typeOver) {
|
|
|
5374
5425
|
// backspace, or delete. So this detects changes that look like
|
|
5375
5426
|
// they're caused by those keys, and reinterprets them as key
|
|
5376
5427
|
// events.
|
|
5377
|
-
if (browser.
|
|
5378
|
-
((change.from == sel.from && change.to == sel.to &&
|
|
5379
|
-
change.insert.length == 1 && change.insert.lines == 2 &&
|
|
5380
|
-
dispatchKey(view.contentDOM, "Enter", 13)) ||
|
|
5381
|
-
(change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 &&
|
|
5382
|
-
dispatchKey(view.contentDOM, "Backspace", 8)) ||
|
|
5383
|
-
(change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
|
|
5384
|
-
dispatchKey(view.contentDOM, "Delete", 46)))) {
|
|
5428
|
+
if (browser.ios && view.inputState.flushIOSKey(view))
|
|
5385
5429
|
return;
|
|
5386
|
-
}
|
|
5387
5430
|
let text = change.insert.toString();
|
|
5388
5431
|
if (view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text)))
|
|
5389
5432
|
return;
|
|
@@ -5685,21 +5728,24 @@ class EditorView {
|
|
|
5685
5728
|
if (state.facet(EditorState.phrases) != this.state.facet(EditorState.phrases))
|
|
5686
5729
|
return this.setState(state);
|
|
5687
5730
|
update = new ViewUpdate(this, state, transactions);
|
|
5688
|
-
let
|
|
5731
|
+
let scrollTarget = null;
|
|
5689
5732
|
try {
|
|
5690
5733
|
this.updateState = 2 /* Updating */;
|
|
5691
5734
|
for (let tr of transactions) {
|
|
5692
|
-
if (
|
|
5693
|
-
|
|
5735
|
+
if (scrollTarget)
|
|
5736
|
+
scrollTarget = scrollTarget.map(tr.changes);
|
|
5694
5737
|
if (tr.scrollIntoView) {
|
|
5695
5738
|
let { main } = tr.state.selection;
|
|
5696
|
-
|
|
5739
|
+
scrollTarget = new ScrollTarget(main.empty ? main : EditorSelection.cursor(main.head, main.head > main.anchor ? -1 : 1));
|
|
5697
5740
|
}
|
|
5698
|
-
for (let e of tr.effects)
|
|
5741
|
+
for (let e of tr.effects) {
|
|
5699
5742
|
if (e.is(scrollTo))
|
|
5700
|
-
|
|
5743
|
+
scrollTarget = new ScrollTarget(e.value);
|
|
5744
|
+
else if (e.is(centerOn))
|
|
5745
|
+
scrollTarget = new ScrollTarget(e.value, true);
|
|
5746
|
+
}
|
|
5701
5747
|
}
|
|
5702
|
-
this.viewState.update(update,
|
|
5748
|
+
this.viewState.update(update, scrollTarget);
|
|
5703
5749
|
this.bidiCache = CachedOrder.update(this.bidiCache, update.changes);
|
|
5704
5750
|
if (!update.empty) {
|
|
5705
5751
|
this.updatePlugins(update);
|
|
@@ -5714,7 +5760,7 @@ class EditorView {
|
|
|
5714
5760
|
finally {
|
|
5715
5761
|
this.updateState = 0 /* Idle */;
|
|
5716
5762
|
}
|
|
5717
|
-
if (redrawn ||
|
|
5763
|
+
if (redrawn || scrollTarget || this.viewState.mustEnforceCursorAssoc)
|
|
5718
5764
|
this.requestMeasure();
|
|
5719
5765
|
if (!update.empty)
|
|
5720
5766
|
for (let listener of this.state.facet(updateListener))
|
|
@@ -5796,7 +5842,7 @@ class EditorView {
|
|
|
5796
5842
|
this.updateState = 1 /* Measuring */;
|
|
5797
5843
|
let oldViewport = this.viewport;
|
|
5798
5844
|
let changed = this.viewState.measure(this.docView, i > 0);
|
|
5799
|
-
if (!changed && !this.measureRequests.length && this.viewState.
|
|
5845
|
+
if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
|
|
5800
5846
|
break;
|
|
5801
5847
|
if (i > 5) {
|
|
5802
5848
|
console.warn("Viewport failed to stabilize");
|
|
@@ -5838,9 +5884,9 @@ class EditorView {
|
|
|
5838
5884
|
logException(this.state, e);
|
|
5839
5885
|
}
|
|
5840
5886
|
}
|
|
5841
|
-
if (this.viewState.
|
|
5842
|
-
this.docView.
|
|
5843
|
-
this.viewState.
|
|
5887
|
+
if (this.viewState.scrollTarget) {
|
|
5888
|
+
this.docView.scrollIntoView(this.viewState.scrollTarget);
|
|
5889
|
+
this.viewState.scrollTarget = null;
|
|
5844
5890
|
}
|
|
5845
5891
|
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to && this.measureRequests.length == 0)
|
|
5846
5892
|
break;
|
|
@@ -6063,12 +6109,9 @@ class EditorView {
|
|
|
6063
6109
|
moveVertically(start, forward, distance) {
|
|
6064
6110
|
return skipAtoms(this, start, moveVertically(this, start, forward, distance));
|
|
6065
6111
|
}
|
|
6066
|
-
|
|
6067
|
-
Scroll the given document position into view.
|
|
6068
|
-
*/
|
|
6112
|
+
// FIXME remove on next major version
|
|
6069
6113
|
scrollPosIntoView(pos) {
|
|
6070
|
-
this.
|
|
6071
|
-
this.requestMeasure();
|
|
6114
|
+
this.dispatch({ effects: scrollTo.of(EditorSelection.cursor(pos)) });
|
|
6072
6115
|
}
|
|
6073
6116
|
/**
|
|
6074
6117
|
Find the DOM parent node and offset (child offset if `node` is
|
|
@@ -6235,7 +6278,7 @@ class EditorView {
|
|
|
6235
6278
|
target editors with a dark or light theme.
|
|
6236
6279
|
*/
|
|
6237
6280
|
static baseTheme(spec) {
|
|
6238
|
-
return Prec.
|
|
6281
|
+
return Prec.lowest(styleModule.of(buildTheme("." + baseThemeID, spec, lightDarkIDs)));
|
|
6239
6282
|
}
|
|
6240
6283
|
}
|
|
6241
6284
|
/**
|
|
@@ -6244,6 +6287,11 @@ transaction to make it scroll the given range into view.
|
|
|
6244
6287
|
*/
|
|
6245
6288
|
EditorView.scrollTo = scrollTo;
|
|
6246
6289
|
/**
|
|
6290
|
+
Effect that makes the editor scroll the given range to the
|
|
6291
|
+
center of the visible view.
|
|
6292
|
+
*/
|
|
6293
|
+
EditorView.centerOn = centerOn;
|
|
6294
|
+
/**
|
|
6247
6295
|
Facet to add a [style
|
|
6248
6296
|
module](https://github.com/marijnh/style-mod#documentation) to
|
|
6249
6297
|
an editor view. The view will ensure that the module is
|
|
@@ -6672,7 +6720,7 @@ const themeSpec = {
|
|
|
6672
6720
|
};
|
|
6673
6721
|
if (CanHidePrimary)
|
|
6674
6722
|
themeSpec[".cm-line"].caretColor = "transparent !important";
|
|
6675
|
-
const hideNativeSelection = /*@__PURE__*/Prec.
|
|
6723
|
+
const hideNativeSelection = /*@__PURE__*/Prec.highest(/*@__PURE__*/EditorView.theme(themeSpec));
|
|
6676
6724
|
function getBase(view) {
|
|
6677
6725
|
let rect = view.scrollDOM.getBoundingClientRect();
|
|
6678
6726
|
let left = view.textDirection == Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codemirror/view",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.14",
|
|
4
4
|
"description": "DOM view component for the CodeMirror code editor",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "cm-runtests",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@codemirror/rangeset": "^0.19.0",
|
|
30
|
-
"@codemirror/state": "^0.19.
|
|
30
|
+
"@codemirror/state": "^0.19.3",
|
|
31
31
|
"@codemirror/text": "^0.19.0",
|
|
32
32
|
"style-mod": "^4.0.0",
|
|
33
33
|
"w3c-keyname": "^2.2.4"
|