Skip to content
Merged
14 changes: 11 additions & 3 deletions src/services/findAllReferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ import {
StringLiteralLike,
stripQuotes,
SuperContainer,
SwitchStatement,
Symbol,
SymbolDisplay,
SymbolDisplayPart,
Expand Down Expand Up @@ -406,7 +407,7 @@ function getContextNodeForNodeEntry(node: Node): ContextNode | undefined {
}

/** @internal */
export function getContextNode(node: NamedDeclaration | BinaryExpression | ForInOrOfStatement | undefined): ContextNode | undefined {
export function getContextNode(node: NamedDeclaration | BinaryExpression | ForInOrOfStatement | SwitchStatement | undefined): ContextNode | undefined {
if (!node) return undefined;
switch (node.kind) {
case SyntaxKind.VariableDeclaration:
Expand Down Expand Up @@ -451,14 +452,18 @@ export function getContextNode(node: NamedDeclaration | BinaryExpression | ForIn
findAncestor(node.parent, node => isBinaryExpression(node) || isForInOrOfStatement(node)) as BinaryExpression | ForInOrOfStatement,
) :
node;

case SyntaxKind.SwitchStatement:
return {
start: find(node.getChildren(node.getSourceFile()), node => node.kind === SyntaxKind.SwitchKeyword)!,
end: (node as SwitchStatement).caseBlock,
};
default:
return node;
}
}

/** @internal */
export function toContextSpan(textSpan: TextSpan, sourceFile: SourceFile, context?: ContextNode): { contextSpan: TextSpan; } | undefined {
export function toContextSpan(textSpan: TextSpan, sourceFile: SourceFile, context: ContextNode | undefined): { contextSpan: TextSpan; } | undefined {
if (!context) return undefined;
const contextSpan = isContextWithStartAndEndNode(context) ?
getTextSpan(context.start, sourceFile, context.end) :
Expand Down Expand Up @@ -874,6 +879,9 @@ function getTextSpan(node: Node, sourceFile: SourceFile, endNode?: Node): TextSp
start += 1;
end -= 1;
}
if (endNode?.kind === SyntaxKind.CaseBlock) {
end = endNode.getFullStart();
}
return createTextSpanFromBounds(start, end);
}

Expand Down
47 changes: 44 additions & 3 deletions src/services/goToDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import {
isClassStaticBlockDeclaration,
isConstructorDeclaration,
isDeclarationFileName,
isDefaultClause,
isExternalModuleNameRelative,
isFunctionLike,
isFunctionLikeDeclaration,
Expand All @@ -69,6 +70,7 @@ import {
isPropertyName,
isRightSideOfPropertyAccess,
isStaticModifier,
isSwitchStatement,
isTypeAliasDeclaration,
isTypeReferenceNode,
isVariableDeclaration,
Expand All @@ -91,6 +93,7 @@ import {
skipTrivia,
some,
SourceFile,
SwitchStatement,
Symbol,
SymbolDisplay,
SymbolFlags,
Expand All @@ -105,6 +108,9 @@ import {
TypeReference,
unescapeLeadingUnderscores,
} from "./_namespaces/ts";
import {
isContextWithStartAndEndNode,
} from "./_namespaces/ts.FindAllReferences";

/** @internal */
export function getDefinitionAtPosition(program: Program, sourceFile: SourceFile, position: number, searchOtherFilesOnly?: boolean, stopAtAlias?: boolean): readonly DefinitionInfo[] | undefined {
Expand Down Expand Up @@ -133,9 +139,26 @@ export function getDefinitionAtPosition(program: Program, sourceFile: SourceFile
return label ? [createDefinitionInfoFromName(typeChecker, label, ScriptElementKind.label, node.text, /*containerName*/ undefined!)] : undefined; // TODO: GH#18217
}

if (node.kind === SyntaxKind.ReturnKeyword) {
const functionDeclaration = findAncestor(node.parent, n => isClassStaticBlockDeclaration(n) ? "quit" : isFunctionLikeDeclaration(n)) as FunctionLikeDeclaration | undefined;
return functionDeclaration ? [createDefinitionFromSignatureDeclaration(typeChecker, functionDeclaration)] : undefined;
switch (node.kind) {
case SyntaxKind.ReturnKeyword:
const functionDeclaration = findAncestor(node.parent, n =>
isClassStaticBlockDeclaration(n)
? "quit"
: isFunctionLikeDeclaration(n)) as FunctionLikeDeclaration | undefined;
return functionDeclaration
? [createDefinitionFromSignatureDeclaration(typeChecker, functionDeclaration)]
: undefined;
case SyntaxKind.DefaultKeyword:
if (!isDefaultClause(node.parent)) {
break;
}
// falls through
case SyntaxKind.CaseKeyword:
const switchStatement = findAncestor(node.parent, isSwitchStatement);
if (switchStatement) {
return [createDefinitionInfoFromSwitch(switchStatement, sourceFile)];
}
break;
}

if (node.kind === SyntaxKind.AwaitKeyword) {
Expand Down Expand Up @@ -634,6 +657,24 @@ function createDefinitionInfoFromName(checker: TypeChecker, declaration: Declara
};
}

function createDefinitionInfoFromSwitch(statement: SwitchStatement, sourceFile: SourceFile): DefinitionInfo {
const keyword = FindAllReferences.getContextNode(statement)!;
const textSpan = createTextSpanFromNode(isContextWithStartAndEndNode(keyword) ? keyword.start : keyword, sourceFile);
return {
fileName: sourceFile.fileName,
textSpan,
kind: ScriptElementKind.keyword,
name: "switch",
containerKind: undefined!,
containerName: "",
...FindAllReferences.toContextSpan(textSpan, sourceFile, keyword),
isLocal: true,
isAmbient: false,
unverified: false,
failedAliasResolution: undefined,
};
}

function isDefinitionVisible(checker: TypeChecker, declaration: Declaration): boolean {
if (checker.isDeclarationVisible(declaration)) return true;
if (!declaration.parent) return false;
Expand Down
17 changes: 17 additions & 0 deletions tests/baselines/reference/goToDefinitionSwitchCase1.baseline.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// === goToDefinition ===
// === /tests/cases/fourslash/goToDefinitionSwitchCase1.ts ===
// <|[|switch|] (null )|> {
// /*GOTO DEF*/case null: break;
// }

// === Details ===
[
{
"kind": "keyword",
"name": "switch",
"containerName": "",
"isLocal": true,
"isAmbient": false,
"unverified": false
}
]
17 changes: 17 additions & 0 deletions tests/baselines/reference/goToDefinitionSwitchCase2.baseline.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// === goToDefinition ===
// === /tests/cases/fourslash/goToDefinitionSwitchCase2.ts ===
// <|[|switch|] (null)|> {
// /*GOTO DEF*/default: break;
// }

// === Details ===
[
{
"kind": "keyword",
"name": "switch",
"containerName": "",
"isLocal": true,
"isAmbient": false,
"unverified": false
}
]
45 changes: 45 additions & 0 deletions tests/baselines/reference/goToDefinitionSwitchCase3.baseline.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// === goToDefinition ===
// === /tests/cases/fourslash/goToDefinitionSwitchCase3.ts ===
// <|[|switch|] (null)|> {
// /*GOTO DEF*/default: {
// switch (null) {
// default: break;
// }
// };
// }

// === Details ===
[
{
"kind": "keyword",
"name": "switch",
"containerName": "",
"isLocal": true,
"isAmbient": false,
"unverified": false
}
]



// === goToDefinition ===
// === /tests/cases/fourslash/goToDefinitionSwitchCase3.ts ===
// switch (null) {
// default: {
// <|[|switch|] (null)|> {
// /*GOTO DEF*/default: break;
// }
// };
// }

// === Details ===
[
{
"kind": "keyword",
"name": "switch",
"containerName": "",
"isLocal": true,
"isAmbient": false,
"unverified": false
}
]
21 changes: 21 additions & 0 deletions tests/baselines/reference/goToDefinitionSwitchCase4.baseline.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// === goToDefinition ===
// === /tests/cases/fourslash/goToDefinitionSwitchCase4.ts ===
// switch (null) {
// case null: break;
// }
//
// <|[|switch|] (null)|> {
// /*GOTO DEF*/case null: break;
// }

// === Details ===
[
{
"kind": "keyword",
"name": "switch",
"containerName": "",
"isLocal": true,
"isAmbient": false,
"unverified": false
}
]
16 changes: 16 additions & 0 deletions tests/baselines/reference/goToDefinitionSwitchCase5.baseline.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// === goToDefinition ===
// === /tests/cases/fourslash/goToDefinitionSwitchCase5.ts ===
// [|export /*GOTO DEF*/default {}|]

// === Details ===
[
{
"kind": "property",
"name": "default",
"containerName": "\"/tests/cases/fourslash/goToDefinitionSwitchCase5\"",
"isLocal": true,
"isAmbient": false,
"unverified": false,
"failedAliasResolution": false
}
]
47 changes: 47 additions & 0 deletions tests/baselines/reference/goToDefinitionSwitchCase6.baseline.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// === goToDefinition ===
// === /tests/cases/fourslash/goToDefinitionSwitchCase6.ts ===
// export default { /*GOTO DEF*/[|{| textSpan: true |}case|] };
// default;
// case 42;

// === Details ===
[
{
"kind": "property",
"name": "case",
"containerName": "__object",
"isLocal": true,
"isAmbient": false,
"unverified": false,
"failedAliasResolution": false
}
]



// === goToDefinition ===
// === /tests/cases/fourslash/goToDefinitionSwitchCase6.ts ===
// [|export default { case };
// /*GOTO DEF*/default;
// case 42;|]

// === Details ===
[
{
"kind": "module",
"name": "\"/tests/cases/fourslash/goToDefinitionSwitchCase6\"",
"containerName": "",
"isLocal": false,
"isAmbient": false,
"unverified": false,
"failedAliasResolution": false
}
]



// === goToDefinition ===
// === /tests/cases/fourslash/goToDefinitionSwitchCase6.ts ===
// export default { case };
// default;
// /*GOTO DEF*/case 42;
18 changes: 18 additions & 0 deletions tests/baselines/reference/goToDefinitionSwitchCase7.baseline.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// === goToDefinition ===
// === /tests/cases/fourslash/goToDefinitionSwitchCase7.ts ===
// switch (null) {
// case null:
// [|export /*GOTO DEF*/default 123;|]

// === Details ===
[
{
"kind": "var",
"name": "default",
"containerName": "",
"isLocal": true,
"isAmbient": false,
"unverified": false,
"failedAliasResolution": false
}
]
7 changes: 7 additions & 0 deletions tests/cases/fourslash/goToDefinitionSwitchCase1.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/// <reference path="fourslash.ts" />

////switch (null ) {
//// [|/*start*/case|] null: break;
////}

verify.baselineGoToDefinition("start");
7 changes: 7 additions & 0 deletions tests/cases/fourslash/goToDefinitionSwitchCase2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/// <reference path="fourslash.ts" />

////switch (null) {
//// [|/*start*/default|]: break;
////}

verify.baselineGoToDefinition("start");
11 changes: 11 additions & 0 deletions tests/cases/fourslash/goToDefinitionSwitchCase3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/// <reference path="fourslash.ts" />

////switch (null) {
//// [|/*start1*/default|]: {
//// switch (null) {
//// [|/*start2*/default|]: break;
//// }
//// };
////}

verify.baselineGoToDefinition("start1", "start2");
11 changes: 11 additions & 0 deletions tests/cases/fourslash/goToDefinitionSwitchCase4.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/// <reference path="fourslash.ts" />

//// switch (null) {
//// case null: break;
//// }
////
//// switch (null) {
//// [|/*start*/case|] null: break;
//// }

verify.baselineGoToDefinition("start");
5 changes: 5 additions & 0 deletions tests/cases/fourslash/goToDefinitionSwitchCase5.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// <reference path="fourslash.ts" />

////export [|/*start*/default|] {}

verify.baselineGoToDefinition("start");
7 changes: 7 additions & 0 deletions tests/cases/fourslash/goToDefinitionSwitchCase6.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/// <reference path="fourslash.ts" />

////export default { [|/*a*/case|] };
////[|/*b*/default|];
////[|/*c*/case|] 42;

verify.baselineGoToDefinition("a", "b", "c");
7 changes: 7 additions & 0 deletions tests/cases/fourslash/goToDefinitionSwitchCase7.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/// <reference path="fourslash.ts" />

////switch (null) {
//// case null:
//// export [|/*start*/default|] 123;

verify.baselineGoToDefinition("start");