From 4fea8ada7414a6a9de775bfd580fc9151242d358 Mon Sep 17 00:00:00 2001 From: Chris Hoeppler Date: Tue, 1 Oct 2024 17:52:11 +0200 Subject: [PATCH] fix(latex-renderer): make inline code more robust 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. --- mistletoe/latex_renderer.py | 26 +++++++------------------- test/test_latex_renderer.py | 24 +++++++++++------------- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/mistletoe/latex_renderer.py b/mistletoe/latex_renderer.py index 3230392..437ec41 100644 --- a/mistletoe/latex_renderer.py +++ b/mistletoe/latex_renderer.py @@ -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): @@ -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): @@ -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'] diff --git a/test/test_latex_renderer.py b/test/test_latex_renderer.py index b23445d..ae70a95 100644 --- a/test/test_latex_renderer.py +++ b/test/test_latex_renderer.py @@ -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}')