lib.rs

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