@codemirror/view 0.19.34 → 0.19.38

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,39 @@
1
+ ## 0.19.38 (2022-01-05)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix a bug that caused line decorations with a `class` property to suppress all other line decorations for that line.
6
+
7
+ Fix a bug that caused scroll effects to be corrupted when further updates came in before they were applied.
8
+
9
+ Fix an issue where, depending on which way a floating point rounding error fell, `posAtCoords` (and thus vertical cursor motion) for positions outside of the vertical range of the document might or might not return the start/end of the document.
10
+
11
+ ## 0.19.37 (2021-12-22)
12
+
13
+ ### Bug fixes
14
+
15
+ Fix regression where plugin replacing decorations that span to the end of the line are ignored.
16
+
17
+ ## 0.19.36 (2021-12-22)
18
+
19
+ ### Bug fixes
20
+
21
+ Fix a crash in `posAtCoords` when the position lies in a block widget that is rendered but scrolled out of view.
22
+
23
+ Adding block decorations from a plugin now raises an error. Replacing decorations that cross lines are ignored, when provided by a plugin.
24
+
25
+ Fix inverted interpretation of the `precise` argument to `posAtCoords`.
26
+
27
+ ## 0.19.35 (2021-12-20)
28
+
29
+ ### Bug fixes
30
+
31
+ 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).
32
+
33
+ Fix an issue where backspacing out a selection on Chrome Android would sometimes only delete the last character due to event order issues.
34
+
35
+ `posAtCoords`, without second argument, will no longer return null for positions below or above the document.
36
+
1
37
  ## 0.19.34 (2021-12-17)
2
38
 
3
39
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -1311,7 +1311,7 @@ class LineView extends ContentView {
1311
1311
  if (attrs)
1312
1312
  this.attrs = combineAttrs(attrs, this.attrs || {});
1313
1313
  if (cls)
1314
- this.attrs = combineAttrs(attrs, { class: cls });
1314
+ this.attrs = combineAttrs({ class: cls }, this.attrs || {});
1315
1315
  }
