diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index fef7ace24f..189c455258 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -78,10 +78,7 @@ pub trait LspAdapter: 'static + Send + Sync { http: Arc, container_dir: PathBuf, ) -> Result; - async fn cached_server_binary( - &self, - container_dir: PathBuf, - ) -> BoxFuture<'static, Option>; + async fn cached_server_binary(&self, container_dir: PathBuf) -> Option; async fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {} @@ -110,11 +107,11 @@ pub trait LspAdapter: 'static + Send + Sync { None } - async fn disk_based_diagnostic_sources(&self) -> &'static [&'static str] { + async fn disk_based_diagnostic_sources(&self) -> Vec { Default::default() } - async fn disk_based_diagnostics_progress_token(&self) -> Option<&'static str> { + async fn disk_based_diagnostics_progress_token(&self) -> Option { None } @@ -179,8 +176,8 @@ pub struct FakeLspAdapter { pub name: &'static str, pub capabilities: lsp::ServerCapabilities, pub initializer: Option>, - pub disk_based_diagnostics_progress_token: Option<&'static str>, - pub disk_based_diagnostics_sources: &'static [&'static str], + pub disk_based_diagnostics_progress_token: Option, + pub disk_based_diagnostics_sources: Vec, } #[derive(Clone, Debug, Deserialize)] @@ -359,7 +356,7 @@ impl LanguageRegistry { let server_binary_path = this .lsp_binary_paths .lock() - .entry(adapter.name()) + .entry(adapter.name().await) .or_insert_with(|| { get_server_binary_path( adapter.clone(), @@ -376,7 +373,7 @@ impl LanguageRegistry { .map_err(|e| anyhow!(e)); let server_binary_path = server_binary_path.await?; - let server_args = adapter.server_args(); + let server_args = adapter.server_args().await; let server = lsp::LanguageServer::new( server_id, &server_binary_path, @@ -402,7 +399,7 @@ async fn get_server_binary_path( download_dir: Arc, statuses: async_broadcast::Sender<(Arc, LanguageServerBinaryStatus)>, ) -> Result { - let container_dir: Arc = download_dir.join(adapter.name().0.as_ref()).into(); + let container_dir = download_dir.join(adapter.name().await.0.as_ref()); if !container_dir.exists() { smol::fs::create_dir_all(&container_dir) .await @@ -544,32 +541,42 @@ impl Language { self.config.line_comment.as_deref() } - pub fn disk_based_diagnostic_sources(&self) -> &'static [&'static str] { - self.adapter.as_ref().map_or(&[] as &[_], |adapter| { - adapter.disk_based_diagnostic_sources() - }) - } - - pub fn disk_based_diagnostics_progress_token(&self) -> Option<&'static str> { - self.adapter - .as_ref() - .and_then(|adapter| adapter.disk_based_diagnostics_progress_token()) - } - - pub fn process_diagnostics(&self, diagnostics: &mut lsp::PublishDiagnosticsParams) { - if let Some(processor) = self.adapter.as_ref() { - processor.process_diagnostics(diagnostics); + pub async fn disk_based_diagnostic_sources(&self) -> Vec { + match self.adapter.as_ref() { + Some(adapter) => adapter.disk_based_diagnostic_sources().await, + None => Vec::new(), } } - pub fn label_for_completion(&self, completion: &lsp::CompletionItem) -> Option { + pub async fn disk_based_diagnostics_progress_token(&self) -> Option { + if let Some(adapter) = self.adapter.as_ref() { + adapter.disk_based_diagnostics_progress_token().await + } else { + None + } + } + + pub async fn process_diagnostics(&self, diagnostics: &mut lsp::PublishDiagnosticsParams) { + if let Some(processor) = self.adapter.as_ref() { + processor.process_diagnostics(diagnostics).await; + } + } + + pub async fn label_for_completion( + &self, + completion: &lsp::CompletionItem, + ) -> Option { self.adapter .as_ref()? .label_for_completion(completion, self) + .await } - pub fn label_for_symbol(&self, name: &str, kind: lsp::SymbolKind) -> Option { - self.adapter.as_ref()?.label_for_symbol(name, kind, self) + pub async fn label_for_symbol(&self, name: &str, kind: lsp::SymbolKind) -> Option { + self.adapter + .as_ref()? + .label_for_symbol(name, kind, self) + .await } pub fn highlight_text<'a>( @@ -678,45 +685,46 @@ impl Default for FakeLspAdapter { capabilities: lsp::LanguageServer::full_capabilities(), initializer: None, disk_based_diagnostics_progress_token: None, - disk_based_diagnostics_sources: &[], + disk_based_diagnostics_sources: Vec::new(), } } } #[cfg(any(test, feature = "test-support"))] +#[async_trait] impl LspAdapter for FakeLspAdapter { - fn name(&self) -> LanguageServerName { + async fn name(&self) -> LanguageServerName { LanguageServerName(self.name.into()) } - fn fetch_latest_server_version( + async fn fetch_latest_server_version( &self, _: Arc, - ) -> BoxFuture<'static, Result>> { + ) -> Result> { unreachable!(); } - fn fetch_server_binary( + async fn fetch_server_binary( &self, _: Box, _: Arc, - _: Arc, - ) -> BoxFuture<'static, Result> { + _: PathBuf, + ) -> Result { unreachable!(); } - fn cached_server_binary(&self, _: Arc) -> BoxFuture<'static, Option> { + async fn cached_server_binary(&self, _: PathBuf) -> Option { unreachable!(); } - fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {} + async fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {} - fn disk_based_diagnostic_sources(&self) -> &'static [&'static str] { - self.disk_based_diagnostics_sources + async fn disk_based_diagnostic_sources(&self) -> Vec { + self.disk_based_diagnostics_sources.clone() } - fn disk_based_diagnostics_progress_token(&self) -> Option<&'static str> { - self.disk_based_diagnostics_progress_token + async fn disk_based_diagnostics_progress_token(&self) -> Option { + self.disk_based_diagnostics_progress_token.clone() } } diff --git a/crates/language/src/proto.rs b/crates/language/src/proto.rs index 7c7ec65fd8..f1d7a73872 100644 --- a/crates/language/src/proto.rs +++ b/crates/language/src/proto.rs @@ -397,7 +397,7 @@ pub fn serialize_completion(completion: &Completion) -> proto::Completion { } } -pub fn deserialize_completion( +pub async fn deserialize_completion( completion: proto::Completion, language: Option<&Arc>, ) -> Result { @@ -413,12 +413,17 @@ pub fn deserialize_completion( Ok(Completion { old_range: old_start..old_end, new_text: completion.new_text, - label: language - .and_then(|l| l.label_for_completion(&lsp_completion)) - .unwrap_or(CodeLabel::plain( + label: { + let label = match language { + Some(l) => l.label_for_completion(&lsp_completion).await, + None => None, + }; + + label.unwrap_or(CodeLabel::plain( lsp_completion.label.clone(), lsp_completion.filter_text.as_deref(), - )), + )) + }, lsp_completion, }) } diff --git a/crates/plugin_macros/src/lib.rs b/crates/plugin_macros/src/lib.rs index 8a008dffd0..7993b3df62 100644 --- a/crates/plugin_macros/src/lib.rs +++ b/crates/plugin_macros/src/lib.rs @@ -2,9 +2,7 @@ use core::panic; use proc_macro::TokenStream; use quote::{format_ident, quote}; -use syn::{ - parse_macro_input, Block, FnArg, ForeignItemFn, Ident, ItemFn, Pat, PatIdent, Type, Visibility, -}; +use syn::{parse_macro_input, Block, FnArg, ForeignItemFn, Ident, ItemFn, Pat, Type, Visibility}; /// Attribute macro to be used guest-side within a plugin. /// ```ignore diff --git a/crates/plugin_runtime/src/plugin.rs b/crates/plugin_runtime/src/plugin.rs index a276213dcc..e4cd05db83 100644 --- a/crates/plugin_runtime/src/plugin.rs +++ b/crates/plugin_runtime/src/plugin.rs @@ -1,5 +1,5 @@ use std::future::Future; -use std::pin::Pin; + use std::{fs::File, marker::PhantomData, path::Path}; use anyhow::{anyhow, Error}; diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 0ac3064e56..ad84ad1e39 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -733,8 +733,9 @@ impl Project { for language in self.languages.to_vec() { 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.language_server_ids.keys() { + let lsp_name = lsp_adapter.name().await; + // .await; + for (worktree_id, started_lsp_name) in self.started_language_servers.keys() { if lsp_name == *started_lsp_name { language_servers_to_stop.push((*worktree_id, started_lsp_name.clone())); } @@ -1648,11 +1649,10 @@ impl Project { this.create_local_worktree(&abs_path, false, cx) }) .await?; + let name = lsp_adapter.name().await; this.update(&mut cx, |this, cx| { - this.language_server_ids.insert( - (worktree.read(cx).id(), language_server_name), - language_server_id, - ); + this.language_servers + .insert((worktree.read(cx).id(), name), (lsp_adapter, lsp_server)); }); (worktree, PathBuf::new()) }; @@ -1799,10 +1799,10 @@ impl Project { Ok(()) } - fn register_buffer_with_language_server( + async fn register_buffer_with_language_server( &mut self, buffer_handle: &ModelHandle, - cx: &mut ModelContext, + cx: &mut ModelContext<'_, Self>, ) { let buffer = buffer_handle.read(cx); let buffer_id = buffer.remote_id(); @@ -1816,7 +1816,7 @@ impl Project { if let Some(language) = buffer.language() { let worktree_id = file.worktree_id(cx); if let Some(adapter) = language.lsp_adapter() { - language_id = adapter.id_for_language(language.name().as_ref()); + language_id = adapter.id_for_language(language.name().as_ref()).await; language_server = self .language_server_ids .get(&(worktree_id, adapter.name())) @@ -1985,6 +1985,7 @@ impl Project { self.language_server_for_buffer(buffer.read(cx), cx)?; if lsp_adapter .disk_based_diagnostics_progress_token() + .await .is_none() { let server_id = language_server.server_id(); @@ -2074,6 +2075,7 @@ impl Project { self.client.http_client(), cx, ); + self.language_servers.insert( server_id, LanguageServerState::Starting(cx.spawn_weak(|this, mut cx| async move { @@ -2405,7 +2407,6 @@ impl Project { } else { return; }; - let server_name = adapter.name(); let stop = self.stop_language_server(worktree_id, server_name.clone(), cx); cx.spawn_weak(|this, mut cx| async move { @@ -2450,7 +2451,7 @@ impl Project { self.update_diagnostics( server_id, params, - adapter.disk_based_diagnostic_sources(), + adapter.disk_based_diagnostic_sources().await, cx, ) .log_err(); @@ -2460,7 +2461,7 @@ impl Project { &mut self, progress: lsp::ProgressParams, server_id: usize, - disk_based_diagnostics_progress_token: Option<&str>, + disk_based_diagnostics_progress_token: Option, cx: &mut ModelContext, ) { let token = match progress.token { @@ -2486,7 +2487,7 @@ impl Project { match progress { lsp::WorkDoneProgress::Begin(report) => { - if Some(token.as_str()) == disk_based_diagnostics_progress_token { + if Some(token) == disk_based_diagnostics_progress_token { language_server_status.has_pending_diagnostic_updates = true; self.disk_based_diagnostics_started(server_id, cx); self.broadcast_language_server_update( @@ -2517,7 +2518,7 @@ impl Project { } } lsp::WorkDoneProgress::Report(report) => { - if Some(token.as_str()) != disk_based_diagnostics_progress_token { + if Some(token) != disk_based_diagnostics_progress_token { self.on_lsp_work_progress( server_id, token.clone(), @@ -2543,7 +2544,7 @@ impl Project { lsp::WorkDoneProgress::End(_) => { language_server_status.progress_tokens.remove(&token); - if Some(token.as_str()) == disk_based_diagnostics_progress_token { + if Some(token) == disk_based_diagnostics_progress_token { language_server_status.has_pending_diagnostic_updates = false; self.disk_based_diagnostics_finished(server_id, cx); self.broadcast_language_server_update( @@ -2692,7 +2693,7 @@ impl Project { &mut self, language_server_id: usize, params: lsp::PublishDiagnosticsParams, - disk_based_sources: &[&str], + disk_based_sources: Vec, cx: &mut ModelContext, ) -> Result<()> { let abs_path = params @@ -2734,9 +2735,8 @@ impl Project { ); } else { let group_id = post_inc(&mut self.next_diagnostic_group_id); - let is_disk_based = source.map_or(false, |source| { - disk_based_sources.contains(&source.as_str()) - }); + let is_disk_based = + source.map_or(false, |source| disk_based_sources.contains(&source)); sources_by_group_id.insert(group_id, source); primary_diagnostic_group_ids @@ -3307,7 +3307,9 @@ impl Project { .languages .select_language(&path) .and_then(|language| { - language.label_for_symbol(&lsp_symbol.name, lsp_symbol.kind) + language + .label_for_symbol(&lsp_symbol.name, lsp_symbol.kind) + .await }) .unwrap_or_else(|| CodeLabel::plain(lsp_symbol.name.clone(), None)); let signature = this.symbol_signature(worktree_id, &path); @@ -3315,7 +3317,7 @@ impl Project { Some(Symbol { source_worktree_id, worktree_id, - language_server_name: adapter.name(), + language_server_name: adapter.name().await, name: lsp_symbol.name, kind: lsp_symbol.kind, label, @@ -3339,10 +3341,9 @@ impl Project { if let Some(this) = this.upgrade(&cx) { this.read_with(&cx, |this, _| { symbols.extend( - response - .symbols - .into_iter() - .filter_map(|symbol| this.deserialize_symbol(symbol).log_err()), + response.symbols.into_iter().filter_map(|symbol| { + this.deserialize_symbol(symbol).log_err().await + }), ); }) } @@ -3548,7 +3549,7 @@ impl Project { new_text, label: language .as_ref() - .and_then(|l| l.label_for_completion(&lsp_completion)) + .and_then(|l| l.label_for_completion(&lsp_completion).await) .unwrap_or_else(|| { CodeLabel::plain( lsp_completion.label.clone(), @@ -3582,7 +3583,7 @@ impl Project { .completions .into_iter() .map(|completion| { - language::proto::deserialize_completion(completion, language.as_ref()) + language::proto::deserialize_completion(completion, language.as_ref()).await }) .collect() }) @@ -5198,7 +5199,8 @@ impl Project { .completion .ok_or_else(|| anyhow!("invalid completion"))?, language, - )?; + ) + .await?; Ok::<_, anyhow::Error>( this.apply_additional_edits_for_completion(buffer, completion, false, cx), ) @@ -5386,7 +5388,7 @@ impl Project { .symbol .ok_or_else(|| anyhow!("invalid symbol"))?; let symbol = this.read_with(&cx, |this, _| { - let symbol = this.deserialize_symbol(symbol)?; + let symbol = this.deserialize_symbol(symbol).await?; let signature = this.symbol_signature(symbol.worktree_id, &symbol.path); if signature == symbol.signature { Ok(symbol) @@ -5591,7 +5593,7 @@ impl Project { }) } - fn deserialize_symbol(&self, serialized_symbol: proto::Symbol) -> Result { + async fn deserialize_symbol(&self, serialized_symbol: proto::Symbol) -> Result { let source_worktree_id = WorktreeId::from_proto(serialized_symbol.source_worktree_id); let worktree_id = WorktreeId::from_proto(serialized_symbol.worktree_id); let start = serialized_symbol @@ -5607,9 +5609,18 @@ impl Project { source_worktree_id, worktree_id, language_server_name: LanguageServerName(serialized_symbol.language_server_name.into()), - label: language - .and_then(|language| language.label_for_symbol(&serialized_symbol.name, kind)) - .unwrap_or_else(|| CodeLabel::plain(serialized_symbol.name.clone(), None)), + label: { + match language { + Some(language) => { + language + .label_for_symbol(&serialized_symbol.name, kind) + .await + } + None => None, + } + .unwrap_or_else(|| CodeLabel::plain(serialized_symbol.name.clone(), None)) + }, + name: serialized_symbol.name, path, range: PointUtf16::new(start.row, start.column)..PointUtf16::new(end.row, end.column), @@ -5864,6 +5875,8 @@ impl Project { cx: &AppContext, ) -> Option<(&Arc, &Arc)> { if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) { + // TODO(isaac): this is not a good idea + let name = cx.background().block(language.lsp_adapter()?.name()); let worktree_id = file.worktree_id(cx); let key = (worktree_id, language.lsp_adapter()?.name());