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.
package/src/step.ts ADDED
@@ -0,0 +1,97 @@
1
+ import {ReplaceError, Schema, Slice, Node} from "prosemirror-model"
2
+
3
+ import {StepMap, Mappable} from "./map"
4
+
5
+ const stepsByID: {[id: string]: {fromJSON(schema: Schema, json: any): Step}} = Object.create(null)
6
+
7
+ /// A step object represents an atomic change. It generally applies
8
+ /// only to the document it was created for, since the positions
9
+ /// stored in it will only make sense for that document.
10
+ ///
11
+ /// New steps are defined by creating classes that extend `Step`,
12
+ /// overriding the `apply`, `invert`, `map`, `getMap` and `fromJSON`
13
+ /// methods, and registering your class with a unique
14
+ /// JSON-serialization identifier using
15
+ /// [`Step.jsonID`](#transform.Step^jsonID).
16
+ export abstract class Step {
17
+ /// Applies this step to the given document, returning a result
18
+ /// object that either indicates failure, if the step can not be
19
+ /// applied to this document, or indicates success by containing a
20
+ /// transformed document.
21
+ abstract apply(doc: Node): StepResult
22
+
23
+ /// Get the step map that represents the changes made by this step,
24
+ /// and which can be used to transform between positions in the old
25
+ /// and the new document.
26
+ getMap(): StepMap { return StepMap.empty }
27
+
28
+ /// Create an inverted version of this step. Needs the document as it
29
+ /// was before the step as argument.
30
+ abstract invert(doc: Node): Step
31
+
32
+ /// Map this step through a mappable thing, returning either a
33
+ /// version of that step with its positions adjusted, or `null` if
34
+ /// the step was entirely deleted by the mapping.
35
+ abstract map(mapping: Mappable): Step | null
36
+
37
+ /// Try to merge this step with another one, to be applied directly
38
+ /// after it. Returns the merged step when possible, null if the
39
+ /// steps can't be merged.
40
+ merge(other: Step): Step | null { return null }
41
+
42
+ /// Create a JSON-serializeable representation of this step. When
43
+ /// defining this for a custom subclass, make sure the result object
44
+ /// includes the step type's [JSON id](#transform.Step^jsonID) under
45
+ /// the `stepType` property.
46
+ abstract toJSON(): any
47
+
48
+ /// Deserialize a step from its JSON representation. Will call
49
+ /// through to the step class' own implementation of this method.
50
+ static fromJSON(schema: Schema, json: any): Step {
51
+ if (!json || !json.stepType) throw new RangeError("Invalid input for Step.fromJSON")
52
+ let type = stepsByID[json.stepType]
53
+ if (!type) throw new RangeError(`No step type ${json.stepType} defined`)
54
+ return type.fromJSON(schema, json)
55
+ }
56
+
57
+ /// To be able to serialize steps to JSON, each step needs a string
58
+ /// ID to attach to its JSON representation. Use this method to
59
+ /// register an ID for your step classes. Try to pick something
60
+ /// that's unlikely to clash with steps from other modules.
61
+ static jsonID(id: string, stepClass: {fromJSON(schema: Schema, json: any): Step}) {
62
+ if (id in stepsByID) throw new RangeError("Duplicate use of step JSON ID " + id)
63
+ stepsByID[id] = stepClass
64
+ ;(stepClass as any).prototype.jsonID = id
65
+ return stepClass
66
+ }
67
+ }
68
+
69
+ /// The result of [applying](#transform.Step.apply) a step. Contains either a
70
+ /// new document or a failure value.
71
+ export class StepResult {
72
+ /// @internal
73
+ constructor(
74
+ /// The transformed document, if successful.
75
+ readonly doc: Node | null,
76
+ /// The failure message, if unsuccessful.
77
+ readonly failed: string | null
78
+ ) {}
79
+
80
+ /// Create a successful step result.
81
+ static ok(doc: Node) { return new StepResult(doc, null) }
82
+
83
+ /// Create a failed step result.
84
+ static fail(message: string) { return new StepResult(null, message) }
85
+
86
+ /// Call [`Node.replace`](#model.Node.replace) with the given
87
+ /// arguments. Create a successful result if it succeeds, and a
88
+ /// failed one if it throws a `ReplaceError`.
89
+ static fromReplace(doc: Node, from: number, to: number, slice: Slice) {
90
+ try {
91
+ return StepResult.ok(doc.replace(from, to, slice))
92
+ } catch (e) {
93
+ if (e instanceof ReplaceError) return StepResult.fail(e.message)
94
+ throw e
95
+ }
96
+ }
97
+ }
@@ -1,18 +1,17 @@
1
- import {Slice, Fragment} from "prosemirror-model"
1
+ import {Slice, Fragment, NodeRange, NodeType, Node, Mark, Attrs, ContentMatch} from "prosemirror-model"
2
2
 
