vendor tsgo

This commit is contained in:
2026-07-09 16:50:43 -04:00
parent c06ea2e5a4
commit 98978e4930
5804 changed files with 1556156 additions and 101 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,459 @@
TestAliasMergingWithNamespace
TestAllowLateBoundSymbolsOverwriteEarlyBoundSymbols
TestAmbientShorthandGotoDefinition
TestArgumentsAreAvailableAfterEditsAtEndOfFunction
TestAugmentedTypesClass1
TestAutoImportAllowImportingTsExtensionsPackageJsonImports1
TestAutoImportCompletionAmbientMergedModule1
TestAutoImportCompletionExportListAugmentation1
TestAutoImportCompletionExportListAugmentation2
TestAutoImportCompletionExportListAugmentation3
TestAutoImportCompletionExportListAugmentation4
TestAutoImportCrossProject_symlinks_stripSrc
TestAutoImportCrossProject_symlinks_toDist
TestAutoImportFileExcludePatterns10
TestAutoImportFileExcludePatterns11
TestAutoImportFileExcludePatterns3
TestAutoImportFileExcludePatterns5
TestAutoImportFileExcludePatterns6
TestAutoImportFileExcludePatterns9
TestAutoImportJsDocImport1
TestAutoImportModuleNone2
TestAutoImportNodeModuleSymlinkRenamed
TestAutoImportNodeNextJSRequire
TestAutoImportPackageJsonImportsCaseSensitivity
TestAutoImportPackageRootPath
TestAutoImportPathsNodeModules
TestAutoImportProvider_exportMap2
TestAutoImportProvider_exportMap9
TestAutoImportProvider_globalTypingsCache
TestAutoImportProvider9
TestAutoImportRelativePathToMonorepoPackage
TestAutoImportSortCaseSensitivity1
TestAutoImportTypeImport4
TestAutoImportTypeOnlyPreferred3
TestAutoImportVerbatimTypeOnly1
TestCloduleTypeOf1
TestCodeCompletionEscaping
TestCodeFixAddParameterNames1
TestCodeFixAddParameterNames2
TestCodeFixAddParameterNames3
TestCodeFixClassExtendAbstractSomePropertiesPresent
TestCodeFixClassImplementInterfaceNoTruncation
TestCodeFixClassSuperMustPrecedeThisAccess
TestCodeFixCorrectQualifiedNameToIndexedAccessType01
TestCodeFixInferFromCallInAssignment
TestCodeFixInferFromExpressionStatement
TestCodeFixInferFromFunctionThisUsageObjectProperty
TestCodeFixInferFromFunctionThisUsageObjectPropertyParameter
TestCodeFixInferFromFunctionThisUsageObjectPropertyShorthand
TestCodeFixInferFromFunctionThisUsageObjectPropertyShorthandParameter
TestCodeFixInferFromFunctionUsage
TestCodeFixInferFromPrimitiveUsage
TestCodeFixInferFromUsageCall
TestCodeFixInferFromUsageMember
TestCodeFixInferFromUsageMember2
TestCodeFixInferFromUsageMember3
TestCodefixInferFromUsageNullish
TestCodeFixInferFromUsageOptionalParam
TestCodeFixInferFromUsageOptionalParam2
TestCodeFixInferFromUsageRestParam
TestCodeFixInferFromUsageRestParam2
TestCodeFixInferFromUsageRestParam3
TestCodeFixInferFromUsageVariable3JS
TestCodeFixMissingTypeAnnotationOnExports16
TestCodeFixMissingTypeAnnotationOnExports23_heritage_formatting
TestCodeFixMissingTypeAnnotationOnExports25_heritage_formatting_3
TestCodeFixMissingTypeAnnotationOnExports28_long_types
TestCodeFixSpelling4
TestCodeFixSpelling5
TestCodeFixSpellingCaseSensitive1
TestCodeFixSpellingCaseSensitive2
TestCodeFixSpellingCaseSensitive3
TestCodeFixSpellingCaseWeight1
TestCodeFixSpellingCaseWeight2
TestCodeFixSpellingShortName1
TestCodeFixTopLevelAwait_module_targetES2017CompilerOptionsInTsConfig
TestCodeFixTopLevelForAwait_module_targetES2017CompilerOptionsInTsConfig
TestCodeFixUndeclaredPropertyAccesses
TestCodeFixUnusedIdentifier_suggestion
TestCodeFixUnusedInterfaceInNamespace1
TestCodeFixUnusedInterfaceInNamespace2
TestCommentsEnumsFourslash
TestCommentsExternalModulesFourslash
TestCommentsImportDeclaration
TestCommentsInheritanceFourslash
TestCommentsInterfaceFourslash
TestCommentsLinePreservation
TestCommentsOverloadsFourslash
TestCommentsVariables
TestCompletionAfterQuestionDot
TestCompletionAutoInsertQuestionDot
TestCompletionCloneQuestionToken
TestCompletionEntryClassMembersWithInferredFunctionReturnType1
TestCompletionEntryForArgumentConstrainedToString
TestCompletionEntryForArrayElementConstrainedToString
TestCompletionEntryForArrayElementConstrainedToString2
TestCompletionEntryForUnionProperty
TestCompletionEntryForUnionProperty2
TestCompletionForComputedStringProperties
TestCompletionForMetaProperty
TestCompletionForObjectProperty
TestCompletionForStringLiteral
TestCompletionForStringLiteral_quotePreference
TestCompletionForStringLiteral_quotePreference1
TestCompletionForStringLiteral_quotePreference2
TestCompletionForStringLiteral_quotePreference3
TestCompletionForStringLiteral_quotePreference4
TestCompletionForStringLiteral_quotePreference5
TestCompletionForStringLiteral_quotePreference6
TestCompletionForStringLiteral_quotePreference7
TestCompletionForStringLiteral_quotePreference8
TestCompletionForStringLiteral4
TestCompletionImportMeta
TestCompletionImportMetaWithGlobalDeclaration
TestCompletionImportModuleSpecifierEndingUnsupportedExtension
TestCompletionInChecks1
TestCompletionInFunctionLikeBody_includesPrimitiveTypes
TestCompletionInUncheckedJSFile
TestCompletionListBuilderLocations_VariableDeclarations
TestCompletionListForDerivedType1
TestCompletionListInArrowFunctionInUnclosedCallSite01
TestCompletionListInNamedFunctionExpression
TestCompletionListInNamedFunctionExpression1
TestCompletionListInNamedFunctionExpressionWithShadowing
TestCompletionListInScope
TestCompletionListInScope_doesNotIncludeAugmentations
TestCompletionListInTemplateLiteralParts1
TestCompletionListInUnclosedCommaExpression01
TestCompletionListInUnclosedCommaExpression02
TestCompletionListInUnclosedFunction08
TestCompletionListInUnclosedFunction09
TestCompletionListInUnclosedTaggedTemplate01
TestCompletionListInUnclosedTaggedTemplate02
TestCompletionListInUnclosedTemplate01
TestCompletionListInUnclosedTemplate02
TestCompletionListInvalidMemberNames
TestCompletionListInvalidMemberNames_escapeQuote
TestCompletionListInvalidMemberNames_startWithSpace
TestCompletionListInvalidMemberNames_withExistingIdentifier
TestCompletionListInvalidMemberNames2
TestCompletionListOnAliases
TestCompletionListsStringLiteralTypeAsIndexedAccessTypeObject
TestCompletionListStringParenthesizedExpression
TestCompletionListStringParenthesizedType
TestCompletionListWithoutVariableinitializer
TestCompletionNoAutoInsertQuestionDotForThis
TestCompletionNoAutoInsertQuestionDotForTypeParameter
TestCompletionNoAutoInsertQuestionDotWithUserPreferencesOff
TestCompletionOfAwaitPromise1
TestCompletionOfAwaitPromise2
TestCompletionOfAwaitPromise3
TestCompletionOfAwaitPromise5
TestCompletionOfAwaitPromise6
TestCompletionOfAwaitPromise7
TestCompletionOfInterfaceAndVar
TestCompletionPreferredSuggestions1
TestCompletionsBeforeRestArg1
TestCompletionsClassMemberImportTypeNodeParameter1
TestCompletionsClassMemberImportTypeNodeParameter2
TestCompletionsClassMemberImportTypeNodeParameter3
TestCompletionsClassMemberImportTypeNodeParameter4
TestCompletionsElementAccessNumeric
TestCompletionsExportImport
TestCompletionsImport_default_anonymous
TestCompletionsImport_details_withMisspelledName
TestCompletionsImport_exportEquals_global
TestCompletionsImport_filteredByInvalidPackageJson_direct
TestCompletionsImport_filteredByPackageJson_direct
TestCompletionsImport_filteredByPackageJson_nested
TestCompletionsImport_filteredByPackageJson_peerDependencies
TestCompletionsImport_filteredByPackageJson_typesImplicit
TestCompletionsImport_filteredByPackageJson_typesOnly
TestCompletionsImport_named_didNotExistBefore
TestCompletionsImport_noSemicolons
TestCompletionsImport_quoteStyle
TestCompletionsImport_umdDefaultNoCrash1
TestCompletionsImport_uriStyleNodeCoreModules2
TestCompletionsImport_uriStyleNodeCoreModules3
TestCompletionsImport_windowsPathsProjectRelative
TestCompletionsImportOrExportSpecifier
TestCompletionsInExport
TestCompletionsInExport_moduleBlock
TestCompletionsJSDocNoCrash1
TestCompletionsJsdocTypeTagCast
TestCompletionsJsxAttributeInitializer2
TestCompletionsLiteralFromInferenceWithinInferredType3
TestCompletionsLiterals
TestCompletionsMergedDeclarations1
TestCompletionsNewTarget
TestCompletionsOptionalMethod
TestCompletionsOverridingMethod1
TestCompletionsOverridingMethod10
TestCompletionsOverridingMethod11
TestCompletionsOverridingMethod14
TestCompletionsOverridingMethod17
TestCompletionsOverridingMethod2
TestCompletionsOverridingMethod3
TestCompletionsOverridingMethod4
TestCompletionsOverridingMethod9
TestCompletionsOverridingMethodCrash1
TestCompletionsOverridingProperties1
TestCompletionsRecommended_namespace
TestCompletionsRecommended_union
TestCompletionsRedeclareModuleAsGlobal
TestCompletionsStringsWithTriggerCharacter
TestCompletionsSymbolMembers
TestCompletionsTriggerCharacter
TestCompletionsTuple
TestCompletionsUniqueSymbol_import
TestCompletionsUniqueSymbol1
TestCompletionsWithDeprecatedTag10
TestConstQuickInfoAndCompletionList
TestContextuallyTypedFunctionExpressionGeneric1
TestContextualTypingOfGenericCallSignatures2
TestDocCommentTemplateClassDecl01
TestDocCommentTemplateClassDeclMethods01
TestDocCommentTemplateFunctionExpression
TestDocCommentTemplateIndentation
TestDocCommentTemplateJsSpecialPropertyAssignment
TestDocCommentTemplatePrototypeMethod
TestDocCommentTemplateWithMultipleJSDoc1
TestDocCommentTemplateWithMultipleJSDoc3
TestDocCommentTemplateWithMultipleJSDocAndParameters
TestDoubleUnderscoreCompletions
TestEditJsdocType
TestErrorsAfterResolvingVariableDeclOfMergedVariableAndClassDecl
TestExportDefaultClass
TestExportDefaultFunction
TestExportEqualTypes
TestFindAllRefs_importType_js1
TestFindAllRefs_importType_js2
TestFindAllRefs_importType_js3
TestFormatDotAfterNumber
TestFormatNoSpaceAfterTemplateHeadAndMiddle
TestFormatSpaceAfterTemplateHeadAndMiddle
TestFormattingObjectLiteralOpenCurlyNewlineTyping
TestFormattingObjectLiteralOpenCurlySingleLine
TestFormattingOnInvalidCodes
TestFormattingOnObjectLiteral
TestFormatV8Directive
TestFunduleWithRecursiveReference
TestGenericCombinators3
TestGenericCombinatorWithConstraints1
TestGenericFunctionWithGenericParams1
TestGetJavaScriptCompletions10
TestGetJavaScriptCompletions12
TestGetJavaScriptCompletions13
TestGetJavaScriptCompletions15
TestGetJavaScriptCompletions20
TestGetJavaScriptCompletions8
TestGetJavaScriptCompletions9
TestGetJavaScriptGlobalCompletions1
TestGetJavaScriptQuickInfo1
TestGetJavaScriptQuickInfo2
TestGetJavaScriptQuickInfo3
TestGetJavaScriptQuickInfo4
TestGetJavaScriptQuickInfo5
TestGetJavaScriptQuickInfo6
TestGetJavaScriptQuickInfo7
TestGetJavaScriptSyntacticDiagnostics24
TestGetJSXOutliningSpans
TestHoverOverComment
TestImportCompletionsPackageJsonImportsPatternRootWildcard
TestImportFixesGlobalTypingsCache
TestImportMetaCompletionDetails
TestImportNameCodeFix_externalNonRelative1
TestImportNameCodeFix_jsx4
TestImportNameCodeFix_jsx6
TestImportNameCodeFix_noDestructureNonObjectLiteral
TestImportNameCodeFix_order2
TestImportNameCodeFix_preferBaseUrl
TestImportNameCodeFix_reExportDefault
TestImportNameCodeFix_require
TestImportNameCodeFix_require_addToExisting
TestImportNameCodeFix_require_importVsRequire_addToExistingWins
TestImportNameCodeFix_symlink_own_package_2
TestImportNameCodeFix_uriStyleNodeCoreModules2
TestImportNameCodeFix_uriStyleNodeCoreModules3
TestImportNameCodeFixDefaultExport4
TestImportNameCodeFixNewImportBaseUrl0
TestImportNameCodeFixNewImportBaseUrl1
TestImportNameCodeFixNewImportBaseUrl2
TestImportNameCodeFixNewImportFile2
TestImportNameCodeFixNewImportTypeRoots1
TestImportStatementCompletions_esModuleInterop2
TestImportStatementCompletions_noSnippet
TestImportStatementCompletions_pnpm1
TestImportStatementCompletions_quotes
TestImportStatementCompletions_semicolons
TestImportSuggestionsCache_invalidPackageJson
TestImportTypeCompletions1
TestImportTypeCompletions3
TestImportTypeCompletions4
TestImportTypeCompletions6
TestImportTypeCompletions7
TestImportTypeCompletions8
TestImportTypeCompletions9
TestIndirectClassInstantiation
TestJavascriptModules20
TestJavascriptModules24
TestJsDocAugments
TestJsDocAugmentsAndExtends
TestJsdocCallbackTag
TestJsDocExtends
TestJsDocFunctionSignatures10
TestJsDocFunctionSignatures13
TestJsDocFunctionSignatures7
TestJsDocFunctionSignatures8
TestJsDocGenerics2
TestJsDocInheritDoc
TestJsDocPropertyDescription1
TestJsDocPropertyDescription11
TestJsDocPropertyDescription4
TestJsDocPropertyDescription6
TestJsDocPropertyDescription7
TestJsDocPropertyDescription9
TestJsDocTagsWithHyphen
TestJsdocTemplatePrototypeCompletions
TestJsdocTypedefTag
TestJsdocTypedefTagNamespace
TestJsFileImportNoTypes2
TestJsQuickInfoGenerallyAcceptableSize
TestJsRequireQuickInfo
TestJsxAttributeCompletionStyleAuto
TestJsxAttributeCompletionStyleBraces
TestJsxAttributeCompletionStyleDefault
TestJsxAttributeCompletionStyleNone
TestJsxAttributeCompletionStyleNoSnippet
TestJsxAttributeSnippetCompletionAfterTypeArgs
TestJsxAttributeSnippetCompletionClosed
TestJsxAttributeSnippetCompletionUnclosed
TestJsxWithTypeParametershasInstantiatedSignatureHelp
TestLetQuickInfoAndCompletionList
TestLocalFunction
TestMemberListInReopenedEnum
TestMemberListInWithBlock
TestMemberListOfExportedClass
TestMultiModuleFundule
TestNgProxy1
TestNgProxy4
TestNoQuickInfoForLabel
TestNoQuickInfoInWhitespace
TestOverloadQuickInfo
TestProtoVarVisibleWithOuterScopeUnderscoreProto
TestQuickfixImplementInterfaceUnreachableTypeUsesRelativeImport
TestQuickInfo_notInsideComment
TestQuickinfo01
TestQuickInfoBindingPatternInJsdocNoCrash1
TestQuickInfoCloduleWithRecursiveReference
TestQuickInfoContextuallyTypedSignatureOptionalParameterFromIntersection1
TestQuickInfoContextualTyping
TestQuickInfoDisplayPartsIife
TestQuickInfoElementAccessDeclaration
TestQuickInfoForConstTypeReference
TestQuickInfoForContextuallyTypedArrowFunctionInSuperCall
TestQuickInfoForGenericConstraints1
TestQuickInfoForGenericTaggedTemplateExpression
TestQuickInfoForGetterAndSetter
TestQuickInfoForIndexerResultWithConstraint
TestQuickInfoForTypeofParameter
TestQuickInfoForTypeParameterInTypeAlias2
TestQuickInfoForUMDModuleAlias
TestQuickinfoForUnionProperty
TestQuickInfoFromContextualUnionType2
TestQuickInfoFromContextualUnionType3
TestQuickInfoFunctionKeyword
TestQuickInfoGenerics
TestQuickInfoGetterSetter
TestQuickInfoInInvalidIndexSignature
TestQuickInfoInJsdocInTsFile1
TestQuickInfoJSDocBackticks
TestQuickInfoJsdocEnum
TestQuickInfoJSDocFunctionNew
TestQuickInfoJSDocFunctionThis
TestQuickInfoJsDocGetterSetterNoCrash1
TestQuickInfoJsdocTypedefMissingType
TestQuickInfoJSExport
TestQuickInfoMappedSpreadTypes
TestQuickInfoMappedType
TestQuickInfoMeaning
TestQuickInfoModuleVariables
TestQuickInfoNarrowedTypeOfAliasSymbol
TestQuickInfoOnArgumentsInsideFunction
TestQuickInfoOnCatchVariable
TestQuickInfoOnClosingJsx
TestQuickInfoOnElementAccessInWriteLocation4
TestQuickInfoOnElementAccessInWriteLocation5
TestQuickInfoOnExpandoLikePropertyWithSetterDeclarationJs1
TestQuickInfoOnExpandoLikePropertyWithSetterDeclarationJs2
TestQuickInfoOnGenericWithConstraints1
TestQuickInfoOnInternalAliases
TestQuickInfoOnMergedModule
TestQuickInfoOnNarrowedTypeInModule
TestQuickInfoOnObjectLiteralWithAccessors
TestQuickInfoOnObjectLiteralWithOnlyGetter
TestQuickInfoOnObjectLiteralWithOnlySetter
TestQuickInfoOnPropDeclaredUsingIndexSignatureOnInterfaceWithBase
TestQuickInfoOnPropertyAccessInWriteLocation4
TestQuickInfoOnPropertyAccessInWriteLocation5
TestQuickInfoOnThis3
TestQuickInfoOnUndefined
TestQuickInfoOnVarInArrowExpression
TestQuickInfoPrivateIdentifierInTypeReferenceNoCrash1
TestQuickInfoSpecialPropertyAssignment
TestQuickInfoTemplateTag
TestQuickInfoTypeAliasDefinedInDifferentFile
TestQuickInfoTypeError
TestQuickInfoTypeOfThisInStatics
TestQuickInfoTypeOnlyNamespaceAndClass
TestQuickInfoUnionOfNamespaces
TestQuickInfoUntypedModuleImport
TestQuickinfoWrongComment
TestRecursiveInternalModuleImport
TestRegexDetection
TestSelfReferencedExternalModule
TestSignatureHelpCallExpressionJs
TestStringCompletionsImportOrExportSpecifier
TestStringCompletionsVsEscaping
TestSuggestionOfUnusedVariableWithExternalModule
TestSymbolCompletionLowerPriority
TestSyntheticImportFromBabelGeneratedFile1
TestSyntheticImportFromBabelGeneratedFile2
TestThisPredicateFunctionQuickInfo01
TestThisPredicateFunctionQuickInfo02
TestTsxQuickInfo4
TestTsxQuickInfo5
TestTsxQuickInfo6
TestTsxQuickInfo7
TestUnusedClassInNamespace1
TestUnusedClassInNamespace3
TestUnusedClassInNamespace4
TestUnusedClassInNamespaceWithTrivia1
TestUnusedClassInNamespaceWithTrivia2
TestUnusedConstantInFunction1
TestUnusedEnumInFunction1
TestUnusedEnumInNamespace1
TestUnusedFunctionInNamespace1
TestUnusedFunctionInNamespace2
TestUnusedFunctionInNamespace3
TestUnusedFunctionInNamespace4
TestUnusedFunctionInNamespace5
TestUnusedFunctionInNamespaceWithTrivia
TestUnusedImports10FS
TestUnusedImports11FS
TestUnusedImports12FS
TestUnusedImports13FS
TestUnusedImports14FS
TestUnusedImports1FS
TestUnusedImports3FS
TestUnusedImports4FS
TestUnusedImports5FS
TestUnusedImports6FS
TestUnusedImports7FS
TestUnusedImports8FS
TestUnusedImports9FS
TestUnusedLocalsinConstructorFS1
TestUnusedLocalsinConstructorFS2
TestUnusedLocalsInFunction1
TestUnusedLocalsInFunction3

