@codemirror/view 0.19.28 → 0.19.32
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 +46 -0
- package/dist/index.cjs +302 -162
- package/dist/index.d.ts +21 -1
- package/dist/index.js +303 -164
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -658,6 +658,7 @@ class TextView extends ContentView {
|
|
|
658
658
|
split(from) {
|
|
659
659
|
let result = new TextView(this.text.slice(from));
|
|
660
660
|
this.text = this.text.slice(0, from);
|
|
661
|
+
this.markDirty();
|
|
661
662
|
return result;
|
|
662
663
|
}
|
|
663
664
|
localPosFromDOM(node, offset) {
|
|
@@ -2254,6 +2255,78 @@ function moveVisually(line, order, dir, start, forward) {
|
|
|
2254
2255
|
return state.EditorSelection.cursor(nextIndex + line.from, forward ? -1 : 1, span.level);
|
|
2255
2256
|
}
|
|
2256
2257
|
|
|
2258
|
+
class DOMReader {
|
|
2259
|
+
constructor(points, view) {
|
|
2260
|
+
this.points = points;
|
|
2261
|
+
this.view = view;
|
|
2262
|
+
this.text = "";
|
|
2263
|
+
this.lineBreak = view.state.lineBreak;
|
|
2264
|
+
}
|
|
2265
|
+
readRange(start, end) {
|
|
2266
|
+
if (!start)
|
|
2267
|
+
return this;
|
|
2268
|
+
let parent = start.parentNode;
|
|
2269
|
+
for (let cur = start;;) {
|
|
2270
|
+
this.findPointBefore(parent, cur);
|
|
2271
|
+
this.readNode(cur);
|
|
2272
|
+
let next = cur.nextSibling;
|
|
2273
|
+
if (next == end)
|
|
2274
|
+
break;
|
|
2275
|
+
let view = ContentView.get(cur), nextView = ContentView.get(next);
|
|
2276
|
+
if (view && nextView ? view.breakAfter :
|
|
2277
|
+
(view ? view.breakAfter : isBlockElement(cur)) ||
|
|
2278
|
+
(isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore)))
|
|
2279
|
+
this.text += this.lineBreak;
|
|
2280
|
+
cur = next;
|
|
2281
|
+
}
|
|
2282
|
+
this.findPointBefore(parent, end);
|
|
2283
|
+
return this;
|
|
2284
|
+
}
|
|
2285
|
+
readNode(node) {
|
|
2286
|
+
if (node.cmIgnore)
|
|
2287
|
+
return;
|
|
2288
|
+
let view = ContentView.get(node);
|
|
2289
|
+
let fromView = view && view.overrideDOMText;
|
|
2290
|
+
let text;
|
|
2291
|
+
if (fromView != null)
|
|
2292
|
+
text = fromView.sliceString(0, undefined, this.lineBreak);
|
|
2293
|
+
else if (node.nodeType == 3)
|
|
2294
|
+
text = node.nodeValue;
|
|
2295
|
+
else if (node.nodeName == "BR")
|
|
2296
|
+
text = node.nextSibling ? this.lineBreak : "";
|
|
2297
|
+
else if (node.nodeType == 1)
|
|
2298
|
+
this.readRange(node.firstChild, null);
|
|
2299
|
+
if (text != null) {
|
|
2300
|
+
this.findPointIn(node, text.length);
|
|
2301
|
+
this.text += text;
|
|
2302
|
+
// Chrome inserts two newlines when pressing shift-enter at the
|
|
2303
|
+
// end of a line. This drops one of those.
|
|
2304
|
+
if (browser.chrome && this.view.inputState.lastKeyCode == 13 && !node.nextSibling && /\n\n$/.test(this.text))
|
|
2305
|
+
this.text = this.text.slice(0, -1);
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
findPointBefore(node, next) {
|
|
2309
|
+
for (let point of this.points)
|
|
2310
|
+
if (point.node == node && node.childNodes[point.offset] == next)
|
|
2311
|
+
point.pos = this.text.length;
|
|
2312
|
+
}
|
|
2313
|
+
findPointIn(node, maxLen) {
|
|
2314
|
+
for (let point of this.points)
|
|
2315
|
+
if (point.node == node)
|
|
2316
|
+
point.pos = this.text.length + Math.min(point.offset, maxLen);
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
function isBlockElement(node) {
|
|
2320
|
+
return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
|
|
2321
|
+
}
|
|
2322
|
+
class DOMPoint {
|
|
2323
|
+
constructor(node, offset) {
|
|
2324
|
+
this.node = node;
|
|
2325
|
+
this.offset = offset;
|
|
2326
|
+
this.pos = -1;
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2257
2330
|
class DocView extends ContentView {
|
|
2258
2331
|
constructor(view) {
|
|
2259
2332
|
super();
|
|
@@ -2303,7 +2376,7 @@ class DocView extends ContentView {
|
|
|
2303
2376
|
}
|
|
2304
2377
|
if (this.view.inputState.composing < 0)
|
|
2305
2378
|
this.compositionDeco = Decoration.none;
|
|
2306
|
-
else if (update.transactions.length)
|
|
2379
|
+
else if (update.transactions.length || this.dirty)
|
|
2307
2380
|
this.compositionDeco = computeCompositionDeco(this.view, update.changes);
|
|
2308
2381
|
// When the DOM nodes around the selection are moved to another
|
|
2309
2382
|
// parent, Chrome sometimes reports a different selection through
|
|
@@ -2326,16 +2399,6 @@ class DocView extends ContentView {
|
|
|
2326
2399
|
return true;
|
|
2327
2400
|
}
|
|
2328
2401
|
}
|
|
2329
|
-
reset(sel) {
|
|
2330
|
-
if (this.dirty) {
|
|
2331
|
-
this.view.observer.ignore(() => this.view.docView.sync());
|
|
2332
|
-
this.dirty = 0 /* Not */;
|
|
2333
|
-
this.updateSelection(true);
|
|
2334
|
-
}
|
|
2335
|
-
else {
|
|
2336
|
-
this.updateSelection();
|
|
2337
|
-
}
|
|
2338
|
-
}
|
|
2339
2402
|
// Used by update and the constructor do perform the actual DOM
|
|
2340
2403
|
// update
|
|
2341
2404
|
updateInner(changes, deco, oldLength) {
|
|
@@ -2411,7 +2474,8 @@ class DocView extends ContentView {
|
|
|
2411
2474
|
// inside an uneditable node, and not bring it back when we
|
|
2412
2475
|
// move the cursor to its proper position. This tries to
|
|
2413
2476
|
// restore the keyboard by cycling focus.
|
|
2414
|
-
if (browser.android && browser.chrome && this.dom.contains(domSel.focusNode) &&
|
|
2477
|
+
if (browser.android && browser.chrome && this.dom.contains(domSel.focusNode) &&
|
|
2478
|
+
inUneditable(domSel.focusNode, this.dom)) {
|
|
2415
2479
|
this.dom.blur();
|
|
2416
2480
|
this.dom.focus({ preventScroll: true });
|
|
2417
2481
|
}
|
|
@@ -2454,7 +2518,7 @@ class DocView extends ContentView {
|
|
|
2454
2518
|
this.impreciseHead = head.precise ? null : new DOMPos(domSel.focusNode, domSel.focusOffset);
|
|
2455
2519
|
}
|
|
2456
2520
|
enforceCursorAssoc() {
|
|
2457
|
-
if (this.
|
|
2521
|
+
if (this.compositionDeco.size)
|
|
2458
2522
|
return;
|
|
2459
2523
|
let cursor = this.view.state.selection.main;
|
|
2460
2524
|
let sel = getSelection(this.root);
|
|
@@ -2679,7 +2743,8 @@ function computeCompositionDeco(view, changes) {
|
|
|
2679
2743
|
topNode = cView.dom;
|
|
2680
2744
|
}
|
|
2681
2745
|
let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
|
|
2682
|
-
let text =
|
|
2746
|
+
let { state } = view, text = topNode.nodeType == 3 ? topNode.nodeValue :
|
|
2747
|
+
new DOMReader([], view).readRange(topNode.firstChild, null).text;
|
|
2683
2748
|
if (newTo - newFrom < text.length) {
|
|
2684
2749
|
if (state.sliceDoc(newFrom, Math.min(state.doc.length, newFrom + text.length)) == text)
|
|
2685
2750
|
newTo = newFrom + text.length;
|
|
@@ -2883,21 +2948,29 @@ function domPosInText(node, x, y) {
|
|
|
2883
2948
|
function posAtCoords(view, { x, y }, precise, bias = -1) {
|
|
2884
2949
|
var _a;
|
|
2885
2950
|
let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop;
|
|
2886
|
-
let
|
|
2887
|
-
|
|
2888
|
-
|
|
2951
|
+
let block, yOffset = y - docTop, { docHeight } = view.viewState;
|
|
2952
|
+
if (yOffset < 0 || yOffset > docHeight) {
|
|
2953
|
+
if (precise)
|
|
2954
|
+
return null;
|
|
2955
|
+
yOffset = yOffset < 0 ? 0 : docHeight;
|
|
2956
|
+
}
|
|
2957
|
+
// Scan for a text block near the queried y position
|
|
2958
|
+
for (let halfLine = view.defaultLineHeight / 2, bounced = false;;) {
|
|
2889
2959
|
block = view.elementAtHeight(yOffset);
|
|
2890
|
-
if (block.
|
|
2891
|
-
|
|
2892
|
-
|
|
2960
|
+
if (block.type == exports.BlockType.Text)
|
|
2961
|
+
break;
|
|
2962
|
+
for (;;) {
|
|
2963
|
+
// Move the y position out of this block
|
|
2964
|
+
yOffset = bias > 0 ? block.bottom + halfLine : block.top - halfLine;
|
|
2965
|
+
if (yOffset >= 0 && yOffset <= docHeight)
|
|
2966
|
+
break;
|
|
2967
|
+
// If the document consists entirely of replaced widgets, we
|
|
2968
|
+
// won't find a text block, so return 0
|
|
2893
2969
|
if (bounced)
|
|
2894
2970
|
return precise ? null : 0;
|
|
2895
|
-
|
|
2896
|
-
|
|
2971
|
+
bounced = true;
|
|
2972
|
+
bias = -bias;
|
|
2897
2973
|
}
|
|
2898
|
-
if (block.type == exports.BlockType.Text)
|
|
2899
|
-
break;
|
|
2900
|
-
yOffset = bias > 0 ? block.bottom + halfLine : block.top - halfLine;
|
|
2901
2974
|
}
|
|
2902
2975
|
y = docTop + yOffset;
|
|
2903
2976
|
let lineStart = block.from;
|
|
@@ -3055,14 +3128,6 @@ class InputState {
|
|
|
3055
3128
|
constructor(view) {
|
|
3056
3129
|
this.lastKeyCode = 0;
|
|
3057
3130
|
this.lastKeyTime = 0;
|
|
3058
|
-
// On Chrome Android, backspace near widgets is just completely
|
|
3059
|
-
// broken, and there are no key events, so we need to handle the
|
|
3060
|
-
// beforeinput event. Deleting stuff will often create a flurry of
|
|
3061
|
-
// events, and interrupting it before it is done just makes
|
|
3062
|
-
// subsequent events even more broken, so again, we hold off doing
|
|
3063
|
-
// anything until the browser is finished with whatever it is trying
|
|
3064
|
-
// to do.
|
|
3065
|
-
this.pendingAndroidKey = undefined;
|
|
3066
3131
|
// On iOS, some keys need to have their default behavior happen
|
|
3067
3132
|
// (after which we retroactively handle them and reset the DOM) to
|
|
3068
3133
|
// avoid messing up the virtual keyboard state.
|
|
@@ -3131,22 +3196,15 @@ class InputState {
|
|
|
3131
3196
|
}
|
|
3132
3197
|
runCustomHandlers(type, view, event) {
|
|
3133
3198
|
for (let set of this.customHandlers) {
|
|
3134
|
-
let handler = set.handlers[type]
|
|
3199
|
+
let handler = set.handlers[type];
|
|
3135
3200
|
if (handler) {
|
|
3136
3201
|
try {
|
|
3137
|
-
|
|
3202
|
+
if (handler.call(set.plugin, event, view) || event.defaultPrevented)
|
|
3203
|
+
return true;
|
|
3138
3204
|
}
|
|
3139
3205
|
catch (e) {
|
|
3140
3206
|
logException(view.state, e);
|
|
3141
3207
|
}
|
|
3142
|
-
if (handled || event.defaultPrevented) {
|
|
3143
|
-
// Chrome for Android often applies a bunch of nonsensical
|
|
3144
|
-
// DOM changes after an enter press, even when
|
|
3145
|
-
// preventDefault-ed. This tries to ignore those.
|
|
3146
|
-
if (browser.android && type == "keydown" && event.keyCode == 13)
|
|
3147
|
-
view.observer.flushSoon();
|
|
3148
|
-
return true;
|
|
3149
|
-
}
|
|
3150
3208
|
}
|
|
3151
3209
|
}
|
|
3152
3210
|
return false;
|
|
@@ -3170,6 +3228,16 @@ class InputState {
|
|
|
3170
3228
|
this.lastKeyTime = Date.now();
|
|
3171
3229
|
if (this.screenKeyEvent(view, event))
|
|
3172
3230
|
return true;
|
|
3231
|
+
// Chrome for Android usually doesn't fire proper key events, but
|
|
3232
|
+
// occasionally does, usually surrounded by a bunch of complicated
|
|
3233
|
+
// composition changes. When an enter or backspace key event is
|
|
3234
|
+
// seen, hold off on handling DOM events for a bit, and then
|
|
3235
|
+
// dispatch it.
|
|
3236
|
+
if (browser.android && browser.chrome && !event.synthetic &&
|
|
3237
|
+
(event.keyCode == 13 || event.keyCode == 8)) {
|
|
3238
|
+
view.observer.delayAndroidKey(event.key, event.keyCode);
|
|
3239
|
+
return true;
|
|
3240
|
+
}
|
|
3173
3241
|
// Prevent the default behavior of Enter on iOS makes the
|
|
3174
3242
|
// virtual keyboard get stuck in the wrong (lowercase)
|
|
3175
3243
|
// state. So we let it go through, and then, in
|
|
@@ -3191,24 +3259,6 @@ class InputState {
|
|
|
3191
3259
|
this.pendingIOSKey = undefined;
|
|
3192
3260
|
return dispatchKey(view.contentDOM, key.key, key.keyCode);
|
|
3193
3261
|
}
|
|
3194
|
-
// This causes the DOM observer to pause for a bit, and sets an
|
|
3195
|
-
// animation frame (which seems the most reliable way to detect
|
|
3196
|
-
// 'Chrome is done flailing about messing with the DOM') to fire a
|
|
3197
|
-
// fake key event and re-sync the view again.
|
|
3198
|
-
setPendingAndroidKey(view, pending) {
|
|
3199
|
-
this.pendingAndroidKey = pending;
|
|
3200
|
-
requestAnimationFrame(() => {
|
|
3201
|
-
let key = this.pendingAndroidKey;
|
|
3202
|
-
if (!key)
|
|
3203
|
-
return;
|
|
3204
|
-
this.pendingAndroidKey = undefined;
|
|
3205
|
-
view.observer.processRecords();
|
|
3206
|
-
let startState = view.state;
|
|
3207
|
-
dispatchKey(view.contentDOM, key.key, key.keyCode);
|
|
3208
|
-
if (view.state == startState)
|
|
3209
|
-
view.docView.reset(true);
|
|
3210
|
-
});
|
|
3211
|
-
}
|
|
3212
3262
|
ignoreDuringComposition(event) {
|
|
3213
3263
|
if (!/^key/.test(event.type))
|
|
3214
3264
|
return false;
|
|
@@ -3238,10 +3288,10 @@ class InputState {
|
|
|
3238
3288
|
return (event.type == "keydown" && event.keyCode != 229) ||
|
|
3239
3289
|
event.type == "compositionend" && !browser.ios;
|
|
3240
3290
|
}
|
|
3241
|
-
startMouseSelection(
|
|
3291
|
+
startMouseSelection(mouseSelection) {
|
|
3242
3292
|
if (this.mouseSelection)
|
|
3243
3293
|
this.mouseSelection.destroy();
|
|
3244
|
-
this.mouseSelection =
|
|
3294
|
+
this.mouseSelection = mouseSelection;
|
|
3245
3295
|
}
|
|
3246
3296
|
update(update) {
|
|
3247
3297
|
if (this.mouseSelection)
|
|
@@ -3262,10 +3312,10 @@ const PendingKeys = [
|
|
|
3262
3312
|
// Key codes for modifier keys
|
|
3263
3313
|
const modifierCodes = [16, 17, 18, 20, 91, 92, 224, 225];
|
|
3264
3314
|
class MouseSelection {
|
|
3265
|
-
constructor(
|
|
3266
|
-
this.inputState = inputState;
|
|
3315
|
+
constructor(view, startEvent, style, mustSelect) {
|
|
3267
3316
|
this.view = view;
|
|
3268
3317
|
this.style = style;
|
|
3318
|
+
this.mustSelect = mustSelect;
|
|
3269
3319
|
this.lastEvent = startEvent;
|
|
3270
3320
|
let doc = view.contentDOM.ownerDocument;
|
|
3271
3321
|
doc.addEventListener("mousemove", this.move = this.move.bind(this));
|
|
@@ -3299,16 +3349,18 @@ class MouseSelection {
|
|
|
3299
3349
|
let doc = this.view.contentDOM.ownerDocument;
|
|
3300
3350
|
doc.removeEventListener("mousemove", this.move);
|
|
3301
3351
|
doc.removeEventListener("mouseup", this.up);
|
|
3302
|
-
this.inputState.mouseSelection = null;
|
|
3352
|
+
this.view.inputState.mouseSelection = null;
|
|
3303
3353
|
}
|
|
3304
3354
|
select(event) {
|
|
3305
3355
|
let selection = this.style.get(event, this.extend, this.multiple);
|
|
3306
|
-
if (!selection.eq(this.view.state.selection) ||
|
|
3356
|
+
if (this.mustSelect || !selection.eq(this.view.state.selection) ||
|
|
3357
|
+
selection.main.assoc != this.view.state.selection.main.assoc)
|
|
3307
3358
|
this.view.dispatch({
|
|
3308
3359
|
selection,
|
|
3309
3360
|
userEvent: "select.pointer",
|
|
3310
3361
|
scrollIntoView: true
|
|
3311
3362
|
});
|
|
3363
|
+
this.mustSelect = false;
|
|
3312
3364
|
}
|
|
3313
3365
|
update(update) {
|
|
3314
3366
|
if (update.docChanged && this.dragging)
|
|
@@ -3427,9 +3479,10 @@ handlers.mousedown = (view, event) => {
|
|
|
3427
3479
|
if (!style && event.button == 0)
|
|
3428
3480
|
style = basicMouseSelection(view, event);
|
|
3429
3481
|
if (style) {
|
|
3430
|
-
|
|
3482
|
+
let mustFocus = view.root.activeElement != view.contentDOM;
|
|
3483
|
+
if (mustFocus)
|
|
3431
3484
|
view.observer.ignore(() => focusPreventScroll(view.contentDOM));
|
|
3432
|
-
view.inputState.startMouseSelection(view, event, style);
|
|
3485
|
+
view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
|
|
3433
3486
|
}
|
|
3434
3487
|
};
|
|
3435
3488
|
function rangeForClick(view, pos, bias, type) {
|
|
@@ -3684,12 +3737,12 @@ handlers.compositionstart = handlers.compositionupdate = view => {
|
|
|
3684
3737
|
if (view.inputState.compositionFirstChange == null)
|
|
3685
3738
|
view.inputState.compositionFirstChange = true;
|
|
3686
3739
|
if (view.inputState.composing < 0) {
|
|
3740
|
+
// FIXME possibly set a timeout to clear it again on Android
|
|
3741
|
+
view.inputState.composing = 0;
|
|
3687
3742
|
if (view.docView.compositionDeco.size) {
|
|
3688
3743
|
view.observer.flush();
|
|
3689
3744
|
forceClearComposition(view, true);
|
|
3690
3745
|
}
|
|
3691
|
-
// FIXME possibly set a timeout to clear it again on Android
|
|
3692
|
-
view.inputState.composing = 0;
|
|
3693
3746
|
}
|
|
3694
3747
|
};
|
|
3695
3748
|
handlers.compositionend = view => {
|
|
@@ -3715,7 +3768,7 @@ handlers.beforeinput = (view, event) => {
|
|
|
3715
3768
|
// seems to do nothing at all on Chrome).
|
|
3716
3769
|
let pending;
|
|
3717
3770
|
if (browser.chrome && browser.android && (pending = PendingKeys.find(key => key.inputType == event.inputType))) {
|
|
3718
|
-
view.
|
|
3771
|
+
view.observer.delayAndroidKey(pending.key, pending.keyCode);
|
|
3719
3772
|
if (pending.key == "Backspace" || pending.key == "Delete") {
|
|
3720
3773
|
let startViewHeight = ((_a = window.visualViewport) === null || _a === void 0 ? void 0 : _a.height) || 0;
|
|
3721
3774
|
setTimeout(() => {
|
|
@@ -4421,8 +4474,8 @@ function visiblePixelRange(dom, paddingTop) {
|
|
|
4421
4474
|
break;
|
|
4422
4475
|
}
|
|
4423
4476
|
}
|
|
4424
|
-
return { left: left - rect.left, right: right - rect.left,
|
|
4425
|
-
top: top - (rect.top + paddingTop), bottom: bottom - (rect.top + paddingTop) };
|
|
4477
|
+
return { left: left - rect.left, right: Math.max(left, right) - rect.left,
|
|
4478
|
+
top: top - (rect.top + paddingTop), bottom: Math.max(top, bottom) - (rect.top + paddingTop) };
|
|
4426
4479
|
}
|
|
4427
4480
|
// Line gaps are placeholder widgets used to hide pieces of overlong
|
|
4428
4481
|
// lines within the viewport, as a kludge to keep the editor
|
|
@@ -4551,9 +4604,9 @@ class ViewState {
|
|
|
4551
4604
|
let updateLines = !update.changes.empty || (update.flags & 2 /* Height */) ||
|
|
4552
4605
|
viewport.from != this.viewport.from || viewport.to != this.viewport.to;
|
|
4553
4606
|
this.viewport = viewport;
|
|
4607
|
+
this.updateForViewport();
|
|
4554
4608
|
if (updateLines)
|
|
4555
4609
|
this.updateViewportLines();
|
|
4556
|
-
this.updateForViewport();
|
|
4557
4610
|
if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
|
|
4558
4611
|
this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps, update.changes)));
|
|
4559
4612
|
update.flags |= this.computeVisibleRanges();
|
|
@@ -4585,7 +4638,12 @@ class ViewState {
|
|
|
4585
4638
|
let pixelViewport = this.printing ? { top: -1e8, bottom: 1e8, left: -1e8, right: 1e8 } : visiblePixelRange(dom, this.paddingTop);
|
|
4586
4639
|
let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
|
|
4587
4640
|
this.pixelViewport = pixelViewport;
|
|
4588
|
-
|
|
4641
|
+
let inView = this.pixelViewport.bottom > this.pixelViewport.top && this.pixelViewport.right > this.pixelViewport.left;
|
|
4642
|
+
if (inView != this.inView) {
|
|
4643
|
+
this.inView = inView;
|
|
4644
|
+
if (inView)
|
|
4645
|
+
measureContent = true;
|
|
4646
|
+
}
|
|
4589
4647
|
if (!this.inView)
|
|
4590
4648
|
return 0;
|
|
4591
4649
|
if (measureContent) {
|
|
@@ -4622,9 +4680,9 @@ class ViewState {
|
|
|
4622
4680
|
this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to);
|
|
4623
4681
|
if (viewportChange)
|
|
4624
4682
|
this.viewport = this.getViewport(bias, this.scrollTarget);
|
|
4683
|
+
this.updateForViewport();
|
|
4625
4684
|
if ((result & 2 /* Height */) || viewportChange)
|
|
4626
4685
|
this.updateViewportLines();
|
|
4627
|
-
this.updateForViewport();
|
|
4628
4686
|
if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
|
|
4629
4687
|
this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps));
|
|
4630
4688
|
result |= this.computeVisibleRanges();
|
|
@@ -4649,7 +4707,7 @@ class ViewState {
|
|
|
4649
4707
|
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);
|
|
4650
4708
|
// If scrollTarget is given, make sure the viewport includes that position
|
|
4651
4709
|
if (scrollTarget) {
|
|
4652
|
-
let { head } = scrollTarget.range, viewHeight =
|
|
4710
|
+
let { head } = scrollTarget.range, viewHeight = this.editorHeight;
|
|
4653
4711
|
if (head < viewport.from || head > viewport.to) {
|
|
4654
4712
|
let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
|
|
4655
4713
|
if (scrollTarget.center)
|
|
@@ -4670,6 +4728,8 @@ class ViewState {
|
|
|
4670
4728
|
// Checks if a given viewport covers the visible part of the
|
|
4671
4729
|
// document and not too much beyond that.
|
|
4672
4730
|
viewportIsAppropriate({ from, to }, bias = 0) {
|
|
4731
|
+
if (!this.inView)
|
|
4732
|
+
return true;
|
|
4673
4733
|
let { top } = this.heightMap.lineAt(from, QueryType.ByPos, this.state.doc, 0, 0);
|
|
4674
4734
|
let { bottom } = this.heightMap.lineAt(to, QueryType.ByPos, this.state.doc, 0, 0);
|
|
4675
4735
|
let { visibleTop, visibleBottom } = this;
|
|
@@ -4776,8 +4836,11 @@ class ViewState {
|
|
|
4776
4836
|
elementAtHeight(height) {
|
|
4777
4837
|
return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.state.doc, 0, 0), this.scaler);
|
|
4778
4838
|
}
|
|
4839
|
+
get docHeight() {
|
|
4840
|
+
return this.scaler.toDOM(this.heightMap.height);
|
|
4841
|
+
}
|
|
4779
4842
|
get contentHeight() {
|
|
4780
|
-
return this.
|
|
4843
|
+
return this.docHeight + this.paddingTop + this.paddingBottom;
|
|
4781
4844
|
}
|
|
4782
4845
|
}
|
|
4783
4846
|
class Viewport {
|
|
@@ -5007,11 +5070,13 @@ const baseTheme = buildTheme("." + baseThemeID, {
|
|
|
5007
5070
|
// recomputation.
|
|
5008
5071
|
"@keyframes cm-blink": { "0%": {}, "50%": { visibility: "hidden" }, "100%": {} },
|
|
5009
5072
|
"@keyframes cm-blink2": { "0%": {}, "50%": { visibility: "hidden" }, "100%": {} },
|
|
5010
|
-
".cm-cursor": {
|
|
5073
|
+
".cm-cursor, .cm-dropCursor": {
|
|
5011
5074
|
position: "absolute",
|
|
5012
5075
|
borderLeft: "1.2px solid black",
|
|
5013
5076
|
marginLeft: "-0.6px",
|
|
5014
5077
|
pointerEvents: "none",
|
|
5078
|
+
},
|
|
5079
|
+
".cm-cursor": {
|
|
5015
5080
|
display: "none"
|
|
5016
5081
|
},
|
|
5017
5082
|
"&dark .cm-cursor": {
|
|
@@ -5099,6 +5164,7 @@ class DOMObserver {
|
|
|
5099
5164
|
this.delayedFlush = -1;
|
|
5100
5165
|
this.resizeTimeout = -1;
|
|
5101
5166
|
this.queue = [];
|
|
5167
|
+
this.delayedAndroidKey = null;
|
|
5102
5168
|
this.scrollTargets = [];
|
|
5103
5169
|
this.intersection = null;
|
|
5104
5170
|
this.resize = null;
|
|
@@ -5152,7 +5218,7 @@ class DOMObserver {
|
|
|
5152
5218
|
this.intersection = new IntersectionObserver(entries => {
|
|
5153
5219
|
if (this.parentCheck < 0)
|
|
5154
5220
|
this.parentCheck = setTimeout(this.listenForScroll.bind(this), 1000);
|
|
5155
|
-
if (entries.length > 0 && entries[entries.length - 1].intersectionRatio > 0 != this.intersecting) {
|
|
5221
|
+
if (entries.length > 0 && (entries[entries.length - 1].intersectionRatio > 0) != this.intersecting) {
|
|
5156
5222
|
this.intersecting = !this.intersecting;
|
|
5157
5223
|
if (this.intersecting != this.view.inView)
|
|
5158
5224
|
this.onScrollChanged(document.createEvent("Event"));
|
|
@@ -5182,7 +5248,7 @@ class DOMObserver {
|
|
|
5182
5248
|
}
|
|
5183
5249
|
}
|
|
5184
5250
|
onSelectionChange(event) {
|
|
5185
|
-
if (!this.readSelectionRange())
|
|
5251
|
+
if (!this.readSelectionRange() || this.delayedAndroidKey)
|
|
5186
5252
|
return;
|
|
5187
5253
|
let { view } = this, sel = this.selectionRange;
|
|
5188
5254
|
if (view.state.facet(editable) ? view.root.activeElement != this.dom : !hasSelection(view.dom, sel))
|
|
@@ -5278,6 +5344,32 @@ class DOMObserver {
|
|
|
5278
5344
|
this.queue.length = 0;
|
|
5279
5345
|
this.selectionChanged = false;
|
|
5280
5346
|
}
|
|
5347
|
+
// Chrome Android, especially in combination with GBoard, not only
|
|
5348
|
+
// doesn't reliably fire regular key events, but also often
|
|
5349
|
+
// surrounds the effect of enter or backspace with a bunch of
|
|
5350
|
+
// composition events that, when interrupted, cause text duplication
|
|
5351
|
+
// or other kinds of corruption. This hack makes the editor back off
|
|
5352
|
+
// from handling DOM changes for a moment when such a key is
|
|
5353
|
+
// detected (via beforeinput or keydown), and then dispatches the
|
|
5354
|
+
// key event, throwing away the DOM changes if it gets handled.
|
|
5355
|
+
delayAndroidKey(key, keyCode) {
|
|
5356
|
+
if (!this.delayedAndroidKey)
|
|
5357
|
+
requestAnimationFrame(() => {
|
|
5358
|
+
let key = this.delayedAndroidKey;
|
|
5359
|
+
this.delayedAndroidKey = null;
|
|
5360
|
+
let startState = this.view.state;
|
|
5361
|
+
if (dispatchKey(this.view.contentDOM, key.key, key.keyCode))
|
|
5362
|
+
this.processRecords();
|
|
5363
|
+
else
|
|
5364
|
+
this.flush();
|
|
5365
|
+
if (this.view.state == startState)
|
|
5366
|
+
this.view.update([]);
|
|
5367
|
+
});
|
|
5368
|
+
// Since backspace beforeinput is sometimes signalled spuriously,
|
|
5369
|
+
// Enter always takes precedence.
|
|
5370
|
+
if (!this.delayedAndroidKey || key == "Enter")
|
|
5371
|
+
this.delayedAndroidKey = { key, keyCode };
|
|
5372
|
+
}
|
|
5281
5373
|
flushSoon() {
|
|
5282
5374
|
if (this.delayedFlush < 0)
|
|
5283
5375
|
this.delayedFlush = window.setTimeout(() => { this.delayedFlush = -1; this.flush(); }, 20);
|
|
@@ -5314,13 +5406,13 @@ class DOMObserver {
|
|
|
5314
5406
|
}
|
|
5315
5407
|
// Apply pending changes, if any
|
|
5316
5408
|
flush(readSelection = true) {
|
|
5317
|
-
if (readSelection)
|
|
5318
|
-
this.readSelectionRange();
|
|
5319
5409
|
// Completely hold off flushing when pending keys are set—the code
|
|
5320
5410
|
// managing those will make sure processRecords is called and the
|
|
5321
5411
|
// view is resynchronized after
|
|
5322
|
-
if (this.delayedFlush >= 0 || this.
|
|
5412
|
+
if (this.delayedFlush >= 0 || this.delayedAndroidKey)
|
|
5323
5413
|
return;
|
|
5414
|
+
if (readSelection)
|
|
5415
|
+
this.readSelectionRange();
|
|
5324
5416
|
let { from, to, typeOver } = this.processRecords();
|
|
5325
5417
|
let newSel = this.selectionChanged && hasSelection(this.dom, this.selectionRange);
|
|
5326
5418
|
if (from < 0 && !newSel)
|
|
@@ -5330,7 +5422,7 @@ class DOMObserver {
|
|
|
5330
5422
|
this.onChange(from, to, typeOver);
|
|
5331
5423
|
// The view wasn't updated
|
|
5332
5424
|
if (this.view.state == startState)
|
|
5333
|
-
this.view.
|
|
5425
|
+
this.view.update([]);
|
|
5334
5426
|
}
|
|
5335
5427
|
readMutation(rec) {
|
|
5336
5428
|
let cView = this.view.docView.nearest(rec.target);
|
|
@@ -5549,76 +5641,6 @@ function findDiff(a, b, preferredPos, preferredSide) {
|
|
|
5549
5641
|
}
|
|
5550
5642
|
return { from, toA, toB };
|
|
5551
5643
|
}
|
|
5552
|
-
class DOMReader {
|
|
5553
|
-
constructor(points, view) {
|
|
5554
|
-
this.points = points;
|
|
5555
|
-
this.view = view;
|
|
5556
|
-
this.text = "";
|
|
5557
|
-
this.lineBreak = view.state.lineBreak;
|
|
5558
|
-
}
|
|
5559
|
-
readRange(start, end) {
|
|
5560
|
-
if (!start)
|
|
5561
|
-
return;
|
|
5562
|
-
let parent = start.parentNode;
|
|
5563
|
-
for (let cur = start;;) {
|
|
5564
|
-
this.findPointBefore(parent, cur);
|
|
5565
|
-
this.readNode(cur);
|
|
5566
|
-
let next = cur.nextSibling;
|
|
5567
|
-
if (next == end)
|
|
5568
|
-
break;
|
|
5569
|
-
let view = ContentView.get(cur), nextView = ContentView.get(next);
|
|
5570
|
-
if (view && nextView ? view.breakAfter :
|
|
5571
|
-
(view ? view.breakAfter : isBlockElement(cur)) ||
|
|
5572
|
-
(isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore)))
|
|
5573
|
-
this.text += this.lineBreak;
|
|
5574
|
-
cur = next;
|
|
5575
|
-
}
|
|
5576
|
-
this.findPointBefore(parent, end);
|
|
5577
|
-
}
|
|
5578
|
-
readNode(node) {
|
|
5579
|
-
if (node.cmIgnore)
|
|
5580
|
-
return;
|
|
5581
|
-
let view = ContentView.get(node);
|
|
5582
|
-
let fromView = view && view.overrideDOMText;
|
|
5583
|
-
let text;
|
|
5584
|
-
if (fromView != null)
|
|
5585
|
-
text = fromView.sliceString(0, undefined, this.lineBreak);
|
|
5586
|
-
else if (node.nodeType == 3)
|
|
5587
|
-
text = node.nodeValue;
|
|
5588
|
-
else if (node.nodeName == "BR")
|
|
5589
|
-
text = node.nextSibling ? this.lineBreak : "";
|
|
5590
|
-
else if (node.nodeType == 1)
|
|
5591
|
-
this.readRange(node.firstChild, null);
|
|
5592
|
-
if (text != null) {
|
|
5593
|
-
this.findPointIn(node, text.length);
|
|
5594
|
-
this.text += text;
|
|
5595
|
-
// Chrome inserts two newlines when pressing shift-enter at the
|
|
5596
|
-
// end of a line. This drops one of those.
|
|
5597
|
-
if (browser.chrome && this.view.inputState.lastKeyCode == 13 && !node.nextSibling && /\n\n$/.test(this.text))
|
|
5598
|
-
this.text = this.text.slice(0, -1);
|
|
5599
|
-
}
|
|
5600
|
-
}
|
|
5601
|
-
findPointBefore(node, next) {
|
|
5602
|
-
for (let point of this.points)
|
|
5603
|
-
if (point.node == node && node.childNodes[point.offset] == next)
|
|
5604
|
-
point.pos = this.text.length;
|
|
5605
|
-
}
|
|
5606
|
-
findPointIn(node, maxLen) {
|
|
5607
|
-
for (let point of this.points)
|
|
5608
|
-
if (point.node == node)
|
|
5609
|
-
point.pos = this.text.length + Math.min(point.offset, maxLen);
|
|
5610
|
-
}
|
|
5611
|
-
}
|
|
5612
|
-
function isBlockElement(node) {
|
|
5613
|
-
return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
|
|
5614
|
-
}
|
|
5615
|
-
class DOMPoint {
|
|
5616
|
-
constructor(node, offset) {
|
|
5617
|
-
this.node = node;
|
|
5618
|
-
this.offset = offset;
|
|
5619
|
-
this.pos = -1;
|
|
5620
|
-
}
|
|
5621
|
-
}
|
|
5622
5644
|
function selectionPoints(view) {
|
|
5623
5645
|
let result = [];
|
|
5624
5646
|
if (view.root.activeElement != view.contentDOM)
|
|
@@ -5904,7 +5926,9 @@ class EditorView {
|
|
|
5904
5926
|
if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
|
|
5905
5927
|
break;
|
|
5906
5928
|
if (i > 5) {
|
|
5907
|
-
console.warn(this.measureRequests.length
|
|
5929
|
+
console.warn(this.measureRequests.length
|
|
5930
|
+
? "Measure loop restarted more than 5 times"
|
|
5931
|
+
: "Viewport failed to stabilize");
|
|
5908
5932
|
break;
|
|
5909
5933
|
}
|
|
5910
5934
|
let measuring = [];
|
|
@@ -5950,7 +5974,8 @@ class EditorView {
|
|
|
5950
5974
|
}
|
|
5951
5975
|
if (redrawn)
|
|
5952
5976
|
this.docView.updateSelection(true);
|
|
5953
|
-
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
|
|
5977
|
+
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
|
|
5978
|
+
this.measureRequests.length == 0)
|
|
5954
5979
|
break;
|
|
5955
5980
|
}
|
|
5956
5981
|
}
|
|
@@ -6242,6 +6267,11 @@ class EditorView {
|
|
|
6242
6267
|
Find the DOM parent node and offset (child offset if `node` is
|
|
6243
6268
|
an element, character offset when it is a text node) at the
|
|
6244
6269
|
given document position.
|
|
6270
|
+
|
|
6271
|
+
Note that for positions that aren't currently in
|
|
6272
|
+
`visibleRanges`, the resulting DOM position isn't necessarily
|
|
6273
|
+
meaningful (it may just point before or after a placeholder
|
|
6274
|
+
element).
|
|
6245
6275
|
*/
|
|
6246
6276
|
domAtPos(pos) {
|
|
6247
6277
|
return this.docView.domAtPos(pos);
|
|
@@ -6882,7 +6912,7 @@ function measureRange(view, range) {
|
|
|
6882
6912
|
let ltr = view.textDirection == exports.Direction.LTR;
|
|
6883
6913
|
let content = view.contentDOM, contentRect = content.getBoundingClientRect(), base = getBase(view);
|
|
6884
6914
|
let lineStyle = window.getComputedStyle(content.firstChild);
|
|
6885
|
-
let leftSide = contentRect.left + parseInt(lineStyle.paddingLeft);
|
|
6915
|
+
let leftSide = contentRect.left + parseInt(lineStyle.paddingLeft) + Math.min(0, parseInt(lineStyle.textIndent));
|
|
6886
6916
|
let rightSide = contentRect.right - parseInt(lineStyle.paddingRight);
|
|
6887
6917
|
let startBlock = blockAt(view, from), endBlock = blockAt(view, to);
|
|
6888
6918
|
let visualStart = startBlock.type == exports.BlockType.Text ? startBlock : null;
|
|
@@ -6902,7 +6932,7 @@ function measureRange(view, range) {
|
|
|
6902
6932
|
let between = [];
|
|
6903
6933
|
if ((visualStart || startBlock).to < (visualEnd || endBlock).from - 1)
|
|
6904
6934
|
between.push(piece(leftSide, top.bottom, rightSide, bottom.top));
|
|
6905
|
-
else if (top.bottom < bottom.top &&
|
|
6935
|
+
else if (top.bottom < bottom.top && view.elementAtHeight((top.bottom + bottom.top) / 2).type == exports.BlockType.Text)
|
|
6906
6936
|
top.bottom = bottom.top = (top.bottom + bottom.top) / 2;
|
|
6907
6937
|
return pieces(top).concat(between).concat(pieces(bottom));
|
|
6908
6938
|
}
|
|
@@ -6967,6 +6997,98 @@ function measureCursor(view, cursor, primary) {
|
|
|
6967
6997
|
return new Piece(pos.left - base.left, pos.top - base.top, -1, pos.bottom - pos.top, primary ? "cm-cursor cm-cursor-primary" : "cm-cursor cm-cursor-secondary");
|
|
6968
6998
|
}
|
|
6969
6999
|
|
|
7000
|
+
const setDropCursorPos = state.StateEffect.define({
|
|
7001
|
+
map(pos, mapping) { return pos == null ? null : mapping.mapPos(pos); }
|
|
7002
|
+
});
|
|
7003
|
+
const dropCursorPos = state.StateField.define({
|
|
7004
|
+
create() { return null; },
|
|
7005
|
+
update(pos, tr) {
|
|
7006
|
+
if (pos != null)
|
|
7007
|
+
pos = tr.changes.mapPos(pos);
|
|
7008
|
+
return tr.effects.reduce((pos, e) => e.is(setDropCursorPos) ? e.value : pos, pos);
|
|
7009
|
+
}
|
|
7010
|
+
});
|
|
7011
|
+
const drawDropCursor = ViewPlugin.fromClass(class {
|
|
7012
|
+
constructor(view) {
|
|
7013
|
+
this.view = view;
|
|
7014
|
+
this.cursor = null;
|
|
7015
|
+
this.measureReq = { read: this.readPos.bind(this), write: this.drawCursor.bind(this) };
|
|
7016
|
+
}
|
|
7017
|
+
update(update) {
|
|
7018
|
+
var _a;
|
|
7019
|
+
let cursorPos = update.state.field(dropCursorPos);
|
|
7020
|
+
if (cursorPos == null) {
|
|
7021
|
+
if (this.cursor != null) {
|
|
7022
|
+
(_a = this.cursor) === null || _a === void 0 ? void 0 : _a.remove();
|
|
7023
|
+
this.cursor = null;
|
|
7024
|
+
}
|
|
7025
|
+
}
|
|
7026
|
+
else {
|
|
7027
|
+
if (!this.cursor) {
|
|
7028
|
+
this.cursor = this.view.scrollDOM.appendChild(document.createElement("div"));
|
|
7029
|
+
this.cursor.className = "cm-dropCursor";
|
|
7030
|
+
}
|
|
7031
|
+
if (update.startState.field(dropCursorPos) != cursorPos || update.docChanged || update.geometryChanged)
|
|
7032
|
+
this.view.requestMeasure(this.measureReq);
|
|
7033
|
+
}
|
|
7034
|
+
}
|
|
7035
|
+
readPos() {
|
|
7036
|
+
let pos = this.view.state.field(dropCursorPos);
|
|
7037
|
+
let rect = pos != null && this.view.coordsAtPos(pos);
|
|
7038
|
+
if (!rect)
|
|
7039
|
+
return null;
|
|
7040
|
+
let outer = this.view.scrollDOM.getBoundingClientRect();
|
|
7041
|
+
return {
|
|
7042
|
+
left: rect.left - outer.left + this.view.scrollDOM.scrollLeft,
|
|
7043
|
+
top: rect.top - outer.top + this.view.scrollDOM.scrollTop,
|
|
7044
|
+
height: rect.bottom - rect.top
|
|
7045
|
+
};
|
|
7046
|
+
}
|
|
7047
|
+
drawCursor(pos) {
|
|
7048
|
+
if (this.cursor) {
|
|
7049
|
+
if (pos) {
|
|
7050
|
+
this.cursor.style.left = pos.left + "px";
|
|
7051
|
+
this.cursor.style.top = pos.top + "px";
|
|
7052
|
+
this.cursor.style.height = pos.height + "px";
|
|
7053
|
+
}
|
|
7054
|
+
else {
|
|
7055
|
+
this.cursor.style.left = "-100000px";
|
|
7056
|
+
}
|
|
7057
|
+
}
|
|
7058
|
+
}
|
|
7059
|
+
destroy() {
|
|
7060
|
+
if (this.cursor)
|
|
7061
|
+
this.cursor.remove();
|
|
7062
|
+
}
|
|
7063
|
+
setDropPos(pos) {
|
|
7064
|
+
if (this.view.state.field(dropCursorPos) != pos)
|
|
7065
|
+
this.view.dispatch({ effects: setDropCursorPos.of(pos) });
|
|
7066
|
+
}
|
|
7067
|
+
}, {
|
|
7068
|
+
eventHandlers: {
|
|
7069
|
+
dragover(event) {
|
|
7070
|
+
this.setDropPos(this.view.posAtCoords({ x: event.clientX, y: event.clientY }));
|
|
7071
|
+
},
|
|
7072
|
+
dragleave(event) {
|
|
7073
|
+
if (event.target == this.view.contentDOM || !this.view.contentDOM.contains(event.relatedTarget))
|
|
7074
|
+
this.setDropPos(null);
|
|
7075
|
+
},
|
|
7076
|
+
dragend() {
|
|
7077
|
+
this.setDropPos(null);
|
|
7078
|
+
},
|
|
7079
|
+
drop() {
|
|
7080
|
+
this.setDropPos(null);
|
|
7081
|
+
}
|
|
7082
|
+
}
|
|
7083
|
+
});
|
|
7084
|
+
/**
|
|
7085
|
+
Draws a cursor at the current drop position when something is
|
|
7086
|
+
dragged over the editor.
|
|
7087
|
+
*/
|
|
7088
|
+
function dropCursor() {
|
|
7089
|
+
return [dropCursorPos, drawDropCursor];
|
|
7090
|
+
}
|
|
7091
|
+
|
|
6970
7092
|
function iterMatches(doc, re, from, to, f) {
|
|
6971
7093
|
re.lastIndex = 0;
|
|
6972
7094
|
for (let cursor = doc.iterRange(from, to), pos = from, m; !cursor.next().done; pos += cursor.value.length) {
|
|
@@ -6975,6 +7097,22 @@ function iterMatches(doc, re, from, to, f) {
|
|
|
6975
7097
|
f(pos + m.index, pos + m.index + m[0].length, m);
|
|
6976
7098
|
}
|
|
6977
7099
|
}
|
|
7100
|
+
function matchRanges(view, maxLength) {
|
|
7101
|
+
let visible = view.visibleRanges;
|
|
7102
|
+
if (visible.length == 1 && visible[0].from == view.viewport.from &&
|
|
7103
|
+
visible[0].to == view.viewport.to)
|
|
7104
|
+
return visible;
|
|
7105
|
+
let result = [];
|
|
7106
|
+
for (let { from, to } of visible) {
|
|
7107
|
+
from = Math.max(view.state.doc.lineAt(from).from, from - maxLength);
|
|
7108
|
+
to = Math.min(view.state.doc.lineAt(to).to, to + maxLength);
|
|
7109
|
+
if (result.length && result[result.length - 1].to >= from)
|
|
7110
|
+
result[result.length - 1].to = to;
|
|
7111
|
+
else
|
|
7112
|
+
result.push({ from, to });
|
|
7113
|
+
}
|
|
7114
|
+
return result;
|
|
7115
|
+
}
|
|
6978
7116
|
/**
|
|
6979
7117
|
Helper class used to make it easier to maintain decorations on
|
|
6980
7118
|
visible code that matches a given regular expression. To be used
|
|
@@ -6986,12 +7124,13 @@ class MatchDecorator {
|
|
|
6986
7124
|
Create a decorator.
|
|
6987
7125
|
*/
|
|
6988
7126
|
constructor(config) {
|
|
6989
|
-
let { regexp, decoration, boundary } = config;
|
|
7127
|
+
let { regexp, decoration, boundary, maxLength = 1000 } = config;
|
|
6990
7128
|
if (!regexp.global)
|
|
6991
7129
|
throw new RangeError("The regular expression given to MatchDecorator should have its 'g' flag set");
|
|
6992
7130
|
this.regexp = regexp;
|
|
6993
7131
|
this.getDeco = typeof decoration == "function" ? decoration : () => decoration;
|
|
6994
7132
|
this.boundary = boundary;
|
|
7133
|
+
this.maxLength = maxLength;
|
|
6995
7134
|
}
|
|
6996
7135
|
/**
|
|
6997
7136
|
Compute the full set of decorations for matches in the given
|
|
@@ -7000,7 +7139,7 @@ class MatchDecorator {
|
|
|
7000
7139
|
*/
|
|
7001
7140
|
createDeco(view) {
|
|
7002
7141
|
let build = new rangeset.RangeSetBuilder();
|
|
7003
|
-
for (let { from, to } of view.
|
|
7142
|
+
for (let { from, to } of matchRanges(view, this.maxLength))
|
|
7004
7143
|
iterMatches(view.state.doc, this.regexp, from, to, (a, b, m) => build.add(a, b, this.getDeco(m, view, a)));
|
|
7005
7144
|
return build.finish();
|
|
7006
7145
|
}
|
|
@@ -7317,6 +7456,7 @@ exports.ViewUpdate = ViewUpdate;
|
|
|
7317
7456
|
exports.WidgetType = WidgetType;
|
|
7318
7457
|
exports.__test = __test;
|
|
7319
7458
|
exports.drawSelection = drawSelection;
|
|
7459
|
+
exports.dropCursor = dropCursor;
|
|
7320
7460
|
exports.highlightActiveLine = highlightActiveLine;
|
|
7321
7461
|
exports.highlightSpecialChars = highlightSpecialChars;
|
|
7322
7462
|
exports.keymap = keymap;
|