Skip to content

Commit 61f4e19

Browse files
authored
Merge pull request #1077 from godot-rust/qol/register-docs-cleanup
XML doc generation: code cleanup
2 parents 36d5b91 + 5fa3673 commit 61f4e19

File tree

4 files changed

+123
-126
lines changed

4 files changed

+123
-126
lines changed

godot-macros/src/class/data_models/inherent_impl.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ pub fn transform_inherent_impl(
9494
let consts = process_godot_constants(&mut impl_block)?;
9595

9696
#[cfg(all(feature = "register-docs", since_api = "4.3"))]
97-
let docs = crate::docs::make_inherent_impl_docs(&funcs, &consts, &signals);
97+
let docs = crate::docs::document_inherent_impl(&funcs, &consts, &signals);
9898
#[cfg(not(all(feature = "register-docs", since_api = "4.3")))]
9999
let docs = quote! {};
100100

godot-macros/src/class/data_models/interface_trait_impl.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub fn transform_trait_impl(original_impl: venial::Impl) -> ParseResult<TokenStr
3434
let prv = quote! { ::godot::private };
3535

3636
#[cfg(all(feature = "register-docs", since_api = "4.3"))]
37-
let docs = crate::docs::make_virtual_impl_docs(&original_impl.body_items);
37+
let docs = crate::docs::document_interface_trait_impl(&original_impl.body_items);
3838
#[cfg(not(all(feature = "register-docs", since_api = "4.3")))]
3939
let docs = quote! {};
4040

godot-macros/src/class/derive_godot_class.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,8 @@ pub fn derive_godot_class(item: venial::Item) -> ParseResult<TokenStream> {
6565
}
6666
let base_ty = &struct_cfg.base_ty;
6767
#[cfg(all(feature = "register-docs", since_api = "4.3"))]
68-
let docs = crate::docs::make_definition_docs(
69-
base_ty.to_string(),
70-
&class.attributes,
71-
&fields.all_fields,
72-
);
68+
let docs =
69+
crate::docs::document_struct(base_ty.to_string(), &class.attributes, &fields.all_fields);
7370
#[cfg(not(all(feature = "register-docs", since_api = "4.3")))]
7471
let docs = quote! {};
7572
let base_class = quote! { ::godot::classes::#base_ty };

godot-macros/src/docs.rs

Lines changed: 119 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,24 @@ mod markdown_converter;
1010
use crate::class::{ConstDefinition, Field, FuncDefinition, SignalDefinition};
1111
use proc_macro2::{Ident, TokenStream};
1212
use quote::{quote, ToTokens};
13-
use venial::*;
1413

15-
pub fn make_definition_docs(
14+
/// Returns code containing the doc information of a `#[derive(GodotClass)] struct MyClass` declaration.
15+
pub fn document_struct(
1616
base: String,
17-
description: &[Attribute],
18-
members: &[Field],
17+
description: &[venial::Attribute],
18+
fields: &[Field],
1919
) -> TokenStream {
2020
let base_escaped = xml_escape(base);
21-
let Some(desc_escaped) = make_docs_from_attributes(description).map(xml_escape) else {
21+
let Some(desc_escaped) = attribute_docs_to_bbcode(description).map(xml_escape) else {
2222
return quote! { None };
2323
};
24-
let members = members
24+
25+
let members = fields
2526
.iter()
26-
.filter(|x| x.var.is_some() | x.export.is_some())
27-
.filter_map(member)
27+
.filter(|field| field.var.is_some() || field.export.is_some())
28+
.filter_map(format_member_xml)
2829
.collect::<String>();
30+
2931
quote! {
3032
Some(
3133
::godot::docs::StructDocs {
@@ -37,79 +39,71 @@ pub fn make_definition_docs(
3739
}
3840
}
3941

40-
pub fn make_inherent_impl_docs(
42+
/// Returns code containing the doc information of a `#[godot_api] impl MyClass` declaration.
43+
pub fn document_inherent_impl(
4144
functions: &[FuncDefinition],
4245
constants: &[ConstDefinition],
4346
signals: &[SignalDefinition],
4447
) -> TokenStream {
45-
/// Generates TokenStream containing field definitions for documented methods and documentation blocks for constants and signals.
46-
fn pieces(
47-
functions: &[FuncDefinition],
48-
signals: &[SignalDefinition],
49-
constants: &[ConstDefinition],
50-
) -> TokenStream {
51-
let to_tagged = |s: String, tag: &str| -> String {
52-
if s.is_empty() {
53-
s
54-
} else {
55-
format!("<{tag}>{s}</{tag}>")
56-
}
57-
};
58-
59-
let signals_block = to_tagged(
60-
signals
61-
.iter()
62-
.filter_map(make_signal_docs)
63-
.collect::<String>(),
64-
"signals",
65-
);
66-
let constants_block = to_tagged(
67-
constants
68-
.iter()
69-
.map(|ConstDefinition { raw_constant }| raw_constant)
70-
.filter_map(make_constant_docs)
71-
.collect::<String>(),
72-
"constants",
73-
);
74-
75-
let methods = functions
76-
.iter()
77-
.filter_map(make_method_docs)
78-
.collect::<String>();
79-
80-
quote! {
81-
::godot::docs::InherentImplDocs {
82-
methods: #methods,
83-
signals_block: #signals_block,
84-
constants_block: #constants_block,
85-
}
48+
let group_xml_block = |s: String, tag: &str| -> String {
49+
if s.is_empty() {
50+
s
51+
} else {
52+
format!("<{tag}>{s}</{tag}>")
53+
}
54+
};
55+
56+
let signal_xml_elems = signals
57+
.iter()
58+
.filter_map(format_signal_xml)
59+
.collect::<String>();
60+
let signals_block = group_xml_block(signal_xml_elems, "signals");
61+
62+
let constant_xml_elems = constants
63+
.iter()
64+
.map(|ConstDefinition { raw_constant }| raw_constant)
65+
.filter_map(format_constant_xml)
66+
.collect::<String>();
67+
let constants_block = group_xml_block(constant_xml_elems, "constants");
68+
69+
let method_xml_elems = functions
70+
.iter()
71+
.filter_map(format_method_xml)
72+
.collect::<String>();
73+
74+
quote! {
75+
::godot::docs::InherentImplDocs {
76+
methods: #method_xml_elems,
77+
signals_block: #signals_block,
78+
constants_block: #constants_block,
8679
}
8780
}
88-
pieces(functions, signals, constants)
8981
}
9082

91-
pub fn make_virtual_impl_docs(vmethods: &[ImplMember]) -> TokenStream {
92-
let virtual_methods = vmethods
83+
/// Returns code containing the doc information of a `#[godot_api] impl ITrait for MyClass` declaration.
84+
pub fn document_interface_trait_impl(impl_members: &[venial::ImplMember]) -> TokenStream {
85+
let interface_methods = impl_members
9386
.iter()
9487
.filter_map(|x| match x {
9588
venial::ImplMember::AssocFunction(f) => Some(f.clone()),
9689
_ => None,
9790
})
98-
.filter_map(make_virtual_method_docs)
91+
.filter_map(format_virtual_method_xml)
9992
.collect::<String>();
10093

101-
quote! { #virtual_methods }
94+
quote! { #interface_methods }
10295
}
10396

10497
/// `///` is expanded to `#[doc = "…"]`.
105-
/// This function goes through and extracts the …
106-
fn siphon_docs_from_attributes(doc: &[Attribute]) -> impl Iterator<Item = String> + '_ {
98+
///
99+
/// This function goes through and extracts the "…" part.
100+
fn extract_docs_from_attributes(doc: &[venial::Attribute]) -> impl Iterator<Item = String> + '_ {
107101
doc.iter()
108-
// find #[doc]
102+
// Find #[doc].
109103
.filter(|x| x.get_single_path_segment().is_some_and(|x| x == "doc"))
110-
// #[doc = "…"]
104+
// Limit to occurrences with syntax #[doc = "…"].
111105
.filter_map(|x| match &x.value {
112-
AttributeValue::Equals(_, doc) => Some(doc),
106+
venial::AttributeValue::Equals(_, doc) => Some(doc),
113107
_ => None,
114108
})
115109
.flat_map(|doc| {
@@ -122,7 +116,7 @@ fn siphon_docs_from_attributes(doc: &[Attribute]) -> impl Iterator<Item = String
122116
}
123117

124118
fn xml_escape(value: String) -> String {
125-
// Most strings have no special characters, so this check helps avoid unnecessary string copying
119+
// Most strings have no special characters, so this check helps avoid unnecessary string copying.
126120
if !value.contains(['&', '<', '>', '"', '\'']) {
127121
return value;
128122
}
@@ -143,29 +137,33 @@ fn xml_escape(value: String) -> String {
143137
result
144138
}
145139

146-
/// Calls [`siphon_docs_from_attributes`] and converts the result to BBCode
147-
/// for Godot's consumption.
148-
fn make_docs_from_attributes(doc: &[Attribute]) -> Option<String> {
149-
let doc = siphon_docs_from_attributes(doc)
140+
/// Calls [`extract_docs_from_attributes`] and converts the result to BBCode for Godot's consumption.
141+
fn attribute_docs_to_bbcode(doc: &[venial::Attribute]) -> Option<String> {
142+
let doc = extract_docs_from_attributes(doc)
150143
.collect::<Vec<String>>()
151144
.join("\n");
152145

153146
(!doc.is_empty()).then(|| markdown_converter::to_bbcode(&doc))
154147
}
155148

156-
fn make_signal_docs(signal: &SignalDefinition) -> Option<String> {
149+
fn format_venial_params_xml(params: &venial::Punctuated<venial::FnParam>) -> String {
150+
let non_receiver_params = params.iter().filter_map(|(param, _punct)| match param {
151+
venial::FnParam::Receiver(_) => None,
152+
venial::FnParam::Typed(p) => Some((&p.name, &p.ty)),
153+
});
154+
155+
format_params_xml(non_receiver_params)
156+
}
157+
158+
fn format_signal_xml(signal: &SignalDefinition) -> Option<String> {
157159
let name = &signal.fn_signature.name;
158-
let params = params(
159-
signal
160-
.fn_signature
161-
.params
162-
.iter()
163-
.filter_map(|(x, _)| match x {
164-
FnParam::Receiver(_) => None,
165-
FnParam::Typed(y) => Some((&y.name, &y.ty)),
166-
}),
167-
);
168-
let desc = make_docs_from_attributes(&signal.external_attributes)?;
160+
let name = xml_escape(name.to_string());
161+
162+
let params = format_venial_params_xml(&signal.fn_signature.params);
163+
164+
let desc = attribute_docs_to_bbcode(&signal.external_attributes)?;
165+
let desc = xml_escape(desc);
166+
169167
Some(format!(
170168
r#"
171169
<signal name="{name}">
@@ -174,14 +172,12 @@ fn make_signal_docs(signal: &SignalDefinition) -> Option<String> {
174172
{desc}
175173
</description>
176174
</signal>
177-
"#,
178-
name = xml_escape(name.to_string()),
179-
desc = xml_escape(desc),
175+
"#
180176
))
181177
}
182178

183-
fn make_constant_docs(constant: &Constant) -> Option<String> {
184-
let docs = make_docs_from_attributes(&constant.attributes)?;
179+
fn format_constant_xml(constant: &venial::Constant) -> Option<String> {
180+
let docs = attribute_docs_to_bbcode(&constant.attributes)?;
185181
let name = constant.name.to_string();
186182
let value = constant
187183
.initializer
@@ -197,11 +193,12 @@ fn make_constant_docs(constant: &Constant) -> Option<String> {
197193
))
198194
}
199195

200-
pub fn member(member: &Field) -> Option<String> {
201-
let docs = make_docs_from_attributes(&member.attributes)?;
196+
pub fn format_member_xml(member: &Field) -> Option<String> {
197+
let docs = attribute_docs_to_bbcode(&member.attributes)?;
202198
let name = &member.name;
203199
let ty = member.ty.to_token_stream().to_string();
204200
let default = member.default_val.to_token_stream().to_string();
201+
205202
Some(format!(
206203
r#"<member name="{name}" type="{ty}" default="{default}">{docs}</member>"#,
207204
name = xml_escape(name.to_string()),
@@ -211,76 +208,79 @@ pub fn member(member: &Field) -> Option<String> {
211208
))
212209
}
213210

214-
fn params<'a, 'b>(params: impl Iterator<Item = (&'a Ident, &'b TypeExpr)>) -> String {
211+
fn format_params_xml<'a, 'b>(
212+
params: impl Iterator<Item = (&'a Ident, &'b venial::TypeExpr)>,
213+
) -> String {
214+
use std::fmt::Write;
215+
215216
let mut output = String::new();
216217
for (index, (name, ty)) in params.enumerate() {
217-
output.push_str(&format!(
218+
write!(
219+
output,
218220
r#"<param index="{index}" name="{name}" type="{ty}" />"#,
219221
name = xml_escape(name.to_string()),
220222
ty = xml_escape(ty.to_token_stream().to_string()),
221-
));
223+
)
224+
.expect("write to string failed");
222225
}
223226
output
224227
}
225228

226-
pub fn make_virtual_method_docs(method: Function) -> Option<String> {
227-
let desc = make_docs_from_attributes(&method.attributes)?;
229+
fn format_virtual_method_xml(method: venial::Function) -> Option<String> {
230+
let desc = attribute_docs_to_bbcode(&method.attributes)?;
231+
let desc = xml_escape(desc);
232+
228233
let name = method.name.to_string();
229-
let ret = method
234+
let name = xml_escape(name);
235+
236+
let return_ty = method
230237
.return_ty
231-
.map(|x| x.to_token_stream().to_string())
238+
.map(|ty| ty.to_token_stream().to_string())
232239
.unwrap_or_else(|| "void".to_string());
240+
let return_ty = xml_escape(return_ty);
241+
242+
let params = format_venial_params_xml(&method.params);
233243

234-
let params = params(method.params.iter().filter_map(|(x, _)| match x {
235-
FnParam::Receiver(_) => None,
236-
FnParam::Typed(y) => Some((&y.name, &y.ty)),
237-
}));
238244
Some(format!(
239245
r#"
240246
<method name="_{name}">
241-
<return type="{ret}" />
247+
<return type="{return_ty}" />
242248
{params}
243249
<description>
244250
{desc}
245251
</description>
246252
</method>
247-
"#,
248-
name = xml_escape(name),
249-
ret = xml_escape(ret),
250-
desc = xml_escape(desc),
253+
"#
251254
))
252255
}
253256

254-
pub fn make_method_docs(method: &FuncDefinition) -> Option<String> {
255-
let desc = make_docs_from_attributes(&method.external_attributes)?;
257+
fn format_method_xml(method: &FuncDefinition) -> Option<String> {
258+
let desc = attribute_docs_to_bbcode(&method.external_attributes)?;
259+
let desc = xml_escape(desc);
260+
256261
let name = method
257262
.registered_name
258263
.clone()
259264
.unwrap_or_else(|| method.rust_ident().to_string());
260-
let ret = method
261-
.signature_info
262-
.return_type
263-
.to_token_stream()
264-
.to_string();
265-
let params = params(
266-
method
267-
.signature_info
268-
.param_idents
269-
.iter()
270-
.zip(&method.signature_info.param_types),
271-
);
265+
let name = xml_escape(name);
266+
267+
let signature = &method.signature_info;
268+
269+
let return_ty = signature.return_type.to_token_stream().to_string();
270+
let return_ty = xml_escape(return_ty);
271+
272+
let param_names_and_types = signature.param_idents.iter().zip(&signature.param_types);
273+
let params = format_params_xml(param_names_and_types);
274+
272275
Some(format!(
273276
r#"
274277
<method name="{name}">
275-
<return type="{ret}" />
278+
<return type="{return_ty}" />
276279
{params}
277280
<description>
278281
{desc}
279282
</description>
280283
</method>
281-
"#,
282-
name = xml_escape(name),
283-
ret = xml_escape(ret),
284-
desc = xml_escape(desc),
284+
"#
285285
))
286286
}

0 commit comments

Comments
 (0)