Skip to content

Commit 07a4c48

Browse files
committed
👌 Remove hard-coded block indent limit
For CommonMark, the presence of indented code blocks prevent any other block element from having an indent of greater than 4 spaces. Certain Markdown flavors and derivatives, such as mdx and djot, disable these code blocks though, since it is more common to use code fences and indenting is desirable. Currently, disabling code blocks does not remove the indent limitation, since most block elements have the 3 space limitation hard-coded. This commit therefore centralises the logic of applying this limitation, and only applies it when indented code blocks are enabled. Note, this is a potential breaking change and divergence from upstream markdown-it, for this niche case, but I feel makes sense.
1 parent e717248 commit 07a4c48

File tree

14 files changed

+100
-27
lines changed

14 files changed

+100
-27
lines changed

markdown_it/main.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from collections.abc import Callable, Generator, Iterable, Mapping, MutableMapping
44
from contextlib import contextmanager
5-
from typing import Any
5+
from typing import Any, overload
66

77
from . import helpers, presets # noqa F401
88
from .common import normalize_url, utils # noqa F401
@@ -20,6 +20,12 @@
2020
linkify_it = None
2121

2222

23+
try:
24+
from typing import Literal
25+
except ImportError: # Literal introduced in python 3.8
26+
from typing_extensions import Literal # type: ignore
27+
28+
2329
_PRESETS = {
2430
"default": presets.default.make(),
2531
"js-default": presets.js_default.make(),
@@ -67,6 +73,26 @@ def __init__(
6773
def __repr__(self) -> str:
6874
return f"{self.__class__.__module__}.{self.__class__.__name__}()"
6975

76+
@overload
77+
def __getitem__(self, name: Literal["inline"]) -> ParserInline:
78+
...
79+
80+
@overload
81+
def __getitem__(self, name: Literal["block"]) -> ParserBlock:
82+
...
83+
84+
@overload
85+
def __getitem__(self, name: Literal["core"]) -> ParserCore:
86+
...
87+
88+
@overload
89+
def __getitem__(self, name: Literal["renderer"]) -> RendererProtocol:
90+
...
91+
92+
@overload
93+
def __getitem__(self, name: str) -> Any:
94+
...
95+
7096
def __getitem__(self, name: str) -> Any:
7197
return {
7298
"inline": self.inline,

markdown_it/rules_block/blockquote.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ def blockquote(state: StateBlock, startLine: int, endLine: int, silent: bool):
1818
pos = state.bMarks[startLine] + state.tShift[startLine]
1919
max = state.eMarks[startLine]
2020

21-
# if it's indented more than 3 spaces, it should be a code block
22-
if (state.sCount[startLine] - state.blkIndent) >= 4:
21+
if state.is_code_block(startLine):
2322
return False
2423

2524
# check the block quote marker

markdown_it/rules_block/code.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
def code(state: StateBlock, startLine: int, endLine: int, silent: bool = False):
1010
LOGGER.debug("entering code: %s, %s, %s, %s", state, startLine, endLine, silent)
1111

12-
if state.sCount[startLine] - state.blkIndent < 4:
12+
if not state.is_code_block(startLine):
1313
return False
1414

1515
last = nextLine = startLine + 1
@@ -19,7 +19,7 @@ def code(state: StateBlock, startLine: int, endLine: int, silent: bool = False):
1919
nextLine += 1
2020
continue
2121

22-
if state.sCount[nextLine] - state.blkIndent >= 4:
22+
if state.is_code_block(nextLine):
2323
nextLine += 1
2424
last = nextLine
2525
continue

markdown_it/rules_block/fence.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ def fence(state: StateBlock, startLine: int, endLine: int, silent: bool):
1313
pos = state.bMarks[startLine] + state.tShift[startLine]
1414
maximum = state.eMarks[startLine]
1515

16-
# if it's indented more than 3 spaces, it should be a code block
17-
if state.sCount[startLine] - state.blkIndent >= 4:
16+
if state.is_code_block(startLine):
1817
return False
1918

2019
if pos + 3 > maximum:
@@ -72,8 +71,7 @@ def fence(state: StateBlock, startLine: int, endLine: int, silent: bool):
7271
except IndexError:
7372
break
7473

75-
if state.sCount[nextLine] - state.blkIndent >= 4:
76-
# closing fence should be indented less than 4 spaces
74+
if state.is_code_block(nextLine):
7775
continue
7876

7977
pos = state.skipChars(pos, marker)

markdown_it/rules_block/heading.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ def heading(state: StateBlock, startLine: int, endLine: int, silent: bool):
1515
pos = state.bMarks[startLine] + state.tShift[startLine]
1616
maximum = state.eMarks[startLine]
1717

18-
# if it's indented more than 3 spaces, it should be a code block
19-
if state.sCount[startLine] - state.blkIndent >= 4:
18+
if state.is_code_block(startLine):
2019
return False
2120

2221
ch: int | None = state.srcCharCode[pos]

markdown_it/rules_block/hr.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ def hr(state: StateBlock, startLine: int, endLine: int, silent: bool):
1616
pos = state.bMarks[startLine] + state.tShift[startLine]
1717
maximum = state.eMarks[startLine]
1818

19-
# if it's indented more than 3 spaces, it should be a code block
20-
if state.sCount[startLine] - state.blkIndent >= 4:
19+
if state.is_code_block(startLine):
2120
return False
2221

2322
try:

markdown_it/rules_block/html_block.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ def html_block(state: StateBlock, startLine: int, endLine: int, silent: bool):
3838
pos = state.bMarks[startLine] + state.tShift[startLine]
3939
maximum = state.eMarks[startLine]
4040

41-
# if it's indented more than 3 spaces, it should be a code block
42-
if state.sCount[startLine] - state.blkIndent >= 4:
41+
if state.is_code_block(startLine):
4342
return False
4443

4544
if not state.md.options.get("html", None):

markdown_it/rules_block/lheading.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ def lheading(state: StateBlock, startLine: int, endLine: int, silent: bool):
1515
ruler: Ruler = state.md.block.ruler
1616
terminatorRules = ruler.getRules("paragraph")
1717

18-
# if it's indented more than 3 spaces, it should be a code block
19-
if state.sCount[startLine] - state.blkIndent >= 4:
18+
if state.is_code_block(startLine):
2019
return False
2120

2221
oldParentType = state.parentType

markdown_it/rules_block/list.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,7 @@ def list_block(state: StateBlock, startLine: int, endLine: int, silent: bool):
102102
isTerminatingParagraph = False
103103
tight = True
104104

105-
# if it's indented more than 3 spaces, it should be a code block
106-
if state.sCount[startLine] - state.blkIndent >= 4:
105+
if state.is_code_block(startLine):
107106
return False
108107

109108
# Special case:
@@ -295,8 +294,7 @@ def list_block(state: StateBlock, startLine: int, endLine: int, silent: bool):
295294
if state.sCount[nextLine] < state.blkIndent:
296295
break
297296

298-
# if it's indented more than 3 spaces, it should be a code block
299-
if state.sCount[startLine] - state.blkIndent >= 4:
297+
if state.is_code_block(startLine):
300298
break
301299

302300
# fail if terminating block found

markdown_it/rules_block/reference.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ def reference(state: StateBlock, startLine, _endLine, silent):
1616
maximum = state.eMarks[startLine]
1717
nextLine = startLine + 1
1818

19-
# if it's indented more than 3 spaces, it should be a code block
20-
if state.sCount[startLine] - state.blkIndent >= 4:
19+
if state.is_code_block(startLine):
2120
return False
2221

2322
if state.srcCharCode[pos] != 0x5B: # /* [ */

markdown_it/rules_block/state_block.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ def __init__(
115115

116116
self.lineMax = len(self.bMarks) - 1 # don't count last fake line
117117

118+
# pre-check if code blocks are enabled, to speed up is_code_block method
119+
self._code_enabled = "code" in self.md["block"].ruler.get_active_rules()
120+
118121
def __repr__(self):
119122
return (
120123
f"{self.__class__.__name__}"
@@ -227,3 +230,7 @@ def getLines(self, begin: int, end: int, indent: int, keepLastLF: bool) -> str:
227230
i += 1
228231

229232
return "".join(queue)
233+
234+
def is_code_block(self, line: int) -> bool:
235+
"""Check if line is a code block, i.e. is indented by more than 3 spaces."""
236+
return self._code_enabled and (self.sCount[line] - self.blkIndent) >= 4

markdown_it/rules_block/table.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,7 @@ def table(state: StateBlock, startLine: int, endLine: int, silent: bool):
5959
if state.sCount[nextLine] < state.blkIndent:
6060
return False
6161

62-
# if it's indented more than 3 spaces, it should be a code block
63-
if state.sCount[nextLine] - state.blkIndent >= 4:
62+
if state.is_code_block(nextLine):
6463
return False
6564

6665
# first character of the second line should be '|', '-', ':',
@@ -124,7 +123,7 @@ def table(state: StateBlock, startLine: int, endLine: int, silent: bool):
124123
lineText = getLine(state, startLine).strip()
125124
if "|" not in lineText:
126125
return False
127-
if state.sCount[startLine] - state.blkIndent >= 4:
126+
if state.is_code_block(startLine):
128127
return False
129128
columns = escapedSplit(lineText)
130129
if columns and columns[0] == "":
@@ -190,7 +189,7 @@ def table(state: StateBlock, startLine: int, endLine: int, silent: bool):
190189
lineText = getLine(state, nextLine).strip()
191190
if not lineText:
192191
break
193-
if state.sCount[nextLine] - state.blkIndent >= 4:
192+
if state.is_code_block(nextLine):
194193
break
195194
columns = escapedSplit(lineText)
196195
if columns and columns[0] == "":
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
indent fence
2+
.
3+
```python
4+
def foo():
5+
pass
6+
```
7+
.
8+
<pre><code class="language-python">def foo():
9+
pass
10+
</code></pre>
11+
.
12+
13+
indent heading
14+
.
15+
# Heading
16+
.
17+
<h1>Heading</h1>
18+
.
19+
20+
indent table
21+
.
22+
| foo | bar |
23+
| --- | --- |
24+
| baz | bim |
25+
.
26+
<table>
27+
<thead>
28+
<tr>
29+
<th>foo</th>
30+
<th>bar</th>
31+
</tr>
32+
</thead>
33+
<tbody>
34+
<tr>
35+
<td>baz</td>
36+
<td>bim</td>
37+
</tr>
38+
</tbody>
39+
</table>
40+
.

tests/test_port/test_fixtures.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,17 @@ def test_strikethrough(line, title, input, expected):
104104
assert text.rstrip() == expected.rstrip()
105105

106106

107+
@pytest.mark.parametrize(
108+
"line,title,input,expected",
109+
read_fixture_file(FIXTURE_PATH.joinpath("disable_code_block.md")),
110+
)
111+
def test_disable_code_block(line, title, input, expected):
112+
md = MarkdownIt().enable("table").disable("code")
113+
text = md.render(input)
114+
print(text.rstrip())
115+
assert text.rstrip() == expected.rstrip()
116+
117+
107118
@pytest.mark.parametrize(
108119
"line,title,input,expected",
109120
read_fixture_file(FIXTURE_PATH.joinpath("issue-fixes.md")),

0 commit comments

Comments
 (0)