1
0
silverbullet/plugs/lib/tree.ts

125 lines
2.9 KiB
TypeScript
Raw Normal View History

2022-04-04 09:51:41 +00:00
export type MarkdownTree = {
type?: string; // undefined === text node
2022-04-04 13:25:07 +00:00
from?: number;
to?: number;
2022-04-04 09:51:41 +00:00
text?: string;
children?: MarkdownTree[];
parent?: MarkdownTree;
};
export function addParentPointers(mdTree: MarkdownTree) {
if (!mdTree.children) {
return;
}
for (let child of mdTree.children) {
child.parent = mdTree;
addParentPointers(child);
}
}
export function removeParentPointers(mdTree: MarkdownTree) {
delete mdTree.parent;
if (!mdTree.children) {
return;
}
for (let child of mdTree.children) {
removeParentPointers(child);
}
}
export function findParentMatching(
mdTree: MarkdownTree,
matchFn: (mdTree: MarkdownTree) => boolean
): MarkdownTree | null {
let node = mdTree.parent;
while (node) {
if (matchFn(node)) {
return node;
}
node = node.parent;
}
return null;
}
export function collectNodesMatching(
mdTree: MarkdownTree,
matchFn: (mdTree: MarkdownTree) => boolean
): MarkdownTree[] {
if (matchFn(mdTree)) {
return [mdTree];
}
let results: MarkdownTree[] = [];
if (mdTree.children) {
for (let child of mdTree.children) {
results = [...results, ...collectNodesMatching(child, matchFn)];
}
}
return results;
}
// return value: returning undefined = not matched, continue, null = delete, new node = replace
2022-04-04 13:25:07 +00:00
export function replaceNodesMatching(
mdTree: MarkdownTree,
substituteFn: (mdTree: MarkdownTree) => MarkdownTree | null | undefined
) {
2022-04-09 12:28:41 +00:00
if (mdTree.children) {
2022-04-04 13:25:07 +00:00
for (let child of mdTree.children) {
2022-04-09 12:28:41 +00:00
let subst = substituteFn(child);
if (subst !== undefined) {
let pos = mdTree.children.indexOf(child);
if (subst) {
mdTree.children.splice(pos, 1, subst);
} else {
// null = delete
mdTree.children.splice(pos, 1);
}
} else {
replaceNodesMatching(child, substituteFn);
}
2022-04-04 13:25:07 +00:00
}
}
}
2022-04-04 09:51:41 +00:00
export function findNodeMatching(
mdTree: MarkdownTree,
matchFn: (mdTree: MarkdownTree) => boolean
): MarkdownTree | null {
return collectNodesMatching(mdTree, matchFn)[0];
}
// Finds non-text node at position
export function nodeAtPos(
mdTree: MarkdownTree,
pos: number
): MarkdownTree | null {
2022-04-04 13:25:07 +00:00
if (pos < mdTree.from! || pos > mdTree.to!) {
2022-04-04 09:51:41 +00:00
return null;
}
if (!mdTree.children) {
return mdTree;
}
for (let child of mdTree.children) {
let n = nodeAtPos(child, pos);
if (n && n.text !== undefined) {
// Got a text node, let's return its parent
return mdTree;
} else if (n) {
// Got it
return n;
}
}
return null;
}
// Turn MarkdownTree back into regular markdown text
2022-04-04 13:25:07 +00:00
export function renderMarkdown(mdTree: MarkdownTree): string {
2022-04-04 09:51:41 +00:00
let pieces: string[] = [];
if (mdTree.text !== undefined) {
return mdTree.text;
}
for (let child of mdTree.children!) {
2022-04-04 13:25:07 +00:00
pieces.push(renderMarkdown(child));
2022-04-04 09:51:41 +00:00
}
return pieces.join("");
}