expandChild method
Partially expand a specific hidden child by name.
Finds the hidden child whose label (in hwMeta) matches childName
and marks it (plus its connecting hyperedges) as partially visible.
Used by the search/navigate routine to reveal only the path to a
target without fully expanding every level.
Returns true if a new child was revealed.
Implementation
bool expandChild(String nodeId, String childName) {
final node = nodeMap[nodeId];
if (node == null) {
return false;
}
if (node.hiddenChildren == null || node.hiddenChildren!.isEmpty) {
return false;
}
// Find the hidden child by name.
//
// We check both hwMeta.name (display name) AND the last segment of
// hierarchyNodeId (instance name from the netlist). These can
// differ for operator/primitive cells whose display name is the
// translated operator name (e.g. "MUX") while the hierarchy uses the
// cell instance name (e.g. "mux_0").
String? targetChildId;
for (final child in node.hiddenChildren!) {
if (child.hwMeta.name == childName) {
targetChildId = child.id;
break;
}
// Fallback: match on instance name from hierarchy path.
final hid = child.hierarchyNodeId;
if (hid != null) {
final lastSeg =
hid.contains('/') ? hid.substring(hid.lastIndexOf('/') + 1) : hid;
if (lastSeg == childName) {
targetChildId = child.id;
break;
}
}
}
if (targetChildId == null) {
return false;
}
// If the target is already visible in the partial set, nothing to do.
// Do NOT recompute matching hyperedges here: the parent may be in
// blocks-only mode (edges intentionally hidden) and recalculating
// edges would upgrade it to a wired partial expansion — defeating
// the purpose of blocks-only mode.
final existingChildren = node.partialChildIds ?? {};
if (existingChildren.contains(targetChildId)) {
return false;
}
// Find hyperedges that connect only visible children (including
// the newly-revealed target) and/or the parent boundary.
// Edges that reference hidden siblings are excluded — they would
// create dangling wires to nodes the user hasn't expanded yet.
final matchingHyperedgeIds = <String>{};
final mergedChildren = <String>{...existingChildren, targetChildId};
// Also include already-visible children (from full expansion).
for (final child in node.children) {
mergedChildren.add(child.id);
}
final hyperedges = node.hyperedges;
if (hyperedges != null) {
for (final h in hyperedges) {
final allEndpointsVisible = h.sources.every(
(s) => s.$1 == node.id || mergedChildren.contains(s.$1),
) &&
h.targets.every(
(t) => t.$1 == node.id || mergedChildren.contains(t.$1),
);
if (allEndpointsVisible) {
matchingHyperedgeIds.add(h.id);
}
}
}
final existingEdges = node.partialHyperedgeIds ?? {};
// Additive: merge into existing partial sets.
node
..partialChildIds = mergedChildren
..partialHyperedgeIds = {...existingEdges, ...matchingHyperedgeIds};
return true;
}