gtk-rs / gtk-rs-core

Rust bindings for GNOME libraries
https://gtk-rs.org/gtk-rs-core
MIT License
280 stars 112 forks source link

[FEATURE REQUEST] Properties macro: Document properties better #1236

Open sophie-h opened 10 months ago

sophie-h commented 10 months ago

I'm using cargo docs online for people to work on Loupe and I'm sometimes using it myself.

With using the Properties macro the docs got a bit worse. Before, getters also had documentation. Now I have to lookup the field in the struct to see it's documentation.

Is it possible to either link to the field in getter/setter docs or do create an overview of all properties for the object or maybe both?

SeaDve commented 10 months ago

I also have the same issue with exposing properties to a public object in a library. Specifically, I had to resort to manually implementing the properties in https://github.com/SeaDve/plotters-gtk4/blob/main/src/paintable.rs to improve the documentation.

I'm wondering if the field documentation on the imp struct can be forwarded to the generated setters and getters docs. For example,

#[derive(glib::Properties)]
#[properties(wrapper_type = Paintable)]
pub struct PaintableImp {
    /// The width of the paintable.
    #[property(get, set)]
    pub(super) width: OnceCell<u32>,
    /// The height of the paintable.
    #[property(get, set)]
    pub(super) height: OnceCell<u32>,
}

will generate the following getters:

impl Paintable {
    /// The width of the paintable.
    pub fn width(&self) -> u32 {
        ...
    }

    /// The height of the paintable.
    pub fn height(&self) -> u32 {
        ...
    }
}

Not sure though of a great API to specialize the documentation for the setters.

carlosmn commented 1 month ago

Unfortunately I don't think you can make the properties get documented in the wrapper type itself as the struct block is implemented by a different macro, but it should be possible to copy the doc from the field into the methods. If we could have a single macro that knows all about the obj and imp all together, then that would be able to do it.

Given that docs are one attribute per line, I wonder if you could add a marker attribute to indicate you're switching to the setter and then the rest of the docs could be for the setter. This is probably terrible stylistically but it could probably work. Something like

#[derive(glib::Properties)]
#[properties(wrapper_type = MyType)]
pub struct MyTypeImp {
    /// The width of the object in my type.
    #[setter_doc]
    /// Set the width of the object. This has a million side effects
    #[property(get, set)]
    pub(super) width: RefCell<u32>,
}

that then becomes

impl MyType {
    /// The width of the paintable.
    pub width() -> u32 {
        ...
    }
    /// Set the width of the object. This has a million side effects
    pub set_width(v: u32) {
        ...
    }
}

you can of course also have

    /// The width of the object in my type.
    #[setter_doc = "Set the width of the object. This has a million side effects"]
    #[property(get, set)]
    pub(super) width: RefCell<u32>,

so you have #[doc = "..."] and #[setter_doc = "..."], the first one rustc does for us and the second we can write ourselves, but of course that's a very annoying way to write docs.I

It doesn't look like we could abuse the //! style of docs here.

carlosmn commented 1 month ago

While trying to implement this I realised it's more complicated because you can have multiple #[property] attributes per field like in the example https://gtk-rs.org/gtk-rs-core/stable/latest/docs/glib/derive.Properties.html#example so it's not just a matter of copying over the doc for the one.

So to support something with comments for both like

/// The name of the author
#[property(name = "author-name", get, set, type = String, member = name)]
/// The author's childhood nickname
#[property(name = "author-nick", get, set, type = String, member = nick)]
author: RefCell<Author>,

and get the right right docs for each of author_name() and author_nick() you'd need to iterate though the doc and property attributes in order and assign the doc to the one that comes after it (to match what I think most people would expect).