Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/fourslash/tests/basicQuickInfo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ class Foo/*3*/ {
`
f := fourslash.NewFourslash(t, nil /*capabilities*/, content)
f.VerifyQuickInfoAt(t, "1", "var someVar: number", "Some var")
f.VerifyQuickInfoAt(t, "2", "var otherVar: number", "Other var\nSee `someVar`")
f.VerifyQuickInfoAt(t, "2", "var otherVar: number", "Other var\nSee [someVar](file:///basicQuickInfo.ts#4,5-4,12)")
}
6 changes: 3 additions & 3 deletions internal/ls/completions.go
Original file line number Diff line number Diff line change
Expand Up @@ -5091,7 +5091,7 @@ func (l *LanguageService) getCompletionItemDetails(
case symbolCompletion.symbol != nil:
symbolDetails := symbolCompletion.symbol
actions := l.getCompletionItemActions(ctx, checker, file, position, itemData, symbolDetails)
return createCompletionDetailsForSymbol(
return l.createCompletionDetailsForSymbol(
item,
symbolDetails.symbol,
checker,
Expand Down Expand Up @@ -5287,7 +5287,7 @@ type codeAction struct {
changes []*lsproto.TextEdit
}

func createCompletionDetailsForSymbol(
func (l *LanguageService) createCompletionDetailsForSymbol(
item *lsproto.CompletionItem,
symbol *ast.Symbol,
checker *checker.Checker,
Expand All @@ -5300,7 +5300,7 @@ func createCompletionDetailsForSymbol(
details = append(details, action.description)
edits = append(edits, action.changes...)
}
quickInfo, documentation := getQuickInfoAndDocumentationForSymbol(checker, symbol, location)
quickInfo, documentation := l.getQuickInfoAndDocumentationForSymbol(checker, symbol, location)
details = append(details, quickInfo)
if len(edits) != 0 {
item.AdditionalTextEdits = &edits
Expand Down
119 changes: 74 additions & 45 deletions internal/ls/hover.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func (l *LanguageService) ProvideHover(ctx context.Context, documentURI lsproto.
}
c, done := program.GetTypeCheckerForFile(ctx, file)
defer done()
quickInfo, documentation := getQuickInfoAndDocumentation(c, node)
quickInfo, documentation := l.getQuickInfoAndDocumentation(c, node)
if quickInfo == "" {
return lsproto.HoverOrNull{}, nil
}
Expand All @@ -43,19 +43,19 @@ func (l *LanguageService) ProvideHover(ctx context.Context, documentURI lsproto.
}, nil
}

func getQuickInfoAndDocumentation(c *checker.Checker, node *ast.Node) (string, string) {
return getQuickInfoAndDocumentationForSymbol(c, c.GetSymbolAtLocation(node), getNodeForQuickInfo(node))
func (l *LanguageService) getQuickInfoAndDocumentation(c *checker.Checker, node *ast.Node) (string, string) {
return l.getQuickInfoAndDocumentationForSymbol(c, c.GetSymbolAtLocation(node), getNodeForQuickInfo(node))
}

func getQuickInfoAndDocumentationForSymbol(c *checker.Checker, symbol *ast.Symbol, node *ast.Node) (string, string) {
func (l *LanguageService) getQuickInfoAndDocumentationForSymbol(c *checker.Checker, symbol *ast.Symbol, node *ast.Node) (string, string) {
quickInfo, declaration := getQuickInfoAndDeclarationAtLocation(c, symbol, node)
if quickInfo == "" {
return "", ""
}
var b strings.Builder
if declaration != nil {
if jsdoc := getJSDocOrTag(declaration); jsdoc != nil && !containsTypedefTag(jsdoc) {
writeComments(&b, jsdoc.Comments())
l.writeComments(&b, c, jsdoc.Comments())
if jsdoc.Kind == ast.KindJSDoc {
if tags := jsdoc.AsJSDoc().Tags; tags != nil {
for _, tag := range tags.Nodes {
Expand Down Expand Up @@ -90,7 +90,7 @@ func getQuickInfoAndDocumentationForSymbol(c *checker.Checker, symbol *ast.Symbo
b.WriteString("— ")
}
}
writeComments(&b, comments)
l.writeComments(&b, c, comments)
}
}
}
Expand Down Expand Up @@ -411,61 +411,90 @@ func writeCode(b *strings.Builder, lang string, code string) {
b.WriteByte('\n')
}

func writeComments(b *strings.Builder, comments []*ast.Node) {
func (l *LanguageService) writeComments(b *strings.Builder, c *checker.Checker, comments []*ast.Node) {
for _, comment := range comments {
switch comment.Kind {
case ast.KindJSDocText:
b.WriteString(comment.Text())
case ast.KindJSDocLink:
name := comment.Name()
text := comment.AsJSDocLink().Text()
if name != nil {
if text == "" {
writeEntityName(b, name)
} else {
writeEntityNameParts(b, name)
}
}
b.WriteString(text)
case ast.KindJSDocLink, ast.KindJSDocLinkPlain:
l.writeJSDocLink(b, c, comment, false /*quote*/)
case ast.KindJSDocLinkCode:
// !!! TODO: This is a temporary placeholder implementation that needs to be updated later
name := comment.Name()
text := comment.AsJSDocLinkCode().Text()
if name != nil {
if text == "" {
writeEntityName(b, name)
} else {
writeEntityNameParts(b, name)
}
}
b.WriteString(text)
case ast.KindJSDocLinkPlain:
// !!! TODO: This is a temporary placeholder implementation that needs to be updated later
name := comment.Name()
text := comment.AsJSDocLinkPlain().Text()
if name != nil {
if text == "" {
writeEntityName(b, name)
} else {
writeEntityNameParts(b, name)
}
l.writeJSDocLink(b, c, comment, true /*quote*/)
}
}
}

func (l *LanguageService) writeJSDocLink(b *strings.Builder, c *checker.Checker, link *ast.Node, quote bool) {
name := link.Name()
text := strings.Trim(link.Text(), " ")
if name == nil {
writeQuotedString(b, text, quote)
return
}
if ast.IsIdentifier(name) && (name.Text() == "http" || name.Text() == "https") && strings.HasPrefix(text, "://") {
linkText := name.Text() + text
linkUri := linkText
if commentPos := strings.IndexFunc(linkText, func(ch rune) bool { return ch == ' ' || ch == '|' }); commentPos >= 0 {
linkUri = linkText[:commentPos]
linkText = trimCommentPrefix(linkText[commentPos:])
if linkText == "" {
linkText = linkUri
}
b.WriteString(text)
}
writeMarkdownLink(b, linkText, linkUri, quote)
return
}
declarations := getDeclarationsFromLocation(c, name)
if len(declarations) != 0 {
declaration := declarations[0]
file := ast.GetSourceFileOfNode(declaration)
node := core.OrElse(ast.GetNameOfDeclaration(declaration), declaration)
loc := l.getMappedLocation(file.FileName(), createRangeFromNode(node, file))
prefixLen := core.IfElse(strings.HasPrefix(text, "()"), 2, 0)
linkText := trimCommentPrefix(text[prefixLen:])
if linkText == "" {
linkText = getEntityNameString(name) + text[:prefixLen]
}
linkUri := fmt.Sprintf("%v#%v,%v-%v,%v", loc.Uri, loc.Range.Start.Line+1, loc.Range.Start.Character+1, loc.Range.End.Line+1, loc.Range.End.Character+1)
writeMarkdownLink(b, linkText, linkUri, quote)
return
}
writeQuotedString(b, getEntityNameString(name)+" "+text, quote)
}

func trimCommentPrefix(text string) string {
return strings.TrimLeft(strings.TrimPrefix(strings.TrimLeft(text, " "), "|"), " ")
}

func writeMarkdownLink(b *strings.Builder, text string, uri string, quote bool) {
b.WriteString("[")
writeQuotedString(b, text, quote)
b.WriteString("](")
b.WriteString(uri)
b.WriteString(")")
}

func writeOptionalEntityName(b *strings.Builder, name *ast.Node) {
if name != nil {
b.WriteString(" ")
writeEntityName(b, name)
writeQuotedString(b, getEntityNameString(name), true /*quote*/)
}
}

func writeQuotedString(b *strings.Builder, str string, quote bool) {
if quote && !strings.Contains(str, "`") {
b.WriteString("`")
b.WriteString(str)
b.WriteString("`")
} else {
b.WriteString(str)
}
}

