Commit 71a910c7 authored by Mickaël Desfrênes's avatar Mickaël Desfrênes
Browse files

use standard help format

parent 29f296ff
Loading
Loading
Loading
Loading
+1 −7
Changes for cli.go: 1 added line, 7 removed lines.
Original line number Diff line number Diff line
@@ -43,11 +43,6 @@ func newRootCmd() *cobra.Command {
	cmd.AddCommand(newProjectsCmd())
	cmd.AddCommand(newCacheClearCmd())
	cmd.AddCommand(newVersionCmd())
	cmd.SetHelpFunc(renderHelp)
	cmd.SetUsageFunc(func(c *cobra.Command) error {
		renderHelp(c, []string{})
		return nil
	})

	return cmd
}
@@ -241,8 +236,7 @@ func newRunCmd() *cobra.Command {
				return err
			}
			if opts.help {
				renderHelp(cmd, []string{})
				return nil
				return cmd.Help()
			}
			return runBundleScript(opts.target, opts.directory, opts.scriptArgs)
		},
+40 −5
Changes for cli_commands_test.go: 40 added lines, 5 removed lines.
Original line number Diff line number Diff line
@@ -113,8 +113,8 @@ func TestCLICommandsWithRequiredArgsValidateArity(t *testing.T) {
	}
}

