signature_help.rs

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