Skip to documentation
SLOP

tiny.smg.command.node

Reference tiny.smg command node

Defined in command.

API (9)

Actions

Public operations.

No direct callersNo direct callscommandnode
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallsNo direct callerscli.optionsflagValuecli.optionshasJsoncli.optionspositionalcli.optionssignedLimitValuecli.optionsslicedLen+7 morecommand.nodelist
Static calls · unresolved targets: 0 · external targets: 10.
Called byCallscommand.nodelistcli.outputwriteOutcommand.noderenderListJsoncommand.nodelistJson
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callscommand.noderendercommand.noderenderListJsontest; no linktools.smg.src.command.nodetest: location includes end line when...command.nodewriteListTablecommand.nodelocation
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallscommand.nodeshowcommand.nodelocationcommand.nodewriteEdgeLinescommand.nodewriteMetadatacommand.noderender
Static calls · unresolved targets: 3 · external targets: 0.
Called byCallscommand.nodelistJsontest; no linktools.smg.src.command.nodetest: list json renders location and ...command.nodelocationcommand.noderenderListJson
Static calls · unresolved targets: 1 · external targets: 11.
Called byCallsNo direct callerscli.optionshasFlagcli.optionshasJsoncli.optionspositionalcli.outputwriteClickUsageErrorcli.outputwriteOut+5 morecommand.nodeshow
Static calls · unresolved targets: 0 · external targets: 8.
Called byCallsNo direct callscommand.noderendercommand.nodewriteEdgeLines
Static calls · unresolved targets: 1 · external targets: 0.
Called byCallscommand.nodelisttest; no linktools.smg.src.command.nodetest: list table renders sorted nodes...private sourcelib.markdown.src.tableappendRowcommand.nodelocationcommand.nodewriteListTable
Static calls · unresolved targets: 1 · external targets: 2.
Called byCallsNo direct callscommand.noderendertest; no linktools.smg.src.command.nodetest: metadata renders sorted for sho...command.nodewriteMetadata
Static calls · unresolved targets: 1 · external targets: 1.

Source: tools/smg/src/command/node.zig

