diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index e1e60a1ecfe4cdd960547ccde7d246b8dedcf20f..84311f9d790bcc401bbe4265bec4ff06587dd1b4 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -44,7 +44,7 @@ use syntax_map::SyntaxSnapshot; use theme::{SyntaxTheme, Theme}; use tree_sitter::{self, Query}; use unicase::UniCase; -use util::{ResultExt, TryFutureExt as _, UnwrapFuture}; +use util::{merge_json_value_into, post_inc, ResultExt, TryFutureExt as _, UnwrapFuture}; #[cfg(any(test, feature = "test-support"))] use futures::channel::mpsc; @@ -87,11 +87,11 @@ pub struct CachedLspAdapter { pub disk_based_diagnostic_sources: Vec, pub disk_based_diagnostics_progress_token: Option, pub language_ids: HashMap, - pub adapter: Box, + pub adapter: Arc, } impl CachedLspAdapter { - pub async fn new(adapter: Box) -> Arc { + pub async fn new(adapter: Arc) -> Arc { let name = adapter.name().await; let server_args = adapter.server_args().await; let initialization_options = adapter.initialization_options().await; @@ -133,6 +133,13 @@ impl CachedLspAdapter { self.adapter.cached_server_binary(container_dir).await } + pub fn workspace_configuration( + &self, + cx: &mut MutableAppContext, + ) -> Option> { + self.adapter.workspace_configuration(cx) + } + pub async fn process_diagnostics(&self, params: &mut lsp::PublishDiagnosticsParams) { self.adapter.process_diagnostics(params).await } @@ -208,6 +215,13 @@ pub trait LspAdapter: 'static + Send + Sync { None } + fn workspace_configuration( + &self, + _: &mut MutableAppContext, + ) -> Option> { + None + } + async fn disk_based_diagnostic_sources(&self) -> Vec { Default::default() } @@ -228,7 +242,7 @@ pub struct CodeLabel { pub filter_range: Range, } -#[derive(Deserialize)] +#[derive(Clone, Deserialize)] pub struct LanguageConfig { pub name: Arc, pub path_suffixes: Vec, @@ -265,7 +279,7 @@ pub struct LanguageScope { override_id: Option, } -#[derive(Deserialize, Default, Debug)] +#[derive(Clone, Deserialize, Default, Debug)] pub struct LanguageConfigOverride { #[serde(default)] pub line_comment: Override>, @@ -275,7 +289,7 @@ pub struct LanguageConfigOverride { pub disabled_bracket_ixs: Vec, } -#[derive(Deserialize, Debug)] +#[derive(Clone, Deserialize, Debug)] #[serde(untagged)] pub enum Override { Remove { remove: bool }, @@ -452,17 +466,20 @@ pub enum LanguageServerBinaryStatus { Failed { error: String }, } +type AvailableLanguageId = usize; + +#[derive(Clone)] struct AvailableLanguage { + id: AvailableLanguageId, path: &'static str, config: LanguageConfig, grammar: tree_sitter::Language, - lsp_adapter: Option>, + lsp_adapter: Option>, get_queries: fn(&str) -> LanguageQueries, } pub struct LanguageRegistry { - languages: RwLock>>, - available_languages: RwLock>, + state: RwLock, language_server_download_dir: Option>, lsp_binary_statuses_tx: async_broadcast::Sender<(Arc, LanguageServerBinaryStatus)>, lsp_binary_statuses_rx: async_broadcast::Receiver<(Arc, LanguageServerBinaryStatus)>, @@ -474,26 +491,37 @@ pub struct LanguageRegistry { Shared>>>, >, >, - subscription: RwLock<(watch::Sender<()>, watch::Receiver<()>)>, - theme: RwLock>>, executor: Option>, - version: AtomicUsize, +} + +struct LanguageRegistryState { + languages: Vec>, + available_languages: Vec, + next_available_language_id: AvailableLanguageId, + loading_languages: HashMap>>>>, + subscription: (watch::Sender<()>, watch::Receiver<()>), + theme: Option>, + version: usize, } impl LanguageRegistry { pub fn new(login_shell_env_loaded: Task<()>) -> Self { let (lsp_binary_statuses_tx, lsp_binary_statuses_rx) = async_broadcast::broadcast(16); Self { + state: RwLock::new(LanguageRegistryState { + languages: vec![PLAIN_TEXT.clone()], + available_languages: Default::default(), + next_available_language_id: 0, + loading_languages: Default::default(), + subscription: watch::channel(), + theme: Default::default(), + version: 0, + }), language_server_download_dir: None, - languages: RwLock::new(vec![PLAIN_TEXT.clone()]), - available_languages: Default::default(), lsp_binary_statuses_tx, lsp_binary_statuses_rx, login_shell_env_loaded: login_shell_env_loaded.shared(), lsp_binary_paths: Default::default(), - subscription: RwLock::new(watch::channel()), - theme: Default::default(), - version: Default::default(), executor: None, } } @@ -512,10 +540,12 @@ impl LanguageRegistry { path: &'static str, config: LanguageConfig, grammar: tree_sitter::Language, - lsp_adapter: Option>, + lsp_adapter: Option>, get_queries: fn(&str) -> LanguageQueries, ) { - self.available_languages.write().push(AvailableLanguage { + let state = &mut *self.state.write(); + state.available_languages.push(AvailableLanguage { + id: post_inc(&mut state.next_available_language_id), path, config, grammar, @@ -525,42 +555,61 @@ impl LanguageRegistry { } pub fn language_names(&self) -> Vec { - let mut result = self + let state = self.state.read(); + let mut result = state .available_languages - .read() .iter() .map(|l| l.config.name.to_string()) - .chain( - self.languages - .read() - .iter() - .map(|l| l.config.name.to_string()), - ) + .chain(state.languages.iter().map(|l| l.config.name.to_string())) .collect::>(); result.sort_unstable_by_key(|language_name| language_name.to_lowercase()); result } - pub fn add(&self, language: Arc) { - if let Some(theme) = self.theme.read().clone() { - language.set_theme(&theme.editor.syntax); + pub fn workspace_configuration(&self, cx: &mut MutableAppContext) -> Task { + let state = self.state.read(); + let mut language_configs = Vec::new(); + for language in &state.available_languages { + if let Some(adapter) = language.lsp_adapter.as_ref() { + if let Some(language_config) = adapter.workspace_configuration(cx) { + language_configs.push(language_config); + } + } + } + for language in &state.languages { + if let Some(adapter) = language.lsp_adapter() { + if let Some(language_config) = adapter.workspace_configuration(cx) { + language_configs.push(language_config); + } + } } - self.languages.write().push(language); - self.version.fetch_add(1, SeqCst); - *self.subscription.write().0.borrow_mut() = (); + + cx.background().spawn(async move { + let mut config = serde_json::json!({}); + let language_configs = futures::future::join_all(language_configs).await; + for language_config in language_configs { + merge_json_value_into(language_config, &mut config); + } + config + }) + } + + pub fn add(&self, language: Arc) { + self.state.write().add(language); } pub fn subscribe(&self) -> watch::Receiver<()> { - self.subscription.read().1.clone() + self.state.read().subscription.1.clone() } pub fn version(&self) -> usize { - self.version.load(SeqCst) + self.state.read().version } pub fn set_theme(&self, theme: Arc) { - *self.theme.write() = Some(theme.clone()); - for language in self.languages.read().iter() { + let mut state = self.state.write(); + state.theme = Some(theme.clone()); + for language in &state.languages { language.set_theme(&theme.editor.syntax); } } @@ -613,43 +662,70 @@ impl LanguageRegistry { ) -> UnwrapFuture>>> { let (tx, rx) = oneshot::channel(); - if let Some(language) = self + let mut state = self.state.write(); + if let Some(language) = state .languages - .read() .iter() .find(|language| callback(&language.config)) { let _ = tx.send(Ok(language.clone())); } else if let Some(executor) = self.executor.clone() { - let mut available_languages = self.available_languages.write(); - - if let Some(ix) = available_languages.iter().position(|l| callback(&l.config)) { - let language = available_languages.remove(ix); - drop(available_languages); - let name = language.config.name.clone(); - let this = self.clone(); - executor - .spawn(async move { - let queries = (language.get_queries)(&language.path); - let language = Language::new(language.config, Some(language.grammar)) - .with_lsp_adapter(language.lsp_adapter) - .await; - match language.with_queries(queries) { - Ok(language) => { - let language = Arc::new(language); - this.add(language.clone()); - let _ = tx.send(Ok(language)); - } - Err(err) => { - let _ = tx.send(Err(anyhow!( - "failed to load language {}: {}", - name, - err - ))); - } - }; - }) - .detach(); + if let Some(language) = state + .available_languages + .iter() + .find(|l| callback(&l.config)) + .cloned() + { + let txs = state + .loading_languages + .entry(language.id) + .or_insert_with(|| { + let this = self.clone(); + executor + .spawn(async move { + let id = language.id; + let queries = (language.get_queries)(&language.path); + let language = + Language::new(language.config, Some(language.grammar)) + .with_lsp_adapter(language.lsp_adapter) + .await; + let name = language.name(); + match language.with_queries(queries) { + Ok(language) => { + let language = Arc::new(language); + let mut state = this.state.write(); + state.add(language.clone()); + state + .available_languages + .retain(|language| language.id != id); + if let Some(mut txs) = state.loading_languages.remove(&id) { + for tx in txs.drain(..) { + let _ = tx.send(Ok(language.clone())); + } + } + } + Err(err) => { + let mut state = this.state.write(); + state + .available_languages + .retain(|language| language.id != id); + if let Some(mut txs) = state.loading_languages.remove(&id) { + for tx in txs.drain(..) { + let _ = tx.send(Err(anyhow!( + "failed to load language {}: {}", + name, + err + ))); + } + } + } + }; + }) + .detach(); + + Vec::new() + }); + txs.push(tx); } else { let _ = tx.send(Err(anyhow!("language not found"))); } @@ -661,7 +737,7 @@ impl LanguageRegistry { } pub fn to_vec(&self) -> Vec> { - self.languages.read().iter().cloned().collect() + self.state.read().languages.iter().cloned().collect() } pub fn start_language_server( @@ -754,6 +830,17 @@ impl LanguageRegistry { } } +impl LanguageRegistryState { + fn add(&mut self, language: Arc) { + if let Some(theme) = self.theme.as_ref() { + language.set_theme(&theme.editor.syntax); + } + self.languages.push(language); + self.version += 1; + *self.subscription.0.borrow_mut() = (); + } +} + #[cfg(any(test, feature = "test-support"))] impl Default for LanguageRegistry { fn default() -> Self { @@ -1085,7 +1172,7 @@ impl Language { Arc::get_mut(self.grammar.as_mut().unwrap()).unwrap() } - pub async fn with_lsp_adapter(mut self, lsp_adapter: Option>) -> Self { + pub async fn with_lsp_adapter(mut self, lsp_adapter: Option>) -> Self { if let Some(adapter) = lsp_adapter { self.adapter = Some(CachedLspAdapter::new(adapter).await); } @@ -1099,7 +1186,7 @@ impl Language { ) -> mpsc::UnboundedReceiver { let (servers_tx, servers_rx) = mpsc::unbounded(); self.fake_adapter = Some((servers_tx, fake_lsp_adapter.clone())); - let adapter = CachedLspAdapter::new(Box::new(fake_lsp_adapter)).await; + let adapter = CachedLspAdapter::new(Arc::new(fake_lsp_adapter)).await; self.adapter = Some(adapter); servers_rx } @@ -1415,3 +1502,76 @@ pub fn range_from_lsp(range: lsp::Range) -> Range> { } start..end } + +#[cfg(test)] +mod tests { + use gpui::TestAppContext; + + use super::*; + + #[gpui::test(iterations = 10)] + async fn test_language_loading(cx: &mut TestAppContext) { + let mut languages = LanguageRegistry::new(Task::ready(())); + languages.set_executor(cx.background()); + let languages = Arc::new(languages); + languages.register( + "/JSON", + LanguageConfig { + name: "JSON".into(), + path_suffixes: vec!["json".into()], + ..Default::default() + }, + tree_sitter_json::language(), + None, + |_| Default::default(), + ); + languages.register( + "/rust", + LanguageConfig { + name: "Rust".into(), + path_suffixes: vec!["rs".into()], + ..Default::default() + }, + tree_sitter_rust::language(), + None, + |_| Default::default(), + ); + assert_eq!( + languages.language_names(), + &[ + "JSON".to_string(), + "Plain Text".to_string(), + "Rust".to_string(), + ] + ); + + let rust1 = languages.language_for_name("Rust"); + let rust2 = languages.language_for_name("Rust"); + + // Ensure language is still listed even if it's being loaded. + assert_eq!( + languages.language_names(), + &[ + "JSON".to_string(), + "Plain Text".to_string(), + "Rust".to_string(), + ] + ); + + let (rust1, rust2) = futures::join!(rust1, rust2); + assert!(Arc::ptr_eq(&rust1.unwrap(), &rust2.unwrap())); + + // Ensure language is still listed even after loading it. + assert_eq!( + languages.language_names(), + &[ + "JSON".to_string(), + "Plain Text".to_string(), + "Rust".to_string(), + ] + ); + + // Loading an unknown language returns an error. + assert!(languages.language_for_name("Unknown").await.is_err()); + } +} diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index fb4e6b5b803e41a5fc8e713a2800cd2e82602c90..5b2fd412e8a272651c54722adc518a791c6cafc5 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -37,7 +37,6 @@ use lsp::{ MarkedString, }; use lsp_command::*; -use parking_lot::Mutex; use postage::watch; use rand::prelude::*; use search::SearchQuery; @@ -64,7 +63,7 @@ use std::{ }; use terminals::Terminals; -use util::{debug_panic, defer, post_inc, ResultExt, TryFutureExt as _}; +use util::{debug_panic, defer, merge_json_value_into, post_inc, ResultExt, TryFutureExt as _}; pub use fs::*; pub use worktree::*; @@ -95,7 +94,6 @@ pub struct Project { language_servers: HashMap, language_server_ids: HashMap<(WorktreeId, LanguageServerName), usize>, language_server_statuses: BTreeMap, - language_server_settings: Arc>, last_workspace_edits_by_language_server: HashMap, next_language_server_id: usize, client: Arc, @@ -125,6 +123,7 @@ pub struct Project { buffers_being_formatted: HashSet, nonce: u128, _maintain_buffer_languages: Task<()>, + _maintain_workspace_config: Task<()>, terminals: Terminals, } @@ -428,6 +427,7 @@ impl Project { client_subscriptions: Vec::new(), _subscriptions: vec![cx.observe_global::(Self::on_settings_changed)], _maintain_buffer_languages: Self::maintain_buffer_languages(&languages, cx), + _maintain_workspace_config: Self::maintain_workspace_config(languages.clone(), cx), active_entry: None, languages, client, @@ -439,7 +439,6 @@ impl Project { language_server_ids: Default::default(), language_server_statuses: Default::default(), last_workspace_edits_by_language_server: Default::default(), - language_server_settings: Default::default(), buffers_being_formatted: Default::default(), next_language_server_id: 0, nonce: StdRng::from_entropy().gen(), @@ -486,6 +485,7 @@ impl Project { active_entry: None, collaborators: Default::default(), _maintain_buffer_languages: Self::maintain_buffer_languages(&languages, cx), + _maintain_workspace_config: Self::maintain_workspace_config(languages.clone(), cx), languages, user_store: user_store.clone(), fs, @@ -501,7 +501,6 @@ impl Project { }), language_servers: Default::default(), language_server_ids: Default::default(), - language_server_settings: Default::default(), language_server_statuses: response .language_servers .into_iter() @@ -1836,6 +1835,42 @@ impl Project { }) } + fn maintain_workspace_config( + languages: Arc, + cx: &mut ModelContext, + ) -> Task<()> { + let (mut settings_changed_tx, mut settings_changed_rx) = watch::channel(); + let _ = postage::stream::Stream::try_recv(&mut settings_changed_rx); + + let settings_observation = cx.observe_global::(move |_, _| { + *settings_changed_tx.borrow_mut() = (); + }); + cx.spawn_weak(|this, mut cx| async move { + while let Some(_) = settings_changed_rx.next().await { + let workspace_config = cx.update(|cx| languages.workspace_configuration(cx)).await; + if let Some(this) = this.upgrade(&cx) { + this.read_with(&cx, |this, _| { + for server_state in this.language_servers.values() { + if let LanguageServerState::Running { server, .. } = server_state { + server + .notify::( + lsp::DidChangeConfigurationParams { + settings: workspace_config.clone(), + }, + ) + .ok(); + } + } + }) + } else { + break; + } + } + + drop(settings_observation); + }) + } + fn detect_language_for_buffer( &mut self, buffer: &ModelHandle, @@ -1875,24 +1910,6 @@ impl Project { } } - fn merge_json_value_into(source: serde_json::Value, target: &mut serde_json::Value) { - use serde_json::Value; - - match (source, target) { - (Value::Object(source), Value::Object(target)) => { - for (key, value) in source { - if let Some(target) = target.get_mut(&key) { - Self::merge_json_value_into(value, target); - } else { - target.insert(key.clone(), value); - } - } - } - - (source, target) => *target = source, - } - } - fn start_language_server( &mut self, worktree_id: WorktreeId, @@ -1920,17 +1937,16 @@ impl Project { let override_options = lsp.map(|s| s.initialization_options.clone()).flatten(); match (&mut initialization_options, override_options) { (Some(initialization_options), Some(override_options)) => { - Self::merge_json_value_into(override_options, initialization_options); + merge_json_value_into(override_options, initialization_options); } - (None, override_options) => initialization_options = override_options, - _ => {} } self.language_server_ids .entry(key.clone()) .or_insert_with(|| { + let languages = self.languages.clone(); let server_id = post_inc(&mut self.next_language_server_id); let language_server = self.languages.start_language_server( server_id, @@ -1942,6 +1958,8 @@ impl Project { self.language_servers.insert( server_id, LanguageServerState::Starting(cx.spawn_weak(|this, mut cx| async move { + let workspace_config = + cx.update(|cx| languages.workspace_configuration(cx)).await; let language_server = language_server?.await.log_err()?; let language_server = language_server .initialize(initialization_options) @@ -1977,23 +1995,24 @@ impl Project { language_server .on_request::({ - let settings = this.read_with(&cx, |this, _| { - this.language_server_settings.clone() - }); - move |params, _| { - let settings = settings.lock().clone(); + let languages = languages.clone(); + move |params, mut cx| { + let languages = languages.clone(); async move { + let workspace_config = cx + .update(|cx| languages.workspace_configuration(cx)) + .await; Ok(params .items .into_iter() .map(|item| { if let Some(section) = &item.section { - settings + workspace_config .get(section) .cloned() .unwrap_or(serde_json::Value::Null) } else { - settings.clone() + workspace_config.clone() } }) .collect()) @@ -2071,6 +2090,14 @@ impl Project { }) .detach(); + language_server + .notify::( + lsp::DidChangeConfigurationParams { + settings: workspace_config, + }, + ) + .ok(); + this.update(&mut cx, |this, cx| { // If the language server for this key doesn't match the server id, don't store the // server. Which will cause it to be dropped, killing the process @@ -2103,13 +2130,6 @@ impl Project { progress_tokens: Default::default(), }, ); - language_server - .notify::( - lsp::DidChangeConfigurationParams { - settings: this.language_server_settings.lock().clone(), - }, - ) - .ok(); if let Some(project_id) = this.remote_id() { this.client @@ -2539,21 +2559,6 @@ impl Project { } } - pub fn set_language_server_settings(&mut self, settings: serde_json::Value) { - for server_state in self.language_servers.values() { - if let LanguageServerState::Running { server, .. } = server_state { - server - .notify::( - lsp::DidChangeConfigurationParams { - settings: settings.clone(), - }, - ) - .ok(); - } - } - *self.language_server_settings.lock() = settings; - } - pub fn language_server_statuses( &self, ) -> impl DoubleEndedIterator { diff --git a/crates/util/Cargo.toml b/crates/util/Cargo.toml index e8c158b637010d1faf3fbe2c0578f0f1fa82a0e7..b13b8af956c0f174c1897a920d47555bde119543 100644 --- a/crates/util/Cargo.toml +++ b/crates/util/Cargo.toml @@ -9,7 +9,7 @@ path = "src/util.rs" doctest = false [features] -test-support = ["serde_json", "tempdir", "git2"] +test-support = ["tempdir", "git2"] [dependencies] anyhow = "1.0.38" @@ -19,11 +19,10 @@ log = { version = "0.4.16", features = ["kv_unstable_serde"] } lazy_static = "1.4.0" rand = { workspace = true } tempdir = { version = "0.3.7", optional = true } -serde_json = { version = "1.0", features = ["preserve_order"], optional = true } +serde_json = { version = "1.0", features = ["preserve_order"] } git2 = { version = "0.15", default-features = false, optional = true } dirs = "3.0" [dev-dependencies] tempdir = { version = "0.3.7" } -serde_json = { version = "1.0", features = ["preserve_order"] } git2 = { version = "0.15", default-features = false } diff --git a/crates/util/src/util.rs b/crates/util/src/util.rs index 3824312a4fa9ec0d9993063c855b37bc55c3121c..6a5ccb8bd52e95b4aa54aa88c5b07d0f71b1906f 100644 --- a/crates/util/src/util.rs +++ b/crates/util/src/util.rs @@ -83,6 +83,24 @@ where } } +pub fn merge_json_value_into(source: serde_json::Value, target: &mut serde_json::Value) { + use serde_json::Value; + + match (source, target) { + (Value::Object(source), Value::Object(target)) => { + for (key, value) in source { + if let Some(target) = target.get_mut(&key) { + merge_json_value_into(value, target); + } else { + target.insert(key.clone(), value); + } + } + } + + (source, target) => *target = source, + } +} + pub trait ResultExt { type Ok; diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index a99c80c00121534347eb35daebf5fb22414490df..1acee4bad4d09814a656bd21e6536278bd62889a 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -2,6 +2,7 @@ use anyhow::Context; pub use language::*; use rust_embed::RustEmbed; use std::{borrow::Cow, str, sync::Arc}; +use theme::ThemeRegistry; mod c; mod elixir; @@ -31,17 +32,17 @@ mod yaml; #[exclude = "*.rs"] struct LanguageDir; -pub fn init(languages: Arc) { +pub fn init(languages: Arc, themes: Arc) { for (name, grammar, lsp_adapter) in [ ( "c", tree_sitter_c::language(), - Some(Box::new(c::CLspAdapter) as Box), + Some(Arc::new(c::CLspAdapter) as Arc), ), ( "cpp", tree_sitter_cpp::language(), - Some(Box::new(c::CLspAdapter)), + Some(Arc::new(c::CLspAdapter)), ), ( "css", @@ -51,17 +52,20 @@ pub fn init(languages: Arc) { ( "elixir", tree_sitter_elixir::language(), - Some(Box::new(elixir::ElixirLspAdapter)), + Some(Arc::new(elixir::ElixirLspAdapter)), ), ( "go", tree_sitter_go::language(), - Some(Box::new(go::GoLspAdapter)), + Some(Arc::new(go::GoLspAdapter)), ), ( "json", tree_sitter_json::language(), - Some(Box::new(json::JsonLspAdapter)), + Some(Arc::new(json::JsonLspAdapter::new( + languages.clone(), + themes.clone(), + ))), ), ( "markdown", @@ -71,12 +75,12 @@ pub fn init(languages: Arc) { ( "python", tree_sitter_python::language(), - Some(Box::new(python::PythonLspAdapter)), + Some(Arc::new(python::PythonLspAdapter)), ), ( "rust", tree_sitter_rust::language(), - Some(Box::new(rust::RustLspAdapter)), + Some(Arc::new(rust::RustLspAdapter)), ), ( "toml", @@ -86,32 +90,32 @@ pub fn init(languages: Arc) { ( "tsx", tree_sitter_typescript::language_tsx(), - Some(Box::new(typescript::TypeScriptLspAdapter)), + Some(Arc::new(typescript::TypeScriptLspAdapter)), ), ( "typescript", tree_sitter_typescript::language_typescript(), - Some(Box::new(typescript::TypeScriptLspAdapter)), + Some(Arc::new(typescript::TypeScriptLspAdapter)), ), ( "javascript", tree_sitter_typescript::language_tsx(), - Some(Box::new(typescript::TypeScriptLspAdapter)), + Some(Arc::new(typescript::TypeScriptLspAdapter)), ), ( "html", tree_sitter_html::language(), - Some(Box::new(html::HtmlLspAdapter)), + Some(Arc::new(html::HtmlLspAdapter)), ), ( "ruby", tree_sitter_ruby::language(), - Some(Box::new(ruby::RubyLanguageServer)), + Some(Arc::new(ruby::RubyLanguageServer)), ), ( "erb", tree_sitter_embedded_template::language(), - Some(Box::new(ruby::RubyLanguageServer)), + Some(Arc::new(ruby::RubyLanguageServer)), ), ( "scheme", @@ -126,12 +130,12 @@ pub fn init(languages: Arc) { ( "lua", tree_sitter_lua::language(), - Some(Box::new(lua::LuaLspAdapter)), + Some(Arc::new(lua::LuaLspAdapter)), ), ( "yaml", tree_sitter_yaml::language(), - Some(Box::new(yaml::YamlLspAdapter)), + Some(Arc::new(yaml::YamlLspAdapter)), ), ] { languages.register(name, load_config(name), grammar, lsp_adapter, load_queries); @@ -142,7 +146,7 @@ pub fn init(languages: Arc) { pub async fn language( name: &str, grammar: tree_sitter::Language, - lsp_adapter: Option>, + lsp_adapter: Option>, ) -> Arc { Arc::new( Language::new(load_config(name), Some(grammar)) diff --git a/crates/zed/src/languages/go.rs b/crates/zed/src/languages/go.rs index dc84599e4eff350f7f27e17d15fb4d79692acd88..dd819338d0bf9a3a36566c97b72dd248a90a8e6d 100644 --- a/crates/zed/src/languages/go.rs +++ b/crates/zed/src/languages/go.rs @@ -314,7 +314,7 @@ mod tests { let language = language( "go", tree_sitter_go::language(), - Some(Box::new(GoLspAdapter)), + Some(Arc::new(GoLspAdapter)), ) .await; diff --git a/crates/zed/src/languages/json.rs b/crates/zed/src/languages/json.rs index d7f87bee6c0d64cf936eedac874581e33690e25d..d5c59d28d119b35ac6b23bbed9c384f303d04e08 100644 --- a/crates/zed/src/languages/json.rs +++ b/crates/zed/src/languages/json.rs @@ -4,14 +4,32 @@ use async_compression::futures::bufread::GzipDecoder; use async_trait::async_trait; use client::http::HttpClient; use collections::HashMap; -use futures::{io::BufReader, StreamExt}; -use language::{LanguageServerName, LspAdapter}; +use futures::{future::BoxFuture, io::BufReader, FutureExt, StreamExt}; +use gpui::MutableAppContext; +use language::{LanguageRegistry, LanguageServerName, LspAdapter}; use serde_json::json; +use settings::{keymap_file_json_schema, settings_file_json_schema}; use smol::fs::{self, File}; -use std::{any::Any, env::consts, path::PathBuf, sync::Arc}; -use util::ResultExt; +use std::{ + any::Any, + env::consts, + future, + path::{Path, PathBuf}, + sync::Arc, +}; +use theme::ThemeRegistry; +use util::{paths, ResultExt, StaffMode}; -pub struct JsonLspAdapter; +pub struct JsonLspAdapter { + languages: Arc, + themes: Arc, +} + +impl JsonLspAdapter { + pub fn new(languages: Arc, themes: Arc) -> Self { + Self { languages, themes } + } +} #[async_trait] impl LspAdapter for JsonLspAdapter { @@ -102,7 +120,45 @@ impl LspAdapter for JsonLspAdapter { })) } + fn workspace_configuration( + &self, + cx: &mut MutableAppContext, + ) -> Option> { + let action_names = cx.all_action_names().collect::>(); + let theme_names = self + .themes + .list(**cx.default_global::()) + .map(|meta| meta.name) + .collect(); + let language_names = self.languages.language_names(); + Some( + future::ready(serde_json::json!({ + "json": { + "format": { + "enable": true, + }, + "schemas": [ + { + "fileMatch": [schema_file_match(&paths::SETTINGS)], + "schema": settings_file_json_schema(theme_names, &language_names), + }, + { + "fileMatch": [schema_file_match(&paths::KEYMAP)], + "schema": keymap_file_json_schema(&action_names), + } + ] + } + })) + .boxed(), + ) + } + async fn language_ids(&self) -> HashMap { [("JSON".into(), "jsonc".into())].into_iter().collect() } } + +fn schema_file_match(path: &Path) -> &Path { + path.strip_prefix(path.parent().unwrap().parent().unwrap()) + .unwrap() +} diff --git a/crates/zed/src/languages/rust.rs b/crates/zed/src/languages/rust.rs index 40948d5005d75a8f04441cd5aef62829f0568a10..a8f7fcbc4dcf34ff3e017be53cfd967a0e185dfe 100644 --- a/crates/zed/src/languages/rust.rs +++ b/crates/zed/src/languages/rust.rs @@ -306,7 +306,7 @@ mod tests { let language = language( "rust", tree_sitter_rust::language(), - Some(Box::new(RustLspAdapter)), + Some(Arc::new(RustLspAdapter)), ) .await; let grammar = language.grammar().unwrap(); @@ -392,7 +392,7 @@ mod tests { let language = language( "rust", tree_sitter_rust::language(), - Some(Box::new(RustLspAdapter)), + Some(Arc::new(RustLspAdapter)), ) .await; let grammar = language.grammar().unwrap(); diff --git a/crates/zed/src/languages/yaml.rs b/crates/zed/src/languages/yaml.rs index 46569111f1ddc82743c4ec379c3e3424b7d6b326..9750ecce88d6f5ea73824bb801d9ac73764d69d9 100644 --- a/crates/zed/src/languages/yaml.rs +++ b/crates/zed/src/languages/yaml.rs @@ -1,12 +1,13 @@ -use std::{any::Any, path::PathBuf, sync::Arc}; - use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; use client::http::HttpClient; -use futures::StreamExt; -use smol::fs; - +use futures::{future::BoxFuture, FutureExt, StreamExt}; +use gpui::MutableAppContext; use language::{LanguageServerName, LspAdapter}; +use serde_json::Value; +use settings::Settings; +use smol::fs; +use std::{any::Any, future, path::PathBuf, sync::Arc}; use util::ResultExt; use super::installation::{npm_install_packages, npm_package_latest_version}; @@ -90,4 +91,19 @@ impl LspAdapter for YamlLspAdapter { .await .log_err() } + + fn workspace_configuration( + &self, + cx: &mut MutableAppContext, + ) -> Option> { + let settings = cx.global::(); + Some( + future::ready(serde_json::json!({ + "[yaml]": { + "editor.tabSize": settings.tab_size(Some("YAML")) + } + })) + .boxed(), + ) + } } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 8cf6879f7abcd5163463d0d6dc9ca2baadc7324d..6b511554980cef30893e9965be2944e09e349287 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -139,7 +139,7 @@ fn main() { languages.set_executor(cx.background().clone()); languages.set_language_server_download_dir(paths::LANGUAGES_DIR.clone()); let languages = Arc::new(languages); - languages::init(languages.clone()); + languages::init(languages.clone(), themes.clone()); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx)); cx.set_global(client.clone()); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 83a8038d77249999ec8e39ba6ba50f97f95ef629..b0239d234c8a30476ee915bbec4f5fec04e3faa1 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -29,10 +29,10 @@ use project_panel::ProjectPanel; use search::{BufferSearchBar, ProjectSearchBar}; use serde::Deserialize; use serde_json::to_string_pretty; -use settings::{keymap_file_json_schema, settings_file_json_schema, Settings}; +use settings::Settings; use std::{borrow::Cow, env, path::Path, str, sync::Arc}; use terminal_view::terminal_button::{self, TerminalButton}; -use util::{channel::ReleaseChannel, paths, ResultExt, StaffMode}; +use util::{channel::ReleaseChannel, paths, ResultExt}; use uuid::Uuid; pub use workspace; use workspace::{sidebar::SidebarSide, AppState, Restart, Workspace}; @@ -296,34 +296,6 @@ pub fn initialize_workspace( cx.emit(workspace::Event::PaneAdded(workspace.active_pane().clone())); cx.emit(workspace::Event::PaneAdded(workspace.dock_pane().clone())); - let theme_names = app_state - .themes - .list(**cx.default_global::()) - .map(|meta| meta.name) - .collect(); - let language_names = app_state.languages.language_names(); - - workspace.project().update(cx, |project, cx| { - let action_names = cx.all_action_names().collect::>(); - project.set_language_server_settings(serde_json::json!({ - "json": { - "format": { - "enable": true, - }, - "schemas": [ - { - "fileMatch": [schema_file_match(&paths::SETTINGS)], - "schema": settings_file_json_schema(theme_names, &language_names), - }, - { - "fileMatch": [schema_file_match(&paths::KEYMAP)], - "schema": keymap_file_json_schema(&action_names), - } - ] - } - })); - }); - let collab_titlebar_item = cx.add_view(|cx| CollabTitlebarItem::new(&workspace_handle, &app_state.user_store, cx)); workspace.set_titlebar_item(collab_titlebar_item, cx); @@ -676,11 +648,6 @@ fn open_bundled_file( .detach(); } -fn schema_file_match(path: &Path) -> &Path { - path.strip_prefix(path.parent().unwrap().parent().unwrap()) - .unwrap() -} - #[cfg(test)] mod tests { use super::*; @@ -1882,7 +1849,8 @@ mod tests { let mut languages = LanguageRegistry::new(Task::ready(())); languages.set_executor(cx.background().clone()); let languages = Arc::new(languages); - languages::init(languages.clone()); + let themes = ThemeRegistry::new((), cx.font_cache().clone()); + languages::init(languages.clone(), themes); for name in languages.language_names() { languages.language_for_name(&name); }