From cfef458e0cc4963f3c4818dbbf79f261b497e699 Mon Sep 17 00:00:00 2001 From: Jesse van den Kieboom Date: Mon, 17 Jun 2024 08:11:12 +0200 Subject: [PATCH] Fix for windows long/short names --- assert_test.go | 52 ++++++++---------------------------------- completion.go | 15 ++++++++++-- completion_test.go | 46 +++++++++++++++++++++++-------------- go.mod | 5 +++- go.sum | 18 +++++++++++++++ help_test.go | 2 +- parser_test.go | 2 +- termsize.go | 8 ++++++- termsize_defaults.go | 3 +++ termsize_nosysioctl.go | 2 +- termsize_windows.go | 11 +++++---- 11 files changed, 93 insertions(+), 71 deletions(-) create mode 100644 termsize_defaults.go diff --git a/assert_test.go b/assert_test.go index cfb7487..b546c31 100644 --- a/assert_test.go +++ b/assert_test.go @@ -2,13 +2,11 @@ package flags import ( "fmt" - "io" - "io/ioutil" - "os" - "os/exec" "path" "runtime" "testing" + + "github.com/sergi/go-diff/diffmatchpatch" ) func assertCallerInfo() (string, int) { @@ -128,50 +126,18 @@ func assertParseFail(t *testing.T, typ ErrorType, msg string, data interface{}, return ret } -func diff(a, b string) (string, error) { - atmp, err := ioutil.TempFile("", "help-diff") - - if err != nil { - return "", err - } - - btmp, err := ioutil.TempFile("", "help-diff") - - if err != nil { - return "", err - } - - if _, err := io.WriteString(atmp, a); err != nil { - return "", err - } - - if _, err := io.WriteString(btmp, b); err != nil { - return "", err - } - - ret, err := exec.Command("diff", "-u", "-d", "--label", "got", atmp.Name(), "--label", "expected", btmp.Name()).Output() - - os.Remove(atmp.Name()) - os.Remove(btmp.Name()) - - if err.Error() == "exit status 1" { - return string(ret), nil - } - - return string(ret), err -} - func assertDiff(t *testing.T, actual, expected, msg string) { if actual == expected { return } - ret, err := diff(actual, expected) + dmp := diffmatchpatch.New() + diffs := dmp.DiffMain(actual, expected, false) - if err != nil { - assertErrorf(t, "Unexpected diff error: %s", err) - assertErrorf(t, "Unexpected %s, expected:\n\n%s\n\nbut got\n\n%s", msg, expected, actual) - } else { - assertErrorf(t, "Unexpected %s:\n\n%s", msg, ret) + if len(diffs) == 0 { + return } + + pretty := dmp.DiffPrettyText(diffs) + assertErrorf(t, "Unexpected %s:\n\n%s", msg, pretty) } diff --git a/completion.go b/completion.go index 8ed61f1..d11a6fa 100644 --- a/completion.go +++ b/completion.go @@ -91,10 +91,21 @@ func (c *completion) completeOptionNames(s *parseState, prefix string, match str var results []Completion repeats := map[string]bool{} + var longprefix string + var shortprefix string + + if prefix == "/" { + longprefix = "/" + shortprefix = "/" + } else { + longprefix = "--" + shortprefix = "-" + } + for name, opt := range s.lookup.longNames { if strings.HasPrefix(name, match) && !opt.Hidden { results = append(results, Completion{ - Item: defaultLongOptDelimiter + name, + Item: longprefix + name, Description: opt.Description, }) @@ -108,7 +119,7 @@ func (c *completion) completeOptionNames(s *parseState, prefix string, match str for name, opt := range s.lookup.shortNames { if _, exist := repeats[name]; !exist && strings.HasPrefix(name, match) && !opt.Hidden { results = append(results, Completion{ - Item: string(defaultShortOptDelimiter) + name, + Item: shortprefix + name, Description: opt.Description, }) } diff --git a/completion_test.go b/completion_test.go index aa3af90..00414cc 100644 --- a/completion_test.go +++ b/completion_test.go @@ -8,6 +8,7 @@ import ( "path/filepath" "reflect" "runtime" + "sort" "strings" "testing" ) @@ -81,6 +82,14 @@ type completionTest struct { var completionTests []completionTest +func makeLongName(option string) string { + return defaultLongOptDelimiter + option +} + +func makeShortName(option string) string { + return string(defaultShortOptDelimiter) + option +} + func init() { _, sourcefile, _, _ := runtime.Caller(0) completionTestSourcedir := filepath.Join(filepath.SplitList(path.Dir(sourcefile))...) @@ -97,15 +106,15 @@ func init() { completionTests = []completionTest{ { // Short names - []string{"-"}, - []string{"--debug", "--required", "--verbose", "--version", "-i"}, + []string{makeShortName("")}, + []string{makeLongName("debug"), makeLongName("required"), makeLongName("verbose"), makeLongName("version"), makeShortName("i")}, false, }, { // Short names full - []string{"-i"}, - []string{"-i"}, + []string{makeShortName("i")}, + []string{makeShortName("i")}, false, }, @@ -137,8 +146,8 @@ func init() { { // Long names partial - []string{"--ver"}, - []string{"--verbose", "--version"}, + []string{makeLongName("ver")}, + []string{makeLongName("verbose"), makeLongName("version")}, false, }, @@ -197,69 +206,69 @@ func init() { { // Flag filename - []string{"rm", "-f", path.Join(completionTestSourcedir, "completion")}, + []string{"rm", makeShortName("f"), filepath.Join(completionTestSourcedir, "completion")}, completionTestFilename, false, }, { // Flag short concat last filename - []string{"rm", "-of", path.Join(completionTestSourcedir, "completion")}, + []string{"rm", "-of", filepath.Join(completionTestSourcedir, "completion")}, completionTestFilename, false, }, { // Flag concat filename - []string{"rm", "-f" + path.Join(completionTestSourcedir, "completion")}, + []string{"rm", "-f" + filepath.Join(completionTestSourcedir, "completion")}, []string{"-f" + completionTestFilename[0], "-f" + completionTestFilename[1]}, false, }, { // Flag equal concat filename - []string{"rm", "-f=" + path.Join(completionTestSourcedir, "completion")}, + []string{"rm", "-f=" + filepath.Join(completionTestSourcedir, "completion")}, []string{"-f=" + completionTestFilename[0], "-f=" + completionTestFilename[1]}, false, }, { // Flag concat long filename - []string{"rm", "--filename=" + path.Join(completionTestSourcedir, "completion")}, + []string{"rm", "--filename=" + filepath.Join(completionTestSourcedir, "completion")}, []string{"--filename=" + completionTestFilename[0], "--filename=" + completionTestFilename[1]}, false, }, { // Flag long filename - []string{"rm", "--filename", path.Join(completionTestSourcedir, "completion")}, + []string{"rm", "--filename", filepath.Join(completionTestSourcedir, "completion")}, completionTestFilename, false, }, { // To subdir - []string{"rm", "--filename", path.Join(completionTestSourcedir, "examples/bash-")}, - []string{path.Join(completionTestSourcedir, "examples/bash-completion/")}, + []string{"rm", "--filename", filepath.Join(completionTestSourcedir, "examples/bash-")}, + []string{filepath.Join(completionTestSourcedir, "examples/bash-completion/")}, false, }, { // Subdirectory - []string{"rm", "--filename", path.Join(completionTestSourcedir, "examples") + "/"}, + []string{"rm", "--filename", filepath.Join(completionTestSourcedir, "examples") + "/"}, completionTestSubdir, false, }, { // Custom completed - []string{"rename", "-c", "hello un"}, + []string{"rename", makeShortName("c"), "hello un"}, []string{"hello universe"}, false, }, { // Multiple flag filename - []string{"add-multi-flag", "-f", filepath.Join(completionTestSourcedir, "completion")}, + []string{"add-multi-flag", makeShortName("f"), filepath.Join(completionTestSourcedir, "completion")}, completionTestFilename, false, }, @@ -282,6 +291,9 @@ func TestCompletion(t *testing.T) { items[i] = v.Item } + sort.Strings(items) + sort.Strings(test.Completed) + if !reflect.DeepEqual(items, test.Completed) { t.Errorf("Args: %#v, %#v\n Expected: %#v\n Got: %#v", test.Args, test.ShowDescriptions, test.Completed, items) } diff --git a/go.mod b/go.mod index 934cfbe..d0daa25 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module github.com/jessevdk/go-flags go 1.20 -require golang.org/x/sys v0.21.0 +require ( + github.com/sergi/go-diff v1.3.1 + golang.org/x/sys v0.21.0 +) diff --git a/go.sum b/go.sum index ac7fb31..bac7088 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,20 @@ +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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +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/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/help_test.go b/help_test.go index dee8533..7f8b463 100644 --- a/help_test.go +++ b/help_test.go @@ -153,7 +153,7 @@ Arguments: Available commands: bommand A command with only hidden options command A command (aliases: cm, cmd) - parent A command with a sub command + parent A parent command ` } else { expected = `Usage: diff --git a/parser_test.go b/parser_test.go index df37044..e675cfd 100644 --- a/parser_test.go +++ b/parser_test.go @@ -101,7 +101,7 @@ func TestDefaults(t *testing.T) { { msg: "non-zero value arguments, expecting overwritten arguments", args: []string{"-3=true"}, - expectedErr: "bool flag `-3' cannot have an argument", + expectedErr: "bool flag `" + makeShortName("3") + "' cannot have an argument", }, { msg: "zero value arguments, expecting overwritten arguments", diff --git a/termsize.go b/termsize.go index 7bcf66f..eb08532 100644 --- a/termsize.go +++ b/termsize.go @@ -4,13 +4,19 @@ package flags import ( + "flag" + "golang.org/x/sys/unix" ) func getTerminalColumns() int { + if flag.Lookup("test.v") != nil { + return defaultTermSize + } + ws, err := unix.IoctlGetWinsize(0, unix.TIOCGWINSZ) if err != nil { - return 80 + return defaultTermSize } return int(ws.Col) } diff --git a/termsize_defaults.go b/termsize_defaults.go new file mode 100644 index 0000000..df2ec29 --- /dev/null +++ b/termsize_defaults.go @@ -0,0 +1,3 @@ +package flags + +const defaultTermSize = 80 diff --git a/termsize_nosysioctl.go b/termsize_nosysioctl.go index d839220..6d557a9 100644 --- a/termsize_nosysioctl.go +++ b/termsize_nosysioctl.go @@ -4,5 +4,5 @@ package flags func getTerminalColumns() int { - return 80 + return defaultTermSize } diff --git a/termsize_windows.go b/termsize_windows.go index 189a1b3..763f2dd 100644 --- a/termsize_windows.go +++ b/termsize_windows.go @@ -4,6 +4,7 @@ package flags import ( + "flag" "syscall" "unsafe" ) @@ -66,21 +67,23 @@ func GetConsoleScreenBufferInfo(handle uintptr) (*CONSOLE_SCREEN_BUFFER_INFO, er } func getTerminalColumns() int { - defaultWidth := 80 + if flag.Lookup("test.v") != nil { + return defaultTermSize + } stdoutHandle, err := getStdHandle(syscall.STD_OUTPUT_HANDLE) if err != nil { - return defaultWidth + return defaultTermSize } info, err := GetConsoleScreenBufferInfo(stdoutHandle) if err != nil { - return defaultWidth + return defaultTermSize } if info.MaximumWindowSize.X > 0 { return int(info.MaximumWindowSize.X) } - return defaultWidth + return defaultTermSize }