chore(deps): update module github.com/urfave/cli/v2 to v2.27.4 (#5)
Some checks are pending
Dev Version / Release (push) Waiting to run

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [github.com/urfave/cli/v2](https://github.com/urfave/cli) | require | minor | `v2.25.7` -> `v2.27.4` |

>  **Important**
>
> Release Notes retrieval for this PR were skipped because no github.com credentials were available.
> If you are self-hosted, please see [this instruction](https://github.com/renovatebot/renovate/blob/master/docs/usage/examples/self-hosting.md#githubcom-token-for-release-notes).

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOC4yMS4yIiwidXBkYXRlZEluVmVyIjoiMzguMjEuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Reviewed-on: #5
Co-authored-by: Renovate Bot <renovate@keks.cloud>
Co-committed-by: Renovate Bot <renovate@keks.cloud>
This commit is contained in:
Renovate Bot 2024-09-14 01:23:23 +00:00 committed by Kekskurse
parent df8ce1f61a
commit b792a8f374
32 changed files with 359 additions and 177 deletions

6
go.mod
View file

@ -7,13 +7,13 @@ require (
github.com/pkg/sftp v1.13.6 github.com/pkg/sftp v1.13.6
github.com/rs/zerolog v1.31.0 github.com/rs/zerolog v1.31.0
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
github.com/urfave/cli/v2 v2.25.7 github.com/urfave/cli/v2 v2.27.4
golang.org/x/crypto v0.14.0 golang.org/x/crypto v0.14.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/kr/fs v0.1.0 // indirect github.com/kr/fs v0.1.0 // indirect
@ -21,6 +21,6 @@ require (
github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-isatty v0.0.19 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
golang.org/x/sys v0.13.0 // indirect golang.org/x/sys v0.13.0 // indirect
) )

6
go.sum
View file

@ -5,6 +5,8 @@ github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQ
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -38,8 +40,12 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8=
github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=

View file

@ -9,6 +9,8 @@ func Render(doc []byte) []byte {
renderer := NewRoffRenderer() renderer := NewRoffRenderer()
return blackfriday.Run(doc, return blackfriday.Run(doc,
[]blackfriday.Option{blackfriday.WithRenderer(renderer), []blackfriday.Option{
blackfriday.WithExtensions(renderer.GetExtensions())}...) blackfriday.WithRenderer(renderer),
blackfriday.WithExtensions(renderer.GetExtensions()),
}...)
} }

View file

@ -1,6 +1,8 @@
package md2man package md2man
import ( import (
"bufio"
"bytes"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -20,34 +22,35 @@ type roffRenderer struct {
} }
const ( const (
titleHeader = ".TH " titleHeader = ".TH "
topLevelHeader = "\n\n.SH " topLevelHeader = "\n\n.SH "
secondLevelHdr = "\n.SH " secondLevelHdr = "\n.SH "
otherHeader = "\n.SS " otherHeader = "\n.SS "
crTag = "\n" crTag = "\n"
emphTag = "\\fI" emphTag = "\\fI"
emphCloseTag = "\\fP" emphCloseTag = "\\fP"
strongTag = "\\fB" strongTag = "\\fB"
strongCloseTag = "\\fP" strongCloseTag = "\\fP"
breakTag = "\n.br\n" breakTag = "\n.br\n"
paraTag = "\n.PP\n" paraTag = "\n.PP\n"
hruleTag = "\n.ti 0\n\\l'\\n(.lu'\n" hruleTag = "\n.ti 0\n\\l'\\n(.lu'\n"
linkTag = "\n\\[la]" linkTag = "\n\\[la]"
linkCloseTag = "\\[ra]" linkCloseTag = "\\[ra]"
codespanTag = "\\fB\\fC" codespanTag = "\\fB"
codespanCloseTag = "\\fR" codespanCloseTag = "\\fR"
codeTag = "\n.PP\n.RS\n\n.nf\n" codeTag = "\n.EX\n"
codeCloseTag = "\n.fi\n.RE\n" codeCloseTag = ".EE\n" // Do not prepend a newline character since code blocks, by definition, include a newline already (or at least as how blackfriday gives us on).
quoteTag = "\n.PP\n.RS\n" quoteTag = "\n.PP\n.RS\n"
quoteCloseTag = "\n.RE\n" quoteCloseTag = "\n.RE\n"
listTag = "\n.RS\n" listTag = "\n.RS\n"
listCloseTag = "\n.RE\n" listCloseTag = "\n.RE\n"
dtTag = "\n.TP\n" dtTag = "\n.TP\n"
dd2Tag = "\n" dd2Tag = "\n"
tableStart = "\n.TS\nallbox;\n" tableStart = "\n.TS\nallbox;\n"
tableEnd = ".TE\n" tableEnd = ".TE\n"
tableCellStart = "T{\n" tableCellStart = "T{\n"
tableCellEnd = "\nT}\n" tableCellEnd = "\nT}\n"
tablePreprocessor = `'\" t`
) )
// NewRoffRenderer creates a new blackfriday Renderer for generating roff documents // NewRoffRenderer creates a new blackfriday Renderer for generating roff documents
@ -74,6 +77,16 @@ func (r *roffRenderer) GetExtensions() blackfriday.Extensions {
// RenderHeader handles outputting the header at document start // RenderHeader handles outputting the header at document start
func (r *roffRenderer) RenderHeader(w io.Writer, ast *blackfriday.Node) { func (r *roffRenderer) RenderHeader(w io.Writer, ast *blackfriday.Node) {
// We need to walk the tree to check if there are any tables.
// If there are, we need to enable the roff table preprocessor.
ast.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
if node.Type == blackfriday.Table {
out(w, tablePreprocessor+"\n")
return blackfriday.Terminate
}
return blackfriday.GoToNext
})
// disable hyphenation // disable hyphenation
out(w, ".nh\n") out(w, ".nh\n")
} }
@ -86,8 +99,7 @@ func (r *roffRenderer) RenderFooter(w io.Writer, ast *blackfriday.Node) {
// RenderNode is called for each node in a markdown document; based on the node // RenderNode is called for each node in a markdown document; based on the node
// type the equivalent roff output is sent to the writer // type the equivalent roff output is sent to the writer
func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus { func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
walkAction := blackfriday.GoToNext
var walkAction = blackfriday.GoToNext
switch node.Type { switch node.Type {
case blackfriday.Text: case blackfriday.Text:
@ -109,9 +121,16 @@ func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering
out(w, strongCloseTag) out(w, strongCloseTag)
} }
case blackfriday.Link: case blackfriday.Link:
if !entering { // Don't render the link text for automatic links, because this
out(w, linkTag+string(node.LinkData.Destination)+linkCloseTag) // will only duplicate the URL in the roff output.
// See https://daringfireball.net/projects/markdown/syntax#autolink
if !bytes.Equal(node.LinkData.Destination, node.FirstChild.Literal) {
out(w, string(node.FirstChild.Literal))
} }
// Hyphens in a link must be escaped to avoid word-wrap in the rendered man page.
escapedLink := strings.ReplaceAll(string(node.LinkData.Destination), "-", "\\-")
out(w, linkTag+escapedLink+linkCloseTag)
walkAction = blackfriday.SkipChildren
case blackfriday.Image: case blackfriday.Image:
// ignore images // ignore images
walkAction = blackfriday.SkipChildren walkAction = blackfriday.SkipChildren
@ -160,6 +179,11 @@ func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering
r.handleTableCell(w, node, entering) r.handleTableCell(w, node, entering)
case blackfriday.HTMLSpan: case blackfriday.HTMLSpan:
// ignore other HTML tags // ignore other HTML tags
case blackfriday.HTMLBlock:
if bytes.HasPrefix(node.Literal, []byte("<!--")) {
break // ignore comments, no warning
}
fmt.Fprintln(os.Stderr, "WARNING: go-md2man does not handle node type "+node.Type.String())
default: default:
fmt.Fprintln(os.Stderr, "WARNING: go-md2man does not handle node type "+node.Type.String()) fmt.Fprintln(os.Stderr, "WARNING: go-md2man does not handle node type "+node.Type.String())
} }
@ -254,7 +278,7 @@ func (r *roffRenderer) handleTableCell(w io.Writer, node *blackfriday.Node, ente
start = "\t" start = "\t"
} }
if node.IsHeader { if node.IsHeader {
start += codespanTag start += strongTag
} else if nodeLiteralSize(node) > 30 { } else if nodeLiteralSize(node) > 30 {
start += tableCellStart start += tableCellStart
} }
@ -262,7 +286,7 @@ func (r *roffRenderer) handleTableCell(w io.Writer, node *blackfriday.Node, ente
} else { } else {
var end string var end string
if node.IsHeader { if node.IsHeader {
end = codespanCloseTag end = strongCloseTag
} else if nodeLiteralSize(node) > 30 { } else if nodeLiteralSize(node) > 30 {
end = tableCellEnd end = tableCellEnd
} }
@ -310,6 +334,28 @@ func out(w io.Writer, output string) {
} }
func escapeSpecialChars(w io.Writer, text []byte) { func escapeSpecialChars(w io.Writer, text []byte) {
scanner := bufio.NewScanner(bytes.NewReader(text))
// count the number of lines in the text
// we need to know this to avoid adding a newline after the last line
n := bytes.Count(text, []byte{'\n'})
idx := 0
for scanner.Scan() {
dt := scanner.Bytes()
if idx < n {
idx++
dt = append(dt, '\n')
}
escapeSpecialCharsLine(w, dt)
}
if err := scanner.Err(); err != nil {
panic(err)
}
}
func escapeSpecialCharsLine(w io.Writer, text []byte) {
for i := 0; i < len(text); i++ { for i := 0; i < len(text); i++ {
// escape initial apostrophe or period // escape initial apostrophe or period
if len(text) >= 1 && (text[0] == '\'' || text[0] == '.') { if len(text) >= 1 && (text[0] == '\'' || text[0] == '.') {

View file

@ -4,6 +4,7 @@
.*envrc .*envrc
.envrc .envrc
.idea .idea
# goimports is installed here if not available
/.local/ /.local/
/site/ /site/
coverage.txt coverage.txt

4
vendor/github.com/urfave/cli/v2/.golangci.yaml generated vendored Normal file
View file

@ -0,0 +1,4 @@
# https://golangci-lint.run/usage/configuration/
linters:
enable:
- misspell

View file

@ -23,8 +23,8 @@ var (
fmt.Sprintf("See %s", appActionDeprecationURL), 2) fmt.Sprintf("See %s", appActionDeprecationURL), 2)
ignoreFlagPrefix = "test." // this is to ignore test flags when adding flags from other packages ignoreFlagPrefix = "test." // this is to ignore test flags when adding flags from other packages
SuggestFlag SuggestFlagFunc = suggestFlag SuggestFlag SuggestFlagFunc = nil // initialized in suggestions.go unless built with urfave_cli_no_suggest
SuggestCommand SuggestCommandFunc = suggestCommand SuggestCommand SuggestCommandFunc = nil // initialized in suggestions.go unless built with urfave_cli_no_suggest
SuggestDidYouMeanTemplate string = suggestDidYouMeanTemplate SuggestDidYouMeanTemplate string = suggestDidYouMeanTemplate
) )
@ -39,6 +39,8 @@ type App struct {
Usage string Usage string
// Text to override the USAGE section of help // Text to override the USAGE section of help
UsageText string UsageText string
// Whether this command supports arguments
Args bool
// Description of the program argument format. // Description of the program argument format.
ArgsUsage string ArgsUsage string
// Version of the program // Version of the program
@ -227,8 +229,6 @@ func (a *App) Setup() {
a.separator.disabled = true a.separator.disabled = true
} }
var newCommands []*Command
for _, c := range a.Commands { for _, c := range a.Commands {
cname := c.Name cname := c.Name
if c.HelpName != "" { if c.HelpName != "" {
@ -237,9 +237,7 @@ func (a *App) Setup() {
c.separator = a.separator c.separator = a.separator
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, cname) c.HelpName = fmt.Sprintf("%s %s", a.HelpName, cname)
c.flagCategories = newFlagCategoriesFromFlags(c.Flags) c.flagCategories = newFlagCategoriesFromFlags(c.Flags)
newCommands = append(newCommands, c)
} }
a.Commands = newCommands
if a.Command(helpCommand.Name) == nil && !a.HideHelp { if a.Command(helpCommand.Name) == nil && !a.HideHelp {
if !a.HideHelpCommand { if !a.HideHelpCommand {
@ -329,6 +327,9 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) {
a.rootCommand = a.newRootCommand() a.rootCommand = a.newRootCommand()
cCtx.Command = a.rootCommand cCtx.Command = a.rootCommand
if err := checkDuplicatedCmds(a.rootCommand); err != nil {
return err
}
return a.rootCommand.Run(cCtx, arguments...) return a.rootCommand.Run(cCtx, arguments...)
} }
@ -363,6 +364,9 @@ func (a *App) suggestFlagFromError(err error, command string) (string, error) {
hideHelp = hideHelp || cmd.HideHelp hideHelp = hideHelp || cmd.HideHelp
} }
if SuggestFlag == nil {
return "", err
}
suggestion := SuggestFlag(flags, flag, hideHelp) suggestion := SuggestFlag(flags, flag, hideHelp)
if len(suggestion) == 0 { if len(suggestion) == 0 {
return "", err return "", err
@ -455,30 +459,6 @@ func (a *App) handleExitCoder(cCtx *Context, err error) {
} }
} }
func (a *App) commandNames() []string {
var cmdNames []string
for _, cmd := range a.Commands {
cmdNames = append(cmdNames, cmd.Names()...)
}
return cmdNames
}
func (a *App) validCommandName(checkCmdName string) bool {
valid := false
allCommandNames := a.commandNames()
for _, cmdName := range allCommandNames {
if checkCmdName == cmdName {
valid = true
break
}
}
return valid
}
func (a *App) argsWithDefaultCommand(oldArgs Args) Args { func (a *App) argsWithDefaultCommand(oldArgs Args) Args {
if a.DefaultCommand != "" { if a.DefaultCommand != "" {
rawArgs := append([]string{a.DefaultCommand}, oldArgs.Slice()...) rawArgs := append([]string{a.DefaultCommand}, oldArgs.Slice()...)

View file

@ -104,17 +104,17 @@ func newFlagCategoriesFromFlags(fs []Flag) FlagCategories {
var categorized bool var categorized bool
for _, fl := range fs { for _, fl := range fs {
if cf, ok := fl.(CategorizableFlag); ok { if cf, ok := fl.(CategorizableFlag); ok {
if cat := cf.GetCategory(); cat != "" { if cat := cf.GetCategory(); cat != "" && cf.IsVisible() {
fc.AddFlag(cat, cf) fc.AddFlag(cat, cf)
categorized = true categorized = true
} }
} }
} }
if categorized == true { if categorized {
for _, fl := range fs { for _, fl := range fs {
if cf, ok := fl.(CategorizableFlag); ok { if cf, ok := fl.(CategorizableFlag); ok {
if cf.GetCategory() == "" { if cf.GetCategory() == "" && cf.IsVisible() {
fc.AddFlag("", fl) fc.AddFlag("", fl)
} }
} }

View file

@ -20,6 +20,8 @@ type Command struct {
UsageText string UsageText string
// A longer explanation of how the command works // A longer explanation of how the command works
Description string Description string
// Whether this command supports arguments
Args bool
// A short description of the arguments of this command // A short description of the arguments of this command
ArgsUsage string ArgsUsage string
// The category the command is part of // The category the command is part of
@ -130,15 +132,12 @@ func (c *Command) setup(ctx *Context) {
} }
sort.Sort(c.categories.(*commandCategories)) sort.Sort(c.categories.(*commandCategories))
var newCmds []*Command
for _, scmd := range c.Subcommands { for _, scmd := range c.Subcommands {
if scmd.HelpName == "" { if scmd.HelpName == "" {
scmd.HelpName = fmt.Sprintf("%s %s", c.HelpName, scmd.Name) scmd.HelpName = fmt.Sprintf("%s %s", c.HelpName, scmd.Name)
} }
scmd.separator = c.separator scmd.separator = c.separator
newCmds = append(newCmds, scmd)
} }
c.Subcommands = newCmds
if c.BashComplete == nil { if c.BashComplete == nil {
c.BashComplete = DefaultCompleteWithFlags(c) c.BashComplete = DefaultCompleteWithFlags(c)
@ -149,6 +148,9 @@ func (c *Command) Run(cCtx *Context, arguments ...string) (err error) {
if !c.isRoot { if !c.isRoot {
c.setup(cCtx) c.setup(cCtx)
if err := checkDuplicatedCmds(c); err != nil {
return err
}
} }
a := args(arguments) a := args(arguments)
@ -404,3 +406,16 @@ func hasCommand(commands []*Command, command *Command) bool {
return false return false
} }
func checkDuplicatedCmds(parent *Command) error {
seen := make(map[string]struct{})
for _, c := range parent.Subcommands {
for _, name := range c.Names() {
if _, exists := seen[name]; exists {
return fmt.Errorf("parent command [%s] has duplicated subcommand name or alias: %s", parent.Name, name)
}
seen[name] = struct{}{}
}
}
return nil
}

View file

@ -144,7 +144,7 @@ func (cCtx *Context) Lineage() []*Context {
return lineage return lineage
} }
// Count returns the num of occurences of this flag // Count returns the num of occurrences of this flag
func (cCtx *Context) Count(name string) int { func (cCtx *Context) Count(name string) int {
if fs := cCtx.lookupFlagSet(name); fs != nil { if fs := cCtx.lookupFlagSet(name); fs != nil {
if cf, ok := fs.Lookup(name).Value.(Countable); ok { if cf, ok := fs.Lookup(name).Value.(Countable); ok {

View file

@ -84,7 +84,10 @@ func (f *BoolFlag) GetDefaultText() string {
if f.DefaultText != "" { if f.DefaultText != "" {
return f.DefaultText return f.DefaultText
} }
return fmt.Sprintf("%v", f.defaultValue) if f.defaultValueSet {
return fmt.Sprintf("%v", f.defaultValue)
}
return fmt.Sprintf("%v", f.Value)
} }
// GetEnvVars returns the env vars for this flag // GetEnvVars returns the env vars for this flag
@ -105,6 +108,7 @@ func (f *BoolFlag) RunAction(c *Context) error {
func (f *BoolFlag) Apply(set *flag.FlagSet) error { func (f *BoolFlag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it // set default value so that environment wont be able to overwrite it
f.defaultValue = f.Value f.defaultValue = f.Value
f.defaultValueSet = true
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {

View file

@ -32,7 +32,10 @@ func (f *DurationFlag) GetDefaultText() string {
if f.DefaultText != "" { if f.DefaultText != "" {
return f.DefaultText return f.DefaultText
} }
return f.defaultValue.String() if f.defaultValueSet {
return f.defaultValue.String()
}
return f.Value.String()
} }
// GetEnvVars returns the env vars for this flag // GetEnvVars returns the env vars for this flag
@ -44,6 +47,7 @@ func (f *DurationFlag) GetEnvVars() []string {
func (f *DurationFlag) Apply(set *flag.FlagSet) error { func (f *DurationFlag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it // set default value so that environment wont be able to overwrite it
f.defaultValue = f.Value f.defaultValue = f.Value
f.defaultValueSet = true
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {

View file

@ -32,7 +32,10 @@ func (f *Float64Flag) GetDefaultText() string {
if f.DefaultText != "" { if f.DefaultText != "" {
return f.DefaultText return f.DefaultText
} }
return f.GetValue() if f.defaultValueSet {
return fmt.Sprintf("%v", f.defaultValue)
}
return fmt.Sprintf("%v", f.Value)
} }
// GetEnvVars returns the env vars for this flag // GetEnvVars returns the env vars for this flag
@ -42,6 +45,9 @@ func (f *Float64Flag) GetEnvVars() []string {
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *Float64Flag) Apply(set *flag.FlagSet) error { func (f *Float64Flag) Apply(set *flag.FlagSet) error {
f.defaultValue = f.Value
f.defaultValueSet = true
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {
valFloat, err := strconv.ParseFloat(val, 64) valFloat, err := strconv.ParseFloat(val, 64)

View file

@ -53,9 +53,15 @@ func (f *GenericFlag) GetDefaultText() string {
if f.DefaultText != "" { if f.DefaultText != "" {
return f.DefaultText return f.DefaultText
} }
if f.defaultValue != nil { val := f.Value
return f.defaultValue.String() if f.defaultValueSet {
val = f.defaultValue
} }
if val != nil {
return val.String()
}
return "" return ""
} }
@ -70,6 +76,7 @@ func (f *GenericFlag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it // set default value so that environment wont be able to overwrite it
if f.Value != nil { if f.Value != nil {
f.defaultValue = &stringGeneric{value: f.Value.String()} f.defaultValue = &stringGeneric{value: f.Value.String()}
f.defaultValueSet = true
} }
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {

View file

@ -32,7 +32,10 @@ func (f *IntFlag) GetDefaultText() string {
if f.DefaultText != "" { if f.DefaultText != "" {
return f.DefaultText return f.DefaultText
} }
return fmt.Sprintf("%d", f.defaultValue) if f.defaultValueSet {
return fmt.Sprintf("%d", f.defaultValue)
}
return fmt.Sprintf("%d", f.Value)
} }
// GetEnvVars returns the env vars for this flag // GetEnvVars returns the env vars for this flag
@ -44,6 +47,7 @@ func (f *IntFlag) GetEnvVars() []string {
func (f *IntFlag) Apply(set *flag.FlagSet) error { func (f *IntFlag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it // set default value so that environment wont be able to overwrite it
f.defaultValue = f.Value f.defaultValue = f.Value
f.defaultValueSet = true
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {

View file

@ -32,7 +32,10 @@ func (f *Int64Flag) GetDefaultText() string {
if f.DefaultText != "" { if f.DefaultText != "" {
return f.DefaultText return f.DefaultText
} }
return fmt.Sprintf("%d", f.defaultValue) if f.defaultValueSet {
return fmt.Sprintf("%d", f.defaultValue)
}
return fmt.Sprintf("%d", f.Value)
} }
// GetEnvVars returns the env vars for this flag // GetEnvVars returns the env vars for this flag
@ -44,6 +47,7 @@ func (f *Int64Flag) GetEnvVars() []string {
func (f *Int64Flag) Apply(set *flag.FlagSet) error { func (f *Int64Flag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it // set default value so that environment wont be able to overwrite it
f.defaultValue = f.Value f.defaultValue = f.Value
f.defaultValueSet = true
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {

View file

@ -33,10 +33,14 @@ func (f *PathFlag) GetDefaultText() string {
if f.DefaultText != "" { if f.DefaultText != "" {
return f.DefaultText return f.DefaultText
} }
if f.defaultValue == "" { val := f.Value
return f.defaultValue if f.defaultValueSet {
val = f.defaultValue
} }
return fmt.Sprintf("%q", f.defaultValue) if val == "" {
return val
}
return fmt.Sprintf("%q", val)
} }
// GetEnvVars returns the env vars for this flag // GetEnvVars returns the env vars for this flag
@ -48,6 +52,7 @@ func (f *PathFlag) GetEnvVars() []string {
func (f *PathFlag) Apply(set *flag.FlagSet) error { func (f *PathFlag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it // set default value so that environment wont be able to overwrite it
f.defaultValue = f.Value f.defaultValue = f.Value
f.defaultValueSet = true
if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
f.Value = val f.Value = val

View file

@ -31,10 +31,15 @@ func (f *StringFlag) GetDefaultText() string {
if f.DefaultText != "" { if f.DefaultText != "" {
return f.DefaultText return f.DefaultText
} }
if f.defaultValue == "" { val := f.Value
return f.defaultValue if f.defaultValueSet {
val = f.defaultValue
} }
return fmt.Sprintf("%q", f.defaultValue)
if val == "" {
return val
}
return fmt.Sprintf("%q", val)
} }
// GetEnvVars returns the env vars for this flag // GetEnvVars returns the env vars for this flag
@ -46,6 +51,7 @@ func (f *StringFlag) GetEnvVars() []string {
func (f *StringFlag) Apply(set *flag.FlagSet) error { func (f *StringFlag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it // set default value so that environment wont be able to overwrite it
f.defaultValue = f.Value f.defaultValue = f.Value
f.defaultValueSet = true
if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
f.Value = val f.Value = val

View file

@ -120,8 +120,13 @@ func (f *TimestampFlag) GetDefaultText() string {
if f.DefaultText != "" { if f.DefaultText != "" {
return f.DefaultText return f.DefaultText
} }
if f.defaultValue != nil && f.defaultValue.timestamp != nil { val := f.Value
return f.defaultValue.timestamp.String() if f.defaultValueSet {
val = f.defaultValue
}
if val != nil && val.timestamp != nil {
return val.timestamp.String()
} }
return "" return ""
@ -144,6 +149,7 @@ func (f *TimestampFlag) Apply(set *flag.FlagSet) error {
f.Value.SetLocation(f.Timezone) f.Value.SetLocation(f.Timezone)
f.defaultValue = f.Value.clone() f.defaultValue = f.Value.clone()
f.defaultValueSet = true
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if err := f.Value.Set(val); err != nil { if err := f.Value.Set(val); err != nil {

View file

@ -25,6 +25,7 @@ func (f *UintFlag) GetCategory() string {
func (f *UintFlag) Apply(set *flag.FlagSet) error { func (f *UintFlag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it // set default value so that environment wont be able to overwrite it
f.defaultValue = f.Value f.defaultValue = f.Value
f.defaultValueSet = true
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {
@ -69,7 +70,10 @@ func (f *UintFlag) GetDefaultText() string {
if f.DefaultText != "" { if f.DefaultText != "" {
return f.DefaultText return f.DefaultText
} }
return fmt.Sprintf("%d", f.defaultValue) if f.defaultValueSet {
return fmt.Sprintf("%d", f.defaultValue)
}
return fmt.Sprintf("%d", f.Value)
} }
// GetEnvVars returns the env vars for this flag // GetEnvVars returns the env vars for this flag

View file

@ -25,6 +25,7 @@ func (f *Uint64Flag) GetCategory() string {
func (f *Uint64Flag) Apply(set *flag.FlagSet) error { func (f *Uint64Flag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it // set default value so that environment wont be able to overwrite it
f.defaultValue = f.Value f.defaultValue = f.Value
f.defaultValueSet = true
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {
@ -69,7 +70,10 @@ func (f *Uint64Flag) GetDefaultText() string {
if f.DefaultText != "" { if f.DefaultText != "" {
return f.DefaultText return f.DefaultText
} }
return fmt.Sprintf("%d", f.defaultValue) if f.defaultValueSet {
return fmt.Sprintf("%d", f.defaultValue)
}
return fmt.Sprintf("%d", f.Value)
} }
// GetEnvVars returns the env vars for this flag // GetEnvVars returns the env vars for this flag

View file

@ -190,6 +190,15 @@ func (f *Uint64SliceFlag) Get(ctx *Context) []uint64 {
return ctx.Uint64Slice(f.Name) return ctx.Uint64Slice(f.Name)
} }
// RunAction executes flag action if set
func (f *Uint64SliceFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Uint64Slice(f.Name))
}
return nil
}
// Uint64Slice looks up the value of a local Uint64SliceFlag, returns // Uint64Slice looks up the value of a local Uint64SliceFlag, returns
// nil if not found // nil if not found
func (cCtx *Context) Uint64Slice(name string) []uint64 { func (cCtx *Context) Uint64Slice(name string) []uint64 {

View file

@ -201,6 +201,15 @@ func (f *UintSliceFlag) Get(ctx *Context) []uint {
return ctx.UintSlice(f.Name) return ctx.UintSlice(f.Name)
} }
// RunAction executes flag action if set
func (f *UintSliceFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.UintSlice(f.Name))
}
return nil
}
// UintSlice looks up the value of a local UintSliceFlag, returns // UintSlice looks up the value of a local UintSliceFlag, returns
// nil if not found // nil if not found
func (cCtx *Context) UintSlice(name string) []uint { func (cCtx *Context) UintSlice(name string) []uint {

View file

@ -27,15 +27,15 @@ application:
VARIABLES VARIABLES
var ( var (
SuggestFlag SuggestFlagFunc = suggestFlag SuggestFlag SuggestFlagFunc = nil // initialized in suggestions.go unless built with urfave_cli_no_suggest
SuggestCommand SuggestCommandFunc = suggestCommand SuggestCommand SuggestCommandFunc = nil // initialized in suggestions.go unless built with urfave_cli_no_suggest
SuggestDidYouMeanTemplate string = suggestDidYouMeanTemplate SuggestDidYouMeanTemplate string = suggestDidYouMeanTemplate
) )
var AppHelpTemplate = `NAME: var AppHelpTemplate = `NAME:
{{template "helpNameTemplate" .}} {{template "helpNameTemplate" .}}
USAGE: USAGE:
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}}{{if .ArgsUsage}} {{.ArgsUsage}}{{else}}{{if .Args}} [arguments...]{{end}}{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}
VERSION: VERSION:
{{.Version}}{{end}}{{end}}{{if .Description}} {{.Version}}{{end}}{{end}}{{if .Description}}
@ -136,7 +136,7 @@ var SubcommandHelpTemplate = `NAME:
{{template "helpNameTemplate" .}} {{template "helpNameTemplate" .}}
USAGE: USAGE:
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}} {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}command [command options]{{end}}{{if .ArgsUsage}} {{.ArgsUsage}}{{else}}{{if .Args}} [arguments...]{{end}}{{end}}{{end}}{{if .Description}}
DESCRIPTION: DESCRIPTION:
{{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}} {{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}}
@ -253,6 +253,8 @@ type App struct {
Usage string Usage string
// Text to override the USAGE section of help // Text to override the USAGE section of help
UsageText string UsageText string
// Whether this command supports arguments
Args bool
// Description of the program argument format. // Description of the program argument format.
ArgsUsage string ArgsUsage string
// Version of the program // Version of the program
@ -523,6 +525,8 @@ type Command struct {
UsageText string UsageText string
// A longer explanation of how the command works // A longer explanation of how the command works
Description string Description string
// Whether this command supports arguments
Args bool
// A short description of the arguments of this command // A short description of the arguments of this command
ArgsUsage string ArgsUsage string
// The category the command is part of // The category the command is part of
@ -649,7 +653,7 @@ func (cCtx *Context) Bool(name string) bool
Bool looks up the value of a local BoolFlag, returns false if not found Bool looks up the value of a local BoolFlag, returns false if not found
func (cCtx *Context) Count(name string) int func (cCtx *Context) Count(name string) int
Count returns the num of occurences of this flag Count returns the num of occurrences of this flag
func (cCtx *Context) Duration(name string) time.Duration func (cCtx *Context) Duration(name string) time.Duration
Duration looks up the value of a local DurationFlag, returns 0 if not found Duration looks up the value of a local DurationFlag, returns 0 if not found
@ -2142,6 +2146,9 @@ func (f *Uint64SliceFlag) IsVisible() bool
func (f *Uint64SliceFlag) Names() []string func (f *Uint64SliceFlag) Names() []string
Names returns the names of the flag Names returns the names of the flag
func (f *Uint64SliceFlag) RunAction(c *Context) error
RunAction executes flag action if set
func (f *Uint64SliceFlag) String() string func (f *Uint64SliceFlag) String() string
String returns a readable representation of this value (for usage defaults) String returns a readable representation of this value (for usage defaults)
@ -2307,6 +2314,9 @@ func (f *UintSliceFlag) IsVisible() bool
func (f *UintSliceFlag) Names() []string func (f *UintSliceFlag) Names() []string
Names returns the names of the flag Names returns the names of the flag
func (f *UintSliceFlag) RunAction(c *Context) error
RunAction executes flag action if set
func (f *UintSliceFlag) String() string func (f *UintSliceFlag) String() string
String returns a readable representation of this value (for usage defaults) String returns a readable representation of this value (for usage defaults)

View file

@ -69,7 +69,7 @@ var helpCommand = &Command{
} }
// Case 3, 5 // Case 3, 5
if (len(cCtx.Command.Subcommands) == 1 && !cCtx.Command.HideHelp) || if (len(cCtx.Command.Subcommands) == 1 && !cCtx.Command.HideHelp && !cCtx.Command.HideHelpCommand) ||
(len(cCtx.Command.Subcommands) == 0 && cCtx.Command.HideHelp) { (len(cCtx.Command.Subcommands) == 0 && cCtx.Command.HideHelp) {
templ := cCtx.Command.CustomHelpTemplate templ := cCtx.Command.CustomHelpTemplate
if templ == "" { if templ == "" {
@ -248,7 +248,6 @@ func ShowCommandHelpAndExit(c *Context, command string, code int) {
// ShowCommandHelp prints help for the given command // ShowCommandHelp prints help for the given command
func ShowCommandHelp(ctx *Context, command string) error { func ShowCommandHelp(ctx *Context, command string) error {
commands := ctx.App.Commands commands := ctx.App.Commands
if ctx.Command.Subcommands != nil { if ctx.Command.Subcommands != nil {
commands = ctx.Command.Subcommands commands = ctx.Command.Subcommands
@ -278,7 +277,7 @@ func ShowCommandHelp(ctx *Context, command string) error {
if ctx.App.CommandNotFound == nil { if ctx.App.CommandNotFound == nil {
errMsg := fmt.Sprintf("No help topic for '%v'", command) errMsg := fmt.Sprintf("No help topic for '%v'", command)
if ctx.App.Suggest { if ctx.App.Suggest && SuggestCommand != nil {
if suggestion := SuggestCommand(ctx.Command.Subcommands, command); suggestion != "" { if suggestion := SuggestCommand(ctx.Command.Subcommands, command); suggestion != "" {
errMsg += ". " + suggestion errMsg += ". " + suggestion
} }
@ -337,7 +336,6 @@ func ShowCommandCompletions(ctx *Context, command string) {
DefaultCompleteWithFlags(c)(ctx) DefaultCompleteWithFlags(c)(ctx)
} }
} }
} }
// printHelpCustom is the default implementation of HelpPrinterCustom. // printHelpCustom is the default implementation of HelpPrinterCustom.
@ -345,7 +343,6 @@ func ShowCommandCompletions(ctx *Context, command string) {
// The customFuncs map will be combined with a default template.FuncMap to // The customFuncs map will be combined with a default template.FuncMap to
// allow using arbitrary functions in template rendering. // allow using arbitrary functions in template rendering.
func printHelpCustom(out io.Writer, templ string, data interface{}, customFuncs map[string]interface{}) { func printHelpCustom(out io.Writer, templ string, data interface{}, customFuncs map[string]interface{}) {
const maxLineLength = 10000 const maxLineLength = 10000
funcMap := template.FuncMap{ funcMap := template.FuncMap{
@ -376,17 +373,26 @@ func printHelpCustom(out io.Writer, templ string, data interface{}, customFuncs
w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0) w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0)
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
t.New("helpNameTemplate").Parse(helpNameTemplate) templates := map[string]string{
t.New("usageTemplate").Parse(usageTemplate) "helpNameTemplate": helpNameTemplate,
t.New("descriptionTemplate").Parse(descriptionTemplate) "usageTemplate": usageTemplate,
t.New("visibleCommandTemplate").Parse(visibleCommandTemplate) "descriptionTemplate": descriptionTemplate,
t.New("copyrightTemplate").Parse(copyrightTemplate) "visibleCommandTemplate": visibleCommandTemplate,
t.New("versionTemplate").Parse(versionTemplate) "copyrightTemplate": copyrightTemplate,
t.New("visibleFlagCategoryTemplate").Parse(visibleFlagCategoryTemplate) "versionTemplate": versionTemplate,
t.New("visibleFlagTemplate").Parse(visibleFlagTemplate) "visibleFlagCategoryTemplate": visibleFlagCategoryTemplate,
t.New("visibleGlobalFlagCategoryTemplate").Parse(strings.Replace(visibleFlagCategoryTemplate, "OPTIONS", "GLOBAL OPTIONS", -1)) "visibleFlagTemplate": visibleFlagTemplate,
t.New("authorsTemplate").Parse(authorsTemplate) "visibleGlobalFlagCategoryTemplate": strings.Replace(visibleFlagCategoryTemplate, "OPTIONS", "GLOBAL OPTIONS", -1),
t.New("visibleCommandCategoryTemplate").Parse(visibleCommandCategoryTemplate) "authorsTemplate": authorsTemplate,
"visibleCommandCategoryTemplate": visibleCommandCategoryTemplate,
}
for name, value := range templates {
if _, err := t.New(name).Parse(value); err != nil {
if os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" {
_, _ = fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err)
}
}
}
err := t.Execute(w, data) err := t.Execute(w, data)
if err != nil { if err != nil {
@ -415,6 +421,9 @@ func checkVersion(cCtx *Context) bool {
} }
func checkHelp(cCtx *Context) bool { func checkHelp(cCtx *Context) bool {
if HelpFlag == nil {
return false
}
found := false found := false
for _, name := range HelpFlag.Names() { for _, name := range HelpFlag.Names() {
if cCtx.Bool(name) { if cCtx.Bool(name) {
@ -426,24 +435,6 @@ func checkHelp(cCtx *Context) bool {
return found return found
} }
func checkCommandHelp(c *Context, name string) bool {
if c.Bool("h") || c.Bool("help") {
_ = ShowCommandHelp(c, name)
return true
}
return false
}
func checkSubcommandHelp(cCtx *Context) bool {
if cCtx.Bool("h") || cCtx.Bool("help") {
_ = ShowSubcommandHelp(cCtx)
return true
}
return false
}
func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) { func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) {
if !a.EnableBashCompletion { if !a.EnableBashCompletion {
return false, arguments return false, arguments
@ -456,6 +447,15 @@ func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) {
return false, arguments return false, arguments
} }
for _, arg := range arguments {
// If arguments include "--", shell completion is disabled
// because after "--" only positional arguments are accepted.
// https://unix.stackexchange.com/a/11382
if arg == "--" {
return false, arguments
}
}
return true, arguments[:pos] return true, arguments[:pos]
} }
@ -505,7 +505,6 @@ func wrap(input string, offset int, wrapAt int) string {
ss = append(ss, wrapped) ss = append(ss, wrapped)
} else { } else {
ss = append(ss, padding+wrapped) ss = append(ss, padding+wrapped)
} }
} }

View file

@ -1,7 +1,7 @@
# NOTE: the mkdocs dependencies will need to be installed out of # NOTE: the mkdocs dependencies will need to be installed out of
# band until this whole thing gets more automated: # band until this whole thing gets more automated:
# #
# pip install -r mkdocs-requirements.txt # pip install -r mkdocs-reqs.txt
# #
site_name: urfave/cli site_name: urfave/cli

View file

@ -1,3 +1,6 @@
//go:build !urfave_cli_no_suggest
// +build !urfave_cli_no_suggest
package cli package cli
import ( import (
@ -6,6 +9,11 @@ import (
"github.com/xrash/smetrics" "github.com/xrash/smetrics"
) )
func init() {
SuggestFlag = suggestFlag
SuggestCommand = suggestCommand
}
func jaroWinkler(a, b string) float64 { func jaroWinkler(a, b string) float64 {
// magic values are from https://github.com/xrash/smetrics/blob/039620a656736e6ad994090895784a7af15e0b80/jaro-winkler.go#L8 // magic values are from https://github.com/xrash/smetrics/blob/039620a656736e6ad994090895784a7af15e0b80/jaro-winkler.go#L8
const ( const (
@ -21,7 +29,7 @@ func suggestFlag(flags []Flag, provided string, hideHelp bool) string {
for _, flag := range flags { for _, flag := range flags {
flagNames := flag.Names() flagNames := flag.Names()
if !hideHelp { if !hideHelp && HelpFlag != nil {
flagNames = append(flagNames, HelpFlag.Names()...) flagNames = append(flagNames, HelpFlag.Names()...)
} }
for _, name := range flagNames { for _, name := range flagNames {

View file

@ -1,7 +1,7 @@
package cli package cli
var helpNameTemplate = `{{$v := offset .HelpName 6}}{{wrap .HelpName 3}}{{if .Usage}} - {{wrap .Usage $v}}{{end}}` var helpNameTemplate = `{{$v := offset .HelpName 6}}{{wrap .HelpName 3}}{{if .Usage}} - {{wrap .Usage $v}}{{end}}`
var usageTemplate = `{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}` var usageTemplate = `{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}}{{if .ArgsUsage}} {{.ArgsUsage}}{{else}}{{if .Args}} [arguments...]{{end}}{{end}}{{end}}`
var descriptionTemplate = `{{wrap .Description 3}}` var descriptionTemplate = `{{wrap .Description 3}}`
var authorsTemplate = `{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: var authorsTemplate = `{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}:
{{range $index, $author := .Authors}}{{if $index}} {{range $index, $author := .Authors}}{{if $index}}
@ -35,7 +35,7 @@ var AppHelpTemplate = `NAME:
{{template "helpNameTemplate" .}} {{template "helpNameTemplate" .}}
USAGE: USAGE:
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}}{{if .ArgsUsage}} {{.ArgsUsage}}{{else}}{{if .Args}} [arguments...]{{end}}{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}
VERSION: VERSION:
{{.Version}}{{end}}{{end}}{{if .Description}} {{.Version}}{{end}}{{end}}{{if .Description}}
@ -83,7 +83,7 @@ var SubcommandHelpTemplate = `NAME:
{{template "helpNameTemplate" .}} {{template "helpNameTemplate" .}}
USAGE: USAGE:
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}} {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}command [command options]{{end}}{{if .ArgsUsage}} {{.ArgsUsage}}{{else}}{{if .Args}} [arguments...]{{end}}{{end}}{{end}}{{if .Description}}
DESCRIPTION: DESCRIPTION:
{{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}} {{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}}

View file

@ -23,7 +23,8 @@ type Float64SliceFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue *Float64Slice defaultValue *Float64Slice
defaultValueSet bool
separator separatorSpec separator separatorSpec
@ -69,7 +70,8 @@ type GenericFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue Generic defaultValue Generic
defaultValueSet bool
TakesFile bool TakesFile bool
@ -120,7 +122,8 @@ type Int64SliceFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue *Int64Slice defaultValue *Int64Slice
defaultValueSet bool
separator separatorSpec separator separatorSpec
@ -166,7 +169,8 @@ type IntSliceFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue *IntSlice defaultValue *IntSlice
defaultValueSet bool
separator separatorSpec separator separatorSpec
@ -212,7 +216,8 @@ type PathFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue Path defaultValue Path
defaultValueSet bool
TakesFile bool TakesFile bool
@ -263,7 +268,8 @@ type StringSliceFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue *StringSlice defaultValue *StringSlice
defaultValueSet bool
separator separatorSpec separator separatorSpec
@ -313,7 +319,8 @@ type TimestampFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue *Timestamp defaultValue *Timestamp
defaultValueSet bool
Layout string Layout string
@ -366,7 +373,8 @@ type Uint64SliceFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue *Uint64Slice defaultValue *Uint64Slice
defaultValueSet bool
separator separatorSpec separator separatorSpec
@ -412,7 +420,8 @@ type UintSliceFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue *UintSlice defaultValue *UintSlice
defaultValueSet bool
separator separatorSpec separator separatorSpec
@ -458,7 +467,8 @@ type BoolFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue bool defaultValue bool
defaultValueSet bool
Count *int Count *int
@ -511,7 +521,8 @@ type Float64Flag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue float64 defaultValue float64
defaultValueSet bool
Action func(*Context, float64) error Action func(*Context, float64) error
} }
@ -560,7 +571,8 @@ type IntFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue int defaultValue int
defaultValueSet bool
Base int Base int
@ -611,7 +623,8 @@ type Int64Flag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue int64 defaultValue int64
defaultValueSet bool
Base int Base int
@ -662,7 +675,8 @@ type StringFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue string defaultValue string
defaultValueSet bool
TakesFile bool TakesFile bool
@ -713,7 +727,8 @@ type DurationFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue time.Duration defaultValue time.Duration
defaultValueSet bool
Action func(*Context, time.Duration) error Action func(*Context, time.Duration) error
} }
@ -762,7 +777,8 @@ type UintFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue uint defaultValue uint
defaultValueSet bool
Base int Base int
@ -813,7 +829,8 @@ type Uint64Flag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue uint64 defaultValue uint64
defaultValueSet bool
Base int Base int

View file

@ -6,36 +6,58 @@ import (
// The Soundex encoding. It is a phonetic algorithm that considers how the words sound in English. Soundex maps a string to a 4-byte code consisting of the first letter of the original string and three numbers. Strings that sound similar should map to the same code. // The Soundex encoding. It is a phonetic algorithm that considers how the words sound in English. Soundex maps a string to a 4-byte code consisting of the first letter of the original string and three numbers. Strings that sound similar should map to the same code.
func Soundex(s string) string { func Soundex(s string) string {
m := map[byte]string{ b := strings.Builder{}
'B': "1", 'P': "1", 'F': "1", 'V': "1", b.Grow(4)
'C': "2", 'S': "2", 'K': "2", 'G': "2", 'J': "2", 'Q': "2", 'X': "2", 'Z': "2",
'D': "3", 'T': "3",
'L': "4",
'M': "5", 'N': "5",
'R': "6",
}
s = strings.ToUpper(s)
r := string(s[0])
p := s[0] p := s[0]
for i := 1; i < len(s) && len(r) < 4; i++ { if p <= 'z' && p >= 'a' {
p -= 32 // convert to uppercase
}
b.WriteByte(p)
n := 0
for i := 1; i < len(s); i++ {
c := s[i] c := s[i]
if (c < 'A' || c > 'Z') || (c == p) { if c <= 'z' && c >= 'a' {
c -= 32 // convert to uppercase
} else if c < 'A' || c > 'Z' {
continue
}
if c == p {
continue continue
} }
p = c p = c
if n, ok := m[c]; ok { switch c {
r += n case 'B', 'P', 'F', 'V':
c = '1'
case 'C', 'S', 'K', 'G', 'J', 'Q', 'X', 'Z':
c = '2'
case 'D', 'T':
c = '3'
case 'L':
c = '4'
case 'M', 'N':
c = '5'
case 'R':
c = '6'
default:
continue
}
b.WriteByte(c)
n++
if n == 3 {
break
} }
} }
for i := len(r); i < 4; i++ { for i := n; i < 3; i++ {
r += "0" b.WriteByte('0')
} }
return r return b.String()
} }

8
vendor/modules.txt vendored
View file

@ -51,7 +51,7 @@ github.com/aws/aws-sdk-go/service/sso/ssoiface
github.com/aws/aws-sdk-go/service/ssooidc github.com/aws/aws-sdk-go/service/ssooidc
github.com/aws/aws-sdk-go/service/sts github.com/aws/aws-sdk-go/service/sts
github.com/aws/aws-sdk-go/service/sts/stsiface github.com/aws/aws-sdk-go/service/sts/stsiface
# github.com/cpuguy83/go-md2man/v2 v2.0.2 # github.com/cpuguy83/go-md2man/v2 v2.0.4
## explicit; go 1.11 ## explicit; go 1.11
github.com/cpuguy83/go-md2man/v2/md2man github.com/cpuguy83/go-md2man/v2/md2man
# github.com/davecgh/go-spew v1.1.1 # github.com/davecgh/go-spew v1.1.1
@ -88,11 +88,11 @@ github.com/russross/blackfriday/v2
# github.com/stretchr/testify v1.8.4 # github.com/stretchr/testify v1.8.4
## explicit; go 1.20 ## explicit; go 1.20
github.com/stretchr/testify/assert github.com/stretchr/testify/assert
# github.com/urfave/cli/v2 v2.25.7 # github.com/urfave/cli/v2 v2.27.4
## explicit; go 1.18 ## explicit; go 1.18
github.com/urfave/cli/v2 github.com/urfave/cli/v2
# github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 # github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1
## explicit ## explicit; go 1.15
github.com/xrash/smetrics github.com/xrash/smetrics
# golang.org/x/crypto v0.14.0 # golang.org/x/crypto v0.14.0
## explicit; go 1.17 ## explicit; go 1.17