lib.rs

  1use gpui::{App, SharedString, UpdateGlobal};
  2use node_runtime::NodeRuntime;
  3use project::Fs;
  4use python::PyprojectTomlManifestProvider;
  5use rust::CargoManifestProvider;
  6use settings::{SemanticTokenRules, SettingsStore};
  7use smol::stream::StreamExt;
  8use std::sync::Arc;
  9use util::ResultExt;
 10
 11pub use language::*;
 12
 13use crate::{
 14    json::JsonTaskProvider,
 15    python::{BasedPyrightLspAdapter, RuffLspAdapter},
 16};
 17
 18mod bash;
 19mod c;
 20mod cpp;
 21mod css;
 22mod eslint;
 23mod go;
 24mod json;
 25mod package_json;
 26mod python;
 27mod rust;
 28mod tailwind;
 29mod tailwindcss;
 30mod typescript;
 31mod vtsls;
 32mod yaml;
 33
 34pub(crate) use package_json::{PackageJson, PackageJsonData};
 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::SoftWrap::EditorWidth),
 44                matcher: LanguageMatcher {
 45                    path_suffixes: vec!["COMMIT_EDITMSG".to_owned()],
 46                    first_line_pattern: None,
 47                    ..LanguageMatcher::default()
 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>, fs: Arc<dyn Fs>, node: NodeRuntime, cx: &mut App) {
 57    #[cfg(feature = "load-grammars")]
 58    languages.register_native_grammars(grammars::native_grammars());
 59
 60    let c_lsp_adapter = Arc::new(c::CLspAdapter);
 61    let css_lsp_adapter = Arc::new(css::CssLspAdapter::new(node.clone()));
 62    let eslint_adapter = Arc::new(eslint::EsLintLspAdapter::new(node.clone()));
 63    let go_context_provider = Arc::new(go::GoContextProvider);
 64    let go_lsp_adapter = Arc::new(go::GoLspAdapter);
 65    let json_context_provider = Arc::new(JsonTaskProvider);
 66    let json_lsp_adapter = Arc::new(json::JsonLspAdapter::new(languages.clone(), node.clone()));
 67    let node_version_lsp_adapter = Arc::new(json::NodeVersionAdapter);
 68    let py_lsp_adapter = Arc::new(python::PyLspAdapter::new());
 69    let ty_lsp_adapter = Arc::new(python::TyLspAdapter::new(fs.clone()));
 70    let python_context_provider = Arc::new(python::PythonContextProvider);
 71    let python_lsp_adapter = Arc::new(python::PyrightLspAdapter::new(node.clone()));
 72    let basedpyright_lsp_adapter = Arc::new(BasedPyrightLspAdapter::new(node.clone()));
 73    let ruff_lsp_adapter = Arc::new(RuffLspAdapter::new(fs.clone()));
 74    let python_toolchain_provider = Arc::new(python::PythonToolchainProvider::new(fs.clone()));
 75    let rust_context_provider = Arc::new(rust::RustContextProvider);
 76    let rust_lsp_adapter = Arc::new(rust::RustLspAdapter);
 77    let tailwind_adapter = Arc::new(tailwind::TailwindLspAdapter::new(node.clone()));
 78    let tailwindcss_adapter = Arc::new(tailwindcss::TailwindCssLspAdapter::new(node.clone()));
 79    let typescript_context = Arc::new(typescript::TypeScriptContextProvider::new(fs.clone()));
 80    let typescript_lsp_adapter = Arc::new(typescript::TypeScriptLspAdapter::new(
 81        node.clone(),
 82        fs.clone(),
 83    ));
 84    let vtsls_adapter = Arc::new(vtsls::VtslsLspAdapter::new(node.clone(), fs.clone()));
 85    let yaml_lsp_adapter = Arc::new(yaml::YamlLspAdapter::new(node));
 86
 87    let built_in_languages = [
 88        LanguageInfo {
 89            name: "bash",
 90            context: Some(Arc::new(bash::bash_task_context())),
 91            ..Default::default()
 92        },
 93        LanguageInfo {
 94            name: "c",
 95            adapters: vec![c_lsp_adapter.clone()],
 96            ..Default::default()
 97        },
 98        LanguageInfo {
 99            name: "cpp",
100            adapters: vec![c_lsp_adapter],
101            semantic_token_rules: Some(cpp::semantic_token_rules()),
102            ..Default::default()
103        },
104        LanguageInfo {
105            name: "css",
106            adapters: vec![css_lsp_adapter],
107            ..Default::default()
108        },
109        LanguageInfo {
110            name: "diff",
111            adapters: vec![],
112            ..Default::default()
113        },
114        LanguageInfo {
115            name: "go",
116            adapters: vec![go_lsp_adapter.clone()],
117            context: Some(go_context_provider.clone()),
118            semantic_token_rules: Some(go::semantic_token_rules()),
119            ..Default::default()
120        },
121        LanguageInfo {
122            name: "gomod",
123            adapters: vec![go_lsp_adapter.clone()],
124            context: Some(go_context_provider.clone()),
125            ..Default::default()
126        },
127        LanguageInfo {
128            name: "gowork",
129            adapters: vec![go_lsp_adapter],
130            context: Some(go_context_provider),
131            ..Default::default()
132        },
133        LanguageInfo {
134            name: "json",
135            adapters: vec![json_lsp_adapter.clone(), node_version_lsp_adapter],
136            context: Some(json_context_provider.clone()),
137            ..Default::default()
138        },
139        LanguageInfo {
140            name: "jsonc",
141            adapters: vec![json_lsp_adapter],
142            context: Some(json_context_provider),
143            ..Default::default()
144        },
145        LanguageInfo {
146            name: "markdown",
147            adapters: vec![],
148            ..Default::default()
149        },
150        LanguageInfo {
151            name: "markdown-inline",
152            adapters: vec![],
153            ..Default::default()
154        },
155        LanguageInfo {
156            name: "python",
157            adapters: vec![
158                basedpyright_lsp_adapter,
159                ruff_lsp_adapter,
160                ty_lsp_adapter,
161                py_lsp_adapter,
162                python_lsp_adapter,
163            ],
164            context: Some(python_context_provider),
165            toolchain: Some(python_toolchain_provider),
166            manifest_name: Some(SharedString::new_static("pyproject.toml").into()),
167            semantic_token_rules: Some(python::semantic_token_rules()),
168        },
169        LanguageInfo {
170            name: "rust",
171            adapters: vec![rust_lsp_adapter],
172            context: Some(rust_context_provider),
173            manifest_name: Some(SharedString::new_static("Cargo.toml").into()),
174            semantic_token_rules: Some(rust::semantic_token_rules()),
175            ..Default::default()
176        },
177        LanguageInfo {
178            name: "tsx",
179            adapters: vec![typescript_lsp_adapter.clone(), vtsls_adapter.clone()],
180            context: Some(typescript_context.clone()),
181            ..Default::default()
182        },
183        LanguageInfo {
184            name: "typescript",
185            adapters: vec![typescript_lsp_adapter.clone(), vtsls_adapter.clone()],
186            context: Some(typescript_context.clone()),
187            ..Default::default()
188        },
189        LanguageInfo {
190            name: "javascript",
191            adapters: vec![typescript_lsp_adapter.clone(), vtsls_adapter.clone()],
192            context: Some(typescript_context),
193            ..Default::default()
194        },
195        LanguageInfo {
196            name: "jsdoc",
197            adapters: vec![typescript_lsp_adapter.clone(), vtsls_adapter.clone()],
198            ..Default::default()
199        },
200        LanguageInfo {
201            name: "regex",
202            adapters: vec![],
203            ..Default::default()
204        },
205        LanguageInfo {
206            name: "yaml",
207            adapters: vec![yaml_lsp_adapter],
208            ..Default::default()
209        },
210        LanguageInfo {
211            name: "gitcommit",
212            ..Default::default()
213        },
214        LanguageInfo {
215            name: "zed-keybind-context",
216            ..Default::default()
217        },
218    ];
219
220    for registration in built_in_languages {
221        register_language(
222            &languages,
223            registration.name,
224            registration.adapters,
225            registration.context,
226            registration.toolchain,
227            registration.manifest_name,
228            registration.semantic_token_rules,
229            cx,
230        );
231    }
232
233    // Register globally available language servers.
234    //
235    // This will allow users to add support for a built-in language server (e.g., Tailwind)
236    // for a given language via the `language_servers` setting:
237    //
238    // ```json
239    // {
240    //   "languages": {
241    //     "My Language": {
242    //       "language_servers": ["tailwindcss-language-server", "..."]
243    //     }
244    //   }
245    // }
246    // ```
247    languages.register_available_lsp_adapter(
248        LanguageServerName("tailwindcss-language-server".into()),
249        tailwind_adapter.clone(),
250    );
251    languages.register_available_lsp_adapter(
252        LanguageServerName("tailwindcss-intellisense-css".into()),
253        tailwindcss_adapter,
254    );
255    languages.register_available_lsp_adapter(
256        LanguageServerName("eslint".into()),
257        eslint_adapter.clone(),
258    );
259    languages.register_available_lsp_adapter(LanguageServerName("vtsls".into()), vtsls_adapter);
260    languages.register_available_lsp_adapter(
261        LanguageServerName("typescript-language-server".into()),
262        typescript_lsp_adapter,
263    );
264
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        "HEEx",
275        "HTML",
276        "JavaScript",
277        "TypeScript",
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(
304                                settings::ExtensionsSettingsContent {
305                                    all_languages: language_settings.clone(),
306                                },
307                                cx,
308                            )
309                            .log_err();
310                    });
311                });
312                prev_language_settings = language_settings;
313            }
314        }
315        anyhow::Ok(())
316    })
317    .detach();
318    let manifest_providers: [Arc<dyn ManifestProvider>; 2] = [
319        Arc::from(CargoManifestProvider),
320        Arc::from(PyprojectTomlManifestProvider),
321    ];
322    for provider in manifest_providers {
323        project::ManifestProvidersStore::global(cx).register(provider);
324    }
325}
326
327#[derive(Default)]
328struct LanguageInfo {
329    name: &'static str,
330    adapters: Vec<Arc<dyn LspAdapter>>,
331    context: Option<Arc<dyn ContextProvider>>,
332    toolchain: Option<Arc<dyn ToolchainLister>>,
333    manifest_name: Option<ManifestName>,
334    semantic_token_rules: Option<SemanticTokenRules>,
335}
336
337fn register_language(
338    languages: &LanguageRegistry,
339    name: &'static str,
340    adapters: Vec<Arc<dyn LspAdapter>>,
341    context: Option<Arc<dyn ContextProvider>>,
342    toolchain: Option<Arc<dyn ToolchainLister>>,
343    manifest_name: Option<ManifestName>,
344    semantic_token_rules: Option<SemanticTokenRules>,
345    cx: &mut App,
346) {
347    let config = load_config(name);
348    if let Some(rules) = &semantic_token_rules {
349        SettingsStore::update_global(cx, |store, cx| {
350            store.set_language_semantic_token_rules(config.name.0.clone(), rules.clone(), cx);
351        });
352    }
353    for adapter in adapters {
354        languages.register_lsp_adapter(config.name.clone(), adapter);
355    }
356    languages.register_language(
357        config.name.clone(),
358        config.grammar.clone(),
359        config.matcher.clone(),
360        config.hidden,
361        manifest_name.clone(),
362        Arc::new(move || {
363            Ok(LoadedLanguage {
364                config: config.clone(),
365                queries: grammars::load_queries(name),
366                context_provider: context.clone(),
367                toolchain_provider: toolchain.clone(),
368                manifest_name: manifest_name.clone(),
369            })
370        }),
371    );
372}
373
374#[cfg(any(test, feature = "test-support"))]
375pub fn language(name: &str, grammar: tree_sitter::Language) -> Arc<Language> {
376    Arc::new(
377        Language::new(grammars::load_config(name), Some(grammar))
378            .with_queries(grammars::load_queries(name))
379            .unwrap(),
380    )
381}
382
383fn load_config(name: &str) -> LanguageConfig {
384    let grammars_loaded = cfg!(any(feature = "load-grammars", test));
385    grammars::load_config_for_feature(name, grammars_loaded)
386}