From 32ac5f2e57ae25d8b75e93a3fca4ad74327e72c7 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 11 Jan 2021 00:02:38 +0900 Subject: [PATCH] Close #207: Now highlight_language supports multiple languages This changes the structure of `highlight_options` to a dictionary that maps language names to option dictionary. It allows to setting pygments options for multiple languages at once. --- CHANGES | 1 + doc/usage/configuration.rst | 23 ++++++++++--- sphinx/config.py | 13 ++++++++ sphinx/writers/html.py | 6 +--- sphinx/writers/html5.py | 6 +--- sphinx/writers/latex.py | 6 +--- tests/roots/test-highlight_options/conf.py | 4 +++ tests/roots/test-highlight_options/index.rst | 14 ++++++++ tests/test_build_html.py | 34 ++++++++++++++++++++ 9 files changed, 88 insertions(+), 19 deletions(-) create mode 100644 tests/roots/test-highlight_options/conf.py create mode 100644 tests/roots/test-highlight_options/index.rst diff --git a/CHANGES b/CHANGES index cc6b20774..74fe0b75a 100644 --- a/CHANGES +++ b/CHANGES @@ -29,6 +29,7 @@ Features added type * #6241: mathjax: Include mathjax.js only on the document using equations * #8132: Add :confval:`project_copyright` as an alias of :confval:`copyright` +* #207: Now :confval:`highlight_language` supports multiple languages Bugs fixed ---------- diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index e9e9190e6..ee3b6f74a 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -582,12 +582,27 @@ General configuration .. confval:: highlight_options - A dictionary of options that modify how the lexer specified by - :confval:`highlight_language` generates highlighted source code. These are - lexer-specific; for the options understood by each, see the - `Pygments documentation `_. + A dictionary that maps language names to options for the lexer modules of + Pygments. These are lexer-specific; for the options understood by each, + see the `Pygments documentation `_. + + Example:: + + highlight_options = { + 'default': {'stripall': True}, + 'php': {'startinline': True}, + } + + A single dictionary of options are also allowed. Then it is recognized + as options to the lexer specified by :confval:`highlight_language`:: + + # configuration for the ``highlight_language`` + highlight_options = {'stripall': True} .. versionadded:: 1.3 + .. versionchanged:: 3.5 + + Allow to configure highlight options for multiple languages .. confval:: pygments_style diff --git a/sphinx/config.py b/sphinx/config.py index 645b09272..4c038b061 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -367,6 +367,18 @@ def convert_source_suffix(app: "Sphinx", config: Config) -> None: "But `%r' is given." % source_suffix)) +def convert_highlight_options(app: "Sphinx", config: Config) -> None: + """Convert old styled highlight_options to new styled one. + + * old style: options + * new style: dict that maps language names to options + """ + options = config.highlight_options + if options and not all(isinstance(v, dict) for v in options.values()): + # old styled option detected because all values are not dictionary. + config.highlight_options = {config.highlight_language: options} # type: ignore + + def init_numfig_format(app: "Sphinx", config: Config) -> None: """Initialize :confval:`numfig_format`.""" numfig_format = {'section': _('Section %s'), @@ -487,6 +499,7 @@ def check_master_doc(app: "Sphinx", env: "BuildEnvironment", added: Set[str], def setup(app: "Sphinx") -> Dict[str, Any]: app.connect('config-inited', convert_source_suffix, priority=800) + app.connect('config-inited', convert_highlight_options, priority=800) app.connect('config-inited', init_numfig_format, priority=800) app.connect('config-inited', correct_copyright_year, priority=800) app.connect('config-inited', check_confval_types, priority=800) diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index fc20f6e3b..46e3e52e9 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -439,11 +439,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): linenos = node.get('linenos', False) highlight_args = node.get('highlight_args', {}) highlight_args['force'] = node.get('force', False) - if lang == self.config.highlight_language: - # only pass highlighter options for original language - opts = self.config.highlight_options - else: - opts = {} + opts = self.config.highlight_options.get(lang, {}) if linenos and self.config.html_codeblock_linenos_style: linenos = self.config.html_codeblock_linenos_style diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index 557027da5..d7c7b95e8 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -390,11 +390,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): linenos = node.get('linenos', False) highlight_args = node.get('highlight_args', {}) highlight_args['force'] = node.get('force', False) - if lang == self.config.highlight_language: - # only pass highlighter options for original language - opts = self.config.highlight_options - else: - opts = {} + opts = self.config.highlight_options.get(lang, {}) if linenos and self.config.html_codeblock_linenos_style: linenos = self.config.html_codeblock_linenos_style diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index dc16bd32c..1591b5056 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -1751,11 +1751,7 @@ class LaTeXTranslator(SphinxTranslator): linenos = node.get('linenos', False) highlight_args = node.get('highlight_args', {}) highlight_args['force'] = node.get('force', False) - if lang == self.config.highlight_language: - # only pass highlighter options for original language - opts = self.config.highlight_options - else: - opts = {} + opts = self.config.highlight_options.get(lang, {}) hlcode = self.highlighter.highlight_block( node.rawsource, lang, opts=opts, linenos=linenos, diff --git a/tests/roots/test-highlight_options/conf.py b/tests/roots/test-highlight_options/conf.py new file mode 100644 index 000000000..90997d444 --- /dev/null +++ b/tests/roots/test-highlight_options/conf.py @@ -0,0 +1,4 @@ +highlight_options = { + 'default': {'default_option': True}, + 'python': {'python_option': True} +} diff --git a/tests/roots/test-highlight_options/index.rst b/tests/roots/test-highlight_options/index.rst new file mode 100644 index 000000000..389041ace --- /dev/null +++ b/tests/roots/test-highlight_options/index.rst @@ -0,0 +1,14 @@ +test-highlight_options +====================== + +.. code-block:: + + blah blah blah + +.. code-block:: python + + blah blah blah + +.. code-block:: java + + blah blah blah diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 21433cb8a..d7b572abd 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -12,6 +12,7 @@ import os import re from distutils.version import LooseVersion from itertools import chain, cycle +from unittest.mock import ANY, call, patch import pygments import pytest @@ -1631,3 +1632,36 @@ def test_html_codeblock_linenos_style_inline(app): assert '1' in content else: assert '1 ' in content + + +@pytest.mark.sphinx('html', testroot='highlight_options') +def test_highlight_options(app): + subject = app.builder.highlighter + with patch.object(subject, 'highlight_block', wraps=subject.highlight_block) as highlight: + app.build() + + call_args = highlight.call_args_list + assert len(call_args) == 3 + assert call_args[0] == call(ANY, 'default', force=False, linenos=False, + location=ANY, opts={'default_option': True}) + assert call_args[1] == call(ANY, 'python', force=False, linenos=False, + location=ANY, opts={'python_option': True}) + assert call_args[2] == call(ANY, 'java', force=False, linenos=False, + location=ANY, opts={}) + + +@pytest.mark.sphinx('html', testroot='highlight_options', + confoverrides={'highlight_options': {'default_option': True}}) +def test_highlight_options_old(app): + subject = app.builder.highlighter + with patch.object(subject, 'highlight_block', wraps=subject.highlight_block) as highlight: + app.build() + + call_args = highlight.call_args_list + assert len(call_args) == 3 + assert call_args[0] == call(ANY, 'default', force=False, linenos=False, + location=ANY, opts={'default_option': True}) + assert call_args[1] == call(ANY, 'python', force=False, linenos=False, + location=ANY, opts={}) + assert call_args[2] == call(ANY, 'java', force=False, linenos=False, + location=ANY, opts={})