1use std::{ops::Range, sync::Arc};
2
3use gpui::{App, AppContext, Entity, FontWeight, HighlightStyle, SharedString};
4use language::LanguageRegistry;
5use lsp::LanguageServerId;
6use markdown::Markdown;
7use rpc::proto::{self, documentation};
8use util::maybe;
9
10#[derive(Debug)]
11pub struct SignatureHelp {
12 pub active_signature: usize,
13 pub signatures: Vec<SignatureHelpData>,
14 pub(super) original_data: lsp::SignatureHelp,
15}
16
17#[derive(Debug, Clone)]
18pub struct SignatureHelpData {
19 pub label: SharedString,
20 pub documentation: Option<Entity<Markdown>>,
21 pub highlights: Vec<(Range<usize>, HighlightStyle)>,
22 pub active_parameter: Option<usize>,
23 pub parameters: Vec<ParameterInfo>,
24}
25
26#[derive(Debug, Clone)]
27pub struct ParameterInfo {
28 pub label_range: Option<Range<usize>>,
29 pub documentation: Option<Entity<Markdown>>,
30}
31
32impl SignatureHelp {
33 pub fn new(
34 help: lsp::SignatureHelp,
35 language_registry: Option<Arc<LanguageRegistry>>,
36 lang_server_id: Option<LanguageServerId>,
37 cx: &mut App,
38 ) -> Option<Self> {
39 if help.signatures.is_empty() {
40 return None;
41 }
42 let active_signature = help.active_signature.unwrap_or(0) as usize;
43 let mut signatures = Vec::<SignatureHelpData>::with_capacity(help.signatures.capacity());
44 for signature in &help.signatures {
45 let label = SharedString::from(signature.label.clone());
46 let active_parameter = signature
47 .active_parameter
48 .unwrap_or_else(|| help.active_parameter.unwrap_or(0))
49 as usize;
50 let mut highlights = Vec::new();
51 let mut parameter_infos = Vec::new();
52
53 if let Some(parameters) = &signature.parameters {
54 for (index, parameter) in parameters.iter().enumerate() {
55 let label_range = match ¶meter.label {
56 &lsp::ParameterLabel::LabelOffsets([offset1, offset2]) => {
57 maybe!({
58 let offset1 = offset1 as usize;
59 let offset2 = offset2 as usize;
60 if offset1 < offset2 {
61 let mut indices = label.char_indices().scan(
62 0,
63 |utf16_offset_acc, (offset, c)| {
64 let utf16_offset = *utf16_offset_acc;
65 *utf16_offset_acc += c.len_utf16();
66 Some((utf16_offset, offset))
67 },
68 );
69 let (_, offset1) = indices
70 .find(|(utf16_offset, _)| *utf16_offset == offset1)?;
71 let (_, offset2) = indices
72 .find(|(utf16_offset, _)| *utf16_offset == offset2)?;
73 Some(offset1..offset2)
74 } else {
75 log::warn!(
76 "language server {lang_server_id:?} produced invalid parameter label range: {offset1:?}..{offset2:?}",
77 );
78 None
79 }
80 })
81 }
82 lsp::ParameterLabel::Simple(parameter_label) => {
83 if let Some(start) = signature.label.find(parameter_label) {
84 Some(start..start + parameter_label.len())
85 } else {
86 None
87 }
88 }
89 };
90
91 if let Some(label_range) = &label_range
92 && index == active_parameter
93 {
94 highlights.push((
95 label_range.clone(),
96 HighlightStyle {
97 font_weight: Some(FontWeight::EXTRA_BOLD),
98 ..HighlightStyle::default()
99 },
100 ));
101 }
102
103 let documentation = parameter
104 .documentation
105 .as_ref()
106 .map(|doc| documentation_to_markdown(doc, language_registry.clone(), cx));
107
108 parameter_infos.push(ParameterInfo {
109 label_range,
110 documentation,
111 });
112 }
113 }
114
115 let documentation = signature
116 .documentation
117 .as_ref()
118 .map(|doc| documentation_to_markdown(doc, language_registry.clone(), cx));
119
120 signatures.push(SignatureHelpData {
121 label,
122 documentation,
123 highlights,
124 active_parameter: Some(active_parameter),
125 parameters: parameter_infos,
126 });
127 }
128 Some(Self {
129 signatures,
130 active_signature,
131 original_data: help,
132 })
133 }
134}
135
136fn documentation_to_markdown(
137 documentation: &lsp::Documentation,
138 language_registry: Option<Arc<LanguageRegistry>>,
139 cx: &mut App,
140) -> Entity<Markdown> {
141 match documentation {
142 lsp::Documentation::String(string) => {
143 cx.new(|cx| Markdown::new_text(SharedString::from(string), cx))
144 }
145 lsp::Documentation::MarkupContent(markup) => match markup.kind {
146 lsp::MarkupKind::PlainText => {
147 cx.new(|cx| Markdown::new_text(SharedString::from(&markup.value), cx))
148 }
149 lsp::MarkupKind::Markdown => cx.new(|cx| {
150 Markdown::new(
151 SharedString::from(&markup.value),
152 language_registry,
153 None,
154 cx,
155 )
156 }),
157 },
158 }
159}
160
161pub fn lsp_to_proto_signature(lsp_help: lsp::SignatureHelp) -> proto::SignatureHelp {
162 proto::SignatureHelp {
163 signatures: lsp_help
164 .signatures
165 .into_iter()
166 .map(|signature| proto::SignatureInformation {
167 label: signature.label,
168 documentation: signature.documentation.map(lsp_to_proto_documentation),
169 parameters: signature
170 .parameters
171 .unwrap_or_default()
172 .into_iter()
173 .map(|parameter_info| proto::ParameterInformation {
174 label: Some(match parameter_info.label {
175 lsp::ParameterLabel::Simple(label) => {
176 proto::parameter_information::Label::Simple(label)
177 }
178 lsp::ParameterLabel::LabelOffsets(offsets) => {
179 proto::parameter_information::Label::LabelOffsets(
180 proto::LabelOffsets {
181 start: offsets[0],
182 end: offsets[1],
183 },
184 )
185 }
186 }),
187 documentation: parameter_info.documentation.map(lsp_to_proto_documentation),
188 })
189 .collect(),
190 active_parameter: signature.active_parameter,
191 })
192 .collect(),
193 active_signature: lsp_help.active_signature,
194 active_parameter: lsp_help.active_parameter,
195 }
196}
197
198fn lsp_to_proto_documentation(documentation: lsp::Documentation) -> proto::Documentation {
199 proto::Documentation {
200 content: Some(match documentation {
201 lsp::Documentation::String(string) => proto::documentation::Content::Value(string),
202 lsp::Documentation::MarkupContent(content) => {
203 proto::documentation::Content::MarkupContent(proto::MarkupContent {
204 is_markdown: matches!(content.kind, lsp::MarkupKind::Markdown),
205 value: content.value,
206 })
207 }
208 }),
209 }
210}
211
212pub fn proto_to_lsp_signature(proto_help: proto::SignatureHelp) -> lsp::SignatureHelp {
213 lsp::SignatureHelp {
214 signatures: proto_help
215 .signatures
216 .into_iter()
217 .map(|signature| lsp::SignatureInformation {
218 label: signature.label,
219 documentation: signature.documentation.and_then(proto_to_lsp_documentation),
220 parameters: Some(
221 signature
222 .parameters
223 .into_iter()
224 .filter_map(|parameter_info| {
225 Some(lsp::ParameterInformation {
226 label: match parameter_info.label? {
227 proto::parameter_information::Label::Simple(string) => {
228 lsp::ParameterLabel::Simple(string)
229 }
230 proto::parameter_information::Label::LabelOffsets(offsets) => {
231 lsp::ParameterLabel::LabelOffsets([
232 offsets.start,
233 offsets.end,
234 ])
235 }
236 },
237 documentation: parameter_info
238 .documentation
239 .and_then(proto_to_lsp_documentation),
240 })
241 })
242 .collect(),
243 ),
244 active_parameter: signature.active_parameter,
245 })
246 .collect(),
247 active_signature: proto_help.active_signature,
248 active_parameter: proto_help.active_parameter,
249 }
250}
251
252fn proto_to_lsp_documentation(documentation: proto::Documentation) -> Option<lsp::Documentation> {
253 {
254 Some(match documentation.content? {
255 documentation::Content::Value(string) => lsp::Documentation::String(string),
256 documentation::Content::MarkupContent(markup) => {
257 lsp::Documentation::MarkupContent(if markup.is_markdown {
258 lsp::MarkupContent {
259 kind: lsp::MarkupKind::Markdown,
260 value: markup.value,
261 }
262 } else {
263 lsp::MarkupContent {
264 kind: lsp::MarkupKind::PlainText,
265 value: markup.value,
266 }
267 })
268 }
269 })
270 }
271}
272
273#[cfg(test)]
274mod tests {
275 use gpui::{FontWeight, HighlightStyle, SharedString, TestAppContext};
276 use lsp::{Documentation, MarkupContent, MarkupKind};
277
278 use crate::lsp_command::signature_help::SignatureHelp;
279
280 fn current_parameter() -> HighlightStyle {
281 HighlightStyle {
282 font_weight: Some(FontWeight::EXTRA_BOLD),
283 ..Default::default()
284 }
285 }
286
287 #[gpui::test]
288 fn test_create_signature_help_markdown_string_1(cx: &mut TestAppContext) {
289 let signature_help = lsp::SignatureHelp {
290 signatures: vec![lsp::SignatureInformation {
291 label: "fn test(foo: u8, bar: &str)".to_string(),
292 documentation: Some(Documentation::String(
293 "This is a test documentation".to_string(),
294 )),
295 parameters: Some(vec![
296 lsp::ParameterInformation {
297 label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
298 documentation: None,
299 },
300 lsp::ParameterInformation {
301 label: lsp::ParameterLabel::Simple("bar: &str".to_string()),
302 documentation: None,
303 },
304 ]),
305 active_parameter: None,
306 }],
307 active_signature: Some(0),
308 active_parameter: Some(0),
309 };
310 let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
311 assert!(maybe_markdown.is_some());
312
313 let markdown = maybe_markdown.unwrap();
314 let signature = markdown.signatures[markdown.active_signature].clone();
315 let markdown = (signature.label, signature.highlights);
316 assert_eq!(
317 markdown,
318 (
319 SharedString::new("fn test(foo: u8, bar: &str)"),
320 vec![(8..15, current_parameter())]
321 )
322 );
323 assert_eq!(
324 signature
325 .documentation
326 .unwrap()
327 .update(cx, |documentation, _| documentation.source().to_owned()),
328 "This is a test documentation",
329 )
330 }
331
332 #[gpui::test]
333 fn test_create_signature_help_markdown_string_2(cx: &mut TestAppContext) {
334 let signature_help = lsp::SignatureHelp {
335 signatures: vec![lsp::SignatureInformation {
336 label: "fn test(foo: u8, bar: &str)".to_string(),
337 documentation: Some(Documentation::MarkupContent(MarkupContent {
338 kind: MarkupKind::Markdown,
339 value: "This is a test documentation".to_string(),
340 })),
341 parameters: Some(vec![
342 lsp::ParameterInformation {
343 label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
344 documentation: None,
345 },
346 lsp::ParameterInformation {
347 label: lsp::ParameterLabel::Simple("bar: &str".to_string()),
348 documentation: None,
349 },
350 ]),
351 active_parameter: None,
352 }],
353 active_signature: Some(0),
354 active_parameter: Some(1),
355 };
356 let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
357 assert!(maybe_markdown.is_some());
358
359 let markdown = maybe_markdown.unwrap();
360 let signature = markdown.signatures[markdown.active_signature].clone();
361 let markdown = (signature.label, signature.highlights);
362 assert_eq!(
363 markdown,
364 (
365 SharedString::new("fn test(foo: u8, bar: &str)"),
366 vec![(17..26, current_parameter())]
367 )
368 );
369 assert_eq!(
370 signature
371 .documentation
372 .unwrap()
373 .update(cx, |documentation, _| documentation.source().to_owned()),
374 "This is a test documentation",
375 )
376 }
377
378 #[gpui::test]
379 fn test_create_signature_help_markdown_string_3(cx: &mut TestAppContext) {
380 let signature_help = lsp::SignatureHelp {
381 signatures: vec![
382 lsp::SignatureInformation {
383 label: "fn test1(foo: u8, bar: &str)".to_string(),
384 documentation: None,
385 parameters: Some(vec![
386 lsp::ParameterInformation {
387 label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
388 documentation: None,
389 },
390 lsp::ParameterInformation {
391 label: lsp::ParameterLabel::Simple("bar: &str".to_string()),
392 documentation: None,
393 },
394 ]),
395 active_parameter: None,
396 },
397 lsp::SignatureInformation {
398 label: "fn test2(hoge: String, fuga: bool)".to_string(),
399 documentation: None,
400 parameters: Some(vec![
401 lsp::ParameterInformation {
402 label: lsp::ParameterLabel::Simple("hoge: String".to_string()),
403 documentation: None,
404 },
405 lsp::ParameterInformation {
406 label: lsp::ParameterLabel::Simple("fuga: bool".to_string()),
407 documentation: None,
408 },
409 ]),
410 active_parameter: None,
411 },
412 ],
413 active_signature: Some(0),
414 active_parameter: Some(0),
415 };
416 let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
417 assert!(maybe_markdown.is_some());
418
419 let markdown = maybe_markdown.unwrap();
420 let signature = markdown.signatures[markdown.active_signature].clone();
421 let markdown = (signature.label, signature.highlights);
422 assert_eq!(
423 markdown,
424 (
425 SharedString::new("fn test1(foo: u8, bar: &str)"),
426 vec![(9..16, current_parameter())]
427 )
428 );
429 }
430
431 #[gpui::test]
432 fn test_create_signature_help_markdown_string_4(cx: &mut TestAppContext) {
433 let signature_help = lsp::SignatureHelp {
434 signatures: vec![
435 lsp::SignatureInformation {
436 label: "fn test1(foo: u8, bar: &str)".to_string(),
437 documentation: None,
438 parameters: Some(vec![
439 lsp::ParameterInformation {
440 label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
441 documentation: None,
442 },
443 lsp::ParameterInformation {
444 label: lsp::ParameterLabel::Simple("bar: &str".to_string()),
445 documentation: None,
446 },
447 ]),
448 active_parameter: None,
449 },
450 lsp::SignatureInformation {
451 label: "fn test2(hoge: String, fuga: bool)".to_string(),
452 documentation: None,
453 parameters: Some(vec![
454 lsp::ParameterInformation {
455 label: lsp::ParameterLabel::Simple("hoge: String".to_string()),
456 documentation: None,
457 },
458 lsp::ParameterInformation {
459 label: lsp::ParameterLabel::Simple("fuga: bool".to_string()),
460 documentation: None,
461 },
462 ]),
463 active_parameter: None,
464 },
465 ],
466 active_signature: Some(1),
467 active_parameter: Some(0),
468 };
469 let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
470 assert!(maybe_markdown.is_some());
471
472 let markdown = maybe_markdown.unwrap();
473 let signature = markdown.signatures[markdown.active_signature].clone();
474 let markdown = (signature.label, signature.highlights);
475 assert_eq!(
476 markdown,
477 (
478 SharedString::new("fn test2(hoge: String, fuga: bool)"),
479 vec![(9..21, current_parameter())]
480 )
481 );
482 }
483
484 #[gpui::test]
485 fn test_create_signature_help_markdown_string_5(cx: &mut TestAppContext) {
486 let signature_help = lsp::SignatureHelp {
487 signatures: vec![
488 lsp::SignatureInformation {
489 label: "fn test1(foo: u8, bar: &str)".to_string(),
490 documentation: None,
491 parameters: Some(vec![
492 lsp::ParameterInformation {
493 label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
494 documentation: None,
495 },
496 lsp::ParameterInformation {
497 label: lsp::ParameterLabel::Simple("bar: &str".to_string()),
498 documentation: None,
499 },
500 ]),
501 active_parameter: None,
502 },
503 lsp::SignatureInformation {
504 label: "fn test2(hoge: String, fuga: bool)".to_string(),
505 documentation: None,
506 parameters: Some(vec![
507 lsp::ParameterInformation {
508 label: lsp::ParameterLabel::Simple("hoge: String".to_string()),
509 documentation: None,
510 },
511 lsp::ParameterInformation {
512 label: lsp::ParameterLabel::Simple("fuga: bool".to_string()),
513 documentation: None,
514 },
515 ]),
516 active_parameter: None,
517 },
518 ],
519 active_signature: Some(1),
520 active_parameter: Some(1),
521 };
522 let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
523 assert!(maybe_markdown.is_some());
524
525 let markdown = maybe_markdown.unwrap();
526 let signature = markdown.signatures[markdown.active_signature].clone();
527 let markdown = (signature.label, signature.highlights);
528 assert_eq!(
529 markdown,
530 (
531 SharedString::new("fn test2(hoge: String, fuga: bool)"),
532 vec![(23..33, current_parameter())]
533 )
534 );
535 }
536
537 #[gpui::test]
538 fn test_create_signature_help_markdown_string_6(cx: &mut TestAppContext) {
539 let signature_help = lsp::SignatureHelp {
540 signatures: vec![
541 lsp::SignatureInformation {
542 label: "fn test1(foo: u8, bar: &str)".to_string(),
543 documentation: None,
544 parameters: Some(vec![
545 lsp::ParameterInformation {
546 label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
547 documentation: None,
548 },
549 lsp::ParameterInformation {
550 label: lsp::ParameterLabel::Simple("bar: &str".to_string()),
551 documentation: None,
552 },
553 ]),
554 active_parameter: None,
555 },
556 lsp::SignatureInformation {
557 label: "fn test2(hoge: String, fuga: bool)".to_string(),
558 documentation: None,
559 parameters: Some(vec![
560 lsp::ParameterInformation {
561 label: lsp::ParameterLabel::Simple("hoge: String".to_string()),
562 documentation: None,
563 },
564 lsp::ParameterInformation {
565 label: lsp::ParameterLabel::Simple("fuga: bool".to_string()),
566 documentation: None,
567 },
568 ]),
569 active_parameter: None,
570 },
571 ],
572 active_signature: Some(1),
573 active_parameter: None,
574 };
575 let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
576 assert!(maybe_markdown.is_some());
577
578 let markdown = maybe_markdown.unwrap();
579 let signature = markdown.signatures[markdown.active_signature].clone();
580 let markdown = (signature.label, signature.highlights);
581 assert_eq!(
582 markdown,
583 (
584 SharedString::new("fn test2(hoge: String, fuga: bool)"),
585 vec![(9..21, current_parameter())]
586 )
587 );
588 }
589
590 #[gpui::test]
591 fn test_create_signature_help_markdown_string_7(cx: &mut TestAppContext) {
592 let signature_help = lsp::SignatureHelp {
593 signatures: vec![
594 lsp::SignatureInformation {
595 label: "fn test1(foo: u8, bar: &str)".to_string(),
596 documentation: None,
597 parameters: Some(vec![
598 lsp::ParameterInformation {
599 label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
600 documentation: None,
601 },
602 lsp::ParameterInformation {
603 label: lsp::ParameterLabel::Simple("bar: &str".to_string()),
604 documentation: None,
605 },
606 ]),
607 active_parameter: None,
608 },
609 lsp::SignatureInformation {
610 label: "fn test2(hoge: String, fuga: bool)".to_string(),
611 documentation: None,
612 parameters: Some(vec![
613 lsp::ParameterInformation {
614 label: lsp::ParameterLabel::Simple("hoge: String".to_string()),
615 documentation: None,
616 },
617 lsp::ParameterInformation {
618 label: lsp::ParameterLabel::Simple("fuga: bool".to_string()),
619 documentation: None,
620 },
621 ]),
622 active_parameter: None,
623 },
624 lsp::SignatureInformation {
625 label: "fn test3(one: usize, two: u32)".to_string(),
626 documentation: None,
627 parameters: Some(vec![
628 lsp::ParameterInformation {
629 label: lsp::ParameterLabel::Simple("one: usize".to_string()),
630 documentation: None,
631 },
632 lsp::ParameterInformation {
633 label: lsp::ParameterLabel::Simple("two: u32".to_string()),
634 documentation: None,
635 },
636 ]),
637 active_parameter: None,
638 },
639 ],
640 active_signature: Some(2),
641 active_parameter: Some(1),
642 };
643 let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
644 assert!(maybe_markdown.is_some());
645
646 let markdown = maybe_markdown.unwrap();
647 let signature = markdown.signatures[markdown.active_signature].clone();
648 let markdown = (signature.label, signature.highlights);
649 assert_eq!(
650 markdown,
651 (
652 SharedString::new("fn test3(one: usize, two: u32)"),
653 vec![(21..29, current_parameter())]
654 )
655 );
656 }
657
658 #[gpui::test]
659 fn test_create_signature_help_markdown_string_8(cx: &mut TestAppContext) {
660 let signature_help = lsp::SignatureHelp {
661 signatures: vec![],
662 active_signature: None,
663 active_parameter: None,
664 };
665 let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
666 assert!(maybe_markdown.is_none());
667 }
668
669 #[gpui::test]
670 fn test_create_signature_help_markdown_string_9(cx: &mut TestAppContext) {
671 let signature_help = lsp::SignatureHelp {
672 signatures: vec![lsp::SignatureInformation {
673 label: "fn test(foo: u8, bar: &str)".to_string(),
674 documentation: None,
675 parameters: Some(vec![
676 lsp::ParameterInformation {
677 label: lsp::ParameterLabel::LabelOffsets([8, 15]),
678 documentation: None,
679 },
680 lsp::ParameterInformation {
681 label: lsp::ParameterLabel::LabelOffsets([17, 26]),
682 documentation: None,
683 },
684 ]),
685 active_parameter: None,
686 }],
687 active_signature: Some(0),
688 active_parameter: Some(0),
689 };
690 let maybe_markdown = cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
691 assert!(maybe_markdown.is_some());
692
693 let markdown = maybe_markdown.unwrap();
694 let signature = markdown.signatures[markdown.active_signature].clone();
695 let markdown = (signature.label, signature.highlights);
696 assert_eq!(
697 markdown,
698 (
699 SharedString::new("fn test(foo: u8, bar: &str)"),
700 vec![(8..15, current_parameter())]
701 )
702 );
703 }
704
705 #[gpui::test]
706 fn test_parameter_documentation(cx: &mut TestAppContext) {
707 let signature_help = lsp::SignatureHelp {
708 signatures: vec![lsp::SignatureInformation {
709 label: "fn test(foo: u8, bar: &str)".to_string(),
710 documentation: Some(Documentation::String(
711 "This is a test documentation".to_string(),
712 )),
713 parameters: Some(vec![
714 lsp::ParameterInformation {
715 label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
716 documentation: Some(Documentation::String("The foo parameter".to_string())),
717 },
718 lsp::ParameterInformation {
719 label: lsp::ParameterLabel::Simple("bar: &str".to_string()),
720 documentation: Some(Documentation::String("The bar parameter".to_string())),
721 },
722 ]),
723 active_parameter: None,
724 }],
725 active_signature: Some(0),
726 active_parameter: Some(0),
727 };
728 let maybe_signature_help =
729 cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
730 assert!(maybe_signature_help.is_some());
731
732 let signature_help = maybe_signature_help.unwrap();
733 let signature = &signature_help.signatures[signature_help.active_signature];
734
735 // Check that parameter documentation is extracted
736 assert_eq!(signature.parameters.len(), 2);
737 assert_eq!(
738 signature.parameters[0]
739 .documentation
740 .as_ref()
741 .unwrap()
742 .update(cx, |documentation, _| documentation.source().to_owned()),
743 "The foo parameter",
744 );
745 assert_eq!(
746 signature.parameters[1]
747 .documentation
748 .as_ref()
749 .unwrap()
750 .update(cx, |documentation, _| documentation.source().to_owned()),
751 "The bar parameter",
752 );
753
754 // Check that the active parameter is correct
755 assert_eq!(signature.active_parameter, Some(0));
756 }
757
758 #[gpui::test]
759 fn test_create_signature_help_implements_utf16_spec(cx: &mut TestAppContext) {
760 let signature_help = lsp::SignatureHelp {
761 signatures: vec![lsp::SignatureInformation {
762 label: "fn test(🦀: u8, 🦀: &str)".to_string(),
763 documentation: None,
764 parameters: Some(vec![
765 lsp::ParameterInformation {
766 label: lsp::ParameterLabel::LabelOffsets([8, 10]),
767 documentation: None,
768 },
769 lsp::ParameterInformation {
770 label: lsp::ParameterLabel::LabelOffsets([16, 18]),
771 documentation: None,
772 },
773 ]),
774 active_parameter: None,
775 }],
776 active_signature: Some(0),
777 active_parameter: Some(0),
778 };
779 let signature_help = cx.update(|cx| SignatureHelp::new(signature_help, None, None, cx));
780 assert!(signature_help.is_some());
781
782 let markdown = signature_help.unwrap();
783 let signature = markdown.signatures[markdown.active_signature].clone();
784 let markdown = (signature.label, signature.highlights);
785 assert_eq!(
786 markdown,
787 (
788 SharedString::new("fn test(🦀: u8, 🦀: &str)"),
789 vec![(8..12, current_parameter())]
790 )
791 );
792 }
793}