Skip to content

Commit b96bd39

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 83d66d4 commit b96bd39

File tree

13 files changed

+104
-26
lines changed

13 files changed

+104
-26
lines changed

markdown_it/rules_block/blockquote.py

+1-2
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

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
def code(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool:
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) -> bool:
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

+2-4
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ def fence(state: StateBlock, startLine: int, endLine: int, silent: bool) -> 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) -> 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

+1-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ def heading(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bo
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

+1-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ def hr(state: StateBlock, startLine: int, endLine: int, silent: bool) -> 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

+1-2
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

+1-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ def lheading(state: StateBlock, startLine: int, endLine: int, silent: bool) -> b
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

+2-4
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

+1-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ def reference(state: StateBlock, startLine: int, _endLine: int, silent: bool) ->
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

+9
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ def __init__(
116116

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

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

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

markdown_it/rules_block/table.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ def table(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool
6161
if state.sCount[nextLine] < state.blkIndent:
6262
return False
6363

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

6867
# first character of the second line should be '|', '-', ':',
@@ -126,7 +125,7 @@ def table(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool
126125
lineText = getLine(state, startLine).strip()
127126
if "|" not in lineText:
128127
return False
129-
if state.sCount[startLine] - state.blkIndent >= 4:
128+
if state.is_code_block(startLine):
130129
return False
131130
columns = escapedSplit(lineText)
132131
if columns and columns[0] == "":
@@ -192,7 +191,7 @@ def table(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool
192191
lineText = getLine(state, nextLine).strip()
193192
if not lineText:
194193
break
195-
if state.sCount[nextLine] - state.blkIndent >= 4:
194+
if state.is_code_block(nextLine):
196195
break
197196
columns = escapedSplit(lineText)
198197
if columns and columns[0] == "":
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
indent paragraph
2+
.
3+
This is a paragraph,
4+
with multiple lines.
5+
6+
This paragraph
7+
has variable indents,
8+
like this.
9+
.
10+
<p>This is a paragraph,
11+
with multiple lines.</p>
12+
<p>This paragraph
13+
has variable indents,
14+
like this.</p>
15+
.
16+
17+
indent in HTML
18+
.
19+
<div>
20+
21+
Paragraph
22+
23+
</div>
24+
.
25+
<div>
26+
<p>Paragraph</p>
27+
</div>
28+
.
29+
30+
indent fence
31+
.
32+
```python
33+
def foo():
34+
pass
35+
```
36+
.
37+
<pre><code class="language-python">def foo():
38+
pass
39+
</code></pre>
40+
.
41+
42+
indent heading
43+
.
44+
# Heading
45+
.
46+
<h1>Heading</h1>
47+
.
48+
49+
indent table
50+
.
51+
| foo | bar |
52+
| --- | --- |
53+
| baz | bim |
54+
.
55+
<table>
56+
<thead>
57+
<tr>
58+
<th>foo</th>
59+
<th>bar</th>
60+
</tr>
61+
</thead>
62+
<tbody>
63+
<tr>
64+
<td>baz</td>
65+
<td>bim</td>
66+
</tr>
67+
</tbody>
68+
</table>
69+
.

tests/test_port/test_fixtures.py

+11
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)