Skip to content

Handle completionItem/resolve #1384

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jul 10, 2025
Merged
23 changes: 23 additions & 0 deletions internal/fourslash/_scripts/convertFourslash.mts
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,29 @@ function parseExpectedCompletionItem(expr: ts.Expression): string | undefined {
}
({ isDeprecated, isOptional, extensions } = modifiers);
break;
case "text":
if (ts.isStringLiteralLike(init)) {
itemProps.push(`Detail: ptrTo(${getGoStringLiteral(init.text)}),`);
}
else {
console.error(`Expected string literal for text, got ${init.getText()}`);
return undefined;
}
break;
case "documentation":
if (ts.isStringLiteral(init)) {
itemProps.push(`Documentation: &lsproto.StringOrMarkupContent{
MarkupContent: &lsproto.MarkupContent{
Kind: lsproto.MarkupKindMarkdown,
Value: ${getGoStringLiteral(init.text)},
},
},`);
}
else {
console.error(`Expected string literal for documentation, got ${init.getText()}`);
return undefined;
}
break;
case "commitCharacters":
case "replacementSpan":
// !!! support these later
Expand Down
47 changes: 47 additions & 0 deletions internal/fourslash/_scripts/failingTests.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
TestAugmentedTypesClass1
TestAutoImportsWithRootDirsAndRootedPath01
TestClosedCommentsInConstructor
TestCodeCompletionEscaping
TestCompletionAfterQuestionDot
TestCompletionAutoInsertQuestionDot
TestCompletionCloneQuestionToken
TestCompletionEntryForArgumentConstrainedToString
TestCompletionEntryForArrayElementConstrainedToString
TestCompletionEntryForArrayElementConstrainedToString2
TestCompletionEntryForClassMembers_StaticWhenBaseTypeIsNotResolved
TestCompletionEntryForPropertyFromUnionOfModuleType
TestCompletionEntryForUnionProperty
TestCompletionEntryForUnionProperty2
TestCompletionExportFrom
TestCompletionForComputedStringProperties
TestCompletionForMetaProperty
TestCompletionForStringLiteral
TestCompletionForStringLiteral12
TestCompletionForStringLiteral15
Expand Down Expand Up @@ -35,6 +42,7 @@ TestCompletionForStringLiteralRelativeImport4
TestCompletionForStringLiteralRelativeImport6
TestCompletionForStringLiteralRelativeImportAllowJSTrue
TestCompletionForStringLiteralWithDynamicImport
TestCompletionForStringLiteral_details
TestCompletionForStringLiteral_quotePreference
TestCompletionForStringLiteral_quotePreference1
TestCompletionForStringLiteral_quotePreference2
Expand All @@ -55,20 +63,26 @@ TestCompletionImportModuleSpecifierEndingTsxReact
TestCompletionImportModuleSpecifierEndingUnsupportedExtension
TestCompletionInFunctionLikeBody_includesPrimitiveTypes
TestCompletionInJsDoc
TestCompletionInJsDocQualifiedNames
TestCompletionInNamedImportLocation
TestCompletionInfoWithExplicitTypeArguments
TestCompletionListAndMemberListOnCommentedDot
TestCompletionListAndMemberListOnCommentedLine
TestCompletionListAndMemberListOnCommentedWhiteSpace
TestCompletionListAtEndOfWordInArrowFunction03
TestCompletionListAtInvalidLocations
TestCompletionListBuilderLocations_VariableDeclarations
TestCompletionListCladule
TestCompletionListClassMembers
TestCompletionListForDerivedType1
TestCompletionListForExportEquals
TestCompletionListForRest
TestCompletionListForTransitivelyExportedMembers01
TestCompletionListForTransitivelyExportedMembers04
TestCompletionListFunctionExpression
TestCompletionListFunctionMembers
TestCompletionListInArrowFunctionInUnclosedCallSite01
TestCompletionListInClassExpressionWithTypeParameter
TestCompletionListInClassStaticBlocks
TestCompletionListInClosedFunction05
TestCompletionListInComments
Expand All @@ -78,6 +92,10 @@ TestCompletionListInExtendsClause
TestCompletionListInImportClause01
TestCompletionListInImportClause05
TestCompletionListInImportClause06
TestCompletionListInNamedClassExpression
TestCompletionListInNamedClassExpressionWithShadowing
TestCompletionListInNamedFunctionExpression1
TestCompletionListInNamedFunctionExpressionWithShadowing
TestCompletionListInScope
TestCompletionListInTemplateLiteralParts1
TestCompletionListInUnclosedCommaExpression01
Expand All @@ -90,6 +108,10 @@ TestCompletionListInUnclosedTemplate01
TestCompletionListInUnclosedTemplate02
TestCompletionListInvalidMemberNames2
TestCompletionListInvalidMemberNames_withExistingIdentifier
TestCompletionListObjectMembers
TestCompletionListObjectMembersInTypeLocationWithTypeof
TestCompletionListOfGenericSymbol
TestCompletionListOnAliases
TestCompletionListOnAliases2
TestCompletionListPrivateNames
TestCompletionListPrivateNamesAccessors
Expand All @@ -102,13 +124,20 @@ TestCompletionListStringParenthesizedExpression
TestCompletionListStringParenthesizedType
TestCompletionListWithoutVariableinitializer
TestCompletionListsStringLiteralTypeAsIndexedAccessTypeObject
TestCompletionNoAutoInsertQuestionDotForTypeParameter
TestCompletionNoAutoInsertQuestionDotWithUserPreferencesOff
TestCompletionOfAwaitPromise6
TestCompletionOfInterfaceAndVar
TestCompletionPreferredSuggestions1
TestCompletionWithConditionalOperatorMissingColon
TestCompletionWithDotFollowedByNamespaceKeyword
TestCompletionsAfterJSDoc
TestCompletionsAugmentedTypesClass2
TestCompletionsBeforeRestArg1
TestCompletionsDefaultExport
TestCompletionsECMAPrivateMemberTriggerCharacter
TestCompletionsExportImport
TestCompletionsGenericTypeWithMultipleBases1
TestCompletionsImport_computedSymbolName
TestCompletionsInRequire
TestCompletionsInterfaceElement
Expand All @@ -119,10 +148,13 @@ TestCompletionsJSDocNoCrash1
TestCompletionsJSDocNoCrash2
TestCompletionsJsPropertyAssignment
TestCompletionsJsdocParamTypeBeforeName
TestCompletionsJsdocTag
TestCompletionsJsdocTypeTagCast
TestCompletionsJsxAttribute2
TestCompletionsJsxAttributeInitializer2
TestCompletionsKeyof
TestCompletionsLiteralFromInferenceWithinInferredType3
TestCompletionsLiterals
TestCompletionsMergedDeclarations1
TestCompletionsNamespaceMergedWithClass
TestCompletionsNamespaceName
Expand Down Expand Up @@ -151,8 +183,12 @@ TestCompletionsStringLiteral_fromTypeConstraint
TestCompletionsStringsWithTriggerCharacter
TestCompletionsSymbolMembers
TestCompletionsTriggerCharacter
TestCompletionsTypeOnlyNamespace
TestCompletionsUniqueSymbol1
TestCompletionsWithStringReplacementMode1
TestDoubleUnderscoreCompletions
TestExportDefaultClass
TestExportDefaultFunction
TestExportEqualCallableInterface
TestFindAllReferencesTripleSlash
TestFindAllReferencesUmdModuleAsGlobalConst
Expand All @@ -166,6 +202,7 @@ TestFindAllRefsModuleDotExports
TestFindAllRefsReExport_broken
TestFindAllRefs_importType_typeofImport
TestFindReferencesAfterEdit
TestGenericTypeWithMultipleBases1MultiFile
TestGetJavaScriptCompletions1
TestGetJavaScriptCompletions10
TestGetJavaScriptCompletions11
Expand Down Expand Up @@ -239,9 +276,17 @@ TestJsxAriaLikeCompletions
TestJsxFindAllReferencesOnRuntimeImportWithPaths1
TestJsxQualifiedTagCompletion
TestLocalGetReferences
TestMemberCompletionOnTypeParameters
TestMemberListErrorRecovery
TestMemberListInReopenedEnum
TestMemberListInWithBlock
TestMemberListOfClass
TestMemberListOfExportedClass
TestMemberListOfModuleAfterInvalidCharater
TestMemberListOnConstructorType
TestMemberListOnExplicitThis
TestMemberListOnThisInClassWithPrivates
TestModuleMembersOfGenericType
TestNoCompletionListOnCommentsInsideObjectLiterals
TestNodeModulesImportCompletions1
TestPathCompletionsAllowModuleAugmentationExtensions
Expand Down Expand Up @@ -292,11 +337,13 @@ TestPathCompletionsTypesVersionsWildcard3
TestPathCompletionsTypesVersionsWildcard4
TestPathCompletionsTypesVersionsWildcard5
TestPathCompletionsTypesVersionsWildcard6
TestProtoVarVisibleWithOuterScopeUnderscoreProto
TestReferencesForExportedValues
TestReferencesForStatementKeywords
TestReferencesInComment
TestReferencesInEmptyFile
TestReferencesIsAvailableThroughGlobalNoCrash
TestSelfReferencedExternalModule
TestStringCompletionsImportOrExportSpecifier
TestStringCompletionsVsEscaping
TestStringLiteralTypeCompletionsInTypeArgForNonGeneric1
Expand Down
52 changes: 32 additions & 20 deletions internal/fourslash/fourslash.go
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ func (f *FourslashTest) verifyCompletionsResult(
}
assert.Equal(t, actual.IsIncomplete, expected.IsIncomplete, prefix+"IsIncomplete mismatch")
verifyCompletionsItemDefaults(t, actual.ItemDefaults, expected.ItemDefaults, prefix+"ItemDefaults mismatch: ")
verifyCompletionsItems(t, prefix, actual.Items, expected.Items)
f.verifyCompletionsItems(t, prefix, actual.Items, expected.Items)
}

