@codemirror/view 0.19.23 → 0.19.27

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); }
@@ -752,7 +845,7 @@ class CompositionView extends WidgetView {
752
845
  // These are drawn around uneditable widgets to avoid a number of
753
846
  // browser bugs that show up when the cursor is directly next to
754
847
  // uneditable inline content.
755
- class WidgetBufferView extends InlineView {
848
+ class WidgetBufferView extends ContentView {
756
849
  constructor(side) {
757
850
  super();
758
851
  this.side = side;
@@ -762,7 +855,7 @@ class WidgetBufferView extends InlineView {
762
855
  become(other) {
763
856
  return other instanceof WidgetBufferView && other.side == this.side;
764
857
  }
765
- slice() { return new WidgetBufferView(this.side); }
858
+ split() { return new WidgetBufferView(this.side); }
766
859
  sync() {
767
860
  if (!this.dom)
768
861
  this.setDOM(document.createTextNode("\u200b"));
@@ -780,90 +873,7 @@ class WidgetBufferView extends InlineView {
780
873
  return Text.of([this.dom.nodeValue.replace(/\u200b/g, "")]);
781
874
  }
782
875
  }
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
- }
876
+ TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren;
867
877
  function inlineDOMAtPos(dom, children, pos) {
868
878
  let i = 0;
869
879
  for (let off = 0; i < children.length; i++) {
@@ -1004,6 +1014,11 @@ class WidgetType {
1004
1014
  @internal
1005
1015
  */
1006
1016
  get customView() { return null; }
1017
+ /**
1018
+ This is called when the an instance of the widget is removed
1019
+ from the editor view.
1020
+ */
1021
+ destroy(_dom) { }
1007
1022
  }
1008
1023
  /**
1009
1024
  The different types of blocks that can occur in an editor view.
@@ -1082,8 +1097,9 @@ class Decoration extends RangeValue {
1082
1097
  position.
1083
1098
  */
1084
1099
  static widget(spec) {
1085
- let side = spec.side || 0;
1086
- return new PointDecoration(spec, side, side, !!spec.block, spec.widget || null, false);
1100
+ let side = spec.side || 0, block = !!spec.block;
1101
+ side += block ? (side > 0 ? 300000000 /* BlockAfter */ : -400000000 /* BlockBefore */) : (side > 0 ? 100000000 /* InlineAfter */ : -100000000 /* InlineBefore */);
1102
+ return new PointDecoration(spec, side, side, block, spec.widget || null, false);
1087
1103
  }
1088
1104
  /**
1089
1105
  Create a replace decoration which replaces the given range with
@@ -1092,8 +1108,8 @@ class Decoration extends RangeValue {
1092
1108
  static replace(spec) {
1093
1109
  let block = !!spec.block;
1094
1110
  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);
1111
+ let startSide = block ? (start ? -300000000 /* BlockIncStart */ : -1 /* InlineIncStart */) : 400000000 /* NonIncStart */;
1112
+ let endSide = block ? (end ? 200000000 /* BlockIncEnd */ : 1 /* InlineIncEnd */) : -500000000 /* NonIncEnd */;
1097
1113
  return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
1098
1114
  }
1099
1115
  /**
@@ -1123,7 +1139,7 @@ Decoration.none = RangeSet.empty;
1123
1139
  class MarkDecoration extends Decoration {
1124
1140
  constructor(spec) {
1125
1141
  let { start, end } = getInclusive(spec);
1126
- super(100000000 /* Big */ * (start ? -1 : 1), 100000000 /* Big */ * (end ? 1 : -1), null, spec);
1142
+ super(start ? -1 /* InlineIncStart */ : 400000000 /* NonIncStart */, end ? 1 /* InlineIncEnd */ : -500000000 /* NonIncEnd */, null, spec);
1127
1143
  this.tagName = spec.tagName || "span";
1128
1144
  this.class = spec.class || "";
1129
1145
  this.attrs = spec.attributes || null;
@@ -1144,7 +1160,7 @@ class MarkDecoration extends Decoration {
1144
1160
  MarkDecoration.prototype.point = false;
1145
1161
  class LineDecoration extends Decoration {
1146
1162
  constructor(spec) {
1147
- super(-100000000 /* Big */, -100000000 /* Big */, null, spec);
1163
+ super(-200000000 /* Line */, -200000000 /* Line */, null, spec);
1148
1164
  }
1149
1165
  eq(other) {
1150
1166
  return other instanceof LineDecoration && attrsEq(this.spec.attributes, other.spec.attributes);
@@ -1162,12 +1178,12 @@ class PointDecoration extends Decoration {
1162
1178
  super(startSide, endSide, widget, spec);
1163
1179
  this.block = block;
1164
1180
  this.isReplace = isReplace;
1165
- this.mapMode = !block ? MapMode.TrackDel : startSide < 0 ? MapMode.TrackBefore : MapMode.TrackAfter;
1181
+ this.mapMode = !block ? MapMode.TrackDel : startSide <= 0 ? MapMode.TrackBefore : MapMode.TrackAfter;
1166
1182
  }
1167
1183
  // Only relevant when this.block == true
1168
1184
  get type() {
1169
1185
  return this.startSide < this.endSide ? BlockType.WidgetRange
1170
- : this.startSide < 0 ? BlockType.WidgetBefore : BlockType.WidgetAfter;
1186
+ : this.startSide <= 0 ? BlockType.WidgetBefore : BlockType.WidgetAfter;
1171
1187
  }
1172
1188
  get heightRelevant() { return this.block || !!this.widget && this.widget.estimatedHeight >= 5; }
1173
1189
  eq(other) {
@@ -1177,7 +1193,7 @@ class PointDecoration extends Decoration {
1177
1193
  this.startSide == other.startSide && this.endSide == other.endSide;
1178
1194
  }
1179
1195
  range(from, to = from) {
1180
- if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide < 0)))
1196
+ if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide <= 0)))
1181
1197
  throw new RangeError("Invalid range for replacement decoration");
1182
1198
  if (!this.isReplace && to != from)
1183
1199
  throw new RangeError("Widget decorations can only have zero-length ranges");
@@ -1214,16 +1230,16 @@ class LineView extends ContentView {
1214
1230
  this.breakAfter = 0;
1215
1231
  }
1216
1232
  // Consumes source
1217
- merge(from, to, source, takeDeco, openStart, openEnd) {
1233
+ merge(from, to, source, hasStart, openStart, openEnd) {
1218
1234
  if (source) {
1219
1235
  if (!(source instanceof LineView))
1220
1236
  return false;
1221
1237
  if (!this.dom)
1222
1238
  source.transferDOM(this); // Reuse source.dom when appropriate
1223
1239
  }
1224
- if (takeDeco)
1240
+ if (hasStart)
1225
1241
  this.setDeco(source ? source.attrs : null);
1226
- mergeInlineChildren(this, from, to, source ? source.children : none$1, openStart, openEnd);
1242
+ mergeChildrenInto(this, from, to, source ? source.children : [], openStart, openEnd);
1227
1243
  return true;
1228
1244
  }
1229
1245
  split(at) {
@@ -1233,16 +1249,14 @@ class LineView extends ContentView {
1233
1249
  return end;
1234
1250
  let { i, off } = this.childPos(at);
1235
1251
  if (off) {
1236
- end.append(this.children[i].slice(off), 0);
1237
- this.children[i].merge(off, this.children[i].length, null, 0, 0);
1252
+ end.append(this.children[i].split(off), 0);
1253
+ this.children[i].merge(off, this.children[i].length, null, false, 0, 0);
1238
1254
  i++;
1239
1255
  }
1240
1256
  for (let j = i; j < this.children.length; j++)
1241
1257
  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
- }
1258
+ while (i > 0 && this.children[i - 1].length == 0)
1259
+ this.children[--i].destroy();
1246
1260
  this.children.length = i;
1247
1261
  this.markDirty();
1248
1262
  this.length = at;
@@ -1265,7 +1279,6 @@ class LineView extends ContentView {
1265
1279
  this.attrs = attrs;
1266
1280
  }
1267
1281
  }
1268
- // Only called when building a line view in ContentBuilder
1269
1282
  append(child, openStart) {
1270
1283
  joinInlineInto(this, child, openStart);
1271
1284
  }
@@ -1322,7 +1335,7 @@ class LineView extends ContentView {
1322
1335
  coordsAt(pos, side) {
1323
1336
  return coordsInChildren(this, pos, side);
1324
1337
  }
1325
- match(_other) { return false; }
1338
+ become(_other) { return false; }
1326
1339
  get type() { return BlockType.Text; }
1327
1340
  static find(docView, pos) {
1328
1341
  for (let i = 0, off = 0;; i++) {
@@ -1337,7 +1350,6 @@ class LineView extends ContentView {
1337
1350
  }
1338
1351
  }
1339
1352
  }
1340
- const none$1 = [];
1341
1353
  class BlockWidgetView extends ContentView {
1342
1354
  constructor(widget, length, type) {
1343
1355
  super();
@@ -1363,7 +1375,7 @@ class BlockWidgetView extends ContentView {
1363
1375
  end.breakAfter = this.breakAfter;
1364
1376
  return end;
1365
1377
  }
1366
- get children() { return none$1; }
1378
+ get children() { return noChildren; }
1367
1379
  sync() {
1368
1380
  if (!this.dom || !this.widget.updateDOM(this.dom)) {
1369
1381
  this.setDOM(this.widget.toDOM(this.editorView));
@@ -1374,7 +1386,7 @@ class BlockWidgetView extends ContentView {
1374
1386
  return this.parent ? this.parent.view.state.doc.slice(this.posAtStart, this.posAtEnd) : Text$1.empty;
1375
1387
  }
1376
1388
  domBoundsAround() { return null; }
1377
- match(other) {
1389
+ become(other) {
1378
1390
  if (other instanceof BlockWidgetView && other.type == this.type &&
1379
1391
  other.widget.constructor == this.widget.constructor) {
1380
1392
  if (!other.widget.eq(this.widget))
@@ -1388,6 +1400,11 @@ class BlockWidgetView extends ContentView {
1388
1400
  }
1389
1401
  ignoreMutation() { return true; }
1390
1402
  ignoreEvent(event) { return this.widget.ignoreEvent(event); }
1403
+ destroy() {
1404
+ super.destroy();
1405
+ if (this.dom)
1406
+ this.widget.destroy(this.dom);
1407
+ }
1391
1408
  }
1392
1409
 
1393
1410
  class ContentBuilder {
@@ -1723,36 +1740,39 @@ class PluginInstance {
1723
1740
  this.value = null;
1724
1741
  }
1725
1742
  takeField(type, target) {
1726
- for (let { field, get } of this.spec.fields)
1727
- if (field == type)
1728
- target.push(get(this.value));
1743
+ if (this.spec)
1744
+ for (let { field, get } of this.spec.fields)
1745
+ if (field == type)
1746
+ target.push(get(this.value));
1729
1747
  }
1730
1748
  update(view) {
1731
1749
  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;
1750
+ if (this.spec) {
1751
+ try {
1752
+ this.value = this.spec.create(view);
1753
+ }
1754
+ catch (e) {
1755
+ logException(view.state, e, "CodeMirror plugin crashed");
1756
+ this.deactivate();
1757
+ }
1738
1758
  }
1739
1759
  }
1740
1760
  else if (this.mustUpdate) {
1741
1761
  let update = this.mustUpdate;
1742
1762
  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;
1763
+ if (this.value.update) {
1764
+ try {
1765
+ this.value.update(update);
1766
+ }
1767
+ catch (e) {
1768
+ logException(update.state, e, "CodeMirror plugin crashed");
1769
+ if (this.value.destroy)
1770
+ try {
1771
+ this.value.destroy();
1772
+ }
1773
+ catch (_) { }
1774
+ this.deactivate();
1775
+ }
1756
1776
  }
1757
1777
  }
1758
1778
  return this;
@@ -1768,20 +1788,12 @@ class PluginInstance {
1768
1788
  }
1769
1789
  }
1770
1790
  }
1791
+ deactivate() {
1792
+ this.spec = this.value = null;
1793
+ }
1771
1794
  }
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
- });
1795
+ const editorAttributes = /*@__PURE__*/Facet.define();
1796
+ const contentAttributes = /*@__PURE__*/Facet.define();
1785
1797
  // Provide decorations
1786
1798
  const decorations = /*@__PURE__*/Facet.define();
1787
1799
  const styleModule = /*@__PURE__*/Facet.define();
@@ -1929,12 +1941,12 @@ class DocView extends ContentView {
1929
1941
  this.compositionDeco = Decoration.none;
1930
1942
  this.decorations = [];
1931
1943
  // 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.
1944
+ // measureVisibleLineHeights, this is updated to point at the width
1945
+ // of a given element and its extent in the document. When a change
1946
+ // happens in that range, these are reset. That way, once we've seen
1947
+ // a line/element of a given length, we keep the editor wide enough
1948
+ // to fit at least that element, until it is changed, at which point
1949
+ // we forget it again.
1938
1950
  this.minWidth = 0;
1939
1951
  this.minWidthFrom = 0;
1940
1952
  this.minWidthTo = 0;
@@ -2004,7 +2016,7 @@ class DocView extends ContentView {
2004
2016
  this.updateSelection();
2005
2017
  }
2006
2018
  }
2007
- // Used both by update and checkLayout do perform the actual DOM
2019
+ // Used by update and the constructor do perform the actual DOM
2008
2020
  // update
2009
2021
  updateInner(changes, deco, oldLength) {
2010
2022
  this.view.viewState.mustMeasureContent = true;
@@ -2045,70 +2057,8 @@ class DocView extends ContentView {
2045
2057
  let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, deco);
2046
2058
  let { i: toI, off: toOff } = cursor.findPos(toA, 1);
2047
2059
  let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
2048
- this.replaceRange(fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
2049
- }
2050
- }
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;
2066
- }
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;
2071
- }
2072
- 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);
2078
- }
2060
+ replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
2079
2061
  }
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;
2087
- }
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;
2095
- }
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);
2098
- }
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
- }
2110
- if (fromI < toI || content.length)
2111
- this.replaceChildren(fromI, toI, content);
2112
2062
  }
