From d08c8cef20fd749879937eecf6aec3ee861d6dda Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Wed, 26 Mar 2025 19:42:51 +0100 Subject: [PATCH] Fix plugs and plug updating --- Library/Std/Config.md | 13 ++ common/PAGE_TEMPLATES.ts | 21 +-- common/syscalls/system.ts | 29 ++++ plugs/plug-manager/plug-manager.plug.yaml | 5 - plugs/plug-manager/plugmanager.ts | 186 ++-------------------- 5 files changed, 62 insertions(+), 192 deletions(-) diff --git a/Library/Std/Config.md b/Library/Std/Config.md index 9133b019..714be890 100644 --- a/Library/Std/Config.md +++ b/Library/Std/Config.md @@ -3,6 +3,19 @@ This defines the [JSON schema](https://json-schema.org/) for built-in settings. ```space-lua +config.define("plugs", { + description = "List of plugs to enable", + oneOf = { + { + type = "array", + items = { type = "string" } + }, + { -- only as a fallback for an empty table in Lua + type = "object", + } + }, +}) + config.define("autoCloseBrackets", { description = "List of brackets to auto close", type = "string", diff --git a/common/PAGE_TEMPLATES.ts b/common/PAGE_TEMPLATES.ts index a92e86a1..5fa4452a 100644 --- a/common/PAGE_TEMPLATES.ts +++ b/common/PAGE_TEMPLATES.ts @@ -36,23 +36,10 @@ This is where you configure SilverBullet to your liking. See [[^Library/Std/Conf \`\`\`space-lua config.set { - actionButtons = { - { - icon = "home", - command = "Navigate: Home", - description = "Go to the index page" - }, - { - icon = "book", - command = "Navigate: Page Picker", - description = "Open page" - }, - { - icon = "terminal", - command = "Open Command Palette", - description = "Run command" - } - }, + plugs = { + -- Add your plugs here (https://silverbullet.md/Plugs) + -- Then run the \`Plugs: Update\` command to update them + } } \`\`\` `; diff --git a/common/syscalls/system.ts b/common/syscalls/system.ts index 5459a8aa..9292b177 100644 --- a/common/syscalls/system.ts +++ b/common/syscalls/system.ts @@ -28,6 +28,22 @@ export function systemSyscalls( } return plug.invoke(functionName, args); }, + "system.invokeFunctionOnServer": ( + ctx, + fullName: string, // plug.function + ...args: any[] + ) => { + console.warn( + "Calling deprecated system.invokeFunctionOnServer, use system.invokeFunction instead", + ); + return api["system.invokeFunction"](ctx, fullName, ...args); + }, + "system.serverSyscall": (_ctx, name: string, ...args: any[]) => { + console.warn( + "Calling deprecated system.serverSyscall, use syscall instead", + ); + return client.clientSystem.localSyscall(name, args); + }, "system.invokeCommand": (_ctx, name: string, args?: string[]) => { if (!client) { throw new Error("Not supported"); @@ -69,6 +85,10 @@ export function systemSyscalls( } return client.loadPlugs(); }, + "system.reloadConfig": (): Record => { + console.warn("system.reloadConfig is deprecated, it's now a no-op"); + return client.config.values; + }, "system.loadSpaceScripts": async () => { await client.clientSystem.loadSpaceScripts(); }, @@ -79,7 +99,16 @@ export function systemSyscalls( await client.loadCustomStyles(); }, // DEPRECATED + "system.getEnv": () => { + console.warn( + "system.getEnv is deprecated, you can assume the env to always be the client", + ); + return null; + }, "system.getSpaceConfig": (_ctx, key, defaultValue?) => { + console.warn( + "system.getSpaceConfig is deprecated, use system.getConfig instead", + ); return client.config.get(key, defaultValue); }, "system.getMode": () => { diff --git a/plugs/plug-manager/plug-manager.plug.yaml b/plugs/plug-manager/plug-manager.plug.yaml index 8954beaf..307dd38e 100644 --- a/plugs/plug-manager/plug-manager.plug.yaml +++ b/plugs/plug-manager/plug-manager.plug.yaml @@ -21,8 +21,3 @@ functions: path: "./plugmanager.ts:getPlugGithubRelease" events: - get-plug:ghr - addPlugCommand: - path: ./plugmanager.ts:addPlugCommand - command: - name: "Plugs: Add" - requireMode: rw diff --git a/plugs/plug-manager/plugmanager.ts b/plugs/plug-manager/plugmanager.ts index 49c13bdf..16cf6b37 100644 --- a/plugs/plug-manager/plugmanager.ts +++ b/plugs/plug-manager/plugmanager.ts @@ -1,33 +1,24 @@ import { editor, events, - language, - markdown, space, system, } from "@silverbulletmd/silverbullet/syscalls"; -import { readYamlPage } from "@silverbulletmd/silverbullet/lib/yaml_page"; import { builtinPlugNames } from "../builtin_plugs.ts"; -import { - findNodeMatching, - traverseTree, -} from "@silverbulletmd/silverbullet/lib/tree"; -import { - addParentPointers, - findNodeOfType, -} from "@silverbulletmd/silverbullet/lib/tree"; -import { assert } from "@std/assert/assert"; - -const plugsPage = "PLUGS"; -const plugsPrelude = - "#meta\n\nThis file lists all plugs added with the {[Plugs: Add]} command. Run the {[Plugs: Update]} command to update all Plugs defined anywhere using Space Config.\n\n"; export async function updatePlugsCommand() { + // Save the current file (could be a config page) await editor.save(); + // Reload the config and commands + await editor.reloadConfigAndCommands(); await editor.flashNotification("Updating plugs..."); try { const plugList: string[] = []; - const configPlugs: any[] = await system.getConfig("plugs", []); + let configPlugs: any[] = await system.getConfig("plugs", []); + if (Object.keys(configPlugs).length === 0) { + // Handle case of empty Lua table + configPlugs = []; + } if (!Array.isArray(configPlugs)) { throw new Error("Expected 'plugs' in Space Config to be an array"); } @@ -40,37 +31,6 @@ export async function updatePlugsCommand() { ); } plugList.push(...stringPlugs); - if (await space.fileExists("PLUGS.md")) { - // This is not primary mode of managing plugs anymore, only here for backwards compatibility. - try { - const pagePlugs: any[] = await readYamlPage("PLUGS"); - if (Array.isArray(pagePlugs)) { - // It's possible that the user is using it for something else, but if it has yaml with an array, assume it's plugs - const pageStringPlugs = pagePlugs.filter((plug) => - typeof plug === "string" - ); - if (pageStringPlugs.length !== pagePlugs.length) { - throw new Error( - `${ - pagePlugs.length - pageStringPlugs.length - } plugs from PLUG page were not in a yaml list format`, - ); - } - plugList.push(...pageStringPlugs); - if (pageStringPlugs.length > 0) { - editor.flashNotification( - `${pageStringPlugs.length} plugs in PLUGS page can be moved to Space Config for better editor support`, - ); - } - } - } catch (e: any) { - editor.flashNotification( - `Error processing PLUGS page: ${e.message}`, - "error", - ); - return; - } - } // De-duplicate URIs, this is safe because by definition they point to the same plug plugList.forEach((uri, index) => { @@ -129,13 +89,15 @@ export async function updatePlugsCommand() { // console.log("Got manifests", plugUri, protocol, manifests); const workerCode = manifests[0] as string; allCustomPlugNames.push(plugName); - // console.log("Writing", `_plug/${plugName}.plug.js`, workerCode); + console.log("Writing", `_plug/${plugName}.plug.js`); await space.writeDocument( `_plug/${plugName}.plug.js`, new TextEncoder().encode(workerCode), ); } + console.log("This part is done"); + const allPlugNames = [...builtinPlugNames, ...allCustomPlugNames]; // And delete extra ones for (const { name: existingPlug } of await space.listPlugs()) { @@ -144,135 +106,19 @@ export async function updatePlugsCommand() { existingPlug.length - ".plug.js".length, ); if (!allPlugNames.includes(plugName)) { + console.log("Deleting", existingPlug); await space.deleteDocument(existingPlug); } } - await editor.flashNotification("And... done!"); + await editor.flashNotification( + "All done!", + ); + system.reloadPlugs(); } catch (e: any) { editor.flashNotification("Error updating plugs: " + e.message, "error"); } } -export async function addPlugCommand(_cmdDef: any, uriSuggestion: string = "") { - let uri = await editor.prompt("Plug URI:", uriSuggestion); - if (!uri) { - return; - } - // Support people copy & pasting the YAML version - if (uri.startsWith("-")) { - uri = uri.replace(/^\-\s*/, ""); - } - - let plugPageContent = plugsPrelude; - if (await space.fileExists(plugsPage + ".md")) { - plugPageContent = await space.readPage(plugsPage); - } else { - await space.writePage(plugsPage, plugPageContent); - } - await editor.navigate({ kind: "page", page: plugsPage }); - // Here we are on the PLUGS page, if it didn't exist before it's filled with prelude - const changeList = await insertIntoPlugPage(uri, plugPageContent); - for (const { from, to, text } of changeList) { - editor.replaceRange(from, to, text); - } - await editor.flashNotification("Plug added!"); - system.reloadPlugs(); -} - -/** Add the plug to the end of the plugs list in Space Config inside the PLUGS page content - * Returns an array for `editor.replaceRange` syscalls. - * - * Rewrites the `yaml` block to `space-config` if present - * Appends the `space-config` block if needed. - * Appends the `plugs` key on root level if needed. - * - * It's exported only to allow testing. - * There are a bunch of asserts to please the type checker that will fail with a malformed page. - */ -export async function insertIntoPlugPage( - uri: string, - pageContent: string, -): Promise<{ from: number; to: number; text: string }[]> { - const edits: { from: number; to: number; text: string }[] = []; - - const tree = await markdown.parseMarkdown(pageContent); - addParentPointers(tree); - - const yamlInfo = findNodeMatching(tree, (n) => { - return n.type === "CodeInfo" && - n.children !== undefined && - n.children.length === 1 && - n.children[0].text === "yaml"; - }); - const configInfo = findNodeMatching(tree, (n) => { - return n.type === "CodeInfo" && - n.children !== undefined && - n.children.length === 1 && - n.children[0].text === "space-config"; - }); - - if (yamlInfo) { - // replace YAML with Space Config, add plugs: line at the start, and the new URI at the end - assert(yamlInfo.from && yamlInfo.to); - edits.push({ from: yamlInfo.from, to: yamlInfo.to, text: "space-config" }); - - assert(yamlInfo.parent); - const yamlText = findNodeOfType(yamlInfo.parent, "CodeText"); - assert(yamlText && yamlText.from && yamlText.to); - edits.push({ from: yamlText.from, to: yamlText.from, text: "plugs:\n" }); - edits.push({ from: yamlText.to, to: yamlText.to, text: `\n- ${uri}` }); - } else if (configInfo) { - // Append the required parts into the Space Config block, using lezer's (upstream) parser - assert(configInfo.parent); - const configText = findNodeOfType(configInfo.parent, "CodeText"); - assert(configText && configText.from && configText.to); - assert(configText.children?.length === 1 && configText.children[0].text); - - const config = configText.children[0].text; - const configTree = await language.parseLanguage("yaml", config); - traverseTree(configTree, (n) => { - if (n.type === "atom") { - if (n.children![0].text === "plugs") { - edits.push({ - from: configText.from! + n.to! + 1, - to: configText.from! + n.to! + 1, - text: `\n- ${uri}`, - }); - return true; - } - } - return false; - }); - - if (edits.length === 0) { - // No plugs in this block - edits.push({ - from: configText.to, - to: configText.to, - text: `\nplugs:\n- ${uri}`, - }); - } - } else { - // Just add the whole block if there's nothing - const configBlock = `\`\`\`space-config -plugs: -- ${uri} -\`\`\``; - edits.push({ - from: pageContent.length, - to: pageContent.length, - // Start on an empty line - text: (pageContent.endsWith("\n") || pageContent === "") - ? configBlock - : ("\n" + configBlock), - }); - } - - // Sort edits from end to start, so they don't affect each other's positions - edits.sort((a, b) => b.from - a.from); - return edits; -} - export async function getPlugHTTPS(url: string): Promise { const fullUrl = `https:${url}`; console.log("Now fetching plug code from", fullUrl);