@nerd-bible/wordgard 0.3.6 → 0.5.2-beta
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 +1 -1
- package/dist/command.d.ts +15 -3
- package/dist/command.js +51 -19
- package/dist/doc.d.ts +16 -13
- package/dist/doc.js +53 -58
- package/dist/editor.d.ts +13 -4
- package/dist/editor.js +441 -279
- package/dist/schema.js +1 -1
- package/dist/state.d.ts +1 -3
- package/dist/state.js +5 -15
- package/dist/table.js +4 -3
- package/dist/types.d.ts +5 -5
- package/dist/types.js +8 -8
- package/package.json +1 -1
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
337
|
-
while (parent && parent.node.
|
|
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.
|
|
412
|
-
while (parent && parent.node.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
1163
|
-
|
|
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.
|
|
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
|
|
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].
|
|
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.
|
|
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
|
|
2160
|
-
|
|
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
|
|
2975
|
+
return this.data[(this.i - 2) >> 1];
|
|
2979
2976
|
}
|
|
2980
2977
|
get slice() {
|
|
2981
|
-
return this.data
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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].
|
|
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
|
|
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;
|