Hilbish/readline/completers/local-filesystem.go

206 lines
5.3 KiB
Go
Raw Permalink Normal View History

package completers
import (
"io/ioutil"
"os"
"os/user"
"path/filepath"
"strings"
"github.com/maxlandon/readline"
)
func completeLocalPath(last string) (string, *readline.CompletionGroup) {
// Completions
completion := &readline.CompletionGroup{
Name: "(console) local path",
MaxLength: 10, // The grid system is not yet able to roll on comps if > MaxLength
DisplayType: readline.TabDisplayGrid,
TrimSlash: true,
}
var suggestions []string
// Any parsing error is silently ignored, for not messing the prompt
processedPath, _ := ParseEnvironmentVariables([]string{last})
// Check if processed input is empty
var inputPath string
if len(processedPath) == 1 {
inputPath = processedPath[0]
}
// Add a slash if the raw input has one but not the processed input
if len(last) > 0 && last[len(last)-1] == '/' {
inputPath += "/"
}
var linePath string // curated version of the inputPath
var absPath string // absolute path (excluding suffix) of the inputPath
var lastPath string // last directory in the input path
if strings.HasSuffix(string(inputPath), "/") {
linePath = filepath.Dir(string(inputPath))
absPath, _ = expand(string(linePath)) // Get absolute path
} else if string(inputPath) == "" {
linePath = "."
absPath, _ = expand(string(linePath))
} else {
linePath = filepath.Dir(string(inputPath))
absPath, _ = expand(string(linePath)) // Get absolute path
lastPath = filepath.Base(string(inputPath)) // Save filter
}
// 2) We take the absolute path we found, and get all dirs in it.
var dirs []string
files, _ := ioutil.ReadDir(absPath)
for _, file := range files {
if file.IsDir() {
dirs = append(dirs, file.Name())
}
}
switch lastPath {
case "":
for _, dir := range dirs {
if strings.HasPrefix(dir, lastPath) || lastPath == dir {
tokenized := addSpaceTokens(dir)
suggestions = append(suggestions, tokenized+"/")
}
}
default:
filtered := []string{}
for _, dir := range dirs {
if strings.HasPrefix(dir, lastPath) {
filtered = append(filtered, dir)
}
}
for _, dir := range filtered {
if !hasPrefix([]rune(lastPath), []rune(dir)) || lastPath == dir {
tokenized := addSpaceTokens(dir)
suggestions = append(suggestions, tokenized+"/")
}
}
}
completion.Suggestions = suggestions
return string(lastPath), completion
}
func addSpaceTokens(in string) (path string) {
items := strings.Split(in, " ")
for i := range items {
if len(items) == i+1 { // If last one, no char, add and return
path += items[i]
return
}
path += items[i] + "\\ " // By default add space char and roll
}
return
}
func completeLocalPathAndFiles(last string) (string, *readline.CompletionGroup) {
// Completions
completion := &readline.CompletionGroup{
Name: "(console) local directory/files",
MaxLength: 10, // The grid system is not yet able to roll on comps if > MaxLength
DisplayType: readline.TabDisplayGrid,
TrimSlash: true,
}
var suggestions []string
// Any parsing error is silently ignored, for not messing the prompt
processedPath, _ := ParseEnvironmentVariables([]string{last})
// Check if processed input is empty
var inputPath string
if len(processedPath) == 1 {
inputPath = processedPath[0]
}
// Add a slash if the raw input has one but not the processed input
if len(last) > 0 && last[len(last)-1] == '/' {
inputPath += "/"
}
var linePath string // curated version of the inputPath
var absPath string // absolute path (excluding suffix) of the inputPath
var lastPath string // last directory in the input path
if strings.HasSuffix(string(inputPath), "/") {
linePath = filepath.Dir(string(inputPath)) // Trim the non needed slash
absPath, _ = expand(string(linePath)) // Get absolute path
} else if string(inputPath) == "" {
linePath = "."
absPath, _ = expand(string(linePath))
} else {
linePath = filepath.Dir(string(inputPath))
absPath, _ = expand(string(linePath)) // Get absolute path
lastPath = filepath.Base(string(inputPath)) // Save filter
}
// 2) We take the absolute path we found, and get all dirs in it.
var dirs []string
files, _ := ioutil.ReadDir(absPath)
for _, file := range files {
if file.IsDir() {
dirs = append(dirs, file.Name())
}
}
switch lastPath {
case "":
for _, file := range files {
if strings.HasPrefix(file.Name(), lastPath) || lastPath == file.Name() {
if file.IsDir() {
suggestions = append(suggestions, file.Name()+"/")
} else {
suggestions = append(suggestions, file.Name())
}
}
}
default:
filtered := []os.FileInfo{}
for _, file := range files {
if strings.HasPrefix(file.Name(), lastPath) {
filtered = append(filtered, file)
}
}
for _, file := range filtered {
if !hasPrefix([]rune(lastPath), []rune(file.Name())) || lastPath == file.Name() {
if file.IsDir() {
suggestions = append(suggestions, file.Name()+"/")
} else {
suggestions = append(suggestions, file.Name())
}
}
}
}
completion.Suggestions = suggestions
return string(lastPath), completion
}
// expand will expand a path with ~ to the $HOME of the current user.
func expand(path string) (string, error) {
if path == "" {
return path, nil
}
home := os.Getenv("HOME")
if home == "" {
usr, err := user.Current()
if err != nil {
return "", err
}
home = usr.HomeDir
}
return filepath.Abs(strings.Replace(path, "~", home, 1))
}