From bea7cc8dd045baa73f3ee1f4b820fc7d12fa420d Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Fri, 28 Apr 2023 00:20:39 +0200 Subject: [PATCH] Add node introspection API Closes #1110 Test Plan: query the introspectNode Endpoint from js Reviewers: Pull Request: https://github.com/GraphiteEditor/Graphite/pull/1183 --- editor/src/dispatcher.rs | 6 +++--- .../portfolio/portfolio_message_handler.rs | 6 +++++- editor/src/node_graph_executor.rs | 4 ++++ frontend/wasm/src/editor_api.rs | 19 +++++++++++++++++++ node-graph/graph-craft/src/proto.rs | 3 ++- .../interpreted-executor/src/executor.rs | 15 +++++++++++++++ 6 files changed, 48 insertions(+), 5 deletions(-) diff --git a/editor/src/dispatcher.rs b/editor/src/dispatcher.rs index 769e3628..31b8027e 100644 --- a/editor/src/dispatcher.rs +++ b/editor/src/dispatcher.rs @@ -8,12 +8,12 @@ use graphene_core::text::Font; pub struct Dispatcher { message_queues: Vec>, pub responses: Vec, - message_handlers: DispatcherMessageHandlers, + pub message_handlers: DispatcherMessageHandlers, } #[remain::sorted] #[derive(Debug, Default)] -struct DispatcherMessageHandlers { +pub struct DispatcherMessageHandlers { broadcast_message_handler: BroadcastMessageHandler, debug_message_handler: DebugMessageHandler, dialog_message_handler: DialogMessageHandler, @@ -21,7 +21,7 @@ struct DispatcherMessageHandlers { input_preprocessor_message_handler: InputPreprocessorMessageHandler, key_mapping_message_handler: KeyMappingMessageHandler, layout_message_handler: LayoutMessageHandler, - portfolio_message_handler: PortfolioMessageHandler, + pub portfolio_message_handler: PortfolioMessageHandler, preferences_message_handler: PreferencesMessageHandler, tool_message_handler: ToolMessageHandler, workspace_message_handler: WorkspaceMessageHandler, diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index 2a77b9da..3f52a855 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -16,7 +16,7 @@ use document_legacy::layers::layer_info::LayerDataType; use document_legacy::layers::style::RenderData; use document_legacy::Operation as DocumentOperation; use graph_craft::document::value::TaggedValue; -use graph_craft::document::NodeInput; +use graph_craft::document::{NodeId, NodeInput}; use graphene_core::raster::Image; use graphene_core::text::Font; @@ -606,6 +606,10 @@ impl MessageHandler Option { + self.executor.introspect_node(node_path) + } + pub fn document(&self, document_id: u64) -> Option<&DocumentMessageHandler> { self.documents.get(&document_id) } diff --git a/editor/src/node_graph_executor.rs b/editor/src/node_graph_executor.rs index 6c965bdc..7f301098 100644 --- a/editor/src/node_graph_executor.rs +++ b/editor/src/node_graph_executor.rs @@ -51,6 +51,10 @@ impl NodeGraphExecutor { } } + pub fn introspect_node(&self, path: &[NodeId]) -> Option { + self.executor.introspect(path).flatten() + } + /// Computes an input for a node in the graph pub fn compute_input(&mut self, old_network: &NodeNetwork, node_path: &[NodeId], mut input_index: usize, editor_api: Cow>) -> Result { let mut network = old_network.clone(); diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index 8bf5798c..2c1a27b9 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -697,6 +697,25 @@ impl JsEditorHandle { let message = DocumentMessage::ToggleLayerExpansion { layer_path }; self.dispatch(message); } + + /// Returns the string representation of the nodes contents + #[wasm_bindgen(js_name = introspectNode)] + pub fn introspect_node(&self, node_path: Vec) -> Option { + let frontend_messages = EDITOR_INSTANCES.with(|instances| { + // Mutably borrow the editors, and if successful, we can access them in the closure + instances.try_borrow_mut().map(|mut editors| { + // Get the editor instance for this editor ID, then dispatch the message to the backend, and return its response `FrontendMessage` queue + editors + .get_mut(&self.editor_id) + .expect("EDITOR_INSTANCES does not contain the current editor_id") + .dispatcher + .message_handlers + .portfolio_message_handler + .introspect_node(&node_path) + }) + }); + frontend_messages.unwrap() + } } // Needed to make JsEditorHandle functions pub to Rust. diff --git a/node-graph/graph-craft/src/proto.rs b/node-graph/graph-craft/src/proto.rs index ad69849c..7d63f094 100644 --- a/node-graph/graph-craft/src/proto.rs +++ b/node-graph/graph-craft/src/proto.rs @@ -275,7 +275,7 @@ impl ProtoNetwork { let paths = self.nodes.iter().map(|(_, node)| node.document_node_path.clone()).collect::>(); let resolved_lookup = resolved.clone(); - if let Some((input_node, id, input, path)) = self.nodes.iter_mut().filter(|(id, _)| !resolved_lookup.contains(id)).find_map(|(id, node)| { + if let Some((input_node, id, input, mut path)) = self.nodes.iter_mut().filter(|(id, _)| !resolved_lookup.contains(id)).find_map(|(id, node)| { if let ProtoNodeInput::Node(input_node, false) = node.input { resolved.insert(*id); let pre_node_input = inputs.get(input_node as usize).expect("input node should exist"); @@ -288,6 +288,7 @@ impl ProtoNetwork { }) { lookup.insert(id, compose_node_id); self.replace_node_references(&lookup, true); + path.push(id); self.nodes.push(( compose_node_id, ProtoNode { diff --git a/node-graph/interpreted-executor/src/executor.rs b/node-graph/interpreted-executor/src/executor.rs index 7db5da40..cc4de12a 100644 --- a/node-graph/interpreted-executor/src/executor.rs +++ b/node-graph/interpreted-executor/src/executor.rs @@ -50,6 +50,10 @@ impl DynamicExecutor { Ok(()) } + pub fn introspect(&self, node_path: &[NodeId]) -> Option> { + self.tree.introspect(node_path) + } + pub fn input_type(&self) -> Option { self.typing_context.type_of(self.output).map(|node_io| node_io.input.clone()) } @@ -99,6 +103,7 @@ impl NodeContainer<'static> { #[derive(Default, Debug, Clone)] pub struct BorrowTree { nodes: HashMap>>>, + source_map: HashMap, NodeId>, } impl BorrowTree { @@ -123,6 +128,7 @@ impl BorrowTree { node.reset(); } old_nodes.remove(&id); + self.source_map.retain(|_, nid| *nid != id); } Ok(old_nodes.into_iter().collect()) } @@ -139,6 +145,14 @@ impl BorrowTree { node } + pub fn introspect(&self, node_path: &[NodeId]) -> Option> { + let id = self.source_map.get(node_path)?; + let node = self.nodes.get(id)?; + let reader = node.read().unwrap(); + let node = reader.node.as_ref(); + Some(node.serialize()) + } + pub fn get(&self, id: NodeId) -> Option>>> { self.nodes.get(&id).cloned() } @@ -163,6 +177,7 @@ impl BorrowTree { pub fn push_node(&mut self, id: NodeId, proto_node: ProtoNode, typing_context: &TypingContext) -> Result<(), String> { let ProtoNode { construction_args, identifier, .. } = proto_node; + self.source_map.insert(proto_node.document_node_path, id); match construction_args { ConstructionArgs::Value(value) => {