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