@codemirror/view 6.39.7 → 6.39.9

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.39.9 (2026-01-06)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix a bug where `EditorSelection.cursor()` with a non-zero `assoc` value would not be visually respected at soft-wrap boundaries on initial view creation.
6
+
7
+ Fix error caused by hover tooltips running a scheduled timeout after their editor has been destroyed.
8
+
9
+ Fix a bug that caused `EditorView.outerDecorations` to not affect the content height map.
10
+
11
+ Fix an issue where composition near a widget could get unnecessarily interrupted.
12
+
13
+ ## 6.39.8 (2025-12-30)
14
+
15
+ ### Bug fixes
16
+
17
+ Fix a bug that cause `coordsAtPos` to use the dimensions of widget buffers when there were more meaningful elements to use nearby.
18
+
19
+ Fix a data structure corruption that could cause crashes during viewport changes.
20
+
1
21
  ## 6.39.7 (2025-12-24)
2
22
 
3
23
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -2149,7 +2149,7 @@ class WidgetBufferTile extends Tile {
2149
2149
  img.setAttribute("aria-hidden", "true");
2150
2150
  super(img, 0, flags);
2151
2151
  }
2152
- get isHidden() { return false; }
2152
+ get isHidden() { return true; }
2153
2153
  get overrideDOMText() { return state.Text.empty; }
2154
2154
  coordsIn(pos) { return this.dom.getBoundingClientRect(); }
2155
2155
  }
@@ -2502,6 +2502,8 @@ class TileCache {
2502
2502
  }
2503
2503
  // Put a tile in the cache.