func isEmptyExpectedList(expected *CompletionsExpectedList) bool {
Expand Down Expand Up @@ -616,7 +616,7 @@ func verifyCompletionsItemDefaults(t *testing.T, actual *lsproto.CompletionItemD
}
}

func verifyCompletionsItems(t *testing.T, prefix string, actual []*lsproto.CompletionItem, expected *CompletionsExpectedItems) {
func (f *FourslashTest) verifyCompletionsItems(t *testing.T, prefix string, actual []*lsproto.CompletionItem, expected *CompletionsExpectedItems) {
if expected.Exact != nil {
if expected.Includes != nil {
t.Fatal(prefix + "Expected exact completion list but also specified 'includes'.")
Expand All @@ -631,7 +631,7 @@ func verifyCompletionsItems(t *testing.T, prefix string, actual []*lsproto.Compl
t.Fatalf(prefix+"Expected %d exact completion items but got %d: %s", len(expected.Exact), len(actual), cmp.Diff(actual, expected.Exact))
}
if len(actual) > 0 {
verifyCompletionsAreExactly(t, prefix, actual, expected.Exact)
f.verifyCompletionsAreExactly(t, prefix, actual, expected.Exact)
}
return
}
Expand Down Expand Up @@ -660,7 +660,7 @@ func verifyCompletionsItems(t *testing.T, prefix string, actual []*lsproto.Compl
t.Fatalf("%sLabel '%s' not found in actual items. Actual items: %s", prefix, item.Label, cmp.Diff(actual, nil))
}
delete(nameToActualItem, item.Label)
verifyCompletionItem(t, prefix+"Includes completion item mismatch for label "+item.Label, actualItem, item)
f.verifyCompletionItem(t, prefix+"Includes completion item mismatch for label "+item.Label, actualItem, item)
default:
t.Fatalf("%sExpected completion item to be a string or *lsproto.CompletionItem, got %T", prefix, item)
}
Expand All @@ -684,7 +684,7 @@ func verifyCompletionsItems(t *testing.T, prefix string, actual []*lsproto.Compl
if !ok {
t.Fatalf("%sLabel '%s' not found in actual items. Actual items: %s", prefix, item.Label, cmp.Diff(actual, nil))
}
verifyCompletionItem(t, prefix+"Includes completion item mismatch for label "+item.Label, actualItem, item)
f.verifyCompletionItem(t, prefix+"Includes completion item mismatch for label "+item.Label, actualItem, item)
default:
t.Fatalf("%sExpected completion item to be a string or *lsproto.CompletionItem, got %T", prefix, item)
}
Expand All @@ -697,7 +697,7 @@ func verifyCompletionsItems(t *testing.T, prefix string, actual []*lsproto.Compl
}
}

func verifyCompletionsAreExactly(t *testing.T, prefix string, actual []*lsproto.CompletionItem, expected []CompletionsExpectedItem) {
func (f *FourslashTest) verifyCompletionsAreExactly(t *testing.T, prefix string, actual []*lsproto.CompletionItem, expected []CompletionsExpectedItem) {
// Verify labels first
assertDeepEqual(t, core.Map(actual, func(item *lsproto.CompletionItem) string {
return item.Label
Expand All @@ -709,24 +709,36 @@ func verifyCompletionsAreExactly(t *testing.T, prefix string, actual []*lsproto.
case string:
continue // already checked labels
case *lsproto.CompletionItem:
verifyCompletionItem(t, prefix+"Completion item mismatch for label "+actualItem.Label, actualItem, expectedItem)
f.verifyCompletionItem(t, prefix+"Completion item mismatch for label "+actualItem.Label, actualItem, expectedItem)
}
}
}

func verifyCompletionItem(t *testing.T, prefix string, actual *lsproto.CompletionItem, expected *lsproto.CompletionItem) {
ignoreKind := cmp.FilterPath(
func(p cmp.Path) bool {
switch p.Last().String() {
case ".Kind", ".SortText":
return true
default:
return false
}
},
cmp.Ignore(),
)
assertDeepEqual(t, actual, expected, prefix, ignoreKind)
var completionIgnoreOpts = cmp.FilterPath(
func(p cmp.Path) bool {
switch p.Last().String() {
case ".Kind", ".SortText", ".Data":
return true
default:
return false
}
},
cmp.Ignore(),
)

func (f *FourslashTest) verifyCompletionItem(t *testing.T, prefix string, actual *lsproto.CompletionItem, expected *lsproto.CompletionItem) {
if expected.Detail != nil || expected.Documentation != nil {
response := f.sendRequest(t, lsproto.MethodCompletionItemResolve, actual)
if response == nil {
t.Fatal(prefix + "Expected non-nil response for completion item resolve, got nil")
}
resolvedItem, ok := response.AsResponse().Result.(*lsproto.CompletionItem)
if !ok {
t.Fatalf(prefix+"Expected response to be *lsproto.CompletionItem, got %T", response.AsResponse().Result)
}
actual = resolvedItem
}
assertDeepEqual(t, actual, expected, prefix, completionIgnoreOpts)
if expected.Kind != nil {
assertDeepEqual(t, actual.Kind, expected.Kind, prefix+" Kind mismatch")
}
Expand Down
66 changes: 66 additions & 0 deletions internal/fourslash/tests/completionsInJsxTag_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package fourslash_test

import (
"testing"

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

func TestCompletionsInJsxTag(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @jsx: preserve
// @Filename: /a.tsx
declare namespace JSX {
interface Element {}
interface IntrinsicElements {
div: {
/** Doc */
foo: string
/** Label docs */
"aria-label": string
}
}
}
class Foo {
render() {
<div /*1*/ ></div>;
<div /*2*/ />
}
}`
f := fourslash.NewFourslash(t, nil /*capabilities*/, content)
f.VerifyCompletions(t, []string{"1", "2"}, &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &defaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Exact: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "aria-label",
Kind: ptrTo(lsproto.CompletionItemKindField),
Detail: ptrTo("(property) \"aria-label\": string"),
Documentation: &lsproto.StringOrMarkupContent{
MarkupContent: &lsproto.MarkupContent{
Kind: lsproto.MarkupKindMarkdown,
Value: "Label docs",
},
},
},
&lsproto.CompletionItem{
Label: "foo",
Kind: ptrTo(lsproto.CompletionItemKindField),
Detail: ptrTo("(property) foo: string"),
Documentation: &lsproto.StringOrMarkupContent{
MarkupContent: &lsproto.MarkupContent{
Kind: lsproto.MarkupKindMarkdown,
Value: "Doc",
},
},
},
},
},
})
}
Loading
Loading