1use anyhow::Context;
2use gpui::AppContext;
3pub use language::*;
4use node_runtime::NodeRuntime;
5use rust_embed::RustEmbed;
6use settings::Settings;
7use std::{str, sync::Arc};
8use util::asset_str;
9
10use crate::{elixir::elixir_task_context, rust::RustContextProvider};
11
12use self::{deno::DenoSettings, elixir::ElixirSettings};
13
14mod astro;
15mod c;
16mod clojure;
17mod csharp;
18mod css;
19mod dart;
20mod deno;
21mod dockerfile;
22mod elixir;
23mod elm;
24mod erlang;
25mod gleam;
26mod go;
27mod haskell;
28mod html;
29mod json;
30mod lua;
31mod nu;
32mod ocaml;
33mod php;
34mod prisma;
35mod purescript;
36mod python;
37mod ruby;
38mod rust;
39mod svelte;
40mod tailwind;
41mod terraform;
42mod toml;
43mod typescript;
44mod vue;
45mod yaml;
46mod zig;
47
48// 1. Add tree-sitter-{language} parser to zed crate
49// 2. Create a language directory in zed/crates/zed/src/languages and add the language to init function below
50// 3. Add config.toml to the newly created language directory using existing languages as a template
51// 4. Copy highlights from tree sitter repo for the language into a highlights.scm file.
52// Note: github highlights take the last match while zed takes the first
53// 5. Add indents.scm, outline.scm, and brackets.scm to implement indent on newline, outline/breadcrumbs,
54// and autoclosing brackets respectively
55// 6. If the language has injections add an injections.scm query file
56
57#[derive(RustEmbed)]
58#[folder = "src/"]
59#[exclude = "*.rs"]
60struct LanguageDir;
61
62pub fn init(
63 languages: Arc<LanguageRegistry>,
64 node_runtime: Arc<dyn NodeRuntime>,
65 cx: &mut AppContext,
66) {
67 ElixirSettings::register(cx);
68 DenoSettings::register(cx);
69
70 languages.register_native_grammars([
71 ("astro", tree_sitter_astro::language()),
72 ("bash", tree_sitter_bash::language()),
73 ("c", tree_sitter_c::language()),
74 ("c_sharp", tree_sitter_c_sharp::language()),
75 ("clojure", tree_sitter_clojure::language()),
76 ("cpp", tree_sitter_cpp::language()),
77 ("css", tree_sitter_css::language()),
78 ("dockerfile", tree_sitter_dockerfile::language()),
79 ("elixir", tree_sitter_elixir::language()),
80 ("elm", tree_sitter_elm::language()),
81 (
82 "embedded_template",
83 tree_sitter_embedded_template::language(),
84 ),
85 ("erlang", tree_sitter_erlang::language()),
86 ("gleam", tree_sitter_gleam::language()),
87 ("glsl", tree_sitter_glsl::language()),
88 ("go", tree_sitter_go::language()),
89 ("gomod", tree_sitter_gomod::language()),
90 ("gowork", tree_sitter_gowork::language()),
91 ("haskell", tree_sitter_haskell::language()),
92 ("hcl", tree_sitter_hcl::language()),
93 ("heex", tree_sitter_heex::language()),
94 ("html", tree_sitter_html::language()),
95 ("jsdoc", tree_sitter_jsdoc::language()),
96 ("json", tree_sitter_json::language()),
97 ("lua", tree_sitter_lua::language()),
98 ("markdown", tree_sitter_markdown::language()),
99 ("nix", tree_sitter_nix::language()),
100 ("nu", tree_sitter_nu::language()),
101 ("ocaml", tree_sitter_ocaml::language_ocaml()),
102 (
103 "ocaml_interface",
104 tree_sitter_ocaml::language_ocaml_interface(),
105 ),
106 ("php", tree_sitter_php::language_php()),
107 ("prisma", tree_sitter_prisma_io::language()),
108 ("proto", tree_sitter_proto::language()),
109 ("purescript", tree_sitter_purescript::language()),
110 ("python", tree_sitter_python::language()),
111 ("racket", tree_sitter_racket::language()),
112 ("regex", tree_sitter_regex::language()),
113 ("ruby", tree_sitter_ruby::language()),
114 ("rust", tree_sitter_rust::language()),
115 ("scheme", tree_sitter_scheme::language()),
116 ("svelte", tree_sitter_svelte::language()),
117 ("toml", tree_sitter_toml::language()),
118 ("tsx", tree_sitter_typescript::language_tsx()),
119 ("typescript", tree_sitter_typescript::language_typescript()),
120 ("vue", tree_sitter_vue::language()),
121 ("yaml", tree_sitter_yaml::language()),
122 ("zig", tree_sitter_zig::language()),
123 ("dart", tree_sitter_dart::language()),
124 ]);
125
126 macro_rules! language {
127 ($name:literal) => {
128 let config = load_config($name);
129 languages.register_language(
130 config.name.clone(),
131 config.grammar.clone(),
132 config.matcher.clone(),
133 move || {
134 Ok((
135 config.clone(),
136 load_queries($name),
137 Some(Arc::new(language::SymbolContextProvider)),
138 ))
139 },
140 );
141 };
142 ($name:literal, $adapters:expr) => {
143 let config = load_config($name);
144 // typeck helper
145 let adapters: Vec<Arc<dyn LspAdapter>> = $adapters;
146 for adapter in adapters {
147 languages.register_lsp_adapter(config.name.clone(), adapter);
148 }
149 languages.register_language(
150 config.name.clone(),
151 config.grammar.clone(),
152 config.matcher.clone(),
153 move || {
154 Ok((
155 config.clone(),
156 load_queries($name),
157 Some(Arc::new(language::SymbolContextProvider)),
158 ))
159 },
160 );
161 };
162 ($name:literal, $adapters:expr, $context_provider:expr) => {
163 let config = load_config($name);
164 // typeck helper
165 let adapters: Vec<Arc<dyn LspAdapter>> = $adapters;
166 for adapter in adapters {
167 languages.register_lsp_adapter(config.name.clone(), adapter);
168 }
169 languages.register_language(
170 config.name.clone(),
171 config.grammar.clone(),
172 config.matcher.clone(),
173 move || {
174 Ok((
175 config.clone(),
176 load_queries($name),
177 Some(Arc::new($context_provider)),
178 ))
179 },
180 );
181 };
182 }
183 language!(
184 "astro",
185 vec![
186 Arc::new(astro::AstroLspAdapter::new(node_runtime.clone())),
187 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
188 ]
189 );
190 language!("bash");
191 language!("c", vec![Arc::new(c::CLspAdapter) as Arc<dyn LspAdapter>]);
192 language!("clojure", vec![Arc::new(clojure::ClojureLspAdapter)]);
193 language!("cpp", vec![Arc::new(c::CLspAdapter)]);
194 language!("csharp", vec![Arc::new(csharp::OmniSharpAdapter {})]);
195 language!(
196 "css",
197 vec![
198 Arc::new(css::CssLspAdapter::new(node_runtime.clone())),
199 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
200 ]
201 );
202
203 language!(
204 "dockerfile",
205 vec![Arc::new(dockerfile::DockerfileLspAdapter::new(
206 node_runtime.clone(),
207 ))]
208 );
209
210 match &ElixirSettings::get(None, cx).lsp {
211 elixir::ElixirLspSetting::ElixirLs => {
212 language!(
213 "elixir",
214 vec![
215 Arc::new(elixir::ElixirLspAdapter),
216 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
217 ],
218 elixir_task_context()
219 );
220 }
221 elixir::ElixirLspSetting::NextLs => {
222 language!(
223 "elixir",
224 vec![Arc::new(elixir::NextLspAdapter)],
225 elixir_task_context()
226 );
227 }
228 elixir::ElixirLspSetting::Local { path, arguments } => {
229 language!(
230 "elixir",
231 vec![Arc::new(elixir::LocalLspAdapter {
232 path: path.clone(),
233 arguments: arguments.clone(),
234 })],
235 elixir_task_context()
236 );
237 }
238 }
239 language!("erlang", vec![Arc::new(erlang::ErlangLspAdapter)]);
240
241 language!("gleam", vec![Arc::new(gleam::GleamLspAdapter)]);
242 language!("go", vec![Arc::new(go::GoLspAdapter)]);
243 language!("gomod");
244 language!("gowork");
245 language!("zig", vec![Arc::new(zig::ZlsAdapter)]);
246 language!(
247 "heex",
248 vec![
249 Arc::new(elixir::ElixirLspAdapter),
250 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
251 ]
252 );
253 language!(
254 "json",
255 vec![Arc::new(json::JsonLspAdapter::new(
256 node_runtime.clone(),
257 languages.clone(),
258 ))]
259 );
260 language!("markdown");
261 language!(
262 "python",
263 vec![Arc::new(python::PythonLspAdapter::new(
264 node_runtime.clone(),
265 ))]
266 );
267 language!(
268 "rust",
269 vec![Arc::new(rust::RustLspAdapter)],
270 RustContextProvider
271 );
272 language!("toml", vec![Arc::new(toml::TaploLspAdapter)]);
273 match &DenoSettings::get(None, cx).enable {
274 true => {
275 language!(
276 "tsx",
277 vec![
278 Arc::new(deno::DenoLspAdapter::new()),
279 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
280 ]
281 );
282 language!("typescript", vec![Arc::new(deno::DenoLspAdapter::new())]);
283 language!(
284 "javascript",
285 vec![
286 Arc::new(deno::DenoLspAdapter::new()),
287 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
288 ]
289 );
290 language!("jsdoc", vec![Arc::new(deno::DenoLspAdapter::new())]);
291 }
292 false => {
293 language!(
294 "tsx",
295 vec![
296 Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
297 Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
298 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
299 ]
300 );
301 language!(
302 "typescript",
303 vec![
304 Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
305 Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
306 ]
307 );
308 language!(
309 "javascript",
310 vec![
311 Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
312 Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
313 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
314 ]
315 );
316 language!(
317 "jsdoc",
318 vec![Arc::new(typescript::TypeScriptLspAdapter::new(
319 node_runtime.clone(),
320 ))]
321 );
322 }
323 }
324
325 language!("haskell", vec![Arc::new(haskell::HaskellLanguageServer {})]);
326 language!(
327 "html",
328 vec![
329 Arc::new(html::HtmlLspAdapter::new(node_runtime.clone())),
330 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
331 ]
332 );
333 language!("ruby", vec![Arc::new(ruby::RubyLanguageServer)]);
334 language!(
335 "erb",
336 vec![
337 Arc::new(ruby::RubyLanguageServer),
338 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
339 ]
340 );
341 language!("scheme");
342 language!("racket");
343 language!("regex");
344 language!("lua", vec![Arc::new(lua::LuaLspAdapter)]);
345 language!(
346 "yaml",
347 vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))]
348 );
349 language!(
350 "svelte",
351 vec![
352 Arc::new(svelte::SvelteLspAdapter::new(node_runtime.clone())),
353 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
354 ]
355 );
356 language!(
357 "php",
358 vec![
359 Arc::new(php::IntelephenseLspAdapter::new(node_runtime.clone())),
360 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
361 ]
362 );
363 language!(
364 "purescript",
365 vec![Arc::new(purescript::PurescriptLspAdapter::new(
366 node_runtime.clone(),
367 ))]
368 );
369 language!(
370 "elm",
371 vec![Arc::new(elm::ElmLspAdapter::new(node_runtime.clone()))]
372 );
373 language!("glsl");
374 language!("nix");
375 language!("nu", vec![Arc::new(nu::NuLanguageServer {})]);
376 language!("ocaml", vec![Arc::new(ocaml::OCamlLspAdapter)]);
377 language!("ocaml-interface", vec![Arc::new(ocaml::OCamlLspAdapter)]);
378 language!(
379 "vue",
380 vec![Arc::new(vue::VueLspAdapter::new(node_runtime.clone()))]
381 );
382 language!("proto");
383 language!("terraform", vec![Arc::new(terraform::TerraformLspAdapter)]);
384 language!(
385 "terraform-vars",
386 vec![Arc::new(terraform::TerraformLspAdapter)]
387 );
388 language!("hcl", vec![]);
389 language!(
390 "prisma",
391 vec![Arc::new(prisma::PrismaLspAdapter::new(
392 node_runtime.clone(),
393 ))]
394 );
395 language!("dart", vec![Arc::new(dart::DartLanguageServer {})]);
396}
397
398#[cfg(any(test, feature = "test-support"))]
399pub fn language(name: &str, grammar: tree_sitter::Language) -> Arc<Language> {
400 Arc::new(
401 Language::new(load_config(name), Some(grammar))
402 .with_queries(load_queries(name))
403 .unwrap(),
404 )
405}
406
407fn load_config(name: &str) -> LanguageConfig {
408 let config_toml = String::from_utf8(
409 LanguageDir::get(&format!("{}/config.toml", name))
410 .unwrap()
411 .data
412 .to_vec(),
413 )
414 .unwrap();
415
416 ::toml::from_str(&config_toml)
417 .with_context(|| format!("failed to load config.toml for language {name:?}"))
418 .unwrap()
419}
420
421fn load_queries(name: &str) -> LanguageQueries {
422 let mut result = LanguageQueries::default();
423 for path in LanguageDir::iter() {
424 if let Some(remainder) = path.strip_prefix(name).and_then(|p| p.strip_prefix('/')) {
425 if !remainder.ends_with(".scm") {
426 continue;
427 }
428 for (name, query) in QUERY_FILENAME_PREFIXES {
429 if remainder.starts_with(name) {
430 let contents = asset_str::<LanguageDir>(path.as_ref());
431 match query(&mut result) {
432 None => *query(&mut result) = Some(contents),
433 Some(r) => r.to_mut().push_str(contents.as_ref()),
434 }
435 }
436 }
437 }
438 }
439 result
440}