@codemirror/view 6.11.1 → 6.11.3

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,25 @@
1
+ ## 6.11.3 (2023-05-17)
2
+
3
+ ### Bug fixes
4
+
5
+ Make sure pointer selection respects `EditorView.atomicRanges`.
6
+
7
+ Preserve DOM widgets when their decoration type changes but they otherwise stay in the same place.
8
+
9
+ Fix a bug in `drawSelection` that could lead to invisible or incorrect selections for a blank line below a block widget.
10
+
11
+ ## 6.11.2 (2023-05-13)
12
+
13
+ ### Bug fixes
14
+
15
+ Fix a bug where the `crosshairCursor` extension could, when non-native key events were fired, trigger disruptive and needless view updates.
16
+
17
+ Fix an Android issue where backspacing at the front of a line with widget decorations could replace those decorations with their text content.
18
+
19
+ Respect scroll margins when scrolling the target of drag-selection into view.
20
+
21
+ Validate selection offsets reported by the browser, to work around Safari giving us invalid values in some cases.
22
+
1
23
  ## 6.11.1 (2023-05-09)
2
24
 
3
25
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -222,7 +222,9 @@ class DOMSelectionState {
222
222
  this.focusNode == domSel.focusNode && this.focusOffset == domSel.focusOffset;
223
223
  }
224
224
  setRange(range) {
225
- this.set(range.anchorNode, range.anchorOffset, range.focusNode, range.focusOffset);
225
+ let { anchorNode, focusNode } = range;
226
+ // Clip offsets to node size to avoid crashes when Safari reports bogus offsets (#1152)
227
+ this.set(anchorNode, Math.min(range.anchorOffset, anchorNode ? maxOffset(anchorNode) : 0), focusNode, Math.min(range.focusOffset, focusNode ? maxOffset(focusNode) : 0));
226
228
  }
