From 52567f4b721bed8106dffad04fbcfc8c89fec054 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 17 Mar 2025 08:13:32 -0700 Subject: [PATCH] Eliminate unnecessary macros in languages crate (#26813) I vibe coded this in Zed, dawg. This avoids a number of cases where we created multiple copies of the same lsp adapter. Now we clone the Arcs. Release Notes: - N/A --- crates/languages/src/lib.rs | 392 +++++++++++++++++------------------- 1 file changed, 187 insertions(+), 205 deletions(-) diff --git a/crates/languages/src/lib.rs b/crates/languages/src/lib.rs index 566d62969ffc84247316dbd075b6c9eb191744c8..5504650fc033bb027f52ecce6fe3987def13def2 100644 --- a/crates/languages/src/lib.rs +++ b/crates/languages/src/lib.rs @@ -1,17 +1,14 @@ use anyhow::Context as _; use gpui::{App, UpdateGlobal}; use json::json_task_context; -pub use language::*; use node_runtime::NodeRuntime; -use python::{PythonContextProvider, PythonToolchainProvider}; use rust_embed::RustEmbed; use settings::SettingsStore; use smol::stream::StreamExt; use std::{str, sync::Arc}; -use typescript::typescript_task_context; use util::{asset_str, ResultExt}; -use crate::{bash::bash_task_context, rust::RustContextProvider}; +pub use language::*; mod bash; mod c; @@ -49,7 +46,7 @@ pub static LANGUAGE_GIT_COMMIT: std::sync::LazyLock> = )) }); -pub fn init(languages: Arc, node_runtime: NodeRuntime, cx: &mut App) { +pub fn init(languages: Arc, node: NodeRuntime, cx: &mut App) { #[cfg(feature = "load-grammars")] languages.register_native_grammars([ ("bash", tree_sitter_bash::LANGUAGE), @@ -74,193 +71,149 @@ pub fn init(languages: Arc, node_runtime: NodeRuntime, cx: &mu ("gitcommit", tree_sitter_gitcommit::LANGUAGE), ]); - // Following are a series of helper macros for registering languages. - // Macros are used instead of a function or for loop in order to avoid - // code duplication and improve readability as the types get quite verbose - // to type out in some cases. - // Additionally, the `provider` fields in LoadedLanguage - // would have be `Copy` if we were to use a function or for-loop to register the languages - // due to the fact that we pass an `Arc` to `languages.register_language` - // that loads and initializes the language lazily. - // We avoid this entirely by using a Macro - - macro_rules! context_provider { - ($name:expr) => { - Some(Arc::new($name) as Arc) - }; - () => { - None - }; - } - - macro_rules! toolchain_provider { - ($name:expr) => { - Some(Arc::new($name) as Arc) - }; - () => { - None - }; - } - - macro_rules! adapters { - ($($item:expr),+ $(,)?) => { - vec![ - $(Arc::new($item) as Arc,)* - ] - }; - () => { - vec![] - }; - } + let c_lsp_adapter = Arc::new(c::CLspAdapter); + let css_lsp_adapter = Arc::new(css::CssLspAdapter::new(node.clone())); + let eslint_adapter = Arc::new(typescript::EsLintLspAdapter::new(node.clone())); + let go_context_provider = Arc::new(go::GoContextProvider); + let go_lsp_adapter = Arc::new(go::GoLspAdapter); + let json_context_provider = Arc::new(json_task_context()); + let json_lsp_adapter = Arc::new(json::JsonLspAdapter::new(node.clone(), languages.clone())); + let node_version_lsp_adapter = Arc::new(json::NodeVersionAdapter); + let py_lsp_adapter = Arc::new(python::PyLspAdapter::new()); + let python_context_provider = Arc::new(python::PythonContextProvider); + let python_lsp_adapter = Arc::new(python::PythonLspAdapter::new(node.clone())); + let python_toolchain_provider = Arc::new(python::PythonToolchainProvider::default()); + let rust_context_provider = Arc::new(rust::RustContextProvider); + let rust_lsp_adapter = Arc::new(rust::RustLspAdapter); + let tailwind_adapter = Arc::new(tailwind::TailwindLspAdapter::new(node.clone())); + let typescript_context = Arc::new(typescript::typescript_task_context()); + let typescript_lsp_adapter = Arc::new(typescript::TypeScriptLspAdapter::new(node.clone())); + let vtsls_adapter = Arc::new(vtsls::VtslsLspAdapter::new(node.clone())); + let yaml_lsp_adapter = Arc::new(yaml::YamlLspAdapter::new(node.clone())); + + let built_in_languages = [ + LanguageInfo { + name: "bash", + context: Some(Arc::new(bash::bash_task_context())), + ..Default::default() + }, + LanguageInfo { + name: "c", + adapters: vec![c_lsp_adapter.clone()], + ..Default::default() + }, + LanguageInfo { + name: "cpp", + adapters: vec![c_lsp_adapter.clone()], + ..Default::default() + }, + LanguageInfo { + name: "css", + adapters: vec![css_lsp_adapter.clone()], + ..Default::default() + }, + LanguageInfo { + name: "diff", + adapters: vec![], + ..Default::default() + }, + LanguageInfo { + name: "go", + adapters: vec![go_lsp_adapter.clone()], + context: Some(go_context_provider.clone()), + ..Default::default() + }, + LanguageInfo { + name: "gomod", + adapters: vec![go_lsp_adapter.clone()], + context: Some(go_context_provider.clone()), + ..Default::default() + }, + LanguageInfo { + name: "gowork", + adapters: vec![go_lsp_adapter.clone()], + context: Some(go_context_provider.clone()), + ..Default::default() + }, + LanguageInfo { + name: "json", + adapters: vec![json_lsp_adapter.clone(), node_version_lsp_adapter.clone()], + context: Some(json_context_provider.clone()), + ..Default::default() + }, + LanguageInfo { + name: "jsonc", + adapters: vec![json_lsp_adapter.clone()], + context: Some(json_context_provider.clone()), + ..Default::default() + }, + LanguageInfo { + name: "markdown", + adapters: vec![], + ..Default::default() + }, + LanguageInfo { + name: "markdown-inline", + adapters: vec![], + ..Default::default() + }, + LanguageInfo { + name: "python", + adapters: vec![python_lsp_adapter.clone(), py_lsp_adapter.clone()], + context: Some(python_context_provider), + toolchain: Some(python_toolchain_provider), + }, + LanguageInfo { + name: "rust", + adapters: vec![rust_lsp_adapter], + context: Some(rust_context_provider), + ..Default::default() + }, + LanguageInfo { + name: "tsx", + adapters: vec![typescript_lsp_adapter.clone(), vtsls_adapter.clone()], + context: Some(typescript_context.clone()), + ..Default::default() + }, + LanguageInfo { + name: "typescript", + adapters: vec![typescript_lsp_adapter.clone(), vtsls_adapter.clone()], + context: Some(typescript_context.clone()), + ..Default::default() + }, + LanguageInfo { + name: "javascript", + adapters: vec![typescript_lsp_adapter.clone(), vtsls_adapter.clone()], + context: Some(typescript_context.clone()), + ..Default::default() + }, + LanguageInfo { + name: "jsdoc", + adapters: vec![typescript_lsp_adapter.clone(), vtsls_adapter.clone()], + ..Default::default() + }, + LanguageInfo { + name: "regex", + adapters: vec![], + ..Default::default() + }, + LanguageInfo { + name: "yaml", + adapters: vec![yaml_lsp_adapter], + ..Default::default() + }, + ]; - macro_rules! register_language { - ($name:expr, adapters => $adapters:expr, context => $context:expr, toolchain => $toolchain:expr) => { - let config = load_config($name); - for adapter in $adapters { - languages.register_lsp_adapter(config.name.clone(), adapter); - } - languages.register_language( - config.name.clone(), - config.grammar.clone(), - config.matcher.clone(), - config.hidden, - Arc::new(move || { - Ok(LoadedLanguage { - config: config.clone(), - queries: load_queries($name), - context_provider: $context, - toolchain_provider: $toolchain, - }) - }), - ); - }; - ($name:expr) => { - register_language!($name, adapters => adapters![], context => context_provider!(), toolchain => toolchain_provider!()) - }; - ($name:expr, adapters => $adapters:expr, context => $context:expr, toolchain => $toolchain:expr) => { - register_language!($name, adapters => $adapters, context => $context, toolchain => $toolchain) - }; - ($name:expr, adapters => $adapters:expr, context => $context:expr) => { - register_language!($name, adapters => $adapters, context => $context, toolchain => toolchain_provider!()) - }; - ($name:expr, adapters => $adapters:expr) => { - register_language!($name, adapters => $adapters, context => context_provider!(), toolchain => toolchain_provider!()) - }; + for registration in built_in_languages { + register_language( + &languages, + registration.name, + registration.adapters, + registration.context, + registration.toolchain, + ); } - register_language!( - "bash", - adapters => adapters![], - context => context_provider!(bash_task_context()), - toolchain => toolchain_provider!() - ); - - register_language!( - "c", - adapters => adapters![c::CLspAdapter] - ); - register_language!( - "cpp", - adapters => adapters![c::CLspAdapter] - ); - - register_language!( - "css", - adapters => adapters![css::CssLspAdapter::new(node_runtime.clone())] - ); - - register_language!("diff"); - - register_language!( - "go", - adapters => adapters![go::GoLspAdapter], - context => context_provider!(go::GoContextProvider) - ); - register_language!( - "gomod", - adapters => adapters![go::GoLspAdapter], - context => context_provider!(go::GoContextProvider) - ); - register_language!( - "gowork", - adapters => adapters![go::GoLspAdapter], - context => context_provider!(go::GoContextProvider) - ); - - register_language!( - "json", - adapters => adapters![ - json::JsonLspAdapter::new(node_runtime.clone(), languages.clone(),), - json::NodeVersionAdapter, - ], - context => context_provider!(json_task_context()) - ); - register_language!( - "jsonc", - adapters => adapters![ - json::JsonLspAdapter::new(node_runtime.clone(), languages.clone(),), - ], - context => context_provider!(json_task_context()) - ); - - register_language!("markdown"); - register_language!("markdown-inline"); - - register_language!( - "python", - adapters => adapters![ - python::PythonLspAdapter::new(node_runtime.clone()), - python::PyLspAdapter::new() - ], - context => context_provider!(PythonContextProvider), - toolchain => toolchain_provider!(PythonToolchainProvider::default()) - ); - register_language!( - "rust", - adapters => adapters![rust::RustLspAdapter], - context => context_provider!(RustContextProvider) - ); - register_language!( - "tsx", - adapters => adapters![ - typescript::TypeScriptLspAdapter::new(node_runtime.clone()), - vtsls::VtslsLspAdapter::new(node_runtime.clone()), - ], - context => context_provider!(typescript_task_context()), - toolchain => toolchain_provider!() - ); - register_language!( - "typescript", - adapters => adapters![ - typescript::TypeScriptLspAdapter::new(node_runtime.clone()), - vtsls::VtslsLspAdapter::new(node_runtime.clone()), - ], - context => context_provider!(typescript_task_context()) - ); - register_language!( - "javascript", - adapters => adapters![ - typescript::TypeScriptLspAdapter::new(node_runtime.clone()), - vtsls::VtslsLspAdapter::new(node_runtime.clone()), - ], - context => context_provider!(typescript_task_context()) - ); - register_language!( - "jsdoc", - adapters => adapters![ - typescript::TypeScriptLspAdapter::new(node_runtime.clone()), - vtsls::VtslsLspAdapter::new(node_runtime.clone()), - ] - ); - - register_language!("regex"); - - register_language!("yaml", - adapters => adapters![ - yaml::YamlLspAdapter::new(node_runtime.clone()), - ] - ); - // Register globally available language servers. // // This will allow users to add support for a built-in language server (e.g., Tailwind) @@ -278,23 +231,23 @@ pub fn init(languages: Arc, node_runtime: NodeRuntime, cx: &mu languages.register_available_lsp_adapter( LanguageServerName("tailwindcss-language-server".into()), { - let node_runtime = node_runtime.clone(); - move || Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())) + let adapter = tailwind_adapter.clone(); + move || adapter.clone() }, ); languages.register_available_lsp_adapter(LanguageServerName("eslint".into()), { - let node_runtime = node_runtime.clone(); - move || Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())) + let adapter = eslint_adapter.clone(); + move || adapter.clone() }); languages.register_available_lsp_adapter(LanguageServerName("vtsls".into()), { - let node_runtime = node_runtime.clone(); - move || Arc::new(vtsls::VtslsLspAdapter::new(node_runtime.clone())) + let adapter = vtsls_adapter.clone(); + move || adapter.clone() }); languages.register_available_lsp_adapter( LanguageServerName("typescript-language-server".into()), { - let node_runtime = node_runtime.clone(); - move || Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())) + let adapter = typescript_lsp_adapter.clone(); + move || adapter.clone() }, ); @@ -316,18 +269,12 @@ pub fn init(languages: Arc, node_runtime: NodeRuntime, cx: &mu ]; for language in tailwind_languages { - languages.register_lsp_adapter( - language.into(), - Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), - ); + languages.register_lsp_adapter(language.into(), tailwind_adapter.clone()); } let eslint_languages = ["TSX", "TypeScript", "JavaScript", "Vue.js", "Svelte"]; for language in eslint_languages { - languages.register_lsp_adapter( - language.into(), - Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())), - ); + languages.register_lsp_adapter(language.into(), eslint_adapter.clone()); } let mut subscription = languages.subscribe(); @@ -352,6 +299,41 @@ pub fn init(languages: Arc, node_runtime: NodeRuntime, cx: &mu .detach(); } +#[derive(Default)] +struct LanguageInfo { + name: &'static str, + adapters: Vec>, + context: Option>, + toolchain: Option>, +} + +fn register_language( + languages: &LanguageRegistry, + name: &'static str, + adapters: Vec>, + context: Option>, + toolchain: Option>, +) { + let config = load_config(name); + for adapter in adapters { + languages.register_lsp_adapter(config.name.clone(), adapter); + } + languages.register_language( + config.name.clone(), + config.grammar.clone(), + config.matcher.clone(), + config.hidden, + Arc::new(move || { + Ok(LoadedLanguage { + config: config.clone(), + queries: load_queries(name), + context_provider: context.clone(), + toolchain_provider: toolchain.clone(), + }) + }), + ); +} + #[cfg(any(test, feature = "test-support"))] pub fn language(name: &str, grammar: tree_sitter::Language) -> Arc { Arc::new(