@codemirror/view 0.19.33 → 0.19.37

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,35 @@
1
+ ## 0.19.37 (2021-12-22)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix regression where plugin replacing decorations that span to the end of the line are ignored.
6
+
7
+ ## 0.19.36 (2021-12-22)
8
+
9
+ ### Bug fixes
10
+
11
+ Fix a crash in `posAtCoords` when the position lies in a block widget that is rendered but scrolled out of view.
12
+
13
+ Adding block decorations from a plugin now raises an error. Replacing decorations that cross lines are ignored, when provided by a plugin.
14
+
15
+ Fix inverted interpretation of the `precise` argument to `posAtCoords`.
16
+
17
+ ## 0.19.35 (2021-12-20)
18
+
19
+ ### Bug fixes
20
+
21
+ 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).
22
+
23
+ Fix an issue where backspacing out a selection on Chrome Android would sometimes only delete the last character due to event order issues.
24
+
25
+ `posAtCoords`, without second argument, will no longer return null for positions below or above the document.
26
+
27
+ ## 0.19.34 (2021-12-17)
28
+
29
+ ### Bug fixes
30
+
31
+ Fix a bug where content line elements would in some cases lose their `cm-line` class. Move test to scrollIntoView
32
+
1
33
  ## 0.19.33 (2021-12-16)
2
34
 
3
35
  ### Breaking changes
package/dist/index.cjs CHANGED
@@ -1331,6 +1331,7 @@ class LineView extends ContentView {
1331
1331
  }
1332
1332
  else if (this.dirty & 4 /* Attrs */) {
1333
1333
  clearAttributes(this.dom);
1334
+ this.dom.className = "cm-line";
1334
1335
  this.prevAttrs = this.attrs ? null : undefined;
1335
1336
  }
