diff --git a/packages/common/tree.ts b/packages/common/tree.ts index f75c69b4..77fcf549 100644 --- a/packages/common/tree.ts +++ b/packages/common/tree.ts @@ -107,6 +107,15 @@ export function findNodeOfType( return collectNodesMatching(tree, (n) => n.type === nodeType)[0]; } +export function traverseTree( + tree: ParseTree, + // Return value = should stop traversal? + matchFn: (tree: ParseTree) => boolean +): void { + // Do a collect, but ignore the result + collectNodesMatching(tree, matchFn); +} + // Finds non-text node at position export function nodeAtPos(tree: ParseTree, pos: number): ParseTree | null { if (pos < tree.from! || pos > tree.to!) { diff --git a/packages/plugs/core/page.ts b/packages/plugs/core/page.ts index 2bfc1866..dc1e5cc0 100644 --- a/packages/plugs/core/page.ts +++ b/packages/plugs/core/page.ts @@ -12,11 +12,8 @@ import { set as storeSet } from "@plugos/plugos-syscall/store"; import { flashNotification, getCurrentPage, - getCursor, getText, - insertAtCursor, matchBefore, - moveCursor, navigate, prompt, } from "@silverbulletmd/plugos-silverbullet-syscall/editor"; diff --git a/packages/plugs/core/plugmanager.ts b/packages/plugs/core/plugmanager.ts index 6e3e7fc4..f52581f4 100644 --- a/packages/plugs/core/plugmanager.ts +++ b/packages/plugs/core/plugmanager.ts @@ -1,22 +1,20 @@ import { dispatch } from "@plugos/plugos-syscall/event"; import { Manifest } from "@silverbulletmd/common/manifest"; -import { findNodeOfType } from "@silverbulletmd/common/tree"; import { flashNotification, save, } from "@silverbulletmd/plugos-silverbullet-syscall/editor"; -import { parseMarkdown } from "@silverbulletmd/plugos-silverbullet-syscall/markdown"; import { deletePage, listPages, - readPage, writePage, } from "@silverbulletmd/plugos-silverbullet-syscall/space"; import { invokeFunction, reloadPlugs, } from "@silverbulletmd/plugos-silverbullet-syscall/system"; -import YAML from "yaml"; + +import { readYamlPage } from "../lib/yaml_page"; async function listPlugs(): Promise { let unfilteredPages = await listPages(true); @@ -28,23 +26,22 @@ async function listPlugs(): Promise { export async function updatePlugsCommand() { await save(); flashNotification("Updating plugs..."); - await invokeFunction("server", "updatePlugs"); - flashNotification("And... done!"); - await reloadPlugs(); + try { + await invokeFunction("server", "updatePlugs"); + flashNotification("And... done!"); + await reloadPlugs(); + } catch (e: any) { + flashNotification("Error updating plugs: " + e.message, "error"); + } } export async function updatePlugs() { - let { text: plugPageText } = await readPage("PLUGS"); - - let tree = await parseMarkdown(plugPageText); - - let codeTextNode = findNodeOfType(tree, "CodeText"); - if (!codeTextNode) { - console.error("Could not find yaml block in PLUGS"); - return; + let plugList: string[] = []; + try { + plugList = await readYamlPage("PLUGS"); + } catch (e: any) { + throw new Error(`Error processing PLUGS: ${e.message}`); } - let plugYaml = codeTextNode.children![0].text; - let plugList = YAML.parse(plugYaml!); console.log("Plug YAML", plugList); let allPlugNames: string[] = []; for (let plugUri of plugList) { diff --git a/packages/plugs/core/template.ts b/packages/plugs/core/template.ts index c622bb91..a52876ef 100644 --- a/packages/plugs/core/template.ts +++ b/packages/plugs/core/template.ts @@ -16,12 +16,14 @@ import { parseMarkdown } from "@silverbulletmd/plugos-silverbullet-syscall/markd import { extractMeta } from "../query/data"; import { renderToText } from "@silverbulletmd/common/tree"; import { niceDate } from "./dates"; - -const pageTemplatePrefix = `template/page/`; -const snippetPrefix = `snippet/`; +import { readSettings } from "../lib/settings_page"; export async function instantiateTemplateCommand() { let allPages = await listPages(); + let { pageTemplatePrefix } = await readSettings({ + pageTemplatePrefix: "template/page/", + }); + let allPageTemplates = allPages.filter((pageMeta) => pageMeta.name.startsWith(pageTemplatePrefix) ); @@ -54,11 +56,17 @@ export async function instantiateTemplateCommand() { export async function insertSnippet() { let allPages = await listPages(); + let { snippetPrefix } = await readSettings({ + snippetPrefix: "snippet/", + }); let cursorPos = await getCursor(); let page = await getCurrentPage(); - let allSnippets = allPages.filter((pageMeta) => - pageMeta.name.startsWith(snippetPrefix) - ); + let allSnippets = allPages + .filter((pageMeta) => pageMeta.name.startsWith(snippetPrefix)) + .map((pageMeta) => ({ + ...pageMeta, + name: pageMeta.name.slice(snippetPrefix.length), + })); let selectedSnippet = await filterBox( "Snippet", @@ -69,7 +77,7 @@ export async function insertSnippet() { if (!selectedSnippet) { return; } - let { text } = await readPage(selectedSnippet.name); + let { text } = await readPage(`${snippetPrefix}${selectedSnippet.name}`); let templateText = replaceTemplateVars(text, page); let carretPos = templateText.indexOf("|^|"); diff --git a/packages/plugs/lib/settings_page.ts b/packages/plugs/lib/settings_page.ts new file mode 100644 index 00000000..acea4c12 --- /dev/null +++ b/packages/plugs/lib/settings_page.ts @@ -0,0 +1,23 @@ +import { readYamlPage } from "./yaml_page"; + +export async function readSettings(settings: T): Promise { + try { + let allSettings = (await readYamlPage("SETTINGS", ["yaml"])) || {}; + // TODO: I'm sure there's a better way to type this than "any" + let collectedSettings: any = {}; + for (let [key, defaultVal] of Object.entries(settings)) { + if (allSettings[key]) { + collectedSettings[key] = allSettings[key]; + } else { + collectedSettings[key] = defaultVal; + } + } + return collectedSettings as T; + } catch (e: any) { + if (e.message === "Page not found") { + // No settings yet, return default values for all + return settings; + } + throw e; + } +} diff --git a/packages/plugs/lib/yaml_page.ts b/packages/plugs/lib/yaml_page.ts new file mode 100644 index 00000000..902539a7 --- /dev/null +++ b/packages/plugs/lib/yaml_page.ts @@ -0,0 +1,42 @@ +import { findNodeOfType, traverseTree } from "@silverbulletmd/common/tree"; +import { parseMarkdown } from "@silverbulletmd/plugos-silverbullet-syscall/markdown"; +import { readPage } from "@silverbulletmd/plugos-silverbullet-syscall/space"; +import YAML from "yaml"; + +export async function readYamlPage( + pageName: string, + allowedLanguages = ["yaml"] +): Promise { + const { text } = await readPage(pageName); + let tree = await parseMarkdown(text); + let data: any = {}; + + traverseTree(tree, (t): boolean => { + // Find a fenced code block + if (t.type !== "FencedCode") { + return false; + } + let codeInfoNode = findNodeOfType(t, "CodeInfo"); + if (!codeInfoNode) { + return false; + } + if (!allowedLanguages.includes(codeInfoNode.children![0].text!)) { + return false; + } + let codeTextNode = findNodeOfType(t, "CodeText"); + if (!codeTextNode) { + // Honestly, this shouldn't happen + return false; + } + let codeText = codeTextNode.children![0].text!; + try { + data = YAML.parse(codeText); + } catch (e: any) { + console.error("YAML Page parser error", e); + throw new Error(`YAML Error: ${e.message}`); + } + return true; + }); + + return data; +}