prosemirror-transform 1.9.0 → 1.10.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 +14 -0
- package/dist/index.cjs +39 -6
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +46 -6
- package/package.json +1 -1
- package/src/replace.ts +2 -1
- package/src/structure.ts +42 -6
- package/src/transform.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## 1.10.1 (2024-10-10)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix an issue where a `deleteRange` heuristic could produce unexpected deletion shapes.
|
|
6
|
+
|
|
7
|
+
Make `Transform.join` convert between newlines and line break replacement nodes when necessary.
|
|
8
|
+
|
|
9
|
+
## 1.10.0 (2024-08-13)
|
|
10
|
+
|
|
11
|
+
### New features
|
|
12
|
+
|
|
13
|
+
`setBlockType` can now take a function that computes attributes for the new nodes, instead of a static attribute object.
|
|
14
|
+
|
|
1
15
|
## 1.9.0 (2024-05-06)
|
|
2
16
|
|
|
3
17
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -942,7 +942,8 @@ function _setBlockType(tr, from, to, type, attrs) {
|
|
|
942
942
|
if (!type.isTextblock) throw new RangeError("Type given to setBlockType should be a textblock");
|
|
943
943
|
var mapFrom = tr.steps.length;
|
|
944
944
|
tr.doc.nodesBetween(from, to, function (node, pos) {
|
|
945
|
-
|
|
945
|
+
var attrsHere = typeof attrs == "function" ? attrs(node) : attrs;
|
|
946
|
+
if (node.isTextblock && !node.hasMarkup(type, attrsHere) && canChangeType(tr.doc, tr.mapping.slice(mapFrom).map(pos), type)) {
|
|
946
947
|
var convertNewlines = null;
|
|
947
948
|
if (type.schema.linebreakReplacement) {
|
|
948
949
|
var pre = type.whitespace == "pre",
|
|
@@ -954,7 +955,7 @@ function _setBlockType(tr, from, to, type, attrs) {
|
|
|
954
955
|
var mapping = tr.mapping.slice(mapFrom);
|
|
955
956
|
var startM = mapping.map(pos, 1),
|
|
956
957
|
endM = mapping.map(pos + node.nodeSize, 1);
|
|
957
|
-
tr.step(new ReplaceAroundStep(startM, endM, startM + 1, endM - 1, new prosemirrorModel.Slice(prosemirrorModel.Fragment.from(type.create(
|
|
958
|
+
tr.step(new ReplaceAroundStep(startM, endM, startM + 1, endM - 1, new prosemirrorModel.Slice(prosemirrorModel.Fragment.from(type.create(attrsHere, null, node.marks)), 0, 0), 1, true));
|
|
958
959
|
if (convertNewlines === true) replaceNewlines(tr, node, pos, mapFrom);
|
|
959
960
|
return false;
|
|
960
961
|
}
|
|
@@ -1033,8 +1034,21 @@ function canJoin(doc, pos) {
|
|
|
1033
1034
|
index = $pos.index();
|
|
1034
1035
|
return joinable($pos.nodeBefore, $pos.nodeAfter) && $pos.parent.canReplace(index, index + 1);
|
|
1035
1036
|
}
|
|
1037
|
+
function canAppendWithSubstitutedLinebeaks(a, b) {
|
|
1038
|
+
if (!b.content.size) a.type.compatibleContent(b.type);
|
|
1039
|
+
var match = a.contentMatchAt(a.childCount);
|
|
1040
|
+
var linebreakReplacement = a.type.schema.linebreakReplacement;
|
|
1041
|
+
for (var i = 0; i < b.childCount; i++) {
|
|
1042
|
+
var child = b.child(i);
|
|
1043
|
+
var type = child.type == linebreakReplacement ? a.type.schema.nodes.text : child.type;
|
|
1044
|
+
match = match.matchType(type);
|
|
1045
|
+
if (!match) return false;
|
|
1046
|
+
if (!a.type.allowsMarks(child.marks)) return false;
|
|
1047
|
+
}
|
|
1048
|
+
return match.validEnd;
|
|
1049
|
+
}
|
|
1036
1050
|
function joinable(a, b) {
|
|
1037
|
-
return !!(a && b && !a.isLeaf && a
|
|
1051
|
+
return !!(a && b && !a.isLeaf && canAppendWithSubstitutedLinebeaks(a, b));
|
|
1038
1052
|
}
|
|
1039
1053
|
function joinPoint(doc, pos) {
|
|
1040
1054
|
var dir = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : -1;
|
|
@@ -1060,8 +1074,27 @@ function joinPoint(doc, pos) {
|
|
|
1060
1074
|
}
|
|
1061
1075
|
}
|
|
1062
1076
|
function _join(tr, pos, depth) {
|
|
1063
|
-
var
|
|
1064
|
-
tr.
|
|
1077
|
+
var convertNewlines = null;
|
|
1078
|
+
var linebreakReplacement = tr.doc.type.schema.linebreakReplacement;
|
|
1079
|
+
if (linebreakReplacement) {
|
|
1080
|
+
var before = tr.doc.resolve(pos - depth).node().type;
|
|
1081
|
+
var pre = before.whitespace == "pre";
|
|
1082
|
+
var supportLinebreak = !!before.contentMatch.matchType(linebreakReplacement);
|
|
1083
|
+
if (pre && !supportLinebreak) convertNewlines = false;else if (!pre && supportLinebreak) convertNewlines = true;
|
|
1084
|
+
}
|
|
1085
|
+
var mapFrom = tr.steps.length;
|
|
1086
|
+
if (convertNewlines === false) {
|
|
1087
|
+
var $after = tr.doc.resolve(pos + depth);
|
|
1088
|
+
replaceLinebreaks(tr, $after.node(), $after.before(), mapFrom);
|
|
1089
|
+
}
|
|
1090
|
+
var mapping = tr.mapping.slice(mapFrom),
|
|
1091
|
+
start = mapping.map(pos - depth);
|
|
1092
|
+
tr.step(new ReplaceStep(start, mapping.map(pos + depth, -1), prosemirrorModel.Slice.empty, true));
|
|
1093
|
+
if (convertNewlines === true) {
|
|
1094
|
+
var $full = tr.doc.resolve(start);
|
|
1095
|
+
replaceNewlines(tr, $full.node(), $full.before(), tr.steps.length);
|
|
1096
|
+
}
|
|
1097
|
+
return tr;
|
|
1065
1098
|
}
|
|
1066
1099
|
function insertPoint(doc, pos, nodeType) {
|
|
1067
1100
|
var $pos = doc.resolve(pos);
|
|
@@ -1474,7 +1507,7 @@ function _deleteRange(tr, from, to) {
|
|
|
1474
1507
|
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));
|
|
1475
1508
|
}
|
|
1476
1509
|
for (var d = 1; d <= $from.depth && d <= $to.depth; d++) {
|
|
1477
|
-
if (from - $from.start(d) == $from.depth - d && to > $from.end(d) && $to.end(d) - to != $to.depth - d) return tr["delete"]($from.before(d), to);
|
|
1510
|
+
if (from - $from.start(d) == $from.depth - d && to > $from.end(d) && $to.end(d) - to != $to.depth - d && $from.start(d - 1) == $to.start(d - 1) && $from.node(d - 1).canReplace($from.index(d - 1), $to.index(d - 1))) return tr["delete"]($from.before(d), to);
|
|
1478
1511
|
}
|
|
1479
1512
|
tr["delete"](from, to);
|
|
1480
1513
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -406,7 +406,7 @@ declare class Transform {
|
|
|
406
406
|
Set the type of all textblocks (partly) between `from` and `to` to
|
|
407
407
|
the given node type with the given attributes.
|
|
408
408
|
*/
|
|
409
|
-
setBlockType(from: number, to: number | undefined, type: NodeType, attrs?: Attrs | null): this;
|
|
409
|
+
setBlockType(from: number, to: number | undefined, type: NodeType, attrs?: Attrs | null | ((oldNode: Node) => Attrs)): this;
|
|
410
410
|
/**
|
|
411
411
|
Change the type, attributes, and/or marks of the node at `pos`.
|
|
412
412
|
When `type` isn't given, the existing node type is preserved,
|
package/dist/index.d.ts
CHANGED
|
@@ -406,7 +406,7 @@ declare class Transform {
|
|
|
406
406
|
Set the type of all textblocks (partly) between `from` and `to` to
|
|
407
407
|
the given node type with the given attributes.
|
|
408
408
|
*/
|
|
409
|
-
setBlockType(from: number, to: number | undefined, type: NodeType, attrs?: Attrs | null): this;
|
|
409
|
+
setBlockType(from: number, to: number | undefined, type: NodeType, attrs?: Attrs | null | ((oldNode: Node) => Attrs)): this;
|
|
410
410
|
/**
|
|
411
411
|
Change the type, attributes, and/or marks of the node at `pos`.
|
|
412
412
|
When `type` isn't given, the existing node type is preserved,
|
package/dist/index.js
CHANGED
|
@@ -1075,7 +1075,9 @@ function setBlockType(tr, from, to, type, attrs) {
|
|
|
1075
1075
|
throw new RangeError("Type given to setBlockType should be a textblock");
|
|
1076
1076
|
let mapFrom = tr.steps.length;
|
|
1077
1077
|
tr.doc.nodesBetween(from, to, (node, pos) => {
|
|
1078
|
-
|
|
1078
|
+
let attrsHere = typeof attrs == "function" ? attrs(node) : attrs;
|
|
1079
|
+
if (node.isTextblock && !node.hasMarkup(type, attrsHere) &&
|
|
1080
|
+
canChangeType(tr.doc, tr.mapping.slice(mapFrom).map(pos), type)) {
|
|
1079
1081
|
let convertNewlines = null;
|
|
1080
1082
|
if (type.schema.linebreakReplacement) {
|
|
1081
1083
|
let pre = type.whitespace == "pre", supportLinebreak = !!type.contentMatch.matchType(type.schema.linebreakReplacement);
|
|
@@ -1090,7 +1092,7 @@ function setBlockType(tr, from, to, type, attrs) {
|
|
|
1090
1092
|
clearIncompatible(tr, tr.mapping.slice(mapFrom).map(pos, 1), type, undefined, convertNewlines === null);
|
|
1091
1093
|
let mapping = tr.mapping.slice(mapFrom);
|
|
1092
1094
|
let startM = mapping.map(pos, 1), endM = mapping.map(pos + node.nodeSize, 1);
|
|
1093
|
-
tr.step(new ReplaceAroundStep(startM, endM, startM + 1, endM - 1, new Slice(Fragment.from(type.create(
|
|
1095
|
+
tr.step(new ReplaceAroundStep(startM, endM, startM + 1, endM - 1, new Slice(Fragment.from(type.create(attrsHere, null, node.marks)), 0, 0), 1, true));
|
|
1094
1096
|
if (convertNewlines === true)
|
|
1095
1097
|
replaceNewlines(tr, node, pos, mapFrom);
|
|
1096
1098
|
return false;
|
|
@@ -1181,8 +1183,24 @@ function canJoin(doc, pos) {
|
|
|
1181
1183
|
return joinable($pos.nodeBefore, $pos.nodeAfter) &&
|
|
1182
1184
|
$pos.parent.canReplace(index, index + 1);
|
|
1183
1185
|
}
|
|
1186
|
+
function canAppendWithSubstitutedLinebeaks(a, b) {
|
|
1187
|
+
if (!b.content.size)
|
|
1188
|
+
a.type.compatibleContent(b.type);
|
|
1189
|
+
let match = a.contentMatchAt(a.childCount);
|
|
1190
|
+
let { linebreakReplacement } = a.type.schema;
|
|
1191
|
+
for (let i = 0; i < b.childCount; i++) {
|
|
1192
|
+
let child = b.child(i);
|
|
1193
|
+
let type = child.type == linebreakReplacement ? a.type.schema.nodes.text : child.type;
|
|
1194
|
+
match = match.matchType(type);
|
|
1195
|
+
if (!match)
|
|
1196
|
+
return false;
|
|
1197
|
+
if (!a.type.allowsMarks(child.marks))
|
|
1198
|
+
return false;
|
|
1199
|
+
}
|
|
1200
|
+
return match.validEnd;
|
|
1201
|
+
}
|
|
1184
1202
|
function joinable(a, b) {
|
|
1185
|
-
return !!(a && b && !a.isLeaf && a
|
|
1203
|
+
return !!(a && b && !a.isLeaf && canAppendWithSubstitutedLinebeaks(a, b));
|
|
1186
1204
|
}
|
|
1187
1205
|
/**
|
|
1188
1206
|
Find an ancestor of the given position that can be joined to the
|
|
@@ -1215,8 +1233,29 @@ function joinPoint(doc, pos, dir = -1) {
|
|
|
1215
1233
|
}
|
|
1216
1234
|
}
|
|
1217
1235
|
function join(tr, pos, depth) {
|
|
1218
|
-
let
|
|
1219
|
-
tr.
|
|
1236
|
+
let convertNewlines = null;
|
|
1237
|
+
let { linebreakReplacement } = tr.doc.type.schema;
|
|
1238
|
+
if (linebreakReplacement) {
|
|
1239
|
+
let before = tr.doc.resolve(pos - depth).node().type;
|
|
1240
|
+
let pre = before.whitespace == "pre";
|
|
1241
|
+
let supportLinebreak = !!before.contentMatch.matchType(linebreakReplacement);
|
|
1242
|
+
if (pre && !supportLinebreak)
|
|
1243
|
+
convertNewlines = false;
|
|
1244
|
+
else if (!pre && supportLinebreak)
|
|
1245
|
+
convertNewlines = true;
|
|
1246
|
+
}
|
|
1247
|
+
let mapFrom = tr.steps.length;
|
|
1248
|
+
if (convertNewlines === false) {
|
|
1249
|
+
let $after = tr.doc.resolve(pos + depth);
|
|
1250
|
+
replaceLinebreaks(tr, $after.node(), $after.before(), mapFrom);
|
|
1251
|
+
}
|
|
1252
|
+
let mapping = tr.mapping.slice(mapFrom), start = mapping.map(pos - depth);
|
|
1253
|
+
tr.step(new ReplaceStep(start, mapping.map(pos + depth, -1), Slice.empty, true));
|
|
1254
|
+
if (convertNewlines === true) {
|
|
1255
|
+
let $full = tr.doc.resolve(start);
|
|
1256
|
+
replaceNewlines(tr, $full.node(), $full.before(), tr.steps.length);
|
|
1257
|
+
}
|
|
1258
|
+
return tr;
|
|
1220
1259
|
}
|
|
1221
1260
|
/**
|
|
1222
1261
|
Try to find a point where a node of the given type can be inserted
|
|
@@ -1700,7 +1739,8 @@ function deleteRange(tr, from, to) {
|
|
|
1700
1739
|
return tr.delete($from.before(depth), $to.after(depth));
|
|
1701
1740
|
}
|
|
1702
1741
|
for (let d = 1; d <= $from.depth && d <= $to.depth; d++) {
|
|
1703
|
-
if (from - $from.start(d) == $from.depth - d && to > $from.end(d) && $to.end(d) - to != $to.depth - d
|
|
1742
|
+
if (from - $from.start(d) == $from.depth - d && to > $from.end(d) && $to.end(d) - to != $to.depth - d &&
|
|
1743
|
+
$from.start(d - 1) == $to.start(d - 1) && $from.node(d - 1).canReplace($from.index(d - 1), $to.index(d - 1)))
|
|
1704
1744
|
return tr.delete($from.before(d), to);
|
|
1705
1745
|
}
|
|
1706
1746
|
tr.delete(from, to);
|
package/package.json
CHANGED
package/src/replace.ts
CHANGED
|
@@ -434,7 +434,8 @@ export function deleteRange(tr: Transform, from: number, to: number) {
|
|
|
434
434
|
return tr.delete($from.before(depth), $to.after(depth))
|
|
435
435
|
}
|
|
436
436
|
for (let d = 1; d <= $from.depth && d <= $to.depth; d++) {
|
|
437
|
-
if (from - $from.start(d) == $from.depth - d && to > $from.end(d) && $to.end(d) - to != $to.depth - d
|
|
437
|
+
if (from - $from.start(d) == $from.depth - d && to > $from.end(d) && $to.end(d) - to != $to.depth - d &&
|
|
438
|
+
$from.start(d - 1) == $to.start(d - 1) && $from.node(d - 1).canReplace($from.index(d - 1), $to.index(d - 1)))
|
|
438
439
|
return tr.delete($from.before(d), to)
|
|
439
440
|
}
|
|
440
441
|
tr.delete(from, to)
|
package/src/structure.ts
CHANGED
|
@@ -112,11 +112,14 @@ export function wrap(tr: Transform, range: NodeRange, wrappers: readonly {type:
|
|
|
112
112
|
tr.step(new ReplaceAroundStep(start, end, start, end, new Slice(content, 0, 0), wrappers.length, true))
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
export function setBlockType(tr: Transform, from: number, to: number,
|
|
115
|
+
export function setBlockType(tr: Transform, from: number, to: number,
|
|
116
|
+
type: NodeType, attrs: Attrs | null | ((oldNode: Node) => Attrs)) {
|
|
116
117
|
if (!type.isTextblock) throw new RangeError("Type given to setBlockType should be a textblock")
|
|
117
118
|
let mapFrom = tr.steps.length
|
|
118
119
|
tr.doc.nodesBetween(from, to, (node, pos) => {
|
|
119
|
-
|
|
120
|
+
let attrsHere = typeof attrs == "function" ? attrs(node) : attrs
|
|
121
|
+
if (node.isTextblock && !node.hasMarkup(type, attrsHere) &&
|
|
122
|
+
canChangeType(tr.doc, tr.mapping.slice(mapFrom).map(pos), type)) {
|
|
120
123
|
let convertNewlines = null
|
|
121
124
|
if (type.schema.linebreakReplacement) {
|
|
122
125
|
let pre = type.whitespace == "pre", supportLinebreak = !!type.contentMatch.matchType(type.schema.linebreakReplacement)
|
|
@@ -129,7 +132,7 @@ export function setBlockType(tr: Transform, from: number, to: number, type: Node
|
|
|
129
132
|
let mapping = tr.mapping.slice(mapFrom)
|
|
130
133
|
let startM = mapping.map(pos, 1), endM = mapping.map(pos + node.nodeSize, 1)
|
|
131
134
|
tr.step(new ReplaceAroundStep(startM, endM, startM + 1, endM - 1,
|
|
132
|
-
|
|
135
|
+
new Slice(Fragment.from(type.create(attrsHere, null, node.marks)), 0, 0), 1, true))
|
|
133
136
|
if (convertNewlines === true) replaceNewlines(tr, node, pos, mapFrom)
|
|
134
137
|
return false
|
|
135
138
|
}
|
|
@@ -223,8 +226,22 @@ export function canJoin(doc: Node, pos: number): boolean {
|
|
|
223
226
|
$pos.parent.canReplace(index, index + 1)
|
|
224
227
|
}
|
|
225
228
|
|
|
229
|
+
function canAppendWithSubstitutedLinebeaks(a: Node, b: Node) {
|
|
230
|
+
if (!b.content.size) a.type.compatibleContent(b.type)
|
|
231
|
+
let match: ContentMatch | null = a.contentMatchAt(a.childCount)
|
|
232
|
+
let {linebreakReplacement} = a.type.schema
|
|
233
|
+
for (let i = 0; i < b.childCount; i++) {
|
|
234
|
+
let child = b.child(i)
|
|
235
|
+
let type = child.type == linebreakReplacement ? a.type.schema.nodes.text : child.type
|
|
236
|
+
match = match.matchType(type)
|
|
237
|
+
if (!match) return false
|
|
238
|
+
if (!a.type.allowsMarks(child.marks)) return false
|
|
239
|
+
}
|
|
240
|
+
return match.validEnd
|
|
241
|
+
}
|
|
242
|
+
|
|
226
243
|
function joinable(a: Node | null, b: Node | null) {
|
|
227
|
-
return !!(a && b && !a.isLeaf && a
|
|
244
|
+
return !!(a && b && !a.isLeaf && canAppendWithSubstitutedLinebeaks(a, b))
|
|
228
245
|
}
|
|
229
246
|
|
|
230
247
|
/// Find an ancestor of the given position that can be joined to the
|
|
@@ -253,8 +270,27 @@ export function joinPoint(doc: Node, pos: number, dir = -1) {
|
|
|
253
270
|
}
|
|
254
271
|
|
|
255
272
|
export function join(tr: Transform, pos: number, depth: number) {
|
|
256
|
-
let
|
|
257
|
-
tr.
|
|
273
|
+
let convertNewlines = null
|
|
274
|
+
let {linebreakReplacement} = tr.doc.type.schema
|
|
275
|
+
if (linebreakReplacement) {
|
|
276
|
+
let before = tr.doc.resolve(pos - depth).node().type
|
|
277
|
+
let pre = before.whitespace == "pre"
|
|
278
|
+
let supportLinebreak = !!before.contentMatch.matchType(linebreakReplacement)
|
|
279
|
+
if (pre && !supportLinebreak) convertNewlines = false
|
|
280
|
+
else if (!pre && supportLinebreak) convertNewlines = true
|
|
281
|
+
}
|
|
282
|
+
let mapFrom = tr.steps.length
|
|
283
|
+
if (convertNewlines === false) {
|
|
284
|
+
let $after = tr.doc.resolve(pos + depth)
|
|
285
|
+
replaceLinebreaks(tr, $after.node(), $after.before(), mapFrom)
|
|
286
|
+
}
|
|
287
|
+
let mapping = tr.mapping.slice(mapFrom), start = mapping.map(pos - depth)
|
|
288
|
+
tr.step(new ReplaceStep(start, mapping.map(pos + depth, - 1), Slice.empty, true))
|
|
289
|
+
if (convertNewlines === true) {
|
|
290
|
+
let $full = tr.doc.resolve(start)
|
|
291
|
+
replaceNewlines(tr, $full.node(), $full.before(), tr.steps.length)
|
|
292
|
+
}
|
|
293
|
+
return tr
|
|
258
294
|
}
|
|
259
295
|
|
|
260
296
|
/// Try to find a point where a node of the given type can be inserted
|
package/src/transform.ts
CHANGED
|
@@ -166,7 +166,7 @@ export class Transform {
|
|
|
166
166
|
|
|
167
167
|
/// Set the type of all textblocks (partly) between `from` and `to` to
|
|
168
168
|
/// the given node type with the given attributes.
|
|
169
|
-
setBlockType(from: number, to = from, type: NodeType, attrs: Attrs | null = null): this {
|
|
169
|
+
setBlockType(from: number, to = from, type: NodeType, attrs: Attrs | null | ((oldNode: Node) => Attrs) = null): this {
|
|
170
170
|
setBlockType(this, from, to, type, attrs)
|
|
171
171
|
return this
|
|
172
172
|
}
|