Skip to content

Commit

Permalink
fix(latex-renderer): make inline code more robust
Browse files Browse the repository at this point in the history
Currently `\verb` is used to render inline code. However, that has some
downsides as it does not work in moving arguments or within macros like
\multicolumn, and it doesn't break long inline code lines. This patch
changes the implementation to use `\texttt`, which fixes those issues.
  • Loading branch information
choeppler committed Oct 1, 2024
1 parent 514d702 commit 4fea8ad
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 32 deletions.
26 changes: 7 additions & 19 deletions mistletoe/latex_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,11 @@
"""

import re
import string
from itertools import chain
from urllib.parse import quote
import mistletoe.latex_token as latex_token
from mistletoe.base_renderer import BaseRenderer

# (customizable) delimiters for inline code
verb_delimiters = string.punctuation + string.digits
for delimiter in '*': # remove invalid delimiters
verb_delimiters.replace(delimiter, '')
for delimiter in reversed('|!"\'=+'): # start with most common delimiters
verb_delimiters = delimiter + verb_delimiters.replace(delimiter, '')


class LaTeXRenderer(BaseRenderer):
def __init__(self, *extras, **kwargs):
Expand All @@ -27,7 +19,6 @@ def __init__(self, *extras, **kwargs):
"""
tokens = self._tokens_from_module(latex_token)
self.packages = {}
self.verb_delimiters = verb_delimiters
super().__init__(*chain(tokens, extras), **kwargs)

def render_strong(self, token):
Expand All @@ -37,18 +28,15 @@ def render_emphasis(self, token):
return '\\textit{{{}}}'.format(self.render_inner(token))

def render_inline_code(self, token):
content = self.render_raw_text(token.children[0], escape=False)

# search for delimiter not present in content
for delimiter in self.verb_delimiters:
if delimiter not in content:
break
# fontenc to get better results for `_{}\` in `\texttt{}`
self.packages['fontenc'] = ['T1']

if delimiter in content: # no delimiter found
raise RuntimeError('Unable to find delimiter for verb macro')
content = self.render_raw_text(token.children[0], escape=True)
# make \texttt behave like \verb w.r.t. whitespace in inline code
content = re.sub(r'\s{2,}', lambda m: '\\ '*len(m.group(0)), content)

template = '\\verb{delimiter}{content}{delimiter}'
return template.format(delimiter=delimiter, content=content)
template = '\\texttt{{{content}}}'
return template.format(content=content)

def render_strikethrough(self, token):
self.packages['ulem'] = ['normalem']
Expand Down
24 changes: 11 additions & 13 deletions test/test_latex_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,19 @@ def test_strong(self):
def test_emphasis(self):
self._test_token('Emphasis', '\\textit{inner}')

def test_inline_code(self):
@parameterized.expand([
('inner', '\\texttt{inner}'),
('a + b', '\\texttt{a + b}'),
('a | b', '\\texttt{a | b}'),
('|ab!|', '\\texttt{|ab!|}'),
('two spaces', '\\texttt{two\\ \\ spaces}'),
('two\t whitespaces', '\\texttt{two\\ \\ whitespaces}'),
])
def test_inline_code(self, content, expected):
func_path = 'mistletoe.latex_renderer.LaTeXRenderer.render_raw_text'

for content, expected in {'inner': '\\verb|inner|',
'a + b': '\\verb|a + b|',
'a | b': '\\verb!a | b!',
'|ab!|': '\\verb"|ab!|"',
}.items():
with mock.patch(func_path, return_value=content):
self._test_token('InlineCode', expected, content=content)

content = mistletoe.latex_renderer.verb_delimiters
with self.assertRaises(RuntimeError):
with mock.patch(func_path, return_value=content):
self._test_token('InlineCode', None, content=content)
with mock.patch(func_path, return_value=content):
self._test_token('InlineCode', expected, content=content)

def test_strikethrough(self):
self._test_token('Strikethrough', '\\sout{inner}')
Expand Down

0 comments on commit 4fea8ad

Please sign in to comment.