@codemirror/view 0.19.35 → 0.19.36

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,13 @@
1
+ ## 0.19.36 (2021-12-22)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix a crash in `posAtCoords` when the position lies in a block widget that is rendered but scrolled out of view.
6
+
7
+ Adding block decorations from a plugin now raises an error. Replacing decorations that cross lines are ignored, when provided by a plugin.
8
+
9
+ Fix inverted interpretation of the `precise` argument to `posAtCoords`.
10
+
1
11
  ## 0.19.35 (2021-12-20)
2
12
 
3
13
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -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(),
@@ -3014,10 +3028,10 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
3014
3028
  let lineStart = block.from;
3015
3029
  // If this is outside of the rendered viewport, we can't determine a position
3016
3030
  if (lineStart < view.viewport.from)
3017
- return view.viewport.from == 0 ? 0 : precise ? posAtCoordsImprecise(view, content, block, x, y) : null;
3031
+ return view.viewport.from == 0 ? 0 : precise ? null : posAtCoordsImprecise(view, content, block, x, y);
3018
3032
  if (lineStart > view.viewport.to)
3019
3033
  return view.viewport.to == view.state.doc.length ? view.state.doc.length :
3020
- precise ? posAtCoordsImprecise(view, content, block, x, y) : null;
3034
+ precise ? null : posAtCoordsImprecise(view, content, block, x, y);
3021
3035
  // Prefer ShadowRootOrDocument.elementFromPoint if present, fall back to document if not
3022
3036
  let doc = view.dom.ownerDocument;
3023
3037
  let root = view.root.elementFromPoint ? view.root : doc;
@@ -3052,6 +3066,8 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
3052
3066
  // No luck, do our own (potentially expensive) search
3053
3067
  if (!node || !view.docView.dom.contains(node)) {
3054
3068
  let line = LineView.find(view.docView, lineStart);
3069
+ if (!line)
3070
+ return yOffset > block.top + block.height / 2 ? block.to : block.from;
3055
3071
  ({ node, offset } = domPosAtCoords(line.dom, x, y));
3056
3072
  }
3057
3073
  return view.docView.posFromDOM(node, offset);
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
  /**
package/dist/index.js CHANGED
@@ -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(),
@@ -3009,10 +3023,10 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
3009
3023
  let lineStart = block.from;
3010
3024
  // If this is outside of the rendered viewport, we can't determine a position
3011
3025
  if (lineStart < view.viewport.from)
3012
- return view.viewport.from == 0 ? 0 : precise ? posAtCoordsImprecise(view, content, block, x, y) : null;
3026
+ return view.viewport.from == 0 ? 0 : precise ? null : posAtCoordsImprecise(view, content, block, x, y);
3013
3027
  if (lineStart > view.viewport.to)
3014
3028
  return view.viewport.to == view.state.doc.length ? view.state.doc.length :
3015
- precise ? posAtCoordsImprecise(view, content, block, x, y) : null;
3029
+ precise ? null : posAtCoordsImprecise(view, content, block, x, y);
3016
3030
  // Prefer ShadowRootOrDocument.elementFromPoint if present, fall back to document if not
3017
3031
  let doc = view.dom.ownerDocument;
3018
3032
  let root = view.root.elementFromPoint ? view.root : doc;
@@ -3047,6 +3061,8 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
3047
3061
  // No luck, do our own (potentially expensive) search
3048
3062
  if (!node || !view.docView.dom.contains(node)) {
3049
3063
  let line = LineView.find(view.docView, lineStart);
3064
+ if (!line)
3065
+ return yOffset > block.top + block.height / 2 ? block.to : block.from;
3050
3066
  ({ node, offset } = domPosAtCoords(line.dom, x, y));
3051
3067
  }
3052
3068
  return view.docView.posFromDOM(node, offset);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "0.19.35",
3
+ "version": "0.19.36",
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",