output: skip top level doc comments on exact/sub queries

the assumption is top level doc comments are of litte to no interest
if an exact/sub query is specified.

a side effect of this change is, if -s option is specified but no
identifier arg is provided, zdoc outputs only top level doc comments.
for example,

    zdoc -s std.log
0.10
alex 2 years ago
parent a7391145f4
commit 19b427b665

@ -7,7 +7,10 @@ usage:
the program searches source code for matching public identifiers,
printing found types and their doc comments to stdout.
the search is case-insensitive and non-exhaustive.
if -s option is specified, any identifier substring matches.
as a side effect, the -s with no identifier arg results in
printing out only top level doc comments.
for example, look up "hello" identifier in a project file:

@ -4,26 +4,29 @@ const Ast = std.zig.Ast;
/// Query specifies the kind of identifier matching to look for when search'ing.
pub const Query = union(enum) {
none, // never match
all, // always match
exact: []const u8, // exact match, case-insensitive
sub: []const u8, // substring match, case-insensitive
};
/// search parses the source code into std.zig.Ast and walks over top level declarations,
/// optionally matching against the query.
/// search parses the source code into std.zig.Ast, walks over top level declarations
/// optionally matching against the query, and prints results using ais.
///
/// results are printed using ais.
pub fn search(alloc: std.mem.Allocator, ais: *output.Ais, source: [:0]const u8, query: ?Query) !void {
/// a .none query matches no identifier; what's left is top level doc comments.
pub fn search(alloc: std.mem.Allocator, ais: *output.Ais, source: [:0]const u8, query: Query) !void {
var tree = try std.zig.parse(alloc, source);
defer tree.deinit(alloc);
try output.renderTopLevelDocComments(ais, tree);
// the assumption is top level doc comments are of litte to no interest
// if an exact/sub query is specified.
if (query == .none or query == .all) {
try output.renderTopLevelDocComments(ais, tree);
}
var insert_newline = false;
for (tree.rootDecls()) |decl| {
if (!isPublic(tree, decl)) {
continue;
}
if (query != null and !identifierMatch(tree, decl, query.?)) {
if (!isPublic(tree, decl) or !identifierMatch(tree, decl, query)) {
continue;
}
if (insert_newline) {
@ -93,14 +96,19 @@ pub fn isPublic(tree: Ast, decl: Ast.Node.Index) bool {
}
/// reports whether the given name matches decl identifier, case-insensitive.
pub fn identifierMatch(tree: Ast, decl: Ast.Node.Index, name: Query) bool {
if (identifier(tree, decl)) |id| {
switch (name) {
.exact => |exact| return std.ascii.eqlIgnoreCase(id, exact),
.sub => |sub| return std.ascii.indexOfIgnoreCase(id, sub) != null,
}
}
return false;
pub fn identifierMatch(tree: Ast, decl: Ast.Node.Index, q: Query) bool {
return switch (q) {
.none => false,
.all => true,
.exact => |qexact| exact: {
const id = identifier(tree, decl);
break :exact id != null and std.ascii.eqlIgnoreCase(id.?, qexact);
},
.sub => |qsub| sub: {
const id = identifier(tree, decl);
break :sub id != null and std.ascii.indexOfIgnoreCase(id.?, qsub) != null;
},
};
}
/// identifier returns node's identifier, if any.

@ -25,7 +25,7 @@ pub fn main() !void {
var sub: bool = false; // -s substr option
};
var nargs: u8 = 0; // excluding opts
var nargs: u8 = 0; // positional only, aka excluding opts
while (args.next()) |a| {
if (std.mem.eql(u8, a, "-s")) {
opts.sub = true;
@ -43,7 +43,7 @@ pub fn main() !void {
else => fatal("too many args", .{}),
}
}
if (nargs == 0) { // expected 1 or 2 args
if (nargs == 0) { // expected 1 or 2 positional args
usage(progname) catch {};
return;
}
@ -55,12 +55,18 @@ pub fn main() !void {
};
const ais = &auto_indenting_stream;
// run the search, one file at a time
var query: ?analyze.Query = null;
// null query always results in a none/all qualifiers:
// the logic is, -s flag implies a substring but identifiers are non-empty,
// so .none query makes most sense. see analyze.search for details.
var query: analyze.Query = undefined;
if (zquery) |q| switch (opts.sub) {
true => query = .{ .sub = q[0..] },
false => query = .{ .exact = q[0..] },
};
} else switch (opts.sub) {
true => query = .none,
false => query = .all,
}
// run the search, one file at a time
const list = try expandSourcePath(alloc, zsource);
for (list.items) |src| {
// no need to print the filename under search if there's only one.
@ -81,7 +87,10 @@ fn usage(prog: []const u8) !void {
\\the program searches source code for matching public identifiers,
\\printing found types and their doc comments to stdout.
\\the search is case-insensitive and non-exhaustive.
\\
\\if -s option is specified, any identifier substring matches.
\\as a side effect, the -s with no identifier arg results in
\\printing out only top level doc comments.
\\
\\for example, look up "hello" identifier in a project file:
\\

Loading…
Cancel
Save