prosemirror-transform 1.7.5 → 1.9.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.d.cts CHANGED
@@ -414,9 +414,15 @@ declare class Transform {
414
414
  setNodeMarkup(pos: number, type?: NodeType | null, attrs?: Attrs | null, marks?: readonly Mark[]): this;
415
415
  /**
416
416
  Set a single attribute on a given node to a new value.
417
+ The `pos` addresses the document content. Use `setDocAttribute`
418
+ to set attributes on the document itself.
417
419
  */
418
420
  setNodeAttribute(pos: number, attr: string, value: any): this;
419
421
  /**
422
+ Set a single attribute on the document to a new value.
423
+ */
424
+ setDocAttribute(attr: string, value: any): this;
425
+ /**
420
426
  Add a mark to the node at position `pos`.
421
427
  */
422
428
  addNodeMark(pos: number, mark: Mark): this;
@@ -797,6 +803,30 @@ declare class AttrStep extends Step {
797
803
  toJSON(): any;
798
804
  static fromJSON(schema: Schema, json: any): AttrStep;
799
805
  }
806
+ /**
807
+ Update an attribute in the doc node.
808
+ */
809
+ declare class DocAttrStep extends Step {
810
+ /**
811
+ The attribute to set.
812
+ */
813
+ readonly attr: string;
814
+ readonly value: any;
815
+ /**
816
+ Construct an attribute step.
817
+ */
818
+ constructor(
819
+ /**
820
+ The attribute to set.
821
+ */
822
+ attr: string, value: any);
823
+ apply(doc: Node): StepResult;
824
+ getMap(): StepMap;
825
+ invert(doc: Node): DocAttrStep;
826
+ map(mapping: Mappable): this;
827
+ toJSON(): any;
828
+ static fromJSON(schema: Schema, json: any): DocAttrStep;
829
+ }
800
830
 
801
831
  /**
802
832
  ‘Fit’ a slice into a given position in the document, producing a
@@ -806,4 +836,4 @@ would be a no-op (an empty slice over an empty range).
806
836
  */
807
837
  declare function replaceStep(doc: Node, from: number, to?: number, slice?: Slice): Step | null;
808
838
 
809
- export { AddMarkStep, AddNodeMarkStep, AttrStep, MapResult, Mappable, Mapping, RemoveMarkStep, RemoveNodeMarkStep, ReplaceAroundStep, ReplaceStep, Step, StepMap, StepResult, Transform, canJoin, canSplit, dropPoint, findWrapping, insertPoint, joinPoint, liftTarget, replaceStep };
839
+ export { AddMarkStep, AddNodeMarkStep, AttrStep, DocAttrStep, MapResult, type Mappable, Mapping, RemoveMarkStep, RemoveNodeMarkStep, ReplaceAroundStep, ReplaceStep, Step, StepMap, StepResult, Transform, canJoin, canSplit, dropPoint, findWrapping, insertPoint, joinPoint, liftTarget, replaceStep };
package/dist/index.d.ts CHANGED
@@ -414,9 +414,15 @@ declare class Transform {
414
414
  setNodeMarkup(pos: number, type?: NodeType | null, attrs?: Attrs | null, marks?: readonly Mark[]): this;
415
415
  /**
416
416
  Set a single attribute on a given node to a new value.
417
+ The `pos` addresses the document content. Use `setDocAttribute`
418
+ to set attributes on the document itself.
417
419
  */
418
420
  setNodeAttribute(pos: number, attr: string, value: any): this;
419
421
  /**
422
+ Set a single attribute on the document to a new value.
423
+ */
424
+ setDocAttribute(attr: string, value: any): this;
425
+ /**
420
426
  Add a mark to the node at position `pos`.
421
427
  */
422
428
  addNodeMark(pos: number, mark: Mark): this;
@@ -797,6 +803,30 @@ declare class AttrStep extends Step {
797
803
  toJSON(): any;
798
804
  static fromJSON(schema: Schema, json: any): AttrStep;
799
805
  }
806
+ /**
807
+ Update an attribute in the doc node.
808
+ */
809
+ declare class DocAttrStep extends Step {
810
+ /**
811
+ The attribute to set.
812
+ */
813
+ readonly attr: string;
814
+ readonly value: any;
815
+ /**
816
+ Construct an attribute step.
817
+ */
818
+ constructor(
819
+ /**
820
+ The attribute to set.
821
+ */
822
+ attr: string, value: any);
823
+ apply(doc: Node): StepResult;
824
+ getMap(): StepMap;
825
+ invert(doc: Node): DocAttrStep;
826
+ map(mapping: Mappable): this;
827
+ toJSON(): any;
828
+ static fromJSON(schema: Schema, json: any): DocAttrStep;
829
+ }
800
830
 
801
831
  /**
802
832
  ‘Fit’ a slice into a given position in the document, producing a
@@ -806,4 +836,4 @@ would be a no-op (an empty slice over an empty range).
806
836
  */
807
837
  declare function replaceStep(doc: Node, from: number, to?: number, slice?: Slice): Step | null;
808
838
 
809
- export { AddMarkStep, AddNodeMarkStep, AttrStep, MapResult, Mappable, Mapping, RemoveMarkStep, RemoveNodeMarkStep, ReplaceAroundStep, ReplaceStep, Step, StepMap, StepResult, Transform, canJoin, canSplit, dropPoint, findWrapping, insertPoint, joinPoint, liftTarget, replaceStep };
839
+ export { AddMarkStep, AddNodeMarkStep, AttrStep, DocAttrStep, MapResult, type Mappable, Mapping, RemoveMarkStep, RemoveNodeMarkStep, ReplaceAroundStep, ReplaceStep, Step, StepMap, StepResult, Transform, canJoin, canSplit, dropPoint, findWrapping, insertPoint, joinPoint, liftTarget, replaceStep };
package/dist/index.js CHANGED
@@ -824,7 +824,8 @@ class ReplaceAroundStep extends Step {
824
824
  }
825
825
  map(mapping) {
826
826
  let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1);
827
- let gapFrom = mapping.map(this.gapFrom, -1), gapTo = mapping.map(this.gapTo, 1);
827
+ let gapFrom = this.from == this.gapFrom ? from.pos : mapping.map(this.gapFrom, -1);
828
+ let gapTo = this.to == this.gapTo ? to.pos : mapping.map(this.gapTo, 1);
828
829
  if ((from.deletedAcross && to.deletedAcross) || gapFrom < from.pos || gapTo > to.pos)
829
830
  return null;
830
831
  return new ReplaceAroundStep(from.pos, to.pos, gapFrom, gapTo, this.slice, this.insert, this.structure);
@@ -936,7 +937,7 @@ function removeMark(tr, from, to, mark) {
936
937
  });
937
938
  matched.forEach(m => tr.step(new RemoveMarkStep(m.from, m.to, m.style)));
938
939
  }
