diff --git a/internal/commands/help.go b/internal/commands/help.go index e5692bc..390b6b6 100644 --- a/internal/commands/help.go +++ b/internal/commands/help.go @@ -26,7 +26,11 @@ func cobraCommandFullName(c *cobra.Command) []string { var Help = &cobra.Command{ Use: _c.HelpCommandName + " [command]", Short: "Display usage information for any command", - Long: `Help provides the valid arguments and options for any command known to ` + runtime.Executable + `. By default, ﹅` + runtime.Executable + ` help﹅ will query the environment variable ﹅COLORFGBG﹅ to decide which style to use when rendering help, except if ﹅` + env.HelpUnstyled + `﹅ is set. Valid styles are: **light**, **dark**, and **auto**.`, + Long: `Help provides the valid arguments and options for any command known to ﹅@chinampa@﹅. + +## Colorized output + +By default, and unless ﹅` + env.NoColor + `﹅ is set, ﹅@chinampa@ help﹅ will query the environment variable ﹅COLORFGBG﹅ to decide which style to use when rendering help, unless if ﹅` + env.HelpStyle + `﹅ is set to any of the following values: **light**, **dark**, **markdown**, and **auto**. 24-bit color is available when ﹅COLORTERM﹅ is set to ﹅truecolor﹅.`, ValidArgsFunction: func(c *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { var completions []string cmd, _, e := c.Root().Find(args) @@ -47,6 +51,7 @@ var Help = &cobra.Command{ return completions, cobra.ShellCompDirectiveNoFileComp }, Run: func(c *cobra.Command, args []string) { + c.Long = strings.ReplaceAll(c.Long, "@chinampa@", runtime.Executable) if len(args) > 0 && c != nil && c.Name() != args[len(args)-1] { c, topicArgs, err := c.Root().Find(args) if err == nil && c != nil && len(topicArgs) == 0 { diff --git a/internal/constants/constants.go b/internal/constants/constants.go index dda49e2..2c7a89c 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -7,7 +7,10 @@ import ( _ "embed" ) +// HelpCommandName sets the name for the command that offers help. const HelpCommandName = "help" +// HelpTemplate is the markdown template to use when rendering help. +// //go:embed help.md var HelpTemplate string diff --git a/internal/constants/help.md b/internal/constants/help.md index 1bec266..4ff61fd 100644 --- a/internal/constants/help.md +++ b/internal/constants/help.md @@ -12,10 +12,14 @@ description: {{ .Command.Short }} `{{ replace .Command.UseLine " [flags]" "" }}{{if .Command.HasAvailableSubCommands}} SUBCOMMAND{{end}}` -{{ if and .Spec.IsRoot (not (eq .Command.Name "help")) }} +{{ if .Spec.IsRoot }} ## Description +{{ if eq .Command.Name "help" -}} +{{ .Command.Long }} +{{- else -}} {{ .Spec.Description }} +{{- end }} {{- if .Spec.HasAdditionalHelp }} {{ .Spec.AdditionalHelp .HTMLOutput }} {{ end -}} diff --git a/pkg/command/help.go b/pkg/command/help.go index 061bfe5..ff370cf 100644 --- a/pkg/command/help.go +++ b/pkg/command/help.go @@ -47,28 +47,18 @@ func (cmd *Command) HelpRenderer(globalOptions Options) func(cc *cobra.Command, func (cmd *Command) ShowHelp(globalOptions Options, args []string) ([]byte, error) { var buf bytes.Buffer + colorEnabled := runtime.ColorEnabled() c := &combinedCommand{ Spec: cmd, Command: cmd.Cobra, GlobalOptions: globalOptions, - HTMLOutput: runtime.UnstyledHelpEnabled(), + HTMLOutput: runtime.HelpStyle() == "markdown", } err := render.HelpTemplate(runtime.Executable).Execute(&buf, c) if err != nil { return nil, err } - colorEnabled := runtime.ColorEnabled() - flags := cmd.Cobra.Flags() - ncf := cmd.Cobra.Flag("no-color") // nolint:ifshort - cf := cmd.Cobra.Flag("color") // nolint:ifshort - - if noColorFlag, err := flags.GetBool("no-color"); err == nil && ncf.Changed { - colorEnabled = !noColorFlag - } else if colorFlag, err := flags.GetBool("color"); err == nil && cf.Changed { - colorEnabled = colorFlag - } - content, err := render.Markdown(buf.Bytes(), colorEnabled) if err != nil { return nil, err diff --git a/pkg/env/env.go b/pkg/env/env.go index 8d1d413..42e9d34 100644 --- a/pkg/env/env.go +++ b/pkg/env/env.go @@ -11,7 +11,6 @@ Package env holds environment variable names that are meant to be overridden by import "git.rob.mx/nidito/chinampa/env" func init() { - env.HelpUnstyled = "MY_APP_PLAIN_HELP" env.HelpStyle = "MY_APP_HELP_STYLE" env.Verbose = "MY_APP_VERBOSE" env.Silent = "MY_APP_SILENT" @@ -20,9 +19,6 @@ Package env holds environment variable names that are meant to be overridden by */ package env -// HelpUnstyled means help will not be colored nor formatted for a TTY. -var HelpUnstyled = "HELP_STYLE_PLAIN" - // HelpStyle identifies the theme to use for help formatting. var HelpStyle = "HELP_STYLE" diff --git a/pkg/render/render.go b/pkg/render/render.go index f98639f..cdfe580 100644 --- a/pkg/render/render.go +++ b/pkg/render/render.go @@ -10,7 +10,7 @@ import ( _c "git.rob.mx/nidito/chinampa/internal/constants" "git.rob.mx/nidito/chinampa/pkg/env" - "git.rob.mx/nidito/chinampa/pkg/runtime" + "git.rob.mx/nidito/chinampa/pkg/logger" "github.com/charmbracelet/glamour" "github.com/sirupsen/logrus" "golang.org/x/term" @@ -23,10 +23,34 @@ func addBackticks(str []byte) []byte { // Markdown renders markdown-formatted content to the tty. func Markdown(content []byte, withColor bool) ([]byte, error) { content = addBackticks(content) + var styleFunc glamour.TermRendererOption - if runtime.UnstyledHelpEnabled() { + style := os.Getenv(env.HelpStyle) + if style == "markdown" { + // markdown will render frontmatter along content and will not format for + // tty readability return content, nil } + if withColor { + switch style { + case "dark": + // For color TTYs with light text on dark background + styleFunc = glamour.WithStandardStyle("dark") + case "light": + // For color TTYs with dark text on light background + styleFunc = glamour.WithStandardStyle("light") + default: + // Glamour selects a style for the user. + styleFunc = glamour.WithStandardStyle("auto") + if style != "" { + logger.Warnf("Unknown %s=%s, assuming \"auto\"", env.HelpStyle, style) + } + } + } else { + // basically the same as the "markdown" style, except formatted for + // tty redability, prettifying and indenting, while not adding color. + styleFunc = glamour.WithStandardStyle("notty") + } width, _, err := term.GetSize(0) if err != nil { @@ -34,22 +58,6 @@ func Markdown(content []byte, withColor bool) ([]byte, error) { width = 80 } - var styleFunc glamour.TermRendererOption - - if withColor { - style := os.Getenv(env.HelpStyle) - switch style { - case "dark": - styleFunc = glamour.WithStandardStyle("dark") - case "light": - styleFunc = glamour.WithStandardStyle("light") - default: - styleFunc = glamour.WithStandardStyle("auto") - } - } else { - styleFunc = glamour.WithStandardStyle("notty") - } - renderer, err := glamour.NewTermRenderer( styleFunc, glamour.WithEmoji(), diff --git a/pkg/render/render_test.go b/pkg/render/render_test.go index 1b08668..a175ae9 100644 --- a/pkg/render/render_test.go +++ b/pkg/render/render_test.go @@ -14,7 +14,7 @@ import ( func TestMarkdownUnstyled(t *testing.T) { content := []byte("# hello") - os.Setenv(env.HelpUnstyled, "true") + os.Setenv(env.HelpStyle, "markdown") res, err := render.Markdown(content, false) if err != nil { @@ -28,7 +28,7 @@ func TestMarkdownUnstyled(t *testing.T) { } func TestMarkdownNoColor(t *testing.T) { - os.Unsetenv(env.HelpUnstyled) + os.Unsetenv(env.HelpStyle) content := []byte("# hello ﹅world﹅") res, err := render.Markdown(content, false) @@ -51,7 +51,7 @@ var autoStyleTestRender = "\n\x1b[38;5;228;48;5;63;1m\x1b[0m\x1b[38;5;228;48;5;6 const lightStyleTestRender = "\n\x1b[38;5;228;48;5;63;1m\x1b[0m\x1b[38;5;228;48;5;63;1m\x1b[0m \x1b[38;5;228;48;5;63;1m \x1b[0m\x1b[38;5;228;48;5;63;1mhello\x1b[0m\x1b[38;5;228;48;5;63;1m \x1b[0m\x1b[38;5;234m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[0m\n\x1b[0m\n" func TestMarkdownColor(t *testing.T) { - os.Unsetenv(env.HelpUnstyled) + os.Unsetenv(env.HelpStyle) content := []byte("# hello") styles := map[string][]byte{ diff --git a/pkg/runtime/runtime.go b/pkg/runtime/runtime.go index caafc9d..fbbd2a3 100644 --- a/pkg/runtime/runtime.go +++ b/pkg/runtime/runtime.go @@ -9,6 +9,7 @@ package runtime import ( "os" "strconv" + "strings" "git.rob.mx/nidito/chinampa/pkg/env" ) @@ -129,12 +130,12 @@ func ColorEnabled() bool { } // we're talking to ttys, we want color unless NO_COLOR/--no-color - return !(isTrueIsh(os.Getenv(env.NoColor)) || UnstyledHelpEnabled() || flagInArgs("no-color")) + return !(isTrueIsh(os.Getenv(env.NoColor)) || flagInArgs("no-color")) } -// UnstyledHelpEnabled tells if help should be printed without formatting. -func UnstyledHelpEnabled() bool { - return isTrueIsh(os.Getenv(env.HelpUnstyled)) +// HelpStyle returns the style to use when rendering help. +func HelpStyle() string { + return strings.ToLower(os.Getenv(env.HelpStyle)) } // EnvironmentMap returns a map of environment keys for color, debugging and verbosity and their values, ready for `os.Setenv`. diff --git a/pkg/runtime/runtime_test.go b/pkg/runtime/runtime_test.go index c322de8..b800e16 100644 --- a/pkg/runtime/runtime_test.go +++ b/pkg/runtime/runtime_test.go @@ -172,20 +172,11 @@ func TestEnabled(t *testing.T) { Name: env.NoColor, Func: ColorEnabled, }, - { - Name: env.HelpUnstyled, - Func: ColorEnabled, - }, { Name: env.Debug, Func: DebugEnabled, Expects: true, }, - { - Name: env.HelpUnstyled, - Func: UnstyledHelpEnabled, - Expects: true, - }, } for _, c := range cases {