@codemirror/view 6.32.0 → 6.34.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,31 @@
1
+ ## 6.34.0 (2024-09-25)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix an issue where the dots past the wrapping point were displayed incorrectly when using `highlightWhitespace` with a wrapped sequence of spaces.
6
+
7
+ Improve performance of documents displaying lots of highlighted spaces by using a CSS background instead of pseudo-element.
8
+
9
+ ### New features
10
+
11
+ `placeholder` now allows a function that constructs the placedholder DOM to be passed in, and uses `cloneNode` when a raw element is passed in, to prevent adding the same element to multiple editors.
12
+
13
+ ## 6.33.1 (2024-08-30)
14
+
15
+ ### Bug fixes
16
+
17
+ Work around odd behavior in Chrome's newly supported `caretPositionFromPoint` method, which could cause CodeMirror to crash with a null dereference.
18
+
19
+ ## 6.33.0 (2024-08-24)
20
+
21
+ ### Bug fixes
22
+
23
+ Make it easier to move the pointer over a hover tooltip with an arrow by not closing the tooltip when the pointer is moving over the gap for the arrow.
24
+
25
+ ### New features
26
+
27
+ The new `EditorView.clipboardInputFilter` and `clipboardOutputFilter` facets allow you to register filter functions that change text taken from or sent to the clipboard.
28
+
1
29
  ## 6.32.0 (2024-08-12)
2
30
 
3
31
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -20,12 +20,6 @@ function getSelection(root) {
20
20
  function contains(dom, node) {
21
21
  return node ? dom == node || dom.contains(node.nodeType != 1 ? node.parentNode : node) : false;
22
22
  }
23
- function deepActiveElement(doc) {
24
- let elt = doc.activeElement;
25
- while (elt && elt.shadowRoot)
26
- elt = elt.shadowRoot.activeElement;
27
- return elt;
28
- }
29
23
  function hasSelection(dom, selection) {
30
24
  if (!selection.anchorNode)
31
25
  return false;
@@ -2365,6 +2359,8 @@ const exceptionSink = state.Facet.define();
2365
2359
  const updateListener = state.Facet.define();
2366
2360
  const inputHandler = state.Facet.define();
2367
2361
  const focusChangeEffect = state.Facet.define();
2362
+ const clipboardInputFilter = state.Facet.define();
2363
+ const clipboardOutputFilter = state.Facet.define();
2368
2364
  const perLineTextDirection = state.Facet.define({
2369
2365
  combine: values => values.some(x => x)
2370
2366
  });
@@ -3579,6 +3575,11 @@ function posAtCoords(view, coords, precise, bias = -1) {
3579
3575
  node = undefined;
3580
3576
  }
3581
3577
  }
3578
+ // Chrome will return offsets into <input> elements without child
3579
+ // nodes, which will lead to a null deref below, so clip the
3580
+ // offset to the node size.
3581
+ if (node)
3582
+ offset = Math.min(maxOffset(node), offset);
3582
3583
  }
3583
3584
  // No luck, do our own (potentially expensive) search
