Skip to content

Commit 9251695

Browse files
authored
👌 Centralise indented code block test (#260)
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/or arbitrary 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 and could even be upstreamed.
1 parent 83d66d4 commit 9251695

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)