Skip to content

Commit 449749d

Browse files
committed
docs
1 parent feff365 commit 449749d

File tree

19 files changed

+404
-11
lines changed

19 files changed

+404
-11
lines changed

examples/dodge-the-creeps/rust/src/main_scene.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use godot::prelude::*;
88
use rand::Rng as _;
99
use std::f32::consts::PI;
1010

11-
// Deriving GodotClass makes the class available to Godot
11+
/// Deriving GodotClass makes the class available to Godot
1212
#[derive(GodotClass)]
1313
#[class(base=Node)]
1414
pub struct Main {
@@ -22,6 +22,7 @@ pub struct Main {
2222
#[godot_api]
2323
impl Main {
2424
#[func]
25+
/// Ends the game.
2526
fn game_over(&mut self) {
2627
let mut score_timer = self.base().get_node_as::<Timer>("ScoreTimer");
2728
let mut mob_timer = self.base().get_node_as::<Timer>("MobTimer");
@@ -37,6 +38,7 @@ impl Main {
3738
}
3839

3940
#[func]
41+
/// Restarts the game.
4042
pub fn new_game(&mut self) {
4143
let start_position = self.base().get_node_as::<Marker2D>("StartPosition");
4244
let mut player = self.base().get_node_as::<player::Player>("Player");
@@ -56,6 +58,7 @@ impl Main {
5658
}
5759

5860
#[func]
61+
/// Begins the initial timers.
5962
fn on_start_timer_timeout(&self) {
6063
let mut mob_timer = self.base().get_node_as::<Timer>("MobTimer");
6164
let mut score_timer = self.base().get_node_as::<Timer>("ScoreTimer");
@@ -64,6 +67,7 @@ impl Main {
6467
}
6568

6669
#[func]
70+
/// Increments the score (why havent you renamed your signals)
6771
fn on_score_timer_timeout(&mut self) {
6872
self.score += 1;
6973

@@ -72,6 +76,7 @@ impl Main {
7276
}
7377

7478
#[func]
79+
/// Called when a new mob needs to be created.
7580
fn on_mob_timer_timeout(&mut self) {
7681
let mut mob_spawn_location = self
7782
.base()

examples/dodge-the-creeps/rust/src/mob.rs

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use rand::seq::SliceRandom;
44

55
#[derive(GodotClass)]
66
#[class(base=RigidBody2D)]
7+
/// This is a *mob*!
78
pub struct Mob {
89
pub min_speed: real,
910
pub max_speed: real,
@@ -14,11 +15,13 @@ pub struct Mob {
1415
#[godot_api]
1516
impl Mob {
1617
#[func]
18+
/// Kills itself.
1719
fn on_visibility_screen_exited(&mut self) {
1820
self.base_mut().queue_free();
1921
}
2022

2123
#[func]
24+
/// Kills itself.
2225
fn on_start_game(&mut self) {
2326
self.base_mut().queue_free();
2427
}

examples/dodge-the-creeps/rust/src/player.rs

+6
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ use godot::prelude::*;
33

44
#[derive(GodotClass)]
55
#[class(base=Area2D)]
6+
/// The player controlled character.
67
pub struct Player {
8+
#[export]
9+
/// DOCUMENTATION
710
speed: real,
811
screen_size: Vector2,
912

@@ -13,9 +16,11 @@ pub struct Player {
1316
#[godot_api]
1417
impl Player {
1518
#[signal]
19+
/// Emitted when we die.
1620
fn hit();
1721

1822
#[func]
23+
/// Dies.
1924
fn on_player_body_entered(&mut self, _body: Gd<PhysicsBody2D>) {
2025
self.base_mut().hide();
2126
self.base_mut().emit_signal("hit".into(), &[]);
@@ -28,6 +33,7 @@ impl Player {
2833
}
2934

3035
#[func]
36+
/// Begins.
3137
pub fn start(&mut self, pos: Vector2) {
3238
self.base_mut().set_global_position(pos);
3339
self.base_mut().show();

godot-core/src/init/mod.rs

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

122+
#[cfg(since_api = "4.3")]
123+
unsafe fn register() {
124+
use crate::registry::plugin::PluginItem;
125+
use std::collections::{hash_map::Entry, HashMap};
126+
#[derive(Default)]
127+
struct DocPieces {
128+
/// base, description, members
129+
definition: [&'static str; 3],
130+
/// methods, signals, constants
131+
inherent: [&'static str; 3],
132+
imethods: &'static str,
133+
}
134+
135+
let mut map = HashMap::<&'static str, DocPieces>::new();
136+
crate::private::iterate_plugins(|x| match x.item {
137+
PluginItem::InherentImpl { docs, .. } => match map.entry(x.class_name.as_str()) {
138+
Entry::Occupied(x) => x.into_mut().inherent = docs,
139+
Entry::Vacant(x) => drop(x.insert(DocPieces {
140+
inherent: docs,
141+
..Default::default()
142+
})),
143+
},
144+
PluginItem::ITraitImpl { docs, .. } => match map.entry(x.class_name.as_str()) {
145+
Entry::Occupied(x) => x.into_mut().imethods = docs,
146+
Entry::Vacant(x) => drop(x.insert(DocPieces {
147+
imethods: docs,
148+
..Default::default()
149+
})),
150+
},
151+
PluginItem::Struct { docs, .. } => match map.entry(x.class_name.as_str()) {
152+
Entry::Occupied(x) => x.into_mut().definition = docs,
153+
Entry::Vacant(x) => drop(x.insert(DocPieces {
154+
definition: docs,
155+
..Default::default()
156+
})),
157+
},
158+
});
159+
for (
160+
class,
161+
DocPieces {
162+
definition: [base, desc, members],
163+
inherent: [methods, signals, constants],
164+
imethods,
165+
},
166+
) in map
167+
{
168+
let brief = desc.lines().next().unwrap_or_default();
169+
let xml = format!(
170+
r#"
171+
<?xml version="1.0" encoding="UTF-8"?>
172+
<class name="{class}" inherits="{base}" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
173+
<brief_description>{brief}</brief_description>
174+
<description>{desc}</description>
175+
<methods>{methods}{imethods}</methods>
176+
<constants>{constants}</constants>
177+
<signals>{signals}</signals>
178+
<members>{members}</members>
179+
</class>"#
180+
);
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,6 +203,8 @@ fn gdext_on_level_init(level: InitLevel) {
136203
ensure_godot_features_compatible();
137204
}
138205
InitLevel::Editor => {
206+
#[cfg(since_api = "4.3")]
207+
register();
139208
sys::load_class_method_table(sys::ClassApiLevel::Editor);
140209
}
141210
}

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
@@ -9,6 +9,7 @@ categories = ["game-engines", "graphics"]
99

1010
[features]
1111
api-custom = ["godot-bindings/api-custom"]
12+
docs = ["dep:markdown"]
1213

1314
[lib]
1415
proc-macro = true
@@ -22,6 +23,8 @@ godot = { path = "../godot" }
2223
proc-macro2 = "1.0.63"
2324
quote = "1.0.29"
2425

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

2730
[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::inherent_impl(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

+8-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,13 @@ 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::virtual_impl(original_impl.body_items.iter().filter_map(|x| match x {
47+
venial::ImplMember::AssocFunction(f) => Some(f.clone()),
48+
_ => None,
49+
}));
50+
#[cfg(not(feature = "docs"))]
51+
let docs = "";
4652
for item in original_impl.body_items.iter() {
4753
let method = if let venial::ImplMember::AssocFunction(f) = item {
4854
f
@@ -388,6 +394,7 @@ pub fn transform_trait_impl(original_impl: venial::Impl) -> ParseResult<TokenStr
388394
::godot::sys::plugin_add!(__GODOT_PLUGIN_REGISTRY in #prv; #prv::ClassPlugin {
389395
class_name: #class_name_obj,
390396
item: #prv::PluginItem::ITraitImpl {
397+
docs: #docs,
391398
user_register_fn: #register_fn,
392399
user_create_fn: #create_fn,
393400
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,

0 commit comments

Comments
 (0)