Skip to content

Commit

Permalink
Implemented KDL Argument Template Gen
Browse files Browse the repository at this point in the history
- Implemented the KDL template generator based on @jdx's spec found [here](https://sr.ht/~jdx/usage/).
- #41.
  • Loading branch information
00JCIV00 committed Feb 12, 2024
1 parent 751418d commit 0b8f65f
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 10 deletions.
2 changes: 1 addition & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ pub fn build(b: *std.Build) void {
cova_mod,
&cova_demo.root_module,
.{
.kinds = &.{ .manpages, .bash },
.kinds = &.{ .all },
.manpages_config = .{
.local_filepath = "meta/manpages",
.version = "0.10.0",
Expand Down
140 changes: 136 additions & 4 deletions src/generate/arg_template.zig
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ pub fn OptionTemplate(OptionT: type) type {

/// Option Name
name: []const u8,
/// Option Long Name
long_name: ?[]const u8,
/// Option Short Name
short_name: ?u8,
/// Option Description
description: []const u8,
/// Option Aliases
Expand All @@ -93,6 +97,8 @@ pub fn OptionTemplate(OptionT: type) type {
pub fn from(comptime opt: OptionT) @This() {
return .{
.name = opt.name,
.long_name = opt.long_name,
.short_name = opt.short_name,
.description = opt.description,
.aliases = opt.alias_long_names,
.group = opt.opt_group,
Expand Down Expand Up @@ -215,9 +221,15 @@ pub fn createArgTemplate(
};
const cmd_template = CommandTemplate(CommandT).from(cmd, at_config);

const at_ctx = ArgTemplateContext{
.include_cmds = at_config.include_cmds,
.include_opts = at_config.include_opts,
.include_vals = at_config.include_vals,
};

switch (at_kind) {
.json => {
const json_opts_config = json.StringifyOptions{
const json_opts_config = json.StringifyOptions{
.whitespace = .indent_4,
.emit_null_optional_fields = false,
};
Expand All @@ -231,12 +243,132 @@ pub fn createArgTemplate(
try json.stringify(
cmd_template,
json_opts_config,
at_writer
at_writer,
);
},
.kdl => {
try at_writer.print("The KDL format generator is not yet available. When it is, it will be formatted to match the `usage` tool: https://sr.ht/~jdx/usage/", .{});
try at_writer.print("# This KDL template is formatted to match the `usage` tool as detailed here: https://sr.ht/~jdx/usage/\n\n", .{});
try at_writer.print(
\\name "{s}"
\\bin "{s}"
\\about "{s}"
\\
, .{
at_name,
at_name,
at_description,
}
);
if (at_config.version) |ver| try at_writer.print("version \"{s}\"\n", .{ ver });
if (at_config.author) |author| try at_writer.print("author \"{s}\"\n", .{ author });
try at_writer.print("\n", .{});
try argTemplateKDL(
CommandT,
cmd,
at_writer,
at_ctx,
);
},
}
log.info("Generated '{s}' Argument Template for '{s}' into '{s}'.", .{ @tagName(at_kind), cmd.name, filepath });
log.info("Generated '{s}' Argument Template for '{s}' into '{s}'.", .{
@tagName(at_kind),
cmd.name,
filepath,
});
}

pub const ArgTemplateContext = struct{
/// Argument Index
idx: u8 = 0,
/// Add a spacer line
add_line: bool = false,

/// Include Commands for Argument Templates.
include_cmds: bool = true,
/// Include Options for Argument Templates.
include_opts: bool = true,
/// Include Values for Argument Templates.
include_vals: bool = true,
};

/// Writes a Argument Template in the KDL for the provided CommandT (`cmd`) to the given Writer (`at_writer`).
/// This function passes the provided ArgumentTemplateContext (`at_ctx`) to track info through recursive calls.
fn argTemplateKDL(
comptime CommandT: type,
comptime cmd: CommandT,
at_writer: anytype,
comptime at_ctx: ArgTemplateContext,
) !void {
const sub_args: bool = (
(at_ctx.include_cmds and cmd.sub_cmds != null) or
(at_ctx.include_opts and cmd.opts != null) or
(at_ctx.include_vals and cmd.vals != null) or
cmd.alias_names != null
);
// if (sub_args and at_ctx.add_line) try at_writer.print("\n", .{});
if (at_ctx.add_line) try at_writer.print("\n", .{});
const indent = if (at_ctx.idx > 1) " " ** (at_ctx.idx - 1) else "";
const sub_indent = if (at_ctx.idx > 0) " " ** (at_ctx.idx) else "";
if (at_ctx.idx > 0) {
try at_writer.print("{s}cmd \"{s}\" help=\"{s}\"{s}\n", .{
indent,
cmd.name,
cmd.description,
if (sub_args) " {" else "",
});
}

var add_line = false;

if (cmd.alias_names) |aliases| addAliases: {
if (at_ctx.idx == 0) break :addAliases;
inline for (aliases) |alias| try at_writer.print("{s}alias \"{s}\"\n", .{ sub_indent, alias });
add_line = true;
}

if (at_ctx.include_opts) addOpts: {
const opts = cmd.opts orelse {
add_line = false;
break :addOpts;
};
if (add_line) try at_writer.print("\n", .{});
// TODO Better handling of prefixes. Check if the usage tool supports alternate prefixes.
inline for (opts) |opt| try at_writer.print("{s}flag \"{s}{s}\" help=\"{s}\"\n", .{
sub_indent,
if (opt.short_name) |short| fmt.comptimePrint("-{c},", .{ short }) else "",
if (opt.long_name) |long| fmt.comptimePrint("--{s}", .{ long }) else "",
opt.description,
});
add_line = true;
}

if (at_ctx.include_vals) addVals: {
const vals = cmd.vals orelse {
add_line = false;
break :addVals;
};
if (add_line) try at_writer.print("\n", .{});
inline for (vals) |val| try at_writer.print("{s}arg \"{s}\" help=\"{s}\"\n", .{
sub_indent,
val.name(),
val.description(),
});
add_line = true;
}

if (at_ctx.include_cmds) addCmds: {
const sub_cmds = cmd.sub_cmds orelse {
add_line = false;
break :addCmds;
};
if (add_line) try at_writer.print("\n", .{});
inline for (sub_cmds, 0..) |sub_cmd, idx| {
comptime var sub_ctx = at_ctx;
sub_ctx.idx += 1;
sub_ctx.add_line = idx > 0;
try argTemplateKDL(CommandT, sub_cmd, at_writer, sub_ctx);
}
}

if (at_ctx.idx > 0 and sub_args) try at_writer.print("{s}}}\n", .{ indent });
}
7 changes: 6 additions & 1 deletion src/generate/manpages.zig
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,12 @@ const ManpageContext = struct {
cur_depth: u8 = 0,
};
/// Create a manpage with Context (`mp_ctx`).
fn createManpageCtx(comptime CommandT: type, comptime cmd: CommandT, comptime mp_config: ManpageConfig, comptime mp_ctx: ManpageContext) !void {
fn createManpageCtx(
comptime CommandT: type,
comptime cmd: CommandT,
comptime mp_config: ManpageConfig,
comptime mp_ctx: ManpageContext
) !void {
//log.info("Generating Manpages for '{s}'...", .{ cmd.name });
const mp_name = mp_ctx.name;
const mp_description = mp_config.description orelse cmd.description;
Expand Down
21 changes: 18 additions & 3 deletions src/generate/tab_completion.zig
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,12 @@ const TabCompletionContext = struct{

/// Writes a Bash Tab Completion script snippet for the provided CommandT (`cmd`) to the given Writer (`tc_writer`).
/// This function passes the provided TabCompletionContext (`tc_ctx`) to track info through recursive calls.
fn cmdTabCompletionBash(comptime CommandT: type, comptime cmd: CommandT, tc_writer: anytype, comptime tc_ctx: TabCompletionContext) !void {
fn cmdTabCompletionBash(
comptime CommandT: type,
comptime cmd: CommandT,
tc_writer: anytype,
comptime tc_ctx: TabCompletionContext,
) !void {
// Get Sub Commands and Options
const long_pf = CommandT.OptionT.long_prefix orelse "";
const args_list: []const u8 = comptime genArgList: {
Expand Down Expand Up @@ -292,7 +297,12 @@ fn cmdTabCompletionBash(comptime CommandT: type, comptime cmd: CommandT, tc_writ

/// Writes a Zsh Tab Completion script snippet for the provided CommandT (`cmd`) to the given Writer (`tc_writer`).
/// This function passes the provided TabCompletionContext (`tc_ctx`) to track info through recursive calls.
fn cmdTabCompletionZsh(comptime CommandT: type, comptime cmd: CommandT, tc_writer: anytype, comptime tc_ctx: TabCompletionContext) !void {
fn cmdTabCompletionZsh(
comptime CommandT: type,
comptime cmd: CommandT,
tc_writer: anytype,
comptime tc_ctx: TabCompletionContext,
) !void {
// Get Sub Commands and Options
const long_pf = CommandT.OptionT.long_prefix orelse "";
const args_list: []const u8 = comptime genArgList: {
Expand Down Expand Up @@ -376,7 +386,12 @@ fn cmdTabCompletionZsh(comptime CommandT: type, comptime cmd: CommandT, tc_write

/// Writes a PowerShell Tab Completion script snippet for the provided CommandT (`cmd`) to the given Writer (`tc_writer`).
/// This function passes the provided TabCompletionContext (`tc_ctx`) to track info through recursive calls.
fn cmdTabCompletionPowerShell(comptime CommandT: type, comptime cmd: CommandT, tc_writer: anytype, comptime tc_ctx: TabCompletionContext) !void {
fn cmdTabCompletionPowerShell(
comptime CommandT: type,
comptime cmd: CommandT,
tc_writer: anytype,
comptime tc_ctx: TabCompletionContext
) !void {
// Get Sub Commands and Options
const long_pf = CommandT.OptionT.long_prefix orelse "";
const suggestions: []const u8 = comptime genSuggestions: {
Expand Down
2 changes: 1 addition & 1 deletion src/generator.zig
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const arg_template_config = optsToConf(generate.ArgTemplateConfig, @import("arg_

const meta_info: []const []const u8 = &.{
"version",
"ver_data",
"ver_date",
"name",
"description",
"author",
Expand Down

0 comments on commit 0b8f65f

Please sign in to comment.