implement wikilinks in viewer
authorThomas Crescenzi <redacted>
Sat, 4 Nov 2023 00:30:55 +0000 (20:30 -0400)
committerThomas Crescenzi <redacted>
Sat, 4 Nov 2023 00:33:39 +0000 (20:33 -0400)
flatnotes/src/autolinkParsers.js [new file with mode: 0644]
flatnotes/src/components/NoteViewerEditor.vue

diff --git a/flatnotes/src/autolinkParsers.js b/flatnotes/src/autolinkParsers.js
new file mode 100644 (file)
index 0000000..cc65895
--- /dev/null
@@ -0,0 +1,94 @@
+function parseWikiLink(source) {
+  const matched = source.matchAll(/\[\[(.*)\]\]/g);
+  if (matched) {
+    return Array.from(matched).map(match => {
+      const text = match[1];
+      return {
+        text,
+        url: encodeURI(`/note/${text}`),
+        range: [match.index, match.index + match[0].length - 1]
+      }
+    });
+  }
+
+  return null;
+}
+
+export function extendedAutolinks(source) {
+  return [...parseUrlLink(source), ...parseEmailLink(source), ...parseWikiLink(source)].sort(
+    (a, b) => a.range[0] - b.range[0]
+  );
+}
+
+/*
+ * Sourced from toast-ui. There autolink options are
+ * either override their built in functionality or 
+ * use their built in functionality. We'd like to have
+ * both so this is the source of their parsers.
+*/
+const DOMAIN = '(?:[w-]+.)*[A-Za-z0-9-]+.[A-Za-z0-9-]+';
+const PATH = '[^<\\s]*[^<?!.,:*_?~\\s]';
+const EMAIL = '[\\w.+-]+@(?:[\\w-]+\\.)+[\\w-]+';
+function trimUnmatchedTrailingParens(source) {
+  const trailingParen = /\)+$/.exec(source);
+  if (trailingParen) {
+    let count = 0;
+    for (const ch of source) {
+      if (ch === '(') {
+        if (count < 0) {
+          count = 1;
+        } else {
+          count += 1;
+        }
+      } else if (ch === ')') {
+        count -= 1;
+      }
+    }
+
+    if (count < 0) {
+      const trimCount = Math.min(-count, trailingParen[0].length);
+      return source.substring(0, source.length - trimCount);
+    }
+  }
+  return source;
+}
+
+function trimTrailingEntity(source) {
+  return source.replace(/&[A-Za-z0-9]+;$/, '');
+}
+export function parseEmailLink(source) {
+  const reEmailLink = new RegExp(EMAIL, 'g');
+  const result = [];
+  let m;
+  while ((m = reEmailLink.exec(source))) {
+    const text = m[0];
+    if (!/[_-]+$/.test(text)) {
+      result.push({
+        text,
+        range: [m.index, m.index + text.length - 1],
+        url: `mailto:${text}`,
+      });
+    }
+  }
+
+  return result;
+}
+
+export function parseUrlLink(source) {
+  const reWwwAutolink = new RegExp(`(www|https?://)\.${DOMAIN}${PATH}`, 'g');
+  const result = [];
+  let m;
+
+  while ((m = reWwwAutolink.exec(source))) {
+    const text = trimTrailingEntity(trimUnmatchedTrailingParens(m[0]));
+    const scheme = m[1] === 'www' ? 'http://' : '';
+    result.push({
+      text,
+      range: [m.index, m.index + text.length - 1],
+      url: `${scheme}${text}`,
+    });
+  }
+
+  return result;
+}
+// end of raw toast-ui source
index ede6ea08d6b1d741b7091f9ac070d90580e151c4..0b2657fa85277ae4e31eb5eee77b17106c79fbc0 100644 (file)
@@ -185,6 +185,7 @@ import { Note } from "../classes";
 import { Viewer } from "@toast-ui/vue-editor";
 import api from "../api";
 import codeSyntaxHighlight from "@toast-ui/editor-plugin-code-syntax-highlight/dist/toastui-editor-plugin-code-syntax-highlight-all.js";
+import { extendedAutolinks } from '../autolinkParsers';
 
 const reservedFilenameCharacters = /[<>:"/\\|?*]/;
 
@@ -234,7 +235,7 @@ export default {
       viewerOptions: {
         customHTMLRenderer: customHTMLRenderer,
         plugins: [codeSyntaxHighlight],
-        extendedAutolinks: true,
+        extendedAutolinks,
       },
       editorOptions: {
         customHTMLRenderer: customHTMLRenderer,
git clone https://git.99rst.org/PROJECT