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::{
12 bash::bash_task_context, elixir::elixir_task_context, python::python_task_context,
13 rust::RustContextProvider,
14};
15
16use self::{deno::DenoSettings, elixir::ElixirSettings};
17
18mod bash;
19mod c;
20mod css;
21mod deno;
22mod elixir;
23mod go;
24mod json;
25mod python;
26mod ruby;
27mod rust;
28mod tailwind;
29mod typescript;
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 ("heex", tree_sitter_heex::language()),
68 ("jsdoc", tree_sitter_jsdoc::language()),
69 ("json", tree_sitter_json::language()),
70 ("markdown", tree_sitter_markdown::language()),
71 ("proto", tree_sitter_proto::language()),
72 ("python", tree_sitter_python::language()),
73 ("regex", tree_sitter_regex::language()),
74 ("ruby", tree_sitter_ruby::language()),
75 ("rust", tree_sitter_rust::language()),
76 ("tsx", tree_sitter_typescript::language_tsx()),
77 ("typescript", tree_sitter_typescript::language_typescript()),
78 ("yaml", tree_sitter_yaml::language()),
79 ]);
80
81 macro_rules! language {
82 ($name:literal) => {
83 let config = load_config($name);
84 languages.register_language(
85 config.name.clone(),
86 config.grammar.clone(),
87 config.matcher.clone(),
88 move || Ok((config.clone(), load_queries($name), None)),
89 );
90 };
91 ($name:literal, $adapters:expr) => {
92 let config = load_config($name);
93 // typeck helper
94 let adapters: Vec<Arc<dyn LspAdapter>> = $adapters;
95 for adapter in adapters {
96 languages.register_lsp_adapter(config.name.clone(), adapter);
97 }
98 languages.register_language(
99 config.name.clone(),
100 config.grammar.clone(),
101 config.matcher.clone(),
102 move || Ok((config.clone(), load_queries($name), None)),
103 );
104 };
105 ($name:literal, $adapters:expr, $context_provider:expr) => {
106 let config = load_config($name);
107 // typeck helper
108 let adapters: Vec<Arc<dyn LspAdapter>> = $adapters;
109 for adapter in adapters {
110 languages.register_lsp_adapter(config.name.clone(), adapter);
111 }
112 languages.register_language(
113 config.name.clone(),
114 config.grammar.clone(),
115 config.matcher.clone(),
116 move || {
117 Ok((
118 config.clone(),
119 load_queries($name),
120 Some(Arc::new($context_provider)),
121 ))
122 },
123 );
124 };
125 }
126 language!("bash", Vec::new(), bash_task_context());
127 language!("c", vec![Arc::new(c::CLspAdapter) as Arc<dyn LspAdapter>]);
128 language!("cpp", vec![Arc::new(c::CLspAdapter)]);
129 language!(
130 "css",
131 vec![
132 Arc::new(css::CssLspAdapter::new(node_runtime.clone())),
133 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
134 ]
135 );
136
137 match &ElixirSettings::get(None, cx).lsp {
138 elixir::ElixirLspSetting::ElixirLs => {
139 language!(
140 "elixir",
141 vec![
142 Arc::new(elixir::ElixirLspAdapter),
143 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
144 ],
145 elixir_task_context()
146 );
147 }
148 elixir::ElixirLspSetting::NextLs => {
149 language!(
150 "elixir",
151 vec![Arc::new(elixir::NextLspAdapter)],
152 elixir_task_context()
153 );
154 }
155 elixir::ElixirLspSetting::Local { path, arguments } => {
156 language!(
157 "elixir",
158 vec![Arc::new(elixir::LocalLspAdapter {
159 path: path.clone(),
160 arguments: arguments.clone(),
161 })],
162 elixir_task_context()
163 );
164 }
165 }
166 language!("go", vec![Arc::new(go::GoLspAdapter)]);
167 language!("gomod");
168 language!("gowork");
169 language!(
170 "heex",
171 vec![
172 Arc::new(elixir::ElixirLspAdapter),
173 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
174 ]
175 );
176 language!(
177 "json",
178 vec![Arc::new(json::JsonLspAdapter::new(
179 node_runtime.clone(),
180 languages.clone(),
181 ))]
182 );
183 language!("markdown");
184 language!(
185 "python",
186 vec![Arc::new(python::PythonLspAdapter::new(
187 node_runtime.clone(),
188 ))],
189 python_task_context()
190 );
191 language!(
192 "rust",
193 vec![Arc::new(rust::RustLspAdapter)],
194 RustContextProvider
195 );
196 match &DenoSettings::get(None, cx).enable {
197 true => {
198 language!(
199 "tsx",
200 vec![
201 Arc::new(deno::DenoLspAdapter::new()),
202 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
203 ]
204 );
205 language!("typescript", vec![Arc::new(deno::DenoLspAdapter::new())]);
206 language!(
207 "javascript",
208 vec![
209 Arc::new(deno::DenoLspAdapter::new()),
210 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
211 ]
212 );
213 language!("jsdoc", vec![Arc::new(deno::DenoLspAdapter::new())]);
214 }
215 false => {
216 language!(
217 "tsx",
218 vec![
219 Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
220 Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
221 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
222 ]
223 );
224 language!(
225 "typescript",
226 vec![
227 Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
228 Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
229 ]
230 );
231 language!(
232 "javascript",
233 vec![
234 Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
235 Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
236 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
237 ]
238 );
239 language!(
240 "jsdoc",
241 vec![Arc::new(typescript::TypeScriptLspAdapter::new(
242 node_runtime.clone(),
243 ))]
244 );
245 }
246 }
247
248 language!("ruby", vec![Arc::new(ruby::RubyLanguageServer)]);
249 language!(
250 "erb",
251 vec![
252 Arc::new(ruby::RubyLanguageServer),
253 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
254 ]
255 );
256 language!("regex");
257 language!(
258 "yaml",
259 vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))]
260 );
261 language!("proto");
262
263 languages.register_secondary_lsp_adapter(
264 "Astro".into(),
265 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
266 );
267 languages.register_secondary_lsp_adapter(
268 "HTML".into(),
269 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
270 );
271 languages.register_secondary_lsp_adapter(
272 "PHP".into(),
273 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
274 );
275 languages.register_secondary_lsp_adapter(
276 "Svelte".into(),
277 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
278 );
279 languages.register_secondary_lsp_adapter(
280 "Vue.js".into(),
281 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
282 );
283
284 let mut subscription = languages.subscribe();
285 let mut prev_language_settings = languages.language_settings();
286
287 cx.spawn(|cx| async move {
288 while subscription.next().await.is_some() {
289 let language_settings = languages.language_settings();
290 if language_settings != prev_language_settings {
291 cx.update(|cx| {
292 cx.update_global(|settings: &mut SettingsStore, cx| {
293 settings
294 .set_extension_settings(language_settings.clone(), cx)
295 .log_err();
296 });
297 })?;
298 prev_language_settings = language_settings;
299 }
300 }
301 anyhow::Ok(())
302 })
303 .detach();
304}
305
306#[cfg(any(test, feature = "test-support"))]
307pub fn language(name: &str, grammar: tree_sitter::Language) -> Arc<Language> {
308 Arc::new(
309 Language::new(load_config(name), Some(grammar))
310 .with_queries(load_queries(name))
311 .unwrap(),
312 )
313}
314
315fn load_config(name: &str) -> LanguageConfig {
316 let config_toml = String::from_utf8(
317 LanguageDir::get(&format!("{}/config.toml", name))
318 .unwrap()
319 .data
320 .to_vec(),
321 )
322 .unwrap();
323
324 ::toml::from_str(&config_toml)
325 .with_context(|| format!("failed to load config.toml for language {name:?}"))
326 .unwrap()
327}
328
329fn load_queries(name: &str) -> LanguageQueries {
330 let mut result = LanguageQueries::default();
331 for path in LanguageDir::iter() {
332 if let Some(remainder) = path.strip_prefix(name).and_then(|p| p.strip_prefix('/')) {
333 if !remainder.ends_with(".scm") {
334 continue;
335 }
336 for (name, query) in QUERY_FILENAME_PREFIXES {
337 if remainder.starts_with(name) {
338 let contents = asset_str::<LanguageDir>(path.as_ref());
339 match query(&mut result) {
340 None => *query(&mut result) = Some(contents),
341 Some(r) => r.to_mut().push_str(contents.as_ref()),
342 }
343 }
344 }
345 }
346 }
347 result
348}