mirror of
https://github.com/zed-industries/zed.git
synced 2025-02-10 20:29:05 +00:00
Use selection instead of just the cursor when fetching code actions
This commit is contained in:
parent
1eea2f3653
commit
fadb94afb2
6 changed files with 88 additions and 63 deletions
|
@ -2230,12 +2230,17 @@ impl Editor {
|
||||||
|
|
||||||
fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
|
fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
|
||||||
let project = self.project.as_ref()?;
|
let project = self.project.as_ref()?;
|
||||||
let new_cursor_position = self.newest_anchor_selection().head();
|
let buffer = self.buffer.read(cx);
|
||||||
let (buffer, head) = self
|
let newest_selection = self.newest_anchor_selection().clone();
|
||||||
.buffer
|
let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
|
||||||
.read(cx)
|
let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
|
||||||
.text_anchor_for_position(new_cursor_position, cx)?;
|
if start_buffer != end_buffer {
|
||||||
let actions = project.update(cx, |project, cx| project.code_actions(&buffer, head, cx));
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let actions = project.update(cx, |project, cx| {
|
||||||
|
project.code_actions(&start_buffer, start..end, cx)
|
||||||
|
});
|
||||||
self.code_actions_task = Some(cx.spawn_weak(|this, mut cx| async move {
|
self.code_actions_task = Some(cx.spawn_weak(|this, mut cx| async move {
|
||||||
let actions = actions.await;
|
let actions = actions.await;
|
||||||
if let Some(this) = this.upgrade(&cx) {
|
if let Some(this) = this.upgrade(&cx) {
|
||||||
|
@ -2244,7 +2249,7 @@ impl Editor {
|
||||||
if actions.is_empty() {
|
if actions.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some((buffer, actions.into()))
|
Some((start_buffer, actions.into()))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
|
|
@ -123,7 +123,7 @@ pub struct Completion {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct CodeAction {
|
pub struct CodeAction {
|
||||||
pub position: Anchor,
|
pub range: Range<Anchor>,
|
||||||
pub lsp_action: lsp::CodeAction,
|
pub lsp_action: lsp::CodeAction,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -428,19 +428,24 @@ pub fn deserialize_completion(
|
||||||
|
|
||||||
pub fn serialize_code_action(action: &CodeAction) -> proto::CodeAction {
|
pub fn serialize_code_action(action: &CodeAction) -> proto::CodeAction {
|
||||||
proto::CodeAction {
|
proto::CodeAction {
|
||||||
position: Some(serialize_anchor(&action.position)),
|
start: Some(serialize_anchor(&action.range.start)),
|
||||||
|
end: Some(serialize_anchor(&action.range.end)),
|
||||||
lsp_action: serde_json::to_vec(&action.lsp_action).unwrap(),
|
lsp_action: serde_json::to_vec(&action.lsp_action).unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deserialize_code_action(action: proto::CodeAction) -> Result<CodeAction> {
|
pub fn deserialize_code_action(action: proto::CodeAction) -> Result<CodeAction> {
|
||||||
let position = action
|
let start = action
|
||||||
.position
|
.start
|
||||||
.and_then(deserialize_anchor)
|
.and_then(deserialize_anchor)
|
||||||
.ok_or_else(|| anyhow!("invalid position"))?;
|
.ok_or_else(|| anyhow!("invalid start"))?;
|
||||||
|
let end = action
|
||||||
|
.end
|
||||||
|
.and_then(deserialize_anchor)
|
||||||
|
.ok_or_else(|| anyhow!("invalid end"))?;
|
||||||
let lsp_action = serde_json::from_slice(&action.lsp_action)?;
|
let lsp_action = serde_json::from_slice(&action.lsp_action)?;
|
||||||
Ok(CodeAction {
|
Ok(CodeAction {
|
||||||
position,
|
range: start..end,
|
||||||
lsp_action,
|
lsp_action,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,9 +15,9 @@ use gpui::{
|
||||||
use language::{
|
use language::{
|
||||||
point_from_lsp,
|
point_from_lsp,
|
||||||
proto::{deserialize_anchor, serialize_anchor},
|
proto::{deserialize_anchor, serialize_anchor},
|
||||||
range_from_lsp, Bias, Buffer, CodeAction, Completion, CompletionLabel, Diagnostic,
|
range_from_lsp, AnchorRangeExt, Bias, Buffer, CodeAction, Completion, CompletionLabel,
|
||||||
DiagnosticEntry, File as _, Language, LanguageRegistry, PointUtf16, ToLspPosition,
|
Diagnostic, DiagnosticEntry, File as _, Language, LanguageRegistry, PointUtf16, ToLspPosition,
|
||||||
ToPointUtf16, Transaction,
|
ToOffset, ToPointUtf16, Transaction,
|
||||||
};
|
};
|
||||||
use lsp::{DiagnosticSeverity, LanguageServer};
|
use lsp::{DiagnosticSeverity, LanguageServer};
|
||||||
use postage::{prelude::Stream, watch};
|
use postage::{prelude::Stream, watch};
|
||||||
|
@ -1474,32 +1474,30 @@ impl Project {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn code_actions<T: ToPointUtf16>(
|
pub fn code_actions<T: ToOffset>(
|
||||||
&self,
|
&self,
|
||||||
source_buffer_handle: &ModelHandle<Buffer>,
|
buffer_handle: &ModelHandle<Buffer>,
|
||||||
position: T,
|
range: Range<T>,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Task<Result<Vec<CodeAction>>> {
|
) -> Task<Result<Vec<CodeAction>>> {
|
||||||
let source_buffer_handle = source_buffer_handle.clone();
|
let buffer_handle = buffer_handle.clone();
|
||||||
let source_buffer = source_buffer_handle.read(cx);
|
let buffer = buffer_handle.read(cx);
|
||||||
let buffer_id = source_buffer.remote_id();
|
let buffer_id = buffer.remote_id();
|
||||||
let worktree;
|
let worktree;
|
||||||
let buffer_abs_path;
|
let buffer_abs_path;
|
||||||
if let Some(file) = File::from_dyn(source_buffer.file()) {
|
if let Some(file) = File::from_dyn(buffer.file()) {
|
||||||
worktree = file.worktree.clone();
|
worktree = file.worktree.clone();
|
||||||
buffer_abs_path = file.as_local().map(|f| f.abs_path(cx));
|
buffer_abs_path = file.as_local().map(|f| f.abs_path(cx));
|
||||||
} else {
|
} else {
|
||||||
return Task::ready(Ok(Default::default()));
|
return Task::ready(Ok(Default::default()));
|
||||||
};
|
};
|
||||||
|
let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
|
||||||
let position = position.to_point_utf16(source_buffer);
|
|
||||||
let anchor = source_buffer.anchor_after(position);
|
|
||||||
|
|
||||||
if worktree.read(cx).as_local().is_some() {
|
if worktree.read(cx).as_local().is_some() {
|
||||||
let buffer_abs_path = buffer_abs_path.unwrap();
|
let buffer_abs_path = buffer_abs_path.unwrap();
|
||||||
let lang_name;
|
let lang_name;
|
||||||
let lang_server;
|
let lang_server;
|
||||||
if let Some(lang) = source_buffer.language() {
|
if let Some(lang) = buffer.language() {
|
||||||
lang_name = lang.name().to_string();
|
lang_name = lang.name().to_string();
|
||||||
if let Some(server) = self
|
if let Some(server) = self
|
||||||
.language_servers
|
.language_servers
|
||||||
|
@ -1513,15 +1511,14 @@ impl Project {
|
||||||
return Task::ready(Ok(Default::default()));
|
return Task::ready(Ok(Default::default()));
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.foreground().spawn(async move {
|
let actions =
|
||||||
let actions = lang_server
|
lang_server.request::<lsp::request::CodeActionRequest>(lsp::CodeActionParams {
|
||||||
.request::<lsp::request::CodeActionRequest>(lsp::CodeActionParams {
|
|
||||||
text_document: lsp::TextDocumentIdentifier::new(
|
text_document: lsp::TextDocumentIdentifier::new(
|
||||||
lsp::Url::from_file_path(buffer_abs_path).unwrap(),
|
lsp::Url::from_file_path(buffer_abs_path).unwrap(),
|
||||||
),
|
),
|
||||||
range: lsp::Range::new(
|
range: lsp::Range::new(
|
||||||
position.to_lsp_position(),
|
range.start.to_point_utf16(buffer).to_lsp_position(),
|
||||||
position.to_lsp_position(),
|
range.end.to_point_utf16(buffer).to_lsp_position(),
|
||||||
),
|
),
|
||||||
work_done_progress_params: Default::default(),
|
work_done_progress_params: Default::default(),
|
||||||
partial_result_params: Default::default(),
|
partial_result_params: Default::default(),
|
||||||
|
@ -1533,22 +1530,23 @@ impl Project {
|
||||||
lsp::CodeActionKind::REFACTOR_EXTRACT,
|
lsp::CodeActionKind::REFACTOR_EXTRACT,
|
||||||
]),
|
]),
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
cx.foreground().spawn(async move {
|
||||||
|
Ok(actions
|
||||||
.await?
|
.await?
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|entry| {
|
.filter_map(|entry| {
|
||||||
if let lsp::CodeActionOrCommand::CodeAction(lsp_action) = entry {
|
if let lsp::CodeActionOrCommand::CodeAction(lsp_action) = entry {
|
||||||
Some(CodeAction {
|
Some(CodeAction {
|
||||||
position: anchor.clone(),
|
range: range.clone(),
|
||||||
lsp_action,
|
lsp_action,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect())
|
||||||
Ok(actions)
|
|
||||||
})
|
})
|
||||||
} else if let Some(project_id) = self.remote_id() {
|
} else if let Some(project_id) = self.remote_id() {
|
||||||
let rpc = self.client.clone();
|
let rpc = self.client.clone();
|
||||||
|
@ -1557,7 +1555,8 @@ impl Project {
|
||||||
.request(proto::GetCodeActions {
|
.request(proto::GetCodeActions {
|
||||||
project_id,
|
project_id,
|
||||||
buffer_id,
|
buffer_id,
|
||||||
position: Some(language::proto::serialize_anchor(&anchor)),
|
start: Some(language::proto::serialize_anchor(&range.start)),
|
||||||
|
end: Some(language::proto::serialize_anchor(&range.end)),
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
response
|
response
|
||||||
|
@ -1590,25 +1589,29 @@ impl Project {
|
||||||
} else {
|
} else {
|
||||||
return Task::ready(Err(anyhow!("buffer does not have a language server")));
|
return Task::ready(Err(anyhow!("buffer does not have a language server")));
|
||||||
};
|
};
|
||||||
let position = action.position.to_point_utf16(buffer).to_lsp_position();
|
let range = action.range.to_point_utf16(buffer);
|
||||||
let fs = self.fs.clone();
|
let fs = self.fs.clone();
|
||||||
|
|
||||||
cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
if let Some(range) = action
|
if let Some(lsp_range) = action
|
||||||
.lsp_action
|
.lsp_action
|
||||||
.data
|
.data
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.and_then(|d| d.get_mut("codeActionParams"))
|
.and_then(|d| d.get_mut("codeActionParams"))
|
||||||
.and_then(|d| d.get_mut("range"))
|
.and_then(|d| d.get_mut("range"))
|
||||||
{
|
{
|
||||||
*range = serde_json::to_value(&lsp::Range::new(position, position)).unwrap();
|
*lsp_range = serde_json::to_value(&lsp::Range::new(
|
||||||
|
range.start.to_lsp_position(),
|
||||||
|
range.end.to_lsp_position(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
action.lsp_action = lang_server
|
action.lsp_action = lang_server
|
||||||
.request::<lsp::request::CodeActionResolveRequest>(action.lsp_action)
|
.request::<lsp::request::CodeActionResolveRequest>(action.lsp_action)
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
let actions = this
|
let actions = this
|
||||||
.update(&mut cx, |this, cx| {
|
.update(&mut cx, |this, cx| {
|
||||||
this.code_actions(&buffer_handle, action.position.clone(), cx)
|
this.code_actions(&buffer_handle, action.range, cx)
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
action.lsp_action = actions
|
action.lsp_action = actions
|
||||||
|
@ -2357,18 +2360,23 @@ impl Project {
|
||||||
mut cx: AsyncAppContext,
|
mut cx: AsyncAppContext,
|
||||||
) -> Result<proto::GetCodeActionsResponse> {
|
) -> Result<proto::GetCodeActionsResponse> {
|
||||||
let sender_id = envelope.original_sender_id()?;
|
let sender_id = envelope.original_sender_id()?;
|
||||||
let position = envelope
|
let start = envelope
|
||||||
.payload
|
.payload
|
||||||
.position
|
.start
|
||||||
.and_then(language::proto::deserialize_anchor)
|
.and_then(language::proto::deserialize_anchor)
|
||||||
.ok_or_else(|| anyhow!("invalid position"))?;
|
.ok_or_else(|| anyhow!("invalid start"))?;
|
||||||
|
let end = envelope
|
||||||
|
.payload
|
||||||
|
.end
|
||||||
|
.and_then(language::proto::deserialize_anchor)
|
||||||
|
.ok_or_else(|| anyhow!("invalid end"))?;
|
||||||
let code_actions = this.update(&mut cx, |this, cx| {
|
let code_actions = this.update(&mut cx, |this, cx| {
|
||||||
let buffer = this
|
let buffer = this
|
||||||
.shared_buffers
|
.shared_buffers
|
||||||
.get(&sender_id)
|
.get(&sender_id)
|
||||||
.and_then(|shared_buffers| shared_buffers.get(&envelope.payload.buffer_id).cloned())
|
.and_then(|shared_buffers| shared_buffers.get(&envelope.payload.buffer_id).cloned())
|
||||||
.ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
|
.ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
|
||||||
Ok::<_, anyhow::Error>(this.code_actions(&buffer, position, cx))
|
Ok::<_, anyhow::Error>(this.code_actions(&buffer, start..end, cx))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(proto::GetCodeActionsResponse {
|
Ok(proto::GetCodeActionsResponse {
|
||||||
|
|
|
@ -246,7 +246,8 @@ message Completion {
|
||||||
message GetCodeActions {
|
message GetCodeActions {
|
||||||
uint64 project_id = 1;
|
uint64 project_id = 1;
|
||||||
uint64 buffer_id = 2;
|
uint64 buffer_id = 2;
|
||||||
Anchor position = 3;
|
Anchor start = 3;
|
||||||
|
Anchor end = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetCodeActionsResponse {
|
message GetCodeActionsResponse {
|
||||||
|
@ -264,8 +265,9 @@ message ApplyCodeActionResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
message CodeAction {
|
message CodeAction {
|
||||||
Anchor position = 1;
|
Anchor start = 1;
|
||||||
bytes lsp_action = 2;
|
Anchor end = 2;
|
||||||
|
bytes lsp_action = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ProjectTransaction {
|
message ProjectTransaction {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::{Point, ToOffset};
|
use super::{Point, ToOffset};
|
||||||
use crate::{rope::TextDimension, BufferSnapshot};
|
use crate::{rope::TextDimension, BufferSnapshot, PointUtf16, ToPointUtf16};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::{cmp::Ordering, fmt::Debug, ops::Range};
|
use std::{cmp::Ordering, fmt::Debug, ops::Range};
|
||||||
use sum_tree::Bias;
|
use sum_tree::Bias;
|
||||||
|
@ -78,6 +78,7 @@ pub trait AnchorRangeExt {
|
||||||
fn cmp(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> Result<Ordering>;
|
fn cmp(&self, b: &Range<Anchor>, buffer: &BufferSnapshot) -> Result<Ordering>;
|
||||||
fn to_offset(&self, content: &BufferSnapshot) -> Range<usize>;
|
fn to_offset(&self, content: &BufferSnapshot) -> Range<usize>;
|
||||||
fn to_point(&self, content: &BufferSnapshot) -> Range<Point>;
|
fn to_point(&self, content: &BufferSnapshot) -> Range<Point>;
|
||||||
|
fn to_point_utf16(&self, content: &BufferSnapshot) -> Range<PointUtf16>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnchorRangeExt for Range<Anchor> {
|
impl AnchorRangeExt for Range<Anchor> {
|
||||||
|
@ -95,4 +96,8 @@ impl AnchorRangeExt for Range<Anchor> {
|
||||||
fn to_point(&self, content: &BufferSnapshot) -> Range<Point> {
|
fn to_point(&self, content: &BufferSnapshot) -> Range<Point> {
|
||||||
self.start.summary::<Point>(&content)..self.end.summary::<Point>(&content)
|
self.start.summary::<Point>(&content)..self.end.summary::<Point>(&content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_point_utf16(&self, content: &BufferSnapshot) -> Range<PointUtf16> {
|
||||||
|
self.start.to_point_utf16(content)..self.end.to_point_utf16(content)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue