@codemirror/view 6.6.0 → 6.7.1

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/dist/index.js CHANGED
@@ -187,6 +187,26 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
187
187
  }
188
188
  }
189
189
  }
190
+ function scrollableParent(dom) {
191
+ let doc = dom.ownerDocument;
192
+ for (let cur = dom.parentNode; cur;) {
193
+ if (cur == doc.body) {
194
+ break;
195
+ }
196
+ else if (cur.nodeType == 1) {
197
+ if (cur.scrollHeight > cur.clientHeight || cur.scrollWidth > cur.clientWidth)
198
+ return cur;
199
+ cur = cur.assignedSlot || cur.parentNode;
200
+ }
201
+ else if (cur.nodeType == 11) {
202
+ cur = cur.host;
203
+ }
204
+ else {
205
+ break;
206
+ }
207
+ }
208
+ return null;
209
+ }
190
210
  class DOMSelectionState {
191
211
  constructor() {
192
212
  this.anchorNode = null;
@@ -1598,6 +1618,7 @@ class ContentBuilder {
1598
1618
  this.curLine = null;
1599
1619
  this.breakAtStart = 0;
1600
1620
  this.pendingBuffer = 0 /* Buf.No */;
1621
+ this.bufferMarks = [];
1601
1622
  // Set to false directly after a widget that covers the position after it
1602
1623
  this.atCursorPos = true;
1603
1624
  this.openStart = -1;
@@ -1620,20 +1641,20 @@ class ContentBuilder {
1620
1641
  }
1621
1642
  return this.curLine;
1622
1643
  }
1623
- flushBuffer(active) {
1644
+ flushBuffer(active = this.bufferMarks) {
1624
1645
  if (this.pendingBuffer) {
1625
1646
  this.curLine.append(wrapMarks(new WidgetBufferView(-1), active), active.length);
1626
1647
  this.pendingBuffer = 0 /* Buf.No */;
1627
1648
  }
1628
1649
  }
1629
1650
  addBlockWidget(view) {
1630
- this.flushBuffer([]);
1651
+ this.flushBuffer();
1631
1652
  this.curLine = null;
1632
1653
  this.content.push(view);
1633
1654
  }
1634
1655
  finish(openEnd) {
1635
- if (!openEnd)
1636
- this.flushBuffer([]);
1656
+ if (this.pendingBuffer && openEnd <= this.bufferMarks.length)
1657
+ this.flushBuffer();
1637
1658
  else
1638
1659
  this.pendingBuffer = 0 /* Buf.No */;
1639
1660
  if (!this.posCovered())
@@ -1653,8 +1674,9 @@ class ContentBuilder {
1653
1674
  this.content[this.content.length - 1].breakAfter = 1;
1654
1675
  else
1655
1676
  this.breakAtStart = 1;
1656
- this.flushBuffer([]);
1677
+ this.flushBuffer();
1657
1678
  this.curLine = null;
1679
+ this.atCursorPos = true;
1658
1680
  length--;
1659
1681
  continue;
1660
1682
  }
@@ -1696,7 +1718,7 @@ class ContentBuilder {
1696
1718
  else {
1697
1719
  let view = WidgetView.create(deco.widget || new NullWidget("span"), len, len ? 0 : deco.startSide);
1698
1720
  let cursorBefore = this.atCursorPos && !view.isEditable && openStart <= active.length && (from < to || deco.startSide > 0);
1699
- let cursorAfter = !view.isEditable && (from < to || deco.startSide <= 0);
1721
+ let cursorAfter = !view.isEditable && (from < to || openStart > active.length || deco.startSide <= 0);
1700
1722
  let line = this.getLine();
1701
1723
  if (this.pendingBuffer == 2 /* Buf.IfCursor */ && !cursorBefore)
1702
1724
  this.pendingBuffer = 0 /* Buf.No */;
@@ -1707,7 +1729,9 @@ class ContentBuilder {
1707
1729
  }
1708
1730
  line.append(wrapMarks(view, active), openStart);
1709
1731
  this.atCursorPos = cursorAfter;
1710
- this.pendingBuffer = !cursorAfter ? 0 /* Buf.No */ : from < to ? 1 /* Buf.Yes */ : 2 /* Buf.IfCursor */;
1732
+ this.pendingBuffer = !cursorAfter ? 0 /* Buf.No */ : from < to || openStart > active.length ? 1 /* Buf.Yes */ : 2 /* Buf.IfCursor */;
1733
+ if (this.pendingBuffer)
1734
+ this.bufferMarks = active.slice();
1711
1735
  }
1712
1736
  }
1713
1737
  else if (this.doc.lineAt(this.pos).from == this.pos) { // Line decoration
@@ -3381,22 +3405,30 @@ class InputState {
3381
3405
  this.compositionFirstChange = null;
3382
3406
  this.compositionEndedAt = 0;
3383
3407
  this.mouseSelection = null;
3408
+ let handleEvent = (handler, event) => {
3409
+ if (this.ignoreDuringComposition(event))
3410
+ return;
3411
+ if (event.type == "keydown" && this.keydown(view, event))
3412
+ return;
3413
+ if (this.mustFlushObserver(event))
3414
+ view.observer.forceFlush();
3415
+ if (this.runCustomHandlers(event.type, view, event))
3416
+ event.preventDefault();
3417
+ else
3418
+ handler(view, event);
3419
+ };
3384
3420
  for (let type in handlers) {
3385
3421
  let handler = handlers[type];
3386
- view.contentDOM.addEventListener(type, (event) => {
3387
- if (!eventBelongsToEditor(view, event) || this.ignoreDuringComposition(event))
3388
- return;
3389
- if (type == "keydown" && this.keydown(view, event))
3390
- return;
3391
- if (this.mustFlushObserver(event))
3392
- view.observer.forceFlush();
3393
- if (this.runCustomHandlers(type, view, event))
3394
- event.preventDefault();
3395
- else
3396
- handler(view, event);
3422
+ view.contentDOM.addEventListener(type, event => {
3423
+ if (eventBelongsToEditor(view, event))
3424
+ handleEvent(handler, event);
3397
3425
  }, handlerOptions[type]);
3398
3426
  this.registeredEvents.push(type);
3399
3427
  }
3428
+ view.scrollDOM.addEventListener("mousedown", (event) => {
3429
+ if (event.target == view.scrollDOM)
3430
+ handleEvent(handlers.mousedown, event);
3431
+ });
3400
3432
  if (browser.chrome && browser.chrome_version == 102) { // FIXME remove at some point
3401
3433
  // On Chrome 102, viewport updates somehow stop wheel-based
3402
3434
  // scrolling. Turning off pointer events during the scroll seems
@@ -3553,12 +3585,18 @@ const PendingKeys = [
3553
3585
  const EmacsyPendingKeys = "dthko";
3554
3586
  // Key codes for modifier keys
3555
3587
  const modifierCodes = [16, 17, 18, 20, 91, 92, 224, 225];
3588
+ function dragScrollSpeed(dist) {
3589
+ return dist * 0.7 + 8;
3590
+ }
3556
3591
  class MouseSelection {
3557
3592
  constructor(view, startEvent, style, mustSelect) {
3558
3593
  this.view = view;
3559
3594
  this.style = style;
3560
3595
  this.mustSelect = mustSelect;
3596
+ this.scrollSpeed = { x: 0, y: 0 };
3597
+ this.scrolling = -1;
3561
3598
  this.lastEvent = startEvent;
3599
+ this.scrollParent = scrollableParent(view.contentDOM);
3562
3600
  let doc = view.contentDOM.ownerDocument;
3563
3601
  doc.addEventListener("mousemove", this.move = this.move.bind(this));
3564
3602
  doc.addEventListener("mouseup", this.up = this.up.bind(this));
@@ -3574,11 +3612,24 @@ class MouseSelection {
3574
3612
  }
3575
3613
  }
3576
3614
  move(event) {
3615
+ var _a;
3577
3616
  if (event.buttons == 0)
3578
3617
  return this.destroy();
3579
3618
  if (this.dragging !== false)
3580
3619
  return;
3581
3620
  this.select(this.lastEvent = event);
3621
+ let sx = 0, sy = 0;
3622
+ let rect = ((_a = this.scrollParent) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect())
3623
+ || { left: 0, top: 0, right: this.view.win.innerWidth, bottom: this.view.win.innerHeight };
3624
+ if (event.clientX <= rect.left)
3625
+ sx = -dragScrollSpeed(rect.left - event.clientX);
3626
+ else if (event.clientX >= rect.right)
3627
+ sx = dragScrollSpeed(event.clientX - rect.right);
3628
+ if (event.clientY <= rect.top)
3629
+ sy = -dragScrollSpeed(rect.top - event.clientY);
3630
+ else if (event.clientY >= rect.bottom)
3631
+ sy = dragScrollSpeed(event.clientY - rect.bottom);
3632
+ this.setScrollSpeed(sx, sy);
3582
3633
  }
3583
3634
  up(event) {
3584
3635
  if (this.dragging == null)
@@ -3588,19 +3639,41 @@ class MouseSelection {
3588
3639
  this.destroy();
3589
3640
  }
3590
3641
  destroy() {
3642
+ this.setScrollSpeed(0, 0);
3591
3643
  let doc = this.view.contentDOM.ownerDocument;
3592
3644
  doc.removeEventListener("mousemove", this.move);
3593
3645
  doc.removeEventListener("mouseup", this.up);
3594
3646
  this.view.inputState.mouseSelection = null;
3595
3647
  }
3648
+ setScrollSpeed(sx, sy) {
3649
+ this.scrollSpeed = { x: sx, y: sy };
3650
+ if (sx || sy) {
3651
+ if (this.scrolling < 0)
3652
+ this.scrolling = setInterval(() => this.scroll(), 50);
3653
+ }
3654
+ else if (this.scrolling > -1) {
3655
+ clearInterval(this.scrolling);
3656
+ this.scrolling = -1;
3657
+ }
3658
+ }
3659
+ scroll() {
3660
+ if (this.scrollParent) {
3661
+ this.scrollParent.scrollLeft += this.scrollSpeed.x;
3662
+ this.scrollParent.scrollTop += this.scrollSpeed.y;
3663
+ }
3664
+ else {
3665
+ this.view.win.scrollBy(this.scrollSpeed.x, this.scrollSpeed.y);
3666
+ }
3667
+ if (this.dragging === false)
3668
+ this.select(this.lastEvent);
3669
+ }
3596
3670
  select(event) {
3597
3671
  let selection = this.style.get(event, this.extend, this.multiple);
3598
3672
  if (this.mustSelect || !selection.eq(this.view.state.selection) ||
3599
3673
  selection.main.assoc != this.view.state.selection.main.assoc)
3600
3674
  this.view.dispatch({
3601
3675
  selection,
3602
- userEvent: "select.pointer",
3603
- scrollIntoView: true
3676
+ userEvent: "select.pointer"
3604
3677
  });
3605
3678
  this.mustSelect = false;
3606
3679
  }
@@ -3791,23 +3864,15 @@ function getClickType(event) {
3791
3864
  function basicMouseSelection(view, event) {
3792
3865
  let start = queryPos(view, event), type = getClickType(event);
3793
3866
  let startSel = view.state.selection;
3794
- let last = start, lastEvent = event;
3795
3867
  return {
3796
3868
  update(update) {
3797
3869
  if (update.docChanged) {
3798
3870
  start.pos = update.changes.mapPos(start.pos);
3799
3871
  startSel = startSel.map(update.changes);
3800
- lastEvent = null;
3801
3872
  }
3802
3873
  },
3803
3874
  get(event, extend, multiple) {
3804
- let cur;
3805
- if (lastEvent && event.clientX == lastEvent.clientX && event.clientY == lastEvent.clientY)
3806
- cur = last;
3807
- else {
3808
- cur = last = queryPos(view, event);
3809
- lastEvent = event;
3810
- }
3875
+ let cur = queryPos(view, event);
3811
3876
  let range = rangeForClick(view, cur.pos, cur.bias, type);
3812
3877
  if (start.pos != cur.pos && !extend) {
3813
3878
  let startRange = rangeForClick(view, start.pos, start.bias, type);
@@ -5284,7 +5349,6 @@ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
5284
5349
  margin: 0,
5285
5350
  flexGrow: 2,
5286
5351
  flexShrink: 0,
5287
- minHeight: "100%",
5288
5352
  display: "block",
5289
5353
  whiteSpace: "pre",
5290
5354
  wordWrap: "normal",
@@ -5431,6 +5495,21 @@ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
5431
5495
  display: "inline-block",
5432
5496
  verticalAlign: "top",
5433
5497
  },
5498
+ ".cm-highlightSpace:before": {
5499
+ content: "attr(data-display)",
5500
+ position: "absolute",
5501
+ pointerEvents: "none",
5502
+ color: "#888"
5503
+ },
5504
+ ".cm-highlightTab": {
5505
+ 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>')`,
5506
+ backgroundSize: "auto 100%",
5507
+ backgroundPosition: "right 90%",
5508
+ backgroundRepeat: "no-repeat"
5509
+ },
5510
+ ".cm-trailingSpace": {
5511
+ backgroundColor: "#ff332255"
5512
+ },
5434
5513
  ".cm-button": {
5435
5514
  verticalAlign: "middle",
5436
5515
  color: "inherit",
@@ -5733,7 +5812,8 @@ class DOMObserver {
5733
5812
  this.lastChange = 0;
5734
5813
  this.scrollTargets = [];
5735
5814
  this.intersection = null;
5736
- this.resize = null;
5815
+ this.resizeScroll = null;
5816
+ this.resizeContent = null;
5737
5817
  this.intersecting = false;
5738
5818
  this.gapIntersection = null;
5739
5819
  this.gaps = [];
@@ -5771,12 +5851,14 @@ class DOMObserver {
5771
5851
  this.onPrint = this.onPrint.bind(this);
5772
5852
  this.onScroll = this.onScroll.bind(this);
5773
5853
  if (typeof ResizeObserver == "function") {
5774
- this.resize = new ResizeObserver(() => {
5854
+ this.resizeScroll = new ResizeObserver(() => {
5775
5855
  var _a;
5776
5856
  if (((_a = this.view.docView) === null || _a === void 0 ? void 0 : _a.lastUpdate) < Date.now() - 75)
5777
5857
  this.onResize();
5778
5858
  });
5779
- this.resize.observe(view.scrollDOM);
5859
+ this.resizeScroll.observe(view.scrollDOM);
5860
+ this.resizeContent = new ResizeObserver(() => this.view.requestMeasure());
5861
+ this.resizeContent.observe(view.contentDOM);
5780
5862
  }
5781
5863
  this.addWindowListeners(this.win = view.win);
5782
5864
  this.start();
@@ -6095,11 +6177,12 @@ class DOMObserver {
6095
6177
  win.document.removeEventListener("selectionchange", this.onSelectionChange);
6096
6178
  }
6097
6179
  destroy() {
6098
- var _a, _b, _c;
6180
+ var _a, _b, _c, _d;
6099
6181
  this.stop();
6100
6182
  (_a = this.intersection) === null || _a === void 0 ? void 0 : _a.disconnect();
6101
6183
  (_b = this.gapIntersection) === null || _b === void 0 ? void 0 : _b.disconnect();
6102
- (_c = this.resize) === null || _c === void 0 ? void 0 : _c.disconnect();
6184
+ (_c = this.resizeScroll) === null || _c === void 0 ? void 0 : _c.disconnect();
6185
+ (_d = this.resizeContent) === null || _d === void 0 ? void 0 : _d.disconnect();
6103
6186
  for (let dom of this.scrollTargets)
6104
6187
  dom.removeEventListener("scroll", this.onScroll);
6105
6188
  this.removeWindowListeners(this.win);
@@ -6198,7 +6281,7 @@ class EditorView {
6198
6281
  this.scrollDOM.className = "cm-scroller";
6199
6282
  this.scrollDOM.appendChild(this.contentDOM);
6200
6283
  this.announceDOM = document.createElement("div");
6201
- this.announceDOM.style.cssText = "position: absolute; top: -10000px";
6284
+ this.announceDOM.style.cssText = "position: fixed; top: -10000px";
6202
6285
  this.announceDOM.setAttribute("aria-live", "polite");
6203
6286
  this.dom = document.createElement("div");
6204
6287
  this.dom.appendChild(this.announceDOM);
@@ -7289,7 +7372,8 @@ a rectangle at a given set of coordinates.
7289
7372
  */
7290
7373
  class RectangleMarker {
7291
7374
  /**
7292
- Create a marker with the given class and dimensions.
7375
+ Create a marker with the given class and dimensions. If `width`
7376
+ is null, the DOM element will get no width style.
7293
7377
  */
7294
7378
  constructor(className, left, top, width, height) {
7295
7379
  this.className = className;
@@ -7313,7 +7397,7 @@ class RectangleMarker {
7313
7397
  adjust(elt) {
7314
7398
  elt.style.left = this.left + "px";
7315
7399
  elt.style.top = this.top + "px";
7316
- if (this.width >= 0)
7400
+ if (this.width != null)
7317
7401
  elt.style.width = this.width + "px";
7318
7402
  elt.style.height = this.height + "px";
7319
7403
  }
@@ -7321,6 +7405,129 @@ class RectangleMarker {
7321
7405
  return this.left == p.left && this.top == p.top && this.width == p.width && this.height == p.height &&
7322
7406
  this.className == p.className;
7323
7407
  }
7408
+ /**
7409
+ Create a set of rectangles for the given selection range,
7410
+ assigning them theclass`className`. Will create a single
7411
+ rectangle for empty ranges, and a set of selection-style
7412
+ rectangles covering the range's content (in a bidi-aware
7413
+ way) for non-empty ones.
7414
+ */
7415
+ static forRange(view, className, range) {
7416
+ if (range.empty) {
7417
+ let pos = view.coordsAtPos(range.head, range.assoc || 1);
7418
+ if (!pos)
7419
+ return [];
7420
+ let base = getBase(view);
7421
+ return [new RectangleMarker(className, pos.left - base.left, pos.top - base.top, null, pos.bottom - pos.top)];
7422
+ }
7423
+ else {
7424
+ return rectanglesForRange(view, className, range);
7425
+ }
7426
+ }
7427
+ }
7428
+ function getBase(view) {
7429
+ let rect = view.scrollDOM.getBoundingClientRect();
7430
+ let left = view.textDirection == Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth;
7431
+ return { left: left - view.scrollDOM.scrollLeft, top: rect.top - view.scrollDOM.scrollTop };
7432
+ }
7433
+ function wrappedLine(view, pos, inside) {
7434
+ let range = EditorSelection.cursor(pos);
7435
+ return { from: Math.max(inside.from, view.moveToLineBoundary(range, false, true).from),
7436
+ to: Math.min(inside.to, view.moveToLineBoundary(range, true, true).from),
7437
+ type: BlockType.Text };
7438
+ }
7439
+ function blockAt(view, pos) {
7440
+ let line = view.lineBlockAt(pos);
7441
+ if (Array.isArray(line.type))
7442
+ for (let l of line.type) {
7443
+ if (l.to > pos || l.to == pos && (l.to == line.to || l.type == BlockType.Text))
7444
+ return l;
7445
+ }
7446
+ return line;
7447
+ }
7448
+ function rectanglesForRange(view, className, range) {
7449
+ if (range.to <= view.viewport.from || range.from >= view.viewport.to)
7450
+ return [];
7451
+ let from = Math.max(range.from, view.viewport.from), to = Math.min(range.to, view.viewport.to);
7452
+ let ltr = view.textDirection == Direction.LTR;
7453
+ let content = view.contentDOM, contentRect = content.getBoundingClientRect(), base = getBase(view);
7454
+ let lineStyle = window.getComputedStyle(content.firstChild);
7455
+ let leftSide = contentRect.left + parseInt(lineStyle.paddingLeft) + Math.min(0, parseInt(lineStyle.textIndent));
7456
+ let rightSide = contentRect.right - parseInt(lineStyle.paddingRight);
7457
+ let startBlock = blockAt(view, from), endBlock = blockAt(view, to);
7458
+ let visualStart = startBlock.type == BlockType.Text ? startBlock : null;
7459
+ let visualEnd = endBlock.type == BlockType.Text ? endBlock : null;
7460
+ if (view.lineWrapping) {
7461
+ if (visualStart)
7462
+ visualStart = wrappedLine(view, from, visualStart);
7463
+ if (visualEnd)
7464
+ visualEnd = wrappedLine(view, to, visualEnd);
7465
+ }
7466
+ if (visualStart && visualEnd && visualStart.from == visualEnd.from) {
7467
+ return pieces(drawForLine(range.from, range.to, visualStart));
7468
+ }
7469
+ else {
7470
+ let top = visualStart ? drawForLine(range.from, null, visualStart) : drawForWidget(startBlock, false);
7471
+ let bottom = visualEnd ? drawForLine(null, range.to, visualEnd) : drawForWidget(endBlock, true);
7472
+ let between = [];
7473
+ if ((visualStart || startBlock).to < (visualEnd || endBlock).from - 1)
7474
+ between.push(piece(leftSide, top.bottom, rightSide, bottom.top));
7475
+ else if (top.bottom < bottom.top && view.elementAtHeight((top.bottom + bottom.top) / 2).type == BlockType.Text)
7476
+ top.bottom = bottom.top = (top.bottom + bottom.top) / 2;
7477
+ return pieces(top).concat(between).concat(pieces(bottom));
7478
+ }
7479
+ function piece(left, top, right, bottom) {
7480
+ return new RectangleMarker(className, left - base.left, top - base.top - 0.01 /* C.Epsilon */, right - left, bottom - top + 0.01 /* C.Epsilon */);
7481
+ }
7482
+ function pieces({ top, bottom, horizontal }) {
7483
+ let pieces = [];
7484
+ for (let i = 0; i < horizontal.length; i += 2)
7485
+ pieces.push(piece(horizontal[i], top, horizontal[i + 1], bottom));
7486
+ return pieces;
7487
+ }
7488
+ // Gets passed from/to in line-local positions
7489
+ function drawForLine(from, to, line) {
7490
+ let top = 1e9, bottom = -1e9, horizontal = [];
7491
+ function addSpan(from, fromOpen, to, toOpen, dir) {
7492
+ // Passing 2/-2 is a kludge to force the view to return
7493
+ // coordinates on the proper side of block widgets, since
7494
+ // normalizing the side there, though appropriate for most
7495
+ // coordsAtPos queries, would break selection drawing.
7496
+ let fromCoords = view.coordsAtPos(from, (from == line.to ? -2 : 2));
7497
+ let toCoords = view.coordsAtPos(to, (to == line.from ? 2 : -2));
7498
+ top = Math.min(fromCoords.top, toCoords.top, top);
7499
+ bottom = Math.max(fromCoords.bottom, toCoords.bottom, bottom);
7500
+ if (dir == Direction.LTR)
7501
+ horizontal.push(ltr && fromOpen ? leftSide : fromCoords.left, ltr && toOpen ? rightSide : toCoords.right);
7502
+ else
7503
+ horizontal.push(!ltr && toOpen ? leftSide : toCoords.left, !ltr && fromOpen ? rightSide : fromCoords.right);
7504
+ }
7505
+ let start = from !== null && from !== void 0 ? from : line.from, end = to !== null && to !== void 0 ? to : line.to;
7506
+ // Split the range by visible range and document line
7507
+ for (let r of view.visibleRanges)
7508
+ if (r.to > start && r.from < end) {
7509
+ for (let pos = Math.max(r.from, start), endPos = Math.min(r.to, end);;) {
7510
+ let docLine = view.state.doc.lineAt(pos);
7511
+ for (let span of view.bidiSpans(docLine)) {
7512
+ let spanFrom = span.from + docLine.from, spanTo = span.to + docLine.from;
7513
+ if (spanFrom >= endPos)
7514
+ break;
7515
+ if (spanTo > pos)
7516
+ addSpan(Math.max(spanFrom, pos), from == null && spanFrom <= start, Math.min(spanTo, endPos), to == null && spanTo >= end, span.dir);
7517
+ }
7518
+ pos = docLine.to + 1;
7519
+ if (pos >= endPos)
7520
+ break;
7521
+ }
7522
+ }
7523
+ if (horizontal.length == 0)
7524
+ addSpan(start, from == null, end, to == null, view.textDirection);
7525
+ return { top, bottom, horizontal };
7526
+ }
7527
+ function drawForWidget(block, top) {
7528
+ let y = contentRect.top + (top ? block.top : block.bottom);
7529
+ return { top: y, bottom: y, horizontal: [] };
7530
+ }
7324
7531
  }
7325
7532
  function sameMarker(a, b) {
7326
7533
  return a.constructor == b.constructor && a.eq(b);
@@ -7380,6 +7587,8 @@ class LayerView {
7380
7587
  }
7381
7588
  }
7382
7589
  destroy() {
7590
+ if (this.layer.destroy)
7591
+ this.layer.destroy(this.dom, this.view);
7383
7592
  this.dom.remove();
7384
7593
  }
7385
7594
  }
@@ -7444,8 +7653,9 @@ const cursorLayer = /*@__PURE__*/layer({
7444
7653
  for (let r of state.selection.ranges) {
7445
7654
  let prim = r == state.selection.main;
7446
7655
  if (r.empty ? !prim || CanHidePrimary : conf.drawRangeCursor) {
7447
- let piece = measureCursor(view, r, prim);
7448
- if (piece)
7656
+ let className = prim ? "cm-cursor cm-cursor-primary" : "cm-cursor cm-cursor-secondary";
7657
+ let cursor = r.empty ? r : EditorSelection.cursor(r.head, r.head > r.anchor ? -1 : 1);
7658
+ for (let piece of RectangleMarker.forRange(view, className, cursor))
7449
7659
  cursors.push(piece);
7450
7660
  }
7451
7661
  }
@@ -7470,7 +7680,8 @@ function setBlinkRate(state, dom) {
7470
7680
  const selectionLayer = /*@__PURE__*/layer({
7471
7681
  above: false,
7472
7682
  markers(view) {
7473
- return view.state.selection.ranges.map(r => r.empty ? [] : measureRange(view, r)).reduce((a, b) => a.concat(b));
7683
+ return view.state.selection.ranges.map(r => r.empty ? [] : RectangleMarker.forRange(view, "cm-selectionBackground", r))
7684
+ .reduce((a, b) => a.concat(b));
7474
7685
  },
7475
7686
  update(update, dom) {
7476
7687
  return update.docChanged || update.selectionSet || update.viewportChanged || configChanged(update);
@@ -7486,117 +7697,6 @@ const themeSpec = {
7486
7697
  if (CanHidePrimary)
7487
7698
  themeSpec[".cm-line"].caretColor = "transparent !important";
7488
7699
  const hideNativeSelection = /*@__PURE__*/Prec.highest(/*@__PURE__*/EditorView.theme(themeSpec));
7489
- function getBase(view) {
7490
- let rect = view.scrollDOM.getBoundingClientRect();
7491
- let left = view.textDirection == Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth;
7492
- return { left: left - view.scrollDOM.scrollLeft, top: rect.top - view.scrollDOM.scrollTop };
7493
- }
7494
- function wrappedLine(view, pos, inside) {
7495
- let range = EditorSelection.cursor(pos);
7496
- return { from: Math.max(inside.from, view.moveToLineBoundary(range, false, true).from),
7497
- to: Math.min(inside.to, view.moveToLineBoundary(range, true, true).from),
7498
- type: BlockType.Text };
7499
- }
7500
- function blockAt(view, pos) {
7501
- let line = view.lineBlockAt(pos);
7502
- if (Array.isArray(line.type))
7503
- for (let l of line.type) {
7504
- if (l.to > pos || l.to == pos && (l.to == line.to || l.type == BlockType.Text))
7505
- return l;
7506
- }
7507
- return line;
7508
- }
7509
- function measureRange(view, range) {
7510
- if (range.to <= view.viewport.from || range.from >= view.viewport.to)
7511
- return [];
7512
- let from = Math.max(range.from, view.viewport.from), to = Math.min(range.to, view.viewport.to);
7513
- let ltr = view.textDirection == Direction.LTR;
7514
- let content = view.contentDOM, contentRect = content.getBoundingClientRect(), base = getBase(view);
7515
- let lineStyle = window.getComputedStyle(content.firstChild);
7516
- let leftSide = contentRect.left + parseInt(lineStyle.paddingLeft) + Math.min(0, parseInt(lineStyle.textIndent));
7517
- let rightSide = contentRect.right - parseInt(lineStyle.paddingRight);
7518
- let startBlock = blockAt(view, from), endBlock = blockAt(view, to);
7519
- let visualStart = startBlock.type == BlockType.Text ? startBlock : null;
7520
- let visualEnd = endBlock.type == BlockType.Text ? endBlock : null;
7521
- if (view.lineWrapping) {
7522
- if (visualStart)
7523
- visualStart = wrappedLine(view, from, visualStart);
7524
- if (visualEnd)
7525
- visualEnd = wrappedLine(view, to, visualEnd);
7526
- }
7527
- if (visualStart && visualEnd && visualStart.from == visualEnd.from) {
7528
- return pieces(drawForLine(range.from, range.to, visualStart));
7529
- }
7530
- else {
7531
- let top = visualStart ? drawForLine(range.from, null, visualStart) : drawForWidget(startBlock, false);
7532
- let bottom = visualEnd ? drawForLine(null, range.to, visualEnd) : drawForWidget(endBlock, true);
7533
- let between = [];
7534
- if ((visualStart || startBlock).to < (visualEnd || endBlock).from - 1)
7535
- between.push(piece(leftSide, top.bottom, rightSide, bottom.top));
7536
- else if (top.bottom < bottom.top && view.elementAtHeight((top.bottom + bottom.top) / 2).type == BlockType.Text)
7537
- top.bottom = bottom.top = (top.bottom + bottom.top) / 2;
7538
- return pieces(top).concat(between).concat(pieces(bottom));
7539
- }
7540
- function piece(left, top, right, bottom) {
7541
- return new RectangleMarker("cm-selectionBackground", left - base.left, top - base.top - 0.01 /* C.Epsilon */, right - left, bottom - top + 0.01 /* C.Epsilon */);
7542
- }
7543
- function pieces({ top, bottom, horizontal }) {
7544
- let pieces = [];
7545
- for (let i = 0; i < horizontal.length; i += 2)
7546
- pieces.push(piece(horizontal[i], top, horizontal[i + 1], bottom));
7547
- return pieces;
7548
- }
7549
- // Gets passed from/to in line-local positions
7550
- function drawForLine(from, to, line) {
7551
- let top = 1e9, bottom = -1e9, horizontal = [];
7552
- function addSpan(from, fromOpen, to, toOpen, dir) {
7553
- // Passing 2/-2 is a kludge to force the view to return
7554
- // coordinates on the proper side of block widgets, since
7555
- // normalizing the side there, though appropriate for most
7556
- // coordsAtPos queries, would break selection drawing.
7557
- let fromCoords = view.coordsAtPos(from, (from == line.to ? -2 : 2));
7558
- let toCoords = view.coordsAtPos(to, (to == line.from ? 2 : -2));
7559
- top = Math.min(fromCoords.top, toCoords.top, top);
7560
- bottom = Math.max(fromCoords.bottom, toCoords.bottom, bottom);
7561
- if (dir == Direction.LTR)
7562
- horizontal.push(ltr && fromOpen ? leftSide : fromCoords.left, ltr && toOpen ? rightSide : toCoords.right);
7563
- else
7564
- horizontal.push(!ltr && toOpen ? leftSide : toCoords.left, !ltr && fromOpen ? rightSide : fromCoords.right);
7565
- }
7566
- let start = from !== null && from !== void 0 ? from : line.from, end = to !== null && to !== void 0 ? to : line.to;
7567
- // Split the range by visible range and document line
7568
- for (let r of view.visibleRanges)
7569
- if (r.to > start && r.from < end) {
7570
- for (let pos = Math.max(r.from, start), endPos = Math.min(r.to, end);;) {
7571
- let docLine = view.state.doc.lineAt(pos);
7572
- for (let span of view.bidiSpans(docLine)) {
7573
- let spanFrom = span.from + docLine.from, spanTo = span.to + docLine.from;
7574
- if (spanFrom >= endPos)
7575
- break;
7576
- if (spanTo > pos)
7577
- addSpan(Math.max(spanFrom, pos), from == null && spanFrom <= start, Math.min(spanTo, endPos), to == null && spanTo >= end, span.dir);
7578
- }
7579
- pos = docLine.to + 1;
7580
- if (pos >= endPos)
7581
- break;
7582
- }
7583
- }
7584
- if (horizontal.length == 0)
7585
- addSpan(start, from == null, end, to == null, view.textDirection);
7586
- return { top, bottom, horizontal };
7587
- }
7588
- function drawForWidget(block, top) {
7589
- let y = contentRect.top + (top ? block.top : block.bottom);
7590
- return { top: y, bottom: y, horizontal: [] };
7591
- }
7592
- }
7593
- function measureCursor(view, cursor, primary) {
7594
- let pos = view.coordsAtPos(cursor.head, cursor.assoc || 1);
7595
- if (!pos)
7596
- return null;
7597
- let base = getBase(view);
7598
- return new RectangleMarker(primary ? "cm-cursor cm-cursor-primary" : "cm-cursor cm-cursor-secondary", pos.left - base.left, pos.top - base.top, -1, pos.bottom - pos.top);
7599
- }
7600
7700
 
7601
7701
  const setDropCursorPos = /*@__PURE__*/StateEffect.define({
7602
7702
  map(pos, mapping) { return pos == null ? null : mapping.mapPos(pos); }
@@ -9363,9 +9463,60 @@ function highlightActiveLineGutter() {
9363
9463
  return activeLineGutterHighlighter;
9364
9464
  }
9365
9465
 
9466
+ const WhitespaceDeco = /*@__PURE__*/new Map();
9467
+ function getWhitespaceDeco(space) {
9468
+ let deco = WhitespaceDeco.get(space);
9469
+ if (!deco)
9470
+ WhitespaceDeco.set(space, deco = Decoration.mark({
9471
+ attributes: space === "\t" ? {
9472
+ class: "cm-highlightTab",
9473
+ } : {
9474
+ class: "cm-highlightSpace",
9475
+ "data-display": space.replace(/ /g, "·")
9476
+ }
9477
+ }));
9478
+ return deco;
9479
+ }
9480
+ function matcher(decorator) {
9481
+ return ViewPlugin.define(view => ({
9482
+ decorations: decorator.createDeco(view),
9483
+ update(u) {
9484
+ this.decorations = decorator.updateDeco(u, this.decorations);
9485
+ },
9486
+ }), {
9487
+ decorations: v => v.decorations
9488
+ });
9489
+ }
9490
+ const whitespaceHighlighter = /*@__PURE__*/matcher(/*@__PURE__*/new MatchDecorator({
9491
+ regexp: /\t| +/g,
9492
+ decoration: match => getWhitespaceDeco(match[0]),
9493
+ boundary: /\S/,
9494
+ }));
9495
+ /**
9496
+ Returns an extension that highlights whitespace, adding a
9497
+ `cm-highlightSpace` class to stretches of spaces, and a
9498
+ `cm-highlightTab` class to individual tab characters. By default,
9499
+ the former are shown as faint dots, and the latter as arrows.
9500
+ */
9501
+ function highlightWhitespace() {
9502
+ return whitespaceHighlighter;
9503
+ }
9504
+ const trailingHighlighter = /*@__PURE__*/matcher(/*@__PURE__*/new MatchDecorator({
9505
+ regexp: /\s+$/g,
9506
+ decoration: /*@__PURE__*/Decoration.mark({ class: "cm-trailingSpace" }),
9507
+ boundary: /\S/,
9508
+ }));
9509
+ /**
9510
+ Returns an extension that adds a `cm-trailingSpace` class to all
9511
+ trailing whitespace.
9512
+ */
9513
+ function highlightTrailingWhitespace() {
9514
+ return trailingHighlighter;
9515
+ }
9516
+
9366
9517
  /**
9367
9518
  @internal
9368
9519
  */
9369
9520
  const __test = { HeightMap, HeightOracle, MeasuredHeights, QueryType, ChangedRange, computeOrder, moveVisually };
9370
9521
 
9371
- export { BidiSpan, BlockInfo, BlockType, Decoration, Direction, EditorView, GutterMarker, MatchDecorator, RectangleMarker, ViewPlugin, ViewUpdate, WidgetType, __test, closeHoverTooltips, crosshairCursor, drawSelection, dropCursor, getPanel, getTooltip, gutter, gutterLineClass, gutters, hasHoverTooltips, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, hoverTooltip, keymap, layer, lineNumberMarkers, lineNumbers, logException, panels, placeholder, rectangularSelection, repositionTooltips, runScopeHandlers, scrollPastEnd, showPanel, showTooltip, tooltips };
9522
+ export { BidiSpan, BlockInfo, BlockType, Decoration, Direction, EditorView, GutterMarker, MatchDecorator, RectangleMarker, ViewPlugin, ViewUpdate, WidgetType, __test, closeHoverTooltips, crosshairCursor, drawSelection, dropCursor, getPanel, getTooltip, gutter, gutterLineClass, gutters, hasHoverTooltips, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, highlightTrailingWhitespace, highlightWhitespace, hoverTooltip, keymap, layer, lineNumberMarkers, lineNumbers, logException, panels, placeholder, rectangularSelection, repositionTooltips, runScopeHandlers, scrollPastEnd, showPanel, showTooltip, tooltips };