1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
package cmd
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/cache"
"github.com/rclone/rclone/fs/config"
"github.com/rclone/rclone/fs/fspath"
"github.com/spf13/cobra"
)
// Make a debug message while doing the completion.
//
// These end up in the file specified by BASH_COMP_DEBUG_FILE
func compLogf(format string, a ...any) {
cobra.CompDebugln(fmt.Sprintf(format, a...), true)
}
// Add remotes to the completions being built up
func addRemotes(toComplete string, completions []string) []string {
remotes := config.FileSections()
for _, remote := range remotes {
remote += ":"
if strings.HasPrefix(remote, toComplete) {
completions = append(completions, remote)
}
}
return completions
}
// Add local files to the completions being built up
func addLocalFiles(toComplete string, result cobra.ShellCompDirective, completions []string) (cobra.ShellCompDirective, []string) {
path := filepath.Clean(toComplete)
dir, file := filepath.Split(path)
if dir == "" {
dir = "."
}
if len(dir) > 0 && dir[0] != filepath.Separator && dir[0] != '/' {
dir = strings.TrimRight(dir, string(filepath.Separator))
dir = strings.TrimRight(dir, "/")
}
fi, err := os.Stat(toComplete)
if err == nil {
if fi.IsDir() {
dir = toComplete
file = ""
}
}
fis, err := os.ReadDir(dir)
if err != nil {
compLogf("Failed to read directory %q: %v", dir, err)
return result, completions
}
for _, fi := range fis {
name := fi.Name()
if strings.HasPrefix(name, file) {
path := filepath.Join(dir, name)
if fi.IsDir() {
path += string(filepath.Separator)
result |= cobra.ShellCompDirectiveNoSpace
}
completions = append(completions, path)
}
}
return result, completions
}
// Add remote files to the completions being built up
func addRemoteFiles(toComplete string, result cobra.ShellCompDirective, completions []string) (cobra.ShellCompDirective, []string) {
ctx := context.Background()
parent, _, err := fspath.Split(toComplete)
if err != nil {
compLogf("Failed to split path %q: %v", toComplete, err)
return result, completions
}
f, err := cache.Get(ctx, parent)
if err == fs.ErrorIsFile {
completions = append(completions, toComplete)
return result, completions
} else if err != nil {
compLogf("Failed to make Fs %q: %v", parent, err)
return result, completions
}
fis, err := f.List(ctx, "")
if err != nil {
compLogf("Failed to list Fs %q: %v", parent, err)
return result, completions
}
for _, fi := range fis {
remote := fi.Remote()
path := parent + remote
if strings.HasPrefix(path, toComplete) {
if _, ok := fi.(fs.Directory); ok {
path += "/"
result |= cobra.ShellCompDirectiveNoSpace
}
completions = append(completions, path)
}
}
return result, completions
}
// Workaround doesn't seem to be needed for BashCompletionV2
const useColonWorkaround = false
// do command completion
//
// This is called by the command completion scripts using a hidden __complete or __completeNoDesc commands.
func validArgs(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
compLogf("ValidArgsFunction called with args=%q toComplete=%q", args, toComplete)
fixBug := -1
if useColonWorkaround {
// Work around what I think is a bug in cobra's bash
// completion which seems to be splitting the arguments on :
// Or there is something I don't understand - ncw
args = append(args, toComplete)
colonArg := -1
for i, arg := range args {
if arg == ":" {
colonArg = i
}
}
if colonArg > 0 {
newToComplete := strings.Join(args[colonArg-1:], "")
fixBug = len(newToComplete) - len(toComplete)
toComplete = newToComplete
}
compLogf("...shuffled args=%q toComplete=%q", args, toComplete)
}
result := cobra.ShellCompDirectiveDefault
completions := []string{}
// See whether we have a valid remote yet
_, err := fspath.Parse(toComplete)
parseOK := err == nil
hasColon := strings.ContainsRune(toComplete, ':')
validRemote := parseOK && hasColon
compLogf("valid remote = %v", validRemote)
// Add remotes for completion
if !validRemote {
completions = addRemotes(toComplete, completions)
}
// Add local files for completion
if !validRemote {
result, completions = addLocalFiles(toComplete, result, completions)
}
// Add remote files for completion
if validRemote {
result, completions = addRemoteFiles(toComplete, result, completions)
}
// If using bug workaround, adjust completions to start with :
if useColonWorkaround && fixBug >= 0 {
for i := range completions {
if len(completions[i]) >= fixBug {
completions[i] = completions[i][fixBug:]
}
}
}
return completions, result
}
|