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