From 15cbc6dbb0b07aaa43ed95fa7fcc7bb8fe107a4e Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 27 Dec 2020 15:25:22 +0900 Subject: [PATCH] Close #6550: html: Allow to use HTML permalink texts Add new configuration variables: `html_permalinks` and `html_permalinks_icon`. This refines the settings around HTML permalinks. * html_add_permalinks * Deprecated. * html_permalinks * Enable or disable permalinks feature. * html_permalinks_icon * Change the icon for permalinks --- CHANGES | 7 ++++++- doc/extdev/deprecated.rst | 10 ++++++++++ doc/usage/configuration.rst | 17 +++++++++++++++++ sphinx/builders/html/__init__.py | 15 ++++++++++++++- sphinx/writers/html.py | 22 ++++++++++++---------- sphinx/writers/html5.py | 24 +++++++++++++----------- tests/test_build_html.py | 20 ++++++++++++++++++++ 7 files changed, 92 insertions(+), 23 deletions(-) diff --git a/CHANGES b/CHANGES index 9f7ffb7f3..604689ed8 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,8 @@ Incompatible changes -------------------- * Update Underscore.js to 1.12.0 +* #6550: html: The config variable ``html_add_permalinks`` is replaced by + :confval:`html_permalinks` and :confval:`html_permalinks_icon` Deprecated ---------- @@ -22,6 +24,8 @@ Deprecated * ``sphinx.ext.autodoc.importer.get_module_members()`` * ``sphinx.ext.autosummary.generate._simple_info()`` * ``sphinx.ext.autosummary.generate._simple_warn()`` +* ``sphinx.writers.html.HTMLTranslator.permalink_text`` +* ``sphinx.writers.html5.HTML5Translator.permalink_text`` Features added -------------- @@ -37,6 +41,8 @@ Features added * #6241: html: Allow to add JS/CSS files to the specific page when an extension calls ``app.add_js_file()`` or ``app.add_css_file()`` on :event:`html-page-context` event +* #6550: html: Allow to use HTML permalink texts via + :confval:`html_permalinks_icon` * #8649: imgconverter: Skip availability check if builder supports the image type * #8573: napoleon: Allow to change the style of custom sections using @@ -53,7 +59,6 @@ Features added * C++, also hyperlink operator overloads in expressions and alias declarations. * #8247: Allow production lists to refer to tokens from other production groups - Bugs fixed ---------- diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst index e8ec65dfe..dc9d9973d 100644 --- a/doc/extdev/deprecated.rst +++ b/doc/extdev/deprecated.rst @@ -71,6 +71,16 @@ The following is a list of deprecated interfaces. - 5.0 - :ref:`logging-api` + * - ``sphinx.writers.html.HTMLTranslator.permalink_text`` + - 3.5 + - 5.0 + - :confval:`html_permalinks_icon` + + * - ``sphinx.writers.html5.HTML5Translator.permalink_text`` + - 3.5 + - 5.0 + - :confval:`html_permalinks_icon` + * - The ``follow_wrapped`` argument of ``sphinx.util.inspect.signature()`` - 3.4 - 5.0 diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index 49e41b078..0b6ce84b0 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -1133,6 +1133,23 @@ that use Sphinx's HTMLWriter class. This can now be a string to select the actual text of the link. Previously, only boolean values were accepted. + .. deprecated:: 3.5 + This has been replaced by :confval:`html_permalinks` + +.. confval:: html_permalinks + + If true, Sphinx will add "permalinks" for each heading and description + environment. Default: ``True``. + + .. versionadded:: 3.5 + +.. confval:: html_permalinks_icon + + A text for permalinks for each heading and description environment. HTML + tags are allowed. Default: a paragraph sign; ``¶`` + + .. versionadded:: 3.5 + .. confval:: html_sidebars Custom sidebar templates, must be a dictionary that maps document names to diff --git a/sphinx/builders/html/__init__.py b/sphinx/builders/html/__init__.py index 5cce98c3f..f5152f6da 100644 --- a/sphinx/builders/html/__init__.py +++ b/sphinx/builders/html/__init__.py @@ -1205,6 +1205,16 @@ def validate_html_favicon(app: Sphinx, config: Config) -> None: config.html_favicon = None # type: ignore +def migrate_html_add_permalinks(app: Sphinx, config: Config) -> None: + """Migrate html_add_permalinks to html_permalinks*.""" + if config.html_add_permalinks: + if (isinstance(config.html_add_permalinks, bool) and + config.html_add_permalinks is False): + config.html_permalinks = False # type: ignore + else: + config.html_permalinks_icon = html.escape(config.html_add_permalinks) # type: ignore # NOQA + + # for compatibility import sphinxcontrib.serializinghtml # NOQA @@ -1235,7 +1245,9 @@ def setup(app: Sphinx) -> Dict[str, Any]: app.add_config_value('html_sidebars', {}, 'html') app.add_config_value('html_additional_pages', {}, 'html') app.add_config_value('html_domain_indices', True, 'html', [list]) - app.add_config_value('html_add_permalinks', '¶', 'html') + app.add_config_value('html_add_permalinks', None, 'html') + app.add_config_value('html_permalinks', True, 'html') + app.add_config_value('html_permalinks_icon', '¶', 'html') app.add_config_value('html_use_index', True, 'html') app.add_config_value('html_split_index', False, 'html') app.add_config_value('html_copy_source', True, 'html') @@ -1267,6 +1279,7 @@ def setup(app: Sphinx) -> Dict[str, Any]: # event handlers app.connect('config-inited', convert_html_css_files, priority=800) app.connect('config-inited', convert_html_js_files, priority=800) + app.connect('config-inited', migrate_html_add_permalinks, priority=800) app.connect('config-inited', validate_html_extra_path, priority=800) app.connect('config-inited', validate_html_static_path, priority=800) app.connect('config-inited', validate_html_logo, priority=800) diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index 46e3e52e9..e4da7d857 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -22,7 +22,7 @@ from docutils.writers.html4css1 import Writer from sphinx import addnodes from sphinx.builders import Builder -from sphinx.deprecation import RemovedInSphinx40Warning +from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning from sphinx.locale import _, __, admonitionlabels from sphinx.util import logging from sphinx.util.docutils import SphinxTranslator @@ -100,11 +100,6 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): self.docnames = [self.builder.current_docname] # for singlehtml builder self.manpages_url = self.config.manpages_url self.protect_literal_text = 0 - self.permalink_text = self.config.html_add_permalinks - # support backwards-compatible setting to a bool - if not isinstance(self.permalink_text, str): - self.permalink_text = '¶' if self.permalink_text else '' - self.permalink_text = self.encode(self.permalink_text) self.secnumber_suffix = self.config.html_secnumber_suffix self.param_separator = '' self.optional_param_level = 0 @@ -333,9 +328,10 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): append_fignumber(figtype, node['ids'][0]) def add_permalink_ref(self, node: Element, title: str) -> None: - if node['ids'] and self.permalink_text and self.builder.add_permalinks: + if node['ids'] and self.config.html_permalinks and self.builder.add_permalinks: format = '%s' - self.body.append(format % (node['ids'][0], title, self.permalink_text)) + self.body.append(format % (node['ids'][0], title, + self.config.html_permalinks_icon)) def generate_targets_for_listing(self, node: Element) -> None: """Generate hyperlink targets for listings. @@ -410,7 +406,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): def depart_title(self, node: Element) -> None: close_tag = self.context[-1] - if (self.permalink_text and self.builder.add_permalinks and + if (self.config.html_permalinks and self.builder.add_permalinks and node.parent.hasattr('ids') and node.parent['ids']): # add permalink anchor if close_tag.startswith('%s' % ( _('Permalink to this headline'), - self.permalink_text)) + self.config.html_permalinks_icon)) elif isinstance(node.parent, nodes.table): self.body.append('') self.add_permalink_ref(node.parent, _('Permalink to this table')) @@ -838,3 +834,9 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): def unknown_visit(self, node: Node) -> None: raise NotImplementedError('Unknown node: ' + node.__class__.__name__) + + @property + def permalink_text(self) -> str: + warnings.warn('HTMLTranslator.permalink_text is deprecated.', + RemovedInSphinx50Warning, stacklevel=2) + return self.config.html_permalinks_icon diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index d7c7b95e8..12f3f423a 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -20,7 +20,7 @@ from docutils.writers.html5_polyglot import HTMLTranslator as BaseTranslator from sphinx import addnodes from sphinx.builders import Builder -from sphinx.deprecation import RemovedInSphinx40Warning +from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning from sphinx.locale import _, __, admonitionlabels from sphinx.util import logging from sphinx.util.docutils import SphinxTranslator @@ -71,11 +71,6 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): self.docnames = [self.builder.current_docname] # for singlehtml builder self.manpages_url = self.config.manpages_url self.protect_literal_text = 0 - self.permalink_text = self.config.html_add_permalinks - # support backwards-compatible setting to a bool - if not isinstance(self.permalink_text, str): - self.permalink_text = '¶' if self.permalink_text else '' - self.permalink_text = self.encode(self.permalink_text) self.secnumber_suffix = self.config.html_secnumber_suffix self.param_separator = '' self.optional_param_level = 0 @@ -304,9 +299,10 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): append_fignumber(figtype, node['ids'][0]) def add_permalink_ref(self, node: Element, title: str) -> None: - if node['ids'] and self.permalink_text and self.builder.add_permalinks: + if node['ids'] and self.config.html_permalinks and self.builder.add_permalinks: format = '%s' - self.body.append(format % (node['ids'][0], title, self.permalink_text)) + self.body.append(format % (node['ids'][0], title, + self.config.html_permalinks_icon)) # overwritten def visit_bullet_list(self, node: Element) -> None: @@ -361,8 +357,8 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): def depart_title(self, node: Element) -> None: close_tag = self.context[-1] - if (self.permalink_text and self.builder.add_permalinks and - node.parent.hasattr('ids') and node.parent['ids']): + if (self.config.html_permalinks and self.builder.add_permalinks and + node.parent.hasattr('ids') and node.parent['ids']): # add permalink anchor if close_tag.startswith('%s' % ( _('Permalink to this headline'), - self.permalink_text)) + self.config.html_permalinks_icon)) elif isinstance(node.parent, nodes.table): self.body.append('') self.add_permalink_ref(node.parent, _('Permalink to this table')) @@ -786,3 +782,9 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): def unknown_visit(self, node: Node) -> None: raise NotImplementedError('Unknown node: ' + node.__class__.__name__) + + @property + def permalink_text(self) -> str: + warnings.warn('HTMLTranslator.permalink_text is deprecated.', + RemovedInSphinx50Warning, stacklevel=2) + return self.config.html_permalinks_icon diff --git a/tests/test_build_html.py b/tests/test_build_html.py index d7b572abd..9abfa19aa 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -1665,3 +1665,23 @@ def test_highlight_options_old(app): location=ANY, opts={}) assert call_args[2] == call(ANY, 'java', force=False, linenos=False, location=ANY, opts={}) + + +@pytest.mark.sphinx('html', testroot='basic', + confoverrides={'html_permalinks': False}) +def test_html_permalink_disable(app): + app.build() + content = (app.outdir / 'index.html').read_text() + + assert '

The basic Sphinx documentation for testing

' in content + + +@pytest.mark.sphinx('html', testroot='basic', + confoverrides={'html_permalinks_icon': '[PERMALINK]'}) +def test_html_permalink_icon(app): + app.build() + content = (app.outdir / 'index.html').read_text() + + assert ('

The basic Sphinx documentation for testing[PERMALINK]

' in content)