Merge pull request #8599 from tk0miya/6550_html_permalinks_icon

Close #6550: html: Allow to use HTML permalink texts
This commit is contained in:
Takeshi KOMIYA 2021-01-24 14:30:06 +09:00 committed by GitHub
commit becf8f43be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 92 additions and 23 deletions

View File

@ -8,6 +8,8 @@ Incompatible changes
-------------------- --------------------
* Update Underscore.js to 1.12.0 * 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 Deprecated
---------- ----------
@ -22,6 +24,8 @@ Deprecated
* ``sphinx.ext.autodoc.importer.get_module_members()`` * ``sphinx.ext.autodoc.importer.get_module_members()``
* ``sphinx.ext.autosummary.generate._simple_info()`` * ``sphinx.ext.autosummary.generate._simple_info()``
* ``sphinx.ext.autosummary.generate._simple_warn()`` * ``sphinx.ext.autosummary.generate._simple_warn()``
* ``sphinx.writers.html.HTMLTranslator.permalink_text``
* ``sphinx.writers.html5.HTML5Translator.permalink_text``
Features added Features added
-------------- --------------
@ -37,6 +41,8 @@ Features added
* #6241: html: Allow to add JS/CSS files to the specific page when an extension * #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 calls ``app.add_js_file()`` or ``app.add_css_file()`` on
:event:`html-page-context` event :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 * #8649: imgconverter: Skip availability check if builder supports the image
type type
* #8573: napoleon: Allow to change the style of custom sections using * #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. * C++, also hyperlink operator overloads in expressions and alias declarations.
* #8247: Allow production lists to refer to tokens from other production groups * #8247: Allow production lists to refer to tokens from other production groups
Bugs fixed Bugs fixed
---------- ----------

View File

@ -71,6 +71,16 @@ The following is a list of deprecated interfaces.
- 5.0 - 5.0
- :ref:`logging-api` - :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()`` * - The ``follow_wrapped`` argument of ``sphinx.util.inspect.signature()``
- 3.4 - 3.4
- 5.0 - 5.0

View File

@ -1133,6 +1133,23 @@ that use Sphinx's HTMLWriter class.
This can now be a string to select the actual text of the link. This can now be a string to select the actual text of the link.
Previously, only boolean values were accepted. 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 .. confval:: html_sidebars
Custom sidebar templates, must be a dictionary that maps document names to Custom sidebar templates, must be a dictionary that maps document names to

View File

@ -1205,6 +1205,16 @@ def validate_html_favicon(app: Sphinx, config: Config) -> None:
config.html_favicon = None # type: ignore 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 # for compatibility
import sphinxcontrib.serializinghtml # NOQA 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_sidebars', {}, 'html')
app.add_config_value('html_additional_pages', {}, 'html') app.add_config_value('html_additional_pages', {}, 'html')
app.add_config_value('html_domain_indices', True, 'html', [list]) 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_use_index', True, 'html')
app.add_config_value('html_split_index', False, 'html') app.add_config_value('html_split_index', False, 'html')
app.add_config_value('html_copy_source', True, 'html') app.add_config_value('html_copy_source', True, 'html')
@ -1267,6 +1279,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
# event handlers # event handlers
app.connect('config-inited', convert_html_css_files, priority=800) app.connect('config-inited', convert_html_css_files, priority=800)
app.connect('config-inited', convert_html_js_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_extra_path, priority=800)
app.connect('config-inited', validate_html_static_path, priority=800) app.connect('config-inited', validate_html_static_path, priority=800)
app.connect('config-inited', validate_html_logo, priority=800) app.connect('config-inited', validate_html_logo, priority=800)

View File

