prosemirror-transform 1.5.0-beta.1 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/dist/index.cjs +301 -42
- package/dist/index.d.ts +123 -4
- package/dist/index.js +229 -15
- package/package.json +1 -1
- package/src/README.md +3 -0
- package/src/attr_step.ts +53 -0
- package/src/index.ts +2 -1
- package/src/map.ts +28 -9
- package/src/mark_step.ts +96 -0
- package/src/replace_step.ts +2 -2
- package/src/transform.ts +27 -0
- package/dist/index.es.js +0 -1702
- package/dist/index.es.js.map +0 -1
- package/dist/index.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ReplaceError, Slice, Fragment, MarkType } from 'prosemirror-model';
|
|
1
|
+
import { ReplaceError, Slice, Fragment, MarkType, Mark } from 'prosemirror-model';
|
|
2
2
|
|
|
3
3
|
// Recovery values encode a range index and an offset. They are
|
|
4
4
|
// represented as numbers, because tons of them will be created when
|
|
@@ -14,6 +14,7 @@ const factor16 = Math.pow(2, 16);
|
|
|
14
14
|
function makeRecover(index, offset) { return index + offset * factor16; }
|
|
15
15
|
function recoverIndex(value) { return value & lower16; }
|
|
16
16
|
function recoverOffset(value) { return (value - (value & lower16)) / factor16; }
|
|
17
|
+
const DEL_BEFORE = 1, DEL_AFTER = 2, DEL_ACROSS = 4, DEL_SIDE = 8;
|
|
17
18
|
/**
|
|
18
19
|
An object representing a mapped position with extra
|
|
19
20
|
information.
|
|
@@ -28,18 +29,37 @@ class MapResult {
|
|
|
28
29
|
*/
|
|
29
30
|
pos,
|
|
30
31
|
/**
|
|
31
|
-
|
|
32
|
-
the step removed its surroundings from the document.
|
|
32
|
+
@internal
|
|
33
33
|
*/
|
|
34
|
-
|
|
34
|
+
delInfo,
|
|
35
35
|
/**
|
|
36
36
|
@internal
|
|
37
37
|
*/
|
|
38
|
-
recover
|
|
38
|
+
recover) {
|
|
39
39
|
this.pos = pos;
|
|
40
|
-
this.
|
|
40
|
+
this.delInfo = delInfo;
|
|
41
41
|
this.recover = recover;
|
|
42
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
Tells you whether the position was deleted, that is, whether the
|
|
45
|
+
step removed the token on the side queried (via the `assoc`)
|
|
46
|
+
argument from the document.
|
|
47
|
+
*/
|
|
48
|
+
get deleted() { return (this.delInfo & DEL_SIDE) > 0; }
|
|
49
|
+
/**
|
|
50
|
+
Tells you whether the token before the mapped position was deleted.
|
|
51
|
+
*/
|
|
52
|
+
get deletedBefore() { return (this.delInfo & (DEL_BEFORE | DEL_ACROSS)) > 0; }
|
|
53
|
+
/**
|
|
54
|
+
True when the token after the mapped position was deleted.
|
|
55
|
+
*/
|
|
56
|
+
get deletedAfter() { return (this.delInfo & (DEL_AFTER | DEL_ACROSS)) > 0; }
|
|
57
|
+
/**
|
|
58
|
+
Tells whether any of the steps mapped through deletes across the
|
|
59
|
+
position (including both the token before and after the
|
|
60
|
+
position).
|
|
61
|
+
*/
|
|
62
|
+
get deletedAcross() { return (this.delInfo & DEL_ACROSS) > 0; }
|
|
43
63
|
}
|
|
44
64
|
/**
|
|
45
65
|
A map describing the deletions and insertions made by a step, which
|
|
@@ -95,11 +115,14 @@ class StepMap {
|
|
|
95
115
|
if (simple)
|
|
96
116
|
return result;
|
|
97
117
|
let recover = pos == (assoc < 0 ? start : end) ? null : makeRecover(i / 3, pos - start);
|
|
98
|
-
|
|
118
|
+
let del = pos == start ? DEL_AFTER : pos == end ? DEL_BEFORE : DEL_ACROSS;
|
|
119
|
+
if (assoc < 0 ? pos != start : pos != end)
|
|
120
|
+
del |= DEL_SIDE;
|
|
121
|
+
return new MapResult(result, del, recover);
|
|
99
122
|
}
|
|
100
123
|
diff += newSize - oldSize;
|
|
101
124
|
}
|
|
102
|
-
return simple ? pos + diff : new MapResult(pos + diff);
|
|
125
|
+
return simple ? pos + diff : new MapResult(pos + diff, 0, null);
|
|
103
126
|
}
|
|
104
127
|
/**
|
|
105
128
|
@internal
|
|
@@ -279,7 +302,7 @@ class Mapping {
|
|
|
279
302
|
@internal
|
|
280
303
|
*/
|
|
281
304
|
_map(pos, assoc, simple) {
|
|
282
|
-
let
|
|
305
|
+
let delInfo = 0;
|
|
283
306
|
for (let i = this.from; i < this.to; i++) {
|
|
284
307
|
let map = this.maps[i], result = map.mapResult(pos, assoc);
|
|
285
308
|
if (result.recover != null) {
|
|
@@ -290,11 +313,10 @@ class Mapping {
|
|
|
290
313
|
continue;
|
|
291
314
|
}
|
|
292
315
|
}
|
|
293
|
-
|
|
294
|
-
deleted = true;
|
|
316
|
+
delInfo |= result.delInfo;
|
|
295
317
|
pos = result.pos;
|
|
296
318
|
}
|
|
297
|
-
return simple ? pos : new MapResult(pos,
|
|
319
|
+
return simple ? pos : new MapResult(pos, delInfo, null);
|
|
298
320
|
}
|
|
299
321
|
}
|
|
300
322
|
|
|
@@ -533,6 +555,113 @@ class RemoveMarkStep extends Step {
|
|
|
533
555
|
}
|
|
534
556
|
}
|
|
535
557
|
Step.jsonID("removeMark", RemoveMarkStep);
|
|
558
|
+
/**
|
|
559
|
+
Add a mark to a specific node.
|
|
560
|
+
*/
|
|
561
|
+
class AddNodeMarkStep extends Step {
|
|
562
|
+
/**
|
|
563
|
+
Create a node mark step.
|
|
564
|
+
*/
|
|
565
|
+
constructor(
|
|
566
|
+
/**
|
|
567
|
+
The position of the target node.
|
|
568
|
+
*/
|
|
569
|
+
pos,
|
|
570
|
+
/**
|
|
571
|
+
The mark to add.
|
|
572
|
+
*/
|
|
573
|
+
mark) {
|
|
574
|
+
super();
|
|
575
|
+
this.pos = pos;
|
|
576
|
+
this.mark = mark;
|
|
577
|
+
}
|
|
578
|
+
apply(doc) {
|
|
579
|
+
let node = doc.nodeAt(this.pos);
|
|
580
|
+
if (!node)
|
|
581
|
+
return StepResult.fail("No node at mark step's position");
|
|
582
|
+
let updated = node.type.create(node.attrs, null, this.mark.addToSet(node.marks));
|
|
583
|
+
return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment.from(updated), 0, node.isLeaf ? 0 : 1));
|
|
584
|
+
}
|
|
585
|
+
invert(doc) {
|
|
586
|
+
let node = doc.nodeAt(this.pos);
|
|
587
|
+
if (node) {
|
|
588
|
+
let newSet = this.mark.addToSet(node.marks);
|
|
589
|
+
if (newSet.length == node.marks.length) {
|
|
590
|
+
for (let i = 0; i < node.marks.length; i++)
|
|
591
|
+
if (!node.marks[i].isInSet(newSet))
|
|
592
|
+
return new AddNodeMarkStep(this.pos, node.marks[i]);
|
|
593
|
+
return new AddNodeMarkStep(this.pos, this.mark);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
return new RemoveNodeMarkStep(this.pos, this.mark);
|
|
597
|
+
}
|
|
598
|
+
map(mapping) {
|
|
599
|
+
let pos = mapping.mapResult(this.pos, 1);
|
|
600
|
+
return pos.deletedAfter ? null : new AddNodeMarkStep(pos.pos, this.mark);
|
|
601
|
+
}
|
|
602
|
+
toJSON() {
|
|
603
|
+
return { stepType: "addNodeMark", pos: this.pos, mark: this.mark.toJSON() };
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
@internal
|
|
607
|
+
*/
|
|
608
|
+
static fromJSON(schema, json) {
|
|
609
|
+
if (typeof json.pos != "number")
|
|
610
|
+
throw new RangeError("Invalid input for AddNodeMarkStep.fromJSON");
|
|
611
|
+
return new AddNodeMarkStep(json.pos, schema.markFromJSON(json.mark));
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
Step.jsonID("addNodeMark", AddNodeMarkStep);
|
|
615
|
+
/**
|
|
616
|
+
Remove a mark from a specific node.
|
|
617
|
+
*/
|
|
618
|
+
class RemoveNodeMarkStep extends Step {
|
|
619
|
+
/**
|
|
620
|
+
Create a mark-removing step.
|
|
621
|
+
*/
|
|
622
|
+
constructor(
|
|
623
|
+
/**
|
|
624
|
+
The position of the target node.
|
|
625
|
+
*/
|
|
626
|
+
pos,
|
|
627
|
+
/**
|
|
628
|
+
The mark to remove.
|
|
629
|
+
*/
|
|
630
|
+
mark) {
|
|
631
|
+
super();
|
|
632
|
+
this.pos = pos;
|
|
633
|
+
this.mark = mark;
|
|
634
|
+
}
|
|
635
|
+
apply(doc) {
|
|
636
|
+
let node = doc.nodeAt(this.pos);
|
|
637
|
+
if (!node)
|
|
638
|
+
return StepResult.fail("No node at mark step's position");
|
|
639
|
+
let updated = node.type.create(node.attrs, null, this.mark.removeFromSet(node.marks));
|
|
640
|
+
return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment.from(updated), 0, node.isLeaf ? 0 : 1));
|
|
641
|
+
}
|
|
642
|
+
invert(doc) {
|
|
643
|
+
let node = doc.nodeAt(this.pos);
|
|
644
|
+
if (!node || !this.mark.isInSet(node.marks))
|
|
645
|
+
return this;
|
|
646
|
+
return new AddNodeMarkStep(this.pos, this.mark);
|
|
647
|
+
}
|
|
648
|
+
map(mapping) {
|
|
649
|
+
let pos = mapping.mapResult(this.pos, 1);
|
|
650
|
+
return pos.deletedAfter ? null : new RemoveNodeMarkStep(pos.pos, this.mark);
|
|
651
|
+
}
|
|
652
|
+
toJSON() {
|
|
653
|
+
return { stepType: "removeNodeMark", pos: this.pos, mark: this.mark.toJSON() };
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
@internal
|
|
657
|
+
*/
|
|
658
|
+
static fromJSON(schema, json) {
|
|
659
|
+
if (typeof json.pos != "number")
|
|
660
|
+
throw new RangeError("Invalid input for RemoveNodeMarkStep.fromJSON");
|
|
661
|
+
return new RemoveNodeMarkStep(json.pos, schema.markFromJSON(json.mark));
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
Step.jsonID("removeNodeMark", RemoveNodeMarkStep);
|
|
536
665
|
|
|
537
666
|
/**
|
|
538
667
|
Replace a part of the document with a slice of new content.
|
|
@@ -583,7 +712,7 @@ class ReplaceStep extends Step {
|
|
|
583
712
|
}
|
|
584
713
|
map(mapping) {
|
|
585
714
|
let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1);
|
|
586
|
-
if (from.
|
|
715
|
+
if (from.deletedAcross && to.deletedAcross)
|
|
587
716
|
return null;
|
|
588
717
|
return new ReplaceStep(from.pos, Math.max(from.pos, to.pos), this.slice);
|
|
589
718
|
}
|
|
@@ -696,7 +825,7 @@ class ReplaceAroundStep extends Step {
|
|
|
696
825
|
map(mapping) {
|
|
697
826
|
let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1);
|
|
698
827
|
let gapFrom = mapping.map(this.gapFrom, -1), gapTo = mapping.map(this.gapTo, 1);
|
|
699
|
-
if ((from.
|
|
828
|
+
if ((from.deletedAcross && to.deletedAcross) || gapFrom < from.pos || gapTo > to.pos)
|
|
700
829
|
return null;
|
|
701
830
|
return new ReplaceAroundStep(from.pos, to.pos, gapFrom, gapTo, this.slice, this.insert, this.structure);
|
|
702
831
|
}
|
|
@@ -1543,6 +1672,61 @@ function coveredDepths($from, $to) {
|
|
|
1543
1672
|
return result;
|
|
1544
1673
|
}
|
|
1545
1674
|
|
|
1675
|
+
/**
|
|
1676
|
+
Update an attribute in a specific node.
|
|
1677
|
+
*/
|
|
1678
|
+
class AttrStep extends Step {
|
|
1679
|
+
/**
|
|
1680
|
+
Construct an attribute step.
|
|
1681
|
+
*/
|
|
1682
|
+
constructor(
|
|
1683
|
+
/**
|
|
1684
|
+
The position of the target node.
|
|
1685
|
+
*/
|
|
1686
|
+
pos,
|
|
1687
|
+
/**
|
|
1688
|
+
The attribute to set.
|
|
1689
|
+
*/
|
|
1690
|
+
attr,
|
|
1691
|
+
// The attribute's new value.
|
|
1692
|
+
value) {
|
|
1693
|
+
super();
|
|
1694
|
+
this.pos = pos;
|
|
1695
|
+
this.attr = attr;
|
|
1696
|
+
this.value = value;
|
|
1697
|
+
}
|
|
1698
|
+
apply(doc) {
|
|
1699
|
+
let node = doc.nodeAt(this.pos);
|
|
1700
|
+
if (!node)
|
|
1701
|
+
return StepResult.fail("No node at attribute step's position");
|
|
1702
|
+
let attrs = Object.create(null);
|
|
1703
|
+
for (let name in node.attrs)
|
|
1704
|
+
attrs[name] = node.attrs[name];
|
|
1705
|
+
attrs[this.attr] = this.value;
|
|
1706
|
+
let updated = node.type.create(attrs, null, node.marks);
|
|
1707
|
+
return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment.from(updated), 0, node.isLeaf ? 0 : 1));
|
|
1708
|
+
}
|
|
1709
|
+
getMap() {
|
|
1710
|
+
return StepMap.empty;
|
|
1711
|
+
}
|
|
1712
|
+
invert(doc) {
|
|
1713
|
+
return new AttrStep(this.pos, this.attr, doc.nodeAt(this.pos).attrs[this.attr]);
|
|
1714
|
+
}
|
|
1715
|
+
map(mapping) {
|
|
1716
|
+
let pos = mapping.mapResult(this.pos, 1);
|
|
1717
|
+
return pos.deletedAfter ? null : new AttrStep(pos.pos, this.attr, this.value);
|
|
1718
|
+
}
|
|
1719
|
+
toJSON() {
|
|
1720
|
+
return { stepType: "attr", pos: this.pos, attr: this.attr, value: this.value };
|
|
1721
|
+
}
|
|
1722
|
+
static fromJSON(schema, json) {
|
|
1723
|
+
if (typeof json.pos != "number" || typeof json.attr != "string")
|
|
1724
|
+
throw new RangeError("Invalid input for AttrStep.fromJSON");
|
|
1725
|
+
return new AttrStep(json.pos, json.attr, json.value);
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
Step.jsonID("attr", AttrStep);
|
|
1729
|
+
|
|
1546
1730
|
/**
|
|
1547
1731
|
@internal
|
|
1548
1732
|
*/
|
|
@@ -1745,6 +1929,36 @@ class Transform {
|
|
|
1745
1929
|
return this;
|
|
1746
1930
|
}
|
|
1747
1931
|
/**
|
|
1932
|
+
Set a single attribute on a given node to a new value.
|
|
1933
|
+
*/
|
|
1934
|
+
setNodeAttribute(pos, attr, value) {
|
|
1935
|
+
this.step(new AttrStep(pos, attr, value));
|
|
1936
|
+
return this;
|
|
1937
|
+
}
|
|
1938
|
+
/**
|
|
1939
|
+
Add a mark to the node at position `pos`.
|
|
1940
|
+
*/
|
|
1941
|
+
addNodeMark(pos, mark) {
|
|
1942
|
+
this.step(new AddNodeMarkStep(pos, mark));
|
|
1943
|
+
return this;
|
|
1944
|
+
}
|
|
1945
|
+
/**
|
|
1946
|
+
Remove a mark (or a mark of the given type) from the node at
|
|
1947
|
+
position `pos`.
|
|
1948
|
+
*/
|
|
1949
|
+
removeNodeMark(pos, mark) {
|
|
1950
|
+
if (!(mark instanceof Mark)) {
|
|
1951
|
+
let node = this.doc.nodeAt(pos);
|
|
1952
|
+
if (!node)
|
|
1953
|
+
throw new RangeError("No node at position " + pos);
|
|
1954
|
+
mark = mark.isInSet(node.marks);
|
|
1955
|
+
if (!mark)
|
|
1956
|
+
return this;
|
|
1957
|
+
}
|
|
1958
|
+
this.step(new RemoveNodeMarkStep(pos, mark));
|
|
1959
|
+
return this;
|
|
1960
|
+
}
|
|
1961
|
+
/**
|
|
1748
1962
|
Split the node at the given position, and optionally, if `depth` is
|
|
1749
1963
|
greater than one, any number of nodes above that. By default, the
|
|
1750
1964
|
parts split off will inherit the node type of the original node.
|
|
@@ -1784,4 +1998,4 @@ class Transform {
|
|
|
1784
1998
|
}
|
|
1785
1999
|
}
|
|
1786
2000
|
|
|
1787
|
-
export { AddMarkStep, MapResult, Mapping, RemoveMarkStep, ReplaceAroundStep, ReplaceStep, Step, StepMap, StepResult, Transform, TransformError, canJoin, canSplit, dropPoint, findWrapping, insertPoint, joinPoint, liftTarget, replaceStep };
|
|
2001
|
+
export { AddMarkStep, AddNodeMarkStep, AttrStep, MapResult, Mapping, RemoveMarkStep, RemoveNodeMarkStep, ReplaceAroundStep, ReplaceStep, Step, StepMap, StepResult, Transform, TransformError, canJoin, canSplit, dropPoint, findWrapping, insertPoint, joinPoint, liftTarget, replaceStep };
|
package/package.json
CHANGED
package/src/README.md
CHANGED
package/src/attr_step.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {Fragment, Slice, Node, Schema} from "prosemirror-model"
|
|
2
|
+
import {Step, StepResult} from "./step"
|
|
3
|
+
import {StepMap, Mappable} from "./map"
|
|
4
|
+
|
|
5
|
+
/// Update an attribute in a specific node.
|
|
6
|
+
export class AttrStep extends Step {
|
|
7
|
+
/// Construct an attribute step.
|
|
8
|
+
constructor(
|
|
9
|
+
/// The position of the target node.
|
|
10
|
+
readonly pos: number,
|
|
11
|
+
/// The attribute to set.
|
|
12
|
+
readonly attr: string,
|
|
13
|
+
// The attribute's new value.
|
|
14
|
+
readonly value: any
|
|
15
|
+
) {
|
|
16
|
+
super()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
apply(doc: Node) {
|
|
20
|
+
let node = doc.nodeAt(this.pos)
|
|
21
|
+
if (!node) return StepResult.fail("No node at attribute step's position")
|
|
22
|
+
let attrs = Object.create(null)
|
|
23
|
+
for (let name in node.attrs) attrs[name] = node.attrs[name]
|
|
24
|
+
attrs[this.attr] = this.value
|
|
25
|
+
let updated = node.type.create(attrs, null, node.marks)
|
|
26
|
+
return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment.from(updated), 0, node.isLeaf ? 0 : 1))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
getMap() {
|
|
30
|
+
return StepMap.empty
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
invert(doc: Node) {
|
|
34
|
+
return new AttrStep(this.pos, this.attr, doc.nodeAt(this.pos)!.attrs[this.attr])
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
map(mapping: Mappable) {
|
|
38
|
+
let pos = mapping.mapResult(this.pos, 1)
|
|
39
|
+
return pos.deletedAfter ? null : new AttrStep(pos.pos, this.attr, this.value)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
toJSON(): any {
|
|
43
|
+
return {stepType: "attr", pos: this.pos, attr: this.attr, value: this.value}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static fromJSON(schema: Schema, json: any) {
|
|
47
|
+
if (typeof json.pos != "number" || typeof json.attr != "string")
|
|
48
|
+
throw new RangeError("Invalid input for AttrStep.fromJSON")
|
|
49
|
+
return new AttrStep(json.pos, json.attr, json.value)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
Step.jsonID("attr", AttrStep)
|
package/src/index.ts
CHANGED
|
@@ -4,7 +4,8 @@ export {TransformError} from "./transform"
|
|
|
4
4
|
export {Step, StepResult} from "./step"
|
|
5
5
|
export {joinPoint, canJoin, canSplit, insertPoint, dropPoint, liftTarget, findWrapping} from "./structure"
|
|
6
6
|
export {StepMap, MapResult, Mapping, Mappable} from "./map"
|
|
7
|
-
export {AddMarkStep, RemoveMarkStep} from "./mark_step"
|
|
7
|
+
export {AddMarkStep, RemoveMarkStep, AddNodeMarkStep, RemoveNodeMarkStep} from "./mark_step"
|
|
8
8
|
export {ReplaceStep, ReplaceAroundStep} from "./replace_step"
|
|
9
|
+
export {AttrStep} from "./attr_step"
|
|
9
10
|
import "./mark"
|
|
10
11
|
export {replaceStep} from "./replace"
|
package/src/map.ts
CHANGED
|
@@ -33,6 +33,8 @@ function makeRecover(index: number, offset: number) { return index + offset * fa
|
|
|
33
33
|
function recoverIndex(value: number) { return value & lower16 }
|
|
34
34
|
function recoverOffset(value: number) { return (value - (value & lower16)) / factor16 }
|
|
35
35
|
|
|
36
|
+
const DEL_BEFORE = 1, DEL_AFTER = 2, DEL_ACROSS = 4, DEL_SIDE = 8
|
|
37
|
+
|
|
36
38
|
/// An object representing a mapped position with extra
|
|
37
39
|
/// information.
|
|
38
40
|
export class MapResult {
|
|
@@ -40,12 +42,27 @@ export class MapResult {
|
|
|
40
42
|
constructor(
|
|
41
43
|
/// The mapped version of the position.
|
|
42
44
|
readonly pos: number,
|
|
43
|
-
/// Tells you whether the position was deleted, that is, whether
|
|
44
|
-
/// the step removed its surroundings from the document.
|
|
45
|
-
readonly deleted: boolean = false,
|
|
46
45
|
/// @internal
|
|
47
|
-
readonly
|
|
46
|
+
readonly delInfo: number,
|
|
47
|
+
/// @internal
|
|
48
|
+
readonly recover: number | null
|
|
48
49
|
) {}
|
|
50
|
+
|
|
51
|
+
/// Tells you whether the position was deleted, that is, whether the
|
|
52
|
+
/// step removed the token on the side queried (via the `assoc`)
|
|
53
|
+
/// argument from the document.
|
|
54
|
+
get deleted() { return (this.delInfo & DEL_SIDE) > 0 }
|
|
55
|
+
|
|
56
|
+
/// Tells you whether the token before the mapped position was deleted.
|
|
57
|
+
get deletedBefore() { return (this.delInfo & (DEL_BEFORE | DEL_ACROSS)) > 0 }
|
|
58
|
+
|
|
59
|
+
/// True when the token after the mapped position was deleted.
|
|
60
|
+
get deletedAfter() { return (this.delInfo & (DEL_AFTER | DEL_ACROSS)) > 0 }
|
|
61
|
+
|
|
62
|
+
/// Tells whether any of the steps mapped through deletes across the
|
|
63
|
+
/// position (including both the token before and after the
|
|
64
|
+
/// position).
|
|
65
|
+
get deletedAcross() { return (this.delInfo & DEL_ACROSS) > 0 }
|
|
49
66
|
}
|
|
50
67
|
|
|
51
68
|
/// A map describing the deletions and insertions made by a step, which
|
|
@@ -89,11 +106,13 @@ export class StepMap implements Mappable {
|
|
|
89
106
|
let result = start + diff + (side < 0 ? 0 : newSize)
|
|
90
107
|
if (simple) return result
|
|
91
108
|
let recover = pos == (assoc < 0 ? start : end) ? null : makeRecover(i / 3, pos - start)
|
|
92
|
-
|
|
109
|
+
let del = pos == start ? DEL_AFTER : pos == end ? DEL_BEFORE : DEL_ACROSS
|
|
110
|
+
if (assoc < 0 ? pos != start : pos != end) del |= DEL_SIDE
|
|
111
|
+
return new MapResult(result, del, recover)
|
|
93
112
|
}
|
|
94
113
|
diff += newSize - oldSize
|
|
95
114
|
}
|
|
96
|
-
return simple ? pos + diff : new MapResult(pos + diff)
|
|
115
|
+
return simple ? pos + diff : new MapResult(pos + diff, 0, null)
|
|
97
116
|
}
|
|
98
117
|
|
|
99
118
|
/// @internal
|
|
@@ -234,7 +253,7 @@ export class Mapping implements Mappable {
|
|
|
234
253
|
|
|
235
254
|
/// @internal
|
|
236
255
|
_map(pos: number, assoc: number, simple: boolean) {
|
|
237
|
-
let
|
|
256
|
+
let delInfo = 0
|
|
238
257
|
|
|
239
258
|
for (let i = this.from; i < this.to; i++) {
|
|
240
259
|
let map = this.maps[i], result = map.mapResult(pos, assoc)
|
|
@@ -247,10 +266,10 @@ export class Mapping implements Mappable {
|
|
|
247
266
|
}
|
|
248
267
|
}
|
|
249
268
|
|
|
250
|
-
|
|
269
|
+
delInfo |= result.delInfo
|
|
251
270
|
pos = result.pos
|
|
252
271
|
}
|
|
253
272
|
|
|
254
|
-
return simple ? pos : new MapResult(pos,
|
|
273
|
+
return simple ? pos : new MapResult(pos, delInfo, null)
|
|
255
274
|
}
|
|
256
275
|
}
|
package/src/mark_step.ts
CHANGED
|
@@ -126,3 +126,99 @@ export class RemoveMarkStep extends Step {
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
Step.jsonID("removeMark", RemoveMarkStep)
|
|
129
|
+
|
|
130
|
+
/// Add a mark to a specific node.
|
|
131
|
+
export class AddNodeMarkStep extends Step {
|
|
132
|
+
/// Create a node mark step.
|
|
133
|
+
constructor(
|
|
134
|
+
/// The position of the target node.
|
|
135
|
+
readonly pos: number,
|
|
136
|
+
/// The mark to add.
|
|
137
|
+
readonly mark: Mark
|
|
138
|
+
) {
|
|
139
|
+
super()
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
apply(doc: Node) {
|
|
143
|
+
let node = doc.nodeAt(this.pos)
|
|
144
|
+
if (!node) return StepResult.fail("No node at mark step's position")
|
|
145
|
+
let updated = node.type.create(node.attrs, null, this.mark.addToSet(node.marks))
|
|
146
|
+
return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment.from(updated), 0, node.isLeaf ? 0 : 1))
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
invert(doc: Node): Step {
|
|
150
|
+
let node = doc.nodeAt(this.pos)
|
|
151
|
+
if (node) {
|
|
152
|
+
let newSet = this.mark.addToSet(node.marks)
|
|
153
|
+
if (newSet.length == node.marks.length) {
|
|
154
|
+
for (let i = 0; i < node.marks.length; i++)
|
|
155
|
+
if (!node.marks[i].isInSet(newSet))
|
|
156
|
+
return new AddNodeMarkStep(this.pos, node.marks[i])
|
|
157
|
+
return new AddNodeMarkStep(this.pos, this.mark)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return new RemoveNodeMarkStep(this.pos, this.mark)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
map(mapping: Mappable): Step | null {
|
|
164
|
+
let pos = mapping.mapResult(this.pos, 1)
|
|
165
|
+
return pos.deletedAfter ? null : new AddNodeMarkStep(pos.pos, this.mark)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
toJSON(): any {
|
|
169
|
+
return {stepType: "addNodeMark", pos: this.pos, mark: this.mark.toJSON()}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/// @internal
|
|
173
|
+
static fromJSON(schema: Schema, json: any) {
|
|
174
|
+
if (typeof json.pos != "number")
|
|
175
|
+
throw new RangeError("Invalid input for AddNodeMarkStep.fromJSON")
|
|
176
|
+
return new AddNodeMarkStep(json.pos, schema.markFromJSON(json.mark))
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
Step.jsonID("addNodeMark", AddNodeMarkStep)
|
|
181
|
+
|
|
182
|
+
/// Remove a mark from a specific node.
|
|
183
|
+
export class RemoveNodeMarkStep extends Step {
|
|
184
|
+
/// Create a mark-removing step.
|
|
185
|
+
constructor(
|
|
186
|
+
/// The position of the target node.
|
|
187
|
+
readonly pos: number,
|
|
188
|
+
/// The mark to remove.
|
|
189
|
+
readonly mark: Mark
|
|
190
|
+
) {
|
|
191
|
+
super()
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
apply(doc: Node) {
|
|
195
|
+
let node = doc.nodeAt(this.pos)
|
|
196
|
+
if (!node) return StepResult.fail("No node at mark step's position")
|
|
197
|
+
let updated = node.type.create(node.attrs, null, this.mark.removeFromSet(node.marks))
|
|
198
|
+
return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment.from(updated), 0, node.isLeaf ? 0 : 1))
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
invert(doc: Node): Step {
|
|
202
|
+
let node = doc.nodeAt(this.pos)
|
|
203
|
+
if (!node || !this.mark.isInSet(node.marks)) return this
|
|
204
|
+
return new AddNodeMarkStep(this.pos, this.mark)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
map(mapping: Mappable): Step | null {
|
|
208
|
+
let pos = mapping.mapResult(this.pos, 1)
|
|
209
|
+
return pos.deletedAfter ? null : new RemoveNodeMarkStep(pos.pos, this.mark)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
toJSON(): any {
|
|
213
|
+
return {stepType: "removeNodeMark", pos: this.pos, mark: this.mark.toJSON()}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/// @internal
|
|
217
|
+
static fromJSON(schema: Schema, json: any) {
|
|
218
|
+
if (typeof json.pos != "number")
|
|
219
|
+
throw new RangeError("Invalid input for RemoveNodeMarkStep.fromJSON")
|
|
220
|
+
return new RemoveNodeMarkStep(json.pos, schema.markFromJSON(json.mark))
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
Step.jsonID("removeNodeMark", RemoveNodeMarkStep)
|
package/src/replace_step.ts
CHANGED
|
@@ -41,7 +41,7 @@ export class ReplaceStep extends Step {
|
|
|
41
41
|
|
|
42
42
|
map(mapping: Mappable) {
|
|
43
43
|
let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1)
|
|
44
|
-
if (from.
|
|
44
|
+
if (from.deletedAcross && to.deletedAcross) return null
|
|
45
45
|
return new ReplaceStep(from.pos, Math.max(from.pos, to.pos), this.slice)
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -135,7 +135,7 @@ export class ReplaceAroundStep extends Step {
|
|
|
135
135
|
map(mapping: Mappable) {
|
|
136
136
|
let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1)
|
|
137
137
|
let gapFrom = mapping.map(this.gapFrom, -1), gapTo = mapping.map(this.gapTo, 1)
|
|
138
|
-
if ((from.
|
|
138
|
+
if ((from.deletedAcross && to.deletedAcross) || gapFrom < from.pos || gapTo > to.pos) return null
|
|
139
139
|
return new ReplaceAroundStep(from.pos, to.pos, gapFrom, gapTo, this.slice, this.insert, this.structure)
|
|
140
140
|
}
|
|
141
141
|
|
package/src/transform.ts
CHANGED
|
@@ -5,6 +5,8 @@ import {Step} from "./step"
|
|
|
5
5
|
import {addMark, removeMark, clearIncompatible} from "./mark"
|
|
6
6
|
import {replaceStep, replaceRange, replaceRangeWith, deleteRange} from "./replace"
|
|
7
7
|
import {lift, wrap, setBlockType, setNodeMarkup, split, join} from "./structure"
|
|
8
|
+
import {AttrStep} from "./attr_step"
|
|
9
|
+
import {AddNodeMarkStep, RemoveNodeMarkStep} from "./mark_step"
|
|
8
10
|
|
|
9
11
|
/// @internal
|
|
10
12
|
export let TransformError = class extends Error {}
|
|
@@ -176,6 +178,31 @@ export class Transform {
|
|
|
176
178
|
return this
|
|
177
179
|
}
|
|
178
180
|
|
|
181
|
+
/// Set a single attribute on a given node to a new value.
|
|
182
|
+
setNodeAttribute(pos: number, attr: string, value: any): this {
|
|
183
|
+
this.step(new AttrStep(pos, attr, value))
|
|
184
|
+
return this
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/// Add a mark to the node at position `pos`.
|
|
188
|
+
addNodeMark(pos: number, mark: Mark): this {
|
|
189
|
+
this.step(new AddNodeMarkStep(pos, mark))
|
|
190
|
+
return this
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/// Remove a mark (or a mark of the given type) from the node at
|
|
194
|
+
/// position `pos`.
|
|
195
|
+
removeNodeMark(pos: number, mark: Mark | MarkType): this {
|
|
196
|
+
if (!(mark instanceof Mark)) {
|
|
197
|
+
let node = this.doc.nodeAt(pos)
|
|
198
|
+
if (!node) throw new RangeError("No node at position " + pos)
|
|
199
|
+
mark = mark.isInSet(node.marks)!
|
|
200
|
+
if (!mark) return this
|
|
201
|
+
}
|
|
202
|
+
this.step(new RemoveNodeMarkStep(pos, mark))
|
|
203
|
+
return this
|
|
204
|
+
}
|
|
205
|
+
|
|
179
206
|
/// Split the node at the given position, and optionally, if `depth` is
|
|
180
207
|
/// greater than one, any number of nodes above that. By default, the
|
|
181
208
|
/// parts split off will inherit the node type of the original node.
|