Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion markdownify/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,13 @@ def convert_pre(self, el, text, parent_tags):
else:
raise ValueError('Invalid value for strip_pre: %s' % self.options['strip_pre'])

return '\n\n```%s\n%s\n```\n\n' % (code_language, text)
# Use a fence long enough that no backtick run inside the code can close
# it early (CommonMark section 4.5: the closing fence must have at least
# as many backticks as the opening).
max_backticks = max((len(match) for match in re.findall(re_backtick_runs, text)), default=0)
fence = '`' * max(3, max_backticks + 1)

return '\n\n%s%s\n%s\n%s\n\n' % (fence, code_language, text, fence)

def convert_q(self, el, text, parent_tags):
return '"' + text + '"'
Expand Down
9 changes: 9 additions & 0 deletions tests/test_conversions.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,15 @@ def test_pre():
assert md("<p>foo</p>\n<pre>bar</pre>\n</p>baz</p>", sub_symbol="^") == "\n\nfoo\n\n```\nbar\n```\n\nbaz"


def test_pre_backticks():
# A backtick run inside the code must not close the fence early: the fence
# needs at least one more backtick than the longest run in the content.
assert md('<pre>foo\n```\nbar</pre>') == '\n\n````\nfoo\n```\nbar\n````\n\n'
assert md('<pre><code>```</code></pre>') == '\n\n````\n```\n````\n\n'
assert md('<pre>a````b</pre>') == '\n\n`````\na````b\n`````\n\n'
assert md('<pre>plain</pre>') == '\n\n```\nplain\n```\n\n'


def test_q():
assert md('foo <q>quote</q> bar') == 'foo "quote" bar'
assert md('foo <q cite="https://example.com">quote</q> bar') == 'foo "quote" bar'
Expand Down