diff --git a/common/parser.test.ts b/common/parser.test.ts index 0ea06784..71ddec3a 100644 --- a/common/parser.test.ts +++ b/common/parser.test.ts @@ -1,6 +1,10 @@ import { parse } from "./parse_tree.ts"; import buildMarkdown from "./parser.ts"; -import { findNodeOfType, renderToText } from "../plug-api/lib/tree.ts"; +import { + collectNodesOfType, + findNodeOfType, + renderToText, +} from "../plug-api/lib/tree.ts"; import { assertEquals, assertNotEquals } from "../test_deps.ts"; const sample1 = `--- @@ -11,6 +15,8 @@ tags: --- # This is a doc +Here is a [[wiki link]] and a [[wiki link|alias]]. + Supper`; const sampleInvalid1 = `--- @@ -25,9 +31,21 @@ Deno.test("Test parser", () => { lang, sample1, ); - console.log("tree", JSON.stringify(tree, null, 2)); + // console.log("tree", JSON.stringify(tree, null, 2)); // Check if rendering back to text works assertEquals(renderToText(tree), sample1); + + // Find wiki link and wiki link alias + const links = collectNodesOfType(tree, "WikiLink"); + assertEquals(links.length, 2); + const nameNode = findNodeOfType(links[0], "WikiLinkPage"); + assertEquals(nameNode?.children![0].text, "wiki link"); + + // Check if alias is parsed properly + const aliasNode = findNodeOfType(links[1], "WikiLinkAlias"); + assertEquals(aliasNode?.children![0].text, "alias"); + + // Find frontmatter let node = findNodeOfType(tree, "FrontMatter"); assertNotEquals(node, undefined); tree = parse(lang, sampleInvalid1); diff --git a/common/parser.ts b/common/parser.ts index 48e6a0dc..2314b3e4 100644 --- a/common/parser.ts +++ b/common/parser.ts @@ -20,10 +20,10 @@ import { mdExtensionSyntaxConfig, } from "./markdown_ext.ts"; -export const pageLinkRegex = /^\[\[([^\]]+)\]\]/; +export const pageLinkRegex = /^\[\[([^\]\|]+)(\|([^\]]+))?\]\]/; const WikiLink: MarkdownConfig = { - defineNodes: ["WikiLink", "WikiLinkPage", { + defineNodes: ["WikiLink", "WikiLinkPage", "WikiLinkAlias", { name: "WikiLinkMark", style: t.processingInstruction, }], @@ -38,11 +38,25 @@ const WikiLink: MarkdownConfig = { ) { return -1; } + const [_fullMatch, page, pipePart, label] = match; const endPos = pos + match[0].length; + let aliasElts: any[] = []; + if (pipePart) { + const pipeStartPos = pos + 2 + page.length; + aliasElts = [ + cx.elt("WikiLinkMark", pipeStartPos, pipeStartPos + 1), + cx.elt( + "WikiLinkAlias", + pipeStartPos + 1, + pipeStartPos + 1 + label.length, + ), + ]; + } return cx.addElement( cx.elt("WikiLink", pos, endPos, [ cx.elt("WikiLinkMark", pos, pos + 2), - cx.elt("WikiLinkPage", pos + 2, endPos - 2), + cx.elt("WikiLinkPage", pos + 2, pos + 2 + page.length), + ...aliasElts, cx.elt("WikiLinkMark", endPos - 2, endPos), ]), ); @@ -222,6 +236,7 @@ export default function buildMarkdown(mdExtensions: MDExt[]): Language { styleTags({ WikiLink: ct.WikiLinkTag, WikiLinkPage: ct.WikiLinkPageTag, + WikiLinkAlias: ct.WikiLinkPageTag, // CommandLink: ct.CommandLinkTag, // CommandLinkName: ct.CommandLinkNameTag, Task: ct.TaskTag, diff --git a/web/cm_plugins/wiki_link.ts b/web/cm_plugins/wiki_link.ts index 1e95047b..ce642c26 100644 --- a/web/cm_plugins/wiki_link.ts +++ b/web/cm_plugins/wiki_link.ts @@ -1,3 +1,4 @@ +import { pageLinkRegex } from "../../common/parser.ts"; import { Decoration, DecorationSet, @@ -29,39 +30,54 @@ class CleanWikiLinkPlugin { // let parentRange: [number, number]; iterateTreeInVisibleRanges(view, { enter: ({ type, from, to }) => { - if (type.name === "WikiLinkPage") { + if (type.name === "WikiLink") { // Adding 2 on each side due to [[ and ]] that are outside the WikiLinkPage node - if (isCursorInRange(view.state, [from - 2, to + 2])) { + if (isCursorInRange(view.state, [from, to])) { return; } // Add decoration to hide the prefix [[ widgets.push( invisibleDecoration.range( - from - 2, from, + from + 2, ), ); // Add decoration to hide the postfix [[ widgets.push( invisibleDecoration.range( + to - 2, to, - to + 2, ), ); - // Now check if there's a "/" inside + // Now check if this page has an alias const text = view.state.sliceDoc(from, to); - if (text.indexOf("/") === -1) { - return; + const match = pageLinkRegex.exec(text); + if (!match) return; + const [_fullMatch, page, pipePart] = match; + + if (!pipePart) { + // No alias, let's check if there's a slash in the page name + if (text.indexOf("/") === -1) { + return; + } + // Add a inivisible decoration to hide the path prefix + widgets.push( + invisibleDecoration.range( + from + 2, // +2 to skip the [[ + from + text.lastIndexOf("/") + 1, + ), + ); + } else { + // Alias is present, so we hide the part before the pipe + widgets.push( + invisibleDecoration.range( + from + 2, + from + page.length + 3, // 3 is for the [[ and the | + ), + ); } - // Add a inivisible decoration to hide the path prefix - widgets.push( - invisibleDecoration.range( - from, - from + text.lastIndexOf("/") + 1, - ), - ); } }, }); diff --git a/website/CHANGELOG.md b/website/CHANGELOG.md index 023f13c6..7e87e1a5 100644 --- a/website/CHANGELOG.md +++ b/website/CHANGELOG.md @@ -1,11 +1,12 @@ -An attempt at documenting of the changes/new features introduced in each +An attempt at documenting the changes/new features introduced in each release. --- ## 0.2.2 -* Removed `publish` out of builtins, this will be available via [silverbullet-publish](https://github.com/silverbulletmd/silverbullet-publish) later. -* Added `invokeFunction` command to run arbitrary functions from the CLI. + +* New page link aliasing syntax (Obsidian compatible) is here: `[[page link|alias]]` e.g. [[CHANGELOG|this is a link to this changelog]]. +* Added `invokeFunction` `silverbullet` CLI sub-command to run arbitrary plug functions from the CLI. ---