3584
3585
  if (!node || !view.docView.dom.contains(node)) {
@@ -4128,7 +4129,6 @@ function selectionFromPoints(points, base) {
4128
4129
  return anchor > -1 && head > -1 ? state.EditorSelection.single(anchor + base, head + base) : null;
4129
4130
  }
4130
4131
 
4131
- // This will also be where dragging info and such goes
4132
4132
  class InputState {
4133
4133
  setSelectionOrigin(origin) {
4134
4134
  this.lastSelectionOrigin = origin;
@@ -4546,7 +4546,13 @@ function capturePaste(view) {
4546
4546
  doPaste(view, target.value);
4547
4547
  }, 50);
4548
4548
  }
4549
+ function textFilter(state, facet, text) {
4550
+ for (let filter of state.facet(facet))
4551
+ text = filter(text, state);
4552
+ return text;
4553
+ }
4549
4554
  function doPaste(view, input) {
4555
+ input = textFilter(view.state, clipboardInputFilter, input);
4550
4556
  let { state: state$1 } = view, changes, i = 1, text = state$1.toText(input);
4551
4557
  let byLine = text.lines == state$1.selection.ranges.length;
4552
4558
  let linewise = lastLinewiseCopy != null && state$1.selection.ranges.every(r => r.empty) && lastLinewiseCopy == text.toString();
@@ -4731,7 +4737,7 @@ handlers.dragstart = (view, event) => {
4731
4737
  inputState.mouseSelection.dragging = true;
4732
4738
  inputState.draggedContent = range;
4733
4739
  if (event.dataTransfer) {
4734
- event.dataTransfer.setData("Text", view.state.sliceDoc(range.from, range.to));
4740
+ event.dataTransfer.setData("Text", textFilter(view.state, clipboardOutputFilter, view.state.sliceDoc(range.from, range.to)));
4735
4741
  event.dataTransfer.effectAllowed = "copyMove";
4736
4742
  }
4737
4743
  return false;
@@ -4741,6 +4747,7 @@ handlers.dragend = view => {
4741
4747
  return false;
4742
4748
  };
4743
4749
  function dropText(view, event, text, direct) {
4750
+ text = textFilter(view.state, clipboardInputFilter, text);
4744
4751
  if (!text)
4745
4752
  return;
4746
4753
  let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
@@ -4841,7 +4848,7 @@ function copiedRange(state) {
4841
4848
  }
4842
4849
  linewise = true;
4843
4850
  }
4844
- return { text: content.join(state.lineBreak), ranges, linewise };
4851
+ return { text: textFilter(state, clipboardOutputFilter, content.join(state.lineBreak)), ranges, linewise };
4845
4852
  }
4846
4853
  let lastLinewiseCopy = null;
4847
4854
  handlers.copy = handlers.cut = (view, event) => {
@@ -6565,11 +6572,9 @@ const baseTheme$1 = buildTheme("." + baseThemeID, {
6565
6572
  display: "inline-block",
6566
6573
  verticalAlign: "top",
6567
6574
  },
6568
- ".cm-highlightSpace:before": {
6569
- content: "attr(data-display)",
6570
- position: "absolute",
6571
- pointerEvents: "none",
6572
- color: "#888"
6575
+ ".cm-highlightSpace": {
6576
+ backgroundImage: "radial-gradient(circle at 50% 55%, #aaa 20%, transparent 5%)",
6577
+ backgroundPosition: "center",
6573
6578
  },
6574
6579
  ".cm-highlightTab": {
6575
6580
  backgroundImage: `url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="200" height="20"><path stroke="%23888" stroke-width="1" fill="none" d="M1 10H196L190 5M190 15L196 10M197 4L197 16"/></svg>')`,
@@ -6767,7 +6772,7 @@ class DOMObserver {
6767
6772
  if (!this.readSelectionRange() || this.delayedAndroidKey)
6768
6773
  return;
6769
6774
  let { view } = this, sel = this.selectionRange;
6770
- if (view.state.facet(editable) ? view.root.activeElement != this.dom : !hasSelection(view.dom, sel))
6775
+ if (view.state.facet(editable) ? view.root.activeElement != this.dom : !hasSelection(this.dom, sel))
6771
6776
  return;
6772
6777
  let context = sel.anchorNode && view.docView.nearest(sel.anchorNode);
6773
6778
  if (context && context.ignoreEvent(event)) {
@@ -6795,7 +6800,7 @@ class DOMObserver {
6795
6800
  if (!selection)
6796
6801
  return false;
6797
6802
  let range = browser.safari && view.root.nodeType == 11 &&
6798
- deepActiveElement(this.dom.ownerDocument) == this.dom &&
6803
+ view.root.activeElement == this.dom &&
6799
6804
  safariSelectionRangeHack(this.view, selection) || selection;
6800
6805
  if (!range || this.selectionRange.eq(range))
6801
6806
  return false;
@@ -8264,6 +8269,15 @@ dispatching the custom behavior as a separate transaction.
8264
8269
  */
8265
8270
  EditorView.inputHandler = inputHandler;
8266
8271
  /**
8272
+ Functions provided in this facet will be used to transform text
8273
+ pasted or dropped into the editor.
8274
+ */
8275
+ EditorView.clipboardInputFilter = clipboardInputFilter;
8276
+ /**
8277
+ Transform text copied or dragged from the editor.
8278
+ */
8279
+ EditorView.clipboardOutputFilter = clipboardOutputFilter;
8280
+ /**
8267
8281
  Scroll handlers can override how things are scrolled into view.
8268
8282
  If they return `true`, no further handling happens for the
8269
8283
  scrolling. If they return false, the default scroll behavior is
@@ -9474,11 +9488,13 @@ class Placeholder extends WidgetType {
9474
9488
  super();
9475
9489
  this.content = content;
9476
9490
  }
9477
- toDOM() {
9491
+ toDOM(view) {
9478
9492
  let wrap = document.createElement("span");
9479
9493
  wrap.className = "cm-placeholder";
9480
9494
  wrap.style.pointerEvents = "none";
9481
- wrap.appendChild(typeof this.content == "string" ? document.createTextNode(this.content) : this.content);
9495
+ wrap.appendChild(typeof this.content == "string" ? document.createTextNode(this.content) :
9496
+ typeof this.content == "function" ? this.content(view) :
9497
+ this.content.cloneNode(true));
9482
9498
  if (typeof this.content == "string")
9483
9499
  wrap.setAttribute("aria-label", "placeholder " + this.content);
9484
9500
  else
@@ -10235,9 +10251,14 @@ class HoverPlugin {
10235
10251
  }
10236
10252
  const tooltipMargin = 4;
10237
10253
  function isInTooltip(tooltip, event) {
10238
- let rect = tooltip.getBoundingClientRect();
10239
- return event.clientX >= rect.left - tooltipMargin && event.clientX <= rect.right + tooltipMargin &&
10240
- event.clientY >= rect.top - tooltipMargin && event.clientY <= rect.bottom + tooltipMargin;
10254
+ let { left, right, top, bottom } = tooltip.getBoundingClientRect(), arrow;
10255
+ if (arrow = tooltip.querySelector(".cm-tooltip-arrow")) {
10256
+ let arrowRect = arrow.getBoundingClientRect();
10257
+ top = Math.min(arrowRect.top, top);
10258
+ bottom = Math.max(arrowRect.bottom, bottom);
10259
+ }
10260
+ return event.clientX >= left - tooltipMargin && event.clientX <= right + tooltipMargin &&
10261
+ event.clientY >= top - tooltipMargin && event.clientY <= bottom + tooltipMargin;
10241
10262
  }
10242
10263
  function isOverRange(view, from, to, x, y, margin) {
10243
10264
  let rect = view.scrollDOM.getBoundingClientRect();
@@ -11008,20 +11029,6 @@ function highlightActiveLineGutter() {
11008
11029
  return activeLineGutterHighlighter;
11009
11030
  }
11010
11031
 
11011
- const WhitespaceDeco = new Map();
11012
- function getWhitespaceDeco(space) {
11013
- let deco = WhitespaceDeco.get(space);
11014
- if (!deco)
11015
- WhitespaceDeco.set(space, deco = Decoration.mark({
11016
- attributes: space === "\t" ? {
11017
- class: "cm-highlightTab",
11018
- } : {
11019
- class: "cm-highlightSpace",
11020
- "data-display": space.replace(/ /g, "·")
11021
- }
11022
- }));
11023
- return deco;
11024
- }
11025
11032
  function matcher(decorator) {
11026
11033
  return ViewPlugin.define(view => ({
11027
11034
  decorations: decorator.createDeco(view),
@@ -11032,9 +11039,11 @@ function matcher(decorator) {
11032
11039
  decorations: v => v.decorations
11033
11040
  });
11034
11041
  }
11042
+ const tabDeco = Decoration.mark({ class: "cm-highlightTab" });
11043
+ const spaceDeco = Decoration.mark({ class: "cm-highlightSpace" });
11035
11044
  const whitespaceHighlighter = matcher(new MatchDecorator({
11036
- regexp: /\t| +/g,
11037
- decoration: match => getWhitespaceDeco(match[0]),
11045
+ regexp: /\t| /g,
11046
+ decoration: match => match[0] == "\t" ? tabDeco : spaceDeco,
11038
11047
  boundary: /\S/,
11039
11048
  }));
11040
11049
  /**
package/dist/index.d.cts CHANGED
@@ -1146,6 +1146,15 @@ declare class EditorView {
1146
1146
  */
1147
1147
  static inputHandler: Facet<(view: EditorView, from: number, to: number, text: string, insert: () => Transaction) => boolean, readonly ((view: EditorView, from: number, to: number, text: string, insert: () => Transaction) => boolean)[]>;
1148
1148
  /**
1149
+ Functions provided in this facet will be used to transform text
1150
+ pasted or dropped into the editor.
1151
+ */
1152
+ static clipboardInputFilter: Facet<(text: string, state: EditorState) => string, readonly ((text: string, state: EditorState) => string)[]>;
1153
+ /**
1154
+ Transform text copied or dragged from the editor.
1155
+ */
1156
+ static clipboardOutputFilter: Facet<(text: string, state: EditorState) => string, readonly ((text: string, state: EditorState) => string)[]>;
1157
+ /**
1149
1158
  Scroll handlers can override how things are scrolled into view.
1150
1159
  If they return `true`, no further handling happens for the
1151
1160
  scrolling. If they return false, the default scroll behavior is
@@ -1576,7 +1585,7 @@ declare function highlightActiveLine(): Extension;
1576
1585
  Extension that enables a placeholder—a piece of example content
1577
1586
  to show when the editor is empty.
1578
1587
  */
1579
- declare function placeholder(content: string | HTMLElement): Extension;
1588
+ declare function placeholder(content: string | HTMLElement | ((view: EditorView) => HTMLElement)): Extension;
1580
1589
 
1581
1590
  /**
1582
1591
  Markers shown in a [layer](https://codemirror.net/6/docs/ref/#view.layer) must conform to this
package/dist/index.d.ts CHANGED
@@ -1146,6 +1146,15 @@ declare class EditorView {
1146
1146
  */
1147
1147
  static inputHandler: Facet<(view: EditorView, from: number, to: number, text: string, insert: () => Transaction) => boolean, readonly ((view: EditorView, from: number, to: number, text: string, insert: () => Transaction) => boolean)[]>;
1148
1148
  /**
1149
+ Functions provided in this facet will be used to transform text
1150
+ pasted or dropped into the editor.
1151
+ */
1152
+ static clipboardInputFilter: Facet<(text: string, state: EditorState) => string, readonly ((text: string, state: EditorState) => string)[]>;
1153
+ /**
1154
+ Transform text copied or dragged from the editor.
1155
+ */
1156
+ static clipboardOutputFilter: Facet<(text: string, state: EditorState) => string, readonly ((text: string, state: EditorState) => string)[]>;
1157
+ /**
1149
1158
  Scroll handlers can override how things are scrolled into view.
1150
1159
  If they return `true`, no further handling happens for the
1151
1160
  scrolling. If they return false, the default scroll behavior is
@@ -1576,7 +1585,7 @@ declare function highlightActiveLine(): Extension;
1576
1585
  Extension that enables a placeholder—a piece of example content
1577
1586
  to show when the editor is empty.
1578
1587
  */
1579
- declare function placeholder(content: string | HTMLElement): Extension;
1588
+ declare function placeholder(content: string | HTMLElement | ((view: EditorView) => HTMLElement)): Extension;
1580
1589
 
1581
1590
  /**
1582
1591
  Markers shown in a [layer](https://codemirror.net/6/docs/ref/#view.layer) must conform to this
package/dist/index.js CHANGED
@@ -18,12 +18,6 @@ function getSelection(root) {
18
18
  function contains(dom, node) {
19
19
  return node ? dom == node || dom.contains(node.nodeType != 1 ? node.parentNode : node) : false;
20
20
  }
21
- function deepActiveElement(doc) {
22
- let elt = doc.activeElement;
23
- while (elt && elt.shadowRoot)
24
- elt = elt.shadowRoot.activeElement;
25
- return elt;
26
- }
27
21
  function hasSelection(dom, selection) {
28
22
  if (!selection.anchorNode)
29
23
  return false;
@@ -2361,6 +2355,8 @@ const exceptionSink = /*@__PURE__*/Facet.define();
2361
2355
  const updateListener = /*@__PURE__*/Facet.define();
2362
2356
  const inputHandler = /*@__PURE__*/Facet.define();
2363
2357
  const focusChangeEffect = /*@__PURE__*/Facet.define();
2358
+ const clipboardInputFilter = /*@__PURE__*/Facet.define();
2359
+ const clipboardOutputFilter = /*@__PURE__*/Facet.define();
2364
2360
  const perLineTextDirection = /*@__PURE__*/Facet.define({
2365
2361
  combine: values => values.some(x => x)
2366
2362
  });
@@ -3575,6 +3571,11 @@ function posAtCoords(view, coords, precise, bias = -1) {
3575
3571
  node = undefined;
3576
3572
  }
3577
3573
  }
3574
+ // Chrome will return offsets into <input> elements without child
3575
+ // nodes, which will lead to a null deref below, so clip the
3576
+ // offset to the node size.
3577
+ if (node)
3578
+ offset = Math.min(maxOffset(node), offset);
3578
3579
  }
3579
3580
  // No luck, do our own (potentially expensive) search
3580
3581
  if (!node || !view.docView.dom.contains(node)) {
@@ -4124,7 +4125,6 @@ function selectionFromPoints(points, base) {
4124
4125
  return anchor > -1 && head > -1 ? EditorSelection.single(anchor + base, head + base) : null;
4125
4126
  }
4126
4127
 
4127
- // This will also be where dragging info and such goes
4128
4128
  class InputState {
4129
4129
  setSelectionOrigin(origin) {
4130
4130
  this.lastSelectionOrigin = origin;
@@ -4542,7 +4542,13 @@ function capturePaste(view) {
4542
4542
  doPaste(view, target.value);
4543
4543
  }, 50);
4544
4544
  }
4545
+ function textFilter(state, facet, text) {
4546
+ for (let filter of state.facet(facet))
4547
+ text = filter(text, state);
4548
+ return text;
4549
+ }
4545
4550
  function doPaste(view, input) {
4551
+ input = textFilter(view.state, clipboardInputFilter, input);
4546
4552
  let { state } = view, changes, i = 1, text = state.toText(input);
4547
4553
  let byLine = text.lines == state.selection.ranges.length;
4548
4554
  let linewise = lastLinewiseCopy != null && state.selection.ranges.every(r => r.empty) && lastLinewiseCopy == text.toString();
@@ -4727,7 +4733,7 @@ handlers.dragstart = (view, event) => {
4727
4733
  inputState.mouseSelection.dragging = true;
4728
4734
  inputState.draggedContent = range;
4729
4735
  if (event.dataTransfer) {
4730
- event.dataTransfer.setData("Text", view.state.sliceDoc(range.from, range.to));
4736
+ event.dataTransfer.setData("Text", textFilter(view.state, clipboardOutputFilter, view.state.sliceDoc(range.from, range.to)));
4731
4737
  event.dataTransfer.effectAllowed = "copyMove";
4732
4738
  }
4733
4739
  return false;
@@ -4737,6 +4743,7 @@ handlers.dragend = view => {
4737
4743
  return false;
4738
4744
  };
4739
4745
  function dropText(view, event, text, direct) {
4746
+ text = textFilter(view.state, clipboardInputFilter, text);
4740
4747
  if (!text)
4741
4748
  return;
4742
4749
  let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
@@ -4837,7 +4844,7 @@ function copiedRange(state) {
4837
4844
  }
4838
4845
  linewise = true;
4839
4846
  }
4840
- return { text: content.join(state.lineBreak), ranges, linewise };
4847
+ return { text: textFilter(state, clipboardOutputFilter, content.join(state.lineBreak)), ranges, linewise };
4841
4848
  }
4842
4849
  let lastLinewiseCopy = null;
4843
4850
  handlers.copy = handlers.cut = (view, event) => {
@@ -6560,11 +6567,9 @@ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
6560
6567
  display: "inline-block",
6561
6568
  verticalAlign: "top",
6562
6569
  },
6563
- ".cm-highlightSpace:before": {
6564
- content: "attr(data-display)",
6565
- position: "absolute",
6566
- pointerEvents: "none",
6567
- color: "#888"
6570
+ ".cm-highlightSpace": {
6571
+ backgroundImage: "radial-gradient(circle at 50% 55%, #aaa 20%, transparent 5%)",
6572
+ backgroundPosition: "center",
6568
6573
  },
6569
6574
  ".cm-highlightTab": {
6570
6575
  backgroundImage: `url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="200" height="20"><path stroke="%23888" stroke-width="1" fill="none" d="M1 10H196L190 5M190 15L196 10M197 4L197 16"/></svg>')`,
@@ -6762,7 +6767,7 @@ class DOMObserver {
6762
6767
  if (!this.readSelectionRange() || this.delayedAndroidKey)
6763
6768
  return;
6764
6769
  let { view } = this, sel = this.selectionRange;
6765
- if (view.state.facet(editable) ? view.root.activeElement != this.dom : !hasSelection(view.dom, sel))
6770
+ if (view.state.facet(editable) ? view.root.activeElement != this.dom : !hasSelection(this.dom, sel))
6766
6771
  return;
6767
6772
  let context = sel.anchorNode && view.docView.nearest(sel.anchorNode);
6768
6773
  if (context && context.ignoreEvent(event)) {
@@ -6790,7 +6795,7 @@ class DOMObserver {
6790
6795
  if (!selection)
6791
6796
  return false;
6792
6797
  let range = browser.safari && view.root.nodeType == 11 &&
6793
- deepActiveElement(this.dom.ownerDocument) == this.dom &&
6798
+ view.root.activeElement == this.dom &&
6794
6799
  safariSelectionRangeHack(this.view, selection) || selection;
6795
6800
  if (!range || this.selectionRange.eq(range))
6796
6801
  return false;
@@ -8259,6 +8264,15 @@ dispatching the custom behavior as a separate transaction.
8259
8264
  */
8260
8265
  EditorView.inputHandler = inputHandler;
8261
8266
  /**
8267
+ Functions provided in this facet will be used to transform text
8268
+ pasted or dropped into the editor.
8269
+ */
8270
+ EditorView.clipboardInputFilter = clipboardInputFilter;
8271
+ /**
8272
+ Transform text copied or dragged from the editor.
8273
+ */
8274
+ EditorView.clipboardOutputFilter = clipboardOutputFilter;
8275
+ /**
8262
8276
  Scroll handlers can override how things are scrolled into view.
8263
8277
  If they return `true`, no further handling happens for the
8264
8278
  scrolling. If they return false, the default scroll behavior is
@@ -9469,11 +9483,13 @@ class Placeholder extends WidgetType {
9469
9483
  super();
9470
9484
  this.content = content;
9471
9485
  }
9472
- toDOM() {
9486
+ toDOM(view) {
9473
9487
  let wrap = document.createElement("span");
9474
9488
  wrap.className = "cm-placeholder";
9475
9489
  wrap.style.pointerEvents = "none";
9476
- wrap.appendChild(typeof this.content == "string" ? document.createTextNode(this.content) : this.content);
9490
+ wrap.appendChild(typeof this.content == "string" ? document.createTextNode(this.content) :
9491
+ typeof this.content == "function" ? this.content(view) :
9492
+ this.content.cloneNode(true));
9477
9493
  if (typeof this.content == "string")
9478
9494
  wrap.setAttribute("aria-label", "placeholder " + this.content);
9479
9495
  else
@@ -10230,9 +10246,14 @@ class HoverPlugin {
10230
10246
  }
10231
10247
  const tooltipMargin = 4;
10232
10248
  function isInTooltip(tooltip, event) {
10233
- let rect = tooltip.getBoundingClientRect();
10234
- return event.clientX >= rect.left - tooltipMargin && event.clientX <= rect.right + tooltipMargin &&
10235
- event.clientY >= rect.top - tooltipMargin && event.clientY <= rect.bottom + tooltipMargin;
10249
+ let { left, right, top, bottom } = tooltip.getBoundingClientRect(), arrow;
10250
+ if (arrow = tooltip.querySelector(".cm-tooltip-arrow")) {
10251
+ let arrowRect = arrow.getBoundingClientRect();
10252
+ top = Math.min(arrowRect.top, top);
10253
+ bottom = Math.max(arrowRect.bottom, bottom);
10254
+ }
10255
+ return event.clientX >= left - tooltipMargin && event.clientX <= right + tooltipMargin &&
10256
+ event.clientY >= top - tooltipMargin && event.clientY <= bottom + tooltipMargin;
10236
10257
  }
10237
10258
  function isOverRange(view, from, to, x, y, margin) {
10238
10259
  let rect = view.scrollDOM.getBoundingClientRect();
@@ -11003,20 +11024,6 @@ function highlightActiveLineGutter() {
11003
11024
  return activeLineGutterHighlighter;
11004
11025
  }
11005
11026
 
11006
- const WhitespaceDeco = /*@__PURE__*/new Map();
11007
- function getWhitespaceDeco(space) {
11008
- let deco = WhitespaceDeco.get(space);
11009
- if (!deco)
11010
- WhitespaceDeco.set(space, deco = Decoration.mark({
11011
- attributes: space === "\t" ? {
11012
- class: "cm-highlightTab",
11013
- } : {
11014
- class: "cm-highlightSpace",
11015
- "data-display": space.replace(/ /g, "·")
11016
- }
11017
- }));
11018
- return deco;
11019
- }
11020
11027
  function matcher(decorator) {
11021
11028
  return ViewPlugin.define(view => ({
11022
11029
  decorations: decorator.createDeco(view),
@@ -11027,9 +11034,11 @@ function matcher(decorator) {
11027
11034
  decorations: v => v.decorations
11028
11035
  });
11029
11036
  }
11037
+ const tabDeco = /*@__PURE__*/Decoration.mark({ class: "cm-highlightTab" });
11038
+ const spaceDeco = /*@__PURE__*/Decoration.mark({ class: "cm-highlightSpace" });
11030
11039
  const whitespaceHighlighter = /*@__PURE__*/matcher(/*@__PURE__*/new MatchDecorator({
11031
- regexp: /\t| +/g,
11032
- decoration: match => getWhitespaceDeco(match[0]),
11040
+ regexp: /\t| /g,
11041
+ decoration: match => match[0] == "\t" ? tabDeco : spaceDeco,
11033
11042
  boundary: /\S/,
11034
11043
  }));
11035
11044
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.32.0",
3
+ "version": "6.34.0",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",