View File

@@ -0,0 +1,83 @@
#!/usr/bin/env -S node --experimental-strip-types --no-warnings
// Usage: node --experimental-strip-types --no-warnings makeManual.mts <testName> [...testNames]
import * as fs from "fs";
import * as path from "path";
const scriptsDir = import.meta.dirname;
const manualTestsPath = path.join(scriptsDir, "manualTests.txt");
const genDir = path.join(scriptsDir, "../", "tests", "gen");
const manualDir = path.join(scriptsDir, "../", "tests", "manual");
const submoduleDir = path.join(scriptsDir, "../../../", "_submodules", "TypeScript", "tests", "cases", "fourslash");
function main() {
const args = process.argv.slice(2);
if (args.length === 0) {
console.error("Please provide the name of the generated test file.");
process.exit(1);
}
const testNames = [];
for (const arg of args) {
const testName = arg;
const testFileName = testName;
const genTestFile = path.join(genDir, testFileName + "_test.go");
const submoduleTestFile = path.join(submoduleDir, testFileName + ".ts");
const submoduleServerTestFile = path.join(submoduleDir, "server", testFileName + ".ts");
let testKind: "gen" | "submodule" | "submoduleServer" | undefined;
if (fs.existsSync(genTestFile)) {
testKind = "gen";
}
else if (fs.existsSync(submoduleTestFile)) {
testKind = "submodule";
}
else if (fs.existsSync(submoduleServerTestFile)) {
testKind = "submoduleServer";
}
if (!testKind) {
console.error(
`Could not find test neither as '${genTestFile}', nor as '${submoduleTestFile}' or '${submoduleServerTestFile}'.` +
`Make sure the test exists in the gen directory or in the submodule.`,
);
process.exit(1);
}
if (!fs.existsSync(manualDir)) {
fs.mkdirSync(manualDir, { recursive: true });
}
if (testKind === "gen") {
const manualTestFile = path.join(manualDir, path.basename(genTestFile));
markAsManual(genTestFile, manualTestFile);
}
testNames.push(testName);
}
let manualTests: string[] = [];
if (fs.existsSync(manualTestsPath)) {
const content = fs.readFileSync(manualTestsPath, "utf-8");
manualTests = content.split("\n").map(line => line.trim()).filter(line => line.length > 0);
}
for (const testName of testNames) {
if (!manualTests.includes(testName)) {
manualTests.push(testName);
}
}
manualTests.sort((a, b) => a.localeCompare(b, "en-US"));
fs.writeFileSync(manualTestsPath, [...manualTests, ""].join("\n"), "utf-8");
}
function markAsManual(genFilePath: string, manualFilePath: string) {
const content = fs.readFileSync(genFilePath, "utf-8");
const updatedContent = content
.replace(/^\/\/ Code generated by convertFourslash; DO NOT EDIT\.\n\/\/ To modify this test, run .*\n\n/m, "")
.replace(/^\s*fourslash\.SkipIfFailing\(t\)\s*$/m, "");
fs.writeFileSync(manualFilePath, updatedContent, "utf-8");
fs.rmSync(genFilePath);
}
main();

View File

@@ -0,0 +1,177 @@
augmentedTypesModule2
autoCloseFragment
autoCloseTag
autoImportCrossProject_symlinks_toSrc
autoImportNoPackageJson_commonjs
autoImportNoPackageJson_nodenext
autoImportPackageRootPathExtension
autoImportPackageRootPathTypeModule
autoImportProvider5
autoImportSymlinkCaseSensitive
codeFixMissingTypeAnnotationOnExports45-decorators
codeFixMissingTypeAnnotationOnExports46-decorators-experimental
completionForStringLiteral_details
completionForStringLiteralExport
completionForStringLiteralImport1
completionForStringLiteralImport2
completionForStringLiteralNonrelativeImport12
completionForStringLiteralNonrelativeImport14
completionForStringLiteralNonrelativeImport16
completionForStringLiteralNonrelativeImport4
completionForStringLiteralNonrelativeImport8
completionForStringLiteralRelativeImport4
completionForStringLiteralWithDynamicImport
completionImportModuleSpecifierEndingDts
completionImportModuleSpecifierEndingJs
completionImportModuleSpecifierEndingJsx
completionImportModuleSpecifierEndingTs
completionImportModuleSpecifierEndingTsxPreserve
completionImportModuleSpecifierEndingTsxReact
completionListInClosedFunction05
completionListInImportClause05
completionListInTypeLiteralInTypeParameter21
completionListWithLabel
completionsAtIncompleteObjectLiteralProperty
completionsImport_defaultAndNamedConflict
completionsImport_defaultFalsePositive
completionsImport_filteredByPackageJson_ambient
completionsImport_reExportDefault
completionsImport_reexportTransient
completionsPaths_importType
completionsPaths_kinds
completionsPaths_pathMapping
completionsPaths_pathMapping_nonTrailingWildcard1
completionsPaths_pathMapping_parentDirectory
completionsPathsJsonModule
completionsPathsRelativeJsonModule
completionsSelfDeclaring1
completionsWithDeprecatedTag4
completionsWithStringReplacementMode1
docCommentTemplateNamespacesAndModules01
duplicatePackageServices_fileChanges
exhaustiveCaseCompletions1
exhaustiveCaseCompletions2
exhaustiveCaseCompletions3
exhaustiveCaseCompletions4
exhaustiveCaseCompletions5
exhaustiveCaseCompletions6
exhaustiveCaseCompletions7
exhaustiveCaseCompletions8
formatOnEnterInComment
getEditsForFileRename_caseInsensitive
getEditsForFileRename_directory_down
getEditsForFileRename_directory_up
getEditsForFileRename_jsExtension
getEditsForFileRename_preferences
getEditsForFileRename_preservePathEnding
getEditsForFileRename_resolveJsonModule
getEditsForFileRename_shortenRelativePaths
getEditsForFileRename_subDir
getEditsForFileRename_unaffectedNonRelativePath
getOutliningSpans
importNameCodeFix_typesVersions
importNameCodeFix_uriStyleNodeCoreModules1
importNameCodeFixDefaultExport5
importNameCodeFixDefaultExport7
importNameCodeFixJsEnding
importNameCodeFixNewImportNodeModules1
importNameCodeFixOptionalImport0
incrementalParsingWithJsDoc
isDefinitionAcrossGlobalProjects
jsdocDeprecated_suggestion1
jsdocDeprecated_suggestion2
jsDocFunctionSignatures12
jsDocFunctionSignatures2
jsdocParameterNameCompletion
linkedEditingJsxTag1
linkedEditingJsxTag12
linkedEditingJsxTag2
linkedEditingJsxTag4
linkedEditingJsxTag5
linkedEditingJsxTag6
linkedEditingJsxTag7
linkedEditingJsxTag9
navigateItemsLet
navigateToImport
navigateToSymbolIterator
navigationBarFunctionPrototype
navigationBarFunctionPrototype2
navigationBarFunctionPrototype3
navigationBarFunctionPrototype4
navigationBarFunctionPrototypeBroken
navigationBarFunctionPrototypeInterlaced
navigationBarFunctionPrototypeNested
navigationBarJsDoc
navigationItemsExactMatch2
navigationItemsInConstructorsExactMatch
navigationItemsPrefixMatch2
navigationItemsSpecialPropertyAssignment
navto_emptyPattern
navto_excludeLib1
navto_excludeLib2
navto_excludeLib3
navto_excludeLib4
navto_serverExcludeLib
nodeModulesImportCompletions1
organizeImportsPathsUnicode3
organizeImportsUnicode3
outliningForNonCompleteInterfaceDeclaration
outliningHintSpansForFunction
overloadOnConstCallSignature
parserCorruptionAfterMapInClass
pathCompletionsPackageJsonExportsBundlerNoNodeCondition
pathCompletionsPackageJsonExportsCustomConditions
pathCompletionsPackageJsonExportsWildcard1
pathCompletionsPackageJsonExportsWildcard10
pathCompletionsPackageJsonExportsWildcard11
pathCompletionsPackageJsonExportsWildcard12
pathCompletionsPackageJsonExportsWildcard2
pathCompletionsPackageJsonExportsWildcard3
pathCompletionsPackageJsonExportsWildcard4
pathCompletionsPackageJsonExportsWildcard5
pathCompletionsPackageJsonExportsWildcard6
pathCompletionsPackageJsonExportsWildcard7
pathCompletionsPackageJsonExportsWildcard8
pathCompletionsPackageJsonExportsWildcard9
pathCompletionsPackageJsonImportsBundlerNoNodeCondition
pathCompletionsPackageJsonImportsCustomConditions
pathCompletionsPackageJsonImportsSrcNoDistWildcard1
pathCompletionsPackageJsonImportsSrcNoDistWildcard2
pathCompletionsPackageJsonImportsSrcNoDistWildcard3
pathCompletionsPackageJsonImportsSrcNoDistWildcard4
pathCompletionsPackageJsonImportsSrcNoDistWildcard5
pathCompletionsPackageJsonImportsSrcNoDistWildcard6
pathCompletionsPackageJsonImportsSrcNoDistWildcard7
pathCompletionsPackageJsonImportsSrcNoDistWildcard8
pathCompletionsPackageJsonImportsSrcNoDistWildcard9
pathCompletionsPackageJsonImportsWildcard1
pathCompletionsPackageJsonImportsWildcard10
pathCompletionsPackageJsonImportsWildcard11
pathCompletionsPackageJsonImportsWildcard12
pathCompletionsPackageJsonImportsWildcard2
pathCompletionsPackageJsonImportsWildcard3
pathCompletionsPackageJsonImportsWildcard4
pathCompletionsPackageJsonImportsWildcard5
pathCompletionsPackageJsonImportsWildcard6
pathCompletionsPackageJsonImportsWildcard7
pathCompletionsPackageJsonImportsWildcard8
pathCompletionsPackageJsonImportsWildcard9
pathCompletionsTypesVersionsWildcard3
pathCompletionsTypesVersionsWildcard4
pathCompletionsTypesVersionsWildcard5
pathCompletionsTypesVersionsWildcard6
quickInfoForOverloadOnConst1
renameDefaultKeyword
renameForDefaultExport01
semanticClassificationClassExpression
semanticModernClassificationFunctions
semicolonFormattingInsideAComment
semicolonFormattingInsideAStringLiteral
signatureHelpImportStarFromExportEquals
signatureHelpOnOverloadOnConst
stringLiteralCompletionsInPositionTypedUsingRest
tripleSlashRefPathCompletionAbsolutePaths
tripleSlashRefPathCompletionHiddenFile
tsconfigComputedPropertyError
tsxCompletion12
typeErrorAfterStringCompletionsInNestedCall

View File

