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