@codemirror/view 0.19.6 → 0.19.10
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 +50 -0
- package/dist/index.cjs +327 -120
- package/dist/index.d.ts +17 -8
- package/dist/index.js +328 -121
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
import { MapMode, Text as Text$1, Facet, StateEffect, ChangeSet, EditorSelection, CharCategory, EditorState, Transaction, Prec, combineConfig } from '@codemirror/state';
|
|
2
2
|
import { StyleModule } from 'style-mod';
|
|
3
|
-
import {
|
|
3
|
+
import { RangeSet, RangeValue, RangeSetBuilder } from '@codemirror/rangeset';
|
|
4
4
|
export { Range } from '@codemirror/rangeset';
|
|
5
5
|
import { Text, findClusterBreak, findColumn, codePointAt, countColumn } from '@codemirror/text';
|
|
6
6
|
import { keyName, base } from 'w3c-keyname';
|
|
7
7
|
|
|
8
8
|
function getSelection(root) {
|
|
9
|
-
|
|
9
|
+
let target;
|
|
10
|
+
// Browsers differ on whether shadow roots have a getSelection
|
|
11
|
+
// method. If it exists, use that, otherwise, call it on the
|
|
12
|
+
// document.
|
|
13
|
+
if (root.nodeType == 11) { // Shadow root
|
|
14
|
+
target = root.getSelection ? root : root.ownerDocument;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
target = root;
|
|
18
|
+
}
|
|
19
|
+
return target.getSelection();
|
|
10
20
|
}
|
|
11
21
|
function contains(dom, node) {
|
|
12
22
|
return node ? dom.contains(node.nodeType != 1 ? node.parentNode : node) : false;
|
|
@@ -238,6 +248,14 @@ function contentEditablePlainTextSupported() {
|
|
|
238
248
|
}
|
|
239
249
|
return _plainTextSupported;
|
|
240
250
|
}
|
|
251
|
+
function getRoot(node) {
|
|
252
|
+
while (node) {
|
|
253
|
+
node = node.assignedSlot || node.parentNode;
|
|
254
|
+
if (node && (node.nodeType == 9 || node.nodeType == 11 && node.host))
|
|
255
|
+
return node;
|
|
256
|
+
}
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
241
259
|
|
|
242
260
|
class DOMPos {
|
|
243
261
|
constructor(node, offset, precise = true) {
|
|
@@ -435,6 +453,7 @@ class ContentView {
|
|
|
435
453
|
(this.breakAfter ? "#" : "");
|
|
436
454
|
}
|
|
437
455
|
static get(node) { return node.cmView; }
|
|
456
|
+
get isEditable() { return true; }
|
|
438
457
|
}
|
|
439
458
|
ContentView.prototype.breakAfter = 0;
|
|
440
459
|
// Remove a DOM node and return its next sibling.
|
|
@@ -482,15 +501,18 @@ const gecko = !ie && /*@__PURE__*//gecko\/(\d+)/i.test(nav.userAgent);
|
|
|
482
501
|
const chrome = !ie && /*@__PURE__*//Chrome\/(\d+)/.exec(nav.userAgent);
|
|
483
502
|
const webkit = "webkitFontSmoothing" in doc.documentElement.style;
|
|
484
503
|
const safari = !ie && /*@__PURE__*//Apple Computer/.test(nav.vendor);
|
|
504
|
+
const ios = safari && (/*@__PURE__*//Mobile\/\w+/.test(nav.userAgent) || nav.maxTouchPoints > 2);
|
|
485
505
|
var browser = {
|
|
486
|
-
mac: /*@__PURE__*//Mac/.test(nav.platform),
|
|
506
|
+
mac: ios || /*@__PURE__*//Mac/.test(nav.platform),
|
|
507
|
+
windows: /*@__PURE__*//Win/.test(nav.platform),
|
|
508
|
+
linux: /*@__PURE__*//Linux|X11/.test(nav.platform),
|
|
487
509
|
ie,
|
|
488
510
|
ie_version: ie_upto10 ? doc.documentMode || 6 : ie_11up ? +ie_11up[1] : ie_edge ? +ie_edge[1] : 0,
|
|
489
511
|
gecko,
|
|
490
512
|
gecko_version: gecko ? +(/*@__PURE__*//Firefox\/(\d+)/.exec(nav.userAgent) || [0, 0])[1] : 0,
|
|
491
513
|
chrome: !!chrome,
|
|
492
514
|
chrome_version: chrome ? +chrome[1] : 0,
|
|
493
|
-
ios
|
|
515
|
+
ios,
|
|
494
516
|
android: /*@__PURE__*//Android\b/.test(nav.userAgent),
|
|
495
517
|
webkit,
|
|
496
518
|
safari,
|
|
@@ -712,6 +734,7 @@ class WidgetView extends InlineView {
|
|
|
712
734
|
}
|
|
713
735
|
return (pos == 0 && side > 0 || pos == this.length && side <= 0) ? rect : flattenRect(rect, pos == 0);
|
|
714
736
|
}
|
|
737
|
+
get isEditable() { return false; }
|
|
715
738
|
}
|
|
716
739
|
class CompositionView extends WidgetView {
|
|
717
740
|
domAtPos(pos) { return new DOMPos(this.widget.text, pos); }
|
|
@@ -723,6 +746,38 @@ class CompositionView extends WidgetView {
|
|
|
723
746
|
ignoreMutation() { return false; }
|
|
724
747
|
get overrideDOMText() { return null; }
|
|
725
748
|
coordsAt(pos, side) { return textCoords(this.widget.text, pos, side); }
|
|
749
|
+
get isEditable() { return true; }
|
|
750
|
+
}
|
|
751
|
+
// These are drawn around uneditable widgets to avoid a number of
|
|
752
|
+
// browser bugs that show up when the cursor is directly next to
|
|
753
|
+
// uneditable inline content.
|
|
754
|
+
class WidgetBufferView extends InlineView {
|
|
755
|
+
constructor(side) {
|
|
756
|
+
super();
|
|
757
|
+
this.side = side;
|
|
758
|
+
}
|
|
759
|
+
get length() { return 0; }
|
|
760
|
+
merge() { return false; }
|
|
761
|
+
become(other) {
|
|
762
|
+
return other instanceof WidgetBufferView && other.side == this.side;
|
|
763
|
+
}
|
|
764
|
+
slice() { return new WidgetBufferView(this.side); }
|
|
765
|
+
sync() {
|
|
766
|
+
if (!this.dom)
|
|
767
|
+
this.setDOM(document.createTextNode("\u200b"));
|
|
768
|
+
else if (this.dirty && this.dom.nodeValue != "\u200b")
|
|
769
|
+
this.dom.nodeValue = "\u200b";
|
|
770
|
+
}
|
|
771
|
+
getSide() { return this.side; }
|
|
772
|
+
domAtPos(pos) { return DOMPos.before(this.dom); }
|
|
773
|
+
domBoundsAround() { return null; }
|
|
774
|
+
coordsAt(pos) {
|
|
775
|
+
let rects = clientRectsFor(this.dom);
|
|
776
|
+
return rects[rects.length - 1];
|
|
777
|
+
}
|
|
778
|
+
get overrideDOMText() {
|
|
779
|
+
return Text.of([this.dom.nodeValue.replace(/\u200b/g, "")]);
|
|
780
|
+
}
|
|
726
781
|
}
|
|
727
782
|
function mergeInlineChildren(parent, from, to, elts, openStart, openEnd) {
|
|
728
783
|
let cur = parent.childCursor();
|
|
@@ -1217,14 +1272,17 @@ class LineView extends ContentView {
|
|
|
1217
1272
|
}
|
|
1218
1273
|
// Only called when building a line view in ContentBuilder
|
|
1219
1274
|
addLineDeco(deco) {
|
|
1220
|
-
let attrs = deco.spec.attributes;
|
|
1275
|
+
let attrs = deco.spec.attributes, cls = deco.spec.class;
|
|
1221
1276
|
if (attrs)
|
|
1222
1277
|
this.attrs = combineAttrs(attrs, this.attrs || {});
|
|
1278
|
+
if (cls)
|
|
1279
|
+
this.attrs = combineAttrs(attrs, { class: cls });
|
|
1223
1280
|
}
|
|
1224
1281
|
domAtPos(pos) {
|
|
1225
1282
|
return inlineDOMAtPos(this.dom, this.children, pos);
|
|
1226
1283
|
}
|
|
1227
1284
|
sync(track) {
|
|
1285
|
+
var _a;
|
|
1228
1286
|
if (!this.dom || (this.dirty & 4 /* Attrs */)) {
|
|
1229
1287
|
this.setDOM(document.createElement("div"));
|
|
1230
1288
|
this.dom.className = "cm-line";
|
|
@@ -1240,7 +1298,7 @@ class LineView extends ContentView {
|
|
|
1240
1298
|
while (last && ContentView.get(last) instanceof MarkView)
|
|
1241
1299
|
last = last.lastChild;
|
|
1242
1300
|
if (!last ||
|
|
1243
|
-
last.nodeName != "BR" && ContentView.get(last)
|
|
1301
|
+
last.nodeName != "BR" && ((_a = ContentView.get(last)) === null || _a === void 0 ? void 0 : _a.isEditable) == false &&
|
|
1244
1302
|
(!browser.ios || !this.children.some(ch => ch instanceof TextView))) {
|
|
1245
1303
|
let hack = document.createElement("BR");
|
|
1246
1304
|
hack.cmIgnore = true;
|
|
@@ -1339,6 +1397,9 @@ class ContentBuilder {
|
|
|
1339
1397
|
this.content = [];
|
|
1340
1398
|
this.curLine = null;
|
|
1341
1399
|
this.breakAtStart = 0;
|
|
1400
|
+
this.pendingBuffer = 0 /* No */;
|
|
1401
|
+
// Set to false directly after a widget that covers the position after it
|
|
1402
|
+
this.atCursorPos = true;
|
|
1342
1403
|
this.openStart = -1;
|
|
1343
1404
|
this.openEnd = -1;
|
|
1344
1405
|
this.text = "";
|
|
@@ -1353,23 +1414,31 @@ class ContentBuilder {
|
|
|
1353
1414
|
return !last.breakAfter && !(last instanceof BlockWidgetView && last.type == BlockType.WidgetBefore);
|
|
1354
1415
|
}
|
|
1355
1416
|
getLine() {
|
|
1356
|
-
if (!this.curLine)
|
|
1417
|
+
if (!this.curLine) {
|
|
1357
1418
|
this.content.push(this.curLine = new LineView);
|
|
1419
|
+
this.atCursorPos = true;
|
|
1420
|
+
}
|
|
1358
1421
|
return this.curLine;
|
|
1359
1422
|
}
|
|
1360
|
-
|
|
1423
|
+
flushBuffer(active) {
|
|
1424
|
+
if (this.pendingBuffer) {
|
|
1425
|
+
this.curLine.append(wrapMarks(new WidgetBufferView(-1), active), active.length);
|
|
1426
|
+
this.pendingBuffer = 0 /* No */;
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
addBlockWidget(view) {
|
|
1430
|
+
this.flushBuffer([]);
|
|
1361
1431
|
this.curLine = null;
|
|
1362
1432
|
this.content.push(view);
|
|
1363
1433
|
}
|
|
1364
|
-
finish() {
|
|
1434
|
+
finish(openEnd) {
|
|
1435
|
+
if (!openEnd)
|
|
1436
|
+
this.flushBuffer([]);
|
|
1437
|
+
else
|
|
1438
|
+
this.pendingBuffer = 0 /* No */;
|
|
1365
1439
|
if (!this.posCovered())
|
|
1366
1440
|
this.getLine();
|
|
1367
1441
|
}
|
|
1368
|
-
wrapMarks(view, active) {
|
|
1369
|
-
for (let mark of active)
|
|
1370
|
-
view = new MarkView(mark, [view], view.length);
|
|
1371
|
-
return view;
|
|
1372
|
-
}
|
|
1373
1442
|
buildText(length, active, openStart) {
|
|
1374
1443
|
while (length > 0) {
|
|
1375
1444
|
if (this.textOff == this.text.length) {
|
|
@@ -1384,6 +1453,7 @@ class ContentBuilder {
|
|
|
1384
1453
|
this.content[this.content.length - 1].breakAfter = 1;
|
|
1385
1454
|
else
|
|
1386
1455
|
this.breakAtStart = 1;
|
|
1456
|
+
this.flushBuffer([]);
|
|
1387
1457
|
this.curLine = null;
|
|
1388
1458
|
length--;
|
|
1389
1459
|
continue;
|
|
@@ -1394,7 +1464,9 @@ class ContentBuilder {
|
|
|
1394
1464
|
}
|
|
1395
1465
|
}
|
|
1396
1466
|
let take = Math.min(this.text.length - this.textOff, length, 512 /* Chunk */);
|
|
1397
|
-
this.
|
|
1467
|
+
this.flushBuffer(active);
|
|
1468
|
+
this.getLine().append(wrapMarks(new TextView(this.text.slice(this.textOff, this.textOff + take)), active), openStart);
|
|
1469
|
+
this.atCursorPos = true;
|
|
1398
1470
|
this.textOff += take;
|
|
1399
1471
|
length -= take;
|
|
1400
1472
|
openStart = 0;
|
|
@@ -1413,11 +1485,23 @@ class ContentBuilder {
|
|
|
1413
1485
|
let { type } = deco;
|
|
1414
1486
|
if (type == BlockType.WidgetAfter && !this.posCovered())
|
|
1415
1487
|
this.getLine();
|
|
1416
|
-
this.
|
|
1488
|
+
this.addBlockWidget(new BlockWidgetView(deco.widget || new NullWidget("div"), len, type));
|
|
1417
1489
|
}
|
|
1418
1490
|
else {
|
|
1419
|
-
let
|
|
1420
|
-
this.
|
|
1491
|
+
let view = WidgetView.create(deco.widget || new NullWidget("span"), len, deco.startSide);
|
|
1492
|
+
let cursorBefore = this.atCursorPos && !view.isEditable && openStart <= active.length && (from < to || deco.startSide > 0);
|
|
1493
|
+
let cursorAfter = !view.isEditable && (from < to || deco.startSide <= 0);
|
|
1494
|
+
let line = this.getLine();
|
|
1495
|
+
if (this.pendingBuffer == 2 /* IfCursor */ && !cursorBefore)
|
|
1496
|
+
this.pendingBuffer = 0 /* No */;
|
|
1497
|
+
this.flushBuffer(active);
|
|
1498
|
+
if (cursorBefore) {
|
|
1499
|
+
line.append(wrapMarks(new WidgetBufferView(1), active), openStart);
|
|
1500
|
+
openStart = active.length + Math.max(0, openStart - active.length);
|
|
1501
|
+
}
|
|
1502
|
+
line.append(wrapMarks(view, active), openStart);
|
|
1503
|
+
this.atCursorPos = cursorAfter;
|
|
1504
|
+
this.pendingBuffer = !cursorAfter ? 0 /* No */ : from < to ? 1 /* Yes */ : 2 /* IfCursor */;
|
|
1421
1505
|
}
|
|
1422
1506
|
}
|
|
1423
1507
|
else if (this.doc.lineAt(this.pos).from == this.pos) { // Line decoration
|
|
@@ -1443,10 +1527,15 @@ class ContentBuilder {
|
|
|
1443
1527
|
builder.openEnd = RangeSet.spans(decorations, from, to, builder);
|
|
1444
1528
|
if (builder.openStart < 0)
|
|
1445
1529
|
builder.openStart = builder.openEnd;
|
|
1446
|
-
builder.finish();
|
|
1530
|
+
builder.finish(builder.openEnd);
|
|
1447
1531
|
return builder;
|
|
1448
1532
|
}
|
|
1449
1533
|
}
|
|
1534
|
+
function wrapMarks(view, active) {
|
|
1535
|
+
for (let mark of active)
|
|
1536
|
+
view = new MarkView(mark, [view], view.length);
|
|
1537
|
+
return view;
|
|
1538
|
+
}
|
|
1450
1539
|
class NullWidget extends WidgetType {
|
|
1451
1540
|
constructor(tag) {
|
|
1452
1541
|
super();
|
|
@@ -1779,7 +1868,9 @@ class ViewUpdate {
|
|
|
1779
1868
|
this.flags |= 2 /* Height */;
|
|
1780
1869
|
}
|
|
1781
1870
|
/**
|
|
1782
|
-
Tells you whether the viewport
|
|
1871
|
+
Tells you whether the [viewport](https://codemirror.net/6/docs/ref/#view.EditorView.viewport) or
|
|
1872
|
+
[visible ranges](https://codemirror.net/6/docs/ref/#view.EditorView.visibleRanges) changed in this
|
|
1873
|
+
update.
|
|
1783
1874
|
*/
|
|
1784
1875
|
get viewportChanged() {
|
|
1785
1876
|
return (this.flags & 4 /* Viewport */) > 0;
|
|
@@ -1795,7 +1886,7 @@ class ViewUpdate {
|
|
|
1795
1886
|
or the lines or characters within it has changed.
|
|
1796
1887
|
*/
|
|
1797
1888
|
get geometryChanged() {
|
|
1798
|
-
return this.docChanged || (this.flags & (
|
|
1889
|
+
return this.docChanged || (this.flags & (8 /* Geometry */ | 2 /* Height */)) > 0;
|
|
1799
1890
|
}
|
|
1800
1891
|
/**
|
|
1801
1892
|
True when this update indicates a focus change.
|
|
@@ -1880,7 +1971,7 @@ class DocView extends ContentView {
|
|
|
1880
1971
|
changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
|
|
1881
1972
|
let pointerSel = update.transactions.some(tr => tr.isUserEvent("select.pointer"));
|
|
1882
1973
|
if (this.dirty == 0 /* Not */ && changedRanges.length == 0 &&
|
|
1883
|
-
!(update.flags &
|
|
1974
|
+
!(update.flags & 4 /* Viewport */) &&
|
|
1884
1975
|
update.state.selection.main.from >= this.view.viewport.from &&
|
|
1885
1976
|
update.state.selection.main.to <= this.view.viewport.to) {
|
|
1886
1977
|
this.updateSelection(forceSelection, pointerSel);
|
|
@@ -1891,6 +1982,14 @@ class DocView extends ContentView {
|
|
|
1891
1982
|
return true;
|
|
1892
1983
|
}
|
|
1893
1984
|
}
|
|
1985
|
+
reset(sel) {
|
|
1986
|
+
if (this.dirty) {
|
|
1987
|
+
this.view.observer.ignore(() => this.view.docView.sync());
|
|
1988
|
+
this.dirty = 0 /* Not */;
|
|
1989
|
+
}
|
|
1990
|
+
if (sel)
|
|
1991
|
+
this.updateSelection();
|
|
1992
|
+
}
|
|
1894
1993
|
// Used both by update and checkLayout do perform the actual DOM
|
|
1895
1994
|
// update
|
|
1896
1995
|
updateInner(changes, deco, oldLength, forceSelection = false, pointerSel = false) {
|
|
@@ -1915,6 +2014,12 @@ class DocView extends ContentView {
|
|
|
1915
2014
|
this.updateSelection(forceSelection, pointerSel);
|
|
1916
2015
|
this.dom.style.height = "";
|
|
1917
2016
|
});
|
|
2017
|
+
let gaps = [];
|
|
2018
|
+
if (this.view.viewport.from || this.view.viewport.to < this.view.state.doc.length)
|
|
2019
|
+
for (let child of this.children)
|
|
2020
|
+
if (child instanceof BlockWidgetView && child.widget instanceof BlockGapWidget)
|
|
2021
|
+
gaps.push(child.dom);
|
|
2022
|
+
observer.updateGaps(gaps);
|
|
1918
2023
|
}
|
|
1919
2024
|
updateChildren(changes, deco, oldLength) {
|
|
1920
2025
|
let cursor = this.childCursor(oldLength);
|
|
@@ -2014,6 +2119,14 @@ class DocView extends ContentView {
|
|
|
2014
2119
|
!isEquivalentPosition(anchor.node, anchor.offset, domSel.anchorNode, domSel.anchorOffset) ||
|
|
2015
2120
|
!isEquivalentPosition(head.node, head.offset, domSel.focusNode, domSel.focusOffset)) {
|
|
2016
2121
|
this.view.observer.ignore(() => {
|
|
2122
|
+
// Chrome Android will hide the virtual keyboard when tapping
|
|
2123
|
+
// inside an uneditable node, and not bring it back when we
|
|
2124
|
+
// move the cursor to its proper position. This tries to
|
|
2125
|
+
// restore the keyboard by cycling focus.
|
|
2126
|
+
if (browser.android && browser.chrome && this.dom.contains(domSel.focusNode) && inUneditable(domSel.focusNode, this.dom)) {
|
|
2127
|
+
this.dom.blur();
|
|
2128
|
+
this.dom.focus({ preventScroll: true });
|
|
2129
|
+
}
|
|
2017
2130
|
let rawSel = getSelection(this.root);
|
|
2018
2131
|
if (main.empty) {
|
|
2019
2132
|
// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=1612076
|
|
@@ -2321,6 +2434,14 @@ function findChangedDeco(a, b, diff) {
|
|
|
2321
2434
|
RangeSet.compare(a, b, diff, comp);
|
|
2322
2435
|
return comp.changes;
|
|
2323
2436
|
}
|
|
2437
|
+
function inUneditable(node, inside) {
|
|
2438
|
+
for (let cur = node; cur && cur != inside; cur = cur.assignedSlot || cur.parentNode) {
|
|
2439
|
+
if (cur.nodeType == 1 && cur.contentEditable == 'false') {
|
|
2440
|
+
return true;
|
|
2441
|
+
}
|
|
2442
|
+
}
|
|
2443
|
+
return false;
|
|
2444
|
+
}
|
|
2324
2445
|
|
|
2325
2446
|
/**
|
|
2326
2447
|
Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
|
|
@@ -2766,6 +2887,7 @@ function domPosInText(node, x, y) {
|
|
|
2766
2887
|
return { node, offset: closestOffset > -1 ? closestOffset : generalSide > 0 ? node.nodeValue.length : 0 };
|
|
2767
2888
|
}
|
|
2768
2889
|
function posAtCoords(view, { x, y }, precise, bias = -1) {
|
|
2890
|
+
var _a;
|
|
2769
2891
|
let content = view.contentDOM.getBoundingClientRect(), block;
|
|
2770
2892
|
let halfLine = view.defaultLineHeight / 2;
|
|
2771
2893
|
for (let bounced = false;;) {
|
|
@@ -2783,25 +2905,27 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
|
|
|
2783
2905
|
y = bias > 0 ? block.bottom + halfLine : block.top - halfLine;
|
|
2784
2906
|
}
|
|
2785
2907
|
let lineStart = block.from;
|
|
2908
|
+
// Clip x to the viewport sides
|
|
2786
2909
|
x = Math.max(content.left + 1, Math.min(content.right - 1, x));
|
|
2787
2910
|
// If this is outside of the rendered viewport, we can't determine a position
|
|
2788
2911
|
if (lineStart < view.viewport.from)
|
|
2789
2912
|
return view.viewport.from == 0 ? 0 : posAtCoordsImprecise(view, content, block, x, y);
|
|
2790
2913
|
if (lineStart > view.viewport.to)
|
|
2791
2914
|
return view.viewport.to == view.state.doc.length ? view.state.doc.length : posAtCoordsImprecise(view, content, block, x, y);
|
|
2792
|
-
//
|
|
2793
|
-
let
|
|
2915
|
+
// Prefer ShadowRootOrDocument.elementFromPoint if present, fall back to document if not
|
|
2916
|
+
let doc = view.dom.ownerDocument;
|
|
2917
|
+
let element = (view.root.elementFromPoint ? view.root : doc).elementFromPoint(x, y);
|
|
2794
2918
|
// There's visible editor content under the point, so we can try
|
|
2795
2919
|
// using caret(Position|Range)FromPoint as a shortcut
|
|
2796
2920
|
let node, offset = -1;
|
|
2797
|
-
if (element && view.contentDOM.contains(element) &&
|
|
2798
|
-
if (
|
|
2799
|
-
let pos =
|
|
2921
|
+
if (element && view.contentDOM.contains(element) && ((_a = view.docView.nearest(element)) === null || _a === void 0 ? void 0 : _a.isEditable) != false) {
|
|
2922
|
+
if (doc.caretPositionFromPoint) {
|
|
2923
|
+
let pos = doc.caretPositionFromPoint(x, y);
|
|
2800
2924
|
if (pos)
|
|
2801
2925
|
({ offsetNode: node, offset } = pos);
|
|
2802
2926
|
}
|
|
2803
|
-
else if (
|
|
2804
|
-
let range =
|
|
2927
|
+
else if (doc.caretRangeFromPoint) {
|
|
2928
|
+
let range = doc.caretRangeFromPoint(x, y);
|
|
2805
2929
|
if (range) {
|
|
2806
2930
|
({ startContainer: node, startOffset: offset } = range);
|
|
2807
2931
|
if (browser.safari && isSuspiciousCaretResult(node, offset, x))
|
|
@@ -2935,7 +3059,23 @@ class InputState {
|
|
|
2935
3059
|
constructor(view) {
|
|
2936
3060
|
this.lastKeyCode = 0;
|
|
2937
3061
|
this.lastKeyTime = 0;
|
|
2938
|
-
|
|
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
|
+
// On Chrome Android, backspace near widgets is just completely
|
|
3067
|
+
// broken, and there are no key events, so we need to handle the
|
|
3068
|
+
// beforeinput event. Deleting stuff will often create a flurry of
|
|
3069
|
+
// events, and interrupting it before it is done just makes
|
|
3070
|
+
// subsequent events even more broken, so again, we hold off doing
|
|
3071
|
+
// anything until the browser is finished with whatever it is trying
|
|
3072
|
+
// to do.
|
|
3073
|
+
//
|
|
3074
|
+
// setPendingKey sets this, causing the DOM observer to pause for a
|
|
3075
|
+
// bit, and setting an animation frame (which seems the most
|
|
3076
|
+
// reliable way to detect 'browser is done flailing') to fire a fake
|
|
3077
|
+
// key event and re-sync the view again.
|
|
3078
|
+
this.pendingKey = undefined;
|
|
2939
3079
|
this.lastSelectionOrigin = null;
|
|
2940
3080
|
this.lastSelectionTime = 0;
|
|
2941
3081
|
this.lastEscPress = 0;
|
|
@@ -3044,20 +3184,27 @@ class InputState {
|
|
|
3044
3184
|
// state. So we let it go through, and then, in
|
|
3045
3185
|
// applyDOMChange, notify key handlers of it and reset to
|
|
3046
3186
|
// the state they produce.
|
|
3047
|
-
|
|
3187
|
+
let pending;
|
|
3188
|
+
if (browser.ios && (pending = PendingKeys.find(key => key.keyCode == event.keyCode)) &&
|
|
3048
3189
|
!(event.ctrlKey || event.altKey || event.metaKey) && !event.synthetic) {
|
|
3049
|
-
this.
|
|
3050
|
-
setTimeout(() => this.flushIOSKey(view), 250);
|
|
3190
|
+
this.setPendingKey(view, pending);
|
|
3051
3191
|
return true;
|
|
3052
3192
|
}
|
|
3053
3193
|
return false;
|
|
3054
3194
|
}
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3195
|
+
setPendingKey(view, pending) {
|
|
3196
|
+
this.pendingKey = pending;
|
|
3197
|
+
requestAnimationFrame(() => {
|
|
3198
|
+
if (!this.pendingKey)
|
|
3199
|
+
return false;
|
|
3200
|
+
let key = this.pendingKey;
|
|
3201
|
+
this.pendingKey = undefined;
|
|
3202
|
+
view.observer.processRecords();
|
|
3203
|
+
let startState = view.state;
|
|
3204
|
+
dispatchKey(view.contentDOM, key.key, key.keyCode);
|
|
3205
|
+
if (view.state == startState)
|
|
3206
|
+
view.docView.reset(true);
|
|
3207
|
+
});
|
|
3061
3208
|
}
|
|
3062
3209
|
ignoreDuringComposition(event) {
|
|
3063
3210
|
if (!/^key/.test(event.type))
|
|
@@ -3104,6 +3251,11 @@ class InputState {
|
|
|
3104
3251
|
this.mouseSelection.destroy();
|
|
3105
3252
|
}
|
|
3106
3253
|
}
|
|
3254
|
+
const PendingKeys = [
|
|
3255
|
+
{ key: "Backspace", keyCode: 8, inputType: "deleteContentBackward" },
|
|
3256
|
+
{ key: "Enter", keyCode: 13, inputType: "insertParagraph" },
|
|
3257
|
+
{ key: "Delete", keyCode: 46, inputType: "deleteContentForward" }
|
|
3258
|
+
];
|
|
3107
3259
|
// Key codes for modifier keys
|
|
3108
3260
|
const modifierCodes = [16, 17, 18, 20, 91, 92, 224, 225];
|
|
3109
3261
|
class MouseSelection {
|
|
@@ -3220,7 +3372,7 @@ function capturePaste(view) {
|
|
|
3220
3372
|
function doPaste(view, input) {
|
|
3221
3373
|
let { state } = view, changes, i = 1, text = state.toText(input);
|
|
3222
3374
|
let byLine = text.lines == state.selection.ranges.length;
|
|
3223
|
-
let linewise = lastLinewiseCopy && state.selection.ranges.every(r => r.empty) && lastLinewiseCopy == text.toString();
|
|
3375
|
+
let linewise = lastLinewiseCopy != null && state.selection.ranges.every(r => r.empty) && lastLinewiseCopy == text.toString();
|
|
3224
3376
|
if (linewise) {
|
|
3225
3377
|
let lastLine = -1;
|
|
3226
3378
|
changes = state.changeByRange(range => {
|
|
@@ -3399,8 +3551,10 @@ function dropText(view, event, text, direct) {
|
|
|
3399
3551
|
});
|
|
3400
3552
|
}
|
|
3401
3553
|
handlers.drop = (view, event) => {
|
|
3402
|
-
if (!event.dataTransfer
|
|
3554
|
+
if (!event.dataTransfer)
|
|
3403
3555
|
return;
|
|
3556
|
+
if (view.state.readOnly)
|
|
3557
|
+
return event.preventDefault();
|
|
3404
3558
|
let files = event.dataTransfer.files;
|
|
3405
3559
|
if (files && files.length) { // For a file drop, read the file's text.
|
|
3406
3560
|
event.preventDefault();
|
|
@@ -3425,13 +3579,12 @@ handlers.drop = (view, event) => {
|
|
|
3425
3579
|
}
|
|
3426
3580
|
};
|
|
3427
3581
|
handlers.paste = (view, event) => {
|
|
3428
|
-
if (
|
|
3429
|
-
return;
|
|
3582
|
+
if (view.state.readOnly)
|
|
3583
|
+
return event.preventDefault();
|
|
3430
3584
|
view.observer.flush();
|
|
3431
3585
|
let data = brokenClipboardAPI ? null : event.clipboardData;
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
doPaste(view, text);
|
|
3586
|
+
if (data) {
|
|
3587
|
+
doPaste(view, data.getData("text/plain"));
|
|
3435
3588
|
event.preventDefault();
|
|
3436
3589
|
}
|
|
3437
3590
|
else {
|
|
@@ -3480,7 +3633,7 @@ function copiedRange(state) {
|
|
|
3480
3633
|
let lastLinewiseCopy = null;
|
|
3481
3634
|
handlers.copy = handlers.cut = (view, event) => {
|
|
3482
3635
|
let { text, ranges, linewise } = copiedRange(view.state);
|
|
3483
|
-
if (!text)
|
|
3636
|
+
if (!text && !linewise)
|
|
3484
3637
|
return;
|
|
3485
3638
|
lastLinewiseCopy = linewise ? text : null;
|
|
3486
3639
|
let data = brokenClipboardAPI ? null : event.clipboardData;
|
|
@@ -3492,7 +3645,7 @@ handlers.copy = handlers.cut = (view, event) => {
|
|
|
3492
3645
|
else {
|
|
3493
3646
|
captureCopy(view, text);
|
|
3494
3647
|
}
|
|
3495
|
-
if (event.type == "cut" && view.state.
|
|
3648
|
+
if (event.type == "cut" && !view.state.readOnly)
|
|
3496
3649
|
view.dispatch({
|
|
3497
3650
|
changes: ranges,
|
|
3498
3651
|
scrollIntoView: true,
|
|
@@ -3548,6 +3701,31 @@ handlers.compositionend = view => {
|
|
|
3548
3701
|
handlers.contextmenu = view => {
|
|
3549
3702
|
view.inputState.lastContextMenu = Date.now();
|
|
3550
3703
|
};
|
|
3704
|
+
handlers.beforeinput = (view, event) => {
|
|
3705
|
+
var _a;
|
|
3706
|
+
// Because Chrome Android doesn't fire useful key events, use
|
|
3707
|
+
// beforeinput to detect backspace (and possibly enter and delete,
|
|
3708
|
+
// but those usually don't even seem to fire beforeinput events at
|
|
3709
|
+
// the moment) and fake a key event for it.
|
|
3710
|
+
//
|
|
3711
|
+
// (preventDefault on beforeinput, though supported in the spec,
|
|
3712
|
+
// seems to do nothing at all on Chrome).
|
|
3713
|
+
let pending;
|
|
3714
|
+
if (browser.chrome && browser.android && (pending = PendingKeys.find(key => key.inputType == event.inputType))) {
|
|
3715
|
+
view.inputState.setPendingKey(view, pending);
|
|
3716
|
+
let startViewHeight = ((_a = window.visualViewport) === null || _a === void 0 ? void 0 : _a.height) || 0;
|
|
3717
|
+
setTimeout(() => {
|
|
3718
|
+
var _a;
|
|
3719
|
+
// Backspacing near uneditable nodes on Chrome Android sometimes
|
|
3720
|
+
// closes the virtual keyboard. This tries to crudely detect
|
|
3721
|
+
// that and refocus to get it back.
|
|
3722
|
+
if ((((_a = window.visualViewport) === null || _a === void 0 ? void 0 : _a.height) || 0) > startViewHeight + 10 && view.hasFocus) {
|
|
3723
|
+
view.contentDOM.blur();
|
|
3724
|
+
view.focus();
|
|
3725
|
+
}
|
|
3726
|
+
}, 50);
|
|
3727
|
+
}
|
|
3728
|
+
};
|
|
3551
3729
|
|
|
3552
3730
|
const wrappingWhiteSpace = ["pre-wrap", "normal", "pre-line"];
|
|
3553
3731
|
class HeightOracle {
|
|
@@ -4320,14 +4498,11 @@ class ViewState {
|
|
|
4320
4498
|
let viewport = heightChanges.length ? this.mapViewport(this.viewport, update.changes) : this.viewport;
|
|
4321
4499
|
if (scrollTo && (scrollTo.head < viewport.from || scrollTo.head > viewport.to) || !this.viewportIsAppropriate(viewport))
|
|
4322
4500
|
viewport = this.getViewport(0, scrollTo);
|
|
4323
|
-
|
|
4324
|
-
this.viewport = viewport;
|
|
4325
|
-
update.flags |= 4 /* Viewport */;
|
|
4326
|
-
}
|
|
4501
|
+
this.viewport = viewport;
|
|
4327
4502
|
this.updateForViewport();
|
|
4328
4503
|
if (this.lineGaps.length || this.viewport.to - this.viewport.from > 15000 /* MinViewPort */)
|
|
4329
|
-
|
|
4330
|
-
this.computeVisibleRanges();
|
|
4504
|
+
this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps, update.changes)));
|
|
4505
|
+
update.flags |= this.computeVisibleRanges();
|
|
4331
4506
|
if (scrollTo)
|
|
4332
4507
|
this.scrollTo = scrollTo;
|
|
4333
4508
|
if (!this.mustEnforceCursorAssoc && update.selectionSet && update.view.lineWrapping &&
|
|
@@ -4336,12 +4511,17 @@ class ViewState {
|
|
|
4336
4511
|
}
|
|
4337
4512
|
measure(docView, repeated) {
|
|
4338
4513
|
let dom = docView.dom, whiteSpace = "", direction = Direction.LTR;
|
|
4514
|
+
let result = 0;
|
|
4339
4515
|
if (!repeated) {
|
|
4340
4516
|
// Vertical padding
|
|
4341
4517
|
let style = window.getComputedStyle(dom);
|
|
4342
4518
|
whiteSpace = style.whiteSpace, direction = (style.direction == "rtl" ? Direction.RTL : Direction.LTR);
|
|
4343
|
-
|
|
4344
|
-
this.
|
|
4519
|
+
let paddingTop = parseInt(style.paddingTop) || 0, paddingBottom = parseInt(style.paddingBottom) || 0;
|
|
4520
|
+
if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) {
|
|
4521
|
+
result |= 8 /* Geometry */;
|
|
4522
|
+
this.paddingTop = paddingTop;
|
|
4523
|
+
this.paddingBottom = paddingBottom;
|
|
4524
|
+
}
|
|
4345
4525
|
}
|
|
4346
4526
|
// Pixel viewport
|
|
4347
4527
|
let pixelViewport = this.printing ? { top: -1e8, bottom: 1e8, left: -1e8, right: 1e8 } : visiblePixelRange(dom, this.paddingTop);
|
|
@@ -4351,7 +4531,7 @@ class ViewState {
|
|
|
4351
4531
|
if (!this.inView)
|
|
4352
4532
|
return 0;
|
|
4353
4533
|
let lineHeights = docView.measureVisibleLineHeights();
|
|
4354
|
-
let refresh = false, bias = 0,
|
|
4534
|
+
let refresh = false, bias = 0, oracle = this.heightOracle;
|
|
4355
4535
|
if (!repeated) {
|
|
4356
4536
|
let contentWidth = docView.dom.clientWidth;
|
|
4357
4537
|
if (oracle.mustRefresh(lineHeights, whiteSpace, direction) ||
|
|
@@ -4360,12 +4540,12 @@ class ViewState {
|
|
|
4360
4540
|
refresh = oracle.refresh(whiteSpace, direction, lineHeight, charWidth, contentWidth / charWidth, lineHeights);
|
|
4361
4541
|
if (refresh) {
|
|
4362
4542
|
docView.minWidth = 0;
|
|
4363
|
-
result |=
|
|
4543
|
+
result |= 8 /* Geometry */;
|
|
4364
4544
|
}
|
|
4365
4545
|
}
|
|
4366
4546
|
if (this.contentWidth != contentWidth) {
|
|
4367
4547
|
this.contentWidth = contentWidth;
|
|
4368
|
-
result |=
|
|
4548
|
+
result |= 8 /* Geometry */;
|
|
4369
4549
|
}
|
|
4370
4550
|
if (dTop > 0 && dBottom > 0)
|
|
4371
4551
|
bias = Math.max(dTop, dBottom);
|
|
@@ -4377,17 +4557,12 @@ class ViewState {
|
|
|
4377
4557
|
if (oracle.heightChanged)
|
|
4378
4558
|
result |= 2 /* Height */;
|
|
4379
4559
|
if (!this.viewportIsAppropriate(this.viewport, bias) ||
|
|
4380
|
-
this.scrollTo && (this.scrollTo.head < this.viewport.from || this.scrollTo.head > this.viewport.to))
|
|
4381
|
-
|
|
4382
|
-
if (newVP.from != this.viewport.from || newVP.to != this.viewport.to) {
|
|
4383
|
-
this.viewport = newVP;
|
|
4384
|
-
result |= 4 /* Viewport */;
|
|
4385
|
-
}
|
|
4386
|
-
}
|
|
4560
|
+
this.scrollTo && (this.scrollTo.head < this.viewport.from || this.scrollTo.head > this.viewport.to))
|
|
4561
|
+
this.viewport = this.getViewport(bias, this.scrollTo);
|
|
4387
4562
|
this.updateForViewport();
|
|
4388
4563
|
if (this.lineGaps.length || this.viewport.to - this.viewport.from > 15000 /* MinViewPort */)
|
|
4389
|
-
|
|
4390
|
-
this.computeVisibleRanges();
|
|
4564
|
+
this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps));
|
|
4565
|
+
result |= this.computeVisibleRanges();
|
|
4391
4566
|
if (this.mustEnforceCursorAssoc) {
|
|
4392
4567
|
this.mustEnforceCursorAssoc = false;
|
|
4393
4568
|
// This is done in the read stage, because moving the selection
|
|
@@ -4509,9 +4684,7 @@ class ViewState {
|
|
|
4509
4684
|
if (!LineGap.same(gaps, this.lineGaps)) {
|
|
4510
4685
|
this.lineGaps = gaps;
|
|
4511
4686
|
this.lineGapDeco = Decoration.set(gaps.map(gap => gap.draw(this.heightOracle.lineWrapping)));
|
|
4512
|
-
return 8 /* LineGaps */;
|
|
4513
4687
|
}
|
|
4514
|
-
return 0;
|
|
4515
4688
|
}
|
|
4516
4689
|
computeVisibleRanges() {
|
|
4517
4690
|
let deco = this.state.facet(decorations);
|
|
@@ -4522,7 +4695,10 @@ class ViewState {
|
|
|
4522
4695
|
span(from, to) { ranges.push({ from, to }); },
|
|
4523
4696
|
point() { }
|
|
4524
4697
|
}, 20);
|
|
4698
|
+
let changed = ranges.length != this.visibleRanges.length ||
|
|
4699
|
+
this.visibleRanges.some((r, i) => r.from != ranges[i].from || r.to != ranges[i].to);
|
|
4525
4700
|
this.visibleRanges = ranges;
|
|
4701
|
+
return changed ? 4 /* Viewport */ : 0;
|
|
4526
4702
|
}
|
|
4527
4703
|
lineAt(pos, editorTop) {
|
|
4528
4704
|
editorTop += this.paddingTop;
|
|
@@ -4547,16 +4723,11 @@ class ViewState {
|
|
|
4547
4723
|
return this.scaler.toDOM(this.heightMap.height, this.paddingTop);
|
|
4548
4724
|
}
|
|
4549
4725
|
}
|
|
4550
|
-
/**
|
|
4551
|
-
Indicates the range of the document that is in the visible
|
|
4552
|
-
viewport.
|
|
4553
|
-
*/
|
|
4554
4726
|
class Viewport {
|
|
4555
4727
|
constructor(from, to) {
|
|
4556
4728
|
this.from = from;
|
|
4557
4729
|
this.to = to;
|
|
4558
4730
|
}
|
|
4559
|
-
eq(b) { return this.from == b.from && this.to == b.to; }
|
|
4560
4731
|
}
|
|
4561
4732
|
function lineStructure(from, to, state) {
|
|
4562
4733
|
let ranges = [], pos = from, total = 0;
|
|
@@ -4682,7 +4853,7 @@ function buildTheme(main, spec, scopes) {
|
|
|
4682
4853
|
});
|
|
4683
4854
|
}
|
|
4684
4855
|
const baseTheme = /*@__PURE__*/buildTheme("." + baseThemeID, {
|
|
4685
|
-
"
|
|
4856
|
+
"&.cm-editor": {
|
|
4686
4857
|
position: "relative !important",
|
|
4687
4858
|
boxSizing: "border-box",
|
|
4688
4859
|
"&.cm-focused": {
|
|
@@ -4849,6 +5020,8 @@ class DOMObserver {
|
|
|
4849
5020
|
this.scrollTargets = [];
|
|
4850
5021
|
this.intersection = null;
|
|
4851
5022
|
this.intersecting = false;
|
|
5023
|
+
this.gapIntersection = null;
|
|
5024
|
+
this.gaps = [];
|
|
4852
5025
|
// Used to work around a Safari Selection/shadow DOM bug (#414)
|
|
4853
5026
|
this._selectionRange = null;
|
|
4854
5027
|
// Timeout for scheduling check of the parents that need scroll handlers
|
|
@@ -4889,13 +5062,17 @@ class DOMObserver {
|
|
|
4889
5062
|
this.intersection = new IntersectionObserver(entries => {
|
|
4890
5063
|
if (this.parentCheck < 0)
|
|
4891
5064
|
this.parentCheck = setTimeout(this.listenForScroll.bind(this), 1000);
|
|
4892
|
-
if (entries[entries.length - 1].intersectionRatio > 0 != this.intersecting) {
|
|
5065
|
+
if (entries.length > 0 && entries[entries.length - 1].intersectionRatio > 0 != this.intersecting) {
|
|
4893
5066
|
this.intersecting = !this.intersecting;
|
|
4894
5067
|
if (this.intersecting != this.view.inView)
|
|
4895
5068
|
this.onScrollChanged(document.createEvent("Event"));
|
|
4896
5069
|
}
|
|
4897
5070
|
}, {});
|
|
4898
5071
|
this.intersection.observe(this.dom);
|
|
5072
|
+
this.gapIntersection = new IntersectionObserver(entries => {
|
|
5073
|
+
if (entries.length > 0 && entries[entries.length - 1].intersectionRatio > 0)
|
|
5074
|
+
this.onScrollChanged(document.createEvent("Event"));
|
|
5075
|
+
}, {});
|
|
4899
5076
|
}
|
|
4900
5077
|
this.listenForScroll();
|
|
4901
5078
|
}
|
|
@@ -4904,6 +5081,14 @@ class DOMObserver {
|
|
|
4904
5081
|
this.flush();
|
|
4905
5082
|
this.onScrollChanged(e);
|
|
4906
5083
|
}
|
|
5084
|
+
updateGaps(gaps) {
|
|
5085
|
+
if (this.gapIntersection && (gaps.length != this.gaps.length || this.gaps.some((g, i) => g != gaps[i]))) {
|
|
5086
|
+
this.gapIntersection.disconnect();
|
|
5087
|
+
for (let gap of gaps)
|
|
5088
|
+
this.gapIntersection.observe(gap);
|
|
5089
|
+
this.gaps = gaps;
|
|
5090
|
+
}
|
|
5091
|
+
}
|
|
4907
5092
|
onSelectionChange(event) {
|
|
4908
5093
|
if (this.lastFlush < Date.now() - 50)
|
|
4909
5094
|
this._selectionRange = null;
|
|
@@ -5019,20 +5204,12 @@ class DOMObserver {
|
|
|
5019
5204
|
this.flush();
|
|
5020
5205
|
}
|
|
5021
5206
|
}
|
|
5022
|
-
|
|
5023
|
-
flush() {
|
|
5024
|
-
if (this.delayedFlush >= 0)
|
|
5025
|
-
return;
|
|
5026
|
-
this.lastFlush = Date.now();
|
|
5207
|
+
processRecords() {
|
|
5027
5208
|
let records = this.queue;
|
|
5028
5209
|
for (let mut of this.observer.takeRecords())
|
|
5029
5210
|
records.push(mut);
|
|
5030
5211
|
if (records.length)
|
|
5031
5212
|
this.queue = [];
|
|
5032
|
-
let selection = this.selectionRange;
|
|
5033
|
-
let newSel = !this.ignoreSelection.eq(selection) && hasSelection(this.dom, selection);
|
|
5034
|
-
if (records.length == 0 && !newSel)
|
|
5035
|
-
return;
|
|
5036
5213
|
let from = -1, to = -1, typeOver = false;
|
|
5037
5214
|
for (let record of records) {
|
|
5038
5215
|
let range = this.readMutation(record);
|
|
@@ -5048,17 +5225,26 @@ class DOMObserver {
|
|
|
5048
5225
|
to = Math.max(range.to, to);
|
|
5049
5226
|
}
|
|
5050
5227
|
}
|
|
5228
|
+
return { from, to, typeOver };
|
|
5229
|
+
}
|
|
5230
|
+
// Apply pending changes, if any
|
|
5231
|
+
flush() {
|
|
5232
|
+
// Completely hold off flushing when pending keys are set—the code
|
|
5233
|
+
// managing those will make sure processRecords is called and the
|
|
5234
|
+
// view is resynchronized after
|
|
5235
|
+
if (this.delayedFlush >= 0 || this.view.inputState.pendingKey)
|
|
5236
|
+
return;
|
|
5237
|
+
this.lastFlush = Date.now();
|
|
5238
|
+
let { from, to, typeOver } = this.processRecords();
|
|
5239
|
+
let selection = this.selectionRange;
|
|
5240
|
+
let newSel = !this.ignoreSelection.eq(selection) && hasSelection(this.dom, selection);
|
|
5241
|
+
if (from < 0 && !newSel)
|
|
5242
|
+
return;
|
|
5051
5243
|
let startState = this.view.state;
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
if (this.view.state == startState)
|
|
5055
|
-
|
|
5056
|
-
this.ignore(() => this.view.docView.sync());
|
|
5057
|
-
this.view.docView.dirty = 0 /* Not */;
|
|
5058
|
-
}
|
|
5059
|
-
if (newSel)
|
|
5060
|
-
this.view.docView.updateSelection();
|
|
5061
|
-
}
|
|
5244
|
+
this.onChange(from, to, typeOver);
|
|
5245
|
+
// The view wasn't updated
|
|
5246
|
+
if (this.view.state == startState)
|
|
5247
|
+
this.view.docView.reset(newSel);
|
|
5062
5248
|
this.clearSelection();
|
|
5063
5249
|
}
|
|
5064
5250
|
readMutation(rec) {
|
|
@@ -5085,6 +5271,8 @@ class DOMObserver {
|
|
|
5085
5271
|
this.stop();
|
|
5086
5272
|
if (this.intersection)
|
|
5087
5273
|
this.intersection.disconnect();
|
|
5274
|
+
if (this.gapIntersection)
|
|
5275
|
+
this.gapIntersection.disconnect();
|
|
5088
5276
|
for (let dom of this.scrollTargets)
|
|
5089
5277
|
dom.removeEventListener("scroll", this.onScroll);
|
|
5090
5278
|
window.removeEventListener("scroll", this.onScroll);
|
|
@@ -5132,7 +5320,7 @@ function safariSelectionRangeHack(view) {
|
|
|
5132
5320
|
function applyDOMChange(view, start, end, typeOver) {
|
|
5133
5321
|
let change, newSel;
|
|
5134
5322
|
let sel = view.state.selection.main, bounds;
|
|
5135
|
-
if (start > -1 && (bounds = view.docView.domBoundsAround(start, end, 0))) {
|
|
5323
|
+
if (start > -1 && !view.state.readOnly && (bounds = view.docView.domBoundsAround(start, end, 0))) {
|
|
5136
5324
|
let { from, to } = bounds;
|
|
5137
5325
|
let selPoints = view.docView.impreciseHead || view.docView.impreciseAnchor ? [] : selectionPoints(view);
|
|
5138
5326
|
let reader = new DOMReader(selPoints, view);
|
|
@@ -5193,9 +5381,9 @@ function applyDOMChange(view, start, end, typeOver) {
|
|
|
5193
5381
|
(change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 &&
|
|
5194
5382
|
dispatchKey(view.contentDOM, "Backspace", 8)) ||
|
|
5195
5383
|
(change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
|
|
5196
|
-
dispatchKey(view.contentDOM, "Delete", 46)))
|
|
5197
|
-
browser.ios && view.inputState.flushIOSKey(view))
|
|
5384
|
+
dispatchKey(view.contentDOM, "Delete", 46)))) {
|
|
5198
5385
|
return;
|
|
5386
|
+
}
|
|
5199
5387
|
let text = change.insert.toString();
|
|
5200
5388
|
if (view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text)))
|
|
5201
5389
|
return;
|
|
@@ -5285,8 +5473,9 @@ class DOMReader {
|
|
|
5285
5473
|
if (next == end)
|
|
5286
5474
|
break;
|
|
5287
5475
|
let view = ContentView.get(cur), nextView = ContentView.get(next);
|
|
5288
|
-
if (
|
|
5289
|
-
(
|
|
5476
|
+
if (view && nextView ? view.breakAfter :
|
|
5477
|
+
(view ? view.breakAfter : isBlockElement(cur)) ||
|
|
5478
|
+
(isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore)))
|
|
5290
5479
|
this.text += this.lineBreak;
|
|
5291
5480
|
cur = next;
|
|
5292
5481
|
}
|
|
@@ -5389,6 +5578,7 @@ class EditorView {
|
|
|
5389
5578
|
this.editorAttrs = {};
|
|
5390
5579
|
this.contentAttrs = {};
|
|
5391
5580
|
this.bidiCache = [];
|
|
5581
|
+
this.destroyed = false;
|
|
5392
5582
|
/**
|
|
5393
5583
|
@internal
|
|
5394
5584
|
*/
|
|
@@ -5414,7 +5604,7 @@ class EditorView {
|
|
|
5414
5604
|
this.dom.appendChild(this.scrollDOM);
|
|
5415
5605
|
this._dispatch = config.dispatch || ((tr) => this.update([tr]));
|
|
5416
5606
|
this.dispatch = this.dispatch.bind(this);
|
|
5417
|
-
this.root = (config.root || document);
|
|
5607
|
+
this.root = (config.root || getRoot(config.parent) || document);
|
|
5418
5608
|
this.viewState = new ViewState(config.state || EditorState.create());
|
|
5419
5609
|
this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec).update(this));
|
|
5420
5610
|
this.observer = new DOMObserver(this, (from, to, typeOver) => {
|
|
@@ -5487,6 +5677,10 @@ class EditorView {
|
|
|
5487
5677
|
throw new RangeError("Trying to update state with a transaction that doesn't start from the previous state.");
|
|
5488
5678
|
state = tr.state;
|
|
5489
5679
|
}
|
|
5680
|
+
if (this.destroyed) {
|
|
5681
|
+
this.viewState.state = state;
|
|
5682
|
+
return;
|
|
5683
|
+
}
|
|
5490
5684
|
// When the phrases change, redraw the editor
|
|
5491
5685
|
if (state.facet(EditorState.phrases) != this.state.facet(EditorState.phrases))
|
|
5492
5686
|
return this.setState(state);
|
|
@@ -5536,6 +5730,10 @@ class EditorView {
|
|
|
5536
5730
|
setState(newState) {
|
|
5537
5731
|
if (this.updateState != 0 /* Idle */)
|
|
5538
5732
|
throw new Error("Calls to EditorView.setState are not allowed while an update is in progress");
|
|
5733
|
+
if (this.destroyed) {
|
|
5734
|
+
this.viewState.state = newState;
|
|
5735
|
+
return;
|
|
5736
|
+
}
|
|
5539
5737
|
this.updateState = 2 /* Updating */;
|
|
5540
5738
|
try {
|
|
5541
5739
|
for (let plugin of this.plugins)
|
|
@@ -5585,6 +5783,8 @@ class EditorView {
|
|
|
5585
5783
|
@internal
|
|
5586
5784
|
*/
|
|
5587
5785
|
measure(flush = true) {
|
|
5786
|
+
if (this.destroyed)
|
|
5787
|
+
return;
|
|
5588
5788
|
if (this.measureScheduled > -1)
|
|
5589
5789
|
cancelAnimationFrame(this.measureScheduled);
|
|
5590
5790
|
this.measureScheduled = -1; // Prevent requestMeasure calls from scheduling another animation frame
|
|
@@ -5594,15 +5794,18 @@ class EditorView {
|
|
|
5594
5794
|
try {
|
|
5595
5795
|
for (let i = 0;; i++) {
|
|
5596
5796
|
this.updateState = 1 /* Measuring */;
|
|
5797
|
+
let oldViewport = this.viewport;
|
|
5597
5798
|
let changed = this.viewState.measure(this.docView, i > 0);
|
|
5598
|
-
|
|
5599
|
-
if (!changed && !measuring.length && this.viewState.scrollTo == null)
|
|
5799
|
+
if (!changed && !this.measureRequests.length && this.viewState.scrollTo == null)
|
|
5600
5800
|
break;
|
|
5601
|
-
this.measureRequests = [];
|
|
5602
5801
|
if (i > 5) {
|
|
5603
5802
|
console.warn("Viewport failed to stabilize");
|
|
5604
5803
|
break;
|
|
5605
5804
|
}
|
|
5805
|
+
let measuring = [];
|
|
5806
|
+
// Only run measure requests in this cycle when the viewport didn't change
|
|
5807
|
+
if (!(changed & 4 /* Viewport */))
|
|
5808
|
+
[this.measureRequests, measuring] = [measuring, this.measureRequests];
|
|
5606
5809
|
let measured = measuring.map(m => {
|
|
5607
5810
|
try {
|
|
5608
5811
|
return m.read(this);
|
|
@@ -5639,7 +5842,7 @@ class EditorView {
|
|
|
5639
5842
|
this.docView.scrollRangeIntoView(this.viewState.scrollTo);
|
|
5640
5843
|
this.viewState.scrollTo = null;
|
|
5641
5844
|
}
|
|
5642
|
-
if (
|
|
5845
|
+
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to && this.measureRequests.length == 0)
|
|
5643
5846
|
break;
|
|
5644
5847
|
}
|
|
5645
5848
|
}
|
|
@@ -5665,16 +5868,20 @@ class EditorView {
|
|
|
5665
5868
|
});
|
|
5666
5869
|
updateAttrs(this.dom, this.editorAttrs, editorAttrs);
|
|
5667
5870
|
this.editorAttrs = editorAttrs;
|
|
5668
|
-
let contentAttrs =
|
|
5871
|
+
let contentAttrs = {
|
|
5669
5872
|
spellcheck: "false",
|
|
5670
5873
|
autocorrect: "off",
|
|
5671
5874
|
autocapitalize: "off",
|
|
5875
|
+
translate: "no",
|
|
5672
5876
|
contenteditable: !this.state.facet(editable) ? "false" : contentEditablePlainTextSupported() ? "plaintext-only" : "true",
|
|
5673
5877
|
class: "cm-content",
|
|
5674
5878
|
style: `${browser.tabSize}: ${this.state.tabSize}`,
|
|
5675
5879
|
role: "textbox",
|
|
5676
5880
|
"aria-multiline": "true"
|
|
5677
|
-
}
|
|
5881
|
+
};
|
|
5882
|
+
if (this.state.readOnly)
|
|
5883
|
+
contentAttrs["aria-readonly"] = "true";
|
|
5884
|
+
combineAttrs(this.state.facet(contentAttributes), contentAttrs);
|
|
5678
5885
|
updateAttrs(this.contentDOM, this.contentAttrs, contentAttrs);
|
|
5679
5886
|
this.contentAttrs = contentAttrs;
|
|
5680
5887
|
}
|
|
@@ -5972,11 +6179,13 @@ class EditorView {
|
|
|
5972
6179
|
destroy() {
|
|
5973
6180
|
for (let plugin of this.plugins)
|
|
5974
6181
|
plugin.destroy(this);
|
|
6182
|
+
this.plugins = [];
|
|
5975
6183
|
this.inputState.destroy();
|
|
5976
6184
|
this.dom.remove();
|
|
5977
6185
|
this.observer.destroy();
|
|
5978
6186
|
if (this.measureScheduled > -1)
|
|
5979
6187
|
cancelAnimationFrame(this.measureScheduled);
|
|
6188
|
+
this.destroyed = true;
|
|
5980
6189
|
}
|
|
5981
6190
|
/**
|
|
5982
6191
|
Facet that can be used to add DOM event handlers. The value
|
|
@@ -6064,12 +6273,12 @@ every time the view updates.
|
|
|
6064
6273
|
*/
|
|
6065
6274
|
EditorView.updateListener = updateListener;
|
|
6066
6275
|
/**
|
|
6067
|
-
Facet that controls whether the editor content is editable.
|
|
6068
|
-
its highest-precedence value is `false`,
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6276
|
+
Facet that controls whether the editor content DOM is editable.
|
|
6277
|
+
When its highest-precedence value is `false`, the element will
|
|
6278
|
+
not longer have its `contenteditable` attribute set. (Note that
|
|
6279
|
+
this doesn't affect API calls that change the editor content,
|
|
6280
|
+
even when those are bound to keys or buttons. See the
|
|
6281
|
+
[`readOnly`](https://codemirror.net/6/docs/ref/#state.EditorState.readOnly) facet for that.)
|
|
6073
6282
|
*/
|
|
6074
6283
|
EditorView.editable = editable;
|
|
6075
6284
|
/**
|
|
@@ -6164,11 +6373,7 @@ class CachedOrder {
|
|
|
6164
6373
|
}
|
|
6165
6374
|
}
|
|
6166
6375
|
|
|
6167
|
-
const currentPlatform =
|
|
6168
|
-
: /*@__PURE__*//Mac/.test(navigator.platform) ? "mac"
|
|
6169
|
-
: /*@__PURE__*//Win/.test(navigator.platform) ? "win"
|
|
6170
|
-
: /*@__PURE__*//Linux|X11/.test(navigator.platform) ? "linux"
|
|
6171
|
-
: "key";
|
|
6376
|
+
const currentPlatform = browser.mac ? "mac" : browser.windows ? "win" : browser.linux ? "linux" : "key";
|
|
6172
6377
|
function normalizeKeyName(name, platform) {
|
|
6173
6378
|
const parts = name.split(/-(?!$)/);
|
|
6174
6379
|
let result = parts[parts.length - 1];
|
|
@@ -6674,7 +6879,7 @@ class MatchDecorator {
|
|
|
6674
6879
|
}
|
|
6675
6880
|
|
|
6676
6881
|
const UnicodeRegexpSupport = /x/.unicode != null ? "gu" : "g";
|
|
6677
|
-
const Specials = /*@__PURE__*/new RegExp("[\u0000-\u0008\u000a-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\ufeff\ufff9-\ufffc]", UnicodeRegexpSupport);
|
|
6882
|
+
const Specials = /*@__PURE__*/new RegExp("[\u0000-\u0008\u000a-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\ufeff\ufff9-\ufffc]", UnicodeRegexpSupport);
|
|
6678
6883
|
const Names = {
|
|
6679
6884
|
0: "null",
|
|
6680
6885
|
7: "bell",
|
|
@@ -6689,6 +6894,8 @@ const Names = {
|
|
|
6689
6894
|
8206: "left-to-right mark",
|
|
6690
6895
|
8207: "right-to-left mark",
|
|
6691
6896
|
8232: "line separator",
|
|
6897
|
+
8237: "left-to-right override",
|
|
6898
|
+
8238: "right-to-left override",
|
|
6692
6899
|
8233: "paragraph separator",
|
|
6693
6900
|
65279: "zero width no-break space",
|
|
6694
6901
|
65532: "object replacement"
|
|
@@ -6855,7 +7062,7 @@ DOM class.
|
|
|
6855
7062
|
function highlightActiveLine() {
|
|
6856
7063
|
return activeLineHighlighter;
|
|
6857
7064
|
}
|
|
6858
|
-
const lineDeco = /*@__PURE__*/Decoration.line({
|
|
7065
|
+
const lineDeco = /*@__PURE__*/Decoration.line({ class: "cm-activeLine" });
|
|
6859
7066
|
const activeLineHighlighter = /*@__PURE__*/ViewPlugin.fromClass(class {
|
|
6860
7067
|
constructor(view) {
|
|
6861
7068
|
this.decorations = this.getDeco(view);
|