@@ -0,0 +1,10 @@
{
"compilerOptions": {
"strict": true,
"noEmit": true,
"module": "nodenext",
"allowImportingTsExtensions": true,
"noFallthroughCasesInSwitch": true,
"types": ["node"]
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,141 @@
#!/usr/bin/env -S node --experimental-strip-types --no-warnings
// Usage: node --experimental-strip-types --no-warnings updateFailing.mts
import * as cp from "child_process";
import * as fs from "fs";
import path from "path";
import * as readline from "readline";
import which from "which";
const failingTestsPath = path.join(import.meta.dirname, "failingTests.txt");
const crashingTestsPath = path.join(import.meta.dirname, "crashingTests.txt");
interface TestEvent {
Time?: string;
Action: string;
Package?: string;
Test?: string;
Output?: string;
Elapsed?: number;
}
async function main() {
const go = which.sync("go");
let testProcess: cp.ChildProcess;
try {
// Run tests with TSGO_FOURSLASH_IGNORE_FAILING=1 to run all tests including those in failingTests.txt
testProcess = cp.spawn(go, ["test", "-json", "./internal/fourslash/tests/gen"], {
stdio: ["ignore", "pipe", "pipe"],
env: { ...process.env, TSGO_FOURSLASH_IGNORE_FAILING: "1" },
});
}
catch (error) {
throw new Error("Failed to spawn test process: " + error);
}
if (!testProcess.stdout || !testProcess.stderr) {
throw new Error("Test process stdout or stderr is null");
}
const failingTests: string[] = [];
const crashingTests: string[] = [];
const testOutputs = new Map<string, string[]>();
const allOutputs: string[] = [];
let hadPanic = false;
const rl = readline.createInterface({
input: testProcess.stdout,
crlfDelay: Infinity,
});
rl.on("line", line => {
try {
const event: TestEvent = JSON.parse(line);
// Collect output for each test
if (event.Action === "output" && event.Output) {
allOutputs.push(event.Output);
if (event.Test) {
if (!testOutputs.has(event.Test)) {
testOutputs.set(event.Test, []);
}
testOutputs.get(event.Test)!.push(event.Output);
}
// Check for panics
if (/^panic/m.test(event.Output)) {
hadPanic = true;
}
}
// Process failed tests
if (event.Action === "fail" && event.Test) {
const outputs = testOutputs.get(event.Test) || [];
// Check if this is a crashing test (contains InternalError)
const hasCrash = outputs.some(line => line.includes("InternalError"));
if (hasCrash) {
crashingTests.push(event.Test);
}
// A test is only considered a baseline-only failure if ALL error messages
// are baseline-related. Any non-baseline error message means it's a real failure.
const baselineMessagePatterns = [
/^\s*baseline\.go:\d+: the baseline file .* has changed\./,
/^\s*baseline\.go:\d+: new baseline created at /,
/^\s*baseline\.go:\d+: the baseline file .* does not exist in the TypeScript submodule/,
/^\s*baseline\.go:\d+: the baseline file .* does not match the reference in the TypeScript submodule/,
];
// Check each output line that looks like an error message
// Error messages from Go tests typically contain ".go:" with a line number
const errorLines = outputs.filter(line => /^\s*\w+\.go:\d+:/.test(line));
// If there are no error lines, it's a real failure.
// If all error lines match baseline patterns, it's a baseline-only failure
const isBaselineOnlyFailure = errorLines.length > 0 &&
errorLines.every(line => baselineMessagePatterns.some(pattern => pattern.test(line)));
if (!isBaselineOnlyFailure) {
failingTests.push(event.Test);
}
}
}
catch (e) {
// Not JSON, possibly stderr or other output - ignore
}
});
testProcess.stderr.on("data", data => {
// Check stderr for panics too
const output = data.toString();
allOutputs.push(output);
if (/^panic/m.test(output)) {
hadPanic = true;
}
});
await new Promise<void>((resolve, reject) => {
testProcess.on("close", code => {
if (hadPanic) {
reject(new Error("Unrecovered panic detected in tests\n" + allOutputs.join("")));
return;
}
fs.writeFileSync(failingTestsPath, failingTests.sort((a, b) => a.localeCompare(b, "en-US")).join("\n") + "\n", "utf-8");
fs.writeFileSync(crashingTestsPath, crashingTests.sort((a, b) => a.localeCompare(b, "en-US")).join("\n") + "\n", "utf-8");
resolve();
});
testProcess.on("error", error => {
reject(error);
});
});
}
main().catch(error => {
console.error("Error:", error);
process.exit(1);
});

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,130 @@
package fourslash
import (
"fmt"
"strings"
"testing"
"github.com/microsoft/typescript-go/internal/ls/lsconv"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
)
type SemanticToken struct {
Type string
Text string
}
func (f *FourslashTest) VerifySemanticTokens(t *testing.T, expected []SemanticToken) {
t.Helper()
params := &lsproto.SemanticTokensParams{
TextDocument: lsproto.TextDocumentIdentifier{
Uri: lsconv.FileNameToDocumentURI(f.activeFilename),
},
}
result := sendRequest(t, f, lsproto.TextDocumentSemanticTokensFullInfo, params)
if result.SemanticTokens == nil {
if len(expected) == 0 {
return
}
t.Fatal("Expected semantic tokens but got nil")
}
// Decode the semantic tokens using token types/modifiers from the test configuration
actual := decodeSemanticTokens(f, result.SemanticTokens.Data, f.semanticTokenTypes, f.semanticTokenModifiers)
// Compare with expected
if len(actual) != len(expected) {
t.Fatalf("Expected %d semantic tokens, got %d\n\nExpected:\n%s\n\nActual:\n%s",
len(expected), len(actual),
formatSemanticTokens(expected),
formatSemanticTokens(actual))
}
for i, exp := range expected {
act := actual[i]
if exp.Type != act.Type || exp.Text != act.Text {
t.Errorf("Token %d mismatch:\n Expected: {Type: %q, Text: %q}\n Actual: {Type: %q, Text: %q}",
i, exp.Type, exp.Text, act.Type, act.Text)
}
}
}
func decodeSemanticTokens(f *FourslashTest, data []uint32, tokenTypes, tokenModifiers []string) []SemanticToken {
if len(data)%5 != 0 {
panic(fmt.Sprintf("Invalid semantic tokens data length: %d", len(data)))
}
scriptInfo := f.scriptInfos[f.activeFilename]
converters := lsconv.NewConverters(lsproto.PositionEncodingKindUTF8, func(_ string) *lsconv.LSPLineMap {
return scriptInfo.lineMap
})
var tokens []SemanticToken
prevLine := uint32(0)
prevChar := uint32(0)
for i := 0; i < len(data); i += 5 {
deltaLine := data[i]
deltaChar := data[i+1]
length := data[i+2]
tokenTypeIdx := data[i+3]
tokenModifierMask := data[i+4]
// Calculate absolute position
line := prevLine + deltaLine
var char uint32
if deltaLine == 0 {
char = prevChar + deltaChar
} else {
char = deltaChar
}
// Get token type
if int(tokenTypeIdx) >= len(tokenTypes) {
panic(fmt.Sprintf("Token type index out of range: %d", tokenTypeIdx))
}
tokenType := tokenTypes[tokenTypeIdx]
// Get modifiers
var modifiers []string
for i, mod := range tokenModifiers {
if tokenModifierMask&(1<<i) != 0 {
modifiers = append(modifiers, mod)
}
}
// Build full type string (type.modifier1.modifier2)
typeStr := tokenType
if len(modifiers) > 0 {
typeStr = typeStr + "." + strings.Join(modifiers, ".")
}
// Get the text
startPos := lsproto.Position{Line: line, Character: char}
endPos := lsproto.Position{Line: line, Character: char + length}
startOffset := int(converters.LineAndCharacterToPosition(scriptInfo, startPos))
endOffset := int(converters.LineAndCharacterToPosition(scriptInfo, endPos))
text := scriptInfo.content[startOffset:endOffset]
tokens = append(tokens, SemanticToken{
Type: typeStr,
Text: text,
})
prevLine = line
prevChar = char
}
return tokens
}
func formatSemanticTokens(tokens []SemanticToken) string {
var lines []string
for i, tok := range tokens {
lines = append(lines, fmt.Sprintf(" [%d] {Type: %q, Text: %q}", i, tok.Type, tok.Text))
}
return strings.Join(lines, "\n")
}

View File

@@ -0,0 +1,53 @@
package fourslash
import (
"bufio"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"testing"
)
var failingTests = sync.OnceValue(func() map[string]struct{} {
failingTestsSet := make(map[string]struct{})
// Get the path to failingTests.txt relative to this source file
_, thisFile, _, ok := runtime.Caller(0)
if !ok {
return failingTestsSet
}
failingTestsPath := filepath.Join(filepath.Dir(thisFile), "_scripts", "failingTests.txt") //nolint:forbidigo
file, err := os.Open(failingTestsPath) //nolint:forbidigo
if err != nil {
return failingTestsSet
}
defer file.Close() //nolint:forbidigo
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line != "" {
failingTestsSet[line] = struct{}{}
}
}
return failingTestsSet
})
// SkipIfFailing checks if the current test is in the failingTests.txt file
// and skips it unless the TSGO_FOURSLASH_IGNORE_FAILING environment variable is set.
// This allows tests to be marked as failing without modifying the test files themselves.
func SkipIfFailing(t *testing.T) {
t.Helper()
if os.Getenv("TSGO_FOURSLASH_IGNORE_FAILING") != "" { //nolint:forbidigo
return
}
if _, found := failingTests()[t.Name()]; found {
t.Skip("Test is in failingTests.txt")
}
}

View File

@@ -0,0 +1,511 @@
package fourslash
import (
"fmt"
"io"
"iter"
"maps"
"slices"
"strings"
"testing"
"github.com/microsoft/typescript-go/internal/collections"
"github.com/microsoft/typescript-go/internal/compiler"
"github.com/microsoft/typescript-go/internal/json"
"github.com/microsoft/typescript-go/internal/ls/lsconv"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/project"
"github.com/microsoft/typescript-go/internal/testutil/fsbaselineutil"
"github.com/microsoft/typescript-go/internal/testutil/lsptestutil"
"github.com/microsoft/typescript-go/internal/tspath"
"github.com/microsoft/typescript-go/internal/vfs/iovfs"
"gotest.tools/v3/assert"
)
type stateBaseline struct {
baseline strings.Builder
fsDiffer *fsbaselineutil.FSDiffer
isInitialized bool
serializedProjects map[string]projectInfo
serializedOpenFiles map[string]*openFileInfo
serializedConfigFileRegistry *project.ConfigFileRegistry
}
func newStateBaseline(fsFromMap iovfs.FsWithSys) *stateBaseline {
stateBaseline := &stateBaseline{
fsDiffer: &fsbaselineutil.FSDiffer{
FS: fsFromMap,
WrittenFiles: &collections.SyncSet[string]{},
},
}
fmt.Fprintf(&stateBaseline.baseline, "UseCaseSensitiveFileNames: %v\n", fsFromMap.UseCaseSensitiveFileNames())
stateBaseline.fsDiffer.BaselineFSwithDiff(&stateBaseline.baseline)
return stateBaseline
}
type requestOrMessage struct {
Method lsproto.Method `json:"method"`
Params any `json:"params,omitzero"`
}
func (f *FourslashTest) baselineRequestOrNotification(t *testing.T, method lsproto.Method, params any) {
t.Helper()
if !f.testData.isStateBaseliningEnabled() {
return
}
res, _ := json.Marshal(requestOrMessage{method, params}, json.WithIndent(" "))
f.stateBaseline.baseline.WriteString("\n")
f.stateBaseline.baseline.Write(res)
f.stateBaseline.baseline.WriteString("\n")
f.stateBaseline.isInitialized = true
}
func (f *FourslashTest) baselineProjectsAfterNotification(t *testing.T, fileName string) {
t.Helper()
if !f.testData.isStateBaseliningEnabled() {
return
}
// Do hover so we have snapshot to check things on!!
_, _, resultOk := lsptestutil.SendRequest(t, f.client, lsproto.TextDocumentHoverInfo, &lsproto.HoverParams{
TextDocument: lsproto.TextDocumentIdentifier{
Uri: lsconv.FileNameToDocumentURI(fileName),
},
Position: lsproto.Position{
Line: uint32(0),
Character: uint32(0),
},
})
assert.Assert(t, resultOk)
f.baselineState(t)
}
func (f *FourslashTest) baselineState(t *testing.T) {
t.Helper()
if !f.testData.isStateBaseliningEnabled() {
return
}
serialized := f.serializedState(t)
if serialized != "" {
f.stateBaseline.baseline.WriteString("\n")
f.stateBaseline.baseline.WriteString(serialized)
}
}
func (f *FourslashTest) serializedState(t *testing.T) string {
t.Helper()
var builder strings.Builder
f.stateBaseline.fsDiffer.BaselineFSwithDiff(&builder)
if strings.TrimSpace(builder.String()) == "" {
builder.Reset()
}
f.printStateDiff(t, &builder)
return builder.String()
}
type projectInfo = *compiler.Program
type openFileInfo struct {
defaultProjectName string
allProjects []string
}
type diffTableOptions struct {
indent string
sortKeys bool
}
type diffTable struct {
diff collections.OrderedMap[string, string]
options diffTableOptions
}
func (d *diffTable) add(key, value string) {
d.diff.Set(key, value)
}
func (d *diffTable) print(w io.Writer, header string) {
count := d.diff.Size()
if count == 0 {
return
}
if header != "" {
fmt.Fprintf(w, "%s%s\n", d.options.indent, header)
}
diffKeys := make([]string, 0, count)
keyWidth := 0
indent := d.options.indent + " "
for key := range d.diff.Keys() {
keyWidth = max(keyWidth, len(key))
diffKeys = append(diffKeys, key)
}
if d.options.sortKeys {
slices.Sort(diffKeys)
}
for _, key := range diffKeys {
value := d.diff.GetOrZero(key)
fmt.Fprintf(w, "%s%-*s %s\n", indent, keyWidth+1, key, value)
}
}
type diffTableWriter struct {
hasChange bool
header string
diffs map[string]func(io.Writer)
}
func newDiffTableWriter(header string) *diffTableWriter {
return &diffTableWriter{header: header, diffs: make(map[string]func(io.Writer))}
}
func (d *diffTableWriter) setHasChange() {
d.hasChange = true
}
func (d *diffTableWriter) add(key string, fn func(io.Writer)) {
d.diffs[key] = fn
}
func (d *diffTableWriter) print(w io.Writer) {
if d.hasChange {
fmt.Fprintf(w, "%s::\n", d.header)
keys := slices.Collect(maps.Keys(d.diffs))
slices.Sort(keys)
for _, key := range keys {
d.diffs[key](w)
}
}
}
func areIterSeqEqual(a, b iter.Seq[tspath.Path]) bool {
aSlice := slices.Collect(a)
bSlice := slices.Collect(b)
slices.Sort(aSlice)
slices.Sort(bSlice)
return slices.Equal(aSlice, bSlice)
}
func printSlicesWithDiffTable(w io.Writer, header string, newSlice []string, getOldSlice func() []string, options diffTableOptions, topChange string, isDefault func(entry string) bool) {
var oldSlice []string
if topChange == "*modified*" {
oldSlice = getOldSlice()
}
table := diffTable{options: options}
for _, entry := range newSlice {
entryChange := ""
if isDefault != nil && isDefault(entry) {
entryChange = "(default) "
}
if topChange == "*modified*" && !slices.Contains(oldSlice, entry) {
entryChange = "*new*"
}
table.add(entry, entryChange)
}
if topChange == "*modified*" {
for _, entry := range oldSlice {
if !slices.Contains(newSlice, entry) {
table.add(entry, "*deleted*")
}
}
}
table.print(w, header)
}
func sliceFromIterSeqPath(seq iter.Seq[tspath.Path]) []string {
var result []string
for path := range seq {
result = append(result, string(path))
}
slices.Sort(result)
return result
}
func printPathIterSeqWithDiffTable(w io.Writer, header string, newIterSeq iter.Seq[tspath.Path], getOldIterSeq func() iter.Seq[tspath.Path], options diffTableOptions, topChange string) {
printSlicesWithDiffTable(
w,
header,
sliceFromIterSeqPath(newIterSeq),
func() []string { return sliceFromIterSeqPath(getOldIterSeq()) },
options,
topChange,
nil,
)
}
func (f *FourslashTest) printStateDiff(t *testing.T, w io.Writer) {
if !f.stateBaseline.isInitialized {
return
}
session := f.client.Server.Session()
snapshot := session.Snapshot()
f.printProjectsDiff(t, snapshot, w)
f.printOpenFilesDiff(t, snapshot, w)
f.printConfigFileRegistryDiff(t, snapshot, w)
}
func (f *FourslashTest) printProjectsDiff(t *testing.T, snapshot *project.Snapshot, w io.Writer) {
t.Helper()
currentProjects := make(map[string]projectInfo)
options := diffTableOptions{indent: " "}
projectsDiffTable := newDiffTableWriter("Projects")
for _, project := range snapshot.ProjectCollection.Projects() {
program := project.GetProgram()
var oldProgram *compiler.Program
currentProjects[project.Name()] = program
projectChange := ""
if existing, ok := f.stateBaseline.serializedProjects[project.Name()]; ok {
oldProgram = existing
if oldProgram != program {
projectChange = "*modified*"
projectsDiffTable.setHasChange()
} else {
projectChange = ""
}
} else {
projectChange = "*new*"
projectsDiffTable.setHasChange()
}
projectsDiffTable.add(project.Name(), func(w io.Writer) {
fmt.Fprintf(w, " [%s] %s\n", project.Name(), projectChange)
subDiff := diffTable{options: options}
if program != nil {
for _, file := range program.GetSourceFiles() {
fileDiff := ""
// No need to write "*new*" for files as its obvious
fileName := file.FileName()
if projectChange == "*modified*" {
if oldProgram == nil {
if !isLibFile(fileName) {
fileDiff = "*new*"
}
} else if oldFile := oldProgram.GetSourceFileByPath(file.Path()); oldFile == nil {
fileDiff = "*new*"
} else if oldFile != file {
fileDiff = "*modified*"
}
}
if fileDiff != "" || !isLibFile(fileName) {
subDiff.add(fileName, fileDiff)
}
}
}
if oldProgram != program && oldProgram != nil {
for _, file := range oldProgram.GetSourceFiles() {
if program == nil || program.GetSourceFileByPath(file.Path()) == nil {
subDiff.add(file.FileName(), "*deleted*")
}
}
}
subDiff.print(w, "")
})
}
for projectName, info := range f.stateBaseline.serializedProjects {
if _, found := currentProjects[projectName]; !found {
projectsDiffTable.setHasChange()
projectsDiffTable.add(projectName, func(w io.Writer) {
fmt.Fprintf(w, " [%s] *deleted*\n", projectName)
subDiff := diffTable{options: options}
if info != nil {
for _, file := range info.GetSourceFiles() {
if fileName := file.FileName(); !isLibFile(fileName) {
subDiff.add(fileName, "")
}
}
}
subDiff.print(w, "")
})
}
}
f.stateBaseline.serializedProjects = currentProjects
projectsDiffTable.print(w)
}
func (f *FourslashTest) printOpenFilesDiff(t *testing.T, snapshot *project.Snapshot, w io.Writer) {
t.Helper()
currentOpenFiles := make(map[string]*openFileInfo)
filesDiffTable := newDiffTableWriter("Open Files")
options := diffTableOptions{indent: " ", sortKeys: true}
for fileName := range f.openFiles {
path := tspath.ToPath(fileName, "/", f.vfs.UseCaseSensitiveFileNames())
defaultProject := snapshot.ProjectCollection.GetDefaultProject(path)
newFileInfo := &openFileInfo{}
if defaultProject != nil {
newFileInfo.defaultProjectName = defaultProject.Name()
}
for _, project := range snapshot.ProjectCollection.Projects() {
if program := project.GetProgram(); program != nil && program.GetSourceFileByPath(path) != nil {
newFileInfo.allProjects = append(newFileInfo.allProjects, project.Name())
}
}
slices.Sort(newFileInfo.allProjects)
currentOpenFiles[fileName] = newFileInfo
openFileChange := ""
var oldFileInfo *openFileInfo
if existing, ok := f.stateBaseline.serializedOpenFiles[fileName]; ok {
oldFileInfo = existing
if existing.defaultProjectName != newFileInfo.defaultProjectName || !slices.Equal(existing.allProjects, newFileInfo.allProjects) {
openFileChange = "*modified*"
filesDiffTable.setHasChange()
} else {
openFileChange = ""
}
} else {
openFileChange = "*new*"
filesDiffTable.setHasChange()
}
filesDiffTable.add(fileName, func(w io.Writer) {
fmt.Fprintf(w, " [%s] %s\n", fileName, openFileChange)
printSlicesWithDiffTable(
w,
"",
newFileInfo.allProjects,
func() []string { return oldFileInfo.allProjects },
options,
openFileChange,
func(projectName string) bool { return projectName == newFileInfo.defaultProjectName },
)
})
}
for fileName := range f.stateBaseline.serializedOpenFiles {
if _, found := currentOpenFiles[fileName]; !found {
filesDiffTable.setHasChange()
filesDiffTable.add(fileName, func(w io.Writer) {
fmt.Fprintf(w, " [%s] *closed*\n", fileName)
})
}
}
f.stateBaseline.serializedOpenFiles = currentOpenFiles
filesDiffTable.print(w)
}
func (f *FourslashTest) printConfigFileRegistryDiff(t *testing.T, snapshot *project.Snapshot, w io.Writer) {
t.Helper()
configFileRegistry := snapshot.ProjectCollection.ConfigFileRegistry()
configDiffsTable := newDiffTableWriter("Config")
configFileNamesDiffsTable := newDiffTableWriter("Config File Names")
if f.stateBaseline.serializedConfigFileRegistry == configFileRegistry {
return
}
options := diffTableOptions{indent: " ", sortKeys: true}
configFileRegistry.ForEachTestConfigEntry(func(path tspath.Path, entry *project.TestConfigEntry) {
configChange := ""
oldEntry := f.stateBaseline.serializedConfigFileRegistry.GetTestConfigEntry(path)
if oldEntry == nil {
configChange = "*new*"
configDiffsTable.setHasChange()
} else if oldEntry != entry {
if !areIterSeqEqual(oldEntry.RetainingProjects, entry.RetainingProjects) ||
!areIterSeqEqual(oldEntry.RetainingOpenFiles, entry.RetainingOpenFiles) ||
!areIterSeqEqual(oldEntry.RetainingConfigs, entry.RetainingConfigs) {
configChange = "*modified*"
configDiffsTable.setHasChange()
}
}
configDiffsTable.add(string(path), func(w io.Writer) {
fmt.Fprintf(w, " [%s] %s\n", entry.FileName, configChange)
// Print the details of the config entry
var retainingProjectsModified string
var retainingOpenFilesModified string
var retainingConfigsModified string
if configChange == "*modified*" {
if !areIterSeqEqual(entry.RetainingProjects, oldEntry.RetainingProjects) {
retainingProjectsModified = " *modified*"
}
if !areIterSeqEqual(entry.RetainingOpenFiles, oldEntry.RetainingOpenFiles) {
retainingOpenFilesModified = " *modified*"
}
if !areIterSeqEqual(entry.RetainingConfigs, oldEntry.RetainingConfigs) {
retainingConfigsModified = " *modified*"
}
}
printPathIterSeqWithDiffTable(w, "RetainingProjects:"+retainingProjectsModified, entry.RetainingProjects, func() iter.Seq[tspath.Path] { return oldEntry.RetainingProjects }, options, configChange)
printPathIterSeqWithDiffTable(w, "RetainingOpenFiles:"+retainingOpenFilesModified, entry.RetainingOpenFiles, func() iter.Seq[tspath.Path] { return oldEntry.RetainingOpenFiles }, options, configChange)
printPathIterSeqWithDiffTable(w, "RetainingConfigs:"+retainingConfigsModified, entry.RetainingConfigs, func() iter.Seq[tspath.Path] { return oldEntry.RetainingConfigs }, options, configChange)
})
})
configFileRegistry.ForEachTestConfigFileNamesEntry(func(path tspath.Path, entry *project.TestConfigFileNamesEntry) {
configFileNamesChange := ""
oldEntry := f.stateBaseline.serializedConfigFileRegistry.GetTestConfigFileNamesEntry(path)
if oldEntry == nil {
configFileNamesChange = "*new*"
configFileNamesDiffsTable.setHasChange()
} else if oldEntry.NearestConfigFileName != entry.NearestConfigFileName ||
!maps.Equal(oldEntry.Ancestors, entry.Ancestors) {
configFileNamesChange = "*modified*"
configFileNamesDiffsTable.setHasChange()
}
configFileNamesDiffsTable.add(string(path), func(w io.Writer) {
fmt.Fprintf(w, " [%s] %s\n", path, configFileNamesChange)
var nearestConfigFileNameModified string
var ancestorDiffModified string
if configFileNamesChange == "*modified*" {
if oldEntry.NearestConfigFileName != entry.NearestConfigFileName {
nearestConfigFileNameModified = " *modified*"
}
if !maps.Equal(oldEntry.Ancestors, entry.Ancestors) {
ancestorDiffModified = " *modified*"
}
}
fmt.Fprintf(w, " NearestConfigFileName: %s%s\n", entry.NearestConfigFileName, nearestConfigFileNameModified)
ancestorDiff := diffTable{options: options}
for config, ancestorOfConfig := range entry.Ancestors {
ancestorChange := ""
if configFileNamesChange == "*modified*" {
if oldConfigFileName, ok := oldEntry.Ancestors[config]; ok {
if oldConfigFileName != ancestorOfConfig {
ancestorChange = "*modified*"
}
} else {
ancestorChange = "*new*"
}
}
ancestorDiff.add(config, fmt.Sprintf("%s %s", ancestorOfConfig, ancestorChange))
}
if configFileNamesChange == "*modified*" {
for ancestorPath, oldConfigFileName := range oldEntry.Ancestors {
if _, ok := entry.Ancestors[ancestorPath]; !ok {
ancestorDiff.add(ancestorPath, oldConfigFileName+" *deleted*")
}
}
}
ancestorDiff.print(w, "Ancestors:"+ancestorDiffModified)
})
})
f.stateBaseline.serializedConfigFileRegistry.ForEachTestConfigEntry(func(path tspath.Path, entry *project.TestConfigEntry) {
if configFileRegistry.GetTestConfigEntry(path) == nil {
configDiffsTable.setHasChange()
configDiffsTable.add(string(path), func(w io.Writer) {
fmt.Fprintf(w, " [%s] *deleted*\n", entry.FileName)
})
}
})
f.stateBaseline.serializedConfigFileRegistry.ForEachTestConfigFileNamesEntry(func(path tspath.Path, entry *project.TestConfigFileNamesEntry) {
if configFileRegistry.GetTestConfigFileNamesEntry(path) == nil {
configFileNamesDiffsTable.setHasChange()
configFileNamesDiffsTable.add(string(path), func(w io.Writer) {
fmt.Fprintf(w, " [%s] *deleted*\n", path)
})
}
})
f.stateBaseline.serializedConfigFileRegistry = configFileRegistry
configDiffsTable.print(w)
configFileNamesDiffsTable.print(w)
}

View File

@@ -0,0 +1,509 @@
package fourslash
import (
"fmt"
"slices"
"strings"
"testing"
"unicode/utf8"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/json"
"github.com/microsoft/typescript-go/internal/ls/lsconv"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/stringutil"
"github.com/microsoft/typescript-go/internal/testrunner"
"github.com/microsoft/typescript-go/internal/tspath"
)
// Inserted in source files by surrounding desired text
// in a range with `[|` and `|]`. For example,
//
// [|text in range|]
//
// is a range with `text in range` "selected".
type RangeMarker struct {
fileName string
Range core.TextRange
LSRange lsproto.Range
Marker *Marker
}
func (r *RangeMarker) LSPos() lsproto.Position {
return r.LSRange.Start
}
func (r *RangeMarker) FileName() string {
return r.fileName
}
func (r *RangeMarker) GetName() *string {
if r.Marker == nil {
return nil
}
return r.Marker.Name
}
func (r *RangeMarker) LSLocation() lsproto.Location {
return lsproto.Location{
Uri: lsconv.FileNameToDocumentURI(r.fileName),
Range: r.LSRange,
}
}
type Marker struct {
fileName string
Position int
LSPosition lsproto.Position
Name *string // `nil` for anonymous markers such as `{| "foo": "bar" |}`
Data map[string]any
}
func (m *Marker) LSPos() lsproto.Position {
return m.LSPosition
}
func (m *Marker) FileName() string {
return m.fileName
}
func (m *Marker) GetName() *string {
return m.Name
}
func (m *Marker) MakerWithSymlink(fileName string) *Marker {
return &Marker{
fileName: fileName,
Position: m.Position,
LSPosition: m.LSPosition,
Name: m.Name,
Data: m.Data,
}
}
type MarkerOrRange interface {
FileName() string
LSPos() lsproto.Position
GetName() *string
}
type TestData struct {
Files []*TestFileInfo
MarkerPositions map[string]*Marker
Markers []*Marker
Symlinks map[string]string
GlobalOptions map[string]string
Ranges []*RangeMarker
}
func (t *TestData) isStateBaseliningEnabled() bool {
return isStateBaseliningEnabled(t.GlobalOptions)
}
type testFileWithMarkers struct {
file *TestFileInfo
markers []*Marker
ranges []*RangeMarker
}
func isStateBaseliningEnabled(globalOptions map[string]string) bool {
return globalOptions["statebaseline"] == "true"
}
func ParseTestData(t *testing.T, contents string, fileName string) TestData {
// List of all the subfiles we've parsed out
var files []*TestFileInfo
markerPositions := make(map[string]*Marker)
var markers []*Marker
var ranges []*RangeMarker
filesWithMarker, symlinks, _, globalOptions, e := testrunner.ParseTestFilesAndSymlinksWithOptions(
contents,
fileName,
parseFileContent,
testrunner.ParseTestFilesOptions{
AllowImplicitFirstFile: true,
},
)
if e != nil {
t.Fatalf("Error parsing fourslash data: %s", e.Error())
}
hasTSConfig := false
for _, file := range filesWithMarker {
files = append(files, file.file)
hasTSConfig = hasTSConfig || isConfigFile(file.file.fileName)
markers = append(markers, file.markers...)
ranges = append(ranges, file.ranges...)
for _, marker := range file.markers {
if marker.Name == nil {
if marker.Data != nil {
// The marker is an anonymous object marker, which does not need a name. Markers are only set into markerPositions if they have a name
continue
}
t.Fatalf(`Marker at position %v is unnamed`, marker.Position)
}
if existing, ok := markerPositions[*marker.Name]; ok {
t.Fatalf(`Duplicate marker name: "%s" at %v and %v`, *marker.Name, marker.Position, existing.Position)
}
markerPositions[*marker.Name] = marker
}
}
if hasTSConfig && hasUnsupportedGlobalOptionsWithConfig(globalOptions) && !isStateBaseliningEnabled(globalOptions) {
t.Fatalf("It is not allowed to use global options along with config files.")
}
return TestData{
Files: files,
MarkerPositions: markerPositions,
Markers: markers,
Symlinks: symlinks,
GlobalOptions: globalOptions,
Ranges: ranges,
}
}
func hasUnsupportedGlobalOptionsWithConfig(globalOptions map[string]string) bool {
for option := range globalOptions {
switch strings.ToLower(option) {
case "symlink", "link", "usecasesensitivefilenames":
continue
default:
return true
}
}
return false
}
func isConfigFile(fileName string) bool {
fileName = strings.ToLower(fileName)
return strings.HasSuffix(fileName, "tsconfig.json") || strings.HasSuffix(fileName, "jsconfig.json")
}
type locationInformation struct {
position int
sourcePosition int
sourceLine int
sourceColumn int
}
type rangeLocationInformation struct {
locationInformation
marker *Marker
}
type TestFileInfo struct {
fileName string
// The contents of the file (with markers, etc stripped out)
Content string
emit bool
}
// FileName implements lsconv.Script.
func (t *TestFileInfo) FileName() string {
return t.fileName
}
// Text implements lsconv.Script.
func (t *TestFileInfo) Text() string {
return t.Content
}
var _ lsconv.Script = (*TestFileInfo)(nil)
const emitThisFileOption = "emitthisfile"
type parserState int
const (
stateNone parserState = iota
stateInSlashStarMarker
stateInObjectMarker
)
func parseFileContent(fileName string, content string, fileOptions map[string]string) (*testFileWithMarkers, error) {
fileName = tspath.GetNormalizedAbsolutePath(fileName, "/")
content = chompLeadingSpace(content)
// The file content (minus metacharacters) so far
var output strings.Builder
var markers []*Marker
/// A stack of the open range markers that are still unclosed
openRanges := []rangeLocationInformation{}
/// A list of closed ranges we've collected so far
rangeMarkers := []*RangeMarker{}
// The total number of metacharacters removed from the file (so far)
difference := 0
// One-based current position data
line := 1
column := 1
// The current marker (or maybe multi-line comment?) we're parsing, possibly
var openMarker *locationInformation
// The latest position of the start of an unflushed plain text area
lastNormalCharPosition := 0
flush := func(lastSafeCharIndex int) {
if lastSafeCharIndex != -1 {
output.WriteString(content[lastNormalCharPosition:lastSafeCharIndex])
} else {
output.WriteString(content[lastNormalCharPosition:])
}
}
state := stateNone
previousCharacter, i := utf8.DecodeRuneInString(content)
var size int
var currentCharacter rune
for ; i < len(content); i = i + size {
currentCharacter, size = utf8.DecodeRuneInString(content[i:])
switch state {
case stateNone:
if previousCharacter == '[' && currentCharacter == '|' {
// found a range start
openRanges = append(openRanges, rangeLocationInformation{
locationInformation: locationInformation{
position: (i - 1) - difference,
sourcePosition: i - 1,
sourceLine: line,
sourceColumn: column,
},
})
// copy all text up to marker position
flush(i - 1)
lastNormalCharPosition = i + 1
difference += 2
} else if previousCharacter == '|' && currentCharacter == ']' {
// found a range end
if len(openRanges) == 0 {
return nil, reportError(fileName, line, column, "Found range end with no matching start.")
}
rangeStart := openRanges[len(openRanges)-1]
openRanges = openRanges[:len(openRanges)-1]
closedRange := &RangeMarker{
fileName: fileName,
Range: core.NewTextRange(rangeStart.position, (i-1)-difference),
Marker: rangeStart.marker,
}
rangeMarkers = append(rangeMarkers, closedRange)
// copy all text up to range marker position
flush(i - 1)
lastNormalCharPosition = i + 1
difference += 2
} else if previousCharacter == '/' && currentCharacter == '*' && (i+1 >= len(content) || content[i+1] != '/') {
// found a possible marker start
state = stateInSlashStarMarker
openMarker = &locationInformation{
position: (i - 1) - difference,
sourcePosition: i - 1,
sourceLine: line,
sourceColumn: column - 1,
}
} else if previousCharacter == '{' && currentCharacter == '|' {
// found an object marker start
state = stateInObjectMarker
openMarker = &locationInformation{
position: (i - 1) - difference,
sourcePosition: i - 1,
sourceLine: line,
sourceColumn: column,
}
flush(i - 1)
}
case stateInObjectMarker:
// Object markers are only ever terminated by |} and have no content restrictions
if previousCharacter == '|' && currentCharacter == '}' {
objectMarkerData := strings.TrimSpace(content[openMarker.sourcePosition+2 : i-1])
marker, e := getObjectMarker(fileName, openMarker, objectMarkerData)
if e != nil {
return nil, e
}
if len(openRanges) > 0 {
openRanges[len(openRanges)-1].marker = marker
}
markers = append(markers, marker)
// Set the current start to point to the end of the current marker to ignore its text
lastNormalCharPosition = i + 1
difference += i + 1 - openMarker.sourcePosition
// Reset the state
openMarker = nil
state = stateNone
}
case stateInSlashStarMarker:
if previousCharacter == '*' && currentCharacter == '/' {
// Record the marker
// start + 2 to ignore the */, -1 on the end to ignore the * (/ is next)
markerNameText := strings.TrimSpace(content[openMarker.sourcePosition+2 : i-1])
marker := &Marker{
fileName: fileName,
Position: openMarker.position,
Name: &markerNameText,
}
if len(openRanges) > 0 {
openRanges[len(openRanges)-1].marker = marker
}
markers = append(markers, marker)
// Set the current start to point to the end of the current marker to ignore its text
flush(openMarker.sourcePosition)
lastNormalCharPosition = i + 1
difference += i + 1 - openMarker.sourcePosition
// Reset the state
openMarker = nil
state = stateNone
} else if !(stringutil.IsDigit(currentCharacter) ||
stringutil.IsASCIILetter(currentCharacter) ||
currentCharacter == '$' ||
currentCharacter == '_') { // Invalid marker character
if currentCharacter == '*' && i < len(content)-1 && content[i+1] == '/' {
// The marker is about to be closed, ignore the 'invalid' char
} else {
// We've hit a non-valid marker character, so we were actually in a block comment
// Bail out the text we've gathered so far back into the output
flush(i)
lastNormalCharPosition = i
openMarker = nil
state = stateNone
}
}
}
if currentCharacter == '\n' && previousCharacter == '\r' {
// Ignore trailing \n after \r
continue
} else if currentCharacter == '\n' || currentCharacter == '\r' {
line++
column = 1
continue
}
column++
if i >= lastNormalCharPosition {
previousCharacter = currentCharacter
} else {
previousCharacter = utf8.RuneError // reset to avoid accidentally reusing marker delimiters as part of other markers
}
}
// Add the remaining text
flush(-1)
if len(openRanges) > 0 {
openRange := openRanges[0]
return nil, reportError(fileName, openRange.sourceLine, openRange.sourceColumn, "Unterminated range.")
}
if openMarker != nil {
return nil, reportError(fileName, openMarker.sourceLine, openMarker.sourceColumn, "Unterminated marker.")
}
outputString := output.String()
// Set LS positions for markers
lineMap := lsconv.ComputeLSPLineStarts(outputString)
converters := lsconv.NewConverters(lsproto.PositionEncodingKindUTF8, func(_ string) *lsconv.LSPLineMap {
return lineMap
})
emit := fileOptions[emitThisFileOption] == "true"
testFileInfo := &TestFileInfo{
fileName: fileName,
Content: outputString,
emit: emit,
}
slices.SortStableFunc(rangeMarkers, func(a, b *RangeMarker) int {
if a.Range.Pos() != b.Range.Pos() {
return a.Range.Pos() - b.Range.Pos()
}
return b.Range.End() - a.Range.End()
})
for _, marker := range markers {
marker.LSPosition = converters.PositionToLineAndCharacter(testFileInfo, core.TextPos(marker.Position))
}
for _, rangeMarker := range rangeMarkers {
rangeMarker.LSRange = lsproto.Range{
Start: converters.PositionToLineAndCharacter(testFileInfo, core.TextPos(rangeMarker.Range.Pos())),
End: converters.PositionToLineAndCharacter(testFileInfo, core.TextPos(rangeMarker.Range.End())),
}
}
return &testFileWithMarkers{
file: testFileInfo,
markers: markers,
ranges: rangeMarkers,
}, nil
}
func getObjectMarker(fileName string, location *locationInformation, text string) (*Marker, error) {
// Attempt to parse the marker value as JSON
var v any
e := json.Unmarshal([]byte("{ "+text+" }"), &v)
if e != nil {
return nil, reportError(fileName, location.sourceLine, location.sourceColumn, "Unable to parse marker text "+text)
}
markerValue, ok := v.(map[string]any)
if !ok || len(markerValue) == 0 {
return nil, reportError(fileName, location.sourceLine, location.sourceColumn, "Object markers can not be empty")
}
marker := &Marker{
fileName: fileName,
Position: location.position,
Data: markerValue,
}
// Object markers can be anonymous
if markerValue["name"] != nil {
if name, ok := markerValue["name"].(string); ok && name != "" {
marker.Name = &name
}
}
return marker, nil
}
func reportError(fileName string, line int, col int, message string) error {
return &fourslashError{fmt.Sprintf("%v (%v,%v): %v", fileName, line, col, message)}
}
func chompLeadingSpace(content string) string {
lines := strings.Split(content, "\n")
for _, line := range lines {
if len(line) > 0 && line[0] != ' ' {
return content
}
}
result := make([]string, len(lines))
for i, line := range lines {
if len(line) > 0 {
result[i] = line[1:]
}
}
return strings.Join(result, "\n")
}
type fourslashError struct {
err string
}
func (e *fourslashError) Error() string {
return e.err
}

View File

@@ -0,0 +1,20 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestGoToDefinitionDecoratorNoCrashOnFunctionDeclaration1(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `function dec(target: any) { return target; }
@/*1*/dec
function foo() {}`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyBaselineGoToDefinition(t, true, "1")
}

View File

@@ -0,0 +1,41 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestArgumentCompletions(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
function foo(a: "a", b: "b") {}
foo("a", /*1*/);
const t3 = ['x', 'y', 'z'] as const;
const x: [string, string, string, 'a' | 'b'] = [...t3, /*2*/];
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{`"b"`},
},
})
f.VerifyCompletions(t, "2", &fourslash.CompletionsExpectedList{
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{`"b"`},
},
})
}

