diff options
Diffstat (limited to 'src/util/replacer.js')
-rw-r--r-- | src/util/replacer.js | 87 |
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') { |