package diag
import (
"path/filepath"
"strings"
)
// serverSpec is how to start one language server and which files to send it.
// Adding a server is adding a row to servers.
type serverSpec struct {
// Bin is the program looked up on PATH, and the Source the model sees.
Bin string
// Args are the extra arguments; most servers need "--stdio" to speak on
// stdin and stdout rather than opening a socket.
Args []string
// Languages maps extension to the LSP languageId sent in didOpen.
Languages map[string]string
// Markers are the files whose nearest ancestor is the workspace root.
Markers []string
}
// servers is the table of language servers, in the order they are tried.
//
// Every one is a server the language's own toolchain ships or recommends,
// which is what makes it likely to be installed already. Detection is by
// PATH lookup alone — no version checks, no probing — because a server that
// is installed but broken shows up as a server that does not start, and that
// path degrades to no diagnostics anyway.
var servers = []serverSpec{
{
Bin: "gopls",
Languages: map[string]string{".go": "go"},
Markers: []string{"go.work", "go.mod"},
},
{
Bin: "typescript-language-server",
Args: []string{"--stdio"},
Languages: map[string]string{
".ts": "typescript", ".mts": "typescript", ".cts": "typescript",
".tsx": "typescriptreact",
".js": "javascript", ".mjs": "javascript", ".cjs": "javascript",
".jsx": "javascriptreact",
},
Markers: []string{"tsconfig.json", "jsconfig.json", "package.json"},
},
{
Bin: "pyright-langserver",
Args: []string{"--stdio"},
Languages: map[string]string{".py": "python", ".pyi": "python"},
Markers: []string{"pyproject.toml", "pyrightconfig.json", "setup.py", "setup.cfg", "requirements.txt"},
},
{
Bin: "rust-analyzer",
Languages: map[string]string{".rs": "rust"},
Markers: []string{"Cargo.toml"},
},
}
// serverFor returns the first server that handles path's extension.
func serverFor(path string) (serverSpec, bool) {
ext := strings.ToLower(filepath.Ext(path))
for _, s := range servers {
if _, ok := s.Languages[ext]; ok {
return s, true
}
}
return serverSpec{}, false
}
// languageID is the LSP languageId for path under spec.
func (s serverSpec) languageID(path string) string {
return s.Languages[strings.ToLower(filepath.Ext(path))]
}