@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.cjs CHANGED
@@ -273,7 +273,7 @@ class DOMPos {
273
273
  static before(dom, precise) { return new DOMPos(dom.parentNode, domIndex(dom), precise); }
274
274
  static after(dom, precise) { return new DOMPos(dom.parentNode, domIndex(dom) + 1, precise); }
275
275
  }
276
- const none$3 = [];
276
+ const noChildren = [];
277
277
  class ContentView {
278
278
  constructor() {
279
279
  this.parent = null;
@@ -439,12 +439,12 @@ class ContentView {
439
439
  v = parent;
440
440
  }
441
441
  }
442
- replaceChildren(from, to, children = none$3) {
442
+ replaceChildren(from, to, children = noChildren) {
443
443
  this.markDirty();
444
444
  for (let i = from; i < to; i++) {
445
445
  let child = this.children[i];
446
446
  if (child.parent == this)
447
- child.parent = null;
447
+ child.destroy();
448
448
  }
449
449
  this.children.splice(from, to - from, ...children);
450
450
  for (let i = 0; i < children.length; i++)
@@ -466,6 +466,17 @@ class ContentView {
466
466
  }
467
467
  static get(node) { return node.cmView; }
468
468
  get isEditable() { return true; }
469
+ merge(from, to, source, hasStart, openStart, openEnd) {
470
+ return false;
471
+ }
472
+ become(other) { return false; }
473
+ // When this is a zero-length view with a side, this should return a
474
+ // number <= 0 to indicate it is before its position, or a
475
+ // number > 0 when after its position.
476
+ getSide() { return 0; }
477
+ destroy() {
478
+ this.parent = null;
479
+ }
469
480
  }
470
481
  ContentView.prototype.breakAfter = 0;
471
482
  // Remove a DOM node and return its next sibling.
@@ -493,6 +504,94 @@ class ChildCursor {
493
504
  }
494
505
  }
495
506
  }
507
+ function replaceRange(parent, fromI, fromOff, toI, toOff, insert, breakAtStart, openStart, openEnd) {
508
+ let { children } = parent;
509
+ let before = children.length ? children[fromI] : null;
510
+ let last = insert.length ? insert[insert.length - 1] : null;
511
+ let breakAtEnd = last ? last.breakAfter : breakAtStart;
512
+ // Change within a single child
513
+ if (fromI == toI && before && !breakAtStart && !breakAtEnd && insert.length < 2 &&
514
+ before.merge(fromOff, toOff, insert.length ? last : null, fromOff == 0, openStart, openEnd))
515
+ return;
516
+ if (toI < children.length) {
517
+ let after = children[toI];
518
+ // Make sure the end of the child after the update is preserved in `after`
519
+ if (after && toOff < after.length) {
520
+ // If we're splitting a child, separate part of it to avoid that
521
+ // being mangled when updating the child before the update.
522
+ if (fromI == toI) {
523
+ after = after.split(toOff);
524
+ toOff = 0;
525
+ }
526
+ // If the element after the replacement should be merged with
527
+ // the last replacing element, update `content`
528
+ if (!breakAtEnd && last && after.merge(0, toOff, last, true, 0, openEnd)) {
529
+ insert[insert.length - 1] = after;
530
+ }
531
+ else {
532
+ // Remove the start of the after element, if necessary, and
533
+ // add it to `content`.
534
+ if (toOff)
535
+ after.merge(0, toOff, null, false, 0, openEnd);
536
+ insert.push(after);
537
+ }
538
+ }
539
+ else if (after === null || after === void 0 ? void 0 : after.breakAfter) {
540
+ // The element at `toI` is entirely covered by this range.
541
+ // Preserve its line break, if any.
542
+ if (last)
543
+ last.breakAfter = 1;
544
+ else
545
+ breakAtStart = 1;
546
+ }
547
+ // Since we've handled the next element from the current elements
548
+ // now, make sure `toI` points after that.
549
+ toI++;
550
+ }
551
+ if (before) {
552
+ before.breakAfter = breakAtStart;
553
+ if (fromOff > 0) {
554
+ if (!breakAtStart && insert.length && before.merge(fromOff, before.length, insert[0], false, openStart, 0)) {
555
+ before.breakAfter = insert.shift().breakAfter;
556
+ }
557
+ else if (fromOff < before.length || before.children.length && before.children[before.children.length - 1].length == 0) {
558
+ before.merge(fromOff, before.length, null, false, openStart, 0);
559
+ }
560
+ fromI++;
561
+ }
562
+ }
563
+ // Try to merge widgets on the boundaries of the replacement
564
+ while (fromI < toI && insert.length) {
565
+ if (children[toI - 1].become(insert[insert.length - 1])) {
566
+ toI--;
567
+ insert.pop();
568
+ openEnd = insert.length ? 0 : openStart;
569
+ }
570
+ else if (children[fromI].become(insert[0])) {
571
+ fromI++;
572
+ insert.shift();
573
+ openStart = insert.length ? 0 : openEnd;
574
+ }
575
+ else {
576
+ break;
577
+ }
578
+ }
579
+ if (!insert.length && fromI && toI < children.length && !children[fromI - 1].breakAfter &&
580
+ children[toI].merge(0, 0, children[fromI - 1], false, openStart, openEnd))
581
+ fromI--;
582
+ if (fromI < toI || insert.length)
583
+ parent.replaceChildren(fromI, toI, insert);
584
+ }
585
+ function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) {
586
+ let cur = parent.childCursor();
587
+ let { i: toI, off: toOff } = cur.findPos(to, 1);
588
+ let { i: fromI, off: fromOff } = cur.findPos(from, -1);
589
+ let dLen = from - to;
590
+ for (let view of insert)
591
+ dLen += view.length;
592
+ parent.length += dLen;
593
+ replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd);
594
+ }
496
595
 
