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.
package/package.json CHANGED
@@ -1,9 +1,16 @@
1
1
  {
2
2
  "name": "prosemirror-transform",
3
- "version": "1.4.0",
3
+ "version": "1.5.0-beta.1",
4
4
  "description": "ProseMirror document transformations",
5
- "main": "dist/index.js",
6
- "module": "dist/index.es.js",
5
+ "type": "module",
6
+ "main": "dist/index.cjs",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ "import": "./dist/index.js",
11
+ "require": "./dist/index.cjs"
12
+ },
13
+ "sideEffects": false,
7
14
  "license": "MIT",
8
15
  "maintainers": [
9
16
  {
@@ -20,16 +27,11 @@
20
27
  "prosemirror-model": "^1.0.0"
21
28
  },
22
29
  "devDependencies": {
23
- "mocha": "^9.1.2",
24
- "ist": "^1.0.0",
25
- "prosemirror-test-builder": "^1.0.0",
26
- "rollup": "^2.26.3",
27
- "@rollup/plugin-buble": "^0.21.3"
30
+ "@prosemirror/buildhelper": "^0.1.5",
31
+ "prosemirror-test-builder": "^1.0.0"
28
32
  },
29
33
  "scripts": {
30
- "test": "mocha test/test-*.js",
31
- "build": "rollup -c",
32
- "watch": "rollup -c -w",
33
- "prepare": "npm run build"
34
+ "test": "pm-runtests",
35
+ "prepare": "pm-buildhelper src/index.ts"
34
36
  }
35
37
  }
@@ -1,7 +1,9 @@
1
- export {Transform, TransformError} from "./transform"
1
+ export {Transform} from "./transform"
2
+ /// @internal
3
+ export {TransformError} from "./transform"
2
4
  export {Step, StepResult} from "./step"
3
5
  export {joinPoint, canJoin, canSplit, insertPoint, dropPoint, liftTarget, findWrapping} from "./structure"
4
- export {StepMap, MapResult, Mapping} from "./map"
6
+ export {StepMap, MapResult, Mapping, Mappable} from "./map"
5
7
  export {AddMarkStep, RemoveMarkStep} from "./mark_step"
6
8
  export {ReplaceStep, ReplaceAroundStep} from "./replace_step"
7
9
  import "./mark"
