lib.rs

  1use anyhow::Context;
  2use gpui::{AppContext, 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 AppContext) {
 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                Arc::new(move || {
 66                    Ok(LoadedLanguage {
 67                        config: config.clone(),
 68                        queries: load_queries($name),
 69                        context_provider: None,
 70                        toolchain_provider: None,
 71                    })
 72                }),
 73            );
 74        };
 75        ($name:literal, $adapters:expr) => {
 76            let config = load_config($name);
 77            // typeck helper
 78            let adapters: Vec<Arc<dyn LspAdapter>> = $adapters;
 79            for adapter in adapters {
 80                languages.register_lsp_adapter(config.name.clone(), adapter);
 81            }
 82            languages.register_language(
 83                config.name.clone(),
 84                config.grammar.clone(),
 85                config.matcher.clone(),
 86                Arc::new(move || {
 87                    Ok(LoadedLanguage {
 88                        config: config.clone(),
 89                        queries: load_queries($name),
 90                        context_provider: None,
 91                        toolchain_provider: None,
 92                    })
 93                }),
 94            );
 95        };
 96        ($name:literal, $adapters:expr, $context_provider:expr) => {
 97            let config = load_config($name);
 98            // typeck helper
 99            let adapters: Vec<Arc<dyn LspAdapter>> = $adapters;
100            for adapter in adapters {
101                languages.register_lsp_adapter(config.name.clone(), adapter);
102            }
103            languages.register_language(
104                config.name.clone(),
105                config.grammar.clone(),
106                config.matcher.clone(),
107                Arc::new(move || {
108                    Ok(LoadedLanguage {
109                        config: config.clone(),
110                        queries: load_queries($name),
111                        context_provider: Some(Arc::new($context_provider)),
112                        toolchain_provider: None,
113                    })
114                }),
115            );
116        };
117        ($name:literal, $adapters:expr, $context_provider:expr, $toolchain_provider:expr) => {
118            let config = load_config($name);
119            // typeck helper
120            let adapters: Vec<Arc<dyn LspAdapter>> = $adapters;
121            for adapter in adapters {
122                languages.register_lsp_adapter(config.name.clone(), adapter);
123            }
124            languages.register_language(
125                config.name.clone(),
126                config.grammar.clone(),
127                config.matcher.clone(),
128                Arc::new(move || {
129                    Ok(LoadedLanguage {
130                        config: config.clone(),
131                        queries: load_queries($name),
132                        context_provider: Some(Arc::new($context_provider)),
133                        toolchain_provider: Some($toolchain_provider),
134                    })
135                }),
136            );
137        };
138    }
139    language!("bash", Vec::new(), bash_task_context());
140    language!("c", vec![Arc::new(c::CLspAdapter) as Arc<dyn LspAdapter>]);
141    language!("cpp", vec![Arc::new(c::CLspAdapter)]);
142    language!(
143        "css",
144        vec![Arc::new(css::CssLspAdapter::new(node_runtime.clone())),]
145    );
146    language!("diff");
147    language!("go", vec![Arc::new(go::GoLspAdapter)], GoContextProvider);
148    language!("gomod", vec![Arc::new(go::GoLspAdapter)], GoContextProvider);
149    language!(
150        "gowork",
151        vec![Arc::new(go::GoLspAdapter)],
152        GoContextProvider
153    );
154
155    language!(
156        "json",
157        vec![
158            Arc::new(json::JsonLspAdapter::new(
159                node_runtime.clone(),
160                languages.clone(),
161            )),
162            Arc::new(json::NodeVersionAdapter)
163        ],
164        json_task_context()
165    );
166    language!(
167        "jsonc",
168        vec![Arc::new(json::JsonLspAdapter::new(
169            node_runtime.clone(),
170            languages.clone(),
171        ))],
172        json_task_context()
173    );
174    language!("markdown");
175    language!("markdown-inline");
176    language!(
177        "python",
178        vec![Arc::new(python::PythonLspAdapter::new(
179            node_runtime.clone(),
180        ))],
181        PythonContextProvider,
182        Arc::new(PythonToolchainProvider::default()) as Arc<dyn ToolchainLister>
183    );
184    language!(
185        "rust",
186        vec![Arc::new(rust::RustLspAdapter)],
187        RustContextProvider
188    );
189    language!(
190        "tsx",
191        vec![
192            Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
193            Arc::new(vtsls::VtslsLspAdapter::new(node_runtime.clone()))
194        ],
195        typescript_task_context()
196    );
197    language!(
198        "typescript",
199        vec![
200            Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
201            Arc::new(vtsls::VtslsLspAdapter::new(node_runtime.clone()))
202        ],
203        typescript_task_context()
204    );
205    language!(
206        "javascript",
207        vec![
208            Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
209            Arc::new(vtsls::VtslsLspAdapter::new(node_runtime.clone()))
210        ],
211        typescript_task_context()
212    );
213    language!(
214        "jsdoc",
215        vec![
216            Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone(),)),
217            Arc::new(vtsls::VtslsLspAdapter::new(node_runtime.clone()))
218        ]
219    );
220    language!("regex");
221    language!(
222        "yaml",
223        vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))]
224    );
225
226    // Register globally available language servers.
227    //
228    // This will allow users to add support for a built-in language server (e.g., Tailwind)
229    // for a given language via the `language_servers` setting:
230    //
231    // ```json
232    // {
233    //   "languages": {
234    //     "My Language": {
235    //       "language_servers": ["tailwindcss-language-server", "..."]
236    //     }
237    //   }
238    // }
239    // ```
240    languages.register_available_lsp_adapter(
241        LanguageServerName("tailwindcss-language-server".into()),
242        {
243            let node_runtime = node_runtime.clone();
244            move || Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone()))
245        },
246    );
247    languages.register_available_lsp_adapter(LanguageServerName("eslint".into()), {
248        let node_runtime = node_runtime.clone();
249        move || Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone()))
250    });
251
252    // Register Tailwind for the existing languages that should have it by default.
253    //
254    // This can be driven by the `language_servers` setting once we have a way for
255    // extensions to provide their own default value for that setting.
256    let tailwind_languages = [
257        "Astro",
258        "CSS",
259        "ERB",
260        "HEEX",
261        "HTML",
262        "JavaScript",
263        "PHP",
264        "Svelte",
265        "TSX",
266        "Vue.js",
267    ];
268
269    for language in tailwind_languages {
270        languages.register_lsp_adapter(
271            language.into(),
272            Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
273        );
274    }
275
276    let eslint_languages = ["TSX", "TypeScript", "JavaScript", "Vue.js", "Svelte"];
277    for language in eslint_languages {
278        languages.register_lsp_adapter(
279            language.into(),
280            Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
281        );
282    }
283
284    let mut subscription = languages.subscribe();
285    let mut prev_language_settings = languages.language_settings();
286
287    cx.spawn(|cx| async move {
288        while subscription.next().await.is_some() {
289            let language_settings = languages.language_settings();
290            if language_settings != prev_language_settings {
291                cx.update(|cx| {
292                    SettingsStore::update_global(cx, |settings, cx| {
293                        settings
294                            .set_extension_settings(language_settings.clone(), cx)
295                            .log_err();
296                    });
297                })?;
298                prev_language_settings = language_settings;
299            }
300        }
301        anyhow::Ok(())
302    })
303    .detach();
304}
305
306#[cfg(any(test, feature = "test-support"))]
307pub fn language(name: &str, grammar: tree_sitter::Language) -> Arc<Language> {
308    Arc::new(
309        Language::new(load_config(name), Some(grammar))
310            .with_queries(load_queries(name))
311            .unwrap(),
312    )
313}
314
315fn load_config(name: &str) -> LanguageConfig {
316    let config_toml = String::from_utf8(
317        LanguageDir::get(&format!("{}/config.toml", name))
318            .unwrap_or_else(|| panic!("missing config for language {:?}", name))
319            .data
320            .to_vec(),
321    )
322    .unwrap();
323
324    #[allow(unused_mut)]
325    let mut config: LanguageConfig = ::toml::from_str(&config_toml)
326        .with_context(|| format!("failed to load config.toml for language {name:?}"))
327        .unwrap();
328
329    #[cfg(not(any(feature = "load-grammars", test)))]
330    {
331        config = LanguageConfig {
332            name: config.name,
333            matcher: config.matcher,
334            ..Default::default()
335        }
336    }
337
338    config
339}
340
341fn load_queries(name: &str) -> LanguageQueries {
342    let mut result = LanguageQueries::default();
343    for path in LanguageDir::iter() {
344        if let Some(remainder) = path.strip_prefix(name).and_then(|p| p.strip_prefix('/')) {
345            if !remainder.ends_with(".scm") {
346                continue;
347            }
348            for (name, query) in QUERY_FILENAME_PREFIXES {
349                if remainder.starts_with(name) {
350                    let contents = asset_str::<LanguageDir>(path.as_ref());
351                    match query(&mut result) {
352                        None => *query(&mut result) = Some(contents),
353                        Some(r) => r.to_mut().push_str(contents.as_ref()),
354                    }
355                }
356            }
357        }
358    }
359    result
360}