diff --git a/CHANGES.rst b/CHANGES.rst index f01226e029a..21b7a557725 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -58,6 +58,8 @@ Features added Patch by Bénédikt Tran and Adam Turner. * #12524: Add a ``class`` option to the :rst:dir:`toctree` directive. Patch by Tim Hoffmann. +* #12536: Add the :rst:dir:`confval` directive. + Patch by Adam Turner. Bugs fixed ---------- diff --git a/doc/conf.py b/doc/conf.py index 3fb4e8ceaf5..65142491fc8 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -328,12 +328,6 @@ def setup(app: Sphinx) -> None: app.connect('autodoc-process-docstring', cut_lines(4, what=['module'])) app.connect('source-read', linkify_issues_in_changelog) app.connect('build-finished', build_redirects) - app.add_object_type( - 'confval', - 'confval', - objname='configuration value', - indextemplate='pair: %s; configuration value', - ) fdesc = GroupedField('parameter', label='Parameters', names=['param'], can_collapse=True) app.add_object_type( 'event', 'event', 'pair: %s; event', parse_event, doc_field_types=[fdesc] diff --git a/doc/usage/domains/standard.rst b/doc/usage/domains/standard.rst index 59b7e72c167..a676a2dcbf5 100644 --- a/doc/usage/domains/standard.rst +++ b/doc/usage/domains/standard.rst @@ -42,6 +42,44 @@ There is a set of directives allowing documenting command-line programs: ``cmdoption`` directive is a deprecated alias for the ``option`` directive. +.. rst:directive:: .. confval:: name + + Describes a configuration value or setting that the documented + code or program uses or defines. + Referenceable by :rst:role:`confval`. + + .. rst:directive:option:: type + :type: text + + Describes the type of the configuration value. + This is optional, and if specified will be interpreted as reStructuredText. + + .. rst:directive:option:: default + :type: text + + Describes the default value of the configuration value. + This is optional, and if specified will be interpreted as reStructuredText. + + Example: + + .. code-block:: rst + + .. confval:: the_answer + :type: ``int`` (a *number*) + :default: **42** + + This is a setting that controls the value of the answer. + + will be rendered as follows: + + .. confval:: the_answer + :no-contents-entry: + :no-index-entry: + :type: ``int`` (a *number*) + :default: **42** + + This is a setting that controls the value of the answer. + .. rst:directive:: .. envvar:: name Describes an environment variable that the documented code or program uses diff --git a/doc/usage/referencing.rst b/doc/usage/referencing.rst index c2ad715be2c..836c1052e95 100644 --- a/doc/usage/referencing.rst +++ b/doc/usage/referencing.rst @@ -222,6 +222,13 @@ Cross-referencing other items of interest The following roles do possibly create a cross-reference, but do not refer to objects: +.. rst:role:: confval + + A configuration value or setting. + Index entries are generated. + Also generates a link to the matching :rst:dir:`confval` directive, + if it exists. + .. rst:role:: envvar An environment variable. Index entries are generated. Also generates a link diff --git a/sphinx/domains/std/__init__.py b/sphinx/domains/std/__init__.py index f2cdbc0e81a..008367b566a 100644 --- a/sphinx/domains/std/__init__.py +++ b/sphinx/domains/std/__init__.py @@ -102,6 +102,76 @@ def result_nodes(self, document: nodes.document, env: BuildEnvironment, node: El return [indexnode, targetnode, node], [] +class ConfigurationValue(ObjectDescription[str]): + index_template: str = _('%s; configuration value') + option_spec: ClassVar[OptionSpec] = { + 'no-index': directives.flag, + 'no-index-entry': directives.flag, + 'no-contents-entry': directives.flag, + 'no-typesetting': directives.flag, + 'type': directives.unchanged_required, + 'default': directives.unchanged_required, + } + + def handle_signature(self, sig: str, sig_node: desc_signature) -> str: + sig_node.clear() + sig_node += addnodes.desc_name(sig, sig) + name = ws_re.sub(' ', sig) + sig_node['fullname'] = name + return name + + def _object_hierarchy_parts(self, sig_node: desc_signature) -> tuple[str, ...]: + return (sig_node['fullname'],) + + def _toc_entry_name(self, sig_node: desc_signature) -> str: + if not sig_node.get('_toc_parts'): + return '' + name, = sig_node['_toc_parts'] + return name + + def add_target_and_index(self, name: str, sig: str, signode: desc_signature) -> None: + node_id = make_id(self.env, self.state.document, self.objtype, name) + signode['ids'].append(node_id) + self.state.document.note_explicit_target(signode) + index_entry = self.index_template % name + self.indexnode['entries'].append(('pair', index_entry, node_id, '', None)) + self.env.domains['std'].note_object(self.objtype, name, node_id, location=signode) + + def transform_content(self, content_node: addnodes.desc_content) -> None: + """Insert *type* and *default* as a field list.""" + field_list = nodes.field_list() + if 'type' in self.options: + field, msgs = self.format_type(self.options['type']) + field_list.append(field) + field_list += msgs + if 'default' in self.options: + field, msgs = self.format_default(self.options['default']) + field_list.append(field) + field_list += msgs + if len(field_list.children) > 0: + content_node.insert(0, field_list) + + def format_type(self, type_: str) -> tuple[nodes.field, list[system_message]]: + """Formats the ``:type:`` option.""" + parsed, msgs = self.parse_inline(type_, lineno=self.lineno) + field = nodes.field( + '', + nodes.field_name('', _('Type')), + nodes.field_body('', *parsed), + ) + return field, msgs + + def format_default(self, default: str) -> tuple[nodes.field, list[system_message]]: + """Formats the ``:default:`` option.""" + parsed, msgs = self.parse_inline(default, lineno=self.lineno) + field = nodes.field( + '', + nodes.field_name('', _('Default')), + nodes.field_body('', *parsed), + ) + return field, msgs + + class Target(SphinxDirective): """ Generic target for user-defined cross-reference types. @@ -527,6 +597,7 @@ class StandardDomain(Domain): 'token': ObjType(_('grammar token'), 'token', searchprio=-1), 'label': ObjType(_('reference label'), 'ref', 'keyword', searchprio=-1), + 'confval': ObjType('configuration value', 'confval'), 'envvar': ObjType(_('environment variable'), 'envvar'), 'cmdoption': ObjType(_('program option'), 'option'), 'doc': ObjType(_('document'), 'doc', searchprio=-1), @@ -536,12 +607,14 @@ class StandardDomain(Domain): 'program': Program, 'cmdoption': Cmdoption, # old name for backwards compatibility 'option': Cmdoption, + 'confval': ConfigurationValue, 'envvar': EnvVar, 'glossary': Glossary, 'productionlist': ProductionList, } roles: dict[str, RoleFunction | XRefRole] = { 'option': OptionXRefRole(warn_dangling=True), + 'confval': XRefRole(warn_dangling=True), 'envvar': EnvVarXRefRole(), # links to tokens in grammar productions 'token': TokenXRefRole(),