@codemirror/view 6.39.17 → 6.41.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,29 @@
1
+ ## 6.41.0 (2026-04-01)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix an issue where `EditorView.posAtCoords` could incorrectly return a position near a higher element on the line, in mixed-font-size lines.
6
+
7
+ Expand the workaround for the Webkit bug that causes nonexistent selections to stay visible to be active on non-Safari Webkit browsers.
8
+
9
+ ### New features
10
+
11
+ The new `EditorView.cursorScrollMargin` facet can now be used to configure the extra space used when scrolling the cursor into view.
12
+
13
+ ## 6.40.0 (2026-03-12)
14
+
15
+ ### Bug fixes
16
+
17
+ Fix a bug that caused Shift-Enter/Backspace/Delete on iOS to lose the shift modifier when delivered to key event handlers.
18
+
19
+ Fix an issue where `EditorView.moveVertically` could move to the wrong place in wrapped lines with a large line height.
20
+
21
+ Make sure the selection head associativity is properly set for mouse selections made with shift held down.
22
+
23
+ ### New features
24
+
25
+ `WidgetType.updateDOM` is now called with the previous widget value as third argument.
26
+
1
27
  ## 6.39.17 (2026-03-10)
2
28
 
3
29
  ### Bug fixes
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @codemirror/view [![NPM version](https://img.shields.io/npm/v/@codemirror/view.svg)](https://www.npmjs.org/package/@codemirror/view)
2
2
 
3
- [ [**WEBSITE**](https://codemirror.net/) | [**DOCS**](https://codemirror.net/docs/ref/#view) | [**ISSUES**](https://github.com/codemirror/dev/issues) | [**FORUM**](https://discuss.codemirror.net/c/next/) | [**CHANGELOG**](https://github.com/codemirror/view/blob/main/CHANGELOG.md) ]
3
+ [ [**WEBSITE**](https://codemirror.net/) | [**DOCS**](https://codemirror.net/docs/ref/#view) | [**ISSUES**](https://github.com/codemirror/dev/issues) | [**FORUM**](https://discuss.codemirror.net/) | [**CHANGELOG**](https://github.com/codemirror/view/blob/main/CHANGELOG.md) ]
4
4
 
5
5
  This package implements the DOM view component for the
6
6
  [CodeMirror](https://codemirror.net/) code editor.
package/dist/index.cjs CHANGED
@@ -134,7 +134,7 @@ class WidgetType {
134
134
  couldn't (in which case the widget will be redrawn). The default
135
135
  implementation just returns false.
136
136
  */
137
- updateDOM(dom, view) { return false; }
137
+ updateDOM(dom, view, from) { return false; }
138
138
  /**
139
139
  @internal
140
140
  */
@@ -548,12 +548,12 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
548
548
  }
549
549
  let moveX = 0, moveY = 0;
550
550
  if (y == "nearest") {
551
- if (rect.top < bounding.top) {
551
+ if (rect.top < bounding.top + yMargin) {
552
552
  moveY = rect.top - (bounding.top + yMargin);
553
553
  if (side > 0 && rect.bottom > bounding.bottom + moveY)
554
554
  moveY = rect.bottom - bounding.bottom + yMargin;
555
555
  }
556
- else if (rect.bottom > bounding.bottom) {
556
+ else if (rect.bottom > bounding.bottom - yMargin) {
557
557
  moveY = rect.bottom - bounding.bottom + yMargin;
558
558
  if (side < 0 && (rect.top - moveY) < bounding.top)
559
559
  moveY = rect.top - (bounding.top + yMargin);
@@ -567,12 +567,12 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
567
567
  moveY = targetTop - bounding.top;
568
568
  }
569
569
  if (x == "nearest") {
570
- if (rect.left < bounding.left) {
570
+ if (rect.left < bounding.left + xMargin) {
571
571
  moveX = rect.left - (bounding.left + xMargin);
572
572
  if (side > 0 && rect.right > bounding.right + moveX)
573
573
  moveX = rect.right - bounding.right + xMargin;
574
574
  }
575
- else if (rect.right > bounding.right) {
575
+ else if (rect.right > bounding.right - xMargin) {
576
576
  moveX = rect.right - bounding.right + xMargin;
577
577
  if (side < 0 && rect.left < bounding.left + moveX)
578
578
  moveX = rect.left - (bounding.left + xMargin);
@@ -1313,7 +1313,7 @@ const nativeSelectionHidden = state.Facet.define({
1313
1313
  });
1314
1314
  const scrollHandler = state.Facet.define();
1315
1315
  class ScrollTarget {
1316
- constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5,
1316
+ constructor(range, y, x, yMargin, xMargin,
1317
1317
  // This data structure is abused to also store precise scroll
1318
1318
  // snapshots, instead of a `scrollIntoView` request. When this
1319
1319
  // flag is `true`, `range` points at a position in the reference
@@ -2539,7 +2539,7 @@ class TileCache {
2539
2539
  let tile = widgets[i];
2540
2540
  if (!this.reused.has(tile) &&
2541
2541
  (pass == 0 ? tile.widget.compare(widget)
2542
- : tile.widget.constructor == widget.constructor && widget.updateDOM(tile.dom, this.view))) {
2542
+ : tile.widget.constructor == widget.constructor && widget.updateDOM(tile.dom, this.view, tile.widget))) {
2543
2543
  widgets.splice(i, 1);
2544
2544
  if (i < this.index[0])
2545
2545
  this.index[0]--;
@@ -3395,6 +3395,7 @@ class DocView {
3395
3395
  this.blockWrappers = this.view.state.facet(blockWrappers).map(v => typeof v == "function" ? v(this.view) : v);
3396
3396
  }
3397
3397
  scrollIntoView(target) {
3398
+ var _a;
3398
3399
  if (target.isSnapshot) {
3399
3400
  let ref = this.view.viewState.lineBlockAt(target.range.head);
3400
3401
  this.view.scrollDOM.scrollTop = ref.top - target.yMargin;
@@ -3411,7 +3412,7 @@ class DocView {
3411
3412
  }
3412
3413
  }
3413
3414
  let { range } = target;
3414
- let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
3415
+ let rect = this.coordsAt(range.head, (_a = range.assoc) !== null && _a !== void 0 ? _a : (range.empty ? 0 : range.head > range.anchor ? -1 : 1)), other;
3415
3416
  if (!rect)
3416
3417
  return;
3417
3418
  if (!range.empty && (other = this.coordsAt(range.anchor, range.anchor > range.head ? -1 : 1)))
@@ -3678,7 +3679,8 @@ function moveVertically(view, start, forward, distance) {
3678
3679
  return state.EditorSelection.cursor(startPos, start.assoc);
3679
3680
  let goal = start.goalColumn, startY;
3680
3681
  let rect = view.contentDOM.getBoundingClientRect();
3681
- let startCoords = view.coordsAtPos(startPos, (start.empty ? start.assoc : 0) || (forward ? 1 : -1)), docTop = view.documentTop;
3682
+ let startCoords = view.coordsAtPos(startPos, start.assoc || ((start.empty ? forward : start.head == start.from) ? 1 : -1));
3683
+ let docTop = view.documentTop;
3682
3684
  if (startCoords) {
3683
3685
  if (goal == null)
3684
3686
  goal = startCoords.left - rect.left;
@@ -3691,9 +3693,16 @@ function moveVertically(view, start, forward, distance) {
3691
3693
  startY = (dir < 0 ? line.top : line.bottom) + docTop;
3692
3694
  }
3693
3695
  let resolvedGoal = rect.left + goal;
3694
- let dist = distance !== null && distance !== void 0 ? distance : (view.viewState.heightOracle.textHeight >> 1);
3695
- let pos = posAtCoords(view, { x: resolvedGoal, y: startY + dist * dir }, false, dir);
3696
- return state.EditorSelection.cursor(pos.pos, pos.assoc, undefined, goal);
3696
+ let halfText = view.viewState.heightOracle.textHeight >> 1, dist = distance !== null && distance !== void 0 ? distance : halfText;
3697
+ for (let scan = 0;; scan += halfText) {
3698
+ let y = startY + (dist + scan) * dir;
3699
+ let pos = posAtCoords(view, { x: resolvedGoal, y }, false, dir);
3700
+ if (forward ? y > rect.bottom : y < rect.top)
3701
+ return state.EditorSelection.cursor(pos.pos, pos.assoc);
3702
+ let posCoords = view.coordsAtPos(pos.pos, pos.assoc), mid = posCoords ? (posCoords.top + posCoords.bottom) / 2 : 0;
3703
+ if (!posCoords || (forward ? mid > startY : mid < startY))
3704
+ return state.EditorSelection.cursor(pos.pos, pos.assoc, undefined, goal);
3705
+ }
3697
3706
  }
3698
3707
  function skipAtomicRanges(atoms, pos, bias) {
3699
3708
  for (;;) {
@@ -3901,6 +3910,18 @@ class InlineCoordsScan {
3901
3910
  this.y = (side.top + side.bottom) / 2;
3902
3911
  return this.scan(positions, getRects);
3903
3912
  }
3913
+ // Handle the case where closest matched a higher element on the
3914
+ // same line as an element below/above the coords
3915
+ if (closestDx) {
3916
+ if (above && above.bottom > closestRect.top) {
3917
+ this.y = above.bottom - 1;
3918
+ return this.scan(positions, getRects);
3919
+ }
3920
+ if (below && below.top < closestRect.bottom) {
3921
+ this.y = below.top + 1;
3922
+ return this.scan(positions, getRects);
3923
+ }
3924
+ }
3904
3925
  let ltr = (bidi ? this.dirAt(positions[closestI], 1) : this.baseDir) == exports.Direction.LTR;
3905
3926
  return {
3906
3927
  i: closestI,
@@ -4552,9 +4573,9 @@ class InputState {
4552
4573
  // applyDOMChange, notify key handlers of it and reset to
4553
4574
  // the state they produce.
4554
4575
  let pending;
4555
- if (browser.ios && !event.synthetic && !event.altKey && !event.metaKey &&
4576
+ if (browser.ios && !event.synthetic && !event.altKey && !event.metaKey && !event.shiftKey &&
4556
4577
  ((pending = PendingKeys.find(key => key.keyCode == event.keyCode)) && !event.ctrlKey ||
4557
- EmacsyPendingKeys.indexOf(event.key) > -1 && event.ctrlKey && !event.shiftKey)) {
4578
+ EmacsyPendingKeys.indexOf(event.key) > -1 && event.ctrlKey)) {
4558
4579
  this.pendingIOSKey = pending || event;
4559
4580
  setTimeout(() => this.flushIOSKey(), 250);
4560
4581
  return true;
@@ -4958,10 +4979,10 @@ function basicMouseSelection(view, event) {
4958
4979
  if (start.pos != cur.pos && !extend) {
4959
4980
  let startRange = rangeForClick(view, start.pos, start.assoc, type);
4960
4981
  let from = Math.min(startRange.from, range.from), to = Math.max(startRange.to, range.to);
4961
- range = from < range.from ? state.EditorSelection.range(from, to) : state.EditorSelection.range(to, from);
4982
+ range = from < range.from ? state.EditorSelection.range(from, to, range.assoc) : state.EditorSelection.range(to, from, range.assoc);
4962
4983
  }
4963
4984
  if (extend)
4964
- return startSel.replaceRange(startSel.main.extend(range.from, range.to));
4985
+ return startSel.replaceRange(startSel.main.extend(range.from, range.to, range.assoc));
4965
4986
  else if (multiple && type == 1 && startSel.ranges.length > 1 && (removed = removeRangeAround(startSel, cur.pos)))
4966
4987
  return removed;
4967
4988
  else if (multiple)
@@ -7918,7 +7939,8 @@ class EditorView {
7918
7939
  scrollTarget = scrollTarget.map(tr.changes);
7919
7940
  if (tr.scrollIntoView) {
7920
7941
  let { main } = tr.state.selection;
7921
- scrollTarget = new ScrollTarget(main.empty ? main : state.EditorSelection.cursor(main.head, main.head > main.anchor ? -1 : 1));
7942
+ let { x, y } = this.state.facet(EditorView.cursorScrollMargin);
7943
+ scrollTarget = new ScrollTarget(main.empty ? main : state.EditorSelection.cursor(main.head, main.head > main.anchor ? -1 : 1), "nearest", "nearest", y, x);
7922
7944
  }
7923
7945
  for (let e of tr.effects)
7924
7946
  if (e.is(scrollIntoView))
@@ -8575,7 +8597,8 @@ class EditorView {
8575
8597
  cause it to scroll the given position or range into view.
8576
8598
  */
8577
8599
  static scrollIntoView(pos, options = {}) {
8578
- return scrollIntoView.of(new ScrollTarget(typeof pos == "number" ? state.EditorSelection.cursor(pos) : pos, options.y, options.x, options.yMargin, options.xMargin));
8600
+ var _a, _b, _c, _d;
8601
+ return scrollIntoView.of(new ScrollTarget(typeof pos == "number" ? state.EditorSelection.cursor(pos) : pos, (_a = options.y) !== null && _a !== void 0 ? _a : "nearest", (_b = options.x) !== null && _b !== void 0 ? _b : "nearest", (_c = options.yMargin) !== null && _c !== void 0 ? _c : 5, (_d = options.xMargin) !== null && _d !== void 0 ? _d : 5));
8579
8602
  }
8580
8603
  /**
8581
8604
  Return an effect that resets the editor to its current (at the
@@ -8834,11 +8857,30 @@ supported.)
8834
8857
  */
8835
8858
  EditorView.bidiIsolatedRanges = bidiIsolatedRanges;
8836
8859
  /**
8860
+ Can be used to specify the distance that scrolling cursor into
8861
+ view keeps it away from the sides of the editor, either as a
8862
+ single pixel number or two different values for the different
8863
+ axes. Defaults to 5 pixels on both axes.
8864
+ */
8865
+ EditorView.cursorScrollMargin = state.Facet.define({
8866
+ combine: inputs => {
8867
+ let x = 5, y = 5;
8868
+ for (let i of inputs) {
8869
+ if (typeof i == "number")
8870
+ x = y = i;
8871
+ else
8872
+ ({ x, y } = i);
8873
+ }
8874
+ return { x, y };
8875
+ }
8876
+ });
8877
+ /**
8837
8878
  Facet that allows extensions to provide additional scroll
8838
8879
  margins (space around the sides of the scrolling element that
8839
8880
  should be considered invisible). This can be useful when the
8840
8881
  plugin introduces elements that cover part of that element (for
8841
- example a horizontally fixed gutter).
8882
+ example a horizontally fixed gutter). Not to be confused with
8883
+ [`cursorScrollMargin`](https://codemirror.net/6/docs/ref/#view.EditorView^cursorScrollMargin).
8842
8884
  */
8843
8885
  EditorView.scrollMargins = scrollMargins;
8844
8886
  /**
@@ -9374,7 +9416,7 @@ class LayerView {
9374
9416
  old = next;
9375
9417
  }
9376
9418
  this.drawn = markers;
9377
- if (browser.safari && browser.safari_version >= 26) // Issue #1600, 1627
9419
+ if (browser.webkit) // Issue #1600, 1627, 1686
9378
9420
  this.dom.style.display = this.dom.firstChild ? "" : "none";
9379
9421
  }
9380
9422
  }
@@ -9454,7 +9496,7 @@ const cursorLayer = layer({
9454
9496
  let prim = r == state$1.selection.main;
9455
9497
  if (r.empty || conf.drawRangeCursor && !(prim && browser.ios && conf.iosSelectionHandles)) {
9456
9498
  let className = prim ? "cm-cursor cm-cursor-primary" : "cm-cursor cm-cursor-secondary";
9457
- let cursor = r.empty ? r : state.EditorSelection.cursor(r.head, r.head > r.anchor ? -1 : 1);
9499
+ let cursor = r.empty ? r : state.EditorSelection.cursor(r.head, r.assoc);
9458
9500
  for (let piece of RectangleMarker.forRange(view, className, cursor))
9459
9501
  cursors.push(piece);
9460
9502
  }
package/dist/index.d.cts CHANGED
@@ -224,7 +224,7 @@ declare abstract class WidgetType {
224
224
  couldn't (in which case the widget will be redrawn). The default
225
225
  implementation just returns false.
226
226
  */
227
- updateDOM(dom: HTMLElement, view: EditorView): boolean;
227
+ updateDOM(dom: HTMLElement, view: EditorView, from: this): boolean;
228
228
  /**
229
229
  The estimated height this widget will have, to be used when
230
230
  estimating the height of content that hasn't been drawn. May
@@ -404,7 +404,7 @@ declare class ScrollTarget {
404
404
  readonly yMargin: number;
405
405
  readonly xMargin: number;
406
406
  readonly isSnapshot: boolean;
407
- constructor(range: SelectionRange, y?: ScrollStrategy, x?: ScrollStrategy, yMargin?: number, xMargin?: number, isSnapshot?: boolean);
407
+ constructor(range: SelectionRange, y: ScrollStrategy, x: ScrollStrategy, yMargin: number, xMargin: number, isSnapshot?: boolean);
408
408
  map(changes: ChangeDesc): ScrollTarget;
409
409
  clip(state: EditorState): ScrollTarget;
410
410
  }
@@ -1356,11 +1356,25 @@ declare class EditorView {
1356
1356
  */
1357
1357
  static bidiIsolatedRanges: Facet<DecorationSet | ((view: EditorView) => DecorationSet), readonly (DecorationSet | ((view: EditorView) => DecorationSet))[]>;
1358
1358
  /**
1359
+ Can be used to specify the distance that scrolling cursor into
1360
+ view keeps it away from the sides of the editor, either as a
1361
+ single pixel number or two different values for the different
1362
+ axes. Defaults to 5 pixels on both axes.
1363
+ */
1364
+ static cursorScrollMargin: Facet<number | {
1365
+ x: number;
1366
+ y: number;
1367
+ }, {
1368
+ x: number;
1369
+ y: number;
1370
+ }>;
1371
+ /**
1359
1372
  Facet that allows extensions to provide additional scroll
1360
1373
  margins (space around the sides of the scrolling element that
1361
1374
  should be considered invisible). This can be useful when the
1362
1375
  plugin introduces elements that cover part of that element (for
1363
- example a horizontally fixed gutter).
1376
+ example a horizontally fixed gutter). Not to be confused with
1377
+ [`cursorScrollMargin`](https://codemirror.net/6/docs/ref/#view.EditorView^cursorScrollMargin).
1364
1378
  */
1365
1379
  static scrollMargins: Facet<(view: EditorView) => Partial<Rect> | null, readonly ((view: EditorView) => Partial<Rect> | null)[]>;
1366
1380
  /**
package/dist/index.d.ts CHANGED
@@ -224,7 +224,7 @@ declare abstract class WidgetType {
224
224
  couldn't (in which case the widget will be redrawn). The default
225
225
  implementation just returns false.
226
226
  */
227
- updateDOM(dom: HTMLElement, view: EditorView): boolean;
227
+ updateDOM(dom: HTMLElement, view: EditorView, from: this): boolean;
228
228
  /**
229
229
  The estimated height this widget will have, to be used when
230
230
  estimating the height of content that hasn't been drawn. May
@@ -404,7 +404,7 @@ declare class ScrollTarget {
404
404
  readonly yMargin: number;
405
405
  readonly xMargin: number;
406
406
  readonly isSnapshot: boolean;
407
- constructor(range: SelectionRange, y?: ScrollStrategy, x?: ScrollStrategy, yMargin?: number, xMargin?: number, isSnapshot?: boolean);
407
+ constructor(range: SelectionRange, y: ScrollStrategy, x: ScrollStrategy, yMargin: number, xMargin: number, isSnapshot?: boolean);
408
408
  map(changes: ChangeDesc): ScrollTarget;
409
409
  clip(state: EditorState): ScrollTarget;
410
410
  }
@@ -1356,11 +1356,25 @@ declare class EditorView {
1356
1356
  */
1357
1357
  static bidiIsolatedRanges: Facet<DecorationSet | ((view: EditorView) => DecorationSet), readonly (DecorationSet | ((view: EditorView) => DecorationSet))[]>;
1358
1358
  /**
1359
+ Can be used to specify the distance that scrolling cursor into
1360
+ view keeps it away from the sides of the editor, either as a
1361
+ single pixel number or two different values for the different
1362
+ axes. Defaults to 5 pixels on both axes.
1363
+ */
1364
+ static cursorScrollMargin: Facet<number | {
1365
+ x: number;
1366
+ y: number;
1367
+ }, {
1368
+ x: number;
1369
+ y: number;
1370
+ }>;
1371
+ /**
1359
1372
  Facet that allows extensions to provide additional scroll
1360
1373
  margins (space around the sides of the scrolling element that
1361
1374
  should be considered invisible). This can be useful when the
1362
1375
  plugin introduces elements that cover part of that element (for
1363
- example a horizontally fixed gutter).
1376
+ example a horizontally fixed gutter). Not to be confused with
1377
+ [`cursorScrollMargin`](https://codemirror.net/6/docs/ref/#view.EditorView^cursorScrollMargin).
1364
1378
  */
1365
1379
  static scrollMargins: Facet<(view: EditorView) => Partial<Rect> | null, readonly ((view: EditorView) => Partial<Rect> | null)[]>;
1366
1380
  /**
package/dist/index.js CHANGED
@@ -132,7 +132,7 @@ class WidgetType {
132
132
  couldn't (in which case the widget will be redrawn). The default
133
133
  implementation just returns false.
134
134
  */
135
- updateDOM(dom, view) { return false; }
135
+ updateDOM(dom, view, from) { return false; }
136
136
  /**
137
137
  @internal
138
138
  */
@@ -545,12 +545,12 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
545
545
  }
546
546
  let moveX = 0, moveY = 0;
547
547
  if (y == "nearest") {
548
- if (rect.top < bounding.top) {
548
+ if (rect.top < bounding.top + yMargin) {
549
549
  moveY = rect.top - (bounding.top + yMargin);
550
550
  if (side > 0 && rect.bottom > bounding.bottom + moveY)
551
551
  moveY = rect.bottom - bounding.bottom + yMargin;
552
552
  }
553
- else if (rect.bottom > bounding.bottom) {
553
+ else if (rect.bottom > bounding.bottom - yMargin) {
554
554
  moveY = rect.bottom - bounding.bottom + yMargin;
555
555
  if (side < 0 && (rect.top - moveY) < bounding.top)
556
556
  moveY = rect.top - (bounding.top + yMargin);
@@ -564,12 +564,12 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
564
564
  moveY = targetTop - bounding.top;
565
565
  }
566
566
  if (x == "nearest") {
567
- if (rect.left < bounding.left) {
567
+ if (rect.left < bounding.left + xMargin) {
568
568
  moveX = rect.left - (bounding.left + xMargin);
569
569
  if (side > 0 && rect.right > bounding.right + moveX)
570
570
  moveX = rect.right - bounding.right + xMargin;
571
571
  }
572
- else if (rect.right > bounding.right) {
572
+ else if (rect.right > bounding.right - xMargin) {
573
573
  moveX = rect.right - bounding.right + xMargin;
574
574
  if (side < 0 && rect.left < bounding.left + moveX)
575
575
  moveX = rect.left - (bounding.left + xMargin);
@@ -1309,7 +1309,7 @@ const nativeSelectionHidden = /*@__PURE__*/Facet.define({
1309
1309
  });
1310
1310
  const scrollHandler = /*@__PURE__*/Facet.define();
1311
1311
  class ScrollTarget {
1312
- constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5,
1312
+ constructor(range, y, x, yMargin, xMargin,
1313
1313
  // This data structure is abused to also store precise scroll
1314
1314
  // snapshots, instead of a `scrollIntoView` request. When this
1315
1315
  // flag is `true`, `range` points at a position in the reference
@@ -2535,7 +2535,7 @@ class TileCache {
2535
2535
  let tile = widgets[i];
2536
2536
  if (!this.reused.has(tile) &&
2537
2537
  (pass == 0 ? tile.widget.compare(widget)
2538
- : tile.widget.constructor == widget.constructor && widget.updateDOM(tile.dom, this.view))) {
2538
+ : tile.widget.constructor == widget.constructor && widget.updateDOM(tile.dom, this.view, tile.widget))) {
2539
2539
  widgets.splice(i, 1);
2540
2540
  if (i < this.index[0])
2541
2541
  this.index[0]--;
@@ -3391,6 +3391,7 @@ class DocView {
3391
3391
  this.blockWrappers = this.view.state.facet(blockWrappers).map(v => typeof v == "function" ? v(this.view) : v);
3392
3392
  }
3393
3393
  scrollIntoView(target) {
3394
+ var _a;
3394
3395
  if (target.isSnapshot) {
3395
3396
  let ref = this.view.viewState.lineBlockAt(target.range.head);
3396
3397
  this.view.scrollDOM.scrollTop = ref.top - target.yMargin;
@@ -3407,7 +3408,7 @@ class DocView {
3407
3408
  }
3408
3409
  }
3409
3410
  let { range } = target;
3410
- let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
3411
+ let rect = this.coordsAt(range.head, (_a = range.assoc) !== null && _a !== void 0 ? _a : (range.empty ? 0 : range.head > range.anchor ? -1 : 1)), other;
3411
3412
  if (!rect)
3412
3413
  return;
3413
3414
  if (!range.empty && (other = this.coordsAt(range.anchor, range.anchor > range.head ? -1 : 1)))
@@ -3674,7 +3675,8 @@ function moveVertically(view, start, forward, distance) {
3674
3675
  return EditorSelection.cursor(startPos, start.assoc);
3675
3676
  let goal = start.goalColumn, startY;
3676
3677
  let rect = view.contentDOM.getBoundingClientRect();
3677
- let startCoords = view.coordsAtPos(startPos, (start.empty ? start.assoc : 0) || (forward ? 1 : -1)), docTop = view.documentTop;
3678
+ let startCoords = view.coordsAtPos(startPos, start.assoc || ((start.empty ? forward : start.head == start.from) ? 1 : -1));
3679
+ let docTop = view.documentTop;
3678
3680
  if (startCoords) {
3679
3681
  if (goal == null)
3680
3682
  goal = startCoords.left - rect.left;
@@ -3687,9 +3689,16 @@ function moveVertically(view, start, forward, distance) {
3687
3689
  startY = (dir < 0 ? line.top : line.bottom) + docTop;
3688
3690
  }
3689
3691
  let resolvedGoal = rect.left + goal;
3690
- let dist = distance !== null && distance !== void 0 ? distance : (view.viewState.heightOracle.textHeight >> 1);
3691
- let pos = posAtCoords(view, { x: resolvedGoal, y: startY + dist * dir }, false, dir);
3692
- return EditorSelection.cursor(pos.pos, pos.assoc, undefined, goal);
3692
+ let halfText = view.viewState.heightOracle.textHeight >> 1, dist = distance !== null && distance !== void 0 ? distance : halfText;
3693
+ for (let scan = 0;; scan += halfText) {
3694
+ let y = startY + (dist + scan) * dir;
3695
+ let pos = posAtCoords(view, { x: resolvedGoal, y }, false, dir);
3696
+ if (forward ? y > rect.bottom : y < rect.top)
3697
+ return EditorSelection.cursor(pos.pos, pos.assoc);
3698
+ let posCoords = view.coordsAtPos(pos.pos, pos.assoc), mid = posCoords ? (posCoords.top + posCoords.bottom) / 2 : 0;
3699
+ if (!posCoords || (forward ? mid > startY : mid < startY))
3700
+ return EditorSelection.cursor(pos.pos, pos.assoc, undefined, goal);
3701
+ }
3693
3702
  }
3694
3703
  function skipAtomicRanges(atoms, pos, bias) {
3695
3704
  for (;;) {
@@ -3897,6 +3906,18 @@ class InlineCoordsScan {
3897
3906
  this.y = (side.top + side.bottom) / 2;
3898
3907
  return this.scan(positions, getRects);
3899
3908
  }
3909
+ // Handle the case where closest matched a higher element on the
3910
+ // same line as an element below/above the coords
3911
+ if (closestDx) {
3912
+ if (above && above.bottom > closestRect.top) {
3913
+ this.y = above.bottom - 1;
3914
+ return this.scan(positions, getRects);
3915
+ }
3916
+ if (below && below.top < closestRect.bottom) {
3917
+ this.y = below.top + 1;
3918
+ return this.scan(positions, getRects);
3919
+ }
3920
+ }
3900
3921
  let ltr = (bidi ? this.dirAt(positions[closestI], 1) : this.baseDir) == Direction.LTR;
3901
3922
  return {
3902
3923
  i: closestI,
@@ -4548,9 +4569,9 @@ class InputState {
4548
4569
  // applyDOMChange, notify key handlers of it and reset to
4549
4570
  // the state they produce.
4550
4571
  let pending;
4551
- if (browser.ios && !event.synthetic && !event.altKey && !event.metaKey &&
4572
+ if (browser.ios && !event.synthetic && !event.altKey && !event.metaKey && !event.shiftKey &&
4552
4573
  ((pending = PendingKeys.find(key => key.keyCode == event.keyCode)) && !event.ctrlKey ||
4553
- EmacsyPendingKeys.indexOf(event.key) > -1 && event.ctrlKey && !event.shiftKey)) {
4574
+ EmacsyPendingKeys.indexOf(event.key) > -1 && event.ctrlKey)) {
4554
4575
  this.pendingIOSKey = pending || event;
4555
4576
  setTimeout(() => this.flushIOSKey(), 250);
4556
4577
  return true;
@@ -4954,10 +4975,10 @@ function basicMouseSelection(view, event) {
4954
4975
  if (start.pos != cur.pos && !extend) {
4955
4976
  let startRange = rangeForClick(view, start.pos, start.assoc, type);
4956
4977
  let from = Math.min(startRange.from, range.from), to = Math.max(startRange.to, range.to);
4957
- range = from < range.from ? EditorSelection.range(from, to) : EditorSelection.range(to, from);
4978
+ range = from < range.from ? EditorSelection.range(from, to, range.assoc) : EditorSelection.range(to, from, range.assoc);
4958
4979
  }
4959
4980
  if (extend)
4960
- return startSel.replaceRange(startSel.main.extend(range.from, range.to));
4981
+ return startSel.replaceRange(startSel.main.extend(range.from, range.to, range.assoc));
4961
4982
  else if (multiple && type == 1 && startSel.ranges.length > 1 && (removed = removeRangeAround(startSel, cur.pos)))
4962
4983
  return removed;
4963
4984
  else if (multiple)
@@ -7913,7 +7934,8 @@ class EditorView {
7913
7934
  scrollTarget = scrollTarget.map(tr.changes);
7914
7935
  if (tr.scrollIntoView) {
7915
7936
  let { main } = tr.state.selection;
7916
- scrollTarget = new ScrollTarget(main.empty ? main : EditorSelection.cursor(main.head, main.head > main.anchor ? -1 : 1));
7937
+ let { x, y } = this.state.facet(EditorView.cursorScrollMargin);
7938
+ scrollTarget = new ScrollTarget(main.empty ? main : EditorSelection.cursor(main.head, main.head > main.anchor ? -1 : 1), "nearest", "nearest", y, x);
7917
7939
  }
7918
7940
  for (let e of tr.effects)
7919
7941
  if (e.is(scrollIntoView))
@@ -8570,7 +8592,8 @@ class EditorView {
8570
8592
  cause it to scroll the given position or range into view.
8571
8593
  */
8572
8594
  static scrollIntoView(pos, options = {}) {
8573
- return scrollIntoView.of(new ScrollTarget(typeof pos == "number" ? EditorSelection.cursor(pos) : pos, options.y, options.x, options.yMargin, options.xMargin));
8595
+ var _a, _b, _c, _d;
8596
+ return scrollIntoView.of(new ScrollTarget(typeof pos == "number" ? EditorSelection.cursor(pos) : pos, (_a = options.y) !== null && _a !== void 0 ? _a : "nearest", (_b = options.x) !== null && _b !== void 0 ? _b : "nearest", (_c = options.yMargin) !== null && _c !== void 0 ? _c : 5, (_d = options.xMargin) !== null && _d !== void 0 ? _d : 5));
8574
8597
  }
8575
8598
  /**
8576
8599
  Return an effect that resets the editor to its current (at the
@@ -8829,11 +8852,30 @@ supported.)
8829
8852
  */
8830
8853
  EditorView.bidiIsolatedRanges = bidiIsolatedRanges;
8831
8854
  /**
8855
+ Can be used to specify the distance that scrolling cursor into
8856
+ view keeps it away from the sides of the editor, either as a
8857
+ single pixel number or two different values for the different
8858
+ axes. Defaults to 5 pixels on both axes.
8859
+ */
8860
+ EditorView.cursorScrollMargin = /*@__PURE__*/Facet.define({
8861
+ combine: inputs => {
8862
+ let x = 5, y = 5;
8863
+ for (let i of inputs) {
8864
+ if (typeof i == "number")
8865
+ x = y = i;
8866
+ else
8867
+ ({ x, y } = i);
8868
+ }
8869
+ return { x, y };
8870
+ }
8871
+ });
8872
+ /**
8832
8873
  Facet that allows extensions to provide additional scroll
8833
8874
  margins (space around the sides of the scrolling element that
8834
8875
  should be considered invisible). This can be useful when the
8835
8876
  plugin introduces elements that cover part of that element (for
8836
- example a horizontally fixed gutter).
8877
+ example a horizontally fixed gutter). Not to be confused with
8878
+ [`cursorScrollMargin`](https://codemirror.net/6/docs/ref/#view.EditorView^cursorScrollMargin).
8837
8879
  */
8838
8880
  EditorView.scrollMargins = scrollMargins;
8839
8881
  /**
@@ -9369,7 +9411,7 @@ class LayerView {
9369
9411
  old = next;
9370
9412
  }
9371
9413
  this.drawn = markers;
9372
- if (browser.safari && browser.safari_version >= 26) // Issue #1600, 1627
9414
+ if (browser.webkit) // Issue #1600, 1627, 1686
9373
9415
  this.dom.style.display = this.dom.firstChild ? "" : "none";
9374
9416
  }
9375
9417
  }
@@ -9449,7 +9491,7 @@ const cursorLayer = /*@__PURE__*/layer({
9449
9491
  let prim = r == state.selection.main;
9450
9492
  if (r.empty || conf.drawRangeCursor && !(prim && browser.ios && conf.iosSelectionHandles)) {
9451
9493
  let className = prim ? "cm-cursor cm-cursor-primary" : "cm-cursor cm-cursor-secondary";
9452
- let cursor = r.empty ? r : EditorSelection.cursor(r.head, r.head > r.anchor ? -1 : 1);
9494
+ let cursor = r.empty ? r : EditorSelection.cursor(r.head, r.assoc);
9453
9495
  for (let piece of RectangleMarker.forRange(view, className, cursor))
9454
9496
  cursors.push(piece);
9455
9497
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.39.17",
3
+ "version": "6.41.0",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",
@@ -26,7 +26,7 @@
26
26
  "sideEffects": false,
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
- "@codemirror/state": "^6.5.0",
29
+ "@codemirror/state": "^6.6.0",
30
30
  "crelt": "^1.0.6",
31
31
  "style-mod": "^4.1.0",
32
32
  "w3c-keyname": "^2.2.4"