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