« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/util/replacer.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/replacer.js')
-rw-r--r--src/util/replacer.js87
1 files changed, 85 insertions, 2 deletions
diff --git a/src/util/replacer.js b/src/util/replacer.js
index 0a3117e..d1b0a26 100644
--- a/src/util/replacer.js
+++ b/src/util/replacer.js
@@ -5,6 +5,8 @@
 // function, which converts nodes parsed here into actual HTML, links, etc
 // for embedding in a wiki webpage.
 
+import * as marked from 'marked';
+
 import * as html from '#html';
 import {escapeRegex, typeAppearance} from '#sugar';
 
@@ -460,7 +462,14 @@ export function postprocessImages(inputNodes) {
       let match = null, parseFrom = 0;
       while (match = imageRegexp.exec(node.data)) {
         const previousText = node.data.slice(parseFrom, match.index);
-        outputNodes.push({type: 'text', data: previousText});
+
+        outputNodes.push({
+          type: 'text',
+          data: previousText,
+          i: node.i + parseFrom,
+          iEnd: node.i + parseFrom + match.index,
+        });
+
         parseFrom = match.index + match[0].length;
 
         const imageNode = {type: 'image'};
@@ -532,6 +541,8 @@ export function postprocessImages(inputNodes) {
         outputNodes.push({
           type: 'text',
           data: node.data.slice(parseFrom),
+          i: node.i + parseFrom,
+          iEnd: node.iEnd,
         });
       }
 
@@ -574,7 +585,78 @@ export function postprocessHeadings(inputNodes) {
       textContent += node.data.slice(parseFrom);
     }
 
-    outputNodes.push({type: 'text', data: textContent});
+    outputNodes.push({
+      type: 'text',
+      data: textContent,
+      i: node.i,
+      iEnd: node.iEnd,
+    });
+  }
+
+  return outputNodes;
+}
+
+export function postprocessExternalLinks(inputNodes) {
+  const outputNodes = [];
+
+  for (const node of inputNodes) {
+    if (node.type !== 'text') {
+      outputNodes.push(node);
+      continue;
+    }
+
+    const plausibleLinkRegexp = /\[.*?\)/g;
+
+    let textContent = '';
+
+    let plausibleMatch = null, parseFrom = 0;
+    while (plausibleMatch = plausibleLinkRegexp.exec(node.data)) {
+      textContent += node.data.slice(parseFrom, plausibleMatch.index);
+
+      // Pedantic rules use more particular parentheses detection in link
+      // destinations - they allow one level of balanced parentheses, and
+      // otherwise, parentheses must be escaped. This allows for entire links
+      // to be wrapped in parentheses, e.g below:
+      //
+      //   This is so cool. ([You know??](https://example.com))
+      //
+      const definiteMatch =
+        marked.Lexer.rules.inline.pedantic.link
+          .exec(node.data.slice(plausibleMatch.index));
+
+      if (definiteMatch) {
+        const {1: label, 2: href} = definiteMatch;
+
+        // Split the containing text node into two - the second of these will
+        // be added after iterating over matches, or by the next match.
+        if (textContent.length) {
+          outputNodes.push({type: 'text', data: textContent});
+          textContent = '';
+        }
+
+        const offset = plausibleMatch.index + definiteMatch.index;
+        const length = definiteMatch[0].length;
+
+        outputNodes.push({
+          i: node.i + offset,
+          iEnd: node.i + offset + length,
+          type: 'external-link',
+          data: {label, href},
+        });
+
+        parseFrom = offset + length;
+      } else {
+        parseFrom = plausibleMatch.index;
+      }
+    }
+
+    if (parseFrom !== node.data.length) {
+      textContent += node.data.slice(parseFrom);
+    }
+
+    if (textContent.length) {
+      outputNodes.push({type: 'text', data: textContent});
+    }
   }
 
   return outputNodes;
@@ -589,6 +671,7 @@ export function parseInput(input) {
     let output = parseNodes(input, 0);
     output = postprocessImages(output);
     output = postprocessHeadings(output);
+    output = postprocessExternalLinks(output);
     return output;
   } catch (errorNode) {
     if (errorNode.type !== 'error') {