diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index b52777cb6fa..fd6d3a109d5 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -16,10 +16,15 @@ namespace ts.FindAllReferences { export const enum EntryKind { Span, Node, StringLiteral, SearchedLocalFoundProperty, SearchedPropertyFoundLocal } export type NodeEntryKind = EntryKind.Node | EntryKind.StringLiteral | EntryKind.SearchedLocalFoundProperty | EntryKind.SearchedPropertyFoundLocal; export type Entry = NodeEntry | SpanEntry; + export interface DeclarationNodeWithStartAndEnd { + start: Node; + end: Node; + } + export type DeclarationNode = Node | DeclarationNodeWithStartAndEnd; export interface NodeEntry { readonly kind: NodeEntryKind; readonly node: Node; - readonly declaration?: Node; + readonly declaration?: DeclarationNode; } export interface SpanEntry { readonly kind: EntryKind.Span; @@ -34,7 +39,11 @@ namespace ts.FindAllReferences { }; } - function getDeclarationForDeclarationSpanForNode(node: Node): Node | undefined { + export function isDeclarationNodeWithStartAndEnd(node: DeclarationNode): node is DeclarationNodeWithStartAndEnd { + return node && (node as Node).kind === undefined; + } + + function getDeclarationForDeclarationSpanForNode(node: Node): DeclarationNode | undefined { if (isDeclaration(node)) { return getDeclarationForDeclarationSpan(node); } @@ -72,7 +81,7 @@ namespace ts.FindAllReferences { return undefined; } - export function getDeclarationForDeclarationSpan(node: NamedDeclaration | BinaryExpression | undefined): Node | undefined { + export function getDeclarationForDeclarationSpan(node: NamedDeclaration | BinaryExpression | undefined): DeclarationNode | undefined { if (!node) return undefined; switch (node.kind) { case SyntaxKind.VariableDeclaration: @@ -80,6 +89,8 @@ namespace ts.FindAllReferences { node : isVariableStatement(node.parent.parent) ? node.parent.parent : + isForInOrOfStatement(node.parent.parent) ? + { start: node.parent.parent.initializer, end: node.parent.parent.expression } : node.parent; case SyntaxKind.BindingElement: @@ -257,7 +268,9 @@ namespace ts.FindAllReferences { displayParts }; if (declaration) { - result.declarationSpan = getTextSpan(declaration, sourceFile); + result.declarationSpan = isDeclarationNodeWithStartAndEnd(declaration) ? + getTextSpan(declaration.start, sourceFile, declaration.end) : + getTextSpan(declaration, sourceFile); } return result; } @@ -298,7 +311,9 @@ namespace ts.FindAllReferences { const sourceFile = entry.node.getSourceFile(); const result: DocumentSpan = { textSpan: getTextSpan(entry.node, sourceFile), fileName: sourceFile.fileName }; if (entry.declaration) { - result.declarationSpan = getTextSpan(entry.declaration, sourceFile); + result.declarationSpan = isDeclarationNodeWithStartAndEnd(entry.declaration) ? + getTextSpan(entry.declaration.start, sourceFile, entry.declaration.end) : + getTextSpan(entry.declaration, sourceFile); } return result; } @@ -392,10 +407,11 @@ namespace ts.FindAllReferences { return { fileName: documentSpan.fileName, span }; } - function getTextSpan(node: Node, sourceFile: SourceFile): TextSpan { + function getTextSpan(node: Node, sourceFile: SourceFile, endNode?: Node): TextSpan { let start = node.getStart(sourceFile); - let end = node.getEnd(); + let end = (endNode || node).getEnd(); if (node.kind === SyntaxKind.StringLiteral) { + Debug.assert(endNode === undefined); start += 1; end -= 1; } diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index 7a04184c5d9..f64a2469156 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -273,6 +273,7 @@ namespace ts.GoToDefinition { function createDefinitionInfoFromName(declaration: Declaration, symbolKind: ScriptElementKind, symbolName: string, containerName: string): DefinitionInfo { const name = getNameOfDeclaration(declaration) || declaration; const sourceFile = name.getSourceFile(); + const declarationNode = FindAllReferences.getDeclarationForDeclarationSpan(declaration)!; return { fileName: sourceFile.fileName, textSpan: createTextSpanFromNode(name, sourceFile), @@ -280,10 +281,9 @@ namespace ts.GoToDefinition { name: symbolName, containerKind: undefined!, // TODO: GH#18217 containerName, - declarationSpan: createTextSpanFromNode( - FindAllReferences.getDeclarationForDeclarationSpan(declaration)!, - sourceFile - ) + declarationSpan: FindAllReferences.isDeclarationNodeWithStartAndEnd(declarationNode) ? + createTextSpanFromNode(declarationNode.start, sourceFile, declarationNode.end) : + createTextSpanFromNode(declarationNode, sourceFile), }; } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 5506b45d380..35fca7f0e65 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1190,8 +1190,8 @@ namespace ts { return !!range && shouldBeReference === tripleSlashDirectivePrefixRegex.test(sourceFile.text.substring(range.pos, range.end)); } - export function createTextSpanFromNode(node: Node, sourceFile?: SourceFile): TextSpan { - return createTextSpanFromBounds(node.getStart(sourceFile), node.getEnd()); + export function createTextSpanFromNode(node: Node, sourceFile?: SourceFile, endNode?: Node): TextSpan { + return createTextSpanFromBounds(node.getStart(sourceFile), (endNode || node).getEnd()); } export function createTextRangeFromNode(node: Node, sourceFile: SourceFile): TextRange { diff --git a/tests/cases/fourslash/renameDestructuringNestedBindingElement.ts b/tests/cases/fourslash/renameDestructuringNestedBindingElement.ts index 6643382f6c9..f262f44c6f0 100644 --- a/tests/cases/fourslash/renameDestructuringNestedBindingElement.ts +++ b/tests/cases/fourslash/renameDestructuringNestedBindingElement.ts @@ -3,18 +3,18 @@ ////interface MultiRobot { //// name: string; //// skills: { -//// [|primary|]: string; +//// [|[|{| "declarationRangeIndex": 0|}primary|]: string;|] //// secondary: string; //// }; ////} ////let multiRobots: MultiRobot[]; -////for (let { skills: {[|primary|]: primaryA, secondary: secondaryA } } of multiRobots) { +////for ([|let { skills: {[|{| "declarationRangeIndex": 2|}primary|]: primaryA, secondary: secondaryA } } of multiRobots|]) { //// console.log(primaryA); ////} -////for (let { skills: {[|primary|], secondary } } of multiRobots) { +////for ([|let { skills: {[|{| "declarationRangeIndex": 4|}primary|], secondary } } of multiRobots|]) { //// console.log([|primary|]); ////} -const [r0, r1, r2, r3] = test.ranges(); +const [r0Def, r0, r1Def, r1, r2Def, r2, r3] = test.ranges(); verify.renameLocations([r0, r1], [r0, r1, { range: r2, suffixText: ": primary" }]); verify.renameLocations([r2, r3], [{ range: r2, prefixText: "primary: " }, r3]);