284 lines
4.8 KiB
Go
284 lines
4.8 KiB
Go
package main
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
gCmdWords = []string{
|
|
"set",
|
|
"map",
|
|
"cmd",
|
|
"up",
|
|
"half-up",
|
|
"page-up",
|
|
"down",
|
|
"half-down",
|
|
"page-down",
|
|
"updir",
|
|
"open",
|
|
"quit",
|
|
"top",
|
|
"bot",
|
|
"toggle",
|
|
"invert",
|
|
"unmark",
|
|
"yank",
|
|
"delete",
|
|
"put",
|
|
"clear",
|
|
"draw",
|
|
"redraw",
|
|
"reload",
|
|
"read",
|
|
"shell",
|
|
"shell-pipe",
|
|
"shell-wait",
|
|
"shell-async",
|
|
"search",
|
|
"search-back",
|
|
"search-next",
|
|
"search-prev",
|
|
"sync",
|
|
"echo",
|
|
"cd",
|
|
"select",
|
|
"push",
|
|
}
|
|
|
|
gOptWords = []string{
|
|
"dircounts",
|
|
"nodircounts",
|
|
"dircounts!",
|
|
"dirfirst",
|
|
"nodirfirst",
|
|
"dirfirst!",
|
|
"globsearch",
|
|
"noglobsearch",
|
|
"globsearch!",
|
|
"hidden",
|
|
"nohidden",
|
|
"hidden!",
|
|
"ignorecase",
|
|
"noignorecase",
|
|
"ignorecase!",
|
|
"preview",
|
|
"nopreview",
|
|
"preview!",
|
|
"reverse",
|
|
"noreverse",
|
|
"reverse!",
|
|
"smartcase",
|
|
"nosmartcase",
|
|
"smartcase!",
|
|
"wrapscan",
|
|
"nowrapscan",
|
|
"wrapscan!",
|
|
"scrolloff",
|
|
"tabstop",
|
|
"filesep",
|
|
"ifs",
|
|
"previewer",
|
|
"promptfmt",
|
|
"shell",
|
|
"sortby",
|
|
"timefmt",
|
|
"ratios",
|
|
"info",
|
|
}
|
|
)
|
|
|
|
func matchLongest(s1, s2 string) string {
|
|
i := 0
|
|
for ; i < len(s1) && i < len(s2); i++ {
|
|
if s1[i] != s2[i] {
|
|
break
|
|
}
|
|
}
|
|
return s1[:i]
|
|
}
|
|
|
|
func matchWord(s string, words []string) (matches []string, longest string) {
|
|
for _, w := range words {
|
|
if strings.HasPrefix(w, s) {
|
|
matches = append(matches, w)
|
|
if longest != "" {
|
|
longest = matchLongest(longest, w)
|
|
} else if s != "" {
|
|
longest = w + " "
|
|
}
|
|
}
|
|
}
|
|
|
|
if longest == "" {
|
|
longest = s
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func matchExec(s string) (matches []string, longest string) {
|
|
var words []string
|
|
|
|
paths := strings.Split(envPath, ":")
|
|
|
|
for _, p := range paths {
|
|
if _, err := os.Stat(p); os.IsNotExist(err) {
|
|
continue
|
|
}
|
|
|
|
fi, err := ioutil.ReadDir(p)
|
|
if err != nil {
|
|
log.Printf("reading path: %s", err)
|
|
}
|
|
|
|
for _, f := range fi {
|
|
if strings.HasPrefix(f.Name(), s) {
|
|
f, err = os.Stat(filepath.Join(p, f.Name()))
|
|
if err != nil {
|
|
log.Printf("getting file information: %s", err)
|
|
continue
|
|
}
|
|
|
|
if !f.Mode().IsRegular() || f.Mode()&0111 == 0 {
|
|
continue
|
|
}
|
|
|
|
words = append(words, f.Name())
|
|
}
|
|
}
|
|
}
|
|
|
|
sort.Strings(words)
|
|
|
|
if len(words) > 0 {
|
|
uniq := words[:1]
|
|
for i := 1; i < len(words); i++ {
|
|
if words[i] != words[i-1] {
|
|
uniq = append(uniq, words[i])
|
|
}
|
|
}
|
|
words = uniq
|
|
}
|
|
|
|
return matchWord(s, words)
|
|
}
|
|
|
|
func matchFile(s string) (matches []string, longest string) {
|
|
dir := strings.Replace(s, "~", gUser.HomeDir, -1)
|
|
|
|
if !filepath.IsAbs(dir) {
|
|
wd, err := os.Getwd()
|
|
if err != nil {
|
|
log.Printf("getting current directory: %s", err)
|
|
}
|
|
dir = wd + string(filepath.Separator) + dir
|
|
}
|
|
|
|
dir = unescape(filepath.Dir(dir))
|
|
|
|
fi, err := ioutil.ReadDir(dir)
|
|
if err != nil {
|
|
log.Printf("reading directory: %s", err)
|
|
}
|
|
|
|
for _, f := range fi {
|
|
f, err := os.Stat(filepath.Join(dir, f.Name()))
|
|
if err != nil {
|
|
log.Printf("getting file information: %s", err)
|
|
return
|
|
}
|
|
|
|
_, last := filepath.Split(s)
|
|
if strings.HasPrefix(escape(f.Name()), last) {
|
|
name := f.Name()
|
|
if isRoot(s) || filepath.Base(s) != s {
|
|
name = filepath.Join(filepath.Dir(s), f.Name())
|
|
}
|
|
name = escape(name)
|
|
item := f.Name()
|
|
if f.Mode().IsDir() {
|
|
item += string(filepath.Separator)
|
|
}
|
|
matches = append(matches, item)
|
|
if longest != "" {
|
|
longest = matchLongest(longest, name)
|
|
} else if s != "" {
|
|
if f.Mode().IsRegular() {
|
|
longest = name + " "
|
|
} else {
|
|
longest = name + string(filepath.Separator)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if longest == "" {
|
|
longest = s
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func compCmd(acc []rune) (matches []string, longestAcc []rune) {
|
|
s := string(acc)
|
|
f := tokenize(s)
|
|
|
|
var longest string
|
|
|
|
switch len(f) {
|
|
case 1:
|
|
words := gCmdWords
|
|
for c := range gOpts.cmds {
|
|
words = append(words, c)
|
|
}
|
|
sort.Strings(words)
|
|
matches, longest = matchWord(s, words)
|
|
longestAcc = []rune(longest)
|
|
case 2:
|
|
switch f[0] {
|
|
case "set":
|
|
matches, longest = matchWord(f[1], gOptWords)
|
|
longestAcc = append(acc[:len(acc)-len(f[len(f)-1])], []rune(longest)...)
|
|
case "map", "cmd":
|
|
longestAcc = acc
|
|
default:
|
|
matches, longest = matchFile(f[len(f)-1])
|
|
longestAcc = append(acc[:len(acc)-len(f[len(f)-1])], []rune(longest)...)
|
|
}
|
|
default:
|
|
switch f[0] {
|
|
case "set", "map", "cmd":
|
|
longestAcc = acc
|
|
default:
|
|
matches, longest = matchFile(f[len(f)-1])
|
|
longestAcc = append(acc[:len(acc)-len(f[len(f)-1])], []rune(longest)...)
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func compShell(acc []rune) (matches []string, longestAcc []rune) {
|
|
s := string(acc)
|
|
f := tokenize(s)
|
|
|
|
var longest string
|
|
|
|
switch len(f) {
|
|
case 1:
|
|
matches, longest = matchExec(s)
|
|
longestAcc = []rune(longest)
|
|
default:
|
|
matches, longest = matchFile(f[len(f)-1])
|
|
longestAcc = append(acc[:len(acc)-len(f[len(f)-1])], []rune(longest)...)
|
|
}
|
|
|
|
return
|
|
}
|