lib.rs

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