@codemirror/view 6.3.1 → 6.4.0

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,21 @@
1
+ ## 6.4.0 (2022-10-18)
2
+
3
+ ### Bug fixes
4
+
5
+ Avoid an issue where `scrollPastEnd` makes a single-line editor have a vertical scrollbar.
6
+
7
+ Work around a Chrome bug where it inserts a newline when you press space at the start of a wrapped line.
8
+
9
+ Align `rectangularSelection`'s behavior with other popular editors by making it create cursors at the end of lines that are too short to touch the rectangle.
10
+
11
+ Fix an issue where coordinates on mark decoration boundaries were sometimes taken from the wrong side of the position.
12
+
13
+ Prevent scrolling artifacts caused by attempts to scroll stuff into view when the editor isn't being displayed.
14
+
15
+ ### New features
16
+
17
+ `TooltipView` objects can now provide a `destroy` method to be called when the tooltip is removed.
18
+
1
19
  ## 6.3.1 (2022-10-10)
2
20
 
3
21
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -1054,57 +1054,31 @@ function joinInlineInto(parent, view, open) {
1054
1054
  parent.length += view.length;
1055
1055
  }
1056
1056
  function coordsInChildren(view, pos, side) {
1057
- if (!view.children.length)
1058
- return fallbackRect(view);
1059
- return (side <= 0 ? coordsInChildrenBefore : coordsInChildrenAfter)(view, pos);
1060
- }
1061
- function coordsInChildrenBefore(view, pos) {
1062
- // Find the last leaf in the tree that touches pos and doesn't have getSide() > 0
1063
- let found = null, foundPos = -1;
1057
+ let before = null, beforePos = -1, after = null, afterPos = -1;
1064
1058
  function scan(view, pos) {
1065
1059
  for (let i = 0, off = 0; i < view.children.length && off <= pos; i++) {
1066
1060
  let child = view.children[i], end = off + child.length;
1067
1061
  if (end >= pos) {
1068
1062
  if (child.children.length) {
1069
- if (scan(child, pos - off))
1070
- return true;
1063
+ scan(child, pos - off);
1071
1064
  }
1072
- else if (end >= pos) {
1073
- if (end == pos && child.getSide() > 0)
1074
- return true;
1075
- found = child;
1076
- foundPos = pos - off;
1065
+ else if (!after && (end > pos || off == end && child.getSide() > 0)) {
1066
+ after = child;
1067
+ afterPos = pos - off;
1077
1068
  }
1078
- }
1079
- off = end;
1080
- }
1081
- }
1082
- scan(view, pos);
1083
- return found ? found.coordsAt(Math.max(0, foundPos), -1) : coordsInChildrenAfter(view, pos);
1084
- }
1085
- function coordsInChildrenAfter(view, pos) {
1086
- // Find the first leaf in the tree that touches pos and doesn't have getSide() < 0
1087
- let found = null, foundPos = -1;
1088
- function scan(view, pos) {
1089
- for (let i = view.children.length - 1, off = view.length; i >= 0 && off >= pos; i--) {
1090
- let child = view.children[i];
1091
- off -= child.length;
1092
- if (off <= pos) {
1093
- if (child.children.length) {
1094
- if (scan(child, pos - off))
1095
- return true;
1096
- }
1097
- else if (off <= pos) {
1098
- if (off == pos && child.getSide() < 0)
1099
- return true;
1100
- found = child;
1101
- foundPos = pos - off;
1069
+ else if (off < pos || (off == end && child.getSide() < 0)) {
1070
+ before = child;
1071
+ beforePos = pos - off;
1102
1072
  }
1103
1073
  }
1074
+ off = end;
1104
1075
  }
1105
1076
  }
1106
1077
  scan(view, pos);
1107
- return found ? found.coordsAt(Math.max(0, foundPos), 1) : coordsInChildrenBefore(view, pos);
1078
+ let target = (side < 0 ? before : after) || before || after;
1079
+ if (target)
1080
+ return target.coordsAt(Math.max(0, target == before ? beforePos : afterPos), side);
1081
+ return fallbackRect(view);
1108
1082
  }
1109
1083
  function fallbackRect(view) {
1110
1084
  let last = view.dom.lastChild;
@@ -5572,6 +5546,15 @@ function applyDOMChange(view, domChange) {
5572
5546
  newSel = state.EditorSelection.single(newSel.main.anchor - 1, newSel.main.head - 1);
5573
5547
  change = { from: sel.from, to: sel.to, insert: state.Text.of([" "]) };
5574
5548
  }
5549
+ else if (browser.chrome && change && change.from == change.to && change.from == sel.head &&
5550
+ change.insert.toString() == "\n " && view.lineWrapping) {
5551
+ // In Chrome, if you insert a space at the start of a wrapped
5552
+ // line, it will actually insert a newline and a space, causing a
5553
+ // bogus new line to be created in CodeMirror (#968)
5554
+ if (newSel)
5555
+ newSel = state.EditorSelection.single(newSel.main.anchor - 1, newSel.main.head - 1);
5556
+ change = { from: sel.from, to: sel.to, insert: state.Text.of([" "]) };
5557
+ }
5575
5558
  if (change) {
5576
5559
  let startState = view.state;
5577
5560
  if (browser.ios && view.inputState.flushIOSKey(view))
@@ -6496,17 +6479,19 @@ class EditorView {
6496
6479
  logException(this.state, e);
6497
6480
  }
6498
6481
  }
6499
- if (this.viewState.scrollTarget) {
6500
- this.docView.scrollIntoView(this.viewState.scrollTarget);
6501
- this.viewState.scrollTarget = null;
6502
- scrolled = true;
6503
- }
6504
- else {
6505
- let diff = this.viewState.lineBlockAt(refBlock.from).top - refBlock.top;
6506
- if (diff > 1 || diff < -1) {
6507
- this.scrollDOM.scrollTop += diff;
6482
+ if (this.viewState.editorHeight) {
6483
+ if (this.viewState.scrollTarget) {
6484
+ this.docView.scrollIntoView(this.viewState.scrollTarget);
6485
+ this.viewState.scrollTarget = null;
6508
6486
  scrolled = true;
6509
6487
  }
6488
+ else {
6489
+ let diff = this.viewState.lineBlockAt(refBlock.from).top - refBlock.top;
6490
+ if (diff > 1 || diff < -1) {
6491
+ this.scrollDOM.scrollTop += diff;
6492
+ scrolled = true;
6493
+ }
6494
+ }
6510
6495
  }
6511
6496
  if (redrawn)
6512
6497
  this.docView.updateSelection(true);
@@ -7912,7 +7897,8 @@ const plugin = ViewPlugin.fromClass(class {
7912
7897
  this.attrs = { style: "padding-bottom: 1000px" };
7913
7898
  }
7914
7899
  update(update) {
7915
- let height = update.view.viewState.editorHeight - update.view.defaultLineHeight;
7900
+ let { view } = update;
7901
+ let height = view.viewState.editorHeight - view.defaultLineHeight - view.documentPadding.top - 0.5;
7916
7902
  if (height != this.height) {
7917
7903
  this.height = height;
7918
7904
  this.attrs = { style: `padding-bottom: ${height}px` };
@@ -8014,7 +8000,10 @@ function rectangleFor(state$1, a, b) {
8014
8000
  for (let i = startLine; i <= endLine; i++) {
8015
8001
  let line = state$1.doc.line(i);
8016
8002
  let start = state.findColumn(line.text, startCol, state$1.tabSize, true);
8017
- if (start > -1) {
8003
+ if (start < 0) {
8004
+ ranges.push(state.EditorSelection.cursor(line.to));
8005
+ }
8006
+ else {
8018
8007
  let end = state.findColumn(line.text, endCol, state$1.tabSize);
8019
8008
  ranges.push(state.EditorSelection.range(line.from + start, line.from + end));
8020
8009
  }
@@ -8126,6 +8115,7 @@ class TooltipViewManager {
8126
8115
  this.tooltipViews = this.tooltips.map(createTooltipView);
8127
8116
  }
8128
8117
  update(update) {
8118
+ var _a;
8129
8119
  let input = update.state.facet(this.facet);
8130
8120
  let tooltips = input.filter(x => x);
8131
8121
  if (input === this.input) {
@@ -8154,8 +8144,10 @@ class TooltipViewManager {
8154
8144
  }
8155
8145
  }
8156
8146
  for (let t of this.tooltipViews)
8157
- if (tooltipViews.indexOf(t) < 0)
8147
+ if (tooltipViews.indexOf(t) < 0) {
8158
8148
  t.dom.remove();
8149
+ (_a = t.destroy) === null || _a === void 0 ? void 0 : _a.call(t);
8150
+ }
8159
8151
  this.input = input;
8160
8152
  this.tooltips = tooltips;
8161
8153
  this.tooltipViews = tooltipViews;
@@ -8274,11 +8266,13 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
8274
8266
  return tooltipView;
8275
8267
  }
8276
8268
  destroy() {
8277
- var _a;
8269
+ var _a, _b;
8278
8270
  this.view.win.removeEventListener("resize", this.measureSoon);
8279
- for (let { dom } of this.manager.tooltipViews)
8280
- dom.remove();
8281
- (_a = this.intersectionObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
8271
+ for (let tooltipView of this.manager.tooltipViews) {
8272
+ tooltipView.dom.remove();
8273
+ (_a = tooltipView.destroy) === null || _a === void 0 ? void 0 : _a.call(tooltipView);
8274
+ }
8275
+ (_b = this.intersectionObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
8282
8276
  clearTimeout(this.measureTimeout);
8283
8277
  }
8284
8278
  readMeasure() {
package/dist/index.d.ts CHANGED
@@ -1579,6 +1579,11 @@ interface TooltipView {
1579
1579
  */
1580
1580
  update?(update: ViewUpdate): void;
1581
1581
  /**
1582
+ Called when the tooltip is removed from the editor or the editor
1583
+ is destroyed.
1584
+ */
1585
+ destroy?(): void;
1586
+ /**
1582
1587
  Called when the tooltip has been (re)positioned.
1583
1588
  */
1584
1589
  positioned?(): void;
package/dist/index.js CHANGED
@@ -1050,57 +1050,31 @@ function joinInlineInto(parent, view, open) {
1050
1050
  parent.length += view.length;
1051
1051
  }
1052
1052
  function coordsInChildren(view, pos, side) {
1053
- if (!view.children.length)
1054
- return fallbackRect(view);
1055
- return (side <= 0 ? coordsInChildrenBefore : coordsInChildrenAfter)(view, pos);
1056
- }
1057
- function coordsInChildrenBefore(view, pos) {
1058
- // Find the last leaf in the tree that touches pos and doesn't have getSide() > 0
1059
- let found = null, foundPos = -1;
1053
+ let before = null, beforePos = -1, after = null, afterPos = -1;
1060
1054
  function scan(view, pos) {
1061
1055
  for (let i = 0, off = 0; i < view.children.length && off <= pos; i++) {
1062
1056
  let child = view.children[i], end = off + child.length;
1063
1057
  if (end >= pos) {
1064
1058
  if (child.children.length) {
1065
- if (scan(child, pos - off))
1066
- return true;
1059
+ scan(child, pos - off);
1067
1060
  }
1068
- else if (end >= pos) {
1069
- if (end == pos && child.getSide() > 0)
1070
- return true;
1071
- found = child;
1072
- foundPos = pos - off;
1061
+ else if (!after && (end > pos || off == end && child.getSide() > 0)) {
1062
+ after = child;
1063
+ afterPos = pos - off;
1073
1064
  }
1074
- }
1075
- off = end;
1076
- }
1077
- }
1078
- scan(view, pos);
1079
- return found ? found.coordsAt(Math.max(0, foundPos), -1) : coordsInChildrenAfter(view, pos);
1080
- }
1081
- function coordsInChildrenAfter(view, pos) {
1082
- // Find the first leaf in the tree that touches pos and doesn't have getSide() < 0
1083
- let found = null, foundPos = -1;
1084
- function scan(view, pos) {
1085
- for (let i = view.children.length - 1, off = view.length; i >= 0 && off >= pos; i--) {
1086
- let child = view.children[i];
1087
- off -= child.length;
1088
- if (off <= pos) {
1089
- if (child.children.length) {
1090
- if (scan(child, pos - off))
1091
- return true;
1092
- }
1093
- else if (off <= pos) {
1094
- if (off == pos && child.getSide() < 0)
1095
- return true;
1096
- found = child;
1097
- foundPos = pos - off;
1065
+ else if (off < pos || (off == end && child.getSide() < 0)) {
1066
+ before = child;
1067
+ beforePos = pos - off;
1098
1068
  }
1099
1069
  }
1070
+ off = end;
1100
1071
  }
1101
1072
  }
1102
1073
  scan(view, pos);
1103
- return found ? found.coordsAt(Math.max(0, foundPos), 1) : coordsInChildrenBefore(view, pos);
1074
+ let target = (side < 0 ? before : after) || before || after;
1075
+ if (target)
1076
+ return target.coordsAt(Math.max(0, target == before ? beforePos : afterPos), side);
1077
+ return fallbackRect(view);
1104
1078
  }
1105
1079
  function fallbackRect(view) {
1106
1080
  let last = view.dom.lastChild;
@@ -5565,6 +5539,15 @@ function applyDOMChange(view, domChange) {
5565
5539
  newSel = EditorSelection.single(newSel.main.anchor - 1, newSel.main.head - 1);
5566
5540
  change = { from: sel.from, to: sel.to, insert: Text.of([" "]) };
5567
5541
  }
5542
+ else if (browser.chrome && change && change.from == change.to && change.from == sel.head &&
5543
+ change.insert.toString() == "\n " && view.lineWrapping) {
5544
+ // In Chrome, if you insert a space at the start of a wrapped
5545
+ // line, it will actually insert a newline and a space, causing a
5546
+ // bogus new line to be created in CodeMirror (#968)
5547
+ if (newSel)
5548
+ newSel = EditorSelection.single(newSel.main.anchor - 1, newSel.main.head - 1);
5549
+ change = { from: sel.from, to: sel.to, insert: Text.of([" "]) };
5550
+ }
5568
5551
  if (change) {
5569
5552
  let startState = view.state;
5570
5553
  if (browser.ios && view.inputState.flushIOSKey(view))
@@ -6489,17 +6472,19 @@ class EditorView {
6489
6472
  logException(this.state, e);
6490
6473
  }
6491
6474
  }
6492
- if (this.viewState.scrollTarget) {
6493
- this.docView.scrollIntoView(this.viewState.scrollTarget);
6494
- this.viewState.scrollTarget = null;
6495
- scrolled = true;
6496
- }
6497
- else {
6498
- let diff = this.viewState.lineBlockAt(refBlock.from).top - refBlock.top;
6499
- if (diff > 1 || diff < -1) {
6500
- this.scrollDOM.scrollTop += diff;
6475
+ if (this.viewState.editorHeight) {
6476
+ if (this.viewState.scrollTarget) {
6477
+ this.docView.scrollIntoView(this.viewState.scrollTarget);
6478
+ this.viewState.scrollTarget = null;
6501
6479
  scrolled = true;
6502
6480
  }
6481
+ else {
6482
+ let diff = this.viewState.lineBlockAt(refBlock.from).top - refBlock.top;
6483
+ if (diff > 1 || diff < -1) {
6484
+ this.scrollDOM.scrollTop += diff;
6485
+ scrolled = true;
6486
+ }
6487
+ }
6503
6488
  }
6504
6489
  if (redrawn)
6505
6490
  this.docView.updateSelection(true);
@@ -7905,7 +7890,8 @@ const plugin = /*@__PURE__*/ViewPlugin.fromClass(class {
7905
7890
  this.attrs = { style: "padding-bottom: 1000px" };
7906
7891
  }
7907
7892
  update(update) {
7908
- let height = update.view.viewState.editorHeight - update.view.defaultLineHeight;
7893
+ let { view } = update;
7894
+ let height = view.viewState.editorHeight - view.defaultLineHeight - view.documentPadding.top - 0.5;
7909
7895
  if (height != this.height) {
7910
7896
  this.height = height;
7911
7897
  this.attrs = { style: `padding-bottom: ${height}px` };
@@ -8007,7 +7993,10 @@ function rectangleFor(state, a, b) {
8007
7993
  for (let i = startLine; i <= endLine; i++) {
8008
7994
  let line = state.doc.line(i);
8009
7995
  let start = findColumn(line.text, startCol, state.tabSize, true);
8010
- if (start > -1) {
7996
+ if (start < 0) {
7997
+ ranges.push(EditorSelection.cursor(line.to));
7998
+ }
7999
+ else {
8011
8000
  let end = findColumn(line.text, endCol, state.tabSize);
8012
8001
  ranges.push(EditorSelection.range(line.from + start, line.from + end));
8013
8002
  }
@@ -8119,6 +8108,7 @@ class TooltipViewManager {
8119
8108
  this.tooltipViews = this.tooltips.map(createTooltipView);
8120
8109
  }
8121
8110
  update(update) {
8111
+ var _a;
8122
8112
  let input = update.state.facet(this.facet);
8123
8113
  let tooltips = input.filter(x => x);
8124
8114
  if (input === this.input) {
@@ -8147,8 +8137,10 @@ class TooltipViewManager {
8147
8137
  }
8148
8138
  }
8149
8139
  for (let t of this.tooltipViews)
8150
- if (tooltipViews.indexOf(t) < 0)
8140
+ if (tooltipViews.indexOf(t) < 0) {
8151
8141
  t.dom.remove();
8142
+ (_a = t.destroy) === null || _a === void 0 ? void 0 : _a.call(t);
8143
+ }
8152
8144
  this.input = input;
8153
8145
  this.tooltips = tooltips;
8154
8146
  this.tooltipViews = tooltipViews;
@@ -8267,11 +8259,13 @@ const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
8267
8259
  return tooltipView;
8268
8260
  }
8269
8261
  destroy() {
8270
- var _a;
8262
+ var _a, _b;
8271
8263
  this.view.win.removeEventListener("resize", this.measureSoon);
8272
- for (let { dom } of this.manager.tooltipViews)
8273
- dom.remove();
8274
- (_a = this.intersectionObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
8264
+ for (let tooltipView of this.manager.tooltipViews) {
8265
+ tooltipView.dom.remove();
8266
+ (_a = tooltipView.destroy) === null || _a === void 0 ? void 0 : _a.call(tooltipView);
8267
+ }
8268
+ (_b = this.intersectionObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
8275
8269
  clearTimeout(this.measureTimeout);
8276
8270
  }
8277
8271
  readMeasure() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.3.1",
3
+ "version": "6.4.0",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",