@@ -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<Self>) -> Option<Task<Result<()>>> {
+ 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,
@@ -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!({
@@ -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<Anchor>,
+}
+
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<InlayHint>;
+ 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<LanguageServer>,
+ _: &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<Vec<lsp::InlayHint>>,
+ _: ModelHandle<Project>,
+ buffer: ModelHandle<Buffer>,
+ _: LanguageServerId,
+ cx: AsyncAppContext,
+ ) -> Result<Vec<InlayHint>> {
+ 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<Project>,
+ _: ModelHandle<Buffer>,
+ _: AsyncAppContext,
+ ) -> Result<Self> {
+ todo!("TODO kb")
+ }
+
+ fn response_to_proto(
+ _: Vec<InlayHint>,
+ _: &mut Project,
+ _: PeerId,
+ _: &clock::Global,
+ _: &mut AppContext,
+ ) -> proto::OnTypeFormattingResponse {
+ todo!("TODO kb")
+ }
+
+ async fn response_from_proto(
+ self,
+ _: proto::OnTypeFormattingResponse,
+ _: ModelHandle<Project>,
+ _: ModelHandle<Buffer>,
+ _: AsyncAppContext,
+ ) -> Result<Vec<InlayHint>> {
+ todo!("TODO kb")
+ }
+
+ fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> u64 {
+ message.buffer_id
+ }
+}
@@ -325,6 +325,45 @@ pub struct Location {
pub range: Range<language::Anchor>,
}
+#[derive(Debug)]
+pub struct InlayHint {
+ pub position: Anchor,
+ pub label: InlayHintLabel,
+ pub kind: Option<String>,
+ pub tooltip: Option<InlayHintTooltip>,
+}
+
+#[derive(Debug)]
+pub enum InlayHintLabel {
+ String(String),
+ LabelParts(Vec<InlayHintLabelPart>),
+}
+
+#[derive(Debug)]
+pub struct InlayHintLabelPart {
+ pub value: String,
+ pub tooltip: Option<InlayHintLabelPartTooltip>,
+ pub location: Option<Location>,
+}
+
+#[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<Location>,
@@ -4837,6 +4876,17 @@ impl Project {
)
}
+ pub fn inlay_hints<T: ToOffset>(
+ &self,
+ buffer_handle: ModelHandle<Buffer>,
+ range: Range<T>,
+ cx: &mut ModelContext<Self>,
+ ) -> Task<Result<Vec<InlayHint>>> {
+ 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,