Fix './' prefix for file completion
This commit is contained in:
parent
659bd9e3d5
commit
61d9904ba1
10
args.go
10
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 {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
29
utils.go
29
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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue