Detailed changes
@@ -654,7 +654,8 @@ impl CompletionsMenu {
}
struct CodeActionsMenu {
- actions: Arc<[CodeAction<Anchor>]>,
+ actions: Arc<[CodeAction]>,
+ buffer: ModelHandle<Buffer>,
selected_item: usize,
list: UniformListState,
}
@@ -2021,24 +2022,40 @@ impl Editor {
}))
}
- fn show_code_actions(&mut self, _: &ShowCodeActions, cx: &mut ViewContext<Self>) {
- let position = if let Some(selection) = self.newest_anchor_selection() {
- selection.head()
+ fn show_code_actions(
+ workspace: &mut Workspace,
+ _: &ShowCodeActions,
+ cx: &mut ViewContext<Workspace>,
+ ) {
+ let active_item = workspace.active_item(cx);
+ let editor_handle = if let Some(editor) = active_item
+ .as_ref()
+ .and_then(|item| item.act_as::<Self>(cx))
+ {
+ editor
} else {
return;
};
- let actions = self
- .buffer
- .update(cx, |buffer, cx| buffer.code_actions(position.clone(), cx));
+ let editor = editor_handle.read(cx);
+ let head = if let Some(selection) = editor.newest_anchor_selection() {
+ selection.head()
+ } else {
+ return;
+ };
+ let (buffer, head) = editor.buffer.read(cx).text_anchor_for_position(head, cx);
+ let actions = workspace
+ .project()
+ .update(cx, |project, cx| project.code_actions(&buffer, head, cx));
- cx.spawn(|this, mut cx| async move {
+ cx.spawn(|_, mut cx| async move {
let actions = actions.await?;
if !actions.is_empty() {
- this.update(&mut cx, |this, cx| {
+ editor_handle.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(),
@@ -2069,15 +2086,7 @@ impl Editor {
};
let action_ix = action_ix.unwrap_or(actions_menu.selected_item);
let action = actions_menu.actions.get(action_ix)?.clone();
- let (buffer, position) = editor
- .buffer
- .read(cx)
- .text_anchor_for_position(action.position, cx);
- let action = CodeAction {
- position,
- lsp_action: action.lsp_action,
- };
- Some((buffer, action))
+ Some((actions_menu.buffer, action))
})?;
let apply_code_actions = workspace.project().update(cx, |project, cx| {
@@ -5,11 +5,11 @@ use anyhow::Result;
use clock::ReplicaId;
use collections::{HashMap, HashSet};
use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task};
+pub use language::Completion;
use language::{
Buffer, BufferChunks, BufferSnapshot, Chunk, DiagnosticEntry, Event, File, Language, Outline,
OutlineItem, Selection, ToOffset as _, ToPoint as _, ToPointUtf16 as _, TransactionId,
};
-pub use language::{CodeAction, Completion};
use std::{
cell::{Ref, RefCell},
cmp, fmt, io,
@@ -861,31 +861,6 @@ impl MultiBuffer {
})
}
- pub fn code_actions<T>(
- &self,
- position: T,
- cx: &mut ModelContext<Self>,
- ) -> Task<Result<Vec<CodeAction<Anchor>>>>
- where
- T: ToOffset,
- {
- let anchor = self.read(cx).anchor_before(position);
- let buffer = self.buffers.borrow()[&anchor.buffer_id].buffer.clone();
- let code_actions = buffer.update(cx, |buffer, cx| {
- buffer.code_actions(anchor.text_anchor.clone(), cx)
- });
- cx.foreground().spawn(async move {
- Ok(code_actions
- .await?
- .into_iter()
- .map(|action| CodeAction {
- position: anchor.clone(),
- lsp_action: action.lsp_action,
- })
- .collect())
- })
- }
-
pub fn completions<T>(
&self,
position: T,
@@ -119,8 +119,8 @@ pub struct Completion<T> {
}
#[derive(Clone, Debug)]
-pub struct CodeAction<T> {
- pub position: T,
+pub struct CodeAction {
+ pub position: Anchor,
pub lsp_action: lsp::CodeAction,
}
@@ -216,13 +216,6 @@ pub trait File {
cx: &mut MutableAppContext,
) -> Task<Result<Option<Transaction>>>;
- fn code_actions(
- &self,
- buffer_id: u64,
- position: Anchor,
- cx: &mut MutableAppContext,
- ) -> Task<Result<Vec<CodeAction<Anchor>>>>;
-
fn buffer_updated(&self, buffer_id: u64, operation: Operation, cx: &mut MutableAppContext);
fn buffer_removed(&self, buffer_id: u64, cx: &mut MutableAppContext);
@@ -311,15 +304,6 @@ impl File for FakeFile {
Task::ready(Ok(Default::default()))
}
- fn code_actions(
- &self,
- _: u64,
- _: Anchor,
- _: &mut MutableAppContext,
- ) -> Task<Result<Vec<CodeAction<Anchor>>>> {
- Task::ready(Ok(Default::default()))
- }
-
fn buffer_updated(&self, _: u64, _: Operation, _: &mut MutableAppContext) {}
fn buffer_removed(&self, _: u64, _: &mut MutableAppContext) {}
@@ -1861,72 +1845,6 @@ impl Buffer {
}
}
- pub fn code_actions<T>(
- &self,
- position: T,
- cx: &mut ModelContext<Self>,
- ) -> Task<Result<Vec<CodeAction<Anchor>>>>
- where
- T: ToPointUtf16,
- {
- let file = if let Some(file) = self.file.as_ref() {
- file
- } else {
- return Task::ready(Ok(Default::default()));
- };
- let position = position.to_point_utf16(self);
- let anchor = self.anchor_after(position);
-
- if let Some(file) = file.as_local() {
- let server = if let Some(language_server) = self.language_server.as_ref() {
- language_server.server.clone()
- } else {
- return Task::ready(Ok(Default::default()));
- };
- let abs_path = file.abs_path(cx);
-
- cx.foreground().spawn(async move {
- let actions = server
- .request::<lsp::request::CodeActionRequest>(lsp::CodeActionParams {
- text_document: lsp::TextDocumentIdentifier::new(
- lsp::Url::from_file_path(abs_path).unwrap(),
- ),
- range: lsp::Range::new(
- position.to_lsp_position(),
- position.to_lsp_position(),
- ),
- work_done_progress_params: Default::default(),
- partial_result_params: Default::default(),
- context: lsp::CodeActionContext {
- diagnostics: Default::default(),
- only: Some(vec![
- lsp::CodeActionKind::QUICKFIX,
- lsp::CodeActionKind::REFACTOR,
- lsp::CodeActionKind::REFACTOR_EXTRACT,
- ]),
- },
- })
- .await?
- .unwrap_or_default()
- .into_iter()
- .filter_map(|entry| {
- if let lsp::CodeActionOrCommand::CodeAction(lsp_action) = entry {
- Some(CodeAction {
- position: anchor.clone(),
- lsp_action,
- })
- } else {
- None
- }
- })
- .collect();
- Ok(actions)
- })
- } else {
- file.code_actions(self.remote_id(), anchor, cx.as_mut())
- }
- }
-
pub fn apply_additional_edits_for_completion(
&mut self,
completion: Completion<Anchor>,
@@ -426,14 +426,14 @@ pub fn deserialize_completion(
})
}
-pub fn serialize_code_action(action: &CodeAction<Anchor>) -> proto::CodeAction {
+pub fn serialize_code_action(action: &CodeAction) -> proto::CodeAction {
proto::CodeAction {
position: Some(serialize_anchor(&action.position)),
lsp_action: serde_json::to_vec(&action.lsp_action).unwrap(),
}
}
-pub fn deserialize_code_action(action: proto::CodeAction) -> Result<CodeAction<Anchor>> {
+pub fn deserialize_code_action(action: proto::CodeAction) -> Result<CodeAction> {
let position = action
.position
.and_then(deserialize_anchor)
@@ -1171,10 +1171,107 @@ impl Project {
}
}
+ pub fn code_actions<T: ToPointUtf16>(
+ &self,
+ source_buffer_handle: &ModelHandle<Buffer>,
+ position: T,
+ cx: &mut ModelContext<Self>,
+ ) -> Task<Result<Vec<CodeAction>>> {
+ let source_buffer_handle = source_buffer_handle.clone();
+ let source_buffer = source_buffer_handle.read(cx);
+ let buffer_id = source_buffer.remote_id();
+ let worktree;
+ let buffer_abs_path;
+ if let Some(file) = File::from_dyn(source_buffer.file()) {
+ worktree = file.worktree.clone();
+ buffer_abs_path = file.as_local().map(|f| f.abs_path(cx));
+ } else {
+ return Task::ready(Err(anyhow!("buffer does not belong to any worktree")));
+ };
+
+ let position = position.to_point_utf16(source_buffer);
+ let anchor = source_buffer.anchor_after(position);
+
+ if worktree.read(cx).as_local().is_some() {
+ let buffer_abs_path = buffer_abs_path.unwrap();
+ let lang_name;
+ let lang_server;
+ if let Some(lang) = source_buffer.language() {
+ lang_name = lang.name().to_string();
+ if let Some(server) = self
+ .language_servers
+ .get(&(worktree.read(cx).id(), lang_name.clone()))
+ {
+ lang_server = server.clone();
+ } else {
+ return Task::ready(Err(anyhow!("buffer does not have a language server")));
+ };
+ } else {
+ return Task::ready(Err(anyhow!("buffer does not have a language")));
+ }
+
+ cx.foreground().spawn(async move {
+ let actions = lang_server
+ .request::<lsp::request::CodeActionRequest>(lsp::CodeActionParams {
+ text_document: lsp::TextDocumentIdentifier::new(
+ lsp::Url::from_file_path(buffer_abs_path).unwrap(),
+ ),
+ range: lsp::Range::new(
+ position.to_lsp_position(),
+ position.to_lsp_position(),
+ ),
+ work_done_progress_params: Default::default(),
+ partial_result_params: Default::default(),
+ context: lsp::CodeActionContext {
+ diagnostics: Default::default(),
+ only: Some(vec![
+ lsp::CodeActionKind::QUICKFIX,
+ lsp::CodeActionKind::REFACTOR,
+ lsp::CodeActionKind::REFACTOR_EXTRACT,
+ ]),
+ },
+ })
+ .await?
+ .unwrap_or_default()
+ .into_iter()
+ .filter_map(|entry| {
+ if let lsp::CodeActionOrCommand::CodeAction(lsp_action) = entry {
+ Some(CodeAction {
+ position: anchor.clone(),
+ lsp_action,
+ })
+ } else {
+ None
+ }
+ })
+ .collect();
+ Ok(actions)
+ })
+ } else if let Some(project_id) = self.remote_id() {
+ let rpc = self.client.clone();
+ cx.foreground().spawn(async move {
+ let response = rpc
+ .request(proto::GetCodeActions {
+ project_id,
+ buffer_id,
+ position: Some(language::proto::serialize_anchor(&anchor)),
+ })
+ .await?;
+ response
+ .actions
+ .into_iter()
+ .map(language::proto::deserialize_code_action)
+ .collect()
+ })
+ } else {
+ Task::ready(Err(anyhow!("project does not have a remote id")))
+ }
+ }
+
pub fn apply_code_action(
&self,
buffer_handle: ModelHandle<Buffer>,
- mut action: CodeAction<language::Anchor>,
+ mut action: CodeAction,
push_to_history: bool,
cx: &mut ModelContext<Self>,
) -> Task<Result<ProjectTransaction>> {
@@ -1206,9 +1303,9 @@ impl Project {
.request::<lsp::request::CodeActionResolveRequest>(action.lsp_action)
.await?;
} else {
- let actions = buffer_handle
- .update(&mut cx, |buffer, cx| {
- buffer.code_actions(action.position.clone(), cx)
+ let actions = this
+ .update(&mut cx, |this, cx| {
+ this.code_actions(&buffer_handle, action.position.clone(), cx)
})
.await?;
action.lsp_action = actions
@@ -2018,9 +2115,9 @@ impl Project {
.position
.and_then(language::proto::deserialize_anchor)
.ok_or_else(|| anyhow!("invalid position"))?;
- cx.spawn(|_, mut cx| async move {
- match buffer
- .update(&mut cx, |buffer, cx| buffer.code_actions(position, cx))
+ cx.spawn(|this, mut cx| async move {
+ match this
+ .update(&mut cx, |this, cx| this.code_actions(&buffer, position, cx))
.await
{
Ok(completions) => rpc.respond(
@@ -1474,38 +1474,6 @@ impl language::File for File {
})
}
- fn code_actions(
- &self,
- buffer_id: u64,
- position: Anchor,
- cx: &mut MutableAppContext,
- ) -> Task<Result<Vec<language::CodeAction<Anchor>>>> {
- let worktree = self.worktree.read(cx);
- let worktree = if let Some(worktree) = worktree.as_remote() {
- worktree
- } else {
- return Task::ready(Err(anyhow!(
- "remote code actions requested on a local worktree"
- )));
- };
- let rpc = worktree.client.clone();
- let project_id = worktree.project_id;
- cx.foreground().spawn(async move {
- let response = rpc
- .request(proto::GetCodeActions {
- project_id,
- buffer_id,
- position: Some(language::proto::serialize_anchor(&position)),
- })
- .await?;
- response
- .actions
- .into_iter()
- .map(language::proto::deserialize_code_action)
- .collect()
- })
- }
-
fn buffer_updated(&self, buffer_id: u64, operation: Operation, cx: &mut MutableAppContext) {
self.worktree.update(cx, |worktree, cx| {
worktree.send_buffer_update(buffer_id, operation, cx);