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/CHANGELOG.md +12 -0
- package/dist/index.cjs +1781 -0
- package/dist/index.d.ts +690 -0
- package/dist/index.es.js +31 -33
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +1699 -1638
- package/dist/index.js.map +1 -1
- package/package.json +14 -12
- package/src/{index.js → index.ts} +4 -2
- package/src/map.ts +256 -0
- package/src/{mark.js → mark.ts} +22 -35
- package/src/{mark_step.js → mark_step.ts} +40 -39
- package/src/{replace.js → replace.ts} +92 -142
- package/src/{replace_step.js → replace_step.ts} +65 -70
- package/src/step.ts +97 -0
- package/src/{structure.js → structure.ts} +78 -99
- package/src/transform.ts +212 -0
- package/rollup.config.js +0 -14
- package/src/map.js +0 -264
- package/src/step.js +0 -110
- package/src/transform.js +0 -71
package/package.json
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prosemirror-transform",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0-beta.1",
|
|
4
4
|
"description": "ProseMirror document transformations",
|
|
5
|
-
"
|
|
6
|
-
"
|
|
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
|
-
"
|
|
24
|
-
"
|
|
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": "
|
|
31
|
-
"
|
|
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
|
|
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
|
+
}
|
package/src/{mark.js → mark.ts}
RENAMED
|
@@ -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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
|
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 =>
|
|
35
|
-
added.forEach(s =>
|
|
36
|
-
return this
|
|
34
|
+
removed.forEach(s => tr.step(s))
|
|
35
|
+
added.forEach(s => tr.step(s))
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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 =>
|
|
79
|
-
return this
|
|
72
|
+
matched.forEach(m => tr.step(new RemoveMarkStep(m.from, m.to, m.style)))
|
|
80
73
|
}
|
|
81
74
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
92
|
+
tr.replace(cur, cur, new Slice(fill!, 0, 0))
|
|
105
93
|
}
|
|
106
|
-
for (let i = delSteps.length - 1; i >= 0; 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
|
-
|
|
16
|
+
/// Add a mark to all inline content between two positions.
|
|
16
17
|
export class AddMarkStep extends Step {
|
|
17
|
-
|
|
18
|
-
constructor(
|
|
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
|
-
|
|
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
|
-
|
|
74
|
+
/// Remove a mark from all inline content between two positions.
|
|
74
75
|
export class RemoveMarkStep extends Step {
|
|
75
|
-
|
|
76
|
-
constructor(
|
|
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
|
-
|
|
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))
|