2024-03-02 20:54:31 +08:00
|
|
|
import {
|
|
|
|
collectNodesMatching,
|
|
|
|
collectNodesOfType,
|
|
|
|
renderToText,
|
2024-08-07 02:11:38 +08:00
|
|
|
} from "@silverbulletmd/silverbullet/lib/tree";
|
2024-08-02 22:47:36 +08:00
|
|
|
import type {
|
|
|
|
CompleteEvent,
|
|
|
|
IndexTreeEvent,
|
|
|
|
ObjectValue,
|
|
|
|
} from "../../plug-api/types.ts";
|
2024-01-25 21:51:40 +08:00
|
|
|
import { indexObjects, queryObjects } from "./api.ts";
|
2024-08-07 02:11:38 +08:00
|
|
|
import { parsePageRef } from "@silverbulletmd/silverbullet/lib/page_ref";
|
|
|
|
import { extractAttributes } from "@silverbulletmd/silverbullet/lib/attribute";
|
2024-10-20 18:39:58 +08:00
|
|
|
import { extractHashtag } from "../../plug-api/lib/tags.ts";
|
2024-01-25 21:51:40 +08:00
|
|
|
|
2024-03-02 20:54:31 +08:00
|
|
|
type HeaderObject = ObjectValue<
|
|
|
|
{
|
|
|
|
name: string;
|
|
|
|
page: string;
|
|
|
|
level: number;
|
|
|
|
pos: number;
|
|
|
|
} & Record<string, any>
|
|
|
|
>;
|
2024-01-25 21:51:40 +08:00
|
|
|
|
|
|
|
export async function indexHeaders({ name: pageName, tree }: IndexTreeEvent) {
|
|
|
|
const headers: ObjectValue<HeaderObject>[] = [];
|
|
|
|
|
2024-03-02 20:54:31 +08:00
|
|
|
for (
|
|
|
|
const n of collectNodesMatching(
|
|
|
|
tree,
|
|
|
|
(t) => !!t.type?.startsWith("ATXHeading"),
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
const level = +n.type!.substring("ATXHeading".length);
|
|
|
|
const tags = new Set<string>();
|
|
|
|
|
|
|
|
collectNodesOfType(n, "Hashtag").forEach((h) => {
|
|
|
|
// Push tag to the list, removing the initial #
|
2024-10-20 18:39:58 +08:00
|
|
|
tags.add(extractHashtag(h.children![0].text!));
|
2024-03-02 20:54:31 +08:00
|
|
|
h.children = [];
|
|
|
|
});
|
|
|
|
|
|
|
|
// Extract attributes and remove from tree
|
|
|
|
const extractedAttributes = await extractAttributes(
|
|
|
|
["header", ...tags],
|
|
|
|
n,
|
|
|
|
);
|
|
|
|
const name = n.children!.slice(1).map(renderToText).join("").trim();
|
|
|
|
|
|
|
|
headers.push({
|
|
|
|
ref: `${pageName}#${name}@${n.from}`,
|
|
|
|
tag: "header",
|
|
|
|
tags: [...tags],
|
|
|
|
level,
|
|
|
|
name,
|
|
|
|
page: pageName,
|
|
|
|
pos: n.from!,
|
|
|
|
...extractedAttributes,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-03-02 21:48:02 +08:00
|
|
|
// console.log("Found", headers, "headers(s)");
|
2024-01-25 21:51:40 +08:00
|
|
|
await indexObjects(pageName, headers);
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function headerComplete(completeEvent: CompleteEvent) {
|
2024-05-28 02:33:41 +08:00
|
|
|
const match = /(?:\[\[|\[.*?\]\()([^\]$:#]*#[^\]\)]*)$/.exec(
|
2024-01-25 21:51:40 +08:00
|
|
|
completeEvent.linePrefix,
|
|
|
|
);
|
|
|
|
if (!match) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
const pageRef = parsePageRef(match[1]).page;
|
|
|
|
const allHeaders = await queryObjects<HeaderObject>("header", {
|
|
|
|
filter: ["=", ["attr", "page"], [
|
|
|
|
"string",
|
|
|
|
pageRef || completeEvent.pageName,
|
|
|
|
]],
|
|
|
|
}, 5);
|
|
|
|
return {
|
|
|
|
from: completeEvent.pos - match[1].length,
|
|
|
|
options: allHeaders.map((a) => ({
|
|
|
|
label: a.page === completeEvent.pageName
|
|
|
|
? `#${a.name}`
|
|
|
|
: a.ref.split("@")[0],
|
|
|
|
type: "header",
|
|
|
|
})),
|
|
|
|
};
|
|
|
|
}
|