From d81374c498f750d42974a7b26092bcab118a15c0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Jun 2026 17:12:23 +0000 Subject: [PATCH 1/4] Initial plan From 5c40d1f0c6670c3de02a4d3925989d79d4a3cbef Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Jun 2026 17:31:26 +0000 Subject: [PATCH 2/4] Fix JSX namespaced intrinsic LSP Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- .../tests/completionsInJsxTag_test.go | 56 +++++++++++++++++++ .../quickInfoJsxNamespacedIntrinsic_test.go | 33 +++++++++++ internal/ls/completions.go | 5 +- internal/ls/hover.go | 2 +- 4 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 internal/fourslash/tests/quickInfoJsxNamespacedIntrinsic_test.go diff --git a/internal/fourslash/tests/completionsInJsxTag_test.go b/internal/fourslash/tests/completionsInJsxTag_test.go index 8dc79999112..18f5ff820a3 100644 --- a/internal/fourslash/tests/completionsInJsxTag_test.go +++ b/internal/fourslash/tests/completionsInJsxTag_test.go @@ -66,3 +66,59 @@ class Foo { }, }) } + +func TestCompletionsInJsxNamespacedIntrinsicTag(t *testing.T) { + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `// @jsx: react +// @Filename: /a.tsx +declare const React: any; +declare namespace JSX { + interface Element {} + interface IntrinsicElements { + /** Element docs */ + "foo:bar": { + /** Foo docs */ + foo: boolean + /** Bar docs */ + bar: string + } + } +} + +` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifyCompletions(t, []string{"1", "2"}, &fourslash.CompletionsExpectedList{ + IsIncomplete: false, + ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{ + CommitCharacters: &DefaultCommitCharacters, + }, + Items: &fourslash.CompletionsExpectedItems{ + Exact: []fourslash.CompletionsExpectedItem{ + &lsproto.CompletionItem{ + Label: "bar", + Kind: new(lsproto.CompletionItemKindField), + Detail: new("(property) bar: string"), + Documentation: &lsproto.StringOrMarkupContent{ + MarkupContent: &lsproto.MarkupContent{ + Kind: lsproto.MarkupKindMarkdown, + Value: "Bar docs", + }, + }, + }, + &lsproto.CompletionItem{ + Label: "foo", + Kind: new(lsproto.CompletionItemKindField), + Detail: new("(property) foo: boolean"), + Documentation: &lsproto.StringOrMarkupContent{ + MarkupContent: &lsproto.MarkupContent{ + Kind: lsproto.MarkupKindMarkdown, + Value: "Foo docs", + }, + }, + }, + }, + }, + }) +} diff --git a/internal/fourslash/tests/quickInfoJsxNamespacedIntrinsic_test.go b/internal/fourslash/tests/quickInfoJsxNamespacedIntrinsic_test.go new file mode 100644 index 00000000000..cf74c564ab3 --- /dev/null +++ b/internal/fourslash/tests/quickInfoJsxNamespacedIntrinsic_test.go @@ -0,0 +1,33 @@ +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestQuickInfoJsxNamespacedIntrinsic(t *testing.T) { + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `// @jsx: react +// @Filename: /a.tsx +declare const React: any; +declare namespace JSX { + interface Element {} + interface IntrinsicElements { + /** Element docs */ + "foo:bar": { + /** Foo docs */ + foo: boolean + /** Bar docs */ + bar: string + } + } +} +` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifyQuickInfoAt(t, "tag", "(property) JSX.IntrinsicElements[\"foo:bar\"]: {\n foo: boolean;\n bar: string;\n}", "Element docs") + f.VerifyQuickInfoAt(t, "attr", "(property) foo: boolean", "Foo docs") +} diff --git a/internal/ls/completions.go b/internal/ls/completions.go index 5eb40157301..a68776a7273 100644 --- a/internal/ls/completions.go +++ b/internal/ls/completions.go @@ -4153,7 +4153,7 @@ func tryGetContainingJsxElement(contextToken *ast.Node, file *ast.SourceFile) *a parent := contextToken.Parent switch contextToken.Kind { case ast.KindGreaterThanToken, ast.KindLessThanSlashToken, ast.KindSlashToken, ast.KindIdentifier, - ast.KindPropertyAccessExpression, ast.KindJsxAttributes, ast.KindJsxAttribute, ast.KindJsxSpreadAttribute: + ast.KindPropertyAccessExpression, ast.KindJsxNamespacedName, ast.KindJsxAttributes, ast.KindJsxAttribute, ast.KindJsxSpreadAttribute: if parent != nil && (parent.Kind == ast.KindJsxSelfClosingElement || parent.Kind == ast.KindJsxOpeningElement) { if contextToken.Kind == ast.KindGreaterThanToken { precedingToken := astnav.FindPrecedingToken(file, contextToken.Pos()) @@ -4163,6 +4163,9 @@ func tryGetContainingJsxElement(contextToken *ast.Node, file *ast.SourceFile) *a } } return parent + } else if parent != nil && ast.IsJsxNamespacedName(parent) && + parent.Parent != nil && (parent.Parent.Kind == ast.KindJsxSelfClosingElement || parent.Parent.Kind == ast.KindJsxOpeningElement) { + return parent.Parent } else if parent != nil && parent.Kind == ast.KindJsxAttribute { // Currently we parse JsxOpeningLikeElement as: // JsxOpeningLikeElement diff --git a/internal/ls/hover.go b/internal/ls/hover.go index c7dbfb80b7a..da4f11883c0 100644 --- a/internal/ls/hover.go +++ b/internal/ls/hover.go @@ -41,7 +41,7 @@ func (l *LanguageService) ProvideHover(ctx context.Context, params *lsproto.Hove c, done := program.GetTypeCheckerForFile(ctx, file) defer done() rangeNode := getNodeForQuickInfo(node) - symbol := getSymbolAtLocationForQuickInfo(c, node) + symbol := getSymbolAtLocationForQuickInfo(c, rangeNode) // Always create VerbosityContext for hover so that canExpandSymbol can signal // canIncreaseVerbosity even at Level 0. The nodebuilder also detects expandable From b0df3ab1436eab1a3ce24eb799f5e4b05d17909a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Jun 2026 17:35:20 +0000 Subject: [PATCH 3/4] Update JSX quick info baselines Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- .../quickInfo/quickInfoImportMeta.baseline | 7 +++-- .../quickInfoOnJsxNamespacedName.baseline | 26 ++++++++++++++++--- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/testdata/baselines/reference/fourslash/quickInfo/quickInfoImportMeta.baseline b/testdata/baselines/reference/fourslash/quickInfo/quickInfoImportMeta.baseline index 9e138c380a5..31230b9447f 100644 --- a/testdata/baselines/reference/fourslash/quickInfo/quickInfoImportMeta.baseline +++ b/testdata/baselines/reference/fourslash/quickInfo/quickInfoImportMeta.baseline @@ -9,9 +9,12 @@ // ^^^^^^^^^^^ // | ---------------------------------------------------------------------- // | ```typescript -// | (property) ImportMetaExpression.meta: ImportMeta +// | interface ImportMeta // | ``` +// | The type of `import.meta`. // | +// | If you need to declare that a given property exists on `import.meta`, +// | this type may be augmented via interface merging. // | ---------------------------------------------------------------------- [ { @@ -39,7 +42,7 @@ "item": { "contents": { "kind": "markdown", - "value": "```typescript\n(property) ImportMetaExpression.meta: ImportMeta\n```\n" + "value": "```typescript\ninterface ImportMeta\n```\nThe type of `import.meta`.\n\nIf you need to declare that a given property exists on `import.meta`,\nthis type may be augmented via interface merging." }, "range": { "start": { diff --git a/testdata/baselines/reference/fourslash/quickInfo/quickInfoOnJsxNamespacedName.baseline b/testdata/baselines/reference/fourslash/quickInfo/quickInfoOnJsxNamespacedName.baseline index 8dbf51c068d..d130f3a1b1e 100644 --- a/testdata/baselines/reference/fourslash/quickInfo/quickInfoOnJsxNamespacedName.baseline +++ b/testdata/baselines/reference/fourslash/quickInfo/quickInfoOnJsxNamespacedName.baseline @@ -1,9 +1,14 @@ // === QuickInfo === === /a.tsx === // ; -// ^ +// ^^^ // | ---------------------------------------------------------------------- -// | No quickinfo at /**/. +// | ```typescript +// | (property) JSX.IntrinsicElements['a:b']: { +// | a: string; +// | } +// | ``` +// | // | ---------------------------------------------------------------------- [ { @@ -16,6 +21,21 @@ "Name": "", "Data": {} }, - "item": null + "item": { + "contents": { + "kind": "markdown", + "value": "```typescript\n(property) JSX.IntrinsicElements['a:b']: {\n a: string;\n}\n```\n" + }, + "range": { + "start": { + "line": 0, + "character": 1 + }, + "end": { + "line": 0, + "character": 4 + } + } + } } ] \ No newline at end of file From 344cd1c7e052827abfb45565f326d0d416ad3364 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Jun 2026 19:45:02 +0000 Subject: [PATCH 4/4] Remove TestQuickInfoOnNewKeyword01 from failingTests.txt The hover.go fix (using rangeNode for symbol lookup) now makes TestQuickInfoOnNewKeyword01 pass, so it no longer needs to be listed as a failing test. Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/fourslash/_scripts/failingTests.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/fourslash/_scripts/failingTests.txt b/internal/fourslash/_scripts/failingTests.txt index 6b5b6c602c4..21b8504355d 100644 --- a/internal/fourslash/_scripts/failingTests.txt +++ b/internal/fourslash/_scripts/failingTests.txt @@ -386,7 +386,6 @@ TestQuickInfoOnInternalAliases TestQuickInfoOnJsxNamespacedNameWithDoc1 TestQuickInfoOnMergedModule TestQuickInfoOnNarrowedTypeInModule -TestQuickInfoOnNewKeyword01 TestQuickInfoOnObjectLiteralWithAccessors TestQuickInfoOnObjectLiteralWithOnlyGetter TestQuickInfoOnObjectLiteralWithOnlySetter