Apply code actions remotely

This commit is contained in:
Antonio Scandurra 2022-02-08 12:18:14 +01:00
parent aedf31e2d8
commit dca974c7d4
9 changed files with 409 additions and 86 deletions

View file

@ -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?;

View file

@ -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, _| {
this.wait_for_edits(edit_ids.iter().copied())
})
.await; .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)

View file

@ -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,
}
}

View file

@ -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)
@ -1311,49 +1327,47 @@ impl Project {
Ok(edited_buffers) Ok(edited_buffers)
}) })
} else { } else if let Some(project_id) = self.remote_id() {
log::info!("applying code actions is not implemented for guests"); let client = self.client.clone();
Task::ready(Ok(Default::default())) 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)?);
} }
// let file = if let Some(file) = self.file.as_ref() {
// file
// } else {
// return Task::ready(Ok(Default::default()));
// };
// if file.is_local() { buffer
// let server = if let Some(language_server) = self.language_server.as_ref() { .update(&mut cx, |buffer, _| {
// language_server.server.clone() buffer.wait_for_edits(buffer_edits.iter().map(|e| e.1))
// } else { })
// return Task::ready(Ok(Default::default())); .await;
// };
// let position = action.position.to_point_utf16(self).to_lsp_position();
// cx.spawn(|this, mut cx| async move { if push_to_history {
// let range = action buffer.update(&mut cx, |buffer, _| {
// .lsp_action buffer
// .data .push_transaction(buffer_edits.iter().map(|e| e.1), Instant::now());
// .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"))?; Ok(edited_buffers)
// *range = serde_json::to_value(&lsp::Range::new(position, position)).unwrap(); })
// let action = server } else {
// .request::<lsp::request::CodeActionResolveRequest>(action.lsp_action) Task::ready(Err(anyhow!("project does not have a remote id")))
// .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>,

View file

@ -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);

View file

@ -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;

View file

@ -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,

View file

@ -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>,

View file

@ -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);
} }
} }