@voithos-labs/aragonite 0.10.2 → 0.10.3
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 +6 -21
- package/THIRD-PARTY-NOTICES.md +25 -0
- package/dist/a11y-strings.d.ts +18 -0
- package/dist/a11y-strings.js +18 -0
- package/dist/action-contracts.d.ts +7 -1
- package/dist/ambient/ambient-dom.js +5 -1
- package/dist/block-component.d.ts +14 -0
- package/dist/components/BlockDragHandle.svelte +35 -24
- package/dist/components/BlockHost.svelte +16 -9
- package/dist/components/Editor.svelte +381 -198
- package/dist/components/Editor.svelte.d.ts +1 -1
- package/dist/components/SelectionOverlay.svelte +17 -3
- package/dist/components/TailInsert.svelte +107 -0
- package/dist/components/TailInsert.svelte.d.ts +17 -0
- package/dist/components/block-content-selector.d.ts +6 -2
- package/dist/components/block-content-selector.js +6 -2
- package/dist/components/blocks/ThematicBreakBlock.svelte +19 -6
- package/dist/components/blocks/ThematicBreakBlock.svelte.d.ts +1 -0
- package/dist/components/blocks/code/CodeBlock.svelte +211 -30
- package/dist/components/blocks/code/CodeBlockRail.svelte +686 -0
- package/dist/components/blocks/code/CodeBlockRail.svelte.d.ts +26 -0
- package/dist/components/blocks/code/code-bootstrap.js +4 -0
- package/dist/components/blocks/code/code-context-actions.d.ts +1 -0
- package/dist/components/blocks/code/code-context-actions.js +24 -0
- package/dist/components/blocks/code/code-fence-exit.d.ts +15 -0
- package/dist/components/blocks/code/code-fence-exit.js +26 -0
- package/dist/components/blocks/code/code-languages.d.ts +6 -0
- package/dist/components/blocks/code/code-languages.js +11 -0
- package/dist/components/blocks/code/code-renderer.js +11 -0
- package/dist/components/blocks/directive/DirectiveContainerBlock.svelte +1 -1
- package/dist/components/blocks/editable-leaf.d.ts +37 -6
- package/dist/components/blocks/editable-leaf.js +243 -29
- package/dist/components/blocks/editable-surface.d.ts +9 -0
- package/dist/components/blocks/editable-surface.js +22 -3
- package/dist/components/blocks/list/ListItemBlock.svelte +3 -3
- package/dist/components/blocks/list/task-checkbox.d.ts +2 -0
- package/dist/components/blocks/list/task-checkbox.js +11 -2
- package/dist/components/blocks/surface-wiring.svelte.d.ts +4 -0
- package/dist/components/blocks/surface-wiring.svelte.js +8 -1
- package/dist/components/blocks/table/TableActionMenu.svelte +210 -86
- package/dist/components/blocks/table/TableActionMenu.svelte.d.ts +5 -0
- package/dist/components/blocks/table/TableBlock.svelte +205 -186
- package/dist/components/blocks/table/TableBlock.svelte.d.ts +1 -0
- package/dist/components/blocks/table/TableCellBlock.svelte +87 -22
- package/dist/components/blocks/table/TableRowBlock.svelte +4 -18
- package/dist/components/blocks/table/TableRowBlock.svelte.d.ts +0 -5
- package/dist/components/blocks/table/cell-clipboard.d.ts +10 -0
- package/dist/components/blocks/table/cell-clipboard.js +34 -1
- package/dist/components/blocks/table/cell-keydown-plan.d.ts +1 -1
- package/dist/components/blocks/table/cell-keydown-plan.js +3 -1
- package/dist/components/blocks/table/table-cell-paste.js +2 -1
- package/dist/components/blocks/table/table-menu-model.d.ts +35 -8
- package/dist/components/blocks/table/table-menu-model.js +36 -17
- package/dist/components/blocks/text/TextEditableBlock.svelte +58 -11
- package/dist/components/blocks/text/delimiter-autopair.d.ts +71 -0
- package/dist/components/blocks/text/delimiter-autopair.js +216 -0
- package/dist/components/blocks/text/edge-policy-dispatch.d.ts +4 -0
- package/dist/components/blocks/text/edge-policy-dispatch.js +56 -2
- package/dist/components/blocks/text/live-selection-edit.js +27 -0
- package/dist/components/blocks/text/text-keydown.d.ts +7 -1
- package/dist/components/blocks/text/text-keydown.js +11 -1
- package/dist/components/blocks/text/text-render.d.ts +1 -1
- package/dist/components/blocks/text/text-render.js +3 -1
- package/dist/components/blocks/text/widget-interaction.d.ts +3 -0
- package/dist/components/blocks/text/widget-interaction.js +144 -22
- package/dist/components/drag-handle.d.ts +35 -0
- package/dist/components/drag-handle.js +126 -0
- package/dist/components/editor-root-focus.d.ts +19 -0
- package/dist/components/editor-root-focus.js +67 -0
- package/dist/components/editor-root-geometry.d.ts +39 -0
- package/dist/components/editor-root-geometry.js +91 -0
- package/dist/components/editor-root-keydown.d.ts +1 -1
- package/dist/components/editor-root-keydown.js +12 -3
- package/dist/components/editor-root-listeners.d.ts +10 -6
- package/dist/components/editor-root-listeners.js +23 -22
- package/dist/components/editor-root-mode-flip.d.ts +36 -0
- package/dist/components/editor-root-mode-flip.js +92 -0
- package/dist/components/image/ImageOverlayHost.svelte +14 -6
- package/dist/components/image/ImageProperties.svelte +512 -70
- package/dist/components/image/ImageProperties.svelte.d.ts +6 -1
- package/dist/components/image/ImageResizeHandles.svelte +59 -34
- package/dist/components/image/image-crop.d.ts +39 -0
- package/dist/components/image/image-crop.js +74 -0
- package/dist/components/image/image-edit-commit.d.ts +1 -0
- package/dist/components/image/image-edit-commit.js +21 -5
- package/dist/components/image/image-source-bytes.js +10 -3
- package/dist/components/image/image-widget-editing.js +1 -0
- package/dist/components/image/widget-dom.js +5 -1
- package/dist/components/link-card/link-card-commit.js +1 -1
- package/dist/components/lrd-map-gate.js +1 -1
- package/dist/components/menu/BlockMenu.svelte +315 -0
- package/dist/components/menu/BlockMenu.svelte.d.ts +34 -0
- package/dist/components/menu/MenuIcon.svelte +153 -0
- package/dist/components/menu/MenuIcon.svelte.d.ts +51 -0
- package/dist/components/menu/clipboard-actions.d.ts +12 -0
- package/dist/components/menu/clipboard-actions.js +42 -0
- package/dist/components/menu/default-context-actions.d.ts +15 -0
- package/dist/components/menu/default-context-actions.js +76 -0
- package/dist/components/menu/flyout-placement.d.ts +6 -0
- package/dist/components/menu/flyout-placement.js +25 -0
- package/dist/core/inline/format-toggle.d.ts +12 -4
- package/dist/core/inline/format-toggle.js +94 -40
- package/dist/core/inline/image-dimensions.d.ts +3 -0
- package/dist/core/inline/image-dimensions.js +46 -10
- package/dist/core/inline/inline-widgets.d.ts +11 -0
- package/dist/core/inline/scan/brackets.js +1 -0
- package/dist/core/inline/scan/plugin-syntax.d.ts +8 -0
- package/dist/core/inline/scan/plugin-syntax.js +15 -1
- package/dist/core/inline-render.d.ts +6 -0
- package/dist/core/inline-render.js +32 -0
- package/dist/core/nodes.d.ts +14 -0
- package/dist/cursor/edge-affinity.js +2 -1
- package/dist/cursor/overlay-remeasure.js +8 -0
- package/dist/cursor/reveal-source.js +7 -2
- package/dist/cursor/widget-offset.d.ts +6 -0
- package/dist/cursor/widget-offset.js +61 -4
- package/dist/debug/interaction-trace.d.ts +4 -0
- package/dist/debug/interaction-trace.js +15 -0
- package/dist/decorations/decoration-state.svelte.js +1 -1
- package/dist/editor-actions/ancestry-folds.d.ts +2 -2
- package/dist/editor-actions/ancestry-folds.js +1 -1
- package/dist/editor-actions/block-edit-scope.js +1 -1
- package/dist/editor-actions/commit/text-batch.d.ts +3 -2
- package/dist/editor-actions/commit/text-batch.js +1 -1
- package/dist/editor-actions/commit/undo-controller.js +4 -2
- package/dist/editor-actions/container-edit.js +2 -1
- package/dist/editor-actions/enter-completion.d.ts +2 -0
- package/dist/editor-actions/enter-completion.js +23 -2
- package/dist/editor-actions/inline-range-commit.js +1 -1
- package/dist/editor-actions/reorder-action.js +19 -10
- package/dist/editor-actions/reorder-drag.js +29 -1
- package/dist/editor-actions/replacement-focus.d.ts +1 -1
- package/dist/editor-actions/replacement-focus.js +1 -1
- package/dist/editor-actions/search-replace.js +1 -1
- package/dist/editor-actions/table-context.d.ts +4 -1
- package/dist/editor-actions/table-context.js +57 -1
- package/dist/editor-events.d.ts +3 -0
- package/dist/editor-keys.d.ts +33 -0
- package/dist/editor-props.d.ts +21 -12
- package/dist/index.d.ts +1 -1
- package/dist/plugin.d.ts +6 -0
- package/dist/plugin.js +11 -0
- package/dist/plugins/latex/BlockMath.svelte +264 -29
- package/dist/plugins/latex/BlockMath.svelte.d.ts +2 -0
- package/dist/plugins/latex/index.d.ts +2 -1
- package/dist/plugins/latex/latex-kind.js +36 -3
- package/dist/plugins/latex/math-completion.js +4 -1
- package/dist/plugins/latex/math-layout.d.ts +15 -0
- package/dist/plugins/latex/math-layout.js +13 -0
- package/dist/plugins/latex/math-source.d.ts +19 -0
- package/dist/plugins/latex/math-source.js +97 -0
- package/dist/plugins/latex/register.d.ts +11 -2
- package/dist/plugins/latex/register.js +3 -1
- package/dist/plugins/latex/renderer.d.ts +3 -3
- package/dist/plugins/latex/renderer.js +13 -6
- package/dist/reactivity/list-windowing.svelte.d.ts +8 -8
- package/dist/reactivity/list-windowing.svelte.js +49 -24
- package/dist/schema/block-completions.d.ts +8 -0
- package/dist/schema/block-completions.js +11 -0
- package/dist/schema/context-actions.d.ts +31 -0
- package/dist/schema/context-actions.js +23 -0
- package/dist/schema/fenced-code-raw.js +31 -1
- package/dist/schema/operations.d.ts +8 -1
- package/dist/schema/reserved-chords.js +25 -4
- package/dist/schema/table-cell-raw.d.ts +1 -1
- package/dist/schema/table-cell-raw.js +1 -1
- package/dist/selection/block-hit-test.js +3 -2
- package/dist/selection/char-endpoint-snap.js +1 -1
- package/dist/selection/clipboard-text.js +6 -1
- package/dist/selection/covered-block.d.ts +10 -0
- package/dist/selection/covered-block.js +24 -0
- package/dist/selection/cross-block/dispatch.d.ts +3 -0
- package/dist/selection/cross-block/dispatch.js +13 -1
- package/dist/selection/cross-block/format-range.d.ts +1 -1
- package/dist/selection/cross-block/format-range.js +4 -15
- package/dist/selection/cross-block/format-toggle.js +1 -1
- package/dist/selection/cross-block/keydown.js +1 -1
- package/dist/selection/cross-block/ops.js +1 -1
- package/dist/selection/cross-block/paste.js +32 -30
- package/dist/selection/cross-block/type-replace.d.ts +3 -2
- package/dist/selection/cross-block/type-replace.js +62 -13
- package/dist/selection/dead-space-caret.d.ts +10 -0
- package/dist/selection/dead-space-caret.js +47 -1
- package/dist/selection/double-click-trim.d.ts +17 -0
- package/dist/selection/double-click-trim.js +57 -0
- package/dist/selection/drag-pointer.d.ts +7 -2
- package/dist/selection/drag-pointer.js +61 -2
- package/dist/selection/gap-caret.js +1 -1
- package/dist/selection/keyboard-extend.js +1 -1
- package/dist/selection/path-lookup.js +1 -1
- package/dist/selection/range-delete-ceremony.js +4 -2
- package/dist/selection/range-delete-chrome.js +2 -1
- package/dist/selection/range-delete-table-coverage.js +2 -1
- package/dist/selection/range-delete-table.js +3 -2
- package/dist/selection/range-delete.js +30 -2
- package/dist/selection/selection-restore.js +1 -1
- package/dist/selection/selection-state.svelte.d.ts +6 -0
- package/dist/selection/selection-state.svelte.js +36 -1
- package/dist/selection/table-endpoint-snap.js +1 -1
- package/dist/selection/table-rect-extend.js +1 -1
- package/dist/styles/editor-theme.css +57 -28
- package/dist/styles/editor.css +217 -18
- package/dist/testing/container-conformance.js +2 -2
- package/dist/testing/inline-conformance.js +2 -1
- package/dist/tree-operations/blockquote.js +1 -1
- package/dist/tree-operations/chain-rebuild.d.ts +63 -0
- package/dist/tree-operations/chain-rebuild.js +142 -0
- package/dist/tree-operations/children.d.ts +1 -1
- package/dist/tree-operations/children.js +1 -1
- package/dist/tree-operations/cleanup.js +1 -1
- package/dist/tree-operations/content-write.d.ts +50 -0
- package/dist/tree-operations/content-write.js +263 -0
- package/dist/tree-operations/index.d.ts +8 -3
- package/dist/tree-operations/index.js +6 -2
- package/dist/tree-operations/list/exit-replacement.js +1 -1
- package/dist/tree-operations/list/unwrap-merge.js +3 -3
- package/dist/tree-operations/node-ops.d.ts +17 -234
- package/dist/tree-operations/node-ops.js +47 -1113
- package/dist/tree-operations/node-primitives.d.ts +75 -0
- package/dist/tree-operations/node-primitives.js +117 -0
- package/dist/tree-operations/paste/apply.js +1 -1
- package/dist/tree-operations/paste/body-write.d.ts +1 -1
- package/dist/tree-operations/paste/body-write.js +2 -2
- package/dist/tree-operations/paste/container-match.js +4 -2
- package/dist/tree-operations/paste/dispatch.js +2 -1
- package/dist/tree-operations/paste/find-enclosing-list.js +1 -1
- package/dist/tree-operations/paste/focus-target.d.ts +1 -1
- package/dist/tree-operations/paste/list-absorb.js +1 -1
- package/dist/tree-operations/paste/list-break-out.js +1 -1
- package/dist/tree-operations/paste/parent-scope.js +1 -1
- package/dist/tree-operations/paste/paste-replacement.js +1 -1
- package/dist/tree-operations/paste/replace-block-at-parent.js +1 -1
- package/dist/tree-operations/path-mutate.d.ts +1 -1
- package/dist/tree-operations/path-mutate.js +2 -1
- package/dist/tree-operations/reorder-unit.js +1 -1
- package/dist/tree-operations/reorder.d.ts +5 -2
- package/dist/tree-operations/reorder.js +55 -2
- package/dist/tree-operations/settle.d.ts +105 -0
- package/dist/tree-operations/settle.js +660 -0
- package/dist/tree-operations/table-grid-clipboard.d.ts +21 -0
- package/dist/tree-operations/table-grid-clipboard.js +90 -0
- package/dist/tree-operations/unshare.d.ts +15 -77
- package/dist/tree-operations/unshare.js +15 -166
- package/docs/guide/consumer-guide.md +126 -96
- package/docs/guide/plugin-api.md +31 -3
- package/docs/guide/plugin-guide.md +26 -3
- package/package.json +4 -2
- package/dist/components/blocks/code/CodeLanguageChip.svelte +0 -127
- package/dist/components/blocks/code/CodeLanguageChip.svelte.d.ts +0 -14
- package/dist/components/blocks/table/TableGrip.svelte +0 -91
- package/dist/components/blocks/table/TableGrip.svelte.d.ts +0 -8
- package/dist/components/blocks/table/table-drop-target.d.ts +0 -1
- package/dist/components/blocks/table/table-drop-target.js +0 -16
- package/dist/components/blocks/table/table-reorder-drag.d.ts +0 -78
- package/dist/components/blocks/table/table-reorder-drag.js +0 -97
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The content write: every byte that enters a node crosses `updateNodeContent`, the sole
|
|
3
|
+
* re-parse transfer funnel (editor.md § 6, § 8), and the container twin that re-derives a
|
|
4
|
+
* container's kind from its rebuilt raw.
|
|
5
|
+
*/
|
|
6
|
+
import { isBlankParagraph, parse } from '../core/parser';
|
|
7
|
+
import { escalatedFenceLength, matchFenceOpen } from '../core/parsers/fence-syntax';
|
|
8
|
+
import { isBlockOpenerRegistered } from '../schema/block-openers';
|
|
9
|
+
import { trailingLineEnding } from '../core/lines';
|
|
10
|
+
import { assignChildIdsDeep } from '../block-id';
|
|
11
|
+
import { perfEnabled, recordContainerKindReparse } from '../perf/instruments';
|
|
12
|
+
import { getBlockKindDescriptor, tryGetBlockKindDescriptor } from '../schema/block-kind-descriptor';
|
|
13
|
+
import { resyncChildIds } from './children';
|
|
14
|
+
import { spliceMany } from './splice-many';
|
|
15
|
+
import { replacePreservingFirst } from './structural-change';
|
|
16
|
+
import { NEXT_PROSE_LINE, ensureEditableContainers, forBody, writeOwnRaw } from './node-primitives';
|
|
17
|
+
import { absorbWindowSeams, focusTargetInReplacement, releaseWrapPeel, restoreSeparatorAfterBlank, restoreSeparatorOnFill, settleSeparatorOnBlank, widenForTailMint } from './settle';
|
|
18
|
+
/**
|
|
19
|
+
* Update raw and re-parse. A kind change mints the reparsed block into the slot rather than
|
|
20
|
+
* reassigning `kind` in place, and multi-block text mints every parsed block; only a same-kind
|
|
21
|
+
* single-block edit writes in place, so routine typing keeps the node's object identity.
|
|
22
|
+
*/
|
|
23
|
+
export function updateNodeContent(parent, blockIndex, text, grammar, sharing) {
|
|
24
|
+
const wasBlank = isBlankParagraph(parent.children[blockIndex]);
|
|
25
|
+
const change = writeParsedContent(parent, blockIndex, text, grammar);
|
|
26
|
+
const lastWritten = lastMintedIndex(change, blockIndex);
|
|
27
|
+
// One blank line served BOTH sides: it separated this block from the one above and stood in
|
|
28
|
+
// as the separator of the block beneath it. Ending it owes each their own.
|
|
29
|
+
if (wasBlank && !isBlankParagraph(parent.children[blockIndex])) {
|
|
30
|
+
restoreSeparatorOnFill(parent, blockIndex, sharing);
|
|
31
|
+
restoreSeparatorAfterBlank(parent, followerIndexAfter(change, blockIndex), sharing);
|
|
32
|
+
releaseWrapPeel(parent, lastWritten);
|
|
33
|
+
return settleWriteSeams(parent, blockIndex, lastWritten, change, sharing);
|
|
34
|
+
}
|
|
35
|
+
// The reverse transition: the block IS the separating line now, so the run it joins gives
|
|
36
|
+
// back the second one. The last block minted is the one that meets the follower.
|
|
37
|
+
if (!wasBlank && isBlankParagraph(parent.children[lastWritten])) {
|
|
38
|
+
const settled = parent.children.length;
|
|
39
|
+
settleSeparatorOnBlank(parent, lastWritten, sharing);
|
|
40
|
+
const widened = widenForTailMint(change, settled, parent.children.length);
|
|
41
|
+
return settleWriteSeams(parent, blockIndex, lastWritten, widened, sharing);
|
|
42
|
+
}
|
|
43
|
+
// Same-kind typing INSIDE content must never pay a neighbour reparse.
|
|
44
|
+
if (change.op === 'noop')
|
|
45
|
+
return { change, textStart: 0 };
|
|
46
|
+
return settleWriteSeams(parent, blockIndex, lastWritten, change, sharing);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Ask every join the write disturbed and report where the written text ended up: an absorb
|
|
50
|
+
* ABOVE leaves the predecessor standing, so its bytes now sit in front of the text.
|
|
51
|
+
*/
|
|
52
|
+
function settleWriteSeams(parent, blockIndex, lastWritten, change, sharing) {
|
|
53
|
+
const tracked = { index: blockIndex, offset: 0 };
|
|
54
|
+
const settled = absorbWindowSeams(parent, blockIndex, lastWritten - blockIndex + 1, blockIndex, change, sharing, tracked);
|
|
55
|
+
return {
|
|
56
|
+
change: settled.change,
|
|
57
|
+
textStart: textOffsetInWindow(parent.children, settled.change, tracked)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The tracked position as an offset in the settled window's committed text, the space every
|
|
62
|
+
* caret door measures in, where the head block's own leading trivia is outside the window.
|
|
63
|
+
*/
|
|
64
|
+
function textOffsetInWindow(children, change, tracked) {
|
|
65
|
+
const at = change.op === 'noop' ? tracked.index : change.at;
|
|
66
|
+
let pos = 0;
|
|
67
|
+
for (let i = at; i < tracked.index; i++) {
|
|
68
|
+
pos += (i === at ? 0 : children[i].leadingTrivia.length) + children[i].raw.length;
|
|
69
|
+
}
|
|
70
|
+
const ownTrivia = tracked.index > at ? children[tracked.index].leadingTrivia.length : 0;
|
|
71
|
+
return pos + ownTrivia + tracked.offset;
|
|
72
|
+
}
|
|
73
|
+
/** Where the filled block's follower ended up: a multi-block reparse pushes it down. */
|
|
74
|
+
function followerIndexAfter(change, blockIndex) {
|
|
75
|
+
return change.op === 'replace' ? change.at + change.newCount : blockIndex + 1;
|
|
76
|
+
}
|
|
77
|
+
/** The last block the write left in the slot: a multi-block reparse mints past the first. */
|
|
78
|
+
function lastMintedIndex(change, blockIndex) {
|
|
79
|
+
return change.op === 'replace' ? change.at + change.newCount - 1 : blockIndex;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The written bytes parsed, with the construct they leave OPEN closed off first: an unterminated
|
|
83
|
+
* construct reads every block below it as its body at the next parse, and the seam settle would
|
|
84
|
+
* converge the live tree to exactly that reading.
|
|
85
|
+
*/
|
|
86
|
+
function closeWrittenConstruct(parent, blockIndex, text, oldKind, grammar) {
|
|
87
|
+
// Fragment scope: this is one block's bytes, whatever its position, so a position-scoped
|
|
88
|
+
// kind must not mint here.
|
|
89
|
+
const parsed = parse(text, { grammar, scope: 'fragment' });
|
|
90
|
+
if (blockIndex + 1 >= parent.children.length)
|
|
91
|
+
return { text, parsed };
|
|
92
|
+
if (parsed.children.length === 1 && parsed.children[0].kind === oldKind)
|
|
93
|
+
return { text, parsed };
|
|
94
|
+
const terminator = openConstructTerminator(text, parsed.children, grammar);
|
|
95
|
+
if (!terminator)
|
|
96
|
+
return { text, parsed };
|
|
97
|
+
const closed = text + terminator;
|
|
98
|
+
return { text: closed, parsed: parse(closed, { grammar, scope: 'fragment' }) };
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The terminator written bytes owe when their last construct absorbs to EOF, asked of the grammar
|
|
102
|
+
* rather than a kind list. Null when the bytes terminate themselves, or when no fence opener
|
|
103
|
+
* explains the absorb (the one family whose closer its opener determines).
|
|
104
|
+
*/
|
|
105
|
+
function openConstructTerminator(text, blocks, grammar) {
|
|
106
|
+
// The terminator is a line of its own, so bytes whose last line is still open have none to
|
|
107
|
+
// append to (G4.20's unterminated tail slice, which absorbs nothing while it stands alone).
|
|
108
|
+
if (!text.endsWith('\n') || blocks.length === 0)
|
|
109
|
+
return null;
|
|
110
|
+
const ending = trailingLineEnding(text);
|
|
111
|
+
const probe = parse(text + ending + NEXT_PROSE_LINE + ending, { grammar, scope: 'fragment' });
|
|
112
|
+
if (probe.children.length !== blocks.length)
|
|
113
|
+
return null;
|
|
114
|
+
const raw = blocks[blocks.length - 1].raw;
|
|
115
|
+
const nl = raw.indexOf('\n');
|
|
116
|
+
const opener = matchFenceOpen(nl < 0 ? raw : raw.slice(0, nl));
|
|
117
|
+
if (!opener)
|
|
118
|
+
return null;
|
|
119
|
+
const body = nl < 0 ? '' : raw.slice(nl + 1);
|
|
120
|
+
const run = escalatedFenceLength(body, opener.marker, opener.length);
|
|
121
|
+
return opener.indent + opener.marker.repeat(run) + ending;
|
|
122
|
+
}
|
|
123
|
+
function writeParsedContent(parent, blockIndex, text, grammar) {
|
|
124
|
+
const node = parent.children[blockIndex];
|
|
125
|
+
const oldKind = node.kind;
|
|
126
|
+
const oldDescriptor = getBlockKindDescriptor(oldKind);
|
|
127
|
+
// Ahead of every reparse below, so a write lands on the kind its committed bytes
|
|
128
|
+
// describe, not the kind the pre-escape text would parse to (`bodyWrite`).
|
|
129
|
+
const bodyText = forBody(parent, text);
|
|
130
|
+
// A context-dependent kind has no standalone recognizer, so reparsing would downgrade it:
|
|
131
|
+
// keep the kind and write raw through its own legality pass.
|
|
132
|
+
if (oldDescriptor.contextDependentKind) {
|
|
133
|
+
writeOwnRaw(node, bodyText, grammar);
|
|
134
|
+
return { op: 'noop' };
|
|
135
|
+
}
|
|
136
|
+
const { text: newText, parsed: reparsed } = closeWrittenConstruct(parent, blockIndex, bodyText, oldKind, grammar);
|
|
137
|
+
const parsed = reparsed.children;
|
|
138
|
+
const first = parsed[0];
|
|
139
|
+
// A marker-consuming container (a GitHub alert) needs its raw rebuilt from the backfilled
|
|
140
|
+
// body, or G1.1 stale-raw fires.
|
|
141
|
+
const firstBackfilled = !!first && isEmptyEditableContainer(first);
|
|
142
|
+
if (first)
|
|
143
|
+
ensureEditableContainers(first);
|
|
144
|
+
// Text-leading blanks fold into the first block's raw (the single-block shape); the
|
|
145
|
+
// rest keep their own trivia.
|
|
146
|
+
if (parsed.length > 1) {
|
|
147
|
+
const rest = parsed.slice(1);
|
|
148
|
+
for (const sibling of rest)
|
|
149
|
+
ensureEditableContainers(sibling);
|
|
150
|
+
first.raw = first.leadingTrivia + first.raw;
|
|
151
|
+
first.leadingTrivia = node.leadingTrivia;
|
|
152
|
+
if (firstBackfilled)
|
|
153
|
+
reconcileBackfilledRaw(first);
|
|
154
|
+
// The peeled line has no follower inside the splice, so it stays in raw.
|
|
155
|
+
rest[rest.length - 1].raw += reparsed.suffix;
|
|
156
|
+
spliceMany(parent.children, blockIndex, 1, parsed);
|
|
157
|
+
return replacePreservingFirst(blockIndex, 1, parsed.length);
|
|
158
|
+
}
|
|
159
|
+
const newKind = first?.kind ?? 'paragraph';
|
|
160
|
+
// In-place refresh keeps the node's object identity: component, IME state, and inline
|
|
161
|
+
// cache are all keyed on it.
|
|
162
|
+
if (newKind === oldKind) {
|
|
163
|
+
node.raw = newText;
|
|
164
|
+
adoptReparsedFields(node, first);
|
|
165
|
+
if (firstBackfilled)
|
|
166
|
+
reconcileBackfilledRaw(node);
|
|
167
|
+
return { op: 'noop' };
|
|
168
|
+
}
|
|
169
|
+
const replacement = first ?? { kind: 'paragraph', leadingTrivia: '', raw: newText };
|
|
170
|
+
replacement.raw = newText;
|
|
171
|
+
replacement.leadingTrivia = node.leadingTrivia;
|
|
172
|
+
if (firstBackfilled)
|
|
173
|
+
reconcileBackfilledRaw(replacement);
|
|
174
|
+
parent.children.splice(blockIndex, 1, replacement);
|
|
175
|
+
return replacePreservingFirst(blockIndex, 1, 1);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Refresh a node in place from its own reparse, keeping its object identity. The id resync is
|
|
179
|
+
* unconditional: the reparse can change the child count while the caller reports `noop`.
|
|
180
|
+
*/
|
|
181
|
+
export function adoptReparsedFields(target, parsed) {
|
|
182
|
+
target.metadata = parsed?.metadata;
|
|
183
|
+
target.children = parsed?.children;
|
|
184
|
+
resyncChildIds(target);
|
|
185
|
+
assignChildIdsDeep(target);
|
|
186
|
+
target.innerPrefix = parsed?.innerPrefix;
|
|
187
|
+
target.innerSuffix = parsed?.innerSuffix;
|
|
188
|
+
}
|
|
189
|
+
/** A container `ensureEditableContainers` will backfill; read BEFORE the backfill runs. */
|
|
190
|
+
function isEmptyEditableContainer(node) {
|
|
191
|
+
const d = getBlockKindDescriptor(node.kind);
|
|
192
|
+
return d.isContainer && d.blockFocus !== 'whole-block' && (node.children?.length ?? 0) === 0;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Sync a just-backfilled container's `raw` to its synthesized body. Needed only for a
|
|
196
|
+
* marker-consuming container whose typed raw lacks a blank body line (`> [!TYPE]`).
|
|
197
|
+
*/
|
|
198
|
+
function reconcileBackfilledRaw(node) {
|
|
199
|
+
getBlockKindDescriptor(node.kind).rebuildRaw?.(node);
|
|
200
|
+
}
|
|
201
|
+
// ── Container kind re-derivation ──
|
|
202
|
+
/**
|
|
203
|
+
* What the grammar opens `line` as, read in isolation: asked of the opener registry and never
|
|
204
|
+
* a kind list, so a kind registered later is covered the day it registers.
|
|
205
|
+
*/
|
|
206
|
+
export function lineOpensAs(line, grammar) {
|
|
207
|
+
return parse(`${line}\n`, { grammar, scope: 'fragment' }).children[0]?.kind ?? 'paragraph';
|
|
208
|
+
}
|
|
209
|
+
/** Whether the ambient grammar still leaves `NEXT_PROSE_LINE` an ordinary paragraph. */
|
|
210
|
+
export function probeLineOpensAsProse(grammar) {
|
|
211
|
+
return lineOpensAs(NEXT_PROSE_LINE, grammar) === 'paragraph';
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Re-derive the container at `index` from its own (already rebuilt) raw, replacing it in the
|
|
215
|
+
* slot when that raw now opens as a different kind (editor.md § 8). Eligibility is the opener
|
|
216
|
+
* registry: registering an opener is exactly the claim that `parse(raw)` reproduces the kind.
|
|
217
|
+
*/
|
|
218
|
+
export function reclassifyContainer(parent, index, grammar) {
|
|
219
|
+
const node = parent.children[index];
|
|
220
|
+
if (!node)
|
|
221
|
+
return null;
|
|
222
|
+
const descriptor = tryGetBlockKindDescriptor(node.kind);
|
|
223
|
+
if (!descriptor?.isContainer || !isBlockOpenerRegistered(node.kind))
|
|
224
|
+
return null;
|
|
225
|
+
if (perfEnabled())
|
|
226
|
+
recordContainerKindReparse();
|
|
227
|
+
const parsed = parse(node.raw, { grammar, scope: 'fragment' }).children;
|
|
228
|
+
// A container's raw is one block by construction; a multi-block reparse means bytes
|
|
229
|
+
// this seam has no slot for, left to the gesture that owns the mutation.
|
|
230
|
+
if (parsed.length !== 1 || parsed[0].kind === node.kind)
|
|
231
|
+
return null;
|
|
232
|
+
const replacement = parsed[0];
|
|
233
|
+
const backfilled = isEmptyEditableContainer(replacement);
|
|
234
|
+
ensureEditableContainers(replacement);
|
|
235
|
+
// The slot's trivia is authoritative, so restore the bytes before overwriting it or
|
|
236
|
+
// anything the parse split off the front vanishes with it.
|
|
237
|
+
replacement.raw = node.raw;
|
|
238
|
+
replacement.leadingTrivia = node.leadingTrivia;
|
|
239
|
+
if (backfilled)
|
|
240
|
+
reconcileBackfilledRaw(replacement);
|
|
241
|
+
// A freshly-parsed node carries no childIds and this swap publishes under the slot's
|
|
242
|
+
// reused component instance, so undefined keys would reach the nested keyed `{#each}`.
|
|
243
|
+
assignChildIdsDeep(replacement);
|
|
244
|
+
parent.children[index] = replacement;
|
|
245
|
+
// Write-then-re-read (tree-operations/unshare.ts header).
|
|
246
|
+
return parent.children[index];
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Where a caret at `offset` in the written text lands once {@link updateNodeContent}'s folds
|
|
250
|
+
* settled: an absorb ABOVE leaves the predecessor holding the bytes, so the slot the gesture
|
|
251
|
+
* named is gone and the offset carries what that predecessor put in front of it.
|
|
252
|
+
*/
|
|
253
|
+
export function settledCaretTarget(settled, at, offset, children) {
|
|
254
|
+
const { change, textStart } = settled;
|
|
255
|
+
if (change.op !== 'replace')
|
|
256
|
+
return { index: at, offset };
|
|
257
|
+
const shifted = offset + textStart;
|
|
258
|
+
if (change.newCount <= 1)
|
|
259
|
+
return { index: change.at, offset: shifted };
|
|
260
|
+
const blocks = children.slice(change.at, change.at + change.newCount);
|
|
261
|
+
const target = focusTargetInReplacement(blocks, shifted);
|
|
262
|
+
return { index: change.at + target.index, offset: target.offset };
|
|
263
|
+
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
export type { NodeParent
|
|
2
|
-
export {
|
|
1
|
+
export type { NodeParent } from './node-primitives';
|
|
2
|
+
export { nodeAt, emptyParagraph, paragraphNode, ensureEditableContainers, normalizeReplacementTrivia } from './node-primitives';
|
|
3
|
+
export { deleteNode, focusTargetInReplacement, restoreSeparatorOnFill, dropDoubledSeparator } from './settle';
|
|
4
|
+
export { updateNodeContent, reclassifyContainer } from './content-write';
|
|
5
|
+
export type { MergeIntoPrevResult, MergeResult, SplitResult } from './node-ops';
|
|
6
|
+
export { splitNode, assertSplitLanding, assertSingleNodeSink, mergeWithNext, mergeIntoPrevDeepLeaf } from './node-ops';
|
|
3
7
|
export { unwrapFirstItemFromList, mergeListItemIntoPrevious } from './list/unwrap-merge';
|
|
4
8
|
export { renumberOrderedList, normalizeItemMarkerToList } from './list/ordered-markers';
|
|
5
9
|
export { isItemUserEmpty } from './list/empty-check';
|
|
@@ -11,6 +15,7 @@ export { insertEmptyRow, insertEmptyColumn, deleteRow, deleteColumn, cycleAlignm
|
|
|
11
15
|
export { copyRectangleAsSubTable } from './sub-table-copy';
|
|
12
16
|
export type { CellPos } from './sub-table-copy';
|
|
13
17
|
export { cascadeCleanupEmptyAncestors } from './cleanup';
|
|
14
|
-
export { ensureUnsharedPath, ensureUnsharedChild, ensureUnsharedNode, ensureUnsharedChildren, ensureUnsharedSubtree, rebuildOwnedContainer
|
|
18
|
+
export { ensureUnsharedPath, ensureUnsharedChild, ensureUnsharedNode, ensureUnsharedChildren, ensureUnsharedSubtree, rebuildOwnedContainer } from './unshare';
|
|
19
|
+
export { rebuildUnsharedChain, rebuildUnsharedAncestry } from './chain-rebuild';
|
|
15
20
|
export { buildPastedReplacement } from './paste/paste-replacement';
|
|
16
21
|
export { cloneDocument, cloneNode } from './clone';
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { nodeAt, emptyParagraph, paragraphNode, ensureEditableContainers, normalizeReplacementTrivia } from './node-primitives';
|
|
2
|
+
export { deleteNode, focusTargetInReplacement, restoreSeparatorOnFill, dropDoubledSeparator } from './settle';
|
|
3
|
+
export { updateNodeContent, reclassifyContainer } from './content-write';
|
|
4
|
+
export { splitNode, assertSplitLanding, assertSingleNodeSink, mergeWithNext, mergeIntoPrevDeepLeaf } from './node-ops';
|
|
2
5
|
export { unwrapFirstItemFromList, mergeListItemIntoPrevious } from './list/unwrap-merge';
|
|
3
6
|
export { renumberOrderedList, normalizeItemMarkerToList } from './list/ordered-markers';
|
|
4
7
|
export { isItemUserEmpty } from './list/empty-check';
|
|
@@ -9,6 +12,7 @@ export { liftFirstChildKeepingContainer } from './container-lift';
|
|
|
9
12
|
export { insertEmptyRow, insertEmptyColumn, deleteRow, deleteColumn, cycleAlignment } from './table-mutations';
|
|
10
13
|
export { copyRectangleAsSubTable } from './sub-table-copy';
|
|
11
14
|
export { cascadeCleanupEmptyAncestors } from './cleanup';
|
|
12
|
-
export { ensureUnsharedPath, ensureUnsharedChild, ensureUnsharedNode, ensureUnsharedChildren, ensureUnsharedSubtree, rebuildOwnedContainer
|
|
15
|
+
export { ensureUnsharedPath, ensureUnsharedChild, ensureUnsharedNode, ensureUnsharedChildren, ensureUnsharedSubtree, rebuildOwnedContainer } from './unshare';
|
|
16
|
+
export { rebuildUnsharedChain, rebuildUnsharedAncestry } from './chain-rebuild';
|
|
13
17
|
export { buildPastedReplacement } from './paste/paste-replacement';
|
|
14
18
|
export { cloneDocument, cloneNode } from './clone';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { metadataOf } from '../../core/nodes';
|
|
2
2
|
import { trailingLineEnding } from '../../core/lines';
|
|
3
3
|
import { cloneNode } from '../clone';
|
|
4
|
-
import { emptyParagraph } from '../node-
|
|
4
|
+
import { emptyParagraph } from '../node-primitives';
|
|
5
5
|
import { assembleListHalf } from './list-builders';
|
|
6
6
|
import { partitionItemChildren } from './item-partition';
|
|
7
7
|
import { orderedBaseOf } from './ordered-markers';
|
|
@@ -110,20 +110,20 @@ function relocateRemainingChildren(list, targetPath, targetItem, currentItem, li
|
|
|
110
110
|
for (let i = 0; i < child.children.length; i++) {
|
|
111
111
|
const item = sharing ? ensureUnsharedChild(child, i, sharing) : child.children[i];
|
|
112
112
|
item.leadingTrivia = '';
|
|
113
|
-
// discovered-descendant mutation, see node-
|
|
113
|
+
// discovered-descendant mutation, see node-primitives.ts header
|
|
114
114
|
pushChild(depthOneList, item);
|
|
115
115
|
}
|
|
116
116
|
rebuildListRaw(depthOneList);
|
|
117
117
|
continue;
|
|
118
118
|
}
|
|
119
|
-
// discovered-descendant mutation, see node-
|
|
119
|
+
// discovered-descendant mutation, see node-primitives.ts header
|
|
120
120
|
pushChild(targetItem, child);
|
|
121
121
|
}
|
|
122
122
|
else {
|
|
123
123
|
// A trailing paragraph keeps its blank-line separator, or the two lazy-continue
|
|
124
124
|
// into one on reload. Other leaves start fresh and need none.
|
|
125
125
|
child.leadingTrivia = child.kind === 'paragraph' ? lineEnding : '';
|
|
126
|
-
// discovered-descendant mutation, see node-
|
|
126
|
+
// discovered-descendant mutation, see node-primitives.ts header
|
|
127
127
|
pushChild(targetItem, child);
|
|
128
128
|
}
|
|
129
129
|
}
|
|
@@ -1,132 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* overwritten. A descendant found by walking the live tree is the exception: mutate it in place on
|
|
6
|
-
* a caller-unshared spine (`unshare.ts`), a STRUCTURAL one via `commitMultiScope`.
|
|
2
|
+
* The split and merge primitives, the two ops that re-tile a body's blocks around a caret, plus
|
|
3
|
+
* the join cleanup every destructive join crosses (live-mode.md § 4.5). They mutate and report;
|
|
4
|
+
* the settle around them is the ceremony's (`settle.ts`).
|
|
7
5
|
*/
|
|
8
|
-
import
|
|
9
|
-
import type {
|
|
10
|
-
import {
|
|
6
|
+
import { type CstNode } from '../core/nodes';
|
|
7
|
+
import type { NodeView } from '../core/node-views';
|
|
8
|
+
import type { GrammarView } from '../schema/block-openers';
|
|
11
9
|
import { type CleanedJoin, type InlineResolverRef, type JoinSeam } from '../schema/inline-construct-policy';
|
|
12
10
|
import type { PresentationMode } from '../presentation-mode';
|
|
13
11
|
import type { SharingState } from './sharing';
|
|
14
12
|
import { type StructuralChange } from './structural-change';
|
|
15
|
-
|
|
16
|
-
export type NodeParent = {
|
|
17
|
-
children: CstNode[];
|
|
18
|
-
};
|
|
19
|
-
/**
|
|
20
|
-
* A {@link NodeParent} that has answered which container owns it — the byte sinks need that
|
|
21
|
-
* answer for the owner's `bodyWrite` grammar and its wrap slots. Both nullable rather than
|
|
22
|
-
* optional so every byte-writing site answers: `undefined` is a real answer (the document
|
|
23
|
-
* root), skipping the question is a compile error.
|
|
24
|
-
*/
|
|
25
|
-
export type BodyParent = NodeParent & {
|
|
26
|
-
ownerKind: AnyBlockKind | undefined;
|
|
27
|
-
owner: CstNode | undefined;
|
|
28
|
-
/**
|
|
29
|
-
* The document root's foldable trailing line, when these children are its.
|
|
30
|
-
* Optional, not nullable: only a ceremony-backed site may carry it, because the settle
|
|
31
|
-
* that consumes it appends a block — the out-of-ceremony write paths stay slotless so a
|
|
32
|
-
* structural materialization is unrepresentable there.
|
|
33
|
-
*/
|
|
34
|
-
suffix?: string;
|
|
35
|
-
};
|
|
36
|
-
/**
|
|
37
|
-
* What the byte sinks accept. A whole `Document` is admitted because it IS the answer
|
|
38
|
-
* (the root owns no body grammar); a bare `{ children }` literal still cannot compile.
|
|
39
|
-
*/
|
|
40
|
-
export type BodyParentArg = BodyParent | Document;
|
|
41
|
-
/**
|
|
42
|
-
* What the separator settles accept: anything that can answer where the body starts, whether
|
|
43
|
-
* it holds the owner's kind directly (the container node, the Document) or the sink's answer
|
|
44
|
-
* to it ({@link BodyParentArg}, which this absorbs). Wider than the byte sinks: a settle
|
|
45
|
-
* writes a line ending, not body text.
|
|
46
|
-
*/
|
|
47
|
-
export type SeparatorParent = {
|
|
48
|
-
kind?: string;
|
|
49
|
-
ownerKind?: AnyBlockKind;
|
|
50
|
-
suffix?: string;
|
|
51
|
-
children?: CstNode[];
|
|
52
|
-
owner?: CstNode;
|
|
53
|
-
};
|
|
54
|
-
/**
|
|
55
|
-
* Text made legal as a child's raw inside a container of kind `ownerKind`. Exported for
|
|
56
|
-
* sinks that build their own bytes rather than handing text to {@link updateNodeContent}.
|
|
57
|
-
*/
|
|
58
|
-
export declare function normalizeBodyWrite(ownerKind: AnyBlockKind | undefined, raw: string): string;
|
|
59
|
-
/**
|
|
60
|
-
* `raw` made legal as `node`'s OWN bytes — {@link normalizeBodyWrite}'s node-side twin, for the
|
|
61
|
-
* kind's own grammar rather than its container's. Sinks that REPLACE the node with a reparse of
|
|
62
|
-
* the result call this door: the reparse re-derives metadata from the bytes, so structure the
|
|
63
|
-
* rule restores from the old metadata (a fence closer a truncation consumed) must land first.
|
|
64
|
-
*/
|
|
65
|
-
export declare function normalizeOwnRaw(node: NodeView, raw: string): string;
|
|
66
|
-
/**
|
|
67
|
-
* Write `raw` as `node`'s OWN bytes through its kind's rule — {@link normalizeOwnRaw}'s in-place
|
|
68
|
-
* sink. Every sink writing a leaf's bytes without the kind's surface in front of it owes one of
|
|
69
|
-
* the two (pinned by `lint/leaf-raw-write-rule`).
|
|
70
|
-
*/
|
|
71
|
-
export declare function writeOwnRaw(node: CstNode, raw: string, grammar: GrammarView | undefined): void;
|
|
72
|
-
/**
|
|
73
|
-
* The paragraph mint. Every argument is required: a paragraph's raw ENDS in a line ending,
|
|
74
|
-
* so a mint site must answer which document it lands in (G4.20) rather than strand a lone
|
|
75
|
-
* LF in a CRLF file. Returns a fresh node every call — a shared instance would alias across
|
|
76
|
-
* tree positions and corrupt the snapshot/unshare model (G1.9).
|
|
77
|
-
*/
|
|
78
|
-
export declare function paragraphNode(leadingTrivia: string, text: string, lineEnding: string): CstNode;
|
|
79
|
-
/** The empty-paragraph placeholder keeping an emptied document or container caret-addressable. */
|
|
80
|
-
export declare function emptyParagraph(leadingTrivia: string, lineEnding: string): CstNode;
|
|
81
|
-
export declare function nodeAt(doc: Document, path: number[]): CstNode | Document | null;
|
|
82
|
-
export declare function nodeAt(doc: DocumentView, path: number[]): NodeView | DocumentView | null;
|
|
83
|
-
/** `nodeAt` pre-narrowed through `isBlockNode`: null at the document root or on no match. */
|
|
84
|
-
export declare function blockNodeAt(doc: Document, path: number[]): CstNode | null;
|
|
85
|
-
export declare function blockNodeAt(doc: DocumentView, path: number[]): NodeView | null;
|
|
86
|
-
/**
|
|
87
|
-
* Narrow a `nodeAt` result to `CstNode`. Structural, not kind-based: a plugin may mint
|
|
88
|
-
* `'document'` as a block kind, so only the absence of `raw` discriminates.
|
|
89
|
-
*/
|
|
90
|
-
export declare function isBlockNode(node: CstNode | Document): node is CstNode;
|
|
91
|
-
export declare function isBlockNode(node: NodeView | DocumentView): node is NodeView;
|
|
13
|
+
import { type BodyParentArg, type NodeParent } from './node-primitives';
|
|
92
14
|
/** What a split leaves the caret: the structural splice, plus where the second half's head
|
|
93
|
-
* landed
|
|
15
|
+
* landed, past `blockIndex + 1` when the first half reparsed to several blocks. */
|
|
94
16
|
export interface SplitResult {
|
|
95
17
|
change: StructuralChange;
|
|
96
18
|
secondHalfIndex: number;
|
|
97
19
|
}
|
|
98
20
|
/**
|
|
99
|
-
* The landing a site is about to consume, held to the primitive's answer (G1.34)
|
|
100
|
-
*
|
|
101
|
-
* on their way to using it; a re-derived `blockIndex + 1` warns instead of shipping.
|
|
21
|
+
* The landing a site is about to consume, held to the primitive's answer (G1.34): a re-derived
|
|
22
|
+
* `blockIndex + 1` warns instead of shipping.
|
|
102
23
|
*/
|
|
103
24
|
export declare function assertSplitLanding(split: SplitResult, landing: number): void;
|
|
104
25
|
/**
|
|
105
26
|
* What a one-slot sink is about to put in its slot, held to one node (G1.35). Asked at the WRITE
|
|
106
|
-
* with the nodes being written, so the guard answers for sink N+1
|
|
107
|
-
* siblings make, or splices a plural replacement into a slot that holds one.
|
|
27
|
+
* with the nodes being written, so the guard answers for sink N+1.
|
|
108
28
|
*/
|
|
109
29
|
export declare function assertSingleNodeSink(sink: string, installed: readonly CstNode[]): void;
|
|
110
30
|
/**
|
|
111
31
|
* Split the node at `blockIndex` at raw `offset` (display-relative). The first half inherits the
|
|
112
|
-
* original ID and the whole structural suffix (a setext underline)
|
|
113
|
-
*
|
|
114
|
-
* ({@link separatorSplitsOffNextLine}) — without it GFM lazy continuation folds the halves back
|
|
115
|
-
* into one block on reload.
|
|
32
|
+
* original ID and the whole structural suffix (a setext underline); the second half opens with a
|
|
33
|
+
* blank separator wherever one does structural work ({@link separatorSplitsOffNextLine}).
|
|
116
34
|
*/
|
|
117
35
|
export declare function splitNode(parent: BodyParentArg, blockIndex: number, offset: number, sharing: SharingState | undefined, presentationMode: PresentationMode | undefined, linkRef: InlineResolverRef | undefined): SplitResult;
|
|
118
36
|
/**
|
|
119
|
-
*
|
|
120
|
-
* line, so the predicate answers for the worst case rather than one construct. Openers are
|
|
121
|
-
* arbitrary code, so no line is unclaimable by construction — {@link probeLineOpensAsProse}
|
|
122
|
-
* is the runtime check.
|
|
123
|
-
*/
|
|
124
|
-
export declare const NEXT_PROSE_LINE = "x";
|
|
125
|
-
/** Whether the ambient grammar still leaves {@link NEXT_PROSE_LINE} an ordinary paragraph. */
|
|
126
|
-
export declare function probeLineOpensAsProse(grammar?: GrammarView): boolean;
|
|
127
|
-
/**
|
|
128
|
-
* `join.mergedRaw` with the delimiter runs the join orphaned at its seam dropped — live only,
|
|
129
|
-
* where those runs are unpainted and a literal concatenation surfaces bytes the reader never saw
|
|
37
|
+
* `join.mergedRaw` with the delimiter runs the join orphaned at its seam dropped, live only
|
|
130
38
|
* (live-mode.md § 4.5). The one registered cleaner verifies its own bytes and otherwise declines,
|
|
131
39
|
* leaving the literal join every other mode gets. Every destructive join crosses this door.
|
|
132
40
|
*/
|
|
@@ -134,7 +42,7 @@ export declare function cleanJoinedRaw(join: JoinSeam, presentationMode: Present
|
|
|
134
42
|
/**
|
|
135
43
|
* The bytes a single-block edit leaves when it deletes `range` out of `display`. A delete-then-
|
|
136
44
|
* insert is a join like any other, so it crosses the same cleanup, and the returned offset is
|
|
137
|
-
* where the two sides now meet
|
|
45
|
+
* where the two sides now meet.
|
|
138
46
|
*/
|
|
139
47
|
export declare function cutRangeFromDisplay(node: NodeView, display: string, range: {
|
|
140
48
|
start: number;
|
|
@@ -144,7 +52,7 @@ export declare function cutRangeFromDisplay(node: NodeView, display: string, ran
|
|
|
144
52
|
offset: number;
|
|
145
53
|
};
|
|
146
54
|
/** What a join leaves the caret: the structural splice, plus where the two blocks met in the
|
|
147
|
-
* survivor's bytes
|
|
55
|
+
* survivor's bytes, which a seam cleanup moves when it drops a run on the first block's side. */
|
|
148
56
|
export interface MergeResult {
|
|
149
57
|
change: StructuralChange;
|
|
150
58
|
joinOffset: number;
|
|
@@ -160,8 +68,7 @@ export interface MergeIntoPrevResult {
|
|
|
160
68
|
}
|
|
161
69
|
/**
|
|
162
70
|
* Merge `curr` into the deepest prose leaf of `prev`, writing into that leaf rather than
|
|
163
|
-
* reparsing concatenated raw
|
|
164
|
-
* leaves' inline caches. Pass `sharing` to unshare everything the merge writes. Null when
|
|
71
|
+
* reparsing concatenated raw, which preserves prev's component identity and IME state. Null when
|
|
165
72
|
* no mergeable leaf exists, so the caller can fall back to move-focus.
|
|
166
73
|
*/
|
|
167
74
|
export declare function mergeIntoPrevDeepLeaf(parent: BodyParentArg, blockIndex: number, sharing: SharingState | undefined, presentationMode: PresentationMode | undefined, linkRef: InlineResolverRef | undefined, grammar?: GrammarView): MergeIntoPrevResult | null;
|
|
@@ -170,127 +77,3 @@ export declare function mergeIntoPrevDeepLeaf(parent: BodyParentArg, blockIndex:
|
|
|
170
77
|
* merged block inherits the current block's ID. Noop at the tail.
|
|
171
78
|
*/
|
|
172
79
|
export declare function mergeWithNext(parent: NodeParent, blockIndex: number, presentationMode: PresentationMode | undefined, linkRef: InlineResolverRef | undefined): MergeResult;
|
|
173
|
-
/**
|
|
174
|
-
* Settle the separator at `index`: nothing above it needs one at the body head or below a blank
|
|
175
|
-
* block, where the parser would read the extra line as one more empty paragraph. Every splice
|
|
176
|
-
* that changes what precedes a block settles through this family (syntax-tree.md § Blank lines).
|
|
177
|
-
* `sharing` owns the write (G1.9).
|
|
178
|
-
*/
|
|
179
|
-
export declare function clearRedundantSeparator(parent: SeparatorParent, index: number, sharing?: SharingState): void;
|
|
180
|
-
/**
|
|
181
|
-
* A blank block IS a blank line, so it and its follower share ONE separator: two of them reload
|
|
182
|
-
* as a second empty paragraph (G2.13). The follower's is the one that stands, so a later fill of
|
|
183
|
-
* this slot (a paste over a cut range) still finds the follower separated. The run-level twin
|
|
184
|
-
* {@link settleSeparatorOnBlank} keeps the first already-standing line instead, the same bytes
|
|
185
|
-
* the other way round.
|
|
186
|
-
*/
|
|
187
|
-
export declare function dropDoubledSeparator(parent: SeparatorParent, index: number, sharing?: SharingState): void;
|
|
188
|
-
/**
|
|
189
|
-
* The separator a block takes back when it stops being blank: its own blank line was what stood
|
|
190
|
-
* between it and a non-blank predecessor. Call at the fill — {@link clearRedundantSeparator}
|
|
191
|
-
* frees a separator in exactly the cases this declines, and a block that was never blank keeps
|
|
192
|
-
* whatever its bytes earned (a paragraph under a heading needs none).
|
|
193
|
-
*/
|
|
194
|
-
export declare function restoreSeparatorOnFill(parent: SeparatorParent, index: number, sharing?: SharingState): void;
|
|
195
|
-
/**
|
|
196
|
-
* The settle a block turning INTO a blank line owes: it joins the blank run around it, and a run
|
|
197
|
-
* carries exactly ONE separating line — across every block in it AND its follower, since a blank
|
|
198
|
-
* block is the follower's line too. The line already standing is the one kept, wherever in the
|
|
199
|
-
* run it sits; a mint lands at the run's head, the only slot one may take.
|
|
200
|
-
*/
|
|
201
|
-
export declare function settleSeparatorOnBlank(parent: SeparatorParent, index: number, sharing?: SharingState): void;
|
|
202
|
-
/**
|
|
203
|
-
* The commit ceremony's settle door: derive the spliced window from the change and settle it
|
|
204
|
-
* against `before`, the pre-mutate children the ceremony still holds. Nodes surviving inside the
|
|
205
|
-
* window are not removals, so a coarse descriptor over an in-place write settles as one.
|
|
206
|
-
* `tracked` is the committing door's caret, carried through the folds. Returns the change widened
|
|
207
|
-
* by a tail line the settle materialized.
|
|
208
|
-
*/
|
|
209
|
-
export declare function settleSeparator(parent: SeparatorParent, before: readonly CstNode[], change: StructuralChange, sharing?: SharingState, tracked?: TrackedPosition): StructuralChange;
|
|
210
|
-
/**
|
|
211
|
-
* The out-of-commit-scope twin of {@link settleSeparator}, for a container discovered by walking
|
|
212
|
-
* the live tree: it splices through the `childIds` door and reads the pre-splice span itself.
|
|
213
|
-
*/
|
|
214
|
-
export declare function spliceChildrenSettled(parent: CstNode | Document, at: number, removeCount: number, replacement: CstNode[], sharing?: SharingState): void;
|
|
215
|
-
/**
|
|
216
|
-
* A byte position the folds carry with them: the slot it lives in, and its offset inside that
|
|
217
|
-
* slot's raw. Written in place, since each fold re-tiles the bytes under it.
|
|
218
|
-
*/
|
|
219
|
-
export interface TrackedPosition {
|
|
220
|
-
index: number;
|
|
221
|
-
offset: number;
|
|
222
|
-
}
|
|
223
|
-
/** What a splice settled: its change widened by every fold, and where a tracked index landed. */
|
|
224
|
-
export interface SettledSplice {
|
|
225
|
-
change: StructuralChange;
|
|
226
|
-
landing: number;
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* The seam question at every join the splice at `at` disturbed — its window's two edges and the
|
|
230
|
-
* joins inside it — since a move can invalidate a join that was already correct. Each
|
|
231
|
-
* fold cascades downward, so the next seam is asked past what that one ate. `tracked` rides the
|
|
232
|
-
* folds for a caller placing a caret in bytes an absorb can move. `headProbe` names the one block
|
|
233
|
-
* whose bytes changed, letting each ask decline on its first line alone; dropped once anything
|
|
234
|
-
* folds, since the index it names has moved by then.
|
|
235
|
-
*/
|
|
236
|
-
export declare function absorbWindowSeams(parent: NodeParent, at: number, added: number, landing: number, change: StructuralChange, sharing?: SharingState, tracked?: TrackedPosition, headProbe?: number, onBeforeSplice?: () => void): SettledSplice;
|
|
237
|
-
/** The materialized tail reported inside the sink's one contiguous window. */
|
|
238
|
-
export declare function widenForTailMint(change: StructuralChange, before: number, after: number): StructuralChange;
|
|
239
|
-
/**
|
|
240
|
-
* Remove the node at `blockIndex`, leaving the next sibling separated from its new predecessor
|
|
241
|
-
* and no more. Takes {@link BodyParentArg} because the settle can hand a freed line to the
|
|
242
|
-
* owner's wrap slots. Pass `sharing` to unshare the nodes written — the successor's trivia is
|
|
243
|
-
* the op's only in-place write.
|
|
244
|
-
*/
|
|
245
|
-
export declare function deleteNode(parent: BodyParentArg, blockIndex: number, sharing?: SharingState): StructuralChange;
|
|
246
|
-
/** What a content write settled: its change widened by every fold, and the offset the written
|
|
247
|
-
* text starts at inside that change's window — nonzero once a fold above absorbed it. */
|
|
248
|
-
export interface SettledContent {
|
|
249
|
-
change: StructuralChange;
|
|
250
|
-
textStart: number;
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* Update raw and re-parse. The sole re-parse transfer funnel: a kind change mints the reparsed
|
|
254
|
-
* block into the slot rather than reassigning `kind` in place, and multi-block text mints every
|
|
255
|
-
* parsed block. Only a same-kind single-block edit writes fields in place, so routine typing keeps
|
|
256
|
-
* the node's object identity; `replacePreservingFirst` carries the id/ref across a mint. `sharing`
|
|
257
|
-
* owns the separator settle's writes, which land on the run's OTHER blocks.
|
|
258
|
-
*/
|
|
259
|
-
export declare function updateNodeContent(parent: BodyParentArg, blockIndex: number, text: string, grammar?: GrammarView, sharing?: SharingState): SettledContent;
|
|
260
|
-
/**
|
|
261
|
-
* What the grammar opens `line` as, read in isolation — the opener-significance test, asked of
|
|
262
|
-
* the opener registry and never a kind list, so a kind registered later is covered the day it
|
|
263
|
-
* registers.
|
|
264
|
-
*/
|
|
265
|
-
export declare function lineOpensAs(line: string, grammar?: GrammarView): AnyBlockKind;
|
|
266
|
-
/**
|
|
267
|
-
* Re-derive the container at `index` from its own (already rebuilt) raw, replacing it in
|
|
268
|
-
* the slot when that raw now opens as a different kind. The container twin of
|
|
269
|
-
* `updateNodeContent`'s kind-change arm (editor.md § 8), which no leaf reparse can see.
|
|
270
|
-
* Eligibility is the opener registry, not a kind list — registering an opener is exactly
|
|
271
|
-
* the claim that `parse(raw)` reproduces the kind, so re-deriving one without would destroy it.
|
|
272
|
-
*/
|
|
273
|
-
export declare function reclassifyContainer(parent: NodeParent, index: number, grammar?: GrammarView): CstNode | null;
|
|
274
|
-
/**
|
|
275
|
-
* Map a post-edit caret offset (in the committed text) to the parsed block it falls in,
|
|
276
|
-
* as a local display offset. The first block's body starts at 0; an offset inside
|
|
277
|
-
* inter-block trivia lands at the next block's start; past-the-end clamps to the last.
|
|
278
|
-
*/
|
|
279
|
-
export declare function focusTargetInReplacement(nodes: readonly NodeView[], offset: number): {
|
|
280
|
-
index: number;
|
|
281
|
-
offset: number;
|
|
282
|
-
};
|
|
283
|
-
/**
|
|
284
|
-
* Where a caret at `offset` in the written text lands once {@link updateNodeContent}'s folds
|
|
285
|
-
* settled: an absorb ABOVE leaves the predecessor holding the bytes, so the slot the gesture
|
|
286
|
-
* named is gone and the offset carries what that predecessor put in front of it. Every caret
|
|
287
|
-
* door onto the content funnel asks this, so the settle is answered in one place.
|
|
288
|
-
*/
|
|
289
|
-
export declare function settledCaretTarget(settled: SettledContent, at: number, offset: number, children: readonly NodeView[]): {
|
|
290
|
-
index: number;
|
|
291
|
-
offset: number;
|
|
292
|
-
};
|
|
293
|
-
/** First node inherits the original block's leadingTrivia; subsequent nodes keep theirs. */
|
|
294
|
-
export declare function normalizeReplacementTrivia(original: CstNode, replacement: CstNode[]): CstNode[];
|
|
295
|
-
/** Ensure every container has at least one child block, so the cursor always has a target. */
|
|
296
|
-
export declare function ensureEditableContainers(node: CstNode): void;
|