View File

@@ -0,0 +1,98 @@
package fourslash
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestAutoCloseTagsWithTriviaAndComplexNames(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
// Using separate files for each example to avoid unclosed JSX tags affecting other tests.
const content = `// @noLib: true
// @Filename: /0.tsx
// JSDoc
const x = <
/** hello world! */
div /** hello world! */
>/*0*/
// @Filename: /1.tsx
// Single-line comments
const x =
<
// hello world!
div // hello world!
>/*1*/
// @Filename: /2.tsx
// Namespaced tag
const x =
<ns:sometag>/*2*/
// @Filename: /3.tsx
// Namespace with single-line comments
const x = <
// pre-ns
ns
// pre-colon
:
// post-colon
sometag
// post-id
>/*3*/
// @Filename: /4.tsx
// UppercaseComponent-named tag
const x = <SomeComponent>/*4*/
// @Filename: /5.tsx
// propertyAccess.Component-named tag
const x = <
someModule
.
SomeComponent
>/*5*/
// @Filename: /6.tsx
// propertyAccess.Component-named tag with single-line comments
const x =
<
// pre-object
someModule
// pre-dot
.
// post-dot
SomeComponent
// post-id
>/*6*/;
// @Filename: /7.tsx
// Generic propertyAccess.Component-named tag
const x =
<
someModule.SomeComponent<string>
prop="stringValue"
>/*7*/;
// @Filename: /8.tsx
// Namespaced tag with hyphens
const x =
<my-namespace:my-tag>/*8*/
// @Filename: /9.tsx
// Generic tag with no attributes
const x = <SomeComponent<number>>/*9*/
// @Filename: /10.tsx
// Tag name containing $ (must be snippet-escaped)
const x = <$Foo>/*10*/
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyBaselineClosingTags(t)
}

View File

@@ -0,0 +1,105 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
// TestAutoImportCJSWithNodeModuleKind verifies that auto-imports use require()
// syntax in CJS files when using node16/node20/nodenext module kinds with a
// package.json that has "type": "commonjs".
func TestAutoImportCJSWithNodeModuleKind(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /tsconfig.json
{
"compilerOptions": {
"allowJs": true,
"module": "node20",
"checkJs": true,
"noEmit": true
}
}
// @Filename: /package.json
{ "type": "commonjs" }
// @Filename: /lib.js
module.exports = { LIB_VERSION: 1 };
// @Filename: /main.js
module.exports.foo = 0;
LIB_VERSION/**/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "")
f.VerifyImportFixAtPosition(t, []string{
`const { LIB_VERSION } = require("./lib");
module.exports.foo = 0;
LIB_VERSION`,
}, nil /*preferences*/)
}
// TestAutoImportCJSWithNodeModuleKindEmptyFile verifies that auto-imports use
// require() syntax even in empty CJS files when using node module kinds.
func TestAutoImportCJSWithNodeModuleKindEmptyFile(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /tsconfig.json
{
"compilerOptions": {
"allowJs": true,
"module": "node20",
"checkJs": true,
"noEmit": true
}
}
// @Filename: /package.json
{ "type": "commonjs" }
// @Filename: /lib.js
module.exports = { LIB_VERSION: 1 };
// @Filename: /main.js
LIB_VERSION/**/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "")
f.VerifyImportFixAtPosition(t, []string{
`const { LIB_VERSION } = require("./lib");
LIB_VERSION`,
}, nil /*preferences*/)
}
// TestAutoImportCJSWithModuleDetectionForce verifies that auto-imports use
// require() syntax in JS files with CJS syntax when moduleDetection is "force",
// even with --module preserve where GetImpliedNodeFormatForEmit won't help.
func TestAutoImportCJSWithModuleDetectionForce(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /tsconfig.json
{
"compilerOptions": {
"allowJs": true,
"module": "preserve",
"moduleDetection": "force",
"checkJs": true,
"noEmit": true
}
}
// @Filename: /lib.js
export const LIB_VERSION = 1;
// @Filename: /main.js
const path = require("path");
LIB_VERSION/**/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "")
f.VerifyImportFixAtPosition(t, []string{
`const path = require("path");
const { LIB_VERSION } = require("./lib");
LIB_VERSION`,
}, nil /*preferences*/)
}

View File

@@ -0,0 +1,122 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/ls/lsutil"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestAutoImportCompletion1(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: a.ts
export const someVar = 10;
// @Filename: b.ts
export const anotherVar = 10;
// @Filename: c.ts
import {someVar} from "./a.ts";
someVar;
a/**/
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "", &fourslash.CompletionsExpectedList{
UserPreferences: &lsutil.UserPreferences{
IncludeCompletionsForModuleExports: core.TSTrue,
IncludeCompletionsForImportStatements: core.TSTrue,
},
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{"someVar", "anotherVar"},
},
})
f.BaselineAutoImportsCompletions(t, []string{""})
f.VerifyCompletions(t, "", &fourslash.CompletionsExpectedList{
UserPreferences: &lsutil.UserPreferences{
// completion autoimport preferences off; this tests if fourslash server communication correctly registers changes in user preferences
IncludeCompletionsForModuleExports: core.TSFalse,
IncludeCompletionsForImportStatements: core.TSFalse,
},
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Excludes: []string{"anotherVar"},
},
})
}
func TestAutoImportCompletion2(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: a.ts
export const someVar = 10;
export const anotherVar = 10;
// @Filename: c.ts
import {someVar} from "./a.ts";
someVar;
a/**/
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "", &fourslash.CompletionsExpectedList{
UserPreferences: &lsutil.UserPreferences{
IncludeCompletionsForModuleExports: core.TSTrue,
IncludeCompletionsForImportStatements: core.TSTrue,
},
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{"someVar", "anotherVar"},
},
})
f.BaselineAutoImportsCompletions(t, []string{""})
}
func TestAutoImportCompletion3(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: a.ts
export const aa = "asdf";
export const someVar = 10;
export const bb = 10;
// @Filename: c.ts
import { aa, someVar } from "./a.ts";
someVar;
b/**/
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "", &fourslash.CompletionsExpectedList{
UserPreferences: &lsutil.UserPreferences{
IncludeCompletionsForModuleExports: core.TSTrue,
IncludeCompletionsForImportStatements: core.TSTrue,
},
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{"bb"},
},
})
f.BaselineAutoImportsCompletions(t, []string{""})
}

View File

@@ -0,0 +1,37 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
util "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestAutoImportCompletionsForArbitraryNonIdentifierExports(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
// @module: esnext
// @Filename: /a.ts
const foo = 0;
export { foo as "foo-bar" };
export const fooBar = 1;
// @Filename: /b.ts
foo/**/
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "", &fourslash.CompletionsExpectedList{
Items: &fourslash.CompletionsExpectedItems{
Excludes: []string{"foo-bar"},
Includes: []fourslash.CompletionsExpectedItem{"fooBar"},
},
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &util.DefaultCommitCharacters,
EditRange: util.Ignored,
},
})
}

View File

@@ -0,0 +1,54 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
// TestAutoImportCrossProjectNodeModules verifies that when multiple projects share
// a node_modules directory, auto-imports for a project only show packages that are
// either listed in that project's package.json dependencies OR directly imported
// by that project's files.
//
// Scenario:
// - Two separate projects (project-a and project-b) share a root node_modules
// - pkg-listed is in both package.json dependencies
// - pkg-unlisted is NOT in any package.json, but project-a imports it directly
// - When requesting completions in project-b, pkg-unlisted should NOT appear
// because project-b doesn't list it and doesn't import it directly.
func TestAutoImportCrossProjectNodeModules(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /node_modules/pkg-listed/package.json
{ "name": "pkg-listed", "version": "1.0.0" }
// @Filename: /node_modules/pkg-listed/index.d.ts
export declare const pkg_listed_value: number;
// @Filename: /node_modules/pkg-unlisted/package.json
{ "name": "pkg-unlisted", "version": "1.0.0" }
// @Filename: /node_modules/pkg-unlisted/index.d.ts
export declare const pkg_unlisted_value: string;
// @Filename: /project-a/tsconfig.json
{ "compilerOptions": { "module": "commonjs", "strict": true } }
// @Filename: /project-a/package.json
{ "name": "project-a", "dependencies": { "pkg-listed": "*" } }
// @Filename: /project-a/index.ts
import { pkg_unlisted_value } from "pkg-unlisted";
console.log(pkg_unlisted_value);
// @Filename: /project-b/tsconfig.json
{ "compilerOptions": { "module": "commonjs", "strict": true } }
// @Filename: /project-b/package.json
{ "name": "project-b", "dependencies": { "pkg-listed": "*" } }
// @Filename: /project-b/index.ts
pkg_/**/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
// Open project-a's file first - this establishes that project-a imports pkg-unlisted
f.GoToFile(t, "/project-a/index.ts")
// Now go to project-b and baseline auto-imports
f.GoToMarker(t, "")
f.BaselineAutoImportsCompletions(t, []string{""})
}

View File

@@ -0,0 +1,56 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/ls"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestAutoImportCssModule(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
// @Filename: /tsconfig.json
{ "compilerOptions": { "module": "nodenext", "moduleResolution": "nodenext" } }
// @Filename: /package.json
{ "type": "module" }
// @Filename: /augmentations.ts
export {};
declare module "./styles.css" {
export const myClass: string;
}
// @Filename: /index.ts
myClass/**/
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
// Verify auto-import completions don't panic when importing from .css module augmentation
f.VerifyCompletions(t, "", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "myClass",
Data: &lsproto.CompletionItemData{
AutoImport: &lsproto.AutoImportFix{
ModuleSpecifier: "./styles.css",
},
},
AdditionalTextEdits: fourslash.AnyTextEdits,
SortText: new(string(ls.SortTextAutoImportSuggestions)),
},
},
},
})
}

View File

@@ -0,0 +1,235 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/ls/lsutil"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestAutoImportDefaultPascalCase(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @jsx: react
// @module: esnext
// @moduleResolution: bundler
// @Filename: /src/components/ChargerHeader.tsx
export default function ChargerHeader() {
return null;
}
// @Filename: /src/screens/SomeScreen.tsx
export function SomeScreen() {
return <ChargerHeader/*1*/
}
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
UserPreferences: &lsutil.UserPreferences{
IncludeCompletionsForModuleExports: core.TSTrue,
IncludeCompletionsForImportStatements: core.TSTrue,
},
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{"ChargerHeader"},
},
})
f.BaselineAutoImportsCompletions(t, []string{"1"})
}
func TestAutoImportDefaultPascalCaseAnonymous(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @jsx: react
// @module: esnext
// @moduleResolution: bundler
// @Filename: /src/components/ChargerHeader.tsx
export default function() {
return null;
}
// @Filename: /src/screens/SomeScreen.tsx
export function SomeScreen() {
return <ChargerHeader/*1*/
}
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
UserPreferences: &lsutil.UserPreferences{
IncludeCompletionsForModuleExports: core.TSTrue,
IncludeCompletionsForImportStatements: core.TSTrue,
},
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{"ChargerHeader"},
},
})
f.BaselineAutoImportsCompletions(t, []string{"1"})
}
func TestAutoImportDefaultPascalCaseCaseInsensitive(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @jsx: react
// @module: esnext
// @moduleResolution: bundler
// @useCaseSensitiveFileNames: false
// @Filename: /src/components/ChargerHeader.tsx
export default function ChargerHeader() {
return null;
}
// @Filename: /src/screens/SomeScreen.tsx
export function SomeScreen() {
return <ChargerHeader/*1*/
}
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
UserPreferences: &lsutil.UserPreferences{
IncludeCompletionsForModuleExports: core.TSTrue,
IncludeCompletionsForImportStatements: core.TSTrue,
},
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{"ChargerHeader"},
},
})
f.BaselineAutoImportsCompletions(t, []string{"1"})
}
func TestAutoImportDefaultPascalCaseAnonymousCaseInsensitive(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @jsx: react
// @module: esnext
// @moduleResolution: bundler
// @useCaseSensitiveFileNames: false
// @Filename: /src/components/ChargerHeader.tsx
export default function() {
return null;
}
// @Filename: /src/screens/SomeScreen.tsx
export function SomeScreen() {
return <ChargerHeader/*1*/
}
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
UserPreferences: &lsutil.UserPreferences{
IncludeCompletionsForModuleExports: core.TSTrue,
IncludeCompletionsForImportStatements: core.TSTrue,
},
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{"ChargerHeader"},
},
})
f.BaselineAutoImportsCompletions(t, []string{"1"})
}
func TestAutoImportDefaultPascalCaseReexportCaseInsensitive(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @jsx: react
// @module: esnext
// @moduleResolution: bundler
// @useCaseSensitiveFileNames: false
// @Filename: /src/components/ChargerHeader.tsx
export default function() {
return null;
}
// @Filename: /src/components/index.ts
export { default } from "./ChargerHeader";
// @Filename: /src/screens/SomeScreen.tsx
export function SomeScreen() {
return <ChargerHeader/*1*/
}
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
UserPreferences: &lsutil.UserPreferences{
IncludeCompletionsForModuleExports: core.TSTrue,
IncludeCompletionsForImportStatements: core.TSTrue,
},
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{"ChargerHeader"},
},
})
f.BaselineAutoImportsCompletions(t, []string{"1"})
}
func TestAutoImportDefaultPascalCaseAliasCaseInsensitive(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @jsx: react
// @module: esnext
// @moduleResolution: bundler
// @useCaseSensitiveFileNames: false
// @Filename: /src/components/ChargerHeader.tsx
function ChargerHeader() {
return null;
}
export default ChargerHeader;
// @Filename: /src/screens/SomeScreen.tsx
export function SomeScreen() {
return <ChargerHeader/*1*/
}
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
UserPreferences: &lsutil.UserPreferences{
IncludeCompletionsForModuleExports: core.TSTrue,
IncludeCompletionsForImportStatements: core.TSTrue,
},
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{"ChargerHeader"},
},
})
f.BaselineAutoImportsCompletions(t, []string{"1"})
}

View File

@@ -0,0 +1,28 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestAutoImportErrorMixedExportKinds(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: a.ts
export function foo(): number {
return 10
}
const bar = 20;
export { bar as foo };
// @Filename: b.ts
foo/**/
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
// Verify we don't crash from the mixed exports
f.BaselineAutoImportsCompletions(t, []string{""})
}

View File

@@ -0,0 +1,35 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestAutoImportExportEqualsOfImportStar(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @module: commonjs
// @Filename: /node_modules/mdx/package.json
{ "name": "mdx", "version": "1.0.0", "types": "index.d.ts" }
// @Filename: /node_modules/mdx/index.d.ts
import * as mdx from './lib/index.js'
export = mdx
// @Filename: /node_modules/mdx/lib/index.d.ts
export * from './core.js'
export * from './compile.js'
// @Filename: /node_modules/mdx/lib/core.d.ts
export declare function core(): void
// @Filename: /node_modules/mdx/lib/compile.d.ts
export declare function compile(): void
// @Filename: /package.json
{ "dependencies": { "mdx": "*" } }
// @Filename: /index.ts
mdx/**/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "")
f.BaselineAutoImportsCompletions(t, []string{""})
}

View File

@@ -0,0 +1,54 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/ls/lsutil"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestAutoImportFileExcludePatterns(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: foo.ts
export const mySymbol = 1;
// @Filename: ignoreme.ts
export const ignoredSymbol = 2;
// @Filename: bar.ts
mySym/*1*/
ignoredSym/*2*/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.Configure(t, lsutil.UserPreferences{
AutoImportFileExcludePatterns: []string{"*ignoreme.ts"},
IncludeCompletionsForModuleExports: core.TSTrue,
IncludeCompletionsForImportStatements: core.TSTrue,
})
// Verify that mySymbol is included, but ignoredSymbol is excluded from completions
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{"mySymbol"},
},
})
f.VerifyCompletions(t, "2", &fourslash.CompletionsExpectedList{
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Excludes: []string{"ignoredSymbol"},
},
})
// Baseline the auto-imports
f.BaselineAutoImportsCompletions(t, []string{"1", "2"})
}

View File

@@ -0,0 +1,30 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestAutoImportModuleAugmentation(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /a.ts
export interface Foo {
x: number;
}
// @Filename: /b.ts
export {};
declare module "./a" {
export const Foo: any;
}
// @Filename: /c.ts
Foo/**/
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.BaselineAutoImportsCompletions(t, []string{""})
}

View File

@@ -0,0 +1,43 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestAutoImportNewLine(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /a.ts
export function readFileSync() {}
// @Filename: /b.ts
import {} from "./other1";
import {} from "./other2";
readFileSync/**/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.BaselineAutoImportsCompletions(t, []string{""})
}
func TestAutoImportNewLineWithHeaderComment(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /a.ts
export function readFileSync() {}
// @Filename: /b.ts
/* file header comment */
import {} from "./other1";
import {} from "./other2";
readFileSync/**/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.BaselineAutoImportsCompletions(t, []string{""})
}

View File

@@ -0,0 +1,57 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/ls"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/testutil"
)
// TestAutoImportNodeBuiltinNodenext is a regression test for
// https://github.com/microsoft/typescript-go/issues/2555, where auto-imports
// for Node.js built-in modules (e.g. "fs") were not appearing when using
// module: nodenext and types: ["node"].
func TestAutoImportNodeBuiltinNodenext(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /tsconfig.json
{ "compilerOptions": { "module": "nodenext", "types": ["node"] } }
// @Filename: /package.json
{ "type": "module" }
// @Filename: /node_modules/@types/node/package.json
{ "name": "@types/node", "version": "22.0.0" }
// @Filename: /node_modules/@types/node/index.d.ts
declare module "fs" {
export function existsSync(path: string): boolean;
export function mkdirSync(path: string, options?: { recursive?: boolean }): void;
}
declare module "node:fs" { export * from "fs"; }
// @Filename: /index.ts
existsSync/**/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "existsSync",
Data: &lsproto.CompletionItemData{
AutoImport: &lsproto.AutoImportFix{
ModuleSpecifier: "node:fs",
},
},
AdditionalTextEdits: fourslash.AnyTextEdits,
SortText: new(string(ls.SortTextAutoImportSuggestions)),
},
},
},
})
}

View File

@@ -0,0 +1,61 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestAutoImportPackageJsonImportsHashSlashNodenext(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /tsconfig.json
{
"compilerOptions": {
"module": "nodenext",
"rootDir": "./",
"outDir": "build"
}
}
// @Filename: /package.json
{
"imports": {
"#/*": {
"types": "./src/*",
"default": "./src/*"
}
}
}
// @Filename: /src/domain/entities/entity.ts
export const entity = 1;
// @Filename: /src/features/deep/consumer.ts
entit/**/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.BaselineAutoImportsCompletions(t, []string{""})
}
func TestAutoImportPackageJsonImportsHashSlashNode16(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /tsconfig.json
{
"compilerOptions": {
"module": "node16"
}
}
// @Filename: /package.json
{
"imports": {
"#/*": "./src/*"
}
}
// @Filename: /src/domain/entities/entity.ts
export const entity = 1;
// @Filename: /src/consumer.ts
entit/**/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.BaselineAutoImportsCompletions(t, []string{""})
}

View File

@@ -0,0 +1,33 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestAutoImportQuoteDetection(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @module: esnext
// @Filename: /a.ts
export const foo = 0;
// @Filename: /b.ts
import {} from 'node:path';
fo/**/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "")
f.VerifyApplyCodeActionFromCompletion(t, new(""), &fourslash.ApplyCodeActionFromCompletionOptions{
Name: "foo",
Source: "./a",
Description: "Add import from \"./a\"",
NewFileContent: new(`import {} from 'node:path';
import { foo } from './a';
fo`),
})
}

View File

@@ -0,0 +1,49 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/ls/lsutil"
"github.com/microsoft/typescript-go/internal/testutil"
)
// TestAutoImportReexportOfCrossPackageAugmentation verifies no crash when auto-importing
// from a package that re-exports from another package that is also augmented.
func TestAutoImportReexportOfCrossPackageAugmentation(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /node_modules/vitest/package.json
{ "name": "vitest", "version": "1.0.0", "types": "index.d.ts" }
// @Filename: /node_modules/vitest/index.d.ts
export { AugmentedInterface, uniqueFunction } from "@vitest/expect";
// @Filename: /node_modules/vitest/augmentation.d.ts
export {};
declare module "@vitest/expect" {
interface AugmentedInterface {
bar: string;
}
function uniqueFunction(): void;
}
// @Filename: /node_modules/@vitest/expect/package.json
{ "name": "@vitest/expect", "version": "1.0.0", "types": "index.d.ts" }
// @Filename: /node_modules/@vitest/expect/index.d.ts
export interface AugmentedInterface {
baz: number;
}
// @Filename: /tsconfig.json
{ "compilerOptions": { "module": "commonjs", "strict": true } }
// @Filename: /package.json
{ "name": "test", "dependencies": { "vitest": "*" } }
// @Filename: /index.ts
uniqueFunction/**/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
prefs := lsutil.NewDefaultUserPreferences()
prefs.AutoImportEntrypointDirectorySearch = core.TSTrue
f.Configure(t, prefs)
f.GoToMarker(t, "")
f.BaselineAutoImportsCompletions(t, []string{""})
}

View File

@@ -0,0 +1,54 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/ls/lsutil"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestAutoImportSpecifierExcludeRegexes(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: foo.ts
export const mySymbol = 1;
// @Filename: ignoreme.ts
export const ignoredSymbol = 2;
// @Filename: bar.ts
mySym/*1*/
ignoredSym/*2*/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.Configure(t, lsutil.UserPreferences{
AutoImportSpecifierExcludeRegexes: []string{".*ignoreme.*"},
IncludeCompletionsForModuleExports: core.TSTrue,
IncludeCompletionsForImportStatements: core.TSTrue,
})
// Verify that mySymbol is included, but ignoredSymbol is excluded from completions
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{"mySymbol"},
},
})
f.VerifyCompletions(t, "2", &fourslash.CompletionsExpectedList{
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Excludes: []string{"ignoredSymbol"},
},
})
// Baseline the auto-imports
f.BaselineAutoImportsCompletions(t, []string{"1", "2"})
}

View File

@@ -0,0 +1,88 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
// TestAutoImportSymlinkedMonorepoGranularUpdate verifies that when a file in a symlinked
// project reference is edited, the auto-import index is updated efficiently via granular
// updates rather than rebuilding the entire node_modules bucket.
//
// Scenario:
// - project-a references project-b via project references (tsconfig)
// - project-b is symlinked into project-a's node_modules
// - Initially, project-b exports projectBValue
// - We get completions in project-a (builds the initial index)
// - We edit project-b to add a new export (newlyAddedFunction)
// - We get completions again in project-a
// - The new export should be available, proving the granular update worked
func TestAutoImportSymlinkedMonorepoGranularUpdate(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /packages/project-b/tsconfig.json
{
"compilerOptions": {
"composite": true,
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"module": "commonjs",
"strict": true
},
"include": ["src"]
}
// @Filename: /packages/project-b/package.json
{
"name": "project-b",
"version": "1.0.0",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
}
}
// @Filename: /packages/project-b/src/index.ts
export const projectBValue: number = 42;
/*projectBEdit*/
// @Filename: /packages/project-b/dist/index.d.ts
export declare const projectBValue: number;
// @Filename: /packages/project-a/tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"strict": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src"],
"references": [{ "path": "../project-b" }]
}
// @Filename: /packages/project-a/package.json
{ "name": "project-a", "dependencies": { "project-b": "*" } }
// @Filename: /packages/project-a/src/index.ts
import { projectBValue } from "project-b";
console.log(projectBValue);
newlyAdded/*projectACompletion*/
// @link: /packages/project-b -> /packages/project-a/node_modules/project-b`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
// Get initial completions in project-a - this builds the initial auto-import index.
// At this point, newlyAddedFunction doesn't exist yet in project-b.
f.GoToMarker(t, "projectACompletion")
f.BaselineAutoImportsCompletions(t, []string{"projectACompletion"})
// Now edit project-b's source file to add a new export.
// This should trigger a granular update when we request completions again.
f.GoToMarker(t, "projectBEdit")
f.Insert(t, "\nexport function newlyAddedFunction(): void {}")
// Go back to project-a and request completions again.
// The granular update should have picked up the new export from project-b.
f.GoToMarker(t, "projectACompletion")
f.BaselineAutoImportsCompletions(t, []string{"projectACompletion"})
}

