From db05e3238926fa7903fdea7cdec6001a64cd38c6 Mon Sep 17 00:00:00 2001 From: Keith Simmons Date: Thu, 30 Jun 2022 16:46:26 -0700 Subject: [PATCH] Prevent creating extra language server instances if there already exists one for that workspace --- crates/lsp/src/lsp.rs | 25 +- crates/project/src/lsp_command.rs | 14 +- crates/project/src/project.rs | 658 +++++++++++++++++------------- 3 files changed, 391 insertions(+), 306 deletions(-) diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index 0bef424104..682d8f1823 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -11,7 +11,7 @@ use serde_json::{json, value::RawValue, Value}; use smol::{ channel, io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader}, - process, + process::{self, Child}, }; use std::{ future::Future, @@ -44,6 +44,7 @@ pub struct LanguageServer { io_tasks: Mutex>, Task>)>>, output_done_rx: Mutex>, root_path: PathBuf, + _server: Option, } pub struct Subscription { @@ -118,11 +119,20 @@ impl LanguageServer { .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::inherit()) + .kill_on_drop(true) .spawn()?; + let stdin = server.stdin.take().unwrap(); - let stdout = server.stdout.take().unwrap(); - let mut server = - Self::new_internal(server_id, stdin, stdout, root_path, cx, |notification| { + let stout = server.stdout.take().unwrap(); + + let mut server = Self::new_internal( + server_id, + stdin, + stout, + Some(server), + root_path, + cx, + |notification| { log::info!( "unhandled notification {}:\n{}", notification.method, @@ -131,7 +141,8 @@ impl LanguageServer { ) .unwrap() ); - }); + }, + ); if let Some(name) = binary_path.file_name() { server.name = name.to_string_lossy().to_string(); } @@ -142,6 +153,7 @@ impl LanguageServer { server_id: usize, stdin: Stdin, stdout: Stdout, + server: Option, root_path: &Path, cx: AsyncAppContext, mut on_unhandled_notification: F, @@ -242,6 +254,7 @@ impl LanguageServer { io_tasks: Mutex::new(Some((input_task, output_task))), output_done_rx: Mutex::new(Some(output_done_rx)), root_path: root_path.to_path_buf(), + _server: server, } } @@ -608,6 +621,7 @@ impl LanguageServer { 0, stdin_writer, stdout_reader, + None, Path::new("/"), cx.clone(), |_| {}, @@ -617,6 +631,7 @@ impl LanguageServer { 0, stdout_writer, stdin_reader, + None, Path::new("/"), cx.clone(), move |msg| { diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index ee2bf37aa1..5897b5fcb6 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -242,7 +242,7 @@ impl LspCommand for PerformRename { .read_with(&cx, |project, cx| { project .language_server_for_buffer(buffer.read(cx), cx) - .cloned() + .map(|(adapter, server)| (adapter.clone(), server.clone())) }) .ok_or_else(|| anyhow!("no language server found for buffer"))?; Project::deserialize_workspace_edit( @@ -359,7 +359,7 @@ impl LspCommand for GetDefinition { .read_with(&cx, |project, cx| { project .language_server_for_buffer(buffer.read(cx), cx) - .cloned() + .map(|(adapter, server)| (adapter.clone(), server.clone())) }) .ok_or_else(|| anyhow!("no language server found for buffer"))?; @@ -388,8 +388,8 @@ impl LspCommand for GetDefinition { .update(&mut cx, |this, cx| { this.open_local_buffer_via_lsp( target_uri, - lsp_adapter.clone(), - language_server.clone(), + language_server.server_id(), + lsp_adapter.name(), cx, ) }) @@ -599,7 +599,7 @@ impl LspCommand for GetReferences { .read_with(&cx, |project, cx| { project .language_server_for_buffer(buffer.read(cx), cx) - .cloned() + .map(|(adapter, server)| (adapter.clone(), server.clone())) }) .ok_or_else(|| anyhow!("no language server found for buffer"))?; @@ -609,8 +609,8 @@ impl LspCommand for GetReferences { .update(&mut cx, |this, cx| { this.open_local_buffer_via_lsp( lsp_location.uri, - lsp_adapter.clone(), - language_server.clone(), + language_server.server_id(), + lsp_adapter.name(), cx, ) }) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 8ce07a6abd..828ffc3c05 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -65,6 +65,14 @@ pub trait Item: Entity { fn entry_id(&self, cx: &AppContext) -> Option; } +pub enum LanguageServerState { + Starting(Task>>), + Running { + adapter: Arc, + server: Arc, + }, +} + pub struct ProjectStore { db: Arc, projects: Vec>, @@ -74,10 +82,8 @@ pub struct Project { worktrees: Vec, active_entry: Option, languages: Arc, - language_servers: - HashMap<(WorktreeId, LanguageServerName), (Arc, Arc)>, - started_language_servers: - HashMap<(WorktreeId, LanguageServerName), Task>>>, + 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, @@ -179,7 +185,7 @@ pub struct LanguageServerStatus { pub name: String, pub pending_work: BTreeMap, pub has_pending_diagnostic_updates: bool, - progress_tokens: HashSet, + pub progress_tokens: HashSet, } #[derive(Clone, Debug, Serialize)] @@ -437,7 +443,7 @@ impl Project { next_entry_id: Default::default(), next_diagnostic_group_id: Default::default(), language_servers: Default::default(), - started_language_servers: Default::default(), + language_server_ids: Default::default(), language_server_statuses: Default::default(), last_workspace_edits_by_language_server: Default::default(), language_server_settings: Default::default(), @@ -536,7 +542,7 @@ impl Project { }), }, language_servers: Default::default(), - started_language_servers: Default::default(), + language_server_ids: Default::default(), language_server_settings: Default::default(), language_server_statuses: response .language_servers @@ -691,7 +697,7 @@ impl Project { if let Some(lsp_adapter) = language.lsp_adapter() { if !settings.enable_language_server(Some(&language.name())) { let lsp_name = lsp_adapter.name(); - for (worktree_id, started_lsp_name) in self.started_language_servers.keys() { + for (worktree_id, started_lsp_name) in self.language_server_ids.keys() { if lsp_name == *started_lsp_name { language_servers_to_stop.push((*worktree_id, started_lsp_name.clone())); } @@ -1538,8 +1544,8 @@ impl Project { fn open_local_buffer_via_lsp( &mut self, abs_path: lsp::Url, - lsp_adapter: Arc, - lsp_server: Arc, + language_server_id: usize, + language_server_name: LanguageServerName, cx: &mut ModelContext, ) -> Task>> { cx.spawn(|this, mut cx| async move { @@ -1557,9 +1563,9 @@ impl Project { }) .await?; this.update(&mut cx, |this, cx| { - this.language_servers.insert( - (worktree.read(cx).id(), lsp_adapter.name()), - (lsp_adapter, lsp_server), + this.language_server_ids.insert( + (worktree.read(cx).id(), language_server_name), + language_server_id, ); }); (worktree, PathBuf::new()) @@ -1726,9 +1732,16 @@ impl Project { if let Some(adapter) = language.lsp_adapter() { language_id = adapter.id_for_language(language.name().as_ref()); language_server = self - .language_servers + .language_server_ids .get(&(worktree_id, adapter.name())) - .cloned(); + .and_then(|id| self.language_servers.get(&id)) + .and_then(|server_state| { + if let LanguageServerState::Running { server, .. } = server_state { + Some(server.clone()) + } else { + None + } + }); } } @@ -1739,7 +1752,7 @@ impl Project { } } - if let Some((_, server)) = language_server { + if let Some(server) = language_server { server .notify::( lsp::DidOpenTextDocumentParams { @@ -1816,9 +1829,9 @@ impl Project { } } BufferEvent::Edited { .. } => { - let (_, language_server) = self - .language_server_for_buffer(buffer.read(cx), cx)? - .clone(); + let language_server = self + .language_server_for_buffer(buffer.read(cx), cx) + .map(|(_, server)| server.clone())?; let buffer = buffer.read(cx); let file = File::from_dyn(buffer.file())?; let abs_path = file.as_local()?.abs_path(cx); @@ -1907,16 +1920,19 @@ impl Project { fn language_servers_for_worktree( &self, worktree_id: WorktreeId, - ) -> impl Iterator, Arc)> { - self.language_servers.iter().filter_map( - move |((language_server_worktree_id, _), server)| { + ) -> impl Iterator, &Arc)> { + self.language_server_ids + .iter() + .filter_map(move |((language_server_worktree_id, _), id)| { if *language_server_worktree_id == worktree_id { - Some(server) - } else { - None + if let Some(LanguageServerState::Running { adapter, server }) = + self.language_servers.get(&id) + { + return Some((adapter, server)); + } } - }, - ) + None + }) } fn assign_language_to_buffer( @@ -1960,7 +1976,8 @@ impl Project { return; }; let key = (worktree_id, adapter.name()); - self.started_language_servers + + self.language_server_ids .entry(key.clone()) .or_insert_with(|| { let server_id = post_inc(&mut self.next_language_server_id); @@ -1971,218 +1988,240 @@ impl Project { self.client.http_client(), cx, ); - cx.spawn_weak(|this, mut cx| async move { - let language_server = language_server?.await.log_err()?; - let language_server = language_server - .initialize(adapter.initialization_options()) - .await - .log_err()?; - let this = this.upgrade(&cx)?; - let disk_based_diagnostics_progress_token = - adapter.disk_based_diagnostics_progress_token(); + self.language_servers.insert( + server_id, + LanguageServerState::Starting(cx.spawn_weak(|this, mut cx| async move { + let language_server = language_server?.await.log_err()?; + let language_server = language_server + .initialize(adapter.initialization_options()) + .await + .log_err()?; + let this = this.upgrade(&cx)?; + let disk_based_diagnostics_progress_token = + adapter.disk_based_diagnostics_progress_token(); - language_server - .on_notification::({ - let this = this.downgrade(); - let adapter = adapter.clone(); - move |params, mut cx| { - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, cx| { - this.on_lsp_diagnostics_published( - server_id, params, &adapter, cx, - ); - }); - } - } - }) - .detach(); - - language_server - .on_request::({ - let settings = this - .read_with(&cx, |this, _| this.language_server_settings.clone()); - move |params, _| { - let settings = settings.lock().clone(); - async move { - Ok(params - .items - .into_iter() - .map(|item| { - if let Some(section) = &item.section { - settings - .get(section) - .cloned() - .unwrap_or(serde_json::Value::Null) - } else { - settings.clone() - } - }) - .collect()) - } - } - }) - .detach(); - - // Even though we don't have handling for these requests, respond to them to - // avoid stalling any language server like `gopls` which waits for a response - // to these requests when initializing. - language_server - .on_request::({ - let this = this.downgrade(); - move |params, mut cx| async move { - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, _| { - if let Some(status) = - this.language_server_statuses.get_mut(&server_id) - { - if let lsp::NumberOrString::String(token) = params.token - { - status.progress_tokens.insert(token); - } - } - }); - } - Ok(()) - } - }) - .detach(); - language_server - .on_request::(|_, _| async { - Ok(()) - }) - .detach(); - - language_server - .on_request::({ - let this = this.downgrade(); - let adapter = adapter.clone(); - let language_server = language_server.clone(); - move |params, cx| { - Self::on_lsp_workspace_edit( - this, - params, - server_id, - adapter.clone(), - language_server.clone(), - cx, - ) - } - }) - .detach(); - - language_server - .on_notification::({ - let this = this.downgrade(); - move |params, mut cx| { - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, cx| { - this.on_lsp_progress( - params, - server_id, - disk_based_diagnostics_progress_token, - cx, - ); - }); - } - } - }) - .detach(); - - this.update(&mut cx, |this, cx| { - this.language_servers - .insert(key.clone(), (adapter.clone(), language_server.clone())); - this.language_server_statuses.insert( - server_id, - LanguageServerStatus { - name: language_server.name().to_string(), - pending_work: Default::default(), - has_pending_diagnostic_updates: false, - progress_tokens: Default::default(), - }, - ); language_server - .notify::( - lsp::DidChangeConfigurationParams { - settings: this.language_server_settings.lock().clone(), - }, - ) - .ok(); - - if let Some(project_id) = this.shared_remote_id() { - this.client - .send(proto::StartLanguageServer { - project_id, - server: Some(proto::LanguageServer { - id: server_id as u64, - name: language_server.name().to_string(), - }), - }) - .log_err(); - } - - // Tell the language server about every open buffer in the worktree that matches the language. - for buffer in this.opened_buffers.values() { - if let Some(buffer_handle) = buffer.upgrade(cx) { - let buffer = buffer_handle.read(cx); - let file = if let Some(file) = File::from_dyn(buffer.file()) { - file - } else { - continue; - }; - let language = if let Some(language) = buffer.language() { - language - } else { - continue; - }; - if file.worktree.read(cx).id() != key.0 - || language.lsp_adapter().map(|a| a.name()) - != Some(key.1.clone()) - { - continue; + .on_notification::({ + let this = this.downgrade(); + let adapter = adapter.clone(); + move |params, mut cx| { + if let Some(this) = this.upgrade(&cx) { + this.update(&mut cx, |this, cx| { + this.on_lsp_diagnostics_published( + server_id, params, &adapter, cx, + ); + }); + } } + }) + .detach(); - let file = file.as_local()?; - let versions = this - .buffer_snapshots - .entry(buffer.remote_id()) - .or_insert_with(|| vec![(0, buffer.text_snapshot())]); - let (version, initial_snapshot) = versions.last().unwrap(); - let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap(); - let language_id = adapter.id_for_language(language.name().as_ref()); - language_server - .notify::( - lsp::DidOpenTextDocumentParams { - text_document: lsp::TextDocumentItem::new( - uri, - language_id.unwrap_or_default(), - *version, - initial_snapshot.text(), - ), - }, - ) - .log_err()?; - buffer_handle.update(cx, |buffer, cx| { - buffer.set_completion_triggers( - language_server - .capabilities() - .completion_provider - .as_ref() - .and_then(|provider| { - provider.trigger_characters.clone() + language_server + .on_request::({ + let settings = this.read_with(&cx, |this, _| { + this.language_server_settings.clone() + }); + move |params, _| { + let settings = settings.lock().clone(); + async move { + Ok(params + .items + .into_iter() + .map(|item| { + if let Some(section) = &item.section { + settings + .get(section) + .cloned() + .unwrap_or(serde_json::Value::Null) + } else { + settings.clone() + } }) - .unwrap_or(Vec::new()), + .collect()) + } + } + }) + .detach(); + + // Even though we don't have handling for these requests, respond to them to + // avoid stalling any language server like `gopls` which waits for a response + // to these requests when initializing. + language_server + .on_request::({ + let this = this.downgrade(); + move |params, mut cx| async move { + if let Some(this) = this.upgrade(&cx) { + this.update(&mut cx, |this, _| { + if let Some(status) = + this.language_server_statuses.get_mut(&server_id) + { + if let lsp::NumberOrString::String(token) = + params.token + { + status.progress_tokens.insert(token); + } + } + }); + } + Ok(()) + } + }) + .detach(); + language_server + .on_request::(|_, _| async { + Ok(()) + }) + .detach(); + + language_server + .on_request::({ + let this = this.downgrade(); + let adapter = adapter.clone(); + let language_server = language_server.clone(); + move |params, cx| { + Self::on_lsp_workspace_edit( + this, + params, + server_id, + adapter.clone(), + language_server.clone(), cx, ) - }); + } + }) + .detach(); + + language_server + .on_notification::({ + let this = this.downgrade(); + move |params, mut cx| { + if let Some(this) = this.upgrade(&cx) { + this.update(&mut cx, |this, cx| { + this.on_lsp_progress( + params, + server_id, + disk_based_diagnostics_progress_token, + cx, + ); + }); + } + } + }) + .detach(); + + this.update(&mut cx, |this, cx| { + // If the language server for this key doesn't match the server id, don't store the + // server. + if this + .language_server_ids + .get(&key) + .map(|id| id != &server_id) + .unwrap_or(false) + { + return None; } - } - cx.notify(); - Some(()) - }); + this.language_servers.insert( + server_id, + LanguageServerState::Running { + adapter: adapter.clone(), + server: language_server.clone(), + }, + ); + this.language_server_statuses.insert( + server_id, + LanguageServerStatus { + name: language_server.name().to_string(), + pending_work: Default::default(), + has_pending_diagnostic_updates: false, + progress_tokens: Default::default(), + }, + ); + language_server + .notify::( + lsp::DidChangeConfigurationParams { + settings: this.language_server_settings.lock().clone(), + }, + ) + .ok(); - Some(language_server) - }) + if let Some(project_id) = this.shared_remote_id() { + this.client + .send(proto::StartLanguageServer { + project_id, + server: Some(proto::LanguageServer { + id: server_id as u64, + name: language_server.name().to_string(), + }), + }) + .log_err(); + } + + // Tell the language server about every open buffer in the worktree that matches the language. + for buffer in this.opened_buffers.values() { + if let Some(buffer_handle) = buffer.upgrade(cx) { + let buffer = buffer_handle.read(cx); + let file = if let Some(file) = File::from_dyn(buffer.file()) { + file + } else { + continue; + }; + let language = if let Some(language) = buffer.language() { + language + } else { + continue; + }; + if file.worktree.read(cx).id() != key.0 + || language.lsp_adapter().map(|a| a.name()) + != Some(key.1.clone()) + { + continue; + } + + let file = file.as_local()?; + let versions = this + .buffer_snapshots + .entry(buffer.remote_id()) + .or_insert_with(|| vec![(0, buffer.text_snapshot())]); + let (version, initial_snapshot) = versions.last().unwrap(); + let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap(); + let language_id = + adapter.id_for_language(language.name().as_ref()); + language_server + .notify::( + lsp::DidOpenTextDocumentParams { + text_document: lsp::TextDocumentItem::new( + uri, + language_id.unwrap_or_default(), + *version, + initial_snapshot.text(), + ), + }, + ) + .log_err()?; + buffer_handle.update(cx, |buffer, cx| { + buffer.set_completion_triggers( + language_server + .capabilities() + .completion_provider + .as_ref() + .and_then(|provider| { + provider.trigger_characters.clone() + }) + .unwrap_or(Vec::new()), + cx, + ) + }); + } + } + + cx.notify(); + Some(language_server) + }) + })), + ); + + server_id }); } @@ -2193,26 +2232,28 @@ impl Project { cx: &mut ModelContext, ) -> Task<()> { let key = (worktree_id, adapter_name); - if let Some((_, language_server)) = self.language_servers.remove(&key) { - self.language_server_statuses - .remove(&language_server.server_id()); - cx.notify(); - } - - if let Some(started_language_server) = self.started_language_servers.remove(&key) { + if let Some(server_id) = self.language_server_ids.remove(&key) { + let server_state = self.language_servers.remove(&server_id); cx.spawn_weak(|this, mut cx| async move { - if let Some(language_server) = started_language_server.await { - if let Some(shutdown) = language_server.shutdown() { + let server = match server_state { + Some(LanguageServerState::Starting(started_language_server)) => { + started_language_server.await + } + Some(LanguageServerState::Running { server, .. }) => Some(server), + None => None, + }; + + if let Some(server) = server { + if let Some(shutdown) = server.shutdown() { shutdown.await; } + } - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, cx| { - this.language_server_statuses - .remove(&language_server.server_id()); - cx.notify(); - }); - } + if let Some(this) = this.upgrade(&cx) { + this.update(&mut cx, |this, cx| { + this.language_server_statuses.remove(&server_id); + cx.notify(); + }); } }) } else { @@ -2498,14 +2539,16 @@ impl Project { } pub fn set_language_server_settings(&mut self, settings: serde_json::Value) { - for (_, server) in self.language_servers.values() { - server - .notify::( - lsp::DidChangeConfigurationParams { - settings: settings.clone(), - }, - ) - .ok(); + 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; } @@ -2968,30 +3011,36 @@ impl Project { pub fn symbols(&self, query: &str, cx: &mut ModelContext) -> Task>> { if self.is_local() { let mut requests = Vec::new(); - for ((worktree_id, _), (lsp_adapter, language_server)) in self.language_servers.iter() { + for ((worktree_id, _), server_id) in self.language_server_ids.iter() { let worktree_id = *worktree_id; if let Some(worktree) = self .worktree_for_id(worktree_id, cx) .and_then(|worktree| worktree.read(cx).as_local()) { - let lsp_adapter = lsp_adapter.clone(); - let worktree_abs_path = worktree.abs_path().clone(); - requests.push( - language_server - .request::(lsp::WorkspaceSymbolParams { - query: query.to_string(), - ..Default::default() - }) - .log_err() - .map(move |response| { - ( - lsp_adapter, - worktree_id, - worktree_abs_path, - response.unwrap_or_default(), + if let Some(LanguageServerState::Running { adapter, server }) = + self.language_servers.get(server_id) + { + let adapter = adapter.clone(); + let worktree_abs_path = worktree.abs_path().clone(); + requests.push( + server + .request::( + lsp::WorkspaceSymbolParams { + query: query.to_string(), + ..Default::default() + }, ) - }), - ); + .log_err() + .map(move |response| { + ( + adapter, + worktree_id, + worktree_abs_path, + response.unwrap_or_default(), + ) + }), + ); + } } } @@ -3074,11 +3123,11 @@ impl Project { cx: &mut ModelContext, ) -> Task>> { if self.is_local() { - let (lsp_adapter, language_server) = if let Some(server) = self.language_servers.get(&( + let language_server_id = if let Some(id) = self.language_server_ids.get(&( symbol.source_worktree_id, symbol.language_server_name.clone(), )) { - server.clone() + *id } else { return Task::ready(Err(anyhow!( "language server for worktree and language not found" @@ -3101,7 +3150,12 @@ impl Project { return Task::ready(Err(anyhow!("invalid symbol path"))); }; - self.open_local_buffer_via_lsp(symbol_uri, lsp_adapter, language_server, cx) + self.open_local_buffer_via_lsp( + symbol_uri, + language_server_id, + symbol.language_server_name.clone(), + cx, + ) } else if let Some(project_id) = self.remote_id() { let request = self.client.request(proto::OpenBufferForSymbol { project_id, @@ -3152,8 +3206,8 @@ impl Project { if worktree.read(cx).as_local().is_some() { let buffer_abs_path = buffer_abs_path.unwrap(); - let (_, lang_server) = - if let Some(server) = self.language_server_for_buffer(source_buffer, cx) { + let lang_server = + if let Some((_, server)) = self.language_server_for_buffer(source_buffer, cx) { server.clone() } else { return Task::ready(Ok(Default::default())); @@ -3310,7 +3364,7 @@ impl Project { let buffer_id = buffer.remote_id(); if self.is_local() { - let (_, lang_server) = if let Some(server) = self.language_server_for_buffer(buffer, cx) + let lang_server = if let Some((_, server)) = self.language_server_for_buffer(buffer, cx) { server.clone() } else { @@ -3407,7 +3461,7 @@ impl Project { if worktree.read(cx).as_local().is_some() { let buffer_abs_path = buffer_abs_path.unwrap(); - let (_, lang_server) = if let Some(server) = self.language_server_for_buffer(buffer, cx) + let lang_server = if let Some((_, server)) = self.language_server_for_buffer(buffer, cx) { server.clone() } else { @@ -3494,8 +3548,8 @@ impl Project { if self.is_local() { let buffer = buffer_handle.read(cx); let (lsp_adapter, lang_server) = - if let Some(server) = self.language_server_for_buffer(buffer, cx) { - server.clone() + if let Some((adapter, server)) = self.language_server_for_buffer(buffer, cx) { + (adapter.clone(), server.clone()) } else { return Task::ready(Ok(Default::default())); }; @@ -3531,8 +3585,8 @@ impl Project { this, edit, push_to_history, - lsp_adapter, - lang_server, + lsp_adapter.clone(), + lang_server.clone(), &mut cx, ) .await @@ -3661,8 +3715,8 @@ impl Project { .update(cx, |this, cx| { this.open_local_buffer_via_lsp( op.text_document.uri, - lsp_adapter.clone(), - language_server.clone(), + language_server.server_id(), + lsp_adapter.name(), cx, ) }) @@ -3956,9 +4010,10 @@ impl Project { let buffer = buffer_handle.read(cx); if self.is_local() { let file = File::from_dyn(buffer.file()).and_then(File::as_local); - if let Some((file, (_, language_server))) = - file.zip(self.language_server_for_buffer(buffer, cx).cloned()) - { + if let Some((file, language_server)) = file.zip( + self.language_server_for_buffer(buffer, cx) + .map(|(_, server)| server.clone()), + ) { let lsp_params = request.to_lsp(&file.abs_path(cx), cx); return cx.spawn(|this, cx| async move { if !request.check_capabilities(&language_server.capabilities()) { @@ -5574,14 +5629,21 @@ impl Project { &self, buffer: &Buffer, cx: &AppContext, - ) -> Option<&(Arc, Arc)> { + ) -> Option<(&Arc, &Arc)> { if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) { let worktree_id = file.worktree_id(cx); - self.language_servers - .get(&(worktree_id, language.lsp_adapter()?.name())) - } else { - None + let key = (worktree_id, language.lsp_adapter()?.name()); + + if let Some(server_id) = self.language_server_ids.get(&key) { + if let Some(LanguageServerState::Running { adapter, server }) = + self.language_servers.get(&server_id) + { + return Some((adapter, server)); + } + } } + + None } } @@ -5741,8 +5803,16 @@ impl Entity for Project { let shutdown_futures = self .language_servers .drain() - .filter_map(|(_, (_, server))| server.shutdown()) + .filter_map(|(_, server_state)| { + // TODO: Handle starting servers? + if let LanguageServerState::Running { server, .. } = server_state { + server.shutdown() + } else { + None + } + }) .collect::>(); + Some( async move { futures::future::join_all(shutdown_futures).await;