@codemirror/view 0.19.44 → 0.19.47

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/CHANGELOG.md CHANGED
@@ -1,3 +1,29 @@
1
+ ## 0.19.47 (2022-03-08)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix an issue where block widgets at the start of the viewport could break height computations.
6
+
7
+ ## 0.19.46 (2022-03-03)
8
+
9
+ ### Bug fixes
10
+
11
+ Fix a bug where block widgets on the edges of viewports could cause the positioning of content to misalign with the gutter and height computations.
12
+
13
+ Improve cursor height next to widgets.
14
+
15
+ Fix a bug where mapping positions to screen coordinates could return incorred coordinates during composition.
16
+
17
+ ## 0.19.45 (2022-02-23)
18
+
19
+ ### Bug fixes
20
+
21
+ Fix an issue where the library failed to call `WidgetType.destroy` on the old widget when replacing a widget with a different widget of the same type.
22
+
23
+ Fix an issue where the editor would compute DOM positions inside composition contexts incorrectly in some cases, causing the selection to be put in the wrong place and needlessly interrupting compositions.
24
+
25
+ Fix leaking of resize event handlers.
26
+
1
27
  ## 0.19.44 (2022-02-17)
2
28
 
3
29
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -22,7 +22,7 @@ function getSelection(root) {
22
22
  return target.getSelection();
23
23
  }
24
24
  function contains(dom, node) {
25
- return node ? dom.contains(node.nodeType != 1 ? node.parentNode : node) : false;
25
+ return node ? dom == node || dom.contains(node.nodeType != 1 ? node.parentNode : node) : false;
26
26
  }
