diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index a5be6e7d62..583c708e0a 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -226,6 +226,7 @@ impl Server { .add_request_handler(forward_project_request::) .add_request_handler(forward_project_request::) .add_request_handler(forward_project_request::) + .add_request_handler(forward_project_request::) .add_message_handler(create_buffer_for_peer) .add_request_handler(update_buffer) .add_message_handler(update_buffer_file) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 23e90f5840..63b3c4f6a3 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -7240,7 +7240,6 @@ impl Editor { predecessor: *predecessor, excerpts: excerpts.clone(), }); - // TODO kb wrong? false } multi_buffer::Event::ExcerptsRemoved { ids } => { diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 9e6b3038a3..fb01becaf4 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -3,7 +3,7 @@ use crate::{ InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Location, LocationLink, MarkupContent, Project, ProjectTransaction, }; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; use client::proto::{self, PeerId}; use fs::LineEnding; @@ -1790,7 +1790,7 @@ impl LspCommand for OnTypeFormatting { impl LspCommand for InlayHints { type Response = Vec; type LspRequest = lsp::InlayHintRequest; - type ProtoRequest = proto::OnTypeFormatting; + type ProtoRequest = proto::InlayHints; fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool { let Some(inlay_hint_provider) = &server_capabilities.inlay_hint_provider else { return false }; @@ -1892,40 +1892,190 @@ impl LspCommand for InlayHints { }) } - fn to_proto(&self, _: u64, _: &Buffer) -> proto::OnTypeFormatting { - todo!("TODO kb") + fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints { + proto::InlayHints { + project_id, + buffer_id: buffer.remote_id(), + start: Some(language::proto::serialize_anchor(&self.range.start)), + end: Some(language::proto::serialize_anchor(&self.range.end)), + version: serialize_version(&buffer.version()), + } } async fn from_proto( - _: proto::OnTypeFormatting, + message: proto::InlayHints, _: ModelHandle, - _: ModelHandle, - _: AsyncAppContext, + buffer: ModelHandle, + mut cx: AsyncAppContext, ) -> Result { - todo!("TODO kb") + let start = message + .start + .and_then(language::proto::deserialize_anchor) + .context("invalid start")?; + let end = message + .end + .and_then(language::proto::deserialize_anchor) + .context("invalid end")?; + // TODO kb has it to be multiple versions instead? + buffer + .update(&mut cx, |buffer, _| { + buffer.wait_for_version(deserialize_version(&message.version)) + }) + .await?; + + Ok(Self { range: start..end }) } fn response_to_proto( - _: Vec, + response: Vec, _: &mut Project, _: PeerId, - _: &clock::Global, + buffer_version: &clock::Global, _: &mut AppContext, - ) -> proto::OnTypeFormattingResponse { - todo!("TODO kb") + ) -> proto::InlayHintsResponse { + proto::InlayHintsResponse { + hints: response + .into_iter() + .map(|response_hint| proto::InlayHint { + position: Some(language::proto::serialize_anchor(&response_hint.position)), + label: Some(proto::InlayHintLabel { + label: Some(match response_hint.label { + InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s), + InlayHintLabel::LabelParts(label_parts) => { + proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts { + parts: label_parts.into_iter().map(|label_part| proto::InlayHintLabelPart { + value: label_part.value, + tooltip: label_part.tooltip.map(|tooltip| { + let proto_tooltip = match tooltip { + InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s), + InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent { + kind: markup_content.kind, + value: markup_content.value, + }), + }; + proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)} + }), + location: label_part.location.map(|location| proto::Location { + start: Some(serialize_anchor(&location.range.start)), + end: Some(serialize_anchor(&location.range.end)), + buffer_id: location.buffer.id() as u64, + }), + }).collect() + }) + } + }), + }), + kind: response_hint.kind, + tooltip: response_hint.tooltip.map(|response_tooltip| { + let proto_tooltip = match response_tooltip { + InlayHintTooltip::String(s) => { + proto::inlay_hint_tooltip::Content::Value(s) + } + InlayHintTooltip::MarkupContent(markup_content) => { + proto::inlay_hint_tooltip::Content::MarkupContent( + proto::MarkupContent { + kind: markup_content.kind, + value: markup_content.value, + }, + ) + } + }; + proto::InlayHintTooltip { + content: Some(proto_tooltip), + } + }), + }) + .collect(), + version: serialize_version(buffer_version), + } } async fn response_from_proto( self, - _: proto::OnTypeFormattingResponse, - _: ModelHandle, - _: ModelHandle, - _: AsyncAppContext, + message: proto::InlayHintsResponse, + project: ModelHandle, + buffer: ModelHandle, + mut cx: AsyncAppContext, ) -> Result> { - todo!("TODO kb") + buffer + .update(&mut cx, |buffer, _| { + buffer.wait_for_version(deserialize_version(&message.version)) + }) + .await?; + + let mut hints = Vec::new(); + for message_hint in message.hints { + let hint = InlayHint { + position: message_hint + .position + .and_then(language::proto::deserialize_anchor) + .context("invalid position")?, + label: match message_hint + .label + .and_then(|label| label.label) + .context("missing label")? + { + proto::inlay_hint_label::Label::Value(s) => InlayHintLabel::String(s), + proto::inlay_hint_label::Label::LabelParts(parts) => { + let mut label_parts = Vec::new(); + for part in parts.parts { + label_parts.push(InlayHintLabelPart { + value: part.value, + tooltip: part.tooltip.map(|tooltip| match tooltip.content { + Some(proto::inlay_hint_label_part_tooltip::Content::Value(s)) => InlayHintLabelPartTooltip::String(s), + Some(proto::inlay_hint_label_part_tooltip::Content::MarkupContent(markup_content)) => InlayHintLabelPartTooltip::MarkupContent(MarkupContent { + kind: markup_content.kind, + value: markup_content.value, + }), + None => InlayHintLabelPartTooltip::String(String::new()), + }), + location: match part.location { + Some(location) => { + let target_buffer = project + .update(&mut cx, |this, cx| { + this.wait_for_remote_buffer(location.buffer_id, cx) + }) + .await?; + Some(Location { + range: location + .start + .and_then(language::proto::deserialize_anchor) + .context("invalid start")? + ..location + .end + .and_then(language::proto::deserialize_anchor) + .context("invalid end")?, + buffer: target_buffer, + })}, + None => None, + }, + }); + } + + InlayHintLabel::LabelParts(label_parts) + } + }, + kind: message_hint.kind, + tooltip: message_hint.tooltip.and_then(|tooltip| { + Some(match tooltip.content? { + proto::inlay_hint_tooltip::Content::Value(s) => InlayHintTooltip::String(s), + proto::inlay_hint_tooltip::Content::MarkupContent(markup_content) => { + InlayHintTooltip::MarkupContent(MarkupContent { + kind: markup_content.kind, + value: markup_content.value, + }) + } + }) + }), + }; + + hints.push(hint); + } + + Ok(hints) } - fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> u64 { + fn buffer_id_from_proto(message: &proto::InlayHints) -> u64 { message.buffer_id } } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 913c0bbab1..e941eee032 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -525,6 +525,7 @@ impl Project { client.add_model_request_handler(Self::handle_apply_additional_edits_for_completion); client.add_model_request_handler(Self::handle_apply_code_action); client.add_model_request_handler(Self::handle_on_type_formatting); + client.add_model_request_handler(Self::handle_inlay_hints); client.add_model_request_handler(Self::handle_reload_buffers); client.add_model_request_handler(Self::handle_synchronize_buffers); client.add_model_request_handler(Self::handle_format_buffers); @@ -6645,6 +6646,47 @@ impl Project { Ok(proto::OnTypeFormattingResponse { transaction }) } + async fn handle_inlay_hints( + this: ModelHandle, + envelope: TypedEnvelope, + _: Arc, + mut cx: AsyncAppContext, + ) -> Result { + let sender_id = envelope.original_sender_id()?; + let buffer = this.update(&mut cx, |this, cx| { + this.opened_buffers + .get(&envelope.payload.buffer_id) + .and_then(|buffer| buffer.upgrade(cx)) + .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id)) + })?; + let buffer_version = deserialize_version(&envelope.payload.version); + + buffer + .update(&mut cx, |buffer, _| { + buffer.wait_for_version(buffer_version.clone()) + }) + .await + .with_context(|| { + format!( + "waiting for version {:?} for buffer {}", + buffer_version, + buffer.id() + ) + })?; + + let buffer_hints = this + .update(&mut cx, |project, cx| { + let end = buffer.read(cx).len(); + project.inlay_hints(buffer, 0..end, cx) + }) + .await + .context("inlay hints fetch")?; + + Ok(this.update(&mut cx, |project, cx| { + InlayHints::response_to_proto(buffer_hints, project, sender_id, &buffer_version, cx) + })) + } + async fn handle_lsp_command( this: ModelHandle, envelope: TypedEnvelope, diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 2bce1ce1e3..6de98c4595 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -136,6 +136,9 @@ message Envelope { OnTypeFormattingResponse on_type_formatting_response = 112; UpdateWorktreeSettings update_worktree_settings = 113; + + InlayHints inlay_hints = 114; + InlayHintsResponse inlay_hints_response = 115; } } @@ -705,6 +708,62 @@ message OnTypeFormattingResponse { Transaction transaction = 1; } +message InlayHints { + uint64 project_id = 1; + uint64 buffer_id = 2; + Anchor start = 3; + Anchor end = 4; + repeated VectorClockEntry version = 5; +} + +message InlayHintsResponse { + repeated InlayHint hints = 1; + repeated VectorClockEntry version = 2; +} + +message InlayHint { + Anchor position = 1; + InlayHintLabel label = 2; + optional string kind = 3; + InlayHintTooltip tooltip = 4; +} + +message InlayHintLabel { + oneof label { + string value = 1; + InlayHintLabelParts label_parts = 2; + } +} + +message InlayHintLabelParts { + repeated InlayHintLabelPart parts = 1; +} + +message InlayHintLabelPart { + string value = 1; + InlayHintLabelPartTooltip tooltip = 2; + Location location = 3; +} + +message InlayHintTooltip { + oneof content { + string value = 1; + MarkupContent markup_content = 2; + } +} + +message InlayHintLabelPartTooltip { + oneof content { + string value = 1; + MarkupContent markup_content = 2; + } +} + +message MarkupContent { + string kind = 1; + string value = 2; +} + message PerformRenameResponse { ProjectTransaction transaction = 2; } diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 4532e798e7..d917ff10cf 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -198,6 +198,8 @@ messages!( (PerformRenameResponse, Background), (OnTypeFormatting, Background), (OnTypeFormattingResponse, Background), + (InlayHints, Background), + (InlayHintsResponse, Background), (Ping, Foreground), (PrepareRename, Background), (PrepareRenameResponse, Background), @@ -286,6 +288,7 @@ request_messages!( (PerformRename, PerformRenameResponse), (PrepareRename, PrepareRenameResponse), (OnTypeFormatting, OnTypeFormattingResponse), + (InlayHints, InlayHintsResponse), (ReloadBuffers, ReloadBuffersResponse), (RequestContact, Ack), (RemoveContact, Ack), @@ -332,6 +335,7 @@ entity_messages!( OpenBufferForSymbol, PerformRename, OnTypeFormatting, + InlayHints, PrepareRename, ReloadBuffers, RemoveProjectCollaborator,