@@ -27,7 +27,7 @@ func (l *LanguageService) ProvideHover(ctx context.Context, documentURI lsproto.
2727 }
2828 c , done := program .GetTypeCheckerForFile (ctx , file )
2929 defer done ()
30- quickInfo , documentation := getQuickInfoAndDocumentation (c , node )
30+ quickInfo , documentation := l . getQuickInfoAndDocumentation (c , node )
3131 if quickInfo == "" {
3232 return lsproto.HoverOrNull {}, nil
3333 }
@@ -43,19 +43,19 @@ func (l *LanguageService) ProvideHover(ctx context.Context, documentURI lsproto.
4343 }, nil
4444}
4545
46- func getQuickInfoAndDocumentation (c * checker.Checker , node * ast.Node ) (string , string ) {
47- return getQuickInfoAndDocumentationForSymbol (c , c .GetSymbolAtLocation (node ), getNodeForQuickInfo (node ))
46+ func ( l * LanguageService ) getQuickInfoAndDocumentation (c * checker.Checker , node * ast.Node ) (string , string ) {
47+ return l . getQuickInfoAndDocumentationForSymbol (c , c .GetSymbolAtLocation (node ), getNodeForQuickInfo (node ))
4848}
4949
50- func getQuickInfoAndDocumentationForSymbol (c * checker.Checker , symbol * ast.Symbol , node * ast.Node ) (string , string ) {
50+ func ( l * LanguageService ) getQuickInfoAndDocumentationForSymbol (c * checker.Checker , symbol * ast.Symbol , node * ast.Node ) (string , string ) {
5151 quickInfo , declaration := getQuickInfoAndDeclarationAtLocation (c , symbol , node )
5252 if quickInfo == "" {
5353 return "" , ""
5454 }
5555 var b strings.Builder
5656 if declaration != nil {
5757 if jsdoc := getJSDocOrTag (declaration ); jsdoc != nil && ! containsTypedefTag (jsdoc ) {
58- writeComments (& b , jsdoc .Comments ())
58+ l . writeComments (& b , c , jsdoc .Comments ())
5959 if jsdoc .Kind == ast .KindJSDoc {
6060 if tags := jsdoc .AsJSDoc ().Tags ; tags != nil {
6161 for _ , tag := range tags .Nodes {
@@ -90,7 +90,7 @@ func getQuickInfoAndDocumentationForSymbol(c *checker.Checker, symbol *ast.Symbo
9090 b .WriteString ("— " )
9191 }
9292 }
93- writeComments (& b , comments )
93+ l . writeComments (& b , c , comments )
9494 }
9595 }
9696 }
@@ -411,61 +411,90 @@ func writeCode(b *strings.Builder, lang string, code string) {
411411 b .WriteByte ('\n' )
412412}
413413
414- func writeComments (b * strings.Builder , comments []* ast.Node ) {
414+ func ( l * LanguageService ) writeComments (b * strings.Builder , c * checker. Checker , comments []* ast.Node ) {
415415 for _ , comment := range comments {
416416 switch comment .Kind {
417417 case ast .KindJSDocText :
418418 b .WriteString (comment .Text ())
419- case ast .KindJSDocLink :
420- name := comment .Name ()
421- text := comment .AsJSDocLink ().Text ()
422- if name != nil {
423- if text == "" {
424- writeEntityName (b , name )
425- } else {
426- writeEntityNameParts (b , name )
427- }
428- }
429- b .WriteString (text )
419+ case ast .KindJSDocLink , ast .KindJSDocLinkPlain :
420+ l .writeJSDocLink (b , c , comment , false /*quote*/ )
430421 case ast .KindJSDocLinkCode :
431- // !!! TODO: This is a temporary placeholder implementation that needs to be updated later
432- name := comment .Name ()
433- text := comment .AsJSDocLinkCode ().Text ()
434- if name != nil {
435- if text == "" {
436- writeEntityName (b , name )
437- } else {
438- writeEntityNameParts (b , name )
439- }
440- }
441- b .WriteString (text )
442- case ast .KindJSDocLinkPlain :
443- // !!! TODO: This is a temporary placeholder implementation that needs to be updated later
444- name := comment .Name ()
445- text := comment .AsJSDocLinkPlain ().Text ()
446- if name != nil {
447- if text == "" {
448- writeEntityName (b , name )
449- } else {
450- writeEntityNameParts (b , name )
451- }
422+ l .writeJSDocLink (b , c , comment , true /*quote*/ )
423+ }
424+ }
425+ }
426+
427+ func (l * LanguageService ) writeJSDocLink (b * strings.Builder , c * checker.Checker , link * ast.Node , quote bool ) {
428+ name := link .Name ()
429+ text := strings .Trim (link .Text (), " " )
430+ if name == nil {
431+ writeQuotedString (b , text , quote )
432+ return
433+ }
434+ if ast .IsIdentifier (name ) && (name .Text () == "http" || name .Text () == "https" ) && strings .HasPrefix (text , "://" ) {
435+ linkText := name .Text () + text
436+ linkUri := linkText
437+ if commentPos := strings .IndexFunc (linkText , func (ch rune ) bool { return ch == ' ' || ch == '|' }); commentPos >= 0 {
438+ linkUri = linkText [:commentPos ]
439+ linkText = trimCommentPrefix (linkText [commentPos :])
440+ if linkText == "" {
441+ linkText = linkUri
452442 }
453- b .WriteString (text )
454443 }
444+ writeMarkdownLink (b , linkText , linkUri , quote )
445+ return
446+ }
447+ declarations := getDeclarationsFromLocation (c , name )
448+ if len (declarations ) != 0 {
449+ declaration := declarations [0 ]
450+ file := ast .GetSourceFileOfNode (declaration )
451+ node := core .OrElse (ast .GetNameOfDeclaration (declaration ), declaration )
452+ loc := l .getMappedLocation (file .FileName (), createRangeFromNode (node , file ))
453+ prefixLen := core .IfElse (strings .HasPrefix (text , "()" ), 2 , 0 )
454+ linkText := trimCommentPrefix (text [prefixLen :])
455+ if linkText == "" {
456+ linkText = getEntityNameString (name ) + text [:prefixLen ]
457+ }
458+ linkUri := fmt .Sprintf ("%s#%d,%d-%d,%d" , loc .Uri , loc .Range .Start .Line + 1 , loc .Range .Start .Character + 1 , loc .Range .End .Line + 1 , loc .Range .End .Character + 1 )
459+ writeMarkdownLink (b , linkText , linkUri , quote )
460+ return
455461 }
462+ writeQuotedString (b , getEntityNameString (name )+ " " + text , quote )
463+ }
464+
465+ func trimCommentPrefix (text string ) string {
466+ return strings .TrimLeft (strings .TrimPrefix (strings .TrimLeft (text , " " ), "|" ), " " )
467+ }
468+
469+ func writeMarkdownLink (b * strings.Builder , text string , uri string , quote bool ) {
470+ b .WriteString ("[" )
471+ writeQuotedString (b , text , quote )
472+ b .WriteString ("](" )
473+ b .WriteString (uri )
474+ b .WriteString (")" )
456475}
457476
458477func writeOptionalEntityName (b * strings.Builder , name * ast.Node ) {
459478 if name != nil {
460479 b .WriteString (" " )
461- writeEntityName (b , name )
480+ writeQuotedString (b , getEntityNameString (name ), true /*quote*/ )
481+ }
482+ }
483+
484+ func writeQuotedString (b * strings.Builder , str string , quote bool ) {
485+ if quote && ! strings .Contains (str , "`" ) {
486+ b .WriteString ("`" )
487+ b .WriteString (str )
488+ b .WriteString ("`" )
489+ } else {
490+ b .WriteString (str )
462491 }
463492}
464493
465- func writeEntityName ( b * strings. Builder , name * ast.Node ) {
466- b . WriteString ( "`" )
467- writeEntityNameParts (b , name )
468- b . WriteString ( "`" )
494+ func getEntityNameString ( name * ast.Node ) string {
495+ var b strings. Builder
496+ writeEntityNameParts (& b , name )
497+ return b . String ( )
469498}
470499
471500func writeEntityNameParts (b * strings.Builder , node * ast.Node ) {
0 commit comments