View File

@@ -0,0 +1,73 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
// TestAutoImportSymlinkedMonorepoProjectReferencesNoPkgExports verifies deduplication
// when a monorepo package has both src and dist files, uses project references,
// but does NOT have package.json "exports" to limit what files are discovered.
//
// Scenario:
// - project-a references project-b via project references (tsconfig)
// - project-b is also symlinked into project-a's node_modules
// - project-b has both src/index.ts and dist/index.d.ts
// - package.json does NOT have "exports", so node_modules search finds BOTH src and dist
// - The program resolves imports to src files via project reference mapping
// - We should NOT see duplicate auto-imports (one from src, one from dist)
func TestAutoImportSymlinkedMonorepoProjectReferencesNoPkgExports(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /packages/project-b/tsconfig.json
{
"compilerOptions": {
"composite": true,
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"module": "commonjs",
"strict": true
},
"include": ["src"]
}
// @Filename: /packages/project-b/package.json
{
"name": "project-b",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts"
}
// @Filename: /packages/project-b/src/index.ts
export const projectBValue: number = 42;
export function projectBFunction(): string { return "hello"; }
// @Filename: /packages/project-b/dist/index.d.ts
export declare const projectBValue: number;
export declare function projectBFunction(): string;
// @Filename: /packages/project-a/tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"strict": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src"],
"references": [{ "path": "../project-b" }]
}
// @Filename: /packages/project-a/package.json
{ "name": "project-a", "dependencies": { "project-b": "*" } }
// @Filename: /packages/project-a/src/index.ts
import { projectBValue } from "project-b";
console.log(projectBValue);
projectBFunc/**/
// @link: /packages/project-b -> /packages/project-a/node_modules/project-b`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "")
// This should show projectBFunction once, not twice (not from both src and dist)
f.BaselineAutoImportsCompletions(t, []string{""})
}

View File

@@ -0,0 +1,78 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
// TestAutoImportSymlinkedMonorepoProjectReferences verifies that when a monorepo package
// is symlinked into node_modules and has both src and dist files with project references,
// auto-imports don't show duplicates.
//
// Scenario:
// - project-a references project-b via project references (tsconfig)
// - project-b is also symlinked into project-a's node_modules
// - project-b has both src/index.ts and dist/index.d.ts
// - package.json "exports" points to dist files
// - The node_modules bucket finds dist/.d.ts files
// - The program resolves to src files via project reference mapping
// - We should NOT see duplicate auto-imports (one from src, one from dist)
func TestAutoImportSymlinkedMonorepoProjectReferences(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /packages/project-b/tsconfig.json
{
"compilerOptions": {
"composite": true,
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"module": "commonjs",
"strict": true
},
"include": ["src"]
}
// @Filename: /packages/project-b/package.json
{
"name": "project-b",
"version": "1.0.0",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
}
}
// @Filename: /packages/project-b/src/index.ts
export const projectBValue: number = 42;
export function projectBFunction(): string { return "hello"; }
// @Filename: /packages/project-b/dist/index.d.ts
export declare const projectBValue: number;
export declare function projectBFunction(): string;
// @Filename: /packages/project-a/tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"strict": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src"],
"references": [{ "path": "../project-b" }]
}
// @Filename: /packages/project-a/package.json
{ "name": "project-a", "dependencies": { "project-b": "*" } }
// @Filename: /packages/project-a/src/index.ts
import { projectBValue } from "project-b";
console.log(projectBValue);
projectBFunc/**/
// @link: /packages/project-b -> /packages/project-a/node_modules/project-b`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "")
// This should show projectBFunction once, not twice (not from both src and dist)
f.BaselineAutoImportsCompletions(t, []string{""})
}

View File

@@ -0,0 +1,90 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/ls/lsutil"
"github.com/microsoft/typescript-go/internal/testutil"
)
// TestAutoImportSymlinkedMonorepoReexport verifies deduplication when a monorepo
// package re-exports symbols from deeper files via `export *`.
//
// Scenario:
// - project-a references project-b via project references (tsconfig)
// - project-b is symlinked into project-a's node_modules
// - project-b has src/utils/foo.ts with the actual export
// - project-b has src/index.ts with `export * from './utils/foo'`
// - The Export's Path field points to src/utils/foo.ts (where symbol is defined)
// - This path must also go through realpath normalization for deduplication
// - We should NOT see duplicate auto-imports
func TestAutoImportSymlinkedMonorepoReexport(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /packages/project-b/tsconfig.json
{
"compilerOptions": {
"composite": true,
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"module": "commonjs",
"strict": true
},
"include": ["src"]
}
// @Filename: /packages/project-b/package.json
{
"name": "project-b",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts"
}
// @Filename: /packages/project-b/src/utils/foo.ts
export function projectBFunction(): string { return "hello"; }
// @Filename: /packages/project-b/src/index.ts
export * from './utils/foo';
export const projectBValue: number = 42;
// @Filename: /packages/project-b/dist/utils/foo.d.ts
export declare function projectBFunction(): string;
// @Filename: /packages/project-b/dist/index.d.ts
export * from './utils/foo';
export declare const projectBValue: number;
// @Filename: /packages/project-a/tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"strict": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src"],
"references": [{ "path": "../project-b" }]
}
// @Filename: /packages/project-a/package.json
{ "name": "project-a", "dependencies": { "project-b": "*" } }
// @Filename: /packages/project-a/src/index.ts
import { projectBValue } from "project-b";
console.log(projectBValue);
projectBFunction/**/
// @link: /packages/project-b -> /packages/project-a/node_modules/project-b`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
prefs := lsutil.NewDefaultUserPreferences()
prefs.AutoImportEntrypointDirectorySearch = core.TSTrue
f.Configure(t, prefs)
f.GoToMarker(t, "")
// This should show projectBFunction once via re-export and once via direct import, not duplicates
f.VerifyImportFixAtPosition(t, []string{
`import { projectBFunction, projectBValue } from "project-b";
console.log(projectBValue);
projectBFunction`,
`import { projectBValue } from "project-b";
import { projectBFunction } from "project-b/src/utils/foo";
console.log(projectBValue);
projectBFunction`,
}, nil /*preferences*/)
}

View File

@@ -0,0 +1,86 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
const TestAutoImportSymlinkedMonorepoSourceUpdateScenario = `
// @Filename: /home/src/workspaces/project/tsconfig.base.json
{
"compilerOptions": {
"module": "nodenext",
"moduleResolution": "nodenext",
"composite": true
}
}
// @Filename: /home/src/workspaces/project/packages/foo/package.json
{
"name": "@packages/foo",
"type": "module",
"exports": {
".": {
"types": "./src/index.ts",
"default": "./dist/index.js"
}
}
}
// @Filename: /home/src/workspaces/project/packages/foo/tsconfig.json
{ "extends": "../../tsconfig.base.json" }
// @Filename: /home/src/workspaces/project/packages/foo/src/index.ts
/*fooEdit*/
// @Filename: /home/src/workspaces/project/packages/bar/package.json
{
"name": "@packages/bar",
"type": "module",
"exports": {
".": {
"types": "./src/index.ts",
"default": "./dist/index.js"
}
},
"dependencies": {
"@packages/foo": "*"
}
}
// @Filename: /home/src/workspaces/project/packages/bar/tsconfig.json
{ "extends": "../../tsconfig.base.json" }
// @Filename: /home/src/workspaces/project/packages/bar/src/index.ts
/*fooCompletion*/
// @Filename: /home/src/workspaces/project/package.json
{ "workspaces": ["packages/*"], "type": "module" }
// @link: /home/src/workspaces/project/packages/bar -> /home/src/workspaces/project/node_modules/@packages/bar
// @link: /home/src/workspaces/project/packages/foo -> /home/src/workspaces/project/node_modules/@packages/foo
`
func TestAutoImportSymlinkedMonorepoSourceUpdate(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, TestAutoImportSymlinkedMonorepoSourceUpdateScenario)
defer done()
// Force auto import to build the cache (no exports yet).
f.GoToMarker(t, "fooCompletion")
f.BaselineAutoImportsCompletions(t, []string{"fooCompletion"})
// Add a new export to the symlinked source package.
f.GoToMarker(t, "fooEdit")
f.Insert(t, "\nexport function foo() {}")
// The new export should appear via granular cache update.
f.GoToMarker(t, "fooCompletion")
f.BaselineAutoImportsCompletions(t, []string{"fooCompletion"})
}

View File

@@ -0,0 +1,42 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
// TestAutoImportSymlinkedMonorepo verifies that when a monorepo package is symlinked
// into node_modules and imported, auto-imports don't show duplicates.
//
// Scenario:
// - project-a imports "project-b" via a symlink in node_modules
// - project-b's source files are at /packages/project-b but symlinked to
// /packages/project-a/node_modules/project-b
// - The program contains project-b files via realpath, AND the symlink exists in node_modules
// - We should NOT see duplicate auto-imports for project-b exports
func TestAutoImportSymlinkedMonorepo(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /packages/project-b/package.json
{ "name": "project-b", "version": "1.0.0", "main": "index.js", "types": "index.d.ts" }
// @Filename: /packages/project-b/index.d.ts
export declare const projectBValue: number;
export declare function projectBFunction(): string;
// @Filename: /packages/project-a/tsconfig.json
{ "compilerOptions": { "module": "commonjs", "strict": true } }
// @Filename: /packages/project-a/package.json
{ "name": "project-a", "dependencies": { "project-b": "*" } }
// @Filename: /packages/project-a/index.ts
import { projectBValue } from "project-b";
console.log(projectBValue);
projectBFunc/**/
// @link: /packages/project-b -> /packages/project-a/node_modules/project-b`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "")
// This should show projectBFunction once, not twice
f.BaselineAutoImportsCompletions(t, []string{""})
}

View File

@@ -0,0 +1,117 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/ls"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/testutil"
)
const TestAutoImportTransitiveLeakScenario = `
// @Filename: /home/src/workspaces/project/tsconfig.base.json
{
"compilerOptions": {
"module": "nodenext",
"moduleResolution": "nodenext",
"composite": true
}
}
// @Filename: /home/src/workspaces/project/packages/foo/package.json
{
"name": "@packages/foo",
"type": "module",
"exports": {
".": {
"types": "./src/index.ts",
"default": "./dist/index.js"
}
},
"imports": {
"#*": {
"types": "./src/*.ts",
"default": "./dist/*.js"
}
}
}
// @Filename: /home/src/workspaces/project/packages/foo/tsconfig.json
{ "extends": "../../tsconfig.base.json" }
// @Filename: /home/src/workspaces/project/packages/foo/src/internal/index.ts
export function fooInternal() {
console.log("foo");
}
// @Filename: /home/src/workspaces/project/packages/foo/src/index.ts
import { fooInternal } from "#internal/index"
export function foo() {
fooInternal();
}
// @Filename: /home/src/workspaces/project/packages/bar/package.json
{
"name": "@packages/bar",
"type": "module",
"exports": {
".": {
"types": "./src/index.ts",
"default": "./dist/index.js"
}
},
"imports": {
"#*": {
"types": "./src/*.ts",
"default": "./dist/*.js"
}
},
"dependencies": {
"@packages/foo": "*"
}
}
// @Filename: /home/src/workspaces/project/packages/bar/tsconfig.json
{ "extends": "../../tsconfig.base.json" }
// @Filename: /home/src/workspaces/project/packages/bar/src/index.ts
import { foo } from "@packages/foo"
fo/*fooCompletion*/
// @Filename: /home/src/workspaces/project/package.json
{ "workspaces": ["packages/*"], "type": "module" }
// @link: /home/src/workspaces/project/packages/bar -> /home/src/workspaces/project/node_modules/@packages/bar
// @link: /home/src/workspaces/project/packages/foo -> /home/src/workspaces/project/node_modules/@packages/foo
`
func TestAutoImportTransitiveLeak(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, TestAutoImportTransitiveLeakScenario)
defer done()
f.VerifyCompletions(t, "fooCompletion", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "foo",
AdditionalTextEdits: fourslash.AnyTextEdits,
SortText: new(string(ls.SortTextLocationPriority)),
},
},
Excludes: []string{"fooInternal"},
},
})
}

View File

@@ -0,0 +1,25 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestAutoImportTypedefMissingName(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @allowJs: true
// @checkJs: true
// @Filename: /utils.js
/** @typedef {{ x: number }} */
export function doSomething() {}
// @Filename: /index.ts
doSomething/**/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "")
f.BaselineAutoImportsCompletions(t, []string{""})
}

View File

@@ -0,0 +1,43 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestBasicBackspace(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `export {};
interface Point {
x: number;
y: number;/*b*/
}
declare const p: Point;
p./*a*/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "a", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{"y"},
},
})
f.GoToMarker(t, "b")
f.Backspace(t, 10) // `y: number;`
f.VerifyCompletions(t, "a", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Excludes: []string{"y"},
},
})
}

View File

@@ -0,0 +1,28 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestBasicClassElementKeywords(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `class C {
/*a*/
}`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "a", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &[]string{},
},
Items: &fourslash.CompletionsExpectedItems{
Exact: CompletionClassElementKeywords,
},
})
}

View File

@@ -0,0 +1,44 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/ls"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestBasicEdit(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `export {};
interface Point {
x: number;
y: number;
}
declare const p: Point;
p/*a*/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "a")
f.Insert(t, ".")
f.GoToEOF(t)
f.VerifyCompletions(t, nil, &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Exact: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "x",
Kind: new(lsproto.CompletionItemKindField),
SortText: new(string(ls.SortTextLocationPriority)),
},
"y",
},
},
})
}

View File

@@ -0,0 +1,27 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestBasicGlobalCompletions(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @lib: es5
/*1*/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Exact: CompletionGlobals,
},
})
}

View File

@@ -0,0 +1,41 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/ls"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestBasicInterfaceMembers(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `export {};
interface Point {
x: number;
y: number;
}
declare const p: Point;
p./*a*/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "a", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Exact: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "x",
Kind: new(lsproto.CompletionItemKindField),
SortText: new(string(ls.SortTextLocationPriority)),
},
"y",
},
},
})
}

View File

@@ -0,0 +1,124 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestBasicJSDocCompletions(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
// @filename: file.js
// @allowJs: true
/**
* @/*1*/
*/
function foo(x) {
return x + 1;
}
/**
* /*2*/
*/
function bar(x, { y }) {
return x + y;
}
/**
* @param {number} x
* /*3*/
*/
function baz(x, { y }) {
return x + y;
}
/**
* @param {number} x
* @param {object} param1
* @param {n/*4*/} param1.y
*/
function baz(x, { y }) {
return x + y;
}
/**
* @/*5*/
*/
function baz(x = 0) {
return x * 2;
}
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "link",
Kind: new(lsproto.CompletionItemKindKeyword),
Detail: new("link"),
},
"param",
"returns",
"param {*} x ",
},
},
})
f.VerifyCompletions(t, "2", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
"@param",
"@param {*} x ",
},
},
})
f.VerifyCompletions(t, "3", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
"@param",
"@param {object} param1 \n* @param {*} param1.y ",
},
},
})
f.VerifyCompletions(t, "4", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
"number",
},
},
})
f.VerifyCompletions(t, "5", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
"param {number} [x=0] ",
},
},
})
}

View File

@@ -0,0 +1,39 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/ls"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestBasicMultifileCompletions(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /a.ts
export const foo = { bar: 'baz' };
// @Filename: /b.ts
import { foo } from './a';
const test = foo./*1*/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "bar",
Kind: new(lsproto.CompletionItemKindField),
SortText: new(string(ls.SortTextLocationPriority)),
},
},
},
})
}

