From 77f8bfd9eda16c41081349b8c5bdc48ec6193b54 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Sat, 17 May 2025 16:13:05 -0700 Subject: [PATCH] Improve clarify of type errors and tooltip diagnostics --- .../node_graph/node_graph_message_handler.rs | 6 ++-- .../utility_types/network_interface.rs | 4 +-- frontend/src/components/views/Graph.svelte | 5 +-- node-graph/gcore/src/types.rs | 30 +++++++++-------- node-graph/graph-craft/src/proto.rs | 33 ++++++++++++++----- 5 files changed, 50 insertions(+), 28 deletions(-) diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs index d64a7535..2a178f4f 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs @@ -2234,7 +2234,7 @@ impl NodeGraphMessageHandler { let mut inputs = inputs.into_iter().map(|input| { input.map(|input| FrontendGraphInput { data_type: FrontendGraphDataType::displayed_type(&input.ty, &input.type_source), - resolved_type: Some(format!("{:?} from {:?}", &input.ty, input.type_source)), + resolved_type: Some(format!("{:?}", &input.ty)), valid_types: input.valid_types.iter().map(|ty| ty.to_string()).collect(), name: input.input_name.unwrap_or_else(|| input.ty.nested_type().to_string()), description: input.input_description.unwrap_or_default(), @@ -2258,7 +2258,7 @@ impl NodeGraphMessageHandler { data_type: frontend_data_type, name: "Output 1".to_string(), description: String::new(), - resolved_type: primary_output_type.map(|(input, type_source)| format!("{input:?} from {type_source:?}")), + resolved_type: primary_output_type.map(|(input, _)| format!("{input:?}")), connected_to, }) } else { @@ -2292,7 +2292,7 @@ impl NodeGraphMessageHandler { data_type: frontend_data_type, name: output_name, description: String::new(), - resolved_type: exposed_output.clone().map(|(input, type_source)| format!("{input:?} from {type_source:?}")), + resolved_type: exposed_output.clone().map(|(input, _)| format!("{input:?}")), connected_to, }); } diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface.rs b/editor/src/messages/portfolio/document/utility_types/network_interface.rs index db083e54..6d65bf48 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -816,7 +816,7 @@ impl NodeNetworkInterface { data_type, name: import_name, description: String::new(), - resolved_type: Some(format!("{input_type:?} from {type_source:?}")), + resolved_type: Some(format!("{input_type:?}")), connected_to, }, click_target, @@ -901,7 +901,7 @@ impl NodeNetworkInterface { data_type: frontend_data_type, name: export_name, description: String::new(), - resolved_type: input_type.map(|(export_type, source)| format!("{export_type:?} from {source:?}")), + resolved_type: input_type.map(|(export_type, _source)| format!("{export_type:?}")), valid_types: self.valid_input_types(&InputConnector::Export(*export_index), network_path).iter().map(|ty| ty.to_string()).collect(), connected_to, }, diff --git a/frontend/src/components/views/Graph.svelte b/frontend/src/components/views/Graph.svelte index d82becbe..e4735c7f 100644 --- a/frontend/src/components/views/Graph.svelte +++ b/frontend/src/components/views/Graph.svelte @@ -581,11 +581,12 @@ } function dataTypeTooltip(value: FrontendGraphInput | FrontendGraphOutput): string { - return value.resolvedType ? `Resolved Data:\n${value.resolvedType}` : `Unresolved Data ${value.dataType}`; + return value.resolvedType ? `Data Type:\n${value.resolvedType}` : `Data Type (Unresolved):\n${value.dataType}`; } function validTypesText(value: FrontendGraphInput): string { - return `Valid Types:\n${value.validTypes.join(",\n ")}`; + const validTypes = value.validTypes.length > 0 ? value.validTypes.map((x) => `• ${x}`).join("\n") : "None"; + return `Valid Types:\n${validTypes}`; } function outputConnectedToText(output: FrontendGraphOutput): string { diff --git a/node-graph/gcore/src/types.rs b/node-graph/gcore/src/types.rs index a616daa1..b65668d1 100644 --- a/node-graph/gcore/src/types.rs +++ b/node-graph/gcore/src/types.rs @@ -324,26 +324,30 @@ fn format_type(ty: &str) -> String { impl core::fmt::Debug for Type { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - Self::Generic(arg0) => write!(f, "Generic<{arg0}>"), + let result = match self { + Self::Generic(name) => name.to_string(), #[cfg(feature = "type_id_logging")] - Self::Concrete(arg0) => write!(f, "Concrete<{}, {:?}>", arg0.name, arg0.id), + Self::Concrete(ty) => format!("Concrete<{}, {:?}>", ty.name, ty.id), #[cfg(not(feature = "type_id_logging"))] - Self::Concrete(arg0) => write!(f, "Concrete<{}>", format_type(&arg0.name)), - Self::Fn(arg0, arg1) => write!(f, "{arg0:?} → {arg1:?}"), - Self::Future(arg0) => write!(f, "Future<{arg0:?}>"), - } + Self::Concrete(ty) => format_type(&ty.name), + Self::Fn(call_arg, return_value) => format!("{return_value:?} called with {call_arg:?}"), + Self::Future(ty) => format!("{ty:?}"), + }; + let result = result.replace("Option>", "Context"); + write!(f, "{}", result) } } impl std::fmt::Display for Type { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Type::Generic(name) => write!(f, "{name}"), - Type::Concrete(ty) => write!(f, "{}", format_type(&ty.name)), - Type::Fn(input, output) => write!(f, "{input} → {output}"), - Type::Future(ty) => write!(f, "Future<{ty}>"), - } + let result = match self { + Type::Generic(name) => name.to_string(), + Type::Concrete(ty) => format_type(&ty.name), + Type::Fn(call_arg, return_value) => format!("{return_value} called with {call_arg}"), + Type::Future(ty) => ty.to_string(), + }; + let result = result.replace("Option>", "Context"); + write!(f, "{}", result) } } diff --git a/node-graph/graph-craft/src/proto.rs b/node-graph/graph-craft/src/proto.rs index 2594eb9a..bc3189e7 100644 --- a/node-graph/graph-craft/src/proto.rs +++ b/node-graph/graph-craft/src/proto.rs @@ -552,19 +552,36 @@ impl core::fmt::Debug for GraphErrorType { GraphErrorType::NoImplementations => write!(f, "No implementations found"), GraphErrorType::NoConstructor => write!(f, "No construct found for node"), GraphErrorType::InvalidImplementations { inputs, error_inputs } => { - let format_error = |(index, (_found, expected)): &(usize, (Type, Type))| format!("• Input {}: {expected}, found: {_found}", index + 1); - let format_error_list = |errors: &Vec<(usize, (Type, Type))>| errors.iter().map(format_error).collect::>().join("\n").replace("Option>", "Context"); + let format_error = |(index, (found, expected)): &(usize, (Type, Type))| { + let index = index + 1; + format!( + "\ + • Input {index}:\n\ + …found: {found}\n\ + …expected: {expected}\ + " + ) + }; + let format_error_list = |errors: &Vec<(usize, (Type, Type))>| errors.iter().map(format_error).collect::>().join("\n"); let mut errors = error_inputs.iter().map(format_error_list).collect::>(); errors.sort(); - let inputs = inputs.replace("Option>", "Context"); + let errors = errors.join("\n"); + let incompatibility = if errors.chars().filter(|&c| c == '•').count() == 1 { + "This input type is incompatible:" + } else { + "These input types are incompatible:" + }; + write!( f, - "This node isn't compatible with the combination of types for the data it is given:\n\ - {inputs}\n\ + "\ + {incompatibility}\n\ + {errors}\n\ \n\ - Each invalid input should be replaced by data with one of these supported types:\n\ - {}", - errors.join("\n") + The node is currently receiving all of the following input types:\n\ + {inputs}\n\ + This is not a supported arrangement of types for the node.\ + " ) } GraphErrorType::MultipleImplementations { inputs, valid } => write!(f, "Multiple implementations found ({inputs}):\n{valid:#?}"),