172 lines · 4.6 KB · Go
RawDownload
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_FILEfunc compLogf(format string, a ...any) {	cobra.CompDebugln(fmt.Sprintf(format, a...), true)} // Add remotes to the completions being built upfunc 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 upfunc 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 upfunc 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 BashCompletionV2const 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}