Skip to content

Commit efc5749

Browse files
committed
docs
1 parent cd31a83 commit efc5749

File tree

16 files changed

+409
-10
lines changed

16 files changed

+409
-10
lines changed

godot-core/src/init/mod.rs

+73
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,77 @@ unsafe extern "C" fn ffi_deinitialize_layer<E: ExtensionLibrary>(
119119
});
120120
}
121121

122+
#[cfg(since_api = "4.3")]
123+
/// # Safety
124+
///
125+
/// The Godot binding must have been initialized before calling this function.
126+
///
127+
/// If "experimental-threads" is not enabled, then this must be called from the same thread that the bindings were initialized from.
128+
unsafe fn register_docs() {
129+
use crate::registry::plugin::PluginItem;
130+
use std::collections::{hash_map::Entry, HashMap};
131+
#[derive(Default)]
132+
struct DocPieces {
133+
/// base, description, members
134+
definition: [&'static str; 3],
135+
/// methods, signals, constants
136+
inherent: [&'static str; 3],
137+
imethods: &'static str,
138+
}
139+
140+
let mut map = HashMap::<&'static str, DocPieces>::new();
141+
crate::private::iterate_plugins(|x| match x.item {
142+
PluginItem::InherentImpl { docs, .. } => match map.entry(x.class_name.as_str()) {
143+
Entry::Occupied(x) => x.into_mut().inherent = docs,
144+
Entry::Vacant(x) => drop(x.insert(DocPieces {
145+
inherent: docs,
146+
..Default::default()
147+
})),
148+
},
149+
PluginItem::ITraitImpl { docs, .. } => match map.entry(x.class_name.as_str()) {
150+
Entry::Occupied(x) => x.into_mut().imethods = docs,
151+
Entry::Vacant(x) => drop(x.insert(DocPieces {
152+
imethods: docs,
153+
..Default::default()
154+
})),
155+
},
156+
PluginItem::Struct { docs, .. } => match map.entry(x.class_name.as_str()) {
157+
Entry::Occupied(x) => x.into_mut().definition = docs,
158+
Entry::Vacant(x) => drop(x.insert(DocPieces {
159+
definition: docs,
160+
..Default::default()
161+
})),
162+
},
163+
});
164+
for (
165+
class,
166+
DocPieces {
167+
definition: [base, desc, members],
168+
inherent: [methods, signals, constants],
169+
imethods,
170+
},
171+
) in map
172+
{
173+
let brief = desc.lines().next().unwrap_or_default();
174+
let xml = format!(
175+
r#"
176+
<?xml version="1.0" encoding="UTF-8"?>
177+
<class name="{class}" inherits="{base}" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
178+
<brief_description>{brief}</brief_description>
179+
<description>{desc}</description>
180+
<methods>{methods}{imethods}</methods>
181+
<constants>{constants}</constants>
182+
<signals>{signals}</signals>
183+
<members>{members}</members>
184+
</class>"#
185+
);
186+
crate::sys::interface_fn!(editor_help_load_xml_from_utf8_chars_and_len)(
187+
xml.as_ptr().cast(),
188+
xml.len() as i64,
189+
);
190+
}
191+
}
192+
122193
/// Tasks needed to be done by gdext internally upon loading an initialization level. Called before user code.
123194
fn gdext_on_level_init(level: InitLevel) {
124195
// SAFETY: we are in the main thread, during initialization, no other logic is happening.
@@ -136,6 +207,8 @@ fn gdext_on_level_init(level: InitLevel) {
136207
ensure_godot_features_compatible();
137208
}
138209
InitLevel::Editor => {
210+
#[cfg(since_api = "4.3")]
211+
register_docs();
139212
sys::load_class_method_table(sys::ClassApiLevel::Editor);
140213
}
141214
}

godot-core/src/registry/class.rs

+3
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;
@@ -282,6 +283,7 @@ fn fill_class_info(item: PluginItem, c: &mut ClassRegistrationInfo) {
282283

283284
PluginItem::InherentImpl {
284285
register_methods_constants_fn,
286+
..
285287
} => {
286288
c.register_methods_constants_fn = Some(register_methods_constants_fn);
287289
}
@@ -299,6 +301,7 @@ fn fill_class_info(item: PluginItem, c: &mut ClassRegistrationInfo) {
299301
user_free_property_list_fn,
300302
user_property_can_revert_fn,
301303
user_property_get_revert_fn,
304+
..
302305
} => {
303306
c.user_register_fn = user_register_fn;
304307

godot-core/src/registry/plugin.rs

+6
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, Members.
100+
docs: [&'static str; 3],
99101
},
100102

101103
/// Collected from `#[godot_api] impl MyClass`.
@@ -104,10 +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,
109+
/// Method, Signal, Constant documentation.
110+
docs: [&'static str; 3],
107111
},
108112

109113
/// Collected from `#[godot_api] impl I... for MyClass`.
110114
ITraitImpl {
115+
/// Virtual method documentation.
116+
docs: &'static str,
111117
/// Callback to user-defined `register_class` function.
112118
user_register_fn: Option<ErasedRegisterFn>,
113119

godot-macros/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ homepage = "https://godot-rust.github.io"
1212

1313
[features]
1414
api-custom = ["godot-bindings/api-custom"]
15+
docs = ["dep:markdown"]
1516

1617
[lib]
1718
proc-macro = true
@@ -21,6 +22,8 @@ proc-macro = true
2122
proc-macro2 = "1.0.63"
2223
quote = "1.0.29"
2324

25+
# Enabled by `docs`
26+
markdown = { version = "1.0.0-alpha.17", optional = true }
2427
venial = "0.6"
2528

2629
[build-dependencies]

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

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::{util, ParseResult};
1010
use proc_macro2::{Ident, TokenStream};
1111
use quote::quote;
1212

13+
#[derive(Clone)]
1314
pub struct ConstDefinition {
1415
pub raw_constant: venial::Constant,
1516
}

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

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
use crate::class::{FieldExport, FieldVar};
99
use proc_macro2::{Ident, TokenStream};
10+
use venial::Attribute;
1011

1112
pub struct Field {
1213
pub name: Ident,
@@ -15,6 +16,7 @@ pub struct Field {
1516
pub var: Option<FieldVar>,
1617
pub export: Option<FieldExport>,
1718
pub is_onready: bool,
19+
pub attributes: Vec<Attribute>,
1820
}
1921

2022
impl Field {
@@ -26,6 +28,7 @@ impl Field {
2628
var: None,
2729
export: None,
2830
is_onready: false,
31+
attributes: field.attributes.clone(),
2932
}
3033
}
3134
}

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use proc_macro2::{Group, Ident, TokenStream, TokenTree};
1111
use quote::{format_ident, quote};
1212

1313
/// Information used for registering a Rust function with Godot.
14+
#[derive(Clone)]
1415
pub struct FuncDefinition {
1516
/// Refined signature, with higher level info and renamed parameters.
1617
pub signature_info: SignatureInfo,
@@ -158,7 +159,7 @@ pub enum ReceiverType {
158159
Static,
159160
}
160161

161-
#[derive(Debug)]
162+
#[derive(Debug, Clone)]
162163
pub struct SignatureInfo {
163164
pub method_name: Ident,
164165
pub receiver_type: ReceiverType,

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

+10-2
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,21 @@ pub fn transform_inherent_impl(mut impl_block: venial::Impl) -> ParseResult<Toke
5151
let (funcs, signals) = process_godot_fns(&class_name, &mut impl_block)?;
5252
let consts = process_godot_constants(&mut impl_block)?;
5353

54-
let signal_registrations = make_signal_registrations(signals, &class_name_obj);
54+
let signal_registrations = make_signal_registrations(signals.clone(), &class_name_obj);
5555

5656
let method_registrations: Vec<TokenStream> = funcs
57+
.clone()
5758
.into_iter()
5859
.map(|func_def| make_method_registration(&class_name, func_def))
5960
.collect::<ParseResult<Vec<TokenStream>>>()?; // <- FIXME transpose this
6061

61-
let constant_registration = make_constant_registration(consts, &class_name, &class_name_obj)?;
62+
let constant_registration =
63+
make_constant_registration(consts.clone(), &class_name, &class_name_obj)?;
64+
65+
#[cfg(feature = "docs")]
66+
let docs = crate::docs::make_inherent_impl_docs(funcs, consts, signals);
67+
#[cfg(not(feature = "docs"))]
68+
let docs = quote! { [""; 3] };
6269

6370
let result = quote! {
6471
#impl_block
@@ -77,6 +84,7 @@ pub fn transform_inherent_impl(mut impl_block: venial::Impl) -> ParseResult<Toke
7784
::godot::sys::plugin_add!(__GODOT_PLUGIN_REGISTRY in #prv; #prv::ClassPlugin {
7885
class_name: #class_name_obj,
7986
item: #prv::PluginItem::InherentImpl {
87+
docs: #docs,
8088
register_methods_constants_fn: #prv::ErasedRegisterFn {
8189
raw: #prv::callbacks::register_user_methods_constants::<#class_name>,
8290
},

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

+5-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +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-
45+
#[cfg(feature = "docs")]
46+
let docs = crate::docs::make_virtual_impl_docs(&original_impl.body_items);
47+
#[cfg(not(feature = "docs"))]
48+
let docs = "";
4649
for item in original_impl.body_items.iter() {
4750
let method = if let venial::ImplMember::AssocFunction(f) = item {
4851
f
@@ -388,6 +391,7 @@ pub fn transform_trait_impl(original_impl: venial::Impl) -> ParseResult<TokenStr
388391
::godot::sys::plugin_add!(__GODOT_PLUGIN_REGISTRY in #prv; #prv::ClassPlugin {
389392
class_name: #class_name_obj,
390393
item: #prv::PluginItem::ITraitImpl {
394+
docs: #docs,
391395
user_register_fn: #register_fn,
392396
user_create_fn: #create_fn,
393397
user_recreate_fn: #recreate_fn,

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

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use proc_macro2::TokenStream;
1010
use quote::quote;
1111

1212
/// Holds information known from a signal's definition
13+
#[derive(Clone)]
1314
pub struct SignalDefinition {
1415
/// The signal's function signature.
1516
pub signature: venial::Function,

godot-macros/src/class/derive_godot_class.rs

+15-5
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ pub fn derive_godot_class(item: venial::Item) -> ParseResult<TokenStream> {
7474

7575
match struct_cfg.init_strategy {
7676
InitStrategy::Generated => {
77-
godot_init_impl = make_godot_init_impl(class_name, fields);
77+
godot_init_impl = make_godot_init_impl(class_name, &fields);
7878
create_fn = quote! { Some(#prv::callbacks::create::<#class_name>) };
7979

8080
if cfg!(since_api = "4.2") {
@@ -104,6 +104,14 @@ pub fn derive_godot_class(item: venial::Item) -> ParseResult<TokenStream> {
104104
};
105105

106106
let is_tool = struct_cfg.is_tool;
107+
#[cfg(feature = "docs")]
108+
let docs = crate::docs::make_definition_docs(
109+
base_class.to_string(),
110+
class.attributes.clone(),
111+
fields.all_fields,
112+
);
113+
#[cfg(not(feature = "docs"))]
114+
let docs = quote! { [""; 3] };
107115

108116
Ok(quote! {
109117
impl ::godot::obj::GodotClass for #class_name {
@@ -129,6 +137,7 @@ pub fn derive_godot_class(item: venial::Item) -> ParseResult<TokenStream> {
129137
::godot::sys::plugin_add!(__GODOT_PLUGIN_REGISTRY in #prv; #prv::ClassPlugin {
130138
class_name: #class_name_obj,
131139
item: #prv::PluginItem::Struct {
140+
docs: #docs,
132141
base_class_name: #base_class_name_obj,
133142
generated_create_fn: #create_fn,
134143
generated_recreate_fn: #recreate_fn,
@@ -192,17 +201,18 @@ struct ClassAttributes {
192201
rename: Option<Ident>,
193202
}
194203

195-
fn make_godot_init_impl(class_name: &Ident, fields: Fields) -> TokenStream {
196-
let base_init = if let Some(Field { name, .. }) = fields.base_field {
204+
fn make_godot_init_impl(class_name: &Ident, fields: &Fields) -> TokenStream {
205+
let base_init = if let Some(Field { name, .. }) = &fields.base_field {
197206
quote! { #name: base, }
198207
} else {
199208
TokenStream::new()
200209
};
201210

202-
let rest_init = fields.all_fields.into_iter().map(|field| {
203-
let field_name = field.name;
211+
let rest_init = fields.all_fields.iter().map(|field| {
212+
let field_name = field.name.clone();
204213
let value_expr = field
205214
.default
215+
.clone()
206216
.unwrap_or_else(|| quote! { ::std::default::Default::default() });
207217

208218
quote! { #field_name: #value_expr, }

0 commit comments

Comments
 (0)