Store inlay hint resolve data
This commit is contained in:
parent
e4b78e322e
commit
3434990b70
4 changed files with 134 additions and 53 deletions
|
@ -1109,7 +1109,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{InlayId, MultiBuffer};
|
use crate::{InlayId, MultiBuffer};
|
||||||
use gpui::AppContext;
|
use gpui::AppContext;
|
||||||
use project::{InlayHint, InlayHintLabel};
|
use project::{InlayHint, InlayHintLabel, ResolveState};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
use std::{cmp::Reverse, env, sync::Arc};
|
use std::{cmp::Reverse, env, sync::Arc};
|
||||||
|
@ -1131,6 +1131,7 @@ mod tests {
|
||||||
padding_right: false,
|
padding_right: false,
|
||||||
tooltip: None,
|
tooltip: None,
|
||||||
kind: None,
|
kind: None,
|
||||||
|
resolve_state: ResolveState::Resolved,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.text
|
.text
|
||||||
|
@ -1151,6 +1152,7 @@ mod tests {
|
||||||
padding_right: true,
|
padding_right: true,
|
||||||
tooltip: None,
|
tooltip: None,
|
||||||
kind: None,
|
kind: None,
|
||||||
|
resolve_state: ResolveState::Resolved,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.text
|
.text
|
||||||
|
@ -1171,6 +1173,7 @@ mod tests {
|
||||||
padding_right: false,
|
padding_right: false,
|
||||||
tooltip: None,
|
tooltip: None,
|
||||||
kind: None,
|
kind: None,
|
||||||
|
resolve_state: ResolveState::Resolved,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.text
|
.text
|
||||||
|
@ -1191,6 +1194,7 @@ mod tests {
|
||||||
padding_right: true,
|
padding_right: true,
|
||||||
tooltip: None,
|
tooltip: None,
|
||||||
kind: None,
|
kind: None,
|
||||||
|
resolve_state: ResolveState::Resolved,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.text
|
.text
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
DocumentHighlight, Hover, HoverBlock, HoverBlockKind, InlayHint, InlayHintLabel,
|
DocumentHighlight, Hover, HoverBlock, HoverBlockKind, InlayHint, InlayHintLabel,
|
||||||
InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Location, LocationLink,
|
InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Location, LocationLink,
|
||||||
MarkupContent, Project, ProjectTransaction,
|
MarkupContent, Project, ProjectTransaction, ResolveState,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
@ -1817,7 +1817,8 @@ impl LspCommand for InlayHints {
|
||||||
server_id: LanguageServerId,
|
server_id: LanguageServerId,
|
||||||
mut cx: AsyncAppContext,
|
mut cx: AsyncAppContext,
|
||||||
) -> Result<Vec<InlayHint>> {
|
) -> Result<Vec<InlayHint>> {
|
||||||
let (lsp_adapter, _) = language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
|
let (lsp_adapter, lsp_server) =
|
||||||
|
language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
|
||||||
// `typescript-language-server` adds padding to the left for type hints, turning
|
// `typescript-language-server` adds padding to the left for type hints, turning
|
||||||
// `const foo: boolean` into `const foo : boolean` which looks odd.
|
// `const foo: boolean` into `const foo : boolean` which looks odd.
|
||||||
// `rust-analyzer` does not have the padding for this case, and we have to accomodate both.
|
// `rust-analyzer` does not have the padding for this case, and we have to accomodate both.
|
||||||
|
@ -1833,6 +1834,15 @@ impl LspCommand for InlayHints {
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|lsp_hint| {
|
.map(|lsp_hint| {
|
||||||
|
let resolve_state = match lsp_server.capabilities().inlay_hint_provider {
|
||||||
|
Some(lsp::OneOf::Right(lsp::InlayHintServerCapabilities::Options(
|
||||||
|
lsp::InlayHintOptions {
|
||||||
|
resolve_provider: Some(true),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
))) => ResolveState::CanResolve(lsp_hint.data),
|
||||||
|
_ => ResolveState::Resolved,
|
||||||
|
};
|
||||||
let kind = lsp_hint.kind.and_then(|kind| match kind {
|
let kind = lsp_hint.kind.and_then(|kind| match kind {
|
||||||
lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
|
lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
|
||||||
lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
|
lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
|
||||||
|
@ -1910,6 +1920,7 @@ impl LspCommand for InlayHints {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
resolve_state,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect())
|
.collect())
|
||||||
|
@ -1959,57 +1970,69 @@ impl LspCommand for InlayHints {
|
||||||
proto::InlayHintsResponse {
|
proto::InlayHintsResponse {
|
||||||
hints: response
|
hints: response
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|response_hint| proto::InlayHint {
|
.map(|response_hint| {
|
||||||
position: Some(language::proto::serialize_anchor(&response_hint.position)),
|
let (state, lsp_resolve_state) = match response_hint.resolve_state {
|
||||||
padding_left: response_hint.padding_left,
|
ResolveState::CanResolve(resolve_data) => {
|
||||||
padding_right: response_hint.padding_right,
|
(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 }))
|
||||||
label: Some(proto::InlayHintLabel {
|
}
|
||||||
label: Some(match response_hint.label {
|
ResolveState::Resolved => (1, None),
|
||||||
InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
|
ResolveState::Resolving => (2, None),
|
||||||
InlayHintLabel::LabelParts(label_parts) => {
|
};
|
||||||
proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
|
let resolve_state = Some(proto::ResolveState {
|
||||||
parts: label_parts.into_iter().map(|label_part| proto::InlayHintLabelPart {
|
state, lsp_resolve_state
|
||||||
value: label_part.value,
|
});
|
||||||
tooltip: label_part.tooltip.map(|tooltip| {
|
proto::InlayHint {
|
||||||
let proto_tooltip = match tooltip {
|
position: Some(language::proto::serialize_anchor(&response_hint.position)),
|
||||||
InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s),
|
padding_left: response_hint.padding_left,
|
||||||
InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent {
|
padding_right: response_hint.padding_right,
|
||||||
kind: markup_content.kind,
|
label: Some(proto::InlayHintLabel {
|
||||||
value: markup_content.value,
|
label: Some(match response_hint.label {
|
||||||
}),
|
InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
|
||||||
};
|
InlayHintLabel::LabelParts(label_parts) => {
|
||||||
proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)}
|
proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
|
||||||
}),
|
parts: label_parts.into_iter().map(|label_part| proto::InlayHintLabelPart {
|
||||||
location: label_part.location.map(|location| proto::Location {
|
value: label_part.value,
|
||||||
start: Some(serialize_anchor(&location.range.start)),
|
tooltip: label_part.tooltip.map(|tooltip| {
|
||||||
end: Some(serialize_anchor(&location.range.end)),
|
let proto_tooltip = match tooltip {
|
||||||
buffer_id: location.buffer.read(cx).remote_id(),
|
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 {
|
||||||
}).collect()
|
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,
|
||||||
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),
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.collect(),
|
.collect(),
|
||||||
version: serialize_version(buffer_version),
|
version: serialize_version(buffer_version),
|
||||||
}
|
}
|
||||||
|
@ -2021,7 +2044,7 @@ impl LspCommand for InlayHints {
|
||||||
project: ModelHandle<Project>,
|
project: ModelHandle<Project>,
|
||||||
buffer: ModelHandle<Buffer>,
|
buffer: ModelHandle<Buffer>,
|
||||||
mut cx: AsyncAppContext,
|
mut cx: AsyncAppContext,
|
||||||
) -> Result<Vec<InlayHint>> {
|
) -> anyhow::Result<Vec<InlayHint>> {
|
||||||
buffer
|
buffer
|
||||||
.update(&mut cx, |buffer, _| {
|
.update(&mut cx, |buffer, _| {
|
||||||
buffer.wait_for_version(deserialize_version(&message.version))
|
buffer.wait_for_version(deserialize_version(&message.version))
|
||||||
|
@ -2035,6 +2058,27 @@ impl LspCommand for InlayHints {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|location| location.buffer_id)
|
.and_then(|location| location.buffer_id)
|
||||||
.context("missing 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 {
|
let hint = InlayHint {
|
||||||
buffer_id,
|
buffer_id,
|
||||||
position: message_hint
|
position: message_hint
|
||||||
|
@ -2103,6 +2147,7 @@ impl LspCommand for InlayHints {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
resolve_state,
|
||||||
};
|
};
|
||||||
|
|
||||||
hints.push(hint);
|
hints.push(hint);
|
||||||
|
|
|
@ -342,6 +342,22 @@ pub struct InlayHint {
|
||||||
pub padding_left: bool,
|
pub padding_left: bool,
|
||||||
pub padding_right: bool,
|
pub padding_right: bool,
|
||||||
pub tooltip: Option<InlayHintTooltip>,
|
pub tooltip: Option<InlayHintTooltip>,
|
||||||
|
pub resolve_state: ResolveState,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum ResolveState {
|
||||||
|
Resolved,
|
||||||
|
CanResolve(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 {
|
impl InlayHint {
|
||||||
|
|
|
@ -754,6 +754,7 @@ message InlayHint {
|
||||||
bool padding_left = 4;
|
bool padding_left = 4;
|
||||||
bool padding_right = 5;
|
bool padding_right = 5;
|
||||||
InlayHintTooltip tooltip = 6;
|
InlayHintTooltip tooltip = 6;
|
||||||
|
ResolveState resolve_state = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message InlayHintLabel {
|
message InlayHintLabel {
|
||||||
|
@ -787,6 +788,21 @@ message InlayHintLabelPartTooltip {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message ResolveState {
|
||||||
|
State state = 1;
|
||||||
|
LspResolveState lsp_resolve_state = 2;
|
||||||
|
|
||||||
|
enum State {
|
||||||
|
Resolved = 0;
|
||||||
|
CanResolve = 1;
|
||||||
|
Resolving = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LspResolveState {
|
||||||
|
string value = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
message RefreshInlayHints {
|
message RefreshInlayHints {
|
||||||
uint64 project_id = 1;
|
uint64 project_id = 1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue