@codemirror/view 0.19.31 → 0.19.35

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,49 @@
1
+ ## 0.19.35 (2021-12-20)
2
+
3
+ ### Bug fixes
4
+
5
+ The editor will now handle double-taps as if they are double-clicks, rather than letting the browser's native behavior happen (because the latter often does the wrong thing).
6
+
7
+ Fix an issue where backspacing out a selection on Chrome Android would sometimes only delete the last character due to event order issues.
8
+
9
+ `posAtCoords`, without second argument, will no longer return null for positions below or above the document.
10
+
11
+ ## 0.19.34 (2021-12-17)
12
+
13
+ ### Bug fixes
14
+
15
+ Fix a bug where content line elements would in some cases lose their `cm-line` class. Move test to scrollIntoView
16
+
17
+ ## 0.19.33 (2021-12-16)
18
+
19
+ ### Breaking changes
20
+
21
+ `EditorView.scrollTo` and `EditorView.centerOn` are deprecated in favor of `EditorView.scrollIntoView`, and will be removed in the next breaking release.
22
+
23
+ ### Bug fixes
24
+
25
+ Fix an issue that could cause the editor to unnecessarily interfere with composition (especially visible on macOS Chrome).
26
+
27
+ A composition started with multiple lines selected will no longer be interruptd by the editor.
28
+
29
+ ### New features
30
+
31
+ The new `EditorView.scrollIntoView` function allows you to do more fine-grained scrolling.
32
+
33
+ ## 0.19.32 (2021-12-15)
34
+
35
+ ### Bug fixes
36
+
37
+ Fix a bug where CodeMirror's own event handers would run even after a user-supplied handler called `preventDefault` on an event.
38
+
39
+ Properly draw selections when negative text-indent is used for soft wrapping.
40
+
41
+ Fix an issue where `viewportLineBlocks` could hold inaccurate height information when the vertical scaling changed.
42
+
43
+ Fixes drop cursor positioning when the document is scrolled. Force a content measure when the editor comes into view
44
+
45
+ Fix a bug that could cause the editor to not measure its layout the first time it came into view.
46
+
1
47
  ## 0.19.31 (2021-12-13)
2
48
 
3
49
  ### New features
package/dist/index.cjs CHANGED
@@ -101,8 +101,7 @@ function windowRect(win) {
101
101
  return { left: 0, right: win.innerWidth,
102
102
  top: 0, bottom: win.innerHeight };
103
103
  }
