@codemirror/view 0.19.48 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/dist/index.cjs +1603 -369
- package/dist/index.d.ts +535 -249
- package/dist/index.js +1564 -344
- package/package.json +2 -4
package/dist/index.cjs
CHANGED
|
@@ -4,8 +4,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var state = require('@codemirror/state');
|
|
6
6
|
var styleMod = require('style-mod');
|
|
7
|
-
var rangeset = require('@codemirror/rangeset');
|
|
8
|
-
var text = require('@codemirror/text');
|
|
9
7
|
var w3cKeyname = require('w3c-keyname');
|
|
10
8
|
|
|
11
9
|
function getSelection(root) {
|
|
@@ -334,7 +332,7 @@ class ContentView {
|
|
|
334
332
|
track.written = true;
|
|
335
333
|
if (child.dom.parentNode == parent) {
|
|
336
334
|
while (next && next != child.dom)
|
|
337
|
-
next = rm(next);
|
|
335
|
+
next = rm$1(next);
|
|
338
336
|
}
|
|
339
337
|
else {
|
|
340
338
|
parent.insertBefore(child.dom, next);
|
|
@@ -345,7 +343,7 @@ class ContentView {
|
|
|
345
343
|
if (next && track && track.node == parent)
|
|
346
344
|
track.written = true;
|
|
347
345
|
while (next)
|
|
348
|
-
next = rm(next);
|
|
346
|
+
next = rm$1(next);
|
|
349
347
|
}
|
|
350
348
|
else if (this.dirty & 1 /* Child */) {
|
|
351
349
|
for (let child of this.children)
|
|
@@ -491,7 +489,7 @@ class ContentView {
|
|
|
491
489
|
}
|
|
492
490
|
ContentView.prototype.breakAfter = 0;
|
|
493
491
|
// Remove a DOM node and return its next sibling.
|
|
494
|
-
function rm(dom) {
|
|
492
|
+
function rm$1(dom) {
|
|
495
493
|
let next = dom.nextSibling;
|
|
496
494
|
dom.parentNode.removeChild(dom);
|
|
497
495
|
return next;
|
|
@@ -827,12 +825,12 @@ class WidgetView extends ContentView {
|
|
|
827
825
|
ignoreEvent(event) { return this.widget.ignoreEvent(event); }
|
|
828
826
|
get overrideDOMText() {
|
|
829
827
|
if (this.length == 0)
|
|
830
|
-
return
|
|
828
|
+
return state.Text.empty;
|
|
831
829
|
let top = this;
|
|
832
830
|
while (top.parent)
|
|
833
831
|
top = top.parent;
|
|
834
|
-
let view = top.editorView, text
|
|
835
|
-
return text
|
|
832
|
+
let view = top.editorView, text = view && view.state.doc, start = this.posAtStart;
|
|
833
|
+
return text ? text.slice(start, start + this.length) : state.Text.empty;
|
|
836
834
|
}
|
|
837
835
|
domAtPos(pos) {
|
|
838
836
|
return pos == 0 ? DOMPos.before(this.dom) : DOMPos.after(this.dom, pos == this.length);
|
|
@@ -955,7 +953,7 @@ class WidgetBufferView extends ContentView {
|
|
|
955
953
|
? { left: imgRect.left, right: imgRect.right, top: siblingRect.top, bottom: siblingRect.bottom } : imgRect;
|
|
956
954
|
}
|
|
957
955
|
get overrideDOMText() {
|
|
958
|
-
return
|
|
956
|
+
return state.Text.empty;
|
|
959
957
|
}
|
|
960
958
|
}
|
|
961
959
|
TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren;
|
|
@@ -1079,7 +1077,7 @@ function updateAttrs(dom, prev, attrs) {
|
|
|
1079
1077
|
Widgets added to the content are described by subclasses of this
|
|
1080
1078
|
class. Using a description object like that makes it possible to
|
|
1081
1079
|
delay creating of the DOM structure for a widget until it is
|
|
1082
|
-
needed, and to avoid redrawing widgets even
|
|
1080
|
+
needed, and to avoid redrawing widgets even if the decorations
|
|
1083
1081
|
that define them are recreated.
|
|
1084
1082
|
*/
|
|
1085
1083
|
class WidgetType {
|
|
@@ -1092,7 +1090,7 @@ class WidgetType {
|
|
|
1092
1090
|
returns `false`, which will cause new instances of the widget to
|
|
1093
1091
|
always be redrawn.
|
|
1094
1092
|
*/
|
|
1095
|
-
eq(
|
|
1093
|
+
eq(widget) { return false; }
|
|
1096
1094
|
/**
|
|
1097
1095
|
Update a DOM element created by a widget of the same type (but
|
|
1098
1096
|
different, non-`eq` content) to reflect this widget. May return
|
|
@@ -1100,7 +1098,7 @@ class WidgetType {
|
|
|
1100
1098
|
couldn't (in which case the widget will be redrawn). The default
|
|
1101
1099
|
implementation just returns false.
|
|
1102
1100
|
*/
|
|
1103
|
-
updateDOM(
|
|
1101
|
+
updateDOM(dom) { return false; }
|
|
1104
1102
|
/**
|
|
1105
1103
|
@internal
|
|
1106
1104
|
*/
|
|
@@ -1119,7 +1117,7 @@ class WidgetType {
|
|
|
1119
1117
|
should be ignored by the editor. The default is to ignore all
|
|
1120
1118
|
events.
|
|
1121
1119
|
*/
|
|
1122
|
-
ignoreEvent(
|
|
1120
|
+
ignoreEvent(event) { return true; }
|
|
1123
1121
|
/**
|
|
1124
1122
|
@internal
|
|
1125
1123
|
*/
|
|
@@ -1128,7 +1126,7 @@ class WidgetType {
|
|
|
1128
1126
|
This is called when the an instance of the widget is removed
|
|
1129
1127
|
from the editor view.
|
|
1130
1128
|
*/
|
|
1131
|
-
destroy(
|
|
1129
|
+
destroy(dom) { }
|
|
1132
1130
|
}
|
|
1133
1131
|
/**
|
|
1134
1132
|
The different types of blocks that can occur in an editor view.
|
|
@@ -1155,9 +1153,10 @@ exports.BlockType = void 0;
|
|
|
1155
1153
|
/**
|
|
1156
1154
|
A decoration provides information on how to draw or style a piece
|
|
1157
1155
|
of content. You'll usually use it wrapped in a
|
|
1158
|
-
[`Range`](https://codemirror.net/6/docs/ref/#
|
|
1156
|
+
[`Range`](https://codemirror.net/6/docs/ref/#state.Range), which adds a start and end position.
|
|
1157
|
+
@nonabstract
|
|
1159
1158
|
*/
|
|
1160
|
-
class Decoration extends
|
|
1159
|
+
class Decoration extends state.RangeValue {
|
|
1161
1160
|
/**
|
|
1162
1161
|
@internal
|
|
1163
1162
|
*/
|
|
@@ -1194,18 +1193,17 @@ class Decoration extends rangeset.RangeValue {
|
|
|
1194
1193
|
Create a mark decoration, which influences the styling of the
|
|
1195
1194
|
content in its range. Nested mark decorations will cause nested
|
|
1196
1195
|
DOM elements to be created. Nesting order is determined by
|
|
1197
|
-
precedence of the [facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations)
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
decorations.
|
|
1196
|
+
precedence of the [facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations), with
|
|
1197
|
+
the higher-precedence decorations creating the inner DOM nodes.
|
|
1198
|
+
Such elements are split on line boundaries and on the boundaries
|
|
1199
|
+
of lower-precedence decorations.
|
|
1202
1200
|
*/
|
|
1203
1201
|
static mark(spec) {
|
|
1204
1202
|
return new MarkDecoration(spec);
|
|
1205
1203
|
}
|
|
1206
1204
|
/**
|
|
1207
|
-
Create a widget decoration, which
|
|
1208
|
-
position.
|
|
1205
|
+
Create a widget decoration, which displays a DOM element at the
|
|
1206
|
+
given position.
|
|
1209
1207
|
*/
|
|
1210
1208
|
static widget(spec) {
|
|
1211
1209
|
let side = spec.side || 0, block = !!spec.block;
|
|
@@ -1242,7 +1240,7 @@ class Decoration extends rangeset.RangeValue {
|
|
|
1242
1240
|
pass `true` for `sort` to make the library sort them for you.
|
|
1243
1241
|
*/
|
|
1244
1242
|
static set(of, sort = false) {
|
|
1245
|
-
return
|
|
1243
|
+
return state.RangeSet.of(of, sort);
|
|
1246
1244
|
}
|
|
1247
1245
|
/**
|
|
1248
1246
|
@internal
|
|
@@ -1252,7 +1250,7 @@ class Decoration extends rangeset.RangeValue {
|
|
|
1252
1250
|
/**
|
|
1253
1251
|
The empty set of decorations.
|
|
1254
1252
|
*/
|
|
1255
|
-
Decoration.none =
|
|
1253
|
+
Decoration.none = state.RangeSet.empty;
|
|
1256
1254
|
class MarkDecoration extends Decoration {
|
|
1257
1255
|
constructor(spec) {
|
|
1258
1256
|
let { start, end } = getInclusive(spec);
|
|
@@ -1543,11 +1541,11 @@ class BlockWidgetView extends ContentView {
|
|
|
1543
1541
|
}
|
|
1544
1542
|
|
|
1545
1543
|
class ContentBuilder {
|
|
1546
|
-
constructor(doc, pos, end,
|
|
1544
|
+
constructor(doc, pos, end, disallowBlockEffectsFor) {
|
|
1547
1545
|
this.doc = doc;
|
|
1548
1546
|
this.pos = pos;
|
|
1549
1547
|
this.end = end;
|
|
1550
|
-
this.
|
|
1548
|
+
this.disallowBlockEffectsFor = disallowBlockEffectsFor;
|
|
1551
1549
|
this.content = [];
|
|
1552
1550
|
this.curLine = null;
|
|
1553
1551
|
this.breakAtStart = 0;
|
|
@@ -1632,7 +1630,13 @@ class ContentBuilder {
|
|
|
1632
1630
|
if (this.openStart < 0)
|
|
1633
1631
|
this.openStart = openStart;
|
|
1634
1632
|
}
|
|
1635
|
-
point(from, to, deco, active, openStart) {
|
|
1633
|
+
point(from, to, deco, active, openStart, index) {
|
|
1634
|
+
if (this.disallowBlockEffectsFor[index] && deco instanceof PointDecoration) {
|
|
1635
|
+
if (deco.block)
|
|
1636
|
+
throw new RangeError("Block decorations may not be specified via plugins");
|
|
1637
|
+
if (to > this.doc.lineAt(this.pos).to)
|
|
1638
|
+
throw new RangeError("Decorations that replace line breaks may not be specified via plugins");
|
|
1639
|
+
}
|
|
1636
1640
|
let len = to - from;
|
|
1637
1641
|
if (deco instanceof PointDecoration) {
|
|
1638
1642
|
if (deco.block) {
|
|
@@ -1676,18 +1680,9 @@ class ContentBuilder {
|
|
|
1676
1680
|
if (this.openStart < 0)
|
|
1677
1681
|
this.openStart = openStart;
|
|
1678
1682
|
}
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
throw new RangeError("Block decorations may not be specified via plugins");
|
|
1683
|
-
if (to > this.doc.lineAt(this.pos).to)
|
|
1684
|
-
throw new RangeError("Decorations that replace line breaks may not be specified via plugins");
|
|
1685
|
-
}
|
|
1686
|
-
return true;
|
|
1687
|
-
}
|
|
1688
|
-
static build(text, from, to, decorations, pluginDecorationLength) {
|
|
1689
|
-
let builder = new ContentBuilder(text, from, to, pluginDecorationLength);
|
|
1690
|
-
builder.openEnd = rangeset.RangeSet.spans(decorations, from, to, builder);
|
|
1683
|
+
static build(text, from, to, decorations, dynamicDecorationMap) {
|
|
1684
|
+
let builder = new ContentBuilder(text, from, to, dynamicDecorationMap);
|
|
1685
|
+
builder.openEnd = state.RangeSet.spans(decorations, from, to, builder);
|
|
1691
1686
|
if (builder.openStart < 0)
|
|
1692
1687
|
builder.openStart = builder.openEnd;
|
|
1693
1688
|
builder.finish(builder.openEnd);
|
|
@@ -1716,13 +1711,8 @@ const mouseSelectionStyle = state.Facet.define();
|
|
|
1716
1711
|
const exceptionSink = state.Facet.define();
|
|
1717
1712
|
const updateListener = state.Facet.define();
|
|
1718
1713
|
const inputHandler = state.Facet.define();
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
map: (range, changes) => range.map(changes)
|
|
1722
|
-
});
|
|
1723
|
-
// FIXME remove
|
|
1724
|
-
const centerOn = state.StateEffect.define({
|
|
1725
|
-
map: (range, changes) => range.map(changes)
|
|
1714
|
+
const perLineTextDirection = state.Facet.define({
|
|
1715
|
+
combine: values => values.some(x => x)
|
|
1726
1716
|
});
|
|
1727
1717
|
class ScrollTarget {
|
|
1728
1718
|
constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5) {
|
|
@@ -1761,83 +1751,6 @@ function logException(state, exception, context) {
|
|
|
1761
1751
|
console.error(exception);
|
|
1762
1752
|
}
|
|
1763
1753
|
const editable = state.Facet.define({ combine: values => values.length ? values[0] : true });
|
|
1764
|
-
/**
|
|
1765
|
-
Used to [declare](https://codemirror.net/6/docs/ref/#view.PluginSpec.provide) which
|
|
1766
|
-
[fields](https://codemirror.net/6/docs/ref/#view.PluginValue) a [view plugin](https://codemirror.net/6/docs/ref/#view.ViewPlugin)
|
|
1767
|
-
provides.
|
|
1768
|
-
*/
|
|
1769
|
-
class PluginFieldProvider {
|
|
1770
|
-
/**
|
|
1771
|
-
@internal
|
|
1772
|
-
*/
|
|
1773
|
-
constructor(
|
|
1774
|
-
/**
|
|
1775
|
-
@internal
|
|
1776
|
-
*/
|
|
1777
|
-
field,
|
|
1778
|
-
/**
|
|
1779
|
-
@internal
|
|
1780
|
-
*/
|
|
1781
|
-
get) {
|
|
1782
|
-
this.field = field;
|
|
1783
|
-
this.get = get;
|
|
1784
|
-
}
|
|
1785
|
-
}
|
|
1786
|
-
/**
|
|
1787
|
-
Plugin fields are a mechanism for allowing plugins to provide
|
|
1788
|
-
values that can be retrieved through the
|
|
1789
|
-
[`pluginField`](https://codemirror.net/6/docs/ref/#view.EditorView.pluginField) view method.
|
|
1790
|
-
*/
|
|
1791
|
-
class PluginField {
|
|
1792
|
-
/**
|
|
1793
|
-
Create a [provider](https://codemirror.net/6/docs/ref/#view.PluginFieldProvider) for this field,
|
|
1794
|
-
to use with a plugin's [provide](https://codemirror.net/6/docs/ref/#view.PluginSpec.provide)
|
|
1795
|
-
option.
|
|
1796
|
-
*/
|
|
1797
|
-
from(get) {
|
|
1798
|
-
return new PluginFieldProvider(this, get);
|
|
1799
|
-
}
|
|
1800
|
-
/**
|
|
1801
|
-
Define a new plugin field.
|
|
1802
|
-
*/
|
|
1803
|
-
static define() { return new PluginField(); }
|
|
1804
|
-
}
|
|
1805
|
-
/**
|
|
1806
|
-
This field can be used by plugins to provide
|
|
1807
|
-
[decorations](https://codemirror.net/6/docs/ref/#view.Decoration).
|
|
1808
|
-
|
|
1809
|
-
**Note**: For reasons of data flow (plugins are only updated
|
|
1810
|
-
after the viewport is computed), decorations produced by plugins
|
|
1811
|
-
are _not_ taken into account when predicting the vertical layout
|
|
1812
|
-
structure of the editor. They **must not** introduce block
|
|
1813
|
-
widgets (that will raise an error) or replacing decorations that
|
|
1814
|
-
cover line breaks (these will be ignored if they occur). Such
|
|
1815
|
-
decorations, or others that cause a large amount of vertical
|
|
1816
|
-
size shift compared to the undecorated content, should be
|
|
1817
|
-
provided through the state-level [`decorations`
|
|
1818
|
-
facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations) instead.
|
|
1819
|
-
*/
|
|
1820
|
-
PluginField.decorations = PluginField.define();
|
|
1821
|
-
/**
|
|
1822
|
-
Used to provide ranges that should be treated as atoms as far as
|
|
1823
|
-
cursor motion is concerned. This causes methods like
|
|
1824
|
-
[`moveByChar`](https://codemirror.net/6/docs/ref/#view.EditorView.moveByChar) and
|
|
1825
|
-
[`moveVertically`](https://codemirror.net/6/docs/ref/#view.EditorView.moveVertically) (and the
|
|
1826
|
-
commands built on top of them) to skip across such regions when
|
|
1827
|
-
a selection endpoint would enter them. This does _not_ prevent
|
|
1828
|
-
direct programmatic [selection
|
|
1829
|
-
updates](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection) from moving into such
|
|
1830
|
-
regions.
|
|
1831
|
-
*/
|
|
1832
|
-
PluginField.atomicRanges = PluginField.define();
|
|
1833
|
-
/**
|
|
1834
|
-
Plugins can provide additional scroll margins (space around the
|
|
1835
|
-
sides of the scrolling element that should be considered
|
|
1836
|
-
invisible) through this field. This can be useful when the
|
|
1837
|
-
plugin introduces elements that cover part of that element (for
|
|
1838
|
-
example a horizontally fixed gutter).
|
|
1839
|
-
*/
|
|
1840
|
-
PluginField.scrollMargins = PluginField.define();
|
|
1841
1754
|
let nextPluginID = 0;
|
|
1842
1755
|
const viewPlugin = state.Facet.define();
|
|
1843
1756
|
/**
|
|
@@ -1858,27 +1771,29 @@ class ViewPlugin {
|
|
|
1858
1771
|
/**
|
|
1859
1772
|
@internal
|
|
1860
1773
|
*/
|
|
1861
|
-
|
|
1774
|
+
domEventHandlers, buildExtensions) {
|
|
1862
1775
|
this.id = id;
|
|
1863
1776
|
this.create = create;
|
|
1864
|
-
this.
|
|
1865
|
-
this.extension =
|
|
1777
|
+
this.domEventHandlers = domEventHandlers;
|
|
1778
|
+
this.extension = buildExtensions(this);
|
|
1866
1779
|
}
|
|
1867
1780
|
/**
|
|
1868
1781
|
Define a plugin from a constructor function that creates the
|
|
1869
1782
|
plugin's value, given an editor view.
|
|
1870
1783
|
*/
|
|
1871
1784
|
static define(create, spec) {
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1785
|
+
const { eventHandlers, provide, decorations: deco } = spec || {};
|
|
1786
|
+
return new ViewPlugin(nextPluginID++, create, eventHandlers, plugin => {
|
|
1787
|
+
let ext = [viewPlugin.of(plugin)];
|
|
1788
|
+
if (deco)
|
|
1789
|
+
ext.push(decorations.of(view => {
|
|
1790
|
+
let pluginInst = view.plugin(plugin);
|
|
1791
|
+
return pluginInst ? deco(pluginInst) : Decoration.none;
|
|
1792
|
+
}));
|
|
1793
|
+
if (provide)
|
|
1794
|
+
ext.push(provide(plugin));
|
|
1795
|
+
return ext;
|
|
1796
|
+
});
|
|
1882
1797
|
}
|
|
1883
1798
|
/**
|
|
1884
1799
|
Create a plugin for a class whose constructor takes a single
|
|
@@ -1888,7 +1803,6 @@ class ViewPlugin {
|
|
|
1888
1803
|
return ViewPlugin.define(view => new cls(view), spec);
|
|
1889
1804
|
}
|
|
1890
1805
|
}
|
|
1891
|
-
const domEventHandlers = PluginField.define();
|
|
1892
1806
|
class PluginInstance {
|
|
1893
1807
|
constructor(spec) {
|
|
1894
1808
|
this.spec = spec;
|
|
@@ -1901,12 +1815,6 @@ class PluginInstance {
|
|
|
1901
1815
|
// initialized on the first update.
|
|
1902
1816
|
this.value = null;
|
|
1903
1817
|
}
|
|
1904
|
-
takeField(type, target) {
|
|
1905
|
-
if (this.spec)
|
|
1906
|
-
for (let { field, get } of this.spec.fields)
|
|
1907
|
-
if (field == type)
|
|
1908
|
-
target.push(get(this.value));
|
|
1909
|
-
}
|
|
1910
1818
|
update(view) {
|
|
1911
1819
|
if (!this.value) {
|
|
1912
1820
|
if (this.spec) {
|
|
@@ -1958,6 +1866,8 @@ const editorAttributes = state.Facet.define();
|
|
|
1958
1866
|
const contentAttributes = state.Facet.define();
|
|
1959
1867
|
// Provide decorations
|
|
1960
1868
|
const decorations = state.Facet.define();
|
|
1869
|
+
const atomicRanges = state.Facet.define();
|
|
1870
|
+
const scrollMargins = state.Facet.define();
|
|
1961
1871
|
const styleModule = state.Facet.define();
|
|
1962
1872
|
class ChangedRange {
|
|
1963
1873
|
constructor(fromA, toA, fromB, toB) {
|
|
@@ -2058,8 +1968,8 @@ class ViewUpdate {
|
|
|
2058
1968
|
return (this.flags & 4 /* Viewport */) > 0;
|
|
2059
1969
|
}
|
|
2060
1970
|
/**
|
|
2061
|
-
Indicates whether the height of
|
|
2062
|
-
in this update.
|
|
1971
|
+
Indicates whether the height of a block element in the editor
|
|
1972
|
+
changed in this update.
|
|
2063
1973
|
*/
|
|
2064
1974
|
get heightChanged() {
|
|
2065
1975
|
return (this.flags & 2 /* Height */) > 0;
|
|
@@ -2395,7 +2305,7 @@ function moveVisually(line, order, dir, start, forward) {
|
|
|
2395
2305
|
startIndex = span.side(!forward, dir);
|
|
2396
2306
|
}
|
|
2397
2307
|
let indexForward = forward == (span.dir == dir);
|
|
2398
|
-
let nextIndex =
|
|
2308
|
+
let nextIndex = state.findClusterBreak(line.text, startIndex, indexForward);
|
|
2399
2309
|
movedOver = line.text.slice(Math.min(startIndex, nextIndex), Math.max(startIndex, nextIndex));
|
|
2400
2310
|
if (nextIndex != span.side(forward, dir))
|
|
2401
2311
|
return state.EditorSelection.cursor(nextIndex + line.from, indexForward ? -1 : 1, span.level);
|
|
@@ -2519,7 +2429,7 @@ class DocView extends ContentView {
|
|
|
2519
2429
|
this.view = view;
|
|
2520
2430
|
this.compositionDeco = Decoration.none;
|
|
2521
2431
|
this.decorations = [];
|
|
2522
|
-
this.
|
|
2432
|
+
this.dynamicDecorationMap = [];
|
|
2523
2433
|
// Track a minimum width for the editor. When measuring sizes in
|
|
2524
2434
|
// measureVisibleLineHeights, this is updated to point at the width
|
|
2525
2435
|
// of a given element and its extent in the document. When a change
|
|
@@ -2625,7 +2535,7 @@ class DocView extends ContentView {
|
|
|
2625
2535
|
if (!next)
|
|
2626
2536
|
break;
|
|
2627
2537
|
let { fromA, toA, fromB, toB } = next;
|
|
2628
|
-
let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, this.decorations, this.
|
|
2538
|
+
let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, this.decorations, this.dynamicDecorationMap);
|
|
2629
2539
|
let { i: toI, off: toOff } = cursor.findPos(toA, 1);
|
|
2630
2540
|
let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
|
|
2631
2541
|
replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
|
|
@@ -2766,11 +2676,11 @@ class DocView extends ContentView {
|
|
|
2766
2676
|
off = start;
|
|
2767
2677
|
}
|
|
2768
2678
|
}
|
|
2769
|
-
measureVisibleLineHeights() {
|
|
2770
|
-
let result = [], { from, to } =
|
|
2679
|
+
measureVisibleLineHeights(viewport) {
|
|
2680
|
+
let result = [], { from, to } = viewport;
|
|
2771
2681
|
let contentWidth = this.view.contentDOM.clientWidth;
|
|
2772
2682
|
let isWider = contentWidth > Math.max(this.view.scrollDOM.clientWidth, this.minWidth) + 1;
|
|
2773
|
-
let widest = -1;
|
|
2683
|
+
let widest = -1, ltr = this.view.textDirection == exports.Direction.LTR;
|
|
2774
2684
|
for (let pos = 0, i = 0; i < this.children.length; i++) {
|
|
2775
2685
|
let child = this.children[i], end = pos + child.length;
|
|
2776
2686
|
if (end > to)
|
|
@@ -2783,8 +2693,7 @@ class DocView extends ContentView {
|
|
|
2783
2693
|
let rects = last ? clientRectsFor(last) : [];
|
|
2784
2694
|
if (rects.length) {
|
|
2785
2695
|
let rect = rects[rects.length - 1];
|
|
2786
|
-
let width =
|
|
2787
|
-
: childRect.right - rect.left;
|
|
2696
|
+
let width = ltr ? rect.right - childRect.left : childRect.right - rect.left;
|
|
2788
2697
|
if (width > widest) {
|
|
2789
2698
|
widest = width;
|
|
2790
2699
|
this.minWidth = contentWidth;
|
|
@@ -2798,6 +2707,10 @@ class DocView extends ContentView {
|
|
|
2798
2707
|
}
|
|
2799
2708
|
return result;
|
|
2800
2709
|
}
|
|
2710
|
+
textDirectionAt(pos) {
|
|
2711
|
+
let { i } = this.childPos(pos, 1);
|
|
2712
|
+
return getComputedStyle(this.children[i].dom).direction == "rtl" ? exports.Direction.RTL : exports.Direction.LTR;
|
|
2713
|
+
}
|
|
2801
2714
|
measureTextSize() {
|
|
2802
2715
|
for (let child of this.children) {
|
|
2803
2716
|
if (child instanceof LineView) {
|
|
@@ -2849,11 +2762,14 @@ class DocView extends ContentView {
|
|
|
2849
2762
|
return Decoration.set(deco);
|
|
2850
2763
|
}
|
|
2851
2764
|
updateDeco() {
|
|
2852
|
-
let
|
|
2853
|
-
|
|
2765
|
+
let allDeco = this.view.state.facet(decorations).map((d, i) => {
|
|
2766
|
+
let dynamic = this.dynamicDecorationMap[i] = typeof d == "function";
|
|
2767
|
+
return dynamic ? d(this.view) : d;
|
|
2768
|
+
});
|
|
2769
|
+
for (let i = allDeco.length; i < allDeco.length + 3; i++)
|
|
2770
|
+
this.dynamicDecorationMap[i] = false;
|
|
2854
2771
|
return this.decorations = [
|
|
2855
|
-
...
|
|
2856
|
-
...this.view.state.facet(decorations),
|
|
2772
|
+
...allDeco,
|
|
2857
2773
|
this.compositionDeco,
|
|
2858
2774
|
this.computeBlockGapDeco(),
|
|
2859
2775
|
this.view.viewState.lineGapDeco
|
|
@@ -2868,7 +2784,7 @@ class DocView extends ContentView {
|
|
|
2868
2784
|
rect = { left: Math.min(rect.left, other.left), top: Math.min(rect.top, other.top),
|
|
2869
2785
|
right: Math.max(rect.right, other.right), bottom: Math.max(rect.bottom, other.bottom) };
|
|
2870
2786
|
let mLeft = 0, mRight = 0, mTop = 0, mBottom = 0;
|
|
2871
|
-
for (let margins of this.view.
|
|
2787
|
+
for (let margins of this.view.state.facet(scrollMargins).map(f => f(this.view)))
|
|
2872
2788
|
if (margins) {
|
|
2873
2789
|
let { left, right, top, bottom } = margins;
|
|
2874
2790
|
if (left != null)
|
|
@@ -3010,7 +2926,7 @@ class DecorationComparator$1 {
|
|
|
3010
2926
|
}
|
|
3011
2927
|
function findChangedDeco(a, b, diff) {
|
|
3012
2928
|
let comp = new DecorationComparator$1;
|
|
3013
|
-
|
|
2929
|
+
state.RangeSet.compare(a, b, diff, comp);
|
|
3014
2930
|
return comp.changes;
|
|
3015
2931
|
}
|
|
3016
2932
|
function inUneditable(node, inside) {
|
|
@@ -3033,18 +2949,18 @@ function groupAt(state$1, pos, bias = 1) {
|
|
|
3033
2949
|
bias = -1;
|
|
3034
2950
|
let from = linePos, to = linePos;
|
|
3035
2951
|
if (bias < 0)
|
|
3036
|
-
from =
|
|
2952
|
+
from = state.findClusterBreak(line.text, linePos, false);
|
|
3037
2953
|
else
|
|
3038
|
-
to =
|
|
2954
|
+
to = state.findClusterBreak(line.text, linePos);
|
|
3039
2955
|
let cat = categorize(line.text.slice(from, to));
|
|
3040
2956
|
while (from > 0) {
|
|
3041
|
-
let prev =
|
|
2957
|
+
let prev = state.findClusterBreak(line.text, from, false);
|
|
3042
2958
|
if (categorize(line.text.slice(prev, from)) != cat)
|
|
3043
2959
|
break;
|
|
3044
2960
|
from = prev;
|
|
3045
2961
|
}
|
|
3046
2962
|
while (to < line.length) {
|
|
3047
|
-
let next =
|
|
2963
|
+
let next = state.findClusterBreak(line.text, to);
|
|
3048
2964
|
if (categorize(line.text.slice(to, next)) != cat)
|
|
3049
2965
|
break;
|
|
3050
2966
|
to = next;
|
|
@@ -3236,7 +3152,7 @@ function posAtCoordsImprecise(view, contentRect, block, x, y) {
|
|
|
3236
3152
|
into += line * view.viewState.heightOracle.lineLength;
|
|
3237
3153
|
}
|
|
3238
3154
|
let content = view.state.sliceDoc(block.from, block.to);
|
|
3239
|
-
return block.from +
|
|
3155
|
+
return block.from + state.findColumn(content, into, view.state.tabSize);
|
|
3240
3156
|
}
|
|
3241
3157
|
// In case of a high line height, Safari's caretRangeFromPoint treats
|
|
3242
3158
|
// the space between lines as belonging to the last character of the
|
|
@@ -3257,7 +3173,8 @@ function moveToLineBoundary(view, start, forward, includeWrap) {
|
|
|
3257
3173
|
: view.coordsAtPos(start.assoc < 0 && start.head > line.from ? start.head - 1 : start.head);
|
|
3258
3174
|
if (coords) {
|
|
3259
3175
|
let editorRect = view.dom.getBoundingClientRect();
|
|
3260
|
-
let
|
|
3176
|
+
let direction = view.textDirectionAt(line.from);
|
|
3177
|
+
let pos = view.posAtCoords({ x: forward == (direction == exports.Direction.LTR) ? editorRect.right - 1 : editorRect.left + 1,
|
|
3261
3178
|
y: (coords.top + coords.bottom) / 2 });
|
|
3262
3179
|
if (pos != null)
|
|
3263
3180
|
return state.EditorSelection.cursor(pos, forward ? -1 : 1);
|
|
@@ -3268,8 +3185,9 @@ function moveToLineBoundary(view, start, forward, includeWrap) {
|
|
|
3268
3185
|
}
|
|
3269
3186
|
function moveByChar(view, start, forward, by) {
|
|
3270
3187
|
let line = view.state.doc.lineAt(start.head), spans = view.bidiSpans(line);
|
|
3188
|
+
let direction = view.textDirectionAt(line.from);
|
|
3271
3189
|
for (let cur = start, check = null;;) {
|
|
3272
|
-
let next = moveVisually(line, spans,
|
|
3190
|
+
let next = moveVisually(line, spans, direction, cur, forward), char = movedOver;
|
|
3273
3191
|
if (!next) {
|
|
3274
3192
|
if (line.number == (forward ? view.state.doc.lines : 1))
|
|
3275
3193
|
return cur;
|
|
@@ -3327,7 +3245,7 @@ function moveVertically(view, start, forward, distance) {
|
|
|
3327
3245
|
}
|
|
3328
3246
|
}
|
|
3329
3247
|
function skipAtoms(view, oldPos, pos) {
|
|
3330
|
-
let atoms = view.
|
|
3248
|
+
let atoms = view.state.facet(atomicRanges).map(f => f(view));
|
|
3331
3249
|
for (;;) {
|
|
3332
3250
|
let moved = false;
|
|
3333
3251
|
for (let set of atoms) {
|
|
@@ -3389,7 +3307,6 @@ class InputState {
|
|
|
3389
3307
|
this.registeredEvents.push(type);
|
|
3390
3308
|
}
|
|
3391
3309
|
this.notifiedFocused = view.hasFocus;
|
|
3392
|
-
this.ensureHandlers(view);
|
|
3393
3310
|
// On Safari adding an input event handler somehow prevents an
|
|
3394
3311
|
// issue where the composition vanishes when you press enter.
|
|
3395
3312
|
if (browser.safari)
|
|
@@ -3399,20 +3316,23 @@ class InputState {
|
|
|
3399
3316
|
this.lastSelectionOrigin = origin;
|
|
3400
3317
|
this.lastSelectionTime = Date.now();
|
|
3401
3318
|
}
|
|
3402
|
-
ensureHandlers(view) {
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
event
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3319
|
+
ensureHandlers(view, plugins) {
|
|
3320
|
+
var _a;
|
|
3321
|
+
let handlers;
|
|
3322
|
+
for (let plugin of plugins)
|
|
3323
|
+
if (handlers = (_a = plugin.update(view).spec) === null || _a === void 0 ? void 0 : _a.domEventHandlers) {
|
|
3324
|
+
this.customHandlers.push({ plugin: plugin.value, handlers });
|
|
3325
|
+
for (let type in handlers)
|
|
3326
|
+
if (this.registeredEvents.indexOf(type) < 0 && type != "scroll") {
|
|
3327
|
+
this.registeredEvents.push(type);
|
|
3328
|
+
view.contentDOM.addEventListener(type, (event) => {
|
|
3329
|
+
if (!eventBelongsToEditor(view, event))
|
|
3330
|
+
return;
|
|
3331
|
+
if (this.runCustomHandlers(type, view, event))
|
|
3332
|
+
event.preventDefault();
|
|
3333
|
+
});
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3416
3336
|
}
|
|
3417
3337
|
runCustomHandlers(type, view, event) {
|
|
3418
3338
|
for (let set of this.customHandlers) {
|
|
@@ -3490,7 +3410,7 @@ class InputState {
|
|
|
3490
3410
|
// compositionend and keydown events are sometimes emitted in the
|
|
3491
3411
|
// wrong order. The key event should still be ignored, even when
|
|
3492
3412
|
// it happens after the compositionend event.
|
|
3493
|
-
if (browser.safari && Date.now() - this.compositionEndedAt <
|
|
3413
|
+
if (browser.safari && Date.now() - this.compositionEndedAt < 100) {
|
|
3494
3414
|
this.compositionEndedAt = 0;
|
|
3495
3415
|
return true;
|
|
3496
3416
|
}
|
|
@@ -3996,9 +3916,8 @@ handlers.beforeinput = (view, event) => {
|
|
|
3996
3916
|
const wrappingWhiteSpace = ["pre-wrap", "normal", "pre-line", "break-spaces"];
|
|
3997
3917
|
class HeightOracle {
|
|
3998
3918
|
constructor() {
|
|
3999
|
-
this.doc =
|
|
3919
|
+
this.doc = state.Text.empty;
|
|
4000
3920
|
this.lineWrapping = false;
|
|
4001
|
-
this.direction = exports.Direction.LTR;
|
|
4002
3921
|
this.heightSamples = {};
|
|
4003
3922
|
this.lineHeight = 14;
|
|
4004
3923
|
this.charWidth = 7;
|
|
@@ -4019,8 +3938,8 @@ class HeightOracle {
|
|
|
4019
3938
|
return lines * this.lineHeight;
|
|
4020
3939
|
}
|
|
4021
3940
|
setDoc(doc) { this.doc = doc; return this; }
|
|
4022
|
-
|
|
4023
|
-
return (wrappingWhiteSpace.indexOf(whiteSpace) > -1) != this.lineWrapping
|
|
3941
|
+
mustRefreshForWrapping(whiteSpace) {
|
|
3942
|
+
return (wrappingWhiteSpace.indexOf(whiteSpace) > -1) != this.lineWrapping;
|
|
4024
3943
|
}
|
|
4025
3944
|
mustRefreshForHeights(lineHeights) {
|
|
4026
3945
|
let newHeight = false;
|
|
@@ -4036,13 +3955,10 @@ class HeightOracle {
|
|
|
4036
3955
|
}
|
|
4037
3956
|
return newHeight;
|
|
4038
3957
|
}
|
|
4039
|
-
refresh(whiteSpace,
|
|
3958
|
+
refresh(whiteSpace, lineHeight, charWidth, lineLength, knownHeights) {
|
|
4040
3959
|
let lineWrapping = wrappingWhiteSpace.indexOf(whiteSpace) > -1;
|
|
4041
|
-
let changed = Math.round(lineHeight) != Math.round(this.lineHeight) ||
|
|
4042
|
-
this.lineWrapping != lineWrapping ||
|
|
4043
|
-
this.direction != direction;
|
|
3960
|
+
let changed = Math.round(lineHeight) != Math.round(this.lineHeight) || this.lineWrapping != lineWrapping;
|
|
4044
3961
|
this.lineWrapping = lineWrapping;
|
|
4045
|
-
this.direction = direction;
|
|
4046
3962
|
this.lineHeight = lineHeight;
|
|
4047
3963
|
this.charWidth = charWidth;
|
|
4048
3964
|
this.lineLength = lineLength;
|
|
@@ -4123,12 +4039,6 @@ class BlockInfo {
|
|
|
4123
4039
|
.concat(Array.isArray(other.type) ? other.type : [other]);
|
|
4124
4040
|
return new BlockInfo(this.from, this.length + other.length, this.top, this.height + other.height, detail);
|
|
4125
4041
|
}
|
|
4126
|
-
/**
|
|
4127
|
-
FIXME remove on next breaking release @internal
|
|
4128
|
-
*/
|
|
4129
|
-
moveY(offset) {
|
|
4130
|
-
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);
|
|
4131
|
-
}
|
|
4132
4042
|
}
|
|
4133
4043
|
var QueryType;
|
|
4134
4044
|
(function (QueryType) {
|
|
@@ -4253,8 +4163,9 @@ class HeightMapBlock extends HeightMap {
|
|
|
4253
4163
|
lineAt(_value, _type, doc, top, offset) {
|
|
4254
4164
|
return this.blockAt(0, doc, top, offset);
|
|
4255
4165
|
}
|
|
4256
|
-
forEachLine(
|
|
4257
|
-
|
|
4166
|
+
forEachLine(from, to, doc, top, offset, f) {
|
|
4167
|
+
if (from <= offset + this.length && to >= offset)
|
|
4168
|
+
f(this.blockAt(0, doc, top, offset));
|
|
4258
4169
|
}
|
|
4259
4170
|
updateHeight(oracle, offset = 0, _force = false, measured) {
|
|
4260
4171
|
if (measured && measured.from <= offset && measured.more)
|
|
@@ -4636,13 +4547,13 @@ class NodeBuilder {
|
|
|
4636
4547
|
// to each other.
|
|
4637
4548
|
static build(oracle, decorations, from, to) {
|
|
4638
4549
|
let builder = new NodeBuilder(from, oracle);
|
|
4639
|
-
|
|
4550
|
+
state.RangeSet.spans(decorations, from, to, builder, 0);
|
|
4640
4551
|
return builder.finish(from);
|
|
4641
4552
|
}
|
|
4642
4553
|
}
|
|
4643
4554
|
function heightRelevantDecoChanges(a, b, diff) {
|
|
4644
4555
|
let comp = new DecorationComparator;
|
|
4645
|
-
|
|
4556
|
+
state.RangeSet.compare(a, b, diff, comp, 0);
|
|
4646
4557
|
return comp.changes;
|
|
4647
4558
|
}
|
|
4648
4559
|
class DecorationComparator {
|
|
@@ -4735,8 +4646,8 @@ class LineGapWidget extends WidgetType {
|
|
|
4735
4646
|
get estimatedHeight() { return this.vertical ? this.size : -1; }
|
|
4736
4647
|
}
|
|
4737
4648
|
class ViewState {
|
|
4738
|
-
constructor(state) {
|
|
4739
|
-
this.state = state;
|
|
4649
|
+
constructor(state$1) {
|
|
4650
|
+
this.state = state$1;
|
|
4740
4651
|
// These are contentDOM-local coordinates
|
|
4741
4652
|
this.pixelViewport = { left: 0, right: window.innerWidth, top: 0, bottom: 0 };
|
|
4742
4653
|
this.inView = true;
|
|
@@ -4755,6 +4666,7 @@ class ViewState {
|
|
|
4755
4666
|
// Flag set when editor content was redrawn, so that the next
|
|
4756
4667
|
// measure stage knows it must read DOM layout
|
|
4757
4668
|
this.mustMeasureContent = true;
|
|
4669
|
+
this.defaultTextDirection = exports.Direction.RTL;
|
|
4758
4670
|
this.visibleRanges = [];
|
|
4759
4671
|
// Cursor 'assoc' is only significant when the cursor is on a line
|
|
4760
4672
|
// wrap point, where it must stick to the character that it is
|
|
@@ -4765,7 +4677,8 @@ class ViewState {
|
|
|
4765
4677
|
// boundary and, if so, reset it to make sure it is positioned in
|
|
4766
4678
|
// the right place.
|
|
4767
4679
|
this.mustEnforceCursorAssoc = false;
|
|
4768
|
-
this.
|
|
4680
|
+
this.stateDeco = state$1.facet(decorations).filter(d => typeof d != "function");
|
|
4681
|
+
this.heightMap = HeightMap.empty().applyChanges(this.stateDeco, state.Text.empty, this.heightOracle.setDoc(state$1.doc), [new ChangedRange(0, 0, 0, state$1.doc.length)]);
|
|
4769
4682
|
this.viewport = this.getViewport(0, null);
|
|
4770
4683
|
this.updateViewportLines();
|
|
4771
4684
|
this.updateForViewport();
|
|
@@ -4793,13 +4706,13 @@ class ViewState {
|
|
|
4793
4706
|
});
|
|
4794
4707
|
}
|
|
4795
4708
|
update(update, scrollTarget = null) {
|
|
4796
|
-
let prev = this.state;
|
|
4797
4709
|
this.state = update.state;
|
|
4798
|
-
let
|
|
4710
|
+
let prevDeco = this.stateDeco;
|
|
4711
|
+
this.stateDeco = this.state.facet(decorations).filter(d => typeof d != "function");
|
|
4799
4712
|
let contentChanges = update.changedRanges;
|
|
4800
|
-
let heightChanges = ChangedRange.extendWithRanges(contentChanges, heightRelevantDecoChanges(
|
|
4713
|
+
let heightChanges = ChangedRange.extendWithRanges(contentChanges, heightRelevantDecoChanges(prevDeco, this.stateDeco, update ? update.changes : state.ChangeSet.empty(this.state.doc.length)));
|
|
4801
4714
|
let prevHeight = this.heightMap.height;
|
|
4802
|
-
this.heightMap = this.heightMap.applyChanges(
|
|
4715
|
+
this.heightMap = this.heightMap.applyChanges(this.stateDeco, update.startState.doc, this.heightOracle.setDoc(this.state.doc), heightChanges);
|
|
4803
4716
|
if (this.heightMap.height != prevHeight)
|
|
4804
4717
|
update.flags |= 2 /* Height */;
|
|
4805
4718
|
let viewport = heightChanges.length ? this.mapViewport(this.viewport, update.changes) : this.viewport;
|
|
@@ -4824,8 +4737,9 @@ class ViewState {
|
|
|
4824
4737
|
measure(view) {
|
|
4825
4738
|
let dom = view.contentDOM, style = window.getComputedStyle(dom);
|
|
4826
4739
|
let oracle = this.heightOracle;
|
|
4827
|
-
let whiteSpace = style.whiteSpace
|
|
4828
|
-
|
|
4740
|
+
let whiteSpace = style.whiteSpace;
|
|
4741
|
+
this.defaultTextDirection = style.direction == "rtl" ? exports.Direction.RTL : exports.Direction.LTR;
|
|
4742
|
+
let refresh = this.heightOracle.mustRefreshForWrapping(whiteSpace);
|
|
4829
4743
|
let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != dom.clientHeight;
|
|
4830
4744
|
let result = 0, bias = 0;
|
|
4831
4745
|
if (this.editorWidth != view.scrollDOM.clientWidth) {
|
|
@@ -4864,12 +4778,12 @@ class ViewState {
|
|
|
4864
4778
|
result |= 8 /* Geometry */;
|
|
4865
4779
|
}
|
|
4866
4780
|
if (measureContent) {
|
|
4867
|
-
let lineHeights = view.docView.measureVisibleLineHeights();
|
|
4781
|
+
let lineHeights = view.docView.measureVisibleLineHeights(this.viewport);
|
|
4868
4782
|
if (oracle.mustRefreshForHeights(lineHeights))
|
|
4869
4783
|
refresh = true;
|
|
4870
4784
|
if (refresh || oracle.lineWrapping && Math.abs(contentWidth - this.contentDOMWidth) > oracle.charWidth) {
|
|
4871
4785
|
let { lineHeight, charWidth } = view.docView.measureTextSize();
|
|
4872
|
-
refresh = oracle.refresh(whiteSpace,
|
|
4786
|
+
refresh = oracle.refresh(whiteSpace, lineHeight, charWidth, contentWidth / charWidth, lineHeights);
|
|
4873
4787
|
if (refresh) {
|
|
4874
4788
|
view.docView.minWidth = 0;
|
|
4875
4789
|
result |= 8 /* Geometry */;
|
|
@@ -4880,7 +4794,10 @@ class ViewState {
|
|
|
4880
4794
|
else if (dTop < 0 && dBottom < 0)
|
|
4881
4795
|
bias = Math.min(dTop, dBottom);
|
|
4882
4796
|
oracle.heightChanged = false;
|
|
4883
|
-
|
|
4797
|
+
for (let vp of this.viewports) {
|
|
4798
|
+
let heights = vp.from == this.viewport.from ? lineHeights : view.docView.measureVisibleLineHeights(vp);
|
|
4799
|
+
this.heightMap = this.heightMap.updateHeight(oracle, 0, refresh, new MeasuredHeights(vp.from, heights));
|
|
4800
|
+
}
|
|
4884
4801
|
if (oracle.heightChanged)
|
|
4885
4802
|
result |= 2 /* Height */;
|
|
4886
4803
|
}
|
|
@@ -4966,12 +4883,12 @@ class ViewState {
|
|
|
4966
4883
|
ensureLineGaps(current) {
|
|
4967
4884
|
let gaps = [];
|
|
4968
4885
|
// This won't work at all in predominantly right-to-left text.
|
|
4969
|
-
if (this.
|
|
4886
|
+
if (this.defaultTextDirection != exports.Direction.LTR)
|
|
4970
4887
|
return gaps;
|
|
4971
4888
|
for (let line of this.viewportLines) {
|
|
4972
4889
|
if (line.length < 4000 /* DoubleMargin */)
|
|
4973
4890
|
continue;
|
|
4974
|
-
let structure = lineStructure(line.from, line.to, this.
|
|
4891
|
+
let structure = lineStructure(line.from, line.to, this.stateDeco);
|
|
4975
4892
|
if (structure.total < 4000 /* DoubleMargin */)
|
|
4976
4893
|
continue;
|
|
4977
4894
|
let viewFrom, viewTo;
|
|
@@ -5022,11 +4939,11 @@ class ViewState {
|
|
|
5022
4939
|
}
|
|
5023
4940
|
}
|
|
5024
4941
|
computeVisibleRanges() {
|
|
5025
|
-
let deco = this.
|
|
4942
|
+
let deco = this.stateDeco;
|
|
5026
4943
|
if (this.lineGaps.length)
|
|
5027
4944
|
deco = deco.concat(this.lineGapDeco);
|
|
5028
4945
|
let ranges = [];
|
|
5029
|
-
|
|
4946
|
+
state.RangeSet.spans(deco, this.viewport.from, this.viewport.to, {
|
|
5030
4947
|
span(from, to) { ranges.push({ from, to }); },
|
|
5031
4948
|
point() { }
|
|
5032
4949
|
}, 20);
|
|
@@ -5058,9 +4975,9 @@ class Viewport {
|
|
|
5058
4975
|
this.to = to;
|
|
5059
4976
|
}
|
|
5060
4977
|
}
|
|
5061
|
-
function lineStructure(from, to,
|
|
4978
|
+
function lineStructure(from, to, stateDeco) {
|
|
5062
4979
|
let ranges = [], pos = from, total = 0;
|
|
5063
|
-
|
|
4980
|
+
state.RangeSet.spans(stateDeco, from, to, {
|
|
5064
4981
|
span() { },
|
|
5065
4982
|
point(from, to) {
|
|
5066
4983
|
if (from > pos) {
|
|
@@ -5193,7 +5110,7 @@ function buildTheme(main, spec, scopes) {
|
|
|
5193
5110
|
}
|
|
5194
5111
|
});
|
|
5195
5112
|
}
|
|
5196
|
-
const baseTheme = buildTheme("." + baseThemeID, {
|
|
5113
|
+
const baseTheme$1 = buildTheme("." + baseThemeID, {
|
|
5197
5114
|
"&.cm-editor": {
|
|
5198
5115
|
position: "relative !important",
|
|
5199
5116
|
boxSizing: "border-box",
|
|
@@ -5298,6 +5215,65 @@ const baseTheme = buildTheme("." + baseThemeID, {
|
|
|
5298
5215
|
"&dark .cm-activeLine": { backgroundColor: "#223039" },
|
|
5299
5216
|
"&light .cm-specialChar": { color: "red" },
|
|
5300
5217
|
"&dark .cm-specialChar": { color: "#f78" },
|
|
5218
|
+
".cm-gutters": {
|
|
5219
|
+
display: "flex",
|
|
5220
|
+
height: "100%",
|
|
5221
|
+
boxSizing: "border-box",
|
|
5222
|
+
left: 0,
|
|
5223
|
+
zIndex: 200
|
|
5224
|
+
},
|
|
5225
|
+
"&light .cm-gutters": {
|
|
5226
|
+
backgroundColor: "#f5f5f5",
|
|
5227
|
+
color: "#6c6c6c",
|
|
5228
|
+
borderRight: "1px solid #ddd"
|
|
5229
|
+
},
|
|
5230
|
+
"&dark .cm-gutters": {
|
|
5231
|
+
backgroundColor: "#333338",
|
|
5232
|
+
color: "#ccc"
|
|
5233
|
+
},
|
|
5234
|
+
".cm-gutter": {
|
|
5235
|
+
display: "flex !important",
|
|
5236
|
+
flexDirection: "column",
|
|
5237
|
+
flexShrink: 0,
|
|
5238
|
+
boxSizing: "border-box",
|
|
5239
|
+
minHeight: "100%",
|
|
5240
|
+
overflow: "hidden"
|
|
5241
|
+
},
|
|
5242
|
+
".cm-gutterElement": {
|
|
5243
|
+
boxSizing: "border-box"
|
|
5244
|
+
},
|
|
5245
|
+
".cm-lineNumbers .cm-gutterElement": {
|
|
5246
|
+
padding: "0 3px 0 5px",
|
|
5247
|
+
minWidth: "20px",
|
|
5248
|
+
textAlign: "right",
|
|
5249
|
+
whiteSpace: "nowrap"
|
|
5250
|
+
},
|
|
5251
|
+
"&light .cm-activeLineGutter": {
|
|
5252
|
+
backgroundColor: "#e2f2ff"
|
|
5253
|
+
},
|
|
5254
|
+
"&dark .cm-activeLineGutter": {
|
|
5255
|
+
backgroundColor: "#222227"
|
|
5256
|
+
},
|
|
5257
|
+
".cm-panels": {
|
|
5258
|
+
boxSizing: "border-box",
|
|
5259
|
+
position: "sticky",
|
|
5260
|
+
left: 0,
|
|
5261
|
+
right: 0
|
|
5262
|
+
},
|
|
5263
|
+
"&light .cm-panels": {
|
|
5264
|
+
backgroundColor: "#f5f5f5",
|
|
5265
|
+
color: "black"
|
|
5266
|
+
},
|
|
5267
|
+
"&light .cm-panels-top": {
|
|
5268
|
+
borderBottom: "1px solid #ddd"
|
|
5269
|
+
},
|
|
5270
|
+
"&light .cm-panels-bottom": {
|
|
5271
|
+
borderTop: "1px solid #ddd"
|
|
5272
|
+
},
|
|
5273
|
+
"&dark .cm-panels": {
|
|
5274
|
+
backgroundColor: "#333338",
|
|
5275
|
+
color: "white"
|
|
5276
|
+
},
|
|
5301
5277
|
".cm-tab": {
|
|
5302
5278
|
display: "inline-block",
|
|
5303
5279
|
overflow: "hidden",
|
|
@@ -5946,9 +5922,9 @@ transactions for editing actions.
|
|
|
5946
5922
|
*/
|
|
5947
5923
|
class EditorView {
|
|
5948
5924
|
/**
|
|
5949
|
-
Construct a new view. You'll
|
|
5950
|
-
|
|
5951
|
-
|
|
5925
|
+
Construct a new view. You'll want to either provide a `parent`
|
|
5926
|
+
option, or put `view.dom` into your document after creating a
|
|
5927
|
+
view, so that the user can see the editor.
|
|
5952
5928
|
*/
|
|
5953
5929
|
constructor(
|
|
5954
5930
|
/**
|
|
@@ -5999,6 +5975,7 @@ class EditorView {
|
|
|
5999
5975
|
this.measure();
|
|
6000
5976
|
});
|
|
6001
5977
|
this.inputState = new InputState(this);
|
|
5978
|
+
this.inputState.ensureHandlers(this, this.plugins);
|
|
6002
5979
|
this.docView = new DocView(this);
|
|
6003
5980
|
this.mountStyles();
|
|
6004
5981
|
this.updateAttrs();
|
|
@@ -6086,14 +6063,9 @@ class EditorView {
|
|
|
6086
6063
|
let { main } = tr.state.selection;
|
|
6087
6064
|
scrollTarget = new ScrollTarget(main.empty ? main : state.EditorSelection.cursor(main.head, main.head > main.anchor ? -1 : 1));
|
|
6088
6065
|
}
|
|
6089
|
-
for (let e of tr.effects)
|
|
6090
|
-
if (e.is(
|
|
6091
|
-
scrollTarget = new ScrollTarget(e.value);
|
|
6092
|
-
else if (e.is(centerOn))
|
|
6093
|
-
scrollTarget = new ScrollTarget(e.value, "center");
|
|
6094
|
-
else if (e.is(scrollIntoView))
|
|
6066
|
+
for (let e of tr.effects)
|
|
6067
|
+
if (e.is(scrollIntoView))
|
|
6095
6068
|
scrollTarget = e.value;
|
|
6096
|
-
}
|
|
6097
6069
|
}
|
|
6098
6070
|
this.viewState.update(update, scrollTarget);
|
|
6099
6071
|
this.bidiCache = CachedOrder.update(this.bidiCache, update.changes);
|
|
@@ -6144,7 +6116,7 @@ class EditorView {
|
|
|
6144
6116
|
for (let plugin of this.plugins)
|
|
6145
6117
|
plugin.update(this);
|
|
6146
6118
|
this.docView = new DocView(this);
|
|
6147
|
-
this.inputState.ensureHandlers(this);
|
|
6119
|
+
this.inputState.ensureHandlers(this, this.plugins);
|
|
6148
6120
|
this.mountStyles();
|
|
6149
6121
|
this.updateAttrs();
|
|
6150
6122
|
this.bidiCache = [];
|
|
@@ -6176,7 +6148,7 @@ class EditorView {
|
|
|
6176
6148
|
plugin.destroy(this);
|
|
6177
6149
|
this.plugins = newPlugins;
|
|
6178
6150
|
this.pluginMap.clear();
|
|
6179
|
-
this.inputState.ensureHandlers(this);
|
|
6151
|
+
this.inputState.ensureHandlers(this, this.plugins);
|
|
6180
6152
|
}
|
|
6181
6153
|
else {
|
|
6182
6154
|
for (let p of this.plugins)
|
|
@@ -6314,7 +6286,7 @@ class EditorView {
|
|
|
6314
6286
|
}
|
|
6315
6287
|
mountStyles() {
|
|
6316
6288
|
this.styleModules = this.state.facet(styleModule);
|
|
6317
|
-
styleMod.StyleModule.mount(this.root, this.styleModules.concat(baseTheme).reverse());
|
|
6289
|
+
styleMod.StyleModule.mount(this.root, this.styleModules.concat(baseTheme$1).reverse());
|
|
6318
6290
|
}
|
|
6319
6291
|
readMeasured() {
|
|
6320
6292
|
if (this.updateState == 2 /* Updating */)
|
|
@@ -6345,16 +6317,6 @@ class EditorView {
|
|
|
6345
6317
|
}
|
|
6346
6318
|
}
|
|
6347
6319
|
/**
|
|
6348
|
-
Collect all values provided by the active plugins for a given
|
|
6349
|
-
field.
|
|
6350
|
-
*/
|
|
6351
|
-
pluginField(field) {
|
|
6352
|
-
let result = [];
|
|
6353
|
-
for (let plugin of this.plugins)
|
|
6354
|
-
plugin.update(this).takeField(field, result);
|
|
6355
|
-
return result;
|
|
6356
|
-
}
|
|
6357
|
-
/**
|
|
6358
6320
|
Get the value of a specific plugin, if present. Note that
|
|
6359
6321
|
plugins that crash can be dropped from a view, so even when you
|
|
6360
6322
|
know you registered a given plugin, it is recommended to check
|
|
@@ -6381,23 +6343,6 @@ class EditorView {
|
|
|
6381
6343
|
return { top: this.viewState.paddingTop, bottom: this.viewState.paddingBottom };
|
|
6382
6344
|
}
|
|
6383
6345
|
/**
|
|
6384
|
-
Find the line or block widget at the given vertical position.
|
|
6385
|
-
|
|
6386
|
-
By default, this position is interpreted as a screen position,
|
|
6387
|
-
meaning `docTop` is set to the DOM top position of the editor
|
|
6388
|
-
content (forcing a layout). You can pass a different `docTop`
|
|
6389
|
-
value—for example 0 to interpret `height` as a document-relative
|
|
6390
|
-
position, or a precomputed document top
|
|
6391
|
-
(`view.contentDOM.getBoundingClientRect().top`) to limit layout
|
|
6392
|
-
queries.
|
|
6393
|
-
|
|
6394
|
-
*Deprecated: use `elementAtHeight` instead.*
|
|
6395
|
-
*/
|
|
6396
|
-
blockAtHeight(height, docTop) {
|
|
6397
|
-
let top = ensureTop(docTop, this);
|
|
6398
|
-
return this.elementAtHeight(height - top).moveY(top);
|
|
6399
|
-
}
|
|
6400
|
-
/**
|
|
6401
6346
|
Find the text line or block widget at the given vertical
|
|
6402
6347
|
position (which is interpreted as relative to the [top of the
|
|
6403
6348
|
document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)
|
|
@@ -6407,23 +6352,6 @@ class EditorView {
|
|
|
6407
6352
|
return this.viewState.elementAtHeight(height);
|
|
6408
6353
|
}
|
|
6409
6354
|
/**
|
|
6410
|
-
Find information for the visual line (see
|
|
6411
|
-
[`visualLineAt`](https://codemirror.net/6/docs/ref/#view.EditorView.visualLineAt)) at the given
|
|
6412
|
-
vertical position. The resulting block info might hold another
|
|
6413
|
-
array of block info structs in its `type` field if this line
|
|
6414
|
-
consists of more than one block.
|
|
6415
|
-
|
|
6416
|
-
Defaults to treating `height` as a screen position. See
|
|
6417
|
-
[`blockAtHeight`](https://codemirror.net/6/docs/ref/#view.EditorView.blockAtHeight) for the
|
|
6418
|
-
interpretation of the `docTop` parameter.
|
|
6419
|
-
|
|
6420
|
-
*Deprecated: use `lineBlockAtHeight` instead.*
|
|
6421
|
-
*/
|
|
6422
|
-
visualLineAtHeight(height, docTop) {
|
|
6423
|
-
let top = ensureTop(docTop, this);
|
|
6424
|
-
return this.lineBlockAtHeight(height - top).moveY(top);
|
|
6425
|
-
}
|
|
6426
|
-
/**
|
|
6427
6355
|
Find the line block (see
|
|
6428
6356
|
[`lineBlockAt`](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) at the given
|
|
6429
6357
|
height.
|
|
@@ -6433,19 +6361,6 @@ class EditorView {
|
|
|
6433
6361
|
return this.viewState.lineBlockAtHeight(height);
|
|
6434
6362
|
}
|
|
6435
6363
|
/**
|
|
6436
|
-
Iterate over the height information of the visual lines in the
|
|
6437
|
-
viewport. The heights of lines are reported relative to the
|
|
6438
|
-
given document top, which defaults to the screen position of the
|
|
6439
|
-
document (forcing a layout).
|
|
6440
|
-
|
|
6441
|
-
*Deprecated: use `viewportLineBlocks` instead.*
|
|
6442
|
-
*/
|
|
6443
|
-
viewportLines(f, docTop) {
|
|
6444
|
-
let top = ensureTop(docTop, this);
|
|
6445
|
-
for (let line of this.viewportLineBlocks)
|
|
6446
|
-
f(line.moveY(top));
|
|
6447
|
-
}
|
|
6448
|
-
/**
|
|
6449
6364
|
Get the extent and vertical position of all [line
|
|
6450
6365
|
blocks](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) in the viewport. Positions
|
|
6451
6366
|
are relative to the [top of the
|
|
@@ -6455,24 +6370,9 @@ class EditorView {
|
|
|
6455
6370
|
return this.viewState.viewportLines;
|
|
6456
6371
|
}
|
|
6457
6372
|
/**
|
|
6458
|
-
Find the extent and height of the visual line (a range delimited
|
|
6459
|
-
on both sides by either non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^range)
|
|
6460
|
-
line breaks, or the start/end of the document) at the given position.
|
|
6461
|
-
|
|
6462
|
-
Vertical positions are computed relative to the `docTop`
|
|
6463
|
-
argument, which defaults to 0 for this method. You can pass
|
|
6464
|
-
`view.contentDOM.getBoundingClientRect().top` here to get screen
|
|
6465
|
-
coordinates.
|
|
6466
|
-
|
|
6467
|
-
*Deprecated: use `lineBlockAt` instead.*
|
|
6468
|
-
*/
|
|
6469
|
-
visualLineAt(pos, docTop = 0) {
|
|
6470
|
-
return this.lineBlockAt(pos).moveY(docTop + this.viewState.paddingTop);
|
|
6471
|
-
}
|
|
6472
|
-
/**
|
|
6473
6373
|
Find the line block around the given document position. A line
|
|
6474
6374
|
block is a range delimited on both sides by either a
|
|
6475
|
-
non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^
|
|
6375
|
+
non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^replace) line breaks, or the
|
|
6476
6376
|
start/end of the document. It will usually just hold a line of
|
|
6477
6377
|
text, but may be broken into multiple textblocks by block
|
|
6478
6378
|
widgets.
|
|
@@ -6488,13 +6388,13 @@ class EditorView {
|
|
|
6488
6388
|
}
|
|
6489
6389
|
/**
|
|
6490
6390
|
Move a cursor position by [grapheme
|
|
6491
|
-
cluster](https://codemirror.net/6/docs/ref/#
|
|
6492
|
-
the motion is away from the line start, or towards it.
|
|
6493
|
-
bidirectional text is in visual order,
|
|
6494
|
-
direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
|
|
6495
|
-
position was the last one on the line, the
|
|
6496
|
-
will be across the line break. If there is no
|
|
6497
|
-
original position is returned.
|
|
6391
|
+
cluster](https://codemirror.net/6/docs/ref/#state.findClusterBreak). `forward` determines whether
|
|
6392
|
+
the motion is away from the line start, or towards it. In
|
|
6393
|
+
bidirectional text, the line is traversed in visual order, using
|
|
6394
|
+
the editor's [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
|
|
6395
|
+
When the start position was the last one on the line, the
|
|
6396
|
+
returned position will be across the line break. If there is no
|
|
6397
|
+
further line, the original position is returned.
|
|
6498
6398
|
|
|
6499
6399
|
By default, this method moves over a single cluster. The
|
|
6500
6400
|
optional `by` argument can be used to move across more. It will
|
|
@@ -6539,10 +6439,6 @@ class EditorView {
|
|
|
6539
6439
|
moveVertically(start, forward, distance) {
|
|
6540
6440
|
return skipAtoms(this, start, moveVertically(this, start, forward, distance));
|
|
6541
6441
|
}
|
|
6542
|
-
// FIXME remove on next major version
|
|
6543
|
-
scrollPosIntoView(pos) {
|
|
6544
|
-
this.dispatch({ effects: scrollTo.of(state.EditorSelection.cursor(pos)) });
|
|
6545
|
-
}
|
|
6546
6442
|
/**
|
|
6547
6443
|
Find the DOM parent node and offset (child offset if `node` is
|
|
6548
6444
|
an element, character offset when it is a text node) at the
|
|
@@ -6598,9 +6494,25 @@ class EditorView {
|
|
|
6598
6494
|
/**
|
|
6599
6495
|
The text direction
|
|
6600
6496
|
([`direction`](https://developer.mozilla.org/en-US/docs/Web/CSS/direction)
|
|
6601
|
-
CSS property) of the editor.
|
|
6497
|
+
CSS property) of the editor's content element.
|
|
6498
|
+
*/
|
|
6499
|
+
get textDirection() { return this.viewState.defaultTextDirection; }
|
|
6500
|
+
/**
|
|
6501
|
+
Find the text direction of the block at the given position, as
|
|
6502
|
+
assigned by CSS. If
|
|
6503
|
+
[`perLineTextDirection`](https://codemirror.net/6/docs/ref/#view.EditorView^perLineTextDirection)
|
|
6504
|
+
isn't enabled, or the given position is outside of the viewport,
|
|
6505
|
+
this will always return the same as
|
|
6506
|
+
[`textDirection`](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection). Note that
|
|
6507
|
+
this may trigger a DOM layout.
|
|
6602
6508
|
*/
|
|
6603
|
-
|
|
6509
|
+
textDirectionAt(pos) {
|
|
6510
|
+
let perLine = this.state.facet(perLineTextDirection);
|
|
6511
|
+
if (!perLine || pos < this.viewport.from || pos > this.viewport.to)
|
|
6512
|
+
return this.textDirection;
|
|
6513
|
+
this.readMeasured();
|
|
6514
|
+
return this.docView.textDirectionAt(pos);
|
|
6515
|
+
}
|
|
6604
6516
|
/**
|
|
6605
6517
|
Whether this editor [wraps lines](https://codemirror.net/6/docs/ref/#view.EditorView.lineWrapping)
|
|
6606
6518
|
(as determined by the
|
|
@@ -6619,11 +6531,11 @@ class EditorView {
|
|
|
6619
6531
|
bidiSpans(line) {
|
|
6620
6532
|
if (line.length > MaxBidiLine)
|
|
6621
6533
|
return trivialOrder(line.length);
|
|
6622
|
-
let dir = this.
|
|
6534
|
+
let dir = this.textDirectionAt(line.from);
|
|
6623
6535
|
for (let entry of this.bidiCache)
|
|
6624
6536
|
if (entry.from == line.from && entry.dir == dir)
|
|
6625
6537
|
return entry.order;
|
|
6626
|
-
let order = computeOrder(line.text,
|
|
6538
|
+
let order = computeOrder(line.text, dir);
|
|
6627
6539
|
this.bidiCache.push(new CachedOrder(line.from, line.to, dir, order));
|
|
6628
6540
|
return order;
|
|
6629
6541
|
}
|
|
@@ -6674,16 +6586,16 @@ class EditorView {
|
|
|
6674
6586
|
return scrollIntoView.of(new ScrollTarget(typeof pos == "number" ? state.EditorSelection.cursor(pos) : pos, options.y, options.x, options.yMargin, options.xMargin));
|
|
6675
6587
|
}
|
|
6676
6588
|
/**
|
|
6677
|
-
|
|
6678
|
-
should be an object mapping event names to handler
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
These are registered
|
|
6683
|
-
element](https://codemirror.net/6/docs/ref/#view.EditorView.contentDOM), except
|
|
6684
|
-
handlers, which will be called any time the
|
|
6685
|
-
element](https://codemirror.net/6/docs/ref/#view.EditorView.scrollDOM) or one of
|
|
6686
|
-
is scrolled.
|
|
6589
|
+
Returns an extension that can be used to add DOM event handlers.
|
|
6590
|
+
The value should be an object mapping event names to handler
|
|
6591
|
+
functions. For any given event, such functions are ordered by
|
|
6592
|
+
extension precedence, and the first handler to return true will
|
|
6593
|
+
be assumed to have handled that event, and no other handlers or
|
|
6594
|
+
built-in behavior will be activated for it. These are registered
|
|
6595
|
+
on the [content element](https://codemirror.net/6/docs/ref/#view.EditorView.contentDOM), except
|
|
6596
|
+
for `scroll` handlers, which will be called any time the
|
|
6597
|
+
editor's [scroll element](https://codemirror.net/6/docs/ref/#view.EditorView.scrollDOM) or one of
|
|
6598
|
+
its parent nodes is scrolled.
|
|
6687
6599
|
*/
|
|
6688
6600
|
static domEventHandlers(handlers) {
|
|
6689
6601
|
return ViewPlugin.define(() => ({}), { eventHandlers: handlers });
|
|
@@ -6725,20 +6637,6 @@ class EditorView {
|
|
|
6725
6637
|
}
|
|
6726
6638
|
}
|
|
6727
6639
|
/**
|
|
6728
|
-
Effect that can be [added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a
|
|
6729
|
-
transaction to make it scroll the given range into view.
|
|
6730
|
-
|
|
6731
|
-
*Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead.
|
|
6732
|
-
*/
|
|
6733
|
-
EditorView.scrollTo = scrollTo;
|
|
6734
|
-
/**
|
|
6735
|
-
Effect that makes the editor scroll the given range to the
|
|
6736
|
-
center of the visible view.
|
|
6737
|
-
|
|
6738
|
-
*Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead.
|
|
6739
|
-
*/
|
|
6740
|
-
EditorView.centerOn = centerOn;
|
|
6741
|
-
/**
|
|
6742
6640
|
Facet to add a [style
|
|
6743
6641
|
module](https://github.com/marijnh/style-mod#documentation) to
|
|
6744
6642
|
an editor view. The view will ensure that the module is
|
|
@@ -6755,6 +6653,13 @@ called and the default behavior is prevented.
|
|
|
6755
6653
|
*/
|
|
6756
6654
|
EditorView.inputHandler = inputHandler;
|
|
6757
6655
|
/**
|
|
6656
|
+
By default, the editor assumes all its content has the same
|
|
6657
|
+
[text direction](https://codemirror.net/6/docs/ref/#view.Direction). Configure this with a `true`
|
|
6658
|
+
value to make it read and store the text direction of every
|
|
6659
|
+
(rendered) line separately.
|
|
6660
|
+
*/
|
|
6661
|
+
EditorView.perLineTextDirection = perLineTextDirection;
|
|
6662
|
+
/**
|
|
6758
6663
|
Allows you to provide a function that should be called when the
|
|
6759
6664
|
library catches an exception from an extension (mostly from view
|
|
6760
6665
|
plugins, but may be used by other extensions to route exceptions
|
|
@@ -6770,9 +6675,9 @@ EditorView.updateListener = updateListener;
|
|
|
6770
6675
|
/**
|
|
6771
6676
|
Facet that controls whether the editor content DOM is editable.
|
|
6772
6677
|
When its highest-precedence value is `false`, the element will
|
|
6773
|
-
not
|
|
6774
|
-
|
|
6775
|
-
|
|
6678
|
+
not have its `contenteditable` attribute set. (Note that this
|
|
6679
|
+
doesn't affect API calls that change the editor content, even
|
|
6680
|
+
when those are bound to keys or buttons. See the
|
|
6776
6681
|
[`readOnly`](https://codemirror.net/6/docs/ref/#state.EditorState.readOnly) facet for that.)
|
|
6777
6682
|
*/
|
|
6778
6683
|
EditorView.editable = editable;
|
|
@@ -6791,18 +6696,45 @@ the drag should move the content.
|
|
|
6791
6696
|
*/
|
|
6792
6697
|
EditorView.dragMovesSelection = dragMovesSelection$1;
|
|
6793
6698
|
/**
|
|
6794
|
-
Facet used to configure whether a given selecting click adds
|
|
6795
|
-
|
|
6699
|
+
Facet used to configure whether a given selecting click adds a
|
|
6700
|
+
new range to the existing selection or replaces it entirely. The
|
|
6701
|
+
default behavior is to check `event.metaKey` on macOS, and
|
|
6702
|
+
`event.ctrlKey` elsewhere.
|
|
6796
6703
|
*/
|
|
6797
6704
|
EditorView.clickAddsSelectionRange = clickAddsSelectionRange;
|
|
6798
6705
|
/**
|
|
6799
6706
|
A facet that determines which [decorations](https://codemirror.net/6/docs/ref/#view.Decoration)
|
|
6800
|
-
are shown in the view.
|
|
6801
|
-
|
|
6802
|
-
|
|
6707
|
+
are shown in the view. Decorations can be provided in two
|
|
6708
|
+
ways—directly, or via a function that takes an editor view.
|
|
6709
|
+
|
|
6710
|
+
Only decoration sets provided directly are allowed to influence
|
|
6711
|
+
the editor's vertical layout structure. The ones provided as
|
|
6712
|
+
functions are called _after_ the new viewport has been computed,
|
|
6713
|
+
and thus **must not** introduce block widgets or replacing
|
|
6714
|
+
decorations that cover line breaks.
|
|
6803
6715
|
*/
|
|
6804
6716
|
EditorView.decorations = decorations;
|
|
6805
6717
|
/**
|
|
6718
|
+
Used to provide ranges that should be treated as atoms as far as
|
|
6719
|
+
cursor motion is concerned. This causes methods like
|
|
6720
|
+
[`moveByChar`](https://codemirror.net/6/docs/ref/#view.EditorView.moveByChar) and
|
|
6721
|
+
[`moveVertically`](https://codemirror.net/6/docs/ref/#view.EditorView.moveVertically) (and the
|
|
6722
|
+
commands built on top of them) to skip across such regions when
|
|
6723
|
+
a selection endpoint would enter them. This does _not_ prevent
|
|
6724
|
+
direct programmatic [selection
|
|
6725
|
+
updates](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection) from moving into such
|
|
6726
|
+
regions.
|
|
6727
|
+
*/
|
|
6728
|
+
EditorView.atomicRanges = atomicRanges;
|
|
6729
|
+
/**
|
|
6730
|
+
Facet that allows extensions to provide additional scroll
|
|
6731
|
+
margins (space around the sides of the scrolling element that
|
|
6732
|
+
should be considered invisible). This can be useful when the
|
|
6733
|
+
plugin introduces elements that cover part of that element (for
|
|
6734
|
+
example a horizontally fixed gutter).
|
|
6735
|
+
*/
|
|
6736
|
+
EditorView.scrollMargins = scrollMargins;
|
|
6737
|
+
/**
|
|
6806
6738
|
This facet records whether a dark theme is active. The extension
|
|
6807
6739
|
returned by [`theme`](https://codemirror.net/6/docs/ref/#view.EditorView^theme) automatically
|
|
6808
6740
|
includes an instance of this when the `dark` option is set to
|
|
@@ -6835,10 +6767,6 @@ search match).
|
|
|
6835
6767
|
EditorView.announce = state.StateEffect.define();
|
|
6836
6768
|
// Maximum line length for which we compute accurate bidi info
|
|
6837
6769
|
const MaxBidiLine = 4096;
|
|
6838
|
-
// FIXME remove this and its callers on next breaking release
|
|
6839
|
-
function ensureTop(given, view) {
|
|
6840
|
-
return (given == null ? view.contentDOM.getBoundingClientRect().top : given) + view.viewState.paddingTop;
|
|
6841
|
-
}
|
|
6842
6770
|
const BadMeasure = {};
|
|
6843
6771
|
class CachedOrder {
|
|
6844
6772
|
constructor(from, to, dir, order) {
|
|
@@ -6941,7 +6869,7 @@ function getKeymap(state) {
|
|
|
6941
6869
|
}
|
|
6942
6870
|
/**
|
|
6943
6871
|
Run the key handlers registered for a given scope. The event
|
|
6944
|
-
object should be `"keydown"` event. Returns true if any of the
|
|
6872
|
+
object should be a `"keydown"` event. Returns true if any of the
|
|
6945
6873
|
handlers handled it.
|
|
6946
6874
|
*/
|
|
6947
6875
|
function runScopeHandlers(view, event, scope) {
|
|
@@ -7421,7 +7349,7 @@ class MatchDecorator {
|
|
|
7421
7349
|
plugin.
|
|
7422
7350
|
*/
|
|
7423
7351
|
createDeco(view) {
|
|
7424
|
-
let build = new
|
|
7352
|
+
let build = new state.RangeSetBuilder();
|
|
7425
7353
|
for (let { from, to } of matchRanges(view, this.maxLength))
|
|
7426
7354
|
iterMatches(view.state.doc, this.regexp, from, to, (a, b, m) => build.add(a, b, this.getDeco(m, view, a)));
|
|
7427
7355
|
return build.finish();
|
|
@@ -7553,10 +7481,10 @@ function specialCharPlugin() {
|
|
|
7553
7481
|
regexp: conf.specialChars,
|
|
7554
7482
|
decoration: (m, view, pos) => {
|
|
7555
7483
|
let { doc } = view.state;
|
|
7556
|
-
let code =
|
|
7484
|
+
let code = state.codePointAt(m[0], 0);
|
|
7557
7485
|
if (code == 9) {
|
|
7558
7486
|
let line = doc.lineAt(pos);
|
|
7559
|
-
let size = view.state.tabSize, col =
|
|
7487
|
+
let size = view.state.tabSize, col = state.countColumn(line.text, size, pos - line.from);
|
|
7560
7488
|
return Decoration.replace({ widget: new TabWidget((size - (col % size)) * this.view.defaultCharacterWidth) });
|
|
7561
7489
|
}
|
|
7562
7490
|
return this.decorationCache[code] ||
|
|
@@ -7718,32 +7646,1338 @@ function placeholder(content) {
|
|
|
7718
7646
|
}, { decorations: v => v.decorations });
|
|
7719
7647
|
}
|
|
7720
7648
|
|
|
7649
|
+
// Don't compute precise column positions for line offsets above this
|
|
7650
|
+
// (since it could get expensive). Assume offset==column for them.
|
|
7651
|
+
const MaxOff = 2000;
|
|
7652
|
+
function rectangleFor(state$1, a, b) {
|
|
7653
|
+
let startLine = Math.min(a.line, b.line), endLine = Math.max(a.line, b.line);
|
|
7654
|
+
let ranges = [];
|
|
7655
|
+
if (a.off > MaxOff || b.off > MaxOff || a.col < 0 || b.col < 0) {
|
|
7656
|
+
let startOff = Math.min(a.off, b.off), endOff = Math.max(a.off, b.off);
|
|
7657
|
+
for (let i = startLine; i <= endLine; i++) {
|
|
7658
|
+
let line = state$1.doc.line(i);
|
|
7659
|
+
if (line.length <= endOff)
|
|
7660
|
+
ranges.push(state.EditorSelection.range(line.from + startOff, line.to + endOff));
|
|
7661
|
+
}
|
|
7662
|
+
}
|
|
7663
|
+
else {
|
|
7664
|
+
let startCol = Math.min(a.col, b.col), endCol = Math.max(a.col, b.col);
|
|
7665
|
+
for (let i = startLine; i <= endLine; i++) {
|
|
7666
|
+
let line = state$1.doc.line(i);
|
|
7667
|
+
let start = state.findColumn(line.text, startCol, state$1.tabSize, true);
|
|
7668
|
+
if (start > -1) {
|
|
7669
|
+
let end = state.findColumn(line.text, endCol, state$1.tabSize);
|
|
7670
|
+
ranges.push(state.EditorSelection.range(line.from + start, line.from + end));
|
|
7671
|
+
}
|
|
7672
|
+
}
|
|
7673
|
+
}
|
|
7674
|
+
return ranges;
|
|
7675
|
+
}
|
|
7676
|
+
function absoluteColumn(view, x) {
|
|
7677
|
+
let ref = view.coordsAtPos(view.viewport.from);
|
|
7678
|
+
return ref ? Math.round(Math.abs((ref.left - x) / view.defaultCharacterWidth)) : -1;
|
|
7679
|
+
}
|
|
7680
|
+
function getPos(view, event) {
|
|
7681
|
+
let offset = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
|
|
7682
|
+
let line = view.state.doc.lineAt(offset), off = offset - line.from;
|
|
7683
|
+
let col = off > MaxOff ? -1
|
|
7684
|
+
: off == line.length ? absoluteColumn(view, event.clientX)
|
|
7685
|
+
: state.countColumn(line.text, view.state.tabSize, offset - line.from);
|
|
7686
|
+
return { line: line.number, col, off };
|
|
7687
|
+
}
|
|
7688
|
+
function rectangleSelectionStyle(view, event) {
|
|
7689
|
+
let start = getPos(view, event), startSel = view.state.selection;
|
|
7690
|
+
if (!start)
|
|
7691
|
+
return null;
|
|
7692
|
+
return {
|
|
7693
|
+
update(update) {
|
|
7694
|
+
if (update.docChanged) {
|
|
7695
|
+
let newStart = update.changes.mapPos(update.startState.doc.line(start.line).from);
|
|
7696
|
+
let newLine = update.state.doc.lineAt(newStart);
|
|
7697
|
+
start = { line: newLine.number, col: start.col, off: Math.min(start.off, newLine.length) };
|
|
7698
|
+
startSel = startSel.map(update.changes);
|
|
7699
|
+
}
|
|
7700
|
+
},
|
|
7701
|
+
get(event, _extend, multiple) {
|
|
7702
|
+
let cur = getPos(view, event);
|
|
7703
|
+
if (!cur)
|
|
7704
|
+
return startSel;
|
|
7705
|
+
let ranges = rectangleFor(view.state, start, cur);
|
|
7706
|
+
if (!ranges.length)
|
|
7707
|
+
return startSel;
|
|
7708
|
+
if (multiple)
|
|
7709
|
+
return state.EditorSelection.create(ranges.concat(startSel.ranges));
|
|
7710
|
+
else
|
|
7711
|
+
return state.EditorSelection.create(ranges);
|
|
7712
|
+
}
|
|
7713
|
+
};
|
|
7714
|
+
}
|
|
7715
|
+
/**
|
|
7716
|
+
Create an extension that enables rectangular selections. By
|
|
7717
|
+
default, it will react to left mouse drag with the Alt key held
|
|
7718
|
+
down. When such a selection occurs, the text within the rectangle
|
|
7719
|
+
that was dragged over will be selected, as one selection
|
|
7720
|
+
[range](https://codemirror.net/6/docs/ref/#state.SelectionRange) per line.
|
|
7721
|
+
*/
|
|
7722
|
+
function rectangularSelection(options) {
|
|
7723
|
+
let filter = (options === null || options === void 0 ? void 0 : options.eventFilter) || (e => e.altKey && e.button == 0);
|
|
7724
|
+
return EditorView.mouseSelectionStyle.of((view, event) => filter(event) ? rectangleSelectionStyle(view, event) : null);
|
|
7725
|
+
}
|
|
7726
|
+
const keys = {
|
|
7727
|
+
Alt: [18, e => e.altKey],
|
|
7728
|
+
Control: [17, e => e.ctrlKey],
|
|
7729
|
+
Shift: [16, e => e.shiftKey],
|
|
7730
|
+
Meta: [91, e => e.metaKey]
|
|
7731
|
+
};
|
|
7732
|
+
const showCrosshair = { style: "cursor: crosshair" };
|
|
7733
|
+
/**
|
|
7734
|
+
Returns an extension that turns the pointer cursor into a
|
|
7735
|
+
crosshair when a given modifier key, defaulting to Alt, is held
|
|
7736
|
+
down. Can serve as a visual hint that rectangular selection is
|
|
7737
|
+
going to happen when paired with
|
|
7738
|
+
[`rectangularSelection`](https://codemirror.net/6/docs/ref/#view.rectangularSelection).
|
|
7739
|
+
*/
|
|
7740
|
+
function crosshairCursor(options = {}) {
|
|
7741
|
+
let [code, getter] = keys[options.key || "Alt"];
|
|
7742
|
+
let plugin = ViewPlugin.fromClass(class {
|
|
7743
|
+
constructor(view) {
|
|
7744
|
+
this.view = view;
|
|
7745
|
+
this.isDown = false;
|
|
7746
|
+
}
|
|
7747
|
+
set(isDown) {
|
|
7748
|
+
if (this.isDown != isDown) {
|
|
7749
|
+
this.isDown = isDown;
|
|
7750
|
+
this.view.update([]);
|
|
7751
|
+
}
|
|
7752
|
+
}
|
|
7753
|
+
}, {
|
|
7754
|
+
eventHandlers: {
|
|
7755
|
+
keydown(e) {
|
|
7756
|
+
this.set(e.keyCode == code || getter(e));
|
|
7757
|
+
},
|
|
7758
|
+
keyup(e) {
|
|
7759
|
+
if (e.keyCode == code || !getter(e))
|
|
7760
|
+
this.set(false);
|
|
7761
|
+
}
|
|
7762
|
+
}
|
|
7763
|
+
});
|
|
7764
|
+
return [
|
|
7765
|
+
plugin,
|
|
7766
|
+
EditorView.contentAttributes.of(view => { var _a; return ((_a = view.plugin(plugin)) === null || _a === void 0 ? void 0 : _a.isDown) ? showCrosshair : null; })
|
|
7767
|
+
];
|
|
7768
|
+
}
|
|
7769
|
+
|
|
7770
|
+
const Outside = "-10000px";
|
|
7771
|
+
class TooltipViewManager {
|
|
7772
|
+
constructor(view, facet, createTooltipView) {
|
|
7773
|
+
this.facet = facet;
|
|
7774
|
+
this.createTooltipView = createTooltipView;
|
|
7775
|
+
this.input = view.state.facet(facet);
|
|
7776
|
+
this.tooltips = this.input.filter(t => t);
|
|
7777
|
+
this.tooltipViews = this.tooltips.map(createTooltipView);
|
|
7778
|
+
}
|
|
7779
|
+
update(update) {
|
|
7780
|
+
let input = update.state.facet(this.facet);
|
|
7781
|
+
let tooltips = input.filter(x => x);
|
|
7782
|
+
if (input === this.input) {
|
|
7783
|
+
for (let t of this.tooltipViews)
|
|
7784
|
+
if (t.update)
|
|
7785
|
+
t.update(update);
|
|
7786
|
+
return false;
|
|
7787
|
+
}
|
|
7788
|
+
let tooltipViews = [];
|
|
7789
|
+
for (let i = 0; i < tooltips.length; i++) {
|
|
7790
|
+
let tip = tooltips[i], known = -1;
|
|
7791
|
+
if (!tip)
|
|
7792
|
+
continue;
|
|
7793
|
+
for (let i = 0; i < this.tooltips.length; i++) {
|
|
7794
|
+
let other = this.tooltips[i];
|
|
7795
|
+
if (other && other.create == tip.create)
|
|
7796
|
+
known = i;
|
|
7797
|
+
}
|
|
7798
|
+
if (known < 0) {
|
|
7799
|
+
tooltipViews[i] = this.createTooltipView(tip);
|
|
7800
|
+
}
|
|
7801
|
+
else {
|
|
7802
|
+
let tooltipView = tooltipViews[i] = this.tooltipViews[known];
|
|
7803
|
+
if (tooltipView.update)
|
|
7804
|
+
tooltipView.update(update);
|
|
7805
|
+
}
|
|
7806
|
+
}
|
|
7807
|
+
for (let t of this.tooltipViews)
|
|
7808
|
+
if (tooltipViews.indexOf(t) < 0)
|
|
7809
|
+
t.dom.remove();
|
|
7810
|
+
this.input = input;
|
|
7811
|
+
this.tooltips = tooltips;
|
|
7812
|
+
this.tooltipViews = tooltipViews;
|
|
7813
|
+
return true;
|
|
7814
|
+
}
|
|
7815
|
+
}
|
|
7816
|
+
/**
|
|
7817
|
+
Creates an extension that configures tooltip behavior.
|
|
7818
|
+
*/
|
|
7819
|
+
function tooltips(config = {}) {
|
|
7820
|
+
return tooltipConfig.of(config);
|
|
7821
|
+
}
|
|
7822
|
+
function windowSpace() {
|
|
7823
|
+
return { top: 0, left: 0, bottom: innerHeight, right: innerWidth };
|
|
7824
|
+
}
|
|
7825
|
+
const tooltipConfig = state.Facet.define({
|
|
7826
|
+
combine: values => {
|
|
7827
|
+
var _a, _b, _c;
|
|
7828
|
+
return ({
|
|
7829
|
+
position: browser.ios ? "absolute" : ((_a = values.find(conf => conf.position)) === null || _a === void 0 ? void 0 : _a.position) || "fixed",
|
|
7830
|
+
parent: ((_b = values.find(conf => conf.parent)) === null || _b === void 0 ? void 0 : _b.parent) || null,
|
|
7831
|
+
tooltipSpace: ((_c = values.find(conf => conf.tooltipSpace)) === null || _c === void 0 ? void 0 : _c.tooltipSpace) || windowSpace,
|
|
7832
|
+
});
|
|
7833
|
+
}
|
|
7834
|
+
});
|
|
7835
|
+
const tooltipPlugin = ViewPlugin.fromClass(class {
|
|
7836
|
+
constructor(view) {
|
|
7837
|
+
var _a;
|
|
7838
|
+
this.view = view;
|
|
7839
|
+
this.inView = true;
|
|
7840
|
+
this.lastTransaction = 0;
|
|
7841
|
+
this.measureTimeout = -1;
|
|
7842
|
+
let config = view.state.facet(tooltipConfig);
|
|
7843
|
+
this.position = config.position;
|
|
7844
|
+
this.parent = config.parent;
|
|
7845
|
+
this.classes = view.themeClasses;
|
|
7846
|
+
this.createContainer();
|
|
7847
|
+
this.measureReq = { read: this.readMeasure.bind(this), write: this.writeMeasure.bind(this), key: this };
|
|
7848
|
+
this.manager = new TooltipViewManager(view, showTooltip, t => this.createTooltip(t));
|
|
7849
|
+
this.intersectionObserver = typeof IntersectionObserver == "function" ? new IntersectionObserver(entries => {
|
|
7850
|
+
if (Date.now() > this.lastTransaction - 50 &&
|
|
7851
|
+
entries.length > 0 && entries[entries.length - 1].intersectionRatio < 1)
|
|
7852
|
+
this.measureSoon();
|
|
7853
|
+
}, { threshold: [1] }) : null;
|
|
7854
|
+
this.observeIntersection();
|
|
7855
|
+
(_a = view.dom.ownerDocument.defaultView) === null || _a === void 0 ? void 0 : _a.addEventListener("resize", this.measureSoon = this.measureSoon.bind(this));
|
|
7856
|
+
this.maybeMeasure();
|
|
7857
|
+
}
|
|
7858
|
+
createContainer() {
|
|
7859
|
+
if (this.parent) {
|
|
7860
|
+
this.container = document.createElement("div");
|
|
7861
|
+
this.container.style.position = "relative";
|
|
7862
|
+
this.container.className = this.view.themeClasses;
|
|
7863
|
+
this.parent.appendChild(this.container);
|
|
7864
|
+
}
|
|
7865
|
+
else {
|
|
7866
|
+
this.container = this.view.dom;
|
|
7867
|
+
}
|
|
7868
|
+
}
|
|
7869
|
+
observeIntersection() {
|
|
7870
|
+
if (this.intersectionObserver) {
|
|
7871
|
+
this.intersectionObserver.disconnect();
|
|
7872
|
+
for (let tooltip of this.manager.tooltipViews)
|
|
7873
|
+
this.intersectionObserver.observe(tooltip.dom);
|
|
7874
|
+
}
|
|
7875
|
+
}
|
|
7876
|
+
measureSoon() {
|
|
7877
|
+
if (this.measureTimeout < 0)
|
|
7878
|
+
this.measureTimeout = setTimeout(() => {
|
|
7879
|
+
this.measureTimeout = -1;
|
|
7880
|
+
this.maybeMeasure();
|
|
7881
|
+
}, 50);
|
|
7882
|
+
}
|
|
7883
|
+
update(update) {
|
|
7884
|
+
if (update.transactions.length)
|
|
7885
|
+
this.lastTransaction = Date.now();
|
|
7886
|
+
let updated = this.manager.update(update);
|
|
7887
|
+
if (updated)
|
|
7888
|
+
this.observeIntersection();
|
|
7889
|
+
let shouldMeasure = updated || update.geometryChanged;
|
|
7890
|
+
let newConfig = update.state.facet(tooltipConfig);
|
|
7891
|
+
if (newConfig.position != this.position) {
|
|
7892
|
+
this.position = newConfig.position;
|
|
7893
|
+
for (let t of this.manager.tooltipViews)
|
|
7894
|
+
t.dom.style.position = this.position;
|
|
7895
|
+
shouldMeasure = true;
|
|
7896
|
+
}
|
|
7897
|
+
if (newConfig.parent != this.parent) {
|
|
7898
|
+
if (this.parent)
|
|
7899
|
+
this.container.remove();
|
|
7900
|
+
this.parent = newConfig.parent;
|
|
7901
|
+
this.createContainer();
|
|
7902
|
+
for (let t of this.manager.tooltipViews)
|
|
7903
|
+
this.container.appendChild(t.dom);
|
|
7904
|
+
shouldMeasure = true;
|
|
7905
|
+
}
|
|
7906
|
+
else if (this.parent && this.view.themeClasses != this.classes) {
|
|
7907
|
+
this.classes = this.container.className = this.view.themeClasses;
|
|
7908
|
+
}
|
|
7909
|
+
if (shouldMeasure)
|
|
7910
|
+
this.maybeMeasure();
|
|
7911
|
+
}
|
|
7912
|
+
createTooltip(tooltip) {
|
|
7913
|
+
let tooltipView = tooltip.create(this.view);
|
|
7914
|
+
tooltipView.dom.classList.add("cm-tooltip");
|
|
7915
|
+
if (tooltip.arrow && !tooltipView.dom.querySelector(".cm-tooltip > .cm-tooltip-arrow")) {
|
|
7916
|
+
let arrow = document.createElement("div");
|
|
7917
|
+
arrow.className = "cm-tooltip-arrow";
|
|
7918
|
+
tooltipView.dom.appendChild(arrow);
|
|
7919
|
+
}
|
|
7920
|
+
tooltipView.dom.style.position = this.position;
|
|
7921
|
+
tooltipView.dom.style.top = Outside;
|
|
7922
|
+
this.container.appendChild(tooltipView.dom);
|
|
7923
|
+
if (tooltipView.mount)
|
|
7924
|
+
tooltipView.mount(this.view);
|
|
7925
|
+
return tooltipView;
|
|
7926
|
+
}
|
|
7927
|
+
destroy() {
|
|
7928
|
+
var _a, _b;
|
|
7929
|
+
(_a = this.view.dom.ownerDocument.defaultView) === null || _a === void 0 ? void 0 : _a.removeEventListener("resize", this.measureSoon);
|
|
7930
|
+
for (let { dom } of this.manager.tooltipViews)
|
|
7931
|
+
dom.remove();
|
|
7932
|
+
(_b = this.intersectionObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
|
|
7933
|
+
clearTimeout(this.measureTimeout);
|
|
7934
|
+
}
|
|
7935
|
+
readMeasure() {
|
|
7936
|
+
let editor = this.view.dom.getBoundingClientRect();
|
|
7937
|
+
return {
|
|
7938
|
+
editor,
|
|
7939
|
+
parent: this.parent ? this.container.getBoundingClientRect() : editor,
|
|
7940
|
+
pos: this.manager.tooltips.map((t, i) => {
|
|
7941
|
+
let tv = this.manager.tooltipViews[i];
|
|
7942
|
+
return tv.getCoords ? tv.getCoords(t.pos) : this.view.coordsAtPos(t.pos);
|
|
7943
|
+
}),
|
|
7944
|
+
size: this.manager.tooltipViews.map(({ dom }) => dom.getBoundingClientRect()),
|
|
7945
|
+
space: this.view.state.facet(tooltipConfig).tooltipSpace(this.view),
|
|
7946
|
+
};
|
|
7947
|
+
}
|
|
7948
|
+
writeMeasure(measured) {
|
|
7949
|
+
let { editor, space } = measured;
|
|
7950
|
+
let others = [];
|
|
7951
|
+
for (let i = 0; i < this.manager.tooltips.length; i++) {
|
|
7952
|
+
let tooltip = this.manager.tooltips[i], tView = this.manager.tooltipViews[i], { dom } = tView;
|
|
7953
|
+
let pos = measured.pos[i], size = measured.size[i];
|
|
7954
|
+
// Hide tooltips that are outside of the editor.
|
|
7955
|
+
if (!pos || pos.bottom <= Math.max(editor.top, space.top) ||
|
|
7956
|
+
pos.top >= Math.min(editor.bottom, space.bottom) ||
|
|
7957
|
+
pos.right < Math.max(editor.left, space.left) - .1 ||
|
|
7958
|
+
pos.left > Math.min(editor.right, space.right) + .1) {
|
|
7959
|
+
dom.style.top = Outside;
|
|
7960
|
+
continue;
|
|
7961
|
+
}
|
|
7962
|
+
let arrow = tooltip.arrow ? tView.dom.querySelector(".cm-tooltip-arrow") : null;
|
|
7963
|
+
let arrowHeight = arrow ? 7 /* Size */ : 0;
|
|
7964
|
+
let width = size.right - size.left, height = size.bottom - size.top;
|
|
7965
|
+
let offset = tView.offset || noOffset, ltr = this.view.textDirection == exports.Direction.LTR;
|
|
7966
|
+
let left = size.width > space.right - space.left ? (ltr ? space.left : space.right - size.width)
|
|
7967
|
+
: ltr ? Math.min(pos.left - (arrow ? 14 /* Offset */ : 0) + offset.x, space.right - width)
|
|
7968
|
+
: Math.max(space.left, pos.left - width + (arrow ? 14 /* Offset */ : 0) - offset.x);
|
|
7969
|
+
let above = !!tooltip.above;
|
|
7970
|
+
if (!tooltip.strictSide && (above
|
|
7971
|
+
? pos.top - (size.bottom - size.top) - offset.y < space.top
|
|
7972
|
+
: pos.bottom + (size.bottom - size.top) + offset.y > space.bottom) &&
|
|
7973
|
+
above == (space.bottom - pos.bottom > pos.top - space.top))
|
|
7974
|
+
above = !above;
|
|
7975
|
+
let top = above ? pos.top - height - arrowHeight - offset.y : pos.bottom + arrowHeight + offset.y;
|
|
7976
|
+
let right = left + width;
|
|
7977
|
+
if (tView.overlap !== true)
|
|
7978
|
+
for (let r of others)
|
|
7979
|
+
if (r.left < right && r.right > left && r.top < top + height && r.bottom > top)
|
|
7980
|
+
top = above ? r.top - height - 2 - arrowHeight : r.bottom + arrowHeight + 2;
|
|
7981
|
+
if (this.position == "absolute") {
|
|
7982
|
+
dom.style.top = (top - measured.parent.top) + "px";
|
|
7983
|
+
dom.style.left = (left - measured.parent.left) + "px";
|
|
7984
|
+
}
|
|
7985
|
+
else {
|
|
7986
|
+
dom.style.top = top + "px";
|
|
7987
|
+
dom.style.left = left + "px";
|
|
7988
|
+
}
|
|
7989
|
+
if (arrow)
|
|
7990
|
+
arrow.style.left = `${pos.left + (ltr ? offset.x : -offset.x) - (left + 14 /* Offset */ - 7 /* Size */)}px`;
|
|
7991
|
+
if (tView.overlap !== true)
|
|
7992
|
+
others.push({ left, top, right, bottom: top + height });
|
|
7993
|
+
dom.classList.toggle("cm-tooltip-above", above);
|
|
7994
|
+
dom.classList.toggle("cm-tooltip-below", !above);
|
|
7995
|
+
if (tView.positioned)
|
|
7996
|
+
tView.positioned();
|
|
7997
|
+
}
|
|
7998
|
+
}
|
|
7999
|
+
maybeMeasure() {
|
|
8000
|
+
if (this.manager.tooltips.length) {
|
|
8001
|
+
if (this.view.inView)
|
|
8002
|
+
this.view.requestMeasure(this.measureReq);
|
|
8003
|
+
if (this.inView != this.view.inView) {
|
|
8004
|
+
this.inView = this.view.inView;
|
|
8005
|
+
if (!this.inView)
|
|
8006
|
+
for (let tv of this.manager.tooltipViews)
|
|
8007
|
+
tv.dom.style.top = Outside;
|
|
8008
|
+
}
|
|
8009
|
+
}
|
|
8010
|
+
}
|
|
8011
|
+
}, {
|
|
8012
|
+
eventHandlers: {
|
|
8013
|
+
scroll() { this.maybeMeasure(); }
|
|
8014
|
+
}
|
|
8015
|
+
});
|
|
8016
|
+
const baseTheme = EditorView.baseTheme({
|
|
8017
|
+
".cm-tooltip": {
|
|
8018
|
+
zIndex: 100
|
|
8019
|
+
},
|
|
8020
|
+
"&light .cm-tooltip": {
|
|
8021
|
+
border: "1px solid #bbb",
|
|
8022
|
+
backgroundColor: "#f5f5f5"
|
|
8023
|
+
},
|
|
8024
|
+
"&light .cm-tooltip-section:not(:first-child)": {
|
|
8025
|
+
borderTop: "1px solid #bbb",
|
|
8026
|
+
},
|
|
8027
|
+
"&dark .cm-tooltip": {
|
|
8028
|
+
backgroundColor: "#333338",
|
|
8029
|
+
color: "white"
|
|
8030
|
+
},
|
|
8031
|
+
".cm-tooltip-arrow": {
|
|
8032
|
+
height: `${7 /* Size */}px`,
|
|
8033
|
+
width: `${7 /* Size */ * 2}px`,
|
|
8034
|
+
position: "absolute",
|
|
8035
|
+
zIndex: -1,
|
|
8036
|
+
overflow: "hidden",
|
|
8037
|
+
"&:before, &:after": {
|
|
8038
|
+
content: "''",
|
|
8039
|
+
position: "absolute",
|
|
8040
|
+
width: 0,
|
|
8041
|
+
height: 0,
|
|
8042
|
+
borderLeft: `${7 /* Size */}px solid transparent`,
|
|
8043
|
+
borderRight: `${7 /* Size */}px solid transparent`,
|
|
8044
|
+
},
|
|
8045
|
+
".cm-tooltip-above &": {
|
|
8046
|
+
bottom: `-${7 /* Size */}px`,
|
|
8047
|
+
"&:before": {
|
|
8048
|
+
borderTop: `${7 /* Size */}px solid #bbb`,
|
|
8049
|
+
},
|
|
8050
|
+
"&:after": {
|
|
8051
|
+
borderTop: `${7 /* Size */}px solid #f5f5f5`,
|
|
8052
|
+
bottom: "1px"
|
|
8053
|
+
}
|
|
8054
|
+
},
|
|
8055
|
+
".cm-tooltip-below &": {
|
|
8056
|
+
top: `-${7 /* Size */}px`,
|
|
8057
|
+
"&:before": {
|
|
8058
|
+
borderBottom: `${7 /* Size */}px solid #bbb`,
|
|
8059
|
+
},
|
|
8060
|
+
"&:after": {
|
|
8061
|
+
borderBottom: `${7 /* Size */}px solid #f5f5f5`,
|
|
8062
|
+
top: "1px"
|
|
8063
|
+
}
|
|
8064
|
+
},
|
|
8065
|
+
},
|
|
8066
|
+
"&dark .cm-tooltip .cm-tooltip-arrow": {
|
|
8067
|
+
"&:before": {
|
|
8068
|
+
borderTopColor: "#333338",
|
|
8069
|
+
borderBottomColor: "#333338"
|
|
8070
|
+
},
|
|
8071
|
+
"&:after": {
|
|
8072
|
+
borderTopColor: "transparent",
|
|
8073
|
+
borderBottomColor: "transparent"
|
|
8074
|
+
}
|
|
8075
|
+
}
|
|
8076
|
+
});
|
|
8077
|
+
const noOffset = { x: 0, y: 0 };
|
|
8078
|
+
/**
|
|
8079
|
+
Facet to which an extension can add a value to show a tooltip.
|
|
8080
|
+
*/
|
|
8081
|
+
const showTooltip = state.Facet.define({
|
|
8082
|
+
enables: [tooltipPlugin, baseTheme]
|
|
8083
|
+
});
|
|
8084
|
+
const showHoverTooltip = state.Facet.define({
|
|
8085
|
+
combine(x) { console.log("show", x.map(x => !!x)); return x; }
|
|
8086
|
+
});
|
|
8087
|
+
class HoverTooltipHost {
|
|
8088
|
+
constructor(view) {
|
|
8089
|
+
this.view = view;
|
|
8090
|
+
this.mounted = false;
|
|
8091
|
+
this.dom = document.createElement("div");
|
|
8092
|
+
this.dom.classList.add("cm-tooltip-hover");
|
|
8093
|
+
this.manager = new TooltipViewManager(view, showHoverTooltip, t => this.createHostedView(t));
|
|
8094
|
+
}
|
|
8095
|
+
// Needs to be static so that host tooltip instances always match
|
|
8096
|
+
static create(view) {
|
|
8097
|
+
return new HoverTooltipHost(view);
|
|
8098
|
+
}
|
|
8099
|
+
createHostedView(tooltip) {
|
|
8100
|
+
let hostedView = tooltip.create(this.view);
|
|
8101
|
+
hostedView.dom.classList.add("cm-tooltip-section");
|
|
8102
|
+
this.dom.appendChild(hostedView.dom);
|
|
8103
|
+
if (this.mounted && hostedView.mount)
|
|
8104
|
+
hostedView.mount(this.view);
|
|
8105
|
+
return hostedView;
|
|
8106
|
+
}
|
|
8107
|
+
mount(view) {
|
|
8108
|
+
for (let hostedView of this.manager.tooltipViews) {
|
|
8109
|
+
if (hostedView.mount)
|
|
8110
|
+
hostedView.mount(view);
|
|
8111
|
+
}
|
|
8112
|
+
this.mounted = true;
|
|
8113
|
+
}
|
|
8114
|
+
positioned() {
|
|
8115
|
+
for (let hostedView of this.manager.tooltipViews) {
|
|
8116
|
+
if (hostedView.positioned)
|
|
8117
|
+
hostedView.positioned();
|
|
8118
|
+
}
|
|
8119
|
+
}
|
|
8120
|
+
update(update) {
|
|
8121
|
+
this.manager.update(update);
|
|
8122
|
+
}
|
|
8123
|
+
}
|
|
8124
|
+
const showHoverTooltipHost = showTooltip.compute([showHoverTooltip], state => {
|
|
8125
|
+
let tooltips = state.facet(showHoverTooltip).filter(t => t);
|
|
8126
|
+
console.log("hover tooltips: ", tooltips.length);
|
|
8127
|
+
if (tooltips.length === 0)
|
|
8128
|
+
return null;
|
|
8129
|
+
return {
|
|
8130
|
+
pos: Math.min(...tooltips.map(t => t.pos)),
|
|
8131
|
+
end: Math.max(...tooltips.filter(t => t.end != null).map(t => t.end)),
|
|
8132
|
+
create: HoverTooltipHost.create,
|
|
8133
|
+
above: tooltips[0].above,
|
|
8134
|
+
arrow: tooltips.some(t => t.arrow),
|
|
8135
|
+
};
|
|
8136
|
+
});
|
|
8137
|
+
class HoverPlugin {
|
|
8138
|
+
constructor(view, source, field, setHover, hoverTime) {
|
|
8139
|
+
this.view = view;
|
|
8140
|
+
this.source = source;
|
|
8141
|
+
this.field = field;
|
|
8142
|
+
this.setHover = setHover;
|
|
8143
|
+
this.hoverTime = hoverTime;
|
|
8144
|
+
this.hoverTimeout = -1;
|
|
8145
|
+
this.restartTimeout = -1;
|
|
8146
|
+
this.pending = null;
|
|
8147
|
+
this.lastMove = { x: 0, y: 0, target: view.dom, time: 0 };
|
|
8148
|
+
this.checkHover = this.checkHover.bind(this);
|
|
8149
|
+
view.dom.addEventListener("mouseleave", this.mouseleave = this.mouseleave.bind(this));
|
|
8150
|
+
view.dom.addEventListener("mousemove", this.mousemove = this.mousemove.bind(this));
|
|
8151
|
+
}
|
|
8152
|
+
update() {
|
|
8153
|
+
if (this.pending) {
|
|
8154
|
+
this.pending = null;
|
|
8155
|
+
clearTimeout(this.restartTimeout);
|
|
8156
|
+
this.restartTimeout = setTimeout(() => this.startHover(), 20);
|
|
8157
|
+
}
|
|
8158
|
+
}
|
|
8159
|
+
get active() {
|
|
8160
|
+
return this.view.state.field(this.field);
|
|
8161
|
+
}
|
|
8162
|
+
checkHover() {
|
|
8163
|
+
this.hoverTimeout = -1;
|
|
8164
|
+
if (this.active)
|
|
8165
|
+
return;
|
|
8166
|
+
let hovered = Date.now() - this.lastMove.time;
|
|
8167
|
+
if (hovered < this.hoverTime)
|
|
8168
|
+
this.hoverTimeout = setTimeout(this.checkHover, this.hoverTime - hovered);
|
|
8169
|
+
else
|
|
8170
|
+
this.startHover();
|
|
8171
|
+
}
|
|
8172
|
+
startHover() {
|
|
8173
|
+
var _a;
|
|
8174
|
+
clearTimeout(this.restartTimeout);
|
|
8175
|
+
let { lastMove } = this;
|
|
8176
|
+
let pos = this.view.contentDOM.contains(lastMove.target) ? this.view.posAtCoords(lastMove) : null;
|
|
8177
|
+
if (pos == null)
|
|
8178
|
+
return;
|
|
8179
|
+
let posCoords = this.view.coordsAtPos(pos);
|
|
8180
|
+
if (posCoords == null || lastMove.y < posCoords.top || lastMove.y > posCoords.bottom ||
|
|
8181
|
+
lastMove.x < posCoords.left - this.view.defaultCharacterWidth ||
|
|
8182
|
+
lastMove.x > posCoords.right + this.view.defaultCharacterWidth)
|
|
8183
|
+
return;
|
|
8184
|
+
let bidi = this.view.bidiSpans(this.view.state.doc.lineAt(pos)).find(s => s.from <= pos && s.to >= pos);
|
|
8185
|
+
let rtl = bidi && bidi.dir == exports.Direction.RTL ? -1 : 1;
|
|
8186
|
+
let open = this.source(this.view, pos, (lastMove.x < posCoords.left ? -rtl : rtl));
|
|
8187
|
+
if ((_a = open) === null || _a === void 0 ? void 0 : _a.then) {
|
|
8188
|
+
let pending = this.pending = { pos };
|
|
8189
|
+
open.then(result => {
|
|
8190
|
+
if (this.pending == pending) {
|
|
8191
|
+
this.pending = null;
|
|
8192
|
+
if (result)
|
|
8193
|
+
this.view.dispatch({ effects: this.setHover.of(result) });
|
|
8194
|
+
}
|
|
8195
|
+
}, e => logException(this.view.state, e, "hover tooltip"));
|
|
8196
|
+
}
|
|
8197
|
+
else if (open) {
|
|
8198
|
+
this.view.dispatch({ effects: this.setHover.of(open) });
|
|
8199
|
+
}
|
|
8200
|
+
}
|
|
8201
|
+
mousemove(event) {
|
|
8202
|
+
var _a;
|
|
8203
|
+
this.lastMove = { x: event.clientX, y: event.clientY, target: event.target, time: Date.now() };
|
|
8204
|
+
if (this.hoverTimeout < 0)
|
|
8205
|
+
this.hoverTimeout = setTimeout(this.checkHover, this.hoverTime);
|
|
8206
|
+
let tooltip = this.active;
|
|
8207
|
+
if (tooltip && !isInTooltip(this.lastMove.target) || this.pending) {
|
|
8208
|
+
let { pos } = tooltip || this.pending, end = (_a = tooltip === null || tooltip === void 0 ? void 0 : tooltip.end) !== null && _a !== void 0 ? _a : pos;
|
|
8209
|
+
if ((pos == end ? this.view.posAtCoords(this.lastMove) != pos
|
|
8210
|
+
: !isOverRange(this.view, pos, end, event.clientX, event.clientY, 6 /* MaxDist */))) {
|
|
8211
|
+
this.view.dispatch({ effects: this.setHover.of(null) });
|
|
8212
|
+
this.pending = null;
|
|
8213
|
+
}
|
|
8214
|
+
}
|
|
8215
|
+
}
|
|
8216
|
+
mouseleave() {
|
|
8217
|
+
clearTimeout(this.hoverTimeout);
|
|
8218
|
+
this.hoverTimeout = -1;
|
|
8219
|
+
if (this.active)
|
|
8220
|
+
this.view.dispatch({ effects: this.setHover.of(null) });
|
|
8221
|
+
}
|
|
8222
|
+
destroy() {
|
|
8223
|
+
clearTimeout(this.hoverTimeout);
|
|
8224
|
+
this.view.dom.removeEventListener("mouseleave", this.mouseleave);
|
|
8225
|
+
this.view.dom.removeEventListener("mousemove", this.mousemove);
|
|
8226
|
+
}
|
|
8227
|
+
}
|
|
8228
|
+
function isInTooltip(elt) {
|
|
8229
|
+
for (let cur = elt; cur; cur = cur.parentNode)
|
|
8230
|
+
if (cur.nodeType == 1 && cur.classList.contains("cm-tooltip"))
|
|
8231
|
+
return true;
|
|
8232
|
+
return false;
|
|
8233
|
+
}
|
|
8234
|
+
function isOverRange(view, from, to, x, y, margin) {
|
|
8235
|
+
let range = document.createRange();
|
|
8236
|
+
let fromDOM = view.domAtPos(from), toDOM = view.domAtPos(to);
|
|
8237
|
+
range.setEnd(toDOM.node, toDOM.offset);
|
|
8238
|
+
range.setStart(fromDOM.node, fromDOM.offset);
|
|
8239
|
+
let rects = range.getClientRects();
|
|
8240
|
+
range.detach();
|
|
8241
|
+
for (let i = 0; i < rects.length; i++) {
|
|
8242
|
+
let rect = rects[i];
|
|
8243
|
+
let dist = Math.max(rect.top - y, y - rect.bottom, rect.left - x, x - rect.right);
|
|
8244
|
+
if (dist <= margin)
|
|
8245
|
+
return true;
|
|
8246
|
+
}
|
|
8247
|
+
return false;
|
|
8248
|
+
}
|
|
8249
|
+
/**
|
|
8250
|
+
Set up a hover tooltip, which shows up when the pointer hovers
|
|
8251
|
+
over ranges of text. The callback is called when the mouse hovers
|
|
8252
|
+
over the document text. It should, if there is a tooltip
|
|
8253
|
+
associated with position `pos`, return the tooltip description
|
|
8254
|
+
(either directly or in a promise). The `side` argument indicates
|
|
8255
|
+
on which side of the position the pointer is—it will be -1 if the
|
|
8256
|
+
pointer is before the position, 1 if after the position.
|
|
8257
|
+
|
|
8258
|
+
Note that all hover tooltips are hosted within a single tooltip
|
|
8259
|
+
container element. This allows multiple tooltips over the same
|
|
8260
|
+
range to be "merged" together without overlapping.
|
|
8261
|
+
*/
|
|
8262
|
+
function hoverTooltip(source, options = {}) {
|
|
8263
|
+
let setHover = state.StateEffect.define();
|
|
8264
|
+
let hoverState = state.StateField.define({
|
|
8265
|
+
create() { return null; },
|
|
8266
|
+
update(value, tr) {
|
|
8267
|
+
console.log("update tooltip", !!value);
|
|
8268
|
+
if (value && (options.hideOnChange && (tr.docChanged || tr.selection)))
|
|
8269
|
+
return null;
|
|
8270
|
+
if (value && tr.docChanged) {
|
|
8271
|
+
let newPos = tr.changes.mapPos(value.pos, -1, state.MapMode.TrackDel);
|
|
8272
|
+
if (newPos == null)
|
|
8273
|
+
return null;
|
|
8274
|
+
let copy = Object.assign(Object.create(null), value);
|
|
8275
|
+
copy.pos = newPos;
|
|
8276
|
+
if (value.end != null)
|
|
8277
|
+
copy.end = tr.changes.mapPos(value.end);
|
|
8278
|
+
value = copy;
|
|
8279
|
+
}
|
|
8280
|
+
for (let effect of tr.effects) {
|
|
8281
|
+
if (effect.is(setHover))
|
|
8282
|
+
value = effect.value;
|
|
8283
|
+
if (effect.is(closeHoverTooltipEffect))
|
|
8284
|
+
value = (console.log("CLOSE"), null);
|
|
8285
|
+
}
|
|
8286
|
+
console.log("updated: " + !!value);
|
|
8287
|
+
return value;
|
|
8288
|
+
},
|
|
8289
|
+
provide: f => showHoverTooltip.from(f)
|
|
8290
|
+
});
|
|
8291
|
+
return [
|
|
8292
|
+
hoverState,
|
|
8293
|
+
ViewPlugin.define(view => new HoverPlugin(view, source, hoverState, setHover, options.hoverTime || 300 /* Time */)),
|
|
8294
|
+
showHoverTooltipHost
|
|
8295
|
+
];
|
|
8296
|
+
}
|
|
8297
|
+
/**
|
|
8298
|
+
Get the active tooltip view for a given tooltip, if available.
|
|
8299
|
+
*/
|
|
8300
|
+
function getTooltip(view, tooltip) {
|
|
8301
|
+
let plugin = view.plugin(tooltipPlugin);
|
|
8302
|
+
if (!plugin)
|
|
8303
|
+
return null;
|
|
8304
|
+
let found = plugin.manager.tooltips.indexOf(tooltip);
|
|
8305
|
+
return found < 0 ? null : plugin.manager.tooltipViews[found];
|
|
8306
|
+
}
|
|
8307
|
+
/**
|
|
8308
|
+
Returns true if any hover tooltips are currently active.
|
|
8309
|
+
*/
|
|
8310
|
+
function hasHoverTooltips(state) {
|
|
8311
|
+
return state.facet(showHoverTooltip).some(x => x);
|
|
8312
|
+
}
|
|
8313
|
+
const closeHoverTooltipEffect = state.StateEffect.define();
|
|
8314
|
+
/**
|
|
8315
|
+
Transaction effect that closes all hover tooltips.
|
|
8316
|
+
*/
|
|
8317
|
+
const closeHoverTooltips = closeHoverTooltipEffect.of(null);
|
|
8318
|
+
/**
|
|
8319
|
+
Tell the tooltip extension to recompute the position of the active
|
|
8320
|
+
tooltips. This can be useful when something happens (such as a
|
|
8321
|
+
re-positioning or CSS change affecting the editor) that could
|
|
8322
|
+
invalidate the existing tooltip positions.
|
|
8323
|
+
*/
|
|
8324
|
+
function repositionTooltips(view) {
|
|
8325
|
+
var _a;
|
|
8326
|
+
(_a = view.plugin(tooltipPlugin)) === null || _a === void 0 ? void 0 : _a.maybeMeasure();
|
|
8327
|
+
}
|
|
8328
|
+
|
|
8329
|
+
const panelConfig = state.Facet.define({
|
|
8330
|
+
combine(configs) {
|
|
8331
|
+
let topContainer, bottomContainer;
|
|
8332
|
+
for (let c of configs) {
|
|
8333
|
+
topContainer = topContainer || c.topContainer;
|
|
8334
|
+
bottomContainer = bottomContainer || c.bottomContainer;
|
|
8335
|
+
}
|
|
8336
|
+
return { topContainer, bottomContainer };
|
|
8337
|
+
}
|
|
8338
|
+
});
|
|
8339
|
+
/**
|
|
8340
|
+
Configures the panel-managing extension.
|
|
8341
|
+
*/
|
|
8342
|
+
function panels(config) {
|
|
8343
|
+
return config ? [panelConfig.of(config)] : [];
|
|
8344
|
+
}
|
|
8345
|
+
/**
|
|
8346
|
+
Get the active panel created by the given constructor, if any.
|
|
8347
|
+
This can be useful when you need access to your panels' DOM
|
|
8348
|
+
structure.
|
|
8349
|
+
*/
|
|
8350
|
+
function getPanel(view, panel) {
|
|
8351
|
+
let plugin = view.plugin(panelPlugin);
|
|
8352
|
+
let index = plugin ? plugin.specs.indexOf(panel) : -1;
|
|
8353
|
+
return index > -1 ? plugin.panels[index] : null;
|
|
8354
|
+
}
|
|
8355
|
+
const panelPlugin = ViewPlugin.fromClass(class {
|
|
8356
|
+
constructor(view) {
|
|
8357
|
+
this.input = view.state.facet(showPanel);
|
|
8358
|
+
this.specs = this.input.filter(s => s);
|
|
8359
|
+
this.panels = this.specs.map(spec => spec(view));
|
|
8360
|
+
let conf = view.state.facet(panelConfig);
|
|
8361
|
+
this.top = new PanelGroup(view, true, conf.topContainer);
|
|
8362
|
+
this.bottom = new PanelGroup(view, false, conf.bottomContainer);
|
|
8363
|
+
this.top.sync(this.panels.filter(p => p.top));
|
|
8364
|
+
this.bottom.sync(this.panels.filter(p => !p.top));
|
|
8365
|
+
for (let p of this.panels) {
|
|
8366
|
+
p.dom.classList.add("cm-panel");
|
|
8367
|
+
if (p.mount)
|
|
8368
|
+
p.mount();
|
|
8369
|
+
}
|
|
8370
|
+
}
|
|
8371
|
+
update(update) {
|
|
8372
|
+
let conf = update.state.facet(panelConfig);
|
|
8373
|
+
if (this.top.container != conf.topContainer) {
|
|
8374
|
+
this.top.sync([]);
|
|
8375
|
+
this.top = new PanelGroup(update.view, true, conf.topContainer);
|
|
8376
|
+
}
|
|
8377
|
+
if (this.bottom.container != conf.bottomContainer) {
|
|
8378
|
+
this.bottom.sync([]);
|
|
8379
|
+
this.bottom = new PanelGroup(update.view, false, conf.bottomContainer);
|
|
8380
|
+
}
|
|
8381
|
+
this.top.syncClasses();
|
|
8382
|
+
this.bottom.syncClasses();
|
|
8383
|
+
let input = update.state.facet(showPanel);
|
|
8384
|
+
if (input != this.input) {
|
|
8385
|
+
let specs = input.filter(x => x);
|
|
8386
|
+
let panels = [], top = [], bottom = [], mount = [];
|
|
8387
|
+
for (let spec of specs) {
|
|
8388
|
+
let known = this.specs.indexOf(spec), panel;
|
|
8389
|
+
if (known < 0) {
|
|
8390
|
+
panel = spec(update.view);
|
|
8391
|
+
mount.push(panel);
|
|
8392
|
+
}
|
|
8393
|
+
else {
|
|
8394
|
+
panel = this.panels[known];
|
|
8395
|
+
if (panel.update)
|
|
8396
|
+
panel.update(update);
|
|
8397
|
+
}
|
|
8398
|
+
panels.push(panel);
|
|
8399
|
+
(panel.top ? top : bottom).push(panel);
|
|
8400
|
+
}
|
|
8401
|
+
this.specs = specs;
|
|
8402
|
+
this.panels = panels;
|
|
8403
|
+
this.top.sync(top);
|
|
8404
|
+
this.bottom.sync(bottom);
|
|
8405
|
+
for (let p of mount) {
|
|
8406
|
+
p.dom.classList.add("cm-panel");
|
|
8407
|
+
if (p.mount)
|
|
8408
|
+
p.mount();
|
|
8409
|
+
}
|
|
8410
|
+
}
|
|
8411
|
+
else {
|
|
8412
|
+
for (let p of this.panels)
|
|
8413
|
+
if (p.update)
|
|
8414
|
+
p.update(update);
|
|
8415
|
+
}
|
|
8416
|
+
}
|
|
8417
|
+
destroy() {
|
|
8418
|
+
this.top.sync([]);
|
|
8419
|
+
this.bottom.sync([]);
|
|
8420
|
+
}
|
|
8421
|
+
}, {
|
|
8422
|
+
provide: plugin => EditorView.scrollMargins.of(view => {
|
|
8423
|
+
let value = view.plugin(plugin);
|
|
8424
|
+
return value && { top: value.top.scrollMargin(), bottom: value.bottom.scrollMargin() };
|
|
8425
|
+
})
|
|
8426
|
+
});
|
|
8427
|
+
class PanelGroup {
|
|
8428
|
+
constructor(view, top, container) {
|
|
8429
|
+
this.view = view;
|
|
8430
|
+
this.top = top;
|
|
8431
|
+
this.container = container;
|
|
8432
|
+
this.dom = undefined;
|
|
8433
|
+
this.classes = "";
|
|
8434
|
+
this.panels = [];
|
|
8435
|
+
this.syncClasses();
|
|
8436
|
+
}
|
|
8437
|
+
sync(panels) {
|
|
8438
|
+
for (let p of this.panels)
|
|
8439
|
+
if (p.destroy && panels.indexOf(p) < 0)
|
|
8440
|
+
p.destroy();
|
|
8441
|
+
this.panels = panels;
|
|
8442
|
+
this.syncDOM();
|
|
8443
|
+
}
|
|
8444
|
+
syncDOM() {
|
|
8445
|
+
if (this.panels.length == 0) {
|
|
8446
|
+
if (this.dom) {
|
|
8447
|
+
this.dom.remove();
|
|
8448
|
+
this.dom = undefined;
|
|
8449
|
+
}
|
|
8450
|
+
return;
|
|
8451
|
+
}
|
|
8452
|
+
if (!this.dom) {
|
|
8453
|
+
this.dom = document.createElement("div");
|
|
8454
|
+
this.dom.className = this.top ? "cm-panels cm-panels-top" : "cm-panels cm-panels-bottom";
|
|
8455
|
+
this.dom.style[this.top ? "top" : "bottom"] = "0";
|
|
8456
|
+
let parent = this.container || this.view.dom;
|
|
8457
|
+
parent.insertBefore(this.dom, this.top ? parent.firstChild : null);
|
|
8458
|
+
}
|
|
8459
|
+
let curDOM = this.dom.firstChild;
|
|
8460
|
+
for (let panel of this.panels) {
|
|
8461
|
+
if (panel.dom.parentNode == this.dom) {
|
|
8462
|
+
while (curDOM != panel.dom)
|
|
8463
|
+
curDOM = rm(curDOM);
|
|
8464
|
+
curDOM = curDOM.nextSibling;
|
|
8465
|
+
}
|
|
8466
|
+
else {
|
|
8467
|
+
this.dom.insertBefore(panel.dom, curDOM);
|
|
8468
|
+
}
|
|
8469
|
+
}
|
|
8470
|
+
while (curDOM)
|
|
8471
|
+
curDOM = rm(curDOM);
|
|
8472
|
+
}
|
|
8473
|
+
scrollMargin() {
|
|
8474
|
+
return !this.dom || this.container ? 0
|
|
8475
|
+
: Math.max(0, this.top ?
|
|
8476
|
+
this.dom.getBoundingClientRect().bottom - Math.max(0, this.view.scrollDOM.getBoundingClientRect().top) :
|
|
8477
|
+
Math.min(innerHeight, this.view.scrollDOM.getBoundingClientRect().bottom) - this.dom.getBoundingClientRect().top);
|
|
8478
|
+
}
|
|
8479
|
+
syncClasses() {
|
|
8480
|
+
if (!this.container || this.classes == this.view.themeClasses)
|
|
8481
|
+
return;
|
|
8482
|
+
for (let cls of this.classes.split(" "))
|
|
8483
|
+
if (cls)
|
|
8484
|
+
this.container.classList.remove(cls);
|
|
8485
|
+
for (let cls of (this.classes = this.view.themeClasses).split(" "))
|
|
8486
|
+
if (cls)
|
|
8487
|
+
this.container.classList.add(cls);
|
|
8488
|
+
}
|
|
8489
|
+
}
|
|
8490
|
+
function rm(node) {
|
|
8491
|
+
let next = node.nextSibling;
|
|
8492
|
+
node.remove();
|
|
8493
|
+
return next;
|
|
8494
|
+
}
|
|
8495
|
+
/**
|
|
8496
|
+
Opening a panel is done by providing a constructor function for
|
|
8497
|
+
the panel through this facet. (The panel is closed again when its
|
|
8498
|
+
constructor is no longer provided.) Values of `null` are ignored.
|
|
8499
|
+
*/
|
|
8500
|
+
const showPanel = state.Facet.define({
|
|
8501
|
+
enables: panelPlugin
|
|
8502
|
+
});
|
|
8503
|
+
|
|
8504
|
+
/**
|
|
8505
|
+
A gutter marker represents a bit of information attached to a line
|
|
8506
|
+
in a specific gutter. Your own custom markers have to extend this
|
|
8507
|
+
class.
|
|
8508
|
+
*/
|
|
8509
|
+
class GutterMarker extends state.RangeValue {
|
|
8510
|
+
/**
|
|
8511
|
+
@internal
|
|
8512
|
+
*/
|
|
8513
|
+
compare(other) {
|
|
8514
|
+
return this == other || this.constructor == other.constructor && this.eq(other);
|
|
8515
|
+
}
|
|
8516
|
+
/**
|
|
8517
|
+
Compare this marker to another marker of the same type.
|
|
8518
|
+
*/
|
|
8519
|
+
eq(other) { return false; }
|
|
8520
|
+
/**
|
|
8521
|
+
Called if the marker has a `toDOM` method and its representation
|
|
8522
|
+
was removed from a gutter.
|
|
8523
|
+
*/
|
|
8524
|
+
destroy(dom) { }
|
|
8525
|
+
}
|
|
8526
|
+
GutterMarker.prototype.elementClass = "";
|
|
8527
|
+
GutterMarker.prototype.toDOM = undefined;
|
|
8528
|
+
GutterMarker.prototype.mapMode = state.MapMode.TrackBefore;
|
|
8529
|
+
GutterMarker.prototype.startSide = GutterMarker.prototype.endSide = -1;
|
|
8530
|
+
GutterMarker.prototype.point = true;
|
|
8531
|
+
/**
|
|
8532
|
+
Facet used to add a class to all gutter elements for a given line.
|
|
8533
|
+
Markers given to this facet should _only_ define an
|
|
8534
|
+
[`elementclass`](https://codemirror.net/6/docs/ref/#view.GutterMarker.elementClass), not a
|
|
8535
|
+
[`toDOM`](https://codemirror.net/6/docs/ref/#view.GutterMarker.toDOM) (or the marker will appear
|
|
8536
|
+
in all gutters for the line).
|
|
8537
|
+
*/
|
|
8538
|
+
const gutterLineClass = state.Facet.define();
|
|
8539
|
+
const defaults = {
|
|
8540
|
+
class: "",
|
|
8541
|
+
renderEmptyElements: false,
|
|
8542
|
+
elementStyle: "",
|
|
8543
|
+
markers: () => state.RangeSet.empty,
|
|
8544
|
+
lineMarker: () => null,
|
|
8545
|
+
lineMarkerChange: null,
|
|
8546
|
+
initialSpacer: null,
|
|
8547
|
+
updateSpacer: null,
|
|
8548
|
+
domEventHandlers: {}
|
|
8549
|
+
};
|
|
8550
|
+
const activeGutters = state.Facet.define();
|
|
8551
|
+
/**
|
|
8552
|
+
Define an editor gutter. The order in which the gutters appear is
|
|
8553
|
+
determined by their extension priority.
|
|
8554
|
+
*/
|
|
8555
|
+
function gutter(config) {
|
|
8556
|
+
return [gutters(), activeGutters.of(Object.assign(Object.assign({}, defaults), config))];
|
|
8557
|
+
}
|
|
8558
|
+
const unfixGutters = state.Facet.define({
|
|
8559
|
+
combine: values => values.some(x => x)
|
|
8560
|
+
});
|
|
8561
|
+
/**
|
|
8562
|
+
The gutter-drawing plugin is automatically enabled when you add a
|
|
8563
|
+
gutter, but you can use this function to explicitly configure it.
|
|
8564
|
+
|
|
8565
|
+
Unless `fixed` is explicitly set to `false`, the gutters are
|
|
8566
|
+
fixed, meaning they don't scroll along with the content
|
|
8567
|
+
horizontally (except on Internet Explorer, which doesn't support
|
|
8568
|
+
CSS [`position:
|
|
8569
|
+
sticky`](https://developer.mozilla.org/en-US/docs/Web/CSS/position#sticky)).
|
|
8570
|
+
*/
|
|
8571
|
+
function gutters(config) {
|
|
8572
|
+
let result = [
|
|
8573
|
+
gutterView,
|
|
8574
|
+
];
|
|
8575
|
+
if (config && config.fixed === false)
|
|
8576
|
+
result.push(unfixGutters.of(true));
|
|
8577
|
+
return result;
|
|
8578
|
+
}
|
|
8579
|
+
const gutterView = ViewPlugin.fromClass(class {
|
|
8580
|
+
constructor(view) {
|
|
8581
|
+
this.view = view;
|
|
8582
|
+
this.prevViewport = view.viewport;
|
|
8583
|
+
this.dom = document.createElement("div");
|
|
8584
|
+
this.dom.className = "cm-gutters";
|
|
8585
|
+
this.dom.setAttribute("aria-hidden", "true");
|
|
8586
|
+
this.dom.style.minHeight = this.view.contentHeight + "px";
|
|
8587
|
+
this.gutters = view.state.facet(activeGutters).map(conf => new SingleGutterView(view, conf));
|
|
8588
|
+
for (let gutter of this.gutters)
|
|
8589
|
+
this.dom.appendChild(gutter.dom);
|
|
8590
|
+
this.fixed = !view.state.facet(unfixGutters);
|
|
8591
|
+
if (this.fixed) {
|
|
8592
|
+
// FIXME IE11 fallback, which doesn't support position: sticky,
|
|
8593
|
+
// by using position: relative + event handlers that realign the
|
|
8594
|
+
// gutter (or just force fixed=false on IE11?)
|
|
8595
|
+
this.dom.style.position = "sticky";
|
|
8596
|
+
}
|
|
8597
|
+
this.syncGutters(false);
|
|
8598
|
+
view.scrollDOM.insertBefore(this.dom, view.contentDOM);
|
|
8599
|
+
}
|
|
8600
|
+
update(update) {
|
|
8601
|
+
if (this.updateGutters(update)) {
|
|
8602
|
+
// Detach during sync when the viewport changed significantly
|
|
8603
|
+
// (such as during scrolling), since for large updates that is
|
|
8604
|
+
// faster.
|
|
8605
|
+
let vpA = this.prevViewport, vpB = update.view.viewport;
|
|
8606
|
+
let vpOverlap = Math.min(vpA.to, vpB.to) - Math.max(vpA.from, vpB.from);
|
|
8607
|
+
this.syncGutters(vpOverlap < (vpB.to - vpB.from) * 0.8);
|
|
8608
|
+
}
|
|
8609
|
+
if (update.geometryChanged)
|
|
8610
|
+
this.dom.style.minHeight = this.view.contentHeight + "px";
|
|
8611
|
+
if (this.view.state.facet(unfixGutters) != !this.fixed) {
|
|
8612
|
+
this.fixed = !this.fixed;
|
|
8613
|
+
this.dom.style.position = this.fixed ? "sticky" : "";
|
|
8614
|
+
}
|
|
8615
|
+
this.prevViewport = update.view.viewport;
|
|
8616
|
+
}
|
|
8617
|
+
syncGutters(detach) {
|
|
8618
|
+
let after = this.dom.nextSibling;
|
|
8619
|
+
if (detach)
|
|
8620
|
+
this.dom.remove();
|
|
8621
|
+
let lineClasses = state.RangeSet.iter(this.view.state.facet(gutterLineClass), this.view.viewport.from);
|
|
8622
|
+
let classSet = [];
|
|
8623
|
+
let contexts = this.gutters.map(gutter => new UpdateContext(gutter, this.view.viewport, -this.view.documentPadding.top));
|
|
8624
|
+
for (let line of this.view.viewportLineBlocks) {
|
|
8625
|
+
let text;
|
|
8626
|
+
if (Array.isArray(line.type)) {
|
|
8627
|
+
for (let b of line.type)
|
|
8628
|
+
if (b.type == exports.BlockType.Text) {
|
|
8629
|
+
text = b;
|
|
8630
|
+
break;
|
|
8631
|
+
}
|
|
8632
|
+
}
|
|
8633
|
+
else {
|
|
8634
|
+
text = line.type == exports.BlockType.Text ? line : undefined;
|
|
8635
|
+
}
|
|
8636
|
+
if (!text)
|
|
8637
|
+
continue;
|
|
8638
|
+
if (classSet.length)
|
|
8639
|
+
classSet = [];
|
|
8640
|
+
advanceCursor(lineClasses, classSet, line.from);
|
|
8641
|
+
for (let cx of contexts)
|
|
8642
|
+
cx.line(this.view, text, classSet);
|
|
8643
|
+
}
|
|
8644
|
+
for (let cx of contexts)
|
|
8645
|
+
cx.finish();
|
|
8646
|
+
if (detach)
|
|
8647
|
+
this.view.scrollDOM.insertBefore(this.dom, after);
|
|
8648
|
+
}
|
|
8649
|
+
updateGutters(update) {
|
|
8650
|
+
let prev = update.startState.facet(activeGutters), cur = update.state.facet(activeGutters);
|
|
8651
|
+
let change = update.docChanged || update.heightChanged || update.viewportChanged ||
|
|
8652
|
+
!state.RangeSet.eq(update.startState.facet(gutterLineClass), update.state.facet(gutterLineClass), update.view.viewport.from, update.view.viewport.to);
|
|
8653
|
+
if (prev == cur) {
|
|
8654
|
+
for (let gutter of this.gutters)
|
|
8655
|
+
if (gutter.update(update))
|
|
8656
|
+
change = true;
|
|
8657
|
+
}
|
|
8658
|
+
else {
|
|
8659
|
+
change = true;
|
|
8660
|
+
let gutters = [];
|
|
8661
|
+
for (let conf of cur) {
|
|
8662
|
+
let known = prev.indexOf(conf);
|
|
8663
|
+
if (known < 0) {
|
|
8664
|
+
gutters.push(new SingleGutterView(this.view, conf));
|
|
8665
|
+
}
|
|
8666
|
+
else {
|
|
8667
|
+
this.gutters[known].update(update);
|
|
8668
|
+
gutters.push(this.gutters[known]);
|
|
8669
|
+
}
|
|
8670
|
+
}
|
|
8671
|
+
for (let g of this.gutters) {
|
|
8672
|
+
g.dom.remove();
|
|
8673
|
+
if (gutters.indexOf(g) < 0)
|
|
8674
|
+
g.destroy();
|
|
8675
|
+
}
|
|
8676
|
+
for (let g of gutters)
|
|
8677
|
+
this.dom.appendChild(g.dom);
|
|
8678
|
+
this.gutters = gutters;
|
|
8679
|
+
}
|
|
8680
|
+
return change;
|
|
8681
|
+
}
|
|
8682
|
+
destroy() {
|
|
8683
|
+
for (let view of this.gutters)
|
|
8684
|
+
view.destroy();
|
|
8685
|
+
this.dom.remove();
|
|
8686
|
+
}
|
|
8687
|
+
}, {
|
|
8688
|
+
provide: plugin => EditorView.scrollMargins.of(view => {
|
|
8689
|
+
let value = view.plugin(plugin);
|
|
8690
|
+
if (!value || value.gutters.length == 0 || !value.fixed)
|
|
8691
|
+
return null;
|
|
8692
|
+
return view.textDirection == exports.Direction.LTR ? { left: value.dom.offsetWidth } : { right: value.dom.offsetWidth };
|
|
8693
|
+
})
|
|
8694
|
+
});
|
|
8695
|
+
function asArray(val) { return (Array.isArray(val) ? val : [val]); }
|
|
8696
|
+
function advanceCursor(cursor, collect, pos) {
|
|
8697
|
+
while (cursor.value && cursor.from <= pos) {
|
|
8698
|
+
if (cursor.from == pos)
|
|
8699
|
+
collect.push(cursor.value);
|
|
8700
|
+
cursor.next();
|
|
8701
|
+
}
|
|
8702
|
+
}
|
|
8703
|
+
class UpdateContext {
|
|
8704
|
+
constructor(gutter, viewport, height) {
|
|
8705
|
+
this.gutter = gutter;
|
|
8706
|
+
this.height = height;
|
|
8707
|
+
this.localMarkers = [];
|
|
8708
|
+
this.i = 0;
|
|
8709
|
+
this.cursor = state.RangeSet.iter(gutter.markers, viewport.from);
|
|
8710
|
+
}
|
|
8711
|
+
line(view, line, extraMarkers) {
|
|
8712
|
+
if (this.localMarkers.length)
|
|
8713
|
+
this.localMarkers = [];
|
|
8714
|
+
advanceCursor(this.cursor, this.localMarkers, line.from);
|
|
8715
|
+
let localMarkers = extraMarkers.length ? this.localMarkers.concat(extraMarkers) : this.localMarkers;
|
|
8716
|
+
let forLine = this.gutter.config.lineMarker(view, line, localMarkers);
|
|
8717
|
+
if (forLine)
|
|
8718
|
+
localMarkers.unshift(forLine);
|
|
8719
|
+
let gutter = this.gutter;
|
|
8720
|
+
if (localMarkers.length == 0 && !gutter.config.renderEmptyElements)
|
|
8721
|
+
return;
|
|
8722
|
+
let above = line.top - this.height;
|
|
8723
|
+
if (this.i == gutter.elements.length) {
|
|
8724
|
+
let newElt = new GutterElement(view, line.height, above, localMarkers);
|
|
8725
|
+
gutter.elements.push(newElt);
|
|
8726
|
+
gutter.dom.appendChild(newElt.dom);
|
|
8727
|
+
}
|
|
8728
|
+
else {
|
|
8729
|
+
gutter.elements[this.i].update(view, line.height, above, localMarkers);
|
|
8730
|
+
}
|
|
8731
|
+
this.height = line.bottom;
|
|
8732
|
+
this.i++;
|
|
8733
|
+
}
|
|
8734
|
+
finish() {
|
|
8735
|
+
let gutter = this.gutter;
|
|
8736
|
+
while (gutter.elements.length > this.i) {
|
|
8737
|
+
let last = gutter.elements.pop();
|
|
8738
|
+
gutter.dom.removeChild(last.dom);
|
|
8739
|
+
last.destroy();
|
|
8740
|
+
}
|
|
8741
|
+
}
|
|
8742
|
+
}
|
|
8743
|
+
class SingleGutterView {
|
|
8744
|
+
constructor(view, config) {
|
|
8745
|
+
this.view = view;
|
|
8746
|
+
this.config = config;
|
|
8747
|
+
this.elements = [];
|
|
8748
|
+
this.spacer = null;
|
|
8749
|
+
this.dom = document.createElement("div");
|
|
8750
|
+
this.dom.className = "cm-gutter" + (this.config.class ? " " + this.config.class : "");
|
|
8751
|
+
for (let prop in config.domEventHandlers) {
|
|
8752
|
+
this.dom.addEventListener(prop, (event) => {
|
|
8753
|
+
let line = view.lineBlockAtHeight(event.clientY - view.documentTop);
|
|
8754
|
+
if (config.domEventHandlers[prop](view, line, event))
|
|
8755
|
+
event.preventDefault();
|
|
8756
|
+
});
|
|
8757
|
+
}
|
|
8758
|
+
this.markers = asArray(config.markers(view));
|
|
8759
|
+
if (config.initialSpacer) {
|
|
8760
|
+
this.spacer = new GutterElement(view, 0, 0, [config.initialSpacer(view)]);
|
|
8761
|
+
this.dom.appendChild(this.spacer.dom);
|
|
8762
|
+
this.spacer.dom.style.cssText += "visibility: hidden; pointer-events: none";
|
|
8763
|
+
}
|
|
8764
|
+
}
|
|
8765
|
+
update(update) {
|
|
8766
|
+
let prevMarkers = this.markers;
|
|
8767
|
+
this.markers = asArray(this.config.markers(update.view));
|
|
8768
|
+
if (this.spacer && this.config.updateSpacer) {
|
|
8769
|
+
let updated = this.config.updateSpacer(this.spacer.markers[0], update);
|
|
8770
|
+
if (updated != this.spacer.markers[0])
|
|
8771
|
+
this.spacer.update(update.view, 0, 0, [updated]);
|
|
8772
|
+
}
|
|
8773
|
+
let vp = update.view.viewport;
|
|
8774
|
+
return !state.RangeSet.eq(this.markers, prevMarkers, vp.from, vp.to) ||
|
|
8775
|
+
(this.config.lineMarkerChange ? this.config.lineMarkerChange(update) : false);
|
|
8776
|
+
}
|
|
8777
|
+
destroy() {
|
|
8778
|
+
for (let elt of this.elements)
|
|
8779
|
+
elt.destroy();
|
|
8780
|
+
}
|
|
8781
|
+
}
|
|
8782
|
+
class GutterElement {
|
|
8783
|
+
constructor(view, height, above, markers) {
|
|
8784
|
+
this.height = -1;
|
|
8785
|
+
this.above = 0;
|
|
8786
|
+
this.markers = [];
|
|
8787
|
+
this.dom = document.createElement("div");
|
|
8788
|
+
this.update(view, height, above, markers);
|
|
8789
|
+
}
|
|
8790
|
+
update(view, height, above, markers) {
|
|
8791
|
+
if (this.height != height)
|
|
8792
|
+
this.dom.style.height = (this.height = height) + "px";
|
|
8793
|
+
if (this.above != above)
|
|
8794
|
+
this.dom.style.marginTop = (this.above = above) ? above + "px" : "";
|
|
8795
|
+
if (!sameMarkers(this.markers, markers))
|
|
8796
|
+
this.setMarkers(view, markers);
|
|
8797
|
+
}
|
|
8798
|
+
setMarkers(view, markers) {
|
|
8799
|
+
let cls = "cm-gutterElement", domPos = this.dom.firstChild;
|
|
8800
|
+
for (let iNew = 0, iOld = 0;;) {
|
|
8801
|
+
let skipTo = iOld, marker = iNew < markers.length ? markers[iNew++] : null, matched = false;
|
|
8802
|
+
if (marker) {
|
|
8803
|
+
let c = marker.elementClass;
|
|
8804
|
+
if (c)
|
|
8805
|
+
cls += " " + c;
|
|
8806
|
+
for (let i = iOld; i < this.markers.length; i++)
|
|
8807
|
+
if (this.markers[i].compare(marker)) {
|
|
8808
|
+
skipTo = i;
|
|
8809
|
+
matched = true;
|
|
8810
|
+
break;
|
|
8811
|
+
}
|
|
8812
|
+
}
|
|
8813
|
+
else {
|
|
8814
|
+
skipTo = this.markers.length;
|
|
8815
|
+
}
|
|
8816
|
+
while (iOld < skipTo) {
|
|
8817
|
+
let next = this.markers[iOld++];
|
|
8818
|
+
if (next.toDOM) {
|
|
8819
|
+
next.destroy(domPos);
|
|
8820
|
+
let after = domPos.nextSibling;
|
|
8821
|
+
domPos.remove();
|
|
8822
|
+
domPos = after;
|
|
8823
|
+
}
|
|
8824
|
+
}
|
|
8825
|
+
if (!marker)
|
|
8826
|
+
break;
|
|
8827
|
+
if (marker.toDOM) {
|
|
8828
|
+
if (matched)
|
|
8829
|
+
domPos = domPos.nextSibling;
|
|
8830
|
+
else
|
|
8831
|
+
this.dom.insertBefore(marker.toDOM(view), domPos);
|
|
8832
|
+
}
|
|
8833
|
+
if (matched)
|
|
8834
|
+
iOld++;
|
|
8835
|
+
}
|
|
8836
|
+
this.dom.className = cls;
|
|
8837
|
+
this.markers = markers;
|
|
8838
|
+
}
|
|
8839
|
+
destroy() {
|
|
8840
|
+
this.setMarkers(null, []); // First argument not used unless creating markers
|
|
8841
|
+
}
|
|
8842
|
+
}
|
|
8843
|
+
function sameMarkers(a, b) {
|
|
8844
|
+
if (a.length != b.length)
|
|
8845
|
+
return false;
|
|
8846
|
+
for (let i = 0; i < a.length; i++)
|
|
8847
|
+
if (!a[i].compare(b[i]))
|
|
8848
|
+
return false;
|
|
8849
|
+
return true;
|
|
8850
|
+
}
|
|
8851
|
+
/**
|
|
8852
|
+
Facet used to provide markers to the line number gutter.
|
|
8853
|
+
*/
|
|
8854
|
+
const lineNumberMarkers = state.Facet.define();
|
|
8855
|
+
const lineNumberConfig = state.Facet.define({
|
|
8856
|
+
combine(values) {
|
|
8857
|
+
return state.combineConfig(values, { formatNumber: String, domEventHandlers: {} }, {
|
|
8858
|
+
domEventHandlers(a, b) {
|
|
8859
|
+
let result = Object.assign({}, a);
|
|
8860
|
+
for (let event in b) {
|
|
8861
|
+
let exists = result[event], add = b[event];
|
|
8862
|
+
result[event] = exists ? (view, line, event) => exists(view, line, event) || add(view, line, event) : add;
|
|
8863
|
+
}
|
|
8864
|
+
return result;
|
|
8865
|
+
}
|
|
8866
|
+
});
|
|
8867
|
+
}
|
|
8868
|
+
});
|
|
8869
|
+
class NumberMarker extends GutterMarker {
|
|
8870
|
+
constructor(number) {
|
|
8871
|
+
super();
|
|
8872
|
+
this.number = number;
|
|
8873
|
+
}
|
|
8874
|
+
eq(other) { return this.number == other.number; }
|
|
8875
|
+
toDOM() { return document.createTextNode(this.number); }
|
|
8876
|
+
}
|
|
8877
|
+
function formatNumber(view, number) {
|
|
8878
|
+
return view.state.facet(lineNumberConfig).formatNumber(number, view.state);
|
|
8879
|
+
}
|
|
8880
|
+
const lineNumberGutter = activeGutters.compute([lineNumberConfig], state => ({
|
|
8881
|
+
class: "cm-lineNumbers",
|
|
8882
|
+
renderEmptyElements: false,
|
|
8883
|
+
markers(view) { return view.state.facet(lineNumberMarkers); },
|
|
8884
|
+
lineMarker(view, line, others) {
|
|
8885
|
+
if (others.some(m => m.toDOM))
|
|
8886
|
+
return null;
|
|
8887
|
+
return new NumberMarker(formatNumber(view, view.state.doc.lineAt(line.from).number));
|
|
8888
|
+
},
|
|
8889
|
+
lineMarkerChange: update => update.startState.facet(lineNumberConfig) != update.state.facet(lineNumberConfig),
|
|
8890
|
+
initialSpacer(view) {
|
|
8891
|
+
return new NumberMarker(formatNumber(view, maxLineNumber(view.state.doc.lines)));
|
|
8892
|
+
},
|
|
8893
|
+
updateSpacer(spacer, update) {
|
|
8894
|
+
let max = formatNumber(update.view, maxLineNumber(update.view.state.doc.lines));
|
|
8895
|
+
return max == spacer.number ? spacer : new NumberMarker(max);
|
|
8896
|
+
},
|
|
8897
|
+
domEventHandlers: state.facet(lineNumberConfig).domEventHandlers
|
|
8898
|
+
}));
|
|
8899
|
+
/**
|
|
8900
|
+
Create a line number gutter extension.
|
|
8901
|
+
*/
|
|
8902
|
+
function lineNumbers(config = {}) {
|
|
8903
|
+
return [
|
|
8904
|
+
lineNumberConfig.of(config),
|
|
8905
|
+
gutters(),
|
|
8906
|
+
lineNumberGutter
|
|
8907
|
+
];
|
|
8908
|
+
}
|
|
8909
|
+
function maxLineNumber(lines) {
|
|
8910
|
+
let last = 9;
|
|
8911
|
+
while (last < lines)
|
|
8912
|
+
last = last * 10 + 9;
|
|
8913
|
+
return last;
|
|
8914
|
+
}
|
|
8915
|
+
const activeLineGutterMarker = new class extends GutterMarker {
|
|
8916
|
+
constructor() {
|
|
8917
|
+
super(...arguments);
|
|
8918
|
+
this.elementClass = "cm-activeLineGutter";
|
|
8919
|
+
}
|
|
8920
|
+
};
|
|
8921
|
+
const activeLineGutterHighlighter = gutterLineClass.compute(["selection"], state$1 => {
|
|
8922
|
+
let marks = [], last = -1;
|
|
8923
|
+
for (let range of state$1.selection.ranges)
|
|
8924
|
+
if (range.empty) {
|
|
8925
|
+
let linePos = state$1.doc.lineAt(range.head).from;
|
|
8926
|
+
if (linePos > last) {
|
|
8927
|
+
last = linePos;
|
|
8928
|
+
marks.push(activeLineGutterMarker.range(linePos));
|
|
8929
|
+
}
|
|
8930
|
+
}
|
|
8931
|
+
return state.RangeSet.of(marks);
|
|
8932
|
+
});
|
|
8933
|
+
/**
|
|
8934
|
+
Returns an extension that adds a `cm-activeLineGutter` class to
|
|
8935
|
+
all gutter elements on the [active
|
|
8936
|
+
line](https://codemirror.net/6/docs/ref/#view.highlightActiveLine).
|
|
8937
|
+
*/
|
|
8938
|
+
function highlightActiveLineGutter() {
|
|
8939
|
+
return activeLineGutterHighlighter;
|
|
8940
|
+
}
|
|
8941
|
+
|
|
7721
8942
|
/**
|
|
7722
8943
|
@internal
|
|
7723
8944
|
*/
|
|
7724
8945
|
const __test = { HeightMap, HeightOracle, MeasuredHeights, QueryType, ChangedRange, computeOrder, moveVisually };
|
|
7725
8946
|
|
|
7726
|
-
Object.defineProperty(exports, 'Range', {
|
|
7727
|
-
enumerable: true,
|
|
7728
|
-
get: function () { return rangeset.Range; }
|
|
7729
|
-
});
|
|
7730
8947
|
exports.BidiSpan = BidiSpan;
|
|
7731
8948
|
exports.BlockInfo = BlockInfo;
|
|
7732
8949
|
exports.Decoration = Decoration;
|
|
7733
8950
|
exports.EditorView = EditorView;
|
|
8951
|
+
exports.GutterMarker = GutterMarker;
|
|
7734
8952
|
exports.MatchDecorator = MatchDecorator;
|
|
7735
|
-
exports.PluginField = PluginField;
|
|
7736
|
-
exports.PluginFieldProvider = PluginFieldProvider;
|
|
7737
8953
|
exports.ViewPlugin = ViewPlugin;
|
|
7738
8954
|
exports.ViewUpdate = ViewUpdate;
|
|
7739
8955
|
exports.WidgetType = WidgetType;
|
|
7740
8956
|
exports.__test = __test;
|
|
8957
|
+
exports.closeHoverTooltips = closeHoverTooltips;
|
|
8958
|
+
exports.crosshairCursor = crosshairCursor;
|
|
7741
8959
|
exports.drawSelection = drawSelection;
|
|
7742
8960
|
exports.dropCursor = dropCursor;
|
|
8961
|
+
exports.getPanel = getPanel;
|
|
8962
|
+
exports.getTooltip = getTooltip;
|
|
8963
|
+
exports.gutter = gutter;
|
|
8964
|
+
exports.gutterLineClass = gutterLineClass;
|
|
8965
|
+
exports.gutters = gutters;
|
|
8966
|
+
exports.hasHoverTooltips = hasHoverTooltips;
|
|
7743
8967
|
exports.highlightActiveLine = highlightActiveLine;
|
|
8968
|
+
exports.highlightActiveLineGutter = highlightActiveLineGutter;
|
|
7744
8969
|
exports.highlightSpecialChars = highlightSpecialChars;
|
|
8970
|
+
exports.hoverTooltip = hoverTooltip;
|
|
7745
8971
|
exports.keymap = keymap;
|
|
8972
|
+
exports.lineNumberMarkers = lineNumberMarkers;
|
|
8973
|
+
exports.lineNumbers = lineNumbers;
|
|
7746
8974
|
exports.logException = logException;
|
|
8975
|
+
exports.panels = panels;
|
|
7747
8976
|
exports.placeholder = placeholder;
|
|
8977
|
+
exports.rectangularSelection = rectangularSelection;
|
|
8978
|
+
exports.repositionTooltips = repositionTooltips;
|
|
7748
8979
|
exports.runScopeHandlers = runScopeHandlers;
|
|
7749
8980
|
exports.scrollPastEnd = scrollPastEnd;
|
|
8981
|
+
exports.showPanel = showPanel;
|
|
8982
|
+
exports.showTooltip = showTooltip;
|
|
8983
|
+
exports.tooltips = tooltips;
|