From 84bf9b5a3a5b1e2261121b2a36e1df058dc4b486 Mon Sep 17 00:00:00 2001 From: mck09 Date: Tue, 1 Sep 2026 20:30:21 +0200 Subject: [PATCH 1/2] fix: strip a tab that follows spaces in an indented code block codeRemoveIndent tried its space alternative first, so a line indented with spaces then a tab lost only the spaces and kept the tab as content. Trying the tab alternative first matches CommonMark example 2, where a tab advances to the next four column stop and the whole run is indent. --- src/rules.ts | 2 +- test/unit/marked.test.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/rules.ts b/src/rules.ts index aeb880c597..92fe66498a 100644 --- a/src/rules.ts +++ b/src/rules.ts @@ -41,7 +41,7 @@ try { })(); export const other = { - codeRemoveIndent: /^(?: {1,4}| {0,3}\t)/gm, + codeRemoveIndent: /^(?: {0,3}\t| {1,4})/gm, outputLinkReplace: /\\([\[\]])/g, indentCodeCompensation: /^(\s+)(?:```)/, beginningSpace: /^\s+/, diff --git a/test/unit/marked.test.js b/test/unit/marked.test.js index 5d1449b4aa..dabbe9f594 100644 --- a/test/unit/marked.test.js +++ b/test/unit/marked.test.js @@ -22,6 +22,34 @@ describe('marked unit', () => { }); }); + // The spec suite compares through htmlIsEqual, which leaves ignoreWhitespaces at its default + // of true, so it cannot see indentation inside pre. These assert exact output. + describe('tab expansion in block structure', () => { + it('should treat spaces then a tab as reaching the next four column stop', () => { + assert.strictEqual(marked.parse(' \tfoo\n'), '
foo\n
\n'); + }); + + it('should keep tabs in the content of an indented code block', () => { + assert.strictEqual(marked.parse(' \tfoo\tbaz\t\tbim\n'), '
foo\tbaz\t\tbim\n
\n'); + }); + + it('should strip three spaces and a tab', () => { + assert.strictEqual(marked.parse(' \tfoo\n'), '
foo\n
\n'); + }); + + it('should still strip a lone leading tab', () => { + assert.strictEqual(marked.parse('\tfoo\n'), '
foo\n
\n'); + }); + + it('should still strip exactly four spaces', () => { + assert.strictEqual(marked.parse(' foo\n'), '
foo\n
\n'); + }); + + it('should keep a fifth space as content', () => { + assert.strictEqual(marked.parse(' foo\n'), '
 foo\n
\n'); + }); + }); + describe('changeDefaults', () => { it('should change global defaults', async() => { const { defaults, setOptions } = await import('../../lib/marked.esm.js'); From e6490be223100a2a5a3a944d65d4c9089da0f2a3 Mon Sep 17 00:00:00 2001 From: mck09 Date: Tue, 1 Sep 2026 20:41:38 +0200 Subject: [PATCH 2/2] test: move the tab tests to test/specs/new Uses renderExact so the fixture compares exactly, which htmlIsEqual does not, and keeps the assertions out of marked.test.js. --- test/specs/new/tabs_indented_code.html | 14 +++++++++++++ test/specs/new/tabs_indented_code.md | 20 ++++++++++++++++++ test/unit/marked.test.js | 28 -------------------------- 3 files changed, 34 insertions(+), 28 deletions(-) create mode 100644 test/specs/new/tabs_indented_code.html create mode 100644 test/specs/new/tabs_indented_code.md diff --git a/test/specs/new/tabs_indented_code.html b/test/specs/new/tabs_indented_code.html new file mode 100644 index 0000000000..d036d907c6 --- /dev/null +++ b/test/specs/new/tabs_indented_code.html @@ -0,0 +1,14 @@ +
foo	baz		bim
+
+

sep

+
three spaces and a tab
+
+

sep

+
lone tab
+
+

sep

+
four spaces
+
+

sep

+
 five spaces
+
diff --git a/test/specs/new/tabs_indented_code.md b/test/specs/new/tabs_indented_code.md new file mode 100644 index 0000000000..86081e613d --- /dev/null +++ b/test/specs/new/tabs_indented_code.md @@ -0,0 +1,20 @@ +--- +renderExact: true +--- + foo baz bim + +sep + + three spaces and a tab + +sep + + lone tab + +sep + + four spaces + +sep + + five spaces diff --git a/test/unit/marked.test.js b/test/unit/marked.test.js index dabbe9f594..5d1449b4aa 100644 --- a/test/unit/marked.test.js +++ b/test/unit/marked.test.js @@ -22,34 +22,6 @@ describe('marked unit', () => { }); }); - // The spec suite compares through htmlIsEqual, which leaves ignoreWhitespaces at its default - // of true, so it cannot see indentation inside pre. These assert exact output. - describe('tab expansion in block structure', () => { - it('should treat spaces then a tab as reaching the next four column stop', () => { - assert.strictEqual(marked.parse(' \tfoo\n'), '
foo\n
\n'); - }); - - it('should keep tabs in the content of an indented code block', () => { - assert.strictEqual(marked.parse(' \tfoo\tbaz\t\tbim\n'), '
foo\tbaz\t\tbim\n
\n'); - }); - - it('should strip three spaces and a tab', () => { - assert.strictEqual(marked.parse(' \tfoo\n'), '
foo\n
\n'); - }); - - it('should still strip a lone leading tab', () => { - assert.strictEqual(marked.parse('\tfoo\n'), '
foo\n
\n'); - }); - - it('should still strip exactly four spaces', () => { - assert.strictEqual(marked.parse(' foo\n'), '
foo\n
\n'); - }); - - it('should keep a fifth space as content', () => { - assert.strictEqual(marked.parse(' foo\n'), '
 foo\n
\n'); - }); - }); - describe('changeDefaults', () => { it('should change global defaults', async() => { const { defaults, setOptions } = await import('../../lib/marked.esm.js');