Skip to content
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

Feat: implement the hierarchical tree for visualization #2499

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion editor/src/messages/broadcast/broadcast_event.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::messages::prelude::*;

#[derive(PartialEq, Eq, Clone, Debug, serde::Serialize, serde::Deserialize, Hash)]
#[impl_message(Message, BroadcastMessage, TriggerEvent)]
#[derive(PartialEq, Eq, Clone, Debug, serde::Serialize, serde::Deserialize, Hash)]
pub enum BroadcastEvent {
AnimationFrame,
CanvasTransformed,
2 changes: 2 additions & 0 deletions editor/src/messages/debug/mod.rs
Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@ mod debug_message_handler;

pub mod utility_types;

pub use utility_types::DebugMessageTree;

#[doc(inline)]
pub use debug_message::{DebugMessage, DebugMessageDiscriminant};
#[doc(inline)]
30 changes: 30 additions & 0 deletions editor/src/messages/debug/utility_types.rs
Original file line number Diff line number Diff line change
@@ -5,3 +5,33 @@ pub enum MessageLoggingVerbosity {
Names,
Contents,
}

pub struct DebugMessageTree {
name: String,
variants: Option<Vec<DebugMessageTree>>,
}

impl DebugMessageTree {
pub fn new(name: &str) -> DebugMessageTree {
DebugMessageTree {
name: name.to_string(),
variants: None,
}
}

pub fn add_variant(&mut self, variant: DebugMessageTree) {
if let Some(variants) = &mut self.variants {
variants.push(variant);
} else {
self.variants = Some(vec![variant]);
}
}

pub fn name(&self) -> &str {
&self.name
}

pub fn variants(&self) -> Option<&Vec<DebugMessageTree>> {
self.variants.as_ref()
}
}
35 changes: 35 additions & 0 deletions editor/src/messages/message.rs
Original file line number Diff line number Diff line change
@@ -45,3 +45,38 @@ impl specta::Type for MessageDiscriminant {
specta::DataType::Any
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn generate_message_tree() {
let res = Message::build_message_tree();
println!("{}", res.name());
if let Some(variants) = res.variants() {
for (i, variant) in variants.iter().enumerate() {
let is_last = i == variants.len() - 1;
print_tree_node(variant, "", is_last);
}
}
}

fn print_tree_node(tree: &DebugMessageTree, prefix: &str, is_last: bool) {
// Print the current node
let branch = if is_last { "└── " } else { "├── " };
println!("{}{}{}", prefix, branch, tree.name());

// Prepare prefix for children
let child_prefix = if is_last { format!("{} ", prefix) } else { format!("{}│ ", prefix) };

// Print children if any
if let Some(variants) = tree.variants() {
let len = variants.len();
for (i, variant) in variants.iter().enumerate() {
let is_last_child = i == len - 1;
print_tree_node(variant, &child_prefix, is_last_child);
}
}
}
}
4 changes: 2 additions & 2 deletions editor/src/messages/prelude.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Root
pub use crate::utility_traits::{ActionList, AsMessage, MessageHandler, ToDiscriminant, TransitiveChild};
pub use crate::utility_traits::{ActionList, AsMessage, HierarchicalTree, MessageHandler, ToDiscriminant, TransitiveChild};

// Message, MessageData, MessageDiscriminant, MessageHandler
pub use crate::messages::animation::{AnimationMessage, AnimationMessageDiscriminant, AnimationMessageHandler};
pub use crate::messages::broadcast::{BroadcastMessage, BroadcastMessageDiscriminant, BroadcastMessageHandler};
pub use crate::messages::debug::{DebugMessage, DebugMessageDiscriminant, DebugMessageHandler};
pub use crate::messages::debug::{DebugMessage, DebugMessageDiscriminant, DebugMessageHandler, DebugMessageTree};
pub use crate::messages::dialog::export_dialog::{ExportDialogMessage, ExportDialogMessageData, ExportDialogMessageDiscriminant, ExportDialogMessageHandler};
pub use crate::messages::dialog::new_document_dialog::{NewDocumentDialogMessage, NewDocumentDialogMessageDiscriminant, NewDocumentDialogMessageHandler};
pub use crate::messages::dialog::preferences_dialog::{PreferencesDialogMessage, PreferencesDialogMessageData, PreferencesDialogMessageDiscriminant, PreferencesDialogMessageHandler};
4 changes: 4 additions & 0 deletions editor/src/utility_traits.rs
Original file line number Diff line number Diff line change
@@ -45,3 +45,7 @@ pub trait TransitiveChild: Into<Self::Parent> + Into<Self::TopParent> {
pub trait Hint {
fn hints(&self) -> HashMap<String, String>;
}

pub trait HierarchicalTree {
fn build_message_tree() -> DebugMessageTree;
}
4 changes: 2 additions & 2 deletions proc-macros/src/combined_message_attrs.rs
Original file line number Diff line number Diff line change
@@ -61,7 +61,7 @@ pub fn combined_message_attrs_impl(attr: TokenStream, input_item: TokenStream) -
<#parent as ToDiscriminant>::Discriminant
};

input.attrs.push(syn::parse_quote! { #[derive(ToDiscriminant, TransitiveChild)] });
input.attrs.push(syn::parse_quote! { #[derive(ToDiscriminant, TransitiveChild, HierarchicalTree)] });
input.attrs.push(syn::parse_quote! { #[parent(#parent, #parent::#variant)] });
if parent_is_top {
input.attrs.push(syn::parse_quote! { #[parent_is_top] });
@@ -97,7 +97,7 @@ pub fn combined_message_attrs_impl(attr: TokenStream, input_item: TokenStream) -
fn top_level_impl(input_item: TokenStream) -> syn::Result<TokenStream> {
let mut input = syn::parse2::<ItemEnum>(input_item)?;

input.attrs.push(syn::parse_quote! { #[derive(ToDiscriminant)] });
input.attrs.push(syn::parse_quote! { #[derive(ToDiscriminant, HierarchicalTree)] });
input.attrs.push(syn::parse_quote! { #[discriminant_attr(derive(Debug, Copy, Clone, PartialEq, Eq, Hash, AsMessage))] });

for var in &mut input.variants {
60 changes: 60 additions & 0 deletions proc-macros/src/hierarchical_tree.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use proc_macro2::{Span, TokenStream};
use quote::{ToTokens, quote};
use syn::{Data, DeriveInput, Fields, Type, parse2};

pub fn generate_hierarchical_tree(input: TokenStream) -> syn::Result<TokenStream> {
let input = parse2::<DeriveInput>(input)?;
let input_type = &input.ident;

let data = match &input.data {
Data::Enum(data) => data,
_ => 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 has_child = variant
.attrs
.iter()
.any(|attr| attr.path().get_ident().map_or(false, |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);
if "Message" == &field_name[field_name.len().saturating_sub(7)..] {
// 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);
}
}
} else {
quote! {
message_tree.add_variant(DebugMessageTree::new(stringify!(#variant_type)));
}
}
} else {
quote! {
message_tree.add_variant(DebugMessageTree::new(stringify!(#variant_type)));
}
}
});

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)*
message_tree
}
}
};

Ok(res)
}
7 changes: 7 additions & 0 deletions proc-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ mod combined_message_attrs;
mod discriminant;
mod helper_structs;
mod helpers;
mod hierarchical_tree;
mod hint;
mod transitive_child;
mod widget_builder;
@@ -16,6 +17,7 @@ use crate::helper_structs::AttrInnerSingleString;
use crate::hint::derive_hint_impl;
use crate::transitive_child::derive_transitive_child_impl;
use crate::widget_builder::derive_widget_builder_impl;
use hierarchical_tree::generate_hierarchical_tree;
use proc_macro::TokenStream;

/// Derive the `ToDiscriminant` trait and create a `<Type Name>Discriminant` enum
@@ -281,6 +283,11 @@ pub fn derive_widget_builder(input_item: TokenStream) -> TokenStream {
TokenStream::from(derive_widget_builder_impl(input_item.into()).unwrap_or_else(|err| err.to_compile_error()))
}

#[proc_macro_derive(HierarchicalTree)]
pub fn derive_hierarchical_tree(input_item: TokenStream) -> TokenStream {
TokenStream::from(generate_hierarchical_tree(input_item.into()).unwrap_or_else(|err| err.to_compile_error()))
}

#[cfg(test)]
mod tests {
use super::*;