497
596
  let [nav, doc] = typeof navigator != "undefined"
498
597
  ? [navigator, document]
@@ -524,21 +623,8 @@ var browser = {
524
623
  tabSize: doc.documentElement.style.tabSize != null ? "tab-size" : "-moz-tab-size"
525
624
  };
526
625
 
527
- const none$2 = [];
528
- class InlineView extends ContentView {
529
- /**
530
- Return true when this view is equivalent to `other` and can take
531
- on its role.
532
- */
533
- become(_other) { return false; }
534
- // When this is a zero-length view with a side, this should return a
535
- // negative number to indicate it is before its position, or a
536
- // positive number when after its position.
537
- getSide() { return 0; }
538
- }
539
- InlineView.prototype.children = none$2;
540
626
  const MaxJoinLen = 256;
541
- class TextView extends InlineView {
627
+ class TextView extends ContentView {
542
628
  constructor(text) {
543
629
  super();
544
630
  this.text = text;
@@ -569,7 +655,7 @@ class TextView extends InlineView {
569
655
  this.markDirty();
570
656
  return true;
571
657
  }
572
- slice(from) {
658
+ split(from) {
573
659
  let result = new TextView(this.text.slice(from));
574
660
  this.text = this.text.slice(0, from);
575
661
  return result;
@@ -585,7 +671,7 @@ class TextView extends InlineView {
585
671
  return textCoords(this.dom, pos, side);
586
672
  }
587
673
  }
588
- class MarkView extends InlineView {
674
+ class MarkView extends ContentView {
589
675
  constructor(mark, children = [], length = 0) {
590
676
  super();
591
677
  this.mark = mark;
@@ -608,20 +694,20 @@ class MarkView extends InlineView {
608
694
  this.createDOM();
609
695
  super.sync(track);
610
696
  }
611
- merge(from, to, source, openStart, openEnd) {
697
+ merge(from, to, source, _hasStart, openStart, openEnd) {
612
698
  if (source && (!(source instanceof MarkView && source.mark.eq(this.mark)) ||
613
699
  (from && openStart <= 0) || (to < this.length && openEnd <= 0)))
614
700
  return false;
615
- mergeInlineChildren(this, from, to, source ? source.children : none$2, openStart - 1, openEnd - 1);
701
+ mergeChildrenInto(this, from, to, source ? source.children : [], openStart - 1, openEnd - 1);
616
702
  this.markDirty();
617
703
  return true;
618
704
  }
619
- slice(from) {
705
+ split(from) {
620
706
  let result = [], off = 0, detachFrom = -1, i = 0;
621
707
  for (let elt of this.children) {
622
708
  let end = off + elt.length;
623
709
  if (end > from)
624
- result.push(off < from ? elt.slice(from - off) : elt);
710
+ result.push(off < from ? elt.split(from - off) : elt);
625
711
  if (detachFrom < 0 && off >= from)
626
712
  detachFrom = i;
627
713
  off = end;
@@ -629,8 +715,10 @@ class MarkView extends InlineView {
629
715
  }
630
716
  let length = this.length - from;
631
717
  this.length = from;
632
- if (detachFrom > -1)
633
- this.replaceChildren(detachFrom, this.children.length);
718
+ if (detachFrom > -1) {
719
+ this.children.length = detachFrom;
720
+ this.markDirty();
721
+ }
634
722
  return new MarkView(this.mark, result, length);
635
723
  }
636
724
  domAtPos(pos) {
@@ -672,7 +760,7 @@ function textCoords(text, pos, side) {
672
760
  return flatten ? flattenRect(rect, flatten < 0) : rect || null;
673
761
  }
674
762
  // Also used for collapsed ranges that don't have a placeholder widget!
675
- class WidgetView extends InlineView {
763
+ class WidgetView extends ContentView {
676
764
  constructor(widget, length, side) {
677
765
  super();
678
766
  this.widget = widget;
@@ -682,7 +770,7 @@ class WidgetView extends InlineView {
682
770
  static create(widget, length, side) {
683
771
  return new (widget.customView || WidgetView)(widget, length, side);
684
772
  }
685
- slice(from) {
773
+ split(from) {
686
774
  let result = WidgetView.create(this.widget, this.length - from, this.side);
687
775
  this.length -= from;
688
776
  return result;
@@ -694,7 +782,7 @@ class WidgetView extends InlineView {
694
782
  }
695
783
  }
696
784
  getSide() { return this.side; }
697
- merge(from, to, source, openStart, openEnd) {
785
+ merge(from, to, source, hasStart, openStart, openEnd) {
698
786
  if (source && (!(source instanceof WidgetView) || !this.widget.compare(source.widget) ||
699
787
  from > 0 && openStart <= 0 || to < this.length && openEnd <= 0))
700
788
  return false;
@@ -739,6 +827,11 @@ class WidgetView extends InlineView {
739
827
  return (pos == 0 && side > 0 || pos == this.length && side <= 0) ? rect : flattenRect(rect, pos == 0);
740
828
  }
741
829
  get isEditable() { return false; }
830
+ destroy() {
831
+ super.destroy();
832
+ if (this.dom)
833
+ this.widget.destroy(this.dom);
834
+ }
742
835
  }
743
836
  class CompositionView extends WidgetView {
744
837
  domAtPos(pos) { return new DOMPos(this.widget.text, pos); }
@@ -755,7 +848,7 @@ class CompositionView extends WidgetView {
755
848
  // These are drawn around uneditable widgets to avoid a number of
756
849
  // browser bugs that show up when the cursor is directly next to
757
850
  // uneditable inline content.
758
- class WidgetBufferView extends InlineView {
851
+ class WidgetBufferView extends ContentView {
759
852
  constructor(side) {
760
853
  super();
761
854
  this.side = side;
@@ -765,7 +858,7 @@ class WidgetBufferView extends InlineView {
765
858
  become(other) {
766
859
  return other instanceof WidgetBufferView && other.side == this.side;
767
860
  }
768
- slice() { return new WidgetBufferView(this.side); }
861
+ split() { return new WidgetBufferView(this.side); }
769
862
  sync() {
770
863
  if (!this.dom)
771
864
  this.setDOM(document.createTextNode("\u200b"));
@@ -783,90 +876,7 @@ class WidgetBufferView extends InlineView {
783
876
  return text.Text.of([this.dom.nodeValue.replace(/\u200b/g, "")]);
784
877
  }
785
878
  }
786
- function mergeInlineChildren(parent, from, to, elts, openStart, openEnd) {
787
- let cur = parent.childCursor();
788
- let { i: toI, off: toOff } = cur.findPos(to, 1);
789
- let { i: fromI, off: fromOff } = cur.findPos(from, -1);
790
- let dLen = from - to;
791
- for (let view of elts)
792
- dLen += view.length;
793
- parent.length += dLen;
794
- let { children } = parent;
795
- // Both from and to point into the same child view
796
- if (fromI == toI && fromOff) {
797
- let start = children[fromI];
798
- // Maybe just update that view and be done
799
- if (elts.length == 1 && start.merge(fromOff, toOff, elts[0], openStart, openEnd))
800
- return;
801
- if (elts.length == 0) {
802
- start.merge(fromOff, toOff, null, openStart, openEnd);
803
- return;
804
- }
805
- // Otherwise split it, so that we don't have to worry about aliasing front/end afterwards
806
- let after = start.slice(toOff);
807
- if (after.merge(0, 0, elts[elts.length - 1], 0, openEnd))
808
- elts[elts.length - 1] = after;
809
- else
810
- elts.push(after);
811
- toI++;
812
- openEnd = toOff = 0;
813
- }
814
- // Make sure start and end positions fall on node boundaries
815
- // (fromOff/toOff are no longer used after this), and that if the
816
- // start or end of the elts can be merged with adjacent nodes,
817
- // this is done
818
- if (toOff) {
819
- let end = children[toI];
820
- if (elts.length && end.merge(0, toOff, elts[elts.length - 1], 0, openEnd)) {
821
- elts.pop();
822
- openEnd = elts.length ? 0 : openStart;
823
- }
824
- else {
825
- end.merge(0, toOff, null, 0, 0);
826
- }
827
- }
828
- else if (toI < children.length && elts.length &&
829
- children[toI].merge(0, 0, elts[elts.length - 1], 0, openEnd)) {
830
- elts.pop();
831
- openEnd = elts.length ? 0 : openStart;
832
- }
833
- if (fromOff) {
834
- let start = children[fromI];
835
- if (elts.length && start.merge(fromOff, start.length, elts[0], openStart, 0)) {
836
- elts.shift();
837
- openStart = elts.length ? 0 : openEnd;
838
- }
839
- else {
840
- start.merge(fromOff, start.length, null, 0, 0);
841
- }
842
- fromI++;
843
- }
844
- else if (fromI && elts.length) {
845
- let end = children[fromI - 1];
846
- if (end.merge(end.length, end.length, elts[0], openStart, 0)) {
847
- elts.shift();
848
- openStart = elts.length ? 0 : openEnd;
849
- }
850
- }
851
- // Then try to merge any mergeable nodes at the start and end of
852
- // the changed range
853
- while (fromI < toI && elts.length && children[toI - 1].become(elts[elts.length - 1])) {
854
- elts.pop();
855
- toI--;
856
- openEnd = elts.length ? 0 : openStart;
857
- }
858
- while (fromI < toI && elts.length && children[fromI].become(elts[0])) {
859
- elts.shift();
860
- fromI++;
861
- openStart = elts.length ? 0 : openEnd;
862
- }
863
- if (!elts.length && fromI && toI < children.length &&
864
- children[toI].merge(0, 0, children[fromI - 1], openStart, openEnd))
865
- fromI--;
866
- // And if anything remains, splice the child array to insert the new elts
867
- if (elts.length || fromI != toI)
868
- parent.replaceChildren(fromI, toI, elts);
869
- }
879
+ TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren;
870
880
  function inlineDOMAtPos(dom, children, pos) {
871
881
  let i = 0;
872
882
  for (let off = 0; i < children.length; i++) {
@@ -1007,6 +1017,11 @@ class WidgetType {
1007
1017
  @internal
1008
1018
  */
1009
1019
  get customView() { return null; }
1020
+ /**
1021
+ This is called when the an instance of the widget is removed
1022
+ from the editor view.
1023
+ */
1024
+ destroy(_dom) { }
1010
1025
  }
1011
1026
  /**
1012
1027
  The different types of blocks that can occur in an editor view.
@@ -1086,8 +1101,9 @@ class Decoration extends rangeset.RangeValue {
1086
1101
  position.
1087
1102
  */
1088
1103
  static widget(spec) {
1089
- let side = spec.side || 0;
1090
- 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);
1091
1107
  }
1092
1108
  /**
1093
1109
  Create a replace decoration which replaces the given range with
@@ -1096,8 +1112,8 @@ class Decoration extends rangeset.RangeValue {
1096
1112
  static replace(spec) {
1097
1113
  let block = !!spec.block;
1098
1114
  let { start, end } = getInclusive(spec, block);
1099
- let startSide = 100000000 /* Big */ * (start ? -1 : 1) * (block ? 2 : 1);
1100
- 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 */;
1101
1117
  return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
1102
1118
  }
1103
1119
  /**
@@ -1127,7 +1143,7 @@ Decoration.none = rangeset.RangeSet.empty;
1127
1143
  class MarkDecoration extends Decoration {
1128
1144
  constructor(spec) {
1129
1145
  let { start, end } = getInclusive(spec);
1130
- 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);
1131
1147
  this.tagName = spec.tagName || "span";
1132
1148
  this.class = spec.class || "";
1133
1149
  this.attrs = spec.attributes || null;
@@ -1148,7 +1164,7 @@ class MarkDecoration extends Decoration {
1148
1164
  MarkDecoration.prototype.point = false;
1149
1165
  class LineDecoration extends Decoration {
1150
1166
  constructor(spec) {
1151
- super(-100000000 /* Big */, -100000000 /* Big */, null, spec);
1167
+ super(-200000000 /* Line */, -200000000 /* Line */, null, spec);
1152
1168
  }
1153
1169
  eq(other) {
1154
1170
  return other instanceof LineDecoration && attrsEq(this.spec.attributes, other.spec.attributes);
@@ -1166,12 +1182,12 @@ class PointDecoration extends Decoration {
1166
1182
  super(startSide, endSide, widget, spec);
1167
1183
  this.block = block;
1168
1184
  this.isReplace = isReplace;
1169
- this.mapMode = !block ? state.MapMode.TrackDel : startSide < 0 ? state.MapMode.TrackBefore : state.MapMode.TrackAfter;
1185
+ this.mapMode = !block ? state.MapMode.TrackDel : startSide <= 0 ? state.MapMode.TrackBefore : state.MapMode.TrackAfter;
1170
1186
  }
1171
1187
  // Only relevant when this.block == true
1172
1188
  get type() {
1173
1189
  return this.startSide < this.endSide ? exports.BlockType.WidgetRange
1174
- : this.startSide < 0 ? exports.BlockType.WidgetBefore : exports.BlockType.WidgetAfter;
1190
+ : this.startSide <= 0 ? exports.BlockType.WidgetBefore : exports.BlockType.WidgetAfter;
1175
1191
  }
1176
1192
  get heightRelevant() { return this.block || !!this.widget && this.widget.estimatedHeight >= 5; }
1177
1193
  eq(other) {
@@ -1181,7 +1197,7 @@ class PointDecoration extends Decoration {
1181
1197
  this.startSide == other.startSide && this.endSide == other.endSide;
1182
1198
  }
1183
1199
  range(from, to = from) {
1184
- 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)))
1185
1201
  throw new RangeError("Invalid range for replacement decoration");
1186
1202
  if (!this.isReplace && to != from)
1187
1203
  throw new RangeError("Widget decorations can only have zero-length ranges");
@@ -1218,16 +1234,16 @@ class LineView extends ContentView {
1218
1234
  this.breakAfter = 0;
1219
1235
  }
1220
1236
  // Consumes source
1221
- merge(from, to, source, takeDeco, openStart, openEnd) {
1237
+ merge(from, to, source, hasStart, openStart, openEnd) {
1222
1238
  if (source) {
1223
1239
  if (!(source instanceof LineView))
1224
1240
  return false;
1225
1241
  if (!this.dom)
1226
1242
  source.transferDOM(this); // Reuse source.dom when appropriate
1227
1243
  }
1228
- if (takeDeco)
1244
+ if (hasStart)
1229
1245
  this.setDeco(source ? source.attrs : null);
1230
- mergeInlineChildren(this, from, to, source ? source.children : none$1, openStart, openEnd);
1246
+ mergeChildrenInto(this, from, to, source ? source.children : [], openStart, openEnd);
1231
1247
  return true;
1232
1248
  }
1233
1249
  split(at) {
@@ -1237,16 +1253,14 @@ class LineView extends ContentView {
1237
1253
  return end;
1238
1254
  let { i, off } = this.childPos(at);
1239
1255
  if (off) {
1240
- end.append(this.children[i].slice(off), 0);
1241
- 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);
1242
1258
  i++;
1243
1259
  }
1244
1260
  for (let j = i; j < this.children.length; j++)
1245
1261
  end.append(this.children[j], 0);
1246
- while (i > 0 && this.children[i - 1].length == 0) {
1247
- this.children[i - 1].parent = null;
1248
- i--;
1249
- }
1262
+ while (i > 0 && this.children[i - 1].length == 0)
1263
+ this.children[--i].destroy();
1250
1264
  this.children.length = i;
1251
1265
  this.markDirty();
1252
1266
  this.length = at;
@@ -1269,7 +1283,6 @@ class LineView extends ContentView {
1269
1283
  this.attrs = attrs;
1270
1284
  }
1271
1285
  }
1272
- // Only called when building a line view in ContentBuilder
1273
1286
  append(child, openStart) {
1274
1287
  joinInlineInto(this, child, openStart);
1275
1288
  }
@@ -1326,7 +1339,7 @@ class LineView extends ContentView {
1326
1339
  coordsAt(pos, side) {
1327
1340
  return coordsInChildren(this, pos, side);
1328
1341
  }
1329
- match(_other) { return false; }
1342
+ become(_other) { return false; }
1330
1343
  get type() { return exports.BlockType.Text; }
1331
1344
  static find(docView, pos) {
1332
1345
  for (let i = 0, off = 0;; i++) {
@@ -1341,7 +1354,6 @@ class LineView extends ContentView {
1341
1354
  }
1342
1355
  }
1343
1356
  }
1344
- const none$1 = [];
1345
1357
  class BlockWidgetView extends ContentView {
1346
1358
  constructor(widget, length, type) {
1347
1359
  super();
@@ -1367,7 +1379,7 @@ class BlockWidgetView extends ContentView {
1367
1379
  end.breakAfter = this.breakAfter;
1368
1380
  return end;
1369
1381
  }
1370
- get children() { return none$1; }
1382
+ get children() { return noChildren; }
1371
1383
  sync() {
1372
1384
  if (!this.dom || !this.widget.updateDOM(this.dom)) {
1373
1385
  this.setDOM(this.widget.toDOM(this.editorView));
@@ -1378,7 +1390,7 @@ class BlockWidgetView extends ContentView {
1378
1390
  return this.parent ? this.parent.view.state.doc.slice(this.posAtStart, this.posAtEnd) : state.Text.empty;
1379
1391
  }
1380
1392
  domBoundsAround() { return null; }
1381
- match(other) {
1393
+ become(other) {
1382
1394
  if (other instanceof BlockWidgetView && other.type == this.type &&
1383
1395
  other.widget.constructor == this.widget.constructor) {
1384
1396
  if (!other.widget.eq(this.widget))
@@ -1392,6 +1404,11 @@ class BlockWidgetView extends ContentView {
1392
1404
  }
1393
1405
  ignoreMutation() { return true; }
1394
1406
  ignoreEvent(event) { return this.widget.ignoreEvent(event); }
1407
+ destroy() {
1408
+ super.destroy();
1409
+ if (this.dom)
1410
+ this.widget.destroy(this.dom);
1411
+ }
1395
1412
  }
1396
1413
 
1397
1414
  class ContentBuilder {
@@ -1727,36 +1744,39 @@ class PluginInstance {
1727
1744
  this.value = null;
1728
1745
  }
1729
1746
  takeField(type, target) {
1730
- for (let { field, get } of this.spec.fields)
1731
- if (field == type)
1732
- 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));
1733
1751
  }
1734
1752
  update(view) {
1735
1753
  if (!this.value) {
1736
- try {
1737
- this.value = this.spec.create(view);
1738
- }
1739
- catch (e) {
1740
- logException(view.state, e, "CodeMirror plugin crashed");
1741
- 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
+ }
1742
1762
  }
1743
1763
  }
1744
1764
  else if (this.mustUpdate) {
1745
1765
  let update = this.mustUpdate;
1746
1766
  this.mustUpdate = null;
1747
- if (!this.value.update)
1748
- return this;
1749
- try {
1750
- this.value.update(update);
1751
- }
1752
- catch (e) {
1753
- logException(update.state, e, "CodeMirror plugin crashed");
1754
- if (this.value.destroy)
1755
- try {
1756
- this.value.destroy();
1757
- }
1758
- catch (_) { }
1759
- 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
+ }
1760
1780
  }
1761
1781
  }
1762
1782
  return this;
@@ -1772,20 +1792,12 @@ class PluginInstance {
1772
1792
  }
1773
1793
  }
1774
1794
  }
1795
+ deactivate() {
1796
+ this.spec = this.value = null;
1797
+ }
1775
1798
  }
1776
- PluginInstance.dummy = new PluginInstance(ViewPlugin.define(() => ({})));
1777
- function combineFacetAttrs(values) {
1778
- let result = {};
1779
- for (let i = values.length - 1; i >= 0; i--)
1780
- combineAttrs(values[i], result);
1781
- return result;
1782
- }
1783
- const editorAttributes = state.Facet.define({
1784
- combine: combineFacetAttrs
1785
- });
1786
- const contentAttributes = state.Facet.define({
1787
- combine: combineFacetAttrs
1788
- });
1799
+ const editorAttributes = state.Facet.define();
1800
+ const contentAttributes = state.Facet.define();
1789
1801
  // Provide decorations
1790
1802
  const decorations = state.Facet.define();
1791
1803
  const styleModule = state.Facet.define();
@@ -1933,12 +1945,12 @@ class DocView extends ContentView {
1933
1945
  this.compositionDeco = Decoration.none;
1934
1946
  this.decorations = [];
1935
1947
  // Track a minimum width for the editor. When measuring sizes in
1936
- // checkLayout, this is updated to point at the width of a given
1937
- // element and its extent in the document. When a change happens in
1938
- // that range, these are reset. That way, once we've seen a
1939
- // line/element of a given length, we keep the editor wide enough to
1940
- // fit at least that element, until it is changed, at which point we
1941
- // forget it again.
1948
+ // measureVisibleLineHeights, this is updated to point at the width
1949
+ // of a given element and its extent in the document. When a change
1950
+ // happens in that range, these are reset. That way, once we've seen
1951
+ // a line/element of a given length, we keep the editor wide enough
1952
+ // to fit at least that element, until it is changed, at which point
1953
+ // we forget it again.
1942
1954
  this.minWidth = 0;
1943
1955
  this.minWidthFrom = 0;
1944
1956
  this.minWidthTo = 0;
@@ -2008,7 +2020,7 @@ class DocView extends ContentView {
2008
2020
  this.updateSelection();
2009
2021
  }
2010
2022
  }
2011
- // Used both by update and checkLayout do perform the actual DOM
2023
+ // Used by update and the constructor do perform the actual DOM
2012
2024
  // update
2013
2025
  updateInner(changes, deco, oldLength) {
2014
2026
  this.view.viewState.mustMeasureContent = true;
@@ -2049,70 +2061,8 @@ class DocView extends ContentView {
2049
2061
  let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, deco);
2050
2062
  let { i: toI, off: toOff } = cursor.findPos(toA, 1);
2051
2063
  let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
2052
- this.replaceRange(fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
2053
- }
2054
- }
2055
- replaceRange(fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd) {
2056
- let before = this.children[fromI], last = content.length ? content[content.length - 1] : null;
2057
- let breakAtEnd = last ? last.breakAfter : breakAtStart;
2058
- // Change within a single line
2059
- if (fromI == toI && !breakAtStart && !breakAtEnd && content.length < 2 &&
2060
- before.merge(fromOff, toOff, content.length ? last : null, fromOff == 0, openStart, openEnd))
2061
- return;
2062
- let after = this.children[toI];
2063
- // Make sure the end of the line after the update is preserved in `after`
2064
- if (toOff < after.length) {
2065
- // If we're splitting a line, separate part of the start line to
2066
- // avoid that being mangled when updating the start line.
2067
- if (fromI == toI) {
2068
- after = after.split(toOff);
2069
- toOff = 0;
2070
- }
2071
- // If the element after the replacement should be merged with
2072
- // the last replacing element, update `content`
2073
- if (!breakAtEnd && last && after.merge(0, toOff, last, true, 0, openEnd)) {
2074
- content[content.length - 1] = after;
2075
- }
2076
- else {
2077
- // Remove the start of the after element, if necessary, and
2078
- // add it to `content`.
2079
- if (toOff)
2080
- after.merge(0, toOff, null, false, 0, openEnd);
2081
- content.push(after);
2082
- }
2064
+ replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
2083
2065
  }
2084
- else if (after.breakAfter) {
2085
- // The element at `toI` is entirely covered by this range.
2086
- // Preserve its line break, if any.
2087
- if (last)
2088
- last.breakAfter = 1;
2089
- else
2090
- breakAtStart = 1;
2091
- }
2092
- // Since we've handled the next element from the current elements
2093
- // now, make sure `toI` points after that.
2094
- toI++;
2095
- before.breakAfter = breakAtStart;
2096
- if (fromOff > 0) {
2097
- if (!breakAtStart && content.length && before.merge(fromOff, before.length, content[0], false, openStart, 0)) {
2098
- before.breakAfter = content.shift().breakAfter;
2099
- }
2100
- else if (fromOff < before.length || before.children.length && before.children[before.children.length - 1].length == 0) {
2101
- before.merge(fromOff, before.length, null, false, openStart, 0);
2102
- }
2103
- fromI++;
2104
- }
2105
- // Try to merge widgets on the boundaries of the replacement
2106
- while (fromI < toI && content.length) {
2107
- if (this.children[toI - 1].match(content[content.length - 1]))
2108
- toI--, content.pop();
2109
- else if (this.children[fromI].match(content[0]))
2110
- fromI++, content.shift();
2111
- else
2112
- break;
2113
- }
2114
- if (fromI < toI || content.length)
2115
- this.replaceChildren(fromI, toI, content);
2116
2066
  }
2117
2067
  // Sync the DOM selection to this.state.selection
2118
2068
  updateSelection(mustRead = false, fromPointer = false) {
@@ -2376,15 +2326,10 @@ function computeCompositionDeco(view, changes) {
2376
2326
  if (!textNode)
2377
2327
  return Decoration.none;
2378
2328
  let cView = view.docView.nearest(textNode);
2329
+ if (!cView)
2330
+ return Decoration.none;
2379
2331
  let from, to, topNode = textNode;
2380
- if (cView instanceof InlineView) {
2381
- while (cView.parent instanceof InlineView)
2382
- cView = cView.parent;
2383
- from = cView.posAtStart;
2384
- to = from + cView.length;
2385
- topNode = cView.dom;
2386
- }
2387
- else if (cView instanceof LineView) {
2332
+ if (cView instanceof LineView) {
2388
2333
  while (topNode.parentNode != cView.dom)
2389
2334
  topNode = topNode.parentNode;
2390
2335
  let prev = topNode.previousSibling;
@@ -2393,7 +2338,17 @@ function computeCompositionDeco(view, changes) {
2393
2338
  from = to = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
2394
2339
  }
2395
2340
  else {
2396
- return Decoration.none;
2341
+ for (;;) {
2342
+ let { parent } = cView;
2343
+ if (!parent)
2344
+ return Decoration.none;
2345
+ if (parent instanceof LineView)
2346
+ break;
2347
+ cView = parent;
2348
+ }
2349
+ from = cView.posAtStart;
2350
+ to = from + cView.length;
2351
+ topNode = cView.dom;
2397
2352
  }
2398
2353
  let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
2399
2354
  let text = textNode.nodeValue, { state } = view;
@@ -2931,7 +2886,7 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
2931
2886
  y = docTop + yOffset;
2932
2887
  let lineStart = block.from;
2933
2888
  // Clip x to the viewport sides
2934
- x = Math.max(content.left + 1, Math.min(content.right - 1, x));
2889
+ x = Math.max(content.left + 1, Math.min(Math.max(content.right, content.left + view.docView.minWidth) - 1, x));
2935
2890
  // If this is outside of the rendered viewport, we can't determine a position
2936
2891
  if (lineStart < view.viewport.from)
2937
2892
  return view.viewport.from == 0 ? 0 : posAtCoordsImprecise(view, content, block, x, y);
@@ -4174,12 +4129,12 @@ class HeightMapBranch extends HeightMap {
4174
4129
  get break() { return this.flags & 1 /* Break */; }
4175
4130
  blockAt(height, doc, top, offset) {
4176
4131
  let mid = top + this.left.height;
4177
- return height < mid || this.right.height == 0 ? this.left.blockAt(height, doc, top, offset)
4132
+ return height < mid ? this.left.blockAt(height, doc, top, offset)
4178
4133
  : this.right.blockAt(height, doc, mid, offset + this.left.length + this.break);
4179
4134
  }
4180
4135
  lineAt(value, type, doc, top, offset) {
4181
4136
  let rightTop = top + this.left.height, rightOffset = offset + this.left.length + this.break;
4182
- let left = type == QueryType.ByHeight ? value < rightTop || this.right.height == 0 : value < rightOffset;
4137
+ let left = type == QueryType.ByHeight ? value < rightTop : value < rightOffset;
4183
4138
  let base = left ? this.left.lineAt(value, type, doc, top, offset)
4184
4139
  : this.right.lineAt(value, type, doc, rightTop, rightOffset);
4185
4140
  if (this.break || (left ? base.to < rightOffset : base.from > rightOffset))
@@ -4320,7 +4275,9 @@ class NodeBuilder {
4320
4275
  }
4321
4276
  point(from, to, deco) {
4322
4277
  if (from < to || deco.heightRelevant) {
4323
- let height = deco.widget ? Math.max(0, deco.widget.estimatedHeight) : 0;
4278
+ let height = deco.widget ? deco.widget.estimatedHeight : 0;
4279
+ if (height < 0)
4280
+ height = this.oracle.lineHeight;
4324
4281
  let len = to - from;
4325
4282
  if (deco.block) {
4326
4283
  this.addBlock(new HeightMapBlock(len, height, deco.type));
@@ -4794,7 +4751,7 @@ class ViewState {
4794
4751
  return changed ? 4 /* Viewport */ : 0;
4795
4752
  }
4796
4753
  lineBlockAt(pos) {
4797
- return (pos >= this.viewport.from && pos <= this.viewport.to && this.viewportLines.find(b => b.from <= pos && b.to <= pos)) ||
4754
+ return (pos >= this.viewport.from && pos <= this.viewport.to && this.viewportLines.find(b => b.from <= pos && b.to >= pos)) ||
4798
4755
  scaleBlock(this.heightMap.lineAt(pos, QueryType.ByPos, this.state.doc, 0, 0), this.scaler);
4799
4756
  }
4800
4757
  lineBlockAtHeight(height) {
@@ -5065,7 +5022,7 @@ const baseTheme = buildTheme("." + baseThemeID, {
5065
5022
  color: "inherit",
5066
5023
  fontSize: "70%",
5067
5024
  padding: ".2em 1em",
5068
- borderRadius: "3px"
5025
+ borderRadius: "1px"
5069
5026
  },
5070
5027
  "&light .cm-button": {
5071
5028
  backgroundImage: "linear-gradient(#eff1f5, #d9d9df)",
@@ -5387,6 +5344,7 @@ class DOMObserver {
5387
5344
  for (let dom of this.scrollTargets)
5388
5345
  dom.removeEventListener("scroll", this.onScroll);
5389
5346
  window.removeEventListener("scroll", this.onScroll);
5347
+ this.dom.ownerDocument.removeEventListener("selectionchange", this.onSelectionChange);
5390
5348
  clearTimeout(this.parentCheck);
5391
5349
  clearTimeout(this.resizeTimeout);
5392
5350
  }
@@ -5683,6 +5641,7 @@ class EditorView {
5683
5641
  */
5684
5642
  config = {}) {
5685
5643
  this.plugins = [];
5644
+ this.pluginMap = new Map;
5686
5645
  this.editorAttrs = {};
5687
5646
  this.contentAttrs = {};
5688
5647
  this.bidiCache = [];
@@ -5714,7 +5673,9 @@ class EditorView {
5714
5673
  this.dispatch = this.dispatch.bind(this);
5715
5674
  this.root = (config.root || getRoot(config.parent) || document);
5716
5675
  this.viewState = new ViewState(config.state || state.EditorState.create());
5717
- this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec).update(this));
5676
+ this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec));
5677
+ for (let plugin of this.plugins)
5678
+ plugin.update(this);
5718
5679
  this.observer = new DOMObserver(this, (from, to, typeOver) => {
5719
5680
  applyDOMChange(this, from, to, typeOver);
5720
5681
  }, event => {
@@ -5851,7 +5812,10 @@ class EditorView {
5851
5812
  for (let plugin of this.plugins)
5852
5813
  plugin.destroy(this);
5853
5814
  this.viewState = new ViewState(newState);
5854
- this.plugins = newState.facet(viewPlugin).map(spec => new PluginInstance(spec).update(this));
5815
+ this.plugins = newState.facet(viewPlugin).map(spec => new PluginInstance(spec));
5816
+ this.pluginMap.clear();
5817
+ for (let plugin of this.plugins)
5818
+ plugin.update(this);
5855
5819
  this.docView = new DocView(this);
5856
5820
  this.inputState.ensureHandlers(this);
5857
5821
  this.mountStyles();
@@ -5882,6 +5846,7 @@ class EditorView {
5882
5846
  if (plugin.mustUpdate != update)
5883
5847
  plugin.destroy(this);
5884
5848
  this.plugins = newPlugins;
5849
+ this.pluginMap.clear();
5885
5850
  this.inputState.ensureHandlers(this);
5886
5851
  }
5887
5852
  else {
@@ -5889,7 +5854,7 @@ class EditorView {
5889
5854
  p.mustUpdate = update;
5890
5855
  }
5891
5856
  for (let i = 0; i < this.plugins.length; i++)
5892
- this.plugins[i] = this.plugins[i].update(this);
5857
+ this.plugins[i].update(this);
5893
5858
  }
5894
5859
  /**
5895
5860
  @internal
@@ -5978,7 +5943,7 @@ class EditorView {
5978
5943
  this.state.facet(theme);
5979
5944
  }
5980
5945
  updateAttrs() {
5981
- let editorAttrs = combineAttrs(this.state.facet(editorAttributes), {
5946
+ let editorAttrs = attrsFromFacet(this, editorAttributes, {
5982
5947
  class: "cm-editor" + (this.hasFocus ? " cm-focused " : " ") + this.themeClasses
5983
5948
  });
5984
5949
  let contentAttrs = {
@@ -5994,7 +5959,7 @@ class EditorView {
5994
5959
  };
5995
5960
  if (this.state.readOnly)
5996
5961
  contentAttrs["aria-readonly"] = "true";
5997
- combineAttrs(this.state.facet(contentAttributes), contentAttrs);
5962
+ attrsFromFacet(this, contentAttributes, contentAttrs);
5998
5963
  this.observer.ignore(() => {
5999
5964
  updateAttrs(this.contentDOM, this.contentAttrs, contentAttrs);
6000
5965
  updateAttrs(this.dom, this.editorAttrs, editorAttrs);
@@ -6063,10 +6028,10 @@ class EditorView {
6063
6028
  the return value of this method.
6064
6029
  */
6065
6030
  plugin(plugin) {
6066
- for (let inst of this.plugins)
6067
- if (inst.spec == plugin)
6068
- return inst.update(this).value;
6069
- return null;
6031
+ let known = this.pluginMap.get(plugin);
6032
+ if (known === undefined || known && known.spec != plugin)
6033
+ this.pluginMap.set(plugin, known = this.plugins.find(p => p.spec == plugin) || null);
6034
+ return known && known.update(this).value;
6070
6035
  }
6071
6036
  /**
6072
6037
  The top position of the document, in screen coordinates. This
@@ -6553,6 +6518,14 @@ class CachedOrder {
6553
6518
  return result;
6554
6519
  }
6555
6520
  }
6521
+ function attrsFromFacet(view, facet, base) {
6522
+ for (let sources = view.state.facet(facet), i = sources.length - 1; i >= 0; i--) {
6523
+ let source = sources[i], value = typeof source == "function" ? source(view) : source;
6524
+ if (value)
6525
+ combineAttrs(value, base);
6526
+ }
6527
+ return base;
6528
+ }
6556
6529
 
6557
6530
  const currentPlatform = browser.mac ? "mac" : browser.windows ? "win" : browser.linux ? "linux" : "key";
6558
6531
  function normalizeKeyName(name, platform) {
@@ -7205,35 +7178,29 @@ class TabWidget extends WidgetType {
7205
7178
  }
7206
7179
 
7207
7180
  const plugin = ViewPlugin.fromClass(class {
7208
- constructor(view) {
7209
- this.height = -1;
7210
- this.measure = {
7211
- read: view => Math.max(0, view.scrollDOM.clientHeight - view.defaultLineHeight),
7212
- write: (value, view) => {
7213
- if (Math.abs(value - this.height) > 1) {
7214
- this.height = value;
7215
- view.contentDOM.style.paddingBottom = value + "px";
7216
- }
7217
- }
7218
- };
7219
- view.requestMeasure(this.measure);
7181
+ constructor() {
7182
+ this.height = 1000;
7183
+ this.attrs = { style: "padding-bottom: 1000px" };
7220
7184
  }
7221
7185
  update(update) {
7222
- if (update.geometryChanged)
7223
- update.view.requestMeasure(this.measure);
7186
+ let height = update.view.viewState.editorHeight - update.view.defaultLineHeight;
7187
+ if (height != this.height) {
7188
+ this.height = height;
7189
+ this.attrs = { style: `padding-bottom: ${height}px` };
7190
+ }
7224
7191
  }
7225
7192
  });
7226
7193
  /**
7227
- Returns a plugin that makes sure the content has a bottom margin
7228
- equivalent to the height of the editor, minus one line height, so
7229
- that every line in the document can be scrolled to the top of the
7230
- editor.
7194
+ Returns an extension that makes sure the content has a bottom
7195
+ margin equivalent to the height of the editor, minus one line
7196
+ height, so that every line in the document can be scrolled to the
7197
+ top of the editor.
7231
7198
 
7232
7199
  This is only meaningful when the editor is scrollable, and should
7233
7200
  not be enabled in editors that take the size of their content.
7234
7201
  */
7235
7202
  function scrollPastEnd() {
7236
- return plugin;
7203
+ return [plugin, contentAttributes.of(view => { var _a; return ((_a = view.plugin(plugin)) === null || _a === void 0 ? void 0 : _a.attrs) || null; })];
7237
7204
  }
7238
7205
 
7239
7206
  /**
@@ -7308,9 +7275,7 @@ const __test = { HeightMap, HeightOracle, MeasuredHeights, QueryType, ChangedRan
7308
7275
 
7309
7276
  Object.defineProperty(exports, 'Range', {
7310
7277
  enumerable: true,
7311
- get: function () {
7312
- return rangeset.Range;
7313
- }
7278
+ get: function () { return rangeset.Range; }
7314
7279
  });
7315
7280
  exports.BidiSpan = BidiSpan;
7316
7281
  exports.BlockInfo = BlockInfo;