diff --git a/internal/fourslash/_scripts/convertFourslash.mts b/internal/fourslash/_scripts/convertFourslash.mts index f5341c73b0a..d2eedfe8876 100755 --- a/internal/fourslash/_scripts/convertFourslash.mts +++ b/internal/fourslash/_scripts/convertFourslash.mts @@ -113,6 +113,7 @@ function parseTypeScriptFiles(manualTests: Set, folder: string): void { const isServer = filePath.split(path.sep).includes("server"); try { const test = parseFileContent(file, content); + if (test === NO_TEST) return; const testContent = generateGoTest(test, isServer); const testPath = path.join(outputDir, `${test.name}_test.go`); fs.writeFileSync(testPath, testContent, "utf-8"); @@ -126,7 +127,10 @@ function parseTypeScriptFiles(manualTests: Set, folder: string): void { }); } -function parseFileContent(filename: string, content: string): GoTest { +const NO_TEST: unique symbol = Symbol("NO_TEST"); +type NoTest = typeof NO_TEST; + +function parseFileContent(filename: string, content: string): GoTest | NoTest { console.error(`Parsing file: ${filename}`); const sourceFile = ts.createSourceFile("temp.ts", content, ts.ScriptTarget.Latest, true /*setParentNodes*/); const statements = sourceFile.statements; @@ -140,7 +144,8 @@ function parseFileContent(filename: string, content: string): GoTest { goTest.commands.push(...result); } if (goTest.commands.length === 0) { - throw new Error(`No commands parsed in file: ${filename}`); + console.error(`No commands parsed in file (skipping): ${filename}`); + return NO_TEST; } validateCodeFixCommands(goTest.commands); return goTest; @@ -222,10 +227,6 @@ function getBadStatementText(statement: ts.Statement): string { return statement.getText(); } -/** - * Parses a Strada fourslash statement and returns the corresponding Corsa commands. - * @returns an array of commands if the statement is a valid fourslash command, or `undefined` if the statement could not be parsed. - */ function parseFourslashStatement(statement: ts.Statement): Cmd[] { if (ts.isVariableStatement(statement)) { // variable declarations (for ranges and markers), e.g. `const range = test.ranges()[0];` @@ -368,6 +369,10 @@ function parseFourslashStatement(statement: ts.Statement): Cmd[] { return parseCodeFixAvailableArgs(callExpression.arguments); case "codeFixAll": return parseCodeFixAllArgs(callExpression.arguments); + case "semanticClassificationsAre": + return parseSemanticClassificationsAre(callExpression.arguments); + case "syntacticClassificationsAre": + return []; } } // `goTo....` @@ -2819,6 +2824,65 @@ function parseOutliningSpansArgs(args: readonly ts.Expression[]): [VerifyOutlini }]; } +function parseSemanticClassificationsAre(args: readonly ts.Expression[]): [VerifySemanticClassificationsCmd] | [] { + if (args.length < 1) { + throw new Error("semanticClassificationsAre requires at least a format argument"); + } + + const formatArg = args[0]; + if (!ts.isStringLiteralLike(formatArg)) { + throw new Error("semanticClassificationsAre first argument must be a string literal"); + } + + const format = formatArg.text; + + // Only handle "2020" format for semantic tokens + if (format !== "2020") { + // Skip other formats like "original" + return []; + } + + const tokens: Array<{ type: string; text: string; }> = []; + + // Parse the classification tokens (c2.semanticToken("type", "text")) + for (let i = 1; i < args.length; i++) { + const arg = args[i]; + if (!ts.isCallExpression(arg)) { + throw new Error(`Expected call expression for token at index ${i}`); + } + + if (!ts.isPropertyAccessExpression(arg.expression) || arg.expression.name.text !== "semanticToken") { + throw new Error(`Expected semanticToken call at index ${i}`); + } + + if (arg.arguments.length < 2) { + throw new Error(`semanticToken requires 2 arguments at index ${i}`); + } + + const typeArg = arg.arguments[0]; + const textArg = arg.arguments[1]; + + if (!ts.isStringLiteralLike(typeArg) || !ts.isStringLiteralLike(textArg)) { + throw new Error(`semanticToken arguments must be string literals at index ${i}`); + } + + // Map TypeScript's internal "member" type to LSP's "method" type + let tokenType = typeArg.text; + tokenType = tokenType.replace(/\bmember\b/g, "method"); + + tokens.push({ + type: tokenType, + text: textArg.text, + }); + } + + return [{ + kind: "verifySemanticClassifications", + format, + tokens, + }]; +} + function parseKind(expr: ts.Expression): string { if (!ts.isStringLiteral(expr)) { throw new Error(`Expected string literal for kind, got ${expr.getText()}`); @@ -3391,6 +3455,12 @@ interface VerifyCodeFixAllCmd { newFileContent: string; } +interface VerifySemanticClassificationsCmd { + kind: "verifySemanticClassifications"; + format: string; + tokens: Array<{ type: string; text: string; }>; +} + type Cmd = | VerifyCompletionsCmd | VerifyApplyCodeActionFromCompletionCmd @@ -3422,6 +3492,7 @@ type Cmd = | VerifyImportFixModuleSpecifiersCmd | VerifyDiagnosticsCmd | VerifyBaselineDiagnosticsCmd + | VerifySemanticClassificationsCmd | VerifyOutliningSpansCmd | VerifyNumberOfErrorsInCurrentFileCmd | VerifyNoErrorsCmd @@ -3698,6 +3769,14 @@ function generateNavigateTo({ args }: VerifyNavToCmd): string { return `f.VerifyWorkspaceSymbol(t, []*fourslash.VerifyWorkspaceSymbolCase{\n${args.join(", ")}})`; } +function generateSemanticClassifications({ format, tokens }: VerifySemanticClassificationsCmd): string { + const tokensStr = tokens.map(t => `{Type: ${getGoStringLiteral(t.type)}, Text: ${getGoStringLiteral(t.text)}}`).join(",\n\t\t"); + const maybeComma = tokens.length > 0 ? "," : ""; + return `f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + ${tokensStr}${maybeComma} + })`; +} + function generateCmd(cmd: Cmd, imports: Set): string { switch (cmd.kind) { case "verifyCompletions": @@ -3806,6 +3885,8 @@ function generateCmd(cmd: Cmd, imports: Set): string { FixID: ${getGoStringLiteral(cmd.fixId)}, NewFileContent: ${getGoMultiLineStringLiteral(cmd.newFileContent)}, })`; + case "verifySemanticClassifications": + return generateSemanticClassifications(cmd); default: let neverCommand: never = cmd; throw new Error(`Unknown command kind: ${neverCommand as Cmd["kind"]}`); diff --git a/internal/fourslash/_scripts/manualTests.txt b/internal/fourslash/_scripts/manualTests.txt index c464cd66c32..bf63f84b2e2 100644 --- a/internal/fourslash/_scripts/manualTests.txt +++ b/internal/fourslash/_scripts/manualTests.txt @@ -130,6 +130,8 @@ pathCompletionsTypesVersionsWildcard6 quickInfoForOverloadOnConst1 renameDefaultKeyword renameForDefaultExport01 +semanticClassificationClassExpression +semanticModernClassificationFunctions semicolonFormattingInsideAComment semicolonFormattingInsideAStringLiteral signatureHelpImportStarFromExportEquals diff --git a/internal/fourslash/_scripts/unparsedTests.txt b/internal/fourslash/_scripts/unparsedTests.txt index 61ba15ce2f7..1b0aa809491 100644 --- a/internal/fourslash/_scripts/unparsedTests.txt +++ b/internal/fourslash/_scripts/unparsedTests.txt @@ -144,7 +144,6 @@ cancellationWhenfindingAllRefsOnDefinition.ts parse error: "Unrecognized foursla chainedFunctionFunctionArgIndent.ts parse error: "Unrecognized verify content function: indentationIs" chainedFunctionLambdaArgIndex.ts parse error: "Unrecognized verify content function: indentationIs" circularGetTypeAtLocation.ts parse error: "Unrecognized fourslash statement: verify.typeAtLocation(...)" -classifyThisParameter.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" classRenamingErrorRecovery.ts parse error: "Unrecognized fourslash statement: verify.not.errorExistsAfterMarker(...)" classSymbolLookup.ts parse error: "Unrecognized fourslash statement: verify.encodedSemanticClassificationsLength(...)" codeFixAddAllParameterNames.ts parse error: "Unsupported code fix ID: addNameToNamelessParameter" @@ -1783,7 +1782,6 @@ incompleteFunctionCallCodefixTypeParameterVariable.ts parse error: "Code fix tes incompleteFunctionCallCodefixTypeUnion.ts parse error: "Code fix test has no allowed fixId and descriptions do not match any allowed prefix" incompleteFunctionCallCodefixTypeUnions.ts parse error: "Code fix test has no allowed fixId and descriptions do not match any allowed prefix" incorrectJsDocObjectLiteralType.ts parse error: "Expected range variable for range in navigateTo item, got { fileName: \"/a.ts\", pos: 58, end: 91 }" -incrementalJsDocAdjustsLengthsRight.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" incrementalParsing1.ts parse error: "Unrecognized fourslash statement: verify.noMatchingBracePositionInCurrentFile(...)" indentAfterImport.ts parse error: "Unrecognized verify content function: indentationIs" indentation.ts parse error: "Unrecognized fourslash statement: test.markers().forEach(...)" @@ -1861,7 +1859,6 @@ javaScriptPrototype2.ts parse error: "Expected string literal or object literal javaScriptPrototype3.ts parse error: "Expected string literal or object literal for expected completion item, got ...[\"myCtor\", \"x\", \"prototype\"].map(name => ({\r\n name,\r\n kind: \"warning\",\r\n sortText: completion.SortText.JavascriptIdentifiers\r\n }))" javaScriptPrototype4.ts parse error: "Expected string literal or object literal for expected completion item, got [\"foo\", \"bar\", \"qua\"].map(\r\n name => ({ name, kind: \"warning\", sortText: completion.SortText.JavascriptIdentifiers }))" javaScriptPrototype5.ts parse error: "Expected string literal or object literal for expected completion item, got [\"foo\", \"bar\"].map(name => ({ name, kind: \"property\" }))" -jsdocCallbackTagNavigateTo.ts parse error: "No commands parsed in file: jsdocCallbackTagNavigateTo.ts" jsDocContextualTagsInJsDoc1.ts parse error: "Expected 1 or 2 arguments in quickInfoIs, got \"1\", \"(parameter) x: any\", \"Does the thing\", undefined" jsDocFunctionSignatures9.ts parse error: "Unrecognized fourslash statement: verify.verifyQuickInfoDisplayParts(...)" jsdocImportTagCompletion2.ts parse error: "Unrecognized fourslash statement: verify.baselineCompletions(...)" @@ -2062,7 +2059,6 @@ multipleExportAssignmentsErrorList0.ts parse error: "Unrecognized edit function: nameOfRetypedClassInModule.ts parse error: "Unrecognized edit function: disableFormatting" nameOrDottedNameClasses.ts parse error: "Unrecognized fourslash statement: verify.baselineCurrentFileNameOrDottedNameSpans(...)" nameOrDottedNameStatements.ts parse error: "Unrecognized fourslash statement: verify.baselineCurrentFileNameOrDottedNameSpans(...)" -navbarForDoubleAmbientModules01.ts parse error: "No commands parsed in file: navbarForDoubleAmbientModules01.ts" navigateItemsConst.ts parse error: "Unrecognized fourslash statement: for (const range of test.ranges()) {\r\n verify.navigateTo({\r\n pattern: range.marker.data.name,\r\n expected: [{ ...range.marker.data, range }],\r\n })\r\n}" navigateItemsExports.ts parse error: "Unrecognized fourslash statement: for (const range of test.ranges()) {\r\n verify.navigateTo({\r\n pattern: range.marker.data.name,\r\n expected: [{ ...range.marker.data, range }],\r\n });\r\n}" navigateItemsImports.ts parse error: "Unrecognized fourslash statement: for (const range of test.ranges()) {\r\n verify.navigateTo({\r\n pattern: range.marker.data.name,\r\n expected: [{ ...range.marker.data, range }],\r\n });\r\n}" @@ -2709,31 +2705,8 @@ removeExportFromInterfaceError0.ts parse error: "Unrecognized edit function: dis removeExportFromInterfaceError1.ts parse error: "Unrecognized edit function: disableFormatting" renameImport.ts parse error: "Unrecognized goTo function: eachRange" runtimeBehaviorTests.ts parse error: "Unrecognized fourslash statement: verify.eval(...)" -semanticClassification1.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" -semanticClassification2.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" -semanticClassificationAlias.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" -semanticClassificationClassExpression.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" -semanticClassificationConstAssertion.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -semanticClassificationInstantiatedModuleWithVariableOfSameName1.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" -semanticClassificationInstantiatedModuleWithVariableOfSameName2.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" -semanticClassificationInTemplateExpressions.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" semanticClassificationJs1.ts parse error: "Unrecognized fourslash statement: verify.encodedSemanticClassificationsLength(...)" -semanticClassificationModules.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" semanticClassificationsCancellation1.ts parse error: "Unrecognized fourslash statement: cancellation.setCancelled(...)" -semanticClassificationUninstantiatedModuleWithVariableOfSameName1.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" -semanticClassificationUninstantiatedModuleWithVariableOfSameName2.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" -semanticClassificationWithUnionTypes.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" -semanticClassificatonTypeAlias.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" -semanticModernClassificationCallableVariables.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" -semanticModernClassificationCallableVariables2.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" -semanticModernClassificationClassProperties.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" -semanticModernClassificationConstructorTypes.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" -semanticModernClassificationFunctions.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" -semanticModernClassificationInfinityAndNaN.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" -semanticModernClassificationInterfaces.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" -semanticModernClassificationMembers.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" -semanticModernClassificationObjectProperties.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" -semanticModernClassificationVariables.ts parse error: "Unrecognized fourslash statement: verify.semanticClassificationsAre(...)" semicolonFormattingNewline.ts parse error: "Unrecognized verify content function: indentationIs" signatureHelpFilteredTriggers01.ts parse error: "Unrecognized fourslash statement: for (const marker of test.markers()) {\n goTo.marker(marker);\n for (const triggerCharacter of [\"<\", \"(\", \",\"]) {\n edit.insert(triggerCharacter);\n verify.noSignatureHelpForTriggerReason({\n kind: \"characterTyped\",\n triggerCharacter,\n });\n verify.signatureHelpPresentForTriggerReason({\n kind: \"retrigger\",\n triggerCharacter,\n });\n edit.backspace();\n }\n verify.signatureHelpPresentForTriggerReason(/*triggerReason*/ undefined);\n verify.signatureHelpPresentForTriggerReason({ kind: \"invoked\" });\n}" signatureHelpFilteredTriggers02.ts parse error: "Unrecognized fourslash statement: for (const markerName of Object.keys(charMap)) {\n const triggerCharacter = charMap[markerName];\n goTo.marker(markerName);\n edit.insert(triggerCharacter);\n verify.noSignatureHelpForTriggerReason({\n kind: \"characterTyped\",\n triggerCharacter,\n });\n verify.signatureHelpPresentForTriggerReason({\n kind: \"retrigger\",\n triggerCharacter,\n });\n edit.backspace(triggerCharacter.length);\n}" @@ -2829,46 +2802,7 @@ stringCompletionsUnterminated.ts parse error: "Unrecognized fourslash statement: stringCompletionsUnterminated2.ts parse error: "Unrecognized fourslash statement: for (let i = 0; i < markers.length; i++) {\r\n verify.completions({\r\n marker: markers[i],\r\n includes: [{\r\n \"name\": \"test\",\r\n \"kind\": \"string\",\r\n \"kindModifiers\": \"\",\r\n \"replacementSpan\": ranges[i],\r\n }],\r\n }); \r\n}" stringTemplateBraceCompletionPosition.ts parse error: "Unrecognized fourslash statement: verify.not.isValidBraceCompletionAtPosition(...)" switchIndenting.ts parse error: "Unrecognized verify content function: indentationIs" -syntacticClassificationForJSDocTemplateTag.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassifications1.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" syntacticClassificationsCancellation1.ts parse error: "Unrecognized fourslash statement: cancellation.setCancelled(...)" -syntacticClassificationsConflictDiff3Markers1.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsConflictDiff3Markers2.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsConflictMarkers1.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsConflictMarkers2.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsDocComment1.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsDocComment2.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsDocComment3.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsDocComment4.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsForOfKeyword.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsForOfKeyword2.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsForOfKeyword3.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsFunctionWithComments.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsJsx1.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsJsx2.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsMergeConflictMarker1.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsObjectLiteral.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsTemplates1.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsTemplates2.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsTripleSlash1.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsTripleSlash10.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsTripleSlash11.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsTripleSlash12.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsTripleSlash13.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsTripleSlash14.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsTripleSlash15.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsTripleSlash16.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsTripleSlash17.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsTripleSlash18.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsTripleSlash2.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsTripleSlash3.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsTripleSlash4.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsTripleSlash5.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsTripleSlash6.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsTripleSlash7.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsTripleSlash8.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationsTripleSlash9.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" -syntacticClassificationWithErrors.ts parse error: "Unrecognized fourslash statement: verify.syntacticClassificationsAre(...)" textChangesIndentStyle.ts parse error: "Unrecognized fourslash statement: verify.moveToNewFile(...)" textChangesPreserveNewlines1.ts parse error: "Unrecognized edit function: applyRefactor" textChangesPreserveNewlines10.ts parse error: "Unrecognized fourslash statement: verify.moveToNewFile(...)" diff --git a/internal/fourslash/fourslash.go b/internal/fourslash/fourslash.go index c7a6fb0033d..de74ea608be 100644 --- a/internal/fourslash/fourslash.go +++ b/internal/fourslash/fourslash.go @@ -63,6 +63,10 @@ type FourslashTest struct { selectionEnd *lsproto.Position isStradaServer bool // Whether this is a fourslash server test in Strada. !!! Remove once we don't need to diff baselines. + + // Semantic token configuration + semanticTokenTypes []string + semanticTokenModifiers []string } type scriptInfo struct { @@ -190,6 +194,8 @@ func NewFourslash(t *testing.T, capabilities *lsproto.ClientCapabilities, conten converters: converters, baselines: make(map[baselineCommand]*strings.Builder), openFiles: make(map[string]struct{}), + semanticTokenTypes: defaultSemanticTokenTypes(), + semanticTokenModifiers: defaultSemanticTokenModifiers(), } client, closeClient := lsptestutil.NewLSPClient(t, serverOpts, f.handleServerRequest) f.client = client @@ -323,6 +329,50 @@ func (f *FourslashTest) initialize(t *testing.T, capabilities *lsproto.ClientCap <-f.client.Server.InitComplete() } +func defaultSemanticTokenTypes() []string { + return []string{ + string(lsproto.SemanticTokenTypeNamespace), + string(lsproto.SemanticTokenTypeClass), + string(lsproto.SemanticTokenTypeEnum), + string(lsproto.SemanticTokenTypeInterface), + string(lsproto.SemanticTokenTypeStruct), + string(lsproto.SemanticTokenTypeTypeParameter), + string(lsproto.SemanticTokenTypeType), + string(lsproto.SemanticTokenTypeParameter), + string(lsproto.SemanticTokenTypeVariable), + string(lsproto.SemanticTokenTypeProperty), + string(lsproto.SemanticTokenTypeEnumMember), + string(lsproto.SemanticTokenTypeDecorator), + string(lsproto.SemanticTokenTypeEvent), + string(lsproto.SemanticTokenTypeFunction), + string(lsproto.SemanticTokenTypeMethod), + string(lsproto.SemanticTokenTypeMacro), + string(lsproto.SemanticTokenTypeLabel), + string(lsproto.SemanticTokenTypeComment), + string(lsproto.SemanticTokenTypeString), + string(lsproto.SemanticTokenTypeKeyword), + string(lsproto.SemanticTokenTypeNumber), + string(lsproto.SemanticTokenTypeRegexp), + string(lsproto.SemanticTokenTypeOperator), + } +} + +func defaultSemanticTokenModifiers() []string { + return []string{ + string(lsproto.SemanticTokenModifierDeclaration), + string(lsproto.SemanticTokenModifierDefinition), + string(lsproto.SemanticTokenModifierReadonly), + string(lsproto.SemanticTokenModifierStatic), + string(lsproto.SemanticTokenModifierDeprecated), + string(lsproto.SemanticTokenModifierAbstract), + string(lsproto.SemanticTokenModifierAsync), + string(lsproto.SemanticTokenModifierModification), + string(lsproto.SemanticTokenModifierDocumentation), + string(lsproto.SemanticTokenModifierDefaultLibrary), + "local", + } +} + // If modifying the defaults, update GetDefaultCapabilities too. var ( ptrTrue = new(true) @@ -500,6 +550,18 @@ func getCapabilitiesWithDefaults(capabilities *lsproto.ClientCapabilities) *lspr if capabilitiesWithDefaults.TextDocument.PublishDiagnostics == nil { capabilitiesWithDefaults.TextDocument.PublishDiagnostics = defaultPublishDiagnosticCapabilities } + if capabilitiesWithDefaults.TextDocument.SemanticTokens == nil { + capabilitiesWithDefaults.TextDocument.SemanticTokens = &lsproto.SemanticTokensClientCapabilities{ + Requests: &lsproto.ClientSemanticTokensRequestOptions{ + Full: &lsproto.BooleanOrClientSemanticTokensRequestFullDelta{ + Boolean: ptrTrue, + }, + }, + TokenTypes: defaultSemanticTokenTypes(), + TokenModifiers: defaultSemanticTokenModifiers(), + Formats: []lsproto.TokenFormat{lsproto.TokenFormatRelative}, + } + } if capabilitiesWithDefaults.Workspace == nil { capabilitiesWithDefaults.Workspace = &lsproto.WorkspaceClientCapabilities{} } diff --git a/internal/fourslash/semantictokens.go b/internal/fourslash/semantictokens.go new file mode 100644 index 00000000000..bb69d03682f --- /dev/null +++ b/internal/fourslash/semantictokens.go @@ -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< 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") +} diff --git a/internal/fourslash/tests/gen/incrementalJsDocAdjustsLengthsRight_test.go b/internal/fourslash/tests/gen/incrementalJsDocAdjustsLengthsRight_test.go new file mode 100644 index 00000000000..c6ff965bf0c --- /dev/null +++ b/internal/fourslash/tests/gen/incrementalJsDocAdjustsLengthsRight_test.go @@ -0,0 +1,28 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual incrementalJsDocAdjustsLengthsRight" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestIncrementalJsDocAdjustsLengthsRight(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `// @noLib: true + +/** + * Pad ` + "`" + `str` + "`" + ` to ` + "`" + `width` + "`" + `. + * + * @param {String} str + * @param {Number} wid/*1*/` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.GoToMarker(t, "1") + f.Insert(t, "th\n@") +} diff --git a/internal/fourslash/tests/gen/semanticClassification1_test.go b/internal/fourslash/tests/gen/semanticClassification1_test.go new file mode 100644 index 00000000000..4b55654fb41 --- /dev/null +++ b/internal/fourslash/tests/gen/semanticClassification1_test.go @@ -0,0 +1,31 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual semanticClassification1" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticClassification1(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `module /*0*/M { + export interface /*1*/I { + } +} +interface /*2*/X extends /*3*/M./*4*/I { }` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "namespace.declaration", Text: "M"}, + {Type: "interface.declaration", Text: "I"}, + {Type: "interface.declaration", Text: "X"}, + {Type: "namespace", Text: "M"}, + {Type: "interface", Text: "I"}, + }) +} diff --git a/internal/fourslash/tests/gen/semanticClassification2_test.go b/internal/fourslash/tests/gen/semanticClassification2_test.go new file mode 100644 index 00000000000..97646e0ec96 --- /dev/null +++ b/internal/fourslash/tests/gen/semanticClassification2_test.go @@ -0,0 +1,32 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual semanticClassification2" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticClassification2(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `interface /*0*/Thing { + toExponential(): number; +} + +var Thing = 0; +Thing.toExponential();` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "interface.declaration", Text: "Thing"}, + {Type: "method.declaration", Text: "toExponential"}, + {Type: "variable.declaration", Text: "Thing"}, + {Type: "variable", Text: "Thing"}, + {Type: "method.defaultLibrary", Text: "toExponential"}, + }) +} diff --git a/internal/fourslash/tests/gen/semanticClassificationAlias_test.go b/internal/fourslash/tests/gen/semanticClassificationAlias_test.go new file mode 100644 index 00000000000..9b30bb27a0e --- /dev/null +++ b/internal/fourslash/tests/gen/semanticClassificationAlias_test.go @@ -0,0 +1,31 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual semanticClassificationAlias" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticClassificationAlias(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `// @Filename: /a.ts +export type x = number; +export class y {}; +// @Filename: /b.ts +import { /*0*/x, /*1*/y } from "./a"; +const v: /*2*/x = /*3*/y;` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.GoToFile(t, "/b.ts") + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "variable.declaration.readonly", Text: "v"}, + {Type: "type", Text: "x"}, + {Type: "class", Text: "y"}, + }) +} diff --git a/internal/fourslash/tests/gen/semanticClassificationInTemplateExpressions_test.go b/internal/fourslash/tests/gen/semanticClassificationInTemplateExpressions_test.go new file mode 100644 index 00000000000..0a65690d179 --- /dev/null +++ b/internal/fourslash/tests/gen/semanticClassificationInTemplateExpressions_test.go @@ -0,0 +1,41 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual semanticClassificationInTemplateExpressions" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticClassificationInTemplateExpressions(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `module /*0*/M { + export class /*1*/C { + static x; + } + export enum /*2*/E { + E1 = 0 + } +} +` + "`" + `abcd${ /*3*/M./*4*/C.x + /*5*/M./*6*/E.E1}efg` + "`" + `` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "namespace.declaration", Text: "M"}, + {Type: "class.declaration", Text: "C"}, + {Type: "property.declaration.static", Text: "x"}, + {Type: "enum.declaration", Text: "E"}, + {Type: "enumMember.declaration.readonly", Text: "E1"}, + {Type: "namespace", Text: "M"}, + {Type: "class", Text: "C"}, + {Type: "property.static", Text: "x"}, + {Type: "namespace", Text: "M"}, + {Type: "enum", Text: "E"}, + {Type: "enumMember.readonly", Text: "E1"}, + }) +} diff --git a/internal/fourslash/tests/gen/semanticClassificationInstantiatedModuleWithVariableOfSameName1_test.go b/internal/fourslash/tests/gen/semanticClassificationInstantiatedModuleWithVariableOfSameName1_test.go new file mode 100644 index 00000000000..912404138a1 --- /dev/null +++ b/internal/fourslash/tests/gen/semanticClassificationInstantiatedModuleWithVariableOfSameName1_test.go @@ -0,0 +1,46 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual semanticClassificationInstantiatedModuleWithVariableOfSameName1" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticClassificationInstantiatedModuleWithVariableOfSameName1(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `module /*0*/M { + export interface /*1*/I { + } + var x = 10; +} + +var /*2*/M = { + foo: 10, + bar: 20 +} + +var v: /*3*/M./*4*/I; + +var x = /*5*/M;` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "namespace.declaration", Text: "M"}, + {Type: "interface.declaration", Text: "I"}, + {Type: "variable.declaration.local", Text: "x"}, + {Type: "variable.declaration", Text: "M"}, + {Type: "property.declaration", Text: "foo"}, + {Type: "property.declaration", Text: "bar"}, + {Type: "variable.declaration", Text: "v"}, + {Type: "namespace", Text: "M"}, + {Type: "interface", Text: "I"}, + {Type: "variable.declaration", Text: "x"}, + {Type: "namespace", Text: "M"}, + }) +} diff --git a/internal/fourslash/tests/gen/semanticClassificationInstantiatedModuleWithVariableOfSameName2_test.go b/internal/fourslash/tests/gen/semanticClassificationInstantiatedModuleWithVariableOfSameName2_test.go new file mode 100644 index 00000000000..130822f2341 --- /dev/null +++ b/internal/fourslash/tests/gen/semanticClassificationInstantiatedModuleWithVariableOfSameName2_test.go @@ -0,0 +1,50 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual semanticClassificationInstantiatedModuleWithVariableOfSameName2" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticClassificationInstantiatedModuleWithVariableOfSameName2(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `module /*0*/M { + export interface /*1*/I { + } +} + +module /*2*/M { + var x = 10; +} + +var /*3*/M = { + foo: 10, + bar: 20 +} + +var v: /*4*/M./*5*/I; + +var x = /*6*/M;` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "namespace.declaration", Text: "M"}, + {Type: "interface.declaration", Text: "I"}, + {Type: "namespace.declaration", Text: "M"}, + {Type: "variable.declaration.local", Text: "x"}, + {Type: "variable.declaration", Text: "M"}, + {Type: "property.declaration", Text: "foo"}, + {Type: "property.declaration", Text: "bar"}, + {Type: "variable.declaration", Text: "v"}, + {Type: "namespace", Text: "M"}, + {Type: "interface", Text: "I"}, + {Type: "variable.declaration", Text: "x"}, + {Type: "namespace", Text: "M"}, + }) +} diff --git a/internal/fourslash/tests/gen/semanticClassificationModules_test.go b/internal/fourslash/tests/gen/semanticClassificationModules_test.go new file mode 100644 index 00000000000..d669ed6ec97 --- /dev/null +++ b/internal/fourslash/tests/gen/semanticClassificationModules_test.go @@ -0,0 +1,39 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual semanticClassificationModules" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticClassificationModules(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `module /*0*/M { + export var v; + export interface /*1*/I { + } +} + +var x: /*2*/M./*3*/I = /*4*/M.v; +var y = /*5*/M;` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "namespace.declaration", Text: "M"}, + {Type: "variable.declaration.local", Text: "v"}, + {Type: "interface.declaration", Text: "I"}, + {Type: "variable.declaration", Text: "x"}, + {Type: "namespace", Text: "M"}, + {Type: "interface", Text: "I"}, + {Type: "namespace", Text: "M"}, + {Type: "variable.local", Text: "v"}, + {Type: "variable.declaration", Text: "y"}, + {Type: "namespace", Text: "M"}, + }) +} diff --git a/internal/fourslash/tests/gen/semanticClassificationUninstantiatedModuleWithVariableOfSameName1_test.go b/internal/fourslash/tests/gen/semanticClassificationUninstantiatedModuleWithVariableOfSameName1_test.go new file mode 100644 index 00000000000..e36745b9a75 --- /dev/null +++ b/internal/fourslash/tests/gen/semanticClassificationUninstantiatedModuleWithVariableOfSameName1_test.go @@ -0,0 +1,32 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual semanticClassificationUninstantiatedModuleWithVariableOfSameName1" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticClassificationUninstantiatedModuleWithVariableOfSameName1(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `declare module /*0*/M { + interface /*1*/I { + + } +} + +var M = { I: 10 };` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "variable", Text: "M"}, + {Type: "interface.declaration", Text: "I"}, + {Type: "variable.declaration", Text: "M"}, + {Type: "property.declaration", Text: "I"}, + }) +} diff --git a/internal/fourslash/tests/gen/semanticClassificationUninstantiatedModuleWithVariableOfSameName2_test.go b/internal/fourslash/tests/gen/semanticClassificationUninstantiatedModuleWithVariableOfSameName2_test.go new file mode 100644 index 00000000000..7a565729e7f --- /dev/null +++ b/internal/fourslash/tests/gen/semanticClassificationUninstantiatedModuleWithVariableOfSameName2_test.go @@ -0,0 +1,44 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual semanticClassificationUninstantiatedModuleWithVariableOfSameName2" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticClassificationUninstantiatedModuleWithVariableOfSameName2(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `module /*0*/M { + export interface /*1*/I { + } +} + +var /*2*/M = { + foo: 10, + bar: 20 +} + +var v: /*3*/M./*4*/I; + +var x = /*5*/M;` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "variable", Text: "M"}, + {Type: "interface.declaration", Text: "I"}, + {Type: "variable.declaration", Text: "M"}, + {Type: "property.declaration", Text: "foo"}, + {Type: "property.declaration", Text: "bar"}, + {Type: "variable.declaration", Text: "v"}, + {Type: "variable", Text: "M"}, + {Type: "interface", Text: "I"}, + {Type: "variable.declaration", Text: "x"}, + {Type: "variable", Text: "M"}, + }) +} diff --git a/internal/fourslash/tests/gen/semanticClassificationWithUnionTypes_test.go b/internal/fourslash/tests/gen/semanticClassificationWithUnionTypes_test.go new file mode 100644 index 00000000000..4f930f25315 --- /dev/null +++ b/internal/fourslash/tests/gen/semanticClassificationWithUnionTypes_test.go @@ -0,0 +1,45 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual semanticClassificationWithUnionTypes" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticClassificationWithUnionTypes(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `module /*0*/M { + export interface /*1*/I { + } +} + +interface /*2*/I { +} +class /*3*/C { +} + +var M: /*4*/M./*5*/I | /*6*/I | /*7*/C; +var I: typeof M | typeof /*8*/C;` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "variable", Text: "M"}, + {Type: "interface.declaration", Text: "I"}, + {Type: "interface.declaration", Text: "I"}, + {Type: "class.declaration", Text: "C"}, + {Type: "variable.declaration", Text: "M"}, + {Type: "variable", Text: "M"}, + {Type: "interface", Text: "I"}, + {Type: "interface", Text: "I"}, + {Type: "class", Text: "C"}, + {Type: "class.declaration", Text: "I"}, + {Type: "variable", Text: "M"}, + {Type: "class", Text: "C"}, + }) +} diff --git a/internal/fourslash/tests/gen/semanticClassificatonTypeAlias_test.go b/internal/fourslash/tests/gen/semanticClassificatonTypeAlias_test.go new file mode 100644 index 00000000000..786374e16b7 --- /dev/null +++ b/internal/fourslash/tests/gen/semanticClassificatonTypeAlias_test.go @@ -0,0 +1,34 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual semanticClassificatonTypeAlias" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticClassificatonTypeAlias(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `type /*0*/Alias = number +var x: /*1*/Alias; +var y = {}; +function f(x: /*3*/Alias): /*4*/Alias { return undefined; }` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "type.declaration", Text: "Alias"}, + {Type: "variable.declaration", Text: "x"}, + {Type: "type", Text: "Alias"}, + {Type: "variable.declaration", Text: "y"}, + {Type: "type", Text: "Alias"}, + {Type: "function.declaration", Text: "f"}, + {Type: "parameter.declaration", Text: "x"}, + {Type: "type", Text: "Alias"}, + {Type: "type", Text: "Alias"}, + }) +} diff --git a/internal/fourslash/tests/gen/semanticModernClassificationCallableVariables2_test.go b/internal/fourslash/tests/gen/semanticModernClassificationCallableVariables2_test.go new file mode 100644 index 00000000000..cab3aef95e2 --- /dev/null +++ b/internal/fourslash/tests/gen/semanticModernClassificationCallableVariables2_test.go @@ -0,0 +1,42 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual semanticModernClassificationCallableVariables2" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticModernClassificationCallableVariables2(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `import "node"; +var fs = require("fs") +require.resolve('react'); +require.resolve.paths; +interface LanguageMode { getFoldingRanges?: (d: string) => number[]; }; +function (mode: LanguageMode | undefined) { if (mode && mode.getFoldingRanges) { return mode.getFoldingRanges('a'); }}; +function b(a: () => void) { a(); };` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "variable.declaration", Text: "fs"}, + {Type: "interface.declaration", Text: "LanguageMode"}, + {Type: "method.declaration", Text: "getFoldingRanges"}, + {Type: "parameter.declaration", Text: "d"}, + {Type: "parameter.declaration", Text: "mode"}, + {Type: "interface", Text: "LanguageMode"}, + {Type: "parameter", Text: "mode"}, + {Type: "parameter", Text: "mode"}, + {Type: "method", Text: "getFoldingRanges"}, + {Type: "parameter", Text: "mode"}, + {Type: "method", Text: "getFoldingRanges"}, + {Type: "function.declaration", Text: "b"}, + {Type: "function.declaration", Text: "a"}, + {Type: "function", Text: "a"}, + }) +} diff --git a/internal/fourslash/tests/gen/semanticModernClassificationCallableVariables_test.go b/internal/fourslash/tests/gen/semanticModernClassificationCallableVariables_test.go new file mode 100644 index 00000000000..c304c55ea88 --- /dev/null +++ b/internal/fourslash/tests/gen/semanticModernClassificationCallableVariables_test.go @@ -0,0 +1,48 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual semanticModernClassificationCallableVariables" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticModernClassificationCallableVariables(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `class A { onEvent: () => void; } +const x = new A().onEvent; +const match = (s: any) => x(); +const other = match; +match({ other }); +interface B = { (): string; }; var b: B +var s: String; +var t: { (): string; foo: string};` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "class.declaration", Text: "A"}, + {Type: "method.declaration", Text: "onEvent"}, + {Type: "function.declaration.readonly", Text: "x"}, + {Type: "class", Text: "A"}, + {Type: "method", Text: "onEvent"}, + {Type: "function.declaration.readonly", Text: "match"}, + {Type: "parameter.declaration", Text: "s"}, + {Type: "function.readonly", Text: "x"}, + {Type: "function.declaration.readonly", Text: "other"}, + {Type: "function.readonly", Text: "match"}, + {Type: "function.readonly", Text: "match"}, + {Type: "method.declaration", Text: "other"}, + {Type: "interface.declaration", Text: "B"}, + {Type: "variable.declaration", Text: "b"}, + {Type: "interface", Text: "B"}, + {Type: "variable.declaration", Text: "s"}, + {Type: "interface.defaultLibrary", Text: "String"}, + {Type: "variable.declaration", Text: "t"}, + {Type: "property.declaration", Text: "foo"}, + }) +} diff --git a/internal/fourslash/tests/gen/semanticModernClassificationClassProperties_test.go b/internal/fourslash/tests/gen/semanticModernClassificationClassProperties_test.go new file mode 100644 index 00000000000..c077def90fa --- /dev/null +++ b/internal/fourslash/tests/gen/semanticModernClassificationClassProperties_test.go @@ -0,0 +1,38 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual semanticModernClassificationClassProperties" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticModernClassificationClassProperties(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `class A { + private y: number; + constructor(public x : number, _y : number) { this.y = _y; } + get z() : number { return this.x + this.y; } + set a(v: number) { } +}` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "class.declaration", Text: "A"}, + {Type: "property.declaration", Text: "y"}, + {Type: "parameter.declaration", Text: "x"}, + {Type: "parameter.declaration", Text: "_y"}, + {Type: "property", Text: "y"}, + {Type: "parameter", Text: "_y"}, + {Type: "property.declaration", Text: "z"}, + {Type: "property", Text: "x"}, + {Type: "property", Text: "y"}, + {Type: "property.declaration", Text: "a"}, + {Type: "parameter.declaration", Text: "v"}, + }) +} diff --git a/internal/fourslash/tests/gen/semanticModernClassificationConstructorTypes_test.go b/internal/fourslash/tests/gen/semanticModernClassificationConstructorTypes_test.go new file mode 100644 index 00000000000..54989ed0068 --- /dev/null +++ b/internal/fourslash/tests/gen/semanticModernClassificationConstructorTypes_test.go @@ -0,0 +1,31 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual semanticModernClassificationConstructorTypes" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticModernClassificationConstructorTypes(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `// @lib: es5 +Object.create(null); +const x = Promise.resolve(Number.MAX_VALUE); +if (x instanceof Promise) {}` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "class.defaultLibrary", Text: "Object"}, + {Type: "method.defaultLibrary", Text: "create"}, + {Type: "variable.declaration.readonly", Text: "x"}, + {Type: "class.defaultLibrary", Text: "Number"}, + {Type: "property.readonly.defaultLibrary", Text: "MAX_VALUE"}, + {Type: "variable.readonly", Text: "x"}, + }) +} diff --git a/internal/fourslash/tests/gen/semanticModernClassificationInfinityAndNaN_test.go b/internal/fourslash/tests/gen/semanticModernClassificationInfinityAndNaN_test.go new file mode 100644 index 00000000000..a44e7b6e337 --- /dev/null +++ b/internal/fourslash/tests/gen/semanticModernClassificationInfinityAndNaN_test.go @@ -0,0 +1,52 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual semanticModernClassificationInfinityAndNaN" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticModernClassificationInfinityAndNaN(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = ` Infinity; + NaN; + +// Regular properties + +const obj1 = { + Infinity: 100, + NaN: 200, + "-Infinity": 300 +}; + +obj1.Infinity; +obj1.NaN; +obj1["-Infinity"]; + +// Shorthand properties + +const obj2 = { + Infinity, + NaN, +} + +obj2.Infinity; +obj2.NaN;` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "variable.declaration.readonly", Text: "obj1"}, + {Type: "variable.readonly", Text: "obj1"}, + {Type: "variable.readonly", Text: "obj1"}, + {Type: "variable.readonly", Text: "obj1"}, + {Type: "variable.declaration.readonly", Text: "obj2"}, + {Type: "variable.readonly", Text: "obj2"}, + {Type: "variable.readonly", Text: "obj2"}, + }) +} diff --git a/internal/fourslash/tests/gen/semanticModernClassificationInterfaces_test.go b/internal/fourslash/tests/gen/semanticModernClassificationInterfaces_test.go new file mode 100644 index 00000000000..9b1895227e7 --- /dev/null +++ b/internal/fourslash/tests/gen/semanticModernClassificationInterfaces_test.go @@ -0,0 +1,38 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual semanticModernClassificationInterfaces" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticModernClassificationInterfaces(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `interface Pos { x: number, y: number }; +const p = { x: 1, y: 2 } as Pos; +const foo = (o: Pos) => o.x + o.y;` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "interface.declaration", Text: "Pos"}, + {Type: "property.declaration", Text: "x"}, + {Type: "property.declaration", Text: "y"}, + {Type: "variable.declaration.readonly", Text: "p"}, + {Type: "property.declaration", Text: "x"}, + {Type: "property.declaration", Text: "y"}, + {Type: "interface", Text: "Pos"}, + {Type: "function.declaration.readonly", Text: "foo"}, + {Type: "parameter.declaration", Text: "o"}, + {Type: "interface", Text: "Pos"}, + {Type: "parameter", Text: "o"}, + {Type: "property", Text: "x"}, + {Type: "parameter", Text: "o"}, + {Type: "property", Text: "y"}, + }) +} diff --git a/internal/fourslash/tests/gen/semanticModernClassificationMembers_test.go b/internal/fourslash/tests/gen/semanticModernClassificationMembers_test.go new file mode 100644 index 00000000000..89763185523 --- /dev/null +++ b/internal/fourslash/tests/gen/semanticModernClassificationMembers_test.go @@ -0,0 +1,41 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual semanticModernClassificationMembers" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticModernClassificationMembers(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `class A { + static x = 9; + f = 9; + async m() { return A.x + await this.m(); }; + get s() { return this.f; + static t() { return new A().f; }; + constructor() {} +}` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "class.declaration", Text: "A"}, + {Type: "property.declaration.static", Text: "x"}, + {Type: "property.declaration", Text: "f"}, + {Type: "method.declaration.async", Text: "m"}, + {Type: "class", Text: "A"}, + {Type: "property.static", Text: "x"}, + {Type: "method.async", Text: "m"}, + {Type: "property.declaration", Text: "s"}, + {Type: "property", Text: "f"}, + {Type: "method.declaration.static", Text: "t"}, + {Type: "class", Text: "A"}, + {Type: "property", Text: "f"}, + }) +} diff --git a/internal/fourslash/tests/gen/semanticModernClassificationObjectProperties_test.go b/internal/fourslash/tests/gen/semanticModernClassificationObjectProperties_test.go new file mode 100644 index 00000000000..19a21cc40d7 --- /dev/null +++ b/internal/fourslash/tests/gen/semanticModernClassificationObjectProperties_test.go @@ -0,0 +1,30 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual semanticModernClassificationObjectProperties" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticModernClassificationObjectProperties(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `let x = 1, y = 1; +const a1 = { e: 1 }; +var a2 = { x };` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "variable.declaration", Text: "x"}, + {Type: "variable.declaration", Text: "y"}, + {Type: "variable.declaration.readonly", Text: "a1"}, + {Type: "property.declaration", Text: "e"}, + {Type: "variable.declaration", Text: "a2"}, + {Type: "property.declaration", Text: "x"}, + }) +} diff --git a/internal/fourslash/tests/gen/semanticModernClassificationVariables_test.go b/internal/fourslash/tests/gen/semanticModernClassificationVariables_test.go new file mode 100644 index 00000000000..a2fb3e0175a --- /dev/null +++ b/internal/fourslash/tests/gen/semanticModernClassificationVariables_test.go @@ -0,0 +1,36 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual semanticModernClassificationVariables" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticModernClassificationVariables(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = ` var x = 9, y1 = [x]; + try { + for (const s of y1) { x = s } + } catch (e) { + throw y1; + }` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "variable.declaration", Text: "x"}, + {Type: "variable.declaration", Text: "y1"}, + {Type: "variable", Text: "x"}, + {Type: "variable.declaration.readonly.local", Text: "s"}, + {Type: "variable", Text: "y1"}, + {Type: "variable", Text: "x"}, + {Type: "variable.readonly.local", Text: "s"}, + {Type: "variable.declaration.local", Text: "e"}, + {Type: "variable", Text: "y1"}, + }) +} diff --git a/internal/fourslash/tests/gen/syntacticClassificationForJSDocTemplateTag_test.go b/internal/fourslash/tests/gen/syntacticClassificationForJSDocTemplateTag_test.go new file mode 100644 index 00000000000..d66500b84b5 --- /dev/null +++ b/internal/fourslash/tests/gen/syntacticClassificationForJSDocTemplateTag_test.go @@ -0,0 +1,27 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual syntacticClassificationForJSDocTemplateTag" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSyntacticClassificationForJSDocTemplateTag(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `/** @template T baring strait */ +function ident: T { +}` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "function.declaration", Text: "ident"}, + {Type: "typeParameter.declaration", Text: "T"}, + {Type: "typeParameter", Text: "T"}, + }) +} diff --git a/internal/fourslash/tests/gen/syntacticClassificationWithErrors_test.go b/internal/fourslash/tests/gen/syntacticClassificationWithErrors_test.go new file mode 100644 index 00000000000..ac423f120e0 --- /dev/null +++ b/internal/fourslash/tests/gen/syntacticClassificationWithErrors_test.go @@ -0,0 +1,27 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual syntacticClassificationWithErrors" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSyntacticClassificationWithErrors(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `class A { + a: +} +c =` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "class.declaration", Text: "A"}, + {Type: "property.declaration", Text: "a"}, + }) +} diff --git a/internal/fourslash/tests/gen/syntacticClassifications1_test.go b/internal/fourslash/tests/gen/syntacticClassifications1_test.go new file mode 100644 index 00000000000..8160758efcd --- /dev/null +++ b/internal/fourslash/tests/gen/syntacticClassifications1_test.go @@ -0,0 +1,47 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual syntacticClassifications1" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSyntacticClassifications1(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `// comment +namespace M { + var v = 0 + 1; + var s = "string"; + + class C { + } + + enum E { + } + + interface I { + } + + namespace M1.M2 { + } +}` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "namespace.declaration", Text: "M"}, + {Type: "variable.declaration.local", Text: "v"}, + {Type: "variable.declaration.local", Text: "s"}, + {Type: "class.declaration", Text: "C"}, + {Type: "typeParameter.declaration", Text: "T"}, + {Type: "enum.declaration", Text: "E"}, + {Type: "interface.declaration", Text: "I"}, + {Type: "namespace.declaration", Text: "M1"}, + {Type: "namespace.declaration", Text: "M2"}, + }) +} diff --git a/internal/fourslash/tests/gen/syntacticClassificationsConflictDiff3Markers1_test.go b/internal/fourslash/tests/gen/syntacticClassificationsConflictDiff3Markers1_test.go new file mode 100644 index 00000000000..81924fd1aee --- /dev/null +++ b/internal/fourslash/tests/gen/syntacticClassificationsConflictDiff3Markers1_test.go @@ -0,0 +1,32 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual syntacticClassificationsConflictDiff3Markers1" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSyntacticClassificationsConflictDiff3Markers1(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `class C { +<<<<<<< HEAD + v = 1; +||||||| merged common ancestors + v = 3; +======= + v = 2; +>>>>>>> Branch - a +}` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "class.declaration", Text: "C"}, + {Type: "property.declaration", Text: "v"}, + }) +} diff --git a/internal/fourslash/tests/gen/syntacticClassificationsConflictDiff3Markers2_test.go b/internal/fourslash/tests/gen/syntacticClassificationsConflictDiff3Markers2_test.go new file mode 100644 index 00000000000..6c8d5f07a5f --- /dev/null +++ b/internal/fourslash/tests/gen/syntacticClassificationsConflictDiff3Markers2_test.go @@ -0,0 +1,29 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual syntacticClassificationsConflictDiff3Markers2" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSyntacticClassificationsConflictDiff3Markers2(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `<<<<<<< HEAD +class C { } +||||||| merged common ancestors +class E { } +======= +class D { } +>>>>>>> Branch - a` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "class.declaration", Text: "C"}, + }) +} diff --git a/internal/fourslash/tests/gen/syntacticClassificationsConflictMarkers1_test.go b/internal/fourslash/tests/gen/syntacticClassificationsConflictMarkers1_test.go new file mode 100644 index 00000000000..d6150d35c80 --- /dev/null +++ b/internal/fourslash/tests/gen/syntacticClassificationsConflictMarkers1_test.go @@ -0,0 +1,30 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual syntacticClassificationsConflictMarkers1" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSyntacticClassificationsConflictMarkers1(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `class C { +<<<<<<< HEAD + v = 1; +======= + v = 2; +>>>>>>> Branch - a +}` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "class.declaration", Text: "C"}, + {Type: "property.declaration", Text: "v"}, + }) +} diff --git a/internal/fourslash/tests/gen/syntacticClassificationsConflictMarkers2_test.go b/internal/fourslash/tests/gen/syntacticClassificationsConflictMarkers2_test.go new file mode 100644 index 00000000000..3dbb8ac4c9f --- /dev/null +++ b/internal/fourslash/tests/gen/syntacticClassificationsConflictMarkers2_test.go @@ -0,0 +1,27 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual syntacticClassificationsConflictMarkers2" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSyntacticClassificationsConflictMarkers2(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `<<<<<<< HEAD +class C { } +======= +class D { } +>>>>>>> Branch - a` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "class.declaration", Text: "C"}, + }) +} diff --git a/internal/fourslash/tests/gen/syntacticClassificationsDocComment1_test.go b/internal/fourslash/tests/gen/syntacticClassificationsDocComment1_test.go new file mode 100644 index 00000000000..3b45c730732 --- /dev/null +++ b/internal/fourslash/tests/gen/syntacticClassificationsDocComment1_test.go @@ -0,0 +1,24 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual syntacticClassificationsDocComment1" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSyntacticClassificationsDocComment1(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `/** @type {number} */ +var v;` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "variable.declaration", Text: "v"}, + }) +} diff --git a/internal/fourslash/tests/gen/syntacticClassificationsDocComment2_test.go b/internal/fourslash/tests/gen/syntacticClassificationsDocComment2_test.go new file mode 100644 index 00000000000..55f05ccec1a --- /dev/null +++ b/internal/fourslash/tests/gen/syntacticClassificationsDocComment2_test.go @@ -0,0 +1,24 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual syntacticClassificationsDocComment2" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSyntacticClassificationsDocComment2(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `/** @param foo { function(x): string } */ +var v;` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "variable.declaration", Text: "v"}, + }) +} diff --git a/internal/fourslash/tests/gen/syntacticClassificationsDocComment3_test.go b/internal/fourslash/tests/gen/syntacticClassificationsDocComment3_test.go new file mode 100644 index 00000000000..526816adcae --- /dev/null +++ b/internal/fourslash/tests/gen/syntacticClassificationsDocComment3_test.go @@ -0,0 +1,24 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual syntacticClassificationsDocComment3" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSyntacticClassificationsDocComment3(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `/** @param foo { number /* } */ +var v;` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "variable.declaration", Text: "v"}, + }) +} diff --git a/internal/fourslash/tests/gen/syntacticClassificationsDocComment4_test.go b/internal/fourslash/tests/gen/syntacticClassificationsDocComment4_test.go new file mode 100644 index 00000000000..9e07ac39c23 --- /dev/null +++ b/internal/fourslash/tests/gen/syntacticClassificationsDocComment4_test.go @@ -0,0 +1,25 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual syntacticClassificationsDocComment4" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSyntacticClassificationsDocComment4(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `/** @param {number} p1 */ +function foo(p1) {}` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "function.declaration", Text: "foo"}, + {Type: "parameter.declaration", Text: "p1"}, + }) +} diff --git a/internal/fourslash/tests/gen/syntacticClassificationsForOfKeyword2_test.go b/internal/fourslash/tests/gen/syntacticClassificationsForOfKeyword2_test.go new file mode 100644 index 00000000000..e108c58a565 --- /dev/null +++ b/internal/fourslash/tests/gen/syntacticClassificationsForOfKeyword2_test.go @@ -0,0 +1,24 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual syntacticClassificationsForOfKeyword2" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSyntacticClassificationsForOfKeyword2(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `for (var of in of) { }` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "variable.declaration", Text: "of"}, + {Type: "variable", Text: "of"}, + }) +} diff --git a/internal/fourslash/tests/gen/syntacticClassificationsForOfKeyword3_test.go b/internal/fourslash/tests/gen/syntacticClassificationsForOfKeyword3_test.go new file mode 100644 index 00000000000..5bc4be5a1c9 --- /dev/null +++ b/internal/fourslash/tests/gen/syntacticClassificationsForOfKeyword3_test.go @@ -0,0 +1,25 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual syntacticClassificationsForOfKeyword3" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSyntacticClassificationsForOfKeyword3(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `for (var of; of; of) { }` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "variable.declaration", Text: "of"}, + {Type: "variable", Text: "of"}, + {Type: "variable", Text: "of"}, + }) +} diff --git a/internal/fourslash/tests/gen/syntacticClassificationsForOfKeyword_test.go b/internal/fourslash/tests/gen/syntacticClassificationsForOfKeyword_test.go new file mode 100644 index 00000000000..09e881bf1ea --- /dev/null +++ b/internal/fourslash/tests/gen/syntacticClassificationsForOfKeyword_test.go @@ -0,0 +1,24 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual syntacticClassificationsForOfKeyword" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSyntacticClassificationsForOfKeyword(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `for (var of of of) { }` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "variable.declaration", Text: "of"}, + {Type: "variable", Text: "of"}, + }) +} diff --git a/internal/fourslash/tests/gen/syntacticClassificationsFunctionWithComments_test.go b/internal/fourslash/tests/gen/syntacticClassificationsFunctionWithComments_test.go new file mode 100644 index 00000000000..2ff9fb718ab --- /dev/null +++ b/internal/fourslash/tests/gen/syntacticClassificationsFunctionWithComments_test.go @@ -0,0 +1,35 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual syntacticClassificationsFunctionWithComments" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSyntacticClassificationsFunctionWithComments(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `/** + * This is my function. + * There are many like it, but this one is mine. + */ +function myFunction(/* x */ x: any) { + var y = x ? x++ : ++x; +} +// end of file` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "function.declaration", Text: "myFunction"}, + {Type: "parameter.declaration", Text: "x"}, + {Type: "variable.declaration.local", Text: "y"}, + {Type: "parameter", Text: "x"}, + {Type: "parameter", Text: "x"}, + {Type: "parameter", Text: "x"}, + }) +} diff --git a/internal/fourslash/tests/gen/syntacticClassificationsJsx1_test.go b/internal/fourslash/tests/gen/syntacticClassificationsJsx1_test.go new file mode 100644 index 00000000000..2088175f39f --- /dev/null +++ b/internal/fourslash/tests/gen/syntacticClassificationsJsx1_test.go @@ -0,0 +1,29 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual syntacticClassificationsJsx1" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSyntacticClassificationsJsx1(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `// @Filename: file1.tsx +let x =
+ some jsx text +
; + +let y = ` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "variable.declaration", Text: "x"}, + {Type: "variable.declaration", Text: "y"}, + }) +} diff --git a/internal/fourslash/tests/gen/syntacticClassificationsJsx2_test.go b/internal/fourslash/tests/gen/syntacticClassificationsJsx2_test.go new file mode 100644 index 00000000000..492e97297c0 --- /dev/null +++ b/internal/fourslash/tests/gen/syntacticClassificationsJsx2_test.go @@ -0,0 +1,29 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual syntacticClassificationsJsx2" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSyntacticClassificationsJsx2(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `// @Filename: file1.tsx +let x = + some jsx text +; + +let y = ` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "variable.declaration", Text: "x"}, + {Type: "variable.declaration", Text: "y"}, + }) +} diff --git a/internal/fourslash/tests/gen/syntacticClassificationsMergeConflictMarker1_test.go b/internal/fourslash/tests/gen/syntacticClassificationsMergeConflictMarker1_test.go new file mode 100644 index 00000000000..1443257863b --- /dev/null +++ b/internal/fourslash/tests/gen/syntacticClassificationsMergeConflictMarker1_test.go @@ -0,0 +1,25 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual syntacticClassificationsMergeConflictMarker1" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSyntacticClassificationsMergeConflictMarker1(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `<<<<<<< HEAD +"AAAA" +======= +"BBBB" +>>>>>>> Feature` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{}) +} diff --git a/internal/fourslash/tests/gen/syntacticClassificationsObjectLiteral_test.go b/internal/fourslash/tests/gen/syntacticClassificationsObjectLiteral_test.go new file mode 100644 index 00000000000..a6d9353329f --- /dev/null +++ b/internal/fourslash/tests/gen/syntacticClassificationsObjectLiteral_test.go @@ -0,0 +1,42 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual syntacticClassificationsObjectLiteral" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSyntacticClassificationsObjectLiteral(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `var v = 10e0; +var x = { + p1: 1, + p2: 2, + any: 3, + function: 4, + var: 5, + void: void 0, + v: v += v, +};` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "variable.declaration", Text: "v"}, + {Type: "variable.declaration", Text: "x"}, + {Type: "property.declaration", Text: "p1"}, + {Type: "property.declaration", Text: "p2"}, + {Type: "property.declaration", Text: "any"}, + {Type: "property.declaration", Text: "function"}, + {Type: "property.declaration", Text: "var"}, + {Type: "property.declaration", Text: "void"}, + {Type: "property.declaration", Text: "v"}, + {Type: "variable", Text: "v"}, + {Type: "variable", Text: "v"}, + }) +} diff --git a/internal/fourslash/tests/gen/syntacticClassificationsTemplates1_test.go b/internal/fourslash/tests/gen/syntacticClassificationsTemplates1_test.go new file mode 100644 index 00000000000..66edaa76566 --- /dev/null +++ b/internal/fourslash/tests/gen/syntacticClassificationsTemplates1_test.go @@ -0,0 +1,30 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual syntacticClassificationsTemplates1" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSyntacticClassificationsTemplates1(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `var v = 10e0; +var x = { + p1: ` + "`" + `hello world` + "`" + `, + p2: ` + "`" + `goodbye ${0} cruel ${0} world` + "`" + `, +};` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "variable.declaration", Text: "v"}, + {Type: "variable.declaration", Text: "x"}, + {Type: "property.declaration", Text: "p1"}, + {Type: "property.declaration", Text: "p2"}, + }) +} diff --git a/internal/fourslash/tests/gen/syntacticClassificationsTemplates2_test.go b/internal/fourslash/tests/gen/syntacticClassificationsTemplates2_test.go new file mode 100644 index 00000000000..b1eafaac7d2 --- /dev/null +++ b/internal/fourslash/tests/gen/syntacticClassificationsTemplates2_test.go @@ -0,0 +1,25 @@ +// Code generated by convertFourslash; DO NOT EDIT. +// To modify this test, run "npm run makemanual syntacticClassificationsTemplates2" + +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSyntacticClassificationsTemplates2(t *testing.T) { + fourslash.SkipIfFailing(t) + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `var tiredOfCanonicalExamples = +` + "`" + `goodbye "${ ` + "`" + `hello world` + "`" + ` }" +and ${ ` + "`" + `good${ " " }riddance` + "`" + ` }` + "`" + `;` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "variable.declaration", Text: "tiredOfCanonicalExamples"}, + }) +} diff --git a/internal/fourslash/tests/manual/semanticClassificationClassExpression_test.go b/internal/fourslash/tests/manual/semanticClassificationClassExpression_test.go new file mode 100644 index 00000000000..ea2939b73ed --- /dev/null +++ b/internal/fourslash/tests/manual/semanticClassificationClassExpression_test.go @@ -0,0 +1,25 @@ +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticClassificationClassExpression(t *testing.T) { + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `var x = class /*0*/C {} +class /*1*/C {} +class /*2*/D extends class /*3*/B{} { }` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "class.declaration", Text: "x"}, + {Type: "class.declaration", Text: "C"}, + {Type: "class.declaration", Text: "C"}, + {Type: "class.declaration", Text: "D"}, + {Type: "class.declaration", Text: "B"}, + }) +} diff --git a/internal/fourslash/tests/manual/semanticModernClassificationFunctions_test.go b/internal/fourslash/tests/manual/semanticModernClassificationFunctions_test.go new file mode 100644 index 00000000000..75e4d9d813e --- /dev/null +++ b/internal/fourslash/tests/manual/semanticModernClassificationFunctions_test.go @@ -0,0 +1,35 @@ +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticModernClassificationFunctions(t *testing.T) { + t.Parallel() + + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `function foo(p1) { + return foo(Math.abs(p1)) +} +` + "`" + `/${window.location}` + "`" + `.split("/").forEach(s => foo(s));` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "function.declaration", Text: "foo"}, + {Type: "parameter.declaration", Text: "p1"}, + {Type: "function", Text: "foo"}, + {Type: "variable.defaultLibrary", Text: "Math"}, + {Type: "method.defaultLibrary", Text: "abs"}, + {Type: "parameter", Text: "p1"}, + {Type: "variable.defaultLibrary", Text: "window"}, + {Type: "property.defaultLibrary", Text: "location"}, + {Type: "method.defaultLibrary", Text: "split"}, + {Type: "method.defaultLibrary", Text: "forEach"}, + {Type: "parameter.declaration", Text: "s"}, + {Type: "function", Text: "foo"}, + {Type: "parameter", Text: "s"}, + }) +} diff --git a/internal/fourslash/tests/semanticClassificationClassExpressionMethod_test.go b/internal/fourslash/tests/semanticClassificationClassExpressionMethod_test.go new file mode 100644 index 00000000000..1d4d5f15443 --- /dev/null +++ b/internal/fourslash/tests/semanticClassificationClassExpressionMethod_test.go @@ -0,0 +1,26 @@ +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticClassificationClassExpressionMethod(t *testing.T) { + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `var x = class C { + equals(other: C) { return this == other; } +};` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "class.declaration", Text: "x"}, + {Type: "class.declaration", Text: "C"}, + {Type: "method.declaration", Text: "equals"}, + {Type: "parameter.declaration", Text: "other"}, + {Type: "class", Text: "C"}, + {Type: "parameter", Text: "other"}, + }) +} diff --git a/internal/fourslash/tests/semanticClassificationJSX_test.go b/internal/fourslash/tests/semanticClassificationJSX_test.go new file mode 100644 index 00000000000..de4a5462b05 --- /dev/null +++ b/internal/fourslash/tests/semanticClassificationJSX_test.go @@ -0,0 +1,26 @@ +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/fourslash" + "github.com/microsoft/typescript-go/internal/testutil" +) + +func TestSemanticClassificationJSX(t *testing.T) { + t.Parallel() + + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `// @Filename: /a.tsx +const Component = () =>
Hello
; +const afterJSX = 42; +const alsoAfterJSX = "test";` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.GoToFile(t, "/a.tsx") + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "function.declaration.readonly", Text: "Component"}, + {Type: "variable.declaration.readonly", Text: "afterJSX"}, + {Type: "variable.declaration.readonly", Text: "alsoAfterJSX"}, + }) +} diff --git a/internal/ls/semantictokens.go b/internal/ls/semantictokens.go new file mode 100644 index 00000000000..84fb49e6a61 --- /dev/null +++ b/internal/ls/semantictokens.go @@ -0,0 +1,575 @@ +package ls + +import ( + "context" + "fmt" + "slices" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/checker" + "github.com/microsoft/typescript-go/internal/compiler" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/ls/lsconv" + "github.com/microsoft/typescript-go/internal/lsp/lsproto" + "github.com/microsoft/typescript-go/internal/scanner" + "github.com/microsoft/typescript-go/internal/tspath" +) + +// tokenTypes defines the order of token types for encoding +var tokenTypes = []lsproto.SemanticTokenType{ + lsproto.SemanticTokenTypeNamespace, + lsproto.SemanticTokenTypeClass, + lsproto.SemanticTokenTypeEnum, + lsproto.SemanticTokenTypeInterface, + lsproto.SemanticTokenTypeStruct, + lsproto.SemanticTokenTypeTypeParameter, + lsproto.SemanticTokenTypeType, + lsproto.SemanticTokenTypeParameter, + lsproto.SemanticTokenTypeVariable, + lsproto.SemanticTokenTypeProperty, + lsproto.SemanticTokenTypeEnumMember, + lsproto.SemanticTokenTypeDecorator, + lsproto.SemanticTokenTypeEvent, + lsproto.SemanticTokenTypeFunction, + lsproto.SemanticTokenTypeMethod, + lsproto.SemanticTokenTypeMacro, + lsproto.SemanticTokenTypeLabel, + lsproto.SemanticTokenTypeComment, + lsproto.SemanticTokenTypeString, + lsproto.SemanticTokenTypeKeyword, + lsproto.SemanticTokenTypeNumber, + lsproto.SemanticTokenTypeRegexp, + lsproto.SemanticTokenTypeOperator, +} + +// tokenModifiers defines the order of token modifiers for encoding +var tokenModifiers = []lsproto.SemanticTokenModifier{ + lsproto.SemanticTokenModifierDeclaration, + lsproto.SemanticTokenModifierDefinition, + lsproto.SemanticTokenModifierReadonly, + lsproto.SemanticTokenModifierStatic, + lsproto.SemanticTokenModifierDeprecated, + lsproto.SemanticTokenModifierAbstract, + lsproto.SemanticTokenModifierAsync, + lsproto.SemanticTokenModifierModification, + lsproto.SemanticTokenModifierDocumentation, + lsproto.SemanticTokenModifierDefaultLibrary, + "local", +} + +type tokenType int + +const ( + tokenTypeNamespace tokenType = iota + tokenTypeClass + tokenTypeEnum + tokenTypeInterface + tokenTypeStruct + tokenTypeTypeParameter + tokenTypeType + tokenTypeParameter + tokenTypeVariable + tokenTypeProperty + tokenTypeEnumMember + tokenTypeDecorator + tokenTypeEvent + tokenTypeFunction + tokenTypeMethod // Previously called "member" in TypeScript + tokenTypeMacro + tokenTypeLabel + tokenTypeComment + tokenTypeString + tokenTypeKeyword + tokenTypeNumber + tokenTypeRegexp + tokenTypeOperator +) + +type tokenModifier int + +const ( + tokenModifierDeclaration tokenModifier = 1 << iota + tokenModifierDefinition + tokenModifierReadonly + tokenModifierStatic + tokenModifierDeprecated + tokenModifierAbstract + tokenModifierAsync + tokenModifierModification + tokenModifierDocumentation + tokenModifierDefaultLibrary + tokenModifierLocal +) + +// SemanticTokensLegend returns the legend describing the token types and modifiers. +// It filters the legend to only include types and modifiers that the client supports, +// as indicated by clientCapabilities. +func SemanticTokensLegend(clientCapabilities lsproto.ResolvedSemanticTokensClientCapabilities) *lsproto.SemanticTokensLegend { + types := make([]string, 0, len(tokenTypes)) + for _, t := range tokenTypes { + if slices.Contains(clientCapabilities.TokenTypes, string(t)) { + types = append(types, string(t)) + } + } + modifiers := make([]string, 0, len(tokenModifiers)) + for _, m := range tokenModifiers { + if slices.Contains(clientCapabilities.TokenModifiers, string(m)) { + modifiers = append(modifiers, string(m)) + } + } + return &lsproto.SemanticTokensLegend{ + TokenTypes: types, + TokenModifiers: modifiers, + } +} + +func (l *LanguageService) ProvideSemanticTokens(ctx context.Context, documentURI lsproto.DocumentUri) (lsproto.SemanticTokensResponse, error) { + program, file := l.getProgramAndFile(documentURI) + + c, done := program.GetTypeCheckerForFile(ctx, file) + defer done() + + tokens := l.collectSemanticTokens(ctx, c, file, program) + + if len(tokens) == 0 { + return lsproto.SemanticTokensOrNull{}, nil + } + + // Convert to LSP format (relative encoding) + encoded := encodeSemanticTokens(ctx, tokens, file, l.converters) + + return lsproto.SemanticTokensOrNull{ + SemanticTokens: &lsproto.SemanticTokens{ + Data: encoded, + }, + }, nil +} + +func (l *LanguageService) ProvideSemanticTokensRange(ctx context.Context, documentURI lsproto.DocumentUri, rng lsproto.Range) (lsproto.SemanticTokensRangeResponse, error) { + program, file := l.getProgramAndFile(documentURI) + + c, done := program.GetTypeCheckerForFile(ctx, file) + defer done() + + start := int(l.converters.LineAndCharacterToPosition(file, rng.Start)) + end := int(l.converters.LineAndCharacterToPosition(file, rng.End)) + + tokens := l.collectSemanticTokensInRange(ctx, c, file, program, start, end) + + if len(tokens) == 0 { + return lsproto.SemanticTokensOrNull{}, nil + } + + // Convert to LSP format (relative encoding) + encoded := encodeSemanticTokens(ctx, tokens, file, l.converters) + + return lsproto.SemanticTokensOrNull{ + SemanticTokens: &lsproto.SemanticTokens{ + Data: encoded, + }, + }, nil +} + +type semanticToken struct { + node *ast.Node + tokenType tokenType + tokenModifier tokenModifier +} + +func (l *LanguageService) collectSemanticTokens(ctx context.Context, c *checker.Checker, file *ast.SourceFile, program *compiler.Program) []semanticToken { + return l.collectSemanticTokensInRange(ctx, c, file, program, file.Pos(), file.End()) +} + +func (l *LanguageService) collectSemanticTokensInRange(ctx context.Context, c *checker.Checker, file *ast.SourceFile, program *compiler.Program, spanStart, spanEnd int) []semanticToken { + tokens := []semanticToken{} + + inJSXElement := false + + var visit func(*ast.Node) bool + visit = func(node *ast.Node) bool { + // Check for cancellation + if ctx.Err() != nil { + return false + } + + if node == nil { + return false + } + if node.Flags&ast.NodeFlagsReparsed != 0 { + return false + } + nodeEnd := node.End() + if node.Pos() >= spanEnd || nodeEnd <= spanStart { + return false + } + + prevInJSXElement := inJSXElement + if ast.IsJsxElement(node) || ast.IsJsxSelfClosingElement(node) { + inJSXElement = true + } else if ast.IsJsxExpression(node) { + inJSXElement = false + } + + if ast.IsIdentifier(node) && node.Text() != "" && !inJSXElement && !isInImportClause(node) && !isInfinityOrNaNString(node.Text()) { + symbol := c.GetSymbolAtLocation(node) + if symbol != nil { + // Resolve aliases + if symbol.Flags&ast.SymbolFlagsAlias != 0 { + symbol = c.GetAliasedSymbol(symbol) + } + + tokenType, ok := classifySymbol(symbol, getMeaningFromLocation(node)) + if ok { + tokenModifier := tokenModifier(0) + + // Check if this is a declaration + parent := node.Parent + if parent != nil { + parentIsDeclaration := ast.IsBindingElement(parent) || tokenFromDeclarationMapping(parent.Kind) == tokenType + if parentIsDeclaration && parent.Name() == node { + tokenModifier |= tokenModifierDeclaration + } + } + + // Property declaration in constructor: reclassify parameters as properties in property access context + if tokenType == tokenTypeParameter && ast.IsRightSideOfQualifiedNameOrPropertyAccess(node) { + tokenType = tokenTypeProperty + } + + // Type-based reclassification + tokenType = reclassifyByType(c, node, tokenType) + + // Get the value declaration to check modifiers + if decl := symbol.ValueDeclaration; decl != nil { + modifiers := ast.GetCombinedModifierFlags(decl) + nodeFlags := ast.GetCombinedNodeFlags(decl) + + if modifiers&ast.ModifierFlagsStatic != 0 { + tokenModifier |= tokenModifierStatic + } + if modifiers&ast.ModifierFlagsAsync != 0 { + tokenModifier |= tokenModifierAsync + } + if tokenType != tokenTypeClass && tokenType != tokenTypeInterface { + if (modifiers&ast.ModifierFlagsReadonly != 0) || (nodeFlags&ast.NodeFlagsConst != 0) || (symbol.Flags&ast.SymbolFlagsEnumMember != 0) { + tokenModifier |= tokenModifierReadonly + } + } + if (tokenType == tokenTypeVariable || tokenType == tokenTypeFunction) && isLocalDeclaration(decl, file) { + tokenModifier |= tokenModifierLocal + } + declSourceFile := ast.GetSourceFileOfNode(decl) + if declSourceFile != nil && program.IsSourceFileDefaultLibrary(tspath.Path(declSourceFile.FileName())) { + tokenModifier |= tokenModifierDefaultLibrary + } + } else if symbol.Declarations != nil { + for _, decl := range symbol.Declarations { + declSourceFile := ast.GetSourceFileOfNode(decl) + if declSourceFile != nil && program.IsSourceFileDefaultLibrary(tspath.Path(declSourceFile.FileName())) { + tokenModifier |= tokenModifierDefaultLibrary + break + } + } + } + + tokens = append(tokens, semanticToken{ + node: node, + tokenType: tokenType, + tokenModifier: tokenModifier, + }) + } + } + } + + node.ForEachChild(visit) + inJSXElement = prevInJSXElement + return false + } + + visit(file.AsNode()) + + // Check for cancellation after collection + if ctx.Err() != nil { + return nil + } + + return tokens +} + +func classifySymbol(symbol *ast.Symbol, meaning ast.SemanticMeaning) (tokenType, bool) { + flags := symbol.Flags + if flags&ast.SymbolFlagsClass != 0 { + return tokenTypeClass, true + } + if flags&ast.SymbolFlagsEnum != 0 { + return tokenTypeEnum, true + } + if flags&ast.SymbolFlagsTypeAlias != 0 { + return tokenTypeType, true + } + if flags&ast.SymbolFlagsInterface != 0 { + if meaning&ast.SemanticMeaningType != 0 { + return tokenTypeInterface, true + } + } + if flags&ast.SymbolFlagsTypeParameter != 0 { + return tokenTypeTypeParameter, true + } + + // Check the value declaration + decl := symbol.ValueDeclaration + if decl == nil && len(symbol.Declarations) > 0 { + decl = symbol.Declarations[0] + } + if decl != nil { + if ast.IsBindingElement(decl) { + decl = getDeclarationForBindingElement(decl) + } + if tokenType := tokenFromDeclarationMapping(decl.Kind); tokenType >= 0 { + return tokenType, true + } + } + + return 0, false +} + +func tokenFromDeclarationMapping(kind ast.Kind) tokenType { + switch kind { + case ast.KindVariableDeclaration: + return tokenTypeVariable + case ast.KindParameter: + return tokenTypeParameter + case ast.KindPropertyDeclaration: + return tokenTypeProperty + case ast.KindModuleDeclaration: + return tokenTypeNamespace + case ast.KindEnumDeclaration: + return tokenTypeEnum + case ast.KindEnumMember: + return tokenTypeEnumMember + case ast.KindClassDeclaration, ast.KindClassExpression: + return tokenTypeClass + case ast.KindMethodDeclaration: + return tokenTypeMethod + case ast.KindFunctionDeclaration, ast.KindFunctionExpression: + return tokenTypeFunction + case ast.KindMethodSignature: + return tokenTypeMethod + case ast.KindGetAccessor, ast.KindSetAccessor: + return tokenTypeProperty + case ast.KindPropertySignature: + return tokenTypeProperty + case ast.KindInterfaceDeclaration: + return tokenTypeInterface + case ast.KindTypeAliasDeclaration: + return tokenTypeType + case ast.KindTypeParameter: + return tokenTypeTypeParameter + case ast.KindPropertyAssignment, ast.KindShorthandPropertyAssignment: + return tokenTypeProperty + default: + return -1 + } +} + +func reclassifyByType(c *checker.Checker, node *ast.Node, tt tokenType) tokenType { + // Type-based reclassification for variables, properties, and parameters + if tt == tokenTypeVariable || tt == tokenTypeProperty || tt == tokenTypeParameter { + typ := c.GetTypeAtLocation(node) + if typ != nil { + test := func(condition func(*checker.Type) bool) bool { + if condition(typ) { + return true + } + if typ.Flags()&checker.TypeFlagsUnion != 0 { + if slices.ContainsFunc(typ.AsUnionType().Types(), condition) { + return true + } + } + return false + } + + // Check for constructor signatures (class-like) + if tt != tokenTypeParameter && test(func(t *checker.Type) bool { + return len(c.GetSignaturesOfType(t, checker.SignatureKindConstruct)) > 0 + }) { + return tokenTypeClass + } + + // Check for call signatures (function-like) + // Must have call signatures AND (no properties OR be used in call context) + hasCallSignatures := test(func(t *checker.Type) bool { + return len(c.GetSignaturesOfType(t, checker.SignatureKindCall)) > 0 + }) + if hasCallSignatures { + hasNoProperties := !test(func(t *checker.Type) bool { + objType := t.AsObjectType() + return objType != nil && len(objType.Properties()) > 0 + }) + if hasNoProperties || isExpressionInCallExpression(node) { + if tt == tokenTypeProperty { + return tokenTypeMethod + } + return tokenTypeFunction + } + } + } + } + return tt +} + +func isLocalDeclaration(decl *ast.Node, sourceFile *ast.SourceFile) bool { + if ast.IsBindingElement(decl) { + decl = getDeclarationForBindingElement(decl) + } + if ast.IsVariableDeclaration(decl) { + parent := decl.Parent + // Check if this is a catch clause parameter + if parent != nil && ast.IsCatchClause(parent) { + return ast.GetSourceFileOfNode(decl) == sourceFile + } + if parent != nil && ast.IsVariableDeclarationList(parent) { + grandparent := parent.Parent + if grandparent != nil { + greatGrandparent := grandparent.Parent + return (!ast.IsSourceFile(greatGrandparent) || ast.IsCatchClause(grandparent)) && + ast.GetSourceFileOfNode(decl) == sourceFile + } + } + } else if ast.IsFunctionDeclaration(decl) { + parent := decl.Parent + return parent != nil && !ast.IsSourceFile(parent) && ast.GetSourceFileOfNode(decl) == sourceFile + } + return false +} + +func getDeclarationForBindingElement(element *ast.Node) *ast.Node { + for { + parent := element.Parent + if parent != nil && ast.IsBindingPattern(parent) { + grandparent := parent.Parent + if grandparent != nil && ast.IsBindingElement(grandparent) { + element = grandparent + continue + } + return parent.Parent + } + return element + } +} + +func isInImportClause(node *ast.Node) bool { + parent := node.Parent + return parent != nil && (ast.IsImportClause(parent) || ast.IsImportSpecifier(parent) || ast.IsNamespaceImport(parent)) +} + +func isExpressionInCallExpression(node *ast.Node) bool { + for ast.IsRightSideOfQualifiedNameOrPropertyAccess(node) { + node = node.Parent + } + parent := node.Parent + return parent != nil && ast.IsCallExpression(parent) && parent.Expression() == node +} + +func isInfinityOrNaNString(text string) bool { + return text == "Infinity" || text == "NaN" +} + +// encodeSemanticTokens encodes tokens into the LSP format using relative positioning. +// It filters tokens based on client capabilities, only including types and modifiers that the client supports. +func encodeSemanticTokens(ctx context.Context, tokens []semanticToken, file *ast.SourceFile, converters *lsconv.Converters) []uint32 { + // Build mapping from server token types/modifiers to client indices + typeMapping := make(map[tokenType]uint32) + modifierMapping := make(map[lsproto.SemanticTokenModifier]uint32) + + clientCapabilities := lsproto.GetClientCapabilities(ctx).TextDocument.SemanticTokens + + // Map server token types to client-supported indices + clientIdx := uint32(0) + for i, serverType := range tokenTypes { + if slices.Contains(clientCapabilities.TokenTypes, string(serverType)) { + typeMapping[tokenType(i)] = clientIdx + clientIdx++ + } + } + + // Map server token modifiers to client-supported bit positions + clientBit := uint32(0) + for _, serverModifier := range tokenModifiers { + if slices.Contains(clientCapabilities.TokenModifiers, string(serverModifier)) { + modifierMapping[serverModifier] = clientBit + clientBit++ + } + } + + // Each token encodes 5 uint32 values: deltaLine, deltaChar, length, tokenType, tokenModifiers + encoded := make([]uint32, 0, len(tokens)*5) + prevLine := uint32(0) + prevChar := uint32(0) + + for _, token := range tokens { + // Skip tokens with types not supported by the client + clientTypeIdx, typeSupported := typeMapping[token.tokenType] + if !typeSupported { + continue + } + + // Map modifiers to client-supported bit mask + clientModifierMask := uint32(0) + for i, serverModifier := range tokenModifiers { + if token.tokenModifier&(1< 0 && (line < prevLine || (line == prevLine && char <= prevChar)) { + panic(fmt.Sprintf("semantic tokens: positions must be strictly increasing: prev=(%d,%d) current=(%d,%d) for token at offset %d", + prevLine, prevChar, line, char, tokenStart)) + } + + // Encode as: [deltaLine, deltaChar, length, tokenType, tokenModifiers] + deltaLine := line - prevLine + var deltaChar uint32 + if deltaLine == 0 { + deltaChar = char - prevChar + } else { + deltaChar = char + } + + encoded = append(encoded, + deltaLine, + deltaChar, + tokenLength, + clientTypeIdx, + clientModifierMask, + ) + + prevLine = line + prevChar = char + } + + return encoded +} diff --git a/internal/lsp/server.go b/internal/lsp/server.go index 7593d6adf28..618aa830370 100644 --- a/internal/lsp/server.go +++ b/internal/lsp/server.go @@ -725,6 +725,8 @@ var handlers = sync.OnceValue(func() handlerMap { registerRequestHandler(handlers, lsproto.WorkspaceSymbolInfo, (*Server).handleWorkspaceSymbol) registerRequestHandler(handlers, lsproto.CompletionItemResolveInfo, (*Server).handleCompletionItemResolve) registerRequestHandler(handlers, lsproto.CodeLensResolveInfo, (*Server).handleCodeLensResolve) + registerLanguageServiceDocumentRequestHandler(handlers, lsproto.TextDocumentSemanticTokensFullInfo, (*Server).handleSemanticTokensFull) + registerLanguageServiceDocumentRequestHandler(handlers, lsproto.TextDocumentSemanticTokensRangeInfo, (*Server).handleSemanticTokensRange) // Developer/debugging commands registerRequestHandler(handlers, lsproto.CustomRunGCInfo, (*Server).handleRunGC) @@ -1080,6 +1082,17 @@ func (s *Server) handleInitialize(ctx context.Context, params *lsproto.Initializ Boolean: new(true), }, CustomSourceDefinitionProvider: new(true), + SemanticTokensProvider: &lsproto.SemanticTokensOptionsOrRegistrationOptions{ + Options: &lsproto.SemanticTokensOptions{ + Legend: ls.SemanticTokensLegend(s.clientCapabilities.TextDocument.SemanticTokens), + Full: &lsproto.BooleanOrSemanticTokensFullDelta{ + Boolean: new(true), + }, + Range: &lsproto.BooleanOrEmptyObject{ + Boolean: new(true), + }, + }, + }, }, } @@ -1442,6 +1455,14 @@ func (s *Server) handleCallHierarchyOutgoingCalls( return languageService.ProvideCallHierarchyOutgoingCalls(ctx, params.Item) } +func (s *Server) handleSemanticTokensFull(ctx context.Context, ls *ls.LanguageService, params *lsproto.SemanticTokensParams) (lsproto.SemanticTokensResponse, error) { + return ls.ProvideSemanticTokens(ctx, params.TextDocument.Uri) +} + +func (s *Server) handleSemanticTokensRange(ctx context.Context, ls *ls.LanguageService, params *lsproto.SemanticTokensRangeParams) (lsproto.SemanticTokensRangeResponse, error) { + return ls.ProvideSemanticTokensRange(ctx, params.TextDocument.Uri, params.Range) +} + func (s *Server) handleInitializeAPISession(ctx context.Context, params *lsproto.InitializeAPISessionParams, _ *lsproto.RequestMessage) (lsproto.CustomInitializeAPISessionResponse, error) { s.apiSessionsMu.Lock() defer s.apiSessionsMu.Unlock()