@codemirror/view 0.19.48 → 0.20.2

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
@@ -1,8 +1,5 @@
1
- import { MapMode, Text as Text$1, Facet, StateEffect, ChangeSet, EditorSelection, EditorState, CharCategory, Transaction, Prec, combineConfig, StateField } from '@codemirror/state';
1
+ import { Text, RangeSet, MapMode, RangeValue, Facet, StateEffect, ChangeSet, findClusterBreak, EditorSelection, EditorState, findColumn, CharCategory, Prec, Transaction, combineConfig, StateField, RangeSetBuilder, codePointAt, countColumn } from '@codemirror/state';
2
2
  import { StyleModule } from 'style-mod';
3
- import { RangeSet, RangeValue, RangeSetBuilder } from '@codemirror/rangeset';
4
- export { Range } from '@codemirror/rangeset';
5
- import { Text, findClusterBreak, findColumn, codePointAt, countColumn } from '@codemirror/text';
6
3
  import { keyName, base } from 'w3c-keyname';
7
4
 
8
5
  function getSelection(root) {
@@ -331,7 +328,7 @@ class ContentView {
331
328
  track.written = true;
332
329
  if (child.dom.parentNode == parent) {
333
330
  while (next && next != child.dom)
334
- next = rm(next);
331
+ next = rm$1(next);
335
332
  }
336
333
  else {
337
334
  parent.insertBefore(child.dom, next);
@@ -342,7 +339,7 @@ class ContentView {
342
339
  if (next && track && track.node == parent)
343
340
  track.written = true;
344
341
  while (next)
345
- next = rm(next);
342
+ next = rm$1(next);
346
343
  }
347
344
  else if (this.dirty & 1 /* Child */) {
348
345
  for (let child of this.children)
@@ -488,7 +485,7 @@ class ContentView {
488
485
  }
489
486
  ContentView.prototype.breakAfter = 0;
490
487
  // Remove a DOM node and return its next sibling.
491
- function rm(dom) {
488
+ function rm$1(dom) {
492
489
  let next = dom.nextSibling;
493
490
  dom.parentNode.removeChild(dom);
494
491
  return next;
@@ -1076,7 +1073,7 @@ function updateAttrs(dom, prev, attrs) {
1076
1073
  Widgets added to the content are described by subclasses of this
1077
1074
  class. Using a description object like that makes it possible to
1078
1075
  delay creating of the DOM structure for a widget until it is
1079
- needed, and to avoid redrawing widgets even when the decorations
1076
+ needed, and to avoid redrawing widgets even if the decorations
1080
1077
  that define them are recreated.
1081
1078
  */
1082
1079
  class WidgetType {
@@ -1089,7 +1086,7 @@ class WidgetType {
1089
1086
  returns `false`, which will cause new instances of the widget to
1090
1087
  always be redrawn.
1091
1088
  */
1092
- eq(_widget) { return false; }
1089
+ eq(widget) { return false; }
1093
1090
  /**
1094
1091
  Update a DOM element created by a widget of the same type (but
1095
1092
  different, non-`eq` content) to reflect this widget. May return
@@ -1097,7 +1094,7 @@ class WidgetType {
1097
1094
  couldn't (in which case the widget will be redrawn). The default
1098
1095
  implementation just returns false.
1099
1096
  */
1100
- updateDOM(_dom) { return false; }
1097
+ updateDOM(dom) { return false; }
1101
1098
  /**
1102
1099
  @internal
1103
1100
  */
@@ -1116,7 +1113,7 @@ class WidgetType {
1116
1113
  should be ignored by the editor. The default is to ignore all
1117
1114
  events.
1118
1115
  */
1119
- ignoreEvent(_event) { return true; }
1116
+ ignoreEvent(event) { return true; }
1120
1117
  /**
1121
1118
  @internal
1122
1119
  */
@@ -1125,7 +1122,7 @@ class WidgetType {
1125
1122
  This is called when the an instance of the widget is removed
1126
1123
  from the editor view.
1127
1124
  */
1128
- destroy(_dom) { }
1125
+ destroy(dom) { }
1129
1126
  }
1130
1127
  /**
1131
1128
  The different types of blocks that can occur in an editor view.
@@ -1151,7 +1148,8 @@ return BlockType})(BlockType || (BlockType = {}));
1151
1148
  /**
1152
1149
  A decoration provides information on how to draw or style a piece
1153
1150
  of content. You'll usually use it wrapped in a
1154
- [`Range`](https://codemirror.net/6/docs/ref/#rangeset.Range), which adds a start and end position.
1151
+ [`Range`](https://codemirror.net/6/docs/ref/#state.Range), which adds a start and end position.
1152
+ @nonabstract
1155
1153
  */
1156
1154
  class Decoration extends RangeValue {
1157
1155
  /**
@@ -1190,18 +1188,17 @@ class Decoration extends RangeValue {
1190
1188
  Create a mark decoration, which influences the styling of the
1191
1189
  content in its range. Nested mark decorations will cause nested
1192
1190
  DOM elements to be created. Nesting order is determined by
1193
- precedence of the [facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations) or
1194
- (below the facet-provided decorations) [view
1195
- plugin](https://codemirror.net/6/docs/ref/#view.PluginSpec.decorations). Such elements are split
1196
- on line boundaries and on the boundaries of higher-precedence
1197
- decorations.
1191
+ precedence of the [facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations), with
1192
+ the higher-precedence decorations creating the inner DOM nodes.
1193
+ Such elements are split on line boundaries and on the boundaries
1194
+ of lower-precedence decorations.
1198
1195
  */
1199
1196
  static mark(spec) {
1200
1197
  return new MarkDecoration(spec);
1201
1198
  }
1202
1199
  /**
1203
- Create a widget decoration, which adds an element at the given
1204
- position.
1200
+ Create a widget decoration, which displays a DOM element at the
1201
+ given position.
1205
1202
  */
1206
1203
  static widget(spec) {
1207
1204
  let side = spec.side || 0, block = !!spec.block;
@@ -1512,7 +1509,7 @@ class BlockWidgetView extends ContentView {
1512
1509
  }
1513
1510
  }
1514
1511
  get overrideDOMText() {
1515
- return this.parent ? this.parent.view.state.doc.slice(this.posAtStart, this.posAtEnd) : Text$1.empty;
1512
+ return this.parent ? this.parent.view.state.doc.slice(this.posAtStart, this.posAtEnd) : Text.empty;
1516
1513
  }
1517
1514
  domBoundsAround() { return null; }
1518
1515
  become(other) {
@@ -1539,11 +1536,11 @@ class BlockWidgetView extends ContentView {
1539
1536
  }
1540
1537
 
1541
1538
  class ContentBuilder {
1542
- constructor(doc, pos, end, disallowBlockEffectsBelow) {
1539
+ constructor(doc, pos, end, disallowBlockEffectsFor) {
1543
1540
  this.doc = doc;
1544
1541
  this.pos = pos;
1545
1542
  this.end = end;
1546
- this.disallowBlockEffectsBelow = disallowBlockEffectsBelow;
1543
+ this.disallowBlockEffectsFor = disallowBlockEffectsFor;
1547
1544
  this.content = [];
1548
1545
  this.curLine = null;
1549
1546
  this.breakAtStart = 0;
@@ -1628,7 +1625,13 @@ class ContentBuilder {
1628
1625
  if (this.openStart < 0)
1629
1626
  this.openStart = openStart;
1630
1627
  }
1631
- point(from, to, deco, active, openStart) {
1628
+ point(from, to, deco, active, openStart, index) {
1629
+ if (this.disallowBlockEffectsFor[index] && deco instanceof PointDecoration) {
1630
+ if (deco.block)
1631
+ throw new RangeError("Block decorations may not be specified via plugins");
1632
+ if (to > this.doc.lineAt(this.pos).to)
1633
+ throw new RangeError("Decorations that replace line breaks may not be specified via plugins");
1634
+ }
1632
1635
  let len = to - from;
1633
1636
  if (deco instanceof PointDecoration) {
1634
1637
  if (deco.block) {
@@ -1672,17 +1675,8 @@ class ContentBuilder {
1672
1675
  if (this.openStart < 0)
1673
1676
  this.openStart = openStart;
1674
1677
  }
1675
- filterPoint(from, to, value, index) {
1676
- if (index < this.disallowBlockEffectsBelow && value instanceof PointDecoration) {
1677
- if (value.block)
1678
- throw new RangeError("Block decorations may not be specified via plugins");
1679
- if (to > this.doc.lineAt(this.pos).to)
1680
- throw new RangeError("Decorations that replace line breaks may not be specified via plugins");
1681
- }
1682
- return true;
1683
- }
1684
- static build(text, from, to, decorations, pluginDecorationLength) {
1685
- let builder = new ContentBuilder(text, from, to, pluginDecorationLength);
1678
+ static build(text, from, to, decorations, dynamicDecorationMap) {
1679
+ let builder = new ContentBuilder(text, from, to, dynamicDecorationMap);
1686
1680
  builder.openEnd = RangeSet.spans(decorations, from, to, builder);
1687
1681
  if (builder.openStart < 0)
1688
1682
  builder.openStart = builder.openEnd;
@@ -1712,13 +1706,8 @@ const mouseSelectionStyle = /*@__PURE__*/Facet.define();
1712
1706
  const exceptionSink = /*@__PURE__*/Facet.define();
1713
1707
  const updateListener = /*@__PURE__*/Facet.define();
1714
1708
  const inputHandler = /*@__PURE__*/Facet.define();
1715
- // FIXME remove
1716
- const scrollTo = /*@__PURE__*/StateEffect.define({
1717
- map: (range, changes) => range.map(changes)
1718
- });
1719
- // FIXME remove
1720
- const centerOn = /*@__PURE__*/StateEffect.define({
1721
- map: (range, changes) => range.map(changes)
1709
+ const perLineTextDirection = /*@__PURE__*/Facet.define({
1710
+ combine: values => values.some(x => x)
1722
1711
  });
1723
1712
  class ScrollTarget {
1724
1713
  constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5) {
@@ -1757,83 +1746,6 @@ function logException(state, exception, context) {
1757
1746
  console.error(exception);
1758
1747
  }
1759
1748
  const editable = /*@__PURE__*/Facet.define({ combine: values => values.length ? values[0] : true });
1760
- /**
1761
- Used to [declare](https://codemirror.net/6/docs/ref/#view.PluginSpec.provide) which
1762
- [fields](https://codemirror.net/6/docs/ref/#view.PluginValue) a [view plugin](https://codemirror.net/6/docs/ref/#view.ViewPlugin)
1763
- provides.
1764
- */
1765
- class PluginFieldProvider {
1766
- /**
1767
- @internal
1768
- */
1769
- constructor(
1770
- /**
1771
- @internal
1772
- */
1773
- field,
1774
- /**
1775
- @internal
1776
- */
1777
- get) {
1778
- this.field = field;
1779
- this.get = get;
1780
- }
1781
- }
1782
- /**
1783
- Plugin fields are a mechanism for allowing plugins to provide
1784
- values that can be retrieved through the
1785
- [`pluginField`](https://codemirror.net/6/docs/ref/#view.EditorView.pluginField) view method.
1786
- */
1787
- class PluginField {
1788
- /**
1789
- Create a [provider](https://codemirror.net/6/docs/ref/#view.PluginFieldProvider) for this field,
1790
- to use with a plugin's [provide](https://codemirror.net/6/docs/ref/#view.PluginSpec.provide)
1791
- option.
1792
- */
1793
- from(get) {
1794
- return new PluginFieldProvider(this, get);
1795
- }
1796
- /**
1797
- Define a new plugin field.
1798
- */
1799
- static define() { return new PluginField(); }
1800
- }
1801
- /**
1802
- This field can be used by plugins to provide
1803
- [decorations](https://codemirror.net/6/docs/ref/#view.Decoration).
1804
-
1805
- **Note**: For reasons of data flow (plugins are only updated
1806
- after the viewport is computed), decorations produced by plugins
1807
- are _not_ taken into account when predicting the vertical layout
1808
- structure of the editor. They **must not** introduce block
1809
- widgets (that will raise an error) or replacing decorations that
1810
- cover line breaks (these will be ignored if they occur). Such
1811
- decorations, or others that cause a large amount of vertical
1812
- size shift compared to the undecorated content, should be
1813
- provided through the state-level [`decorations`
1814
- facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations) instead.
1815
- */
1816
- PluginField.decorations = /*@__PURE__*/PluginField.define();
1817
- /**
1818
- Used to provide ranges that should be treated as atoms as far as
1819
- cursor motion is concerned. This causes methods like
1820
- [`moveByChar`](https://codemirror.net/6/docs/ref/#view.EditorView.moveByChar) and
1821
- [`moveVertically`](https://codemirror.net/6/docs/ref/#view.EditorView.moveVertically) (and the
1822
- commands built on top of them) to skip across such regions when
1823
- a selection endpoint would enter them. This does _not_ prevent
1824
- direct programmatic [selection
1825
- updates](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection) from moving into such
1826
- regions.
1827
- */
1828
- PluginField.atomicRanges = /*@__PURE__*/PluginField.define();
1829
- /**
1830
- Plugins can provide additional scroll margins (space around the
1831
- sides of the scrolling element that should be considered
1832
- invisible) through this field. This can be useful when the
1833
- plugin introduces elements that cover part of that element (for
1834
- example a horizontally fixed gutter).
1835
- */
1836
- PluginField.scrollMargins = /*@__PURE__*/PluginField.define();
1837
1749
  let nextPluginID = 0;
1838
1750
  const viewPlugin = /*@__PURE__*/Facet.define();
1839
1751
  /**
@@ -1854,27 +1766,29 @@ class ViewPlugin {
1854
1766
  /**
1855
1767
  @internal
1856
1768
  */
1857
- fields) {
1769
+ domEventHandlers, buildExtensions) {
1858
1770
  this.id = id;
1859
1771
  this.create = create;
1860
- this.fields = fields;
1861
- this.extension = viewPlugin.of(this);
1772
+ this.domEventHandlers = domEventHandlers;
1773
+ this.extension = buildExtensions(this);
1862
1774
  }
1863
1775
  /**
1864
1776
  Define a plugin from a constructor function that creates the
1865
1777
  plugin's value, given an editor view.
1866
1778
  */
1867
1779
  static define(create, spec) {
1868
- let { eventHandlers, provide, decorations } = spec || {};
1869
- let fields = [];
1870
- if (provide)
1871
- for (let provider of Array.isArray(provide) ? provide : [provide])
1872
- fields.push(provider);
1873
- if (eventHandlers)
1874
- fields.push(domEventHandlers.from((value) => ({ plugin: value, handlers: eventHandlers })));
1875
- if (decorations)
1876
- fields.push(PluginField.decorations.from(decorations));
1877
- return new ViewPlugin(nextPluginID++, create, fields);
1780
+ const { eventHandlers, provide, decorations: deco } = spec || {};
1781
+ return new ViewPlugin(nextPluginID++, create, eventHandlers, plugin => {
1782
+ let ext = [viewPlugin.of(plugin)];
1783
+ if (deco)
1784
+ ext.push(decorations.of(view => {
1785
+ let pluginInst = view.plugin(plugin);
1786
+ return pluginInst ? deco(pluginInst) : Decoration.none;
1787
+ }));
1788
+ if (provide)
1789
+ ext.push(provide(plugin));
1790
+ return ext;
1791
+ });
1878
1792
  }
1879
1793
  /**
1880
1794
  Create a plugin for a class whose constructor takes a single
@@ -1884,7 +1798,6 @@ class ViewPlugin {
1884
1798
  return ViewPlugin.define(view => new cls(view), spec);
1885
1799
  }
1886
1800
  }
1887
- const domEventHandlers = /*@__PURE__*/PluginField.define();
1888
1801
  class PluginInstance {
1889
1802
  constructor(spec) {
1890
1803
  this.spec = spec;
@@ -1897,12 +1810,6 @@ class PluginInstance {
1897
1810
  // initialized on the first update.
1898
1811
  this.value = null;
1899
1812
  }
1900
- takeField(type, target) {
1901
- if (this.spec)
1902
- for (let { field, get } of this.spec.fields)
1903
- if (field == type)
1904
- target.push(get(this.value));
1905
- }
1906
1813
  update(view) {
1907
1814
  if (!this.value) {
1908
1815
  if (this.spec) {
@@ -1954,6 +1861,8 @@ const editorAttributes = /*@__PURE__*/Facet.define();
1954
1861
  const contentAttributes = /*@__PURE__*/Facet.define();
1955
1862
  // Provide decorations
1956
1863
  const decorations = /*@__PURE__*/Facet.define();
1864
+ const atomicRanges = /*@__PURE__*/Facet.define();
1865
+ const scrollMargins = /*@__PURE__*/Facet.define();
1957
1866
  const styleModule = /*@__PURE__*/Facet.define();
1958
1867
  class ChangedRange {
1959
1868
  constructor(fromA, toA, fromB, toB) {
@@ -2054,8 +1963,8 @@ class ViewUpdate {
2054
1963
  return (this.flags & 4 /* Viewport */) > 0;
2055
1964
  }
2056
1965
  /**
2057
- Indicates whether the height of an element in the editor changed
2058
- in this update.
1966
+ Indicates whether the height of a block element in the editor
1967
+ changed in this update.
2059
1968
  */
2060
1969
  get heightChanged() {
2061
1970
  return (this.flags & 2 /* Height */) > 0;
@@ -2514,7 +2423,7 @@ class DocView extends ContentView {
2514
2423
  this.view = view;
2515
2424
  this.compositionDeco = Decoration.none;
2516
2425
  this.decorations = [];
2517
- this.pluginDecorationLength = 0;
2426
+ this.dynamicDecorationMap = [];
2518
2427
  // Track a minimum width for the editor. When measuring sizes in
2519
2428
  // measureVisibleLineHeights, this is updated to point at the width
2520
2429
  // of a given element and its extent in the document. When a change
@@ -2620,7 +2529,7 @@ class DocView extends ContentView {
2620
2529
  if (!next)
2621
2530
  break;
2622
2531
  let { fromA, toA, fromB, toB } = next;
2623
- let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, this.decorations, this.pluginDecorationLength);
2532
+ let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, this.decorations, this.dynamicDecorationMap);
2624
2533
  let { i: toI, off: toOff } = cursor.findPos(toA, 1);
2625
2534
  let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
2626
2535
  replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
@@ -2761,11 +2670,11 @@ class DocView extends ContentView {
2761
2670
  off = start;
2762
2671
  }
2763
2672
  }
2764
- measureVisibleLineHeights() {
2765
- let result = [], { from, to } = this.view.viewState.viewport;
2673
+ measureVisibleLineHeights(viewport) {
2674
+ let result = [], { from, to } = viewport;
2766
2675
  let contentWidth = this.view.contentDOM.clientWidth;
2767
2676
  let isWider = contentWidth > Math.max(this.view.scrollDOM.clientWidth, this.minWidth) + 1;
2768
- let widest = -1;
2677
+ let widest = -1, ltr = this.view.textDirection == Direction.LTR;
2769
2678
  for (let pos = 0, i = 0; i < this.children.length; i++) {
2770
2679
  let child = this.children[i], end = pos + child.length;
2771
2680
  if (end > to)
@@ -2778,8 +2687,7 @@ class DocView extends ContentView {
2778
2687
  let rects = last ? clientRectsFor(last) : [];
2779
2688
  if (rects.length) {
2780
2689
  let rect = rects[rects.length - 1];
2781
- let width = this.view.textDirection == Direction.LTR ? rect.right - childRect.left
2782
- : childRect.right - rect.left;
2690
+ let width = ltr ? rect.right - childRect.left : childRect.right - rect.left;
2783
2691
  if (width > widest) {
2784
2692
  widest = width;
2785
2693
  this.minWidth = contentWidth;
@@ -2793,6 +2701,10 @@ class DocView extends ContentView {
2793
2701
  }
2794
2702
  return result;
2795
2703
  }
2704
+ textDirectionAt(pos) {
2705
+ let { i } = this.childPos(pos, 1);
2706
+ return getComputedStyle(this.children[i].dom).direction == "rtl" ? Direction.RTL : Direction.LTR;
2707
+ }
2796
2708
  measureTextSize() {
2797
2709
  for (let child of this.children) {
2798
2710
  if (child instanceof LineView) {
@@ -2844,11 +2756,14 @@ class DocView extends ContentView {
2844
2756
  return Decoration.set(deco);
2845
2757
  }
2846
2758
  updateDeco() {
2847
- let pluginDecorations = this.view.pluginField(PluginField.decorations);
2848
- this.pluginDecorationLength = pluginDecorations.length;
2759
+ let allDeco = this.view.state.facet(decorations).map((d, i) => {
2760
+ let dynamic = this.dynamicDecorationMap[i] = typeof d == "function";
2761
+ return dynamic ? d(this.view) : d;
2762
+ });
2763
+ for (let i = allDeco.length; i < allDeco.length + 3; i++)
2764
+ this.dynamicDecorationMap[i] = false;
2849
2765
  return this.decorations = [
2850
- ...pluginDecorations,
2851
- ...this.view.state.facet(decorations),
2766
+ ...allDeco,
2852
2767
  this.compositionDeco,
2853
2768
  this.computeBlockGapDeco(),
2854
2769
  this.view.viewState.lineGapDeco
@@ -2863,7 +2778,7 @@ class DocView extends ContentView {
2863
2778
  rect = { left: Math.min(rect.left, other.left), top: Math.min(rect.top, other.top),
2864
2779
  right: Math.max(rect.right, other.right), bottom: Math.max(rect.bottom, other.bottom) };
2865
2780
  let mLeft = 0, mRight = 0, mTop = 0, mBottom = 0;
2866
- for (let margins of this.view.pluginField(PluginField.scrollMargins))
2781
+ for (let margins of this.view.state.facet(scrollMargins).map(f => f(this.view)))
2867
2782
  if (margins) {
2868
2783
  let { left, right, top, bottom } = margins;
2869
2784
  if (left != null)
@@ -3252,7 +3167,8 @@ function moveToLineBoundary(view, start, forward, includeWrap) {
3252
3167
  : view.coordsAtPos(start.assoc < 0 && start.head > line.from ? start.head - 1 : start.head);
3253
3168
  if (coords) {
3254
3169
  let editorRect = view.dom.getBoundingClientRect();
3255
- let pos = view.posAtCoords({ x: forward == (view.textDirection == Direction.LTR) ? editorRect.right - 1 : editorRect.left + 1,
3170
+ let direction = view.textDirectionAt(line.from);
3171
+ let pos = view.posAtCoords({ x: forward == (direction == Direction.LTR) ? editorRect.right - 1 : editorRect.left + 1,
3256
3172
  y: (coords.top + coords.bottom) / 2 });
3257
3173
  if (pos != null)
3258
3174
  return EditorSelection.cursor(pos, forward ? -1 : 1);
@@ -3263,8 +3179,9 @@ function moveToLineBoundary(view, start, forward, includeWrap) {
3263
3179
  }
3264
3180
  function moveByChar(view, start, forward, by) {
3265
3181
  let line = view.state.doc.lineAt(start.head), spans = view.bidiSpans(line);
3182
+ let direction = view.textDirectionAt(line.from);
3266
3183
  for (let cur = start, check = null;;) {
3267
- let next = moveVisually(line, spans, view.textDirection, cur, forward), char = movedOver;
3184
+ let next = moveVisually(line, spans, direction, cur, forward), char = movedOver;
3268
3185
  if (!next) {
3269
3186
  if (line.number == (forward ? view.state.doc.lines : 1))
3270
3187
  return cur;
@@ -3307,7 +3224,7 @@ function moveVertically(view, start, forward, distance) {
3307
3224
  startY = dir < 0 ? startCoords.top : startCoords.bottom;
3308
3225
  }
3309
3226
  else {
3310
- let line = view.viewState.lineBlockAt(startPos - docTop);
3227
+ let line = view.viewState.lineBlockAt(startPos);
3311
3228
  if (goal == null)
3312
3229
  goal = Math.min(rect.right - rect.left, view.defaultCharacterWidth * (startPos - line.from));
3313
3230
  startY = (dir < 0 ? line.top : line.bottom) + docTop;
@@ -3322,7 +3239,7 @@ function moveVertically(view, start, forward, distance) {
3322
3239
  }
3323
3240
  }
3324
3241
  function skipAtoms(view, oldPos, pos) {
3325
- let atoms = view.pluginField(PluginField.atomicRanges);
3242
+ let atoms = view.state.facet(atomicRanges).map(f => f(view));
3326
3243
  for (;;) {
3327
3244
  let moved = false;
3328
3245
  for (let set of atoms) {
@@ -3384,7 +3301,6 @@ class InputState {
3384
3301
  this.registeredEvents.push(type);
3385
3302
  }
3386
3303
  this.notifiedFocused = view.hasFocus;
3387
- this.ensureHandlers(view);
3388
3304
  // On Safari adding an input event handler somehow prevents an
3389
3305
  // issue where the composition vanishes when you press enter.
3390
3306
  if (browser.safari)
@@ -3394,20 +3310,23 @@ class InputState {
3394
3310
  this.lastSelectionOrigin = origin;
3395
3311
  this.lastSelectionTime = Date.now();
3396
3312
  }
3397
- ensureHandlers(view) {
3398
- let handlers = this.customHandlers = view.pluginField(domEventHandlers);
3399
- for (let set of handlers) {
3400
- for (let type in set.handlers)
3401
- if (this.registeredEvents.indexOf(type) < 0 && type != "scroll") {
3402
- this.registeredEvents.push(type);
3403
- view.contentDOM.addEventListener(type, (event) => {
3404
- if (!eventBelongsToEditor(view, event))
3405
- return;
3406
- if (this.runCustomHandlers(type, view, event))
3407
- event.preventDefault();
3408
- });
3409
- }
3410
- }
3313
+ ensureHandlers(view, plugins) {
3314
+ var _a;
3315
+ let handlers;
3316
+ for (let plugin of plugins)
3317
+ if (handlers = (_a = plugin.update(view).spec) === null || _a === void 0 ? void 0 : _a.domEventHandlers) {
3318
+ this.customHandlers.push({ plugin: plugin.value, handlers });
3319
+ for (let type in handlers)
3320
+ if (this.registeredEvents.indexOf(type) < 0 && type != "scroll") {
3321
+ this.registeredEvents.push(type);
3322
+ view.contentDOM.addEventListener(type, (event) => {
3323
+ if (!eventBelongsToEditor(view, event))
3324
+ return;
3325
+ if (this.runCustomHandlers(type, view, event))
3326
+ event.preventDefault();
3327
+ });
3328
+ }
3329
+ }
3411
3330
  }
3412
3331
  runCustomHandlers(type, view, event) {
3413
3332
  for (let set of this.customHandlers) {
@@ -3485,7 +3404,7 @@ class InputState {
3485
3404
  // compositionend and keydown events are sometimes emitted in the
3486
3405
  // wrong order. The key event should still be ignored, even when
3487
3406
  // it happens after the compositionend event.
3488
- if (browser.safari && Date.now() - this.compositionEndedAt < 500) {
3407
+ if (browser.safari && Date.now() - this.compositionEndedAt < 100) {
3489
3408
  this.compositionEndedAt = 0;
3490
3409
  return true;
3491
3410
  }
@@ -3993,7 +3912,6 @@ class HeightOracle {
3993
3912
  constructor() {
3994
3913
  this.doc = Text.empty;
3995
3914
  this.lineWrapping = false;
3996
- this.direction = Direction.LTR;
3997
3915
  this.heightSamples = {};
3998
3916
  this.lineHeight = 14;
3999
3917
  this.charWidth = 7;
@@ -4014,8 +3932,8 @@ class HeightOracle {
4014
3932
  return lines * this.lineHeight;
4015
3933
  }
4016
3934
  setDoc(doc) { this.doc = doc; return this; }
4017
- mustRefreshForStyle(whiteSpace, direction) {
4018
- return (wrappingWhiteSpace.indexOf(whiteSpace) > -1) != this.lineWrapping || this.direction != direction;
3935
+ mustRefreshForWrapping(whiteSpace) {
3936
+ return (wrappingWhiteSpace.indexOf(whiteSpace) > -1) != this.lineWrapping;
4019
3937
  }
4020
3938
  mustRefreshForHeights(lineHeights) {
4021
3939
  let newHeight = false;
@@ -4031,13 +3949,10 @@ class HeightOracle {
4031
3949
  }
4032
3950
  return newHeight;
4033
3951
  }
4034
- refresh(whiteSpace, direction, lineHeight, charWidth, lineLength, knownHeights) {
3952
+ refresh(whiteSpace, lineHeight, charWidth, lineLength, knownHeights) {
4035
3953
  let lineWrapping = wrappingWhiteSpace.indexOf(whiteSpace) > -1;
4036
- let changed = Math.round(lineHeight) != Math.round(this.lineHeight) ||
4037
- this.lineWrapping != lineWrapping ||
4038
- this.direction != direction;
3954
+ let changed = Math.round(lineHeight) != Math.round(this.lineHeight) || this.lineWrapping != lineWrapping;
4039
3955
  this.lineWrapping = lineWrapping;
4040
- this.direction = direction;
4041
3956
  this.lineHeight = lineHeight;
4042
3957
  this.charWidth = charWidth;
4043
3958
  this.lineLength = lineLength;
@@ -4118,12 +4033,6 @@ class BlockInfo {
4118
4033
  .concat(Array.isArray(other.type) ? other.type : [other]);
4119
4034
  return new BlockInfo(this.from, this.length + other.length, this.top, this.height + other.height, detail);
4120
4035
  }
4121
- /**
4122
- FIXME remove on next breaking release @internal
4123
- */
4124
- moveY(offset) {
4125
- return !offset ? this : new BlockInfo(this.from, this.length, this.top + offset, this.height, Array.isArray(this.type) ? this.type.map(b => b.moveY(offset)) : this.type);
4126
- }
4127
4036
  }
4128
4037
  var QueryType = /*@__PURE__*/(function (QueryType) {
4129
4038
  QueryType[QueryType["ByPos"] = 0] = "ByPos";
@@ -4247,8 +4156,9 @@ class HeightMapBlock extends HeightMap {
4247
4156
  lineAt(_value, _type, doc, top, offset) {
4248
4157
  return this.blockAt(0, doc, top, offset);
4249
4158
  }
4250
- forEachLine(_from, _to, doc, top, offset, f) {
4251
- f(this.blockAt(0, doc, top, offset));
4159
+ forEachLine(from, to, doc, top, offset, f) {
4160
+ if (from <= offset + this.length && to >= offset)
4161
+ f(this.blockAt(0, doc, top, offset));
4252
4162
  }
4253
4163
  updateHeight(oracle, offset = 0, _force = false, measured) {
4254
4164
  if (measured && measured.from <= offset && measured.more)
@@ -4749,6 +4659,7 @@ class ViewState {
4749
4659
  // Flag set when editor content was redrawn, so that the next
4750
4660
  // measure stage knows it must read DOM layout
4751
4661
  this.mustMeasureContent = true;
4662
+ this.defaultTextDirection = Direction.RTL;
4752
4663
  this.visibleRanges = [];
4753
4664
  // Cursor 'assoc' is only significant when the cursor is on a line
4754
4665
  // wrap point, where it must stick to the character that it is
@@ -4759,7 +4670,8 @@ class ViewState {
4759
4670
  // boundary and, if so, reset it to make sure it is positioned in
4760
4671
  // the right place.
4761
4672
  this.mustEnforceCursorAssoc = false;
4762
- this.heightMap = HeightMap.empty().applyChanges(state.facet(decorations), Text.empty, this.heightOracle.setDoc(state.doc), [new ChangedRange(0, 0, 0, state.doc.length)]);
4673
+ this.stateDeco = state.facet(decorations).filter(d => typeof d != "function");
4674
+ this.heightMap = HeightMap.empty().applyChanges(this.stateDeco, Text.empty, this.heightOracle.setDoc(state.doc), [new ChangedRange(0, 0, 0, state.doc.length)]);
4763
4675
  this.viewport = this.getViewport(0, null);
4764
4676
  this.updateViewportLines();
4765
4677
  this.updateForViewport();
@@ -4787,13 +4699,13 @@ class ViewState {
4787
4699
  });
4788
4700
  }
4789
4701
  update(update, scrollTarget = null) {
4790
- let prev = this.state;
4791
4702
  this.state = update.state;
4792
- let newDeco = this.state.facet(decorations);
4703
+ let prevDeco = this.stateDeco;
4704
+ this.stateDeco = this.state.facet(decorations).filter(d => typeof d != "function");
4793
4705
  let contentChanges = update.changedRanges;
4794
- let heightChanges = ChangedRange.extendWithRanges(contentChanges, heightRelevantDecoChanges(update.startState.facet(decorations), newDeco, update ? update.changes : ChangeSet.empty(this.state.doc.length)));
4706
+ let heightChanges = ChangedRange.extendWithRanges(contentChanges, heightRelevantDecoChanges(prevDeco, this.stateDeco, update ? update.changes : ChangeSet.empty(this.state.doc.length)));
4795
4707
  let prevHeight = this.heightMap.height;
4796
- this.heightMap = this.heightMap.applyChanges(newDeco, prev.doc, this.heightOracle.setDoc(this.state.doc), heightChanges);
4708
+ this.heightMap = this.heightMap.applyChanges(this.stateDeco, update.startState.doc, this.heightOracle.setDoc(this.state.doc), heightChanges);
4797
4709
  if (this.heightMap.height != prevHeight)
4798
4710
  update.flags |= 2 /* Height */;
4799
4711
  let viewport = heightChanges.length ? this.mapViewport(this.viewport, update.changes) : this.viewport;
@@ -4818,8 +4730,9 @@ class ViewState {
4818
4730
  measure(view) {
4819
4731
  let dom = view.contentDOM, style = window.getComputedStyle(dom);
4820
4732
  let oracle = this.heightOracle;
4821
- let whiteSpace = style.whiteSpace, direction = style.direction == "rtl" ? Direction.RTL : Direction.LTR;
4822
- let refresh = this.heightOracle.mustRefreshForStyle(whiteSpace, direction);
4733
+ let whiteSpace = style.whiteSpace;
4734
+ this.defaultTextDirection = style.direction == "rtl" ? Direction.RTL : Direction.LTR;
4735
+ let refresh = this.heightOracle.mustRefreshForWrapping(whiteSpace);
4823
4736
  let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != dom.clientHeight;
4824
4737
  let result = 0, bias = 0;
4825
4738
  if (this.editorWidth != view.scrollDOM.clientWidth) {
@@ -4858,12 +4771,12 @@ class ViewState {
4858
4771
  result |= 8 /* Geometry */;
4859
4772
  }
4860
4773
  if (measureContent) {
4861
- let lineHeights = view.docView.measureVisibleLineHeights();
4774
+ let lineHeights = view.docView.measureVisibleLineHeights(this.viewport);
4862
4775
  if (oracle.mustRefreshForHeights(lineHeights))
4863
4776
  refresh = true;
4864
4777
  if (refresh || oracle.lineWrapping && Math.abs(contentWidth - this.contentDOMWidth) > oracle.charWidth) {
4865
4778
  let { lineHeight, charWidth } = view.docView.measureTextSize();
4866
- refresh = oracle.refresh(whiteSpace, direction, lineHeight, charWidth, contentWidth / charWidth, lineHeights);
4779
+ refresh = oracle.refresh(whiteSpace, lineHeight, charWidth, contentWidth / charWidth, lineHeights);
4867
4780
  if (refresh) {
4868
4781
  view.docView.minWidth = 0;
4869
4782
  result |= 8 /* Geometry */;
@@ -4874,7 +4787,10 @@ class ViewState {
4874
4787
  else if (dTop < 0 && dBottom < 0)
4875
4788
  bias = Math.min(dTop, dBottom);
4876
4789
  oracle.heightChanged = false;
4877
- this.heightMap = this.heightMap.updateHeight(oracle, 0, refresh, new MeasuredHeights(this.viewport.from, lineHeights));
4790
+ for (let vp of this.viewports) {
4791
+ let heights = vp.from == this.viewport.from ? lineHeights : view.docView.measureVisibleLineHeights(vp);
4792
+ this.heightMap = this.heightMap.updateHeight(oracle, 0, refresh, new MeasuredHeights(vp.from, heights));
4793
+ }
4878
4794
  if (oracle.heightChanged)
4879
4795
  result |= 2 /* Height */;
4880
4796
  }
@@ -4960,12 +4876,12 @@ class ViewState {
4960
4876
  ensureLineGaps(current) {
4961
4877
  let gaps = [];
4962
4878
  // This won't work at all in predominantly right-to-left text.
4963
- if (this.heightOracle.direction != Direction.LTR)
4879
+ if (this.defaultTextDirection != Direction.LTR)
4964
4880
  return gaps;
4965
4881
  for (let line of this.viewportLines) {
4966
4882
  if (line.length < 4000 /* DoubleMargin */)
4967
4883
  continue;
4968
- let structure = lineStructure(line.from, line.to, this.state);
4884
+ let structure = lineStructure(line.from, line.to, this.stateDeco);
4969
4885
  if (structure.total < 4000 /* DoubleMargin */)
4970
4886
  continue;
4971
4887
  let viewFrom, viewTo;
@@ -5016,7 +4932,7 @@ class ViewState {
5016
4932
  }
5017
4933
  }
5018
4934
  computeVisibleRanges() {
5019
- let deco = this.state.facet(decorations);
4935
+ let deco = this.stateDeco;
5020
4936
  if (this.lineGaps.length)
5021
4937
  deco = deco.concat(this.lineGapDeco);
5022
4938
  let ranges = [];
@@ -5052,9 +4968,9 @@ class Viewport {
5052
4968
  this.to = to;
5053
4969
  }
5054
4970
  }
5055
- function lineStructure(from, to, state) {
4971
+ function lineStructure(from, to, stateDeco) {
5056
4972
  let ranges = [], pos = from, total = 0;
5057
- RangeSet.spans(state.facet(decorations), from, to, {
4973
+ RangeSet.spans(stateDeco, from, to, {
5058
4974
  span() { },
5059
4975
  point(from, to) {
5060
4976
  if (from > pos) {
@@ -5187,7 +5103,7 @@ function buildTheme(main, spec, scopes) {
5187
5103
  }
5188
5104
  });
5189
5105
  }
5190
- const baseTheme = /*@__PURE__*/buildTheme("." + baseThemeID, {
5106
+ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
5191
5107
  "&.cm-editor": {
5192
5108
  position: "relative !important",
5193
5109
  boxSizing: "border-box",
@@ -5292,6 +5208,65 @@ const baseTheme = /*@__PURE__*/buildTheme("." + baseThemeID, {
5292
5208
  "&dark .cm-activeLine": { backgroundColor: "#223039" },
5293
5209
  "&light .cm-specialChar": { color: "red" },
5294
5210
  "&dark .cm-specialChar": { color: "#f78" },
5211
+ ".cm-gutters": {
5212
+ display: "flex",
5213
+ height: "100%",
5214
+ boxSizing: "border-box",
5215
+ left: 0,
5216
+ zIndex: 200
5217
+ },
5218
+ "&light .cm-gutters": {
5219
+ backgroundColor: "#f5f5f5",
5220
+ color: "#6c6c6c",
5221
+ borderRight: "1px solid #ddd"
5222
+ },
5223
+ "&dark .cm-gutters": {
5224
+ backgroundColor: "#333338",
5225
+ color: "#ccc"
5226
+ },
5227
+ ".cm-gutter": {
5228
+ display: "flex !important",
5229
+ flexDirection: "column",
5230
+ flexShrink: 0,
5231
+ boxSizing: "border-box",
5232
+ minHeight: "100%",
5233
+ overflow: "hidden"
5234
+ },
5235
+ ".cm-gutterElement": {
5236
+ boxSizing: "border-box"
5237
+ },
5238
+ ".cm-lineNumbers .cm-gutterElement": {
5239
+ padding: "0 3px 0 5px",
5240
+ minWidth: "20px",
5241
+ textAlign: "right",
5242
+ whiteSpace: "nowrap"
5243
+ },
5244
+ "&light .cm-activeLineGutter": {
5245
+ backgroundColor: "#e2f2ff"
5246
+ },
5247
+ "&dark .cm-activeLineGutter": {
5248
+ backgroundColor: "#222227"
5249
+ },
5250
+ ".cm-panels": {
5251
+ boxSizing: "border-box",
5252
+ position: "sticky",
5253
+ left: 0,
5254
+ right: 0
5255
+ },
5256
+ "&light .cm-panels": {
5257
+ backgroundColor: "#f5f5f5",
5258
+ color: "black"
5259
+ },
5260
+ "&light .cm-panels-top": {
5261
+ borderBottom: "1px solid #ddd"
5262
+ },
5263
+ "&light .cm-panels-bottom": {
5264
+ borderTop: "1px solid #ddd"
5265
+ },
5266
+ "&dark .cm-panels": {
5267
+ backgroundColor: "#333338",
5268
+ color: "white"
5269
+ },
5295
5270
  ".cm-tab": {
5296
5271
  display: "inline-block",
5297
5272
  overflow: "hidden",
@@ -5749,7 +5724,7 @@ function applyDOMChange(view, start, end, typeOver) {
5749
5724
  diff.toB == diff.from + 2 && reader.text.slice(diff.from, diff.toB) == LineBreakPlaceholder + LineBreakPlaceholder)
5750
5725
  diff.toB--;
5751
5726
  change = { from: from + diff.from, to: from + diff.toA,
5752
- insert: Text$1.of(reader.text.slice(diff.from, diff.toB).split(LineBreakPlaceholder)) };
5727
+ insert: Text.of(reader.text.slice(diff.from, diff.toB).split(LineBreakPlaceholder)) };
5753
5728
  }
5754
5729
  newSel = selectionFromPoints(selPoints, from);
5755
5730
  }
@@ -5940,9 +5915,9 @@ transactions for editing actions.
5940
5915
  */
5941
5916
  class EditorView {
5942
5917
  /**
5943
- Construct a new view. You'll usually want to put `view.dom` into
5944
- your document after creating a view, so that the user can see
5945
- it.
5918
+ Construct a new view. You'll want to either provide a `parent`
5919
+ option, or put `view.dom` into your document after creating a
5920
+ view, so that the user can see the editor.
5946
5921
  */
5947
5922
  constructor(
5948
5923
  /**
@@ -5993,6 +5968,7 @@ class EditorView {
5993
5968
  this.measure();
5994
5969
  });
5995
5970
  this.inputState = new InputState(this);
5971
+ this.inputState.ensureHandlers(this, this.plugins);
5996
5972
  this.docView = new DocView(this);
5997
5973
  this.mountStyles();
5998
5974
  this.updateAttrs();
@@ -6080,14 +6056,9 @@ class EditorView {
6080
6056
  let { main } = tr.state.selection;
6081
6057
  scrollTarget = new ScrollTarget(main.empty ? main : EditorSelection.cursor(main.head, main.head > main.anchor ? -1 : 1));
6082
6058
  }
6083
- for (let e of tr.effects) {
6084
- if (e.is(scrollTo))
6085
- scrollTarget = new ScrollTarget(e.value);
6086
- else if (e.is(centerOn))
6087
- scrollTarget = new ScrollTarget(e.value, "center");
6088
- else if (e.is(scrollIntoView))
6059
+ for (let e of tr.effects)
6060
+ if (e.is(scrollIntoView))
6089
6061
  scrollTarget = e.value;
6090
- }
6091
6062
  }
6092
6063
  this.viewState.update(update, scrollTarget);
6093
6064
  this.bidiCache = CachedOrder.update(this.bidiCache, update.changes);
@@ -6138,7 +6109,7 @@ class EditorView {
6138
6109
  for (let plugin of this.plugins)
6139
6110
  plugin.update(this);
6140
6111
  this.docView = new DocView(this);
6141
- this.inputState.ensureHandlers(this);
6112
+ this.inputState.ensureHandlers(this, this.plugins);
6142
6113
  this.mountStyles();
6143
6114
  this.updateAttrs();
6144
6115
  this.bidiCache = [];
@@ -6170,7 +6141,7 @@ class EditorView {
6170
6141
  plugin.destroy(this);
6171
6142
  this.plugins = newPlugins;
6172
6143
  this.pluginMap.clear();
6173
- this.inputState.ensureHandlers(this);
6144
+ this.inputState.ensureHandlers(this, this.plugins);
6174
6145
  }
6175
6146
  else {
6176
6147
  for (let p of this.plugins)
@@ -6308,7 +6279,7 @@ class EditorView {
6308
6279
  }
6309
6280
  mountStyles() {
6310
6281
  this.styleModules = this.state.facet(styleModule);
6311
- StyleModule.mount(this.root, this.styleModules.concat(baseTheme).reverse());
6282
+ StyleModule.mount(this.root, this.styleModules.concat(baseTheme$1).reverse());
6312
6283
  }
6313
6284
  readMeasured() {
6314
6285
  if (this.updateState == 2 /* Updating */)
@@ -6339,16 +6310,6 @@ class EditorView {
6339
6310
  }
6340
6311
  }
6341
6312
  /**
6342
- Collect all values provided by the active plugins for a given
6343
- field.
6344
- */
6345
- pluginField(field) {
6346
- let result = [];
6347
- for (let plugin of this.plugins)
6348
- plugin.update(this).takeField(field, result);
6349
- return result;
6350
- }
6351
- /**
6352
6313
  Get the value of a specific plugin, if present. Note that
6353
6314
  plugins that crash can be dropped from a view, so even when you
6354
6315
  know you registered a given plugin, it is recommended to check
@@ -6375,23 +6336,6 @@ class EditorView {
6375
6336
  return { top: this.viewState.paddingTop, bottom: this.viewState.paddingBottom };
6376
6337
  }
6377
6338
  /**
6378
- Find the line or block widget at the given vertical position.
6379
-
6380
- By default, this position is interpreted as a screen position,
6381
- meaning `docTop` is set to the DOM top position of the editor
6382
- content (forcing a layout). You can pass a different `docTop`
6383
- value—for example 0 to interpret `height` as a document-relative
6384
- position, or a precomputed document top
6385
- (`view.contentDOM.getBoundingClientRect().top`) to limit layout
6386
- queries.
6387
-
6388
- *Deprecated: use `elementAtHeight` instead.*
6389
- */
6390
- blockAtHeight(height, docTop) {
6391
- let top = ensureTop(docTop, this);
6392
- return this.elementAtHeight(height - top).moveY(top);
6393
- }
6394
- /**
6395
6339
  Find the text line or block widget at the given vertical
6396
6340
  position (which is interpreted as relative to the [top of the
6397
6341
  document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)
@@ -6401,23 +6345,6 @@ class EditorView {
6401
6345
  return this.viewState.elementAtHeight(height);
6402
6346
  }
6403
6347
  /**
6404
- Find information for the visual line (see
6405
- [`visualLineAt`](https://codemirror.net/6/docs/ref/#view.EditorView.visualLineAt)) at the given
6406
- vertical position. The resulting block info might hold another
6407
- array of block info structs in its `type` field if this line
6408
- consists of more than one block.
6409
-
6410
- Defaults to treating `height` as a screen position. See
6411
- [`blockAtHeight`](https://codemirror.net/6/docs/ref/#view.EditorView.blockAtHeight) for the
6412
- interpretation of the `docTop` parameter.
6413
-
6414
- *Deprecated: use `lineBlockAtHeight` instead.*
6415
- */
6416
- visualLineAtHeight(height, docTop) {
6417
- let top = ensureTop(docTop, this);
6418
- return this.lineBlockAtHeight(height - top).moveY(top);
6419
- }
6420
- /**
6421
6348
  Find the line block (see
6422
6349
  [`lineBlockAt`](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) at the given
6423
6350
  height.
@@ -6427,19 +6354,6 @@ class EditorView {
6427
6354
  return this.viewState.lineBlockAtHeight(height);
6428
6355
  }
6429
6356
  /**
6430
- Iterate over the height information of the visual lines in the
6431
- viewport. The heights of lines are reported relative to the
6432
- given document top, which defaults to the screen position of the
6433
- document (forcing a layout).
6434
-
6435
- *Deprecated: use `viewportLineBlocks` instead.*
6436
- */
6437
- viewportLines(f, docTop) {
6438
- let top = ensureTop(docTop, this);
6439
- for (let line of this.viewportLineBlocks)
6440
- f(line.moveY(top));
6441
- }
6442
- /**
6443
6357
  Get the extent and vertical position of all [line
6444
6358
  blocks](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) in the viewport. Positions
6445
6359
  are relative to the [top of the
@@ -6449,24 +6363,9 @@ class EditorView {
6449
6363
  return this.viewState.viewportLines;
6450
6364
  }
6451
6365
  /**
6452
- Find the extent and height of the visual line (a range delimited
6453
- on both sides by either non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^range)
6454
- line breaks, or the start/end of the document) at the given position.
6455
-
6456
- Vertical positions are computed relative to the `docTop`
6457
- argument, which defaults to 0 for this method. You can pass
6458
- `view.contentDOM.getBoundingClientRect().top` here to get screen
6459
- coordinates.
6460
-
6461
- *Deprecated: use `lineBlockAt` instead.*
6462
- */
6463
- visualLineAt(pos, docTop = 0) {
6464
- return this.lineBlockAt(pos).moveY(docTop + this.viewState.paddingTop);
6465
- }
6466
- /**
6467
6366
  Find the line block around the given document position. A line
6468
6367
  block is a range delimited on both sides by either a
6469
- non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^range) line breaks, or the
6368
+ non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^replace) line breaks, or the
6470
6369
  start/end of the document. It will usually just hold a line of
6471
6370
  text, but may be broken into multiple textblocks by block
6472
6371
  widgets.
@@ -6482,13 +6381,13 @@ class EditorView {
6482
6381
  }
6483
6382
  /**
6484
6383
  Move a cursor position by [grapheme
6485
- cluster](https://codemirror.net/6/docs/ref/#text.findClusterBreak). `forward` determines whether
6486
- the motion is away from the line start, or towards it. Motion in
6487
- bidirectional text is in visual order, in the editor's [text
6488
- direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection). When the start
6489
- position was the last one on the line, the returned position
6490
- will be across the line break. If there is no further line, the
6491
- original position is returned.
6384
+ cluster](https://codemirror.net/6/docs/ref/#state.findClusterBreak). `forward` determines whether
6385
+ the motion is away from the line start, or towards it. In
6386
+ bidirectional text, the line is traversed in visual order, using
6387
+ the editor's [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
6388
+ When the start position was the last one on the line, the
6389
+ returned position will be across the line break. If there is no
6390
+ further line, the original position is returned.
6492
6391
 
6493
6392
  By default, this method moves over a single cluster. The
6494
6393
  optional `by` argument can be used to move across more. It will
@@ -6533,10 +6432,6 @@ class EditorView {
6533
6432
  moveVertically(start, forward, distance) {
6534
6433
  return skipAtoms(this, start, moveVertically(this, start, forward, distance));
6535
6434
  }
6536
- // FIXME remove on next major version
6537
- scrollPosIntoView(pos) {
6538
- this.dispatch({ effects: scrollTo.of(EditorSelection.cursor(pos)) });
6539
- }
6540
6435
  /**
6541
6436
  Find the DOM parent node and offset (child offset if `node` is
6542
6437
  an element, character offset when it is a text node) at the
@@ -6592,9 +6487,25 @@ class EditorView {
6592
6487
  /**
6593
6488
  The text direction
6594
6489
  ([`direction`](https://developer.mozilla.org/en-US/docs/Web/CSS/direction)
6595
- CSS property) of the editor.
6490
+ CSS property) of the editor's content element.
6596
6491
  */
6597
- get textDirection() { return this.viewState.heightOracle.direction; }
6492
+ get textDirection() { return this.viewState.defaultTextDirection; }
6493
+ /**
6494
+ Find the text direction of the block at the given position, as
6495
+ assigned by CSS. If
6496
+ [`perLineTextDirection`](https://codemirror.net/6/docs/ref/#view.EditorView^perLineTextDirection)
6497
+ isn't enabled, or the given position is outside of the viewport,
6498
+ this will always return the same as
6499
+ [`textDirection`](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection). Note that
6500
+ this may trigger a DOM layout.
6501
+ */
6502
+ textDirectionAt(pos) {
6503
+ let perLine = this.state.facet(perLineTextDirection);
6504
+ if (!perLine || pos < this.viewport.from || pos > this.viewport.to)
6505
+ return this.textDirection;
6506
+ this.readMeasured();
6507
+ return this.docView.textDirectionAt(pos);
6508
+ }
6598
6509
  /**
6599
6510
  Whether this editor [wraps lines](https://codemirror.net/6/docs/ref/#view.EditorView.lineWrapping)
6600
6511
  (as determined by the
@@ -6613,11 +6524,11 @@ class EditorView {
6613
6524
  bidiSpans(line) {
6614
6525
  if (line.length > MaxBidiLine)
6615
6526
  return trivialOrder(line.length);
6616
- let dir = this.textDirection;
6527
+ let dir = this.textDirectionAt(line.from);
6617
6528
  for (let entry of this.bidiCache)
6618
6529
  if (entry.from == line.from && entry.dir == dir)
6619
6530
  return entry.order;
6620
- let order = computeOrder(line.text, this.textDirection);
6531
+ let order = computeOrder(line.text, dir);
6621
6532
  this.bidiCache.push(new CachedOrder(line.from, line.to, dir, order));
6622
6533
  return order;
6623
6534
  }
@@ -6668,16 +6579,16 @@ class EditorView {
6668
6579
  return scrollIntoView.of(new ScrollTarget(typeof pos == "number" ? EditorSelection.cursor(pos) : pos, options.y, options.x, options.yMargin, options.xMargin));
6669
6580
  }
6670
6581
  /**
6671
- Facet that can be used to add DOM event handlers. The value
6672
- should be an object mapping event names to handler functions. The
6673
- first such function to return true will be assumed to have handled
6674
- that event, and no other handlers or built-in behavior will be
6675
- activated for it.
6676
- These are registered on the [content
6677
- element](https://codemirror.net/6/docs/ref/#view.EditorView.contentDOM), except for `scroll`
6678
- handlers, which will be called any time the editor's [scroll
6679
- element](https://codemirror.net/6/docs/ref/#view.EditorView.scrollDOM) or one of its parent nodes
6680
- is scrolled.
6582
+ Returns an extension that can be used to add DOM event handlers.
6583
+ The value should be an object mapping event names to handler
6584
+ functions. For any given event, such functions are ordered by
6585
+ extension precedence, and the first handler to return true will
6586
+ be assumed to have handled that event, and no other handlers or
6587
+ built-in behavior will be activated for it. These are registered
6588
+ on the [content element](https://codemirror.net/6/docs/ref/#view.EditorView.contentDOM), except
6589
+ for `scroll` handlers, which will be called any time the
6590
+ editor's [scroll element](https://codemirror.net/6/docs/ref/#view.EditorView.scrollDOM) or one of
6591
+ its parent nodes is scrolled.
6681
6592
  */
6682
6593
  static domEventHandlers(handlers) {
6683
6594
  return ViewPlugin.define(() => ({}), { eventHandlers: handlers });
@@ -6719,20 +6630,6 @@ class EditorView {
6719
6630
  }
6720
6631
  }
6721
6632
  /**
6722
- Effect that can be [added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a
6723
- transaction to make it scroll the given range into view.
6724
-
6725
- *Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead.
6726
- */
6727
- EditorView.scrollTo = scrollTo;
6728
- /**
6729
- Effect that makes the editor scroll the given range to the
6730
- center of the visible view.
6731
-
6732
- *Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead.
6733
- */
6734
- EditorView.centerOn = centerOn;
6735
- /**
6736
6633
  Facet to add a [style
6737
6634
  module](https://github.com/marijnh/style-mod#documentation) to
6738
6635
  an editor view. The view will ensure that the module is
@@ -6749,6 +6646,13 @@ called and the default behavior is prevented.
6749
6646
  */
6750
6647
  EditorView.inputHandler = inputHandler;
6751
6648
  /**
6649
+ By default, the editor assumes all its content has the same
6650
+ [text direction](https://codemirror.net/6/docs/ref/#view.Direction). Configure this with a `true`
6651
+ value to make it read and store the text direction of every
6652
+ (rendered) line separately.
6653
+ */
6654
+ EditorView.perLineTextDirection = perLineTextDirection;
6655
+ /**
6752
6656
  Allows you to provide a function that should be called when the
6753
6657
  library catches an exception from an extension (mostly from view
6754
6658
  plugins, but may be used by other extensions to route exceptions
@@ -6764,9 +6668,9 @@ EditorView.updateListener = updateListener;
6764
6668
  /**
6765
6669
  Facet that controls whether the editor content DOM is editable.
6766
6670
  When its highest-precedence value is `false`, the element will
6767
- not longer have its `contenteditable` attribute set. (Note that
6768
- this doesn't affect API calls that change the editor content,
6769
- even when those are bound to keys or buttons. See the
6671
+ not have its `contenteditable` attribute set. (Note that this
6672
+ doesn't affect API calls that change the editor content, even
6673
+ when those are bound to keys or buttons. See the
6770
6674
  [`readOnly`](https://codemirror.net/6/docs/ref/#state.EditorState.readOnly) facet for that.)
6771
6675
  */
6772
6676
  EditorView.editable = editable;
@@ -6785,18 +6689,45 @@ the drag should move the content.
6785
6689
  */
6786
6690
  EditorView.dragMovesSelection = dragMovesSelection$1;
6787
6691
  /**
6788
- Facet used to configure whether a given selecting click adds
6789
- a new range to the existing selection or replaces it entirely.
6692
+ Facet used to configure whether a given selecting click adds a
6693
+ new range to the existing selection or replaces it entirely. The
6694
+ default behavior is to check `event.metaKey` on macOS, and
6695
+ `event.ctrlKey` elsewhere.
6790
6696
  */
6791
6697
  EditorView.clickAddsSelectionRange = clickAddsSelectionRange;
6792
6698
  /**
6793
6699
  A facet that determines which [decorations](https://codemirror.net/6/docs/ref/#view.Decoration)
6794
- are shown in the view. See also [view
6795
- plugins](https://codemirror.net/6/docs/ref/#view.EditorView^decorations), which have a separate
6796
- mechanism for providing decorations.
6700
+ are shown in the view. Decorations can be provided in two
6701
+ ways—directly, or via a function that takes an editor view.
6702
+
6703
+ Only decoration sets provided directly are allowed to influence
6704
+ the editor's vertical layout structure. The ones provided as
6705
+ functions are called _after_ the new viewport has been computed,
6706
+ and thus **must not** introduce block widgets or replacing
6707
+ decorations that cover line breaks.
6797
6708
  */
6798
6709
  EditorView.decorations = decorations;
6799
6710
  /**
6711
+ Used to provide ranges that should be treated as atoms as far as
6712
+ cursor motion is concerned. This causes methods like
6713
+ [`moveByChar`](https://codemirror.net/6/docs/ref/#view.EditorView.moveByChar) and
6714
+ [`moveVertically`](https://codemirror.net/6/docs/ref/#view.EditorView.moveVertically) (and the
6715
+ commands built on top of them) to skip across such regions when
6716
+ a selection endpoint would enter them. This does _not_ prevent
6717
+ direct programmatic [selection
6718
+ updates](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection) from moving into such
6719
+ regions.
6720
+ */
6721
+ EditorView.atomicRanges = atomicRanges;
6722
+ /**
6723
+ Facet that allows extensions to provide additional scroll
6724
+ margins (space around the sides of the scrolling element that
6725
+ should be considered invisible). This can be useful when the
6726
+ plugin introduces elements that cover part of that element (for
6727
+ example a horizontally fixed gutter).
6728
+ */
6729
+ EditorView.scrollMargins = scrollMargins;
6730
+ /**
6800
6731
  This facet records whether a dark theme is active. The extension
6801
6732
  returned by [`theme`](https://codemirror.net/6/docs/ref/#view.EditorView^theme) automatically
6802
6733
  includes an instance of this when the `dark` option is set to
@@ -6829,10 +6760,6 @@ search match).
6829
6760
  EditorView.announce = /*@__PURE__*/StateEffect.define();
6830
6761
  // Maximum line length for which we compute accurate bidi info
6831
6762
  const MaxBidiLine = 4096;
6832
- // FIXME remove this and its callers on next breaking release
6833
- function ensureTop(given, view) {
6834
- return (given == null ? view.contentDOM.getBoundingClientRect().top : given) + view.viewState.paddingTop;
6835
- }
6836
6763
  const BadMeasure = {};
6837
6764
  class CachedOrder {
6838
6765
  constructor(from, to, dir, order) {
@@ -6935,7 +6862,7 @@ function getKeymap(state) {
6935
6862
  }
6936
6863
  /**
6937
6864
  Run the key handlers registered for a given scope. The event
6938
- object should be `"keydown"` event. Returns true if any of the
6865
+ object should be a `"keydown"` event. Returns true if any of the
6939
6866
  handlers handled it.
6940
6867
  */
6941
6868
  function runScopeHandlers(view, event, scope) {
@@ -7712,9 +7639,1298 @@ function placeholder(content) {
7712
7639
  }, { decorations: v => v.decorations });
7713
7640
  }
7714
7641
 
7642
+ // Don't compute precise column positions for line offsets above this
7643
+ // (since it could get expensive). Assume offset==column for them.
7644
+ const MaxOff = 2000;
7645
+ function rectangleFor(state, a, b) {
7646
+ let startLine = Math.min(a.line, b.line), endLine = Math.max(a.line, b.line);
7647
+ let ranges = [];
7648
+ if (a.off > MaxOff || b.off > MaxOff || a.col < 0 || b.col < 0) {
7649
+ let startOff = Math.min(a.off, b.off), endOff = Math.max(a.off, b.off);
7650
+ for (let i = startLine; i <= endLine; i++) {
7651
+ let line = state.doc.line(i);
7652
+ if (line.length <= endOff)
7653
+ ranges.push(EditorSelection.range(line.from + startOff, line.to + endOff));
7654
+ }
7655
+ }
7656
+ else {
7657
+ let startCol = Math.min(a.col, b.col), endCol = Math.max(a.col, b.col);
7658
+ for (let i = startLine; i <= endLine; i++) {
7659
+ let line = state.doc.line(i);
7660
+ let start = findColumn(line.text, startCol, state.tabSize, true);
7661
+ if (start > -1) {
7662
+ let end = findColumn(line.text, endCol, state.tabSize);
7663
+ ranges.push(EditorSelection.range(line.from + start, line.from + end));
7664
+ }
7665
+ }
7666
+ }
7667
+ return ranges;
7668
+ }
7669
+ function absoluteColumn(view, x) {
7670
+ let ref = view.coordsAtPos(view.viewport.from);
7671
+ return ref ? Math.round(Math.abs((ref.left - x) / view.defaultCharacterWidth)) : -1;
7672
+ }
7673
+ function getPos(view, event) {
7674
+ let offset = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
7675
+ let line = view.state.doc.lineAt(offset), off = offset - line.from;
7676
+ let col = off > MaxOff ? -1
7677
+ : off == line.length ? absoluteColumn(view, event.clientX)
7678
+ : countColumn(line.text, view.state.tabSize, offset - line.from);
7679
+ return { line: line.number, col, off };
7680
+ }
7681
+ function rectangleSelectionStyle(view, event) {
7682
+ let start = getPos(view, event), startSel = view.state.selection;
7683
+ if (!start)
7684
+ return null;
7685
+ return {
7686
+ update(update) {
7687
+ if (update.docChanged) {
7688
+ let newStart = update.changes.mapPos(update.startState.doc.line(start.line).from);
7689
+ let newLine = update.state.doc.lineAt(newStart);
7690
+ start = { line: newLine.number, col: start.col, off: Math.min(start.off, newLine.length) };
7691
+ startSel = startSel.map(update.changes);
7692
+ }
7693
+ },
7694
+ get(event, _extend, multiple) {
7695
+ let cur = getPos(view, event);
7696
+ if (!cur)
7697
+ return startSel;
7698
+ let ranges = rectangleFor(view.state, start, cur);
7699
+ if (!ranges.length)
7700
+ return startSel;
7701
+ if (multiple)
7702
+ return EditorSelection.create(ranges.concat(startSel.ranges));
7703
+ else
7704
+ return EditorSelection.create(ranges);
7705
+ }
7706
+ };
7707
+ }
7708
+ /**
7709
+ Create an extension that enables rectangular selections. By
7710
+ default, it will react to left mouse drag with the Alt key held
7711
+ down. When such a selection occurs, the text within the rectangle
7712
+ that was dragged over will be selected, as one selection
7713
+ [range](https://codemirror.net/6/docs/ref/#state.SelectionRange) per line.
7714
+ */
7715
+ function rectangularSelection(options) {
7716
+ let filter = (options === null || options === void 0 ? void 0 : options.eventFilter) || (e => e.altKey && e.button == 0);
7717
+ return EditorView.mouseSelectionStyle.of((view, event) => filter(event) ? rectangleSelectionStyle(view, event) : null);
7718
+ }
7719
+ const keys = {
7720
+ Alt: [18, e => e.altKey],
7721
+ Control: [17, e => e.ctrlKey],
7722
+ Shift: [16, e => e.shiftKey],
7723
+ Meta: [91, e => e.metaKey]
7724
+ };
7725
+ const showCrosshair = { style: "cursor: crosshair" };
7726
+ /**
7727
+ Returns an extension that turns the pointer cursor into a
7728
+ crosshair when a given modifier key, defaulting to Alt, is held
7729
+ down. Can serve as a visual hint that rectangular selection is
7730
+ going to happen when paired with
7731
+ [`rectangularSelection`](https://codemirror.net/6/docs/ref/#view.rectangularSelection).
7732
+ */
7733
+ function crosshairCursor(options = {}) {
7734
+ let [code, getter] = keys[options.key || "Alt"];
7735
+ let plugin = ViewPlugin.fromClass(class {
7736
+ constructor(view) {
7737
+ this.view = view;
7738
+ this.isDown = false;
7739
+ }
7740
+ set(isDown) {
7741
+ if (this.isDown != isDown) {
7742
+ this.isDown = isDown;
7743
+ this.view.update([]);
7744
+ }
7745
+ }
7746
+ }, {
7747
+ eventHandlers: {
7748
+ keydown(e) {
7749
+ this.set(e.keyCode == code || getter(e));
7750
+ },
7751
+ keyup(e) {
7752
+ if (e.keyCode == code || !getter(e))
7753
+ this.set(false);
7754
+ }
7755
+ }
7756
+ });
7757
+ return [
7758
+ plugin,
7759
+ EditorView.contentAttributes.of(view => { var _a; return ((_a = view.plugin(plugin)) === null || _a === void 0 ? void 0 : _a.isDown) ? showCrosshair : null; })
7760
+ ];
7761
+ }
7762
+
7763
+ const Outside = "-10000px";
7764
+ class TooltipViewManager {
7765
+ constructor(view, facet, createTooltipView) {
7766
+ this.facet = facet;
7767
+ this.createTooltipView = createTooltipView;
7768
+ this.input = view.state.facet(facet);
7769
+ this.tooltips = this.input.filter(t => t);
7770
+ this.tooltipViews = this.tooltips.map(createTooltipView);
7771
+ }
7772
+ update(update) {
7773
+ let input = update.state.facet(this.facet);
7774
+ let tooltips = input.filter(x => x);
7775
+ if (input === this.input) {
7776
+ for (let t of this.tooltipViews)
7777
+ if (t.update)
7778
+ t.update(update);
7779
+ return false;
7780
+ }
7781
+ let tooltipViews = [];
7782
+ for (let i = 0; i < tooltips.length; i++) {
7783
+ let tip = tooltips[i], known = -1;
7784
+ if (!tip)
7785
+ continue;
7786
+ for (let i = 0; i < this.tooltips.length; i++) {
7787
+ let other = this.tooltips[i];
7788
+ if (other && other.create == tip.create)
7789
+ known = i;
7790
+ }
7791
+ if (known < 0) {
7792
+ tooltipViews[i] = this.createTooltipView(tip);
7793
+ }
7794
+ else {
7795
+ let tooltipView = tooltipViews[i] = this.tooltipViews[known];
7796
+ if (tooltipView.update)
7797
+ tooltipView.update(update);
7798
+ }
7799
+ }
7800
+ for (let t of this.tooltipViews)
7801
+ if (tooltipViews.indexOf(t) < 0)
7802
+ t.dom.remove();
7803
+ this.input = input;
7804
+ this.tooltips = tooltips;
7805
+ this.tooltipViews = tooltipViews;
7806
+ return true;
7807
+ }
7808
+ }
7809
+ /**
7810
+ Creates an extension that configures tooltip behavior.
7811
+ */
7812
+ function tooltips(config = {}) {
7813
+ return tooltipConfig.of(config);
7814
+ }
7815
+ function windowSpace() {
7816
+ return { top: 0, left: 0, bottom: innerHeight, right: innerWidth };
7817
+ }
7818
+ const tooltipConfig = /*@__PURE__*/Facet.define({
7819
+ combine: values => {
7820
+ var _a, _b, _c;
7821
+ return ({
7822
+ position: browser.ios ? "absolute" : ((_a = values.find(conf => conf.position)) === null || _a === void 0 ? void 0 : _a.position) || "fixed",
7823
+ parent: ((_b = values.find(conf => conf.parent)) === null || _b === void 0 ? void 0 : _b.parent) || null,
7824
+ tooltipSpace: ((_c = values.find(conf => conf.tooltipSpace)) === null || _c === void 0 ? void 0 : _c.tooltipSpace) || windowSpace,
7825
+ });
7826
+ }
7827
+ });
7828
+ const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
7829
+ constructor(view) {
7830
+ var _a;
7831
+ this.view = view;
7832
+ this.inView = true;
7833
+ this.lastTransaction = 0;
7834
+ this.measureTimeout = -1;
7835
+ let config = view.state.facet(tooltipConfig);
7836
+ this.position = config.position;
7837
+ this.parent = config.parent;
7838
+ this.classes = view.themeClasses;
7839
+ this.createContainer();
7840
+ this.measureReq = { read: this.readMeasure.bind(this), write: this.writeMeasure.bind(this), key: this };
7841
+ this.manager = new TooltipViewManager(view, showTooltip, t => this.createTooltip(t));
7842
+ this.intersectionObserver = typeof IntersectionObserver == "function" ? new IntersectionObserver(entries => {
7843
+ if (Date.now() > this.lastTransaction - 50 &&
7844
+ entries.length > 0 && entries[entries.length - 1].intersectionRatio < 1)
7845
+ this.measureSoon();
7846
+ }, { threshold: [1] }) : null;
7847
+ this.observeIntersection();
7848
+ (_a = view.dom.ownerDocument.defaultView) === null || _a === void 0 ? void 0 : _a.addEventListener("resize", this.measureSoon = this.measureSoon.bind(this));
7849
+ this.maybeMeasure();
7850
+ }
7851
+ createContainer() {
7852
+ if (this.parent) {
7853
+ this.container = document.createElement("div");
7854
+ this.container.style.position = "relative";
7855
+ this.container.className = this.view.themeClasses;
7856
+ this.parent.appendChild(this.container);
7857
+ }
7858
+ else {
7859
+ this.container = this.view.dom;
7860
+ }
7861
+ }
7862
+ observeIntersection() {
7863
+ if (this.intersectionObserver) {
7864
+ this.intersectionObserver.disconnect();
7865
+ for (let tooltip of this.manager.tooltipViews)
7866
+ this.intersectionObserver.observe(tooltip.dom);
7867
+ }
7868
+ }
7869
+ measureSoon() {
7870
+ if (this.measureTimeout < 0)
7871
+ this.measureTimeout = setTimeout(() => {
7872
+ this.measureTimeout = -1;
7873
+ this.maybeMeasure();
7874
+ }, 50);
7875
+ }
7876
+ update(update) {
7877
+ if (update.transactions.length)
7878
+ this.lastTransaction = Date.now();
7879
+ let updated = this.manager.update(update);
7880
+ if (updated)
7881
+ this.observeIntersection();
7882
+ let shouldMeasure = updated || update.geometryChanged;
7883
+ let newConfig = update.state.facet(tooltipConfig);
7884
+ if (newConfig.position != this.position) {
7885
+ this.position = newConfig.position;
7886
+ for (let t of this.manager.tooltipViews)
7887
+ t.dom.style.position = this.position;
7888
+ shouldMeasure = true;
7889
+ }
7890
+ if (newConfig.parent != this.parent) {
7891
+ if (this.parent)
7892
+ this.container.remove();
7893
+ this.parent = newConfig.parent;
7894
+ this.createContainer();
7895
+ for (let t of this.manager.tooltipViews)
7896
+ this.container.appendChild(t.dom);
7897
+ shouldMeasure = true;
7898
+ }
7899
+ else if (this.parent && this.view.themeClasses != this.classes) {
7900
+ this.classes = this.container.className = this.view.themeClasses;
7901
+ }
7902
+ if (shouldMeasure)
7903
+ this.maybeMeasure();
7904
+ }
7905
+ createTooltip(tooltip) {
7906
+ let tooltipView = tooltip.create(this.view);
7907
+ tooltipView.dom.classList.add("cm-tooltip");
7908
+ if (tooltip.arrow && !tooltipView.dom.querySelector(".cm-tooltip > .cm-tooltip-arrow")) {
7909
+ let arrow = document.createElement("div");
7910
+ arrow.className = "cm-tooltip-arrow";
7911
+ tooltipView.dom.appendChild(arrow);
7912
+ }
7913
+ tooltipView.dom.style.position = this.position;
7914
+ tooltipView.dom.style.top = Outside;
7915
+ this.container.appendChild(tooltipView.dom);
7916
+ if (tooltipView.mount)
7917
+ tooltipView.mount(this.view);
7918
+ return tooltipView;
7919
+ }
7920
+ destroy() {
7921
+ var _a, _b;
7922
+ (_a = this.view.dom.ownerDocument.defaultView) === null || _a === void 0 ? void 0 : _a.removeEventListener("resize", this.measureSoon);
7923
+ for (let { dom } of this.manager.tooltipViews)
7924
+ dom.remove();
7925
+ (_b = this.intersectionObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
7926
+ clearTimeout(this.measureTimeout);
7927
+ }
7928
+ readMeasure() {
7929
+ let editor = this.view.dom.getBoundingClientRect();
7930
+ return {
7931
+ editor,
7932
+ parent: this.parent ? this.container.getBoundingClientRect() : editor,
7933
+ pos: this.manager.tooltips.map((t, i) => {
7934
+ let tv = this.manager.tooltipViews[i];
7935
+ return tv.getCoords ? tv.getCoords(t.pos) : this.view.coordsAtPos(t.pos);
7936
+ }),
7937
+ size: this.manager.tooltipViews.map(({ dom }) => dom.getBoundingClientRect()),
7938
+ space: this.view.state.facet(tooltipConfig).tooltipSpace(this.view),
7939
+ };
7940
+ }
7941
+ writeMeasure(measured) {
7942
+ let { editor, space } = measured;
7943
+ let others = [];
7944
+ for (let i = 0; i < this.manager.tooltips.length; i++) {
7945
+ let tooltip = this.manager.tooltips[i], tView = this.manager.tooltipViews[i], { dom } = tView;
7946
+ let pos = measured.pos[i], size = measured.size[i];
7947
+ // Hide tooltips that are outside of the editor.
7948
+ if (!pos || pos.bottom <= Math.max(editor.top, space.top) ||
7949
+ pos.top >= Math.min(editor.bottom, space.bottom) ||
7950
+ pos.right < Math.max(editor.left, space.left) - .1 ||
7951
+ pos.left > Math.min(editor.right, space.right) + .1) {
7952
+ dom.style.top = Outside;
7953
+ continue;
7954
+ }
7955
+ let arrow = tooltip.arrow ? tView.dom.querySelector(".cm-tooltip-arrow") : null;
7956
+ let arrowHeight = arrow ? 7 /* Size */ : 0;
7957
+ let width = size.right - size.left, height = size.bottom - size.top;
7958
+ let offset = tView.offset || noOffset, ltr = this.view.textDirection == Direction.LTR;
7959
+ let left = size.width > space.right - space.left ? (ltr ? space.left : space.right - size.width)
7960
+ : ltr ? Math.min(pos.left - (arrow ? 14 /* Offset */ : 0) + offset.x, space.right - width)
7961
+ : Math.max(space.left, pos.left - width + (arrow ? 14 /* Offset */ : 0) - offset.x);
7962
+ let above = !!tooltip.above;
7963
+ if (!tooltip.strictSide && (above
7964
+ ? pos.top - (size.bottom - size.top) - offset.y < space.top
7965
+ : pos.bottom + (size.bottom - size.top) + offset.y > space.bottom) &&
7966
+ above == (space.bottom - pos.bottom > pos.top - space.top))
7967
+ above = !above;
7968
+ let top = above ? pos.top - height - arrowHeight - offset.y : pos.bottom + arrowHeight + offset.y;
7969
+ let right = left + width;
7970
+ if (tView.overlap !== true)
7971
+ for (let r of others)
7972
+ if (r.left < right && r.right > left && r.top < top + height && r.bottom > top)
7973
+ top = above ? r.top - height - 2 - arrowHeight : r.bottom + arrowHeight + 2;
7974
+ if (this.position == "absolute") {
7975
+ dom.style.top = (top - measured.parent.top) + "px";
7976
+ dom.style.left = (left - measured.parent.left) + "px";
7977
+ }
7978
+ else {
7979
+ dom.style.top = top + "px";
7980
+ dom.style.left = left + "px";
7981
+ }
7982
+ if (arrow)
7983
+ arrow.style.left = `${pos.left + (ltr ? offset.x : -offset.x) - (left + 14 /* Offset */ - 7 /* Size */)}px`;
7984
+ if (tView.overlap !== true)
7985
+ others.push({ left, top, right, bottom: top + height });
7986
+ dom.classList.toggle("cm-tooltip-above", above);
7987
+ dom.classList.toggle("cm-tooltip-below", !above);
7988
+ if (tView.positioned)
7989
+ tView.positioned();
7990
+ }
7991
+ }
7992
+ maybeMeasure() {
7993
+ if (this.manager.tooltips.length) {
7994
+ if (this.view.inView)
7995
+ this.view.requestMeasure(this.measureReq);
7996
+ if (this.inView != this.view.inView) {
7997
+ this.inView = this.view.inView;
7998
+ if (!this.inView)
7999
+ for (let tv of this.manager.tooltipViews)
8000
+ tv.dom.style.top = Outside;
8001
+ }
8002
+ }
8003
+ }
8004
+ }, {
8005
+ eventHandlers: {
8006
+ scroll() { this.maybeMeasure(); }
8007
+ }
8008
+ });
8009
+ const baseTheme = /*@__PURE__*/EditorView.baseTheme({
8010
+ ".cm-tooltip": {
8011
+ zIndex: 100
8012
+ },
8013
+ "&light .cm-tooltip": {
8014
+ border: "1px solid #bbb",
8015
+ backgroundColor: "#f5f5f5"
8016
+ },
8017
+ "&light .cm-tooltip-section:not(:first-child)": {
8018
+ borderTop: "1px solid #bbb",
8019
+ },
8020
+ "&dark .cm-tooltip": {
8021
+ backgroundColor: "#333338",
8022
+ color: "white"
8023
+ },
8024
+ ".cm-tooltip-arrow": {
8025
+ height: `${7 /* Size */}px`,
8026
+ width: `${7 /* Size */ * 2}px`,
8027
+ position: "absolute",
8028
+ zIndex: -1,
8029
+ overflow: "hidden",
8030
+ "&:before, &:after": {
8031
+ content: "''",
8032
+ position: "absolute",
8033
+ width: 0,
8034
+ height: 0,
8035
+ borderLeft: `${7 /* Size */}px solid transparent`,
8036
+ borderRight: `${7 /* Size */}px solid transparent`,
8037
+ },
8038
+ ".cm-tooltip-above &": {
8039
+ bottom: `-${7 /* Size */}px`,
8040
+ "&:before": {
8041
+ borderTop: `${7 /* Size */}px solid #bbb`,
8042
+ },
8043
+ "&:after": {
8044
+ borderTop: `${7 /* Size */}px solid #f5f5f5`,
8045
+ bottom: "1px"
8046
+ }
8047
+ },
8048
+ ".cm-tooltip-below &": {
8049
+ top: `-${7 /* Size */}px`,
8050
+ "&:before": {
8051
+ borderBottom: `${7 /* Size */}px solid #bbb`,
8052
+ },
8053
+ "&:after": {
8054
+ borderBottom: `${7 /* Size */}px solid #f5f5f5`,
8055
+ top: "1px"
8056
+ }
8057
+ },
8058
+ },
8059
+ "&dark .cm-tooltip .cm-tooltip-arrow": {
8060
+ "&:before": {
8061
+ borderTopColor: "#333338",
8062
+ borderBottomColor: "#333338"
8063
+ },
8064
+ "&:after": {
8065
+ borderTopColor: "transparent",
8066
+ borderBottomColor: "transparent"
8067
+ }
8068
+ }
8069
+ });
8070
+ const noOffset = { x: 0, y: 0 };
8071
+ /**
8072
+ Facet to which an extension can add a value to show a tooltip.
8073
+ */
8074
+ const showTooltip = /*@__PURE__*/Facet.define({
8075
+ enables: [tooltipPlugin, baseTheme]
8076
+ });
8077
+ const showHoverTooltip = /*@__PURE__*/Facet.define();
8078
+ class HoverTooltipHost {
8079
+ constructor(view) {
8080
+ this.view = view;
8081
+ this.mounted = false;
8082
+ this.dom = document.createElement("div");
8083
+ this.dom.classList.add("cm-tooltip-hover");
8084
+ this.manager = new TooltipViewManager(view, showHoverTooltip, t => this.createHostedView(t));
8085
+ }
8086
+ // Needs to be static so that host tooltip instances always match
8087
+ static create(view) {
8088
+ return new HoverTooltipHost(view);
8089
+ }
8090
+ createHostedView(tooltip) {
8091
+ let hostedView = tooltip.create(this.view);
8092
+ hostedView.dom.classList.add("cm-tooltip-section");
8093
+ this.dom.appendChild(hostedView.dom);
8094
+ if (this.mounted && hostedView.mount)
8095
+ hostedView.mount(this.view);
8096
+ return hostedView;
8097
+ }
8098
+ mount(view) {
8099
+ for (let hostedView of this.manager.tooltipViews) {
8100
+ if (hostedView.mount)
8101
+ hostedView.mount(view);
8102
+ }
8103
+ this.mounted = true;
8104
+ }
8105
+ positioned() {
8106
+ for (let hostedView of this.manager.tooltipViews) {
8107
+ if (hostedView.positioned)
8108
+ hostedView.positioned();
8109
+ }
8110
+ }
8111
+ update(update) {
8112
+ this.manager.update(update);
8113
+ }
8114
+ }
8115
+ const showHoverTooltipHost = /*@__PURE__*/showTooltip.compute([showHoverTooltip], state => {
8116
+ let tooltips = state.facet(showHoverTooltip).filter(t => t);
8117
+ if (tooltips.length === 0)
8118
+ return null;
8119
+ return {
8120
+ pos: Math.min(...tooltips.map(t => t.pos)),
8121
+ end: Math.max(...tooltips.filter(t => t.end != null).map(t => t.end)),
8122
+ create: HoverTooltipHost.create,
8123
+ above: tooltips[0].above,
8124
+ arrow: tooltips.some(t => t.arrow),
8125
+ };
8126
+ });
8127
+ class HoverPlugin {
8128
+ constructor(view, source, field, setHover, hoverTime) {
8129
+ this.view = view;
8130
+ this.source = source;
8131
+ this.field = field;
8132
+ this.setHover = setHover;
8133
+ this.hoverTime = hoverTime;
8134
+ this.hoverTimeout = -1;
8135
+ this.restartTimeout = -1;
8136
+ this.pending = null;
8137
+ this.lastMove = { x: 0, y: 0, target: view.dom, time: 0 };
8138
+ this.checkHover = this.checkHover.bind(this);
8139
+ view.dom.addEventListener("mouseleave", this.mouseleave = this.mouseleave.bind(this));
8140
+ view.dom.addEventListener("mousemove", this.mousemove = this.mousemove.bind(this));
8141
+ }
8142
+ update() {
8143
+ if (this.pending) {
8144
+ this.pending = null;
8145
+ clearTimeout(this.restartTimeout);
8146
+ this.restartTimeout = setTimeout(() => this.startHover(), 20);
8147
+ }
8148
+ }
8149
+ get active() {
8150
+ return this.view.state.field(this.field);
8151
+ }
8152
+ checkHover() {
8153
+ this.hoverTimeout = -1;
8154
+ if (this.active)
8155
+ return;
8156
+ let hovered = Date.now() - this.lastMove.time;
8157
+ if (hovered < this.hoverTime)
8158
+ this.hoverTimeout = setTimeout(this.checkHover, this.hoverTime - hovered);
8159
+ else
8160
+ this.startHover();
8161
+ }
8162
+ startHover() {
8163
+ var _a;
8164
+ clearTimeout(this.restartTimeout);
8165
+ let { lastMove } = this;
8166
+ let pos = this.view.contentDOM.contains(lastMove.target) ? this.view.posAtCoords(lastMove) : null;
8167
+ if (pos == null)
8168
+ return;
8169
+ let posCoords = this.view.coordsAtPos(pos);
8170
+ if (posCoords == null || lastMove.y < posCoords.top || lastMove.y > posCoords.bottom ||
8171
+ lastMove.x < posCoords.left - this.view.defaultCharacterWidth ||
8172
+ lastMove.x > posCoords.right + this.view.defaultCharacterWidth)
8173
+ return;
8174
+ let bidi = this.view.bidiSpans(this.view.state.doc.lineAt(pos)).find(s => s.from <= pos && s.to >= pos);
8175
+ let rtl = bidi && bidi.dir == Direction.RTL ? -1 : 1;
8176
+ let open = this.source(this.view, pos, (lastMove.x < posCoords.left ? -rtl : rtl));
8177
+ if ((_a = open) === null || _a === void 0 ? void 0 : _a.then) {
8178
+ let pending = this.pending = { pos };
8179
+ open.then(result => {
8180
+ if (this.pending == pending) {
8181
+ this.pending = null;
8182
+ if (result)
8183
+ this.view.dispatch({ effects: this.setHover.of(result) });
8184
+ }
8185
+ }, e => logException(this.view.state, e, "hover tooltip"));
8186
+ }
8187
+ else if (open) {
8188
+ this.view.dispatch({ effects: this.setHover.of(open) });
8189
+ }
8190
+ }
8191
+ mousemove(event) {
8192
+ var _a;
8193
+ this.lastMove = { x: event.clientX, y: event.clientY, target: event.target, time: Date.now() };
8194
+ if (this.hoverTimeout < 0)
8195
+ this.hoverTimeout = setTimeout(this.checkHover, this.hoverTime);
8196
+ let tooltip = this.active;
8197
+ if (tooltip && !isInTooltip(this.lastMove.target) || this.pending) {
8198
+ let { pos } = tooltip || this.pending, end = (_a = tooltip === null || tooltip === void 0 ? void 0 : tooltip.end) !== null && _a !== void 0 ? _a : pos;
8199
+ if ((pos == end ? this.view.posAtCoords(this.lastMove) != pos
8200
+ : !isOverRange(this.view, pos, end, event.clientX, event.clientY, 6 /* MaxDist */))) {
8201
+ this.view.dispatch({ effects: this.setHover.of(null) });
8202
+ this.pending = null;
8203
+ }
8204
+ }
8205
+ }
8206
+ mouseleave() {
8207
+ clearTimeout(this.hoverTimeout);
8208
+ this.hoverTimeout = -1;
8209
+ if (this.active)
8210
+ this.view.dispatch({ effects: this.setHover.of(null) });
8211
+ }
8212
+ destroy() {
8213
+ clearTimeout(this.hoverTimeout);
8214
+ this.view.dom.removeEventListener("mouseleave", this.mouseleave);
8215
+ this.view.dom.removeEventListener("mousemove", this.mousemove);
8216
+ }
8217
+ }
8218
+ function isInTooltip(elt) {
8219
+ for (let cur = elt; cur; cur = cur.parentNode)
8220
+ if (cur.nodeType == 1 && cur.classList.contains("cm-tooltip"))
8221
+ return true;
8222
+ return false;
8223
+ }
8224
+ function isOverRange(view, from, to, x, y, margin) {
8225
+ let range = document.createRange();
8226
+ let fromDOM = view.domAtPos(from), toDOM = view.domAtPos(to);
8227
+ range.setEnd(toDOM.node, toDOM.offset);
8228
+ range.setStart(fromDOM.node, fromDOM.offset);
8229
+ let rects = range.getClientRects();
8230
+ range.detach();
8231
+ for (let i = 0; i < rects.length; i++) {
8232
+ let rect = rects[i];
8233
+ let dist = Math.max(rect.top - y, y - rect.bottom, rect.left - x, x - rect.right);
8234
+ if (dist <= margin)
8235
+ return true;
8236
+ }
8237
+ return false;
8238
+ }
8239
+ /**
8240
+ Set up a hover tooltip, which shows up when the pointer hovers
8241
+ over ranges of text. The callback is called when the mouse hovers
8242
+ over the document text. It should, if there is a tooltip
8243
+ associated with position `pos`, return the tooltip description
8244
+ (either directly or in a promise). The `side` argument indicates
8245
+ on which side of the position the pointer is—it will be -1 if the
8246
+ pointer is before the position, 1 if after the position.
8247
+
8248
+ Note that all hover tooltips are hosted within a single tooltip
8249
+ container element. This allows multiple tooltips over the same
8250
+ range to be "merged" together without overlapping.
8251
+ */
8252
+ function hoverTooltip(source, options = {}) {
8253
+ let setHover = StateEffect.define();
8254
+ let hoverState = StateField.define({
8255
+ create() { return null; },
8256
+ update(value, tr) {
8257
+ if (value && (options.hideOnChange && (tr.docChanged || tr.selection) ||
8258
+ options.hideOn && options.hideOn(tr, value)))
8259
+ return null;
8260
+ if (value && tr.docChanged) {
8261
+ let newPos = tr.changes.mapPos(value.pos, -1, MapMode.TrackDel);
8262
+ if (newPos == null)
8263
+ return null;
8264
+ let copy = Object.assign(Object.create(null), value);
8265
+ copy.pos = newPos;
8266
+ if (value.end != null)
8267
+ copy.end = tr.changes.mapPos(value.end);
8268
+ value = copy;
8269
+ }
8270
+ for (let effect of tr.effects) {
8271
+ if (effect.is(setHover))
8272
+ value = effect.value;
8273
+ if (effect.is(closeHoverTooltipEffect))
8274
+ value = null;
8275
+ }
8276
+ return value;
8277
+ },
8278
+ provide: f => showHoverTooltip.from(f)
8279
+ });
8280
+ return [
8281
+ hoverState,
8282
+ ViewPlugin.define(view => new HoverPlugin(view, source, hoverState, setHover, options.hoverTime || 300 /* Time */)),
8283
+ showHoverTooltipHost
8284
+ ];
8285
+ }
8286
+ /**
8287
+ Get the active tooltip view for a given tooltip, if available.
8288
+ */
8289
+ function getTooltip(view, tooltip) {
8290
+ let plugin = view.plugin(tooltipPlugin);
8291
+ if (!plugin)
8292
+ return null;
8293
+ let found = plugin.manager.tooltips.indexOf(tooltip);
8294
+ return found < 0 ? null : plugin.manager.tooltipViews[found];
8295
+ }
8296
+ /**
8297
+ Returns true if any hover tooltips are currently active.
8298
+ */
8299
+ function hasHoverTooltips(state) {
8300
+ return state.facet(showHoverTooltip).some(x => x);
8301
+ }
8302
+ const closeHoverTooltipEffect = /*@__PURE__*/StateEffect.define();
8303
+ /**
8304
+ Transaction effect that closes all hover tooltips.
8305
+ */
8306
+ const closeHoverTooltips = /*@__PURE__*/closeHoverTooltipEffect.of(null);
8307
+ /**
8308
+ Tell the tooltip extension to recompute the position of the active
8309
+ tooltips. This can be useful when something happens (such as a
8310
+ re-positioning or CSS change affecting the editor) that could
8311
+ invalidate the existing tooltip positions.
8312
+ */
8313
+ function repositionTooltips(view) {
8314
+ var _a;
8315
+ (_a = view.plugin(tooltipPlugin)) === null || _a === void 0 ? void 0 : _a.maybeMeasure();
8316
+ }
8317
+
8318
+ const panelConfig = /*@__PURE__*/Facet.define({
8319
+ combine(configs) {
8320
+ let topContainer, bottomContainer;
8321
+ for (let c of configs) {
8322
+ topContainer = topContainer || c.topContainer;
8323
+ bottomContainer = bottomContainer || c.bottomContainer;
8324
+ }
8325
+ return { topContainer, bottomContainer };
8326
+ }
8327
+ });
8328
+ /**
8329
+ Configures the panel-managing extension.
8330
+ */
8331
+ function panels(config) {
8332
+ return config ? [panelConfig.of(config)] : [];
8333
+ }
8334
+ /**
8335
+ Get the active panel created by the given constructor, if any.
8336
+ This can be useful when you need access to your panels' DOM
8337
+ structure.
8338
+ */
8339
+ function getPanel(view, panel) {
8340
+ let plugin = view.plugin(panelPlugin);
8341
+ let index = plugin ? plugin.specs.indexOf(panel) : -1;
8342
+ return index > -1 ? plugin.panels[index] : null;
8343
+ }
8344
+ const panelPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
8345
+ constructor(view) {
8346
+ this.input = view.state.facet(showPanel);
8347
+ this.specs = this.input.filter(s => s);
8348
+ this.panels = this.specs.map(spec => spec(view));
8349
+ let conf = view.state.facet(panelConfig);
8350
+ this.top = new PanelGroup(view, true, conf.topContainer);
8351
+ this.bottom = new PanelGroup(view, false, conf.bottomContainer);
8352
+ this.top.sync(this.panels.filter(p => p.top));
8353
+ this.bottom.sync(this.panels.filter(p => !p.top));
8354
+ for (let p of this.panels) {
8355
+ p.dom.classList.add("cm-panel");
8356
+ if (p.mount)
8357
+ p.mount();
8358
+ }
8359
+ }
8360
+ update(update) {
8361
+ let conf = update.state.facet(panelConfig);
8362
+ if (this.top.container != conf.topContainer) {
8363
+ this.top.sync([]);
8364
+ this.top = new PanelGroup(update.view, true, conf.topContainer);
8365
+ }
8366
+ if (this.bottom.container != conf.bottomContainer) {
8367
+ this.bottom.sync([]);
8368
+ this.bottom = new PanelGroup(update.view, false, conf.bottomContainer);
8369
+ }
8370
+ this.top.syncClasses();
8371
+ this.bottom.syncClasses();
8372
+ let input = update.state.facet(showPanel);
8373
+ if (input != this.input) {
8374
+ let specs = input.filter(x => x);
8375
+ let panels = [], top = [], bottom = [], mount = [];
8376
+ for (let spec of specs) {
8377
+ let known = this.specs.indexOf(spec), panel;
8378
+ if (known < 0) {
8379
+ panel = spec(update.view);
8380
+ mount.push(panel);
8381
+ }
8382
+ else {
8383
+ panel = this.panels[known];
8384
+ if (panel.update)
8385
+ panel.update(update);
8386
+ }
8387
+ panels.push(panel);
8388
+ (panel.top ? top : bottom).push(panel);
8389
+ }
8390
+ this.specs = specs;
8391
+ this.panels = panels;
8392
+ this.top.sync(top);
8393
+ this.bottom.sync(bottom);
8394
+ for (let p of mount) {
8395
+ p.dom.classList.add("cm-panel");
8396
+ if (p.mount)
8397
+ p.mount();
8398
+ }
8399
+ }
8400
+ else {
8401
+ for (let p of this.panels)
8402
+ if (p.update)
8403
+ p.update(update);
8404
+ }
8405
+ }
8406
+ destroy() {
8407
+ this.top.sync([]);
8408
+ this.bottom.sync([]);
8409
+ }
8410
+ }, {
8411
+ provide: plugin => EditorView.scrollMargins.of(view => {
8412
+ let value = view.plugin(plugin);
8413
+ return value && { top: value.top.scrollMargin(), bottom: value.bottom.scrollMargin() };
8414
+ })
8415
+ });
8416
+ class PanelGroup {
8417
+ constructor(view, top, container) {
8418
+ this.view = view;
8419
+ this.top = top;
8420
+ this.container = container;
8421
+ this.dom = undefined;
8422
+ this.classes = "";
8423
+ this.panels = [];
8424
+ this.syncClasses();
8425
+ }
8426
+ sync(panels) {
8427
+ for (let p of this.panels)
8428
+ if (p.destroy && panels.indexOf(p) < 0)
8429
+ p.destroy();
8430
+ this.panels = panels;
8431
+ this.syncDOM();
8432
+ }
8433
+ syncDOM() {
8434
+ if (this.panels.length == 0) {
8435
+ if (this.dom) {
8436
+ this.dom.remove();
8437
+ this.dom = undefined;
8438
+ }
8439
+ return;
8440
+ }
8441
+ if (!this.dom) {
8442
+ this.dom = document.createElement("div");
8443
+ this.dom.className = this.top ? "cm-panels cm-panels-top" : "cm-panels cm-panels-bottom";
8444
+ this.dom.style[this.top ? "top" : "bottom"] = "0";
8445
+ let parent = this.container || this.view.dom;
8446
+ parent.insertBefore(this.dom, this.top ? parent.firstChild : null);
8447
+ }
8448
+ let curDOM = this.dom.firstChild;
8449
+ for (let panel of this.panels) {
8450
+ if (panel.dom.parentNode == this.dom) {
8451
+ while (curDOM != panel.dom)
8452
+ curDOM = rm(curDOM);
8453
+ curDOM = curDOM.nextSibling;
8454
+ }
8455
+ else {
8456
+ this.dom.insertBefore(panel.dom, curDOM);
8457
+ }
8458
+ }
8459
+ while (curDOM)
8460
+ curDOM = rm(curDOM);
8461
+ }
8462
+ scrollMargin() {
8463
+ return !this.dom || this.container ? 0
8464
+ : Math.max(0, this.top ?
8465
+ this.dom.getBoundingClientRect().bottom - Math.max(0, this.view.scrollDOM.getBoundingClientRect().top) :
8466
+ Math.min(innerHeight, this.view.scrollDOM.getBoundingClientRect().bottom) - this.dom.getBoundingClientRect().top);
8467
+ }
8468
+ syncClasses() {
8469
+ if (!this.container || this.classes == this.view.themeClasses)
8470
+ return;
8471
+ for (let cls of this.classes.split(" "))
8472
+ if (cls)
8473
+ this.container.classList.remove(cls);
8474
+ for (let cls of (this.classes = this.view.themeClasses).split(" "))
8475
+ if (cls)
8476
+ this.container.classList.add(cls);
8477
+ }
8478
+ }
8479
+ function rm(node) {
8480
+ let next = node.nextSibling;
8481
+ node.remove();
8482
+ return next;
8483
+ }
8484
+ /**
8485
+ Opening a panel is done by providing a constructor function for
8486
+ the panel through this facet. (The panel is closed again when its
8487
+ constructor is no longer provided.) Values of `null` are ignored.
8488
+ */
8489
+ const showPanel = /*@__PURE__*/Facet.define({
8490
+ enables: panelPlugin
8491
+ });
8492
+
8493
+ /**
8494
+ A gutter marker represents a bit of information attached to a line
8495
+ in a specific gutter. Your own custom markers have to extend this
8496
+ class.
8497
+ */
8498
+ class GutterMarker extends RangeValue {
8499
+ /**
8500
+ @internal
8501
+ */
8502
+ compare(other) {
8503
+ return this == other || this.constructor == other.constructor && this.eq(other);
8504
+ }
8505
+ /**
8506
+ Compare this marker to another marker of the same type.
8507
+ */
8508
+ eq(other) { return false; }
8509
+ /**
8510
+ Called if the marker has a `toDOM` method and its representation
8511
+ was removed from a gutter.
8512
+ */
8513
+ destroy(dom) { }
8514
+ }
8515
+ GutterMarker.prototype.elementClass = "";
8516
+ GutterMarker.prototype.toDOM = undefined;
8517
+ GutterMarker.prototype.mapMode = MapMode.TrackBefore;
8518
+ GutterMarker.prototype.startSide = GutterMarker.prototype.endSide = -1;
8519
+ GutterMarker.prototype.point = true;
8520
+ /**
8521
+ Facet used to add a class to all gutter elements for a given line.
8522
+ Markers given to this facet should _only_ define an
8523
+ [`elementclass`](https://codemirror.net/6/docs/ref/#view.GutterMarker.elementClass), not a
8524
+ [`toDOM`](https://codemirror.net/6/docs/ref/#view.GutterMarker.toDOM) (or the marker will appear
8525
+ in all gutters for the line).
8526
+ */
8527
+ const gutterLineClass = /*@__PURE__*/Facet.define();
8528
+ const defaults = {
8529
+ class: "",
8530
+ renderEmptyElements: false,
8531
+ elementStyle: "",
8532
+ markers: () => RangeSet.empty,
8533
+ lineMarker: () => null,
8534
+ lineMarkerChange: null,
8535
+ initialSpacer: null,
8536
+ updateSpacer: null,
8537
+ domEventHandlers: {}
8538
+ };
8539
+ const activeGutters = /*@__PURE__*/Facet.define();
8540
+ /**
8541
+ Define an editor gutter. The order in which the gutters appear is
8542
+ determined by their extension priority.
8543
+ */
8544
+ function gutter(config) {
8545
+ return [gutters(), activeGutters.of(Object.assign(Object.assign({}, defaults), config))];
8546
+ }
8547
+ const unfixGutters = /*@__PURE__*/Facet.define({
8548
+ combine: values => values.some(x => x)
8549
+ });
8550
+ /**
8551
+ The gutter-drawing plugin is automatically enabled when you add a
8552
+ gutter, but you can use this function to explicitly configure it.
8553
+
8554
+ Unless `fixed` is explicitly set to `false`, the gutters are
8555
+ fixed, meaning they don't scroll along with the content
8556
+ horizontally (except on Internet Explorer, which doesn't support
8557
+ CSS [`position:
8558
+ sticky`](https://developer.mozilla.org/en-US/docs/Web/CSS/position#sticky)).
8559
+ */
8560
+ function gutters(config) {
8561
+ let result = [
8562
+ gutterView,
8563
+ ];
8564
+ if (config && config.fixed === false)
8565
+ result.push(unfixGutters.of(true));
8566
+ return result;
8567
+ }
8568
+ const gutterView = /*@__PURE__*/ViewPlugin.fromClass(class {
8569
+ constructor(view) {
8570
+ this.view = view;
8571
+ this.prevViewport = view.viewport;
8572
+ this.dom = document.createElement("div");
8573
+ this.dom.className = "cm-gutters";
8574
+ this.dom.setAttribute("aria-hidden", "true");
8575
+ this.dom.style.minHeight = this.view.contentHeight + "px";
8576
+ this.gutters = view.state.facet(activeGutters).map(conf => new SingleGutterView(view, conf));
8577
+ for (let gutter of this.gutters)
8578
+ this.dom.appendChild(gutter.dom);
8579
+ this.fixed = !view.state.facet(unfixGutters);
8580
+ if (this.fixed) {
8581
+ // FIXME IE11 fallback, which doesn't support position: sticky,
8582
+ // by using position: relative + event handlers that realign the
8583
+ // gutter (or just force fixed=false on IE11?)
8584
+ this.dom.style.position = "sticky";
8585
+ }
8586
+ this.syncGutters(false);
8587
+ view.scrollDOM.insertBefore(this.dom, view.contentDOM);
8588
+ }
8589
+ update(update) {
8590
+ if (this.updateGutters(update)) {
8591
+ // Detach during sync when the viewport changed significantly
8592
+ // (such as during scrolling), since for large updates that is
8593
+ // faster.
8594
+ let vpA = this.prevViewport, vpB = update.view.viewport;
8595
+ let vpOverlap = Math.min(vpA.to, vpB.to) - Math.max(vpA.from, vpB.from);
8596
+ this.syncGutters(vpOverlap < (vpB.to - vpB.from) * 0.8);
8597
+ }
8598
+ if (update.geometryChanged)
8599
+ this.dom.style.minHeight = this.view.contentHeight + "px";
8600
+ if (this.view.state.facet(unfixGutters) != !this.fixed) {
8601
+ this.fixed = !this.fixed;
8602
+ this.dom.style.position = this.fixed ? "sticky" : "";
8603
+ }
8604
+ this.prevViewport = update.view.viewport;
8605
+ }
8606
+ syncGutters(detach) {
8607
+ let after = this.dom.nextSibling;
8608
+ if (detach)
8609
+ this.dom.remove();
8610
+ let lineClasses = RangeSet.iter(this.view.state.facet(gutterLineClass), this.view.viewport.from);
8611
+ let classSet = [];
8612
+ let contexts = this.gutters.map(gutter => new UpdateContext(gutter, this.view.viewport, -this.view.documentPadding.top));
8613
+ for (let line of this.view.viewportLineBlocks) {
8614
+ let text;
8615
+ if (Array.isArray(line.type)) {
8616
+ for (let b of line.type)
8617
+ if (b.type == BlockType.Text) {
8618
+ text = b;
8619
+ break;
8620
+ }
8621
+ }
8622
+ else {
8623
+ text = line.type == BlockType.Text ? line : undefined;
8624
+ }
8625
+ if (!text)
8626
+ continue;
8627
+ if (classSet.length)
8628
+ classSet = [];
8629
+ advanceCursor(lineClasses, classSet, line.from);
8630
+ for (let cx of contexts)
8631
+ cx.line(this.view, text, classSet);
8632
+ }
8633
+ for (let cx of contexts)
8634
+ cx.finish();
8635
+ if (detach)
8636
+ this.view.scrollDOM.insertBefore(this.dom, after);
8637
+ }
8638
+ updateGutters(update) {
8639
+ let prev = update.startState.facet(activeGutters), cur = update.state.facet(activeGutters);
8640
+ let change = update.docChanged || update.heightChanged || update.viewportChanged ||
8641
+ !RangeSet.eq(update.startState.facet(gutterLineClass), update.state.facet(gutterLineClass), update.view.viewport.from, update.view.viewport.to);
8642
+ if (prev == cur) {
8643
+ for (let gutter of this.gutters)
8644
+ if (gutter.update(update))
8645
+ change = true;
8646
+ }
8647
+ else {
8648
+ change = true;
8649
+ let gutters = [];
8650
+ for (let conf of cur) {
8651
+ let known = prev.indexOf(conf);
8652
+ if (known < 0) {
8653
+ gutters.push(new SingleGutterView(this.view, conf));
8654
+ }
8655
+ else {
8656
+ this.gutters[known].update(update);
8657
+ gutters.push(this.gutters[known]);
8658
+ }
8659
+ }
8660
+ for (let g of this.gutters) {
8661
+ g.dom.remove();
8662
+ if (gutters.indexOf(g) < 0)
8663
+ g.destroy();
8664
+ }
8665
+ for (let g of gutters)
8666
+ this.dom.appendChild(g.dom);
8667
+ this.gutters = gutters;
8668
+ }
8669
+ return change;
8670
+ }
8671
+ destroy() {
8672
+ for (let view of this.gutters)
8673
+ view.destroy();
8674
+ this.dom.remove();
8675
+ }
8676
+ }, {
8677
+ provide: plugin => EditorView.scrollMargins.of(view => {
8678
+ let value = view.plugin(plugin);
8679
+ if (!value || value.gutters.length == 0 || !value.fixed)
8680
+ return null;
8681
+ return view.textDirection == Direction.LTR ? { left: value.dom.offsetWidth } : { right: value.dom.offsetWidth };
8682
+ })
8683
+ });
8684
+ function asArray(val) { return (Array.isArray(val) ? val : [val]); }
8685
+ function advanceCursor(cursor, collect, pos) {
8686
+ while (cursor.value && cursor.from <= pos) {
8687
+ if (cursor.from == pos)
8688
+ collect.push(cursor.value);
8689
+ cursor.next();
8690
+ }
8691
+ }
8692
+ class UpdateContext {
8693
+ constructor(gutter, viewport, height) {
8694
+ this.gutter = gutter;
8695
+ this.height = height;
8696
+ this.localMarkers = [];
8697
+ this.i = 0;
8698
+ this.cursor = RangeSet.iter(gutter.markers, viewport.from);
8699
+ }
8700
+ line(view, line, extraMarkers) {
8701
+ if (this.localMarkers.length)
8702
+ this.localMarkers = [];
8703
+ advanceCursor(this.cursor, this.localMarkers, line.from);
8704
+ let localMarkers = extraMarkers.length ? this.localMarkers.concat(extraMarkers) : this.localMarkers;
8705
+ let forLine = this.gutter.config.lineMarker(view, line, localMarkers);
8706
+ if (forLine)
8707
+ localMarkers.unshift(forLine);
8708
+ let gutter = this.gutter;
8709
+ if (localMarkers.length == 0 && !gutter.config.renderEmptyElements)
8710
+ return;
8711
+ let above = line.top - this.height;
8712
+ if (this.i == gutter.elements.length) {
8713
+ let newElt = new GutterElement(view, line.height, above, localMarkers);
8714
+ gutter.elements.push(newElt);
8715
+ gutter.dom.appendChild(newElt.dom);
8716
+ }
8717
+ else {
8718
+ gutter.elements[this.i].update(view, line.height, above, localMarkers);
8719
+ }
8720
+ this.height = line.bottom;
8721
+ this.i++;
8722
+ }
8723
+ finish() {
8724
+ let gutter = this.gutter;
8725
+ while (gutter.elements.length > this.i) {
8726
+ let last = gutter.elements.pop();
8727
+ gutter.dom.removeChild(last.dom);
8728
+ last.destroy();
8729
+ }
8730
+ }
8731
+ }
8732
+ class SingleGutterView {
8733
+ constructor(view, config) {
8734
+ this.view = view;
8735
+ this.config = config;
8736
+ this.elements = [];
8737
+ this.spacer = null;
8738
+ this.dom = document.createElement("div");
8739
+ this.dom.className = "cm-gutter" + (this.config.class ? " " + this.config.class : "");
8740
+ for (let prop in config.domEventHandlers) {
8741
+ this.dom.addEventListener(prop, (event) => {
8742
+ let line = view.lineBlockAtHeight(event.clientY - view.documentTop);
8743
+ if (config.domEventHandlers[prop](view, line, event))
8744
+ event.preventDefault();
8745
+ });
8746
+ }
8747
+ this.markers = asArray(config.markers(view));
8748
+ if (config.initialSpacer) {
8749
+ this.spacer = new GutterElement(view, 0, 0, [config.initialSpacer(view)]);
8750
+ this.dom.appendChild(this.spacer.dom);
8751
+ this.spacer.dom.style.cssText += "visibility: hidden; pointer-events: none";
8752
+ }
8753
+ }
8754
+ update(update) {
8755
+ let prevMarkers = this.markers;
8756
+ this.markers = asArray(this.config.markers(update.view));
8757
+ if (this.spacer && this.config.updateSpacer) {
8758
+ let updated = this.config.updateSpacer(this.spacer.markers[0], update);
8759
+ if (updated != this.spacer.markers[0])
8760
+ this.spacer.update(update.view, 0, 0, [updated]);
8761
+ }
8762
+ let vp = update.view.viewport;
8763
+ return !RangeSet.eq(this.markers, prevMarkers, vp.from, vp.to) ||
8764
+ (this.config.lineMarkerChange ? this.config.lineMarkerChange(update) : false);
8765
+ }
8766
+ destroy() {
8767
+ for (let elt of this.elements)
8768
+ elt.destroy();
8769
+ }
8770
+ }
8771
+ class GutterElement {
8772
+ constructor(view, height, above, markers) {
8773
+ this.height = -1;
8774
+ this.above = 0;
8775
+ this.markers = [];
8776
+ this.dom = document.createElement("div");
8777
+ this.update(view, height, above, markers);
8778
+ }
8779
+ update(view, height, above, markers) {
8780
+ if (this.height != height)
8781
+ this.dom.style.height = (this.height = height) + "px";
8782
+ if (this.above != above)
8783
+ this.dom.style.marginTop = (this.above = above) ? above + "px" : "";
8784
+ if (!sameMarkers(this.markers, markers))
8785
+ this.setMarkers(view, markers);
8786
+ }
8787
+ setMarkers(view, markers) {
8788
+ let cls = "cm-gutterElement", domPos = this.dom.firstChild;
8789
+ for (let iNew = 0, iOld = 0;;) {
8790
+ let skipTo = iOld, marker = iNew < markers.length ? markers[iNew++] : null, matched = false;
8791
+ if (marker) {
8792
+ let c = marker.elementClass;
8793
+ if (c)
8794
+ cls += " " + c;
8795
+ for (let i = iOld; i < this.markers.length; i++)
8796
+ if (this.markers[i].compare(marker)) {
8797
+ skipTo = i;
8798
+ matched = true;
8799
+ break;
8800
+ }
8801
+ }
8802
+ else {
8803
+ skipTo = this.markers.length;
8804
+ }
8805
+ while (iOld < skipTo) {
8806
+ let next = this.markers[iOld++];
8807
+ if (next.toDOM) {
8808
+ next.destroy(domPos);
8809
+ let after = domPos.nextSibling;
8810
+ domPos.remove();
8811
+ domPos = after;
8812
+ }
8813
+ }
8814
+ if (!marker)
8815
+ break;
8816
+ if (marker.toDOM) {
8817
+ if (matched)
8818
+ domPos = domPos.nextSibling;
8819
+ else
8820
+ this.dom.insertBefore(marker.toDOM(view), domPos);
8821
+ }
8822
+ if (matched)
8823
+ iOld++;
8824
+ }
8825
+ this.dom.className = cls;
8826
+ this.markers = markers;
8827
+ }
8828
+ destroy() {
8829
+ this.setMarkers(null, []); // First argument not used unless creating markers
8830
+ }
8831
+ }
8832
+ function sameMarkers(a, b) {
8833
+ if (a.length != b.length)
8834
+ return false;
8835
+ for (let i = 0; i < a.length; i++)
8836
+ if (!a[i].compare(b[i]))
8837
+ return false;
8838
+ return true;
8839
+ }
8840
+ /**
8841
+ Facet used to provide markers to the line number gutter.
8842
+ */
8843
+ const lineNumberMarkers = /*@__PURE__*/Facet.define();
8844
+ const lineNumberConfig = /*@__PURE__*/Facet.define({
8845
+ combine(values) {
8846
+ return combineConfig(values, { formatNumber: String, domEventHandlers: {} }, {
8847
+ domEventHandlers(a, b) {
8848
+ let result = Object.assign({}, a);
8849
+ for (let event in b) {
8850
+ let exists = result[event], add = b[event];
8851
+ result[event] = exists ? (view, line, event) => exists(view, line, event) || add(view, line, event) : add;
8852
+ }
8853
+ return result;
8854
+ }
8855
+ });
8856
+ }
8857
+ });
8858
+ class NumberMarker extends GutterMarker {
8859
+ constructor(number) {
8860
+ super();
8861
+ this.number = number;
8862
+ }
8863
+ eq(other) { return this.number == other.number; }
8864
+ toDOM() { return document.createTextNode(this.number); }
8865
+ }
8866
+ function formatNumber(view, number) {
8867
+ return view.state.facet(lineNumberConfig).formatNumber(number, view.state);
8868
+ }
8869
+ const lineNumberGutter = /*@__PURE__*/activeGutters.compute([lineNumberConfig], state => ({
8870
+ class: "cm-lineNumbers",
8871
+ renderEmptyElements: false,
8872
+ markers(view) { return view.state.facet(lineNumberMarkers); },
8873
+ lineMarker(view, line, others) {
8874
+ if (others.some(m => m.toDOM))
8875
+ return null;
8876
+ return new NumberMarker(formatNumber(view, view.state.doc.lineAt(line.from).number));
8877
+ },
8878
+ lineMarkerChange: update => update.startState.facet(lineNumberConfig) != update.state.facet(lineNumberConfig),
8879
+ initialSpacer(view) {
8880
+ return new NumberMarker(formatNumber(view, maxLineNumber(view.state.doc.lines)));
8881
+ },
8882
+ updateSpacer(spacer, update) {
8883
+ let max = formatNumber(update.view, maxLineNumber(update.view.state.doc.lines));
8884
+ return max == spacer.number ? spacer : new NumberMarker(max);
8885
+ },
8886
+ domEventHandlers: state.facet(lineNumberConfig).domEventHandlers
8887
+ }));
8888
+ /**
8889
+ Create a line number gutter extension.
8890
+ */
8891
+ function lineNumbers(config = {}) {
8892
+ return [
8893
+ lineNumberConfig.of(config),
8894
+ gutters(),
8895
+ lineNumberGutter
8896
+ ];
8897
+ }
8898
+ function maxLineNumber(lines) {
8899
+ let last = 9;
8900
+ while (last < lines)
8901
+ last = last * 10 + 9;
8902
+ return last;
8903
+ }
8904
+ const activeLineGutterMarker = /*@__PURE__*/new class extends GutterMarker {
8905
+ constructor() {
8906
+ super(...arguments);
8907
+ this.elementClass = "cm-activeLineGutter";
8908
+ }
8909
+ };
8910
+ const activeLineGutterHighlighter = /*@__PURE__*/gutterLineClass.compute(["selection"], state => {
8911
+ let marks = [], last = -1;
8912
+ for (let range of state.selection.ranges)
8913
+ if (range.empty) {
8914
+ let linePos = state.doc.lineAt(range.head).from;
8915
+ if (linePos > last) {
8916
+ last = linePos;
8917
+ marks.push(activeLineGutterMarker.range(linePos));
8918
+ }
8919
+ }
8920
+ return RangeSet.of(marks);
8921
+ });
8922
+ /**
8923
+ Returns an extension that adds a `cm-activeLineGutter` class to
8924
+ all gutter elements on the [active
8925
+ line](https://codemirror.net/6/docs/ref/#view.highlightActiveLine).
8926
+ */
8927
+ function highlightActiveLineGutter() {
8928
+ return activeLineGutterHighlighter;
8929
+ }
8930
+
7715
8931
  /**
7716
8932
  @internal
7717
8933
  */
7718
8934
  const __test = { HeightMap, HeightOracle, MeasuredHeights, QueryType, ChangedRange, computeOrder, moveVisually };
7719
8935
 
7720
- export { BidiSpan, BlockInfo, BlockType, Decoration, Direction, EditorView, MatchDecorator, PluginField, PluginFieldProvider, ViewPlugin, ViewUpdate, WidgetType, __test, drawSelection, dropCursor, highlightActiveLine, highlightSpecialChars, keymap, logException, placeholder, runScopeHandlers, scrollPastEnd };
8936
+ export { BidiSpan, BlockInfo, BlockType, Decoration, Direction, EditorView, GutterMarker, MatchDecorator, ViewPlugin, ViewUpdate, WidgetType, __test, closeHoverTooltips, crosshairCursor, drawSelection, dropCursor, getPanel, getTooltip, gutter, gutterLineClass, gutters, hasHoverTooltips, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, hoverTooltip, keymap, lineNumberMarkers, lineNumbers, logException, panels, placeholder, rectangularSelection, repositionTooltips, runScopeHandlers, scrollPastEnd, showPanel, showTooltip, tooltips };