Simple, intuitive CLI framework for Go pkg.go.dev/go.followtheprocess.codes/cli
go cli
0

Configure Feed

Select the types of activity you want to include in your feed.

Add a `Sorted` iterator on flag set for help text and keep `All` unordered (#210)

authored by

Tom Fleet and committed by
GitHub
(Apr 23, 2026, 12:20 PM +0100) 76ce7f52 a30ee62d

+109 -35
+1 -1
command.go
··· 653 653 func writeFlags(cmd *Command, s *strings.Builder) error { 654 654 tw := style.Tabwriter(s) 655 655 656 - for name, fl := range cmd.flags.All() { 656 + for name, fl := range cmd.flags.Sorted() { 657 657 var shorthand string 658 658 if fl.Short() != publicflag.NoShortHand { 659 659 shorthand = "-" + string(fl.Short())
+9 -2
internal/flag/set.go
··· 4 4 "errors" 5 5 "fmt" 6 6 "iter" 7 + "maps" 7 8 "os" 8 9 "slices" 9 10 "strings" ··· 200 201 return nil 201 202 } 202 203 203 - // All returns an iterator through the flags in the flagset 204 + // Sorted returns an iterator through the flags in the flagset 204 205 // in alphabetical order by name. 205 - func (s *Set) All() iter.Seq2[string, Value] { 206 + func (s *Set) Sorted() iter.Seq2[string, Value] { 206 207 return func(yield func(string, Value) bool) { 207 208 names := make([]string, 0, len(s.flags)) 208 209 for name := range s.flags { ··· 217 218 } 218 219 } 219 220 } 221 + } 222 + 223 + // All returns an iterator through the flags in the flagset 224 + // in alphabetical order by name. 225 + func (s *Set) All() iter.Seq2[string, Value] { 226 + return maps.All(s.flags) 220 227 } 221 228 222 229 // applyEnvVars looks up each configured environment variable and applies its value
+99 -32
internal/flag/set_test.go
··· 2307 2307 } 2308 2308 } 2309 2309 2310 + func TestSorted(t *testing.T) { 2311 + tests := []struct { 2312 + newSet func(t *testing.T) *flag.Set 2313 + test func(t *testing.T, set *flag.Set) 2314 + name string 2315 + }{ 2316 + { 2317 + name: "empty", 2318 + newSet: func(t *testing.T) *flag.Set { 2319 + return flag.NewSet() 2320 + }, 2321 + test: func(t *testing.T, set *flag.Set) { 2322 + // Iterator should yield no values 2323 + got := maps.Collect(set.Sorted()) 2324 + test.Equal(t, len(got), 0) 2325 + }, 2326 + }, 2327 + { 2328 + name: "full", 2329 + newSet: func(t *testing.T) *flag.Set { 2330 + set := flag.NewSet() 2331 + 2332 + verbose, err := flag.New(new(bool), "verbose", 'v', "Show verbose info", flag.Config[bool]{}) 2333 + test.Ok(t, err) 2334 + 2335 + debug, err := flag.New(new(bool), "debug", 'd', "Show debug info", flag.Config[bool]{}) 2336 + test.Ok(t, err) 2337 + 2338 + thing, err := flag.New(new(string), "thing", 't', "A thing", flag.Config[string]{}) 2339 + test.Ok(t, err) 2340 + 2341 + number, err := flag.New(new(int), "number", 'n', "Number of times", flag.Config[int]{}) 2342 + test.Ok(t, err) 2343 + 2344 + duration, err := flag.New(new(time.Duration), "duration", 'D', "The time to do something for", flag.Config[time.Duration]{}) 2345 + test.Ok(t, err) 2346 + 2347 + test.Ok(t, flag.AddToSet(set, verbose)) 2348 + test.Ok(t, flag.AddToSet(set, debug)) 2349 + test.Ok(t, flag.AddToSet(set, thing)) 2350 + test.Ok(t, flag.AddToSet(set, number)) 2351 + test.Ok(t, flag.AddToSet(set, duration)) 2352 + 2353 + return set 2354 + }, 2355 + test: func(t *testing.T, set *flag.Set) { 2356 + next, stop := iter.Pull2(set.Sorted()) 2357 + defer stop() 2358 + 2359 + // Should now be in alphabetical order 2360 + name, fl, ok := next() 2361 + test.True(t, ok) 2362 + test.Equal(t, name, "debug") 2363 + test.Equal(t, fl.Name(), "debug") 2364 + 2365 + name, fl, ok = next() 2366 + test.True(t, ok) 2367 + test.Equal(t, name, "duration") 2368 + test.Equal(t, fl.Name(), "duration") 2369 + 2370 + name, fl, ok = next() 2371 + test.True(t, ok) 2372 + test.Equal(t, name, "number") 2373 + test.Equal(t, fl.Name(), "number") 2374 + 2375 + name, fl, ok = next() 2376 + test.True(t, ok) 2377 + test.Equal(t, name, "thing") 2378 + test.Equal(t, fl.Name(), "thing") 2379 + 2380 + name, fl, ok = next() 2381 + test.True(t, ok) 2382 + test.Equal(t, name, "verbose") 2383 + test.Equal(t, fl.Name(), "verbose") 2384 + 2385 + // Thats it 2386 + name, fl, ok = next() 2387 + test.False(t, ok) 2388 + test.Equal(t, name, "") 2389 + test.Equal(t, fl, nil) 2390 + }, 2391 + }, 2392 + } 2393 + 2394 + for _, tt := range tests { 2395 + t.Run(tt.name, func(t *testing.T) { 2396 + set := tt.newSet(t) 2397 + tt.test(t, set) 2398 + }) 2399 + } 2400 + } 2401 + 2310 2402 func TestAll(t *testing.T) { 2311 2403 tests := []struct { 2312 2404 newSet func(t *testing.T) *flag.Set ··· 2353 2445 return set 2354 2446 }, 2355 2447 test: func(t *testing.T, set *flag.Set) { 2356 - // Iterator should yield no values 2357 - next, stop := iter.Pull2(set.All()) 2358 - defer stop() 2448 + // Should get everything back, but order is not deterministic 2449 + all := maps.Collect(set.All()) 2359 2450 2360 - // Should now be in alphabetical order 2361 - name, fl, ok := next() 2362 - test.True(t, ok) 2363 - test.Equal(t, name, "debug") 2364 - test.Equal(t, fl.Name(), "debug") 2451 + want := []string{"verbose", "debug", "thing", "number", "duration"} 2452 + slices.Sort(want) 2365 2453 2366 - name, fl, ok = next() 2367 - test.True(t, ok) 2368 - test.Equal(t, name, "duration") 2369 - test.Equal(t, fl.Name(), "duration") 2454 + got := slices.Collect(maps.Keys(all)) 2455 + slices.Sort(got) 2370 2456 2371 - name, fl, ok = next() 2372 - test.True(t, ok) 2373 - test.Equal(t, name, "number") 2374 - test.Equal(t, fl.Name(), "number") 2375 - 2376 - name, fl, ok = next() 2377 - test.True(t, ok) 2378 - test.Equal(t, name, "thing") 2379 - test.Equal(t, fl.Name(), "thing") 2380 - 2381 - name, fl, ok = next() 2382 - test.True(t, ok) 2383 - test.Equal(t, name, "verbose") 2384 - test.Equal(t, fl.Name(), "verbose") 2385 - 2386 - // Thats it 2387 - name, fl, ok = next() 2388 - test.False(t, ok) 2389 - test.Equal(t, name, "") 2390 - test.Equal(t, fl, nil) 2457 + test.EqualFunc(t, got, want, slices.Equal) 2391 2458 }, 2392 2459 }, 2393 2460 }