tiny.smg.command.scan.text
Defined in command.scan.
API (1)
Actions
Public operations.
Source
Source: tools/smg/src/command/scan/root.zig:8
zig
pub const text = @import("text.zig");Source: tools/smg/src/command/scan/text.zig
zig
const std = @import("std");const smg = @import("../../root.zig");const counts = smg.command.scan.counts;const hints = smg.command.scan.hints;const model = smg.model;const output = smg.cli.output;const scan = smg.scan;pub fn render(allocator: std.mem.Allocator, stats: scan.Stats, full: bool) ![]const u8 { var rows: std.ArrayList(ScanTextRow) = .empty; const languages = try counts.parts(allocator, stats.lang_counts.items); const types = try counts.parts(allocator, stats.type_counts.items); const node_value = if (stats.nodes_removed != 0) try std.fmt.allocPrint(allocator, "+{d} -{d}", .{ stats.nodes_added, stats.nodes_removed }) else types; try rows.append(allocator, .{ .label = "Nodes", .value = node_value }); const edge_value = if (stats.edges_removed != 0) try std.fmt.allocPrint(allocator, "+{d} -{d}", .{ stats.edges_added, stats.edges_removed }) else try std.fmt.allocPrint(allocator, "{d}", .{stats.edges_added}); try rows.append(allocator, .{ .label = "Edges", .value = edge_value }); if (stats.skipped_edges != 0) { try rows.append(allocator, .{ .label = "Skipped", .value = try std.fmt.allocPrint(allocator, "{d} unresolved", .{stats.skipped_edges}) }); try rows.append(allocator, .{ .label = "Skipped by kind", .value = try counts.parts(allocator, stats.skipped_edge_categories.items) }); const advice = try counts.sorted(allocator, stats.skipped_edge_categories.items); const cap = @min(advice.len, @as(usize, 3)); for (advice[0..cap]) |item| { try rows.append(allocator, .{ .label = try std.fmt.allocPrint(allocator, "Hint: {s}", .{item.name}), .value = hints.text(item.name), }); } } var out: std.Io.Writer.Allocating = .init(allocator); try out.writer.print("Scanned {d} files ({s})\n", .{ stats.files, languages }); try writeRows(allocator, &out.writer, rows.items); const total_edges = stats.edges_added + stats.skipped_edges; if (stats.skipped_edges > 0 and total_edges > 0 and stats.skipped_edges * 2 > total_edges) { try out.writer.print("\nHint: {d}/{d} edges were skipped (targets outside the scanned scope). Try scanning a wider directory to resolve cross-module edges.\n", .{ stats.skipped_edges, total_edges }); } if (full and stats.skipped_edge_samples.items.len != 0) { try out.writer.writeAll("Skipped edge examples:\n"); const cap = @min(stats.skipped_edge_samples.items.len, @as(usize, 5)); for (stats.skipped_edge_samples.items[0..cap]) |sample| { try out.writer.print(" {s} --{s}--> {s} ({s}: {s})\n", .{ sample.source, sample.rel, sample.target, sample.category, sample.reason }); } } else if (stats.skipped_edge_samples.items.len != 0) { try out.writer.writeAll("Skipped edge examples hidden; use --full to show samples.\n"); } if (full and stats.orphaned_manual_edges.items.len != 0) { try out.writer.print("\nWarning: {d} manual edge(s) orphaned:\n", .{stats.orphaned_manual_edges.items.len}); for (stats.orphaned_manual_edges.items) |edge| { try out.writer.print(" {s} --{s}--> {s} ({s})\n", .{ edge.source, edge.rel, edge.target, edge.reason }); } } return try out.toOwnedSlice();}const ScanTextRow = struct { label: []const u8, value: []const u8,};fn writeRows(allocator: std.mem.Allocator, writer: *std.Io.Writer, rows: []const ScanTextRow) !void { var label_width: usize = 0; for (rows) |row| label_width = @max(label_width, row.label.len); const value_width = if (label_width + 2 >= 80) @as(usize, 1) else 80 - label_width - 2; for (rows) |row| try writeRow(allocator, writer, row, label_width, value_width);}fn writeRow(allocator: std.mem.Allocator, writer: *std.Io.Writer, row: ScanTextRow, label_width: usize, value_width: usize) !void { var rest = row.value; var first = true; while (first or rest.len != 0) { const chunk_len = wrapLength(rest, value_width); var line: std.Io.Writer.Allocating = .init(allocator); if (first) { try output.writeDocText(&line.writer, row.label); try output.writeSpacesDoc(&line.writer, label_width -| row.label.len); } else { try output.writeSpacesDoc(&line.writer, label_width); } try output.writeDocText(&line.writer, " "); try output.writeDocText(&line.writer, rest[0..chunk_len]); try output.writeSpacesDoc(&line.writer, 80 -| line.written().len); try writeTrimmedLine(writer, line.written()); rest = rest[chunk_len..]; while (rest.len != 0 and rest[0] == ' ') rest = rest[1..]; first = false; }}fn wrapLength(value: []const u8, width: usize) usize { if (value.len <= width) return value.len; var index = width; while (index > 0) : (index -= 1) { if (value[index] == ' ') return index; } return width;}fn writeTrimmedLine(writer: *std.Io.Writer, line: []const u8) !void { var end = line.len; while (end > 0 and line[end - 1] == ' ') end -= 1; try output.writeDocText(writer, line[0..end]); try output.writeDocHardline(writer);}test "scan text renders rich table oracle shape" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); const allocator = arena.allocator(); var stats = scan.Stats{ .files = 1, .nodes_added = 4, .nodes_removed = 4, .edges_added = 4, .skipped_edges = 1, }; try stats.lang_counts.append(allocator, .{ .name = "Python", .count = 1 }); try stats.skipped_edge_categories.append(allocator, .{ .name = "external_or_generated", .count = 1 }); try stats.skipped_edge_samples.append(allocator, .{ .source = "app", .rel = model.RelType.imports, .target = "os", .reason = "unresolved target", .category = "external_or_generated", }); const rendered = try render(allocator, stats, false); try std.testing.expectEqualStrings( \\Scanned 1 files (1 Python) \\Nodes +4 -4 \\Edges 4 \\Skipped 1 unresolved \\Skipped by kind 1 external_or_generated \\Hint: external_or_generated External or generated dependency; widen scan scope \\ only if this target is first-party code. \\Skipped edge examples hidden; use --full to show samples. \\ , rendered);}Audit
| Definitions | 2 |
|---|---|
| Public names | 3 |
| Members | 0 |
| Version | 26.7.0 |
| Revision | daab053ee433 |