Implement Project::definition when the buffer is remote

This commit is contained in:
Antonio Scandurra 2022-01-24 12:27:13 +01:00
parent 528a4dd9b4
commit 245490f934
6 changed files with 392 additions and 53 deletions

View file

@ -13,6 +13,7 @@ use gpui::{
WeakModelHandle,
};
use language::{
proto::{deserialize_anchor, serialize_anchor},
range_from_lsp, Bias, Buffer, Diagnostic, DiagnosticEntry, File as _, Language,
LanguageRegistry, Operation, PointUtf16, ToOffset, ToPointUtf16,
};
@ -336,6 +337,7 @@ impl Project {
client.subscribe_to_entity(remote_id, cx, Self::handle_save_buffer),
client.subscribe_to_entity(remote_id, cx, Self::handle_buffer_saved),
client.subscribe_to_entity(remote_id, cx, Self::handle_format_buffer),
client.subscribe_to_entity(remote_id, cx, Self::handle_get_definition),
]);
}
}
@ -951,10 +953,10 @@ impl Project {
cx: &mut ModelContext<Self>,
) -> Task<Result<Vec<Definition>>> {
let source_buffer_handle = source_buffer_handle.clone();
let buffer = source_buffer_handle.read(cx);
let source_buffer = source_buffer_handle.read(cx);
let worktree;
let buffer_abs_path;
if let Some(file) = File::from_dyn(buffer.file()) {
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 {
@ -962,11 +964,11 @@ impl Project {
};
if worktree.read(cx).as_local().is_some() {
let point = buffer.offset_to_point_utf16(position.to_offset(buffer));
let point = source_buffer.offset_to_point_utf16(position.to_offset(source_buffer));
let buffer_abs_path = buffer_abs_path.unwrap();
let lang_name;
let lang_server;
if let Some(lang) = buffer.language() {
if let Some(lang) = source_buffer.language() {
lang_name = lang.name().to_string();
if let Some(server) = self
.language_servers
@ -1061,9 +1063,67 @@ impl Project {
Ok(definitions)
})
} else if let Some(project_id) = self.remote_id() {
let client = self.client.clone();
let replica_id = self.replica_id();
let request = proto::GetDefinition {
project_id,
buffer_id: source_buffer.remote_id(),
position: Some(serialize_anchor(&source_buffer.anchor_before(position))),
};
cx.spawn(|this, mut cx| async move {
let response = client.request(request).await?;
this.update(&mut cx, |this, cx| {
let mut definitions = Vec::new();
for definition in response.definitions {
let target_buffer = match definition
.buffer
.ok_or_else(|| anyhow!("missing buffer"))?
{
proto::definition::Buffer::Id(id) => this
.open_buffers
.get(&(id as usize))
.and_then(|buffer| buffer.upgrade(cx))
.ok_or_else(|| anyhow!("no buffer exists for id {}", id))?,
proto::definition::Buffer::State(mut buffer) => {
let file = if let Some(file) = buffer.file.take() {
let worktree_id = WorktreeId::from_proto(file.worktree_id);
let worktree =
this.worktree_for_id(worktree_id, cx).ok_or_else(|| {
anyhow!("no worktree found for id {}", file.worktree_id)
})?;
let file = File::from_proto(file, worktree.clone(), cx)?;
Some(Box::new(file) as Box<dyn language::File>)
} else {
None
};
let buffer = cx.add_model(|cx| {
Buffer::from_proto(replica_id, buffer, file, cx).unwrap()
});
this.register_buffer(&buffer, &worktree, cx)?;
buffer
}
};
let target_start = definition
.target_start
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing target start"))?;
let target_end = definition
.target_end
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing target end"))?;
definitions.push(Definition {
target_buffer,
target_range: target_start..target_end,
})
}
Ok(definitions)
})
})
} else {
log::info!("go to definition is not yet implemented for guests");
Task::ready(Ok(Default::default()))
Task::ready(Err(anyhow!("project does not have a remote id")))
}
}
@ -1627,6 +1687,62 @@ impl Project {
Ok(())
}
pub fn handle_get_definition(
&mut self,
envelope: TypedEnvelope<proto::GetDefinition>,
rpc: Arc<Client>,
cx: &mut ModelContext<Self>,
) -> Result<()> {
let receipt = envelope.receipt();
let sender_id = envelope.original_sender_id()?;
let source_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(deserialize_anchor)
.ok_or_else(|| anyhow!("invalid position"))?;
if !source_buffer.read(cx).can_resolve(&position) {
return Err(anyhow!("cannot resolve position"));
}
let definitions = self.definition(&source_buffer, position, cx);
cx.spawn(|this, mut cx| async move {
let definitions = definitions.await?;
let mut response = proto::GetDefinitionResponse {
definitions: Default::default(),
};
this.update(&mut cx, |this, cx| {
for definition in definitions {
let buffer_id = definition.target_buffer.read(cx).remote_id();
let shared_buffers = this.shared_buffers.entry(sender_id).or_default();
let buffer = match shared_buffers.entry(buffer_id) {
hash_map::Entry::Occupied(_) => proto::definition::Buffer::Id(buffer_id),
hash_map::Entry::Vacant(entry) => {
entry.insert(definition.target_buffer.clone());
proto::definition::Buffer::State(
definition.target_buffer.read(cx).to_proto(),
)
}
};
response.definitions.push(proto::Definition {
target_start: Some(serialize_anchor(&definition.target_range.start)),
target_end: Some(serialize_anchor(&definition.target_range.end)),
buffer: Some(buffer),
});
}
});
rpc.respond(receipt, response).await?;
Ok::<_, anyhow::Error>(())
})
.detach_and_log_err(cx);
Ok(())
}
pub fn handle_open_buffer(
&mut self,
envelope: TypedEnvelope<proto::OpenBuffer>,