@@ -2,8 +2,10 @@ use std::{ops::Range, sync::Arc};
use gpui::{App, AppContext, Entity, FontWeight, HighlightStyle, SharedString};
use language::LanguageRegistry;
+use lsp::LanguageServerId;
use markdown::Markdown;
use rpc::proto::{self, documentation};
+use util::maybe;
#[derive(Debug)]
pub struct SignatureHelp {
@@ -31,6 +33,7 @@ impl SignatureHelp {
pub fn new(
help: lsp::SignatureHelp,
language_registry: Option<Arc<LanguageRegistry>>,
+ lang_server_id: Option<LanguageServerId>,
cx: &mut App,
) -> Option<Self> {
if help.signatures.is_empty() {
@@ -39,6 +42,7 @@ impl SignatureHelp {
let active_signature = help.active_signature.unwrap_or(0) as usize;
let mut signatures = Vec::<SignatureHelpData>::with_capacity(help.signatures.capacity());
for signature in &help.signatures {
+ let label = SharedString::from(signature.label.clone());
let active_parameter = signature
.active_parameter
.unwrap_or_else(|| help.active_parameter.unwrap_or(0))
@@ -49,39 +53,53 @@ impl SignatureHelp {
if let Some(parameters) = &signature.parameters {
for (index, parameter) in parameters.iter().enumerate() {
let label_range = match ¶meter.label {
- lsp::ParameterLabel::LabelOffsets(parameter_label_offsets) => {
- let range = *parameter_label_offsets.get(0)? as usize
- ..*parameter_label_offsets.get(1)? as usize;
- if index == active_parameter {
- highlights.push((
- range.clone(),
- HighlightStyle {
- font_weight: Some(FontWeight::EXTRA_BOLD),
- ..HighlightStyle::default()
- },
- ));
- }
- Some(range)
+ &lsp::ParameterLabel::LabelOffsets([offset1, offset2]) => {
+ maybe!({
+ let offset1 = offset1 as usize;
+ let offset2 = offset2 as usize;
+ if offset1 < offset2 {
+ let mut indices = label.char_indices().scan(
+ 0,
+ |utf16_offset_acc, (offset, c)| {
+ let utf16_offset = *utf16_offset_acc;
+ *utf16_offset_acc += c.len_utf16();
+ Some((utf16_offset, offset))
+ },
+ );
+ let (_, offset1) = indices
+ .find(|(utf16_offset, _)| *utf16_offset == offset1)?;
+ let (_, offset2) = indices
+ .find(|(utf16_offset, _)| *utf16_offset == offset2)?;
+ Some(offset1..offset2)
+ } else {
+ log::warn!(
+ "language server {lang_server_id:?} produced invalid parameter label range: {offset1:?}..{offset2:?}",
+ );
+ None
+ }
+ })
}
lsp::ParameterLabel::Simple(parameter_label) => {
if let Some(start) = signature.label.find(parameter_label) {
- let range = start..start + parameter_label.len();
- if index == active_parameter {
- highlights.push((
- range.clone(),
- HighlightStyle {
- font_weight: Some(FontWeight::EXTRA_BOLD),
- ..HighlightStyle::default()
- },
- ));
- }
- Some(range)
+ Some(start..start + parameter_label.len())
} else {
None
}
}
};
+ if let Some(label_range) = &label_range
+ && index == active_parameter
+ {
+ highlights.push((
+ label_range.clone(),
+ HighlightStyle {
+ font_weight: Some(FontWeight::EXTRA_BOLD),
+ ..HighlightStyle::default()
+ },
+ ));
+ }
+
let documentation = parameter
.documentation
.as_ref()
@@ -94,7 +112,6 @@ impl SignatureHelp {
}
}
- let label = SharedString::from(signature.label.clone());
let documentation = signature
.documentation
.as_ref()
@@ -290,7 +307,7 @@ mod tests {
active_signature: Some(0),
active_parameter: Some(0),
};
- let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, cx));
+ let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
assert!(maybe_markdown.is_some());
let markdown = maybe_markdown.unwrap();
@@ -336,7 +353,7 @@ mod tests {
active_signature: Some(0),
active_parameter: Some(1),
};
- let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, cx));
+ let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
assert!(maybe_markdown.is_some());
let markdown = maybe_markdown.unwrap();
@@ -396,7 +413,7 @@ mod tests {
active_signature: Some(0),
active_parameter: Some(0),
};
- let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, cx));
+ let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
assert!(maybe_markdown.is_some());
let markdown = maybe_markdown.unwrap();
@@ -449,7 +466,7 @@ mod tests {
active_signature: Some(1),
active_parameter: Some(0),
};
- let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, cx));
+ let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
assert!(maybe_markdown.is_some());
let markdown = maybe_markdown.unwrap();
@@ -502,7 +519,7 @@ mod tests {
active_signature: Some(1),
active_parameter: Some(1),
};
- let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, cx));
+ let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
assert!(maybe_markdown.is_some());
let markdown = maybe_markdown.unwrap();
@@ -555,7 +572,7 @@ mod tests {
active_signature: Some(1),
active_parameter: None,
};
- let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, cx));
+ let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
assert!(maybe_markdown.is_some());
let markdown = maybe_markdown.unwrap();
@@ -623,7 +640,7 @@ mod tests {
active_signature: Some(2),
active_parameter: Some(1),
};
- let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, cx));
+ let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
assert!(maybe_markdown.is_some());
let markdown = maybe_markdown.unwrap();
@@ -645,7 +662,7 @@ mod tests {
active_signature: None,
active_parameter: None,
};
- let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, cx));
+ let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
assert!(maybe_markdown.is_none());
}
@@ -670,7 +687,7 @@ mod tests {
active_signature: Some(0),
active_parameter: Some(0),
};
- let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, cx));
+ let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
assert!(maybe_markdown.is_some());
let markdown = maybe_markdown.unwrap();
@@ -708,7 +725,8 @@ mod tests {
active_signature: Some(0),
active_parameter: Some(0),
};
- let maybe_signature_help = cx.update(|cx| SignatureHelp::new(signature_help, None, cx));
+ let maybe_signature_help =
+ cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
assert!(maybe_signature_help.is_some());
let signature_help = maybe_signature_help.unwrap();
@@ -736,4 +754,40 @@ mod tests {
// Check that the active parameter is correct
assert_eq!(signature.active_parameter, Some(0));
}
+
+ #[gpui::test]
+ fn test_create_signature_help_implements_utf16_spec(cx: &mut TestAppContext) {
+ let signature_help = lsp::SignatureHelp {
+ signatures: vec![lsp::SignatureInformation {
+ label: "fn test(🦀: u8, 🦀: &str)".to_string(),
+ documentation: None,
+ parameters: Some(vec![
+ lsp::ParameterInformation {
+ label: lsp::ParameterLabel::LabelOffsets([8, 10]),
+ documentation: None,
+ },
+ lsp::ParameterInformation {
+ label: lsp::ParameterLabel::LabelOffsets([16, 18]),
+ documentation: None,
+ },
+ ]),
+ active_parameter: None,
+ }],
+ active_signature: Some(0),
+ active_parameter: Some(0),
+ };
+ let signature_help = cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
+ assert!(signature_help.is_some());
+
+ let markdown = signature_help.unwrap();
+ let signature = markdown.signatures[markdown.active_signature].clone();
+ let markdown = (signature.label, signature.highlights);
+ assert_eq!(
+ markdown,
+ (
+ SharedString::new("fn test(🦀: u8, 🦀: &str)"),
+ vec![(8..12, current_parameter())]
+ )
+ );
+ }
}