Skip to content
This repository has been archived by the owner on Feb 24, 2022. It is now read-only.

Update to zig 0.7.1 #4

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# ztags
ctags implementation for Zig written in Zig
ctags implementation for Zig written in Zig 0.7.1

## Vim Tagbar Configuration

Add this to your `.vimrc` (fixing `ctagsbin` accordingly):

```
```vimscript
let g:tagbar_type_zig = {
\ 'ctagstype' : 'zig',
\ 'kinds' : [
Expand Down
21 changes: 19 additions & 2 deletions build.zig
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
const Builder = @import("std").build.Builder;

pub fn build(b: *Builder) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{});

// Standard release options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardReleaseOptions();

const exe = b.addExecutable("ztags", "src/main.zig");
exe.setTarget(target);
exe.setBuildMode(mode);
exe.install();

const run_cmd = exe.run();
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
}

b.default_step.dependOn(&exe.step);
b.installArtifact(exe);
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
}
7 changes: 7 additions & 0 deletions src/helper.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/sh
#generated by ztags
if [ -z "$1" ]; then
echo "Usage: $0 FILE(s)"
else
echo '!_TAG_FILE_SORTED 1' > tags && {s} $@ | LC_ALL=C sort >> tags
fi
213 changes: 115 additions & 98 deletions src/main.zig
Original file line number Diff line number Diff line change
@@ -1,29 +1,37 @@
const std = @import("std");

fn tagKind(tree: *std.zig.ast.Tree, node: *std.zig.ast.Node) u8 {
return switch (node.id) {
std.zig.ast.Node.Id.FnProto => 'f',
std.zig.ast.Node.Id.VarDecl => blk: {
const NTag = std.zig.ast.Node.Tag;
return switch (node.tag) {
NTag.FnProto => 'f',
NTag.VarDecl => {
const var_decl_node = node.cast(std.zig.ast.Node.VarDecl).?;
if (var_decl_node.init_node) |init_node| {
if (init_node.id == std.zig.ast.Node.Id.ContainerDecl) {
if (var_decl_node.getInitNode()) |init_node| {
if (init_node.tag == NTag.ContainerDecl) {
const container_node = init_node.cast(std.zig.ast.Node.ContainerDecl).?;
break :blk switch (tree.tokens.at(container_node.kind_token).id) {
return switch (tree.token_ids[container_node.kind_token]) {
std.zig.Token.Id.Keyword_struct => 's',
std.zig.Token.Id.Keyword_union => 'u',
std.zig.Token.Id.Keyword_enum => 'e',
else => u8(0),
std.zig.Token.Id.Keyword_enum => 'g',
else => @as(u8, 0),
};
} else if (init_node.id == std.zig.ast.Node.Id.ErrorType or
init_node.id == std.zig.ast.Node.Id.ErrorSetDecl)
{
} else if (init_node.tag == NTag.ErrorType or init_node.tag == NTag.ErrorSetDecl) {
return 'r';
}
}
break :blk 'v';
return 'v';
},
std.zig.ast.Node.Id.StructField, std.zig.ast.Node.Id.UnionTag, std.zig.ast.Node.Id.EnumTag => 'm',
else => u8(0),
NTag.ContainerField => {
const member_decl_node = node.castTag(NTag.ContainerField).?;
// hacky but enumerated types do not allow for type expressions while union/structs require
// this just happens to be an easy check if the container field is a enum or otherwise (assuming valid code).
if (member_decl_node.type_expr == null) {
return 'e';
} else {
return 'm';
}
},
else => @as(u8, 0),
};
}

Expand Down Expand Up @@ -51,73 +59,52 @@ const ErrorSet = error{
WriteError,
};

const ParseArgs = struct {
fn findTags(
allocator: *std.mem.Allocator,
tree: *std.zig.ast.Tree,
node: *std.zig.ast.Node,
path: []const u8,
scope_field_name: []const u8,
scope: []const u8,
tags_file_stream: *std.os.File.OutStream.Stream,
};

fn findTags(args: *const ParseArgs) ErrorSet!void {
tags_file_stream: anytype,
) ErrorSet!void {
var token_index: ?std.zig.ast.TokenIndex = null;
switch (args.node.id) {
std.zig.ast.Node.Id.StructField => {
const struct_field = args.node.cast(std.zig.ast.Node.StructField).?;
token_index = struct_field.name_token;
},
std.zig.ast.Node.Id.UnionTag => {
const union_tag = args.node.cast(std.zig.ast.Node.UnionTag).?;
token_index = union_tag.name_token;
const NTag = std.zig.ast.Node.Tag;
switch (node.tag) {
NTag.ContainerField => {
const container_field = node.castTag(NTag.ContainerField).?;
token_index = container_field.name_token;
},
std.zig.ast.Node.Id.EnumTag => {
const enum_tag = args.node.cast(std.zig.ast.Node.EnumTag).?;
token_index = enum_tag.name_token;
},
std.zig.ast.Node.Id.FnProto => {
const fn_node = args.node.cast(std.zig.ast.Node.FnProto).?;
if (fn_node.name_token) |name_index| {
NTag.FnProto => {
const fn_node = node.castTag(NTag.FnProto).?;
if (fn_node.getNameToken()) |name_index| {
token_index = name_index;
}
},
std.zig.ast.Node.Id.VarDecl => blk: {
const var_node = args.node.cast(std.zig.ast.Node.VarDecl).?;
NTag.VarDecl => {
const var_node = node.castTag(NTag.VarDecl).?;
token_index = var_node.name_token;

if (var_node.init_node) |init_node| {
if (init_node.id == std.zig.ast.Node.Id.ContainerDecl) {
if (var_node.getInitNode()) |init_node| {
if (init_node.tag == NTag.ContainerDecl) {
const container_node = init_node.cast(std.zig.ast.Node.ContainerDecl).?;
const container_kind = args.tree.tokenSlice(container_node.kind_token);
const container_name = args.tree.tokenSlice(token_index.?);
const container_kind = tree.tokenSlice(container_node.kind_token);
const container_name = tree.tokenSlice(token_index.?);
const delim = ".";
var sub_scope: []u8 = undefined;
if (args.scope.len > 0) {
sub_scope = try args.allocator.alloc(u8, args.scope.len + delim.len + container_name.len);
std.mem.copy(u8, sub_scope[0..args.scope.len], args.scope);
std.mem.copy(u8, sub_scope[args.scope.len .. args.scope.len + delim.len], delim);
std.mem.copy(u8, sub_scope[args.scope.len + delim.len ..], container_name);
if (scope.len > 0) {
sub_scope = try allocator.alloc(u8, scope.len + delim.len + container_name.len);
std.mem.copy(u8, sub_scope[0..scope.len], scope);
std.mem.copy(u8, sub_scope[scope.len .. scope.len + delim.len], delim);
std.mem.copy(u8, sub_scope[scope.len + delim.len ..], container_name);
} else {
sub_scope = try std.mem.dupe(args.allocator, u8, container_name);
sub_scope = try std.mem.dupe(allocator, u8, container_name);
}
defer args.allocator.free(sub_scope);
var it = container_node.fields_and_decls.iterator(0);
while (it.next()) |child| {
const child_args = ParseArgs{
.allocator = args.allocator,
.tree = args.tree,
.node = child.*,
.path = args.path,
.scope_field_name = container_kind,
.scope = sub_scope,
.tags_file_stream = args.tags_file_stream,
};
try findTags(&child_args);
defer allocator.free(sub_scope);
for (container_node.fieldsAndDecls()) |child| {
try findTags(allocator, tree, child, path, container_kind, sub_scope, tags_file_stream);
}
} else if (init_node.id == std.zig.ast.Node.Id.ErrorSetDecl or
init_node.id == std.zig.ast.Node.Id.ErrorType)
{}
}
}
},
else => {},
Expand All @@ -127,46 +114,76 @@ fn findTags(args: *const ParseArgs) ErrorSet!void {
return;
}

const name = args.tree.tokenSlice(token_index.?);
const location = args.tree.tokenLocation(0, token_index.?);
const line = args.tree.source[location.line_start..location.line_end];
const escaped_line = try escapeString(args.allocator, line);
defer args.allocator.free(escaped_line);
const name = tree.tokenSlice(token_index.?);
const location = tree.tokenLocation(0, token_index.?);
const line = tree.source[location.line_start..location.line_end];
const escaped_line = try escapeString(allocator, line);
defer allocator.free(escaped_line);

tags_file_stream.print("{s}\t{s}\t/^{s}$/;\"\t{c}", .{
name,
path,
escaped_line,
tagKind(tree, node),
}) catch return ErrorSet.WriteError;

args.tags_file_stream.print("{}\t{}\t/^{}$/;\"\t{c}", name, args.path, escaped_line, tagKind(args.tree, args.node)) catch return ErrorSet.WriteError;
if (args.scope.len > 0) {
args.tags_file_stream.print("\t{}:{}", args.scope_field_name, args.scope) catch return ErrorSet.WriteError;
if (scope.len > 0) {
tags_file_stream.print("\t{s}:{s}", .{ scope_field_name, scope }) catch return ErrorSet.WriteError;
}
args.tags_file_stream.print("\n") catch return ErrorSet.WriteError;
tags_file_stream.print("\n", .{}) catch return ErrorSet.WriteError;
}

pub fn main() !void {
var direct_allocator = std.heap.DirectAllocator.init();
defer direct_allocator.deinit();
const allocator = &direct_allocator.allocator;
var args_it = std.os.args();
_ = args_it.skip(); // Discard program name
const path = try args_it.next(allocator).?;
defer allocator.free(path);
const source = try std.io.readFileAlloc(allocator, path);
defer allocator.free(source);
var tree = try std.zig.parse(allocator, source);
defer tree.deinit();

var stdout_file = try std.io.getStdOut();
const stdout = &stdout_file.outStream().stream;
const node = &tree.root_node.base;
var child_i: usize = 0;
while (node.iterate(child_i)) |child| : (child_i += 1) {
const child_args = ParseArgs{
.allocator = allocator,
.tree = &tree,
.node = child,
.path = path,
.scope_field_name = "",
.scope = "",
.tags_file_stream = stdout,
pub fn main() !u8 {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();

const allocator = &gpa.allocator;
var args_it = std.process.args();

// storing program name for bash sort helper script
const program_name = try args_it.next(allocator) orelse unreachable;
defer allocator.free(program_name);

var stdout = std.io.getStdOut().writer();

var parsed_files: usize = 0;
while (args_it.next(allocator)) |try_path| {
const path = try try_path;
defer allocator.free(path);

const source = std.fs.cwd().readFileAlloc(allocator, path, std.math.maxInt(usize)) catch |err| {
switch (err) {
error.IsDir => {
std.debug.warn("Input '{s}' is a directory, skipping...\n", .{path});
continue;
},
error.FileNotFound => {
std.debug.warn("Input '{s}' not found, skipping...\n", .{path});
continue;
},
else => {
return err;
},
}
};
try findTags(&child_args);
defer allocator.free(source);

var tree = try std.zig.parse(allocator, source);
defer tree.deinit();

const node = &tree.root_node.base;
var child_i: usize = 0;
while (node.iterate(child_i)) |child| : (child_i += 1) {
try findTags(allocator, tree, child, path, "", "", stdout);
}
parsed_files += 1;
}

if (parsed_files == 0) {
std.debug.warn("Usage: ztags FILE(s)\n", .{});
std.debug.warn("\nTo sort and speed up large tag files you may want to use the following pipe-able bash script to generate a tags file\n", .{});
try stdout.print(@embedFile("helper.sh"), .{program_name});
return 1;
}
return 0;
}