import { collectNodesMatching, collectNodesOfType, renderToText, } from "@silverbulletmd/silverbullet/lib/tree"; import type { CompleteEvent, IndexTreeEvent, ObjectValue, } from "../../plug-api/types.ts"; import { indexObjects, queryObjects } from "./api.ts"; import { parsePageRef } from "@silverbulletmd/silverbullet/lib/page_ref"; import { extractAttributes } from "@silverbulletmd/silverbullet/lib/attribute"; import { extractHashtag } from "../../plug-api/lib/tags.ts"; 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( ["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, }); } // console.log("Found", headers, "headers(s)"); await indexObjects(pageName, headers); } export async function headerComplete(completeEvent: CompleteEvent) { const match = /(?:\[\[|\[.*?\]\()([^\]$:#]*#[^\]\)]*)$/.exec( completeEvent.linePrefix, ); if (!match) { return null; } const pageRef = parsePageRef(match[1]).page; const allHeaders = await queryObjects("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", })), }; }