package lsp import ( "os" "path/filepath" "time" "gopkg.in/yaml.v3" ) type RegistryEntry struct { Name string `yaml:"name" json:"name"` Language string `yaml:"language" json:"language"` Description string `yaml:"description" json:"description"` Command string `yaml:"command" json:"command"` InstallCmd string `yaml:"install_cmd" json:"install_cmd"` InstallType string `yaml:"install_type" json:"install_type"` Category string `yaml:"category" json:"category"` FilePatterns []string `yaml:"file_patterns,omitempty" json:"file_patterns,omitempty"` ConfigFiles []string `yaml:"config_files,omitempty" json:"config_files,omitempty"` Tags []string `yaml:"tags,omitempty" json:"tags,omitempty"` HomePage string `yaml:"homepage,omitempty" json:"homepage,omitempty"` NeovimSetup string `yaml:"neovim_setup,omitempty" json:"neovim_setup,omitempty"` HelixLanguage string `yaml:"helix_language,omitempty" json:"helix_language,omitempty"` } type LSPRegistry struct { SchemaVersion string `yaml:"schema_version"` UpdatedAt time.Time `yaml:"updated_at"` Servers []RegistryEntry `yaml:"servers"` } func DefaultLSPRegistry() *LSPRegistry { return &LSPRegistry{ SchemaVersion: "v1", UpdatedAt: time.Now(), Servers: []RegistryEntry{ { Name: "gopls", Language: "go", Description: "Go language server", Command: "gopls", InstallCmd: "go install golang.org/x/tools/gopls@latest", InstallType: "go", Category: "lsp", FilePatterns: []string{"*.go"}, ConfigFiles: []string{"go.mod"}, Tags: []string{"go", "linting", "completion"}, HomePage: "https://github.com/golang/tools", NeovimSetup: `lspconfig.gopls.setup{}`, HelixLanguage: "go", }, { Name: "pyright", Language: "python", Description: "Python type checker and language server", Command: "pyright", InstallCmd: "npm install -g pyright", InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.py", "*.pyi"}, ConfigFiles: []string{"requirements.txt", "pyproject.toml", "setup.py", "Pipfile"}, Tags: []string{"python", "type-checking"}, HomePage: "https://github.com/microsoft/pyright", NeovimSetup: `lspconfig.pyright.setup{}`, HelixLanguage: "python", }, { Name: "typescript-language-server", Language: "typescript", Description: "TypeScript and JavaScript language server", Command: "typescript-language-server", InstallCmd: "npm install -g typescript-language-server typescript", InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.ts", "*.tsx", "*.js", "*.jsx"}, ConfigFiles: []string{"tsconfig.json", "package.json"}, Tags: []string{"typescript", "javascript"}, HomePage: "https://github.com/typescript-language-server/typescript-language-server", NeovimSetup: `lspconfig.tsserver.setup{}`, HelixLanguage: "typescript", }, { Name: "vscode-json-language-server", Language: "json", Description: "JSON language server", Command: "vscode-json-language-server", InstallCmd: "npm install -g vscode-langservers-extracted", InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.json", "*.jsonc"}, Tags: []string{"json"}, NeovimSetup: `lspconfig.jsonls.setup{}`, HelixLanguage: "json", }, { Name: "vscode-html-language-server", Language: "html", Description: "HTML language server", Command: "vscode-html-language-server", InstallCmd: "npm install -g vscode-langservers-extracted", InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.html", "*.htm"}, Tags: []string{"html"}, NeovimSetup: `lspconfig.html.setup{}`, HelixLanguage: "html", }, { Name: "vscode-css-language-server", Language: "css", Description: "CSS/SCSS/LESS language server", Command: "vscode-css-language-server", InstallCmd: "npm install -g vscode-langservers-extracted", InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.css", "*.scss", "*.less"}, Tags: []string{"css"}, NeovimSetup: `lspconfig.cssls.setup{}`, HelixLanguage: "css", }, { Name: "yaml-language-server", Language: "yaml", Description: "YAML language server", Command: "yaml-language-server", InstallCmd: "npm install -g yaml-language-server", InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.yml", "*.yaml"}, Tags: []string{"yaml"}, NeovimSetup: `lspconfig.yamlls.setup{}`, HelixLanguage: "yaml", }, { Name: "bash-language-server", Language: "bash", Description: "Bash language server", Command: "bash-language-server", InstallCmd: "npm install -g bash-language-server", InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.sh", "*.bash"}, Tags: []string{"bash", "shell"}, NeovimSetup: `lspconfig.bashls.setup{}`, HelixLanguage: "bash", }, { Name: "rust-analyzer", Language: "rust", Description: "Rust language server", Command: "rust-analyzer", InstallCmd: "rustup component add rust-analyzer", InstallType: "rustup", Category: "lsp", FilePatterns: []string{"*.rs"}, ConfigFiles: []string{"Cargo.toml"}, Tags: []string{"rust"}, HomePage: "https://github.com/rust-lang/rust-analyzer", NeovimSetup: `lspconfig.rust_analyzer.setup{}`, HelixLanguage: "rust", }, { Name: "clangd", Language: "c/c++", Description: "C/C++ language server", Command: "clangd", InstallCmd: "", InstallType: "system", Category: "lsp", FilePatterns: []string{"*.c", "*.cpp", "*.h", "*.hpp"}, ConfigFiles: []string{"CMakeLists.txt", "Makefile"}, Tags: []string{"c", "cpp"}, NeovimSetup: `lspconfig.clangd.setup{}`, HelixLanguage: "c", }, { Name: "lua-language-server", Language: "lua", Description: "Lua language server", Command: "lua-language-server", InstallCmd: "npm install -g lua-language-server", InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.lua"}, Tags: []string{"lua"}, NeovimSetup: `lspconfig.lua_ls.setup{}`, HelixLanguage: "lua", }, { Name: "dockerfile-language-server", Language: "dockerfile", Description: "Dockerfile language server", Command: "docker-langserver", InstallCmd: "npm install -g dockerfile-language-server-nodejs", InstallType: "npm", Category: "lsp", FilePatterns: []string{"Dockerfile", "Dockerfile.*"}, Tags: []string{"docker"}, NeovimSetup: `lspconfig.dockerls.setup{}`, HelixLanguage: "dockerfile", }, { Name: "tailwindcss-language-server", Language: "tailwind", Description: "Tailwind CSS language server", Command: "tailwindcss-language-server", InstallCmd: "npm install -g @tailwindcss/language-server", InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.html", "*.tsx", "*.jsx"}, ConfigFiles: []string{"tailwind.config.js", "tailwind.config.ts"}, Tags: []string{"tailwind", "css"}, NeovimSetup: `lspconfig.tailwindcss.setup{}`, }, { Name: "svelte-language-server", Language: "svelte", Description: "Svelte language server", Command: "svelteserver", InstallCmd: "npm install -g svelte-language-server", InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.svelte"}, Tags: []string{"svelte"}, NeovimSetup: `lspconfig.svelte.setup{}`, HelixLanguage: "svelte", }, { Name: "vue-language-server", Language: "vue", Description: "Vue language server", Command: "vue-language-server", InstallCmd: "npm install -g @vue/language-server", InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.vue"}, Tags: []string{"vue"}, NeovimSetup: `lspconfig.vuels.setup{}`, }, { Name: "golangci-lint-langserver", Language: "go-lint", Description: "Go linter language server", Command: "golangci-lint-langserver", InstallCmd: "go install github.com/nametake/golangci-lint-langserver@latest", InstallType: "go", Category: "lsp", FilePatterns: []string{"*.go"}, Tags: []string{"go", "linting"}, }, }, } } var lspRegistryPath string func init() { home, _ := os.UserHomeDir() if home != "" { lspRegistryPath = filepath.Join(home, ".muyue", "lsp-registry.yaml") } } func SetLSPRegistryPath(p string) { lspRegistryPath = p } func LoadLSPRegistry() (*LSPRegistry, error) { if lspRegistryPath == "" { return DefaultLSPRegistry(), nil } data, err := os.ReadFile(lspRegistryPath) if err != nil { return DefaultLSPRegistry(), nil } var reg LSPRegistry if err := yaml.Unmarshal(data, ®); err != nil { return nil, err } return ®, nil } func SaveLSPRegistry(reg *LSPRegistry) error { if lspRegistryPath == "" { return nil } reg.UpdatedAt = time.Now() data, err := yaml.Marshal(reg) if err != nil { return err } os.MkdirAll(filepath.Dir(lspRegistryPath), 0755) return os.WriteFile(lspRegistryPath, data, 0644) } func InitLSPRegistry() error { if lspRegistryPath == "" { return nil } if _, err := os.Stat(lspRegistryPath); err == nil { return nil } return SaveLSPRegistry(DefaultLSPRegistry()) } func DetectProjectLanguages(projectDir string) []string { if projectDir == "" { return nil } langDetectors := map[string][]string{ "go": {"go.mod", "go.sum"}, "python": {"requirements.txt", "pyproject.toml", "setup.py", "Pipfile", "uv.lock"}, "typescript": {"tsconfig.json", "package.json"}, "javascript": {"package.json"}, "rust": {"Cargo.toml"}, "ruby": {"Gemfile"}, "java": {"pom.xml", "build.gradle"}, "c": {"CMakeLists.txt", "Makefile"}, "cpp": {"CMakeLists.txt"}, "php": {"composer.json"}, "lua": {".luarc.json"}, "docker": {"Dockerfile"}, } extDetectors := map[string]string{ ".go": "go", ".py": "python", ".rs": "rust", ".ts": "typescript", ".tsx": "typescript", ".js": "javascript", ".jsx": "javascript", ".rb": "ruby", ".java": "java", ".c": "c", ".cpp": "cpp", ".h": "c", ".lua": "lua", ".vue": "vue", ".svelte": "svelte", } detected := map[string]bool{} for lang, files := range langDetectors { for _, f := range files { if _, err := os.Stat(filepath.Join(projectDir, f)); err == nil { detected[lang] = true break } } } entries, err := os.ReadDir(projectDir) if err == nil { for _, e := range entries { if e.IsDir() { continue } ext := filepath.Ext(e.Name()) if lang, ok := extDetectors[ext]; ok { detected[lang] = true } } } var languages []string for lang := range detected { languages = append(languages, lang) } return languages } func GenerateNeovimConfig(servers []RegistryEntry) string { config := `-- Generated by Muyue LSP Manager -- Add to your init.lua or require from lspconfig setup local lspconfig = require('lspconfig') ` for _, s := range servers { if s.NeovimSetup != "" { config += s.NeovimSetup + "\n" } } return config } func GenerateHelixConfig(servers []RegistryEntry) string { config := `# Generated by Muyue LSP Manager # Add to ~/.config/helix/languages.toml ` for _, s := range servers { if s.HelixLanguage != "" { config += "[[language]]\n" config += "name = \"" + s.HelixLanguage + "\"\n" config += "language-servers = [\"" + s.Name + "\"]\n\n" } } return config } func GenerateVSCodeRecommendations(servers []RegistryEntry) []string { extensionMap := map[string][]string{ "gopls": {"golang.go"}, "pyright": {"ms-python.python", "ms-python.vscode-pylance"}, "typescript-language-server": {"ms-vscode.vscode-typescript-next"}, "rust-analyzer": {"rust-lang.rust-analyzer"}, "lua-language-server": {"sumneko.lua"}, "tailwindcss-language-server": {"bradlc.vscode-tailwindcss"}, "svelte-language-server": {"svelte.svelte-vscode"}, "vue-language-server": {"vue.volar"}, "yaml-language-server": {"redhat.vscode-yaml"}, "bash-language-server": {"mads-hartmann.bash-ide-vscode"}, } var extensions []string for _, s := range servers { if exts, ok := extensionMap[s.Name]; ok { extensions = append(extensions, exts...) } } return extensions }