import { collectNodesMatching, collectNodesOfType, renderToText, } from "@silverbulletmd/silverbullet/lib/tree"; import type { IndexTreeEvent } from "../../type/event.ts"; import { indexObjects, queryLuaObjects } from "./api.ts"; import { getNameFromPath, parseToRef } from "../../plug-api/lib/ref.ts"; import { extractAttributes } from "@silverbulletmd/silverbullet/lib/attribute"; import { extractHashtag } from "../../plug-api/lib/tags.ts"; import { lua } from "@silverbulletmd/silverbullet/syscalls"; import type { ObjectValue } from "../../type/index.ts"; import type { CompleteEvent } from "@silverbulletmd/silverbullet/type/client"; type HeaderObject = ObjectValue< { name: string; page: string; level: number; pos: number; } & Record >; export async function indexHeaders({ name: pageName, tree }: IndexTreeEvent) { const headers: ObjectValue[] = []; for ( const n of collectNodesMatching( tree, (t) => !!t.type?.startsWith("ATXHeading"), ) ) { const level = +n.type!.substring("ATXHeading".length); const tags = new Set(); collectNodesOfType(n, "Hashtag").forEach((h) => { // Push tag to the list, removing the initial # tags.add(extractHashtag(h.children![0].text!)); h.children = []; }); // Extract attributes and remove from tree const extractedAttributes = await extractAttributes(n); const name = n.children!.slice(1).map(renderToText).join("").trim(); headers.push({ ref: `${pageName}@${n.from}`, tag: "header", tags: [...tags], level, name, page: pageName, pos: n.from!, ...extractedAttributes, }); } // console.log("Found", headers, "headers(s)"); await indexObjects(pageName, headers); } export async function headerComplete(completeEvent: CompleteEvent) { const match = /(?:\[\[|\[.*?\]\()(?[^\[]*)$/.exec( completeEvent.linePrefix, ); if (!match || !match.groups?.path) { return; } const ref = parseToRef(match.groups.path); if (!ref || ref.details?.type !== "header") { return; } const headers = await queryLuaObjects( "header", { objectVariable: "_", where: await lua.parseExpression(`_.page == name`), }, { name: getNameFromPath(ref.path) || completeEvent.pageName }, 5, ); return { from: completeEvent.pos - match.groups.path.length, options: headers.map((header) => ({ label: `${getNameFromPath(ref.path)}#${header.name}`, type: "header", })), }; }