From 2fcdcac080e070724f9a75bdec5d4546abbf6df6 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 11 Feb 2022 14:41:19 +0100 Subject: [PATCH] Fetch code actions on cursor movement instead of on-demand Co-Authored-By: Nathan Sobo --- crates/editor/src/editor.rs | 93 +++++++++++++++++++------------ crates/find/src/find.rs | 2 +- crates/gpui/src/app.rs | 66 ++++++++++++++++------ crates/gpui/src/presenter.rs | 18 +++++- crates/lsp/src/lsp.rs | 11 +--- crates/project/src/project.rs | 2 +- crates/project/src/worktree.rs | 4 +- crates/workspace/src/pane.rs | 2 +- crates/workspace/src/workspace.rs | 6 +- 9 files changed, 134 insertions(+), 70 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 8aa922eb5b..c10650fefc 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -430,6 +430,8 @@ pub struct Editor { context_menu: Option, completion_tasks: Vec<(CompletionId, Task>)>, next_completion_id: CompletionId, + available_code_actions: Option, + code_actions_task: Option>, } pub struct EditorSnapshot { @@ -656,6 +658,7 @@ impl CompletionsMenu { } } +#[derive(Clone)] struct CodeActionsMenu { actions: Arc<[CodeAction]>, buffer: ModelHandle, @@ -861,6 +864,8 @@ impl Editor { context_menu: None, completion_tasks: Default::default(), next_completion_id: 0, + available_code_actions: Default::default(), + code_actions_task: Default::default(), }; this.end_selection(cx); this @@ -1924,7 +1929,7 @@ impl Editor { menu.filter(query.as_deref(), cx.background()).await; - if let Some(this) = cx.read(|cx| this.upgrade(cx)) { + if let Some(this) = this.upgrade(&cx) { this.update(&mut cx, |this, cx| { match this.context_menu.as_ref() { None => {} @@ -2057,32 +2062,23 @@ impl Editor { } fn show_code_actions(&mut self, _: &ShowCodeActions, cx: &mut ViewContext) { - let head = self.newest_anchor_selection().head(); - let project = if let Some(project) = self.project.clone() { - project - } else { - return; - }; + let mut task = self.code_actions_task.take(); + cx.spawn_weak(|this, mut cx| async move { + while let Some(prev_task) = task { + prev_task.await; + task = this + .upgrade(&cx) + .and_then(|this| this.update(&mut cx, |this, _| this.code_actions_task.take())); + } - let (buffer, head) = self.buffer.read(cx).text_anchor_for_position(head, cx); - let actions = project.update(cx, |project, cx| project.code_actions(&buffer, head, cx)); - - cx.spawn(|this, mut cx| async move { - let actions = actions.await?; - if !actions.is_empty() { + if let Some(this) = this.upgrade(&cx) { this.update(&mut cx, |this, cx| { if this.focused { - this.show_context_menu( - ContextMenu::CodeActions(CodeActionsMenu { - buffer, - actions: actions.into(), - selected_item: 0, - list: UniformListState::default(), - }), - cx, - ); + if let Some(menu) = this.available_code_actions.clone() { + this.show_context_menu(ContextMenu::CodeActions(menu), cx); + } } - }); + }) } Ok::<_, anyhow::Error>(()) }) @@ -4357,15 +4353,14 @@ impl Editor { .selections .iter() .max_by_key(|s| s.id) - .map(|s| s.head()); + .map(|s| s.head()) + .unwrap(); - if let Some(new_cursor_position) = new_cursor_position.as_ref() { - self.push_to_nav_history( - old_cursor_position, - Some(new_cursor_position.to_point(&buffer)), - cx, - ); - } + self.push_to_nav_history( + old_cursor_position, + Some(new_cursor_position.to_point(&buffer)), + cx, + ); let completion_menu = match self.context_menu.as_mut() { Some(ContextMenu::Completions(menu)) => Some(menu), @@ -4375,8 +4370,8 @@ impl Editor { } }; - if let Some((completion_menu, cursor_position)) = completion_menu.zip(new_cursor_position) { - let cursor_position = cursor_position.to_offset(&buffer); + if let Some(completion_menu) = completion_menu { + let cursor_position = new_cursor_position.to_offset(&buffer); let (word_range, kind) = buffer.surrounding_word(completion_menu.initial_position.clone()); if kind == Some(CharKind::Word) && word_range.to_inclusive().contains(&cursor_position) @@ -4390,6 +4385,34 @@ impl Editor { } } + if let Some(project) = self.project.as_ref() { + let (buffer, head) = self + .buffer + .read(cx) + .text_anchor_for_position(new_cursor_position, cx); + let actions = project.update(cx, |project, cx| project.code_actions(&buffer, head, cx)); + self.code_actions_task = Some(cx.spawn_weak(|this, mut cx| async move { + let actions = actions.await; + if let Some(this) = this.upgrade(&cx) { + this.update(&mut cx, |this, cx| { + this.available_code_actions = actions.log_err().and_then(|actions| { + if actions.is_empty() { + None + } else { + Some(CodeActionsMenu { + actions: actions.into(), + buffer, + selected_item: 0, + list: Default::default(), + }) + } + }); + cx.notify(); + }) + } + })); + } + self.pause_cursor_blinking(cx); cx.emit(Event::SelectionsChanged); } @@ -4703,7 +4726,7 @@ impl Editor { let this = this.downgrade(); async move { Timer::after(CURSOR_BLINK_INTERVAL).await; - if let Some(this) = cx.read(|cx| this.upgrade(cx)) { + if let Some(this) = this.upgrade(&cx) { this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx)) } } @@ -4728,7 +4751,7 @@ impl Editor { let this = this.downgrade(); async move { Timer::after(CURSOR_BLINK_INTERVAL).await; - if let Some(this) = cx.read(|cx| this.upgrade(cx)) { + if let Some(this) = this.upgrade(&cx) { this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx)); } } diff --git a/crates/find/src/find.rs b/crates/find/src/find.rs index 524af7d183..47d31cf765 100644 --- a/crates/find/src/find.rs +++ b/crates/find/src/find.rs @@ -464,7 +464,7 @@ impl FindBar { self.pending_search = Some(cx.spawn(|this, mut cx| async move { match ranges.await { Ok(ranges) => { - if let Some(editor) = cx.read(|cx| editor.upgrade(cx)) { + if let Some(editor) = editor.upgrade(&cx) { this.update(&mut cx, |this, cx| { this.highlighted_editors.insert(editor.downgrade()); editor.update(cx, |editor, cx| { diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index bd81f81eb1..337c9b29ea 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -80,8 +80,14 @@ pub trait UpdateModel { } pub trait UpgradeModelHandle { - fn upgrade_model_handle(&self, handle: WeakModelHandle) - -> Option>; + fn upgrade_model_handle( + &self, + handle: &WeakModelHandle, + ) -> Option>; +} + +pub trait UpgradeViewHandle { + fn upgrade_view_handle(&self, handle: &WeakViewHandle) -> Option>; } pub trait ReadView { @@ -558,12 +564,18 @@ impl UpdateModel for AsyncAppContext { impl UpgradeModelHandle for AsyncAppContext { fn upgrade_model_handle( &self, - handle: WeakModelHandle, + handle: &WeakModelHandle, ) -> Option> { self.0.borrow_mut().upgrade_model_handle(handle) } } +impl UpgradeViewHandle for AsyncAppContext { + fn upgrade_view_handle(&self, handle: &WeakViewHandle) -> Option> { + self.0.borrow_mut().upgrade_view_handle(handle) + } +} + impl ReadModelWith for AsyncAppContext { fn read_model_with( &self, @@ -1732,12 +1744,18 @@ impl UpdateModel for MutableAppContext { impl UpgradeModelHandle for MutableAppContext { fn upgrade_model_handle( &self, - handle: WeakModelHandle, + handle: &WeakModelHandle, ) -> Option> { self.cx.upgrade_model_handle(handle) } } +impl UpgradeViewHandle for MutableAppContext { + fn upgrade_view_handle(&self, handle: &WeakViewHandle) -> Option> { + self.cx.upgrade_view_handle(handle) + } +} + impl ReadView for MutableAppContext { fn read_view(&self, handle: &ViewHandle) -> &T { if let Some(view) = self.cx.views.get(&(handle.window_id, handle.view_id)) { @@ -1846,7 +1864,7 @@ impl ReadModel for AppContext { impl UpgradeModelHandle for AppContext { fn upgrade_model_handle( &self, - handle: WeakModelHandle, + handle: &WeakModelHandle, ) -> Option> { if self.models.contains_key(&handle.model_id) { Some(ModelHandle::new(handle.model_id, &self.ref_counts)) @@ -1856,6 +1874,20 @@ impl UpgradeModelHandle for AppContext { } } +impl UpgradeViewHandle for AppContext { + fn upgrade_view_handle(&self, handle: &WeakViewHandle) -> Option> { + if self.ref_counts.lock().is_entity_alive(handle.view_id) { + Some(ViewHandle::new( + handle.window_id, + handle.view_id, + &self.ref_counts, + )) + } else { + None + } + } +} + impl ReadView for AppContext { fn read_view(&self, handle: &ViewHandle) -> &T { if let Some(view) = self.views.get(&(handle.window_id, handle.view_id)) { @@ -2228,7 +2260,7 @@ impl UpdateModel for ModelContext<'_, M> { impl UpgradeModelHandle for ModelContext<'_, M> { fn upgrade_model_handle( &self, - handle: WeakModelHandle, + handle: &WeakModelHandle, ) -> Option> { self.cx.upgrade_model_handle(handle) } @@ -2558,12 +2590,18 @@ impl ReadModel for ViewContext<'_, V> { impl UpgradeModelHandle for ViewContext<'_, V> { fn upgrade_model_handle( &self, - handle: WeakModelHandle, + handle: &WeakModelHandle, ) -> Option> { self.cx.upgrade_model_handle(handle) } } +impl UpgradeViewHandle for ViewContext<'_, V> { + fn upgrade_view_handle(&self, handle: &WeakViewHandle) -> Option> { + self.cx.upgrade_view_handle(handle) + } +} + impl UpdateModel for ViewContext<'_, V> { fn update_model( &mut self, @@ -2861,7 +2899,7 @@ impl WeakModelHandle { self.model_id } - pub fn upgrade(self, cx: &impl UpgradeModelHandle) -> Option> { + pub fn upgrade(&self, cx: &impl UpgradeModelHandle) -> Option> { cx.upgrade_model_handle(self) } } @@ -3277,16 +3315,8 @@ impl WeakViewHandle { self.view_id } - pub fn upgrade(&self, cx: &AppContext) -> Option> { - if cx.ref_counts.lock().is_entity_alive(self.view_id) { - Some(ViewHandle::new( - self.window_id, - self.view_id, - &cx.ref_counts, - )) - } else { - None - } + pub fn upgrade(&self, cx: &impl UpgradeViewHandle) -> Option> { + cx.upgrade_view_handle(self) } } diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index 2666a329f0..1b5adbc994 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -7,7 +7,8 @@ use crate::{ platform::Event, text_layout::TextLayoutCache, Action, AnyAction, AnyViewHandle, AssetCache, ElementBox, Entity, FontSystem, ModelHandle, - ReadModel, ReadView, Scene, View, ViewHandle, + ReadModel, ReadView, Scene, UpgradeModelHandle, UpgradeViewHandle, View, ViewHandle, + WeakModelHandle, WeakViewHandle, }; use pathfinder_geometry::vector::{vec2f, Vector2F}; use serde_json::json; @@ -270,6 +271,21 @@ impl<'a> ReadModel for LayoutContext<'a> { } } +impl<'a> UpgradeModelHandle for LayoutContext<'a> { + fn upgrade_model_handle( + &self, + handle: &WeakModelHandle, + ) -> Option> { + self.app.upgrade_model_handle(handle) + } +} + +impl<'a> UpgradeViewHandle for LayoutContext<'a> { + fn upgrade_view_handle(&self, handle: &WeakViewHandle) -> Option> { + self.app.upgrade_view_handle(handle) + } +} + pub struct PaintContext<'a> { rendered_views: &'a mut HashMap, pub scene: &'a mut Scene, diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index b3cb2abb70..4c673e2ff4 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -664,14 +664,9 @@ mod tests { })); let lib_file_uri = Url::from_file_path(root_dir.path().join("src/lib.rs")).unwrap(); - let server = cx.read(|cx| { - LanguageServer::new( - Path::new("rust-analyzer"), - root_dir.path(), - cx.background().clone(), - ) - .unwrap() - }); + let server = + LanguageServer::new(Path::new("rust-analyzer"), root_dir.path(), cx.background()) + .unwrap(); server.next_idle_notification().await; server diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index c12535f557..36d04ae9ee 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -867,7 +867,7 @@ impl Project { // Process all the LSP events. cx.spawn_weak(|this, mut cx| async move { while let Ok(message) = diagnostics_rx.recv().await { - let this = cx.read(|cx| this.upgrade(cx))?; + let this = this.upgrade(&cx)?; match message { LspEvent::DiagnosticsStart => { this.update(&mut cx, |this, cx| { diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 94cfdb3150..ba59afd9b9 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -291,7 +291,7 @@ impl Worktree { let this = worktree_handle.downgrade(); cx.spawn(|mut cx| async move { while let Some(_) = snapshot_rx.recv().await { - if let Some(this) = cx.read(|cx| this.upgrade(cx)) { + if let Some(this) = this.upgrade(&cx) { this.update(&mut cx, |this, cx| this.poll_snapshot(cx)); } else { break; @@ -516,7 +516,7 @@ impl LocalWorktree { cx.spawn_weak(|this, mut cx| async move { while let Ok(scan_state) = scan_states_rx.recv().await { - if let Some(handle) = cx.read(|cx| this.upgrade(cx)) { + if let Some(handle) = this.upgrade(&cx) { let to_send = handle.update(&mut cx, |this, cx| { last_scan_state_tx.blocking_send(scan_state).ok(); this.poll_snapshot(cx); diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index fc2681161c..8c7c001e33 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -221,7 +221,7 @@ impl Pane { let task = workspace.load_path(project_path, cx); cx.spawn(|workspace, mut cx| async move { let item = task.await; - if let Some(pane) = cx.read(|cx| pane.upgrade(cx)) { + if let Some(pane) = pane.upgrade(&cx) { if let Some(item) = item.log_err() { workspace.update(&mut cx, |workspace, cx| { pane.update(cx, |p, _| p.nav_history.borrow_mut().set_mode(mode)); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 3cf63ade2e..2203e8cbf7 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -326,7 +326,7 @@ impl WeakItemHandle for WeakModelHandle { } fn upgrade(&self, cx: &AppContext) -> Option> { - WeakModelHandle::::upgrade(*self, cx).map(|i| Box::new(i) as Box) + WeakModelHandle::::upgrade(self, cx).map(|i| Box::new(i) as Box) } } @@ -591,7 +591,7 @@ impl Workspace { while stream.recv().await.is_some() { cx.update(|cx| { - if let Some(this) = this.upgrade(&cx) { + if let Some(this) = this.upgrade(cx) { this.update(cx, |_, cx| cx.notify()); } }) @@ -774,7 +774,7 @@ impl Workspace { let item = load_task.await?; this.update(&mut cx, |this, cx| { let pane = pane - .upgrade(&cx) + .upgrade(cx) .ok_or_else(|| anyhow!("could not upgrade pane reference"))?; Ok(this.open_item_in_pane(item, &pane, cx)) })