@codemirror/view 0.19.47 → 0.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +52 -0
- package/dist/index.cjs +1635 -402
- package/dist/index.d.ts +533 -247
- package/dist/index.js +1595 -376
- package/package.json +2 -4
package/dist/index.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import {
|
|
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) {
|
|
@@ -315,32 +312,34 @@ class ContentView {
|
|
|
315
312
|
sync(track) {
|
|
316
313
|
if (this.dirty & 2 /* Node */) {
|
|
317
314
|
let parent = this.dom;
|
|
318
|
-
let
|
|
315
|
+
let prev = null, next;
|
|
319
316
|
for (let child of this.children) {
|
|
320
317
|
if (child.dirty) {
|
|
321
|
-
if (!child.dom &&
|
|
322
|
-
let contentView = ContentView.get(
|
|
318
|
+
if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild)) {
|
|
319
|
+
let contentView = ContentView.get(next);
|
|
323
320
|
if (!contentView || !contentView.parent && contentView.constructor == child.constructor)
|
|
324
|
-
child.reuseDOM(
|
|
321
|
+
child.reuseDOM(next);
|
|
325
322
|
}
|
|
326
323
|
child.sync(track);
|
|
327
324
|
child.dirty = 0 /* Not */;
|
|
328
325
|
}
|
|
329
|
-
|
|
326
|
+
next = prev ? prev.nextSibling : parent.firstChild;
|
|
327
|
+
if (track && !track.written && track.node == parent && next != child.dom)
|
|
330
328
|
track.written = true;
|
|
331
329
|
if (child.dom.parentNode == parent) {
|
|
332
|
-
while (
|
|
333
|
-
|
|
334
|
-
pos = child.dom.nextSibling;
|
|
330
|
+
while (next && next != child.dom)
|
|
331
|
+
next = rm$1(next);
|
|
335
332
|
}
|
|
336
333
|
else {
|
|
337
|
-
parent.insertBefore(child.dom,
|
|
334
|
+
parent.insertBefore(child.dom, next);
|
|
338
335
|
}
|
|
336
|
+
prev = child.dom;
|
|
339
337
|
}
|
|
340
|
-
|
|
338
|
+
next = prev ? prev.nextSibling : parent.firstChild;
|
|
339
|
+
if (next && track && track.node == parent)
|
|
341
340
|
track.written = true;
|
|
342
|
-
while (
|
|
343
|
-
|
|
341
|
+
while (next)
|
|
342
|
+
next = rm$1(next);
|
|
344
343
|
}
|
|
345
344
|
else if (this.dirty & 1 /* Child */) {
|
|
346
345
|
for (let child of this.children)
|
|
@@ -486,7 +485,7 @@ class ContentView {
|
|
|
486
485
|
}
|
|
487
486
|
ContentView.prototype.breakAfter = 0;
|
|
488
487
|
// Remove a DOM node and return its next sibling.
|
|
489
|
-
function rm(dom) {
|
|
488
|
+
function rm$1(dom) {
|
|
490
489
|
let next = dom.nextSibling;
|
|
491
490
|
dom.parentNode.removeChild(dom);
|
|
492
491
|
return next;
|
|
@@ -1074,7 +1073,7 @@ function updateAttrs(dom, prev, attrs) {
|
|
|
1074
1073
|
Widgets added to the content are described by subclasses of this
|
|
1075
1074
|
class. Using a description object like that makes it possible to
|
|
1076
1075
|
delay creating of the DOM structure for a widget until it is
|
|
1077
|
-
needed, and to avoid redrawing widgets even
|
|
1076
|
+
needed, and to avoid redrawing widgets even if the decorations
|
|
1078
1077
|
that define them are recreated.
|
|
1079
1078
|
*/
|
|
1080
1079
|
class WidgetType {
|
|
@@ -1087,7 +1086,7 @@ class WidgetType {
|
|
|
1087
1086
|
returns `false`, which will cause new instances of the widget to
|
|
1088
1087
|
always be redrawn.
|
|
1089
1088
|
*/
|
|
1090
|
-
eq(
|
|
1089
|
+
eq(widget) { return false; }
|
|
1091
1090
|
/**
|
|
1092
1091
|
Update a DOM element created by a widget of the same type (but
|
|
1093
1092
|
different, non-`eq` content) to reflect this widget. May return
|
|
@@ -1095,7 +1094,7 @@ class WidgetType {
|
|
|
1095
1094
|
couldn't (in which case the widget will be redrawn). The default
|
|
1096
1095
|
implementation just returns false.
|
|
1097
1096
|
*/
|
|
1098
|
-
updateDOM(
|
|
1097
|
+
updateDOM(dom) { return false; }
|
|
1099
1098
|
/**
|
|
1100
1099
|
@internal
|
|
1101
1100
|
*/
|
|
@@ -1114,7 +1113,7 @@ class WidgetType {
|
|
|
1114
1113
|
should be ignored by the editor. The default is to ignore all
|
|
1115
1114
|
events.
|
|
1116
1115
|
*/
|
|
1117
|
-
ignoreEvent(
|
|
1116
|
+
ignoreEvent(event) { return true; }
|
|
1118
1117
|
/**
|
|
1119
1118
|
@internal
|
|
1120
1119
|
*/
|
|
@@ -1123,7 +1122,7 @@ class WidgetType {
|
|
|
1123
1122
|
This is called when the an instance of the widget is removed
|
|
1124
1123
|
from the editor view.
|
|
1125
1124
|
*/
|
|
1126
|
-
destroy(
|
|
1125
|
+
destroy(dom) { }
|
|
1127
1126
|
}
|
|
1128
1127
|
/**
|
|
1129
1128
|
The different types of blocks that can occur in an editor view.
|
|
@@ -1149,7 +1148,8 @@ return BlockType})(BlockType || (BlockType = {}));
|
|
|
1149
1148
|
/**
|
|
1150
1149
|
A decoration provides information on how to draw or style a piece
|
|
1151
1150
|
of content. You'll usually use it wrapped in a
|
|
1152
|
-
[`Range`](https://codemirror.net/6/docs/ref/#
|
|
1151
|
+
[`Range`](https://codemirror.net/6/docs/ref/#state.Range), which adds a start and end position.
|
|
1152
|
+
@nonabstract
|
|
1153
1153
|
*/
|
|
1154
1154
|
class Decoration extends RangeValue {
|
|
1155
1155
|
/**
|
|
@@ -1188,18 +1188,17 @@ class Decoration extends RangeValue {
|
|
|
1188
1188
|
Create a mark decoration, which influences the styling of the
|
|
1189
1189
|
content in its range. Nested mark decorations will cause nested
|
|
1190
1190
|
DOM elements to be created. Nesting order is determined by
|
|
1191
|
-
precedence of the [facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations)
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
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.
|
|
1196
1195
|
*/
|
|
1197
1196
|
static mark(spec) {
|
|
1198
1197
|
return new MarkDecoration(spec);
|
|
1199
1198
|
}
|
|
1200
1199
|
/**
|
|
1201
|
-
Create a widget decoration, which
|
|
1202
|
-
position.
|
|
1200
|
+
Create a widget decoration, which displays a DOM element at the
|
|
1201
|
+
given position.
|
|
1203
1202
|
*/
|
|
1204
1203
|
static widget(spec) {
|
|
1205
1204
|
let side = spec.side || 0, block = !!spec.block;
|
|
@@ -1510,7 +1509,7 @@ class BlockWidgetView extends ContentView {
|
|
|
1510
1509
|
}
|
|
1511
1510
|
}
|
|
1512
1511
|
get overrideDOMText() {
|
|
1513
|
-
return this.parent ? this.parent.view.state.doc.slice(this.posAtStart, this.posAtEnd) : Text
|
|
1512
|
+
return this.parent ? this.parent.view.state.doc.slice(this.posAtStart, this.posAtEnd) : Text.empty;
|
|
1514
1513
|
}
|
|
1515
1514
|
domBoundsAround() { return null; }
|
|
1516
1515
|
become(other) {
|
|
@@ -1537,11 +1536,11 @@ class BlockWidgetView extends ContentView {
|
|
|
1537
1536
|
}
|
|
1538
1537
|
|
|
1539
1538
|
class ContentBuilder {
|
|
1540
|
-
constructor(doc, pos, end,
|
|
1539
|
+
constructor(doc, pos, end, disallowBlockEffectsFor) {
|
|
1541
1540
|
this.doc = doc;
|
|
1542
1541
|
this.pos = pos;
|
|
1543
1542
|
this.end = end;
|
|
1544
|
-
this.
|
|
1543
|
+
this.disallowBlockEffectsFor = disallowBlockEffectsFor;
|
|
1545
1544
|
this.content = [];
|
|
1546
1545
|
this.curLine = null;
|
|
1547
1546
|
this.breakAtStart = 0;
|
|
@@ -1626,7 +1625,13 @@ class ContentBuilder {
|
|
|
1626
1625
|
if (this.openStart < 0)
|
|
1627
1626
|
this.openStart = openStart;
|
|
1628
1627
|
}
|
|
1629
|
-
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
|
+
}
|
|
1630
1635
|
let len = to - from;
|
|
1631
1636
|
if (deco instanceof PointDecoration) {
|
|
1632
1637
|
if (deco.block) {
|
|
@@ -1670,17 +1675,8 @@ class ContentBuilder {
|
|
|
1670
1675
|
if (this.openStart < 0)
|
|
1671
1676
|
this.openStart = openStart;
|
|
1672
1677
|
}
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
if (value.block)
|
|
1676
|
-
throw new RangeError("Block decorations may not be specified via plugins");
|
|
1677
|
-
if (to > this.doc.lineAt(this.pos).to)
|
|
1678
|
-
throw new RangeError("Decorations that replace line breaks may not be specified via plugins");
|
|
1679
|
-
}
|
|
1680
|
-
return true;
|
|
1681
|
-
}
|
|
1682
|
-
static build(text, from, to, decorations, pluginDecorationLength) {
|
|
1683
|
-
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);
|
|
1684
1680
|
builder.openEnd = RangeSet.spans(decorations, from, to, builder);
|
|
1685
1681
|
if (builder.openStart < 0)
|
|
1686
1682
|
builder.openStart = builder.openEnd;
|
|
@@ -1710,13 +1706,8 @@ const mouseSelectionStyle = /*@__PURE__*/Facet.define();
|
|
|
1710
1706
|
const exceptionSink = /*@__PURE__*/Facet.define();
|
|
1711
1707
|
const updateListener = /*@__PURE__*/Facet.define();
|
|
1712
1708
|
const inputHandler = /*@__PURE__*/Facet.define();
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
map: (range, changes) => range.map(changes)
|
|
1716
|
-
});
|
|
1717
|
-
// FIXME remove
|
|
1718
|
-
const centerOn = /*@__PURE__*/StateEffect.define({
|
|
1719
|
-
map: (range, changes) => range.map(changes)
|
|
1709
|
+
const perLineTextDirection = /*@__PURE__*/Facet.define({
|
|
1710
|
+
combine: values => values.some(x => x)
|
|
1720
1711
|
});
|
|
1721
1712
|
class ScrollTarget {
|
|
1722
1713
|
constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5) {
|
|
@@ -1755,83 +1746,6 @@ function logException(state, exception, context) {
|
|
|
1755
1746
|
console.error(exception);
|
|
1756
1747
|
}
|
|
1757
1748
|
const editable = /*@__PURE__*/Facet.define({ combine: values => values.length ? values[0] : true });
|
|
1758
|
-
/**
|
|
1759
|
-
Used to [declare](https://codemirror.net/6/docs/ref/#view.PluginSpec.provide) which
|
|
1760
|
-
[fields](https://codemirror.net/6/docs/ref/#view.PluginValue) a [view plugin](https://codemirror.net/6/docs/ref/#view.ViewPlugin)
|
|
1761
|
-
provides.
|
|
1762
|
-
*/
|
|
1763
|
-
class PluginFieldProvider {
|
|
1764
|
-
/**
|
|
1765
|
-
@internal
|
|
1766
|
-
*/
|
|
1767
|
-
constructor(
|
|
1768
|
-
/**
|
|
1769
|
-
@internal
|
|
1770
|
-
*/
|
|
1771
|
-
field,
|
|
1772
|
-
/**
|
|
1773
|
-
@internal
|
|
1774
|
-
*/
|
|
1775
|
-
get) {
|
|
1776
|
-
this.field = field;
|
|
1777
|
-
this.get = get;
|
|
1778
|
-
}
|
|
1779
|
-
}
|
|
1780
|
-
/**
|
|
1781
|
-
Plugin fields are a mechanism for allowing plugins to provide
|
|
1782
|
-
values that can be retrieved through the
|
|
1783
|
-
[`pluginField`](https://codemirror.net/6/docs/ref/#view.EditorView.pluginField) view method.
|
|
1784
|
-
*/
|
|
1785
|
-
class PluginField {
|
|
1786
|
-
/**
|
|
1787
|
-
Create a [provider](https://codemirror.net/6/docs/ref/#view.PluginFieldProvider) for this field,
|
|
1788
|
-
to use with a plugin's [provide](https://codemirror.net/6/docs/ref/#view.PluginSpec.provide)
|
|
1789
|
-
option.
|
|
1790
|
-
*/
|
|
1791
|
-
from(get) {
|
|
1792
|
-
return new PluginFieldProvider(this, get);
|
|
1793
|
-
}
|
|
1794
|
-
/**
|
|
1795
|
-
Define a new plugin field.
|
|
1796
|
-
*/
|
|
1797
|
-
static define() { return new PluginField(); }
|
|
1798
|
-
}
|
|
1799
|
-
/**
|
|
1800
|
-
This field can be used by plugins to provide
|
|
1801
|
-
[decorations](https://codemirror.net/6/docs/ref/#view.Decoration).
|
|
1802
|
-
|
|
1803
|
-
**Note**: For reasons of data flow (plugins are only updated
|
|
1804
|
-
after the viewport is computed), decorations produced by plugins
|
|
1805
|
-
are _not_ taken into account when predicting the vertical layout
|
|
1806
|
-
structure of the editor. They **must not** introduce block
|
|
1807
|
-
widgets (that will raise an error) or replacing decorations that
|
|
1808
|
-
cover line breaks (these will be ignored if they occur). Such
|
|
1809
|
-
decorations, or others that cause a large amount of vertical
|
|
1810
|
-
size shift compared to the undecorated content, should be
|
|
1811
|
-
provided through the state-level [`decorations`
|
|
1812
|
-
facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations) instead.
|
|
1813
|
-
*/
|
|
1814
|
-
PluginField.decorations = /*@__PURE__*/PluginField.define();
|
|
1815
|
-
/**
|
|
1816
|
-
Used to provide ranges that should be treated as atoms as far as
|
|
1817
|
-
cursor motion is concerned. This causes methods like
|
|
1818
|
-
[`moveByChar`](https://codemirror.net/6/docs/ref/#view.EditorView.moveByChar) and
|
|
1819
|
-
[`moveVertically`](https://codemirror.net/6/docs/ref/#view.EditorView.moveVertically) (and the
|
|
1820
|
-
commands built on top of them) to skip across such regions when
|
|
1821
|
-
a selection endpoint would enter them. This does _not_ prevent
|
|
1822
|
-
direct programmatic [selection
|
|
1823
|
-
updates](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection) from moving into such
|
|
1824
|
-
regions.
|
|
1825
|
-
*/
|
|
1826
|
-
PluginField.atomicRanges = /*@__PURE__*/PluginField.define();
|
|
1827
|
-
/**
|
|
1828
|
-
Plugins can provide additional scroll margins (space around the
|
|
1829
|
-
sides of the scrolling element that should be considered
|
|
1830
|
-
invisible) through this field. This can be useful when the
|
|
1831
|
-
plugin introduces elements that cover part of that element (for
|
|
1832
|
-
example a horizontally fixed gutter).
|
|
1833
|
-
*/
|
|
1834
|
-
PluginField.scrollMargins = /*@__PURE__*/PluginField.define();
|
|
1835
1749
|
let nextPluginID = 0;
|
|
1836
1750
|
const viewPlugin = /*@__PURE__*/Facet.define();
|
|
1837
1751
|
/**
|
|
@@ -1852,27 +1766,29 @@ class ViewPlugin {
|
|
|
1852
1766
|
/**
|
|
1853
1767
|
@internal
|
|
1854
1768
|
*/
|
|
1855
|
-
|
|
1769
|
+
domEventHandlers, buildExtensions) {
|
|
1856
1770
|
this.id = id;
|
|
1857
1771
|
this.create = create;
|
|
1858
|
-
this.
|
|
1859
|
-
this.extension =
|
|
1772
|
+
this.domEventHandlers = domEventHandlers;
|
|
1773
|
+
this.extension = buildExtensions(this);
|
|
1860
1774
|
}
|
|
1861
1775
|
/**
|
|
1862
1776
|
Define a plugin from a constructor function that creates the
|
|
1863
1777
|
plugin's value, given an editor view.
|
|
1864
1778
|
*/
|
|
1865
1779
|
static define(create, spec) {
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
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
|
+
});
|
|
1876
1792
|
}
|
|
1877
1793
|
/**
|
|
1878
1794
|
Create a plugin for a class whose constructor takes a single
|
|
@@ -1882,7 +1798,6 @@ class ViewPlugin {
|
|
|
1882
1798
|
return ViewPlugin.define(view => new cls(view), spec);
|
|
1883
1799
|
}
|
|
1884
1800
|
}
|
|
1885
|
-
const domEventHandlers = /*@__PURE__*/PluginField.define();
|
|
1886
1801
|
class PluginInstance {
|
|
1887
1802
|
constructor(spec) {
|
|
1888
1803
|
this.spec = spec;
|
|
@@ -1895,12 +1810,6 @@ class PluginInstance {
|
|
|
1895
1810
|
// initialized on the first update.
|
|
1896
1811
|
this.value = null;
|
|
1897
1812
|
}
|
|
1898
|
-
takeField(type, target) {
|
|
1899
|
-
if (this.spec)
|
|
1900
|
-
for (let { field, get } of this.spec.fields)
|
|
1901
|
-
if (field == type)
|
|
1902
|
-
target.push(get(this.value));
|
|
1903
|
-
}
|
|
1904
1813
|
update(view) {
|
|
1905
1814
|
if (!this.value) {
|
|
1906
1815
|
if (this.spec) {
|
|
@@ -1952,6 +1861,8 @@ const editorAttributes = /*@__PURE__*/Facet.define();
|
|
|
1952
1861
|
const contentAttributes = /*@__PURE__*/Facet.define();
|
|
1953
1862
|
// Provide decorations
|
|
1954
1863
|
const decorations = /*@__PURE__*/Facet.define();
|
|
1864
|
+
const atomicRanges = /*@__PURE__*/Facet.define();
|
|
1865
|
+
const scrollMargins = /*@__PURE__*/Facet.define();
|
|
1955
1866
|
const styleModule = /*@__PURE__*/Facet.define();
|
|
1956
1867
|
class ChangedRange {
|
|
1957
1868
|
constructor(fromA, toA, fromB, toB) {
|
|
@@ -2052,8 +1963,8 @@ class ViewUpdate {
|
|
|
2052
1963
|
return (this.flags & 4 /* Viewport */) > 0;
|
|
2053
1964
|
}
|
|
2054
1965
|
/**
|
|
2055
|
-
Indicates whether the height of
|
|
2056
|
-
in this update.
|
|
1966
|
+
Indicates whether the height of a block element in the editor
|
|
1967
|
+
changed in this update.
|
|
2057
1968
|
*/
|
|
2058
1969
|
get heightChanged() {
|
|
2059
1970
|
return (this.flags & 2 /* Height */) > 0;
|
|
@@ -2512,7 +2423,7 @@ class DocView extends ContentView {
|
|
|
2512
2423
|
this.view = view;
|
|
2513
2424
|
this.compositionDeco = Decoration.none;
|
|
2514
2425
|
this.decorations = [];
|
|
2515
|
-
this.
|
|
2426
|
+
this.dynamicDecorationMap = [];
|
|
2516
2427
|
// Track a minimum width for the editor. When measuring sizes in
|
|
2517
2428
|
// measureVisibleLineHeights, this is updated to point at the width
|
|
2518
2429
|
// of a given element and its extent in the document. When a change
|
|
@@ -2618,7 +2529,7 @@ class DocView extends ContentView {
|
|
|
2618
2529
|
if (!next)
|
|
2619
2530
|
break;
|
|
2620
2531
|
let { fromA, toA, fromB, toB } = next;
|
|
2621
|
-
let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, this.decorations, this.
|
|
2532
|
+
let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, this.decorations, this.dynamicDecorationMap);
|
|
2622
2533
|
let { i: toI, off: toOff } = cursor.findPos(toA, 1);
|
|
2623
2534
|
let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
|
|
2624
2535
|
replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
|
|
@@ -2759,11 +2670,11 @@ class DocView extends ContentView {
|
|
|
2759
2670
|
off = start;
|
|
2760
2671
|
}
|
|
2761
2672
|
}
|
|
2762
|
-
measureVisibleLineHeights() {
|
|
2763
|
-
let result = [], { from, to } =
|
|
2673
|
+
measureVisibleLineHeights(viewport) {
|
|
2674
|
+
let result = [], { from, to } = viewport;
|
|
2764
2675
|
let contentWidth = this.view.contentDOM.clientWidth;
|
|
2765
2676
|
let isWider = contentWidth > Math.max(this.view.scrollDOM.clientWidth, this.minWidth) + 1;
|
|
2766
|
-
let widest = -1;
|
|
2677
|
+
let widest = -1, ltr = this.view.textDirection == Direction.LTR;
|
|
2767
2678
|
for (let pos = 0, i = 0; i < this.children.length; i++) {
|
|
2768
2679
|
let child = this.children[i], end = pos + child.length;
|
|
2769
2680
|
if (end > to)
|
|
@@ -2776,8 +2687,7 @@ class DocView extends ContentView {
|
|
|
2776
2687
|
let rects = last ? clientRectsFor(last) : [];
|
|
2777
2688
|
if (rects.length) {
|
|
2778
2689
|
let rect = rects[rects.length - 1];
|
|
2779
|
-
let width =
|
|
2780
|
-
: childRect.right - rect.left;
|
|
2690
|
+
let width = ltr ? rect.right - childRect.left : childRect.right - rect.left;
|
|
2781
2691
|
if (width > widest) {
|
|
2782
2692
|
widest = width;
|
|
2783
2693
|
this.minWidth = contentWidth;
|
|
@@ -2791,6 +2701,10 @@ class DocView extends ContentView {
|
|
|
2791
2701
|
}
|
|
2792
2702
|
return result;
|
|
2793
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
|
+
}
|
|
2794
2708
|
measureTextSize() {
|
|
2795
2709
|
for (let child of this.children) {
|
|
2796
2710
|
if (child instanceof LineView) {
|
|
@@ -2842,11 +2756,14 @@ class DocView extends ContentView {
|
|
|
2842
2756
|
return Decoration.set(deco);
|
|
2843
2757
|
}
|
|
2844
2758
|
updateDeco() {
|
|
2845
|
-
let
|
|
2846
|
-
|
|
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;
|
|
2847
2765
|
return this.decorations = [
|
|
2848
|
-
...
|
|
2849
|
-
...this.view.state.facet(decorations),
|
|
2766
|
+
...allDeco,
|
|
2850
2767
|
this.compositionDeco,
|
|
2851
2768
|
this.computeBlockGapDeco(),
|
|
2852
2769
|
this.view.viewState.lineGapDeco
|
|
@@ -2861,7 +2778,7 @@ class DocView extends ContentView {
|
|
|
2861
2778
|
rect = { left: Math.min(rect.left, other.left), top: Math.min(rect.top, other.top),
|
|
2862
2779
|
right: Math.max(rect.right, other.right), bottom: Math.max(rect.bottom, other.bottom) };
|
|
2863
2780
|
let mLeft = 0, mRight = 0, mTop = 0, mBottom = 0;
|
|
2864
|
-
for (let margins of this.view.
|
|
2781
|
+
for (let margins of this.view.state.facet(scrollMargins).map(f => f(this.view)))
|
|
2865
2782
|
if (margins) {
|
|
2866
2783
|
let { left, right, top, bottom } = margins;
|
|
2867
2784
|
if (left != null)
|
|
@@ -3250,7 +3167,8 @@ function moveToLineBoundary(view, start, forward, includeWrap) {
|
|
|
3250
3167
|
: view.coordsAtPos(start.assoc < 0 && start.head > line.from ? start.head - 1 : start.head);
|
|
3251
3168
|
if (coords) {
|
|
3252
3169
|
let editorRect = view.dom.getBoundingClientRect();
|
|
3253
|
-
let
|
|
3170
|
+
let direction = view.textDirectionAt(line.from);
|
|
3171
|
+
let pos = view.posAtCoords({ x: forward == (direction == Direction.LTR) ? editorRect.right - 1 : editorRect.left + 1,
|
|
3254
3172
|
y: (coords.top + coords.bottom) / 2 });
|
|
3255
3173
|
if (pos != null)
|
|
3256
3174
|
return EditorSelection.cursor(pos, forward ? -1 : 1);
|
|
@@ -3261,8 +3179,9 @@ function moveToLineBoundary(view, start, forward, includeWrap) {
|
|
|
3261
3179
|
}
|
|
3262
3180
|
function moveByChar(view, start, forward, by) {
|
|
3263
3181
|
let line = view.state.doc.lineAt(start.head), spans = view.bidiSpans(line);
|
|
3182
|
+
let direction = view.textDirectionAt(line.from);
|
|
3264
3183
|
for (let cur = start, check = null;;) {
|
|
3265
|
-
let next = moveVisually(line, spans,
|
|
3184
|
+
let next = moveVisually(line, spans, direction, cur, forward), char = movedOver;
|
|
3266
3185
|
if (!next) {
|
|
3267
3186
|
if (line.number == (forward ? view.state.doc.lines : 1))
|
|
3268
3187
|
return cur;
|
|
@@ -3305,7 +3224,7 @@ function moveVertically(view, start, forward, distance) {
|
|
|
3305
3224
|
startY = dir < 0 ? startCoords.top : startCoords.bottom;
|
|
3306
3225
|
}
|
|
3307
3226
|
else {
|
|
3308
|
-
let line = view.viewState.lineBlockAt(startPos
|
|
3227
|
+
let line = view.viewState.lineBlockAt(startPos);
|
|
3309
3228
|
if (goal == null)
|
|
3310
3229
|
goal = Math.min(rect.right - rect.left, view.defaultCharacterWidth * (startPos - line.from));
|
|
3311
3230
|
startY = (dir < 0 ? line.top : line.bottom) + docTop;
|
|
@@ -3320,7 +3239,7 @@ function moveVertically(view, start, forward, distance) {
|
|
|
3320
3239
|
}
|
|
3321
3240
|
}
|
|
3322
3241
|
function skipAtoms(view, oldPos, pos) {
|
|
3323
|
-
let atoms = view.
|
|
3242
|
+
let atoms = view.state.facet(atomicRanges).map(f => f(view));
|
|
3324
3243
|
for (;;) {
|
|
3325
3244
|
let moved = false;
|
|
3326
3245
|
for (let set of atoms) {
|
|
@@ -3368,10 +3287,10 @@ class InputState {
|
|
|
3368
3287
|
for (let type in handlers) {
|
|
3369
3288
|
let handler = handlers[type];
|
|
3370
3289
|
view.contentDOM.addEventListener(type, (event) => {
|
|
3371
|
-
if (type == "keydown" && this.keydown(view, event))
|
|
3372
|
-
return;
|
|
3373
3290
|
if (!eventBelongsToEditor(view, event) || this.ignoreDuringComposition(event))
|
|
3374
3291
|
return;
|
|
3292
|
+
if (type == "keydown" && this.keydown(view, event))
|
|
3293
|
+
return;
|
|
3375
3294
|
if (this.mustFlushObserver(event))
|
|
3376
3295
|
view.observer.forceFlush();
|
|
3377
3296
|
if (this.runCustomHandlers(type, view, event))
|
|
@@ -3382,7 +3301,6 @@ class InputState {
|
|
|
3382
3301
|
this.registeredEvents.push(type);
|
|
3383
3302
|
}
|
|
3384
3303
|
this.notifiedFocused = view.hasFocus;
|
|
3385
|
-
this.ensureHandlers(view);
|
|
3386
3304
|
// On Safari adding an input event handler somehow prevents an
|
|
3387
3305
|
// issue where the composition vanishes when you press enter.
|
|
3388
3306
|
if (browser.safari)
|
|
@@ -3392,20 +3310,23 @@ class InputState {
|
|
|
3392
3310
|
this.lastSelectionOrigin = origin;
|
|
3393
3311
|
this.lastSelectionTime = Date.now();
|
|
3394
3312
|
}
|
|
3395
|
-
ensureHandlers(view) {
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
event
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
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
|
+
}
|
|
3409
3330
|
}
|
|
3410
3331
|
runCustomHandlers(type, view, event) {
|
|
3411
3332
|
for (let set of this.customHandlers) {
|
|
@@ -3439,7 +3360,7 @@ class InputState {
|
|
|
3439
3360
|
// Must always run, even if a custom handler handled the event
|
|
3440
3361
|
this.lastKeyCode = event.keyCode;
|
|
3441
3362
|
this.lastKeyTime = Date.now();
|
|
3442
|
-
if (
|
|
3363
|
+
if (event.keyCode == 9 && Date.now() < this.lastEscPress + 2000)
|
|
3443
3364
|
return true;
|
|
3444
3365
|
// Chrome for Android usually doesn't fire proper key events, but
|
|
3445
3366
|
// occasionally does, usually surrounded by a bunch of complicated
|
|
@@ -3483,20 +3404,12 @@ class InputState {
|
|
|
3483
3404
|
// compositionend and keydown events are sometimes emitted in the
|
|
3484
3405
|
// wrong order. The key event should still be ignored, even when
|
|
3485
3406
|
// it happens after the compositionend event.
|
|
3486
|
-
if (browser.safari && Date.now() - this.compositionEndedAt <
|
|
3407
|
+
if (browser.safari && Date.now() - this.compositionEndedAt < 100) {
|
|
3487
3408
|
this.compositionEndedAt = 0;
|
|
3488
3409
|
return true;
|
|
3489
3410
|
}
|
|
3490
3411
|
return false;
|
|
3491
3412
|
}
|
|
3492
|
-
screenKeyEvent(view, event) {
|
|
3493
|
-
let protectedTab = event.keyCode == 9 && Date.now() < this.lastEscPress + 2000;
|
|
3494
|
-
if (event.keyCode == 27)
|
|
3495
|
-
this.lastEscPress = Date.now();
|
|
3496
|
-
else if (modifierCodes.indexOf(event.keyCode) < 0)
|
|
3497
|
-
this.lastEscPress = 0;
|
|
3498
|
-
return protectedTab;
|
|
3499
|
-
}
|
|
3500
3413
|
mustFlushObserver(event) {
|
|
3501
3414
|
return (event.type == "keydown" && event.keyCode != 229) ||
|
|
3502
3415
|
event.type == "compositionend" && !browser.ios;
|
|
@@ -3670,6 +3583,10 @@ function doPaste(view, input) {
|
|
|
3670
3583
|
}
|
|
3671
3584
|
handlers.keydown = (view, event) => {
|
|
3672
3585
|
view.inputState.setSelectionOrigin("select");
|
|
3586
|
+
if (event.keyCode == 27)
|
|
3587
|
+
view.inputState.lastEscPress = Date.now();
|
|
3588
|
+
else if (modifierCodes.indexOf(event.keyCode) < 0)
|
|
3589
|
+
view.inputState.lastEscPress = 0;
|
|
3673
3590
|
};
|
|
3674
3591
|
let lastTouch = 0;
|
|
3675
3592
|
handlers.touchstart = (view, e) => {
|
|
@@ -3927,14 +3844,6 @@ handlers.focus = handlers.blur = view => {
|
|
|
3927
3844
|
view.update([]);
|
|
3928
3845
|
}, 10);
|
|
3929
3846
|
};
|
|
3930
|
-
handlers.beforeprint = view => {
|
|
3931
|
-
view.viewState.printing = true;
|
|
3932
|
-
view.requestMeasure();
|
|
3933
|
-
setTimeout(() => {
|
|
3934
|
-
view.viewState.printing = false;
|
|
3935
|
-
view.requestMeasure();
|
|
3936
|
-
}, 2000);
|
|
3937
|
-
};
|
|
3938
3847
|
function forceClearComposition(view, rapid) {
|
|
3939
3848
|
if (view.docView.compositionDeco.size) {
|
|
3940
3849
|
view.inputState.rapidCompositionStart = rapid;
|
|
@@ -4003,7 +3912,6 @@ class HeightOracle {
|
|
|
4003
3912
|
constructor() {
|
|
4004
3913
|
this.doc = Text.empty;
|
|
4005
3914
|
this.lineWrapping = false;
|
|
4006
|
-
this.direction = Direction.LTR;
|
|
4007
3915
|
this.heightSamples = {};
|
|
4008
3916
|
this.lineHeight = 14;
|
|
4009
3917
|
this.charWidth = 7;
|
|
@@ -4024,8 +3932,8 @@ class HeightOracle {
|
|
|
4024
3932
|
return lines * this.lineHeight;
|
|
4025
3933
|
}
|
|
4026
3934
|
setDoc(doc) { this.doc = doc; return this; }
|
|
4027
|
-
|
|
4028
|
-
return (wrappingWhiteSpace.indexOf(whiteSpace) > -1) != this.lineWrapping
|
|
3935
|
+
mustRefreshForWrapping(whiteSpace) {
|
|
3936
|
+
return (wrappingWhiteSpace.indexOf(whiteSpace) > -1) != this.lineWrapping;
|
|
4029
3937
|
}
|
|
4030
3938
|
mustRefreshForHeights(lineHeights) {
|
|
4031
3939
|
let newHeight = false;
|
|
@@ -4041,13 +3949,10 @@ class HeightOracle {
|
|
|
4041
3949
|
}
|
|
4042
3950
|
return newHeight;
|
|
4043
3951
|
}
|
|
4044
|
-
refresh(whiteSpace,
|
|
3952
|
+
refresh(whiteSpace, lineHeight, charWidth, lineLength, knownHeights) {
|
|
4045
3953
|
let lineWrapping = wrappingWhiteSpace.indexOf(whiteSpace) > -1;
|
|
4046
|
-
let changed = Math.round(lineHeight) != Math.round(this.lineHeight) ||
|
|
4047
|
-
this.lineWrapping != lineWrapping ||
|
|
4048
|
-
this.direction != direction;
|
|
3954
|
+
let changed = Math.round(lineHeight) != Math.round(this.lineHeight) || this.lineWrapping != lineWrapping;
|
|
4049
3955
|
this.lineWrapping = lineWrapping;
|
|
4050
|
-
this.direction = direction;
|
|
4051
3956
|
this.lineHeight = lineHeight;
|
|
4052
3957
|
this.charWidth = charWidth;
|
|
4053
3958
|
this.lineLength = lineLength;
|
|
@@ -4128,12 +4033,6 @@ class BlockInfo {
|
|
|
4128
4033
|
.concat(Array.isArray(other.type) ? other.type : [other]);
|
|
4129
4034
|
return new BlockInfo(this.from, this.length + other.length, this.top, this.height + other.height, detail);
|
|
4130
4035
|
}
|
|
4131
|
-
/**
|
|
4132
|
-
FIXME remove on next breaking release @internal
|
|
4133
|
-
*/
|
|
4134
|
-
moveY(offset) {
|
|
4135
|
-
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);
|
|
4136
|
-
}
|
|
4137
4036
|
}
|
|
4138
4037
|
var QueryType = /*@__PURE__*/(function (QueryType) {
|
|
4139
4038
|
QueryType[QueryType["ByPos"] = 0] = "ByPos";
|
|
@@ -4257,8 +4156,9 @@ class HeightMapBlock extends HeightMap {
|
|
|
4257
4156
|
lineAt(_value, _type, doc, top, offset) {
|
|
4258
4157
|
return this.blockAt(0, doc, top, offset);
|
|
4259
4158
|
}
|
|
4260
|
-
forEachLine(
|
|
4261
|
-
|
|
4159
|
+
forEachLine(from, to, doc, top, offset, f) {
|
|
4160
|
+
if (from <= offset + this.length && to >= offset)
|
|
4161
|
+
f(this.blockAt(0, doc, top, offset));
|
|
4262
4162
|
}
|
|
4263
4163
|
updateHeight(oracle, offset = 0, _force = false, measured) {
|
|
4264
4164
|
if (measured && measured.from <= offset && measured.more)
|
|
@@ -4689,6 +4589,11 @@ function visiblePixelRange(dom, paddingTop) {
|
|
|
4689
4589
|
return { left: left - rect.left, right: Math.max(left, right) - rect.left,
|
|
4690
4590
|
top: top - (rect.top + paddingTop), bottom: Math.max(top, bottom) - (rect.top + paddingTop) };
|
|
4691
4591
|
}
|
|
4592
|
+
function fullPixelRange(dom, paddingTop) {
|
|
4593
|
+
let rect = dom.getBoundingClientRect();
|
|
4594
|
+
return { left: 0, right: rect.right - rect.left,
|
|
4595
|
+
top: paddingTop, bottom: rect.bottom - (rect.top + paddingTop) };
|
|
4596
|
+
}
|
|
4692
4597
|
// Line gaps are placeholder widgets used to hide pieces of overlong
|
|
4693
4598
|
// lines within the viewport, as a kludge to keep the editor
|
|
4694
4599
|
// responsive when a ridiculously long line is loaded into it.
|
|
@@ -4754,6 +4659,7 @@ class ViewState {
|
|
|
4754
4659
|
// Flag set when editor content was redrawn, so that the next
|
|
4755
4660
|
// measure stage knows it must read DOM layout
|
|
4756
4661
|
this.mustMeasureContent = true;
|
|
4662
|
+
this.defaultTextDirection = Direction.RTL;
|
|
4757
4663
|
this.visibleRanges = [];
|
|
4758
4664
|
// Cursor 'assoc' is only significant when the cursor is on a line
|
|
4759
4665
|
// wrap point, where it must stick to the character that it is
|
|
@@ -4764,7 +4670,8 @@ class ViewState {
|
|
|
4764
4670
|
// boundary and, if so, reset it to make sure it is positioned in
|
|
4765
4671
|
// the right place.
|
|
4766
4672
|
this.mustEnforceCursorAssoc = false;
|
|
4767
|
-
this.
|
|
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)]);
|
|
4768
4675
|
this.viewport = this.getViewport(0, null);
|
|
4769
4676
|
this.updateViewportLines();
|
|
4770
4677
|
this.updateForViewport();
|
|
@@ -4792,13 +4699,13 @@ class ViewState {
|
|
|
4792
4699
|
});
|
|
4793
4700
|
}
|
|
4794
4701
|
update(update, scrollTarget = null) {
|
|
4795
|
-
let prev = this.state;
|
|
4796
4702
|
this.state = update.state;
|
|
4797
|
-
let
|
|
4703
|
+
let prevDeco = this.stateDeco;
|
|
4704
|
+
this.stateDeco = this.state.facet(decorations).filter(d => typeof d != "function");
|
|
4798
4705
|
let contentChanges = update.changedRanges;
|
|
4799
|
-
let heightChanges = ChangedRange.extendWithRanges(contentChanges, heightRelevantDecoChanges(
|
|
4706
|
+
let heightChanges = ChangedRange.extendWithRanges(contentChanges, heightRelevantDecoChanges(prevDeco, this.stateDeco, update ? update.changes : ChangeSet.empty(this.state.doc.length)));
|
|
4800
4707
|
let prevHeight = this.heightMap.height;
|
|
4801
|
-
this.heightMap = this.heightMap.applyChanges(
|
|
4708
|
+
this.heightMap = this.heightMap.applyChanges(this.stateDeco, update.startState.doc, this.heightOracle.setDoc(this.state.doc), heightChanges);
|
|
4802
4709
|
if (this.heightMap.height != prevHeight)
|
|
4803
4710
|
update.flags |= 2 /* Height */;
|
|
4804
4711
|
let viewport = heightChanges.length ? this.mapViewport(this.viewport, update.changes) : this.viewport;
|
|
@@ -4823,8 +4730,9 @@ class ViewState {
|
|
|
4823
4730
|
measure(view) {
|
|
4824
4731
|
let dom = view.contentDOM, style = window.getComputedStyle(dom);
|
|
4825
4732
|
let oracle = this.heightOracle;
|
|
4826
|
-
let whiteSpace = style.whiteSpace
|
|
4827
|
-
|
|
4733
|
+
let whiteSpace = style.whiteSpace;
|
|
4734
|
+
this.defaultTextDirection = style.direction == "rtl" ? Direction.RTL : Direction.LTR;
|
|
4735
|
+
let refresh = this.heightOracle.mustRefreshForWrapping(whiteSpace);
|
|
4828
4736
|
let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != dom.clientHeight;
|
|
4829
4737
|
let result = 0, bias = 0;
|
|
4830
4738
|
if (this.editorWidth != view.scrollDOM.clientWidth) {
|
|
@@ -4845,8 +4753,7 @@ class ViewState {
|
|
|
4845
4753
|
}
|
|
4846
4754
|
}
|
|
4847
4755
|
// Pixel viewport
|
|
4848
|
-
let pixelViewport = this.printing ?
|
|
4849
|
-
: visiblePixelRange(dom, this.paddingTop);
|
|
4756
|
+
let pixelViewport = (this.printing ? fullPixelRange : visiblePixelRange)(dom, this.paddingTop);
|
|
4850
4757
|
let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
|
|
4851
4758
|
this.pixelViewport = pixelViewport;
|
|
4852
4759
|
let inView = this.pixelViewport.bottom > this.pixelViewport.top && this.pixelViewport.right > this.pixelViewport.left;
|
|
@@ -4864,12 +4771,12 @@ class ViewState {
|
|
|
4864
4771
|
result |= 8 /* Geometry */;
|
|
4865
4772
|
}
|
|
4866
4773
|
if (measureContent) {
|
|
4867
|
-
let lineHeights = view.docView.measureVisibleLineHeights();
|
|
4774
|
+
let lineHeights = view.docView.measureVisibleLineHeights(this.viewport);
|
|
4868
4775
|
if (oracle.mustRefreshForHeights(lineHeights))
|
|
4869
4776
|
refresh = true;
|
|
4870
4777
|
if (refresh || oracle.lineWrapping && Math.abs(contentWidth - this.contentDOMWidth) > oracle.charWidth) {
|
|
4871
4778
|
let { lineHeight, charWidth } = view.docView.measureTextSize();
|
|
4872
|
-
refresh = oracle.refresh(whiteSpace,
|
|
4779
|
+
refresh = oracle.refresh(whiteSpace, lineHeight, charWidth, contentWidth / charWidth, lineHeights);
|
|
4873
4780
|
if (refresh) {
|
|
4874
4781
|
view.docView.minWidth = 0;
|
|
4875
4782
|
result |= 8 /* Geometry */;
|
|
@@ -4880,7 +4787,10 @@ class ViewState {
|
|
|
4880
4787
|
else if (dTop < 0 && dBottom < 0)
|
|
4881
4788
|
bias = Math.min(dTop, dBottom);
|
|
4882
4789
|
oracle.heightChanged = false;
|
|
4883
|
-
|
|
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
|
+
}
|
|
4884
4794
|
if (oracle.heightChanged)
|
|
4885
4795
|
result |= 2 /* Height */;
|
|
4886
4796
|
}
|
|
@@ -4966,12 +4876,12 @@ class ViewState {
|
|
|
4966
4876
|
ensureLineGaps(current) {
|
|
4967
4877
|
let gaps = [];
|
|
4968
4878
|
// This won't work at all in predominantly right-to-left text.
|
|
4969
|
-
if (this.
|
|
4879
|
+
if (this.defaultTextDirection != Direction.LTR)
|
|
4970
4880
|
return gaps;
|
|
4971
4881
|
for (let line of this.viewportLines) {
|
|
4972
4882
|
if (line.length < 4000 /* DoubleMargin */)
|
|
4973
4883
|
continue;
|
|
4974
|
-
let structure = lineStructure(line.from, line.to, this.
|
|
4884
|
+
let structure = lineStructure(line.from, line.to, this.stateDeco);
|
|
4975
4885
|
if (structure.total < 4000 /* DoubleMargin */)
|
|
4976
4886
|
continue;
|
|
4977
4887
|
let viewFrom, viewTo;
|
|
@@ -5022,7 +4932,7 @@ class ViewState {
|
|
|
5022
4932
|
}
|
|
5023
4933
|
}
|
|
5024
4934
|
computeVisibleRanges() {
|
|
5025
|
-
let deco = this.
|
|
4935
|
+
let deco = this.stateDeco;
|
|
5026
4936
|
if (this.lineGaps.length)
|
|
5027
4937
|
deco = deco.concat(this.lineGapDeco);
|
|
5028
4938
|
let ranges = [];
|
|
@@ -5058,9 +4968,9 @@ class Viewport {
|
|
|
5058
4968
|
this.to = to;
|
|
5059
4969
|
}
|
|
5060
4970
|
}
|
|
5061
|
-
function lineStructure(from, to,
|
|
4971
|
+
function lineStructure(from, to, stateDeco) {
|
|
5062
4972
|
let ranges = [], pos = from, total = 0;
|
|
5063
|
-
RangeSet.spans(
|
|
4973
|
+
RangeSet.spans(stateDeco, from, to, {
|
|
5064
4974
|
span() { },
|
|
5065
4975
|
point(from, to) {
|
|
5066
4976
|
if (from > pos) {
|
|
@@ -5193,7 +5103,7 @@ function buildTheme(main, spec, scopes) {
|
|
|
5193
5103
|
}
|
|
5194
5104
|
});
|
|
5195
5105
|
}
|
|
5196
|
-
const baseTheme = /*@__PURE__*/buildTheme("." + baseThemeID, {
|
|
5106
|
+
const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
|
|
5197
5107
|
"&.cm-editor": {
|
|
5198
5108
|
position: "relative !important",
|
|
5199
5109
|
boxSizing: "border-box",
|
|
@@ -5298,6 +5208,65 @@ const baseTheme = /*@__PURE__*/buildTheme("." + baseThemeID, {
|
|
|
5298
5208
|
"&dark .cm-activeLine": { backgroundColor: "#223039" },
|
|
5299
5209
|
"&light .cm-specialChar": { color: "red" },
|
|
5300
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
|
+
},
|
|
5301
5270
|
".cm-tab": {
|
|
5302
5271
|
display: "inline-block",
|
|
5303
5272
|
overflow: "hidden",
|
|
@@ -5423,6 +5392,7 @@ class DOMObserver {
|
|
|
5423
5392
|
});
|
|
5424
5393
|
this.resize.observe(view.scrollDOM);
|
|
5425
5394
|
}
|
|
5395
|
+
window.addEventListener("beforeprint", this.onPrint = this.onPrint.bind(this));
|
|
5426
5396
|
this.start();
|
|
5427
5397
|
window.addEventListener("scroll", this.onScroll = this.onScroll.bind(this));
|
|
5428
5398
|
if (typeof IntersectionObserver == "function") {
|
|
@@ -5457,6 +5427,14 @@ class DOMObserver {
|
|
|
5457
5427
|
this.view.requestMeasure();
|
|
5458
5428
|
}, 50);
|
|
5459
5429
|
}
|
|
5430
|
+
onPrint() {
|
|
5431
|
+
this.view.viewState.printing = true;
|
|
5432
|
+
this.view.measure();
|
|
5433
|
+
setTimeout(() => {
|
|
5434
|
+
this.view.viewState.printing = false;
|
|
5435
|
+
this.view.requestMeasure();
|
|
5436
|
+
}, 500);
|
|
5437
|
+
}
|
|
5460
5438
|
updateGaps(gaps) {
|
|
5461
5439
|
if (this.gapIntersection && (gaps.length != this.gaps.length || this.gaps.some((g, i) => g != gaps[i]))) {
|
|
5462
5440
|
this.gapIntersection.disconnect();
|
|
@@ -5674,6 +5652,7 @@ class DOMObserver {
|
|
|
5674
5652
|
dom.removeEventListener("scroll", this.onScroll);
|
|
5675
5653
|
window.removeEventListener("scroll", this.onScroll);
|
|
5676
5654
|
window.removeEventListener("resize", this.onResize);
|
|
5655
|
+
window.removeEventListener("beforeprint", this.onPrint);
|
|
5677
5656
|
this.dom.ownerDocument.removeEventListener("selectionchange", this.onSelectionChange);
|
|
5678
5657
|
clearTimeout(this.parentCheck);
|
|
5679
5658
|
clearTimeout(this.resizeTimeout);
|
|
@@ -5745,7 +5724,7 @@ function applyDOMChange(view, start, end, typeOver) {
|
|
|
5745
5724
|
diff.toB == diff.from + 2 && reader.text.slice(diff.from, diff.toB) == LineBreakPlaceholder + LineBreakPlaceholder)
|
|
5746
5725
|
diff.toB--;
|
|
5747
5726
|
change = { from: from + diff.from, to: from + diff.toA,
|
|
5748
|
-
insert: Text
|
|
5727
|
+
insert: Text.of(reader.text.slice(diff.from, diff.toB).split(LineBreakPlaceholder)) };
|
|
5749
5728
|
}
|
|
5750
5729
|
newSel = selectionFromPoints(selPoints, from);
|
|
5751
5730
|
}
|
|
@@ -5936,9 +5915,9 @@ transactions for editing actions.
|
|
|
5936
5915
|
*/
|
|
5937
5916
|
class EditorView {
|
|
5938
5917
|
/**
|
|
5939
|
-
Construct a new view. You'll
|
|
5940
|
-
|
|
5941
|
-
|
|
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.
|
|
5942
5921
|
*/
|
|
5943
5922
|
constructor(
|
|
5944
5923
|
/**
|
|
@@ -5989,6 +5968,7 @@ class EditorView {
|
|
|
5989
5968
|
this.measure();
|
|
5990
5969
|
});
|
|
5991
5970
|
this.inputState = new InputState(this);
|
|
5971
|
+
this.inputState.ensureHandlers(this, this.plugins);
|
|
5992
5972
|
this.docView = new DocView(this);
|
|
5993
5973
|
this.mountStyles();
|
|
5994
5974
|
this.updateAttrs();
|
|
@@ -6076,14 +6056,9 @@ class EditorView {
|
|
|
6076
6056
|
let { main } = tr.state.selection;
|
|
6077
6057
|
scrollTarget = new ScrollTarget(main.empty ? main : EditorSelection.cursor(main.head, main.head > main.anchor ? -1 : 1));
|
|
6078
6058
|
}
|
|
6079
|
-
for (let e of tr.effects)
|
|
6080
|
-
if (e.is(
|
|
6081
|
-
scrollTarget = new ScrollTarget(e.value);
|
|
6082
|
-
else if (e.is(centerOn))
|
|
6083
|
-
scrollTarget = new ScrollTarget(e.value, "center");
|
|
6084
|
-
else if (e.is(scrollIntoView))
|
|
6059
|
+
for (let e of tr.effects)
|
|
6060
|
+
if (e.is(scrollIntoView))
|
|
6085
6061
|
scrollTarget = e.value;
|
|
6086
|
-
}
|
|
6087
6062
|
}
|
|
6088
6063
|
this.viewState.update(update, scrollTarget);
|
|
6089
6064
|
this.bidiCache = CachedOrder.update(this.bidiCache, update.changes);
|
|
@@ -6134,7 +6109,7 @@ class EditorView {
|
|
|
6134
6109
|
for (let plugin of this.plugins)
|
|
6135
6110
|
plugin.update(this);
|
|
6136
6111
|
this.docView = new DocView(this);
|
|
6137
|
-
this.inputState.ensureHandlers(this);
|
|
6112
|
+
this.inputState.ensureHandlers(this, this.plugins);
|
|
6138
6113
|
this.mountStyles();
|
|
6139
6114
|
this.updateAttrs();
|
|
6140
6115
|
this.bidiCache = [];
|
|
@@ -6166,7 +6141,7 @@ class EditorView {
|
|
|
6166
6141
|
plugin.destroy(this);
|
|
6167
6142
|
this.plugins = newPlugins;
|
|
6168
6143
|
this.pluginMap.clear();
|
|
6169
|
-
this.inputState.ensureHandlers(this);
|
|
6144
|
+
this.inputState.ensureHandlers(this, this.plugins);
|
|
6170
6145
|
}
|
|
6171
6146
|
else {
|
|
6172
6147
|
for (let p of this.plugins)
|
|
@@ -6304,7 +6279,7 @@ class EditorView {
|
|
|
6304
6279
|
}
|
|
6305
6280
|
mountStyles() {
|
|
6306
6281
|
this.styleModules = this.state.facet(styleModule);
|
|
6307
|
-
StyleModule.mount(this.root, this.styleModules.concat(baseTheme).reverse());
|
|
6282
|
+
StyleModule.mount(this.root, this.styleModules.concat(baseTheme$1).reverse());
|
|
6308
6283
|
}
|
|
6309
6284
|
readMeasured() {
|
|
6310
6285
|
if (this.updateState == 2 /* Updating */)
|
|
@@ -6335,16 +6310,6 @@ class EditorView {
|
|
|
6335
6310
|
}
|
|
6336
6311
|
}
|
|
6337
6312
|
/**
|
|
6338
|
-
Collect all values provided by the active plugins for a given
|
|
6339
|
-
field.
|
|
6340
|
-
*/
|
|
6341
|
-
pluginField(field) {
|
|
6342
|
-
let result = [];
|
|
6343
|
-
for (let plugin of this.plugins)
|
|
6344
|
-
plugin.update(this).takeField(field, result);
|
|
6345
|
-
return result;
|
|
6346
|
-
}
|
|
6347
|
-
/**
|
|
6348
6313
|
Get the value of a specific plugin, if present. Note that
|
|
6349
6314
|
plugins that crash can be dropped from a view, so even when you
|
|
6350
6315
|
know you registered a given plugin, it is recommended to check
|
|
@@ -6371,23 +6336,6 @@ class EditorView {
|
|
|
6371
6336
|
return { top: this.viewState.paddingTop, bottom: this.viewState.paddingBottom };
|
|
6372
6337
|
}
|
|
6373
6338
|
/**
|
|
6374
|
-
Find the line or block widget at the given vertical position.
|
|
6375
|
-
|
|
6376
|
-
By default, this position is interpreted as a screen position,
|
|
6377
|
-
meaning `docTop` is set to the DOM top position of the editor
|
|
6378
|
-
content (forcing a layout). You can pass a different `docTop`
|
|
6379
|
-
value—for example 0 to interpret `height` as a document-relative
|
|
6380
|
-
position, or a precomputed document top
|
|
6381
|
-
(`view.contentDOM.getBoundingClientRect().top`) to limit layout
|
|
6382
|
-
queries.
|
|
6383
|
-
|
|
6384
|
-
*Deprecated: use `elementAtHeight` instead.*
|
|
6385
|
-
*/
|
|
6386
|
-
blockAtHeight(height, docTop) {
|
|
6387
|
-
let top = ensureTop(docTop, this);
|
|
6388
|
-
return this.elementAtHeight(height - top).moveY(top);
|
|
6389
|
-
}
|
|
6390
|
-
/**
|
|
6391
6339
|
Find the text line or block widget at the given vertical
|
|
6392
6340
|
position (which is interpreted as relative to the [top of the
|
|
6393
6341
|
document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)
|
|
@@ -6397,23 +6345,6 @@ class EditorView {
|
|
|
6397
6345
|
return this.viewState.elementAtHeight(height);
|
|
6398
6346
|
}
|
|
6399
6347
|
/**
|
|
6400
|
-
Find information for the visual line (see
|
|
6401
|
-
[`visualLineAt`](https://codemirror.net/6/docs/ref/#view.EditorView.visualLineAt)) at the given
|
|
6402
|
-
vertical position. The resulting block info might hold another
|
|
6403
|
-
array of block info structs in its `type` field if this line
|
|
6404
|
-
consists of more than one block.
|
|
6405
|
-
|
|
6406
|
-
Defaults to treating `height` as a screen position. See
|
|
6407
|
-
[`blockAtHeight`](https://codemirror.net/6/docs/ref/#view.EditorView.blockAtHeight) for the
|
|
6408
|
-
interpretation of the `docTop` parameter.
|
|
6409
|
-
|
|
6410
|
-
*Deprecated: use `lineBlockAtHeight` instead.*
|
|
6411
|
-
*/
|
|
6412
|
-
visualLineAtHeight(height, docTop) {
|
|
6413
|
-
let top = ensureTop(docTop, this);
|
|
6414
|
-
return this.lineBlockAtHeight(height - top).moveY(top);
|
|
6415
|
-
}
|
|
6416
|
-
/**
|
|
6417
6348
|
Find the line block (see
|
|
6418
6349
|
[`lineBlockAt`](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) at the given
|
|
6419
6350
|
height.
|
|
@@ -6423,19 +6354,6 @@ class EditorView {
|
|
|
6423
6354
|
return this.viewState.lineBlockAtHeight(height);
|
|
6424
6355
|
}
|
|
6425
6356
|
/**
|
|
6426
|
-
Iterate over the height information of the visual lines in the
|
|
6427
|
-
viewport. The heights of lines are reported relative to the
|
|
6428
|
-
given document top, which defaults to the screen position of the
|
|
6429
|
-
document (forcing a layout).
|
|
6430
|
-
|
|
6431
|
-
*Deprecated: use `viewportLineBlocks` instead.*
|
|
6432
|
-
*/
|
|
6433
|
-
viewportLines(f, docTop) {
|
|
6434
|
-
let top = ensureTop(docTop, this);
|
|
6435
|
-
for (let line of this.viewportLineBlocks)
|
|
6436
|
-
f(line.moveY(top));
|
|
6437
|
-
}
|
|
6438
|
-
/**
|
|
6439
6357
|
Get the extent and vertical position of all [line
|
|
6440
6358
|
blocks](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) in the viewport. Positions
|
|
6441
6359
|
are relative to the [top of the
|
|
@@ -6445,24 +6363,9 @@ class EditorView {
|
|
|
6445
6363
|
return this.viewState.viewportLines;
|
|
6446
6364
|
}
|
|
6447
6365
|
/**
|
|
6448
|
-
Find the extent and height of the visual line (a range delimited
|
|
6449
|
-
on both sides by either non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^range)
|
|
6450
|
-
line breaks, or the start/end of the document) at the given position.
|
|
6451
|
-
|
|
6452
|
-
Vertical positions are computed relative to the `docTop`
|
|
6453
|
-
argument, which defaults to 0 for this method. You can pass
|
|
6454
|
-
`view.contentDOM.getBoundingClientRect().top` here to get screen
|
|
6455
|
-
coordinates.
|
|
6456
|
-
|
|
6457
|
-
*Deprecated: use `lineBlockAt` instead.*
|
|
6458
|
-
*/
|
|
6459
|
-
visualLineAt(pos, docTop = 0) {
|
|
6460
|
-
return this.lineBlockAt(pos).moveY(docTop + this.viewState.paddingTop);
|
|
6461
|
-
}
|
|
6462
|
-
/**
|
|
6463
6366
|
Find the line block around the given document position. A line
|
|
6464
6367
|
block is a range delimited on both sides by either a
|
|
6465
|
-
non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^
|
|
6368
|
+
non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^replace) line breaks, or the
|
|
6466
6369
|
start/end of the document. It will usually just hold a line of
|
|
6467
6370
|
text, but may be broken into multiple textblocks by block
|
|
6468
6371
|
widgets.
|
|
@@ -6478,13 +6381,13 @@ class EditorView {
|
|
|
6478
6381
|
}
|
|
6479
6382
|
/**
|
|
6480
6383
|
Move a cursor position by [grapheme
|
|
6481
|
-
cluster](https://codemirror.net/6/docs/ref/#
|
|
6482
|
-
the motion is away from the line start, or towards it.
|
|
6483
|
-
bidirectional text is in visual order,
|
|
6484
|
-
direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
|
|
6485
|
-
position was the last one on the line, the
|
|
6486
|
-
will be across the line break. If there is no
|
|
6487
|
-
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.
|
|
6488
6391
|
|
|
6489
6392
|
By default, this method moves over a single cluster. The
|
|
6490
6393
|
optional `by` argument can be used to move across more. It will
|
|
@@ -6529,10 +6432,6 @@ class EditorView {
|
|
|
6529
6432
|
moveVertically(start, forward, distance) {
|
|
6530
6433
|
return skipAtoms(this, start, moveVertically(this, start, forward, distance));
|
|
6531
6434
|
}
|
|
6532
|
-
// FIXME remove on next major version
|
|
6533
|
-
scrollPosIntoView(pos) {
|
|
6534
|
-
this.dispatch({ effects: scrollTo.of(EditorSelection.cursor(pos)) });
|
|
6535
|
-
}
|
|
6536
6435
|
/**
|
|
6537
6436
|
Find the DOM parent node and offset (child offset if `node` is
|
|
6538
6437
|
an element, character offset when it is a text node) at the
|
|
@@ -6588,9 +6487,25 @@ class EditorView {
|
|
|
6588
6487
|
/**
|
|
6589
6488
|
The text direction
|
|
6590
6489
|
([`direction`](https://developer.mozilla.org/en-US/docs/Web/CSS/direction)
|
|
6591
|
-
CSS property) of the editor.
|
|
6490
|
+
CSS property) of the editor's content element.
|
|
6491
|
+
*/
|
|
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.
|
|
6592
6501
|
*/
|
|
6593
|
-
|
|
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
|
+
}
|
|
6594
6509
|
/**
|
|
6595
6510
|
Whether this editor [wraps lines](https://codemirror.net/6/docs/ref/#view.EditorView.lineWrapping)
|
|
6596
6511
|
(as determined by the
|
|
@@ -6609,11 +6524,11 @@ class EditorView {
|
|
|
6609
6524
|
bidiSpans(line) {
|
|
6610
6525
|
if (line.length > MaxBidiLine)
|
|
6611
6526
|
return trivialOrder(line.length);
|
|
6612
|
-
let dir = this.
|
|
6527
|
+
let dir = this.textDirectionAt(line.from);
|
|
6613
6528
|
for (let entry of this.bidiCache)
|
|
6614
6529
|
if (entry.from == line.from && entry.dir == dir)
|
|
6615
6530
|
return entry.order;
|
|
6616
|
-
let order = computeOrder(line.text,
|
|
6531
|
+
let order = computeOrder(line.text, dir);
|
|
6617
6532
|
this.bidiCache.push(new CachedOrder(line.from, line.to, dir, order));
|
|
6618
6533
|
return order;
|
|
6619
6534
|
}
|
|
@@ -6664,16 +6579,16 @@ class EditorView {
|
|
|
6664
6579
|
return scrollIntoView.of(new ScrollTarget(typeof pos == "number" ? EditorSelection.cursor(pos) : pos, options.y, options.x, options.yMargin, options.xMargin));
|
|
6665
6580
|
}
|
|
6666
6581
|
/**
|
|
6667
|
-
|
|
6668
|
-
should be an object mapping event names to handler
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
These are registered
|
|
6673
|
-
element](https://codemirror.net/6/docs/ref/#view.EditorView.contentDOM), except
|
|
6674
|
-
handlers, which will be called any time the
|
|
6675
|
-
element](https://codemirror.net/6/docs/ref/#view.EditorView.scrollDOM) or one of
|
|
6676
|
-
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.
|
|
6677
6592
|
*/
|
|
6678
6593
|
static domEventHandlers(handlers) {
|
|
6679
6594
|
return ViewPlugin.define(() => ({}), { eventHandlers: handlers });
|
|
@@ -6715,20 +6630,6 @@ class EditorView {
|
|
|
6715
6630
|
}
|
|
6716
6631
|
}
|
|
6717
6632
|
/**
|
|
6718
|
-
Effect that can be [added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a
|
|
6719
|
-
transaction to make it scroll the given range into view.
|
|
6720
|
-
|
|
6721
|
-
*Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead.
|
|
6722
|
-
*/
|
|
6723
|
-
EditorView.scrollTo = scrollTo;
|
|
6724
|
-
/**
|
|
6725
|
-
Effect that makes the editor scroll the given range to the
|
|
6726
|
-
center of the visible view.
|
|
6727
|
-
|
|
6728
|
-
*Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead.
|
|
6729
|
-
*/
|
|
6730
|
-
EditorView.centerOn = centerOn;
|
|
6731
|
-
/**
|
|
6732
6633
|
Facet to add a [style
|
|
6733
6634
|
module](https://github.com/marijnh/style-mod#documentation) to
|
|
6734
6635
|
an editor view. The view will ensure that the module is
|
|
@@ -6745,6 +6646,13 @@ called and the default behavior is prevented.
|
|
|
6745
6646
|
*/
|
|
6746
6647
|
EditorView.inputHandler = inputHandler;
|
|
6747
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
|
+
/**
|
|
6748
6656
|
Allows you to provide a function that should be called when the
|
|
6749
6657
|
library catches an exception from an extension (mostly from view
|
|
6750
6658
|
plugins, but may be used by other extensions to route exceptions
|
|
@@ -6760,9 +6668,9 @@ EditorView.updateListener = updateListener;
|
|
|
6760
6668
|
/**
|
|
6761
6669
|
Facet that controls whether the editor content DOM is editable.
|
|
6762
6670
|
When its highest-precedence value is `false`, the element will
|
|
6763
|
-
not
|
|
6764
|
-
|
|
6765
|
-
|
|
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
|
|
6766
6674
|
[`readOnly`](https://codemirror.net/6/docs/ref/#state.EditorState.readOnly) facet for that.)
|
|
6767
6675
|
*/
|
|
6768
6676
|
EditorView.editable = editable;
|
|
@@ -6781,18 +6689,45 @@ the drag should move the content.
|
|
|
6781
6689
|
*/
|
|
6782
6690
|
EditorView.dragMovesSelection = dragMovesSelection$1;
|
|
6783
6691
|
/**
|
|
6784
|
-
Facet used to configure whether a given selecting click adds
|
|
6785
|
-
|
|
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.
|
|
6786
6696
|
*/
|
|
6787
6697
|
EditorView.clickAddsSelectionRange = clickAddsSelectionRange;
|
|
6788
6698
|
/**
|
|
6789
6699
|
A facet that determines which [decorations](https://codemirror.net/6/docs/ref/#view.Decoration)
|
|
6790
|
-
are shown in the view.
|
|
6791
|
-
|
|
6792
|
-
|
|
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.
|
|
6793
6708
|
*/
|
|
6794
6709
|
EditorView.decorations = decorations;
|
|
6795
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
|
+
/**
|
|
6796
6731
|
This facet records whether a dark theme is active. The extension
|
|
6797
6732
|
returned by [`theme`](https://codemirror.net/6/docs/ref/#view.EditorView^theme) automatically
|
|
6798
6733
|
includes an instance of this when the `dark` option is set to
|
|
@@ -6825,10 +6760,6 @@ search match).
|
|
|
6825
6760
|
EditorView.announce = /*@__PURE__*/StateEffect.define();
|
|
6826
6761
|
// Maximum line length for which we compute accurate bidi info
|
|
6827
6762
|
const MaxBidiLine = 4096;
|
|
6828
|
-
// FIXME remove this and its callers on next breaking release
|
|
6829
|
-
function ensureTop(given, view) {
|
|
6830
|
-
return (given == null ? view.contentDOM.getBoundingClientRect().top : given) + view.viewState.paddingTop;
|
|
6831
|
-
}
|
|
6832
6763
|
const BadMeasure = {};
|
|
6833
6764
|
class CachedOrder {
|
|
6834
6765
|
constructor(from, to, dir, order) {
|
|
@@ -6931,7 +6862,7 @@ function getKeymap(state) {
|
|
|
6931
6862
|
}
|
|
6932
6863
|
/**
|
|
6933
6864
|
Run the key handlers registered for a given scope. The event
|
|
6934
|
-
object should be `"keydown"` event. Returns true if any of the
|
|
6865
|
+
object should be a `"keydown"` event. Returns true if any of the
|
|
6935
6866
|
handlers handled it.
|
|
6936
6867
|
*/
|
|
6937
6868
|
function runScopeHandlers(view, event, scope) {
|
|
@@ -7708,9 +7639,1297 @@ function placeholder(content) {
|
|
|
7708
7639
|
}, { decorations: v => v.decorations });
|
|
7709
7640
|
}
|
|
7710
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
|
+
return null;
|
|
8259
|
+
if (value && tr.docChanged) {
|
|
8260
|
+
let newPos = tr.changes.mapPos(value.pos, -1, MapMode.TrackDel);
|
|
8261
|
+
if (newPos == null)
|
|
8262
|
+
return null;
|
|
8263
|
+
let copy = Object.assign(Object.create(null), value);
|
|
8264
|
+
copy.pos = newPos;
|
|
8265
|
+
if (value.end != null)
|
|
8266
|
+
copy.end = tr.changes.mapPos(value.end);
|
|
8267
|
+
value = copy;
|
|
8268
|
+
}
|
|
8269
|
+
for (let effect of tr.effects) {
|
|
8270
|
+
if (effect.is(setHover))
|
|
8271
|
+
value = effect.value;
|
|
8272
|
+
if (effect.is(closeHoverTooltipEffect))
|
|
8273
|
+
value = null;
|
|
8274
|
+
}
|
|
8275
|
+
return value;
|
|
8276
|
+
},
|
|
8277
|
+
provide: f => showHoverTooltip.from(f)
|
|
8278
|
+
});
|
|
8279
|
+
return [
|
|
8280
|
+
hoverState,
|
|
8281
|
+
ViewPlugin.define(view => new HoverPlugin(view, source, hoverState, setHover, options.hoverTime || 300 /* Time */)),
|
|
8282
|
+
showHoverTooltipHost
|
|
8283
|
+
];
|
|
8284
|
+
}
|
|
8285
|
+
/**
|
|
8286
|
+
Get the active tooltip view for a given tooltip, if available.
|
|
8287
|
+
*/
|
|
8288
|
+
function getTooltip(view, tooltip) {
|
|
8289
|
+
let plugin = view.plugin(tooltipPlugin);
|
|
8290
|
+
if (!plugin)
|
|
8291
|
+
return null;
|
|
8292
|
+
let found = plugin.manager.tooltips.indexOf(tooltip);
|
|
8293
|
+
return found < 0 ? null : plugin.manager.tooltipViews[found];
|
|
8294
|
+
}
|
|
8295
|
+
/**
|
|
8296
|
+
Returns true if any hover tooltips are currently active.
|
|
8297
|
+
*/
|
|
8298
|
+
function hasHoverTooltips(state) {
|
|
8299
|
+
return state.facet(showHoverTooltip).some(x => x);
|
|
8300
|
+
}
|
|
8301
|
+
const closeHoverTooltipEffect = /*@__PURE__*/StateEffect.define();
|
|
8302
|
+
/**
|
|
8303
|
+
Transaction effect that closes all hover tooltips.
|
|
8304
|
+
*/
|
|
8305
|
+
const closeHoverTooltips = /*@__PURE__*/closeHoverTooltipEffect.of(null);
|
|
8306
|
+
/**
|
|
8307
|
+
Tell the tooltip extension to recompute the position of the active
|
|
8308
|
+
tooltips. This can be useful when something happens (such as a
|
|
8309
|
+
re-positioning or CSS change affecting the editor) that could
|
|
8310
|
+
invalidate the existing tooltip positions.
|
|
8311
|
+
*/
|
|
8312
|
+
function repositionTooltips(view) {
|
|
8313
|
+
var _a;
|
|
8314
|
+
(_a = view.plugin(tooltipPlugin)) === null || _a === void 0 ? void 0 : _a.maybeMeasure();
|
|
8315
|
+
}
|
|
8316
|
+
|
|
8317
|
+
const panelConfig = /*@__PURE__*/Facet.define({
|
|
8318
|
+
combine(configs) {
|
|
8319
|
+
let topContainer, bottomContainer;
|
|
8320
|
+
for (let c of configs) {
|
|
8321
|
+
topContainer = topContainer || c.topContainer;
|
|
8322
|
+
bottomContainer = bottomContainer || c.bottomContainer;
|
|
8323
|
+
}
|
|
8324
|
+
return { topContainer, bottomContainer };
|
|
8325
|
+
}
|
|
8326
|
+
});
|
|
8327
|
+
/**
|
|
8328
|
+
Configures the panel-managing extension.
|
|
8329
|
+
*/
|
|
8330
|
+
function panels(config) {
|
|
8331
|
+
return config ? [panelConfig.of(config)] : [];
|
|
8332
|
+
}
|
|
8333
|
+
/**
|
|
8334
|
+
Get the active panel created by the given constructor, if any.
|
|
8335
|
+
This can be useful when you need access to your panels' DOM
|
|
8336
|
+
structure.
|
|
8337
|
+
*/
|
|
8338
|
+
function getPanel(view, panel) {
|
|
8339
|
+
let plugin = view.plugin(panelPlugin);
|
|
8340
|
+
let index = plugin ? plugin.specs.indexOf(panel) : -1;
|
|
8341
|
+
return index > -1 ? plugin.panels[index] : null;
|
|
8342
|
+
}
|
|
8343
|
+
const panelPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
|
|
8344
|
+
constructor(view) {
|
|
8345
|
+
this.input = view.state.facet(showPanel);
|
|
8346
|
+
this.specs = this.input.filter(s => s);
|
|
8347
|
+
this.panels = this.specs.map(spec => spec(view));
|
|
8348
|
+
let conf = view.state.facet(panelConfig);
|
|
8349
|
+
this.top = new PanelGroup(view, true, conf.topContainer);
|
|
8350
|
+
this.bottom = new PanelGroup(view, false, conf.bottomContainer);
|
|
8351
|
+
this.top.sync(this.panels.filter(p => p.top));
|
|
8352
|
+
this.bottom.sync(this.panels.filter(p => !p.top));
|
|
8353
|
+
for (let p of this.panels) {
|
|
8354
|
+
p.dom.classList.add("cm-panel");
|
|
8355
|
+
if (p.mount)
|
|
8356
|
+
p.mount();
|
|
8357
|
+
}
|
|
8358
|
+
}
|
|
8359
|
+
update(update) {
|
|
8360
|
+
let conf = update.state.facet(panelConfig);
|
|
8361
|
+
if (this.top.container != conf.topContainer) {
|
|
8362
|
+
this.top.sync([]);
|
|
8363
|
+
this.top = new PanelGroup(update.view, true, conf.topContainer);
|
|
8364
|
+
}
|
|
8365
|
+
if (this.bottom.container != conf.bottomContainer) {
|
|
8366
|
+
this.bottom.sync([]);
|
|
8367
|
+
this.bottom = new PanelGroup(update.view, false, conf.bottomContainer);
|
|
8368
|
+
}
|
|
8369
|
+
this.top.syncClasses();
|
|
8370
|
+
this.bottom.syncClasses();
|
|
8371
|
+
let input = update.state.facet(showPanel);
|
|
8372
|
+
if (input != this.input) {
|
|
8373
|
+
let specs = input.filter(x => x);
|
|
8374
|
+
let panels = [], top = [], bottom = [], mount = [];
|
|
8375
|
+
for (let spec of specs) {
|
|
8376
|
+
let known = this.specs.indexOf(spec), panel;
|
|
8377
|
+
if (known < 0) {
|
|
8378
|
+
panel = spec(update.view);
|
|
8379
|
+
mount.push(panel);
|
|
8380
|
+
}
|
|
8381
|
+
else {
|
|
8382
|
+
panel = this.panels[known];
|
|
8383
|
+
if (panel.update)
|
|
8384
|
+
panel.update(update);
|
|
8385
|
+
}
|
|
8386
|
+
panels.push(panel);
|
|
8387
|
+
(panel.top ? top : bottom).push(panel);
|
|
8388
|
+
}
|
|
8389
|
+
this.specs = specs;
|
|
8390
|
+
this.panels = panels;
|
|
8391
|
+
this.top.sync(top);
|
|
8392
|
+
this.bottom.sync(bottom);
|
|
8393
|
+
for (let p of mount) {
|
|
8394
|
+
p.dom.classList.add("cm-panel");
|
|
8395
|
+
if (p.mount)
|
|
8396
|
+
p.mount();
|
|
8397
|
+
}
|
|
8398
|
+
}
|
|
8399
|
+
else {
|
|
8400
|
+
for (let p of this.panels)
|
|
8401
|
+
if (p.update)
|
|
8402
|
+
p.update(update);
|
|
8403
|
+
}
|
|
8404
|
+
}
|
|
8405
|
+
destroy() {
|
|
8406
|
+
this.top.sync([]);
|
|
8407
|
+
this.bottom.sync([]);
|
|
8408
|
+
}
|
|
8409
|
+
}, {
|
|
8410
|
+
provide: plugin => EditorView.scrollMargins.of(view => {
|
|
8411
|
+
let value = view.plugin(plugin);
|
|
8412
|
+
return value && { top: value.top.scrollMargin(), bottom: value.bottom.scrollMargin() };
|
|
8413
|
+
})
|
|
8414
|
+
});
|
|
8415
|
+
class PanelGroup {
|
|
8416
|
+
constructor(view, top, container) {
|
|
8417
|
+
this.view = view;
|
|
8418
|
+
this.top = top;
|
|
8419
|
+
this.container = container;
|
|
8420
|
+
this.dom = undefined;
|
|
8421
|
+
this.classes = "";
|
|
8422
|
+
this.panels = [];
|
|
8423
|
+
this.syncClasses();
|
|
8424
|
+
}
|
|
8425
|
+
sync(panels) {
|
|
8426
|
+
for (let p of this.panels)
|
|
8427
|
+
if (p.destroy && panels.indexOf(p) < 0)
|
|
8428
|
+
p.destroy();
|
|
8429
|
+
this.panels = panels;
|
|
8430
|
+
this.syncDOM();
|
|
8431
|
+
}
|
|
8432
|
+
syncDOM() {
|
|
8433
|
+
if (this.panels.length == 0) {
|
|
8434
|
+
if (this.dom) {
|
|
8435
|
+
this.dom.remove();
|
|
8436
|
+
this.dom = undefined;
|
|
8437
|
+
}
|
|
8438
|
+
return;
|
|
8439
|
+
}
|
|
8440
|
+
if (!this.dom) {
|
|
8441
|
+
this.dom = document.createElement("div");
|
|
8442
|
+
this.dom.className = this.top ? "cm-panels cm-panels-top" : "cm-panels cm-panels-bottom";
|
|
8443
|
+
this.dom.style[this.top ? "top" : "bottom"] = "0";
|
|
8444
|
+
let parent = this.container || this.view.dom;
|
|
8445
|
+
parent.insertBefore(this.dom, this.top ? parent.firstChild : null);
|
|
8446
|
+
}
|
|
8447
|
+
let curDOM = this.dom.firstChild;
|
|
8448
|
+
for (let panel of this.panels) {
|
|
8449
|
+
if (panel.dom.parentNode == this.dom) {
|
|
8450
|
+
while (curDOM != panel.dom)
|
|
8451
|
+
curDOM = rm(curDOM);
|
|
8452
|
+
curDOM = curDOM.nextSibling;
|
|
8453
|
+
}
|
|
8454
|
+
else {
|
|
8455
|
+
this.dom.insertBefore(panel.dom, curDOM);
|
|
8456
|
+
}
|
|
8457
|
+
}
|
|
8458
|
+
while (curDOM)
|
|
8459
|
+
curDOM = rm(curDOM);
|
|
8460
|
+
}
|
|
8461
|
+
scrollMargin() {
|
|
8462
|
+
return !this.dom || this.container ? 0
|
|
8463
|
+
: Math.max(0, this.top ?
|
|
8464
|
+
this.dom.getBoundingClientRect().bottom - Math.max(0, this.view.scrollDOM.getBoundingClientRect().top) :
|
|
8465
|
+
Math.min(innerHeight, this.view.scrollDOM.getBoundingClientRect().bottom) - this.dom.getBoundingClientRect().top);
|
|
8466
|
+
}
|
|
8467
|
+
syncClasses() {
|
|
8468
|
+
if (!this.container || this.classes == this.view.themeClasses)
|
|
8469
|
+
return;
|
|
8470
|
+
for (let cls of this.classes.split(" "))
|
|
8471
|
+
if (cls)
|
|
8472
|
+
this.container.classList.remove(cls);
|
|
8473
|
+
for (let cls of (this.classes = this.view.themeClasses).split(" "))
|
|
8474
|
+
if (cls)
|
|
8475
|
+
this.container.classList.add(cls);
|
|
8476
|
+
}
|
|
8477
|
+
}
|
|
8478
|
+
function rm(node) {
|
|
8479
|
+
let next = node.nextSibling;
|
|
8480
|
+
node.remove();
|
|
8481
|
+
return next;
|
|
8482
|
+
}
|
|
8483
|
+
/**
|
|
8484
|
+
Opening a panel is done by providing a constructor function for
|
|
8485
|
+
the panel through this facet. (The panel is closed again when its
|
|
8486
|
+
constructor is no longer provided.) Values of `null` are ignored.
|
|
8487
|
+
*/
|
|
8488
|
+
const showPanel = /*@__PURE__*/Facet.define({
|
|
8489
|
+
enables: panelPlugin
|
|
8490
|
+
});
|
|
8491
|
+
|
|
8492
|
+
/**
|
|
8493
|
+
A gutter marker represents a bit of information attached to a line
|
|
8494
|
+
in a specific gutter. Your own custom markers have to extend this
|
|
8495
|
+
class.
|
|
8496
|
+
*/
|
|
8497
|
+
class GutterMarker extends RangeValue {
|
|
8498
|
+
/**
|
|
8499
|
+
@internal
|
|
8500
|
+
*/
|
|
8501
|
+
compare(other) {
|
|
8502
|
+
return this == other || this.constructor == other.constructor && this.eq(other);
|
|
8503
|
+
}
|
|
8504
|
+
/**
|
|
8505
|
+
Compare this marker to another marker of the same type.
|
|
8506
|
+
*/
|
|
8507
|
+
eq(other) { return false; }
|
|
8508
|
+
/**
|
|
8509
|
+
Called if the marker has a `toDOM` method and its representation
|
|
8510
|
+
was removed from a gutter.
|
|
8511
|
+
*/
|
|
8512
|
+
destroy(dom) { }
|
|
8513
|
+
}
|
|
8514
|
+
GutterMarker.prototype.elementClass = "";
|
|
8515
|
+
GutterMarker.prototype.toDOM = undefined;
|
|
8516
|
+
GutterMarker.prototype.mapMode = MapMode.TrackBefore;
|
|
8517
|
+
GutterMarker.prototype.startSide = GutterMarker.prototype.endSide = -1;
|
|
8518
|
+
GutterMarker.prototype.point = true;
|
|
8519
|
+
/**
|
|
8520
|
+
Facet used to add a class to all gutter elements for a given line.
|
|
8521
|
+
Markers given to this facet should _only_ define an
|
|
8522
|
+
[`elementclass`](https://codemirror.net/6/docs/ref/#view.GutterMarker.elementClass), not a
|
|
8523
|
+
[`toDOM`](https://codemirror.net/6/docs/ref/#view.GutterMarker.toDOM) (or the marker will appear
|
|
8524
|
+
in all gutters for the line).
|
|
8525
|
+
*/
|
|
8526
|
+
const gutterLineClass = /*@__PURE__*/Facet.define();
|
|
8527
|
+
const defaults = {
|
|
8528
|
+
class: "",
|
|
8529
|
+
renderEmptyElements: false,
|
|
8530
|
+
elementStyle: "",
|
|
8531
|
+
markers: () => RangeSet.empty,
|
|
8532
|
+
lineMarker: () => null,
|
|
8533
|
+
lineMarkerChange: null,
|
|
8534
|
+
initialSpacer: null,
|
|
8535
|
+
updateSpacer: null,
|
|
8536
|
+
domEventHandlers: {}
|
|
8537
|
+
};
|
|
8538
|
+
const activeGutters = /*@__PURE__*/Facet.define();
|
|
8539
|
+
/**
|
|
8540
|
+
Define an editor gutter. The order in which the gutters appear is
|
|
8541
|
+
determined by their extension priority.
|
|
8542
|
+
*/
|
|
8543
|
+
function gutter(config) {
|
|
8544
|
+
return [gutters(), activeGutters.of(Object.assign(Object.assign({}, defaults), config))];
|
|
8545
|
+
}
|
|
8546
|
+
const unfixGutters = /*@__PURE__*/Facet.define({
|
|
8547
|
+
combine: values => values.some(x => x)
|
|
8548
|
+
});
|
|
8549
|
+
/**
|
|
8550
|
+
The gutter-drawing plugin is automatically enabled when you add a
|
|
8551
|
+
gutter, but you can use this function to explicitly configure it.
|
|
8552
|
+
|
|
8553
|
+
Unless `fixed` is explicitly set to `false`, the gutters are
|
|
8554
|
+
fixed, meaning they don't scroll along with the content
|
|
8555
|
+
horizontally (except on Internet Explorer, which doesn't support
|
|
8556
|
+
CSS [`position:
|
|
8557
|
+
sticky`](https://developer.mozilla.org/en-US/docs/Web/CSS/position#sticky)).
|
|
8558
|
+
*/
|
|
8559
|
+
function gutters(config) {
|
|
8560
|
+
let result = [
|
|
8561
|
+
gutterView,
|
|
8562
|
+
];
|
|
8563
|
+
if (config && config.fixed === false)
|
|
8564
|
+
result.push(unfixGutters.of(true));
|
|
8565
|
+
return result;
|
|
8566
|
+
}
|
|
8567
|
+
const gutterView = /*@__PURE__*/ViewPlugin.fromClass(class {
|
|
8568
|
+
constructor(view) {
|
|
8569
|
+
this.view = view;
|
|
8570
|
+
this.prevViewport = view.viewport;
|
|
8571
|
+
this.dom = document.createElement("div");
|
|
8572
|
+
this.dom.className = "cm-gutters";
|
|
8573
|
+
this.dom.setAttribute("aria-hidden", "true");
|
|
8574
|
+
this.dom.style.minHeight = this.view.contentHeight + "px";
|
|
8575
|
+
this.gutters = view.state.facet(activeGutters).map(conf => new SingleGutterView(view, conf));
|
|
8576
|
+
for (let gutter of this.gutters)
|
|
8577
|
+
this.dom.appendChild(gutter.dom);
|
|
8578
|
+
this.fixed = !view.state.facet(unfixGutters);
|
|
8579
|
+
if (this.fixed) {
|
|
8580
|
+
// FIXME IE11 fallback, which doesn't support position: sticky,
|
|
8581
|
+
// by using position: relative + event handlers that realign the
|
|
8582
|
+
// gutter (or just force fixed=false on IE11?)
|
|
8583
|
+
this.dom.style.position = "sticky";
|
|
8584
|
+
}
|
|
8585
|
+
this.syncGutters(false);
|
|
8586
|
+
view.scrollDOM.insertBefore(this.dom, view.contentDOM);
|
|
8587
|
+
}
|
|
8588
|
+
update(update) {
|
|
8589
|
+
if (this.updateGutters(update)) {
|
|
8590
|
+
// Detach during sync when the viewport changed significantly
|
|
8591
|
+
// (such as during scrolling), since for large updates that is
|
|
8592
|
+
// faster.
|
|
8593
|
+
let vpA = this.prevViewport, vpB = update.view.viewport;
|
|
8594
|
+
let vpOverlap = Math.min(vpA.to, vpB.to) - Math.max(vpA.from, vpB.from);
|
|
8595
|
+
this.syncGutters(vpOverlap < (vpB.to - vpB.from) * 0.8);
|
|
8596
|
+
}
|
|
8597
|
+
if (update.geometryChanged)
|
|
8598
|
+
this.dom.style.minHeight = this.view.contentHeight + "px";
|
|
8599
|
+
if (this.view.state.facet(unfixGutters) != !this.fixed) {
|
|
8600
|
+
this.fixed = !this.fixed;
|
|
8601
|
+
this.dom.style.position = this.fixed ? "sticky" : "";
|
|
8602
|
+
}
|
|
8603
|
+
this.prevViewport = update.view.viewport;
|
|
8604
|
+
}
|
|
8605
|
+
syncGutters(detach) {
|
|
8606
|
+
let after = this.dom.nextSibling;
|
|
8607
|
+
if (detach)
|
|
8608
|
+
this.dom.remove();
|
|
8609
|
+
let lineClasses = RangeSet.iter(this.view.state.facet(gutterLineClass), this.view.viewport.from);
|
|
8610
|
+
let classSet = [];
|
|
8611
|
+
let contexts = this.gutters.map(gutter => new UpdateContext(gutter, this.view.viewport, -this.view.documentPadding.top));
|
|
8612
|
+
for (let line of this.view.viewportLineBlocks) {
|
|
8613
|
+
let text;
|
|
8614
|
+
if (Array.isArray(line.type)) {
|
|
8615
|
+
for (let b of line.type)
|
|
8616
|
+
if (b.type == BlockType.Text) {
|
|
8617
|
+
text = b;
|
|
8618
|
+
break;
|
|
8619
|
+
}
|
|
8620
|
+
}
|
|
8621
|
+
else {
|
|
8622
|
+
text = line.type == BlockType.Text ? line : undefined;
|
|
8623
|
+
}
|
|
8624
|
+
if (!text)
|
|
8625
|
+
continue;
|
|
8626
|
+
if (classSet.length)
|
|
8627
|
+
classSet = [];
|
|
8628
|
+
advanceCursor(lineClasses, classSet, line.from);
|
|
8629
|
+
for (let cx of contexts)
|
|
8630
|
+
cx.line(this.view, text, classSet);
|
|
8631
|
+
}
|
|
8632
|
+
for (let cx of contexts)
|
|
8633
|
+
cx.finish();
|
|
8634
|
+
if (detach)
|
|
8635
|
+
this.view.scrollDOM.insertBefore(this.dom, after);
|
|
8636
|
+
}
|
|
8637
|
+
updateGutters(update) {
|
|
8638
|
+
let prev = update.startState.facet(activeGutters), cur = update.state.facet(activeGutters);
|
|
8639
|
+
let change = update.docChanged || update.heightChanged || update.viewportChanged ||
|
|
8640
|
+
!RangeSet.eq(update.startState.facet(gutterLineClass), update.state.facet(gutterLineClass), update.view.viewport.from, update.view.viewport.to);
|
|
8641
|
+
if (prev == cur) {
|
|
8642
|
+
for (let gutter of this.gutters)
|
|
8643
|
+
if (gutter.update(update))
|
|
8644
|
+
change = true;
|
|
8645
|
+
}
|
|
8646
|
+
else {
|
|
8647
|
+
change = true;
|
|
8648
|
+
let gutters = [];
|
|
8649
|
+
for (let conf of cur) {
|
|
8650
|
+
let known = prev.indexOf(conf);
|
|
8651
|
+
if (known < 0) {
|
|
8652
|
+
gutters.push(new SingleGutterView(this.view, conf));
|
|
8653
|
+
}
|
|
8654
|
+
else {
|
|
8655
|
+
this.gutters[known].update(update);
|
|
8656
|
+
gutters.push(this.gutters[known]);
|
|
8657
|
+
}
|
|
8658
|
+
}
|
|
8659
|
+
for (let g of this.gutters) {
|
|
8660
|
+
g.dom.remove();
|
|
8661
|
+
if (gutters.indexOf(g) < 0)
|
|
8662
|
+
g.destroy();
|
|
8663
|
+
}
|
|
8664
|
+
for (let g of gutters)
|
|
8665
|
+
this.dom.appendChild(g.dom);
|
|
8666
|
+
this.gutters = gutters;
|
|
8667
|
+
}
|
|
8668
|
+
return change;
|
|
8669
|
+
}
|
|
8670
|
+
destroy() {
|
|
8671
|
+
for (let view of this.gutters)
|
|
8672
|
+
view.destroy();
|
|
8673
|
+
this.dom.remove();
|
|
8674
|
+
}
|
|
8675
|
+
}, {
|
|
8676
|
+
provide: plugin => EditorView.scrollMargins.of(view => {
|
|
8677
|
+
let value = view.plugin(plugin);
|
|
8678
|
+
if (!value || value.gutters.length == 0 || !value.fixed)
|
|
8679
|
+
return null;
|
|
8680
|
+
return view.textDirection == Direction.LTR ? { left: value.dom.offsetWidth } : { right: value.dom.offsetWidth };
|
|
8681
|
+
})
|
|
8682
|
+
});
|
|
8683
|
+
function asArray(val) { return (Array.isArray(val) ? val : [val]); }
|
|
8684
|
+
function advanceCursor(cursor, collect, pos) {
|
|
8685
|
+
while (cursor.value && cursor.from <= pos) {
|
|
8686
|
+
if (cursor.from == pos)
|
|
8687
|
+
collect.push(cursor.value);
|
|
8688
|
+
cursor.next();
|
|
8689
|
+
}
|
|
8690
|
+
}
|
|
8691
|
+
class UpdateContext {
|
|
8692
|
+
constructor(gutter, viewport, height) {
|
|
8693
|
+
this.gutter = gutter;
|
|
8694
|
+
this.height = height;
|
|
8695
|
+
this.localMarkers = [];
|
|
8696
|
+
this.i = 0;
|
|
8697
|
+
this.cursor = RangeSet.iter(gutter.markers, viewport.from);
|
|
8698
|
+
}
|
|
8699
|
+
line(view, line, extraMarkers) {
|
|
8700
|
+
if (this.localMarkers.length)
|
|
8701
|
+
this.localMarkers = [];
|
|
8702
|
+
advanceCursor(this.cursor, this.localMarkers, line.from);
|
|
8703
|
+
let localMarkers = extraMarkers.length ? this.localMarkers.concat(extraMarkers) : this.localMarkers;
|
|
8704
|
+
let forLine = this.gutter.config.lineMarker(view, line, localMarkers);
|
|
8705
|
+
if (forLine)
|
|
8706
|
+
localMarkers.unshift(forLine);
|
|
8707
|
+
let gutter = this.gutter;
|
|
8708
|
+
if (localMarkers.length == 0 && !gutter.config.renderEmptyElements)
|
|
8709
|
+
return;
|
|
8710
|
+
let above = line.top - this.height;
|
|
8711
|
+
if (this.i == gutter.elements.length) {
|
|
8712
|
+
let newElt = new GutterElement(view, line.height, above, localMarkers);
|
|
8713
|
+
gutter.elements.push(newElt);
|
|
8714
|
+
gutter.dom.appendChild(newElt.dom);
|
|
8715
|
+
}
|
|
8716
|
+
else {
|
|
8717
|
+
gutter.elements[this.i].update(view, line.height, above, localMarkers);
|
|
8718
|
+
}
|
|
8719
|
+
this.height = line.bottom;
|
|
8720
|
+
this.i++;
|
|
8721
|
+
}
|
|
8722
|
+
finish() {
|
|
8723
|
+
let gutter = this.gutter;
|
|
8724
|
+
while (gutter.elements.length > this.i) {
|
|
8725
|
+
let last = gutter.elements.pop();
|
|
8726
|
+
gutter.dom.removeChild(last.dom);
|
|
8727
|
+
last.destroy();
|
|
8728
|
+
}
|
|
8729
|
+
}
|
|
8730
|
+
}
|
|
8731
|
+
class SingleGutterView {
|
|
8732
|
+
constructor(view, config) {
|
|
8733
|
+
this.view = view;
|
|
8734
|
+
this.config = config;
|
|
8735
|
+
this.elements = [];
|
|
8736
|
+
this.spacer = null;
|
|
8737
|
+
this.dom = document.createElement("div");
|
|
8738
|
+
this.dom.className = "cm-gutter" + (this.config.class ? " " + this.config.class : "");
|
|
8739
|
+
for (let prop in config.domEventHandlers) {
|
|
8740
|
+
this.dom.addEventListener(prop, (event) => {
|
|
8741
|
+
let line = view.lineBlockAtHeight(event.clientY - view.documentTop);
|
|
8742
|
+
if (config.domEventHandlers[prop](view, line, event))
|
|
8743
|
+
event.preventDefault();
|
|
8744
|
+
});
|
|
8745
|
+
}
|
|
8746
|
+
this.markers = asArray(config.markers(view));
|
|
8747
|
+
if (config.initialSpacer) {
|
|
8748
|
+
this.spacer = new GutterElement(view, 0, 0, [config.initialSpacer(view)]);
|
|
8749
|
+
this.dom.appendChild(this.spacer.dom);
|
|
8750
|
+
this.spacer.dom.style.cssText += "visibility: hidden; pointer-events: none";
|
|
8751
|
+
}
|
|
8752
|
+
}
|
|
8753
|
+
update(update) {
|
|
8754
|
+
let prevMarkers = this.markers;
|
|
8755
|
+
this.markers = asArray(this.config.markers(update.view));
|
|
8756
|
+
if (this.spacer && this.config.updateSpacer) {
|
|
8757
|
+
let updated = this.config.updateSpacer(this.spacer.markers[0], update);
|
|
8758
|
+
if (updated != this.spacer.markers[0])
|
|
8759
|
+
this.spacer.update(update.view, 0, 0, [updated]);
|
|
8760
|
+
}
|
|
8761
|
+
let vp = update.view.viewport;
|
|
8762
|
+
return !RangeSet.eq(this.markers, prevMarkers, vp.from, vp.to) ||
|
|
8763
|
+
(this.config.lineMarkerChange ? this.config.lineMarkerChange(update) : false);
|
|
8764
|
+
}
|
|
8765
|
+
destroy() {
|
|
8766
|
+
for (let elt of this.elements)
|
|
8767
|
+
elt.destroy();
|
|
8768
|
+
}
|
|
8769
|
+
}
|
|
8770
|
+
class GutterElement {
|
|
8771
|
+
constructor(view, height, above, markers) {
|
|
8772
|
+
this.height = -1;
|
|
8773
|
+
this.above = 0;
|
|
8774
|
+
this.markers = [];
|
|
8775
|
+
this.dom = document.createElement("div");
|
|
8776
|
+
this.update(view, height, above, markers);
|
|
8777
|
+
}
|
|
8778
|
+
update(view, height, above, markers) {
|
|
8779
|
+
if (this.height != height)
|
|
8780
|
+
this.dom.style.height = (this.height = height) + "px";
|
|
8781
|
+
if (this.above != above)
|
|
8782
|
+
this.dom.style.marginTop = (this.above = above) ? above + "px" : "";
|
|
8783
|
+
if (!sameMarkers(this.markers, markers))
|
|
8784
|
+
this.setMarkers(view, markers);
|
|
8785
|
+
}
|
|
8786
|
+
setMarkers(view, markers) {
|
|
8787
|
+
let cls = "cm-gutterElement", domPos = this.dom.firstChild;
|
|
8788
|
+
for (let iNew = 0, iOld = 0;;) {
|
|
8789
|
+
let skipTo = iOld, marker = iNew < markers.length ? markers[iNew++] : null, matched = false;
|
|
8790
|
+
if (marker) {
|
|
8791
|
+
let c = marker.elementClass;
|
|
8792
|
+
if (c)
|
|
8793
|
+
cls += " " + c;
|
|
8794
|
+
for (let i = iOld; i < this.markers.length; i++)
|
|
8795
|
+
if (this.markers[i].compare(marker)) {
|
|
8796
|
+
skipTo = i;
|
|
8797
|
+
matched = true;
|
|
8798
|
+
break;
|
|
8799
|
+
}
|
|
8800
|
+
}
|
|
8801
|
+
else {
|
|
8802
|
+
skipTo = this.markers.length;
|
|
8803
|
+
}
|
|
8804
|
+
while (iOld < skipTo) {
|
|
8805
|
+
let next = this.markers[iOld++];
|
|
8806
|
+
if (next.toDOM) {
|
|
8807
|
+
next.destroy(domPos);
|
|
8808
|
+
let after = domPos.nextSibling;
|
|
8809
|
+
domPos.remove();
|
|
8810
|
+
domPos = after;
|
|
8811
|
+
}
|
|
8812
|
+
}
|
|
8813
|
+
if (!marker)
|
|
8814
|
+
break;
|
|
8815
|
+
if (marker.toDOM) {
|
|
8816
|
+
if (matched)
|
|
8817
|
+
domPos = domPos.nextSibling;
|
|
8818
|
+
else
|
|
8819
|
+
this.dom.insertBefore(marker.toDOM(view), domPos);
|
|
8820
|
+
}
|
|
8821
|
+
if (matched)
|
|
8822
|
+
iOld++;
|
|
8823
|
+
}
|
|
8824
|
+
this.dom.className = cls;
|
|
8825
|
+
this.markers = markers;
|
|
8826
|
+
}
|
|
8827
|
+
destroy() {
|
|
8828
|
+
this.setMarkers(null, []); // First argument not used unless creating markers
|
|
8829
|
+
}
|
|
8830
|
+
}
|
|
8831
|
+
function sameMarkers(a, b) {
|
|
8832
|
+
if (a.length != b.length)
|
|
8833
|
+
return false;
|
|
8834
|
+
for (let i = 0; i < a.length; i++)
|
|
8835
|
+
if (!a[i].compare(b[i]))
|
|
8836
|
+
return false;
|
|
8837
|
+
return true;
|
|
8838
|
+
}
|
|
8839
|
+
/**
|
|
8840
|
+
Facet used to provide markers to the line number gutter.
|
|
8841
|
+
*/
|
|
8842
|
+
const lineNumberMarkers = /*@__PURE__*/Facet.define();
|
|
8843
|
+
const lineNumberConfig = /*@__PURE__*/Facet.define({
|
|
8844
|
+
combine(values) {
|
|
8845
|
+
return combineConfig(values, { formatNumber: String, domEventHandlers: {} }, {
|
|
8846
|
+
domEventHandlers(a, b) {
|
|
8847
|
+
let result = Object.assign({}, a);
|
|
8848
|
+
for (let event in b) {
|
|
8849
|
+
let exists = result[event], add = b[event];
|
|
8850
|
+
result[event] = exists ? (view, line, event) => exists(view, line, event) || add(view, line, event) : add;
|
|
8851
|
+
}
|
|
8852
|
+
return result;
|
|
8853
|
+
}
|
|
8854
|
+
});
|
|
8855
|
+
}
|
|
8856
|
+
});
|
|
8857
|
+
class NumberMarker extends GutterMarker {
|
|
8858
|
+
constructor(number) {
|
|
8859
|
+
super();
|
|
8860
|
+
this.number = number;
|
|
8861
|
+
}
|
|
8862
|
+
eq(other) { return this.number == other.number; }
|
|
8863
|
+
toDOM() { return document.createTextNode(this.number); }
|
|
8864
|
+
}
|
|
8865
|
+
function formatNumber(view, number) {
|
|
8866
|
+
return view.state.facet(lineNumberConfig).formatNumber(number, view.state);
|
|
8867
|
+
}
|
|
8868
|
+
const lineNumberGutter = /*@__PURE__*/activeGutters.compute([lineNumberConfig], state => ({
|
|
8869
|
+
class: "cm-lineNumbers",
|
|
8870
|
+
renderEmptyElements: false,
|
|
8871
|
+
markers(view) { return view.state.facet(lineNumberMarkers); },
|
|
8872
|
+
lineMarker(view, line, others) {
|
|
8873
|
+
if (others.some(m => m.toDOM))
|
|
8874
|
+
return null;
|
|
8875
|
+
return new NumberMarker(formatNumber(view, view.state.doc.lineAt(line.from).number));
|
|
8876
|
+
},
|
|
8877
|
+
lineMarkerChange: update => update.startState.facet(lineNumberConfig) != update.state.facet(lineNumberConfig),
|
|
8878
|
+
initialSpacer(view) {
|
|
8879
|
+
return new NumberMarker(formatNumber(view, maxLineNumber(view.state.doc.lines)));
|
|
8880
|
+
},
|
|
8881
|
+
updateSpacer(spacer, update) {
|
|
8882
|
+
let max = formatNumber(update.view, maxLineNumber(update.view.state.doc.lines));
|
|
8883
|
+
return max == spacer.number ? spacer : new NumberMarker(max);
|
|
8884
|
+
},
|
|
8885
|
+
domEventHandlers: state.facet(lineNumberConfig).domEventHandlers
|
|
8886
|
+
}));
|
|
8887
|
+
/**
|
|
8888
|
+
Create a line number gutter extension.
|
|
8889
|
+
*/
|
|
8890
|
+
function lineNumbers(config = {}) {
|
|
8891
|
+
return [
|
|
8892
|
+
lineNumberConfig.of(config),
|
|
8893
|
+
gutters(),
|
|
8894
|
+
lineNumberGutter
|
|
8895
|
+
];
|
|
8896
|
+
}
|
|
8897
|
+
function maxLineNumber(lines) {
|
|
8898
|
+
let last = 9;
|
|
8899
|
+
while (last < lines)
|
|
8900
|
+
last = last * 10 + 9;
|
|
8901
|
+
return last;
|
|
8902
|
+
}
|
|
8903
|
+
const activeLineGutterMarker = /*@__PURE__*/new class extends GutterMarker {
|
|
8904
|
+
constructor() {
|
|
8905
|
+
super(...arguments);
|
|
8906
|
+
this.elementClass = "cm-activeLineGutter";
|
|
8907
|
+
}
|
|
8908
|
+
};
|
|
8909
|
+
const activeLineGutterHighlighter = /*@__PURE__*/gutterLineClass.compute(["selection"], state => {
|
|
8910
|
+
let marks = [], last = -1;
|
|
8911
|
+
for (let range of state.selection.ranges)
|
|
8912
|
+
if (range.empty) {
|
|
8913
|
+
let linePos = state.doc.lineAt(range.head).from;
|
|
8914
|
+
if (linePos > last) {
|
|
8915
|
+
last = linePos;
|
|
8916
|
+
marks.push(activeLineGutterMarker.range(linePos));
|
|
8917
|
+
}
|
|
8918
|
+
}
|
|
8919
|
+
return RangeSet.of(marks);
|
|
8920
|
+
});
|
|
8921
|
+
/**
|
|
8922
|
+
Returns an extension that adds a `cm-activeLineGutter` class to
|
|
8923
|
+
all gutter elements on the [active
|
|
8924
|
+
line](https://codemirror.net/6/docs/ref/#view.highlightActiveLine).
|
|
8925
|
+
*/
|
|
8926
|
+
function highlightActiveLineGutter() {
|
|
8927
|
+
return activeLineGutterHighlighter;
|
|
8928
|
+
}
|
|
8929
|
+
|
|
7711
8930
|
/**
|
|
7712
8931
|
@internal
|
|
7713
8932
|
*/
|
|
7714
8933
|
const __test = { HeightMap, HeightOracle, MeasuredHeights, QueryType, ChangedRange, computeOrder, moveVisually };
|
|
7715
8934
|
|
|
7716
|
-
export { BidiSpan, BlockInfo, BlockType, Decoration, Direction, EditorView,
|
|
8935
|
+
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 };
|