From 5a0a048fba392303b2f938cd05fb11da208e5b87 Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Fri, 12 Jun 2026 16:19:28 -0700 Subject: [PATCH 1/3] feat: capture flag default values in help data Add a default: Option(String) field to help.Flag and populate it from each flag's configured default via flag_default_info (with a join_csv helper for list flags). Foundation for rendering defaults in --help and for the public document tree. --- CHANGELOG.md | 3 +++ src/glint.gleam | 24 ++++++++++++++++++++++++ src/glint/internal/help.gleam | 4 ++-- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 573dbc3..57335a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## [Unreleased](https://github.com/TanklesXL/glint/compare/v1.1.0...HEAD) +- captured each flag's configured default value in the help data + (`help.Flag.default`) + # v1 ## [1.3.0](https://github.com/TanklesXL/glint/compare/v1.2.1...v1.3.0) diff --git a/src/glint.gleam b/src/glint.gleam index 5ba3494..8968495 100644 --- a/src/glint.gleam +++ b/src/glint.gleam @@ -729,6 +729,29 @@ fn flag_type_info(flag: FlagEntry) { } } +fn flag_default_info(flag: FlagEntry) -> Option(String) { + case flag.value { + I(FlagInternals(value: Some(v), ..)) -> Some(int.to_string(v)) + F(FlagInternals(value: Some(v), ..)) -> Some(float.to_string(v)) + S(FlagInternals(value: Some(v), ..)) -> Some(v) + B(FlagInternals(value: Some(v), ..)) -> + Some(case v { + True -> "true" + False -> "false" + }) + LI(FlagInternals(value: Some(v), ..)) -> Some(join_csv(v, int.to_string)) + LF(FlagInternals(value: Some(v), ..)) -> Some(join_csv(v, float.to_string)) + LS(FlagInternals(value: Some(v), ..)) -> Some(string.join(v, ",")) + _ -> None + } +} + +/// stringify each item and join with commas, for list-flag defaults. +/// +fn join_csv(items: List(a), to_string: fn(a) -> String) -> String { + items |> list.map(to_string) |> string.join(",") +} + /// build the help representation for a list of flags /// fn build_flags_help(flags: Flags) -> List(help.Flag) { @@ -737,6 +760,7 @@ fn build_flags_help(flags: Flags) -> List(help.Flag) { help.Flag( meta: help.Metadata(name: name, description: flag.description), type_: flag_type_info(flag), + default: flag_default_info(flag), ), ..acc ] diff --git a/src/glint/internal/help.gleam b/src/glint/internal/help.gleam index f1ec672..0778f27 100644 --- a/src/glint/internal/help.gleam +++ b/src/glint/internal/help.gleam @@ -20,7 +20,7 @@ fn heading_style(heading: String, colour: Colour) -> String { // --- HELP: CONSTANTS --- // -pub const help_flag = Flag(Metadata("help", "Print help information"), "") +pub const help_flag = Flag(Metadata("help", "Print help information"), "", None) const flags_heading = "FLAGS:" @@ -61,7 +61,7 @@ pub type Metadata { /// Help type for flag metadata /// pub type Flag { - Flag(meta: Metadata, type_: String) + Flag(meta: Metadata, type_: String, default: Option(String)) } /// Help type for command metadata From f0aa6d7cae7282061036f9bb0d50b9d78669f0b9 Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Fri, 12 Jun 2026 16:21:01 -0700 Subject: [PATCH 2/3] feat: add show_flag_defaults to render defaults in --help Add the glint.show_flag_defaults builder and Config flag. When enabled, each flag with a configured default has (default: ) appended to its description in --help output. Disabled by default to preserve existing formatting. --- CHANGELOG.md | 3 +- src/glint.gleam | 12 ++++++++ src/glint/internal/help.gleam | 11 +++++++- test/glint/show_flag_defaults_test.gleam | 36 ++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 test/glint/show_flag_defaults_test.gleam diff --git a/CHANGELOG.md b/CHANGELOG.md index 57335a0..1dcb9cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,8 @@ ## [Unreleased](https://github.com/TanklesXL/glint/compare/v1.1.0...HEAD) - captured each flag's configured default value in the help data - (`help.Flag.default`) + (`help.Flag.default`) and added the `glint.show_flag_defaults` builder + to opt in to rendering `(default: )` in `--help` output # v1 diff --git a/src/glint.gleam b/src/glint.gleam index 8968495..cd8beab 100644 --- a/src/glint.gleam +++ b/src/glint.gleam @@ -29,6 +29,7 @@ type Config { max_output_width: Int, min_first_column_width: Int, column_gap: Int, + show_flag_defaults: Bool, ) } @@ -52,6 +53,7 @@ const default_config = Config( max_output_width: 80, min_first_column_width: 20, column_gap: 2, + show_flag_defaults: False, ) // -- CONFIGURATION: FUNCTIONS -- @@ -127,6 +129,15 @@ pub fn with_column_gap(glint: Glint(a), column_gap: Int) -> Glint(a) { Glint(..glint, config: Config(..glint.config, column_gap:)) } +/// Enable rendering of flag default values in `--help` output. When enabled, +/// each flag with a configured default has `(default: )` appended to +/// its description. +/// +/// Disabled by default to preserve existing help text formatting. +pub fn show_flag_defaults(glint: Glint(a), enabled: Bool) -> Glint(a) { + Glint(..glint, config: Config(..glint.config, show_flag_defaults: enabled)) +} + // --- CORE --- // -- CORE: TYPES -- @@ -682,6 +693,7 @@ fn build_help_config(config: Config) -> help.Config { column_gap: config.column_gap, flag_prefix: flag_prefix, flag_delimiter: flag_delimiter, + show_flag_defaults: config.show_flag_defaults, ) } diff --git a/src/glint/internal/help.gleam b/src/glint/internal/help.gleam index 0778f27..c8a5015 100644 --- a/src/glint/internal/help.gleam +++ b/src/glint/internal/help.gleam @@ -49,6 +49,7 @@ pub type Config { column_gap: Int, flag_prefix: String, flag_delimiter: String, + show_flag_defaults: Bool, ) } @@ -208,7 +209,15 @@ fn flags_help_to_string(help: List(Flag), config: Config) -> String { let content = to_spaced_indented_string( [help_flag, ..help], - fn(help) { #(flag_help_to_string(help, config), help.meta.description) }, + fn(help) { + let description = case config.show_flag_defaults, help.default { + True, Some(default) -> + help.meta.description <> " (default: " <> default <> ")" + _, _ -> help.meta.description + } + + #(flag_help_to_string(help, config), description) + }, longest_flag_length, config, ) diff --git a/test/glint/show_flag_defaults_test.gleam b/test/glint/show_flag_defaults_test.gleam new file mode 100644 index 0000000..da1e439 --- /dev/null +++ b/test/glint/show_flag_defaults_test.gleam @@ -0,0 +1,36 @@ +import gleam/string +import gleeunit/should +import glint.{Help} + +fn cli(show_defaults: Bool) -> glint.Glint(Nil) { + let count = + glint.int_flag("count") + |> glint.flag_default(3) + |> glint.flag_help("How many times") + + glint.new() + |> glint.show_flag_defaults(show_defaults) + |> glint.add(at: [], do: { + use _count <- glint.flag(count) + glint.command(fn(_, _, _) { Nil }) + }) +} + +fn help_text(g: glint.Glint(Nil)) -> String { + let assert Ok(Help(help)) = glint.execute(g, ["--help"]) + help +} + +pub fn enabled_renders_default_test() { + cli(True) + |> help_text + |> string.contains("(default: 3)") + |> should.be_true +} + +pub fn disabled_omits_default_test() { + cli(False) + |> help_text + |> string.contains("(default:") + |> should.be_false +} From eb0d1c8e2bd139d32742d56633ad81adeb544a6b Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Fri, 12 Jun 2026 16:46:01 -0700 Subject: [PATCH 3/3] refactor: extract shared help types into public glint/help module Introduce glint/help with Metadata, Flag, and ArgsCount as the single source of truth; internal/help imports them instead of redeclaring. Extract a to_help_args helper and route the help/flag builders through the public types. Pure refactor: no behavior change. --- src/glint.gleam | 36 ++++++++++++++++++++--------------- src/glint/help.gleam | 30 +++++++++++++++++++++++++++++ src/glint/internal/help.gleam | 20 +++---------------- 3 files changed, 54 insertions(+), 32 deletions(-) create mode 100644 src/glint/help.gleam diff --git a/src/glint.gleam b/src/glint.gleam index cd8beab..7d2257c 100644 --- a/src/glint.gleam +++ b/src/glint.gleam @@ -9,6 +9,7 @@ import gleam/result import gleam/string import gleam_community/colour.{type Colour} import glint/constraint +import glint/help as pub_help import glint/internal/help import snag.{type Snag} @@ -705,7 +706,7 @@ fn build_command_help(name: String, node: CommandNode(_)) -> help.Command { |> option.map(fn(cmd) { #( node.description, - build_flags_help(merge(node.group_flags, cmd.flags)), + build_flags(merge(node.group_flags, cmd.flags)), cmd.unnamed_args, cmd.named_args, ) @@ -713,20 +714,24 @@ fn build_command_help(name: String, node: CommandNode(_)) -> help.Command { |> option.unwrap(#(node.description, [], None, [])) help.Command( - meta: help.Metadata(name: name, description: description), + meta: pub_help.Metadata(name: name, description: description), flags: flags, subcommands: build_subcommands_help(node.subcommands), - unnamed_args: { - use args <- option.map(unnamed_args) - case args { - EqArgs(n) -> help.EqArgs(n) - MinArgs(n) -> help.MinArgs(n) - } - }, + unnamed_args: to_help_args(unnamed_args), named_args: named_args, ) } +/// remap an internal `ArgsCount` to the public `help.ArgsCount`. +/// +fn to_help_args(args: Option(ArgsCount)) -> Option(pub_help.ArgsCount) { + use args <- option.map(args) + case args { + EqArgs(n) -> pub_help.EqArgs(n) + MinArgs(n) -> pub_help.MinArgs(n) + } +} + /// generate the string representation for the type of a flag /// fn flag_type_info(flag: FlagEntry) { @@ -764,13 +769,14 @@ fn join_csv(items: List(a), to_string: fn(a) -> String) -> String { items |> list.map(to_string) |> string.join(",") } -/// build the help representation for a list of flags +/// build the public flag representation for a list of flags. +/// Shared by both `--help` rendering and the `document` doc tree. /// -fn build_flags_help(flags: Flags) -> List(help.Flag) { +fn build_flags(flags: Flags) -> List(pub_help.Flag) { use acc, name, flag <- fold(flags, []) [ - help.Flag( - meta: help.Metadata(name: name, description: flag.description), + pub_help.Flag( + meta: pub_help.Metadata(name: name, description: flag.description), type_: flag_type_info(flag), default: flag_default_info(flag), ), @@ -782,9 +788,9 @@ fn build_flags_help(flags: Flags) -> List(help.Flag) { /// fn build_subcommands_help( subcommands: dict.Dict(String, CommandNode(_)), -) -> List(help.Metadata) { +) -> List(pub_help.Metadata) { use acc, name, node <- dict.fold(subcommands, []) - [help.Metadata(name: name, description: node.description), ..acc] + [pub_help.Metadata(name: name, description: node.description), ..acc] } // ----- FLAGS ----- diff --git a/src/glint/help.gleam b/src/glint/help.gleam new file mode 100644 index 0000000..926d48f --- /dev/null +++ b/src/glint/help.gleam @@ -0,0 +1,30 @@ +//// Stable, public introspection API for glint command trees. +//// +//// This module exposes the shared public help types (`Metadata`, `Flag`, +//// and `ArgsCount`) used when rendering help text and, in future, when +//// auto-generating reference documentation from a command tree. + +import gleam/option.{type Option} + +/// Metadata shared by commands and flags: the `name` used in usage text and +/// headings, plus a human-readable `description`. +/// +/// Re-declared as a fresh public type (rather than aliasing +/// `glint/internal/help.Metadata`) so downstream tools can both read and +/// construct `Metadata` values without importing `glint/internal/help`. +pub type Metadata { + Metadata(name: String, description: String) +} + +/// Number of unnamed positional arguments accepted by a command. +/// +/// Re-declared (rather than aliased) so that the `EqArgs` and `MinArgs` +/// constructors are accessible without importing `glint/internal/help`. +pub type ArgsCount { + EqArgs(Int) + MinArgs(Int) +} + +pub type Flag { + Flag(meta: Metadata, type_: String, default: Option(String)) +} diff --git a/src/glint/internal/help.gleam b/src/glint/internal/help.gleam index c8a5015..57eda92 100644 --- a/src/glint/internal/help.gleam +++ b/src/glint/internal/help.gleam @@ -5,6 +5,9 @@ import gleam/option.{type Option, None, Some} import gleam/string import gleam_community/ansi import gleam_community/colour.{type Colour} +import glint/help.{ + type ArgsCount, type Flag, type Metadata, EqArgs, Flag, Metadata, MinArgs, +} import glint/internal/utils /// Style heading text with the provided rgb colouring @@ -30,11 +33,6 @@ const usage_heading = "USAGE:" // --- HELP: TYPES --- -pub type ArgsCount { - MinArgs(Int) - EqArgs(Int) -} - pub type Config { Config( name: Option(String), @@ -53,18 +51,6 @@ pub type Config { ) } -/// Common metadata for commands and flags -/// -pub type Metadata { - Metadata(name: String, description: String) -} - -/// Help type for flag metadata -/// -pub type Flag { - Flag(meta: Metadata, type_: String, default: Option(String)) -} - /// Help type for command metadata pub type Command { Command(