Skip to content

Commit 245ef3a

Browse files
authored
Merge pull request #1355 from Yarwin/fix-docs-in-godot-api-secondary
Class Docs – register docs in `#[godot_api(secondary)]`, simplify docs registration logic
2 parents 2dde78e + ae07e11 commit 245ef3a

File tree

13 files changed

+291
-122
lines changed

13 files changed

+291
-122
lines changed

godot-core/src/docs.rs

Lines changed: 82 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,41 @@
88
use std::collections::HashMap;
99

1010
use crate::meta::ClassId;
11-
use crate::registry::plugin::{ITraitImpl, InherentImpl, PluginItem, Struct};
11+
use crate::obj::GodotClass;
12+
13+
/// Piece of information that is gathered by the self-registration ("plugin") system.
14+
///
15+
/// You should not manually construct this struct, but rather use [`DocsPlugin::new()`].
16+
#[derive(Debug)]
17+
pub struct DocsPlugin {
18+
/// The name of the class to register docs for.
19+
class_name: ClassId,
20+
21+
/// The actual item being registered.
22+
item: DocsItem,
23+
}
24+
25+
impl DocsPlugin {
26+
/// Creates a new `DocsPlugin`, automatically setting the `class_name` to the values defined in [`GodotClass`].
27+
pub fn new<T: GodotClass>(item: DocsItem) -> Self {
28+
Self {
29+
class_name: T::class_id(),
30+
item,
31+
}
32+
}
33+
}
34+
35+
type ITraitImplDocs = &'static str;
36+
37+
#[derive(Debug)]
38+
pub enum DocsItem {
39+
/// Docs for `#[derive(GodotClass)] struct MyClass`.
40+
Struct(StructDocs),
41+
/// Docs for `#[godot_api] impl MyClass`.
42+
InherentImpl(InherentImplDocs),
43+
/// Docs for `#[godot_api] impl ITrait for MyClass`.
44+
ITraitImpl(ITraitImplDocs),
45+
}
1246

1347
/// Created for documentation on
1448
/// ```ignore
@@ -29,7 +63,7 @@ pub struct StructDocs {
2963
pub members: &'static str,
3064
}
3165

