diff --git a/src/Tokenizer.ts b/src/Tokenizer.ts index 4f176de664..ec1da32dd7 100644 --- a/src/Tokenizer.ts +++ b/src/Tokenizer.ts @@ -3,6 +3,7 @@ import { rtrim, splitCells, findClosingBracket, + expandTabs, } from './helpers.ts'; import type { Rules } from './rules.ts'; import type { _Lexer } from './Lexer.ts'; @@ -267,7 +268,7 @@ export class _Tokenizer { raw = cap[0]; src = src.substring(raw.length); - let line = cap[2].split('\n', 1)[0].replace(this.rules.other.listReplaceTabs, (t: string) => ' '.repeat(3 * t.length)); + let line = expandTabs(cap[2].split('\n', 1)[0], cap[1].length); let nextLine = src.split('\n', 1)[0]; let blankLine = !line.trim(); @@ -278,7 +279,7 @@ export class _Tokenizer { } else if (blankLine) { indent = cap[1].length + 1; } else { - indent = cap[2].search(this.rules.other.nonSpaceChar); // Find first non-space char + indent = line.search(this.rules.other.nonSpaceChar); // Find first non-space char indent = indent > 4 ? 1 : indent; // Treat indented code blocks (> 4 spaces) as having only 1 indent itemContents = line.slice(indent); indent += cap[1].length; diff --git a/src/helpers.ts b/src/helpers.ts index 16f07d2654..7c009e4525 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -147,3 +147,20 @@ export function findClosingBracket(str: string, b: string) { return -1; } + +export function expandTabs(line: string, indent = 0) { + let col = indent; + let expanded = ''; + for (const char of line) { + if (char === '\t') { + const added = 4 - (col % 4); + expanded += ' '.repeat(added); + col += added; + } else { + expanded += char; + col++; + } + } + + return expanded; +} diff --git a/src/rules.ts b/src/rules.ts index 2f95b80be2..73e0f0d912 100644 --- a/src/rules.ts +++ b/src/rules.ts @@ -44,7 +44,6 @@ export const other = { blockquoteStart: /^ {0,3}>/, blockquoteSetextReplace: /\n {0,3}((?:=+|-+) *)(?=\n|$)/g, blockquoteSetextReplace2: /^ {0,3}>[ \t]?/gm, - listReplaceTabs: /^\t+/, listReplaceNesting: /^ {1,4}(?=( {4})*[^ ])/g, listIsTask: /^\[[ xX]\] +\S/, listReplaceTask: /^\[[ xX]\] +/, @@ -158,7 +157,7 @@ const paragraph = edit(_paragraph) .replace('|table', '') .replace('blockquote', ' {0,3}>') .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n') - .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt + .replace('list', ' {0,3}(?:[*+-]|1[.)])[ \\t]') // only lists starting from 1 can interrupt .replace('html', ')|<(?:script|pre|style|textarea|!--)') .replace('tag', _tag) // pars can be interrupted by type (6) html blocks .getRegex(); @@ -202,7 +201,7 @@ const gfmTable = edit( .replace('blockquote', ' {0,3}>') .replace('code', '(?: {4}| {0,3}\t)[^\\n]') .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n') - .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt + .replace('list', ' {0,3}(?:[*+-]|1[.)])[ \\t]') // only lists starting from 1 can interrupt .replace('html', ')|<(?:script|pre|style|textarea|!--)') .replace('tag', _tag) // tables can be interrupted by type (6) html blocks .getRegex(); @@ -218,7 +217,7 @@ const blockGfm: Record = { .replace('table', gfmTable) // interrupt paragraphs with table .replace('blockquote', ' {0,3}>') .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n') - .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt + .replace('list', ' {0,3}(?:[*+-]|1[.)])[ \\t]') // only lists starting from 1 can interrupt .replace('html', ')|<(?:script|pre|style|textarea|!--)') .replace('tag', _tag) // pars can be interrupted by type (6) html blocks .getRegex(), diff --git a/test/specs/new/list_item_tabs.html b/test/specs/new/list_item_tabs.html index e1b33013ea..9b07ed5811 100644 --- a/test/specs/new/list_item_tabs.html +++ b/test/specs/new/list_item_tabs.html @@ -1,17 +1,64 @@
    -
  • A
  • -
  • B
  • -
  • C
  • -
  • D
  • -
  • E
  • -
  • F
  • +
  • A
  • +
  • B
  • +
  • C
  • +
  • D
  • +
  • E
  • +
  • F
-
    -
  1. A
  2. -
  3. B
  4. -
  5. C
  6. -
  7. D
  8. -
  9. E
  10. -
  11. F
  12. +
  13. A
  14. +
  15. B
  16. +
  17. C
  18. +
  19. D
  20. +
  21. E
  22. +
  23. F
+

I am using spaces after the number:

+
    +
  1. Some Text
  2. +
  3. Some Text
  4. +
  5. Some Text
  6. +
+

I am using tabs after the number:

+
    +
  1. SomeText
  2. +
  3. SomeText
  4. +
  5. SomeText
  6. +
+

I am using tabs after the number and have a space:

+
    +
  1. SomeText
  2. +
  3. SomeText
  4. +
  5. SomeText
  6. +
+

I am using space after the number and have a tab:

+
    +
  1. SomeText
  2. +
  3. SomeText
  4. +
  5. SomeText
  6. +
+

I am using spaces after the bullet:

+
    +
  • Some Text
  • +
  • Some Text
  • +
  • Some Text
  • +
+

I am using tabs after the bullet:

+
    +
  • SomeText
  • +
  • SomeText
  • +
  • SomeText
  • +
+

I am using tabs after the bullet and have a space:

+
    +
  • SomeText
  • +
  • SomeText
  • +
  • SomeText
  • +
+

I am using space after the bullet and have a tab:

+
    +
  • SomeText
  • +
  • SomeText
  • +
  • SomeText
  • +
diff --git a/test/specs/new/list_item_tabs.md b/test/specs/new/list_item_tabs.md index b42c88b8cc..25d7f4fc52 100644 --- a/test/specs/new/list_item_tabs.md +++ b/test/specs/new/list_item_tabs.md @@ -11,3 +11,47 @@ 4. D 5. E 6. F + +I am using spaces after the number: +1. Some Text +3. Some Text +4. Some Text + +I am using tabs after the number: +1. SomeText +2. SomeText +3. SomeText + +I am using tabs after the number and have a space: + +1. SomeText +2. SomeText +3. SomeText + +I am using space after the number and have a tab: + +1. SomeText +2. SomeText +3. SomeText + +I am using spaces after the bullet: +- Some Text +- Some Text +- Some Text + +I am using tabs after the bullet: +- SomeText +- SomeText +- SomeText + +I am using tabs after the bullet and have a space: + +- SomeText +- SomeText +- SomeText + +I am using space after the bullet and have a tab: + +- SomeText +- SomeText +- SomeText