mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-09 05:29:32 +03:00
Consolidate all code actions logic into Project
This commit is contained in:
parent
e0fe8b5a7c
commit
722c84c976
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user