View File

@@ -0,0 +1,33 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestBasicQuickInfo(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
/**
* Some var
*/
var someVar/*1*/ = 123;
/**
* Other var
* See {@link someVar}
*/
var otherVar/*2*/ = someVar;
class Foo/*3*/ {
#bar: string;
}
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyQuickInfoAt(t, "1", "var someVar: number", "Some var")
f.VerifyQuickInfoAt(t, "2", "var otherVar: number", "Other var\nSee [someVar](file:///basicQuickInfo.ts#5,5-5,12)")
}

View File

@@ -0,0 +1,43 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestBasicReplaceLine(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `export {};
interface Point {
x: number;
y: number;
}
declare const p: Point;
p./*a*/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "a", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{"y"},
},
})
f.ReplaceLine(t, 3, " z: number;") // `y: number;`
f.VerifyCompletions(t, "a", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Excludes: []string{"y"},
Includes: []fourslash.CompletionsExpectedItem{"z"},
},
})
}

View File

@@ -0,0 +1,21 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCallHierarchyAnonymousClassNoCrash1(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /main.ts
class {
con/*1*/structor() {}
}`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "1")
f.VerifyBaselineCallHierarchy(t)
}

View File

@@ -0,0 +1,21 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCallHierarchyAnonymousClassNoCrash2(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /main.ts
(class {
con/*1*/structor() {}
})`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "1")
f.VerifyBaselineCallHierarchy(t)
}

View File

@@ -0,0 +1,27 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCallHierarchyAnonymousClassNoCrash3(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /main.ts
import Bar from "./other";
function foo() {
new /*1*/Bar();
}
// @Filename: /other.ts
export default class {
constructor() {}
}`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "1")
f.VerifyBaselineCallHierarchy(t)
}

View File

@@ -0,0 +1,19 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCallHierarchyAnonymousFunctionNoCrash1(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /main.ts
func/*1*/tion() {}`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "1")
f.VerifyBaselineCallHierarchy(t)
}

View File

@@ -0,0 +1,19 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCallHierarchyAnonymousFunctionNoCrash2(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /main.ts
(func/*1*/tion() {})`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "1")
f.VerifyBaselineCallHierarchy(t)
}

View File

@@ -0,0 +1,25 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCallHierarchyAnonymousFunctionNoCrash3(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /main.ts
import bar from "./other";
function foo() {
/*1*/bar();
}
// @Filename: /other.ts
export default function() {}`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "1")
f.VerifyBaselineCallHierarchy(t)
}

View File

@@ -0,0 +1,25 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCallHierarchyInPropDeclarationOfExportedDefaultClass1(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @Filename: /main.ts
export default class {
onSave = () => {
const values = [];
values./*m1*/push(1);
};
}
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "m1")
f.VerifyBaselineCallHierarchy(t)
}

View File

@@ -0,0 +1,33 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCallHierarchyIncomingCallsNoCrashArrayPush(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `function splitNames(name: string) {
return (name || "").split(",").filter(Boolean);
}
async function trim(packageNames: string[]) {
const nameOrPkgs = packageNames.filter(Boolean);
const names = [];
for (const nameOrPkg of nameOrPkgs) {
try {
names./*push*/push(nameOrPkg);
} catch (error) {
}
}
return names;
}
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "push")
f.VerifyBaselineCallHierarchy(t)
}

View File

@@ -0,0 +1,25 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCallHierarchyIncomingCallsObjectLiteralMethodInExpressionComputedProperty(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `const obj = {
[1 + 2]: {
method() {
return ""./*split*/split(",");
}
}
};
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "split")
f.VerifyBaselineCallHierarchy(t)
}

View File

@@ -0,0 +1,26 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCallHierarchyIncomingCallsObjectLiteralMethodInIdentifierComputedProperty(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `const key = "x";
const obj = {
[key]: {
method() {
return ""./*split*/split(",");
}
}
};
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "split")
f.VerifyBaselineCallHierarchy(t)
}

View File

@@ -0,0 +1,25 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCallHierarchyIncomingCallsObjectLiteralMethodInStringLiteralComputedProperty(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `const obj = {
["x"]: {
method() {
return ""./*split*/split(",");
}
}
};
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "split")
f.VerifyBaselineCallHierarchy(t)
}

View File

@@ -0,0 +1,29 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCallHierarchyUnclosedTemplateExprNoCrash1(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
// Regression test for a crash in prepareCallHierarchy caused by parser error
// recovery: when a template expression is truncated mid-call (e.g. `${format`
// without closing `)`), the parser misinterprets the `class` keyword in
// subsequent HTML template literals as a TypeScript class declaration.
// The resulting anonymous ClassDeclaration (no name, no `default` modifier)
// previously caused a "Expected call hierarchy declaration to have a reference
// node" assertion failure.
const content = "// @Filename: /main.ts\n" +
"function updateBadge() {\n" +
" const header = `<div class=\"sub\">${format`;\n" +
" const badge = `<div /*1*/class=\"badge\">`;\n" +
"}"
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "1")
f.VerifyBaselineCallHierarchy(t)
}

View File

@@ -0,0 +1,52 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestChineseCharacterDisplayInHover(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
interface 中文界面 {
上居中: string;
下居中: string;
}
class 中文类 {
获取中文属性(): 中文界面 {
return {
上居中: "上居中",
下居中: "下居中"
};
}
}
let /*instanceHover*/实例 = new 中文类();
let 属性对象 = 实例./*methodHover*/获取中文属性();`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyQuickInfoAt(t, "instanceHover", "let 实例: 中文类", "")
f.VerifyQuickInfoAt(t, "methodHover", "(method) 中文类.获取中文属性(): 中文界面", "")
}
func TestChineseCharacterDisplayInUnionTypes(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
// Test the original issue: Chinese characters in method parameters should display correctly
class TSLine {
setLengthTextPositionPreset(/*methodParam*/preset: "上居中" | "下居中" | "右居中" | "左居中"): void {}
}
let lines = new TSLine();
lines./*method*/setLengthTextPositionPreset;`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
// Verify that the method displays Chinese characters correctly in hover (this was the original problem)
f.VerifyQuickInfoAt(t, "method", `(method) TSLine.setLengthTextPositionPreset(preset: "上居中" | "下居中" | "右居中" | "左居中"): void`, "")
}

View File

@@ -0,0 +1,27 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
// https://github.com/microsoft/typescript-go/issues/3944
func TestCodeFixImportNonTextualSpecifierText(t *testing.T) {
fourslash.SkipIfFailing(t)
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = "// @Filename: /a.ts\n" +
"import type { A } from `./${myFolder}/${myFile}`;\n" +
"\n" +
"new A/**/()"
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "")
f.VerifyImportFixAtPosition(t, []string{
"import { A } from `./${myFolder}/${myFile}`;\n" +
"\n" +
"new A()",
}, nil /*preferences*/)
}

View File

@@ -0,0 +1,24 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCodeFixMissingTypeAnnotationOnExports_arrowParensParamOnly(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @isolatedDeclarations: true
// @declaration: true
export const func = /*a*/x/*b*/ => 0;`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "a")
f.VerifyCodeFix(t, fourslash.VerifyCodeFixOptions{
Description: `Add annotation of type 'any'`,
NewFileContent: `export const func = (x: any) => 0;`,
ApplyChanges: true,
})
}

View File

@@ -0,0 +1,22 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCodeFixMissingTypeAnnotationOnExports_arrowParens(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @isolatedDeclarations: true
// @declaration: true
export const func = x => x.substring("foo");`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCodeFixAll(t, fourslash.VerifyCodeFixAllOptions{
FixID: "fixMissingTypeAnnotationOnExports",
NewFileContent: `export const func = (x: any): any => x.substring("foo");`,
})
}

View File

@@ -0,0 +1,38 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCodeFixMissingTypeAnnotationOnExports_expandoNoDuplicates(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @declaration: true
// @isolatedDeclarations: true
// @Filename: /foo.mts
export function foo(): void {
}
foo.blah = 123;`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
// Verify that only one code fix action is returned, not three identical ones.
f.VerifyCodeFixAvailableExact(t, []string{
"Annotate types of properties expando function in a namespace",
})
f.VerifyCodeFix(t, fourslash.VerifyCodeFixOptions{
Description: "Annotate types of properties expando function in a namespace",
NewFileContent: `export function foo(): void {
}
export declare namespace foo {
export var blah: number;
}
foo.blah = 123;`,
})
}

View File

@@ -0,0 +1,26 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCodeFixMissingTypeAnnotationOnExports_jsxWhitespaceText(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @isolatedDeclarations: true
// @declaration: true
// @module: preserve
// @Filename: /test.tsx
export const /**/elem = <div>
<span />
</div>;`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "")
f.VerifyCodeFixAvailable(t, nil)
}

View File

@@ -0,0 +1,75 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
// Test that auto-imports for JSX tags don't crash when React is type-imported.
// When both the JSX namespace (React) and the component need to be imported,
// getSymbolNamesToImport returns multiple names and the type-only promotion
// path should handle this gracefully instead of panicking.
func TestCodeFixPromoteTypeOnlyImportJsxTag(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @module: preserve
// @verbatimModuleSyntax: true
// @jsx: react
// @Filename: /react.ts
const React: any = {};
export default React;
// @Filename: /bar.tsx
import type React from "./react";
<Foo/**/ />;`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "")
// The fix should promote the type-only import of React to a regular import.
// The "Cannot find name 'Foo'" error does not produce an auto-import for
// React since it's already imported (as type-only, handled by promotion).
f.VerifyImportFixAtPosition(t, []string{
`import React from "./react";
<Foo />;`,
}, nil /*preferences*/)
}
// Test edge case where both the component name (Foo) and the JSX namespace (React)
// are type-only imported. Each diagnostic is matched to its symbol via the error
// message, so each produces only its own promotion fix (no duplicates).
func TestCodeFixPromoteTypeOnlyImportJsxTagBothTypeOnly(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @module: preserve
// @verbatimModuleSyntax: true
// @jsx: react
// @Filename: /react.ts
const React: any = {};
export default React;
// @Filename: /foo.ts
export function Foo() { return null; }
// @Filename: /bar.tsx
import type React from "./react";
import type { Foo } from "./foo";
<Foo/**/ />;`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "")
// Both Foo and React are type-only imported. The error message string
// matching disambiguates which diagnostic is about which symbol, so each
// diagnostic produces only its own promotion fix (no duplicates).
f.VerifyImportFixAtPosition(t, []string{
`import type React from "./react";
import { Foo } from "./foo";
<Foo />;`,
`import React from "./react";
import type { Foo } from "./foo";
<Foo />;`,
}, nil /*preferences*/)
}

View File

@@ -0,0 +1,41 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
// Test case for crash when promoting type-only import to value import
// when existing type imports precede the new value import
// https://github.com/microsoft/typescript-go/issues/2559
func TestCodeFixPromoteTypeOnlyOrderingCrash(t *testing.T) {
fourslash.SkipIfFailing(t)
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @module: node18
// @verbatimModuleSyntax: true
// @Filename: /bar.ts
export interface AAA {}
export class BBB {}
// @Filename: /foo.ts
import type {
AAA,
BBB,
} from "./bar";
let x: AAA = new BBB()`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToFile(t, "/foo.ts")
f.VerifyImportFixAtPosition(t, []string{
`import {
BBB,
type AAA,
} from "./bar";
let x: AAA = new BBB()`,
}, nil /*preferences*/)
}

View File

@@ -0,0 +1,57 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/ls/lsutil"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCodeLensFunctionExpressions01(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
// @filename: anonymousFunctionExpressions.ts
export let anonFn1 = function () {};
export const anonFn2 = function () {};
let anonFn3 = function () {};
const anonFn4 = function () {};
// @filename: arrowFunctions.ts
export let arrowFn1 = () => {};
export const arrowFn2 = () => {};
let arrowFn3 = () => {};
const arrowFn4 = () => {};
// @filename: namedFunctions.ts
export let namedFn1 = function namedFn1() {
namedFn1();
}
namedFn1();
export const namedFn2 = function namedFn2() {
namedFn2();
}
namedFn2();
let namedFn3 = function namedFn3() {};
const namedFn4 = function namedFn4() {};
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyBaselineCodeLens(t, &lsutil.UserPreferences{
CodeLens: lsutil.CodeLensUserPreferences{
ReferencesCodeLensEnabled: core.TSTrue,
ReferencesCodeLensShowOnAllFunctions: core.TSTrue,
ImplementationsCodeLensEnabled: core.TSTrue,
ImplementationsCodeLensShowOnInterfaceMethods: core.TSTrue,
ImplementationsCodeLensShowOnAllClassMethods: core.TSTrue,
},
})
}

View File

@@ -0,0 +1,54 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/ls/lsutil"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCodeLensFunctionsAndConstants01(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
// @module: preserve
// @filename: ./exports.ts
let callCount = 0;
export function foo(n: number): void {
callCount++;
if (n > 0) {
foo(n - 1);
}
else {
console.log("function was called " + callCount + " times");
}
}
foo(5);
export const bar = 123;
// @filename: ./importer.ts
import { foo, bar } from "./exports";
foo(5);
console.log(bar);
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyBaselineCodeLens(t, &lsutil.UserPreferences{
CodeLens: lsutil.CodeLensUserPreferences{
ReferencesCodeLensEnabled: core.TSTrue,
ReferencesCodeLensShowOnAllFunctions: core.TSTrue,
ImplementationsCodeLensEnabled: core.TSTrue,
ImplementationsCodeLensShowOnInterfaceMethods: core.TSTrue,
ImplementationsCodeLensShowOnAllClassMethods: core.TSTrue,
},
})
}

View File

@@ -0,0 +1,63 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/ls/lsutil"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCodeLensInterface01(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
// @module: preserve
// @filename: ./pointable.ts
export interface Pointable {
getX(): number;
getY(): number;
}
// @filename: ./classPointable.ts
import { Pointable } from "./pointable";
class Point implements Pointable {
getX(): number {
return 0;
}
getY(): number {
return 0;
}
}
// @filename: ./objectPointable.ts
import { Pointable } from "./pointable";
let x = 0;
let y = 0;
const p: Pointable = {
getX(): number {
return x;
},
getY(): number {
return y;
},
};
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyBaselineCodeLens(t, &lsutil.UserPreferences{
CodeLens: lsutil.CodeLensUserPreferences{
ReferencesCodeLensEnabled: core.TSTrue,
ReferencesCodeLensShowOnAllFunctions: core.TSTrue,
ImplementationsCodeLensEnabled: core.TSTrue,
ImplementationsCodeLensShowOnInterfaceMethods: core.TSTrue,
ImplementationsCodeLensShowOnAllClassMethods: core.TSTrue,
},
})
}

View File

@@ -0,0 +1,43 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/ls/lsutil"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCodeLensOverloads01(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
export function foo(x: number): number;
export function foo(x: string): string;
export function foo(x: string | number): string | number {
return x;
}
foo(1);
foo("hello");
// This one isn't expected to match any overload,
// but is really just here to test how it affects how code lens.
foo(Math.random() ? 1 : "hello");
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyBaselineCodeLens(t, &lsutil.UserPreferences{
CodeLens: lsutil.CodeLensUserPreferences{
ReferencesCodeLensEnabled: core.TSTrue,
ReferencesCodeLensShowOnAllFunctions: core.TSTrue,
ImplementationsCodeLensEnabled: core.TSTrue,
ImplementationsCodeLensShowOnInterfaceMethods: core.TSTrue,
ImplementationsCodeLensShowOnAllClassMethods: core.TSTrue,
},
})
}

View File

@@ -0,0 +1,47 @@
package fourslash_test
import (
"fmt"
"testing"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/ls/lsutil"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCodeLensReferencesShowOnAllClassMethods(t *testing.T) {
t.Parallel()
containingTestName := t.Name()
for _, value := range []core.Tristate{core.TSTrue, core.TSFalse} {
t.Run(fmt.Sprintf("%s=%v", containingTestName, value.IsTrue()), func(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
export abstract class ABC {
abstract methodA(): void;
methodB(): void {}
#methodC(): void {}
protected methodD(): void {}
private methodE(): void {}
protected abstract methodG(): void;
public methodH(): void {}
static methodStaticA(): void {}
protected static methodStaticB(): void {}
private static methodStaticC(): void {}
static #methodStaticD(): void {}
}
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyBaselineCodeLens(t, &lsutil.UserPreferences{
CodeLens: lsutil.CodeLensUserPreferences{
ImplementationsCodeLensEnabled: core.TSTrue,
ImplementationsCodeLensShowOnAllClassMethods: value,
},
})
})
}
}

View File

@@ -0,0 +1,42 @@
package fourslash_test
import (
"fmt"
"testing"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/ls/lsutil"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCodeLensReferencesShowOnAllFunctions(t *testing.T) {
t.Parallel()
containingTestName := t.Name()
for _, value := range []core.Tristate{core.TSTrue, core.TSFalse} {
t.Run(fmt.Sprintf("%s=%v", containingTestName, value.IsTrue()), func(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
export function f1(): void {}
function f2(): void {}
export const f3 = () => {};
const f4 = () => {};
const f5 = function() {};
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyBaselineCodeLens(t, &lsutil.UserPreferences{
CodeLens: lsutil.CodeLensUserPreferences{
ReferencesCodeLensEnabled: core.TSTrue,
ReferencesCodeLensShowOnAllFunctions: value,
},
})
})
}
}

View File

@@ -0,0 +1,56 @@
package fourslash_test
import (
"fmt"
"testing"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/ls/lsutil"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCodeLensReferencesShowOnInterfaceMethods(t *testing.T) {
t.Parallel()
containingTestName := t.Name()
for _, value := range []core.Tristate{core.TSTrue, core.TSFalse} {
t.Run(fmt.Sprintf("%s=%v", containingTestName, value.IsTrue()), func(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
export interface I {
methodA(): void;
}
export interface I {
methodB(): void;
}
interface J extends I {
methodB(): void;
methodC(): void;
}
class C implements J {
methodA(): void {}
methodB(): void {}
methodC(): void {}
}
class AbstractC implements J {
abstract methodA(): void;
methodB(): void {}
abstract methodC(): void;
}
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyBaselineCodeLens(t, &lsutil.UserPreferences{
CodeLens: lsutil.CodeLensUserPreferences{
ImplementationsCodeLensEnabled: core.TSTrue,
ImplementationsCodeLensShowOnInterfaceMethods: value,
},
})
})
}
}

View File

@@ -0,0 +1,32 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCompletionAfterCallExpression(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `let x = someCall() /**/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
"satisfies",
"as",
},
},
})
}

View File

@@ -0,0 +1,43 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCompletionAfterExtendsL10nInJs(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
// @allowJs: true
// @checkJs: true
// @Filename: /interfaces.d.ts
export interface IL10n {}
// @Filename: /genericl10n.js
/** @typedef {import("./interfaces").IL10n} IL10n */
class L10n {
constructor(options) {
this.options = options;
}
}
/**
* @implements {IL10n}
*/
class GenericL10n extends L10n/*1*/ {
constructor(lang) {
super({ lang });
}
}
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToMarker(t, "1")
f.GetCompletions(t, nil /*userPreferences*/)
}

