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