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