func writeEntityName(b *strings.Builder, name *ast.Node) {
b.WriteString("`")
writeEntityNameParts(b, name)
b.WriteString("`")
func getEntityNameString(name *ast.Node) string {
var b strings.Builder
writeEntityNameParts(&b, name)
return b.String()
}

func writeEntityNameParts(b *strings.Builder, node *ast.Node) {
Expand Down
6 changes: 3 additions & 3 deletions internal/ls/string_completions.go
Original file line number Diff line number Diff line change
Expand Up @@ -683,10 +683,10 @@ func (l *LanguageService) getStringLiteralCompletionDetails(
if completions == nil {
return item
}
return stringLiteralCompletionDetails(item, name, contextToken, completions, file, checker)
return l.stringLiteralCompletionDetails(item, name, contextToken, completions, file, checker)
}

func stringLiteralCompletionDetails(
func (l *LanguageService) stringLiteralCompletionDetails(
item *lsproto.CompletionItem,
name string,
location *ast.Node,
Expand All @@ -706,7 +706,7 @@ func stringLiteralCompletionDetails(
properties := completion.fromProperties
for _, symbol := range properties.symbols {
if symbol.Name == name {
return createCompletionDetailsForSymbol(item, symbol, checker, location, nil /*actions*/)
return l.createCompletionDetailsForSymbol(item, symbol, checker, location, nil /*actions*/)
}
}
case completion.fromTypes != nil:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@
// | ```tsx
// | function CC(): void
// | ```
// | `C`
// | [C](file:///jsdocLink1.ts#1,7-1,8)
// |
// | *@wat* — Makes a `C`. A default one.
// | C()
// | C|postfix text
// | unformattedpostfix text
// | *@wat* — Makes a [C](file:///jsdocLink1.ts#1,7-1,8). A default one.
// | [C()](file:///jsdocLink1.ts#1,7-1,8)
// | [postfix text](file:///jsdocLink1.ts#1,7-1,8)
// | unformatted postfix text
// |
// | *@see* — `C` its great
// | *@see* — [C](file:///jsdocLink1.ts#1,7-1,8) its great
// |
// | ----------------------------------------------------------------------
// }
Expand All @@ -41,7 +41,7 @@
"item": {
"contents": {
"kind": "markdown",
"value": "```tsx\nfunction CC(): void\n```\n`C`\n\n*@wat* — Makes a `C`. A default one.\nC()\nC|postfix text\nunformattedpostfix text\n\n*@see* — `C` its great\n"
"value": "```tsx\nfunction CC(): void\n```\n[C](file:///jsdocLink1.ts#1,7-1,8)\n\n*@wat* — Makes a [C](file:///jsdocLink1.ts#1,7-1,8). A default one.\n[C()](file:///jsdocLink1.ts#1,7-1,8)\n[postfix text](file:///jsdocLink1.ts#1,7-1,8)\nunformatted postfix text\n\n*@see* — [C](file:///jsdocLink1.ts#1,7-1,8) its great\n"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// | ```tsx
// | (method) I.bar(): void
// | ```
// | `I`
// | [I](file:///jsdocLink4.ts#1,15-1,16)
// | ----------------------------------------------------------------------
// }
// /** {@link I} */
Expand All @@ -18,7 +18,7 @@
// | ```tsx
// | var n: number
// | ```
// | `I`
// | [I](file:///jsdocLink4.ts#1,15-1,16)
// | ----------------------------------------------------------------------
// /**
// * A real, very serious {@link I to an interface}. Right there.
Expand All @@ -32,9 +32,9 @@
// | ```tsx
// | function f(x: any): void
// | ```
// | A real, very serious Ito an interface. Right there.
// | A real, very serious [to an interface](file:///jsdocLink4.ts#1,15-1,16). Right there.
// |
// | *@param* `x` — one Poshere too
// | *@param* `x` — one [here too](file:///jsdocLink4.ts#14,6-14,9)
// | ----------------------------------------------------------------------
// type Pos = [number, number]
[
Expand All @@ -51,7 +51,7 @@
"item": {
"contents": {
"kind": "markdown",
"value": "```tsx\n(method) I.bar(): void\n```\n`I`"
"value": "```tsx\n(method) I.bar(): void\n```\n[I](file:///jsdocLink4.ts#1,15-1,16)"
}
}
},
Expand All @@ -68,7 +68,7 @@
"item": {
"contents": {
"kind": "markdown",
"value": "```tsx\nvar n: number\n```\n`I`"
"value": "```tsx\nvar n: number\n```\n[I](file:///jsdocLink4.ts#1,15-1,16)"
}
}
},
Expand All @@ -85,7 +85,7 @@
"item": {
"contents": {
"kind": "markdown",
"value": "```tsx\nfunction f(x: any): void\n```\nA real, very serious Ito an interface. Right there.\n\n*@param* `x` — one Poshere too"
"value": "```tsx\nfunction f(x: any): void\n```\nA real, very serious [to an interface](file:///jsdocLink4.ts#1,15-1,16). Right there.\n\n*@param* `x` — one [here too](file:///jsdocLink4.ts#14,6-14,9)"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
// | ```tsx
// | function f(x: any): void
// | ```
// | g() g() g() g() 0 g()1 g() 2
// | u() u() u() u() 0 u()1 u() 2
// | [g()](file:///jsdocLink5.ts#1,10-1,11) [g()](file:///jsdocLink5.ts#1,10-1,11) [g()](file:///jsdocLink5.ts#1,10-1,11) [0](file:///jsdocLink5.ts#1,10-1,11) [1](file:///jsdocLink5.ts#1,10-1,11) [2](file:///jsdocLink5.ts#1,10-1,11)
// | u () u () u () u () 0 u ()1 u () 2
// | ----------------------------------------------------------------------
[
{
Expand All @@ -30,7 +30,7 @@
"item": {
"contents": {
"kind": "markdown",
"value": "```tsx\nfunction f(x: any): void\n```\ng() g() g() g() 0 g()1 g() 2\nu() u() u() u() 0 u()1 u() 2"
"value": "```tsx\nfunction f(x: any): void\n```\n[g()](file:///jsdocLink5.ts#1,10-1,11) [g()](file:///jsdocLink5.ts#1,10-1,11) [g()](file:///jsdocLink5.ts#1,10-1,11) [0](file:///jsdocLink5.ts#1,10-1,11) [1](file:///jsdocLink5.ts#1,10-1,11) [2](file:///jsdocLink5.ts#1,10-1,11)\nu () u () u () u () 0 u ()1 u () 2"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
// | ```
// |
// |
// | *@see* — https://hva
// | *@see* — [https://hva](https://hva)
// | ----------------------------------------------------------------------
//
// /** {@link https://hvaD} */
Expand All @@ -62,7 +62,7 @@
// | ```tsx
// | var see3: boolean
// | ```
// | https://hvaD
// | [https://hvaD](https://hvaD)
// | ----------------------------------------------------------------------
[
{
Expand Down Expand Up @@ -146,7 +146,7 @@
"item": {
"contents": {
"kind": "markdown",
"value": "```tsx\nvar see2: boolean\n```\n\n\n*@see* — https://hva "
"value": "```tsx\nvar see2: boolean\n```\n\n\n*@see* — [https://hva](https://hva) "
}
}
},
Expand All @@ -163,7 +163,7 @@
"item": {
"contents": {
"kind": "markdown",
"value": "```tsx\nvar see3: boolean\n```\nhttps://hvaD"
"value": "```tsx\nvar see3: boolean\n```\n[https://hvaD](https://hvaD)"
}
}
}
Expand Down
Loading