Skip to content
Open
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: 0 additions & 2 deletions internal/fourslash/_scripts/failingTests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,6 @@ TestQuickInfoJSDocFunctionThis
TestQuickInfoJsDocGetterSetterNoCrash1
TestQuickInfoJsdocTypedefMissingType
TestQuickInfoJSExport
TestQuickInfoMappedSpreadTypes
TestQuickInfoMappedType
TestQuickInfoMeaning
TestQuickInfoModuleVariables
Expand Down Expand Up @@ -364,7 +363,6 @@ TestQuickInfoTypeOfThisInStatics
TestQuickInfoTypeOnlyNamespaceAndClass
TestQuickInfoUnionOfNamespaces
TestQuickInfoUntypedModuleImport
TestQuickinfoWrongComment
TestRecursiveInternalModuleImport
TestRegexDetection
TestSelfReferencedExternalModule
Expand Down
35 changes: 35 additions & 0 deletions internal/fourslash/tests/quickInfoMappedTypeJSDoc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package fourslash_test

import (
"testing"

"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)

func TestQuickInfoMappedTypeJSDoc(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")

// https://github.com/microsoft/typescript-go/issues/3659
const content = `
type Test = {
/** a's comment */
a: string;
};

type Mapped = {
[K in keyof Test]: number;
};

const x: Mapped = {
a: 123
};

x./*1*/a
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()

f.VerifyQuickInfoAt(t, "1", "(property) a: number", "a's comment")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package fourslash_test

import (
"testing"

"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)

func TestQuickInfoRootSymbolJSDocAggregation(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")

const content = `
declare const distinct: {
/** first */
a: number;
} & {
/** second */
a: number;
};

declare const duplicate: {
/** same */
a: number;
} & {
/** same */
a: number;
} & {
/** third */
a: number;
};

declare const mixed: {
/** first */
a: number;
} & {
/** second */
a: number;
} & {
/** first */
a: number;
};

distinct./*distinct*/a
duplicate./*duplicate*/a
mixed./*mixed*/a
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()

f.VerifyQuickInfoAt(t, "distinct", "(property) a: number", "first\nsecond")
f.VerifyQuickInfoAt(t, "duplicate", "(property) a: number", "same\nthird")
f.VerifyQuickInfoAt(t, "mixed", "(property) a: number", "first\nsecond")
}
43 changes: 43 additions & 0 deletions internal/ls/hover.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ func (l *LanguageService) getQuickInfoAndDocumentationForSymbol(c *checker.Check
return quickInfo, documentation
}

documentation = l.documentationFromRootSymbols(c, symbol, node, contentFormat)
if documentation != "" {
return quickInfo, documentation
}

return quickInfo, l.documentationFromAlias(c, symbol, node, contentFormat)
}

Expand Down Expand Up @@ -148,6 +153,44 @@ func (l *LanguageService) documentationFromAlias(c *checker.Checker, symbol *ast
return ""
}

func (l *LanguageService) documentationFromRootSymbols(c *checker.Checker, symbol *ast.Symbol, node *ast.Node, contentFormat lsproto.MarkupKind) string {
if symbol == nil {
return ""
}

var docs []string
for _, rootSymbol := range c.GetRootSymbols(symbol) {
if rootSymbol == nil {
continue
}
declaration := rootSymbol.ValueDeclaration
if declaration == nil {
declaration = core.FirstOrNil(rootSymbol.Declarations)
}
if declaration == nil {
continue
}
if documentation := l.getDocumentationFromDeclaration(c, rootSymbol, declaration, node, contentFormat, false /*commentOnly*/); documentation != "" {
docs = core.AppendIfUnique(docs, documentation)
}
}
Comment on lines +162 to +176
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetRootSymbols(symbol) can return multiple candidates (e.g., union/intersection properties). Returning the first non-empty documentation can yield incorrect or non-deterministic hover text when root symbols have differing JSDoc. Consider only returning documentation when it is unique/consistent across all root symbols (e.g., collect non-empty docs and return it only if they’re all identical), otherwise fall back to the existing alias/documentation path.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, actually in Strada all declarations (with some deduplication) contribute to the documentation: https://github.com/microsoft/TypeScript/blob/f350b52331494b68c90ab02e2b6d0828d2a22a74/src/services/jsDoc.ts#L187-L221


if len(docs) == 0 {
return ""
}
if len(docs) == 1 {
return docs[0]
}

var b strings.Builder
b.WriteString(docs[0])
for _, doc := range docs[1:] {
b.WriteString("\n")
b.WriteString(doc)
}
return b.String()
}

func (l *LanguageService) getDocumentationFromDeclaration(c *checker.Checker, symbol *ast.Symbol, declaration *ast.Node, location *ast.Node, contentFormat lsproto.MarkupKind, commentOnly bool) string {
if declaration == nil {
return ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
// | ```typescript
// | (property) language?: string | undefined
// | ```
// |
// | A language id, like `typescript`.
// | ----------------------------------------------------------------------
[
{
Expand All @@ -46,7 +46,7 @@
"item": {
"contents": {
"kind": "markdown",
"value": "```typescript\n(property) language?: string | undefined\n```\n"
"value": "```typescript\n(property) language?: string | undefined\n```\nA language id, like `typescript`."
},
"range": {
"start": {
Expand Down
Loading