Skip to content

Commit d2907be

Browse files
Merge pull request #455 from momja/fix-code-block-indentation-in-lists
Fix code block indentation in lists
2 parents e69e37e + c376f73 commit d2907be

File tree

5 files changed

+109
-26
lines changed

5 files changed

+109
-26
lines changed

lib/markdown2.py

Lines changed: 65 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1879,8 +1879,6 @@ def _code_block_sub(self, match, is_fenced_code_block=False):
18791879
lexer_name = None
18801880
if is_fenced_code_block:
18811881
lexer_name = match.group(2)
1882-
if lexer_name:
1883-
formatter_opts = self.extras['fenced-code-blocks'] or {}
18841882
codeblock = match.group(3)
18851883
codeblock = codeblock[:-1] # drop one trailing newline
18861884
else:
@@ -1895,43 +1893,62 @@ def _code_block_sub(self, match, is_fenced_code_block=False):
18951893
lexer_name, rest = codeblock.split('\n', 1)
18961894
lexer_name = lexer_name[3:].strip()
18971895
codeblock = rest.lstrip("\n") # Remove lexer declaration line.
1898-
formatter_opts = self.extras['code-color'] or {}
18991896

19001897
# Use pygments only if not using the highlightjs-lang extra
19011898
if lexer_name and "highlightjs-lang" not in self.extras:
1902-
def unhash_code(codeblock):
1903-
for key, sanitized in list(self.html_spans.items()):
1904-
codeblock = codeblock.replace(key, sanitized)
1905-
replacements = [
1906-
("&", "&"),
1907-
("&lt;", "<"),
1908-
("&gt;", ">")
1909-
]
1910-
for old, new in replacements:
1911-
codeblock = codeblock.replace(old, new)
1912-
return codeblock
19131899
lexer = self._get_pygments_lexer(lexer_name)
19141900
if lexer:
1915-
# remove leading indent from code block
1916-
leading_indent, codeblock = self._uniform_outdent(codeblock)
1901+
leading_indent = ' '*(len(match.group(1)) - len(match.group(1).lstrip()))
1902+
return self._code_block_with_lexer_sub(codeblock, leading_indent, lexer, is_fenced_code_block)
19171903

1918-
codeblock = unhash_code( codeblock )
1919-
colored = self._color_with_pygments(codeblock, lexer,
1920-
**formatter_opts)
1921-
1922-
# add back the indent to all lines
1923-
return "\n%s\n" % self._uniform_indent(colored, leading_indent, True)
1924-
1925-
codeblock = self._encode_code(codeblock)
19261904
pre_class_str = self._html_class_str_from_tag("pre")
19271905

19281906
if "highlightjs-lang" in self.extras and lexer_name:
19291907
code_class_str = ' class="%s language-%s"' % (lexer_name, lexer_name)
19301908
else:
19311909
code_class_str = self._html_class_str_from_tag("code")
19321910

1933-
return "\n<pre%s><code%s>%s\n</code></pre>\n" % (
1934-
pre_class_str, code_class_str, codeblock)
1911+
if is_fenced_code_block:
1912+
# Fenced code blocks need to be outdented before encoding, and then reapplied
1913+
leading_indent = ' '*(len(match.group(1)) - len(match.group(1).lstrip()))
1914+
leading_indent, codeblock = self._uniform_outdent_limit(codeblock, leading_indent)
1915+
1916+
codeblock = self._encode_code(codeblock)
1917+
1918+
return "\n%s<pre%s><code%s>%s\n</code></pre>\n" % (
1919+
leading_indent, pre_class_str, code_class_str, codeblock)
1920+
else:
1921+
codeblock = self._encode_code(codeblock)
1922+
1923+
return "\n<pre%s><code%s>%s\n</code></pre>\n" % (
1924+
pre_class_str, code_class_str, codeblock)
1925+
1926+
def _code_block_with_lexer_sub(self, codeblock, leading_indent, lexer, is_fenced_code_block):
1927+
if is_fenced_code_block:
1928+
formatter_opts = self.extras['fenced-code-blocks'] or {}
1929+
else:
1930+
formatter_opts = self.extras['code-color'] or {}
1931+
1932+
def unhash_code(codeblock):
1933+
for key, sanitized in list(self.html_spans.items()):
1934+
codeblock = codeblock.replace(key, sanitized)
1935+
replacements = [
1936+
("&amp;", "&"),
1937+
("&lt;", "<"),
1938+
("&gt;", ">")
1939+
]
1940+
for old, new in replacements:
1941+
codeblock = codeblock.replace(old, new)
1942+
return codeblock
1943+
# remove leading indent from code block
1944+
leading_indent, codeblock = self._uniform_outdent(codeblock)
1945+
1946+
codeblock = unhash_code( codeblock )
1947+
colored = self._color_with_pygments(codeblock, lexer,
1948+
**formatter_opts)
1949+
1950+
# add back the indent to all lines
1951+
return "\n%s\n" % self._uniform_indent(colored, leading_indent, True)
19351952