227
229
  set(anchorNode, anchorOffset, focusNode, focusOffset) {
228
230
  this.anchorNode = anchorNode;
@@ -295,6 +297,8 @@ function atElementStart(doc, selection) {
295
297
  let node = selection.focusNode, offset = selection.focusOffset;
296
298
  if (!node || selection.anchorNode != node || selection.anchorOffset != offset)
297
299
  return false;
300
+ // Safari can report bogus offsets (#1152)
301
+ offset = Math.min(offset, maxOffset(node));
298
302
  for (;;) {
299
303
  if (offset) {
300
304
  if (node.nodeType != 1)
@@ -1643,7 +1647,7 @@ class BlockWidgetView extends ContentView {
1643
1647
  }
1644
1648
  domBoundsAround() { return null; }
1645
1649
  become(other) {
1646
- if (other instanceof BlockWidgetView && other.type == this.type &&
1650
+ if (other instanceof BlockWidgetView &&
1647
1651
  other.widget.constructor == this.widget.constructor) {
1648
1652
  if (!other.widget.compare(this.widget))
1649
1653
  this.markDirty(true);
@@ -1651,6 +1655,7 @@ class BlockWidgetView extends ContentView {
1651
1655
  this.prevWidget = this.widget;
1652
1656
  this.widget = other.widget;
1653
1657
  this.length = other.length;
1658
+ this.type = other.type;
1654
1659
  this.breakAfter = other.breakAfter;
1655
1660
  return true;
1656
1661
  }
@@ -2007,6 +2012,23 @@ const contentAttributes = state.Facet.define();
2007
2012
  const decorations = state.Facet.define();
2008
2013
  const atomicRanges = state.Facet.define();
2009
2014
  const scrollMargins = state.Facet.define();
2015
+ function getScrollMargins(view) {
2016
+ let left = 0, right = 0, top = 0, bottom = 0;
2017
+ for (let source of view.state.facet(scrollMargins)) {
2018
+ let m = source(view);
2019
+ if (m) {
2020
+ if (m.left != null)
2021
+ left = Math.max(left, m.left);
2022
+ if (m.right != null)
2023
+ right = Math.max(right, m.right);
2024
+ if (m.top != null)
2025
+ top = Math.max(top, m.top);
2026
+ if (m.bottom != null)
2027
+ bottom = Math.max(bottom, m.bottom);
2028
+ }
2029
+ }
2030
+ return { left, right, top, bottom };
2031
+ }
2010
2032
  const styleModule = state.Facet.define();
2011
2033
  class ChangedRange {
2012
2034
  constructor(fromA, toA, fromB, toB) {
@@ -2460,6 +2482,7 @@ class DOMReader {
2460
2482
  constructor(points, state$1) {
2461
2483
  this.points = points;
2462
2484
  this.text = "";
2485
+ console.log("make reader");
2463
2486
  this.lineSeparator = state$1.facet(state.EditorState.lineSeparator);
2464
2487
  }
2465
2488
  append(text) {
@@ -2474,6 +2497,7 @@ class DOMReader {
2474
2497
  let parent = start.parentNode;
2475
2498
  for (let cur = start;;) {
2476
2499
  this.findPointBefore(parent, cur);
2500
+ let oldLen = this.text.length;
2477
2501
  this.readNode(cur);
2478
2502
  let next = cur.nextSibling;
2479
2503
  if (next == end)
@@ -2481,7 +2505,7 @@ class DOMReader {
2481
2505
  let view = ContentView.get(cur), nextView = ContentView.get(next);
2482
2506
  if (view && nextView ? view.breakAfter :
2483
2507
  (view ? view.breakAfter : isBlockElement(cur)) ||
2484
- (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore)))
2508
+ (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen))
2485
2509
  this.lineBreak();
2486
2510
  cur = next;
2487
2511
  }
@@ -2938,22 +2962,10 @@ class DocView extends ContentView {
2938
2962
  if (!range.empty && (other = this.coordsAt(range.anchor, range.anchor > range.head ? -1 : 1)))
2939
2963
  rect = { left: Math.min(rect.left, other.left), top: Math.min(rect.top, other.top),
2940
2964
  right: Math.max(rect.right, other.right), bottom: Math.max(rect.bottom, other.bottom) };
2941
- let mLeft = 0, mRight = 0, mTop = 0, mBottom = 0;
2942
- for (let margins of this.view.state.facet(scrollMargins).map(f => f(this.view)))
2943
- if (margins) {
2944
- let { left, right, top, bottom } = margins;
2945
- if (left != null)
2946
- mLeft = Math.max(mLeft, left);
2947
- if (right != null)
2948
- mRight = Math.max(mRight, right);
2949
- if (top != null)
2950
- mTop = Math.max(mTop, top);
2951
- if (bottom != null)
2952
- mBottom = Math.max(mBottom, bottom);
2953
- }
2965
+ let margins = getScrollMargins(this.view);
2954
2966
  let targetRect = {
2955
- left: rect.left - mLeft, top: rect.top - mTop,
2956
- right: rect.right + mRight, bottom: rect.bottom + mBottom
2967
+ left: rect.left - margins.left, top: rect.top - margins.top,
2968
+ right: rect.right + margins.right, bottom: rect.bottom + margins.bottom
2957
2969
  };
2958
2970
  scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, target.xMargin, target.yMargin, this.view.textDirection == exports.Direction.LTR);
2959
2971
  }
@@ -3442,15 +3454,15 @@ function moveVertically(view, start, forward, distance) {
3442
3454
  return state.EditorSelection.cursor(pos, start.assoc, undefined, goal);
3443
3455
  }
3444
3456
  }
3445
- function skipAtoms(view, oldPos, pos) {
3446
- let atoms = view.state.facet(atomicRanges).map(f => f(view));
3457
+ function skipAtomicRanges(atoms, pos, bias) {
3447
3458
  for (;;) {
3448
- let moved = false;
3459
+ let moved = 0;
3449
3460
  for (let set of atoms) {
3450
- set.between(pos.from - 1, pos.from + 1, (from, to, value) => {
3451
- if (pos.from > from && pos.from < to) {
3452
- pos = oldPos.head > pos.from ? state.EditorSelection.cursor(from, 1) : state.EditorSelection.cursor(to, -1);
3453
- moved = true;
3461
+ set.between(pos - 1, pos + 1, (from, to, value) => {
3462
+ if (pos > from && pos < to) {
3463
+ let side = moved || bias || (pos - from < to - pos ? -1 : 1);
3464
+ pos = side < 0 ? from : to;
3465
+ moved = side;
3454
3466
  }
3455
3467
  });
3456
3468
  }
@@ -3458,6 +3470,10 @@ function skipAtoms(view, oldPos, pos) {
3458
3470
  return pos;
3459
3471
  }
3460
3472
  }
3473
+ function skipAtoms(view, oldPos, pos) {
3474
+ let newPos = skipAtomicRanges(view.state.facet(atomicRanges).map(f => f(view)), pos.from, oldPos.head > pos.from ? -1 : 1);
3475
+ return newPos == pos.from ? pos : state.EditorSelection.cursor(newPos, newPos < pos.from ? 1 : -1);
3476
+ }
3461
3477
 
3462
3478
  // This will also be where dragging info and such goes
3463
3479
  class InputState {
@@ -3707,6 +3723,7 @@ class MouseSelection {
3707
3723
  this.scrolling = -1;
3708
3724
  this.lastEvent = startEvent;
3709
3725
  this.scrollParent = scrollableParent(view.contentDOM);
3726
+ this.atoms = view.state.facet(atomicRanges).map(f => f(view));
3710
3727
  let doc = view.contentDOM.ownerDocument;
3711
3728
  doc.addEventListener("mousemove", this.move = this.move.bind(this));
3712
3729
  doc.addEventListener("mouseup", this.up = this.up.bind(this));
@@ -3733,13 +3750,14 @@ class MouseSelection {
3733
3750
  let sx = 0, sy = 0;
3734
3751
  let rect = ((_a = this.scrollParent) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect())
3735
3752
  || { left: 0, top: 0, right: this.view.win.innerWidth, bottom: this.view.win.innerHeight };
3736
- if (event.clientX <= rect.left + dragScrollMargin)
3753
+ let margins = getScrollMargins(this.view);
3754
+ if (event.clientX - margins.left <= rect.left + dragScrollMargin)
3737
3755
  sx = -dragScrollSpeed(rect.left - event.clientX);
3738
- else if (event.clientX >= rect.right - dragScrollMargin)
3756
+ else if (event.clientX + margins.right >= rect.right - dragScrollMargin)
3739
3757
  sx = dragScrollSpeed(event.clientX - rect.right);
3740
- if (event.clientY <= rect.top + dragScrollMargin)
3758
+ if (event.clientY - margins.top <= rect.top + dragScrollMargin)
3741
3759
  sy = -dragScrollSpeed(rect.top - event.clientY);
3742
- else if (event.clientY >= rect.bottom - dragScrollMargin)
3760
+ else if (event.clientY + margins.bottom >= rect.bottom - dragScrollMargin)
3743
3761
  sy = dragScrollSpeed(event.clientY - rect.bottom);
3744
3762
  this.setScrollSpeed(sx, sy);
3745
3763
  }
@@ -3779,10 +3797,33 @@ class MouseSelection {
3779
3797
  if (this.dragging === false)
3780
3798
  this.select(this.lastEvent);
3781
3799
  }
3800
+ skipAtoms(sel) {
3801
+ let ranges = null;
3802
+ for (let i = 0; i < sel.ranges.length; i++) {
3803
+ let range = sel.ranges[i], updated = null;
3804
+ if (range.empty) {
3805
+ let pos = skipAtomicRanges(this.atoms, range.from, 0);
3806
+ if (pos != range.from)
3807
+ updated = state.EditorSelection.cursor(pos, -1);
3808
+ }
3809
+ else {
3810
+ let from = skipAtomicRanges(this.atoms, range.from, -1);
3811
+ let to = skipAtomicRanges(this.atoms, range.to, 1);
3812
+ if (from != range.from || to != range.to)
3813
+ updated = state.EditorSelection.range(range.from == range.anchor ? from : to, range.from == range.head ? from : to);
3814
+ }
3815
+ if (updated) {
3816
+ if (!ranges)
3817
+ ranges = sel.ranges.slice();
3818
+ ranges[i] = updated;
3819
+ }
3820
+ }
3821
+ return ranges ? state.EditorSelection.create(ranges, sel.mainIndex) : sel;
3822
+ }
3782
3823
  select(event) {
3783
- let selection = this.style.get(event, this.extend, this.multiple);
3784
- if (this.mustSelect || !selection.eq(this.view.state.selection) ||
3785
- selection.main.assoc != this.view.state.selection.main.assoc)
3824
+ let { view } = this, selection = this.skipAtoms(this.style.get(event, this.extend, this.multiple));
3825
+ if (this.mustSelect || !selection.eq(view.state.selection) ||
3826
+ selection.main.assoc != view.state.selection.main.assoc)
3786
3827
  this.view.dispatch({
3787
3828
  selection,
3788
3829
  userEvent: "select.pointer"
@@ -5781,13 +5822,13 @@ class DOMChange {
5781
5822
  function applyDOMChange(view, domChange) {
5782
5823
  let change;
5783
5824
  let { newSel } = domChange, sel = view.state.selection.main;
5825
+ let lastKey = view.inputState.lastKeyTime > Date.now() - 100 ? view.inputState.lastKeyCode : -1;
5784
5826
  if (domChange.bounds) {
5785
5827
  let { from, to } = domChange.bounds;
5786
5828
  let preferredPos = sel.from, preferredSide = null;
5787
5829
  // Prefer anchoring to end when Backspace is pressed (or, on
5788
5830
  // Android, when something was deleted)
5789
- if (view.inputState.lastKeyCode === 8 && view.inputState.lastKeyTime > Date.now() - 100 ||
5790
- browser.android && domChange.text.length < to - from) {
5831
+ if (lastKey === 8 || browser.android && domChange.text.length < to - from) {
5791
5832
  preferredPos = sel.to;
5792
5833
  preferredSide = "end";
5793
5834
  }
@@ -5795,7 +5836,7 @@ function applyDOMChange(view, domChange) {
5795
5836
  if (diff) {
5796
5837
  // Chrome inserts two newlines when pressing shift-enter at the
5797
5838
  // end of a line. DomChange drops one of those.
5798
- if (browser.chrome && view.inputState.lastKeyCode == 13 &&
5839
+ if (browser.chrome && lastKey == 13 &&
5799
5840
  diff.toB == diff.from + 2 && domChange.text.slice(diff.from, diff.toB) == LineBreakPlaceholder + LineBreakPlaceholder)
5800
5841
  diff.toB--;
5801
5842
  change = { from: from + diff.from, to: from + diff.toA,
@@ -5853,7 +5894,8 @@ function applyDOMChange(view, domChange) {
5853
5894
  ((change.from == sel.from && change.to == sel.to &&
5854
5895
  change.insert.length == 1 && change.insert.lines == 2 &&
5855
5896
  dispatchKey(view.contentDOM, "Enter", 13)) ||
5856
- (change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 &&
5897
+ ((change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 ||
5898
+ lastKey == 8 && change.insert.length < change.to - change.from) &&
5857
5899
  dispatchKey(view.contentDOM, "Backspace", 8)) ||
5858
5900
  (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
5859
5901
  dispatchKey(view.contentDOM, "Delete", 46))))
@@ -6251,7 +6293,10 @@ class DOMObserver {
6251
6293
  let key = this.delayedAndroidKey;
6252
6294
  if (key) {
6253
6295
  this.clearDelayedAndroidKey();
6254
- if (!this.flush() && key.force)
6296
+ this.view.inputState.lastKeyCode = key.keyCode;
6297
+ this.view.inputState.lastKeyTime = Date.now();
6298
+ let flushed = this.flush();
6299
+ if (!flushed && key.force)
6255
6300
  dispatchKey(this.dom, key.key, key.keyCode);
6256
6301
  }
6257
6302
  };
@@ -7704,7 +7749,7 @@ function rectanglesForRange(view, className, range) {
7704
7749
  let top = visualStart ? drawForLine(range.from, null, visualStart) : drawForWidget(startBlock, false);
7705
7750
  let bottom = visualEnd ? drawForLine(null, range.to, visualEnd) : drawForWidget(endBlock, true);
7706
7751
  let between = [];
7707
- if ((visualStart || startBlock).to < (visualEnd || endBlock).from - 1)
7752
+ if ((visualStart || startBlock).to < (visualEnd || endBlock).from - (visualStart && visualEnd ? 1 : 0))
7708
7753
  between.push(piece(leftSide, top.bottom, rightSide, bottom.top));
7709
7754
  else if (top.bottom < bottom.top && view.elementAtHeight((top.bottom + bottom.top) / 2).type == exports.BlockType.Text)
7710
7755
  top.bottom = bottom.top = (top.bottom + bottom.top) / 2;
@@ -8480,10 +8525,10 @@ function rectangularSelection(options) {
8480
8525
  return EditorView.mouseSelectionStyle.of((view, event) => filter(event) ? rectangleSelectionStyle(view, event) : null);
8481
8526
  }
8482
8527
  const keys = {
8483
- Alt: [18, e => e.altKey],
8484
- Control: [17, e => e.ctrlKey],
8485
- Shift: [16, e => e.shiftKey],
8486
- Meta: [91, e => e.metaKey]
8528
+ Alt: [18, e => !!e.altKey],
8529
+ Control: [17, e => !!e.ctrlKey],
8530
+ Shift: [16, e => !!e.shiftKey],
8531
+ Meta: [91, e => !!e.metaKey]
8487
8532
  };
8488
8533
  const showCrosshair = { style: "cursor: crosshair" };
8489
8534
  /**
package/dist/index.js CHANGED
@@ -218,7 +218,9 @@ class DOMSelectionState {
218
218
  this.focusNode == domSel.focusNode && this.focusOffset == domSel.focusOffset;
219
219
  }
220
220
  setRange(range) {
221
- this.set(range.anchorNode, range.anchorOffset, range.focusNode, range.focusOffset);
221
+ let { anchorNode, focusNode } = range;
222
+ // Clip offsets to node size to avoid crashes when Safari reports bogus offsets (#1152)
223
+ this.set(anchorNode, Math.min(range.anchorOffset, anchorNode ? maxOffset(anchorNode) : 0), focusNode, Math.min(range.focusOffset, focusNode ? maxOffset(focusNode) : 0));
222
224
  }
223
225
  set(anchorNode, anchorOffset, focusNode, focusOffset) {
224
226
  this.anchorNode = anchorNode;
@@ -291,6 +293,8 @@ function atElementStart(doc, selection) {
291
293
  let node = selection.focusNode, offset = selection.focusOffset;
292
294
  if (!node || selection.anchorNode != node || selection.anchorOffset != offset)
293
295
  return false;
296
+ // Safari can report bogus offsets (#1152)
297
+ offset = Math.min(offset, maxOffset(node));
294
298
  for (;;) {
295
299
  if (offset) {
296
300
  if (node.nodeType != 1)
@@ -1638,7 +1642,7 @@ class BlockWidgetView extends ContentView {
1638
1642
  }
1639
1643
  domBoundsAround() { return null; }
1640
1644
  become(other) {
1641
- if (other instanceof BlockWidgetView && other.type == this.type &&
1645
+ if (other instanceof BlockWidgetView &&
1642
1646
  other.widget.constructor == this.widget.constructor) {
1643
1647
  if (!other.widget.compare(this.widget))
1644
1648
  this.markDirty(true);
@@ -1646,6 +1650,7 @@ class BlockWidgetView extends ContentView {
1646
1650
  this.prevWidget = this.widget;
1647
1651
  this.widget = other.widget;
1648
1652
  this.length = other.length;
1653
+ this.type = other.type;
1649
1654
  this.breakAfter = other.breakAfter;
1650
1655
  return true;
1651
1656
  }
@@ -2002,6 +2007,23 @@ const contentAttributes = /*@__PURE__*/Facet.define();
2002
2007
  const decorations = /*@__PURE__*/Facet.define();
2003
2008
  const atomicRanges = /*@__PURE__*/Facet.define();
2004
2009
  const scrollMargins = /*@__PURE__*/Facet.define();
2010
+ function getScrollMargins(view) {
2011
+ let left = 0, right = 0, top = 0, bottom = 0;
2012
+ for (let source of view.state.facet(scrollMargins)) {
2013
+ let m = source(view);
2014
+ if (m) {
2015
+ if (m.left != null)
2016
+ left = Math.max(left, m.left);
2017
+ if (m.right != null)
2018
+ right = Math.max(right, m.right);
2019
+ if (m.top != null)
2020
+ top = Math.max(top, m.top);
2021
+ if (m.bottom != null)
2022
+ bottom = Math.max(bottom, m.bottom);
2023
+ }
2024
+ }
2025
+ return { left, right, top, bottom };
2026
+ }
2005
2027
  const styleModule = /*@__PURE__*/Facet.define();
2006
2028
  class ChangedRange {
2007
2029
  constructor(fromA, toA, fromB, toB) {
@@ -2454,6 +2476,7 @@ class DOMReader {
2454
2476
  constructor(points, state) {
2455
2477
  this.points = points;
2456
2478
  this.text = "";
2479
+ console.log("make reader");
2457
2480
  this.lineSeparator = state.facet(EditorState.lineSeparator);
2458
2481
  }
2459
2482
  append(text) {
@@ -2468,6 +2491,7 @@ class DOMReader {
2468
2491
  let parent = start.parentNode;
2469
2492
  for (let cur = start;;) {
2470
2493
  this.findPointBefore(parent, cur);
2494
+ let oldLen = this.text.length;
2471
2495
  this.readNode(cur);
2472
2496
  let next = cur.nextSibling;
2473
2497
  if (next == end)
@@ -2475,7 +2499,7 @@ class DOMReader {
2475
2499
  let view = ContentView.get(cur), nextView = ContentView.get(next);
2476
2500
  if (view && nextView ? view.breakAfter :
2477
2501
  (view ? view.breakAfter : isBlockElement(cur)) ||
2478
- (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore)))
2502
+ (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen))
2479
2503
  this.lineBreak();
2480
2504
  cur = next;
2481
2505
  }
@@ -2932,22 +2956,10 @@ class DocView extends ContentView {
2932
2956
  if (!range.empty && (other = this.coordsAt(range.anchor, range.anchor > range.head ? -1 : 1)))
2933
2957
  rect = { left: Math.min(rect.left, other.left), top: Math.min(rect.top, other.top),
2934
2958
  right: Math.max(rect.right, other.right), bottom: Math.max(rect.bottom, other.bottom) };
2935
- let mLeft = 0, mRight = 0, mTop = 0, mBottom = 0;
2936
- for (let margins of this.view.state.facet(scrollMargins).map(f => f(this.view)))
2937
- if (margins) {
2938
- let { left, right, top, bottom } = margins;
2939
- if (left != null)
2940
- mLeft = Math.max(mLeft, left);
2941
- if (right != null)
2942
- mRight = Math.max(mRight, right);
2943
- if (top != null)
2944
- mTop = Math.max(mTop, top);
2945
- if (bottom != null)
2946
- mBottom = Math.max(mBottom, bottom);
2947
- }
2959
+ let margins = getScrollMargins(this.view);
2948
2960
  let targetRect = {
2949
- left: rect.left - mLeft, top: rect.top - mTop,
2950
- right: rect.right + mRight, bottom: rect.bottom + mBottom
2961
+ left: rect.left - margins.left, top: rect.top - margins.top,
2962
+ right: rect.right + margins.right, bottom: rect.bottom + margins.bottom
2951
2963
  };
2952
2964
  scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, target.xMargin, target.yMargin, this.view.textDirection == Direction.LTR);
2953
2965
  }
@@ -3436,15 +3448,15 @@ function moveVertically(view, start, forward, distance) {
3436
3448
  return EditorSelection.cursor(pos, start.assoc, undefined, goal);
3437
3449
  }
3438
3450
  }
3439
- function skipAtoms(view, oldPos, pos) {
3440
- let atoms = view.state.facet(atomicRanges).map(f => f(view));
3451
+ function skipAtomicRanges(atoms, pos, bias) {
3441
3452
  for (;;) {
3442
- let moved = false;
3453
+ let moved = 0;
3443
3454
  for (let set of atoms) {
3444
- set.between(pos.from - 1, pos.from + 1, (from, to, value) => {
3445
- if (pos.from > from && pos.from < to) {
3446
- pos = oldPos.head > pos.from ? EditorSelection.cursor(from, 1) : EditorSelection.cursor(to, -1);
3447
- moved = true;
3455
+ set.between(pos - 1, pos + 1, (from, to, value) => {
3456
+ if (pos > from && pos < to) {
3457
+ let side = moved || bias || (pos - from < to - pos ? -1 : 1);
3458
+ pos = side < 0 ? from : to;
3459
+ moved = side;
3448
3460
  }
3449
3461
  });
3450
3462
  }
@@ -3452,6 +3464,10 @@ function skipAtoms(view, oldPos, pos) {
3452
3464
  return pos;
3453
3465
  }
3454
3466
  }
3467
+ function skipAtoms(view, oldPos, pos) {
3468
+ let newPos = skipAtomicRanges(view.state.facet(atomicRanges).map(f => f(view)), pos.from, oldPos.head > pos.from ? -1 : 1);
3469
+ return newPos == pos.from ? pos : EditorSelection.cursor(newPos, newPos < pos.from ? 1 : -1);
3470
+ }
3455
3471
 
3456
3472
  // This will also be where dragging info and such goes
3457
3473
  class InputState {
@@ -3701,6 +3717,7 @@ class MouseSelection {
3701
3717
  this.scrolling = -1;
3702
3718
  this.lastEvent = startEvent;
3703
3719
  this.scrollParent = scrollableParent(view.contentDOM);
3720
+ this.atoms = view.state.facet(atomicRanges).map(f => f(view));
3704
3721
  let doc = view.contentDOM.ownerDocument;
3705
3722
  doc.addEventListener("mousemove", this.move = this.move.bind(this));
3706
3723
  doc.addEventListener("mouseup", this.up = this.up.bind(this));
@@ -3727,13 +3744,14 @@ class MouseSelection {
3727
3744
  let sx = 0, sy = 0;
3728
3745
  let rect = ((_a = this.scrollParent) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect())
3729
3746
  || { left: 0, top: 0, right: this.view.win.innerWidth, bottom: this.view.win.innerHeight };
3730
- if (event.clientX <= rect.left + dragScrollMargin)
3747
+ let margins = getScrollMargins(this.view);
3748
+ if (event.clientX - margins.left <= rect.left + dragScrollMargin)
3731
3749
  sx = -dragScrollSpeed(rect.left - event.clientX);
3732
- else if (event.clientX >= rect.right - dragScrollMargin)
3750
+ else if (event.clientX + margins.right >= rect.right - dragScrollMargin)
3733
3751
  sx = dragScrollSpeed(event.clientX - rect.right);
3734
- if (event.clientY <= rect.top + dragScrollMargin)
3752
+ if (event.clientY - margins.top <= rect.top + dragScrollMargin)
3735
3753
  sy = -dragScrollSpeed(rect.top - event.clientY);
3736
- else if (event.clientY >= rect.bottom - dragScrollMargin)
3754
+ else if (event.clientY + margins.bottom >= rect.bottom - dragScrollMargin)
3737
3755
  sy = dragScrollSpeed(event.clientY - rect.bottom);
3738
3756
  this.setScrollSpeed(sx, sy);
3739
3757
  }
@@ -3773,10 +3791,33 @@ class MouseSelection {
3773
3791
  if (this.dragging === false)
3774
3792
  this.select(this.lastEvent);
3775
3793
  }
3794
+ skipAtoms(sel) {
3795
+ let ranges = null;
3796
+ for (let i = 0; i < sel.ranges.length; i++) {
3797
+ let range = sel.ranges[i], updated = null;
3798
+ if (range.empty) {
3799
+ let pos = skipAtomicRanges(this.atoms, range.from, 0);
3800
+ if (pos != range.from)
3801
+ updated = EditorSelection.cursor(pos, -1);
3802
+ }
3803
+ else {
3804
+ let from = skipAtomicRanges(this.atoms, range.from, -1);
3805
+ let to = skipAtomicRanges(this.atoms, range.to, 1);
3806
+ if (from != range.from || to != range.to)
3807
+ updated = EditorSelection.range(range.from == range.anchor ? from : to, range.from == range.head ? from : to);
3808
+ }
3809
+ if (updated) {
3810
+ if (!ranges)
3811
+ ranges = sel.ranges.slice();
3812
+ ranges[i] = updated;
3813
+ }
3814
+ }
3815
+ return ranges ? EditorSelection.create(ranges, sel.mainIndex) : sel;
3816
+ }
3776
3817
  select(event) {
3777
- let selection = this.style.get(event, this.extend, this.multiple);
3778
- if (this.mustSelect || !selection.eq(this.view.state.selection) ||
3779
- selection.main.assoc != this.view.state.selection.main.assoc)
3818
+ let { view } = this, selection = this.skipAtoms(this.style.get(event, this.extend, this.multiple));
3819
+ if (this.mustSelect || !selection.eq(view.state.selection) ||
3820
+ selection.main.assoc != view.state.selection.main.assoc)
3780
3821
  this.view.dispatch({
3781
3822
  selection,
3782
3823
  userEvent: "select.pointer"
@@ -5774,13 +5815,13 @@ class DOMChange {
5774
5815
  function applyDOMChange(view, domChange) {
5775
5816
  let change;
5776
5817
  let { newSel } = domChange, sel = view.state.selection.main;
5818
+ let lastKey = view.inputState.lastKeyTime > Date.now() - 100 ? view.inputState.lastKeyCode : -1;
5777
5819
  if (domChange.bounds) {
5778
5820
  let { from, to } = domChange.bounds;
5779
5821
  let preferredPos = sel.from, preferredSide = null;
5780
5822
  // Prefer anchoring to end when Backspace is pressed (or, on
5781
5823
  // Android, when something was deleted)
5782
- if (view.inputState.lastKeyCode === 8 && view.inputState.lastKeyTime > Date.now() - 100 ||
5783
- browser.android && domChange.text.length < to - from) {
5824
+ if (lastKey === 8 || browser.android && domChange.text.length < to - from) {
5784
5825
  preferredPos = sel.to;
5785
5826
  preferredSide = "end";
5786
5827
  }
@@ -5788,7 +5829,7 @@ function applyDOMChange(view, domChange) {
5788
5829
  if (diff) {
5789
5830
  // Chrome inserts two newlines when pressing shift-enter at the
5790
5831
  // end of a line. DomChange drops one of those.
5791
- if (browser.chrome && view.inputState.lastKeyCode == 13 &&
5832
+ if (browser.chrome && lastKey == 13 &&
5792
5833
  diff.toB == diff.from + 2 && domChange.text.slice(diff.from, diff.toB) == LineBreakPlaceholder + LineBreakPlaceholder)
5793
5834
  diff.toB--;
5794
5835
  change = { from: from + diff.from, to: from + diff.toA,
@@ -5846,7 +5887,8 @@ function applyDOMChange(view, domChange) {
5846
5887
  ((change.from == sel.from && change.to == sel.to &&
5847
5888
  change.insert.length == 1 && change.insert.lines == 2 &&
5848
5889
  dispatchKey(view.contentDOM, "Enter", 13)) ||
5849
- (change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 &&
5890
+ ((change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 ||
5891
+ lastKey == 8 && change.insert.length < change.to - change.from) &&
5850
5892
  dispatchKey(view.contentDOM, "Backspace", 8)) ||
5851
5893
  (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
5852
5894
  dispatchKey(view.contentDOM, "Delete", 46))))
@@ -6244,7 +6286,10 @@ class DOMObserver {
6244
6286
  let key = this.delayedAndroidKey;
6245
6287
  if (key) {
6246
6288
  this.clearDelayedAndroidKey();
6247
- if (!this.flush() && key.force)
6289
+ this.view.inputState.lastKeyCode = key.keyCode;
6290
+ this.view.inputState.lastKeyTime = Date.now();
6291
+ let flushed = this.flush();
6292
+ if (!flushed && key.force)
6248
6293
  dispatchKey(this.dom, key.key, key.keyCode);
6249
6294
  }
6250
6295
  };
@@ -7697,7 +7742,7 @@ function rectanglesForRange(view, className, range) {
7697
7742
  let top = visualStart ? drawForLine(range.from, null, visualStart) : drawForWidget(startBlock, false);
7698
7743
  let bottom = visualEnd ? drawForLine(null, range.to, visualEnd) : drawForWidget(endBlock, true);
7699
7744
  let between = [];
7700
- if ((visualStart || startBlock).to < (visualEnd || endBlock).from - 1)
7745
+ if ((visualStart || startBlock).to < (visualEnd || endBlock).from - (visualStart && visualEnd ? 1 : 0))
7701
7746
  between.push(piece(leftSide, top.bottom, rightSide, bottom.top));
7702
7747
  else if (top.bottom < bottom.top && view.elementAtHeight((top.bottom + bottom.top) / 2).type == BlockType.Text)
7703
7748
  top.bottom = bottom.top = (top.bottom + bottom.top) / 2;
@@ -8473,10 +8518,10 @@ function rectangularSelection(options) {
8473
8518
  return EditorView.mouseSelectionStyle.of((view, event) => filter(event) ? rectangleSelectionStyle(view, event) : null);
8474
8519
  }
8475
8520
  const keys = {
8476
- Alt: [18, e => e.altKey],
8477
- Control: [17, e => e.ctrlKey],
8478
- Shift: [16, e => e.shiftKey],
8479
- Meta: [91, e => e.metaKey]
8521
+ Alt: [18, e => !!e.altKey],
8522
+ Control: [17, e => !!e.ctrlKey],
8523
+ Shift: [16, e => !!e.shiftKey],
8524
+ Meta: [91, e => !!e.metaKey]
8480
8525
  };
8481
8526
  const showCrosshair = { style: "cursor: crosshair" };
8482
8527
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.11.1",
3
+ "version": "6.11.3",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",