Skip to content

Commit 6ebf19d

Browse files
committed
move doc concatenation to runtime
1 parent 44a33f5 commit 6ebf19d

File tree

8 files changed

+98
-73
lines changed

8 files changed

+98
-73
lines changed

godot-core/src/init/mod.rs

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

8+
use std::collections::{hash_map::Entry, HashMap};
89
use std::sync::atomic::{AtomicBool, Ordering::Relaxed};
910

1011
use godot_ffi as sys;
@@ -13,6 +14,7 @@ use sys::GodotFfi;
1314

1415
use crate::builtin::{GString, StringName};
1516
use crate::out;
17+
use crate::registry::plugin::PluginItem;
1618

1719
pub use sys::GdextBuild;
1820

@@ -119,6 +121,71 @@ unsafe extern "C" fn ffi_deinitialize_layer<E: ExtensionLibrary>(
119121
});
120122
}
121123

124+
unsafe fn register() {
125+
#[derive(Default)]
126+
struct DocPieces {
127+
/// base, description
128+
definition: [&'static str; 2],
129+
/// methods, signals, constants
130+
inherent: [&'static str; 3],
131+
imethods: &'static str,
132+
}
133+
134+
let mut map = HashMap::<&'static str, DocPieces>::new();
135+
crate::private::iterate_plugins(|x| match x.item {
136+
PluginItem::InherentImpl { docs, .. } => match map.entry(x.class_name.as_str()) {
137+
Entry::Occupied(x) => x.into_mut().inherent = docs,
138+
Entry::Vacant(x) => drop(x.insert(DocPieces {
139+
inherent: docs,
140+
..Default::default()
141+
})),
142+
},
143+
PluginItem::ITraitImpl { docs, .. } => match map.entry(x.class_name.as_str()) {
144+
Entry::Occupied(x) => x.into_mut().imethods = docs,
145+
Entry::Vacant(x) => drop(x.insert(DocPieces {
146+
imethods: docs,
147+
..Default::default()
148+
})),
149+
},
150+
PluginItem::Struct { docs, .. } => match map.entry(x.class_name.as_str()) {
151+
Entry::Occupied(x) => x.into_mut().definition = docs,
152+
Entry::Vacant(x) => drop(x.insert(DocPieces {
153+
definition: docs,
154+
..Default::default()
155+
})),
156+
},
157+
});
158+
for (
159+
class,
160+
DocPieces {
161+
definition: [base, desc],
162+
inherent: [methods, signals, constants],
163+
imethods,
164+
},
165+
) in map
166+
{
167+
let brief = desc.lines().next().unwrap_or_default();
168+
let xml = format!(
169+
r#"
170+
<?xml version="1.0" encoding="UTF-8"?>
171+
<class name="{class}" inherits="{base}" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
172+
<brief_description>{brief}</brief_description>
173+
<description>{desc}</description>
174+
<members></members>
175+
<methods>{methods}{imethods}</methods>
176+
<constants>{constants}</constants>
177+
<signals>{signals}</signals>
178+
</class>"#
179+
);
180+
println!("{xml}");
181+
// SAFETY: the godot binding is initialized
182+
crate::sys::interface_fn!(editor_help_load_xml_from_utf8_chars_and_len)(
183+
xml.as_ptr().cast(),
184+
xml.len() as _,
185+
);
186+
}
187+
}
188+
122189
/// Tasks needed to be done by gdext internally upon loading an initialization level. Called before user code.
123190
fn gdext_on_level_init(level: InitLevel) {
124191
// SAFETY: we are in the main thread, during initialization, no other logic is happening.
@@ -136,17 +203,8 @@ fn gdext_on_level_init(level: InitLevel) {
136203
ensure_godot_features_compatible();
137204
}
138205
InitLevel::Editor => {
139-
crate::private::iterate_plugins(|x| match x.item {
140-
crate::registry::plugin::PluginItem::InherentImpl {
141-
register_documentation,
142-
..
143-
} => register_documentation(),
144-
// crate::registry::plugin::PluginItem::ITraitImpl {
145-
// register_documentation,
146-
// ..
147-
// } => register_documentation(),
148-
_ => (),
149-
});
206+
#[cfg(since_api = "4.3")]
207+
register();
150208
sys::load_class_method_table(sys::ClassApiLevel::Editor);
151209
}
152210
}

godot-core/src/registry/class.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ fn fill_class_info(item: PluginItem, c: &mut ClassRegistrationInfo) {
232232
is_editor_plugin,
233233
is_hidden,
234234
is_instantiable,
235+
..
235236
} => {
236237
c.parent_class_name = Some(base_class_name);
237238
c.default_virtual_fn = default_get_virtual_fn;

godot-core/src/registry/plugin.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ pub enum PluginItem {
9696

9797
/// Whether the class has a default constructor.
9898
is_instantiable: bool,
99+
/// Base, Description.
100+
docs: [&'static str; 2],
99101
},
100102

101103
/// Collected from `#[godot_api] impl MyClass`.
@@ -104,14 +106,14 @@ pub enum PluginItem {
104106
///
105107
/// Always present since that's the entire point of this `impl` block.
106108
register_methods_constants_fn: ErasedRegisterFn,
107-
/// Registers the documentation for this class.
108-
register_documentation: unsafe fn(),
109+
/// Method, Signal, Constant documentation.
110+
docs: [&'static str; 3],
109111
},
110112

111113
/// Collected from `#[godot_api] impl I... for MyClass`.
112114
ITraitImpl {
113-
/// Registers the documentation for this class's virtual (`_ready`, …) methods.
114-
register_documentation: unsafe fn(),
115+
/// Virtual method documentation.
116+
docs: &'static str,
115117
/// Callback to user-defined `register_class` function.
116118
user_register_fn: Option<ErasedRegisterFn>,
117119

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ pub fn transform_inherent_impl(mut impl_block: venial::Impl) -> ParseResult<Toke
6262
let constant_registration =
6363
make_constant_registration(consts.clone(), &class_name, &class_name_obj)?;
6464

65-
let docs = crate::docs::class(funcs, None, consts, signals);
65+
let docs = crate::docs::inherent_impl(funcs, consts, signals);
6666
let result = quote! {
6767
#impl_block
6868

@@ -80,7 +80,7 @@ pub fn transform_inherent_impl(mut impl_block: venial::Impl) -> ParseResult<Toke
8080
::godot::sys::plugin_add!(__GODOT_PLUGIN_REGISTRY in #prv; #prv::ClassPlugin {
8181
class_name: #class_name_obj,
8282
item: #prv::PluginItem::InherentImpl {
83-
register_documentation: { #docs docs::<#class_name> },
83+
docs: #docs,
8484
register_methods_constants_fn: #prv::ErasedRegisterFn {
8585
raw: #prv::callbacks::register_user_methods_constants::<#class_name>,
8686
},

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

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,10 @@ pub fn transform_trait_impl(original_impl: venial::Impl) -> ParseResult<TokenStr
4242
let mut virtual_method_names = vec![];
4343

4444
let prv = quote! { ::godot::private };
45-
let docs = crate::docs::class(
46-
None,
47-
original_impl.body_items.iter().filter_map(|x| match x {
48-
venial::ImplMember::AssocFunction(f) => Some(f.clone()),
49-
_ => None,
50-
}),
51-
None,
52-
None,
53-
);
45+
let docs = crate::docs::virtual_impl(original_impl.body_items.iter().filter_map(|x| match x {
46+
venial::ImplMember::AssocFunction(f) => Some(f.clone()),
47+
_ => None,
48+
}));
5449
for item in original_impl.body_items.iter() {
5550
let method = if let venial::ImplMember::AssocFunction(f) = item {
5651
f
@@ -396,7 +391,7 @@ pub fn transform_trait_impl(original_impl: venial::Impl) -> ParseResult<TokenStr
396391
::godot::sys::plugin_add!(__GODOT_PLUGIN_REGISTRY in #prv; #prv::ClassPlugin {
397392
class_name: #class_name_obj,
398393
item: #prv::PluginItem::ITraitImpl {
399-
register_documentation: { #docs docs::<#class_name> },
394+
docs: #docs,
400395
user_register_fn: #register_fn,
401396
user_create_fn: #create_fn,
402397
user_recreate_fn: #recreate_fn,

godot-macros/src/class/derive_godot_class.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,18 +104,14 @@ pub fn derive_godot_class(item: venial::Item) -> ParseResult<TokenStream> {
104104
};
105105

106106
let is_tool = struct_cfg.is_tool;
107-
108-
let docs = crate::docs::docs(class.attributes.clone());
107+
let docs = crate::docs::define(base_class.to_string(), class.attributes.clone());
109108
Ok(quote! {
110109
impl ::godot::obj::GodotClass for #class_name {
111110
type Base = #base_class;
112111

113112
fn class_name() -> ::godot::meta::ClassName {
114113
::godot::meta::ClassName::from_ascii_cstr(#class_name_cstr)
115114
}
116-
117-
const __DOCS: &'static str = #docs;
118-
const __BASE: ::core::option::Option<&'static str> = Some(stringify!(#base_ty));
119115
}
120116

121117
unsafe impl ::godot::obj::Bounds for #class_name {
@@ -133,6 +129,7 @@ pub fn derive_godot_class(item: venial::Item) -> ParseResult<TokenStream> {
133129
::godot::sys::plugin_add!(__GODOT_PLUGIN_REGISTRY in #prv; #prv::ClassPlugin {
134130
class_name: #class_name_obj,
135131
item: #prv::PluginItem::Struct {
132+
docs: #docs,
136133
base_class_name: #base_class_name_obj,
137134
generated_create_fn: #create_fn,
138135
generated_recreate_fn: #recreate_fn,

godot-macros/src/docs.rs

Lines changed: 12 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -89,56 +89,28 @@ pub fn constant(cnst: Constant) -> String {
8989
format!(r#"<constant name="{name}" value="{value}">{docs}</constant>"#)
9090
}
9191

92-
/// This will register the documentation.
93-
pub fn class(
92+
pub fn define(base: String, description: Vec<Attribute>) -> TokenStream {
93+
let desc = docs(description);
94+
quote::quote![[#base, #desc]]
95+
}
96+
97+
pub fn inherent_impl(
9498
functions: impl IntoIterator<Item = FuncDefinition>,
95-
imethods: impl IntoIterator<Item = Function>,
9699
constants: impl IntoIterator<Item = ConstDefinition>,
97100
signals: impl IntoIterator<Item = SignalDefinition>,
98101
) -> TokenStream {
99-
let methods = functions
100-
.into_iter()
101-
.map(method)
102-
.chain(imethods.into_iter().map(imethod))
103-
.collect::<String>();
104-
102+
let methods = functions.into_iter().map(method).collect::<String>();
103+
let signals = signals.into_iter().map(signal).collect::<String>();
105104
let constants = constants
106105
.into_iter()
107106
.map(|ConstDefinition { raw_constant: x }| x)
108107
.map(constant)
109108
.collect::<String>();
109+
quote::quote![[#methods, #signals, #constants]]
110+
}
110111

111-
let signals = signals.into_iter().map(signal).collect::<String>();
112-
113-
let init = format!(
114-
r#"
115-
<?xml version="1.0" encoding="UTF-8" ?>
116-
<class name="CLASS⯂" inherits="BASE⯂"
117-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
118-
<brief_description>BRIEF⯂</brief_description>
119-
<description>DESC⯂</description>
120-
<methods>{methods}</methods>
121-
<constants>{constants}</constants>
122-
<signals>{signals}</signals>
123-
</class>
124-
"#
125-
);
126-
127-
quote::quote! {
128-
unsafe fn docs<T: ::godot::prelude::GodotClass>() {
129-
let xml = #init
130-
.replace("CLASS⯂", T::class_name().as_str())
131-
.replace("BASE⯂", T::__BASE.unwrap_or("Object"))
132-
.replace("BRIEF⯂", T::__DOCS.lines().next().unwrap_or_default())
133-
.replace("DESC⯂", T::__DOCS);
134-
// SAFETY: the godot binding is initialized
135-
// #[cfg(since_api = "4.3")]
136-
::godot::sys::interface_fn!(editor_help_load_xml_from_utf8_chars_and_len)(
137-
xml.as_ptr().cast(),
138-
xml.len() as _,
139-
);
140-
}
141-
}
112+
pub fn virtual_impl(imethods: impl IntoIterator<Item = Function>) -> String {
113+
imethods.into_iter().map(imethod).collect::<String>()
142114
}
143115

144116
pub fn docs(doc: impl IntoIterator<Item = Attribute>) -> String {

itest/rust/src/register_tests/constant_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ godot::sys::plugin_add!(
171171
::godot::private::ClassPlugin {
172172
class_name: HasOtherConstants::class_name(),
173173
item: ::godot::private::PluginItem::InherentImpl {
174-
register_documentation: { unsafe fn x() {} x },
174+
docs: ["";3],
175175
register_methods_constants_fn: ::godot::private::ErasedRegisterFn {
176176
raw: ::godot::private::callbacks::register_user_methods_constants::<HasOtherConstants>,
177177
},

0 commit comments

Comments
 (0)