From 7ecf2cd215838551f373a33d2a3262020db98109 Mon Sep 17 00:00:00 2001 From: comp500 Date: Wed, 18 Sep 2019 22:58:46 +0100 Subject: [PATCH] Add bash/powershell/zsh completions Was this really necessary? No Are there more important things to implement? Yes Do I care? No --- main.go | 1 + utils/completion.go | 186 ++++++++++++++++++++++++++++++++++++++++++++ utils/utils.go | 16 ++++ 3 files changed, 203 insertions(+) create mode 100644 utils/completion.go create mode 100644 utils/utils.go diff --git a/main.go b/main.go index a06120d..7638a14 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( // Modules of packwiz "github.com/comp500/packwiz/cmd" _ "github.com/comp500/packwiz/curseforge" + _ "github.com/comp500/packwiz/utils" ) func main() { diff --git a/utils/completion.go b/utils/completion.go new file mode 100644 index 0000000..bcd20ed --- /dev/null +++ b/utils/completion.go @@ -0,0 +1,186 @@ +package utils + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// completionCmd represents the completion command +var completionCmd = &cobra.Command{ + Use: "completion [bash/powershell/zsh]", + Short: "Installs bash/powershell/zsh completion commands", + Long: `Installs bash/powershell/zsh completion commands. +Please note that the completions may be incomplete or broken, see https://github.com/spf13/cobra`, + Args: cobra.ExactValidArgs(1), + ValidArgs: []string{"bash", "powershell", "zsh"}, + Run: func(cmd *cobra.Command, args []string) { + if args[0] == "bash" { + if viper.GetBool("utils.completion.source") { + cmd.Root().GenBashCompletion(os.Stdout) + } else { + file, err := getConfigPath("completion.sh") + if err != nil { + fmt.Printf("Error saving completion file: %s\n", err) + os.Exit(1) + } + err = cmd.Root().GenBashCompletionFile(file) + if err != nil { + fmt.Printf("Error saving completion file: %s\n", err) + os.Exit(1) + } + + // Get the value of $HOME + home, err := os.UserHomeDir() + if err != nil { + fmt.Printf("Failed to get $HOME location: %s\n", err) + os.Exit(1) + } + bashrc := filepath.Join(home, ".bashrc") + + absFile, err := filepath.Abs(file) + if err != nil { + fmt.Printf("Failed to resolve path: %s\n", err) + os.Exit(1) + } + command := ". " + absFile + commandFound := false + // Check for existing text in bashrc + data, err := ioutil.ReadFile(bashrc) + if err == nil { + commandFound = strings.Contains(string(data), command) + } + if !commandFound { + // Append to bashrc + os.MkdirAll(filepath.Dir(bashrc), os.ModePerm) + f, err := os.OpenFile(bashrc, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + fmt.Printf("Failed to open bashrc: %s\n", err) + os.Exit(1) + } + _, err = f.WriteString("\r\n" + command + "\r\n") + if err != nil { + fmt.Printf("Failed to write to bashrc: %s\n", err) + f.Close() + os.Exit(1) + } + f.Close() + } + fmt.Println("Completions installed! Restart your shell to load them.") + } + } else if args[0] == "powershell" { + if viper.GetBool("utils.completion.source") { + cmd.Root().GenPowerShellCompletion(os.Stdout) + } else { + file, err := getConfigPath("completion.ps1") + if err != nil { + fmt.Printf("Error saving completion file: %s\n", err) + os.Exit(1) + } + err = cmd.Root().GenPowerShellCompletionFile(file) + if err != nil { + fmt.Printf("Error saving completion file: %s\n", err) + os.Exit(1) + } + + // Get the value of $PROFILE (not an environment variable!!) + profile, err := getPowershellProfile() + if err != nil { + fmt.Printf("Failed to get profile location: %s\n", err) + os.Exit(1) + } + + absFile, err := filepath.Abs(file) + if err != nil { + fmt.Printf("Failed to resolve path: %s\n", err) + os.Exit(1) + } + command := ". " + absFile + commandFound := false + // Check for existing text in profile + data, err := ioutil.ReadFile(profile) + if err == nil { + commandFound = strings.Contains(string(data), command) + } + if !commandFound { + // Append to profile + os.MkdirAll(filepath.Dir(profile), os.ModePerm) + f, err := os.OpenFile(profile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + fmt.Printf("Failed to open profile: %s\n", err) + os.Exit(1) + } + _, err = f.WriteString("\r\n" + command + "\r\n") + if err != nil { + fmt.Printf("Failed to write to profile: %s\n", err) + f.Close() + os.Exit(1) + } + f.Close() + } + fmt.Println("Completions installed! Restart your shell to load them.") + } + } else if args[0] == "zsh" { + if viper.GetBool("utils.completion.source") { + cmd.Root().GenZshCompletion(os.Stdout) + } else { + file, err := getConfigPath("completion.zsh") + if err != nil { + fmt.Printf("Error saving completion file: %s\n", err) + os.Exit(1) + } + err = cmd.Root().GenZshCompletionFile(file) + if err != nil { + fmt.Printf("Error saving completion file: %s\n", err) + os.Exit(1) + } + + // If someone knows how to do this automagically, please PR it!! + fmt.Println("Completions saved to " + file) + fmt.Println("You need to put this file in your $fpath manually!") + } + } + }, +} + +func getConfigPath(fileName string) (string, error) { + dir, err := os.UserConfigDir() + if err != nil { + return "", err + } + dir = filepath.Join(dir, "packwiz") + err = os.MkdirAll(dir, os.ModePerm) + if err != nil { + return "", err + } + return filepath.Join(dir, fileName), nil +} + +func getPowershellProfile() (string, error) { + ps, err := exec.LookPath("powershell.exe") + if err != nil { + return "", err + } + cmd := exec.Command(ps, "-NoProfile", "-NonInteractive", "-Command", "$PROFILE") + var stdout bytes.Buffer + cmd.Stdout = &stdout + err = cmd.Run() + if err != nil { + return "", err + } + return strings.TrimSpace(stdout.String()), nil +} + +func init() { + utilsCmd.AddCommand(completionCmd) + + completionCmd.Flags().Bool("source", false, "Output the source of the commands to be installed, rather than installing them") + viper.BindPFlag("utils.completion.source", completionCmd.Flags().Lookup("source")) +} diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..eb8436c --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,16 @@ +package utils + +import ( + "github.com/comp500/packwiz/cmd" + "github.com/spf13/cobra" +) + +// utilsCmd represents the base command when called without any subcommands +var utilsCmd = &cobra.Command{ + Use: "utils", + Short: "Utilities for managing packwiz itself", +} + +func init() { + cmd.Add(utilsCmd) +}