Fix './' prefix for file completion

This commit is contained in:
Eyal Posener 2017-05-15 23:52:04 +03:00
parent 659bd9e3d5
commit 61d9904ba1
8 changed files with 86 additions and 39 deletions

10
args.go
View File

@ -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 {

View File

@ -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",

View File

@ -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",

View File

@ -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
}

View File

@ -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)
}

View File

@ -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) {

View File

@ -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"},
},
}

View File

@ -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
}