11package figtree
22
33import (
4- "flag"
54 "fmt"
65 "os"
76 "path/filepath"
7+ "sort"
88 "strings"
99
1010 "golang.org/x/term"
@@ -23,52 +23,110 @@ func (tree *figTree) UsageString() string {
2323 }
2424 }
2525
26+ // First pass: Collect all primary flag names and aliases correctly
27+ // Also calculate maxFlagLen for formatting
28+ type flagInfo struct {
29+ name string
30+ aliases []string
31+ defValue string
32+ usage string
33+ mutagenesis Mutagenesis
34+ isAlias bool // Mark if this is an alias entry itself
35+ originalName string // For aliases, store the original flag name
36+ }
37+ allFlagData := make (map [string ]* flagInfo ) // Use map to deduplicate and process by main flag name
38+
39+ tree .mu .RLock () // Lock for accessing tree.figs and tree.aliases
40+
41+ // Populate with main flags
42+ for name , fruit := range tree .figs {
43+ f := tree .flagSet .Lookup (name ) // Get the flag.Flag object
44+ if f == nil {
45+ continue // Should not happen if figs map is consistent with flagSet
46+ }
47+
48+ info := & flagInfo {
49+ name : f .Name ,
50+ defValue : f .DefValue ,
51+ usage : f .Usage ,
52+ mutagenesis : fruit .Mutagenesis , // Get mutagenesis from figFruit
53+ isAlias : false ,
54+ }
55+ allFlagData [name ] = info
56+ }
57+
58+ // Add aliases to their primary flags
59+ for alias , originalName := range tree .aliases {
60+ if info , ok := allFlagData [originalName ]; ok {
61+ info .aliases = append (info .aliases , alias )
62+ }
63+ }
64+
65+ tree .mu .RUnlock () // Release lock
66+
67+ // Second pass: Re-evaluate maxFlagLen based on combined flag names
2668 maxFlagLen := 0
27- flag .VisitAll (func (f * flag.Flag ) {
28- flagStr := f .Name
29- // Handle special cases for default values
30- defValue := f .DefValue
31- if defValue == `""` || defValue == "[]" || defValue == "{}" {
32- defValue = ""
69+ for name := range allFlagData { // Iterate over main flag names only
70+ info := allFlagData [name ]
71+
72+ flagStr := info .name
73+ if len (info .aliases ) > 0 {
74+ // Sort aliases for consistent output
75+ // Sort is important here because map iteration order is not guaranteed.
76+ // This ensures "-a|-alpha" is always consistent.
77+ sortedAliases := make ([]string , len (info .aliases ))
78+ copy (sortedAliases , info .aliases )
79+ // Apply tree.mu.RLock() and tree.mu.RUnlock() or sort.Strings outside loop
80+ sort .Strings (sortedAliases ) // Needs sort import
81+
82+ flagStr = fmt .Sprintf ("%s|-%s" , strings .Join (sortedAliases , "|-" ), info .name )
3383 }
34- if defValue != "" {
35- flagStr = fmt .Sprintf ("%s[=%s]" , f .Name , defValue )
84+
85+ displayValue := info .defValue
86+ if displayValue == `""` || displayValue == "[]" || displayValue == "{}" {
87+ displayValue = ""
88+ }
89+ if displayValue != "" {
90+ flagStr = fmt .Sprintf ("%s[=%s]" , flagStr , displayValue )
3691 }
92+
3793 if len (flagStr ) > maxFlagLen {
3894 maxFlagLen = len (flagStr )
3995 }
40- })
96+ }
4197
4298 var sb strings.Builder
4399 _ , _ = fmt .Fprintf (& sb , "Usage of %s (powered by figtree %s):\n " , filepath .Base (os .Args [0 ]), Version ())
44- flag .VisitAll (func (f * flag.Flag ) {
45- flagStr := f .Name
46- defValue := f .DefValue
47- if defValue == `""` || defValue == "[]" || defValue == "{}" {
48- defValue = ""
49- }
50- if defValue != "" {
51- flagStr = fmt .Sprintf ("%s[=%s]" , f .Name , defValue )
52- }
53100
54- typeStr := "Unknown"
55- tree .mu .RLock ()
56- if fig , ok := tree .figs [f .Name ]; ok && fig != nil {
57- typeStr = string (fig .Mutagenesis )
101+ sortedFlagNames := make ([]string , 0 , len (allFlagData ))
102+ for name := range allFlagData {
103+ sortedFlagNames = append (sortedFlagNames , name )
104+ }
105+ sort .Strings (sortedFlagNames )
106+
107+ for _ , name := range sortedFlagNames {
108+ info := allFlagData [name ]
109+
110+ flagStr := info .name
111+ if len (info .aliases ) > 0 {
112+ sortedAliases := make ([]string , len (info .aliases ))
113+ copy (sortedAliases , info .aliases )
114+ sort .Strings (sortedAliases )
115+ flagStr = fmt .Sprintf ("%s|-%s" , strings .Join (sortedAliases , "|-" ), info .name )
58116 }
59- tree .mu .RUnlock ()
60- typeField := fmt .Sprintf ("[%s]" , typeStr )
61- var aliasList []string
62- for alias , name := range tree .aliases {
63- if name == f .Name {
64- aliasList = append (aliasList , alias )
65- }
117+
118+ displayValue := info .defValue
119+ if displayValue == `""` || displayValue == "[]" || displayValue == "{}" {
120+ displayValue = ""
66121 }
67- if len ( aliasList ) > 0 {
68- flagStr = fmt .Sprintf ("%s|-%s " , strings . Join ( aliasList , "|-" ), flagStr )
122+ if displayValue != "" {
123+ flagStr = fmt .Sprintf ("%s[=%s] " , flagStr , displayValue )
69124 }
70125
71- line := fmt .Sprintf (" -%-*s %-8s %s" , maxFlagLen , flagStr , typeField , f .Usage )
126+ typeField := fmt .Sprintf ("[%s]" , info .mutagenesis ) // Use Mutagenesis from figFruit
127+
128+ // The f.Usage is the usage string of the main flag.
129+ line := fmt .Sprintf (" -%-*s %-8s %s" , maxFlagLen , flagStr , typeField , info .usage )
72130
73131 // Wrap the usage text if it exceeds terminal width
74132 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 {
81139 _ , _ = fmt .Fprintf (& sb , "%s%s\n " , indent , l )
82140 }
83141 }
84- })
142+ }
85143
86144 return sb .String ()
87145}
0 commit comments