@codemirror/view 6.24.0 → 6.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/dist/index.cjs +80 -13
- package/dist/index.d.cts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +80 -13
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
## 6.25.0 (2024-03-04)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Properly recognize Android GBoard enter presses that strip a space at the end of the line as enter.
|
|
6
|
+
|
|
7
|
+
Fix a bug that caused the gutter to have the wrong height when the editor was scaled after construction.
|
|
8
|
+
|
|
9
|
+
When starting a composition after a non-inclusive mark decoration, temporarily insert a widget that prevents the composed text from inheriting that mark's styles.
|
|
10
|
+
|
|
11
|
+
Make sure the selection is repositioned when a transaction changes decorations without changing the document.
|
|
12
|
+
|
|
13
|
+
### New features
|
|
14
|
+
|
|
15
|
+
View plugins can now provide a `docViewUpdate` method that is called whenever the document view is updated.
|
|
16
|
+
|
|
17
|
+
Layers now take a `updateOnDocUpdate` option that controls whether they are automatically updated when the document view changes.
|
|
18
|
+
|
|
19
|
+
## 6.24.1 (2024-02-19)
|
|
20
|
+
|
|
21
|
+
### Bug fixes
|
|
22
|
+
|
|
23
|
+
Fix a crash that happens when hover tooltips are active during changes, introduced in 6.24.0.
|
|
24
|
+
|
|
1
25
|
## 6.24.0 (2024-02-09)
|
|
2
26
|
|
|
3
27
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -1726,10 +1726,10 @@ class ContentBuilder {
|
|
|
1726
1726
|
if (deco.block) {
|
|
1727
1727
|
if (deco.startSide > 0 && !this.posCovered())
|
|
1728
1728
|
this.getLine();
|
|
1729
|
-
this.addBlockWidget(new BlockWidgetView(deco.widget ||
|
|
1729
|
+
this.addBlockWidget(new BlockWidgetView(deco.widget || NullWidget.block, len, deco));
|
|
1730
1730
|
}
|
|
1731
1731
|
else {
|
|
1732
|
-
let view = WidgetView.create(deco.widget ||
|
|
1732
|
+
let view = WidgetView.create(deco.widget || NullWidget.inline, len, len ? 0 : deco.startSide);
|
|
1733
1733
|
let cursorBefore = this.atCursorPos && !view.isEditable && openStart <= active.length &&
|
|
1734
1734
|
(from < to || deco.startSide > 0);
|
|
1735
1735
|
let cursorAfter = !view.isEditable && (from < to || openStart > active.length || deco.startSide <= 0);
|
|
@@ -1790,6 +1790,8 @@ class NullWidget extends WidgetType {
|
|
|
1790
1790
|
updateDOM(elt) { return elt.nodeName.toLowerCase() == this.tag; }
|
|
1791
1791
|
get isHidden() { return true; }
|
|
1792
1792
|
}
|
|
1793
|
+
NullWidget.inline = new NullWidget("span");
|
|
1794
|
+
NullWidget.block = new NullWidget("div");
|
|
1793
1795
|
|
|
1794
1796
|
/**
|
|
1795
1797
|
Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
|
|
@@ -2650,10 +2652,11 @@ class DocView extends ContentView {
|
|
|
2650
2652
|
super();
|
|
2651
2653
|
this.view = view;
|
|
2652
2654
|
this.decorations = [];
|
|
2653
|
-
this.dynamicDecorationMap = [];
|
|
2655
|
+
this.dynamicDecorationMap = [false];
|
|
2654
2656
|
this.domChanged = null;
|
|
2655
2657
|
this.hasComposition = null;
|
|
2656
2658
|
this.markedForComposition = new Set;
|
|
2659
|
+
this.compositionBarrier = Decoration.none;
|
|
2657
2660
|
// Track a minimum width for the editor. When measuring sizes in
|
|
2658
2661
|
// measureVisibleLineHeights, this is updated to point at the width
|
|
2659
2662
|
// of a given element and its extent in the document. When a change
|
|
@@ -2917,7 +2920,7 @@ class DocView extends ContentView {
|
|
|
2917
2920
|
// composition, avoid moving it across it and disrupting the
|
|
2918
2921
|
// composition.
|
|
2919
2922
|
suppressWidgetCursorChange(sel, cursor) {
|
|
2920
|
-
return this.hasComposition && cursor.empty &&
|
|
2923
|
+
return this.hasComposition && cursor.empty && !this.compositionBarrier.size &&
|
|
2921
2924
|
isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset) &&
|
|
2922
2925
|
this.posFromDOM(sel.focusNode, sel.focusOffset) == cursor.head;
|
|
2923
2926
|
}
|
|
@@ -3125,8 +3128,9 @@ class DocView extends ContentView {
|
|
|
3125
3128
|
return Decoration.set(deco);
|
|
3126
3129
|
}
|
|
3127
3130
|
updateDeco() {
|
|
3128
|
-
let
|
|
3129
|
-
|
|
3131
|
+
let i = 1;
|
|
3132
|
+
let allDeco = this.view.state.facet(decorations).map(d => {
|
|
3133
|
+
let dynamic = this.dynamicDecorationMap[i++] = typeof d == "function";
|
|
3130
3134
|
return dynamic ? d(this.view) : d;
|
|
3131
3135
|
});
|
|
3132
3136
|
let dynamicOuter = false, outerDeco = this.view.state.facet(outerDecorations).map((d, i) => {
|
|
@@ -3136,16 +3140,46 @@ class DocView extends ContentView {
|
|
|
3136
3140
|
return dynamic ? d(this.view) : d;
|
|
3137
3141
|
});
|
|
3138
3142
|
if (outerDeco.length) {
|
|
3139
|
-
this.dynamicDecorationMap[
|
|
3143
|
+
this.dynamicDecorationMap[i++] = dynamicOuter;
|
|
3140
3144
|
allDeco.push(state.RangeSet.join(outerDeco));
|
|
3141
3145
|
}
|
|
3142
|
-
|
|
3143
|
-
this.
|
|
3144
|
-
return this.decorations = [
|
|
3146
|
+
this.decorations = [
|
|
3147
|
+
this.compositionBarrier,
|
|
3145
3148
|
...allDeco,
|
|
3146
3149
|
this.computeBlockGapDeco(),
|
|
3147
3150
|
this.view.viewState.lineGapDeco
|
|
3148
3151
|
];
|
|
3152
|
+
while (i < this.decorations.length)
|
|
3153
|
+
this.dynamicDecorationMap[i++] = false;
|
|
3154
|
+
return this.decorations;
|
|
3155
|
+
}
|
|
3156
|
+
// Starting a composition will style the inserted text with the
|
|
3157
|
+
// style of the text before it, and this is only cleared when the
|
|
3158
|
+
// composition ends, because touching it before that will abort it.
|
|
3159
|
+
// This (called from compositionstart handler) tries to notice when
|
|
3160
|
+
// the cursor is after a non-inclusive mark, where the styling could
|
|
3161
|
+
// be jarring, and insert an ad-hoc widget before the cursor to
|
|
3162
|
+
// isolate it from the style before it.
|
|
3163
|
+
maybeCreateCompositionBarrier() {
|
|
3164
|
+
let { main: { head, empty } } = this.view.state.selection;
|
|
3165
|
+
if (!empty)
|
|
3166
|
+
return false;
|
|
3167
|
+
let found = null;
|
|
3168
|
+
for (let set of this.decorations) {
|
|
3169
|
+
set.between(head, head, (from, to, value) => {
|
|
3170
|
+
if (value.point)
|
|
3171
|
+
found = false;
|
|
3172
|
+
else if (value.endSide < 0 && from < head && to == head)
|
|
3173
|
+
found = true;
|
|
3174
|
+
});
|
|
3175
|
+
if (found === false)
|
|
3176
|
+
break;
|
|
3177
|
+
}
|
|
3178
|
+
this.compositionBarrier = found ? Decoration.set(compositionBarrierWidget.range(head)) : Decoration.none;
|
|
3179
|
+
return !!found;
|
|
3180
|
+
}
|
|
3181
|
+
clearCompositionBarrier() {
|
|
3182
|
+
this.compositionBarrier = Decoration.none;
|
|
3149
3183
|
}
|
|
3150
3184
|
scrollIntoView(target) {
|
|
3151
3185
|
if (target.isSnapshot) {
|
|
@@ -3170,6 +3204,7 @@ class DocView extends ContentView {
|
|
|
3170
3204
|
scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, Math.max(Math.min(target.xMargin, offsetWidth), -offsetWidth), Math.max(Math.min(target.yMargin, offsetHeight), -offsetHeight), this.view.textDirection == exports.Direction.LTR);
|
|
3171
3205
|
}
|
|
3172
3206
|
}
|
|
3207
|
+
const compositionBarrierWidget = Decoration.widget({ side: -1, widget: NullWidget.inline });
|
|
3173
3208
|
function betweenUneditable(pos) {
|
|
3174
3209
|
return pos.node.nodeType == 1 && pos.node.firstChild &&
|
|
3175
3210
|
(pos.offset == 0 || pos.node.childNodes[pos.offset - 1].contentEditable == "false") &&
|
|
@@ -4419,6 +4454,10 @@ observers.compositionstart = observers.compositionupdate = view => {
|
|
|
4419
4454
|
if (view.inputState.composing < 0) {
|
|
4420
4455
|
// FIXME possibly set a timeout to clear it again on Android
|
|
4421
4456
|
view.inputState.composing = 0;
|
|
4457
|
+
if (view.docView.maybeCreateCompositionBarrier()) {
|
|
4458
|
+
view.update([]);
|
|
4459
|
+
view.docView.clearCompositionBarrier();
|
|
4460
|
+
}
|
|
4422
4461
|
}
|
|
4423
4462
|
};
|
|
4424
4463
|
observers.compositionend = view => {
|
|
@@ -6298,7 +6337,10 @@ function applyDOMChange(view, domChange) {
|
|
|
6298
6337
|
// events and the pendingAndroidKey mechanism, but that's not
|
|
6299
6338
|
// reliable in all situations.)
|
|
6300
6339
|
if (browser.android &&
|
|
6301
|
-
((change.
|
|
6340
|
+
((change.to == sel.to &&
|
|
6341
|
+
// GBoard will sometimes remove a space it just inserted
|
|
6342
|
+
// after a completion when you press enter
|
|
6343
|
+
(change.from == sel.from || change.from == sel.from - 1 && view.state.sliceDoc(change.from, sel.from) == " ") &&
|
|
6302
6344
|
change.insert.length == 1 && change.insert.lines == 2 &&
|
|
6303
6345
|
dispatchKey(view.contentDOM, "Enter", 13)) ||
|
|
6304
6346
|
((change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 ||
|
|
@@ -7118,6 +7160,8 @@ class EditorView {
|
|
|
7118
7160
|
this.viewState.mustMeasureContent = true;
|
|
7119
7161
|
if (redrawn || attrsChanged || scrollTarget || this.viewState.mustEnforceCursorAssoc || this.viewState.mustMeasureContent)
|
|
7120
7162
|
this.requestMeasure();
|
|
7163
|
+
if (redrawn)
|
|
7164
|
+
this.docViewUpdate();
|
|
7121
7165
|
if (!update.empty)
|
|
7122
7166
|
for (let listener of this.state.facet(updateListener)) {
|
|
7123
7167
|
try {
|
|
@@ -7205,6 +7249,19 @@ class EditorView {
|
|
|
7205
7249
|
if (prevSpecs != specs)
|
|
7206
7250
|
this.inputState.ensureHandlers(this.plugins);
|
|
7207
7251
|
}
|
|
7252
|
+
docViewUpdate() {
|
|
7253
|
+
for (let plugin of this.plugins) {
|
|
7254
|
+
let val = plugin.value;
|
|
7255
|
+
if (val && val.docViewUpdate) {
|
|
7256
|
+
try {
|
|
7257
|
+
val.docViewUpdate(this);
|
|
7258
|
+
}
|
|
7259
|
+
catch (e) {
|
|
7260
|
+
logException(this.state, e, "doc view update listener");
|
|
7261
|
+
}
|
|
7262
|
+
}
|
|
7263
|
+
}
|
|
7264
|
+
}
|
|
7208
7265
|
/**
|
|
7209
7266
|
@internal
|
|
7210
7267
|
*/
|
|
@@ -7275,6 +7332,8 @@ class EditorView {
|
|
|
7275
7332
|
this.inputState.update(update);
|
|
7276
7333
|
this.updateAttrs();
|
|
7277
7334
|
redrawn = this.docView.update(update);
|
|
7335
|
+
if (redrawn)
|
|
7336
|
+
this.docViewUpdate();
|
|
7278
7337
|
}
|
|
7279
7338
|
for (let i = 0; i < measuring.length; i++)
|
|
7280
7339
|
if (measured[i] != BadMeasure) {
|
|
@@ -8423,6 +8482,10 @@ class LayerView {
|
|
|
8423
8482
|
update.view.requestMeasure(this.measureReq);
|
|
8424
8483
|
}
|
|
8425
8484
|
}
|
|
8485
|
+
docViewUpdate(view) {
|
|
8486
|
+
if (this.layer.updateOnDocViewUpdate !== false)
|
|
8487
|
+
view.requestMeasure(this.measureReq);
|
|
8488
|
+
}
|
|
8426
8489
|
setOrder(state) {
|
|
8427
8490
|
let pos = 0, order = state.facet(layerOrder);
|
|
8428
8491
|
while (pos < order.length && order[pos] != this.layer)
|
|
@@ -9811,6 +9874,7 @@ function hoverTooltip(source, options = {}) {
|
|
|
9811
9874
|
else if (options.hideOn)
|
|
9812
9875
|
value = value.filter(v => !options.hideOn(tr, v));
|
|
9813
9876
|
if (tr.docChanged) {
|
|
9877
|
+
let mapped = [];
|
|
9814
9878
|
for (let tooltip of value) {
|
|
9815
9879
|
let newPos = tr.changes.mapPos(tooltip.pos, -1, state.MapMode.TrackDel);
|
|
9816
9880
|
if (newPos != null) {
|
|
@@ -9818,8 +9882,10 @@ function hoverTooltip(source, options = {}) {
|
|
|
9818
9882
|
copy.pos = newPos;
|
|
9819
9883
|
if (copy.end != null)
|
|
9820
9884
|
copy.end = tr.changes.mapPos(copy.end);
|
|
9885
|
+
mapped.push(copy);
|
|
9821
9886
|
}
|
|
9822
9887
|
}
|
|
9888
|
+
value = mapped;
|
|
9823
9889
|
}
|
|
9824
9890
|
}
|
|
9825
9891
|
for (let effect of tr.effects) {
|
|
@@ -10152,8 +10218,9 @@ const gutterView = ViewPlugin.fromClass(class {
|
|
|
10152
10218
|
let vpOverlap = Math.min(vpA.to, vpB.to) - Math.max(vpA.from, vpB.from);
|
|
10153
10219
|
this.syncGutters(vpOverlap < (vpB.to - vpB.from) * 0.8);
|
|
10154
10220
|
}
|
|
10155
|
-
if (update.geometryChanged)
|
|
10156
|
-
this.dom.style.minHeight = this.view.contentHeight + "px";
|
|
10221
|
+
if (update.geometryChanged) {
|
|
10222
|
+
this.dom.style.minHeight = (this.view.contentHeight / this.view.scaleY) + "px";
|
|
10223
|
+
}
|
|
10157
10224
|
if (this.view.state.facet(unfixGutters) != !this.fixed) {
|
|
10158
10225
|
this.fixed = !this.fixed;
|
|
10159
10226
|
this.dom.style.position = this.fixed ? "sticky" : "";
|
package/dist/index.d.cts
CHANGED
|
@@ -404,6 +404,13 @@ interface PluginValue extends Object {
|
|
|
404
404
|
*/
|
|
405
405
|
update?(update: ViewUpdate): void;
|
|
406
406
|
/**
|
|
407
|
+
Called when the document view is updated (due to content,
|
|
408
|
+
decoration, or viewport changes). Should not try to immediately
|
|
409
|
+
start another view update. Often useful for calling
|
|
410
|
+
[`requestMeasure`](https://codemirror.net/6/docs/ref/#view.EditorView.requestMeasure).
|
|
411
|
+
*/
|
|
412
|
+
docViewUpdate?(view: EditorView): void;
|
|
413
|
+
/**
|
|
407
414
|
Called when the plugin is no longer going to be used. Should
|
|
408
415
|
revert any changes the plugin made to the DOM.
|
|
409
416
|
*/
|
|
@@ -791,6 +798,7 @@ declare class EditorView {
|
|
|
791
798
|
*/
|
|
792
799
|
setState(newState: EditorState): void;
|
|
793
800
|
private updatePlugins;
|
|
801
|
+
private docViewUpdate;
|
|
794
802
|
/**
|
|
795
803
|
Get the CSS classes for the currently active editor themes.
|
|
796
804
|
*/
|
|
@@ -1638,6 +1646,11 @@ interface LayerConfig {
|
|
|
1638
1646
|
*/
|
|
1639
1647
|
update(update: ViewUpdate, layer: HTMLElement): boolean;
|
|
1640
1648
|
/**
|
|
1649
|
+
Whether to update this layer every time the document view
|
|
1650
|
+
changes. Defaults to true.
|
|
1651
|
+
*/
|
|
1652
|
+
updateOnDocViewUpdate: boolean;
|
|
1653
|
+
/**
|
|
1641
1654
|
Build a set of markers for this layer, and measure their
|
|
1642
1655
|
dimensions.
|
|
1643
1656
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -404,6 +404,13 @@ interface PluginValue extends Object {
|
|
|
404
404
|
*/
|
|
405
405
|
update?(update: ViewUpdate): void;
|
|
406
406
|
/**
|
|
407
|
+
Called when the document view is updated (due to content,
|
|
408
|
+
decoration, or viewport changes). Should not try to immediately
|
|
409
|
+
start another view update. Often useful for calling
|
|
410
|
+
[`requestMeasure`](https://codemirror.net/6/docs/ref/#view.EditorView.requestMeasure).
|
|
411
|
+
*/
|
|
412
|
+
docViewUpdate?(view: EditorView): void;
|
|
413
|
+
/**
|
|
407
414
|
Called when the plugin is no longer going to be used. Should
|
|
408
415
|
revert any changes the plugin made to the DOM.
|
|
409
416
|
*/
|
|
@@ -791,6 +798,7 @@ declare class EditorView {
|
|
|
791
798
|
*/
|
|
792
799
|
setState(newState: EditorState): void;
|
|
793
800
|
private updatePlugins;
|
|
801
|
+
private docViewUpdate;
|
|
794
802
|
/**
|
|
795
803
|
Get the CSS classes for the currently active editor themes.
|
|
796
804
|
*/
|
|
@@ -1638,6 +1646,11 @@ interface LayerConfig {
|
|
|
1638
1646
|
*/
|
|
1639
1647
|
update(update: ViewUpdate, layer: HTMLElement): boolean;
|
|
1640
1648
|
/**
|
|
1649
|
+
Whether to update this layer every time the document view
|
|
1650
|
+
changes. Defaults to true.
|
|
1651
|
+
*/
|
|
1652
|
+
updateOnDocViewUpdate: boolean;
|
|
1653
|
+
/**
|
|
1641
1654
|
Build a set of markers for this layer, and measure their
|
|
1642
1655
|
dimensions.
|
|
1643
1656
|
*/
|
package/dist/index.js
CHANGED
|
@@ -1723,10 +1723,10 @@ class ContentBuilder {
|
|
|
1723
1723
|
if (deco.block) {
|
|
1724
1724
|
if (deco.startSide > 0 && !this.posCovered())
|
|
1725
1725
|
this.getLine();
|
|
1726
|
-
this.addBlockWidget(new BlockWidgetView(deco.widget ||
|
|
1726
|
+
this.addBlockWidget(new BlockWidgetView(deco.widget || NullWidget.block, len, deco));
|
|
1727
1727
|
}
|
|
1728
1728
|
else {
|
|
1729
|
-
let view = WidgetView.create(deco.widget ||
|
|
1729
|
+
let view = WidgetView.create(deco.widget || NullWidget.inline, len, len ? 0 : deco.startSide);
|
|
1730
1730
|
let cursorBefore = this.atCursorPos && !view.isEditable && openStart <= active.length &&
|
|
1731
1731
|
(from < to || deco.startSide > 0);
|
|
1732
1732
|
let cursorAfter = !view.isEditable && (from < to || openStart > active.length || deco.startSide <= 0);
|
|
@@ -1787,6 +1787,8 @@ class NullWidget extends WidgetType {
|
|
|
1787
1787
|
updateDOM(elt) { return elt.nodeName.toLowerCase() == this.tag; }
|
|
1788
1788
|
get isHidden() { return true; }
|
|
1789
1789
|
}
|
|
1790
|
+
NullWidget.inline = /*@__PURE__*/new NullWidget("span");
|
|
1791
|
+
NullWidget.block = /*@__PURE__*/new NullWidget("div");
|
|
1790
1792
|
|
|
1791
1793
|
/**
|
|
1792
1794
|
Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
|
|
@@ -2646,10 +2648,11 @@ class DocView extends ContentView {
|
|
|
2646
2648
|
super();
|
|
2647
2649
|
this.view = view;
|
|
2648
2650
|
this.decorations = [];
|
|
2649
|
-
this.dynamicDecorationMap = [];
|
|
2651
|
+
this.dynamicDecorationMap = [false];
|
|
2650
2652
|
this.domChanged = null;
|
|
2651
2653
|
this.hasComposition = null;
|
|
2652
2654
|
this.markedForComposition = new Set;
|
|
2655
|
+
this.compositionBarrier = Decoration.none;
|
|
2653
2656
|
// Track a minimum width for the editor. When measuring sizes in
|
|
2654
2657
|
// measureVisibleLineHeights, this is updated to point at the width
|
|
2655
2658
|
// of a given element and its extent in the document. When a change
|
|
@@ -2913,7 +2916,7 @@ class DocView extends ContentView {
|
|
|
2913
2916
|
// composition, avoid moving it across it and disrupting the
|
|
2914
2917
|
// composition.
|
|
2915
2918
|
suppressWidgetCursorChange(sel, cursor) {
|
|
2916
|
-
return this.hasComposition && cursor.empty &&
|
|
2919
|
+
return this.hasComposition && cursor.empty && !this.compositionBarrier.size &&
|
|
2917
2920
|
isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset) &&
|
|
2918
2921
|
this.posFromDOM(sel.focusNode, sel.focusOffset) == cursor.head;
|
|
2919
2922
|
}
|
|
@@ -3121,8 +3124,9 @@ class DocView extends ContentView {
|
|
|
3121
3124
|
return Decoration.set(deco);
|
|
3122
3125
|
}
|
|
3123
3126
|
updateDeco() {
|
|
3124
|
-
let
|
|
3125
|
-
|
|
3127
|
+
let i = 1;
|
|
3128
|
+
let allDeco = this.view.state.facet(decorations).map(d => {
|
|
3129
|
+
let dynamic = this.dynamicDecorationMap[i++] = typeof d == "function";
|
|
3126
3130
|
return dynamic ? d(this.view) : d;
|
|
3127
3131
|
});
|
|
3128
3132
|
let dynamicOuter = false, outerDeco = this.view.state.facet(outerDecorations).map((d, i) => {
|
|
@@ -3132,16 +3136,46 @@ class DocView extends ContentView {
|
|
|
3132
3136
|
return dynamic ? d(this.view) : d;
|
|
3133
3137
|
});
|
|
3134
3138
|
if (outerDeco.length) {
|
|
3135
|
-
this.dynamicDecorationMap[
|
|
3139
|
+
this.dynamicDecorationMap[i++] = dynamicOuter;
|
|
3136
3140
|
allDeco.push(RangeSet.join(outerDeco));
|
|
3137
3141
|
}
|
|
3138
|
-
|
|
3139
|
-
this.
|
|
3140
|
-
return this.decorations = [
|
|
3142
|
+
this.decorations = [
|
|
3143
|
+
this.compositionBarrier,
|
|
3141
3144
|
...allDeco,
|
|
3142
3145
|
this.computeBlockGapDeco(),
|
|
3143
3146
|
this.view.viewState.lineGapDeco
|
|
3144
3147
|
];
|
|
3148
|
+
while (i < this.decorations.length)
|
|
3149
|
+
this.dynamicDecorationMap[i++] = false;
|
|
3150
|
+
return this.decorations;
|
|
3151
|
+
}
|
|
3152
|
+
// Starting a composition will style the inserted text with the
|
|
3153
|
+
// style of the text before it, and this is only cleared when the
|
|
3154
|
+
// composition ends, because touching it before that will abort it.
|
|
3155
|
+
// This (called from compositionstart handler) tries to notice when
|
|
3156
|
+
// the cursor is after a non-inclusive mark, where the styling could
|
|
3157
|
+
// be jarring, and insert an ad-hoc widget before the cursor to
|
|
3158
|
+
// isolate it from the style before it.
|
|
3159
|
+
maybeCreateCompositionBarrier() {
|
|
3160
|
+
let { main: { head, empty } } = this.view.state.selection;
|
|
3161
|
+
if (!empty)
|
|
3162
|
+
return false;
|
|
3163
|
+
let found = null;
|
|
3164
|
+
for (let set of this.decorations) {
|
|
3165
|
+
set.between(head, head, (from, to, value) => {
|
|
3166
|
+
if (value.point)
|
|
3167
|
+
found = false;
|
|
3168
|
+
else if (value.endSide < 0 && from < head && to == head)
|
|
3169
|
+
found = true;
|
|
3170
|
+
});
|
|
3171
|
+
if (found === false)
|
|
3172
|
+
break;
|
|
3173
|
+
}
|
|
3174
|
+
this.compositionBarrier = found ? Decoration.set(compositionBarrierWidget.range(head)) : Decoration.none;
|
|
3175
|
+
return !!found;
|
|
3176
|
+
}
|
|
3177
|
+
clearCompositionBarrier() {
|
|
3178
|
+
this.compositionBarrier = Decoration.none;
|
|
3145
3179
|
}
|
|
3146
3180
|
scrollIntoView(target) {
|
|
3147
3181
|
if (target.isSnapshot) {
|
|
@@ -3166,6 +3200,7 @@ class DocView extends ContentView {
|
|
|
3166
3200
|
scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, Math.max(Math.min(target.xMargin, offsetWidth), -offsetWidth), Math.max(Math.min(target.yMargin, offsetHeight), -offsetHeight), this.view.textDirection == Direction.LTR);
|
|
3167
3201
|
}
|
|
3168
3202
|
}
|
|
3203
|
+
const compositionBarrierWidget = /*@__PURE__*/Decoration.widget({ side: -1, widget: NullWidget.inline });
|
|
3169
3204
|
function betweenUneditable(pos) {
|
|
3170
3205
|
return pos.node.nodeType == 1 && pos.node.firstChild &&
|
|
3171
3206
|
(pos.offset == 0 || pos.node.childNodes[pos.offset - 1].contentEditable == "false") &&
|
|
@@ -4415,6 +4450,10 @@ observers.compositionstart = observers.compositionupdate = view => {
|
|
|
4415
4450
|
if (view.inputState.composing < 0) {
|
|
4416
4451
|
// FIXME possibly set a timeout to clear it again on Android
|
|
4417
4452
|
view.inputState.composing = 0;
|
|
4453
|
+
if (view.docView.maybeCreateCompositionBarrier()) {
|
|
4454
|
+
view.update([]);
|
|
4455
|
+
view.docView.clearCompositionBarrier();
|
|
4456
|
+
}
|
|
4418
4457
|
}
|
|
4419
4458
|
};
|
|
4420
4459
|
observers.compositionend = view => {
|
|
@@ -6293,7 +6332,10 @@ function applyDOMChange(view, domChange) {
|
|
|
6293
6332
|
// events and the pendingAndroidKey mechanism, but that's not
|
|
6294
6333
|
// reliable in all situations.)
|
|
6295
6334
|
if (browser.android &&
|
|
6296
|
-
((change.
|
|
6335
|
+
((change.to == sel.to &&
|
|
6336
|
+
// GBoard will sometimes remove a space it just inserted
|
|
6337
|
+
// after a completion when you press enter
|
|
6338
|
+
(change.from == sel.from || change.from == sel.from - 1 && view.state.sliceDoc(change.from, sel.from) == " ") &&
|
|
6297
6339
|
change.insert.length == 1 && change.insert.lines == 2 &&
|
|
6298
6340
|
dispatchKey(view.contentDOM, "Enter", 13)) ||
|
|
6299
6341
|
((change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 ||
|
|
@@ -7113,6 +7155,8 @@ class EditorView {
|
|
|
7113
7155
|
this.viewState.mustMeasureContent = true;
|
|
7114
7156
|
if (redrawn || attrsChanged || scrollTarget || this.viewState.mustEnforceCursorAssoc || this.viewState.mustMeasureContent)
|
|
7115
7157
|
this.requestMeasure();
|
|
7158
|
+
if (redrawn)
|
|
7159
|
+
this.docViewUpdate();
|
|
7116
7160
|
if (!update.empty)
|
|
7117
7161
|
for (let listener of this.state.facet(updateListener)) {
|
|
7118
7162
|
try {
|
|
@@ -7200,6 +7244,19 @@ class EditorView {
|
|
|
7200
7244
|
if (prevSpecs != specs)
|
|
7201
7245
|
this.inputState.ensureHandlers(this.plugins);
|
|
7202
7246
|
}
|
|
7247
|
+
docViewUpdate() {
|
|
7248
|
+
for (let plugin of this.plugins) {
|
|
7249
|
+
let val = plugin.value;
|
|
7250
|
+
if (val && val.docViewUpdate) {
|
|
7251
|
+
try {
|
|
7252
|
+
val.docViewUpdate(this);
|
|
7253
|
+
}
|
|
7254
|
+
catch (e) {
|
|
7255
|
+
logException(this.state, e, "doc view update listener");
|
|
7256
|
+
}
|
|
7257
|
+
}
|
|
7258
|
+
}
|
|
7259
|
+
}
|
|
7203
7260
|
/**
|
|
7204
7261
|
@internal
|
|
7205
7262
|
*/
|
|
@@ -7270,6 +7327,8 @@ class EditorView {
|
|
|
7270
7327
|
this.inputState.update(update);
|
|
7271
7328
|
this.updateAttrs();
|
|
7272
7329
|
redrawn = this.docView.update(update);
|
|
7330
|
+
if (redrawn)
|
|
7331
|
+
this.docViewUpdate();
|
|
7273
7332
|
}
|
|
7274
7333
|
for (let i = 0; i < measuring.length; i++)
|
|
7275
7334
|
if (measured[i] != BadMeasure) {
|
|
@@ -8418,6 +8477,10 @@ class LayerView {
|
|
|
8418
8477
|
update.view.requestMeasure(this.measureReq);
|
|
8419
8478
|
}
|
|
8420
8479
|
}
|
|
8480
|
+
docViewUpdate(view) {
|
|
8481
|
+
if (this.layer.updateOnDocViewUpdate !== false)
|
|
8482
|
+
view.requestMeasure(this.measureReq);
|
|
8483
|
+
}
|
|
8421
8484
|
setOrder(state) {
|
|
8422
8485
|
let pos = 0, order = state.facet(layerOrder);
|
|
8423
8486
|
while (pos < order.length && order[pos] != this.layer)
|
|
@@ -9806,6 +9869,7 @@ function hoverTooltip(source, options = {}) {
|
|
|
9806
9869
|
else if (options.hideOn)
|
|
9807
9870
|
value = value.filter(v => !options.hideOn(tr, v));
|
|
9808
9871
|
if (tr.docChanged) {
|
|
9872
|
+
let mapped = [];
|
|
9809
9873
|
for (let tooltip of value) {
|
|
9810
9874
|
let newPos = tr.changes.mapPos(tooltip.pos, -1, MapMode.TrackDel);
|
|
9811
9875
|
if (newPos != null) {
|
|
@@ -9813,8 +9877,10 @@ function hoverTooltip(source, options = {}) {
|
|
|
9813
9877
|
copy.pos = newPos;
|
|
9814
9878
|
if (copy.end != null)
|
|
9815
9879
|
copy.end = tr.changes.mapPos(copy.end);
|
|
9880
|
+
mapped.push(copy);
|
|
9816
9881
|
}
|
|
9817
9882
|
}
|
|
9883
|
+
value = mapped;
|
|
9818
9884
|
}
|
|
9819
9885
|
}
|
|
9820
9886
|
for (let effect of tr.effects) {
|
|
@@ -10147,8 +10213,9 @@ const gutterView = /*@__PURE__*/ViewPlugin.fromClass(class {
|
|
|
10147
10213
|
let vpOverlap = Math.min(vpA.to, vpB.to) - Math.max(vpA.from, vpB.from);
|
|
10148
10214
|
this.syncGutters(vpOverlap < (vpB.to - vpB.from) * 0.8);
|
|
10149
10215
|
}
|
|
10150
|
-
if (update.geometryChanged)
|
|
10151
|
-
this.dom.style.minHeight = this.view.contentHeight + "px";
|
|
10216
|
+
if (update.geometryChanged) {
|
|
10217
|
+
this.dom.style.minHeight = (this.view.contentHeight / this.view.scaleY) + "px";
|
|
10218
|
+
}
|
|
10152
10219
|
if (this.view.state.facet(unfixGutters) != !this.fixed) {
|
|
10153
10220
|
this.fixed = !this.fixed;
|
|
10154
10221
|
this.dom.style.position = this.fixed ? "sticky" : "";
|