Skip to content

Add struct field visualization to the editor message hierarchy tree visualization on the website #2917

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ use crate::messages::prelude::*;
#[impl_message(Message, DialogMessage, ExportDialog)]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum ExportDialogMessage {
#[child]
FileType(FileType),
#[child]
ExportBounds(ExportBounds),
ScaleFactor(f64),
TransparentBackground(bool),
ExportBounds(ExportBounds),

Submit,
}
2 changes: 2 additions & 0 deletions editor/src/messages/frontend/utility_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub enum MouseCursorIcon {
Rotate,
}

#[impl_message(Message, ExportDialogMessage, FileType)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum FileType {
#[default]
Expand All @@ -47,6 +48,7 @@ impl FileType {
}
}

#[impl_message(Message, ExportDialogMessage, ExportBounds)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum ExportBounds {
#[default]
Expand Down
32 changes: 23 additions & 9 deletions editor/src/messages/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ pub enum Message {
Workspace(WorkspaceMessage),

// Messages
NoOp,
Batched {
messages: Box<[Message]>,
},
NoOp,
}

/// Provides an impl of `specta::Type` for `MessageDiscriminant`, the struct created by `impl_message`.
Expand Down Expand Up @@ -96,6 +96,17 @@ mod test {
}
}

// Print message field if any
if let Some(fields) = tree.fields() {
let len = fields.len();
for (i, field) in fields.iter().enumerate() {
let is_last_field = i == len - 1;
let branch = if is_last_field { "└── " } else { "├── " };

file.write_all(format!("{}{}{}\n", child_prefix, branch, field).as_bytes()).unwrap();
}
}

