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 css;
16mod dart;
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 ("dart", tree_sitter_dart::language()),
96 ]);
97
98 macro_rules! language {
99 ($name:literal) => {
100 let config = load_config($name);
101 languages.register_language(
102 config.name.clone(),
103 config.grammar.clone(),
104 config.matcher.clone(),
105 move || {
106 Ok((
107 config.clone(),
108 load_queries($name),
109 Some(Arc::new(language::SymbolContextProvider)),
110 ))
111 },
112 );
113 };
114 ($name:literal, $adapters:expr) => {
115 let config = load_config($name);
116 // typeck helper
117 let adapters: Vec<Arc<dyn LspAdapter>> = $adapters;
118 for adapter in adapters {
119 languages.register_lsp_adapter(config.name.clone(), adapter);
120 }
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, $context_provider: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($context_provider)),
150 ))
151 },
152 );
153 };
154 }
155 language!("bash");
156 language!("c", vec![Arc::new(c::CLspAdapter) as Arc<dyn LspAdapter>]);
157 language!("cpp", vec![Arc::new(c::CLspAdapter)]);
158 language!(
159 "css",
160 vec![
161 Arc::new(css::CssLspAdapter::new(node_runtime.clone())),
162 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
163 ]
164 );
165
166 match &ElixirSettings::get(None, cx).lsp {
167 elixir::ElixirLspSetting::ElixirLs => {
168 language!(
169 "elixir",
170 vec![
171 Arc::new(elixir::ElixirLspAdapter),
172 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
173 ],
174 elixir_task_context()
175 );
176 }
177 elixir::ElixirLspSetting::NextLs => {
178 language!(
179 "elixir",
180 vec![Arc::new(elixir::NextLspAdapter)],
181 elixir_task_context()
182 );
183 }
184 elixir::ElixirLspSetting::Local { path, arguments } => {
185 language!(
186 "elixir",
187 vec![Arc::new(elixir::LocalLspAdapter {
188 path: path.clone(),
189 arguments: arguments.clone(),
190 })],
191 elixir_task_context()
192 );
193 }
194 }
195 language!("go", vec![Arc::new(go::GoLspAdapter)]);
196 language!("gomod");
197 language!("gowork");
198 language!(
199 "heex",
200 vec![
201 Arc::new(elixir::ElixirLspAdapter),
202 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
203 ]
204 );
205 language!(
206 "json",
207 vec![Arc::new(json::JsonLspAdapter::new(
208 node_runtime.clone(),
209 languages.clone(),
210 ))]
211 );
212 language!("markdown");
213 language!(
214 "python",
215 vec![Arc::new(python::PythonLspAdapter::new(
216 node_runtime.clone(),
217 ))]
218 );
219 language!(
220 "rust",
221 vec![Arc::new(rust::RustLspAdapter)],
222 RustContextProvider
223 );
224 match &DenoSettings::get(None, cx).enable {
225 true => {
226 language!(
227 "tsx",
228 vec![
229 Arc::new(deno::DenoLspAdapter::new()),
230 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
231 ]
232 );
233 language!("typescript", vec![Arc::new(deno::DenoLspAdapter::new())]);
234 language!(
235 "javascript",
236 vec![
237 Arc::new(deno::DenoLspAdapter::new()),
238 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
239 ]
240 );
241 language!("jsdoc", vec![Arc::new(deno::DenoLspAdapter::new())]);
242 }
243 false => {
244 language!(
245 "tsx",
246 vec![
247 Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
248 Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
249 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
250 ]
251 );
252 language!(
253 "typescript",
254 vec![
255 Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
256 Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
257 ]
258 );
259 language!(
260 "javascript",
261 vec![
262 Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
263 Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
264 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
265 ]
266 );
267 language!(
268 "jsdoc",
269 vec![Arc::new(typescript::TypeScriptLspAdapter::new(
270 node_runtime.clone(),
271 ))]
272 );
273 }
274 }
275
276 language!("ruby", vec![Arc::new(ruby::RubyLanguageServer)]);
277 language!(
278 "erb",
279 vec![
280 Arc::new(ruby::RubyLanguageServer),
281 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
282 ]
283 );
284 language!("scheme");
285 language!("racket");
286 language!("regex");
287 language!("lua", vec![Arc::new(lua::LuaLspAdapter)]);
288 language!(
289 "yaml",
290 vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))]
291 );
292 language!(
293 "elm",
294 vec![Arc::new(elm::ElmLspAdapter::new(node_runtime.clone()))]
295 );
296 language!("glsl");
297 language!("nix");
298 language!("nu", vec![Arc::new(nu::NuLanguageServer {})]);
299 language!("ocaml", vec![Arc::new(ocaml::OCamlLspAdapter)]);
300 language!("ocaml-interface", vec![Arc::new(ocaml::OCamlLspAdapter)]);
301 language!(
302 "vue",
303 vec![
304 Arc::new(vue::VueLspAdapter::new(node_runtime.clone())),
305 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
306 ]
307 );
308 language!("proto");
309 language!("terraform", vec![Arc::new(terraform::TerraformLspAdapter)]);
310 language!(
311 "terraform-vars",
312 vec![Arc::new(terraform::TerraformLspAdapter)]
313 );
314 language!("hcl", vec![]);
315 language!("dart", vec![Arc::new(dart::DartLanguageServer {})]);
316
317 languages.register_secondary_lsp_adapter(
318 "Astro".into(),
319 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
320 );
321 languages.register_secondary_lsp_adapter(
322 "HTML".into(),
323 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
324 );
325 languages.register_secondary_lsp_adapter(
326 "PHP".into(),
327 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
328 );
329 languages.register_secondary_lsp_adapter(
330 "Svelte".into(),
331 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
332 );
333}
334
335#[cfg(any(test, feature = "test-support"))]
336pub fn language(name: &str, grammar: tree_sitter::Language) -> Arc<Language> {
337 Arc::new(
338 Language::new(load_config(name), Some(grammar))
339 .with_queries(load_queries(name))
340 .unwrap(),
341 )
342}
343
344fn load_config(name: &str) -> LanguageConfig {
345 let config_toml = String::from_utf8(
346 LanguageDir::get(&format!("{}/config.toml", name))
347 .unwrap()
348 .data
349 .to_vec(),
350 )
351 .unwrap();
352
353 ::toml::from_str(&config_toml)
354 .with_context(|| format!("failed to load config.toml for language {name:?}"))
355 .unwrap()
356}
357
358fn load_queries(name: &str) -> LanguageQueries {
359 let mut result = LanguageQueries::default();
360 for path in LanguageDir::iter() {
361 if let Some(remainder) = path.strip_prefix(name).and_then(|p| p.strip_prefix('/')) {
362 if !remainder.ends_with(".scm") {
363 continue;
364 }
365 for (name, query) in QUERY_FILENAME_PREFIXES {
366 if remainder.starts_with(name) {
367 let contents = asset_str::<LanguageDir>(path.as_ref());
368 match query(&mut result) {
369 None => *query(&mut result) = Some(contents),
370 Some(r) => r.to_mut().push_str(contents.as_ref()),
371 }
372 }
373 }
374 }
375 }
376 result
377}