@codemirror/view 6.24.0 → 6.25.0

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,27 @@
1
+ ## 6.25.0 (2024-03-04)
2
+
3
+ ### Bug fixes
4
+
5
+ Properly recognize Android GBoard enter presses that strip a space at the end of the line as enter.
6
+
7
+ Fix a bug that caused the gutter to have the wrong height when the editor was scaled after construction.
8
+
9
+ When starting a composition after a non-inclusive mark decoration, temporarily insert a widget that prevents the composed text from inheriting that mark's styles.
10
+
11
+ Make sure the selection is repositioned when a transaction changes decorations without changing the document.
12
+
13
+ ### New features
14
+
15
+ View plugins can now provide a `docViewUpdate` method that is called whenever the document view is updated.
16
+
17
+ Layers now take a `updateOnDocUpdate` option that controls whether they are automatically updated when the document view changes.
18
+
19
+ ## 6.24.1 (2024-02-19)
20
+
21
+ ### Bug fixes
22
+
23
+ Fix a crash that happens when hover tooltips are active during changes, introduced in 6.24.0.
24
+
1
25
  ## 6.24.0 (2024-02-09)
2
26
 
3
27
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -1726,10 +1726,10 @@ class ContentBuilder {
1726
1726
  if (deco.block) {
1727
1727
  if (deco.startSide > 0 && !this.posCovered())
1728
1728
  this.getLine();
1729
- this.addBlockWidget(new BlockWidgetView(deco.widget || new NullWidget("div"), len, deco));
1729
+ this.addBlockWidget(new BlockWidgetView(deco.widget || NullWidget.block, len, deco));
1730
1730
  }
1731
1731
  else {
1732
- let view = WidgetView.create(deco.widget || new NullWidget("span"), len, len ? 0 : deco.startSide);
1732
+ let view = WidgetView.create(deco.widget || NullWidget.inline, len, len ? 0 : deco.startSide);
1733
1733
  let cursorBefore = this.atCursorPos && !view.isEditable && openStart <= active.length &&
1734
1734
  (from < to || deco.startSide > 0);
1735
1735
  let cursorAfter = !view.isEditable && (from < to || openStart > active.length || deco.startSide <= 0);
@@ -1790,6 +1790,8 @@ class NullWidget extends WidgetType {
1790
1790
  updateDOM(elt) { return elt.nodeName.toLowerCase() == this.tag; }
1791
1791
  get isHidden() { return true; }
1792
1792
  }
1793
+ NullWidget.inline = new NullWidget("span");
1794
+ NullWidget.block = new NullWidget("div");
1793
1795
 
1794
1796
  /**
1795
1797
  Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
@@ -2650,10 +2652,11 @@ class DocView extends ContentView {
2650
2652
  super();
2651
2653
  this.view = view;
2652
2654
  this.decorations = [];
2653
- this.dynamicDecorationMap = [];
2655
+ this.dynamicDecorationMap = [false];
2654
2656
  this.domChanged = null;
2655
2657
  this.hasComposition = null;
2656
2658
  this.markedForComposition = new Set;
2659
+ this.compositionBarrier = Decoration.none;
2657
2660
  // Track a minimum width for the editor. When measuring sizes in
2658
2661
  // measureVisibleLineHeights, this is updated to point at the width
2659
2662
  // of a given element and its extent in the document. When a change
@@ -2917,7 +2920,7 @@ class DocView extends ContentView {
2917
2920
  // composition, avoid moving it across it and disrupting the
2918
2921
  // composition.
2919
2922
  suppressWidgetCursorChange(sel, cursor) {
2920
- return this.hasComposition && cursor.empty &&
2923
+ return this.hasComposition && cursor.empty && !this.compositionBarrier.size &&
2921
2924
  isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset) &&
2922
2925
  this.posFromDOM(sel.focusNode, sel.focusOffset) == cursor.head;
2923
2926
  }
@@ -3125,8 +3128,9 @@ class DocView extends ContentView {
3125
3128
  return Decoration.set(deco);
3126
3129
  }
3127
3130
  updateDeco() {
3128
- let allDeco = this.view.state.facet(decorations).map((d, i) => {
3129
- let dynamic = this.dynamicDecorationMap[i] = typeof d == "function";
3131
+ let i = 1;
3132
+ let allDeco = this.view.state.facet(decorations).map(d => {
3133
+ let dynamic = this.dynamicDecorationMap[i++] = typeof d == "function";
3130
3134
  return dynamic ? d(this.view) : d;
3131
3135
  });
3132
3136
  let dynamicOuter = false, outerDeco = this.view.state.facet(outerDecorations).map((d, i) => {
@@ -3136,16 +3140,46 @@ class DocView extends ContentView {
3136
3140
  return dynamic ? d(this.view) : d;
3137
3141
  });
3138
3142
  if (outerDeco.length) {
3139
- this.dynamicDecorationMap[allDeco.length] = dynamicOuter;
3143
+ this.dynamicDecorationMap[i++] = dynamicOuter;
3140
3144
  allDeco.push(state.RangeSet.join(outerDeco));
3141
3145
  }
3142
- for (let i = allDeco.length; i < allDeco.length + 3; i++)
3143
- this.dynamicDecorationMap[i] = false;
3144
- return this.decorations = [
3146
+ this.decorations = [
3147
+ this.compositionBarrier,
3145
3148
  ...allDeco,
3146
3149
  this.computeBlockGapDeco(),
3147
3150
  this.view.viewState.lineGapDeco
3148
3151
  ];
3152
+ while (i < this.decorations.length)
3153
+ this.dynamicDecorationMap[i++] = false;
3154
+ return this.decorations;
3155
+ }
3156
+ // Starting a composition will style the inserted text with the
3157
+ // style of the text before it, and this is only cleared when the
3158
+ // composition ends, because touching it before that will abort it.
3159
+ // This (called from compositionstart handler) tries to notice when
3160
+ // the cursor is after a non-inclusive mark, where the styling could
3161
+ // be jarring, and insert an ad-hoc widget before the cursor to
3162
+ // isolate it from the style before it.
3163
+ maybeCreateCompositionBarrier() {
3164
+ let { main: { head, empty } } = this.view.state.selection;
3165
+ if (!empty)
3166
+ return false;
3167
+ let found = null;
3168
+ for (let set of this.decorations) {
3169
+ set.between(head, head, (from, to, value) => {
3170
+ if (value.point)
3171
+ found = false;
3172
+ else if (value.endSide < 0 && from < head && to == head)
3173
+ found = true;
3174
+ });
3175
+ if (found === false)
3176
+ break;
3177
+ }
3178
+ this.compositionBarrier = found ? Decoration.set(compositionBarrierWidget.range(head)) : Decoration.none;
3179
+ return !!found;
3180
+ }
3181
+ clearCompositionBarrier() {
3182
+ this.compositionBarrier = Decoration.none;
3149
3183
  }
3150
3184
  scrollIntoView(target) {
3151
3185
  if (target.isSnapshot) {
@@ -3170,6 +3204,7 @@ class DocView extends ContentView {
3170
3204
  scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, Math.max(Math.min(target.xMargin, offsetWidth), -offsetWidth), Math.max(Math.min(target.yMargin, offsetHeight), -offsetHeight), this.view.textDirection == exports.Direction.LTR);
3171
3205
  }
3172
3206
  }
3207
+ const compositionBarrierWidget = Decoration.widget({ side: -1, widget: NullWidget.inline });
3173
3208
  function betweenUneditable(pos) {
3174
3209
  return pos.node.nodeType == 1 && pos.node.firstChild &&
3175
3210
  (pos.offset == 0 || pos.node.childNodes[pos.offset - 1].contentEditable == "false") &&
@@ -4419,6 +4454,10 @@ observers.compositionstart = observers.compositionupdate = view => {
4419
4454
  if (view.inputState.composing < 0) {
4420
4455
  // FIXME possibly set a timeout to clear it again on Android
4421
4456
  view.inputState.composing = 0;
4457
+ if (view.docView.maybeCreateCompositionBarrier()) {
4458
+ view.update([]);
4459
+ view.docView.clearCompositionBarrier();
4460
+ }
4422
4461
  }
4423
4462
  };
4424
4463
  observers.compositionend = view => {
@@ -6298,7 +6337,10 @@ function applyDOMChange(view, domChange) {
6298
6337
  // events and the pendingAndroidKey mechanism, but that's not
6299
6338
  // reliable in all situations.)
6300
6339
  if (browser.android &&
6301
- ((change.from == sel.from && change.to == sel.to &&
6340
+ ((change.to == sel.to &&
6341
+ // GBoard will sometimes remove a space it just inserted
6342
+ // after a completion when you press enter
6343
+ (change.from == sel.from || change.from == sel.from - 1 && view.state.sliceDoc(change.from, sel.from) == " ") &&
6302
6344
  change.insert.length == 1 && change.insert.lines == 2 &&
6303
6345
  dispatchKey(view.contentDOM, "Enter", 13)) ||
6304
6346
  ((change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 ||
@@ -7118,6 +7160,8 @@ class EditorView {
7118
7160
  this.viewState.mustMeasureContent = true;
7119
7161
  if (redrawn || attrsChanged || scrollTarget || this.viewState.mustEnforceCursorAssoc || this.viewState.mustMeasureContent)
7120
7162
  this.requestMeasure();
7163
+ if (redrawn)
7164
+ this.docViewUpdate();
7121
7165
  if (!update.empty)
7122
7166
  for (let listener of this.state.facet(updateListener)) {
7123
7167
  try {
@@ -7205,6 +7249,19 @@ class EditorView {
7205
7249
  if (prevSpecs != specs)
7206
7250
  this.inputState.ensureHandlers(this.plugins);
7207
7251
  }
7252
+ docViewUpdate() {
7253
+ for (let plugin of this.plugins) {
7254
+ let val = plugin.value;
7255
+ if (val && val.docViewUpdate) {
7256
+ try {
7257
+ val.docViewUpdate(this);
7258
+ }
7259
+ catch (e) {
7260
+ logException(this.state, e, "doc view update listener");
7261
+ }
7262
+ }
7263
+ }
7264
+ }
7208
7265
  /**
7209
7266
  @internal
7210
7267
  */
@@ -7275,6 +7332,8 @@ class EditorView {
7275
7332
  this.inputState.update(update);
7276
7333
  this.updateAttrs();
7277
7334
  redrawn = this.docView.update(update);
7335
+ if (redrawn)
7336
+ this.docViewUpdate();
7278
7337
  }
7279
7338
  for (let i = 0; i < measuring.length; i++)
7280
7339
  if (measured[i] != BadMeasure) {
@@ -8423,6 +8482,10 @@ class LayerView {
8423
8482
  update.view.requestMeasure(this.measureReq);
8424
8483
  }
8425
8484
  }
8485
+ docViewUpdate(view) {
8486
+ if (this.layer.updateOnDocViewUpdate !== false)
8487
+ view.requestMeasure(this.measureReq);
8488
+ }
8426
8489
  setOrder(state) {
8427
8490
  let pos = 0, order = state.facet(layerOrder);
8428
8491
  while (pos < order.length && order[pos] != this.layer)
@@ -9811,6 +9874,7 @@ function hoverTooltip(source, options = {}) {
9811
9874
  else if (options.hideOn)
9812
9875
  value = value.filter(v => !options.hideOn(tr, v));
9813
9876
  if (tr.docChanged) {
9877
+ let mapped = [];
9814
9878
  for (let tooltip of value) {
9815
9879
  let newPos = tr.changes.mapPos(tooltip.pos, -1, state.MapMode.TrackDel);
9816
9880
  if (newPos != null) {
@@ -9818,8 +9882,10 @@ function hoverTooltip(source, options = {}) {
9818
9882
  copy.pos = newPos;
9819
9883
  if (copy.end != null)
9820
9884
  copy.end = tr.changes.mapPos(copy.end);
9885
+ mapped.push(copy);
9821
9886
  }
9822
9887
  }
9888
+ value = mapped;
9823
9889
  }
9824
9890
  }
9825
9891
  for (let effect of tr.effects) {
@@ -10152,8 +10218,9 @@ const gutterView = ViewPlugin.fromClass(class {
10152
10218
  let vpOverlap = Math.min(vpA.to, vpB.to) - Math.max(vpA.from, vpB.from);
10153
10219
  this.syncGutters(vpOverlap < (vpB.to - vpB.from) * 0.8);
10154
10220
  }
10155
- if (update.geometryChanged)
10156
- this.dom.style.minHeight = this.view.contentHeight + "px";
10221
+ if (update.geometryChanged) {
10222
+ this.dom.style.minHeight = (this.view.contentHeight / this.view.scaleY) + "px";
10223
+ }
10157
10224
  if (this.view.state.facet(unfixGutters) != !this.fixed) {
10158
10225
  this.fixed = !this.fixed;
10159
10226
  this.dom.style.position = this.fixed ? "sticky" : "";
package/dist/index.d.cts CHANGED
@@ -404,6 +404,13 @@ interface PluginValue extends Object {
404
404
  */
405
405
  update?(update: ViewUpdate): void;
406
406
  /**
407
+ Called when the document view is updated (due to content,
408
+ decoration, or viewport changes). Should not try to immediately
409
+ start another view update. Often useful for calling
410
+ [`requestMeasure`](https://codemirror.net/6/docs/ref/#view.EditorView.requestMeasure).
411
+ */
412
+ docViewUpdate?(view: EditorView): void;
413
+ /**
407
414
  Called when the plugin is no longer going to be used. Should
408
415
  revert any changes the plugin made to the DOM.
409
416
  */
@@ -791,6 +798,7 @@ declare class EditorView {
791
798
  */
792
799
  setState(newState: EditorState): void;
793
800
  private updatePlugins;
801
+ private docViewUpdate;
794
802
  /**
795
803
  Get the CSS classes for the currently active editor themes.
796
804
  */
@@ -1638,6 +1646,11 @@ interface LayerConfig {
1638
1646
  */
1639
1647
  update(update: ViewUpdate, layer: HTMLElement): boolean;
1640
1648
  /**
1649
+ Whether to update this layer every time the document view
1650
+ changes. Defaults to true.
1651
+ */
1652
+ updateOnDocViewUpdate: boolean;
1653
+ /**
1641
1654
  Build a set of markers for this layer, and measure their
1642
1655
  dimensions.
1643
1656
  */
package/dist/index.d.ts CHANGED
@@ -404,6 +404,13 @@ interface PluginValue extends Object {
404
404
  */
405
405
  update?(update: ViewUpdate): void;
406
406
  /**
407
+ Called when the document view is updated (due to content,
408
+ decoration, or viewport changes). Should not try to immediately
409
+ start another view update. Often useful for calling
410
+ [`requestMeasure`](https://codemirror.net/6/docs/ref/#view.EditorView.requestMeasure).
411
+ */
412
+ docViewUpdate?(view: EditorView): void;
413
+ /**
407
414
  Called when the plugin is no longer going to be used. Should
408
415
  revert any changes the plugin made to the DOM.
409
416
  */
@@ -791,6 +798,7 @@ declare class EditorView {
791
798
  */
792
799
  setState(newState: EditorState): void;
793
800
  private updatePlugins;
801
+ private docViewUpdate;
794
802
  /**
795
803
  Get the CSS classes for the currently active editor themes.
796
804
  */
@@ -1638,6 +1646,11 @@ interface LayerConfig {
1638
1646
  */
1639
1647
  update(update: ViewUpdate, layer: HTMLElement): boolean;
1640
1648
  /**
1649
+ Whether to update this layer every time the document view
1650
+ changes. Defaults to true.
1651
+ */
1652
+ updateOnDocViewUpdate: boolean;
1653
+ /**
1641
1654
  Build a set of markers for this layer, and measure their
1642
1655
  dimensions.
1643
1656
  */
package/dist/index.js CHANGED
@@ -1723,10 +1723,10 @@ class ContentBuilder {
1723
1723
  if (deco.block) {
1724
1724
  if (deco.startSide > 0 && !this.posCovered())
1725
1725
  this.getLine();
1726
- this.addBlockWidget(new BlockWidgetView(deco.widget || new NullWidget("div"), len, deco));
1726
+ this.addBlockWidget(new BlockWidgetView(deco.widget || NullWidget.block, len, deco));
1727
1727
  }
1728
1728
  else {
1729
- let view = WidgetView.create(deco.widget || new NullWidget("span"), len, len ? 0 : deco.startSide);
1729
+ let view = WidgetView.create(deco.widget || NullWidget.inline, len, len ? 0 : deco.startSide);
1730
1730
  let cursorBefore = this.atCursorPos && !view.isEditable && openStart <= active.length &&
1731
1731
  (from < to || deco.startSide > 0);
1732
1732
  let cursorAfter = !view.isEditable && (from < to || openStart > active.length || deco.startSide <= 0);
@@ -1787,6 +1787,8 @@ class NullWidget extends WidgetType {
1787
1787
  updateDOM(elt) { return elt.nodeName.toLowerCase() == this.tag; }
1788
1788
  get isHidden() { return true; }
1789
1789
  }
1790
+ NullWidget.inline = /*@__PURE__*/new NullWidget("span");
1791
+ NullWidget.block = /*@__PURE__*/new NullWidget("div");
1790
1792
 
1791
1793
  /**
1792
1794
  Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
@@ -2646,10 +2648,11 @@ class DocView extends ContentView {
2646
2648
  super();
2647
2649
  this.view = view;
2648
2650
  this.decorations = [];
2649
- this.dynamicDecorationMap = [];
2651
+ this.dynamicDecorationMap = [false];
2650
2652
  this.domChanged = null;
2651
2653
  this.hasComposition = null;
2652
2654
  this.markedForComposition = new Set;
2655
+ this.compositionBarrier = Decoration.none;
2653
2656
  // Track a minimum width for the editor. When measuring sizes in
2654
2657
  // measureVisibleLineHeights, this is updated to point at the width
2655
2658
  // of a given element and its extent in the document. When a change
@@ -2913,7 +2916,7 @@ class DocView extends ContentView {
2913
2916
  // composition, avoid moving it across it and disrupting the
2914
2917
  // composition.
2915
2918
  suppressWidgetCursorChange(sel, cursor) {
2916
- return this.hasComposition && cursor.empty &&
2919
+ return this.hasComposition && cursor.empty && !this.compositionBarrier.size &&
2917
2920
  isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset) &&
2918
2921
  this.posFromDOM(sel.focusNode, sel.focusOffset) == cursor.head;
2919
2922
  }
@@ -3121,8 +3124,9 @@ class DocView extends ContentView {
3121
3124
  return Decoration.set(deco);
3122
3125
  }
3123
3126
  updateDeco() {
3124
- let allDeco = this.view.state.facet(decorations).map((d, i) => {
3125
- let dynamic = this.dynamicDecorationMap[i] = typeof d == "function";
3127
+ let i = 1;
3128
+ let allDeco = this.view.state.facet(decorations).map(d => {
3129
+ let dynamic = this.dynamicDecorationMap[i++] = typeof d == "function";
3126
3130
  return dynamic ? d(this.view) : d;
3127
3131
  });
3128
3132
  let dynamicOuter = false, outerDeco = this.view.state.facet(outerDecorations).map((d, i) => {
@@ -3132,16 +3136,46 @@ class DocView extends ContentView {
3132
3136
  return dynamic ? d(this.view) : d;
3133
3137
  });
3134
3138
  if (outerDeco.length) {
3135
- this.dynamicDecorationMap[allDeco.length] = dynamicOuter;
3139
+ this.dynamicDecorationMap[i++] = dynamicOuter;
3136
3140
  allDeco.push(RangeSet.join(outerDeco));
3137
3141
  }
3138
- for (let i = allDeco.length; i < allDeco.length + 3; i++)
3139
- this.dynamicDecorationMap[i] = false;
3140
- return this.decorations = [
3142
+ this.decorations = [
3143
+ this.compositionBarrier,
3141
3144
  ...allDeco,
3142
3145
  this.computeBlockGapDeco(),
3143
3146
  this.view.viewState.lineGapDeco
3144
3147
  ];
3148
+ while (i < this.decorations.length)
3149
+ this.dynamicDecorationMap[i++] = false;
3150
+ return this.decorations;
3151
+ }
3152
+ // Starting a composition will style the inserted text with the
3153
+ // style of the text before it, and this is only cleared when the
3154
+ // composition ends, because touching it before that will abort it.
3155
+ // This (called from compositionstart handler) tries to notice when
3156
+ // the cursor is after a non-inclusive mark, where the styling could
3157
+ // be jarring, and insert an ad-hoc widget before the cursor to
3158
+ // isolate it from the style before it.
3159
+ maybeCreateCompositionBarrier() {
3160
+ let { main: { head, empty } } = this.view.state.selection;
3161
+ if (!empty)
3162
+ return false;
3163
+ let found = null;
3164
+ for (let set of this.decorations) {
3165
+ set.between(head, head, (from, to, value) => {
3166
+ if (value.point)
3167
+ found = false;
3168
+ else if (value.endSide < 0 && from < head && to == head)
3169
+ found = true;
3170
+ });
3171
+ if (found === false)
3172
+ break;
3173
+ }
3174
+ this.compositionBarrier = found ? Decoration.set(compositionBarrierWidget.range(head)) : Decoration.none;
3175
+ return !!found;
3176
+ }
3177
+ clearCompositionBarrier() {
3178
+ this.compositionBarrier = Decoration.none;
3145
3179
  }
3146
3180
  scrollIntoView(target) {
3147
3181
  if (target.isSnapshot) {
@@ -3166,6 +3200,7 @@ class DocView extends ContentView {
3166
3200
  scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, Math.max(Math.min(target.xMargin, offsetWidth), -offsetWidth), Math.max(Math.min(target.yMargin, offsetHeight), -offsetHeight), this.view.textDirection == Direction.LTR);
3167
3201
  }
3168
3202
  }
3203
+ const compositionBarrierWidget = /*@__PURE__*/Decoration.widget({ side: -1, widget: NullWidget.inline });
3169
3204
  function betweenUneditable(pos) {
3170
3205
  return pos.node.nodeType == 1 && pos.node.firstChild &&
3171
3206
  (pos.offset == 0 || pos.node.childNodes[pos.offset - 1].contentEditable == "false") &&
@@ -4415,6 +4450,10 @@ observers.compositionstart = observers.compositionupdate = view => {
4415
4450
  if (view.inputState.composing < 0) {
4416
4451
  // FIXME possibly set a timeout to clear it again on Android
4417
4452
  view.inputState.composing = 0;
4453
+ if (view.docView.maybeCreateCompositionBarrier()) {
4454
+ view.update([]);
4455
+ view.docView.clearCompositionBarrier();
4456
+ }
4418
4457
  }
4419
4458
  };
4420
4459
  observers.compositionend = view => {
@@ -6293,7 +6332,10 @@ function applyDOMChange(view, domChange) {
6293
6332
  // events and the pendingAndroidKey mechanism, but that's not
6294
6333
  // reliable in all situations.)
6295
6334
  if (browser.android &&
6296
- ((change.from == sel.from && change.to == sel.to &&
6335
+ ((change.to == sel.to &&
6336
+ // GBoard will sometimes remove a space it just inserted
6337
+ // after a completion when you press enter
6338
+ (change.from == sel.from || change.from == sel.from - 1 && view.state.sliceDoc(change.from, sel.from) == " ") &&
6297
6339
  change.insert.length == 1 && change.insert.lines == 2 &&
6298
6340
  dispatchKey(view.contentDOM, "Enter", 13)) ||
6299
6341
  ((change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 ||
@@ -7113,6 +7155,8 @@ class EditorView {
7113
7155
  this.viewState.mustMeasureContent = true;
7114
7156
  if (redrawn || attrsChanged || scrollTarget || this.viewState.mustEnforceCursorAssoc || this.viewState.mustMeasureContent)
7115
7157
  this.requestMeasure();
7158
+ if (redrawn)
7159
+ this.docViewUpdate();
7116
7160
  if (!update.empty)
7117
7161
  for (let listener of this.state.facet(updateListener)) {
7118
7162
  try {
@@ -7200,6 +7244,19 @@ class EditorView {
7200
7244
  if (prevSpecs != specs)
7201
7245
  this.inputState.ensureHandlers(this.plugins);
7202
7246
  }
7247
+ docViewUpdate() {
7248
+ for (let plugin of this.plugins) {
7249
+ let val = plugin.value;
7250
+ if (val && val.docViewUpdate) {
7251
+ try {
7252
+ val.docViewUpdate(this);
7253
+ }
7254
+ catch (e) {
7255
+ logException(this.state, e, "doc view update listener");
7256
+ }
7257
+ }
7258
+ }
7259
+ }
7203
7260
  /**
7204
7261
  @internal
7205
7262
  */
@@ -7270,6 +7327,8 @@ class EditorView {
7270
7327
  this.inputState.update(update);
7271
7328
  this.updateAttrs();
7272
7329
  redrawn = this.docView.update(update);
7330
+ if (redrawn)
7331
+ this.docViewUpdate();
7273
7332
  }
7274
7333
  for (let i = 0; i < measuring.length; i++)
7275
7334
  if (measured[i] != BadMeasure) {
@@ -8418,6 +8477,10 @@ class LayerView {
8418
8477
  update.view.requestMeasure(this.measureReq);
8419
8478
  }
8420
8479
  }
8480
+ docViewUpdate(view) {
8481
+ if (this.layer.updateOnDocViewUpdate !== false)
8482
+ view.requestMeasure(this.measureReq);
8483
+ }
8421
8484
  setOrder(state) {
8422
8485
  let pos = 0, order = state.facet(layerOrder);
8423
8486
  while (pos < order.length && order[pos] != this.layer)
@@ -9806,6 +9869,7 @@ function hoverTooltip(source, options = {}) {
9806
9869
  else if (options.hideOn)
9807
9870
  value = value.filter(v => !options.hideOn(tr, v));
9808
9871
  if (tr.docChanged) {
9872
+ let mapped = [];
9809
9873
  for (let tooltip of value) {
9810
9874
  let newPos = tr.changes.mapPos(tooltip.pos, -1, MapMode.TrackDel);
9811
9875
  if (newPos != null) {
@@ -9813,8 +9877,10 @@ function hoverTooltip(source, options = {}) {
9813
9877
  copy.pos = newPos;
9814
9878
  if (copy.end != null)
9815
9879
  copy.end = tr.changes.mapPos(copy.end);
9880
+ mapped.push(copy);
9816
9881
  }
9817
9882
  }
9883
+ value = mapped;
9818
9884
  }
9819
9885
  }
9820
9886
  for (let effect of tr.effects) {
@@ -10147,8 +10213,9 @@ const gutterView = /*@__PURE__*/ViewPlugin.fromClass(class {
10147
10213
  let vpOverlap = Math.min(vpA.to, vpB.to) - Math.max(vpA.from, vpB.from);
10148
10214
  this.syncGutters(vpOverlap < (vpB.to - vpB.from) * 0.8);
10149
10215
  }
10150
- if (update.geometryChanged)
10151
- this.dom.style.minHeight = this.view.contentHeight + "px";
10216
+ if (update.geometryChanged) {
10217
+ this.dom.style.minHeight = (this.view.contentHeight / this.view.scaleY) + "px";
10218
+ }
10152
10219
  if (this.view.state.facet(unfixGutters) != !this.fixed) {
10153
10220
  this.fixed = !this.fixed;
10154
10221
  this.dom.style.position = this.fixed ? "sticky" : "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.24.0",
3
+ "version": "6.25.0",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",