@codemirror/view 6.41.1 → 6.42.1

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,23 @@
1
+ ## 6.42.1 (2026-05-07)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix a bug where block wrappers could be applied incorrectly during composition.
6
+
7
+ Fix an issue leading to constant text geometry resets in variable-width fonts.
8
+
9
+ ## 6.42.0 (2026-05-06)
10
+
11
+ ### Bug fixes
12
+
13
+ Make sure `posAtCoords` doesn't recurse endlessly.
14
+
15
+ ### New features
16
+
17
+ The new `activateHover` function can be used to explicitly activate hover tooltips at a given position.
18
+
19
+ `closeHoverTooltip` now allows you to close a specific hover tooltip.
20
+
1
21
  ## 6.41.1 (2026-04-18)
2
22
 
3
23
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -2311,6 +2311,7 @@ class TileBuilder {
2311
2311
  this.cache.reused.set(oldTile, 2 /* Reused.DOM */);
2312
2312
  let text = new TextTile(composition.text, composition.text.nodeValue);
2313
2313
  text.flags |= 8 /* TileFlag.Composition */;
2314
+ this.pos = composition.range.toB;
2314
2315
  head.append(text);
2315
2316
  }
2316
2317
  addInlineWidget(widget, marks, openStart) {
@@ -3835,7 +3836,7 @@ class InlineCoordsScan {
3835
3836
  // (including the position after the last piece). For a text tile,
3836
3837
  // these will be character clusters, for a composite tile, these
3837
3838
  // will be child tiles.
3838
- scan(positions, getRects) {
3839
+ scan(positions, getRects, recursed = false) {
3839
3840
  let lo = 0, hi = positions.length - 1, seen = new Set();
3840
3841
  let bidi = this.bidiIn(positions[0], positions[hi]);
3841
3842
  let above, below;
@@ -3908,19 +3909,19 @@ class InlineCoordsScan {
3908
3909
  if (!closestRect) {
3909
3910
  let side = above && (!below || (this.y - above.bottom < below.top - this.y)) ? above : below;
3910
3911
  this.y = (side.top + side.bottom) / 2;
3911
- return this.scan(positions, getRects);
3912
+ return this.scan(positions, getRects, true);
3912
3913
  }
3913
3914
  // Handle the case where closest matched a higher element on the
3914
3915
  // same line as an element below/above the coords
3915
- if (closestDx) {
3916
+ if (closestDx && !recursed) {
3916
3917
  let { top, bottom } = closestRect;
3917
3918
  if (above && above.bottom > (top + top + bottom) / 3) {
3918
3919
  this.y = above.bottom - 1;
3919
- return this.scan(positions, getRects);
3920
+ return this.scan(positions, getRects, true);
3920
3921
  }
3921
3922
  if (below && below.top < (top + bottom + bottom) / 3) {
3922
3923
  this.y = below.top + 1;
3923
- return this.scan(positions, getRects);
3924
+ return this.scan(positions, getRects, true);
3924
3925
  }
3925
3926
  }
3926
3927
  let ltr = (bidi ? this.dirAt(positions[closestI], 1) : this.baseDir) == exports.Direction.LTR;
@@ -5214,7 +5215,7 @@ observers.compositionend = view => {
5214
5215
  view.inputState.compositionFirstChange = null;
5215
5216
  if (browser.chrome && browser.android) {
5216
5217
  // Delay flushing for a bit on Android because it'll often fire a
5217
- // bunch of contradictory changes in a row at end of compositon
5218
+ // bunch of contradictory changes in a row at end of composition
5218
5219
  view.observer.flushSoon();
5219
5220
  }
5220
5221
  else if (view.inputState.compositionPendingChange) {
@@ -5345,8 +5346,7 @@ class HeightOracle {
5345
5346
  }
5346
5347
  refresh(whiteSpace, lineHeight, charWidth, textHeight, lineLength, knownHeights) {
5347
5348
  let lineWrapping = wrappingWhiteSpace.indexOf(whiteSpace) > -1;
5348
- let changed = Math.abs(lineHeight - this.lineHeight) > 0.3 || this.lineWrapping != lineWrapping ||
5349
- Math.abs(charWidth - this.charWidth) > 0.1;
5349
+ let changed = Math.abs(lineHeight - this.lineHeight) > 0.3 || this.lineWrapping != lineWrapping;
5350
5350
  this.lineWrapping = lineWrapping;
5351
5351
  this.lineHeight = lineHeight;
5352
5352
  this.charWidth = charWidth;
@@ -10638,11 +10638,13 @@ const showHoverTooltipHost = showTooltip.compute([showHoverTooltip], state => {
10638
10638
  arrow: tooltips.some(t => t.arrow),
10639
10639
  };
10640
10640
  });
10641
+ const hoverPlugin = state.Facet.define();
10641
10642
  class HoverPlugin {
10642
- constructor(view, source, field, setHover, hoverTime) {
10643
+ constructor(view, source, field, locked, setHover, hoverTime) {
10643
10644
  this.view = view;
10644
10645
  this.source = source;
10645
10646
  this.field = field;
10647
+ this.locked = locked;
10646
10648
  this.setHover = setHover;
10647
10649
  this.hoverTime = hoverTime;
10648
10650
  this.hoverTimeout = -1;
@@ -10653,7 +10655,7 @@ class HoverPlugin {
10653
10655
  view.dom.addEventListener("mouseleave", this.mouseleave = this.mouseleave.bind(this));
10654
10656
  view.dom.addEventListener("mousemove", this.mousemove = this.mousemove.bind(this));
10655
10657
  }
10656
- update() {
10658
+ update(update) {
10657
10659
  if (this.pending) {
10658
10660
  this.pending = null;
10659
10661
  clearTimeout(this.restartTimeout);
@@ -10697,19 +10699,29 @@ class HoverPlugin {
10697
10699
  let rtl = bidi && bidi.dir == exports.Direction.RTL ? -1 : 1;
10698
10700
  side = (lastMove.x < posCoords.left ? -rtl : rtl);
10699
10701
  }
10702
+ this.activateHover(view, pos, side);
10703
+ }
10704
+ activateHover(view, pos, side, locked) {
10700
10705
  let open = this.source(view, pos, side);
10701
- if (open === null || open === void 0 ? void 0 : open.then) {
10706
+ let done = (value) => {
10707
+ if (value && !(Array.isArray(value) && !value.length)) {
10708
+ let tooltips = Array.isArray(value) ? value : [value];
10709
+ if (locked)
10710
+ this.locked.set(tooltips, locked);
10711
+ view.dispatch({ effects: this.setHover.of(tooltips) });
10712
+ }
10713
+ };
10714
+ if (open && "then" in open) {
10702
10715
  let pending = this.pending = { pos };
10703
10716
  open.then(result => {
10704
10717
  if (this.pending == pending) {
10705
10718
  this.pending = null;
10706
- if (result && !(Array.isArray(result) && !result.length))
10707
- view.dispatch({ effects: this.setHover.of(Array.isArray(result) ? result : [result]) });
10719
+ done(result);
10708
10720
  }
10709
10721
  }, e => logException(view.state, e, "hover tooltip"));
10710
10722
  }
10711
- else if (open && !(Array.isArray(open) && !open.length)) {
10712
- view.dispatch({ effects: this.setHover.of(Array.isArray(open) ? open : [open]) });
10723
+ else {
10724
+ done(open);
10713
10725
  }
10714
10726
  }
10715
10727
  get tooltip() {
@@ -10723,7 +10735,7 @@ class HoverPlugin {
10723
10735
  if (this.hoverTimeout < 0)
10724
10736
  this.hoverTimeout = setTimeout(this.checkHover, this.hoverTime);
10725
10737
  let { active, tooltip } = this;
10726
- if (active.length && tooltip && !isInTooltip(tooltip.dom, event) || this.pending) {
10738
+ if (active.length && !this.locked.has(active) && tooltip && !isInTooltip(tooltip.dom, event) || this.pending) {
10727
10739
  let { pos } = active[0] || this.pending, end = (_b = (_a = active[0]) === null || _a === void 0 ? void 0 : _a.end) !== null && _b !== void 0 ? _b : pos;
10728
10740
  if ((pos == end ? this.view.posAtCoords(this.lastMove) != pos
10729
10741
  : !isOverRange(this.view, pos, end, event.clientX, event.clientY))) {
@@ -10736,7 +10748,7 @@ class HoverPlugin {
10736
10748
  clearTimeout(this.hoverTimeout);
10737
10749
  this.hoverTimeout = -1;
10738
10750
  let { active } = this;
10739
- if (active.length) {
10751
+ if (active.length && !this.locked.has(active)) {
10740
10752
  let { tooltip } = this;
10741
10753
  let inTooltip = tooltip && tooltip.dom.contains(event.relatedTarget);
10742
10754
  if (!inTooltip)
@@ -10748,7 +10760,8 @@ class HoverPlugin {
10748
10760
  watchTooltipLeave(tooltip) {
10749
10761
  let watch = (event) => {
10750
10762
  tooltip.removeEventListener("mouseleave", watch);
10751
- if (this.active.length && !this.view.dom.contains(event.relatedTarget))
10763
+ let { active } = this;
10764
+ if (active.length && !this.locked.has(active) && !this.view.dom.contains(event.relatedTarget))
10752
10765
  this.view.dispatch({ effects: this.setHover.of([]) });
10753
10766
  };
10754
10767
  tooltip.addEventListener("mouseleave", watch);
@@ -10799,49 +10812,85 @@ extension.
10799
10812
  */
10800
10813
  function hoverTooltip(source, options = {}) {
10801
10814
  let setHover = state.StateEffect.define();
10815
+ // This would be better stored in the state field, but we've set
10816
+ // down the type of the field in our interface, so it's indirectly
10817
+ // stored by array identity.
10818
+ let locked = new WeakMap();
10802
10819
  let hoverState = state.StateField.define({
10803
10820
  create() { return []; },
10804
10821
  update(value, tr) {
10822
+ let lock = locked.get(value);
10805
10823
  if (value.length) {
10806
10824
  if (options.hideOnChange && (tr.docChanged || tr.selection))
10807
10825
  value = [];
10826
+ else if (lock && lock(tr))
10827
+ value = [];
10808
10828
  else if (options.hideOn)
10809
10829
  value = value.filter(v => !options.hideOn(tr, v));
10810
- if (tr.docChanged) {
10811
- let mapped = [];
10812
- for (let tooltip of value) {
10813
- let newPos = tr.changes.mapPos(tooltip.pos, -1, state.MapMode.TrackDel);
10814
- if (newPos != null) {
10815
- let copy = Object.assign(Object.create(null), tooltip);
10816
- copy.pos = newPos;
10817
- if (copy.end != null)
10818
- copy.end = tr.changes.mapPos(copy.end);
10819
- mapped.push(copy);
10820
- }
10830
+ }
10831
+ if (tr.docChanged && value.length) {
10832
+ let mapped = [];
10833
+ for (let tooltip of value) {
10834
+ let newPos = tr.changes.mapPos(tooltip.pos, -1, state.MapMode.TrackDel);
10835
+ if (newPos != null) {
10836
+ let copy = Object.assign(Object.create(null), tooltip);
10837
+ copy.pos = newPos;
10838
+ if (copy.end != null)
10839
+ copy.end = tr.changes.mapPos(copy.end);
10840
+ mapped.push(copy);
10821
10841
  }
10822
- value = mapped;
10823
10842
  }
10843
+ value = mapped;
10824
10844
  }
10825
10845
  for (let effect of tr.effects) {
10826
- if (effect.is(setHover))
10846
+ if (effect.is(setHover)) {
10827
10847
  value = effect.value;
10828
- if (effect.is(closeHoverTooltipEffect))
10848
+ lock = undefined;
10849
+ }
10850
+ if (effect.is(closeHoverTooltipEffect) && !effect.value || effect.value == hoverState)
10829
10851
  value = [];
10830
10852
  }
10853
+ if (value.length && lock)
10854
+ locked.set(value, lock);
10831
10855
  return value;
10832
10856
  },
10833
10857
  provide: f => showHoverTooltip.from(f)
10834
10858
  });
10859
+ const plugin = ViewPlugin.define(view => new HoverPlugin(view, source, hoverState, locked, setHover, options.hoverTime || 300 /* Hover.Time */));
10835
10860
  return {
10836
10861
  active: hoverState,
10837
10862
  extension: [
10838
10863
  hoverState,
10839
- ViewPlugin.define(view => new HoverPlugin(view, source, hoverState, setHover, options.hoverTime || 300 /* Hover.Time */)),
10864
+ plugin,
10865
+ hoverPlugin.of(plugin),
10840
10866
  showHoverTooltipHost
10841
10867
  ]
10842
10868
  };
10843
10869
  }
10844
10870
  /**
10871
+ Activate hover tooltips for the given position and side. If you
10872
+ provide a specific hover tooltip (the value returned from
10873
+ [`hoverTooltip`](https://codemirror.net/6/docs/ref/#view.hoverTooltip)), only that one will be
10874
+ activated. If not given, all hover tooltips at the given position
10875
+ are triggered.
10876
+
10877
+ Note that tooltips opened this way don't close automatically, and
10878
+ you'll want to pass an `until` callback or use
10879
+ [`closeHoverTooltip`](https://codemirror.net/6/docs/ref/#view.closeHoverTooltip)/[`closeHoverTooltips`](https://codemirror.net/6/docs/ref/#view.closeHoverTooltips)
10880
+ to deactivate them.
10881
+ */
10882
+ function activateHover(view, pos, side, options = {}) {
10883
+ var _a;
10884
+ let plugins = view.state.facet(hoverPlugin).map(p => view.plugin(p)).filter((p) => !!p);
10885
+ if (options.tooltip && options.tooltip.active) {
10886
+ let found = plugins.find(p => p.field == options.tooltip.active);
10887
+ if (found)
10888
+ plugins = [found];
10889
+ }
10890
+ for (let plugin of plugins)
10891
+ plugin.activateHover(view, pos, side, (_a = options.until) !== null && _a !== void 0 ? _a : (() => false));
10892
+ }
10893
+ /**
10845
10894
  Get the active tooltip view for a given tooltip, if available.
10846
10895
  */
10847
10896
  function getTooltip(view, tooltip) {
@@ -10863,6 +10912,12 @@ Transaction effect that closes all hover tooltips.
10863
10912
  */
10864
10913
  const closeHoverTooltips = closeHoverTooltipEffect.of(null);
10865
10914
  /**
10915
+ Transaction effect that closes a specific hover tooltip.
10916
+ */
10917
+ function closeHoverTooltip(tooltip) {
10918
+ return closeHoverTooltipEffect.of(tooltip.active);
10919
+ }
10920
+ /**
10866
10921
  Tell the tooltip extension to recompute the position of the active
10867
10922
  tooltips. This can be useful when something happens (such as a
10868
10923
  re-positioning or CSS change affecting the editor) that could
@@ -11760,6 +11815,8 @@ exports.ViewPlugin = ViewPlugin;
11760
11815
  exports.ViewUpdate = ViewUpdate;
11761
11816
  exports.WidgetType = WidgetType;
11762
11817
  exports.__test = __test;
11818
+ exports.activateHover = activateHover;
11819
+ exports.closeHoverTooltip = closeHoverTooltip;
11763
11820
  exports.closeHoverTooltips = closeHoverTooltips;
11764
11821
  exports.crosshairCursor = crosshairCursor;
11765
11822
  exports.drawSelection = drawSelection;
package/dist/index.d.cts CHANGED
@@ -2090,6 +2090,22 @@ declare function hoverTooltip(source: HoverTooltipSource, options?: {
2090
2090
  active: StateField<readonly Tooltip[]>;
2091
2091
  };
2092
2092
  /**
2093
+ Activate hover tooltips for the given position and side. If you
2094
+ provide a specific hover tooltip (the value returned from
2095
+ [`hoverTooltip`](https://codemirror.net/6/docs/ref/#view.hoverTooltip)), only that one will be
2096
+ activated. If not given, all hover tooltips at the given position
2097
+ are triggered.
2098
+
2099
+ Note that tooltips opened this way don't close automatically, and
2100
+ you'll want to pass an `until` callback or use
2101
+ [`closeHoverTooltip`](https://codemirror.net/6/docs/ref/#view.closeHoverTooltip)/[`closeHoverTooltips`](https://codemirror.net/6/docs/ref/#view.closeHoverTooltips)
2102
+ to deactivate them.
2103
+ */
2104
+ declare function activateHover(view: EditorView, pos: number, side: -1 | 1, options?: {
2105
+ tooltip?: Extension;
2106
+ until?: (tr: Transaction) => boolean;
2107
+ }): void;
2108
+ /**
2093
2109
  Get the active tooltip view for a given tooltip, if available.
2094
2110
  */
2095
2111
  declare function getTooltip(view: EditorView, tooltip: Tooltip): TooltipView | null;
@@ -2100,7 +2116,13 @@ declare function hasHoverTooltips(state: EditorState): boolean;
2100
2116
  /**
2101
2117
  Transaction effect that closes all hover tooltips.
2102
2118
  */
2103
- declare const closeHoverTooltips: StateEffect<null>;
2119
+ declare const closeHoverTooltips: StateEffect<unknown>;
2120
+ /**
2121
+ Transaction effect that closes a specific hover tooltip.
2122
+ */
2123
+ declare function closeHoverTooltip(tooltip: Extension & {
2124
+ active: StateField<readonly Tooltip[]>;
2125
+ }): StateEffect<unknown>;
2104
2126
  /**
2105
2127
  Tell the tooltip extension to recompute the position of the active
2106
2128
  tooltips. This can be useful when something happens (such as a
@@ -2386,4 +2408,4 @@ trailing whitespace.
2386
2408
  */
2387
2409
  declare function highlightTrailingWhitespace(): Extension;
2388
2410
 
2389
- export { BidiSpan, BlockInfo, BlockType, BlockWrapper, type Command, type DOMEventHandlers, type DOMEventMap, Decoration, type DecorationSet, Direction, EditorView, type EditorViewConfig, GutterMarker, type HoverTooltipSource, type KeyBinding, type LayerMarker, MatchDecorator, type MouseSelectionStyle, type Panel, type PanelConstructor, type PluginSpec, type PluginValue, type Rect, RectangleMarker, type Tooltip, type TooltipView, ViewPlugin, ViewUpdate, WidgetType, closeHoverTooltips, crosshairCursor, drawSelection, dropCursor, getDialog, getDrawSelectionConfig, getPanel, getTooltip, gutter, gutterLineClass, gutterWidgetClass, gutters, hasHoverTooltips, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, highlightTrailingWhitespace, highlightWhitespace, hoverTooltip, keymap, layer, lineNumberMarkers, lineNumberWidgetMarker, lineNumbers, logException, panels, placeholder, rectangularSelection, repositionTooltips, runScopeHandlers, scrollPastEnd, showDialog, showPanel, showTooltip, tooltips };
2411
+ export { BidiSpan, BlockInfo, BlockType, BlockWrapper, type Command, type DOMEventHandlers, type DOMEventMap, Decoration, type DecorationSet, Direction, EditorView, type EditorViewConfig, GutterMarker, type HoverTooltipSource, type KeyBinding, type LayerMarker, MatchDecorator, type MouseSelectionStyle, type Panel, type PanelConstructor, type PluginSpec, type PluginValue, type Rect, RectangleMarker, type Tooltip, type TooltipView, ViewPlugin, ViewUpdate, WidgetType, activateHover, closeHoverTooltip, closeHoverTooltips, crosshairCursor, drawSelection, dropCursor, getDialog, getDrawSelectionConfig, getPanel, getTooltip, gutter, gutterLineClass, gutterWidgetClass, gutters, hasHoverTooltips, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, highlightTrailingWhitespace, highlightWhitespace, hoverTooltip, keymap, layer, lineNumberMarkers, lineNumberWidgetMarker, lineNumbers, logException, panels, placeholder, rectangularSelection, repositionTooltips, runScopeHandlers, scrollPastEnd, showDialog, showPanel, showTooltip, tooltips };
package/dist/index.d.ts CHANGED
@@ -2090,6 +2090,22 @@ declare function hoverTooltip(source: HoverTooltipSource, options?: {
2090
2090
  active: StateField<readonly Tooltip[]>;
2091
2091
  };
2092
2092
  /**
2093
+ Activate hover tooltips for the given position and side. If you
2094
+ provide a specific hover tooltip (the value returned from
2095
+ [`hoverTooltip`](https://codemirror.net/6/docs/ref/#view.hoverTooltip)), only that one will be
2096
+ activated. If not given, all hover tooltips at the given position
2097
+ are triggered.
2098
+
2099
+ Note that tooltips opened this way don't close automatically, and
2100
+ you'll want to pass an `until` callback or use
2101
+ [`closeHoverTooltip`](https://codemirror.net/6/docs/ref/#view.closeHoverTooltip)/[`closeHoverTooltips`](https://codemirror.net/6/docs/ref/#view.closeHoverTooltips)
2102
+ to deactivate them.
2103
+ */
2104
+ declare function activateHover(view: EditorView, pos: number, side: -1 | 1, options?: {
2105
+ tooltip?: Extension;
2106
+ until?: (tr: Transaction) => boolean;
2107
+ }): void;
2108
+ /**
2093
2109
  Get the active tooltip view for a given tooltip, if available.
2094
2110
  */
2095
2111
  declare function getTooltip(view: EditorView, tooltip: Tooltip): TooltipView | null;
@@ -2100,7 +2116,13 @@ declare function hasHoverTooltips(state: EditorState): boolean;
2100
2116
  /**
2101
2117
  Transaction effect that closes all hover tooltips.
2102
2118
  */
2103
- declare const closeHoverTooltips: StateEffect<null>;
2119
+ declare const closeHoverTooltips: StateEffect<unknown>;
2120
+ /**
2121
+ Transaction effect that closes a specific hover tooltip.
2122
+ */
2123
+ declare function closeHoverTooltip(tooltip: Extension & {
2124
+ active: StateField<readonly Tooltip[]>;
2125
+ }): StateEffect<unknown>;
2104
2126
  /**
2105
2127
  Tell the tooltip extension to recompute the position of the active
2106
2128
  tooltips. This can be useful when something happens (such as a
@@ -2386,4 +2408,4 @@ trailing whitespace.
2386
2408
  */
2387
2409
  declare function highlightTrailingWhitespace(): Extension;
2388
2410
 
2389
- export { BidiSpan, BlockInfo, BlockType, BlockWrapper, type Command, type DOMEventHandlers, type DOMEventMap, Decoration, type DecorationSet, Direction, EditorView, type EditorViewConfig, GutterMarker, type HoverTooltipSource, type KeyBinding, type LayerMarker, MatchDecorator, type MouseSelectionStyle, type Panel, type PanelConstructor, type PluginSpec, type PluginValue, type Rect, RectangleMarker, type Tooltip, type TooltipView, ViewPlugin, ViewUpdate, WidgetType, closeHoverTooltips, crosshairCursor, drawSelection, dropCursor, getDialog, getDrawSelectionConfig, getPanel, getTooltip, gutter, gutterLineClass, gutterWidgetClass, gutters, hasHoverTooltips, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, highlightTrailingWhitespace, highlightWhitespace, hoverTooltip, keymap, layer, lineNumberMarkers, lineNumberWidgetMarker, lineNumbers, logException, panels, placeholder, rectangularSelection, repositionTooltips, runScopeHandlers, scrollPastEnd, showDialog, showPanel, showTooltip, tooltips };
2411
+ export { BidiSpan, BlockInfo, BlockType, BlockWrapper, type Command, type DOMEventHandlers, type DOMEventMap, Decoration, type DecorationSet, Direction, EditorView, type EditorViewConfig, GutterMarker, type HoverTooltipSource, type KeyBinding, type LayerMarker, MatchDecorator, type MouseSelectionStyle, type Panel, type PanelConstructor, type PluginSpec, type PluginValue, type Rect, RectangleMarker, type Tooltip, type TooltipView, ViewPlugin, ViewUpdate, WidgetType, activateHover, closeHoverTooltip, closeHoverTooltips, crosshairCursor, drawSelection, dropCursor, getDialog, getDrawSelectionConfig, getPanel, getTooltip, gutter, gutterLineClass, gutterWidgetClass, gutters, hasHoverTooltips, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, highlightTrailingWhitespace, highlightWhitespace, hoverTooltip, keymap, layer, lineNumberMarkers, lineNumberWidgetMarker, lineNumbers, logException, panels, placeholder, rectangularSelection, repositionTooltips, runScopeHandlers, scrollPastEnd, showDialog, showPanel, showTooltip, tooltips };
package/dist/index.js CHANGED
@@ -2307,6 +2307,7 @@ class TileBuilder {
2307
2307
  this.cache.reused.set(oldTile, 2 /* Reused.DOM */);
2308
2308
  let text = new TextTile(composition.text, composition.text.nodeValue);
2309
2309
  text.flags |= 8 /* TileFlag.Composition */;
2310
+ this.pos = composition.range.toB;
2310
2311
  head.append(text);
2311
2312
  }
2312
2313
  addInlineWidget(widget, marks, openStart) {
@@ -3831,7 +3832,7 @@ class InlineCoordsScan {
3831
3832
  // (including the position after the last piece). For a text tile,
3832
3833
  // these will be character clusters, for a composite tile, these
3833
3834
  // will be child tiles.
3834
- scan(positions, getRects) {
3835
+ scan(positions, getRects, recursed = false) {
3835
3836
  let lo = 0, hi = positions.length - 1, seen = new Set();
3836
3837
  let bidi = this.bidiIn(positions[0], positions[hi]);
3837
3838
  let above, below;
@@ -3904,19 +3905,19 @@ class InlineCoordsScan {
3904
3905
  if (!closestRect) {
3905
3906
  let side = above && (!below || (this.y - above.bottom < below.top - this.y)) ? above : below;
3906
3907
  this.y = (side.top + side.bottom) / 2;
3907
- return this.scan(positions, getRects);
3908
+ return this.scan(positions, getRects, true);
3908
3909
  }
3909
3910
  // Handle the case where closest matched a higher element on the
3910
3911
  // same line as an element below/above the coords
3911
- if (closestDx) {
3912
+ if (closestDx && !recursed) {
3912
3913
  let { top, bottom } = closestRect;
3913
3914
  if (above && above.bottom > (top + top + bottom) / 3) {
3914
3915
  this.y = above.bottom - 1;
3915
- return this.scan(positions, getRects);
3916
+ return this.scan(positions, getRects, true);
3916
3917
  }
3917
3918
  if (below && below.top < (top + bottom + bottom) / 3) {
3918
3919
  this.y = below.top + 1;
3919
- return this.scan(positions, getRects);
3920
+ return this.scan(positions, getRects, true);
3920
3921
  }
3921
3922
  }
3922
3923
  let ltr = (bidi ? this.dirAt(positions[closestI], 1) : this.baseDir) == Direction.LTR;
@@ -5210,7 +5211,7 @@ observers.compositionend = view => {
5210
5211
  view.inputState.compositionFirstChange = null;
5211
5212
  if (browser.chrome && browser.android) {
5212
5213
  // Delay flushing for a bit on Android because it'll often fire a
5213
- // bunch of contradictory changes in a row at end of compositon
5214
+ // bunch of contradictory changes in a row at end of composition
5214
5215
  view.observer.flushSoon();
5215
5216
  }
5216
5217
  else if (view.inputState.compositionPendingChange) {
@@ -5341,8 +5342,7 @@ class HeightOracle {
5341
5342
  }
5342
5343
  refresh(whiteSpace, lineHeight, charWidth, textHeight, lineLength, knownHeights) {
5343
5344
  let lineWrapping = wrappingWhiteSpace.indexOf(whiteSpace) > -1;
5344
- let changed = Math.abs(lineHeight - this.lineHeight) > 0.3 || this.lineWrapping != lineWrapping ||
5345
- Math.abs(charWidth - this.charWidth) > 0.1;
5345
+ let changed = Math.abs(lineHeight - this.lineHeight) > 0.3 || this.lineWrapping != lineWrapping;
5346
5346
  this.lineWrapping = lineWrapping;
5347
5347
  this.lineHeight = lineHeight;
5348
5348
  this.charWidth = charWidth;
@@ -10633,11 +10633,13 @@ const showHoverTooltipHost = /*@__PURE__*/showTooltip.compute([showHoverTooltip]
10633
10633
  arrow: tooltips.some(t => t.arrow),
10634
10634
  };
10635
10635
  });
10636
+ const hoverPlugin = /*@__PURE__*/Facet.define();
10636
10637
  class HoverPlugin {
10637
- constructor(view, source, field, setHover, hoverTime) {
10638
+ constructor(view, source, field, locked, setHover, hoverTime) {
10638
10639
  this.view = view;
10639
10640
  this.source = source;
10640
10641
  this.field = field;
10642
+ this.locked = locked;
10641
10643
  this.setHover = setHover;
10642
10644
  this.hoverTime = hoverTime;
10643
10645
  this.hoverTimeout = -1;
@@ -10648,7 +10650,7 @@ class HoverPlugin {
10648
10650
  view.dom.addEventListener("mouseleave", this.mouseleave = this.mouseleave.bind(this));
10649
10651
  view.dom.addEventListener("mousemove", this.mousemove = this.mousemove.bind(this));
10650
10652
  }
10651
- update() {
10653
+ update(update) {
10652
10654
  if (this.pending) {
10653
10655
  this.pending = null;
10654
10656
  clearTimeout(this.restartTimeout);
@@ -10692,19 +10694,29 @@ class HoverPlugin {
10692
10694
  let rtl = bidi && bidi.dir == Direction.RTL ? -1 : 1;
10693
10695
  side = (lastMove.x < posCoords.left ? -rtl : rtl);
10694
10696
  }
10697
+ this.activateHover(view, pos, side);
10698
+ }
10699
+ activateHover(view, pos, side, locked) {
10695
10700
  let open = this.source(view, pos, side);
10696
- if (open === null || open === void 0 ? void 0 : open.then) {
10701
+ let done = (value) => {
10702
+ if (value && !(Array.isArray(value) && !value.length)) {
10703
+ let tooltips = Array.isArray(value) ? value : [value];
10704
+ if (locked)
10705
+ this.locked.set(tooltips, locked);
10706
+ view.dispatch({ effects: this.setHover.of(tooltips) });
10707
+ }
10708
+ };
10709
+ if (open && "then" in open) {
10697
10710
  let pending = this.pending = { pos };
10698
10711
  open.then(result => {
10699
10712
  if (this.pending == pending) {
10700
10713
  this.pending = null;
10701
- if (result && !(Array.isArray(result) && !result.length))
10702
- view.dispatch({ effects: this.setHover.of(Array.isArray(result) ? result : [result]) });
10714
+ done(result);
10703
10715
  }
10704
10716
  }, e => logException(view.state, e, "hover tooltip"));
10705
10717
  }
10706
- else if (open && !(Array.isArray(open) && !open.length)) {
10707
- view.dispatch({ effects: this.setHover.of(Array.isArray(open) ? open : [open]) });
10718
+ else {
10719
+ done(open);
10708
10720
  }
10709
10721
  }
10710
10722
  get tooltip() {
@@ -10718,7 +10730,7 @@ class HoverPlugin {
10718
10730
  if (this.hoverTimeout < 0)
10719
10731
  this.hoverTimeout = setTimeout(this.checkHover, this.hoverTime);
10720
10732
  let { active, tooltip } = this;
10721
- if (active.length && tooltip && !isInTooltip(tooltip.dom, event) || this.pending) {
10733
+ if (active.length && !this.locked.has(active) && tooltip && !isInTooltip(tooltip.dom, event) || this.pending) {
10722
10734
  let { pos } = active[0] || this.pending, end = (_b = (_a = active[0]) === null || _a === void 0 ? void 0 : _a.end) !== null && _b !== void 0 ? _b : pos;
10723
10735
  if ((pos == end ? this.view.posAtCoords(this.lastMove) != pos
10724
10736
  : !isOverRange(this.view, pos, end, event.clientX, event.clientY))) {
@@ -10731,7 +10743,7 @@ class HoverPlugin {
10731
10743
  clearTimeout(this.hoverTimeout);
10732
10744
  this.hoverTimeout = -1;
10733
10745
  let { active } = this;
10734
- if (active.length) {
10746
+ if (active.length && !this.locked.has(active)) {
10735
10747
  let { tooltip } = this;
10736
10748
  let inTooltip = tooltip && tooltip.dom.contains(event.relatedTarget);
10737
10749
  if (!inTooltip)
@@ -10743,7 +10755,8 @@ class HoverPlugin {
10743
10755
  watchTooltipLeave(tooltip) {
10744
10756
  let watch = (event) => {
10745
10757
  tooltip.removeEventListener("mouseleave", watch);
10746
- if (this.active.length && !this.view.dom.contains(event.relatedTarget))
10758
+ let { active } = this;
10759
+ if (active.length && !this.locked.has(active) && !this.view.dom.contains(event.relatedTarget))
10747
10760
  this.view.dispatch({ effects: this.setHover.of([]) });
10748
10761
  };
10749
10762
  tooltip.addEventListener("mouseleave", watch);
@@ -10794,49 +10807,85 @@ extension.
10794
10807
  */
10795
10808
  function hoverTooltip(source, options = {}) {
10796
10809
  let setHover = StateEffect.define();
10810
+ // This would be better stored in the state field, but we've set
10811
+ // down the type of the field in our interface, so it's indirectly
10812
+ // stored by array identity.
10813
+ let locked = new WeakMap();
10797
10814
  let hoverState = StateField.define({
10798
10815
  create() { return []; },
10799
10816
  update(value, tr) {
10817
+ let lock = locked.get(value);
10800
10818
  if (value.length) {
10801
10819
  if (options.hideOnChange && (tr.docChanged || tr.selection))
10802
10820
  value = [];
10821
+ else if (lock && lock(tr))
10822
+ value = [];
10803
10823
  else if (options.hideOn)
10804
10824
  value = value.filter(v => !options.hideOn(tr, v));
10805
- if (tr.docChanged) {
10806
- let mapped = [];
10807
- for (let tooltip of value) {
10808
- let newPos = tr.changes.mapPos(tooltip.pos, -1, MapMode.TrackDel);
10809
- if (newPos != null) {
10810
- let copy = Object.assign(Object.create(null), tooltip);
10811
- copy.pos = newPos;
10812
- if (copy.end != null)
10813
- copy.end = tr.changes.mapPos(copy.end);
10814
- mapped.push(copy);
10815
- }
10825
+ }
10826
+ if (tr.docChanged && value.length) {
10827
+ let mapped = [];
10828
+ for (let tooltip of value) {
10829
+ let newPos = tr.changes.mapPos(tooltip.pos, -1, MapMode.TrackDel);
10830
+ if (newPos != null) {
10831
+ let copy = Object.assign(Object.create(null), tooltip);
10832
+ copy.pos = newPos;
10833
+ if (copy.end != null)
10834
+ copy.end = tr.changes.mapPos(copy.end);
10835
+ mapped.push(copy);
10816
10836
  }
10817
- value = mapped;
10818
10837
  }
10838
+ value = mapped;
10819
10839
  }
10820
10840
  for (let effect of tr.effects) {
10821
- if (effect.is(setHover))
10841
+ if (effect.is(setHover)) {
10822
10842
  value = effect.value;
10823
- if (effect.is(closeHoverTooltipEffect))
10843
+ lock = undefined;
10844
+ }
10845
+ if (effect.is(closeHoverTooltipEffect) && !effect.value || effect.value == hoverState)
10824
10846
  value = [];
10825
10847
  }
10848
+ if (value.length && lock)
10849
+ locked.set(value, lock);
10826
10850
  return value;
10827
10851
  },
10828
10852
  provide: f => showHoverTooltip.from(f)
10829
10853
  });
10854
+ const plugin = ViewPlugin.define(view => new HoverPlugin(view, source, hoverState, locked, setHover, options.hoverTime || 300 /* Hover.Time */));
10830
10855
  return {
10831
10856
  active: hoverState,
10832
10857
  extension: [
10833
10858
  hoverState,
10834
- ViewPlugin.define(view => new HoverPlugin(view, source, hoverState, setHover, options.hoverTime || 300 /* Hover.Time */)),
10859
+ plugin,
10860
+ hoverPlugin.of(plugin),
10835
10861
  showHoverTooltipHost
10836
10862
  ]
10837
10863
  };
10838
10864
  }
10839
10865
  /**
10866
+ Activate hover tooltips for the given position and side. If you
10867
+ provide a specific hover tooltip (the value returned from
10868
+ [`hoverTooltip`](https://codemirror.net/6/docs/ref/#view.hoverTooltip)), only that one will be
10869
+ activated. If not given, all hover tooltips at the given position
10870
+ are triggered.
10871
+
10872
+ Note that tooltips opened this way don't close automatically, and
10873
+ you'll want to pass an `until` callback or use
10874
+ [`closeHoverTooltip`](https://codemirror.net/6/docs/ref/#view.closeHoverTooltip)/[`closeHoverTooltips`](https://codemirror.net/6/docs/ref/#view.closeHoverTooltips)
10875
+ to deactivate them.
10876
+ */
10877
+ function activateHover(view, pos, side, options = {}) {
10878
+ var _a;
10879
+ let plugins = view.state.facet(hoverPlugin).map(p => view.plugin(p)).filter((p) => !!p);
10880
+ if (options.tooltip && options.tooltip.active) {
10881
+ let found = plugins.find(p => p.field == options.tooltip.active);
10882
+ if (found)
10883
+ plugins = [found];
10884
+ }
10885
+ for (let plugin of plugins)
10886
+ plugin.activateHover(view, pos, side, (_a = options.until) !== null && _a !== void 0 ? _a : (() => false));
10887
+ }
10888
+ /**
10840
10889
  Get the active tooltip view for a given tooltip, if available.
10841
10890
  */
10842
10891
  function getTooltip(view, tooltip) {
@@ -10858,6 +10907,12 @@ Transaction effect that closes all hover tooltips.
10858
10907
  */
10859
10908
  const closeHoverTooltips = /*@__PURE__*/closeHoverTooltipEffect.of(null);
10860
10909
  /**
10910
+ Transaction effect that closes a specific hover tooltip.
10911
+ */
10912
+ function closeHoverTooltip(tooltip) {
10913
+ return closeHoverTooltipEffect.of(tooltip.active);
10914
+ }
10915
+ /**
10861
10916
  Tell the tooltip extension to recompute the position of the active
10862
10917
  tooltips. This can be useful when something happens (such as a
10863
10918
  re-positioning or CSS change affecting the editor) that could
@@ -11743,4 +11798,4 @@ function highlightTrailingWhitespace() {
11743
11798
  const __test = { HeightMap, HeightOracle, MeasuredHeights, QueryType, ChangedRange, computeOrder,
11744
11799
  moveVisually, clearHeightChangeFlag, getHeightChangeFlag: () => heightChangeFlag };
11745
11800
 
11746
- export { BidiSpan, BlockInfo, BlockType, BlockWrapper, Decoration, Direction, EditorView, GutterMarker, MatchDecorator, RectangleMarker, ViewPlugin, ViewUpdate, WidgetType, __test, closeHoverTooltips, crosshairCursor, drawSelection, dropCursor, getDialog, getDrawSelectionConfig, getPanel, getTooltip, gutter, gutterLineClass, gutterWidgetClass, gutters, hasHoverTooltips, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, highlightTrailingWhitespace, highlightWhitespace, hoverTooltip, keymap, layer, lineNumberMarkers, lineNumberWidgetMarker, lineNumbers, logException, panels, placeholder, rectangularSelection, repositionTooltips, runScopeHandlers, scrollPastEnd, showDialog, showPanel, showTooltip, tooltips };
11801
+ export { BidiSpan, BlockInfo, BlockType, BlockWrapper, Decoration, Direction, EditorView, GutterMarker, MatchDecorator, RectangleMarker, ViewPlugin, ViewUpdate, WidgetType, __test, activateHover, closeHoverTooltip, closeHoverTooltips, crosshairCursor, drawSelection, dropCursor, getDialog, getDrawSelectionConfig, getPanel, getTooltip, gutter, gutterLineClass, gutterWidgetClass, gutters, hasHoverTooltips, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, highlightTrailingWhitespace, highlightWhitespace, hoverTooltip, keymap, layer, lineNumberMarkers, lineNumberWidgetMarker, lineNumbers, logException, panels, placeholder, rectangularSelection, repositionTooltips, runScopeHandlers, scrollPastEnd, showDialog, showPanel, showTooltip, tooltips };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.41.1",
3
+ "version": "6.42.1",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",