19361953
def _html_class_str_from_tag(self, tag):
19371954
"""Get the appropriate ' class="..."' string (note the leading
@@ -2444,6 +2461,8 @@ def _uniform_outdent(self, text):
24442461

24452462
# Find leading indentation of each line
24462463
ws = re.findall(r'(^[ \t]*)(?:[^ \t\n])', text, re.MULTILINE)
2464+
if not ws:
2465+
return '', text
24472466
# Get smallest common leading indent
24482467
ws = sorted(ws)[0]
24492468
# Dedent every line by smallest common indent
@@ -2452,6 +2471,26 @@ def _uniform_outdent(self, text):
24522471
for line in text.splitlines(True)
24532472
)
24542473

2474+
def _uniform_outdent_limit(self, text, outdent):
2475+
# Outdents up to `outdent`. Similar to `_uniform_outdent`, but
2476+
# will leave some indentation on the line with the smallest common
2477+
# leading indentation depending on the amount specified.
2478+
# If the smallest leading indentation is less than `outdent`, it will
2479+
# perform identical to `_uniform_outdent`
2480+
2481+
# Find leading indentation of each line
2482+
ws = re.findall(r'(^[ \t]*)(?:[^ \t\n])', text, re.MULTILINE)
2483+
if not ws:
2484+
return outdent, text
2485+
# Get smallest common leading indent
2486+
ws = sorted(ws)[0]
2487+
if len(outdent) > len(ws):
2488+
outdent = ws
2489+
return outdent, ''.join(
2490+
(line.replace(outdent, '', 1) if line.startswith(outdent) else line)
2491+
for line in text.splitlines(True)
2492+
)
2493+
24552494
def _uniform_indent(self, text, indent, include_empty_lines=False):
24562495
return ''.join(
24572496
(indent + line if line.strip() or include_empty_lines else '')
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<ol>
2+
<li><p>This is my first list item</p>
3+
4+
<pre><code>And this is my code item
5+
</code></pre>
6+
7+
<p>Followed by another paragraph</p></li>
8+
<li><p>This is my second list item</p>
9+
10+
<pre><code>
11+
</code></pre>
12+
13+
<p>empty codeblock just for sh*ts and giggles</p>
14+
15+
<div class="codehilite"><pre><span></span><code><span class="n">test</span> <span class="k">with</span> <span class="n">language</span> <span class="nb">set</span>
16+
</code></pre></div>
17+
18+
<pre><code>This is a regular code block
19+
Multiline
20+
</code></pre></li>
21+
</ol>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"extras": ["fenced-code-blocks"]}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
extra fenced-code-blocks pygments
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
1. This is my first list item
2+
3+
```
4+
And this is my code item
5+
```
6+
7+
Followed by another paragraph
8+
9+
2. This is my second list item
10+
11+
```
12+
```
13+
14+
empty codeblock just for sh\*ts and giggles
15+
16+
```python
17+
test with language set
18+
```
19+
20+
This is a regular code block
21+
Multiline

0 commit comments

Comments
 (0)