2504
2504
  add(tile) {
2505
+ if (tile.demo)
2506
+ console.log("Add widget to cache");
2505
2507
  let i = tile.constructor.bucket, bucket = this.buckets[i];
2506
2508
  if (bucket.length < 6 /* C.Bucket */)
2507
2509
  bucket.push(tile);
@@ -2526,6 +2528,8 @@ class TileCache {
2526
2528
  }
2527
2529
  findWidget(widget, length, flags) {
2528
2530
  let widgets = this.buckets[0];
2531
+ if (widget.demo)
2532
+ console.log("looking for widget", widget, "in cache", widgets.slice());
2529
2533
  if (widgets.length)
2530
2534
  for (let i = 0, pass = 0;; i++) {
2531
2535
  if (i == widgets.length) {
@@ -2541,10 +2545,14 @@ class TileCache {
2541
2545
  widgets.splice(i, 1);
2542
2546
  if (i < this.index[0])
2543
2547
  this.index[0]--;
2544
- this.reused.set(tile, 1 /* Reused.Full */);
2545
- tile.length = length;
2546
- tile.flags = (tile.flags & ~(496 /* TileFlag.Widget */ | 1 /* TileFlag.BreakAfter */)) | flags;
2547
- return tile;
2548
+ if (tile.length == length && (tile.flags & (496 /* TileFlag.Widget */ | 1 /* TileFlag.BreakAfter */)) == flags) {
2549
+ this.reused.set(tile, 1 /* Reused.Full */);
2550
+ return tile;
2551
+ }
2552
+ else {
2553
+ this.reused.set(tile, 2 /* Reused.DOM */);
2554
+ return new WidgetTile(tile.dom, length, widget, (tile.flags & ~(496 /* TileFlag.Widget */ | 1 /* TileFlag.BreakAfter */)) | flags);
2555
+ }
2548
2556
  }
2549
2557
  }
2550
2558
  }
@@ -2558,6 +2566,10 @@ class TileCache {
2558
2566
  this.reused.set(tile, type);
2559
2567
  return tile.dom;
2560
2568
  }
2569
+ clear() {
2570
+ for (let i = 0; i < this.buckets.length; i++)
2571
+ this.buckets[i].length = this.index[i] = 0;
2572
+ }
2561
2573
  }
2562
2574
  // This class organizes a pass over the document, guided by the array
2563
2575
  // of replaced ranges. For ranges that haven't changed, it iterates
@@ -2600,17 +2612,20 @@ class TileUpdate {
2600
2612
  }
2601
2613
  if (!next)
2602
2614
  break;
2603
- this.forward(next.fromA, next.toA);
2604
2615
  // Compositions need to be handled specially, forcing the
2605
2616
  // focused text node and its parent nodes to remain stable at
2606
2617
  // that point in the document.
2607
2618
  if (composition && next.fromA <= composition.range.fromA && next.toA >= composition.range.toA) {
2619
+ this.forward(next.fromA, composition.range.fromA);
2608
2620
  this.emit(posB, composition.range.fromB);
2621
+ this.cache.clear(); // Must not reuse DOM across composition
2609
2622
  this.builder.addComposition(composition, compositionContext);
2610
2623
  this.text.skip(composition.range.toB - composition.range.fromB);
2624
+ this.forward(composition.range.fromA, next.toA);
2611
2625
  this.emit(composition.range.toB, next.toB);
2612
2626
  }
2613
2627
  else {
2628
+ this.forward(next.fromA, next.toA);
2614
2629
  this.emit(posB, next.toB);
2615
2630
  }
2616
2631
  posB = next.toB;
@@ -6023,7 +6038,7 @@ class ViewState {
6023
6038
  this.mustEnforceCursorAssoc = false;
6024
6039
  let guessWrapping = state$1.facet(contentAttributes).some(v => typeof v != "function" && v.class == "cm-lineWrapping");
6025
6040
  this.heightOracle = new HeightOracle(guessWrapping);
6026
- this.stateDeco = state$1.facet(decorations).filter(d => typeof d != "function");
6041
+ this.stateDeco = staticDeco(state$1);
6027
6042
  this.heightMap = HeightMap.empty().applyChanges(this.stateDeco, state.Text.empty, this.heightOracle.setDoc(state$1.doc), [new ChangedRange(0, 0, 0, state$1.doc.length)]);
6028
6043
  for (let i = 0; i < 2; i++) {
6029
6044
  this.viewport = this.getViewport(0, null);
@@ -6062,7 +6077,7 @@ class ViewState {
6062
6077
  update(update, scrollTarget = null) {
6063
6078
  this.state = update.state;
6064
6079
  let prevDeco = this.stateDeco;
6065
- this.stateDeco = this.state.facet(decorations).filter(d => typeof d != "function");
6080
+ this.stateDeco = staticDeco(this.state);
6066
6081
  let contentChanges = update.changedRanges;
6067
6082
  let heightChanges = ChangedRange.extendWithRanges(contentChanges, heightRelevantDecoChanges(prevDeco, this.stateDeco, update ? update.changes : state.ChangeSet.empty(this.state.doc.length)));
6068
6083
  let prevHeight = this.heightMap.height;
@@ -6093,7 +6108,7 @@ class ViewState {
6093
6108
  update.flags |= this.computeVisibleRanges(update.changes);
6094
6109
  if (scrollTarget)
6095
6110
  this.scrollTarget = scrollTarget;
6096
- if (!this.mustEnforceCursorAssoc && update.selectionSet && update.view.lineWrapping &&
6111
+ if (!this.mustEnforceCursorAssoc && (update.selectionSet || update.focusChanged) && update.view.lineWrapping &&
6097
6112
  update.state.selection.main.empty && update.state.selection.main.assoc &&
6098
6113
  !update.state.facet(nativeSelectionHidden))
6099
6114
  this.mustEnforceCursorAssoc = true;
@@ -6491,6 +6506,13 @@ const IdScaler = {
6491
6506
  scale: 1,
6492
6507
  eq(other) { return other == this; }
6493
6508
  };
6509
+ function staticDeco(state$1) {
6510
+ let deco = state$1.facet(decorations).filter(d => typeof d != "function");
6511
+ let outer = state$1.facet(outerDecorations).filter(d => typeof d != "function");
6512
+ if (outer.length)
6513
+ deco.push(state.RangeSet.join(outer));
6514
+ return deco;
6515
+ }
6494
6516
  // When the height is too big (> VP.MaxDOMHeight), scale down the
6495
6517
  // regions outside the viewports so that the total height is
6496
6518
  // VP.MaxDOMHeight.
@@ -10520,6 +10542,7 @@ class HoverPlugin {
10520
10542
  }
10521
10543
  destroy() {
10522
10544
  clearTimeout(this.hoverTimeout);
10545
+ clearTimeout(this.restartTimeout);
10523
10546
  this.view.dom.removeEventListener("mouseleave", this.mouseleave);
10524
10547
  this.view.dom.removeEventListener("mousemove", this.mousemove);
10525
10548
  }
package/dist/index.js CHANGED
@@ -2145,7 +2145,7 @@ class WidgetBufferTile extends Tile {
2145
2145
  img.setAttribute("aria-hidden", "true");
2146
2146
  super(img, 0, flags);
2147
2147
  }
2148
- get isHidden() { return false; }
2148
+ get isHidden() { return true; }
2149
2149
  get overrideDOMText() { return Text.empty; }
2150
2150
  coordsIn(pos) { return this.dom.getBoundingClientRect(); }
2151
2151
  }
@@ -2498,6 +2498,8 @@ class TileCache {
2498
2498
  }
2499
2499
  // Put a tile in the cache.
2500
2500
  add(tile) {
2501
+ if (tile.demo)
2502
+ console.log("Add widget to cache");
2501
2503
  let i = tile.constructor.bucket, bucket = this.buckets[i];
2502
2504
  if (bucket.length < 6 /* C.Bucket */)
2503
2505
  bucket.push(tile);
@@ -2522,6 +2524,8 @@ class TileCache {
2522
2524
  }
2523
2525
  findWidget(widget, length, flags) {
2524
2526
  let widgets = this.buckets[0];
2527
+ if (widget.demo)
2528
+ console.log("looking for widget", widget, "in cache", widgets.slice());
2525
2529
  if (widgets.length)
2526
2530
  for (let i = 0, pass = 0;; i++) {
2527
2531
  if (i == widgets.length) {
@@ -2537,10 +2541,14 @@ class TileCache {
2537
2541
  widgets.splice(i, 1);
2538
2542
  if (i < this.index[0])
2539
2543
  this.index[0]--;
2540
- this.reused.set(tile, 1 /* Reused.Full */);
2541
- tile.length = length;
2542
- tile.flags = (tile.flags & ~(496 /* TileFlag.Widget */ | 1 /* TileFlag.BreakAfter */)) | flags;
2543
- return tile;
2544
+ if (tile.length == length && (tile.flags & (496 /* TileFlag.Widget */ | 1 /* TileFlag.BreakAfter */)) == flags) {
2545
+ this.reused.set(tile, 1 /* Reused.Full */);
2546
+ return tile;
2547
+ }
2548
+ else {
2549
+ this.reused.set(tile, 2 /* Reused.DOM */);
2550
+ return new WidgetTile(tile.dom, length, widget, (tile.flags & ~(496 /* TileFlag.Widget */ | 1 /* TileFlag.BreakAfter */)) | flags);
2551
+ }
2544
2552
  }
2545
2553
  }
2546
2554
  }
@@ -2554,6 +2562,10 @@ class TileCache {
2554
2562
  this.reused.set(tile, type);
2555
2563
  return tile.dom;
2556
2564
  }
2565
+ clear() {
2566
+ for (let i = 0; i < this.buckets.length; i++)
2567
+ this.buckets[i].length = this.index[i] = 0;
2568
+ }
2557
2569
  }
2558
2570
  // This class organizes a pass over the document, guided by the array
2559
2571
  // of replaced ranges. For ranges that haven't changed, it iterates
@@ -2596,17 +2608,20 @@ class TileUpdate {
2596
2608
  }
2597
2609
  if (!next)
2598
2610
  break;
2599
- this.forward(next.fromA, next.toA);
2600
2611
  // Compositions need to be handled specially, forcing the
2601
2612
  // focused text node and its parent nodes to remain stable at
2602
2613
  // that point in the document.
2603
2614
  if (composition && next.fromA <= composition.range.fromA && next.toA >= composition.range.toA) {
2615
+ this.forward(next.fromA, composition.range.fromA);
2604
2616
  this.emit(posB, composition.range.fromB);
2617
+ this.cache.clear(); // Must not reuse DOM across composition
2605
2618
  this.builder.addComposition(composition, compositionContext);
2606
2619
  this.text.skip(composition.range.toB - composition.range.fromB);
2620
+ this.forward(composition.range.fromA, next.toA);
2607
2621
  this.emit(composition.range.toB, next.toB);
2608
2622
  }
2609
2623
  else {
2624
+ this.forward(next.fromA, next.toA);
2610
2625
  this.emit(posB, next.toB);
2611
2626
  }
2612
2627
  posB = next.toB;
@@ -6018,7 +6033,7 @@ class ViewState {
6018
6033
  this.mustEnforceCursorAssoc = false;
6019
6034
  let guessWrapping = state.facet(contentAttributes).some(v => typeof v != "function" && v.class == "cm-lineWrapping");
6020
6035
  this.heightOracle = new HeightOracle(guessWrapping);
6021
- this.stateDeco = state.facet(decorations).filter(d => typeof d != "function");
6036
+ this.stateDeco = staticDeco(state);
6022
6037
  this.heightMap = HeightMap.empty().applyChanges(this.stateDeco, Text.empty, this.heightOracle.setDoc(state.doc), [new ChangedRange(0, 0, 0, state.doc.length)]);
6023
6038
  for (let i = 0; i < 2; i++) {
6024
6039
  this.viewport = this.getViewport(0, null);
@@ -6057,7 +6072,7 @@ class ViewState {
6057
6072
  update(update, scrollTarget = null) {
6058
6073
  this.state = update.state;
6059
6074
  let prevDeco = this.stateDeco;
6060
- this.stateDeco = this.state.facet(decorations).filter(d => typeof d != "function");
6075
+ this.stateDeco = staticDeco(this.state);
6061
6076
  let contentChanges = update.changedRanges;
6062
6077
  let heightChanges = ChangedRange.extendWithRanges(contentChanges, heightRelevantDecoChanges(prevDeco, this.stateDeco, update ? update.changes : ChangeSet.empty(this.state.doc.length)));
6063
6078
  let prevHeight = this.heightMap.height;
@@ -6088,7 +6103,7 @@ class ViewState {
6088
6103
  update.flags |= this.computeVisibleRanges(update.changes);
6089
6104
  if (scrollTarget)
6090
6105
  this.scrollTarget = scrollTarget;
6091
- if (!this.mustEnforceCursorAssoc && update.selectionSet && update.view.lineWrapping &&
6106
+ if (!this.mustEnforceCursorAssoc && (update.selectionSet || update.focusChanged) && update.view.lineWrapping &&
6092
6107
  update.state.selection.main.empty && update.state.selection.main.assoc &&
6093
6108
  !update.state.facet(nativeSelectionHidden))
6094
6109
  this.mustEnforceCursorAssoc = true;
@@ -6486,6 +6501,13 @@ const IdScaler = {
6486
6501
  scale: 1,
6487
6502
  eq(other) { return other == this; }
6488
6503
  };
6504
+ function staticDeco(state) {
6505
+ let deco = state.facet(decorations).filter(d => typeof d != "function");
6506
+ let outer = state.facet(outerDecorations).filter(d => typeof d != "function");
6507
+ if (outer.length)
6508
+ deco.push(RangeSet.join(outer));
6509
+ return deco;
6510
+ }
6489
6511
  // When the height is too big (> VP.MaxDOMHeight), scale down the
6490
6512
  // regions outside the viewports so that the total height is
6491
6513
  // VP.MaxDOMHeight.
@@ -10515,6 +10537,7 @@ class HoverPlugin {
10515
10537
  }
10516
10538
  destroy() {
10517
10539
  clearTimeout(this.hoverTimeout);
10540
+ clearTimeout(this.restartTimeout);
10518
10541
  this.view.dom.removeEventListener("mouseleave", this.mouseleave);
10519
10542
  this.view.dom.removeEventListener("mousemove", this.mousemove);
10520
10543
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.39.7",
3
+ "version": "6.39.9",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",
@@ -36,6 +36,6 @@
36
36
  },
37
37
  "repository": {
38
38
  "type": "git",
39
- "url": "https://github.com/codemirror/view.git"
39
+ "url": "git+https://github.com/codemirror/view.git"
40
40
  }
41
41
  }