Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions demo.js
Original file line number Diff line number Diff line change
@@ -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();
});
87 changes: 86 additions & 1 deletion src/index.test.ts
Original file line number Diff line number Diff line change
@@ -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) => {
Expand Down Expand Up @@ -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", () => {
Expand Down Expand Up @@ -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 `<span class="${classList}">${str}</span>`;
});
});

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 \\\\\\\\ <span class=\"katex\">\\begin{pmatrix} a \\\\ b \\end{pmatrix}</span> \\\\\\\\ 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 = `<span class="katex">${mathContent}</span>`;

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: <span class=\"katex\">x=1</span> \\\\\\\\ Final line";
const result = renderMath(input);
expect(result).toBe(expected);
});
});
});
15 changes: 14 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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), {
Expand All @@ -281,6 +293,7 @@ export {
unescapeCharacters,
revertEscapedCharacters,
parseDelimiters,
convertNewlinesToLatexBreaks,
// Export types for use in tests
ParsedSegment,
Delimiter,
Expand Down