diff --git a/args.go b/args.go index 2dbb816..73c356d 100644 --- a/args.go +++ b/args.go @@ -28,19 +28,13 @@ type Args struct { // in case that it is not, we fall back to the current directory. func (a Args) Directory() string { if info, err := os.Stat(a.Last); err == nil && info.IsDir() { - if !filepath.IsAbs(a.Last) { - return relativePath(a.Last) - } - return a.Last + return fixPathForm(a.Last, a.Last) } dir := filepath.Dir(a.Last) if info, err := os.Stat(dir); err != nil || !info.IsDir() { return "./" } - if !filepath.IsAbs(dir) { - dir = relativePath(dir) - } - return dir + return fixPathForm(a.Last, dir) } func newArgs(line []string) Args { diff --git a/args_test.go b/args_test.go index 78d346f..a211815 100644 --- a/args_test.go +++ b/args_test.go @@ -162,7 +162,7 @@ func TestArgs_Directory(t *testing.T) { }, { line: "a b c /tmp", - directory: "/tmp", + directory: "/tmp/", }, { line: "a b c /tmp ", @@ -178,7 +178,7 @@ func TestArgs_Directory(t *testing.T) { }, { line: "a b c dir", - directory: "./dir/", + directory: "dir/", }, { line: "a b c ./di", diff --git a/complete_test.go b/complete_test.go index cd61ceb..135c6ad 100644 --- a/complete_test.go +++ b/complete_test.go @@ -35,8 +35,6 @@ func TestCompleter_Complete(t *testing.T) { }, } - testTXTFiles := []string{"./a.txt", "./b.txt", "./c.txt", "./.dot.txt"} - tests := []struct { args string want []string @@ -75,7 +73,7 @@ func TestCompleter_Complete(t *testing.T) { }, { args: "sub2 ", - want: []string{"./", "./dir/", "./outer/", "./readme.md", "-flag2", "-flag3", "-h", "-global1"}, + want: []string{"./", "dir/", "outer/", "readme.md", "-flag2", "-flag3", "-h", "-global1"}, }, { args: "sub2 ./", @@ -83,11 +81,15 @@ func TestCompleter_Complete(t *testing.T) { }, { args: "sub2 re", + want: []string{"readme.md"}, + }, + { + args: "sub2 ./re", want: []string{"./readme.md"}, }, { args: "sub2 -flag2 ", - want: []string{"./", "./dir/", "./outer/", "./readme.md", "-flag2", "-flag3", "-h", "-global1"}, + want: []string{"./", "dir/", "outer/", "readme.md", "-flag2", "-flag3", "-h", "-global1"}, }, { args: "sub1 -fl", @@ -123,7 +125,7 @@ func TestCompleter_Complete(t *testing.T) { }, { args: "-o ", - want: append(testTXTFiles, "./", "./dir/", "./outer/"), + want: []string{"a.txt", "b.txt", "c.txt", ".dot.txt", "./", "dir/", "outer/"}, }, { args: "-o ./no-su", @@ -131,7 +133,11 @@ func TestCompleter_Complete(t *testing.T) { }, { args: "-o ./", - want: append(testTXTFiles, "./", "./dir/", "./outer/"), + want: []string{"./a.txt", "./b.txt", "./c.txt", "./.dot.txt", "./", "./dir/", "./outer/"}, + }, + { + args: "-o .", + want: []string{"./a.txt", "./b.txt", "./c.txt", "./.dot.txt", "./", "./dir/", "./outer/"}, }, { args: "-o ./read", diff --git a/gocomplete/tests_test.go b/gocomplete/tests_test.go index f1b294a..a72f38f 100644 --- a/gocomplete/tests_test.go +++ b/gocomplete/tests_test.go @@ -1,10 +1,11 @@ package main import ( + "os" + "sort" "testing" "github.com/posener/complete" - "os" ) func TestPredictions(t *testing.T) { @@ -49,7 +50,7 @@ func TestPredictions(t *testing.T) { { name: "predict runnable ok", predictor: complete.PredictFunc(predictRunnableFiles), - completion: []string{"./complete.go"}, + completion: []string{"complete.go"}, }, { name: "predict runnable not found", @@ -79,6 +80,8 @@ func Example() { } func equal(s1, s2 []string) bool { + sort.Strings(s1) + sort.Strings(s2) if len(s1) != len(s2) { return false } diff --git a/match/file.go b/match/file.go index eee5bec..051171e 100644 --- a/match/file.go +++ b/match/file.go @@ -4,13 +4,16 @@ import "strings" // File returns true if prefix can match the file func File(file, prefix string) bool { - // special case for current directory completion if file == "./" && (prefix == "." || prefix == "") { return true } + if prefix == "." && strings.HasPrefix(file, ".") { + return true + } file = strings.TrimPrefix(file, "./") prefix = strings.TrimPrefix(prefix, "./") + return strings.HasPrefix(file, prefix) } diff --git a/predict_files.go b/predict_files.go index 0ba2d79..4b8c84a 100644 --- a/predict_files.go +++ b/predict_files.go @@ -42,7 +42,7 @@ func files(pattern string, allowFiles bool) PredictFunc { // if the result is only one item, we might want to recursively check // for more accurate results. - if prediction[0] == a.Last { // avoid loop forever + if prediction[0] == a.Last { return } @@ -73,13 +73,9 @@ func predictFiles(a Args, pattern string, allowFiles bool) []string { // PredictFilesSet predict according to file rules to a given set of file names func PredictFilesSet(files []string) PredictFunc { return func(a Args) (prediction []string) { - rel := !filepath.IsAbs(a.Directory()) // add all matching files to prediction for _, f := range files { - // change file name to relative if necessary - if rel { - f = relativePath(f) - } + f = fixPathForm(a.Last, f) // test matching of file to the argument if match.File(f, a.Last) { diff --git a/predict_test.go b/predict_test.go index b2840c0..ac26e33 100644 --- a/predict_test.go +++ b/predict_test.go @@ -60,7 +60,7 @@ func TestPredicate(t *testing.T) { { name: "files/txt", p: PredictFiles("*.txt"), - want: []string{"./", "./dir/", "./outer/", "./a.txt", "./b.txt", "./c.txt", "./.dot.txt"}, + want: []string{"./", "dir/", "outer/", "a.txt", "b.txt", "c.txt", ".dot.txt"}, }, { name: "files/txt", @@ -83,38 +83,68 @@ func TestPredicate(t *testing.T) { { name: "files/md", p: PredictFiles("*.md"), - argList: []string{"", ".", "./"}, + argList: []string{""}, + want: []string{"./", "dir/", "outer/", "readme.md"}, + }, + { + name: "files/md with ./ prefix", + p: PredictFiles("*.md"), + argList: []string{".", "./"}, want: []string{"./", "./dir/", "./outer/", "./readme.md"}, }, { name: "dirs", p: PredictDirs("*"), - argList: []string{"./dir/", "./di", "di", "dir", "dir/"}, + argList: []string{"di", "dir", "dir/"}, + want: []string{"dir/"}, + }, + { + name: "dirs with ./ prefix", + p: PredictDirs("*"), + argList: []string{"./di", "./dir", "./dir/"}, want: []string{"./dir/"}, }, { name: "predict anything in dir", p: PredictFiles("*"), - argList: []string{"./dir", "dir", "./dir/", "./di"}, + argList: []string{"dir", "dir/", "di"}, + want: []string{"dir/", "dir/foo", "dir/bar"}, + }, + { + name: "predict anything in dir with ./ prefix", + p: PredictFiles("*"), + argList: []string{"./dir", "./dir/", "./di"}, want: []string{"./dir/", "./dir/foo", "./dir/bar"}, }, { name: "root directories", p: PredictDirs("*"), - argList: []string{"", ".", "./"}, + argList: []string{""}, + want: []string{"./", "dir/", "outer/"}, + }, + { + name: "root directories with ./ prefix", + p: PredictDirs("*"), + argList: []string{".", "./"}, want: []string{"./", "./dir/", "./outer/"}, }, { name: "nested directories", p: PredictDirs("*.md"), - argList: []string{"ou", "./ou", "./outer", "./outer/"}, + argList: []string{"ou", "outer", "outer/"}, + want: []string{"outer/", "outer/inner/"}, + }, + { + name: "nested directories with ./ prefix", + p: PredictDirs("*.md"), + argList: []string{"./ou", "./outer", "./outer/"}, want: []string{"./outer/", "./outer/inner/"}, }, { name: "nested inner directory", p: PredictFiles("*.md"), argList: []string{"outer/i"}, - want: []string{"./outer/inner/", "./outer/inner/readme.md"}, + want: []string{"outer/inner/", "outer/inner/readme.md"}, }, } diff --git a/utils.go b/utils.go index a59a0d4..58b8b79 100644 --- a/utils.go +++ b/utils.go @@ -3,10 +3,11 @@ package complete import ( "os" "path/filepath" + "strings" ) -// relativePath changes a file name to a relative name -func relativePath(file string) string { +// fixPathForm changes a file name to a relative name +func fixPathForm(last string, file string) string { // get wording directory for relative name workDir, err := os.Getwd() if err != nil { @@ -17,15 +18,29 @@ func relativePath(file string) string { if err != nil { return file } + + // if last is absolute, return path as absolute + if filepath.IsAbs(last) { + return fixDirPath(abs) + } + rel, err := filepath.Rel(workDir, abs) if err != nil { return file } - if rel != "." { + + // fix ./ prefix of path + if rel != "." && strings.HasPrefix(last, ".") { rel = "./" + rel } - if info, err := os.Stat(rel); err == nil && info.IsDir() { - rel += "/" - } - return rel + + return fixDirPath(rel) +} + +func fixDirPath(path string) string { + info, err := os.Stat(path) + if err == nil && info.IsDir() && !strings.HasSuffix(path, "/") { + path += "/" + } + return path }