Apply code actions remotely
This commit is contained in:
parent
aedf31e2d8
commit
dca974c7d4
9 changed files with 409 additions and 86 deletions
|
@ -2078,7 +2078,7 @@ impl Editor {
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let apply_code_actions = workspace.project().update(cx, |project, cx| {
|
let apply_code_actions = workspace.project().update(cx, |project, cx| {
|
||||||
project.apply_code_action(buffer, action, cx)
|
project.apply_code_action(buffer, action, true, cx)
|
||||||
});
|
});
|
||||||
Some(cx.spawn(|workspace, mut cx| async move {
|
Some(cx.spawn(|workspace, mut cx| async move {
|
||||||
let buffers = apply_code_actions.await?;
|
let buffers = apply_code_actions.await?;
|
||||||
|
|
|
@ -216,6 +216,13 @@ pub trait File {
|
||||||
cx: &mut MutableAppContext,
|
cx: &mut MutableAppContext,
|
||||||
) -> Task<Result<Vec<clock::Local>>>;
|
) -> Task<Result<Vec<clock::Local>>>;
|
||||||
|
|
||||||
|
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_updated(&self, buffer_id: u64, operation: Operation, cx: &mut MutableAppContext);
|
||||||
|
|
||||||
fn buffer_removed(&self, buffer_id: u64, cx: &mut MutableAppContext);
|
fn buffer_removed(&self, buffer_id: u64, cx: &mut MutableAppContext);
|
||||||
|
@ -304,6 +311,15 @@ impl File for FakeFile {
|
||||||
Task::ready(Ok(Default::default()))
|
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_updated(&self, _: u64, _: Operation, _: &mut MutableAppContext) {}
|
||||||
|
|
||||||
fn buffer_removed(&self, _: u64, _: &mut MutableAppContext) {}
|
fn buffer_removed(&self, _: u64, _: &mut MutableAppContext) {}
|
||||||
|
@ -1350,10 +1366,29 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn push_transaction(
|
||||||
|
&mut self,
|
||||||
|
edit_ids: impl IntoIterator<Item = clock::Local>,
|
||||||
|
now: Instant,
|
||||||
|
) {
|
||||||
|
self.text.push_transaction(edit_ids, now);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn avoid_grouping_next_transaction(&mut self) {
|
pub fn avoid_grouping_next_transaction(&mut self) {
|
||||||
self.text.avoid_grouping_next_transaction();
|
self.text.avoid_grouping_next_transaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn forget_transaction(&mut self, transaction_id: TransactionId) {
|
||||||
|
self.text.forget_transaction(transaction_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wait_for_edits(
|
||||||
|
&mut self,
|
||||||
|
edit_ids: impl IntoIterator<Item = clock::Local>,
|
||||||
|
) -> impl Future<Output = ()> {
|
||||||
|
self.text.wait_for_edits(edit_ids)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_active_selections(
|
pub fn set_active_selections(
|
||||||
&mut self,
|
&mut self,
|
||||||
selections: Arc<[Selection<Anchor>]>,
|
selections: Arc<[Selection<Anchor>]>,
|
||||||
|
@ -1873,6 +1908,8 @@ impl Buffer {
|
||||||
} else {
|
} else {
|
||||||
return Task::ready(Ok(Default::default()));
|
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() {
|
if let Some(file) = file.as_local() {
|
||||||
let server = if let Some(language_server) = self.language_server.as_ref() {
|
let server = if let Some(language_server) = self.language_server.as_ref() {
|
||||||
|
@ -1881,8 +1918,6 @@ impl Buffer {
|
||||||
return Task::ready(Ok(Default::default()));
|
return Task::ready(Ok(Default::default()));
|
||||||
};
|
};
|
||||||
let abs_path = file.abs_path(cx);
|
let abs_path = file.abs_path(cx);
|
||||||
let position = position.to_point_utf16(self);
|
|
||||||
let anchor = self.anchor_after(position);
|
|
||||||
|
|
||||||
cx.foreground().spawn(async move {
|
cx.foreground().spawn(async move {
|
||||||
let actions = server
|
let actions = server
|
||||||
|
@ -1922,8 +1957,7 @@ impl Buffer {
|
||||||
Ok(actions)
|
Ok(actions)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
log::info!("code actions are not implemented for guests");
|
file.code_actions(self.remote_id(), anchor, cx.as_mut())
|
||||||
Task::ready(Ok(Default::default()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1959,7 +1993,7 @@ impl Buffer {
|
||||||
let edits = this.apply_lsp_edits(additional_edits, None, cx);
|
let edits = this.apply_lsp_edits(additional_edits, None, cx);
|
||||||
if let Some(transaction_id) = this.end_transaction(cx) {
|
if let Some(transaction_id) = this.end_transaction(cx) {
|
||||||
if !push_to_history {
|
if !push_to_history {
|
||||||
this.text.forget_transaction(transaction_id);
|
this.forget_transaction(transaction_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(edits?.into_iter().map(|(_, edit_id)| edit_id).collect())
|
Ok(edits?.into_iter().map(|(_, edit_id)| edit_id).collect())
|
||||||
|
@ -1976,12 +2010,13 @@ impl Buffer {
|
||||||
);
|
);
|
||||||
cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
let edit_ids = apply_edits.await?;
|
let edit_ids = apply_edits.await?;
|
||||||
this.update(&mut cx, |this, _| this.text.wait_for_edits(&edit_ids))
|
this.update(&mut cx, |this, _| {
|
||||||
.await;
|
this.wait_for_edits(edit_ids.iter().copied())
|
||||||
|
})
|
||||||
|
.await;
|
||||||
if push_to_history {
|
if push_to_history {
|
||||||
this.update(&mut cx, |this, _| {
|
this.update(&mut cx, |this, _| {
|
||||||
this.text
|
this.push_transaction(edit_ids.iter().copied(), Instant::now());
|
||||||
.push_transaction(edit_ids.iter().copied(), Instant::now());
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(edit_ids)
|
Ok(edit_ids)
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
diagnostic_set::DiagnosticEntry, Completion, CompletionLabel, Diagnostic, Language, Operation,
|
diagnostic_set::DiagnosticEntry, CodeAction, Completion, CompletionLabel, Diagnostic, Language,
|
||||||
|
Operation,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use clock::ReplicaId;
|
use clock::ReplicaId;
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
use lsp::DiagnosticSeverity;
|
use lsp::DiagnosticSeverity;
|
||||||
use rpc::proto;
|
use rpc::proto;
|
||||||
use std::sync::Arc;
|
use std::{ops::Range, sync::Arc};
|
||||||
use text::*;
|
use text::*;
|
||||||
|
|
||||||
pub use proto::{Buffer, BufferState, SelectionSet};
|
pub use proto::{Buffer, BufferState, SelectionSet};
|
||||||
|
@ -411,3 +412,62 @@ pub fn deserialize_completion(
|
||||||
lsp_completion,
|
lsp_completion,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn serialize_code_action(action: &CodeAction<Anchor>) -> 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>> {
|
||||||
|
let position = action
|
||||||
|
.position
|
||||||
|
.and_then(deserialize_anchor)
|
||||||
|
.ok_or_else(|| anyhow!("invalid position"))?;
|
||||||
|
let lsp_action = serde_json::from_slice(&action.lsp_action)?;
|
||||||
|
Ok(CodeAction {
|
||||||
|
position,
|
||||||
|
lsp_action,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize_code_action_edit(
|
||||||
|
edit_id: clock::Local,
|
||||||
|
old_range: &Range<Anchor>,
|
||||||
|
) -> proto::CodeActionEdit {
|
||||||
|
proto::CodeActionEdit {
|
||||||
|
id: Some(serialize_edit_id(edit_id)),
|
||||||
|
old_start: Some(serialize_anchor(&old_range.start)),
|
||||||
|
old_end: Some(serialize_anchor(&old_range.end)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize_code_action_edit(
|
||||||
|
edit: proto::CodeActionEdit,
|
||||||
|
) -> Result<(Range<Anchor>, clock::Local)> {
|
||||||
|
let old_start = edit
|
||||||
|
.old_start
|
||||||
|
.and_then(deserialize_anchor)
|
||||||
|
.ok_or_else(|| anyhow!("invalid old_start"))?;
|
||||||
|
let old_end = edit
|
||||||
|
.old_end
|
||||||
|
.and_then(deserialize_anchor)
|
||||||
|
.ok_or_else(|| anyhow!("invalid old_end"))?;
|
||||||
|
let edit_id = deserialize_edit_id(edit.id.ok_or_else(|| anyhow!("invalid edit_id"))?);
|
||||||
|
Ok((old_start..old_end, edit_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize_edit_id(edit_id: clock::Local) -> proto::EditId {
|
||||||
|
proto::EditId {
|
||||||
|
replica_id: edit_id.replica_id as u32,
|
||||||
|
local_timestamp: edit_id.value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize_edit_id(edit_id: proto::EditId) -> clock::Local {
|
||||||
|
clock::Local {
|
||||||
|
replica_id: edit_id.replica_id as ReplicaId,
|
||||||
|
value: edit_id.local_timestamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ use std::{
|
||||||
ops::Range,
|
ops::Range,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::{atomic::AtomicBool, Arc},
|
sync::{atomic::AtomicBool, Arc},
|
||||||
|
time::Instant,
|
||||||
};
|
};
|
||||||
use util::{post_inc, ResultExt, TryFutureExt as _};
|
use util::{post_inc, ResultExt, TryFutureExt as _};
|
||||||
|
|
||||||
|
@ -341,6 +342,8 @@ impl Project {
|
||||||
cx,
|
cx,
|
||||||
Self::handle_apply_additional_edits_for_completion,
|
Self::handle_apply_additional_edits_for_completion,
|
||||||
),
|
),
|
||||||
|
client.subscribe_to_entity(remote_id, cx, Self::handle_get_code_actions),
|
||||||
|
client.subscribe_to_entity(remote_id, cx, Self::handle_apply_code_action),
|
||||||
client.subscribe_to_entity(remote_id, cx, Self::handle_get_definition),
|
client.subscribe_to_entity(remote_id, cx, Self::handle_get_definition),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -1169,6 +1172,7 @@ impl Project {
|
||||||
&self,
|
&self,
|
||||||
buffer_handle: ModelHandle<Buffer>,
|
buffer_handle: ModelHandle<Buffer>,
|
||||||
mut action: CodeAction<language::Anchor>,
|
mut action: CodeAction<language::Anchor>,
|
||||||
|
push_to_history: bool,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Task<Result<HashMap<ModelHandle<Buffer>, Vec<(Range<language::Anchor>, clock::Local)>>>>
|
) -> Task<Result<HashMap<ModelHandle<Buffer>, Vec<(Range<language::Anchor>, clock::Local)>>>>
|
||||||
{
|
{
|
||||||
|
@ -1299,7 +1303,19 @@ impl Project {
|
||||||
lsp::OneOf::Left(edit) => edit,
|
lsp::OneOf::Left(edit) => edit,
|
||||||
lsp::OneOf::Right(edit) => edit.text_edit,
|
lsp::OneOf::Right(edit) => edit.text_edit,
|
||||||
});
|
});
|
||||||
buffer.apply_lsp_edits(edits, op.text_document.version, cx)
|
if !push_to_history {
|
||||||
|
buffer.avoid_grouping_next_transaction();
|
||||||
|
}
|
||||||
|
buffer.start_transaction();
|
||||||
|
let edits =
|
||||||
|
buffer.apply_lsp_edits(edits, op.text_document.version, cx);
|
||||||
|
if let Some(transaction_id) = buffer.end_transaction(cx) {
|
||||||
|
if !push_to_history {
|
||||||
|
buffer.forget_transaction(transaction_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
edits
|
||||||
})?;
|
})?;
|
||||||
edited_buffers
|
edited_buffers
|
||||||
.entry(buffer_to_edit)
|
.entry(buffer_to_edit)
|
||||||
|
@ -1309,51 +1325,49 @@ impl Project {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(edited_buffers)
|
||||||
|
})
|
||||||
|
} else if let Some(project_id) = self.remote_id() {
|
||||||
|
let client = self.client.clone();
|
||||||
|
let request = proto::ApplyCodeAction {
|
||||||
|
project_id,
|
||||||
|
buffer_id: buffer_handle.read(cx).remote_id(),
|
||||||
|
action: Some(language::proto::serialize_code_action(&action)),
|
||||||
|
};
|
||||||
|
cx.spawn(|this, mut cx| async move {
|
||||||
|
let response = client.request(request).await?;
|
||||||
|
let mut edited_buffers = HashMap::default();
|
||||||
|
for buffer_edit in response.buffer_edits {
|
||||||
|
let buffer = buffer_edit
|
||||||
|
.buffer
|
||||||
|
.ok_or_else(|| anyhow!("invalid buffer"))?;
|
||||||
|
let buffer = this.update(&mut cx, |this, cx| {
|
||||||
|
this.deserialize_remote_buffer(buffer, cx)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let buffer_edits = edited_buffers.entry(buffer.clone()).or_insert(Vec::new());
|
||||||
|
for edit in buffer_edit.edits {
|
||||||
|
buffer_edits.push(language::proto::deserialize_code_action_edit(edit)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer
|
||||||
|
.update(&mut cx, |buffer, _| {
|
||||||
|
buffer.wait_for_edits(buffer_edits.iter().map(|e| e.1))
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
if push_to_history {
|
||||||
|
buffer.update(&mut cx, |buffer, _| {
|
||||||
|
buffer
|
||||||
|
.push_transaction(buffer_edits.iter().map(|e| e.1), Instant::now());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(edited_buffers)
|
Ok(edited_buffers)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
log::info!("applying code actions is not implemented for guests");
|
Task::ready(Err(anyhow!("project does not have a remote id")))
|
||||||
Task::ready(Ok(Default::default()))
|
|
||||||
}
|
}
|
||||||
// let file = if let Some(file) = self.file.as_ref() {
|
|
||||||
// file
|
|
||||||
// } else {
|
|
||||||
// return Task::ready(Ok(Default::default()));
|
|
||||||
// };
|
|
||||||
|
|
||||||
// if file.is_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 position = action.position.to_point_utf16(self).to_lsp_position();
|
|
||||||
|
|
||||||
// cx.spawn(|this, mut cx| async move {
|
|
||||||
// let range = action
|
|
||||||
// .lsp_action
|
|
||||||
// .data
|
|
||||||
// .as_mut()
|
|
||||||
// .and_then(|d| d.get_mut("codeActionParams"))
|
|
||||||
// .and_then(|d| d.get_mut("range"))
|
|
||||||
// .ok_or_else(|| anyhow!("code action has no range"))?;
|
|
||||||
// *range = serde_json::to_value(&lsp::Range::new(position, position)).unwrap();
|
|
||||||
// let action = server
|
|
||||||
// .request::<lsp::request::CodeActionResolveRequest>(action.lsp_action)
|
|
||||||
// .await?;
|
|
||||||
// let edit = action
|
|
||||||
// .edit
|
|
||||||
// .ok_or_else(|| anyhow!("code action has no edit"));
|
|
||||||
// match edit {
|
|
||||||
// Ok(edit) => edit.,
|
|
||||||
// Err(_) => todo!(),
|
|
||||||
// }
|
|
||||||
// Ok(Default::default())
|
|
||||||
// })
|
|
||||||
// } else {
|
|
||||||
// log::info!("applying code actions is not implemented for guests");
|
|
||||||
// Task::ready(Ok(Default::default()))
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_or_create_local_worktree(
|
pub fn find_or_create_local_worktree(
|
||||||
|
@ -1951,7 +1965,7 @@ impl Project {
|
||||||
envelope
|
envelope
|
||||||
.payload
|
.payload
|
||||||
.completion
|
.completion
|
||||||
.ok_or_else(|| anyhow!("invalid position"))?,
|
.ok_or_else(|| anyhow!("invalid completion"))?,
|
||||||
language,
|
language,
|
||||||
)?;
|
)?;
|
||||||
cx.spawn(|_, mut cx| async move {
|
cx.spawn(|_, mut cx| async move {
|
||||||
|
@ -1966,10 +1980,7 @@ impl Project {
|
||||||
proto::ApplyCompletionAdditionalEditsResponse {
|
proto::ApplyCompletionAdditionalEditsResponse {
|
||||||
additional_edits: edit_ids
|
additional_edits: edit_ids
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|edit_id| proto::AdditionalEdit {
|
.map(language::proto::serialize_edit_id)
|
||||||
replica_id: edit_id.replica_id as u32,
|
|
||||||
local_timestamp: edit_id.value,
|
|
||||||
})
|
|
||||||
.collect(),
|
.collect(),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -1985,6 +1996,99 @@ impl Project {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_get_code_actions(
|
||||||
|
&mut self,
|
||||||
|
envelope: TypedEnvelope<proto::GetCodeActions>,
|
||||||
|
rpc: Arc<Client>,
|
||||||
|
cx: &mut ModelContext<Self>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let receipt = envelope.receipt();
|
||||||
|
let sender_id = envelope.original_sender_id()?;
|
||||||
|
let buffer = self
|
||||||
|
.shared_buffers
|
||||||
|
.get(&sender_id)
|
||||||
|
.and_then(|shared_buffers| shared_buffers.get(&envelope.payload.buffer_id).cloned())
|
||||||
|
.ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
|
||||||
|
let position = envelope
|
||||||
|
.payload
|
||||||
|
.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))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(completions) => rpc.respond(
|
||||||
|
receipt,
|
||||||
|
proto::GetCodeActionsResponse {
|
||||||
|
actions: completions
|
||||||
|
.iter()
|
||||||
|
.map(language::proto::serialize_code_action)
|
||||||
|
.collect(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Err(error) => rpc.respond_with_error(
|
||||||
|
receipt,
|
||||||
|
proto::Error {
|
||||||
|
message: error.to_string(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach_and_log_err(cx);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_apply_code_action(
|
||||||
|
&mut self,
|
||||||
|
envelope: TypedEnvelope<proto::ApplyCodeAction>,
|
||||||
|
rpc: Arc<Client>,
|
||||||
|
cx: &mut ModelContext<Self>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let receipt = envelope.receipt();
|
||||||
|
let sender_id = envelope.original_sender_id()?;
|
||||||
|
let buffer = self
|
||||||
|
.shared_buffers
|
||||||
|
.get(&sender_id)
|
||||||
|
.and_then(|shared_buffers| shared_buffers.get(&envelope.payload.buffer_id).cloned())
|
||||||
|
.ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
|
||||||
|
let action = language::proto::deserialize_code_action(
|
||||||
|
envelope
|
||||||
|
.payload
|
||||||
|
.action
|
||||||
|
.ok_or_else(|| anyhow!("invalid action"))?,
|
||||||
|
)?;
|
||||||
|
let apply_code_action = self.apply_code_action(buffer, action, false, cx);
|
||||||
|
cx.spawn(|this, mut cx| async move {
|
||||||
|
match apply_code_action.await {
|
||||||
|
Ok(edited_buffers) => this.update(&mut cx, |this, cx| {
|
||||||
|
let buffer_edits = edited_buffers
|
||||||
|
.into_iter()
|
||||||
|
.map(|(buffer, edits)| proto::CodeActionBufferEdits {
|
||||||
|
buffer: Some(this.serialize_buffer_for_peer(&buffer, sender_id, cx)),
|
||||||
|
edits: edits
|
||||||
|
.into_iter()
|
||||||
|
.map(|(range, edit_id)| {
|
||||||
|
language::proto::serialize_code_action_edit(edit_id, &range)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
rpc.respond(receipt, proto::ApplyCodeActionResponse { buffer_edits })
|
||||||
|
}),
|
||||||
|
Err(error) => rpc.respond_with_error(
|
||||||
|
receipt,
|
||||||
|
proto::Error {
|
||||||
|
message: error.to_string(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach_and_log_err(cx);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_get_definition(
|
pub fn handle_get_definition(
|
||||||
&mut self,
|
&mut self,
|
||||||
envelope: TypedEnvelope<proto::GetDefinition>,
|
envelope: TypedEnvelope<proto::GetDefinition>,
|
||||||
|
|
|
@ -1469,14 +1469,43 @@ impl language::File for File {
|
||||||
Ok(response
|
Ok(response
|
||||||
.additional_edits
|
.additional_edits
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|edit| clock::Local {
|
.map(language::proto::deserialize_edit_id)
|
||||||
replica_id: edit.replica_id as ReplicaId,
|
|
||||||
value: edit.local_timestamp,
|
|
||||||
})
|
|
||||||
.collect())
|
.collect())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
fn buffer_updated(&self, buffer_id: u64, operation: Operation, cx: &mut MutableAppContext) {
|
||||||
self.worktree.update(cx, |worktree, cx| {
|
self.worktree.update(cx, |worktree, cx| {
|
||||||
worktree.send_buffer_update(buffer_id, operation, cx);
|
worktree.send_buffer_update(buffer_id, operation, cx);
|
||||||
|
|
|
@ -44,22 +44,26 @@ message Envelope {
|
||||||
GetCompletionsResponse get_completions_response = 36;
|
GetCompletionsResponse get_completions_response = 36;
|
||||||
ApplyCompletionAdditionalEdits apply_completion_additional_edits = 37;
|
ApplyCompletionAdditionalEdits apply_completion_additional_edits = 37;
|
||||||
ApplyCompletionAdditionalEditsResponse apply_completion_additional_edits_response = 38;
|
ApplyCompletionAdditionalEditsResponse apply_completion_additional_edits_response = 38;
|
||||||
|
GetCodeActions get_code_actions = 39;
|
||||||
|
GetCodeActionsResponse get_code_actions_response = 40;
|
||||||
|
ApplyCodeAction apply_code_action = 41;
|
||||||
|
ApplyCodeActionResponse apply_code_action_response = 42;
|
||||||
|
|
||||||
GetChannels get_channels = 39;
|
GetChannels get_channels = 43;
|
||||||
GetChannelsResponse get_channels_response = 40;
|
GetChannelsResponse get_channels_response = 44;
|
||||||
JoinChannel join_channel = 41;
|
JoinChannel join_channel = 45;
|
||||||
JoinChannelResponse join_channel_response = 42;
|
JoinChannelResponse join_channel_response = 46;
|
||||||
LeaveChannel leave_channel = 43;
|
LeaveChannel leave_channel = 47;
|
||||||
SendChannelMessage send_channel_message = 44;
|
SendChannelMessage send_channel_message = 48;
|
||||||
SendChannelMessageResponse send_channel_message_response = 45;
|
SendChannelMessageResponse send_channel_message_response = 49;
|
||||||
ChannelMessageSent channel_message_sent = 46;
|
ChannelMessageSent channel_message_sent = 50;
|
||||||
GetChannelMessages get_channel_messages = 47;
|
GetChannelMessages get_channel_messages = 51;
|
||||||
GetChannelMessagesResponse get_channel_messages_response = 48;
|
GetChannelMessagesResponse get_channel_messages_response = 52;
|
||||||
|
|
||||||
UpdateContacts update_contacts = 49;
|
UpdateContacts update_contacts = 53;
|
||||||
|
|
||||||
GetUsers get_users = 50;
|
GetUsers get_users = 54;
|
||||||
GetUsersResponse get_users_response = 51;
|
GetUsersResponse get_users_response = 55;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,12 +228,7 @@ message ApplyCompletionAdditionalEdits {
|
||||||
}
|
}
|
||||||
|
|
||||||
message ApplyCompletionAdditionalEditsResponse {
|
message ApplyCompletionAdditionalEditsResponse {
|
||||||
repeated AdditionalEdit additional_edits = 1;
|
repeated EditId additional_edits = 1;
|
||||||
}
|
|
||||||
|
|
||||||
message AdditionalEdit {
|
|
||||||
uint32 replica_id = 1;
|
|
||||||
uint32 local_timestamp = 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message Completion {
|
message Completion {
|
||||||
|
@ -239,6 +238,47 @@ message Completion {
|
||||||
bytes lsp_completion = 4;
|
bytes lsp_completion = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message GetCodeActions {
|
||||||
|
uint64 project_id = 1;
|
||||||
|
uint64 buffer_id = 2;
|
||||||
|
Anchor position = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetCodeActionsResponse {
|
||||||
|
repeated CodeAction actions = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ApplyCodeAction {
|
||||||
|
uint64 project_id = 1;
|
||||||
|
uint64 buffer_id = 2;
|
||||||
|
CodeAction action = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ApplyCodeActionResponse {
|
||||||
|
repeated CodeActionBufferEdits buffer_edits = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CodeAction {
|
||||||
|
Anchor position = 1;
|
||||||
|
bytes lsp_action = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CodeActionBufferEdits {
|
||||||
|
Buffer buffer = 1;
|
||||||
|
repeated CodeActionEdit edits = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CodeActionEdit {
|
||||||
|
EditId id = 1;
|
||||||
|
Anchor old_start = 2;
|
||||||
|
Anchor old_end = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message EditId {
|
||||||
|
uint32 replica_id = 1;
|
||||||
|
uint32 local_timestamp = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message UpdateDiagnosticSummary {
|
message UpdateDiagnosticSummary {
|
||||||
uint64 project_id = 1;
|
uint64 project_id = 1;
|
||||||
uint64 worktree_id = 2;
|
uint64 worktree_id = 2;
|
||||||
|
|
|
@ -122,6 +122,8 @@ macro_rules! entity_messages {
|
||||||
messages!(
|
messages!(
|
||||||
Ack,
|
Ack,
|
||||||
AddProjectCollaborator,
|
AddProjectCollaborator,
|
||||||
|
ApplyCodeAction,
|
||||||
|
ApplyCodeActionResponse,
|
||||||
ApplyCompletionAdditionalEdits,
|
ApplyCompletionAdditionalEdits,
|
||||||
ApplyCompletionAdditionalEditsResponse,
|
ApplyCompletionAdditionalEditsResponse,
|
||||||
BufferReloaded,
|
BufferReloaded,
|
||||||
|
@ -136,6 +138,8 @@ messages!(
|
||||||
GetChannelMessagesResponse,
|
GetChannelMessagesResponse,
|
||||||
GetChannels,
|
GetChannels,
|
||||||
GetChannelsResponse,
|
GetChannelsResponse,
|
||||||
|
GetCodeActions,
|
||||||
|
GetCodeActionsResponse,
|
||||||
GetCompletions,
|
GetCompletions,
|
||||||
GetCompletionsResponse,
|
GetCompletionsResponse,
|
||||||
GetDefinition,
|
GetDefinition,
|
||||||
|
@ -171,6 +175,7 @@ messages!(
|
||||||
);
|
);
|
||||||
|
|
||||||
request_messages!(
|
request_messages!(
|
||||||
|
(ApplyCodeAction, ApplyCodeActionResponse),
|
||||||
(
|
(
|
||||||
ApplyCompletionAdditionalEdits,
|
ApplyCompletionAdditionalEdits,
|
||||||
ApplyCompletionAdditionalEditsResponse
|
ApplyCompletionAdditionalEditsResponse
|
||||||
|
@ -178,6 +183,7 @@ request_messages!(
|
||||||
(FormatBuffer, Ack),
|
(FormatBuffer, Ack),
|
||||||
(GetChannelMessages, GetChannelMessagesResponse),
|
(GetChannelMessages, GetChannelMessagesResponse),
|
||||||
(GetChannels, GetChannelsResponse),
|
(GetChannels, GetChannelsResponse),
|
||||||
|
(GetCodeActions, GetCodeActionsResponse),
|
||||||
(GetCompletions, GetCompletionsResponse),
|
(GetCompletions, GetCompletionsResponse),
|
||||||
(GetDefinition, GetDefinitionResponse),
|
(GetDefinition, GetDefinitionResponse),
|
||||||
(GetUsers, GetUsersResponse),
|
(GetUsers, GetUsersResponse),
|
||||||
|
@ -197,6 +203,7 @@ request_messages!(
|
||||||
entity_messages!(
|
entity_messages!(
|
||||||
project_id,
|
project_id,
|
||||||
AddProjectCollaborator,
|
AddProjectCollaborator,
|
||||||
|
ApplyCodeAction,
|
||||||
ApplyCompletionAdditionalEdits,
|
ApplyCompletionAdditionalEdits,
|
||||||
BufferReloaded,
|
BufferReloaded,
|
||||||
BufferSaved,
|
BufferSaved,
|
||||||
|
@ -204,6 +211,7 @@ entity_messages!(
|
||||||
DiskBasedDiagnosticsUpdated,
|
DiskBasedDiagnosticsUpdated,
|
||||||
DiskBasedDiagnosticsUpdating,
|
DiskBasedDiagnosticsUpdating,
|
||||||
FormatBuffer,
|
FormatBuffer,
|
||||||
|
GetCodeActions,
|
||||||
GetCompletions,
|
GetCompletions,
|
||||||
GetDefinition,
|
GetDefinition,
|
||||||
JoinProject,
|
JoinProject,
|
||||||
|
|
|
@ -85,6 +85,8 @@ impl Server {
|
||||||
.add_handler(Server::format_buffer)
|
.add_handler(Server::format_buffer)
|
||||||
.add_handler(Server::get_completions)
|
.add_handler(Server::get_completions)
|
||||||
.add_handler(Server::apply_additional_edits_for_completion)
|
.add_handler(Server::apply_additional_edits_for_completion)
|
||||||
|
.add_handler(Server::get_code_actions)
|
||||||
|
.add_handler(Server::apply_code_action)
|
||||||
.add_handler(Server::get_channels)
|
.add_handler(Server::get_channels)
|
||||||
.add_handler(Server::get_users)
|
.add_handler(Server::get_users)
|
||||||
.add_handler(Server::join_channel)
|
.add_handler(Server::join_channel)
|
||||||
|
@ -737,6 +739,52 @@ impl Server {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_code_actions(
|
||||||
|
self: Arc<Server>,
|
||||||
|
request: TypedEnvelope<proto::GetCodeActions>,
|
||||||
|
) -> tide::Result<()> {
|
||||||
|
let host;
|
||||||
|
{
|
||||||
|
let state = self.state();
|
||||||
|
let project = state
|
||||||
|
.read_project(request.payload.project_id, request.sender_id)
|
||||||
|
.ok_or_else(|| anyhow!(NO_SUCH_PROJECT))?;
|
||||||
|
host = project.host_connection_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sender = request.sender_id;
|
||||||
|
let receipt = request.receipt();
|
||||||
|
let response = self
|
||||||
|
.peer
|
||||||
|
.forward_request(sender, host, request.payload.clone())
|
||||||
|
.await?;
|
||||||
|
self.peer.respond(receipt, response)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn apply_code_action(
|
||||||
|
self: Arc<Server>,
|
||||||
|
request: TypedEnvelope<proto::ApplyCodeAction>,
|
||||||
|
) -> tide::Result<()> {
|
||||||
|
let host;
|
||||||
|
{
|
||||||
|
let state = self.state();
|
||||||
|
let project = state
|
||||||
|
.read_project(request.payload.project_id, request.sender_id)
|
||||||
|
.ok_or_else(|| anyhow!(NO_SUCH_PROJECT))?;
|
||||||
|
host = project.host_connection_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sender = request.sender_id;
|
||||||
|
let receipt = request.receipt();
|
||||||
|
let response = self
|
||||||
|
.peer
|
||||||
|
.forward_request(sender, host, request.payload.clone())
|
||||||
|
.await?;
|
||||||
|
self.peer.respond(receipt, response)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn update_buffer(
|
async fn update_buffer(
|
||||||
self: Arc<Server>,
|
self: Arc<Server>,
|
||||||
request: TypedEnvelope<proto::UpdateBuffer>,
|
request: TypedEnvelope<proto::UpdateBuffer>,
|
||||||
|
|
|
@ -570,7 +570,6 @@ impl Buffer {
|
||||||
Self {
|
Self {
|
||||||
remote_id,
|
remote_id,
|
||||||
replica_id,
|
replica_id,
|
||||||
|
|
||||||
history: History::new("".into()),
|
history: History::new("".into()),
|
||||||
deferred_ops: OperationQueue::new(),
|
deferred_ops: OperationQueue::new(),
|
||||||
deferred_replicas: Default::default(),
|
deferred_replicas: Default::default(),
|
||||||
|
@ -1294,13 +1293,13 @@ impl Buffer {
|
||||||
|
|
||||||
pub fn wait_for_edits(
|
pub fn wait_for_edits(
|
||||||
&mut self,
|
&mut self,
|
||||||
edit_ids: &[clock::Local],
|
edit_ids: impl IntoIterator<Item = clock::Local>,
|
||||||
) -> impl 'static + Future<Output = ()> {
|
) -> impl 'static + Future<Output = ()> {
|
||||||
let mut futures = Vec::new();
|
let mut futures = Vec::new();
|
||||||
for edit_id in edit_ids {
|
for edit_id in edit_ids {
|
||||||
if !self.version.observed(*edit_id) {
|
if !self.version.observed(edit_id) {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
self.edit_id_resolvers.entry(*edit_id).or_default().push(tx);
|
self.edit_id_resolvers.entry(edit_id).or_default().push(tx);
|
||||||
futures.push(rx);
|
futures.push(rx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue