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