@codemirror/view 0.19.25 → 0.19.26
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 +6 -0
- package/dist/index.cjs +162 -203
- package/dist/index.d.ts +5 -0
- package/dist/index.js +162 -203
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -273,7 +273,7 @@ class DOMPos {
|
|
|
273
273
|
static before(dom, precise) { return new DOMPos(dom.parentNode, domIndex(dom), precise); }
|
|
274
274
|
static after(dom, precise) { return new DOMPos(dom.parentNode, domIndex(dom) + 1, precise); }
|
|
275
275
|
}
|
|
276
|
-
const
|
|
276
|
+
const noChildren = [];
|
|
277
277
|
class ContentView {
|
|
278
278
|
constructor() {
|
|
279
279
|
this.parent = null;
|
|
@@ -439,12 +439,12 @@ class ContentView {
|
|
|
439
439
|
v = parent;
|
|
440
440
|
}
|
|
441
441
|
}
|
|
442
|
-
replaceChildren(from, to, children =
|
|
442
|
+
replaceChildren(from, to, children = noChildren) {
|
|
443
443
|
this.markDirty();
|
|
444
444
|
for (let i = from; i < to; i++) {
|
|
445
445
|
let child = this.children[i];
|
|
446
446
|
if (child.parent == this)
|
|
447
|
-
child.
|
|
447
|
+
child.destroy();
|
|
448
448
|
}
|
|
449
449
|
this.children.splice(from, to - from, ...children);
|
|
450
450
|
for (let i = 0; i < children.length; i++)
|
|
@@ -466,6 +466,17 @@ class ContentView {
|
|
|
466
466
|
}
|
|
467
467
|
static get(node) { return node.cmView; }
|
|
468
468
|
get isEditable() { return true; }
|
|
469
|
+
merge(from, to, source, hasStart, openStart, openEnd) {
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
become(other) { return false; }
|
|
473
|
+
// When this is a zero-length view with a side, this should return a
|
|
474
|
+
// number <= 0 to indicate it is before its position, or a
|
|
475
|
+
// number > 0 when after its position.
|
|
476
|
+
getSide() { return 0; }
|
|
477
|
+
destroy() {
|
|
478
|
+
this.parent = null;
|
|
479
|
+
}
|
|
469
480
|
}
|
|
470
481
|
ContentView.prototype.breakAfter = 0;
|
|
471
482
|
// Remove a DOM node and return its next sibling.
|
|
@@ -493,6 +504,94 @@ class ChildCursor {
|
|
|
493
504
|
}
|
|
494
505
|
}
|
|
495
506
|
}
|
|
507
|
+
function replaceRange(parent, fromI, fromOff, toI, toOff, insert, breakAtStart, openStart, openEnd) {
|
|
508
|
+
let { children } = parent;
|
|
509
|
+
let before = children.length ? children[fromI] : null;
|
|
510
|
+
let last = insert.length ? insert[insert.length - 1] : null;
|
|
511
|
+
let breakAtEnd = last ? last.breakAfter : breakAtStart;
|
|
512
|
+
// Change within a single child
|
|
513
|
+
if (fromI == toI && before && !breakAtStart && !breakAtEnd && insert.length < 2 &&
|
|
514
|
+
before.merge(fromOff, toOff, insert.length ? last : null, fromOff == 0, openStart, openEnd))
|
|
515
|
+
return;
|
|
516
|
+
if (toI < children.length) {
|
|
517
|
+
let after = children[toI];
|
|
518
|
+
// Make sure the end of the child after the update is preserved in `after`
|
|
519
|
+
if (after && toOff < after.length) {
|
|
520
|
+
// If we're splitting a child, separate part of it to avoid that
|
|
521
|
+
// being mangled when updating the child before the update.
|
|
522
|
+
if (fromI == toI) {
|
|
523
|
+
after = after.split(toOff);
|
|
524
|
+
toOff = 0;
|
|
525
|
+
}
|
|
526
|
+
// If the element after the replacement should be merged with
|
|
527
|
+
// the last replacing element, update `content`
|
|
528
|
+
if (!breakAtEnd && last && after.merge(0, toOff, last, true, 0, openEnd)) {
|
|
529
|
+
insert[insert.length - 1] = after;
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
// Remove the start of the after element, if necessary, and
|
|
533
|
+
// add it to `content`.
|
|
534
|
+
if (toOff)
|
|
535
|
+
after.merge(0, toOff, null, false, 0, openEnd);
|
|
536
|
+
insert.push(after);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
else if (after === null || after === void 0 ? void 0 : after.breakAfter) {
|
|
540
|
+
// The element at `toI` is entirely covered by this range.
|
|
541
|
+
// Preserve its line break, if any.
|
|
542
|
+
if (last)
|
|
543
|
+
last.breakAfter = 1;
|
|
544
|
+
else
|
|
545
|
+
breakAtStart = 1;
|
|
546
|
+
}
|
|
547
|
+
// Since we've handled the next element from the current elements
|
|
548
|
+
// now, make sure `toI` points after that.
|
|
549
|
+
toI++;
|
|
550
|
+
}
|
|
551
|
+
if (before) {
|
|
552
|
+
before.breakAfter = breakAtStart;
|
|
553
|
+
if (fromOff > 0) {
|
|
554
|
+
if (!breakAtStart && insert.length && before.merge(fromOff, before.length, insert[0], false, openStart, 0)) {
|
|
555
|
+
before.breakAfter = insert.shift().breakAfter;
|
|
556
|
+
}
|
|
557
|
+
else if (fromOff < before.length || before.children.length && before.children[before.children.length - 1].length == 0) {
|
|
558
|
+
before.merge(fromOff, before.length, null, false, openStart, 0);
|
|
559
|
+
}
|
|
560
|
+
fromI++;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
// Try to merge widgets on the boundaries of the replacement
|
|
564
|
+
while (fromI < toI && insert.length) {
|
|
565
|
+
if (children[toI - 1].become(insert[insert.length - 1])) {
|
|
566
|
+
toI--;
|
|
567
|
+
insert.pop();
|
|
568
|
+
openEnd = insert.length ? 0 : openStart;
|
|
569
|
+
}
|
|
570
|
+
else if (children[fromI].become(insert[0])) {
|
|
571
|
+
fromI++;
|
|
572
|
+
insert.shift();
|
|
573
|
+
openStart = insert.length ? 0 : openEnd;
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
break;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
if (!insert.length && fromI && toI < children.length && !children[fromI - 1].breakAfter &&
|
|
580
|
+
children[toI].merge(0, 0, children[fromI - 1], false, openStart, openEnd))
|
|
581
|
+
fromI--;
|
|
582
|
+
if (fromI < toI || insert.length)
|
|
583
|
+
parent.replaceChildren(fromI, toI, insert);
|
|
584
|
+
}
|
|
585
|
+
function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) {
|
|
586
|
+
let cur = parent.childCursor();
|
|
587
|
+
let { i: toI, off: toOff } = cur.findPos(to, 1);
|
|
588
|
+
let { i: fromI, off: fromOff } = cur.findPos(from, -1);
|
|
589
|
+
let dLen = from - to;
|
|
590
|
+
for (let view of insert)
|
|
591
|
+
dLen += view.length;
|
|
592
|
+
parent.length += dLen;
|
|
593
|
+
replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd);
|
|
594
|
+
}
|
|
496
595
|
|
|
497
596
|
let [nav, doc] = typeof navigator != "undefined"
|
|
498
597
|
? [navigator, document]
|
|
@@ -524,21 +623,8 @@ var browser = {
|
|
|
524
623
|
tabSize: doc.documentElement.style.tabSize != null ? "tab-size" : "-moz-tab-size"
|
|
525
624
|
};
|
|
526
625
|
|
|
527
|
-
const none$2 = [];
|
|
528
|
-
class InlineView extends ContentView {
|
|
529
|
-
/**
|
|
530
|
-
Return true when this view is equivalent to `other` and can take
|
|
531
|
-
on its role.
|
|
532
|
-
*/
|
|
533
|
-
become(_other) { return false; }
|
|
534
|
-
// When this is a zero-length view with a side, this should return a
|
|
535
|
-
// negative number to indicate it is before its position, or a
|
|
536
|
-
// positive number when after its position.
|
|
537
|
-
getSide() { return 0; }
|
|
538
|
-
}
|
|
539
|
-
InlineView.prototype.children = none$2;
|
|
540
626
|
const MaxJoinLen = 256;
|
|
541
|
-
class TextView extends
|
|
627
|
+
class TextView extends ContentView {
|
|
542
628
|
constructor(text) {
|
|
543
629
|
super();
|
|
544
630
|
this.text = text;
|
|
@@ -569,7 +655,7 @@ class TextView extends InlineView {
|
|
|
569
655
|
this.markDirty();
|
|
570
656
|
return true;
|
|
571
657
|
}
|
|
572
|
-
|
|
658
|
+
split(from) {
|
|
573
659
|
let result = new TextView(this.text.slice(from));
|
|
574
660
|
this.text = this.text.slice(0, from);
|
|
575
661
|
return result;
|
|
@@ -585,7 +671,7 @@ class TextView extends InlineView {
|
|
|
585
671
|
return textCoords(this.dom, pos, side);
|
|
586
672
|
}
|
|
587
673
|
}
|
|
588
|
-
class MarkView extends
|
|
674
|
+
class MarkView extends ContentView {
|
|
589
675
|
constructor(mark, children = [], length = 0) {
|
|
590
676
|
super();
|
|
591
677
|
this.mark = mark;
|
|
@@ -608,20 +694,20 @@ class MarkView extends InlineView {
|
|
|
608
694
|
this.createDOM();
|
|
609
695
|
super.sync(track);
|
|
610
696
|
}
|
|
611
|
-
merge(from, to, source, openStart, openEnd) {
|
|
697
|
+
merge(from, to, source, _hasStart, openStart, openEnd) {
|
|
612
698
|
if (source && (!(source instanceof MarkView && source.mark.eq(this.mark)) ||
|
|
613
699
|
(from && openStart <= 0) || (to < this.length && openEnd <= 0)))
|
|
614
700
|
return false;
|
|
615
|
-
|
|
701
|
+
mergeChildrenInto(this, from, to, source ? source.children : [], openStart - 1, openEnd - 1);
|
|
616
702
|
this.markDirty();
|
|
617
703
|
return true;
|
|
618
704
|
}
|
|
619
|
-
|
|
705
|
+
split(from) {
|
|
620
706
|
let result = [], off = 0, detachFrom = -1, i = 0;
|
|
621
707
|
for (let elt of this.children) {
|
|
622
708
|
let end = off + elt.length;
|
|
623
709
|
if (end > from)
|
|
624
|
-
result.push(off < from ? elt.
|
|
710
|
+
result.push(off < from ? elt.split(from - off) : elt);
|
|
625
711
|
if (detachFrom < 0 && off >= from)
|
|
626
712
|
detachFrom = i;
|
|
627
713
|
off = end;
|
|
@@ -629,8 +715,10 @@ class MarkView extends InlineView {
|
|
|
629
715
|
}
|
|
630
716
|
let length = this.length - from;
|
|
631
717
|
this.length = from;
|
|
632
|
-
if (detachFrom > -1)
|
|
633
|
-
this.
|
|
718
|
+
if (detachFrom > -1) {
|
|
719
|
+
this.children.length = detachFrom;
|
|
720
|
+
this.markDirty();
|
|
721
|
+
}
|
|
634
722
|
return new MarkView(this.mark, result, length);
|
|
635
723
|
}
|
|
636
724
|
domAtPos(pos) {
|
|
@@ -672,7 +760,7 @@ function textCoords(text, pos, side) {
|
|
|
672
760
|
return flatten ? flattenRect(rect, flatten < 0) : rect || null;
|
|
673
761
|
}
|
|
674
762
|
// Also used for collapsed ranges that don't have a placeholder widget!
|
|
675
|
-
class WidgetView extends
|
|
763
|
+
class WidgetView extends ContentView {
|
|
676
764
|
constructor(widget, length, side) {
|
|
677
765
|
super();
|
|
678
766
|
this.widget = widget;
|
|
@@ -682,7 +770,7 @@ class WidgetView extends InlineView {
|
|
|
682
770
|
static create(widget, length, side) {
|
|
683
771
|
return new (widget.customView || WidgetView)(widget, length, side);
|
|
684
772
|
}
|
|
685
|
-
|
|
773
|
+
split(from) {
|
|
686
774
|
let result = WidgetView.create(this.widget, this.length - from, this.side);
|
|
687
775
|
this.length -= from;
|
|
688
776
|
return result;
|
|
@@ -694,7 +782,7 @@ class WidgetView extends InlineView {
|
|
|
694
782
|
}
|
|
695
783
|
}
|
|
696
784
|
getSide() { return this.side; }
|
|
697
|
-
merge(from, to, source, openStart, openEnd) {
|
|
785
|
+
merge(from, to, source, hasStart, openStart, openEnd) {
|
|
698
786
|
if (source && (!(source instanceof WidgetView) || !this.widget.compare(source.widget) ||
|
|
699
787
|
from > 0 && openStart <= 0 || to < this.length && openEnd <= 0))
|
|
700
788
|
return false;
|
|
@@ -739,6 +827,11 @@ class WidgetView extends InlineView {
|
|
|
739
827
|
return (pos == 0 && side > 0 || pos == this.length && side <= 0) ? rect : flattenRect(rect, pos == 0);
|
|
740
828
|
}
|
|
741
829
|
get isEditable() { return false; }
|
|
830
|
+
destroy() {
|
|
831
|
+
super.destroy();
|
|
832
|
+
if (this.dom)
|
|
833
|
+
this.widget.destroy(this.dom);
|
|
834
|
+
}
|
|
742
835
|
}
|
|
743
836
|
class CompositionView extends WidgetView {
|
|
744
837
|
domAtPos(pos) { return new DOMPos(this.widget.text, pos); }
|
|
@@ -755,7 +848,7 @@ class CompositionView extends WidgetView {
|
|
|
755
848
|
// These are drawn around uneditable widgets to avoid a number of
|
|
756
849
|
// browser bugs that show up when the cursor is directly next to
|
|
757
850
|
// uneditable inline content.
|
|
758
|
-
class WidgetBufferView extends
|
|
851
|
+
class WidgetBufferView extends ContentView {
|
|
759
852
|
constructor(side) {
|
|
760
853
|
super();
|
|
761
854
|
this.side = side;
|
|
@@ -765,7 +858,7 @@ class WidgetBufferView extends InlineView {
|
|
|
765
858
|
become(other) {
|
|
766
859
|
return other instanceof WidgetBufferView && other.side == this.side;
|
|
767
860
|
}
|
|
768
|
-
|
|
861
|
+
split() { return new WidgetBufferView(this.side); }
|
|
769
862
|
sync() {
|
|
770
863
|
if (!this.dom)
|
|
771
864
|
this.setDOM(document.createTextNode("\u200b"));
|
|
@@ -783,90 +876,7 @@ class WidgetBufferView extends InlineView {
|
|
|
783
876
|
return text.Text.of([this.dom.nodeValue.replace(/\u200b/g, "")]);
|
|
784
877
|
}
|
|
785
878
|
}
|
|
786
|
-
|
|
787
|
-
let cur = parent.childCursor();
|
|
788
|
-
let { i: toI, off: toOff } = cur.findPos(to, 1);
|
|
789
|
-
let { i: fromI, off: fromOff } = cur.findPos(from, -1);
|
|
790
|
-
let dLen = from - to;
|
|
791
|
-
for (let view of elts)
|
|
792
|
-
dLen += view.length;
|
|
793
|
-
parent.length += dLen;
|
|
794
|
-
let { children } = parent;
|
|
795
|
-
// Both from and to point into the same child view
|
|
796
|
-
if (fromI == toI && fromOff) {
|
|
797
|
-
let start = children[fromI];
|
|
798
|
-
// Maybe just update that view and be done
|
|
799
|
-
if (elts.length == 1 && start.merge(fromOff, toOff, elts[0], openStart, openEnd))
|
|
800
|
-
return;
|
|
801
|
-
if (elts.length == 0) {
|
|
802
|
-
start.merge(fromOff, toOff, null, openStart, openEnd);
|
|
803
|
-
return;
|
|
804
|
-
}
|
|
805
|
-
// Otherwise split it, so that we don't have to worry about aliasing front/end afterwards
|
|
806
|
-
let after = start.slice(toOff);
|
|
807
|
-
if (after.merge(0, 0, elts[elts.length - 1], 0, openEnd))
|
|
808
|
-
elts[elts.length - 1] = after;
|
|
809
|
-
else
|
|
810
|
-
elts.push(after);
|
|
811
|
-
toI++;
|
|
812
|
-
openEnd = toOff = 0;
|
|
813
|
-
}
|
|
814
|
-
// Make sure start and end positions fall on node boundaries
|
|
815
|
-
// (fromOff/toOff are no longer used after this), and that if the
|
|
816
|
-
// start or end of the elts can be merged with adjacent nodes,
|
|
817
|
-
// this is done
|
|
818
|
-
if (toOff) {
|
|
819
|
-
let end = children[toI];
|
|
820
|
-
if (elts.length && end.merge(0, toOff, elts[elts.length - 1], 0, openEnd)) {
|
|
821
|
-
elts.pop();
|
|
822
|
-
openEnd = elts.length ? 0 : openStart;
|
|
823
|
-
}
|
|
824
|
-
else {
|
|
825
|
-
end.merge(0, toOff, null, 0, 0);
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
else if (toI < children.length && elts.length &&
|
|
829
|
-
children[toI].merge(0, 0, elts[elts.length - 1], 0, openEnd)) {
|
|
830
|
-
elts.pop();
|
|
831
|
-
openEnd = elts.length ? 0 : openStart;
|
|
832
|
-
}
|
|
833
|
-
if (fromOff) {
|
|
834
|
-
let start = children[fromI];
|
|
835
|
-
if (elts.length && start.merge(fromOff, start.length, elts[0], openStart, 0)) {
|
|
836
|
-
elts.shift();
|
|
837
|
-
openStart = elts.length ? 0 : openEnd;
|
|
838
|
-
}
|
|
839
|
-
else {
|
|
840
|
-
start.merge(fromOff, start.length, null, 0, 0);
|
|
841
|
-
}
|
|
842
|
-
fromI++;
|
|
843
|
-
}
|
|
844
|
-
else if (fromI && elts.length) {
|
|
845
|
-
let end = children[fromI - 1];
|
|
846
|
-
if (end.merge(end.length, end.length, elts[0], openStart, 0)) {
|
|
847
|
-
elts.shift();
|
|
848
|
-
openStart = elts.length ? 0 : openEnd;
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
// Then try to merge any mergeable nodes at the start and end of
|
|
852
|
-
// the changed range
|
|
853
|
-
while (fromI < toI && elts.length && children[toI - 1].become(elts[elts.length - 1])) {
|
|
854
|
-
elts.pop();
|
|
855
|
-
toI--;
|
|
856
|
-
openEnd = elts.length ? 0 : openStart;
|
|
857
|
-
}
|
|
858
|
-
while (fromI < toI && elts.length && children[fromI].become(elts[0])) {
|
|
859
|
-
elts.shift();
|
|
860
|
-
fromI++;
|
|
861
|
-
openStart = elts.length ? 0 : openEnd;
|
|
862
|
-
}
|
|
863
|
-
if (!elts.length && fromI && toI < children.length &&
|
|
864
|
-
children[toI].merge(0, 0, children[fromI - 1], openStart, openEnd))
|
|
865
|
-
fromI--;
|
|
866
|
-
// And if anything remains, splice the child array to insert the new elts
|
|
867
|
-
if (elts.length || fromI != toI)
|
|
868
|
-
parent.replaceChildren(fromI, toI, elts);
|
|
869
|
-
}
|
|
879
|
+
TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren;
|
|
870
880
|
function inlineDOMAtPos(dom, children, pos) {
|
|
871
881
|
let i = 0;
|
|
872
882
|
for (let off = 0; i < children.length; i++) {
|
|
@@ -1007,6 +1017,11 @@ class WidgetType {
|
|
|
1007
1017
|
@internal
|
|
1008
1018
|
*/
|
|
1009
1019
|
get customView() { return null; }
|
|
1020
|
+
/**
|
|
1021
|
+
This is called when the an instance of the widget is removed
|
|
1022
|
+
from the editor view.
|
|
1023
|
+
*/
|
|
1024
|
+
destroy(_dom) { }
|
|
1010
1025
|
}
|
|
1011
1026
|
/**
|
|
1012
1027
|
The different types of blocks that can occur in an editor view.
|
|
@@ -1167,12 +1182,12 @@ class PointDecoration extends Decoration {
|
|
|
1167
1182
|
super(startSide, endSide, widget, spec);
|
|
1168
1183
|
this.block = block;
|
|
1169
1184
|
this.isReplace = isReplace;
|
|
1170
|
-
this.mapMode = !block ? state.MapMode.TrackDel : startSide
|
|
1185
|
+
this.mapMode = !block ? state.MapMode.TrackDel : startSide <= 0 ? state.MapMode.TrackBefore : state.MapMode.TrackAfter;
|
|
1171
1186
|
}
|
|
1172
1187
|
// Only relevant when this.block == true
|
|
1173
1188
|
get type() {
|
|
1174
1189
|
return this.startSide < this.endSide ? exports.BlockType.WidgetRange
|
|
1175
|
-
: this.startSide
|
|
1190
|
+
: this.startSide <= 0 ? exports.BlockType.WidgetBefore : exports.BlockType.WidgetAfter;
|
|
1176
1191
|
}
|
|
1177
1192
|
get heightRelevant() { return this.block || !!this.widget && this.widget.estimatedHeight >= 5; }
|
|
1178
1193
|
eq(other) {
|
|
@@ -1182,7 +1197,7 @@ class PointDecoration extends Decoration {
|
|
|
1182
1197
|
this.startSide == other.startSide && this.endSide == other.endSide;
|
|
1183
1198
|
}
|
|
1184
1199
|
range(from, to = from) {
|
|
1185
|
-
if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide
|
|
1200
|
+
if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide <= 0)))
|
|
1186
1201
|
throw new RangeError("Invalid range for replacement decoration");
|
|
1187
1202
|
if (!this.isReplace && to != from)
|
|
1188
1203
|
throw new RangeError("Widget decorations can only have zero-length ranges");
|
|
@@ -1219,16 +1234,16 @@ class LineView extends ContentView {
|
|
|
1219
1234
|
this.breakAfter = 0;
|
|
1220
1235
|
}
|
|
1221
1236
|
// Consumes source
|
|
1222
|
-
merge(from, to, source,
|
|
1237
|
+
merge(from, to, source, hasStart, openStart, openEnd) {
|
|
1223
1238
|
if (source) {
|
|
1224
1239
|
if (!(source instanceof LineView))
|
|
1225
1240
|
return false;
|
|
1226
1241
|
if (!this.dom)
|
|
1227
1242
|
source.transferDOM(this); // Reuse source.dom when appropriate
|
|
1228
1243
|
}
|
|
1229
|
-
if (
|
|
1244
|
+
if (hasStart)
|
|
1230
1245
|
this.setDeco(source ? source.attrs : null);
|
|
1231
|
-
|
|
1246
|
+
mergeChildrenInto(this, from, to, source ? source.children : [], openStart, openEnd);
|
|
1232
1247
|
return true;
|
|
1233
1248
|
}
|
|
1234
1249
|
split(at) {
|
|
@@ -1238,16 +1253,14 @@ class LineView extends ContentView {
|
|
|
1238
1253
|
return end;
|
|
1239
1254
|
let { i, off } = this.childPos(at);
|
|
1240
1255
|
if (off) {
|
|
1241
|
-
end.append(this.children[i].
|
|
1242
|
-
this.children[i].merge(off, this.children[i].length, null, 0, 0);
|
|
1256
|
+
end.append(this.children[i].split(off), 0);
|
|
1257
|
+
this.children[i].merge(off, this.children[i].length, null, false, 0, 0);
|
|
1243
1258
|
i++;
|
|
1244
1259
|
}
|
|
1245
1260
|
for (let j = i; j < this.children.length; j++)
|
|
1246
1261
|
end.append(this.children[j], 0);
|
|
1247
|
-
while (i > 0 && this.children[i - 1].length == 0)
|
|
1248
|
-
this.children[i
|
|
1249
|
-
i--;
|
|
1250
|
-
}
|
|
1262
|
+
while (i > 0 && this.children[i - 1].length == 0)
|
|
1263
|
+
this.children[--i].destroy();
|
|
1251
1264
|
this.children.length = i;
|
|
1252
1265
|
this.markDirty();
|
|
1253
1266
|
this.length = at;
|
|
@@ -1270,7 +1283,6 @@ class LineView extends ContentView {
|
|
|
1270
1283
|
this.attrs = attrs;
|
|
1271
1284
|
}
|
|
1272
1285
|
}
|
|
1273
|
-
// Only called when building a line view in ContentBuilder
|
|
1274
1286
|
append(child, openStart) {
|
|
1275
1287
|
joinInlineInto(this, child, openStart);
|
|
1276
1288
|
}
|
|
@@ -1327,7 +1339,7 @@ class LineView extends ContentView {
|
|
|
1327
1339
|
coordsAt(pos, side) {
|
|
1328
1340
|
return coordsInChildren(this, pos, side);
|
|
1329
1341
|
}
|
|
1330
|
-
|
|
1342
|
+
become(_other) { return false; }
|
|
1331
1343
|
get type() { return exports.BlockType.Text; }
|
|
1332
1344
|
static find(docView, pos) {
|
|
1333
1345
|
for (let i = 0, off = 0;; i++) {
|
|
@@ -1342,7 +1354,6 @@ class LineView extends ContentView {
|
|
|
1342
1354
|
}
|
|
1343
1355
|
}
|
|
1344
1356
|
}
|
|
1345
|
-
const none$1 = [];
|
|
1346
1357
|
class BlockWidgetView extends ContentView {
|
|
1347
1358
|
constructor(widget, length, type) {
|
|
1348
1359
|
super();
|
|
@@ -1368,7 +1379,7 @@ class BlockWidgetView extends ContentView {
|
|
|
1368
1379
|
end.breakAfter = this.breakAfter;
|
|
1369
1380
|
return end;
|
|
1370
1381
|
}
|
|
1371
|
-
get children() { return
|
|
1382
|
+
get children() { return noChildren; }
|
|
1372
1383
|
sync() {
|
|
1373
1384
|
if (!this.dom || !this.widget.updateDOM(this.dom)) {
|
|
1374
1385
|
this.setDOM(this.widget.toDOM(this.editorView));
|
|
@@ -1379,7 +1390,7 @@ class BlockWidgetView extends ContentView {
|
|
|
1379
1390
|
return this.parent ? this.parent.view.state.doc.slice(this.posAtStart, this.posAtEnd) : state.Text.empty;
|
|
1380
1391
|
}
|
|
1381
1392
|
domBoundsAround() { return null; }
|
|
1382
|
-
|
|
1393
|
+
become(other) {
|
|
1383
1394
|
if (other instanceof BlockWidgetView && other.type == this.type &&
|
|
1384
1395
|
other.widget.constructor == this.widget.constructor) {
|
|
1385
1396
|
if (!other.widget.eq(this.widget))
|
|
@@ -1393,6 +1404,11 @@ class BlockWidgetView extends ContentView {
|
|
|
1393
1404
|
}
|
|
1394
1405
|
ignoreMutation() { return true; }
|
|
1395
1406
|
ignoreEvent(event) { return this.widget.ignoreEvent(event); }
|
|
1407
|
+
destroy() {
|
|
1408
|
+
super.destroy();
|
|
1409
|
+
if (this.dom)
|
|
1410
|
+
this.widget.destroy(this.dom);
|
|
1411
|
+
}
|
|
1396
1412
|
}
|
|
1397
1413
|
|
|
1398
1414
|
class ContentBuilder {
|
|
@@ -2045,71 +2061,9 @@ class DocView extends ContentView {
|
|
|
2045
2061
|
let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, deco);
|
|
2046
2062
|
let { i: toI, off: toOff } = cursor.findPos(toA, 1);
|
|
2047
2063
|
let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
|
|
2048
|
-
|
|
2064
|
+
replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
|
|
2049
2065
|
}
|
|
2050
2066
|
}
|
|
2051
|
-
replaceRange(fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd) {
|
|
2052
|
-
let before = this.children[fromI], last = content.length ? content[content.length - 1] : null;
|
|
2053
|
-
let breakAtEnd = last ? last.breakAfter : breakAtStart;
|
|
2054
|
-
// Change within a single line
|
|
2055
|
-
if (fromI == toI && !breakAtStart && !breakAtEnd && content.length < 2 &&
|
|
2056
|
-
before.merge(fromOff, toOff, content.length ? last : null, fromOff == 0, openStart, openEnd))
|
|
2057
|
-
return;
|
|
2058
|
-
let after = this.children[toI];
|
|
2059
|
-
// Make sure the end of the line after the update is preserved in `after`
|
|
2060
|
-
if (toOff < after.length) {
|
|
2061
|
-
// If we're splitting a line, separate part of the start line to
|
|
2062
|
-
// avoid that being mangled when updating the start line.
|
|
2063
|
-
if (fromI == toI) {
|
|
2064
|
-
after = after.split(toOff);
|
|
2065
|
-
toOff = 0;
|
|
2066
|
-
}
|
|
2067
|
-
// If the element after the replacement should be merged with
|
|
2068
|
-
// the last replacing element, update `content`
|
|
2069
|
-
if (!breakAtEnd && last && after.merge(0, toOff, last, true, 0, openEnd)) {
|
|
2070
|
-
content[content.length - 1] = after;
|
|
2071
|
-
}
|
|
2072
|
-
else {
|
|
2073
|
-
// Remove the start of the after element, if necessary, and
|
|
2074
|
-
// add it to `content`.
|
|
2075
|
-
if (toOff)
|
|
2076
|
-
after.merge(0, toOff, null, false, 0, openEnd);
|
|
2077
|
-
content.push(after);
|
|
2078
|
-
}
|
|
2079
|
-
}
|
|
2080
|
-
else if (after.breakAfter) {
|
|
2081
|
-
// The element at `toI` is entirely covered by this range.
|
|
2082
|
-
// Preserve its line break, if any.
|
|
2083
|
-
if (last)
|
|
2084
|
-
last.breakAfter = 1;
|
|
2085
|
-
else
|
|
2086
|
-
breakAtStart = 1;
|
|
2087
|
-
}
|
|
2088
|
-
// Since we've handled the next element from the current elements
|
|
2089
|
-
// now, make sure `toI` points after that.
|
|
2090
|
-
toI++;
|
|
2091
|
-
before.breakAfter = breakAtStart;
|
|
2092
|
-
if (fromOff > 0) {
|
|
2093
|
-
if (!breakAtStart && content.length && before.merge(fromOff, before.length, content[0], false, openStart, 0)) {
|
|
2094
|
-
before.breakAfter = content.shift().breakAfter;
|
|
2095
|
-
}
|
|
2096
|
-
else if (fromOff < before.length || before.children.length && before.children[before.children.length - 1].length == 0) {
|
|
2097
|
-
before.merge(fromOff, before.length, null, false, openStart, 0);
|
|
2098
|
-
}
|
|
2099
|
-
fromI++;
|
|
2100
|
-
}
|
|
2101
|
-
// Try to merge widgets on the boundaries of the replacement
|
|
2102
|
-
while (fromI < toI && content.length) {
|
|
2103
|
-
if (this.children[toI - 1].match(content[content.length - 1]))
|
|
2104
|
-
toI--, content.pop();
|
|
2105
|
-
else if (this.children[fromI].match(content[0]))
|
|
2106
|
-
fromI++, content.shift();
|
|
2107
|
-
else
|
|
2108
|
-
break;
|
|
2109
|
-
}
|
|
2110
|
-
if (fromI < toI || content.length)
|
|
2111
|
-
this.replaceChildren(fromI, toI, content);
|
|
2112
|
-
}
|
|
2113
2067
|
// Sync the DOM selection to this.state.selection
|
|
2114
2068
|
updateSelection(mustRead = false, fromPointer = false) {
|
|
2115
2069
|
if (mustRead)
|
|
@@ -2372,15 +2326,10 @@ function computeCompositionDeco(view, changes) {
|
|
|
2372
2326
|
if (!textNode)
|
|
2373
2327
|
return Decoration.none;
|
|
2374
2328
|
let cView = view.docView.nearest(textNode);
|
|
2329
|
+
if (!cView)
|
|
2330
|
+
return Decoration.none;
|
|
2375
2331
|
let from, to, topNode = textNode;
|
|
2376
|
-
if (cView instanceof
|
|
2377
|
-
while (cView.parent instanceof InlineView)
|
|
2378
|
-
cView = cView.parent;
|
|
2379
|
-
from = cView.posAtStart;
|
|
2380
|
-
to = from + cView.length;
|
|
2381
|
-
topNode = cView.dom;
|
|
2382
|
-
}
|
|
2383
|
-
else if (cView instanceof LineView) {
|
|
2332
|
+
if (cView instanceof LineView) {
|
|
2384
2333
|
while (topNode.parentNode != cView.dom)
|
|
2385
2334
|
topNode = topNode.parentNode;
|
|
2386
2335
|
let prev = topNode.previousSibling;
|
|
@@ -2389,7 +2338,17 @@ function computeCompositionDeco(view, changes) {
|
|
|
2389
2338
|
from = to = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
|
|
2390
2339
|
}
|
|
2391
2340
|
else {
|
|
2392
|
-
|
|
2341
|
+
for (;;) {
|
|
2342
|
+
let { parent } = cView;
|
|
2343
|
+
if (!parent)
|
|
2344
|
+
return Decoration.none;
|
|
2345
|
+
if (parent instanceof LineView)
|
|
2346
|
+
break;
|
|
2347
|
+
cView = parent;
|
|
2348
|
+
}
|
|
2349
|
+
from = cView.posAtStart;
|
|
2350
|
+
to = from + cView.length;
|
|
2351
|
+
topNode = cView.dom;
|
|
2393
2352
|
}
|
|
2394
2353
|
let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
|
|
2395
2354
|
let text = textNode.nodeValue, { state } = view;
|
package/dist/index.d.ts
CHANGED
|
@@ -171,6 +171,11 @@ declare abstract class WidgetType {
|
|
|
171
171
|
events.
|
|
172
172
|
*/
|
|
173
173
|
ignoreEvent(_event: Event): boolean;
|
|
174
|
+
/**
|
|
175
|
+
This is called when the an instance of the widget is removed
|
|
176
|
+
from the editor view.
|
|
177
|
+
*/
|
|
178
|
+
destroy(_dom: HTMLElement): void;
|
|
174
179
|
}
|
|
175
180
|
/**
|
|
176
181
|
A decoration set represents a collection of decorated ranges,
|
package/dist/index.js
CHANGED
|
@@ -270,7 +270,7 @@ class DOMPos {
|
|
|
270
270
|
static before(dom, precise) { return new DOMPos(dom.parentNode, domIndex(dom), precise); }
|
|
271
271
|
static after(dom, precise) { return new DOMPos(dom.parentNode, domIndex(dom) + 1, precise); }
|
|
272
272
|
}
|
|
273
|
-
const
|
|
273
|
+
const noChildren = [];
|
|
274
274
|
class ContentView {
|
|
275
275
|
constructor() {
|
|
276
276
|
this.parent = null;
|
|
@@ -436,12 +436,12 @@ class ContentView {
|
|
|
436
436
|
v = parent;
|
|
437
437
|
}
|
|
438
438
|
}
|
|
439
|
-
replaceChildren(from, to, children =
|
|
439
|
+
replaceChildren(from, to, children = noChildren) {
|
|
440
440
|
this.markDirty();
|
|
441
441
|
for (let i = from; i < to; i++) {
|
|
442
442
|
let child = this.children[i];
|
|
443
443
|
if (child.parent == this)
|
|
444
|
-
child.
|
|
444
|
+
child.destroy();
|
|
445
445
|
}
|
|
446
446
|
this.children.splice(from, to - from, ...children);
|
|
447
447
|
for (let i = 0; i < children.length; i++)
|
|
@@ -463,6 +463,17 @@ class ContentView {
|
|
|
463
463
|
}
|
|
464
464
|
static get(node) { return node.cmView; }
|
|
465
465
|
get isEditable() { return true; }
|
|
466
|
+
merge(from, to, source, hasStart, openStart, openEnd) {
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
become(other) { return false; }
|
|
470
|
+
// When this is a zero-length view with a side, this should return a
|
|
471
|
+
// number <= 0 to indicate it is before its position, or a
|
|
472
|
+
// number > 0 when after its position.
|
|
473
|
+
getSide() { return 0; }
|
|
474
|
+
destroy() {
|
|
475
|
+
this.parent = null;
|
|
476
|
+
}
|
|
466
477
|
}
|
|
467
478
|
ContentView.prototype.breakAfter = 0;
|
|
468
479
|
// Remove a DOM node and return its next sibling.
|
|
@@ -490,6 +501,94 @@ class ChildCursor {
|
|
|
490
501
|
}
|
|
491
502
|
}
|
|
492
503
|
}
|
|
504
|
+
function replaceRange(parent, fromI, fromOff, toI, toOff, insert, breakAtStart, openStart, openEnd) {
|
|
505
|
+
let { children } = parent;
|
|
506
|
+
let before = children.length ? children[fromI] : null;
|
|
507
|
+
let last = insert.length ? insert[insert.length - 1] : null;
|
|
508
|
+
let breakAtEnd = last ? last.breakAfter : breakAtStart;
|
|
509
|
+
// Change within a single child
|
|
510
|
+
if (fromI == toI && before && !breakAtStart && !breakAtEnd && insert.length < 2 &&
|
|
511
|
+
before.merge(fromOff, toOff, insert.length ? last : null, fromOff == 0, openStart, openEnd))
|
|
512
|
+
return;
|
|
513
|
+
if (toI < children.length) {
|
|
514
|
+
let after = children[toI];
|
|
515
|
+
// Make sure the end of the child after the update is preserved in `after`
|
|
516
|
+
if (after && toOff < after.length) {
|
|
517
|
+
// If we're splitting a child, separate part of it to avoid that
|
|
518
|
+
// being mangled when updating the child before the update.
|
|
519
|
+
if (fromI == toI) {
|
|
520
|
+
after = after.split(toOff);
|
|
521
|
+
toOff = 0;
|
|
522
|
+
}
|
|
523
|
+
// If the element after the replacement should be merged with
|
|
524
|
+
// the last replacing element, update `content`
|
|
525
|
+
if (!breakAtEnd && last && after.merge(0, toOff, last, true, 0, openEnd)) {
|
|
526
|
+
insert[insert.length - 1] = after;
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
// Remove the start of the after element, if necessary, and
|
|
530
|
+
// add it to `content`.
|
|
531
|
+
if (toOff)
|
|
532
|
+
after.merge(0, toOff, null, false, 0, openEnd);
|
|
533
|
+
insert.push(after);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
else if (after === null || after === void 0 ? void 0 : after.breakAfter) {
|
|
537
|
+
// The element at `toI` is entirely covered by this range.
|
|
538
|
+
// Preserve its line break, if any.
|
|
539
|
+
if (last)
|
|
540
|
+
last.breakAfter = 1;
|
|
541
|
+
else
|
|
542
|
+
breakAtStart = 1;
|
|
543
|
+
}
|
|
544
|
+
// Since we've handled the next element from the current elements
|
|
545
|
+
// now, make sure `toI` points after that.
|
|
546
|
+
toI++;
|
|
547
|
+
}
|
|
548
|
+
if (before) {
|
|
549
|
+
before.breakAfter = breakAtStart;
|
|
550
|
+
if (fromOff > 0) {
|
|
551
|
+
if (!breakAtStart && insert.length && before.merge(fromOff, before.length, insert[0], false, openStart, 0)) {
|
|
552
|
+
before.breakAfter = insert.shift().breakAfter;
|
|
553
|
+
}
|
|
554
|
+
else if (fromOff < before.length || before.children.length && before.children[before.children.length - 1].length == 0) {
|
|
555
|
+
before.merge(fromOff, before.length, null, false, openStart, 0);
|
|
556
|
+
}
|
|
557
|
+
fromI++;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
// Try to merge widgets on the boundaries of the replacement
|
|
561
|
+
while (fromI < toI && insert.length) {
|
|
562
|
+
if (children[toI - 1].become(insert[insert.length - 1])) {
|
|
563
|
+
toI--;
|
|
564
|
+
insert.pop();
|
|
565
|
+
openEnd = insert.length ? 0 : openStart;
|
|
566
|
+
}
|
|
567
|
+
else if (children[fromI].become(insert[0])) {
|
|
568
|
+
fromI++;
|
|
569
|
+
insert.shift();
|
|
570
|
+
openStart = insert.length ? 0 : openEnd;
|
|
571
|
+
}
|
|
572
|
+
else {
|
|
573
|
+
break;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
if (!insert.length && fromI && toI < children.length && !children[fromI - 1].breakAfter &&
|
|
577
|
+
children[toI].merge(0, 0, children[fromI - 1], false, openStart, openEnd))
|
|
578
|
+
fromI--;
|
|
579
|
+
if (fromI < toI || insert.length)
|
|
580
|
+
parent.replaceChildren(fromI, toI, insert);
|
|
581
|
+
}
|
|
582
|
+
function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) {
|
|
583
|
+
let cur = parent.childCursor();
|
|
584
|
+
let { i: toI, off: toOff } = cur.findPos(to, 1);
|
|
585
|
+
let { i: fromI, off: fromOff } = cur.findPos(from, -1);
|
|
586
|
+
let dLen = from - to;
|
|
587
|
+
for (let view of insert)
|
|
588
|
+
dLen += view.length;
|
|
589
|
+
parent.length += dLen;
|
|
590
|
+
replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd);
|
|
591
|
+
}
|
|
493
592
|
|
|
494
593
|
let [nav, doc] = typeof navigator != "undefined"
|
|
495
594
|
? [navigator, document]
|
|
@@ -521,21 +620,8 @@ var browser = {
|
|
|
521
620
|
tabSize: doc.documentElement.style.tabSize != null ? "tab-size" : "-moz-tab-size"
|
|
522
621
|
};
|
|
523
622
|
|
|
524
|
-
const none$2 = [];
|
|
525
|
-
class InlineView extends ContentView {
|
|
526
|
-
/**
|
|
527
|
-
Return true when this view is equivalent to `other` and can take
|
|
528
|
-
on its role.
|
|
529
|
-
*/
|
|
530
|
-
become(_other) { return false; }
|
|
531
|
-
// When this is a zero-length view with a side, this should return a
|
|
532
|
-
// negative number to indicate it is before its position, or a
|
|
533
|
-
// positive number when after its position.
|
|
534
|
-
getSide() { return 0; }
|
|
535
|
-
}
|
|
536
|
-
InlineView.prototype.children = none$2;
|
|
537
623
|
const MaxJoinLen = 256;
|
|
538
|
-
class TextView extends
|
|
624
|
+
class TextView extends ContentView {
|
|
539
625
|
constructor(text) {
|
|
540
626
|
super();
|
|
541
627
|
this.text = text;
|
|
@@ -566,7 +652,7 @@ class TextView extends InlineView {
|
|
|
566
652
|
this.markDirty();
|
|
567
653
|
return true;
|
|
568
654
|
}
|
|
569
|
-
|
|
655
|
+
split(from) {
|
|
570
656
|
let result = new TextView(this.text.slice(from));
|
|
571
657
|
this.text = this.text.slice(0, from);
|
|
572
658
|
return result;
|
|
@@ -582,7 +668,7 @@ class TextView extends InlineView {
|
|
|
582
668
|
return textCoords(this.dom, pos, side);
|
|
583
669
|
}
|
|
584
670
|
}
|
|
585
|
-
class MarkView extends
|
|
671
|
+
class MarkView extends ContentView {
|
|
586
672
|
constructor(mark, children = [], length = 0) {
|
|
587
673
|
super();
|
|
588
674
|
this.mark = mark;
|
|
@@ -605,20 +691,20 @@ class MarkView extends InlineView {
|
|
|
605
691
|
this.createDOM();
|
|
606
692
|
super.sync(track);
|
|
607
693
|
}
|
|
608
|
-
merge(from, to, source, openStart, openEnd) {
|
|
694
|
+
merge(from, to, source, _hasStart, openStart, openEnd) {
|
|
609
695
|
if (source && (!(source instanceof MarkView && source.mark.eq(this.mark)) ||
|
|
610
696
|
(from && openStart <= 0) || (to < this.length && openEnd <= 0)))
|
|
611
697
|
return false;
|
|
612
|
-
|
|
698
|
+
mergeChildrenInto(this, from, to, source ? source.children : [], openStart - 1, openEnd - 1);
|
|
613
699
|
this.markDirty();
|
|
614
700
|
return true;
|
|
615
701
|
}
|
|
616
|
-
|
|
702
|
+
split(from) {
|
|
617
703
|
let result = [], off = 0, detachFrom = -1, i = 0;
|
|
618
704
|
for (let elt of this.children) {
|
|
619
705
|
let end = off + elt.length;
|
|
620
706
|
if (end > from)
|
|
621
|
-
result.push(off < from ? elt.
|
|
707
|
+
result.push(off < from ? elt.split(from - off) : elt);
|
|
622
708
|
if (detachFrom < 0 && off >= from)
|
|
623
709
|
detachFrom = i;
|
|
624
710
|
off = end;
|
|
@@ -626,8 +712,10 @@ class MarkView extends InlineView {
|
|
|
626
712
|
}
|
|
627
713
|
let length = this.length - from;
|
|
628
714
|
this.length = from;
|
|
629
|
-
if (detachFrom > -1)
|
|
630
|
-
this.
|
|
715
|
+
if (detachFrom > -1) {
|
|
716
|
+
this.children.length = detachFrom;
|
|
717
|
+
this.markDirty();
|
|
718
|
+
}
|
|
631
719
|
return new MarkView(this.mark, result, length);
|
|
632
720
|
}
|
|
633
721
|
domAtPos(pos) {
|
|
@@ -669,7 +757,7 @@ function textCoords(text, pos, side) {
|
|
|
669
757
|
return flatten ? flattenRect(rect, flatten < 0) : rect || null;
|
|
670
758
|
}
|
|
671
759
|
// Also used for collapsed ranges that don't have a placeholder widget!
|
|
672
|
-
class WidgetView extends
|
|
760
|
+
class WidgetView extends ContentView {
|
|
673
761
|
constructor(widget, length, side) {
|
|
674
762
|
super();
|
|
675
763
|
this.widget = widget;
|
|
@@ -679,7 +767,7 @@ class WidgetView extends InlineView {
|
|
|
679
767
|
static create(widget, length, side) {
|
|
680
768
|
return new (widget.customView || WidgetView)(widget, length, side);
|
|
681
769
|
}
|
|
682
|
-
|
|
770
|
+
split(from) {
|
|
683
771
|
let result = WidgetView.create(this.widget, this.length - from, this.side);
|
|
684
772
|
this.length -= from;
|
|
685
773
|
return result;
|
|
@@ -691,7 +779,7 @@ class WidgetView extends InlineView {
|
|
|
691
779
|
}
|
|
692
780
|
}
|
|
693
781
|
getSide() { return this.side; }
|
|
694
|
-
merge(from, to, source, openStart, openEnd) {
|
|
782
|
+
merge(from, to, source, hasStart, openStart, openEnd) {
|
|
695
783
|
if (source && (!(source instanceof WidgetView) || !this.widget.compare(source.widget) ||
|
|
696
784
|
from > 0 && openStart <= 0 || to < this.length && openEnd <= 0))
|
|
697
785
|
return false;
|
|
@@ -736,6 +824,11 @@ class WidgetView extends InlineView {
|
|
|
736
824
|
return (pos == 0 && side > 0 || pos == this.length && side <= 0) ? rect : flattenRect(rect, pos == 0);
|
|
737
825
|
}
|
|
738
826
|
get isEditable() { return false; }
|
|
827
|
+
destroy() {
|
|
828
|
+
super.destroy();
|
|
829
|
+
if (this.dom)
|
|
830
|
+
this.widget.destroy(this.dom);
|
|
831
|
+
}
|
|
739
832
|
}
|
|
740
833
|
class CompositionView extends WidgetView {
|
|
741
834
|
domAtPos(pos) { return new DOMPos(this.widget.text, pos); }
|
|
@@ -752,7 +845,7 @@ class CompositionView extends WidgetView {
|
|
|
752
845
|
// These are drawn around uneditable widgets to avoid a number of
|
|
753
846
|
// browser bugs that show up when the cursor is directly next to
|
|
754
847
|
// uneditable inline content.
|
|
755
|
-
class WidgetBufferView extends
|
|
848
|
+
class WidgetBufferView extends ContentView {
|
|
756
849
|
constructor(side) {
|
|
757
850
|
super();
|
|
758
851
|
this.side = side;
|
|
@@ -762,7 +855,7 @@ class WidgetBufferView extends InlineView {
|
|
|
762
855
|
become(other) {
|
|
763
856
|
return other instanceof WidgetBufferView && other.side == this.side;
|
|
764
857
|
}
|
|
765
|
-
|
|
858
|
+
split() { return new WidgetBufferView(this.side); }
|
|
766
859
|
sync() {
|
|
767
860
|
if (!this.dom)
|
|
768
861
|
this.setDOM(document.createTextNode("\u200b"));
|
|
@@ -780,90 +873,7 @@ class WidgetBufferView extends InlineView {
|
|
|
780
873
|
return Text.of([this.dom.nodeValue.replace(/\u200b/g, "")]);
|
|
781
874
|
}
|
|
782
875
|
}
|
|
783
|
-
|
|
784
|
-
let cur = parent.childCursor();
|
|
785
|
-
let { i: toI, off: toOff } = cur.findPos(to, 1);
|
|
786
|
-
let { i: fromI, off: fromOff } = cur.findPos(from, -1);
|
|
787
|
-
let dLen = from - to;
|
|
788
|
-
for (let view of elts)
|
|
789
|
-
dLen += view.length;
|
|
790
|
-
parent.length += dLen;
|
|
791
|
-
let { children } = parent;
|
|
792
|
-
// Both from and to point into the same child view
|
|
793
|
-
if (fromI == toI && fromOff) {
|
|
794
|
-
let start = children[fromI];
|
|
795
|
-
// Maybe just update that view and be done
|
|
796
|
-
if (elts.length == 1 && start.merge(fromOff, toOff, elts[0], openStart, openEnd))
|
|
797
|
-
return;
|
|
798
|
-
if (elts.length == 0) {
|
|
799
|
-
start.merge(fromOff, toOff, null, openStart, openEnd);
|
|
800
|
-
return;
|
|
801
|
-
}
|
|
802
|
-
// Otherwise split it, so that we don't have to worry about aliasing front/end afterwards
|
|
803
|
-
let after = start.slice(toOff);
|
|
804
|
-
if (after.merge(0, 0, elts[elts.length - 1], 0, openEnd))
|
|
805
|
-
elts[elts.length - 1] = after;
|
|
806
|
-
else
|
|
807
|
-
elts.push(after);
|
|
808
|
-
toI++;
|
|
809
|
-
openEnd = toOff = 0;
|
|
810
|
-
}
|
|
811
|
-
// Make sure start and end positions fall on node boundaries
|
|
812
|
-
// (fromOff/toOff are no longer used after this), and that if the
|
|
813
|
-
// start or end of the elts can be merged with adjacent nodes,
|
|
814
|
-
// this is done
|
|
815
|
-
if (toOff) {
|
|
816
|
-
let end = children[toI];
|
|
817
|
-
if (elts.length && end.merge(0, toOff, elts[elts.length - 1], 0, openEnd)) {
|
|
818
|
-
elts.pop();
|
|
819
|
-
openEnd = elts.length ? 0 : openStart;
|
|
820
|
-
}
|
|
821
|
-
else {
|
|
822
|
-
end.merge(0, toOff, null, 0, 0);
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
else if (toI < children.length && elts.length &&
|
|
826
|
-
children[toI].merge(0, 0, elts[elts.length - 1], 0, openEnd)) {
|
|
827
|
-
elts.pop();
|
|
828
|
-
openEnd = elts.length ? 0 : openStart;
|
|
829
|
-
}
|
|
830
|
-
if (fromOff) {
|
|
831
|
-
let start = children[fromI];
|
|
832
|
-
if (elts.length && start.merge(fromOff, start.length, elts[0], openStart, 0)) {
|
|
833
|
-
elts.shift();
|
|
834
|
-
openStart = elts.length ? 0 : openEnd;
|
|
835
|
-
}
|
|
836
|
-
else {
|
|
837
|
-
start.merge(fromOff, start.length, null, 0, 0);
|
|
838
|
-
}
|
|
839
|
-
fromI++;
|
|
840
|
-
}
|
|
841
|
-
else if (fromI && elts.length) {
|
|
842
|
-
let end = children[fromI - 1];
|
|
843
|
-
if (end.merge(end.length, end.length, elts[0], openStart, 0)) {
|
|
844
|
-
elts.shift();
|
|
845
|
-
openStart = elts.length ? 0 : openEnd;
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
// Then try to merge any mergeable nodes at the start and end of
|
|
849
|
-
// the changed range
|
|
850
|
-
while (fromI < toI && elts.length && children[toI - 1].become(elts[elts.length - 1])) {
|
|
851
|
-
elts.pop();
|
|
852
|
-
toI--;
|
|
853
|
-
openEnd = elts.length ? 0 : openStart;
|
|
854
|
-
}
|
|
855
|
-
while (fromI < toI && elts.length && children[fromI].become(elts[0])) {
|
|
856
|
-
elts.shift();
|
|
857
|
-
fromI++;
|
|
858
|
-
openStart = elts.length ? 0 : openEnd;
|
|
859
|
-
}
|
|
860
|
-
if (!elts.length && fromI && toI < children.length &&
|
|
861
|
-
children[toI].merge(0, 0, children[fromI - 1], openStart, openEnd))
|
|
862
|
-
fromI--;
|
|
863
|
-
// And if anything remains, splice the child array to insert the new elts
|
|
864
|
-
if (elts.length || fromI != toI)
|
|
865
|
-
parent.replaceChildren(fromI, toI, elts);
|
|
866
|
-
}
|
|
876
|
+
TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren;
|
|
867
877
|
function inlineDOMAtPos(dom, children, pos) {
|
|
868
878
|
let i = 0;
|
|
869
879
|
for (let off = 0; i < children.length; i++) {
|
|
@@ -1004,6 +1014,11 @@ class WidgetType {
|
|
|
1004
1014
|
@internal
|
|
1005
1015
|
*/
|
|
1006
1016
|
get customView() { return null; }
|
|
1017
|
+
/**
|
|
1018
|
+
This is called when the an instance of the widget is removed
|
|
1019
|
+
from the editor view.
|
|
1020
|
+
*/
|
|
1021
|
+
destroy(_dom) { }
|
|
1007
1022
|
}
|
|
1008
1023
|
/**
|
|
1009
1024
|
The different types of blocks that can occur in an editor view.
|
|
@@ -1163,12 +1178,12 @@ class PointDecoration extends Decoration {
|
|
|
1163
1178
|
super(startSide, endSide, widget, spec);
|
|
1164
1179
|
this.block = block;
|
|
1165
1180
|
this.isReplace = isReplace;
|
|
1166
|
-
this.mapMode = !block ? MapMode.TrackDel : startSide
|
|
1181
|
+
this.mapMode = !block ? MapMode.TrackDel : startSide <= 0 ? MapMode.TrackBefore : MapMode.TrackAfter;
|
|
1167
1182
|
}
|
|
1168
1183
|
// Only relevant when this.block == true
|
|
1169
1184
|
get type() {
|
|
1170
1185
|
return this.startSide < this.endSide ? BlockType.WidgetRange
|
|
1171
|
-
: this.startSide
|
|
1186
|
+
: this.startSide <= 0 ? BlockType.WidgetBefore : BlockType.WidgetAfter;
|
|
1172
1187
|
}
|
|
1173
1188
|
get heightRelevant() { return this.block || !!this.widget && this.widget.estimatedHeight >= 5; }
|
|
1174
1189
|
eq(other) {
|
|
@@ -1178,7 +1193,7 @@ class PointDecoration extends Decoration {
|
|
|
1178
1193
|
this.startSide == other.startSide && this.endSide == other.endSide;
|
|
1179
1194
|
}
|
|
1180
1195
|
range(from, to = from) {
|
|
1181
|
-
if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide
|
|
1196
|
+
if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide <= 0)))
|
|
1182
1197
|
throw new RangeError("Invalid range for replacement decoration");
|
|
1183
1198
|
if (!this.isReplace && to != from)
|
|
1184
1199
|
throw new RangeError("Widget decorations can only have zero-length ranges");
|
|
@@ -1215,16 +1230,16 @@ class LineView extends ContentView {
|
|
|
1215
1230
|
this.breakAfter = 0;
|
|
1216
1231
|
}
|
|
1217
1232
|
// Consumes source
|
|
1218
|
-
merge(from, to, source,
|
|
1233
|
+
merge(from, to, source, hasStart, openStart, openEnd) {
|
|
1219
1234
|
if (source) {
|
|
1220
1235
|
if (!(source instanceof LineView))
|
|
1221
1236
|
return false;
|
|
1222
1237
|
if (!this.dom)
|
|
1223
1238
|
source.transferDOM(this); // Reuse source.dom when appropriate
|
|
1224
1239
|
}
|
|
1225
|
-
if (
|
|
1240
|
+
if (hasStart)
|
|
1226
1241
|
this.setDeco(source ? source.attrs : null);
|
|
1227
|
-
|
|
1242
|
+
mergeChildrenInto(this, from, to, source ? source.children : [], openStart, openEnd);
|
|
1228
1243
|
return true;
|
|
1229
1244
|
}
|
|
1230
1245
|
split(at) {
|
|
@@ -1234,16 +1249,14 @@ class LineView extends ContentView {
|
|
|
1234
1249
|
return end;
|
|
1235
1250
|
let { i, off } = this.childPos(at);
|
|
1236
1251
|
if (off) {
|
|
1237
|
-
end.append(this.children[i].
|
|
1238
|
-
this.children[i].merge(off, this.children[i].length, null, 0, 0);
|
|
1252
|
+
end.append(this.children[i].split(off), 0);
|
|
1253
|
+
this.children[i].merge(off, this.children[i].length, null, false, 0, 0);
|
|
1239
1254
|
i++;
|
|
1240
1255
|
}
|
|
1241
1256
|
for (let j = i; j < this.children.length; j++)
|
|
1242
1257
|
end.append(this.children[j], 0);
|
|
1243
|
-
while (i > 0 && this.children[i - 1].length == 0)
|
|
1244
|
-
this.children[i
|
|
1245
|
-
i--;
|
|
1246
|
-
}
|
|
1258
|
+
while (i > 0 && this.children[i - 1].length == 0)
|
|
1259
|
+
this.children[--i].destroy();
|
|
1247
1260
|
this.children.length = i;
|
|
1248
1261
|
this.markDirty();
|
|
1249
1262
|
this.length = at;
|
|
@@ -1266,7 +1279,6 @@ class LineView extends ContentView {
|
|
|
1266
1279
|
this.attrs = attrs;
|
|
1267
1280
|
}
|
|
1268
1281
|
}
|
|
1269
|
-
// Only called when building a line view in ContentBuilder
|
|
1270
1282
|
append(child, openStart) {
|
|
1271
1283
|
joinInlineInto(this, child, openStart);
|
|
1272
1284
|
}
|
|
@@ -1323,7 +1335,7 @@ class LineView extends ContentView {
|
|
|
1323
1335
|
coordsAt(pos, side) {
|
|
1324
1336
|
return coordsInChildren(this, pos, side);
|
|
1325
1337
|
}
|
|
1326
|
-
|
|
1338
|
+
become(_other) { return false; }
|
|
1327
1339
|
get type() { return BlockType.Text; }
|
|
1328
1340
|
static find(docView, pos) {
|
|
1329
1341
|
for (let i = 0, off = 0;; i++) {
|
|
@@ -1338,7 +1350,6 @@ class LineView extends ContentView {
|
|
|
1338
1350
|
}
|
|
1339
1351
|
}
|
|
1340
1352
|
}
|
|
1341
|
-
const none$1 = [];
|
|
1342
1353
|
class BlockWidgetView extends ContentView {
|
|
1343
1354
|
constructor(widget, length, type) {
|
|
1344
1355
|
super();
|
|
@@ -1364,7 +1375,7 @@ class BlockWidgetView extends ContentView {
|
|
|
1364
1375
|
end.breakAfter = this.breakAfter;
|
|
1365
1376
|
return end;
|
|
1366
1377
|
}
|
|
1367
|
-
get children() { return
|
|
1378
|
+
get children() { return noChildren; }
|
|
1368
1379
|
sync() {
|
|
1369
1380
|
if (!this.dom || !this.widget.updateDOM(this.dom)) {
|
|
1370
1381
|
this.setDOM(this.widget.toDOM(this.editorView));
|
|
@@ -1375,7 +1386,7 @@ class BlockWidgetView extends ContentView {
|
|
|
1375
1386
|
return this.parent ? this.parent.view.state.doc.slice(this.posAtStart, this.posAtEnd) : Text$1.empty;
|
|
1376
1387
|
}
|
|
1377
1388
|
domBoundsAround() { return null; }
|
|
1378
|
-
|
|
1389
|
+
become(other) {
|
|
1379
1390
|
if (other instanceof BlockWidgetView && other.type == this.type &&
|
|
1380
1391
|
other.widget.constructor == this.widget.constructor) {
|
|
1381
1392
|
if (!other.widget.eq(this.widget))
|
|
@@ -1389,6 +1400,11 @@ class BlockWidgetView extends ContentView {
|
|
|
1389
1400
|
}
|
|
1390
1401
|
ignoreMutation() { return true; }
|
|
1391
1402
|
ignoreEvent(event) { return this.widget.ignoreEvent(event); }
|
|
1403
|
+
destroy() {
|
|
1404
|
+
super.destroy();
|
|
1405
|
+
if (this.dom)
|
|
1406
|
+
this.widget.destroy(this.dom);
|
|
1407
|
+
}
|
|
1392
1408
|
}
|
|
1393
1409
|
|
|
1394
1410
|
class ContentBuilder {
|
|
@@ -2041,71 +2057,9 @@ class DocView extends ContentView {
|
|
|
2041
2057
|
let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, deco);
|
|
2042
2058
|
let { i: toI, off: toOff } = cursor.findPos(toA, 1);
|
|
2043
2059
|
let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
|
|
2044
|
-
|
|
2060
|
+
replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
|
|
2045
2061
|
}
|
|
2046
2062
|
}
|
|
2047
|
-
replaceRange(fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd) {
|
|
2048
|
-
let before = this.children[fromI], last = content.length ? content[content.length - 1] : null;
|
|
2049
|
-
let breakAtEnd = last ? last.breakAfter : breakAtStart;
|
|
2050
|
-
// Change within a single line
|
|
2051
|
-
if (fromI == toI && !breakAtStart && !breakAtEnd && content.length < 2 &&
|
|
2052
|
-
before.merge(fromOff, toOff, content.length ? last : null, fromOff == 0, openStart, openEnd))
|
|
2053
|
-
return;
|
|
2054
|
-
let after = this.children[toI];
|
|
2055
|
-
// Make sure the end of the line after the update is preserved in `after`
|
|
2056
|
-
if (toOff < after.length) {
|
|
2057
|
-
// If we're splitting a line, separate part of the start line to
|
|
2058
|
-
// avoid that being mangled when updating the start line.
|
|
2059
|
-
if (fromI == toI) {
|
|
2060
|
-
after = after.split(toOff);
|
|
2061
|
-
toOff = 0;
|
|
2062
|
-
}
|
|
2063
|
-
// If the element after the replacement should be merged with
|
|
2064
|
-
// the last replacing element, update `content`
|
|
2065
|
-
if (!breakAtEnd && last && after.merge(0, toOff, last, true, 0, openEnd)) {
|
|
2066
|
-
content[content.length - 1] = after;
|
|
2067
|
-
}
|
|
2068
|
-
else {
|
|
2069
|
-
// Remove the start of the after element, if necessary, and
|
|
2070
|
-
// add it to `content`.
|
|
2071
|
-
if (toOff)
|
|
2072
|
-
after.merge(0, toOff, null, false, 0, openEnd);
|
|
2073
|
-
content.push(after);
|
|
2074
|
-
}
|
|
2075
|
-
}
|
|
2076
|
-
else if (after.breakAfter) {
|
|
2077
|
-
// The element at `toI` is entirely covered by this range.
|
|
2078
|
-
// Preserve its line break, if any.
|
|
2079
|
-
if (last)
|
|
2080
|
-
last.breakAfter = 1;
|
|
2081
|
-
else
|
|
2082
|
-
breakAtStart = 1;
|
|
2083
|
-
}
|
|
2084
|
-
// Since we've handled the next element from the current elements
|
|
2085
|
-
// now, make sure `toI` points after that.
|
|
2086
|
-
toI++;
|
|
2087
|
-
before.breakAfter = breakAtStart;
|
|
2088
|
-
if (fromOff > 0) {
|
|
2089
|
-
if (!breakAtStart && content.length && before.merge(fromOff, before.length, content[0], false, openStart, 0)) {
|
|
2090
|
-
before.breakAfter = content.shift().breakAfter;
|
|
2091
|
-
}
|
|
2092
|
-
else if (fromOff < before.length || before.children.length && before.children[before.children.length - 1].length == 0) {
|
|
2093
|
-
before.merge(fromOff, before.length, null, false, openStart, 0);
|
|
2094
|
-
}
|
|
2095
|
-
fromI++;
|
|
2096
|
-
}
|
|
2097
|
-
// Try to merge widgets on the boundaries of the replacement
|
|
2098
|
-
while (fromI < toI && content.length) {
|
|
2099
|
-
if (this.children[toI - 1].match(content[content.length - 1]))
|
|
2100
|
-
toI--, content.pop();
|
|
2101
|
-
else if (this.children[fromI].match(content[0]))
|
|
2102
|
-
fromI++, content.shift();
|
|
2103
|
-
else
|
|
2104
|
-
break;
|
|
2105
|
-
}
|
|
2106
|
-
if (fromI < toI || content.length)
|
|
2107
|
-
this.replaceChildren(fromI, toI, content);
|
|
2108
|
-
}
|
|
2109
2063
|
// Sync the DOM selection to this.state.selection
|
|
2110
2064
|
updateSelection(mustRead = false, fromPointer = false) {
|
|
2111
2065
|
if (mustRead)
|
|
@@ -2368,15 +2322,10 @@ function computeCompositionDeco(view, changes) {
|
|
|
2368
2322
|
if (!textNode)
|
|
2369
2323
|
return Decoration.none;
|
|
2370
2324
|
let cView = view.docView.nearest(textNode);
|
|
2325
|
+
if (!cView)
|
|
2326
|
+
return Decoration.none;
|
|
2371
2327
|
let from, to, topNode = textNode;
|
|
2372
|
-
if (cView instanceof
|
|
2373
|
-
while (cView.parent instanceof InlineView)
|
|
2374
|
-
cView = cView.parent;
|
|
2375
|
-
from = cView.posAtStart;
|
|
2376
|
-
to = from + cView.length;
|
|
2377
|
-
topNode = cView.dom;
|
|
2378
|
-
}
|
|
2379
|
-
else if (cView instanceof LineView) {
|
|
2328
|
+
if (cView instanceof LineView) {
|
|
2380
2329
|
while (topNode.parentNode != cView.dom)
|
|
2381
2330
|
topNode = topNode.parentNode;
|
|
2382
2331
|
let prev = topNode.previousSibling;
|
|
@@ -2385,7 +2334,17 @@ function computeCompositionDeco(view, changes) {
|
|
|
2385
2334
|
from = to = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
|
|
2386
2335
|
}
|
|
2387
2336
|
else {
|
|
2388
|
-
|
|
2337
|
+
for (;;) {
|
|
2338
|
+
let { parent } = cView;
|
|
2339
|
+
if (!parent)
|
|
2340
|
+
return Decoration.none;
|
|
2341
|
+
if (parent instanceof LineView)
|
|
2342
|
+
break;
|
|
2343
|
+
cView = parent;
|
|
2344
|
+
}
|
|
2345
|
+
from = cView.posAtStart;
|
|
2346
|
+
to = from + cView.length;
|
|
2347
|
+
topNode = cView.dom;
|
|
2389
2348
|
}
|
|
2390
2349
|
let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
|
|
2391
2350
|
let text = textNode.nodeValue, { state } = view;
|