Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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("%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)
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