lib.rs

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