From cde6311bcb9f8f8f28505bbf7c71a574c294e69f Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Sat, 19 Nov 2022 15:22:43 +0100 Subject: [PATCH] Fixes #122 --- plugs/directive/command.ts | 107 ++++++++++++++++++++-------- plugs/directive/directive.plug.yaml | 4 +- plugs/directive/directives.ts | 7 +- 3 files changed, 79 insertions(+), 39 deletions(-) diff --git a/plugs/directive/command.ts b/plugs/directive/command.ts index 4c94f727..69b77084 100644 --- a/plugs/directive/command.ts +++ b/plugs/directive/command.ts @@ -1,39 +1,84 @@ -import { editor, space, system } from "$sb/silverbullet-syscall/mod.ts"; -import { renderDirectives } from "./directives.ts"; +import { editor, markdown, system } from "$sb/silverbullet-syscall/mod.ts"; +import { nodeAtPos } from "$sb/lib/tree.ts"; +import { replaceAsync } from "$sb/lib/util.ts"; +import { directiveRegex, renderDirectives } from "./directives.ts"; +import { extractMeta } from "./data.ts"; export async function updateDirectivesOnPageCommand() { - const currentPage = await editor.getCurrentPage(); - await editor.save(); - if ( - await system.invokeFunction( - "server", - "updateDirectivesOnPage", - currentPage, - ) - ) { - await editor.reloadPage(); + const pageName = await editor.getCurrentPage(); + const text = await editor.getText(); + const tree = await markdown.parseMarkdown(text); + const metaData = extractMeta(tree, ["$disableDirectives"]); + if (metaData.$disableDirectives) { + // Not updating, directives disabled + return; } + + // Collect all directives and their body replacements + const replacements: { startInst: string; text?: string }[] = []; + + await replaceAsync( + text, + directiveRegex, + async (fullMatch, startInst, _type, _arg, _body, endInst, index) => { + const replacement: { startInst: string; text?: string } = { startInst }; + // Pushing to the replacement array + replacements.push(replacement); + const currentNode = nodeAtPos(tree, index + 1); + if (currentNode?.type !== "CommentBlock") { + // If not a comment block, it's likely a code block, ignore + // console.log("Not comment block, ingoring", fullMatch); + return fullMatch; + } + try { + const replacementText = await system.invokeFunction( + "server", + "serverRenderDirective", + pageName, + fullMatch, + ); + replacement.text = replacementText; + return replacementText; + } catch (e: any) { + return `${startInst}\n**ERROR:** ${e.message}\n${endInst}`; + } + }, + ); + let counter = 0; + // Iterate again and replace the bodies. Iterating again (not using previous positions) + // because text may have changed in the mean time (directive processing may take some time) + // Hypothetically in the mean time directives in text may have been changed/swapped, in which + // case this will break. This would be a rare edge case, however. + await replaceAsync( + text, + directiveRegex, + async (fullMatch, startInst, _type, _arg, _body, endInst, index) => { + const replacement = replacements[counter++]; + if (!replacement.text) { + return `${startInst}\n$**ERROR:** Internal error, no replacement found\n${endInst}`; + } + if (replacement.text === fullMatch) { + // No change, no need to dispatch + return fullMatch; + } + // Dispatch the change as a somewhat minimal diff in order not to interfere with current editing + await editor.dispatch({ + changes: { + from: index, + to: index + fullMatch.length, + insert: replacement.text, + }, + }); + return replacement.text; + }, + ); } // Called from client, running on server -export async function updateDirectivesOnPage( +// The text passed here is going to be a single directive block (not a full page) +export function serverRenderDirective( pageName: string, -): Promise { - let text = ""; - try { - text = await space.readPage(pageName); - } catch { - console.warn( - "Could not read page", - pageName, - "perhaps it doesn't yet exist", - ); - return false; - } - const newText = await renderDirectives(pageName, text); - if (text !== newText) { - await space.writePage(pageName, newText); - return true; - } - return false; + text: string, +): Promise { + return renderDirectives(pageName, text); } diff --git a/plugs/directive/directive.plug.yaml b/plugs/directive/directive.plug.yaml index b7d594d3..be14fe65 100644 --- a/plugs/directive/directive.plug.yaml +++ b/plugs/directive/directive.plug.yaml @@ -2,8 +2,8 @@ name: directive imports: - https://get.silverbullet.md/global.plug.json functions: - updateDirectivesOnPage: - path: ./command.ts:updateDirectivesOnPage + serverRenderDirective: + path: ./command.ts:serverRenderDirective updateDirectivesOnPageCommand: path: ./command.ts:updateDirectivesOnPageCommand command: diff --git a/plugs/directive/directives.ts b/plugs/directive/directives.ts index a51add25..e6b04aa8 100644 --- a/plugs/directive/directives.ts +++ b/plugs/directive/directives.ts @@ -52,12 +52,7 @@ export async function renderDirectives( text: string, ): Promise { const tree = await markdown.parseMarkdown(text); - const metaData = extractMeta(tree, ["$disableDirectives"]); - // console.log("Meta data", pageName, metaData); - if (metaData.$disableDirectives) { - return text; - } - text = renderToText(tree); + text = await directiveDispatcher(pageName, text, tree, { use: templateDirectiveRenderer, "use-verbose": templateDirectiveRenderer,