Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,8 @@ structalign [flags] [packages]
-nolint-linters string
//nolint tokens that suppress a finding (default
"fieldalignment"; a bare //nolint always counts)
-version print version and exit
-no-rc skip loading .structalignrc files
-no-rc skip loading .structalignrc files
-version print version and exit
```

In the default `-color=auto`, color is emitted only when stdout is a terminal and
Expand Down Expand Up @@ -526,6 +526,22 @@ diffing uses `go-udiff` rather than x/tools' own diff package:
compiler rejects it. `go-udiff` is a public port of the same gopls diff code,
so the results are equivalent.

## Easter eggs

A few hidden flags are intentionally kept out of `-help` and the flag table above:
**`-cga`, `-green`, `-amber`** — shortcuts for the retro-theme palettes otherwise
selected with `STRUCTALIGN_THEME=…`. Pick one per invocation; the egg flag wins
over the environment variable.

```sh
structalign -cga ./... # cyan/magenta/white CGA palette
structalign -green -inspect ./_example # single-hue green-phosphor look
structalign -amber -diff=side ./... # amber-phosphor side-by-side diff
```

They are stripped before flag parsing, so they never trip *"flag provided but not
defined"* when combined with normal flags.

## Changelog

See [CHANGELOG.md](CHANGELOG.md). Commits follow
Expand Down
17 changes: 13 additions & 4 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ type options struct {
threshold int
showNolint bool
nolintLinters string
noRC bool
}

// savings is the absolute bytes a finding saves, or 0 when sizes are unknown or
Expand Down Expand Up @@ -119,6 +120,7 @@ func (a *App) Run(args []string) int {
fs.IntVar(&opt.threshold, "threshold", 0, "in diff mode, only show structs that save at least this many bytes")
fs.BoolVar(&opt.showNolint, "show-nolint", false, "show structs even when their type carries a recognized //nolint directive")
fs.StringVar(&opt.nolintLinters, "nolint-linters", "fieldalignment", "comma-separated //nolint tokens that suppress a finding (bare //nolint always counts)")
fs.BoolVar(&opt.noRC, "no-rc", false, "skip loading .structalignrc files")
fs.Usage = func() {
fmt.Fprint(a.Stderr, //nolint:errcheck
"structalign: show how a struct's fields could be reordered to use less memory\n\n",
Expand Down Expand Up @@ -160,7 +162,9 @@ func (a *App) Run(args []string) int {
// Easter-egg theme flags: -cga/-green/-amber select a retro palette. Like
// -fix, they are caught before parsing and stripped from args, so they stay
// invisible in -help and never trip "flag provided but not defined".
// Also scans for -no-rc to disable RC loading.
// Also peeks at -no-rc so RC loading (which precedes fs.Parse) can honor it;
// the flag itself is still registered with fs so fs.Parse binds opt.noRC and
// -h lists it like any other config flag.
themeName, noRC, args := a.scanEarlyFlags(args)

// Apply configuration layers as defaults.
Expand Down Expand Up @@ -344,9 +348,13 @@ func cmp[T ~int | ~int64](a, b T) int {

var eggRE = regexp.MustCompile(`^--?([^=]+)(?:=(.*))?$`)

// scanEarlyFlags scans args for retro-theme "easter egg" flags and the -no-rc
// flag, returning the chosen theme name, whether RC loading is disabled, and
// the args slice with those flags removed. It stops at the first "--" separator.
// scanEarlyFlags peeks at args ahead of fs.Parse for two reasons: the
// retro-theme "easter egg" flags (-cga/-green/-amber) are not registered with
// the FlagSet (they stay invisible in -help) and must be stripped so fs.Parse
// doesn't reject them; and -no-rc must be known before RC loading runs.
// Returns the chosen theme name, whether RC loading is disabled, and the args
// slice. The egg flags are stripped; -no-rc is left in place so fs.Parse can
// also bind it to opt.noRC. Stops at the first "--" separator.
func (a *App) scanEarlyFlags(args []string) (theme string, noRC bool, filtered []string) {
filtered = make([]string, 0, len(args))
afterDD := false
Expand All @@ -363,6 +371,7 @@ func (a *App) scanEarlyFlags(args []string) (theme string, noRC bool, filtered [
if !strings.Contains(arg, "=") || val == "true" || val == "1" {
noRC = true
}
filtered = append(filtered, arg)
continue
}
if name == "cga" || name == "green" || name == "amber" {
Expand Down
2 changes: 1 addition & 1 deletion internal/app/usage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ func TestUsageHasManPageSections(t *testing.T) {
// PrintDefaults still emits the real flags after the header.
assert.Contains(t, u, "-diff", "the flag list is still printed")
assert.Contains(t, u, "-inspect")
assert.Contains(t, u, "-no-rc", "the -no-rc config flag is discoverable in -h")
}

func TestUsageOmitsEasterEggs(t *testing.T) {
u := usageText(t)
assert.NotContains(t, u, "-fix", "the -fix egg stays out of help")
assert.NotContains(t, u, "-no-rc", "the -no-rc flag stays out of help")
assert.NotContains(t, u, "-cga", "theme eggs stay out of help")
assert.NotContains(t, u, "-green")
assert.NotContains(t, u, "-amber")
Expand Down
Loading