@codemirror/view 0.19.40 → 0.19.41
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 +16 -0
- package/dist/index.cjs +100 -51
- package/dist/index.js +101 -52
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## 0.19.41 (2022-02-04)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix an issue where the editor's view of its content height could go out of sync with the DOM when a line-wrapping editor had its width changed, causing wrapping to change.
|
|
6
|
+
|
|
7
|
+
Fix a bug that caused the editor to draw way too much content when scrolling to a position in an editor (much) taller than the window.
|
|
8
|
+
|
|
9
|
+
Report an error when a replace decoration from a plugin crosses a line break, rather than silently ignoring it.
|
|
10
|
+
|
|
11
|
+
Fix an issue where reading DOM changes was broken when `lineSeparator` contained more than one character.
|
|
12
|
+
|
|
13
|
+
Make ordering of replace and mark decorations with the same extent and inclusivness more predictable by giving replace decorations precedence.
|
|
14
|
+
|
|
15
|
+
Fix a bug where, on Chrome, replacement across line boundaries and next to widgets could cause bogus zero-width characters to appear in the content.
|
|
16
|
+
|
|
1
17
|
## 0.19.40 (2022-01-19)
|
|
2
18
|
|
|
3
19
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -602,9 +602,8 @@ function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) {
|
|
|
602
602
|
replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd);
|
|
603
603
|
}
|
|
604
604
|
|
|
605
|
-
let
|
|
606
|
-
|
|
607
|
-
: [{ userAgent: "", vendor: "", platform: "" }, { documentElement: { style: {} } }];
|
|
605
|
+
let nav = typeof navigator != "undefined" ? navigator : { userAgent: "", vendor: "", platform: "" };
|
|
606
|
+
let doc = typeof document != "undefined" ? document : { documentElement: { style: {} } };
|
|
608
607
|
const ie_edge = /Edge\/(\d+)/.exec(nav.userAgent);
|
|
609
608
|
const ie_upto10 = /MSIE \d/.test(nav.userAgent);
|
|
610
609
|
const ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(nav.userAgent);
|
|
@@ -1131,8 +1130,8 @@ class Decoration extends rangeset.RangeValue {
|
|
|
1131
1130
|
static replace(spec) {
|
|
1132
1131
|
let block = !!spec.block;
|
|
1133
1132
|
let { start, end } = getInclusive(spec, block);
|
|
1134
|
-
let startSide =
|
|
1135
|
-
let endSide =
|
|
1133
|
+
let startSide = (start ? (block ? -300000000 /* BlockIncStart */ : -1 /* InlineIncStart */) : 400000000 /* NonIncStart */) - 1;
|
|
1134
|
+
let endSide = (end ? (block ? 200000000 /* BlockIncEnd */ : 1 /* InlineIncEnd */) : -500000000 /* NonIncEnd */) + 1;
|
|
1136
1135
|
return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
|
|
1137
1136
|
}
|
|
1138
1137
|
/**
|
|
@@ -1577,11 +1576,13 @@ class ContentBuilder {
|
|
|
1577
1576
|
this.openStart = openStart;
|
|
1578
1577
|
}
|
|
1579
1578
|
filterPoint(from, to, value, index) {
|
|
1580
|
-
if (index
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1579
|
+
if (index < this.disallowBlockEffectsBelow && value instanceof PointDecoration) {
|
|
1580
|
+
if (value.block)
|
|
1581
|
+
throw new RangeError("Block decorations may not be specified via plugins");
|
|
1582
|
+
if (to > this.doc.lineAt(this.pos).to)
|
|
1583
|
+
throw new RangeError("Decorations that replace line breaks may not be specified via plugins");
|
|
1584
|
+
}
|
|
1585
|
+
return true;
|
|
1585
1586
|
}
|
|
1586
1587
|
static build(text, from, to, decorations, pluginDecorationLength) {
|
|
1587
1588
|
let builder = new ContentBuilder(text, from, to, pluginDecorationLength);
|
|
@@ -2305,12 +2306,18 @@ function moveVisually(line, order, dir, start, forward) {
|
|
|
2305
2306
|
return state.EditorSelection.cursor(nextIndex + line.from, forward ? -1 : 1, span.level);
|
|
2306
2307
|
}
|
|
2307
2308
|
|
|
2309
|
+
const LineBreakPlaceholder = "\uffff";
|
|
2308
2310
|
class DOMReader {
|
|
2309
|
-
constructor(points,
|
|
2311
|
+
constructor(points, state$1) {
|
|
2310
2312
|
this.points = points;
|
|
2311
|
-
this.view = view;
|
|
2312
2313
|
this.text = "";
|
|
2313
|
-
this.
|
|
2314
|
+
this.lineSeparator = state$1.facet(state.EditorState.lineSeparator);
|
|
2315
|
+
}
|
|
2316
|
+
append(text) {
|
|
2317
|
+
this.text += text;
|
|
2318
|
+
}
|
|
2319
|
+
lineBreak() {
|
|
2320
|
+
this.text += LineBreakPlaceholder;
|
|
2314
2321
|
}
|
|
2315
2322
|
readRange(start, end) {
|
|
2316
2323
|
if (!start)
|
|
@@ -2326,42 +2333,61 @@ class DOMReader {
|
|
|
2326
2333
|
if (view && nextView ? view.breakAfter :
|
|
2327
2334
|
(view ? view.breakAfter : isBlockElement(cur)) ||
|
|
2328
2335
|
(isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore)))
|
|
2329
|
-
this.
|
|
2336
|
+
this.lineBreak();
|
|
2330
2337
|
cur = next;
|
|
2331
2338
|
}
|
|
2332
2339
|
this.findPointBefore(parent, end);
|
|
2333
2340
|
return this;
|
|
2334
2341
|
}
|
|
2335
2342
|
readTextNode(node) {
|
|
2336
|
-
var _a, _b;
|
|
2337
2343
|
let text = node.nodeValue;
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2344
|
+
for (let point of this.points)
|
|
2345
|
+
if (point.node == node)
|
|
2346
|
+
point.pos = this.text.length + Math.min(point.offset, text.length);
|
|
2347
|
+
for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
|
|
2348
|
+
let nextBreak = -1, breakSize = 1, m;
|
|
2349
|
+
if (this.lineSeparator) {
|
|
2350
|
+
nextBreak = text.indexOf(this.lineSeparator, off);
|
|
2351
|
+
breakSize = this.lineSeparator.length;
|
|
2352
|
+
}
|
|
2353
|
+
else if (m = re.exec(text)) {
|
|
2354
|
+
nextBreak = m.index;
|
|
2355
|
+
breakSize = m[0].length;
|
|
2356
|
+
}
|
|
2357
|
+
this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
|
|
2358
|
+
if (nextBreak < 0)
|
|
2359
|
+
break;
|
|
2360
|
+
this.lineBreak();
|
|
2361
|
+
if (breakSize > 1)
|
|
2362
|
+
for (let point of this.points)
|
|
2363
|
+
if (point.node == node && point.pos > this.text.length)
|
|
2364
|
+
point.pos -= breakSize - 1;
|
|
2365
|
+
off = nextBreak + breakSize;
|
|
2366
|
+
}
|
|
2343
2367
|
}
|
|
2344
2368
|
readNode(node) {
|
|
2345
2369
|
if (node.cmIgnore)
|
|
2346
2370
|
return;
|
|
2347
2371
|
let view = ContentView.get(node);
|
|
2348
2372
|
let fromView = view && view.overrideDOMText;
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2373
|
+
if (fromView != null) {
|
|
2374
|
+
this.findPointInside(node);
|
|
2375
|
+
for (let i = fromView.iter(); !i.next().done;) {
|
|
2376
|
+
if (i.lineBreak)
|
|
2377
|
+
this.lineBreak();
|
|
2378
|
+
else
|
|
2379
|
+
this.append(i.value);
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
else if (node.nodeType == 3) {
|
|
2383
|
+
this.readTextNode(node);
|
|
2384
|
+
}
|
|
2385
|
+
else if (node.nodeName == "BR") {
|
|
2386
|
+
if (node.nextSibling)
|
|
2387
|
+
this.lineBreak();
|
|
2388
|
+
}
|
|
2389
|
+
else if (node.nodeType == 1) {
|
|
2357
2390
|
this.readRange(node.firstChild, null);
|
|
2358
|
-
if (text != null) {
|
|
2359
|
-
this.findPointIn(node, text.length);
|
|
2360
|
-
this.text += text;
|
|
2361
|
-
// Chrome inserts two newlines when pressing shift-enter at the
|
|
2362
|
-
// end of a line. This drops one of those.
|
|
2363
|
-
if (browser.chrome && this.view.inputState.lastKeyCode == 13 && !node.nextSibling && /\n\n$/.test(this.text))
|
|
2364
|
-
this.text = this.text.slice(0, -1);
|
|
2365
2391
|
}
|
|
2366
2392
|
}
|
|
2367
2393
|
findPointBefore(node, next) {
|
|
@@ -2369,10 +2395,10 @@ class DOMReader {
|
|
|
2369
2395
|
if (point.node == node && node.childNodes[point.offset] == next)
|
|
2370
2396
|
point.pos = this.text.length;
|
|
2371
2397
|
}
|
|
2372
|
-
|
|
2398
|
+
findPointInside(node) {
|
|
2373
2399
|
for (let point of this.points)
|
|
2374
|
-
if (point.node == node)
|
|
2375
|
-
point.pos = this.text.length
|
|
2400
|
+
if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
|
|
2401
|
+
point.pos = this.text.length;
|
|
2376
2402
|
}
|
|
2377
2403
|
}
|
|
2378
2404
|
function isBlockElement(node) {
|
|
@@ -2815,16 +2841,16 @@ function computeCompositionDeco(view, changes) {
|
|
|
2815
2841
|
let { from, to, node, text: textNode } = surrounding;
|
|
2816
2842
|
let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
|
|
2817
2843
|
let { state } = view, text = node.nodeType == 3 ? node.nodeValue :
|
|
2818
|
-
new DOMReader([],
|
|
2844
|
+
new DOMReader([], state).readRange(node.firstChild, null).text;
|
|
2819
2845
|
if (newTo - newFrom < text.length) {
|
|
2820
|
-
if (state.
|
|
2846
|
+
if (state.doc.sliceString(newFrom, Math.min(state.doc.length, newFrom + text.length), LineBreakPlaceholder) == text)
|
|
2821
2847
|
newTo = newFrom + text.length;
|
|
2822
|
-
else if (state.
|
|
2848
|
+
else if (state.doc.sliceString(Math.max(0, newTo - text.length), newTo, LineBreakPlaceholder) == text)
|
|
2823
2849
|
newFrom = newTo - text.length;
|
|
2824
2850
|
else
|
|
2825
2851
|
return Decoration.none;
|
|
2826
2852
|
}
|
|
2827
|
-
else if (state.
|
|
2853
|
+
else if (state.doc.sliceString(newFrom, newTo, LineBreakPlaceholder) != text) {
|
|
2828
2854
|
return Decoration.none;
|
|
2829
2855
|
}
|
|
2830
2856
|
return Decoration.set(Decoration.replace({ widget: new CompositionWidget(node, textNode) }).range(newFrom, newTo));
|
|
@@ -4697,6 +4723,12 @@ class ViewState {
|
|
|
4697
4723
|
let refresh = this.heightOracle.mustRefreshForStyle(whiteSpace, direction);
|
|
4698
4724
|
let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != dom.clientHeight;
|
|
4699
4725
|
let result = 0, bias = 0;
|
|
4726
|
+
if (this.editorWidth != view.scrollDOM.clientWidth) {
|
|
4727
|
+
if (oracle.lineWrapping)
|
|
4728
|
+
measureContent = true;
|
|
4729
|
+
this.editorWidth = view.scrollDOM.clientWidth;
|
|
4730
|
+
result |= 8 /* Geometry */;
|
|
4731
|
+
}
|
|
4700
4732
|
if (measureContent) {
|
|
4701
4733
|
this.mustMeasureContent = false;
|
|
4702
4734
|
this.contentDOMHeight = dom.clientHeight;
|
|
@@ -4722,11 +4754,9 @@ class ViewState {
|
|
|
4722
4754
|
if (!this.inView)
|
|
4723
4755
|
return 0;
|
|
4724
4756
|
let contentWidth = dom.clientWidth;
|
|
4725
|
-
if (this.contentDOMWidth != contentWidth || this.editorHeight != view.scrollDOM.clientHeight
|
|
4726
|
-
this.editorWidth != view.scrollDOM.clientWidth) {
|
|
4757
|
+
if (this.contentDOMWidth != contentWidth || this.editorHeight != view.scrollDOM.clientHeight) {
|
|
4727
4758
|
this.contentDOMWidth = contentWidth;
|
|
4728
4759
|
this.editorHeight = view.scrollDOM.clientHeight;
|
|
4729
|
-
this.editorWidth = view.scrollDOM.clientWidth;
|
|
4730
4760
|
result |= 8 /* Geometry */;
|
|
4731
4761
|
}
|
|
4732
4762
|
if (measureContent) {
|
|
@@ -4781,8 +4811,9 @@ class ViewState {
|
|
|
4781
4811
|
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);
|
|
4782
4812
|
// If scrollTarget is given, make sure the viewport includes that position
|
|
4783
4813
|
if (scrollTarget) {
|
|
4784
|
-
let { head } = scrollTarget.range
|
|
4814
|
+
let { head } = scrollTarget.range;
|
|
4785
4815
|
if (head < viewport.from || head > viewport.to) {
|
|
4816
|
+
let viewHeight = Math.min(this.editorHeight, this.pixelViewport.bottom - this.pixelViewport.top);
|
|
4786
4817
|
let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
|
|
4787
4818
|
if (scrollTarget.y == "center")
|
|
4788
4819
|
topPos = (block.top + block.bottom) / 2 - viewHeight / 2;
|
|
@@ -5582,9 +5613,8 @@ function applyDOMChange(view, start, end, typeOver) {
|
|
|
5582
5613
|
return;
|
|
5583
5614
|
let { from, to } = bounds;
|
|
5584
5615
|
let selPoints = view.docView.impreciseHead || view.docView.impreciseAnchor ? [] : selectionPoints(view);
|
|
5585
|
-
let reader = new DOMReader(selPoints, view);
|
|
5616
|
+
let reader = new DOMReader(selPoints, view.state);
|
|
5586
5617
|
reader.readRange(bounds.startDOM, bounds.endDOM);
|
|
5587
|
-
newSel = selectionFromPoints(selPoints, from);
|
|
5588
5618
|
let preferredPos = sel.from, preferredSide = null;
|
|
5589
5619
|
// Prefer anchoring to end when Backspace is pressed (or, on
|
|
5590
5620
|
// Android, when something was deleted)
|
|
@@ -5593,10 +5623,29 @@ function applyDOMChange(view, start, end, typeOver) {
|
|
|
5593
5623
|
preferredPos = sel.to;
|
|
5594
5624
|
preferredSide = "end";
|
|
5595
5625
|
}
|
|
5596
|
-
let diff = findDiff(view.state.
|
|
5597
|
-
if (diff)
|
|
5626
|
+
let diff = findDiff(view.state.doc.sliceString(from, to, LineBreakPlaceholder), reader.text, preferredPos - from, preferredSide);
|
|
5627
|
+
if (diff) {
|
|
5628
|
+
let orig = diff;
|
|
5629
|
+
// Chrome inserts two newlines when pressing shift-enter at the
|
|
5630
|
+
// end of a line. This drops one of those.
|
|
5631
|
+
if (browser.chrome && view.inputState.lastKeyCode == 13 &&
|
|
5632
|
+
diff.toB == diff.from + 2 && reader.text.slice(diff.from, diff.toB) == LineBreakPlaceholder + LineBreakPlaceholder)
|
|
5633
|
+
diff.toB--;
|
|
5634
|
+
// Strip leading and trailing zero-width spaces from the inserted
|
|
5635
|
+
// content, to work around widget buffers being moved into text
|
|
5636
|
+
// nodes by the browser.
|
|
5637
|
+
while (diff.from < diff.toB && reader.text[diff.from] == "\u200b") {
|
|
5638
|
+
diff = { from: diff.from + 1, toA: diff.toA, toB: diff.toB };
|
|
5639
|
+
selPoints.forEach(p => p.pos -= p.pos > orig.from ? 1 : 0);
|
|
5640
|
+
}
|
|
5641
|
+
while (diff.toB > diff.from && reader.text[diff.toB - 1] == "\u200b") {
|
|
5642
|
+
diff = { from: diff.from, toA: diff.toA, toB: diff.toB - 1 };
|
|
5643
|
+
selPoints.forEach(p => p.pos -= p.pos > orig.toB ? 1 : 0);
|
|
5644
|
+
}
|
|
5598
5645
|
change = { from: from + diff.from, to: from + diff.toA,
|
|
5599
|
-
insert:
|
|
5646
|
+
insert: state.Text.of(reader.text.slice(diff.from, diff.toB).split(LineBreakPlaceholder)) };
|
|
5647
|
+
}
|
|
5648
|
+
newSel = selectionFromPoints(selPoints, from);
|
|
5600
5649
|
}
|
|
5601
5650
|
else if (view.hasFocus || !view.state.facet(editable)) {
|
|
5602
5651
|
let domSel = view.observer.selectionRange;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MapMode, Text as Text$1, Facet, StateEffect, ChangeSet, EditorSelection,
|
|
1
|
+
import { MapMode, Text as Text$1, Facet, StateEffect, ChangeSet, EditorSelection, EditorState, CharCategory, Transaction, Prec, combineConfig, StateField } from '@codemirror/state';
|
|
2
2
|
import { StyleModule } from 'style-mod';
|
|
3
3
|
import { RangeSet, RangeValue, RangeSetBuilder } from '@codemirror/rangeset';
|
|
4
4
|
export { Range } from '@codemirror/rangeset';
|
|
@@ -599,9 +599,8 @@ function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) {
|
|
|
599
599
|
replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd);
|
|
600
600
|
}
|
|
601
601
|
|
|
602
|
-
let
|
|
603
|
-
|
|
604
|
-
: [{ userAgent: "", vendor: "", platform: "" }, { documentElement: { style: {} } }];
|
|
602
|
+
let nav = typeof navigator != "undefined" ? navigator : { userAgent: "", vendor: "", platform: "" };
|
|
603
|
+
let doc = typeof document != "undefined" ? document : { documentElement: { style: {} } };
|
|
605
604
|
const ie_edge = /*@__PURE__*//Edge\/(\d+)/.exec(nav.userAgent);
|
|
606
605
|
const ie_upto10 = /*@__PURE__*//MSIE \d/.test(nav.userAgent);
|
|
607
606
|
const ie_11up = /*@__PURE__*//Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(nav.userAgent);
|
|
@@ -1127,8 +1126,8 @@ class Decoration extends RangeValue {
|
|
|
1127
1126
|
static replace(spec) {
|
|
1128
1127
|
let block = !!spec.block;
|
|
1129
1128
|
let { start, end } = getInclusive(spec, block);
|
|
1130
|
-
let startSide =
|
|
1131
|
-
let endSide =
|
|
1129
|
+
let startSide = (start ? (block ? -300000000 /* BlockIncStart */ : -1 /* InlineIncStart */) : 400000000 /* NonIncStart */) - 1;
|
|
1130
|
+
let endSide = (end ? (block ? 200000000 /* BlockIncEnd */ : 1 /* InlineIncEnd */) : -500000000 /* NonIncEnd */) + 1;
|
|
1132
1131
|
return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
|
|
1133
1132
|
}
|
|
1134
1133
|
/**
|
|
@@ -1573,11 +1572,13 @@ class ContentBuilder {
|
|
|
1573
1572
|
this.openStart = openStart;
|
|
1574
1573
|
}
|
|
1575
1574
|
filterPoint(from, to, value, index) {
|
|
1576
|
-
if (index
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1575
|
+
if (index < this.disallowBlockEffectsBelow && value instanceof PointDecoration) {
|
|
1576
|
+
if (value.block)
|
|
1577
|
+
throw new RangeError("Block decorations may not be specified via plugins");
|
|
1578
|
+
if (to > this.doc.lineAt(this.pos).to)
|
|
1579
|
+
throw new RangeError("Decorations that replace line breaks may not be specified via plugins");
|
|
1580
|
+
}
|
|
1581
|
+
return true;
|
|
1581
1582
|
}
|
|
1582
1583
|
static build(text, from, to, decorations, pluginDecorationLength) {
|
|
1583
1584
|
let builder = new ContentBuilder(text, from, to, pluginDecorationLength);
|
|
@@ -2300,12 +2301,18 @@ function moveVisually(line, order, dir, start, forward) {
|
|
|
2300
2301
|
return EditorSelection.cursor(nextIndex + line.from, forward ? -1 : 1, span.level);
|
|
2301
2302
|
}
|
|
2302
2303
|
|
|
2304
|
+
const LineBreakPlaceholder = "\uffff";
|
|
2303
2305
|
class DOMReader {
|
|
2304
|
-
constructor(points,
|
|
2306
|
+
constructor(points, state) {
|
|
2305
2307
|
this.points = points;
|
|
2306
|
-
this.view = view;
|
|
2307
2308
|
this.text = "";
|
|
2308
|
-
this.
|
|
2309
|
+
this.lineSeparator = state.facet(EditorState.lineSeparator);
|
|
2310
|
+
}
|
|
2311
|
+
append(text) {
|
|
2312
|
+
this.text += text;
|
|
2313
|
+
}
|
|
2314
|
+
lineBreak() {
|
|
2315
|
+
this.text += LineBreakPlaceholder;
|
|
2309
2316
|
}
|
|
2310
2317
|
readRange(start, end) {
|
|
2311
2318
|
if (!start)
|
|
@@ -2321,42 +2328,61 @@ class DOMReader {
|
|
|
2321
2328
|
if (view && nextView ? view.breakAfter :
|
|
2322
2329
|
(view ? view.breakAfter : isBlockElement(cur)) ||
|
|
2323
2330
|
(isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore)))
|
|
2324
|
-
this.
|
|
2331
|
+
this.lineBreak();
|
|
2325
2332
|
cur = next;
|
|
2326
2333
|
}
|
|
2327
2334
|
this.findPointBefore(parent, end);
|
|
2328
2335
|
return this;
|
|
2329
2336
|
}
|
|
2330
2337
|
readTextNode(node) {
|
|
2331
|
-
var _a, _b;
|
|
2332
2338
|
let text = node.nodeValue;
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2339
|
+
for (let point of this.points)
|
|
2340
|
+
if (point.node == node)
|
|
2341
|
+
point.pos = this.text.length + Math.min(point.offset, text.length);
|
|
2342
|
+
for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
|
|
2343
|
+
let nextBreak = -1, breakSize = 1, m;
|
|
2344
|
+
if (this.lineSeparator) {
|
|
2345
|
+
nextBreak = text.indexOf(this.lineSeparator, off);
|
|
2346
|
+
breakSize = this.lineSeparator.length;
|
|
2347
|
+
}
|
|
2348
|
+
else if (m = re.exec(text)) {
|
|
2349
|
+
nextBreak = m.index;
|
|
2350
|
+
breakSize = m[0].length;
|
|
2351
|
+
}
|
|
2352
|
+
this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
|
|
2353
|
+
if (nextBreak < 0)
|
|
2354
|
+
break;
|
|
2355
|
+
this.lineBreak();
|
|
2356
|
+
if (breakSize > 1)
|
|
2357
|
+
for (let point of this.points)
|
|
2358
|
+
if (point.node == node && point.pos > this.text.length)
|
|
2359
|
+
point.pos -= breakSize - 1;
|
|
2360
|
+
off = nextBreak + breakSize;
|
|
2361
|
+
}
|
|
2338
2362
|
}
|
|
2339
2363
|
readNode(node) {
|
|
2340
2364
|
if (node.cmIgnore)
|
|
2341
2365
|
return;
|
|
2342
2366
|
let view = ContentView.get(node);
|
|
2343
2367
|
let fromView = view && view.overrideDOMText;
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2368
|
+
if (fromView != null) {
|
|
2369
|
+
this.findPointInside(node);
|
|
2370
|
+
for (let i = fromView.iter(); !i.next().done;) {
|
|
2371
|
+
if (i.lineBreak)
|
|
2372
|
+
this.lineBreak();
|
|
2373
|
+
else
|
|
2374
|
+
this.append(i.value);
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
else if (node.nodeType == 3) {
|
|
2378
|
+
this.readTextNode(node);
|
|
2379
|
+
}
|
|
2380
|
+
else if (node.nodeName == "BR") {
|
|
2381
|
+
if (node.nextSibling)
|
|
2382
|
+
this.lineBreak();
|
|
2383
|
+
}
|
|
2384
|
+
else if (node.nodeType == 1) {
|
|
2352
2385
|
this.readRange(node.firstChild, null);
|
|
2353
|
-
if (text != null) {
|
|
2354
|
-
this.findPointIn(node, text.length);
|
|
2355
|
-
this.text += text;
|
|
2356
|
-
// Chrome inserts two newlines when pressing shift-enter at the
|
|
2357
|
-
// end of a line. This drops one of those.
|
|
2358
|
-
if (browser.chrome && this.view.inputState.lastKeyCode == 13 && !node.nextSibling && /\n\n$/.test(this.text))
|
|
2359
|
-
this.text = this.text.slice(0, -1);
|
|
2360
2386
|
}
|
|
2361
2387
|
}
|
|
2362
2388
|
findPointBefore(node, next) {
|
|
@@ -2364,10 +2390,10 @@ class DOMReader {
|
|
|
2364
2390
|
if (point.node == node && node.childNodes[point.offset] == next)
|
|
2365
2391
|
point.pos = this.text.length;
|
|
2366
2392
|
}
|
|
2367
|
-
|
|
2393
|
+
findPointInside(node) {
|
|
2368
2394
|
for (let point of this.points)
|
|
2369
|
-
if (point.node == node)
|
|
2370
|
-
point.pos = this.text.length
|
|
2395
|
+
if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
|
|
2396
|
+
point.pos = this.text.length;
|
|
2371
2397
|
}
|
|
2372
2398
|
}
|
|
2373
2399
|
function isBlockElement(node) {
|
|
@@ -2810,16 +2836,16 @@ function computeCompositionDeco(view, changes) {
|
|
|
2810
2836
|
let { from, to, node, text: textNode } = surrounding;
|
|
2811
2837
|
let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
|
|
2812
2838
|
let { state } = view, text = node.nodeType == 3 ? node.nodeValue :
|
|
2813
|
-
new DOMReader([],
|
|
2839
|
+
new DOMReader([], state).readRange(node.firstChild, null).text;
|
|
2814
2840
|
if (newTo - newFrom < text.length) {
|
|
2815
|
-
if (state.
|
|
2841
|
+
if (state.doc.sliceString(newFrom, Math.min(state.doc.length, newFrom + text.length), LineBreakPlaceholder) == text)
|
|
2816
2842
|
newTo = newFrom + text.length;
|
|
2817
|
-
else if (state.
|
|
2843
|
+
else if (state.doc.sliceString(Math.max(0, newTo - text.length), newTo, LineBreakPlaceholder) == text)
|
|
2818
2844
|
newFrom = newTo - text.length;
|
|
2819
2845
|
else
|
|
2820
2846
|
return Decoration.none;
|
|
2821
2847
|
}
|
|
2822
|
-
else if (state.
|
|
2848
|
+
else if (state.doc.sliceString(newFrom, newTo, LineBreakPlaceholder) != text) {
|
|
2823
2849
|
return Decoration.none;
|
|
2824
2850
|
}
|
|
2825
2851
|
return Decoration.set(Decoration.replace({ widget: new CompositionWidget(node, textNode) }).range(newFrom, newTo));
|
|
@@ -4691,6 +4717,12 @@ class ViewState {
|
|
|
4691
4717
|
let refresh = this.heightOracle.mustRefreshForStyle(whiteSpace, direction);
|
|
4692
4718
|
let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != dom.clientHeight;
|
|
4693
4719
|
let result = 0, bias = 0;
|
|
4720
|
+
if (this.editorWidth != view.scrollDOM.clientWidth) {
|
|
4721
|
+
if (oracle.lineWrapping)
|
|
4722
|
+
measureContent = true;
|
|
4723
|
+
this.editorWidth = view.scrollDOM.clientWidth;
|
|
4724
|
+
result |= 8 /* Geometry */;
|
|
4725
|
+
}
|
|
4694
4726
|
if (measureContent) {
|
|
4695
4727
|
this.mustMeasureContent = false;
|
|
4696
4728
|
this.contentDOMHeight = dom.clientHeight;
|
|
@@ -4716,11 +4748,9 @@ class ViewState {
|
|
|
4716
4748
|
if (!this.inView)
|
|
4717
4749
|
return 0;
|
|
4718
4750
|
let contentWidth = dom.clientWidth;
|
|
4719
|
-
if (this.contentDOMWidth != contentWidth || this.editorHeight != view.scrollDOM.clientHeight
|
|
4720
|
-
this.editorWidth != view.scrollDOM.clientWidth) {
|
|
4751
|
+
if (this.contentDOMWidth != contentWidth || this.editorHeight != view.scrollDOM.clientHeight) {
|
|
4721
4752
|
this.contentDOMWidth = contentWidth;
|
|
4722
4753
|
this.editorHeight = view.scrollDOM.clientHeight;
|
|
4723
|
-
this.editorWidth = view.scrollDOM.clientWidth;
|
|
4724
4754
|
result |= 8 /* Geometry */;
|
|
4725
4755
|
}
|
|
4726
4756
|
if (measureContent) {
|
|
@@ -4775,8 +4805,9 @@ class ViewState {
|
|
|
4775
4805
|
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);
|
|
4776
4806
|
// If scrollTarget is given, make sure the viewport includes that position
|
|
4777
4807
|
if (scrollTarget) {
|
|
4778
|
-
let { head } = scrollTarget.range
|
|
4808
|
+
let { head } = scrollTarget.range;
|
|
4779
4809
|
if (head < viewport.from || head > viewport.to) {
|
|
4810
|
+
let viewHeight = Math.min(this.editorHeight, this.pixelViewport.bottom - this.pixelViewport.top);
|
|
4780
4811
|
let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
|
|
4781
4812
|
if (scrollTarget.y == "center")
|
|
4782
4813
|
topPos = (block.top + block.bottom) / 2 - viewHeight / 2;
|
|
@@ -5576,9 +5607,8 @@ function applyDOMChange(view, start, end, typeOver) {
|
|
|
5576
5607
|
return;
|
|
5577
5608
|
let { from, to } = bounds;
|
|
5578
5609
|
let selPoints = view.docView.impreciseHead || view.docView.impreciseAnchor ? [] : selectionPoints(view);
|
|
5579
|
-
let reader = new DOMReader(selPoints, view);
|
|
5610
|
+
let reader = new DOMReader(selPoints, view.state);
|
|
5580
5611
|
reader.readRange(bounds.startDOM, bounds.endDOM);
|
|
5581
|
-
newSel = selectionFromPoints(selPoints, from);
|
|
5582
5612
|
let preferredPos = sel.from, preferredSide = null;
|
|
5583
5613
|
// Prefer anchoring to end when Backspace is pressed (or, on
|
|
5584
5614
|
// Android, when something was deleted)
|
|
@@ -5587,10 +5617,29 @@ function applyDOMChange(view, start, end, typeOver) {
|
|
|
5587
5617
|
preferredPos = sel.to;
|
|
5588
5618
|
preferredSide = "end";
|
|
5589
5619
|
}
|
|
5590
|
-
let diff = findDiff(view.state.
|
|
5591
|
-
if (diff)
|
|
5620
|
+
let diff = findDiff(view.state.doc.sliceString(from, to, LineBreakPlaceholder), reader.text, preferredPos - from, preferredSide);
|
|
5621
|
+
if (diff) {
|
|
5622
|
+
let orig = diff;
|
|
5623
|
+
// Chrome inserts two newlines when pressing shift-enter at the
|
|
5624
|
+
// end of a line. This drops one of those.
|
|
5625
|
+
if (browser.chrome && view.inputState.lastKeyCode == 13 &&
|
|
5626
|
+
diff.toB == diff.from + 2 && reader.text.slice(diff.from, diff.toB) == LineBreakPlaceholder + LineBreakPlaceholder)
|
|
5627
|
+
diff.toB--;
|
|
5628
|
+
// Strip leading and trailing zero-width spaces from the inserted
|
|
5629
|
+
// content, to work around widget buffers being moved into text
|
|
5630
|
+
// nodes by the browser.
|
|
5631
|
+
while (diff.from < diff.toB && reader.text[diff.from] == "\u200b") {
|
|
5632
|
+
diff = { from: diff.from + 1, toA: diff.toA, toB: diff.toB };
|
|
5633
|
+
selPoints.forEach(p => p.pos -= p.pos > orig.from ? 1 : 0);
|
|
5634
|
+
}
|
|
5635
|
+
while (diff.toB > diff.from && reader.text[diff.toB - 1] == "\u200b") {
|
|
5636
|
+
diff = { from: diff.from, toA: diff.toA, toB: diff.toB - 1 };
|
|
5637
|
+
selPoints.forEach(p => p.pos -= p.pos > orig.toB ? 1 : 0);
|
|
5638
|
+
}
|
|
5592
5639
|
change = { from: from + diff.from, to: from + diff.toA,
|
|
5593
|
-
insert:
|
|
5640
|
+
insert: Text$1.of(reader.text.slice(diff.from, diff.toB).split(LineBreakPlaceholder)) };
|
|
5641
|
+
}
|
|
5642
|
+
newSel = selectionFromPoints(selPoints, from);
|
|
5594
5643
|
}
|
|
5595
5644
|
else if (view.hasFocus || !view.state.facet(editable)) {
|
|
5596
5645
|
let domSel = view.observer.selectionRange;
|