2113
2063
  // Sync the DOM selection to this.state.selection
2114
2064
  updateSelection(mustRead = false, fromPointer = false) {
@@ -2372,15 +2322,10 @@ function computeCompositionDeco(view, changes) {
2372
2322
  if (!textNode)
2373
2323
  return Decoration.none;
2374
2324
  let cView = view.docView.nearest(textNode);
2325
+ if (!cView)
2326
+ return Decoration.none;
2375
2327
  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) {
2328
+ if (cView instanceof LineView) {
2384
2329
  while (topNode.parentNode != cView.dom)
2385
2330
  topNode = topNode.parentNode;
2386
2331
  let prev = topNode.previousSibling;
@@ -2389,7 +2334,17 @@ function computeCompositionDeco(view, changes) {
2389
2334
  from = to = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
2390
2335
  }
2391
2336
  else {
2392
- return Decoration.none;
2337
+ for (;;) {
2338
+ let { parent } = cView;
2339
+ if (!parent)
2340
+ return Decoration.none;
2341
+ if (parent instanceof LineView)
2342
+ break;
2343
+ cView = parent;
2344
+ }
2345
+ from = cView.posAtStart;
2346
+ to = from + cView.length;
2347
+ topNode = cView.dom;
2393
2348
  }
2394
2349
  let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
2395
2350
  let text = textNode.nodeValue, { state } = view;
@@ -2926,7 +2881,7 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
2926
2881
  y = docTop + yOffset;
2927
2882
  let lineStart = block.from;
2928
2883
  // Clip x to the viewport sides
2929
- x = Math.max(content.left + 1, Math.min(content.right - 1, x));
2884
+ x = Math.max(content.left + 1, Math.min(Math.max(content.right, content.left + view.docView.minWidth) - 1, x));
2930
2885
  // If this is outside of the rendered viewport, we can't determine a position
2931
2886
  if (lineStart < view.viewport.from)
2932
2887
  return view.viewport.from == 0 ? 0 : posAtCoordsImprecise(view, content, block, x, y);
@@ -4168,12 +4123,12 @@ class HeightMapBranch extends HeightMap {
4168
4123
  get break() { return this.flags & 1 /* Break */; }
4169
4124
  blockAt(height, doc, top, offset) {
4170
4125
  let mid = top + this.left.height;
4171
- return height < mid || this.right.height == 0 ? this.left.blockAt(height, doc, top, offset)
4126
+ return height < mid ? this.left.blockAt(height, doc, top, offset)
4172
4127
  : this.right.blockAt(height, doc, mid, offset + this.left.length + this.break);
4173
4128
  }
4174
4129
  lineAt(value, type, doc, top, offset) {
4175
4130
  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;
4131
+ let left = type == QueryType.ByHeight ? value < rightTop : value < rightOffset;
4177
4132
  let base = left ? this.left.lineAt(value, type, doc, top, offset)
4178
4133
  : this.right.lineAt(value, type, doc, rightTop, rightOffset);
4179
4134
  if (this.break || (left ? base.to < rightOffset : base.from > rightOffset))
@@ -4314,7 +4269,9 @@ class NodeBuilder {
4314
4269
  }
4315
4270
  point(from, to, deco) {
4316
4271
  if (from < to || deco.heightRelevant) {
4317
- let height = deco.widget ? Math.max(0, deco.widget.estimatedHeight) : 0;
4272
+ let height = deco.widget ? deco.widget.estimatedHeight : 0;
4273
+ if (height < 0)
4274
+ height = this.oracle.lineHeight;
4318
4275
  let len = to - from;
4319
4276
  if (deco.block) {
4320
4277
  this.addBlock(new HeightMapBlock(len, height, deco.type));
@@ -4788,7 +4745,7 @@ class ViewState {
4788
4745
  return changed ? 4 /* Viewport */ : 0;
4789
4746
  }
4790
4747
  lineBlockAt(pos) {
4791
- return (pos >= this.viewport.from && pos <= this.viewport.to && this.viewportLines.find(b => b.from <= pos && b.to <= pos)) ||
4748
+ return (pos >= this.viewport.from && pos <= this.viewport.to && this.viewportLines.find(b => b.from <= pos && b.to >= pos)) ||
4792
4749
  scaleBlock(this.heightMap.lineAt(pos, QueryType.ByPos, this.state.doc, 0, 0), this.scaler);
4793
4750
  }
4794
4751
  lineBlockAtHeight(height) {
@@ -5059,7 +5016,7 @@ const baseTheme = /*@__PURE__*/buildTheme("." + baseThemeID, {
5059
5016
  color: "inherit",
5060
5017
  fontSize: "70%",
5061
5018
  padding: ".2em 1em",
5062
- borderRadius: "3px"
5019
+ borderRadius: "1px"
5063
5020
  },
5064
5021
  "&light .cm-button": {
5065
5022
  backgroundImage: "linear-gradient(#eff1f5, #d9d9df)",
@@ -5381,6 +5338,7 @@ class DOMObserver {
5381
5338
  for (let dom of this.scrollTargets)
5382
5339
  dom.removeEventListener("scroll", this.onScroll);
5383
5340
  window.removeEventListener("scroll", this.onScroll);
5341
+ this.dom.ownerDocument.removeEventListener("selectionchange", this.onSelectionChange);
5384
5342
  clearTimeout(this.parentCheck);
5385
5343
  clearTimeout(this.resizeTimeout);
5386
5344
  }
@@ -5677,6 +5635,7 @@ class EditorView {
5677
5635
  */
5678
5636
  config = {}) {
5679
5637
  this.plugins = [];
5638
+ this.pluginMap = new Map;
5680
5639
  this.editorAttrs = {};
5681
5640
  this.contentAttrs = {};
5682
5641
  this.bidiCache = [];
@@ -5708,7 +5667,9 @@ class EditorView {
5708
5667
  this.dispatch = this.dispatch.bind(this);
5709
5668
  this.root = (config.root || getRoot(config.parent) || document);
5710
5669
  this.viewState = new ViewState(config.state || EditorState.create());
5711
- this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec).update(this));
5670
+ this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec));
5671
+ for (let plugin of this.plugins)
5672
+ plugin.update(this);
5712
5673
  this.observer = new DOMObserver(this, (from, to, typeOver) => {
5713
5674
  applyDOMChange(this, from, to, typeOver);
5714
5675
  }, event => {
@@ -5845,7 +5806,10 @@ class EditorView {
5845
5806
  for (let plugin of this.plugins)
5846
5807
  plugin.destroy(this);
5847
5808
  this.viewState = new ViewState(newState);
5848
- this.plugins = newState.facet(viewPlugin).map(spec => new PluginInstance(spec).update(this));
5809
+ this.plugins = newState.facet(viewPlugin).map(spec => new PluginInstance(spec));
5810
+ this.pluginMap.clear();
5811
+ for (let plugin of this.plugins)
5812
+ plugin.update(this);
5849
5813
  this.docView = new DocView(this);
5850
5814
  this.inputState.ensureHandlers(this);
5851
5815
  this.mountStyles();
@@ -5876,6 +5840,7 @@ class EditorView {
5876
5840
  if (plugin.mustUpdate != update)
5877
5841
  plugin.destroy(this);
5878
5842
  this.plugins = newPlugins;
5843
+ this.pluginMap.clear();
5879
5844
  this.inputState.ensureHandlers(this);
5880
5845
  }
5881
5846
  else {
@@ -5883,7 +5848,7 @@ class EditorView {
5883
5848
  p.mustUpdate = update;
5884
5849
  }
5885
5850
  for (let i = 0; i < this.plugins.length; i++)
5886
- this.plugins[i] = this.plugins[i].update(this);
5851
+ this.plugins[i].update(this);
5887
5852
  }
5888
5853
  /**
5889
5854
  @internal
@@ -5972,7 +5937,7 @@ class EditorView {
5972
5937
  this.state.facet(theme);
5973
5938
  }
5974
5939
  updateAttrs() {
5975
- let editorAttrs = combineAttrs(this.state.facet(editorAttributes), {
5940
+ let editorAttrs = attrsFromFacet(this, editorAttributes, {
5976
5941
  class: "cm-editor" + (this.hasFocus ? " cm-focused " : " ") + this.themeClasses
5977
5942
  });
5978
5943
  let contentAttrs = {
@@ -5988,7 +5953,7 @@ class EditorView {
5988
5953
  };
5989
5954
  if (this.state.readOnly)
5990
5955
  contentAttrs["aria-readonly"] = "true";
5991
- combineAttrs(this.state.facet(contentAttributes), contentAttrs);
5956
+ attrsFromFacet(this, contentAttributes, contentAttrs);
5992
5957
  this.observer.ignore(() => {
5993
5958
  updateAttrs(this.contentDOM, this.contentAttrs, contentAttrs);
5994
5959
  updateAttrs(this.dom, this.editorAttrs, editorAttrs);
@@ -6057,10 +6022,10 @@ class EditorView {
6057
6022
  the return value of this method.
6058
6023
  */
6059
6024
  plugin(plugin) {
6060
- for (let inst of this.plugins)
6061
- if (inst.spec == plugin)
6062
- return inst.update(this).value;
6063
- return null;
6025
+ let known = this.pluginMap.get(plugin);
6026
+ if (known === undefined || known && known.spec != plugin)
6027
+ this.pluginMap.set(plugin, known = this.plugins.find(p => p.spec == plugin) || null);
6028
+ return known && known.update(this).value;
6064
6029
  }
6065
6030
  /**
6066
6031
  The top position of the document, in screen coordinates. This
@@ -6547,6 +6512,14 @@ class CachedOrder {
6547
6512
  return result;
6548
6513
  }
6549
6514
  }
6515
+ function attrsFromFacet(view, facet, base) {
6516
+ for (let sources = view.state.facet(facet), i = sources.length - 1; i >= 0; i--) {
6517
+ let source = sources[i], value = typeof source == "function" ? source(view) : source;
6518
+ if (value)
6519
+ combineAttrs(value, base);
6520
+ }
6521
+ return base;
6522
+ }
6550
6523
 
6551
6524
  const currentPlatform = browser.mac ? "mac" : browser.windows ? "win" : browser.linux ? "linux" : "key";
6552
6525
  function normalizeKeyName(name, platform) {
@@ -7199,35 +7172,29 @@ class TabWidget extends WidgetType {
7199
7172
  }
7200
7173
 
7201
7174
  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);
7175
+ constructor() {
7176
+ this.height = 1000;
7177
+ this.attrs = { style: "padding-bottom: 1000px" };
7214
7178
  }
7215
7179
  update(update) {
7216
- if (update.geometryChanged)
7217
- update.view.requestMeasure(this.measure);
7180
+ let height = update.view.viewState.editorHeight - update.view.defaultLineHeight;
7181
+ if (height != this.height) {
7182
+ this.height = height;
7183
+ this.attrs = { style: `padding-bottom: ${height}px` };
7184
+ }
7218
7185
  }
7219
7186
  });
7220
7187
  /**
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.
7188
+ Returns an extension that makes sure the content has a bottom
7189
+ margin equivalent to the height of the editor, minus one line
7190
+ height, so that every line in the document can be scrolled to the
7191
+ top of the editor.
7225
7192
 
7226
7193
  This is only meaningful when the editor is scrollable, and should
7227
7194
  not be enabled in editors that take the size of their content.
7228
7195
  */
7229
7196
  function scrollPastEnd() {
7230
- return plugin;
7197
+ return [plugin, contentAttributes.of(view => { var _a; return ((_a = view.plugin(plugin)) === null || _a === void 0 ? void 0 : _a.attrs) || null; })];
7231
7198
  }
7232
7199
 
7233
7200
  /**