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";
|
2024-08-07 02:11:38 +08:00
|
|
|
import { rewritePageRefs } from "@silverbulletmd/silverbullet/lib/resolve";
|
2023-10-03 20:16:33 +08:00
|
|
|
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
|
|
|
|
2023-10-03 20:16:33 +08:00
|
|
|
export type ItemObject = ObjectValue<
|
|
|
|
{
|
|
|
|
page: string;
|
2024-07-07 02:52:27 +08:00
|
|
|
name: string;
|
|
|
|
text: string;
|
2023-10-03 20:16:33 +08:00
|
|
|
pos: number;
|
|
|
|
} & Record<string, any>
|
|
|
|
>;
|
2022-03-29 23:02:28 +08:00
|
|
|
|
2022-04-20 16:56:43 +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) {
|
2023-10-03 20:16:33 +08:00
|
|
|
const items: ObjectValue<ItemObject>[] = [];
|
2022-03-29 23:02:28 +08:00
|
|
|
|
2024-01-11 20:20:50 +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;
|
2022-07-08 15:50:26 +08:00
|
|
|
}
|
|
|
|
|
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,
|
2024-02-28 03:05:12 +08:00
|
|
|
n,
|
2024-08-22 02:13:40 +08:00
|
|
|
frontmatter,
|
2024-02-28 03:05:12 +08:00
|
|
|
);
|
|
|
|
|
2024-08-22 02:13:40 +08:00
|
|
|
items.push(item);
|
2022-07-08 15:50:26 +08:00
|
|
|
|
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-01-11 20:20:50 +08:00
|
|
|
}
|
2024-08-22 02:13:40 +08:00
|
|
|
clonedTextNodes.push(deepClone(child, ["parent"]));
|
|
|
|
}
|
2024-01-11 20:20:50 +08:00
|
|
|
|
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-02-28 03:05:12 +08:00
|
|
|
|
2024-08-22 02:13:40 +08:00
|
|
|
item.name = clonedTextNodes.map(renderToText).join("").trim();
|
2022-07-08 15:50:26 +08:00
|
|
|
|
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
|
|
|
}
|
2022-04-19 22:54:47 +08:00
|
|
|
}
|