// Smoke-tests every help path so command construction, custom help rendering,
// and argument validators cooperate without executing command side effects.
// Smoke-tests every help path so command construction, default Cobra help, and
// argument validators cooperate without executing command side effects.
func TestHelpRunsForAllCLICommands(t *testing.T) {
	withDiscardedStdIO(t, func() {
		cases := [][]string{
@@ -153,8 +153,43 @@ func TestHelpRunsForAllCLICommands(t *testing.T) {
	})
}

// Protects the custom usage template from hiding optional positional arguments
// in localized help output.
func TestHelpUsesDefaultCobraFormatWithoutStyledTables(t *testing.T) {
	previous := currentLocale
	setLocale(localeEN)
	t.Cleanup(func() {
		setLocale(previous)
	})

	for _, args := range [][]string{
		{"--help"},
		{"bundles-list", "--help"},
		{"run", "--help"},
		{"projects", "--help"},
	} {
		tcName := strings.Join(args, " ")
		t.Run(tcName, func(t *testing.T) {
			out := captureStdout(t, func() {
				root := newRootCmd()
				root.SetArgs(args)
				if err := root.Execute(); err != nil {
					t.Fatalf("help execution failed for %q: %v", tcName, err)
				}
			})

			if !strings.Contains(out, "Usage:") {
				t.Fatalf("expected default Cobra usage header, got:\n%s", out)
			}
			for _, border := range []string{"┌", "│", "└"} {
				if strings.Contains(out, border) {
					t.Fatalf("help should not contain styled table border %q, got:\n%s", border, out)
				}
			}
		})
	}
}

// Protects default Cobra help from hiding optional positional arguments in
// localized help output.
func TestCommandHelpShowsPositionalArgsInUsage(t *testing.T) {
	previous := currentLocale
	setLocale(localeFR)
@@ -170,7 +205,7 @@ func TestCommandHelpShowsPositionalArgsInUsage(t *testing.T) {
		}
	})

	if !strings.Contains(out, "climax new [OPTIONS] [directory]") {
	if !strings.Contains(out, "climax new [directory] [flags]") {
		t.Fatalf("expected usage to include [directory], got:\n%s", out)
	}
}
+1 −1
Changes for main.go: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -10,7 +10,7 @@ import (
//go:embed max_releases.json
var fallbackReleases []byte

const version = "0.5.0"
const version = "0.5.1"

const maxConfigFile = "config.xml"
const minSupportedMaxVersion = "0.0.5"
+0 −122
Changes for ui.go: 0 added lines, 122 removed lines.
Original line number Diff line number Diff line
@@ -11,8 +11,6 @@ import (

	"github.com/jedib0t/go-pretty/v6/table"
	prettytext "github.com/jedib0t/go-pretty/v6/text"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
)

func newStyledTable() table.Writer {
@@ -35,126 +33,6 @@ func coloredYesNo(v bool) string {
	return prettytext.Colors{prettytext.FgRed}.Sprint(T("msg.no"))
}

func renderHelp(cmd *cobra.Command, _ []string) {
	applyLocaleFromCommand(cmd)

	usage := fmt.Sprintf("%s: %s [OPTIONS]", T("help.usage_prefix"), cmd.CommandPath())
	if cmd.HasAvailableSubCommands() {
		usage += fmt.Sprintf(" %s [%s]...", T("help.command_token"), T("help.args_token"))
	} else if argsSpec := commandArgsSpec(cmd); argsSpec != "" {
		usage += " " + argsSpec
	}
	fmt.Println(usage)
	fmt.Println()

	if strings.TrimSpace(cmd.Long) != "" {
		fmt.Println(strings.TrimSpace(cmd.Long))
		fmt.Println()
	}

	optionRows := make([]table.Row, 0)
	cmd.NonInheritedFlags().VisitAll(func(f *pflag.Flag) {
		if f.Hidden {
			return
		}
		optionRows = append(optionRows, table.Row{
			coloredName(flagLabel(f)),
			flagUsage(f),
		})
	})
	cmd.InheritedFlags().VisitAll(func(f *pflag.Flag) {
		if f.Hidden {
			return
		}
		optionRows = append(optionRows, table.Row{
			coloredName(flagLabel(f)),
			flagUsage(f),
		})
	})
	if len(optionRows) > 0 {
		fmt.Println(T("help.section.options"))
		t := newStyledTable()
		t.AppendRows(optionRows)
		t.Render()
		fmt.Println()
	}

	commandRows := make([]table.Row, 0)
	for _, c := range cmd.Commands() {
		if !c.IsAvailableCommand() || c.Hidden {
			continue
		}
		commandRows = append(commandRows, table.Row{
			coloredName(c.Name()),
			c.Short,
		})
	}
	if len(commandRows) > 0 {
		fmt.Println(T("help.section.commands"))
		t := newStyledTable()
		t.AppendRows(commandRows)
		t.Render()
	}
}

func commandArgsSpec(cmd *cobra.Command) string {
	if cmd == nil {
		return ""
	}
	parts := strings.Fields(strings.TrimSpace(cmd.Use))
	if len(parts) <= 1 {
		return ""
	}
	return strings.Join(parts[1:], " ")
}

func flagLabel(f *pflag.Flag) string {
	if f.Shorthand != "" && f.ShorthandDeprecated == "" {
		return fmt.Sprintf("-%s, --%s", f.Shorthand, f.Name)
	}
	return "--" + f.Name
}

func coloredName(s string) string {
	return prettytext.Colors{prettytext.FgCyan}.Sprint(s)
}

// Cobra's built-in help text is not localized, so core flag descriptions are
// translated at render time while command-specific usage remains local data.
func flagUsage(f *pflag.Flag) string {
	switch f.Name {
	case "help":
		return T("flag.help")
	case "lang":
		return T("flag.lang")
	default:
		return f.Usage
	}
}

// Help rendering happens before RunE, so it must resolve --lang directly from
// the command flags instead of relying only on startup argument parsing.
func applyLocaleFromCommand(cmd *cobra.Command) {
	if cmd == nil {
		return
	}
	if flags := cmd.Flags(); flags != nil {
		if flags.Lookup("lang") != nil {
			if lang, err := flags.GetString("lang"); err == nil && strings.TrimSpace(lang) != "" {
				setLocale(lang)
				return
			}
		}
	}
	if flags := cmd.InheritedFlags(); flags != nil {
		if flags.Lookup("lang") != nil {
			if lang, err := flags.GetString("lang"); err == nil && strings.TrimSpace(lang) != "" {
				setLocale(lang)
			}
		}
	}
}

// Accept both numeric menu choices and exact values; this lets scripted stdin
// avoid depending on the displayed ordering when it already knows the value.
func chooseBetween(values []string, label string) (string, error) {