tiny.smg.command.about.render
Defined in command.about.
API (4)
Actions
Public operations.
Source
Source: tools/smg/src/command/about/render.zig
zig
const std = @import("std");const smg = @import("../../root.zig");const exports = smg.exports;const graph_mod = smg.graph;const model = smg.model;const node_cli = smg.command.node;const query = smg.query;const view = smg.view;pub fn incoming(allocator: std.mem.Allocator, edges: []const model.Edge, coupling_only: bool) ![]const model.Edge { var out: std.ArrayList(model.Edge) = .empty; for (edges) |edge| { if (coupling_only and !exports.isCoupling(edge.rel)) continue; try out.append(allocator, edge); } std.mem.sort(model.Edge, out.items, {}, incomingLess); return try out.toOwnedSlice(allocator);}pub fn outgoing(allocator: std.mem.Allocator, edges: []const model.Edge, coupling_only: bool) ![]const model.Edge { var out: std.ArrayList(model.Edge) = .empty; for (edges) |edge| { if (coupling_only and !exports.isCoupling(edge.rel)) continue; try out.append(allocator, edge); } std.mem.sort(model.Edge, out.items, {}, outgoingLess); return try out.toOwnedSlice(allocator);}fn incomingLess(_: void, a: model.Edge, b: model.Edge) bool { const rel_order = std.mem.order(u8, a.rel, b.rel); if (rel_order != .eq) return rel_order == .lt; return std.mem.lessThan(u8, a.source, b.source);}fn outgoingLess(_: void, a: model.Edge, b: model.Edge) bool { const rel_order = std.mem.order(u8, a.rel, b.rel); if (rel_order != .eq) return rel_order == .lt; return std.mem.lessThan(u8, a.target, b.target);}pub fn neighbors(allocator: std.mem.Allocator, graph: graph_mod.Graph, name: []const u8, coupling_only: bool) ![]const []const u8 { const sub = try query.subgraph(allocator, graph, name, 2, coupling_only); var out: std.ArrayList([]const u8) = .empty; for (sub.nodes.items) |item| { if (!std.mem.eql(u8, item.name, name)) try out.append(allocator, item.name); } std.mem.sort([]const u8, out.items, {}, stringLess); return try out.toOwnedSlice(allocator);}fn stringLess(_: void, a: []const u8, b: []const u8) bool { return std.mem.lessThan(u8, a, b);}pub fn writeText(allocator: std.mem.Allocator, writer: *std.Io.Writer, graph: graph_mod.Graph, node: model.Node, depth: usize, full: bool, coupling_only: bool, incoming_all: usize, outgoing_all: usize, filtered_incoming: []const model.Edge, filtered_outgoing: []const model.Edge, path: []const []const u8, neighborhood: []const []const u8) !void { try writer.print("{s} [{s}]\n", .{ node.name, node.type }); const loc = try node_cli.location(allocator, node); if (loc.len != 0) try writer.print("file: {s}\n", .{loc}); if (node.docstring) |doc| try writer.print("doc: {s}\n", .{firstLine(doc)}); try node_cli.writeMetadata(allocator, writer, node.metadata); if (depth >= 1) { if (path.len > 1) { try writer.writeAll("\nPath: "); for (path, 0..) |item, index| { if (index != 0) try writer.writeAll(" > "); try writer.writeAll(item); } try writer.writeByte('\n'); } const edge_cap = if (full) @as(usize, 0) else @as(usize, 10); if (filtered_incoming.len != 0) { try writer.print("\nIncoming ({d})\n", .{filtered_incoming.len}); for (filtered_incoming, 0..) |edge, index| { if (edge_cap != 0 and index >= edge_cap) { try writer.print(" ... and {d} more (use --full)\n", .{filtered_incoming.len - edge_cap}); break; } try writer.print(" {s} --{s}-->\n", .{ edge.source, edge.rel }); } } if (filtered_outgoing.len != 0) { try writer.print("\nOutgoing ({d})\n", .{filtered_outgoing.len}); for (filtered_outgoing, 0..) |edge, index| { if (edge_cap != 0 and index >= edge_cap) { try writer.print(" ... and {d} more (use --full)\n", .{filtered_outgoing.len - edge_cap}); break; } try writer.print(" --{s}--> {s}\n", .{ edge.rel, edge.target }); } } if (coupling_only) { const hidden_total = incoming_all - filtered_incoming.len + outgoing_all - filtered_outgoing.len; if (hidden_total != 0) try writer.print("\n{d} hidden non-coupling edge(s); rerun with --all-rels to include them\n", .{hidden_total}); } } if (depth >= 2 and neighborhood.len != 0) { const neighbor_cap = if (full) @as(usize, 0) else @as(usize, 10); try writer.print("\nNeighborhood ({d} nodes within 2 hops)\n", .{neighborhood.len}); for (neighborhood, 0..) |neighbor, index| { if (neighbor_cap != 0 and index >= neighbor_cap) { try writer.print(" ... and {d} more (use --full)\n", .{neighborhood.len - neighbor_cap}); break; } const neighbor_node = graph_mod.getNode(&graph, neighbor); const neighbor_type = if (neighbor_node) |item| item.type else "?"; try writer.print(" [{s}] {s}\n", .{ neighbor_type, neighbor }); } }}fn firstLine(value: []const u8) []const u8 { return value[0..(std.mem.indexOfScalar(u8, value, '\n') orelse value.len)];}test "about text renders coupling context and hidden edge count" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); const allocator = arena.allocator(); var graph = graph_mod.init(allocator); try graph_mod.addNode(&graph, .{ .name = "app", .type = model.NodeType.module, .file = "src/app.py" }); try graph_mod.addNode(&graph, .{ .name = "app.main", .type = model.NodeType.function, .file = "src/app.py", .line = 1, .docstring = "main doc\nmore" }); try graph_mod.addNode(&graph, .{ .name = "app.helper", .type = model.NodeType.function, .file = "src/app.py", .line = 5 }); try graph_mod.addEdge(&graph, .{ .source = "app", .rel = model.RelType.contains, .target = "app.main" }); try graph_mod.addEdge(&graph, .{ .source = "app.main", .rel = model.RelType.calls, .target = "app.helper" }); const node = graph_mod.getNode(&graph, "app.main").?; const incoming_all = try view.incoming(graph, allocator, "app.main", null); const outgoing_all = try view.outgoing(graph, allocator, "app.main", null); const filtered_incoming = try incoming(allocator, incoming_all, true); const filtered_outgoing = try outgoing(allocator, outgoing_all, true); const path = try query.containmentPath(allocator, graph, "app.main"); var out: std.Io.Writer.Allocating = .init(allocator); try writeText(allocator, &out.writer, graph, node, 1, false, true, incoming_all.len, outgoing_all.len, filtered_incoming, filtered_outgoing, path, &.{}); try std.testing.expectEqualStrings( \\app.main [function] \\file: src/app.py:1 \\doc: main doc \\ \\Path: app > app.main \\ \\Outgoing (1) \\ --calls--> app.helper \\ \\1 hidden non-coupling edge(s); rerun with --all-rels to include them \\ , out.written());}Source: tools/smg/src/command/about/root.zig:2
zig
pub const render = @import("render.zig");Audit
| Definitions | 5 |
|---|---|
| Public names | 9 |
| Members | 0 |
| Version | 26.7.0 |
| Revision | daab053ee433 |