32-
/// Keeps documentation for inherent `impl` blocks, such as:
66+
/// Keeps documentation for inherent `impl` blocks (primary and secondary), such as:
3367
/// ```ignore
3468
/// #[godot_api]
3569
/// impl Struct {
@@ -46,18 +80,19 @@ pub struct StructDocs {
4680
/// }
4781
/// ```
4882
/// All fields are XML parts, escaped where necessary.
49-
#[derive(Default, Copy, Clone, Debug)]
83+
#[derive(Default, Clone, Debug)]
5084
pub struct InherentImplDocs {
5185
pub methods: &'static str,
52-
pub signals_block: &'static str,
53-
pub constants_block: &'static str,
86+
pub signals: &'static str,
87+
pub constants: &'static str,
5488
}
5589

5690
#[derive(Default)]
5791
struct DocPieces {
5892
definition: StructDocs,
59-
inherent: InherentImplDocs,
60-
virtual_methods: &'static str,
93+
methods: Vec<&'static str>,
94+
signals: Vec<&'static str>,
95+
constants: Vec<&'static str>,
6196
}
6297

6398
/// This function scours the registered plugins to find their documentation pieces,
@@ -76,24 +111,27 @@ struct DocPieces {
76111
#[doc(hidden)]
77112
pub fn gather_xml_docs() -> impl Iterator<Item = String> {
78113
let mut map = HashMap::<ClassId, DocPieces>::new();
79-
crate::private::iterate_plugins(|x| {
114+
crate::private::iterate_docs_plugins(|x| {
80115
let class_name = x.class_name;
81-
82-
match x.item {
83-
PluginItem::InherentImpl(InherentImpl { docs, .. }) => {
84-
map.entry(class_name).or_default().inherent = docs
116+
match &x.item {
117+
DocsItem::Struct(s) => {
118+
map.entry(class_name).or_default().definition = *s;
85119
}
86-
87-
PluginItem::ITraitImpl(ITraitImpl {
88-
virtual_method_docs,
89-
..
90-
}) => map.entry(class_name).or_default().virtual_methods = virtual_method_docs,
91-
92-
PluginItem::Struct(Struct { docs, .. }) => {
93-
map.entry(class_name).or_default().definition = docs
120+
DocsItem::InherentImpl(trait_docs) => {
121+
let InherentImplDocs {
122+
methods,
123+
constants,
124+
signals,
125+
} = trait_docs;
126+
map.entry(class_name).or_default().methods.push(methods);
127+
map.entry(class_name)
128+
.and_modify(|pieces| pieces.constants.push(constants));
129+
map.entry(class_name)
130+
.and_modify(|pieces| pieces.signals.push(signals));
131+
}
132+
DocsItem::ITraitImpl(methods) => {
133+
map.entry(class_name).or_default().methods.push(methods);
94134
}
95-
96-
_ => (),
97135
}
98136
});
99137

@@ -106,22 +144,31 @@ pub fn gather_xml_docs() -> impl Iterator<Item = String> {
106144
members,
107145
} = pieces.definition;
108146

109-
let InherentImplDocs {
110-
methods,
111-
signals_block,
112-
constants_block,
113-
} = pieces.inherent;
114-
115-
let virtual_methods = pieces.virtual_methods;
116-
let methods_block = (virtual_methods.is_empty() && methods.is_empty())
117-
.then(String::new)
118-
.unwrap_or_else(|| format!("<methods>{methods}{virtual_methods}</methods>"));
119147

148+
let method_docs = String::from_iter(pieces.methods);
149+
let signal_docs = String::from_iter(pieces.signals);
150+
let constant_docs = String::from_iter(pieces.constants);
151+
152+
let methods_block = if method_docs.is_empty() {
153+
String::new()
154+
} else {
155+
format!("<methods>{method_docs}</methods>")
156+
};
157+
let signals_block = if signal_docs.is_empty() {
158+
String::new()
159+
} else {
160+
format!("<signals>{signal_docs}</signals>")
161+
};
162+
let constants_block = if constant_docs.is_empty() {
163+
String::new()
164+
} else {
165+
format!("<constants>{constant_docs}</constants>")
166+
};
120167
let (brief, description) = match description
121168
.split_once("[br]") {
122-
Some((brief, description)) => (brief, description.trim_start_matches("[br]")),
123-
None => (description, ""),
124-
};
169+
Some((brief, description)) => (brief, description.trim_start_matches("[br]")),
170+
None => (description, ""),
171+
};
125172

126173
format!(r#"<?xml version="1.0" encoding="UTF-8"?>
127174
<class name="{class}" inherits="{base}"{deprecated}{experimental} xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">

godot-core/src/private.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ use crate::{classes, sys};
2222
// Public re-exports
2323

2424
mod reexport_pub {
25+
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
26+
pub use crate::docs::{DocsItem, DocsPlugin, InherentImplDocs, StructDocs};
2527
pub use crate::gen::classes::class_macros;
2628
#[cfg(feature = "trace")]
2729
pub use crate::meta::trace;
@@ -52,6 +54,8 @@ static CALL_ERRORS: Global<CallErrors> = Global::default();
5254
static ERROR_PRINT_LEVEL: atomic::AtomicU8 = atomic::AtomicU8::new(2);
5355

5456
sys::plugin_registry!(pub __GODOT_PLUGIN_REGISTRY: ClassPlugin);
57+
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
58+
sys::plugin_registry!(pub __GODOT_DOCS_REGISTRY: DocsPlugin);
5559

5660
// ----------------------------------------------------------------------------------------------------------------------------------------------
5761
// Call error handling
@@ -146,6 +150,11 @@ pub(crate) fn iterate_plugins(mut visitor: impl FnMut(&ClassPlugin)) {
146150
sys::plugin_foreach!(__GODOT_PLUGIN_REGISTRY; visitor);
147151
}
148152

153+
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
154+
pub(crate) fn iterate_docs_plugins(mut visitor: impl FnMut(&DocsPlugin)) {
155+
sys::plugin_foreach!(__GODOT_DOCS_REGISTRY; visitor);
156+
}
157+
149158
#[cfg(feature = "codegen-full")] // Remove if used in other scenarios.
150159
pub(crate) fn find_inherent_impl(class_name: crate::meta::ClassId) -> Option<InherentImpl> {
151160
// We do this manually instead of using `iterate_plugins()` because we want to break as soon as we find a match.

godot-core/src/registry/class.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -424,8 +424,6 @@ fn fill_class_info(item: PluginItem, c: &mut ClassRegistrationInfo) {
424424
is_editor_plugin,
425425
is_internal,
426426
is_instantiable,
427-
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
428-
docs: _,
429427
reference_fn,
430428
unreference_fn,
431429
}) => {
@@ -473,8 +471,6 @@ fn fill_class_info(item: PluginItem, c: &mut ClassRegistrationInfo) {
473471
PluginItem::InherentImpl(InherentImpl {
474472
register_methods_constants_fn,
475473
register_rpcs_fn: _,
476-
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
477-
docs: _,
478474
}) => {
479475
c.register_methods_constants_fn = Some(register_methods_constants_fn);
480476
}
@@ -492,8 +488,6 @@ fn fill_class_info(item: PluginItem, c: &mut ClassRegistrationInfo) {
492488
user_free_property_list_fn,
493489
user_property_can_revert_fn,
494490
user_property_get_revert_fn,
495-
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
496-
virtual_method_docs: _,
497491
validate_property_fn,
498492
}) => {
499493
c.user_register_fn = user_register_fn;

godot-core/src/registry/plugin.rs

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
use std::any::Any;
99
use std::{any, fmt};
1010

11-
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
12-
use crate::docs::*;
1311
use crate::init::InitLevel;
1412
use crate::meta::ClassId;
1513
use crate::obj::{bounds, cap, Bounds, DynGd, Gd, GodotClass, Inherits, UserClass};
@@ -197,16 +195,10 @@ pub struct Struct {
197195

198196
/// Whether the class has a default constructor.
199197
pub(crate) is_instantiable: bool,
200-
201-
/// Documentation extracted from the struct's RustDoc.
202-
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
203-
pub(crate) docs: StructDocs,
204198
}
205199

206200
impl Struct {
207-
pub fn new<T: GodotClass + cap::ImplementsGodotExports>(
208-
#[cfg(all(since_api = "4.3", feature = "register-docs"))] docs: StructDocs,
209-
) -> Self {
201+
pub fn new<T: GodotClass + cap::ImplementsGodotExports>() -> Self {
210202
let refcounted = <T::Memory as bounds::Memory>::IS_REF_COUNTED;
211203

212204
Self {
@@ -222,8 +214,6 @@ impl Struct {
222214
is_editor_plugin: false,
223215
is_internal: false,
224216
is_instantiable: false,
225-
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
226-
docs,
227217
// While Godot doesn't do anything with these callbacks for non-RefCounted classes, we can avoid instantiating them in Rust.
228218
reference_fn: refcounted.then_some(callbacks::reference::<T>),
229219
unreference_fn: refcounted.then_some(callbacks::unreference::<T>),
@@ -294,34 +284,23 @@ pub struct InherentImpl {
294284
// This field is only used during codegen-full.
295285
#[cfg_attr(not(feature = "codegen-full"), expect(dead_code))]
296286
pub(crate) register_rpcs_fn: Option<ErasedRegisterRpcsFn>,
297-
298-
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
299-
pub docs: InherentImplDocs,
300287
}
301288

302289
impl InherentImpl {
303-
pub fn new<T: cap::ImplementsGodotApi>(
304-
#[cfg(all(since_api = "4.3", feature = "register-docs"))] docs: InherentImplDocs,
305-
) -> Self {
290+
pub fn new<T: cap::ImplementsGodotApi>() -> Self {
306291
Self {
307292
register_methods_constants_fn: ErasedRegisterFn {
308293
raw: callbacks::register_user_methods_constants::<T>,
309294
},
310295
register_rpcs_fn: Some(ErasedRegisterRpcsFn {
311296
raw: callbacks::register_user_rpcs::<T>,
312297
}),
313-
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
314-
docs,
315298
}
316299
}
317300
}
318301

319302
#[derive(Default, Clone, Debug)]
320303
pub struct ITraitImpl {
321-
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
322-
/// Virtual method documentation.
323-
pub(crate) virtual_method_docs: &'static str,
324-
325304
/// Callback to user-defined `register_class` function.
326305
pub(crate) user_register_fn: Option<ErasedRegisterFn>,
327306

@@ -436,12 +415,8 @@ pub struct ITraitImpl {
436415
}
437416

438417
impl ITraitImpl {
439-
pub fn new<T: GodotClass + cap::ImplementsGodotVirtual>(
440-
#[cfg(all(since_api = "4.3", feature = "register-docs"))] virtual_method_docs: &'static str,
441-
) -> Self {
418+
pub fn new<T: GodotClass + cap::ImplementsGodotVirtual>() -> Self {
442419
Self {
443-
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
444-
virtual_method_docs,
445420
get_virtual_fn: Some(callbacks::get_virtual::<T>),
446421
..Default::default()
447422
}

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

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,8 @@ pub fn transform_inherent_impl(
9191
let (funcs, signals) = process_godot_fns(&class_name, &mut impl_block, meta.secondary)?;
9292
let consts = process_godot_constants(&mut impl_block)?;
9393

94-
#[cfg(all(feature = "register-docs", since_api = "4.3"))]
95-
let docs = crate::docs::document_inherent_impl(&funcs, &consts, &signals);
96-
#[cfg(not(all(feature = "register-docs", since_api = "4.3")))]
97-
let docs = quote! {};
94+
let inherent_impl_docs =
95+
crate::docs::make_trait_docs_registration(&funcs, &consts, &signals, &class_name, &prv);
9896

9997
// Container struct holding names of all registered #[func]s.
10098
// The struct is declared by #[derive(GodotClass)].
@@ -127,17 +125,20 @@ pub fn transform_inherent_impl(
127125
let method_storage_name = format_ident!("__registration_methods_{class_name}");
128126
let constants_storage_name = format_ident!("__registration_constants_{class_name}");
129127

130-
let fill_storage = quote! {
131-
::godot::sys::plugin_execute_pre_main!({
132-
#method_storage_name.lock().unwrap().push(|| {
133-
#( #method_registrations )*
134-
#( #signal_registrations )*
135-
});
128+
let fill_storage = {
129+
quote! {
130+
::godot::sys::plugin_execute_pre_main!({
131+
#method_storage_name.lock().unwrap().push(|| {
132+
#( #method_registrations )*
133+
#( #signal_registrations )*
134+
});
135+
136+
#constants_storage_name.lock().unwrap().push(|| {
137+
#constant_registration
138+
});
136139

137-
#constants_storage_name.lock().unwrap().push(|| {
138-
#constant_registration
139140
});
140-
});
141+
}
141142
};
142143

143144
if !meta.secondary {
@@ -175,7 +176,7 @@ pub fn transform_inherent_impl(
175176

176177
let class_registration = quote! {
177178
::godot::sys::plugin_add!(#prv::__GODOT_PLUGIN_REGISTRY; #prv::ClassPlugin::new::<#class_name>(
178-
#prv::PluginItem::InherentImpl(#prv::InherentImpl::new::<#class_name>(#docs))
179+
#prv::PluginItem::InherentImpl(#prv::InherentImpl::new::<#class_name>())
179180
));
180181
};
181182

@@ -189,6 +190,7 @@ pub fn transform_inherent_impl(
189190
#( #func_name_constants )*
190191
}
191192
#signal_symbol_types
193+
#inherent_impl_docs
192194
};
193195

194196
Ok(result)
@@ -202,6 +204,7 @@ pub fn transform_inherent_impl(
202204
impl #funcs_collection {
203205
#( #func_name_constants )*
204206
}
207+
#inherent_impl_docs
205208
};
206209

207210
Ok(result)

0 commit comments

Comments
 (0)