Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sphinx-doc/sphinx.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/sphinx
diff options
context:
space:
mode:
authorJeremy Maitin-Shepard <jbms@google.com>2022-03-11 06:33:56 +0300
committerJeremy Maitin-Shepard <jbms@google.com>2022-03-20 06:37:49 +0300
commit099b54cb87db3ca210f6edd67dfdbde3ec83c9a4 (patch)
tree9dad6cce1d15dab01960a2780bfa5b3068c76383 /sphinx
parentb3812f72a98b01bae4b1158761082edc46cfa87f (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.py58
-rw-r--r--sphinx/writers/html.py17
-rw-r--r--sphinx/writers/html5.py17
-rw-r--r--sphinx/writers/latex.py19
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('}}')