lib.rs

  1use anyhow::Context;
  2use gpui::{AppContext, BorrowAppContext};
  3pub use language::*;
  4use node_runtime::NodeRuntime;
  5use rust_embed::RustEmbed;
  6use settings::{Settings, SettingsStore};
  7use smol::stream::StreamExt;
  8use std::{str, sync::Arc};
  9use util::{asset_str, ResultExt};
 10
 11use crate::{elixir::elixir_task_context, rust::RustContextProvider};
 12
 13use self::{deno::DenoSettings, elixir::ElixirSettings};
 14
 15mod c;
 16mod css;
 17mod deno;
 18mod elixir;
 19mod go;
 20mod json;
 21mod lua;
 22mod nu;
 23mod ocaml;
 24mod python;
 25mod ruby;
 26mod rust;
 27mod tailwind;
 28mod terraform;
 29mod typescript;
 30mod vue;
 31mod yaml;
 32
 33// 1. Add tree-sitter-{language} parser to zed crate
 34// 2. Create a language directory in zed/crates/zed/src/languages and add the language to init function below
 35// 3. Add config.toml to the newly created language directory using existing languages as a template
 36// 4. Copy highlights from tree sitter repo for the language into a highlights.scm file.
 37//      Note: github highlights take the last match while zed takes the first
 38// 5. Add indents.scm, outline.scm, and brackets.scm to implement indent on newline, outline/breadcrumbs,
 39//    and autoclosing brackets respectively
 40// 6. If the language has injections add an injections.scm query file
 41
 42#[derive(RustEmbed)]
 43#[folder = "src/"]
 44#[exclude = "*.rs"]
 45struct LanguageDir;
 46
 47pub fn init(
 48    languages: Arc<LanguageRegistry>,
 49    node_runtime: Arc<dyn NodeRuntime>,
 50    cx: &mut AppContext,
 51) {
 52    ElixirSettings::register(cx);
 53    DenoSettings::register(cx);
 54
 55    languages.register_native_grammars([
 56        ("bash", tree_sitter_bash::language()),
 57        ("c", tree_sitter_c::language()),
 58        ("cpp", tree_sitter_cpp::language()),
 59        ("css", tree_sitter_css::language()),
 60        ("elixir", tree_sitter_elixir::language()),
 61        (
 62            "embedded_template",
 63            tree_sitter_embedded_template::language(),
 64        ),
 65        ("glsl", tree_sitter_glsl::language()),
 66        ("go", tree_sitter_go::language()),
 67        ("gomod", tree_sitter_gomod::language()),
 68        ("gowork", tree_sitter_gowork::language()),
 69        ("hcl", tree_sitter_hcl::language()),
 70        ("heex", tree_sitter_heex::language()),
 71        ("jsdoc", tree_sitter_jsdoc::language()),
 72        ("json", tree_sitter_json::language()),
 73        ("lua", tree_sitter_lua::language()),
 74        ("markdown", tree_sitter_markdown::language()),
 75        ("nix", tree_sitter_nix::language()),
 76        ("nu", tree_sitter_nu::language()),
 77        ("ocaml", tree_sitter_ocaml::language_ocaml()),
 78        (
 79            "ocaml_interface",
 80            tree_sitter_ocaml::language_ocaml_interface(),
 81        ),
 82        ("proto", tree_sitter_proto::language()),
 83        ("python", tree_sitter_python::language()),
 84        ("racket", tree_sitter_racket::language()),
 85        ("regex", tree_sitter_regex::language()),
 86        ("ruby", tree_sitter_ruby::language()),
 87        ("rust", tree_sitter_rust::language()),
 88        ("scheme", tree_sitter_scheme::language()),
 89        ("tsx", tree_sitter_typescript::language_tsx()),
 90        ("typescript", tree_sitter_typescript::language_typescript()),
 91        ("vue", tree_sitter_vue::language()),
 92        ("yaml", tree_sitter_yaml::language()),
 93    ]);
 94
 95    macro_rules! language {
 96        ($name:literal) => {
 97            let config = load_config($name);
 98            languages.register_language(
 99                config.name.clone(),
100                config.grammar.clone(),
101                config.matcher.clone(),
102                move || {
103                    Ok((
104                        config.clone(),
105                        load_queries($name),
106                        Some(Arc::new(language::SymbolContextProvider)),
107                    ))
108                },
109            );
110        };
111        ($name:literal, $adapters:expr) => {
112            let config = load_config($name);
113            // typeck helper
114            let adapters: Vec<Arc<dyn LspAdapter>> = $adapters;
115            for adapter in adapters {
116                languages.register_lsp_adapter(config.name.clone(), adapter);
117            }
118            languages.register_language(
119                config.name.clone(),
120                config.grammar.clone(),
121                config.matcher.clone(),
122                move || {
123                    Ok((
124                        config.clone(),
125                        load_queries($name),
126                        Some(Arc::new(language::SymbolContextProvider)),
127                    ))
128                },
129            );
130        };
131        ($name:literal, $adapters:expr, $context_provider:expr) => {
132            let config = load_config($name);
133            // typeck helper
134            let adapters: Vec<Arc<dyn LspAdapter>> = $adapters;
135            for adapter in adapters {
136                languages.register_lsp_adapter(config.name.clone(), adapter);
137            }
138            languages.register_language(
139                config.name.clone(),
140                config.grammar.clone(),
141                config.matcher.clone(),
142                move || {
143                    Ok((
144                        config.clone(),
145                        load_queries($name),
146                        Some(Arc::new($context_provider)),
147                    ))
148                },
149            );
150        };
151    }
152    language!("bash");
153    language!("c", vec![Arc::new(c::CLspAdapter) as Arc<dyn LspAdapter>]);
154    language!("cpp", vec![Arc::new(c::CLspAdapter)]);
155    language!(
156        "css",
157        vec![
158            Arc::new(css::CssLspAdapter::new(node_runtime.clone())),
159            Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
160        ]
161    );
162
163    match &ElixirSettings::get(None, cx).lsp {
164        elixir::ElixirLspSetting::ElixirLs => {
165            language!(
166                "elixir",
167                vec![
168                    Arc::new(elixir::ElixirLspAdapter),
169                    Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
170                ],
171                elixir_task_context()
172            );
173        }
174        elixir::ElixirLspSetting::NextLs => {
175            language!(
176                "elixir",
177                vec![Arc::new(elixir::NextLspAdapter)],
178                elixir_task_context()
179            );
180        }
181        elixir::ElixirLspSetting::Local { path, arguments } => {
182            language!(
183                "elixir",
184                vec![Arc::new(elixir::LocalLspAdapter {
185                    path: path.clone(),
186                    arguments: arguments.clone(),
187                })],
188                elixir_task_context()
189            );
190        }
191    }
192    language!("go", vec![Arc::new(go::GoLspAdapter)]);
193    language!("gomod");
194    language!("gowork");
195    language!(
196        "heex",
197        vec![
198            Arc::new(elixir::ElixirLspAdapter),
199            Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
200        ]
201    );
202    language!(
203        "json",
204        vec![Arc::new(json::JsonLspAdapter::new(
205            node_runtime.clone(),
206            languages.clone(),
207        ))]
208    );
209    language!("markdown");
210    language!(
211        "python",
212        vec![Arc::new(python::PythonLspAdapter::new(
213            node_runtime.clone(),
214        ))]
215    );
216    language!(
217        "rust",
218        vec![Arc::new(rust::RustLspAdapter)],
219        RustContextProvider
220    );
221    match &DenoSettings::get(None, cx).enable {
222        true => {
223            language!(
224                "tsx",
225                vec![
226                    Arc::new(deno::DenoLspAdapter::new()),
227                    Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
228                ]
229            );
230            language!("typescript", vec![Arc::new(deno::DenoLspAdapter::new())]);
231            language!(
232                "javascript",
233                vec![
234                    Arc::new(deno::DenoLspAdapter::new()),
235                    Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
236                ]
237            );
238            language!("jsdoc", vec![Arc::new(deno::DenoLspAdapter::new())]);
239        }
240        false => {
241            language!(
242                "tsx",
243                vec![
244                    Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
245                    Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
246                    Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
247                ]
248            );
249            language!(
250                "typescript",
251                vec![
252                    Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
253                    Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
254                ]
255            );
256            language!(
257                "javascript",
258                vec![
259                    Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
260                    Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
261                    Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
262                ]
263            );
264            language!(
265                "jsdoc",
266                vec![Arc::new(typescript::TypeScriptLspAdapter::new(
267                    node_runtime.clone(),
268                ))]
269            );
270        }
271    }
272
273    language!("ruby", vec![Arc::new(ruby::RubyLanguageServer)]);
274    language!(
275        "erb",
276        vec![
277            Arc::new(ruby::RubyLanguageServer),
278            Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
279        ]
280    );
281    language!("scheme");
282    language!("racket");
283    language!("regex");
284    language!("lua", vec![Arc::new(lua::LuaLspAdapter)]);
285    language!(
286        "yaml",
287        vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))]
288    );
289    language!("glsl");
290    language!("nix");
291    language!("nu", vec![Arc::new(nu::NuLanguageServer {})]);
292    language!("ocaml", vec![Arc::new(ocaml::OCamlLspAdapter)]);
293    language!("ocaml-interface", vec![Arc::new(ocaml::OCamlLspAdapter)]);
294    language!(
295        "vue",
296        vec![
297            Arc::new(vue::VueLspAdapter::new(node_runtime.clone())),
298            Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
299        ]
300    );
301    language!("proto");
302    language!("terraform", vec![Arc::new(terraform::TerraformLspAdapter)]);
303    language!(
304        "terraform-vars",
305        vec![Arc::new(terraform::TerraformLspAdapter)]
306    );
307    language!("hcl", vec![]);
308
309    languages.register_secondary_lsp_adapter(
310        "Astro".into(),
311        Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
312    );
313    languages.register_secondary_lsp_adapter(
314        "HTML".into(),
315        Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
316    );
317    languages.register_secondary_lsp_adapter(
318        "PHP".into(),
319        Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
320    );
321    languages.register_secondary_lsp_adapter(
322        "Svelte".into(),
323        Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
324    );
325
326    let mut subscription = languages.subscribe();
327    let mut prev_language_settings = languages.language_settings();
328
329    cx.spawn(|cx| async move {
330        while subscription.next().await.is_some() {
331            let language_settings = languages.language_settings();
332            if language_settings != prev_language_settings {
333                cx.update(|cx| {
334                    cx.update_global(|settings: &mut SettingsStore, cx| {
335                        settings
336                            .set_extension_settings(language_settings.clone(), cx)
337                            .log_err();
338                    });
339                })?;
340                prev_language_settings = language_settings;
341            }
342        }
343        anyhow::Ok(())
344    })
345    .detach();
346}
347
348#[cfg(any(test, feature = "test-support"))]
349pub fn language(name: &str, grammar: tree_sitter::Language) -> Arc<Language> {
350    Arc::new(
351        Language::new(load_config(name), Some(grammar))
352            .with_queries(load_queries(name))
353            .unwrap(),
354    )
355}
356
357fn load_config(name: &str) -> LanguageConfig {
358    let config_toml = String::from_utf8(
359        LanguageDir::get(&format!("{}/config.toml", name))
360            .unwrap()
361            .data
362            .to_vec(),
363    )
364    .unwrap();
365
366    ::toml::from_str(&config_toml)
367        .with_context(|| format!("failed to load config.toml for language {name:?}"))
368        .unwrap()
369}
370
371fn load_queries(name: &str) -> LanguageQueries {
372    let mut result = LanguageQueries::default();
373    for path in LanguageDir::iter() {
374        if let Some(remainder) = path.strip_prefix(name).and_then(|p| p.strip_prefix('/')) {
375            if !remainder.ends_with(".scm") {
376                continue;
377            }
378            for (name, query) in QUERY_FILENAME_PREFIXES {
379                if remainder.starts_with(name) {
380                    let contents = asset_str::<LanguageDir>(path.as_ref());
381                    match query(&mut result) {
382                        None => *query(&mut result) = Some(contents),
383                        Some(r) => r.to_mut().push_str(contents.as_ref()),
384                    }
385                }
386            }
387        }
388    }
389    result
390}