languages.rs

  1use anyhow::Context;
  2pub use language::*;
  3use node_runtime::NodeRuntime;
  4use rust_embed::RustEmbed;
  5use std::{borrow::Cow, str, sync::Arc};
  6use util::asset_str;
  7
  8mod c;
  9mod elixir;
 10mod go;
 11mod html;
 12mod json;
 13#[cfg(feature = "plugin_runtime")]
 14mod language_plugin;
 15mod lua;
 16mod php;
 17mod python;
 18mod ruby;
 19mod rust;
 20mod svelte;
 21mod typescript;
 22mod yaml;
 23
 24// 1. Add tree-sitter-{language} parser to zed crate
 25// 2. Create a language directory in zed/crates/zed/src/languages and add the language to init function below
 26// 3. Add config.toml to the newly created language directory using existing languages as a template
 27// 4. Copy highlights from tree sitter repo for the language into a highlights.scm file.
 28//      Note: github highlights take the last match while zed takes the first
 29// 5. Add indents.scm, outline.scm, and brackets.scm to implement indent on newline, outline/breadcrumbs,
 30//    and autoclosing brackets respectively
 31// 6. If the language has injections add an injections.scm query file
 32
 33#[derive(RustEmbed)]
 34#[folder = "src/languages"]
 35#[exclude = "*.rs"]
 36struct LanguageDir;
 37
 38pub fn init(languages: Arc<LanguageRegistry>, node_runtime: Arc<NodeRuntime>) {
 39    let language = |name, grammar, adapters| {
 40        languages.register(name, load_config(name), grammar, adapters, load_queries)
 41    };
 42
 43    language("bash", tree_sitter_bash::language(), vec![]);
 44    language(
 45        "c",
 46        tree_sitter_c::language(),
 47        vec![Arc::new(c::CLspAdapter) as Arc<dyn LspAdapter>],
 48    );
 49    language(
 50        "cpp",
 51        tree_sitter_cpp::language(),
 52        vec![Arc::new(c::CLspAdapter)],
 53    );
 54    language("css", tree_sitter_css::language(), vec![]);
 55    language(
 56        "elixir",
 57        tree_sitter_elixir::language(),
 58        vec![Arc::new(elixir::ElixirLspAdapter)],
 59    );
 60    language(
 61        "go",
 62        tree_sitter_go::language(),
 63        vec![Arc::new(go::GoLspAdapter)],
 64    );
 65    language(
 66        "heex",
 67        tree_sitter_heex::language(),
 68        vec![Arc::new(elixir::ElixirLspAdapter)],
 69    );
 70    language(
 71        "json",
 72        tree_sitter_json::language(),
 73        vec![Arc::new(json::JsonLspAdapter::new(
 74            node_runtime.clone(),
 75            languages.clone(),
 76        ))],
 77    );
 78    language("markdown", tree_sitter_markdown::language(), vec![]);
 79    language(
 80        "python",
 81        tree_sitter_python::language(),
 82        vec![Arc::new(python::PythonLspAdapter::new(
 83            node_runtime.clone(),
 84        ))],
 85    );
 86    language(
 87        "rust",
 88        tree_sitter_rust::language(),
 89        vec![Arc::new(rust::RustLspAdapter)],
 90    );
 91    language("toml", tree_sitter_toml::language(), vec![]);
 92    language(
 93        "tsx",
 94        tree_sitter_typescript::language_tsx(),
 95        vec![
 96            Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
 97            Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
 98        ],
 99    );
100    language(
101        "typescript",
102        tree_sitter_typescript::language_typescript(),
103        vec![
104            Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
105            Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
106        ],
107    );
108    language(
109        "javascript",
110        tree_sitter_typescript::language_tsx(),
111        vec![
112            Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
113            Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
114        ],
115    );
116    language(
117        "html",
118        tree_sitter_html::language(),
119        vec![Arc::new(html::HtmlLspAdapter::new(node_runtime.clone()))],
120    );
121    language(
122        "ruby",
123        tree_sitter_ruby::language(),
124        vec![Arc::new(ruby::RubyLanguageServer)],
125    );
126    language(
127        "erb",
128        tree_sitter_embedded_template::language(),
129        vec![Arc::new(ruby::RubyLanguageServer)],
130    );
131    language("scheme", tree_sitter_scheme::language(), vec![]);
132    language("racket", tree_sitter_racket::language(), vec![]);
133    language(
134        "lua",
135        tree_sitter_lua::language(),
136        vec![Arc::new(lua::LuaLspAdapter)],
137    );
138    language(
139        "yaml",
140        tree_sitter_yaml::language(),
141        vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))],
142    );
143    language(
144        "svelte",
145        tree_sitter_svelte::language(),
146        vec![Arc::new(svelte::SvelteLspAdapter::new(
147            node_runtime.clone(),
148        ))],
149    );
150    language(
151        "php",
152        tree_sitter_php::language(),
153        vec![Arc::new(php::IntelephenseLspAdapter::new(node_runtime))],
154    );
155    language("elm", tree_sitter_elm::language(), vec![]);
156    language("glsl", tree_sitter_glsl::language(), vec![]);
157}
158
159#[cfg(any(test, feature = "test-support"))]
160pub async fn language(
161    name: &str,
162    grammar: tree_sitter::Language,
163    lsp_adapter: Option<Arc<dyn LspAdapter>>,
164) -> Arc<Language> {
165    Arc::new(
166        Language::new(load_config(name), Some(grammar))
167            .with_lsp_adapters(lsp_adapter.into_iter().collect())
168            .await
169            .with_queries(load_queries(name))
170            .unwrap(),
171    )
172}
173
174fn load_config(name: &str) -> LanguageConfig {
175    toml::from_slice(
176        &LanguageDir::get(&format!("{}/config.toml", name))
177            .unwrap()
178            .data,
179    )
180    .with_context(|| format!("failed to load config.toml for language {name:?}"))
181    .unwrap()
182}
183
184fn load_queries(name: &str) -> LanguageQueries {
185    LanguageQueries {
186        highlights: load_query(name, "/highlights"),
187        brackets: load_query(name, "/brackets"),
188        indents: load_query(name, "/indents"),
189        outline: load_query(name, "/outline"),
190        embedding: load_query(name, "/embedding"),
191        injections: load_query(name, "/injections"),
192        overrides: load_query(name, "/overrides"),
193    }
194}
195
196fn load_query(name: &str, filename_prefix: &str) -> Option<Cow<'static, str>> {
197    let mut result = None;
198    for path in LanguageDir::iter() {
199        if let Some(remainder) = path.strip_prefix(name) {
200            if remainder.starts_with(filename_prefix) {
201                let contents = asset_str::<LanguageDir>(path.as_ref());
202                match &mut result {
203                    None => result = Some(contents),
204                    Some(r) => r.to_mut().push_str(contents.as_ref()),
205                }
206            }
207        }
208    }
209    result
210}