Vectrum
Sign In
73 lines · 2.3 KB · Go
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))]
}