prosemirror-transform 1.4.2 → 1.5.0-beta.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/dist/index.cjs +1781 -0
- package/dist/index.d.ts +690 -0
- package/dist/index.es.js +22 -22
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +1699 -1636
- package/dist/index.js.map +1 -1
- package/package.json +14 -12
- package/src/{index.js → index.ts} +4 -2
- package/src/map.ts +256 -0
- package/src/{mark.js → mark.ts} +22 -35
- package/src/{mark_step.js → mark_step.ts} +40 -39
- package/src/{replace.js → replace.ts} +83 -131
- package/src/{replace_step.js → replace_step.ts} +65 -70
- package/src/step.ts +97 -0
- package/src/{structure.js → structure.ts} +78 -99
- package/src/transform.ts +212 -0
- package/rollup.config.js +0 -14
- package/src/map.js +0 -264
- package/src/step.js +0 -110
- package/src/transform.js +0 -71
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {Fragment, Slice} from "prosemirror-model"
|
|
1
|
+
import {Fragment, Slice, Node, ResolvedPos, NodeType, ContentMatch, Attrs} from "prosemirror-model"
|
|
2
2
|
|
|
3
|
+
import {Step} from "./step"
|
|
3
4
|
import {ReplaceStep, ReplaceAroundStep} from "./replace_step"
|
|
4
5
|
import {Transform} from "./transform"
|
|
5
6
|
import {insertPoint} from "./structure"
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
export function replaceStep(doc, from, to = from, slice = Slice.empty) {
|
|
8
|
+
/// ‘Fit’ a slice into a given position in the document, producing a
|
|
9
|
+
/// [step](#transform.Step) that inserts it. Will return null if
|
|
10
|
+
/// there's no meaningful way to insert the slice here, or inserting it
|
|
11
|
+
/// would be a no-op (an empty slice over an empty range).
|
|
12
|
+
export function replaceStep(doc: Node, from: number, to = from, slice = Slice.empty): Step | null {
|
|
13
13
|
if (from == to && !slice.size) return null
|
|
14
14
|
|
|
15
15
|
let $from = doc.resolve(from), $to = doc.resolve(to)
|
|
@@ -18,39 +18,19 @@ export function replaceStep(doc, from, to = from, slice = Slice.empty) {
|
|
|
18
18
|
return new Fitter($from, $to, slice).fit()
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
// Replace the part of the document between `from` and `to` with the
|
|
23
|
-
// given `slice`.
|
|
24
|
-
Transform.prototype.replace = function(from, to = from, slice = Slice.empty) {
|
|
25
|
-
let step = replaceStep(this.doc, from, to, slice)
|
|
26
|
-
if (step) this.step(step)
|
|
27
|
-
return this
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// :: (number, number, union<Fragment, Node, [Node]>) → this
|
|
31
|
-
// Replace the given range with the given content, which may be a
|
|
32
|
-
// fragment, node, or array of nodes.
|
|
33
|
-
Transform.prototype.replaceWith = function(from, to, content) {
|
|
34
|
-
return this.replace(from, to, new Slice(Fragment.from(content), 0, 0))
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// :: (number, number) → this
|
|
38
|
-
// Delete the content between the given positions.
|
|
39
|
-
Transform.prototype.delete = function(from, to) {
|
|
40
|
-
return this.replace(from, to, Slice.empty)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// :: (number, union<Fragment, Node, [Node]>) → this
|
|
44
|
-
// Insert the given content at the given position.
|
|
45
|
-
Transform.prototype.insert = function(pos, content) {
|
|
46
|
-
return this.replaceWith(pos, pos, content)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function fitsTrivially($from, $to, slice) {
|
|
21
|
+
function fitsTrivially($from: ResolvedPos, $to: ResolvedPos, slice: Slice) {
|
|
50
22
|
return !slice.openStart && !slice.openEnd && $from.start() == $to.start() &&
|
|
51
23
|
$from.parent.canReplace($from.index(), $to.index(), slice.content)
|
|
52
24
|
}
|
|
53
25
|
|
|
26
|
+
interface Fittable {
|
|
27
|
+
sliceDepth: number
|
|
28
|
+
frontierDepth: number
|
|
29
|
+
parent: Node | null
|
|
30
|
+
inject?: Fragment | null
|
|
31
|
+
wrap?: readonly NodeType[]
|
|
32
|
+
}
|
|
33
|
+
|
|
54
34
|
// Algorithm for 'placing' the elements of a slice into a gap:
|
|
55
35
|
//
|
|
56
36
|
// We consider the content of each node that is open to the left to be
|
|
@@ -72,12 +52,14 @@ function fitsTrivially($from, $to, slice) {
|
|
|
72
52
|
// - `placed` is a fragment of placed content. Its open-start value
|
|
73
53
|
// is implicit in `$from`, and its open-end value in `frontier`.
|
|
74
54
|
class Fitter {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
55
|
+
frontier: {type: NodeType, match: ContentMatch}[] = []
|
|
56
|
+
placed: Fragment = Fragment.empty
|
|
57
|
+
|
|
58
|
+
constructor(
|
|
59
|
+
readonly $from: ResolvedPos,
|
|
60
|
+
readonly $to: ResolvedPos,
|
|
61
|
+
public unplaced: Slice
|
|
62
|
+
) {
|
|
81
63
|
for (let i = 0; i <= $from.depth; i++) {
|
|
82
64
|
let node = $from.node(i)
|
|
83
65
|
this.frontier.push({
|
|
@@ -86,7 +68,6 @@ class Fitter {
|
|
|
86
68
|
})
|
|
87
69
|
}
|
|
88
70
|
|
|
89
|
-
this.placed = Fragment.empty
|
|
90
71
|
for (let i = $from.depth; i > 0; i--)
|
|
91
72
|
this.placed = Fragment.from($from.node(i).copy(this.placed))
|
|
92
73
|
}
|
|
@@ -114,7 +95,7 @@ class Fitter {
|
|
|
114
95
|
// If closing to `$to` succeeded, create a step
|
|
115
96
|
let content = this.placed, openStart = $from.depth, openEnd = $to.depth
|
|
116
97
|
while (openStart && openEnd && content.childCount == 1) { // Normalize by dropping open parent nodes
|
|
117
|
-
content = content.firstChild
|
|
98
|
+
content = content.firstChild!.content
|
|
118
99
|
openStart--; openEnd--
|
|
119
100
|
}
|
|
120
101
|
let slice = new Slice(content, openStart, openEnd)
|
|
@@ -122,31 +103,32 @@ class Fitter {
|
|
|
122
103
|
return new ReplaceAroundStep($from.pos, moveInline, this.$to.pos, this.$to.end(), slice, placedSize)
|
|
123
104
|
if (slice.size || $from.pos != this.$to.pos) // Don't generate no-op steps
|
|
124
105
|
return new ReplaceStep($from.pos, $to.pos, slice)
|
|
106
|
+
return null
|
|
125
107
|
}
|
|
126
108
|
|
|
127
109
|
// Find a position on the start spine of `this.unplaced` that has
|
|
128
110
|
// content that can be moved somewhere on the frontier. Returns two
|
|
129
111
|
// depths, one for the slice and one for the frontier.
|
|
130
|
-
findFittable() {
|
|
112
|
+
findFittable(): Fittable | undefined {
|
|
131
113
|
// Only try wrapping nodes (pass 2) after finding a place without
|
|
132
114
|
// wrapping failed.
|
|
133
115
|
for (let pass = 1; pass <= 2; pass++) {
|
|
134
116
|
for (let sliceDepth = this.unplaced.openStart; sliceDepth >= 0; sliceDepth--) {
|
|
135
|
-
let fragment, parent
|
|
117
|
+
let fragment, parent = null
|
|
136
118
|
if (sliceDepth) {
|
|
137
119
|
parent = contentAt(this.unplaced.content, sliceDepth - 1).firstChild
|
|
138
|
-
fragment = parent
|
|
120
|
+
fragment = parent!.content
|
|
139
121
|
} else {
|
|
140
122
|
fragment = this.unplaced.content
|
|
141
123
|
}
|
|
142
124
|
let first = fragment.firstChild
|
|
143
125
|
for (let frontierDepth = this.depth; frontierDepth >= 0; frontierDepth--) {
|
|
144
|
-
let {type, match} = this.frontier[frontierDepth], wrap, inject
|
|
126
|
+
let {type, match} = this.frontier[frontierDepth], wrap, inject: Fragment | null = null
|
|
145
127
|
// In pass 1, if the next node matches, or there is no next
|
|
146
128
|
// node but the parents look compatible, we've found a
|
|
147
129
|
// place.
|
|
148
130
|
if (pass == 1 && (first ? match.matchType(first.type) || (inject = match.fillBefore(Fragment.from(first), false))
|
|
149
|
-
: type.compatibleContent(parent.type)))
|
|
131
|
+
: parent && type.compatibleContent(parent.type)))
|
|
150
132
|
return {sliceDepth, frontierDepth, parent, inject}
|
|
151
133
|
// In pass 2, look for a set of wrapping nodes that make
|
|
152
134
|
// `first` fit here.
|
|
@@ -163,7 +145,7 @@ class Fitter {
|
|
|
163
145
|
openMore() {
|
|
164
146
|
let {content, openStart, openEnd} = this.unplaced
|
|
165
147
|
let inner = contentAt(content, openStart)
|
|
166
|
-
if (!inner.childCount || inner.firstChild
|
|
148
|
+
if (!inner.childCount || inner.firstChild!.isLeaf) return false
|
|
167
149
|
this.unplaced = new Slice(content, openStart + 1,
|
|
168
150
|
Math.max(openEnd, inner.size + openStart >= content.size - openEnd ? openStart + 1 : 0))
|
|
169
151
|
return true
|
|
@@ -181,11 +163,10 @@ class Fitter {
|
|
|
181
163
|
}
|
|
182
164
|
}
|
|
183
165
|
|
|
184
|
-
// : ({sliceDepth: number, frontierDepth: number, parent: ?Node, wrap: ?[NodeType], inject: ?Fragment})
|
|
185
166
|
// Move content from the unplaced slice at `sliceDepth` to the
|
|
186
167
|
// frontier node at `frontierDepth`. Close that frontier node when
|
|
187
168
|
// applicable.
|
|
188
|
-
placeNodes({sliceDepth, frontierDepth, parent, inject, wrap}) {
|
|
169
|
+
placeNodes({sliceDepth, frontierDepth, parent, inject, wrap}: Fittable) {
|
|
189
170
|
while (this.depth > frontierDepth) this.closeFrontierNode()
|
|
190
171
|
if (wrap) for (let i = 0; i < wrap.length; i++) this.openFrontierNode(wrap[i])
|
|
191
172
|
|
|
@@ -195,7 +176,7 @@ class Fitter {
|
|
|
195
176
|
let {match, type} = this.frontier[frontierDepth]
|
|
196
177
|
if (inject) {
|
|
197
178
|
for (let i = 0; i < inject.childCount; i++) add.push(inject.child(i))
|
|
198
|
-
match = match.matchFragment(inject)
|
|
179
|
+
match = match.matchFragment(inject)!
|
|
199
180
|
}
|
|
200
181
|
// Computes the amount of (end) open nodes at the end of the
|
|
201
182
|
// fragment. When 0, the parent is open, but no more. When
|
|
@@ -226,7 +207,7 @@ class Fitter {
|
|
|
226
207
|
|
|
227
208
|
// Add new frontier nodes for any open nodes at the end.
|
|
228
209
|
for (let i = 0, cur = fragment; i < openEndCount; i++) {
|
|
229
|
-
let node = cur.lastChild
|
|
210
|
+
let node = cur.lastChild!
|
|
230
211
|
this.frontier.push({type: node.type, match: node.contentMatchAt(node.childCount)})
|
|
231
212
|
cur = node.content
|
|
232
213
|
}
|
|
@@ -251,7 +232,7 @@ class Fitter {
|
|
|
251
232
|
return after
|
|
252
233
|
}
|
|
253
234
|
|
|
254
|
-
findCloseLevel($to) {
|
|
235
|
+
findCloseLevel($to: ResolvedPos) {
|
|
255
236
|
scan: for (let i = Math.min(this.depth, $to.depth); i >= 0; i--) {
|
|
256
237
|
let {match, type} = this.frontier[i]
|
|
257
238
|
let dropInner = i < $to.depth && $to.end(i + 1) == $to.pos + ($to.depth - (i + 1))
|
|
@@ -266,7 +247,7 @@ class Fitter {
|
|
|
266
247
|
}
|
|
267
248
|
}
|
|
268
249
|
|
|
269
|
-
close($to) {
|
|
250
|
+
close($to: ResolvedPos) {
|
|
270
251
|
let close = this.findCloseLevel($to)
|
|
271
252
|
if (!close) return null
|
|
272
253
|
|
|
@@ -274,95 +255,79 @@ class Fitter {
|
|
|
274
255
|
if (close.fit.childCount) this.placed = addToFragment(this.placed, close.depth, close.fit)
|
|
275
256
|
$to = close.move
|
|
276
257
|
for (let d = close.depth + 1; d <= $to.depth; d++) {
|
|
277
|
-
let node = $to.node(d), add = node.type.contentMatch.fillBefore(node.content, true, $to.index(d))
|
|
258
|
+
let node = $to.node(d), add = node.type.contentMatch.fillBefore(node.content, true, $to.index(d))!
|
|
278
259
|
this.openFrontierNode(node.type, node.attrs, add)
|
|
279
260
|
}
|
|
280
261
|
return $to
|
|
281
262
|
}
|
|
282
263
|
|
|
283
|
-
openFrontierNode(type, attrs, content) {
|
|
264
|
+
openFrontierNode(type: NodeType, attrs: Attrs | null = null, content?: Fragment) {
|
|
284
265
|
let top = this.frontier[this.depth]
|
|
285
|
-
top.match = top.match.matchType(type)
|
|
266
|
+
top.match = top.match.matchType(type)!
|
|
286
267
|
this.placed = addToFragment(this.placed, this.depth, Fragment.from(type.create(attrs, content)))
|
|
287
268
|
this.frontier.push({type, match: type.contentMatch})
|
|
288
269
|
}
|
|
289
270
|
|
|
290
271
|
closeFrontierNode() {
|
|
291
|
-
let open = this.frontier.pop()
|
|
292
|
-
let add = open.match.fillBefore(Fragment.empty, true)
|
|
272
|
+
let open = this.frontier.pop()!
|
|
273
|
+
let add = open.match.fillBefore(Fragment.empty, true)!
|
|
293
274
|
if (add.childCount) this.placed = addToFragment(this.placed, this.frontier.length, add)
|
|
294
275
|
}
|
|
295
276
|
}
|
|
296
277
|
|
|
297
|
-
function dropFromFragment(fragment, depth, count) {
|
|
298
|
-
if (depth == 0) return fragment.cutByIndex(count)
|
|
299
|
-
return fragment.replaceChild(0, fragment.firstChild
|
|
278
|
+
function dropFromFragment(fragment: Fragment, depth: number, count: number): Fragment {
|
|
279
|
+
if (depth == 0) return fragment.cutByIndex(count, fragment.childCount)
|
|
280
|
+
return fragment.replaceChild(0, fragment.firstChild!.copy(dropFromFragment(fragment.firstChild!.content, depth - 1, count)))
|
|
300
281
|
}
|
|
301
282
|
|
|
302
|
-
function addToFragment(fragment, depth, content) {
|
|
283
|
+
function addToFragment(fragment: Fragment, depth: number, content: Fragment): Fragment {
|
|
303
284
|
if (depth == 0) return fragment.append(content)
|
|
304
285
|
return fragment.replaceChild(fragment.childCount - 1,
|
|
305
|
-
fragment.lastChild
|
|
286
|
+
fragment.lastChild!.copy(addToFragment(fragment.lastChild!.content, depth - 1, content)))
|
|
306
287
|
}
|
|
307
288
|
|
|
308
|
-
function contentAt(fragment, depth) {
|
|
309
|
-
for (let i = 0; i < depth; i++) fragment = fragment.firstChild
|
|
289
|
+
function contentAt(fragment: Fragment, depth: number) {
|
|
290
|
+
for (let i = 0; i < depth; i++) fragment = fragment.firstChild!.content
|
|
310
291
|
return fragment
|
|
311
292
|
}
|
|
312
293
|
|
|
313
|
-
function closeNodeStart(node, openStart, openEnd) {
|
|
294
|
+
function closeNodeStart(node: Node, openStart: number, openEnd: number) {
|
|
314
295
|
if (openStart <= 0) return node
|
|
315
296
|
let frag = node.content
|
|
316
297
|
if (openStart > 1)
|
|
317
|
-
frag = frag.replaceChild(0, closeNodeStart(frag.firstChild
|
|
298
|
+
frag = frag.replaceChild(0, closeNodeStart(frag.firstChild!, openStart - 1, frag.childCount == 1 ? openEnd - 1 : 0))
|
|
318
299
|
if (openStart > 0) {
|
|
319
|
-
frag = node.type.contentMatch.fillBefore(frag)
|
|
320
|
-
if (openEnd <= 0) frag = frag.append(node.type.contentMatch.matchFragment(frag)
|
|
300
|
+
frag = node.type.contentMatch.fillBefore(frag)!.append(frag)
|
|
301
|
+
if (openEnd <= 0) frag = frag.append(node.type.contentMatch.matchFragment(frag)!.fillBefore(Fragment.empty, true)!)
|
|
321
302
|
}
|
|
322
303
|
return node.copy(frag)
|
|
323
304
|
}
|
|
324
305
|
|
|
325
|
-
function contentAfterFits($to, depth, type, match, open) {
|
|
306
|
+
function contentAfterFits($to: ResolvedPos, depth: number, type: NodeType, match: ContentMatch, open: boolean) {
|
|
326
307
|
let node = $to.node(depth), index = open ? $to.indexAfter(depth) : $to.index(depth)
|
|
327
308
|
if (index == node.childCount && !type.compatibleContent(node.type)) return null
|
|
328
309
|
let fit = match.fillBefore(node.content, true, index)
|
|
329
310
|
return fit && !invalidMarks(type, node.content, index) ? fit : null
|
|
330
311
|
}
|
|
331
312
|
|
|
332
|
-
function invalidMarks(type, fragment, start) {
|
|
313
|
+
function invalidMarks(type: NodeType, fragment: Fragment, start: number) {
|
|
333
314
|
for (let i = start; i < fragment.childCount; i++)
|
|
334
315
|
if (!type.allowsMarks(fragment.child(i).marks)) return true
|
|
335
316
|
return false
|
|
336
317
|
}
|
|
337
318
|
|
|
338
|
-
function definesContent(type) {
|
|
319
|
+
function definesContent(type: NodeType) {
|
|
339
320
|
return type.spec.defining || type.spec.definingForContent
|
|
340
321
|
}
|
|
341
322
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
// grow the replaced area or close open nodes in the slice in order to
|
|
347
|
-
// get a fit that is more in line with WYSIWYG expectations, by
|
|
348
|
-
// dropping fully covered parent nodes of the replaced region when
|
|
349
|
-
// they are marked [non-defining as context](#model.NodeSpec.definingAsContext), or
|
|
350
|
-
// including an open parent node from the slice that _is_ marked as
|
|
351
|
-
// [defining its content](#model.NodeSpec.definingForContent).
|
|
352
|
-
//
|
|
353
|
-
// This is the method, for example, to handle paste. The similar
|
|
354
|
-
// [`replace`](#transform.Transform.replace) method is a more
|
|
355
|
-
// primitive tool which will _not_ move the start and end of its given
|
|
356
|
-
// range, and is useful in situations where you need more precise
|
|
357
|
-
// control over what happens.
|
|
358
|
-
Transform.prototype.replaceRange = function(from, to, slice) {
|
|
359
|
-
if (!slice.size) return this.deleteRange(from, to)
|
|
360
|
-
|
|
361
|
-
let $from = this.doc.resolve(from), $to = this.doc.resolve(to)
|
|
323
|
+
export function replaceRange(tr: Transform, from: number, to: number, slice: Slice) {
|
|
324
|
+
if (!slice.size) return tr.deleteRange(from, to)
|
|
325
|
+
|
|
326
|
+
let $from = tr.doc.resolve(from), $to = tr.doc.resolve(to)
|
|
362
327
|
if (fitsTrivially($from, $to, slice))
|
|
363
|
-
return
|
|
328
|
+
return tr.step(new ReplaceStep(from, to, slice))
|
|
364
329
|
|
|
365
|
-
let targetDepths = coveredDepths($from,
|
|
330
|
+
let targetDepths = coveredDepths($from, tr.doc.resolve(to))
|
|
366
331
|
// Can't replace the whole document, so remove 0 if it's present
|
|
367
332
|
if (targetDepths[targetDepths.length - 1] == 0) targetDepths.pop()
|
|
368
333
|
// Negative numbers represent not expansion over the whole node at
|
|
@@ -385,7 +350,7 @@ Transform.prototype.replaceRange = function(from, to, slice) {
|
|
|
385
350
|
|
|
386
351
|
let leftNodes = [], preferredDepth = slice.openStart
|
|
387
352
|
for (let content = slice.content, i = 0;; i++) {
|
|
388
|
-
let node = content.firstChild
|
|
353
|
+
let node = content.firstChild!
|
|
389
354
|
leftNodes.push(node)
|
|
390
355
|
if (i == slice.openStart) break
|
|
391
356
|
content = node.content
|
|
@@ -410,76 +375,63 @@ Transform.prototype.replaceRange = function(from, to, slice) {
|
|
|
410
375
|
if (targetDepth < 0) { expand = false; targetDepth = -targetDepth }
|
|
411
376
|
let parent = $from.node(targetDepth - 1), index = $from.index(targetDepth - 1)
|
|
412
377
|
if (parent.canReplaceWith(index, index, insert.type, insert.marks))
|
|
413
|
-
return
|
|
378
|
+
return tr.replace($from.before(targetDepth), expand ? $to.after(targetDepth) : to,
|
|
414
379
|
new Slice(closeFragment(slice.content, 0, slice.openStart, openDepth),
|
|
415
380
|
openDepth, slice.openEnd))
|
|
416
381
|
}
|
|
417
382
|
}
|
|
418
383
|
|
|
419
|
-
let startSteps =
|
|
384
|
+
let startSteps = tr.steps.length
|
|
420
385
|
for (let i = targetDepths.length - 1; i >= 0; i--) {
|
|
421
|
-
|
|
422
|
-
if (
|
|
386
|
+
tr.replace(from, to, slice)
|
|
387
|
+
if (tr.steps.length > startSteps) break
|
|
423
388
|
let depth = targetDepths[i]
|
|
424
389
|
if (depth < 0) continue
|
|
425
390
|
from = $from.before(depth); to = $to.after(depth)
|
|
426
391
|
}
|
|
427
|
-
return this
|
|
428
392
|
}
|
|
429
393
|
|
|
430
|
-
function closeFragment(fragment, depth, oldOpen, newOpen, parent) {
|
|
394
|
+
function closeFragment(fragment: Fragment, depth: number, oldOpen: number, newOpen: number, parent?: Node) {
|
|
431
395
|
if (depth < oldOpen) {
|
|
432
|
-
let first = fragment.firstChild
|
|
396
|
+
let first = fragment.firstChild!
|
|
433
397
|
fragment = fragment.replaceChild(0, first.copy(closeFragment(first.content, depth + 1, oldOpen, newOpen, first)))
|
|
434
398
|
}
|
|
435
399
|
if (depth > newOpen) {
|
|
436
|
-
let match = parent
|
|
437
|
-
let start = match.fillBefore(fragment)
|
|
438
|
-
fragment = start.append(match.matchFragment(start)
|
|
400
|
+
let match = parent!.contentMatchAt(0)!
|
|
401
|
+
let start = match.fillBefore(fragment)!.append(fragment)
|
|
402
|
+
fragment = start.append(match.matchFragment(start)!.fillBefore(Fragment.empty, true)!)
|
|
439
403
|
}
|
|
440
404
|
return fragment
|
|
441
405
|
}
|
|
442
406
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
// and are at the start or end of a parent node in which the given
|
|
447
|
-
// node doesn't fit, this method may _move_ them out towards a parent
|
|
448
|
-
// that does allow the given node to be placed. When the given range
|
|
449
|
-
// completely covers a parent node, this method may completely replace
|
|
450
|
-
// that parent node.
|
|
451
|
-
Transform.prototype.replaceRangeWith = function(from, to, node) {
|
|
452
|
-
if (!node.isInline && from == to && this.doc.resolve(from).parent.content.size) {
|
|
453
|
-
let point = insertPoint(this.doc, from, node.type)
|
|
407
|
+
export function replaceRangeWith(tr: Transform, from: number, to: number, node: Node) {
|
|
408
|
+
if (!node.isInline && from == to && tr.doc.resolve(from).parent.content.size) {
|
|
409
|
+
let point = insertPoint(tr.doc, from, node.type)
|
|
454
410
|
if (point != null) from = to = point
|
|
455
411
|
}
|
|
456
|
-
|
|
412
|
+
tr.replaceRange(from, to, new Slice(Fragment.from(node), 0, 0))
|
|
457
413
|
}
|
|
458
414
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
// parent nodes until a valid replace is found.
|
|
462
|
-
Transform.prototype.deleteRange = function(from, to) {
|
|
463
|
-
let $from = this.doc.resolve(from), $to = this.doc.resolve(to)
|
|
415
|
+
export function deleteRange(tr: Transform, from: number, to: number) {
|
|
416
|
+
let $from = tr.doc.resolve(from), $to = tr.doc.resolve(to)
|
|
464
417
|
let covered = coveredDepths($from, $to)
|
|
465
418
|
for (let i = 0; i < covered.length; i++) {
|
|
466
419
|
let depth = covered[i], last = i == covered.length - 1
|
|
467
420
|
if ((last && depth == 0) || $from.node(depth).type.contentMatch.validEnd)
|
|
468
|
-
return
|
|
421
|
+
return tr.delete($from.start(depth), $to.end(depth))
|
|
469
422
|
if (depth > 0 && (last || $from.node(depth - 1).canReplace($from.index(depth - 1), $to.indexAfter(depth - 1))))
|
|
470
|
-
return
|
|
423
|
+
return tr.delete($from.before(depth), $to.after(depth))
|
|
471
424
|
}
|
|
472
425
|
for (let d = 1; d <= $from.depth && d <= $to.depth; d++) {
|
|
473
426
|
if (from - $from.start(d) == $from.depth - d && to > $from.end(d) && $to.end(d) - to != $to.depth - d)
|
|
474
|
-
return
|
|
427
|
+
return tr.delete($from.before(d), to)
|
|
475
428
|
}
|
|
476
|
-
|
|
429
|
+
tr.delete(from, to)
|
|
477
430
|
}
|
|
478
431
|
|
|
479
|
-
// : (ResolvedPos, ResolvedPos) → [number]
|
|
480
432
|
// Returns an array of all depths for which $from - $to spans the
|
|
481
433
|
// whole content of the nodes at that depth.
|
|
482
|
-
function coveredDepths($from, $to) {
|
|
434
|
+
function coveredDepths($from: ResolvedPos, $to: ResolvedPos) {
|
|
483
435
|
let result = [], minDepth = Math.min($from.depth, $to.depth)
|
|
484
436
|
for (let d = minDepth; d >= 0; d--) {
|
|
485
437
|
let start = $from.start(d)
|
|
@@ -1,33 +1,31 @@
|
|
|
1
|
-
import {Slice} from "prosemirror-model"
|
|
1
|
+
import {Slice, Node, Schema} from "prosemirror-model"
|
|
2
2
|
|
|
3
3
|
import {Step, StepResult} from "./step"
|
|
4
|
-
import {StepMap} from "./map"
|
|
4
|
+
import {StepMap, Mappable} from "./map"
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
/// Replace a part of the document with a slice of new content.
|
|
7
7
|
export class ReplaceStep extends Step {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
/// The given `slice` should fit the 'gap' between `from` and
|
|
9
|
+
/// `to`—the depths must line up, and the surrounding nodes must be
|
|
10
|
+
/// able to be joined with the open sides of the slice. When
|
|
11
|
+
/// `structure` is true, the step will fail if the content between
|
|
12
|
+
/// from and to is not just a sequence of closing and then opening
|
|
13
|
+
/// tokens (this is to guard against rebased replace steps
|
|
14
|
+
/// overwriting something they weren't supposed to).
|
|
15
|
+
constructor(
|
|
16
|
+
/// The start position of the replaced range.
|
|
17
|
+
readonly from: number,
|
|
18
|
+
/// The end position of the replaced range.
|
|
19
|
+
readonly to: number,
|
|
20
|
+
/// The slice to insert.
|
|
21
|
+
readonly slice: Slice,
|
|
22
|
+
/// @internal
|
|
23
|
+
readonly structure = false
|
|
24
|
+
) {
|
|
17
25
|
super()
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
// :: number
|
|
22
|
-
// The end position of the replaced range.
|
|
23
|
-
this.to = to
|
|
24
|
-
// :: Slice
|
|
25
|
-
// The slice to insert.
|
|
26
|
-
this.slice = slice
|
|
27
|
-
this.structure = !!structure
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
apply(doc) {
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
apply(doc: Node) {
|
|
31
29
|
if (this.structure && contentBetween(doc, this.from, this.to))
|
|
32
30
|
return StepResult.fail("Structure replace would overwrite content")
|
|
33
31
|
return StepResult.fromReplace(doc, this.from, this.to, this.slice)
|
|
@@ -37,17 +35,17 @@ export class ReplaceStep extends Step {
|
|
|
37
35
|
return new StepMap([this.from, this.to - this.from, this.slice.size])
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
invert(doc) {
|
|
38
|
+
invert(doc: Node) {
|
|
41
39
|
return new ReplaceStep(this.from, this.from + this.slice.size, doc.slice(this.from, this.to))
|
|
42
40
|
}
|
|
43
41
|
|
|
44
|
-
map(mapping) {
|
|
42
|
+
map(mapping: Mappable) {
|
|
45
43
|
let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1)
|
|
46
44
|
if (from.deleted && to.deleted) return null
|
|
47
45
|
return new ReplaceStep(from.pos, Math.max(from.pos, to.pos), this.slice)
|
|
48
46
|
}
|
|
49
47
|
|
|
50
|
-
merge(other) {
|
|
48
|
+
merge(other: Step) {
|
|
51
49
|
if (!(other instanceof ReplaceStep) || other.structure || this.structure) return null
|
|
52
50
|
|
|
53
51
|
if (this.from + this.slice.size == other.from && !this.slice.openEnd && !other.slice.openStart) {
|
|
@@ -63,14 +61,15 @@ export class ReplaceStep extends Step {
|
|
|
63
61
|
}
|
|
64
62
|
}
|
|
65
63
|
|
|
66
|
-
toJSON() {
|
|
67
|
-
let json = {stepType: "replace", from: this.from, to: this.to}
|
|
64
|
+
toJSON(): any {
|
|
65
|
+
let json: any = {stepType: "replace", from: this.from, to: this.to}
|
|
68
66
|
if (this.slice.size) json.slice = this.slice.toJSON()
|
|
69
67
|
if (this.structure) json.structure = true
|
|
70
68
|
return json
|
|
71
69
|
}
|
|
72
70
|
|
|
73
|
-
|
|
71
|
+
/// @internal
|
|
72
|
+
static fromJSON(schema: Schema, json: any) {
|
|
74
73
|
if (typeof json.from != "number" || typeof json.to != "number")
|
|
75
74
|
throw new RangeError("Invalid input for ReplaceStep.fromJSON")
|
|
76
75
|
return new ReplaceStep(json.from, json.to, Slice.fromJSON(schema, json.slice), !!json.structure)
|
|
@@ -79,40 +78,35 @@ export class ReplaceStep extends Step {
|
|
|
79
78
|
|
|
80
79
|
Step.jsonID("replace", ReplaceStep)
|
|
81
80
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
/// Replace a part of the document with a slice of content, but
|
|
82
|
+
/// preserve a range of the replaced content by moving it into the
|
|
83
|
+
/// slice.
|
|
85
84
|
export class ReplaceAroundStep extends Step {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
85
|
+
/// Create a replace-around step with the given range and gap.
|
|
86
|
+
/// `insert` should be the point in the slice into which the content
|
|
87
|
+
/// of the gap should be moved. `structure` has the same meaning as
|
|
88
|
+
/// it has in the [`ReplaceStep`](#transform.ReplaceStep) class.
|
|
89
|
+
constructor(
|
|
90
|
+
/// The start position of the replaced range.
|
|
91
|
+
readonly from: number,
|
|
92
|
+
/// The end position of the replaced range.
|
|
93
|
+
readonly to: number,
|
|
94
|
+
/// The start of preserved range.
|
|
95
|
+
readonly gapFrom: number,
|
|
96
|
+
/// The end of preserved range.
|
|
97
|
+
readonly gapTo: number,
|
|
98
|
+
/// The slice to insert.
|
|
99
|
+
readonly slice: Slice,
|
|
100
|
+
/// The position in the slice where the preserved range should be
|
|
101
|
+
/// inserted.
|
|
102
|
+
readonly insert: number,
|
|
103
|
+
/// @internal
|
|
104
|
+
readonly structure = false
|
|
105
|
+
) {
|
|
92
106
|
super()
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
// :: number
|
|
97
|
-
// The end position of the replaced range.
|
|
98
|
-
this.to = to
|
|
99
|
-
// :: number
|
|
100
|
-
// The start of preserved range.
|
|
101
|
-
this.gapFrom = gapFrom
|
|
102
|
-
// :: number
|
|
103
|
-
// The end of preserved range.
|
|
104
|
-
this.gapTo = gapTo
|
|
105
|
-
// :: Slice
|
|
106
|
-
// The slice to insert.
|
|
107
|
-
this.slice = slice
|
|
108
|
-
// :: number
|
|
109
|
-
// The position in the slice where the preserved range should be
|
|
110
|
-
// inserted.
|
|
111
|
-
this.insert = insert
|
|
112
|
-
this.structure = !!structure
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
apply(doc) {
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
apply(doc: Node) {
|
|
116
110
|
if (this.structure && (contentBetween(doc, this.from, this.gapFrom) ||
|
|
117
111
|
contentBetween(doc, this.gapTo, this.to)))
|
|
118
112
|
return StepResult.fail("Structure gap-replace would overwrite content")
|
|
@@ -130,7 +124,7 @@ export class ReplaceAroundStep extends Step {
|
|
|
130
124
|
this.gapTo, this.to - this.gapTo, this.slice.size - this.insert])
|
|
131
125
|
}
|
|
132
126
|
|
|
133
|
-
invert(doc) {
|
|
127
|
+
invert(doc: Node) {
|
|
134
128
|
let gap = this.gapTo - this.gapFrom
|
|
135
129
|
return new ReplaceAroundStep(this.from, this.from + this.slice.size + gap,
|
|
136
130
|
this.from + this.insert, this.from + this.insert + gap,
|
|
@@ -138,22 +132,23 @@ export class ReplaceAroundStep extends Step {
|
|
|
138
132
|
this.gapFrom - this.from, this.structure)
|
|
139
133
|
}
|
|
140
134
|
|
|
141
|
-
map(mapping) {
|
|
135
|
+
map(mapping: Mappable) {
|
|
142
136
|
let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1)
|
|
143
137
|
let gapFrom = mapping.map(this.gapFrom, -1), gapTo = mapping.map(this.gapTo, 1)
|
|
144
138
|
if ((from.deleted && to.deleted) || gapFrom < from.pos || gapTo > to.pos) return null
|
|
145
139
|
return new ReplaceAroundStep(from.pos, to.pos, gapFrom, gapTo, this.slice, this.insert, this.structure)
|
|
146
140
|
}
|
|
147
141
|
|
|
148
|
-
toJSON() {
|
|
149
|
-
let json = {stepType: "replaceAround", from: this.from, to: this.to,
|
|
150
|
-
|
|
142
|
+
toJSON(): any {
|
|
143
|
+
let json: any = {stepType: "replaceAround", from: this.from, to: this.to,
|
|
144
|
+
gapFrom: this.gapFrom, gapTo: this.gapTo, insert: this.insert}
|
|
151
145
|
if (this.slice.size) json.slice = this.slice.toJSON()
|
|
152
146
|
if (this.structure) json.structure = true
|
|
153
147
|
return json
|
|
154
148
|
}
|
|
155
149
|
|
|
156
|
-
|
|
150
|
+
/// @internal
|
|
151
|
+
static fromJSON(schema: Schema, json: any) {
|
|
157
152
|
if (typeof json.from != "number" || typeof json.to != "number" ||
|
|
158
153
|
typeof json.gapFrom != "number" || typeof json.gapTo != "number" || typeof json.insert != "number")
|
|
159
154
|
throw new RangeError("Invalid input for ReplaceAroundStep.fromJSON")
|
|
@@ -164,7 +159,7 @@ export class ReplaceAroundStep extends Step {
|
|
|
164
159
|
|
|
165
160
|
Step.jsonID("replaceAround", ReplaceAroundStep)
|
|
166
161
|
|
|
167
|
-
function contentBetween(doc, from, to) {
|
|
162
|
+
function contentBetween(doc: Node, from: number, to: number) {
|
|
168
163
|
let $from = doc.resolve(from), dist = to - from, depth = $from.depth
|
|
169
164
|
while (dist > 0 && depth > 0 && $from.indexAfter(depth) == $from.node(depth).childCount) {
|
|
170
165
|
depth--
|