diff --git a/antsibull/jinja2/filters.py b/antsibull/jinja2/filters.py
index e52a0a30b..a73d872cb 100644
--- a/antsibull/jinja2/filters.py
+++ b/antsibull/jinja2/filters.py
@@ -31,10 +31,10 @@
def _option_name_html(matcher):
- parts = matcher.group(1).split('=', 1)
- if len(parts) == 1:
- return f'{parts[0]}'
- return f'{parts[0]}={parts[1]}
'
+ text = matcher.group(1)
+ if '=' not in text and ':' not in text:
+ return f'{text}
'
+ return f'{text}
'
def html_ify(text):
@@ -54,8 +54,10 @@ def html_ify(text):
text, _counts['link'] = _LINK.subn(r"\1", text)
text, _counts['const'] = _CONST.subn(r"\1
", text)
text, _counts['option-name'] = _SEM_OPTION_NAME.subn(_option_name_html, text)
- text, _counts['option-value'] = _SEM_OPTION_VALUE.subn(r"\1
", text)
- text, _counts['environment-var'] = _SEM_ENV_VARIABLE.subn(r"\1
", text)
+ text, _counts['option-value'] = _SEM_OPTION_VALUE.subn(
+ r"""\1
""", text)
+ text, _counts['environment-var'] = _SEM_ENV_VARIABLE.subn(
+ r"""\1
""", text)
text, _counts['ruler'] = _RULER.subn(r"
", text)
text = text.strip()
@@ -85,13 +87,6 @@ def do_max(seq):
return max(seq)
-def _option_name_rst(matcher):
- parts = matcher.group(1).split('=', 1)
- if len(parts) == 1:
- return f'*{parts[0]}*'
- return f'*{parts[0]}* |equalsign| ``{parts[1]}``'
-
-
def rst_ify(text):
''' convert symbols like I(this is in italics) to valid restructured text '''
@@ -107,9 +102,9 @@ def rst_ify(text):
text, _counts['ref'] = _URL.subn(r"\1", text)
text, _counts['link'] = _REF.subn(r":ref:`\1 <\2>`", text)
text, _counts['const'] = _CONST.subn(r"``\1``", text)
- text, _counts['option-name'] = _SEM_OPTION_NAME.subn(_option_name_rst, text)
- text, _counts['option-value'] = _SEM_OPTION_VALUE.subn(r"``\1``", text)
- text, _counts['environment-var'] = _SEM_ENV_VARIABLE.subn(r"``\1``", text)
+ text, _counts['option-name'] = _SEM_OPTION_NAME.subn(r":ansopt:`\1`", text)
+ text, _counts['option-value'] = _SEM_OPTION_VALUE.subn(r":ansval:`\1`", text)
+ text, _counts['environment-var'] = _SEM_ENV_VARIABLE.subn(r":envvar:`\1`", text)
text, _counts['ruler'] = _RULER.subn(f"\n\n{'-' * 13}\n\n", text)
flog.fields(counts=_counts).info('Number of macros converted to rst equivalents')
diff --git a/antsibull/lint_extra_docs.py b/antsibull/lint_extra_docs.py
index 337277ec7..ec5d21391 100644
--- a/antsibull/lint_extra_docs.py
+++ b/antsibull/lint_extra_docs.py
@@ -11,6 +11,10 @@
import docutils.utils
import rstcheck
+from docutils.parsers.rst import roles as docutils_roles
+
+from sphinx_antsibull_ext import roles as antsibull_roles
+
from .extra_docs import (
find_extra_docs,
lint_required_conditions,
@@ -46,6 +50,12 @@ def lint_optional_conditions(content: str, path: str, collection_name: str
return [(result[0], 0, result[1]) for result in results]
+def _setup_rstcheck():
+ '''Make sure that rstcheck knows about our roles.'''
+ for name, role in antsibull_roles.ROLES.items():
+ docutils_roles.register_local_role(name, role)
+
+
def lint_collection_extra_docs_files(path_to_collection: str
) -> t.List[t.Tuple[str, int, int, str]]:
try:
@@ -56,6 +66,7 @@ def lint_collection_extra_docs_files(path_to_collection: str
result = []
all_labels = set()
docs = find_extra_docs(path_to_collection)
+ _setup_rstcheck()
for doc in docs:
try:
# Load content
diff --git a/sphinx_antsibull_ext/__init__.py b/sphinx_antsibull_ext/__init__.py
index a7cf7ca1c..c6e3cce70 100644
--- a/sphinx_antsibull_ext/__init__.py
+++ b/sphinx_antsibull_ext/__init__.py
@@ -15,6 +15,7 @@
from .assets import setup_assets
+from .roles import setup_roles
def setup(app):
@@ -26,6 +27,9 @@ def setup(app):
# Add assets
setup_assets(app)
+ # Add roles
+ setup_roles(app)
+
return dict(
parallel_read_safe=True,
parallel_write_safe=True,
diff --git a/sphinx_antsibull_ext/roles.py b/sphinx_antsibull_ext/roles.py
new file mode 100644
index 000000000..a7b3a47f3
--- /dev/null
+++ b/sphinx_antsibull_ext/roles.py
@@ -0,0 +1,71 @@
+# coding: utf-8
+# Author: Felix Fontein
+# License: GPLv3+
+# Copyright: Ansible Project, 2021
+'''
+Add roles for semantic markup.
+'''
+
+from __future__ import (absolute_import, division, print_function)
+
+
+from docutils import nodes
+
+
+def option_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
+ """Format Ansible option key, or option key-value.
+
+ Returns 2 part tuple containing list of nodes to insert into the
+ document and a list of system messages. Both are allowed to be
+ empty.
+
+ :param name: The role name used in the document.
+ :param rawtext: The entire markup snippet, with role.
+ :param text: The text marked with the role.
+ :param lineno: The line number where rawtext appears in the input.
+ :param inliner: The inliner instance that called us.
+ :param options: Directive options for customization.
+ :param content: The directive content for customization.
+ """
+ children = []
+ classes = []
+ if '=' not in text and ':' not in text:
+ children.append(nodes.strong(rawtext, text))
+ rawtext = ''
+ text = ''
+ classes.append('ansible-option')
+ else:
+ classes.append('ansible-option-value')
+ return [nodes.literal(rawtext, text, *children, classes=classes)], []
+
+
+def value_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
+ """Format Ansible option value.
+
+ Returns 2 part tuple containing list of nodes to insert into the
+ document and a list of system messages. Both are allowed to be
+ empty.
+
+ :param name: The role name used in the document.
+ :param rawtext: The entire markup snippet, with role.
+ :param text: The text marked with the role.
+ :param lineno: The line number where rawtext appears in the input.
+ :param inliner: The inliner instance that called us.
+ :param options: Directive options for customization.
+ :param content: The directive content for customization.
+ """
+ return [nodes.literal(rawtext, text, classes=['ansible-value'])], []
+
+
+ROLES = {
+ 'ansopt': option_role,
+ 'ansval': value_role,
+}
+
+
+def setup_roles(app):
+ '''
+ Setup roles for a Sphinx app object.
+ '''
+ for name, role in ROLES.items():
+ app.add_role(name, role)