|
| 1 | +package commands |
| 2 | + |
| 3 | +import ( |
| 4 | + "fmt" |
| 5 | + "os" |
| 6 | + "path/filepath" |
| 7 | + "strings" |
| 8 | + |
| 9 | + "github.com/MayR-Labs/mayrlabs-go/internal/utils" |
| 10 | + "github.com/spf13/cobra" |
| 11 | +) |
| 12 | + |
| 13 | +// InstallCompletionCmd installs autocompletion for the specified shell |
| 14 | +var InstallCompletionCmd = &cobra.Command{ |
| 15 | + Use: "install-completion [shell...]", |
| 16 | + Short: "Install autocompletion for your shell", |
| 17 | + Long: "Install autocompletion for zsh, bash, fish, or powershell. You can select multiple shells.", |
| 18 | + RunE: func(cmd *cobra.Command, args []string) error { |
| 19 | + shells := args |
| 20 | + if len(shells) == 0 { |
| 21 | + selected, err := utils.PromptMultiSelect( |
| 22 | + "Select shells to install completion for:", |
| 23 | + []string{"zsh", "bash", "fish", "powershell"}, |
| 24 | + ) |
| 25 | + if err != nil { |
| 26 | + return err |
| 27 | + } |
| 28 | + shells = selected |
| 29 | + } |
| 30 | + |
| 31 | + if len(shells) == 0 { |
| 32 | + return fmt.Errorf("no shells selected") |
| 33 | + } |
| 34 | + |
| 35 | + homeDir, err := os.UserHomeDir() |
| 36 | + if err != nil { |
| 37 | + return fmt.Errorf("failed to get home directory: %w", err) |
| 38 | + } |
| 39 | + |
| 40 | + configDir := filepath.Join(homeDir, ".mayrlabs") |
| 41 | + if err := os.MkdirAll(configDir, 0755); err != nil { |
| 42 | + return fmt.Errorf("failed to create config directory: %w", err) |
| 43 | + } |
| 44 | + |
| 45 | + for _, shell := range shells { |
| 46 | + fmt.Printf("Installing completion for %s...\n", shell) |
| 47 | + |
| 48 | + var completionFile string |
| 49 | + var rcFile string |
| 50 | + var sourceCmd string |
| 51 | + |
| 52 | + switch shell { |
| 53 | + case "zsh": |
| 54 | + completionFile = filepath.Join(configDir, "completion.zsh") |
| 55 | + rcFile = filepath.Join(homeDir, ".zshrc") |
| 56 | + sourceCmd = fmt.Sprintf("source %s", completionFile) |
| 57 | + // Generate completion |
| 58 | + if err := cmd.Root().GenZshCompletionFile(completionFile); err != nil { |
| 59 | + fmt.Printf("❌ Failed to generate zsh completion: %v\n", err) |
| 60 | + continue |
| 61 | + } |
| 62 | + case "bash": |
| 63 | + completionFile = filepath.Join(configDir, "completion.bash") |
| 64 | + rcFile = filepath.Join(homeDir, ".bashrc") |
| 65 | + // On macOS, it might be .bash_profile |
| 66 | + if _, err := os.Stat(rcFile); os.IsNotExist(err) { |
| 67 | + profile := filepath.Join(homeDir, ".bash_profile") |
| 68 | + if _, err := os.Stat(profile); err == nil { |
| 69 | + rcFile = profile |
| 70 | + } |
| 71 | + } |
| 72 | + sourceCmd = fmt.Sprintf("source %s", completionFile) |
| 73 | + if err := cmd.Root().GenBashCompletionFile(completionFile); err != nil { |
| 74 | + fmt.Printf("❌ Failed to generate bash completion: %v\n", err) |
| 75 | + continue |
| 76 | + } |
| 77 | + case "fish": |
| 78 | + completionFile = filepath.Join(configDir, "completion.fish") |
| 79 | + configPath := filepath.Join(homeDir, ".config", "fish") |
| 80 | + rcFile = filepath.Join(configPath, "config.fish") |
| 81 | + sourceCmd = fmt.Sprintf("source %s", completionFile) |
| 82 | + |
| 83 | + // Ensure fish config dir exists |
| 84 | + _ = os.MkdirAll(configPath, 0755) |
| 85 | + |
| 86 | + if err := cmd.Root().GenFishCompletionFile(completionFile, true); err != nil { |
| 87 | + fmt.Printf("❌ Failed to generate fish completion: %v\n", err) |
| 88 | + continue |
| 89 | + } |
| 90 | + case "powershell": |
| 91 | + completionFile = filepath.Join(configDir, "completion.ps1") |
| 92 | + // PowerShell profile path is variable, but usually in Documents |
| 93 | + // This is a simplification |
| 94 | + documentsDir := filepath.Join(homeDir, "Documents") |
| 95 | + // Check for OneDrive |
| 96 | + oneDriveDocs := filepath.Join(homeDir, "OneDrive", "Documents") |
| 97 | + if _, err := os.Stat(oneDriveDocs); err == nil { |
| 98 | + documentsDir = oneDriveDocs |
| 99 | + } |
| 100 | + |
| 101 | + psDir := filepath.Join(documentsDir, "PowerShell") |
| 102 | + _ = os.MkdirAll(psDir, 0755) |
| 103 | + rcFile = filepath.Join(psDir, "Microsoft.PowerShell_profile.ps1") |
| 104 | + |
| 105 | + sourceCmd = fmt.Sprintf(". %s", completionFile) |
| 106 | + if err := cmd.Root().GenPowerShellCompletionFile(completionFile); err != nil { |
| 107 | + fmt.Printf("❌ Failed to generate powershell completion: %v\n", err) |
| 108 | + continue |
| 109 | + } |
| 110 | + default: |
| 111 | + fmt.Printf("⚠️ Unsupported shell: %s\n", shell) |
| 112 | + continue |
| 113 | + } |
| 114 | + |
| 115 | + // Add to RC file if not present |
| 116 | + if rcFile != "" { |
| 117 | + // Check if RC file exists, if not create it |
| 118 | + f, err := os.OpenFile(rcFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) |
| 119 | + if err != nil { |
| 120 | + fmt.Printf("⚠️ Could not open RC file %s: %v\n", rcFile, err) |
| 121 | + fmt.Printf(" Please manually add this line to your config: %s\n", sourceCmd) |
| 122 | + continue |
| 123 | + } |
| 124 | + |
| 125 | + content, err := os.ReadFile(rcFile) |
| 126 | + if err == nil { |
| 127 | + if !strings.Contains(string(content), completionFile) { |
| 128 | + if _, err := f.WriteString(fmt.Sprintf("\n# mayrlabs completion\n%s\n", sourceCmd)); err != nil { |
| 129 | + fmt.Printf("⚠️ Failed to write to RC file: %v\n", err) |
| 130 | + } else { |
| 131 | + fmt.Printf("✅ Added source command to %s\n", rcFile) |
| 132 | + } |
| 133 | + } else { |
| 134 | + fmt.Printf("ℹ️ Completion already configured in %s\n", rcFile) |
| 135 | + } |
| 136 | + } |
| 137 | + _ = f.Close() |
| 138 | + } |
| 139 | + |
| 140 | + fmt.Printf("✅ Completion installed for %s!\n", shell) |
| 141 | + } |
| 142 | + |
| 143 | + fmt.Println("\n🎉 Installation complete! Please restart your shell or source your config file to apply changes.") |
| 144 | + return nil |
| 145 | + }, |
| 146 | +} |
0 commit comments