package/src/map.ts ADDED
@@ -0,0 +1,256 @@
1
+ /// There are several things that positions can be mapped through.
2
+ /// Such objects conform to this interface.
3
+ export interface Mappable {
4
+ /// Map a position through this object. When given, `assoc` (should
5
+ /// be -1 or 1, defaults to 1) determines with which side the
6
+ /// position is associated, which determines in which direction to
7
+ /// move when a chunk of content is inserted at the mapped position.
8
+ map: (pos: number, assoc?: number) => number
9
+
10
+ /// Map a position, and return an object containing additional
11
+ /// information about the mapping. The result's `deleted` field tells
12
+ /// you whether the position was deleted (completely enclosed in a
13
+ /// replaced range) during the mapping. When content on only one side
14
+ /// is deleted, the position itself is only considered deleted when
15
+ /// `assoc` points in the direction of the deleted content.
16
+ mapResult: (pos: number, assoc?: number) => MapResult
17
+ }
18
+
19
+ // Recovery values encode a range index and an offset. They are
20
+ // represented as numbers, because tons of them will be created when
21
+ // mapping, for example, a large number of decorations. The number's
22
+ // lower 16 bits provide the index, the remaining bits the offset.
23
+ //
24
+ // Note: We intentionally don't use bit shift operators to en- and
25
+ // decode these, since those clip to 32 bits, which we might in rare
26
+ // cases want to overflow. A 64-bit float can represent 48-bit
27
+ // integers precisely.
28
+
29
+ const lower16 = 0xffff
30
+ const factor16 = Math.pow(2, 16)
31
+
32
+ function makeRecover(index: number, offset: number) { return index + offset * factor16 }
33
+ function recoverIndex(value: number) { return value & lower16 }
34
+ function recoverOffset(value: number) { return (value - (value & lower16)) / factor16 }
35
+
36
+ /// An object representing a mapped position with extra
37
+ /// information.
38
+ export class MapResult {
39
+ /// @internal
40
+ constructor(
41
+ /// The mapped version of the position.
42
+ readonly pos: number,
43
+ /// Tells you whether the position was deleted, that is, whether
44
+ /// the step removed its surroundings from the document.
45
+ readonly deleted: boolean = false,
46
+ /// @internal
47
+ readonly recover: number | null = null
48
+ ) {}
49
+ }
50
+
51
+ /// A map describing the deletions and insertions made by a step, which
52
+ /// can be used to find the correspondence between positions in the
53
+ /// pre-step version of a document and the same position in the
54
+ /// post-step version.
55
+ export class StepMap implements Mappable {
56
+ /// Create a position map. The modifications to the document are
57
+ /// represented as an array of numbers, in which each group of three
58
+ /// represents a modified chunk as `[start, oldSize, newSize]`.
59
+ constructor(
60
+ /// @internal
61
+ readonly ranges: readonly number[],
62
+ /// @internal
63
+ readonly inverted = false
64
+ ) {
65
+ if (!ranges.length && StepMap.empty) return StepMap.empty
66
+ }
67
+
68
+ /// @internal
69
+ recover(value: number) {
70
+ let diff = 0, index = recoverIndex(value)
71
+ if (!this.inverted) for (let i = 0; i < index; i++)
72
+ diff += this.ranges[i * 3 + 2] - this.ranges[i * 3 + 1]
73
+ return this.ranges[index * 3] + diff + recoverOffset(value)
74
+ }
75
+
76
+ mapResult(pos: number, assoc = 1): MapResult { return this._map(pos, assoc, false) as MapResult }
77
+
78
+ map(pos: number, assoc = 1): number { return this._map(pos, assoc, true) as number }
79
+
80
+ /// @internal
81
+ _map(pos: number, assoc: number, simple: boolean) {
82
+ let diff = 0, oldIndex = this.inverted ? 2 : 1, newIndex = this.inverted ? 1 : 2
83
+ for (let i = 0; i < this.ranges.length; i += 3) {
84
+ let start = this.ranges[i] - (this.inverted ? diff : 0)
85
+ if (start > pos) break
86
+ let oldSize = this.ranges[i + oldIndex], newSize = this.ranges[i + newIndex], end = start + oldSize
87
+ if (pos <= end) {
88
+ let side = !oldSize ? assoc : pos == start ? -1 : pos == end ? 1 : assoc
89
+ let result = start + diff + (side < 0 ? 0 : newSize)
90
+ if (simple) return result
91
+ let recover = pos == (assoc < 0 ? start : end) ? null : makeRecover(i / 3, pos - start)
92
+ return new MapResult(result, assoc < 0 ? pos != start : pos != end, recover)
93
+ }
94
+ diff += newSize - oldSize
95
+ }
96
+ return simple ? pos + diff : new MapResult(pos + diff)
97
+ }
98
+
99
+ /// @internal
100
+ touches(pos: number, recover: number) {
101
+ let diff = 0, index = recoverIndex(recover)
102
+ let oldIndex = this.inverted ? 2 : 1, newIndex = this.inverted ? 1 : 2
103
+ for (let i = 0; i < this.ranges.length; i += 3) {
104
+ let start = this.ranges[i] - (this.inverted ? diff : 0)
105
+ if (start > pos) break
106
+ let oldSize = this.ranges[i + oldIndex], end = start + oldSize
107
+ if (pos <= end && i == index * 3) return true
108
+ diff += this.ranges[i + newIndex] - oldSize
109
+ }
110
+ return false
111
+ }
112
+
113
+ /// Calls the given function on each of the changed ranges included in
114
+ /// this map.
115
+ forEach(f: (oldStart: number, oldEnd: number, newStart: number, newEnd: number) => void) {
116
+ let oldIndex = this.inverted ? 2 : 1, newIndex = this.inverted ? 1 : 2
117
+ for (let i = 0, diff = 0; i < this.ranges.length; i += 3) {
118
+ let start = this.ranges[i], oldStart = start - (this.inverted ? diff : 0), newStart = start + (this.inverted ? 0 : diff)
119
+ let oldSize = this.ranges[i + oldIndex], newSize = this.ranges[i + newIndex]
120
+ f(oldStart, oldStart + oldSize, newStart, newStart + newSize)
121
+ diff += newSize - oldSize
122
+ }
123
+ }
124
+
125
+ /// Create an inverted version of this map. The result can be used to
126
+ /// map positions in the post-step document to the pre-step document.
127
+ invert() {
128
+ return new StepMap(this.ranges, !this.inverted)
129
+ }
130
+
131
+ /// @internal
132
+ toString() {
133
+ return (this.inverted ? "-" : "") + JSON.stringify(this.ranges)
134
+ }
135
+
136
+ /// Create a map that moves all positions by offset `n` (which may be
137
+ /// negative). This can be useful when applying steps meant for a
138
+ /// sub-document to a larger document, or vice-versa.
139
+ static offset(n: number) {
140
+ return n == 0 ? StepMap.empty : new StepMap(n < 0 ? [0, -n, 0] : [0, 0, n])
141
+ }
142
+
143
+ /// A StepMap that contains no changed ranges.
144
+ static empty = new StepMap([])
145
+ }
146
+
147
+ /// A mapping represents a pipeline of zero or more [step
148
+ /// maps](#transform.StepMap). It has special provisions for losslessly
149
+ /// handling mapping positions through a series of steps in which some
150
+ /// steps are inverted versions of earlier steps. (This comes up when
151
+ /// ‘[rebasing](/docs/guide/#transform.rebasing)’ steps for
152
+ /// collaboration or history management.)
153
+ export class Mapping implements Mappable {
154
+ /// Create a new mapping with the given position maps.
155
+ constructor(
156
+ /// The step maps in this mapping.
157
+ readonly maps: StepMap[] = [],
158
+ /// @internal
159
+ public mirror?: number[],
160
+ /// The starting position in the `maps` array, used when `map` or
161
+ /// `mapResult` is called.
162
+ public from = 0,
163
+ /// The end position in the `maps` array.
164
+ public to = maps.length
165
+ ) {}
166
+
167
+ /// Create a mapping that maps only through a part of this one.
168
+ slice(from = 0, to = this.maps.length) {
169
+ return new Mapping(this.maps, this.mirror, from, to)
170
+ }
171
+
172
+ /// @internal
173
+ copy() {
174
+ return new Mapping(this.maps.slice(), this.mirror && this.mirror.slice(), this.from, this.to)
175
+ }
176
+
177
+ /// Add a step map to the end of this mapping. If `mirrors` is
178
+ /// given, it should be the index of the step map that is the mirror
179
+ /// image of this one.
180
+ appendMap(map: StepMap, mirrors?: number) {
181
+ this.to = this.maps.push(map)
182
+ if (mirrors != null) this.setMirror(this.maps.length - 1, mirrors)
183
+ }
184
+
185
+ /// Add all the step maps in a given mapping to this one (preserving
186
+ /// mirroring information).
187
+ appendMapping(mapping: Mapping) {
188
+ for (let i = 0, startSize = this.maps.length; i < mapping.maps.length; i++) {
189
+ let mirr = mapping.getMirror(i)
190
+ this.appendMap(mapping.maps[i], mirr != null && mirr < i ? startSize + mirr : undefined)
191
+ }
192
+ }
193
+
194
+ /// Finds the offset of the step map that mirrors the map at the
195
+ /// given offset, in this mapping (as per the second argument to
196
+ /// `appendMap`).
197
+ getMirror(n: number): number | undefined {
198
+ if (this.mirror) for (let i = 0; i < this.mirror.length; i++)
199
+ if (this.mirror[i] == n) return this.mirror[i + (i % 2 ? -1 : 1)]
200
+ }
201
+
202
+ /// @internal
203
+ setMirror(n: number, m: number) {
204
+ if (!this.mirror) this.mirror = []
205
+ this.mirror.push(n, m)
206
+ }
207
+
208
+ /// Append the inverse of the given mapping to this one.
209
+ appendMappingInverted(mapping: Mapping) {
210
+ for (let i = mapping.maps.length - 1, totalSize = this.maps.length + mapping.maps.length; i >= 0; i--) {
211
+ let mirr = mapping.getMirror(i)
212
+ this.appendMap(mapping.maps[i].invert(), mirr != null && mirr > i ? totalSize - mirr - 1 : undefined)
213
+ }
214
+ }
215
+
216
+ /// Create an inverted version of this mapping.
217
+ invert() {
218
+ let inverse = new Mapping
219
+ inverse.appendMappingInverted(this)
220
+ return inverse
221
+ }
222
+
223
+ /// Map a position through this mapping.
224
+ map(pos: number, assoc = 1) {
225
+ if (this.mirror) return this._map(pos, assoc, true) as number
226
+ for (let i = this.from; i < this.to; i++)
227
+ pos = this.maps[i].map(pos, assoc)
228
+ return pos
229
+ }
230
+
231
+ /// Map a position through this mapping, returning a mapping
232
+ /// result.
233
+ mapResult(pos: number, assoc = 1) { return this._map(pos, assoc, false) as MapResult }
234
+
235
+ /// @internal
236
+ _map(pos: number, assoc: number, simple: boolean) {
237
+ let deleted = false
238
+
239
+ for (let i = this.from; i < this.to; i++) {
240
+ let map = this.maps[i], result = map.mapResult(pos, assoc)
241
+ if (result.recover != null) {
242
+ let corr = this.getMirror(i)
243
+ if (corr != null && corr > i && corr < this.to) {
244
+ i = corr
245
+ pos = this.maps[corr].recover(result.recover)
246
+ continue
247
+ }
248
+ }
249
+
250
+ if (result.deleted) deleted = true
251
+ pos = result.pos
252
+ }
253
+
254
+ return simple ? pos : new MapResult(pos, deleted)
255
+ }
256
+ }
@@ -1,49 +1,43 @@
1
- import {MarkType, Slice, Fragment} from "prosemirror-model"
1
+ import {Mark, MarkType, Slice, Fragment, NodeType} from "prosemirror-model"
2
2
 
