@codemirror/view 0.19.24 → 0.19.28

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
@@ -270,7 +270,7 @@ class DOMPos {
270
270
  static before(dom, precise) { return new DOMPos(dom.parentNode, domIndex(dom), precise); }
271
271
  static after(dom, precise) { return new DOMPos(dom.parentNode, domIndex(dom) + 1, precise); }
272
272
  }
273
- const none$3 = [];
273
+ const noChildren = [];
274
274
  class ContentView {
275
275
  constructor() {
276
276
  this.parent = null;
@@ -436,12 +436,12 @@ class ContentView {
436
436
  v = parent;
437
437
  }
438
438
  }
439
- replaceChildren(from, to, children = none$3) {
439
+ replaceChildren(from, to, children = noChildren) {
440
440
  this.markDirty();
441
441
  for (let i = from; i < to; i++) {
442
442
  let child = this.children[i];
443
443
  if (child.parent == this)
444
- child.parent = null;
444
+ child.destroy();
445
445
  }
446
446
  this.children.splice(from, to - from, ...children);
447
447
  for (let i = 0; i < children.length; i++)
@@ -463,6 +463,17 @@ class ContentView {
463
463
  }
464
464
  static get(node) { return node.cmView; }
465
465
  get isEditable() { return true; }
466
+ merge(from, to, source, hasStart, openStart, openEnd) {
467
+ return false;
468
+ }
469
+ become(other) { return false; }
470
+ // When this is a zero-length view with a side, this should return a
471
+ // number <= 0 to indicate it is before its position, or a
472
+ // number > 0 when after its position.
473
+ getSide() { return 0; }
474
+ destroy() {
475
+ this.parent = null;
476
+ }
466
477
  }
467
478
  ContentView.prototype.breakAfter = 0;
468
479
  // Remove a DOM node and return its next sibling.
@@ -490,6 +501,94 @@ class ChildCursor {
490
501
  }
491
502
  }
492
503
  }
504
+ function replaceRange(parent, fromI, fromOff, toI, toOff, insert, breakAtStart, openStart, openEnd) {
505
+ let { children } = parent;
506
+ let before = children.length ? children[fromI] : null;
507
+ let last = insert.length ? insert[insert.length - 1] : null;
508
+ let breakAtEnd = last ? last.breakAfter : breakAtStart;
509
+ // Change within a single child
510
+ if (fromI == toI && before && !breakAtStart && !breakAtEnd && insert.length < 2 &&
511
+ before.merge(fromOff, toOff, insert.length ? last : null, fromOff == 0, openStart, openEnd))
512
+ return;
513
+ if (toI < children.length) {
514
+ let after = children[toI];
515
+ // Make sure the end of the child after the update is preserved in `after`
516
+ if (after && toOff < after.length) {
517
+ // If we're splitting a child, separate part of it to avoid that
518
+ // being mangled when updating the child before the update.
519
+ if (fromI == toI) {
520
+ after = after.split(toOff);
521
+ toOff = 0;
522
+ }
523
+ // If the element after the replacement should be merged with
524
+ // the last replacing element, update `content`
525
+ if (!breakAtEnd && last && after.merge(0, toOff, last, true, 0, openEnd)) {
526
+ insert[insert.length - 1] = after;
527
+ }
528
+ else {
529
+ // Remove the start of the after element, if necessary, and
530
+ // add it to `content`.
531
+ if (toOff)
532
+ after.merge(0, toOff, null, false, 0, openEnd);
533
+ insert.push(after);
534
+ }
535
+ }
536
+ else if (after === null || after === void 0 ? void 0 : after.breakAfter) {
537
+ // The element at `toI` is entirely covered by this range.
538
+ // Preserve its line break, if any.
539
+ if (last)
540
+ last.breakAfter = 1;
541
+ else
542
+ breakAtStart = 1;
543
+ }
544
+ // Since we've handled the next element from the current elements
545
+ // now, make sure `toI` points after that.
546
+ toI++;
547
+ }
548
+ if (before) {
549
+ before.breakAfter = breakAtStart;
550
+ if (fromOff > 0) {
551
+ if (!breakAtStart && insert.length && before.merge(fromOff, before.length, insert[0], false, openStart, 0)) {
552
+ before.breakAfter = insert.shift().breakAfter;
553
+ }
554
+ else if (fromOff < before.length || before.children.length && before.children[before.children.length - 1].length == 0) {
555
+ before.merge(fromOff, before.length, null, false, openStart, 0);
556
+ }
557
+ fromI++;
558
+ }
559
+ }
560
+ // Try to merge widgets on the boundaries of the replacement
561
+ while (fromI < toI && insert.length) {
562
+ if (children[toI - 1].become(insert[insert.length - 1])) {
563
+ toI--;
564
+ insert.pop();
565
+ openEnd = insert.length ? 0 : openStart;
566
+ }
567
+ else if (children[fromI].become(insert[0])) {
568
+ fromI++;
569
+ insert.shift();
570
+ openStart = insert.length ? 0 : openEnd;
571
+ }
572
+ else {
573
+ break;
574
+ }
575
+ }
576
+ if (!insert.length && fromI && toI < children.length && !children[fromI - 1].breakAfter &&
577
+ children[toI].merge(0, 0, children[fromI - 1], false, openStart, openEnd))
578
+ fromI--;
579
+ if (fromI < toI || insert.length)
580
+ parent.replaceChildren(fromI, toI, insert);
581
+ }
582
+ function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) {
583
+ let cur = parent.childCursor();
584
+ let { i: toI, off: toOff } = cur.findPos(to, 1);
585
+ let { i: fromI, off: fromOff } = cur.findPos(from, -1);
586
+ let dLen = from - to;
587
+ for (let view of insert)
588
+ dLen += view.length;
589
+ parent.length += dLen;
590
+ replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd);
591
+ }
493
592
 
494
593
  let [nav, doc] = typeof navigator != "undefined"
495
594
  ? [navigator, document]
@@ -521,21 +620,8 @@ var browser = {
521
620
  tabSize: doc.documentElement.style.tabSize != null ? "tab-size" : "-moz-tab-size"
522
621
  };
523
622
 
524
- const none$2 = [];
525
- class InlineView extends ContentView {
526
- /**
527
- Return true when this view is equivalent to `other` and can take
528
- on its role.
529
- */
530
- become(_other) { return false; }
531
- // When this is a zero-length view with a side, this should return a
532
- // negative number to indicate it is before its position, or a
533
- // positive number when after its position.
534
- getSide() { return 0; }
535
- }
536
- InlineView.prototype.children = none$2;
537
623
  const MaxJoinLen = 256;
