Fix plugs and plug updating

This commit is contained in:
Zef Hemel
2025-03-26 19:42:51 +01:00
parent 706588cd56
commit d08c8cef20
5 changed files with 62 additions and 192 deletions
+13
View File
@@ -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",
+4 -17
View File
@@ -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
}
}
\`\`\`
`;
+29
View File
@@ -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<string, any> => {
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": () => {
@@ -21,8 +21,3 @@ functions:
path: "./plugmanager.ts:getPlugGithubRelease"
events:
- get-plug:ghr
addPlugCommand:
path: ./plugmanager.ts:addPlugCommand
command:
name: "Plugs: Add"
requireMode: rw
+16 -170
View File
@@ -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<string> {
const fullUrl = `https:${url}`;
console.log("Now fetching plug code from", fullUrl);