package lsp import ( "path/filepath" "ollie/util" ) // Adapter describes an LSP server for a specific language. type Adapter struct { Name string Extensions []string RootMarkers []string Cmd []string InitOptions map[string]any } // WorkspaceRoot finds the project root for a file using this adapter's markers. func (a *Adapter) WorkspaceRoot(file, fallback string) string { return FindRoot(file, a.RootMarkers, fallback) } // Adapters is the registry of known LSP adapters. var Adapters = []*Adapter{ { Name: "gopls", Extensions: []string{".go"}, RootMarkers: []string{"go.work", "go.mod"}, Cmd: []string{"gopls", "serve"}, InitOptions: map[string]any{ "usePlaceholders": true, "staticcheck": true, "gofumpt": true, }, }, { Name: "clangd", Extensions: []string{".c", ".h", ".cc", ".cpp", ".cxx", ".hpp", ".hxx"}, RootMarkers: []string{"compile_commands.json", "CMakeLists.txt", ".clangd", "Makefile"}, Cmd: []string{"clangd", "--background-index"}, InitOptions: nil, }, { Name: "intelephense", Extensions: []string{".php"}, RootMarkers: []string{"composer.json", ".git"}, Cmd: []string{"intelephense", "--stdio"}, InitOptions: nil, }, } // extMap builds on first call. var extMap map[string]*Adapter func buildExtMap() { extMap = make(map[string]*Adapter, 16) for _, a := range Adapters { for _, ext := range a.Extensions { extMap[ext] = a } } } // Detect returns the adapter for a file path based on extension, or nil. func Detect(filepath string) *Adapter { if extMap == nil { buildExtMap() } ext := filepathExt(filepath) return extMap[ext] } func filepathExt(path string) string { return filepath.Ext(path) } // FileURI converts an absolute path to a file:// URI. func FileURI(path string) string { abs, err := filepath.Abs(path) if err != nil { abs = path } return "file://" + abs } // URIToPath converts a file:// URI to a local path. func URIToPath(uri string) string { const prefix = "file://" if len(uri) > len(prefix) && uri[:len(prefix)] == prefix { return uri[len(prefix):] } return uri } // LanguageID returns the LSP language identifier for a file extension. func LanguageID(path string) string { ext := filepath.Ext(path) m := map[string]string{ ".go": "go", ".c": "c", ".h": "c", ".cc": "cpp", ".cpp": "cpp", ".cxx": "cpp", ".hpp": "cpp", ".hxx": "cpp", ".php": "php", ".py": "python", ".js": "javascript", ".ts": "typescript", ".rs": "rust", ".java": "java", ".rb": "ruby", } if id, ok := m[ext]; ok { return id } return "plaintext" } // SockPath returns the path to the bridge Unix socket. func SockPath() string { return filepath.Join(util.RuntimeDir(), "ollie-lsp.sock") } // PidPath returns the path to the bridge PID file. func PidPath() string { return SockPath() + ".pid" }