Merge branch 'main' into multi-server-completions-tailwind
This commit is contained in:
commit
ff3865a4ad
427 changed files with 43123 additions and 12861 deletions
|
@ -1,25 +1,27 @@
|
|||
use crate::{
|
||||
DocumentHighlight, Hover, HoverBlock, HoverBlockKind, InlayHint, InlayHintLabel,
|
||||
InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Location, LocationLink,
|
||||
MarkupContent, Project, ProjectTransaction,
|
||||
MarkupContent, Project, ProjectTransaction, ResolveState,
|
||||
};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use client::proto::{self, PeerId};
|
||||
use fs::LineEnding;
|
||||
use futures::future;
|
||||
use gpui::{AppContext, AsyncAppContext, ModelHandle};
|
||||
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::{
|
||||
CompletionListItemDefaultsEditRange, DocumentHighlightKind, LanguageServer, LanguageServerId,
|
||||
ServerCapabilities,
|
||||
OneOf, ServerCapabilities,
|
||||
};
|
||||
use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc};
|
||||
use text::LineEnding;
|
||||
|
||||
pub fn lsp_formatting_options(tab_size: u32) -> lsp::FormattingOptions {
|
||||
lsp::FormattingOptions {
|
||||
|
@ -1467,7 +1469,7 @@ impl LspCommand for GetCompletions {
|
|||
})
|
||||
});
|
||||
|
||||
Ok(futures::future::join_all(completions).await)
|
||||
Ok(future::join_all(completions).await)
|
||||
}
|
||||
|
||||
fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCompletions {
|
||||
|
@ -1535,7 +1537,7 @@ impl LspCommand for GetCompletions {
|
|||
let completions = message.completions.into_iter().map(|completion| {
|
||||
language::proto::deserialize_completion(completion, language.clone())
|
||||
});
|
||||
futures::future::try_join_all(completions).await
|
||||
future::try_join_all(completions).await
|
||||
}
|
||||
|
||||
fn buffer_id_from_proto(message: &proto::GetCompletions) -> u64 {
|
||||
|
@ -1689,7 +1691,11 @@ impl LspCommand for OnTypeFormatting {
|
|||
type ProtoRequest = proto::OnTypeFormatting;
|
||||
|
||||
fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
|
||||
let Some(on_type_formatting_options) = &server_capabilities.document_on_type_formatting_provider else { return false };
|
||||
let Some(on_type_formatting_options) =
|
||||
&server_capabilities.document_on_type_formatting_provider
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
on_type_formatting_options
|
||||
.first_trigger_character
|
||||
.contains(&self.trigger)
|
||||
|
@ -1803,7 +1809,9 @@ impl LspCommand for OnTypeFormatting {
|
|||
_: ModelHandle<Buffer>,
|
||||
_: AsyncAppContext,
|
||||
) -> Result<Option<Transaction>> {
|
||||
let Some(transaction) = message.transaction else { return Ok(None) };
|
||||
let Some(transaction) = message.transaction else {
|
||||
return Ok(None);
|
||||
};
|
||||
Ok(Some(language::proto::deserialize_transaction(transaction)?))
|
||||
}
|
||||
|
||||
|
@ -1812,6 +1820,377 @@ impl LspCommand for OnTypeFormatting {
|
|||
}
|
||||
}
|
||||
|
||||
impl InlayHints {
|
||||
pub async fn lsp_to_project_hint(
|
||||
lsp_hint: lsp::InlayHint,
|
||||
buffer_handle: &ModelHandle<Buffer>,
|
||||
server_id: LanguageServerId,
|
||||
resolve_state: ResolveState,
|
||||
force_no_type_left_padding: bool,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> anyhow::Result<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 position = cx.update(|cx| {
|
||||
let buffer = buffer_handle.read(cx);
|
||||
let position = buffer.clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
|
||||
if kind == Some(InlayHintKind::Parameter) {
|
||||
buffer.anchor_before(position)
|
||||
} else {
|
||||
buffer.anchor_after(position)
|
||||
}
|
||||
});
|
||||
let label = Self::lsp_inlay_label_to_project(lsp_hint.label, server_id)
|
||||
.await
|
||||
.context("lsp to project inlay hint conversion")?;
|
||||
let padding_left = if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
|
||||
false
|
||||
} else {
|
||||
lsp_hint.padding_left.unwrap_or(false)
|
||||
};
|
||||
|
||||
Ok(InlayHint {
|
||||
position,
|
||||
padding_left,
|
||||
padding_right: lsp_hint.padding_right.unwrap_or(false),
|
||||
label,
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
async fn lsp_inlay_label_to_project(
|
||||
lsp_label: lsp::InlayHintLabel,
|
||||
server_id: LanguageServerId,
|
||||
) -> anyhow::Result<InlayHintLabel> {
|
||||
let label = match lsp_label {
|
||||
lsp::InlayHintLabel::String(s) => InlayHintLabel::String(s),
|
||||
lsp::InlayHintLabel::LabelParts(lsp_parts) => {
|
||||
let mut parts = Vec::with_capacity(lsp_parts.len());
|
||||
for lsp_part in lsp_parts {
|
||||
parts.push(InlayHintLabelPart {
|
||||
value: lsp_part.value,
|
||||
tooltip: lsp_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: Some(server_id).zip(lsp_part.location),
|
||||
});
|
||||
}
|
||||
InlayHintLabel::LabelParts(parts)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(label)
|
||||
}
|
||||
|
||||
pub fn project_to_proto_hint(response_hint: InlayHint) -> proto::InlayHint {
|
||||
let (state, lsp_resolve_state) = match response_hint.resolve_state {
|
||||
ResolveState::Resolved => (0, None),
|
||||
ResolveState::CanResolve(server_id, resolve_data) => (
|
||||
1,
|
||||
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::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| {
|
||||
let location_url = label_part.location.as_ref().map(|(_, location)| location.uri.to_string());
|
||||
let location_range_start = label_part.location.as_ref().map(|(_, location)| point_from_lsp(location.range.start).0).map(|point| proto::PointUtf16 { row: point.row, column: point.column });
|
||||
let location_range_end = label_part.location.as_ref().map(|(_, location)| point_from_lsp(location.range.end).0).map(|point| proto::PointUtf16 { row: point.row, column: point.column });
|
||||
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_url,
|
||||
location_range_start,
|
||||
location_range_end,
|
||||
language_server_id: label_part.location.as_ref().map(|(server_id, _)| server_id.0 as u64),
|
||||
}}).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 fn proto_to_project_hint(message_hint: proto::InlayHint) -> anyhow::Result<InlayHint> {
|
||||
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 {
|
||||
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_url
|
||||
.zip(
|
||||
part.location_range_start.and_then(|start| {
|
||||
Some(start..part.location_range_end?)
|
||||
}),
|
||||
)
|
||||
.zip(part.language_server_id)
|
||||
{
|
||||
Some(((uri, range), server_id)) => Some((
|
||||
LanguageServerId(server_id as usize),
|
||||
lsp::Location {
|
||||
uri: lsp::Url::parse(&uri)
|
||||
.context("invalid uri in hint part {part:?}")?,
|
||||
range: lsp::Range::new(
|
||||
point_to_lsp(PointUtf16::new(
|
||||
range.start.row,
|
||||
range.start.column,
|
||||
)),
|
||||
point_to_lsp(PointUtf16::new(
|
||||
range.end.row,
|
||||
range.end.column,
|
||||
)),
|
||||
),
|
||||
},
|
||||
)),
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn project_to_lsp_hint(hint: InlayHint, snapshot: &BufferSnapshot) -> 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.map(|(_, location)| location),
|
||||
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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_resolve_inlays(capabilities: &ServerCapabilities) -> bool {
|
||||
capabilities
|
||||
.inlay_hint_provider
|
||||
.as_ref()
|
||||
.and_then(|options| match options {
|
||||
OneOf::Left(_is_supported) => None,
|
||||
OneOf::Right(capabilities) => match capabilities {
|
||||
lsp::InlayHintServerCapabilities::Options(o) => o.resolve_provider,
|
||||
lsp::InlayHintServerCapabilities::RegistrationOptions(o) => {
|
||||
o.inlay_hint_options.resolve_provider
|
||||
}
|
||||
},
|
||||
})
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspCommand for InlayHints {
|
||||
type Response = Vec<InlayHint>;
|
||||
|
@ -1819,7 +2198,9 @@ impl LspCommand for InlayHints {
|
|||
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 };
|
||||
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 {
|
||||
|
@ -1852,8 +2233,9 @@ impl LspCommand for InlayHints {
|
|||
buffer: ModelHandle<Buffer>,
|
||||
server_id: LanguageServerId,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<Vec<InlayHint>> {
|
||||
let (lsp_adapter, _) = language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
|
||||
) -> anyhow::Result<Vec<InlayHint>> {
|
||||
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
|
||||
// `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.
|
||||
|
@ -1863,93 +2245,32 @@ impl LspCommand for InlayHints {
|
|||
// Hence let's use a heuristic first to handle the most awkward case and look for more.
|
||||
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()
|
||||
.map(|lsp_hint| {
|
||||
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,
|
||||
})
|
||||
}
|
||||
}),
|
||||
}
|
||||
})
|
||||
.collect())
|
||||
})
|
||||
|
||||
let hints = message.unwrap_or_default().into_iter().map(|lsp_hint| {
|
||||
let resolve_state = if InlayHints::can_resolve_inlays(lsp_server.capabilities()) {
|
||||
ResolveState::CanResolve(lsp_server.server_id(), lsp_hint.data.clone())
|
||||
} else {
|
||||
ResolveState::Resolved
|
||||
};
|
||||
|
||||
let buffer = buffer.clone();
|
||||
cx.spawn(|mut cx| async move {
|
||||
InlayHints::lsp_to_project_hint(
|
||||
lsp_hint,
|
||||
&buffer,
|
||||
server_id,
|
||||
resolve_state,
|
||||
force_no_type_left_padding,
|
||||
&mut cx,
|
||||
)
|
||||
.await
|
||||
})
|
||||
});
|
||||
future::join_all(hints)
|
||||
.await
|
||||
.into_iter()
|
||||
.collect::<anyhow::Result<_>>()
|
||||
.context("lsp to project inlay hints conversion")
|
||||
}
|
||||
|
||||
fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints {
|
||||
|
@ -1995,23 +2316,7 @@ impl LspCommand for InlayHints {
|
|||
proto::InlayHintsResponse {
|
||||
hints: response
|
||||
.into_iter()
|
||||
.map(|response_hint| proto::InlayHint {
|
||||
position: Some(language::proto::serialize_anchor(&response_hint.position)),
|
||||
padding_left: response_hint.padding_left,
|
||||
padding_right: response_hint.padding_right,
|
||||
kind: response_hint.kind.map(|kind| kind.name().to_string()),
|
||||
// Do not pass extra data such as tooltips to clients: host can put tooltip data from the cache during resolution.
|
||||
tooltip: None,
|
||||
// Similarly, do not pass label parts to clients: host can return a detailed list during resolution.
|
||||
label: Some(proto::InlayHintLabel {
|
||||
label: Some(proto::inlay_hint_label::Label::Value(
|
||||
match response_hint.label {
|
||||
InlayHintLabel::String(s) => s,
|
||||
InlayHintLabel::LabelParts(_) => response_hint.text(),
|
||||
},
|
||||
)),
|
||||
}),
|
||||
})
|
||||
.map(|response_hint| InlayHints::project_to_proto_hint(response_hint))
|
||||
.collect(),
|
||||
version: serialize_version(buffer_version),
|
||||
}
|
||||
|
@ -2020,10 +2325,10 @@ impl LspCommand for InlayHints {
|
|||
async fn response_from_proto(
|
||||
self,
|
||||
message: proto::InlayHintsResponse,
|
||||
project: ModelHandle<Project>,
|
||||
_: ModelHandle<Project>,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<Vec<InlayHint>> {
|
||||
) -> anyhow::Result<Vec<InlayHint>> {
|
||||
buffer
|
||||
.update(&mut cx, |buffer, _| {
|
||||
buffer.wait_for_version(deserialize_version(&message.version))
|
||||
|
@ -2032,82 +2337,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 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,
|
||||
})
|
||||
}
|
||||
})
|
||||
}),
|
||||
};
|
||||
|
||||
hints.push(hint);
|
||||
hints.push(InlayHints::proto_to_project_hint(message_hint)?);
|
||||
}
|
||||
|
||||
Ok(hints)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue