From ffd9f96c71670e8e9d9a17d1c4a2c24184fe9c48 Mon Sep 17 00:00:00 2001 From: xtof Date: Wed, 14 Oct 2026 14:46:13 +0200 Subject: [PATCH] Add "Show in Schematic" to the design tree's instance menu Right-clicking an instance row in the tree now offers "Show in Schematic (replace view)": it sends resolve_instance, and the instance_resolved reply draws the instance alone with every pin open, inside the frames of its enclosing modules -- the same view a host focus_instance push gives. Not offered on the top design, which has no box of its own. Part of #31. Co-Authored-By: Claude Opus 5.5 --- README.md | 5 +++-- src/NetlistTree.cpp | 13 +++++++++++++ src/NetlistTree.h | 4 ++++ tests/NetlistTreeTest.cpp | 14 ++++++++++++++ tests/SelectionTest.cpp | 8 ++++---- 5 files changed, 38 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4cd571d..dc46e5d 100644 --- a/README.md +++ b/README.md @@ -59,8 +59,9 @@ your own tool). - **Trace to Driver**: the whole combinational fan-in cone of a pin, back to flip-flops and ports. Overlay several traces: each gets its own color, shared wires are striped and shared gates outlined. -- **Grow the schematic by hand**: click an open pin to add its net, - double-click a box to show all of its pins. +- **Grow the schematic by hand**: start from any instance (right-click it + in the design tree, then Show in Schematic), click an open pin to add its + net, double-click a box to show all of its pins. - **Design tree**, loaded lazily so large netlists open fast: instances, ports, nets and bus bits. - **Show Sources**: where an instance, pin or net is declared and, for diff --git a/src/NetlistTree.cpp b/src/NetlistTree.cpp index b5ae6ae..bd8ad03 100644 --- a/src/NetlistTree.cpp +++ b/src/NetlistTree.cpp @@ -274,6 +274,12 @@ void NetlistTreeNode::render() { } } else if (isInstanceNode()) { if (ImGui::BeginPopupContextItem()) { + // The top design has no box of its own to draw. + if (!isRoot()) { + bool clicked = ImGui::MenuItem("Show in Schematic (replace view)"); + viewModeTooltip(true); + if (clicked) getTree()->sendShowInstance(getInstancePath()); + } json req; req["kind"] = "instance"; writePath(req, getInstancePath()); @@ -680,6 +686,13 @@ void NetlistTree::sendLoadEquipotential(const NetlistTree::Path& path, const Ter ws_->send(req.dump()); } +void NetlistTree::sendShowInstance(const InstancePath& path) const { + json req; + req["request"] = "resolve_instance"; + writePath(req, path); + ws_->send(req.dump()); +} + void NetlistTree::sendTraceDriver(const NetlistTree::Path& path, unsigned termChildID, const std::vector& bits, const std::string& label, bool clearView) const { diff --git a/src/NetlistTree.h b/src/NetlistTree.h index 807772f..ee502df 100644 --- a/src/NetlistTree.h +++ b/src/NetlistTree.h @@ -51,6 +51,10 @@ class NetlistTree { void sendTraceDriver(const Path& path, unsigned termChildID, const std::vector& bits, const std::string& label, bool clearView = true) const; + // Asks the provider to resolve the instance at `path`; its + // instance_resolved reply redraws the schematic with that instance + // alone, every pin open (same as a host focus_instance push). + void sendShowInstance(const InstancePath& path) const; INetlistProvider* getProvider() const { return ws_; } // Called before every tree-initiated equipotential request. diff --git a/tests/NetlistTreeTest.cpp b/tests/NetlistTreeTest.cpp index 9ff4dc1..2c1d407 100644 --- a/tests/NetlistTreeTest.cpp +++ b/tests/NetlistTreeTest.cpp @@ -229,6 +229,20 @@ TEST(NetlistTree, SendLoadEquipotentialCanKeepTheView) { TraceStore::clear(); } +// "Show in Schematic": resolve_instance by ids and names; the reply draws it. +TEST(NetlistTree, SendShowInstanceRequestsResolveInstance) { + FakeNetlistProvider provider; + NetlistTree tree(&provider); + + tree.sendShowInstance(InstancePath{{1, "u1"}, {4, ""}}); + + ASSERT_EQ(provider.sent.size(), 1u); + auto req = nlohmann::json::parse(provider.sent[0]); + EXPECT_EQ(req["request"], "resolve_instance"); + EXPECT_EQ(req["id_path"], (std::vector{1, 4})); + EXPECT_EQ(req["path"], (std::vector{"u1", ""})); +} + TEST(NetlistTree, SendTraceDriverFormatsScalarRequestAndFiresCallback) { FakeNetlistProvider provider; NetlistTree tree(&provider); diff --git a/tests/SelectionTest.cpp b/tests/SelectionTest.cpp index 3400fdb..bf7c6a3 100644 --- a/tests/SelectionTest.cpp +++ b/tests/SelectionTest.cpp @@ -331,12 +331,12 @@ TEST_F(TreeReveal, ContextMenuClickDoesNotSelectTheRowUnderIt) { provider.sent.clear(); click(at(4, 60.f), ImGuiMouseButton_Right); // u0's context menu - // Its first entry ("Show Properties") lies over the next row, u1's. + // Its first entry ("Show in Schematic") lies over the next row, u1's. click(ImVec2(at(4, 60.f).x + 30.f, at(5, 0.f).y), ImGuiMouseButton_Left); - auto props = take("get_properties"); - ASSERT_EQ(props.size(), 1u); - EXPECT_EQ(props[0]["id_path"], nlohmann::json::array({0})); // u0's + auto resolve = take("resolve_instance"); + ASSERT_EQ(resolve.size(), 1u); + EXPECT_EQ(resolve[0]["id_path"], nlohmann::json::array({0})); // u0's EXPECT_FALSE(SelectionStore::isSelected(InstancePath{{1, "u1"}})); EXPECT_FALSE(SelectionStore::isSelected(InstancePath{{2, "u2"}}));