prosemirror-transform 1.11.0 → 1.12.1
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/CONTRIBUTING.md +19 -19
- package/README.md +3 -3
- package/dist/index.cjs +27 -7
- package/dist/index.d.cts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +43 -4
- package/package.json +2 -2
- package/src/replace.ts +27 -4
- package/src/replace_step.ts +10 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## 1.12.1 (2026-09-02)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix an issue where replace step fitting could create an invalid slice, and then crash or loop forever.
|
|
6
|
+
|
|
7
|
+
## 1.12.0 (2026-03-30)
|
|
8
|
+
|
|
9
|
+
### Bug fixes
|
|
10
|
+
|
|
11
|
+
Handle deletions that go from the beginning of one textblock to the beginning of another specially, deleting the selected textblocks entirely.
|
|
12
|
+
|
|
13
|
+
### New features
|
|
14
|
+
|
|
15
|
+
`ReplaceStep.MAP_BIAS` can now be adjusted to change the way insertion steps are mapped over other insertions at the same place. The default behavior remains the same for the sake of backwards compatibility.
|
|
16
|
+
|
|
1
17
|
## 1.11.0 (2026-01-21)
|
|
2
18
|
|
|
3
19
|
### New features
|
package/CONTRIBUTING.md
CHANGED
|
@@ -12,7 +12,7 @@ Community discussion, questions, and informal bug reporting is done on the
|
|
|
12
12
|
## Submitting bug reports
|
|
13
13
|
|
|
14
14
|
Report bugs on the
|
|
15
|
-
[
|
|
15
|
+
[issue tracker](https://code.haverbeke.berlin/prosemirror/prosemirror/issues).
|
|
16
16
|
Before reporting a bug, please read these pointers.
|
|
17
17
|
|
|
18
18
|
- The issue tracker is for *bugs*, not requests for help. Questions
|
|
@@ -34,25 +34,28 @@ Before reporting a bug, please read these pointers.
|
|
|
34
34
|
|
|
35
35
|
## Contributing code
|
|
36
36
|
|
|
37
|
+
Code generated by a language model is not welcome in this project.
|
|
38
|
+
Please don't waste my time with it.
|
|
39
|
+
|
|
37
40
|
If you want to make a change that involves a significant overhaul of
|
|
38
41
|
the code or introduces a user-visible new feature, create an
|
|
39
|
-
[
|
|
42
|
+
[issue](https://code.haverbeke.berlin/prosemirror/prosemirror/issues/)
|
|
43
|
+
first with your proposal.
|
|
40
44
|
|
|
41
|
-
- Make sure you have a [
|
|
45
|
+
- Make sure you have a [Codeberg](https://codeberg.org/user/sign_up)
|
|
46
|
+
or [GitHub](https://github.com/signup/free) account.
|
|
47
|
+
|
|
48
|
+
- Use that to create a [code.haverbeke.berlin
|
|
49
|
+
account](https://code.haverbeke.berlin/user/login).
|
|
42
50
|
|
|
43
|
-
- Fork the relevant repository
|
|
44
|
-
([how to fork a repo](https://help.github.com/articles/fork-a-repo))
|
|
51
|
+
- Fork the relevant repository.
|
|
45
52
|
|
|
46
53
|
- Create a local checkout of the code. You can use the
|
|
47
|
-
[main repository](https://
|
|
54
|
+
[main repository](https://code.haverbeke.berlin/prosemirror/prosemirror) to
|
|
48
55
|
easily check out all core modules.
|
|
49
56
|
|
|
50
57
|
- Make your changes, and commit them
|
|
51
58
|
|
|
52
|
-
- Follow the code style of the rest of the project (see below). Run
|
|
53
|
-
`npm run lint` (in the main repository checkout) to make sure that
|
|
54
|
-
the linter is happy.
|
|
55
|
-
|
|
56
59
|
- If your changes are easy to test or likely to regress, add tests in
|
|
57
60
|
the relevant `test/` directory. Either put them in an existing
|
|
58
61
|
`test-*.js` file, if they fit there, or add a new file.
|
|
@@ -60,13 +63,13 @@ the code or introduces a user-visible new feature, create an
|
|
|
60
63
|
- Make sure all tests pass. Run `npm run test` to verify tests pass
|
|
61
64
|
(you will need Node.js v6+).
|
|
62
65
|
|
|
63
|
-
- Submit a pull request
|
|
64
|
-
|
|
66
|
+
- Submit a pull request. Don't put more than one feature/fix in a
|
|
67
|
+
single pull request.
|
|
65
68
|
|
|
66
69
|
By contributing code to ProseMirror you
|
|
67
70
|
|
|
68
71
|
- Agree to license the contributed code under the project's [MIT
|
|
69
|
-
license](https://
|
|
72
|
+
license](https://code.haverbeke.berlin/prosemirror/prosemirror/blob/main/LICENSE).
|
|
70
73
|
|
|
71
74
|
- Confirm that you have the right to contribute and license the code
|
|
72
75
|
in question. (Either you hold all rights on the code, or the rights
|
|
@@ -86,19 +89,16 @@ By contributing code to ProseMirror you
|
|
|
86
89
|
- Follow the surrounding code when it comes to spacing, brace
|
|
87
90
|
placement, etc.
|
|
88
91
|
|
|
89
|
-
- Brace-less single-statement bodies are encouraged
|
|
90
|
-
don't impact readability
|
|
92
|
+
- Brace-less single-statement bodies are encouraged whenever they
|
|
93
|
+
don't impact readability.
|
|
91
94
|
|
|
92
|
-
- [getdocs](https://
|
|
95
|
+
- [getdocs-ts](https://code.haverbeke.berlin/marijn/getdocs-ts)-style doc comments
|
|
93
96
|
above items that are part of the public API.
|
|
94
97
|
|
|
95
98
|
- When documenting non-public items, you can put the type after a
|
|
96
99
|
single colon, so that getdocs doesn't pick it up and add it to the
|
|
97
100
|
API reference.
|
|
98
101
|
|
|
99
|
-
- The linter (`npm run lint`) complains about unused variables and
|
|
100
|
-
functions. Prefix their names with an underscore to muffle it.
|
|
101
|
-
|
|
102
102
|
- ProseMirror does *not* follow JSHint or JSLint prescribed style.
|
|
103
103
|
Patches that try to 'fix' code to pass one of these linters will not
|
|
104
104
|
be accepted.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# prosemirror-transform
|
|
2
2
|
|
|
3
|
-
[ [**WEBSITE**](https://prosemirror.net) | [**ISSUES**](https://
|
|
3
|
+
[ [**WEBSITE**](https://prosemirror.net) | [**ISSUES**](https://code.haverbeke.berlin/prosemirror/prosemirror/issues) | [**FORUM**](https://discuss.prosemirror.net) | [**CHANGELOG**](https://code.haverbeke.berlin/prosemirror/prosemirror-transform/src/branch/main/CHANGELOG.md) ]
|
|
4
4
|
|
|
5
5
|
This is a [core module](https://prosemirror.net/docs/ref/#transform) of [ProseMirror](https://prosemirror.net).
|
|
6
6
|
ProseMirror is a well-behaved rich semantic content editor based on
|
|
@@ -17,10 +17,10 @@ number of [examples](https://prosemirror.net/examples/) and the
|
|
|
17
17
|
[documentation](https://prosemirror.net/docs/).
|
|
18
18
|
|
|
19
19
|
This code is released under an
|
|
20
|
-
[MIT license](https://
|
|
20
|
+
[MIT license](https://code.haverbeke.berlin/prosemirror/prosemirror/src/branch/main/LICENSE).
|
|
21
21
|
There's a [forum](http://discuss.prosemirror.net) for general
|
|
22
22
|
discussion and support requests, and the
|
|
23
|
-
[
|
|
23
|
+
[bug tracker](https://code.haverbeke.berlin/prosemirror/prosemirror/issues)
|
|
24
24
|
is the place to report issues.
|
|
25
25
|
|
|
26
26
|
We aim to be an inclusive, welcoming community. To make that explicit,
|
package/dist/index.cjs
CHANGED
|
@@ -610,8 +610,8 @@ var ReplaceStep = function (_Step5) {
|
|
|
610
610
|
}, {
|
|
611
611
|
key: "map",
|
|
612
612
|
value: function map(mapping) {
|
|
613
|
-
var
|
|
614
|
-
|
|
613
|
+
var to = mapping.mapResult(this.to, -1);
|
|
614
|
+
var from = this.from == this.to && ReplaceStep.MAP_BIAS < 0 ? to : mapping.mapResult(this.from, 1);
|
|
615
615
|
if (from.deletedAcross && to.deletedAcross) return null;
|
|
616
616
|
return new ReplaceStep(from.pos, Math.max(from.pos, to.pos), this.slice, this.structure);
|
|
617
617
|
}
|
|
@@ -650,6 +650,7 @@ var ReplaceStep = function (_Step5) {
|
|
|
650
650
|
}]);
|
|
651
651
|
return ReplaceStep;
|
|
652
652
|
}(Step);
|
|
653
|
+
ReplaceStep.MAP_BIAS = 1;
|
|
653
654
|
Step.jsonID("replace", ReplaceStep);
|
|
654
655
|
var ReplaceAroundStep = function (_Step6) {
|
|
655
656
|
_inherits(ReplaceAroundStep, _Step6);
|
|
@@ -1253,9 +1254,9 @@ var Fitter = function () {
|
|
|
1253
1254
|
content = _this$unplaced.content,
|
|
1254
1255
|
openStart = _this$unplaced.openStart,
|
|
1255
1256
|
openEnd = _this$unplaced.openEnd;
|
|
1256
|
-
|
|
1257
|
-
if (
|
|
1258
|
-
this.unplaced = new prosemirrorModel.Slice(content, openStart + 1,
|
|
1257
|
+
if (maxOpen(content, -1) <= openStart) return false;
|
|
1258
|
+
if (this.unplaced.size > 1 && maxOpen(content, 1) > openEnd) openEnd++;
|
|
1259
|
+
this.unplaced = new prosemirrorModel.Slice(content, openStart + 1, openEnd);
|
|
1259
1260
|
return true;
|
|
1260
1261
|
}
|
|
1261
1262
|
}, {
|
|
@@ -1431,6 +1432,13 @@ function invalidMarks(type, fragment, start) {
|
|
|
1431
1432
|
function definesContent(type) {
|
|
1432
1433
|
return type.spec.defining || type.spec.definingForContent;
|
|
1433
1434
|
}
|
|
1435
|
+
function maxOpen(frag, side) {
|
|
1436
|
+
for (var count = 0;; count++) {
|
|
1437
|
+
var ch = side < 0 ? frag.firstChild : frag.lastChild;
|
|
1438
|
+
if (!ch || ch.isAtom) return count;
|
|
1439
|
+
frag = ch.content;
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1434
1442
|
function _replaceRange(tr, from, to, slice) {
|
|
1435
1443
|
if (!slice.size) return tr.deleteRange(from, to);
|
|
1436
1444
|
var $from = tr.doc.resolve(from),
|
|
@@ -1507,6 +1515,18 @@ function _replaceRangeWith(tr, from, to, node) {
|
|
|
1507
1515
|
function _deleteRange(tr, from, to) {
|
|
1508
1516
|
var $from = tr.doc.resolve(from),
|
|
1509
1517
|
$to = tr.doc.resolve(to);
|
|
1518
|
+
if ($from.parent.isTextblock && $to.parent.isTextblock && $from.start() != $to.start() && $from.parentOffset == 0 && $to.parentOffset == 0) {
|
|
1519
|
+
var shared = $from.sharedDepth(to),
|
|
1520
|
+
isolated = false;
|
|
1521
|
+
for (var d = $from.depth; d > shared; d--) if ($from.node(d).type.spec.isolating) isolated = true;
|
|
1522
|
+
for (var _d4 = $to.depth; _d4 > shared; _d4--) if ($to.node(_d4).type.spec.isolating) isolated = true;
|
|
1523
|
+
if (!isolated) {
|
|
1524
|
+
for (var _d5 = $from.depth; _d5 > 0 && from == $from.start(_d5); _d5--) from = $from.before(_d5);
|
|
1525
|
+
for (var _d6 = $to.depth; _d6 > 0 && to == $to.start(_d6); _d6--) to = $to.before(_d6);
|
|
1526
|
+
$from = tr.doc.resolve(from);
|
|
1527
|
+
$to = tr.doc.resolve(to);
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1510
1530
|
var covered = coveredDepths($from, $to);
|
|
1511
1531
|
for (var i = 0; i < covered.length; i++) {
|
|
1512
1532
|
var depth = covered[i],
|
|
@@ -1514,8 +1534,8 @@ function _deleteRange(tr, from, to) {
|
|
|
1514
1534
|
if (last && depth == 0 || $from.node(depth).type.contentMatch.validEnd) return tr["delete"]($from.start(depth), $to.end(depth));
|
|
1515
1535
|
if (depth > 0 && (last || $from.node(depth - 1).canReplace($from.index(depth - 1), $to.indexAfter(depth - 1)))) return tr["delete"]($from.before(depth), $to.after(depth));
|
|
1516
1536
|
}
|
|
1517
|
-
for (var
|
|
1518
|
-
if (from - $from.start(
|
|
1537
|
+
for (var _d7 = 1; _d7 <= $from.depth && _d7 <= $to.depth; _d7++) {
|
|
1538
|
+
if (from - $from.start(_d7) == $from.depth - _d7 && to > $from.end(_d7) && $to.end(_d7) - to != $to.depth - _d7 && $from.start(_d7 - 1) == $to.start(_d7 - 1) && $from.node(_d7 - 1).canReplace($from.index(_d7 - 1), $to.index(_d7 - 1))) return tr["delete"]($from.before(_d7), to);
|
|
1519
1539
|
}
|
|
1520
1540
|
tr["delete"](from, to);
|
|
1521
1541
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -704,6 +704,15 @@ declare class ReplaceStep extends Step {
|
|
|
704
704
|
map(mapping: Mappable): ReplaceStep | null;
|
|
705
705
|
merge(other: Step): ReplaceStep | null;
|
|
706
706
|
toJSON(): any;
|
|
707
|
+
/**
|
|
708
|
+
By default, for backwards compatibility, an inserting step
|
|
709
|
+
mapped over an insertion at that same position fill move after
|
|
710
|
+
the inserted content. In a collaborative editing situation, that
|
|
711
|
+
can make redone insertions appear in unexpected places. You can
|
|
712
|
+
set this to -1 to make such mapping keep the step before the
|
|
713
|
+
insertion instead.
|
|
714
|
+
*/
|
|
715
|
+
static MAP_BIAS: -1 | 1;
|
|
707
716
|
}
|
|
708
717
|
/**
|
|
709
718
|
Replace a part of the document with a slice of content, but
|
package/dist/index.d.ts
CHANGED
|
@@ -704,6 +704,15 @@ declare class ReplaceStep extends Step {
|
|
|
704
704
|
map(mapping: Mappable): ReplaceStep | null;
|
|
705
705
|
merge(other: Step): ReplaceStep | null;
|
|
706
706
|
toJSON(): any;
|
|
707
|
+
/**
|
|
708
|
+
By default, for backwards compatibility, an inserting step
|
|
709
|
+
mapped over an insertion at that same position fill move after
|
|
710
|
+
the inserted content. In a collaborative editing situation, that
|
|
711
|
+
can make redone insertions appear in unexpected places. You can
|
|
712
|
+
set this to -1 to make such mapping keep the step before the
|
|
713
|
+
insertion instead.
|
|
714
|
+
*/
|
|
715
|
+
static MAP_BIAS: -1 | 1;
|
|
707
716
|
}
|
|
708
717
|
/**
|
|
709
718
|
Replace a part of the document with a slice of content, but
|
package/dist/index.js
CHANGED
|
@@ -711,7 +711,8 @@ class ReplaceStep extends Step {
|
|
|
711
711
|
return new ReplaceStep(this.from, this.from + this.slice.size, doc.slice(this.from, this.to));
|
|
712
712
|
}
|
|
713
713
|
map(mapping) {
|
|
714
|
-
let
|
|
714
|
+
let to = mapping.mapResult(this.to, -1);
|
|
715
|
+
let from = this.from == this.to && ReplaceStep.MAP_BIAS < 0 ? to : mapping.mapResult(this.from, 1);
|
|
715
716
|
if (from.deletedAcross && to.deletedAcross)
|
|
716
717
|
return null;
|
|
717
718
|
return new ReplaceStep(from.pos, Math.max(from.pos, to.pos), this.slice, this.structure);
|
|
@@ -750,6 +751,15 @@ class ReplaceStep extends Step {
|
|
|
750
751
|
return new ReplaceStep(json.from, json.to, Slice.fromJSON(schema, json.slice), !!json.structure);
|
|
751
752
|
}
|
|
752
753
|
}
|
|
754
|
+
/**
|
|
755
|
+
By default, for backwards compatibility, an inserting step
|
|
756
|
+
mapped over an insertion at that same position fill move after
|
|
757
|
+
the inserted content. In a collaborative editing situation, that
|
|
758
|
+
can make redone insertions appear in unexpected places. You can
|
|
759
|
+
set this to -1 to make such mapping keep the step before the
|
|
760
|
+
insertion instead.
|
|
761
|
+
*/
|
|
762
|
+
ReplaceStep.MAP_BIAS = 1;
|
|
753
763
|
Step.jsonID("replace", ReplaceStep);
|
|
754
764
|
/**
|
|
755
765
|
Replace a part of the document with a slice of content, but
|
|
@@ -1464,10 +1474,11 @@ class Fitter {
|
|
|
1464
1474
|
}
|
|
1465
1475
|
openMore() {
|
|
1466
1476
|
let { content, openStart, openEnd } = this.unplaced;
|
|
1467
|
-
|
|
1468
|
-
if (!inner.childCount || inner.firstChild.isLeaf)
|
|
1477
|
+
if (maxOpen(content, -1) <= openStart)
|
|
1469
1478
|
return false;
|
|
1470
|
-
this.unplaced
|
|
1479
|
+
if (this.unplaced.size > 1 && maxOpen(content, 1) > openEnd)
|
|
1480
|
+
openEnd++;
|
|
1481
|
+
this.unplaced = new Slice(content, openStart + 1, openEnd);
|
|
1471
1482
|
return true;
|
|
1472
1483
|
}
|
|
1473
1484
|
dropNode() {
|
|
@@ -1637,6 +1648,14 @@ function invalidMarks(type, fragment, start) {
|
|
|
1637
1648
|
function definesContent(type) {
|
|
1638
1649
|
return type.spec.defining || type.spec.definingForContent;
|
|
1639
1650
|
}
|
|
1651
|
+
function maxOpen(frag, side) {
|
|
1652
|
+
for (let count = 0;; count++) {
|
|
1653
|
+
let ch = side < 0 ? frag.firstChild : frag.lastChild;
|
|
1654
|
+
if (!ch || ch.isAtom)
|
|
1655
|
+
return count;
|
|
1656
|
+
frag = ch.content;
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1640
1659
|
function replaceRange(tr, from, to, slice) {
|
|
1641
1660
|
if (!slice.size)
|
|
1642
1661
|
return tr.deleteRange(from, to);
|
|
@@ -1736,6 +1755,26 @@ function replaceRangeWith(tr, from, to, node) {
|
|
|
1736
1755
|
}
|
|
1737
1756
|
function deleteRange(tr, from, to) {
|
|
1738
1757
|
let $from = tr.doc.resolve(from), $to = tr.doc.resolve(to);
|
|
1758
|
+
// When the deleted range spans from the start of one textblock to
|
|
1759
|
+
// the start of another one, move out of the start of both blocks.
|
|
1760
|
+
if ($from.parent.isTextblock && $to.parent.isTextblock && $from.start() != $to.start() &&
|
|
1761
|
+
$from.parentOffset == 0 && $to.parentOffset == 0) {
|
|
1762
|
+
let shared = $from.sharedDepth(to), isolated = false;
|
|
1763
|
+
for (let d = $from.depth; d > shared; d--)
|
|
1764
|
+
if ($from.node(d).type.spec.isolating)
|
|
1765
|
+
isolated = true;
|
|
1766
|
+
for (let d = $to.depth; d > shared; d--)
|
|
1767
|
+
if ($to.node(d).type.spec.isolating)
|
|
1768
|
+
isolated = true;
|
|
1769
|
+
if (!isolated) {
|
|
1770
|
+
for (let d = $from.depth; d > 0 && from == $from.start(d); d--)
|
|
1771
|
+
from = $from.before(d);
|
|
1772
|
+
for (let d = $to.depth; d > 0 && to == $to.start(d); d--)
|
|
1773
|
+
to = $to.before(d);
|
|
1774
|
+
$from = tr.doc.resolve(from);
|
|
1775
|
+
$to = tr.doc.resolve(to);
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1739
1778
|
let covered = coveredDepths($from, $to);
|
|
1740
1779
|
for (let i = 0; i < covered.length; i++) {
|
|
1741
1780
|
let depth = covered[i], last = i == covered.length - 1;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prosemirror-transform",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.1",
|
|
4
4
|
"description": "ProseMirror document transformations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
],
|
|
22
22
|
"repository": {
|
|
23
23
|
"type": "git",
|
|
24
|
-
"url": "git://
|
|
24
|
+
"url": "git+https://code.haverbeke.berlin/prosemirror/prosemirror-transform.git"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"prosemirror-model": "^1.21.0"
|
package/src/replace.ts
CHANGED
|
@@ -155,10 +155,9 @@ class Fitter {
|
|
|
155
155
|
|
|
156
156
|
openMore() {
|
|
157
157
|
let {content, openStart, openEnd} = this.unplaced
|
|
158
|
-
|
|
159
|
-
if (
|
|
160
|
-
this.unplaced = new Slice(content, openStart + 1,
|
|
161
|
-
Math.max(openEnd, inner.size + openStart >= content.size - openEnd ? openStart + 1 : 0))
|
|
158
|
+
if (maxOpen(content, -1) <= openStart) return false
|
|
159
|
+
if (this.unplaced.size > 1 && maxOpen(content, 1) > openEnd) openEnd++
|
|
160
|
+
this.unplaced = new Slice(content, openStart + 1, openEnd)
|
|
162
161
|
return true
|
|
163
162
|
}
|
|
164
163
|
|
|
@@ -331,6 +330,14 @@ function definesContent(type: NodeType) {
|
|
|
331
330
|
return type.spec.defining || type.spec.definingForContent
|
|
332
331
|
}
|
|
333
332
|
|
|
333
|
+
function maxOpen(frag: Fragment, side: -1 | 1) {
|
|
334
|
+
for (let count = 0;; count++) {
|
|
335
|
+
let ch = side < 0 ? frag.firstChild : frag.lastChild
|
|
336
|
+
if (!ch || ch.isAtom) return count
|
|
337
|
+
frag = ch.content
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
334
341
|
export function replaceRange(tr: Transform, from: number, to: number, slice: Slice) {
|
|
335
342
|
if (!slice.size) return tr.deleteRange(from, to)
|
|
336
343
|
|
|
@@ -425,6 +432,22 @@ export function replaceRangeWith(tr: Transform, from: number, to: number, node:
|
|
|
425
432
|
|
|
426
433
|
export function deleteRange(tr: Transform, from: number, to: number) {
|
|
427
434
|
let $from = tr.doc.resolve(from), $to = tr.doc.resolve(to)
|
|
435
|
+
|
|
436
|
+
// When the deleted range spans from the start of one textblock to
|
|
437
|
+
// the start of another one, move out of the start of both blocks.
|
|
438
|
+
if ($from.parent.isTextblock && $to.parent.isTextblock && $from.start() != $to.start() &&
|
|
439
|
+
$from.parentOffset == 0 && $to.parentOffset == 0) {
|
|
440
|
+
let shared = $from.sharedDepth(to), isolated = false
|
|
441
|
+
for (let d = $from.depth; d > shared; d--) if ($from.node(d).type.spec.isolating) isolated = true
|
|
442
|
+
for (let d = $to.depth; d > shared; d--) if ($to.node(d).type.spec.isolating) isolated = true
|
|
443
|
+
if (!isolated) {
|
|
444
|
+
for (let d = $from.depth; d > 0 && from == $from.start(d); d--) from = $from.before(d)
|
|
445
|
+
for (let d = $to.depth; d > 0 && to == $to.start(d); d--) to = $to.before(d)
|
|
446
|
+
$from = tr.doc.resolve(from)
|
|
447
|
+
$to = tr.doc.resolve(to)
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
428
451
|
let covered = coveredDepths($from, $to)
|
|
429
452
|
for (let i = 0; i < covered.length; i++) {
|
|
430
453
|
let depth = covered[i], last = i == covered.length - 1
|
package/src/replace_step.ts
CHANGED
|
@@ -40,7 +40,8 @@ export class ReplaceStep extends Step {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
map(mapping: Mappable) {
|
|
43
|
-
let
|
|
43
|
+
let to = mapping.mapResult(this.to, -1)
|
|
44
|
+
let from = this.from == this.to && ReplaceStep.MAP_BIAS < 0 ? to : mapping.mapResult(this.from, 1)
|
|
44
45
|
if (from.deletedAcross && to.deletedAcross) return null
|
|
45
46
|
return new ReplaceStep(from.pos, Math.max(from.pos, to.pos), this.slice, this.structure)
|
|
46
47
|
}
|
|
@@ -74,6 +75,14 @@ export class ReplaceStep extends Step {
|
|
|
74
75
|
throw new RangeError("Invalid input for ReplaceStep.fromJSON")
|
|
75
76
|
return new ReplaceStep(json.from, json.to, Slice.fromJSON(schema, json.slice), !!json.structure)
|
|
76
77
|
}
|
|
78
|
+
|
|
79
|
+
/// By default, for backwards compatibility, an inserting step
|
|
80
|
+
/// mapped over an insertion at that same position fill move after
|
|
81
|
+
/// the inserted content. In a collaborative editing situation, that
|
|
82
|
+
/// can make redone insertions appear in unexpected places. You can
|
|
83
|
+
/// set this to -1 to make such mapping keep the step before the
|
|
84
|
+
/// insertion instead.
|
|
85
|
+
static MAP_BIAS: -1 | 1 = 1
|
|
77
86
|
}
|
|
78
87
|
|
|
79
88
|
Step.jsonID("replace", ReplaceStep)
|