From 8314af05ae89103f67a28bac0ea3ee7337055eb0 Mon Sep 17 00:00:00 2001
From: =?utf8?q?Vladim=C3=ADr=20Vondru=C5=A1?=
Date: Wed, 28 Aug 2019 22:31:29 +0200
Subject: [PATCH] m.sphinx: added a :p: role for parameter referencing.
So tiny yet so useful. Doxygen, take a note.
---
doc/plugins/sphinx.rst | 9 ++++++++-
.../test_python/content/content.html | 2 +-
documentation/test_python/content/docs.rst | 4 ++--
plugins/m/sphinx.py | 20 ++++++++++++++++---
4 files changed, 28 insertions(+), 7 deletions(-)
diff --git a/doc/plugins/sphinx.rst b/doc/plugins/sphinx.rst
index 5a81819e..fbade22f 100644
--- a/doc/plugins/sphinx.rst
+++ b/doc/plugins/sphinx.rst
@@ -313,10 +313,17 @@ function signature will cause a warning. Example:
:param overwrite_existing: Overwrite existing value if already present
in the container
:return: The inserted tuple or the existing
- key/value pair in case ``overwrite_existing`` is not set
+ key/value pair in case :p:`overwrite_existing` is not set
The operation has a :math:`\mathcal{O}(\log{}n)` complexity.
+.. block-success:: Referencing function parameters
+
+ What's also shown in the above snippet is the :rst:`:p:` directive. It
+ looks the same as if you would write just :rst:```overwrite_existing```,
+ but in addition it checks the parameter name against current function signature, emitting a warning in case of a mismatch. This is useful to
+ ensure the documentation doesn't get out of sync with the actual signature.
+
For overloaded functions (such as those coming from pybind11), it's possible to
specify the full signature to distinguish between particular overloads.
Directives with the full signature have a priority, if no signature matches
diff --git a/documentation/test_python/content/content.html b/documentation/test_python/content/content.html
index 47506485..1d8573f8 100644
--- a/documentation/test_python/content/content.html
+++ b/documentation/test_python/content/content.html
@@ -213,7 +213,7 @@ or parsed in any way.
b |
- The second one |
+ The second one is different from a |
c |
diff --git a/documentation/test_python/content/docs.rst b/documentation/test_python/content/docs.rst
index 3c10487b..e9303f12 100644
--- a/documentation/test_python/content/docs.rst
+++ b/documentation/test_python/content/docs.rst
@@ -100,7 +100,7 @@
.. py:function:: content.param_docs
:param a: First parameter
- :param b: The second one
+ :param b: The second one is different from :p:`a`
:param c: And a ``float``
:return: String, of course, it's all *stringly* typed
@@ -110,7 +110,7 @@
:param a: First
:param c: Third
- The ``b`` is not documented, while ``c`` isn't in the signature.
+ The :p:`b` is not documented, while :p:`c` isn't in the signature.
.. py:function:: content.full_docstring
:param a: First parameter
diff --git a/plugins/m/sphinx.py b/plugins/m/sphinx.py
index 49b706e0..9d37e215 100755
--- a/plugins/m/sphinx.py
+++ b/plugins/m/sphinx.py
@@ -46,6 +46,7 @@ import m.htmlsanity
# All those initialized in register() or register_mcss()
current_referer_path = None
+current_param_names = None
module_doc_output = None
class_doc_output = None
enum_doc_output = None
@@ -306,19 +307,31 @@ def ref(name, rawtext, text, lineno, inliner: Inliner, options={}, content=[]):
node = nodes.literal(rawtext, target, **_options)
return [node], []
-def scope_enter(path, **kwargs):
- global current_referer_path
+def scope_enter(path, param_names=None, **kwargs):
+ global current_referer_path, current_param_names
current_referer_path += [path]
+ current_param_names = param_names
def scope_exit(path, **kwargs):
- global current_referer_path
+ global current_referer_path, current_param_names
assert current_referer_path[-1] == path, "%s %s" % (current_referer_path, path)
current_referer_path = current_referer_path[:-1]
+ current_param_names = None
def check_scope_stack_empty(**kwargs):
global current_referer_path
assert not current_referer_path
+def p(name, rawtext, text, lineno, inliner: Inliner, options={}, content=[]):
+ global current_referer_path, current_param_names
+ if not current_param_names:
+ logging.warning("can't reference parameter %s outside of a function scope", text)
+ elif text not in current_param_names:
+ logging.warning("parameter %s not found in %s(%s) function signature", text, '.'.join(current_referer_path[-1]), ', '.join(current_param_names))
+
+ node = nodes.literal(rawtext, text, **options)
+ return [node], []
+
def consume_docstring(type, path: List[str], signature: Optional[str], doc: str) -> str:
# Create the directive header based on type
if type.name == 'MODULE':
@@ -495,6 +508,7 @@ def register_mcss(mcss_settings, module_doc_contents, class_doc_contents, enum_d
rst.directives.register_directive('py:data', PyData)
rst.roles.register_local_role('ref', ref)
+ rst.roles.register_local_role('p', p)
hooks_pre_scope += [scope_enter]
hooks_post_scope += [scope_exit]
--
2.30.2