Skip to content

Commit ce4903b

Browse files
YarwinHoutamelo
authored andcommitted
Cherry pick godot-rust#1355
1 parent 659d473 commit ce4903b

File tree

13 files changed

+606
-499
lines changed

13 files changed

+606
-499
lines changed

godot-core/src/docs.rs

Lines changed: 93 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,40 @@
55
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
66
*/
77

8-
use crate::meta::ClassName;
9-
use crate::registry::plugin::{ITraitImpl, InherentImpl, PluginItem, Struct};
108
use std::collections::HashMap;
119

10+
use crate::meta::ClassName;
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+
pub(crate) class_name: ClassId,
20+
21+
/// The actual item being registered.
22+
pub 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+
#[derive(Debug)]
36+
pub enum DocsItem {
37+
Struct(StructDocs),
38+
InherentImpl(InherentImplDocs),
39+
VirtualMethods(&'static str),
40+
}
41+
1242
/// Created for documentation on
1343
/// ```ignore
1444
/// #[derive(GodotClass)]
@@ -28,7 +58,7 @@ pub struct StructDocs {
2858
pub members: &'static str,
2959
}
3060

31-
/// Keeps documentation for inherent `impl` blocks, such as:
61+
/// Keeps documentation for inherent `impl` blocks (primary and secondary), such as:
3262
/// ```ignore
3363
/// #[godot_api]
3464
/// impl Struct {
@@ -47,16 +77,17 @@ pub struct StructDocs {
4777
/// All fields are XML parts, escaped where necessary.
4878
#[derive(Clone, Debug, Default)]
4979
pub struct InherentImplDocs {
50-
pub methods: Vec<&'static str>,
51-
pub signals: Vec<&'static str>,
52-
pub constants: Vec<&'static str>,
80+
pub methods: &'static str,
81+
pub signals: &'static str,
82+
pub constants: &'static str,
5383
}
5484

5585
#[derive(Default)]
5686
struct DocPieces {
5787
definition: StructDocs,
58-
inherent: InherentImplDocs,
59-
virtual_methods: Vec<&'static str>,
88+
methods: Vec<&'static str>,
89+
signals: Vec<&'static str>,
90+
constants: Vec<&'static str>,
6091
}
6192

6293
/// This function scours the registered plugins to find their documentation pieces,
@@ -75,68 +106,66 @@ struct DocPieces {
75106
#[doc(hidden)]
76107
pub fn gather_xml_docs() -> impl Iterator<Item = String> {
77108
let mut map = HashMap::<ClassName, DocPieces>::new();
78-
crate::private::iterate_plugins(|x| {
109+
crate::private::iterate_docs_plugins(|x| {
79110
let class_name = x.class_name;
80-
81111
match &x.item {
82-
PluginItem::InherentImpl(InherentImpl { docs, .. }) => {
83-
map.entry(class_name).or_default().inherent = docs.clone();
112+
DocsItem::Struct(s) => {
113+
map.entry(class_name).or_default().definition = *s;
84114
}
85-
PluginItem::ITraitImpl(ITraitImpl {
86-
virtual_method_docs,
87-
..
88-
}) => map
89-
.entry(class_name)
90-
.or_default()
91-
.virtual_methods
92-
.push(virtual_method_docs),
93-
94-
PluginItem::Struct(Struct { docs, .. }) => {
95-
map.entry(class_name).or_default().definition = *docs;
115+
DocsItem::InherentImpl(i) => {
116+
let InherentImplDocs {
117+
methods,
118+
constants,
119+
signals,
120+
} = i;
121+
map.entry(class_name).or_default().methods.push(methods);
122+
map.entry(class_name)
123+
.and_modify(|pieces| pieces.constants.push(constants));
124+
map.entry(class_name)
125+
.and_modify(|pieces| pieces.signals.push(signals));
126+
}
127+
DocsItem::VirtualMethods(methods) => {
128+
map.entry(class_name).or_default().methods.push(methods);
96129
}
97-
98-
_ => (),
99130
}
100131
});
101132

102133
map.into_iter().map(|(class, pieces)| {
103-
let StructDocs {
104-
base,
105-
description,
106-
experimental,
107-
deprecated,
108-
members,
109-
} = pieces.definition;
110-
111-
let virtual_methods = pieces.virtual_methods;
112-
113-
let mut method_docs = String::from_iter(pieces.inherent.methods);
114-
let signal_docs = String::from_iter(pieces.inherent.signals);
115-
let constant_docs = String::from_iter(pieces.inherent.constants);
116-
117-
method_docs.extend(virtual_methods);
118-
let methods_block = if method_docs.is_empty() {
119-
String::new()
120-
} else {
121-
format!("<methods>{method_docs}</methods>")
122-
};
123-
let signals_block = if signal_docs.is_empty() {
124-
String::new()
125-
} else {
126-
format!("<signals>{signal_docs}</signals>")
127-
};
128-
let constants_block = if constant_docs.is_empty() {
129-
String::new()
130-
} else {
131-
format!("<constants>{constant_docs}</constants>")
132-
};
133-
let (brief, description) = match description
134-
.split_once("[br]") {
135-
Some((brief, description)) => (brief, description.trim_start_matches("[br]")),
136-
None => (description, ""),
137-
};
138-
139-
format!(r#"<?xml version="1.0" encoding="UTF-8"?>
134+
let StructDocs {
135+
base,
136+
description,
137+
experimental,
138+
deprecated,
139+
members,
140+
} = pieces.definition;
141+
142+
143+
let method_docs = String::from_iter(pieces.methods);
144+
let signal_docs = String::from_iter(pieces.signals);
145+
let constant_docs = String::from_iter(pieces.constants);
146+
147+
let methods_block = if method_docs.is_empty() {
148+
String::new()
149+
} else {
150+
format!("<methods>{method_docs}</methods>")
151+
};
152+
let signals_block = if signal_docs.is_empty() {
153+
String::new()
154+
} else {
155+
format!("<signals>{signal_docs}</signals>")
156+
};
157+
let constants_block = if constant_docs.is_empty() {
158+
String::new()
159+
} else {
160+
format!("<constants>{constant_docs}</constants>")
161+
};
162+
let (brief, description) = match description
163+
.split_once("[br]") {
164+
Some((brief, description)) => (brief, description.trim_start_matches("[br]")),
165+
None => (description, ""),
166+
};
167+
168+
format!(r#"<?xml version="1.0" encoding="UTF-8"?>
140169
<class name="{class}" inherits="{base}"{deprecated}{experimental} xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
141170
<brief_description>{brief}</brief_description>
142171
<description>{description}</description>
@@ -145,8 +174,8 @@ pub fn gather_xml_docs() -> impl Iterator<Item = String> {
145174
{signals_block}
146175
<members>{members}</members>
147176
</class>"#)
148-
},
149-
)
177+
},
178+
)
150179
}
151180

152181
/// # Safety

godot-core/src/private.rs

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ static CALL_ERRORS: Global<CallErrors> = Global::default();
4343
static ERROR_PRINT_LEVEL: atomic::AtomicU8 = atomic::AtomicU8::new(2);
4444

4545
sys::plugin_registry!(pub __GODOT_PLUGIN_REGISTRY: ClassPlugin);
46+
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
47+
sys::plugin_registry!(pub __GODOT_DOCS_REGISTRY: DocsPlugin);
4648

4749
// ----------------------------------------------------------------------------------------------------------------------------------------------
4850
// Call error handling
@@ -133,26 +135,15 @@ pub fn next_class_id() -> u16 {
133135
NEXT_CLASS_ID.fetch_add(1, atomic::Ordering::Relaxed)
134136
}
135137

136-
// Don't touch unless you know what you're doing.
137-
#[doc(hidden)]
138-
pub fn edit_inherent_impl(class_name: crate::meta::ClassName, f: impl FnOnce(&mut InherentImpl)) {
139-
let mut plugins = __GODOT_PLUGIN_REGISTRY.lock().unwrap();
140-
141-
for elem in plugins.iter_mut().filter(|p| p.class_name == class_name) {
142-
match &mut elem.item {
143-
PluginItem::InherentImpl(inherent_impl) => {
144-
f(inherent_impl);
145-
return;
146-
}
147-
PluginItem::Struct(_) | PluginItem::ITraitImpl(_) | PluginItem::DynTraitImpl(_) => {}
148-
}
149-
}
150-
}
151-
152138
pub(crate) fn iterate_plugins(mut visitor: impl FnMut(&ClassPlugin)) {
153139
sys::plugin_foreach!(__GODOT_PLUGIN_REGISTRY; visitor);
154140
}
155141

142+
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
143+
pub(crate) fn iterate_docs_plugins(mut visitor: impl FnMut(&DocsPlugin)) {
144+
sys::plugin_foreach!(__GODOT_DOCS_REGISTRY; visitor);
145+
}
146+
156147
#[cfg(feature = "codegen-full")] // Remove if used in other scenarios.
157148
pub(crate) fn find_inherent_impl(class_name: crate::meta::ClassName) -> Option<InherentImpl> {
158149
// 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: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,7 @@ impl ClassRegistrationInfo {
116116
// Note: when changing this match, make sure the array has sufficient size.
117117
let index = match item {
118118
PluginItem::Struct { .. } => 0,
119-
PluginItem::InherentImpl(_) => {
120-
// Inherent impls don't need to be unique.
121-
return;
122-
}
119+
PluginItem::InherentImpl(_) => 1,
123120
PluginItem::ITraitImpl { .. } => 2,
124121

125122
// Multiple dyn traits can be registered, thus don't validate for uniqueness.
@@ -428,8 +425,6 @@ fn fill_class_info(item: PluginItem, c: &mut ClassRegistrationInfo) {
428425
is_editor_plugin,
429426
is_internal,
430427
is_instantiable,
431-
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
432-
docs: _,
433428
}) => {
434429
c.parent_class_name = Some(base_class_name);
435430
c.default_virtual_fn = default_get_virtual_fn;
@@ -481,8 +476,6 @@ fn fill_class_info(item: PluginItem, c: &mut ClassRegistrationInfo) {
481476
PluginItem::InherentImpl(InherentImpl {
482477
register_methods_constants_fn,
483478
register_rpcs_fn: _,
484-
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
485-
docs: _,
486479
}) => {
487480
c.register_methods_constants_fn = Some(register_methods_constants_fn);
488481
}
@@ -500,8 +493,6 @@ fn fill_class_info(item: PluginItem, c: &mut ClassRegistrationInfo) {
500493
user_free_property_list_fn,
501494
user_property_can_revert_fn,
502495
user_property_get_revert_fn,
503-
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
504-
virtual_method_docs: _,
505496
#[cfg(since_api = "4.2")]
506497
validate_property_fn,
507498
}) => {

godot-core/src/registry/plugin.rs

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,15 @@
55
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
66
*/
77

8-
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
9-
use crate::docs::*;
8+
use std::any::Any;
9+
use std::{any, fmt};
10+
1011
use crate::init::InitLevel;
1112
use crate::meta::ClassName;
1213
use crate::obj::{bounds, cap, Bounds, DynGd, Gd, GodotClass, Inherits, UserClass};
1314
use crate::registry::callbacks;
1415
use crate::registry::class::GodotGetVirtual;
1516
use crate::{classes, sys};
16-
use std::any::Any;
17-
use std::{any, fmt};
1817

1918
// TODO(bromeon): some information coming from the proc-macro API is deferred through PluginItem, while others is directly
2019
// translated to code. Consider moving more code to the PluginItem, which allows for more dynamic registration and will
@@ -190,16 +189,10 @@ pub struct Struct {
190189

191190
/// Whether the class has a default constructor.
192191
pub(crate) is_instantiable: bool,
193-
194-
/// Documentation extracted from the struct's RustDoc.
195-
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
196-
pub(crate) docs: StructDocs,
197192
}
198193

199194
impl Struct {
200-
pub fn new<T: GodotClass + cap::ImplementsGodotExports>(
201-
#[cfg(all(since_api = "4.3", feature = "register-docs"))] docs: StructDocs,
202-
) -> Self {
195+
pub fn new<T: GodotClass + cap::ImplementsGodotExports>() -> Self {
203196
Self {
204197
base_class_name: T::Base::class_name(),
205198
generated_create_fn: None,
@@ -213,8 +206,6 @@ impl Struct {
213206
is_editor_plugin: false,
214207
is_internal: false,
215208
is_instantiable: false,
216-
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
217-
docs,
218209
}
219210
}
220211

@@ -284,9 +275,6 @@ pub struct InherentImpl {
284275
// This field is only used during codegen-full.
285276
#[cfg_attr(not(feature = "codegen-full"), expect(dead_code))]
286277
pub(crate) register_rpcs_fn: Option<ErasedRegisterRpcsFn>,
287-
288-
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
289-
pub docs: InherentImplDocs,
290278
}
291279

292280
impl InherentImpl {
@@ -298,18 +286,12 @@ impl InherentImpl {
298286
register_rpcs_fn: Some(ErasedRegisterRpcsFn {
299287
raw: callbacks::register_user_rpcs::<T>,
300288
}),
301-
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
302-
docs: Default::default(),
303289
}
304290
}
305291
}
306292

307293
#[derive(Default, Clone, Debug)]
308294
pub struct ITraitImpl {
309-
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
310-
/// Virtual method documentation.
311-
pub(crate) virtual_method_docs: &'static str,
312-
313295
/// Callback to user-defined `register_class` function.
314296
pub(crate) user_register_fn: Option<ErasedRegisterFn>,
315297

@@ -429,12 +411,8 @@ pub struct ITraitImpl {
429411
}
430412

431413
impl ITraitImpl {
432-
pub fn new<T: GodotClass + cap::ImplementsGodotVirtual>(
433-
#[cfg(all(since_api = "4.3", feature = "register-docs"))] virtual_method_docs: &'static str,
434-
) -> Self {
414+
pub fn new<T: GodotClass + cap::ImplementsGodotVirtual>() -> Self {
435415
Self {
436-
#[cfg(all(since_api = "4.3", feature = "register-docs"))]
437-
virtual_method_docs,
438416
get_virtual_fn: Some(callbacks::get_virtual::<T>),
439417
..Default::default()
440418
}

0 commit comments

Comments
 (0)