3
3
  import {Transform} from "./transform"
4
4
  import {ReplaceStep, ReplaceAroundStep} from "./replace_step"
5
5
 
6
- function canCut(node, start, end) {
6
+ function canCut(node: Node, start: number, end: number) {
7
7
  return (start == 0 || node.canReplace(start, node.childCount)) &&
8
8
  (end == node.childCount || node.canReplace(0, end))
9
9
  }
10
10
 
11
- // :: (NodeRange) → ?number
12
- // Try to find a target depth to which the content in the given range
13
- // can be lifted. Will not go across
14
- // [isolating](#model.NodeSpec.isolating) parent nodes.
15
- export function liftTarget(range) {
11
+ /// Try to find a target depth to which the content in the given range
12
+ /// can be lifted. Will not go across
13
+ /// [isolating](#model.NodeSpec.isolating) parent nodes.
14
+ export function liftTarget(range: NodeRange): number | null {
16
15
  let parent = range.parent
17
16
  let content = parent.content.cutByIndex(range.startIndex, range.endIndex)
18
17
  for (let depth = range.depth;; --depth) {
@@ -22,15 +21,10 @@ export function liftTarget(range) {
22
21
  return depth
23
22
  if (depth == 0 || node.type.spec.isolating || !canCut(node, index, endIndex)) break
24
23
  }
24
+ return null
25
25
  }
26
26
 
27
- // :: (NodeRange, number) → this
28
- // Split the content in the given range off from its parent, if there
29
- // is sibling content before or after it, and move it up the tree to
30
- // the depth specified by `target`. You'll probably want to use
31
- // [`liftTarget`](#transform.liftTarget) to compute `target`, to make
32
- // sure the lift is valid.
33
- Transform.prototype.lift = function(range, target) {
27
+ export function lift(tr: Transform, range: NodeRange, target: number) {
34
28
  let {$from, $to, depth} = range
35
29
 
36
30
  let gapStart = $from.before(depth + 1), gapEnd = $to.after(depth + 1)
@@ -55,28 +49,33 @@ Transform.prototype.lift = function(range, target) {
55
49
  end++
56
50
  }
57
51
 
58
- return this.step(new ReplaceAroundStep(start, end, gapStart, gapEnd,
59
- new Slice(before.append(after), openStart, openEnd),
60
- before.size - openStart, true))
52
+ tr.step(new ReplaceAroundStep(start, end, gapStart, gapEnd,
53
+ new Slice(before.append(after), openStart, openEnd),
54
+ before.size - openStart, true))
61
55
  }
62
56
 
63
- // :: (NodeRange, NodeType, ?Object, ?NodeRange) → ?[{type: NodeType, attrs: ?Object}]
64
- // Try to find a valid way to wrap the content in the given range in a
65
- // node of the given type. May introduce extra nodes around and inside
66
- // the wrapper node, if necessary. Returns null if no valid wrapping
67
- // could be found. When `innerRange` is given, that range's content is
68
- // used as the content to fit into the wrapping, instead of the
69
- // content of `range`.
70
- export function findWrapping(range, nodeType, attrs, innerRange = range) {
57
+ /// Try to find a valid way to wrap the content in the given range in a
58
+ /// node of the given type. May introduce extra nodes around and inside
59
+ /// the wrapper node, if necessary. Returns null if no valid wrapping
60
+ /// could be found. When `innerRange` is given, that range's content is
61
+ /// used as the content to fit into the wrapping, instead of the
62
+ /// content of `range`.
63
+ export function findWrapping(
64
+ range: NodeRange,
65
+ nodeType: NodeType,
66
+ attrs: Attrs | null = null,
67
+ innerRange = range
68
+ ): {type: NodeType, attrs: Attrs | null}[] | null {
71
69
  let around = findWrappingOutside(range, nodeType)
72
70
  let inner = around && findWrappingInside(innerRange, nodeType)
73
71
  if (!inner) return null
74
- return around.map(withAttrs).concat({type: nodeType, attrs}).concat(inner.map(withAttrs))
72
+ return (around!.map(withAttrs) as {type: NodeType, attrs: Attrs | null}[])
73
+ .concat({type: nodeType, attrs}).concat(inner.map(withAttrs))
75
74
  }
76
75
 
77
- function withAttrs(type) { return {type, attrs: null} }
76
+ function withAttrs(type: NodeType) { return {type, attrs: null} }
78
77
 
79
- function findWrappingOutside(range, type) {
78
+ function findWrappingOutside(range: NodeRange, type: NodeType) {
80
79
  let {parent, startIndex, endIndex} = range
81
80
  let around = parent.contentMatchAt(startIndex).findWrapping(type)
82
81
  if (!around) return null
@@ -84,24 +83,20 @@ function findWrappingOutside(range, type) {
84
83
  return parent.canReplaceWith(startIndex, endIndex, outer) ? around : null
85
84
  }
86
85
 
87
- function findWrappingInside(range, type) {
86
+ function findWrappingInside(range: NodeRange, type: NodeType) {
88
87
  let {parent, startIndex, endIndex} = range
89
88
  let inner = parent.child(startIndex)
90
89
  let inside = type.contentMatch.findWrapping(inner.type)
91
90
  if (!inside) return null
92
91
  let lastType = inside.length ? inside[inside.length - 1] : type
93
- let innerMatch = lastType.contentMatch
92
+ let innerMatch: ContentMatch | null = lastType.contentMatch
94
93
  for (let i = startIndex; innerMatch && i < endIndex; i++)
95
94
  innerMatch = innerMatch.matchType(parent.child(i).type)
96
95
  if (!innerMatch || !innerMatch.validEnd) return null
97
96
  return inside
98
97
  }
99
98
 
100
- // :: (NodeRange, [{type: NodeType, attrs: ?Object}]) → this
101
- // Wrap the given [range](#model.NodeRange) in the given set of wrappers.
102
- // The wrappers are assumed to be valid in this position, and should
103
- // probably be computed with [`findWrapping`](#transform.findWrapping).
104
- Transform.prototype.wrap = function(range, wrappers) {
99
+ export function wrap(tr: Transform, range: NodeRange, wrappers: readonly {type: NodeType, attrs?: Attrs | null}[]) {
105
100
  let content = Fragment.empty
106
101
  for (let i = wrappers.length - 1; i >= 0; i--) {
107
102
  if (content.size) {
@@ -113,55 +108,51 @@ Transform.prototype.wrap = function(range, wrappers) {
113
108
  }
114
109
 
115
110
  let start = range.start, end = range.end
116
- return this.step(new ReplaceAroundStep(start, end, start, end, new Slice(content, 0, 0), wrappers.length, true))
111
+ tr.step(new ReplaceAroundStep(start, end, start, end, new Slice(content, 0, 0), wrappers.length, true))
117
112
  }
118
113
 
119
- // :: (number, ?number, NodeType, ?Object) → this
120
- // Set the type of all textblocks (partly) between `from` and `to` to
121
- // the given node type with the given attributes.
122
- Transform.prototype.setBlockType = function(from, to = from, type, attrs) {
114
+ export function setBlockType(tr: Transform, from: number, to: number, type: NodeType, attrs: Attrs | null) {
123
115
  if (!type.isTextblock) throw new RangeError("Type given to setBlockType should be a textblock")
124
- let mapFrom = this.steps.length
125
- this.doc.nodesBetween(from, to, (node, pos) => {
126
- if (node.isTextblock && !node.hasMarkup(type, attrs) && canChangeType(this.doc, this.mapping.slice(mapFrom).map(pos), type)) {
116
+ let mapFrom = tr.steps.length
117
+ tr.doc.nodesBetween(from, to, (node, pos) => {
118
+ if (node.isTextblock && !node.hasMarkup(type, attrs) && canChangeType(tr.doc, tr.mapping.slice(mapFrom).map(pos), type)) {
127
119
  // Ensure all markup that isn't allowed in the new node type is cleared
128
- this.clearIncompatible(this.mapping.slice(mapFrom).map(pos, 1), type)
129
- let mapping = this.mapping.slice(mapFrom)
120
+ tr.clearIncompatible(tr.mapping.slice(mapFrom).map(pos, 1), type)
121
+ let mapping = tr.mapping.slice(mapFrom)
130
122
  let startM = mapping.map(pos, 1), endM = mapping.map(pos + node.nodeSize, 1)
131
- this.step(new ReplaceAroundStep(startM, endM, startM + 1, endM - 1,
123
+ tr.step(new ReplaceAroundStep(startM, endM, startM + 1, endM - 1,
132
124
  new Slice(Fragment.from(type.create(attrs, null, node.marks)), 0, 0), 1, true))
133
125
  return false
134
126
  }
135
127
  })
136
- return this
137
128
  }
138
129
 
139
- function canChangeType(doc, pos, type) {
130
+ function canChangeType(doc: Node, pos: number, type: NodeType) {
140
131
  let $pos = doc.resolve(pos), index = $pos.index()
141
132
  return $pos.parent.canReplaceWith(index, index + 1, type)
142
133
  }
143
134
 
144
- // :: (number, ?NodeType, ?Object, ?[Mark]) → this
145
- // Change the type, attributes, and/or marks of the node at `pos`.
146
- // When `type` isn't given, the existing node type is preserved,
147
- Transform.prototype.setNodeMarkup = function(pos, type, attrs, marks) {
148
- let node = this.doc.nodeAt(pos)
135
+ /// Change the type, attributes, and/or marks of the node at `pos`.
136
+ /// When `type` isn't given, the existing node type is preserved,
137
+ export function setNodeMarkup(tr: Transform, pos: number, type: NodeType | undefined | null,
138
+ attrs: Attrs | null, marks: readonly Mark[]) {
139
+ let node = tr.doc.nodeAt(pos)
149
140
  if (!node) throw new RangeError("No node at given position")
150
141
  if (!type) type = node.type
151
142
  let newNode = type.create(attrs, null, marks || node.marks)
152
143
  if (node.isLeaf)
153
- return this.replaceWith(pos, pos + node.nodeSize, newNode)
144
+ return tr.replaceWith(pos, pos + node.nodeSize, newNode)
154
145
 
155
146
  if (!type.validContent(node.content))
156
147
  throw new RangeError("Invalid content for node type " + type.name)
157
148
 
158
- return this.step(new ReplaceAroundStep(pos, pos + node.nodeSize, pos + 1, pos + node.nodeSize - 1,
159
- new Slice(Fragment.from(newNode), 0, 0), 1, true))
149
+ tr.step(new ReplaceAroundStep(pos, pos + node.nodeSize, pos + 1, pos + node.nodeSize - 1,
150
+ new Slice(Fragment.from(newNode), 0, 0), 1, true))
160
151
  }
161
152
 
162
- // :: (Node, number, number, ?[?{type: NodeType, attrs: ?Object}]) → bool
163
- // Check whether splitting at the given position is allowed.
164
- export function canSplit(doc, pos, depth = 1, typesAfter) {
153
+ /// Check whether splitting at the given position is allowed.
154
+ export function canSplit(doc: Node, pos: number, depth = 1,
155
+ typesAfter?: (null | {type: NodeType, attrs?: Attrs | null})[]): boolean {
165
156
  let $pos = doc.resolve(pos), base = $pos.depth - depth
166
157
  let innerType = (typesAfter && typesAfter[typesAfter.length - 1]) || $pos.parent
167
158
  if (base < 0 || $pos.parent.type.spec.isolating ||
@@ -182,40 +173,32 @@ export function canSplit(doc, pos, depth = 1, typesAfter) {
182
173
  return $pos.node(base).canReplaceWith(index, index, baseType ? baseType.type : $pos.node(base + 1).type)
183
174
  }
184
175
 
185
- // :: (number, ?number, ?[?{type: NodeType, attrs: ?Object}]) → this
186
- // Split the node at the given position, and optionally, if `depth` is
187
- // greater than one, any number of nodes above that. By default, the
188
- // parts split off will inherit the node type of the original node.
189
- // This can be changed by passing an array of types and attributes to
190
- // use after the split.
191
- Transform.prototype.split = function(pos, depth = 1, typesAfter) {
192
- let $pos = this.doc.resolve(pos), before = Fragment.empty, after = Fragment.empty
176
+ export function split(tr: Transform, pos: number, depth = 1, typesAfter?: (null | {type: NodeType, attrs?: Attrs | null})[]) {
177
+ let $pos = tr.doc.resolve(pos), before = Fragment.empty, after = Fragment.empty
193
178
  for (let d = $pos.depth, e = $pos.depth - depth, i = depth - 1; d > e; d--, i--) {
194
179
  before = Fragment.from($pos.node(d).copy(before))
195
180
  let typeAfter = typesAfter && typesAfter[i]
196
181
  after = Fragment.from(typeAfter ? typeAfter.type.create(typeAfter.attrs, after) : $pos.node(d).copy(after))
197
182
  }
198
- return this.step(new ReplaceStep(pos, pos, new Slice(before.append(after), depth, depth), true))
183
+ tr.step(new ReplaceStep(pos, pos, new Slice(before.append(after), depth, depth), true))
199
184
  }
200
185
 
201
- // :: (Node, number) → bool
202
- // Test whether the blocks before and after a given position can be
203
- // joined.
204
- export function canJoin(doc, pos) {
186
+ /// Test whether the blocks before and after a given position can be
187
+ /// joined.
188
+ export function canJoin(doc: Node, pos: number): boolean {
205
189
  let $pos = doc.resolve(pos), index = $pos.index()
206
190
  return joinable($pos.nodeBefore, $pos.nodeAfter) &&
207
191
  $pos.parent.canReplace(index, index + 1)
208
192
  }
209
193
 
210
- function joinable(a, b) {
211
- return a && b && !a.isLeaf && a.canAppend(b)
194
+ function joinable(a: Node | null, b: Node | null) {
195
+ return !!(a && b && !a.isLeaf && a.canAppend(b))
212
196
  }
213
197
 
214
- // :: (Node, number, ?number) → ?number
215
- // Find an ancestor of the given position that can be joined to the
216
- // block before (or after if `dir` is positive). Returns the joinable
217
- // point, if any.
218
- export function joinPoint(doc, pos, dir = -1) {
198
+ /// Find an ancestor of the given position that can be joined to the
199
+ /// block before (or after if `dir` is positive). Returns the joinable
200
+ /// point, if any.
201
+ export function joinPoint(doc: Node, pos: number, dir = -1) {
219
202
  let $pos = doc.resolve(pos)
220
203
  for (let d = $pos.depth;; d--) {
221
204
  let before, after, index = $pos.index(d)
@@ -237,20 +220,16 @@ export function joinPoint(doc, pos, dir = -1) {
237
220
  }
238
221
  }
239
222
 
240
- // :: (number, ?number) → this
241
- // Join the blocks around the given position. If depth is 2, their
242
- // last and first siblings are also joined, and so on.
243
- Transform.prototype.join = function(pos, depth = 1) {
223
+ export function join(tr: Transform, pos: number, depth: number) {
244
224
  let step = new ReplaceStep(pos - depth, pos + depth, Slice.empty, true)
245
- return this.step(step)
225
+ tr.step(step)
246
226
  }
247
227
 
248
- // :: (Node, number, NodeType) → ?number
249
- // Try to find a point where a node of the given type can be inserted
250
- // near `pos`, by searching up the node hierarchy when `pos` itself
251
- // isn't a valid place but is at the start or end of a node. Return
252
- // null if no position was found.
253
- export function insertPoint(doc, pos, nodeType) {
228
+ /// Try to find a point where a node of the given type can be inserted
229
+ /// near `pos`, by searching up the node hierarchy when `pos` itself
230
+ /// isn't a valid place but is at the start or end of a node. Return
231
+ /// null if no position was found.
232
+ export function insertPoint(doc: Node, pos: number, nodeType: NodeType): number | null {
254
233
  let $pos = doc.resolve(pos)
255
234
  if ($pos.parent.canReplaceWith($pos.index(), $pos.index(), nodeType)) return pos
256
235
 
@@ -266,27 +245,27 @@ export function insertPoint(doc, pos, nodeType) {
266
245
  if ($pos.node(d).canReplaceWith(index, index, nodeType)) return $pos.after(d + 1)
267
246
  if (index < $pos.node(d).childCount) return null
268
247
  }
248
+ return null
269
249
  }
270
250
 
271
- // :: (Node, number, Slice) → ?number
272
- // Finds a position at or around the given position where the given
273
- // slice can be inserted. Will look at parent nodes' nearest boundary
274
- // and try there, even if the original position wasn't directly at the
275
- // start or end of that node. Returns null when no position was found.
276
- export function dropPoint(doc, pos, slice) {
251
+ /// Finds a position at or around the given position where the given
252
+ /// slice can be inserted. Will look at parent nodes' nearest boundary
253
+ /// and try there, even if the original position wasn't directly at the
254
+ /// start or end of that node. Returns null when no position was found.
255
+ export function dropPoint(doc: Node, pos: number, slice: Slice): number | null {
277
256
  let $pos = doc.resolve(pos)
278
257
  if (!slice.content.size) return pos
279
258
  let content = slice.content
280
- for (let i = 0; i < slice.openStart; i++) content = content.firstChild.content
259
+ for (let i = 0; i < slice.openStart; i++) content = content.firstChild!.content
281
260
  for (let pass = 1; pass <= (slice.openStart == 0 && slice.size ? 2 : 1); pass++) {
282
261
  for (let d = $pos.depth; d >= 0; d--) {
283
262
  let bias = d == $pos.depth ? 0 : $pos.pos <= ($pos.start(d + 1) + $pos.end(d + 1)) / 2 ? -1 : 1
284
263
  let insertPos = $pos.index(d) + (bias > 0 ? 1 : 0)
285
- let parent = $pos.node(d), fits = false
264
+ let parent = $pos.node(d), fits: boolean | null = false
286
265
  if (pass == 1) {
287
266
  fits = parent.canReplace(insertPos, insertPos, content)
288
267
  } else {
289
- let wrapping = parent.contentMatchAt(insertPos).findWrapping(content.firstChild.type)
268
+ let wrapping = parent.contentMatchAt(insertPos).findWrapping(content.firstChild!.type)
290
269
  fits = wrapping && parent.canReplaceWith(insertPos, insertPos, wrapping[0])
291
270
  }
292
271
  if (fits)
@@ -0,0 +1,212 @@
1
+ import {Node, NodeType, Mark, MarkType, ContentMatch, Slice, Fragment, NodeRange, Attrs} from "prosemirror-model"
2
+
3
+ import {Mapping} from "./map"
4
+ import {Step} from "./step"
5
+ import {addMark, removeMark, clearIncompatible} from "./mark"
6
+ import {replaceStep, replaceRange, replaceRangeWith, deleteRange} from "./replace"
7
+ import {lift, wrap, setBlockType, setNodeMarkup, split, join} from "./structure"
8
+
9
+ /// @internal
10
+ export let TransformError = class extends Error {}
11
+
12
+ TransformError = function TransformError(this: any, message: string) {
13
+ let err = Error.call(this, message)
14
+ ;(err as any).__proto__ = TransformError.prototype
15
+ return err
16
+ } as any
17
+
18
+ TransformError.prototype = Object.create(Error.prototype)
19
+ TransformError.prototype.constructor = TransformError
20
+ TransformError.prototype.name = "TransformError"
21
+
22
+ /// Abstraction to build up and track an array of
23
+ /// [steps](#transform.Step) representing a document transformation.
24
+ ///
25
+ /// Most transforming methods return the `Transform` object itself, so
26
+ /// that they can be chained.
27
+ export class Transform {
28
+ /// The steps in this transform.
29
+ readonly steps: Step[] = []
30
+ /// The documents before each of the steps.
31
+ readonly docs: Node[] = []
32
+ /// A mapping with the maps for each of the steps in this transform.
33
+ readonly mapping: Mapping = new Mapping
34
+
35
+ /// Create a transform that starts with the given document.
36
+ constructor(
37
+ /// The current document (the result of applying the steps in the
38
+ /// transform).
39
+ public doc: Node
40
+ ) {}
41
+
42
+ /// The starting document.
43
+ get before() { return this.docs.length ? this.docs[0] : this.doc }
44
+
45
+ /// Apply a new step in this transform, saving the result. Throws an
46
+ /// error when the step fails.
47
+ step(step: Step) {
48
+ let result = this.maybeStep(step)
49
+ if (result.failed) throw new TransformError(result.failed)
50
+ return this
51
+ }
52
+
53
+ /// Try to apply a step in this transformation, ignoring it if it
54
+ /// fails. Returns the step result.
55
+ maybeStep(step: Step) {
56
+ let result = step.apply(this.doc)
57
+ if (!result.failed) this.addStep(step, result.doc!)
58
+ return result
59
+ }
60
+
61
+ /// True when the document has been changed (when there are any
62
+ /// steps).
63
+ get docChanged() {
64
+ return this.steps.length > 0
65
+ }
66
+
67
+ /// @internal
68
+ addStep(step: Step, doc: Node) {
69
+ this.docs.push(this.doc)
70
+ this.steps.push(step)
71
+ this.mapping.appendMap(step.getMap())
72
+ this.doc = doc
73
+ }
74
+
75
+ /// Replace the part of the document between `from` and `to` with the
76
+ /// given `slice`.
77
+ replace(from: number, to = from, slice = Slice.empty): this {
78
+ let step = replaceStep(this.doc, from, to, slice)
79
+ if (step) this.step(step)
80
+ return this
81
+ }
82
+
83
+ /// Replace the given range with the given content, which may be a
84
+ /// fragment, node, or array of nodes.
85
+ replaceWith(from: number, to: number, content: Fragment | Node | readonly Node[]): this {
86
+ return this.replace(from, to, new Slice(Fragment.from(content), 0, 0))
87
+ }
88
+
89
+ /// Delete the content between the given positions.
90
+ delete(from: number, to: number): this {
91
+ return this.replace(from, to, Slice.empty)
92
+ }
93
+
94
+ /// Insert the given content at the given position.
95
+ insert(pos: number, content: Fragment | Node | readonly Node[]): this {
96
+ return this.replaceWith(pos, pos, content)
97
+ }
98
+
99
+ /// Replace a range of the document with a given slice, using
100
+ /// `from`, `to`, and the slice's
101
+ /// [`openStart`](#model.Slice.openStart) property as hints, rather
102
+ /// than fixed start and end points. This method may grow the
103
+ /// replaced area or close open nodes in the slice in order to get a
104
+ /// fit that is more in line with WYSIWYG expectations, by dropping
105
+ /// fully covered parent nodes of the replaced region when they are
106
+ /// marked [non-defining as
107
+ /// context](#model.NodeSpec.definingAsContext), or including an
108
+ /// open parent node from the slice that _is_ marked as [defining
109
+ /// its content](#model.NodeSpec.definingForContent).
110
+ ///
111
+ /// This is the method, for example, to handle paste. The similar
112
+ /// [`replace`](#transform.Transform.replace) method is a more
113
+ /// primitive tool which will _not_ move the start and end of its given
114
+ /// range, and is useful in situations where you need more precise
115
+ /// control over what happens.
116
+ replaceRange(from: number, to: number, slice: Slice): this {
117
+ replaceRange(this, from, to, slice)
118
+ return this
119
+ }
120
+
121
+ /// Replace the given range with a node, but use `from` and `to` as
122
+ /// hints, rather than precise positions. When from and to are the same
123
+ /// and are at the start or end of a parent node in which the given
124
+ /// node doesn't fit, this method may _move_ them out towards a parent
125
+ /// that does allow the given node to be placed. When the given range
126
+ /// completely covers a parent node, this method may completely replace
127
+ /// that parent node.
128
+ replaceRangeWith(from: number, to: number, node: Node): this {
129
+ replaceRangeWith(this, from, to, node)
130
+ return this
131
+ }
132
+
133
+ /// Delete the given range, expanding it to cover fully covered
134
+ /// parent nodes until a valid replace is found.
135
+ deleteRange(from: number, to: number): this {
136
+ deleteRange(this, from, to)
137
+ return this
138
+ }
139
+
140
+ /// Split the content in the given range off from its parent, if there
141
+ /// is sibling content before or after it, and move it up the tree to
142
+ /// the depth specified by `target`. You'll probably want to use
143
+ /// [`liftTarget`](#transform.liftTarget) to compute `target`, to make
144
+ /// sure the lift is valid.
145
+ lift(range: NodeRange, target: number): this {
146
+ lift(this, range, target)
147
+ return this
148
+ }
149
+
150
+ /// Join the blocks around the given position. If depth is 2, their
151
+ /// last and first siblings are also joined, and so on.
152
+ join(pos: number, depth: number = 1): this {
153
+ join(this, pos, depth)
154
+ return this
155
+ }
156
+
157
+ /// Wrap the given [range](#model.NodeRange) in the given set of wrappers.
158
+ /// The wrappers are assumed to be valid in this position, and should
159
+ /// probably be computed with [`findWrapping`](#transform.findWrapping).
160
+ wrap(range: NodeRange, wrappers: readonly {type: NodeType, attrs?: Attrs | null}[]): this {
161
+ wrap(this, range, wrappers)
162
+ return this
163
+ }
164
+
165
+ /// Set the type of all textblocks (partly) between `from` and `to` to
166
+ /// the given node type with the given attributes.
167
+ setBlockType(from: number, to = from, type: NodeType, attrs: Attrs | null = null): this {
168
+ setBlockType(this, from, to, type, attrs)
169
+ return this
170
+ }
171
+
172
+ /// Change the type, attributes, and/or marks of the node at `pos`.
173
+ /// When `type` isn't given, the existing node type is preserved,
174
+ setNodeMarkup(pos: number, type?: NodeType | null, attrs: Attrs | null = null, marks: readonly Mark[] = []): this {
175
+ setNodeMarkup(this, pos, type, attrs, marks)
176
+ return this
177
+ }
178
+
179
+ /// Split the node at the given position, and optionally, if `depth` is
180
+ /// greater than one, any number of nodes above that. By default, the
181
+ /// parts split off will inherit the node type of the original node.
182
+ /// This can be changed by passing an array of types and attributes to
183
+ /// use after the split.
184
+ split(pos: number, depth = 1, typesAfter?: (null | {type: NodeType, attrs?: Attrs | null})[]) {
185
+ split(this, pos, depth, typesAfter)
186
+ return this
187
+ }
188
+
189
+ /// Add the given mark to the inline content between `from` and `to`.
190
+ addMark(from: number, to: number, mark: Mark): this {
191
+ addMark(this, from, to, mark)
192
+ return this
193
+ }
194
+
195
+ /// Remove marks from inline nodes between `from` and `to`. When
196
+ /// `mark` is a single mark, remove precisely that mark. When it is
197
+ /// a mark type, remove all marks of that type. When it is null,
198
+ /// remove all marks of any type.
199
+ removeMark(from: number, to: number, mark?: Mark | MarkType | null) {
200
+ removeMark(this, from, to, mark)
201
+ return this
202
+ }
203
+
204
+ /// Removes all marks and nodes from the content of the node at
205
+ /// `pos` that don't match the given new parent node type. Accepts
206
+ /// an optional starting [content match](#model.ContentMatch) as
207
+ /// third argument.
208
+ clearIncompatible(pos: number, parentType: NodeType, match?: ContentMatch) {
209
+ clearIncompatible(this, pos, parentType, match)
210
+ return this
211
+ }
212
+ }
package/rollup.config.js DELETED
@@ -1,14 +0,0 @@
1
- module.exports = {
2
- input: './src/index.js',
3
- output: [{
4
- file: 'dist/index.js',
5
- format: 'cjs',
6
- sourcemap: true
7
- }, {
8
- file: 'dist/index.es.js',
9
- format: 'es',
10
- sourcemap: true
11
- }],
12
- plugins: [require('@rollup/plugin-buble')()],
13
- external(id) { return id[0] != "." && !require("path").isAbsolute(id) }
14
- }