silverbullet/plugs/index/item.ts

166 lines
3.8 KiB
TypeScript
Raw Normal View History

2024-08-22 02:13:40 +08:00
import type { IndexTreeEvent, ObjectValue } from "../../plug-api/types.ts";
2022-04-01 23:07:08 +08:00
2024-02-29 22:23:05 +08:00
import {
2024-08-22 02:13:40 +08:00
findParentMatching,
2024-07-30 23:33:33 +08:00
type ParseTree,
2024-02-29 22:23:05 +08:00
renderToText,
2024-08-22 02:13:40 +08:00
traverseTreeAsync,
} from "@silverbulletmd/silverbullet/lib/tree";
import {
cleanAttributes,
extractAttributes,
} from "@silverbulletmd/silverbullet/lib/attribute";
import { rewritePageRefs } from "@silverbulletmd/silverbullet/lib/resolve";
import { indexObjects } from "./api.ts";
2024-08-22 02:13:40 +08:00
import {
cleanHashTags,
extractHashTags,
updateITags,
} from "@silverbulletmd/silverbullet/lib/tags";
import {
extractFrontmatter,
type FrontMatter,
} from "@silverbulletmd/silverbullet/lib/frontmatter";
import { deepClone } from "@silverbulletmd/silverbullet/lib/json";
2022-03-29 23:02:28 +08:00
export type ItemObject = ObjectValue<
{
page: string;
2024-07-07 02:52:27 +08:00
name: string;
text: string;
pos: number;
} & Record<string, any>
>;
2022-03-29 23:02:28 +08:00
export async function indexItems({ name, tree }: IndexTreeEvent) {
2024-08-22 02:13:40 +08:00
const items = await extractItems(name, tree);
2024-08-22 03:30:29 +08:00
// console.log("Found", items, "item(s)");
2024-08-22 02:13:40 +08:00
await indexObjects(name, items);
}
export async function extractItems(name: string, tree: ParseTree) {
const items: ObjectValue<ItemObject>[] = [];
2022-03-29 23:02:28 +08:00
const frontmatter = await extractFrontmatter(tree);
2022-04-04 17:51:41 +08:00
2024-08-22 02:13:40 +08:00
await traverseTreeAsync(tree, async (n) => {
if (n.type !== "ListItem") {
return false;
}
2022-04-04 17:51:41 +08:00
if (!n.children) {
2024-08-22 02:13:40 +08:00
// Weird, let's jump out
return true;
}
2024-08-22 03:30:29 +08:00
// Is this a task?
if (n.children.find((n) => n.type === "Task")) {
// Skip tasks
return true;
}
2024-08-22 02:13:40 +08:00
const item: ItemObject = await extractItemFromNode(
name,
n,
2024-08-22 02:13:40 +08:00
frontmatter,
);
2024-08-22 02:13:40 +08:00
items.push(item);
2024-08-22 02:13:40 +08:00
return false;
});
return items;
}
2024-07-07 02:52:27 +08:00
2024-08-22 02:13:40 +08:00
export async function extractItemFromNode(
name: string,
itemNode: ParseTree,
frontmatter: FrontMatter,
) {
const item: ItemObject = {
ref: `${name}@${itemNode.from}`,
tag: "item",
name: "",
text: "",
page: name,
pos: itemNode.from!,
};
// Now let's extract tags and attributes
const tags = extractHashTags(itemNode);
const extractedAttributes = await extractAttributes(
["item", ...tags],
itemNode,
);
const clonedTextNodes: ParseTree[] = [];
for (const child of itemNode.children!.slice(1)) {
rewritePageRefs(child, name);
if (child.type === "OrderedList" || child.type === "BulletList") {
break;
}
2024-08-22 02:13:40 +08:00
clonedTextNodes.push(deepClone(child, ["parent"]));
}
2024-08-22 02:13:40 +08:00
// Original text
item.text = clonedTextNodes.map(renderToText).join("").trim();
// Clean out attribtus and tags and render a clean item name
for (const clonedTextNode of clonedTextNodes) {
cleanHashTags(clonedTextNode);
cleanAttributes(clonedTextNode);
}
2024-08-22 02:13:40 +08:00
item.name = clonedTextNodes.map(renderToText).join("").trim();
2024-08-22 02:13:40 +08:00
if (tags.length > 0) {
item.tags = tags;
}
for (const [key, value] of Object.entries(extractedAttributes)) {
item[key] = value;
}
updateITags(item, frontmatter);
await enrichItemFromParents(itemNode, item, name, frontmatter);
return item;
}
export async function enrichItemFromParents(
n: ParseTree,
item: ObjectValue<any>,
pageName: string,
frontmatter: FrontMatter,
) {
let directParent = true;
let parentItemNode = findParentMatching(n, (n) => n.type === "ListItem");
while (parentItemNode) {
// console.log("Got parent", parentItemNode);
const parentItem = await extractItemFromNode(
pageName,
parentItemNode,
frontmatter,
);
if (directParent) {
item.parent = parentItem.ref;
directParent = false;
}
// Merge tags
item.itags = [
...new Set([
...item.itags || [],
...(parentItem.itags!.filter((t) => !["item", "task"].includes(t))),
]),
];
parentItemNode = findParentMatching(
parentItemNode,
(n) => n.type === "ListItem",
);
2023-07-26 23:12:56 +08:00
}
}