Skip to content

Commit 66278a0

Browse files
committed
Merge branch 'main' into update_ci
2 parents 17e5dee + 38003d1 commit 66278a0

File tree

9 files changed

+131
-41
lines changed

9 files changed

+131
-41
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,11 @@ jobs:
4545
- if: matrix.go == '1.20.x' && matrix.os == 'ubuntu-latest'
4646
run: make v3diff
4747
- if: success() && matrix.go == '1.20.x' && matrix.os == 'ubuntu-latest'
48-
uses: codecov/codecov-action@v3
48+
uses: codecov/codecov-action@v4
4949
with:
5050
token: ${{ secrets.CODECOV_TOKEN }}
5151
fail_ci_if_error: true
52+
verbose: true
5253

5354
test-docs:
5455
name: test-docs

docs/v3/examples/bash-completions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ The default shell completion flag (`--generate-bash-completion`) is defined as
188188

189189
<!-- {
190190
"args": ["&#45;&#45;generate&#45;shell&#45;completion"],
191-
"output": "wat\nhelp\nh"
191+
"output": "wat\nhelp\n"
192192
} -->
193193
```go
194194
package main

examples_test.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -269,11 +269,8 @@ func ExampleCommand_Run_shellComplete_bash_withShortFlag() {
269269
_ = cmd.Run(context.Background(), os.Args)
270270
// Output:
271271
// --other
272-
// -o
273272
// --xyz
274-
// -x
275273
// --help
276-
// -h
277274
}
278275

279276
func ExampleCommand_Run_shellComplete_bash_withLongFlag() {
@@ -376,10 +373,8 @@ func ExampleCommand_Run_shellComplete_bash() {
376373
_ = cmd.Run(context.Background(), os.Args)
377374
// Output:
378375
// describeit
379-
// d
380376
// next
381377
// help
382-
// h
383378
}
384379

385380
func ExampleCommand_Run_shellComplete_zsh() {
@@ -415,10 +410,8 @@ func ExampleCommand_Run_shellComplete_zsh() {
415410
_ = cmd.Run(context.Background(), os.Args)
416411
// Output:
417412
// describeit:use it to see a description
418-
// d:use it to see a description
419413
// next:next example
420414
// help:Shows a list of commands or help for one command
421-
// h:Shows a list of commands or help for one command
422415
}
423416

424417
func ExampleCommand_Run_sliceValues() {

go.mod

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ module github.com/urfave/cli/v3
22

33
go 1.18
44

5-
require (
6-
github.com/stretchr/testify v1.8.4
7-
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673
8-
)
5+
require github.com/stretchr/testify v1.8.4
96

107
require (
118
github.com/BurntSushi/toml v1.3.2 // indirect

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
88
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
99
github.com/urfave/cli-altsrc/v3 v3.0.0-alpha2 h1:j4SaBpPB8++L0c0KuTnz/Yus3UQoWJ54hQjhIMW8rCM=
1010
github.com/urfave/cli-altsrc/v3 v3.0.0-alpha2/go.mod h1:Q79oyIY/z4jtzIrKEK6MUeWC7/szGr46x4QdOaOAIWc=
11-
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
12-
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
1311
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
1412
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
1513
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

help.go

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -160,13 +160,9 @@ func printCommandSuggestions(commands []*Command, writer io.Writer) {
160160
continue
161161
}
162162
if strings.HasSuffix(os.Getenv("SHELL"), "zsh") {
163-
for _, name := range command.Names() {
164-
_, _ = fmt.Fprintf(writer, "%s:%s\n", name, command.Usage)
165-
}
163+
_, _ = fmt.Fprintf(writer, "%s:%s\n", command.Name, command.Usage)
166164
} else {
167-
for _, name := range command.Names() {
168-
_, _ = fmt.Fprintf(writer, "%s\n", name)
169-
}
165+
_, _ = fmt.Fprintf(writer, "%s\n", command.Name)
170166
}
171167
}
172168
}
@@ -195,23 +191,23 @@ func printFlagSuggestions(lastArg string, flags []Flag, writer io.Writer) {
195191
if bflag, ok := flag.(*BoolFlag); ok && bflag.Hidden {
196192
continue
197193
}
198-
for _, name := range flag.Names() {
199-
name = strings.TrimSpace(name)
200-
// this will get total count utf8 letters in flag name
201-
count := utf8.RuneCountInString(name)
202-
if count > 2 {
203-
count = 2 // reuse this count to generate single - or -- in flag completion
204-
}
205-
// if flag name has more than one utf8 letter and last argument in cli has -- prefix then
206-
// skip flag completion for short flags example -v or -x
207-
if strings.HasPrefix(lastArg, "--") && count == 1 {
208-
continue
209-
}
210-
// match if last argument matches this flag and it is not repeated
211-
if strings.HasPrefix(name, cur) && cur != name && !cliArgContains(name) {
212-
flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name)
213-
fmt.Fprintln(writer, flagCompletion)
214-
}
194+
195+
name := strings.TrimSpace(flag.Names()[0])
196+
// this will get total count utf8 letters in flag name
197+
count := utf8.RuneCountInString(name)
198+
if count > 2 {
199+
count = 2 // reuse this count to generate single - or -- in flag completion
200+
}
201+
// if flag name has more than one utf8 letter and last argument in cli has -- prefix then
202+
// skip flag completion for short flags example -v or -x
203+
if strings.HasPrefix(lastArg, "--") && count == 1 {
204+
continue
205+
}
206+
// match if last argument matches this flag and it is not repeated
207+
if strings.HasPrefix(name, cur) && cur != name && !cliArgContains(name) {
208+
flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name)
209+
fmt.Fprintln(writer, flagCompletion)
210+
215211
}
216212
}
217213
}

suggestions.go

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package cli
22

33
import (
4-
"github.com/xrash/smetrics"
4+
"math"
55
)
66

77
const suggestDidYouMeanTemplate = "Did you mean %q?"
@@ -16,13 +16,90 @@ type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string
1616

1717
type SuggestCommandFunc func(commands []*Command, provided string) string
1818

19+
// jaroDistance is the measure of similarity between two strings. It returns a
20+
// value between 0 and 1, where 1 indicates identical strings and 0 indicates
21+
// completely different strings.
22+
//
23+
// Adapted from https://github.com/xrash/smetrics/blob/5f08fbb34913bc8ab95bb4f2a89a0637ca922666/jaro.go.
24+
func jaroDistance(a, b string) float64 {
25+
if len(a) == 0 && len(b) == 0 {
26+
return 1
27+
}
28+
if len(a) == 0 || len(b) == 0 {
29+
return 0
30+
}
31+
32+
lenA := float64(len(a))
33+
lenB := float64(len(b))
34+
hashA := make([]bool, len(a))
35+
hashB := make([]bool, len(b))
36+
maxDistance := int(math.Max(0, math.Floor(math.Max(lenA, lenB)/2.0)-1))
37+
38+
var matches float64
39+
for i := 0; i < len(a); i++ {
40+
start := int(math.Max(0, float64(i-maxDistance)))
41+
end := int(math.Min(lenB-1, float64(i+maxDistance)))
42+
43+
for j := start; j <= end; j++ {
44+
if hashB[j] {
45+
continue
46+
}
47+
if a[i] == b[j] {
48+
hashA[i] = true
49+
hashB[j] = true
50+
matches++
51+
break
52+
}
53+
}
54+
}
55+
if matches == 0 {
56+
return 0
57+
}
58+
59+
var transpositions float64
60+
var j int
61+
for i := 0; i < len(a); i++ {
62+
if !hashA[i] {
63+
continue
64+
}
65+
for !hashB[j] {
66+
j++
67+
}
68+
if a[i] != b[j] {
69+
transpositions++
70+
}
71+
j++
72+
}
73+
74+
transpositions /= 2
75+
return ((matches / lenA) + (matches / lenB) + ((matches - transpositions) / matches)) / 3.0
76+
}
77+
78+
// jaroWinkler is more accurate when strings have a common prefix up to a
79+
// defined maximum length.
80+
//
81+
// Adapted from https://github.com/xrash/smetrics/blob/5f08fbb34913bc8ab95bb4f2a89a0637ca922666/jaro-winkler.go.
1982
func jaroWinkler(a, b string) float64 {
20-
// magic values are from https://github.com/xrash/smetrics/blob/039620a656736e6ad994090895784a7af15e0b80/jaro-winkler.go#L8
2183
const (
2284
boostThreshold = 0.7
2385
prefixSize = 4
2486
)
25-
return smetrics.JaroWinkler(a, b, boostThreshold, prefixSize)
87+
jaroDist := jaroDistance(a, b)
88+
if jaroDist <= boostThreshold {
89+
return jaroDist
90+
}
91+
92+
prefix := int(math.Min(float64(len(a)), math.Min(float64(prefixSize), float64(len(b)))))
93+
94+
var prefixMatch float64
95+
for i := 0; i < prefix; i++ {
96+
if a[i] == b[i] {
97+
prefixMatch++
98+
} else {
99+
break
100+
}
101+
}
102+
return jaroDist + 0.1*prefixMatch*(1.0-jaroDist)
26103
}
27104

28105
func suggestFlag(flags []Flag, provided string, hideHelp bool) string {

suggestions_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,33 @@ import (
88
"github.com/stretchr/testify/assert"
99
)
1010

11+
func TestJaroWinkler(t *testing.T) {
12+
// Given
13+
for _, testCase := range []struct {
14+
a, b string
15+
expected float64
16+
}{
17+
{"", "", 1},
18+
{"a", "", 0},
19+
{"", "a", 0},
20+
{"a", "a", 1},
21+
{"a", "b", 0},
22+
{"aa", "aa", 1},
23+
{"aa", "bb", 0},
24+
{"aaa", "aaa", 1},
25+
{"aa", "ab", 0.6666666666666666},
26+
{"aa", "ba", 0.6666666666666666},
27+
{"ba", "aa", 0.6666666666666666},
28+
{"ab", "aa", 0.6666666666666666},
29+
} {
30+
// When
31+
res := jaroWinkler(testCase.a, testCase.b)
32+
33+
// Then
34+
assert.Equal(t, testCase.expected, res)
35+
}
36+
}
37+
1138
func TestSuggestFlag(t *testing.T) {
1239
// Given
1340
app := buildExtendedTestCommand()

template.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ var authorsTemplate = `{{with $length := len .Authors}}{{if ne 1 $length}}S{{end
1010
var visibleCommandTemplate = `{{ $cv := offsetCommands .VisibleCommands 5}}{{range .VisibleCommands}}
1111
{{$s := join .Names ", "}}{{$s}}{{ $sp := subtract $cv (offset $s 3) }}{{ indent $sp ""}}{{wrap .Usage $cv}}{{end}}`
1212
var visibleCommandCategoryTemplate = `{{range .VisibleCategories}}{{if .Name}}
13+
1314
{{.Name}}:{{range .VisibleCommands}}
1415
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{template "visibleCommandTemplate" .}}{{end}}{{end}}`
1516
var visibleFlagCategoryTemplate = `{{range .VisibleFlagCategories}}

0 commit comments

Comments
 (0)