lib.rs

  1use anyhow::Context as _;
  2use gpui::{App, UpdateGlobal};
  3use json::json_task_context;
  4pub use language::*;
  5use lsp::LanguageServerName;
  6use node_runtime::NodeRuntime;
  7use python::{PythonContextProvider, PythonToolchainProvider};
  8use rust_embed::RustEmbed;
  9use settings::SettingsStore;
 10use smol::stream::StreamExt;
 11use std::{str, sync::Arc};
 12use typescript::typescript_task_context;
 13use util::{asset_str, ResultExt};
 14
 15use crate::{bash::bash_task_context, go::GoContextProvider, rust::RustContextProvider};
 16
 17mod bash;
 18mod c;
 19mod css;
 20mod go;
 21mod json;
 22mod python;
 23mod rust;
 24mod tailwind;
 25mod typescript;
 26mod vtsls;
 27mod yaml;
 28
 29#[derive(RustEmbed)]
 30#[folder = "src/"]
 31#[exclude = "*.rs"]
 32struct LanguageDir;
 33
 34pub fn init(languages: Arc<LanguageRegistry>, node_runtime: NodeRuntime, cx: &mut App) {
 35    #[cfg(feature = "load-grammars")]
 36    languages.register_native_grammars([
 37        ("bash", tree_sitter_bash::LANGUAGE),
 38        ("c", tree_sitter_c::LANGUAGE),
 39        ("cpp", tree_sitter_cpp::LANGUAGE),
 40        ("css", tree_sitter_css::LANGUAGE),
 41        ("diff", tree_sitter_diff::LANGUAGE),
 42        ("go", tree_sitter_go::LANGUAGE),
 43        ("gomod", tree_sitter_go_mod::LANGUAGE),
 44        ("gowork", tree_sitter_gowork::LANGUAGE),
 45        ("jsdoc", tree_sitter_jsdoc::LANGUAGE),
 46        ("json", tree_sitter_json::LANGUAGE),
 47        ("jsonc", tree_sitter_json::LANGUAGE),
 48        ("markdown", tree_sitter_md::LANGUAGE),
 49        ("markdown-inline", tree_sitter_md::INLINE_LANGUAGE),
 50        ("python", tree_sitter_python::LANGUAGE),
 51        ("regex", tree_sitter_regex::LANGUAGE),
 52        ("rust", tree_sitter_rust::LANGUAGE),
 53        ("tsx", tree_sitter_typescript::LANGUAGE_TSX),
 54        ("typescript", tree_sitter_typescript::LANGUAGE_TYPESCRIPT),
 55        ("yaml", tree_sitter_yaml::LANGUAGE),
 56    ]);
 57
 58    macro_rules! language {
 59        ($name:literal) => {
 60            let config = load_config($name);
 61            languages.register_language(
 62                config.name.clone(),
 63                config.grammar.clone(),
 64                config.matcher.clone(),
 65                config.hidden,
 66                Arc::new(move || {
 67                    Ok(LoadedLanguage {
 68                        config: config.clone(),
 69                        queries: load_queries($name),
 70                        context_provider: None,
 71                        toolchain_provider: None,
 72                    })
 73                }),
 74            );
 75        };
 76        ($name:literal, $adapters:expr) => {
 77            let config = load_config($name);
 78            // typeck helper
 79            let adapters: Vec<Arc<dyn LspAdapter>> = $adapters;
 80            for adapter in adapters {
 81                languages.register_lsp_adapter(config.name.clone(), adapter);
 82            }
 83            languages.register_language(
 84                config.name.clone(),
 85                config.grammar.clone(),
 86                config.matcher.clone(),
 87                config.hidden,
 88                Arc::new(move || {
 89                    Ok(LoadedLanguage {
 90                        config: config.clone(),
 91                        queries: load_queries($name),
 92                        context_provider: None,
 93                        toolchain_provider: None,
 94                    })
 95                }),
 96            );
 97        };
 98        ($name:literal, $adapters:expr, $context_provider:expr) => {
 99            let config = load_config($name);
100            // typeck helper
101            let adapters: Vec<Arc<dyn LspAdapter>> = $adapters;
102            for adapter in adapters {
103                languages.register_lsp_adapter(config.name.clone(), adapter);
104            }
105            languages.register_language(
106                config.name.clone(),
107                config.grammar.clone(),
108                config.matcher.clone(),
109                config.hidden,
110                Arc::new(move || {
111                    Ok(LoadedLanguage {
112                        config: config.clone(),
113                        queries: load_queries($name),
114                        context_provider: Some(Arc::new($context_provider)),
115                        toolchain_provider: None,
116                    })
117                }),
118            );
119        };
120        ($name:literal, $adapters:expr, $context_provider:expr, $toolchain_provider:expr) => {
121            let config = load_config($name);
122            // typeck helper
123            let adapters: Vec<Arc<dyn LspAdapter>> = $adapters;
124            for adapter in adapters {
125                languages.register_lsp_adapter(config.name.clone(), adapter);
126            }
127            languages.register_language(
128                config.name.clone(),
129                config.grammar.clone(),
130                config.matcher.clone(),
131                config.hidden,
132                Arc::new(move || {
133                    Ok(LoadedLanguage {
134                        config: config.clone(),
135                        queries: load_queries($name),
136                        context_provider: Some(Arc::new($context_provider)),
137                        toolchain_provider: Some($toolchain_provider),
138                    })
139                }),
140            );
141        };
142    }
143    language!("bash", Vec::new(), bash_task_context());
144    language!("c", vec![Arc::new(c::CLspAdapter) as Arc<dyn LspAdapter>]);
145    language!("cpp", vec![Arc::new(c::CLspAdapter)]);
146    language!(
147        "css",
148        vec![Arc::new(css::CssLspAdapter::new(node_runtime.clone())),]
149    );
150    language!("diff");
151    language!("go", vec![Arc::new(go::GoLspAdapter)], GoContextProvider);
152    language!("gomod", vec![Arc::new(go::GoLspAdapter)], GoContextProvider);
153    language!(
154        "gowork",
155        vec![Arc::new(go::GoLspAdapter)],
156        GoContextProvider
157    );
158
159    language!(
160        "json",
161        vec![
162            Arc::new(json::JsonLspAdapter::new(
163                node_runtime.clone(),
164                languages.clone(),
165            )),
166            Arc::new(json::NodeVersionAdapter)
167        ],
168        json_task_context()
169    );
170    language!(
171        "jsonc",
172        vec![Arc::new(json::JsonLspAdapter::new(
173            node_runtime.clone(),
174            languages.clone(),
175        ))],
176        json_task_context()
177    );
178    language!("markdown");
179    language!("markdown-inline");
180    language!(
181        "python",
182        vec![
183            Arc::new(python::PythonLspAdapter::new(node_runtime.clone(),)),
184            Arc::new(python::PyLspAdapter::new())
185        ],
186        PythonContextProvider,
187        Arc::new(PythonToolchainProvider::default()) as Arc<dyn ToolchainLister>
188    );
189    language!(
190        "rust",
191        vec![Arc::new(rust::RustLspAdapter)],
192        RustContextProvider
193    );
194    language!(
195        "tsx",
196        vec![
197            Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
198            Arc::new(vtsls::VtslsLspAdapter::new(node_runtime.clone()))
199        ],
200        typescript_task_context()
201    );
202    language!(
203        "typescript",
204        vec![
205            Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
206            Arc::new(vtsls::VtslsLspAdapter::new(node_runtime.clone()))
207        ],
208        typescript_task_context()
209    );
210    language!(
211        "javascript",
212        vec![
213            Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
214            Arc::new(vtsls::VtslsLspAdapter::new(node_runtime.clone()))
215        ],
216        typescript_task_context()
217    );
218    language!(
219        "jsdoc",
220        vec![
221            Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone(),)),
222            Arc::new(vtsls::VtslsLspAdapter::new(node_runtime.clone()))
223        ]
224    );
225    language!("regex");
226    language!(
227        "yaml",
228        vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))]
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 node_runtime = node_runtime.clone();
249            move || Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone()))
250        },
251    );
252    languages.register_available_lsp_adapter(LanguageServerName("eslint".into()), {
253        let node_runtime = node_runtime.clone();
254        move || Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone()))
255    });
256
257    // Register Tailwind for the existing languages that should have it by default.
258    //
259    // This can be driven by the `language_servers` setting once we have a way for
260    // extensions to provide their own default value for that setting.
261    let tailwind_languages = [
262        "Astro",
263        "CSS",
264        "ERB",
265        "HEEX",
266        "HTML",
267        "JavaScript",
268        "PHP",
269        "Svelte",
270        "TSX",
271        "Vue.js",
272    ];
273
274    for language in tailwind_languages {
275        languages.register_lsp_adapter(
276            language.into(),
277            Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
278        );
279    }
280
281    let eslint_languages = ["TSX", "TypeScript", "JavaScript", "Vue.js", "Svelte"];
282    for language in eslint_languages {
283        languages.register_lsp_adapter(
284            language.into(),
285            Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
286        );
287    }
288
289    let mut subscription = languages.subscribe();
290    let mut prev_language_settings = languages.language_settings();
291
292    cx.spawn(|cx| async move {
293        while subscription.next().await.is_some() {
294            let language_settings = languages.language_settings();
295            if language_settings != prev_language_settings {
296                cx.update(|cx| {
297                    SettingsStore::update_global(cx, |settings, cx| {
298                        settings
299                            .set_extension_settings(language_settings.clone(), cx)
300                            .log_err();
301                    });
302                })?;
303                prev_language_settings = language_settings;
304            }
305        }
306        anyhow::Ok(())
307    })
308    .detach();
309}
310
311#[cfg(any(test, feature = "test-support"))]
312pub fn language(name: &str, grammar: tree_sitter::Language) -> Arc<Language> {
313    Arc::new(
314        Language::new(load_config(name), Some(grammar))
315            .with_queries(load_queries(name))
316            .unwrap(),
317    )
318}
319
320fn load_config(name: &str) -> LanguageConfig {
321    let config_toml = String::from_utf8(
322        LanguageDir::get(&format!("{}/config.toml", name))
323            .unwrap_or_else(|| panic!("missing config for language {:?}", name))
324            .data
325            .to_vec(),
326    )
327    .unwrap();
328
329    #[allow(unused_mut)]
330    let mut config: LanguageConfig = ::toml::from_str(&config_toml)
331        .with_context(|| format!("failed to load config.toml for language {name:?}"))
332        .unwrap();
333
334    #[cfg(not(any(feature = "load-grammars", test)))]
335    {
336        config = LanguageConfig {
337            name: config.name,
338            matcher: config.matcher,
339            ..Default::default()
340        }
341    }
342
343    config
344}
345
346fn load_queries(name: &str) -> LanguageQueries {
347    let mut result = LanguageQueries::default();
348    for path in LanguageDir::iter() {
349        if let Some(remainder) = path.strip_prefix(name).and_then(|p| p.strip_prefix('/')) {
350            if !remainder.ends_with(".scm") {
351                continue;
352            }
353            for (name, query) in QUERY_FILENAME_PREFIXES {
354                if remainder.starts_with(name) {
355                    let contents = asset_str::<LanguageDir>(path.as_ref());
356                    match query(&mut result) {
357                        None => *query(&mut result) = Some(contents),
358                        Some(r) => r.to_mut().push_str(contents.as_ref()),
359                    }
360                }
361            }
362        }
363    }
364    result
365}