@ -22,7 +22,7 @@ from docutils.writers.html4css1 import Writer
from sphinx import addnodes from sphinx import addnodes
from sphinx.builders import Builder from sphinx.builders import Builder
from sphinx.deprecation import RemovedInSphinx40Warning from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
from sphinx.locale import _, __, admonitionlabels from sphinx.locale import _, __, admonitionlabels
from sphinx.util import logging from sphinx.util import logging
from sphinx.util.docutils import SphinxTranslator from sphinx.util.docutils import SphinxTranslator
@ -100,11 +100,6 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
self.docnames = [self.builder.current_docname] # for singlehtml builder self.docnames = [self.builder.current_docname] # for singlehtml builder
self.manpages_url = self.config.manpages_url self.manpages_url = self.config.manpages_url
self.protect_literal_text = 0 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.secnumber_suffix = self.config.html_secnumber_suffix
self.param_separator = '' self.param_separator = ''
self.optional_param_level = 0 self.optional_param_level = 0
@ -333,9 +328,10 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
append_fignumber(figtype, node['ids'][0]) append_fignumber(figtype, node['ids'][0])
def add_permalink_ref(self, node: Element, title: str) -> None: 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 = '<a class="headerlink" href="#%s" title="%s">%s</a>' format = '<a class="headerlink" href="#%s" title="%s">%s</a>'
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: def generate_targets_for_listing(self, node: Element) -> None:
"""Generate hyperlink targets for listings. """Generate hyperlink targets for listings.
@ -410,7 +406,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
def depart_title(self, node: Element) -> None: def depart_title(self, node: Element) -> None:
close_tag = self.context[-1] 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']): node.parent.hasattr('ids') and node.parent['ids']):
# add permalink anchor # add permalink anchor
if close_tag.startswith('</h'): if close_tag.startswith('</h'):
@ -420,7 +416,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
node.parent['ids'][0] + node.parent['ids'][0] +
'title="%s">%s' % ( 'title="%s">%s' % (
_('Permalink to this headline'), _('Permalink to this headline'),
self.permalink_text)) self.config.html_permalinks_icon))
elif isinstance(node.parent, nodes.table): elif isinstance(node.parent, nodes.table):
self.body.append('</span>') self.body.append('</span>')
self.add_permalink_ref(node.parent, _('Permalink to this table')) 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: def unknown_visit(self, node: Node) -> None:
raise NotImplementedError('Unknown node: ' + node.__class__.__name__) 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

View File

@ -20,7 +20,7 @@ from docutils.writers.html5_polyglot import HTMLTranslator as BaseTranslator
from sphinx import addnodes from sphinx import addnodes
from sphinx.builders import Builder from sphinx.builders import Builder
from sphinx.deprecation import RemovedInSphinx40Warning from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
from sphinx.locale import _, __, admonitionlabels from sphinx.locale import _, __, admonitionlabels
from sphinx.util import logging from sphinx.util import logging
from sphinx.util.docutils import SphinxTranslator from sphinx.util.docutils import SphinxTranslator
@ -71,11 +71,6 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
self.docnames = [self.builder.current_docname] # for singlehtml builder self.docnames = [self.builder.current_docname] # for singlehtml builder
self.manpages_url = self.config.manpages_url self.manpages_url = self.config.manpages_url
self.protect_literal_text = 0 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.secnumber_suffix = self.config.html_secnumber_suffix
self.param_separator = '' self.param_separator = ''
self.optional_param_level = 0 self.optional_param_level = 0
@ -304,9 +299,10 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
append_fignumber(figtype, node['ids'][0]) append_fignumber(figtype, node['ids'][0])
def add_permalink_ref(self, node: Element, title: str) -> None: 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 = '<a class="headerlink" href="#%s" title="%s">%s</a>' format = '<a class="headerlink" href="#%s" title="%s">%s</a>'
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 # overwritten
def visit_bullet_list(self, node: Element) -> None: def visit_bullet_list(self, node: Element) -> None:
@ -361,7 +357,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
def depart_title(self, node: Element) -> None: def depart_title(self, node: Element) -> None:
close_tag = self.context[-1] 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']): node.parent.hasattr('ids') and node.parent['ids']):
# add permalink anchor # add permalink anchor
if close_tag.startswith('</h'): if close_tag.startswith('</h'):
@ -371,7 +367,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
node.parent['ids'][0] + node.parent['ids'][0] +
'title="%s">%s' % ( 'title="%s">%s' % (
_('Permalink to this headline'), _('Permalink to this headline'),
self.permalink_text)) self.config.html_permalinks_icon))
elif isinstance(node.parent, nodes.table): elif isinstance(node.parent, nodes.table):
self.body.append('</span>') self.body.append('</span>')
self.add_permalink_ref(node.parent, _('Permalink to this table')) 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: def unknown_visit(self, node: Node) -> None:
raise NotImplementedError('Unknown node: ' + node.__class__.__name__) 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

View File

@ -1665,3 +1665,23 @@ def test_highlight_options_old(app):
location=ANY, opts={}) location=ANY, opts={})
assert call_args[2] == call(ANY, 'java', force=False, linenos=False, assert call_args[2] == call(ANY, 'java', force=False, linenos=False,
location=ANY, opts={}) 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 '<h1>The basic Sphinx documentation for testing</h1>' in content
@pytest.mark.sphinx('html', testroot='basic',
confoverrides={'html_permalinks_icon': '<span>[PERMALINK]</span>'})
def test_html_permalink_icon(app):
app.build()
content = (app.outdir / 'index.html').read_text()
assert ('<h1>The basic Sphinx documentation for testing<a class="headerlink" '
'href="#the-basic-sphinx-documentation-for-testing" '
'title="Permalink to this headline"><span>[PERMALINK]</span></a></h1>' in content)