prosemirror-transform 1.4.0 → 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.
@@ -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,21 +350,19 @@ 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
392
357
  }
393
- // Back up if the node directly above openStart, or the node above
394
- // that separated only by a non-defining textblock node, is defining.
395
- if (preferredDepth > 0 && definesContent(leftNodes[preferredDepth - 1].type) &&
396
- $from.node(preferredTargetIndex).type != leftNodes[preferredDepth - 1].type)
397
- preferredDepth -= 1
398
- else if (preferredDepth >= 2 &&
399
- leftNodes[preferredDepth - 1].isTextblock &&
400
- definesContent(leftNodes[preferredDepth - 2].type) &&
401
- $from.node(preferredTargetIndex).type != leftNodes[preferredDepth - 2].type)
402
- preferredDepth -= 2
358
+
359
+ // Back up preferredDepth to cover defining textblocks directly
360
+ // above it, possibly skipping a non-defining textblock.
361
+ for (let d = preferredDepth - 1; d >= 0; d--) {
362
+ let type = leftNodes[d].type, def = definesContent(type)
363
+ if (def && $from.node(preferredTargetIndex).type != type) preferredDepth = d
364
+ else if (def || !type.isTextblock) break
365
+ }
403
366
 
404
367
  for (let j = slice.openStart; j >= 0; j--) {
405
368
  let openDepth = (j + preferredDepth + 1) % (slice.openStart + 1)
@@ -412,76 +375,63 @@ Transform.prototype.replaceRange = function(from, to, slice) {
412
375
  if (targetDepth < 0) { expand = false; targetDepth = -targetDepth }
413
376
  let parent = $from.node(targetDepth - 1), index = $from.index(targetDepth - 1)
414
377
  if (parent.canReplaceWith(index, index, insert.type, insert.marks))
415
- return this.replace($from.before(targetDepth), expand ? $to.after(targetDepth) : to,
378
+ return tr.replace($from.before(targetDepth), expand ? $to.after(targetDepth) : to,
416
379
  new Slice(closeFragment(slice.content, 0, slice.openStart, openDepth),
417
380
  openDepth, slice.openEnd))
418
381
  }
419
382
  }
420
383
 
421
- let startSteps = this.steps.length
384
+ let startSteps = tr.steps.length
422
385
  for (let i = targetDepths.length - 1; i >= 0; i--) {
423
- this.replace(from, to, slice)
424
- if (this.steps.length > startSteps) break
386
+ tr.replace(from, to, slice)
387
+ if (tr.steps.length > startSteps) break
425
388
  let depth = targetDepths[i]
426
389
  if (depth < 0) continue
427
390
  from = $from.before(depth); to = $to.after(depth)
428
391
  }
429
- return this
430
392
  }
431
393
 
432
- function closeFragment(fragment, depth, oldOpen, newOpen, parent) {
394
+ function closeFragment(fragment: Fragment, depth: number, oldOpen: number, newOpen: number, parent?: Node) {
433
395
  if (depth < oldOpen) {
434
- let first = fragment.firstChild
396
+ let first = fragment.firstChild!
435
397
  fragment = fragment.replaceChild(0, first.copy(closeFragment(first.content, depth + 1, oldOpen, newOpen, first)))
436
398
  }
437
399
  if (depth > newOpen) {
438
- let match = parent.contentMatchAt(0)
439
- let start = match.fillBefore(fragment).append(fragment)
440
- 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)!)
441
403
  }
442
404
  return fragment
443
405
  }
444
406
 
445
- // :: (number, number, Node) → this
446
- // Replace the given range with a node, but use `from` and `to` as
447
- // hints, rather than precise positions. When from and to are the same
448
- // and are at the start or end of a parent node in which the given
449
- // node doesn't fit, this method may _move_ them out towards a parent
450
- // that does allow the given node to be placed. When the given range
451
- // completely covers a parent node, this method may completely replace
452
- // that parent node.
453
- Transform.prototype.replaceRangeWith = function(from, to, node) {
454
- if (!node.isInline && from == to && this.doc.resolve(from).parent.content.size) {
455
- 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)
456
410
  if (point != null) from = to = point
457
411
  }
458
- return this.replaceRange(from, to, new Slice(Fragment.from(node), 0, 0))
412
+ tr.replaceRange(from, to, new Slice(Fragment.from(node), 0, 0))
459
413
  }
460
414
 
461
- // :: (number, number) → this
462
- // Delete the given range, expanding it to cover fully covered
463
- // parent nodes until a valid replace is found.
464
- Transform.prototype.deleteRange = function(from, to) {
465
- 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)
466
417
  let covered = coveredDepths($from, $to)
467
418
  for (let i = 0; i < covered.length; i++) {
468
419
  let depth = covered[i], last = i == covered.length - 1
469
420
  if ((last && depth == 0) || $from.node(depth).type.contentMatch.validEnd)
470
- return this.delete($from.start(depth), $to.end(depth))
421
+ return tr.delete($from.start(depth), $to.end(depth))
471
422
  if (depth > 0 && (last || $from.node(depth - 1).canReplace($from.index(depth - 1), $to.indexAfter(depth - 1))))
472
- return this.delete($from.before(depth), $to.after(depth))
423
+ return tr.delete($from.before(depth), $to.after(depth))
473
424
  }
474
425
  for (let d = 1; d <= $from.depth && d <= $to.depth; d++) {
475
426
  if (from - $from.start(d) == $from.depth - d && to > $from.end(d) && $to.end(d) - to != $to.depth - d)
476
- return this.delete($from.before(d), to)
427
+ return tr.delete($from.before(d), to)
477
428
  }
478
- return this.delete(from, to)
429
+ tr.delete(from, to)
479
430
  }
480
431
 
481
- // : (ResolvedPos, ResolvedPos) → [number]
482
432
  // Returns an array of all depths for which $from - $to spans the
483
433
  // whole content of the nodes at that depth.
484
- function coveredDepths($from, $to) {
434
+ function coveredDepths($from: ResolvedPos, $to: ResolvedPos) {
485
435
  let result = [], minDepth = Math.min($from.depth, $to.depth)
486
436
  for (let d = minDepth; d >= 0; d--) {
487
437
  let start = $from.start(d)