prosemirror-transform 0.16.0 → 0.20.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/dist/index.js CHANGED
@@ -1,15 +1,15 @@
1
1
  ;var assign;
2
- ((assign = require("./transform"), exports.Transform = assign.Transform, exports.TransformError = assign.TransformError))
2
+ ((assign = require("./transform"), exports.Transform = assign.Transform, exports.TransformError = assign.TransformError, assign))
3
3
  ;var assign$1;
4
- ((assign$1 = require("./step"), exports.Step = assign$1.Step, exports.StepResult = assign$1.StepResult))
4
+ ((assign$1 = require("./step"), exports.Step = assign$1.Step, exports.StepResult = assign$1.StepResult, assign$1))
5
5
  ;var assign$2;
6
- ((assign$2 = require("./structure"), exports.joinPoint = assign$2.joinPoint, exports.canJoin = assign$2.canJoin, exports.canSplit = assign$2.canSplit, exports.insertPoint = assign$2.insertPoint, exports.liftTarget = assign$2.liftTarget, exports.findWrapping = assign$2.findWrapping))
6
+ ((assign$2 = require("./structure"), exports.joinPoint = assign$2.joinPoint, exports.canJoin = assign$2.canJoin, exports.canSplit = assign$2.canSplit, exports.insertPoint = assign$2.insertPoint, exports.liftTarget = assign$2.liftTarget, exports.findWrapping = assign$2.findWrapping, assign$2))
7
7
  ;var assign$3;
8
- ((assign$3 = require("./map"), exports.StepMap = assign$3.StepMap, exports.MapResult = assign$3.MapResult, exports.Mapping = assign$3.Mapping))
8
+ ((assign$3 = require("./map"), exports.StepMap = assign$3.StepMap, exports.MapResult = assign$3.MapResult, exports.Mapping = assign$3.Mapping, assign$3))
9
9
  ;var assign$4;
10
- ((assign$4 = require("./mark_step"), exports.AddMarkStep = assign$4.AddMarkStep, exports.RemoveMarkStep = assign$4.RemoveMarkStep))
10
+ ((assign$4 = require("./mark_step"), exports.AddMarkStep = assign$4.AddMarkStep, exports.RemoveMarkStep = assign$4.RemoveMarkStep, assign$4))
11
11
  ;var assign$5;
12
- ((assign$5 = require("./replace_step"), exports.ReplaceStep = assign$5.ReplaceStep, exports.ReplaceAroundStep = assign$5.ReplaceAroundStep))
12
+ ((assign$5 = require("./replace_step"), exports.ReplaceStep = assign$5.ReplaceStep, exports.ReplaceAroundStep = assign$5.ReplaceAroundStep, assign$5))
13
13
  require("./mark")
14
14
  ;var assign$6;
15
- ((assign$6 = require("./replace"), exports.replaceStep = assign$6.replaceStep))
15
+ ((assign$6 = require("./replace"), exports.replaceStep = assign$6.replaceStep, assign$6))
package/dist/map.js CHANGED
@@ -165,7 +165,7 @@ var Mapping = function Mapping(maps, mirror, from, to) {
165
165
  };
166
166
 
167
167
  // :: (?number, ?number) → Mapping
