From e6374a3c52d45a74d94512672c9c25f3da6dec67 Mon Sep 17 00:00:00 2001 From: Andrei Merlescu Date: Mon, 23 Jun 2025 16:22:00 -0400 Subject: [PATCH] Removed duplicate entries for WithAlias from UsageString --- VERSION | 2 +- alias_test.go | 2 + usage.go | 126 ++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 95 insertions(+), 35 deletions(-) diff --git a/VERSION b/VERSION index 0797f73..93b1c84 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.11 \ No newline at end of file +v2.0.12 \ No newline at end of file diff --git a/alias_test.go b/alias_test.go index abbd876..a172a39 100644 --- a/alias_test.go +++ b/alias_test.go @@ -89,5 +89,7 @@ func TestWithAlias(t *testing.T) { figs = figs.WithAlias("two", "x") // Should this overwrite or be ignored? assert.NoError(t, figs.Parse()) assert.Equal(t, "value1", *figs.String("x")) // Clarify expected behavior + us := figs.UsageString() + assert.NotEmpty(t, us) }) } diff --git a/usage.go b/usage.go index 6472afd..4668303 100644 --- a/usage.go +++ b/usage.go @@ -1,10 +1,10 @@ package figtree import ( - "flag" "fmt" "os" "path/filepath" + "sort" "strings" "golang.org/x/term" @@ -23,52 +23,110 @@ func (tree *figTree) UsageString() string { } } + // First pass: Collect all primary flag names and aliases correctly + // Also calculate maxFlagLen for formatting + type flagInfo struct { + name string + aliases []string + defValue string + usage string + mutagenesis Mutagenesis + isAlias bool // Mark if this is an alias entry itself + originalName string // For aliases, store the original flag name + } + allFlagData := make(map[string]*flagInfo) // Use map to deduplicate and process by main flag name + + tree.mu.RLock() // Lock for accessing tree.figs and tree.aliases + + // Populate with main flags + for name, fruit := range tree.figs { + f := tree.flagSet.Lookup(name) // Get the flag.Flag object + if f == nil { + continue // Should not happen if figs map is consistent with flagSet + } + + info := &flagInfo{ + name: f.Name, + defValue: f.DefValue, + usage: f.Usage, + mutagenesis: fruit.Mutagenesis, // Get mutagenesis from figFruit + isAlias: false, + } + allFlagData[name] = info + } + + // Add aliases to their primary flags + for alias, originalName := range tree.aliases { + if info, ok := allFlagData[originalName]; ok { + info.aliases = append(info.aliases, alias) + } + } + + tree.mu.RUnlock() // Release lock + + // Second pass: Re-evaluate maxFlagLen based on combined flag names maxFlagLen := 0 - flag.VisitAll(func(f *flag.Flag) { - flagStr := f.Name - // Handle special cases for default values - defValue := f.DefValue - if defValue == `""` || defValue == "[]" || defValue == "{}" { - defValue = "" + for name := range allFlagData { // Iterate over main flag names only + info := allFlagData[name] + + flagStr := info.name + if len(info.aliases) > 0 { + // Sort aliases for consistent output + // Sort is important here because map iteration order is not guaranteed. + // This ensures "-a|-alpha" is always consistent. + sortedAliases := make([]string, len(info.aliases)) + copy(sortedAliases, info.aliases) + // Apply tree.mu.RLock() and tree.mu.RUnlock() or sort.Strings outside loop + sort.Strings(sortedAliases) // Needs sort import + + flagStr = fmt.Sprintf("%s|-%s", strings.Join(sortedAliases, "|-"), info.name) } - if defValue != "" { - flagStr = fmt.Sprintf("%s[=%s]", f.Name, defValue) + + displayValue := info.defValue + if displayValue == `""` || displayValue == "[]" || displayValue == "{}" { + displayValue = "" + } + if displayValue != "" { + flagStr = fmt.Sprintf("%s[=%s]", flagStr, displayValue) } + if len(flagStr) > maxFlagLen { maxFlagLen = len(flagStr) } - }) + } var sb strings.Builder _, _ = fmt.Fprintf(&sb, "Usage of %s (powered by figtree %s):\n", filepath.Base(os.Args[0]), Version()) - flag.VisitAll(func(f *flag.Flag) { - flagStr := f.Name - defValue := f.DefValue - if defValue == `""` || defValue == "[]" || defValue == "{}" { - defValue = "" - } - if defValue != "" { - flagStr = fmt.Sprintf("%s[=%s]", f.Name, defValue) - } - typeStr := "Unknown" - tree.mu.RLock() - if fig, ok := tree.figs[f.Name]; ok && fig != nil { - typeStr = string(fig.Mutagenesis) + sortedFlagNames := make([]string, 0, len(allFlagData)) + for name := range allFlagData { + sortedFlagNames = append(sortedFlagNames, name) + } + sort.Strings(sortedFlagNames) + + for _, name := range sortedFlagNames { + info := allFlagData[name] + + flagStr := info.name + if len(info.aliases) > 0 { + sortedAliases := make([]string, len(info.aliases)) + copy(sortedAliases, info.aliases) + sort.Strings(sortedAliases) + flagStr = fmt.Sprintf("%s|-%s", strings.Join(sortedAliases, "|-"), info.name) } - tree.mu.RUnlock() - typeField := fmt.Sprintf("[%s]", typeStr) - var aliasList []string - for alias, name := range tree.aliases { - if name == f.Name { - aliasList = append(aliasList, alias) - } + + displayValue := info.defValue + if displayValue == `""` || displayValue == "[]" || displayValue == "{}" { + displayValue = "" } - if len(aliasList) > 0 { - flagStr = fmt.Sprintf("%s|-%s", strings.Join(aliasList, "|-"), flagStr) + if displayValue != "" { + flagStr = fmt.Sprintf("%s[=%s]", flagStr, displayValue) } - line := fmt.Sprintf(" -%-*s %-8s %s", maxFlagLen, flagStr, typeField, f.Usage) + typeField := fmt.Sprintf("[%s]", info.mutagenesis) // Use Mutagenesis from figFruit + + // The f.Usage is the usage string of the main flag. + line := fmt.Sprintf(" -%-*s %-8s %s", maxFlagLen, flagStr, typeField, info.usage) // Wrap the usage text if it exceeds terminal width lines := wrapText(line, termWidth, maxFlagLen+8+3+3) // 8 for type field, 3 spaces each side @@ -81,7 +139,7 @@ func (tree *figTree) UsageString() string { _, _ = fmt.Fprintf(&sb, "%s%s\n", indent, l) } } - }) + } return sb.String() }