1use serde::Serialize;
2use std::path::Path;
3
4#[derive(Debug, Clone, Serialize)]
5pub struct LspRecipe {
6 pub language: &'static str,
7 pub extensions: &'static [&'static str],
8 pub server_name: &'static str,
9 pub install_command: &'static str,
10 pub binary_name: &'static str,
11 pub args: &'static [&'static str],
12 pub package_manager: &'static str,
13}
14
15pub const LSP_RECIPES: &[LspRecipe] = &[
16 LspRecipe {
17 language: "python",
18 extensions: &["py"],
19 server_name: "pyright",
20 install_command: "npm install -g pyright",
21 binary_name: "pyright-langserver",
22 args: &["--stdio"],
23 package_manager: "npm",
24 },
25 LspRecipe {
26 language: "typescript",
27 extensions: &["ts", "tsx", "js", "jsx", "mjs", "cjs"],
28 server_name: "typescript-language-server",
29 install_command: "npm install -g typescript-language-server typescript",
30 binary_name: "typescript-language-server",
31 args: &["--stdio"],
32 package_manager: "npm",
33 },
34 LspRecipe {
35 language: "rust",
36 extensions: &["rs"],
37 server_name: "rust-analyzer",
38 install_command: "rustup component add rust-analyzer",
39 binary_name: "rust-analyzer",
40 args: &[],
41 package_manager: "rustup",
42 },
43 LspRecipe {
44 language: "go",
45 extensions: &["go"],
46 server_name: "gopls",
47 install_command: "go install golang.org/x/tools/gopls@latest",
48 binary_name: "gopls",
49 args: &["serve"],
50 package_manager: "go",
51 },
52 LspRecipe {
53 language: "java",
54 extensions: &["java"],
55 server_name: "jdtls",
56 install_command: "brew install jdtls",
57 binary_name: "jdtls",
58 args: &[],
59 package_manager: "brew",
60 },
61 LspRecipe {
62 language: "kotlin",
63 extensions: &["kt", "kts"],
64 server_name: "kotlin-language-server",
65 install_command: "brew install kotlin-language-server",
66 binary_name: "kotlin-language-server",
67 args: &[],
68 package_manager: "brew",
69 },
70 LspRecipe {
71 language: "c_cpp",
72 extensions: &["c", "h", "cpp", "cc", "cxx", "hpp"],
73 server_name: "clangd",
74 install_command: "brew install llvm",
75 binary_name: "clangd",
76 args: &["--background-index"],
77 package_manager: "brew",
78 },
79 LspRecipe {
80 language: "ruby",
81 extensions: &["rb"],
82 server_name: "solargraph",
83 install_command: "gem install solargraph",
84 binary_name: "solargraph",
85 args: &["stdio"],
86 package_manager: "gem",
87 },
88 LspRecipe {
89 language: "php",
90 extensions: &["php"],
91 server_name: "intelephense",
92 install_command: "npm install -g intelephense",
93 binary_name: "intelephense",
94 args: &["--stdio"],
95 package_manager: "npm",
96 },
97 LspRecipe {
98 language: "scala",
99 extensions: &["scala", "sc"],
100 server_name: "metals",
101 install_command: "cs install metals",
102 binary_name: "metals",
103 args: &[],
104 package_manager: "coursier",
105 },
106 LspRecipe {
107 language: "swift",
108 extensions: &["swift"],
109 server_name: "sourcekit-lsp",
110 install_command: "xcode-select --install",
111 binary_name: "sourcekit-lsp",
112 args: &[],
113 package_manager: "xcode",
114 },
115 LspRecipe {
116 language: "csharp",
117 extensions: &["cs"],
118 server_name: "omnisharp",
119 install_command: "dotnet tool install -g csharp-ls",
120 binary_name: "csharp-ls",
121 args: &[],
122 package_manager: "dotnet",
123 },
124 LspRecipe {
125 language: "dart",
126 extensions: &["dart"],
127 server_name: "dart-language-server",
128 install_command: "dart pub global activate dart_language_server",
129 binary_name: "dart",
130 args: &["language-server", "--protocol=lsp"],
131 package_manager: "dart",
132 },
133 LspRecipe {
135 language: "lua",
136 extensions: &["lua"],
137 server_name: "lua-language-server",
138 install_command: "brew install lua-language-server",
139 binary_name: "lua-language-server",
140 args: &[],
141 package_manager: "brew",
142 },
143 LspRecipe {
144 language: "zig",
145 extensions: &["zig"],
146 server_name: "zls",
147 install_command: "brew install zls",
148 binary_name: "zls",
149 args: &[],
150 package_manager: "brew",
151 },
152 LspRecipe {
153 language: "elixir",
154 extensions: &["ex", "exs"],
155 server_name: "next-ls",
156 install_command: "mix escript.install hex next_ls",
157 binary_name: "nextls",
158 args: &["--stdio"],
159 package_manager: "mix",
160 },
161 LspRecipe {
162 language: "haskell",
163 extensions: &["hs"],
164 server_name: "haskell-language-server",
165 install_command: "ghcup install hls",
166 binary_name: "haskell-language-server-wrapper",
167 args: &["--lsp"],
168 package_manager: "ghcup",
169 },
170 LspRecipe {
171 language: "ocaml",
172 extensions: &["ml", "mli"],
173 server_name: "ocamllsp",
174 install_command: "opam install ocaml-lsp-server",
175 binary_name: "ocamllsp",
176 args: &[],
177 package_manager: "opam",
178 },
179 LspRecipe {
180 language: "erlang",
181 extensions: &["erl", "hrl"],
182 server_name: "erlang_ls",
183 install_command: "brew install erlang_ls",
184 binary_name: "erlang_ls",
185 args: &[],
186 package_manager: "brew",
187 },
188 LspRecipe {
189 language: "r",
190 extensions: &["r", "R"],
191 server_name: "languageserver",
192 install_command: "R -e 'install.packages(\"languageserver\")'",
193 binary_name: "R",
194 args: &["--slave", "-e", "languageserver::run()"],
195 package_manager: "R",
196 },
197 LspRecipe {
198 language: "shellscript",
199 extensions: &["sh", "bash"],
200 server_name: "bash-language-server",
201 install_command: "npm install -g bash-language-server",
202 binary_name: "bash-language-server",
203 args: &["start"],
204 package_manager: "npm",
205 },
206 LspRecipe {
207 language: "julia",
208 extensions: &["jl"],
209 server_name: "julia-lsp",
210 install_command: "julia -e 'using Pkg; Pkg.add(\"LanguageServer\")'",
211 binary_name: "julia",
212 args: &["--project=@.", "-e", "using LanguageServer; runserver()"],
213 package_manager: "julia",
214 },
215 ];
217
218pub fn check_lsp_status() -> Vec<LspStatus> {
220 LSP_RECIPES
221 .iter()
222 .map(|recipe| {
223 let installed = std::process::Command::new("which")
224 .arg(recipe.binary_name)
225 .output()
226 .map(|o| o.status.success())
227 .unwrap_or(false);
228 LspStatus {
229 language: recipe.language,
230 server_name: recipe.server_name,
231 installed,
232 install_command: recipe.install_command,
233 }
234 })
235 .collect()
236}
237
238#[derive(Debug, Clone, Serialize)]
239pub struct LspStatus {
240 pub language: &'static str,
241 pub server_name: &'static str,
242 pub installed: bool,
243 pub install_command: &'static str,
244}
245
246pub fn get_lsp_recipe(extension: &str) -> Option<&'static LspRecipe> {
248 let ext = extension.to_ascii_lowercase();
249 LSP_RECIPES
250 .iter()
251 .find(|r| r.extensions.contains(&ext.as_str()))
252}
253
254pub fn default_lsp_command_for_extension(extension: &str) -> Option<&'static str> {
255 get_lsp_recipe(extension).map(|recipe| recipe.binary_name)
256}
257
258pub fn default_lsp_command_for_path(file_path: &str) -> Option<&'static str> {
259 Path::new(file_path)
260 .extension()
261 .and_then(|ext| ext.to_str())
262 .and_then(default_lsp_command_for_extension)
263}
264
265pub fn default_lsp_args_for_command(command: &str) -> Option<&'static [&'static str]> {
266 LSP_RECIPES
267 .iter()
268 .find(|recipe| recipe.binary_name == command)
269 .map(|recipe| recipe.args)
270}