3
+ import {Step} from "./step"
3
4
  import {Transform} from "./transform"
4
5
  import {AddMarkStep, RemoveMarkStep} from "./mark_step"
5
6
  import {ReplaceStep} from "./replace_step"
6
7
 
7
- // :: (number, number, Mark) → this
8
- // Add the given mark to the inline content between `from` and `to`.
9
- Transform.prototype.addMark = function(from, to, mark) {
10
- let removed = [], added = [], removing = null, adding = null
11
- this.doc.nodesBetween(from, to, (node, pos, parent) => {
8
+ export function addMark(tr: Transform, from: number, to: number, mark: Mark) {
9
+ let removed: Step[] = [], added: Step[] = []
10
+ let removing: RemoveMarkStep | undefined, adding: AddMarkStep | undefined
11
+ tr.doc.nodesBetween(from, to, (node, pos, parent) => {
12
12
  if (!node.isInline) return
13
13
  let marks = node.marks
14
- if (!mark.isInSet(marks) && parent.type.allowsMarkType(mark.type)) {
14
+ if (!mark.isInSet(marks) && parent!.type.allowsMarkType(mark.type)) {
15
15
  let start = Math.max(pos, from), end = Math.min(pos + node.nodeSize, to)
16
16
  let newSet = mark.addToSet(marks)
17
17
 
18
18
  for (let i = 0; i < marks.length; i++) {
19
19
  if (!marks[i].isInSet(newSet)) {
20
20
  if (removing && removing.to == start && removing.mark.eq(marks[i]))
21
- removing.to = end
21
+ (removing as any).to = end
22
22
  else
23
23
  removed.push(removing = new RemoveMarkStep(start, end, marks[i]))
24
24
  }
25
25
  }
26
26
 
27
27
  if (adding && adding.to == start)
28
- adding.to = end
28
+ (adding as any).to = end
29
29
  else
30
30
  added.push(adding = new AddMarkStep(start, end, mark))
31
31
  }
32
32
  })
33
33
 
34
- removed.forEach(s => this.step(s))
35
- added.forEach(s => this.step(s))
36
- return this
34
+ removed.forEach(s => tr.step(s))
35
+ added.forEach(s => tr.step(s))
37
36
  }
38
37
 
39
- // :: (number, number, ?union<Mark, MarkType>) → this
40
- // Remove marks from inline nodes between `from` and `to`. When `mark`
41
- // is a single mark, remove precisely that mark. When it is a mark type,
42
- // remove all marks of that type. When it is null, remove all marks of
43
- // any type.
44
- Transform.prototype.removeMark = function(from, to, mark = null) {
45
- let matched = [], step = 0
46
- this.doc.nodesBetween(from, to, (node, pos) => {
38
+ export function removeMark(tr: Transform, from: number, to: number, mark?: Mark | MarkType | null) {
39
+ let matched: {style: Mark, from: number, to: number, step: number}[] = [], step = 0
40
+ tr.doc.nodesBetween(from, to, (node, pos) => {
47
41
  if (!node.isInline) return
48
42
  step++
49
43
  let toRemove = null
@@ -75,34 +69,27 @@ Transform.prototype.removeMark = function(from, to, mark = null) {
75
69
  }
76
70
  }
77
71
  })
78
- matched.forEach(m => this.step(new RemoveMarkStep(m.from, m.to, m.style)))
79
- return this
72
+ matched.forEach(m => tr.step(new RemoveMarkStep(m.from, m.to, m.style)))
80
73
  }
81
74
 
82
- // :: (number, NodeType, ?ContentMatch) → this
83
- // Removes all marks and nodes from the content of the node at `pos`
84
- // that don't match the given new parent node type. Accepts an
85
- // optional starting [content match](#model.ContentMatch) as third
86
- // argument.
87
- Transform.prototype.clearIncompatible = function(pos, parentType, match = parentType.contentMatch) {
88
- let node = this.doc.nodeAt(pos)
89
- let delSteps = [], cur = pos + 1
75
+ export function clearIncompatible(tr: Transform, pos: number, parentType: NodeType, match = parentType.contentMatch) {
76
+ let node = tr.doc.nodeAt(pos)!
77
+ let delSteps: Step[] = [], cur = pos + 1
90
78
  for (let i = 0; i < node.childCount; i++) {
91
79
  let child = node.child(i), end = cur + child.nodeSize
92
- let allowed = match.matchType(child.type, child.attrs)
80
+ let allowed = match.matchType(child.type)
93
81
  if (!allowed) {
94
82
  delSteps.push(new ReplaceStep(cur, end, Slice.empty))
95
83
  } else {
96
84
  match = allowed
97
85
  for (let j = 0; j < child.marks.length; j++) if (!parentType.allowsMarkType(child.marks[j].type))
98
- this.step(new RemoveMarkStep(cur, end, child.marks[j]))
86
+ tr.step(new RemoveMarkStep(cur, end, child.marks[j]))
99
87
  }
100
88
  cur = end
101
89
  }
102
90
  if (!match.validEnd) {
103
91
  let fill = match.fillBefore(Fragment.empty, true)
104
- this.replace(cur, cur, new Slice(fill, 0, 0))
92
+ tr.replace(cur, cur, new Slice(fill!, 0, 0))
105
93
  }
106
- for (let i = delSteps.length - 1; i >= 0; i--) this.step(delSteps[i])
107
- return this
94
+ for (let i = delSteps.length - 1; i >= 0; i--) tr.step(delSteps[i])
108
95
  }
@@ -1,7 +1,8 @@
1
- import {Fragment, Slice} from "prosemirror-model"
1
+ import {Fragment, Slice, Node, Mark, Schema} from "prosemirror-model"
2
2
  import {Step, StepResult} from "./step"
3
+ import {Mappable} from "./map"
3
4
 
4
- function mapFragment(fragment, f, parent) {
5
+ function mapFragment(fragment: Fragment, f: (child: Node, parent: Node, i: number) => Node, parent: Node): Fragment {
5
6
  let mapped = []
6
7
  for (let i = 0; i < fragment.childCount; i++) {
7
8
  let child = fragment.child(i)
@@ -12,23 +13,21 @@ function mapFragment(fragment, f, parent) {
12
13
  return Fragment.fromArray(mapped)
13
14
  }
14
15
 
15
- // ::- Add a mark to all inline content between two positions.
16
+ /// Add a mark to all inline content between two positions.
16
17
  export class AddMarkStep extends Step {
17
- // :: (number, number, Mark)
18
- constructor(from, to, mark) {
18
+ /// Create a mark step.
19
+ constructor(
20
+ /// The start of the marked range.
21
+ readonly from: number,
22
+ /// The end of the marked range.
23
+ readonly to: number,
24
+ /// The mark to add.
25
+ readonly mark: Mark
26
+ ) {
19
27
  super()
20
- // :: number
21
- // The start of the marked range.
22
- this.from = from
23
- // :: number
24
- // The end of the marked range.
25
- this.to = to
26
- // :: Mark
27
- // The mark to add.
28
- this.mark = mark
29
28
  }
30
29
 
31
- apply(doc) {
30
+ apply(doc: Node) {
32
31
  let oldSlice = doc.slice(this.from, this.to), $from = doc.resolve(this.from)
33
32
  let parent = $from.node($from.sharedDepth(this.to))
34
33
  let slice = new Slice(mapFragment(oldSlice.content, (node, parent) => {
@@ -38,30 +37,32 @@ export class AddMarkStep extends Step {
38
37
  return StepResult.fromReplace(doc, this.from, this.to, slice)
39
38
  }
40
39
 
41
- invert() {
40
+ invert(): Step {
42
41
  return new RemoveMarkStep(this.from, this.to, this.mark)
43
42
  }
44
43
 
45
- map(mapping) {
44
+ map(mapping: Mappable): Step | null {
46
45
  let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1)
47
46
  if (from.deleted && to.deleted || from.pos >= to.pos) return null
48
47
  return new AddMarkStep(from.pos, to.pos, this.mark)
49
48
  }
50
49
 
51
- merge(other) {
50
+ merge(other: Step): Step | null {
52
51
  if (other instanceof AddMarkStep &&
53
52
  other.mark.eq(this.mark) &&
54
53
  this.from <= other.to && this.to >= other.from)
55
54
  return new AddMarkStep(Math.min(this.from, other.from),
56
55
  Math.max(this.to, other.to), this.mark)
56
+ return null
57
57
  }
58
58
 
59
- toJSON() {
59
+ toJSON(): any {
60
60
  return {stepType: "addMark", mark: this.mark.toJSON(),
61
61
  from: this.from, to: this.to}
62
62
  }
63
63
 
64
- static fromJSON(schema, json) {
64
+ /// @internal
65
+ static fromJSON(schema: Schema, json: any) {
65
66
  if (typeof json.from != "number" || typeof json.to != "number")
66
67
  throw new RangeError("Invalid input for AddMarkStep.fromJSON")
67
68
  return new AddMarkStep(json.from, json.to, schema.markFromJSON(json.mark))
@@ -70,54 +71,54 @@ export class AddMarkStep extends Step {
70
71
 
71
72
  Step.jsonID("addMark", AddMarkStep)
72
73
 
73
- // ::- Remove a mark from all inline content between two positions.
74
+ /// Remove a mark from all inline content between two positions.
74
75
  export class RemoveMarkStep extends Step {
75
- // :: (number, number, Mark)
76
- constructor(from, to, mark) {
76
+ /// Create a mark-removing step.
77
+ constructor(
78
+ /// The start of the unmarked range.
79
+ readonly from: number,
80
+ /// The end of the unmarked range.
81
+ readonly to: number,
82
+ /// The mark to remove.
83
+ readonly mark: Mark
84
+ ) {
77
85
  super()
78
- // :: number
79
- // The start of the unmarked range.
80
- this.from = from
81
- // :: number
82
- // The end of the unmarked range.
83
- this.to = to
84
- // :: Mark
85
- // The mark to remove.
86
- this.mark = mark
87
86
  }
88
87
 
89
- apply(doc) {
88
+ apply(doc: Node) {
90
89
  let oldSlice = doc.slice(this.from, this.to)
91
90
  let slice = new Slice(mapFragment(oldSlice.content, node => {
92
91
  return node.mark(this.mark.removeFromSet(node.marks))
93
- }), oldSlice.openStart, oldSlice.openEnd)
92
+ }, doc), oldSlice.openStart, oldSlice.openEnd)
94
93
  return StepResult.fromReplace(doc, this.from, this.to, slice)
95
94
  }
96
95
 
97
- invert() {
96
+ invert(): Step {
98
97
  return new AddMarkStep(this.from, this.to, this.mark)
99
98
  }
100
99
 
101
- map(mapping) {
100
+ map(mapping: Mappable): Step | null {
102
101
  let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1)
103
102
  if (from.deleted && to.deleted || from.pos >= to.pos) return null
104
103
  return new RemoveMarkStep(from.pos, to.pos, this.mark)
105
104
  }
106
105
 
107
- merge(other) {
106
+ merge(other: Step): Step | null {
108
107
  if (other instanceof RemoveMarkStep &&
109
108
  other.mark.eq(this.mark) &&
110
109
  this.from <= other.to && this.to >= other.from)
111
110
  return new RemoveMarkStep(Math.min(this.from, other.from),
112
111
  Math.max(this.to, other.to), this.mark)
112
+ return null
113
113
  }
114
114
 
115
- toJSON() {
115
+ toJSON(): any {
116
116
  return {stepType: "removeMark", mark: this.mark.toJSON(),
117
117
  from: this.from, to: this.to}
118
118
  }
119
119
 
120
- static fromJSON(schema, json) {
120
+ /// @internal
121
+ static fromJSON(schema: Schema, json: any) {
121
122
  if (typeof json.from != "number" || typeof json.to != "number")
122
123
  throw new RangeError("Invalid input for RemoveMarkStep.fromJSON")
123
124
  return new RemoveMarkStep(json.from, json.to, schema.markFromJSON(json.mark))