Tidy up collab-related signature help data (#14377)

Follow-up of https://github.com/zed-industries/zed/pull/12909

* Fully preserve LSP data when sending it via collab, and only strip it
on the client.
* Avoid extra custom request handlers, and extend multi LSP server query
protocol instead.


Release Notes:

- N/A
This commit is contained in:
Kirill Bulatov 2024-07-13 04:06:01 +03:00 committed by GitHub
parent dd63e25f23
commit 9ce989a704
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 269 additions and 167 deletions

View file

@ -5,6 +5,7 @@ use language::{
markdown::{MarkdownHighlight, MarkdownHighlightStyle},
Language,
};
use rpc::proto::{self, documentation};
pub const SIGNATURE_HELP_HIGHLIGHT_CURRENT: MarkdownHighlight =
MarkdownHighlight::Style(MarkdownHighlightStyle {
@ -26,38 +27,31 @@ pub const SIGNATURE_HELP_HIGHLIGHT_OVERLOAD: MarkdownHighlight =
pub struct SignatureHelp {
pub markdown: String,
pub highlights: Vec<(Range<usize>, MarkdownHighlight)>,
pub(super) original_data: lsp::SignatureHelp,
}
impl SignatureHelp {
pub fn new(
lsp::SignatureHelp {
signatures,
active_signature,
active_parameter,
..
}: lsp::SignatureHelp,
language: Option<Arc<Language>>,
) -> Option<Self> {
let function_options_count = signatures.len();
pub fn new(help: lsp::SignatureHelp, language: Option<Arc<Language>>) -> Option<Self> {
let function_options_count = help.signatures.len();
let signature_information = active_signature
.and_then(|active_signature| signatures.get(active_signature as usize))
.or_else(|| signatures.first())?;
let signature_information = help
.active_signature
.and_then(|active_signature| help.signatures.get(active_signature as usize))
.or_else(|| help.signatures.first())?;
let str_for_join = ", ";
let parameter_length = signature_information
.parameters
.as_ref()
.map(|parameters| parameters.len())
.unwrap_or(0);
.map_or(0, |parameters| parameters.len());
let mut highlight_start = 0;
let (markdown, mut highlights): (Vec<_>, Vec<_>) = signature_information
.parameters
.as_ref()?
.iter()
.enumerate()
.filter_map(|(i, parameter_information)| {
let string = match parameter_information.label.clone() {
.map(|(i, parameter_information)| {
let label = match parameter_information.label.clone() {
lsp::ParameterLabel::Simple(string) => string,
lsp::ParameterLabel::LabelOffsets(offset) => signature_information
.label
@ -66,33 +60,28 @@ impl SignatureHelp {
.take((offset[1] - offset[0]) as usize)
.collect::<String>(),
};
let string_length = string.len();
let label_length = label.len();
let result = if let Some(active_parameter) = active_parameter {
let highlights = help.active_parameter.and_then(|active_parameter| {
if i == active_parameter as usize {
Some((
string,
Some((
highlight_start..(highlight_start + string_length),
SIGNATURE_HELP_HIGHLIGHT_CURRENT,
)),
highlight_start..(highlight_start + label_length),
SIGNATURE_HELP_HIGHLIGHT_CURRENT,
))
} else {
Some((string, None))
None
}
} else {
Some((string, None))
};
});
if i != parameter_length {
highlight_start += string_length + str_for_join.len();
highlight_start += label_length + str_for_join.len();
}
result
(label, highlights)
})
.unzip();
let result = if markdown.is_empty() {
if markdown.is_empty() {
None
} else {
let markdown = markdown.join(str_for_join);
@ -112,16 +101,130 @@ impl SignatureHelp {
format!("```{language_name}\n{markdown}")
};
Some((markdown, highlights.into_iter().flatten().collect()))
};
result.map(|(markdown, highlights)| Self {
markdown,
highlights,
})
Some(Self {
markdown,
highlights: highlights.into_iter().flatten().collect(),
original_data: help,
})
}
}
}
pub fn lsp_to_proto_signature(lsp_help: lsp::SignatureHelp) -> proto::SignatureHelp {
proto::SignatureHelp {
signatures: lsp_help
.signatures
.into_iter()
.map(|signature| proto::SignatureInformation {
label: signature.label,
documentation: signature
.documentation
.map(|documentation| lsp_to_proto_documentation(documentation)),
parameters: signature
.parameters
.unwrap_or_default()
.into_iter()
.map(|parameter_info| proto::ParameterInformation {
label: Some(match parameter_info.label {
lsp::ParameterLabel::Simple(label) => {
proto::parameter_information::Label::Simple(label)
}
lsp::ParameterLabel::LabelOffsets(offsets) => {
proto::parameter_information::Label::LabelOffsets(
proto::LabelOffsets {
start: offsets[0],
end: offsets[1],
},
)
}
}),
documentation: parameter_info.documentation.map(lsp_to_proto_documentation),
})
.collect(),
active_parameter: signature.active_parameter,
})
.collect(),
active_signature: lsp_help.active_signature,
active_parameter: lsp_help.active_parameter,
}
}
fn lsp_to_proto_documentation(documentation: lsp::Documentation) -> proto::Documentation {
proto::Documentation {
content: Some(match documentation {
lsp::Documentation::String(string) => proto::documentation::Content::Value(string),
lsp::Documentation::MarkupContent(content) => {
proto::documentation::Content::MarkupContent(proto::MarkupContent {
is_markdown: matches!(content.kind, lsp::MarkupKind::Markdown),
value: content.value,
})
}
}),
}
}
pub fn proto_to_lsp_signature(proto_help: proto::SignatureHelp) -> lsp::SignatureHelp {
lsp::SignatureHelp {
signatures: proto_help
.signatures
.into_iter()
.map(|signature| lsp::SignatureInformation {
label: signature.label,
documentation: signature.documentation.and_then(proto_to_lsp_documentation),
parameters: Some(
signature
.parameters
.into_iter()
.filter_map(|parameter_info| {
Some(lsp::ParameterInformation {
label: match parameter_info.label? {
proto::parameter_information::Label::Simple(string) => {
lsp::ParameterLabel::Simple(string)
}
proto::parameter_information::Label::LabelOffsets(offsets) => {
lsp::ParameterLabel::LabelOffsets([
offsets.start,
offsets.end,
])
}
},
documentation: parameter_info
.documentation
.and_then(proto_to_lsp_documentation),
})
})
.collect(),
),
active_parameter: signature.active_parameter,
})
.collect(),
active_signature: proto_help.active_signature,
active_parameter: proto_help.active_parameter,
}
}
fn proto_to_lsp_documentation(documentation: proto::Documentation) -> Option<lsp::Documentation> {
let documentation = {
Some(match documentation.content? {
documentation::Content::Value(string) => lsp::Documentation::String(string),
documentation::Content::MarkupContent(markup) => {
lsp::Documentation::MarkupContent(if markup.is_markdown {
lsp::MarkupContent {
kind: lsp::MarkupKind::Markdown,
value: markup.value,
}
} else {
lsp::MarkupContent {
kind: lsp::MarkupKind::PlainText,
value: markup.value,
}
})
}
})
};
documentation
}
#[cfg(test)]
mod tests {
use crate::lsp_command::signature_help::{