@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.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); }
@@ -752,10 +845,13 @@ class CompositionView extends WidgetView {
752
845
  coordsAt(pos, side) { return textCoords(this.widget.text, pos, side); }
753
846
  get isEditable() { return true; }
754
847
  }
848
+ // Use two characters on Android, to prevent Chrome from closing the
849
+ // virtual keyboard when backspacing after a widget (#602).
850
+ const ZeroWidthSpace = browser.android ? "\u200b\u200b" : "\u200b";
755
851
  // These are drawn around uneditable widgets to avoid a number of
756
852
  // browser bugs that show up when the cursor is directly next to
757
853
  // uneditable inline content.
758
- class WidgetBufferView extends InlineView {
854
+ class WidgetBufferView extends ContentView {
759
855
  constructor(side) {
760
856
  super();
761
857
  this.side = side;
@@ -765,15 +861,16 @@ class WidgetBufferView extends InlineView {
765
861
  become(other) {
766
862
  return other instanceof WidgetBufferView && other.side == this.side;
767
863
  }
768
- slice() { return new WidgetBufferView(this.side); }
864
+ split() { return new WidgetBufferView(this.side); }
769
865
  sync() {
770
866
  if (!this.dom)
771
- this.setDOM(document.createTextNode("\u200b"));
772
- else if (this.dirty && this.dom.nodeValue != "\u200b")
773
- this.dom.nodeValue = "\u200b";
867
+ this.setDOM(document.createTextNode(ZeroWidthSpace));
868
+ else if (this.dirty && this.dom.nodeValue != ZeroWidthSpace)
869
+ this.dom.nodeValue = ZeroWidthSpace;
774
870
  }
775
871
  getSide() { return this.side; }
776
872
  domAtPos(pos) { return DOMPos.before(this.dom); }
873
+ localPosFromDOM() { return 0; }
777
874
  domBoundsAround() { return null; }
778
875
  coordsAt(pos) {
779
876
  let rects = clientRectsFor(this.dom);
@@ -783,90 +880,7 @@ class WidgetBufferView extends InlineView {
783
880
  return text.Text.of([this.dom.nodeValue.replace(/\u200b/g, "")]);
784
881
  }
785
882
  }
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
- }
883
+ TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren;
870
884
  function inlineDOMAtPos(dom, children, pos) {
871
885
  let i = 0;
872
886
  for (let off = 0; i < children.length; i++) {
@@ -1007,6 +1021,11 @@ class WidgetType {
1007
1021
  @internal
1008
1022
  */
1009
1023
  get customView() { return null; }
1024
+ /**
1025
+ This is called when the an instance of the widget is removed
1026
+ from the editor view.
1027
+ */
1028
+ destroy(_dom) { }
1010
1029
  }
1011
1030
  /**
1012
1031
  The different types of blocks that can occur in an editor view.
@@ -1086,8 +1105,9 @@ class Decoration extends rangeset.RangeValue {
1086
1105
  position.
1087
1106
  */
1088
1107
  static widget(spec) {
1089
- let side = spec.side || 0;
1090
- return new PointDecoration(spec, side, side, !!spec.block, spec.widget || null, false);
1108
+ let side = spec.side || 0, block = !!spec.block;
1109
+ side += block ? (side > 0 ? 300000000 /* BlockAfter */ : -400000000 /* BlockBefore */) : (side > 0 ? 100000000 /* InlineAfter */ : -100000000 /* InlineBefore */);
1110
+ return new PointDecoration(spec, side, side, block, spec.widget || null, false);
1091
1111
  }
1092
1112
  /**
1093
1113
  Create a replace decoration which replaces the given range with
@@ -1096,8 +1116,8 @@ class Decoration extends rangeset.RangeValue {
1096
1116
  static replace(spec) {
1097
1117
  let block = !!spec.block;
1098
1118
  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);
1119
+ let startSide = block ? (start ? -300000000 /* BlockIncStart */ : -1 /* InlineIncStart */) : 400000000 /* NonIncStart */;
1120
+ let endSide = block ? (end ? 200000000 /* BlockIncEnd */ : 1 /* InlineIncEnd */) : -500000000 /* NonIncEnd */;
1101
1121
  return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
1102
1122
  }
1103
1123
  /**
@@ -1127,7 +1147,7 @@ Decoration.none = rangeset.RangeSet.empty;
1127
1147
  class MarkDecoration extends Decoration {
1128
1148
  constructor(spec) {
1129
1149
  let { start, end } = getInclusive(spec);
1130
- super(100000000 /* Big */ * (start ? -1 : 1), 100000000 /* Big */ * (end ? 1 : -1), null, spec);
1150
+ super(start ? -1 /* InlineIncStart */ : 400000000 /* NonIncStart */, end ? 1 /* InlineIncEnd */ : -500000000 /* NonIncEnd */, null, spec);
1131
1151
  this.tagName = spec.tagName || "span";
1132
1152
  this.class = spec.class || "";
1133
1153
  this.attrs = spec.attributes || null;
@@ -1148,7 +1168,7 @@ class MarkDecoration extends Decoration {
1148
1168
  MarkDecoration.prototype.point = false;
1149
1169
  class LineDecoration extends Decoration {
1150
1170
  constructor(spec) {
1151
- super(-100000000 /* Big */, -100000000 /* Big */, null, spec);
1171
+ super(-200000000 /* Line */, -200000000 /* Line */, null, spec);
1152
1172
  }
1153
1173
  eq(other) {
1154
1174
  return other instanceof LineDecoration && attrsEq(this.spec.attributes, other.spec.attributes);
@@ -1166,12 +1186,12 @@ class PointDecoration extends Decoration {
1166
1186
  super(startSide, endSide, widget, spec);
1167
1187
  this.block = block;
1168
1188
  this.isReplace = isReplace;
1169
- this.mapMode = !block ? state.MapMode.TrackDel : startSide < 0 ? state.MapMode.TrackBefore : state.MapMode.TrackAfter;
1189
+ this.mapMode = !block ? state.MapMode.TrackDel : startSide <= 0 ? state.MapMode.TrackBefore : state.MapMode.TrackAfter;
1170
1190
  }
1171
1191
  // Only relevant when this.block == true
1172
1192
  get type() {
1173
1193
  return this.startSide < this.endSide ? exports.BlockType.WidgetRange
1174
- : this.startSide < 0 ? exports.BlockType.WidgetBefore : exports.BlockType.WidgetAfter;
1194
+ : this.startSide <= 0 ? exports.BlockType.WidgetBefore : exports.BlockType.WidgetAfter;
1175
1195
  }
1176
1196
  get heightRelevant() { return this.block || !!this.widget && this.widget.estimatedHeight >= 5; }
1177
1197
  eq(other) {
@@ -1181,7 +1201,7 @@ class PointDecoration extends Decoration {
1181
1201
  this.startSide == other.startSide && this.endSide == other.endSide;
1182
1202
  }
1183
1203
  range(from, to = from) {
1184
- if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide < 0)))
1204
+ if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide <= 0)))
1185
1205
  throw new RangeError("Invalid range for replacement decoration");
1186
1206
  if (!this.isReplace && to != from)
1187
1207
  throw new RangeError("Widget decorations can only have zero-length ranges");
@@ -1218,16 +1238,16 @@ class LineView extends ContentView {
1218
1238
  this.breakAfter = 0;
1219
1239
  }
1220
1240
  // Consumes source
1221
- merge(from, to, source, takeDeco, openStart, openEnd) {
1241
+ merge(from, to, source, hasStart, openStart, openEnd) {
1222
1242
  if (source) {
1223
1243
  if (!(source instanceof LineView))
1224
1244
  return false;
1225
1245
  if (!this.dom)
1226
1246
  source.transferDOM(this); // Reuse source.dom when appropriate
1227
1247
  }
1228
- if (takeDeco)
1248
+ if (hasStart)
1229
1249
  this.setDeco(source ? source.attrs : null);
1230
- mergeInlineChildren(this, from, to, source ? source.children : none$1, openStart, openEnd);
1250
+ mergeChildrenInto(this, from, to, source ? source.children : [], openStart, openEnd);
1231
1251
  return true;
1232
1252
  }
1233
1253
  split(at) {
@@ -1237,16 +1257,14 @@ class LineView extends ContentView {
1237
1257
  return end;
1238
1258
  let { i, off } = this.childPos(at);
1239
1259
  if (off) {
1240
- end.append(this.children[i].slice(off), 0);
1241
- this.children[i].merge(off, this.children[i].length, null, 0, 0);
1260
+ end.append(this.children[i].split(off), 0);
1261
+ this.children[i].merge(off, this.children[i].length, null, false, 0, 0);
1242
1262
  i++;
1243
1263
  }
1244
1264
  for (let j = i; j < this.children.length; j++)
1245
1265
  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
- }
1266
+ while (i > 0 && this.children[i - 1].length == 0)
1267
+ this.children[--i].destroy();
1250
1268
  this.children.length = i;
1251
1269
  this.markDirty();
1252
1270
  this.length = at;
@@ -1269,7 +1287,6 @@ class LineView extends ContentView {
1269
1287
  this.attrs = attrs;
1270
1288
  }
1271
1289
  }
1272
- // Only called when building a line view in ContentBuilder
1273
1290
  append(child, openStart) {
1274
1291
  joinInlineInto(this, child, openStart);
1275
1292
  }
@@ -1326,7 +1343,7 @@ class LineView extends ContentView {
1326
1343
  coordsAt(pos, side) {
1327
1344
  return coordsInChildren(this, pos, side);
1328
1345
  }
1329
- match(_other) { return false; }
1346
+ become(_other) { return false; }
1330
1347
  get type() { return exports.BlockType.Text; }
1331
1348
  static find(docView, pos) {
1332
1349
  for (let i = 0, off = 0;; i++) {
@@ -1341,7 +1358,6 @@ class LineView extends ContentView {
1341
1358
  }
1342
1359
  }
1343
1360
  }
1344
- const none$1 = [];
1345
1361
  class BlockWidgetView extends ContentView {
1346
1362
  constructor(widget, length, type) {
1347
1363
  super();
@@ -1367,7 +1383,7 @@ class BlockWidgetView extends ContentView {
1367
1383
  end.breakAfter = this.breakAfter;
1368
1384
  return end;
1369
1385
  }
1370
- get children() { return none$1; }
1386
+ get children() { return noChildren; }
1371
1387
  sync() {
1372
1388
  if (!this.dom || !this.widget.updateDOM(this.dom)) {
1373
1389
  this.setDOM(this.widget.toDOM(this.editorView));
@@ -1378,7 +1394,7 @@ class BlockWidgetView extends ContentView {
1378
1394
  return this.parent ? this.parent.view.state.doc.slice(this.posAtStart, this.posAtEnd) : state.Text.empty;
1379
1395
  }
1380
1396
  domBoundsAround() { return null; }
1381
- match(other) {
1397
+ become(other) {
1382
1398
  if (other instanceof BlockWidgetView && other.type == this.type &&
1383
1399
  other.widget.constructor == this.widget.constructor) {
1384
1400
  if (!other.widget.eq(this.widget))
@@ -1392,6 +1408,11 @@ class BlockWidgetView extends ContentView {
1392
1408
  }
1393
1409
  ignoreMutation() { return true; }
1394
1410
  ignoreEvent(event) { return this.widget.ignoreEvent(event); }
1411
+ destroy() {
1412
+ super.destroy();
1413
+ if (this.dom)
1414
+ this.widget.destroy(this.dom);
1415
+ }
1395
1416
  }
1396
1417
 
1397
1418
  class ContentBuilder {
@@ -1727,36 +1748,39 @@ class PluginInstance {
1727
1748
  this.value = null;
1728
1749
  }
1729
1750
  takeField(type, target) {
1730
- for (let { field, get } of this.spec.fields)
1731
- if (field == type)
1732
- target.push(get(this.value));
1751
+ if (this.spec)
1752
+ for (let { field, get } of this.spec.fields)
1753
+ if (field == type)
1754
+ target.push(get(this.value));
1733
1755
  }
1734
1756
  update(view) {
1735
1757
  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;
1758
+ if (this.spec) {
1759
+ try {
1760
+ this.value = this.spec.create(view);
1761
+ }
1762
+ catch (e) {
1763
+ logException(view.state, e, "CodeMirror plugin crashed");
1764
+ this.deactivate();
1765
+ }
1742
1766
  }
1743
1767
  }
1744
1768
  else if (this.mustUpdate) {
1745
1769
  let update = this.mustUpdate;
1746
1770
  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;
1771
+ if (this.value.update) {
1772
+ try {
1773
+ this.value.update(update);
1774
+ }
1775
+ catch (e) {
1776
+ logException(update.state, e, "CodeMirror plugin crashed");
1777
+ if (this.value.destroy)
1778
+ try {
1779
+ this.value.destroy();
1780
+ }
1781
+ catch (_) { }
1782
+ this.deactivate();
1783
+ }
1760
1784
  }
1761
1785
  }
1762
1786
  return this;
@@ -1772,20 +1796,12 @@ class PluginInstance {
1772
1796
  }
1773
1797
  }
1774
1798
  }
1799
+ deactivate() {
1800
+ this.spec = this.value = null;
1801
+ }
1775
1802
  }
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
- });
1803
+ const editorAttributes = state.Facet.define();
1804
+ const contentAttributes = state.Facet.define();
1789
1805
  // Provide decorations
1790
1806
  const decorations = state.Facet.define();
1791
1807
  const styleModule = state.Facet.define();
@@ -1926,196 +1942,446 @@ class ViewUpdate {
1926
1942
  get empty() { return this.flags == 0 && this.transactions.length == 0; }
1927
1943
  }
1928
1944
 
1929
- class DocView extends ContentView {
1930
- constructor(view) {
1931
- super();
1932
- this.view = view;
1933
- this.compositionDeco = Decoration.none;
1934
- this.decorations = [];
1935
- // 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.
1942
- this.minWidth = 0;
1943
- this.minWidthFrom = 0;
1944
- this.minWidthTo = 0;
1945
- // Track whether the DOM selection was set in a lossy way, so that
1946
- // we don't mess it up when reading it back it
1947
- this.impreciseAnchor = null;
1948
- this.impreciseHead = null;
1949
- this.forceSelection = false;
1950
- // Used by the resize observer to ignore resizes that we caused
1951
- // ourselves
1952
- this.lastUpdate = Date.now();
1953
- this.setDOM(view.contentDOM);
1954
- this.children = [new LineView];
1955
- this.children[0].setParent(this);
1956
- this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], this.updateDeco(), 0);
1945
+ /**
1946
+ Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
1947
+ */
1948
+ exports.Direction = void 0;
1949
+ (function (Direction) {
1950
+ // (These are chosen to match the base levels, in bidi algorithm
1951
+ // terms, of spans in that direction.)
1952
+ /**
1953
+ Left-to-right.
1954
+ */
1955
+ Direction[Direction["LTR"] = 0] = "LTR";
1956
+ /**
1957
+ Right-to-left.
1958
+ */
1959
+ Direction[Direction["RTL"] = 1] = "RTL";
1960
+ })(exports.Direction || (exports.Direction = {}));
1961
+ const LTR = exports.Direction.LTR, RTL = exports.Direction.RTL;
1962
+ // Decode a string with each type encoded as log2(type)
1963
+ function dec(str) {
1964
+ let result = [];
1965
+ for (let i = 0; i < str.length; i++)
1966
+ result.push(1 << +str[i]);
1967
+ return result;
1968
+ }
1969
+ // Character types for codepoints 0 to 0xf8
1970
+ const LowTypes = dec("88888888888888888888888888888888888666888888787833333333337888888000000000000000000000000008888880000000000000000000000000088888888888888888888888888888888888887866668888088888663380888308888800000000000000000000000800000000000000000000000000000008");
1971
+ // Character types for codepoints 0x600 to 0x6f9
1972
+ const ArabicTypes = dec("4444448826627288999999999992222222222222222222222222222222222222222222222229999999999999999999994444444444644222822222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222999999949999999229989999223333333333");
1973
+ const Brackets = Object.create(null), BracketStack = [];
1974
+ // There's a lot more in
1975
+ // https://www.unicode.org/Public/UCD/latest/ucd/BidiBrackets.txt,
1976
+ // which are left out to keep code size down.
1977
+ for (let p of ["()", "[]", "{}"]) {
1978
+ let l = p.charCodeAt(0), r = p.charCodeAt(1);
1979
+ Brackets[l] = r;
1980
+ Brackets[r] = -l;
1981
+ }
1982
+ function charType(ch) {
1983
+ return ch <= 0xf7 ? LowTypes[ch] :
1984
+ 0x590 <= ch && ch <= 0x5f4 ? 2 /* R */ :
1985
+ 0x600 <= ch && ch <= 0x6f9 ? ArabicTypes[ch - 0x600] :
1986
+ 0x6ee <= ch && ch <= 0x8ac ? 4 /* AL */ :
1987
+ 0x2000 <= ch && ch <= 0x200b ? 256 /* NI */ :
1988
+ ch == 0x200c ? 256 /* NI */ : 1 /* L */;
1989
+ }
1990
+ const BidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
1991
+ /**
1992
+ Represents a contiguous range of text that has a single direction
1993
+ (as in left-to-right or right-to-left).
1994
+ */
1995
+ class BidiSpan {
1996
+ /**
1997
+ @internal
1998
+ */
1999
+ constructor(
2000
+ /**
2001
+ The start of the span (relative to the start of the line).
2002
+ */
2003
+ from,
2004
+ /**
2005
+ The end of the span.
2006
+ */
2007
+ to,
2008
+ /**
2009
+ The ["bidi
2010
+ level"](https://unicode.org/reports/tr9/#Basic_Display_Algorithm)
2011
+ of the span (in this context, 0 means
2012
+ left-to-right, 1 means right-to-left, 2 means left-to-right
2013
+ number inside right-to-left text).
2014
+ */
2015
+ level) {
2016
+ this.from = from;
2017
+ this.to = to;
2018
+ this.level = level;
1957
2019
  }
1958
- get root() { return this.view.root; }
1959
- get editorView() { return this.view; }
1960
- get length() { return this.view.state.doc.length; }
1961
- // Update the document view to a given state. scrollIntoView can be
1962
- // used as a hint to compute a new viewport that includes that
1963
- // position, if we know the editor is going to scroll that position
1964
- // into view.
1965
- update(update) {
1966
- let changedRanges = update.changedRanges;
1967
- if (this.minWidth > 0 && changedRanges.length) {
1968
- if (!changedRanges.every(({ fromA, toA }) => toA < this.minWidthFrom || fromA > this.minWidthTo)) {
1969
- this.minWidth = 0;
1970
- }
1971
- else {
1972
- this.minWidthFrom = update.changes.mapPos(this.minWidthFrom, 1);
1973
- this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
1974
- }
1975
- }
1976
- if (this.view.inputState.composing < 0)
1977
- this.compositionDeco = Decoration.none;
1978
- else if (update.transactions.length)
1979
- this.compositionDeco = computeCompositionDeco(this.view, update.changes);
1980
- // When the DOM nodes around the selection are moved to another
1981
- // parent, Chrome sometimes reports a different selection through
1982
- // getSelection than the one that it actually shows to the user.
1983
- // This forces a selection update when lines are joined to work
1984
- // around that. Issue #54
1985
- if ((browser.ie || browser.chrome) && !this.compositionDeco.size && update &&
1986
- update.state.doc.lines != update.startState.doc.lines)
1987
- this.forceSelection = true;
1988
- let prevDeco = this.decorations, deco = this.updateDeco();
1989
- let decoDiff = findChangedDeco(prevDeco, deco, update.changes);
1990
- changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
1991
- if (this.dirty == 0 /* Not */ && changedRanges.length == 0) {
1992
- return false;
1993
- }
1994
- else {
1995
- this.updateInner(changedRanges, deco, update.startState.doc.length);
1996
- if (update.transactions.length)
1997
- this.lastUpdate = Date.now();
1998
- return true;
2020
+ /**
2021
+ The direction of this span.
2022
+ */
2023
+ get dir() { return this.level % 2 ? RTL : LTR; }
2024
+ /**
2025
+ @internal
2026
+ */
2027
+ side(end, dir) { return (this.dir == dir) == end ? this.to : this.from; }
2028
+ /**
2029
+ @internal
2030
+ */
2031
+ static find(order, index, level, assoc) {
2032
+ let maybe = -1;
2033
+ for (let i = 0; i < order.length; i++) {
2034
+ let span = order[i];
2035
+ if (span.from <= index && span.to >= index) {
2036
+ if (span.level == level)
2037
+ return i;
2038
+ // When multiple spans match, if assoc != 0, take the one that
2039
+ // covers that side, otherwise take the one with the minimum
2040
+ // level.
2041
+ if (maybe < 0 || (assoc != 0 ? (assoc < 0 ? span.from < index : span.to > index) : order[maybe].level > span.level))
2042
+ maybe = i;
2043
+ }
1999
2044
  }
2045
+ if (maybe < 0)
2046
+ throw new RangeError("Index out of range");
2047
+ return maybe;
2000
2048
  }
2001
- reset(sel) {
2002
- if (this.dirty) {
2003
- this.view.observer.ignore(() => this.view.docView.sync());
2004
- this.dirty = 0 /* Not */;
2005
- this.updateSelection(true);
2049
+ }
2050
+ // Reused array of character types
2051
+ const types = [];
2052
+ function computeOrder(line, direction) {
2053
+ let len = line.length, outerType = direction == LTR ? 1 /* L */ : 2 /* R */, oppositeType = direction == LTR ? 2 /* R */ : 1 /* L */;
2054
+ if (!line || outerType == 1 /* L */ && !BidiRE.test(line))
2055
+ return trivialOrder(len);
2056
+ // W1. Examine each non-spacing mark (NSM) in the level run, and
2057
+ // change the type of the NSM to the type of the previous
2058
+ // character. If the NSM is at the start of the level run, it will
2059
+ // get the type of sor.
2060
+ // W2. Search backwards from each instance of a European number
2061
+ // until the first strong type (R, L, AL, or sor) is found. If an
2062
+ // AL is found, change the type of the European number to Arabic
2063
+ // number.
2064
+ // W3. Change all ALs to R.
2065
+ // (Left after this: L, R, EN, AN, ET, CS, NI)
2066
+ for (let i = 0, prev = outerType, prevStrong = outerType; i < len; i++) {
2067
+ let type = charType(line.charCodeAt(i));
2068
+ if (type == 512 /* NSM */)
2069
+ type = prev;
2070
+ else if (type == 8 /* EN */ && prevStrong == 4 /* AL */)
2071
+ type = 16 /* AN */;
2072
+ types[i] = type == 4 /* AL */ ? 2 /* R */ : type;
2073
+ if (type & 7 /* Strong */)
2074
+ prevStrong = type;
2075
+ prev = type;
2076
+ }
2077
+ // W5. A sequence of European terminators adjacent to European
2078
+ // numbers changes to all European numbers.
2079
+ // W6. Otherwise, separators and terminators change to Other
2080
+ // Neutral.
2081
+ // W7. Search backwards from each instance of a European number
2082
+ // until the first strong type (R, L, or sor) is found. If an L is
2083
+ // found, then change the type of the European number to L.
2084
+ // (Left after this: L, R, EN+AN, NI)
2085
+ for (let i = 0, prev = outerType, prevStrong = outerType; i < len; i++) {
2086
+ let type = types[i];
2087
+ if (type == 128 /* CS */) {
2088
+ if (i < len - 1 && prev == types[i + 1] && (prev & 24 /* Num */))
2089
+ type = types[i] = prev;
2090
+ else
2091
+ types[i] = 256 /* NI */;
2006
2092
  }
2007
- else {
2008
- this.updateSelection();
2093
+ else if (type == 64 /* ET */) {
2094
+ let end = i + 1;
2095
+ while (end < len && types[end] == 64 /* ET */)
2096
+ end++;
2097
+ let replace = (i && prev == 8 /* EN */) || (end < len && types[end] == 8 /* EN */) ? (prevStrong == 1 /* L */ ? 1 /* L */ : 8 /* EN */) : 256 /* NI */;
2098
+ for (let j = i; j < end; j++)
2099
+ types[j] = replace;
2100
+ i = end - 1;
2009
2101
  }
2010
- }
2011
- // Used both by update and checkLayout do perform the actual DOM
2012
- // update
2013
- updateInner(changes, deco, oldLength) {
2014
- this.view.viewState.mustMeasureContent = true;
2015
- this.updateChildren(changes, deco, oldLength);
2016
- let { observer } = this.view;
2017
- observer.ignore(() => {
2018
- // Lock the height during redrawing, since Chrome sometimes
2019
- // messes with the scroll position during DOM mutation (though
2020
- // no relayout is triggered and I cannot imagine how it can
2021
- // recompute the scroll position without a layout)
2022
- this.dom.style.height = this.view.viewState.contentHeight + "px";
2023
- this.dom.style.minWidth = this.minWidth ? this.minWidth + "px" : "";
2024
- // Chrome will sometimes, when DOM mutations occur directly
2025
- // around the selection, get confused and report a different
2026
- // selection from the one it displays (issue #218). This tries
2027
- // to detect that situation.
2028
- let track = browser.chrome || browser.ios ? { node: observer.selectionRange.focusNode, written: false } : undefined;
2029
- this.sync(track);
2030
- this.dirty = 0 /* Not */;
2031
- if (track && (track.written || observer.selectionRange.focusNode != track.node))
2032
- this.forceSelection = true;
2033
- this.dom.style.height = "";
2034
- });
2035
- let gaps = [];
2036
- if (this.view.viewport.from || this.view.viewport.to < this.view.state.doc.length)
2037
- for (let child of this.children)
2038
- if (child instanceof BlockWidgetView && child.widget instanceof BlockGapWidget)
2039
- gaps.push(child.dom);
2040
- observer.updateGaps(gaps);
2041
- }
2042
- updateChildren(changes, deco, oldLength) {
2043
- let cursor = this.childCursor(oldLength);
2044
- for (let i = changes.length - 1;; i--) {
2045
- let next = i >= 0 ? changes[i] : null;
2046
- if (!next)
2047
- break;
2048
- let { fromA, toA, fromB, toB } = next;
2049
- let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, deco);
2050
- let { i: toI, off: toOff } = cursor.findPos(toA, 1);
2051
- let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
2052
- this.replaceRange(fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
2102
+ else if (type == 8 /* EN */ && prevStrong == 1 /* L */) {
2103
+ types[i] = 1 /* L */;
2053
2104
  }
2105
+ prev = type;
2106
+ if (type & 7 /* Strong */)
2107
+ prevStrong = type;
2054
2108
  }
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;
2109
+ // N0. Process bracket pairs in an isolating run sequence
2110
+ // sequentially in the logical order of the text positions of the
2111
+ // opening paired brackets using the logic given below. Within this
2112
+ // scope, bidirectional types EN and AN are treated as R.
2113
+ for (let i = 0, sI = 0, context = 0, ch, br, type; i < len; i++) {
2114
+ // Keeps [startIndex, type, strongSeen] triples for each open
2115
+ // bracket on BracketStack.
2116
+ if (br = Brackets[ch = line.charCodeAt(i)]) {
2117
+ if (br < 0) { // Closing bracket
2118
+ for (let sJ = sI - 3; sJ >= 0; sJ -= 3) {
2119
+ if (BracketStack[sJ + 1] == -br) {
2120
+ let flags = BracketStack[sJ + 2];
2121
+ let type = (flags & 2 /* EmbedInside */) ? outerType :
2122
+ !(flags & 4 /* OppositeInside */) ? 0 :
2123
+ (flags & 1 /* OppositeBefore */) ? oppositeType : outerType;
2124
+ if (type)
2125
+ types[i] = types[BracketStack[sJ]] = type;
2126
+ sI = sJ;
2127
+ break;
2128
+ }
2129
+ }
2070
2130
  }
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;
2131
+ else if (BracketStack.length == 189 /* MaxDepth */) {
2132
+ break;
2075
2133
  }
2076
2134
  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);
2135
+ BracketStack[sI++] = i;
2136
+ BracketStack[sI++] = ch;
2137
+ BracketStack[sI++] = context;
2082
2138
  }
2083
2139
  }
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;
2140
+ else if ((type = types[i]) == 2 /* R */ || type == 1 /* L */) {
2141
+ let embed = type == outerType;
2142
+ context = embed ? 0 : 1 /* OppositeBefore */;
2143
+ for (let sJ = sI - 3; sJ >= 0; sJ -= 3) {
2144
+ let cur = BracketStack[sJ + 2];
2145
+ if (cur & 2 /* EmbedInside */)
2146
+ break;
2147
+ if (embed) {
2148
+ BracketStack[sJ + 2] |= 2 /* EmbedInside */;
2149
+ }
2150
+ else {
2151
+ if (cur & 4 /* OppositeInside */)
2152
+ break;
2153
+ BracketStack[sJ + 2] |= 4 /* OppositeInside */;
2154
+ }
2155
+ }
2091
2156
  }
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;
2157
+ }
2158
+ // N1. A sequence of neutrals takes the direction of the
2159
+ // surrounding strong text if the text on both sides has the same
2160
+ // direction. European and Arabic numbers act as if they were R in
2161
+ // terms of their influence on neutrals. Start-of-level-run (sor)
2162
+ // and end-of-level-run (eor) are used at level run boundaries.
2163
+ // N2. Any remaining neutrals take the embedding direction.
2164
+ // (Left after this: L, R, EN+AN)
2165
+ for (let i = 0; i < len; i++) {
2166
+ if (types[i] == 256 /* NI */) {
2167
+ let end = i + 1;
2168
+ while (end < len && types[end] == 256 /* NI */)
2169
+ end++;
2170
+ let beforeL = (i ? types[i - 1] : outerType) == 1 /* L */;
2171
+ let afterL = (end < len ? types[end] : outerType) == 1 /* L */;
2172
+ let replace = beforeL == afterL ? (beforeL ? 1 /* L */ : 2 /* R */) : outerType;
2173
+ for (let j = i; j < end; j++)
2174
+ types[j] = replace;
2175
+ i = end - 1;
2176
+ }
2177
+ }
2178
+ // Here we depart from the documented algorithm, in order to avoid
2179
+ // building up an actual levels array. Since there are only three
2180
+ // levels (0, 1, 2) in an implementation that doesn't take
2181
+ // explicit embedding into account, we can build up the order on
2182
+ // the fly, without following the level-based algorithm.
2183
+ let order = [];
2184
+ if (outerType == 1 /* L */) {
2185
+ for (let i = 0; i < len;) {
2186
+ let start = i, rtl = types[i++] != 1 /* L */;
2187
+ while (i < len && rtl == (types[i] != 1 /* L */))
2188
+ i++;
2189
+ if (rtl) {
2190
+ for (let j = i; j > start;) {
2191
+ let end = j, l = types[--j] != 2 /* R */;
2192
+ while (j > start && l == (types[j - 1] != 2 /* R */))
2193
+ j--;
2194
+ order.push(new BidiSpan(j, end, l ? 2 : 1));
2195
+ }
2099
2196
  }
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);
2197
+ else {
2198
+ order.push(new BidiSpan(start, i, 0));
2102
2199
  }
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
2200
  }
2114
- if (fromI < toI || content.length)
2115
- this.replaceChildren(fromI, toI, content);
2116
2201
  }
2117
- // Sync the DOM selection to this.state.selection
2118
- updateSelection(mustRead = false, fromPointer = false) {
2202
+ else {
2203
+ for (let i = 0; i < len;) {
2204
+ let start = i, rtl = types[i++] == 2 /* R */;
2205
+ while (i < len && rtl == (types[i] == 2 /* R */))
2206
+ i++;
2207
+ order.push(new BidiSpan(start, i, rtl ? 1 : 2));
2208
+ }
2209
+ }
2210
+ return order;
2211
+ }
2212
+ function trivialOrder(length) {
2213
+ return [new BidiSpan(0, length, 0)];
2214
+ }
2215
+ let movedOver = "";
2216
+ function moveVisually(line, order, dir, start, forward) {
2217
+ var _a;
2218
+ let startIndex = start.head - line.from, spanI = -1;
2219
+ if (startIndex == 0) {
2220
+ if (!forward || !line.length)
2221
+ return null;
2222
+ if (order[0].level != dir) {
2223
+ startIndex = order[0].side(false, dir);
2224
+ spanI = 0;
2225
+ }
2226
+ }
2227
+ else if (startIndex == line.length) {
2228
+ if (forward)
2229
+ return null;
2230
+ let last = order[order.length - 1];
2231
+ if (last.level != dir) {
2232
+ startIndex = last.side(true, dir);
2233
+ spanI = order.length - 1;
2234
+ }
2235
+ }
2236
+ if (spanI < 0)
2237
+ spanI = BidiSpan.find(order, startIndex, (_a = start.bidiLevel) !== null && _a !== void 0 ? _a : -1, start.assoc);
2238
+ let span = order[spanI];
2239
+ // End of span. (But not end of line--that was checked for above.)
2240
+ if (startIndex == span.side(forward, dir)) {
2241
+ span = order[spanI += forward ? 1 : -1];
2242
+ startIndex = span.side(!forward, dir);
2243
+ }
2244
+ let indexForward = forward == (span.dir == dir);
2245
+ let nextIndex = text.findClusterBreak(line.text, startIndex, indexForward);
2246
+ movedOver = line.text.slice(Math.min(startIndex, nextIndex), Math.max(startIndex, nextIndex));
2247
+ if (nextIndex != span.side(forward, dir))
2248
+ return state.EditorSelection.cursor(nextIndex + line.from, indexForward ? -1 : 1, span.level);
2249
+ let nextSpan = spanI == (forward ? order.length - 1 : 0) ? null : order[spanI + (forward ? 1 : -1)];
2250
+ if (!nextSpan && span.level != dir)
2251
+ return state.EditorSelection.cursor(forward ? line.to : line.from, forward ? -1 : 1, dir);
2252
+ if (nextSpan && nextSpan.level < span.level)
2253
+ return state.EditorSelection.cursor(nextSpan.side(!forward, dir) + line.from, forward ? 1 : -1, nextSpan.level);
2254
+ return state.EditorSelection.cursor(nextIndex + line.from, forward ? -1 : 1, span.level);
2255
+ }
2256
+
2257
+ class DocView extends ContentView {
2258
+ constructor(view) {
2259
+ super();
2260
+ this.view = view;
2261
+ this.compositionDeco = Decoration.none;
2262
+ this.decorations = [];
2263
+ // Track a minimum width for the editor. When measuring sizes in
2264
+ // measureVisibleLineHeights, this is updated to point at the width
2265
+ // of a given element and its extent in the document. When a change
2266
+ // happens in that range, these are reset. That way, once we've seen
2267
+ // a line/element of a given length, we keep the editor wide enough
2268
+ // to fit at least that element, until it is changed, at which point
2269
+ // we forget it again.
2270
+ this.minWidth = 0;
2271
+ this.minWidthFrom = 0;
2272
+ this.minWidthTo = 0;
2273
+ // Track whether the DOM selection was set in a lossy way, so that
2274
+ // we don't mess it up when reading it back it
2275
+ this.impreciseAnchor = null;
2276
+ this.impreciseHead = null;
2277
+ this.forceSelection = false;
2278
+ // Used by the resize observer to ignore resizes that we caused
2279
+ // ourselves
2280
+ this.lastUpdate = Date.now();
2281
+ this.setDOM(view.contentDOM);
2282
+ this.children = [new LineView];
2283
+ this.children[0].setParent(this);
2284
+ this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], this.updateDeco(), 0);
2285
+ }
2286
+ get root() { return this.view.root; }
2287
+ get editorView() { return this.view; }
2288
+ get length() { return this.view.state.doc.length; }
2289
+ // Update the document view to a given state. scrollIntoView can be
2290
+ // used as a hint to compute a new viewport that includes that
2291
+ // position, if we know the editor is going to scroll that position
2292
+ // into view.
2293
+ update(update) {
2294
+ let changedRanges = update.changedRanges;
2295
+ if (this.minWidth > 0 && changedRanges.length) {
2296
+ if (!changedRanges.every(({ fromA, toA }) => toA < this.minWidthFrom || fromA > this.minWidthTo)) {
2297
+ this.minWidth = this.minWidthFrom = this.minWidthTo = 0;
2298
+ }
2299
+ else {
2300
+ this.minWidthFrom = update.changes.mapPos(this.minWidthFrom, 1);
2301
+ this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
2302
+ }
2303
+ }
2304
+ if (this.view.inputState.composing < 0)
2305
+ this.compositionDeco = Decoration.none;
2306
+ else if (update.transactions.length)
2307
+ this.compositionDeco = computeCompositionDeco(this.view, update.changes);
2308
+ // When the DOM nodes around the selection are moved to another
2309
+ // parent, Chrome sometimes reports a different selection through
2310
+ // getSelection than the one that it actually shows to the user.
2311
+ // This forces a selection update when lines are joined to work
2312
+ // around that. Issue #54
2313
+ if ((browser.ie || browser.chrome) && !this.compositionDeco.size && update &&
2314
+ update.state.doc.lines != update.startState.doc.lines)
2315
+ this.forceSelection = true;
2316
+ let prevDeco = this.decorations, deco = this.updateDeco();
2317
+ let decoDiff = findChangedDeco(prevDeco, deco, update.changes);
2318
+ changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
2319
+ if (this.dirty == 0 /* Not */ && changedRanges.length == 0) {
2320
+ return false;
2321
+ }
2322
+ else {
2323
+ this.updateInner(changedRanges, deco, update.startState.doc.length);
2324
+ if (update.transactions.length)
2325
+ this.lastUpdate = Date.now();
2326
+ return true;
2327
+ }
2328
+ }
2329
+ reset(sel) {
2330
+ if (this.dirty) {
2331
+ this.view.observer.ignore(() => this.view.docView.sync());
2332
+ this.dirty = 0 /* Not */;
2333
+ this.updateSelection(true);
2334
+ }
2335
+ else {
2336
+ this.updateSelection();
2337
+ }
2338
+ }
2339
+ // Used by update and the constructor do perform the actual DOM
2340
+ // update
2341
+ updateInner(changes, deco, oldLength) {
2342
+ this.view.viewState.mustMeasureContent = true;
2343
+ this.updateChildren(changes, deco, oldLength);
2344
+ let { observer } = this.view;
2345
+ observer.ignore(() => {
2346
+ // Lock the height during redrawing, since Chrome sometimes
2347
+ // messes with the scroll position during DOM mutation (though
2348
+ // no relayout is triggered and I cannot imagine how it can
2349
+ // recompute the scroll position without a layout)
2350
+ this.dom.style.height = this.view.viewState.contentHeight + "px";
2351
+ this.dom.style.minWidth = this.minWidth ? this.minWidth + "px" : "";
2352
+ // Chrome will sometimes, when DOM mutations occur directly
2353
+ // around the selection, get confused and report a different
2354
+ // selection from the one it displays (issue #218). This tries
2355
+ // to detect that situation.
2356
+ let track = browser.chrome || browser.ios ? { node: observer.selectionRange.focusNode, written: false } : undefined;
2357
+ this.sync(track);
2358
+ this.dirty = 0 /* Not */;
2359
+ if (track && (track.written || observer.selectionRange.focusNode != track.node))
2360
+ this.forceSelection = true;
2361
+ this.dom.style.height = "";
2362
+ });
2363
+ let gaps = [];
2364
+ if (this.view.viewport.from || this.view.viewport.to < this.view.state.doc.length)
2365
+ for (let child of this.children)
2366
+ if (child instanceof BlockWidgetView && child.widget instanceof BlockGapWidget)
2367
+ gaps.push(child.dom);
2368
+ observer.updateGaps(gaps);
2369
+ }
2370
+ updateChildren(changes, deco, oldLength) {
2371
+ let cursor = this.childCursor(oldLength);
2372
+ for (let i = changes.length - 1;; i--) {
2373
+ let next = i >= 0 ? changes[i] : null;
2374
+ if (!next)
2375
+ break;
2376
+ let { fromA, toA, fromB, toB } = next;
2377
+ let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, deco);
2378
+ let { i: toI, off: toOff } = cursor.findPos(toA, 1);
2379
+ let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
2380
+ replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
2381
+ }
2382
+ }
2383
+ // Sync the DOM selection to this.state.selection
2384
+ updateSelection(mustRead = false, fromPointer = false) {
2119
2385
  if (mustRead)
2120
2386
  this.view.observer.readSelectionRange();
2121
2387
  if (!(fromPointer || this.mayControlSelection()) ||
@@ -2250,18 +2516,30 @@ class DocView extends ContentView {
2250
2516
  }
2251
2517
  measureVisibleLineHeights() {
2252
2518
  let result = [], { from, to } = this.view.viewState.viewport;
2253
- let minWidth = Math.max(this.view.scrollDOM.clientWidth, this.minWidth) + 1;
2519
+ let contentWidth = this.view.contentDOM.clientWidth;
2520
+ let isWider = contentWidth > Math.max(this.view.scrollDOM.clientWidth, this.minWidth) + 1;
2521
+ let widest = -1;
2254
2522
  for (let pos = 0, i = 0; i < this.children.length; i++) {
2255
2523
  let child = this.children[i], end = pos + child.length;
2256
2524
  if (end > to)
2257
2525
  break;
2258
2526
  if (pos >= from) {
2259
- result.push(child.dom.getBoundingClientRect().height);
2260
- let width = child.dom.scrollWidth;
2261
- if (width > minWidth) {
2262
- this.minWidth = minWidth = width;
2263
- this.minWidthFrom = pos;
2264
- this.minWidthTo = end;
2527
+ let childRect = child.dom.getBoundingClientRect();
2528
+ result.push(childRect.height);
2529
+ if (isWider) {
2530
+ let last = child.dom.lastChild;
2531
+ let rects = last ? clientRectsFor(last) : [];
2532
+ if (rects.length) {
2533
+ let rect = rects[rects.length - 1];
2534
+ let width = this.view.textDirection == exports.Direction.LTR ? rect.right - childRect.left
2535
+ : childRect.right - rect.left;
2536
+ if (width > widest) {
2537
+ widest = width;
2538
+ this.minWidth = contentWidth;
2539
+ this.minWidthFrom = pos;
2540
+ this.minWidthTo = end;
2541
+ }
2542
+ }
2265
2543
  }
2266
2544
  }
2267
2545
  pos = end + child.breakAfter;
@@ -2292,489 +2570,182 @@ class DocView extends ContentView {
2292
2570
  childCursor(pos = this.length) {
2293
2571
  // Move back to start of last element when possible, so that
2294
2572
  // `ChildCursor.findPos` doesn't have to deal with the edge case
2295
- // of being after the last element.
2296
- let i = this.children.length;
2297
- if (i)
2298
- pos -= this.children[--i].length;
2299
- return new ChildCursor(this.children, pos, i);
2300
- }
2301
- computeBlockGapDeco() {
2302
- let deco = [], vs = this.view.viewState;
2303
- for (let pos = 0, i = 0;; i++) {
2304
- let next = i == vs.viewports.length ? null : vs.viewports[i];
2305
- let end = next ? next.from - 1 : this.length;
2306
- if (end > pos) {
2307
- let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
2308
- deco.push(Decoration.replace({ widget: new BlockGapWidget(height), block: true, inclusive: true }).range(pos, end));
2309
- }
2310
- if (!next)
2311
- break;
2312
- pos = next.to + 1;
2313
- }
2314
- return Decoration.set(deco);
2315
- }
2316
- updateDeco() {
2317
- return this.decorations = [
2318
- ...this.view.pluginField(PluginField.decorations),
2319
- ...this.view.state.facet(decorations),
2320
- this.compositionDeco,
2321
- this.computeBlockGapDeco(),
2322
- this.view.viewState.lineGapDeco
2323
- ];
2324
- }
2325
- scrollIntoView({ range, center }) {
2326
- let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
2327
- if (!rect)
2328
- return;
2329
- if (!range.empty && (other = this.coordsAt(range.anchor, range.anchor > range.head ? -1 : 1)))
2330
- rect = { left: Math.min(rect.left, other.left), top: Math.min(rect.top, other.top),
2331
- right: Math.max(rect.right, other.right), bottom: Math.max(rect.bottom, other.bottom) };
2332
- let mLeft = 0, mRight = 0, mTop = 0, mBottom = 0;
2333
- for (let margins of this.view.pluginField(PluginField.scrollMargins))
2334
- if (margins) {
2335
- let { left, right, top, bottom } = margins;
2336
- if (left != null)
2337
- mLeft = Math.max(mLeft, left);
2338
- if (right != null)
2339
- mRight = Math.max(mRight, right);
2340
- if (top != null)
2341
- mTop = Math.max(mTop, top);
2342
- if (bottom != null)
2343
- mBottom = Math.max(mBottom, bottom);
2344
- }
2345
- scrollRectIntoView(this.view.scrollDOM, {
2346
- left: rect.left - mLeft, top: rect.top - mTop,
2347
- right: rect.right + mRight, bottom: rect.bottom + mBottom
2348
- }, range.head < range.anchor ? -1 : 1, center);
2349
- }
2350
- }
2351
- function betweenUneditable(pos) {
2352
- return pos.node.nodeType == 1 && pos.node.firstChild &&
2353
- (pos.offset == 0 || pos.node.childNodes[pos.offset - 1].contentEditable == "false") &&
2354
- (pos.offset == pos.node.childNodes.length || pos.node.childNodes[pos.offset].contentEditable == "false");
2355
- }
2356
- class BlockGapWidget extends WidgetType {
2357
- constructor(height) {
2358
- super();
2359
- this.height = height;
2360
- }
2361
- toDOM() {
2362
- let elt = document.createElement("div");
2363
- this.updateDOM(elt);
2364
- return elt;
2365
- }
2366
- eq(other) { return other.height == this.height; }
2367
- updateDOM(elt) {
2368
- elt.style.height = this.height + "px";
2369
- return true;
2370
- }
2371
- get estimatedHeight() { return this.height; }
2372
- }
2373
- function computeCompositionDeco(view, changes) {
2374
- let sel = view.observer.selectionRange;
2375
- let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
2376
- if (!textNode)
2377
- return Decoration.none;
2378
- let cView = view.docView.nearest(textNode);
2379
- 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) {
2388
- while (topNode.parentNode != cView.dom)
2389
- topNode = topNode.parentNode;
2390
- let prev = topNode.previousSibling;
2391
- while (prev && !ContentView.get(prev))
2392
- prev = prev.previousSibling;
2393
- from = to = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
2394
- }
2395
- else {
2396
- return Decoration.none;
2397
- }
2398
- let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
2399
- let text = textNode.nodeValue, { state } = view;
2400
- if (newTo - newFrom < text.length) {
2401
- if (state.sliceDoc(newFrom, Math.min(state.doc.length, newFrom + text.length)) == text)
2402
- newTo = newFrom + text.length;
2403
- else if (state.sliceDoc(Math.max(0, newTo - text.length), newTo) == text)
2404
- newFrom = newTo - text.length;
2405
- else
2406
- return Decoration.none;
2407
- }
2408
- else if (state.sliceDoc(newFrom, newTo) != text) {
2409
- return Decoration.none;
2410
- }
2411
- return Decoration.set(Decoration.replace({ widget: new CompositionWidget(topNode, textNode) }).range(newFrom, newTo));
2412
- }
2413
- class CompositionWidget extends WidgetType {
2414
- constructor(top, text) {
2415
- super();
2416
- this.top = top;
2417
- this.text = text;
2418
- }
2419
- eq(other) { return this.top == other.top && this.text == other.text; }
2420
- toDOM() { return this.top; }
2421
- ignoreEvent() { return false; }
2422
- get customView() { return CompositionView; }
2423
- }
2424
- function nearbyTextNode(node, offset, side) {
2425
- for (;;) {
2426
- if (node.nodeType == 3)
2427
- return node;
2428
- if (node.nodeType == 1 && offset > 0 && side <= 0) {
2429
- node = node.childNodes[offset - 1];
2430
- offset = maxOffset(node);
2431
- }
2432
- else if (node.nodeType == 1 && offset < node.childNodes.length && side >= 0) {
2433
- node = node.childNodes[offset];
2434
- offset = 0;
2435
- }
2436
- else {
2437
- return null;
2438
- }
2439
- }
2440
- }
2441
- function nextToUneditable(node, offset) {
2442
- if (node.nodeType != 1)
2443
- return 0;
2444
- return (offset && node.childNodes[offset - 1].contentEditable == "false" ? 1 /* Before */ : 0) |
2445
- (offset < node.childNodes.length && node.childNodes[offset].contentEditable == "false" ? 2 /* After */ : 0);
2446
- }
2447
- class DecorationComparator$1 {
2448
- constructor() {
2449
- this.changes = [];
2450
- }
2451
- compareRange(from, to) { addRange(from, to, this.changes); }
2452
- comparePoint(from, to) { addRange(from, to, this.changes); }
2453
- }
2454
- function findChangedDeco(a, b, diff) {
2455
- let comp = new DecorationComparator$1;
2456
- rangeset.RangeSet.compare(a, b, diff, comp);
2457
- return comp.changes;
2458
- }
2459
- function inUneditable(node, inside) {
2460
- for (let cur = node; cur && cur != inside; cur = cur.assignedSlot || cur.parentNode) {
2461
- if (cur.nodeType == 1 && cur.contentEditable == 'false') {
2462
- return true;
2463
- }
2464
- }
2465
- return false;
2466
- }
2467
-
2468
- /**
2469
- Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
2470
- */
2471
- exports.Direction = void 0;
2472
- (function (Direction) {
2473
- // (These are chosen to match the base levels, in bidi algorithm
2474
- // terms, of spans in that direction.)
2475
- /**
2476
- Left-to-right.
2477
- */
2478
- Direction[Direction["LTR"] = 0] = "LTR";
2479
- /**
2480
- Right-to-left.
2481
- */
2482
- Direction[Direction["RTL"] = 1] = "RTL";
2483
- })(exports.Direction || (exports.Direction = {}));
2484
- const LTR = exports.Direction.LTR, RTL = exports.Direction.RTL;
2485
- // Decode a string with each type encoded as log2(type)
2486
- function dec(str) {
2487
- let result = [];
2488
- for (let i = 0; i < str.length; i++)
2489
- result.push(1 << +str[i]);
2490
- return result;
2491
- }
2492
- // Character types for codepoints 0 to 0xf8
2493
- const LowTypes = dec("88888888888888888888888888888888888666888888787833333333337888888000000000000000000000000008888880000000000000000000000000088888888888888888888888888888888888887866668888088888663380888308888800000000000000000000000800000000000000000000000000000008");
2494
- // Character types for codepoints 0x600 to 0x6f9
2495
- const ArabicTypes = dec("4444448826627288999999999992222222222222222222222222222222222222222222222229999999999999999999994444444444644222822222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222999999949999999229989999223333333333");
2496
- const Brackets = Object.create(null), BracketStack = [];
2497
- // There's a lot more in
2498
- // https://www.unicode.org/Public/UCD/latest/ucd/BidiBrackets.txt,
2499
- // which are left out to keep code size down.
2500
- for (let p of ["()", "[]", "{}"]) {
2501
- let l = p.charCodeAt(0), r = p.charCodeAt(1);
2502
- Brackets[l] = r;
2503
- Brackets[r] = -l;
2504
- }
2505
- function charType(ch) {
2506
- return ch <= 0xf7 ? LowTypes[ch] :
2507
- 0x590 <= ch && ch <= 0x5f4 ? 2 /* R */ :
2508
- 0x600 <= ch && ch <= 0x6f9 ? ArabicTypes[ch - 0x600] :
2509
- 0x6ee <= ch && ch <= 0x8ac ? 4 /* AL */ :
2510
- 0x2000 <= ch && ch <= 0x200b ? 256 /* NI */ :
2511
- ch == 0x200c ? 256 /* NI */ : 1 /* L */;
2512
- }
2513
- const BidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
2514
- /**
2515
- Represents a contiguous range of text that has a single direction
2516
- (as in left-to-right or right-to-left).
2517
- */
2518
- class BidiSpan {
2519
- /**
2520
- @internal
2521
- */
2522
- constructor(
2523
- /**
2524
- The start of the span (relative to the start of the line).
2525
- */
2526
- from,
2527
- /**
2528
- The end of the span.
2529
- */
2530
- to,
2531
- /**
2532
- The ["bidi
2533
- level"](https://unicode.org/reports/tr9/#Basic_Display_Algorithm)
2534
- of the span (in this context, 0 means
2535
- left-to-right, 1 means right-to-left, 2 means left-to-right
2536
- number inside right-to-left text).
2537
- */
2538
- level) {
2539
- this.from = from;
2540
- this.to = to;
2541
- this.level = level;
2542
- }
2543
- /**
2544
- The direction of this span.
2545
- */
2546
- get dir() { return this.level % 2 ? RTL : LTR; }
2547
- /**
2548
- @internal
2549
- */
2550
- side(end, dir) { return (this.dir == dir) == end ? this.to : this.from; }
2551
- /**
2552
- @internal
2553
- */
2554
- static find(order, index, level, assoc) {
2555
- let maybe = -1;
2556
- for (let i = 0; i < order.length; i++) {
2557
- let span = order[i];
2558
- if (span.from <= index && span.to >= index) {
2559
- if (span.level == level)
2560
- return i;
2561
- // When multiple spans match, if assoc != 0, take the one that
2562
- // covers that side, otherwise take the one with the minimum
2563
- // level.
2564
- if (maybe < 0 || (assoc != 0 ? (assoc < 0 ? span.from < index : span.to > index) : order[maybe].level > span.level))
2565
- maybe = i;
2566
- }
2567
- }
2568
- if (maybe < 0)
2569
- throw new RangeError("Index out of range");
2570
- return maybe;
2571
- }
2572
- }
2573
- // Reused array of character types
2574
- const types = [];
2575
- function computeOrder(line, direction) {
2576
- let len = line.length, outerType = direction == LTR ? 1 /* L */ : 2 /* R */, oppositeType = direction == LTR ? 2 /* R */ : 1 /* L */;
2577
- if (!line || outerType == 1 /* L */ && !BidiRE.test(line))
2578
- return trivialOrder(len);
2579
- // W1. Examine each non-spacing mark (NSM) in the level run, and
2580
- // change the type of the NSM to the type of the previous
2581
- // character. If the NSM is at the start of the level run, it will
2582
- // get the type of sor.
2583
- // W2. Search backwards from each instance of a European number
2584
- // until the first strong type (R, L, AL, or sor) is found. If an
2585
- // AL is found, change the type of the European number to Arabic
2586
- // number.
2587
- // W3. Change all ALs to R.
2588
- // (Left after this: L, R, EN, AN, ET, CS, NI)
2589
- for (let i = 0, prev = outerType, prevStrong = outerType; i < len; i++) {
2590
- let type = charType(line.charCodeAt(i));
2591
- if (type == 512 /* NSM */)
2592
- type = prev;
2593
- else if (type == 8 /* EN */ && prevStrong == 4 /* AL */)
2594
- type = 16 /* AN */;
2595
- types[i] = type == 4 /* AL */ ? 2 /* R */ : type;
2596
- if (type & 7 /* Strong */)
2597
- prevStrong = type;
2598
- prev = type;
2599
- }
2600
- // W5. A sequence of European terminators adjacent to European
2601
- // numbers changes to all European numbers.
2602
- // W6. Otherwise, separators and terminators change to Other
2603
- // Neutral.
2604
- // W7. Search backwards from each instance of a European number
2605
- // until the first strong type (R, L, or sor) is found. If an L is
2606
- // found, then change the type of the European number to L.
2607
- // (Left after this: L, R, EN+AN, NI)
2608
- for (let i = 0, prev = outerType, prevStrong = outerType; i < len; i++) {
2609
- let type = types[i];
2610
- if (type == 128 /* CS */) {
2611
- if (i < len - 1 && prev == types[i + 1] && (prev & 24 /* Num */))
2612
- type = types[i] = prev;
2613
- else
2614
- types[i] = 256 /* NI */;
2615
- }
2616
- else if (type == 64 /* ET */) {
2617
- let end = i + 1;
2618
- while (end < len && types[end] == 64 /* ET */)
2619
- end++;
2620
- let replace = (i && prev == 8 /* EN */) || (end < len && types[end] == 8 /* EN */) ? (prevStrong == 1 /* L */ ? 1 /* L */ : 8 /* EN */) : 256 /* NI */;
2621
- for (let j = i; j < end; j++)
2622
- types[j] = replace;
2623
- i = end - 1;
2624
- }
2625
- else if (type == 8 /* EN */ && prevStrong == 1 /* L */) {
2626
- types[i] = 1 /* L */;
2627
- }
2628
- prev = type;
2629
- if (type & 7 /* Strong */)
2630
- prevStrong = type;
2631
- }
2632
- // N0. Process bracket pairs in an isolating run sequence
2633
- // sequentially in the logical order of the text positions of the
2634
- // opening paired brackets using the logic given below. Within this
2635
- // scope, bidirectional types EN and AN are treated as R.
2636
- for (let i = 0, sI = 0, context = 0, ch, br, type; i < len; i++) {
2637
- // Keeps [startIndex, type, strongSeen] triples for each open
2638
- // bracket on BracketStack.
2639
- if (br = Brackets[ch = line.charCodeAt(i)]) {
2640
- if (br < 0) { // Closing bracket
2641
- for (let sJ = sI - 3; sJ >= 0; sJ -= 3) {
2642
- if (BracketStack[sJ + 1] == -br) {
2643
- let flags = BracketStack[sJ + 2];
2644
- let type = (flags & 2 /* EmbedInside */) ? outerType :
2645
- !(flags & 4 /* OppositeInside */) ? 0 :
2646
- (flags & 1 /* OppositeBefore */) ? oppositeType : outerType;
2647
- if (type)
2648
- types[i] = types[BracketStack[sJ]] = type;
2649
- sI = sJ;
2650
- break;
2651
- }
2652
- }
2653
- }
2654
- else if (BracketStack.length == 189 /* MaxDepth */) {
2655
- break;
2656
- }
2657
- else {
2658
- BracketStack[sI++] = i;
2659
- BracketStack[sI++] = ch;
2660
- BracketStack[sI++] = context;
2661
- }
2662
- }
2663
- else if ((type = types[i]) == 2 /* R */ || type == 1 /* L */) {
2664
- let embed = type == outerType;
2665
- context = embed ? 0 : 1 /* OppositeBefore */;
2666
- for (let sJ = sI - 3; sJ >= 0; sJ -= 3) {
2667
- let cur = BracketStack[sJ + 2];
2668
- if (cur & 2 /* EmbedInside */)
2669
- break;
2670
- if (embed) {
2671
- BracketStack[sJ + 2] |= 2 /* EmbedInside */;
2672
- }
2673
- else {
2674
- if (cur & 4 /* OppositeInside */)
2675
- break;
2676
- BracketStack[sJ + 2] |= 4 /* OppositeInside */;
2677
- }
2573
+ // of being after the last element.
2574
+ let i = this.children.length;
2575
+ if (i)
2576
+ pos -= this.children[--i].length;
2577
+ return new ChildCursor(this.children, pos, i);
2578
+ }
2579
+ computeBlockGapDeco() {
2580
+ let deco = [], vs = this.view.viewState;
2581
+ for (let pos = 0, i = 0;; i++) {
2582
+ let next = i == vs.viewports.length ? null : vs.viewports[i];
2583
+ let end = next ? next.from - 1 : this.length;
2584
+ if (end > pos) {
2585
+ let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
2586
+ deco.push(Decoration.replace({ widget: new BlockGapWidget(height), block: true, inclusive: true }).range(pos, end));
2678
2587
  }
2588
+ if (!next)
2589
+ break;
2590
+ pos = next.to + 1;
2679
2591
  }
2592
+ return Decoration.set(deco);
2680
2593
  }
2681
- // N1. A sequence of neutrals takes the direction of the
2682
- // surrounding strong text if the text on both sides has the same
2683
- // direction. European and Arabic numbers act as if they were R in
2684
- // terms of their influence on neutrals. Start-of-level-run (sor)
2685
- // and end-of-level-run (eor) are used at level run boundaries.
2686
- // N2. Any remaining neutrals take the embedding direction.
2687
- // (Left after this: L, R, EN+AN)
2688
- for (let i = 0; i < len; i++) {
2689
- if (types[i] == 256 /* NI */) {
2690
- let end = i + 1;
2691
- while (end < len && types[end] == 256 /* NI */)
2692
- end++;
2693
- let beforeL = (i ? types[i - 1] : outerType) == 1 /* L */;
2694
- let afterL = (end < len ? types[end] : outerType) == 1 /* L */;
2695
- let replace = beforeL == afterL ? (beforeL ? 1 /* L */ : 2 /* R */) : outerType;
2696
- for (let j = i; j < end; j++)
2697
- types[j] = replace;
2698
- i = end - 1;
2699
- }
2594
+ updateDeco() {
2595
+ return this.decorations = [
2596
+ ...this.view.pluginField(PluginField.decorations),
2597
+ ...this.view.state.facet(decorations),
2598
+ this.compositionDeco,
2599
+ this.computeBlockGapDeco(),
2600
+ this.view.viewState.lineGapDeco
2601
+ ];
2700
2602
  }
2701
- // Here we depart from the documented algorithm, in order to avoid
2702
- // building up an actual levels array. Since there are only three
2703
- // levels (0, 1, 2) in an implementation that doesn't take
2704
- // explicit embedding into account, we can build up the order on
2705
- // the fly, without following the level-based algorithm.
2706
- let order = [];
2707
- if (outerType == 1 /* L */) {
2708
- for (let i = 0; i < len;) {
2709
- let start = i, rtl = types[i++] != 1 /* L */;
2710
- while (i < len && rtl == (types[i] != 1 /* L */))
2711
- i++;
2712
- if (rtl) {
2713
- for (let j = i; j > start;) {
2714
- let end = j, l = types[--j] != 2 /* R */;
2715
- while (j > start && l == (types[j - 1] != 2 /* R */))
2716
- j--;
2717
- order.push(new BidiSpan(j, end, l ? 2 : 1));
2718
- }
2719
- }
2720
- else {
2721
- order.push(new BidiSpan(start, i, 0));
2603
+ scrollIntoView({ range, center }) {
2604
+ let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
2605
+ if (!rect)
2606
+ return;
2607
+ if (!range.empty && (other = this.coordsAt(range.anchor, range.anchor > range.head ? -1 : 1)))
2608
+ rect = { left: Math.min(rect.left, other.left), top: Math.min(rect.top, other.top),
2609
+ right: Math.max(rect.right, other.right), bottom: Math.max(rect.bottom, other.bottom) };
2610
+ let mLeft = 0, mRight = 0, mTop = 0, mBottom = 0;
2611
+ for (let margins of this.view.pluginField(PluginField.scrollMargins))
2612
+ if (margins) {
2613
+ let { left, right, top, bottom } = margins;
2614
+ if (left != null)
2615
+ mLeft = Math.max(mLeft, left);
2616
+ if (right != null)
2617
+ mRight = Math.max(mRight, right);
2618
+ if (top != null)
2619
+ mTop = Math.max(mTop, top);
2620
+ if (bottom != null)
2621
+ mBottom = Math.max(mBottom, bottom);
2722
2622
  }
2723
- }
2623
+ scrollRectIntoView(this.view.scrollDOM, {
2624
+ left: rect.left - mLeft, top: rect.top - mTop,
2625
+ right: rect.right + mRight, bottom: rect.bottom + mBottom
2626
+ }, range.head < range.anchor ? -1 : 1, center);
2627
+ }
2628
+ }
2629
+ function betweenUneditable(pos) {
2630
+ return pos.node.nodeType == 1 && pos.node.firstChild &&
2631
+ (pos.offset == 0 || pos.node.childNodes[pos.offset - 1].contentEditable == "false") &&
2632
+ (pos.offset == pos.node.childNodes.length || pos.node.childNodes[pos.offset].contentEditable == "false");
2633
+ }
2634
+ class BlockGapWidget extends WidgetType {
2635
+ constructor(height) {
2636
+ super();
2637
+ this.height = height;
2638
+ }
2639
+ toDOM() {
2640
+ let elt = document.createElement("div");
2641
+ this.updateDOM(elt);
2642
+ return elt;
2643
+ }
2644
+ eq(other) { return other.height == this.height; }
2645
+ updateDOM(elt) {
2646
+ elt.style.height = this.height + "px";
2647
+ return true;
2648
+ }
2649
+ get estimatedHeight() { return this.height; }
2650
+ }
2651
+ function computeCompositionDeco(view, changes) {
2652
+ let sel = view.observer.selectionRange;
2653
+ let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
2654
+ if (!textNode)
2655
+ return Decoration.none;
2656
+ let cView = view.docView.nearest(textNode);
2657
+ if (!cView)
2658
+ return Decoration.none;
2659
+ let from, to, topNode = textNode;
2660
+ if (cView instanceof LineView) {
2661
+ while (topNode.parentNode != cView.dom)
2662
+ topNode = topNode.parentNode;
2663
+ let prev = topNode.previousSibling;
2664
+ while (prev && !ContentView.get(prev))
2665
+ prev = prev.previousSibling;
2666
+ from = to = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
2724
2667
  }
2725
2668
  else {
2726
- for (let i = 0; i < len;) {
2727
- let start = i, rtl = types[i++] == 2 /* R */;
2728
- while (i < len && rtl == (types[i] == 2 /* R */))
2729
- i++;
2730
- order.push(new BidiSpan(start, i, rtl ? 1 : 2));
2669
+ for (;;) {
2670
+ let { parent } = cView;
2671
+ if (!parent)
2672
+ return Decoration.none;
2673
+ if (parent instanceof LineView)
2674
+ break;
2675
+ cView = parent;
2731
2676
  }
2677
+ from = cView.posAtStart;
2678
+ to = from + cView.length;
2679
+ topNode = cView.dom;
2732
2680
  }
2733
- return order;
2681
+ let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
2682
+ let text = textNode.nodeValue, { state } = view;
2683
+ if (newTo - newFrom < text.length) {
2684
+ if (state.sliceDoc(newFrom, Math.min(state.doc.length, newFrom + text.length)) == text)
2685
+ newTo = newFrom + text.length;
2686
+ else if (state.sliceDoc(Math.max(0, newTo - text.length), newTo) == text)
2687
+ newFrom = newTo - text.length;
2688
+ else
2689
+ return Decoration.none;
2690
+ }
2691
+ else if (state.sliceDoc(newFrom, newTo) != text) {
2692
+ return Decoration.none;
2693
+ }
2694
+ return Decoration.set(Decoration.replace({ widget: new CompositionWidget(topNode, textNode) }).range(newFrom, newTo));
2734
2695
  }
2735
- function trivialOrder(length) {
2736
- return [new BidiSpan(0, length, 0)];
2696
+ class CompositionWidget extends WidgetType {
2697
+ constructor(top, text) {
2698
+ super();
2699
+ this.top = top;
2700
+ this.text = text;
2701
+ }
2702
+ eq(other) { return this.top == other.top && this.text == other.text; }
2703
+ toDOM() { return this.top; }
2704
+ ignoreEvent() { return false; }
2705
+ get customView() { return CompositionView; }
2737
2706
  }
2738
- let movedOver = "";
2739
- function moveVisually(line, order, dir, start, forward) {
2740
- var _a;
2741
- let startIndex = start.head - line.from, spanI = -1;
2742
- if (startIndex == 0) {
2743
- if (!forward || !line.length)
2744
- return null;
2745
- if (order[0].level != dir) {
2746
- startIndex = order[0].side(false, dir);
2747
- spanI = 0;
2707
+ function nearbyTextNode(node, offset, side) {
2708
+ for (;;) {
2709
+ if (node.nodeType == 3)
2710
+ return node;
2711
+ if (node.nodeType == 1 && offset > 0 && side <= 0) {
2712
+ node = node.childNodes[offset - 1];
2713
+ offset = maxOffset(node);
2748
2714
  }
2749
- }
2750
- else if (startIndex == line.length) {
2751
- if (forward)
2715
+ else if (node.nodeType == 1 && offset < node.childNodes.length && side >= 0) {
2716
+ node = node.childNodes[offset];
2717
+ offset = 0;
2718
+ }
2719
+ else {
2752
2720
  return null;
2753
- let last = order[order.length - 1];
2754
- if (last.level != dir) {
2755
- startIndex = last.side(true, dir);
2756
- spanI = order.length - 1;
2757
2721
  }
2758
2722
  }
2759
- if (spanI < 0)
2760
- spanI = BidiSpan.find(order, startIndex, (_a = start.bidiLevel) !== null && _a !== void 0 ? _a : -1, start.assoc);
2761
- let span = order[spanI];
2762
- // End of span. (But not end of line--that was checked for above.)
2763
- if (startIndex == span.side(forward, dir)) {
2764
- span = order[spanI += forward ? 1 : -1];
2765
- startIndex = span.side(!forward, dir);
2723
+ }
2724
+ function nextToUneditable(node, offset) {
2725
+ if (node.nodeType != 1)
2726
+ return 0;
2727
+ return (offset && node.childNodes[offset - 1].contentEditable == "false" ? 1 /* Before */ : 0) |
2728
+ (offset < node.childNodes.length && node.childNodes[offset].contentEditable == "false" ? 2 /* After */ : 0);
2729
+ }
2730
+ class DecorationComparator$1 {
2731
+ constructor() {
2732
+ this.changes = [];
2766
2733
  }
2767
- let indexForward = forward == (span.dir == dir);
2768
- let nextIndex = text.findClusterBreak(line.text, startIndex, indexForward);
2769
- movedOver = line.text.slice(Math.min(startIndex, nextIndex), Math.max(startIndex, nextIndex));
2770
- if (nextIndex != span.side(forward, dir))
2771
- return state.EditorSelection.cursor(nextIndex + line.from, indexForward ? -1 : 1, span.level);
2772
- let nextSpan = spanI == (forward ? order.length - 1 : 0) ? null : order[spanI + (forward ? 1 : -1)];
2773
- if (!nextSpan && span.level != dir)
2774
- return state.EditorSelection.cursor(forward ? line.to : line.from, forward ? -1 : 1, dir);
2775
- if (nextSpan && nextSpan.level < span.level)
2776
- return state.EditorSelection.cursor(nextSpan.side(!forward, dir) + line.from, forward ? 1 : -1, nextSpan.level);
2777
- return state.EditorSelection.cursor(nextIndex + line.from, forward ? -1 : 1, span.level);
2734
+ compareRange(from, to) { addRange(from, to, this.changes); }
2735
+ comparePoint(from, to) { addRange(from, to, this.changes); }
2736
+ }
2737
+ function findChangedDeco(a, b, diff) {
2738
+ let comp = new DecorationComparator$1;
2739
+ rangeset.RangeSet.compare(a, b, diff, comp);
2740
+ return comp.changes;
2741
+ }
2742
+ function inUneditable(node, inside) {
2743
+ for (let cur = node; cur && cur != inside; cur = cur.assignedSlot || cur.parentNode) {
2744
+ if (cur.nodeType == 1 && cur.contentEditable == 'false') {
2745
+ return true;
2746
+ }
2747
+ }
2748
+ return false;
2778
2749
  }
2779
2750
 
2780
2751
  function groupAt(state$1, pos, bias = 1) {
@@ -2931,7 +2902,7 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
2931
2902
  y = docTop + yOffset;
2932
2903
  let lineStart = block.from;
2933
2904
  // Clip x to the viewport sides
2934
- x = Math.max(content.left + 1, Math.min(content.right - 1, x));
2905
+ x = Math.max(content.left + 1, Math.min(Math.max(content.right, content.left + view.docView.minWidth) - 1, x));
2935
2906
  // If this is outside of the rendered viewport, we can't determine a position
2936
2907
  if (lineStart < view.viewport.from)
2937
2908
  return view.viewport.from == 0 ? 0 : posAtCoordsImprecise(view, content, block, x, y);
@@ -4174,12 +4145,12 @@ class HeightMapBranch extends HeightMap {
4174
4145
  get break() { return this.flags & 1 /* Break */; }
4175
4146
  blockAt(height, doc, top, offset) {
4176
4147
  let mid = top + this.left.height;
4177
- return height < mid || this.right.height == 0 ? this.left.blockAt(height, doc, top, offset)
4148
+ return height < mid ? this.left.blockAt(height, doc, top, offset)
4178
4149
  : this.right.blockAt(height, doc, mid, offset + this.left.length + this.break);
4179
4150
  }
4180
4151
  lineAt(value, type, doc, top, offset) {
4181
4152
  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;
4153
+ let left = type == QueryType.ByHeight ? value < rightTop : value < rightOffset;
4183
4154
  let base = left ? this.left.lineAt(value, type, doc, top, offset)
4184
4155
  : this.right.lineAt(value, type, doc, rightTop, rightOffset);
4185
4156
  if (this.break || (left ? base.to < rightOffset : base.from > rightOffset))
@@ -4320,7 +4291,9 @@ class NodeBuilder {
4320
4291
  }
4321
4292
  point(from, to, deco) {
4322
4293
  if (from < to || deco.heightRelevant) {
4323
- let height = deco.widget ? Math.max(0, deco.widget.estimatedHeight) : 0;
4294
+ let height = deco.widget ? deco.widget.estimatedHeight : 0;
4295
+ if (height < 0)
4296
+ height = this.oracle.lineHeight;
4324
4297
  let len = to - from;
4325
4298
  if (deco.block) {
4326
4299
  this.addBlock(new HeightMapBlock(len, height, deco.type));
@@ -5058,14 +5031,15 @@ const baseTheme = buildTheme("." + baseThemeID, {
5058
5031
  },
5059
5032
  ".cm-placeholder": {
5060
5033
  color: "#888",
5061
- display: "inline-block"
5034
+ display: "inline-block",
5035
+ verticalAlign: "top",
5062
5036
  },
5063
5037
  ".cm-button": {
5064
5038
  verticalAlign: "middle",
5065
5039
  color: "inherit",
5066
5040
  fontSize: "70%",
5067
5041
  padding: ".2em 1em",
5068
- borderRadius: "3px"
5042
+ borderRadius: "1px"
5069
5043
  },
5070
5044
  "&light .cm-button": {
5071
5045
  backgroundImage: "linear-gradient(#eff1f5, #d9d9df)",
@@ -5387,6 +5361,7 @@ class DOMObserver {
5387
5361
  for (let dom of this.scrollTargets)
5388
5362
  dom.removeEventListener("scroll", this.onScroll);
5389
5363
  window.removeEventListener("scroll", this.onScroll);
5364
+ this.dom.ownerDocument.removeEventListener("selectionchange", this.onSelectionChange);
5390
5365
  clearTimeout(this.parentCheck);
5391
5366
  clearTimeout(this.resizeTimeout);
5392
5367
  }
@@ -5486,11 +5461,22 @@ function applyDOMChange(view, start, end, typeOver) {
5486
5461
  };
5487
5462
  if (change) {
5488
5463
  let startState = view.state;
5464
+ if (browser.ios && view.inputState.flushIOSKey(view))
5465
+ return;
5489
5466
  // Android browsers don't fire reasonable key events for enter,
5490
5467
  // backspace, or delete. So this detects changes that look like
5491
5468
  // they're caused by those keys, and reinterprets them as key
5492
- // events.
5493
- if (browser.ios && view.inputState.flushIOSKey(view))
5469
+ // events. (Some of these keys are also handled by beforeinput
5470
+ // events and the pendingAndroidKey mechanism, but that's not
5471
+ // reliable in all situations.)
5472
+ if (browser.android &&
5473
+ ((change.from == sel.from && change.to == sel.to &&
5474
+ change.insert.length == 1 && change.insert.lines == 2 &&
5475
+ dispatchKey(view.contentDOM, "Enter", 13)) ||
5476
+ (change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 &&
5477
+ dispatchKey(view.contentDOM, "Backspace", 8)) ||
5478
+ (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
5479
+ dispatchKey(view.contentDOM, "Delete", 46))))
5494
5480
  return;
5495
5481
  let text = change.insert.toString();
5496
5482
  if (view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text)))
@@ -5683,6 +5669,7 @@ class EditorView {
5683
5669
  */
5684
5670
  config = {}) {
5685
5671
  this.plugins = [];
5672
+ this.pluginMap = new Map;
5686
5673
  this.editorAttrs = {};
5687
5674
  this.contentAttrs = {};
5688
5675
  this.bidiCache = [];
@@ -5714,7 +5701,9 @@ class EditorView {
5714
5701
  this.dispatch = this.dispatch.bind(this);
5715
5702
  this.root = (config.root || getRoot(config.parent) || document);
5716
5703
  this.viewState = new ViewState(config.state || state.EditorState.create());
5717
- this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec).update(this));
5704
+ this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec));
5705
+ for (let plugin of this.plugins)
5706
+ plugin.update(this);
5718
5707
  this.observer = new DOMObserver(this, (from, to, typeOver) => {
5719
5708
  applyDOMChange(this, from, to, typeOver);
5720
5709
  }, event => {
@@ -5851,7 +5840,10 @@ class EditorView {
5851
5840
  for (let plugin of this.plugins)
5852
5841
  plugin.destroy(this);
5853
5842
  this.viewState = new ViewState(newState);
5854
- this.plugins = newState.facet(viewPlugin).map(spec => new PluginInstance(spec).update(this));
5843
+ this.plugins = newState.facet(viewPlugin).map(spec => new PluginInstance(spec));
5844
+ this.pluginMap.clear();
5845
+ for (let plugin of this.plugins)
5846
+ plugin.update(this);
5855
5847
  this.docView = new DocView(this);
5856
5848
  this.inputState.ensureHandlers(this);
5857
5849
  this.mountStyles();
@@ -5882,6 +5874,7 @@ class EditorView {
5882
5874
  if (plugin.mustUpdate != update)
5883
5875
  plugin.destroy(this);
5884
5876
  this.plugins = newPlugins;
5877
+ this.pluginMap.clear();
5885
5878
  this.inputState.ensureHandlers(this);
5886
5879
  }
5887
5880
  else {
@@ -5889,7 +5882,7 @@ class EditorView {
5889
5882
  p.mustUpdate = update;
5890
5883
  }
5891
5884
  for (let i = 0; i < this.plugins.length; i++)
5892
- this.plugins[i] = this.plugins[i].update(this);
5885
+ this.plugins[i].update(this);
5893
5886
  }
5894
5887
  /**
5895
5888
  @internal
@@ -5978,7 +5971,7 @@ class EditorView {
5978
5971
  this.state.facet(theme);
5979
5972
  }
5980
5973
  updateAttrs() {
5981
- let editorAttrs = combineAttrs(this.state.facet(editorAttributes), {
5974
+ let editorAttrs = attrsFromFacet(this, editorAttributes, {
5982
5975
  class: "cm-editor" + (this.hasFocus ? " cm-focused " : " ") + this.themeClasses
5983
5976
  });
5984
5977
  let contentAttrs = {
@@ -5994,7 +5987,7 @@ class EditorView {
5994
5987
  };
5995
5988
  if (this.state.readOnly)
5996
5989
  contentAttrs["aria-readonly"] = "true";
5997
- combineAttrs(this.state.facet(contentAttributes), contentAttrs);
5990
+ attrsFromFacet(this, contentAttributes, contentAttrs);
5998
5991
  this.observer.ignore(() => {
5999
5992
  updateAttrs(this.contentDOM, this.contentAttrs, contentAttrs);
6000
5993
  updateAttrs(this.dom, this.editorAttrs, editorAttrs);
@@ -6063,10 +6056,10 @@ class EditorView {
6063
6056
  the return value of this method.
6064
6057
  */
6065
6058
  plugin(plugin) {
6066
- for (let inst of this.plugins)
6067
- if (inst.spec == plugin)
6068
- return inst.update(this).value;
6069
- return null;
6059
+ let known = this.pluginMap.get(plugin);
6060
+ if (known === undefined || known && known.spec != plugin)
6061
+ this.pluginMap.set(plugin, known = this.plugins.find(p => p.spec == plugin) || null);
6062
+ return known && known.update(this).value;
6070
6063
  }
6071
6064
  /**
6072
6065
  The top position of the document, in screen coordinates. This
@@ -6553,6 +6546,14 @@ class CachedOrder {
6553
6546
  return result;
6554
6547
  }
6555
6548
  }
6549
+ function attrsFromFacet(view, facet, base) {
6550
+ for (let sources = view.state.facet(facet), i = sources.length - 1; i >= 0; i--) {
6551
+ let source = sources[i], value = typeof source == "function" ? source(view) : source;
6552
+ if (value)
6553
+ combineAttrs(value, base);
6554
+ }
6555
+ return base;
6556
+ }
6556
6557
 
6557
6558
  const currentPlatform = browser.mac ? "mac" : browser.windows ? "win" : browser.linux ? "linux" : "key";
6558
6559
  function normalizeKeyName(name, platform) {
@@ -7205,35 +7206,29 @@ class TabWidget extends WidgetType {
7205
7206
  }
7206
7207
 
7207
7208
  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);
7209
+ constructor() {
7210
+ this.height = 1000;
7211
+ this.attrs = { style: "padding-bottom: 1000px" };
7220
7212
  }
7221
7213
  update(update) {
7222
- if (update.geometryChanged)
7223
- update.view.requestMeasure(this.measure);
7214
+ let height = update.view.viewState.editorHeight - update.view.defaultLineHeight;
7215
+ if (height != this.height) {
7216
+ this.height = height;
7217
+ this.attrs = { style: `padding-bottom: ${height}px` };
7218
+ }
7224
7219
  }
7225
7220
  });
7226
7221
  /**
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.
7222
+ Returns an extension that makes sure the content has a bottom
7223
+ margin equivalent to the height of the editor, minus one line
7224
+ height, so that every line in the document can be scrolled to the
7225
+ top of the editor.
7231
7226
 
7232
7227
  This is only meaningful when the editor is scrollable, and should
7233
7228
  not be enabled in editors that take the size of their content.
7234
7229
  */
7235
7230
  function scrollPastEnd() {
7236
- return plugin;
7231
+ return [plugin, contentAttributes.of(view => { var _a; return ((_a = view.plugin(plugin)) === null || _a === void 0 ? void 0 : _a.attrs) || null; })];
7237
7232
  }
7238
7233
 
7239
7234
  /**
@@ -7308,9 +7303,7 @@ const __test = { HeightMap, HeightOracle, MeasuredHeights, QueryType, ChangedRan
7308
7303
 
7309
7304
  Object.defineProperty(exports, 'Range', {
7310
7305
  enumerable: true,
7311
- get: function () {
7312
- return rangeset.Range;
7313
- }
7306
+ get: function () { return rangeset.Range; }
7314
7307
  });
7315
7308
  exports.BidiSpan = BidiSpan;
7316
7309
  exports.BlockInfo = BlockInfo;