168
- // Create a remapping that maps only through a part of this one.
168
+ // Create a mapping that maps only through a part of this one.
169
169
  Mapping.prototype.slice = function slice (from, to) {
170
170
  if ( from === void 0 ) from = 0;
171
171
  if ( to === void 0 ) to = this.maps.length;
@@ -190,7 +190,7 @@ Mapping.prototype.setMirror = function setMirror (n, m) {
190
190
  };
191
191
 
192
192
  // :: (StepMap, ?number)
193
- // Add a step map to the end of this remapping. If `mirrors` is
193
+ // Add a step map to the end of this mapping. If `mirrors` is
194
194
  // given, it should be the index of the step map that is the mirror
195
195
  // image of this one.
196
196
  Mapping.prototype.appendMap = function appendMap (map, mirrors) {
@@ -210,8 +210,27 @@ Mapping.prototype.appendMapping = function appendMapping (mapping) {
210
210
  }
211
211
  };
212
212
 
213
+ // :: (Mapping)
214
+ // Append the inverse of the given mapping to this one.
215
+ Mapping.prototype.appendMappingInverted = function appendMappingInverted (mapping) {
216
+ var this$1 = this;
217
+
218
+ for (var i = mapping.maps.length - 1, totalSize = this.maps.length + mapping.maps.length; i >= 0; i--) {
219
+ var mirr = mapping.getMirror(i)
220
+ this$1.appendMap(mapping.maps[i].invert(), mirr != null && mirr > i ? totalSize - mirr - 1 : null)
221
+ }
222
+ };
223
+
224
+ // () → Mapping
225
+ // Create an inverted version of this mapping.
226
+ Mapping.prototype.invert = function invert () {
227
+ var inverse = new Mapping
228
+ inverse.appendMappingInverted(this)
229
+ return inverse
230
+ };
231
+
213
232
  // :: (number, ?number) → number
214
- // Map a position through this remapping.
233
+ // Map a position through this mapping.
215
234
  Mapping.prototype.map = function map (pos, assoc) {
216
235
  var this$1 = this;
217
236
 
@@ -222,7 +241,7 @@ Mapping.prototype.map = function map (pos, assoc) {
222
241
  };
223
242
 
224
243
  // :: (number, ?number) → MapResult
225
- // Map a position through this remapping, returning a mapping
244
+ // Map a position through this mapping, returning a mapping
226
245
  // result.
227
246
  Mapping.prototype.mapResult = function mapResult (pos, assoc) { return this._map(pos, assoc, false) };
228
247
 
package/dist/mark_step.js CHANGED
@@ -59,6 +59,10 @@ var AddMarkStep = (function (Step) {
59
59
  Math.max(this.to, other.to), this.mark) }
60
60
  };
61
61
 
62
+ AddMarkStep.prototype.offset = function offset (n) {
63
+ return new AddMarkStep(this.from + n, this.to + n, this.mark)
64
+ };
65
+
62
66
  AddMarkStep.fromJSON = function fromJSON (schema, json) {
63
67
  return new AddMarkStep(json.from, json.to, schema.markFromJSON(json.mark))
64
68
  };
@@ -110,6 +114,10 @@ var RemoveMarkStep = (function (Step) {
110
114
  Math.max(this.to, other.to), this.mark) }
111
115
  };
112
116
 
117
+ RemoveMarkStep.prototype.offset = function offset (n) {
118
+ return new RemoveMarkStep(this.from + n, this.to + n, this.mark)
119
+ };
120
+
113
121
  RemoveMarkStep.fromJSON = function fromJSON (schema, json) {
114
122
  return new RemoveMarkStep(json.from, json.to, schema.markFromJSON(json.mark))
115
123
  };
