diff --git a/packages/app-cli/tests/MdToHtml.ts b/packages/app-cli/tests/MdToHtml.ts index 44831ace44a..8da7784ee40 100644 --- a/packages/app-cli/tests/MdToHtml.ts +++ b/packages/app-cli/tests/MdToHtml.ts @@ -252,4 +252,41 @@ describe('MdToHtml', () => { ); } })); + + it('should attach source blocks to block KaTeX', async () => { + const mdToHtml = newTestMdToHtml(); + + const katex = [ + '3 + 3', + '\n\\int_0^1 x dx\n\n', + '\n\\int_0^1 x dx\n3 + 3\n', + '\n\t2^{3^4}\n\t3 + 3\n', + '3\n4', + ]; + const surroundingTextChoices = [ + ['', ''], + ['Test', ''], + ['Test', 'Test!'], + ['Test\n\n', '\n\nTest!'], + ]; + + const tests = []; + for (const texSource of katex) { + for (const [start, end] of surroundingTextChoices) { + tests.push([texSource, `${start}\n$$${texSource}$$\n${end}`]); + } + } + + for (const [tex, input] of tests) { + const html = await mdToHtml.render(input, null, { bodyOnly: true }); + + const opening = '
'; + const closing = ''; + + // Remove any single leading and trailing newlines, those are included in data-joplin-source-open + // and data-joplin-source-close. + const trimmedTex = tex.replace(/^[\n]/, '').replace(/[\n]$/, ''); + expect(html.html).toContain(opening + trimmedTex + closing); + } + }); }); diff --git a/packages/renderer/MdToHtml/rules/katex.ts b/packages/renderer/MdToHtml/rules/katex.ts index 95ba21ccf65..239fa3f1523 100644 --- a/packages/renderer/MdToHtml/rules/katex.ts +++ b/packages/renderer/MdToHtml/rules/katex.ts @@ -246,9 +246,24 @@ function math_block(state: any, start: number, end: number, silent: boolean) { state.line = next + 1; + const contentLines = []; + if (firstLine && firstLine.trim()) { + contentLines.push(firstLine); + } + + const includeTrailingNewline = false; + const interiorLines = state.getLines(start + 1, next, state.tShift[start], includeTrailingNewline); + if (interiorLines.length > 0) { + contentLines.push(interiorLines); + } + + if (lastLine && lastLine.trim()) { + contentLines.push(lastLine); + } + const token = state.push('math_block', 'math', 0); token.block = true; - token.content = (firstLine && firstLine.trim() ? `${firstLine}\n` : '') + state.getLines(start + 1, next, state.tShift[start], true) + (lastLine && lastLine.trim() ? lastLine : ''); + token.content = contentLines.join('\n'); token.map = [start, state.line]; token.markup = '$$'; return true; @@ -312,6 +327,7 @@ export default { } catch (error) { outputHtml = renderKatexError(latex, error); } + return `
${markdownIt.utils.escapeHtml(latex)}${outputHtml}