@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/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.parentNode; cur;) {
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 (rect.top < bounding.top) {
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, pos = null;
321
+ let parent = this.dom;
322
+ let pos = parent.firstChild;
300
323
  for (let child of this.children) {
301
324
  if (child.dirty) {
302
- let next = pos ? pos.nextSibling : parent.firstChild;
303
- if (!child.dom && next && !((_a = ContentView.get(next)) === null || _a === void 0 ? void 0 : _a.parent))
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
- syncNodeInto(parent, pos, child.dom);
311
- pos = child.dom;
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
- let next = pos ? pos.nextSibling : parent.firstChild;
314
- if (next && track && track.node == parent)
341
+ if (pos && track && track.node == parent)
315
342
  track.written = true;
316
- while (next)
317
- next = rm(next);
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: safari && (/*@__PURE__*//Mobile\/\w+/.test(nav.userAgent) || nav.maxTouchPoints > 2),
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) instanceof WidgetView &&
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
- addWidget(view) {
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.getLine().append(this.wrapMarks(new TextView(this.text.slice(this.textOff, this.textOff + take)), active), openStart);
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.addWidget(new BlockWidgetView(deco.widget || new NullWidget("div"), len, type));
1499
+ this.addBlockWidget(new BlockWidgetView(deco.widget || new NullWidget("div"), len, type));
1427
1500
  }
1428
1501
  else {
1429
- let widget = this.wrapMarks(WidgetView.create(deco.widget || new NullWidget("span"), len, deco.startSide), active);
1430
- this.getLine().append(widget, openStart);
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
- scrollRangeIntoView(range) {
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.dom, {
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) && !(view.docView.nearest(element) instanceof WidgetView)) {
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
- this.pendingIOSKey = null;
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
- if (browser.ios && (event.keyCode == 13 || event.keyCode == 8) &&
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.pendingIOSKey = event.keyCode == 13 ? "enter" : "backspace";
3070
- setTimeout(() => this.flushIOSKey(view), 250);
3204
+ this.setPendingKey(view, pending);
3071
3205
  return true;
3072
3206
  }
3073
3207
  return false;
3074
3208
  }
3075
- flushIOSKey(view) {
3076
- if (!this.pendingIOSKey)
3077
- return false;
3078
- let dom = view.contentDOM, key = this.pendingIOSKey;
3079
- this.pendingIOSKey = null;
3080
- return key == "enter" ? dispatchKey(dom, "Enter", 13) : dispatchKey(dom, "Backspace", 8);
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.scrollTo = null;
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, scrollTo = null) {
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 (scrollTo && (scrollTo.head < viewport.from || scrollTo.head > viewport.to) || !this.viewportIsAppropriate(viewport))
4343
- viewport = this.getViewport(0, scrollTo);
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 > 15000 /* MinViewPort */)
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 (scrollTo)
4350
- this.scrollTo = scrollTo;
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.scrollTo && (this.scrollTo.head < this.viewport.from || this.scrollTo.head > this.viewport.to))
4404
- this.viewport = this.getViewport(bias, this.scrollTo);
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 > 15000 /* MinViewPort */)
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, scrollTo) {
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 scrollTo is given, make sure the viewport includes that position
4429
- if (scrollTo) {
4430
- if (scrollTo.head < viewport.from) {
4431
- let { top: newTop } = map.lineAt(scrollTo.head, QueryType.ByPos, doc, 0, 0);
4432
- viewport = new Viewport(map.lineAt(newTop - 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(newTop + (visibleBottom - visibleTop) + 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).to);
4433
- }
4434
- else if (scrollTo.head > viewport.to) {
4435
- let { bottom: newBottom } = map.lineAt(scrollTo.head, QueryType.ByPos, doc, 0, 0);
4436
- viewport = new Viewport(map.lineAt(newBottom - (visibleBottom - visibleTop) - 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(newBottom + 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).to);
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 < 10000 /* Margin */)
4668
+ if (line.length < 4000 /* DoubleMargin */)
4479
4669
  return;
4480
4670
  let structure = lineStructure(line.from, line.to, this.state);
4481
- if (structure.total < 10000 /* Margin */)
4671
+ if (structure.total < 4000 /* DoubleMargin */)
4482
4672
  return;
4483
4673
  let viewFrom, viewTo;
4484
4674
  if (this.heightOracle.lineWrapping) {
4485
- if (line.from != this.viewport.from)
4486
- viewFrom = line.from;
4487
- else
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
- viewFrom = findPosition(structure, this.pixelViewport.left / totalWidth);
4497
- viewTo = findPosition(structure, this.pixelViewport.right / totalWidth);
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 gap doesn't cover a selection end
4501
- if (sel.from <= viewFrom && sel.to >= line.from)
4502
- viewFrom = sel.from;
4503
- if (sel.from <= line.to && sel.to >= viewTo)
4504
- viewTo = sel.to;
4505
- let gapTo = viewFrom - 10000 /* Margin */, gapFrom = viewTo + 10000 /* Margin */;
4506
- if (gapTo > line.from + 5000 /* HalfMargin */)
4507
- gaps.push(find(current, gap => gap.from == line.from && gap.to > gapTo - 5000 /* HalfMargin */ && gap.to < gapTo + 5000 /* HalfMargin */) ||
4508
- new LineGap(line.from, gapTo, this.gapSize(line, gapTo, true, structure)));
4509
- if (gapFrom < line.to - 5000 /* HalfMargin */)
4510
- gaps.push(find(current, gap => gap.to == line.to && gap.from > gapFrom - 5000 /* HalfMargin */ &&
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, pos, start, structure) {
4705
+ gapSize(line, from, to, structure) {
4706
+ let fraction = findFraction(structure, to) - findFraction(structure, from);
4517
4707
  if (this.heightOracle.lineWrapping) {
4518
- let height = line.height * findFraction(structure, pos);
4519
- return start ? height : line.height - height;
4708
+ return line.height * fraction;
4520
4709
  }
4521
4710
  else {
4522
- let ratio = findFraction(structure, pos);
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
- // Apply pending changes, if any
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
- if (from > -1 || newSel)
5081
- this.onChange(from, to, typeOver);
5082
- if (this.view.state == startState) { // The view wasn't updated
5083
- if (this.view.docView.dirty) {
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 scrollPos = null;
5733
+ let scrollTarget = null;
5531
5734
  try {
5532
5735
  this.updateState = 2 /* Updating */;
5533
5736
  for (let tr of transactions) {
5534
- if (scrollPos)
5535
- scrollPos = scrollPos.map(tr.changes);
5737
+ if (scrollTarget)
5738
+ scrollTarget = scrollTarget.map(tr.changes);
5536
5739
  if (tr.scrollIntoView) {
5537
5740
  let { main } = tr.state.selection;
5538
- scrollPos = main.empty ? main : EditorSelection.cursor(main.head, main.head > main.anchor ? -1 : 1);
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
- scrollPos = e.value;
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, scrollPos);
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 || scrollPos || this.viewState.mustEnforceCursorAssoc)
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.scrollTo == null)
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.scrollTo) {
5684
- this.docView.scrollRangeIntoView(this.viewState.scrollTo);
5685
- this.viewState.scrollTo = null;
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.viewState.scrollTo = EditorSelection.cursor(pos);
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.fallback(styleModule.of(buildTheme("." + baseThemeID, spec, lightDarkIDs)));
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 = typeof navigator == "undefined" ? "key"
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.override(/*@__PURE__*/EditorView.theme(themeSpec));
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({ attributes: { class: "cm-activeLine" } });
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);