Skip to content

proposal: flag: maintain insertion order and allow Visit/VisitAll to use that as a sort option #76683

@mattmc3

Description

@mattmc3

Proposal Details

The flag package forces lexicographical sorting through sortFlags, which is called by Visit and VisitAll. That makes it hard to keep related flags grouped together in help output unless you track that yourself. For example, if you have paired flags like -no-list/-list or -lower/-upper, there’s no way to keep them next to each other in the help output without PrintDefaults customization.

Developers already express the order they want simply by the order they define their flags. FlagSet discards that insertion order by storing flags only in a map. There's no reason to throw away that order. Adding an optional “insertion order” sort mode would keep the default behavior intact, but give developers a straightforward way to present flags in a logical order when generating help.

This is a small, backward-compatible change: default behavior stays the same, but developers who want predictable, grouped help output can opt in without writing their own tracking layer.


An example implementation

  1. Add an insertion slice to FlagSet
type FlagSet struct {
    // existing fields...
    formal map[string]*Flag

    // new: preserve definition order
    insertion []*Flag // this could also be a []string of flag names
}
  1. Record insertion order in Var
func (f *FlagSet) Var(value Value, name, usage string) {
    // existing logic...
    fl := &Flag{Name: name, Usage: usage, Value: value}
    f.formal[name] = fl
    f.insertion = append(f.insertion, fl)
}

3.Add a sort mode and update sortFlags

type SortMode int

const (
    SortLexical SortMode = iota
    SortInsertion
)

var sortMode = SortLexical

func SetSortMode(m SortMode) { sortMode = m }

// use whatever sort mode the user set
func (f *FlagSet) sortFlags(list []*Flag) []*Flag {
    switch sortMode {
    case SortInsertion:
        return f.insertion
    default:
        sort.Slice(list, func(i, j int) bool {
            return list[i].Name < list[j].Name
        })
        return list
    }
}

There are other possible implementations, but this one is simple and maintains backward compatibility. Yes, a developer can manually maintain their own insertion slice and override PrintDefaults, but they shouldn’t have to - FlagSet already knows the order flags were defined, and that information is currently lost. Allowing insertion-order sorting makes builtin help generation more predictable without breaking any existing behavior.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions