From 8a3b515f56a9045dde8503440ff6c20134d85779 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 30 May 2023 16:41:57 +0300 Subject: [PATCH] Initial protocol check commit --- crates/editor/src/editor.rs | 25 +++++ crates/lsp/src/lsp.rs | 8 ++ crates/project/src/lsp_command.rs | 153 +++++++++++++++++++++++++++++- crates/project/src/project.rs | 50 ++++++++++ 4 files changed, 234 insertions(+), 2 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 8adf98f1bc..038b62f499 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2151,6 +2151,10 @@ impl Editor { } } + if let Some(hints_task) = this.request_inlay_hints(cx) { + hints_task.detach_and_log_err(cx); + } + if had_active_copilot_suggestion { this.refresh_copilot_suggestions(true, cx); if !this.has_active_copilot_suggestion(cx) { @@ -2577,6 +2581,27 @@ impl Editor { } } + // TODO kb proper inlay hints handling + fn request_inlay_hints(&self, cx: &mut ViewContext) -> Option>> { + let project = self.project.as_ref()?; + let position = self.selections.newest_anchor().head(); + let (buffer, _) = self + .buffer + .read(cx) + .text_anchor_for_position(position.clone(), cx)?; + + let end = buffer.read(cx).len(); + let inlay_hints_task = project.update(cx, |project, cx| { + project.inlay_hints(buffer.clone(), 0..end, cx) + }); + + Some(cx.spawn(|_, _| async move { + let inlay_hints = inlay_hints_task.await?; + dbg!(inlay_hints); + Ok(()) + })) + } + fn trigger_on_type_formatting( &self, input: String, diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index 1293408324..95c7dc5fa9 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -388,6 +388,9 @@ impl LanguageServer { resolve_support: None, ..WorkspaceSymbolClientCapabilities::default() }), + inlay_hint: Some(InlayHintWorkspaceClientCapabilities { + refresh_support: Default::default(), + }), ..Default::default() }), text_document: Some(TextDocumentClientCapabilities { @@ -429,6 +432,11 @@ impl LanguageServer { content_format: Some(vec![MarkupKind::Markdown]), ..Default::default() }), + // TODO kb add the resolution at least + inlay_hint: Some(InlayHintClientCapabilities { + resolve_support: None, + dynamic_registration: Some(false), + }), ..Default::default() }), experimental: Some(json!({ diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 8435de71e2..9e6b3038a3 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -1,6 +1,7 @@ use crate::{ - DocumentHighlight, Hover, HoverBlock, HoverBlockKind, Location, LocationLink, Project, - ProjectTransaction, + DocumentHighlight, Hover, HoverBlock, HoverBlockKind, InlayHint, InlayHintLabel, + InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Location, LocationLink, + MarkupContent, Project, ProjectTransaction, }; use anyhow::{anyhow, Result}; use async_trait::async_trait; @@ -126,6 +127,10 @@ pub(crate) struct OnTypeFormatting { pub push_to_history: bool, } +pub(crate) struct InlayHints { + pub range: Range, +} + pub(crate) struct FormattingOptions { tab_size: u32, } @@ -1780,3 +1785,147 @@ impl LspCommand for OnTypeFormatting { message.buffer_id } } + +#[async_trait(?Send)] +impl LspCommand for InlayHints { + type Response = Vec; + type LspRequest = lsp::InlayHintRequest; + type ProtoRequest = proto::OnTypeFormatting; + + fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool { + let Some(inlay_hint_provider) = &server_capabilities.inlay_hint_provider else { return false }; + match inlay_hint_provider { + lsp::OneOf::Left(enabled) => *enabled, + lsp::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities { + lsp::InlayHintServerCapabilities::Options(_) => true, + // TODO kb there could be dynamic registrations, resolve options + lsp::InlayHintServerCapabilities::RegistrationOptions(_) => false, + }, + } + } + + fn to_lsp( + &self, + path: &Path, + buffer: &Buffer, + _: &Arc, + _: &AppContext, + ) -> lsp::InlayHintParams { + lsp::InlayHintParams { + text_document: lsp::TextDocumentIdentifier { + uri: lsp::Url::from_file_path(path).unwrap(), + }, + range: range_to_lsp(self.range.to_point_utf16(buffer)), + work_done_progress_params: Default::default(), + } + } + + async fn response_from_lsp( + self, + message: Option>, + _: ModelHandle, + buffer: ModelHandle, + _: LanguageServerId, + cx: AsyncAppContext, + ) -> Result> { + cx.read(|cx| { + let origin_buffer = buffer.read(cx); + Ok(message + .unwrap_or_default() + .into_iter() + .map(|lsp_hint| InlayHint { + position: origin_buffer.anchor_after( + origin_buffer + .clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left), + ), + label: match lsp_hint.label { + lsp::InlayHintLabel::String(s) => InlayHintLabel::String(s), + lsp::InlayHintLabel::LabelParts(lsp_parts) => InlayHintLabel::LabelParts( + lsp_parts + .into_iter() + .map(|label_part| InlayHintLabelPart { + value: label_part.value, + tooltip: label_part.tooltip.map(|tooltip| match tooltip { + lsp::InlayHintLabelPartTooltip::String(s) => { + InlayHintLabelPartTooltip::String(s) + } + lsp::InlayHintLabelPartTooltip::MarkupContent( + markup_content, + ) => InlayHintLabelPartTooltip::MarkupContent( + MarkupContent { + kind: format!("{:?}", markup_content.kind), + value: markup_content.value, + }, + ), + }), + location: label_part.location.map(|lsp_location| { + let target_start = origin_buffer.clip_point_utf16( + point_from_lsp(lsp_location.range.start), + Bias::Left, + ); + let target_end = origin_buffer.clip_point_utf16( + point_from_lsp(lsp_location.range.end), + Bias::Left, + ); + Location { + buffer: buffer.clone(), + range: origin_buffer.anchor_after(target_start) + ..origin_buffer.anchor_before(target_end), + } + }), + }) + .collect(), + ), + }, + kind: lsp_hint.kind.map(|kind| format!("{kind:?}")), + tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip { + lsp::InlayHintTooltip::String(s) => InlayHintTooltip::String(s), + lsp::InlayHintTooltip::MarkupContent(markup_content) => { + InlayHintTooltip::MarkupContent(MarkupContent { + kind: format!("{:?}", markup_content.kind), + value: markup_content.value, + }) + } + }), + }) + .collect()) + }) + } + + fn to_proto(&self, _: u64, _: &Buffer) -> proto::OnTypeFormatting { + todo!("TODO kb") + } + + async fn from_proto( + _: proto::OnTypeFormatting, + _: ModelHandle, + _: ModelHandle, + _: AsyncAppContext, + ) -> Result { + todo!("TODO kb") + } + + fn response_to_proto( + _: Vec, + _: &mut Project, + _: PeerId, + _: &clock::Global, + _: &mut AppContext, + ) -> proto::OnTypeFormattingResponse { + todo!("TODO kb") + } + + async fn response_from_proto( + self, + _: proto::OnTypeFormattingResponse, + _: ModelHandle, + _: ModelHandle, + _: AsyncAppContext, + ) -> Result> { + todo!("TODO kb") + } + + fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> u64 { + message.buffer_id + } +} diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 270ee32bab..a7ab9e9068 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -325,6 +325,45 @@ pub struct Location { pub range: Range, } +#[derive(Debug)] +pub struct InlayHint { + pub position: Anchor, + pub label: InlayHintLabel, + pub kind: Option, + pub tooltip: Option, +} + +#[derive(Debug)] +pub enum InlayHintLabel { + String(String), + LabelParts(Vec), +} + +#[derive(Debug)] +pub struct InlayHintLabelPart { + pub value: String, + pub tooltip: Option, + pub location: Option, +} + +#[derive(Debug)] +pub enum InlayHintTooltip { + String(String), + MarkupContent(MarkupContent), +} + +#[derive(Debug)] +pub enum InlayHintLabelPartTooltip { + String(String), + MarkupContent(MarkupContent), +} + +#[derive(Debug)] +pub struct MarkupContent { + pub kind: String, + pub value: String, +} + #[derive(Debug, Clone)] pub struct LocationLink { pub origin: Option, @@ -4837,6 +4876,17 @@ impl Project { ) } + pub fn inlay_hints( + &self, + buffer_handle: ModelHandle, + range: Range, + cx: &mut ModelContext, + ) -> Task>> { + let buffer = buffer_handle.read(cx); + let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end); + self.request_lsp(buffer_handle, InlayHints { range }, cx) + } + #[allow(clippy::type_complexity)] pub fn search( &self,