// Print handler field if any
if let Some(data) = tree.message_handler_fields() {
let len = data.fields().len();
Expand All @@ -104,16 +115,19 @@ mod test {
} else {
("└── ", format!("{} ", prefix))
};
if data.path().is_empty() {
file.write_all(format!("{}{}{}\n", prefix, branch, data.name()).as_bytes()).unwrap();
} else {

const FRONTEND_MESSAGE_STR: &str = "FrontendMessage";
if data.name().is_empty() && tree.name() != FRONTEND_MESSAGE_STR {
panic!("{}'s MessageHandler is missing #[message_handler_data]", tree.name());
} else if tree.name() != FRONTEND_MESSAGE_STR {
file.write_all(format!("{}{}{} `{}`\n", prefix, branch, data.name(), data.path()).as_bytes()).unwrap();
}
for (i, field) in data.fields().iter().enumerate() {
let is_last_field = i == len - 1;
let branch = if is_last_field { "└── " } else { "├── " };

file.write_all(format!("{}{}{}\n", child_prefix, branch, field.0).as_bytes()).unwrap();
for (i, field) in data.fields().iter().enumerate() {
let is_last_field = i == len - 1;
let branch = if is_last_field { "└── " } else { "├── " };

file.write_all(format!("{}{}{}\n", child_prefix, branch, field.0).as_bytes()).unwrap();
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion editor/src/messages/prelude.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Root
pub use crate::utility_traits::{ActionList, AsMessage, HierarchicalTree, MessageHandler, ToDiscriminant, TransitiveChild};
pub use crate::utility_traits::{ActionList, AsMessage, ExtractField, HierarchicalTree, MessageHandler, ToDiscriminant, TransitiveChild};
pub use crate::utility_types::{DebugMessageTree, MessageData};
// Message, MessageData, MessageDiscriminant, MessageHandler
pub use crate::messages::animation::{AnimationMessage, AnimationMessageDiscriminant, AnimationMessageHandler};
Expand Down
3 changes: 2 additions & 1 deletion editor/src/messages/tool/tool_messages/shape_tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use graphene_std::renderer::Quad;
use graphene_std::vector::misc::ArcType;
use std::vec;

#[derive(Default)]
#[derive(Default, ExtractField)]
pub struct ShapeTool {
fsm_state: ShapeToolFsmState,
tool_data: ShapeToolData,
Expand Down Expand Up @@ -191,6 +191,7 @@ impl LayoutHolder for ShapeTool {
}
}

#[message_handler_data]
impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for ShapeTool {
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
let ToolMessage::Shape(ShapeToolMessage::UpdateOptions(action)) = message else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ pub struct TransformLayerMessageHandler {
ghost_outline: Vec<(Vec<ClickTargetType>, DAffine2)>,
}

#[message_handler_data]
impl MessageHandler<TransformLayerMessage, TransformLayerMessageContext<'_>> for TransformLayerMessageHandler {
fn process_message(&mut self, message: TransformLayerMessage, responses: &mut VecDeque<Message>, context: TransformLayerMessageContext) {
let TransformLayerMessageContext {
Expand Down
6 changes: 6 additions & 0 deletions editor/src/utility_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,9 @@ pub trait HierarchicalTree {
""
}
}

pub trait ExtractField {
fn field_types() -> Vec<(String, usize)>;
fn path() -> &'static str;
fn print_field_types();
}
20 changes: 12 additions & 8 deletions editor/src/utility_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ impl MessageData {
#[derive(Debug)]
pub struct DebugMessageTree {
name: String,
fields: Option<Vec<String>>,
variants: Option<Vec<DebugMessageTree>>,
message_handler: Option<MessageData>,
message_handler_data: Option<MessageData>,
Expand All @@ -36,13 +37,18 @@ impl DebugMessageTree {
pub fn new(name: &str) -> DebugMessageTree {
DebugMessageTree {
name: name.to_string(),
fields: None,
variants: None,
message_handler: None,
message_handler_data: None,
path: "",
}
}

pub fn add_fields(&mut self, fields: Vec<String>) {
self.fields = Some(fields);
}

pub fn set_path(&mut self, path: &'static str) {
self.path = path;
}
Expand All @@ -67,6 +73,10 @@ impl DebugMessageTree {
&self.name
}

pub fn fields(&self) -> Option<&Vec<String>> {
self.fields.as_ref()
}

pub fn path(&self) -> &'static str {
self.path
}
Expand All @@ -84,16 +94,10 @@ impl DebugMessageTree {
}

pub fn has_message_handler_data_fields(&self) -> bool {
match self.message_handler_data_fields() {
Some(_) => true,
None => false,
}
self.message_handler_data_fields().is_some()
}

pub fn has_message_handler_fields(&self) -> bool {
match self.message_handler_fields() {
Some(_) => true,
None => false,
}
self.message_handler_fields().is_some()
}
}
4 changes: 3 additions & 1 deletion node-graph/interpreted-executor/src/node_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use graph_craft::document::value::RenderOutput;
use graph_craft::proto::{NodeConstructor, TypeErasedBox};
use graphene_core::raster::color::Color;
use graphene_core::raster::*;
use graphene_core::raster_types::{CPU, GPU, Raster};
#[cfg(feature = "gpu")]
use graphene_core::raster_types::GPU;
use graphene_core::raster_types::{CPU, Raster};
use graphene_core::{Artboard, concrete, generic};
use graphene_core::{Cow, ProtoNodeIdentifier, Type};
use graphene_core::{NodeIO, NodeIOTypes};
Expand Down
10 changes: 5 additions & 5 deletions proc-macros/src/extract_fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,23 @@ pub fn derive_extract_field_impl(input: TokenStream) -> syn::Result<TokenStream>
})
.collect::<Vec<_>>();

let field_str = field_info.into_iter().map(|(name, ty)| (format!("{}: {}", name, ty)));
let field_str = field_info.into_iter().map(|(name, ty)| (format!("{name}: {ty}")));

let res = quote! {
impl #impl_generics #struct_name #ty_generics #where_clause {
pub fn field_types() -> Vec<(String, usize)> {
impl #impl_generics ExtractField for #struct_name #ty_generics #where_clause {
fn field_types() -> Vec<(String, usize)> {
vec![
#((String::from(#field_str), #field_line)),*
]
}

pub fn print_field_types() {
fn print_field_types() {
for (field, line) in Self::field_types() {
println!("{} at line {}", field, line);
}
}

pub fn path() -> &'static str {
fn path() -> &'static str {
file!()
}
}
Expand Down
103 changes: 71 additions & 32 deletions proc-macros/src/hierarchical_tree.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::helpers::clean_rust_type_syntax;
use proc_macro2::{Span, TokenStream};
use quote::{ToTokens, quote};
use syn::{Data, DeriveInput, Fields, Type, parse2};
Expand All @@ -11,51 +12,89 @@ pub fn generate_hierarchical_tree(input: TokenStream) -> syn::Result<TokenStream
_ => return Err(syn::Error::new(Span::call_site(), "Tried to derive HierarchicalTree for non-enum")),
};

let build_message_tree = data.variants.iter().map(|variant| {
let variant_type = &variant.ident;
let build_message_tree: Result<Vec<_>, syn::Error> = data
.variants
.iter()
.map(|variant| {
let variant_type = &variant.ident;

let has_child = variant
.attrs
.iter()
.any(|attr| attr.path().get_ident().is_some_and(|ident| ident == "sub_discriminant" || ident == "child"));
let has_child = variant
.attrs
.iter()
.any(|attr| attr.path().get_ident().is_some_and(|ident| ident == "sub_discriminant" || ident == "child"));

if has_child {
if let Fields::Unnamed(fields) = &variant.fields {
let field_type = &fields.unnamed.first().unwrap().ty;
quote! {
{
let mut variant_tree = DebugMessageTree::new(stringify!(#variant_type));
let field_name = stringify!(#field_type);
const message_string: &str = "Message";
if message_string == &field_name[field_name.len().saturating_sub(message_string.len())..] {
// The field is a Message type, recursively build its tree
let sub_tree = #field_type::build_message_tree();
variant_tree.add_variant(sub_tree);
}
message_tree.add_variant(variant_tree);
match &variant.fields {
Fields::Unit => Ok(quote! {
message_tree.add_variant(DebugMessageTree::new(stringify!(#variant_type)));
}),
Fields::Unnamed(fields) => {
if has_child {
let field_type = &fields.unnamed.first().unwrap().ty;
Ok(quote! {
{
let mut variant_tree = DebugMessageTree::new(stringify!(#variant_type));
let field_name = stringify!(#field_type);
const MESSAGE_SUFFIX: &str = "Message";
if MESSAGE_SUFFIX == &field_name[field_name.len().saturating_sub(MESSAGE_SUFFIX.len())..] {
// The field is a Message type, recursively build its tree
let sub_tree = #field_type::build_message_tree();
variant_tree.add_variant(sub_tree);
} else {
variant_tree.add_fields(vec![format!("{field_name}")]);
}
message_tree.add_variant(variant_tree);
}
})
} else {
let error_msg = match fields.unnamed.len() {
0 => format!("Remove the unnecessary `()` from the `{}` message enum variant.", variant_type),
1 => {
let field_type = &fields.unnamed.first().unwrap().ty;
format!(
"The `{}` message should be defined as a struct-style (not tuple-style) enum variant to maintain consistent formatting across all editor messages.\n\
Replace `{}` with a named field using {{curly braces}} instead of a positional field using (parentheses).",
variant_type,
field_type.to_token_stream()
)
}
_ => {
let field_types = fields.unnamed.iter().map(|f| f.ty.to_token_stream().to_string()).collect::<Vec<_>>().join(", ");
format!(
"The `{}` message should be defined as a struct-style (not tuple-style) enum variant to maintain consistent formatting across all editor messages.\n\
Replace `{}` with named fields using {{curly braces}} instead of positional fields using (parentheses).",
variant_type, field_types
)
}
};
return Err(syn::Error::new(Span::call_site(), error_msg));
}
}
} else {
quote! {
message_tree.add_variant(DebugMessageTree::new(stringify!(#variant_type)));
Fields::Named(fields) => {
let names = fields.named.iter().map(|f| f.ident.as_ref().unwrap());
let ty = fields.named.iter().map(|f| clean_rust_type_syntax(f.ty.to_token_stream().to_string()));
Ok(quote! {
{
let mut field_names = Vec::new();
#(field_names.push(format!("{}: {}",stringify!(#names), #ty));)*
let mut variant_tree = DebugMessageTree::new(stringify!(#variant_type));
variant_tree.add_fields(field_names);
message_tree.add_variant(variant_tree);
}
})
}
}
} else {
quote! {
message_tree.add_variant(DebugMessageTree::new(stringify!(#variant_type)));
}
}
});
})
.collect();
let build_message_tree = build_message_tree?;

let res = quote! {
impl HierarchicalTree for #input_type {
fn build_message_tree() -> DebugMessageTree {
let mut message_tree = DebugMessageTree::new(stringify!(#input_type));
#(#build_message_tree)*

let message_handler_str = #input_type::message_handler_str();
if message_handler_str.fields().len() > 0 {
message_tree.add_message_handler_field(message_handler_str);
}
message_tree.add_message_handler_field(message_handler_str);

let message_handler_data_str = #input_type::message_handler_data_str();
if message_handler_data_str.fields().len() > 0 {
Expand Down
4 changes: 1 addition & 3 deletions proc-macros/src/message_handler_data_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,8 @@ pub fn message_handler_data_attr_impl(attr: TokenStream, input_item: TokenStream
quote! {
#input_item
impl #message_type {
pub fn message_handler_data_str() -> MessageData
{
pub fn message_handler_data_str() -> MessageData {
MessageData::new(format!("{}", stringify!(#type_name)), #type_name::field_types(), #type_name::path())

}
pub fn message_handler_str() -> MessageData {
MessageData::new(format!("{}", stringify!(#input_type)), #input_type::field_types(), #input_type::path())
Expand Down
Loading
Loading