@nerd-bible/wordgard 0.3.6 → 0.5.2-beta2

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Wordgard
2
2
 
3
- [ [**WEBSITE**](https://wordgard.net/) | [**DOCS**](https://wordgard.net/docs/) | [**ISSUES**](https://code.haverbeke.berlin/wordgard/wordgard/issues) | [**FORUM**](https://discuss.wordgard.net/)
3
+ [ [**WEBSITE**](https://wordgard.net/) | [**DOCS**](https://wordgard.net/docs/) | [**ISSUES**](https://code.haverbeke.berlin/wordgard/wordgard/issues) | [**FORUM**](https://discuss.wordgard.net/) ]
4
4
 
5
5
  This is the [Wordgard](https://wordgard.net) package. It implements a
6
6
  rich text editor framework for the browser.
package/dist/command.d.ts CHANGED
@@ -510,8 +510,9 @@ declare const insertLineBreak: Command.Pure;
510
510
  /**
511
511
  The command that handles enter presses. The default handler will,
512
512
  if the selection is not in an inline context, insert an empty
513
- default textblock in its position. Otherwise it first tries
514
- `liftEmptyBlock`, then `splitTextblock`.
513
+ default textblock in its position. Otherwise it first tries {@link
514
+ enterInCode}, then {@link liftEmptyBlock}, and finally {@link
515
+ splitTextblock}.
515
516
  */
516
517
  declare const enter: Command.Pure;
517
518
  /**
@@ -694,6 +695,17 @@ parent, return a transaction that does this.
694
695
  */
695
696
  declare function liftEmptyBlock(state: GardState): Transaction.Spec | false;
696
697
  /**
698
+ If the selection is in a {@link Plot.Spec.preserveWhitespace
699
+ `preserveWhitespace`} textblock, replace the selected content with
700
+ a {@link Schema.lineBreak line break}.
701
+
702
+ If the selection additionally is a cursor on an otherwise empty
703
+ line after a blank line, split or trunctate (if at end) the parent
704
+ textblock and create a blank default text block in place of the
705
+ empty line.
706
+ */
707
+ declare function enterInCode(state: GardState): Transaction.Spec | false;
708
+ /**
697
709
  Split the textblock at the cursor position, if any. If the
698
710
  textblock is the first child of a list item, also split that item,
699
711
  unless `splitListItem` is false.
@@ -793,4 +805,4 @@ updated to perform those joins.
793
805
  */
794
806
  declare function autoJoinBlocks(state: GardState, tr: Transaction.Spec): Transaction.Spec;
795
807
 
796
- export { Command, Menu, autoJoinBlocks, canAddMarkInRange, clearNonFitting, deleteBackward, deleteEmptyPlot, deleteForward, deleteLine, deleteSelection, deleteToLineEnd, deleteUnit, deleteWord, doUnwrapBlock, enter, findUnwrappable, findWrappable, insertLineBreak, insertText, joinBackward, joinBlocks, joinForward, joinListItems, liftEmptyBlock, listIsActive, moveByLine, moveByPage, moveByUnit, moveByWord, moveToDocSide, moveToLineSide, moveToTextblockSide, redo, selectAll, selectedTextblocks, setAlignment, setDirection, setTextblockType, splitTextblock, toggleBlock, toggleEmphasis, toggleList, toggleMark, toggleStrong, toggleUnderline, transposeChars, undo, unwrapBlock, wrapBlock, wrapBlockRange };
808
+ export { Command, Menu, autoJoinBlocks, canAddMarkInRange, clearNonFitting, deleteBackward, deleteEmptyPlot, deleteForward, deleteLine, deleteSelection, deleteToLineEnd, deleteUnit, deleteWord, doUnwrapBlock, enter, enterInCode, findUnwrappable, findWrappable, insertLineBreak, insertText, joinBackward, joinBlocks, joinForward, joinListItems, liftEmptyBlock, listIsActive, moveByLine, moveByPage, moveByUnit, moveByWord, moveToDocSide, moveToLineSide, moveToTextblockSide, redo, selectAll, selectedTextblocks, setAlignment, setDirection, setTextblockType, splitTextblock, toggleBlock, toggleEmphasis, toggleList, toggleMark, toggleStrong, toggleUnderline, transposeChars, undo, unwrapBlock, wrapBlock, wrapBlockRange };
package/dist/command.js CHANGED
@@ -62,7 +62,7 @@ function liftEmptyBlock(state) {
62
62
  scrollIntoView: true,
63
63
  userEvent: "unwrap.empty"
64
64
  };
65
- if (level.node.type.isInline || level.node.type.isolating)
65
+ if (level.node.isInline || level.node.type.isolating)
66
66
  break;
67
67
  if (index)
68
68
  atStart = false;
@@ -79,6 +79,35 @@ function liftEmptyBlock(state) {
79
79
  }
80
80
  return false;
81
81
  }
82
+ function enterInCode(state) {
83
+ let { sel } = state, { head } = sel;
84
+ if (!head.parent.node.inlineContent || !head.parent.node.type.preserveWhitespace ||
85
+ head.parent.start != sel.anchor.parent.start || !state.schema.lineBreak)
86
+ return false;
87
+ if (sel.selection.empty && head.parent.parent && !head.inText && head.index > 1 &&
88
+ head.nodeBefore.type == state.schema.lineBreak.type &&
89
+ (head.pos == head.parent.end || head.nodeAfter.type == state.schema.lineBreak.type) &&
90
+ head.parent.node.content[head.index - 2].type == state.schema.lineBreak.type) {
91
+ let textblock = state.schema.defaultContentPlot(head.parent.parent.node.type);
92
+ if (textblock?.type.inlineContent)
93
+ return {
94
+ changes: {
95
+ from: head.pos - 2, to: head.pos + 1,
96
+ insert: head.pos == head.parent.end ? [Plot.End, textblock, Plot.End]
97
+ : [Plot.End, textblock, Plot.End, head.parent.node.tag]
98
+ },
99
+ selection: GardSelection.cursor(head.pos, -1),
100
+ scrollIntoView: true,
101
+ userEvent: "split.textblock"
102
+ };
103
+ }
104
+ return {
105
+ changes: { from: sel.from.pos, to: sel.to.pos, insert: [state.schema.lineBreak] },
106
+ selection: GardSelection.cursor(sel.from.pos + 1, -1),
107
+ scrollIntoView: true,
108
+ userEvent: "input"
109
+ };
110
+ }
82
111
  function splitTextblock(state, splitListItem = true) {
83
112
  let { from, to } = state.sel.replacementRange, { schema } = state.doc;
84
113
  let before = from.textblockParent;
@@ -174,12 +203,12 @@ function joinBackward(state) {
174
203
  if (!scan.parent)
175
204
  return false;
176
205
  scan = scan.parent;
177
- if (scan.node.type.isolating || !scan.node.type.isBlock)
206
+ if (scan.node.type.isolating || !scan.node.isBlock)
178
207
  return false;
179
208
  }
180
209
  let before = scan.previousSibling, parent = scan.parent.node, pos = scan.start - 1;
181
210
  while (before.isLeaf || !before.isTextblock) {
182
- if (before.isLeaf || state.isAtom(before.type) || before.type.isolating || !before.type.isBlock)
211
+ if (before.isLeaf || state.isAtom(before.type) || before.type.isolating || !before.isBlock)
183
212
  return false;
184
213
  let last = before.content.length - 1;
185
214
  if (last < 0)
@@ -216,7 +245,7 @@ function joinListItems(state) {
216
245
  let next = scan.parent;
217
246
  if (!next)
218
247
  return false;
219
- if (scan.node.type.isBlock && next.node.type.hasRole(Node.Role.List)) {
248
+ if (scan.node.isBlock && next.node.type.hasRole(Node.Role.List)) {
220
249
  const prev = scan.previousSibling;
221
250
  if (!prev || !prev.isLeaf && scan.node.content.some(ch => !state.schema.canContain(prev.type, ch.type)))
222
251
  return false;
@@ -244,12 +273,12 @@ function joinForward(state) {
244
273
  if (scan.index < scan.parent.node.content.length - 1)
245
274
  break;
246
275
  scan = scan.parent;
247
- if (scan.node.type.isolating || !scan.node.type.isBlock)
276
+ if (scan.node.type.isolating || !scan.node.isBlock)
248
277
  return false;
249
278
  }
250
279
  let after = scan.nextSibling, parent = scan.parent.node, pos = scan.after;
251
280
  while (after.isLeaf || !after.isTextblock) {
252
- if (after.isLeaf || after.type.isolating || state.isAtom(after.type) || !after.type.isBlock || !after.content.length)
281
+ if (after.isLeaf || after.type.isolating || state.isAtom(after.type) || !after.isBlock || !after.content.length)
253
282
  return false;
254
283
  parent = after;
255
284
  after = after.content[0];
@@ -279,7 +308,7 @@ function deleteBackward(state, word = false) {
279
308
  let { parent: scan, index, pos } = sel.head;
280
309
  if (!sel.head.inText)
281
310
  while (!index) {
282
- if (scan.node.type.isolating || !scan.parent)
311
+ if (scan.node.type.isolating || !scan.parent || (!scan.node.contentLength && scan.node.isInline))
283
312
  return false;
284
313
  index = scan.index;
285
314
  scan = scan.parent;
@@ -333,8 +362,8 @@ function deleteBackward(state, word = false) {
333
362
  }
334
363
  let from = pos - next.length, to = pos;
335
364
  let parent = state.doc.resolve(pos).parent;
336
- if (next.type.isBlock)
337
- while (parent && parent.node.type.isBlock && parent.node.content.length == 1) {
365
+ if (next.isBlock)
366
+ while (parent && parent.node.isBlock && parent.node.content.length == 1) {
338
367
  if (!parent.parent)
339
368
  return false;
340
369
  parent = parent.parent;
@@ -354,7 +383,7 @@ function deleteForward(state, word = false) {
354
383
  let { parent: scan, index, pos } = sel.head;
355
384
  if (!sel.head.inText)
356
385
  while (index == scan.node.content.length) {
357
- if (scan.node.type.isolating || !scan.parent)
386
+ if (scan.node.type.isolating || !scan.parent || (!scan.node.contentLength && scan.node.isInline))
358
387
  return false;
359
388
  index = scan.index + 1;
360
389
  scan = scan.parent;
@@ -408,8 +437,8 @@ function deleteForward(state, word = false) {
408
437
  }
409
438
  let from = pos, to = pos + next.length;
410
439
  let parent = state.doc.resolve(pos).parent;
411
- if (next.type.isBlock)
412
- while (parent && parent.node.type.isBlock && parent.node.content.length == 1) {
440
+ if (next.isBlock)
441
+ while (parent && parent.node.isBlock && parent.node.content.length == 1) {
413
442
  if (!parent.parent)
414
443
  return false;
415
444
  parent = parent.parent;
@@ -510,7 +539,7 @@ function findUnwrappable(schema, from, to, query) {
510
539
  let outerCandidates = [];
511
540
  let { doc } = from;
512
541
  doc.iterate(fromStart, toEnd, (node, p, parent) => {
513
- if (node.type.isBlock && node.isPlot && !node.inlineContent && parent &&
542
+ if (node.isBlock && node.isPlot && !node.inlineContent && parent &&
514
543
  (fromTextblock ? doc.schema.canContain(parent.type, fromTextblock) : textblockChild(doc.schema, parent.type)) &&
515
544
  (!query || schema.matchNode(node.type, query))) {
516
545
  let pos = doc.resolveNode(p), depth = pos.depth;
@@ -675,13 +704,13 @@ function autoJoinBlocks(state, tr) {
675
704
  let cursor = doc.resolve(0), check = (pos) => {
676
705
  cursor = cursor.advance(pos - cursor.pos);
677
706
  let before = cursor.nodeBefore, after = cursor.nodeAfter;
678
- if (before && after && before.isPlot && before.type.isBlock && after.isPlot && after.type == before.type) {
707
+ if (before && after && before.isPlot && before.isBlock && after.isPlot && after.type == before.type) {
679
708
  let { autoJoin } = after.type.spec;
680
709
  if (autoJoin && (typeof autoJoin != "function" || autoJoin(before.tag, after.tag))) {
681
710
  let from = pos - 1, to = pos + 1;
682
711
  for (;;) {
683
712
  let last = before.lastChild, first = after.firstChild;
684
- if (!first || !last || first.isLeaf || last.isLeaf || first.type != last.type || first.type.isInline)
713
+ if (!first || !last || first.isLeaf || last.isLeaf || first.type != last.type || first.isInline)
685
714
  break;
686
715
  autoJoin = last.type.spec.autoJoin;
687
716
  if (!autoJoin || (typeof autoJoin == "function" && !autoJoin(last.tag, first.tag)))
@@ -757,7 +786,7 @@ const enter = ({ state }) => {
757
786
  userEvent: "insert.textblock"
758
787
  };
759
788
  }
760
- return liftEmptyBlock(state) || splitTextblock(state);
789
+ return enterInCode(state) || liftEmptyBlock(state) || splitTextblock(state);
761
790
  };
762
791
  const deleteUnit = ({ state }, dir) => {
763
792
  if (state.readOnly)
@@ -1159,8 +1188,11 @@ const moveByPage = (wg, { dir, extend }) => {
1159
1188
  return moved ? setSelection(extend ? extendSel(selection, moved) : moved) : false;
1160
1189
  };
1161
1190
  const moveToLineSide = (wg, { dir, extend }) => {
1162
- let pos = wg.moveToLineBoundary(wg.state.selection, isForward(dir, wg.state));
1163
- return pos ? setSelection(extend ? extendSel(wg.state.selection, pos) : pos) : false;
1191
+ let forward = isForward(dir, wg.state), { selection } = wg.state;
1192
+ let pos = wg.moveToLineBoundary(selection, forward);
1193
+ return pos ? setSelection(extend ? extendSel(selection, pos) : pos)
1194
+ : extend || selection.empty ? false
1195
+ : setSelection(GardSelection.near(wg.state, forward ? selection.to : selection.from));
1164
1196
  };
1165
1197
  const moveToTextblockSide = (wg, { dir, extend }) => {
1166
1198
  let { state } = wg, block = state.sel.head.textblockParent;
@@ -1444,4 +1476,4 @@ const Menu = /*@__PURE__*/(function (Menu) {
1444
1476
  Menu.resolve = resolve;
1445
1477
  ;return Menu})({});
1446
1478
 
1447
- export { Command, Menu, autoJoinBlocks, canAddMarkInRange, clearNonFitting, deleteBackward, deleteEmptyPlot, deleteForward, deleteLine, deleteSelection, deleteToLineEnd, deleteUnit, deleteWord, doUnwrapBlock, enter, findUnwrappable, findWrappable, insertLineBreak, insertText, joinBackward, joinBlocks, joinForward, joinListItems, liftEmptyBlock, listIsActive, moveByLine, moveByPage, moveByUnit, moveByWord, moveToDocSide, moveToLineSide, moveToTextblockSide, redo, selectAll, selectedTextblocks, setAlignment, setDirection, setTextblockType, splitTextblock, toggleBlock, toggleEmphasis, toggleList, toggleMark, toggleStrong, toggleUnderline, transposeChars, undo, unwrapBlock, wrapBlock, wrapBlockRange };
1479
+ export { Command, Menu, autoJoinBlocks, canAddMarkInRange, clearNonFitting, deleteBackward, deleteEmptyPlot, deleteForward, deleteLine, deleteSelection, deleteToLineEnd, deleteUnit, deleteWord, doUnwrapBlock, enter, enterInCode, findUnwrappable, findWrappable, insertLineBreak, insertText, joinBackward, joinBlocks, joinForward, joinListItems, liftEmptyBlock, listIsActive, moveByLine, moveByPage, moveByUnit, moveByWord, moveToDocSide, moveToLineSide, moveToTextblockSide, redo, selectAll, selectedTextblocks, setAlignment, setDirection, setTextblockType, splitTextblock, toggleBlock, toggleEmphasis, toggleList, toggleMark, toggleStrong, toggleUnderline, transposeChars, undo, unwrapBlock, wrapBlock, wrapBlockRange };
package/dist/doc.d.ts CHANGED
@@ -1136,6 +1136,14 @@ declare namespace Node {
1136
1136
  */
1137
1137
  isPlot: boolean;
1138
1138
  /**
1139
+ True when this node is a block node.
1140
+ */
1141
+ isBlock: boolean;
1142
+ /**
1143
+ Indicate whether this is an inline node.
1144
+ */
1145
+ isInline: boolean;
1146
+ /**
1139
1147
  Convert this node to its JSON-serializeable representation.
1140
1148
  */
1141
1149
  toJSON(): Node.JSON;
@@ -1415,6 +1423,8 @@ declare class Leaf<Param = unknown> extends BaseTag<Param> implements Node.Share
1415
1423
  get tokenType(): Token.Type.Node;
1416
1424
  get isLeaf(): true;
1417
1425
  get isPlot(): false;
1426
+ get isInline(): boolean;
1427
+ get isBlock(): boolean;
1418
1428
  get length(): number;
1419
1429
  /**
1420
1430
  Create a text node with the given text and mark set.
@@ -1469,6 +1479,9 @@ declare namespace Leaf {
1469
1479
  a given set of marks. The only leaf with a length that isn't
1470
1480
  always 1. Adjacent text leaves with the same marks are merged
1471
1481
  automatically.
1482
+
1483
+ Text leaves must not include newline or return characters. Line
1484
+ breaks in the document are modeled with {@link LineBreak} nodes.
1472
1485
  */
1473
1486
  const Text: Leaf.Type<string>;
1474
1487
  }
@@ -1518,6 +1531,8 @@ declare class Plot implements Node.Shared {
1518
1531
  get isTextblock(): boolean;
1519
1532
  get isLeaf(): false;
1520
1533
  get isPlot(): true;
1534
+ get isInline(): boolean;
1535
+ get isBlock(): boolean;
1521
1536
  /**
1522
1537
  True if this is a document node.
1523
1538
  */
@@ -1685,11 +1700,6 @@ declare namespace Plot {
1685
1700
  */
1686
1701
  readonly orientation: "row" | "column";
1687
1702
  /**
1688
- True if this is an inline plot with {@link
1689
- Plot.Spec.cursorInsideBounds `cursorInsideBounds`} set.
1690
- */
1691
- readonly cursorInsideBounds: boolean;
1692
- /**
1693
1703
  The spec used to define this plot type.
1694
1704
  */
1695
1705
  readonly spec: Plot.Spec<any>;
@@ -1818,13 +1828,6 @@ declare namespace Plot {
1818
1828
  this to true on a textblock type will prevent that behavior.
1819
1829
  */
1820
1830
  preserveOnSplitAtEnd?: boolean;
1821
- /**
1822
- For inline nodes with inline content, this determines whether
1823
- there are normalized cursor positions directly inside the node.
1824
- The default is to only have cursor positions right outside the
1825
- node.
1826
- */
1827
- cursorInsideBounds?: boolean;
1828
1831
  }
1829
1832
  /**
1830
1833
  Document plots are used as the top level plot in a document.
@@ -2017,7 +2020,7 @@ declare class ChangeSet {
2017
2020
  position of the range in the original document, `posB` the
2018
2021
  position in the changed document.
2019
2022
  */
2020
- iterGaps(gap: (fromA: number, toA: number, fromB: number, toB: number) => void, change?: (fromA: number, toA: number, fromB: number, toB: number) => void): void;
2023
+ iterGaps(gap: (fromA: number, toA: number, fromB: number, toB: number, last: boolean) => void, change?: (fromA: number, toA: number, fromB: number, toB: number) => void): void;
2021
2024
  /**
2022
2025
  Iterate over the ranges changed (either replaced or modified) by
2023
2026
  this change desc. Joins adjacent changed ranges together.
package/dist/doc.js CHANGED
@@ -13,7 +13,7 @@ class TextOutput {
13
13
  : node.type.spec.toText ? node.type.spec.toText(node)
14
14
  : this.leafText ? this.leafText(node)
15
15
  : "";
16
- if (node.isLeaf ? node.type.isBlock && nodeText : node.isTextblock)
16
+ if (node.isLeaf ? node.isBlock && nodeText : node.isTextblock)
17
17
  this.openBlock();
18
18
  if (nodeText != null) {
19
19
  this.text += nodeText;
@@ -1001,7 +1001,7 @@ class Pos {
1001
1001
  }
1002
1002
  Pos.Plot = Plot;
1003
1003
  ;return Pos})(Pos);
1004
- const posCache = /*@__PURE__*/(() => new Map())(), cacheSize = 8;
1004
+ const posCache = /*@__PURE__*/(() => new WeakMap())(), cacheSize = 8;
1005
1005
  let cachePos = 0;
1006
1006
  function cacheFor(doc) {
1007
1007
  let found = posCache.get(doc);
@@ -1184,6 +1184,8 @@ class Leaf extends BaseTag {
1184
1184
  get tokenType() { return Token.Type.Node; }
1185
1185
  get isLeaf() { return true; }
1186
1186
  get isPlot() { return false; }
1187
+ get isInline() { return this.type.isInline; }
1188
+ get isBlock() { return this.type.isBlock; }
1187
1189
  get length() { return this.is(Leaf.Text) ? this.param.length : 1; }
1188
1190
  pushTo(nodes) {
1189
1191
  if (this.is(Leaf.Text)) {
@@ -1291,6 +1293,8 @@ class Plot {
1291
1293
  get isTextblock() { return this.type.isTextblock; }
1292
1294
  get isLeaf() { return false; }
1293
1295
  get isPlot() { return true; }
1296
+ get isInline() { return this.type.isInline; }
1297
+ get isBlock() { return this.type.isBlock; }
1294
1298
  get isDoc() { return this.type.isDoc; }
1295
1299
  get firstChild() {
1296
1300
  return this.content.length ? this.content[0] : null;
@@ -1429,7 +1433,6 @@ class Plot {
1429
1433
  neutral;
1430
1434
  preserveWhitespace;
1431
1435
  orientation;
1432
- cursorInsideBounds;
1433
1436
  spec;
1434
1437
  constructor(name, flags, spec) {
1435
1438
  super(name, flags, spec, NodeShape.from(name, false, spec.shape));
@@ -1441,7 +1444,6 @@ class Plot {
1441
1444
  this.neutral = spec.neutral ?? !this.defining;
1442
1445
  this.preserveWhitespace = spec.preserveWhitespace ?? !!this.hasRole(Node.Role.Code);
1443
1446
  this.orientation = flags & 2 ? "row" : spec.orientation || "column";
1444
- this.cursorInsideBounds = !!((flags & 1) && spec.cursorInsideBounds);
1445
1447
  this.default = "defaultParam" in spec ? Plot.Tag.new(this, spec.defaultParam, none) :
1446
1448
  (flags & 16) ? Plot.Tag.new(this, null, none) : null;
1447
1449
  if (!this.shape.atom && this.isInline && !this.inlineContent)
@@ -1558,7 +1560,7 @@ function sliceContent(out, content, from, to) {
1558
1560
  }
1559
1561
  }
1560
1562
  function joinText(nodes) {
1561
- if (!nodes.length || nodes[0].type.isBlock)
1563
+ if (!nodes.length || nodes[0].isBlock)
1562
1564
  return nodes;
1563
1565
  let joined;
1564
1566
  for (let i = 0, last = null; i < nodes.length; i++) {
@@ -1629,7 +1631,7 @@ class Schema {
1629
1631
  if (!node.type.canBeEmpty && node.content.length == 0)
1630
1632
  throw new ValidationError(`Node ${node.name} with block content may not be empty`);
1631
1633
  for (let ch of node.content) {
1632
- if (!this.canContain(node.type, ch.type) || node.inlineContent != ch.type.isInline)
1634
+ if (!this.canContain(node.type, ch.type) || node.inlineContent != ch.isInline)
1633
1635
  throw new ValidationError(`Node type ${node.name} cannot contain child ${ch.name}`);
1634
1636
  this.validate(ch);
1635
1637
  }
@@ -2156,8 +2158,43 @@ class ChangeSet {
2156
2158
  return fix ? set.compose(fix) : set;
2157
2159
  }
2158
2160
  compose(other) {
2159
- let { sections, data } = compose(this.sections, other.sections, this.data, other.data);
2160
- return new ChangeSet(sections, data);
2161
+ let sectionsA = this.sections, dataA = this.data;
2162
+ let sectionsB = other.sections, dataB = other.data;
2163
+ let sections = [], data = [];
2164
+ let a = new SectionIter(sectionsA, dataA), b = new SectionIter(sectionsB, dataB);
2165
+ for (let open = false;;) {
2166
+ if (a.done && b.done) {
2167
+ return new ChangeSet(sections, data);
2168
+ }
2169
+ else if (a.ins == 0) { addSection(sections, data, a.len, 0, a.slice, open);
2170
+ a.next();
2171
+ }
2172
+ else if (b.len == 0 && !b.done) { addSection(sections, data, 0, b.ins, b.slice, open);
2173
+ b.next();
2174
+ }
2175
+ else if (a.done || b.done) {
2176
+ throw new ValidationError("Mismatched change set lengths");
2177
+ }
2178
+ else {
2179
+ let len = Math.min(a.len2, b.len), sectionLen = sections.length;
2180
+ if (a.keep && b.keep) {
2181
+ let mods = combineMods(a.mods, b.mods);
2182
+ addSection(sections, data, len, mods ? -2 : -1, mods, open);
2183
+ }
2184
+ else if (a.keep) {
2185
+ addSection(sections, data, len, b.off ? 0 : b.ins, b.off ? Slice.empty : b.slice, open);
2186
+ }
2187
+ else if (b.keep) {
2188
+ addSection(sections, data, a.off ? 0 : a.len, len, applyModsToSlice(a.slicePart(len), b.mods), open);
2189
+ }
2190
+ else {
2191
+ addSection(sections, data, a.off ? 0 : a.len, b.off ? 0 : b.ins, b.off ? Slice.empty : b.slice, open);
2192
+ }
2193
+ open = (a.ins > len || b.ins >= 0 && b.len > len) && (open || sections.length > sectionLen);
2194
+ a.forward2(len);
2195
+ b.forward(len);
2196
+ }
2197
+ }
2161
2198
  }
2162
2199
  invert(doc) {
2163
2200
  let sections = [], data = [];
@@ -2276,7 +2313,7 @@ class ChangeSet {
2276
2313
  len += this.sections[i];
2277
2314
  i += 2;
2278
2315
  }
2279
- gap(posA, posA + len, posB, posB + len);
2316
+ gap(posA, posA + len, posB, posB + len, i == this.sections.length);
2280
2317
  posB += len;
2281
2318
  }
2282
2319
  else {
@@ -2378,9 +2415,6 @@ class ChangeSet {
2378
2415
  }
2379
2416
  return result;
2380
2417
  }
2381
- static composeSections(a, b) {
2382
- return compose(a, b).sections;
2383
- }
2384
2418
  static transform(doc, a, b) {
2385
2419
  let { set: mA, fix } = transform(a, b, doc, true, true);
2386
2420
  let mB = transform(b, a, doc, false, false).set;
@@ -2579,43 +2613,6 @@ function transform(setA, setB, doc, before, fit) {
2579
2613
  }
2580
2614
  }
2581
2615
  }
2582
- function compose(sectionsA, sectionsB, dataA, dataB) {
2583
- let sections = [], data = dataA ? [] : null;
2584
- let a = new SectionIter(sectionsA, dataA), b = new SectionIter(sectionsB, dataB);
2585
- for (let open = false;;) {
2586
- if (a.done && b.done) {
2587
- return { sections, data };
2588
- }
2589
- else if (a.ins == 0) { addSection(sections, data, a.len, 0, a.slice, open);
2590
- a.next();
2591
- }
2592
- else if (b.len == 0 && !b.done) { addSection(sections, data, 0, b.ins, b.slice, open);
2593
- b.next();
2594
- }
2595
- else if (a.done || b.done) {
2596
- throw new ValidationError("Mismatched change set lengths");
2597
- }
2598
- else {
2599
- let len = Math.min(a.len2, b.len), sectionLen = sections.length;
2600
- if (a.keep && b.keep) {
2601
- let mods = combineMods(a.mods, b.mods);
2602
- addSection(sections, data, len, (data ? mods : a.ins == -2 || b.ins == -2) ? -2 : -1, mods, open);
2603
- }
2604
- else if (a.keep) {
2605
- addSection(sections, data, len, b.off ? 0 : b.ins, b.off ? Slice.empty : b.slice, open);
2606
- }
2607
- else if (b.keep) {
2608
- addSection(sections, data, a.off ? 0 : a.len, len, data ? applyModsToSlice(a.slicePart(len), b.mods) : null, open);
2609
- }
2610
- else {
2611
- addSection(sections, data, a.off ? 0 : a.len, b.off ? 0 : b.ins, b.off ? Slice.empty : b.slice, open);
2612
- }
2613
- open = (a.ins > len || b.ins >= 0 && b.len > len) && (open || sections.length > sectionLen);
2614
- a.forward2(len);
2615
- b.forward(len);
2616
- }
2617
- }
2618
- }
2619
2616
  function combineMods(a, b) {
2620
2617
  return !a ? b : !b ? a : a.concat(b);
2621
2618
  }
@@ -2975,10 +2972,10 @@ class SectionIter {
2975
2972
  get done() { return this.ins == -3; }
2976
2973
  get len2() { return this.ins < 0 ? this.len : this.ins; }
2977
2974
  get mods() {
2978
- return this.data ? this.data[(this.i - 2) >> 1] : null;
2975
+ return this.data[(this.i - 2) >> 1];
2979
2976
  }
2980
2977
  get slice() {
2981
- return this.data ? this.data[(this.i - 2) >> 1] : Slice.empty;
2978
+ return this.data[(this.i - 2) >> 1];
2982
2979
  }
2983
2980
  slicePart(len) {
2984
2981
  return this.slice.slice(this.off, len == null ? undefined : this.off + len);
@@ -3007,7 +3004,7 @@ function addSection(sections, data, len, ins, value, forceJoin = false) {
3007
3004
  return;
3008
3005
  let last = sections.length - 2;
3009
3006
  if (last >= 0 && ins <= 0 && ins == sections[last + 1]) {
3010
- let lastValue = data ? data[data.length - 1] : null;
3007
+ let lastValue = data[data.length - 1];
3011
3008
  let match = ins == 0 ? true
3012
3009
  : value ? lastValue && compareModifications(lastValue, value)
3013
3010
  : !lastValue;
@@ -3019,13 +3016,11 @@ function addSection(sections, data, len, ins, value, forceJoin = false) {
3019
3016
  if (forceJoin || last >= 0 && len == 0 && sections[last] == 0) {
3020
3017
  sections[last] += len;
3021
3018
  sections[last + 1] += ins;
3022
- if (data)
3023
- data[data.length - 1] = data[data.length - 1].concat(value);
3019
+ data[data.length - 1] = data[data.length - 1].concat(value);
3024
3020
  }
3025
3021
  else {
3026
3022
  sections.push(len, ins);
3027
- if (data)
3028
- data.push(value);
3023
+ data.push(value);
3029
3024
  }
3030
3025
  }
3031
3026
  function finishCx(cx, schema) {
@@ -3072,7 +3067,7 @@ function splatContext(top, cx) {
3072
3067
  top.push(ch);
3073
3068
  }
3074
3069
  function isFitBarrier(plot) {
3075
- return plot.isolating || plot.cursorInsideBounds;
3070
+ return plot.isolating || plot.isInline;
3076
3071
  }
3077
3072
  function fitReplacement(doc, from, to, slice, context) {
3078
3073
  if (!slice.length)
@@ -3360,7 +3355,7 @@ class ParseContext {
3360
3355
  else if (!match || match.rule.ignore === "skip") {
3361
3356
  let sync, top = this.top;
3362
3357
  if (blockTags.has(name)) {
3363
- if (top.children.length && top.children[0].type.isInline && top.parent)
3358
+ if (top.children.length && top.children[0].isInline && top.parent)
3364
3359
  this.close();
3365
3360
  sync = true;
3366
3361
  }
package/dist/editor.d.ts CHANGED
@@ -351,11 +351,13 @@ declare class PointSet<T extends PointSet.Value = PointSet.Value> {
351
351
  set with the adjusted points. May delete points when the content
352
352
  around them was deleted.
353
353
  */
354
- map(changes: ChangeSet): PointSet<T>;
354
+ map(changes: ChangeSet, start?: number): PointSet<T>;
355
355
  /**
356
- Returns the union of this set and the given set.
356
+ Returns the union of this set and the given set. If
357
+ `maskFrom`/`maskTo` are given, drop any points from `this`
358
+ between or at those positions.
357
359
  */
358
- merge(other: PointSet<T>): PointSet<T>;
360
+ merge(other: PointSet<T>, maskFrom?: number, maskTo?: number | undefined): PointSet<T>;
359
361
  /**
360
362
  Get the value at the given position, if any. If there's multiple
361
363
  values at that position, the one with the lowest side is
@@ -421,7 +423,13 @@ declare class RangeSet<T extends RangeSet.Value = RangeSet.Value> {
421
423
  Adjust the positions of the ranges for the given change set.
422
424
  Returns a set with the updated ranges.
423
425
  */
424
- map(changes: ChangeSet): RangeSet<T>;
426
+ map(changes: ChangeSet, start?: number): RangeSet<T>;
427
+ /**
428
+ Merge this set with another set. If `maskFrom`/`maskTo` are
429
+ given, any ranges overlapping the masked range in `this` are
430
+ not included in the merged set.
431
+ */
432
+ merge(other: RangeSet<T>, maskFrom?: number, maskTo?: number | undefined): RangeSet<T>;
425
433
  /**
426
434
  Create a range set from an iterable of `[from, to, value]`
427
435
  tuples, or a function that calls its argument for every range to
@@ -512,6 +520,7 @@ declare abstract class Tile {
512
520
  get boundary(): 0 | 1;
513
521
  get firstChild(): Tile | null;
514
522
  get lastChild(): Tile | null;
523
+ get nodeParent(): Tile;
515
524
  ignoreEvent(event: Event): boolean;
516
525
  get ignoreMutations(): boolean;
517
526
  toString(): string;