538
- class TextView extends InlineView {
624
+ class TextView extends ContentView {
539
625
  constructor(text) {
540
626
  super();
541
627
  this.text = text;
@@ -566,7 +652,7 @@ class TextView extends InlineView {
566
652
  this.markDirty();
567
653
  return true;
568
654
  }
569
- slice(from) {
655
+ split(from) {
570
656
  let result = new TextView(this.text.slice(from));
571
657
  this.text = this.text.slice(0, from);
572
658
  return result;
@@ -582,7 +668,7 @@ class TextView extends InlineView {
582
668
  return textCoords(this.dom, pos, side);
583
669
  }
584
670
  }
585
- class MarkView extends InlineView {
671
+ class MarkView extends ContentView {
586
672
  constructor(mark, children = [], length = 0) {
587
673
  super();
588
674
  this.mark = mark;
@@ -605,20 +691,20 @@ class MarkView extends InlineView {
605
691
  this.createDOM();
606
692
  super.sync(track);
607
693
  }
608
- merge(from, to, source, openStart, openEnd) {
694
+ merge(from, to, source, _hasStart, openStart, openEnd) {
609
695
  if (source && (!(source instanceof MarkView && source.mark.eq(this.mark)) ||
610
696
  (from && openStart <= 0) || (to < this.length && openEnd <= 0)))
611
697
  return false;
612
- mergeInlineChildren(this, from, to, source ? source.children : none$2, openStart - 1, openEnd - 1);
698
+ mergeChildrenInto(this, from, to, source ? source.children : [], openStart - 1, openEnd - 1);
613
699
  this.markDirty();
614
700
  return true;
615
701
  }
616
- slice(from) {
702
+ split(from) {
617
703
  let result = [], off = 0, detachFrom = -1, i = 0;
618
704
  for (let elt of this.children) {
619
705
  let end = off + elt.length;
620
706
  if (end > from)
621
- result.push(off < from ? elt.slice(from - off) : elt);
707
+ result.push(off < from ? elt.split(from - off) : elt);
622
708
  if (detachFrom < 0 && off >= from)
623
709
  detachFrom = i;
624
710
  off = end;
@@ -626,8 +712,10 @@ class MarkView extends InlineView {
626
712
  }
627
713
  let length = this.length - from;
628
714
  this.length = from;
629
- if (detachFrom > -1)
630
- this.replaceChildren(detachFrom, this.children.length);
715
+ if (detachFrom > -1) {
716
+ this.children.length = detachFrom;
717
+ this.markDirty();
718
+ }
631
719
  return new MarkView(this.mark, result, length);
632
720
  }
633
721
  domAtPos(pos) {
@@ -669,7 +757,7 @@ function textCoords(text, pos, side) {
669
757
  return flatten ? flattenRect(rect, flatten < 0) : rect || null;
670
758
  }
671
759
  // Also used for collapsed ranges that don't have a placeholder widget!
672
- class WidgetView extends InlineView {
760
+ class WidgetView extends ContentView {
673
761
  constructor(widget, length, side) {
674
762
  super();
675
763
  this.widget = widget;
@@ -679,7 +767,7 @@ class WidgetView extends InlineView {
679
767
  static create(widget, length, side) {
680
768
  return new (widget.customView || WidgetView)(widget, length, side);
681
769
  }
682
- slice(from) {
770
+ split(from) {
683
771
  let result = WidgetView.create(this.widget, this.length - from, this.side);
684
772
  this.length -= from;
685
773
  return result;
@@ -691,7 +779,7 @@ class WidgetView extends InlineView {
691
779
  }
692
780
  }
693
781
  getSide() { return this.side; }
694
- merge(from, to, source, openStart, openEnd) {
782
+ merge(from, to, source, hasStart, openStart, openEnd) {
695
783
  if (source && (!(source instanceof WidgetView) || !this.widget.compare(source.widget) ||
696
784
  from > 0 && openStart <= 0 || to < this.length && openEnd <= 0))
697
785
  return false;
@@ -736,6 +824,11 @@ class WidgetView extends InlineView {
736
824
  return (pos == 0 && side > 0 || pos == this.length && side <= 0) ? rect : flattenRect(rect, pos == 0);
737
825
  }
738
826
  get isEditable() { return false; }
827
+ destroy() {
828
+ super.destroy();
829
+ if (this.dom)
830
+ this.widget.destroy(this.dom);
831
+ }
739
832
  }
740
833
  class CompositionView extends WidgetView {
741
834
  domAtPos(pos) { return new DOMPos(this.widget.text, pos); }
@@ -749,10 +842,13 @@ class CompositionView extends WidgetView {
749
842
  coordsAt(pos, side) { return textCoords(this.widget.text, pos, side); }
750
843
  get isEditable() { return true; }
751
844
  }
845
+ // Use two characters on Android, to prevent Chrome from closing the
846
+ // virtual keyboard when backspacing after a widget (#602).
847
+ const ZeroWidthSpace = browser.android ? "\u200b\u200b" : "\u200b";
752
848
  // These are drawn around uneditable widgets to avoid a number of
753
849
  // browser bugs that show up when the cursor is directly next to
754
850
  // uneditable inline content.
755
- class WidgetBufferView extends InlineView {
851
+ class WidgetBufferView extends ContentView {
756
852
  constructor(side) {
757
853
  super();
758
854
  this.side = side;
@@ -762,15 +858,16 @@ class WidgetBufferView extends InlineView {
762
858
  become(other) {
763
859
  return other instanceof WidgetBufferView && other.side == this.side;
764
860
  }
765
- slice() { return new WidgetBufferView(this.side); }
861
+ split() { return new WidgetBufferView(this.side); }
766
862
  sync() {
767
863
  if (!this.dom)
768
- this.setDOM(document.createTextNode("\u200b"));
769
- else if (this.dirty && this.dom.nodeValue != "\u200b")
770
- this.dom.nodeValue = "\u200b";
864
+ this.setDOM(document.createTextNode(ZeroWidthSpace));
865
+ else if (this.dirty && this.dom.nodeValue != ZeroWidthSpace)
866
+ this.dom.nodeValue = ZeroWidthSpace;
771
867
  }
772
868
  getSide() { return this.side; }
773
869
  domAtPos(pos) { return DOMPos.before(this.dom); }
870
+ localPosFromDOM() { return 0; }
774
871
  domBoundsAround() { return null; }
775
872
  coordsAt(pos) {
776
873
  let rects = clientRectsFor(this.dom);
@@ -780,90 +877,7 @@ class WidgetBufferView extends InlineView {
780
877
  return Text.of([this.dom.nodeValue.replace(/\u200b/g, "")]);
781
878
  }
782
879
  }
783
- function mergeInlineChildren(parent, from, to, elts, openStart, openEnd) {
784
- let cur = parent.childCursor();
785
- let { i: toI, off: toOff } = cur.findPos(to, 1);
786
- let { i: fromI, off: fromOff } = cur.findPos(from, -1);
787
- let dLen = from - to;
788
- for (let view of elts)
789
- dLen += view.length;
790
- parent.length += dLen;
791
- let { children } = parent;
792
- // Both from and to point into the same child view
793
- if (fromI == toI && fromOff) {
794
- let start = children[fromI];
795
- // Maybe just update that view and be done
796
- if (elts.length == 1 && start.merge(fromOff, toOff, elts[0], openStart, openEnd))
797
- return;
798
- if (elts.length == 0) {
799
- start.merge(fromOff, toOff, null, openStart, openEnd);
800
- return;
801
- }
802
- // Otherwise split it, so that we don't have to worry about aliasing front/end afterwards
803
- let after = start.slice(toOff);
804
- if (after.merge(0, 0, elts[elts.length - 1], 0, openEnd))
805
- elts[elts.length - 1] = after;
806
- else
807
- elts.push(after);
808
- toI++;
809
- openEnd = toOff = 0;
810
- }
811
- // Make sure start and end positions fall on node boundaries
812
- // (fromOff/toOff are no longer used after this), and that if the
813
- // start or end of the elts can be merged with adjacent nodes,
814
- // this is done
815
- if (toOff) {
816
- let end = children[toI];
817
- if (elts.length && end.merge(0, toOff, elts[elts.length - 1], 0, openEnd)) {
818
- elts.pop();
819
- openEnd = elts.length ? 0 : openStart;
820
- }
821
- else {
822
- end.merge(0, toOff, null, 0, 0);
823
- }
824
- }
825
- else if (toI < children.length && elts.length &&
826
- children[toI].merge(0, 0, elts[elts.length - 1], 0, openEnd)) {
827
- elts.pop();
828
- openEnd = elts.length ? 0 : openStart;
829
- }
830
- if (fromOff) {
831
- let start = children[fromI];
832
- if (elts.length && start.merge(fromOff, start.length, elts[0], openStart, 0)) {
833
- elts.shift();
834
- openStart = elts.length ? 0 : openEnd;
835
- }
836
- else {
837
- start.merge(fromOff, start.length, null, 0, 0);
838
- }
839
- fromI++;
840
- }
841
- else if (fromI && elts.length) {
842
- let end = children[fromI - 1];
843
- if (end.merge(end.length, end.length, elts[0], openStart, 0)) {
844
- elts.shift();
845
- openStart = elts.length ? 0 : openEnd;
846
- }
847
- }
848
- // Then try to merge any mergeable nodes at the start and end of
849
- // the changed range
850
- while (fromI < toI && elts.length && children[toI - 1].become(elts[elts.length - 1])) {
851
- elts.pop();
852
- toI--;
853
- openEnd = elts.length ? 0 : openStart;
854
- }
855
- while (fromI < toI && elts.length && children[fromI].become(elts[0])) {
856
- elts.shift();
857
- fromI++;
858
- openStart = elts.length ? 0 : openEnd;
859
- }
860
- if (!elts.length && fromI && toI < children.length &&
861
- children[toI].merge(0, 0, children[fromI - 1], openStart, openEnd))
862
- fromI--;
863
- // And if anything remains, splice the child array to insert the new elts
864
- if (elts.length || fromI != toI)
865
- parent.replaceChildren(fromI, toI, elts);
866
- }
880
+ TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren;
867
881
  function inlineDOMAtPos(dom, children, pos) {
868
882
  let i = 0;
869
883
  for (let off = 0; i < children.length; i++) {
@@ -1004,6 +1018,11 @@ class WidgetType {
1004
1018
  @internal
1005
1019
  */
1006
1020
  get customView() { return null; }
1021
+ /**
1022
+ This is called when the an instance of the widget is removed
1023
+ from the editor view.
1024
+ */
1025
+ destroy(_dom) { }
1007
1026
  }
1008
1027
  /**
1009
1028
  The different types of blocks that can occur in an editor view.
@@ -1082,8 +1101,9 @@ class Decoration extends RangeValue {
1082
1101
  position.
1083
1102
  */
1084
1103
  static widget(spec) {
1085
- let side = spec.side || 0;
1086
- return new PointDecoration(spec, side, side, !!spec.block, spec.widget || null, false);
1104
+ let side = spec.side || 0, block = !!spec.block;
1105
+ side += block ? (side > 0 ? 300000000 /* BlockAfter */ : -400000000 /* BlockBefore */) : (side > 0 ? 100000000 /* InlineAfter */ : -100000000 /* InlineBefore */);
1106
+ return new PointDecoration(spec, side, side, block, spec.widget || null, false);
1087
1107
  }
1088
1108
  /**
1089
1109
  Create a replace decoration which replaces the given range with
@@ -1092,8 +1112,8 @@ class Decoration extends RangeValue {
1092
1112
  static replace(spec) {
1093
1113
  let block = !!spec.block;
1094
1114
  let { start, end } = getInclusive(spec, block);
1095
- let startSide = 100000000 /* Big */ * (start ? -1 : 1) * (block ? 2 : 1);
1096
- let endSide = 100000000 /* Big */ * (end ? 1 : -1) * (block ? 2 : 1);
1115
+ let startSide = block ? (start ? -300000000 /* BlockIncStart */ : -1 /* InlineIncStart */) : 400000000 /* NonIncStart */;
1116
+ let endSide = block ? (end ? 200000000 /* BlockIncEnd */ : 1 /* InlineIncEnd */) : -500000000 /* NonIncEnd */;
1097
1117
  return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
1098
1118
  }
1099
1119
  /**
@@ -1123,7 +1143,7 @@ Decoration.none = RangeSet.empty;
1123
1143
  class MarkDecoration extends Decoration {
1124
1144
  constructor(spec) {
1125
1145
  let { start, end } = getInclusive(spec);
1126
- super(100000000 /* Big */ * (start ? -1 : 1), 100000000 /* Big */ * (end ? 1 : -1), null, spec);
1146
+ super(start ? -1 /* InlineIncStart */ : 400000000 /* NonIncStart */, end ? 1 /* InlineIncEnd */ : -500000000 /* NonIncEnd */, null, spec);
1127
1147
  this.tagName = spec.tagName || "span";
1128
1148
  this.class = spec.class || "";
1129
1149
  this.attrs = spec.attributes || null;
@@ -1144,7 +1164,7 @@ class MarkDecoration extends Decoration {
1144
1164
  MarkDecoration.prototype.point = false;
1145
1165
  class LineDecoration extends Decoration {
1146
1166
  constructor(spec) {
1147
- super(-100000000 /* Big */, -100000000 /* Big */, null, spec);
1167
+ super(-200000000 /* Line */, -200000000 /* Line */, null, spec);
1148
1168
  }
1149
1169
  eq(other) {
1150
1170
  return other instanceof LineDecoration && attrsEq(this.spec.attributes, other.spec.attributes);
@@ -1162,12 +1182,12 @@ class PointDecoration extends Decoration {
1162
1182
  super(startSide, endSide, widget, spec);
1163
1183
  this.block = block;
1164
1184
  this.isReplace = isReplace;
1165
- this.mapMode = !block ? MapMode.TrackDel : startSide < 0 ? MapMode.TrackBefore : MapMode.TrackAfter;
1185
+ this.mapMode = !block ? MapMode.TrackDel : startSide <= 0 ? MapMode.TrackBefore : MapMode.TrackAfter;
1166
1186
  }
1167
1187
  // Only relevant when this.block == true
1168
1188
  get type() {
1169
1189
  return this.startSide < this.endSide ? BlockType.WidgetRange
1170
- : this.startSide < 0 ? BlockType.WidgetBefore : BlockType.WidgetAfter;
1190
+ : this.startSide <= 0 ? BlockType.WidgetBefore : BlockType.WidgetAfter;
1171
1191
  }
1172
1192
  get heightRelevant() { return this.block || !!this.widget && this.widget.estimatedHeight >= 5; }
1173
1193
  eq(other) {
@@ -1177,7 +1197,7 @@ class PointDecoration extends Decoration {
1177
1197
  this.startSide == other.startSide && this.endSide == other.endSide;
1178
1198
  }
1179
1199
  range(from, to = from) {
1180
- if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide < 0)))
1200
+ if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide <= 0)))
1181
1201
  throw new RangeError("Invalid range for replacement decoration");
1182
1202
  if (!this.isReplace && to != from)
1183
1203
  throw new RangeError("Widget decorations can only have zero-length ranges");
@@ -1214,16 +1234,16 @@ class LineView extends ContentView {
1214
1234
  this.breakAfter = 0;
1215
1235
  }
1216
1236
  // Consumes source
1217
- merge(from, to, source, takeDeco, openStart, openEnd) {
1237
+ merge(from, to, source, hasStart, openStart, openEnd) {
1218
1238
  if (source) {
1219
1239
  if (!(source instanceof LineView))
1220
1240
  return false;
1221
1241
  if (!this.dom)
1222
1242
  source.transferDOM(this); // Reuse source.dom when appropriate
1223
1243
  }
1224
- if (takeDeco)
1244
+ if (hasStart)
1225
1245
  this.setDeco(source ? source.attrs : null);
1226
- mergeInlineChildren(this, from, to, source ? source.children : none$1, openStart, openEnd);
1246
+ mergeChildrenInto(this, from, to, source ? source.children : [], openStart, openEnd);
1227
1247
  return true;
1228
1248
  }
1229
1249
  split(at) {
@@ -1233,16 +1253,14 @@ class LineView extends ContentView {
1233
1253
  return end;
1234
1254
  let { i, off } = this.childPos(at);
1235
1255
  if (off) {
1236
- end.append(this.children[i].slice(off), 0);
1237
- this.children[i].merge(off, this.children[i].length, null, 0, 0);
1256
+ end.append(this.children[i].split(off), 0);
1257
+ this.children[i].merge(off, this.children[i].length, null, false, 0, 0);
1238
1258
  i++;
1239
1259
  }
1240
1260
  for (let j = i; j < this.children.length; j++)
1241
1261
  end.append(this.children[j], 0);
1242
- while (i > 0 && this.children[i - 1].length == 0) {
1243
- this.children[i - 1].parent = null;
1244
- i--;
1245
- }
1262
+ while (i > 0 && this.children[i - 1].length == 0)
1263
+ this.children[--i].destroy();
1246
1264
  this.children.length = i;
1247
1265
  this.markDirty();
1248
1266
  this.length = at;
@@ -1265,7 +1283,6 @@ class LineView extends ContentView {
1265
1283
  this.attrs = attrs;
1266
1284
  }
1267
1285
  }
1268
- // Only called when building a line view in ContentBuilder
1269
1286
  append(child, openStart) {
1270
1287
  joinInlineInto(this, child, openStart);
1271
1288
  }
@@ -1322,7 +1339,7 @@ class LineView extends ContentView {
1322
1339
  coordsAt(pos, side) {
1323
1340
  return coordsInChildren(this, pos, side);
1324
1341
  }
1325
- match(_other) { return false; }
1342
+ become(_other) { return false; }
1326
1343
  get type() { return BlockType.Text; }
1327
1344
  static find(docView, pos) {
1328
1345
  for (let i = 0, off = 0;; i++) {
@@ -1337,7 +1354,6 @@ class LineView extends ContentView {
1337
1354
  }
1338
1355
  }
1339
1356
  }
1340
- const none$1 = [];
1341
1357
  class BlockWidgetView extends ContentView {
1342
1358
  constructor(widget, length, type) {
1343
1359
  super();
@@ -1363,7 +1379,7 @@ class BlockWidgetView extends ContentView {
1363
1379
  end.breakAfter = this.breakAfter;
1364
1380
  return end;
1365
1381
  }
1366
- get children() { return none$1; }
1382
+ get children() { return noChildren; }
1367
1383
  sync() {
1368
1384
  if (!this.dom || !this.widget.updateDOM(this.dom)) {
1369
1385
  this.setDOM(this.widget.toDOM(this.editorView));
@@ -1374,7 +1390,7 @@ class BlockWidgetView extends ContentView {
1374
1390
  return this.parent ? this.parent.view.state.doc.slice(this.posAtStart, this.posAtEnd) : Text$1.empty;
1375
1391
  }
1376
1392
  domBoundsAround() { return null; }
1377
- match(other) {
1393
+ become(other) {
1378
1394
  if (other instanceof BlockWidgetView && other.type == this.type &&
1379
1395
  other.widget.constructor == this.widget.constructor) {
1380
1396
  if (!other.widget.eq(this.widget))
@@ -1388,6 +1404,11 @@ class BlockWidgetView extends ContentView {
1388
1404
  }
1389
1405
  ignoreMutation() { return true; }
1390
1406
  ignoreEvent(event) { return this.widget.ignoreEvent(event); }
1407
+ destroy() {
1408
+ super.destroy();
1409
+ if (this.dom)
1410
+ this.widget.destroy(this.dom);
1411
+ }
1391
1412
  }
1392
1413
 
1393
1414
  class ContentBuilder {
@@ -1723,36 +1744,39 @@ class PluginInstance {
1723
1744
  this.value = null;
1724
1745
  }
1725
1746
  takeField(type, target) {
1726
- for (let { field, get } of this.spec.fields)
1727
- if (field == type)
1728
- target.push(get(this.value));
1747
+ if (this.spec)
1748
+ for (let { field, get } of this.spec.fields)
1749
+ if (field == type)
1750
+ target.push(get(this.value));
1729
1751
  }
1730
1752
  update(view) {
1731
1753
  if (!this.value) {
1732
- try {
1733
- this.value = this.spec.create(view);
1734
- }
1735
- catch (e) {
1736
- logException(view.state, e, "CodeMirror plugin crashed");
1737
- return PluginInstance.dummy;
1754
+ if (this.spec) {
1755
+ try {
1756
+ this.value = this.spec.create(view);
1757
+ }
1758
+ catch (e) {
1759
+ logException(view.state, e, "CodeMirror plugin crashed");
1760
+ this.deactivate();
1761
+ }
1738
1762
  }
1739
1763
  }
1740
1764
  else if (this.mustUpdate) {
1741
1765
  let update = this.mustUpdate;
1742
1766
  this.mustUpdate = null;
1743
- if (!this.value.update)
1744
- return this;
1745
- try {
1746
- this.value.update(update);
1747
- }
1748
- catch (e) {
1749
- logException(update.state, e, "CodeMirror plugin crashed");
1750
- if (this.value.destroy)
1751
- try {
1752
- this.value.destroy();
1753
- }
1754
- catch (_) { }
1755
- return PluginInstance.dummy;
1767
+ if (this.value.update) {
1768
+ try {
1769
+ this.value.update(update);
1770
+ }
1771
+ catch (e) {
1772
+ logException(update.state, e, "CodeMirror plugin crashed");
1773
+ if (this.value.destroy)
1774
+ try {
1775
+ this.value.destroy();
1776
+ }
1777
+ catch (_) { }
1778
+ this.deactivate();
1779
+ }
1756
1780
  }
1757
1781
  }
1758
1782
  return this;
@@ -1768,20 +1792,12 @@ class PluginInstance {
1768
1792
  }
1769
1793
  }
1770
1794
  }
1795
+ deactivate() {
1796
+ this.spec = this.value = null;
1797
+ }
1771
1798
  }
1772
- PluginInstance.dummy = /*@__PURE__*/new PluginInstance(/*@__PURE__*/ViewPlugin.define(() => ({})));
1773
- function combineFacetAttrs(values) {
1774
- let result = {};
1775
- for (let i = values.length - 1; i >= 0; i--)
1776
- combineAttrs(values[i], result);
1777
- return result;
1778
- }
1779
- const editorAttributes = /*@__PURE__*/Facet.define({
1780
- combine: combineFacetAttrs
1781
- });
1782
- const contentAttributes = /*@__PURE__*/Facet.define({
1783
- combine: combineFacetAttrs
1784
- });
1799
+ const editorAttributes = /*@__PURE__*/Facet.define();
1800
+ const contentAttributes = /*@__PURE__*/Facet.define();
1785
1801
  // Provide decorations
1786
1802
  const decorations = /*@__PURE__*/Facet.define();
1787
1803
  const styleModule = /*@__PURE__*/Facet.define();
@@ -1922,197 +1938,446 @@ class ViewUpdate {
1922
1938
  get empty() { return this.flags == 0 && this.transactions.length == 0; }
1923
1939
  }
1924
1940
 
1925
- class DocView extends ContentView {
1926
- constructor(view) {
1927
- super();
1928
- this.view = view;
1929
- this.compositionDeco = Decoration.none;
1930
- this.decorations = [];
1931
- // Track a minimum width for the editor. When measuring sizes in
1932
- // checkLayout, this is updated to point at the width of a given
1933
- // element and its extent in the document. When a change happens in
1934
- // that range, these are reset. That way, once we've seen a
1935
- // line/element of a given length, we keep the editor wide enough to
1936
- // fit at least that element, until it is changed, at which point we
1937
- // forget it again.
1938
- this.minWidth = 0;
1939
- this.minWidthFrom = 0;
1940
- this.minWidthTo = 0;
1941
- // Track whether the DOM selection was set in a lossy way, so that
1942
- // we don't mess it up when reading it back it
1943
- this.impreciseAnchor = null;
1944
- this.impreciseHead = null;
1945
- this.forceSelection = false;
1946
- // Used by the resize observer to ignore resizes that we caused
1947
- // ourselves
1948
- this.lastUpdate = Date.now();
1949
- this.setDOM(view.contentDOM);
1950
- this.children = [new LineView];
1951
- this.children[0].setParent(this);
1952
- this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], this.updateDeco(), 0);
1941
+ /**
1942
+ Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
1943
+ */
1944
+ var Direction = /*@__PURE__*/(function (Direction) {
1945
+ // (These are chosen to match the base levels, in bidi algorithm
1946
+ // terms, of spans in that direction.)
1947
+ /**
1948
+ Left-to-right.
1949
+ */
1950
+ Direction[Direction["LTR"] = 0] = "LTR";
1951
+ /**
1952
+ Right-to-left.
1953
+ */
1954
+ Direction[Direction["RTL"] = 1] = "RTL";
1955
+ return Direction})(Direction || (Direction = {}));
1956
+ const LTR = Direction.LTR, RTL = Direction.RTL;
1957
+ // Decode a string with each type encoded as log2(type)
1958
+ function dec(str) {
1959
+ let result = [];
1960
+ for (let i = 0; i < str.length; i++)
1961
+ result.push(1 << +str[i]);
1962
+ return result;
1963
+ }
1964
+ // Character types for codepoints 0 to 0xf8
1965
+ const LowTypes = /*@__PURE__*/dec("88888888888888888888888888888888888666888888787833333333337888888000000000000000000000000008888880000000000000000000000000088888888888888888888888888888888888887866668888088888663380888308888800000000000000000000000800000000000000000000000000000008");
1966
+ // Character types for codepoints 0x600 to 0x6f9
1967
+ const ArabicTypes = /*@__PURE__*/dec("4444448826627288999999999992222222222222222222222222222222222222222222222229999999999999999999994444444444644222822222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222999999949999999229989999223333333333");
1968
+ const Brackets = /*@__PURE__*/Object.create(null), BracketStack = [];
1969
+ // There's a lot more in
1970
+ // https://www.unicode.org/Public/UCD/latest/ucd/BidiBrackets.txt,
1971
+ // which are left out to keep code size down.
1972
+ for (let p of ["()", "[]", "{}"]) {
1973
+ let l = /*@__PURE__*/p.charCodeAt(0), r = /*@__PURE__*/p.charCodeAt(1);
1974
+ Brackets[l] = r;
1975
+ Brackets[r] = -l;
1976
+ }
1977
+ function charType(ch) {
1978
+ return ch <= 0xf7 ? LowTypes[ch] :
1979
+ 0x590 <= ch && ch <= 0x5f4 ? 2 /* R */ :
1980
+ 0x600 <= ch && ch <= 0x6f9 ? ArabicTypes[ch - 0x600] :
1981
+ 0x6ee <= ch && ch <= 0x8ac ? 4 /* AL */ :
1982
+ 0x2000 <= ch && ch <= 0x200b ? 256 /* NI */ :
1983
+ ch == 0x200c ? 256 /* NI */ : 1 /* L */;
1984
+ }
1985
+ const BidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
1986
+ /**
1987
+ Represents a contiguous range of text that has a single direction
1988
+ (as in left-to-right or right-to-left).
1989
+ */
1990
+ class BidiSpan {
1991
+ /**
1992
+ @internal
1993
+ */
1994
+ constructor(
1995
+ /**
1996
+ The start of the span (relative to the start of the line).
1997
+ */
1998
+ from,
1999
+ /**
2000
+ The end of the span.
2001
+ */
2002
+ to,
2003
+ /**
2004
+ The ["bidi
2005
+ level"](https://unicode.org/reports/tr9/#Basic_Display_Algorithm)
2006
+ of the span (in this context, 0 means
2007
+ left-to-right, 1 means right-to-left, 2 means left-to-right
2008
+ number inside right-to-left text).
2009
+ */
2010
+ level) {
2011
+ this.from = from;
2012
+ this.to = to;
2013
+ this.level = level;
1953
2014
  }
1954
- get root() { return this.view.root; }
1955
- get editorView() { return this.view; }
1956
- get length() { return this.view.state.doc.length; }
1957
- // Update the document view to a given state. scrollIntoView can be
1958
- // used as a hint to compute a new viewport that includes that
1959
- // position, if we know the editor is going to scroll that position
1960
- // into view.
1961
- update(update) {
1962
- let changedRanges = update.changedRanges;
1963
- if (this.minWidth > 0 && changedRanges.length) {
1964
- if (!changedRanges.every(({ fromA, toA }) => toA < this.minWidthFrom || fromA > this.minWidthTo)) {
1965
- this.minWidth = 0;
1966
- }
1967
- else {
1968
- this.minWidthFrom = update.changes.mapPos(this.minWidthFrom, 1);
1969
- this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
1970
- }
1971
- }
1972
- if (this.view.inputState.composing < 0)
1973
- this.compositionDeco = Decoration.none;
1974
- else if (update.transactions.length)
1975
- this.compositionDeco = computeCompositionDeco(this.view, update.changes);
1976
- // When the DOM nodes around the selection are moved to another
1977
- // parent, Chrome sometimes reports a different selection through
1978
- // getSelection than the one that it actually shows to the user.
1979
- // This forces a selection update when lines are joined to work
1980
- // around that. Issue #54
1981
- if ((browser.ie || browser.chrome) && !this.compositionDeco.size && update &&
1982
- update.state.doc.lines != update.startState.doc.lines)
1983
- this.forceSelection = true;
1984
- let prevDeco = this.decorations, deco = this.updateDeco();
1985
- let decoDiff = findChangedDeco(prevDeco, deco, update.changes);
1986
- changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
1987
- if (this.dirty == 0 /* Not */ && changedRanges.length == 0) {
1988
- return false;
1989
- }
1990
- else {
1991
- this.updateInner(changedRanges, deco, update.startState.doc.length);
1992
- if (update.transactions.length)
1993
- this.lastUpdate = Date.now();
1994
- return true;
2015
+ /**
2016
+ The direction of this span.
2017
+ */
2018
+ get dir() { return this.level % 2 ? RTL : LTR; }
2019
+ /**
2020
+ @internal
2021
+ */
2022
+ side(end, dir) { return (this.dir == dir) == end ? this.to : this.from; }
2023
+ /**
2024
+ @internal
2025
+ */
2026
+ static find(order, index, level, assoc) {
2027
+ let maybe = -1;
2028
+ for (let i = 0; i < order.length; i++) {
2029
+ let span = order[i];
2030
+ if (span.from <= index && span.to >= index) {
2031
+ if (span.level == level)
2032
+ return i;
2033
+ // When multiple spans match, if assoc != 0, take the one that
2034
+ // covers that side, otherwise take the one with the minimum
2035
+ // level.
2036
+ if (maybe < 0 || (assoc != 0 ? (assoc < 0 ? span.from < index : span.to > index) : order[maybe].level > span.level))
2037
+ maybe = i;
2038
+ }
1995
2039
  }
2040
+ if (maybe < 0)
2041
+ throw new RangeError("Index out of range");
2042
+ return maybe;
1996
2043
  }
1997
- reset(sel) {
1998
- if (this.dirty) {
1999
- this.view.observer.ignore(() => this.view.docView.sync());
2000
- this.dirty = 0 /* Not */;
2001
- this.updateSelection(true);
2044
+ }
2045
+ // Reused array of character types
2046
+ const types = [];
2047
+ function computeOrder(line, direction) {
2048
+ let len = line.length, outerType = direction == LTR ? 1 /* L */ : 2 /* R */, oppositeType = direction == LTR ? 2 /* R */ : 1 /* L */;
2049
+ if (!line || outerType == 1 /* L */ && !BidiRE.test(line))
2050
+ return trivialOrder(len);
2051
+ // W1. Examine each non-spacing mark (NSM) in the level run, and
2052
+ // change the type of the NSM to the type of the previous
2053
+ // character. If the NSM is at the start of the level run, it will
2054
+ // get the type of sor.
2055
+ // W2. Search backwards from each instance of a European number
2056
+ // until the first strong type (R, L, AL, or sor) is found. If an
2057
+ // AL is found, change the type of the European number to Arabic
2058
+ // number.
2059
+ // W3. Change all ALs to R.
2060
+ // (Left after this: L, R, EN, AN, ET, CS, NI)
2061
+ for (let i = 0, prev = outerType, prevStrong = outerType; i < len; i++) {
2062
+ let type = charType(line.charCodeAt(i));
2063
+ if (type == 512 /* NSM */)
2064
+ type = prev;
2065
+ else if (type == 8 /* EN */ && prevStrong == 4 /* AL */)
2066
+ type = 16 /* AN */;
2067
+ types[i] = type == 4 /* AL */ ? 2 /* R */ : type;
2068
+ if (type & 7 /* Strong */)
2069
+ prevStrong = type;
2070
+ prev = type;
2071
+ }
2072
+ // W5. A sequence of European terminators adjacent to European
2073
+ // numbers changes to all European numbers.
2074
+ // W6. Otherwise, separators and terminators change to Other
2075
+ // Neutral.
2076
+ // W7. Search backwards from each instance of a European number
2077
+ // until the first strong type (R, L, or sor) is found. If an L is
2078
+ // found, then change the type of the European number to L.
2079
+ // (Left after this: L, R, EN+AN, NI)
2080
+ for (let i = 0, prev = outerType, prevStrong = outerType; i < len; i++) {
2081
+ let type = types[i];
2082
+ if (type == 128 /* CS */) {
2083
+ if (i < len - 1 && prev == types[i + 1] && (prev & 24 /* Num */))
2084
+ type = types[i] = prev;
2085
+ else
2086
+ types[i] = 256 /* NI */;
2002
2087
  }
2003
- else {
2004
- this.updateSelection();
2088
+ else if (type == 64 /* ET */) {
2089
+ let end = i + 1;
2090
+ while (end < len && types[end] == 64 /* ET */)
2091
+ end++;
2092
+ let replace = (i && prev == 8 /* EN */) || (end < len && types[end] == 8 /* EN */) ? (prevStrong == 1 /* L */ ? 1 /* L */ : 8 /* EN */) : 256 /* NI */;
2093
+ for (let j = i; j < end; j++)
2094
+ types[j] = replace;
2095
+ i = end - 1;
2005
2096
  }
2006
- }
2007
- // Used both by update and checkLayout do perform the actual DOM
2008
- // update
2009
- updateInner(changes, deco, oldLength) {
2010
- this.view.viewState.mustMeasureContent = true;
2011
- this.updateChildren(changes, deco, oldLength);
2012
- let { observer } = this.view;
2013
- observer.ignore(() => {
2014
- // Lock the height during redrawing, since Chrome sometimes
2015
- // messes with the scroll position during DOM mutation (though
2016
- // no relayout is triggered and I cannot imagine how it can
2017
- // recompute the scroll position without a layout)
2018
- this.dom.style.height = this.view.viewState.contentHeight + "px";
2019
- this.dom.style.minWidth = this.minWidth ? this.minWidth + "px" : "";
2020
- // Chrome will sometimes, when DOM mutations occur directly
2021
- // around the selection, get confused and report a different
2022
- // selection from the one it displays (issue #218). This tries
2023
- // to detect that situation.
2024
- let track = browser.chrome || browser.ios ? { node: observer.selectionRange.focusNode, written: false } : undefined;
2025
- this.sync(track);
2026
- this.dirty = 0 /* Not */;
2027
- if (track && (track.written || observer.selectionRange.focusNode != track.node))
2028
- this.forceSelection = true;
2029
- this.dom.style.height = "";
2030
- });
2031
- let gaps = [];
2032
- if (this.view.viewport.from || this.view.viewport.to < this.view.state.doc.length)
2033
- for (let child of this.children)
2034
- if (child instanceof BlockWidgetView && child.widget instanceof BlockGapWidget)
2035
- gaps.push(child.dom);
2036
- observer.updateGaps(gaps);
2037
- }
2038
- updateChildren(changes, deco, oldLength) {
2039
- let cursor = this.childCursor(oldLength);
2040
- for (let i = changes.length - 1;; i--) {
2041
- let next = i >= 0 ? changes[i] : null;
2042
- if (!next)
2043
- break;
2044
- let { fromA, toA, fromB, toB } = next;
2045
- let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, deco);
2046
- let { i: toI, off: toOff } = cursor.findPos(toA, 1);
2047
- let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
2048
- this.replaceRange(fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
2097
+ else if (type == 8 /* EN */ && prevStrong == 1 /* L */) {
2098
+ types[i] = 1 /* L */;
2049
2099
  }
2100
+ prev = type;
2101
+ if (type & 7 /* Strong */)
2102
+ prevStrong = type;
2050
2103
  }
2051
- replaceRange(fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd) {
2052
- let before = this.children[fromI], last = content.length ? content[content.length - 1] : null;
2053
- let breakAtEnd = last ? last.breakAfter : breakAtStart;
2054
- // Change within a single line
2055
- if (fromI == toI && !breakAtStart && !breakAtEnd && content.length < 2 &&
2056
- before.merge(fromOff, toOff, content.length ? last : null, fromOff == 0, openStart, openEnd))
2057
- return;
2058
- let after = this.children[toI];
2059
- // Make sure the end of the line after the update is preserved in `after`
2060
- if (toOff < after.length) {
2061
- // If we're splitting a line, separate part of the start line to
2062
- // avoid that being mangled when updating the start line.
2063
- if (fromI == toI) {
2064
- after = after.split(toOff);
2065
- toOff = 0;
2104
+ // N0. Process bracket pairs in an isolating run sequence
2105
+ // sequentially in the logical order of the text positions of the
2106
+ // opening paired brackets using the logic given below. Within this
2107
+ // scope, bidirectional types EN and AN are treated as R.
2108
+ for (let i = 0, sI = 0, context = 0, ch, br, type; i < len; i++) {
2109
+ // Keeps [startIndex, type, strongSeen] triples for each open
2110
+ // bracket on BracketStack.
2111
+ if (br = Brackets[ch = line.charCodeAt(i)]) {
2112
+ if (br < 0) { // Closing bracket
2113
+ for (let sJ = sI - 3; sJ >= 0; sJ -= 3) {
2114
+ if (BracketStack[sJ + 1] == -br) {
2115
+ let flags = BracketStack[sJ + 2];
2116
+ let type = (flags & 2 /* EmbedInside */) ? outerType :
2117
+ !(flags & 4 /* OppositeInside */) ? 0 :
2118
+ (flags & 1 /* OppositeBefore */) ? oppositeType : outerType;
2119
+ if (type)
2120
+ types[i] = types[BracketStack[sJ]] = type;
2121
+ sI = sJ;
2122
+ break;
2123
+ }
2124
+ }
2066
2125
  }
2067
- // If the element after the replacement should be merged with
2068
- // the last replacing element, update `content`
2069
- if (!breakAtEnd && last && after.merge(0, toOff, last, true, 0, openEnd)) {
2070
- content[content.length - 1] = after;
2126
+ else if (BracketStack.length == 189 /* MaxDepth */) {
2127
+ break;
2071
2128
  }
2072
2129
  else {
2073
- // Remove the start of the after element, if necessary, and
2074
- // add it to `content`.
2075
- if (toOff)
2076
- after.merge(0, toOff, null, false, 0, openEnd);
2077
- content.push(after);
2130
+ BracketStack[sI++] = i;
2131
+ BracketStack[sI++] = ch;
2132
+ BracketStack[sI++] = context;
2078
2133
  }
2079
2134
  }
2080
- else if (after.breakAfter) {
2081
- // The element at `toI` is entirely covered by this range.
2082
- // Preserve its line break, if any.
2083
- if (last)
2084
- last.breakAfter = 1;
2085
- else
2086
- breakAtStart = 1;
2135
+ else if ((type = types[i]) == 2 /* R */ || type == 1 /* L */) {
2136
+ let embed = type == outerType;
2137
+ context = embed ? 0 : 1 /* OppositeBefore */;
2138
+ for (let sJ = sI - 3; sJ >= 0; sJ -= 3) {
2139
+ let cur = BracketStack[sJ + 2];
2140
+ if (cur & 2 /* EmbedInside */)
2141
+ break;
2142
+ if (embed) {
2143
+ BracketStack[sJ + 2] |= 2 /* EmbedInside */;
2144
+ }
2145
+ else {
2146
+ if (cur & 4 /* OppositeInside */)
2147
+ break;
2148
+ BracketStack[sJ + 2] |= 4 /* OppositeInside */;
2149
+ }
2150
+ }
2087
2151
  }
2088
- // Since we've handled the next element from the current elements
2089
- // now, make sure `toI` points after that.
2090
- toI++;
2091
- before.breakAfter = breakAtStart;
2092
- if (fromOff > 0) {
2093
- if (!breakAtStart && content.length && before.merge(fromOff, before.length, content[0], false, openStart, 0)) {
2094
- before.breakAfter = content.shift().breakAfter;
2152
+ }
2153
+ // N1. A sequence of neutrals takes the direction of the
2154
+ // surrounding strong text if the text on both sides has the same
2155
+ // direction. European and Arabic numbers act as if they were R in
2156
+ // terms of their influence on neutrals. Start-of-level-run (sor)
2157
+ // and end-of-level-run (eor) are used at level run boundaries.
2158
+ // N2. Any remaining neutrals take the embedding direction.
2159
+ // (Left after this: L, R, EN+AN)
2160
+ for (let i = 0; i < len; i++) {
2161
+ if (types[i] == 256 /* NI */) {
2162
+ let end = i + 1;
2163
+ while (end < len && types[end] == 256 /* NI */)
2164
+ end++;
2165
+ let beforeL = (i ? types[i - 1] : outerType) == 1 /* L */;
2166
+ let afterL = (end < len ? types[end] : outerType) == 1 /* L */;
2167
+ let replace = beforeL == afterL ? (beforeL ? 1 /* L */ : 2 /* R */) : outerType;
2168
+ for (let j = i; j < end; j++)
2169
+ types[j] = replace;
2170
+ i = end - 1;
2171
+ }
2172
+ }
2173
+ // Here we depart from the documented algorithm, in order to avoid
2174
+ // building up an actual levels array. Since there are only three
2175
+ // levels (0, 1, 2) in an implementation that doesn't take
2176
+ // explicit embedding into account, we can build up the order on
2177
+ // the fly, without following the level-based algorithm.
2178
+ let order = [];
2179
+ if (outerType == 1 /* L */) {
2180
+ for (let i = 0; i < len;) {
2181
+ let start = i, rtl = types[i++] != 1 /* L */;
2182
+ while (i < len && rtl == (types[i] != 1 /* L */))
2183
+ i++;
2184
+ if (rtl) {
2185
+ for (let j = i; j > start;) {
2186
+ let end = j, l = types[--j] != 2 /* R */;
2187
+ while (j > start && l == (types[j - 1] != 2 /* R */))
2188
+ j--;
2189
+ order.push(new BidiSpan(j, end, l ? 2 : 1));
2190
+ }
2095
2191
  }
2096
- else if (fromOff < before.length || before.children.length && before.children[before.children.length - 1].length == 0) {
2097
- before.merge(fromOff, before.length, null, false, openStart, 0);
2192
+ else {
2193
+ order.push(new BidiSpan(start, i, 0));
2098
2194
  }
2099
- fromI++;
2100
- }
2101
- // Try to merge widgets on the boundaries of the replacement
2102
- while (fromI < toI && content.length) {
2103
- if (this.children[toI - 1].match(content[content.length - 1]))
2104
- toI--, content.pop();
2105
- else if (this.children[fromI].match(content[0]))
2106
- fromI++, content.shift();
2107
- else
2108
- break;
2109
2195
  }
2110
- if (fromI < toI || content.length)
2111
- this.replaceChildren(fromI, toI, content);
2112
2196
  }
2113
- // Sync the DOM selection to this.state.selection
2114
- updateSelection(mustRead = false, fromPointer = false) {
2115
- if (mustRead)
2197
+ else {
2198
+ for (let i = 0; i < len;) {
2199
+ let start = i, rtl = types[i++] == 2 /* R */;
2200
+ while (i < len && rtl == (types[i] == 2 /* R */))
2201
+ i++;
2202
+ order.push(new BidiSpan(start, i, rtl ? 1 : 2));
2203
+ }
2204
+ }
2205
+ return order;
2206
+ }
2207
+ function trivialOrder(length) {
2208
+ return [new BidiSpan(0, length, 0)];
2209
+ }
2210
+ let movedOver = "";
2211
+ function moveVisually(line, order, dir, start, forward) {
2212
+ var _a;
2213
+ let startIndex = start.head - line.from, spanI = -1;
2214
+ if (startIndex == 0) {
2215
+ if (!forward || !line.length)
2216
+ return null;
2217
+ if (order[0].level != dir) {
2218
+ startIndex = order[0].side(false, dir);
2219
+ spanI = 0;
2220
+ }
2221
+ }
2222
+ else if (startIndex == line.length) {
2223
+ if (forward)
2224
+ return null;
2225
+ let last = order[order.length - 1];
2226
+ if (last.level != dir) {
2227
+ startIndex = last.side(true, dir);
2228
+ spanI = order.length - 1;
2229
+ }
2230
+ }
2231
+ if (spanI < 0)
2232
+ spanI = BidiSpan.find(order, startIndex, (_a = start.bidiLevel) !== null && _a !== void 0 ? _a : -1, start.assoc);
2233
+ let span = order[spanI];
2234
+ // End of span. (But not end of line--that was checked for above.)
2235
+ if (startIndex == span.side(forward, dir)) {
2236
+ span = order[spanI += forward ? 1 : -1];
2237
+ startIndex = span.side(!forward, dir);
2238
+ }
2239
+ let indexForward = forward == (span.dir == dir);
2240
+ let nextIndex = findClusterBreak(line.text, startIndex, indexForward);
2241
+ movedOver = line.text.slice(Math.min(startIndex, nextIndex), Math.max(startIndex, nextIndex));
2242
+ if (nextIndex != span.side(forward, dir))
2243
+ return EditorSelection.cursor(nextIndex + line.from, indexForward ? -1 : 1, span.level);
2244
+ let nextSpan = spanI == (forward ? order.length - 1 : 0) ? null : order[spanI + (forward ? 1 : -1)];
2245
+ if (!nextSpan && span.level != dir)
2246
+ return EditorSelection.cursor(forward ? line.to : line.from, forward ? -1 : 1, dir);
2247
+ if (nextSpan && nextSpan.level < span.level)
2248
+ return EditorSelection.cursor(nextSpan.side(!forward, dir) + line.from, forward ? 1 : -1, nextSpan.level);
2249
+ return EditorSelection.cursor(nextIndex + line.from, forward ? -1 : 1, span.level);
2250
+ }
2251
+
2252
+ class DocView extends ContentView {
2253
+ constructor(view) {
2254
+ super();
2255
+ this.view = view;
2256
+ this.compositionDeco = Decoration.none;
2257
+ this.decorations = [];
2258
+ // Track a minimum width for the editor. When measuring sizes in
2259
+ // measureVisibleLineHeights, this is updated to point at the width
2260
+ // of a given element and its extent in the document. When a change
2261
+ // happens in that range, these are reset. That way, once we've seen
2262
+ // a line/element of a given length, we keep the editor wide enough
2263
+ // to fit at least that element, until it is changed, at which point
2264
+ // we forget it again.
2265
+ this.minWidth = 0;
2266
+ this.minWidthFrom = 0;
2267
+ this.minWidthTo = 0;
2268
+ // Track whether the DOM selection was set in a lossy way, so that
2269
+ // we don't mess it up when reading it back it
2270
+ this.impreciseAnchor = null;
2271
+ this.impreciseHead = null;
2272
+ this.forceSelection = false;
2273
+ // Used by the resize observer to ignore resizes that we caused
2274
+ // ourselves
2275
+ this.lastUpdate = Date.now();
2276
+ this.setDOM(view.contentDOM);
2277
+ this.children = [new LineView];
2278
+ this.children[0].setParent(this);
2279
+ this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], this.updateDeco(), 0);
2280
+ }
2281
+ get root() { return this.view.root; }
2282
+ get editorView() { return this.view; }
2283
+ get length() { return this.view.state.doc.length; }
2284
+ // Update the document view to a given state. scrollIntoView can be
2285
+ // used as a hint to compute a new viewport that includes that
2286
+ // position, if we know the editor is going to scroll that position
2287
+ // into view.
2288
+ update(update) {
2289
+ let changedRanges = update.changedRanges;
2290
+ if (this.minWidth > 0 && changedRanges.length) {
2291
+ if (!changedRanges.every(({ fromA, toA }) => toA < this.minWidthFrom || fromA > this.minWidthTo)) {
2292
+ this.minWidth = this.minWidthFrom = this.minWidthTo = 0;
2293
+ }
2294
+ else {
2295
+ this.minWidthFrom = update.changes.mapPos(this.minWidthFrom, 1);
2296
+ this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
2297
+ }
2298
+ }
2299
+ if (this.view.inputState.composing < 0)
2300
+ this.compositionDeco = Decoration.none;
2301
+ else if (update.transactions.length)
2302
+ this.compositionDeco = computeCompositionDeco(this.view, update.changes);
2303
+ // When the DOM nodes around the selection are moved to another
2304
+ // parent, Chrome sometimes reports a different selection through
2305
+ // getSelection than the one that it actually shows to the user.
2306
+ // This forces a selection update when lines are joined to work
2307
+ // around that. Issue #54
2308
+ if ((browser.ie || browser.chrome) && !this.compositionDeco.size && update &&
2309
+ update.state.doc.lines != update.startState.doc.lines)
2310
+ this.forceSelection = true;
2311
+ let prevDeco = this.decorations, deco = this.updateDeco();
2312
+ let decoDiff = findChangedDeco(prevDeco, deco, update.changes);
2313
+ changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
2314
+ if (this.dirty == 0 /* Not */ && changedRanges.length == 0) {
2315
+ return false;
2316
+ }
2317
+ else {
2318
+ this.updateInner(changedRanges, deco, update.startState.doc.length);
2319
+ if (update.transactions.length)
2320
+ this.lastUpdate = Date.now();
2321
+ return true;
2322
+ }
2323
+ }
2324
+ reset(sel) {
2325
+ if (this.dirty) {
2326
+ this.view.observer.ignore(() => this.view.docView.sync());
2327
+ this.dirty = 0 /* Not */;
2328
+ this.updateSelection(true);
2329
+ }
2330
+ else {
2331
+ this.updateSelection();
2332
+ }
2333
+ }
2334
+ // Used by update and the constructor do perform the actual DOM
2335
+ // update
2336
+ updateInner(changes, deco, oldLength) {
2337
+ this.view.viewState.mustMeasureContent = true;
2338
+ this.updateChildren(changes, deco, oldLength);
2339
+ let { observer } = this.view;
2340
+ observer.ignore(() => {
2341
+ // Lock the height during redrawing, since Chrome sometimes
2342
+ // messes with the scroll position during DOM mutation (though
2343
+ // no relayout is triggered and I cannot imagine how it can
2344
+ // recompute the scroll position without a layout)
2345
+ this.dom.style.height = this.view.viewState.contentHeight + "px";
2346
+ this.dom.style.minWidth = this.minWidth ? this.minWidth + "px" : "";
2347
+ // Chrome will sometimes, when DOM mutations occur directly
2348
+ // around the selection, get confused and report a different
2349
+ // selection from the one it displays (issue #218). This tries
2350
+ // to detect that situation.
2351
+ let track = browser.chrome || browser.ios ? { node: observer.selectionRange.focusNode, written: false } : undefined;
2352
+ this.sync(track);
2353
+ this.dirty = 0 /* Not */;
2354
+ if (track && (track.written || observer.selectionRange.focusNode != track.node))
2355
+ this.forceSelection = true;
2356
+ this.dom.style.height = "";
2357
+ });
2358
+ let gaps = [];
2359
+ if (this.view.viewport.from || this.view.viewport.to < this.view.state.doc.length)
2360
+ for (let child of this.children)
2361
+ if (child instanceof BlockWidgetView && child.widget instanceof BlockGapWidget)
2362
+ gaps.push(child.dom);
2363
+ observer.updateGaps(gaps);
2364
+ }
2365
+ updateChildren(changes, deco, oldLength) {
2366
+ let cursor = this.childCursor(oldLength);
2367
+ for (let i = changes.length - 1;; i--) {
2368
+ let next = i >= 0 ? changes[i] : null;
2369
+ if (!next)
2370
+ break;
2371
+ let { fromA, toA, fromB, toB } = next;
2372
+ let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, deco);
2373
+ let { i: toI, off: toOff } = cursor.findPos(toA, 1);
2374
+ let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
2375
+ replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
2376
+ }
2377
+ }
2378
+ // Sync the DOM selection to this.state.selection
2379
+ updateSelection(mustRead = false, fromPointer = false) {
2380
+ if (mustRead)
2116
2381
  this.view.observer.readSelectionRange();
2117
2382
  if (!(fromPointer || this.mayControlSelection()) ||
2118
2383
  browser.ios && this.view.inputState.rapidCompositionStart)
@@ -2246,18 +2511,30 @@ class DocView extends ContentView {
2246
2511
  }
2247
2512
  measureVisibleLineHeights() {
2248
2513
  let result = [], { from, to } = this.view.viewState.viewport;
2249
- let minWidth = Math.max(this.view.scrollDOM.clientWidth, this.minWidth) + 1;
2514
+ let contentWidth = this.view.contentDOM.clientWidth;
2515
+ let isWider = contentWidth > Math.max(this.view.scrollDOM.clientWidth, this.minWidth) + 1;
2516
+ let widest = -1;
2250
2517
  for (let pos = 0, i = 0; i < this.children.length; i++) {
2251
2518
  let child = this.children[i], end = pos + child.length;
2252
2519
  if (end > to)
2253
2520
  break;
2254
2521
  if (pos >= from) {
2255
- result.push(child.dom.getBoundingClientRect().height);
2256
- let width = child.dom.scrollWidth;
2257
- if (width > minWidth) {
2258
- this.minWidth = minWidth = width;
2259
- this.minWidthFrom = pos;
2260
- this.minWidthTo = end;
2522
+ let childRect = child.dom.getBoundingClientRect();
2523
+ result.push(childRect.height);
2524
+ if (isWider) {
2525
+ let last = child.dom.lastChild;
2526
+ let rects = last ? clientRectsFor(last) : [];
2527
+ if (rects.length) {
2528
+ let rect = rects[rects.length - 1];
2529
+ let width = this.view.textDirection == Direction.LTR ? rect.right - childRect.left
2530
+ : childRect.right - rect.left;
2531
+ if (width > widest) {
2532
+ widest = width;
2533
+ this.minWidth = contentWidth;
2534
+ this.minWidthFrom = pos;
2535
+ this.minWidthTo = end;
2536
+ }
2537
+ }
2261
2538
  }
2262
2539
  }
2263
2540
  pos = end + child.breakAfter;
@@ -2287,489 +2564,183 @@ class DocView extends ContentView {
2287
2564
  }
2288
2565
  childCursor(pos = this.length) {
2289
2566
  // Move back to start of last element when possible, so that
2290
- // `ChildCursor.findPos` doesn't have to deal with the edge case
2291
- // of being after the last element.
2292
- let i = this.children.length;
2293
- if (i)
2294
- pos -= this.children[--i].length;
2295
- return new ChildCursor(this.children, pos, i);
2296
- }
2297
- computeBlockGapDeco() {
2298
- let deco = [], vs = this.view.viewState;
2299
- for (let pos = 0, i = 0;; i++) {
2300
- let next = i == vs.viewports.length ? null : vs.viewports[i];
2301
- let end = next ? next.from - 1 : this.length;
2302
- if (end > pos) {
2303
- let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
2304
- deco.push(Decoration.replace({ widget: new BlockGapWidget(height), block: true, inclusive: true }).range(pos, end));
2305
- }
2306
- if (!next)
2307
- break;
2308
- pos = next.to + 1;
2309
- }
2310
- return Decoration.set(deco);
2311
- }
2312
- updateDeco() {
2313
- return this.decorations = [
2314
- ...this.view.pluginField(PluginField.decorations),
2315
- ...this.view.state.facet(decorations),
2316
- this.compositionDeco,
2317
- this.computeBlockGapDeco(),
2318
- this.view.viewState.lineGapDeco
2319
- ];
2320
- }
2321
- scrollIntoView({ range, center }) {
2322
- let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
2323
- if (!rect)
2324
- return;
2325
- if (!range.empty && (other = this.coordsAt(range.anchor, range.anchor > range.head ? -1 : 1)))
2326
- rect = { left: Math.min(rect.left, other.left), top: Math.min(rect.top, other.top),
2327
- right: Math.max(rect.right, other.right), bottom: Math.max(rect.bottom, other.bottom) };
2328
- let mLeft = 0, mRight = 0, mTop = 0, mBottom = 0;
2329
- for (let margins of this.view.pluginField(PluginField.scrollMargins))
2330
- if (margins) {
2331
- let { left, right, top, bottom } = margins;
2332
- if (left != null)
2333
- mLeft = Math.max(mLeft, left);
2334
- if (right != null)
2335
- mRight = Math.max(mRight, right);
2336
- if (top != null)
2337
- mTop = Math.max(mTop, top);
2338
- if (bottom != null)
2339
- mBottom = Math.max(mBottom, bottom);
2340
- }
2341
- scrollRectIntoView(this.view.scrollDOM, {
2342
- left: rect.left - mLeft, top: rect.top - mTop,
2343
- right: rect.right + mRight, bottom: rect.bottom + mBottom
2344
- }, range.head < range.anchor ? -1 : 1, center);
2345
- }
2346
- }
2347
- function betweenUneditable(pos) {
2348
- return pos.node.nodeType == 1 && pos.node.firstChild &&
2349
- (pos.offset == 0 || pos.node.childNodes[pos.offset - 1].contentEditable == "false") &&
2350
- (pos.offset == pos.node.childNodes.length || pos.node.childNodes[pos.offset].contentEditable == "false");
2351
- }
2352
- class BlockGapWidget extends WidgetType {
2353
- constructor(height) {
2354
- super();
2355
- this.height = height;
2356
- }
2357
- toDOM() {
2358
- let elt = document.createElement("div");
2359
- this.updateDOM(elt);
2360
- return elt;
2361
- }
2362
- eq(other) { return other.height == this.height; }
2363
- updateDOM(elt) {
2364
- elt.style.height = this.height + "px";
2365
- return true;
2366
- }
2367
- get estimatedHeight() { return this.height; }
2368
- }
2369
- function computeCompositionDeco(view, changes) {
2370
- let sel = view.observer.selectionRange;
2371
- let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
2372
- if (!textNode)
2373
- return Decoration.none;
2374
- let cView = view.docView.nearest(textNode);
2375
- let from, to, topNode = textNode;
2376
- if (cView instanceof InlineView) {
2377
- while (cView.parent instanceof InlineView)
2378
- cView = cView.parent;
2379
- from = cView.posAtStart;
2380
- to = from + cView.length;
2381
- topNode = cView.dom;
2382
- }
2383
- else if (cView instanceof LineView) {
2384
- while (topNode.parentNode != cView.dom)
2385
- topNode = topNode.parentNode;
2386
- let prev = topNode.previousSibling;
2387
- while (prev && !ContentView.get(prev))
2388
- prev = prev.previousSibling;
2389
- from = to = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
2390
- }
2391
- else {
2392
- return Decoration.none;
2393
- }
2394
- let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
2395
- let text = textNode.nodeValue, { state } = view;
2396
- if (newTo - newFrom < text.length) {
2397
- if (state.sliceDoc(newFrom, Math.min(state.doc.length, newFrom + text.length)) == text)
2398
- newTo = newFrom + text.length;
2399
- else if (state.sliceDoc(Math.max(0, newTo - text.length), newTo) == text)
2400
- newFrom = newTo - text.length;
2401
- else
2402
- return Decoration.none;
2403
- }
2404
- else if (state.sliceDoc(newFrom, newTo) != text) {
2405
- return Decoration.none;
2406
- }
2407
- return Decoration.set(Decoration.replace({ widget: new CompositionWidget(topNode, textNode) }).range(newFrom, newTo));
2408
- }
2409
- class CompositionWidget extends WidgetType {
2410
- constructor(top, text) {
2411
- super();
2412
- this.top = top;
2413
- this.text = text;
2414
- }
2415
- eq(other) { return this.top == other.top && this.text == other.text; }
2416
- toDOM() { return this.top; }
2417
- ignoreEvent() { return false; }
2418
- get customView() { return CompositionView; }
2419
- }
2420
- function nearbyTextNode(node, offset, side) {
2421
- for (;;) {
2422
- if (node.nodeType == 3)
2423
- return node;
2424
- if (node.nodeType == 1 && offset > 0 && side <= 0) {
2425
- node = node.childNodes[offset - 1];
2426
- offset = maxOffset(node);
2427
- }
2428
- else if (node.nodeType == 1 && offset < node.childNodes.length && side >= 0) {
2429
- node = node.childNodes[offset];
2430
- offset = 0;
2431
- }
2432
- else {
2433
- return null;
2434
- }
2435
- }
2436
- }
2437
- function nextToUneditable(node, offset) {
2438
- if (node.nodeType != 1)
2439
- return 0;
2440
- return (offset && node.childNodes[offset - 1].contentEditable == "false" ? 1 /* Before */ : 0) |
2441
- (offset < node.childNodes.length && node.childNodes[offset].contentEditable == "false" ? 2 /* After */ : 0);
2442
- }
2443
- class DecorationComparator$1 {
2444
- constructor() {
2445
- this.changes = [];
2446
- }
2447
- compareRange(from, to) { addRange(from, to, this.changes); }
2448
- comparePoint(from, to) { addRange(from, to, this.changes); }
2449
- }
2450
- function findChangedDeco(a, b, diff) {
2451
- let comp = new DecorationComparator$1;
2452
- RangeSet.compare(a, b, diff, comp);
2453
- return comp.changes;
2454
- }
2455
- function inUneditable(node, inside) {
2456
- for (let cur = node; cur && cur != inside; cur = cur.assignedSlot || cur.parentNode) {
2457
- if (cur.nodeType == 1 && cur.contentEditable == 'false') {
2458
- return true;
2459
- }
2460
- }
2461
- return false;
2462
- }
2463
-
2464
- /**
2465
- Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
2466
- */
2467
- var Direction = /*@__PURE__*/(function (Direction) {
2468
- // (These are chosen to match the base levels, in bidi algorithm
2469
- // terms, of spans in that direction.)
2470
- /**
2471
- Left-to-right.
2472
- */
2473
- Direction[Direction["LTR"] = 0] = "LTR";
2474
- /**
2475
- Right-to-left.
2476
- */
2477
- Direction[Direction["RTL"] = 1] = "RTL";
2478
- return Direction})(Direction || (Direction = {}));
2479
- const LTR = Direction.LTR, RTL = Direction.RTL;
2480
- // Decode a string with each type encoded as log2(type)
2481
- function dec(str) {
2482
- let result = [];
2483
- for (let i = 0; i < str.length; i++)
2484
- result.push(1 << +str[i]);
2485
- return result;
2486
- }
2487
- // Character types for codepoints 0 to 0xf8
2488
- const LowTypes = /*@__PURE__*/dec("88888888888888888888888888888888888666888888787833333333337888888000000000000000000000000008888880000000000000000000000000088888888888888888888888888888888888887866668888088888663380888308888800000000000000000000000800000000000000000000000000000008");
2489
- // Character types for codepoints 0x600 to 0x6f9
2490
- const ArabicTypes = /*@__PURE__*/dec("4444448826627288999999999992222222222222222222222222222222222222222222222229999999999999999999994444444444644222822222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222999999949999999229989999223333333333");
2491
- const Brackets = /*@__PURE__*/Object.create(null), BracketStack = [];
2492
- // There's a lot more in
2493
- // https://www.unicode.org/Public/UCD/latest/ucd/BidiBrackets.txt,
2494
- // which are left out to keep code size down.
2495
- for (let p of ["()", "[]", "{}"]) {
2496
- let l = /*@__PURE__*/p.charCodeAt(0), r = /*@__PURE__*/p.charCodeAt(1);
2497
- Brackets[l] = r;
2498
- Brackets[r] = -l;
2499
- }
2500
- function charType(ch) {
2501
- return ch <= 0xf7 ? LowTypes[ch] :
2502
- 0x590 <= ch && ch <= 0x5f4 ? 2 /* R */ :
2503
- 0x600 <= ch && ch <= 0x6f9 ? ArabicTypes[ch - 0x600] :
2504
- 0x6ee <= ch && ch <= 0x8ac ? 4 /* AL */ :
2505
- 0x2000 <= ch && ch <= 0x200b ? 256 /* NI */ :
2506
- ch == 0x200c ? 256 /* NI */ : 1 /* L */;
2507
- }
2508
- const BidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
2509
- /**
2510
- Represents a contiguous range of text that has a single direction
2511
- (as in left-to-right or right-to-left).
2512
- */
2513
- class BidiSpan {
2514
- /**
2515
- @internal
2516
- */
2517
- constructor(
2518
- /**
2519
- The start of the span (relative to the start of the line).
2520
- */
2521
- from,
2522
- /**
2523
- The end of the span.
2524
- */
2525
- to,
2526
- /**
2527
- The ["bidi
2528
- level"](https://unicode.org/reports/tr9/#Basic_Display_Algorithm)
2529
- of the span (in this context, 0 means
2530
- left-to-right, 1 means right-to-left, 2 means left-to-right
2531
- number inside right-to-left text).
2532
- */
2533
- level) {
2534
- this.from = from;
2535
- this.to = to;
2536
- this.level = level;
2537
- }
2538
- /**
2539
- The direction of this span.
2540
- */
2541
- get dir() { return this.level % 2 ? RTL : LTR; }
2542
- /**
2543
- @internal
2544
- */
2545
- side(end, dir) { return (this.dir == dir) == end ? this.to : this.from; }
2546
- /**
2547
- @internal
2548
- */
2549
- static find(order, index, level, assoc) {
2550
- let maybe = -1;
2551
- for (let i = 0; i < order.length; i++) {
2552
- let span = order[i];
2553
- if (span.from <= index && span.to >= index) {
2554
- if (span.level == level)
2555
- return i;
2556
- // When multiple spans match, if assoc != 0, take the one that
2557
- // covers that side, otherwise take the one with the minimum
2558
- // level.
2559
- if (maybe < 0 || (assoc != 0 ? (assoc < 0 ? span.from < index : span.to > index) : order[maybe].level > span.level))
2560
- maybe = i;
2561
- }
2562
- }
2563
- if (maybe < 0)
2564
- throw new RangeError("Index out of range");
2565
- return maybe;
2566
- }
2567
- }
2568
- // Reused array of character types
2569
- const types = [];
2570
- function computeOrder(line, direction) {
2571
- let len = line.length, outerType = direction == LTR ? 1 /* L */ : 2 /* R */, oppositeType = direction == LTR ? 2 /* R */ : 1 /* L */;
2572
- if (!line || outerType == 1 /* L */ && !BidiRE.test(line))
2573
- return trivialOrder(len);
2574
- // W1. Examine each non-spacing mark (NSM) in the level run, and
2575
- // change the type of the NSM to the type of the previous
2576
- // character. If the NSM is at the start of the level run, it will
2577
- // get the type of sor.
2578
- // W2. Search backwards from each instance of a European number
2579
- // until the first strong type (R, L, AL, or sor) is found. If an
2580
- // AL is found, change the type of the European number to Arabic
2581
- // number.
2582
- // W3. Change all ALs to R.
2583
- // (Left after this: L, R, EN, AN, ET, CS, NI)
2584
- for (let i = 0, prev = outerType, prevStrong = outerType; i < len; i++) {
2585
- let type = charType(line.charCodeAt(i));
2586
- if (type == 512 /* NSM */)
2587
- type = prev;
2588
- else if (type == 8 /* EN */ && prevStrong == 4 /* AL */)
2589
- type = 16 /* AN */;
2590
- types[i] = type == 4 /* AL */ ? 2 /* R */ : type;
2591
- if (type & 7 /* Strong */)
2592
- prevStrong = type;
2593
- prev = type;
2594
- }
2595
- // W5. A sequence of European terminators adjacent to European
2596
- // numbers changes to all European numbers.
2597
- // W6. Otherwise, separators and terminators change to Other
2598
- // Neutral.
2599
- // W7. Search backwards from each instance of a European number
2600
- // until the first strong type (R, L, or sor) is found. If an L is
2601
- // found, then change the type of the European number to L.
2602
- // (Left after this: L, R, EN+AN, NI)
2603
- for (let i = 0, prev = outerType, prevStrong = outerType; i < len; i++) {
2604
- let type = types[i];
2605
- if (type == 128 /* CS */) {
2606
- if (i < len - 1 && prev == types[i + 1] && (prev & 24 /* Num */))
2607
- type = types[i] = prev;
2608
- else
2609
- types[i] = 256 /* NI */;
2610
- }
2611
- else if (type == 64 /* ET */) {
2612
- let end = i + 1;
2613
- while (end < len && types[end] == 64 /* ET */)
2614
- end++;
2615
- let replace = (i && prev == 8 /* EN */) || (end < len && types[end] == 8 /* EN */) ? (prevStrong == 1 /* L */ ? 1 /* L */ : 8 /* EN */) : 256 /* NI */;
2616
- for (let j = i; j < end; j++)
2617
- types[j] = replace;
2618
- i = end - 1;
2619
- }
2620
- else if (type == 8 /* EN */ && prevStrong == 1 /* L */) {
2621
- types[i] = 1 /* L */;
2622
- }
2623
- prev = type;
2624
- if (type & 7 /* Strong */)
2625
- prevStrong = type;
2626
- }
2627
- // N0. Process bracket pairs in an isolating run sequence
2628
- // sequentially in the logical order of the text positions of the
2629
- // opening paired brackets using the logic given below. Within this
2630
- // scope, bidirectional types EN and AN are treated as R.
2631
- for (let i = 0, sI = 0, context = 0, ch, br, type; i < len; i++) {
2632
- // Keeps [startIndex, type, strongSeen] triples for each open
2633
- // bracket on BracketStack.
2634
- if (br = Brackets[ch = line.charCodeAt(i)]) {
2635
- if (br < 0) { // Closing bracket
2636
- for (let sJ = sI - 3; sJ >= 0; sJ -= 3) {
2637
- if (BracketStack[sJ + 1] == -br) {
2638
- let flags = BracketStack[sJ + 2];
2639
- let type = (flags & 2 /* EmbedInside */) ? outerType :
2640
- !(flags & 4 /* OppositeInside */) ? 0 :
2641
- (flags & 1 /* OppositeBefore */) ? oppositeType : outerType;
2642
- if (type)
2643
- types[i] = types[BracketStack[sJ]] = type;
2644
- sI = sJ;
2645
- break;
2646
- }
2647
- }
2648
- }
2649
- else if (BracketStack.length == 189 /* MaxDepth */) {
2650
- break;
2651
- }
2652
- else {
2653
- BracketStack[sI++] = i;
2654
- BracketStack[sI++] = ch;
2655
- BracketStack[sI++] = context;
2656
- }
2657
- }
2658
- else if ((type = types[i]) == 2 /* R */ || type == 1 /* L */) {
2659
- let embed = type == outerType;
2660
- context = embed ? 0 : 1 /* OppositeBefore */;
2661
- for (let sJ = sI - 3; sJ >= 0; sJ -= 3) {
2662
- let cur = BracketStack[sJ + 2];
2663
- if (cur & 2 /* EmbedInside */)
2664
- break;
2665
- if (embed) {
2666
- BracketStack[sJ + 2] |= 2 /* EmbedInside */;
2667
- }
2668
- else {
2669
- if (cur & 4 /* OppositeInside */)
2670
- break;
2671
- BracketStack[sJ + 2] |= 4 /* OppositeInside */;
2672
- }
2567
+ // `ChildCursor.findPos` doesn't have to deal with the edge case
2568
+ // of being after the last element.
2569
+ let i = this.children.length;
2570
+ if (i)
2571
+ pos -= this.children[--i].length;
2572
+ return new ChildCursor(this.children, pos, i);
2573
+ }
2574
+ computeBlockGapDeco() {
2575
+ let deco = [], vs = this.view.viewState;
2576
+ for (let pos = 0, i = 0;; i++) {
2577
+ let next = i == vs.viewports.length ? null : vs.viewports[i];
2578
+ let end = next ? next.from - 1 : this.length;
2579
+ if (end > pos) {
2580
+ let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
2581
+ deco.push(Decoration.replace({ widget: new BlockGapWidget(height), block: true, inclusive: true }).range(pos, end));
2673
2582
  }
2583
+ if (!next)
2584
+ break;
2585
+ pos = next.to + 1;
2674
2586
  }
2587
+ return Decoration.set(deco);
2675
2588
  }
2676
- // N1. A sequence of neutrals takes the direction of the
2677
- // surrounding strong text if the text on both sides has the same
2678
- // direction. European and Arabic numbers act as if they were R in
2679
- // terms of their influence on neutrals. Start-of-level-run (sor)
2680
- // and end-of-level-run (eor) are used at level run boundaries.
2681
- // N2. Any remaining neutrals take the embedding direction.
2682
- // (Left after this: L, R, EN+AN)
2683
- for (let i = 0; i < len; i++) {
2684
- if (types[i] == 256 /* NI */) {
2685
- let end = i + 1;
2686
- while (end < len && types[end] == 256 /* NI */)
2687
- end++;
2688
- let beforeL = (i ? types[i - 1] : outerType) == 1 /* L */;
2689
- let afterL = (end < len ? types[end] : outerType) == 1 /* L */;
2690
- let replace = beforeL == afterL ? (beforeL ? 1 /* L */ : 2 /* R */) : outerType;
2691
- for (let j = i; j < end; j++)
2692
- types[j] = replace;
2693
- i = end - 1;
2694
- }
2589
+ updateDeco() {
2590
+ return this.decorations = [
2591
+ ...this.view.pluginField(PluginField.decorations),
2592
+ ...this.view.state.facet(decorations),
2593
+ this.compositionDeco,
2594
+ this.computeBlockGapDeco(),
2595
+ this.view.viewState.lineGapDeco
2596
+ ];
2695
2597
  }
2696
- // Here we depart from the documented algorithm, in order to avoid
2697
- // building up an actual levels array. Since there are only three
2698
- // levels (0, 1, 2) in an implementation that doesn't take
2699
- // explicit embedding into account, we can build up the order on
2700
- // the fly, without following the level-based algorithm.
2701
- let order = [];
2702
- if (outerType == 1 /* L */) {
2703
- for (let i = 0; i < len;) {
2704
- let start = i, rtl = types[i++] != 1 /* L */;
2705
- while (i < len && rtl == (types[i] != 1 /* L */))
2706
- i++;
2707
- if (rtl) {
2708
- for (let j = i; j > start;) {
2709
- let end = j, l = types[--j] != 2 /* R */;
2710
- while (j > start && l == (types[j - 1] != 2 /* R */))
2711
- j--;
2712
- order.push(new BidiSpan(j, end, l ? 2 : 1));
2713
- }
2714
- }
2715
- else {
2716
- order.push(new BidiSpan(start, i, 0));
2598
+ scrollIntoView({ range, center }) {
2599
+ let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
2600
+ if (!rect)
2601
+ return;
2602
+ if (!range.empty && (other = this.coordsAt(range.anchor, range.anchor > range.head ? -1 : 1)))
2603
+ rect = { left: Math.min(rect.left, other.left), top: Math.min(rect.top, other.top),
2604
+ right: Math.max(rect.right, other.right), bottom: Math.max(rect.bottom, other.bottom) };
2605
+ let mLeft = 0, mRight = 0, mTop = 0, mBottom = 0;
2606
+ for (let margins of this.view.pluginField(PluginField.scrollMargins))
2607
+ if (margins) {
2608
+ let { left, right, top, bottom } = margins;
2609
+ if (left != null)
2610
+ mLeft = Math.max(mLeft, left);
2611
+ if (right != null)
2612
+ mRight = Math.max(mRight, right);
2613
+ if (top != null)
2614
+ mTop = Math.max(mTop, top);
2615
+ if (bottom != null)
2616
+ mBottom = Math.max(mBottom, bottom);
2717
2617
  }
2718
- }
2618
+ scrollRectIntoView(this.view.scrollDOM, {
2619
+ left: rect.left - mLeft, top: rect.top - mTop,
2620
+ right: rect.right + mRight, bottom: rect.bottom + mBottom
2621
+ }, range.head < range.anchor ? -1 : 1, center);
2622
+ }
2623
+ }
2624
+ function betweenUneditable(pos) {
2625
+ return pos.node.nodeType == 1 && pos.node.firstChild &&
2626
+ (pos.offset == 0 || pos.node.childNodes[pos.offset - 1].contentEditable == "false") &&
2627
+ (pos.offset == pos.node.childNodes.length || pos.node.childNodes[pos.offset].contentEditable == "false");
2628
+ }
2629
+ class BlockGapWidget extends WidgetType {
2630
+ constructor(height) {
2631
+ super();
2632
+ this.height = height;
2633
+ }
2634
+ toDOM() {
2635
+ let elt = document.createElement("div");
2636
+ this.updateDOM(elt);
2637
+ return elt;
2638
+ }
2639
+ eq(other) { return other.height == this.height; }
2640
+ updateDOM(elt) {
2641
+ elt.style.height = this.height + "px";
2642
+ return true;
2643
+ }
2644
+ get estimatedHeight() { return this.height; }
2645
+ }
2646
+ function computeCompositionDeco(view, changes) {
2647
+ let sel = view.observer.selectionRange;
2648
+ let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
2649
+ if (!textNode)
2650
+ return Decoration.none;
2651
+ let cView = view.docView.nearest(textNode);
2652
+ if (!cView)
2653
+ return Decoration.none;
2654
+ let from, to, topNode = textNode;
2655
+ if (cView instanceof LineView) {
2656
+ while (topNode.parentNode != cView.dom)
2657
+ topNode = topNode.parentNode;
2658
+ let prev = topNode.previousSibling;
2659
+ while (prev && !ContentView.get(prev))
2660
+ prev = prev.previousSibling;
2661
+ from = to = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
2719
2662
  }
2720
2663
  else {
2721
- for (let i = 0; i < len;) {
2722
- let start = i, rtl = types[i++] == 2 /* R */;
2723
- while (i < len && rtl == (types[i] == 2 /* R */))
2724
- i++;
2725
- order.push(new BidiSpan(start, i, rtl ? 1 : 2));
2664
+ for (;;) {
2665
+ let { parent } = cView;
2666
+ if (!parent)
2667
+ return Decoration.none;
2668
+ if (parent instanceof LineView)
2669
+ break;
2670
+ cView = parent;
2726
2671
  }
2672
+ from = cView.posAtStart;
2673
+ to = from + cView.length;
2674
+ topNode = cView.dom;
2727
2675
  }
2728
- return order;
2676
+ let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
2677
+ let text = textNode.nodeValue, { state } = view;
2678
+ if (newTo - newFrom < text.length) {
2679
+ if (state.sliceDoc(newFrom, Math.min(state.doc.length, newFrom + text.length)) == text)
2680
+ newTo = newFrom + text.length;
2681
+ else if (state.sliceDoc(Math.max(0, newTo - text.length), newTo) == text)
2682
+ newFrom = newTo - text.length;
2683
+ else
2684
+ return Decoration.none;
2685
+ }
2686
+ else if (state.sliceDoc(newFrom, newTo) != text) {
2687
+ return Decoration.none;
2688
+ }
2689
+ return Decoration.set(Decoration.replace({ widget: new CompositionWidget(topNode, textNode) }).range(newFrom, newTo));
2729
2690
  }
2730
- function trivialOrder(length) {
2731
- return [new BidiSpan(0, length, 0)];
2691
+ class CompositionWidget extends WidgetType {
2692
+ constructor(top, text) {
2693
+ super();
2694
+ this.top = top;
2695
+ this.text = text;
2696
+ }
2697
+ eq(other) { return this.top == other.top && this.text == other.text; }
2698
+ toDOM() { return this.top; }
2699
+ ignoreEvent() { return false; }
2700
+ get customView() { return CompositionView; }
2732
2701
  }
2733
- let movedOver = "";
2734
- function moveVisually(line, order, dir, start, forward) {
2735
- var _a;
2736
- let startIndex = start.head - line.from, spanI = -1;
2737
- if (startIndex == 0) {
2738
- if (!forward || !line.length)
2739
- return null;
2740
- if (order[0].level != dir) {
2741
- startIndex = order[0].side(false, dir);
2742
- spanI = 0;
2702
+ function nearbyTextNode(node, offset, side) {
2703
+ for (;;) {
2704
+ if (node.nodeType == 3)
2705
+ return node;
2706
+ if (node.nodeType == 1 && offset > 0 && side <= 0) {
2707
+ node = node.childNodes[offset - 1];
2708
+ offset = maxOffset(node);
2743
2709
  }
2744
- }
2745
- else if (startIndex == line.length) {
2746
- if (forward)
2710
+ else if (node.nodeType == 1 && offset < node.childNodes.length && side >= 0) {
2711
+ node = node.childNodes[offset];
2712
+ offset = 0;
2713
+ }
2714
+ else {
2747
2715
  return null;
2748
- let last = order[order.length - 1];
2749
- if (last.level != dir) {
2750
- startIndex = last.side(true, dir);
2751
- spanI = order.length - 1;
2752
2716
  }
2753
2717
  }
2754
- if (spanI < 0)
2755
- spanI = BidiSpan.find(order, startIndex, (_a = start.bidiLevel) !== null && _a !== void 0 ? _a : -1, start.assoc);
2756
- let span = order[spanI];
2757
- // End of span. (But not end of line--that was checked for above.)
2758
- if (startIndex == span.side(forward, dir)) {
2759
- span = order[spanI += forward ? 1 : -1];
2760
- startIndex = span.side(!forward, dir);
2718
+ }
2719
+ function nextToUneditable(node, offset) {
2720
+ if (node.nodeType != 1)
2721
+ return 0;
2722
+ return (offset && node.childNodes[offset - 1].contentEditable == "false" ? 1 /* Before */ : 0) |
2723
+ (offset < node.childNodes.length && node.childNodes[offset].contentEditable == "false" ? 2 /* After */ : 0);
2724
+ }
2725
+ class DecorationComparator$1 {
2726
+ constructor() {
2727
+ this.changes = [];
2761
2728
  }
2762
- let indexForward = forward == (span.dir == dir);
2763
- let nextIndex = findClusterBreak(line.text, startIndex, indexForward);
2764
- movedOver = line.text.slice(Math.min(startIndex, nextIndex), Math.max(startIndex, nextIndex));
2765
- if (nextIndex != span.side(forward, dir))
2766
- return EditorSelection.cursor(nextIndex + line.from, indexForward ? -1 : 1, span.level);
2767
- let nextSpan = spanI == (forward ? order.length - 1 : 0) ? null : order[spanI + (forward ? 1 : -1)];
2768
- if (!nextSpan && span.level != dir)
2769
- return EditorSelection.cursor(forward ? line.to : line.from, forward ? -1 : 1, dir);
2770
- if (nextSpan && nextSpan.level < span.level)
2771
- return EditorSelection.cursor(nextSpan.side(!forward, dir) + line.from, forward ? 1 : -1, nextSpan.level);
2772
- return EditorSelection.cursor(nextIndex + line.from, forward ? -1 : 1, span.level);
2729
+ compareRange(from, to) { addRange(from, to, this.changes); }
2730
+ comparePoint(from, to) { addRange(from, to, this.changes); }
2731
+ }
2732
+ function findChangedDeco(a, b, diff) {
2733
+ let comp = new DecorationComparator$1;
2734
+ RangeSet.compare(a, b, diff, comp);
2735
+ return comp.changes;
2736
+ }
2737
+ function inUneditable(node, inside) {
2738
+ for (let cur = node; cur && cur != inside; cur = cur.assignedSlot || cur.parentNode) {
2739
+ if (cur.nodeType == 1 && cur.contentEditable == 'false') {
2740
+ return true;
2741
+ }
2742
+ }
2743
+ return false;
2773
2744
  }
2774
2745
 
2775
2746
  function groupAt(state, pos, bias = 1) {
@@ -2926,7 +2897,7 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
2926
2897
  y = docTop + yOffset;
2927
2898
  let lineStart = block.from;
2928
2899
  // Clip x to the viewport sides
2929
- x = Math.max(content.left + 1, Math.min(content.right - 1, x));
2900
+ x = Math.max(content.left + 1, Math.min(Math.max(content.right, content.left + view.docView.minWidth) - 1, x));
2930
2901
  // If this is outside of the rendered viewport, we can't determine a position
2931
2902
  if (lineStart < view.viewport.from)
2932
2903
  return view.viewport.from == 0 ? 0 : posAtCoordsImprecise(view, content, block, x, y);
@@ -4168,12 +4139,12 @@ class HeightMapBranch extends HeightMap {
4168
4139
  get break() { return this.flags & 1 /* Break */; }
4169
4140
  blockAt(height, doc, top, offset) {
4170
4141
  let mid = top + this.left.height;
4171
- return height < mid || this.right.height == 0 ? this.left.blockAt(height, doc, top, offset)
4142
+ return height < mid ? this.left.blockAt(height, doc, top, offset)
4172
4143
  : this.right.blockAt(height, doc, mid, offset + this.left.length + this.break);
4173
4144
  }
4174
4145
  lineAt(value, type, doc, top, offset) {
4175
4146
  let rightTop = top + this.left.height, rightOffset = offset + this.left.length + this.break;
4176
- let left = type == QueryType.ByHeight ? value < rightTop || this.right.height == 0 : value < rightOffset;
4147
+ let left = type == QueryType.ByHeight ? value < rightTop : value < rightOffset;
4177
4148
  let base = left ? this.left.lineAt(value, type, doc, top, offset)
4178
4149
  : this.right.lineAt(value, type, doc, rightTop, rightOffset);
4179
4150
  if (this.break || (left ? base.to < rightOffset : base.from > rightOffset))
@@ -4314,7 +4285,9 @@ class NodeBuilder {
4314
4285
  }
4315
4286
  point(from, to, deco) {
4316
4287
  if (from < to || deco.heightRelevant) {
4317
- let height = deco.widget ? Math.max(0, deco.widget.estimatedHeight) : 0;
4288
+ let height = deco.widget ? deco.widget.estimatedHeight : 0;
4289
+ if (height < 0)
4290
+ height = this.oracle.lineHeight;
4318
4291
  let len = to - from;
4319
4292
  if (deco.block) {
4320
4293
  this.addBlock(new HeightMapBlock(len, height, deco.type));
@@ -5052,14 +5025,15 @@ const baseTheme = /*@__PURE__*/buildTheme("." + baseThemeID, {
5052
5025
  },
5053
5026
  ".cm-placeholder": {
5054
5027
  color: "#888",
5055
- display: "inline-block"
5028
+ display: "inline-block",
5029
+ verticalAlign: "top",
5056
5030
  },
5057
5031
  ".cm-button": {
5058
5032
  verticalAlign: "middle",
5059
5033
  color: "inherit",
5060
5034
  fontSize: "70%",
5061
5035
  padding: ".2em 1em",
5062
- borderRadius: "3px"
5036
+ borderRadius: "1px"
5063
5037
  },
5064
5038
  "&light .cm-button": {
5065
5039
  backgroundImage: "linear-gradient(#eff1f5, #d9d9df)",
@@ -5381,6 +5355,7 @@ class DOMObserver {
5381
5355
  for (let dom of this.scrollTargets)
5382
5356
  dom.removeEventListener("scroll", this.onScroll);
5383
5357
  window.removeEventListener("scroll", this.onScroll);
5358
+ this.dom.ownerDocument.removeEventListener("selectionchange", this.onSelectionChange);
5384
5359
  clearTimeout(this.parentCheck);
5385
5360
  clearTimeout(this.resizeTimeout);
5386
5361
  }
@@ -5480,11 +5455,22 @@ function applyDOMChange(view, start, end, typeOver) {
5480
5455
  };
5481
5456
  if (change) {
5482
5457
  let startState = view.state;
5458
+ if (browser.ios && view.inputState.flushIOSKey(view))
5459
+ return;
5483
5460
  // Android browsers don't fire reasonable key events for enter,
5484
5461
  // backspace, or delete. So this detects changes that look like
5485
5462
  // they're caused by those keys, and reinterprets them as key
5486
- // events.
5487
- if (browser.ios && view.inputState.flushIOSKey(view))
5463
+ // events. (Some of these keys are also handled by beforeinput
5464
+ // events and the pendingAndroidKey mechanism, but that's not
5465
+ // reliable in all situations.)
5466
+ if (browser.android &&
5467
+ ((change.from == sel.from && change.to == sel.to &&
5468
+ change.insert.length == 1 && change.insert.lines == 2 &&
5469
+ dispatchKey(view.contentDOM, "Enter", 13)) ||
5470
+ (change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 &&
5471
+ dispatchKey(view.contentDOM, "Backspace", 8)) ||
5472
+ (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
5473
+ dispatchKey(view.contentDOM, "Delete", 46))))
5488
5474
  return;
5489
5475
  let text = change.insert.toString();
5490
5476
  if (view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text)))
@@ -5677,6 +5663,7 @@ class EditorView {
5677
5663
  */
5678
5664
  config = {}) {
5679
5665
  this.plugins = [];
5666
+ this.pluginMap = new Map;
5680
5667
  this.editorAttrs = {};
5681
5668
  this.contentAttrs = {};
5682
5669
  this.bidiCache = [];
@@ -5708,7 +5695,9 @@ class EditorView {
5708
5695
  this.dispatch = this.dispatch.bind(this);
5709
5696
  this.root = (config.root || getRoot(config.parent) || document);
5710
5697
  this.viewState = new ViewState(config.state || EditorState.create());
5711
- this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec).update(this));
5698
+ this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec));
5699
+ for (let plugin of this.plugins)
5700
+ plugin.update(this);
5712
5701
  this.observer = new DOMObserver(this, (from, to, typeOver) => {
5713
5702
  applyDOMChange(this, from, to, typeOver);
5714
5703
  }, event => {
@@ -5845,7 +5834,10 @@ class EditorView {
5845
5834
  for (let plugin of this.plugins)
5846
5835
  plugin.destroy(this);
5847
5836
  this.viewState = new ViewState(newState);
5848
- this.plugins = newState.facet(viewPlugin).map(spec => new PluginInstance(spec).update(this));
5837
+ this.plugins = newState.facet(viewPlugin).map(spec => new PluginInstance(spec));
5838
+ this.pluginMap.clear();
5839
+ for (let plugin of this.plugins)
5840
+ plugin.update(this);
5849
5841
  this.docView = new DocView(this);
5850
5842
  this.inputState.ensureHandlers(this);
5851
5843
  this.mountStyles();
@@ -5876,6 +5868,7 @@ class EditorView {
5876
5868
  if (plugin.mustUpdate != update)
5877
5869
  plugin.destroy(this);
5878
5870
  this.plugins = newPlugins;
5871
+ this.pluginMap.clear();
5879
5872
  this.inputState.ensureHandlers(this);
5880
5873
  }
5881
5874
  else {
@@ -5883,7 +5876,7 @@ class EditorView {
5883
5876
  p.mustUpdate = update;
5884
5877
  }
5885
5878
  for (let i = 0; i < this.plugins.length; i++)
5886
- this.plugins[i] = this.plugins[i].update(this);
5879
+ this.plugins[i].update(this);
5887
5880
  }
5888
5881
  /**
5889
5882
  @internal
@@ -5972,7 +5965,7 @@ class EditorView {
5972
5965
  this.state.facet(theme);
5973
5966
  }
5974
5967
  updateAttrs() {
5975
- let editorAttrs = combineAttrs(this.state.facet(editorAttributes), {
5968
+ let editorAttrs = attrsFromFacet(this, editorAttributes, {
5976
5969
  class: "cm-editor" + (this.hasFocus ? " cm-focused " : " ") + this.themeClasses
5977
5970
  });
5978
5971
  let contentAttrs = {
@@ -5988,7 +5981,7 @@ class EditorView {
5988
5981
  };
5989
5982
  if (this.state.readOnly)
5990
5983
  contentAttrs["aria-readonly"] = "true";
5991
- combineAttrs(this.state.facet(contentAttributes), contentAttrs);
5984
+ attrsFromFacet(this, contentAttributes, contentAttrs);
5992
5985
  this.observer.ignore(() => {
5993
5986
  updateAttrs(this.contentDOM, this.contentAttrs, contentAttrs);
5994
5987
  updateAttrs(this.dom, this.editorAttrs, editorAttrs);
@@ -6057,10 +6050,10 @@ class EditorView {
6057
6050
  the return value of this method.
6058
6051
  */
6059
6052
  plugin(plugin) {
6060
- for (let inst of this.plugins)
6061
- if (inst.spec == plugin)
6062
- return inst.update(this).value;
6063
- return null;
6053
+ let known = this.pluginMap.get(plugin);
6054
+ if (known === undefined || known && known.spec != plugin)
6055
+ this.pluginMap.set(plugin, known = this.plugins.find(p => p.spec == plugin) || null);
6056
+ return known && known.update(this).value;
6064
6057
  }
6065
6058
  /**
6066
6059
  The top position of the document, in screen coordinates. This
@@ -6547,6 +6540,14 @@ class CachedOrder {
6547
6540
  return result;
6548
6541
  }
6549
6542
  }
6543
+ function attrsFromFacet(view, facet, base) {
6544
+ for (let sources = view.state.facet(facet), i = sources.length - 1; i >= 0; i--) {
6545
+ let source = sources[i], value = typeof source == "function" ? source(view) : source;
6546
+ if (value)
6547
+ combineAttrs(value, base);
6548
+ }
6549
+ return base;
6550
+ }
6550
6551
 
6551
6552
  const currentPlatform = browser.mac ? "mac" : browser.windows ? "win" : browser.linux ? "linux" : "key";
6552
6553
  function normalizeKeyName(name, platform) {
@@ -7199,35 +7200,29 @@ class TabWidget extends WidgetType {
7199
7200
  }
7200
7201
 
7201
7202
  const plugin = /*@__PURE__*/ViewPlugin.fromClass(class {
7202
- constructor(view) {
7203
- this.height = -1;
7204
- this.measure = {
7205
- read: view => Math.max(0, view.scrollDOM.clientHeight - view.defaultLineHeight),
7206
- write: (value, view) => {
7207
- if (Math.abs(value - this.height) > 1) {
7208
- this.height = value;
7209
- view.contentDOM.style.paddingBottom = value + "px";
7210
- }
7211
- }
7212
- };
7213
- view.requestMeasure(this.measure);
7203
+ constructor() {
7204
+ this.height = 1000;
7205
+ this.attrs = { style: "padding-bottom: 1000px" };
7214
7206
  }
7215
7207
  update(update) {
7216
- if (update.geometryChanged)
7217
- update.view.requestMeasure(this.measure);
7208
+ let height = update.view.viewState.editorHeight - update.view.defaultLineHeight;
7209
+ if (height != this.height) {
7210
+ this.height = height;
7211
+ this.attrs = { style: `padding-bottom: ${height}px` };
7212
+ }
7218
7213
  }
7219
7214
  });
7220
7215
  /**
7221
- Returns a plugin that makes sure the content has a bottom margin
7222
- equivalent to the height of the editor, minus one line height, so
7223
- that every line in the document can be scrolled to the top of the
7224
- editor.
7216
+ Returns an extension that makes sure the content has a bottom
7217
+ margin equivalent to the height of the editor, minus one line
7218
+ height, so that every line in the document can be scrolled to the
7219
+ top of the editor.
7225
7220
 
7226
7221
  This is only meaningful when the editor is scrollable, and should
7227
7222
  not be enabled in editors that take the size of their content.
7228
7223
  */
7229
7224
  function scrollPastEnd() {
7230
- return plugin;
7225
+ return [plugin, contentAttributes.of(view => { var _a; return ((_a = view.plugin(plugin)) === null || _a === void 0 ? void 0 : _a.attrs) || null; })];
7231
7226
  }
7232
7227
 
7233
7228
  /**