/* * MarkdownCode is a named function that will parse `inline code` and ```codeblock``` syntaxes * @param {Object} message - The message object */ import hljs from 'highlight.js'; class MarkdownCode { constructor(message) { if (s.trim(message.html)) { if (message.tokens == null) { message.tokens = []; } MarkdownCode.handle_codeblocks(message); MarkdownCode.handle_inlinecode(message); if (window && window.rocketDebug) { console.log('Markdown', message); } } return message; } static handle_inlinecode(message) { // Support `text` return message.html = message.html.replace(/(^|>|[ >_*~])\`([^`\r\n]+)\`([<_*~]|\B|\b|$)/gm, (match, p1, p2, p3) => { const token = `=!=${ Random.id() }=!=`; message.tokens.push({ token, text: `${ p1 }\`${ p2 }\`${ p3 }`, noHtml: match }); return token; }); } static handle_codeblocks(message) { // Count occurencies of ``` const count = (message.html.match(/```/g) || []).length; if (count) { // Check if we need to add a final ``` if ((count % 2) > 0) { message.html = `${ message.html }\n\`\`\``; message.msg = `${ message.msg }\n\`\`\``; } // Separate text in code blocks and non code blocks const msgParts = message.html.split(/(^.*)(```(?:[a-zA-Z]+)?(?:(?:.|\n)*?)```)(.*\n?)$/gm); for (let index = 0; index < msgParts.length; index++) { // Verify if this part is code const part = msgParts[index]; const codeMatch = part.match(/^```(.*[\n\ ]?)([\s\S]*?)```+?$/); if (codeMatch != null) { // Process highlight if this part is code let code; let lang; let result; const singleLine = codeMatch[0].indexOf('\n') === -1; if (singleLine) { lang = ''; code = _.unescapeHTML(codeMatch[1] + codeMatch[2]); } else { lang = codeMatch[1]; code = _.unescapeHTML(codeMatch[2]); } if (s.trim(lang) === '') { lang = ''; } if (!Array.from(hljs.listLanguages()).includes(s.trim(lang))) { result = hljs.highlightAuto((lang + code)); } else { result = hljs.highlight(s.trim(lang), code); } const token = `=!=${ Random.id() }=!=`; message.tokens.push({ highlight: true, token, text: `
\`\`\`
${ result.value }
\`\`\`
`, noHtml: `\`\`\`\n${ s.stripTags(result.value) }\n\`\`\`` }); msgParts[index] = token; } else { msgParts[index] = part; } } // Re-mount message return message.html = msgParts.join(''); } } } RocketChat.MarkdownCode = MarkdownCode; const MarkdownCodeCB = (message) => new MarkdownCode(message); // MarkdownCode gets higher priority over Markdown so it's possible place a callback in between (katex for exmaple) RocketChat.callbacks.add('renderMessage', MarkdownCodeCB, RocketChat.callbacks.priority.HIGH - 2, 'markdowncode');