Send inlay hint resolve requests
This commit is contained in:
parent
3434990b70
commit
80e8714241
8 changed files with 705 additions and 343 deletions
|
@ -1125,7 +1125,6 @@ mod tests {
|
|||
Anchor::min(),
|
||||
&InlayHint {
|
||||
label: InlayHintLabel::String("a".to_string()),
|
||||
buffer_id: 0,
|
||||
position: text::Anchor::default(),
|
||||
padding_left: false,
|
||||
padding_right: false,
|
||||
|
@ -1146,7 +1145,6 @@ mod tests {
|
|||
Anchor::min(),
|
||||
&InlayHint {
|
||||
label: InlayHintLabel::String("a".to_string()),
|
||||
buffer_id: 0,
|
||||
position: text::Anchor::default(),
|
||||
padding_left: true,
|
||||
padding_right: true,
|
||||
|
@ -1167,7 +1165,6 @@ mod tests {
|
|||
Anchor::min(),
|
||||
&InlayHint {
|
||||
label: InlayHintLabel::String(" a ".to_string()),
|
||||
buffer_id: 0,
|
||||
position: text::Anchor::default(),
|
||||
padding_left: false,
|
||||
padding_right: false,
|
||||
|
@ -1188,7 +1185,6 @@ mod tests {
|
|||
Anchor::min(),
|
||||
&InlayHint {
|
||||
label: InlayHintLabel::String(" a ".to_string()),
|
||||
buffer_id: 0,
|
||||
position: text::Anchor::default(),
|
||||
padding_left: true,
|
||||
padding_right: true,
|
||||
|
|
|
@ -42,7 +42,7 @@ use language::{
|
|||
};
|
||||
use project::{
|
||||
project_settings::{GitGutterSetting, ProjectSettings},
|
||||
InlayHintLabelPart, ProjectPath,
|
||||
InlayHintLabelPart, ProjectPath, ResolveState,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
|
@ -456,82 +456,21 @@ impl EditorElement {
|
|||
) -> bool {
|
||||
// This will be handled more correctly once https://github.com/zed-industries/zed/issues/1218 is completed
|
||||
// Don't trigger hover popover if mouse is hovering over context menu
|
||||
let mut go_to_definition_point = None;
|
||||
let mut hover_at_point = None;
|
||||
if text_bounds.contains_point(position) {
|
||||
let point_for_position = position_map.point_for_position(text_bounds, position);
|
||||
if let Some(point) = point_for_position.as_valid() {
|
||||
update_go_to_definition_link(editor, Some(point), cmd, shift, cx);
|
||||
hover_at(editor, Some(point), cx);
|
||||
return true;
|
||||
go_to_definition_point = Some(point);
|
||||
hover_at_point = Some(point);
|
||||
} else {
|
||||
let hint_start_offset = position_map
|
||||
.snapshot
|
||||
.display_point_to_inlay_offset(point_for_position.previous_valid, Bias::Left);
|
||||
let hint_end_offset = position_map
|
||||
.snapshot
|
||||
.display_point_to_inlay_offset(point_for_position.next_valid, Bias::Right);
|
||||
let offset_overshoot = point_for_position.column_overshoot_after_line_end as usize;
|
||||
let hovered_offset = if offset_overshoot == 0 {
|
||||
Some(position_map.snapshot.display_point_to_inlay_offset(
|
||||
point_for_position.exact_unclipped,
|
||||
Bias::Left,
|
||||
))
|
||||
} else if (hint_end_offset - hint_start_offset).0 >= offset_overshoot {
|
||||
Some(InlayOffset(hint_start_offset.0 + offset_overshoot))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(hovered_offset) = hovered_offset {
|
||||
let buffer = editor.buffer().read(cx);
|
||||
let snapshot = buffer.snapshot(cx);
|
||||
let previous_valid_anchor = snapshot.anchor_at(
|
||||
point_for_position
|
||||
.previous_valid
|
||||
.to_point(&position_map.snapshot.display_snapshot),
|
||||
Bias::Left,
|
||||
);
|
||||
let next_valid_anchor = snapshot.anchor_at(
|
||||
point_for_position
|
||||
.next_valid
|
||||
.to_point(&position_map.snapshot.display_snapshot),
|
||||
Bias::Right,
|
||||
);
|
||||
if let Some(hovered_hint) = editor
|
||||
.visible_inlay_hints(cx)
|
||||
.into_iter()
|
||||
.skip_while(|hint| {
|
||||
hint.position.cmp(&previous_valid_anchor, &snapshot).is_lt()
|
||||
})
|
||||
.take_while(|hint| hint.position.cmp(&next_valid_anchor, &snapshot).is_le())
|
||||
.max_by_key(|hint| hint.id)
|
||||
{
|
||||
if let Some(cached_hint) = editor
|
||||
.inlay_hint_cache()
|
||||
.hint_by_id(previous_valid_anchor.excerpt_id, hovered_hint.id)
|
||||
{
|
||||
match &cached_hint.label {
|
||||
project::InlayHintLabel::String(regular_label) => {
|
||||
// TODO kb remove + check for tooltip for hover and resolve, if needed
|
||||
eprintln!("regular string: {regular_label}");
|
||||
}
|
||||
project::InlayHintLabel::LabelParts(label_parts) => {
|
||||
if let Some(hovered_hint_part) = find_hovered_hint_part(
|
||||
&label_parts,
|
||||
hint_start_offset..hint_end_offset,
|
||||
hovered_offset,
|
||||
) {
|
||||
// TODO kb remove + check for tooltip and location and resolve, if needed
|
||||
eprintln!("hint_part: {hovered_hint_part:?}");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
(go_to_definition_point, hover_at_point) =
|
||||
inlay_link_and_hover_points(position_map, point_for_position, editor, cx);
|
||||
}
|
||||
};
|
||||
|
||||
update_go_to_definition_link(editor, None, cmd, shift, cx);
|
||||
hover_at(editor, None, cx);
|
||||
update_go_to_definition_link(editor, go_to_definition_point, cmd, shift, cx);
|
||||
hover_at(editor, hover_at_point, cx);
|
||||
true
|
||||
}
|
||||
|
||||
|
@ -1876,6 +1815,104 @@ impl EditorElement {
|
|||
}
|
||||
}
|
||||
|
||||
fn inlay_link_and_hover_points(
|
||||
position_map: &PositionMap,
|
||||
point_for_position: PointForPosition,
|
||||
editor: &mut Editor,
|
||||
cx: &mut ViewContext<'_, '_, Editor>,
|
||||
) -> (Option<DisplayPoint>, Option<DisplayPoint>) {
|
||||
let hint_start_offset = position_map
|
||||
.snapshot
|
||||
.display_point_to_inlay_offset(point_for_position.previous_valid, Bias::Left);
|
||||
let hint_end_offset = position_map
|
||||
.snapshot
|
||||
.display_point_to_inlay_offset(point_for_position.next_valid, Bias::Right);
|
||||
let offset_overshoot = point_for_position.column_overshoot_after_line_end as usize;
|
||||
let hovered_offset = if offset_overshoot == 0 {
|
||||
Some(
|
||||
position_map
|
||||
.snapshot
|
||||
.display_point_to_inlay_offset(point_for_position.exact_unclipped, Bias::Left),
|
||||
)
|
||||
} else if (hint_end_offset - hint_start_offset).0 >= offset_overshoot {
|
||||
Some(InlayOffset(hint_start_offset.0 + offset_overshoot))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut go_to_definition_point = None;
|
||||
let mut hover_at_point = None;
|
||||
if let Some(hovered_offset) = hovered_offset {
|
||||
let buffer = editor.buffer().read(cx);
|
||||
let snapshot = buffer.snapshot(cx);
|
||||
let previous_valid_anchor = snapshot.anchor_at(
|
||||
point_for_position
|
||||
.previous_valid
|
||||
.to_point(&position_map.snapshot.display_snapshot),
|
||||
Bias::Left,
|
||||
);
|
||||
let next_valid_anchor = snapshot.anchor_at(
|
||||
point_for_position
|
||||
.next_valid
|
||||
.to_point(&position_map.snapshot.display_snapshot),
|
||||
Bias::Right,
|
||||
);
|
||||
if let Some(hovered_hint) = editor
|
||||
.visible_inlay_hints(cx)
|
||||
.into_iter()
|
||||
.skip_while(|hint| hint.position.cmp(&previous_valid_anchor, &snapshot).is_lt())
|
||||
.take_while(|hint| hint.position.cmp(&next_valid_anchor, &snapshot).is_le())
|
||||
.max_by_key(|hint| hint.id)
|
||||
{
|
||||
let inlay_hint_cache = editor.inlay_hint_cache();
|
||||
if let Some(cached_hint) =
|
||||
inlay_hint_cache.hint_by_id(previous_valid_anchor.excerpt_id, hovered_hint.id)
|
||||
{
|
||||
match &cached_hint.resolve_state {
|
||||
ResolveState::CanResolve(_, _) => {
|
||||
if let Some(buffer_id) = previous_valid_anchor.buffer_id {
|
||||
inlay_hint_cache.spawn_hint_resolve(
|
||||
buffer_id,
|
||||
previous_valid_anchor.excerpt_id,
|
||||
hovered_hint.id,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
ResolveState::Resolved => {
|
||||
match &cached_hint.label {
|
||||
project::InlayHintLabel::String(_) => {
|
||||
if cached_hint.tooltip.is_some() {
|
||||
dbg!(&cached_hint.tooltip); // TODO kb
|
||||
// hover_at_point = Some(hovered_offset);
|
||||
}
|
||||
}
|
||||
project::InlayHintLabel::LabelParts(label_parts) => {
|
||||
if let Some(hovered_hint_part) = find_hovered_hint_part(
|
||||
&label_parts,
|
||||
hint_start_offset..hint_end_offset,
|
||||
hovered_offset,
|
||||
) {
|
||||
if hovered_hint_part.tooltip.is_some() {
|
||||
dbg!(&hovered_hint_part.tooltip); // TODO kb
|
||||
// hover_at_point = Some(hovered_offset);
|
||||
}
|
||||
if let Some(location) = &hovered_hint_part.location {
|
||||
dbg!(location); // TODO kb
|
||||
// go_to_definition_point = Some(location);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
ResolveState::Resolving => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(go_to_definition_point, hover_at_point)
|
||||
}
|
||||
|
||||
fn find_hovered_hint_part<'a>(
|
||||
label_parts: &'a [InlayHintLabelPart],
|
||||
hint_range: Range<InlayOffset>,
|
||||
|
|
|
@ -13,7 +13,7 @@ use gpui::{ModelContext, ModelHandle, Task, ViewContext};
|
|||
use language::{language_settings::InlayHintKind, Buffer, BufferSnapshot};
|
||||
use log::error;
|
||||
use parking_lot::RwLock;
|
||||
use project::InlayHint;
|
||||
use project::{InlayHint, ResolveState};
|
||||
|
||||
use collections::{hash_map, HashMap, HashSet};
|
||||
use language::language_settings::InlayHintSettings;
|
||||
|
@ -60,7 +60,7 @@ struct ExcerptHintsUpdate {
|
|||
excerpt_id: ExcerptId,
|
||||
remove_from_visible: Vec<InlayId>,
|
||||
remove_from_cache: HashSet<InlayId>,
|
||||
add_to_cache: HashSet<InlayHint>,
|
||||
add_to_cache: Vec<InlayHint>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
@ -409,6 +409,79 @@ impl InlayHintCache {
|
|||
pub fn version(&self) -> usize {
|
||||
self.version
|
||||
}
|
||||
|
||||
pub fn spawn_hint_resolve(
|
||||
&self,
|
||||
buffer_id: u64,
|
||||
excerpt_id: ExcerptId,
|
||||
id: InlayId,
|
||||
cx: &mut ViewContext<'_, '_, Editor>,
|
||||
) {
|
||||
if let Some(excerpt_hints) = self.hints.get(&excerpt_id) {
|
||||
let mut guard = excerpt_hints.write();
|
||||
if let Some(cached_hint) = guard
|
||||
.hints
|
||||
.iter_mut()
|
||||
.find(|(hint_id, _)| hint_id == &id)
|
||||
.map(|(_, hint)| hint)
|
||||
{
|
||||
if let ResolveState::CanResolve(server_id, _) = &cached_hint.resolve_state {
|
||||
let hint_to_resolve = cached_hint.clone();
|
||||
let server_id = *server_id;
|
||||
cached_hint.resolve_state = ResolveState::Resolving;
|
||||
drop(guard);
|
||||
cx.spawn(|editor, mut cx| async move {
|
||||
let resolved_hint_task = editor.update(&mut cx, |editor, cx| {
|
||||
editor
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.buffer(buffer_id)
|
||||
.and_then(|buffer| {
|
||||
let project = editor.project.as_ref()?;
|
||||
Some(project.update(cx, |project, cx| {
|
||||
project.resolve_inlay_hint(
|
||||
hint_to_resolve,
|
||||
buffer,
|
||||
server_id,
|
||||
cx,
|
||||
)
|
||||
}))
|
||||
})
|
||||
})?;
|
||||
if let Some(resolved_hint_task) = resolved_hint_task {
|
||||
if let Some(mut resolved_hint) =
|
||||
resolved_hint_task.await.context("hint resolve task")?
|
||||
{
|
||||
editor.update(&mut cx, |editor, _| {
|
||||
if let Some(excerpt_hints) =
|
||||
editor.inlay_hint_cache.hints.get(&excerpt_id)
|
||||
{
|
||||
let mut guard = excerpt_hints.write();
|
||||
if let Some(cached_hint) = guard
|
||||
.hints
|
||||
.iter_mut()
|
||||
.find(|(hint_id, _)| hint_id == &id)
|
||||
.map(|(_, hint)| hint)
|
||||
{
|
||||
if cached_hint.resolve_state == ResolveState::Resolving
|
||||
{
|
||||
resolved_hint.resolve_state =
|
||||
ResolveState::Resolved;
|
||||
*cached_hint = resolved_hint;
|
||||
}
|
||||
}
|
||||
}
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_new_update_tasks(
|
||||
|
@ -632,7 +705,7 @@ fn calculate_hint_updates(
|
|||
cached_excerpt_hints: Option<Arc<RwLock<CachedExcerptHints>>>,
|
||||
visible_hints: &[Inlay],
|
||||
) -> Option<ExcerptHintsUpdate> {
|
||||
let mut add_to_cache: HashSet<InlayHint> = HashSet::default();
|
||||
let mut add_to_cache = Vec::<InlayHint>::new();
|
||||
let mut excerpt_hints_to_persist = HashMap::default();
|
||||
for new_hint in new_excerpt_hints {
|
||||
if !contains_position(&fetch_range, new_hint.position, buffer_snapshot) {
|
||||
|
@ -659,7 +732,7 @@ fn calculate_hint_updates(
|
|||
None => true,
|
||||
};
|
||||
if missing_from_cache {
|
||||
add_to_cache.insert(new_hint);
|
||||
add_to_cache.push(new_hint);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
DocumentHighlight, Hover, HoverBlock, HoverBlockKind, InlayHint, InlayHintLabel,
|
||||
InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Location, LocationLink,
|
||||
InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Item, Location, LocationLink,
|
||||
MarkupContent, Project, ProjectTransaction, ResolveState,
|
||||
};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
|
@ -12,8 +12,9 @@ use language::{
|
|||
language_settings::{language_settings, InlayHintKind},
|
||||
point_from_lsp, point_to_lsp,
|
||||
proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},
|
||||
range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, CachedLspAdapter, CharKind, CodeAction,
|
||||
Completion, OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction, Unclipped,
|
||||
range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CharKind,
|
||||
CodeAction, Completion, OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction,
|
||||
Unclipped,
|
||||
};
|
||||
use lsp::{DocumentHighlightKind, LanguageServer, LanguageServerId, ServerCapabilities};
|
||||
use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc};
|
||||
|
@ -1776,6 +1777,371 @@ impl LspCommand for OnTypeFormatting {
|
|||
}
|
||||
}
|
||||
|
||||
impl InlayHints {
|
||||
pub fn lsp_to_project_hint(
|
||||
lsp_hint: lsp::InlayHint,
|
||||
buffer_handle: &ModelHandle<Buffer>,
|
||||
resolve_state: ResolveState,
|
||||
force_no_type_left_padding: bool,
|
||||
cx: &AppContext,
|
||||
) -> InlayHint {
|
||||
let kind = lsp_hint.kind.and_then(|kind| match kind {
|
||||
lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
|
||||
lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
|
||||
_ => None,
|
||||
});
|
||||
let buffer = buffer_handle.read(cx);
|
||||
let position = buffer.clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
|
||||
let padding_left = if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
|
||||
false
|
||||
} else {
|
||||
lsp_hint.padding_left.unwrap_or(false)
|
||||
};
|
||||
InlayHint {
|
||||
position: if kind == Some(InlayHintKind::Parameter) {
|
||||
buffer.anchor_before(position)
|
||||
} else {
|
||||
buffer.anchor_after(position)
|
||||
},
|
||||
padding_left,
|
||||
padding_right: lsp_hint.padding_right.unwrap_or(false),
|
||||
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: match markup_content.kind {
|
||||
lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
|
||||
lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
|
||||
},
|
||||
value: markup_content.value,
|
||||
})
|
||||
}
|
||||
}),
|
||||
location: label_part.location.map(|lsp_location| {
|
||||
let target_start = buffer.clip_point_utf16(
|
||||
point_from_lsp(lsp_location.range.start),
|
||||
Bias::Left,
|
||||
);
|
||||
let target_end = buffer.clip_point_utf16(
|
||||
point_from_lsp(lsp_location.range.end),
|
||||
Bias::Left,
|
||||
);
|
||||
Location {
|
||||
buffer: buffer_handle.clone(),
|
||||
range: buffer.anchor_after(target_start)
|
||||
..buffer.anchor_before(target_end),
|
||||
}
|
||||
}),
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
},
|
||||
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: match markup_content.kind {
|
||||
lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
|
||||
lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
|
||||
},
|
||||
value: markup_content.value,
|
||||
})
|
||||
}
|
||||
}),
|
||||
resolve_state,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn project_to_proto_hint(response_hint: InlayHint, cx: &AppContext) -> proto::InlayHint {
|
||||
let (state, lsp_resolve_state) = match response_hint.resolve_state {
|
||||
ResolveState::CanResolve(server_id, resolve_data) => (
|
||||
0,
|
||||
resolve_data
|
||||
.map(|json_data| {
|
||||
serde_json::to_string(&json_data)
|
||||
.expect("failed to serialize resolve json data")
|
||||
})
|
||||
.map(|value| proto::resolve_state::LspResolveState {
|
||||
server_id: server_id.0 as u64,
|
||||
value,
|
||||
}),
|
||||
),
|
||||
ResolveState::Resolved => (1, None),
|
||||
ResolveState::Resolving => (2, None),
|
||||
};
|
||||
let resolve_state = Some(proto::ResolveState {
|
||||
state,
|
||||
lsp_resolve_state,
|
||||
});
|
||||
proto::InlayHint {
|
||||
position: Some(language::proto::serialize_anchor(&response_hint.position)),
|
||||
padding_left: response_hint.padding_left,
|
||||
padding_right: response_hint.padding_right,
|
||||
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 {
|
||||
is_markdown: markup_content.kind == HoverBlockKind::Markdown,
|
||||
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.read(cx).remote_id(),
|
||||
}),
|
||||
}).collect()
|
||||
})
|
||||
}
|
||||
}),
|
||||
}),
|
||||
kind: response_hint.kind.map(|kind| kind.name().to_string()),
|
||||
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 {
|
||||
is_markdown: markup_content.kind == HoverBlockKind::Markdown,
|
||||
value: markup_content.value,
|
||||
},
|
||||
)
|
||||
}
|
||||
};
|
||||
proto::InlayHintTooltip {
|
||||
content: Some(proto_tooltip),
|
||||
}
|
||||
}),
|
||||
resolve_state,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn proto_to_project_hint(
|
||||
message_hint: proto::InlayHint,
|
||||
project: &ModelHandle<Project>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> anyhow::Result<InlayHint> {
|
||||
let buffer_id = message_hint
|
||||
.position
|
||||
.as_ref()
|
||||
.and_then(|location| location.buffer_id)
|
||||
.context("missing buffer id")?;
|
||||
let resolve_state = message_hint.resolve_state.as_ref().unwrap_or_else(|| {
|
||||
panic!("incorrect proto inlay hint message: no resolve state in hint {message_hint:?}",)
|
||||
});
|
||||
let resolve_state_data = resolve_state
|
||||
.lsp_resolve_state.as_ref()
|
||||
.map(|lsp_resolve_state| {
|
||||
serde_json::from_str::<Option<lsp::LSPAny>>(&lsp_resolve_state.value)
|
||||
.with_context(|| format!("incorrect proto inlay hint message: non-json resolve state {lsp_resolve_state:?}"))
|
||||
.map(|state| (LanguageServerId(lsp_resolve_state.server_id as usize), state))
|
||||
})
|
||||
.transpose()?;
|
||||
let resolve_state = match resolve_state.state {
|
||||
0 => ResolveState::Resolved,
|
||||
1 => {
|
||||
let (server_id, lsp_resolve_state) = resolve_state_data.with_context(|| {
|
||||
format!(
|
||||
"No lsp resolve data for the hint that can be resolved: {message_hint:?}"
|
||||
)
|
||||
})?;
|
||||
ResolveState::CanResolve(server_id, lsp_resolve_state)
|
||||
}
|
||||
2 => ResolveState::Resolving,
|
||||
invalid => {
|
||||
anyhow::bail!("Unexpected resolve state {invalid} for hint {message_hint:?}")
|
||||
}
|
||||
};
|
||||
Ok(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 {
|
||||
let buffer = project
|
||||
.update(cx, |this, cx| this.wait_for_remote_buffer(buffer_id, cx))
|
||||
.await?;
|
||||
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: if markup_content.is_markdown {
|
||||
HoverBlockKind::Markdown
|
||||
} else {
|
||||
HoverBlockKind::PlainText
|
||||
},
|
||||
value: markup_content.value,
|
||||
}),
|
||||
None => InlayHintLabelPartTooltip::String(String::new()),
|
||||
}),
|
||||
location: match part.location {
|
||||
Some(location) => 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,
|
||||
}),
|
||||
None => None,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
InlayHintLabel::LabelParts(label_parts)
|
||||
}
|
||||
},
|
||||
padding_left: message_hint.padding_left,
|
||||
padding_right: message_hint.padding_right,
|
||||
kind: message_hint
|
||||
.kind
|
||||
.as_deref()
|
||||
.and_then(InlayHintKind::from_name),
|
||||
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: if markup_content.is_markdown {
|
||||
HoverBlockKind::Markdown
|
||||
} else {
|
||||
HoverBlockKind::PlainText
|
||||
},
|
||||
value: markup_content.value,
|
||||
})
|
||||
}
|
||||
})
|
||||
}),
|
||||
resolve_state,
|
||||
})
|
||||
}
|
||||
|
||||
// TODO kb instead, store all LSP data inside the project::InlayHint?
|
||||
pub fn project_to_lsp_hint(
|
||||
hint: InlayHint,
|
||||
project: &ModelHandle<Project>,
|
||||
snapshot: &BufferSnapshot,
|
||||
cx: &AsyncAppContext,
|
||||
) -> lsp::InlayHint {
|
||||
lsp::InlayHint {
|
||||
position: point_to_lsp(hint.position.to_point_utf16(snapshot)),
|
||||
kind: hint.kind.map(|kind| match kind {
|
||||
InlayHintKind::Type => lsp::InlayHintKind::TYPE,
|
||||
InlayHintKind::Parameter => lsp::InlayHintKind::PARAMETER,
|
||||
}),
|
||||
text_edits: None,
|
||||
tooltip: hint.tooltip.and_then(|tooltip| {
|
||||
Some(match tooltip {
|
||||
InlayHintTooltip::String(s) => lsp::InlayHintTooltip::String(s),
|
||||
InlayHintTooltip::MarkupContent(markup_content) => {
|
||||
lsp::InlayHintTooltip::MarkupContent(lsp::MarkupContent {
|
||||
kind: match markup_content.kind {
|
||||
HoverBlockKind::PlainText => lsp::MarkupKind::PlainText,
|
||||
HoverBlockKind::Markdown => lsp::MarkupKind::Markdown,
|
||||
HoverBlockKind::Code { .. } => return None,
|
||||
},
|
||||
value: markup_content.value,
|
||||
})
|
||||
}
|
||||
})
|
||||
}),
|
||||
label: match hint.label {
|
||||
InlayHintLabel::String(s) => lsp::InlayHintLabel::String(s),
|
||||
InlayHintLabel::LabelParts(label_parts) => lsp::InlayHintLabel::LabelParts(
|
||||
label_parts
|
||||
.into_iter()
|
||||
.map(|part| lsp::InlayHintLabelPart {
|
||||
value: part.value,
|
||||
tooltip: part.tooltip.and_then(|tooltip| {
|
||||
Some(match tooltip {
|
||||
InlayHintLabelPartTooltip::String(s) => {
|
||||
lsp::InlayHintLabelPartTooltip::String(s)
|
||||
}
|
||||
InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
|
||||
lsp::InlayHintLabelPartTooltip::MarkupContent(
|
||||
lsp::MarkupContent {
|
||||
kind: match markup_content.kind {
|
||||
HoverBlockKind::PlainText => {
|
||||
lsp::MarkupKind::PlainText
|
||||
}
|
||||
HoverBlockKind::Markdown => {
|
||||
lsp::MarkupKind::Markdown
|
||||
}
|
||||
HoverBlockKind::Code { .. } => return None,
|
||||
},
|
||||
value: markup_content.value,
|
||||
},
|
||||
)
|
||||
}
|
||||
})
|
||||
}),
|
||||
location: part.location.and_then(|location| {
|
||||
let path = cx.read(|cx| {
|
||||
let project_path = location.buffer.read(cx).project_path(cx)?;
|
||||
project.read(cx).absolute_path(&project_path, cx)
|
||||
})?;
|
||||
Some(lsp::Location::new(
|
||||
lsp::Url::from_file_path(path).unwrap(),
|
||||
range_to_lsp(
|
||||
location.range.start.to_point_utf16(snapshot)
|
||||
..location.range.end.to_point_utf16(snapshot),
|
||||
),
|
||||
))
|
||||
}),
|
||||
command: None,
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
},
|
||||
padding_left: Some(hint.padding_left),
|
||||
padding_right: Some(hint.padding_right),
|
||||
data: match hint.resolve_state {
|
||||
ResolveState::CanResolve(_, data) => data,
|
||||
ResolveState::Resolving | ResolveState::Resolved => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspCommand for InlayHints {
|
||||
type Response = Vec<InlayHint>;
|
||||
|
@ -1829,7 +2195,6 @@ impl LspCommand for InlayHints {
|
|||
let force_no_type_left_padding =
|
||||
lsp_adapter.name.0.as_ref() == "typescript-language-server";
|
||||
cx.read(|cx| {
|
||||
let origin_buffer = buffer.read(cx);
|
||||
Ok(message
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
|
@ -1840,88 +2205,18 @@ impl LspCommand for InlayHints {
|
|||
resolve_provider: Some(true),
|
||||
..
|
||||
},
|
||||
))) => ResolveState::CanResolve(lsp_hint.data),
|
||||
))) => {
|
||||
ResolveState::CanResolve(lsp_server.server_id(), lsp_hint.data.clone())
|
||||
}
|
||||
_ => ResolveState::Resolved,
|
||||
};
|
||||
let kind = lsp_hint.kind.and_then(|kind| match kind {
|
||||
lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
|
||||
lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
|
||||
_ => None,
|
||||
});
|
||||
let position = origin_buffer
|
||||
.clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
|
||||
let padding_left =
|
||||
if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
|
||||
false
|
||||
} else {
|
||||
lsp_hint.padding_left.unwrap_or(false)
|
||||
};
|
||||
InlayHint {
|
||||
buffer_id: origin_buffer.remote_id(),
|
||||
position: if kind == Some(InlayHintKind::Parameter) {
|
||||
origin_buffer.anchor_before(position)
|
||||
} else {
|
||||
origin_buffer.anchor_after(position)
|
||||
},
|
||||
padding_left,
|
||||
padding_right: lsp_hint.padding_right.unwrap_or(false),
|
||||
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,
|
||||
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,
|
||||
})
|
||||
}
|
||||
}),
|
||||
InlayHints::lsp_to_project_hint(
|
||||
lsp_hint,
|
||||
&buffer,
|
||||
resolve_state,
|
||||
}
|
||||
force_no_type_left_padding,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.collect())
|
||||
})
|
||||
|
@ -1970,69 +2265,7 @@ impl LspCommand for InlayHints {
|
|||
proto::InlayHintsResponse {
|
||||
hints: response
|
||||
.into_iter()
|
||||
.map(|response_hint| {
|
||||
let (state, lsp_resolve_state) = match response_hint.resolve_state {
|
||||
ResolveState::CanResolve(resolve_data) => {
|
||||
(0, resolve_data.map(|json_data| serde_json::to_string(&json_data).expect("failed to serialize resolve json data")).map(|value| proto::resolve_state::LspResolveState{ value }))
|
||||
}
|
||||
ResolveState::Resolved => (1, None),
|
||||
ResolveState::Resolving => (2, None),
|
||||
};
|
||||
let resolve_state = Some(proto::ResolveState {
|
||||
state, lsp_resolve_state
|
||||
});
|
||||
proto::InlayHint {
|
||||
position: Some(language::proto::serialize_anchor(&response_hint.position)),
|
||||
padding_left: response_hint.padding_left,
|
||||
padding_right: response_hint.padding_right,
|
||||
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.read(cx).remote_id(),
|
||||
}),
|
||||
}).collect()
|
||||
})
|
||||
}
|
||||
}),
|
||||
}),
|
||||
kind: response_hint.kind.map(|kind| kind.name().to_string()),
|
||||
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),
|
||||
}
|
||||
}),
|
||||
resolve_state,
|
||||
}})
|
||||
.map(|response_hint| InlayHints::project_to_proto_hint(response_hint, cx))
|
||||
.collect(),
|
||||
version: serialize_version(buffer_version),
|
||||
}
|
||||
|
@ -2053,104 +2286,7 @@ impl LspCommand for InlayHints {
|
|||
|
||||
let mut hints = Vec::new();
|
||||
for message_hint in message.hints {
|
||||
let buffer_id = message_hint
|
||||
.position
|
||||
.as_ref()
|
||||
.and_then(|location| location.buffer_id)
|
||||
.context("missing buffer id")?;
|
||||
let resolve_state = message_hint.resolve_state.as_ref().unwrap_or_else(|| {
|
||||
panic!(
|
||||
"incorrect proto inlay hint message: no resolve state in hint {message_hint:?}",
|
||||
)
|
||||
});
|
||||
|
||||
let lsp_resolve_state = resolve_state
|
||||
.lsp_resolve_state.as_ref()
|
||||
.map(|lsp_resolve_state| {
|
||||
serde_json::from_str::<lsp::LSPAny>(&lsp_resolve_state.value)
|
||||
.with_context(|| format!("incorrect proto inlay hint message: non-json resolve state {lsp_resolve_state:?}"))
|
||||
})
|
||||
.transpose()?;
|
||||
let resolve_state = match resolve_state.state {
|
||||
0 => ResolveState::Resolved,
|
||||
1 => ResolveState::CanResolve(lsp_resolve_state),
|
||||
2 => ResolveState::Resolving,
|
||||
invalid => {
|
||||
anyhow::bail!("Unexpected resolve state {invalid} for hint {message_hint:?}")
|
||||
}
|
||||
};
|
||||
let hint = InlayHint {
|
||||
buffer_id,
|
||||
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)
|
||||
}
|
||||
},
|
||||
padding_left: message_hint.padding_left,
|
||||
padding_right: message_hint.padding_right,
|
||||
kind: message_hint
|
||||
.kind
|
||||
.as_deref()
|
||||
.and_then(InlayHintKind::from_name),
|
||||
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,
|
||||
})
|
||||
}
|
||||
})
|
||||
}),
|
||||
resolve_state,
|
||||
};
|
||||
|
||||
hints.push(hint);
|
||||
hints.push(InlayHints::proto_to_project_hint(message_hint, &project, &mut cx).await?);
|
||||
}
|
||||
|
||||
Ok(hints)
|
||||
|
|
|
@ -333,9 +333,8 @@ pub struct Location {
|
|||
pub range: Range<language::Anchor>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct InlayHint {
|
||||
pub buffer_id: u64,
|
||||
pub position: language::Anchor,
|
||||
pub label: InlayHintLabel,
|
||||
pub kind: Option<InlayHintKind>,
|
||||
|
@ -348,18 +347,10 @@ pub struct InlayHint {
|
|||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ResolveState {
|
||||
Resolved,
|
||||
CanResolve(Option<lsp::LSPAny>),
|
||||
CanResolve(LanguageServerId, Option<lsp::LSPAny>),
|
||||
Resolving,
|
||||
}
|
||||
|
||||
impl Hash for ResolveState {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
// Regular `lsp::LSPAny` is not hashable, so we can't hash it.
|
||||
// LSP expects this data to not to change between requests, so we only hash the discriminant.
|
||||
std::mem::discriminant(self).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl InlayHint {
|
||||
pub fn text(&self) -> String {
|
||||
match &self.label {
|
||||
|
@ -369,34 +360,34 @@ impl InlayHint {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum InlayHintLabel {
|
||||
String(String),
|
||||
LabelParts(Vec<InlayHintLabelPart>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct InlayHintLabelPart {
|
||||
pub value: String,
|
||||
pub tooltip: Option<InlayHintLabelPartTooltip>,
|
||||
pub location: Option<Location>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum InlayHintTooltip {
|
||||
String(String),
|
||||
MarkupContent(MarkupContent),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum InlayHintLabelPartTooltip {
|
||||
String(String),
|
||||
MarkupContent(MarkupContent),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MarkupContent {
|
||||
pub kind: String,
|
||||
pub kind: HoverBlockKind,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
|
@ -430,7 +421,7 @@ pub struct HoverBlock {
|
|||
pub kind: HoverBlockKind,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum HoverBlockKind {
|
||||
PlainText,
|
||||
Markdown,
|
||||
|
@ -567,6 +558,7 @@ impl Project {
|
|||
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_resolve_inlay_hint);
|
||||
client.add_model_request_handler(Self::handle_refresh_inlay_hints);
|
||||
client.add_model_request_handler(Self::handle_reload_buffers);
|
||||
client.add_model_request_handler(Self::handle_synchronize_buffers);
|
||||
|
@ -4985,7 +4977,7 @@ impl Project {
|
|||
buffer_handle: ModelHandle<Buffer>,
|
||||
range: Range<T>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<Vec<InlayHint>>> {
|
||||
) -> Task<anyhow::Result<Vec<InlayHint>>> {
|
||||
let buffer = buffer_handle.read(cx);
|
||||
let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
|
||||
let range_start = range.start;
|
||||
|
@ -5035,6 +5027,79 @@ impl Project {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn resolve_inlay_hint(
|
||||
&self,
|
||||
hint: InlayHint,
|
||||
buffer_handle: ModelHandle<Buffer>,
|
||||
server_id: LanguageServerId,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<anyhow::Result<Option<InlayHint>>> {
|
||||
if self.is_local() {
|
||||
let buffer = buffer_handle.read(cx);
|
||||
let (_, lang_server) = if let Some((adapter, server)) =
|
||||
self.language_server_for_buffer(buffer, server_id, cx)
|
||||
{
|
||||
(adapter.clone(), server.clone())
|
||||
} else {
|
||||
return Task::ready(Ok(None));
|
||||
};
|
||||
let can_resolve = lang_server
|
||||
.capabilities()
|
||||
.completion_provider
|
||||
.as_ref()
|
||||
.and_then(|options| options.resolve_provider)
|
||||
.unwrap_or(false);
|
||||
if !can_resolve {
|
||||
return Task::ready(Ok(None));
|
||||
}
|
||||
|
||||
let buffer_snapshot = buffer.snapshot();
|
||||
cx.spawn(|project, cx| async move {
|
||||
let resolve_task = lang_server.request::<lsp::request::InlayHintResolveRequest>(
|
||||
InlayHints::project_to_lsp_hint(hint, &project, &buffer_snapshot, &cx),
|
||||
);
|
||||
let resolved_hint = resolve_task
|
||||
.await
|
||||
.context("inlay hint resolve LSP request")?;
|
||||
let resolved_hint = cx.read(|cx| {
|
||||
InlayHints::lsp_to_project_hint(
|
||||
resolved_hint,
|
||||
&buffer_handle,
|
||||
ResolveState::Resolved,
|
||||
false,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
Ok(Some(resolved_hint))
|
||||
})
|
||||
} else if let Some(project_id) = self.remote_id() {
|
||||
let client = self.client.clone();
|
||||
let request = proto::ResolveInlayHint {
|
||||
project_id,
|
||||
buffer_id: buffer_handle.read(cx).remote_id(),
|
||||
language_server_id: server_id.0 as u64,
|
||||
hint: Some(InlayHints::project_to_proto_hint(hint, cx)),
|
||||
};
|
||||
cx.spawn(|project, mut cx| async move {
|
||||
let response = client
|
||||
.request(request)
|
||||
.await
|
||||
.context("inlay hints proto request")?;
|
||||
match response.hint {
|
||||
Some(resolved_hint) => {
|
||||
InlayHints::proto_to_project_hint(resolved_hint, &project, &mut cx)
|
||||
.await
|
||||
.map(Some)
|
||||
.context("inlay hints proto response conversion")
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Task::ready(Err(anyhow!("project does not have a remote id")))
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn search(
|
||||
&self,
|
||||
|
@ -6832,6 +6897,43 @@ impl Project {
|
|||
}))
|
||||
}
|
||||
|
||||
async fn handle_resolve_inlay_hint(
|
||||
this: ModelHandle<Self>,
|
||||
envelope: TypedEnvelope<proto::ResolveInlayHint>,
|
||||
_: Arc<Client>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<proto::ResolveInlayHintResponse> {
|
||||
let proto_hint = envelope
|
||||
.payload
|
||||
.hint
|
||||
.expect("incorrect protobuf resolve inlay hint message: missing the inlay hint");
|
||||
let hint = InlayHints::proto_to_project_hint(proto_hint, &this, &mut cx)
|
||||
.await
|
||||
.context("resolved proto inlay hint conversion")?;
|
||||
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 resolved_hint = this
|
||||
.update(&mut cx, |project, cx| {
|
||||
project.resolve_inlay_hint(
|
||||
hint,
|
||||
buffer,
|
||||
LanguageServerId(envelope.payload.language_server_id as usize),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.await
|
||||
.context("inlay hints fetch")?
|
||||
.map(|hint| cx.read(|cx| InlayHints::project_to_proto_hint(hint, cx)));
|
||||
|
||||
Ok(proto::ResolveInlayHintResponse {
|
||||
hint: resolved_hint,
|
||||
})
|
||||
}
|
||||
|
||||
async fn handle_refresh_inlay_hints(
|
||||
this: ModelHandle<Self>,
|
||||
_: TypedEnvelope<proto::RefreshInlayHints>,
|
||||
|
|
|
@ -128,6 +128,8 @@ message Envelope {
|
|||
|
||||
InlayHints inlay_hints = 116;
|
||||
InlayHintsResponse inlay_hints_response = 117;
|
||||
ResolveInlayHint resolve_inlay_hint = 131;
|
||||
ResolveInlayHintResponse resolve_inlay_hint_response = 132;
|
||||
RefreshInlayHints refresh_inlay_hints = 118;
|
||||
|
||||
CreateChannel create_channel = 119;
|
||||
|
@ -800,15 +802,27 @@ message ResolveState {
|
|||
|
||||
message LspResolveState {
|
||||
string value = 1;
|
||||
uint64 server_id = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message ResolveInlayHint {
|
||||
uint64 project_id = 1;
|
||||
uint64 buffer_id = 2;
|
||||
uint64 language_server_id = 3;
|
||||
InlayHint hint = 4;
|
||||
}
|
||||
|
||||
message ResolveInlayHintResponse {
|
||||
InlayHint hint = 1;
|
||||
}
|
||||
|
||||
message RefreshInlayHints {
|
||||
uint64 project_id = 1;
|
||||
}
|
||||
|
||||
message MarkupContent {
|
||||
string kind = 1;
|
||||
bool is_markdown = 1;
|
||||
string value = 2;
|
||||
}
|
||||
|
||||
|
|
|
@ -197,6 +197,8 @@ messages!(
|
|||
(OnTypeFormattingResponse, Background),
|
||||
(InlayHints, Background),
|
||||
(InlayHintsResponse, Background),
|
||||
(ResolveInlayHint, Background),
|
||||
(ResolveInlayHintResponse, Background),
|
||||
(RefreshInlayHints, Foreground),
|
||||
(Ping, Foreground),
|
||||
(PrepareRename, Background),
|
||||
|
@ -299,6 +301,7 @@ request_messages!(
|
|||
(PrepareRename, PrepareRenameResponse),
|
||||
(OnTypeFormatting, OnTypeFormattingResponse),
|
||||
(InlayHints, InlayHintsResponse),
|
||||
(ResolveInlayHint, ResolveInlayHintResponse),
|
||||
(RefreshInlayHints, Ack),
|
||||
(ReloadBuffers, ReloadBuffersResponse),
|
||||
(RequestContact, Ack),
|
||||
|
@ -355,6 +358,7 @@ entity_messages!(
|
|||
PerformRename,
|
||||
OnTypeFormatting,
|
||||
InlayHints,
|
||||
ResolveInlayHint,
|
||||
RefreshInlayHints,
|
||||
PrepareRename,
|
||||
ReloadBuffers,
|
||||
|
|
|
@ -6,4 +6,4 @@ pub use conn::Connection;
|
|||
pub use peer::*;
|
||||
mod macros;
|
||||
|
||||
pub const PROTOCOL_VERSION: u32 = 60;
|
||||
pub const PROTOCOL_VERSION: u32 = 61;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue