@codemirror/view 0.19.9 → 0.19.13
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 +48 -0
- package/dist/index.cjs +361 -154
- package/dist/index.d.ts +12 -4
- package/dist/index.js +361 -154
- package/package.json +2 -2
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;
|
|
@@ -248,6 +262,14 @@ function contentEditablePlainTextSupported() {
|
|
|
248
262
|
}
|
|
249
263
|
return _plainTextSupported;
|
|
250
264
|
}
|
|
265
|
+
function getRoot(node) {
|
|
266
|
+
while (node) {
|
|
267
|
+
node = node.assignedSlot || node.parentNode;
|
|
268
|
+
if (node && (node.nodeType == 9 || node.nodeType == 11 && node.host))
|
|
269
|
+
return node;
|
|
270
|
+
}
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
251
273
|
|
|
252
274
|
class DOMPos {
|
|
253
275
|
constructor(node, offset, precise = true) {
|
|
@@ -296,25 +318,30 @@ class ContentView {
|
|
|
296
318
|
sync(track) {
|
|
297
319
|
var _a;
|
|
298
320
|
if (this.dirty & 2 /* Node */) {
|
|
299
|
-
let parent = this.dom
|
|
321
|
+
let parent = this.dom;
|
|
322
|
+
let pos = parent.firstChild;
|
|
300
323
|
for (let child of this.children) {
|
|
301
324
|
if (child.dirty) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
child.reuseDOM(next);
|
|
325
|
+
if (!child.dom && pos && !((_a = ContentView.get(pos)) === null || _a === void 0 ? void 0 : _a.parent))
|
|
326
|
+
child.reuseDOM(pos);
|
|
305
327
|
child.sync(track);
|
|
306
328
|
child.dirty = 0 /* Not */;
|
|
307
329
|
}
|
|
308
|
-
if (track && track.node == parent && pos != child.dom)
|
|
330
|
+
if (track && !track.written && track.node == parent && pos != child.dom)
|
|
309
331
|
track.written = true;
|
|
310
|
-
|
|
311
|
-
|
|
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
|
+
}
|
|
312
340
|
}
|
|
313
|
-
|
|
314
|
-
if (next && track && track.node == parent)
|
|
341
|
+
if (pos && track && track.node == parent)
|
|
315
342
|
track.written = true;
|
|
316
|
-
while (
|
|
317
|
-
|
|
343
|
+
while (pos)
|
|
344
|
+
pos = rm(pos);
|
|
318
345
|
}
|
|
319
346
|
else if (this.dirty & 1 /* Child */) {
|
|
320
347
|
for (let child of this.children)
|
|
@@ -445,6 +472,7 @@ class ContentView {
|
|
|
445
472
|
(this.breakAfter ? "#" : "");
|
|
446
473
|
}
|
|
447
474
|
static get(node) { return node.cmView; }
|
|
475
|
+
get isEditable() { return true; }
|
|
448
476
|
}
|
|
449
477
|
ContentView.prototype.breakAfter = 0;
|
|
450
478
|
// Remove a DOM node and return its next sibling.
|
|
@@ -453,14 +481,6 @@ function rm(dom) {
|
|
|
453
481
|
dom.parentNode.removeChild(dom);
|
|
454
482
|
return next;
|
|
455
483
|
}
|
|
456
|
-
function syncNodeInto(parent, after, dom) {
|
|
457
|
-
let next = after ? after.nextSibling : parent.firstChild;
|
|
458
|
-
if (dom.parentNode == parent)
|
|
459
|
-
while (next != dom)
|
|
460
|
-
next = rm(next);
|
|
461
|
-
else
|
|
462
|
-
parent.insertBefore(dom, next);
|
|
463
|
-
}
|
|
464
484
|
class ChildCursor {
|
|
465
485
|
constructor(children, pos, i) {
|
|
466
486
|
this.children = children;
|
|
@@ -492,15 +512,18 @@ const gecko = !ie && /*@__PURE__*//gecko\/(\d+)/i.test(nav.userAgent);
|
|
|
492
512
|
const chrome = !ie && /*@__PURE__*//Chrome\/(\d+)/.exec(nav.userAgent);
|
|
493
513
|
const webkit = "webkitFontSmoothing" in doc.documentElement.style;
|
|
494
514
|
const safari = !ie && /*@__PURE__*//Apple Computer/.test(nav.vendor);
|
|
515
|
+
const ios = safari && (/*@__PURE__*//Mobile\/\w+/.test(nav.userAgent) || nav.maxTouchPoints > 2);
|
|
495
516
|
var browser = {
|
|
496
|
-
mac: /*@__PURE__*//Mac/.test(nav.platform),
|
|
517
|
+
mac: ios || /*@__PURE__*//Mac/.test(nav.platform),
|
|
518
|
+
windows: /*@__PURE__*//Win/.test(nav.platform),
|
|
519
|
+
linux: /*@__PURE__*//Linux|X11/.test(nav.platform),
|
|
497
520
|
ie,
|
|
498
521
|
ie_version: ie_upto10 ? doc.documentMode || 6 : ie_11up ? +ie_11up[1] : ie_edge ? +ie_edge[1] : 0,
|
|
499
522
|
gecko,
|
|
500
523
|
gecko_version: gecko ? +(/*@__PURE__*//Firefox\/(\d+)/.exec(nav.userAgent) || [0, 0])[1] : 0,
|
|
501
524
|
chrome: !!chrome,
|
|
502
525
|
chrome_version: chrome ? +chrome[1] : 0,
|
|
503
|
-
ios
|
|
526
|
+
ios,
|
|
504
527
|
android: /*@__PURE__*//Android\b/.test(nav.userAgent),
|
|
505
528
|
webkit,
|
|
506
529
|
safari,
|
|
@@ -722,6 +745,7 @@ class WidgetView extends InlineView {
|
|
|
722
745
|
}
|
|
723
746
|
return (pos == 0 && side > 0 || pos == this.length && side <= 0) ? rect : flattenRect(rect, pos == 0);
|
|
724
747
|
}
|
|
748
|
+
get isEditable() { return false; }
|
|
725
749
|
}
|
|
726
750
|
class CompositionView extends WidgetView {
|
|
727
751
|
domAtPos(pos) { return new DOMPos(this.widget.text, pos); }
|
|
@@ -733,6 +757,38 @@ class CompositionView extends WidgetView {
|
|
|
733
757
|
ignoreMutation() { return false; }
|
|
734
758
|
get overrideDOMText() { return null; }
|
|
735
759
|
coordsAt(pos, side) { return textCoords(this.widget.text, pos, side); }
|
|
760
|
+
get isEditable() { return true; }
|
|
761
|
+
}
|
|
762
|
+
// These are drawn around uneditable widgets to avoid a number of
|
|
763
|
+
// browser bugs that show up when the cursor is directly next to
|
|
764
|
+
// uneditable inline content.
|
|
765
|
+
class WidgetBufferView extends InlineView {
|
|
766
|
+
constructor(side) {
|
|
767
|
+
super();
|
|
768
|
+
this.side = side;
|
|
769
|
+
}
|
|
770
|
+
get length() { return 0; }
|
|
771
|
+
merge() { return false; }
|
|
772
|
+
become(other) {
|
|
773
|
+
return other instanceof WidgetBufferView && other.side == this.side;
|
|
774
|
+
}
|
|
775
|
+
slice() { return new WidgetBufferView(this.side); }
|
|
776
|
+
sync() {
|
|
777
|
+
if (!this.dom)
|
|
778
|
+
this.setDOM(document.createTextNode("\u200b"));
|
|
779
|
+
else if (this.dirty && this.dom.nodeValue != "\u200b")
|
|
780
|
+
this.dom.nodeValue = "\u200b";
|
|
781
|
+
}
|
|
782
|
+
getSide() { return this.side; }
|
|
783
|
+
domAtPos(pos) { return DOMPos.before(this.dom); }
|
|
784
|
+
domBoundsAround() { return null; }
|
|
785
|
+
coordsAt(pos) {
|
|
786
|
+
let rects = clientRectsFor(this.dom);
|
|
787
|
+
return rects[rects.length - 1];
|
|
788
|
+
}
|
|
789
|
+
get overrideDOMText() {
|
|
790
|
+
return Text.of([this.dom.nodeValue.replace(/\u200b/g, "")]);
|
|
791
|
+
}
|
|
736
792
|
}
|
|
737
793
|
function mergeInlineChildren(parent, from, to, elts, openStart, openEnd) {
|
|
738
794
|
let cur = parent.childCursor();
|
|
@@ -1227,14 +1283,17 @@ class LineView extends ContentView {
|
|
|
1227
1283
|
}
|
|
1228
1284
|
// Only called when building a line view in ContentBuilder
|
|
1229
1285
|
addLineDeco(deco) {
|
|
1230
|
-
let attrs = deco.spec.attributes;
|
|
1286
|
+
let attrs = deco.spec.attributes, cls = deco.spec.class;
|
|
1231
1287
|
if (attrs)
|
|
1232
1288
|
this.attrs = combineAttrs(attrs, this.attrs || {});
|
|
1289
|
+
if (cls)
|
|
1290
|
+
this.attrs = combineAttrs(attrs, { class: cls });
|
|
1233
1291
|
}
|
|
1234
1292
|
domAtPos(pos) {
|
|
1235
1293
|
return inlineDOMAtPos(this.dom, this.children, pos);
|
|
1236
1294
|
}
|
|
1237
1295
|
sync(track) {
|
|
1296
|
+
var _a;
|
|
1238
1297
|
if (!this.dom || (this.dirty & 4 /* Attrs */)) {
|
|
1239
1298
|
this.setDOM(document.createElement("div"));
|
|
1240
1299
|
this.dom.className = "cm-line";
|
|
@@ -1250,7 +1309,7 @@ class LineView extends ContentView {
|
|
|
1250
1309
|
while (last && ContentView.get(last) instanceof MarkView)
|
|
1251
1310
|
last = last.lastChild;
|
|
1252
1311
|
if (!last ||
|
|
1253
|
-
last.nodeName != "BR" && ContentView.get(last)
|
|
1312
|
+
last.nodeName != "BR" && ((_a = ContentView.get(last)) === null || _a === void 0 ? void 0 : _a.isEditable) == false &&
|
|
1254
1313
|
(!browser.ios || !this.children.some(ch => ch instanceof TextView))) {
|
|
1255
1314
|
let hack = document.createElement("BR");
|
|
1256
1315
|
hack.cmIgnore = true;
|
|
@@ -1349,6 +1408,9 @@ class ContentBuilder {
|
|
|
1349
1408
|
this.content = [];
|
|
1350
1409
|
this.curLine = null;
|
|
1351
1410
|
this.breakAtStart = 0;
|
|
1411
|
+
this.pendingBuffer = 0 /* No */;
|
|
1412
|
+
// Set to false directly after a widget that covers the position after it
|
|
1413
|
+
this.atCursorPos = true;
|
|
1352
1414
|
this.openStart = -1;
|
|
1353
1415
|
this.openEnd = -1;
|
|
1354
1416
|
this.text = "";
|
|
@@ -1363,23 +1425,31 @@ class ContentBuilder {
|
|
|
1363
1425
|
return !last.breakAfter && !(last instanceof BlockWidgetView && last.type == BlockType.WidgetBefore);
|
|
1364
1426
|
}
|
|
1365
1427
|
getLine() {
|
|
1366
|
-
if (!this.curLine)
|
|
1428
|
+
if (!this.curLine) {
|
|
1367
1429
|
this.content.push(this.curLine = new LineView);
|
|
1430
|
+
this.atCursorPos = true;
|
|
1431
|
+
}
|
|
1368
1432
|
return this.curLine;
|
|
1369
1433
|
}
|
|
1370
|
-
|
|
1434
|
+
flushBuffer(active) {
|
|
1435
|
+
if (this.pendingBuffer) {
|
|
1436
|
+
this.curLine.append(wrapMarks(new WidgetBufferView(-1), active), active.length);
|
|
1437
|
+
this.pendingBuffer = 0 /* No */;
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
addBlockWidget(view) {
|
|
1441
|
+
this.flushBuffer([]);
|
|
1371
1442
|
this.curLine = null;
|
|
1372
1443
|
this.content.push(view);
|
|
1373
1444
|
}
|
|
1374
|
-
finish() {
|
|
1445
|
+
finish(openEnd) {
|
|
1446
|
+
if (!openEnd)
|
|
1447
|
+
this.flushBuffer([]);
|
|
1448
|
+
else
|
|
1449
|
+
this.pendingBuffer = 0 /* No */;
|
|
1375
1450
|
if (!this.posCovered())
|
|
1376
1451
|
this.getLine();
|
|
1377
1452
|
}
|
|
1378
|
-
wrapMarks(view, active) {
|
|
1379
|
-
for (let mark of active)
|
|
1380
|
-
view = new MarkView(mark, [view], view.length);
|
|
1381
|
-
return view;
|
|
1382
|
-
}
|
|
1383
1453
|
buildText(length, active, openStart) {
|
|
1384
1454
|
while (length > 0) {
|
|
1385
1455
|
if (this.textOff == this.text.length) {
|
|
@@ -1394,6 +1464,7 @@ class ContentBuilder {
|
|
|
1394
1464
|
this.content[this.content.length - 1].breakAfter = 1;
|
|
1395
1465
|
else
|
|
1396
1466
|
this.breakAtStart = 1;
|
|
1467
|
+
this.flushBuffer([]);
|
|
1397
1468
|
this.curLine = null;
|
|
1398
1469
|
length--;
|
|
1399
1470
|
continue;
|
|
@@ -1404,7 +1475,9 @@ class ContentBuilder {
|
|
|
1404
1475
|
}
|
|
1405
1476
|
}
|
|
1406
1477
|
let take = Math.min(this.text.length - this.textOff, length, 512 /* Chunk */);
|
|
1407
|
-
this.
|
|
1478
|
+
this.flushBuffer(active);
|
|
1479
|
+
this.getLine().append(wrapMarks(new TextView(this.text.slice(this.textOff, this.textOff + take)), active), openStart);
|
|
1480
|
+
this.atCursorPos = true;
|
|
1408
1481
|
this.textOff += take;
|
|
1409
1482
|
length -= take;
|
|
1410
1483
|
openStart = 0;
|
|
@@ -1423,11 +1496,23 @@ class ContentBuilder {
|
|
|
1423
1496
|
let { type } = deco;
|
|
1424
1497
|
if (type == BlockType.WidgetAfter && !this.posCovered())
|
|
1425
1498
|
this.getLine();
|
|
1426
|
-
this.
|
|
1499
|
+
this.addBlockWidget(new BlockWidgetView(deco.widget || new NullWidget("div"), len, type));
|
|
1427
1500
|
}
|
|
1428
1501
|
else {
|
|
1429
|
-
let
|
|
1430
|
-
this.
|
|
1502
|
+
let view = WidgetView.create(deco.widget || new NullWidget("span"), len, deco.startSide);
|
|
1503
|
+
let cursorBefore = this.atCursorPos && !view.isEditable && openStart <= active.length && (from < to || deco.startSide > 0);
|
|
1504
|
+
let cursorAfter = !view.isEditable && (from < to || deco.startSide <= 0);
|
|
1505
|
+
let line = this.getLine();
|
|
1506
|
+
if (this.pendingBuffer == 2 /* IfCursor */ && !cursorBefore)
|
|
1507
|
+
this.pendingBuffer = 0 /* No */;
|
|
1508
|
+
this.flushBuffer(active);
|
|
1509
|
+
if (cursorBefore) {
|
|
1510
|
+
line.append(wrapMarks(new WidgetBufferView(1), active), openStart);
|
|
1511
|
+
openStart = active.length + Math.max(0, openStart - active.length);
|
|
1512
|
+
}
|
|
1513
|
+
line.append(wrapMarks(view, active), openStart);
|
|
1514
|
+
this.atCursorPos = cursorAfter;
|
|
1515
|
+
this.pendingBuffer = !cursorAfter ? 0 /* No */ : from < to ? 1 /* Yes */ : 2 /* IfCursor */;
|
|
1431
1516
|
}
|
|
1432
1517
|
}
|
|
1433
1518
|
else if (this.doc.lineAt(this.pos).from == this.pos) { // Line decoration
|
|
@@ -1453,10 +1538,15 @@ class ContentBuilder {
|
|
|
1453
1538
|
builder.openEnd = RangeSet.spans(decorations, from, to, builder);
|
|
1454
1539
|
if (builder.openStart < 0)
|
|
1455
1540
|
builder.openStart = builder.openEnd;
|
|
1456
|
-
builder.finish();
|
|
1541
|
+
builder.finish(builder.openEnd);
|
|
1457
1542
|
return builder;
|
|
1458
1543
|
}
|
|
1459
1544
|
}
|
|
1545
|
+
function wrapMarks(view, active) {
|
|
1546
|
+
for (let mark of active)
|
|
1547
|
+
view = new MarkView(mark, [view], view.length);
|
|
1548
|
+
return view;
|
|
1549
|
+
}
|
|
1460
1550
|
class NullWidget extends WidgetType {
|
|
1461
1551
|
constructor(tag) {
|
|
1462
1552
|
super();
|
|
@@ -1477,6 +1567,9 @@ const inputHandler = /*@__PURE__*/Facet.define();
|
|
|
1477
1567
|
const scrollTo = /*@__PURE__*/StateEffect.define({
|
|
1478
1568
|
map: (range, changes) => range.map(changes)
|
|
1479
1569
|
});
|
|
1570
|
+
const centerOn = /*@__PURE__*/StateEffect.define({
|
|
1571
|
+
map: (range, changes) => range.map(changes)
|
|
1572
|
+
});
|
|
1480
1573
|
/**
|
|
1481
1574
|
Log or report an unhandled exception in client code. Should
|
|
1482
1575
|
probably only be used by extension code that allows client code to
|
|
@@ -1903,6 +1996,14 @@ class DocView extends ContentView {
|
|
|
1903
1996
|
return true;
|
|
1904
1997
|
}
|
|
1905
1998
|
}
|
|
1999
|
+
reset(sel) {
|
|
2000
|
+
if (this.dirty) {
|
|
2001
|
+
this.view.observer.ignore(() => this.view.docView.sync());
|
|
2002
|
+
this.dirty = 0 /* Not */;
|
|
2003
|
+
}
|
|
2004
|
+
if (sel)
|
|
2005
|
+
this.updateSelection();
|
|
2006
|
+
}
|
|
1906
2007
|
// Used both by update and checkLayout do perform the actual DOM
|
|
1907
2008
|
// update
|
|
1908
2009
|
updateInner(changes, deco, oldLength, forceSelection = false, pointerSel = false) {
|
|
@@ -2032,6 +2133,14 @@ class DocView extends ContentView {
|
|
|
2032
2133
|
!isEquivalentPosition(anchor.node, anchor.offset, domSel.anchorNode, domSel.anchorOffset) ||
|
|
2033
2134
|
!isEquivalentPosition(head.node, head.offset, domSel.focusNode, domSel.focusOffset)) {
|
|
2034
2135
|
this.view.observer.ignore(() => {
|
|
2136
|
+
// Chrome Android will hide the virtual keyboard when tapping
|
|
2137
|
+
// inside an uneditable node, and not bring it back when we
|
|
2138
|
+
// move the cursor to its proper position. This tries to
|
|
2139
|
+
// restore the keyboard by cycling focus.
|
|
2140
|
+
if (browser.android && browser.chrome && this.dom.contains(domSel.focusNode) && inUneditable(domSel.focusNode, this.dom)) {
|
|
2141
|
+
this.dom.blur();
|
|
2142
|
+
this.dom.focus({ preventScroll: true });
|
|
2143
|
+
}
|
|
2035
2144
|
let rawSel = getSelection(this.root);
|
|
2036
2145
|
if (main.empty) {
|
|
2037
2146
|
// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=1612076
|
|
@@ -2205,7 +2314,7 @@ class DocView extends ContentView {
|
|
|
2205
2314
|
this.view.viewState.lineGapDeco
|
|
2206
2315
|
];
|
|
2207
2316
|
}
|
|
2208
|
-
|
|
2317
|
+
scrollIntoView({ range, center }) {
|
|
2209
2318
|
let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
|
|
2210
2319
|
if (!rect)
|
|
2211
2320
|
return;
|
|
@@ -2225,10 +2334,10 @@ class DocView extends ContentView {
|
|
|
2225
2334
|
if (bottom != null)
|
|
2226
2335
|
mBottom = Math.max(mBottom, bottom);
|
|
2227
2336
|
}
|
|
2228
|
-
scrollRectIntoView(this.
|
|
2337
|
+
scrollRectIntoView(this.view.scrollDOM, {
|
|
2229
2338
|
left: rect.left - mLeft, top: rect.top - mTop,
|
|
2230
2339
|
right: rect.right + mRight, bottom: rect.bottom + mBottom
|
|
2231
|
-
}, range.head < range.anchor ? -1 : 1);
|
|
2340
|
+
}, range.head < range.anchor ? -1 : 1, center);
|
|
2232
2341
|
}
|
|
2233
2342
|
}
|
|
2234
2343
|
function betweenUneditable(pos) {
|
|
@@ -2339,6 +2448,14 @@ function findChangedDeco(a, b, diff) {
|
|
|
2339
2448
|
RangeSet.compare(a, b, diff, comp);
|
|
2340
2449
|
return comp.changes;
|
|
2341
2450
|
}
|
|
2451
|
+
function inUneditable(node, inside) {
|
|
2452
|
+
for (let cur = node; cur && cur != inside; cur = cur.assignedSlot || cur.parentNode) {
|
|
2453
|
+
if (cur.nodeType == 1 && cur.contentEditable == 'false') {
|
|
2454
|
+
return true;
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
return false;
|
|
2458
|
+
}
|
|
2342
2459
|
|
|
2343
2460
|
/**
|
|
2344
2461
|
Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
|
|
@@ -2784,6 +2901,7 @@ function domPosInText(node, x, y) {
|
|
|
2784
2901
|
return { node, offset: closestOffset > -1 ? closestOffset : generalSide > 0 ? node.nodeValue.length : 0 };
|
|
2785
2902
|
}
|
|
2786
2903
|
function posAtCoords(view, { x, y }, precise, bias = -1) {
|
|
2904
|
+
var _a;
|
|
2787
2905
|
let content = view.contentDOM.getBoundingClientRect(), block;
|
|
2788
2906
|
let halfLine = view.defaultLineHeight / 2;
|
|
2789
2907
|
for (let bounced = false;;) {
|
|
@@ -2814,7 +2932,7 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
|
|
|
2814
2932
|
// There's visible editor content under the point, so we can try
|
|
2815
2933
|
// using caret(Position|Range)FromPoint as a shortcut
|
|
2816
2934
|
let node, offset = -1;
|
|
2817
|
-
if (element && view.contentDOM.contains(element) &&
|
|
2935
|
+
if (element && view.contentDOM.contains(element) && ((_a = view.docView.nearest(element)) === null || _a === void 0 ? void 0 : _a.isEditable) != false) {
|
|
2818
2936
|
if (doc.caretPositionFromPoint) {
|
|
2819
2937
|
let pos = doc.caretPositionFromPoint(x, y);
|
|
2820
2938
|
if (pos)
|
|
@@ -2955,7 +3073,23 @@ class InputState {
|
|
|
2955
3073
|
constructor(view) {
|
|
2956
3074
|
this.lastKeyCode = 0;
|
|
2957
3075
|
this.lastKeyTime = 0;
|
|
2958
|
-
|
|
3076
|
+
// On iOS, some keys need to have their default behavior happen
|
|
3077
|
+
// (after which we retroactively handle them and reset the DOM) to
|
|
3078
|
+
// avoid messing up the virtual keyboard state.
|
|
3079
|
+
//
|
|
3080
|
+
// On Chrome Android, backspace near widgets is just completely
|
|
3081
|
+
// broken, and there are no key events, so we need to handle the
|
|
3082
|
+
// beforeinput event. Deleting stuff will often create a flurry of
|
|
3083
|
+
// events, and interrupting it before it is done just makes
|
|
3084
|
+
// subsequent events even more broken, so again, we hold off doing
|
|
3085
|
+
// anything until the browser is finished with whatever it is trying
|
|
3086
|
+
// to do.
|
|
3087
|
+
//
|
|
3088
|
+
// setPendingKey sets this, causing the DOM observer to pause for a
|
|
3089
|
+
// bit, and setting an animation frame (which seems the most
|
|
3090
|
+
// reliable way to detect 'browser is done flailing') to fire a fake
|
|
3091
|
+
// key event and re-sync the view again.
|
|
3092
|
+
this.pendingKey = undefined;
|
|
2959
3093
|
this.lastSelectionOrigin = null;
|
|
2960
3094
|
this.lastSelectionTime = 0;
|
|
2961
3095
|
this.lastEscPress = 0;
|
|
@@ -3064,20 +3198,31 @@ class InputState {
|
|
|
3064
3198
|
// state. So we let it go through, and then, in
|
|
3065
3199
|
// applyDOMChange, notify key handlers of it and reset to
|
|
3066
3200
|
// the state they produce.
|
|
3067
|
-
|
|
3201
|
+
let pending;
|
|
3202
|
+
if (browser.ios && (pending = PendingKeys.find(key => key.keyCode == event.keyCode)) &&
|
|
3068
3203
|
!(event.ctrlKey || event.altKey || event.metaKey) && !event.synthetic) {
|
|
3069
|
-
this.
|
|
3070
|
-
setTimeout(() => this.flushIOSKey(view), 250);
|
|
3204
|
+
this.setPendingKey(view, pending);
|
|
3071
3205
|
return true;
|
|
3072
3206
|
}
|
|
3073
3207
|
return false;
|
|
3074
3208
|
}
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3209
|
+
setPendingKey(view, pending) {
|
|
3210
|
+
this.pendingKey = pending;
|
|
3211
|
+
let flush = () => {
|
|
3212
|
+
if (!this.pendingKey)
|
|
3213
|
+
return false;
|
|
3214
|
+
let key = this.pendingKey;
|
|
3215
|
+
this.pendingKey = undefined;
|
|
3216
|
+
view.observer.processRecords();
|
|
3217
|
+
let startState = view.state;
|
|
3218
|
+
dispatchKey(view.contentDOM, key.key, key.keyCode);
|
|
3219
|
+
if (view.state == startState)
|
|
3220
|
+
view.docView.reset(true);
|
|
3221
|
+
};
|
|
3222
|
+
if (browser.ios)
|
|
3223
|
+
setTimeout(() => requestAnimationFrame(flush), 50);
|
|
3224
|
+
else
|
|
3225
|
+
requestAnimationFrame(flush);
|
|
3081
3226
|
}
|
|
3082
3227
|
ignoreDuringComposition(event) {
|
|
3083
3228
|
if (!/^key/.test(event.type))
|
|
@@ -3124,6 +3269,11 @@ class InputState {
|
|
|
3124
3269
|
this.mouseSelection.destroy();
|
|
3125
3270
|
}
|
|
3126
3271
|
}
|
|
3272
|
+
const PendingKeys = [
|
|
3273
|
+
{ key: "Backspace", keyCode: 8, inputType: "deleteContentBackward" },
|
|
3274
|
+
{ key: "Enter", keyCode: 13, inputType: "insertParagraph" },
|
|
3275
|
+
{ key: "Delete", keyCode: 46, inputType: "deleteContentForward" }
|
|
3276
|
+
];
|
|
3127
3277
|
// Key codes for modifier keys
|
|
3128
3278
|
const modifierCodes = [16, 17, 18, 20, 91, 92, 224, 225];
|
|
3129
3279
|
class MouseSelection {
|
|
@@ -3569,6 +3719,33 @@ handlers.compositionend = view => {
|
|
|
3569
3719
|
handlers.contextmenu = view => {
|
|
3570
3720
|
view.inputState.lastContextMenu = Date.now();
|
|
3571
3721
|
};
|
|
3722
|
+
handlers.beforeinput = (view, event) => {
|
|
3723
|
+
var _a;
|
|
3724
|
+
// Because Chrome Android doesn't fire useful key events, use
|
|
3725
|
+
// beforeinput to detect backspace (and possibly enter and delete,
|
|
3726
|
+
// but those usually don't even seem to fire beforeinput events at
|
|
3727
|
+
// the moment) and fake a key event for it.
|
|
3728
|
+
//
|
|
3729
|
+
// (preventDefault on beforeinput, though supported in the spec,
|
|
3730
|
+
// seems to do nothing at all on Chrome).
|
|
3731
|
+
let pending;
|
|
3732
|
+
if (browser.chrome && browser.android && (pending = PendingKeys.find(key => key.inputType == event.inputType))) {
|
|
3733
|
+
view.inputState.setPendingKey(view, pending);
|
|
3734
|
+
if (pending.key == "Backspace" || pending.key == "Delete") {
|
|
3735
|
+
let startViewHeight = ((_a = window.visualViewport) === null || _a === void 0 ? void 0 : _a.height) || 0;
|
|
3736
|
+
setTimeout(() => {
|
|
3737
|
+
var _a;
|
|
3738
|
+
// Backspacing near uneditable nodes on Chrome Android sometimes
|
|
3739
|
+
// closes the virtual keyboard. This tries to crudely detect
|
|
3740
|
+
// that and refocus to get it back.
|
|
3741
|
+
if ((((_a = window.visualViewport) === null || _a === void 0 ? void 0 : _a.height) || 0) > startViewHeight + 10 && view.hasFocus) {
|
|
3742
|
+
view.contentDOM.blur();
|
|
3743
|
+
view.focus();
|
|
3744
|
+
}
|
|
3745
|
+
}, 100);
|
|
3746
|
+
}
|
|
3747
|
+
}
|
|
3748
|
+
};
|
|
3572
3749
|
|
|
3573
3750
|
const wrappingWhiteSpace = ["pre-wrap", "normal", "pre-line"];
|
|
3574
3751
|
class HeightOracle {
|
|
@@ -4283,6 +4460,15 @@ class LineGapWidget extends WidgetType {
|
|
|
4283
4460
|
}
|
|
4284
4461
|
get estimatedHeight() { return this.vertical ? this.size : -1; }
|
|
4285
4462
|
}
|
|
4463
|
+
class ScrollTarget {
|
|
4464
|
+
constructor(range, center = false) {
|
|
4465
|
+
this.range = range;
|
|
4466
|
+
this.center = center;
|
|
4467
|
+
}
|
|
4468
|
+
map(changes) {
|
|
4469
|
+
return changes.empty ? this : new ScrollTarget(this.range.map(changes), this.center);
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4286
4472
|
class ViewState {
|
|
4287
4473
|
constructor(state) {
|
|
4288
4474
|
this.state = state;
|
|
@@ -4295,7 +4481,7 @@ class ViewState {
|
|
|
4295
4481
|
this.heightOracle = new HeightOracle;
|
|
4296
4482
|
// See VP.MaxDOMHeight
|
|
4297
4483
|
this.scaler = IdScaler;
|
|
4298
|
-
this.
|
|
4484
|
+
this.scrollTarget = null;
|
|
4299
4485
|
// Briefly set to true when printing, to disable viewport limiting
|
|
4300
4486
|
this.printing = false;
|
|
4301
4487
|
this.visibleRanges = [];
|
|
@@ -4328,7 +4514,7 @@ class ViewState {
|
|
|
4328
4514
|
this.scaler = this.heightMap.height <= 7000000 /* MaxDOMHeight */ ? IdScaler :
|
|
4329
4515
|
new BigScaler(this.heightOracle.doc, this.heightMap, this.viewports);
|
|
4330
4516
|
}
|
|
4331
|
-
update(update,
|
|
4517
|
+
update(update, scrollTarget = null) {
|
|
4332
4518
|
let prev = this.state;
|
|
4333
4519
|
this.state = update.state;
|
|
4334
4520
|
let newDeco = this.state.facet(decorations);
|
|
@@ -4339,15 +4525,16 @@ class ViewState {
|
|
|
4339
4525
|
if (this.heightMap.height != prevHeight)
|
|
4340
4526
|
update.flags |= 2 /* Height */;
|
|
4341
4527
|
let viewport = heightChanges.length ? this.mapViewport(this.viewport, update.changes) : this.viewport;
|
|
4342
|
-
if (
|
|
4343
|
-
|
|
4528
|
+
if (scrollTarget && (scrollTarget.range.head < viewport.from || scrollTarget.range.head > viewport.to) ||
|
|
4529
|
+
!this.viewportIsAppropriate(viewport))
|
|
4530
|
+
viewport = this.getViewport(0, scrollTarget);
|
|
4344
4531
|
this.viewport = viewport;
|
|
4345
4532
|
this.updateForViewport();
|
|
4346
|
-
if (this.lineGaps.length || this.viewport.to - this.viewport.from >
|
|
4533
|
+
if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
|
|
4347
4534
|
this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps, update.changes)));
|
|
4348
4535
|
update.flags |= this.computeVisibleRanges();
|
|
4349
|
-
if (
|
|
4350
|
-
this.
|
|
4536
|
+
if (scrollTarget)
|
|
4537
|
+
this.scrollTarget = scrollTarget;
|
|
4351
4538
|
if (!this.mustEnforceCursorAssoc && update.selectionSet && update.view.lineWrapping &&
|
|
4352
4539
|
update.state.selection.main.empty && update.state.selection.main.assoc)
|
|
4353
4540
|
this.mustEnforceCursorAssoc = true;
|
|
@@ -4400,10 +4587,10 @@ class ViewState {
|
|
|
4400
4587
|
if (oracle.heightChanged)
|
|
4401
4588
|
result |= 2 /* Height */;
|
|
4402
4589
|
if (!this.viewportIsAppropriate(this.viewport, bias) ||
|
|
4403
|
-
this.
|
|
4404
|
-
this.viewport = this.getViewport(bias, this.
|
|
4590
|
+
this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to))
|
|
4591
|
+
this.viewport = this.getViewport(bias, this.scrollTarget);
|
|
4405
4592
|
this.updateForViewport();
|
|
4406
|
-
if (this.lineGaps.length || this.viewport.to - this.viewport.from >
|
|
4593
|
+
if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
|
|
4407
4594
|
this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps));
|
|
4408
4595
|
result |= this.computeVisibleRanges();
|
|
4409
4596
|
if (this.mustEnforceCursorAssoc) {
|
|
@@ -4418,22 +4605,25 @@ class ViewState {
|
|
|
4418
4605
|
}
|
|
4419
4606
|
get visibleTop() { return this.scaler.fromDOM(this.pixelViewport.top, 0); }
|
|
4420
4607
|
get visibleBottom() { return this.scaler.fromDOM(this.pixelViewport.bottom, 0); }
|
|
4421
|
-
getViewport(bias,
|
|
4608
|
+
getViewport(bias, scrollTarget) {
|
|
4422
4609
|
// This will divide VP.Margin between the top and the
|
|
4423
4610
|
// bottom, depending on the bias (the change in viewport position
|
|
4424
4611
|
// since the last update). It'll hold a number between 0 and 1
|
|
4425
4612
|
let marginTop = 0.5 - Math.max(-0.5, Math.min(0.5, bias / 1000 /* Margin */ / 2));
|
|
4426
4613
|
let map = this.heightMap, doc = this.state.doc, { visibleTop, visibleBottom } = this;
|
|
4427
4614
|
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);
|
|
4428
|
-
// If
|
|
4429
|
-
if (
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4615
|
+
// If scrollTarget is given, make sure the viewport includes that position
|
|
4616
|
+
if (scrollTarget) {
|
|
4617
|
+
let { head } = scrollTarget.range, viewHeight = visibleBottom - visibleTop;
|
|
4618
|
+
if (head < viewport.from || head > viewport.to) {
|
|
4619
|
+
let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
|
|
4620
|
+
if (scrollTarget.center)
|
|
4621
|
+
topPos = (block.top + block.bottom) / 2 - viewHeight / 2;
|
|
4622
|
+
else if (head < viewport.from)
|
|
4623
|
+
topPos = block.top;
|
|
4624
|
+
else
|
|
4625
|
+
topPos = block.bottom - viewHeight;
|
|
4626
|
+
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);
|
|
4437
4627
|
}
|
|
4438
4628
|
}
|
|
4439
4629
|
return viewport;
|
|
@@ -4475,52 +4665,50 @@ class ViewState {
|
|
|
4475
4665
|
if (this.heightOracle.direction != Direction.LTR)
|
|
4476
4666
|
return gaps;
|
|
4477
4667
|
this.heightMap.forEachLine(this.viewport.from, this.viewport.to, this.state.doc, 0, 0, line => {
|
|
4478
|
-
if (line.length <
|
|
4668
|
+
if (line.length < 4000 /* DoubleMargin */)
|
|
4479
4669
|
return;
|
|
4480
4670
|
let structure = lineStructure(line.from, line.to, this.state);
|
|
4481
|
-
if (structure.total <
|
|
4671
|
+
if (structure.total < 4000 /* DoubleMargin */)
|
|
4482
4672
|
return;
|
|
4483
4673
|
let viewFrom, viewTo;
|
|
4484
4674
|
if (this.heightOracle.lineWrapping) {
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
viewFrom = findPosition(structure, (this.visibleTop - line.top) / line.height);
|
|
4489
|
-
if (line.to != this.viewport.to)
|
|
4490
|
-
viewTo = line.to;
|
|
4491
|
-
else
|
|
4492
|
-
viewTo = findPosition(structure, (this.visibleBottom - line.top) / line.height);
|
|
4675
|
+
let marginHeight = (2000 /* Margin */ / this.heightOracle.lineLength) * this.heightOracle.lineHeight;
|
|
4676
|
+
viewFrom = findPosition(structure, (this.visibleTop - line.top - marginHeight) / line.height);
|
|
4677
|
+
viewTo = findPosition(structure, (this.visibleBottom - line.top + marginHeight) / line.height);
|
|
4493
4678
|
}
|
|
4494
4679
|
else {
|
|
4495
4680
|
let totalWidth = structure.total * this.heightOracle.charWidth;
|
|
4496
|
-
|
|
4497
|
-
|
|
4681
|
+
let marginWidth = 2000 /* Margin */ * this.heightOracle.charWidth;
|
|
4682
|
+
viewFrom = findPosition(structure, (this.pixelViewport.left - marginWidth) / totalWidth);
|
|
4683
|
+
viewTo = findPosition(structure, (this.pixelViewport.right + marginWidth) / totalWidth);
|
|
4498
4684
|
}
|
|
4685
|
+
let outside = [];
|
|
4686
|
+
if (viewFrom > line.from)
|
|
4687
|
+
outside.push({ from: line.from, to: viewFrom });
|
|
4688
|
+
if (viewTo < line.to)
|
|
4689
|
+
outside.push({ from: viewTo, to: line.to });
|
|
4499
4690
|
let sel = this.state.selection.main;
|
|
4500
|
-
// Make sure the
|
|
4501
|
-
if (sel.from
|
|
4502
|
-
|
|
4503
|
-
if (sel.
|
|
4504
|
-
|
|
4505
|
-
let
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
gap.from < gapFrom + 5000 /* HalfMargin */) ||
|
|
4512
|
-
new LineGap(gapFrom, line.to, this.gapSize(line, gapFrom, false, structure)));
|
|
4691
|
+
// Make sure the gaps don't cover a selection end
|
|
4692
|
+
if (sel.from >= line.from && sel.from <= line.to)
|
|
4693
|
+
cutRange(outside, sel.from - 10 /* SelectionMargin */, sel.from + 10 /* SelectionMargin */);
|
|
4694
|
+
if (!sel.empty && sel.to >= line.from && sel.to <= line.to)
|
|
4695
|
+
cutRange(outside, sel.to - 10 /* SelectionMargin */, sel.to + 10 /* SelectionMargin */);
|
|
4696
|
+
for (let { from, to } of outside)
|
|
4697
|
+
if (to - from > 1000 /* HalfMargin */) {
|
|
4698
|
+
gaps.push(find(current, gap => gap.from >= line.from && gap.to <= line.to &&
|
|
4699
|
+
Math.abs(gap.from - from) < 1000 /* HalfMargin */ && Math.abs(gap.to - to) < 1000 /* HalfMargin */) ||
|
|
4700
|
+
new LineGap(from, to, this.gapSize(line, from, to, structure)));
|
|
4701
|
+
}
|
|
4513
4702
|
});
|
|
4514
4703
|
return gaps;
|
|
4515
4704
|
}
|
|
4516
|
-
gapSize(line,
|
|
4705
|
+
gapSize(line, from, to, structure) {
|
|
4706
|
+
let fraction = findFraction(structure, to) - findFraction(structure, from);
|
|
4517
4707
|
if (this.heightOracle.lineWrapping) {
|
|
4518
|
-
|
|
4519
|
-
return start ? height : line.height - height;
|
|
4708
|
+
return line.height * fraction;
|
|
4520
4709
|
}
|
|
4521
4710
|
else {
|
|
4522
|
-
|
|
4523
|
-
return structure.total * this.heightOracle.charWidth * (start ? ratio : 1 - ratio);
|
|
4711
|
+
return structure.total * this.heightOracle.charWidth * fraction;
|
|
4524
4712
|
}
|
|
4525
4713
|
}
|
|
4526
4714
|
updateLineGaps(gaps) {
|
|
@@ -4614,6 +4802,20 @@ function findFraction(structure, pos) {
|
|
|
4614
4802
|
}
|
|
4615
4803
|
return counted / structure.total;
|
|
4616
4804
|
}
|
|
4805
|
+
function cutRange(ranges, from, to) {
|
|
4806
|
+
for (let i = 0; i < ranges.length; i++) {
|
|
4807
|
+
let r = ranges[i];
|
|
4808
|
+
if (r.from < to && r.to > from) {
|
|
4809
|
+
let pieces = [];
|
|
4810
|
+
if (r.from < from)
|
|
4811
|
+
pieces.push({ from: r.from, to: from });
|
|
4812
|
+
if (r.to > to)
|
|
4813
|
+
pieces.push({ from: to, to: r.to });
|
|
4814
|
+
ranges.splice(i, 1, ...pieces);
|
|
4815
|
+
i += pieces.length - 1;
|
|
4816
|
+
}
|
|
4817
|
+
}
|
|
4818
|
+
}
|
|
4617
4819
|
function find(array, f) {
|
|
4618
4820
|
for (let val of array)
|
|
4619
4821
|
if (f(val))
|
|
@@ -4696,7 +4898,7 @@ function buildTheme(main, spec, scopes) {
|
|
|
4696
4898
|
});
|
|
4697
4899
|
}
|
|
4698
4900
|
const baseTheme = /*@__PURE__*/buildTheme("." + baseThemeID, {
|
|
4699
|
-
"
|
|
4901
|
+
"&.cm-editor": {
|
|
4700
4902
|
position: "relative !important",
|
|
4701
4903
|
boxSizing: "border-box",
|
|
4702
4904
|
"&.cm-focused": {
|
|
@@ -4905,7 +5107,7 @@ class DOMObserver {
|
|
|
4905
5107
|
this.intersection = new IntersectionObserver(entries => {
|
|
4906
5108
|
if (this.parentCheck < 0)
|
|
4907
5109
|
this.parentCheck = setTimeout(this.listenForScroll.bind(this), 1000);
|
|
4908
|
-
if (entries[entries.length - 1].intersectionRatio > 0 != this.intersecting) {
|
|
5110
|
+
if (entries.length > 0 && entries[entries.length - 1].intersectionRatio > 0 != this.intersecting) {
|
|
4909
5111
|
this.intersecting = !this.intersecting;
|
|
4910
5112
|
if (this.intersecting != this.view.inView)
|
|
4911
5113
|
this.onScrollChanged(document.createEvent("Event"));
|
|
@@ -4913,7 +5115,7 @@ class DOMObserver {
|
|
|
4913
5115
|
}, {});
|
|
4914
5116
|
this.intersection.observe(this.dom);
|
|
4915
5117
|
this.gapIntersection = new IntersectionObserver(entries => {
|
|
4916
|
-
if (entries[entries.length - 1].intersectionRatio > 0)
|
|
5118
|
+
if (entries.length > 0 && entries[entries.length - 1].intersectionRatio > 0)
|
|
4917
5119
|
this.onScrollChanged(document.createEvent("Event"));
|
|
4918
5120
|
}, {});
|
|
4919
5121
|
}
|
|
@@ -5047,20 +5249,12 @@ class DOMObserver {
|
|
|
5047
5249
|
this.flush();
|
|
5048
5250
|
}
|
|
5049
5251
|
}
|
|
5050
|
-
|
|
5051
|
-
flush() {
|
|
5052
|
-
if (this.delayedFlush >= 0)
|
|
5053
|
-
return;
|
|
5054
|
-
this.lastFlush = Date.now();
|
|
5252
|
+
processRecords() {
|
|
5055
5253
|
let records = this.queue;
|
|
5056
5254
|
for (let mut of this.observer.takeRecords())
|
|
5057
5255
|
records.push(mut);
|
|
5058
5256
|
if (records.length)
|
|
5059
5257
|
this.queue = [];
|
|
5060
|
-
let selection = this.selectionRange;
|
|
5061
|
-
let newSel = !this.ignoreSelection.eq(selection) && hasSelection(this.dom, selection);
|
|
5062
|
-
if (records.length == 0 && !newSel)
|
|
5063
|
-
return;
|
|
5064
5258
|
let from = -1, to = -1, typeOver = false;
|
|
5065
5259
|
for (let record of records) {
|
|
5066
5260
|
let range = this.readMutation(record);
|
|
@@ -5076,17 +5270,26 @@ class DOMObserver {
|
|
|
5076
5270
|
to = Math.max(range.to, to);
|
|
5077
5271
|
}
|
|
5078
5272
|
}
|
|
5273
|
+
return { from, to, typeOver };
|
|
5274
|
+
}
|
|
5275
|
+
// Apply pending changes, if any
|
|
5276
|
+
flush() {
|
|
5277
|
+
// Completely hold off flushing when pending keys are set—the code
|
|
5278
|
+
// managing those will make sure processRecords is called and the
|
|
5279
|
+
// view is resynchronized after
|
|
5280
|
+
if (this.delayedFlush >= 0 || this.view.inputState.pendingKey)
|
|
5281
|
+
return;
|
|
5282
|
+
this.lastFlush = Date.now();
|
|
5283
|
+
let { from, to, typeOver } = this.processRecords();
|
|
5284
|
+
let selection = this.selectionRange;
|
|
5285
|
+
let newSel = !this.ignoreSelection.eq(selection) && hasSelection(this.dom, selection);
|
|
5286
|
+
if (from < 0 && !newSel)
|
|
5287
|
+
return;
|
|
5079
5288
|
let startState = this.view.state;
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
if (this.view.state == startState)
|
|
5083
|
-
|
|
5084
|
-
this.ignore(() => this.view.docView.sync());
|
|
5085
|
-
this.view.docView.dirty = 0 /* Not */;
|
|
5086
|
-
}
|
|
5087
|
-
if (newSel)
|
|
5088
|
-
this.view.docView.updateSelection();
|
|
5089
|
-
}
|
|
5289
|
+
this.onChange(from, to, typeOver);
|
|
5290
|
+
// The view wasn't updated
|
|
5291
|
+
if (this.view.state == startState)
|
|
5292
|
+
this.view.docView.reset(newSel);
|
|
5090
5293
|
this.clearSelection();
|
|
5091
5294
|
}
|
|
5092
5295
|
readMutation(rec) {
|
|
@@ -5223,9 +5426,9 @@ function applyDOMChange(view, start, end, typeOver) {
|
|
|
5223
5426
|
(change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 &&
|
|
5224
5427
|
dispatchKey(view.contentDOM, "Backspace", 8)) ||
|
|
5225
5428
|
(change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
|
|
5226
|
-
dispatchKey(view.contentDOM, "Delete", 46)))
|
|
5227
|
-
browser.ios && view.inputState.flushIOSKey(view))
|
|
5429
|
+
dispatchKey(view.contentDOM, "Delete", 46)))) {
|
|
5228
5430
|
return;
|
|
5431
|
+
}
|
|
5229
5432
|
let text = change.insert.toString();
|
|
5230
5433
|
if (view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text)))
|
|
5231
5434
|
return;
|
|
@@ -5446,7 +5649,7 @@ class EditorView {
|
|
|
5446
5649
|
this.dom.appendChild(this.scrollDOM);
|
|
5447
5650
|
this._dispatch = config.dispatch || ((tr) => this.update([tr]));
|
|
5448
5651
|
this.dispatch = this.dispatch.bind(this);
|
|
5449
|
-
this.root = (config.root || document);
|
|
5652
|
+
this.root = (config.root || getRoot(config.parent) || document);
|
|
5450
5653
|
this.viewState = new ViewState(config.state || EditorState.create());
|
|
5451
5654
|
this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec).update(this));
|
|
5452
5655
|
this.observer = new DOMObserver(this, (from, to, typeOver) => {
|
|
@@ -5527,21 +5730,24 @@ class EditorView {
|
|
|
5527
5730
|
if (state.facet(EditorState.phrases) != this.state.facet(EditorState.phrases))
|
|
5528
5731
|
return this.setState(state);
|
|
5529
5732
|
update = new ViewUpdate(this, state, transactions);
|
|
5530
|
-
let
|
|
5733
|
+
let scrollTarget = null;
|
|
5531
5734
|
try {
|
|
5532
5735
|
this.updateState = 2 /* Updating */;
|
|
5533
5736
|
for (let tr of transactions) {
|
|
5534
|
-
if (
|
|
5535
|
-
|
|
5737
|
+
if (scrollTarget)
|
|
5738
|
+
scrollTarget = scrollTarget.map(tr.changes);
|
|
5536
5739
|
if (tr.scrollIntoView) {
|
|
5537
5740
|
let { main } = tr.state.selection;
|
|
5538
|
-
|
|
5741
|
+
scrollTarget = new ScrollTarget(main.empty ? main : EditorSelection.cursor(main.head, main.head > main.anchor ? -1 : 1));
|
|
5539
5742
|
}
|
|
5540
|
-
for (let e of tr.effects)
|
|
5743
|
+
for (let e of tr.effects) {
|
|
5541
5744
|
if (e.is(scrollTo))
|
|
5542
|
-
|
|
5745
|
+
scrollTarget = new ScrollTarget(e.value);
|
|
5746
|
+
else if (e.is(centerOn))
|
|
5747
|
+
scrollTarget = new ScrollTarget(e.value, true);
|
|
5748
|
+
}
|
|
5543
5749
|
}
|
|
5544
|
-
this.viewState.update(update,
|
|
5750
|
+
this.viewState.update(update, scrollTarget);
|
|
5545
5751
|
this.bidiCache = CachedOrder.update(this.bidiCache, update.changes);
|
|
5546
5752
|
if (!update.empty) {
|
|
5547
5753
|
this.updatePlugins(update);
|
|
@@ -5556,7 +5762,7 @@ class EditorView {
|
|
|
5556
5762
|
finally {
|
|
5557
5763
|
this.updateState = 0 /* Idle */;
|
|
5558
5764
|
}
|
|
5559
|
-
if (redrawn ||
|
|
5765
|
+
if (redrawn || scrollTarget || this.viewState.mustEnforceCursorAssoc)
|
|
5560
5766
|
this.requestMeasure();
|
|
5561
5767
|
if (!update.empty)
|
|
5562
5768
|
for (let listener of this.state.facet(updateListener))
|
|
@@ -5638,7 +5844,7 @@ class EditorView {
|
|
|
5638
5844
|
this.updateState = 1 /* Measuring */;
|
|
5639
5845
|
let oldViewport = this.viewport;
|
|
5640
5846
|
let changed = this.viewState.measure(this.docView, i > 0);
|
|
5641
|
-
if (!changed && !this.measureRequests.length && this.viewState.
|
|
5847
|
+
if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
|
|
5642
5848
|
break;
|
|
5643
5849
|
if (i > 5) {
|
|
5644
5850
|
console.warn("Viewport failed to stabilize");
|
|
@@ -5680,9 +5886,9 @@ class EditorView {
|
|
|
5680
5886
|
logException(this.state, e);
|
|
5681
5887
|
}
|
|
5682
5888
|
}
|
|
5683
|
-
if (this.viewState.
|
|
5684
|
-
this.docView.
|
|
5685
|
-
this.viewState.
|
|
5889
|
+
if (this.viewState.scrollTarget) {
|
|
5890
|
+
this.docView.scrollIntoView(this.viewState.scrollTarget);
|
|
5891
|
+
this.viewState.scrollTarget = null;
|
|
5686
5892
|
}
|
|
5687
5893
|
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to && this.measureRequests.length == 0)
|
|
5688
5894
|
break;
|
|
@@ -5714,6 +5920,7 @@ class EditorView {
|
|
|
5714
5920
|
spellcheck: "false",
|
|
5715
5921
|
autocorrect: "off",
|
|
5716
5922
|
autocapitalize: "off",
|
|
5923
|
+
translate: "no",
|
|
5717
5924
|
contenteditable: !this.state.facet(editable) ? "false" : contentEditablePlainTextSupported() ? "plaintext-only" : "true",
|
|
5718
5925
|
class: "cm-content",
|
|
5719
5926
|
style: `${browser.tabSize}: ${this.state.tabSize}`,
|
|
@@ -5904,12 +6111,9 @@ class EditorView {
|
|
|
5904
6111
|
moveVertically(start, forward, distance) {
|
|
5905
6112
|
return skipAtoms(this, start, moveVertically(this, start, forward, distance));
|
|
5906
6113
|
}
|
|
5907
|
-
|
|
5908
|
-
Scroll the given document position into view.
|
|
5909
|
-
*/
|
|
6114
|
+
// FIXME remove on next major version
|
|
5910
6115
|
scrollPosIntoView(pos) {
|
|
5911
|
-
this.
|
|
5912
|
-
this.requestMeasure();
|
|
6116
|
+
this.dispatch({ effects: scrollTo.of(EditorSelection.cursor(pos)) });
|
|
5913
6117
|
}
|
|
5914
6118
|
/**
|
|
5915
6119
|
Find the DOM parent node and offset (child offset if `node` is
|
|
@@ -6076,7 +6280,7 @@ class EditorView {
|
|
|
6076
6280
|
target editors with a dark or light theme.
|
|
6077
6281
|
*/
|
|
6078
6282
|
static baseTheme(spec) {
|
|
6079
|
-
return Prec.
|
|
6283
|
+
return Prec.lowest(styleModule.of(buildTheme("." + baseThemeID, spec, lightDarkIDs)));
|
|
6080
6284
|
}
|
|
6081
6285
|
}
|
|
6082
6286
|
/**
|
|
@@ -6085,6 +6289,11 @@ transaction to make it scroll the given range into view.
|
|
|
6085
6289
|
*/
|
|
6086
6290
|
EditorView.scrollTo = scrollTo;
|
|
6087
6291
|
/**
|
|
6292
|
+
Effect that makes the editor scroll the given range to the
|
|
6293
|
+
center of the visible view.
|
|
6294
|
+
*/
|
|
6295
|
+
EditorView.centerOn = centerOn;
|
|
6296
|
+
/**
|
|
6088
6297
|
Facet to add a [style
|
|
6089
6298
|
module](https://github.com/marijnh/style-mod#documentation) to
|
|
6090
6299
|
an editor view. The view will ensure that the module is
|
|
@@ -6214,11 +6423,7 @@ class CachedOrder {
|
|
|
6214
6423
|
}
|
|
6215
6424
|
}
|
|
6216
6425
|
|
|
6217
|
-
const currentPlatform =
|
|
6218
|
-
: /*@__PURE__*//Mac/.test(navigator.platform) ? "mac"
|
|
6219
|
-
: /*@__PURE__*//Win/.test(navigator.platform) ? "win"
|
|
6220
|
-
: /*@__PURE__*//Linux|X11/.test(navigator.platform) ? "linux"
|
|
6221
|
-
: "key";
|
|
6426
|
+
const currentPlatform = browser.mac ? "mac" : browser.windows ? "win" : browser.linux ? "linux" : "key";
|
|
6222
6427
|
function normalizeKeyName(name, platform) {
|
|
6223
6428
|
const parts = name.split(/-(?!$)/);
|
|
6224
6429
|
let result = parts[parts.length - 1];
|
|
@@ -6517,7 +6722,7 @@ const themeSpec = {
|
|
|
6517
6722
|
};
|
|
6518
6723
|
if (CanHidePrimary)
|
|
6519
6724
|
themeSpec[".cm-line"].caretColor = "transparent !important";
|
|
6520
|
-
const hideNativeSelection = /*@__PURE__*/Prec.
|
|
6725
|
+
const hideNativeSelection = /*@__PURE__*/Prec.highest(/*@__PURE__*/EditorView.theme(themeSpec));
|
|
6521
6726
|
function getBase(view) {
|
|
6522
6727
|
let rect = view.scrollDOM.getBoundingClientRect();
|
|
6523
6728
|
let left = view.textDirection == Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth;
|
|
@@ -6724,7 +6929,7 @@ class MatchDecorator {
|
|
|
6724
6929
|
}
|
|
6725
6930
|
|
|
6726
6931
|
const UnicodeRegexpSupport = /x/.unicode != null ? "gu" : "g";
|
|
6727
|
-
const Specials = /*@__PURE__*/new RegExp("[\u0000-\u0008\u000a-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\ufeff\ufff9-\ufffc]", UnicodeRegexpSupport);
|
|
6932
|
+
const Specials = /*@__PURE__*/new RegExp("[\u0000-\u0008\u000a-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\ufeff\ufff9-\ufffc]", UnicodeRegexpSupport);
|
|
6728
6933
|
const Names = {
|
|
6729
6934
|
0: "null",
|
|
6730
6935
|
7: "bell",
|
|
@@ -6739,6 +6944,8 @@ const Names = {
|
|
|
6739
6944
|
8206: "left-to-right mark",
|
|
6740
6945
|
8207: "right-to-left mark",
|
|
6741
6946
|
8232: "line separator",
|
|
6947
|
+
8237: "left-to-right override",
|
|
6948
|
+
8238: "right-to-left override",
|
|
6742
6949
|
8233: "paragraph separator",
|
|
6743
6950
|
65279: "zero width no-break space",
|
|
6744
6951
|
65532: "object replacement"
|
|
@@ -6905,7 +7112,7 @@ DOM class.
|
|
|
6905
7112
|
function highlightActiveLine() {
|
|
6906
7113
|
return activeLineHighlighter;
|
|
6907
7114
|
}
|
|
6908
|
-
const lineDeco = /*@__PURE__*/Decoration.line({
|
|
7115
|
+
const lineDeco = /*@__PURE__*/Decoration.line({ class: "cm-activeLine" });
|
|
6909
7116
|
const activeLineHighlighter = /*@__PURE__*/ViewPlugin.fromClass(class {
|
|
6910
7117
|
constructor(view) {
|
|
6911
7118
|
this.decorations = this.getDeco(view);
|