@codemirror/view 0.19.28 → 0.19.29
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 +12 -0
- package/dist/index.cjs +126 -99
- package/dist/index.d.ts +5 -0
- package/dist/index.js +126 -99
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## 0.19.29 (2021-12-09)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix a bug that could cause out-of-view editors to get a nonsensical viewport and fail to scroll into view when asked to.
|
|
6
|
+
|
|
7
|
+
Fix a bug where would return 0 when clicking below the content if the last line was replaced with a block widget decoration.
|
|
8
|
+
|
|
9
|
+
Fix an issue where clicking at the position of the previous cursor in a blurred editor would cause the selection to reset to the start of the document.
|
|
10
|
+
|
|
11
|
+
Fix an issue where composition could be interrupted if the browser created a new node inside a mark decoration node.
|
|
12
|
+
|
|
1
13
|
## 0.19.28 (2021-12-08)
|
|
2
14
|
|
|
3
15
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -2254,6 +2254,78 @@ function moveVisually(line, order, dir, start, forward) {
|
|
|
2254
2254
|
return state.EditorSelection.cursor(nextIndex + line.from, forward ? -1 : 1, span.level);
|
|
2255
2255
|
}
|
|
2256
2256
|
|
|
2257
|
+
class DOMReader {
|
|
2258
|
+
constructor(points, view) {
|
|
2259
|
+
this.points = points;
|
|
2260
|
+
this.view = view;
|
|
2261
|
+
this.text = "";
|
|
2262
|
+
this.lineBreak = view.state.lineBreak;
|
|
2263
|
+
}
|
|
2264
|
+
readRange(start, end) {
|
|
2265
|
+
if (!start)
|
|
2266
|
+
return this;
|
|
2267
|
+
let parent = start.parentNode;
|
|
2268
|
+
for (let cur = start;;) {
|
|
2269
|
+
this.findPointBefore(parent, cur);
|
|
2270
|
+
this.readNode(cur);
|
|
2271
|
+
let next = cur.nextSibling;
|
|
2272
|
+
if (next == end)
|
|
2273
|
+
break;
|
|
2274
|
+
let view = ContentView.get(cur), nextView = ContentView.get(next);
|
|
2275
|
+
if (view && nextView ? view.breakAfter :
|
|
2276
|
+
(view ? view.breakAfter : isBlockElement(cur)) ||
|
|
2277
|
+
(isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore)))
|
|
2278
|
+
this.text += this.lineBreak;
|
|
2279
|
+
cur = next;
|
|
2280
|
+
}
|
|
2281
|
+
this.findPointBefore(parent, end);
|
|
2282
|
+
return this;
|
|
2283
|
+
}
|
|
2284
|
+
readNode(node) {
|
|
2285
|
+
if (node.cmIgnore)
|
|
2286
|
+
return;
|
|
2287
|
+
let view = ContentView.get(node);
|
|
2288
|
+
let fromView = view && view.overrideDOMText;
|
|
2289
|
+
let text;
|
|
2290
|
+
if (fromView != null)
|
|
2291
|
+
text = fromView.sliceString(0, undefined, this.lineBreak);
|
|
2292
|
+
else if (node.nodeType == 3)
|
|
2293
|
+
text = node.nodeValue;
|
|
2294
|
+
else if (node.nodeName == "BR")
|
|
2295
|
+
text = node.nextSibling ? this.lineBreak : "";
|
|
2296
|
+
else if (node.nodeType == 1)
|
|
2297
|
+
this.readRange(node.firstChild, null);
|
|
2298
|
+
if (text != null) {
|
|
2299
|
+
this.findPointIn(node, text.length);
|
|
2300
|
+
this.text += text;
|
|
2301
|
+
// Chrome inserts two newlines when pressing shift-enter at the
|
|
2302
|
+
// end of a line. This drops one of those.
|
|
2303
|
+
if (browser.chrome && this.view.inputState.lastKeyCode == 13 && !node.nextSibling && /\n\n$/.test(this.text))
|
|
2304
|
+
this.text = this.text.slice(0, -1);
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
findPointBefore(node, next) {
|
|
2308
|
+
for (let point of this.points)
|
|
2309
|
+
if (point.node == node && node.childNodes[point.offset] == next)
|
|
2310
|
+
point.pos = this.text.length;
|
|
2311
|
+
}
|
|
2312
|
+
findPointIn(node, maxLen) {
|
|
2313
|
+
for (let point of this.points)
|
|
2314
|
+
if (point.node == node)
|
|
2315
|
+
point.pos = this.text.length + Math.min(point.offset, maxLen);
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
function isBlockElement(node) {
|
|
2319
|
+
return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
|
|
2320
|
+
}
|
|
2321
|
+
class DOMPoint {
|
|
2322
|
+
constructor(node, offset) {
|
|
2323
|
+
this.node = node;
|
|
2324
|
+
this.offset = offset;
|
|
2325
|
+
this.pos = -1;
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
|
|
2257
2329
|
class DocView extends ContentView {
|
|
2258
2330
|
constructor(view) {
|
|
2259
2331
|
super();
|
|
@@ -2303,7 +2375,7 @@ class DocView extends ContentView {
|
|
|
2303
2375
|
}
|
|
2304
2376
|
if (this.view.inputState.composing < 0)
|
|
2305
2377
|
this.compositionDeco = Decoration.none;
|
|
2306
|
-
else if (update.transactions.length)
|
|
2378
|
+
else if (update.transactions.length || this.dirty)
|
|
2307
2379
|
this.compositionDeco = computeCompositionDeco(this.view, update.changes);
|
|
2308
2380
|
// When the DOM nodes around the selection are moved to another
|
|
2309
2381
|
// parent, Chrome sometimes reports a different selection through
|
|
@@ -2454,7 +2526,7 @@ class DocView extends ContentView {
|
|
|
2454
2526
|
this.impreciseHead = head.precise ? null : new DOMPos(domSel.focusNode, domSel.focusOffset);
|
|
2455
2527
|
}
|
|
2456
2528
|
enforceCursorAssoc() {
|
|
2457
|
-
if (this.
|
|
2529
|
+
if (this.compositionDeco.size)
|
|
2458
2530
|
return;
|
|
2459
2531
|
let cursor = this.view.state.selection.main;
|
|
2460
2532
|
let sel = getSelection(this.root);
|
|
@@ -2679,7 +2751,8 @@ function computeCompositionDeco(view, changes) {
|
|
|
2679
2751
|
topNode = cView.dom;
|
|
2680
2752
|
}
|
|
2681
2753
|
let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
|
|
2682
|
-
let text =
|
|
2754
|
+
let { state } = view, text = topNode.nodeType == 3 ? topNode.nodeValue :
|
|
2755
|
+
new DOMReader([], view).readRange(topNode.firstChild, null).text;
|
|
2683
2756
|
if (newTo - newFrom < text.length) {
|
|
2684
2757
|
if (state.sliceDoc(newFrom, Math.min(state.doc.length, newFrom + text.length)) == text)
|
|
2685
2758
|
newTo = newFrom + text.length;
|
|
@@ -2883,21 +2956,29 @@ function domPosInText(node, x, y) {
|
|
|
2883
2956
|
function posAtCoords(view, { x, y }, precise, bias = -1) {
|
|
2884
2957
|
var _a;
|
|
2885
2958
|
let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop;
|
|
2886
|
-
let
|
|
2887
|
-
|
|
2888
|
-
|
|
2959
|
+
let block, yOffset = y - docTop, { docHeight } = view.viewState;
|
|
2960
|
+
if (yOffset < 0 || yOffset > docHeight) {
|
|
2961
|
+
if (precise)
|
|
2962
|
+
return null;
|
|
2963
|
+
yOffset = yOffset < 0 ? 0 : docHeight;
|
|
2964
|
+
}
|
|
2965
|
+
// Scan for a text block near the queried y position
|
|
2966
|
+
for (let halfLine = view.defaultLineHeight / 2, bounced = false;;) {
|
|
2889
2967
|
block = view.elementAtHeight(yOffset);
|
|
2890
|
-
if (block.
|
|
2891
|
-
|
|
2892
|
-
|
|
2968
|
+
if (block.type == exports.BlockType.Text)
|
|
2969
|
+
break;
|
|
2970
|
+
for (;;) {
|
|
2971
|
+
// Move the y position out of this block
|
|
2972
|
+
yOffset = bias > 0 ? block.bottom + halfLine : block.top - halfLine;
|
|
2973
|
+
if (yOffset >= 0 && yOffset <= docHeight)
|
|
2974
|
+
break;
|
|
2975
|
+
// If the document consists entirely of replaced widgets, we
|
|
2976
|
+
// won't find a text block, so return 0
|
|
2893
2977
|
if (bounced)
|
|
2894
2978
|
return precise ? null : 0;
|
|
2895
|
-
|
|
2896
|
-
|
|
2979
|
+
bounced = true;
|
|
2980
|
+
bias = -bias;
|
|
2897
2981
|
}
|
|
2898
|
-
if (block.type == exports.BlockType.Text)
|
|
2899
|
-
break;
|
|
2900
|
-
yOffset = bias > 0 ? block.bottom + halfLine : block.top - halfLine;
|
|
2901
2982
|
}
|
|
2902
2983
|
y = docTop + yOffset;
|
|
2903
2984
|
let lineStart = block.from;
|
|
@@ -3238,10 +3319,10 @@ class InputState {
|
|
|
3238
3319
|
return (event.type == "keydown" && event.keyCode != 229) ||
|
|
3239
3320
|
event.type == "compositionend" && !browser.ios;
|
|
3240
3321
|
}
|
|
3241
|
-
startMouseSelection(
|
|
3322
|
+
startMouseSelection(mouseSelection) {
|
|
3242
3323
|
if (this.mouseSelection)
|
|
3243
3324
|
this.mouseSelection.destroy();
|
|
3244
|
-
this.mouseSelection =
|
|
3325
|
+
this.mouseSelection = mouseSelection;
|
|
3245
3326
|
}
|
|
3246
3327
|
update(update) {
|
|
3247
3328
|
if (this.mouseSelection)
|
|
@@ -3262,10 +3343,10 @@ const PendingKeys = [
|
|
|
3262
3343
|
// Key codes for modifier keys
|
|
3263
3344
|
const modifierCodes = [16, 17, 18, 20, 91, 92, 224, 225];
|
|
3264
3345
|
class MouseSelection {
|
|
3265
|
-
constructor(
|
|
3266
|
-
this.inputState = inputState;
|
|
3346
|
+
constructor(view, startEvent, style, mustSelect) {
|
|
3267
3347
|
this.view = view;
|
|
3268
3348
|
this.style = style;
|
|
3349
|
+
this.mustSelect = mustSelect;
|
|
3269
3350
|
this.lastEvent = startEvent;
|
|
3270
3351
|
let doc = view.contentDOM.ownerDocument;
|
|
3271
3352
|
doc.addEventListener("mousemove", this.move = this.move.bind(this));
|
|
@@ -3299,16 +3380,18 @@ class MouseSelection {
|
|
|
3299
3380
|
let doc = this.view.contentDOM.ownerDocument;
|
|
3300
3381
|
doc.removeEventListener("mousemove", this.move);
|
|
3301
3382
|
doc.removeEventListener("mouseup", this.up);
|
|
3302
|
-
this.inputState.mouseSelection = null;
|
|
3383
|
+
this.view.inputState.mouseSelection = null;
|
|
3303
3384
|
}
|
|
3304
3385
|
select(event) {
|
|
3305
3386
|
let selection = this.style.get(event, this.extend, this.multiple);
|
|
3306
|
-
if (!selection.eq(this.view.state.selection) ||
|
|
3387
|
+
if (this.mustSelect || !selection.eq(this.view.state.selection) ||
|
|
3388
|
+
selection.main.assoc != this.view.state.selection.main.assoc)
|
|
3307
3389
|
this.view.dispatch({
|
|
3308
3390
|
selection,
|
|
3309
3391
|
userEvent: "select.pointer",
|
|
3310
3392
|
scrollIntoView: true
|
|
3311
3393
|
});
|
|
3394
|
+
this.mustSelect = false;
|
|
3312
3395
|
}
|
|
3313
3396
|
update(update) {
|
|
3314
3397
|
if (update.docChanged && this.dragging)
|
|
@@ -3427,9 +3510,10 @@ handlers.mousedown = (view, event) => {
|
|
|
3427
3510
|
if (!style && event.button == 0)
|
|
3428
3511
|
style = basicMouseSelection(view, event);
|
|
3429
3512
|
if (style) {
|
|
3430
|
-
|
|
3513
|
+
let mustFocus = view.root.activeElement != view.contentDOM;
|
|
3514
|
+
if (mustFocus)
|
|
3431
3515
|
view.observer.ignore(() => focusPreventScroll(view.contentDOM));
|
|
3432
|
-
view.inputState.startMouseSelection(view, event, style);
|
|
3516
|
+
view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
|
|
3433
3517
|
}
|
|
3434
3518
|
};
|
|
3435
3519
|
function rangeForClick(view, pos, bias, type) {
|
|
@@ -4421,8 +4505,8 @@ function visiblePixelRange(dom, paddingTop) {
|
|
|
4421
4505
|
break;
|
|
4422
4506
|
}
|
|
4423
4507
|
}
|
|
4424
|
-
return { left: left - rect.left, right: right - rect.left,
|
|
4425
|
-
top: top - (rect.top + paddingTop), bottom: bottom - (rect.top + paddingTop) };
|
|
4508
|
+
return { left: left - rect.left, right: Math.max(left, right) - rect.left,
|
|
4509
|
+
top: top - (rect.top + paddingTop), bottom: Math.max(top, bottom) - (rect.top + paddingTop) };
|
|
4426
4510
|
}
|
|
4427
4511
|
// Line gaps are placeholder widgets used to hide pieces of overlong
|
|
4428
4512
|
// lines within the viewport, as a kludge to keep the editor
|
|
@@ -4649,7 +4733,7 @@ class ViewState {
|
|
|
4649
4733
|
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
4734
|
// If scrollTarget is given, make sure the viewport includes that position
|
|
4651
4735
|
if (scrollTarget) {
|
|
4652
|
-
let { head } = scrollTarget.range, viewHeight =
|
|
4736
|
+
let { head } = scrollTarget.range, viewHeight = this.editorHeight;
|
|
4653
4737
|
if (head < viewport.from || head > viewport.to) {
|
|
4654
4738
|
let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
|
|
4655
4739
|
if (scrollTarget.center)
|
|
@@ -4670,6 +4754,8 @@ class ViewState {
|
|
|
4670
4754
|
// Checks if a given viewport covers the visible part of the
|
|
4671
4755
|
// document and not too much beyond that.
|
|
4672
4756
|
viewportIsAppropriate({ from, to }, bias = 0) {
|
|
4757
|
+
if (!this.inView)
|
|
4758
|
+
return true;
|
|
4673
4759
|
let { top } = this.heightMap.lineAt(from, QueryType.ByPos, this.state.doc, 0, 0);
|
|
4674
4760
|
let { bottom } = this.heightMap.lineAt(to, QueryType.ByPos, this.state.doc, 0, 0);
|
|
4675
4761
|
let { visibleTop, visibleBottom } = this;
|
|
@@ -4776,8 +4862,11 @@ class ViewState {
|
|
|
4776
4862
|
elementAtHeight(height) {
|
|
4777
4863
|
return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.state.doc, 0, 0), this.scaler);
|
|
4778
4864
|
}
|
|
4865
|
+
get docHeight() {
|
|
4866
|
+
return this.scaler.toDOM(this.heightMap.height);
|
|
4867
|
+
}
|
|
4779
4868
|
get contentHeight() {
|
|
4780
|
-
return this.
|
|
4869
|
+
return this.docHeight + this.paddingTop + this.paddingBottom;
|
|
4781
4870
|
}
|
|
4782
4871
|
}
|
|
4783
4872
|
class Viewport {
|
|
@@ -5330,7 +5419,7 @@ class DOMObserver {
|
|
|
5330
5419
|
this.onChange(from, to, typeOver);
|
|
5331
5420
|
// The view wasn't updated
|
|
5332
5421
|
if (this.view.state == startState)
|
|
5333
|
-
this.view.
|
|
5422
|
+
this.view.update([]);
|
|
5334
5423
|
}
|
|
5335
5424
|
readMutation(rec) {
|
|
5336
5425
|
let cView = this.view.docView.nearest(rec.target);
|
|
@@ -5549,76 +5638,6 @@ function findDiff(a, b, preferredPos, preferredSide) {
|
|
|
5549
5638
|
}
|
|
5550
5639
|
return { from, toA, toB };
|
|
5551
5640
|
}
|
|
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
5641
|
function selectionPoints(view) {
|
|
5623
5642
|
let result = [];
|
|
5624
5643
|
if (view.root.activeElement != view.contentDOM)
|
|
@@ -5904,7 +5923,9 @@ class EditorView {
|
|
|
5904
5923
|
if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
|
|
5905
5924
|
break;
|
|
5906
5925
|
if (i > 5) {
|
|
5907
|
-
console.warn(this.measureRequests.length
|
|
5926
|
+
console.warn(this.measureRequests.length
|
|
5927
|
+
? "Measure loop restarted more than 5 times"
|
|
5928
|
+
: "Viewport failed to stabilize");
|
|
5908
5929
|
break;
|
|
5909
5930
|
}
|
|
5910
5931
|
let measuring = [];
|
|
@@ -5950,7 +5971,8 @@ class EditorView {
|
|
|
5950
5971
|
}
|
|
5951
5972
|
if (redrawn)
|
|
5952
5973
|
this.docView.updateSelection(true);
|
|
5953
|
-
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
|
|
5974
|
+
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
|
|
5975
|
+
this.measureRequests.length == 0)
|
|
5954
5976
|
break;
|
|
5955
5977
|
}
|
|
5956
5978
|
}
|
|
@@ -6242,6 +6264,11 @@ class EditorView {
|
|
|
6242
6264
|
Find the DOM parent node and offset (child offset if `node` is
|
|
6243
6265
|
an element, character offset when it is a text node) at the
|
|
6244
6266
|
given document position.
|
|
6267
|
+
|
|
6268
|
+
Note that for positions that aren't currently in
|
|
6269
|
+
`visibleRanges`, the resulting DOM position isn't necessarily
|
|
6270
|
+
meaningful (it may just point before or after a placeholder
|
|
6271
|
+
element).
|
|
6245
6272
|
*/
|
|
6246
6273
|
domAtPos(pos) {
|
|
6247
6274
|
return this.docView.domAtPos(pos);
|
package/dist/index.d.ts
CHANGED
|
@@ -919,6 +919,11 @@ declare class EditorView {
|
|
|
919
919
|
Find the DOM parent node and offset (child offset if `node` is
|
|
920
920
|
an element, character offset when it is a text node) at the
|
|
921
921
|
given document position.
|
|
922
|
+
|
|
923
|
+
Note that for positions that aren't currently in
|
|
924
|
+
`visibleRanges`, the resulting DOM position isn't necessarily
|
|
925
|
+
meaningful (it may just point before or after a placeholder
|
|
926
|
+
element).
|
|
922
927
|
*/
|
|
923
928
|
domAtPos(pos: number): {
|
|
924
929
|
node: Node;
|
package/dist/index.js
CHANGED
|
@@ -2249,6 +2249,78 @@ function moveVisually(line, order, dir, start, forward) {
|
|
|
2249
2249
|
return EditorSelection.cursor(nextIndex + line.from, forward ? -1 : 1, span.level);
|
|
2250
2250
|
}
|
|
2251
2251
|
|
|
2252
|
+
class DOMReader {
|
|
2253
|
+
constructor(points, view) {
|
|
2254
|
+
this.points = points;
|
|
2255
|
+
this.view = view;
|
|
2256
|
+
this.text = "";
|
|
2257
|
+
this.lineBreak = view.state.lineBreak;
|
|
2258
|
+
}
|
|
2259
|
+
readRange(start, end) {
|
|
2260
|
+
if (!start)
|
|
2261
|
+
return this;
|
|
2262
|
+
let parent = start.parentNode;
|
|
2263
|
+
for (let cur = start;;) {
|
|
2264
|
+
this.findPointBefore(parent, cur);
|
|
2265
|
+
this.readNode(cur);
|
|
2266
|
+
let next = cur.nextSibling;
|
|
2267
|
+
if (next == end)
|
|
2268
|
+
break;
|
|
2269
|
+
let view = ContentView.get(cur), nextView = ContentView.get(next);
|
|
2270
|
+
if (view && nextView ? view.breakAfter :
|
|
2271
|
+
(view ? view.breakAfter : isBlockElement(cur)) ||
|
|
2272
|
+
(isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore)))
|
|
2273
|
+
this.text += this.lineBreak;
|
|
2274
|
+
cur = next;
|
|
2275
|
+
}
|
|
2276
|
+
this.findPointBefore(parent, end);
|
|
2277
|
+
return this;
|
|
2278
|
+
}
|
|
2279
|
+
readNode(node) {
|
|
2280
|
+
if (node.cmIgnore)
|
|
2281
|
+
return;
|
|
2282
|
+
let view = ContentView.get(node);
|
|
2283
|
+
let fromView = view && view.overrideDOMText;
|
|
2284
|
+
let text;
|
|
2285
|
+
if (fromView != null)
|
|
2286
|
+
text = fromView.sliceString(0, undefined, this.lineBreak);
|
|
2287
|
+
else if (node.nodeType == 3)
|
|
2288
|
+
text = node.nodeValue;
|
|
2289
|
+
else if (node.nodeName == "BR")
|
|
2290
|
+
text = node.nextSibling ? this.lineBreak : "";
|
|
2291
|
+
else if (node.nodeType == 1)
|
|
2292
|
+
this.readRange(node.firstChild, null);
|
|
2293
|
+
if (text != null) {
|
|
2294
|
+
this.findPointIn(node, text.length);
|
|
2295
|
+
this.text += text;
|
|
2296
|
+
// Chrome inserts two newlines when pressing shift-enter at the
|
|
2297
|
+
// end of a line. This drops one of those.
|
|
2298
|
+
if (browser.chrome && this.view.inputState.lastKeyCode == 13 && !node.nextSibling && /\n\n$/.test(this.text))
|
|
2299
|
+
this.text = this.text.slice(0, -1);
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
findPointBefore(node, next) {
|
|
2303
|
+
for (let point of this.points)
|
|
2304
|
+
if (point.node == node && node.childNodes[point.offset] == next)
|
|
2305
|
+
point.pos = this.text.length;
|
|
2306
|
+
}
|
|
2307
|
+
findPointIn(node, maxLen) {
|
|
2308
|
+
for (let point of this.points)
|
|
2309
|
+
if (point.node == node)
|
|
2310
|
+
point.pos = this.text.length + Math.min(point.offset, maxLen);
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
function isBlockElement(node) {
|
|
2314
|
+
return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
|
|
2315
|
+
}
|
|
2316
|
+
class DOMPoint {
|
|
2317
|
+
constructor(node, offset) {
|
|
2318
|
+
this.node = node;
|
|
2319
|
+
this.offset = offset;
|
|
2320
|
+
this.pos = -1;
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2252
2324
|
class DocView extends ContentView {
|
|
2253
2325
|
constructor(view) {
|
|
2254
2326
|
super();
|
|
@@ -2298,7 +2370,7 @@ class DocView extends ContentView {
|
|
|
2298
2370
|
}
|
|
2299
2371
|
if (this.view.inputState.composing < 0)
|
|
2300
2372
|
this.compositionDeco = Decoration.none;
|
|
2301
|
-
else if (update.transactions.length)
|
|
2373
|
+
else if (update.transactions.length || this.dirty)
|
|
2302
2374
|
this.compositionDeco = computeCompositionDeco(this.view, update.changes);
|
|
2303
2375
|
// When the DOM nodes around the selection are moved to another
|
|
2304
2376
|
// parent, Chrome sometimes reports a different selection through
|
|
@@ -2449,7 +2521,7 @@ class DocView extends ContentView {
|
|
|
2449
2521
|
this.impreciseHead = head.precise ? null : new DOMPos(domSel.focusNode, domSel.focusOffset);
|
|
2450
2522
|
}
|
|
2451
2523
|
enforceCursorAssoc() {
|
|
2452
|
-
if (this.
|
|
2524
|
+
if (this.compositionDeco.size)
|
|
2453
2525
|
return;
|
|
2454
2526
|
let cursor = this.view.state.selection.main;
|
|
2455
2527
|
let sel = getSelection(this.root);
|
|
@@ -2674,7 +2746,8 @@ function computeCompositionDeco(view, changes) {
|
|
|
2674
2746
|
topNode = cView.dom;
|
|
2675
2747
|
}
|
|
2676
2748
|
let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
|
|
2677
|
-
let text =
|
|
2749
|
+
let { state } = view, text = topNode.nodeType == 3 ? topNode.nodeValue :
|
|
2750
|
+
new DOMReader([], view).readRange(topNode.firstChild, null).text;
|
|
2678
2751
|
if (newTo - newFrom < text.length) {
|
|
2679
2752
|
if (state.sliceDoc(newFrom, Math.min(state.doc.length, newFrom + text.length)) == text)
|
|
2680
2753
|
newTo = newFrom + text.length;
|
|
@@ -2878,21 +2951,29 @@ function domPosInText(node, x, y) {
|
|
|
2878
2951
|
function posAtCoords(view, { x, y }, precise, bias = -1) {
|
|
2879
2952
|
var _a;
|
|
2880
2953
|
let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop;
|
|
2881
|
-
let
|
|
2882
|
-
|
|
2883
|
-
|
|
2954
|
+
let block, yOffset = y - docTop, { docHeight } = view.viewState;
|
|
2955
|
+
if (yOffset < 0 || yOffset > docHeight) {
|
|
2956
|
+
if (precise)
|
|
2957
|
+
return null;
|
|
2958
|
+
yOffset = yOffset < 0 ? 0 : docHeight;
|
|
2959
|
+
}
|
|
2960
|
+
// Scan for a text block near the queried y position
|
|
2961
|
+
for (let halfLine = view.defaultLineHeight / 2, bounced = false;;) {
|
|
2884
2962
|
block = view.elementAtHeight(yOffset);
|
|
2885
|
-
if (block.
|
|
2886
|
-
|
|
2887
|
-
|
|
2963
|
+
if (block.type == BlockType.Text)
|
|
2964
|
+
break;
|
|
2965
|
+
for (;;) {
|
|
2966
|
+
// Move the y position out of this block
|
|
2967
|
+
yOffset = bias > 0 ? block.bottom + halfLine : block.top - halfLine;
|
|
2968
|
+
if (yOffset >= 0 && yOffset <= docHeight)
|
|
2969
|
+
break;
|
|
2970
|
+
// If the document consists entirely of replaced widgets, we
|
|
2971
|
+
// won't find a text block, so return 0
|
|
2888
2972
|
if (bounced)
|
|
2889
2973
|
return precise ? null : 0;
|
|
2890
|
-
|
|
2891
|
-
|
|
2974
|
+
bounced = true;
|
|
2975
|
+
bias = -bias;
|
|
2892
2976
|
}
|
|
2893
|
-
if (block.type == BlockType.Text)
|
|
2894
|
-
break;
|
|
2895
|
-
yOffset = bias > 0 ? block.bottom + halfLine : block.top - halfLine;
|
|
2896
2977
|
}
|
|
2897
2978
|
y = docTop + yOffset;
|
|
2898
2979
|
let lineStart = block.from;
|
|
@@ -3233,10 +3314,10 @@ class InputState {
|
|
|
3233
3314
|
return (event.type == "keydown" && event.keyCode != 229) ||
|
|
3234
3315
|
event.type == "compositionend" && !browser.ios;
|
|
3235
3316
|
}
|
|
3236
|
-
startMouseSelection(
|
|
3317
|
+
startMouseSelection(mouseSelection) {
|
|
3237
3318
|
if (this.mouseSelection)
|
|
3238
3319
|
this.mouseSelection.destroy();
|
|
3239
|
-
this.mouseSelection =
|
|
3320
|
+
this.mouseSelection = mouseSelection;
|
|
3240
3321
|
}
|
|
3241
3322
|
update(update) {
|
|
3242
3323
|
if (this.mouseSelection)
|
|
@@ -3257,10 +3338,10 @@ const PendingKeys = [
|
|
|
3257
3338
|
// Key codes for modifier keys
|
|
3258
3339
|
const modifierCodes = [16, 17, 18, 20, 91, 92, 224, 225];
|
|
3259
3340
|
class MouseSelection {
|
|
3260
|
-
constructor(
|
|
3261
|
-
this.inputState = inputState;
|
|
3341
|
+
constructor(view, startEvent, style, mustSelect) {
|
|
3262
3342
|
this.view = view;
|
|
3263
3343
|
this.style = style;
|
|
3344
|
+
this.mustSelect = mustSelect;
|
|
3264
3345
|
this.lastEvent = startEvent;
|
|
3265
3346
|
let doc = view.contentDOM.ownerDocument;
|
|
3266
3347
|
doc.addEventListener("mousemove", this.move = this.move.bind(this));
|
|
@@ -3294,16 +3375,18 @@ class MouseSelection {
|
|
|
3294
3375
|
let doc = this.view.contentDOM.ownerDocument;
|
|
3295
3376
|
doc.removeEventListener("mousemove", this.move);
|
|
3296
3377
|
doc.removeEventListener("mouseup", this.up);
|
|
3297
|
-
this.inputState.mouseSelection = null;
|
|
3378
|
+
this.view.inputState.mouseSelection = null;
|
|
3298
3379
|
}
|
|
3299
3380
|
select(event) {
|
|
3300
3381
|
let selection = this.style.get(event, this.extend, this.multiple);
|
|
3301
|
-
if (!selection.eq(this.view.state.selection) ||
|
|
3382
|
+
if (this.mustSelect || !selection.eq(this.view.state.selection) ||
|
|
3383
|
+
selection.main.assoc != this.view.state.selection.main.assoc)
|
|
3302
3384
|
this.view.dispatch({
|
|
3303
3385
|
selection,
|
|
3304
3386
|
userEvent: "select.pointer",
|
|
3305
3387
|
scrollIntoView: true
|
|
3306
3388
|
});
|
|
3389
|
+
this.mustSelect = false;
|
|
3307
3390
|
}
|
|
3308
3391
|
update(update) {
|
|
3309
3392
|
if (update.docChanged && this.dragging)
|
|
@@ -3422,9 +3505,10 @@ handlers.mousedown = (view, event) => {
|
|
|
3422
3505
|
if (!style && event.button == 0)
|
|
3423
3506
|
style = basicMouseSelection(view, event);
|
|
3424
3507
|
if (style) {
|
|
3425
|
-
|
|
3508
|
+
let mustFocus = view.root.activeElement != view.contentDOM;
|
|
3509
|
+
if (mustFocus)
|
|
3426
3510
|
view.observer.ignore(() => focusPreventScroll(view.contentDOM));
|
|
3427
|
-
view.inputState.startMouseSelection(view, event, style);
|
|
3511
|
+
view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
|
|
3428
3512
|
}
|
|
3429
3513
|
};
|
|
3430
3514
|
function rangeForClick(view, pos, bias, type) {
|
|
@@ -4415,8 +4499,8 @@ function visiblePixelRange(dom, paddingTop) {
|
|
|
4415
4499
|
break;
|
|
4416
4500
|
}
|
|
4417
4501
|
}
|
|
4418
|
-
return { left: left - rect.left, right: right - rect.left,
|
|
4419
|
-
top: top - (rect.top + paddingTop), bottom: bottom - (rect.top + paddingTop) };
|
|
4502
|
+
return { left: left - rect.left, right: Math.max(left, right) - rect.left,
|
|
4503
|
+
top: top - (rect.top + paddingTop), bottom: Math.max(top, bottom) - (rect.top + paddingTop) };
|
|
4420
4504
|
}
|
|
4421
4505
|
// Line gaps are placeholder widgets used to hide pieces of overlong
|
|
4422
4506
|
// lines within the viewport, as a kludge to keep the editor
|
|
@@ -4643,7 +4727,7 @@ class ViewState {
|
|
|
4643
4727
|
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);
|
|
4644
4728
|
// If scrollTarget is given, make sure the viewport includes that position
|
|
4645
4729
|
if (scrollTarget) {
|
|
4646
|
-
let { head } = scrollTarget.range, viewHeight =
|
|
4730
|
+
let { head } = scrollTarget.range, viewHeight = this.editorHeight;
|
|
4647
4731
|
if (head < viewport.from || head > viewport.to) {
|
|
4648
4732
|
let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
|
|
4649
4733
|
if (scrollTarget.center)
|
|
@@ -4664,6 +4748,8 @@ class ViewState {
|
|
|
4664
4748
|
// Checks if a given viewport covers the visible part of the
|
|
4665
4749
|
// document and not too much beyond that.
|
|
4666
4750
|
viewportIsAppropriate({ from, to }, bias = 0) {
|
|
4751
|
+
if (!this.inView)
|
|
4752
|
+
return true;
|
|
4667
4753
|
let { top } = this.heightMap.lineAt(from, QueryType.ByPos, this.state.doc, 0, 0);
|
|
4668
4754
|
let { bottom } = this.heightMap.lineAt(to, QueryType.ByPos, this.state.doc, 0, 0);
|
|
4669
4755
|
let { visibleTop, visibleBottom } = this;
|
|
@@ -4770,8 +4856,11 @@ class ViewState {
|
|
|
4770
4856
|
elementAtHeight(height) {
|
|
4771
4857
|
return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.state.doc, 0, 0), this.scaler);
|
|
4772
4858
|
}
|
|
4859
|
+
get docHeight() {
|
|
4860
|
+
return this.scaler.toDOM(this.heightMap.height);
|
|
4861
|
+
}
|
|
4773
4862
|
get contentHeight() {
|
|
4774
|
-
return this.
|
|
4863
|
+
return this.docHeight + this.paddingTop + this.paddingBottom;
|
|
4775
4864
|
}
|
|
4776
4865
|
}
|
|
4777
4866
|
class Viewport {
|
|
@@ -5324,7 +5413,7 @@ class DOMObserver {
|
|
|
5324
5413
|
this.onChange(from, to, typeOver);
|
|
5325
5414
|
// The view wasn't updated
|
|
5326
5415
|
if (this.view.state == startState)
|
|
5327
|
-
this.view.
|
|
5416
|
+
this.view.update([]);
|
|
5328
5417
|
}
|
|
5329
5418
|
readMutation(rec) {
|
|
5330
5419
|
let cView = this.view.docView.nearest(rec.target);
|
|
@@ -5543,76 +5632,6 @@ function findDiff(a, b, preferredPos, preferredSide) {
|
|
|
5543
5632
|
}
|
|
5544
5633
|
return { from, toA, toB };
|
|
5545
5634
|
}
|
|
5546
|
-
class DOMReader {
|
|
5547
|
-
constructor(points, view) {
|
|
5548
|
-
this.points = points;
|
|
5549
|
-
this.view = view;
|
|
5550
|
-
this.text = "";
|
|
5551
|
-
this.lineBreak = view.state.lineBreak;
|
|
5552
|
-
}
|
|
5553
|
-
readRange(start, end) {
|
|
5554
|
-
if (!start)
|
|
5555
|
-
return;
|
|
5556
|
-
let parent = start.parentNode;
|
|
5557
|
-
for (let cur = start;;) {
|
|
5558
|
-
this.findPointBefore(parent, cur);
|
|
5559
|
-
this.readNode(cur);
|
|
5560
|
-
let next = cur.nextSibling;
|
|
5561
|
-
if (next == end)
|
|
5562
|
-
break;
|
|
5563
|
-
let view = ContentView.get(cur), nextView = ContentView.get(next);
|
|
5564
|
-
if (view && nextView ? view.breakAfter :
|
|
5565
|
-
(view ? view.breakAfter : isBlockElement(cur)) ||
|
|
5566
|
-
(isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore)))
|
|
5567
|
-
this.text += this.lineBreak;
|
|
5568
|
-
cur = next;
|
|
5569
|
-
}
|
|
5570
|
-
this.findPointBefore(parent, end);
|
|
5571
|
-
}
|
|
5572
|
-
readNode(node) {
|
|
5573
|
-
if (node.cmIgnore)
|
|
5574
|
-
return;
|
|
5575
|
-
let view = ContentView.get(node);
|
|
5576
|
-
let fromView = view && view.overrideDOMText;
|
|
5577
|
-
let text;
|
|
5578
|
-
if (fromView != null)
|
|
5579
|
-
text = fromView.sliceString(0, undefined, this.lineBreak);
|
|
5580
|
-
else if (node.nodeType == 3)
|
|
5581
|
-
text = node.nodeValue;
|
|
5582
|
-
else if (node.nodeName == "BR")
|
|
5583
|
-
text = node.nextSibling ? this.lineBreak : "";
|
|
5584
|
-
else if (node.nodeType == 1)
|
|
5585
|
-
this.readRange(node.firstChild, null);
|
|
5586
|
-
if (text != null) {
|
|
5587
|
-
this.findPointIn(node, text.length);
|
|
5588
|
-
this.text += text;
|
|
5589
|
-
// Chrome inserts two newlines when pressing shift-enter at the
|
|
5590
|
-
// end of a line. This drops one of those.
|
|
5591
|
-
if (browser.chrome && this.view.inputState.lastKeyCode == 13 && !node.nextSibling && /\n\n$/.test(this.text))
|
|
5592
|
-
this.text = this.text.slice(0, -1);
|
|
5593
|
-
}
|
|
5594
|
-
}
|
|
5595
|
-
findPointBefore(node, next) {
|
|
5596
|
-
for (let point of this.points)
|
|
5597
|
-
if (point.node == node && node.childNodes[point.offset] == next)
|
|
5598
|
-
point.pos = this.text.length;
|
|
5599
|
-
}
|
|
5600
|
-
findPointIn(node, maxLen) {
|
|
5601
|
-
for (let point of this.points)
|
|
5602
|
-
if (point.node == node)
|
|
5603
|
-
point.pos = this.text.length + Math.min(point.offset, maxLen);
|
|
5604
|
-
}
|
|
5605
|
-
}
|
|
5606
|
-
function isBlockElement(node) {
|
|
5607
|
-
return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
|
|
5608
|
-
}
|
|
5609
|
-
class DOMPoint {
|
|
5610
|
-
constructor(node, offset) {
|
|
5611
|
-
this.node = node;
|
|
5612
|
-
this.offset = offset;
|
|
5613
|
-
this.pos = -1;
|
|
5614
|
-
}
|
|
5615
|
-
}
|
|
5616
5635
|
function selectionPoints(view) {
|
|
5617
5636
|
let result = [];
|
|
5618
5637
|
if (view.root.activeElement != view.contentDOM)
|
|
@@ -5898,7 +5917,9 @@ class EditorView {
|
|
|
5898
5917
|
if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
|
|
5899
5918
|
break;
|
|
5900
5919
|
if (i > 5) {
|
|
5901
|
-
console.warn(this.measureRequests.length
|
|
5920
|
+
console.warn(this.measureRequests.length
|
|
5921
|
+
? "Measure loop restarted more than 5 times"
|
|
5922
|
+
: "Viewport failed to stabilize");
|
|
5902
5923
|
break;
|
|
5903
5924
|
}
|
|
5904
5925
|
let measuring = [];
|
|
@@ -5944,7 +5965,8 @@ class EditorView {
|
|
|
5944
5965
|
}
|
|
5945
5966
|
if (redrawn)
|
|
5946
5967
|
this.docView.updateSelection(true);
|
|
5947
|
-
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
|
|
5968
|
+
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
|
|
5969
|
+
this.measureRequests.length == 0)
|
|
5948
5970
|
break;
|
|
5949
5971
|
}
|
|
5950
5972
|
}
|
|
@@ -6236,6 +6258,11 @@ class EditorView {
|
|
|
6236
6258
|
Find the DOM parent node and offset (child offset if `node` is
|
|
6237
6259
|
an element, character offset when it is a text node) at the
|
|
6238
6260
|
given document position.
|
|
6261
|
+
|
|
6262
|
+
Note that for positions that aren't currently in
|
|
6263
|
+
`visibleRanges`, the resulting DOM position isn't necessarily
|
|
6264
|
+
meaningful (it may just point before or after a placeholder
|
|
6265
|
+
element).
|
|
6239
6266
|
*/
|
|
6240
6267
|
domAtPos(pos) {
|
|
6241
6268
|
return this.docView.domAtPos(pos);
|