diff options
author | Jeremy Maitin-Shepard <jbms@google.com> | 2022-03-11 06:33:56 +0300 |
---|---|---|
committer | Jeremy Maitin-Shepard <jbms@google.com> | 2022-03-20 06:37:49 +0300 |
commit | 099b54cb87db3ca210f6edd67dfdbde3ec83c9a4 (patch) | |
tree | 9dad6cce1d15dab01960a2780bfa5b3068c76383 /sphinx | |
parent | b3812f72a98b01bae4b1158761082edc46cfa87f (diff) |
Make code role highlighting consistent with code-block directive
Fixes https://github.com/sphinx-doc/sphinx/issues/5157
This is factored out of the sphinx-immaterial theme:
https://github.com/jbms/sphinx-immaterial/blob/1ef121a612d4f5afc2a9ca9c4e3f20fca89065e8/sphinx_immaterial/inlinesyntaxhighlight.py#L1
See also:
https://github.com/sphinx-doc/sphinx/pull/6916
Diffstat (limited to 'sphinx')
-rw-r--r-- | sphinx/roles.py | 58 | ||||
-rw-r--r-- | sphinx/writers/html.py | 17 | ||||
-rw-r--r-- | sphinx/writers/html5.py | 17 | ||||
-rw-r--r-- | sphinx/writers/latex.py | 19 |
4 files changed, 108 insertions, 3 deletions
diff --git a/sphinx/roles.py b/sphinx/roles.py index 09cfac9c7..6b57a5d85 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -11,6 +11,9 @@ import re from typing import TYPE_CHECKING, Any, Dict, List, Tuple, Type +import docutils.parsers.rst.directives +import docutils.parsers.rst.roles +import docutils.parsers.rst.states from docutils import nodes, utils from docutils.nodes import Element, Node, TextElement, system_message @@ -341,6 +344,57 @@ class Abbreviation(SphinxRole): return [nodes.abbreviation(self.rawtext, text, **options)], [] +# Sphinx provides the `code-block` directive for highlighting code blocks. +# Docutils provides the `code` role which in theory can be used similarly by +# defining a custom role for a given programming language: +# +# .. .. role:: python(code) +# :language: python +# :class: highlight +# +# In practice this does not produce correct highlighting because it uses a +# separate highlighting mechanism that results in the "long" pygments class +# names rather than "short" pygments class names produced by the Sphinx +# `code-block` directive and for which this extension contains CSS rules. +# +# In addition, even if that issue is fixed, because the highlighting +# implementation in docutils, despite being based on pygments, differs from that +# used by Sphinx, the output does not exactly match that produced by the Sphinx +# `code-block` directive. +# +# This issue is noted here: //github.com/sphinx-doc/sphinx/issues/5157 +# +# This overrides the docutils `code` role to perform highlighting in the same +# way as the Sphinx `code-block` directive. +# +# TODO: Change to use `SphinxRole` once SphinxRole is fixed to support options. +def code_role(name: str, rawtext: str, text: str, lineno: int, + inliner: docutils.parsers.rst.states.Inliner, + options: Dict = {}, content: List[str] = [] + ) -> Tuple[List[Node], List[system_message]]: + options = options.copy() + docutils.parsers.rst.roles.set_classes(options) + language = options.get('language', '') + classes = ['code'] + if language: + classes.append('highlight') + if 'classes' in options: + classes.extend(options['classes']) + + if language and language not in classes: + classes.append(language) + + node = nodes.literal(rawtext, text, classes=classes, language=language) + + return [node], [] + + +code_role.options = { # type: ignore + 'class': docutils.parsers.rst.directives.class_option, + 'language': docutils.parsers.rst.directives.unchanged, +} + + specific_docroles: Dict[str, RoleFunction] = { # links to download references 'download': XRefRole(nodeclass=addnodes.download_reference), @@ -368,6 +422,10 @@ def setup(app: "Sphinx") -> Dict[str, Any]: for rolename, func in specific_docroles.items(): roles.register_local_role(rolename, func) + # Since docutils registers it as a canonical role, override it as a + # canonical role as well. + roles.register_canonical_role('code', code_role) + return { 'version': 'builtin', 'parallel_read_safe': True, diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index 34b73a0a5..43ad2a925 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -511,10 +511,25 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): if 'kbd' in node['classes']: self.body.append(self.starttag(node, 'kbd', '', CLASS='docutils literal notranslate')) - else: + return + lang = node.get("language", None) + if 'code' not in node['classes'] or not lang: self.body.append(self.starttag(node, 'code', '', CLASS='docutils literal notranslate')) self.protect_literal_text += 1 + return + + opts = self.config.highlight_options.get(lang, {}) + highlighted = self.highlighter.highlight_block( + node.astext(), lang, opts=opts, location=node, nowrap=True) + starttag = self.starttag( + node, + "code", + suffix="", + CLASS="docutils literal highlight highlight-%s" % lang, + ) + self.body.append(starttag + highlighted.strip() + "</code>") + raise nodes.SkipNode def depart_literal(self, node: Element) -> None: if 'kbd' in node['classes']: diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index b9d0f648c..4104bf81f 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -470,10 +470,25 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): if 'kbd' in node['classes']: self.body.append(self.starttag(node, 'kbd', '', CLASS='docutils literal notranslate')) - else: + return + lang = node.get("language", None) + if 'code' not in node['classes'] or not lang: self.body.append(self.starttag(node, 'code', '', CLASS='docutils literal notranslate')) self.protect_literal_text += 1 + return + + opts = self.config.highlight_options.get(lang, {}) + highlighted = self.highlighter.highlight_block( + node.astext(), lang, opts=opts, location=node, nowrap=True) + starttag = self.starttag( + node, + "code", + suffix="", + CLASS="docutils literal highlight highlight-%s" % lang, + ) + self.body.append(starttag + highlighted.strip() + "</code>") + raise nodes.SkipNode def depart_literal(self, node: Element) -> None: if 'kbd' in node['classes']: diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 5afc5fca7..fd68b4442 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -1747,10 +1747,27 @@ class LaTeXTranslator(SphinxTranslator): def visit_literal(self, node: Element) -> None: if self.in_title: self.body.append(r'\sphinxstyleliteralintitle{\sphinxupquote{') + return elif 'kbd' in node['classes']: self.body.append(r'\sphinxkeyboard{\sphinxupquote{') - else: + return + lang = node.get("language", None) + if 'code' not in node['classes'] or not lang: self.body.append(r'\sphinxcode{\sphinxupquote{') + return + + opts = self.config.highlight_options.get(lang, {}) + hlcode = self.highlighter.highlight_block( + node.astext(), lang, opts=opts, location=node) + # TODO: Use nowrap option once LaTeX formatter supports it + # https://github.com/pygments/pygments/pull/1343 + hlcode = hlcode.replace(r'\begin{Verbatim}[commandchars=\\\{\}]', + r'\sphinxcode{\sphinxupquote{') + # get consistent trailer + hlcode = hlcode.rstrip()[:-14] # strip \end{Verbatim} + self.body.append(hlcode) + self.body.append('}}') + raise nodes.SkipNode def depart_literal(self, node: Element) -> None: self.body.append('}}') |