@codemirror/view 0.19.47 → 0.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +52 -0
- package/dist/index.cjs +1635 -402
- package/dist/index.d.ts +533 -247
- package/dist/index.js +1595 -376
- package/package.json +2 -4
package/dist/index.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) {
|
|
@@ -318,32 +316,34 @@ class ContentView {
|
|
|
318
316
|
sync(track) {
|
|
319
317
|
if (this.dirty & 2 /* Node */) {
|
|
320
318
|
let parent = this.dom;
|
|
321
|
-
let
|
|
319
|
+
let prev = null, next;
|
|
322
320
|
for (let child of this.children) {
|
|
323
321
|
if (child.dirty) {
|
|
324
|
-
if (!child.dom &&
|
|
325
|
-
let contentView = ContentView.get(
|
|
322
|
+
if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild)) {
|
|
323
|
+
let contentView = ContentView.get(next);
|
|
326
324
|
if (!contentView || !contentView.parent && contentView.constructor == child.constructor)
|
|
327
|
-
child.reuseDOM(
|
|
325
|
+
child.reuseDOM(next);
|
|
328
326
|
}
|
|
329
327
|
child.sync(track);
|
|
330
328
|
child.dirty = 0 /* Not */;
|
|
331
329
|
}
|
|
332
|
-
|
|
330
|
+
next = prev ? prev.nextSibling : parent.firstChild;
|
|
331
|
+
if (track && !track.written && track.node == parent && next != child.dom)
|
|
333
332
|
track.written = true;
|
|
334
333
|
if (child.dom.parentNode == parent) {
|
|
335
|
-
while (
|
|
336
|
-
|
|
337
|
-
pos = child.dom.nextSibling;
|
|
334
|
+
while (next && next != child.dom)
|
|
335
|
+
next = rm$1(next);
|
|
338
336
|
}
|
|
339
337
|
else {
|
|
340
|
-
parent.insertBefore(child.dom,
|
|
338
|
+
parent.insertBefore(child.dom, next);
|
|
341
339
|
}
|
|
340
|
+
prev = child.dom;
|
|
342
341
|
}
|
|
343
|
-
|
|
342
|
+
next = prev ? prev.nextSibling : parent.firstChild;
|
|
343
|
+
if (next && track && track.node == parent)
|
|
344
344
|
track.written = true;
|
|
345
|
-
while (
|
|
346
|
-
|
|
345
|
+
while (next)
|
|
346
|
+
next = rm$1(next);
|
|
347
347
|
}
|
|
348
348
|
else if (this.dirty & 1 /* Child */) {
|
|
349
349
|
for (let child of this.children)
|
|
@@ -489,7 +489,7 @@ class ContentView {
|
|
|
489
489
|
}
|
|
490
490
|
ContentView.prototype.breakAfter = 0;
|
|
491
491
|
// Remove a DOM node and return its next sibling.
|
|
492
|
-
function rm(dom) {
|
|
492
|
+
function rm$1(dom) {
|
|
493
493
|
let next = dom.nextSibling;
|
|
494
494
|
dom.parentNode.removeChild(dom);
|
|
495
495
|
return next;
|
|
@@ -825,12 +825,12 @@ class WidgetView extends ContentView {
|
|
|
825
825
|
ignoreEvent(event) { return this.widget.ignoreEvent(event); }
|
|
826
826
|
get overrideDOMText() {
|
|
827
827
|
if (this.length == 0)
|
|
828
|
-
return
|
|
828
|
+
return state.Text.empty;
|
|
829
829
|
let top = this;
|
|
830
830
|
while (top.parent)
|
|
831
831
|
top = top.parent;
|
|
832
|
-
let view = top.editorView, text
|
|
833
|
-
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;
|
|
834
834
|
}
|
|
835
835
|
domAtPos(pos) {
|
|
836
836
|
return pos == 0 ? DOMPos.before(this.dom) : DOMPos.after(this.dom, pos == this.length);
|
|
@@ -953,7 +953,7 @@ class WidgetBufferView extends ContentView {
|
|
|
953
953
|
? { left: imgRect.left, right: imgRect.right, top: siblingRect.top, bottom: siblingRect.bottom } : imgRect;
|
|
954
954
|
}
|
|
955
955
|
get overrideDOMText() {
|
|
956
|
-
return
|
|
956
|
+
return state.Text.empty;
|
|
957
957
|
}
|
|
958
958
|
}
|
|
959
959
|
TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren;
|
|
@@ -1077,7 +1077,7 @@ function updateAttrs(dom, prev, attrs) {
|
|
|
1077
1077
|
Widgets added to the content are described by subclasses of this
|
|
1078
1078
|
class. Using a description object like that makes it possible to
|
|
1079
1079
|
delay creating of the DOM structure for a widget until it is
|
|
1080
|
-
needed, and to avoid redrawing widgets even
|
|
1080
|
+
needed, and to avoid redrawing widgets even if the decorations
|
|
1081
1081
|
that define them are recreated.
|
|
1082
1082
|
*/
|
|
1083
1083
|
class WidgetType {
|
|
@@ -1090,7 +1090,7 @@ class WidgetType {
|
|
|
1090
1090
|
returns `false`, which will cause new instances of the widget to
|
|
1091
1091
|
always be redrawn.
|
|
1092
1092
|
*/
|
|
1093
|
-
eq(
|
|
1093
|
+
eq(widget) { return false; }
|
|
1094
1094
|
/**
|
|
1095
1095
|
Update a DOM element created by a widget of the same type (but
|
|
1096
1096
|
different, non-`eq` content) to reflect this widget. May return
|
|
@@ -1098,7 +1098,7 @@ class WidgetType {
|
|
|
1098
1098
|
couldn't (in which case the widget will be redrawn). The default
|
|
1099
1099
|
implementation just returns false.
|
|
1100
1100
|
*/
|
|
1101
|
-
updateDOM(
|
|
1101
|
+
updateDOM(dom) { return false; }
|
|
1102
1102
|
/**
|
|
1103
1103
|
@internal
|
|
1104
1104
|
*/
|
|
@@ -1117,7 +1117,7 @@ class WidgetType {
|
|
|
1117
1117
|
should be ignored by the editor. The default is to ignore all
|
|
1118
1118
|
events.
|
|
1119
1119
|
*/
|
|
1120
|
-
ignoreEvent(
|
|
1120
|
+
ignoreEvent(event) { return true; }
|
|
1121
1121
|
/**
|
|
1122
1122
|
@internal
|
|
1123
1123
|
*/
|
|
@@ -1126,7 +1126,7 @@ class WidgetType {
|
|
|
1126
1126
|
This is called when the an instance of the widget is removed
|
|
1127
1127
|
from the editor view.
|
|
1128
1128
|
*/
|
|
1129
|
-
destroy(
|
|
1129
|
+
destroy(dom) { }
|
|
1130
1130
|
}
|
|
1131
1131
|
/**
|
|
1132
1132
|
The different types of blocks that can occur in an editor view.
|
|
@@ -1153,9 +1153,10 @@ exports.BlockType = void 0;
|
|
|
1153
1153
|
/**
|
|
1154
1154
|
A decoration provides information on how to draw or style a piece
|
|
1155
1155
|
of content. You'll usually use it wrapped in a
|
|
1156
|
-
[`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
|
|
1157
1158
|
*/
|
|
1158
|
-
class Decoration extends
|
|
1159
|
+
class Decoration extends state.RangeValue {
|
|
1159
1160
|
/**
|
|
1160
1161
|
@internal
|
|
1161
1162
|
*/
|
|
@@ -1192,18 +1193,17 @@ class Decoration extends rangeset.RangeValue {
|
|
|
1192
1193
|
Create a mark decoration, which influences the styling of the
|
|
1193
1194
|
content in its range. Nested mark decorations will cause nested
|
|
1194
1195
|
DOM elements to be created. Nesting order is determined by
|
|
1195
|
-
precedence of the [facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations)
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
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.
|
|
1200
1200
|
*/
|
|
1201
1201
|
static mark(spec) {
|
|
1202
1202
|
return new MarkDecoration(spec);
|
|
1203
1203
|
}
|
|
1204
1204
|
/**
|
|
1205
|
-
Create a widget decoration, which
|
|
1206
|
-
position.
|
|
1205
|
+
Create a widget decoration, which displays a DOM element at the
|
|
1206
|
+
given position.
|
|
1207
1207
|
*/
|
|
1208
1208
|
static widget(spec) {
|
|
1209
1209
|
let side = spec.side || 0, block = !!spec.block;
|
|
@@ -1240,7 +1240,7 @@ class Decoration extends rangeset.RangeValue {
|
|
|
1240
1240
|
pass `true` for `sort` to make the library sort them for you.
|
|
1241
1241
|
*/
|
|
1242
1242
|
static set(of, sort = false) {
|
|
1243
|
-
return
|
|
1243
|
+
return state.RangeSet.of(of, sort);
|
|
1244
1244
|
}
|
|
1245
1245
|
/**
|
|
1246
1246
|
@internal
|
|
@@ -1250,7 +1250,7 @@ class Decoration extends rangeset.RangeValue {
|
|
|
1250
1250
|
/**
|
|
1251
1251
|
The empty set of decorations.
|
|
1252
1252
|
*/
|
|
1253
|
-
Decoration.none =
|
|
1253
|
+
Decoration.none = state.RangeSet.empty;
|
|
1254
1254
|
class MarkDecoration extends Decoration {
|
|
1255
1255
|
constructor(spec) {
|
|
1256
1256
|
let { start, end } = getInclusive(spec);
|
|
@@ -1541,11 +1541,11 @@ class BlockWidgetView extends ContentView {
|
|
|
1541
1541
|
}
|
|
1542
1542
|
|
|
1543
1543
|
class ContentBuilder {
|
|
1544
|
-
constructor(doc, pos, end,
|
|
1544
|
+
constructor(doc, pos, end, disallowBlockEffectsFor) {
|
|
1545
1545
|
this.doc = doc;
|
|
1546
1546
|
this.pos = pos;
|
|
1547
1547
|
this.end = end;
|
|
1548
|
-
this.
|
|
1548
|
+
this.disallowBlockEffectsFor = disallowBlockEffectsFor;
|
|
1549
1549
|
this.content = [];
|
|
1550
1550
|
this.curLine = null;
|
|
1551
1551
|
this.breakAtStart = 0;
|
|
@@ -1630,7 +1630,13 @@ class ContentBuilder {
|
|
|
1630
1630
|
if (this.openStart < 0)
|
|
1631
1631
|
this.openStart = openStart;
|
|
1632
1632
|
}
|
|
1633
|
-
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
|
+
}
|
|
1634
1640
|
let len = to - from;
|
|
1635
1641
|
if (deco instanceof PointDecoration) {
|
|
1636
1642
|
if (deco.block) {
|
|
@@ -1674,18 +1680,9 @@ class ContentBuilder {
|
|
|
1674
1680
|
if (this.openStart < 0)
|
|
1675
1681
|
this.openStart = openStart;
|
|
1676
1682
|
}
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
throw new RangeError("Block decorations may not be specified via plugins");
|
|
1681
|
-
if (to > this.doc.lineAt(this.pos).to)
|
|
1682
|
-
throw new RangeError("Decorations that replace line breaks may not be specified via plugins");
|
|
1683
|
-
}
|
|
1684
|
-
return true;
|
|
1685
|
-
}
|
|
1686
|
-
static build(text, from, to, decorations, pluginDecorationLength) {
|
|
1687
|
-
let builder = new ContentBuilder(text, from, to, pluginDecorationLength);
|
|
1688
|
-
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);
|
|
1689
1686
|
if (builder.openStart < 0)
|
|
1690
1687
|
builder.openStart = builder.openEnd;
|
|
1691
1688
|
builder.finish(builder.openEnd);
|
|
@@ -1714,13 +1711,8 @@ const mouseSelectionStyle = state.Facet.define();
|
|
|
1714
1711
|
const exceptionSink = state.Facet.define();
|
|
1715
1712
|
const updateListener = state.Facet.define();
|
|
1716
1713
|
const inputHandler = state.Facet.define();
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
map: (range, changes) => range.map(changes)
|
|
1720
|
-
});
|
|
1721
|
-
// FIXME remove
|
|
1722
|
-
const centerOn = state.StateEffect.define({
|
|
1723
|
-
map: (range, changes) => range.map(changes)
|
|
1714
|
+
const perLineTextDirection = state.Facet.define({
|
|
1715
|
+
combine: values => values.some(x => x)
|
|
1724
1716
|
});
|
|
1725
1717
|
class ScrollTarget {
|
|
1726
1718
|
constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5) {
|
|
@@ -1759,83 +1751,6 @@ function logException(state, exception, context) {
|
|
|
1759
1751
|
console.error(exception);
|
|
1760
1752
|
}
|
|
1761
1753
|
const editable = state.Facet.define({ combine: values => values.length ? values[0] : true });
|
|
1762
|
-
/**
|
|
1763
|
-
Used to [declare](https://codemirror.net/6/docs/ref/#view.PluginSpec.provide) which
|
|
1764
|
-
[fields](https://codemirror.net/6/docs/ref/#view.PluginValue) a [view plugin](https://codemirror.net/6/docs/ref/#view.ViewPlugin)
|
|
1765
|
-
provides.
|
|
1766
|
-
*/
|
|
1767
|
-
class PluginFieldProvider {
|
|
1768
|
-
/**
|
|
1769
|
-
@internal
|
|
1770
|
-
*/
|
|
1771
|
-
constructor(
|
|
1772
|
-
/**
|
|
1773
|
-
@internal
|
|
1774
|
-
*/
|
|
1775
|
-
field,
|
|
1776
|
-
/**
|
|
1777
|
-
@internal
|
|
1778
|
-
*/
|
|
1779
|
-
get) {
|
|
1780
|
-
this.field = field;
|
|
1781
|
-
this.get = get;
|
|
1782
|
-
}
|
|
1783
|
-
}
|
|
1784
|
-
/**
|
|
1785
|
-
Plugin fields are a mechanism for allowing plugins to provide
|
|
1786
|
-
values that can be retrieved through the
|
|
1787
|
-
[`pluginField`](https://codemirror.net/6/docs/ref/#view.EditorView.pluginField) view method.
|
|
1788
|
-
*/
|
|
1789
|
-
class PluginField {
|
|
1790
|
-
/**
|
|
1791
|
-
Create a [provider](https://codemirror.net/6/docs/ref/#view.PluginFieldProvider) for this field,
|
|
1792
|
-
to use with a plugin's [provide](https://codemirror.net/6/docs/ref/#view.PluginSpec.provide)
|
|
1793
|
-
option.
|
|
1794
|
-
*/
|
|
1795
|
-
from(get) {
|
|
1796
|
-
return new PluginFieldProvider(this, get);
|
|
1797
|
-
}
|
|
1798
|
-
/**
|
|
1799
|
-
Define a new plugin field.
|
|
1800
|
-
*/
|
|
1801
|
-
static define() { return new PluginField(); }
|
|
1802
|
-
}
|
|
1803
|
-
/**
|
|
1804
|
-
This field can be used by plugins to provide
|
|
1805
|
-
[decorations](https://codemirror.net/6/docs/ref/#view.Decoration).
|
|
1806
|
-
|
|
1807
|
-
**Note**: For reasons of data flow (plugins are only updated
|
|
1808
|
-
after the viewport is computed), decorations produced by plugins
|
|
1809
|
-
are _not_ taken into account when predicting the vertical layout
|
|
1810
|
-
structure of the editor. They **must not** introduce block
|
|
1811
|
-
widgets (that will raise an error) or replacing decorations that
|
|
1812
|
-
cover line breaks (these will be ignored if they occur). Such
|
|
1813
|
-
decorations, or others that cause a large amount of vertical
|
|
1814
|
-
size shift compared to the undecorated content, should be
|
|
1815
|
-
provided through the state-level [`decorations`
|
|
1816
|
-
facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations) instead.
|
|
1817
|
-
*/
|
|
1818
|
-
PluginField.decorations = PluginField.define();
|
|
1819
|
-
/**
|
|
1820
|
-
Used to provide ranges that should be treated as atoms as far as
|
|
1821
|
-
cursor motion is concerned. This causes methods like
|
|
1822
|
-
[`moveByChar`](https://codemirror.net/6/docs/ref/#view.EditorView.moveByChar) and
|
|
1823
|
-
[`moveVertically`](https://codemirror.net/6/docs/ref/#view.EditorView.moveVertically) (and the
|
|
1824
|
-
commands built on top of them) to skip across such regions when
|
|
1825
|
-
a selection endpoint would enter them. This does _not_ prevent
|
|
1826
|
-
direct programmatic [selection
|
|
1827
|
-
updates](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection) from moving into such
|
|
1828
|
-
regions.
|
|
1829
|
-
*/
|
|
1830
|
-
PluginField.atomicRanges = PluginField.define();
|
|
1831
|
-
/**
|
|
1832
|
-
Plugins can provide additional scroll margins (space around the
|
|
1833
|
-
sides of the scrolling element that should be considered
|
|
1834
|
-
invisible) through this field. This can be useful when the
|
|
1835
|
-
plugin introduces elements that cover part of that element (for
|
|
1836
|
-
example a horizontally fixed gutter).
|
|
1837
|
-
*/
|
|
1838
|
-
PluginField.scrollMargins = PluginField.define();
|
|
1839
1754
|
let nextPluginID = 0;
|
|
1840
1755
|
const viewPlugin = state.Facet.define();
|
|
1841
1756
|
/**
|
|
@@ -1856,27 +1771,29 @@ class ViewPlugin {
|
|
|
1856
1771
|
/**
|
|
1857
1772
|
@internal
|
|
1858
1773
|
*/
|
|
1859
|
-
|
|
1774
|
+
domEventHandlers, buildExtensions) {
|
|
1860
1775
|
this.id = id;
|
|
1861
1776
|
this.create = create;
|
|
1862
|
-
this.
|
|
1863
|
-
this.extension =
|
|
1777
|
+
this.domEventHandlers = domEventHandlers;
|
|
1778
|
+
this.extension = buildExtensions(this);
|
|
1864
1779
|
}
|
|
1865
1780
|
/**
|
|
1866
1781
|
Define a plugin from a constructor function that creates the
|
|
1867
1782
|
plugin's value, given an editor view.
|
|
1868
1783
|
*/
|
|
1869
1784
|
static define(create, spec) {
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
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
|
+
});
|
|
1880
1797
|
}
|
|
1881
1798
|
/**
|
|
1882
1799
|
Create a plugin for a class whose constructor takes a single
|
|
@@ -1886,7 +1803,6 @@ class ViewPlugin {
|
|
|
1886
1803
|
return ViewPlugin.define(view => new cls(view), spec);
|
|
1887
1804
|
}
|
|
1888
1805
|
}
|
|
1889
|
-
const domEventHandlers = PluginField.define();
|
|
1890
1806
|
class PluginInstance {
|
|
1891
1807
|
constructor(spec) {
|
|
1892
1808
|
this.spec = spec;
|
|
@@ -1899,12 +1815,6 @@ class PluginInstance {
|
|
|
1899
1815
|
// initialized on the first update.
|
|
1900
1816
|
this.value = null;
|
|
1901
1817
|
}
|
|
1902
|
-
takeField(type, target) {
|
|
1903
|
-
if (this.spec)
|
|
1904
|
-
for (let { field, get } of this.spec.fields)
|
|
1905
|
-
if (field == type)
|
|
1906
|
-
target.push(get(this.value));
|
|
1907
|
-
}
|
|
1908
1818
|
update(view) {
|
|
1909
1819
|
if (!this.value) {
|
|
1910
1820
|
if (this.spec) {
|
|
@@ -1956,6 +1866,8 @@ const editorAttributes = state.Facet.define();
|
|
|
1956
1866
|
const contentAttributes = state.Facet.define();
|
|
1957
1867
|
// Provide decorations
|
|
1958
1868
|
const decorations = state.Facet.define();
|
|
1869
|
+
const atomicRanges = state.Facet.define();
|
|
1870
|
+
const scrollMargins = state.Facet.define();
|
|
1959
1871
|
const styleModule = state.Facet.define();
|
|
1960
1872
|
class ChangedRange {
|
|
1961
1873
|
constructor(fromA, toA, fromB, toB) {
|
|
@@ -2056,8 +1968,8 @@ class ViewUpdate {
|
|
|
2056
1968
|
return (this.flags & 4 /* Viewport */) > 0;
|
|
2057
1969
|
}
|
|
2058
1970
|
/**
|
|
2059
|
-
Indicates whether the height of
|
|
2060
|
-
in this update.
|
|
1971
|
+
Indicates whether the height of a block element in the editor
|
|
1972
|
+
changed in this update.
|
|
2061
1973
|
*/
|
|
2062
1974
|
get heightChanged() {
|
|
2063
1975
|
return (this.flags & 2 /* Height */) > 0;
|
|
@@ -2393,7 +2305,7 @@ function moveVisually(line, order, dir, start, forward) {
|
|
|
2393
2305
|
startIndex = span.side(!forward, dir);
|
|
2394
2306
|
}
|
|
2395
2307
|
let indexForward = forward == (span.dir == dir);
|
|
2396
|
-
let nextIndex =
|
|
2308
|
+
let nextIndex = state.findClusterBreak(line.text, startIndex, indexForward);
|
|
2397
2309
|
movedOver = line.text.slice(Math.min(startIndex, nextIndex), Math.max(startIndex, nextIndex));
|
|
2398
2310
|
if (nextIndex != span.side(forward, dir))
|
|
2399
2311
|
return state.EditorSelection.cursor(nextIndex + line.from, indexForward ? -1 : 1, span.level);
|
|
@@ -2517,7 +2429,7 @@ class DocView extends ContentView {
|
|
|
2517
2429
|
this.view = view;
|
|
2518
2430
|
this.compositionDeco = Decoration.none;
|
|
2519
2431
|
this.decorations = [];
|
|
2520
|
-
this.
|
|
2432
|
+
this.dynamicDecorationMap = [];
|
|
2521
2433
|
// Track a minimum width for the editor. When measuring sizes in
|
|
2522
2434
|
// measureVisibleLineHeights, this is updated to point at the width
|
|
2523
2435
|
// of a given element and its extent in the document. When a change
|
|
@@ -2623,7 +2535,7 @@ class DocView extends ContentView {
|
|
|
2623
2535
|
if (!next)
|
|
2624
2536
|
break;
|
|
2625
2537
|
let { fromA, toA, fromB, toB } = next;
|
|
2626
|
-
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);
|
|
2627
2539
|
let { i: toI, off: toOff } = cursor.findPos(toA, 1);
|
|
2628
2540
|
let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
|
|
2629
2541
|
replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
|
|
@@ -2764,11 +2676,11 @@ class DocView extends ContentView {
|
|
|
2764
2676
|
off = start;
|
|
2765
2677
|
}
|
|
2766
2678
|
}
|
|
2767
|
-
measureVisibleLineHeights() {
|
|
2768
|
-
let result = [], { from, to } =
|
|
2679
|
+
measureVisibleLineHeights(viewport) {
|
|
2680
|
+
let result = [], { from, to } = viewport;
|
|
2769
2681
|
let contentWidth = this.view.contentDOM.clientWidth;
|
|
2770
2682
|
let isWider = contentWidth > Math.max(this.view.scrollDOM.clientWidth, this.minWidth) + 1;
|
|
2771
|
-
let widest = -1;
|
|
2683
|
+
let widest = -1, ltr = this.view.textDirection == exports.Direction.LTR;
|
|
2772
2684
|
for (let pos = 0, i = 0; i < this.children.length; i++) {
|
|
2773
2685
|
let child = this.children[i], end = pos + child.length;
|
|
2774
2686
|
if (end > to)
|
|
@@ -2781,8 +2693,7 @@ class DocView extends ContentView {
|
|
|
2781
2693
|
let rects = last ? clientRectsFor(last) : [];
|
|
2782
2694
|
if (rects.length) {
|
|
2783
2695
|
let rect = rects[rects.length - 1];
|
|
2784
|
-
let width =
|
|
2785
|
-
: childRect.right - rect.left;
|
|
2696
|
+
let width = ltr ? rect.right - childRect.left : childRect.right - rect.left;
|
|
2786
2697
|
if (width > widest) {
|
|
2787
2698
|
widest = width;
|
|
2788
2699
|
this.minWidth = contentWidth;
|
|
@@ -2796,6 +2707,10 @@ class DocView extends ContentView {
|
|
|
2796
2707
|
}
|
|
2797
2708
|
return result;
|
|
2798
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
|
+
}
|
|
2799
2714
|
measureTextSize() {
|
|
2800
2715
|
for (let child of this.children) {
|
|
2801
2716
|
if (child instanceof LineView) {
|
|
@@ -2847,11 +2762,14 @@ class DocView extends ContentView {
|
|
|
2847
2762
|
return Decoration.set(deco);
|
|
2848
2763
|
}
|
|
2849
2764
|
updateDeco() {
|
|
2850
|
-
let
|
|
2851
|
-
|
|
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;
|
|
2852
2771
|
return this.decorations = [
|
|
2853
|
-
...
|
|
2854
|
-
...this.view.state.facet(decorations),
|
|
2772
|
+
...allDeco,
|
|
2855
2773
|
this.compositionDeco,
|
|
2856
2774
|
this.computeBlockGapDeco(),
|
|
2857
2775
|
this.view.viewState.lineGapDeco
|
|
@@ -2866,7 +2784,7 @@ class DocView extends ContentView {
|
|
|
2866
2784
|
rect = { left: Math.min(rect.left, other.left), top: Math.min(rect.top, other.top),
|
|
2867
2785
|
right: Math.max(rect.right, other.right), bottom: Math.max(rect.bottom, other.bottom) };
|
|
2868
2786
|
let mLeft = 0, mRight = 0, mTop = 0, mBottom = 0;
|
|
2869
|
-
for (let margins of this.view.
|
|
2787
|
+
for (let margins of this.view.state.facet(scrollMargins).map(f => f(this.view)))
|
|
2870
2788
|
if (margins) {
|
|
2871
2789
|
let { left, right, top, bottom } = margins;
|
|
2872
2790
|
if (left != null)
|
|
@@ -3008,7 +2926,7 @@ class DecorationComparator$1 {
|
|
|
3008
2926
|
}
|
|
3009
2927
|
function findChangedDeco(a, b, diff) {
|
|
3010
2928
|
let comp = new DecorationComparator$1;
|
|
3011
|
-
|
|
2929
|
+
state.RangeSet.compare(a, b, diff, comp);
|
|
3012
2930
|
return comp.changes;
|
|
3013
2931
|
}
|
|
3014
2932
|
function inUneditable(node, inside) {
|
|
@@ -3031,18 +2949,18 @@ function groupAt(state$1, pos, bias = 1) {
|
|
|
3031
2949
|
bias = -1;
|
|
3032
2950
|
let from = linePos, to = linePos;
|
|
3033
2951
|
if (bias < 0)
|
|
3034
|
-
from =
|
|
2952
|
+
from = state.findClusterBreak(line.text, linePos, false);
|
|
3035
2953
|
else
|
|
3036
|
-
to =
|
|
2954
|
+
to = state.findClusterBreak(line.text, linePos);
|
|
3037
2955
|
let cat = categorize(line.text.slice(from, to));
|
|
3038
2956
|
while (from > 0) {
|
|
3039
|
-
let prev =
|
|
2957
|
+
let prev = state.findClusterBreak(line.text, from, false);
|
|
3040
2958
|
if (categorize(line.text.slice(prev, from)) != cat)
|
|
3041
2959
|
break;
|
|
3042
2960
|
from = prev;
|
|
3043
2961
|
}
|
|
3044
2962
|
while (to < line.length) {
|
|
3045
|
-
let next =
|
|
2963
|
+
let next = state.findClusterBreak(line.text, to);
|
|
3046
2964
|
if (categorize(line.text.slice(to, next)) != cat)
|
|
3047
2965
|
break;
|
|
3048
2966
|
to = next;
|
|
@@ -3234,7 +3152,7 @@ function posAtCoordsImprecise(view, contentRect, block, x, y) {
|
|
|
3234
3152
|
into += line * view.viewState.heightOracle.lineLength;
|
|
3235
3153
|
}
|
|
3236
3154
|
let content = view.state.sliceDoc(block.from, block.to);
|
|
3237
|
-
return block.from +
|
|
3155
|
+
return block.from + state.findColumn(content, into, view.state.tabSize);
|
|
3238
3156
|
}
|
|
3239
3157
|
// In case of a high line height, Safari's caretRangeFromPoint treats
|
|
3240
3158
|
// the space between lines as belonging to the last character of the
|
|
@@ -3255,7 +3173,8 @@ function moveToLineBoundary(view, start, forward, includeWrap) {
|
|
|
3255
3173
|
: view.coordsAtPos(start.assoc < 0 && start.head > line.from ? start.head - 1 : start.head);
|
|
3256
3174
|
if (coords) {
|
|
3257
3175
|
let editorRect = view.dom.getBoundingClientRect();
|
|
3258
|
-
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,
|
|
3259
3178
|
y: (coords.top + coords.bottom) / 2 });
|
|
3260
3179
|
if (pos != null)
|
|
3261
3180
|
return state.EditorSelection.cursor(pos, forward ? -1 : 1);
|
|
@@ -3266,8 +3185,9 @@ function moveToLineBoundary(view, start, forward, includeWrap) {
|
|
|
3266
3185
|
}
|
|
3267
3186
|
function moveByChar(view, start, forward, by) {
|
|
3268
3187
|
let line = view.state.doc.lineAt(start.head), spans = view.bidiSpans(line);
|
|
3188
|
+
let direction = view.textDirectionAt(line.from);
|
|
3269
3189
|
for (let cur = start, check = null;;) {
|
|
3270
|
-
let next = moveVisually(line, spans,
|
|
3190
|
+
let next = moveVisually(line, spans, direction, cur, forward), char = movedOver;
|
|
3271
3191
|
if (!next) {
|
|
3272
3192
|
if (line.number == (forward ? view.state.doc.lines : 1))
|
|
3273
3193
|
return cur;
|
|
@@ -3310,7 +3230,7 @@ function moveVertically(view, start, forward, distance) {
|
|
|
3310
3230
|
startY = dir < 0 ? startCoords.top : startCoords.bottom;
|
|
3311
3231
|
}
|
|
3312
3232
|
else {
|
|
3313
|
-
let line = view.viewState.lineBlockAt(startPos
|
|
3233
|
+
let line = view.viewState.lineBlockAt(startPos);
|
|
3314
3234
|
if (goal == null)
|
|
3315
3235
|
goal = Math.min(rect.right - rect.left, view.defaultCharacterWidth * (startPos - line.from));
|
|
3316
3236
|
startY = (dir < 0 ? line.top : line.bottom) + docTop;
|
|
@@ -3325,7 +3245,7 @@ function moveVertically(view, start, forward, distance) {
|
|
|
3325
3245
|
}
|
|
3326
3246
|
}
|
|
3327
3247
|
function skipAtoms(view, oldPos, pos) {
|
|
3328
|
-
let atoms = view.
|
|
3248
|
+
let atoms = view.state.facet(atomicRanges).map(f => f(view));
|
|
3329
3249
|
for (;;) {
|
|
3330
3250
|
let moved = false;
|
|
3331
3251
|
for (let set of atoms) {
|
|
@@ -3373,10 +3293,10 @@ class InputState {
|
|
|
3373
3293
|
for (let type in handlers) {
|
|
3374
3294
|
let handler = handlers[type];
|
|
3375
3295
|
view.contentDOM.addEventListener(type, (event) => {
|
|
3376
|
-
if (type == "keydown" && this.keydown(view, event))
|
|
3377
|
-
return;
|
|
3378
3296
|
if (!eventBelongsToEditor(view, event) || this.ignoreDuringComposition(event))
|
|
3379
3297
|
return;
|
|
3298
|
+
if (type == "keydown" && this.keydown(view, event))
|
|
3299
|
+
return;
|
|
3380
3300
|
if (this.mustFlushObserver(event))
|
|
3381
3301
|
view.observer.forceFlush();
|
|
3382
3302
|
if (this.runCustomHandlers(type, view, event))
|
|
@@ -3387,7 +3307,6 @@ class InputState {
|
|
|
3387
3307
|
this.registeredEvents.push(type);
|
|
3388
3308
|
}
|
|
3389
3309
|
this.notifiedFocused = view.hasFocus;
|
|
3390
|
-
this.ensureHandlers(view);
|
|
3391
3310
|
// On Safari adding an input event handler somehow prevents an
|
|
3392
3311
|
// issue where the composition vanishes when you press enter.
|
|
3393
3312
|
if (browser.safari)
|
|
@@ -3397,20 +3316,23 @@ class InputState {
|
|
|
3397
3316
|
this.lastSelectionOrigin = origin;
|
|
3398
3317
|
this.lastSelectionTime = Date.now();
|
|
3399
3318
|
}
|
|
3400
|
-
ensureHandlers(view) {
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
event
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
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
|
+
}
|
|
3414
3336
|
}
|
|
3415
3337
|
runCustomHandlers(type, view, event) {
|
|
3416
3338
|
for (let set of this.customHandlers) {
|
|
@@ -3444,7 +3366,7 @@ class InputState {
|
|
|
3444
3366
|
// Must always run, even if a custom handler handled the event
|
|
3445
3367
|
this.lastKeyCode = event.keyCode;
|
|
3446
3368
|
this.lastKeyTime = Date.now();
|
|
3447
|
-
if (
|
|
3369
|
+
if (event.keyCode == 9 && Date.now() < this.lastEscPress + 2000)
|
|
3448
3370
|
return true;
|
|
3449
3371
|
// Chrome for Android usually doesn't fire proper key events, but
|
|
3450
3372
|
// occasionally does, usually surrounded by a bunch of complicated
|
|
@@ -3488,20 +3410,12 @@ class InputState {
|
|
|
3488
3410
|
// compositionend and keydown events are sometimes emitted in the
|
|
3489
3411
|
// wrong order. The key event should still be ignored, even when
|
|
3490
3412
|
// it happens after the compositionend event.
|
|
3491
|
-
if (browser.safari && Date.now() - this.compositionEndedAt <
|
|
3413
|
+
if (browser.safari && Date.now() - this.compositionEndedAt < 100) {
|
|
3492
3414
|
this.compositionEndedAt = 0;
|
|
3493
3415
|
return true;
|
|
3494
3416
|
}
|
|
3495
3417
|
return false;
|
|
3496
3418
|
}
|
|
3497
|
-
screenKeyEvent(view, event) {
|
|
3498
|
-
let protectedTab = event.keyCode == 9 && Date.now() < this.lastEscPress + 2000;
|
|
3499
|
-
if (event.keyCode == 27)
|
|
3500
|
-
this.lastEscPress = Date.now();
|
|
3501
|
-
else if (modifierCodes.indexOf(event.keyCode) < 0)
|
|
3502
|
-
this.lastEscPress = 0;
|
|
3503
|
-
return protectedTab;
|
|
3504
|
-
}
|
|
3505
3419
|
mustFlushObserver(event) {
|
|
3506
3420
|
return (event.type == "keydown" && event.keyCode != 229) ||
|
|
3507
3421
|
event.type == "compositionend" && !browser.ios;
|
|
@@ -3675,6 +3589,10 @@ function doPaste(view, input) {
|
|
|
3675
3589
|
}
|
|
3676
3590
|
handlers.keydown = (view, event) => {
|
|
3677
3591
|
view.inputState.setSelectionOrigin("select");
|
|
3592
|
+
if (event.keyCode == 27)
|
|
3593
|
+
view.inputState.lastEscPress = Date.now();
|
|
3594
|
+
else if (modifierCodes.indexOf(event.keyCode) < 0)
|
|
3595
|
+
view.inputState.lastEscPress = 0;
|
|
3678
3596
|
};
|
|
3679
3597
|
let lastTouch = 0;
|
|
3680
3598
|
handlers.touchstart = (view, e) => {
|
|
@@ -3932,14 +3850,6 @@ handlers.focus = handlers.blur = view => {
|
|
|
3932
3850
|
view.update([]);
|
|
3933
3851
|
}, 10);
|
|
3934
3852
|
};
|
|
3935
|
-
handlers.beforeprint = view => {
|
|
3936
|
-
view.viewState.printing = true;
|
|
3937
|
-
view.requestMeasure();
|
|
3938
|
-
setTimeout(() => {
|
|
3939
|
-
view.viewState.printing = false;
|
|
3940
|
-
view.requestMeasure();
|
|
3941
|
-
}, 2000);
|
|
3942
|
-
};
|
|
3943
3853
|
function forceClearComposition(view, rapid) {
|
|
3944
3854
|
if (view.docView.compositionDeco.size) {
|
|
3945
3855
|
view.inputState.rapidCompositionStart = rapid;
|
|
@@ -4006,9 +3916,8 @@ handlers.beforeinput = (view, event) => {
|
|
|
4006
3916
|
const wrappingWhiteSpace = ["pre-wrap", "normal", "pre-line", "break-spaces"];
|
|
4007
3917
|
class HeightOracle {
|
|
4008
3918
|
constructor() {
|
|
4009
|
-
this.doc =
|
|
3919
|
+
this.doc = state.Text.empty;
|
|
4010
3920
|
this.lineWrapping = false;
|
|
4011
|
-
this.direction = exports.Direction.LTR;
|
|
4012
3921
|
this.heightSamples = {};
|
|
4013
3922
|
this.lineHeight = 14;
|
|
4014
3923
|
this.charWidth = 7;
|
|
@@ -4029,8 +3938,8 @@ class HeightOracle {
|
|
|
4029
3938
|
return lines * this.lineHeight;
|
|
4030
3939
|
}
|
|
4031
3940
|
setDoc(doc) { this.doc = doc; return this; }
|
|
4032
|
-
|
|
4033
|
-
return (wrappingWhiteSpace.indexOf(whiteSpace) > -1) != this.lineWrapping
|
|
3941
|
+
mustRefreshForWrapping(whiteSpace) {
|
|
3942
|
+
return (wrappingWhiteSpace.indexOf(whiteSpace) > -1) != this.lineWrapping;
|
|
4034
3943
|
}
|
|
4035
3944
|
mustRefreshForHeights(lineHeights) {
|
|
4036
3945
|
let newHeight = false;
|
|
@@ -4046,13 +3955,10 @@ class HeightOracle {
|
|
|
4046
3955
|
}
|
|
4047
3956
|
return newHeight;
|
|
4048
3957
|
}
|
|
4049
|
-
refresh(whiteSpace,
|
|
3958
|
+
refresh(whiteSpace, lineHeight, charWidth, lineLength, knownHeights) {
|
|
4050
3959
|
let lineWrapping = wrappingWhiteSpace.indexOf(whiteSpace) > -1;
|
|
4051
|
-
let changed = Math.round(lineHeight) != Math.round(this.lineHeight) ||
|
|
4052
|
-
this.lineWrapping != lineWrapping ||
|
|
4053
|
-
this.direction != direction;
|
|
3960
|
+
let changed = Math.round(lineHeight) != Math.round(this.lineHeight) || this.lineWrapping != lineWrapping;
|
|
4054
3961
|
this.lineWrapping = lineWrapping;
|
|
4055
|
-
this.direction = direction;
|
|
4056
3962
|
this.lineHeight = lineHeight;
|
|
4057
3963
|
this.charWidth = charWidth;
|
|
4058
3964
|
this.lineLength = lineLength;
|
|
@@ -4133,12 +4039,6 @@ class BlockInfo {
|
|
|
4133
4039
|
.concat(Array.isArray(other.type) ? other.type : [other]);
|
|
4134
4040
|
return new BlockInfo(this.from, this.length + other.length, this.top, this.height + other.height, detail);
|
|
4135
4041
|
}
|
|
4136
|
-
/**
|
|
4137
|
-
FIXME remove on next breaking release @internal
|
|
4138
|
-
*/
|
|
4139
|
-
moveY(offset) {
|
|
4140
|
-
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);
|
|
4141
|
-
}
|
|
4142
4042
|
}
|
|
4143
4043
|
var QueryType;
|
|
4144
4044
|
(function (QueryType) {
|
|
@@ -4263,8 +4163,9 @@ class HeightMapBlock extends HeightMap {
|
|
|
4263
4163
|
lineAt(_value, _type, doc, top, offset) {
|
|
4264
4164
|
return this.blockAt(0, doc, top, offset);
|
|
4265
4165
|
}
|
|
4266
|
-
forEachLine(
|
|
4267
|
-
|
|
4166
|
+
forEachLine(from, to, doc, top, offset, f) {
|
|
4167
|
+
if (from <= offset + this.length && to >= offset)
|
|
4168
|
+
f(this.blockAt(0, doc, top, offset));
|
|
4268
4169
|
}
|
|
4269
4170
|
updateHeight(oracle, offset = 0, _force = false, measured) {
|
|
4270
4171
|
if (measured && measured.from <= offset && measured.more)
|
|
@@ -4646,13 +4547,13 @@ class NodeBuilder {
|
|
|
4646
4547
|
// to each other.
|
|
4647
4548
|
static build(oracle, decorations, from, to) {
|
|
4648
4549
|
let builder = new NodeBuilder(from, oracle);
|
|
4649
|
-
|
|
4550
|
+
state.RangeSet.spans(decorations, from, to, builder, 0);
|
|
4650
4551
|
return builder.finish(from);
|
|
4651
4552
|
}
|
|
4652
4553
|
}
|
|
4653
4554
|
function heightRelevantDecoChanges(a, b, diff) {
|
|
4654
4555
|
let comp = new DecorationComparator;
|
|
4655
|
-
|
|
4556
|
+
state.RangeSet.compare(a, b, diff, comp, 0);
|
|
4656
4557
|
return comp.changes;
|
|
4657
4558
|
}
|
|
4658
4559
|
class DecorationComparator {
|
|
@@ -4695,6 +4596,11 @@ function visiblePixelRange(dom, paddingTop) {
|
|
|
4695
4596
|
return { left: left - rect.left, right: Math.max(left, right) - rect.left,
|
|
4696
4597
|
top: top - (rect.top + paddingTop), bottom: Math.max(top, bottom) - (rect.top + paddingTop) };
|
|
4697
4598
|
}
|
|
4599
|
+
function fullPixelRange(dom, paddingTop) {
|
|
4600
|
+
let rect = dom.getBoundingClientRect();
|
|
4601
|
+
return { left: 0, right: rect.right - rect.left,
|
|
4602
|
+
top: paddingTop, bottom: rect.bottom - (rect.top + paddingTop) };
|
|
4603
|
+
}
|
|
4698
4604
|
// Line gaps are placeholder widgets used to hide pieces of overlong
|
|
4699
4605
|
// lines within the viewport, as a kludge to keep the editor
|
|
4700
4606
|
// responsive when a ridiculously long line is loaded into it.
|
|
@@ -4740,8 +4646,8 @@ class LineGapWidget extends WidgetType {
|
|
|
4740
4646
|
get estimatedHeight() { return this.vertical ? this.size : -1; }
|
|
4741
4647
|
}
|
|
4742
4648
|
class ViewState {
|
|
4743
|
-
constructor(state) {
|
|
4744
|
-
this.state = state;
|
|
4649
|
+
constructor(state$1) {
|
|
4650
|
+
this.state = state$1;
|
|
4745
4651
|
// These are contentDOM-local coordinates
|
|
4746
4652
|
this.pixelViewport = { left: 0, right: window.innerWidth, top: 0, bottom: 0 };
|
|
4747
4653
|
this.inView = true;
|
|
@@ -4760,6 +4666,7 @@ class ViewState {
|
|
|
4760
4666
|
// Flag set when editor content was redrawn, so that the next
|
|
4761
4667
|
// measure stage knows it must read DOM layout
|
|
4762
4668
|
this.mustMeasureContent = true;
|
|
4669
|
+
this.defaultTextDirection = exports.Direction.RTL;
|
|
4763
4670
|
this.visibleRanges = [];
|
|
4764
4671
|
// Cursor 'assoc' is only significant when the cursor is on a line
|
|
4765
4672
|
// wrap point, where it must stick to the character that it is
|
|
@@ -4770,7 +4677,8 @@ class ViewState {
|
|
|
4770
4677
|
// boundary and, if so, reset it to make sure it is positioned in
|
|
4771
4678
|
// the right place.
|
|
4772
4679
|
this.mustEnforceCursorAssoc = false;
|
|
4773
|
-
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)]);
|
|
4774
4682
|
this.viewport = this.getViewport(0, null);
|
|
4775
4683
|
this.updateViewportLines();
|
|
4776
4684
|
this.updateForViewport();
|
|
@@ -4798,13 +4706,13 @@ class ViewState {
|
|
|
4798
4706
|
});
|
|
4799
4707
|
}
|
|
4800
4708
|
update(update, scrollTarget = null) {
|
|
4801
|
-
let prev = this.state;
|
|
4802
4709
|
this.state = update.state;
|
|
4803
|
-
let
|
|
4710
|
+
let prevDeco = this.stateDeco;
|
|
4711
|
+
this.stateDeco = this.state.facet(decorations).filter(d => typeof d != "function");
|
|
4804
4712
|
let contentChanges = update.changedRanges;
|
|
4805
|
-
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)));
|
|
4806
4714
|
let prevHeight = this.heightMap.height;
|
|
4807
|
-
this.heightMap = this.heightMap.applyChanges(
|
|
4715
|
+
this.heightMap = this.heightMap.applyChanges(this.stateDeco, update.startState.doc, this.heightOracle.setDoc(this.state.doc), heightChanges);
|
|
4808
4716
|
if (this.heightMap.height != prevHeight)
|
|
4809
4717
|
update.flags |= 2 /* Height */;
|
|
4810
4718
|
let viewport = heightChanges.length ? this.mapViewport(this.viewport, update.changes) : this.viewport;
|
|
@@ -4829,8 +4737,9 @@ class ViewState {
|
|
|
4829
4737
|
measure(view) {
|
|
4830
4738
|
let dom = view.contentDOM, style = window.getComputedStyle(dom);
|
|
4831
4739
|
let oracle = this.heightOracle;
|
|
4832
|
-
let whiteSpace = style.whiteSpace
|
|
4833
|
-
|
|
4740
|
+
let whiteSpace = style.whiteSpace;
|
|
4741
|
+
this.defaultTextDirection = style.direction == "rtl" ? exports.Direction.RTL : exports.Direction.LTR;
|
|
4742
|
+
let refresh = this.heightOracle.mustRefreshForWrapping(whiteSpace);
|
|
4834
4743
|
let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != dom.clientHeight;
|
|
4835
4744
|
let result = 0, bias = 0;
|
|
4836
4745
|
if (this.editorWidth != view.scrollDOM.clientWidth) {
|
|
@@ -4851,8 +4760,7 @@ class ViewState {
|
|
|
4851
4760
|
}
|
|
4852
4761
|
}
|
|
4853
4762
|
// Pixel viewport
|
|
4854
|
-
let pixelViewport = this.printing ?
|
|
4855
|
-
: visiblePixelRange(dom, this.paddingTop);
|
|
4763
|
+
let pixelViewport = (this.printing ? fullPixelRange : visiblePixelRange)(dom, this.paddingTop);
|
|
4856
4764
|
let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
|
|
4857
4765
|
this.pixelViewport = pixelViewport;
|
|
4858
4766
|
let inView = this.pixelViewport.bottom > this.pixelViewport.top && this.pixelViewport.right > this.pixelViewport.left;
|
|
@@ -4870,12 +4778,12 @@ class ViewState {
|
|
|
4870
4778
|
result |= 8 /* Geometry */;
|
|
4871
4779
|
}
|
|
4872
4780
|
if (measureContent) {
|
|
4873
|
-
let lineHeights = view.docView.measureVisibleLineHeights();
|
|
4781
|
+
let lineHeights = view.docView.measureVisibleLineHeights(this.viewport);
|
|
4874
4782
|
if (oracle.mustRefreshForHeights(lineHeights))
|
|
4875
4783
|
refresh = true;
|
|
4876
4784
|
if (refresh || oracle.lineWrapping && Math.abs(contentWidth - this.contentDOMWidth) > oracle.charWidth) {
|
|
4877
4785
|
let { lineHeight, charWidth } = view.docView.measureTextSize();
|
|
4878
|
-
refresh = oracle.refresh(whiteSpace,
|
|
4786
|
+
refresh = oracle.refresh(whiteSpace, lineHeight, charWidth, contentWidth / charWidth, lineHeights);
|
|
4879
4787
|
if (refresh) {
|
|
4880
4788
|
view.docView.minWidth = 0;
|
|
4881
4789
|
result |= 8 /* Geometry */;
|
|
@@ -4886,7 +4794,10 @@ class ViewState {
|
|
|
4886
4794
|
else if (dTop < 0 && dBottom < 0)
|
|
4887
4795
|
bias = Math.min(dTop, dBottom);
|
|
4888
4796
|
oracle.heightChanged = false;
|
|
4889
|
-
|
|
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
|
+
}
|
|
4890
4801
|
if (oracle.heightChanged)
|
|
4891
4802
|
result |= 2 /* Height */;
|
|
4892
4803
|
}
|
|
@@ -4972,12 +4883,12 @@ class ViewState {
|
|
|
4972
4883
|
ensureLineGaps(current) {
|
|
4973
4884
|
let gaps = [];
|
|
4974
4885
|
// This won't work at all in predominantly right-to-left text.
|
|
4975
|
-
if (this.
|
|
4886
|
+
if (this.defaultTextDirection != exports.Direction.LTR)
|
|
4976
4887
|
return gaps;
|
|
4977
4888
|
for (let line of this.viewportLines) {
|
|
4978
4889
|
if (line.length < 4000 /* DoubleMargin */)
|
|
4979
4890
|
continue;
|
|
4980
|
-
let structure = lineStructure(line.from, line.to, this.
|
|
4891
|
+
let structure = lineStructure(line.from, line.to, this.stateDeco);
|
|
4981
4892
|
if (structure.total < 4000 /* DoubleMargin */)
|
|
4982
4893
|
continue;
|
|
4983
4894
|
let viewFrom, viewTo;
|
|
@@ -5028,11 +4939,11 @@ class ViewState {
|
|
|
5028
4939
|
}
|
|
5029
4940
|
}
|
|
5030
4941
|
computeVisibleRanges() {
|
|
5031
|
-
let deco = this.
|
|
4942
|
+
let deco = this.stateDeco;
|
|
5032
4943
|
if (this.lineGaps.length)
|
|
5033
4944
|
deco = deco.concat(this.lineGapDeco);
|
|
5034
4945
|
let ranges = [];
|
|
5035
|
-
|
|
4946
|
+
state.RangeSet.spans(deco, this.viewport.from, this.viewport.to, {
|
|
5036
4947
|
span(from, to) { ranges.push({ from, to }); },
|
|
5037
4948
|
point() { }
|
|
5038
4949
|
}, 20);
|
|
@@ -5064,9 +4975,9 @@ class Viewport {
|
|
|
5064
4975
|
this.to = to;
|
|
5065
4976
|
}
|
|
5066
4977
|
}
|
|
5067
|
-
function lineStructure(from, to,
|
|
4978
|
+
function lineStructure(from, to, stateDeco) {
|
|
5068
4979
|
let ranges = [], pos = from, total = 0;
|
|
5069
|
-
|
|
4980
|
+
state.RangeSet.spans(stateDeco, from, to, {
|
|
5070
4981
|
span() { },
|
|
5071
4982
|
point(from, to) {
|
|
5072
4983
|
if (from > pos) {
|
|
@@ -5199,7 +5110,7 @@ function buildTheme(main, spec, scopes) {
|
|
|
5199
5110
|
}
|
|
5200
5111
|
});
|
|
5201
5112
|
}
|
|
5202
|
-
const baseTheme = buildTheme("." + baseThemeID, {
|
|
5113
|
+
const baseTheme$1 = buildTheme("." + baseThemeID, {
|
|
5203
5114
|
"&.cm-editor": {
|
|
5204
5115
|
position: "relative !important",
|
|
5205
5116
|
boxSizing: "border-box",
|
|
@@ -5304,6 +5215,65 @@ const baseTheme = buildTheme("." + baseThemeID, {
|
|
|
5304
5215
|
"&dark .cm-activeLine": { backgroundColor: "#223039" },
|
|
5305
5216
|
"&light .cm-specialChar": { color: "red" },
|
|
5306
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
|
+
},
|
|
5307
5277
|
".cm-tab": {
|
|
5308
5278
|
display: "inline-block",
|
|
5309
5279
|
overflow: "hidden",
|
|
@@ -5429,6 +5399,7 @@ class DOMObserver {
|
|
|
5429
5399
|
});
|
|
5430
5400
|
this.resize.observe(view.scrollDOM);
|
|
5431
5401
|
}
|
|
5402
|
+
window.addEventListener("beforeprint", this.onPrint = this.onPrint.bind(this));
|
|
5432
5403
|
this.start();
|
|
5433
5404
|
window.addEventListener("scroll", this.onScroll = this.onScroll.bind(this));
|
|
5434
5405
|
if (typeof IntersectionObserver == "function") {
|
|
@@ -5463,6 +5434,14 @@ class DOMObserver {
|
|
|
5463
5434
|
this.view.requestMeasure();
|
|
5464
5435
|
}, 50);
|
|
5465
5436
|
}
|
|
5437
|
+
onPrint() {
|
|
5438
|
+
this.view.viewState.printing = true;
|
|
5439
|
+
this.view.measure();
|
|
5440
|
+
setTimeout(() => {
|
|
5441
|
+
this.view.viewState.printing = false;
|
|
5442
|
+
this.view.requestMeasure();
|
|
5443
|
+
}, 500);
|
|
5444
|
+
}
|
|
5466
5445
|
updateGaps(gaps) {
|
|
5467
5446
|
if (this.gapIntersection && (gaps.length != this.gaps.length || this.gaps.some((g, i) => g != gaps[i]))) {
|
|
5468
5447
|
this.gapIntersection.disconnect();
|
|
@@ -5680,6 +5659,7 @@ class DOMObserver {
|
|
|
5680
5659
|
dom.removeEventListener("scroll", this.onScroll);
|
|
5681
5660
|
window.removeEventListener("scroll", this.onScroll);
|
|
5682
5661
|
window.removeEventListener("resize", this.onResize);
|
|
5662
|
+
window.removeEventListener("beforeprint", this.onPrint);
|
|
5683
5663
|
this.dom.ownerDocument.removeEventListener("selectionchange", this.onSelectionChange);
|
|
5684
5664
|
clearTimeout(this.parentCheck);
|
|
5685
5665
|
clearTimeout(this.resizeTimeout);
|
|
@@ -5942,9 +5922,9 @@ transactions for editing actions.
|
|
|
5942
5922
|
*/
|
|
5943
5923
|
class EditorView {
|
|
5944
5924
|
/**
|
|
5945
|
-
Construct a new view. You'll
|
|
5946
|
-
|
|
5947
|
-
|
|
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.
|
|
5948
5928
|
*/
|
|
5949
5929
|
constructor(
|
|
5950
5930
|
/**
|
|
@@ -5995,6 +5975,7 @@ class EditorView {
|
|
|
5995
5975
|
this.measure();
|
|
5996
5976
|
});
|
|
5997
5977
|
this.inputState = new InputState(this);
|
|
5978
|
+
this.inputState.ensureHandlers(this, this.plugins);
|
|
5998
5979
|
this.docView = new DocView(this);
|
|
5999
5980
|
this.mountStyles();
|
|
6000
5981
|
this.updateAttrs();
|
|
@@ -6082,14 +6063,9 @@ class EditorView {
|
|
|
6082
6063
|
let { main } = tr.state.selection;
|
|
6083
6064
|
scrollTarget = new ScrollTarget(main.empty ? main : state.EditorSelection.cursor(main.head, main.head > main.anchor ? -1 : 1));
|
|
6084
6065
|
}
|
|
6085
|
-
for (let e of tr.effects)
|
|
6086
|
-
if (e.is(
|
|
6087
|
-
scrollTarget = new ScrollTarget(e.value);
|
|
6088
|
-
else if (e.is(centerOn))
|
|
6089
|
-
scrollTarget = new ScrollTarget(e.value, "center");
|
|
6090
|
-
else if (e.is(scrollIntoView))
|
|
6066
|
+
for (let e of tr.effects)
|
|
6067
|
+
if (e.is(scrollIntoView))
|
|
6091
6068
|
scrollTarget = e.value;
|
|
6092
|
-
}
|
|
6093
6069
|
}
|
|
6094
6070
|
this.viewState.update(update, scrollTarget);
|
|
6095
6071
|
this.bidiCache = CachedOrder.update(this.bidiCache, update.changes);
|
|
@@ -6140,7 +6116,7 @@ class EditorView {
|
|
|
6140
6116
|
for (let plugin of this.plugins)
|
|
6141
6117
|
plugin.update(this);
|
|
6142
6118
|
this.docView = new DocView(this);
|
|
6143
|
-
this.inputState.ensureHandlers(this);
|
|
6119
|
+
this.inputState.ensureHandlers(this, this.plugins);
|
|
6144
6120
|
this.mountStyles();
|
|
6145
6121
|
this.updateAttrs();
|
|
6146
6122
|
this.bidiCache = [];
|
|
@@ -6172,7 +6148,7 @@ class EditorView {
|
|
|
6172
6148
|
plugin.destroy(this);
|
|
6173
6149
|
this.plugins = newPlugins;
|
|
6174
6150
|
this.pluginMap.clear();
|
|
6175
|
-
this.inputState.ensureHandlers(this);
|
|
6151
|
+
this.inputState.ensureHandlers(this, this.plugins);
|
|
6176
6152
|
}
|
|
6177
6153
|
else {
|
|
6178
6154
|
for (let p of this.plugins)
|
|
@@ -6310,7 +6286,7 @@ class EditorView {
|
|
|
6310
6286
|
}
|
|
6311
6287
|
mountStyles() {
|
|
6312
6288
|
this.styleModules = this.state.facet(styleModule);
|
|
6313
|
-
styleMod.StyleModule.mount(this.root, this.styleModules.concat(baseTheme).reverse());
|
|
6289
|
+
styleMod.StyleModule.mount(this.root, this.styleModules.concat(baseTheme$1).reverse());
|
|
6314
6290
|
}
|
|
6315
6291
|
readMeasured() {
|
|
6316
6292
|
if (this.updateState == 2 /* Updating */)
|
|
@@ -6341,16 +6317,6 @@ class EditorView {
|
|
|
6341
6317
|
}
|
|
6342
6318
|
}
|
|
6343
6319
|
/**
|
|
6344
|
-
Collect all values provided by the active plugins for a given
|
|
6345
|
-
field.
|
|
6346
|
-
*/
|
|
6347
|
-
pluginField(field) {
|
|
6348
|
-
let result = [];
|
|
6349
|
-
for (let plugin of this.plugins)
|
|
6350
|
-
plugin.update(this).takeField(field, result);
|
|
6351
|
-
return result;
|
|
6352
|
-
}
|
|
6353
|
-
/**
|
|
6354
6320
|
Get the value of a specific plugin, if present. Note that
|
|
6355
6321
|
plugins that crash can be dropped from a view, so even when you
|
|
6356
6322
|
know you registered a given plugin, it is recommended to check
|
|
@@ -6377,23 +6343,6 @@ class EditorView {
|
|
|
6377
6343
|
return { top: this.viewState.paddingTop, bottom: this.viewState.paddingBottom };
|
|
6378
6344
|
}
|
|
6379
6345
|
/**
|
|
6380
|
-
Find the line or block widget at the given vertical position.
|
|
6381
|
-
|
|
6382
|
-
By default, this position is interpreted as a screen position,
|
|
6383
|
-
meaning `docTop` is set to the DOM top position of the editor
|
|
6384
|
-
content (forcing a layout). You can pass a different `docTop`
|
|
6385
|
-
value—for example 0 to interpret `height` as a document-relative
|
|
6386
|
-
position, or a precomputed document top
|
|
6387
|
-
(`view.contentDOM.getBoundingClientRect().top`) to limit layout
|
|
6388
|
-
queries.
|
|
6389
|
-
|
|
6390
|
-
*Deprecated: use `elementAtHeight` instead.*
|
|
6391
|
-
*/
|
|
6392
|
-
blockAtHeight(height, docTop) {
|
|
6393
|
-
let top = ensureTop(docTop, this);
|
|
6394
|
-
return this.elementAtHeight(height - top).moveY(top);
|
|
6395
|
-
}
|
|
6396
|
-
/**
|
|
6397
6346
|
Find the text line or block widget at the given vertical
|
|
6398
6347
|
position (which is interpreted as relative to the [top of the
|
|
6399
6348
|
document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)
|
|
@@ -6403,23 +6352,6 @@ class EditorView {
|
|
|
6403
6352
|
return this.viewState.elementAtHeight(height);
|
|
6404
6353
|
}
|
|
6405
6354
|
/**
|
|
6406
|
-
Find information for the visual line (see
|
|
6407
|
-
[`visualLineAt`](https://codemirror.net/6/docs/ref/#view.EditorView.visualLineAt)) at the given
|
|
6408
|
-
vertical position. The resulting block info might hold another
|
|
6409
|
-
array of block info structs in its `type` field if this line
|
|
6410
|
-
consists of more than one block.
|
|
6411
|
-
|
|
6412
|
-
Defaults to treating `height` as a screen position. See
|
|
6413
|
-
[`blockAtHeight`](https://codemirror.net/6/docs/ref/#view.EditorView.blockAtHeight) for the
|
|
6414
|
-
interpretation of the `docTop` parameter.
|
|
6415
|
-
|
|
6416
|
-
*Deprecated: use `lineBlockAtHeight` instead.*
|
|
6417
|
-
*/
|
|
6418
|
-
visualLineAtHeight(height, docTop) {
|
|
6419
|
-
let top = ensureTop(docTop, this);
|
|
6420
|
-
return this.lineBlockAtHeight(height - top).moveY(top);
|
|
6421
|
-
}
|
|
6422
|
-
/**
|
|
6423
6355
|
Find the line block (see
|
|
6424
6356
|
[`lineBlockAt`](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) at the given
|
|
6425
6357
|
height.
|
|
@@ -6429,19 +6361,6 @@ class EditorView {
|
|
|
6429
6361
|
return this.viewState.lineBlockAtHeight(height);
|
|
6430
6362
|
}
|
|
6431
6363
|
/**
|
|
6432
|
-
Iterate over the height information of the visual lines in the
|
|
6433
|
-
viewport. The heights of lines are reported relative to the
|
|
6434
|
-
given document top, which defaults to the screen position of the
|
|
6435
|
-
document (forcing a layout).
|
|
6436
|
-
|
|
6437
|
-
*Deprecated: use `viewportLineBlocks` instead.*
|
|
6438
|
-
*/
|
|
6439
|
-
viewportLines(f, docTop) {
|
|
6440
|
-
let top = ensureTop(docTop, this);
|
|
6441
|
-
for (let line of this.viewportLineBlocks)
|
|
6442
|
-
f(line.moveY(top));
|
|
6443
|
-
}
|
|
6444
|
-
/**
|
|
6445
6364
|
Get the extent and vertical position of all [line
|
|
6446
6365
|
blocks](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) in the viewport. Positions
|
|
6447
6366
|
are relative to the [top of the
|
|
@@ -6451,24 +6370,9 @@ class EditorView {
|
|
|
6451
6370
|
return this.viewState.viewportLines;
|
|
6452
6371
|
}
|
|
6453
6372
|
/**
|
|
6454
|
-
Find the extent and height of the visual line (a range delimited
|
|
6455
|
-
on both sides by either non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^range)
|
|
6456
|
-
line breaks, or the start/end of the document) at the given position.
|
|
6457
|
-
|
|
6458
|
-
Vertical positions are computed relative to the `docTop`
|
|
6459
|
-
argument, which defaults to 0 for this method. You can pass
|
|
6460
|
-
`view.contentDOM.getBoundingClientRect().top` here to get screen
|
|
6461
|
-
coordinates.
|
|
6462
|
-
|
|
6463
|
-
*Deprecated: use `lineBlockAt` instead.*
|
|
6464
|
-
*/
|
|
6465
|
-
visualLineAt(pos, docTop = 0) {
|
|
6466
|
-
return this.lineBlockAt(pos).moveY(docTop + this.viewState.paddingTop);
|
|
6467
|
-
}
|
|
6468
|
-
/**
|
|
6469
6373
|
Find the line block around the given document position. A line
|
|
6470
6374
|
block is a range delimited on both sides by either a
|
|
6471
|
-
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
|
|
6472
6376
|
start/end of the document. It will usually just hold a line of
|
|
6473
6377
|
text, but may be broken into multiple textblocks by block
|
|
6474
6378
|
widgets.
|
|
@@ -6484,13 +6388,13 @@ class EditorView {
|
|
|
6484
6388
|
}
|
|
6485
6389
|
/**
|
|
6486
6390
|
Move a cursor position by [grapheme
|
|
6487
|
-
cluster](https://codemirror.net/6/docs/ref/#
|
|
6488
|
-
the motion is away from the line start, or towards it.
|
|
6489
|
-
bidirectional text is in visual order,
|
|
6490
|
-
direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
|
|
6491
|
-
position was the last one on the line, the
|
|
6492
|
-
will be across the line break. If there is no
|
|
6493
|
-
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.
|
|
6494
6398
|
|
|
6495
6399
|
By default, this method moves over a single cluster. The
|
|
6496
6400
|
optional `by` argument can be used to move across more. It will
|
|
@@ -6535,10 +6439,6 @@ class EditorView {
|
|
|
6535
6439
|
moveVertically(start, forward, distance) {
|
|
6536
6440
|
return skipAtoms(this, start, moveVertically(this, start, forward, distance));
|
|
6537
6441
|
}
|
|
6538
|
-
// FIXME remove on next major version
|
|
6539
|
-
scrollPosIntoView(pos) {
|
|
6540
|
-
this.dispatch({ effects: scrollTo.of(state.EditorSelection.cursor(pos)) });
|
|
6541
|
-
}
|
|
6542
6442
|
/**
|
|
6543
6443
|
Find the DOM parent node and offset (child offset if `node` is
|
|
6544
6444
|
an element, character offset when it is a text node) at the
|
|
@@ -6594,9 +6494,25 @@ class EditorView {
|
|
|
6594
6494
|
/**
|
|
6595
6495
|
The text direction
|
|
6596
6496
|
([`direction`](https://developer.mozilla.org/en-US/docs/Web/CSS/direction)
|
|
6597
|
-
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.
|
|
6598
6508
|
*/
|
|
6599
|
-
|
|
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
|
+
}
|
|
6600
6516
|
/**
|
|
6601
6517
|
Whether this editor [wraps lines](https://codemirror.net/6/docs/ref/#view.EditorView.lineWrapping)
|
|
6602
6518
|
(as determined by the
|
|
@@ -6615,11 +6531,11 @@ class EditorView {
|
|
|
6615
6531
|
bidiSpans(line) {
|
|
6616
6532
|
if (line.length > MaxBidiLine)
|
|
6617
6533
|
return trivialOrder(line.length);
|
|
6618
|
-
let dir = this.
|
|
6534
|
+
let dir = this.textDirectionAt(line.from);
|
|
6619
6535
|
for (let entry of this.bidiCache)
|
|
6620
6536
|
if (entry.from == line.from && entry.dir == dir)
|
|
6621
6537
|
return entry.order;
|
|
6622
|
-
let order = computeOrder(line.text,
|
|
6538
|
+
let order = computeOrder(line.text, dir);
|
|
6623
6539
|
this.bidiCache.push(new CachedOrder(line.from, line.to, dir, order));
|
|
6624
6540
|
return order;
|
|
6625
6541
|
}
|
|
@@ -6670,16 +6586,16 @@ class EditorView {
|
|
|
6670
6586
|
return scrollIntoView.of(new ScrollTarget(typeof pos == "number" ? state.EditorSelection.cursor(pos) : pos, options.y, options.x, options.yMargin, options.xMargin));
|
|
6671
6587
|
}
|
|
6672
6588
|
/**
|
|
6673
|
-
|
|
6674
|
-
should be an object mapping event names to handler
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
These are registered
|
|
6679
|
-
element](https://codemirror.net/6/docs/ref/#view.EditorView.contentDOM), except
|
|
6680
|
-
handlers, which will be called any time the
|
|
6681
|
-
element](https://codemirror.net/6/docs/ref/#view.EditorView.scrollDOM) or one of
|
|
6682
|
-
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.
|
|
6683
6599
|
*/
|
|
6684
6600
|
static domEventHandlers(handlers) {
|
|
6685
6601
|
return ViewPlugin.define(() => ({}), { eventHandlers: handlers });
|
|
@@ -6721,20 +6637,6 @@ class EditorView {
|
|
|
6721
6637
|
}
|
|
6722
6638
|
}
|
|
6723
6639
|
/**
|
|
6724
|
-
Effect that can be [added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a
|
|
6725
|
-
transaction to make it scroll the given range into view.
|
|
6726
|
-
|
|
6727
|
-
*Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead.
|
|
6728
|
-
*/
|
|
6729
|
-
EditorView.scrollTo = scrollTo;
|
|
6730
|
-
/**
|
|
6731
|
-
Effect that makes the editor scroll the given range to the
|
|
6732
|
-
center of the visible view.
|
|
6733
|
-
|
|
6734
|
-
*Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead.
|
|
6735
|
-
*/
|
|
6736
|
-
EditorView.centerOn = centerOn;
|
|
6737
|
-
/**
|
|
6738
6640
|
Facet to add a [style
|
|
6739
6641
|
module](https://github.com/marijnh/style-mod#documentation) to
|
|
6740
6642
|
an editor view. The view will ensure that the module is
|
|
@@ -6751,6 +6653,13 @@ called and the default behavior is prevented.
|
|
|
6751
6653
|
*/
|
|
6752
6654
|
EditorView.inputHandler = inputHandler;
|
|
6753
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
|
+
/**
|
|
6754
6663
|
Allows you to provide a function that should be called when the
|
|
6755
6664
|
library catches an exception from an extension (mostly from view
|
|
6756
6665
|
plugins, but may be used by other extensions to route exceptions
|
|
@@ -6766,9 +6675,9 @@ EditorView.updateListener = updateListener;
|
|
|
6766
6675
|
/**
|
|
6767
6676
|
Facet that controls whether the editor content DOM is editable.
|
|
6768
6677
|
When its highest-precedence value is `false`, the element will
|
|
6769
|
-
not
|
|
6770
|
-
|
|
6771
|
-
|
|
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
|
|
6772
6681
|
[`readOnly`](https://codemirror.net/6/docs/ref/#state.EditorState.readOnly) facet for that.)
|
|
6773
6682
|
*/
|
|
6774
6683
|
EditorView.editable = editable;
|
|
@@ -6787,18 +6696,45 @@ the drag should move the content.
|
|
|
6787
6696
|
*/
|
|
6788
6697
|
EditorView.dragMovesSelection = dragMovesSelection$1;
|
|
6789
6698
|
/**
|
|
6790
|
-
Facet used to configure whether a given selecting click adds
|
|
6791
|
-
|
|
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.
|
|
6792
6703
|
*/
|
|
6793
6704
|
EditorView.clickAddsSelectionRange = clickAddsSelectionRange;
|
|
6794
6705
|
/**
|
|
6795
6706
|
A facet that determines which [decorations](https://codemirror.net/6/docs/ref/#view.Decoration)
|
|
6796
|
-
are shown in the view.
|
|
6797
|
-
|
|
6798
|
-
|
|
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.
|
|
6799
6715
|
*/
|
|
6800
6716
|
EditorView.decorations = decorations;
|
|
6801
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
|
+
/**
|
|
6802
6738
|
This facet records whether a dark theme is active. The extension
|
|
6803
6739
|
returned by [`theme`](https://codemirror.net/6/docs/ref/#view.EditorView^theme) automatically
|
|
6804
6740
|
includes an instance of this when the `dark` option is set to
|
|
@@ -6831,10 +6767,6 @@ search match).
|
|
|
6831
6767
|
EditorView.announce = state.StateEffect.define();
|
|
6832
6768
|
// Maximum line length for which we compute accurate bidi info
|
|
6833
6769
|
const MaxBidiLine = 4096;
|
|
6834
|
-
// FIXME remove this and its callers on next breaking release
|
|
6835
|
-
function ensureTop(given, view) {
|
|
6836
|
-
return (given == null ? view.contentDOM.getBoundingClientRect().top : given) + view.viewState.paddingTop;
|
|
6837
|
-
}
|
|
6838
6770
|
const BadMeasure = {};
|
|
6839
6771
|
class CachedOrder {
|
|
6840
6772
|
constructor(from, to, dir, order) {
|
|
@@ -6937,7 +6869,7 @@ function getKeymap(state) {
|
|
|
6937
6869
|
}
|
|
6938
6870
|
/**
|
|
6939
6871
|
Run the key handlers registered for a given scope. The event
|
|
6940
|
-
object should be `"keydown"` event. Returns true if any of the
|
|
6872
|
+
object should be a `"keydown"` event. Returns true if any of the
|
|
6941
6873
|
handlers handled it.
|
|
6942
6874
|
*/
|
|
6943
6875
|
function runScopeHandlers(view, event, scope) {
|
|
@@ -7417,7 +7349,7 @@ class MatchDecorator {
|
|
|
7417
7349
|
plugin.
|
|
7418
7350
|
*/
|
|
7419
7351
|
createDeco(view) {
|
|
7420
|
-
let build = new
|
|
7352
|
+
let build = new state.RangeSetBuilder();
|
|
7421
7353
|
for (let { from, to } of matchRanges(view, this.maxLength))
|
|
7422
7354
|
iterMatches(view.state.doc, this.regexp, from, to, (a, b, m) => build.add(a, b, this.getDeco(m, view, a)));
|
|
7423
7355
|
return build.finish();
|
|
@@ -7549,10 +7481,10 @@ function specialCharPlugin() {
|
|
|
7549
7481
|
regexp: conf.specialChars,
|
|
7550
7482
|
decoration: (m, view, pos) => {
|
|
7551
7483
|
let { doc } = view.state;
|
|
7552
|
-
let code =
|
|
7484
|
+
let code = state.codePointAt(m[0], 0);
|
|
7553
7485
|
if (code == 9) {
|
|
7554
7486
|
let line = doc.lineAt(pos);
|
|
7555
|
-
let size = view.state.tabSize, col =
|
|
7487
|
+
let size = view.state.tabSize, col = state.countColumn(line.text, size, pos - line.from);
|
|
7556
7488
|
return Decoration.replace({ widget: new TabWidget((size - (col % size)) * this.view.defaultCharacterWidth) });
|
|
7557
7489
|
}
|
|
7558
7490
|
return this.decorationCache[code] ||
|
|
@@ -7714,32 +7646,1333 @@ function placeholder(content) {
|
|
|
7714
7646
|
}, { decorations: v => v.decorations });
|
|
7715
7647
|
}
|
|
7716
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
|
+
}
|
|
7717
7715
|
/**
|
|
7718
|
-
|
|
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.
|
|
7719
7721
|
*/
|
|
7720
|
-
|
|
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
|
+
}
|
|
7721
7769
|
|
|
7722
|
-
|
|
7723
|
-
|
|
7724
|
-
|
|
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
|
+
}
|
|
7725
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
|
+
class HoverTooltipHost {
|
|
8086
|
+
constructor(view) {
|
|
8087
|
+
this.view = view;
|
|
8088
|
+
this.mounted = false;
|
|
8089
|
+
this.dom = document.createElement("div");
|
|
8090
|
+
this.dom.classList.add("cm-tooltip-hover");
|
|
8091
|
+
this.manager = new TooltipViewManager(view, showHoverTooltip, t => this.createHostedView(t));
|
|
8092
|
+
}
|
|
8093
|
+
// Needs to be static so that host tooltip instances always match
|
|
8094
|
+
static create(view) {
|
|
8095
|
+
return new HoverTooltipHost(view);
|
|
8096
|
+
}
|
|
8097
|
+
createHostedView(tooltip) {
|
|
8098
|
+
let hostedView = tooltip.create(this.view);
|
|
8099
|
+
hostedView.dom.classList.add("cm-tooltip-section");
|
|
8100
|
+
this.dom.appendChild(hostedView.dom);
|
|
8101
|
+
if (this.mounted && hostedView.mount)
|
|
8102
|
+
hostedView.mount(this.view);
|
|
8103
|
+
return hostedView;
|
|
8104
|
+
}
|
|
8105
|
+
mount(view) {
|
|
8106
|
+
for (let hostedView of this.manager.tooltipViews) {
|
|
8107
|
+
if (hostedView.mount)
|
|
8108
|
+
hostedView.mount(view);
|
|
8109
|
+
}
|
|
8110
|
+
this.mounted = true;
|
|
8111
|
+
}
|
|
8112
|
+
positioned() {
|
|
8113
|
+
for (let hostedView of this.manager.tooltipViews) {
|
|
8114
|
+
if (hostedView.positioned)
|
|
8115
|
+
hostedView.positioned();
|
|
8116
|
+
}
|
|
8117
|
+
}
|
|
8118
|
+
update(update) {
|
|
8119
|
+
this.manager.update(update);
|
|
8120
|
+
}
|
|
8121
|
+
}
|
|
8122
|
+
const showHoverTooltipHost = showTooltip.compute([showHoverTooltip], state => {
|
|
8123
|
+
let tooltips = state.facet(showHoverTooltip).filter(t => t);
|
|
8124
|
+
if (tooltips.length === 0)
|
|
8125
|
+
return null;
|
|
8126
|
+
return {
|
|
8127
|
+
pos: Math.min(...tooltips.map(t => t.pos)),
|
|
8128
|
+
end: Math.max(...tooltips.filter(t => t.end != null).map(t => t.end)),
|
|
8129
|
+
create: HoverTooltipHost.create,
|
|
8130
|
+
above: tooltips[0].above,
|
|
8131
|
+
arrow: tooltips.some(t => t.arrow),
|
|
8132
|
+
};
|
|
8133
|
+
});
|
|
8134
|
+
class HoverPlugin {
|
|
8135
|
+
constructor(view, source, field, setHover, hoverTime) {
|
|
8136
|
+
this.view = view;
|
|
8137
|
+
this.source = source;
|
|
8138
|
+
this.field = field;
|
|
8139
|
+
this.setHover = setHover;
|
|
8140
|
+
this.hoverTime = hoverTime;
|
|
8141
|
+
this.hoverTimeout = -1;
|
|
8142
|
+
this.restartTimeout = -1;
|
|
8143
|
+
this.pending = null;
|
|
8144
|
+
this.lastMove = { x: 0, y: 0, target: view.dom, time: 0 };
|
|
8145
|
+
this.checkHover = this.checkHover.bind(this);
|
|
8146
|
+
view.dom.addEventListener("mouseleave", this.mouseleave = this.mouseleave.bind(this));
|
|
8147
|
+
view.dom.addEventListener("mousemove", this.mousemove = this.mousemove.bind(this));
|
|
8148
|
+
}
|
|
8149
|
+
update() {
|
|
8150
|
+
if (this.pending) {
|
|
8151
|
+
this.pending = null;
|
|
8152
|
+
clearTimeout(this.restartTimeout);
|
|
8153
|
+
this.restartTimeout = setTimeout(() => this.startHover(), 20);
|
|
8154
|
+
}
|
|
8155
|
+
}
|
|
8156
|
+
get active() {
|
|
8157
|
+
return this.view.state.field(this.field);
|
|
8158
|
+
}
|
|
8159
|
+
checkHover() {
|
|
8160
|
+
this.hoverTimeout = -1;
|
|
8161
|
+
if (this.active)
|
|
8162
|
+
return;
|
|
8163
|
+
let hovered = Date.now() - this.lastMove.time;
|
|
8164
|
+
if (hovered < this.hoverTime)
|
|
8165
|
+
this.hoverTimeout = setTimeout(this.checkHover, this.hoverTime - hovered);
|
|
8166
|
+
else
|
|
8167
|
+
this.startHover();
|
|
8168
|
+
}
|
|
8169
|
+
startHover() {
|
|
8170
|
+
var _a;
|
|
8171
|
+
clearTimeout(this.restartTimeout);
|
|
8172
|
+
let { lastMove } = this;
|
|
8173
|
+
let pos = this.view.contentDOM.contains(lastMove.target) ? this.view.posAtCoords(lastMove) : null;
|
|
8174
|
+
if (pos == null)
|
|
8175
|
+
return;
|
|
8176
|
+
let posCoords = this.view.coordsAtPos(pos);
|
|
8177
|
+
if (posCoords == null || lastMove.y < posCoords.top || lastMove.y > posCoords.bottom ||
|
|
8178
|
+
lastMove.x < posCoords.left - this.view.defaultCharacterWidth ||
|
|
8179
|
+
lastMove.x > posCoords.right + this.view.defaultCharacterWidth)
|
|
8180
|
+
return;
|
|
8181
|
+
let bidi = this.view.bidiSpans(this.view.state.doc.lineAt(pos)).find(s => s.from <= pos && s.to >= pos);
|
|
8182
|
+
let rtl = bidi && bidi.dir == exports.Direction.RTL ? -1 : 1;
|
|
8183
|
+
let open = this.source(this.view, pos, (lastMove.x < posCoords.left ? -rtl : rtl));
|
|
8184
|
+
if ((_a = open) === null || _a === void 0 ? void 0 : _a.then) {
|
|
8185
|
+
let pending = this.pending = { pos };
|
|
8186
|
+
open.then(result => {
|
|
8187
|
+
if (this.pending == pending) {
|
|
8188
|
+
this.pending = null;
|
|
8189
|
+
if (result)
|
|
8190
|
+
this.view.dispatch({ effects: this.setHover.of(result) });
|
|
8191
|
+
}
|
|
8192
|
+
}, e => logException(this.view.state, e, "hover tooltip"));
|
|
8193
|
+
}
|
|
8194
|
+
else if (open) {
|
|
8195
|
+
this.view.dispatch({ effects: this.setHover.of(open) });
|
|
8196
|
+
}
|
|
8197
|
+
}
|
|
8198
|
+
mousemove(event) {
|
|
8199
|
+
var _a;
|
|
8200
|
+
this.lastMove = { x: event.clientX, y: event.clientY, target: event.target, time: Date.now() };
|
|
8201
|
+
if (this.hoverTimeout < 0)
|
|
8202
|
+
this.hoverTimeout = setTimeout(this.checkHover, this.hoverTime);
|
|
8203
|
+
let tooltip = this.active;
|
|
8204
|
+
if (tooltip && !isInTooltip(this.lastMove.target) || this.pending) {
|
|
8205
|
+
let { pos } = tooltip || this.pending, end = (_a = tooltip === null || tooltip === void 0 ? void 0 : tooltip.end) !== null && _a !== void 0 ? _a : pos;
|
|
8206
|
+
if ((pos == end ? this.view.posAtCoords(this.lastMove) != pos
|
|
8207
|
+
: !isOverRange(this.view, pos, end, event.clientX, event.clientY, 6 /* MaxDist */))) {
|
|
8208
|
+
this.view.dispatch({ effects: this.setHover.of(null) });
|
|
8209
|
+
this.pending = null;
|
|
8210
|
+
}
|
|
8211
|
+
}
|
|
8212
|
+
}
|
|
8213
|
+
mouseleave() {
|
|
8214
|
+
clearTimeout(this.hoverTimeout);
|
|
8215
|
+
this.hoverTimeout = -1;
|
|
8216
|
+
if (this.active)
|
|
8217
|
+
this.view.dispatch({ effects: this.setHover.of(null) });
|
|
8218
|
+
}
|
|
8219
|
+
destroy() {
|
|
8220
|
+
clearTimeout(this.hoverTimeout);
|
|
8221
|
+
this.view.dom.removeEventListener("mouseleave", this.mouseleave);
|
|
8222
|
+
this.view.dom.removeEventListener("mousemove", this.mousemove);
|
|
8223
|
+
}
|
|
8224
|
+
}
|
|
8225
|
+
function isInTooltip(elt) {
|
|
8226
|
+
for (let cur = elt; cur; cur = cur.parentNode)
|
|
8227
|
+
if (cur.nodeType == 1 && cur.classList.contains("cm-tooltip"))
|
|
8228
|
+
return true;
|
|
8229
|
+
return false;
|
|
8230
|
+
}
|
|
8231
|
+
function isOverRange(view, from, to, x, y, margin) {
|
|
8232
|
+
let range = document.createRange();
|
|
8233
|
+
let fromDOM = view.domAtPos(from), toDOM = view.domAtPos(to);
|
|
8234
|
+
range.setEnd(toDOM.node, toDOM.offset);
|
|
8235
|
+
range.setStart(fromDOM.node, fromDOM.offset);
|
|
8236
|
+
let rects = range.getClientRects();
|
|
8237
|
+
range.detach();
|
|
8238
|
+
for (let i = 0; i < rects.length; i++) {
|
|
8239
|
+
let rect = rects[i];
|
|
8240
|
+
let dist = Math.max(rect.top - y, y - rect.bottom, rect.left - x, x - rect.right);
|
|
8241
|
+
if (dist <= margin)
|
|
8242
|
+
return true;
|
|
8243
|
+
}
|
|
8244
|
+
return false;
|
|
8245
|
+
}
|
|
8246
|
+
/**
|
|
8247
|
+
Set up a hover tooltip, which shows up when the pointer hovers
|
|
8248
|
+
over ranges of text. The callback is called when the mouse hovers
|
|
8249
|
+
over the document text. It should, if there is a tooltip
|
|
8250
|
+
associated with position `pos`, return the tooltip description
|
|
8251
|
+
(either directly or in a promise). The `side` argument indicates
|
|
8252
|
+
on which side of the position the pointer is—it will be -1 if the
|
|
8253
|
+
pointer is before the position, 1 if after the position.
|
|
8254
|
+
|
|
8255
|
+
Note that all hover tooltips are hosted within a single tooltip
|
|
8256
|
+
container element. This allows multiple tooltips over the same
|
|
8257
|
+
range to be "merged" together without overlapping.
|
|
8258
|
+
*/
|
|
8259
|
+
function hoverTooltip(source, options = {}) {
|
|
8260
|
+
let setHover = state.StateEffect.define();
|
|
8261
|
+
let hoverState = state.StateField.define({
|
|
8262
|
+
create() { return null; },
|
|
8263
|
+
update(value, tr) {
|
|
8264
|
+
if (value && (options.hideOnChange && (tr.docChanged || tr.selection)))
|
|
8265
|
+
return null;
|
|
8266
|
+
if (value && tr.docChanged) {
|
|
8267
|
+
let newPos = tr.changes.mapPos(value.pos, -1, state.MapMode.TrackDel);
|
|
8268
|
+
if (newPos == null)
|
|
8269
|
+
return null;
|
|
8270
|
+
let copy = Object.assign(Object.create(null), value);
|
|
8271
|
+
copy.pos = newPos;
|
|
8272
|
+
if (value.end != null)
|
|
8273
|
+
copy.end = tr.changes.mapPos(value.end);
|
|
8274
|
+
value = copy;
|
|
8275
|
+
}
|
|
8276
|
+
for (let effect of tr.effects) {
|
|
8277
|
+
if (effect.is(setHover))
|
|
8278
|
+
value = effect.value;
|
|
8279
|
+
if (effect.is(closeHoverTooltipEffect))
|
|
8280
|
+
value = null;
|
|
8281
|
+
}
|
|
8282
|
+
return value;
|
|
8283
|
+
},
|
|
8284
|
+
provide: f => showHoverTooltip.from(f)
|
|
8285
|
+
});
|
|
8286
|
+
return [
|
|
8287
|
+
hoverState,
|
|
8288
|
+
ViewPlugin.define(view => new HoverPlugin(view, source, hoverState, setHover, options.hoverTime || 300 /* Time */)),
|
|
8289
|
+
showHoverTooltipHost
|
|
8290
|
+
];
|
|
8291
|
+
}
|
|
8292
|
+
/**
|
|
8293
|
+
Get the active tooltip view for a given tooltip, if available.
|
|
8294
|
+
*/
|
|
8295
|
+
function getTooltip(view, tooltip) {
|
|
8296
|
+
let plugin = view.plugin(tooltipPlugin);
|
|
8297
|
+
if (!plugin)
|
|
8298
|
+
return null;
|
|
8299
|
+
let found = plugin.manager.tooltips.indexOf(tooltip);
|
|
8300
|
+
return found < 0 ? null : plugin.manager.tooltipViews[found];
|
|
8301
|
+
}
|
|
8302
|
+
/**
|
|
8303
|
+
Returns true if any hover tooltips are currently active.
|
|
8304
|
+
*/
|
|
8305
|
+
function hasHoverTooltips(state) {
|
|
8306
|
+
return state.facet(showHoverTooltip).some(x => x);
|
|
8307
|
+
}
|
|
8308
|
+
const closeHoverTooltipEffect = state.StateEffect.define();
|
|
8309
|
+
/**
|
|
8310
|
+
Transaction effect that closes all hover tooltips.
|
|
8311
|
+
*/
|
|
8312
|
+
const closeHoverTooltips = closeHoverTooltipEffect.of(null);
|
|
8313
|
+
/**
|
|
8314
|
+
Tell the tooltip extension to recompute the position of the active
|
|
8315
|
+
tooltips. This can be useful when something happens (such as a
|
|
8316
|
+
re-positioning or CSS change affecting the editor) that could
|
|
8317
|
+
invalidate the existing tooltip positions.
|
|
8318
|
+
*/
|
|
8319
|
+
function repositionTooltips(view) {
|
|
8320
|
+
var _a;
|
|
8321
|
+
(_a = view.plugin(tooltipPlugin)) === null || _a === void 0 ? void 0 : _a.maybeMeasure();
|
|
8322
|
+
}
|
|
8323
|
+
|
|
8324
|
+
const panelConfig = state.Facet.define({
|
|
8325
|
+
combine(configs) {
|
|
8326
|
+
let topContainer, bottomContainer;
|
|
8327
|
+
for (let c of configs) {
|
|
8328
|
+
topContainer = topContainer || c.topContainer;
|
|
8329
|
+
bottomContainer = bottomContainer || c.bottomContainer;
|
|
8330
|
+
}
|
|
8331
|
+
return { topContainer, bottomContainer };
|
|
8332
|
+
}
|
|
8333
|
+
});
|
|
8334
|
+
/**
|
|
8335
|
+
Configures the panel-managing extension.
|
|
8336
|
+
*/
|
|
8337
|
+
function panels(config) {
|
|
8338
|
+
return config ? [panelConfig.of(config)] : [];
|
|
8339
|
+
}
|
|
8340
|
+
/**
|
|
8341
|
+
Get the active panel created by the given constructor, if any.
|
|
8342
|
+
This can be useful when you need access to your panels' DOM
|
|
8343
|
+
structure.
|
|
8344
|
+
*/
|
|
8345
|
+
function getPanel(view, panel) {
|
|
8346
|
+
let plugin = view.plugin(panelPlugin);
|
|
8347
|
+
let index = plugin ? plugin.specs.indexOf(panel) : -1;
|
|
8348
|
+
return index > -1 ? plugin.panels[index] : null;
|
|
8349
|
+
}
|
|
8350
|
+
const panelPlugin = ViewPlugin.fromClass(class {
|
|
8351
|
+
constructor(view) {
|
|
8352
|
+
this.input = view.state.facet(showPanel);
|
|
8353
|
+
this.specs = this.input.filter(s => s);
|
|
8354
|
+
this.panels = this.specs.map(spec => spec(view));
|
|
8355
|
+
let conf = view.state.facet(panelConfig);
|
|
8356
|
+
this.top = new PanelGroup(view, true, conf.topContainer);
|
|
8357
|
+
this.bottom = new PanelGroup(view, false, conf.bottomContainer);
|
|
8358
|
+
this.top.sync(this.panels.filter(p => p.top));
|
|
8359
|
+
this.bottom.sync(this.panels.filter(p => !p.top));
|
|
8360
|
+
for (let p of this.panels) {
|
|
8361
|
+
p.dom.classList.add("cm-panel");
|
|
8362
|
+
if (p.mount)
|
|
8363
|
+
p.mount();
|
|
8364
|
+
}
|
|
8365
|
+
}
|
|
8366
|
+
update(update) {
|
|
8367
|
+
let conf = update.state.facet(panelConfig);
|
|
8368
|
+
if (this.top.container != conf.topContainer) {
|
|
8369
|
+
this.top.sync([]);
|
|
8370
|
+
this.top = new PanelGroup(update.view, true, conf.topContainer);
|
|
8371
|
+
}
|
|
8372
|
+
if (this.bottom.container != conf.bottomContainer) {
|
|
8373
|
+
this.bottom.sync([]);
|
|
8374
|
+
this.bottom = new PanelGroup(update.view, false, conf.bottomContainer);
|
|
8375
|
+
}
|
|
8376
|
+
this.top.syncClasses();
|
|
8377
|
+
this.bottom.syncClasses();
|
|
8378
|
+
let input = update.state.facet(showPanel);
|
|
8379
|
+
if (input != this.input) {
|
|
8380
|
+
let specs = input.filter(x => x);
|
|
8381
|
+
let panels = [], top = [], bottom = [], mount = [];
|
|
8382
|
+
for (let spec of specs) {
|
|
8383
|
+
let known = this.specs.indexOf(spec), panel;
|
|
8384
|
+
if (known < 0) {
|
|
8385
|
+
panel = spec(update.view);
|
|
8386
|
+
mount.push(panel);
|
|
8387
|
+
}
|
|
8388
|
+
else {
|
|
8389
|
+
panel = this.panels[known];
|
|
8390
|
+
if (panel.update)
|
|
8391
|
+
panel.update(update);
|
|
8392
|
+
}
|
|
8393
|
+
panels.push(panel);
|
|
8394
|
+
(panel.top ? top : bottom).push(panel);
|
|
8395
|
+
}
|
|
8396
|
+
this.specs = specs;
|
|
8397
|
+
this.panels = panels;
|
|
8398
|
+
this.top.sync(top);
|
|
8399
|
+
this.bottom.sync(bottom);
|
|
8400
|
+
for (let p of mount) {
|
|
8401
|
+
p.dom.classList.add("cm-panel");
|
|
8402
|
+
if (p.mount)
|
|
8403
|
+
p.mount();
|
|
8404
|
+
}
|
|
8405
|
+
}
|
|
8406
|
+
else {
|
|
8407
|
+
for (let p of this.panels)
|
|
8408
|
+
if (p.update)
|
|
8409
|
+
p.update(update);
|
|
8410
|
+
}
|
|
8411
|
+
}
|
|
8412
|
+
destroy() {
|
|
8413
|
+
this.top.sync([]);
|
|
8414
|
+
this.bottom.sync([]);
|
|
8415
|
+
}
|
|
8416
|
+
}, {
|
|
8417
|
+
provide: plugin => EditorView.scrollMargins.of(view => {
|
|
8418
|
+
let value = view.plugin(plugin);
|
|
8419
|
+
return value && { top: value.top.scrollMargin(), bottom: value.bottom.scrollMargin() };
|
|
8420
|
+
})
|
|
8421
|
+
});
|
|
8422
|
+
class PanelGroup {
|
|
8423
|
+
constructor(view, top, container) {
|
|
8424
|
+
this.view = view;
|
|
8425
|
+
this.top = top;
|
|
8426
|
+
this.container = container;
|
|
8427
|
+
this.dom = undefined;
|
|
8428
|
+
this.classes = "";
|
|
8429
|
+
this.panels = [];
|
|
8430
|
+
this.syncClasses();
|
|
8431
|
+
}
|
|
8432
|
+
sync(panels) {
|
|
8433
|
+
for (let p of this.panels)
|
|
8434
|
+
if (p.destroy && panels.indexOf(p) < 0)
|
|
8435
|
+
p.destroy();
|
|
8436
|
+
this.panels = panels;
|
|
8437
|
+
this.syncDOM();
|
|
8438
|
+
}
|
|
8439
|
+
syncDOM() {
|
|
8440
|
+
if (this.panels.length == 0) {
|
|
8441
|
+
if (this.dom) {
|
|
8442
|
+
this.dom.remove();
|
|
8443
|
+
this.dom = undefined;
|
|
8444
|
+
}
|
|
8445
|
+
return;
|
|
8446
|
+
}
|
|
8447
|
+
if (!this.dom) {
|
|
8448
|
+
this.dom = document.createElement("div");
|
|
8449
|
+
this.dom.className = this.top ? "cm-panels cm-panels-top" : "cm-panels cm-panels-bottom";
|
|
8450
|
+
this.dom.style[this.top ? "top" : "bottom"] = "0";
|
|
8451
|
+
let parent = this.container || this.view.dom;
|
|
8452
|
+
parent.insertBefore(this.dom, this.top ? parent.firstChild : null);
|
|
8453
|
+
}
|
|
8454
|
+
let curDOM = this.dom.firstChild;
|
|
8455
|
+
for (let panel of this.panels) {
|
|
8456
|
+
if (panel.dom.parentNode == this.dom) {
|
|
8457
|
+
while (curDOM != panel.dom)
|
|
8458
|
+
curDOM = rm(curDOM);
|
|
8459
|
+
curDOM = curDOM.nextSibling;
|
|
8460
|
+
}
|
|
8461
|
+
else {
|
|
8462
|
+
this.dom.insertBefore(panel.dom, curDOM);
|
|
8463
|
+
}
|
|
8464
|
+
}
|
|
8465
|
+
while (curDOM)
|
|
8466
|
+
curDOM = rm(curDOM);
|
|
8467
|
+
}
|
|
8468
|
+
scrollMargin() {
|
|
8469
|
+
return !this.dom || this.container ? 0
|
|
8470
|
+
: Math.max(0, this.top ?
|
|
8471
|
+
this.dom.getBoundingClientRect().bottom - Math.max(0, this.view.scrollDOM.getBoundingClientRect().top) :
|
|
8472
|
+
Math.min(innerHeight, this.view.scrollDOM.getBoundingClientRect().bottom) - this.dom.getBoundingClientRect().top);
|
|
8473
|
+
}
|
|
8474
|
+
syncClasses() {
|
|
8475
|
+
if (!this.container || this.classes == this.view.themeClasses)
|
|
8476
|
+
return;
|
|
8477
|
+
for (let cls of this.classes.split(" "))
|
|
8478
|
+
if (cls)
|
|
8479
|
+
this.container.classList.remove(cls);
|
|
8480
|
+
for (let cls of (this.classes = this.view.themeClasses).split(" "))
|
|
8481
|
+
if (cls)
|
|
8482
|
+
this.container.classList.add(cls);
|
|
8483
|
+
}
|
|
8484
|
+
}
|
|
8485
|
+
function rm(node) {
|
|
8486
|
+
let next = node.nextSibling;
|
|
8487
|
+
node.remove();
|
|
8488
|
+
return next;
|
|
8489
|
+
}
|
|
8490
|
+
/**
|
|
8491
|
+
Opening a panel is done by providing a constructor function for
|
|
8492
|
+
the panel through this facet. (The panel is closed again when its
|
|
8493
|
+
constructor is no longer provided.) Values of `null` are ignored.
|
|
8494
|
+
*/
|
|
8495
|
+
const showPanel = state.Facet.define({
|
|
8496
|
+
enables: panelPlugin
|
|
8497
|
+
});
|
|
8498
|
+
|
|
8499
|
+
/**
|
|
8500
|
+
A gutter marker represents a bit of information attached to a line
|
|
8501
|
+
in a specific gutter. Your own custom markers have to extend this
|
|
8502
|
+
class.
|
|
8503
|
+
*/
|
|
8504
|
+
class GutterMarker extends state.RangeValue {
|
|
8505
|
+
/**
|
|
8506
|
+
@internal
|
|
8507
|
+
*/
|
|
8508
|
+
compare(other) {
|
|
8509
|
+
return this == other || this.constructor == other.constructor && this.eq(other);
|
|
8510
|
+
}
|
|
8511
|
+
/**
|
|
8512
|
+
Compare this marker to another marker of the same type.
|
|
8513
|
+
*/
|
|
8514
|
+
eq(other) { return false; }
|
|
8515
|
+
/**
|
|
8516
|
+
Called if the marker has a `toDOM` method and its representation
|
|
8517
|
+
was removed from a gutter.
|
|
8518
|
+
*/
|
|
8519
|
+
destroy(dom) { }
|
|
8520
|
+
}
|
|
8521
|
+
GutterMarker.prototype.elementClass = "";
|
|
8522
|
+
GutterMarker.prototype.toDOM = undefined;
|
|
8523
|
+
GutterMarker.prototype.mapMode = state.MapMode.TrackBefore;
|
|
8524
|
+
GutterMarker.prototype.startSide = GutterMarker.prototype.endSide = -1;
|
|
8525
|
+
GutterMarker.prototype.point = true;
|
|
8526
|
+
/**
|
|
8527
|
+
Facet used to add a class to all gutter elements for a given line.
|
|
8528
|
+
Markers given to this facet should _only_ define an
|
|
8529
|
+
[`elementclass`](https://codemirror.net/6/docs/ref/#view.GutterMarker.elementClass), not a
|
|
8530
|
+
[`toDOM`](https://codemirror.net/6/docs/ref/#view.GutterMarker.toDOM) (or the marker will appear
|
|
8531
|
+
in all gutters for the line).
|
|
8532
|
+
*/
|
|
8533
|
+
const gutterLineClass = state.Facet.define();
|
|
8534
|
+
const defaults = {
|
|
8535
|
+
class: "",
|
|
8536
|
+
renderEmptyElements: false,
|
|
8537
|
+
elementStyle: "",
|
|
8538
|
+
markers: () => state.RangeSet.empty,
|
|
8539
|
+
lineMarker: () => null,
|
|
8540
|
+
lineMarkerChange: null,
|
|
8541
|
+
initialSpacer: null,
|
|
8542
|
+
updateSpacer: null,
|
|
8543
|
+
domEventHandlers: {}
|
|
8544
|
+
};
|
|
8545
|
+
const activeGutters = state.Facet.define();
|
|
8546
|
+
/**
|
|
8547
|
+
Define an editor gutter. The order in which the gutters appear is
|
|
8548
|
+
determined by their extension priority.
|
|
8549
|
+
*/
|
|
8550
|
+
function gutter(config) {
|
|
8551
|
+
return [gutters(), activeGutters.of(Object.assign(Object.assign({}, defaults), config))];
|
|
8552
|
+
}
|
|
8553
|
+
const unfixGutters = state.Facet.define({
|
|
8554
|
+
combine: values => values.some(x => x)
|
|
8555
|
+
});
|
|
8556
|
+
/**
|
|
8557
|
+
The gutter-drawing plugin is automatically enabled when you add a
|
|
8558
|
+
gutter, but you can use this function to explicitly configure it.
|
|
8559
|
+
|
|
8560
|
+
Unless `fixed` is explicitly set to `false`, the gutters are
|
|
8561
|
+
fixed, meaning they don't scroll along with the content
|
|
8562
|
+
horizontally (except on Internet Explorer, which doesn't support
|
|
8563
|
+
CSS [`position:
|
|
8564
|
+
sticky`](https://developer.mozilla.org/en-US/docs/Web/CSS/position#sticky)).
|
|
8565
|
+
*/
|
|
8566
|
+
function gutters(config) {
|
|
8567
|
+
let result = [
|
|
8568
|
+
gutterView,
|
|
8569
|
+
];
|
|
8570
|
+
if (config && config.fixed === false)
|
|
8571
|
+
result.push(unfixGutters.of(true));
|
|
8572
|
+
return result;
|
|
8573
|
+
}
|
|
8574
|
+
const gutterView = ViewPlugin.fromClass(class {
|
|
8575
|
+
constructor(view) {
|
|
8576
|
+
this.view = view;
|
|
8577
|
+
this.prevViewport = view.viewport;
|
|
8578
|
+
this.dom = document.createElement("div");
|
|
8579
|
+
this.dom.className = "cm-gutters";
|
|
8580
|
+
this.dom.setAttribute("aria-hidden", "true");
|
|
8581
|
+
this.dom.style.minHeight = this.view.contentHeight + "px";
|
|
8582
|
+
this.gutters = view.state.facet(activeGutters).map(conf => new SingleGutterView(view, conf));
|
|
8583
|
+
for (let gutter of this.gutters)
|
|
8584
|
+
this.dom.appendChild(gutter.dom);
|
|
8585
|
+
this.fixed = !view.state.facet(unfixGutters);
|
|
8586
|
+
if (this.fixed) {
|
|
8587
|
+
// FIXME IE11 fallback, which doesn't support position: sticky,
|
|
8588
|
+
// by using position: relative + event handlers that realign the
|
|
8589
|
+
// gutter (or just force fixed=false on IE11?)
|
|
8590
|
+
this.dom.style.position = "sticky";
|
|
8591
|
+
}
|
|
8592
|
+
this.syncGutters(false);
|
|
8593
|
+
view.scrollDOM.insertBefore(this.dom, view.contentDOM);
|
|
8594
|
+
}
|
|
8595
|
+
update(update) {
|
|
8596
|
+
if (this.updateGutters(update)) {
|
|
8597
|
+
// Detach during sync when the viewport changed significantly
|
|
8598
|
+
// (such as during scrolling), since for large updates that is
|
|
8599
|
+
// faster.
|
|
8600
|
+
let vpA = this.prevViewport, vpB = update.view.viewport;
|
|
8601
|
+
let vpOverlap = Math.min(vpA.to, vpB.to) - Math.max(vpA.from, vpB.from);
|
|
8602
|
+
this.syncGutters(vpOverlap < (vpB.to - vpB.from) * 0.8);
|
|
8603
|
+
}
|
|
8604
|
+
if (update.geometryChanged)
|
|
8605
|
+
this.dom.style.minHeight = this.view.contentHeight + "px";
|
|
8606
|
+
if (this.view.state.facet(unfixGutters) != !this.fixed) {
|
|
8607
|
+
this.fixed = !this.fixed;
|
|
8608
|
+
this.dom.style.position = this.fixed ? "sticky" : "";
|
|
8609
|
+
}
|
|
8610
|
+
this.prevViewport = update.view.viewport;
|
|
8611
|
+
}
|
|
8612
|
+
syncGutters(detach) {
|
|
8613
|
+
let after = this.dom.nextSibling;
|
|
8614
|
+
if (detach)
|
|
8615
|
+
this.dom.remove();
|
|
8616
|
+
let lineClasses = state.RangeSet.iter(this.view.state.facet(gutterLineClass), this.view.viewport.from);
|
|
8617
|
+
let classSet = [];
|
|
8618
|
+
let contexts = this.gutters.map(gutter => new UpdateContext(gutter, this.view.viewport, -this.view.documentPadding.top));
|
|
8619
|
+
for (let line of this.view.viewportLineBlocks) {
|
|
8620
|
+
let text;
|
|
8621
|
+
if (Array.isArray(line.type)) {
|
|
8622
|
+
for (let b of line.type)
|
|
8623
|
+
if (b.type == exports.BlockType.Text) {
|
|
8624
|
+
text = b;
|
|
8625
|
+
break;
|
|
8626
|
+
}
|
|
8627
|
+
}
|
|
8628
|
+
else {
|
|
8629
|
+
text = line.type == exports.BlockType.Text ? line : undefined;
|
|
8630
|
+
}
|
|
8631
|
+
if (!text)
|
|
8632
|
+
continue;
|
|
8633
|
+
if (classSet.length)
|
|
8634
|
+
classSet = [];
|
|
8635
|
+
advanceCursor(lineClasses, classSet, line.from);
|
|
8636
|
+
for (let cx of contexts)
|
|
8637
|
+
cx.line(this.view, text, classSet);
|
|
8638
|
+
}
|
|
8639
|
+
for (let cx of contexts)
|
|
8640
|
+
cx.finish();
|
|
8641
|
+
if (detach)
|
|
8642
|
+
this.view.scrollDOM.insertBefore(this.dom, after);
|
|
8643
|
+
}
|
|
8644
|
+
updateGutters(update) {
|
|
8645
|
+
let prev = update.startState.facet(activeGutters), cur = update.state.facet(activeGutters);
|
|
8646
|
+
let change = update.docChanged || update.heightChanged || update.viewportChanged ||
|
|
8647
|
+
!state.RangeSet.eq(update.startState.facet(gutterLineClass), update.state.facet(gutterLineClass), update.view.viewport.from, update.view.viewport.to);
|
|
8648
|
+
if (prev == cur) {
|
|
8649
|
+
for (let gutter of this.gutters)
|
|
8650
|
+
if (gutter.update(update))
|
|
8651
|
+
change = true;
|
|
8652
|
+
}
|
|
8653
|
+
else {
|
|
8654
|
+
change = true;
|
|
8655
|
+
let gutters = [];
|
|
8656
|
+
for (let conf of cur) {
|
|
8657
|
+
let known = prev.indexOf(conf);
|
|
8658
|
+
if (known < 0) {
|
|
8659
|
+
gutters.push(new SingleGutterView(this.view, conf));
|
|
8660
|
+
}
|
|
8661
|
+
else {
|
|
8662
|
+
this.gutters[known].update(update);
|
|
8663
|
+
gutters.push(this.gutters[known]);
|
|
8664
|
+
}
|
|
8665
|
+
}
|
|
8666
|
+
for (let g of this.gutters) {
|
|
8667
|
+
g.dom.remove();
|
|
8668
|
+
if (gutters.indexOf(g) < 0)
|
|
8669
|
+
g.destroy();
|
|
8670
|
+
}
|
|
8671
|
+
for (let g of gutters)
|
|
8672
|
+
this.dom.appendChild(g.dom);
|
|
8673
|
+
this.gutters = gutters;
|
|
8674
|
+
}
|
|
8675
|
+
return change;
|
|
8676
|
+
}
|
|
8677
|
+
destroy() {
|
|
8678
|
+
for (let view of this.gutters)
|
|
8679
|
+
view.destroy();
|
|
8680
|
+
this.dom.remove();
|
|
8681
|
+
}
|
|
8682
|
+
}, {
|
|
8683
|
+
provide: plugin => EditorView.scrollMargins.of(view => {
|
|
8684
|
+
let value = view.plugin(plugin);
|
|
8685
|
+
if (!value || value.gutters.length == 0 || !value.fixed)
|
|
8686
|
+
return null;
|
|
8687
|
+
return view.textDirection == exports.Direction.LTR ? { left: value.dom.offsetWidth } : { right: value.dom.offsetWidth };
|
|
8688
|
+
})
|
|
8689
|
+
});
|
|
8690
|
+
function asArray(val) { return (Array.isArray(val) ? val : [val]); }
|
|
8691
|
+
function advanceCursor(cursor, collect, pos) {
|
|
8692
|
+
while (cursor.value && cursor.from <= pos) {
|
|
8693
|
+
if (cursor.from == pos)
|
|
8694
|
+
collect.push(cursor.value);
|
|
8695
|
+
cursor.next();
|
|
8696
|
+
}
|
|
8697
|
+
}
|
|
8698
|
+
class UpdateContext {
|
|
8699
|
+
constructor(gutter, viewport, height) {
|
|
8700
|
+
this.gutter = gutter;
|
|
8701
|
+
this.height = height;
|
|
8702
|
+
this.localMarkers = [];
|
|
8703
|
+
this.i = 0;
|
|
8704
|
+
this.cursor = state.RangeSet.iter(gutter.markers, viewport.from);
|
|
8705
|
+
}
|
|
8706
|
+
line(view, line, extraMarkers) {
|
|
8707
|
+
if (this.localMarkers.length)
|
|
8708
|
+
this.localMarkers = [];
|
|
8709
|
+
advanceCursor(this.cursor, this.localMarkers, line.from);
|
|
8710
|
+
let localMarkers = extraMarkers.length ? this.localMarkers.concat(extraMarkers) : this.localMarkers;
|
|
8711
|
+
let forLine = this.gutter.config.lineMarker(view, line, localMarkers);
|
|
8712
|
+
if (forLine)
|
|
8713
|
+
localMarkers.unshift(forLine);
|
|
8714
|
+
let gutter = this.gutter;
|
|
8715
|
+
if (localMarkers.length == 0 && !gutter.config.renderEmptyElements)
|
|
8716
|
+
return;
|
|
8717
|
+
let above = line.top - this.height;
|
|
8718
|
+
if (this.i == gutter.elements.length) {
|
|
8719
|
+
let newElt = new GutterElement(view, line.height, above, localMarkers);
|
|
8720
|
+
gutter.elements.push(newElt);
|
|
8721
|
+
gutter.dom.appendChild(newElt.dom);
|
|
8722
|
+
}
|
|
8723
|
+
else {
|
|
8724
|
+
gutter.elements[this.i].update(view, line.height, above, localMarkers);
|
|
8725
|
+
}
|
|
8726
|
+
this.height = line.bottom;
|
|
8727
|
+
this.i++;
|
|
8728
|
+
}
|
|
8729
|
+
finish() {
|
|
8730
|
+
let gutter = this.gutter;
|
|
8731
|
+
while (gutter.elements.length > this.i) {
|
|
8732
|
+
let last = gutter.elements.pop();
|
|
8733
|
+
gutter.dom.removeChild(last.dom);
|
|
8734
|
+
last.destroy();
|
|
8735
|
+
}
|
|
8736
|
+
}
|
|
8737
|
+
}
|
|
8738
|
+
class SingleGutterView {
|
|
8739
|
+
constructor(view, config) {
|
|
8740
|
+
this.view = view;
|
|
8741
|
+
this.config = config;
|
|
8742
|
+
this.elements = [];
|
|
8743
|
+
this.spacer = null;
|
|
8744
|
+
this.dom = document.createElement("div");
|
|
8745
|
+
this.dom.className = "cm-gutter" + (this.config.class ? " " + this.config.class : "");
|
|
8746
|
+
for (let prop in config.domEventHandlers) {
|
|
8747
|
+
this.dom.addEventListener(prop, (event) => {
|
|
8748
|
+
let line = view.lineBlockAtHeight(event.clientY - view.documentTop);
|
|
8749
|
+
if (config.domEventHandlers[prop](view, line, event))
|
|
8750
|
+
event.preventDefault();
|
|
8751
|
+
});
|
|
8752
|
+
}
|
|
8753
|
+
this.markers = asArray(config.markers(view));
|
|
8754
|
+
if (config.initialSpacer) {
|
|
8755
|
+
this.spacer = new GutterElement(view, 0, 0, [config.initialSpacer(view)]);
|
|
8756
|
+
this.dom.appendChild(this.spacer.dom);
|
|
8757
|
+
this.spacer.dom.style.cssText += "visibility: hidden; pointer-events: none";
|
|
8758
|
+
}
|
|
8759
|
+
}
|
|
8760
|
+
update(update) {
|
|
8761
|
+
let prevMarkers = this.markers;
|
|
8762
|
+
this.markers = asArray(this.config.markers(update.view));
|
|
8763
|
+
if (this.spacer && this.config.updateSpacer) {
|
|
8764
|
+
let updated = this.config.updateSpacer(this.spacer.markers[0], update);
|
|
8765
|
+
if (updated != this.spacer.markers[0])
|
|
8766
|
+
this.spacer.update(update.view, 0, 0, [updated]);
|
|
8767
|
+
}
|
|
8768
|
+
let vp = update.view.viewport;
|
|
8769
|
+
return !state.RangeSet.eq(this.markers, prevMarkers, vp.from, vp.to) ||
|
|
8770
|
+
(this.config.lineMarkerChange ? this.config.lineMarkerChange(update) : false);
|
|
8771
|
+
}
|
|
8772
|
+
destroy() {
|
|
8773
|
+
for (let elt of this.elements)
|
|
8774
|
+
elt.destroy();
|
|
8775
|
+
}
|
|
8776
|
+
}
|
|
8777
|
+
class GutterElement {
|
|
8778
|
+
constructor(view, height, above, markers) {
|
|
8779
|
+
this.height = -1;
|
|
8780
|
+
this.above = 0;
|
|
8781
|
+
this.markers = [];
|
|
8782
|
+
this.dom = document.createElement("div");
|
|
8783
|
+
this.update(view, height, above, markers);
|
|
8784
|
+
}
|
|
8785
|
+
update(view, height, above, markers) {
|
|
8786
|
+
if (this.height != height)
|
|
8787
|
+
this.dom.style.height = (this.height = height) + "px";
|
|
8788
|
+
if (this.above != above)
|
|
8789
|
+
this.dom.style.marginTop = (this.above = above) ? above + "px" : "";
|
|
8790
|
+
if (!sameMarkers(this.markers, markers))
|
|
8791
|
+
this.setMarkers(view, markers);
|
|
8792
|
+
}
|
|
8793
|
+
setMarkers(view, markers) {
|
|
8794
|
+
let cls = "cm-gutterElement", domPos = this.dom.firstChild;
|
|
8795
|
+
for (let iNew = 0, iOld = 0;;) {
|
|
8796
|
+
let skipTo = iOld, marker = iNew < markers.length ? markers[iNew++] : null, matched = false;
|
|
8797
|
+
if (marker) {
|
|
8798
|
+
let c = marker.elementClass;
|
|
8799
|
+
if (c)
|
|
8800
|
+
cls += " " + c;
|
|
8801
|
+
for (let i = iOld; i < this.markers.length; i++)
|
|
8802
|
+
if (this.markers[i].compare(marker)) {
|
|
8803
|
+
skipTo = i;
|
|
8804
|
+
matched = true;
|
|
8805
|
+
break;
|
|
8806
|
+
}
|
|
8807
|
+
}
|
|
8808
|
+
else {
|
|
8809
|
+
skipTo = this.markers.length;
|
|
8810
|
+
}
|
|
8811
|
+
while (iOld < skipTo) {
|
|
8812
|
+
let next = this.markers[iOld++];
|
|
8813
|
+
if (next.toDOM) {
|
|
8814
|
+
next.destroy(domPos);
|
|
8815
|
+
let after = domPos.nextSibling;
|
|
8816
|
+
domPos.remove();
|
|
8817
|
+
domPos = after;
|
|
8818
|
+
}
|
|
8819
|
+
}
|
|
8820
|
+
if (!marker)
|
|
8821
|
+
break;
|
|
8822
|
+
if (marker.toDOM) {
|
|
8823
|
+
if (matched)
|
|
8824
|
+
domPos = domPos.nextSibling;
|
|
8825
|
+
else
|
|
8826
|
+
this.dom.insertBefore(marker.toDOM(view), domPos);
|
|
8827
|
+
}
|
|
8828
|
+
if (matched)
|
|
8829
|
+
iOld++;
|
|
8830
|
+
}
|
|
8831
|
+
this.dom.className = cls;
|
|
8832
|
+
this.markers = markers;
|
|
8833
|
+
}
|
|
8834
|
+
destroy() {
|
|
8835
|
+
this.setMarkers(null, []); // First argument not used unless creating markers
|
|
8836
|
+
}
|
|
8837
|
+
}
|
|
8838
|
+
function sameMarkers(a, b) {
|
|
8839
|
+
if (a.length != b.length)
|
|
8840
|
+
return false;
|
|
8841
|
+
for (let i = 0; i < a.length; i++)
|
|
8842
|
+
if (!a[i].compare(b[i]))
|
|
8843
|
+
return false;
|
|
8844
|
+
return true;
|
|
8845
|
+
}
|
|
8846
|
+
/**
|
|
8847
|
+
Facet used to provide markers to the line number gutter.
|
|
8848
|
+
*/
|
|
8849
|
+
const lineNumberMarkers = state.Facet.define();
|
|
8850
|
+
const lineNumberConfig = state.Facet.define({
|
|
8851
|
+
combine(values) {
|
|
8852
|
+
return state.combineConfig(values, { formatNumber: String, domEventHandlers: {} }, {
|
|
8853
|
+
domEventHandlers(a, b) {
|
|
8854
|
+
let result = Object.assign({}, a);
|
|
8855
|
+
for (let event in b) {
|
|
8856
|
+
let exists = result[event], add = b[event];
|
|
8857
|
+
result[event] = exists ? (view, line, event) => exists(view, line, event) || add(view, line, event) : add;
|
|
8858
|
+
}
|
|
8859
|
+
return result;
|
|
8860
|
+
}
|
|
8861
|
+
});
|
|
8862
|
+
}
|
|
8863
|
+
});
|
|
8864
|
+
class NumberMarker extends GutterMarker {
|
|
8865
|
+
constructor(number) {
|
|
8866
|
+
super();
|
|
8867
|
+
this.number = number;
|
|
8868
|
+
}
|
|
8869
|
+
eq(other) { return this.number == other.number; }
|
|
8870
|
+
toDOM() { return document.createTextNode(this.number); }
|
|
8871
|
+
}
|
|
8872
|
+
function formatNumber(view, number) {
|
|
8873
|
+
return view.state.facet(lineNumberConfig).formatNumber(number, view.state);
|
|
8874
|
+
}
|
|
8875
|
+
const lineNumberGutter = activeGutters.compute([lineNumberConfig], state => ({
|
|
8876
|
+
class: "cm-lineNumbers",
|
|
8877
|
+
renderEmptyElements: false,
|
|
8878
|
+
markers(view) { return view.state.facet(lineNumberMarkers); },
|
|
8879
|
+
lineMarker(view, line, others) {
|
|
8880
|
+
if (others.some(m => m.toDOM))
|
|
8881
|
+
return null;
|
|
8882
|
+
return new NumberMarker(formatNumber(view, view.state.doc.lineAt(line.from).number));
|
|
8883
|
+
},
|
|
8884
|
+
lineMarkerChange: update => update.startState.facet(lineNumberConfig) != update.state.facet(lineNumberConfig),
|
|
8885
|
+
initialSpacer(view) {
|
|
8886
|
+
return new NumberMarker(formatNumber(view, maxLineNumber(view.state.doc.lines)));
|
|
8887
|
+
},
|
|
8888
|
+
updateSpacer(spacer, update) {
|
|
8889
|
+
let max = formatNumber(update.view, maxLineNumber(update.view.state.doc.lines));
|
|
8890
|
+
return max == spacer.number ? spacer : new NumberMarker(max);
|
|
8891
|
+
},
|
|
8892
|
+
domEventHandlers: state.facet(lineNumberConfig).domEventHandlers
|
|
8893
|
+
}));
|
|
8894
|
+
/**
|
|
8895
|
+
Create a line number gutter extension.
|
|
8896
|
+
*/
|
|
8897
|
+
function lineNumbers(config = {}) {
|
|
8898
|
+
return [
|
|
8899
|
+
lineNumberConfig.of(config),
|
|
8900
|
+
gutters(),
|
|
8901
|
+
lineNumberGutter
|
|
8902
|
+
];
|
|
8903
|
+
}
|
|
8904
|
+
function maxLineNumber(lines) {
|
|
8905
|
+
let last = 9;
|
|
8906
|
+
while (last < lines)
|
|
8907
|
+
last = last * 10 + 9;
|
|
8908
|
+
return last;
|
|
8909
|
+
}
|
|
8910
|
+
const activeLineGutterMarker = new class extends GutterMarker {
|
|
8911
|
+
constructor() {
|
|
8912
|
+
super(...arguments);
|
|
8913
|
+
this.elementClass = "cm-activeLineGutter";
|
|
8914
|
+
}
|
|
8915
|
+
};
|
|
8916
|
+
const activeLineGutterHighlighter = gutterLineClass.compute(["selection"], state$1 => {
|
|
8917
|
+
let marks = [], last = -1;
|
|
8918
|
+
for (let range of state$1.selection.ranges)
|
|
8919
|
+
if (range.empty) {
|
|
8920
|
+
let linePos = state$1.doc.lineAt(range.head).from;
|
|
8921
|
+
if (linePos > last) {
|
|
8922
|
+
last = linePos;
|
|
8923
|
+
marks.push(activeLineGutterMarker.range(linePos));
|
|
8924
|
+
}
|
|
8925
|
+
}
|
|
8926
|
+
return state.RangeSet.of(marks);
|
|
8927
|
+
});
|
|
8928
|
+
/**
|
|
8929
|
+
Returns an extension that adds a `cm-activeLineGutter` class to
|
|
8930
|
+
all gutter elements on the [active
|
|
8931
|
+
line](https://codemirror.net/6/docs/ref/#view.highlightActiveLine).
|
|
8932
|
+
*/
|
|
8933
|
+
function highlightActiveLineGutter() {
|
|
8934
|
+
return activeLineGutterHighlighter;
|
|
8935
|
+
}
|
|
8936
|
+
|
|
8937
|
+
/**
|
|
8938
|
+
@internal
|
|
8939
|
+
*/
|
|
8940
|
+
const __test = { HeightMap, HeightOracle, MeasuredHeights, QueryType, ChangedRange, computeOrder, moveVisually };
|
|
8941
|
+
|
|
7726
8942
|
exports.BidiSpan = BidiSpan;
|
|
7727
8943
|
exports.BlockInfo = BlockInfo;
|
|
7728
8944
|
exports.Decoration = Decoration;
|
|
7729
8945
|
exports.EditorView = EditorView;
|
|
8946
|
+
exports.GutterMarker = GutterMarker;
|
|
7730
8947
|
exports.MatchDecorator = MatchDecorator;
|
|
7731
|
-
exports.PluginField = PluginField;
|
|
7732
|
-
exports.PluginFieldProvider = PluginFieldProvider;
|
|
7733
8948
|
exports.ViewPlugin = ViewPlugin;
|
|
7734
8949
|
exports.ViewUpdate = ViewUpdate;
|
|
7735
8950
|
exports.WidgetType = WidgetType;
|
|
7736
8951
|
exports.__test = __test;
|
|
8952
|
+
exports.closeHoverTooltips = closeHoverTooltips;
|
|
8953
|
+
exports.crosshairCursor = crosshairCursor;
|
|
7737
8954
|
exports.drawSelection = drawSelection;
|
|
7738
8955
|
exports.dropCursor = dropCursor;
|
|
8956
|
+
exports.getPanel = getPanel;
|
|
8957
|
+
exports.getTooltip = getTooltip;
|
|
8958
|
+
exports.gutter = gutter;
|
|
8959
|
+
exports.gutterLineClass = gutterLineClass;
|
|
8960
|
+
exports.gutters = gutters;
|
|
8961
|
+
exports.hasHoverTooltips = hasHoverTooltips;
|
|
7739
8962
|
exports.highlightActiveLine = highlightActiveLine;
|
|
8963
|
+
exports.highlightActiveLineGutter = highlightActiveLineGutter;
|
|
7740
8964
|
exports.highlightSpecialChars = highlightSpecialChars;
|
|
8965
|
+
exports.hoverTooltip = hoverTooltip;
|
|
7741
8966
|
exports.keymap = keymap;
|
|
8967
|
+
exports.lineNumberMarkers = lineNumberMarkers;
|
|
8968
|
+
exports.lineNumbers = lineNumbers;
|
|
7742
8969
|
exports.logException = logException;
|
|
8970
|
+
exports.panels = panels;
|
|
7743
8971
|
exports.placeholder = placeholder;
|
|
8972
|
+
exports.rectangularSelection = rectangularSelection;
|
|
8973
|
+
exports.repositionTooltips = repositionTooltips;
|
|
7744
8974
|
exports.runScopeHandlers = runScopeHandlers;
|
|
7745
8975
|
exports.scrollPastEnd = scrollPastEnd;
|
|
8976
|
+
exports.showPanel = showPanel;
|
|
8977
|
+
exports.showTooltip = showTooltip;
|
|
8978
|
+
exports.tooltips = tooltips;
|