sphinx-java-autodoc 0.3.0__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/PKG-INFO +1 -1
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/javadoc-support.rst +5 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/schema.md +20 -7
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/usage.rst +10 -2
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/examples/standalone/src/demo/Widget.java +1 -1
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/pyproject.toml +1 -1
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/src/sphinx_java_autodoc/domain.py +94 -19
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/src/sphinx_java_autodoc/extractor.py +1 -1
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/src/sphinx_java_autodoc/java/SphinxDoclet.java +182 -34
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/src/sphinx_java_autodoc/render.py +19 -3
- sphinx_java_autodoc-0.5.0/tests/test_conformance.py +235 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/tests/test_generic_throws.py +2 -1
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/tests/test_integration.py +51 -8
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/tests/test_navigation.py +12 -5
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/tests/test_signatures.py +25 -1
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/tests/visual_check.py +13 -2
- sphinx_java_autodoc-0.3.0/tests/test_conformance.py +0 -92
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/.gitignore +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/CONTRIBUTING.md +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/LICENSE +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/README.md +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/SECURITY.md +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/adr/0001-jdk-doclet-xml-sphinx-domain.md +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/agents/domain.md +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/agents/issue-tracker.md +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/alternatives.rst +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/build-integration.rst +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/compatibility.rst +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/conf.py +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/design.rst +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/gallery/comments.rst +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/gallery/declarations.rst +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/gallery/directives.rst +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/gallery/index.rst +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/gallery/inheritance.rst +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/gallery/links.rst +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/gallery/warnings.rst +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/getting-started.rst +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/guide.rst +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/index.rst +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/performance.rst +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/showcase.rst +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/docs/troubleshooting.rst +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/examples/README.md +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/examples/build-consumer/BuildInfo.java.in +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/examples/build-consumer/src/demo/Consumer.java +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/examples/dependency/src/vendor/Token.java +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/examples/gradle/build.gradle +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/examples/gradle/settings.gradle +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/examples/maven/pom.xml +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/examples/standalone/docs/conf.py +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/examples/standalone/docs/guide.rst +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/examples/standalone/docs/index.rst +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/examples/standalone/docs/reference.rst +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/examples/standalone/src/demo/Label.java +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/examples/standalone/src/demo/Mode.java +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/examples/standalone/src/demo/Point.java +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/examples/standalone/src/demo/Shape.java +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/examples/standalone/src/demo/Transformer.java +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/examples/standalone/src/demo/package-info.java +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/src/sphinx_java_autodoc/__init__.py +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/src/sphinx_java_autodoc/__main__.py +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/src/sphinx_java_autodoc/javadoc.py +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/src/sphinx_java_autodoc/model.py +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/src/sphinx_java_autodoc/pages.py +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/src/sphinx_java_autodoc/static/java-autodoc.css +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/tests/benchmark.py +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/tests/build_tools_check.py +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/tests/test_boundaries.py +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/tests/test_javadoc.py +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/tests/test_options.py +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/tests/test_snippets.py +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/tests/test_sourcepath.py +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/tests/test_tables.py +0 -0
- {sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/tests/test_value.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sphinx-java-autodoc
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Compiler-backed Java API documentation in native Sphinx nodes
|
|
5
5
|
Project-URL: Documentation, https://r4sky0.github.io/sphinx-java-autodoc/
|
|
6
6
|
Project-URL: Repository, https://github.com/r4sky0/sphinx-java-autodoc
|
|
@@ -20,6 +20,11 @@ Tags and HTML
|
|
|
20
20
|
``@author``, ``@version`` and the ``@serial`` tags are omitted silently, as in the
|
|
21
21
|
standard doclet's default output. Other custom tags warn.
|
|
22
22
|
|
|
23
|
+
Text reads as in the standard doclet. A link without a label shows
|
|
24
|
+
``Map.Entry``, ``String.format(String, Object...)``, or ``of(L, R)`` for a member of
|
|
25
|
+
the same type. Sphinx's smart quotes are off, so quotes and ``...`` stay as written.
|
|
26
|
+
``@throws`` names link to the exception type.
|
|
27
|
+
|
|
23
28
|
Constant values
|
|
24
29
|
---------------
|
|
25
30
|
|
|
@@ -26,12 +26,20 @@ not a general XML document interchange service. No external entities are fetched
|
|
|
26
26
|
parameter or record component. `r` carries `target` (canonical ID) and the external
|
|
27
27
|
Javadoc `path`, `module` and `fragment`; its text is the nested simple name.
|
|
28
28
|
Readers reject unknown tokens and malformed `r` targets.
|
|
29
|
-
- `doc/part`: `kind` (`body`, `
|
|
30
|
-
`since`, `deprecated`, `apiNote`, `implSpec`, `implNote`)
|
|
31
|
-
|
|
29
|
+
- `doc/part`: `kind` (`body`, `specifiedBy`, `overrides`, `param`, `typeparam`,
|
|
30
|
+
`return`, `throws`, `see`, `since`, `deprecated`, `apiNote`, `implSpec`, `implNote`)
|
|
31
|
+
and a `label`. The three note kinds, `specifiedBy` and `overrides` are additive in
|
|
32
|
+
schema 1: older renderers show them under their kind name. `specifiedBy` and
|
|
33
|
+
`overrides` follow `body`: one `specifiedBy` per implemented interface method, then
|
|
34
|
+
the superclass method as `overrides`, or `specifiedBy` when it is abstract. Each
|
|
35
|
+
holds a method `link`, ` in class ` or ` in interface ` text, and a type `link`. Parameter labels follow the child declaration
|
|
32
36
|
even when prose is inherited. Part contents are an ordered token sequence.
|
|
37
|
+
`typeparam` parts document the type parameters of generic types and methods.
|
|
33
38
|
Throws labels name the overriding method's exception after generic substitution;
|
|
34
|
-
type variables use their declared names.
|
|
39
|
+
type variables use their declared names. A `throws` part whose exception is a type
|
|
40
|
+
also carries `target`, `role` and the external Javadoc `path`, `module` and
|
|
41
|
+
`fragment`, as `link` does, and its label is the type's name without its package.
|
|
42
|
+
These attributes are additive in schema 1.
|
|
35
43
|
Repeated `throws` descriptions for one exception share a part, separated by
|
|
36
44
|
paragraph tokens, so explicit and omitted inheritance preserve all descriptions.
|
|
37
45
|
- Tokens: `text`, `literal`, `code`, `link`, `start`, `end`. Text is element text,
|
|
@@ -52,7 +60,9 @@ not a general XML document interchange service. No external entities are fetched
|
|
|
52
60
|
- `link`: `target` is the compiler's canonical symbol ID; `role` identifies the
|
|
53
61
|
domain object kind. `path`, `module`, `fragment` hold external Javadoc location
|
|
54
62
|
metadata; `plain` distinguishes link/plainlink presentation. Children form the
|
|
55
|
-
label
|
|
63
|
+
label; a comment link without one gets the standard doclet's text, such as
|
|
64
|
+
`Map.Entry`, `String.format(String, Object...)` or `of(L, R)` within the same type.
|
|
65
|
+
An unresolved link has no resolved location and has a diagnostic.
|
|
56
66
|
- `diagnostic`: symbol-qualified warning text, including unsupported constructs.
|
|
57
67
|
Warnings survive cache hits and are re-emitted by Sphinx.
|
|
58
68
|
|
|
@@ -61,8 +71,11 @@ Identity examples: `example.Outer.Inner`, `example.Outer#FIELD`,
|
|
|
61
71
|
Parameters in canonical IDs are fully qualified erased types; varargs use `[]`.
|
|
62
72
|
External Javadoc fragments instead preserve declared type variables and use `...`
|
|
63
73
|
for varargs, as verified against the standard doclet. Return types and
|
|
64
|
-
parameter names do not affect IDs. HTML anchors are `java
|
|
65
|
-
|
|
74
|
+
parameter names do not affect IDs. HTML anchors are `java:` plus the ID, with `#`
|
|
75
|
+
replaced by `-` and `<init>` by `new`, as in `java:example.Outer-find(java.lang.String,int[])`
|
|
76
|
+
and `java:example.Outer-new()`. They are deterministic, reversible and collision-free:
|
|
77
|
+
docutils never generates `:` in IDs, Java names never contain `-`, and the keyword
|
|
78
|
+
`new` cannot name a method.
|
|
66
79
|
|
|
67
80
|
Declarations and diagnostics are sorted; no build timestamp or source path is
|
|
68
81
|
stored. Renderers derive a declaration's file from its package and top-level type
|
|
@@ -95,7 +95,9 @@ Other directives work the same way:
|
|
|
95
95
|
|
|
96
96
|
``java:autopackage`` renders the ``package-info.java`` comment.
|
|
97
97
|
``java:autosummary`` lists each entry with a link and its first sentence; unknown
|
|
98
|
-
entries warn as ``java.summary``.
|
|
98
|
+
entries warn as ``java.summary``. As in Javadoc, members are listed with their
|
|
99
|
+
declared parameters, and methods and fields get a column with their modifiers and
|
|
100
|
+
type, such as ``static <L, R> Pair<L, R>``. Generated pages consist of these directives.
|
|
99
101
|
|
|
100
102
|
What gets documented
|
|
101
103
|
--------------------
|
|
@@ -105,7 +107,12 @@ What gets documented
|
|
|
105
107
|
protected types.
|
|
106
108
|
- Only declared members are listed. An overriding method inherits its ancestor's
|
|
107
109
|
comment; other inherited methods stay on the declaring type's page.
|
|
110
|
+
- As in Javadoc, an overriding method notes what it implements or overrides:
|
|
111
|
+
"Specified by: ``convert`` in interface ``Transformer``", or "Overrides:
|
|
112
|
+
``hashCode`` in class ``Object``".
|
|
108
113
|
- Nested types get their own pages.
|
|
114
|
+
- Signatures show only ``@Documented`` annotations, as the standard doclet does, so
|
|
115
|
+
``@Override`` and ``@SuppressWarnings`` are left out.
|
|
109
116
|
|
|
110
117
|
Roles
|
|
111
118
|
-----
|
|
@@ -147,7 +154,8 @@ source root, such as ``com/example/Widget.java``) and ``line``:
|
|
|
147
154
|
f"{info['source']}#L{info['line']}"
|
|
148
155
|
)
|
|
149
156
|
|
|
150
|
-
``line`` is empty for compiler-generated members, such as an enum's
|
|
157
|
+
``line`` is empty for packages and for compiler-generated members, such as an enum's
|
|
158
|
+
``values()``.
|
|
151
159
|
With several source roots, choose the root in ``linkcode_resolve``, for example by
|
|
152
160
|
checking which root contains ``source``.
|
|
153
161
|
|
{sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/examples/standalone/src/demo/Widget.java
RENAMED
|
@@ -50,7 +50,7 @@ public class Widget implements Transformer<String> {
|
|
|
50
50
|
* @throws IllegalArgumentException {@inheritDoc}
|
|
51
51
|
*/
|
|
52
52
|
@Override public String convert(String renamed) throws IllegalArgumentException { return renamed; }
|
|
53
|
-
/** Converts a number to text
|
|
53
|
+
/** Converts a number to text, such as "42"...
|
|
54
54
|
* @param number the number
|
|
55
55
|
* @return decimal text
|
|
56
56
|
*/
|
|
@@ -29,7 +29,9 @@ LOGGER = logging.getLogger(__name__)
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
def anchor(symbol: str) -> str:
|
|
32
|
-
|
|
32
|
+
# Docutils IDs never contain ":", Java names never contain "-", and the keyword
|
|
33
|
+
# "new" cannot name a method, so these readable anchors cannot collide.
|
|
34
|
+
return "java:" + symbol.replace("#<init>(", "#new(").replace("#", "-")
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
def role_for(element):
|
|
@@ -98,6 +100,49 @@ def signature_nodes(tokens) -> list[nodes.Node]:
|
|
|
98
100
|
return result
|
|
99
101
|
|
|
100
102
|
|
|
103
|
+
# Javadoc's "Modifier and Type" column leaves out visibility and implementation details.
|
|
104
|
+
SUMMARY_MODIFIERS = {"protected", "static", "final", "abstract", "default"}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def summary_type(element) -> list[nodes.Node]:
|
|
108
|
+
"""A member's modifiers, type parameters and type, as in Javadoc's summary column."""
|
|
109
|
+
tokens = []
|
|
110
|
+
sig = list(element.find("sig"))
|
|
111
|
+
i = 0
|
|
112
|
+
while i < len(sig) and sig[i].tag != "n":
|
|
113
|
+
if sig[i].tag == "t" and sig[i].text == "@":
|
|
114
|
+
# An annotation: "@", its type, optional arguments and a space.
|
|
115
|
+
i += 2
|
|
116
|
+
if i < len(sig) and sig[i].tag == "t" and sig[i].text.startswith("("):
|
|
117
|
+
i += 1
|
|
118
|
+
else:
|
|
119
|
+
tokens.append(sig[i])
|
|
120
|
+
i += 1
|
|
121
|
+
owner = SYMBOLS.get(element.get("id").partition("#")[0])
|
|
122
|
+
interface = owner is not None and owner.get("kind") in {
|
|
123
|
+
"interface",
|
|
124
|
+
"annotation_type",
|
|
125
|
+
}
|
|
126
|
+
modifiers = []
|
|
127
|
+
while tokens and (tokens[0].tag == "k" or tokens[0].text == " "):
|
|
128
|
+
text = tokens.pop(0).text
|
|
129
|
+
if text in SUMMARY_MODIFIERS and not (interface and text == "abstract"):
|
|
130
|
+
modifiers.append(text)
|
|
131
|
+
if tokens and tokens[-1].tag == "t" and tokens[-1].text == " ":
|
|
132
|
+
tokens.pop()
|
|
133
|
+
prefix = [nodes.Text(" ".join(modifiers) + " ")] if modifiers else []
|
|
134
|
+
return prefix + signature_nodes(tokens)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def summary_label(element) -> str:
|
|
138
|
+
"""A member's name with declared parameters, as in Javadoc's summaries."""
|
|
139
|
+
params = element.find("sig/params")
|
|
140
|
+
if element.tag == "type" or params is None:
|
|
141
|
+
return local_name(element)
|
|
142
|
+
declared = ("".join(t.text or "" for t in param) for param in params)
|
|
143
|
+
return f"{element.find('sig/n').text}({', '.join(declared)})"
|
|
144
|
+
|
|
145
|
+
|
|
101
146
|
def display_name(symbol: str) -> str:
|
|
102
147
|
owner, _, member = symbol.partition("#")
|
|
103
148
|
return f"{owner}.{member_label(symbol)}" if member else symbol
|
|
@@ -261,34 +306,49 @@ class AutoPackage(JavaDirective):
|
|
|
261
306
|
self.missing("package", package, "package")
|
|
262
307
|
return []
|
|
263
308
|
domain = self.env.get_domain("java")
|
|
264
|
-
target =
|
|
265
|
-
|
|
266
|
-
|
|
309
|
+
target = anchor(package)
|
|
310
|
+
# Javadoc's package pages open with this declaration; as an object description,
|
|
311
|
+
# the package's notes and fields look like those of its types in every theme.
|
|
312
|
+
desc = addnodes.desc(
|
|
313
|
+
"",
|
|
314
|
+
domain="java",
|
|
315
|
+
objtype="package",
|
|
316
|
+
desctype="package",
|
|
317
|
+
classes=["java", "package"],
|
|
318
|
+
)
|
|
319
|
+
sig = addnodes.desc_signature("", "", ids=[target], fullname=package)
|
|
320
|
+
sig += addnodes.desc_sig_keyword("package", "package")
|
|
321
|
+
sig += addnodes.desc_sig_space()
|
|
322
|
+
sig += addnodes.desc_name(package, package)
|
|
323
|
+
self.state.document.note_explicit_target(sig)
|
|
324
|
+
desc += sig
|
|
325
|
+
desc += addnodes.desc_content("", *documentation(element, self.get_location()))
|
|
326
|
+
domain.data["objects"][package] = (self.env.docname, target, "package")
|
|
267
327
|
index = addnodes.index(
|
|
268
|
-
entries=[("single", f"{package} (Java package)",
|
|
328
|
+
entries=[("single", f"{package} (Java package)", target, "", None)]
|
|
269
329
|
)
|
|
270
|
-
return [index,
|
|
330
|
+
return [index, desc]
|
|
271
331
|
|
|
272
332
|
|
|
273
333
|
class AutoSummary(JavaDirective):
|
|
274
|
-
"""A table of linked names and first sentences, like sphinx.ext.autosummary.
|
|
334
|
+
"""A table of linked names and first sentences, like sphinx.ext.autosummary.
|
|
335
|
+
|
|
336
|
+
Methods and fields get a first column with their modifiers and type, as in Javadoc.
|
|
337
|
+
"""
|
|
275
338
|
|
|
276
339
|
has_content = True
|
|
277
340
|
|
|
278
341
|
def run(self):
|
|
279
342
|
self.env.note_dependency(self.env.java_model_path)
|
|
280
343
|
table = nodes.table("", classes=["autosummary", "longtable", "java"])
|
|
281
|
-
|
|
282
|
-
group += nodes.colspec("", colwidth=10)
|
|
283
|
-
group += nodes.colspec("", colwidth=90)
|
|
284
|
-
body = nodes.tbody()
|
|
344
|
+
rows = []
|
|
285
345
|
for line in self.content:
|
|
286
346
|
symbol = re.sub(r"\s+", "", line)
|
|
287
347
|
if not symbol:
|
|
288
348
|
continue
|
|
289
349
|
element = self.lookup(symbol)
|
|
290
350
|
if element is not None:
|
|
291
|
-
label =
|
|
351
|
+
label = summary_label(element)
|
|
292
352
|
elif symbol in PACKAGES:
|
|
293
353
|
element, label = PACKAGES[symbol], symbol
|
|
294
354
|
else:
|
|
@@ -303,17 +363,32 @@ class AutoSummary(JavaDirective):
|
|
|
303
363
|
refexplicit=True,
|
|
304
364
|
refwarn=True,
|
|
305
365
|
)
|
|
306
|
-
text = nodes.paragraph("")
|
|
366
|
+
text = nodes.paragraph("", support_smartquotes=False)
|
|
307
367
|
if deprecated(element):
|
|
308
368
|
text += [nodes.strong("", "Deprecated."), nodes.Text(" ")]
|
|
309
369
|
text.extend(summary(element, self.get_location()))
|
|
310
|
-
|
|
311
|
-
"",
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
)
|
|
315
|
-
|
|
370
|
+
typed = element.tag == "member" and element.get("kind") in {
|
|
371
|
+
"method",
|
|
372
|
+
"field",
|
|
373
|
+
}
|
|
374
|
+
kind = nodes.literal("", "", *summary_type(element)) if typed else None
|
|
375
|
+
rows.append((kind, nodes.paragraph("", "", link), text))
|
|
376
|
+
if not rows:
|
|
316
377
|
return []
|
|
378
|
+
typed = any(kind is not None for kind, _, _ in rows)
|
|
379
|
+
widths = (15, 25, 60) if typed else (10, 90)
|
|
380
|
+
group = nodes.tgroup("", cols=len(widths))
|
|
381
|
+
group.extend(nodes.colspec("", colwidth=width) for width in widths)
|
|
382
|
+
body = nodes.tbody()
|
|
383
|
+
for kind, name, text in rows:
|
|
384
|
+
cells = []
|
|
385
|
+
if typed:
|
|
386
|
+
cells.append(
|
|
387
|
+
nodes.entry()
|
|
388
|
+
if kind is None
|
|
389
|
+
else nodes.entry("", nodes.paragraph("", "", kind))
|
|
390
|
+
)
|
|
391
|
+
body += nodes.row("", *cells, nodes.entry("", name), nodes.entry("", text))
|
|
317
392
|
group += body
|
|
318
393
|
table += group
|
|
319
394
|
return [table]
|
{sphinx_java_autodoc-0.3.0 → sphinx_java_autodoc-0.5.0}/src/sphinx_java_autodoc/extractor.py
RENAMED
|
@@ -16,7 +16,7 @@ from sphinx.errors import ExtensionError
|
|
|
16
16
|
|
|
17
17
|
from .model import Model, read_model
|
|
18
18
|
|
|
19
|
-
VERSION = "0.
|
|
19
|
+
VERSION = "0.5.0" # x-release-please-version
|
|
20
20
|
DOCLET = Path(__file__).parent / "java" / "SphinxDoclet.java"
|
|
21
21
|
PACKAGE = re.compile(r"[^\W\d]\w*(?:\.[^\W\d]\w*)*")
|
|
22
22
|
|
|
@@ -3,6 +3,7 @@ package sphinxjava;
|
|
|
3
3
|
import com.sun.source.doctree.*;
|
|
4
4
|
import com.sun.source.util.DocTreePath;
|
|
5
5
|
import com.sun.source.util.DocTrees;
|
|
6
|
+
import java.lang.annotation.Documented;
|
|
6
7
|
import java.nio.file.*;
|
|
7
8
|
import java.util.*;
|
|
8
9
|
import java.util.function.Consumer;
|
|
@@ -301,7 +302,7 @@ public final class SphinxDoclet implements Doclet {
|
|
|
301
302
|
|
|
302
303
|
private void sig(Xml parent, Element e) {
|
|
303
304
|
var sig = parent.add("sig");
|
|
304
|
-
for (var annotation : e.getAnnotationMirrors()) {
|
|
305
|
+
for (var annotation : documented(e.getAnnotationMirrors())) {
|
|
305
306
|
annotation(sig, annotation);
|
|
306
307
|
sig.addText("t", " ");
|
|
307
308
|
}
|
|
@@ -344,7 +345,7 @@ public final class SphinxDoclet implements Doclet {
|
|
|
344
345
|
var params = sig.add("params");
|
|
345
346
|
for (var component : type.getRecordComponents()) {
|
|
346
347
|
var param = params.add("param");
|
|
347
|
-
for (var annotation : component.getAnnotationMirrors()) {
|
|
348
|
+
for (var annotation : documented(component.getAnnotationMirrors())) {
|
|
348
349
|
annotation(param, annotation);
|
|
349
350
|
param.addText("t", " ");
|
|
350
351
|
}
|
|
@@ -380,7 +381,7 @@ public final class SphinxDoclet implements Doclet {
|
|
|
380
381
|
for (int i = 0; i < parameters.size(); i++) {
|
|
381
382
|
var parameter = parameters.get(i);
|
|
382
383
|
var param = params.add("param");
|
|
383
|
-
for (var annotation : parameter.getAnnotationMirrors()) {
|
|
384
|
+
for (var annotation : documented(parameter.getAnnotationMirrors())) {
|
|
384
385
|
annotation(param, annotation);
|
|
385
386
|
param.addText("t", " ");
|
|
386
387
|
}
|
|
@@ -448,7 +449,7 @@ public final class SphinxDoclet implements Doclet {
|
|
|
448
449
|
}
|
|
449
450
|
|
|
450
451
|
private void typeTokens(Xml sig, TypeMirror type) {
|
|
451
|
-
for (var annotation : type.getAnnotationMirrors()) {
|
|
452
|
+
for (var annotation : documented(type.getAnnotationMirrors())) {
|
|
452
453
|
annotation(sig, annotation);
|
|
453
454
|
sig.addText("t", " ");
|
|
454
455
|
}
|
|
@@ -501,13 +502,24 @@ public final class SphinxDoclet implements Doclet {
|
|
|
501
502
|
|
|
502
503
|
/** A link to a type: its compiler ID, external Javadoc location and nested simple name. */
|
|
503
504
|
private void typeReference(Xml sig, TypeElement type) {
|
|
504
|
-
String pkg = elements.getPackageOf(type).getQualifiedName().toString();
|
|
505
|
-
String name = type.getQualifiedName().toString();
|
|
506
505
|
var reference = sig.add("r").set("target", id(type));
|
|
507
506
|
javadocLocation(reference, type);
|
|
508
|
-
reference
|
|
509
|
-
|
|
510
|
-
|
|
507
|
+
reference.node().setTextContent(Xml.safe(localName(type)));
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/** The type's name without its package, such as {@code Map.Entry}. */
|
|
511
|
+
private String localName(TypeElement type) {
|
|
512
|
+
String pkg = elements.getPackageOf(type).getQualifiedName().toString();
|
|
513
|
+
String name = type.getQualifiedName().toString();
|
|
514
|
+
return pkg.isEmpty() ? name : name.substring(pkg.length() + 1);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/** The standard doclet shows only annotations whose type is {@code @Documented}. */
|
|
518
|
+
private static List<? extends AnnotationMirror> documented(
|
|
519
|
+
List<? extends AnnotationMirror> annotations) {
|
|
520
|
+
return annotations.stream()
|
|
521
|
+
.filter(a -> a.getAnnotationType().asElement().getAnnotation(Documented.class) != null)
|
|
522
|
+
.toList();
|
|
511
523
|
}
|
|
512
524
|
|
|
513
525
|
private void annotation(Xml sig, AnnotationMirror annotation) {
|
|
@@ -601,8 +613,14 @@ public final class SphinxDoclet implements Doclet {
|
|
|
601
613
|
private void putParam(Map<String, Part> result, Element e, ParamTree param) {
|
|
602
614
|
String name = param.getName().toString();
|
|
603
615
|
String key = "param:" + name;
|
|
604
|
-
|
|
605
|
-
|
|
616
|
+
List<? extends Element> declared = null;
|
|
617
|
+
if (param.isTypeParameter() && e instanceof Parameterizable generic) {
|
|
618
|
+
declared = generic.getTypeParameters();
|
|
619
|
+
} else if (e instanceof ExecutableElement method) {
|
|
620
|
+
declared = method.getParameters();
|
|
621
|
+
}
|
|
622
|
+
// Record components keep their names; positions follow renamed overriding parameters.
|
|
623
|
+
if (declared != null) {
|
|
606
624
|
int index = -1;
|
|
607
625
|
for (int i = 0; i < declared.size(); i++) {
|
|
608
626
|
if (declared.get(i).getSimpleName().contentEquals(name)) {
|
|
@@ -761,8 +779,9 @@ public final class SphinxDoclet implements Doclet {
|
|
|
761
779
|
private void documentation(Xml node, Element e) {
|
|
762
780
|
var doc = node.add("doc");
|
|
763
781
|
var own = parts(e);
|
|
764
|
-
var keys = new LinkedHashSet
|
|
782
|
+
var keys = new LinkedHashSet<String>();
|
|
765
783
|
keys.add("body");
|
|
784
|
+
keys.addAll(own.keySet());
|
|
766
785
|
var method = overridingMethod(e);
|
|
767
786
|
if (method != null) {
|
|
768
787
|
for (int i = 0; i < method.getParameters().size(); i++) {
|
|
@@ -779,26 +798,121 @@ public final class SphinxDoclet implements Doclet {
|
|
|
779
798
|
}
|
|
780
799
|
}
|
|
781
800
|
for (var key : keys) {
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
801
|
+
part(doc, e, own, method, key);
|
|
802
|
+
// The standard doclet notes overridden methods between the description and the tags.
|
|
803
|
+
if (key.equals("body")) {
|
|
804
|
+
overrides(doc, e);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
private void part(
|
|
810
|
+
Xml doc, Element e, Map<String, Part> own, ExecutableElement method, String key) {
|
|
811
|
+
Part part = own.get(key);
|
|
812
|
+
if (part == null && method != null) {
|
|
813
|
+
part = findPart(e, key, new HashSet<>());
|
|
814
|
+
}
|
|
815
|
+
if (part == null) {
|
|
816
|
+
if (method != null) {
|
|
817
|
+
warn(
|
|
818
|
+
e,
|
|
819
|
+
"Cannot inherit %s; provide the overridden method's source documentation or"
|
|
820
|
+
+ " document this part explicitly.",
|
|
821
|
+
key);
|
|
785
822
|
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
var xml = doc.add("part").set("kind", key.split(":", 2)[0]).set("label", label(e, key, part));
|
|
826
|
+
if (key.startsWith(THROWS)
|
|
827
|
+
&& elements.getTypeElement(key.substring(THROWS.length())) instanceof TypeElement thrown) {
|
|
828
|
+
xml.set("label", localName(thrown)).set("target", id(thrown)).set("role", "type");
|
|
829
|
+
javadocLocation(xml, thrown);
|
|
830
|
+
}
|
|
831
|
+
String context = key.startsWith(THROWS) ? THROWS + part.label() : key;
|
|
832
|
+
emit(xml, part.owner(), context, part.trees(), new HashSet<>());
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* The standard doclet's notes: "Specified by" for each implemented interface method, then
|
|
837
|
+
* "Overrides" for the superclass method, or "Specified by" when that method is abstract.
|
|
838
|
+
*/
|
|
839
|
+
private void overrides(Xml doc, Element e) {
|
|
840
|
+
if (!(e instanceof ExecutableElement method)
|
|
841
|
+
|| method.getKind() != ElementKind.METHOD
|
|
842
|
+
|| method.getModifiers().contains(Modifier.STATIC)) {
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
var owner = (TypeElement) method.getEnclosingElement();
|
|
846
|
+
var specified = new ArrayList<ExecutableElement>();
|
|
847
|
+
implemented(method, owner, owner.getInterfaces(), specified, new HashSet<>());
|
|
848
|
+
// An interface method that another listed method overrides adds nothing.
|
|
849
|
+
specified.removeIf(
|
|
850
|
+
a ->
|
|
851
|
+
specified.stream()
|
|
852
|
+
.anyMatch(
|
|
853
|
+
b ->
|
|
854
|
+
b != a && elements.overrides(b, a, (TypeElement) b.getEnclosingElement())));
|
|
855
|
+
specified.forEach(ancestor -> overridePart(doc, "specifiedBy", ancestor));
|
|
856
|
+
for (var type = owner.getSuperclass();
|
|
857
|
+
types.asElement(type) instanceof TypeElement superclass;
|
|
858
|
+
type = superclass.getSuperclass()) {
|
|
859
|
+
for (var candidate : superclass.getEnclosedElements()) {
|
|
860
|
+
if (candidate instanceof ExecutableElement ancestor
|
|
861
|
+
&& ancestor.getKind() == ElementKind.METHOD
|
|
862
|
+
&& visible(ancestor, false)
|
|
863
|
+
&& elements.overrides(method, ancestor, owner)) {
|
|
864
|
+
boolean specifies = ancestor.getModifiers().contains(Modifier.ABSTRACT);
|
|
865
|
+
overridePart(doc, specifies ? "specifiedBy" : "overrides", ancestor);
|
|
866
|
+
return;
|
|
793
867
|
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/** Collects the methods of {@code interfaces} and their superinterfaces that method overrides. */
|
|
873
|
+
private void implemented(
|
|
874
|
+
ExecutableElement method,
|
|
875
|
+
TypeElement owner,
|
|
876
|
+
List<? extends TypeMirror> interfaces,
|
|
877
|
+
List<ExecutableElement> result,
|
|
878
|
+
Set<Element> seen) {
|
|
879
|
+
for (var type : interfaces) {
|
|
880
|
+
var face = (TypeElement) types.asElement(type);
|
|
881
|
+
if (!seen.add(face)) {
|
|
794
882
|
continue;
|
|
795
883
|
}
|
|
796
|
-
var
|
|
797
|
-
|
|
798
|
-
|
|
884
|
+
for (var candidate : face.getEnclosedElements()) {
|
|
885
|
+
if (candidate instanceof ExecutableElement ancestor
|
|
886
|
+
&& ancestor.getKind() == ElementKind.METHOD
|
|
887
|
+
&& visible(ancestor, false)
|
|
888
|
+
&& elements.overrides(method, ancestor, owner)) {
|
|
889
|
+
result.add(ancestor);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
implemented(method, owner, face.getInterfaces(), result, seen);
|
|
799
893
|
}
|
|
800
894
|
}
|
|
801
895
|
|
|
896
|
+
/** A note such as "{@code hashCode} in class {@code Object}", linking both names. */
|
|
897
|
+
private void overridePart(Xml doc, String kind, ExecutableElement ancestor) {
|
|
898
|
+
var holder = (TypeElement) ancestor.getEnclosingElement();
|
|
899
|
+
var part = doc.add("part").set("kind", kind).set("label", "");
|
|
900
|
+
link(part, ancestor, ancestor.getSimpleName().toString());
|
|
901
|
+
part.addText("text", holder.getKind().isInterface() ? " in interface " : " in class ");
|
|
902
|
+
link(part, holder, localName(holder));
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
private void link(Xml parent, Element target, String text) {
|
|
906
|
+
var link =
|
|
907
|
+
parent
|
|
908
|
+
.add("link")
|
|
909
|
+
.set("plain", "false")
|
|
910
|
+
.set("target", id(target))
|
|
911
|
+
.set("role", role(target));
|
|
912
|
+
javadocLocation(link, target);
|
|
913
|
+
link.addText("text", text);
|
|
914
|
+
}
|
|
915
|
+
|
|
802
916
|
/** Labels follow the documented declaration, even when the prose is inherited. */
|
|
803
917
|
private static String label(Element e, String key, Part part) {
|
|
804
918
|
if (key.startsWith(THROWS)) {
|
|
@@ -918,23 +1032,57 @@ public final class SphinxDoclet implements Doclet {
|
|
|
918
1032
|
"Unresolved Javadoc reference %s; check spelling, source paths and classpath.",
|
|
919
1033
|
ref);
|
|
920
1034
|
} else {
|
|
921
|
-
|
|
922
|
-
switch (target) {
|
|
923
|
-
case TypeElement _ -> "type";
|
|
924
|
-
case ExecutableElement _ -> "method";
|
|
925
|
-
case PackageElement _ -> "package";
|
|
926
|
-
default -> "field";
|
|
927
|
-
};
|
|
928
|
-
link.set("role", role);
|
|
1035
|
+
link.set("role", role(target));
|
|
929
1036
|
javadocLocation(link, target);
|
|
930
1037
|
}
|
|
931
1038
|
if (label.isEmpty()) {
|
|
932
|
-
link.addText("text", ref.toString());
|
|
1039
|
+
link.addText("text", target == null ? ref.toString() : linkLabel(owner, ref, target));
|
|
933
1040
|
} else {
|
|
934
1041
|
emit(link, owner, "label", label, new HashSet<>());
|
|
935
1042
|
}
|
|
936
1043
|
}
|
|
937
1044
|
|
|
1045
|
+
/**
|
|
1046
|
+
* The standard doclet's text for a link without a label: {@code Map.Entry}, {@code
|
|
1047
|
+
* String.format(String, Object...)}, or {@code of(L, R)} for a member of the same type.
|
|
1048
|
+
*/
|
|
1049
|
+
private String linkLabel(Element owner, ReferenceTree ref, Element target) {
|
|
1050
|
+
if (target instanceof PackageElement pkg) {
|
|
1051
|
+
return pkg.getQualifiedName().toString();
|
|
1052
|
+
}
|
|
1053
|
+
if (target instanceof TypeElement type) {
|
|
1054
|
+
return localName(type);
|
|
1055
|
+
}
|
|
1056
|
+
String signature = ref.getSignature();
|
|
1057
|
+
String member = signature.substring(signature.indexOf('#') + 1);
|
|
1058
|
+
if (target instanceof ExecutableElement method && !member.contains("(")) {
|
|
1059
|
+
member +=
|
|
1060
|
+
method.getParameters().stream()
|
|
1061
|
+
.map(p -> linkType(p.asType()).replaceAll("^.*\\.", ""))
|
|
1062
|
+
.collect(Collectors.joining(", ", "(", ")"));
|
|
1063
|
+
if (method.isVarArgs()) {
|
|
1064
|
+
member = member.substring(0, member.length() - 3) + "...)";
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
Element scope = owner;
|
|
1068
|
+
while (scope != null && !(scope instanceof TypeElement)) {
|
|
1069
|
+
scope = scope.getEnclosingElement();
|
|
1070
|
+
}
|
|
1071
|
+
var type = (TypeElement) target.getEnclosingElement();
|
|
1072
|
+
return target.getKind() == ElementKind.CONSTRUCTOR || type.equals(scope)
|
|
1073
|
+
? member
|
|
1074
|
+
: localName(type) + "." + member;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
private static String role(Element target) {
|
|
1078
|
+
return switch (target) {
|
|
1079
|
+
case TypeElement _ -> "type";
|
|
1080
|
+
case ExecutableElement _ -> "method";
|
|
1081
|
+
case PackageElement _ -> "package";
|
|
1082
|
+
default -> "field";
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
|
|
938
1086
|
/** Records where standard Javadoc publishes the target, for external Javadoc links. */
|
|
939
1087
|
private void javadocLocation(Xml link, Element target) {
|
|
940
1088
|
Element type = target;
|
|
@@ -356,6 +356,9 @@ def comment(part: ET.Element, location, quiet: bool = False) -> list[nodes.Node]
|
|
|
356
356
|
)
|
|
357
357
|
elif not p.astext().strip():
|
|
358
358
|
p.parent.remove(p)
|
|
359
|
+
# Javadoc prints comment text as written; Sphinx would curl quotes and join dots.
|
|
360
|
+
for node in root.children:
|
|
361
|
+
node["support_smartquotes"] = False
|
|
359
362
|
return root.children
|
|
360
363
|
|
|
361
364
|
|
|
@@ -408,6 +411,8 @@ def quiet_reference(app, env, node, contnode):
|
|
|
408
411
|
|
|
409
412
|
|
|
410
413
|
FIELD_TITLES = {
|
|
414
|
+
"specifiedBy": "Specified by",
|
|
415
|
+
"overrides": "Overrides",
|
|
411
416
|
"param": "Parameters",
|
|
412
417
|
"typeparam": "Type parameters",
|
|
413
418
|
"return": "Returns",
|
|
@@ -427,10 +432,11 @@ def field(kind: str, items) -> nodes.field:
|
|
|
427
432
|
for label, content in items:
|
|
428
433
|
content = list(content)
|
|
429
434
|
if label:
|
|
435
|
+
label = nodes.strong("", "", label)
|
|
430
436
|
if content and isinstance(content[0], nodes.paragraph):
|
|
431
|
-
content[0].insert(0, [
|
|
437
|
+
content[0].insert(0, [label, nodes.Text(" – ")])
|
|
432
438
|
else:
|
|
433
|
-
content.insert(0, nodes.paragraph("", "",
|
|
439
|
+
content.insert(0, nodes.paragraph("", "", label))
|
|
434
440
|
bodies.append(content)
|
|
435
441
|
if len(bodies) == 1:
|
|
436
442
|
body = nodes.field_body("", *bodies[0])
|
|
@@ -453,12 +459,22 @@ def documentation(element: ET.Element, location) -> list[nodes.Node]:
|
|
|
453
459
|
for part in element.findall("doc/part"):
|
|
454
460
|
content = comment(part, location)
|
|
455
461
|
kind = part.get("kind")
|
|
462
|
+
if kind in {"specifiedBy", "overrides"}:
|
|
463
|
+
# Ancestors outside the documented API stay text, as in summaries.
|
|
464
|
+
for node in content:
|
|
465
|
+
for xref in node.findall(addnodes.pending_xref):
|
|
466
|
+
xref["java:quiet"] = True
|
|
456
467
|
if kind == "body":
|
|
457
468
|
result.extend(content)
|
|
458
469
|
elif kind == "deprecated":
|
|
459
470
|
notice.extend(content)
|
|
460
471
|
else:
|
|
461
|
-
|
|
472
|
+
label = part.get("label") and nodes.Text(part.get("label"))
|
|
473
|
+
if label and part.get("target"):
|
|
474
|
+
# A documented exception links to its type, as in signatures.
|
|
475
|
+
label = reference(part, [label])
|
|
476
|
+
label["refwarn"] = False
|
|
477
|
+
fields_by_kind.setdefault(kind, []).append((label, content))
|
|
462
478
|
for kind, items in fields_by_kind.items():
|
|
463
479
|
fields += field(kind, items)
|
|
464
480
|
if notice is not None:
|