#How can i get DocComments in my macro and use them?

43 messages · Page 1 of 1 (latest)

raven spear
#

I saw in the https://github.com/RichoDemus/bevy-console crate, that you can just write comments and they then get used by the macro to describe the command and its fields in the GUI.

How could i do something similar?
I'd like to be able to describe my struct with a /// comment, and have my derive macro just pick that up and put it in one of its generated functions.

#

@polar aurora / @tawny jasper
sorry for the pings, i see you guys are quite active/the creator.
if one of you ever have some time explaining that to me that would be greatly appreciated!

I simply can't find the relevant code in your crate and it drives me crazy xD
maybe i'm just blind though or am missing something.

tawny jasper
#

Hey, I believe clap already does that

raven spear
#

oh

#

that'd explain why i can't find the relevant code piece

tawny jasper
#

Yeah have a look at the Parser derive macro

raven spear
#

thanks a lot, sorry for the pings again!

tawny jasper
#

no worries!

#

the help command will print the clap cmd.get_about info

raven spear
#

i see

raven spear
#

thanks :D

untold cave
#

If you're struggling to use it with your macro, paste what you have and I can help

raven spear
#

awesome, thank you!

raven spear
#

@untold cave
I'm not quite sure on how to get to the attributes.

https://github.com/thebluefish/bevy_commandify/blob/main/src/lib.rs#L24
here you seem to originally get your args, which you then parse.
and give it to commandify

in here you get the attributes: https://github.com/thebluefish/bevy_commandify/blob/bec89d64f4d8ad049e527d896c94e399e4c99642/src/gen.rs#L16

which you then give to the docs function.

but i don't really understand the first part.

how i've done it so far is:

#[proc_macro_derive(QevyEntity, attributes(qevy_entity))]
pub fn qevy_entity_derive_macro(
    item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
    entities::qevy_entity_derive_macro2(item.into())
        .unwrap()
        .into()
}

this is my derive macro, and i only get the tokenstream.
i'm unsure on how to get the attributes for docs, as &[Attribute] type.

untold cave
#

what does qevy_entity_derive_macro2 do?

#

with a derive macro like that, you should parse it like ```rs
let input = parse_macro_input!(item as DeriveInput);

Then `input.attrs` will contain your doc comments (and other attributes)
raven spear
# untold cave what does `qevy_entity_derive_macro2` do?
use syn::{Attribute, DeriveInput, Meta};

#[derive(deluxe::ExtractAttributes)]
#[deluxe(attributes(qevy_entity))]
struct QevyEntityStructAttributes {
    #[deluxe(default = "Point".to_string())]
    entity_type: String,
    #[deluxe(default = "".to_string())]
    desc: String, // -> i'd like to replace this with doc comments!
}

pub(crate) fn qevy_entity_derive_macro2(
    item: proc_macro2::TokenStream,
) -> deluxe::Result<proc_macro2::TokenStream> {
    // Parse
    let mut ast: DeriveInput = syn::parse2(item)?;

    // Extract struct attributes
    let QevyEntityStructAttributes { entity_type, desc } = deluxe::extract_attributes(&mut ast)?;
    // only exists to check if the entity type is valid
    let _ = QevyEntityType::from_short_string(entity_type.as_str())
        .expect(format!("Invalid entity type: {}", entity_type).as_str());

    // Extract field attributes
    // TODO: do we need field attributes for entities? if so, what?
    // Description!! doc comments? yes

    // define impl variables
    let ident = &ast.ident;
    let (impl_generics, type_generics, where_clause) = ast.generics.split_for_impl();

    // Generate code
    let generated_code = quote::quote!(
        impl #impl_generics QevyEntity for #ident #type_generics #where_clause {
            fn get_description(&self) -> &str {
                #desc
            }

            fn get_entity_type(&self) -> QevyEntityType {
                QevyEntityType::from_short_string(#entity_type).unwrap()
            }
        }
    );

    Ok(generated_code)
}

// Copied from: https://github.com/thebluefish/bevy_commandify/blob/main/src/parse.rs#L303
fn get_comments(attrs: &[Attribute]) -> proc_macro2::TokenStream {
    let mut docs = Vec::new();
    for attr in attrs {
        if let Meta::NameValue(meta) = &attr.meta {
            if meta.path.is_ident("doc") {
                docs.push(attr);
            }
        }
    }
    quote::quote!(#(#docs)*)
}
#

this, plus the given part above is all i currently have

#

i gtg, need to reply further later. thanks for the help though!

untold cave
#

So if you just want to copy the doc comments out as-is, you might do ```rs
// Extract field attributes
let docs = get_comments(&ast.attrs);

#

And then you could slap them on your generated items like rs #docs fn get_description(&self) -> &str {

raven spear
raven spear
#

Whenever i see you i expect a not so nice Person because of your Profile picture XD

Glad you're not, thanks for your help!

untold cave
#

What are you trying to do with it?

#

The doc comment will become an attribute that looks like rs #[doc = "foo"]

raven spear
raven spear
untold cave
#

Hmm well that's for one..

raven spear
#

With "one" you mean

/// Blablabla

Works, but

/// Blablabla
/// Blablabla

Does not work?

untold cave
#

filter_map it like that 😄

untold cave
#

That would just get the first, you would ideally get them all and return a list (or concatenate them into one string like how bevy_reflect offers doc comments)

raven spear
#

Once i'm on my PC again i'll have to Look into it

#

Thanks!
Should be able to work from here

raven spear
raven spear
#
fn get_comments(attrs: &[Attribute]) -> Option<String> {
    let mut docs = Vec::new();
    for attr in attrs {
        if let Meta::NameValue(MetaNameValue { path, value, .. }) = &attr.meta {
            if path.is_ident("doc") {
                match value {
                    syn::Expr::Lit(lit) => {
                        if let syn::Lit::Str(lit_str) = &lit.lit {
                            docs.push(lit_str.value());
                        }
                    }
                    _ => {}
                }
            }
        }
    }
    None
}

that seems to at least compile, need to check if it works

#

okay, cool! got it working