diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 84f9cae01d..3b31afc5c0 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -37,7 +37,7 @@ jobs: - name: vet run: go run internal/build/build.go vet - - name: test with tags + - name: test with urfave_cli_no_docs tag run: go run internal/build/build.go -tags urfave_cli_no_docs test - name: test @@ -47,7 +47,7 @@ jobs: run: go run internal/build/build.go check-binary-size - name: check-binary-size with tags (informational only) - run: go run internal/build/build.go -tags urfave_cli_no_docs check-binary-size || true + run: go run internal/build/build.go -tags urfave_cli_no_docs check-binary-size - name: Upload coverage to Codecov if: success() && matrix.go == '1.18.x' && matrix.os == 'ubuntu-latest' @@ -76,19 +76,37 @@ jobs: uses: actions/checkout@v3 - name: Install Dependencies - run: - mkdir -p "${GITHUB_WORKSPACE}/.local/bin" && - curl -fsSL -o "${GITHUB_WORKSPACE}/.local/bin/gfmrun" "https://github.com/urfave/gfmrun/releases/download/v1.3.0/gfmrun-$(go env GOOS)-$(go env GOARCH)-v1.3.0" && - chmod +x "${GITHUB_WORKSPACE}/.local/bin/gfmrun" && - npm install -g markdown-toc@1.2.0 + run: | + mkdir -p "${GITHUB_WORKSPACE}/.local/bin" + curl -fsSL -o "${GITHUB_WORKSPACE}/.local/bin/gfmrun" "https://github.com/urfave/gfmrun/releases/download/v1.3.0/gfmrun-$(go env GOOS)-$(go env GOARCH)-v1.3.0" + chmod +x "${GITHUB_WORKSPACE}/.local/bin/gfmrun" - name: gfmrun run: go run internal/build/build.go gfmrun docs/v2/manual.md - - name: toc - run: go run internal/build/build.go toc docs/v2/manual.md - - name: diff check run: | git diff --exit-code git diff --cached --exit-code + + publish: + if: startswith(github.ref, 'refs/tags/') + name: publish + needs: [test-docs] + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Setup mkdocs + run: | + pip install -U pip + pip install -r mkdocs-requirements.txt + git remote rm origin + git remote add origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/urfave/cli.git + + - name: Publish Docs + run: | + mkdocs gh-deploy --force diff --git a/.gitignore b/.gitignore index e0c50aba49..c04fcc5389 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,10 @@ *.coverprofile *.orig -node_modules/ vendor .idea internal/*/built-example coverage.txt /.local/ +/site/ *.exe diff --git a/Makefile b/Makefile index 52e9204d6c..3b0e5e0bbd 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ # attention on files that are primarily Go. .PHONY: all -all: generate vet tag-test test check-binary-size tag-check-binary-size gfmrun toc v2diff +all: generate vet tag-test test check-binary-size tag-check-binary-size gfmrun v2diff # NOTE: this is a special catch-all rule to run any of the commands # defined in internal/build/build.go with optional arguments passed @@ -27,6 +27,14 @@ tag-check-binary-size: gfmrun: go run internal/build/build.go gfmrun docs/v2/manual.md -.PHONY: toc -toc: - go run internal/build/build.go toc docs/v2/manual.md +.PHONY: docs +docs: + mkdocs build + +.PHONY: docs-deps +docs-deps: + pip install -r mkdocs-requirements.txt + +.PHONY: serve-docs +serve-docs: + mkdocs serve diff --git a/README.md b/README.md index 6e4d698c24..beb5963e2c 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ You can use the following build tags: When set, this removes `ToMarkdown` and `ToMan` methods, so your application won't be able to call those. This reduces the resulting binary size by about -300-400 KB (measured using Go 1.18.1 on Linux/amd64), due to less dependencies. +300-400 KB (measured using Go 1.18.1 on Linux/amd64), due to fewer dependencies. ### GOPATH diff --git a/app.go b/app.go index 52ef1b0fa0..463437a58a 100644 --- a/app.go +++ b/app.go @@ -94,6 +94,8 @@ type App struct { // single-character bool arguments into one // i.e. foobar -o -v -> foobar -ov UseShortOptionHandling bool + // Enable suggestions for commands and flags + Suggest bool didSetup bool } @@ -264,6 +266,11 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) { return err } _, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) + if a.Suggest { + if suggestion, err := a.suggestFlagFromError(err, ""); err == nil { + fmt.Fprintf(a.Writer, suggestion) + } + } _ = ShowAppHelp(cCtx) return err } @@ -383,6 +390,11 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { return err } _, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) + if a.Suggest { + if suggestion, err := a.suggestFlagFromError(err, cCtx.Command.Name); err == nil { + fmt.Fprintf(a.Writer, suggestion) + } + } _ = ShowSubcommandHelp(cCtx) return err } diff --git a/command.go b/command.go index ba0169bb11..3b9b837034 100644 --- a/command.go +++ b/command.go @@ -119,6 +119,11 @@ func (c *Command) Run(ctx *Context) (err error) { } _, _ = fmt.Fprintln(cCtx.App.Writer, "Incorrect Usage:", err.Error()) _, _ = fmt.Fprintln(cCtx.App.Writer) + if ctx.App.Suggest { + if suggestion, err := ctx.App.suggestFlagFromError(err, c.Name); err == nil { + fmt.Fprintf(cCtx.App.Writer, suggestion) + } + } _ = ShowCommandHelp(cCtx, c.Name) return err } @@ -249,6 +254,7 @@ func (c *Command) startApp(ctx *Context) error { app.ErrWriter = ctx.App.ErrWriter app.ExitErrHandler = ctx.App.ExitErrHandler app.UseShortOptionHandling = ctx.App.UseShortOptionHandling + app.Suggest = ctx.App.Suggest app.categories = newCommandCategories() for _, command := range c.Subcommands { diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 0000000000..8654f8f01b --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +cli.urfave.org diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md new file mode 120000 index 0000000000..0400d57460 --- /dev/null +++ b/docs/CODE_OF_CONDUCT.md @@ -0,0 +1 @@ +../CODE_OF_CONDUCT.md \ No newline at end of file diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 37055fff38..7e80bdbf13 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -98,6 +98,28 @@ line help system which may be consulted for further information, e.g.: go run internal/genflags/cmd/genflags/main.go --help ``` +#### docs output + +The documentation in the `docs` directory is automatically built via `mkdocs` into a +static site and published when releases are pushed (see [RELEASING](./RELEASING/)). There +is no strict requirement to build the documentation when developing locally, but the +following `make` targets may be used if desired: + +```sh +# install documentation dependencies with `pip` +make docs-deps +``` + +```sh +# build the static site in `./site` +make docs +``` + +```sh +# start an mkdocs development server +make serve-docs +``` + ### pull requests Please feel free to open a pull request to fix a bug or add a feature. The @urfave/cli diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000000..792704494a --- /dev/null +++ b/docs/index.md @@ -0,0 +1,21 @@ +# Welcome to urfave/cli + +[![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://pkg.go.dev/github.com/urfave/cli/v2) +[![codebeat](https://codebeat.co/badges/0a8f30aa-f975-404b-b878-5fab3ae1cc5f)](https://codebeat.co/projects/github-com-urfave-cli) +[![Go Report Card](https://goreportcard.com/badge/urfave/cli)](https://goreportcard.com/report/urfave/cli) +[![codecov](https://codecov.io/gh/urfave/cli/branch/main/graph/badge.svg)](https://codecov.io/gh/urfave/cli) + +`urfave/cli` is a simple, fast, and fun package for building command line apps in Go. The +goal is to enable developers to write fast and distributable command line applications in +an expressive way. + +These are the guides for each major supported version: + +- [`v2`](./v2/) +- [`v1`](./v1/) + +In addition to the version-specific guides, these other documents are available: + +- [CONTRIBUTING](./CONTRIBUTING/) +- [CODE OF CONDUCT](./CODE_OF_CONDUCT/) +- [RELEASING](./RELEASING/) diff --git a/docs/migrate-v1-to-v2.md b/docs/migrate-v1-to-v2.md index c555468beb..7c703c847f 100644 --- a/docs/migrate-v1-to-v2.md +++ b/docs/migrate-v1-to-v2.md @@ -1,6 +1,4 @@ -Migration Guide: v1 to v2 -=== - +# Migration Guide: v1 to v2 v2 has a number of breaking changes but converting is relatively straightforward: make the changes documented below then resolve any @@ -11,25 +9,6 @@ If you find any issues not covered by this document, please post a comment on [Issue 921](https://github.com/urfave/cli/issues/921) or consider sending a PR to help improve this guide. - - - * [Flags before args](#flags-before-args) - * [Import string changed](#import-string-changed) - * [Flag aliases are done differently](#flag-aliases-are-done-differently) - * [EnvVar is now a list (EnvVars)](#envvar-is-now-a-list-envvars) - * [Actions returns errors](#actions-returns-errors) - * [cli.Flag changed](#cliflag-changed) - * [Commands are now lists of pointers](#commands-are-now-lists-of-pointers) - * [Lists of commands should be pointers](#lists-of-commands-should-be-pointers) - * [Appending Commands](#appending-commands) - * [GlobalString, GlobalBool and its likes are deprecated](#globalstring-globalbool-and-its-likes-are-deprecated) - * [BoolTFlag and BoolT are deprecated](#booltflag-and-boolt-are-deprecated) - * [&cli.StringSlice{""} replaced with cli.NewStringSlice("")](#clistringslice-replaced-with-clinewstringslice) - * [Replace deprecated functions](#replace-deprecated-functions) - * [Everything else](#everything-else) - - - # Flags before args In v2 flags must come before args. This is more POSIX-compliant. You diff --git a/docs/v1/index.md b/docs/v1/index.md new file mode 120000 index 0000000000..9d0493a7f2 --- /dev/null +++ b/docs/v1/index.md @@ -0,0 +1 @@ +manual.md \ No newline at end of file diff --git a/docs/v1/manual.md b/docs/v1/manual.md index dd22bdbdc7..6f568b73f1 100644 --- a/docs/v1/manual.md +++ b/docs/v1/manual.md @@ -1,35 +1,4 @@ -cli v1 manual -=== - - - -- [Getting Started](#getting-started) -- [Examples](#examples) - * [Arguments](#arguments) - * [Flags](#flags) - + [Placeholder Values](#placeholder-values) - + [Alternate Names](#alternate-names) - + [Ordering](#ordering) - + [Values from the Environment](#values-from-the-environment) - + [Values from files](#values-from-files) - + [Values from alternate input sources (YAML, TOML, and others)](#values-from-alternate-input-sources-yaml-toml-and-others) - + [Precedence](#precedence) - * [Subcommands](#subcommands) - * [Subcommands categories](#subcommands-categories) - * [Exit code](#exit-code) - * [Combining short options](#combining-short-options) - * [Bash Completion](#bash-completion) - + [Enabling](#enabling) - + [Distribution](#distribution) - + [Customization](#customization) - * [Generated Help Text](#generated-help-text) - + [Customization](#customization-1) - * [Version Flag](#version-flag) - + [Customization](#customization-2) - + [Full API Example](#full-api-example) - * [Migrating to V2](#migrating-to-v2) - - +# v1 guide ## Getting Started diff --git a/docs/v2/index.md b/docs/v2/index.md new file mode 120000 index 0000000000..9d0493a7f2 --- /dev/null +++ b/docs/v2/index.md @@ -0,0 +1 @@ +manual.md \ No newline at end of file diff --git a/docs/v2/manual.md b/docs/v2/manual.md index 5ca044c232..3d96bb86cc 100644 --- a/docs/v2/manual.md +++ b/docs/v2/manual.md @@ -1,51 +1,4 @@ -cli v2 manual -=== - - - -- [Migrating From Older Releases](#migrating-from-older-releases) -- [Getting Started](#getting-started) -- [Examples](#examples) - * [Arguments](#arguments) - * [Flags](#flags) - + [Placeholder Values](#placeholder-values) - + [Alternate Names](#alternate-names) - + [Ordering](#ordering) - + [Values from the Environment](#values-from-the-environment) - + [Values from files](#values-from-files) - + [Values from alternate input sources (YAML, TOML, and others)](#values-from-alternate-input-sources-yaml-toml-and-others) - + [Required Flags](#required-flags) - + [Default Values for help output](#default-values-for-help-output) - + [Precedence](#precedence) - * [Subcommands](#subcommands) - * [Subcommands categories](#subcommands-categories) - * [Exit code](#exit-code) - * [Combining short options](#combining-short-options) - * [Bash Completion](#bash-completion) - + [Default auto-completion](#default-auto-completion) - + [Custom auto-completion](#custom-auto-completion) - + [Enabling](#enabling) - + [Distribution and Persistent Autocompletion](#distribution-and-persistent-autocompletion) - + [Customization](#customization) - + [ZSH Support](#zsh-support) - + [ZSH default auto-complete example](#zsh-default-auto-complete-example) - + [ZSH custom auto-complete example](#zsh-custom-auto-complete-example) - + [PowerShell Support](#powershell-support) - * [Generated Help Text](#generated-help-text) - + [Customization](#customization-1) - * [Version Flag](#version-flag) - + [Customization](#customization-2) - * [Timestamp Flag](#timestamp-flag) - * [Full API Example](#full-api-example) - - - -## Migrating From Older Releases - -There are a small set of breaking changes between v1 and v2. -Converting is relatively straightforward and typically takes less than -an hour. Specific steps are included in -[Migration Guide: v1 to v2](../migrate-v1-to-v2.md). Also see the [pkg.go.dev docs](https://pkg.go.dev/github.com/urfave/cli/v2) for v2 API documentation. +# v2 guide ## Getting Started @@ -1456,6 +1409,13 @@ In this example the flag could be used like this : Side note: quotes may be necessary around the date depending on your layout (if you have spaces for instance) +### Suggestions + +To enable flag and command suggestions, set `app.Suggest = true`. If the suggest +feature is enabled, then the help output of the corresponding command will +provide an appropriate suggestion for the provided flag or subcommand if +available. + ### Full API Example **Notice**: This is a contrived (functioning) example meant strictly for API @@ -1713,3 +1673,11 @@ func wopAction(c *cli.Context) error { return nil } ``` + +## Migrating From Older Releases + +There are a small set of breaking changes between v1 and v2. +Converting is relatively straightforward and typically takes less than +an hour. Specific steps are included in +[Migration Guide: v1 to v2](../migrate-v1-to-v2.md). Also see the [pkg.go.dev docs](https://pkg.go.dev/github.com/urfave/cli/v2) for v2 API documentation. + diff --git a/flag_bool.go b/flag_bool.go index f984acfc2f..12775043f2 100644 --- a/flag_bool.go +++ b/flag_bool.go @@ -6,11 +6,6 @@ import ( "strconv" ) -// IsRequired returns whether or not the flag is required -func (f *BoolFlag) IsRequired() bool { - return f.Required -} - // TakesValue returns true of the flag takes a value, otherwise false func (f *BoolFlag) TakesValue() bool { return false @@ -27,11 +22,6 @@ func (f *BoolFlag) GetValue() string { return "" } -// IsVisible returns true if the flag is not hidden, otherwise false -func (f *BoolFlag) IsVisible() bool { - return !f.Hidden -} - // GetDefaultText returns the default text for this flag func (f *BoolFlag) GetDefaultText() string { if f.DefaultText != "" { diff --git a/flag_duration.go b/flag_duration.go index a6677ad288..236056cd4b 100644 --- a/flag_duration.go +++ b/flag_duration.go @@ -6,11 +6,6 @@ import ( "time" ) -// IsRequired returns whether or not the flag is required -func (f *DurationFlag) IsRequired() bool { - return f.Required -} - // TakesValue returns true of the flag takes a value, otherwise false func (f *DurationFlag) TakesValue() bool { return true @@ -27,11 +22,6 @@ func (f *DurationFlag) GetValue() string { return f.Value.String() } -// IsVisible returns true if the flag is not hidden, otherwise false -func (f *DurationFlag) IsVisible() bool { - return !f.Hidden -} - // GetDefaultText returns the default text for this flag func (f *DurationFlag) GetDefaultText() string { if f.DefaultText != "" { diff --git a/flag_float64.go b/flag_float64.go index 62a1973007..aa2f359465 100644 --- a/flag_float64.go +++ b/flag_float64.go @@ -6,11 +6,6 @@ import ( "strconv" ) -// IsRequired returns whether or not the flag is required -func (f *Float64Flag) IsRequired() bool { - return f.Required -} - // TakesValue returns true of the flag takes a value, otherwise false func (f *Float64Flag) TakesValue() bool { return true @@ -40,11 +35,6 @@ func (f *Float64Flag) GetEnvVars() []string { return f.EnvVars } -// IsVisible returns true if the flag is not hidden, otherwise false -func (f *Float64Flag) IsVisible() bool { - return !f.Hidden -} - // Apply populates the flag given the flag set and environment func (f *Float64Flag) Apply(set *flag.FlagSet) error { if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { diff --git a/flag_float64_slice.go b/flag_float64_slice.go index 0f09c8cfe3..e2bfc4c97d 100644 --- a/flag_float64_slice.go +++ b/flag_float64_slice.go @@ -81,11 +81,6 @@ func (f *Float64SliceFlag) String() string { return withEnvHint(f.GetEnvVars(), stringifyFloat64SliceFlag(f)) } -// IsRequired returns whether or not the flag is required -func (f *Float64SliceFlag) IsRequired() bool { - return f.Required -} - // TakesValue returns true if the flag takes a value, otherwise false func (f *Float64SliceFlag) TakesValue() bool { return true @@ -105,11 +100,6 @@ func (f *Float64SliceFlag) GetValue() string { return "" } -// IsVisible returns true if the flag is not hidden, otherwise false -func (f *Float64SliceFlag) IsVisible() bool { - return !f.Hidden -} - // GetDefaultText returns the default text for this flag func (f *Float64SliceFlag) GetDefaultText() string { if f.DefaultText != "" { diff --git a/flag_generic.go b/flag_generic.go index 71e7173166..8be32b8729 100644 --- a/flag_generic.go +++ b/flag_generic.go @@ -11,11 +11,6 @@ type Generic interface { String() string } -// IsRequired returns whether or not the flag is required -func (f *GenericFlag) IsRequired() bool { - return f.Required -} - // TakesValue returns true of the flag takes a value, otherwise false func (f *GenericFlag) TakesValue() bool { return true @@ -35,11 +30,6 @@ func (f *GenericFlag) GetValue() string { return "" } -// IsVisible returns true if the flag is not hidden, otherwise false -func (f *GenericFlag) IsVisible() bool { - return !f.Hidden -} - // GetDefaultText returns the default text for this flag func (f *GenericFlag) GetDefaultText() string { if f.DefaultText != "" { diff --git a/flag_int.go b/flag_int.go index b68c3e86e3..3f5dec551e 100644 --- a/flag_int.go +++ b/flag_int.go @@ -6,11 +6,6 @@ import ( "strconv" ) -// IsRequired returns whether or not the flag is required -func (f *IntFlag) IsRequired() bool { - return f.Required -} - // TakesValue returns true of the flag takes a value, otherwise false func (f *IntFlag) TakesValue() bool { return true @@ -27,11 +22,6 @@ func (f *IntFlag) GetValue() string { return fmt.Sprintf("%d", f.Value) } -// IsVisible returns true if the flag is not hidden, otherwise false -func (f *IntFlag) IsVisible() bool { - return !f.Hidden -} - // GetDefaultText returns the default text for this flag func (f *IntFlag) GetDefaultText() string { if f.DefaultText != "" { diff --git a/flag_int64.go b/flag_int64.go index 3f71134bcc..d005866255 100644 --- a/flag_int64.go +++ b/flag_int64.go @@ -6,11 +6,6 @@ import ( "strconv" ) -// IsRequired returns whether or not the flag is required -func (f *Int64Flag) IsRequired() bool { - return f.Required -} - // TakesValue returns true of the flag takes a value, otherwise false func (f *Int64Flag) TakesValue() bool { return true @@ -27,11 +22,6 @@ func (f *Int64Flag) GetValue() string { return fmt.Sprintf("%d", f.Value) } -// IsVisible returns true if the flag is not hidden, otherwise false -func (f *Int64Flag) IsVisible() bool { - return !f.Hidden -} - // GetDefaultText returns the default text for this flag func (f *Int64Flag) GetDefaultText() string { if f.DefaultText != "" { diff --git a/flag_int64_slice.go b/flag_int64_slice.go index 14d8c41c6a..b61bd7f855 100644 --- a/flag_int64_slice.go +++ b/flag_int64_slice.go @@ -82,11 +82,6 @@ func (f *Int64SliceFlag) String() string { return withEnvHint(f.GetEnvVars(), stringifyInt64SliceFlag(f)) } -// IsRequired returns whether or not the flag is required -func (f *Int64SliceFlag) IsRequired() bool { - return f.Required -} - // TakesValue returns true of the flag takes a value, otherwise false func (f *Int64SliceFlag) TakesValue() bool { return true @@ -106,11 +101,6 @@ func (f *Int64SliceFlag) GetValue() string { return "" } -// IsVisible returns true if the flag is not hidden, otherwise false -func (f *Int64SliceFlag) IsVisible() bool { - return !f.Hidden -} - // GetDefaultText returns the default text for this flag func (f *Int64SliceFlag) GetDefaultText() string { if f.DefaultText != "" { diff --git a/flag_int_slice.go b/flag_int_slice.go index 4ed7f2c59e..f9713cc4d4 100644 --- a/flag_int_slice.go +++ b/flag_int_slice.go @@ -93,11 +93,6 @@ func (f *IntSliceFlag) String() string { return withEnvHint(f.GetEnvVars(), stringifyIntSliceFlag(f)) } -// IsRequired returns whether or not the flag is required -func (f *IntSliceFlag) IsRequired() bool { - return f.Required -} - // TakesValue returns true of the flag takes a value, otherwise false func (f *IntSliceFlag) TakesValue() bool { return true @@ -117,11 +112,6 @@ func (f *IntSliceFlag) GetValue() string { return "" } -// IsVisible returns true if the flag is not hidden, otherwise false -func (f *IntSliceFlag) IsVisible() bool { - return !f.Hidden -} - // GetDefaultText returns the default text for this flag func (f *IntSliceFlag) GetDefaultText() string { if f.DefaultText != "" { diff --git a/flag_path.go b/flag_path.go index 095a59648a..b0c2215b22 100644 --- a/flag_path.go +++ b/flag_path.go @@ -7,11 +7,6 @@ import ( type Path = string -// IsRequired returns whether or not the flag is required -func (f *PathFlag) IsRequired() bool { - return f.Required -} - // TakesValue returns true of the flag takes a value, otherwise false func (f *PathFlag) TakesValue() bool { return true @@ -28,11 +23,6 @@ func (f *PathFlag) GetValue() string { return f.Value } -// IsVisible returns true if the flag is not hidden, otherwise false -func (f *PathFlag) IsVisible() bool { - return !f.Hidden -} - // GetDefaultText returns the default text for this flag func (f *PathFlag) GetDefaultText() string { if f.DefaultText != "" { diff --git a/flag_string.go b/flag_string.go index 4831c17959..24adbe9055 100644 --- a/flag_string.go +++ b/flag_string.go @@ -5,11 +5,6 @@ import ( "fmt" ) -// IsRequired returns whether or not the flag is required -func (f *StringFlag) IsRequired() bool { - return f.Required -} - // TakesValue returns true of the flag takes a value, otherwise false func (f *StringFlag) TakesValue() bool { return true @@ -26,11 +21,6 @@ func (f *StringFlag) GetValue() string { return f.Value } -// IsVisible returns true if the flag is not hidden, otherwise false -func (f *StringFlag) IsVisible() bool { - return !f.Hidden -} - // GetDefaultText returns the default text for this flag func (f *StringFlag) GetDefaultText() string { if f.DefaultText != "" { diff --git a/flag_string_slice.go b/flag_string_slice.go index 9e69d0008b..d0195d5479 100644 --- a/flag_string_slice.go +++ b/flag_string_slice.go @@ -76,11 +76,6 @@ func (f *StringSliceFlag) String() string { return withEnvHint(f.GetEnvVars(), stringifyStringSliceFlag(f)) } -// IsRequired returns whether or not the flag is required -func (f *StringSliceFlag) IsRequired() bool { - return f.Required -} - // TakesValue returns true of the flag takes a value, otherwise false func (f *StringSliceFlag) TakesValue() bool { return true @@ -100,11 +95,6 @@ func (f *StringSliceFlag) GetValue() string { return "" } -// IsVisible returns true if the flag is not hidden, otherwise false -func (f *StringSliceFlag) IsVisible() bool { - return !f.Hidden -} - // GetDefaultText returns the default text for this flag func (f *StringSliceFlag) GetDefaultText() string { if f.DefaultText != "" { diff --git a/flag_timestamp.go b/flag_timestamp.go index 32899f5321..ed480cfc94 100644 --- a/flag_timestamp.go +++ b/flag_timestamp.go @@ -58,11 +58,6 @@ func (t *Timestamp) Get() interface{} { return *t } -// IsRequired returns whether or not the flag is required -func (f *TimestampFlag) IsRequired() bool { - return f.Required -} - // TakesValue returns true of the flag takes a value, otherwise false func (f *TimestampFlag) TakesValue() bool { return true @@ -82,11 +77,6 @@ func (f *TimestampFlag) GetValue() string { return "" } -// IsVisible returns true if the flag is not hidden, otherwise false -func (f *TimestampFlag) IsVisible() bool { - return !f.Hidden -} - // GetDefaultText returns the default text for this flag func (f *TimestampFlag) GetDefaultText() string { if f.DefaultText != "" { diff --git a/flag_uint.go b/flag_uint.go index 625f11c15d..1ec9713f8b 100644 --- a/flag_uint.go +++ b/flag_uint.go @@ -6,11 +6,6 @@ import ( "strconv" ) -// IsRequired returns whether or not the flag is required -func (f *UintFlag) IsRequired() bool { - return f.Required -} - // TakesValue returns true of the flag takes a value, otherwise false func (f *UintFlag) TakesValue() bool { return true @@ -21,11 +16,6 @@ func (f *UintFlag) GetUsage() string { return f.Usage } -// IsVisible returns true if the flag is not hidden, otherwise false -func (f *UintFlag) IsVisible() bool { - return !f.Hidden -} - // Apply populates the flag given the flag set and environment func (f *UintFlag) Apply(set *flag.FlagSet) error { if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { diff --git a/flag_uint64.go b/flag_uint64.go index 58969a5d13..55ba08a0c9 100644 --- a/flag_uint64.go +++ b/flag_uint64.go @@ -6,11 +6,6 @@ import ( "strconv" ) -// IsRequired returns whether or not the flag is required -func (f *Uint64Flag) IsRequired() bool { - return f.Required -} - // TakesValue returns true of the flag takes a value, otherwise false func (f *Uint64Flag) TakesValue() bool { return true @@ -21,11 +16,6 @@ func (f *Uint64Flag) GetUsage() string { return f.Usage } -// IsVisible returns true if the flag is not hidden, otherwise false -func (f *Uint64Flag) IsVisible() bool { - return !f.Hidden -} - // Apply populates the flag given the flag set and environment func (f *Uint64Flag) Apply(set *flag.FlagSet) error { if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { diff --git a/go.mod b/go.mod index 3455d264f4..f9111b0b37 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.18 require ( github.com/BurntSushi/toml v1.1.0 + github.com/antzucaro/matchr v0.0.0-20210222213004-b04723ef80f0 github.com/cpuguy83/go-md2man/v2 v2.0.1 golang.org/x/text v0.3.7 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index 2c20c28ff3..afac06b6c4 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,17 @@ github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/antzucaro/matchr v0.0.0-20180616170659-cbc221335f3c h1:CucViv7orgFBMkehuFFdkCVF5ERovbkRRyhvaYaHu/k= +github.com/antzucaro/matchr v0.0.0-20180616170659-cbc221335f3c/go.mod h1:bV/CkX4+ANGDaBwbHkt9kK287al/i9BsB18PRBvyqYo= +github.com/antzucaro/matchr v0.0.0-20210222213004-b04723ef80f0 h1:R/qAiUxFT3mNgQaNqJe0IVznjKRNm23ohAIh9lgtlzc= +github.com/antzucaro/matchr v0.0.0-20210222213004-b04723ef80f0/go.mod h1:v3ZDlfVAL1OrkKHbGSFFK60k0/7hruHPDq2XMs9Gu6U= github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc= diff --git a/godoc-current.txt b/godoc-current.txt index 1bddc54736..90700ebde7 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -305,6 +305,8 @@ type App struct { // single-character bool arguments into one // i.e. foobar -o -v -> foobar -ov UseShortOptionHandling bool + // Enable suggestions for commands and flags + Suggest bool // Has unexported fields. } diff --git a/help.go b/help.go index 4b03abea1d..26c497befb 100644 --- a/help.go +++ b/help.go @@ -10,9 +10,14 @@ import ( "unicode/utf8" ) +const ( + helpName = "help" + helpAlias = "h" +) + var helpCommand = &Command{ - Name: "help", - Aliases: []string{"h"}, + Name: helpName, + Aliases: []string{helpAlias}, Usage: "Shows a list of commands or help for one command", ArgsUsage: "[command]", Action: func(cCtx *Context) error { @@ -27,8 +32,8 @@ var helpCommand = &Command{ } var helpSubcommand = &Command{ - Name: "help", - Aliases: []string{"h"}, + Name: helpName, + Aliases: []string{helpAlias}, Usage: "Shows a list of commands or help for one command", ArgsUsage: "[command]", Action: func(cCtx *Context) error { @@ -214,7 +219,13 @@ func ShowCommandHelp(ctx *Context, command string) error { } if ctx.App.CommandNotFound == nil { - return Exit(fmt.Sprintf("No help topic for '%v'", command), 3) + errMsg := fmt.Sprintf("No help topic for '%v'", command) + if ctx.App.Suggest { + if suggestion := suggestCommand(ctx.App.Commands, command); suggestion != "" { + errMsg += ". " + suggestion + } + } + return Exit(errMsg, 3) } ctx.App.CommandNotFound(ctx, command) diff --git a/internal/build/build.go b/internal/build/build.go index 929ab0fbf9..26967605b5 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -63,10 +63,6 @@ func main() { Name: "gfmrun", Action: GfmrunActionFunc, }, - { - Name: "toc", - Action: TocActionFunc, - }, { Name: "check-binary-size", Action: checkBinarySizeActionFunc, @@ -216,15 +212,6 @@ func GfmrunActionFunc(c *cli.Context) error { return runCmd("gfmrun", "-c", fmt.Sprint(counter), "-s", filename) } -func TocActionFunc(c *cli.Context) error { - filename := c.Args().Get(0) - if filename == "" { - filename = "README.md" - } - - return runCmd("markdown-toc", "-i", filename) -} - // checkBinarySizeActionFunc checks the size of an example binary to ensure that we are keeping size down // this was originally inspired by https://github.com/urfave/cli/issues/1055, and followed up on as a part // of https://github.com/urfave/cli/issues/1057 diff --git a/internal/genflags/generated.gotmpl b/internal/genflags/generated.gotmpl index 5d6f0dde1a..99c292aa7c 100644 --- a/internal/genflags/generated.gotmpl +++ b/internal/genflags/generated.gotmpl @@ -31,7 +31,7 @@ type {{.TypeName}} struct { func (f *{{.TypeName}}) String() string { return {{$.UrfaveCLINamespace}}FlagStringer(f) } -{{end}} +{{end}}{{/* /if .GenerateFmtStringerInterface */}} {{if .GenerateFlagInterface}} // IsSet returns whether or not the flag has been set through env or file @@ -45,6 +45,20 @@ func (f *{{.TypeName}}) Names() []string { } {{end}}{{/* /if .GenerateFlagInterface */}} + +{{if .GenerateRequiredFlagInterface}} +// IsRequired returns whether or not the flag is required +func (f *{{.TypeName}}) IsRequired() bool { + return f.Required +} +{{end}}{{/* /if .GenerateRequiredFlagInterface */}} + +{{if .GenerateVisibleFlagInterface}} +// IsVisible returns true if the flag is not hidden, otherwise false +func (f *{{.TypeName}}) IsVisible() bool { + return !f.Hidden +} +{{end}}{{/* /if .GenerateVisibleFlagInterface */}} {{end}}{{/* /range .SortedFlagTypes */}} // vim{{/* 👻 */}}:ro diff --git a/internal/genflags/generated_test.gotmpl b/internal/genflags/generated_test.gotmpl index 44e9ad4428..52de4e4bf4 100644 --- a/internal/genflags/generated_test.gotmpl +++ b/internal/genflags/generated_test.gotmpl @@ -19,6 +19,22 @@ func Test{{.TypeName}}_SatisfiesFmtStringerInterface(t *testing.T) { _ = f.String() } {{end}} + +{{if .GenerateRequiredFlagInterface}} +func Test{{.TypeName}}_SatisfiesRequiredFlagInterface(t *testing.T) { + var f {{$.UrfaveCLITestNamespace}}RequiredFlag = &{{$.UrfaveCLITestNamespace}}{{.TypeName}}{} + + _ = f.IsRequired() +} +{{end}} + +{{if .GenerateVisibleFlagInterface}} +func Test{{.TypeName}}_SatisfiesVisibleFlagInterface(t *testing.T) { + var f {{$.UrfaveCLITestNamespace}}VisibleFlag = &{{$.UrfaveCLITestNamespace}}{{.TypeName}}{} + + _ = f.IsVisible() +} +{{end}} {{end}} // vim{{/* 👻 */}}:ro diff --git a/internal/genflags/spec.go b/internal/genflags/spec.go index d9f18db03f..a7afb22143 100644 --- a/internal/genflags/spec.go +++ b/internal/genflags/spec.go @@ -83,6 +83,14 @@ func (ft *FlagType) GenerateFlagInterface() bool { return ft.skipInterfaceNamed("Flag") } +func (ft *FlagType) GenerateRequiredFlagInterface() bool { + return ft.skipInterfaceNamed("RequiredFlag") +} + +func (ft *FlagType) GenerateVisibleFlagInterface() bool { + return ft.skipInterfaceNamed("VisibleFlag") +} + func (ft *FlagType) skipInterfaceNamed(name string) bool { if ft.Config == nil { return true diff --git a/mkdocs-requirements.txt b/mkdocs-requirements.txt new file mode 100644 index 0000000000..482ad0622d --- /dev/null +++ b/mkdocs-requirements.txt @@ -0,0 +1,5 @@ +mkdocs-git-revision-date-localized-plugin~=1.0 +mkdocs-material-extensions~=1.0 +mkdocs-material~=8.2 +mkdocs~=1.3 +pygments~=2.12 diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000000..73b88c5093 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,62 @@ +# NOTE: the mkdocs dependencies will need to be installed out of +# band until this whole thing gets more automated: +# +# pip install -r mkdocs-requirements.txt +# + +site_name: urfave/cli +site_url: https://cli.urfave.org/ +repo_url: https://github.com/urfave/cli +edit_uri: edit/main/docs/ +nav: + - Home: index.md + - v2 Manual: v2/index.md + - v1 Manual: v1/index.md +theme: + name: material + palette: + - media: "(prefers-color-scheme: light)" + scheme: default + toggle: + icon: material/brightness-4 + name: dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + toggle: + icon: material/brightness-7 + name: light mode +plugins: + - git-revision-date-localized + - search +# NOTE: this is the recommended configuration from +# https://squidfunk.github.io/mkdocs-material/setup/extensions/#recommended-configuration +markdown_extensions: + - abbr + - admonition + - attr_list + - def_list + - footnotes + - meta + - md_in_html + - toc: + permalink: true + - pymdownx.arithmatex: + generic: true + - pymdownx.betterem: + smart_enable: all + - pymdownx.caret + - pymdownx.details + - pymdownx.emoji: + emoji_index: !!python/name:materialx.emoji.twemoji + emoji_generator: !!python/name:materialx.emoji.to_svg + - pymdownx.highlight + - pymdownx.inlinehilite + - pymdownx.keys + - pymdownx.mark + - pymdownx.smartsymbols + - pymdownx.superfences + - pymdownx.tabbed: + alternate_style: true + - pymdownx.tasklist: + custom_checkbox: true + - pymdownx.tilde diff --git a/parse.go b/parse.go index 7df17296a4..a2db306e12 100644 --- a/parse.go +++ b/parse.go @@ -26,9 +26,8 @@ func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComple return err } - errStr := err.Error() - trimmed := strings.TrimPrefix(errStr, "flag provided but not defined: -") - if errStr == trimmed { + trimmed, trimErr := flagFromError(err) + if trimErr != nil { return err } @@ -67,6 +66,19 @@ func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComple } } +const providedButNotDefinedErrMsg = "flag provided but not defined: -" + +// flagFromError tries to parse a provided flag from an error message. If the +// parsing fials, it returns the input error and an empty string +func flagFromError(err error) (string, error) { + errStr := err.Error() + trimmed := strings.TrimPrefix(errStr, providedButNotDefinedErrMsg) + if errStr == trimmed { + return "", err + } + return trimmed, nil +} + func splitShortOptions(set *flag.FlagSet, arg string) []string { shortFlagsExist := func(s string) bool { for _, c := range s[1:] { diff --git a/suggestions.go b/suggestions.go new file mode 100644 index 0000000000..476af4de50 --- /dev/null +++ b/suggestions.go @@ -0,0 +1,75 @@ +package cli + +import ( + "fmt" + + "github.com/antzucaro/matchr" +) + +const didYouMeanTemplate = "Did you mean '%s'?" + +func (a *App) suggestFlagFromError(err error, command string) (string, error) { + flag, parseErr := flagFromError(err) + if parseErr != nil { + return "", err + } + + flags := a.Flags + if command != "" { + cmd := a.Command(command) + if cmd == nil { + return "", err + } + flags = cmd.Flags + } + + suggestion := a.suggestFlag(flags, flag) + if len(suggestion) == 0 { + return "", err + } + + return fmt.Sprintf(didYouMeanTemplate+"\n\n", suggestion), nil +} + +func (a *App) suggestFlag(flags []Flag, provided string) (suggestion string) { + distance := 0.0 + + for _, flag := range flags { + flagNames := flag.Names() + if !a.HideHelp { + flagNames = append(flagNames, HelpFlag.Names()...) + } + for _, name := range flagNames { + newDistance := matchr.JaroWinkler(name, provided, true) + if newDistance > distance { + distance = newDistance + suggestion = name + } + } + } + + if len(suggestion) == 1 { + suggestion = "-" + suggestion + } else if len(suggestion) > 1 { + suggestion = "--" + suggestion + } + + return suggestion +} + +// suggestCommand takes a list of commands and a provided string to suggest a +// command name +func suggestCommand(commands []*Command, provided string) (suggestion string) { + distance := 0.0 + for _, command := range commands { + for _, name := range append(command.Names(), helpName, helpAlias) { + newDistance := matchr.JaroWinkler(name, provided, true) + if newDistance > distance { + distance = newDistance + suggestion = name + } + } + } + + return fmt.Sprintf(didYouMeanTemplate, suggestion) +} diff --git a/suggestions_test.go b/suggestions_test.go new file mode 100644 index 0000000000..4ebe9c0c7f --- /dev/null +++ b/suggestions_test.go @@ -0,0 +1,188 @@ +package cli + +import ( + "errors" + "fmt" + "testing" +) + +func TestSuggestFlag(t *testing.T) { + // Given + app := testApp() + + for _, testCase := range []struct { + provided, expected string + }{ + {"", ""}, + {"a", "--another-flag"}, + {"hlp", "--help"}, + {"k", ""}, + {"s", "-s"}, + } { + // When + res := app.suggestFlag(app.Flags, testCase.provided) + + // Then + expect(t, res, testCase.expected) + } +} + +func TestSuggestFlagHideHelp(t *testing.T) { + // Given + app := testApp() + app.HideHelp = true + + // When + res := app.suggestFlag(app.Flags, "hlp") + + // Then + expect(t, res, "--fl") +} + +func TestSuggestFlagFromError(t *testing.T) { + // Given + app := testApp() + + for _, testCase := range []struct { + command, provided, expected string + }{ + {"", "hel", "--help"}, + {"", "soccer", "--socket"}, + {"config", "anot", "--another-flag"}, + } { + // When + res, _ := app.suggestFlagFromError( + errors.New(providedButNotDefinedErrMsg+testCase.provided), + testCase.command, + ) + + // Then + expect(t, res, fmt.Sprintf(didYouMeanTemplate+"\n\n", testCase.expected)) + } +} + +func TestSuggestFlagFromErrorWrongError(t *testing.T) { + // Given + app := testApp() + + // When + _, err := app.suggestFlagFromError(errors.New("invalid"), "") + + // Then + expect(t, true, err != nil) +} + +func TestSuggestFlagFromErrorWrongCommand(t *testing.T) { + // Given + app := testApp() + + // When + _, err := app.suggestFlagFromError( + errors.New(providedButNotDefinedErrMsg+"flag"), + "invalid", + ) + + // Then + expect(t, true, err != nil) +} + +func TestSuggestFlagFromErrorNoSuggestion(t *testing.T) { + // Given + app := testApp() + + // When + _, err := app.suggestFlagFromError( + errors.New(providedButNotDefinedErrMsg+""), + "", + ) + + // Then + expect(t, true, err != nil) +} + +func TestSuggestCommand(t *testing.T) { + // Given + app := testApp() + + for _, testCase := range []struct { + provided, expected string + }{ + {"", ""}, + {"conf", "config"}, + {"i", "i"}, + {"information", "info"}, + {"not-existing", "info"}, + } { + // When + res := suggestCommand(app.Commands, testCase.provided) + + // Then + expect(t, res, fmt.Sprintf(didYouMeanTemplate, testCase.expected)) + } +} + +func ExampleApp_Suggest() { + app := &App{ + Name: "greet", + Suggest: true, + HideHelp: true, + HideHelpCommand: true, + CustomAppHelpTemplate: "(this space intentionally left blank)\n", + Flags: []Flag{ + &StringFlag{Name: "name", Value: "squirrel", Usage: "a name to say"}, + }, + Action: func(cCtx *Context) error { + fmt.Printf("Hello %v\n", cCtx.String("name")) + return nil + }, + } + + app.Run([]string{"greet", "--nema", "chipmunk"}) + // Output: + // Incorrect Usage. flag provided but not defined: -nema + // + // Did you mean '--name'? + // + // (this space intentionally left blank) +} + +func ExampleApp_Suggest_command() { + app := &App{ + Name: "greet", + Suggest: true, + HideHelp: true, + HideHelpCommand: true, + CustomAppHelpTemplate: "(this space intentionally left blank)\n", + Flags: []Flag{ + &StringFlag{Name: "name", Value: "squirrel", Usage: "a name to say"}, + }, + Action: func(cCtx *Context) error { + fmt.Printf("Hello %v\n", cCtx.String("name")) + return nil + }, + Commands: []*Command{ + { + Name: "neighbors", + CustomHelpTemplate: "(this space intentionally left blank)\n", + Flags: []Flag{ + &BoolFlag{Name: "smiling"}, + }, + Action: func(cCtx *Context) error { + if cCtx.Bool("smiling") { + fmt.Println("😀") + } + fmt.Println("Hello, neighbors") + return nil + }, + }, + }, + } + + app.Run([]string{"greet", "neighbors", "--sliming"}) + // Output: + // Incorrect Usage: flag provided but not defined: -sliming + // + // Did you mean '--smiling'? + // + // (this space intentionally left blank) +} diff --git a/testdata/godoc-v2.x.txt b/testdata/godoc-v2.x.txt index 1bddc54736..90700ebde7 100644 --- a/testdata/godoc-v2.x.txt +++ b/testdata/godoc-v2.x.txt @@ -305,6 +305,8 @@ type App struct { // single-character bool arguments into one // i.e. foobar -o -v -> foobar -ov UseShortOptionHandling bool + // Enable suggestions for commands and flags + Suggest bool // Has unexported fields. } diff --git a/zz_generated.flags.go b/zz_generated.flags.go index 703e66a406..6c16c3e75a 100644 --- a/zz_generated.flags.go +++ b/zz_generated.flags.go @@ -33,6 +33,16 @@ func (f *Float64SliceFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } +// IsRequired returns whether or not the flag is required +func (f *Float64SliceFlag) IsRequired() bool { + return f.Required +} + +// IsVisible returns true if the flag is not hidden, otherwise false +func (f *Float64SliceFlag) IsVisible() bool { + return !f.Hidden +} + // GenericFlag is a flag with type Generic type GenericFlag struct { Name string @@ -69,6 +79,16 @@ func (f *GenericFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } +// IsRequired returns whether or not the flag is required +func (f *GenericFlag) IsRequired() bool { + return f.Required +} + +// IsVisible returns true if the flag is not hidden, otherwise false +func (f *GenericFlag) IsVisible() bool { + return !f.Hidden +} + // Int64SliceFlag is a flag with type *Int64Slice type Int64SliceFlag struct { Name string @@ -98,6 +118,16 @@ func (f *Int64SliceFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } +// IsRequired returns whether or not the flag is required +func (f *Int64SliceFlag) IsRequired() bool { + return f.Required +} + +// IsVisible returns true if the flag is not hidden, otherwise false +func (f *Int64SliceFlag) IsVisible() bool { + return !f.Hidden +} + // IntSliceFlag is a flag with type *IntSlice type IntSliceFlag struct { Name string @@ -127,6 +157,16 @@ func (f *IntSliceFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } +// IsRequired returns whether or not the flag is required +func (f *IntSliceFlag) IsRequired() bool { + return f.Required +} + +// IsVisible returns true if the flag is not hidden, otherwise false +func (f *IntSliceFlag) IsVisible() bool { + return !f.Hidden +} + // PathFlag is a flag with type Path type PathFlag struct { Name string @@ -163,6 +203,16 @@ func (f *PathFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } +// IsRequired returns whether or not the flag is required +func (f *PathFlag) IsRequired() bool { + return f.Required +} + +// IsVisible returns true if the flag is not hidden, otherwise false +func (f *PathFlag) IsVisible() bool { + return !f.Hidden +} + // StringSliceFlag is a flag with type *StringSlice type StringSliceFlag struct { Name string @@ -194,6 +244,16 @@ func (f *StringSliceFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } +// IsRequired returns whether or not the flag is required +func (f *StringSliceFlag) IsRequired() bool { + return f.Required +} + +// IsVisible returns true if the flag is not hidden, otherwise false +func (f *StringSliceFlag) IsVisible() bool { + return !f.Hidden +} + // TimestampFlag is a flag with type *Timestamp type TimestampFlag struct { Name string @@ -230,6 +290,16 @@ func (f *TimestampFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } +// IsRequired returns whether or not the flag is required +func (f *TimestampFlag) IsRequired() bool { + return f.Required +} + +// IsVisible returns true if the flag is not hidden, otherwise false +func (f *TimestampFlag) IsVisible() bool { + return !f.Hidden +} + // BoolFlag is a flag with type bool type BoolFlag struct { Name string @@ -264,6 +334,16 @@ func (f *BoolFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } +// IsRequired returns whether or not the flag is required +func (f *BoolFlag) IsRequired() bool { + return f.Required +} + +// IsVisible returns true if the flag is not hidden, otherwise false +func (f *BoolFlag) IsVisible() bool { + return !f.Hidden +} + // Float64Flag is a flag with type float64 type Float64Flag struct { Name string @@ -298,6 +378,16 @@ func (f *Float64Flag) Names() []string { return FlagNames(f.Name, f.Aliases) } +// IsRequired returns whether or not the flag is required +func (f *Float64Flag) IsRequired() bool { + return f.Required +} + +// IsVisible returns true if the flag is not hidden, otherwise false +func (f *Float64Flag) IsVisible() bool { + return !f.Hidden +} + // IntFlag is a flag with type int type IntFlag struct { Name string @@ -332,6 +422,16 @@ func (f *IntFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } +// IsRequired returns whether or not the flag is required +func (f *IntFlag) IsRequired() bool { + return f.Required +} + +// IsVisible returns true if the flag is not hidden, otherwise false +func (f *IntFlag) IsVisible() bool { + return !f.Hidden +} + // Int64Flag is a flag with type int64 type Int64Flag struct { Name string @@ -366,6 +466,16 @@ func (f *Int64Flag) Names() []string { return FlagNames(f.Name, f.Aliases) } +// IsRequired returns whether or not the flag is required +func (f *Int64Flag) IsRequired() bool { + return f.Required +} + +// IsVisible returns true if the flag is not hidden, otherwise false +func (f *Int64Flag) IsVisible() bool { + return !f.Hidden +} + // StringFlag is a flag with type string type StringFlag struct { Name string @@ -402,6 +512,16 @@ func (f *StringFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } +// IsRequired returns whether or not the flag is required +func (f *StringFlag) IsRequired() bool { + return f.Required +} + +// IsVisible returns true if the flag is not hidden, otherwise false +func (f *StringFlag) IsVisible() bool { + return !f.Hidden +} + // DurationFlag is a flag with type time.Duration type DurationFlag struct { Name string @@ -436,6 +556,16 @@ func (f *DurationFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } +// IsRequired returns whether or not the flag is required +func (f *DurationFlag) IsRequired() bool { + return f.Required +} + +// IsVisible returns true if the flag is not hidden, otherwise false +func (f *DurationFlag) IsVisible() bool { + return !f.Hidden +} + // UintFlag is a flag with type uint type UintFlag struct { Name string @@ -470,6 +600,16 @@ func (f *UintFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } +// IsRequired returns whether or not the flag is required +func (f *UintFlag) IsRequired() bool { + return f.Required +} + +// IsVisible returns true if the flag is not hidden, otherwise false +func (f *UintFlag) IsVisible() bool { + return !f.Hidden +} + // Uint64Flag is a flag with type uint64 type Uint64Flag struct { Name string @@ -504,4 +644,14 @@ func (f *Uint64Flag) Names() []string { return FlagNames(f.Name, f.Aliases) } +// IsRequired returns whether or not the flag is required +func (f *Uint64Flag) IsRequired() bool { + return f.Required +} + +// IsVisible returns true if the flag is not hidden, otherwise false +func (f *Uint64Flag) IsVisible() bool { + return !f.Hidden +} + // vim:ro diff --git a/zz_generated.flags_test.go b/zz_generated.flags_test.go index b8363d4a35..1d9afdaa71 100644 --- a/zz_generated.flags_test.go +++ b/zz_generated.flags_test.go @@ -16,6 +16,18 @@ func TestFloat64SliceFlag_SatisfiesFlagInterface(t *testing.T) { _ = f.Names() } +func TestFloat64SliceFlag_SatisfiesRequiredFlagInterface(t *testing.T) { + var f cli.RequiredFlag = &cli.Float64SliceFlag{} + + _ = f.IsRequired() +} + +func TestFloat64SliceFlag_SatisfiesVisibleFlagInterface(t *testing.T) { + var f cli.VisibleFlag = &cli.Float64SliceFlag{} + + _ = f.IsVisible() +} + func TestGenericFlag_SatisfiesFlagInterface(t *testing.T) { var f cli.Flag = &cli.GenericFlag{} @@ -29,6 +41,18 @@ func TestGenericFlag_SatisfiesFmtStringerInterface(t *testing.T) { _ = f.String() } +func TestGenericFlag_SatisfiesRequiredFlagInterface(t *testing.T) { + var f cli.RequiredFlag = &cli.GenericFlag{} + + _ = f.IsRequired() +} + +func TestGenericFlag_SatisfiesVisibleFlagInterface(t *testing.T) { + var f cli.VisibleFlag = &cli.GenericFlag{} + + _ = f.IsVisible() +} + func TestInt64SliceFlag_SatisfiesFlagInterface(t *testing.T) { var f cli.Flag = &cli.Int64SliceFlag{} @@ -36,6 +60,18 @@ func TestInt64SliceFlag_SatisfiesFlagInterface(t *testing.T) { _ = f.Names() } +func TestInt64SliceFlag_SatisfiesRequiredFlagInterface(t *testing.T) { + var f cli.RequiredFlag = &cli.Int64SliceFlag{} + + _ = f.IsRequired() +} + +func TestInt64SliceFlag_SatisfiesVisibleFlagInterface(t *testing.T) { + var f cli.VisibleFlag = &cli.Int64SliceFlag{} + + _ = f.IsVisible() +} + func TestIntSliceFlag_SatisfiesFlagInterface(t *testing.T) { var f cli.Flag = &cli.IntSliceFlag{} @@ -43,6 +79,18 @@ func TestIntSliceFlag_SatisfiesFlagInterface(t *testing.T) { _ = f.Names() } +func TestIntSliceFlag_SatisfiesRequiredFlagInterface(t *testing.T) { + var f cli.RequiredFlag = &cli.IntSliceFlag{} + + _ = f.IsRequired() +} + +func TestIntSliceFlag_SatisfiesVisibleFlagInterface(t *testing.T) { + var f cli.VisibleFlag = &cli.IntSliceFlag{} + + _ = f.IsVisible() +} + func TestPathFlag_SatisfiesFlagInterface(t *testing.T) { var f cli.Flag = &cli.PathFlag{} @@ -56,6 +104,18 @@ func TestPathFlag_SatisfiesFmtStringerInterface(t *testing.T) { _ = f.String() } +func TestPathFlag_SatisfiesRequiredFlagInterface(t *testing.T) { + var f cli.RequiredFlag = &cli.PathFlag{} + + _ = f.IsRequired() +} + +func TestPathFlag_SatisfiesVisibleFlagInterface(t *testing.T) { + var f cli.VisibleFlag = &cli.PathFlag{} + + _ = f.IsVisible() +} + func TestStringSliceFlag_SatisfiesFlagInterface(t *testing.T) { var f cli.Flag = &cli.StringSliceFlag{} @@ -63,6 +123,18 @@ func TestStringSliceFlag_SatisfiesFlagInterface(t *testing.T) { _ = f.Names() } +func TestStringSliceFlag_SatisfiesRequiredFlagInterface(t *testing.T) { + var f cli.RequiredFlag = &cli.StringSliceFlag{} + + _ = f.IsRequired() +} + +func TestStringSliceFlag_SatisfiesVisibleFlagInterface(t *testing.T) { + var f cli.VisibleFlag = &cli.StringSliceFlag{} + + _ = f.IsVisible() +} + func TestTimestampFlag_SatisfiesFlagInterface(t *testing.T) { var f cli.Flag = &cli.TimestampFlag{} @@ -76,6 +148,18 @@ func TestTimestampFlag_SatisfiesFmtStringerInterface(t *testing.T) { _ = f.String() } +func TestTimestampFlag_SatisfiesRequiredFlagInterface(t *testing.T) { + var f cli.RequiredFlag = &cli.TimestampFlag{} + + _ = f.IsRequired() +} + +func TestTimestampFlag_SatisfiesVisibleFlagInterface(t *testing.T) { + var f cli.VisibleFlag = &cli.TimestampFlag{} + + _ = f.IsVisible() +} + func TestBoolFlag_SatisfiesFlagInterface(t *testing.T) { var f cli.Flag = &cli.BoolFlag{} @@ -89,6 +173,18 @@ func TestBoolFlag_SatisfiesFmtStringerInterface(t *testing.T) { _ = f.String() } +func TestBoolFlag_SatisfiesRequiredFlagInterface(t *testing.T) { + var f cli.RequiredFlag = &cli.BoolFlag{} + + _ = f.IsRequired() +} + +func TestBoolFlag_SatisfiesVisibleFlagInterface(t *testing.T) { + var f cli.VisibleFlag = &cli.BoolFlag{} + + _ = f.IsVisible() +} + func TestFloat64Flag_SatisfiesFlagInterface(t *testing.T) { var f cli.Flag = &cli.Float64Flag{} @@ -102,6 +198,18 @@ func TestFloat64Flag_SatisfiesFmtStringerInterface(t *testing.T) { _ = f.String() } +func TestFloat64Flag_SatisfiesRequiredFlagInterface(t *testing.T) { + var f cli.RequiredFlag = &cli.Float64Flag{} + + _ = f.IsRequired() +} + +func TestFloat64Flag_SatisfiesVisibleFlagInterface(t *testing.T) { + var f cli.VisibleFlag = &cli.Float64Flag{} + + _ = f.IsVisible() +} + func TestIntFlag_SatisfiesFlagInterface(t *testing.T) { var f cli.Flag = &cli.IntFlag{} @@ -115,6 +223,18 @@ func TestIntFlag_SatisfiesFmtStringerInterface(t *testing.T) { _ = f.String() } +func TestIntFlag_SatisfiesRequiredFlagInterface(t *testing.T) { + var f cli.RequiredFlag = &cli.IntFlag{} + + _ = f.IsRequired() +} + +func TestIntFlag_SatisfiesVisibleFlagInterface(t *testing.T) { + var f cli.VisibleFlag = &cli.IntFlag{} + + _ = f.IsVisible() +} + func TestInt64Flag_SatisfiesFlagInterface(t *testing.T) { var f cli.Flag = &cli.Int64Flag{} @@ -128,6 +248,18 @@ func TestInt64Flag_SatisfiesFmtStringerInterface(t *testing.T) { _ = f.String() } +func TestInt64Flag_SatisfiesRequiredFlagInterface(t *testing.T) { + var f cli.RequiredFlag = &cli.Int64Flag{} + + _ = f.IsRequired() +} + +func TestInt64Flag_SatisfiesVisibleFlagInterface(t *testing.T) { + var f cli.VisibleFlag = &cli.Int64Flag{} + + _ = f.IsVisible() +} + func TestStringFlag_SatisfiesFlagInterface(t *testing.T) { var f cli.Flag = &cli.StringFlag{} @@ -141,6 +273,18 @@ func TestStringFlag_SatisfiesFmtStringerInterface(t *testing.T) { _ = f.String() } +func TestStringFlag_SatisfiesRequiredFlagInterface(t *testing.T) { + var f cli.RequiredFlag = &cli.StringFlag{} + + _ = f.IsRequired() +} + +func TestStringFlag_SatisfiesVisibleFlagInterface(t *testing.T) { + var f cli.VisibleFlag = &cli.StringFlag{} + + _ = f.IsVisible() +} + func TestDurationFlag_SatisfiesFlagInterface(t *testing.T) { var f cli.Flag = &cli.DurationFlag{} @@ -154,6 +298,18 @@ func TestDurationFlag_SatisfiesFmtStringerInterface(t *testing.T) { _ = f.String() } +func TestDurationFlag_SatisfiesRequiredFlagInterface(t *testing.T) { + var f cli.RequiredFlag = &cli.DurationFlag{} + + _ = f.IsRequired() +} + +func TestDurationFlag_SatisfiesVisibleFlagInterface(t *testing.T) { + var f cli.VisibleFlag = &cli.DurationFlag{} + + _ = f.IsVisible() +} + func TestUintFlag_SatisfiesFlagInterface(t *testing.T) { var f cli.Flag = &cli.UintFlag{} @@ -167,6 +323,18 @@ func TestUintFlag_SatisfiesFmtStringerInterface(t *testing.T) { _ = f.String() } +func TestUintFlag_SatisfiesRequiredFlagInterface(t *testing.T) { + var f cli.RequiredFlag = &cli.UintFlag{} + + _ = f.IsRequired() +} + +func TestUintFlag_SatisfiesVisibleFlagInterface(t *testing.T) { + var f cli.VisibleFlag = &cli.UintFlag{} + + _ = f.IsVisible() +} + func TestUint64Flag_SatisfiesFlagInterface(t *testing.T) { var f cli.Flag = &cli.Uint64Flag{} @@ -180,4 +348,16 @@ func TestUint64Flag_SatisfiesFmtStringerInterface(t *testing.T) { _ = f.String() } +func TestUint64Flag_SatisfiesRequiredFlagInterface(t *testing.T) { + var f cli.RequiredFlag = &cli.Uint64Flag{} + + _ = f.IsRequired() +} + +func TestUint64Flag_SatisfiesVisibleFlagInterface(t *testing.T) { + var f cli.VisibleFlag = &cli.Uint64Flag{} + + _ = f.IsVisible() +} + // vim:ro