Skip to content

Commit fe94254

Browse files
HoutameloYarwin
authored andcommitted
Fix secondary api blocks not registering docs.
1 parent d8864f1 commit fe94254

File tree

6 files changed

+120
-73
lines changed

6 files changed

+120
-73
lines changed

godot-core/src/docs.rs

Lines changed: 51 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,16 @@ pub struct StructDocs {
4848
/// All fields are XML parts, escaped where necessary.
4949
#[derive(Default, Copy, Clone, Debug)]
5050
pub struct InherentImplDocs {
51-
pub methods: &'static str,
52-
pub signals_block: &'static str,
53-
pub constants_block: &'static str,
51+
pub methods: Vec<&'static str>,
52+
pub signals: Vec<&'static str>,
53+
pub constants: Vec<&'static str>,
5454
}
5555

5656
#[derive(Default)]
5757
struct DocPieces {
5858
definition: StructDocs,
5959
inherent: InherentImplDocs,
60-
virtual_methods: &'static str,
60+
virtual_methods: Vec<&'static str>,
6161
}
6262

6363
/// This function scours the registered plugins to find their documentation pieces,
@@ -79,51 +79,65 @@ pub fn gather_xml_docs() -> impl Iterator<Item = String> {
7979
crate::private::iterate_plugins(|x| {
8080
let class_name = x.class_name;
8181

82-
match x.item {
82+
match &x.item {
8383
PluginItem::InherentImpl(InherentImpl { docs, .. }) => {
84-
map.entry(class_name).or_default().inherent = docs
84+
map.entry(class_name).or_default().inherent = docs.clone();
8585
}
86-
8786
PluginItem::ITraitImpl(ITraitImpl {
8887
virtual_method_docs,
8988
..
90-
}) => map.entry(class_name).or_default().virtual_methods = virtual_method_docs,
89+
}) => map
90+
.entry(class_name)
91+
.or_default()
92+
.virtual_methods
93+
.push(virtual_method_docs),
9194

9295
PluginItem::Struct(Struct { docs, .. }) => {
93-
map.entry(class_name).or_default().definition = docs
96+
map.entry(class_name).or_default().definition = *docs;
9497
}
9598

9699
_ => (),
97100
}
98101
});
99102

100103
map.into_iter().map(|(class, pieces)| {
101-
let StructDocs {
102-
base,
103-
description,
104-
experimental,
105-
deprecated,
106-
members,
107-
} = pieces.definition;
108-
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>"));
119-
120-
let (brief, description) = match description
121-
.split_once("[br]") {
122-
Some((brief, description)) => (brief, description.trim_start_matches("[br]")),
123-
None => (description, ""),
124-
};
125-
126-
format!(r#"<?xml version="1.0" encoding="UTF-8"?>
104+
let StructDocs {
105+
base,
106+
description,
107+
experimental,
108+
deprecated,
109+
members,
110+
} = pieces.definition;
111+
112+
let virtual_methods = pieces.virtual_methods;
113+
114+
let mut method_docs = String::from_iter(pieces.inherent.methods);
115+
let signal_docs = String::from_iter(pieces.inherent.signals);
116+
let constant_docs = String::from_iter(pieces.inherent.constants);
117+
118+
method_docs.extend(virtual_methods);
119+
let methods_block = if method_docs.is_empty() {
120+
String::new()
121+
} else {
122+
format!("<methods>{method_docs}</methods>")
123+
};
124+
let signals_block = if signal_docs.is_empty() {
125+
String::new()
126+
} else {
127+
format!("<signals>{signal_docs}</signals>")
128+
};
129+
let constants_block = if constant_docs.is_empty() {
130+
String::new()
131+
} else {
132+
format!("<constants>{constant_docs}</constants>")
133+
};
134+
let (brief, description) = match description
135+
.split_once("[br]") {
136+
Some((brief, description)) => (brief, description.trim_start_matches("[br]")),
137+
None => (description, ""),
138+
};
139+
140+
format!(r#"<?xml version="1.0" encoding="UTF-8"?>
127141
<class name="{class}" inherits="{base}"{deprecated}{experimental} xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
128142
<brief_description>{brief}</brief_description>
129143
<description>{description}</description>
@@ -132,8 +146,8 @@ pub fn gather_xml_docs() -> impl Iterator<Item = String> {
132146
{signals_block}
133147
<members>{members}</members>
134148
</class>"#)
135-
},
136-
)
149+
},
150+
)
137151
}
138152

139153
/// # Safety

godot-core/src/private.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,22 @@ pub fn next_class_id() -> u16 {
142142
NEXT_CLASS_ID.fetch_add(1, atomic::Ordering::Relaxed)
143143
}
144144

145+
// Don't touch unless you know what you're doing.
146+
#[doc(hidden)]
147+
pub fn edit_inherent_impl(class_name: crate::meta::ClassName, f: impl FnOnce(&mut InherentImpl)) {
148+
let mut plugins = __GODOT_PLUGIN_REGISTRY.lock().unwrap();
149+
150+
for elem in plugins.iter_mut().filter(|p| p.class_name == class_name) {
151+
match &mut elem.item {
152+
PluginItem::InherentImpl(inherent_impl) => {
153+
f(inherent_impl);
154+
return;
155+
}
156+
PluginItem::Struct(_) | PluginItem::ITraitImpl(_) | PluginItem::DynTraitImpl(_) => {}
157+
}
158+
}
159+
}
160+
145161
pub(crate) fn iterate_plugins(mut visitor: impl FnMut(&ClassPlugin)) {
146162
sys::plugin_foreach!(__GODOT_PLUGIN_REGISTRY; visitor);
147163
}

godot-core/src/registry/class.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,10 @@ impl ClassRegistrationInfo {
115115
// Note: when changing this match, make sure the array has sufficient size.
116116
let index = match item {
117117
PluginItem::Struct { .. } => 0,
118-
PluginItem::InherentImpl(_) => 1,
118+
PluginItem::InherentImpl(_) => {
119+
// Inherent impls don't need to be unique.
120+
return;
121+
}
119122
PluginItem::ITraitImpl { .. } => 2,
120123

121124
// Multiple dyn traits can be registered, thus don't validate for uniqueness.

godot-core/src/registry/plugin.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ pub struct ClassPlugin {
3939
/// Incorrectly setting this value should not cause any UB but will likely cause errors during registration time.
4040
// Init-level is per ClassPlugin and not per PluginItem, because all components of all classes are mixed together in one
4141
// huge linker list. There is no per-class aggregation going on, so this allows to easily filter relevant classes.
42-
pub(crate) init_level: InitLevel,
42+
pub init_level: InitLevel,
4343

4444
/// The actual item being registered.
45-
pub(crate) item: PluginItem,
45+
pub item: PluginItem,
4646
}
4747

4848
impl ClassPlugin {
@@ -300,9 +300,7 @@ pub struct InherentImpl {
300300
}
301301

302302
impl InherentImpl {
303-
pub fn new<T: cap::ImplementsGodotApi>(
304-
#[cfg(all(since_api = "4.3", feature = "register-docs"))] docs: InherentImplDocs,
305-
) -> Self {
303+
pub fn new<T: cap::ImplementsGodotApi>() -> Self {
306304
Self {
307305
register_methods_constants_fn: ErasedRegisterFn {
308306
raw: callbacks::register_user_methods_constants::<T>,
@@ -311,7 +309,7 @@ impl InherentImpl {
311309
raw: callbacks::register_user_rpcs::<T>,
312310
}),
313311
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
314-
docs,
312+
docs: Default::default(),
315313
}
316314
}
317315
}

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

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,6 @@ pub fn transform_inherent_impl(
9393

9494
#[cfg(all(feature = "register-docs", since_api = "4.3"))]
9595
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! {};
9896

9997
// Container struct holding names of all registered #[func]s.
10098
// The struct is declared by #[derive(GodotClass)].
@@ -127,17 +125,41 @@ 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+
#[cfg(all(feature = "register-docs", since_api = "4.3"))]
130+
let push_docs = {
131+
let crate::docs::InherentImplXmlDocs {
132+
method_xml_elems,
133+
constant_xml_elems,
134+
signal_xml_elems,
135+
} = docs;
136+
137+
quote! {
138+
#prv::edit_inherent_impl(#class_name_obj, |inherent_impl| {
139+
inherent_impl.docs.methods.push(#method_xml_elems);
140+
inherent_impl.docs.constants.push(#constant_xml_elems);
141+
inherent_impl.docs.signals.push(#signal_xml_elems);
142+
});
143+
}
144+
};
145+
146+
#[cfg(not(all(feature = "register-docs", since_api = "4.3")))]
147+
let push_docs = TokenStream::new();
136148

137-
#constants_storage_name.lock().unwrap().push(|| {
138-
#constant_registration
149+
quote! {
150+
::godot::sys::plugin_execute_pre_main!({
151+
#method_storage_name.lock().unwrap().push(|| {
152+
#( #method_registrations )*
153+
#( #signal_registrations )*
154+
});
155+
156+
#constants_storage_name.lock().unwrap().push(|| {
157+
#constant_registration
158+
});
159+
160+
#push_docs
139161
});
140-
});
162+
}
141163
};
142164

143165
if !meta.secondary {
@@ -175,7 +197,7 @@ pub fn transform_inherent_impl(
175197

176198
let class_registration = quote! {
177199
::godot::sys::plugin_add!(#prv::__GODOT_PLUGIN_REGISTRY; #prv::ClassPlugin::new::<#class_name>(
178-
#prv::PluginItem::InherentImpl(#prv::InherentImpl::new::<#class_name>(#docs))
200+
#prv::PluginItem::InherentImpl(#prv::InherentImpl::new::<#class_name>())
179201
));
180202
};
181203

godot-macros/src/docs.rs

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ struct XmlParagraphs {
2424
deprecated_attr: String,
2525
}
2626

27+
pub struct InherentImplXmlDocs {
28+
pub method_xml_elems: String,
29+
pub constant_xml_elems: String,
30+
pub signal_xml_elems: String,
31+
}
32+
2733
/// Returns code containing the doc information of a `#[derive(GodotClass)] struct MyClass` declaration iff class or any of its members is documented.
2834
pub fn document_struct(
2935
base: String,
@@ -59,39 +65,27 @@ pub fn document_inherent_impl(
5965
functions: &[FuncDefinition],
6066
constants: &[ConstDefinition],
6167
signals: &[SignalDefinition],
62-
) -> TokenStream {
63-
let group_xml_block = |s: String, tag: &str| -> String {
64-
if s.is_empty() {
65-
s
66-
} else {
67-
format!("<{tag}>{s}</{tag}>")
68-
}
69-
};
70-
68+
) -> InherentImplXmlDocs {
7169
let signal_xml_elems = signals
7270
.iter()
7371
.filter_map(format_signal_xml)
7472
.collect::<String>();
75-
let signals_block = group_xml_block(signal_xml_elems, "signals");
7673

7774
let constant_xml_elems = constants
7875
.iter()
7976
.map(|ConstDefinition { raw_constant }| raw_constant)
8077
.filter_map(format_constant_xml)
8178
.collect::<String>();
82-
let constants_block = group_xml_block(constant_xml_elems, "constants");
8379

8480
let method_xml_elems = functions
8581
.iter()
8682
.filter_map(format_method_xml)
8783
.collect::<String>();
8884

89-
quote! {
90-
::godot::docs::InherentImplDocs {
91-
methods: #method_xml_elems,
92-
signals_block: #signals_block,
93-
constants_block: #constants_block,
94-
}
85+
InherentImplXmlDocs {
86+
method_xml_elems,
87+
constant_xml_elems,
88+
signal_xml_elems,
9589
}
9690
}
9791

0 commit comments

Comments
 (0)