diff --git a/CHANGES b/CHANGES index 8f138482022..690b32cab52 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,9 @@ Deprecated Features added -------------- +* #10366: std domain: Add support for emphasising placeholders in :rst:dir`option` + directives through a new ``option_emphasise_placeholders`` configuration option. + Bugs fixed ---------- diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index f297e5c5cbc..4c587c5241f 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -706,6 +706,15 @@ General configuration .. versionadded:: 3.0 +.. confval:: option_emphasise_placeholders + + Default is ``False``. + When enabled, emphasise placeholders in ``.. option:`` directives. + To display literal braces, escape with a backslash (``\{``). For example, + ``option_emphasise_placeholders=True`` and ``.. option:: -foption={TYPE}`` would + render with ``TYPE`` emphasised. + + .. versionadded:: 5.1 .. _intl-options: diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst index 30bde8ea138..54e32540037 100644 --- a/doc/usage/restructuredtext/domains.rst +++ b/doc/usage/restructuredtext/domains.rst @@ -1750,6 +1750,9 @@ There is a set of directives allowing documenting command-line programs: referenceable by :rst:role:`option` (in the example case, you'd use something like ``:option:`dest_dir```, ``:option:`-m```, or ``:option:`--module```). + Use :confval:`option_emphasise_placeholders` for parsing of + "variable part" of a literal text (similarly to the ``samp`` role). + ``cmdoption`` directive is a deprecated alias for the ``option`` directive. .. rst:directive:: .. envvar:: name diff --git a/sphinx/config.py b/sphinx/config.py index 8c6dcfe3246..318173f2752 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -140,6 +140,7 @@ class Config: 'smartquotes_excludes': ({'languages': ['ja'], 'builders': ['man', 'text']}, 'env', []), + 'option_emphasise_placeholders': (False, 'env', []), } def __init__(self, config: Dict[str, Any] = {}, overrides: Dict[str, Any] = {}) -> None: diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index d5c962dc827..8ff199ade50 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -15,7 +15,7 @@ from sphinx.directives import ObjectDescription from sphinx.domains import Domain, ObjType from sphinx.locale import _, __ -from sphinx.roles import XRefRole +from sphinx.roles import EmphasizedLiteral, XRefRole from sphinx.util import docname_join, logging, ws_re from sphinx.util.docutils import SphinxDirective from sphinx.util.nodes import clean_astext, make_id, make_refnode @@ -34,6 +34,8 @@ # RE for grammar tokens token_re = re.compile(r'`((~?\w*:)?\w+)`', re.U) +samp_role = EmphasizedLiteral() + class GenericObject(ObjectDescription[str]): """ @@ -170,15 +172,41 @@ def handle_signature(self, sig: str, signode: desc_signature) -> str: location=signode) continue optname, args = m.groups() - if optname.endswith('[') and args.endswith(']'): + if optname[-1] == '[' and args[-1] == ']': # optional value surrounded by brackets (ex. foo[=bar]) optname = optname[:-1] args = '[' + args if count: - signode += addnodes.desc_addname(', ', ', ') + if self.env.config.option_emphasise_placeholders: + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_space() + else: + signode += addnodes.desc_addname(', ', ', ') signode += addnodes.desc_name(optname, optname) - signode += addnodes.desc_addname(args, args) + if self.env.config.option_emphasise_placeholders: + add_end_bracket = False + if not args: + continue + if args[0] == '[' and args[-1] == ']': + add_end_bracket = True + signode += addnodes.desc_sig_punctuation('[', '[') + args = args[1:-1] + if args[0] == ' ': + signode += addnodes.desc_sig_space() + args = args.strip() + if args[0] == '=': + signode += addnodes.desc_sig_punctuation('=', '=') + args = args[1:] + for part in samp_role.parse(args): + if isinstance(part, nodes.Text): + signode += nodes.Text(part.astext()) + else: + signode += part + if add_end_bracket: + signode += addnodes.desc_sig_punctuation(']', ']') + else: + signode += addnodes.desc_addname(args, args) if not count: firstname = optname signode['allnames'] = [optname] diff --git a/tests/roots/test-root/objects.txt b/tests/roots/test-root/objects.txt index 7e2db1bb872..a4a5c667c9f 100644 --- a/tests/roots/test-root/objects.txt +++ b/tests/roots/test-root/objects.txt @@ -194,6 +194,15 @@ Link to :option:`perl +p`, :option:`--ObjC++`, :option:`--plugin.option`, :optio Link to :option:`hg commit` and :option:`git commit -p`. +.. option:: --abi={TYPE} + +.. option:: --test={WHERE}-{COUNT} + +.. option:: --wrap=\{\{value\}\} + +.. option:: -allowable_client {client_name} + +Foo bar. User markup =========== diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 75f89a67a0f..3ef0e36550d 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -1735,3 +1735,25 @@ def test_html_code_role(app): assert ('
' + '
' +
             common_content) in content
+
+
+@pytest.mark.sphinx('html', testroot='root',
+                    confoverrides={'option_emphasise_placeholders': True})
+def test_option_emphasise_placeholders(app, status, warning):
+    app.build()
+    content = (app.outdir / 'objects.html').read_text()
+    assert 'TYPE' in content
+    assert '{TYPE}' not in content
+    assert ('WHERE'
+            '-'
+            'COUNT' in content)
+    assert '{{value}}' in content
+
+
+@pytest.mark.sphinx('html', testroot='root')
+def test_option_emphasise_placeholders_default(app, status, warning):
+    app.build()
+    content = (app.outdir / 'objects.html').read_text()
+    assert '={TYPE}' in content
+    assert '={WHERE}-{COUNT}' in content
+    assert '{client_name}' in content