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