1336
1337
  if (this.prevAttrs !== undefined) {
@@ -1371,16 +1372,17 @@ class LineView extends ContentView {
1371
1372
  become(_other) { return false; }
1372
1373
  get type() { return exports.BlockType.Text; }
1373
1374
  static find(docView, pos) {
1374
- for (let i = 0, off = 0;; i++) {
1375
+ for (let i = 0, off = 0; i < docView.children.length; i++) {
1375
1376
  let block = docView.children[i], end = off + block.length;
1376
1377
  if (end >= pos) {
1377
1378
  if (block instanceof LineView)
1378
1379
  return block;
1379
- if (block.length)
1380
- return null;
1380
+ if (end > pos)
1381
+ break;
1381
1382
  }
1382
1383
  off = end + block.breakAfter;
1383
1384
  }
1385
+ return null;
1384
1386
  }
1385
1387
  }
1386
1388
  class BlockWidgetView extends ContentView {
@@ -1441,10 +1443,11 @@ class BlockWidgetView extends ContentView {
1441
1443
  }
1442
1444
 
1443
1445
  class ContentBuilder {
1444
- constructor(doc, pos, end) {
1446
+ constructor(doc, pos, end, disallowBlockEffectsBelow) {
1445
1447
  this.doc = doc;
1446
1448
  this.pos = pos;
1447
1449
  this.end = end;
1450
+ this.disallowBlockEffectsBelow = disallowBlockEffectsBelow;
1448
1451
  this.content = [];
1449
1452
  this.curLine = null;
1450
1453
  this.breakAtStart = 0;
@@ -1573,8 +1576,15 @@ class ContentBuilder {
1573
1576
  if (this.openStart < 0)
1574
1577
  this.openStart = openStart;
1575
1578
  }
1576
- static build(text, from, to, decorations) {
1577
- 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);
1578
1588
  builder.openEnd = rangeset.RangeSet.spans(decorations, from, to, builder);
1579
1589
  if (builder.openStart < 0)
1580
1590
  builder.openStart = builder.openEnd;
@@ -1697,11 +1707,13 @@ This field can be used by plugins to provide
1697
1707
  **Note**: For reasons of data flow (plugins are only updated
1698
1708
  after the viewport is computed), decorations produced by plugins
1699
1709
  are _not_ taken into account when predicting the vertical layout
1700
- structure of the editor. Thus, things like large widgets or big
1701
- replacements (i.e. code folding) should be provided through the
1702
- state-level [`decorations` facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations),
1703
- not this plugin field. Specifically, replacing decorations that
1704
- 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.
1705
1717
  */
1706
1718
  PluginField.decorations = PluginField.define();
1707
1719
  /**
@@ -1934,8 +1946,6 @@ class ViewUpdate {
1934
1946
  view.inputState.notifiedFocused = focus;
1935
1947
  this.flags |= 1 /* Focus */;
1936
1948
  }
1937
- if (this.docChanged)
1938
- this.flags |= 2 /* Height */;
1939
1949
  }
1940
1950
  /**
1941
1951
  Tells you whether the [viewport](https://codemirror.net/6/docs/ref/#view.EditorView.viewport) or
@@ -1946,14 +1956,15 @@ class ViewUpdate {
1946
1956
  return (this.flags & 4 /* Viewport */) > 0;
1947
1957
  }
1948
1958
  /**
1949
- 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.
1950
1961
  */
1951
1962
  get heightChanged() {
1952
1963
  return (this.flags & 2 /* Height */) > 0;
1953
1964
  }
1954
1965
  /**
1955
- Returns true when the document changed or the size of the editor
1956
- 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.
1957
1968
  */
1958
1969
  get geometryChanged() {
1959
1970
  return this.docChanged || (this.flags & (8 /* Geometry */ | 2 /* Height */)) > 0;
@@ -2372,6 +2383,7 @@ class DocView extends ContentView {
2372
2383
  this.view = view;
2373
2384
  this.compositionDeco = Decoration.none;
2374
2385
  this.decorations = [];
2386
+ this.pluginDecorationLength = 0;
2375
2387
  // Track a minimum width for the editor. When measuring sizes in
2376
2388
  // measureVisibleLineHeights, this is updated to point at the width
2377
2389
  // of a given element and its extent in the document. When a change
@@ -2393,7 +2405,8 @@ class DocView extends ContentView {
2393
2405
  this.setDOM(view.contentDOM);
2394
2406
  this.children = [new LineView];
2395
2407
  this.children[0].setParent(this);
2396
- 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);
2397
2410
  }
2398
2411
  get root() { return this.view.root; }
2399
2412
  get editorView() { return this.view; }
@@ -2432,7 +2445,7 @@ class DocView extends ContentView {
2432
2445
  return false;
2433
2446
  }
2434
2447
  else {
2435
- this.updateInner(changedRanges, deco, update.startState.doc.length);
2448
+ this.updateInner(changedRanges, update.startState.doc.length);
2436
2449
  if (update.transactions.length)
2437
2450
  this.lastUpdate = Date.now();
2438
2451
  return true;
@@ -2440,9 +2453,9 @@ class DocView extends ContentView {
2440
2453
  }
2441
2454
  // Used by update and the constructor do perform the actual DOM
2442
2455
  // update
2443
- updateInner(changes, deco, oldLength) {
2456
+ updateInner(changes, oldLength) {
2444
2457
  this.view.viewState.mustMeasureContent = true;
2445
- this.updateChildren(changes, deco, oldLength);
2458
+ this.updateChildren(changes, oldLength);
2446
2459
  let { observer } = this.view;
2447
2460
  observer.ignore(() => {
2448
2461
  // Lock the height during redrawing, since Chrome sometimes
@@ -2469,14 +2482,14 @@ class DocView extends ContentView {
2469
2482
  gaps.push(child.dom);
2470
2483
  observer.updateGaps(gaps);
2471
2484
  }
2472
- updateChildren(changes, deco, oldLength) {
2485
+ updateChildren(changes, oldLength) {
2473
2486
  let cursor = this.childCursor(oldLength);
2474
2487
  for (let i = changes.length - 1;; i--) {
2475
2488
  let next = i >= 0 ? changes[i] : null;
2476
2489
  if (!next)
2477
2490
  break;
2478
2491
  let { fromA, toA, fromB, toB } = next;
2479
- 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);
2480
2493
  let { i: toI, off: toOff } = cursor.findPos(toA, 1);
2481
2494
  let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
2482
2495
  replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
@@ -2695,8 +2708,10 @@ class DocView extends ContentView {
2695
2708
  return Decoration.set(deco);
2696
2709
  }
2697
2710
  updateDeco() {
2711
+ let pluginDecorations = this.view.pluginField(PluginField.decorations);
2712
+ this.pluginDecorationLength = pluginDecorations.length;
2698
2713
  return this.decorations = [
2699
- ...this.view.pluginField(PluginField.decorations),
2714
+ ...pluginDecorations,
2700
2715
  ...this.view.state.facet(decorations),
2701
2716
  this.compositionDeco,
2702
2717
  this.computeBlockGapDeco(),
@@ -2989,12 +3004,8 @@ function domPosInText(node, x, y) {
2989
3004
  function posAtCoords(view, { x, y }, precise, bias = -1) {
2990
3005
  var _a;
2991
3006
  let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop;
2992
- let block, yOffset = y - docTop, { docHeight } = view.viewState;
2993
- if (yOffset < 0 || yOffset > docHeight) {
2994
- if (precise)
2995
- return null;
2996
- yOffset = yOffset < 0 ? 0 : docHeight;
2997
- }
3007
+ let block, { docHeight } = view.viewState;
3008
+ let yOffset = Math.max(0, Math.min(y - docTop, docHeight));
2998
3009
  // Scan for a text block near the queried y position
2999
3010
  for (let halfLine = view.defaultLineHeight / 2, bounced = false;;) {
3000
3011
  block = view.elementAtHeight(yOffset);
@@ -3015,20 +3026,29 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
3015
3026
  }
3016
3027
  y = docTop + yOffset;
3017
3028
  let lineStart = block.from;
3018
- // Clip x to the viewport sides
3019
- x = Math.max(content.left + 1, Math.min(Math.max(content.right, content.left + view.docView.minWidth) - 1, x));
3020
3029
  // If this is outside of the rendered viewport, we can't determine a position
3021
3030
  if (lineStart < view.viewport.from)
3022
- return view.viewport.from == 0 ? 0 : posAtCoordsImprecise(view, content, block, x, y);
3031
+ return view.viewport.from == 0 ? 0 : precise ? null : posAtCoordsImprecise(view, content, block, x, y);
3023
3032
  if (lineStart > view.viewport.to)
3024
- return view.viewport.to == view.state.doc.length ? view.state.doc.length : posAtCoordsImprecise(view, content, block, x, y);
3033
+ return view.viewport.to == view.state.doc.length ? view.state.doc.length :
3034
+ precise ? null : posAtCoordsImprecise(view, content, block, x, y);
3025
3035
  // Prefer ShadowRootOrDocument.elementFromPoint if present, fall back to document if not
3026
3036
  let doc = view.dom.ownerDocument;
3027
- let element = (view.root.elementFromPoint ? view.root : doc).elementFromPoint(x, y);
3037
+ let root = view.root.elementFromPoint ? view.root : doc;
3038
+ let element = root.elementFromPoint(x, y);
3039
+ if (element && !view.contentDOM.contains(element))
3040
+ element = null;
3041
+ // If the element is unexpected, clip x at the sides of the content area and try again
3042
+ if (!element) {
3043
+ x = Math.max(content.left + 1, Math.min(content.right - 1, x));
3044
+ element = root.elementFromPoint(x, y);
3045
+ if (element && !view.contentDOM.contains(element))
3046
+ element = null;
3047
+ }
3028
3048
  // There's visible editor content under the point, so we can try
3029
3049
  // using caret(Position|Range)FromPoint as a shortcut
3030
3050
  let node, offset = -1;
3031
- if (element && view.contentDOM.contains(element) && ((_a = view.docView.nearest(element)) === null || _a === void 0 ? void 0 : _a.isEditable) != false) {
3051
+ if (element && ((_a = view.docView.nearest(element)) === null || _a === void 0 ? void 0 : _a.isEditable) != false) {
3032
3052
  if (doc.caretPositionFromPoint) {
3033
3053
  let pos = doc.caretPositionFromPoint(x, y);
3034
3054
  if (pos)
@@ -3046,6 +3066,8 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
3046
3066
  // No luck, do our own (potentially expensive) search
3047
3067
  if (!node || !view.docView.dom.contains(node)) {
3048
3068
  let line = LineView.find(view.docView, lineStart);
3069
+ if (!line)
3070
+ return yOffset > block.top + block.height / 2 ? block.to : block.from;
3049
3071
  ({ node, offset } = domPosAtCoords(line.dom, x, y));
3050
3072
  }
3051
3073
  return view.docView.posFromDOM(node, offset);
@@ -3509,7 +3531,7 @@ handlers.touchmove = view => {
3509
3531
  };
3510
3532
  handlers.mousedown = (view, event) => {
3511
3533
  view.observer.flush();
3512
- if (lastTouch > Date.now() - 2000)
3534
+ if (lastTouch > Date.now() - 2000 && getClickType(event) == 1)
3513
3535
  return; // Ignore touch interaction
3514
3536
  let style = null;
3515
3537
  for (let makeStyle of view.state.facet(mouseSelectionStyle)) {
@@ -3631,9 +3653,9 @@ handlers.dragstart = (view, event) => {
3631
3653
  }
3632
3654
  };
3633
3655
  function dropText(view, event, text, direct) {
3634
- let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY });
3635
- if (dropPos == null || !text)
3656
+ if (!text)
3636
3657
  return;
3658
+ let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
3637
3659
  event.preventDefault();
3638
3660
  let { mouseSelection } = view.inputState;
3639
3661
  let del = direct && mouseSelection && mouseSelection.dragging && mouseSelection.dragMove ?
@@ -4744,7 +4766,7 @@ class ViewState {
4744
4766
  let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
4745
4767
  if (scrollTarget.y == "center")
4746
4768
  topPos = (block.top + block.bottom) / 2 - viewHeight / 2;
4747
- else if (scrollTarget.y == "start" || head < viewport.from)
4769
+ else if (scrollTarget.y == "start" || scrollTarget.y == "nearest" && head < viewport.from)
4748
4770
  topPos = block.top;
4749
4771
  else
4750
4772
  topPos = block.bottom - viewHeight;
@@ -5291,8 +5313,10 @@ class DOMObserver {
5291
5313
  // Deletions on IE11 fire their events in the wrong order, giving
5292
5314
  // us a selection change event before the DOM changes are
5293
5315
  // reported.
5294
- // (Selection.isCollapsed isn't reliable on IE)
5295
- if (browser.ie && browser.ie_version <= 11 && !view.state.selection.main.empty &&
5316
+ // Chrome Android has a similar issue when backspacing out a
5317
+ // selection (#645).
5318
+ if ((browser.ie && browser.ie_version <= 11 || browser.android && browser.chrome) && !view.state.selection.main.empty &&
5319
+ // (Selection.isCollapsed isn't reliable on IE)
5296
5320
  sel.focusNode && isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset))
5297
5321
  this.flushSoon();
5298
5322
  else
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
@@ -1327,6 +1327,7 @@ class LineView extends ContentView {
1327
1327
  }
1328
1328
  else if (this.dirty & 4 /* Attrs */) {
1329
1329
  clearAttributes(this.dom);
1330
+ this.dom.className = "cm-line";
1330
1331
  this.prevAttrs = this.attrs ? null : undefined;
1331
1332
  }
1332
1333
  if (this.prevAttrs !== undefined) {
@@ -1367,16 +1368,17 @@ class LineView extends ContentView {
1367
1368
  become(_other) { return false; }
1368
1369
  get type() { return BlockType.Text; }
1369
1370
  static find(docView, pos) {
1370
- for (let i = 0, off = 0;; i++) {
1371
+ for (let i = 0, off = 0; i < docView.children.length; i++) {
1371
1372
  let block = docView.children[i], end = off + block.length;
1372
1373
  if (end >= pos) {
1373
1374
  if (block instanceof LineView)
1374
1375
  return block;
1375
- if (block.length)
1376
- return null;
1376
+ if (end > pos)
1377
+ break;
1377
1378
  }
1378
1379
  off = end + block.breakAfter;
1379
1380
  }
1381
+ return null;
1380
1382
  }
1381
1383
  }
1382
1384
  class BlockWidgetView extends ContentView {
@@ -1437,10 +1439,11 @@ class BlockWidgetView extends ContentView {
1437
1439
  }
1438
1440
 
1439
1441
  class ContentBuilder {
1440
- constructor(doc, pos, end) {
1442
+ constructor(doc, pos, end, disallowBlockEffectsBelow) {
1441
1443
  this.doc = doc;
1442
1444
  this.pos = pos;
1443
1445
  this.end = end;
1446
+ this.disallowBlockEffectsBelow = disallowBlockEffectsBelow;
1444
1447
  this.content = [];
1445
1448
  this.curLine = null;
1446
1449
  this.breakAtStart = 0;
@@ -1569,8 +1572,15 @@ class ContentBuilder {
1569
1572
  if (this.openStart < 0)
1570
1573
  this.openStart = openStart;
1571
1574
  }
1572
- static build(text, from, to, decorations) {
1573
- 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);
1574
1584
  builder.openEnd = RangeSet.spans(decorations, from, to, builder);
1575
1585
  if (builder.openStart < 0)
1576
1586
  builder.openStart = builder.openEnd;
@@ -1693,11 +1703,13 @@ This field can be used by plugins to provide
1693
1703
  **Note**: For reasons of data flow (plugins are only updated
1694
1704
  after the viewport is computed), decorations produced by plugins
1695
1705
  are _not_ taken into account when predicting the vertical layout
1696
- structure of the editor. Thus, things like large widgets or big
1697
- replacements (i.e. code folding) should be provided through the
1698
- state-level [`decorations` facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations),
1699
- not this plugin field. Specifically, replacing decorations that
1700
- 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.
1701
1713
  */
1702
1714
  PluginField.decorations = /*@__PURE__*/PluginField.define();
1703
1715
  /**
@@ -1930,8 +1942,6 @@ class ViewUpdate {
1930
1942
  view.inputState.notifiedFocused = focus;
1931
1943
  this.flags |= 1 /* Focus */;
1932
1944
  }
1933
- if (this.docChanged)
1934
- this.flags |= 2 /* Height */;
1935
1945
  }
1936
1946
  /**
1937
1947
  Tells you whether the [viewport](https://codemirror.net/6/docs/ref/#view.EditorView.viewport) or
@@ -1942,14 +1952,15 @@ class ViewUpdate {
1942
1952
  return (this.flags & 4 /* Viewport */) > 0;
1943
1953
  }
1944
1954
  /**
1945
- 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.
1946
1957
  */
1947
1958
  get heightChanged() {
1948
1959
  return (this.flags & 2 /* Height */) > 0;
1949
1960
  }
1950
1961
  /**
1951
- Returns true when the document changed or the size of the editor
1952
- 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.
1953
1964
  */
1954
1965
  get geometryChanged() {
1955
1966
  return this.docChanged || (this.flags & (8 /* Geometry */ | 2 /* Height */)) > 0;
@@ -2367,6 +2378,7 @@ class DocView extends ContentView {
2367
2378
  this.view = view;
2368
2379
  this.compositionDeco = Decoration.none;
2369
2380
  this.decorations = [];
2381
+ this.pluginDecorationLength = 0;
2370
2382
  // Track a minimum width for the editor. When measuring sizes in
2371
2383
  // measureVisibleLineHeights, this is updated to point at the width
2372
2384
  // of a given element and its extent in the document. When a change
@@ -2388,7 +2400,8 @@ class DocView extends ContentView {
2388
2400
  this.setDOM(view.contentDOM);
2389
2401
  this.children = [new LineView];
2390
2402
  this.children[0].setParent(this);
2391
- 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);
2392
2405
  }
2393
2406
  get root() { return this.view.root; }
2394
2407
  get editorView() { return this.view; }
@@ -2427,7 +2440,7 @@ class DocView extends ContentView {
2427
2440
  return false;
2428
2441
  }
2429
2442
  else {
2430
- this.updateInner(changedRanges, deco, update.startState.doc.length);
2443
+ this.updateInner(changedRanges, update.startState.doc.length);
2431
2444
  if (update.transactions.length)
2432
2445
  this.lastUpdate = Date.now();
2433
2446
  return true;
@@ -2435,9 +2448,9 @@ class DocView extends ContentView {
2435
2448
  }
2436
2449
  // Used by update and the constructor do perform the actual DOM
2437
2450
  // update
2438
- updateInner(changes, deco, oldLength) {
2451
+ updateInner(changes, oldLength) {
2439
2452
  this.view.viewState.mustMeasureContent = true;
2440
- this.updateChildren(changes, deco, oldLength);
2453
+ this.updateChildren(changes, oldLength);
2441
2454
  let { observer } = this.view;
2442
2455
  observer.ignore(() => {
2443
2456
  // Lock the height during redrawing, since Chrome sometimes
@@ -2464,14 +2477,14 @@ class DocView extends ContentView {
2464
2477
  gaps.push(child.dom);
2465
2478
  observer.updateGaps(gaps);
2466
2479
  }
2467
- updateChildren(changes, deco, oldLength) {
2480
+ updateChildren(changes, oldLength) {
2468
2481
  let cursor = this.childCursor(oldLength);
2469
2482
  for (let i = changes.length - 1;; i--) {
2470
2483
  let next = i >= 0 ? changes[i] : null;
2471
2484
  if (!next)
2472
2485
  break;
2473
2486
  let { fromA, toA, fromB, toB } = next;
2474
- 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);
2475
2488
  let { i: toI, off: toOff } = cursor.findPos(toA, 1);
2476
2489
  let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
2477
2490
  replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
@@ -2690,8 +2703,10 @@ class DocView extends ContentView {
2690
2703
  return Decoration.set(deco);
2691
2704
  }
2692
2705
  updateDeco() {
2706
+ let pluginDecorations = this.view.pluginField(PluginField.decorations);
2707
+ this.pluginDecorationLength = pluginDecorations.length;
2693
2708
  return this.decorations = [
2694
- ...this.view.pluginField(PluginField.decorations),
2709
+ ...pluginDecorations,
2695
2710
  ...this.view.state.facet(decorations),
2696
2711
  this.compositionDeco,
2697
2712
  this.computeBlockGapDeco(),
@@ -2984,12 +2999,8 @@ function domPosInText(node, x, y) {
2984
2999
  function posAtCoords(view, { x, y }, precise, bias = -1) {
2985
3000
  var _a;
2986
3001
  let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop;
2987
- let block, yOffset = y - docTop, { docHeight } = view.viewState;
2988
- if (yOffset < 0 || yOffset > docHeight) {
2989
- if (precise)
2990
- return null;
2991
- yOffset = yOffset < 0 ? 0 : docHeight;
2992
- }
3002
+ let block, { docHeight } = view.viewState;
3003
+ let yOffset = Math.max(0, Math.min(y - docTop, docHeight));
2993
3004
  // Scan for a text block near the queried y position
2994
3005
  for (let halfLine = view.defaultLineHeight / 2, bounced = false;;) {
2995
3006
  block = view.elementAtHeight(yOffset);
@@ -3010,20 +3021,29 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
3010
3021
  }
3011
3022
  y = docTop + yOffset;
3012
3023
  let lineStart = block.from;
3013
- // Clip x to the viewport sides
3014
- x = Math.max(content.left + 1, Math.min(Math.max(content.right, content.left + view.docView.minWidth) - 1, x));
3015
3024
  // If this is outside of the rendered viewport, we can't determine a position
3016
3025
  if (lineStart < view.viewport.from)
3017
- return view.viewport.from == 0 ? 0 : posAtCoordsImprecise(view, content, block, x, y);
3026
+ return view.viewport.from == 0 ? 0 : precise ? null : posAtCoordsImprecise(view, content, block, x, y);
3018
3027
  if (lineStart > view.viewport.to)
3019
- return view.viewport.to == view.state.doc.length ? view.state.doc.length : posAtCoordsImprecise(view, content, block, x, y);
3028
+ return view.viewport.to == view.state.doc.length ? view.state.doc.length :
3029
+ precise ? null : posAtCoordsImprecise(view, content, block, x, y);
3020
3030
  // Prefer ShadowRootOrDocument.elementFromPoint if present, fall back to document if not
3021
3031
  let doc = view.dom.ownerDocument;
3022
- let element = (view.root.elementFromPoint ? view.root : doc).elementFromPoint(x, y);
3032
+ let root = view.root.elementFromPoint ? view.root : doc;
3033
+ let element = root.elementFromPoint(x, y);
3034
+ if (element && !view.contentDOM.contains(element))
3035
+ element = null;
3036
+ // If the element is unexpected, clip x at the sides of the content area and try again
3037
+ if (!element) {
3038
+ x = Math.max(content.left + 1, Math.min(content.right - 1, x));
3039
+ element = root.elementFromPoint(x, y);
3040
+ if (element && !view.contentDOM.contains(element))
3041
+ element = null;
3042
+ }
3023
3043
  // There's visible editor content under the point, so we can try
3024
3044
  // using caret(Position|Range)FromPoint as a shortcut
3025
3045
  let node, offset = -1;
3026
- if (element && view.contentDOM.contains(element) && ((_a = view.docView.nearest(element)) === null || _a === void 0 ? void 0 : _a.isEditable) != false) {
3046
+ if (element && ((_a = view.docView.nearest(element)) === null || _a === void 0 ? void 0 : _a.isEditable) != false) {
3027
3047
  if (doc.caretPositionFromPoint) {
3028
3048
  let pos = doc.caretPositionFromPoint(x, y);
3029
3049
  if (pos)
@@ -3041,6 +3061,8 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
3041
3061
  // No luck, do our own (potentially expensive) search
3042
3062
  if (!node || !view.docView.dom.contains(node)) {
3043
3063
  let line = LineView.find(view.docView, lineStart);
3064
+ if (!line)
3065
+ return yOffset > block.top + block.height / 2 ? block.to : block.from;
3044
3066
  ({ node, offset } = domPosAtCoords(line.dom, x, y));
3045
3067
  }
3046
3068
  return view.docView.posFromDOM(node, offset);
@@ -3504,7 +3526,7 @@ handlers.touchmove = view => {
3504
3526
  };
3505
3527
  handlers.mousedown = (view, event) => {
3506
3528
  view.observer.flush();
3507
- if (lastTouch > Date.now() - 2000)
3529
+ if (lastTouch > Date.now() - 2000 && getClickType(event) == 1)
3508
3530
  return; // Ignore touch interaction
3509
3531
  let style = null;
3510
3532
  for (let makeStyle of view.state.facet(mouseSelectionStyle)) {
@@ -3626,9 +3648,9 @@ handlers.dragstart = (view, event) => {
3626
3648
  }
3627
3649
  };
3628
3650
  function dropText(view, event, text, direct) {
3629
- let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY });
3630
- if (dropPos == null || !text)
3651
+ if (!text)
3631
3652
  return;
3653
+ let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
3632
3654
  event.preventDefault();
3633
3655
  let { mouseSelection } = view.inputState;
3634
3656
  let del = direct && mouseSelection && mouseSelection.dragging && mouseSelection.dragMove ?
@@ -4738,7 +4760,7 @@ class ViewState {
4738
4760
  let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
4739
4761
  if (scrollTarget.y == "center")
4740
4762
  topPos = (block.top + block.bottom) / 2 - viewHeight / 2;
4741
- else if (scrollTarget.y == "start" || head < viewport.from)
4763
+ else if (scrollTarget.y == "start" || scrollTarget.y == "nearest" && head < viewport.from)
4742
4764
  topPos = block.top;
4743
4765
  else
4744
4766
  topPos = block.bottom - viewHeight;
@@ -5285,8 +5307,10 @@ class DOMObserver {
5285
5307
  // Deletions on IE11 fire their events in the wrong order, giving
5286
5308
  // us a selection change event before the DOM changes are
5287
5309
  // reported.
5288
- // (Selection.isCollapsed isn't reliable on IE)
5289
- if (browser.ie && browser.ie_version <= 11 && !view.state.selection.main.empty &&
5310
+ // Chrome Android has a similar issue when backspacing out a
5311
+ // selection (#645).
5312
+ if ((browser.ie && browser.ie_version <= 11 || browser.android && browser.chrome) && !view.state.selection.main.empty &&
5313
+ // (Selection.isCollapsed isn't reliable on IE)
5290
5314
  sel.focusNode && isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset))
5291
5315
  this.flushSoon();
5292
5316
  else
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "0.19.33",
3
+ "version": "0.19.37",
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",