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![
179            Arc::new(python::PythonLspAdapter::new(node_runtime.clone(),)),
180            Arc::new(python::PyLspAdapter::new())
181        ],
182        PythonContextProvider,
183        Arc::new(PythonToolchainProvider::default()) as Arc<dyn ToolchainLister>
184    );
185    language!(
186        "rust",
187        vec![Arc::new(rust::RustLspAdapter)],
188        RustContextProvider
189    );
190    language!(
191        "tsx",
192        vec![
193            Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
194            Arc::new(vtsls::VtslsLspAdapter::new(node_runtime.clone()))
195        ],
196        typescript_task_context()
197    );
198    language!(
199        "typescript",
200        vec![
201            Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
202            Arc::new(vtsls::VtslsLspAdapter::new(node_runtime.clone()))
203        ],
204        typescript_task_context()
205    );
206    language!(
207        "javascript",
208        vec![
209            Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
210            Arc::new(vtsls::VtslsLspAdapter::new(node_runtime.clone()))
211        ],
212        typescript_task_context()
213    );
214    language!(
215        "jsdoc",
216        vec![
217            Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone(),)),
218            Arc::new(vtsls::VtslsLspAdapter::new(node_runtime.clone()))
219        ]
220    );
221    language!("regex");
222    language!(
223        "yaml",
224        vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))]
225    );
226
227    // Register globally available language servers.
228    //
229    // This will allow users to add support for a built-in language server (e.g., Tailwind)
230    // for a given language via the `language_servers` setting:
231    //
232    // ```json
233    // {
234    //   "languages": {
235    //     "My Language": {
236    //       "language_servers": ["tailwindcss-language-server", "..."]
237    //     }
238    //   }
239    // }
240    // ```
241    languages.register_available_lsp_adapter(
242        LanguageServerName("tailwindcss-language-server".into()),
243        {
244            let node_runtime = node_runtime.clone();
245            move || Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone()))
246        },
247    );
248    languages.register_available_lsp_adapter(LanguageServerName("eslint".into()), {
249        let node_runtime = node_runtime.clone();
250        move || Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone()))
251    });
252
253    // Register Tailwind for the existing languages that should have it by default.
254    //
255    // This can be driven by the `language_servers` setting once we have a way for
256    // extensions to provide their own default value for that setting.
257    let tailwind_languages = [
258        "Astro",
259        "CSS",
260        "ERB",
261        "HEEX",
262        "HTML",
263        "JavaScript",
264        "PHP",
265        "Svelte",
266        "TSX",
267        "Vue.js",
268    ];
269
270    for language in tailwind_languages {
271        languages.register_lsp_adapter(
272            language.into(),
273            Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
274        );
275    }
276
277    let eslint_languages = ["TSX", "TypeScript", "JavaScript", "Vue.js", "Svelte"];
278    for language in eslint_languages {
279        languages.register_lsp_adapter(
280            language.into(),
281            Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
282        );
283    }
284
285    let mut subscription = languages.subscribe();
286    let mut prev_language_settings = languages.language_settings();
287
288    cx.spawn(|cx| async move {
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}
306
307#[cfg(any(test, feature = "test-support"))]
308pub fn language(name: &str, grammar: tree_sitter::Language) -> Arc<Language> {
309    Arc::new(
310        Language::new(load_config(name), Some(grammar))
311            .with_queries(load_queries(name))
312            .unwrap(),
313    )
314}
315
316fn load_config(name: &str) -> LanguageConfig {
317    let config_toml = String::from_utf8(
318        LanguageDir::get(&format!("{}/config.toml", name))
319            .unwrap_or_else(|| panic!("missing config for language {:?}", name))
320            .data
321            .to_vec(),
322    )
323    .unwrap();
324
325    #[allow(unused_mut)]
326    let mut config: LanguageConfig = ::toml::from_str(&config_toml)
327        .with_context(|| format!("failed to load config.toml for language {name:?}"))
328        .unwrap();
329
330    #[cfg(not(any(feature = "load-grammars", test)))]
331    {
332        config = LanguageConfig {
333            name: config.name,
334            matcher: config.matcher,
335            ..Default::default()
336        }
337    }
338
339    config
340}
341
342fn load_queries(name: &str) -> LanguageQueries {
343    let mut result = LanguageQueries::default();
344    for path in LanguageDir::iter() {
345        if let Some(remainder) = path.strip_prefix(name).and_then(|p| p.strip_prefix('/')) {
346            if !remainder.ends_with(".scm") {
347                continue;
348            }
349            for (name, query) in QUERY_FILENAME_PREFIXES {
350                if remainder.starts_with(name) {
351                    let contents = asset_str::<LanguageDir>(path.as_ref());
352                    match query(&mut result) {
353                        None => *query(&mut result) = Some(contents),
354                        Some(r) => r.to_mut().push_str(contents.as_ref()),
355                    }
356                }
357            }
358        }
359    }
360    result
361}