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