@codemirror/view 0.19.39 → 0.19.43
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 +51 -1
- package/dist/index.cjs +173 -85
- package/dist/index.d.ts +1 -1
- package/dist/index.js +174 -86
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,53 @@
|
|
|
1
|
+
## 0.19.43 (2022-02-16)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix several issues where editing or composition went wrong due to our zero-width space kludge characters ending up in unexpected places.
|
|
6
|
+
|
|
7
|
+
Make sure the editor re-measures its dimensions whenever its theme changes.
|
|
8
|
+
|
|
9
|
+
Fix an issue where some keys on Android phones could leave the editor DOM unsynced with the actual document.
|
|
10
|
+
|
|
11
|
+
## 0.19.42 (2022-02-05)
|
|
12
|
+
|
|
13
|
+
### Bug fixes
|
|
14
|
+
|
|
15
|
+
Fix a regression in cursor position determination after making an edit next to a widget.
|
|
16
|
+
|
|
17
|
+
## 0.19.41 (2022-02-04)
|
|
18
|
+
|
|
19
|
+
### Bug fixes
|
|
20
|
+
|
|
21
|
+
Fix an issue where the editor's view of its content height could go out of sync with the DOM when a line-wrapping editor had its width changed, causing wrapping to change.
|
|
22
|
+
|
|
23
|
+
Fix a bug that caused the editor to draw way too much content when scrolling to a position in an editor (much) taller than the window.
|
|
24
|
+
|
|
25
|
+
Report an error when a replace decoration from a plugin crosses a line break, rather than silently ignoring it.
|
|
26
|
+
|
|
27
|
+
Fix an issue where reading DOM changes was broken when `lineSeparator` contained more than one character.
|
|
28
|
+
|
|
29
|
+
Make ordering of replace and mark decorations with the same extent and inclusivness more predictable by giving replace decorations precedence.
|
|
30
|
+
|
|
31
|
+
Fix a bug where, on Chrome, replacement across line boundaries and next to widgets could cause bogus zero-width characters to appear in the content.
|
|
32
|
+
|
|
33
|
+
## 0.19.40 (2022-01-19)
|
|
34
|
+
|
|
35
|
+
### Bug fixes
|
|
36
|
+
|
|
37
|
+
Make composition input properly appear at secondary cursors (except when those are in the DOM node with the composition, in which case the browser won't allow us to intervene without aborting the composition).
|
|
38
|
+
|
|
39
|
+
Fix a bug that cause the editor to get confused about which content was visible after scrolling something into view.
|
|
40
|
+
|
|
41
|
+
Fix a bug where the dummy elements rendered around widgets could end up in a separate set of wrapping marks, and thus become visible.
|
|
42
|
+
|
|
43
|
+
`EditorView.moveVertically` now preserves the `assoc` property of the input range.
|
|
44
|
+
|
|
45
|
+
Get rid of gaps between selection elements drawn by `drawSelection`.
|
|
46
|
+
|
|
47
|
+
Fix an issue where replacing text next to a widget might leak bogus zero-width spaces into the document.
|
|
48
|
+
|
|
49
|
+
Avoid browser selection mishandling when a focused view has `setState` called by eagerly refocusing it.
|
|
50
|
+
|
|
1
51
|
## 0.19.39 (2022-01-06)
|
|
2
52
|
|
|
3
53
|
### Bug fixes
|
|
@@ -48,7 +98,7 @@ Fix an issue where backspacing out a selection on Chrome Android would sometimes
|
|
|
48
98
|
|
|
49
99
|
### Bug fixes
|
|
50
100
|
|
|
51
|
-
Fix a bug where content line elements would in some cases lose their `cm-line` class.
|
|
101
|
+
Fix a bug where content line elements would in some cases lose their `cm-line` class.
|
|
52
102
|
|
|
53
103
|
## 0.19.33 (2021-12-16)
|
|
54
104
|
|
package/dist/index.cjs
CHANGED
|
@@ -602,9 +602,8 @@ function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) {
|
|
|
602
602
|
replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd);
|
|
603
603
|
}
|
|
604
604
|
|
|
605
|
-
let
|
|
606
|
-
|
|
607
|
-
: [{ userAgent: "", vendor: "", platform: "" }, { documentElement: { style: {} } }];
|
|
605
|
+
let nav = typeof navigator != "undefined" ? navigator : { userAgent: "", vendor: "", platform: "" };
|
|
606
|
+
let doc = typeof document != "undefined" ? document : { documentElement: { style: {} } };
|
|
608
607
|
const ie_edge = /Edge\/(\d+)/.exec(nav.userAgent);
|
|
609
608
|
const ie_upto10 = /MSIE \d/.test(nav.userAgent);
|
|
610
609
|
const ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(nav.userAgent);
|
|
@@ -860,9 +859,6 @@ class CompositionView extends WidgetView {
|
|
|
860
859
|
coordsAt(pos, side) { return textCoords(this.widget.text, pos, side); }
|
|
861
860
|
get isEditable() { return true; }
|
|
862
861
|
}
|
|
863
|
-
// Use two characters on Android, to prevent Chrome from closing the
|
|
864
|
-
// virtual keyboard when backspacing after a widget (#602).
|
|
865
|
-
const ZeroWidthSpace = browser.android ? "\u200b\u200b" : "\u200b";
|
|
866
862
|
// These are drawn around uneditable widgets to avoid a number of
|
|
867
863
|
// browser bugs that show up when the cursor is directly next to
|
|
868
864
|
// uneditable inline content.
|
|
@@ -878,21 +874,21 @@ class WidgetBufferView extends ContentView {
|
|
|
878
874
|
}
|
|
879
875
|
split() { return new WidgetBufferView(this.side); }
|
|
880
876
|
sync() {
|
|
881
|
-
if (!this.dom)
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
this.dom
|
|
877
|
+
if (!this.dom) {
|
|
878
|
+
let dom = document.createElement("img");
|
|
879
|
+
dom.className = "cm-widgetBuffer";
|
|
880
|
+
this.setDOM(dom);
|
|
881
|
+
}
|
|
885
882
|
}
|
|
886
883
|
getSide() { return this.side; }
|
|
887
884
|
domAtPos(pos) { return DOMPos.before(this.dom); }
|
|
888
885
|
localPosFromDOM() { return 0; }
|
|
889
886
|
domBoundsAround() { return null; }
|
|
890
887
|
coordsAt(pos) {
|
|
891
|
-
|
|
892
|
-
return rects[rects.length - 1] || null;
|
|
888
|
+
return this.dom.getBoundingClientRect();
|
|
893
889
|
}
|
|
894
890
|
get overrideDOMText() {
|
|
895
|
-
return text.Text.
|
|
891
|
+
return text.Text.empty;
|
|
896
892
|
}
|
|
897
893
|
}
|
|
898
894
|
TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren;
|
|
@@ -1131,8 +1127,8 @@ class Decoration extends rangeset.RangeValue {
|
|
|
1131
1127
|
static replace(spec) {
|
|
1132
1128
|
let block = !!spec.block;
|
|
1133
1129
|
let { start, end } = getInclusive(spec, block);
|
|
1134
|
-
let startSide =
|
|
1135
|
-
let endSide =
|
|
1130
|
+
let startSide = (start ? (block ? -300000000 /* BlockIncStart */ : -1 /* InlineIncStart */) : 400000000 /* NonIncStart */) - 1;
|
|
1131
|
+
let endSide = (end ? (block ? 200000000 /* BlockIncEnd */ : 1 /* InlineIncEnd */) : -500000000 /* NonIncEnd */) + 1;
|
|
1136
1132
|
return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
|
|
1137
1133
|
}
|
|
1138
1134
|
/**
|
|
@@ -1237,7 +1233,7 @@ function widgetsEq(a, b) {
|
|
|
1237
1233
|
}
|
|
1238
1234
|
function addRange(from, to, ranges, margin = 0) {
|
|
1239
1235
|
let last = ranges.length - 1;
|
|
1240
|
-
if (last >= 0 && ranges[last] + margin
|
|
1236
|
+
if (last >= 0 && ranges[last] + margin >= from)
|
|
1241
1237
|
ranges[last] = Math.max(ranges[last], to);
|
|
1242
1238
|
else
|
|
1243
1239
|
ranges.push(from, to);
|
|
@@ -1518,7 +1514,7 @@ class ContentBuilder {
|
|
|
1518
1514
|
}
|
|
1519
1515
|
}
|
|
1520
1516
|
let take = Math.min(this.text.length - this.textOff, length, 512 /* Chunk */);
|
|
1521
|
-
this.flushBuffer(active);
|
|
1517
|
+
this.flushBuffer(active.slice(0, openStart));
|
|
1522
1518
|
this.getLine().append(wrapMarks(new TextView(this.text.slice(this.textOff, this.textOff + take)), active), openStart);
|
|
1523
1519
|
this.atCursorPos = true;
|
|
1524
1520
|
this.textOff += take;
|
|
@@ -1577,11 +1573,13 @@ class ContentBuilder {
|
|
|
1577
1573
|
this.openStart = openStart;
|
|
1578
1574
|
}
|
|
1579
1575
|
filterPoint(from, to, value, index) {
|
|
1580
|
-
if (index
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1576
|
+
if (index < this.disallowBlockEffectsBelow && value instanceof PointDecoration) {
|
|
1577
|
+
if (value.block)
|
|
1578
|
+
throw new RangeError("Block decorations may not be specified via plugins");
|
|
1579
|
+
if (to > this.doc.lineAt(this.pos).to)
|
|
1580
|
+
throw new RangeError("Decorations that replace line breaks may not be specified via plugins");
|
|
1581
|
+
}
|
|
1582
|
+
return true;
|
|
1585
1583
|
}
|
|
1586
1584
|
static build(text, from, to, decorations, pluginDecorationLength) {
|
|
1587
1585
|
let builder = new ContentBuilder(text, from, to, pluginDecorationLength);
|
|
@@ -2305,12 +2303,18 @@ function moveVisually(line, order, dir, start, forward) {
|
|
|
2305
2303
|
return state.EditorSelection.cursor(nextIndex + line.from, forward ? -1 : 1, span.level);
|
|
2306
2304
|
}
|
|
2307
2305
|
|
|
2306
|
+
const LineBreakPlaceholder = "\uffff";
|
|
2308
2307
|
class DOMReader {
|
|
2309
|
-
constructor(points,
|
|
2308
|
+
constructor(points, state$1) {
|
|
2310
2309
|
this.points = points;
|
|
2311
|
-
this.view = view;
|
|
2312
2310
|
this.text = "";
|
|
2313
|
-
this.
|
|
2311
|
+
this.lineSeparator = state$1.facet(state.EditorState.lineSeparator);
|
|
2312
|
+
}
|
|
2313
|
+
append(text) {
|
|
2314
|
+
this.text += text;
|
|
2315
|
+
}
|
|
2316
|
+
lineBreak() {
|
|
2317
|
+
this.text += LineBreakPlaceholder;
|
|
2314
2318
|
}
|
|
2315
2319
|
readRange(start, end) {
|
|
2316
2320
|
if (!start)
|
|
@@ -2326,33 +2330,61 @@ class DOMReader {
|
|
|
2326
2330
|
if (view && nextView ? view.breakAfter :
|
|
2327
2331
|
(view ? view.breakAfter : isBlockElement(cur)) ||
|
|
2328
2332
|
(isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore)))
|
|
2329
|
-
this.
|
|
2333
|
+
this.lineBreak();
|
|
2330
2334
|
cur = next;
|
|
2331
2335
|
}
|
|
2332
2336
|
this.findPointBefore(parent, end);
|
|
2333
2337
|
return this;
|
|
2334
2338
|
}
|
|
2339
|
+
readTextNode(node) {
|
|
2340
|
+
let text = node.nodeValue;
|
|
2341
|
+
for (let point of this.points)
|
|
2342
|
+
if (point.node == node)
|
|
2343
|
+
point.pos = this.text.length + Math.min(point.offset, text.length);
|
|
2344
|
+
for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
|
|
2345
|
+
let nextBreak = -1, breakSize = 1, m;
|
|
2346
|
+
if (this.lineSeparator) {
|
|
2347
|
+
nextBreak = text.indexOf(this.lineSeparator, off);
|
|
2348
|
+
breakSize = this.lineSeparator.length;
|
|
2349
|
+
}
|
|
2350
|
+
else if (m = re.exec(text)) {
|
|
2351
|
+
nextBreak = m.index;
|
|
2352
|
+
breakSize = m[0].length;
|
|
2353
|
+
}
|
|
2354
|
+
this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
|
|
2355
|
+
if (nextBreak < 0)
|
|
2356
|
+
break;
|
|
2357
|
+
this.lineBreak();
|
|
2358
|
+
if (breakSize > 1)
|
|
2359
|
+
for (let point of this.points)
|
|
2360
|
+
if (point.node == node && point.pos > this.text.length)
|
|
2361
|
+
point.pos -= breakSize - 1;
|
|
2362
|
+
off = nextBreak + breakSize;
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2335
2365
|
readNode(node) {
|
|
2336
2366
|
if (node.cmIgnore)
|
|
2337
2367
|
return;
|
|
2338
2368
|
let view = ContentView.get(node);
|
|
2339
2369
|
let fromView = view && view.overrideDOMText;
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2370
|
+
if (fromView != null) {
|
|
2371
|
+
this.findPointInside(node, fromView.length);
|
|
2372
|
+
for (let i = fromView.iter(); !i.next().done;) {
|
|
2373
|
+
if (i.lineBreak)
|
|
2374
|
+
this.lineBreak();
|
|
2375
|
+
else
|
|
2376
|
+
this.append(i.value);
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
else if (node.nodeType == 3) {
|
|
2380
|
+
this.readTextNode(node);
|
|
2381
|
+
}
|
|
2382
|
+
else if (node.nodeName == "BR") {
|
|
2383
|
+
if (node.nextSibling)
|
|
2384
|
+
this.lineBreak();
|
|
2385
|
+
}
|
|
2386
|
+
else if (node.nodeType == 1) {
|
|
2348
2387
|
this.readRange(node.firstChild, null);
|
|
2349
|
-
if (text != null) {
|
|
2350
|
-
this.findPointIn(node, text.length);
|
|
2351
|
-
this.text += text;
|
|
2352
|
-
// Chrome inserts two newlines when pressing shift-enter at the
|
|
2353
|
-
// end of a line. This drops one of those.
|
|
2354
|
-
if (browser.chrome && this.view.inputState.lastKeyCode == 13 && !node.nextSibling && /\n\n$/.test(this.text))
|
|
2355
|
-
this.text = this.text.slice(0, -1);
|
|
2356
2388
|
}
|
|
2357
2389
|
}
|
|
2358
2390
|
findPointBefore(node, next) {
|
|
@@ -2360,10 +2392,10 @@ class DOMReader {
|
|
|
2360
2392
|
if (point.node == node && node.childNodes[point.offset] == next)
|
|
2361
2393
|
point.pos = this.text.length;
|
|
2362
2394
|
}
|
|
2363
|
-
|
|
2395
|
+
findPointInside(node, maxLen) {
|
|
2364
2396
|
for (let point of this.points)
|
|
2365
|
-
if (point.node == node)
|
|
2366
|
-
point.pos = this.text.length + Math.min(point.offset
|
|
2397
|
+
if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
|
|
2398
|
+
point.pos = this.text.length + Math.min(maxLen, point.offset);
|
|
2367
2399
|
}
|
|
2368
2400
|
}
|
|
2369
2401
|
function isBlockElement(node) {
|
|
@@ -2768,51 +2800,57 @@ class BlockGapWidget extends WidgetType {
|
|
|
2768
2800
|
}
|
|
2769
2801
|
get estimatedHeight() { return this.height; }
|
|
2770
2802
|
}
|
|
2771
|
-
function
|
|
2803
|
+
function compositionSurroundingNode(view) {
|
|
2772
2804
|
let sel = view.observer.selectionRange;
|
|
2773
2805
|
let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
|
|
2774
2806
|
if (!textNode)
|
|
2775
|
-
return
|
|
2807
|
+
return null;
|
|
2776
2808
|
let cView = view.docView.nearest(textNode);
|
|
2777
2809
|
if (!cView)
|
|
2778
|
-
return
|
|
2779
|
-
let from, to, topNode = textNode;
|
|
2810
|
+
return null;
|
|
2780
2811
|
if (cView instanceof LineView) {
|
|
2812
|
+
let topNode = textNode;
|
|
2781
2813
|
while (topNode.parentNode != cView.dom)
|
|
2782
2814
|
topNode = topNode.parentNode;
|
|
2783
2815
|
let prev = topNode.previousSibling;
|
|
2784
2816
|
while (prev && !ContentView.get(prev))
|
|
2785
2817
|
prev = prev.previousSibling;
|
|
2786
|
-
|
|
2818
|
+
let pos = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
|
|
2819
|
+
return { from: pos, to: pos, node: topNode, text: textNode };
|
|
2787
2820
|
}
|
|
2788
2821
|
else {
|
|
2789
2822
|
for (;;) {
|
|
2790
2823
|
let { parent } = cView;
|
|
2791
2824
|
if (!parent)
|
|
2792
|
-
return
|
|
2825
|
+
return null;
|
|
2793
2826
|
if (parent instanceof LineView)
|
|
2794
2827
|
break;
|
|
2795
2828
|
cView = parent;
|
|
2796
2829
|
}
|
|
2797
|
-
from = cView.posAtStart;
|
|
2798
|
-
|
|
2799
|
-
topNode = cView.dom;
|
|
2830
|
+
let from = cView.posAtStart;
|
|
2831
|
+
return { from, to: from + cView.length, node: cView.dom, text: textNode };
|
|
2800
2832
|
}
|
|
2833
|
+
}
|
|
2834
|
+
function computeCompositionDeco(view, changes) {
|
|
2835
|
+
let surrounding = compositionSurroundingNode(view);
|
|
2836
|
+
if (!surrounding)
|
|
2837
|
+
return Decoration.none;
|
|
2838
|
+
let { from, to, node, text: textNode } = surrounding;
|
|
2801
2839
|
let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
|
|
2802
|
-
let { state } = view, text =
|
|
2803
|
-
new DOMReader([],
|
|
2840
|
+
let { state } = view, text = node.nodeType == 3 ? node.nodeValue :
|
|
2841
|
+
new DOMReader([], state).readRange(node.firstChild, null).text;
|
|
2804
2842
|
if (newTo - newFrom < text.length) {
|
|
2805
|
-
if (state.
|
|
2843
|
+
if (state.doc.sliceString(newFrom, Math.min(state.doc.length, newFrom + text.length), LineBreakPlaceholder) == text)
|
|
2806
2844
|
newTo = newFrom + text.length;
|
|
2807
|
-
else if (state.
|
|
2845
|
+
else if (state.doc.sliceString(Math.max(0, newTo - text.length), newTo, LineBreakPlaceholder) == text)
|
|
2808
2846
|
newFrom = newTo - text.length;
|
|
2809
2847
|
else
|
|
2810
2848
|
return Decoration.none;
|
|
2811
2849
|
}
|
|
2812
|
-
else if (state.
|
|
2850
|
+
else if (state.doc.sliceString(newFrom, newTo, LineBreakPlaceholder) != text) {
|
|
2813
2851
|
return Decoration.none;
|
|
2814
2852
|
}
|
|
2815
|
-
return Decoration.set(Decoration.replace({ widget: new CompositionWidget(
|
|
2853
|
+
return Decoration.set(Decoration.replace({ widget: new CompositionWidget(node, textNode) }).range(newFrom, newTo));
|
|
2816
2854
|
}
|
|
2817
2855
|
class CompositionWidget extends WidgetType {
|
|
2818
2856
|
constructor(top, text) {
|
|
@@ -3149,7 +3187,7 @@ function byGroup(view, pos, start) {
|
|
|
3149
3187
|
function moveVertically(view, start, forward, distance) {
|
|
3150
3188
|
let startPos = start.head, dir = forward ? 1 : -1;
|
|
3151
3189
|
if (startPos == (forward ? view.state.doc.length : 0))
|
|
3152
|
-
return state.EditorSelection.cursor(startPos);
|
|
3190
|
+
return state.EditorSelection.cursor(startPos, start.assoc);
|
|
3153
3191
|
let goal = start.goalColumn, startY;
|
|
3154
3192
|
let rect = view.contentDOM.getBoundingClientRect();
|
|
3155
3193
|
let startCoords = view.coordsAtPos(startPos), docTop = view.documentTop;
|
|
@@ -3170,7 +3208,7 @@ function moveVertically(view, start, forward, distance) {
|
|
|
3170
3208
|
let curY = startY + (dist + extra) * dir;
|
|
3171
3209
|
let pos = posAtCoords(view, { x: resolvedGoal, y: curY }, false, dir);
|
|
3172
3210
|
if (curY < rect.top || curY > rect.bottom || (dir < 0 ? pos < startPos : pos > startPos))
|
|
3173
|
-
return state.EditorSelection.cursor(pos,
|
|
3211
|
+
return state.EditorSelection.cursor(pos, start.assoc, undefined, goal);
|
|
3174
3212
|
}
|
|
3175
3213
|
}
|
|
3176
3214
|
function skipAtoms(view, oldPos, pos) {
|
|
@@ -4682,6 +4720,12 @@ class ViewState {
|
|
|
4682
4720
|
let refresh = this.heightOracle.mustRefreshForStyle(whiteSpace, direction);
|
|
4683
4721
|
let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != dom.clientHeight;
|
|
4684
4722
|
let result = 0, bias = 0;
|
|
4723
|
+
if (this.editorWidth != view.scrollDOM.clientWidth) {
|
|
4724
|
+
if (oracle.lineWrapping)
|
|
4725
|
+
measureContent = true;
|
|
4726
|
+
this.editorWidth = view.scrollDOM.clientWidth;
|
|
4727
|
+
result |= 8 /* Geometry */;
|
|
4728
|
+
}
|
|
4685
4729
|
if (measureContent) {
|
|
4686
4730
|
this.mustMeasureContent = false;
|
|
4687
4731
|
this.contentDOMHeight = dom.clientHeight;
|
|
@@ -4694,7 +4738,8 @@ class ViewState {
|
|
|
4694
4738
|
}
|
|
4695
4739
|
}
|
|
4696
4740
|
// Pixel viewport
|
|
4697
|
-
let pixelViewport = this.printing ? { top: -1e8, bottom: 1e8, left: -1e8, right: 1e8 }
|
|
4741
|
+
let pixelViewport = this.printing ? { top: -1e8, bottom: 1e8, left: -1e8, right: 1e8 }
|
|
4742
|
+
: visiblePixelRange(dom, this.paddingTop);
|
|
4698
4743
|
let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
|
|
4699
4744
|
this.pixelViewport = pixelViewport;
|
|
4700
4745
|
let inView = this.pixelViewport.bottom > this.pixelViewport.top && this.pixelViewport.right > this.pixelViewport.left;
|
|
@@ -4706,11 +4751,9 @@ class ViewState {
|
|
|
4706
4751
|
if (!this.inView)
|
|
4707
4752
|
return 0;
|
|
4708
4753
|
let contentWidth = dom.clientWidth;
|
|
4709
|
-
if (this.contentDOMWidth != contentWidth || this.editorHeight != view.scrollDOM.clientHeight
|
|
4710
|
-
this.editorWidth != view.scrollDOM.clientWidth) {
|
|
4754
|
+
if (this.contentDOMWidth != contentWidth || this.editorHeight != view.scrollDOM.clientHeight) {
|
|
4711
4755
|
this.contentDOMWidth = contentWidth;
|
|
4712
4756
|
this.editorHeight = view.scrollDOM.clientHeight;
|
|
4713
|
-
this.editorWidth = view.scrollDOM.clientWidth;
|
|
4714
4757
|
result |= 8 /* Geometry */;
|
|
4715
4758
|
}
|
|
4716
4759
|
if (measureContent) {
|
|
@@ -4765,8 +4808,9 @@ class ViewState {
|
|
|
4765
4808
|
let viewport = new Viewport(map.lineAt(visibleTop - marginTop * 1000 /* Margin */, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(visibleBottom + (1 - marginTop) * 1000 /* Margin */, QueryType.ByHeight, doc, 0, 0).to);
|
|
4766
4809
|
// If scrollTarget is given, make sure the viewport includes that position
|
|
4767
4810
|
if (scrollTarget) {
|
|
4768
|
-
let { head } = scrollTarget.range
|
|
4811
|
+
let { head } = scrollTarget.range;
|
|
4769
4812
|
if (head < viewport.from || head > viewport.to) {
|
|
4813
|
+
let viewHeight = Math.min(this.editorHeight, this.pixelViewport.bottom - this.pixelViewport.top);
|
|
4770
4814
|
let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
|
|
4771
4815
|
if (scrollTarget.y == "center")
|
|
4772
4816
|
topPos = (block.top + block.bottom) / 2 - viewHeight / 2;
|
|
@@ -5152,6 +5196,10 @@ const baseTheme = buildTheme("." + baseThemeID, {
|
|
|
5152
5196
|
overflow: "hidden",
|
|
5153
5197
|
verticalAlign: "bottom"
|
|
5154
5198
|
},
|
|
5199
|
+
".cm-widgetBuffer": {
|
|
5200
|
+
verticalAlign: "text-bottom",
|
|
5201
|
+
height: "1em",
|
|
5202
|
+
},
|
|
5155
5203
|
".cm-placeholder": {
|
|
5156
5204
|
color: "#888",
|
|
5157
5205
|
display: "inline-block",
|
|
@@ -5400,7 +5448,7 @@ class DOMObserver {
|
|
|
5400
5448
|
}
|
|
5401
5449
|
// Throw away any pending changes
|
|
5402
5450
|
clear() {
|
|
5403
|
-
this.
|
|
5451
|
+
this.processRecords();
|
|
5404
5452
|
this.queue.length = 0;
|
|
5405
5453
|
this.selectionChanged = false;
|
|
5406
5454
|
}
|
|
@@ -5566,9 +5614,8 @@ function applyDOMChange(view, start, end, typeOver) {
|
|
|
5566
5614
|
return;
|
|
5567
5615
|
let { from, to } = bounds;
|
|
5568
5616
|
let selPoints = view.docView.impreciseHead || view.docView.impreciseAnchor ? [] : selectionPoints(view);
|
|
5569
|
-
let reader = new DOMReader(selPoints, view);
|
|
5617
|
+
let reader = new DOMReader(selPoints, view.state);
|
|
5570
5618
|
reader.readRange(bounds.startDOM, bounds.endDOM);
|
|
5571
|
-
newSel = selectionFromPoints(selPoints, from);
|
|
5572
5619
|
let preferredPos = sel.from, preferredSide = null;
|
|
5573
5620
|
// Prefer anchoring to end when Backspace is pressed (or, on
|
|
5574
5621
|
// Android, when something was deleted)
|
|
@@ -5577,10 +5624,17 @@ function applyDOMChange(view, start, end, typeOver) {
|
|
|
5577
5624
|
preferredPos = sel.to;
|
|
5578
5625
|
preferredSide = "end";
|
|
5579
5626
|
}
|
|
5580
|
-
let diff = findDiff(view.state.
|
|
5581
|
-
if (diff)
|
|
5627
|
+
let diff = findDiff(view.state.doc.sliceString(from, to, LineBreakPlaceholder), reader.text, preferredPos - from, preferredSide);
|
|
5628
|
+
if (diff) {
|
|
5629
|
+
// Chrome inserts two newlines when pressing shift-enter at the
|
|
5630
|
+
// end of a line. This drops one of those.
|
|
5631
|
+
if (browser.chrome && view.inputState.lastKeyCode == 13 &&
|
|
5632
|
+
diff.toB == diff.from + 2 && reader.text.slice(diff.from, diff.toB) == LineBreakPlaceholder + LineBreakPlaceholder)
|
|
5633
|
+
diff.toB--;
|
|
5582
5634
|
change = { from: from + diff.from, to: from + diff.toA,
|
|
5583
|
-
insert:
|
|
5635
|
+
insert: state.Text.of(reader.text.slice(diff.from, diff.toB).split(LineBreakPlaceholder)) };
|
|
5636
|
+
}
|
|
5637
|
+
newSel = selectionFromPoints(selPoints, from);
|
|
5584
5638
|
}
|
|
5585
5639
|
else if (view.hasFocus || !view.state.facet(editable)) {
|
|
5586
5640
|
let domSel = view.observer.selectionRange;
|
|
@@ -5637,19 +5691,47 @@ function applyDOMChange(view, start, end, typeOver) {
|
|
|
5637
5691
|
view.inputState.composing++;
|
|
5638
5692
|
let tr;
|
|
5639
5693
|
if (change.from >= sel.from && change.to <= sel.to && change.to - change.from >= (sel.to - sel.from) / 3 &&
|
|
5640
|
-
(!newSel || newSel.main.empty && newSel.main.from == change.from + change.insert.length)
|
|
5694
|
+
(!newSel || newSel.main.empty && newSel.main.from == change.from + change.insert.length) &&
|
|
5695
|
+
view.inputState.composing < 0) {
|
|
5641
5696
|
let before = sel.from < change.from ? startState.sliceDoc(sel.from, change.from) : "";
|
|
5642
5697
|
let after = sel.to > change.to ? startState.sliceDoc(change.to, sel.to) : "";
|
|
5643
|
-
tr = startState.replaceSelection(view.state.toText(before + change.insert.sliceString(0, undefined, view.state.lineBreak) +
|
|
5644
|
-
after));
|
|
5698
|
+
tr = startState.replaceSelection(view.state.toText(before + change.insert.sliceString(0, undefined, view.state.lineBreak) + after));
|
|
5645
5699
|
}
|
|
5646
5700
|
else {
|
|
5647
5701
|
let changes = startState.changes(change);
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5702
|
+
let mainSel = newSel && !startState.selection.main.eq(newSel.main) && newSel.main.to <= changes.newLength
|
|
5703
|
+
? newSel.main : undefined;
|
|
5704
|
+
// Try to apply a composition change to all cursors
|
|
5705
|
+
if (startState.selection.ranges.length > 1 && view.inputState.composing >= 0 &&
|
|
5706
|
+
change.to <= sel.to && change.to >= sel.to - 10) {
|
|
5707
|
+
let replaced = view.state.sliceDoc(change.from, change.to);
|
|
5708
|
+
let compositionRange = compositionSurroundingNode(view) || view.state.doc.lineAt(sel.head);
|
|
5709
|
+
let offset = sel.to - change.to, size = sel.to - sel.from;
|
|
5710
|
+
tr = startState.changeByRange(range => {
|
|
5711
|
+
if (range.from == sel.from && range.to == sel.to)
|
|
5712
|
+
return { changes, range: mainSel || range.map(changes) };
|
|
5713
|
+
let to = range.to - offset, from = to - replaced.length;
|
|
5714
|
+
if (range.to - range.from != size || view.state.sliceDoc(from, to) != replaced ||
|
|
5715
|
+
// Unfortunately, there's no way to make multiple
|
|
5716
|
+
// changes in the same node work without aborting
|
|
5717
|
+
// composition, so cursors in the composition range are
|
|
5718
|
+
// ignored.
|
|
5719
|
+
compositionRange && range.to >= compositionRange.from && range.from <= compositionRange.to)
|
|
5720
|
+
return { range };
|
|
5721
|
+
let rangeChanges = startState.changes({ from, to, insert: change.insert }), selOff = range.to - sel.to;
|
|
5722
|
+
return {
|
|
5723
|
+
changes: rangeChanges,
|
|
5724
|
+
range: !mainSel ? range.map(rangeChanges) :
|
|
5725
|
+
state.EditorSelection.range(Math.max(0, mainSel.anchor + selOff), Math.max(0, mainSel.head + selOff))
|
|
5726
|
+
};
|
|
5727
|
+
});
|
|
5728
|
+
}
|
|
5729
|
+
else {
|
|
5730
|
+
tr = {
|
|
5731
|
+
changes,
|
|
5732
|
+
selection: mainSel && startState.selection.replaceRange(mainSel)
|
|
5733
|
+
};
|
|
5734
|
+
}
|
|
5653
5735
|
}
|
|
5654
5736
|
let userEvent = "input.type";
|
|
5655
5737
|
if (view.composing) {
|
|
@@ -5899,7 +5981,9 @@ class EditorView {
|
|
|
5899
5981
|
finally {
|
|
5900
5982
|
this.updateState = 0 /* Idle */;
|
|
5901
5983
|
}
|
|
5902
|
-
if (
|
|
5984
|
+
if (update.startState.facet(theme) != update.state.facet(theme))
|
|
5985
|
+
this.viewState.mustMeasureContent = true;
|
|
5986
|
+
if (redrawn || scrollTarget || this.viewState.mustEnforceCursorAssoc || this.viewState.mustMeasureContent)
|
|
5903
5987
|
this.requestMeasure();
|
|
5904
5988
|
if (!update.empty)
|
|
5905
5989
|
for (let listener of this.state.facet(updateListener))
|
|
@@ -5920,6 +6004,7 @@ class EditorView {
|
|
|
5920
6004
|
return;
|
|
5921
6005
|
}
|
|
5922
6006
|
this.updateState = 2 /* Updating */;
|
|
6007
|
+
let hadFocus = this.hasFocus;
|
|
5923
6008
|
try {
|
|
5924
6009
|
for (let plugin of this.plugins)
|
|
5925
6010
|
plugin.destroy(this);
|
|
@@ -5937,6 +6022,8 @@ class EditorView {
|
|
|
5937
6022
|
finally {
|
|
5938
6023
|
this.updateState = 0 /* Idle */;
|
|
5939
6024
|
}
|
|
6025
|
+
if (hadFocus)
|
|
6026
|
+
this.focus();
|
|
5940
6027
|
this.requestMeasure();
|
|
5941
6028
|
}
|
|
5942
6029
|
updatePlugins(update) {
|
|
@@ -6006,7 +6093,7 @@ class EditorView {
|
|
|
6006
6093
|
return BadMeasure;
|
|
6007
6094
|
}
|
|
6008
6095
|
});
|
|
6009
|
-
let update = new ViewUpdate(this, this.state), redrawn = false;
|
|
6096
|
+
let update = new ViewUpdate(this, this.state), redrawn = false, scrolled = false;
|
|
6010
6097
|
update.flags |= changed;
|
|
6011
6098
|
if (!updated)
|
|
6012
6099
|
updated = update;
|
|
@@ -6033,11 +6120,12 @@ class EditorView {
|
|
|
6033
6120
|
if (this.viewState.scrollTarget) {
|
|
6034
6121
|
this.docView.scrollIntoView(this.viewState.scrollTarget);
|
|
6035
6122
|
this.viewState.scrollTarget = null;
|
|
6123
|
+
scrolled = true;
|
|
6036
6124
|
}
|
|
6037
6125
|
if (redrawn)
|
|
6038
6126
|
this.docView.updateSelection(true);
|
|
6039
6127
|
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
|
|
6040
|
-
this.measureRequests.length == 0)
|
|
6128
|
+
!scrolled && this.measureRequests.length == 0)
|
|
6041
6129
|
break;
|
|
6042
6130
|
}
|
|
6043
6131
|
}
|
|
@@ -6173,7 +6261,7 @@ class EditorView {
|
|
|
6173
6261
|
(`view.contentDOM.getBoundingClientRect().top`) to limit layout
|
|
6174
6262
|
queries.
|
|
6175
6263
|
|
|
6176
|
-
*Deprecated: use `
|
|
6264
|
+
*Deprecated: use `elementAtHeight` instead.*
|
|
6177
6265
|
*/
|
|
6178
6266
|
blockAtHeight(height, docTop) {
|
|
6179
6267
|
let top = ensureTop(docTop, this);
|
|
@@ -7018,7 +7106,7 @@ function measureRange(view, range) {
|
|
|
7018
7106
|
return pieces(top).concat(between).concat(pieces(bottom));
|
|
7019
7107
|
}
|
|
7020
7108
|
function piece(left, top, right, bottom) {
|
|
7021
|
-
return new Piece(left - base.left, top - base.top
|
|
7109
|
+
return new Piece(left - base.left, top - base.top - 0.01 /* Epsilon */, right - left, bottom - top + 0.01 /* Epsilon */, "cm-selectionBackground");
|
|
7022
7110
|
}
|
|
7023
7111
|
function pieces({ top, bottom, horizontal }) {
|
|
7024
7112
|
let pieces = [];
|
package/dist/index.d.ts
CHANGED
|
@@ -802,7 +802,7 @@ declare class EditorView {
|
|
|
802
802
|
(`view.contentDOM.getBoundingClientRect().top`) to limit layout
|
|
803
803
|
queries.
|
|
804
804
|
|
|
805
|
-
*Deprecated: use `
|
|
805
|
+
*Deprecated: use `elementAtHeight` instead.*
|
|
806
806
|
*/
|
|
807
807
|
blockAtHeight(height: number, docTop?: number): BlockInfo;
|
|
808
808
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MapMode, Text as Text$1, Facet, StateEffect, ChangeSet, EditorSelection,
|
|
1
|
+
import { MapMode, Text as Text$1, Facet, StateEffect, ChangeSet, EditorSelection, EditorState, CharCategory, Transaction, Prec, combineConfig, StateField } from '@codemirror/state';
|
|
2
2
|
import { StyleModule } from 'style-mod';
|
|
3
3
|
import { RangeSet, RangeValue, RangeSetBuilder } from '@codemirror/rangeset';
|
|
4
4
|
export { Range } from '@codemirror/rangeset';
|
|
@@ -599,9 +599,8 @@ function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) {
|
|
|
599
599
|
replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd);
|
|
600
600
|
}
|
|
601
601
|
|
|
602
|
-
let
|
|
603
|
-
|
|
604
|
-
: [{ userAgent: "", vendor: "", platform: "" }, { documentElement: { style: {} } }];
|
|
602
|
+
let nav = typeof navigator != "undefined" ? navigator : { userAgent: "", vendor: "", platform: "" };
|
|
603
|
+
let doc = typeof document != "undefined" ? document : { documentElement: { style: {} } };
|
|
605
604
|
const ie_edge = /*@__PURE__*//Edge\/(\d+)/.exec(nav.userAgent);
|
|
606
605
|
const ie_upto10 = /*@__PURE__*//MSIE \d/.test(nav.userAgent);
|
|
607
606
|
const ie_11up = /*@__PURE__*//Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(nav.userAgent);
|
|
@@ -857,9 +856,6 @@ class CompositionView extends WidgetView {
|
|
|
857
856
|
coordsAt(pos, side) { return textCoords(this.widget.text, pos, side); }
|
|
858
857
|
get isEditable() { return true; }
|
|
859
858
|
}
|
|
860
|
-
// Use two characters on Android, to prevent Chrome from closing the
|
|
861
|
-
// virtual keyboard when backspacing after a widget (#602).
|
|
862
|
-
const ZeroWidthSpace = browser.android ? "\u200b\u200b" : "\u200b";
|
|
863
859
|
// These are drawn around uneditable widgets to avoid a number of
|
|
864
860
|
// browser bugs that show up when the cursor is directly next to
|
|
865
861
|
// uneditable inline content.
|
|
@@ -875,21 +871,21 @@ class WidgetBufferView extends ContentView {
|
|
|
875
871
|
}
|
|
876
872
|
split() { return new WidgetBufferView(this.side); }
|
|
877
873
|
sync() {
|
|
878
|
-
if (!this.dom)
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
this.dom
|
|
874
|
+
if (!this.dom) {
|
|
875
|
+
let dom = document.createElement("img");
|
|
876
|
+
dom.className = "cm-widgetBuffer";
|
|
877
|
+
this.setDOM(dom);
|
|
878
|
+
}
|
|
882
879
|
}
|
|
883
880
|
getSide() { return this.side; }
|
|
884
881
|
domAtPos(pos) { return DOMPos.before(this.dom); }
|
|
885
882
|
localPosFromDOM() { return 0; }
|
|
886
883
|
domBoundsAround() { return null; }
|
|
887
884
|
coordsAt(pos) {
|
|
888
|
-
|
|
889
|
-
return rects[rects.length - 1] || null;
|
|
885
|
+
return this.dom.getBoundingClientRect();
|
|
890
886
|
}
|
|
891
887
|
get overrideDOMText() {
|
|
892
|
-
return Text.
|
|
888
|
+
return Text.empty;
|
|
893
889
|
}
|
|
894
890
|
}
|
|
895
891
|
TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren;
|
|
@@ -1127,8 +1123,8 @@ class Decoration extends RangeValue {
|
|
|
1127
1123
|
static replace(spec) {
|
|
1128
1124
|
let block = !!spec.block;
|
|
1129
1125
|
let { start, end } = getInclusive(spec, block);
|
|
1130
|
-
let startSide =
|
|
1131
|
-
let endSide =
|
|
1126
|
+
let startSide = (start ? (block ? -300000000 /* BlockIncStart */ : -1 /* InlineIncStart */) : 400000000 /* NonIncStart */) - 1;
|
|
1127
|
+
let endSide = (end ? (block ? 200000000 /* BlockIncEnd */ : 1 /* InlineIncEnd */) : -500000000 /* NonIncEnd */) + 1;
|
|
1132
1128
|
return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
|
|
1133
1129
|
}
|
|
1134
1130
|
/**
|
|
@@ -1233,7 +1229,7 @@ function widgetsEq(a, b) {
|
|
|
1233
1229
|
}
|
|
1234
1230
|
function addRange(from, to, ranges, margin = 0) {
|
|
1235
1231
|
let last = ranges.length - 1;
|
|
1236
|
-
if (last >= 0 && ranges[last] + margin
|
|
1232
|
+
if (last >= 0 && ranges[last] + margin >= from)
|
|
1237
1233
|
ranges[last] = Math.max(ranges[last], to);
|
|
1238
1234
|
else
|
|
1239
1235
|
ranges.push(from, to);
|
|
@@ -1514,7 +1510,7 @@ class ContentBuilder {
|
|
|
1514
1510
|
}
|
|
1515
1511
|
}
|
|
1516
1512
|
let take = Math.min(this.text.length - this.textOff, length, 512 /* Chunk */);
|
|
1517
|
-
this.flushBuffer(active);
|
|
1513
|
+
this.flushBuffer(active.slice(0, openStart));
|
|
1518
1514
|
this.getLine().append(wrapMarks(new TextView(this.text.slice(this.textOff, this.textOff + take)), active), openStart);
|
|
1519
1515
|
this.atCursorPos = true;
|
|
1520
1516
|
this.textOff += take;
|
|
@@ -1573,11 +1569,13 @@ class ContentBuilder {
|
|
|
1573
1569
|
this.openStart = openStart;
|
|
1574
1570
|
}
|
|
1575
1571
|
filterPoint(from, to, value, index) {
|
|
1576
|
-
if (index
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1572
|
+
if (index < this.disallowBlockEffectsBelow && value instanceof PointDecoration) {
|
|
1573
|
+
if (value.block)
|
|
1574
|
+
throw new RangeError("Block decorations may not be specified via plugins");
|
|
1575
|
+
if (to > this.doc.lineAt(this.pos).to)
|
|
1576
|
+
throw new RangeError("Decorations that replace line breaks may not be specified via plugins");
|
|
1577
|
+
}
|
|
1578
|
+
return true;
|
|
1581
1579
|
}
|
|
1582
1580
|
static build(text, from, to, decorations, pluginDecorationLength) {
|
|
1583
1581
|
let builder = new ContentBuilder(text, from, to, pluginDecorationLength);
|
|
@@ -2300,12 +2298,18 @@ function moveVisually(line, order, dir, start, forward) {
|
|
|
2300
2298
|
return EditorSelection.cursor(nextIndex + line.from, forward ? -1 : 1, span.level);
|
|
2301
2299
|
}
|
|
2302
2300
|
|
|
2301
|
+
const LineBreakPlaceholder = "\uffff";
|
|
2303
2302
|
class DOMReader {
|
|
2304
|
-
constructor(points,
|
|
2303
|
+
constructor(points, state) {
|
|
2305
2304
|
this.points = points;
|
|
2306
|
-
this.view = view;
|
|
2307
2305
|
this.text = "";
|
|
2308
|
-
this.
|
|
2306
|
+
this.lineSeparator = state.facet(EditorState.lineSeparator);
|
|
2307
|
+
}
|
|
2308
|
+
append(text) {
|
|
2309
|
+
this.text += text;
|
|
2310
|
+
}
|
|
2311
|
+
lineBreak() {
|
|
2312
|
+
this.text += LineBreakPlaceholder;
|
|
2309
2313
|
}
|
|
2310
2314
|
readRange(start, end) {
|
|
2311
2315
|
if (!start)
|
|
@@ -2321,33 +2325,61 @@ class DOMReader {
|
|
|
2321
2325
|
if (view && nextView ? view.breakAfter :
|
|
2322
2326
|
(view ? view.breakAfter : isBlockElement(cur)) ||
|
|
2323
2327
|
(isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore)))
|
|
2324
|
-
this.
|
|
2328
|
+
this.lineBreak();
|
|
2325
2329
|
cur = next;
|
|
2326
2330
|
}
|
|
2327
2331
|
this.findPointBefore(parent, end);
|
|
2328
2332
|
return this;
|
|
2329
2333
|
}
|
|
2334
|
+
readTextNode(node) {
|
|
2335
|
+
let text = node.nodeValue;
|
|
2336
|
+
for (let point of this.points)
|
|
2337
|
+
if (point.node == node)
|
|
2338
|
+
point.pos = this.text.length + Math.min(point.offset, text.length);
|
|
2339
|
+
for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
|
|
2340
|
+
let nextBreak = -1, breakSize = 1, m;
|
|
2341
|
+
if (this.lineSeparator) {
|
|
2342
|
+
nextBreak = text.indexOf(this.lineSeparator, off);
|
|
2343
|
+
breakSize = this.lineSeparator.length;
|
|
2344
|
+
}
|
|
2345
|
+
else if (m = re.exec(text)) {
|
|
2346
|
+
nextBreak = m.index;
|
|
2347
|
+
breakSize = m[0].length;
|
|
2348
|
+
}
|
|
2349
|
+
this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
|
|
2350
|
+
if (nextBreak < 0)
|
|
2351
|
+
break;
|
|
2352
|
+
this.lineBreak();
|
|
2353
|
+
if (breakSize > 1)
|
|
2354
|
+
for (let point of this.points)
|
|
2355
|
+
if (point.node == node && point.pos > this.text.length)
|
|
2356
|
+
point.pos -= breakSize - 1;
|
|
2357
|
+
off = nextBreak + breakSize;
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2330
2360
|
readNode(node) {
|
|
2331
2361
|
if (node.cmIgnore)
|
|
2332
2362
|
return;
|
|
2333
2363
|
let view = ContentView.get(node);
|
|
2334
2364
|
let fromView = view && view.overrideDOMText;
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2365
|
+
if (fromView != null) {
|
|
2366
|
+
this.findPointInside(node, fromView.length);
|
|
2367
|
+
for (let i = fromView.iter(); !i.next().done;) {
|
|
2368
|
+
if (i.lineBreak)
|
|
2369
|
+
this.lineBreak();
|
|
2370
|
+
else
|
|
2371
|
+
this.append(i.value);
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
else if (node.nodeType == 3) {
|
|
2375
|
+
this.readTextNode(node);
|
|
2376
|
+
}
|
|
2377
|
+
else if (node.nodeName == "BR") {
|
|
2378
|
+
if (node.nextSibling)
|
|
2379
|
+
this.lineBreak();
|
|
2380
|
+
}
|
|
2381
|
+
else if (node.nodeType == 1) {
|
|
2343
2382
|
this.readRange(node.firstChild, null);
|
|
2344
|
-
if (text != null) {
|
|
2345
|
-
this.findPointIn(node, text.length);
|
|
2346
|
-
this.text += text;
|
|
2347
|
-
// Chrome inserts two newlines when pressing shift-enter at the
|
|
2348
|
-
// end of a line. This drops one of those.
|
|
2349
|
-
if (browser.chrome && this.view.inputState.lastKeyCode == 13 && !node.nextSibling && /\n\n$/.test(this.text))
|
|
2350
|
-
this.text = this.text.slice(0, -1);
|
|
2351
2383
|
}
|
|
2352
2384
|
}
|
|
2353
2385
|
findPointBefore(node, next) {
|
|
@@ -2355,10 +2387,10 @@ class DOMReader {
|
|
|
2355
2387
|
if (point.node == node && node.childNodes[point.offset] == next)
|
|
2356
2388
|
point.pos = this.text.length;
|
|
2357
2389
|
}
|
|
2358
|
-
|
|
2390
|
+
findPointInside(node, maxLen) {
|
|
2359
2391
|
for (let point of this.points)
|
|
2360
|
-
if (point.node == node)
|
|
2361
|
-
point.pos = this.text.length + Math.min(point.offset
|
|
2392
|
+
if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
|
|
2393
|
+
point.pos = this.text.length + Math.min(maxLen, point.offset);
|
|
2362
2394
|
}
|
|
2363
2395
|
}
|
|
2364
2396
|
function isBlockElement(node) {
|
|
@@ -2763,51 +2795,57 @@ class BlockGapWidget extends WidgetType {
|
|
|
2763
2795
|
}
|
|
2764
2796
|
get estimatedHeight() { return this.height; }
|
|
2765
2797
|
}
|
|
2766
|
-
function
|
|
2798
|
+
function compositionSurroundingNode(view) {
|
|
2767
2799
|
let sel = view.observer.selectionRange;
|
|
2768
2800
|
let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
|
|
2769
2801
|
if (!textNode)
|
|
2770
|
-
return
|
|
2802
|
+
return null;
|
|
2771
2803
|
let cView = view.docView.nearest(textNode);
|
|
2772
2804
|
if (!cView)
|
|
2773
|
-
return
|
|
2774
|
-
let from, to, topNode = textNode;
|
|
2805
|
+
return null;
|
|
2775
2806
|
if (cView instanceof LineView) {
|
|
2807
|
+
let topNode = textNode;
|
|
2776
2808
|
while (topNode.parentNode != cView.dom)
|
|
2777
2809
|
topNode = topNode.parentNode;
|
|
2778
2810
|
let prev = topNode.previousSibling;
|
|
2779
2811
|
while (prev && !ContentView.get(prev))
|
|
2780
2812
|
prev = prev.previousSibling;
|
|
2781
|
-
|
|
2813
|
+
let pos = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
|
|
2814
|
+
return { from: pos, to: pos, node: topNode, text: textNode };
|
|
2782
2815
|
}
|
|
2783
2816
|
else {
|
|
2784
2817
|
for (;;) {
|
|
2785
2818
|
let { parent } = cView;
|
|
2786
2819
|
if (!parent)
|
|
2787
|
-
return
|
|
2820
|
+
return null;
|
|
2788
2821
|
if (parent instanceof LineView)
|
|
2789
2822
|
break;
|
|
2790
2823
|
cView = parent;
|
|
2791
2824
|
}
|
|
2792
|
-
from = cView.posAtStart;
|
|
2793
|
-
|
|
2794
|
-
topNode = cView.dom;
|
|
2825
|
+
let from = cView.posAtStart;
|
|
2826
|
+
return { from, to: from + cView.length, node: cView.dom, text: textNode };
|
|
2795
2827
|
}
|
|
2828
|
+
}
|
|
2829
|
+
function computeCompositionDeco(view, changes) {
|
|
2830
|
+
let surrounding = compositionSurroundingNode(view);
|
|
2831
|
+
if (!surrounding)
|
|
2832
|
+
return Decoration.none;
|
|
2833
|
+
let { from, to, node, text: textNode } = surrounding;
|
|
2796
2834
|
let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
|
|
2797
|
-
let { state } = view, text =
|
|
2798
|
-
new DOMReader([],
|
|
2835
|
+
let { state } = view, text = node.nodeType == 3 ? node.nodeValue :
|
|
2836
|
+
new DOMReader([], state).readRange(node.firstChild, null).text;
|
|
2799
2837
|
if (newTo - newFrom < text.length) {
|
|
2800
|
-
if (state.
|
|
2838
|
+
if (state.doc.sliceString(newFrom, Math.min(state.doc.length, newFrom + text.length), LineBreakPlaceholder) == text)
|
|
2801
2839
|
newTo = newFrom + text.length;
|
|
2802
|
-
else if (state.
|
|
2840
|
+
else if (state.doc.sliceString(Math.max(0, newTo - text.length), newTo, LineBreakPlaceholder) == text)
|
|
2803
2841
|
newFrom = newTo - text.length;
|
|
2804
2842
|
else
|
|
2805
2843
|
return Decoration.none;
|
|
2806
2844
|
}
|
|
2807
|
-
else if (state.
|
|
2845
|
+
else if (state.doc.sliceString(newFrom, newTo, LineBreakPlaceholder) != text) {
|
|
2808
2846
|
return Decoration.none;
|
|
2809
2847
|
}
|
|
2810
|
-
return Decoration.set(Decoration.replace({ widget: new CompositionWidget(
|
|
2848
|
+
return Decoration.set(Decoration.replace({ widget: new CompositionWidget(node, textNode) }).range(newFrom, newTo));
|
|
2811
2849
|
}
|
|
2812
2850
|
class CompositionWidget extends WidgetType {
|
|
2813
2851
|
constructor(top, text) {
|
|
@@ -3144,7 +3182,7 @@ function byGroup(view, pos, start) {
|
|
|
3144
3182
|
function moveVertically(view, start, forward, distance) {
|
|
3145
3183
|
let startPos = start.head, dir = forward ? 1 : -1;
|
|
3146
3184
|
if (startPos == (forward ? view.state.doc.length : 0))
|
|
3147
|
-
return EditorSelection.cursor(startPos);
|
|
3185
|
+
return EditorSelection.cursor(startPos, start.assoc);
|
|
3148
3186
|
let goal = start.goalColumn, startY;
|
|
3149
3187
|
let rect = view.contentDOM.getBoundingClientRect();
|
|
3150
3188
|
let startCoords = view.coordsAtPos(startPos), docTop = view.documentTop;
|
|
@@ -3165,7 +3203,7 @@ function moveVertically(view, start, forward, distance) {
|
|
|
3165
3203
|
let curY = startY + (dist + extra) * dir;
|
|
3166
3204
|
let pos = posAtCoords(view, { x: resolvedGoal, y: curY }, false, dir);
|
|
3167
3205
|
if (curY < rect.top || curY > rect.bottom || (dir < 0 ? pos < startPos : pos > startPos))
|
|
3168
|
-
return EditorSelection.cursor(pos,
|
|
3206
|
+
return EditorSelection.cursor(pos, start.assoc, undefined, goal);
|
|
3169
3207
|
}
|
|
3170
3208
|
}
|
|
3171
3209
|
function skipAtoms(view, oldPos, pos) {
|
|
@@ -4676,6 +4714,12 @@ class ViewState {
|
|
|
4676
4714
|
let refresh = this.heightOracle.mustRefreshForStyle(whiteSpace, direction);
|
|
4677
4715
|
let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != dom.clientHeight;
|
|
4678
4716
|
let result = 0, bias = 0;
|
|
4717
|
+
if (this.editorWidth != view.scrollDOM.clientWidth) {
|
|
4718
|
+
if (oracle.lineWrapping)
|
|
4719
|
+
measureContent = true;
|
|
4720
|
+
this.editorWidth = view.scrollDOM.clientWidth;
|
|
4721
|
+
result |= 8 /* Geometry */;
|
|
4722
|
+
}
|
|
4679
4723
|
if (measureContent) {
|
|
4680
4724
|
this.mustMeasureContent = false;
|
|
4681
4725
|
this.contentDOMHeight = dom.clientHeight;
|
|
@@ -4688,7 +4732,8 @@ class ViewState {
|
|
|
4688
4732
|
}
|
|
4689
4733
|
}
|
|
4690
4734
|
// Pixel viewport
|
|
4691
|
-
let pixelViewport = this.printing ? { top: -1e8, bottom: 1e8, left: -1e8, right: 1e8 }
|
|
4735
|
+
let pixelViewport = this.printing ? { top: -1e8, bottom: 1e8, left: -1e8, right: 1e8 }
|
|
4736
|
+
: visiblePixelRange(dom, this.paddingTop);
|
|
4692
4737
|
let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
|
|
4693
4738
|
this.pixelViewport = pixelViewport;
|
|
4694
4739
|
let inView = this.pixelViewport.bottom > this.pixelViewport.top && this.pixelViewport.right > this.pixelViewport.left;
|
|
@@ -4700,11 +4745,9 @@ class ViewState {
|
|
|
4700
4745
|
if (!this.inView)
|
|
4701
4746
|
return 0;
|
|
4702
4747
|
let contentWidth = dom.clientWidth;
|
|
4703
|
-
if (this.contentDOMWidth != contentWidth || this.editorHeight != view.scrollDOM.clientHeight
|
|
4704
|
-
this.editorWidth != view.scrollDOM.clientWidth) {
|
|
4748
|
+
if (this.contentDOMWidth != contentWidth || this.editorHeight != view.scrollDOM.clientHeight) {
|
|
4705
4749
|
this.contentDOMWidth = contentWidth;
|
|
4706
4750
|
this.editorHeight = view.scrollDOM.clientHeight;
|
|
4707
|
-
this.editorWidth = view.scrollDOM.clientWidth;
|
|
4708
4751
|
result |= 8 /* Geometry */;
|
|
4709
4752
|
}
|
|
4710
4753
|
if (measureContent) {
|
|
@@ -4759,8 +4802,9 @@ class ViewState {
|
|
|
4759
4802
|
let viewport = new Viewport(map.lineAt(visibleTop - marginTop * 1000 /* Margin */, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(visibleBottom + (1 - marginTop) * 1000 /* Margin */, QueryType.ByHeight, doc, 0, 0).to);
|
|
4760
4803
|
// If scrollTarget is given, make sure the viewport includes that position
|
|
4761
4804
|
if (scrollTarget) {
|
|
4762
|
-
let { head } = scrollTarget.range
|
|
4805
|
+
let { head } = scrollTarget.range;
|
|
4763
4806
|
if (head < viewport.from || head > viewport.to) {
|
|
4807
|
+
let viewHeight = Math.min(this.editorHeight, this.pixelViewport.bottom - this.pixelViewport.top);
|
|
4764
4808
|
let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
|
|
4765
4809
|
if (scrollTarget.y == "center")
|
|
4766
4810
|
topPos = (block.top + block.bottom) / 2 - viewHeight / 2;
|
|
@@ -5146,6 +5190,10 @@ const baseTheme = /*@__PURE__*/buildTheme("." + baseThemeID, {
|
|
|
5146
5190
|
overflow: "hidden",
|
|
5147
5191
|
verticalAlign: "bottom"
|
|
5148
5192
|
},
|
|
5193
|
+
".cm-widgetBuffer": {
|
|
5194
|
+
verticalAlign: "text-bottom",
|
|
5195
|
+
height: "1em",
|
|
5196
|
+
},
|
|
5149
5197
|
".cm-placeholder": {
|
|
5150
5198
|
color: "#888",
|
|
5151
5199
|
display: "inline-block",
|
|
@@ -5394,7 +5442,7 @@ class DOMObserver {
|
|
|
5394
5442
|
}
|
|
5395
5443
|
// Throw away any pending changes
|
|
5396
5444
|
clear() {
|
|
5397
|
-
this.
|
|
5445
|
+
this.processRecords();
|
|
5398
5446
|
this.queue.length = 0;
|
|
5399
5447
|
this.selectionChanged = false;
|
|
5400
5448
|
}
|
|
@@ -5560,9 +5608,8 @@ function applyDOMChange(view, start, end, typeOver) {
|
|
|
5560
5608
|
return;
|
|
5561
5609
|
let { from, to } = bounds;
|
|
5562
5610
|
let selPoints = view.docView.impreciseHead || view.docView.impreciseAnchor ? [] : selectionPoints(view);
|
|
5563
|
-
let reader = new DOMReader(selPoints, view);
|
|
5611
|
+
let reader = new DOMReader(selPoints, view.state);
|
|
5564
5612
|
reader.readRange(bounds.startDOM, bounds.endDOM);
|
|
5565
|
-
newSel = selectionFromPoints(selPoints, from);
|
|
5566
5613
|
let preferredPos = sel.from, preferredSide = null;
|
|
5567
5614
|
// Prefer anchoring to end when Backspace is pressed (or, on
|
|
5568
5615
|
// Android, when something was deleted)
|
|
@@ -5571,10 +5618,17 @@ function applyDOMChange(view, start, end, typeOver) {
|
|
|
5571
5618
|
preferredPos = sel.to;
|
|
5572
5619
|
preferredSide = "end";
|
|
5573
5620
|
}
|
|
5574
|
-
let diff = findDiff(view.state.
|
|
5575
|
-
if (diff)
|
|
5621
|
+
let diff = findDiff(view.state.doc.sliceString(from, to, LineBreakPlaceholder), reader.text, preferredPos - from, preferredSide);
|
|
5622
|
+
if (diff) {
|
|
5623
|
+
// Chrome inserts two newlines when pressing shift-enter at the
|
|
5624
|
+
// end of a line. This drops one of those.
|
|
5625
|
+
if (browser.chrome && view.inputState.lastKeyCode == 13 &&
|
|
5626
|
+
diff.toB == diff.from + 2 && reader.text.slice(diff.from, diff.toB) == LineBreakPlaceholder + LineBreakPlaceholder)
|
|
5627
|
+
diff.toB--;
|
|
5576
5628
|
change = { from: from + diff.from, to: from + diff.toA,
|
|
5577
|
-
insert:
|
|
5629
|
+
insert: Text$1.of(reader.text.slice(diff.from, diff.toB).split(LineBreakPlaceholder)) };
|
|
5630
|
+
}
|
|
5631
|
+
newSel = selectionFromPoints(selPoints, from);
|
|
5578
5632
|
}
|
|
5579
5633
|
else if (view.hasFocus || !view.state.facet(editable)) {
|
|
5580
5634
|
let domSel = view.observer.selectionRange;
|
|
@@ -5631,19 +5685,47 @@ function applyDOMChange(view, start, end, typeOver) {
|
|
|
5631
5685
|
view.inputState.composing++;
|
|
5632
5686
|
let tr;
|
|
5633
5687
|
if (change.from >= sel.from && change.to <= sel.to && change.to - change.from >= (sel.to - sel.from) / 3 &&
|
|
5634
|
-
(!newSel || newSel.main.empty && newSel.main.from == change.from + change.insert.length)
|
|
5688
|
+
(!newSel || newSel.main.empty && newSel.main.from == change.from + change.insert.length) &&
|
|
5689
|
+
view.inputState.composing < 0) {
|
|
5635
5690
|
let before = sel.from < change.from ? startState.sliceDoc(sel.from, change.from) : "";
|
|
5636
5691
|
let after = sel.to > change.to ? startState.sliceDoc(change.to, sel.to) : "";
|
|
5637
|
-
tr = startState.replaceSelection(view.state.toText(before + change.insert.sliceString(0, undefined, view.state.lineBreak) +
|
|
5638
|
-
after));
|
|
5692
|
+
tr = startState.replaceSelection(view.state.toText(before + change.insert.sliceString(0, undefined, view.state.lineBreak) + after));
|
|
5639
5693
|
}
|
|
5640
5694
|
else {
|
|
5641
5695
|
let changes = startState.changes(change);
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5696
|
+
let mainSel = newSel && !startState.selection.main.eq(newSel.main) && newSel.main.to <= changes.newLength
|
|
5697
|
+
? newSel.main : undefined;
|
|
5698
|
+
// Try to apply a composition change to all cursors
|
|
5699
|
+
if (startState.selection.ranges.length > 1 && view.inputState.composing >= 0 &&
|
|
5700
|
+
change.to <= sel.to && change.to >= sel.to - 10) {
|
|
5701
|
+
let replaced = view.state.sliceDoc(change.from, change.to);
|
|
5702
|
+
let compositionRange = compositionSurroundingNode(view) || view.state.doc.lineAt(sel.head);
|
|
5703
|
+
let offset = sel.to - change.to, size = sel.to - sel.from;
|
|
5704
|
+
tr = startState.changeByRange(range => {
|
|
5705
|
+
if (range.from == sel.from && range.to == sel.to)
|
|
5706
|
+
return { changes, range: mainSel || range.map(changes) };
|
|
5707
|
+
let to = range.to - offset, from = to - replaced.length;
|
|
5708
|
+
if (range.to - range.from != size || view.state.sliceDoc(from, to) != replaced ||
|
|
5709
|
+
// Unfortunately, there's no way to make multiple
|
|
5710
|
+
// changes in the same node work without aborting
|
|
5711
|
+
// composition, so cursors in the composition range are
|
|
5712
|
+
// ignored.
|
|
5713
|
+
compositionRange && range.to >= compositionRange.from && range.from <= compositionRange.to)
|
|
5714
|
+
return { range };
|
|
5715
|
+
let rangeChanges = startState.changes({ from, to, insert: change.insert }), selOff = range.to - sel.to;
|
|
5716
|
+
return {
|
|
5717
|
+
changes: rangeChanges,
|
|
5718
|
+
range: !mainSel ? range.map(rangeChanges) :
|
|
5719
|
+
EditorSelection.range(Math.max(0, mainSel.anchor + selOff), Math.max(0, mainSel.head + selOff))
|
|
5720
|
+
};
|
|
5721
|
+
});
|
|
5722
|
+
}
|
|
5723
|
+
else {
|
|
5724
|
+
tr = {
|
|
5725
|
+
changes,
|
|
5726
|
+
selection: mainSel && startState.selection.replaceRange(mainSel)
|
|
5727
|
+
};
|
|
5728
|
+
}
|
|
5647
5729
|
}
|
|
5648
5730
|
let userEvent = "input.type";
|
|
5649
5731
|
if (view.composing) {
|
|
@@ -5893,7 +5975,9 @@ class EditorView {
|
|
|
5893
5975
|
finally {
|
|
5894
5976
|
this.updateState = 0 /* Idle */;
|
|
5895
5977
|
}
|
|
5896
|
-
if (
|
|
5978
|
+
if (update.startState.facet(theme) != update.state.facet(theme))
|
|
5979
|
+
this.viewState.mustMeasureContent = true;
|
|
5980
|
+
if (redrawn || scrollTarget || this.viewState.mustEnforceCursorAssoc || this.viewState.mustMeasureContent)
|
|
5897
5981
|
this.requestMeasure();
|
|
5898
5982
|
if (!update.empty)
|
|
5899
5983
|
for (let listener of this.state.facet(updateListener))
|
|
@@ -5914,6 +5998,7 @@ class EditorView {
|
|
|
5914
5998
|
return;
|
|
5915
5999
|
}
|
|
5916
6000
|
this.updateState = 2 /* Updating */;
|
|
6001
|
+
let hadFocus = this.hasFocus;
|
|
5917
6002
|
try {
|
|
5918
6003
|
for (let plugin of this.plugins)
|
|
5919
6004
|
plugin.destroy(this);
|
|
@@ -5931,6 +6016,8 @@ class EditorView {
|
|
|
5931
6016
|
finally {
|
|
5932
6017
|
this.updateState = 0 /* Idle */;
|
|
5933
6018
|
}
|
|
6019
|
+
if (hadFocus)
|
|
6020
|
+
this.focus();
|
|
5934
6021
|
this.requestMeasure();
|
|
5935
6022
|
}
|
|
5936
6023
|
updatePlugins(update) {
|
|
@@ -6000,7 +6087,7 @@ class EditorView {
|
|
|
6000
6087
|
return BadMeasure;
|
|
6001
6088
|
}
|
|
6002
6089
|
});
|
|
6003
|
-
let update = new ViewUpdate(this, this.state), redrawn = false;
|
|
6090
|
+
let update = new ViewUpdate(this, this.state), redrawn = false, scrolled = false;
|
|
6004
6091
|
update.flags |= changed;
|
|
6005
6092
|
if (!updated)
|
|
6006
6093
|
updated = update;
|
|
@@ -6027,11 +6114,12 @@ class EditorView {
|
|
|
6027
6114
|
if (this.viewState.scrollTarget) {
|
|
6028
6115
|
this.docView.scrollIntoView(this.viewState.scrollTarget);
|
|
6029
6116
|
this.viewState.scrollTarget = null;
|
|
6117
|
+
scrolled = true;
|
|
6030
6118
|
}
|
|
6031
6119
|
if (redrawn)
|
|
6032
6120
|
this.docView.updateSelection(true);
|
|
6033
6121
|
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
|
|
6034
|
-
this.measureRequests.length == 0)
|
|
6122
|
+
!scrolled && this.measureRequests.length == 0)
|
|
6035
6123
|
break;
|
|
6036
6124
|
}
|
|
6037
6125
|
}
|
|
@@ -6167,7 +6255,7 @@ class EditorView {
|
|
|
6167
6255
|
(`view.contentDOM.getBoundingClientRect().top`) to limit layout
|
|
6168
6256
|
queries.
|
|
6169
6257
|
|
|
6170
|
-
*Deprecated: use `
|
|
6258
|
+
*Deprecated: use `elementAtHeight` instead.*
|
|
6171
6259
|
*/
|
|
6172
6260
|
blockAtHeight(height, docTop) {
|
|
6173
6261
|
let top = ensureTop(docTop, this);
|
|
@@ -7012,7 +7100,7 @@ function measureRange(view, range) {
|
|
|
7012
7100
|
return pieces(top).concat(between).concat(pieces(bottom));
|
|
7013
7101
|
}
|
|
7014
7102
|
function piece(left, top, right, bottom) {
|
|
7015
|
-
return new Piece(left - base.left, top - base.top
|
|
7103
|
+
return new Piece(left - base.left, top - base.top - 0.01 /* Epsilon */, right - left, bottom - top + 0.01 /* Epsilon */, "cm-selectionBackground");
|
|
7016
7104
|
}
|
|
7017
7105
|
function pieces({ top, bottom, horizontal }) {
|
|
7018
7106
|
let pieces = [];
|