lsp_manager/install.go

196 lines
5.8 KiB
Go

package main
import (
"fmt"
"io"
"net/http"
"os"
"os/exec"
"path/filepath"
"strings"
// "github.com/adrg/xdg"
)
// Installer verwaltet die Installation von LSP-Servern
type Installer struct {
installDir string
binDir string
}
// NewInstaller erstellt einen XDG-konformen Installer
func NewInstaller(baseDir string) *Installer {
installDir := filepath.Join(baseDir, "servers")
binDir := filepath.Join(baseDir, "bin")
// Verzeichnisse erstellen
os.MkdirAll(installDir, 0755)
os.MkdirAll(binDir, 0755)
return &Installer{
installDir: installDir,
binDir: binDir,
}
}
// PrintPathInstructions zeigt dem Benutzer, wie der PATH gesetzt wird
func (i *Installer) PrintPathInstructions() {
fmt.Println("\n📁 LSP-Server werden installiert in:")
fmt.Printf(" Binaries: %s\n", i.binDir)
fmt.Printf(" Data: %s\n\n", i.installDir)
// Prüfen ob bereits im PATH
pathEnv := os.Getenv("PATH")
pathList := filepath.SplitList(pathEnv)
inPath := false
for _, p := range pathList {
if p == i.binDir {
inPath = true
break
}
}
if !inPath {
fmt.Println("⚠️ Das Binary-Verzeichnis ist noch nicht im PATH!")
fmt.Println("\nFüge folgende Zeile zu deiner Shell-Konfiguration hinzu:")
fmt.Printf("\n # Für Bash (~/.bashrc):\n")
fmt.Printf(" export PATH=\"%s:$PATH\"\n\n", i.binDir)
fmt.Printf(" # Für Zsh (~/.zshrc):\n")
fmt.Printf(" export PATH=\"%s:$PATH\"\n\n", i.binDir)
fmt.Printf(" # Für Fish (~/.config/fish/config.fish):\n")
fmt.Printf(" set -x PATH %s $PATH\n\n", i.binDir)
} else {
fmt.Println("✅ Binary-Verzeichnis ist bereits im PATH!")
}
}
// Install installiert einen LSP-Server basierend auf dem Typ
func (i *Installer) Install(server LSPServer) error {
switch server.InstallType {
case "go", "golang":
return i.installGo(server)
case "npm":
return i.installNpm(server)
case "github":
return i.installGithub(server)
case "cargo":
return i.installCargo(server)
default:
return fmt.Errorf("unsupported install type: %s (für %s)", server.InstallType, server.Name)
}
}
// installGo installiert einen LSP-Server via go install
func (i *Installer) installGo(server LSPServer) error {
// Mason Source format: "golang.org/x/tools/gopls" oder ähnlich
source := server.Source
if !strings.Contains(source, "@") {
source = source + "@" + server.Version
}
cmd := exec.Command("go", "install", source)
cmd.Env = append(os.Environ(),
"GOBIN="+i.binDir,
)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("go install failed: %v\nOutput: %s", err, output)
}
return nil
}
// installNpm installiert einen LSP-Server via npm
func (i *Installer) installNpm(server LSPServer) error {
serverDir := filepath.Join(i.installDir, server.Name)
os.MkdirAll(serverDir, 0755)
// Mason Source format: "package-name" (einfacher npm Package-Name)
cmd := exec.Command("npm", "install", "-g", "--prefix", serverDir, server.Source)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("npm install failed: %v\nOutput: %s", err, output)
}
// Symlink zum bin-Verzeichnis erstellen
// npm installiert in serverDir/lib/node_modules/.bin/
binPath := filepath.Join(serverDir, "lib", "node_modules", ".bin", server.Executable)
linkPath := filepath.Join(i.binDir, server.Executable)
os.Remove(linkPath) // Alten Link entfernen
return os.Symlink(binPath, linkPath)
}
// installGithub lädt ein Binary von GitHub Releases herunter
func (i *Installer) installGithub(server LSPServer) error {
// Mason Source format: "owner/repo"
// Wir müssen die Download-URL konstruieren (vereinfacht)
downloadURL := fmt.Sprintf("https://github.com/%s/releases/latest/download/%s",
server.Source, server.Executable)
resp, err := http.Get(downloadURL)
if err != nil {
return fmt.Errorf("download failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("download failed with status: %d", resp.StatusCode)
}
targetPath := filepath.Join(i.binDir, server.Executable)
file, err := os.Create(targetPath)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(file, resp.Body)
if err != nil {
return err
}
// Executable-Rechte setzen
return os.Chmod(targetPath, 0755)
}
// installCargo installiert einen LSP-Server via cargo
func (i *Installer) installCargo(server LSPServer) error {
serverDir := filepath.Join(i.installDir, server.Name)
// Mason Source format: "crate-name"
cmd := exec.Command("cargo", "install",
server.Source,
"--root", serverDir)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("cargo install failed: %v\nOutput: %s", err, output)
}
// Symlink erstellen
binPath := filepath.Join(serverDir, "bin", server.Executable)
linkPath := filepath.Join(i.binDir, server.Executable)
os.Remove(linkPath)
return os.Symlink(binPath, linkPath)
}
// IsInstalled prüft, ob ein Server installiert ist
func (i *Installer) IsInstalled(server LSPServer) bool {
binPath := filepath.Join(i.binDir, server.Executable)
_, err := os.Stat(binPath)
return err == nil
}
// Uninstall entfernt einen installierten LSP-Server
func (i *Installer) Uninstall(server LSPServer) error {
binPath := filepath.Join(i.binDir, server.Executable)
serverDir := filepath.Join(i.installDir, server.Name)
os.Remove(binPath)
os.RemoveAll(serverDir)
return nil
}