Forge
csharpdeeb25a2
1#nullable enable
2using CascadeIDE.Cockpit.Graph;
3
4namespace CascadeIDE.Cockpit.Channels.TraceFlow;
5
6/// <summary>
7/// Code-flow trace channel: computes a lightweight dominant path for current control-flow subgraph.
8/// </summary>
9public sealed class CodeFlowTraceChannel : ITraceFlowChannel
10{
11 public TraceFlowChannelSnapshot Build(in TraceFlowChannelContext context)
12 {
13 var subgraph = context.Subgraph;
14 if (subgraph.Nodes.Count == 0)
15 return Empty();
16
17 var highlightedNodeIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
18 var highlightedEdgeKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
19 var outgoing = subgraph.Edges
20 .GroupBy(e => e.FromId, StringComparer.OrdinalIgnoreCase)
21 .ToDictionary(g => g.Key, g => g.ToList(), StringComparer.OrdinalIgnoreCase);
22
23 var start = subgraph.Nodes.FirstOrDefault(n => IsAnchor(n.Kind))?.Id ?? subgraph.Nodes[0].Id;
24 var current = start;
25 highlightedNodeIds.Add(current);
26
27 var maxSteps = Math.Max(1, subgraph.Edges.Count + 2);
28 for (var i = 0; i < maxSteps; i++)
29 {
30 if (!outgoing.TryGetValue(current, out var nextEdges) || nextEdges.Count == 0)
31 break;
32
33 var next = PickDominantEdge(nextEdges);
34 highlightedEdgeKeys.Add(MakeEdgeKey(next.FromId, next.ToId));
35 highlightedNodeIds.Add(next.ToId);
36 current = next.ToId;
37
38 if (IsExit(next.Kind))
39 break;
40 }
41
42 return new TraceFlowChannelSnapshot(highlightedNodeIds, highlightedEdgeKeys);
43 }
44
45 private static TraceFlowChannelSnapshot Empty() =>
46 new(new HashSet<string>(StringComparer.OrdinalIgnoreCase), new HashSet<string>(StringComparer.OrdinalIgnoreCase));
47
48 private static bool IsAnchor(string? kind) =>
49 string.Equals(kind, "anchor", StringComparison.OrdinalIgnoreCase);
50
51 private static bool IsExit(string? kind) =>
52 string.Equals(kind, "Exit", StringComparison.OrdinalIgnoreCase);
53
54 private static GraphEdge PickDominantEdge(IReadOnlyList<GraphEdge> edges)
55 {
56 if (edges.Count == 1)
57 return edges[0];
58
59 var nonExit = edges.FirstOrDefault(e => !IsExit(e.Kind));
60 return nonExit ?? edges[0];
61 }
62
63 private static string MakeEdgeKey(string fromId, string toId) => $"{fromId}->{toId}";
64}
65
View only · write via MCP/CIDE