diff --git a/plugins/core/navigate.ts b/plugins/core/navigate.ts index 756a11ca..fc703462 100644 --- a/plugins/core/navigate.ts +++ b/plugins/core/navigate.ts @@ -1,22 +1,35 @@ import { ClickEvent } from "../../webapp/src/app_event.ts"; import { syscall } from "./lib/syscall.ts"; -export async function linkNavigate() { - let syntaxNode = await syscall("editor.getSyntaxNodeUnderCursor"); - if (syntaxNode && syntaxNode.name === "WikiLinkPage") { - await syscall("editor.navigate", syntaxNode.text); +async function navigate(syntaxNode: any) { + if (!syntaxNode) { + return; + } + console.log("Attempting to navigate based on syntax node", syntaxNode); + switch (syntaxNode.name) { + case "WikiLinkPage": + await syscall("editor.navigate", syntaxNode.text); + break; + case "URL": + await syscall("editor.openUrl", syntaxNode.text); + break; + case "Link": + // Markdown link: [bla](URLHERE) needs extraction + let match = /\[[^\\]+\]\(([^\)]+)\)/.exec(syntaxNode.text); + if (match) { + await syscall("editor.openUrl", match[1]); + } } } -export async function clickNavigate(event: ClickEvent) { - let syntaxNode = await syscall("editor.getSyntaxNodeAtPos", event.pos); +export async function linkNavigate() { + navigate(await syscall("editor.getSyntaxNodeUnderCursor")); +} +export async function clickNavigate(event: ClickEvent) { if (event.ctrlKey || event.metaKey) { - console.log("Here", syntaxNode); - if (syntaxNode && syntaxNode.name === "WikiLinkPage") { - await syscall("editor.navigate", syntaxNode.text); - return; - } + let syntaxNode = await syscall("editor.getSyntaxNodeAtPos", event.pos); + navigate(syntaxNode); } } diff --git a/plugins/core/page.ts b/plugins/core/page.ts index f077a8fa..c4c312a2 100644 --- a/plugins/core/page.ts +++ b/plugins/core/page.ts @@ -5,7 +5,6 @@ import { syscall } from "./lib/syscall.ts"; const wikilinkRegex = new RegExp(pageLinkRegex, "g"); export async function indexLinks({ name, text }: IndexEvent) { - console.log("Now indexing", name); let backLinks: { key: string; value: string }[] = []; for (let match of text.matchAll(wikilinkRegex)) { let toPage = match[1]; diff --git a/webapp/src/parser.ts b/webapp/src/parser.ts index f7c297ba..971745ec 100644 --- a/webapp/src/parser.ts +++ b/webapp/src/parser.ts @@ -1,4 +1,4 @@ -import { styleTags } from "@codemirror/highlight"; +import { styleTags, tags as t } from "@codemirror/highlight"; import { MarkdownConfig, TaskList } from "@lezer/markdown"; import { commonmark, mkLang } from "./markdown/markdown"; import * as ct from "./customtags"; @@ -53,8 +53,32 @@ const AtMention: MarkdownConfig = { }, ], }; + +const urlRegexp = + /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/; + +const UnmarkedUrl: MarkdownConfig = { + defineNodes: ["URL"], + parseInline: [ + { + name: "URL", + parse(cx, next, pos) { + let match: RegExpMatchArray | null; + if ( + next != 104 /* 'h' */ || + !(match = urlRegexp.exec(cx.slice(pos, cx.end))) + ) { + return -1; + } + return cx.addElement(cx.elt("URL", pos, pos + match[0].length)); + }, + after: "Emphasis", + }, + ], +}; + const TagLink: MarkdownConfig = { - defineNodes: ["Checkbox"], + defineNodes: ["TagLink"], parseInline: [ { name: "TagLink", @@ -77,6 +101,7 @@ const WikiMarkdown = commonmark.configure([ AtMention, TagLink, TaskList, + UnmarkedUrl, { props: [ styleTags({ @@ -86,6 +111,7 @@ const WikiMarkdown = commonmark.configure([ TagLink: ct.TagTag, Task: ct.TaskTag, TaskMarker: ct.TaskMarkerTag, + Url: t.url, }), ], }, diff --git a/webapp/src/styles.css b/webapp/src/styles.css index 12a1e823..33337760 100644 --- a/webapp/src/styles.css +++ b/webapp/src/styles.css @@ -138,6 +138,10 @@ body { .cm-editor .link.url { color: #7e7d7d; } +.cm-editor .url:not(.link) { + color: #0330cb; + text-decoration: underline; +} .cm-editor .wiki-link-page { color: #0330cb; diff --git a/webapp/src/syscalls/editor.browser.ts b/webapp/src/syscalls/editor.browser.ts index 144fa3f1..09c85722 100644 --- a/webapp/src/syscalls/editor.browser.ts +++ b/webapp/src/syscalls/editor.browser.ts @@ -39,6 +39,9 @@ export default (editor: Editor) => ({ "editor.navigate": async (name: string) => { await editor.navigate(name); }, + "editor.openUrl": async (url: string) => { + window.open(url, "_blank")!.focus(); + }, "editor.insertAtPos": (text: string, pos: number) => { editor.editorView!.dispatch({ changes: {