104
- const ScrollSpace = 5;
105
- function scrollRectIntoView(dom, rect, side, center) {
104
+ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
106
105
  let doc = dom.ownerDocument, win = doc.defaultView;
107
106
  for (let cur = dom; cur;) {
108
107
  if (cur.nodeType == 1) { // Element
@@ -121,38 +120,42 @@ function scrollRectIntoView(dom, rect, side, center) {
121
120
  top: rect.top, bottom: rect.top + cur.clientHeight };
122
121
  }
123
122
  let moveX = 0, moveY = 0;
124
- if (center) {
123
+ if (y == "nearest") {
124
+ if (rect.top < bounding.top) {
125
+ moveY = -(bounding.top - rect.top + yMargin);
126
+ if (side > 0 && rect.bottom > bounding.bottom + moveY)
127
+ moveY = rect.bottom - bounding.bottom + moveY + yMargin;
128
+ }
129
+ else if (rect.bottom > bounding.bottom) {
130
+ moveY = rect.bottom - bounding.bottom + yMargin;
131
+ if (side < 0 && (rect.top - moveY) < bounding.top)
132
+ moveY = -(bounding.top + moveY - rect.top + yMargin);
133
+ }
134
+ }
135
+ else {
125
136
  let rectHeight = rect.bottom - rect.top, boundingHeight = bounding.bottom - bounding.top;
126
- let targetTop;
127
- if (rectHeight <= boundingHeight)
128
- targetTop = rect.top + rectHeight / 2 - boundingHeight / 2;
129
- else if (side < 0)
130
- targetTop = rect.top - ScrollSpace;
131
- else
132
- targetTop = rect.bottom + ScrollSpace - boundingHeight;
137
+ let targetTop = y == "center" && rectHeight <= boundingHeight ? rect.top + rectHeight / 2 - boundingHeight / 2 :
138
+ y == "start" || y == "center" && side < 0 ? rect.top - yMargin :
139
+ rect.bottom - boundingHeight + yMargin;
133
140
  moveY = targetTop - bounding.top;
134
- if (Math.abs(moveY) <= 1)
135
- moveY = 0;
136
- }
137
- else if (rect.top < bounding.top) {
138
- moveY = -(bounding.top - rect.top + ScrollSpace);
139
- if (side > 0 && rect.bottom > bounding.bottom + moveY)
140
- moveY = rect.bottom - bounding.bottom + moveY + ScrollSpace;
141
- }
142
- else if (rect.bottom > bounding.bottom) {
143
- moveY = rect.bottom - bounding.bottom + ScrollSpace;
144
- if (side < 0 && (rect.top - moveY) < bounding.top)
145
- moveY = -(bounding.top + moveY - rect.top + ScrollSpace);
146
141
  }
147
- if (rect.left < bounding.left) {
148
- moveX = -(bounding.left - rect.left + ScrollSpace);
149
- if (side > 0 && rect.right > bounding.right + moveX)
150
- moveX = rect.right - bounding.right + moveX + ScrollSpace;
142
+ if (x == "nearest") {
143
+ if (rect.left < bounding.left) {
144
+ moveX = -(bounding.left - rect.left + xMargin);
145
+ if (side > 0 && rect.right > bounding.right + moveX)
146
+ moveX = rect.right - bounding.right + moveX + xMargin;
147
+ }
148
+ else if (rect.right > bounding.right) {
149
+ moveX = rect.right - bounding.right + xMargin;
150
+ if (side < 0 && rect.left < bounding.left + moveX)
151
+ moveX = -(bounding.left + moveX - rect.left + xMargin);
152
+ }
151
153
  }
152
- else if (rect.right > bounding.right) {
153
- moveX = rect.right - bounding.right + ScrollSpace;
154
- if (side < 0 && rect.left < bounding.left + moveX)
155
- moveX = -(bounding.left + moveX - rect.left + ScrollSpace);
154
+ else {
155
+ let targetLeft = x == "center" ? rect.left + (rect.right - rect.left) / 2 - (bounding.right - bounding.left) / 2 :
156
+ (x == "start") == ltr ? rect.left - xMargin :
157
+ rect.right - (bounding.right - bounding.left) + xMargin;
158
+ moveX = targetLeft - bounding.left;
156
159
  }
157
160
  if (moveX || moveY) {
158
161
  if (top) {
@@ -176,7 +179,7 @@ function scrollRectIntoView(dom, rect, side, center) {
176
179
  if (top)
177
180
  break;
178
181
  cur = cur.assignedSlot || cur.parentNode;
179
- center = false;
182
+ x = y = "nearest";
180
183
  }
181
184
  else if (cur.nodeType == 11) { // A shadow root
182
185
  cur = cur.host;
@@ -263,6 +266,10 @@ function getRoot(node) {
263
266
  }
264
267
  return null;
265
268
  }
269
+ function clearAttributes(node) {
270
+ while (node.attributes.length)
271
+ node.removeAttributeNode(node.attributes[0]);
272
+ }
266
273
 
267
274
  class DOMPos {
268
275
  constructor(node, offset, precise = true) {
@@ -309,14 +316,16 @@ class ContentView {
309
316
  // given position.
310
317
  coordsAt(_pos, _side) { return null; }
311
318
  sync(track) {
312
- var _a;
313
319
  if (this.dirty & 2 /* Node */) {
314
320
  let parent = this.dom;
315
321
  let pos = parent.firstChild;
316
322
  for (let child of this.children) {
317
323
  if (child.dirty) {
318
- if (!child.dom && pos && !((_a = ContentView.get(pos)) === null || _a === void 0 ? void 0 : _a.parent))
319
- child.reuseDOM(pos);
324
+ if (!child.dom && pos) {
325
+ let contentView = ContentView.get(pos);
326
+ if (!contentView || !contentView.parent && contentView.constructor == child.constructor)
327
+ child.reuseDOM(pos);
328
+ }
320
329
  child.sync(track);
321
330
  child.dirty = 0 /* Not */;
322
331
  }
@@ -344,7 +353,7 @@ class ContentView {
344
353
  }
345
354
  }
346
355
  }
347
- reuseDOM(_dom) { return false; }
356
+ reuseDOM(_dom) { }
348
357
  localPosFromDOM(node, offset) {
349
358
  let after;
350
359
  if (node == this.dom) {
@@ -643,10 +652,8 @@ class TextView extends ContentView {
643
652
  }
644
653
  }
645
654
  reuseDOM(dom) {
646
- if (dom.nodeType != 3)
647
- return false;
648
- this.createDOM(dom);
649
- return true;
655
+ if (dom.nodeType == 3)
656
+ this.createDOM(dom);
650
657
  }
651
658
  merge(from, to, source) {
652
659
  if (source && (!(source instanceof TextView) || this.length - (to - from) + source.length > MaxJoinLen))
@@ -681,18 +688,26 @@ class MarkView extends ContentView {
681
688
  for (let ch of children)
682
689
  ch.setParent(this);
683
690
  }
684
- createDOM() {
685
- let dom = document.createElement(this.mark.tagName);
691
+ setAttrs(dom) {
692
+ clearAttributes(dom);
686
693
  if (this.mark.class)
687
694
  dom.className = this.mark.class;
688
695
  if (this.mark.attrs)
689
696
  for (let name in this.mark.attrs)
690
697
  dom.setAttribute(name, this.mark.attrs[name]);
691
- this.setDOM(dom);
698
+ return dom;
699
+ }
700
+ reuseDOM(node) {
701
+ if (node.nodeName == this.mark.tagName.toUpperCase()) {
702
+ this.setDOM(node);
703
+ this.dirty |= 4 /* Attrs */ | 2 /* Node */;
704
+ }
692
705
  }
693
706
  sync(track) {
694
- if (!this.dom || (this.dirty & 4 /* Attrs */))
695
- this.createDOM();
707
+ if (!this.dom)
708
+ this.setDOM(this.setAttrs(document.createElement(this.mark.tagName)));
709
+ else if (this.dirty & 4 /* Attrs */)
710
+ this.setAttrs(this.dom);
696
711
  super.sync(track);
697
712
  }
698
713
  merge(from, to, source, _hasStart, openStart, openEnd) {
@@ -836,8 +851,7 @@ class WidgetView extends ContentView {
836
851
  }
837
852
  class CompositionView extends WidgetView {
838
853
  domAtPos(pos) { return new DOMPos(this.widget.text, pos); }
839
- sync() { if (!this.dom)
840
- this.setDOM(this.widget.toDOM()); }
854
+ sync() { this.setDOM(this.widget.toDOM()); }
841
855
  localPosFromDOM(node, offset) {
842
856
  return !offset ? 0 : node.nodeType == 3 ? Math.min(offset, this.length) : this.length;
843
857
  }
@@ -1302,13 +1316,24 @@ class LineView extends ContentView {
1302
1316
  domAtPos(pos) {
1303
1317
  return inlineDOMAtPos(this.dom, this.children, pos);
1304
1318
  }
1319
+ reuseDOM(node) {
1320
+ if (node.nodeName == "DIV") {
1321
+ this.setDOM(node);
1322
+ this.dirty |= 4 /* Attrs */ | 2 /* Node */;
1323
+ }
1324
+ }
1305
1325
  sync(track) {
1306
1326
  var _a;
1307
- if (!this.dom || (this.dirty & 4 /* Attrs */)) {
1327
+ if (!this.dom) {
1308
1328
  this.setDOM(document.createElement("div"));
1309
1329
  this.dom.className = "cm-line";
1310
1330
  this.prevAttrs = this.attrs ? null : undefined;
1311
1331
  }
1332
+ else if (this.dirty & 4 /* Attrs */) {
1333
+ clearAttributes(this.dom);
1334
+ this.dom.className = "cm-line";
1335
+ this.prevAttrs = this.attrs ? null : undefined;
1336
+ }
1312
1337
  if (this.prevAttrs !== undefined) {
1313
1338
  updateAttrs(this.dom, this.prevAttrs, this.attrs);
1314
1339
  this.dom.classList.add("cm-line");
@@ -1580,12 +1605,27 @@ const mouseSelectionStyle = state.Facet.define();
1580
1605
  const exceptionSink = state.Facet.define();
1581
1606
  const updateListener = state.Facet.define();
1582
1607
  const inputHandler = state.Facet.define();
1608
+ // FIXME remove
1583
1609
  const scrollTo = state.StateEffect.define({
1584
1610
  map: (range, changes) => range.map(changes)
1585
1611
  });
1612
+ // FIXME remove
1586
1613
  const centerOn = state.StateEffect.define({
1587
1614
  map: (range, changes) => range.map(changes)
1588
1615
  });
1616
+ class ScrollTarget {
1617
+ constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5) {
1618
+ this.range = range;
1619
+ this.y = y;
1620
+ this.x = x;
1621
+ this.yMargin = yMargin;
1622
+ this.xMargin = xMargin;
1623
+ }
1624
+ map(changes) {
1625
+ return changes.empty ? this : new ScrollTarget(this.range.map(changes), this.y, this.x, this.yMargin, this.xMargin);
1626
+ }
1627
+ }
1628
+ const scrollIntoView = state.StateEffect.define({ map: (t, ch) => t.map(ch) });
1589
1629
  /**
1590
1630
  Log or report an unhandled exception in client code. Should
1591
1631
  probably only be used by extension code that allows client code to
@@ -2664,7 +2704,8 @@ class DocView extends ContentView {
2664
2704
  this.view.viewState.lineGapDeco
2665
2705
  ];
2666
2706
  }
2667
- scrollIntoView({ range, center }) {
2707
+ scrollIntoView(target) {
2708
+ let { range } = target;
2668
2709
  let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
2669
2710
  if (!rect)
2670
2711
  return;
@@ -2684,10 +2725,11 @@ class DocView extends ContentView {
2684
2725
  if (bottom != null)
2685
2726
  mBottom = Math.max(mBottom, bottom);
2686
2727
  }
2687
- scrollRectIntoView(this.view.scrollDOM, {
2728
+ let targetRect = {
2688
2729
  left: rect.left - mLeft, top: rect.top - mTop,
2689
2730
  right: rect.right + mRight, bottom: rect.bottom + mBottom
2690
- }, range.head < range.anchor ? -1 : 1, center);
2731
+ };
2732
+ 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);
2691
2733
  }
2692
2734
  }
2693
2735
  function betweenUneditable(pos) {
@@ -2948,12 +2990,8 @@ function domPosInText(node, x, y) {
2948
2990
  function posAtCoords(view, { x, y }, precise, bias = -1) {
2949
2991
  var _a;
2950
2992
  let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop;
2951
- let block, yOffset = y - docTop, { docHeight } = view.viewState;
2952
- if (yOffset < 0 || yOffset > docHeight) {
2953
- if (precise)
2954
- return null;
2955
- yOffset = yOffset < 0 ? 0 : docHeight;
2956
- }
2993
+ let block, { docHeight } = view.viewState;
2994
+ let yOffset = Math.max(0, Math.min(y - docTop, docHeight));
2957
2995
  // Scan for a text block near the queried y position
2958
2996
  for (let halfLine = view.defaultLineHeight / 2, bounced = false;;) {
2959
2997
  block = view.elementAtHeight(yOffset);
@@ -2974,20 +3012,29 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
2974
3012
  }
2975
3013
  y = docTop + yOffset;
2976
3014
  let lineStart = block.from;
2977
- // Clip x to the viewport sides
2978
- x = Math.max(content.left + 1, Math.min(Math.max(content.right, content.left + view.docView.minWidth) - 1, x));
2979
3015
  // If this is outside of the rendered viewport, we can't determine a position
2980
3016
  if (lineStart < view.viewport.from)
2981
- return view.viewport.from == 0 ? 0 : posAtCoordsImprecise(view, content, block, x, y);
3017
+ return view.viewport.from == 0 ? 0 : precise ? posAtCoordsImprecise(view, content, block, x, y) : null;
2982
3018
  if (lineStart > view.viewport.to)
2983
- return view.viewport.to == view.state.doc.length ? view.state.doc.length : posAtCoordsImprecise(view, content, block, x, y);
3019
+ return view.viewport.to == view.state.doc.length ? view.state.doc.length :
3020
+ precise ? posAtCoordsImprecise(view, content, block, x, y) : null;
2984
3021
  // Prefer ShadowRootOrDocument.elementFromPoint if present, fall back to document if not
2985
3022
  let doc = view.dom.ownerDocument;
2986
- let element = (view.root.elementFromPoint ? view.root : doc).elementFromPoint(x, y);
3023
+ let root = view.root.elementFromPoint ? view.root : doc;
3024
+ let element = root.elementFromPoint(x, y);
3025
+ if (element && !view.contentDOM.contains(element))
3026
+ element = null;
3027
+ // If the element is unexpected, clip x at the sides of the content area and try again
3028
+ if (!element) {
3029
+ x = Math.max(content.left + 1, Math.min(content.right - 1, x));
3030
+ element = root.elementFromPoint(x, y);
3031
+ if (element && !view.contentDOM.contains(element))
3032
+ element = null;
3033
+ }
2987
3034
  // There's visible editor content under the point, so we can try
2988
3035
  // using caret(Position|Range)FromPoint as a shortcut
2989
3036
  let node, offset = -1;
2990
- if (element && view.contentDOM.contains(element) && ((_a = view.docView.nearest(element)) === null || _a === void 0 ? void 0 : _a.isEditable) != false) {
3037
+ if (element && ((_a = view.docView.nearest(element)) === null || _a === void 0 ? void 0 : _a.isEditable) != false) {
2991
3038
  if (doc.caretPositionFromPoint) {
2992
3039
  let pos = doc.caretPositionFromPoint(x, y);
2993
3040
  if (pos)
@@ -3199,7 +3246,7 @@ class InputState {
3199
3246
  let handler = set.handlers[type];
3200
3247
  if (handler) {
3201
3248
  try {
3202
- if (handler.call(set.plugin, event, view))
3249
+ if (handler.call(set.plugin, event, view) || event.defaultPrevented)
3203
3250
  return true;
3204
3251
  }
3205
3252
  catch (e) {
@@ -3468,7 +3515,7 @@ handlers.touchmove = view => {
3468
3515
  };
3469
3516
  handlers.mousedown = (view, event) => {
3470
3517
  view.observer.flush();
3471
- if (lastTouch > Date.now() - 2000)
3518
+ if (lastTouch > Date.now() - 2000 && getClickType(event) == 1)
3472
3519
  return; // Ignore touch interaction
3473
3520
  let style = null;
3474
3521
  for (let makeStyle of view.state.facet(mouseSelectionStyle)) {
@@ -3590,9 +3637,9 @@ handlers.dragstart = (view, event) => {
3590
3637
  }
3591
3638
  };
3592
3639
  function dropText(view, event, text, direct) {
3593
- let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY });
3594
- if (dropPos == null || !text)
3640
+ if (!text)
3595
3641
  return;
3642
+ let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
3596
3643
  event.preventDefault();
3597
3644
  let { mouseSelection } = view.inputState;
3598
3645
  let del = direct && mouseSelection && mouseSelection.dragging && mouseSelection.dragMove ?
@@ -4521,15 +4568,6 @@ class LineGapWidget extends WidgetType {
4521
4568
  }
4522
4569
  get estimatedHeight() { return this.vertical ? this.size : -1; }
4523
4570
  }
4524
- class ScrollTarget {
4525
- constructor(range, center = false) {
4526
- this.range = range;
4527
- this.center = center;
4528
- }
4529
- map(changes) {
4530
- return changes.empty ? this : new ScrollTarget(this.range.map(changes), this.center);
4531
- }
4532
- }
4533
4571
  class ViewState {
4534
4572
  constructor(state) {
4535
4573
  this.state = state;
@@ -4604,9 +4642,9 @@ class ViewState {
4604
4642
  let updateLines = !update.changes.empty || (update.flags & 2 /* Height */) ||
4605
4643
  viewport.from != this.viewport.from || viewport.to != this.viewport.to;
4606
4644
  this.viewport = viewport;
4645
+ this.updateForViewport();
4607
4646
  if (updateLines)
4608
4647
  this.updateViewportLines();
4609
- this.updateForViewport();
4610
4648
  if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
4611
4649
  this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps, update.changes)));
4612
4650
  update.flags |= this.computeVisibleRanges();
@@ -4638,7 +4676,12 @@ class ViewState {
4638
4676
  let pixelViewport = this.printing ? { top: -1e8, bottom: 1e8, left: -1e8, right: 1e8 } : visiblePixelRange(dom, this.paddingTop);
4639
4677
  let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
4640
4678
  this.pixelViewport = pixelViewport;
4641
- this.inView = this.pixelViewport.bottom > this.pixelViewport.top && this.pixelViewport.right > this.pixelViewport.left;
4679
+ let inView = this.pixelViewport.bottom > this.pixelViewport.top && this.pixelViewport.right > this.pixelViewport.left;
4680
+ if (inView != this.inView) {
4681
+ this.inView = inView;
4682
+ if (inView)
4683
+ measureContent = true;
4684
+ }
4642
4685
  if (!this.inView)
4643
4686
  return 0;
4644
4687
  if (measureContent) {
@@ -4675,9 +4718,9 @@ class ViewState {
4675
4718
  this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to);
4676
4719
  if (viewportChange)
4677
4720
  this.viewport = this.getViewport(bias, this.scrollTarget);
4721
+ this.updateForViewport();
4678
4722
  if ((result & 2 /* Height */) || viewportChange)
4679
4723
  this.updateViewportLines();
4680
- this.updateForViewport();
4681
4724
  if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
4682
4725
  this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps));
4683
4726
  result |= this.computeVisibleRanges();
@@ -4705,9 +4748,9 @@ class ViewState {
4705
4748
  let { head } = scrollTarget.range, viewHeight = this.editorHeight;
4706
4749
  if (head < viewport.from || head > viewport.to) {
4707
4750
  let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
4708
- if (scrollTarget.center)
4751
+ if (scrollTarget.y == "center")
4709
4752
  topPos = (block.top + block.bottom) / 2 - viewHeight / 2;
4710
- else if (head < viewport.from)
4753
+ else if (scrollTarget.y == "start" || scrollTarget.y == "nearest" && head < viewport.from)
4711
4754
  topPos = block.top;
4712
4755
  else
4713
4756
  topPos = block.bottom - viewHeight;
@@ -5213,7 +5256,7 @@ class DOMObserver {
5213
5256
  this.intersection = new IntersectionObserver(entries => {
5214
5257
  if (this.parentCheck < 0)
5215
5258
  this.parentCheck = setTimeout(this.listenForScroll.bind(this), 1000);
5216
- if (entries.length > 0 && entries[entries.length - 1].intersectionRatio > 0 != this.intersecting) {
5259
+ if (entries.length > 0 && (entries[entries.length - 1].intersectionRatio > 0) != this.intersecting) {
5217
5260
  this.intersecting = !this.intersecting;
5218
5261
  if (this.intersecting != this.view.inView)
5219
5262
  this.onScrollChanged(document.createEvent("Event"));
@@ -5254,8 +5297,10 @@ class DOMObserver {
5254
5297
  // Deletions on IE11 fire their events in the wrong order, giving
5255
5298
  // us a selection change event before the DOM changes are
5256
5299
  // reported.
5257
- // (Selection.isCollapsed isn't reliable on IE)
5258
- if (browser.ie && browser.ie_version <= 11 && !view.state.selection.main.empty &&
5300
+ // Chrome Android has a similar issue when backspacing out a
5301
+ // selection (#645).
5302
+ if ((browser.ie && browser.ie_version <= 11 || browser.android && browser.chrome) && !view.state.selection.main.empty &&
5303
+ // (Selection.isCollapsed isn't reliable on IE)
5259
5304
  sel.focusNode && isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset))
5260
5305
  this.flushSoon();
5261
5306
  else
@@ -5813,7 +5858,9 @@ class EditorView {
5813
5858
  if (e.is(scrollTo))
5814
5859
  scrollTarget = new ScrollTarget(e.value);
5815
5860
  else if (e.is(centerOn))
5816
- scrollTarget = new ScrollTarget(e.value, true);
5861
+ scrollTarget = new ScrollTarget(e.value, "center");
5862
+ else if (e.is(scrollIntoView))
5863
+ scrollTarget = e.value;
5817
5864
  }
5818
5865
  }
5819
5866
  this.viewState.update(update, scrollTarget);
@@ -6381,6 +6428,14 @@ class EditorView {
6381
6428
  this.destroyed = true;
6382
6429
  }
6383
6430
  /**
6431
+ Returns an effect that can be
6432
+ [added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a transaction to
6433
+ cause it to scroll the given position or range into view.
6434
+ */
6435
+ static scrollIntoView(pos, options = {}) {
6436
+ return scrollIntoView.of(new ScrollTarget(typeof pos == "number" ? state.EditorSelection.cursor(pos) : pos, options.y, options.x, options.yMargin, options.xMargin));
6437
+ }
6438
+ /**
6384
6439
  Facet that can be used to add DOM event handlers. The value
6385
6440
  should be an object mapping event names to handler functions. The
6386
6441
  first such function to return true will be assumed to have handled
@@ -6434,11 +6489,15 @@ class EditorView {
6434
6489
  /**
6435
6490
  Effect that can be [added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a
6436
6491
  transaction to make it scroll the given range into view.
6492
+
6493
+ *Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead.
6437
6494
  */
6438
6495
  EditorView.scrollTo = scrollTo;
6439
6496
  /**
6440
6497
  Effect that makes the editor scroll the given range to the
6441
6498
  center of the visible view.
6499
+
6500
+ *Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead.
6442
6501
  */
6443
6502
  EditorView.centerOn = centerOn;
6444
6503
  /**
@@ -6907,7 +6966,7 @@ function measureRange(view, range) {
6907
6966
  let ltr = view.textDirection == exports.Direction.LTR;
6908
6967
  let content = view.contentDOM, contentRect = content.getBoundingClientRect(), base = getBase(view);
6909
6968
  let lineStyle = window.getComputedStyle(content.firstChild);
6910
- let leftSide = contentRect.left + parseInt(lineStyle.paddingLeft);
6969
+ let leftSide = contentRect.left + parseInt(lineStyle.paddingLeft) + Math.min(0, parseInt(lineStyle.textIndent));
6911
6970
  let rightSide = contentRect.right - parseInt(lineStyle.paddingRight);
6912
6971
  let startBlock = blockAt(view, from), endBlock = blockAt(view, to);
6913
6972
  let visualStart = startBlock.type == exports.BlockType.Text ? startBlock : null;
@@ -7033,7 +7092,11 @@ const drawDropCursor = ViewPlugin.fromClass(class {
7033
7092
  if (!rect)
7034
7093
  return null;
7035
7094
  let outer = this.view.scrollDOM.getBoundingClientRect();
7036
- return { left: rect.left - outer.left, top: rect.top - outer.top, height: rect.bottom - rect.top };
7095
+ return {
7096
+ left: rect.left - outer.left + this.view.scrollDOM.scrollLeft,
7097
+ top: rect.top - outer.top + this.view.scrollDOM.scrollTop,
7098
+ height: rect.bottom - rect.top
7099
+ };
7037
7100
  }
7038
7101
  drawCursor(pos) {
7039
7102
  if (this.cursor) {
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import * as _codemirror_rangeset from '@codemirror/rangeset';
2
2
  import { RangeSet, RangeValue, Range } from '@codemirror/rangeset';
3
3
  export { Range } from '@codemirror/rangeset';
4
4
  import * as _codemirror_state from '@codemirror/state';
5
- import { EditorState, Extension, Transaction, ChangeSet, EditorSelection, TransactionSpec, SelectionRange, Facet } from '@codemirror/state';
5
+ import { EditorState, Extension, Transaction, ChangeSet, EditorSelection, TransactionSpec, SelectionRange, StateEffect, Facet } from '@codemirror/state';
6
6
  import { Line } from '@codemirror/text';
7
7
  import { StyleModule, StyleSpec } from 'style-mod';
8
8
 
@@ -264,6 +264,7 @@ interface Rect {
264
264
  readonly top: number;
265
265
  readonly bottom: number;
266
266
  }
267
+ declare type ScrollStrategy = "nearest" | "start" | "end" | "center";
267
268
 
268
269
  /**
269
270
  Command functions are used in key bindings and other types of user
@@ -936,8 +937,11 @@ declare class EditorView {
936
937
  */
937
938
  posAtDOM(node: Node, offset?: number): number;
938
939
  /**
939
- Get the document position at the given screen coordinates.
940
- Returns null if no valid position could be found.
940
+ Get the document position at the given screen coordinates. For
941
+ positions not covered by the visible viewport's DOM structure,
942
+ this will return null, unless `false` is passed as second
943
+ argument, in which case it'll return an estimated position that
944
+ would be near the coordinates if it were rendered.
941
945
  */
942
946
  posAtCoords(coords: {
943
947
  x: number;
@@ -1006,14 +1010,49 @@ declare class EditorView {
1006
1010
  /**
1007
1011
  Effect that can be [added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a
1008
1012
  transaction to make it scroll the given range into view.
1013
+
1014
+ *Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead.
1009
1015
  */
1010
1016
  static scrollTo: _codemirror_state.StateEffectType<SelectionRange>;
1011
1017
  /**
1012
1018
  Effect that makes the editor scroll the given range to the
1013
1019
  center of the visible view.
1020
+
1021
+ *Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead.
1014
1022
  */
1015
1023
  static centerOn: _codemirror_state.StateEffectType<SelectionRange>;
1016
1024
  /**
1025
+ Returns an effect that can be
1026
+ [added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a transaction to
1027
+ cause it to scroll the given position or range into view.
1028
+ */
1029
+ static scrollIntoView(pos: number | SelectionRange, options?: {
1030
+ /**
1031
+ By default (`"nearest"`) the position will be vertically
1032
+ scrolled only the minimal amount required to move the given
1033
+ position into view. You can set this to `"start"` to move it
1034
+ to the top of the view, `"end"` to move it to the bottom, or
1035
+ `"center"` to move it to the center.
1036
+ */
1037
+ y?: ScrollStrategy;
1038
+ /**
1039
+ Effect similar to
1040
+ [`y`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView^options.y), but for the
1041
+ horizontal scroll position.
1042
+ */
1043
+ x?: ScrollStrategy;
1044
+ /**
1045
+ Extra vertical distance to add when moving something into
1046
+ view. Not used with the `"center"` strategy. Defaults to 5.
1047
+ */
1048
+ yMargin?: number;
1049
+ /**
1050
+ Extra horizontal distance to add. Not used with the `"center"`
1051
+ strategy. Defaults to 5.
1052
+ */
1053
+ xMargin?: number;
1054
+ }): StateEffect<unknown>;
1055
+ /**
1017
1056
  Facet to add a [style
1018
1057
  module](https://github.com/marijnh/style-mod#documentation) to
1019
1058
  an editor view. The view will ensure that the module is
package/dist/index.js CHANGED
@@ -98,8 +98,7 @@ function windowRect(win) {
98
98
  return { left: 0, right: win.innerWidth,
99
99
  top: 0, bottom: win.innerHeight };
100
100
  }
101
- const ScrollSpace = 5;
102
- function scrollRectIntoView(dom, rect, side, center) {
101
+ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
103
102
  let doc = dom.ownerDocument, win = doc.defaultView;
104
103
  for (let cur = dom; cur;) {
105
104
  if (cur.nodeType == 1) { // Element
@@ -118,38 +117,42 @@ function scrollRectIntoView(dom, rect, side, center) {
118
117
  top: rect.top, bottom: rect.top + cur.clientHeight };
119
118
  }
120
119
  let moveX = 0, moveY = 0;
121
- if (center) {
120
+ if (y == "nearest") {
121
+ if (rect.top < bounding.top) {
122
+ moveY = -(bounding.top - rect.top + yMargin);
123
+ if (side > 0 && rect.bottom > bounding.bottom + moveY)
124
+ moveY = rect.bottom - bounding.bottom + moveY + yMargin;
125
+ }
126
+ else if (rect.bottom > bounding.bottom) {
127
+ moveY = rect.bottom - bounding.bottom + yMargin;
128
+ if (side < 0 && (rect.top - moveY) < bounding.top)
129
+ moveY = -(bounding.top + moveY - rect.top + yMargin);
130
+ }
131
+ }
132
+ else {
122
133
  let rectHeight = rect.bottom - rect.top, boundingHeight = bounding.bottom - bounding.top;
123
- let targetTop;
124
- if (rectHeight <= boundingHeight)
125
- targetTop = rect.top + rectHeight / 2 - boundingHeight / 2;
126
- else if (side < 0)
127
- targetTop = rect.top - ScrollSpace;
128
- else
129
- targetTop = rect.bottom + ScrollSpace - boundingHeight;
134
+ let targetTop = y == "center" && rectHeight <= boundingHeight ? rect.top + rectHeight / 2 - boundingHeight / 2 :
135
+ y == "start" || y == "center" && side < 0 ? rect.top - yMargin :
136
+ rect.bottom - boundingHeight + yMargin;
130
137
  moveY = targetTop - bounding.top;
131
- if (Math.abs(moveY) <= 1)
132
- moveY = 0;
133
- }
134
- else if (rect.top < bounding.top) {
135
- moveY = -(bounding.top - rect.top + ScrollSpace);
136
- if (side > 0 && rect.bottom > bounding.bottom + moveY)
137
- moveY = rect.bottom - bounding.bottom + moveY + ScrollSpace;
138
- }
139
- else if (rect.bottom > bounding.bottom) {
140
- moveY = rect.bottom - bounding.bottom + ScrollSpace;
141
- if (side < 0 && (rect.top - moveY) < bounding.top)
142
- moveY = -(bounding.top + moveY - rect.top + ScrollSpace);
143
138
  }
144
- if (rect.left < bounding.left) {
145
- moveX = -(bounding.left - rect.left + ScrollSpace);
146
- if (side > 0 && rect.right > bounding.right + moveX)
147
- moveX = rect.right - bounding.right + moveX + ScrollSpace;
139
+ if (x == "nearest") {
140
+ if (rect.left < bounding.left) {
141
+ moveX = -(bounding.left - rect.left + xMargin);
142
+ if (side > 0 && rect.right > bounding.right + moveX)
143
+ moveX = rect.right - bounding.right + moveX + xMargin;
144
+ }
145
+ else if (rect.right > bounding.right) {
146
+ moveX = rect.right - bounding.right + xMargin;
147
+ if (side < 0 && rect.left < bounding.left + moveX)
148
+ moveX = -(bounding.left + moveX - rect.left + xMargin);
149
+ }
148
150
  }
149
- else if (rect.right > bounding.right) {
150
- moveX = rect.right - bounding.right + ScrollSpace;
151
- if (side < 0 && rect.left < bounding.left + moveX)
152
- moveX = -(bounding.left + moveX - rect.left + ScrollSpace);
151
+ else {
152
+ let targetLeft = x == "center" ? rect.left + (rect.right - rect.left) / 2 - (bounding.right - bounding.left) / 2 :
153
+ (x == "start") == ltr ? rect.left - xMargin :
154
+ rect.right - (bounding.right - bounding.left) + xMargin;
155
+ moveX = targetLeft - bounding.left;
153
156
  }
154
157
  if (moveX || moveY) {
155
158
  if (top) {
@@ -173,7 +176,7 @@ function scrollRectIntoView(dom, rect, side, center) {
173
176
  if (top)
174
177
  break;
175
178
  cur = cur.assignedSlot || cur.parentNode;
176
- center = false;
179
+ x = y = "nearest";
177
180
  }
178
181
  else if (cur.nodeType == 11) { // A shadow root
179
182
  cur = cur.host;
@@ -260,6 +263,10 @@ function getRoot(node) {
260
263
  }
261
264
  return null;
262
265
  }
266
+ function clearAttributes(node) {
267
+ while (node.attributes.length)
268
+ node.removeAttributeNode(node.attributes[0]);
269
+ }
263
270
 
264
271
  class DOMPos {
265
272
  constructor(node, offset, precise = true) {
@@ -306,14 +313,16 @@ class ContentView {
306
313
  // given position.
307
314
  coordsAt(_pos, _side) { return null; }
308
315
  sync(track) {
309
- var _a;
310
316
  if (this.dirty & 2 /* Node */) {
311
317
  let parent = this.dom;
312
318
  let pos = parent.firstChild;
313
319
  for (let child of this.children) {
314
320
  if (child.dirty) {
315
- if (!child.dom && pos && !((_a = ContentView.get(pos)) === null || _a === void 0 ? void 0 : _a.parent))
316
- child.reuseDOM(pos);
321
+ if (!child.dom && pos) {
322
+ let contentView = ContentView.get(pos);
323
+ if (!contentView || !contentView.parent && contentView.constructor == child.constructor)
324
+ child.reuseDOM(pos);
325
+ }
317
326
  child.sync(track);
318
327
  child.dirty = 0 /* Not */;
319
328
  }
@@ -341,7 +350,7 @@ class ContentView {
341
350
  }
342
351
  }
343
352
  }
344
- reuseDOM(_dom) { return false; }
353
+ reuseDOM(_dom) { }
345
354
  localPosFromDOM(node, offset) {
346
355
  let after;
347
356
  if (node == this.dom) {
@@ -640,10 +649,8 @@ class TextView extends ContentView {
640
649
  }
641
650
  }
642
651
  reuseDOM(dom) {
643
- if (dom.nodeType != 3)
644
- return false;
645
- this.createDOM(dom);
646
- return true;
652
+ if (dom.nodeType == 3)
653
+ this.createDOM(dom);
647
654
  }
648
655
  merge(from, to, source) {
649
656
  if (source && (!(source instanceof TextView) || this.length - (to - from) + source.length > MaxJoinLen))
@@ -678,18 +685,26 @@ class MarkView extends ContentView {
678
685
  for (let ch of children)
679
686
  ch.setParent(this);
680
687
  }
681
- createDOM() {
682
- let dom = document.createElement(this.mark.tagName);
688
+ setAttrs(dom) {
689
+ clearAttributes(dom);
683
690
  if (this.mark.class)
684
691
  dom.className = this.mark.class;
685
692
  if (this.mark.attrs)
686
693
  for (let name in this.mark.attrs)
687
694
  dom.setAttribute(name, this.mark.attrs[name]);
688
- this.setDOM(dom);
695
+ return dom;
696
+ }
697
+ reuseDOM(node) {
698
+ if (node.nodeName == this.mark.tagName.toUpperCase()) {
699
+ this.setDOM(node);
700
+ this.dirty |= 4 /* Attrs */ | 2 /* Node */;
701
+ }
689
702
  }
690
703
  sync(track) {
691
- if (!this.dom || (this.dirty & 4 /* Attrs */))
692
- this.createDOM();
704
+ if (!this.dom)
705
+ this.setDOM(this.setAttrs(document.createElement(this.mark.tagName)));
706
+ else if (this.dirty & 4 /* Attrs */)
707
+ this.setAttrs(this.dom);
693
708
  super.sync(track);
694
709
  }
695
710
  merge(from, to, source, _hasStart, openStart, openEnd) {
@@ -833,8 +848,7 @@ class WidgetView extends ContentView {
833
848
  }
834
849
  class CompositionView extends WidgetView {
835
850
  domAtPos(pos) { return new DOMPos(this.widget.text, pos); }
836
- sync() { if (!this.dom)
837
- this.setDOM(this.widget.toDOM()); }
851
+ sync() { this.setDOM(this.widget.toDOM()); }
838
852
  localPosFromDOM(node, offset) {
839
853
  return !offset ? 0 : node.nodeType == 3 ? Math.min(offset, this.length) : this.length;
840
854
  }
@@ -1298,13 +1312,24 @@ class LineView extends ContentView {
1298
1312
  domAtPos(pos) {
1299
1313
  return inlineDOMAtPos(this.dom, this.children, pos);
1300
1314
  }
1315
+ reuseDOM(node) {
1316
+ if (node.nodeName == "DIV") {
1317
+ this.setDOM(node);
1318
+ this.dirty |= 4 /* Attrs */ | 2 /* Node */;
1319
+ }
1320
+ }
1301
1321
  sync(track) {
1302
1322
  var _a;
1303
- if (!this.dom || (this.dirty & 4 /* Attrs */)) {
1323
+ if (!this.dom) {
1304
1324
  this.setDOM(document.createElement("div"));
1305
1325
  this.dom.className = "cm-line";
1306
1326
  this.prevAttrs = this.attrs ? null : undefined;
1307
1327
  }
1328
+ else if (this.dirty & 4 /* Attrs */) {
1329
+ clearAttributes(this.dom);
1330
+ this.dom.className = "cm-line";
1331
+ this.prevAttrs = this.attrs ? null : undefined;
1332
+ }
1308
1333
  if (this.prevAttrs !== undefined) {
1309
1334
  updateAttrs(this.dom, this.prevAttrs, this.attrs);
1310
1335
  this.dom.classList.add("cm-line");
@@ -1576,12 +1601,27 @@ const mouseSelectionStyle = /*@__PURE__*/Facet.define();
1576
1601
  const exceptionSink = /*@__PURE__*/Facet.define();
1577
1602
  const updateListener = /*@__PURE__*/Facet.define();
1578
1603
  const inputHandler = /*@__PURE__*/Facet.define();
1604
+ // FIXME remove
1579
1605
  const scrollTo = /*@__PURE__*/StateEffect.define({
1580
1606
  map: (range, changes) => range.map(changes)
1581
1607
  });
1608
+ // FIXME remove
1582
1609
  const centerOn = /*@__PURE__*/StateEffect.define({
1583
1610
  map: (range, changes) => range.map(changes)
1584
1611
  });
1612
+ class ScrollTarget {
1613
+ constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5) {
1614
+ this.range = range;
1615
+ this.y = y;
1616
+ this.x = x;
1617
+ this.yMargin = yMargin;
1618
+ this.xMargin = xMargin;
1619
+ }
1620
+ map(changes) {
1621
+ return changes.empty ? this : new ScrollTarget(this.range.map(changes), this.y, this.x, this.yMargin, this.xMargin);
1622
+ }
1623
+ }
1624
+ const scrollIntoView = /*@__PURE__*/StateEffect.define({ map: (t, ch) => t.map(ch) });
1585
1625
  /**
1586
1626
  Log or report an unhandled exception in client code. Should
1587
1627
  probably only be used by extension code that allows client code to
@@ -2659,7 +2699,8 @@ class DocView extends ContentView {
2659
2699
  this.view.viewState.lineGapDeco
2660
2700
  ];
2661
2701
  }
2662
- scrollIntoView({ range, center }) {
2702
+ scrollIntoView(target) {
2703
+ let { range } = target;
2663
2704
  let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
2664
2705
  if (!rect)
2665
2706
  return;
@@ -2679,10 +2720,11 @@ class DocView extends ContentView {
2679
2720
  if (bottom != null)
2680
2721
  mBottom = Math.max(mBottom, bottom);
2681
2722
  }
2682
- scrollRectIntoView(this.view.scrollDOM, {
2723
+ let targetRect = {
2683
2724
  left: rect.left - mLeft, top: rect.top - mTop,
2684
2725
  right: rect.right + mRight, bottom: rect.bottom + mBottom
2685
- }, range.head < range.anchor ? -1 : 1, center);
2726
+ };
2727
+ scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, target.xMargin, target.yMargin, this.view.textDirection == Direction.LTR);
2686
2728
  }
2687
2729
  }
2688
2730
  function betweenUneditable(pos) {
@@ -2943,12 +2985,8 @@ function domPosInText(node, x, y) {
2943
2985
  function posAtCoords(view, { x, y }, precise, bias = -1) {
2944
2986
  var _a;
2945
2987
  let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop;
2946
- let block, yOffset = y - docTop, { docHeight } = view.viewState;
2947
- if (yOffset < 0 || yOffset > docHeight) {
2948
- if (precise)
2949
- return null;
2950
- yOffset = yOffset < 0 ? 0 : docHeight;
2951
- }
2988
+ let block, { docHeight } = view.viewState;
2989
+ let yOffset = Math.max(0, Math.min(y - docTop, docHeight));
2952
2990
  // Scan for a text block near the queried y position
2953
2991
  for (let halfLine = view.defaultLineHeight / 2, bounced = false;;) {
2954
2992
  block = view.elementAtHeight(yOffset);
@@ -2969,20 +3007,29 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
2969
3007
  }
2970
3008
  y = docTop + yOffset;
2971
3009
  let lineStart = block.from;
2972
- // Clip x to the viewport sides
2973
- x = Math.max(content.left + 1, Math.min(Math.max(content.right, content.left + view.docView.minWidth) - 1, x));
2974
3010
  // If this is outside of the rendered viewport, we can't determine a position
2975
3011
  if (lineStart < view.viewport.from)
2976
- return view.viewport.from == 0 ? 0 : posAtCoordsImprecise(view, content, block, x, y);
3012
+ return view.viewport.from == 0 ? 0 : precise ? posAtCoordsImprecise(view, content, block, x, y) : null;
2977
3013
  if (lineStart > view.viewport.to)
2978
- return view.viewport.to == view.state.doc.length ? view.state.doc.length : posAtCoordsImprecise(view, content, block, x, y);
3014
+ return view.viewport.to == view.state.doc.length ? view.state.doc.length :
3015
+ precise ? posAtCoordsImprecise(view, content, block, x, y) : null;
2979
3016
  // Prefer ShadowRootOrDocument.elementFromPoint if present, fall back to document if not
2980
3017
  let doc = view.dom.ownerDocument;
2981
- let element = (view.root.elementFromPoint ? view.root : doc).elementFromPoint(x, y);
3018
+ let root = view.root.elementFromPoint ? view.root : doc;
3019
+ let element = root.elementFromPoint(x, y);
3020
+ if (element && !view.contentDOM.contains(element))
3021
+ element = null;
3022
+ // If the element is unexpected, clip x at the sides of the content area and try again
3023
+ if (!element) {
3024
+ x = Math.max(content.left + 1, Math.min(content.right - 1, x));
3025
+ element = root.elementFromPoint(x, y);
3026
+ if (element && !view.contentDOM.contains(element))
3027
+ element = null;
3028
+ }
2982
3029
  // There's visible editor content under the point, so we can try
2983
3030
  // using caret(Position|Range)FromPoint as a shortcut
2984
3031
  let node, offset = -1;
2985
- if (element && view.contentDOM.contains(element) && ((_a = view.docView.nearest(element)) === null || _a === void 0 ? void 0 : _a.isEditable) != false) {
3032
+ if (element && ((_a = view.docView.nearest(element)) === null || _a === void 0 ? void 0 : _a.isEditable) != false) {
2986
3033
  if (doc.caretPositionFromPoint) {
2987
3034
  let pos = doc.caretPositionFromPoint(x, y);
2988
3035
  if (pos)
@@ -3194,7 +3241,7 @@ class InputState {
3194
3241
  let handler = set.handlers[type];
3195
3242
  if (handler) {
3196
3243
  try {
3197
- if (handler.call(set.plugin, event, view))
3244
+ if (handler.call(set.plugin, event, view) || event.defaultPrevented)
3198
3245
  return true;
3199
3246
  }
3200
3247
  catch (e) {
@@ -3463,7 +3510,7 @@ handlers.touchmove = view => {
3463
3510
  };
3464
3511
  handlers.mousedown = (view, event) => {
3465
3512
  view.observer.flush();
3466
- if (lastTouch > Date.now() - 2000)
3513
+ if (lastTouch > Date.now() - 2000 && getClickType(event) == 1)
3467
3514
  return; // Ignore touch interaction
3468
3515
  let style = null;
3469
3516
  for (let makeStyle of view.state.facet(mouseSelectionStyle)) {
@@ -3585,9 +3632,9 @@ handlers.dragstart = (view, event) => {
3585
3632
  }
3586
3633
  };
3587
3634
  function dropText(view, event, text, direct) {
3588
- let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY });
3589
- if (dropPos == null || !text)
3635
+ if (!text)
3590
3636
  return;
3637
+ let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
3591
3638
  event.preventDefault();
3592
3639
  let { mouseSelection } = view.inputState;
3593
3640
  let del = direct && mouseSelection && mouseSelection.dragging && mouseSelection.dragMove ?
@@ -4515,15 +4562,6 @@ class LineGapWidget extends WidgetType {
4515
4562
  }
4516
4563
  get estimatedHeight() { return this.vertical ? this.size : -1; }
4517
4564
  }
4518
- class ScrollTarget {
4519
- constructor(range, center = false) {
4520
- this.range = range;
4521
- this.center = center;
4522
- }
4523
- map(changes) {
4524
- return changes.empty ? this : new ScrollTarget(this.range.map(changes), this.center);
4525
- }
4526
- }
4527
4565
  class ViewState {
4528
4566
  constructor(state) {
4529
4567
  this.state = state;
@@ -4598,9 +4636,9 @@ class ViewState {
4598
4636
  let updateLines = !update.changes.empty || (update.flags & 2 /* Height */) ||
4599
4637
  viewport.from != this.viewport.from || viewport.to != this.viewport.to;
4600
4638
  this.viewport = viewport;
4639
+ this.updateForViewport();
4601
4640
  if (updateLines)
4602
4641
  this.updateViewportLines();
4603
- this.updateForViewport();
4604
4642
  if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
4605
4643
  this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps, update.changes)));
4606
4644
  update.flags |= this.computeVisibleRanges();
@@ -4632,7 +4670,12 @@ class ViewState {
4632
4670
  let pixelViewport = this.printing ? { top: -1e8, bottom: 1e8, left: -1e8, right: 1e8 } : visiblePixelRange(dom, this.paddingTop);
4633
4671
  let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
4634
4672
  this.pixelViewport = pixelViewport;
4635
- this.inView = this.pixelViewport.bottom > this.pixelViewport.top && this.pixelViewport.right > this.pixelViewport.left;
4673
+ let inView = this.pixelViewport.bottom > this.pixelViewport.top && this.pixelViewport.right > this.pixelViewport.left;
4674
+ if (inView != this.inView) {
4675
+ this.inView = inView;
4676
+ if (inView)
4677
+ measureContent = true;
4678
+ }
4636
4679
  if (!this.inView)
4637
4680
  return 0;
4638
4681
  if (measureContent) {
@@ -4669,9 +4712,9 @@ class ViewState {
4669
4712
  this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to);
4670
4713
  if (viewportChange)
4671
4714
  this.viewport = this.getViewport(bias, this.scrollTarget);
4715
+ this.updateForViewport();
4672
4716
  if ((result & 2 /* Height */) || viewportChange)
4673
4717
  this.updateViewportLines();
4674
- this.updateForViewport();
4675
4718
  if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
4676
4719
  this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps));
4677
4720
  result |= this.computeVisibleRanges();
@@ -4699,9 +4742,9 @@ class ViewState {
4699
4742
  let { head } = scrollTarget.range, viewHeight = this.editorHeight;
4700
4743
  if (head < viewport.from || head > viewport.to) {
4701
4744
  let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
4702
- if (scrollTarget.center)
4745
+ if (scrollTarget.y == "center")
4703
4746
  topPos = (block.top + block.bottom) / 2 - viewHeight / 2;
4704
- else if (head < viewport.from)
4747
+ else if (scrollTarget.y == "start" || scrollTarget.y == "nearest" && head < viewport.from)
4705
4748
  topPos = block.top;
4706
4749
  else
4707
4750
  topPos = block.bottom - viewHeight;
@@ -5207,7 +5250,7 @@ class DOMObserver {
5207
5250
  this.intersection = new IntersectionObserver(entries => {
5208
5251
  if (this.parentCheck < 0)
5209
5252
  this.parentCheck = setTimeout(this.listenForScroll.bind(this), 1000);
5210
- if (entries.length > 0 && entries[entries.length - 1].intersectionRatio > 0 != this.intersecting) {
5253
+ if (entries.length > 0 && (entries[entries.length - 1].intersectionRatio > 0) != this.intersecting) {
5211
5254
  this.intersecting = !this.intersecting;
5212
5255
  if (this.intersecting != this.view.inView)
5213
5256
  this.onScrollChanged(document.createEvent("Event"));
@@ -5248,8 +5291,10 @@ class DOMObserver {
5248
5291
  // Deletions on IE11 fire their events in the wrong order, giving
5249
5292
  // us a selection change event before the DOM changes are
5250
5293
  // reported.
5251
- // (Selection.isCollapsed isn't reliable on IE)
5252
- if (browser.ie && browser.ie_version <= 11 && !view.state.selection.main.empty &&
5294
+ // Chrome Android has a similar issue when backspacing out a
5295
+ // selection (#645).
5296
+ if ((browser.ie && browser.ie_version <= 11 || browser.android && browser.chrome) && !view.state.selection.main.empty &&
5297
+ // (Selection.isCollapsed isn't reliable on IE)
5253
5298
  sel.focusNode && isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset))
5254
5299
  this.flushSoon();
5255
5300
  else
@@ -5807,7 +5852,9 @@ class EditorView {
5807
5852
  if (e.is(scrollTo))
5808
5853
  scrollTarget = new ScrollTarget(e.value);
5809
5854
  else if (e.is(centerOn))
5810
- scrollTarget = new ScrollTarget(e.value, true);
5855
+ scrollTarget = new ScrollTarget(e.value, "center");
5856
+ else if (e.is(scrollIntoView))
5857
+ scrollTarget = e.value;
5811
5858
  }
5812
5859
  }
5813
5860
  this.viewState.update(update, scrollTarget);
@@ -6375,6 +6422,14 @@ class EditorView {
6375
6422
  this.destroyed = true;
6376
6423
  }
6377
6424
  /**
6425
+ Returns an effect that can be
6426
+ [added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a transaction to
6427
+ cause it to scroll the given position or range into view.
6428
+ */
6429
+ static scrollIntoView(pos, options = {}) {
6430
+ return scrollIntoView.of(new ScrollTarget(typeof pos == "number" ? EditorSelection.cursor(pos) : pos, options.y, options.x, options.yMargin, options.xMargin));
6431
+ }
6432
+ /**
6378
6433
  Facet that can be used to add DOM event handlers. The value
6379
6434
  should be an object mapping event names to handler functions. The
6380
6435
  first such function to return true will be assumed to have handled
@@ -6428,11 +6483,15 @@ class EditorView {
6428
6483
  /**
6429
6484
  Effect that can be [added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a
6430
6485
  transaction to make it scroll the given range into view.
6486
+
6487
+ *Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead.
6431
6488
  */
6432
6489
  EditorView.scrollTo = scrollTo;
6433
6490
  /**
6434
6491
  Effect that makes the editor scroll the given range to the
6435
6492
  center of the visible view.
6493
+
6494
+ *Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead.
6436
6495
  */
6437
6496
  EditorView.centerOn = centerOn;
6438
6497
  /**
@@ -6901,7 +6960,7 @@ function measureRange(view, range) {
6901
6960
  let ltr = view.textDirection == Direction.LTR;
6902
6961
  let content = view.contentDOM, contentRect = content.getBoundingClientRect(), base = getBase(view);
6903
6962
  let lineStyle = window.getComputedStyle(content.firstChild);
6904
- let leftSide = contentRect.left + parseInt(lineStyle.paddingLeft);
6963
+ let leftSide = contentRect.left + parseInt(lineStyle.paddingLeft) + Math.min(0, parseInt(lineStyle.textIndent));
6905
6964
  let rightSide = contentRect.right - parseInt(lineStyle.paddingRight);
6906
6965
  let startBlock = blockAt(view, from), endBlock = blockAt(view, to);
6907
6966
  let visualStart = startBlock.type == BlockType.Text ? startBlock : null;
@@ -7027,7 +7086,11 @@ const drawDropCursor = /*@__PURE__*/ViewPlugin.fromClass(class {
7027
7086
  if (!rect)
7028
7087
  return null;
7029
7088
  let outer = this.view.scrollDOM.getBoundingClientRect();
7030
- return { left: rect.left - outer.left, top: rect.top - outer.top, height: rect.bottom - rect.top };
7089
+ return {
7090
+ left: rect.left - outer.left + this.view.scrollDOM.scrollLeft,
7091
+ top: rect.top - outer.top + this.view.scrollDOM.scrollTop,
7092
+ height: rect.bottom - rect.top
7093
+ };
7031
7094
  }
7032
7095
  drawCursor(pos) {
7033
7096
  if (this.cursor) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "0.19.31",
3
+ "version": "0.19.35",
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/rangeset": "^0.19.0",
29
+ "@codemirror/rangeset": "^0.19.4",
30
30
  "@codemirror/state": "^0.19.3",
31
31
  "@codemirror/text": "^0.19.0",
32
32
  "style-mod": "^4.0.0",