939
- function clearIncompatible(tr, pos, parentType, match = parentType.contentMatch) {
940
+ function clearIncompatible(tr, pos, parentType, match = parentType.contentMatch, clearNewlines = true) {
940
941
  let node = tr.doc.nodeAt(pos);
941
942
  let replSteps = [], cur = pos + 1;
942
943
  for (let i = 0; i < node.childCount; i++) {
@@ -950,7 +951,7 @@ function clearIncompatible(tr, pos, parentType, match = parentType.contentMatch)
950
951
  for (let j = 0; j < child.marks.length; j++)
951
952
  if (!parentType.allowsMarkType(child.marks[j].type))
952
953
  tr.step(new RemoveMarkStep(cur, end, child.marks[j]));
953
- if (child.isText && !parentType.spec.code) {
954
+ if (clearNewlines && child.isText && parentType.whitespace != "pre") {
954
955
  let m, newline = /\r?\n|\r/g, slice;
955
956
  while (m = newline.exec(child.text)) {
956
957
  if (!slice)
@@ -1075,15 +1076,46 @@ function setBlockType(tr, from, to, type, attrs) {
1075
1076
  let mapFrom = tr.steps.length;
1076
1077
  tr.doc.nodesBetween(from, to, (node, pos) => {
1077
1078
  if (node.isTextblock && !node.hasMarkup(type, attrs) && canChangeType(tr.doc, tr.mapping.slice(mapFrom).map(pos), type)) {
1079
+ let convertNewlines = null;
1080
+ if (type.schema.linebreakReplacement) {
1081
+ let pre = type.whitespace == "pre", supportLinebreak = !!type.contentMatch.matchType(type.schema.linebreakReplacement);
1082
+ if (pre && !supportLinebreak)
1083
+ convertNewlines = false;
1084
+ else if (!pre && supportLinebreak)
1085
+ convertNewlines = true;
1086
+ }
1078
1087
  // Ensure all markup that isn't allowed in the new node type is cleared
1079
- tr.clearIncompatible(tr.mapping.slice(mapFrom).map(pos, 1), type);
1088
+ if (convertNewlines === false)
1089
+ replaceLinebreaks(tr, node, pos, mapFrom);
1090
+ clearIncompatible(tr, tr.mapping.slice(mapFrom).map(pos, 1), type, undefined, convertNewlines === null);
1080
1091
  let mapping = tr.mapping.slice(mapFrom);
1081
1092
  let startM = mapping.map(pos, 1), endM = mapping.map(pos + node.nodeSize, 1);
1082
1093
  tr.step(new ReplaceAroundStep(startM, endM, startM + 1, endM - 1, new Slice(Fragment.from(type.create(attrs, null, node.marks)), 0, 0), 1, true));
1094
+ if (convertNewlines === true)
1095
+ replaceNewlines(tr, node, pos, mapFrom);
1083
1096
  return false;
1084
1097
  }
1085
1098
  });
1086
1099
  }
1100
+ function replaceNewlines(tr, node, pos, mapFrom) {
1101
+ node.forEach((child, offset) => {
1102
+ if (child.isText) {
1103
+ let m, newline = /\r?\n|\r/g;
1104
+ while (m = newline.exec(child.text)) {
1105
+ let start = tr.mapping.slice(mapFrom).map(pos + 1 + offset + m.index);
1106
+ tr.replaceWith(start, start + 1, node.type.schema.linebreakReplacement.create());
1107
+ }
1108
+ }
1109
+ });
1110
+ }
1111
+ function replaceLinebreaks(tr, node, pos, mapFrom) {
1112
+ node.forEach((child, offset) => {
1113
+ if (child.type == child.type.schema.linebreakReplacement) {
1114
+ let start = tr.mapping.slice(mapFrom).map(pos + 1 + offset);
1115
+ tr.replaceWith(start, start + 1, node.type.schema.text("\n"));
1116
+ }
1117
+ });
1118
+ }
1087
1119
  function canChangeType(doc, pos, type) {
1088
1120
  let $pos = doc.resolve(pos), index = $pos.index();
1089
1121
  return $pos.parent.canReplaceWith(index, index + 1, type);
@@ -1746,6 +1778,51 @@ class AttrStep extends Step {
1746
1778
  }
1747
1779
  }
1748
1780
  Step.jsonID("attr", AttrStep);
1781
+ /**
1782
+ Update an attribute in the doc node.
1783
+ */
1784
+ class DocAttrStep extends Step {
1785
+ /**
1786
+ Construct an attribute step.
1787
+ */
1788
+ constructor(
1789
+ /**
1790
+ The attribute to set.
1791
+ */
1792
+ attr,
1793
+ // The attribute's new value.
1794
+ value) {
1795
+ super();
1796
+ this.attr = attr;
1797
+ this.value = value;
1798
+ }
1799
+ apply(doc) {
1800
+ let attrs = Object.create(null);
1801
+ for (let name in doc.attrs)
1802
+ attrs[name] = doc.attrs[name];
1803
+ attrs[this.attr] = this.value;
1804
+ let updated = doc.type.create(attrs, doc.content, doc.marks);
1805
+ return StepResult.ok(updated);
1806
+ }
1807
+ getMap() {
1808
+ return StepMap.empty;
1809
+ }
1810
+ invert(doc) {
1811
+ return new DocAttrStep(this.attr, doc.attrs[this.attr]);
1812
+ }
1813
+ map(mapping) {
1814
+ return this;
1815
+ }
1816
+ toJSON() {
1817
+ return { stepType: "docAttr", attr: this.attr, value: this.value };
1818
+ }
1819
+ static fromJSON(schema, json) {
1820
+ if (typeof json.attr != "string")
1821
+ throw new RangeError("Invalid input for DocAttrStep.fromJSON");
1822
+ return new DocAttrStep(json.attr, json.value);
1823
+ }
1824
+ }
1825
+ Step.jsonID("docAttr", DocAttrStep);
1749
1826
 
1750
1827
  /**
1751
1828
  @internal
@@ -1950,12 +2027,21 @@ class Transform {
1950
2027
  }
1951
2028
  /**
1952
2029
  Set a single attribute on a given node to a new value.
2030
+ The `pos` addresses the document content. Use `setDocAttribute`
2031
+ to set attributes on the document itself.
1953
2032
  */
1954
2033
  setNodeAttribute(pos, attr, value) {
1955
2034
  this.step(new AttrStep(pos, attr, value));
1956
2035
  return this;
1957
2036
  }
1958
2037
  /**
2038
+ Set a single attribute on the document to a new value.
2039
+ */
2040
+ setDocAttribute(attr, value) {
2041
+ this.step(new DocAttrStep(attr, value));
2042
+ return this;
2043
+ }
2044
+ /**
1959
2045
  Add a mark to the node at position `pos`.
1960
2046
  */
1961
2047
  addNodeMark(pos, mark) {
@@ -2018,4 +2104,4 @@ class Transform {
2018
2104
  }
2019
2105
  }
2020
2106
 
2021
- export { AddMarkStep, AddNodeMarkStep, AttrStep, MapResult, Mapping, RemoveMarkStep, RemoveNodeMarkStep, ReplaceAroundStep, ReplaceStep, Step, StepMap, StepResult, Transform, TransformError, canJoin, canSplit, dropPoint, findWrapping, insertPoint, joinPoint, liftTarget, replaceStep };
2107
+ export { AddMarkStep, AddNodeMarkStep, AttrStep, DocAttrStep, MapResult, Mapping, RemoveMarkStep, RemoveNodeMarkStep, ReplaceAroundStep, ReplaceStep, Step, StepMap, StepResult, Transform, TransformError, canJoin, canSplit, dropPoint, findWrapping, insertPoint, joinPoint, liftTarget, replaceStep };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prosemirror-transform",
3
- "version": "1.7.5",
3
+ "version": "1.9.0",
4
4
  "description": "ProseMirror document transformations",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -24,7 +24,7 @@
24
24
  "url": "git://github.com/prosemirror/prosemirror-transform.git"
25
25
  },
26
26
  "dependencies": {
27
- "prosemirror-model": "^1.0.0"
27
+ "prosemirror-model": "^1.21.0"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@prosemirror/buildhelper": "^0.1.5",
package/src/README.md CHANGED
@@ -23,6 +23,7 @@ called a [`Transform`](#transform.Transform).
23
23
  @AddNodeMarkStep
24
24
  @RemoveNodeMarkStep
25
25
  @AttrStep
26
+ @DocAttrStep
26
27
 
27
28
  ### Position Mapping
28
29
 
package/src/attr_step.ts CHANGED
@@ -51,3 +51,48 @@ export class AttrStep extends Step {
51
51
  }
52
52
 
53
53
  Step.jsonID("attr", AttrStep)
54
+
55
+ /// Update an attribute in the doc node.
56
+ export class DocAttrStep extends Step {
57
+ /// Construct an attribute step.
58
+ constructor(
59
+ /// The attribute to set.
60
+ readonly attr: string,
61
+ // The attribute's new value.
62
+ readonly value: any
63
+ ) {
64
+ super()
65
+ }
66
+
67
+ apply(doc: Node) {
68
+ let attrs = Object.create(null)
69
+ for (let name in doc.attrs) attrs[name] = doc.attrs[name]
70
+ attrs[this.attr] = this.value
71
+ let updated = doc.type.create(attrs, doc.content, doc.marks)
72
+ return StepResult.ok(updated)
73
+ }
74
+
75
+ getMap() {
76
+ return StepMap.empty
77
+ }
78
+
79
+ invert(doc: Node) {
80
+ return new DocAttrStep(this.attr, doc.attrs[this.attr])
81
+ }
82
+
83
+ map(mapping: Mappable) {
84
+ return this
85
+ }
86
+
87
+ toJSON(): any {
88
+ return {stepType: "docAttr", attr: this.attr, value: this.value}
89
+ }
90
+
91
+ static fromJSON(schema: Schema, json: any) {
92
+ if (typeof json.attr != "string")
93
+ throw new RangeError("Invalid input for DocAttrStep.fromJSON")
94
+ return new DocAttrStep(json.attr, json.value)
95
+ }
96
+ }
97
+
98
+ Step.jsonID("docAttr", DocAttrStep)
package/src/index.ts CHANGED
@@ -6,6 +6,6 @@ export {joinPoint, canJoin, canSplit, insertPoint, dropPoint, liftTarget, findWr
6
6
  export {StepMap, MapResult, Mapping, Mappable} from "./map"
7
7
  export {AddMarkStep, RemoveMarkStep, AddNodeMarkStep, RemoveNodeMarkStep} from "./mark_step"
8
8
  export {ReplaceStep, ReplaceAroundStep} from "./replace_step"
9
- export {AttrStep} from "./attr_step"
9
+ export {AttrStep, DocAttrStep} from "./attr_step"
10
10
  import "./mark"
11
11
  export {replaceStep} from "./replace"
package/src/mark.ts CHANGED
@@ -72,7 +72,9 @@ export function removeMark(tr: Transform, from: number, to: number, mark?: Mark
72
72
  matched.forEach(m => tr.step(new RemoveMarkStep(m.from, m.to, m.style)))
73
73
  }
74
74
 
75
- export function clearIncompatible(tr: Transform, pos: number, parentType: NodeType, match = parentType.contentMatch) {
75
+ export function clearIncompatible(tr: Transform, pos: number, parentType: NodeType,
76
+ match = parentType.contentMatch,
77
+ clearNewlines = true) {
76
78
  let node = tr.doc.nodeAt(pos)!
77
79
  let replSteps: Step[] = [], cur = pos + 1
78
80
  for (let i = 0; i < node.childCount; i++) {
@@ -85,7 +87,7 @@ export function clearIncompatible(tr: Transform, pos: number, parentType: NodeTy
85
87
  for (let j = 0; j < child.marks.length; j++) if (!parentType.allowsMarkType(child.marks[j].type))
86
88
  tr.step(new RemoveMarkStep(cur, end, child.marks[j]))
87
89
 
88
- if (child.isText && !parentType.spec.code) {
90
+ if (clearNewlines && child.isText && parentType.whitespace != "pre") {
89
91
  let m, newline = /\r?\n|\r/g, slice
90
92
  while (m = newline.exec(child.text!)) {
91
93
  if (!slice) slice = new Slice(Fragment.from(parentType.schema.text(" ", parentType.allowedMarks(child.marks))),
@@ -134,7 +134,8 @@ export class ReplaceAroundStep extends Step {
134
134
 
135
135
  map(mapping: Mappable) {
136
136
  let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1)
137
- let gapFrom = mapping.map(this.gapFrom, -1), gapTo = mapping.map(this.gapTo, 1)
137
+ let gapFrom = this.from == this.gapFrom ? from.pos : mapping.map(this.gapFrom, -1)
138
+ let gapTo = this.to == this.gapTo ? to.pos : mapping.map(this.gapTo, 1)
138
139
  if ((from.deletedAcross && to.deletedAcross) || gapFrom < from.pos || gapTo > to.pos) return null
139
140
  return new ReplaceAroundStep(from.pos, to.pos, gapFrom, gapTo, this.slice, this.insert, this.structure)
140
141
  }
package/src/structure.ts CHANGED
@@ -2,6 +2,7 @@ import {Slice, Fragment, NodeRange, NodeType, Node, Mark, Attrs, ContentMatch} f
2
2
 
3
3
  import {Transform} from "./transform"
4
4
  import {ReplaceStep, ReplaceAroundStep} from "./replace_step"
5
+ import {clearIncompatible} from "./mark"
5
6
 
6
7
  function canCut(node: Node, start: number, end: number) {
7
8
  return (start == 0 || node.canReplace(start, node.childCount)) &&
@@ -116,17 +117,46 @@ export function setBlockType(tr: Transform, from: number, to: number, type: Node
116
117
  let mapFrom = tr.steps.length
117
118
  tr.doc.nodesBetween(from, to, (node, pos) => {
118
119
  if (node.isTextblock && !node.hasMarkup(type, attrs) && canChangeType(tr.doc, tr.mapping.slice(mapFrom).map(pos), type)) {
120
+ let convertNewlines = null
121
+ if (type.schema.linebreakReplacement) {
122
+ let pre = type.whitespace == "pre", supportLinebreak = !!type.contentMatch.matchType(type.schema.linebreakReplacement)
123
+ if (pre && !supportLinebreak) convertNewlines = false
124
+ else if (!pre && supportLinebreak) convertNewlines = true
125
+ }
119
126
  // Ensure all markup that isn't allowed in the new node type is cleared
120
- tr.clearIncompatible(tr.mapping.slice(mapFrom).map(pos, 1), type)
127
+ if (convertNewlines === false) replaceLinebreaks(tr, node, pos, mapFrom)
128
+ clearIncompatible(tr, tr.mapping.slice(mapFrom).map(pos, 1), type, undefined, convertNewlines === null)
121
129
  let mapping = tr.mapping.slice(mapFrom)
122
130
  let startM = mapping.map(pos, 1), endM = mapping.map(pos + node.nodeSize, 1)
123
131
  tr.step(new ReplaceAroundStep(startM, endM, startM + 1, endM - 1,
124
132
  new Slice(Fragment.from(type.create(attrs, null, node.marks)), 0, 0), 1, true))
133
+ if (convertNewlines === true) replaceNewlines(tr, node, pos, mapFrom)
125
134
  return false
126
135
  }
127
136
  })
128
137
  }
129
138
 
139
+ function replaceNewlines(tr: Transform, node: Node, pos: number, mapFrom: number) {
140
+ node.forEach((child, offset) => {
141
+ if (child.isText) {
142
+ let m, newline = /\r?\n|\r/g
143
+ while (m = newline.exec(child.text!)) {
144
+ let start = tr.mapping.slice(mapFrom).map(pos + 1 + offset + m.index)
145
+ tr.replaceWith(start, start + 1, node.type.schema.linebreakReplacement!.create())
146
+ }
147
+ }
148
+ })
149
+ }
150
+
151
+ function replaceLinebreaks(tr: Transform, node: Node, pos: number, mapFrom: number) {
152
+ node.forEach((child, offset) => {
153
+ if (child.type == child.type.schema.linebreakReplacement) {
154
+ let start = tr.mapping.slice(mapFrom).map(pos + 1 + offset)
155
+ tr.replaceWith(start, start + 1, node.type.schema.text("\n"))
156
+ }
157
+ })
158
+ }
159
+
130
160
  function canChangeType(doc: Node, pos: number, type: NodeType) {
131
161
  let $pos = doc.resolve(pos), index = $pos.index()
132
162
  return $pos.parent.canReplaceWith(index, index + 1, type)
package/src/transform.ts CHANGED
@@ -5,7 +5,7 @@ import {Step} from "./step"
5
5
  import {addMark, removeMark, clearIncompatible} from "./mark"
6
6
  import {replaceStep, replaceRange, replaceRangeWith, deleteRange} from "./replace"
7
7
  import {lift, wrap, setBlockType, setNodeMarkup, split, join} from "./structure"
8
- import {AttrStep} from "./attr_step"
8
+ import {AttrStep, DocAttrStep} from "./attr_step"
9
9
  import {AddNodeMarkStep, RemoveNodeMarkStep} from "./mark_step"
10
10
 
11
11
  /// @internal
@@ -179,11 +179,19 @@ export class Transform {
179
179
  }
180
180
 
181
181
  /// Set a single attribute on a given node to a new value.
182
+ /// The `pos` addresses the document content. Use `setDocAttribute`
183
+ /// to set attributes on the document itself.
182
184
  setNodeAttribute(pos: number, attr: string, value: any): this {
183
185
  this.step(new AttrStep(pos, attr, value))
184
186
  return this
185
187
  }
186
188
 
189
+ /// Set a single attribute on the document to a new value.
190
+ setDocAttribute(attr: string, value: any): this {
191
+ this.step(new DocAttrStep(attr, value))
192
+ return this
193
+ }
194
+
187
195
  /// Add a mark to the node at position `pos`.
188
196
  addNodeMark(pos: number, mark: Mark): this {
189
197
  this.step(new AddNodeMarkStep(pos, mark))