@codemirror/view 6.1.0 → 6.1.3
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 +26 -0
- package/dist/index.cjs +89 -19
- package/dist/index.js +89 -19
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
## 6.1.3 (2022-08-03)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix a bug where a document that contains only non-printing characters would lead to bogus text measurements (and, from those, to crashing).
|
|
6
|
+
|
|
7
|
+
Make sure differences between estimated and actual block heights don't cause visible scroll glitches.
|
|
8
|
+
|
|
9
|
+
## 6.1.2 (2022-07-27)
|
|
10
|
+
|
|
11
|
+
### Bug fixes
|
|
12
|
+
|
|
13
|
+
Fix an issue where double tapping enter to confirm IME input and insert a newline on iOS would sometimes insert two newlines.
|
|
14
|
+
|
|
15
|
+
Fix an issue on iOS where a composition could get aborted if the editor scrolled on backspace.
|
|
16
|
+
|
|
17
|
+
## 6.1.1 (2022-07-25)
|
|
18
|
+
|
|
19
|
+
### Bug fixes
|
|
20
|
+
|
|
21
|
+
Make `highlightSpecialChars` replace directional isolate characters by default.
|
|
22
|
+
|
|
23
|
+
The editor will now try to suppress browsers' native behavior of resetting the selection in the editable content when the editable element is focused (programmatically, with tab, etc).
|
|
24
|
+
|
|
25
|
+
Fix a CSS issue that made it possible, when the gutters were wide enough, for them to overlap with the content.
|
|
26
|
+
|
|
1
27
|
## 6.1.0 (2022-07-19)
|
|
2
28
|
|
|
3
29
|
### New features
|
package/dist/index.cjs
CHANGED
|
@@ -268,6 +268,31 @@ function clearAttributes(node) {
|
|
|
268
268
|
while (node.attributes.length)
|
|
269
269
|
node.removeAttributeNode(node.attributes[0]);
|
|
270
270
|
}
|
|
271
|
+
function atElementStart(doc, selection) {
|
|
272
|
+
let node = selection.focusNode, offset = selection.focusOffset;
|
|
273
|
+
if (!node || selection.anchorNode != node || selection.anchorOffset != offset)
|
|
274
|
+
return false;
|
|
275
|
+
for (;;) {
|
|
276
|
+
if (offset) {
|
|
277
|
+
if (node.nodeType != 1)
|
|
278
|
+
return false;
|
|
279
|
+
let prev = node.childNodes[offset - 1];
|
|
280
|
+
if (prev.contentEditable == "false")
|
|
281
|
+
offset--;
|
|
282
|
+
else {
|
|
283
|
+
node = prev;
|
|
284
|
+
offset = maxOffset(node);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
else if (node == doc) {
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
offset = domIndex(node);
|
|
292
|
+
node = node.parentNode;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
271
296
|
|
|
272
297
|
class DOMPos {
|
|
273
298
|
constructor(node, offset, precise = true) {
|
|
@@ -1448,15 +1473,17 @@ class LineView extends ContentView {
|
|
|
1448
1473
|
return null;
|
|
1449
1474
|
let totalWidth = 0;
|
|
1450
1475
|
for (let child of this.children) {
|
|
1451
|
-
if (!(child instanceof TextView))
|
|
1476
|
+
if (!(child instanceof TextView) || /[^ -~]/.test(child.text))
|
|
1452
1477
|
return null;
|
|
1453
1478
|
let rects = clientRectsFor(child.dom);
|
|
1454
1479
|
if (rects.length != 1)
|
|
1455
1480
|
return null;
|
|
1456
1481
|
totalWidth += rects[0].width;
|
|
1457
1482
|
}
|
|
1458
|
-
return
|
|
1459
|
-
|
|
1483
|
+
return !totalWidth ? null : {
|
|
1484
|
+
lineHeight: this.dom.getBoundingClientRect().height,
|
|
1485
|
+
charWidth: totalWidth / this.length
|
|
1486
|
+
};
|
|
1460
1487
|
}
|
|
1461
1488
|
coordsAt(pos, side) {
|
|
1462
1489
|
return coordsInChildren(this, pos, side);
|
|
@@ -3289,6 +3316,10 @@ class InputState {
|
|
|
3289
3316
|
constructor(view) {
|
|
3290
3317
|
this.lastKeyCode = 0;
|
|
3291
3318
|
this.lastKeyTime = 0;
|
|
3319
|
+
this.lastTouchTime = 0;
|
|
3320
|
+
this.lastFocusTime = 0;
|
|
3321
|
+
this.lastScrollTop = 0;
|
|
3322
|
+
this.lastScrollLeft = 0;
|
|
3292
3323
|
this.chromeScrollHack = -1;
|
|
3293
3324
|
// On iOS, some keys need to have their default behavior happen
|
|
3294
3325
|
// (after which we retroactively handle them and reset the DOM) to
|
|
@@ -3327,10 +3358,10 @@ class InputState {
|
|
|
3327
3358
|
event.preventDefault();
|
|
3328
3359
|
else
|
|
3329
3360
|
handler(view, event);
|
|
3330
|
-
});
|
|
3361
|
+
}, handlerOptions[type]);
|
|
3331
3362
|
this.registeredEvents.push(type);
|
|
3332
3363
|
}
|
|
3333
|
-
if (browser.chrome && browser.chrome_version
|
|
3364
|
+
if (browser.chrome && browser.chrome_version == 102) { // FIXME remove at some point
|
|
3334
3365
|
// On Chrome 102, viewport updates somehow stop wheel-based
|
|
3335
3366
|
// scrolling. Turning off pointer events during the scroll seems
|
|
3336
3367
|
// to avoid the issue.
|
|
@@ -3390,6 +3421,8 @@ class InputState {
|
|
|
3390
3421
|
return false;
|
|
3391
3422
|
}
|
|
3392
3423
|
runScrollHandlers(view, event) {
|
|
3424
|
+
this.lastScrollTop = view.scrollDOM.scrollTop;
|
|
3425
|
+
this.lastScrollLeft = view.scrollDOM.scrollLeft;
|
|
3393
3426
|
for (let set of this.customHandlers) {
|
|
3394
3427
|
let handler = set.handlers.scroll;
|
|
3395
3428
|
if (handler) {
|
|
@@ -3450,7 +3483,7 @@ class InputState {
|
|
|
3450
3483
|
// compositionend and keydown events are sometimes emitted in the
|
|
3451
3484
|
// wrong order. The key event should still be ignored, even when
|
|
3452
3485
|
// it happens after the compositionend event.
|
|
3453
|
-
if (browser.safari && Date.now() - this.compositionEndedAt < 100) {
|
|
3486
|
+
if (browser.safari && !browser.ios && Date.now() - this.compositionEndedAt < 100) {
|
|
3454
3487
|
this.compositionEndedAt = 0;
|
|
3455
3488
|
return true;
|
|
3456
3489
|
}
|
|
@@ -3578,6 +3611,7 @@ function eventBelongsToEditor(view, event) {
|
|
|
3578
3611
|
return true;
|
|
3579
3612
|
}
|
|
3580
3613
|
const handlers = Object.create(null);
|
|
3614
|
+
const handlerOptions = Object.create(null);
|
|
3581
3615
|
// This is very crude, but unfortunately both these browsers _pretend_
|
|
3582
3616
|
// that they have a clipboard API—all the objects and methods are
|
|
3583
3617
|
// there, they just don't work, and they are hard to test.
|
|
@@ -3634,17 +3668,17 @@ handlers.keydown = (view, event) => {
|
|
|
3634
3668
|
else if (modifierCodes.indexOf(event.keyCode) < 0)
|
|
3635
3669
|
view.inputState.lastEscPress = 0;
|
|
3636
3670
|
};
|
|
3637
|
-
let lastTouch = 0;
|
|
3638
3671
|
handlers.touchstart = (view, e) => {
|
|
3639
|
-
|
|
3672
|
+
view.inputState.lastTouchTime = Date.now();
|
|
3640
3673
|
view.inputState.setSelectionOrigin("select.pointer");
|
|
3641
3674
|
};
|
|
3642
3675
|
handlers.touchmove = view => {
|
|
3643
3676
|
view.inputState.setSelectionOrigin("select.pointer");
|
|
3644
3677
|
};
|
|
3678
|
+
handlerOptions.touchstart = handlerOptions.touchmove = { passive: true };
|
|
3645
3679
|
handlers.mousedown = (view, event) => {
|
|
3646
3680
|
view.observer.flush();
|
|
3647
|
-
if (
|
|
3681
|
+
if (view.inputState.lastTouchTime > Date.now() - 2000 && getClickType(event) == 1)
|
|
3648
3682
|
return; // Ignore touch interaction
|
|
3649
3683
|
let style = null;
|
|
3650
3684
|
for (let makeStyle of view.state.facet(mouseSelectionStyle)) {
|
|
@@ -3898,7 +3932,15 @@ function updateForFocusChange(view) {
|
|
|
3898
3932
|
view.update([]);
|
|
3899
3933
|
}, 10);
|
|
3900
3934
|
}
|
|
3901
|
-
handlers.focus =
|
|
3935
|
+
handlers.focus = view => {
|
|
3936
|
+
view.inputState.lastFocusTime = Date.now();
|
|
3937
|
+
// When focusing reset the scroll position, move it back to where it was
|
|
3938
|
+
if (!view.scrollDOM.scrollTop && (view.inputState.lastScrollTop || view.inputState.lastScrollLeft)) {
|
|
3939
|
+
view.scrollDOM.scrollTop = view.inputState.lastScrollTop;
|
|
3940
|
+
view.scrollDOM.scrollLeft = view.inputState.lastScrollLeft;
|
|
3941
|
+
}
|
|
3942
|
+
updateForFocusChange(view);
|
|
3943
|
+
};
|
|
3902
3944
|
handlers.blur = view => {
|
|
3903
3945
|
view.observer.clearSelectionRange();
|
|
3904
3946
|
updateForFocusChange(view);
|
|
@@ -4635,7 +4677,7 @@ function visiblePixelRange(dom, paddingTop) {
|
|
|
4635
4677
|
left = Math.max(left, parentRect.left);
|
|
4636
4678
|
right = Math.min(right, parentRect.right);
|
|
4637
4679
|
top = Math.max(top, parentRect.top);
|
|
4638
|
-
bottom = Math.min(bottom, parentRect.bottom);
|
|
4680
|
+
bottom = parent == dom.parentNode ? parentRect.bottom : Math.min(bottom, parentRect.bottom);
|
|
4639
4681
|
}
|
|
4640
4682
|
parent = style.position == "absolute" || style.position == "fixed" ? elt.offsetParent : elt.parentNode;
|
|
4641
4683
|
}
|
|
@@ -5269,6 +5311,7 @@ const baseTheme$1 = buildTheme("." + baseThemeID, {
|
|
|
5269
5311
|
"&light .cm-specialChar": { color: "red" },
|
|
5270
5312
|
"&dark .cm-specialChar": { color: "#f78" },
|
|
5271
5313
|
".cm-gutters": {
|
|
5314
|
+
flexShrink: 0,
|
|
5272
5315
|
display: "flex",
|
|
5273
5316
|
height: "100%",
|
|
5274
5317
|
boxSizing: "border-box",
|
|
@@ -5525,15 +5568,28 @@ class DOMObserver {
|
|
|
5525
5568
|
this.flush(false);
|
|
5526
5569
|
}
|
|
5527
5570
|
readSelectionRange() {
|
|
5528
|
-
let {
|
|
5571
|
+
let { view } = this;
|
|
5529
5572
|
// The Selection object is broken in shadow roots in Safari. See
|
|
5530
5573
|
// https://github.com/codemirror/dev/issues/414
|
|
5531
|
-
let range = browser.safari && root.nodeType == 11 && deepActiveElement() == this.
|
|
5532
|
-
safariSelectionRangeHack(this.view) || getSelection(root);
|
|
5574
|
+
let range = browser.safari && view.root.nodeType == 11 && deepActiveElement() == this.dom &&
|
|
5575
|
+
safariSelectionRangeHack(this.view) || getSelection(view.root);
|
|
5533
5576
|
if (!range || this.selectionRange.eq(range))
|
|
5534
5577
|
return false;
|
|
5578
|
+
let local = hasSelection(this.dom, range);
|
|
5579
|
+
// Detect the situation where the browser has, on focus, moved the
|
|
5580
|
+
// selection to the start of the content element. Reset it to the
|
|
5581
|
+
// position from the editor state.
|
|
5582
|
+
if (local && !this.selectionChanged && this.selectionRange.focusNode &&
|
|
5583
|
+
view.inputState.lastFocusTime > Date.now() - 200 &&
|
|
5584
|
+
view.inputState.lastTouchTime < Date.now() - 300 &&
|
|
5585
|
+
atElementStart(this.dom, range)) {
|
|
5586
|
+
view.docView.updateSelection();
|
|
5587
|
+
return false;
|
|
5588
|
+
}
|
|
5535
5589
|
this.selectionRange.setRange(range);
|
|
5536
|
-
|
|
5590
|
+
if (local)
|
|
5591
|
+
this.selectionChanged = true;
|
|
5592
|
+
return true;
|
|
5537
5593
|
}
|
|
5538
5594
|
setSelectionRange(anchor, head) {
|
|
5539
5595
|
this.selectionRange.set(anchor.node, anchor.offset, head.node, head.offset);
|
|
@@ -5620,7 +5676,7 @@ class DOMObserver {
|
|
|
5620
5676
|
this.delayedAndroidKey = null;
|
|
5621
5677
|
this.delayedFlush = -1;
|
|
5622
5678
|
if (!this.flush())
|
|
5623
|
-
dispatchKey(this.
|
|
5679
|
+
dispatchKey(this.dom, key.key, key.keyCode);
|
|
5624
5680
|
});
|
|
5625
5681
|
// Since backspace beforeinput is sometimes signalled spuriously,
|
|
5626
5682
|
// Enter always takes precedence.
|
|
@@ -5635,8 +5691,8 @@ class DOMObserver {
|
|
|
5635
5691
|
if (this.delayedFlush >= 0) {
|
|
5636
5692
|
window.clearTimeout(this.delayedFlush);
|
|
5637
5693
|
this.delayedFlush = -1;
|
|
5638
|
-
this.flush();
|
|
5639
5694
|
}
|
|
5695
|
+
this.flush();
|
|
5640
5696
|
}
|
|
5641
5697
|
processRecords() {
|
|
5642
5698
|
let records = this.queue;
|
|
@@ -5674,6 +5730,7 @@ class DOMObserver {
|
|
|
5674
5730
|
let newSel = this.selectionChanged && hasSelection(this.dom, this.selectionRange);
|
|
5675
5731
|
if (from < 0 && !newSel)
|
|
5676
5732
|
return;
|
|
5733
|
+
this.view.inputState.lastFocusTime = 0;
|
|
5677
5734
|
this.selectionChanged = false;
|
|
5678
5735
|
let startState = this.view.state;
|
|
5679
5736
|
let handled = this.onChange(from, to, typeOver);
|
|
@@ -6227,12 +6284,15 @@ class EditorView {
|
|
|
6227
6284
|
cancelAnimationFrame(this.measureScheduled);
|
|
6228
6285
|
this.measureScheduled = 0; // Prevent requestMeasure calls from scheduling another animation frame
|
|
6229
6286
|
if (flush)
|
|
6230
|
-
this.observer.
|
|
6287
|
+
this.observer.forceFlush();
|
|
6231
6288
|
let updated = null;
|
|
6289
|
+
let { scrollHeight, scrollTop, clientHeight } = this.scrollDOM;
|
|
6290
|
+
let refHeight = scrollTop > scrollHeight - clientHeight - 4 ? scrollHeight : scrollTop;
|
|
6232
6291
|
try {
|
|
6233
6292
|
for (let i = 0;; i++) {
|
|
6234
6293
|
this.updateState = 1 /* Measuring */;
|
|
6235
6294
|
let oldViewport = this.viewport;
|
|
6295
|
+
let refBlock = this.viewState.lineBlockAtHeight(refHeight);
|
|
6236
6296
|
let changed = this.viewState.measure(this);
|
|
6237
6297
|
if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
|
|
6238
6298
|
break;
|
|
@@ -6284,6 +6344,13 @@ class EditorView {
|
|
|
6284
6344
|
this.viewState.scrollTarget = null;
|
|
6285
6345
|
scrolled = true;
|
|
6286
6346
|
}
|
|
6347
|
+
else {
|
|
6348
|
+
let diff = this.viewState.lineBlockAt(refBlock.from).top - refBlock.top;
|
|
6349
|
+
if (diff > 1 || diff < -1) {
|
|
6350
|
+
this.scrollDOM.scrollTop += diff;
|
|
6351
|
+
scrolled = true;
|
|
6352
|
+
}
|
|
6353
|
+
}
|
|
6287
6354
|
if (redrawn)
|
|
6288
6355
|
this.docView.updateSelection(true);
|
|
6289
6356
|
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
|
|
@@ -7495,7 +7562,7 @@ class MatchDecorator {
|
|
|
7495
7562
|
}
|
|
7496
7563
|
|
|
7497
7564
|
const UnicodeRegexpSupport = /x/.unicode != null ? "gu" : "g";
|
|
7498
|
-
const Specials = new RegExp("[\u0000-\u0008\u000a-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\ufeff\ufff9-\ufffc]", UnicodeRegexpSupport);
|
|
7565
|
+
const Specials = new RegExp("[\u0000-\u0008\u000a-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\u2066\u2067\u2069\ufeff\ufff9-\ufffc]", UnicodeRegexpSupport);
|
|
7499
7566
|
const Names = {
|
|
7500
7567
|
0: "null",
|
|
7501
7568
|
7: "bell",
|
|
@@ -7512,6 +7579,9 @@ const Names = {
|
|
|
7512
7579
|
8232: "line separator",
|
|
7513
7580
|
8237: "left-to-right override",
|
|
7514
7581
|
8238: "right-to-left override",
|
|
7582
|
+
8294: "left-to-right isolate",
|
|
7583
|
+
8295: "right-to-left isolate",
|
|
7584
|
+
8297: "pop directional isolate",
|
|
7515
7585
|
8233: "paragraph separator",
|
|
7516
7586
|
65279: "zero width no-break space",
|
|
7517
7587
|
65532: "object replacement"
|
package/dist/index.js
CHANGED
|
@@ -264,6 +264,31 @@ function clearAttributes(node) {
|
|
|
264
264
|
while (node.attributes.length)
|
|
265
265
|
node.removeAttributeNode(node.attributes[0]);
|
|
266
266
|
}
|
|
267
|
+
function atElementStart(doc, selection) {
|
|
268
|
+
let node = selection.focusNode, offset = selection.focusOffset;
|
|
269
|
+
if (!node || selection.anchorNode != node || selection.anchorOffset != offset)
|
|
270
|
+
return false;
|
|
271
|
+
for (;;) {
|
|
272
|
+
if (offset) {
|
|
273
|
+
if (node.nodeType != 1)
|
|
274
|
+
return false;
|
|
275
|
+
let prev = node.childNodes[offset - 1];
|
|
276
|
+
if (prev.contentEditable == "false")
|
|
277
|
+
offset--;
|
|
278
|
+
else {
|
|
279
|
+
node = prev;
|
|
280
|
+
offset = maxOffset(node);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
else if (node == doc) {
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
offset = domIndex(node);
|
|
288
|
+
node = node.parentNode;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
267
292
|
|
|
268
293
|
class DOMPos {
|
|
269
294
|
constructor(node, offset, precise = true) {
|
|
@@ -1443,15 +1468,17 @@ class LineView extends ContentView {
|
|
|
1443
1468
|
return null;
|
|
1444
1469
|
let totalWidth = 0;
|
|
1445
1470
|
for (let child of this.children) {
|
|
1446
|
-
if (!(child instanceof TextView))
|
|
1471
|
+
if (!(child instanceof TextView) || /[^ -~]/.test(child.text))
|
|
1447
1472
|
return null;
|
|
1448
1473
|
let rects = clientRectsFor(child.dom);
|
|
1449
1474
|
if (rects.length != 1)
|
|
1450
1475
|
return null;
|
|
1451
1476
|
totalWidth += rects[0].width;
|
|
1452
1477
|
}
|
|
1453
|
-
return
|
|
1454
|
-
|
|
1478
|
+
return !totalWidth ? null : {
|
|
1479
|
+
lineHeight: this.dom.getBoundingClientRect().height,
|
|
1480
|
+
charWidth: totalWidth / this.length
|
|
1481
|
+
};
|
|
1455
1482
|
}
|
|
1456
1483
|
coordsAt(pos, side) {
|
|
1457
1484
|
return coordsInChildren(this, pos, side);
|
|
@@ -3283,6 +3310,10 @@ class InputState {
|
|
|
3283
3310
|
constructor(view) {
|
|
3284
3311
|
this.lastKeyCode = 0;
|
|
3285
3312
|
this.lastKeyTime = 0;
|
|
3313
|
+
this.lastTouchTime = 0;
|
|
3314
|
+
this.lastFocusTime = 0;
|
|
3315
|
+
this.lastScrollTop = 0;
|
|
3316
|
+
this.lastScrollLeft = 0;
|
|
3286
3317
|
this.chromeScrollHack = -1;
|
|
3287
3318
|
// On iOS, some keys need to have their default behavior happen
|
|
3288
3319
|
// (after which we retroactively handle them and reset the DOM) to
|
|
@@ -3321,10 +3352,10 @@ class InputState {
|
|
|
3321
3352
|
event.preventDefault();
|
|
3322
3353
|
else
|
|
3323
3354
|
handler(view, event);
|
|
3324
|
-
});
|
|
3355
|
+
}, handlerOptions[type]);
|
|
3325
3356
|
this.registeredEvents.push(type);
|
|
3326
3357
|
}
|
|
3327
|
-
if (browser.chrome && browser.chrome_version
|
|
3358
|
+
if (browser.chrome && browser.chrome_version == 102) { // FIXME remove at some point
|
|
3328
3359
|
// On Chrome 102, viewport updates somehow stop wheel-based
|
|
3329
3360
|
// scrolling. Turning off pointer events during the scroll seems
|
|
3330
3361
|
// to avoid the issue.
|
|
@@ -3384,6 +3415,8 @@ class InputState {
|
|
|
3384
3415
|
return false;
|
|
3385
3416
|
}
|
|
3386
3417
|
runScrollHandlers(view, event) {
|
|
3418
|
+
this.lastScrollTop = view.scrollDOM.scrollTop;
|
|
3419
|
+
this.lastScrollLeft = view.scrollDOM.scrollLeft;
|
|
3387
3420
|
for (let set of this.customHandlers) {
|
|
3388
3421
|
let handler = set.handlers.scroll;
|
|
3389
3422
|
if (handler) {
|
|
@@ -3444,7 +3477,7 @@ class InputState {
|
|
|
3444
3477
|
// compositionend and keydown events are sometimes emitted in the
|
|
3445
3478
|
// wrong order. The key event should still be ignored, even when
|
|
3446
3479
|
// it happens after the compositionend event.
|
|
3447
|
-
if (browser.safari && Date.now() - this.compositionEndedAt < 100) {
|
|
3480
|
+
if (browser.safari && !browser.ios && Date.now() - this.compositionEndedAt < 100) {
|
|
3448
3481
|
this.compositionEndedAt = 0;
|
|
3449
3482
|
return true;
|
|
3450
3483
|
}
|
|
@@ -3572,6 +3605,7 @@ function eventBelongsToEditor(view, event) {
|
|
|
3572
3605
|
return true;
|
|
3573
3606
|
}
|
|
3574
3607
|
const handlers = /*@__PURE__*/Object.create(null);
|
|
3608
|
+
const handlerOptions = /*@__PURE__*/Object.create(null);
|
|
3575
3609
|
// This is very crude, but unfortunately both these browsers _pretend_
|
|
3576
3610
|
// that they have a clipboard API—all the objects and methods are
|
|
3577
3611
|
// there, they just don't work, and they are hard to test.
|
|
@@ -3628,17 +3662,17 @@ handlers.keydown = (view, event) => {
|
|
|
3628
3662
|
else if (modifierCodes.indexOf(event.keyCode) < 0)
|
|
3629
3663
|
view.inputState.lastEscPress = 0;
|
|
3630
3664
|
};
|
|
3631
|
-
let lastTouch = 0;
|
|
3632
3665
|
handlers.touchstart = (view, e) => {
|
|
3633
|
-
|
|
3666
|
+
view.inputState.lastTouchTime = Date.now();
|
|
3634
3667
|
view.inputState.setSelectionOrigin("select.pointer");
|
|
3635
3668
|
};
|
|
3636
3669
|
handlers.touchmove = view => {
|
|
3637
3670
|
view.inputState.setSelectionOrigin("select.pointer");
|
|
3638
3671
|
};
|
|
3672
|
+
handlerOptions.touchstart = handlerOptions.touchmove = { passive: true };
|
|
3639
3673
|
handlers.mousedown = (view, event) => {
|
|
3640
3674
|
view.observer.flush();
|
|
3641
|
-
if (
|
|
3675
|
+
if (view.inputState.lastTouchTime > Date.now() - 2000 && getClickType(event) == 1)
|
|
3642
3676
|
return; // Ignore touch interaction
|
|
3643
3677
|
let style = null;
|
|
3644
3678
|
for (let makeStyle of view.state.facet(mouseSelectionStyle)) {
|
|
@@ -3892,7 +3926,15 @@ function updateForFocusChange(view) {
|
|
|
3892
3926
|
view.update([]);
|
|
3893
3927
|
}, 10);
|
|
3894
3928
|
}
|
|
3895
|
-
handlers.focus =
|
|
3929
|
+
handlers.focus = view => {
|
|
3930
|
+
view.inputState.lastFocusTime = Date.now();
|
|
3931
|
+
// When focusing reset the scroll position, move it back to where it was
|
|
3932
|
+
if (!view.scrollDOM.scrollTop && (view.inputState.lastScrollTop || view.inputState.lastScrollLeft)) {
|
|
3933
|
+
view.scrollDOM.scrollTop = view.inputState.lastScrollTop;
|
|
3934
|
+
view.scrollDOM.scrollLeft = view.inputState.lastScrollLeft;
|
|
3935
|
+
}
|
|
3936
|
+
updateForFocusChange(view);
|
|
3937
|
+
};
|
|
3896
3938
|
handlers.blur = view => {
|
|
3897
3939
|
view.observer.clearSelectionRange();
|
|
3898
3940
|
updateForFocusChange(view);
|
|
@@ -4628,7 +4670,7 @@ function visiblePixelRange(dom, paddingTop) {
|
|
|
4628
4670
|
left = Math.max(left, parentRect.left);
|
|
4629
4671
|
right = Math.min(right, parentRect.right);
|
|
4630
4672
|
top = Math.max(top, parentRect.top);
|
|
4631
|
-
bottom = Math.min(bottom, parentRect.bottom);
|
|
4673
|
+
bottom = parent == dom.parentNode ? parentRect.bottom : Math.min(bottom, parentRect.bottom);
|
|
4632
4674
|
}
|
|
4633
4675
|
parent = style.position == "absolute" || style.position == "fixed" ? elt.offsetParent : elt.parentNode;
|
|
4634
4676
|
}
|
|
@@ -5262,6 +5304,7 @@ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
|
|
|
5262
5304
|
"&light .cm-specialChar": { color: "red" },
|
|
5263
5305
|
"&dark .cm-specialChar": { color: "#f78" },
|
|
5264
5306
|
".cm-gutters": {
|
|
5307
|
+
flexShrink: 0,
|
|
5265
5308
|
display: "flex",
|
|
5266
5309
|
height: "100%",
|
|
5267
5310
|
boxSizing: "border-box",
|
|
@@ -5518,15 +5561,28 @@ class DOMObserver {
|
|
|
5518
5561
|
this.flush(false);
|
|
5519
5562
|
}
|
|
5520
5563
|
readSelectionRange() {
|
|
5521
|
-
let {
|
|
5564
|
+
let { view } = this;
|
|
5522
5565
|
// The Selection object is broken in shadow roots in Safari. See
|
|
5523
5566
|
// https://github.com/codemirror/dev/issues/414
|
|
5524
|
-
let range = browser.safari && root.nodeType == 11 && deepActiveElement() == this.
|
|
5525
|
-
safariSelectionRangeHack(this.view) || getSelection(root);
|
|
5567
|
+
let range = browser.safari && view.root.nodeType == 11 && deepActiveElement() == this.dom &&
|
|
5568
|
+
safariSelectionRangeHack(this.view) || getSelection(view.root);
|
|
5526
5569
|
if (!range || this.selectionRange.eq(range))
|
|
5527
5570
|
return false;
|
|
5571
|
+
let local = hasSelection(this.dom, range);
|
|
5572
|
+
// Detect the situation where the browser has, on focus, moved the
|
|
5573
|
+
// selection to the start of the content element. Reset it to the
|
|
5574
|
+
// position from the editor state.
|
|
5575
|
+
if (local && !this.selectionChanged && this.selectionRange.focusNode &&
|
|
5576
|
+
view.inputState.lastFocusTime > Date.now() - 200 &&
|
|
5577
|
+
view.inputState.lastTouchTime < Date.now() - 300 &&
|
|
5578
|
+
atElementStart(this.dom, range)) {
|
|
5579
|
+
view.docView.updateSelection();
|
|
5580
|
+
return false;
|
|
5581
|
+
}
|
|
5528
5582
|
this.selectionRange.setRange(range);
|
|
5529
|
-
|
|
5583
|
+
if (local)
|
|
5584
|
+
this.selectionChanged = true;
|
|
5585
|
+
return true;
|
|
5530
5586
|
}
|
|
5531
5587
|
setSelectionRange(anchor, head) {
|
|
5532
5588
|
this.selectionRange.set(anchor.node, anchor.offset, head.node, head.offset);
|
|
@@ -5613,7 +5669,7 @@ class DOMObserver {
|
|
|
5613
5669
|
this.delayedAndroidKey = null;
|
|
5614
5670
|
this.delayedFlush = -1;
|
|
5615
5671
|
if (!this.flush())
|
|
5616
|
-
dispatchKey(this.
|
|
5672
|
+
dispatchKey(this.dom, key.key, key.keyCode);
|
|
5617
5673
|
});
|
|
5618
5674
|
// Since backspace beforeinput is sometimes signalled spuriously,
|
|
5619
5675
|
// Enter always takes precedence.
|
|
@@ -5628,8 +5684,8 @@ class DOMObserver {
|
|
|
5628
5684
|
if (this.delayedFlush >= 0) {
|
|
5629
5685
|
window.clearTimeout(this.delayedFlush);
|
|
5630
5686
|
this.delayedFlush = -1;
|
|
5631
|
-
this.flush();
|
|
5632
5687
|
}
|
|
5688
|
+
this.flush();
|
|
5633
5689
|
}
|
|
5634
5690
|
processRecords() {
|
|
5635
5691
|
let records = this.queue;
|
|
@@ -5667,6 +5723,7 @@ class DOMObserver {
|
|
|
5667
5723
|
let newSel = this.selectionChanged && hasSelection(this.dom, this.selectionRange);
|
|
5668
5724
|
if (from < 0 && !newSel)
|
|
5669
5725
|
return;
|
|
5726
|
+
this.view.inputState.lastFocusTime = 0;
|
|
5670
5727
|
this.selectionChanged = false;
|
|
5671
5728
|
let startState = this.view.state;
|
|
5672
5729
|
let handled = this.onChange(from, to, typeOver);
|
|
@@ -6220,12 +6277,15 @@ class EditorView {
|
|
|
6220
6277
|
cancelAnimationFrame(this.measureScheduled);
|
|
6221
6278
|
this.measureScheduled = 0; // Prevent requestMeasure calls from scheduling another animation frame
|
|
6222
6279
|
if (flush)
|
|
6223
|
-
this.observer.
|
|
6280
|
+
this.observer.forceFlush();
|
|
6224
6281
|
let updated = null;
|
|
6282
|
+
let { scrollHeight, scrollTop, clientHeight } = this.scrollDOM;
|
|
6283
|
+
let refHeight = scrollTop > scrollHeight - clientHeight - 4 ? scrollHeight : scrollTop;
|
|
6225
6284
|
try {
|
|
6226
6285
|
for (let i = 0;; i++) {
|
|
6227
6286
|
this.updateState = 1 /* Measuring */;
|
|
6228
6287
|
let oldViewport = this.viewport;
|
|
6288
|
+
let refBlock = this.viewState.lineBlockAtHeight(refHeight);
|
|
6229
6289
|
let changed = this.viewState.measure(this);
|
|
6230
6290
|
if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
|
|
6231
6291
|
break;
|
|
@@ -6277,6 +6337,13 @@ class EditorView {
|
|
|
6277
6337
|
this.viewState.scrollTarget = null;
|
|
6278
6338
|
scrolled = true;
|
|
6279
6339
|
}
|
|
6340
|
+
else {
|
|
6341
|
+
let diff = this.viewState.lineBlockAt(refBlock.from).top - refBlock.top;
|
|
6342
|
+
if (diff > 1 || diff < -1) {
|
|
6343
|
+
this.scrollDOM.scrollTop += diff;
|
|
6344
|
+
scrolled = true;
|
|
6345
|
+
}
|
|
6346
|
+
}
|
|
6280
6347
|
if (redrawn)
|
|
6281
6348
|
this.docView.updateSelection(true);
|
|
6282
6349
|
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
|
|
@@ -7488,7 +7555,7 @@ class MatchDecorator {
|
|
|
7488
7555
|
}
|
|
7489
7556
|
|
|
7490
7557
|
const UnicodeRegexpSupport = /x/.unicode != null ? "gu" : "g";
|
|
7491
|
-
const Specials = /*@__PURE__*/new RegExp("[\u0000-\u0008\u000a-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\ufeff\ufff9-\ufffc]", UnicodeRegexpSupport);
|
|
7558
|
+
const Specials = /*@__PURE__*/new RegExp("[\u0000-\u0008\u000a-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\u2066\u2067\u2069\ufeff\ufff9-\ufffc]", UnicodeRegexpSupport);
|
|
7492
7559
|
const Names = {
|
|
7493
7560
|
0: "null",
|
|
7494
7561
|
7: "bell",
|
|
@@ -7505,6 +7572,9 @@ const Names = {
|
|
|
7505
7572
|
8232: "line separator",
|
|
7506
7573
|
8237: "left-to-right override",
|
|
7507
7574
|
8238: "right-to-left override",
|
|
7575
|
+
8294: "left-to-right isolate",
|
|
7576
|
+
8295: "right-to-left isolate",
|
|
7577
|
+
8297: "pop directional isolate",
|
|
7508
7578
|
8233: "paragraph separator",
|
|
7509
7579
|
65279: "zero width no-break space",
|
|
7510
7580
|
65532: "object replacement"
|