zig
const std = @import("std");const pretty_json = @import("pretty").json;const smg = @import("../root.zig");const exports = smg.exports;const graph_mod = smg.graph;const model = smg.model;const options = smg.cli.options;const output = smg.cli.output;const resolve = smg.cli.resolve;const session = smg.command.session;const storage = smg.storage;const table = smg.cli.table;const validation = smg.cli.validation;const view = smg.view;pub fn list(allocator: std.mem.Allocator, _: std.mem.Allocator, args: []const []const u8, limits: smg.Limits, boundary: session.PhaseBoundary) !u8 {    if (try validation.rejectMissingOptionValues(allocator, args, &.{ "--type", "--format", "--limit" })) return 2;    if (try validation.rejectUnexpectedOptions(allocator, "list", "[OPTIONS]", args, &.{ "--type", "--format", "--json", "--limit", "--full" })) return 2;    if (try validation.rejectInvalidChoiceOption(allocator, "list", "[OPTIONS]", args, "--format", &.{ "text", "json" })) return 2;    if (try validation.rejectInvalidIntegerOption(allocator, "list", "[OPTIONS]", args, "--limit")) return 2;    const pos = try options.positional(allocator, args);    if (pos.len != 0) {        try output.writeClickUsageError(allocator, "list", "[OPTIONS]", try validation.unexpectedArgumentMessage(allocator, pos));        return 2;    }    const type_filter = options.flagValue(args, "--type");    const limit = options.signedLimitValue(args);    const root = try storage.requireRoot(allocator);    const listed = try storage.nodes.listed(        allocator,        root,        type_filter,        options.storageLimit(limit),        limits.storage,    );    defer storage.nodes.freeListed(allocator, listed);    boundary.seal();    defer boundary.beginTeardown();    const nodes = listed.nodes;    const total = listed.total;    const displayed = options.slicedLen(total, limit);    if (options.hasJson(args)) return try listJson(allocator, nodes, total, displayed, limit);    var out: std.Io.Writer.Allocating = .init(allocator);    if (nodes.len == 0) try out.writer.writeAll("No nodes.\n") else try writeListTable(allocator, &out.writer, nodes[0..displayed], total);    return try output.writeOut(out.written());}pub fn show(allocator: std.mem.Allocator, phases: std.mem.Allocator, args: []const []const u8, limits: smg.Limits, boundary: session.PhaseBoundary) !u8 {    const command = "show";    const signature = "[OPTIONS] NAME";    if (try validation.rejectMissingOptionValues(allocator, args, &.{"--format"})) return 2;    if (try validation.rejectUnexpectedOptionsPlain(allocator, command, signature, args, &.{ "--format", "--json", "--full" })) return 2;    if (try validation.rejectInvalidChoiceOption(allocator, command, signature, args, "--format", &.{ "text", "json" })) return 2;    const pos = try options.positional(allocator, args);    if (pos.len == 0) {        try output.writeClickUsageError(allocator, command, signature, "Missing argument 'NAME'.");        return 2;    }    if (pos.len > 1) {        try output.writeClickUsageError(allocator, command, signature, try validation.unexpectedArgumentMessage(allocator, pos[1..]));        return 2;    }    const loaded = try session.loadRead(allocator, phases, limits, boundary);    defer boundary.beginTeardown();    const graph = loaded[1];    const name = try resolve.graphNode(allocator, graph, pos[0], limits.suggestions);    const node = graph_mod.getNode(&graph, name) orelse return error.NodeNotFound;    const incoming = try view.incoming(graph, allocator, name, null);    const outgoing = try view.outgoing(graph, allocator, name, null);    if (options.hasJson(args)) return try output.writeOut(try exports.nodeJson(allocator, node, incoming, outgoing));    var out: std.Io.Writer.Allocating = .init(allocator);    try render(allocator, &out.writer, node, incoming, outgoing, options.hasFlag(args, "--full"));    return try output.writeOut(out.written());}pub fn location(allocator: std.mem.Allocator, node: model.Node) ![]const u8 {    const file = node.file orelse return "";    if (node.line) |line| {        if (node.end_line) |end_line| {            if (end_line != line) return try std.fmt.allocPrint(allocator, "{s}:{d}-{d}", .{ file, line, end_line });        }        return try std.fmt.allocPrint(allocator, "{s}:{d}", .{ file, line });    }    return file;}pub fn render(allocator: std.mem.Allocator, writer: *std.Io.Writer, node: model.Node, incoming: []const model.Edge, outgoing: []const model.Edge, full: bool) !void {    try writer.print("{s}  [{s}]\n", .{ node.name, node.type });    if (node.file) |file| {        _ = file;        try writer.print("file:  {s}\n", .{try location(allocator, node)});    }    if (node.docstring) |doc| try writer.print("doc:   {s}\n", .{doc});    try writeMetadata(allocator, writer, node.metadata);    const limit = if (full) @as(usize, 0) else @as(usize, 20);    if (incoming.len != 0) {        try writer.print("\nIncoming ({d})\n", .{incoming.len});        try writeEdgeLines(writer, incoming, limit);    }    if (outgoing.len != 0) {        try writer.print("\nOutgoing ({d})\n", .{outgoing.len});        try writeEdgeLines(writer, outgoing, limit);    }}pub fn writeMetadata(allocator: std.mem.Allocator, writer: *std.Io.Writer, pairs: []const model.Pair) !void {    const sorted = try allocator.dupe(model.Pair, pairs);    std.mem.sort(model.Pair, sorted, {}, metadataLess);    for (sorted) |pair| try writer.print("{s}:  {s}\n", .{ pair.key, pair.value });}fn metadataLess(_: void, a: model.Pair, b: model.Pair) bool {    return std.mem.lessThan(u8, a.key, b.key);}pub fn writeEdgeLines(writer: *std.Io.Writer, edges: []const model.Edge, limit: usize) !void {    for (edges, 0..) |edge, index| {        if (limit != 0 and index >= limit) {            try writer.print("  ... and {d} more\n", .{edges.len - limit});            break;        }        try writer.print("  {s} --{s}--> {s}\n", .{ edge.source, edge.rel, edge.target });    }}pub fn listJson(allocator: std.mem.Allocator, nodes: []const model.Node, total: usize, displayed: usize, limit: i64) !u8 {    return try output.writeOut(try renderListJson(allocator, nodes, total, displayed, limit));}pub fn renderListJson(allocator: std.mem.Allocator, nodes: []const model.Node, total: usize, displayed: usize, limit: i64) ![]const u8 {    var out: std.Io.Writer.Allocating = .init(allocator);    errdefer out.deinit();    var scratch_state = std.heap.ArenaAllocator.init(allocator);    defer scratch_state.deinit();    const scratch = scratch_state.allocator();    var writer = pretty_json.Writer.init(&out.writer, .minified);    try writer.beginObject();    try writer.objectField("rows");    try writer.beginArray();    for (nodes[0..displayed]) |item| {        try writer.beginObject();        try writer.objectField("type");        try writer.write(item.type);        try writer.objectField("name");        try writer.write(item.name);        try writer.objectField("file");        try writer.write(try location(scratch, item));        try writer.endObject();    }    try writer.endArray();    try writer.objectField("total");    try writer.write(total);    try writer.objectField("displayed");    try writer.write(displayed);    try writer.objectField("truncated");    try writer.write(displayed < total);    try writer.objectField("limit");    try writer.write(limit);    try writer.endObject();    try out.writer.writeByte('\n');    return try out.toOwnedSlice();}pub fn writeListTable(allocator: std.mem.Allocator, writer: *std.Io.Writer, nodes: []const model.Node, total: usize) !void {    const columns = [_]table.Column{        .{ .header = "type", .max_width = 12 },        .{ .header = "name", .max_width = 40 },        .{ .header = "file", .max_width = 40 },    };    var scratch_state = std.heap.ArenaAllocator.init(allocator);    defer scratch_state.deinit();    const scratch = scratch_state.allocator();    var rows: std.ArrayList([]const []const u8) = .empty;    for (nodes) |item| {        try table.appendRow(scratch, &rows, &.{            item.type,            item.name,            try location(scratch, item),        });    }    try table.write(allocator, writer, &columns, rows.items, total);}test "location includes end line when distinct" {    const allocator = std.testing.allocator;    const value = try location(allocator, .{ .name = "app.range", .type = model.NodeType.function, .file = "src/app.py", .line = 4, .end_line = 8 });    defer allocator.free(value);    try std.testing.expectEqualStrings("src/app.py:4-8", value);}test "metadata renders sorted for show text" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    const pairs = [_]model.Pair{        .{ .key = "source", .value = "manual" },        .{ .key = "metrics", .value = "{}" },    };    var out: std.Io.Writer.Allocating = .init(allocator);    try writeMetadata(allocator, &out.writer, &pairs);    try std.testing.expectEqualStrings(        \\metrics:  {}        \\source:  manual        \\    , out.written());}test "list table renders sorted nodes with source locations" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    const nodes = [_]model.Node{        .{ .name = "app", .type = model.NodeType.module, .file = "src/app.py" },        .{ .name = "app.helper", .type = model.NodeType.function, .file = "src/app.py", .line = 5 },        .{ .name = "app.main", .type = model.NodeType.function, .file = "src/app.py", .line = 1 },    };    var out: std.Io.Writer.Allocating = .init(allocator);    try writeListTable(allocator, &out.writer, &nodes, nodes.len);    try std.testing.expectEqualStrings(        \\type      name        file        \\--------  ----------  ------------        \\module    app         src/app.py        \\function  app.helper  src/app.py:5        \\function  app.main    src/app.py:1        \\    , out.written());}test "list json renders location and truncation fields" {    const allocator = std.testing.allocator;    const nodes = [_]model.Node{        .{ .name = "app", .type = model.NodeType.module, .file = "src/app.py" },        .{ .name = "app.helper", .type = model.NodeType.function, .file = "src/app.py", .line = 5 },    };    const rendered = try renderListJson(allocator, &nodes, 3, 2, 2);    defer allocator.free(rendered);    try std.testing.expectEqualStrings(        "{\"rows\":[{\"type\":\"module\",\"name\":\"app\",\"file\":\"src/app.py\"},{\"type\":\"function\",\"name\":\"app.helper\",\"file\":\"src/app.py:5\"}],\"total\":3,\"displayed\":2,\"truncated\":true,\"limit\":2}\n",        rendered,    );}

Source: tools/smg/src/command/root.zig:16

zig
pub const node = @import("node.zig");

Complete call list for command.node.list

12 direct calls.

Complete call list for command.node.show

10 direct calls.

Audit

Definitions10
Public names10
Members0
Version26.7.0
Revisiondaab053ee433