prosemirror-transform 1.4.1 → 1.5.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.
@@ -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
- // :: (Node, number, ?number, ?Slice) → ?Step
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, 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
- // :: (number, ?number, ?Slice) → this
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
- constructor($from, $to, slice) {
76
- this.$to = $to
77
- this.$from = $from
78
- this.unplaced = slice
79
-
80
- this.frontier = []
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.content
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.content
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.isLeaf) return false
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
  }
@@ -241,7 +222,7 @@ class Fitter {
241
222
  }
242
223
 
243
224
  mustMoveInline() {
244
- if (!this.$to.parent.isTextblock || this.$to.end() == this.$to.pos) return -1
225
+ if (!this.$to.parent.isTextblock) return -1
245
226
  let top = this.frontier[this.depth], level
246
227
  if (!top.type.isTextblock || !contentAfterFits(this.$to, this.$to.depth, top.type, top.match, false) ||
247
228
  (this.$to.depth == this.depth && (level = this.findCloseLevel(this.$to)) && level.depth == this.depth)) return -1
@@ -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.copy(dropFromFragment(fragment.firstChild.content, depth - 1, count)))
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.copy(addToFragment(fragment.lastChild.content, depth - 1, content)))
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.content
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, openStart - 1, frag.childCount == 1 ? openEnd - 1 : 0))
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).append(frag)
320
- if (openEnd <= 0) frag = frag.append(node.type.contentMatch.matchFragment(frag).fillBefore(Fragment.empty, true))
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
- // :: (number, number, Slice) → this
343
- // Replace a range of the document with a given slice, using `from`,
344
- // `to`, and the slice's [`openStart`](#model.Slice.openStart) property
345
- // as hints, rather than fixed start and end points. This method may
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 this.step(new ReplaceStep(from, to, slice))
328
+ return tr.step(new ReplaceStep(from, to, slice))
364
329
 
365
- let targetDepths = coveredDepths($from, this.doc.resolve(to))
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 this.replace($from.before(targetDepth), expand ? $to.after(targetDepth) : to,
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 = this.steps.length
384
+ let startSteps = tr.steps.length
420
385
  for (let i = targetDepths.length - 1; i >= 0; i--) {
421
- this.replace(from, to, slice)
422
- if (this.steps.length > startSteps) break
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.contentMatchAt(0)
437
- let start = match.fillBefore(fragment).append(fragment)
438
- fragment = start.append(match.matchFragment(start).fillBefore(Fragment.empty, true))
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
- // :: (number, number, Node) → this
444
- // Replace the given range with a node, but use `from` and `to` as
445
- // hints, rather than precise positions. When from and to are the same
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
- return this.replaceRange(from, to, new Slice(Fragment.from(node), 0, 0))
412
+ tr.replaceRange(from, to, new Slice(Fragment.from(node), 0, 0))
457
413
  }
458
414
 
459
- // :: (number, number) → this
460
- // Delete the given range, expanding it to cover fully covered
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 this.delete($from.start(depth), $to.end(depth))
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 this.delete($from.before(depth), $to.after(depth))
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 this.delete($from.before(d), to)
427
+ return tr.delete($from.before(d), to)
475
428
  }
476
- return this.delete(from, to)
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
- // ::- Replace a part of the document with a slice of new content.
6
+ /// Replace a part of the document with a slice of new content.
7
7
  export class ReplaceStep extends Step {
8
- // :: (number, number, Slice, ?bool)
9
- // The given `slice` should fit the 'gap' between `from` and
10
- // `to`—the depths must line up, and the surrounding nodes must be
11
- // able to be joined with the open sides of the slice. When
12
- // `structure` is true, the step will fail if the content between
13
- // from and to is not just a sequence of closing and then opening
14
- // tokens (this is to guard against rebased replace steps
15
- // overwriting something they weren't supposed to).
16
- constructor(from, to, slice, structure) {
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
- // :: number
19
- // The start position of the replaced range.
20
- this.from = from
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
- static fromJSON(schema, json) {
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
- // ::- Replace a part of the document with a slice of content, but
83
- // preserve a range of the replaced content by moving it into the
84
- // slice.
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
- // :: (number, number, number, number, Slice, number, ?bool)
87
- // Create a replace-around step with the given range and gap.
88
- // `insert` should be the point in the slice into which the content
89
- // of the gap should be moved. `structure` has the same meaning as
90
- // it has in the [`ReplaceStep`](#transform.ReplaceStep) class.
91
- constructor(from, to, gapFrom, gapTo, slice, insert, structure) {
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
- // :: number
94
- // The start position of the replaced range.
95
- this.from = from
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
- gapFrom: this.gapFrom, gapTo: this.gapTo, insert: this.insert}
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
- static fromJSON(schema, json) {
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--