Skip to content

Commit 05c0e2b

Browse files
authored
Add completion helpers and port more completion fourslash tests (#1290)
1 parent 31b66c6 commit 05c0e2b

File tree

606 files changed

+33968
-3784
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

606 files changed

+33968
-3784
lines changed

internal/astnav/tokens.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ func getTokenAtPosition(
133133
// Check if the rightmost token of prevSubtree should be returned based on the
134134
// `includePrecedingTokenAtEndPosition` callback.
135135
if prevSubtree != nil {
136-
child := findRightmostNode(prevSubtree)
137-
if child.End() == position && includePrecedingTokenAtEndPosition(child) {
136+
child := FindPrecedingTokenEx(sourceFile, position, prevSubtree, false /*excludeJSDoc*/)
137+
if child != nil && child.End() == position && includePrecedingTokenAtEndPosition(child) {
138138
// Optimization: includePrecedingTokenAtEndPosition only ever returns true
139139
// for real AST nodes, so we don't run the scanner here.
140140
return child
@@ -511,7 +511,10 @@ func findRightmostValidToken(endPos int, sourceFile *ast.SourceFile, containingN
511511

512512
// Case 3: childless node.
513513
if !hasChildren {
514-
return n
514+
if n != containingNode {
515+
return n
516+
}
517+
return nil
515518
}
516519
// Case 1: recur on rightmostValidNode.
517520
if rightmostValidNode != nil {

internal/core/core.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,3 +563,10 @@ func IndexAfter(s string, pattern string, startIndex int) int {
563563
func ShouldRewriteModuleSpecifier(specifier string, compilerOptions *CompilerOptions) bool {
564564
return compilerOptions.RewriteRelativeImportExtensions.IsTrue() && tspath.PathIsRelative(specifier) && !tspath.IsDeclarationFileName(specifier) && tspath.HasTSFileExtension(specifier)
565565
}
566+
567+
func SingleElementSlice[T any](element *T) []*T {
568+
if element == nil {
569+
return nil
570+
}
571+
return []*T{element}
572+
}

internal/fourslash/_scripts/convertFourslash.mts

Lines changed: 98 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ let inputFileSet: Set<string> | undefined;
1111
const failingTestsPath = path.join(import.meta.dirname, "failingTests.txt");
1212
const failingTestsList = fs.readFileSync(failingTestsPath, "utf-8").split("\n").map(line => line.trim().substring(4)).filter(line => line.length > 0);
1313
const failingTests = new Set(failingTestsList);
14+
const helperFilePath = path.join(import.meta.dirname, "../", "tests", "util_test.go");
1415

1516
const outputDir = path.join(import.meta.dirname, "../", "tests", "gen");
1617

@@ -183,6 +184,28 @@ function parseVerifyCompletionsArgs(args: readonly ts.Expression[]): VerifyCompl
183184
return cmds;
184185
}
185186

187+
const completionConstants = new Map([
188+
["completion.globals", "completionGlobals"],
189+
["completion.globalTypes", "completionGlobalTypes"],
190+
["completion.classElementKeywords", "completionClassElementKeywords"],
191+
["completion.classElementInJsKeywords", "completionClassElementInJSKeywords"],
192+
["completion.constructorParameterKeywords", "completionConstructorParameterKeywords"],
193+
["completion.functionMembersWithPrototype", "completionFunctionMembersWithPrototype"],
194+
["completion.functionMembers", "completionFunctionMembers"],
195+
["completion.typeKeywords", "completionTypeKeywords"],
196+
["completion.undefinedVarEntry", "completionUndefinedVarItem"],
197+
["completion.typeAssertionKeywords", "completionTypeAssertionKeywords"],
198+
]);
199+
200+
const completionPlus = new Map([
201+
["completion.globalsPlus", "completionGlobalsPlus"],
202+
["completion.globalTypesPlus", "completionGlobalTypesPlus"],
203+
["completion.functionMembersPlus", "completionFunctionMembersPlus"],
204+
["completion.functionMembersWithPrototypePlus", "completionFunctionMembersWithPrototypePlus"],
205+
["completion.globalsInJsPlus", "completionGlobalsInJSPlus"],
206+
["completion.typeKeywordsPlus", "completionTypeKeywordsPlus"],
207+
]);
208+
186209
function parseVerifyCompletionArg(arg: ts.Expression): VerifyCompletionsCmd | undefined {
187210
let marker: string | undefined;
188211
let goArgs: VerifyCompletionsArgs | undefined;
@@ -236,31 +259,84 @@ function parseVerifyCompletionArg(arg: ts.Expression): VerifyCompletionsCmd | un
236259
args: undefined,
237260
};
238261
}
239-
let expected = "[]fourslash.ExpectedCompletionItem{";
240-
if (ts.isArrayLiteralExpression(init)) {
241-
for (const elem of init.elements) {
262+
let expected: string;
263+
const initText = init.getText();
264+
if (completionConstants.has(initText)) {
265+
expected = completionConstants.get(initText)!;
266+
}
267+
else if (completionPlus.keys().some(funcName => initText.startsWith(funcName))) {
268+
const tsFunc = completionPlus.keys().find(funcName => initText.startsWith(funcName));
269+
const funcName = completionPlus.get(tsFunc!)!;
270+
const items = (init as ts.CallExpression).arguments[0];
271+
const opts = (init as ts.CallExpression).arguments[1];
272+
if (!ts.isArrayLiteralExpression(items)) {
273+
console.error(`Expected array literal expression for completion.globalsPlus items, got ${items.getText()}`);
274+
return undefined;
275+
}
276+
expected = `${funcName}([]fourslash.CompletionsExpectedItem{`;
277+
for (const elem of items.elements) {
242278
const result = parseExpectedCompletionItem(elem);
243279
if (!result) {
244280
return undefined;
245281
}
246282
expected += result + ", ";
247283
}
284+
expected += "}";
285+
if (opts) {
286+
if (!ts.isObjectLiteralExpression(opts)) {
287+
console.error(`Expected object literal expression for completion.globalsPlus options, got ${opts.getText()}`);
288+
return undefined;
289+
}
290+
const noLib = opts.properties[0];
291+
if (noLib && ts.isPropertyAssignment(noLib) && noLib.name.getText() === "noLib") {
292+
if (noLib.initializer.kind === ts.SyntaxKind.TrueKeyword) {
293+
expected += ", true";
294+
}
295+
else if (noLib.initializer.kind === ts.SyntaxKind.FalseKeyword) {
296+
expected += ", false";
297+
}
298+
else {
299+
console.error(`Expected boolean literal for noLib, got ${noLib.initializer.getText()}`);
300+
return undefined;
301+
}
302+
}
303+
else {
304+
console.error(`Expected noLib property in completion.globalsPlus options, got ${opts.getText()}`);
305+
return undefined;
306+
}
307+
}
308+
else if (tsFunc === "completion.globalsPlus" || tsFunc === "completion.globalsInJsPlus") {
309+
expected += ", false"; // Default for noLib
310+
}
311+
expected += ")";
248312
}
249313
else {
250-
const result = parseExpectedCompletionItem(init);
251-
if (!result) {
252-
return undefined;
314+
expected = "[]fourslash.CompletionsExpectedItem{";
315+
if (ts.isArrayLiteralExpression(init)) {
316+
for (const elem of init.elements) {
317+
const result = parseExpectedCompletionItem(elem);
318+
if (!result) {
319+
return undefined;
320+
}
321+
expected += result + ", ";
322+
}
323+
}
324+
else {
325+
const result = parseExpectedCompletionItem(init);
326+
if (!result) {
327+
return undefined;
328+
}
329+
expected += result;
253330
}
254-
expected += result;
331+
expected += "}";
255332
}
256-
expected += "}";
257333
if (propName === "includes") {
258334
(goArgs ??= {}).includes = expected;
259335
}
260336
else {
261337
(goArgs ??= {}).exact = expected;
262338
}
263-
break; // !!! parse these args
339+
break;
264340
case "excludes":
265341
let excludes = "[]string{";
266342
if (ts.isStringLiteral(init)) {
@@ -286,7 +362,7 @@ function parseVerifyCompletionArg(arg: ts.Expression): VerifyCompletionsCmd | un
286362
case "triggerCharacter":
287363
case "defaultCommitCharacters":
288364
break; // !!! parse once they're supported in fourslash
289-
case "optionalReplacementSpan":
365+
case "optionalReplacementSpan": // the only two tests that use this will require manual conversion
290366
case "isGlobalCompletion":
291367
break; // Ignored, unused
292368
default:
@@ -303,6 +379,9 @@ function parseVerifyCompletionArg(arg: ts.Expression): VerifyCompletionsCmd | un
303379
}
304380

305381
function parseExpectedCompletionItem(expr: ts.Expression): string | undefined {
382+
if (completionConstants.has(expr.getText())) {
383+
return completionConstants.get(expr.getText())!;
384+
}
306385
if (ts.isStringLiteral(expr)) {
307386
return getGoStringLiteral(expr.text);
308387
}
@@ -501,16 +580,16 @@ function parseSortText(expr: ts.Expression): string | undefined {
501580
return "ls.SortTextOptionalMember";
502581
case "completion.SortText.MemberDeclaredBySpreadAssignment":
503582
return "ls.SortTextMemberDeclaredBySpreadAssignment";
504-
case "completion.SortText.SuggestedClassMember":
505-
return "ls.SortTextSuggestedClassMember";
583+
case "completion.SortText.SuggestedClassMembers":
584+
return "ls.SortTextSuggestedClassMembers";
506585
case "completion.SortText.GlobalsOrKeywords":
507586
return "ls.SortTextGlobalsOrKeywords";
508587
case "completion.SortText.AutoImportSuggestions":
509588
return "ls.SortTextAutoImportSuggestions";
510589
case "completion.SortText.ClassMemberSnippets":
511590
return "ls.SortTextClassMemberSnippets";
512-
case "completion.SortText.JavaScriptIdentifiers":
513-
return "ls.SortTextJavaScriptIdentifiers";
591+
case "completion.SortText.JavascriptIdentifiers":
592+
return "ls.SortTextJavascriptIdentifiers";
514593
default:
515594
console.error(`Unrecognized sort text: ${text}`);
516595
return undefined; // !!! support deprecated/obj literal prop/etc
@@ -545,14 +624,14 @@ function generateVerifyCompletions({ marker, args, isNewIdentifierLocation }: Ve
545624
if (args.excludes) expected.push(`Excludes: ${args.excludes},`);
546625
if (args.exact) expected.push(`Exact: ${args.exact},`);
547626
// !!! isIncomplete
548-
// !!! itemDefaults/commitCharacters from `isNewIdentifierLocation`
549627
const commitCharacters = isNewIdentifierLocation ? "[]string{}" : "defaultCommitCharacters";
550-
expectedList = `&fourslash.VerifyCompletionsExpectedList{
628+
expectedList = `&fourslash.CompletionsExpectedList{
551629
IsIncomplete: false,
552-
ItemDefaults: &lsproto.CompletionItemDefaults{
630+
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
553631
CommitCharacters: &${commitCharacters},
632+
EditRange: ignored,
554633
},
555-
Items: &fourslash.VerifyCompletionsExpectedItems{
634+
Items: &fourslash.CompletionsExpectedItems{
556635
${expected.join("\n")}
557636
},
558637
}`;
@@ -614,14 +693,7 @@ func Test${testName}(t *testing.T) {
614693
}
615694

616695
function generateHelperFile() {
617-
const helper = `package fourslash_test
618-
619-
func ptrTo[T any](v T) *T {
620-
return &v
621-
}
622-
623-
var defaultCommitCharacters = []string{".", ",", ";"}`;
624-
fs.writeFileSync(path.join(outputDir, "util_test.go"), helper, "utf-8");
696+
fs.copyFileSync(helperFilePath, path.join(outputDir, "util_test.go"));
625697
}
626698

627699
main();

internal/fourslash/_scripts/failingTests.txt

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
TestAsOperatorCompletion
22
TestAutoImportsWithRootDirsAndRootedPath01
33
TestClosedCommentsInConstructor
4+
TestCodeCompletionEscaping
5+
TestCompletionAfterNewline
6+
TestCompletionAfterNewline2
47
TestCompletionCloneQuestionToken
58
TestCompletionEntryForArgumentConstrainedToString
69
TestCompletionEntryForArrayElementConstrainedToString
710
TestCompletionEntryForArrayElementConstrainedToString2
8-
TestCompletionEntryForShorthandPropertyAssignment
11+
TestCompletionEntryForClassMembers_StaticWhenBaseTypeIsNotResolved
912
TestCompletionExportFrom
1013
TestCompletionForComputedStringProperties
1114
TestCompletionForStringLiteral
@@ -58,19 +61,25 @@ TestCompletionListAndMemberListOnCommentedDot
5861
TestCompletionListAndMemberListOnCommentedLine
5962
TestCompletionListAndMemberListOnCommentedWhiteSpace
6063
TestCompletionListAtInvalidLocations
64+
TestCompletionListBuilderLocations_VariableDeclarations
6165
TestCompletionListForExportEquals
6266
TestCompletionListForTransitivelyExportedMembers01
6367
TestCompletionListForTransitivelyExportedMembers04
6468
TestCompletionListForUnicodeEscapeName
69+
TestCompletionListFunctionMembers
6570
TestCompletionListInArrowFunctionInUnclosedCallSite01
71+
TestCompletionListInClassStaticBlocks
6672
TestCompletionListInClosedFunction05
6773
TestCompletionListInComments
6874
TestCompletionListInComments2
75+
TestCompletionListInComments3
76+
TestCompletionListInExtendsClause
6977
TestCompletionListInImportClause01
7078
TestCompletionListInImportClause05
7179
TestCompletionListInImportClause06
7280
TestCompletionListInObjectBindingPattern16
7381
TestCompletionListInScope
82+
TestCompletionListInTemplateLiteralParts1
7483
TestCompletionListInUnclosedCommaExpression01
7584
TestCompletionListInUnclosedCommaExpression02
7685
TestCompletionListInUnclosedFunction08
@@ -81,40 +90,43 @@ TestCompletionListInUnclosedTaggedTemplate01
8190
TestCompletionListInUnclosedTaggedTemplate02
8291
TestCompletionListInUnclosedTemplate01
8392
TestCompletionListInUnclosedTemplate02
93+
TestCompletionListInvalidMemberNames2
8494
TestCompletionListInvalidMemberNames_withExistingIdentifier
95+
TestCompletionListStaticMembers
8596
TestCompletionListStaticProtectedMembers
97+
TestCompletionListStaticProtectedMembers2
98+
TestCompletionListStaticProtectedMembers3
8699
TestCompletionListStringParenthesizedExpression
87100
TestCompletionListStringParenthesizedType
88101
TestCompletionListWithoutVariableinitializer
89102
TestCompletionListsStringLiteralTypeAsIndexedAccessTypeObject
90103
TestCompletionNoAutoInsertQuestionDotWithUserPreferencesOff
91104
TestCompletionOfAwaitPromise6
92105
TestCompletionPreferredSuggestions1
93-
TestCompletionSatisfiesKeyword
94106
TestCompletionWithConditionalOperatorMissingColon
95-
TestCompletionsAfterKeywordsInBlock
96-
TestCompletionsAsserts
97-
TestCompletionsAtIncompleteObjectLiteralProperty
98-
TestCompletionsClassPropertiesAfterPrivateProperty
107+
TestCompletionsAfterJSDoc
108+
TestCompletionsBeforeRestArg1
99109
TestCompletionsECMAPrivateMemberTriggerCharacter
100-
TestCompletionsGeneratorFunctions
101110
TestCompletionsImportDefaultExportCrash1
102-
TestCompletionsImport_promoteTypeOnly2
103111
TestCompletionsImport_umdDefaultNoCrash2
104112
TestCompletionsInRequire
105113
TestCompletionsInterfaceElement
106114
TestCompletionsJSDocImportTagAttributesEmptyModuleSpecifier1
107115
TestCompletionsJSDocImportTagAttributesErrorModuleSpecifier1
108116
TestCompletionsJSDocImportTagEmptyModuleSpecifier1
109117
TestCompletionsJSDocNoCrash1
118+
TestCompletionsJSDocNoCrash2
110119
TestCompletionsJsPropertyAssignment
120+
TestCompletionsJsdocParamTypeBeforeName
121+
TestCompletionsJsdocTypeTagCast
111122
TestCompletionsJsxAttribute2
112123
TestCompletionsKeyof
113124
TestCompletionsLiteralFromInferenceWithinInferredType3
125+
TestCompletionsMergedDeclarations1
126+
TestCompletionsNamespaceMergedWithClass
114127
TestCompletionsNamespaceName
115128
TestCompletionsNewTarget
116129
TestCompletionsNonExistentImport
117-
TestCompletionsObjectLiteralMethod6
118130
TestCompletionsOptionalKindModifier
119131
TestCompletionsOptionalMethod
120132
TestCompletionsOverridingMethod1
@@ -135,25 +147,29 @@ TestCompletionsPaths_pathMapping_parentDirectory
135147
TestCompletionsPropertiesPriorities
136148
TestCompletionsRecommended_union
137149
TestCompletionsRedeclareModuleAsGlobal
138-
TestCompletionsSelfDeclaring1
139150
TestCompletionsStringLiteral_fromTypeConstraint
140151
TestCompletionsStringsWithTriggerCharacter
141152
TestCompletionsSymbolMembers
142153
TestCompletionsTriggerCharacter
143154
TestCompletionsUniqueSymbol1
144-
TestCompletionsWithDeprecatedTag3
145155
TestCompletionsWithStringReplacementMode1
156+
TestExportEqualCallableInterface
146157
TestGetJavaScriptCompletions1
147158
TestGetJavaScriptCompletions10
148159
TestGetJavaScriptCompletions11
160+
TestGetJavaScriptCompletions12
161+
TestGetJavaScriptCompletions13
149162
TestGetJavaScriptCompletions14
163+
TestGetJavaScriptCompletions15
150164
TestGetJavaScriptCompletions2
165+
TestGetJavaScriptCompletions20
151166
TestGetJavaScriptCompletions21
152167
TestGetJavaScriptCompletions3
153168
TestGetJavaScriptCompletions4
154169
TestGetJavaScriptCompletions5
155170
TestGetJavaScriptCompletions8
156171
TestGetJavaScriptCompletions9
172+
TestGetJavaScriptGlobalCompletions1
157173
TestImportCompletionsPackageJsonExportsSpecifierEndsInTs
158174
TestImportCompletionsPackageJsonExportsTrailingSlash1
159175
TestImportCompletionsPackageJsonImportsConditions1
@@ -178,20 +194,29 @@ TestImportStatementCompletions_pnpmTransitive
178194
TestImportTypeMemberCompletions
179195
TestJavaScriptModules12
180196
TestJavaScriptModules14
197+
TestJavascriptModules20
198+
TestJavascriptModules21
181199
TestJavascriptModulesTypeImport
182200
TestJsDocGenerics1
183-
TestJsdocImportTagCompletion1
201+
TestJsdocExtendsTagCompletion
202+
TestJsdocImplementsTagCompletion
184203
TestJsdocOverloadTagCompletion
185204
TestJsdocParamTagSpecialKeywords
186205
TestJsdocParameterNameCompletion
187206
TestJsdocPropTagCompletion
188-
TestJsdocSatisfiesTagCompletion2
207+
TestJsdocPropertyTagCompletion
208+
TestJsdocSatisfiesTagCompletion1
189209
TestJsdocTemplatePrototypeCompletions
210+
TestJsdocTemplateTagCompletion
211+
TestJsdocThrowsTagCompletion
190212
TestJsdocTypedefTag1
191213
TestJsdocTypedefTag2
214+
TestJsdocTypedefTagNamespace
215+
TestJsdocTypedefTagTypeExpressionCompletion
192216
TestJsxAriaLikeCompletions
193217
TestMemberListErrorRecovery
194218
TestMemberListInWithBlock
219+
TestMemberListOnConstructorType
195220
TestNoCompletionListOnCommentsInsideObjectLiterals
196221
TestNodeModulesImportCompletions1
197222
TestPathCompletionsAllowModuleAugmentationExtensions

0 commit comments

Comments
 (0)