From 77a875a37945819f7bb063f3429436fa2e206527 Mon Sep 17 00:00:00 2001 From: Robert Attard Date: Mon, 12 Aug 2024 17:05:51 -0400 Subject: [PATCH] new function-based ansi colouring for helptext headers --- README.md | 2 +- gleam.toml | 2 +- manifest.toml | 5 +- src/glint.gleam | 111 +++++++++++++++++++++++++++++++--- src/glint/internal/help.gleam | 34 ++--------- test/examples/hello.gleam | 2 +- test/glint_test.gleam | 1 + 7 files changed, 117 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 9fcf396..303d551 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ pub fn main() { // with an app name of "hello", this is used when printing help text |> glint.with_name("hello") // with pretty help enabled, using the built-in colours - |> glint.pretty_help(glint.default_pretty_help()) + |> glint.with_default_header_style // with a root command that executes the `hello` function |> glint.add(at: [], do: hello()) // execute given arguments from stdin diff --git a/gleam.toml b/gleam.toml index 609cec5..e740b56 100644 --- a/gleam.toml +++ b/gleam.toml @@ -1,5 +1,5 @@ name = "glint" -version = "1.0.1" +version = "1.1.0-dev" # Fill out these fields if you intend to generate HTML documentation or publishname = "glint" # your project to the Hex package manager. diff --git a/manifest.toml b/manifest.toml index 415ccfa..1a86efe 100644 --- a/manifest.toml +++ b/manifest.toml @@ -3,10 +3,11 @@ packages = [ { name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" }, - { name = "birdie", version = "1.1.8", build_tools = ["gleam"], requirements = ["argv", "filepath", "glance", "gleam_community_ansi", "gleam_erlang", "gleam_stdlib", "justin", "rank", "simplifile", "trie_again"], otp_app = "birdie", source = "hex", outer_checksum = "D225C0A3035FCD73A88402925A903AAD3567A1515C9EAE8364F11C17AD1805BB" }, + { name = "birdie", version = "1.2.0", build_tools = ["gleam"], requirements = ["argv", "edit_distance", "filepath", "glance", "gleam_community_ansi", "gleam_erlang", "gleam_stdlib", "justin", "rank", "simplifile", "trie_again"], otp_app = "birdie", source = "hex", outer_checksum = "7EE6286C5660650143A5AC69E1D900DA1B6EAF3C6EBB7E50AC3DBF06511279CC" }, + { name = "edit_distance", version = "2.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "edit_distance", source = "hex", outer_checksum = "A1E485C69A70210223E46E63985FA1008B8B2DDA9848B7897469171B29020C05" }, { name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" }, { name = "glance", version = "0.11.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "glexer"], otp_app = "glance", source = "hex", outer_checksum = "8F3314D27773B7C3B9FB58D8C02C634290422CE531988C0394FA0DF8676B964D" }, - { name = "gleam_community_ansi", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "FE79E08BF97009729259B6357EC058315B6FBB916FAD1C2FF9355115FEB0D3A4" }, + { name = "gleam_community_ansi", version = "1.4.1", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "4CD513FC62523053E62ED7BAC2F36136EC17D6A8942728250A9A00A15E340E4B" }, { name = "gleam_community_colour", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "795964217EBEDB3DA656F5EB8F67D7AD22872EB95182042D3E7AFEF32D3FD2FE" }, { name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" }, { name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" }, diff --git a/src/glint.gleam b/src/glint.gleam index b87527b..f9df6a9 100644 --- a/src/glint.gleam +++ b/src/glint.gleam @@ -1,12 +1,14 @@ import gleam import gleam/dict import gleam/float +import gleam/function import gleam/int import gleam/io import gleam/list import gleam/option.{type Option, None, Some} import gleam/result import gleam/string +import gleam_community/ansi import gleam_community/colour.{type Colour} import glint/constraint import glint/internal/help @@ -20,7 +22,7 @@ import snag.{type Snag} /// type Config { Config( - pretty_help: Option(PrettyHelp), + pretty_help: HeaderFormat, name: Option(String), as_module: Bool, description: Option(String), @@ -32,8 +34,83 @@ type Config { ) } +type HeaderFormat { + HeaderFormat( + usage: fn(String) -> String, + flags: fn(String) -> String, + subcommands: fn(String) -> String, + ) +} + +/// Set the style for each of the glint helptext headers (usage, flags, subcommands). +/// +/// This function will likely be used with colouring functions from [gleam_community/ansi](https://hexdocs.pm/gleam_community_ansi/). +/// +/// When setting your own header styles, you may want to leverage the default formatting as provided by [`glint.default_header_format`](#default_header_format) +/// which can be composed witth other functions from [gleam_community/ansi](https://hexdocs.pm/gleam_community_ansi/).. +/// +/// +/// Example: +/// +/// ```gleam +/// glint.with_header_style( +/// glint, +/// usage: fn(s) { s |> glint.default_header_format |> ansi.cyan }, +/// flags: fn(s) { s |> glint.default_header_format |> ansi.magenta }, +/// subcommands: fn(s) { s |> glint.default_header_format |> ansi.yellow }, +/// ) +/// ``` +pub fn with_header_style( + glint: Glint(a), + usage usage: fn(String) -> String, + flags flags: fn(String) -> String, + subcommands subcommands: fn(String) -> String, +) -> Glint(a) { + Glint( + ..glint, + config: Config( + ..glint.config, + pretty_help: HeaderFormat( + usage: usage, + flags: flags, + subcommands: subcommands, + ), + ), + ) +} + +/// This function sets the glint help text header styles using glint's defaults. +/// +/// Each help text header is formatted as bold, italic and underline (see [`glint.default_header_format`](#default_header_format)). +/// +/// The default colours are ANSI compatible as follows: +/// - usage: cyan +/// - flags: pink +/// - subcommands: yellow +/// +pub fn with_default_header_style(glint: Glint(a)) -> Glint(a) { + with_header_style( + glint, + usage: fn(s) { s |> default_header_format |> ansi.cyan }, + flags: fn(s) { s |> default_header_format |> ansi.pink }, + subcommands: fn(s) { s |> default_header_format |> ansi.yellow }, + ) +} + +/// Style heading text as bold, underlined and italic. +/// +/// This function can be combined with other functions from [gleam_community/ansi](https://hexdocs.pm/gleam_community_ansi/) +/// +pub fn default_header_format(heading: String) -> String { + heading + |> ansi.bold + |> ansi.underline + |> ansi.italic +} + /// PrettyHelp defines the header colours to be used when styling help text /// +@deprecated("this type will be removed in an upcoming version of glint") pub type PrettyHelp { PrettyHelp(usage: Colour, flags: Colour, subcommands: Colour) } @@ -43,7 +120,11 @@ pub type PrettyHelp { /// default config /// const default_config = Config( - pretty_help: None, + pretty_help: HeaderFormat( + function.identity, + function.identity, + function.identity, + ), name: None, as_module: False, description: None, @@ -58,10 +139,25 @@ const default_config = Config( /// Enable custom colours for help text headers. /// -/// For a pre-made style, pass in [`glint.default_pretty_help`](#default_pretty_help) +/// For a pre-made style, pass in [`glint.with_default_header_style`](#with_default_header_style) instead. /// +@deprecated("use glint.with_header_style instead") pub fn pretty_help(glint: Glint(a), pretty: PrettyHelp) -> Glint(a) { - Glint(..glint, config: Config(..glint.config, pretty_help: Some(pretty))) + let style = fn(s: String, colour: colour.Colour) -> String { + s |> default_header_format |> ansi.colour(colour) + } + + Glint( + ..glint, + config: Config( + ..glint.config, + pretty_help: HeaderFormat( + usage: style(_, pretty.usage), + flags: style(_, pretty.flags), + subcommands: style(_, pretty.subcommands), + ), + ), + ) } /// Give the current glint application a name. @@ -623,6 +719,7 @@ pub fn run_and_handle( /// /// buttercup (r: 252, g: 226, b: 174) colour for subcommands /// +@deprecated("use glint.with_default_header_style instead") pub fn default_pretty_help() -> PrettyHelp { let assert Ok(usage_colour) = colour.from_rgb255(182, 255, 234) let assert Ok(flags_colour) = colour.from_rgb255(255, 175, 243) @@ -652,9 +749,9 @@ fn cmd_help(path: List(String), cmd: CommandNode(a), config: Config) -> String { fn build_help_config(config: Config) -> help.Config { help.Config( name: config.name, - usage_colour: option.map(config.pretty_help, fn(p) { p.usage }), - flags_colour: option.map(config.pretty_help, fn(p) { p.flags }), - subcommands_colour: option.map(config.pretty_help, fn(p) { p.subcommands }), + usage_colour: config.pretty_help.usage, + flags_colour: config.pretty_help.flags, + subcommands_colour: config.pretty_help.subcommands, as_module: config.as_module, description: config.description, indent_width: config.indent_width, diff --git a/src/glint/internal/help.gleam b/src/glint/internal/help.gleam index c9f8b03..c6ec557 100644 --- a/src/glint/internal/help.gleam +++ b/src/glint/internal/help.gleam @@ -3,21 +3,8 @@ import gleam/int import gleam/list import gleam/option.{type Option, None, Some} import gleam/string -import gleam_community/ansi -import gleam_community/colour.{type Colour} import glint/internal/utils -/// Style heading text with the provided rgb colouring -/// this is only intended for use within glint itself. -/// -fn heading_style(heading: String, colour: Colour) -> String { - heading - |> ansi.bold - |> ansi.underline - |> ansi.italic - |> ansi.hex(colour.to_rgb_hex(colour)) -} - // --- HELP: CONSTANTS --- // pub const help_flag = Flag(Metadata("help", "Print help information"), "") @@ -38,9 +25,9 @@ pub type ArgsCount { pub type Config { Config( name: Option(String), - usage_colour: Option(Colour), - flags_colour: Option(Colour), - subcommands_colour: Option(Colour), + usage_colour: fn(String) -> String, + flags_colour: fn(String) -> String, + subcommands_colour: fn(String) -> String, as_module: Bool, description: Option(String), indent_width: Int, @@ -175,10 +162,7 @@ fn command_help_to_usage_string(help: Command, config: Config) -> String { |> utils.wordwrap(max_usage_width) |> string.join("\n" <> string.repeat(" ", config.indent_width * 2)) - case config.usage_colour { - None -> usage_heading - Some(pretty) -> heading_style(usage_heading, pretty) - } + config.usage_colour(usage_heading) <> "\n" <> string.repeat(" ", config.indent_width) <> content @@ -197,10 +181,7 @@ fn flags_help_to_string(help: List(Flag), config: Config) -> String { |> utils.max_string_length |> int.max(config.min_first_column_width) - let heading = case config.flags_colour { - None -> flags_heading - Some(pretty) -> heading_style(flags_heading, pretty) - } + let heading = config.flags_colour(flags_heading) let content = to_spaced_indented_string( @@ -237,10 +218,7 @@ fn subcommands_help_to_string(help: List(Metadata), config: Config) -> String { |> utils.max_string_length |> int.max(config.min_first_column_width) - let heading = case config.subcommands_colour { - None -> subcommands_heading - Some(pretty) -> heading_style(subcommands_heading, pretty) - } + let heading = config.subcommands_colour(subcommands_heading) let content = to_spaced_indented_string( diff --git a/test/examples/hello.gleam b/test/examples/hello.gleam index b2af37d..cf4f1ea 100644 --- a/test/examples/hello.gleam +++ b/test/examples/hello.gleam @@ -275,7 +275,7 @@ pub fn app() { // show in usage text that the current app is run as a gleam module |> glint.as_module // with pretty help enabled, using the built-in colours - |> glint.pretty_help(glint.default_pretty_help()) + |> glint.with_default_header_style // with group level flags // with flag `caps` for all commands (equivalent of using glint.global_flag) |> glint.group_flag([], caps_flag()) diff --git a/test/glint_test.gleam b/test/glint_test.gleam index 403dbf8..c12cf9a 100644 --- a/test/glint_test.gleam +++ b/test/glint_test.gleam @@ -354,6 +354,7 @@ pub fn global_and_group_flags_test() { |> glint.execute(["sub", "sub", "--sub_group_flag=2"]) } +@deprecated("remove when the pretty help removal happens") pub fn default_pretty_help_test() { // default_pretty_help has asserts // we need to call the function to make sure it does not crash