* Format a single node and its relationships
( node: Node, subgraph: Subgraph, outgoing: Map<string, Edge[]>, printed: Set<string>, lines: string[], depth: number, prefix: string )
| 164 | * Format a single node and its relationships |
| 165 | */ |
| 166 | function formatNodeTree( |
| 167 | node: Node, |
| 168 | subgraph: Subgraph, |
| 169 | outgoing: Map<string, Edge[]>, |
| 170 | printed: Set<string>, |
| 171 | lines: string[], |
| 172 | depth: number, |
| 173 | prefix: string |
| 174 | ): void { |
| 175 | if (printed.has(node.id)) { |
| 176 | return; |
| 177 | } |
| 178 | printed.add(node.id); |
| 179 | |
| 180 | // Node header |
| 181 | const location = node.startLine ? `:${node.startLine}` : ''; |
| 182 | const signature = node.signature ? ` - ${truncate(node.signature, 50)}` : ''; |
| 183 | lines.push(`${prefix}${node.kind}: ${node.name} (${node.filePath}${location})${signature}`); |
| 184 | |
| 185 | // Outgoing edges |
| 186 | const edges = outgoing.get(node.id) ?? []; |
| 187 | const significantEdges = edges.filter((e) => |
| 188 | ['calls', 'extends', 'implements', 'imports', 'references'].includes(e.kind) |
| 189 | ); |
| 190 | |
| 191 | // Group by kind |
| 192 | const edgesByKind = new Map<string, Edge[]>(); |
| 193 | for (const edge of significantEdges) { |
| 194 | const existing = edgesByKind.get(edge.kind) ?? []; |
| 195 | existing.push(edge); |
| 196 | edgesByKind.set(edge.kind, existing); |
| 197 | } |
| 198 | |
| 199 | // Print edges grouped by kind |
| 200 | const newPrefix = prefix + ' '; |
| 201 | for (const [kind, kindEdges] of edgesByKind) { |
| 202 | if (kindEdges.length > 3) { |
| 203 | // Summarize if too many |
| 204 | const names = kindEdges |
| 205 | .slice(0, 3) |
| 206 | .map((e) => { |
| 207 | const target = subgraph.nodes.get(e.target); |
| 208 | return target?.name ?? 'unknown'; |
| 209 | }) |
| 210 | .join(', '); |
| 211 | lines.push(`${newPrefix}├── ${kind}: ${names} and ${kindEdges.length - 3} more`); |
| 212 | } else { |
| 213 | for (let i = 0; i < kindEdges.length; i++) { |
| 214 | const edge = kindEdges[i]!; |
| 215 | const target = subgraph.nodes.get(edge.target); |
| 216 | const targetName = target?.name ?? 'unknown'; |
| 217 | const connector = i === kindEdges.length - 1 ? '└──' : '├──'; |
| 218 | lines.push(`${newPrefix}${connector} ${kind} → ${targetName}`); |
| 219 | } |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | // Recurse for directly connected nodes (limited depth) |