feat: render doTry/doCatch/doFinally like route-level handler sections
Backend: DO_TRY compounds now use a virtual _TRY_BODY wrapper with LR layout for the try body, while DO_CATCH/DO_FINALLY stack below as separate sections (TB). Edges from DO_TRY are skipped like route-level handler edges. Removes ELK-v2 debug logging. Frontend: _TRY_BODY renders as transparent wrapper, DO_CATCH as red tinted section, DO_FINALLY as teal section. DO_FINALLY color changed from red to teal (completion handler, not error). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -265,10 +265,11 @@ public class ElkDiagramRenderer implements DiagramRenderer {
|
||||
}
|
||||
|
||||
// Process main flow nodes into the root ELK graph
|
||||
Set<String> doTryNodeIds = new HashSet<>();
|
||||
for (RouteNode rn : mainNodes) {
|
||||
if (!elkNodeMap.containsKey(rn.getId())) {
|
||||
createElkNodeRecursive(rn, rootNode, factory, elkNodeMap, nodeColors,
|
||||
compoundNodeIds, childNodeIds);
|
||||
compoundNodeIds, childNodeIds, doTryNodeIds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,13 +288,20 @@ public class ElkDiagramRenderer implements DiagramRenderer {
|
||||
NodePlacementStrategy.LINEAR_SEGMENTS);
|
||||
|
||||
createElkNodeRecursive(rn, handlerRoot, factory, elkNodeMap, nodeColors,
|
||||
compoundNodeIds, childNodeIds);
|
||||
compoundNodeIds, childNodeIds, doTryNodeIds);
|
||||
handlerRoots.add(handlerRoot);
|
||||
}
|
||||
|
||||
// Create ELK edges — skip edges that cross between different ELK root graphs
|
||||
if (graph.getEdges() != null) {
|
||||
for (RouteEdge re : graph.getEdges()) {
|
||||
// Skip all edges originating from DO_TRY nodes — these are entry/handler
|
||||
// edges that don't need layout (try body has its own internal flow,
|
||||
// handler edges are like route-level ON_EXCEPTION edges)
|
||||
if (doTryNodeIds.contains(re.getSource())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ElkNode sourceElk = elkNodeMap.get(re.getSource());
|
||||
ElkNode targetElk = elkNodeMap.get(re.getTarget());
|
||||
if (sourceElk == null || targetElk == null) {
|
||||
@@ -322,17 +330,6 @@ public class ElkDiagramRenderer implements DiagramRenderer {
|
||||
engine.layout(handlerRoot, new BasicProgressMonitor());
|
||||
}
|
||||
|
||||
// Debug: log root dimensions and children after layout
|
||||
System.out.println("[ELK-v2] rootNode " + rootNode.getWidth() + "x" + rootNode.getHeight()
|
||||
+ " children=" + rootNode.getChildren().size()
|
||||
+ " mainNodes=" + mainNodes.size() + " handlerNodes=" + handlerNodes.size());
|
||||
for (ElkNode child : rootNode.getChildren()) {
|
||||
System.out.println("[ELK-v2] " + child.getIdentifier()
|
||||
+ " x=" + String.format("%.1f", child.getX())
|
||||
+ " y=" + String.format("%.1f", child.getY())
|
||||
+ " parent=" + (child.getParent() != null ? child.getParent().getIdentifier() : "null"));
|
||||
}
|
||||
|
||||
// Extract positioned nodes
|
||||
List<PositionedNode> positionedNodes = new ArrayList<>();
|
||||
Map<String, CompoundInfo> compoundInfos = new HashMap<>();
|
||||
@@ -533,7 +530,8 @@ public class ElkDiagramRenderer implements DiagramRenderer {
|
||||
private void createElkNodeRecursive(
|
||||
RouteNode rn, ElkNode parentElk, ElkGraphFactory factory,
|
||||
Map<String, ElkNode> elkNodeMap, Map<String, Color> nodeColors,
|
||||
Set<String> compoundNodeIds, Set<String> childNodeIds) {
|
||||
Set<String> compoundNodeIds, Set<String> childNodeIds,
|
||||
Set<String> doTryNodeIds) {
|
||||
|
||||
boolean isCompound = rn.getType() != null && COMPOUND_TYPES.contains(rn.getType())
|
||||
&& rn.getChildren() != null && !rn.getChildren().isEmpty();
|
||||
@@ -542,7 +540,63 @@ public class ElkDiagramRenderer implements DiagramRenderer {
|
||||
elkNode.setIdentifier(rn.getId());
|
||||
elkNode.setParent(parentElk);
|
||||
|
||||
if (isCompound) {
|
||||
if (isCompound && rn.getType() == NodeType.DO_TRY) {
|
||||
// DO_TRY: vertical container with a virtual _TRY_BODY wrapper for the try body
|
||||
// and DO_CATCH/DO_FINALLY as separate children below
|
||||
doTryNodeIds.add(rn.getId());
|
||||
compoundNodeIds.add(rn.getId());
|
||||
elkNode.setWidth(200);
|
||||
elkNode.setHeight(100);
|
||||
elkNode.setProperty(CoreOptions.ALGORITHM, "org.eclipse.elk.layered");
|
||||
elkNode.setProperty(CoreOptions.DIRECTION, Direction.DOWN);
|
||||
elkNode.setProperty(CoreOptions.SPACING_NODE_NODE, NODE_SPACING * 0.6);
|
||||
elkNode.setProperty(CoreOptions.SPACING_EDGE_NODE, EDGE_SPACING * 0.5);
|
||||
elkNode.setProperty(CoreOptions.PADDING,
|
||||
new org.eclipse.elk.core.math.ElkPadding(COMPOUND_TOP_PADDING,
|
||||
COMPOUND_SIDE_PADDING, COMPOUND_SIDE_PADDING, COMPOUND_SIDE_PADDING));
|
||||
|
||||
// Separate try body children from handler children
|
||||
List<RouteNode> tryBodyChildren = new ArrayList<>();
|
||||
List<RouteNode> handlerChildren = new ArrayList<>();
|
||||
for (RouteNode child : rn.getChildren()) {
|
||||
if (child.getType() == NodeType.DO_CATCH || child.getType() == NodeType.DO_FINALLY) {
|
||||
handlerChildren.add(child);
|
||||
} else {
|
||||
tryBodyChildren.add(child);
|
||||
}
|
||||
}
|
||||
|
||||
// Virtual _TRY_BODY wrapper with LR direction for horizontal try body chain
|
||||
if (!tryBodyChildren.isEmpty()) {
|
||||
String wrapperId = rn.getId() + "._try_body";
|
||||
ElkNode wrapper = factory.createElkNode();
|
||||
wrapper.setIdentifier(wrapperId);
|
||||
wrapper.setParent(elkNode);
|
||||
wrapper.setWidth(200);
|
||||
wrapper.setHeight(40);
|
||||
wrapper.setProperty(CoreOptions.ALGORITHM, "org.eclipse.elk.layered");
|
||||
wrapper.setProperty(CoreOptions.DIRECTION, Direction.RIGHT);
|
||||
wrapper.setProperty(CoreOptions.SPACING_NODE_NODE, NODE_SPACING * 0.5);
|
||||
wrapper.setProperty(CoreOptions.SPACING_EDGE_NODE, EDGE_SPACING * 0.5);
|
||||
wrapper.setProperty(CoreOptions.PADDING,
|
||||
new org.eclipse.elk.core.math.ElkPadding(8, 8, 8, 8));
|
||||
compoundNodeIds.add(wrapperId);
|
||||
elkNodeMap.put(wrapperId, wrapper);
|
||||
|
||||
for (RouteNode child : tryBodyChildren) {
|
||||
childNodeIds.add(child.getId());
|
||||
createElkNodeRecursive(child, wrapper, factory, elkNodeMap, nodeColors,
|
||||
compoundNodeIds, childNodeIds, doTryNodeIds);
|
||||
}
|
||||
}
|
||||
|
||||
// Handler children are direct children of DO_TRY (stacked below)
|
||||
for (RouteNode child : handlerChildren) {
|
||||
childNodeIds.add(child.getId());
|
||||
createElkNodeRecursive(child, elkNode, factory, elkNodeMap, nodeColors,
|
||||
compoundNodeIds, childNodeIds, doTryNodeIds);
|
||||
}
|
||||
} else if (isCompound) {
|
||||
compoundNodeIds.add(rn.getId());
|
||||
elkNode.setWidth(200);
|
||||
elkNode.setHeight(100);
|
||||
@@ -557,7 +611,7 @@ public class ElkDiagramRenderer implements DiagramRenderer {
|
||||
for (RouteNode child : rn.getChildren()) {
|
||||
childNodeIds.add(child.getId());
|
||||
createElkNodeRecursive(child, elkNode, factory, elkNodeMap, nodeColors,
|
||||
compoundNodeIds, childNodeIds);
|
||||
compoundNodeIds, childNodeIds, doTryNodeIds);
|
||||
}
|
||||
} else {
|
||||
elkNode.setWidth(NODE_WIDTH);
|
||||
@@ -583,11 +637,47 @@ public class ElkDiagramRenderer implements DiagramRenderer {
|
||||
List<PositionedNode> children = List.of();
|
||||
if (compoundNodeIds.contains(rn.getId()) && rn.getChildren() != null) {
|
||||
children = new ArrayList<>();
|
||||
for (RouteNode child : rn.getChildren()) {
|
||||
ElkNode childElk = elkNodeMap.get(child.getId());
|
||||
if (childElk != null) {
|
||||
children.add(extractPositionedNode(child, childElk, elkNodeMap,
|
||||
compoundNodeIds, compoundInfos, rootNode));
|
||||
|
||||
if (rn.getType() == NodeType.DO_TRY) {
|
||||
// DO_TRY: extract virtual _TRY_BODY wrapper first, then handler children
|
||||
String wrapperId = rn.getId() + "._try_body";
|
||||
ElkNode wrapperElk = elkNodeMap.get(wrapperId);
|
||||
if (wrapperElk != null) {
|
||||
List<PositionedNode> wrapperChildren = new ArrayList<>();
|
||||
for (RouteNode child : rn.getChildren()) {
|
||||
if (child.getType() != NodeType.DO_CATCH && child.getType() != NodeType.DO_FINALLY) {
|
||||
ElkNode childElk = elkNodeMap.get(child.getId());
|
||||
if (childElk != null) {
|
||||
wrapperChildren.add(extractPositionedNode(child, childElk, elkNodeMap,
|
||||
compoundNodeIds, compoundInfos, rootNode));
|
||||
}
|
||||
}
|
||||
}
|
||||
children.add(new PositionedNode(
|
||||
wrapperId, "", "_TRY_BODY",
|
||||
getAbsoluteX(wrapperElk, rootNode),
|
||||
getAbsoluteY(wrapperElk, rootNode),
|
||||
wrapperElk.getWidth(), wrapperElk.getHeight(),
|
||||
wrapperChildren));
|
||||
compoundInfos.put(wrapperId, new CompoundInfo(wrapperId, Color.WHITE));
|
||||
}
|
||||
// Handler children (DO_CATCH, DO_FINALLY)
|
||||
for (RouteNode child : rn.getChildren()) {
|
||||
if (child.getType() == NodeType.DO_CATCH || child.getType() == NodeType.DO_FINALLY) {
|
||||
ElkNode childElk = elkNodeMap.get(child.getId());
|
||||
if (childElk != null) {
|
||||
children.add(extractPositionedNode(child, childElk, elkNodeMap,
|
||||
compoundNodeIds, compoundInfos, rootNode));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (RouteNode child : rn.getChildren()) {
|
||||
ElkNode childElk = elkNodeMap.get(child.getId());
|
||||
if (childElk != null) {
|
||||
children.add(extractPositionedNode(child, childElk, elkNodeMap,
|
||||
compoundNodeIds, compoundInfos, rootNode));
|
||||
}
|
||||
}
|
||||
}
|
||||
compoundInfos.put(rn.getId(), new CompoundInfo(rn.getId(), colorForType(rn.getType())));
|
||||
|
||||
Reference in New Issue
Block a user