1316
1316
  domAtPos(pos) {
1317
1317
  return inlineDOMAtPos(this.dom, this.children, pos);
@@ -1372,16 +1372,17 @@ class LineView extends ContentView {
1372
1372
  become(_other) { return false; }
1373
1373
  get type() { return exports.BlockType.Text; }
1374
1374
  static find(docView, pos) {
1375
- for (let i = 0, off = 0;; i++) {
1375
+ for (let i = 0, off = 0; i < docView.children.length; i++) {
1376
1376
  let block = docView.children[i], end = off + block.length;
1377
1377
  if (end >= pos) {
1378
1378
  if (block instanceof LineView)
1379
1379
  return block;
1380
- if (block.length)
1381
- return null;
1380
+ if (end > pos)
1381
+ break;
1382
1382
  }
1383
1383
  off = end + block.breakAfter;
1384
1384
  }
1385
+ return null;
1385
1386
  }
1386
1387
  }
1387
1388
  class BlockWidgetView extends ContentView {
@@ -1442,10 +1443,11 @@ class BlockWidgetView extends ContentView {
1442
1443
  }
1443
1444
 
1444
1445
  class ContentBuilder {
1445
- constructor(doc, pos, end) {
1446
+ constructor(doc, pos, end, disallowBlockEffectsBelow) {
1446
1447
  this.doc = doc;
1447
1448
  this.pos = pos;
1448
1449
  this.end = end;
1450
+ this.disallowBlockEffectsBelow = disallowBlockEffectsBelow;
1449
1451
  this.content = [];
1450
1452
  this.curLine = null;
1451
1453
  this.breakAtStart = 0;
@@ -1574,8 +1576,15 @@ class ContentBuilder {
1574
1576
  if (this.openStart < 0)
1575
1577
  this.openStart = openStart;
1576
1578
  }
1577
- static build(text, from, to, decorations) {
1578
- let builder = new ContentBuilder(text, from, to);
1579
+ filterPoint(from, to, value, index) {
1580
+ if (index >= this.disallowBlockEffectsBelow || !(value instanceof PointDecoration))
1581
+ return true;
1582
+ if (value.block)
1583
+ throw new RangeError("Block decorations may not be specified via plugins");
1584
+ return to <= this.doc.lineAt(this.pos).to;
1585
+ }
1586
+ static build(text, from, to, decorations, pluginDecorationLength) {
1587
+ let builder = new ContentBuilder(text, from, to, pluginDecorationLength);
1579
1588
  builder.openEnd = rangeset.RangeSet.spans(decorations, from, to, builder);
1580
1589
  if (builder.openStart < 0)
1581
1590
  builder.openStart = builder.openEnd;
@@ -1698,11 +1707,13 @@ This field can be used by plugins to provide
1698
1707
  **Note**: For reasons of data flow (plugins are only updated
1699
1708
  after the viewport is computed), decorations produced by plugins
1700
1709
  are _not_ taken into account when predicting the vertical layout
1701
- structure of the editor. Thus, things like large widgets or big
1702
- replacements (i.e. code folding) should be provided through the
1703
- state-level [`decorations` facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations),
1704
- not this plugin field. Specifically, replacing decorations that
1705
- cross line boundaries will break if provided through a plugin.
1710
+ structure of the editor. They **must not** introduce block
1711
+ widgets (that will raise an error) or replacing decorations that
1712
+ cover line breaks (these will be ignored if they occur). Such
1713
+ decorations, or others that cause a large amount of vertical
1714
+ size shift compared to the undecorated content, should be
1715
+ provided through the state-level [`decorations`
1716
+ facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations) instead.
1706
1717
  */
1707
1718
  PluginField.decorations = PluginField.define();
1708
1719
  /**
@@ -1935,8 +1946,6 @@ class ViewUpdate {
1935
1946
  view.inputState.notifiedFocused = focus;
1936
1947
  this.flags |= 1 /* Focus */;
1937
1948
  }
1938
- if (this.docChanged)
1939
- this.flags |= 2 /* Height */;
1940
1949
  }
1941
1950
  /**
1942
1951
  Tells you whether the [viewport](https://codemirror.net/6/docs/ref/#view.EditorView.viewport) or
@@ -1947,14 +1956,15 @@ class ViewUpdate {
1947
1956
  return (this.flags & 4 /* Viewport */) > 0;
1948
1957
  }
1949
1958
  /**
1950
- Indicates whether the line height in the editor changed in this update.
1959
+ Indicates whether the height of an element in the editor changed
1960
+ in this update.
1951
1961
  */
1952
1962
  get heightChanged() {
1953
1963
  return (this.flags & 2 /* Height */) > 0;
1954
1964
  }
1955
1965
  /**
1956
- Returns true when the document changed or the size of the editor
1957
- or the lines or characters within it has changed.
1966
+ Returns true when the document was modified or the size of the
1967
+ editor, or elements within the editor, changed.
1958
1968
  */
1959
1969
  get geometryChanged() {
1960
1970
  return this.docChanged || (this.flags & (8 /* Geometry */ | 2 /* Height */)) > 0;
@@ -2373,6 +2383,7 @@ class DocView extends ContentView {
2373
2383
  this.view = view;
2374
2384
  this.compositionDeco = Decoration.none;
2375
2385
  this.decorations = [];
2386
+ this.pluginDecorationLength = 0;
2376
2387
  // Track a minimum width for the editor. When measuring sizes in
2377
2388
  // measureVisibleLineHeights, this is updated to point at the width
2378
2389
  // of a given element and its extent in the document. When a change
@@ -2394,7 +2405,8 @@ class DocView extends ContentView {
2394
2405
  this.setDOM(view.contentDOM);
2395
2406
  this.children = [new LineView];
2396
2407
  this.children[0].setParent(this);
2397
- this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], this.updateDeco(), 0);
2408
+ this.updateDeco();
2409
+ this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], 0);
2398
2410
  }
2399
2411
  get root() { return this.view.root; }
2400
2412
  get editorView() { return this.view; }
@@ -2433,7 +2445,7 @@ class DocView extends ContentView {
2433
2445
  return false;
2434
2446
  }
2435
2447
  else {
2436
- this.updateInner(changedRanges, deco, update.startState.doc.length);
2448
+ this.updateInner(changedRanges, update.startState.doc.length);
2437
2449
  if (update.transactions.length)
2438
2450
  this.lastUpdate = Date.now();
2439
2451
  return true;
@@ -2441,9 +2453,9 @@ class DocView extends ContentView {
2441
2453
  }
2442
2454
  // Used by update and the constructor do perform the actual DOM
2443
2455
  // update
2444
- updateInner(changes, deco, oldLength) {
2456
+ updateInner(changes, oldLength) {
2445
2457
  this.view.viewState.mustMeasureContent = true;
2446
- this.updateChildren(changes, deco, oldLength);
2458
+ this.updateChildren(changes, oldLength);
2447
2459
  let { observer } = this.view;
2448
2460
  observer.ignore(() => {
2449
2461
  // Lock the height during redrawing, since Chrome sometimes
@@ -2470,14 +2482,14 @@ class DocView extends ContentView {
2470
2482
  gaps.push(child.dom);
2471
2483
  observer.updateGaps(gaps);
2472
2484
  }
2473
- updateChildren(changes, deco, oldLength) {
2485
+ updateChildren(changes, oldLength) {
2474
2486
  let cursor = this.childCursor(oldLength);
2475
2487
  for (let i = changes.length - 1;; i--) {
2476
2488
  let next = i >= 0 ? changes[i] : null;
2477
2489
  if (!next)
2478
2490
  break;
2479
2491
  let { fromA, toA, fromB, toB } = next;
2480
- let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, deco);
2492
+ let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, this.decorations, this.pluginDecorationLength);
2481
2493
  let { i: toI, off: toOff } = cursor.findPos(toA, 1);
2482
2494
  let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
2483
2495
  replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
@@ -2696,8 +2708,10 @@ class DocView extends ContentView {
2696
2708
  return Decoration.set(deco);
2697
2709
  }
2698
2710
  updateDeco() {
2711
+ let pluginDecorations = this.view.pluginField(PluginField.decorations);
2712
+ this.pluginDecorationLength = pluginDecorations.length;
2699
2713
  return this.decorations = [
2700
- ...this.view.pluginField(PluginField.decorations),
2714
+ ...pluginDecorations,
2701
2715
  ...this.view.state.facet(decorations),
2702
2716
  this.compositionDeco,
2703
2717
  this.computeBlockGapDeco(),
@@ -2990,12 +3004,12 @@ function domPosInText(node, x, y) {
2990
3004
  function posAtCoords(view, { x, y }, precise, bias = -1) {
2991
3005
  var _a;
2992
3006
  let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop;
2993
- let block, yOffset = y - docTop, { docHeight } = view.viewState;
2994
- if (yOffset < 0 || yOffset > docHeight) {
2995
- if (precise)
2996
- return null;
2997
- yOffset = yOffset < 0 ? 0 : docHeight;
2998
- }
3007
+ let block, { docHeight } = view.viewState;
3008
+ let yOffset = y - docTop;
3009
+ if (yOffset < 0)
3010
+ return 0;
3011
+ if (yOffset > docHeight)
3012
+ return view.state.doc.length;
2999
3013
  // Scan for a text block near the queried y position
3000
3014
  for (let halfLine = view.defaultLineHeight / 2, bounced = false;;) {
3001
3015
  block = view.elementAtHeight(yOffset);
@@ -3016,20 +3030,29 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
3016
3030
  }
3017
3031
  y = docTop + yOffset;
3018
3032
  let lineStart = block.from;
3019
- // Clip x to the viewport sides
3020
- x = Math.max(content.left + 1, Math.min(Math.max(content.right, content.left + view.docView.minWidth) - 1, x));
3021
3033
  // If this is outside of the rendered viewport, we can't determine a position
3022
3034
  if (lineStart < view.viewport.from)
3023
- return view.viewport.from == 0 ? 0 : posAtCoordsImprecise(view, content, block, x, y);
3035
+ return view.viewport.from == 0 ? 0 : precise ? null : posAtCoordsImprecise(view, content, block, x, y);
3024
3036
  if (lineStart > view.viewport.to)
3025
- return view.viewport.to == view.state.doc.length ? view.state.doc.length : posAtCoordsImprecise(view, content, block, x, y);
3037
+ return view.viewport.to == view.state.doc.length ? view.state.doc.length :
3038
+ precise ? null : posAtCoordsImprecise(view, content, block, x, y);
3026
3039
  // Prefer ShadowRootOrDocument.elementFromPoint if present, fall back to document if not
3027
3040
  let doc = view.dom.ownerDocument;
3028
- let element = (view.root.elementFromPoint ? view.root : doc).elementFromPoint(x, y);
3041
+ let root = view.root.elementFromPoint ? view.root : doc;
3042
+ let element = root.elementFromPoint(x, y);
3043
+ if (element && !view.contentDOM.contains(element))
3044
+ element = null;
3045
+ // If the element is unexpected, clip x at the sides of the content area and try again
3046
+ if (!element) {
3047
+ x = Math.max(content.left + 1, Math.min(content.right - 1, x));
3048
+ element = root.elementFromPoint(x, y);
3049
+ if (element && !view.contentDOM.contains(element))
3050
+ element = null;
3051
+ }
3029
3052
  // There's visible editor content under the point, so we can try
3030
3053
  // using caret(Position|Range)FromPoint as a shortcut
3031
3054
  let node, offset = -1;
3032
- if (element && view.contentDOM.contains(element) && ((_a = view.docView.nearest(element)) === null || _a === void 0 ? void 0 : _a.isEditable) != false) {
3055
+ if (element && ((_a = view.docView.nearest(element)) === null || _a === void 0 ? void 0 : _a.isEditable) != false) {
3033
3056
  if (doc.caretPositionFromPoint) {
3034
3057
  let pos = doc.caretPositionFromPoint(x, y);
3035
3058
  if (pos)
@@ -3047,6 +3070,8 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
3047
3070
  // No luck, do our own (potentially expensive) search
3048
3071
  if (!node || !view.docView.dom.contains(node)) {
3049
3072
  let line = LineView.find(view.docView, lineStart);
3073
+ if (!line)
3074
+ return yOffset > block.top + block.height / 2 ? block.to : block.from;
3050
3075
  ({ node, offset } = domPosAtCoords(line.dom, x, y));
3051
3076
  }
3052
3077
  return view.docView.posFromDOM(node, offset);
@@ -3510,7 +3535,7 @@ handlers.touchmove = view => {
3510
3535
  };
3511
3536
  handlers.mousedown = (view, event) => {
3512
3537
  view.observer.flush();
3513
- if (lastTouch > Date.now() - 2000)
3538
+ if (lastTouch > Date.now() - 2000 && getClickType(event) == 1)
3514
3539
  return; // Ignore touch interaction
3515
3540
  let style = null;
3516
3541
  for (let makeStyle of view.state.facet(mouseSelectionStyle)) {
@@ -3632,9 +3657,9 @@ handlers.dragstart = (view, event) => {
3632
3657
  }
3633
3658
  };
3634
3659
  function dropText(view, event, text, direct) {
3635
- let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY });
3636
- if (dropPos == null || !text)
3660
+ if (!text)
3637
3661
  return;
3662
+ let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
3638
3663
  event.preventDefault();
3639
3664
  let { mouseSelection } = view.inputState;
3640
3665
  let del = direct && mouseSelection && mouseSelection.dragging && mouseSelection.dragMove ?
@@ -4745,7 +4770,7 @@ class ViewState {
4745
4770
  let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
4746
4771
  if (scrollTarget.y == "center")
4747
4772
  topPos = (block.top + block.bottom) / 2 - viewHeight / 2;
4748
- else if (scrollTarget.y == "start" || head < viewport.from)
4773
+ else if (scrollTarget.y == "start" || scrollTarget.y == "nearest" && head < viewport.from)
4749
4774
  topPos = block.top;
4750
4775
  else
4751
4776
  topPos = block.bottom - viewHeight;
@@ -5292,8 +5317,10 @@ class DOMObserver {
5292
5317
  // Deletions on IE11 fire their events in the wrong order, giving
5293
5318
  // us a selection change event before the DOM changes are
5294
5319
  // reported.
5295
- // (Selection.isCollapsed isn't reliable on IE)
5296
- if (browser.ie && browser.ie_version <= 11 && !view.state.selection.main.empty &&
5320
+ // Chrome Android has a similar issue when backspacing out a
5321
+ // selection (#645).
5322
+ if ((browser.ie && browser.ie_version <= 11 || browser.android && browser.chrome) && !view.state.selection.main.empty &&
5323
+ // (Selection.isCollapsed isn't reliable on IE)
5297
5324
  sel.focusNode && isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset))
5298
5325
  this.flushSoon();
5299
5326
  else
@@ -5837,7 +5864,7 @@ class EditorView {
5837
5864
  if (state$1.facet(state.EditorState.phrases) != this.state.facet(state.EditorState.phrases))
5838
5865
  return this.setState(state$1);
5839
5866
  update = new ViewUpdate(this, state$1, transactions);
5840
- let scrollTarget = null;
5867
+ let scrollTarget = this.viewState.scrollTarget;
5841
5868
  try {
5842
5869
  this.updateState = 2 /* Updating */;
5843
5870
  for (let tr of transactions) {
package/dist/index.d.ts CHANGED
@@ -340,11 +340,13 @@ declare class PluginField<T> {
340
340
  **Note**: For reasons of data flow (plugins are only updated
341
341
  after the viewport is computed), decorations produced by plugins
342
342
  are _not_ taken into account when predicting the vertical layout
343
- structure of the editor. Thus, things like large widgets or big
344
- replacements (i.e. code folding) should be provided through the
345
- state-level [`decorations` facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations),
346
- not this plugin field. Specifically, replacing decorations that
347
- cross line boundaries will break if provided through a plugin.
343
+ structure of the editor. They **must not** introduce block
344
+ widgets (that will raise an error) or replacing decorations that
345
+ cover line breaks (these will be ignored if they occur). Such
346
+ decorations, or others that cause a large amount of vertical
347
+ size shift compared to the undecorated content, should be
348
+ provided through the state-level [`decorations`
349
+ facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations) instead.
348
350
  */
349
351
  static decorations: PluginField<DecorationSet>;
350
352
  /**
@@ -470,12 +472,13 @@ declare class ViewUpdate {
470
472
  */
471
473
  get viewportChanged(): boolean;
472
474
  /**
473
- Indicates whether the line height in the editor changed in this update.
475
+ Indicates whether the height of an element in the editor changed
476
+ in this update.
474
477
  */
475
478
  get heightChanged(): boolean;
476
479
  /**
477
- Returns true when the document changed or the size of the editor
478
- or the lines or characters within it has changed.
480
+ Returns true when the document was modified or the size of the
481
+ editor, or elements within the editor, changed.
479
482
  */
480
483
  get geometryChanged(): boolean;
481
484
  /**
@@ -937,8 +940,11 @@ declare class EditorView {
937
940
  */
938
941
  posAtDOM(node: Node, offset?: number): number;
939
942
  /**
940
- Get the document position at the given screen coordinates.
941
- Returns null if no valid position could be found.
943
+ Get the document position at the given screen coordinates. For
944
+ positions not covered by the visible viewport's DOM structure,
945
+ this will return null, unless `false` is passed as second
946
+ argument, in which case it'll return an estimated position that
947
+ would be near the coordinates if it were rendered.
942
948
  */
943
949
  posAtCoords(coords: {
944
950
  x: number;
package/dist/index.js CHANGED
@@ -1307,7 +1307,7 @@ class LineView extends ContentView {
1307
1307
  if (attrs)
1308
1308
  this.attrs = combineAttrs(attrs, this.attrs || {});
1309
1309
  if (cls)
1310
- this.attrs = combineAttrs(attrs, { class: cls });
1310
+ this.attrs = combineAttrs({ class: cls }, this.attrs || {});
1311
1311
  }
1312
1312
  domAtPos(pos) {
1313
1313
  return inlineDOMAtPos(this.dom, this.children, pos);
@@ -1368,16 +1368,17 @@ class LineView extends ContentView {
1368
1368
  become(_other) { return false; }
1369
1369
  get type() { return BlockType.Text; }
1370
1370
  static find(docView, pos) {
1371
- for (let i = 0, off = 0;; i++) {
1371
+ for (let i = 0, off = 0; i < docView.children.length; i++) {
1372
1372
  let block = docView.children[i], end = off + block.length;
1373
1373
  if (end >= pos) {
1374
1374
  if (block instanceof LineView)
1375
1375
  return block;
1376
- if (block.length)
1377
- return null;
1376
+ if (end > pos)
1377
+ break;
1378
1378
  }
1379
1379
  off = end + block.breakAfter;
1380
1380
  }
1381
+ return null;
1381
1382
  }
1382
1383
  }
1383
1384
  class BlockWidgetView extends ContentView {
@@ -1438,10 +1439,11 @@ class BlockWidgetView extends ContentView {
1438
1439
  }
1439
1440
 
1440
1441
  class ContentBuilder {
1441
- constructor(doc, pos, end) {
1442
+ constructor(doc, pos, end, disallowBlockEffectsBelow) {
1442
1443
  this.doc = doc;
1443
1444
  this.pos = pos;
1444
1445
  this.end = end;
1446
+ this.disallowBlockEffectsBelow = disallowBlockEffectsBelow;
1445
1447
  this.content = [];
1446
1448
  this.curLine = null;
1447
1449
  this.breakAtStart = 0;
@@ -1570,8 +1572,15 @@ class ContentBuilder {
1570
1572
  if (this.openStart < 0)
1571
1573
  this.openStart = openStart;
1572
1574
  }
1573
- static build(text, from, to, decorations) {
1574
- let builder = new ContentBuilder(text, from, to);
1575
+ filterPoint(from, to, value, index) {
1576
+ if (index >= this.disallowBlockEffectsBelow || !(value instanceof PointDecoration))
1577
+ return true;
1578
+ if (value.block)
1579
+ throw new RangeError("Block decorations may not be specified via plugins");
1580
+ return to <= this.doc.lineAt(this.pos).to;
1581
+ }
1582
+ static build(text, from, to, decorations, pluginDecorationLength) {
1583
+ let builder = new ContentBuilder(text, from, to, pluginDecorationLength);
1575
1584
  builder.openEnd = RangeSet.spans(decorations, from, to, builder);
1576
1585
  if (builder.openStart < 0)
1577
1586
  builder.openStart = builder.openEnd;
@@ -1694,11 +1703,13 @@ This field can be used by plugins to provide
1694
1703
  **Note**: For reasons of data flow (plugins are only updated
1695
1704
  after the viewport is computed), decorations produced by plugins
1696
1705
  are _not_ taken into account when predicting the vertical layout
1697
- structure of the editor. Thus, things like large widgets or big
1698
- replacements (i.e. code folding) should be provided through the
1699
- state-level [`decorations` facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations),
1700
- not this plugin field. Specifically, replacing decorations that
1701
- cross line boundaries will break if provided through a plugin.
1706
+ structure of the editor. They **must not** introduce block
1707
+ widgets (that will raise an error) or replacing decorations that
1708
+ cover line breaks (these will be ignored if they occur). Such
1709
+ decorations, or others that cause a large amount of vertical
1710
+ size shift compared to the undecorated content, should be
1711
+ provided through the state-level [`decorations`
1712
+ facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations) instead.
1702
1713
  */
1703
1714
  PluginField.decorations = /*@__PURE__*/PluginField.define();
1704
1715
  /**
@@ -1931,8 +1942,6 @@ class ViewUpdate {
1931
1942
  view.inputState.notifiedFocused = focus;
1932
1943
  this.flags |= 1 /* Focus */;
1933
1944
  }
1934
- if (this.docChanged)
1935
- this.flags |= 2 /* Height */;
1936
1945
  }
1937
1946
  /**
1938
1947
  Tells you whether the [viewport](https://codemirror.net/6/docs/ref/#view.EditorView.viewport) or
@@ -1943,14 +1952,15 @@ class ViewUpdate {
1943
1952
  return (this.flags & 4 /* Viewport */) > 0;
1944
1953
  }
1945
1954
  /**
1946
- Indicates whether the line height in the editor changed in this update.
1955
+ Indicates whether the height of an element in the editor changed
1956
+ in this update.
1947
1957
  */
1948
1958
  get heightChanged() {
1949
1959
  return (this.flags & 2 /* Height */) > 0;
1950
1960
  }
1951
1961
  /**
1952
- Returns true when the document changed or the size of the editor
1953
- or the lines or characters within it has changed.
1962
+ Returns true when the document was modified or the size of the
1963
+ editor, or elements within the editor, changed.
1954
1964
  */
1955
1965
  get geometryChanged() {
1956
1966
  return this.docChanged || (this.flags & (8 /* Geometry */ | 2 /* Height */)) > 0;
@@ -2368,6 +2378,7 @@ class DocView extends ContentView {
2368
2378
  this.view = view;
2369
2379
  this.compositionDeco = Decoration.none;
2370
2380
  this.decorations = [];
2381
+ this.pluginDecorationLength = 0;
2371
2382
  // Track a minimum width for the editor. When measuring sizes in
2372
2383
  // measureVisibleLineHeights, this is updated to point at the width
2373
2384
  // of a given element and its extent in the document. When a change
@@ -2389,7 +2400,8 @@ class DocView extends ContentView {
2389
2400
  this.setDOM(view.contentDOM);
2390
2401
  this.children = [new LineView];
2391
2402
  this.children[0].setParent(this);
2392
- this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], this.updateDeco(), 0);
2403
+ this.updateDeco();
2404
+ this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], 0);
2393
2405
  }
2394
2406
  get root() { return this.view.root; }
2395
2407
  get editorView() { return this.view; }
@@ -2428,7 +2440,7 @@ class DocView extends ContentView {
2428
2440
  return false;
2429
2441
  }
2430
2442
  else {
2431
- this.updateInner(changedRanges, deco, update.startState.doc.length);
2443
+ this.updateInner(changedRanges, update.startState.doc.length);
2432
2444
  if (update.transactions.length)
2433
2445
  this.lastUpdate = Date.now();
2434
2446
  return true;
@@ -2436,9 +2448,9 @@ class DocView extends ContentView {
2436
2448
  }
2437
2449
  // Used by update and the constructor do perform the actual DOM
2438
2450
  // update
2439
- updateInner(changes, deco, oldLength) {
2451
+ updateInner(changes, oldLength) {
2440
2452
  this.view.viewState.mustMeasureContent = true;
2441
- this.updateChildren(changes, deco, oldLength);
2453
+ this.updateChildren(changes, oldLength);
2442
2454
  let { observer } = this.view;
2443
2455
  observer.ignore(() => {
2444
2456
  // Lock the height during redrawing, since Chrome sometimes
@@ -2465,14 +2477,14 @@ class DocView extends ContentView {
2465
2477
  gaps.push(child.dom);
2466
2478
  observer.updateGaps(gaps);
2467
2479
  }
2468
- updateChildren(changes, deco, oldLength) {
2480
+ updateChildren(changes, oldLength) {
2469
2481
  let cursor = this.childCursor(oldLength);
2470
2482
  for (let i = changes.length - 1;; i--) {
2471
2483
  let next = i >= 0 ? changes[i] : null;
2472
2484
  if (!next)
2473
2485
  break;
2474
2486
  let { fromA, toA, fromB, toB } = next;
2475
- let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, deco);
2487
+ let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, this.decorations, this.pluginDecorationLength);
2476
2488
  let { i: toI, off: toOff } = cursor.findPos(toA, 1);
2477
2489
  let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
2478
2490
  replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
@@ -2691,8 +2703,10 @@ class DocView extends ContentView {
2691
2703
  return Decoration.set(deco);
2692
2704
  }
2693
2705
  updateDeco() {
2706
+ let pluginDecorations = this.view.pluginField(PluginField.decorations);
2707
+ this.pluginDecorationLength = pluginDecorations.length;
2694
2708
  return this.decorations = [
2695
- ...this.view.pluginField(PluginField.decorations),
2709
+ ...pluginDecorations,
2696
2710
  ...this.view.state.facet(decorations),
2697
2711
  this.compositionDeco,
2698
2712
  this.computeBlockGapDeco(),
@@ -2985,12 +2999,12 @@ function domPosInText(node, x, y) {
2985
2999
  function posAtCoords(view, { x, y }, precise, bias = -1) {
2986
3000
  var _a;
2987
3001
  let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop;
2988
- let block, yOffset = y - docTop, { docHeight } = view.viewState;
2989
- if (yOffset < 0 || yOffset > docHeight) {
2990
- if (precise)
2991
- return null;
2992
- yOffset = yOffset < 0 ? 0 : docHeight;
2993
- }
3002
+ let block, { docHeight } = view.viewState;
3003
+ let yOffset = y - docTop;
3004
+ if (yOffset < 0)
3005
+ return 0;
3006
+ if (yOffset > docHeight)
3007
+ return view.state.doc.length;
2994
3008
  // Scan for a text block near the queried y position
2995
3009
  for (let halfLine = view.defaultLineHeight / 2, bounced = false;;) {
2996
3010
  block = view.elementAtHeight(yOffset);
@@ -3011,20 +3025,29 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
3011
3025
  }
3012
3026
  y = docTop + yOffset;
3013
3027
  let lineStart = block.from;
3014
- // Clip x to the viewport sides
3015
- x = Math.max(content.left + 1, Math.min(Math.max(content.right, content.left + view.docView.minWidth) - 1, x));
3016
3028
  // If this is outside of the rendered viewport, we can't determine a position
3017
3029
  if (lineStart < view.viewport.from)
3018
- return view.viewport.from == 0 ? 0 : posAtCoordsImprecise(view, content, block, x, y);
3030
+ return view.viewport.from == 0 ? 0 : precise ? null : posAtCoordsImprecise(view, content, block, x, y);
3019
3031
  if (lineStart > view.viewport.to)
3020
- return view.viewport.to == view.state.doc.length ? view.state.doc.length : posAtCoordsImprecise(view, content, block, x, y);
3032
+ return view.viewport.to == view.state.doc.length ? view.state.doc.length :
3033
+ precise ? null : posAtCoordsImprecise(view, content, block, x, y);
3021
3034
  // Prefer ShadowRootOrDocument.elementFromPoint if present, fall back to document if not
3022
3035
  let doc = view.dom.ownerDocument;
3023
- let element = (view.root.elementFromPoint ? view.root : doc).elementFromPoint(x, y);
3036
+ let root = view.root.elementFromPoint ? view.root : doc;
3037
+ let element = root.elementFromPoint(x, y);
3038
+ if (element && !view.contentDOM.contains(element))
3039
+ element = null;
3040
+ // If the element is unexpected, clip x at the sides of the content area and try again
3041
+ if (!element) {
3042
+ x = Math.max(content.left + 1, Math.min(content.right - 1, x));
3043
+ element = root.elementFromPoint(x, y);
3044
+ if (element && !view.contentDOM.contains(element))
3045
+ element = null;
3046
+ }
3024
3047
  // There's visible editor content under the point, so we can try
3025
3048
  // using caret(Position|Range)FromPoint as a shortcut
3026
3049
  let node, offset = -1;
3027
- if (element && view.contentDOM.contains(element) && ((_a = view.docView.nearest(element)) === null || _a === void 0 ? void 0 : _a.isEditable) != false) {
3050
+ if (element && ((_a = view.docView.nearest(element)) === null || _a === void 0 ? void 0 : _a.isEditable) != false) {
3028
3051
  if (doc.caretPositionFromPoint) {
3029
3052
  let pos = doc.caretPositionFromPoint(x, y);
3030
3053
  if (pos)
@@ -3042,6 +3065,8 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
3042
3065
  // No luck, do our own (potentially expensive) search
3043
3066
  if (!node || !view.docView.dom.contains(node)) {
3044
3067
  let line = LineView.find(view.docView, lineStart);
3068
+ if (!line)
3069
+ return yOffset > block.top + block.height / 2 ? block.to : block.from;
3045
3070
  ({ node, offset } = domPosAtCoords(line.dom, x, y));
3046
3071
  }
3047
3072
  return view.docView.posFromDOM(node, offset);
@@ -3505,7 +3530,7 @@ handlers.touchmove = view => {
3505
3530
  };
3506
3531
  handlers.mousedown = (view, event) => {
3507
3532
  view.observer.flush();
3508
- if (lastTouch > Date.now() - 2000)
3533
+ if (lastTouch > Date.now() - 2000 && getClickType(event) == 1)
3509
3534
  return; // Ignore touch interaction
3510
3535
  let style = null;
3511
3536
  for (let makeStyle of view.state.facet(mouseSelectionStyle)) {
@@ -3627,9 +3652,9 @@ handlers.dragstart = (view, event) => {
3627
3652
  }
3628
3653
  };
3629
3654
  function dropText(view, event, text, direct) {
3630
- let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY });
3631
- if (dropPos == null || !text)
3655
+ if (!text)
3632
3656
  return;
3657
+ let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
3633
3658
  event.preventDefault();
3634
3659
  let { mouseSelection } = view.inputState;
3635
3660
  let del = direct && mouseSelection && mouseSelection.dragging && mouseSelection.dragMove ?
@@ -4739,7 +4764,7 @@ class ViewState {
4739
4764
  let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
4740
4765
  if (scrollTarget.y == "center")
4741
4766
  topPos = (block.top + block.bottom) / 2 - viewHeight / 2;
4742
- else if (scrollTarget.y == "start" || head < viewport.from)
4767
+ else if (scrollTarget.y == "start" || scrollTarget.y == "nearest" && head < viewport.from)
4743
4768
  topPos = block.top;
4744
4769
  else
4745
4770
  topPos = block.bottom - viewHeight;
@@ -5286,8 +5311,10 @@ class DOMObserver {
5286
5311
  // Deletions on IE11 fire their events in the wrong order, giving
5287
5312
  // us a selection change event before the DOM changes are
5288
5313
  // reported.
5289
- // (Selection.isCollapsed isn't reliable on IE)
5290
- if (browser.ie && browser.ie_version <= 11 && !view.state.selection.main.empty &&
5314
+ // Chrome Android has a similar issue when backspacing out a
5315
+ // selection (#645).
5316
+ if ((browser.ie && browser.ie_version <= 11 || browser.android && browser.chrome) && !view.state.selection.main.empty &&
5317
+ // (Selection.isCollapsed isn't reliable on IE)
5291
5318
  sel.focusNode && isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset))
5292
5319
  this.flushSoon();
5293
5320
  else
@@ -5831,7 +5858,7 @@ class EditorView {
5831
5858
  if (state.facet(EditorState.phrases) != this.state.facet(EditorState.phrases))
5832
5859
  return this.setState(state);
5833
5860
  update = new ViewUpdate(this, state, transactions);
5834
- let scrollTarget = null;
5861
+ let scrollTarget = this.viewState.scrollTarget;
5835
5862
  try {
5836
5863
  this.updateState = 2 /* Updating */;
5837
5864
  for (let tr of transactions) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "0.19.34",
3
+ "version": "0.19.38",
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.4",
29
+ "@codemirror/rangeset": "^0.19.5",
30
30
  "@codemirror/state": "^0.19.3",
31
31
  "@codemirror/text": "^0.19.0",
32
32
  "style-mod": "^4.0.0",