package/dist/replace.js CHANGED
@@ -31,11 +31,12 @@ Transform.prototype.replaceRange = function(from, to, slice) {
31
31
 
32
32
  if (!slice.size) { return this.deleteRange(from, to) }
33
33
 
34
- var $from = this.doc.resolve(from)
35
- if (fitsTrivially($from, this.doc.resolve(to), slice))
34
+ var $from = this.doc.resolve(from), $to = this.doc.resolve(to)
35
+ if (fitsTrivially($from, $to, slice))
36
36
  { return this.step(new ReplaceStep(from, to, slice)) }
37
37
 
38
38
  var canExpand = coveredDepths($from, this.doc.resolve(to)), preferredExpand = 0
39
+ if (canExpand[canExpand.length - 1] == 0) { canExpand.pop() }
39
40
  canExpand.unshift($from.depth + 1)
40
41
  for (var d = $from.depth; d > 0; d--) {
41
42
  if ($from.node(d).type.spec.defining) { break }
@@ -67,7 +68,7 @@ Transform.prototype.replaceRange = function(from, to, slice) {
67
68
  var expandDepth = canExpand[(i$1 + preferredExpand) % canExpand.length]
68
69
  var parent = $from.node(expandDepth - 1), index = $from.index(expandDepth - 1)
69
70
  if (parent.canReplaceWith(index, index, insert.type, insert.attrs, insert.marks))
70
- { return this$1.replace($from.before(expandDepth), expandDepth > $from.depth ? to : $from.after(expandDepth),
71
+ { return this$1.replace($from.before(expandDepth), expandDepth > $from.depth ? to : $to.after(expandDepth),
71
72
  new Slice(closeFragment(slice.content, 0, slice.openLeft, openDepth),
72
73
  openDepth, slice.openRight)) }
73
74
  }
@@ -103,28 +104,22 @@ Transform.prototype.replaceRangeWith = function(from, to, node) {
103
104
  }
104
105
 
105
106
  // :: (number, number) → Transform
106
- // Delete the given range, and any fully covered parent nodes that are
107
- // not allowed to be empty.
107
+ // Delete the given range, expanding it to cover fully covered
108
+ // parent nodes until a valid replace is found.
108
109
  Transform.prototype.deleteRange = function(from, to) {
109
- var $from = this.doc.resolve(from)
110
- var covered = coveredDepths($from, this.doc.resolve(to)), grown = false
111
- // Find the innermost covered node that allows its whole content to
112
- // be deleted
110
+ var $from = this.doc.resolve(from), $to = this.doc.resolve(to)
111
+ var covered = coveredDepths($from, $to)
113
112
  for (var i = 0; i < covered.length; i++) {
114
- if ($from.node(covered[i]).contentMatchAt(0).validEnd()) {
115
- from = $from.start(covered[i])
116
- to = $from.end(covered[i])
117
- grown = true
113
+ var depth = covered[i], last = i == covered.length - 1
114
+ if ((last && depth == 0) || $from.node(depth).contentMatchAt(0).validEnd()) {
115
+ from = $from.start(depth)
116
+ to = $to.end(depth)
118
117
  break
119
118
  }
120
- }
121
- // If no such node was found and the outermose covered node can be
122
- // deleted entirely, do that
123
- if (!grown && covered.length) {
124
- var depth = covered[covered.length - 1]
125
- if ($from.node(depth - 1).canReplace($from.index(depth - 1), $from.indexAfter(depth - 1))) {
119
+ if (depth > 0 && (last || $from.node(depth - 1).canReplace($from.index(depth - 1), $to.indexAfter(depth - 1)))) {
126
120
  from = $from.before(depth)
127
- to = $from.after(depth)
121
+ to = $to.after(depth)
122
+ break
128
123
  }
129
124
  }
130
125
  return this.delete(from, to)
@@ -132,13 +127,14 @@ Transform.prototype.deleteRange = function(from, to) {
132
127
 
133
128
  // : (ResolvedPos, ResolvedPos) → [number]
134
129
  // Returns an array of all depths for which $from - $to spans the
135
- // whole content of the node at that depth.
130
+ // whole content of the nodes at that depth.
136
131
  function coveredDepths($from, $to) {
137
- var result = []
138
- for (var i = 0; i < $from.depth; i++) {
139
- var depth = $from.depth - i
140
- if ($from.pos - i > $from.start(depth)) { break }
141
- if ($to.depth >= depth && $to.pos + ($to.depth - depth) == $from.end(depth)) { result.push(depth) }
132
+ var result = [], minDepth = Math.min($from.depth, $to.depth)
133
+ for (var d = minDepth; d >= 0; d--) {
134
+ var start = $from.start(d)
135
+ if (start < $from.pos - ($from.depth - d) ||
136
+ $to.end(d) > $to.pos + ($to.depth - d)) { break }
137
+ if (start == $to.start(d)) { result.push(d) }
142
138
  }
143
139
  return result
144
140
  }
@@ -388,7 +384,7 @@ function placeSlice($from, slice) {
388
384
  if (dSlice >= 0) {
389
385
  if (dSlice > 0) { // Inside slice
390
386
  ;var assign;
391
- ((assign = nodeLeft(slice.content, dSlice), curType = assign.type, curAttrs = assign.attrs, curFragment = assign.content))
387
+ ((assign = nodeLeft(slice.content, dSlice), curType = assign.type, curAttrs = assign.attrs, curFragment = assign.content, assign))
392
388
  } else if (dSlice == 0) { // Top of slice
393
389
  curFragment = slice.content
394
390
  }
@@ -408,6 +404,9 @@ function placeSlice($from, slice) {
408
404
  // This will go through the positions in $from, down from dFrom,
409
405
  // to find a fit
410
406
  var found = findPlacement(curFragment, $from, dFrom, placed)
407
+ if (found && unneccesaryFallthrough($from, dFrom, found.depth, slice, dSlice))
408
+ { found = null }
409
+
411
410
  if (found) {
412
411
  // If there was a fit, store it, and consider this content placed
413
412
  if (found.fragment.size > 0) { placed[found.depth] = {
@@ -433,7 +432,7 @@ function placeSlice($from, slice) {
433
432
  // Store the result for subsequent iterations.
434
433
  parents = [{type: top.type, attrs: top.attrs}].concat(wrap)
435
434
  ;var assign$1;
436
- ((assign$1 = last, curType = assign$1.type, curAttrs = assign$1.attrs))
435
+ ((assign$1 = last, curType = assign$1.type, curAttrs = assign$1.attrs, assign$1))
437
436
  }
438
437
  if (curFragment.size) {
439
438
  curFragment = curType.contentExpr.start(curAttrs).fillBefore(curFragment, true).append(curFragment)
@@ -482,3 +481,13 @@ function matchStrippingMarks(match, fragment) {
482
481
  }
483
482
  return Fragment.from(newNodes)
484
483
  }
484
+
485
+ function unneccesaryFallthrough($from, dFrom, dFound, slice, dSlice) {
486
+ if (dSlice < 1) { return false }
487
+ for (; dFrom > dFound; dFrom--) {
488
+ var here = $from.node(dFrom).contentMatchAt($from.indexAfter(dFrom))
489
+ for (var d = dSlice - 1; d >= 0; d--)
490
+ { if (here.matchNode(nodeLeft(slice.content, d))) { return true } }
491
+ }
492
+ return false
493
+ }
@@ -64,6 +64,10 @@ var ReplaceStep = (function (Step) {
64
64
  return json
65
65
  };
66
66
 
67
+ ReplaceStep.prototype.offset = function offset (n) {
68
+ return new ReplaceStep(this.from + n, this.to + n, this.slice, this.structure)
69
+ };
70
+
67
71
  ReplaceStep.fromJSON = function fromJSON (schema, json) {
68
72
  return new ReplaceStep(json.from, json.to, Slice.fromJSON(schema, json.slice), !!json.structure)
69
73
  };
@@ -133,6 +137,11 @@ var ReplaceAroundStep = (function (Step) {
133
137
  return true
134
138
  };
135
139
 
140
+ ReplaceAroundStep.prototype.offset = function offset (n) {
141
+ return new ReplaceAroundStep(this.from + n, this.to + n, this.gapFrom + n, this.gapTo + n,
142
+ this.slice, this.insert, this.structure)
143
+ };
144
+
136
145
  ReplaceAroundStep.fromJSON = function fromJSON (schema, json) {
137
146
  return new ReplaceAroundStep(json.from, json.to, json.gapFrom, json.gapTo,
138
147
  Slice.fromJSON(schema, json.slice), json.insert, !!json.structure)
package/dist/step.js CHANGED
@@ -43,6 +43,13 @@ Step.prototype.map = function map (_mapping) { return mustOverride() };
43
43
  // steps can't be merged.
44
44
  Step.prototype.merge = function merge (_other) { return null };
45
45
 
46
+ // :: (n: number) → Step
47
+ // Returns a copy of this step in which all positions have `n` added
48
+ // to them. The main use for this is to take a step in one document,
49
+ // and make it apply to a sub-document, or a larger document that
50
+ // the original document is a part of.
51
+ Step.prototype.offset = function offset (_n) { return mustOverride() };
52
+
46
53
  // :: () → Object
47
54
  // Create a JSON-serializeable representation of this step. By
48
55
  // default, it'll create an object with the step's [JSON
package/dist/structure.js CHANGED
@@ -145,20 +145,21 @@ Transform.prototype.setBlockType = function(from, to, type, attrs) {
145
145
  return this
146
146
  }
147
147
 
148
- // :: (number, ?NodeType, ?Object) → Transform
148
+ // :: (number, ?NodeType, ?Object, ?[Mark]) → Transform
149
149
  // Change the type and attributes of the node after `pos`.
150
- Transform.prototype.setNodeType = function(pos, type, attrs) {
150
+ Transform.prototype.setNodeType = function(pos, type, attrs, marks) {
151
151
  var node = this.doc.nodeAt(pos)
152
152
  if (!node) { throw new RangeError("No node at given position") }
153
153
  if (!type) { type = node.type }
154
+ var newNode = type.create(attrs, null, marks || node.marks)
154
155
  if (node.isLeaf)
155
- { return this.replaceWith(pos, pos + node.nodeSize, type.create(attrs, null, node.marks)) }
156
+ { return this.replaceWith(pos, pos + node.nodeSize, newNode) }
156
157
 
157
158
  if (!type.validContent(node.content, attrs))
158
159
  { throw new RangeError("Invalid content for node type " + type.name) }
159
160
 
160
161
  return this.step(new ReplaceAroundStep(pos, pos + node.nodeSize, pos + 1, pos + node.nodeSize - 1,
161
- new Slice(Fragment.from(type.create(attrs)), 0, 0), 1, true))
162
+ new Slice(Fragment.from(newNode), 0, 0), 1, true))
162
163
  }
163
164
 
164
165
  // :: (Node, number, ?[?{type: NodeType, attrs: ?Object}]) → bool
package/dist/transform.js CHANGED
@@ -2,13 +2,16 @@ var ref = require("./map");
2
2
  var Mapping = ref.Mapping;
3
3
 
4
4
  var TransformError = (function (Error) {
5
- function TransformError () {
6
- Error.apply(this, arguments);
7
- }if ( Error ) TransformError.__proto__ = Error;
5
+ function TransformError(message) { Error.call(this, message) }
6
+
7
+ if ( Error ) TransformError.__proto__ = Error;
8
8
  TransformError.prototype = Object.create( Error && Error.prototype );
9
9
  TransformError.prototype.constructor = TransformError;
10
10
 
11
-
11
+ var prototypeAccessors = { name: {} };
12
+ prototypeAccessors.name.get = function () { return "TransformError" };
13
+
14
+ Object.defineProperties( TransformError.prototype, prototypeAccessors );
12
15
 
13
16
  return TransformError;
14
17
  }(Error));
@@ -35,10 +38,10 @@ var Transform = function Transform(doc) {
35
38
  this.mapping = new Mapping
36
39
  };
37
40
 
38
- var prototypeAccessors = { before: {} };
41
+ var prototypeAccessors$1 = { before: {},docChanged: {} };
39
42
 
40
43
  // :: Node The document at the start of the transformation.
41
- prototypeAccessors.before.get = function () { return this.docs.length ? this.docs[0] : this.doc };
44
+ prototypeAccessors$1.before.get = function () { return this.docs.length ? this.docs[0] : this.doc };
42
45
 
43
46
  // :: (step: Step) → Transform
44
47
  // Apply a new step in this transformation, saving the result.
@@ -54,14 +57,22 @@ Transform.prototype.step = function step (object) {
54
57
  // fails. Returns the step result.
55
58
  Transform.prototype.maybeStep = function maybeStep (step) {
56
59
  var result = step.apply(this.doc)
57
- if (!result.failed) {
58
- this.docs.push(this.doc)
59
- this.steps.push(step)
60
- this.mapping.appendMap(step.getMap())
61
- this.doc = result.doc
62
- }
60
+ if (!result.failed) { this.addStep(step, result.doc) }
63
61
  return result
64
62
  };
65
63
 
66
- Object.defineProperties( Transform.prototype, prototypeAccessors );
64
+ // :: bool
65
+ // True when this transaction changes the document.
66
+ prototypeAccessors$1.docChanged.get = function () {
67
+ return this.steps.length > 0
68
+ };
69
+
70
+ Transform.prototype.addStep = function addStep (step, doc) {
71
+ this.docs.push(this.doc)
72
+ this.steps.push(step)
73
+ this.mapping.appendMap(step.getMap())
74
+ this.doc = doc
75
+ };
76
+
77
+ Object.defineProperties( Transform.prototype, prototypeAccessors$1 );
67
78
  exports.Transform = Transform
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prosemirror-transform",
3
- "version": "0.16.0",
3
+ "version": "0.20.0",
4
4
  "description": "ProseMirror document transformations",
5
5
  "main": "dist/index.js",
6
6
  "license": "MIT",
@@ -16,13 +16,14 @@
16
16
  "url": "git://github.com/prosemirror/prosemirror-transform.git"
17
17
  },
18
18
  "dependencies": {
19
- "prosemirror-model": "^0.16.0"
19
+ "prosemirror-model": "^0.20.0"
20
20
  },
21
21
  "devDependencies": {
22
22
  "buble": "^0.15.1",
23
23
  "mocha": "^3.0.2",
24
24
  "ist": "^1.0.0",
25
- "rimraf": "^2.5.4"
25
+ "rimraf": "^2.5.4",
26
+ "prosemirror-test-builder": "^0.20.0"
26
27
  },
27
28
  "scripts": {
28
29
  "test": "mocha test/test-*.js",
package/src/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  This module defines a way to transform documents. You can read more
2
- about transformations in [this guide](guide/transform.html).
2
+ about transformations in [this guide](/docs/guides/transform/).
3
3
 
4
4
  ### Steps
5
5
 
package/src/map.js CHANGED
@@ -147,7 +147,7 @@ StepMap.empty = new StepMap([])
147
147
  // class implements [`Mappable`](#transform.Mappable).
148
148
  class Mapping {
149
149
  // :: (?[StepMap])
150
- // Create a new remapping with the given position maps.
150
+ // Create a new mapping with the given position maps.
151
151
  constructor(maps, mirror, from, to) {
152
152
  // :: [StepMap]
153
153
  // The step maps in this mapping.
@@ -163,7 +163,7 @@ class Mapping {
163
163
  }
164
164
 
165
165
  // :: (?number, ?number) → Mapping
166
- // Create a remapping that maps only through a part of this one.
166
+ // Create a mapping that maps only through a part of this one.
167
167
  slice(from = 0, to = this.maps.length) {
168
168
  return new Mapping(this.maps, this.mirror, from, to)
169
169
  }
@@ -183,7 +183,7 @@ class Mapping {
183
183
  }
184
184
 
185
185
  // :: (StepMap, ?number)
186
- // Add a step map to the end of this remapping. If `mirrors` is
186
+ // Add a step map to the end of this mapping. If `mirrors` is
187
187
  // given, it should be the index of the step map that is the mirror
188
188
  // image of this one.
189
189
  appendMap(map, mirrors) {
@@ -201,8 +201,25 @@ class Mapping {
201
201
  }
202
202
  }
203
203
 
204
+ // :: (Mapping)
205
+ // Append the inverse of the given mapping to this one.
206
+ appendMappingInverted(mapping) {
207
+ for (let i = mapping.maps.length - 1, totalSize = this.maps.length + mapping.maps.length; i >= 0; i--) {
208
+ let mirr = mapping.getMirror(i)
209
+ this.appendMap(mapping.maps[i].invert(), mirr != null && mirr > i ? totalSize - mirr - 1 : null)
210
+ }
211
+ }
212
+
213
+ // () → Mapping
214
+ // Create an inverted version of this mapping.
215
+ invert() {
216
+ let inverse = new Mapping
217
+ inverse.appendMappingInverted(this)
218
+ return inverse
219
+ }
220
+
204
221
  // :: (number, ?number) → number
205
- // Map a position through this remapping.
222
+ // Map a position through this mapping.
206
223
  map(pos, assoc) {
207
224
  if (this.mirror) return this._map(pos, assoc, true)
208
225
  for (let i = this.from; i < this.to; i++)
@@ -211,7 +228,7 @@ class Mapping {
211
228
  }
212
229
 
213
230
  // :: (number, ?number) → MapResult
214
- // Map a position through this remapping, returning a mapping
231
+ // Map a position through this mapping, returning a mapping
215
232
  // result.
216
233
  mapResult(pos, assoc) { return this._map(pos, assoc, false) }
217
234
 
package/src/mark_step.js CHANGED
@@ -50,6 +50,10 @@ class AddMarkStep extends Step {
50
50
  Math.max(this.to, other.to), this.mark)
51
51
  }
52
52
 
53
+ offset(n) {
54
+ return new AddMarkStep(this.from + n, this.to + n, this.mark)
55
+ }
56
+
53
57
  static fromJSON(schema, json) {
54
58
  return new AddMarkStep(json.from, json.to, schema.markFromJSON(json.mark))
55
59
  }
@@ -94,6 +98,10 @@ class RemoveMarkStep extends Step {
94
98
  Math.max(this.to, other.to), this.mark)
95
99
  }
96
100
 
101
+ offset(n) {
102
+ return new RemoveMarkStep(this.from + n, this.to + n, this.mark)
103
+ }
104
+
97
105
  static fromJSON(schema, json) {
98
106
  return new RemoveMarkStep(json.from, json.to, schema.markFromJSON(json.mark))
99
107
  }
package/src/replace.js CHANGED
@@ -23,11 +23,12 @@ const {insertPoint} = require("./structure")
23
23
  Transform.prototype.replaceRange = function(from, to, slice) {
24
24
  if (!slice.size) return this.deleteRange(from, to)
25
25
 
26
- let $from = this.doc.resolve(from)
27
- if (fitsTrivially($from, this.doc.resolve(to), slice))
26
+ let $from = this.doc.resolve(from), $to = this.doc.resolve(to)
27
+ if (fitsTrivially($from, $to, slice))
28
28
  return this.step(new ReplaceStep(from, to, slice))
29
29
 
30
30
  let canExpand = coveredDepths($from, this.doc.resolve(to)), preferredExpand = 0
31
+ if (canExpand[canExpand.length - 1] == 0) canExpand.pop()
31
32
  canExpand.unshift($from.depth + 1)
32
33
  for (let d = $from.depth; d > 0; d--) {
33
34
  if ($from.node(d).type.spec.defining) break
@@ -59,7 +60,7 @@ Transform.prototype.replaceRange = function(from, to, slice) {
59
60
  let expandDepth = canExpand[(i + preferredExpand) % canExpand.length]
60
61
  let parent = $from.node(expandDepth - 1), index = $from.index(expandDepth - 1)
61
62
  if (parent.canReplaceWith(index, index, insert.type, insert.attrs, insert.marks))
62
- return this.replace($from.before(expandDepth), expandDepth > $from.depth ? to : $from.after(expandDepth),
63
+ return this.replace($from.before(expandDepth), expandDepth > $from.depth ? to : $to.after(expandDepth),
63
64
  new Slice(closeFragment(slice.content, 0, slice.openLeft, openDepth),
64
65
  openDepth, slice.openRight))
65
66
  }
@@ -95,28 +96,22 @@ Transform.prototype.replaceRangeWith = function(from, to, node) {
95
96
  }
96
97
 
97
98
  // :: (number, number) → Transform
98
- // Delete the given range, and any fully covered parent nodes that are
99
- // not allowed to be empty.
99
+ // Delete the given range, expanding it to cover fully covered
100
+ // parent nodes until a valid replace is found.
100
101
  Transform.prototype.deleteRange = function(from, to) {
101
- let $from = this.doc.resolve(from)
102
- let covered = coveredDepths($from, this.doc.resolve(to)), grown = false
103
- // Find the innermost covered node that allows its whole content to
104
- // be deleted
102
+ let $from = this.doc.resolve(from), $to = this.doc.resolve(to)
103
+ let covered = coveredDepths($from, $to)
105
104
  for (let i = 0; i < covered.length; i++) {
106
- if ($from.node(covered[i]).contentMatchAt(0).validEnd()) {
107
- from = $from.start(covered[i])
108
- to = $from.end(covered[i])
109
- grown = true
105
+ let depth = covered[i], last = i == covered.length - 1
106
+ if ((last && depth == 0) || $from.node(depth).contentMatchAt(0).validEnd()) {
107
+ from = $from.start(depth)
108
+ to = $to.end(depth)
110
109
  break
111
110
  }
112
- }
113
- // If no such node was found and the outermose covered node can be
114
- // deleted entirely, do that
115
- if (!grown && covered.length) {
116
- let depth = covered[covered.length - 1]
117
- if ($from.node(depth - 1).canReplace($from.index(depth - 1), $from.indexAfter(depth - 1))) {
111
+ if (depth > 0 && (last || $from.node(depth - 1).canReplace($from.index(depth - 1), $to.indexAfter(depth - 1)))) {
118
112
  from = $from.before(depth)
119
- to = $from.after(depth)
113
+ to = $to.after(depth)
114
+ break
120
115
  }
121
116
  }
122
117
  return this.delete(from, to)
@@ -124,13 +119,14 @@ Transform.prototype.deleteRange = function(from, to) {
124
119
 
125
120
  // : (ResolvedPos, ResolvedPos) → [number]
126
121
  // Returns an array of all depths for which $from - $to spans the
127
- // whole content of the node at that depth.
122
+ // whole content of the nodes at that depth.
128
123
  function coveredDepths($from, $to) {
129
- let result = []
130
- for (let i = 0; i < $from.depth; i++) {
131
- let depth = $from.depth - i
132
- if ($from.pos - i > $from.start(depth)) break
133
- if ($to.depth >= depth && $to.pos + ($to.depth - depth) == $from.end(depth)) result.push(depth)
124
+ let result = [], minDepth = Math.min($from.depth, $to.depth)
125
+ for (let d = minDepth; d >= 0; d--) {
126
+ let start = $from.start(d)
127
+ if (start < $from.pos - ($from.depth - d) ||
128
+ $to.end(d) > $to.pos + ($to.depth - d)) break
129
+ if (start == $to.start(d)) result.push(d)
134
130
  }
135
131
  return result
136
132
  }
@@ -391,6 +387,9 @@ function placeSlice($from, slice) {
391
387
  // This will go through the positions in $from, down from dFrom,
392
388
  // to find a fit
393
389
  let found = findPlacement(curFragment, $from, dFrom, placed)
390
+ if (found && unneccesaryFallthrough($from, dFrom, found.depth, slice, dSlice))
391
+ found = null
392
+
394
393
  if (found) {
395
394
  // If there was a fit, store it, and consider this content placed
396
395
  if (found.fragment.size > 0) placed[found.depth] = {
@@ -464,3 +463,13 @@ function matchStrippingMarks(match, fragment) {
464
463
  }
465
464
  return Fragment.from(newNodes)
466
465
  }
466
+
467
+ function unneccesaryFallthrough($from, dFrom, dFound, slice, dSlice) {
468
+ if (dSlice < 1) return false
469
+ for (; dFrom > dFound; dFrom--) {
470
+ let here = $from.node(dFrom).contentMatchAt($from.indexAfter(dFrom))
471
+ for (let d = dSlice - 1; d >= 0; d--)
472
+ if (here.matchNode(nodeLeft(slice.content, d))) return true
473
+ }
474
+ return false
475
+ }
@@ -5,7 +5,7 @@ const {StepMap} = require("./map")
5
5
 
6
6
  // ::- Replace a part of the document with a slice of new content.
7
7
  class ReplaceStep extends Step {
8
- // :: (number, number, Slice, bool)
8
+ // :: (number, number, Slice, ?bool)
9
9
  // The given `slice` should fit the 'gap' between `from` and
10
10
  // `to`—the depths must line up, and the surrounding nodes must be
11
11
  // able to be joined with the open sides of the slice. When
@@ -64,6 +64,10 @@ class ReplaceStep extends Step {
64
64
  return json
65
65
  }
66
66
 
67
+ offset(n) {
68
+ return new ReplaceStep(this.from + n, this.to + n, this.slice, this.structure)
69
+ }
70
+
67
71
  static fromJSON(schema, json) {
68
72
  return new ReplaceStep(json.from, json.to, Slice.fromJSON(schema, json.slice), !!json.structure)
69
73
  }
@@ -76,7 +80,7 @@ Step.jsonID("replace", ReplaceStep)
76
80
  // preserve a range of the replaced content by moving it into the
77
81
  // slice.
78
82
  class ReplaceAroundStep extends Step {
79
- // :: (number, number, number, number, Slice, number, bool)
83
+ // :: (number, number, number, number, Slice, number, ?bool)
80
84
  // Create a replace-wrap step with the given range and gap. `insert`
81
85
  // should be the point in the slice into which the gap should be
82
86
  // moved. `structure` has the same meaning as it has in the
@@ -132,6 +136,11 @@ class ReplaceAroundStep extends Step {
132
136
  return true
133
137
  }
134
138
 
139
+ offset(n) {
140
+ return new ReplaceAroundStep(this.from + n, this.to + n, this.gapFrom + n, this.gapTo + n,
141
+ this.slice, this.insert, this.structure)
142
+ }
143
+
135
144
  static fromJSON(schema, json) {
136
145
  return new ReplaceAroundStep(json.from, json.to, json.gapFrom, json.gapTo,
137
146
  Slice.fromJSON(schema, json.slice), json.insert, !!json.structure)
package/src/step.js CHANGED
@@ -45,6 +45,13 @@ class Step {
45
45
  // steps can't be merged.
46
46
  merge(_other) { return null }
47
47
 
48
+ // :: (n: number) → Step
49
+ // Returns a copy of this step in which all positions have `n` added
50
+ // to them. The main use for this is to take a step in one document,
51
+ // and make it apply to a sub-document, or a larger document that
52
+ // the original document is a part of.
53
+ offset(_n) { return mustOverride() }
54
+
48
55
  // :: () → Object
49
56
  // Create a JSON-serializeable representation of this step. By
50
57
  // default, it'll create an object with the step's [JSON
package/src/structure.js CHANGED
@@ -129,20 +129,21 @@ Transform.prototype.setBlockType = function(from, to = from, type, attrs) {
129
129
  return this
130
130
  }
131
131
 
132
- // :: (number, ?NodeType, ?Object) → Transform
132
+ // :: (number, ?NodeType, ?Object, ?[Mark]) → Transform
133
133
  // Change the type and attributes of the node after `pos`.
134
- Transform.prototype.setNodeType = function(pos, type, attrs) {
134
+ Transform.prototype.setNodeType = function(pos, type, attrs, marks) {
135
135
  let node = this.doc.nodeAt(pos)
136
136
  if (!node) throw new RangeError("No node at given position")
137
137
  if (!type) type = node.type
138
+ let newNode = type.create(attrs, null, marks || node.marks)
138
139
  if (node.isLeaf)
139
- return this.replaceWith(pos, pos + node.nodeSize, type.create(attrs, null, node.marks))
140
+ return this.replaceWith(pos, pos + node.nodeSize, newNode)
140
141
 
141
142
  if (!type.validContent(node.content, attrs))
142
143
  throw new RangeError("Invalid content for node type " + type.name)
143
144
 
144
145
  return this.step(new ReplaceAroundStep(pos, pos + node.nodeSize, pos + 1, pos + node.nodeSize - 1,
145
- new Slice(Fragment.from(type.create(attrs)), 0, 0), 1, true))
146
+ new Slice(Fragment.from(newNode), 0, 0), 1, true))
146
147
  }
147
148
 
148
149
  // :: (Node, number, ?[?{type: NodeType, attrs: ?Object}]) → bool
package/src/transform.js CHANGED
@@ -1,6 +1,9 @@
1
1
  const {Mapping} = require("./map")
2
2
 
3
- class TransformError extends Error {}
3
+ class TransformError extends Error {
4
+ constructor(message) { super(message) }
5
+ get name() { return "TransformError" }
6
+ }
4
7
  exports.TransformError = TransformError
5
8
 
6
9
  // ::- Abstraction to build up and track such an array of
@@ -44,13 +47,21 @@ class Transform {
44
47
  // fails. Returns the step result.
45
48
  maybeStep(step) {
46
49
  let result = step.apply(this.doc)
47
- if (!result.failed) {
48
- this.docs.push(this.doc)
49
- this.steps.push(step)
50
- this.mapping.appendMap(step.getMap())
51
- this.doc = result.doc
52
- }
50
+ if (!result.failed) this.addStep(step, result.doc)
53
51
  return result
54
52
  }
53
+
54
+ // :: bool
55
+ // True when this transaction changes the document.
56
+ get docChanged() {
57
+ return this.steps.length > 0
58
+ }
59
+
60
+ addStep(step, doc) {
61
+ this.docs.push(this.doc)
62
+ this.steps.push(step)
63
+ this.mapping.appendMap(step.getMap())
64
+ this.doc = doc
65
+ }
55
66
  }
56
67
  exports.Transform = Transform