diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 572bb8c8aeea49b2b3099499cc06183dcc6010cf..bab8d90f7cd4333e3a1e61bab4cdd8ccef7e6672 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -18,6 +18,7 @@ use gpui::{MutableAppContext, Task}; use highlight_map::HighlightMap; use lazy_static::lazy_static; use parking_lot::{Mutex, RwLock}; +use postage::watch; use regex::Regex; use serde::{de, Deserialize, Deserializer}; use serde_json::Value; @@ -316,6 +317,7 @@ pub struct LanguageRegistry { Shared>>>, >, >, + subscription: RwLock<(watch::Sender<()>, watch::Receiver<()>)>, } impl LanguageRegistry { @@ -328,6 +330,7 @@ impl LanguageRegistry { lsp_binary_statuses_rx, login_shell_env_loaded: login_shell_env_loaded.shared(), lsp_binary_paths: Default::default(), + subscription: RwLock::new(watch::channel()), } } @@ -338,6 +341,11 @@ impl LanguageRegistry { pub fn add(&self, language: Arc) { self.languages.write().push(language.clone()); + *self.subscription.write().0.borrow_mut() = (); + } + + pub fn subscribe(&self) -> watch::Receiver<()> { + self.subscription.read().1.clone() } pub fn set_theme(&self, theme: &SyntaxTheme) { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 7adab020b5b92ea815c7b3319b1f7fde84b3fd9b..96e33de17edd98c4726c4a2f28b7be341552de3a 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -125,6 +125,7 @@ pub struct Project { buffer_snapshots: HashMap>, nonce: u128, initialized_persistent_state: bool, + _maintain_buffer_languages: Task<()>, } #[derive(Error, Debug)] @@ -472,6 +473,7 @@ impl Project { opened_buffer: (Rc::new(RefCell::new(opened_buffer_tx)), opened_buffer_rx), client_subscriptions: Vec::new(), _subscriptions: vec![cx.observe_global::(Self::on_settings_changed)], + _maintain_buffer_languages: Self::maintain_buffer_languages(&languages, cx), active_entry: None, languages, client, @@ -549,6 +551,7 @@ impl Project { loading_local_worktrees: Default::default(), active_entry: None, collaborators: Default::default(), + _maintain_buffer_languages: Self::maintain_buffer_languages(&languages, cx), languages, user_store: user_store.clone(), project_store, @@ -2019,6 +2022,34 @@ impl Project { }) } + fn maintain_buffer_languages( + languages: &LanguageRegistry, + cx: &mut ModelContext, + ) -> Task<()> { + let mut subscription = languages.subscribe(); + cx.spawn_weak(|project, mut cx| async move { + while let Some(_) = subscription.next().await { + if let Some(project) = project.upgrade(&cx) { + project.update(&mut cx, |project, cx| { + let mut buffers_without_language = Vec::new(); + for buffer in project.opened_buffers.values() { + if let Some(buffer) = buffer.upgrade(cx) { + if buffer.read(cx).language().is_none() { + buffers_without_language.push(buffer); + } + } + } + + for buffer in buffers_without_language { + project.assign_language_to_buffer(&buffer, cx); + project.register_buffer_with_language_server(&buffer, cx); + } + }); + } + } + }) + } + fn assign_language_to_buffer( &mut self, buffer: &ModelHandle, @@ -2089,9 +2120,10 @@ impl Project { move |params, mut cx| { if let Some(this) = this.upgrade(&cx) { this.update(&mut cx, |this, cx| { - this.on_lsp_diagnostics_published( + // TODO(isaac): remove block on + smol::block_on(this.on_lsp_diagnostics_published( server_id, params, &adapter, cx, - ); + )); }); } } diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index e16edf27d23dfe608994e5e0b5a4a51d909ac548..cce598c685c7a79c33c59ab414db28512340749b 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -122,11 +122,46 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { .await; let project = Project::test(fs.clone(), ["/the-root".as_ref()], cx).await; - project.update(cx, |project, _| { - project.languages.add(Arc::new(rust_language)); + + // Open a buffer before languages have been added + let json_buffer = project + .update(cx, |project, cx| { + project.open_local_buffer("/the-root/package.json", cx) + }) + .await + .unwrap(); + + // Assert that this buffer does not have a language + assert!(json_buffer.read_with(cx, |buffer, _| { buffer.language().is_none() })); + + // Now we add the languages to the project, and subscribe to the watcher + project.update(cx, |project, cx| { + // Get a handle to the channel and clear out default item + let mut recv = project.languages.subscribe(); + recv.blocking_recv(); + + // Add, then wait to be notified that JSON has been added project.languages.add(Arc::new(json_language)); + recv.blocking_recv(); + + // Add, then wait to be notified that Rust has been added + project.languages.add(Arc::new(rust_language)); + recv.blocking_recv(); + // Uncommenting this would cause the thread to block indefinitely: + // recv.blocking_recv(); + + // Force the assignment, we know the watcher has been notified + // but have no way to wait for the watcher to assign to the project + project.assign_language_to_buffer(&json_buffer, cx); }); + // Assert that the opened buffer does have a language, and that it is JSON + let name = json_buffer.read_with(cx, |buffer, _| buffer.language().map(|l| l.name())); + assert_eq!(name, Some("JSON".into())); + + // Close the JSON buffer we opened + cx.update(|_| drop(json_buffer)); + // Open a buffer without an associated language server. let toml_buffer = project .update(cx, |project, cx| { diff --git a/crates/zed/src/languages/language_plugin.rs b/crates/zed/src/languages/language_plugin.rs index fe74d7d9eb2d3f63856abbe195089e81f929dde7..664a3e2c0f3f4d247dd0be7852569058e3e13c2c 100644 --- a/crates/zed/src/languages/language_plugin.rs +++ b/crates/zed/src/languages/language_plugin.rs @@ -26,6 +26,7 @@ pub async fn new_json(executor: Arc) -> Result { include_bytes!("../../../../plugins/bin/json_language.wasm.pre"), ) .await?; + // smol::Timer::after(std::time::Duration::from_secs(5)).await; PluginLspAdapter::new(plugin, executor).await }