diff --git a/demo.js b/demo.js new file mode 100644 index 0000000..24755bd --- /dev/null +++ b/demo.js @@ -0,0 +1,34 @@ +import { renderMath } from './dist/index.js'; + +// Test cases to demonstrate newline handling +const testCases = [ + { + name: "Basic newline conversion", + input: "First line\nSecond line\nThird line" + }, + { + name: "Newlines with math (should preserve newlines in math)", + input: "Text line 1\nText line 2\n$\\begin{pmatrix} a \\\\ b \\end{pmatrix}$\nText line 3" + }, + { + name: "Multiple consecutive newlines", + input: "Line 1\n\n\nLine 2" + }, + { + name: "Mixed content with escaped characters", + input: "Price: \\$5\nNext line\nMath: $x=1$\nFinal line" + }, + { + name: "LaTeX equation with internal newlines (should be preserved)", + input: "$$\\begin{align}\na &= 1\\\\\nb &= 2\n\\end{align}$$" + } +]; + +console.log("=== Newline Handling Demo ===\n"); + +testCases.forEach((testCase, index) => { + console.log(`${index + 1}. ${testCase.name}`); + console.log(`Input: ${JSON.stringify(testCase.input)}`); + console.log(`Output: ${JSON.stringify(renderMath(testCase.input))}`); + console.log(); +}); \ No newline at end of file diff --git a/src/index.test.ts b/src/index.test.ts index f48c4e2..870ee07 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -1,5 +1,5 @@ import * as katex from "katex"; -import { renderMath, parseDelimiters, ParsedSegment, escapeRegex, replacePredefinedEscapeSequences, unescapeCharacters, revertEscapedCharacters } from "./index"; +import { renderMath, parseDelimiters, ParsedSegment, escapeRegex, replacePredefinedEscapeSequences, unescapeCharacters, revertEscapedCharacters, convertNewlinesToLatexBreaks } from "./index"; jest.mock("katex", () => ({ renderToString: jest.fn((str, options) => { @@ -78,6 +78,35 @@ describe("Helper Functions", () => { expect(revertEscapedCharacters(input)).toBe(input); }); }); + + describe("convertNewlinesToLatexBreaks", () => { + it("should convert single newlines to LaTeX line breaks", () => { + const input = "Line 1\nLine 2"; + const expected = "Line 1 \\\\\\\\ Line 2"; + expect(convertNewlinesToLatexBreaks(input)).toBe(expected); + }); + + it("should convert multiple newlines to multiple LaTeX line breaks", () => { + const input = "Line 1\n\n\nLine 2"; + const expected = "Line 1 \\\\\\\\ \\\\\\\\ \\\\\\\\ Line 2"; + expect(convertNewlinesToLatexBreaks(input)).toBe(expected); + }); + + it("should handle empty strings", () => { + expect(convertNewlinesToLatexBreaks("")).toBe(""); + }); + + it("should handle strings with no newlines", () => { + const input = "No newlines here"; + expect(convertNewlinesToLatexBreaks(input)).toBe(input); + }); + + it("should handle newlines at start and end", () => { + const input = "\nStart\nEnd\n"; + const expected = " \\\\\\\\ Start \\\\\\\\ End \\\\\\\\ "; + expect(convertNewlinesToLatexBreaks(input)).toBe(expected); + }); + }); }); describe("parseDelimiters", () => { @@ -322,4 +351,60 @@ describe("renderMath", () => { expect(result).toContain('title="SomeOtherError: A different issue"'); }); }); + + describe("Newline Handling", () => { + beforeEach(() => { + // Reset mock to default behavior for newline tests + mockedKatex.renderToString.mockImplementation((str, options) => { + const displayClass = options?.displayMode ? "katex-display" : ""; + const classList = ["katex", displayClass].filter(Boolean).join(" "); + return `${str}`; + }); + }); + + it("should convert newlines to LaTeX line breaks in text segments", () => { + const input = "First line\nSecond line\nThird line"; + const expected = "First line \\\\\\\\ Second line \\\\\\\\ Third line"; + const result = renderMath(input); + expect(result).toBe(expected); + }); + + it("should convert newlines in text but preserve them in math", () => { + const input = "Text line 1\nText line 2\n$\\begin{pmatrix} a \\\\ b \\end{pmatrix}$\nText line 3"; + const expected = "Text line 1 \\\\\\\\ Text line 2 \\\\\\\\ \\begin{pmatrix} a \\\\ b \\end{pmatrix} \\\\\\\\ Text line 3"; + const result = renderMath(input); + expect(result).toBe(expected); + }); + + it("should handle multiple consecutive newlines", () => { + const input = "Line 1\n\n\nLine 2"; + const expected = "Line 1 \\\\\\\\ \\\\\\\\ \\\\\\\\ Line 2"; + const result = renderMath(input); + expect(result).toBe(expected); + }); + + it("should handle newlines at start and end of text", () => { + const input = "\nStart with newline\nEnd with newline\n"; + const expected = " \\\\\\\\ Start with newline \\\\\\\\ End with newline \\\\\\\\ "; + const result = renderMath(input); + expect(result).toBe(expected); + }); + + it("should not affect newlines inside LaTeX equations", () => { + const input = "$\\begin{align}\na &= 1\\\\\nb &= 2\n\\end{align}$"; + const mathContent = "\\begin{align}\na &= 1\\\\\nb &= 2\n\\end{align}"; + const expected = `${mathContent}`; + + const result = renderMath(input); + expect(result).toBe(expected); + expect(mockedKatex.renderToString).toHaveBeenCalledWith(mathContent, expect.any(Object)); + }); + + it("should handle mixed newlines and escaped characters", () => { + const input = "Price: \\$5\nNext line\nMath: $x=1$\nFinal line"; + const expected = "Price: $5 \\\\\\\\ Next line \\\\\\\\ Math: x=1 \\\\\\\\ Final line"; + const result = renderMath(input); + expect(result).toBe(expected); + }); + }); }); \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 31e550d..0789bd2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -138,6 +138,16 @@ const revertEscapedCharacters = (input: string): string => { return input.replace(unescapeAllPlaceholdersRegex, (match: string) => mathUnEscapes[match]); }; +/** + * Converts newlines in text content to LaTeX line breaks. + * This function replaces `\n` with ` \\\\ ` which represents a line break in LaTeX. + * @param input - The text content to process. + * @returns The text with newlines converted to LaTeX line breaks. + */ +const convertNewlinesToLatexBreaks = (input: string): string => { + return input.replace(/\n/g, ' \\\\\\\\ '); +}; + /** * Parses an input string to find and extract content enclosed within delimiters. * It segments the input into an array of objects, distinguishing between regular @@ -254,7 +264,9 @@ const renderMath = (input: string): string => { let renderedOutput = ''; parsedInput.forEach((match: ParsedSegment) => { if (match.type == 'text') { - renderedOutput += unescapeCharacters(match.content); + // Convert newlines to LaTeX line breaks in text segments + const textWithLineBreaks = convertNewlinesToLatexBreaks(match.content); + renderedOutput += unescapeCharacters(textWithLineBreaks); } else if (match.type == 'math') { renderedOutput += katex .renderToString(revertEscapedCharacters(match.content), { @@ -281,6 +293,7 @@ export { unescapeCharacters, revertEscapedCharacters, parseDelimiters, + convertNewlinesToLatexBreaks, // Export types for use in tests ParsedSegment, Delimiter,