From e0b6fbed3e7a8edfcce409cf390a6c0d720d6263 Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Fri, 3 Nov 2023 12:01:33 +0100 Subject: [PATCH] Change anchor reference syntax --- plug-api/lib/page.ts | 6 ++++++ plugs/editor/broken_links.ts | 2 +- plugs/editor/complete.ts | 2 +- plugs/editor/navigate.ts | 4 ++-- plugs/index/anchor.ts | 10 ++++++---- plugs/index/mentions_ps.ts | 2 +- plugs/index/page_links.ts | 4 +--- plugs/markdown/assets/markdown_widget.css | 2 +- plugs/markdown/markdown_content_widget.ts | 4 ++-- plugs/markdown/markdown_render.ts | 2 +- plugs/query/query.ts | 1 + plugs/query/template.ts | 2 ++ web/client.ts | 2 +- web/cm_plugins/fenced_code.ts | 14 +++++++------- web/cm_plugins/wiki_link.ts | 4 +--- web/navigator.ts | 2 +- website/Anchors.md | 4 +++- website/CHANGELOG.md | 4 +++- website/Page Name Rules.md | 6 ++++++ website/Special Pages.md | 1 - 20 files changed, 47 insertions(+), 31 deletions(-) create mode 100644 website/Page Name Rules.md diff --git a/plug-api/lib/page.ts b/plug-api/lib/page.ts index 8e0fd53..4ebc113 100644 --- a/plug-api/lib/page.ts +++ b/plug-api/lib/page.ts @@ -6,6 +6,12 @@ export function validatePageName(name: string) { if (name.startsWith(".")) { throw new Error("Page name cannot start with a '.'"); } + if (name.includes("@")) { + throw new Error("Page name cannot contain '@'"); + } + if (name.includes("$")) { + throw new Error("Page name cannot contain '$'"); + } if (/\.[a-zA-Z]+$/.test(name)) { throw new Error("Page name can not end with a file extension"); } diff --git a/plugs/editor/broken_links.ts b/plugs/editor/broken_links.ts index f5ce7fc..260a1a6 100644 --- a/plugs/editor/broken_links.ts +++ b/plugs/editor/broken_links.ts @@ -13,7 +13,7 @@ export async function brokenLinksCommand() { traverseTree(tree, (tree) => { if (tree.type === "WikiLinkPage") { // Add the prefix in the link text - const [pageName] = tree.children![0].text!.split("@"); + const [pageName] = tree.children![0].text!.split(/[@$]/); if (pageName.startsWith("!")) { return true; } diff --git a/plugs/editor/complete.ts b/plugs/editor/complete.ts index c97d3a9..0f91387 100644 --- a/plugs/editor/complete.ts +++ b/plugs/editor/complete.ts @@ -5,7 +5,7 @@ import { cacheFileListing } from "../federation/federation.ts"; // Completion export async function pageComplete(completeEvent: CompleteEvent) { - const match = /\[\[([^\]@:\{}]*)$/.exec(completeEvent.linePrefix); + const match = /\[\[([^\]@$:\{}]*)$/.exec(completeEvent.linePrefix); if (!match) { return null; } diff --git a/plugs/editor/navigate.ts b/plugs/editor/navigate.ts index 19ec9e5..b1cc1d0 100644 --- a/plugs/editor/navigate.ts +++ b/plugs/editor/navigate.ts @@ -41,8 +41,8 @@ async function actionClickOrActionEnter( case "WikiLink": { let pageLink = mdTree.children![1]!.children![0].text!; let pos; - if (pageLink.includes("@")) { - [pageLink, pos] = pageLink.split("@"); + if (pageLink.includes("@") || pageLink.includes("$")) { + [pageLink, pos] = pageLink.split(/[@$]/); if (pos.match(/^\d+$/)) { pos = +pos; } diff --git a/plugs/index/anchor.ts b/plugs/index/anchor.ts index d980068..0254feb 100644 --- a/plugs/index/anchor.ts +++ b/plugs/index/anchor.ts @@ -17,7 +17,7 @@ export async function indexAnchors({ name: pageName, tree }: IndexTreeEvent) { collectNodesOfType(tree, "NamedAnchor").forEach((n) => { const aName = n.children![0].text!.substring(1); anchors.push({ - ref: `${pageName}@${aName}`, + ref: `${pageName}$${aName}`, tags: ["anchor"], name: aName, page: pageName, @@ -29,12 +29,14 @@ export async function indexAnchors({ name: pageName, tree }: IndexTreeEvent) { } export async function anchorComplete(completeEvent: CompleteEvent) { - const match = /\[\[([^\]@:]*@[\w\.\-\/]*)$/.exec(completeEvent.linePrefix); + const match = /\[\[([^\]@$:]*[@$][\w\.\-\/]*)$/.exec( + completeEvent.linePrefix, + ); if (!match) { return null; } - const pageRef = match[1].split("@")[0]; + const pageRef = match[1].split(/[@$]/)[0]; let filter: QueryExpression | undefined = ["=", ["attr", "page"], [ "string", pageRef, @@ -47,7 +49,7 @@ export async function anchorComplete(completeEvent: CompleteEvent) { return { from: completeEvent.pos - match[1].length, options: allAnchors.map((a) => ({ - label: a.page === completeEvent.pageName ? `@${a.name}` : a.ref, + label: a.page === completeEvent.pageName ? `\$${a.name}` : a.ref, type: "anchor", })), }; diff --git a/plugs/index/mentions_ps.ts b/plugs/index/mentions_ps.ts index 664a03d..638ebc9 100644 --- a/plugs/index/mentions_ps.ts +++ b/plugs/index/mentions_ps.ts @@ -26,7 +26,7 @@ export async function updateMentions() { // use internal navigation via syscall to prevent reloading the full page. export async function navigate(ref: string) { - const [page, pos] = ref.split("@"); + const [page, pos] = ref.split(/[@$]/); await editor.navigate(page, +pos); } diff --git a/plugs/index/page_links.ts b/plugs/index/page_links.ts index 4b38d93..47aecc5 100644 --- a/plugs/index/page_links.ts +++ b/plugs/index/page_links.ts @@ -103,9 +103,7 @@ export async function indexLinks({ name, tree }: IndexTreeEvent) { const wikiLinkAlias = findNodeOfType(n, "WikiLinkAlias"); let toPage = resolvePath(name, wikiLinkPage.children![0].text!); const pos = wikiLinkPage.from!; - if (toPage.includes("@")) { - toPage = toPage.split("@")[0]; - } + toPage = toPage.split(/[@$]/)[0]; const link: LinkObject = { ref: `${name}@${pos}`, tags: ["link"], diff --git a/plugs/markdown/assets/markdown_widget.css b/plugs/markdown/assets/markdown_widget.css index 9e6f44d..49ec94a 100644 --- a/plugs/markdown/assets/markdown_widget.css +++ b/plugs/markdown/assets/markdown_widget.css @@ -81,7 +81,7 @@ body:active #button-bar { } #edit-button, -#source-button { +#reload-button { margin-left: -10px; } diff --git a/plugs/markdown/markdown_content_widget.ts b/plugs/markdown/markdown_content_widget.ts index 0c7b34f..06176cf 100644 --- a/plugs/markdown/markdown_content_widget.ts +++ b/plugs/markdown/markdown_content_widget.ts @@ -13,7 +13,7 @@ export async function markdownContentWidget( const html = renderMarkdownToHtml(mdTree, { smartHardBreak: true }); return { html: await wrapHTML(html), - script: await prepareJS(markdownText, pageName), + script: await prepareJS(pageName, markdownText), // And add back the markdown text so we can render it in a different way if desired markdown: markdownText, }; @@ -41,8 +41,8 @@ export async function wrapHTML(html: string): Promise {
- +
diff --git a/plugs/markdown/markdown_render.ts b/plugs/markdown/markdown_render.ts index 2d8d6e4..4a94428 100644 --- a/plugs/markdown/markdown_render.ts +++ b/plugs/markdown/markdown_render.ts @@ -273,7 +273,7 @@ function render( let externalTaskRef = ""; collectNodesOfType(t, "WikiLinkPage").forEach((wikilink) => { const ref = wikilink.children![0].text!; - if (!externalTaskRef && ref.includes("@")) { + if (!externalTaskRef && (ref.includes("@") || ref.includes("$"))) { externalTaskRef = ref; } }); diff --git a/plugs/query/query.ts b/plugs/query/query.ts index 5fc4b4a..d3c4dae 100644 --- a/plugs/query/query.ts +++ b/plugs/query/query.ts @@ -62,6 +62,7 @@ export async function widget( return system.invokeFunction( "markdown.markdownContentWidget", resultMarkdown, + pageName, ); } catch (e: any) { return system.invokeFunction( diff --git a/plugs/query/template.ts b/plugs/query/template.ts index 3ba8921..be63f3e 100644 --- a/plugs/query/template.ts +++ b/plugs/query/template.ts @@ -55,11 +55,13 @@ export async function widget( return system.invokeFunction( "markdown.markdownContentWidget", rendered, + pageName, ); } catch (e: any) { return system.invokeFunction( "markdown.markdownContentWidget", `**Error:** ${e.message}`, + pageName, ); } } diff --git a/web/client.ts b/web/client.ts index 0ecbc3c..95f0187 100644 --- a/web/client.ts +++ b/web/client.ts @@ -280,7 +280,7 @@ export class Client { const matchingAnchor = await this.system.system.localSyscall( "index", "system.invokeFunction", - ["getObjectByRef", pageName, "anchor", `${pageName}@${pos}`], + ["getObjectByRef", pageName, "anchor", `${pageName}$${pos}`], ); if (!matchingAnchor) { diff --git a/web/cm_plugins/fenced_code.ts b/web/cm_plugins/fenced_code.ts index 9b2fa4c..a035808 100644 --- a/web/cm_plugins/fenced_code.ts +++ b/web/cm_plugins/fenced_code.ts @@ -15,7 +15,7 @@ class IFrameWidget extends WidgetType { constructor( readonly from: number, readonly to: number, - readonly editor: Client, + readonly client: Client, readonly bodyText: string, readonly codeWidgetCallback: CodeWidgetCallback, ) { @@ -25,20 +25,20 @@ class IFrameWidget extends WidgetType { toDOM(): HTMLElement { const from = this.from; const iframe = createWidgetSandboxIFrame( - this.editor, + this.client, this.bodyText, - this.codeWidgetCallback(this.bodyText, this.editor.currentPage!), + this.codeWidgetCallback(this.bodyText, this.client.currentPage!), (message) => { switch (message.type) { case "blur": - this.editor.editorView.dispatch({ + this.client.editorView.dispatch({ selection: { anchor: from }, }); - this.editor.focus(); + this.client.focus(); break; case "reload": - this.codeWidgetCallback(this.bodyText, this.editor.currentPage!) + this.codeWidgetCallback(this.bodyText, this.client.currentPage!) .then( (widgetContent: WidgetContent) => { iframe.contentWindow!.postMessage({ @@ -61,7 +61,7 @@ class IFrameWidget extends WidgetType { } get estimatedHeight(): number { - const cachedHeight = this.editor.space.getCachedWidgetHeight(this.bodyText); + const cachedHeight = this.client.space.getCachedWidgetHeight(this.bodyText); // console.log("Calling estimated height", cachedHeight); return cachedHeight > 0 ? cachedHeight : 150; } diff --git a/web/cm_plugins/wiki_link.ts b/web/cm_plugins/wiki_link.ts index 0c5fcf8..edce0d6 100644 --- a/web/cm_plugins/wiki_link.ts +++ b/web/cm_plugins/wiki_link.ts @@ -31,9 +31,7 @@ export function cleanWikiLinkPlugin(editor: Client) { const allPages = editor.space.listPages(); let pageExists = !editor.fullSyncCompleted; let cleanPage = page; - if (page.includes("@")) { - cleanPage = page.split("@")[0]; - } + cleanPage = page.split(/[@$]/)[0]; cleanPage = resolvePath(editor.currentPage!, cleanPage); const lowerCasePageName = cleanPage.toLowerCase(); for (const pageMeta of allPages) { diff --git a/web/navigator.ts b/web/navigator.ts index d54f97b..8fafc03 100644 --- a/web/navigator.ts +++ b/web/navigator.ts @@ -67,7 +67,7 @@ export class PathPageNavigator { decodeURI(): [string, number | string] { const [page, pos] = decodeURI( location.pathname.substring(this.root.length + 1), - ).split("@"); + ).split(/[@$]/); if (pos) { if (pos.match(/^\d+$/)) { return [page, +pos]; diff --git a/website/Anchors.md b/website/Anchors.md index d7c1e3a..38194b9 100644 --- a/website/Anchors.md +++ b/website/Anchors.md @@ -1 +1,3 @@ -Anchor represent named locations within a page and are defined using the $anchor syntax. They can then be referenced withing the page using [[@anchor]], or cross-page via [[Anchors@anchor]]. \ No newline at end of file +Anchor represent named locations within a page and are defined using the $anchor syntax. They can then be referenced withing the page using [[$anchor]], or cross-page via [[Anchors$anchor]]. + +The legacy syntax is to use `@` instead of `$` for referencing, which is still supported but deprecated. \ No newline at end of file diff --git a/website/CHANGELOG.md b/website/CHANGELOG.md index 58d82f3..275f63f 100644 --- a/website/CHANGELOG.md +++ b/website/CHANGELOG.md @@ -6,8 +6,10 @@ release. * Many styling fixes and improvements to [[Live Queries]] and [[Live Templates]] * Added a “source” button to [[Live Queries]] and [[Live Templates]] for better debugging (showing you the markdown code rendered by the template so you can more easily detect issues) * [[Live Queries]]: - * Support for `render all` where the entire result set is passed to a single template allowing you to e.g. dynamically build up tables, see [[Live Queries@render]] for an example. + * Support for `render all` where the entire result set is passed to a single template allowing you to e.g. dynamically build up tables, see [[Live Queries$render]] for an example. * The default generated [[SETTINGS]] page now contains a link to [[SETTINGS]] on silverbullet.md for documentation purposes. +* The syntax to reference [[Anchors]] has now changed to use `$`, instead of `@` (e.g. [[Live Queries$render]]), the old syntax still works but is deprecated. The reason for this change is consistency: you define an anchor using the `$myanchor` syntax, referencing it the same way makes more sense. +* [[Page Name Rules]] are now documented --- ## 0.5.3 diff --git a/website/Page Name Rules.md b/website/Page Name Rules.md new file mode 100644 index 0000000..6a8b198 --- /dev/null +++ b/website/Page Name Rules.md @@ -0,0 +1,6 @@ +A few rules regarding page names: + +* Page names cannot be empty +* Page names cannot start with a `.` +* Page names cannot `@` or `$`, due to ambiguity with referencing specific positions or anchors inside a page +* Page names cannot end with a _file extension_ containing just letters. That is, a page name like `test.md` is not allowed, whereas `test.123` would be. \ No newline at end of file diff --git a/website/Special Pages.md b/website/Special Pages.md index c9dafab..7a1686f 100644 --- a/website/Special Pages.md +++ b/website/Special Pages.md @@ -7,4 +7,3 @@ Here are the current list of “special pages” known to humankind: * [[SETTINGS]] for setting various settings * [[PLUGS]] as a source for the plug manager to decide what plugs to load and where from * [[VIMRC]] for tweaking [[Vim]] mode -* [[STYLES]] to override SilverBullet CSS styles (experimental) \ No newline at end of file