View File

@@ -0,0 +1,52 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCompletionAfterTrailingAtInJSDoc1(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @allowJs: true
// @Filename: /atTagPosition.js
/**
* @/*1*/
*/
function foo(x) {}
// @Filename: /atAfterExistingParam.js
/**
* @param {string} x ok
* @/*2*/
*/
function bar(x, y) {}
// @Filename: /atMidLine.js
/**
* some text @/*3*/
*/
function baz(y) {}
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, []string{"1", "2", "3"}, &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "param",
Kind: new(lsproto.CompletionItemKindKeyword),
},
},
},
})
}

View File

@@ -0,0 +1,38 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCompletionClassMemberAfterJSDocWithInvalidJSDocTagInTheComment1(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `export class NeedsPrefix {
private _prefixes: {
add: Record<string, any>;
browsers: {selected: string[]};
};
constructor(browsers: string[]) {
}
/** Checks whether an @-rule needs to be prefixed. */
/**/atRule(identifier: string): boolean {
return true;
}
}`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &[]string{},
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{},
})
}

View File

@@ -0,0 +1,46 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/ls"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestClassMembersAfterConstAssertionInitializer(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
interface A {
a: number
def: string
}
class B implements A {
a = 1 as const
/**/
}
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &[]string{},
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Exact: append([]fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "def",
Kind: new(lsproto.CompletionItemKindField),
SortText: new(string(ls.SortTextLocationPriority)),
},
}, CompletionClassElementKeywords...),
},
})
}

View File

@@ -0,0 +1,40 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCompletionColonToken(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
// @filename: /a.ts
:/*a*/
// @filename: /b.ts
function b(class: /*b*/) {}
// @filename: /c.ts
function c(enum: /*c*/) {}
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
for _, marker := range f.Ranges() {
f.VerifyCompletions(t, marker, &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: CompletionGlobals,
},
})
}
}

View File

@@ -0,0 +1,43 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/ls"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCompletionDetailSignature(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
/*a*/
function foo(x: string): string;
function foo(x: number): number;
function foo(x: any): any {
return x;
}`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "a", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "foo",
Kind: new(lsproto.CompletionItemKindFunction),
SortText: new(string(ls.SortTextLocationPriority)),
Detail: new("function foo(x: string): string\nfunction foo(x: number): number"),
},
},
},
})
}

View File

@@ -0,0 +1,236 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/ls"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCompletionFilterText1(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
class Foo1 {
#bar: number;
constructor(bar: number) {
this.[|b|]/*1*/
}
}
class Foo5 {
#bar: number;
constructor(bar: number) {
this./*5*/
}
}
class Foo2 {
#bar: number;
constructor(bar: number) {
this.[|#b|]/*2*/
}
}
class Foo6 {
#bar: number;
constructor(bar: number) {
this.[|#|]/*6*/
}
}
class Foo3 {
#bar: number;
constructor(bar: number) {
[|b|]/*3*/
}
}
class Foo7 {
#bar: number;
constructor(bar: number) {
/*7*/
}
}
class Foo4 {
#bar: number;
constructor(bar: number) {
[|#b|]/*4*/
}
}
class Foo8 {
#bar: number;
constructor(bar: number) {
[|#|]/*8*/
}
}
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: &fourslash.EditRange{
Insert: f.Ranges()[0],
Replace: f.Ranges()[0],
},
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "#bar",
Kind: new(lsproto.CompletionItemKindField),
SortText: new(string(ls.SortTextLocationPriority)),
FilterText: new("bar"),
},
},
},
})
f.VerifyCompletions(t, "5", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "#bar",
Kind: new(lsproto.CompletionItemKindField),
SortText: new(string(ls.SortTextLocationPriority)),
FilterText: new("bar"),
},
},
},
})
f.VerifyCompletions(t, "2", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: &fourslash.EditRange{
Insert: f.Ranges()[1],
Replace: f.Ranges()[1],
},
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "#bar",
Kind: new(lsproto.CompletionItemKindField),
SortText: new(string(ls.SortTextLocationPriority)),
},
},
},
})
f.VerifyCompletions(t, "6", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: &fourslash.EditRange{
Insert: f.Ranges()[2],
Replace: f.Ranges()[2],
},
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "#bar",
Kind: new(lsproto.CompletionItemKindField),
SortText: new(string(ls.SortTextLocationPriority)),
},
},
},
})
f.VerifyCompletions(t, "3", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "#bar",
Kind: new(lsproto.CompletionItemKindField),
SortText: new(string(ls.SortTextSuggestedClassMembers)),
FilterText: new("bar"),
TextEdit: &lsproto.TextEditOrInsertReplaceEdit{
InsertReplaceEdit: &lsproto.InsertReplaceEdit{
NewText: "this.#bar",
Insert: f.Ranges()[3].LSRange,
Replace: f.Ranges()[3].LSRange,
},
},
},
},
},
})
f.VerifyCompletions(t, "7", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "#bar",
Kind: new(lsproto.CompletionItemKindField),
SortText: new(string(ls.SortTextSuggestedClassMembers)),
FilterText: new("bar"),
InsertText: new("this.#bar"),
},
},
},
})
f.VerifyCompletions(t, "4", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "#bar",
Kind: new(lsproto.CompletionItemKindField),
SortText: new(string(ls.SortTextSuggestedClassMembers)),
TextEdit: &lsproto.TextEditOrInsertReplaceEdit{
InsertReplaceEdit: &lsproto.InsertReplaceEdit{
NewText: "this.#bar",
Insert: f.Ranges()[4].LSRange,
Replace: f.Ranges()[4].LSRange,
},
},
},
},
},
})
f.VerifyCompletions(t, "8", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "#bar",
Kind: new(lsproto.CompletionItemKindField),
SortText: new(string(ls.SortTextSuggestedClassMembers)),
TextEdit: &lsproto.TextEditOrInsertReplaceEdit{
InsertReplaceEdit: &lsproto.InsertReplaceEdit{
NewText: "this.#bar",
Insert: f.Ranges()[5].LSRange,
Replace: f.Ranges()[5].LSRange,
},
},
},
},
},
})
}

View File

@@ -0,0 +1,65 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/ls"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCompletionFilterText2(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @strict: true
declare const foo1: { bar: string } | undefined;
if (true) {
foo1[|.|]/*1*/
}
else {
foo1?./*2*/
}
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "bar",
Kind: new(lsproto.CompletionItemKindField),
SortText: new(string(ls.SortTextLocationPriority)),
InsertText: new("?.bar"),
FilterText: new(".bar"),
TextEdit: &lsproto.TextEditOrInsertReplaceEdit{
TextEdit: &lsproto.TextEdit{
NewText: "?.bar",
Range: f.Ranges()[0].LSRange,
},
},
},
},
},
})
f.VerifyCompletions(t, "2", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "bar",
Kind: new(lsproto.CompletionItemKindField),
SortText: new(string(ls.SortTextLocationPriority)),
},
},
},
})
}

View File

@@ -0,0 +1,153 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/ls"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCompletionFilterText3(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @strict: true
declare const foo1: { b: number; "a bc": string; };
if (true) {
foo1[|.|]/*1*/
}
else {
foo1[|.a|]/*2*/
}
declare const foo2: { b: number; "a bc": string; } | undefined;
if (true) {
foo2[|.|]/*3*/
} else if (false) {
foo2[|.a|]/*4*/
} else {
foo2[|?.|]/*5*/
}
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "a bc",
Kind: new(lsproto.CompletionItemKindField),
SortText: new(string(ls.SortTextLocationPriority)),
InsertText: new("[\"a bc\"]"),
FilterText: new(".a bc"),
TextEdit: &lsproto.TextEditOrInsertReplaceEdit{
TextEdit: &lsproto.TextEdit{
NewText: "[\"a bc\"]",
Range: f.Ranges()[0].LSRange,
},
},
},
},
},
})
f.VerifyCompletions(t, "2", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "a bc",
Kind: new(lsproto.CompletionItemKindField),
SortText: new(string(ls.SortTextLocationPriority)),
InsertText: new("[\"a bc\"]"),
FilterText: new(".a bc"),
TextEdit: &lsproto.TextEditOrInsertReplaceEdit{
TextEdit: &lsproto.TextEdit{
NewText: "[\"a bc\"]",
Range: f.Ranges()[1].LSRange,
},
},
},
},
},
})
f.VerifyCompletions(t, "3", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "a bc",
Kind: new(lsproto.CompletionItemKindField),
SortText: new(string(ls.SortTextLocationPriority)),
InsertText: new("?.[\"a bc\"]"),
FilterText: new(".a bc"),
TextEdit: &lsproto.TextEditOrInsertReplaceEdit{
TextEdit: &lsproto.TextEdit{
NewText: "?.[\"a bc\"]",
Range: f.Ranges()[2].LSRange,
},
},
},
},
},
})
f.VerifyCompletions(t, "4", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "a bc",
Kind: new(lsproto.CompletionItemKindField),
SortText: new(string(ls.SortTextLocationPriority)),
InsertText: new("?.[\"a bc\"]"),
FilterText: new(".a bc"),
TextEdit: &lsproto.TextEditOrInsertReplaceEdit{
TextEdit: &lsproto.TextEdit{
NewText: "?.[\"a bc\"]",
Range: f.Ranges()[3].LSRange,
},
},
},
},
},
})
f.VerifyCompletions(t, "5", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "a bc",
Kind: new(lsproto.CompletionItemKindField),
SortText: new(string(ls.SortTextLocationPriority)),
InsertText: new("?.[\"a bc\"]"),
FilterText: new("?.a bc"),
TextEdit: &lsproto.TextEditOrInsertReplaceEdit{
TextEdit: &lsproto.TextEdit{
NewText: "?.[\"a bc\"]",
Range: f.Ranges()[4].LSRange,
},
},
},
},
},
})
}

View File

@@ -0,0 +1,44 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/ls"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCompletionFilterText4(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `declare const x: [number, number];
x[|.|]/**/;
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "0",
Kind: new(lsproto.CompletionItemKindField),
SortText: new(string(ls.SortTextLocationPriority)),
InsertText: new("[0]"),
FilterText: new(".0"),
TextEdit: &lsproto.TextEditOrInsertReplaceEdit{
TextEdit: &lsproto.TextEdit{
NewText: "[0]",
Range: f.Ranges()[0].LSRange,
},
},
},
},
},
})
}

View File

@@ -0,0 +1,31 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
// !!! can delete, there are similar tests that haven't been ported yet.
func TestCompletionImportAttributes(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
// @target: esnext
// @module: esnext
// @filename: main.ts
import yadda1 from "yadda" with {/*attr*/}
import yadda2 from "yadda" with {attr/*attrEnd1*/: true}
import yadda3 from "yadda" with {attr: /*attrValue*/}
// @filename: yadda
export default {};
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToEachMarker(t, nil, func(marker *fourslash.Marker, index int) {
f.VerifyCompletions(t, marker, nil)
})
}

View File

@@ -0,0 +1,55 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
// Test that requesting completions after a keyword in an import statement
// doesn't crash with a nil pointer dereference.
// Reproduces issue: import super/*cursor*/
// Before the fix, this would panic with: runtime error: invalid memory address or nil pointer dereference
func TestCompletionImportKeywordNoCrash(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
emptyCommitChars := []string{}
// Test with "super" keyword
{
const content = `import super/*1*/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &emptyCommitChars,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
"type",
},
},
})
done()
}
// Test with "this" keyword
{
const content = `import this/*1*/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &emptyCommitChars,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
"type",
},
},
})
done()
}
}

View File

@@ -0,0 +1,30 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCompletionInArrayLiteralAfterInvalidToken1(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `const pairs: Record<string, [string, string]> = {
a: ["x",:/*m1*/]
};
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "m1", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{},
})
}

View File

@@ -0,0 +1,31 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCompletionInJSDocPropertyWithLinkNoCrash1(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
// @allowJs: true
// @filename: /file.js
export function foo() {}
/**
* @typedef MyType
* @property {number} [timeout] - The /*1*/timeout; defaults to {@linkcode DEFAULT}
*/
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{CommitCharacters: &[]string{".", ",", ";"}},
Items: &fourslash.CompletionsExpectedItems{},
})
}

View File

@@ -0,0 +1,33 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCompletionJSDocNoCrash2(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
// @allowJs: true
// @filename: file.js
/**
* @param {Object} obj
* @param {string} obj.first The first property
* @param {string} obj.second The second property
* @param {string} obj.third The {@link foo} third property
*/
/*1*/function foo(obj) {}
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
// The assertion here is simply "does not crash/panic".
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{CommitCharacters: &[]string{".", ",", ";"}},
Items: &fourslash.CompletionsExpectedItems{},
})
}

View File

@@ -0,0 +1,32 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCompletionJSDocNoCrash(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
// @allowJs: true
// @filename: file.js
class ErrorMap {
/**
* @type {string}
*//*1*/
errorMap;
}
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
// The assertion here is simply "does not crash/panic".
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{CommitCharacters: &[]string{".", ",", ";"}},
Items: &fourslash.CompletionsExpectedItems{},
})
}

View File

@@ -0,0 +1,26 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCompletionJsxNoCrash(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
// @filename: file.tsx
<Foo/>/*1*/
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
// The assertion here is simply "does not crash/panic".
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{CommitCharacters: &[]string{".", ",", ";"}},
Items: &fourslash.CompletionsExpectedItems{},
})
}

View File

@@ -0,0 +1,47 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCompletionListAlreadyImportedNamespaceExportAlias(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @module: node18
// @Filename: /values.ts
export const A = 1;
export const B = 2;
// @Filename: /namespace.ts
import * as Group from "./values.js";
type Group = (typeof Group)[keyof typeof Group];
export { Group };
// @Filename: /index.ts
import { Group } from "./namespace.js";
console.log(Grou/**/);`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
result := f.VerifyCompletions(t, "", &fourslash.CompletionsExpectedList{
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{Label: "Group"},
},
},
})
result.AndHasNoCodeAction(t, &fourslash.CompletionsExpectedCodeAction{
Name: "Group",
Source: "./namespace.js",
})
}

View File

@@ -0,0 +1,28 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCompletionListDefaultTypeArgumentPositionTypeOnly(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `// @lib: es5
const foo = "foo";
function test1<T = /*1*/>() {}`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "1", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &DefaultCommitCharacters,
},
Items: &fourslash.CompletionsExpectedItems{
Exact: CompletionGlobalTypes,
},
})
}

View File

@@ -0,0 +1,79 @@
package fourslash_test
import (
"strings"
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
. "github.com/microsoft/typescript-go/internal/fourslash/tests/util"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCompletionListInUnclosedTypeArguments(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `let x = 10;
type Type = void;
declare function f<T>(): void;
declare function f2<T, U>(): void;
f</*1a*/T/*2a*/y/*3a*/
f</*1b*/T/*2b*/y/*3b*/;
f</*1c*/T/*2c*/y/*3c*/>
f</*1d*/T/*2d*/y/*3d*/>
f</*1e*/T/*2e*/y/*3e*/>();
f2</*1k*/T/*2k*/y/*3k*/,
f2</*1l*/T/*2l*/y/*3l*/,{| "newId": true |}T{| "newId": true |}y{| "newId": true |}
f2</*1m*/T/*2m*/y/*3m*/,{| "newId": true |}T{| "newId": true |}y{| "newId": true |};
f2</*1n*/T/*2n*/y/*3n*/,{| "newId": false |}T{| "newId": false |}y{| "newId": false |}>
f2</*1o*/T/*2o*/y/*3o*/,{| "newId": false |}T{| "newId": false |}y{| "newId": false |}>
f2</*1p*/T/*2p*/y/*3p*/,{| "newId": true, "typeOnly": true |}T{| "newId": true, "typeOnly": true |}y{| "newId": true, "typeOnly": true |}>();
f2<typeof /*1uValueOnly*/x, {| "newId": true |}T{| "newId": true |}y{| "newId": true |}
f2</*1x*/T/*2x*/y/*3x*/, () =>/*4x*/T/*5x*/y/*6x*/
f2<() =>/*1y*/T/*2y*/y/*3y*/, () =>/*4y*/T/*5y*/y/*6y*/
f2<any, () =>/*1z*/T/*2z*/y/*3z*/`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.GoToEachMarker(t, nil, func(marker *fourslash.Marker, index int) {
markerName := marker.Name
valueOnly := markerName != nil && strings.HasSuffix(*markerName, "ValueOnly")
commitCharacters := &DefaultCommitCharacters
if marker.Data != nil {
newId := marker.Data["newId"]
typeOnly := marker.Data["typeOnly"]
if newId != nil && newId.(bool) && !(typeOnly != nil && typeOnly.(bool)) {
commitCharacters = &[]string{".", ";"}
}
}
var includes []fourslash.CompletionsExpectedItem
var excludes []string
if valueOnly {
includes = []fourslash.CompletionsExpectedItem{
"x",
}
excludes = []string{
"Type",
}
} else {
includes = []fourslash.CompletionsExpectedItem{
"Type",
}
excludes = []string{
"x",
}
}
f.VerifyCompletions(t, marker, &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: commitCharacters,
EditRange: Ignored,
},
Items: &fourslash.CompletionsExpectedItems{
Includes: includes,
Excludes: excludes,
},
})
})
}

View File

@@ -0,0 +1,45 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCompletionResolveAfterEdit(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `
// @filename: /index.ts
interface Point {
x: number;
y: number;
}
declare const p: Point;
/*a*/
// @filename: /foo.ts
/*b*/
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
// Step 1: Get completions at the marker.
f.GoToMarker(t, "a")
completions := f.GetCompletions(t, nil /*userPreferences*/)
if completions == nil || len(completions.Items) == 0 {
t.Fatal("Expected completions but got none")
}
firstItem := completions.Items[0]
// Step 2: Make a file change (insert a comment after marker).
f.GoToMarker(t, "b")
f.Insert(t, "1")
// Step 3: Resolve the first completion item from the original list.
resolved := f.ResolveCompletionItem(t, firstItem)
if resolved == nil {
t.Fatal("Expected resolved completion item but got nil")
}
}

View File

@@ -0,0 +1,36 @@
package fourslash_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/ls"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/testutil"
)
func TestCompletionResolveKeyword(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `class C {
/*a*/
}`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyCompletions(t, "a", &fourslash.CompletionsExpectedList{
IsIncomplete: false,
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
CommitCharacters: &[]string{},
},
Items: &fourslash.CompletionsExpectedItems{
Includes: []fourslash.CompletionsExpectedItem{
&lsproto.CompletionItem{
Label: "abstract",
Kind: new(lsproto.CompletionItemKindKeyword),
SortText: new(string(ls.SortTextGlobalsOrKeywords)),
Detail: new("abstract"),
},
},
},
})
}

Some files were not shown because too many files have changed in this diff Show More