27
27
  function deepActiveElement() {
28
28
  let elt = document.activeElement;
@@ -781,6 +781,7 @@ class WidgetView extends ContentView {
781
781
  this.widget = widget;
782
782
  this.length = length;
783
783
  this.side = side;
784
+ this.prevWidget = null;
784
785
  }
785
786
  static create(widget, length, side) {
786
787
  return new (widget.customView || WidgetView)(widget, length, side);
@@ -792,6 +793,9 @@ class WidgetView extends ContentView {
792
793
  }
793
794
  sync() {
794
795
  if (!this.dom || !this.widget.updateDOM(this.dom)) {
796
+ if (this.dom && this.prevWidget)
797
+ this.prevWidget.destroy(this.dom);
798
+ this.prevWidget = null;
795
799
  this.setDOM(this.widget.toDOM(this.editorView));
796
800
  this.dom.contentEditable = "false";
797
801
  }
@@ -809,6 +813,8 @@ class WidgetView extends ContentView {
809
813
  if (this.widget.constructor == other.widget.constructor) {
810
814
  if (!this.widget.eq(other.widget))
811
815
  this.markDirty(true);
816
+ if (this.dom && !this.prevWidget)
817
+ this.prevWidget = this.widget;
812
818
  this.widget = other.widget;
813
819
  return true;
814
820
  }
@@ -849,16 +855,69 @@ class WidgetView extends ContentView {
849
855
  }
850
856
  }
851
857
  class CompositionView extends WidgetView {
852
- domAtPos(pos) { return new DOMPos(this.widget.text, pos); }
858
+ domAtPos(pos) {
859
+ let { topView, text } = this.widget;
860
+ if (!topView)
861
+ return new DOMPos(text, Math.min(pos, text.nodeValue.length));
862
+ return scanCompositionTree(pos, 0, topView, text, (v, p) => v.domAtPos(p), p => new DOMPos(text, Math.min(p, text.nodeValue.length)));
863
+ }
853
864
  sync() { this.setDOM(this.widget.toDOM()); }
854
865
  localPosFromDOM(node, offset) {
855
- return !offset ? 0 : node.nodeType == 3 ? Math.min(offset, this.length) : this.length;
866
+ let { topView, text } = this.widget;
867
+ if (!topView)
868
+ return Math.min(offset, this.length);
869
+ return posFromDOMInCompositionTree(node, offset, topView, text);
856
870
  }
857
871
  ignoreMutation() { return false; }
858
872
  get overrideDOMText() { return null; }
859
- coordsAt(pos, side) { return textCoords(this.widget.text, pos, side); }
873
+ coordsAt(pos, side) {
874
+ let { topView, text } = this.widget;
875
+ if (!topView)
876
+ return textCoords(text, pos, side);
877
+ return scanCompositionTree(pos, side, topView, text, (v, pos, side) => v.coordsAt(pos, side), (pos, side) => textCoords(text, pos, side));
878
+ }
879
+ destroy() {
880
+ var _a;
881
+ super.destroy();
882
+ (_a = this.widget.topView) === null || _a === void 0 ? void 0 : _a.destroy();
883
+ }
860
884
  get isEditable() { return true; }
861
885
  }
886
+ // Uses the old structure of a chunk of content view frozen for
887
+ // composition to try and find a reasonable DOM location for the given
888
+ // offset.
889
+ function scanCompositionTree(pos, side, view, text, enterView, fromText) {
890
+ if (view instanceof MarkView) {
891
+ for (let child of view.children) {
892
+ let hasComp = contains(child.dom, text);
893
+ let len = hasComp ? text.nodeValue.length : child.length;
894
+ if (pos < len || pos == len && child.getSide() <= 0)
895
+ return hasComp ? scanCompositionTree(pos, side, child, text, enterView, fromText) : enterView(child, pos, side);
896
+ pos -= len;
897
+ }
898
+ return enterView(view, view.length, -1);
899
+ }
900
+ else if (view.dom == text) {
901
+ return fromText(pos, side);
902
+ }
903
+ else {
904
+ return enterView(view, pos, side);
905
+ }
906
+ }
907
+ function posFromDOMInCompositionTree(node, offset, view, text) {
908
+ if (view instanceof MarkView) {
909
+ for (let child of view.children) {
910
+ let pos = 0, hasComp = contains(child.dom, text);
911
+ if (contains(child.dom, node))
912
+ return pos + (hasComp ? posFromDOMInCompositionTree(node, offset, child, text) : child.localPosFromDOM(node, offset));
913
+ pos += hasComp ? text.nodeValue.length : child.length;
914
+ }
915
+ }
916
+ else if (view.dom == text) {
917
+ return Math.min(offset, text.nodeValue.length);
918
+ }
919
+ return view.localPosFromDOM(node, offset);
920
+ }
862
921
  // These are drawn around uneditable widgets to avoid a number of
863
922
  // browser bugs that show up when the cursor is directly next to
864
923
  // uneditable inline content.
@@ -877,6 +936,7 @@ class WidgetBufferView extends ContentView {
877
936
  if (!this.dom) {
878
937
  let dom = document.createElement("img");
879
938
  dom.className = "cm-widgetBuffer";
939
+ dom.setAttribute("aria-hidden", "true");
880
940
  this.setDOM(dom);
881
941
  }
882
942
  }
@@ -885,13 +945,43 @@ class WidgetBufferView extends ContentView {
885
945
  localPosFromDOM() { return 0; }
886
946
  domBoundsAround() { return null; }
887
947
  coordsAt(pos) {
888
- return this.dom.getBoundingClientRect();
948
+ let imgRect = this.dom.getBoundingClientRect();
949
+ // Since the <img> height doesn't correspond to text height, try
950
+ // to borrow the height from some sibling node.
951
+ let siblingRect = inlineSiblingRect(this, this.side > 0 ? -1 : 1);
952
+ return siblingRect && siblingRect.top < imgRect.bottom && siblingRect.bottom > imgRect.top
953
+ ? { left: imgRect.left, right: imgRect.right, top: siblingRect.top, bottom: siblingRect.bottom } : imgRect;
889
954
  }
890
955
  get overrideDOMText() {
891
956
  return text.Text.empty;
892
957
  }
893
958
  }
894
959
  TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren;
960
+ function inlineSiblingRect(view, side) {
961
+ let parent = view.parent, index = parent ? parent.children.indexOf(view) : -1;
962
+ while (parent && index >= 0) {
963
+ if (side < 0 ? index > 0 : index < parent.children.length) {
964
+ let next = parent.children[index + side];
965
+ if (next instanceof TextView) {
966
+ let nextRect = next.coordsAt(side < 0 ? next.length : 0, side);
967
+ if (nextRect)
968
+ return nextRect;
969
+ }
970
+ index += side;
971
+ }
972
+ else if (parent instanceof MarkView && parent.parent) {
973
+ index = parent.parent.children.indexOf(parent) + (side < 0 ? 0 : 1);
974
+ parent = parent.parent;
975
+ }
976
+ else {
977
+ let last = parent.dom.lastChild;
978
+ if (last && last.nodeName == "BR")
979
+ return last.getClientRects()[0];
980
+ break;
981
+ }
982
+ }
983
+ return undefined;
984
+ }
895
985
  function inlineDOMAtPos(dom, children, pos) {
896
986
  let i = 0;
897
987
  for (let off = 0; i < children.length; i++) {
@@ -1125,10 +1215,16 @@ class Decoration extends rangeset.RangeValue {
1125
1215
  a widget, or simply hides it.
1126
1216
  */
1127
1217
  static replace(spec) {
1128
- let block = !!spec.block;
1129
- let { start, end } = getInclusive(spec, block);
1130
- let startSide = (start ? (block ? -300000000 /* BlockIncStart */ : -1 /* InlineIncStart */) : 400000000 /* NonIncStart */) - 1;
1131
- let endSide = (end ? (block ? 200000000 /* BlockIncEnd */ : 1 /* InlineIncEnd */) : -500000000 /* NonIncEnd */) + 1;
1218
+ let block = !!spec.block, startSide, endSide;
1219
+ if (spec.isBlockGap) {
1220
+ startSide = -500000000 /* GapStart */;
1221
+ endSide = 400000000 /* GapEnd */;
1222
+ }
1223
+ else {
1224
+ let { start, end } = getInclusive(spec, block);
1225
+ startSide = (start ? (block ? -300000000 /* BlockIncStart */ : -1 /* InlineIncStart */) : 500000000 /* NonIncStart */) - 1;
1226
+ endSide = (end ? (block ? 200000000 /* BlockIncEnd */ : 1 /* InlineIncEnd */) : -600000000 /* NonIncEnd */) + 1;
1227
+ }
1132
1228
  return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
1133
1229
  }
1134
1230
  /**
@@ -1158,7 +1254,7 @@ Decoration.none = rangeset.RangeSet.empty;
1158
1254
  class MarkDecoration extends Decoration {
1159
1255
  constructor(spec) {
1160
1256
  let { start, end } = getInclusive(spec);
1161
- super(start ? -1 /* InlineIncStart */ : 400000000 /* NonIncStart */, end ? 1 /* InlineIncEnd */ : -500000000 /* NonIncEnd */, null, spec);
1257
+ super(start ? -1 /* InlineIncStart */ : 500000000 /* NonIncStart */, end ? 1 /* InlineIncEnd */ : -600000000 /* NonIncEnd */, null, spec);
1162
1258
  this.tagName = spec.tagName || "span";
1163
1259
  this.class = spec.class || "";
1164
1260
  this.attrs = spec.attributes || null;
@@ -1339,7 +1435,7 @@ class LineView extends ContentView {
1339
1435
  let last = this.dom.lastChild;
1340
1436
  while (last && ContentView.get(last) instanceof MarkView)
1341
1437
  last = last.lastChild;
1342
- if (!last ||
1438
+ if (!last || !this.length ||
1343
1439
  last.nodeName != "BR" && ((_a = ContentView.get(last)) === null || _a === void 0 ? void 0 : _a.isEditable) == false &&
1344
1440
  (!browser.ios || !this.children.some(ch => ch instanceof TextView))) {
1345
1441
  let hack = document.createElement("BR");
@@ -1388,6 +1484,7 @@ class BlockWidgetView extends ContentView {
1388
1484
  this.length = length;
1389
1485
  this.type = type;
1390
1486
  this.breakAfter = 0;
1487
+ this.prevWidget = null;
1391
1488
  }
1392
1489
  merge(from, to, source, _takeDeco, openStart, openEnd) {
1393
1490
  if (source && (!(source instanceof BlockWidgetView) || !this.widget.compare(source.widget) ||
@@ -1409,6 +1506,9 @@ class BlockWidgetView extends ContentView {
1409
1506
  get children() { return noChildren; }
1410
1507
  sync() {
1411
1508
  if (!this.dom || !this.widget.updateDOM(this.dom)) {
1509
+ if (this.dom && this.prevWidget)
1510
+ this.prevWidget.destroy(this.dom);
1511
+ this.prevWidget = null;
1412
1512
  this.setDOM(this.widget.toDOM(this.editorView));
1413
1513
  this.dom.contentEditable = "false";
1414
1514
  }
@@ -1422,6 +1522,8 @@ class BlockWidgetView extends ContentView {
1422
1522
  other.widget.constructor == this.widget.constructor) {
1423
1523
  if (!other.widget.eq(this.widget))
1424
1524
  this.markDirty(true);
1525
+ if (this.dom && !this.prevWidget)
1526
+ this.prevWidget = this.widget;
1425
1527
  this.widget = other.widget;
1426
1528
  this.length = other.length;
1427
1529
  this.breakAfter = other.breakAfter;
@@ -2731,7 +2833,12 @@ class DocView extends ContentView {
2731
2833
  let end = next ? next.from - 1 : this.length;
2732
2834
  if (end > pos) {
2733
2835
  let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
2734
- deco.push(Decoration.replace({ widget: new BlockGapWidget(height), block: true, inclusive: true }).range(pos, end));
2836
+ deco.push(Decoration.replace({
2837
+ widget: new BlockGapWidget(height),
2838
+ block: true,
2839
+ inclusive: true,
2840
+ isBlockGap: true,
2841
+ }).range(pos, end));
2735
2842
  }
2736
2843
  if (!next)
2737
2844
  break;
@@ -2850,13 +2957,19 @@ function computeCompositionDeco(view, changes) {
2850
2957
  else if (state.doc.sliceString(newFrom, newTo, LineBreakPlaceholder) != text) {
2851
2958
  return Decoration.none;
2852
2959
  }
2853
- return Decoration.set(Decoration.replace({ widget: new CompositionWidget(node, textNode) }).range(newFrom, newTo));
2960
+ let topView = ContentView.get(node);
2961
+ if (topView instanceof CompositionView)
2962
+ topView = topView.widget.topView;
2963
+ else if (topView)
2964
+ topView.parent = null;
2965
+ return Decoration.set(Decoration.replace({ widget: new CompositionWidget(node, textNode, topView) }).range(newFrom, newTo));
2854
2966
  }
2855
2967
  class CompositionWidget extends WidgetType {
2856
- constructor(top, text) {
2968
+ constructor(top, text, topView) {
2857
2969
  super();
2858
2970
  this.top = top;
2859
2971
  this.text = text;
2972
+ this.topView = topView;
2860
2973
  }
2861
2974
  eq(other) { return this.top == other.top && this.text == other.text; }
2862
2975
  toDOM() { return this.top; }
@@ -5197,8 +5310,9 @@ const baseTheme = buildTheme("." + baseThemeID, {
5197
5310
  verticalAlign: "bottom"
5198
5311
  },
5199
5312
  ".cm-widgetBuffer": {
5200
- verticalAlign: "text-bottom",
5313
+ verticalAlign: "text-top",
5201
5314
  height: "1em",
5315
+ display: "inline"
5202
5316
  },
5203
5317
  ".cm-placeholder": {
5204
5318
  color: "#888",
@@ -5307,19 +5421,16 @@ class DOMObserver {
5307
5421
  this.flushSoon();
5308
5422
  };
5309
5423
  this.onSelectionChange = this.onSelectionChange.bind(this);
5424
+ window.addEventListener("resize", this.onResize = this.onResize.bind(this));
5310
5425
  if (typeof ResizeObserver == "function") {
5311
5426
  this.resize = new ResizeObserver(() => {
5312
- if (this.view.docView.lastUpdate < Date.now() - 75 && this.resizeTimeout < 0)
5313
- this.resizeTimeout = setTimeout(() => {
5314
- this.resizeTimeout = -1;
5315
- this.view.requestMeasure();
5316
- }, 50);
5427
+ if (this.view.docView.lastUpdate < Date.now() - 75)
5428
+ this.onResize();
5317
5429
  });
5318
5430
  this.resize.observe(view.scrollDOM);
5319
5431
  }
5320
5432
  this.start();
5321
- this.onScroll = this.onScroll.bind(this);
5322
- window.addEventListener("scroll", this.onScroll);
5433
+ window.addEventListener("scroll", this.onScroll = this.onScroll.bind(this));
5323
5434
  if (typeof IntersectionObserver == "function") {
5324
5435
  this.intersection = new IntersectionObserver(entries => {
5325
5436
  if (this.parentCheck < 0)
@@ -5345,6 +5456,13 @@ class DOMObserver {
5345
5456
  this.flush(false);
5346
5457
  this.onScrollChanged(e);
5347
5458
  }
5459
+ onResize() {
5460
+ if (this.resizeTimeout < 0)
5461
+ this.resizeTimeout = setTimeout(() => {
5462
+ this.resizeTimeout = -1;
5463
+ this.view.requestMeasure();
5464
+ }, 50);
5465
+ }
5348
5466
  updateGaps(gaps) {
5349
5467
  if (this.gapIntersection && (gaps.length != this.gaps.length || this.gaps.some((g, i) => g != gaps[i]))) {
5350
5468
  this.gapIntersection.disconnect();
@@ -5561,6 +5679,7 @@ class DOMObserver {
5561
5679
  for (let dom of this.scrollTargets)
5562
5680
  dom.removeEventListener("scroll", this.onScroll);
5563
5681
  window.removeEventListener("scroll", this.onScroll);
5682
+ window.removeEventListener("resize", this.onResize);
5564
5683
  this.dom.ownerDocument.removeEventListener("selectionchange", this.onSelectionChange);
5565
5684
  clearTimeout(this.parentCheck);
5566
5685
  clearTimeout(this.resizeTimeout);
@@ -5880,7 +5999,6 @@ class EditorView {
5880
5999
  this.mountStyles();
5881
6000
  this.updateAttrs();
5882
6001
  this.updateState = 0 /* Idle */;
5883
- ensureGlobalHandler();
5884
6002
  this.requestMeasure();
5885
6003
  if (config.parent)
5886
6004
  config.parent.appendChild(this.dom);
@@ -6717,22 +6835,6 @@ const MaxBidiLine = 4096;
6717
6835
  function ensureTop(given, view) {
6718
6836
  return (given == null ? view.contentDOM.getBoundingClientRect().top : given) + view.viewState.paddingTop;
6719
6837
  }
6720
- let resizeDebounce = -1;
6721
- function ensureGlobalHandler() {
6722
- window.addEventListener("resize", () => {
6723
- if (resizeDebounce == -1)
6724
- resizeDebounce = setTimeout(handleResize, 50);
6725
- });
6726
- }
6727
- function handleResize() {
6728
- resizeDebounce = -1;
6729
- let found = document.querySelectorAll(".cm-content");
6730
- for (let i = 0; i < found.length; i++) {
6731
- let docView = ContentView.get(found[i]);
6732
- if (docView)
6733
- docView.editorView.requestMeasure();
6734
- }
6735
- }
6736
6838
  const BadMeasure = {};
6737
6839
  class CachedOrder {
6738
6840
  constructor(from, to, dir, order) {
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ function getSelection(root) {
19
19
  return target.getSelection();
20
20
  }
21
21
  function contains(dom, node) {
22
- return node ? dom.contains(node.nodeType != 1 ? node.parentNode : node) : false;
22
+ return node ? dom == node || dom.contains(node.nodeType != 1 ? node.parentNode : node) : false;
23
23
  }
24
24
  function deepActiveElement() {
25
25
  let elt = document.activeElement;
@@ -778,6 +778,7 @@ class WidgetView extends ContentView {
778
778
  this.widget = widget;
779
779
  this.length = length;
780
780
  this.side = side;
781
+ this.prevWidget = null;
781
782
  }
782
783
  static create(widget, length, side) {
783
784
  return new (widget.customView || WidgetView)(widget, length, side);
@@ -789,6 +790,9 @@ class WidgetView extends ContentView {
789
790
  }
790
791
  sync() {
791
792
  if (!this.dom || !this.widget.updateDOM(this.dom)) {
793
+ if (this.dom && this.prevWidget)
794
+ this.prevWidget.destroy(this.dom);
795
+ this.prevWidget = null;
792
796
  this.setDOM(this.widget.toDOM(this.editorView));
793
797
  this.dom.contentEditable = "false";
794
798
  }
@@ -806,6 +810,8 @@ class WidgetView extends ContentView {
806
810
  if (this.widget.constructor == other.widget.constructor) {
807
811
  if (!this.widget.eq(other.widget))
808
812
  this.markDirty(true);
813
+ if (this.dom && !this.prevWidget)
814
+ this.prevWidget = this.widget;
809
815
  this.widget = other.widget;
810
816
  return true;
811
817
  }
@@ -846,16 +852,69 @@ class WidgetView extends ContentView {
846
852
  }
847
853
  }
848
854
  class CompositionView extends WidgetView {
849
- domAtPos(pos) { return new DOMPos(this.widget.text, pos); }
855
+ domAtPos(pos) {
856
+ let { topView, text } = this.widget;
857
+ if (!topView)
858
+ return new DOMPos(text, Math.min(pos, text.nodeValue.length));
859
+ return scanCompositionTree(pos, 0, topView, text, (v, p) => v.domAtPos(p), p => new DOMPos(text, Math.min(p, text.nodeValue.length)));
860
+ }
850
861
  sync() { this.setDOM(this.widget.toDOM()); }
851
862
  localPosFromDOM(node, offset) {
852
- return !offset ? 0 : node.nodeType == 3 ? Math.min(offset, this.length) : this.length;
863
+ let { topView, text } = this.widget;
864
+ if (!topView)
865
+ return Math.min(offset, this.length);
866
+ return posFromDOMInCompositionTree(node, offset, topView, text);
853
867
  }
854
868
  ignoreMutation() { return false; }
855
869
  get overrideDOMText() { return null; }
856
- coordsAt(pos, side) { return textCoords(this.widget.text, pos, side); }
870
+ coordsAt(pos, side) {
871
+ let { topView, text } = this.widget;
872
+ if (!topView)
873
+ return textCoords(text, pos, side);
874
+ return scanCompositionTree(pos, side, topView, text, (v, pos, side) => v.coordsAt(pos, side), (pos, side) => textCoords(text, pos, side));
875
+ }
876
+ destroy() {
877
+ var _a;
878
+ super.destroy();
879
+ (_a = this.widget.topView) === null || _a === void 0 ? void 0 : _a.destroy();
880
+ }
857
881
  get isEditable() { return true; }
858
882
  }
883
+ // Uses the old structure of a chunk of content view frozen for
884
+ // composition to try and find a reasonable DOM location for the given
885
+ // offset.
886
+ function scanCompositionTree(pos, side, view, text, enterView, fromText) {
887
+ if (view instanceof MarkView) {
888
+ for (let child of view.children) {
889
+ let hasComp = contains(child.dom, text);
890
+ let len = hasComp ? text.nodeValue.length : child.length;
891
+ if (pos < len || pos == len && child.getSide() <= 0)
892
+ return hasComp ? scanCompositionTree(pos, side, child, text, enterView, fromText) : enterView(child, pos, side);
893
+ pos -= len;
894
+ }
895
+ return enterView(view, view.length, -1);
896
+ }
897
+ else if (view.dom == text) {
898
+ return fromText(pos, side);
899
+ }
900
+ else {
901
+ return enterView(view, pos, side);
902
+ }
903
+ }
904
+ function posFromDOMInCompositionTree(node, offset, view, text) {
905
+ if (view instanceof MarkView) {
906
+ for (let child of view.children) {
907
+ let pos = 0, hasComp = contains(child.dom, text);
908
+ if (contains(child.dom, node))
909
+ return pos + (hasComp ? posFromDOMInCompositionTree(node, offset, child, text) : child.localPosFromDOM(node, offset));
910
+ pos += hasComp ? text.nodeValue.length : child.length;
911
+ }
912
+ }
913
+ else if (view.dom == text) {
914
+ return Math.min(offset, text.nodeValue.length);
915
+ }
916
+ return view.localPosFromDOM(node, offset);
917
+ }
859
918
  // These are drawn around uneditable widgets to avoid a number of
860
919
  // browser bugs that show up when the cursor is directly next to
861
920
  // uneditable inline content.
@@ -874,6 +933,7 @@ class WidgetBufferView extends ContentView {
874
933
  if (!this.dom) {
875
934
  let dom = document.createElement("img");
876
935
  dom.className = "cm-widgetBuffer";
936
+ dom.setAttribute("aria-hidden", "true");
877
937
  this.setDOM(dom);
878
938
  }
879
939
  }
@@ -882,13 +942,43 @@ class WidgetBufferView extends ContentView {
882
942
  localPosFromDOM() { return 0; }
883
943
  domBoundsAround() { return null; }
884
944
  coordsAt(pos) {
885
- return this.dom.getBoundingClientRect();
945
+ let imgRect = this.dom.getBoundingClientRect();
946
+ // Since the <img> height doesn't correspond to text height, try
947
+ // to borrow the height from some sibling node.
948
+ let siblingRect = inlineSiblingRect(this, this.side > 0 ? -1 : 1);
949
+ return siblingRect && siblingRect.top < imgRect.bottom && siblingRect.bottom > imgRect.top
950
+ ? { left: imgRect.left, right: imgRect.right, top: siblingRect.top, bottom: siblingRect.bottom } : imgRect;
886
951
  }
887
952
  get overrideDOMText() {
888
953
  return Text.empty;
889
954
  }
890
955
  }
891
956
  TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren;
957
+ function inlineSiblingRect(view, side) {
958
+ let parent = view.parent, index = parent ? parent.children.indexOf(view) : -1;
959
+ while (parent && index >= 0) {
960
+ if (side < 0 ? index > 0 : index < parent.children.length) {
961
+ let next = parent.children[index + side];
962
+ if (next instanceof TextView) {
963
+ let nextRect = next.coordsAt(side < 0 ? next.length : 0, side);
964
+ if (nextRect)
965
+ return nextRect;
966
+ }
967
+ index += side;
968
+ }
969
+ else if (parent instanceof MarkView && parent.parent) {
970
+ index = parent.parent.children.indexOf(parent) + (side < 0 ? 0 : 1);
971
+ parent = parent.parent;
972
+ }
973
+ else {
974
+ let last = parent.dom.lastChild;
975
+ if (last && last.nodeName == "BR")
976
+ return last.getClientRects()[0];
977
+ break;
978
+ }
979
+ }
980
+ return undefined;
981
+ }
892
982
  function inlineDOMAtPos(dom, children, pos) {
893
983
  let i = 0;
894
984
  for (let off = 0; i < children.length; i++) {
@@ -1121,10 +1211,16 @@ class Decoration extends RangeValue {
1121
1211
  a widget, or simply hides it.
1122
1212
  */
1123
1213
  static replace(spec) {
1124
- let block = !!spec.block;
1125
- let { start, end } = getInclusive(spec, block);
1126
- let startSide = (start ? (block ? -300000000 /* BlockIncStart */ : -1 /* InlineIncStart */) : 400000000 /* NonIncStart */) - 1;
1127
- let endSide = (end ? (block ? 200000000 /* BlockIncEnd */ : 1 /* InlineIncEnd */) : -500000000 /* NonIncEnd */) + 1;
1214
+ let block = !!spec.block, startSide, endSide;
1215
+ if (spec.isBlockGap) {
1216
+ startSide = -500000000 /* GapStart */;
1217
+ endSide = 400000000 /* GapEnd */;
1218
+ }
1219
+ else {
1220
+ let { start, end } = getInclusive(spec, block);
1221
+ startSide = (start ? (block ? -300000000 /* BlockIncStart */ : -1 /* InlineIncStart */) : 500000000 /* NonIncStart */) - 1;
1222
+ endSide = (end ? (block ? 200000000 /* BlockIncEnd */ : 1 /* InlineIncEnd */) : -600000000 /* NonIncEnd */) + 1;
1223
+ }
1128
1224
  return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
1129
1225
  }
1130
1226
  /**
@@ -1154,7 +1250,7 @@ Decoration.none = RangeSet.empty;
1154
1250
  class MarkDecoration extends Decoration {
1155
1251
  constructor(spec) {
1156
1252
  let { start, end } = getInclusive(spec);
1157
- super(start ? -1 /* InlineIncStart */ : 400000000 /* NonIncStart */, end ? 1 /* InlineIncEnd */ : -500000000 /* NonIncEnd */, null, spec);
1253
+ super(start ? -1 /* InlineIncStart */ : 500000000 /* NonIncStart */, end ? 1 /* InlineIncEnd */ : -600000000 /* NonIncEnd */, null, spec);
1158
1254
  this.tagName = spec.tagName || "span";
1159
1255
  this.class = spec.class || "";
1160
1256
  this.attrs = spec.attributes || null;
@@ -1335,7 +1431,7 @@ class LineView extends ContentView {
1335
1431
  let last = this.dom.lastChild;
1336
1432
  while (last && ContentView.get(last) instanceof MarkView)
1337
1433
  last = last.lastChild;
1338
- if (!last ||
1434
+ if (!last || !this.length ||
1339
1435
  last.nodeName != "BR" && ((_a = ContentView.get(last)) === null || _a === void 0 ? void 0 : _a.isEditable) == false &&
1340
1436
  (!browser.ios || !this.children.some(ch => ch instanceof TextView))) {
1341
1437
  let hack = document.createElement("BR");
@@ -1384,6 +1480,7 @@ class BlockWidgetView extends ContentView {
1384
1480
  this.length = length;
1385
1481
  this.type = type;
1386
1482
  this.breakAfter = 0;
1483
+ this.prevWidget = null;
1387
1484
  }
1388
1485
  merge(from, to, source, _takeDeco, openStart, openEnd) {
1389
1486
  if (source && (!(source instanceof BlockWidgetView) || !this.widget.compare(source.widget) ||
@@ -1405,6 +1502,9 @@ class BlockWidgetView extends ContentView {
1405
1502
  get children() { return noChildren; }
1406
1503
  sync() {
1407
1504
  if (!this.dom || !this.widget.updateDOM(this.dom)) {
1505
+ if (this.dom && this.prevWidget)
1506
+ this.prevWidget.destroy(this.dom);
1507
+ this.prevWidget = null;
1408
1508
  this.setDOM(this.widget.toDOM(this.editorView));
1409
1509
  this.dom.contentEditable = "false";
1410
1510
  }
@@ -1418,6 +1518,8 @@ class BlockWidgetView extends ContentView {
1418
1518
  other.widget.constructor == this.widget.constructor) {
1419
1519
  if (!other.widget.eq(this.widget))
1420
1520
  this.markDirty(true);
1521
+ if (this.dom && !this.prevWidget)
1522
+ this.prevWidget = this.widget;
1421
1523
  this.widget = other.widget;
1422
1524
  this.length = other.length;
1423
1525
  this.breakAfter = other.breakAfter;
@@ -2726,7 +2828,12 @@ class DocView extends ContentView {
2726
2828
  let end = next ? next.from - 1 : this.length;
2727
2829
  if (end > pos) {
2728
2830
  let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
2729
- deco.push(Decoration.replace({ widget: new BlockGapWidget(height), block: true, inclusive: true }).range(pos, end));
2831
+ deco.push(Decoration.replace({
2832
+ widget: new BlockGapWidget(height),
2833
+ block: true,
2834
+ inclusive: true,
2835
+ isBlockGap: true,
2836
+ }).range(pos, end));
2730
2837
  }
2731
2838
  if (!next)
2732
2839
  break;
@@ -2845,13 +2952,19 @@ function computeCompositionDeco(view, changes) {
2845
2952
  else if (state.doc.sliceString(newFrom, newTo, LineBreakPlaceholder) != text) {
2846
2953
  return Decoration.none;
2847
2954
  }
2848
- return Decoration.set(Decoration.replace({ widget: new CompositionWidget(node, textNode) }).range(newFrom, newTo));
2955
+ let topView = ContentView.get(node);
2956
+ if (topView instanceof CompositionView)
2957
+ topView = topView.widget.topView;
2958
+ else if (topView)
2959
+ topView.parent = null;
2960
+ return Decoration.set(Decoration.replace({ widget: new CompositionWidget(node, textNode, topView) }).range(newFrom, newTo));
2849
2961
  }
2850
2962
  class CompositionWidget extends WidgetType {
2851
- constructor(top, text) {
2963
+ constructor(top, text, topView) {
2852
2964
  super();
2853
2965
  this.top = top;
2854
2966
  this.text = text;
2967
+ this.topView = topView;
2855
2968
  }
2856
2969
  eq(other) { return this.top == other.top && this.text == other.text; }
2857
2970
  toDOM() { return this.top; }
@@ -5191,8 +5304,9 @@ const baseTheme = /*@__PURE__*/buildTheme("." + baseThemeID, {
5191
5304
  verticalAlign: "bottom"
5192
5305
  },
5193
5306
  ".cm-widgetBuffer": {
5194
- verticalAlign: "text-bottom",
5307
+ verticalAlign: "text-top",
5195
5308
  height: "1em",
5309
+ display: "inline"
5196
5310
  },
5197
5311
  ".cm-placeholder": {
5198
5312
  color: "#888",
@@ -5301,19 +5415,16 @@ class DOMObserver {
5301
5415
  this.flushSoon();
5302
5416
  };
5303
5417
  this.onSelectionChange = this.onSelectionChange.bind(this);
5418
+ window.addEventListener("resize", this.onResize = this.onResize.bind(this));
5304
5419
  if (typeof ResizeObserver == "function") {
5305
5420
  this.resize = new ResizeObserver(() => {
5306
- if (this.view.docView.lastUpdate < Date.now() - 75 && this.resizeTimeout < 0)
5307
- this.resizeTimeout = setTimeout(() => {
5308
- this.resizeTimeout = -1;
5309
- this.view.requestMeasure();
5310
- }, 50);
5421
+ if (this.view.docView.lastUpdate < Date.now() - 75)
5422
+ this.onResize();
5311
5423
  });
5312
5424
  this.resize.observe(view.scrollDOM);
5313
5425
  }
5314
5426
  this.start();
5315
- this.onScroll = this.onScroll.bind(this);
5316
- window.addEventListener("scroll", this.onScroll);
5427
+ window.addEventListener("scroll", this.onScroll = this.onScroll.bind(this));
5317
5428
  if (typeof IntersectionObserver == "function") {
5318
5429
  this.intersection = new IntersectionObserver(entries => {
5319
5430
  if (this.parentCheck < 0)
@@ -5339,6 +5450,13 @@ class DOMObserver {
5339
5450
  this.flush(false);
5340
5451
  this.onScrollChanged(e);
5341
5452
  }
5453
+ onResize() {
5454
+ if (this.resizeTimeout < 0)
5455
+ this.resizeTimeout = setTimeout(() => {
5456
+ this.resizeTimeout = -1;
5457
+ this.view.requestMeasure();
5458
+ }, 50);
5459
+ }
5342
5460
  updateGaps(gaps) {
5343
5461
  if (this.gapIntersection && (gaps.length != this.gaps.length || this.gaps.some((g, i) => g != gaps[i]))) {
5344
5462
  this.gapIntersection.disconnect();
@@ -5555,6 +5673,7 @@ class DOMObserver {
5555
5673
  for (let dom of this.scrollTargets)
5556
5674
  dom.removeEventListener("scroll", this.onScroll);
5557
5675
  window.removeEventListener("scroll", this.onScroll);
5676
+ window.removeEventListener("resize", this.onResize);
5558
5677
  this.dom.ownerDocument.removeEventListener("selectionchange", this.onSelectionChange);
5559
5678
  clearTimeout(this.parentCheck);
5560
5679
  clearTimeout(this.resizeTimeout);
@@ -5874,7 +5993,6 @@ class EditorView {
5874
5993
  this.mountStyles();
5875
5994
  this.updateAttrs();
5876
5995
  this.updateState = 0 /* Idle */;
5877
- ensureGlobalHandler();
5878
5996
  this.requestMeasure();
5879
5997
  if (config.parent)
5880
5998
  config.parent.appendChild(this.dom);
@@ -6711,22 +6829,6 @@ const MaxBidiLine = 4096;
6711
6829
  function ensureTop(given, view) {
6712
6830
  return (given == null ? view.contentDOM.getBoundingClientRect().top : given) + view.viewState.paddingTop;
6713
6831
  }
6714
- let resizeDebounce = -1;
6715
- function ensureGlobalHandler() {
6716
- window.addEventListener("resize", () => {
6717
- if (resizeDebounce == -1)
6718
- resizeDebounce = setTimeout(handleResize, 50);
6719
- });
6720
- }
6721
- function handleResize() {
6722
- resizeDebounce = -1;
6723
- let found = document.querySelectorAll(".cm-content");
6724
- for (let i = 0; i < found.length; i++) {
6725
- let docView = ContentView.get(found[i]);
6726
- if (docView)
6727
- docView.editorView.requestMeasure();
6728
- }
6729
- }
6730
6832
  const BadMeasure = {};
6731
6833
  class CachedOrder {
6732
6834
  constructor(from, to, dir, order) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "0.19.44",
3
+ "version": "0.19.47",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",