1use anyhow::Context;
2use gpui::{AppContext, UpdateGlobal};
3use json::json_task_context;
4pub use language::*;
5use lsp::LanguageServerName;
6use node_runtime::NodeRuntime;
7use python::{PythonContextProvider, PythonToolchainProvider};
8use rust_embed::RustEmbed;
9use settings::SettingsStore;
10use smol::stream::StreamExt;
11use std::{str, sync::Arc};
12use typescript::typescript_task_context;
13use util::{asset_str, ResultExt};
14
15use crate::{bash::bash_task_context, go::GoContextProvider, rust::RustContextProvider};
16
17mod bash;
18mod c;
19mod css;
20mod go;
21mod json;
22mod python;
23mod rust;
24mod tailwind;
25mod typescript;
26mod vtsls;
27mod yaml;
28
29#[derive(RustEmbed)]
30#[folder = "src/"]
31#[exclude = "*.rs"]
32struct LanguageDir;
33
34pub fn init(languages: Arc<LanguageRegistry>, node_runtime: NodeRuntime, cx: &mut AppContext) {
35 #[cfg(feature = "load-grammars")]
36 languages.register_native_grammars([
37 ("bash", tree_sitter_bash::LANGUAGE),
38 ("c", tree_sitter_c::LANGUAGE),
39 ("cpp", tree_sitter_cpp::LANGUAGE),
40 ("css", tree_sitter_css::LANGUAGE),
41 ("diff", tree_sitter_diff::LANGUAGE),
42 ("go", tree_sitter_go::LANGUAGE),
43 ("gomod", tree_sitter_go_mod::LANGUAGE),
44 ("gowork", tree_sitter_gowork::LANGUAGE),
45 ("jsdoc", tree_sitter_jsdoc::LANGUAGE),
46 ("json", tree_sitter_json::LANGUAGE),
47 ("jsonc", tree_sitter_json::LANGUAGE),
48 ("markdown", tree_sitter_md::LANGUAGE),
49 ("markdown-inline", tree_sitter_md::INLINE_LANGUAGE),
50 ("python", tree_sitter_python::LANGUAGE),
51 ("regex", tree_sitter_regex::LANGUAGE),
52 ("rust", tree_sitter_rust::LANGUAGE),
53 ("tsx", tree_sitter_typescript::LANGUAGE_TSX),
54 ("typescript", tree_sitter_typescript::LANGUAGE_TYPESCRIPT),
55 ("yaml", tree_sitter_yaml::LANGUAGE),
56 ]);
57
58 macro_rules! language {
59 ($name:literal) => {
60 let config = load_config($name);
61 languages.register_language(
62 config.name.clone(),
63 config.grammar.clone(),
64 config.matcher.clone(),
65 Arc::new(move || {
66 Ok(LoadedLanguage {
67 config: config.clone(),
68 queries: load_queries($name),
69 context_provider: None,
70 toolchain_provider: None,
71 })
72 }),
73 );
74 };
75 ($name:literal, $adapters:expr) => {
76 let config = load_config($name);
77 // typeck helper
78 let adapters: Vec<Arc<dyn LspAdapter>> = $adapters;
79 for adapter in adapters {
80 languages.register_lsp_adapter(config.name.clone(), adapter);
81 }
82 languages.register_language(
83 config.name.clone(),
84 config.grammar.clone(),
85 config.matcher.clone(),
86 Arc::new(move || {
87 Ok(LoadedLanguage {
88 config: config.clone(),
89 queries: load_queries($name),
90 context_provider: None,
91 toolchain_provider: None,
92 })
93 }),
94 );
95 };
96 ($name:literal, $adapters:expr, $context_provider:expr) => {
97 let config = load_config($name);
98 // typeck helper
99 let adapters: Vec<Arc<dyn LspAdapter>> = $adapters;
100 for adapter in adapters {
101 languages.register_lsp_adapter(config.name.clone(), adapter);
102 }
103 languages.register_language(
104 config.name.clone(),
105 config.grammar.clone(),
106 config.matcher.clone(),
107 Arc::new(move || {
108 Ok(LoadedLanguage {
109 config: config.clone(),
110 queries: load_queries($name),
111 context_provider: Some(Arc::new($context_provider)),
112 toolchain_provider: None,
113 })
114 }),
115 );
116 };
117 ($name:literal, $adapters:expr, $context_provider:expr, $toolchain_provider:expr) => {
118 let config = load_config($name);
119 // typeck helper
120 let adapters: Vec<Arc<dyn LspAdapter>> = $adapters;
121 for adapter in adapters {
122 languages.register_lsp_adapter(config.name.clone(), adapter);
123 }
124 languages.register_language(
125 config.name.clone(),
126 config.grammar.clone(),
127 config.matcher.clone(),
128 Arc::new(move || {
129 Ok(LoadedLanguage {
130 config: config.clone(),
131 queries: load_queries($name),
132 context_provider: Some(Arc::new($context_provider)),
133 toolchain_provider: Some($toolchain_provider),
134 })
135 }),
136 );
137 };
138 }
139 language!("bash", Vec::new(), bash_task_context());
140 language!("c", vec![Arc::new(c::CLspAdapter) as Arc<dyn LspAdapter>]);
141 language!("cpp", vec![Arc::new(c::CLspAdapter)]);
142 language!(
143 "css",
144 vec![Arc::new(css::CssLspAdapter::new(node_runtime.clone())),]
145 );
146 language!("diff");
147 language!("go", vec![Arc::new(go::GoLspAdapter)], GoContextProvider);
148 language!("gomod", vec![Arc::new(go::GoLspAdapter)], GoContextProvider);
149 language!(
150 "gowork",
151 vec![Arc::new(go::GoLspAdapter)],
152 GoContextProvider
153 );
154
155 language!(
156 "json",
157 vec![
158 Arc::new(json::JsonLspAdapter::new(
159 node_runtime.clone(),
160 languages.clone(),
161 )),
162 Arc::new(json::NodeVersionAdapter)
163 ],
164 json_task_context()
165 );
166 language!(
167 "jsonc",
168 vec![Arc::new(json::JsonLspAdapter::new(
169 node_runtime.clone(),
170 languages.clone(),
171 ))],
172 json_task_context()
173 );
174 language!("markdown");
175 language!("markdown-inline");
176 language!(
177 "python",
178 vec![
179 Arc::new(python::PythonLspAdapter::new(node_runtime.clone(),)),
180 Arc::new(python::PyLspAdapter::new())
181 ],
182 PythonContextProvider,
183 Arc::new(PythonToolchainProvider::default()) as Arc<dyn ToolchainLister>
184 );
185 language!(
186 "rust",
187 vec![Arc::new(rust::RustLspAdapter)],
188 RustContextProvider
189 );
190 language!(
191 "tsx",
192 vec![
193 Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
194 Arc::new(vtsls::VtslsLspAdapter::new(node_runtime.clone()))
195 ],
196 typescript_task_context()
197 );
198 language!(
199 "typescript",
200 vec![
201 Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
202 Arc::new(vtsls::VtslsLspAdapter::new(node_runtime.clone()))
203 ],
204 typescript_task_context()
205 );
206 language!(
207 "javascript",
208 vec![
209 Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
210 Arc::new(vtsls::VtslsLspAdapter::new(node_runtime.clone()))
211 ],
212 typescript_task_context()
213 );
214 language!(
215 "jsdoc",
216 vec![
217 Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone(),)),
218 Arc::new(vtsls::VtslsLspAdapter::new(node_runtime.clone()))
219 ]
220 );
221 language!("regex");
222 language!(
223 "yaml",
224 vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))]
225 );
226
227 // Register globally available language servers.
228 //
229 // This will allow users to add support for a built-in language server (e.g., Tailwind)
230 // for a given language via the `language_servers` setting:
231 //
232 // ```json
233 // {
234 // "languages": {
235 // "My Language": {
236 // "language_servers": ["tailwindcss-language-server", "..."]
237 // }
238 // }
239 // }
240 // ```
241 languages.register_available_lsp_adapter(
242 LanguageServerName("tailwindcss-language-server".into()),
243 {
244 let node_runtime = node_runtime.clone();
245 move || Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone()))
246 },
247 );
248 languages.register_available_lsp_adapter(LanguageServerName("eslint".into()), {
249 let node_runtime = node_runtime.clone();
250 move || Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone()))
251 });
252
253 // Register Tailwind for the existing languages that should have it by default.
254 //
255 // This can be driven by the `language_servers` setting once we have a way for
256 // extensions to provide their own default value for that setting.
257 let tailwind_languages = [
258 "Astro",
259 "CSS",
260 "ERB",
261 "HEEX",
262 "HTML",
263 "JavaScript",
264 "PHP",
265 "Svelte",
266 "TSX",
267 "Vue.js",
268 ];
269
270 for language in tailwind_languages {
271 languages.register_lsp_adapter(
272 language.into(),
273 Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
274 );
275 }
276
277 let eslint_languages = ["TSX", "TypeScript", "JavaScript", "Vue.js", "Svelte"];
278 for language in eslint_languages {
279 languages.register_lsp_adapter(
280 language.into(),
281 Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
282 );
283 }
284
285 let mut subscription = languages.subscribe();
286 let mut prev_language_settings = languages.language_settings();
287
288 cx.spawn(|cx| async move {
289 while subscription.next().await.is_some() {
290 let language_settings = languages.language_settings();
291 if language_settings != prev_language_settings {
292 cx.update(|cx| {
293 SettingsStore::update_global(cx, |settings, cx| {
294 settings
295 .set_extension_settings(language_settings.clone(), cx)
296 .log_err();
297 });
298 })?;
299 prev_language_settings = language_settings;
300 }
301 }
302 anyhow::Ok(())
303 })
304 .detach();
305}
306
307#[cfg(any(test, feature = "test-support"))]
308pub fn language(name: &str, grammar: tree_sitter::Language) -> Arc<Language> {
309 Arc::new(
310 Language::new(load_config(name), Some(grammar))
311 .with_queries(load_queries(name))
312 .unwrap(),
313 )
314}
315
316fn load_config(name: &str) -> LanguageConfig {
317 let config_toml = String::from_utf8(
318 LanguageDir::get(&format!("{}/config.toml", name))
319 .unwrap_or_else(|| panic!("missing config for language {:?}", name))
320 .data
321 .to_vec(),
322 )
323 .unwrap();
324
325 #[allow(unused_mut)]
326 let mut config: LanguageConfig = ::toml::from_str(&config_toml)
327 .with_context(|| format!("failed to load config.toml for language {name:?}"))
328 .unwrap();
329
330 #[cfg(not(any(feature = "load-grammars", test)))]
331 {
332 config = LanguageConfig {
333 name: config.name,
334 matcher: config.matcher,
335 ..Default::default()
336 }
337 }
338
339 config
340}
341
342fn load_queries(name: &str) -> LanguageQueries {
343 let mut result = LanguageQueries::default();
344 for path in LanguageDir::iter() {
345 if let Some(remainder) = path.strip_prefix(name).and_then(|p| p.strip_prefix('/')) {
346 if !remainder.ends_with(".scm") {
347 continue;
348 }
349 for (name, query) in QUERY_FILENAME_PREFIXES {
350 if remainder.starts_with(name) {
351 let contents = asset_str::<LanguageDir>(path.as_ref());
352 match query(&mut result) {
353 None => *query(&mut result) = Some(contents),
354 Some(r) => r.to_mut().push_str(contents.as_ref()),
355 }
356 }
357 }
358 }
359 }
360 result
361}