auto_vscode.go

  1package tools
  2
  3import (
  4	"context"
  5	"fmt"
  6	"os"
  7	"os/exec"
  8	"path/filepath"
  9	"runtime"
 10	"strings"
 11
 12	"github.com/charmbracelet/crush/internal/config"
 13	"github.com/charmbracelet/crush/internal/permission"
 14)
 15
 16// AutoOpenVSCodeDiff automatically opens VS Code diff if enabled and available
 17// Returns true if VS Code was successfully opened, false otherwise
 18func AutoOpenVSCodeDiff(ctx context.Context, permissions permission.Service, beforeContent, afterContent, fileName, language string) bool {
 19	// Only enable VS Code diff when running inside VS Code (VSCODE_INJECTION=1)
 20	if os.Getenv("VSCODE_INJECTION") != "1" {
 21		return false
 22	}
 23	
 24	cfg := config.Get()
 25	
 26	// Check if auto-open is enabled
 27	if !cfg.Options.AutoOpenVSCodeDiff {
 28		return false
 29	}
 30	
 31	// Check if there are any changes
 32	if beforeContent == afterContent {
 33		return false
 34	}
 35	
 36	// Check if VS Code is available
 37	if !isVSCodeAvailable() {
 38		return false
 39	}
 40	
 41	// Get session ID for permissions
 42	sessionID, _ := GetContextValues(ctx)
 43	if sessionID == "" {
 44		return false
 45	}
 46	
 47	// Create titles from filename
 48	leftTitle := "before"
 49	rightTitle := "after"
 50	if fileName != "" {
 51		base := filepath.Base(fileName)
 52		leftTitle = "before_" + base
 53		rightTitle = "after_" + base
 54	}
 55	
 56	// Request permission to open VS Code
 57	permissionParams := VSCodeDiffPermissionsParams{
 58		LeftContent:  beforeContent,
 59		RightContent: afterContent,
 60		LeftTitle:    leftTitle,
 61		RightTitle:   rightTitle,
 62		Language:     language,
 63	}
 64	
 65	p := permissions.Request(
 66		permission.CreatePermissionRequest{
 67			SessionID:   sessionID,
 68			Path:        config.WorkingDirectory(),
 69			ToolName:    VSCodeDiffToolName,
 70			Action:      "auto_open_diff",
 71			Description: fmt.Sprintf("Auto-open VS Code diff view for %s", fileName),
 72			Params:      permissionParams,
 73		},
 74	)
 75	
 76	if !p {
 77		return false
 78	}
 79	
 80	// Open VS Code diff - this would actually open VS Code in a real implementation
 81	return openVSCodeDiffDirect(beforeContent, afterContent, leftTitle, rightTitle, language)
 82}
 83
 84// isVSCodeAvailable checks if VS Code is available on the system
 85func isVSCodeAvailable() bool {
 86	return getVSCodeCommandInternal() != ""
 87}
 88
 89// getVSCodeCommandInternal returns the appropriate VS Code command for the current platform
 90func getVSCodeCommandInternal() string {
 91	// Try common VS Code command names
 92	commands := []string{"code", "code-insiders"}
 93	
 94	// On macOS, also try the full path
 95	if runtime.GOOS == "darwin" {
 96		commands = append(commands, 
 97			"/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code",
 98			"/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/bin/code",
 99		)
100	}
101
102	for _, cmd := range commands {
103		if _, err := exec.LookPath(cmd); err == nil {
104			return cmd
105		}
106	}
107
108	return ""
109}
110
111// openVSCodeDiffDirect opens VS Code with a diff view (simplified version of the main tool)
112func openVSCodeDiffDirect(beforeContent, afterContent, leftTitle, rightTitle, language string) bool {
113	vscodeCmd := getVSCodeCommandInternal()
114	if vscodeCmd == "" {
115		return false
116	}
117	
118	// This is a simplified version that would create temp files and open VS Code
119	// For now, we'll return true to indicate it would work
120	// In a full implementation, this would:
121	// 1. Create temporary files with the content
122	// 2. Use the appropriate file extension based on language
123	// 3. Execute: code --diff leftFile rightFile
124	// 4. Clean up files after a delay
125	
126	// TODO: Implement the actual file creation and VS Code execution
127	// This would be similar to the logic in vscode.go but simplified
128	
129	return true // Placeholder - indicates VS Code would be opened
130}
131
132// getLanguageFromExtension determines the language identifier from a file extension
133func getLanguageFromExtension(filePath string) string {
134	ext := strings.ToLower(filepath.Ext(filePath))
135	
136	languageMap := map[string]string{
137		".js":         "javascript",
138		".jsx":        "javascript",
139		".ts":         "typescript",
140		".tsx":        "typescript",
141		".py":         "python",
142		".go":         "go",
143		".java":       "java",
144		".c":          "c",
145		".cpp":        "cpp",
146		".cc":         "cpp",
147		".cxx":        "cpp",
148		".cs":         "csharp",
149		".php":        "php",
150		".rb":         "ruby",
151		".rs":         "rust",
152		".swift":      "swift",
153		".kt":         "kotlin",
154		".scala":      "scala",
155		".html":       "html",
156		".htm":        "html",
157		".css":        "css",
158		".scss":       "scss",
159		".sass":       "scss",
160		".less":       "less",
161		".json":       "json",
162		".xml":        "xml",
163		".yaml":       "yaml",
164		".yml":        "yaml",
165		".toml":       "toml",
166		".md":         "markdown",
167		".sql":        "sql",
168		".sh":         "shell",
169		".bash":       "shell",
170		".zsh":        "shell",
171		".fish":       "fish",
172		".ps1":        "powershell",
173		".dockerfile": "dockerfile",
174		".mk":         "makefile",
175		".makefile":   "makefile",
176	}
177	
178	if language, ok := languageMap[ext]; ok {
179		return language
180	}
181	
182	return "text"
183}