Skip to content

Commit fd86f40

Browse files
committed
Include declarationSpan as relevant declaration span when defintion or other places are declaration name
Fixes #30849
1 parent a2b4029 commit fd86f40

File tree

13 files changed

+658
-237
lines changed

13 files changed

+658
-237
lines changed

src/server/protocol.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -872,16 +872,24 @@ namespace ts.server.protocol {
872872
file: string;
873873
}
874874

875+
export interface DeclarationTextSpan extends TextSpan {
876+
declarationStart?: Location;
877+
declarationEnd?: Location;
878+
}
879+
880+
export interface DeclarationFileSpan extends FileSpan, DeclarationTextSpan {
881+
}
882+
875883
export interface DefinitionInfoAndBoundSpan {
876-
definitions: ReadonlyArray<FileSpan>;
884+
definitions: ReadonlyArray<DeclarationFileSpan>;
877885
textSpan: TextSpan;
878886
}
879887

880888
/**
881889
* Definition response message. Gives text range for definition.
882890
*/
883891
export interface DefinitionResponse extends Response {
884-
body?: FileSpan[];
892+
body?: DeclarationFileSpan[];
885893
}
886894

887895
export interface DefinitionInfoAndBoundSpanReponse extends Response {
@@ -892,14 +900,14 @@ namespace ts.server.protocol {
892900
* Definition response message. Gives text range for definition.
893901
*/
894902
export interface TypeDefinitionResponse extends Response {
895-
body?: FileSpan[];
903+
body?: DeclarationFileSpan[];
896904
}
897905

898906
/**
899907
* Implementation response message. Gives text range for implementations.
900908
*/
901909
export interface ImplementationResponse extends Response {
902-
body?: FileSpan[];
910+
body?: DeclarationFileSpan[];
903911
}
904912

905913
/**
@@ -942,7 +950,7 @@ namespace ts.server.protocol {
942950
}
943951

944952
/** @deprecated */
945-
export interface OccurrencesResponseItem extends FileSpan {
953+
export interface OccurrencesResponseItem extends DeclarationFileSpan {
946954
/**
947955
* True if the occurrence is a write location, false otherwise.
948956
*/
@@ -972,7 +980,7 @@ namespace ts.server.protocol {
972980
/**
973981
* Span augmented with extra information that denotes the kind of the highlighting to be used for span.
974982
*/
975-
export interface HighlightSpan extends TextSpan {
983+
export interface HighlightSpan extends DeclarationTextSpan {
976984
kind: HighlightSpanKind;
977985
}
978986

@@ -1007,7 +1015,7 @@ namespace ts.server.protocol {
10071015
command: CommandTypes.References;
10081016
}
10091017

1010-
export interface ReferencesResponseItem extends FileSpan {
1018+
export interface ReferencesResponseItem extends DeclarationFileSpan {
10111019
/** Text of line containing the reference. Including this
10121020
* with the response avoids latency of editor loading files
10131021
* to show text of reference line (the server already has
@@ -1150,7 +1158,7 @@ namespace ts.server.protocol {
11501158
locs: RenameTextSpan[];
11511159
}
11521160

1153-
export interface RenameTextSpan extends TextSpan {
1161+
export interface RenameTextSpan extends DeclarationTextSpan {
11541162
readonly prefixText?: string;
11551163
readonly suffixText?: string;
11561164
}

src/server/session.ts

Lines changed: 131 additions & 106 deletions
Large diffs are not rendered by default.

src/services/findAllReferences.ts

Lines changed: 95 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,55 @@ namespace ts.FindAllReferences {
1919
export interface NodeEntry {
2020
readonly kind: NodeEntryKind;
2121
readonly node: Node;
22+
readonly declaration?: Node;
2223
}
2324
export interface SpanEntry {
2425
readonly kind: EntryKind.Span;
2526
readonly fileName: string;
2627
readonly textSpan: TextSpan;
2728
}
2829
export function nodeEntry(node: Node, kind: NodeEntryKind = EntryKind.Node): NodeEntry {
29-
return { kind, node: (node as NamedDeclaration).name || node };
30+
const declaration = getDeclarationForDeclarationSpan(
31+
isDeclaration(node) ?
32+
node :
33+
node.parent && isDeclaration(node.parent) && node.parent.name === node ?
34+
node.parent :
35+
undefined
36+
);
37+
return { kind, node: (node as NamedDeclaration).name || node, declaration };
38+
}
39+
40+
export function getDeclarationForDeclarationSpan(node: NamedDeclaration | undefined): Node | undefined {
41+
if (!node) return undefined;
42+
switch (node.kind) {
43+
case SyntaxKind.VariableDeclaration:
44+
return !isVariableDeclarationList(node.parent) || node.parent.declarations.length !== 1 ?
45+
node :
46+
isVariableStatement(node.parent.parent) ?
47+
node.parent.parent :
48+
node.parent;
49+
50+
case SyntaxKind.BindingElement:
51+
return getDeclarationForDeclarationSpan(node.parent.parent as NamedDeclaration);
52+
53+
case SyntaxKind.ImportSpecifier:
54+
return node.parent.parent.parent;
55+
56+
case SyntaxKind.ExportSpecifier:
57+
case SyntaxKind.NamespaceImport:
58+
return node.parent.parent;
59+
60+
case SyntaxKind.ImportClause:
61+
return node.parent;
62+
63+
// Not really interesting definition
64+
// Should we show whole object literal instead?
65+
case SyntaxKind.ShorthandPropertyAssignment:
66+
return undefined;
67+
68+
default:
69+
return node;
70+
}
3071
}
3172

3273
export interface Options {
@@ -123,7 +164,16 @@ namespace ts.FindAllReferences {
123164
const { symbol } = def;
124165
const { displayParts, kind } = getDefinitionKindAndDisplayParts(symbol, checker, originalNode);
125166
const name = displayParts.map(p => p.text).join("");
126-
return { node: symbol.declarations ? getNameOfDeclaration(first(symbol.declarations)) || first(symbol.declarations) : originalNode, name, kind, displayParts };
167+
const declaration = symbol.declarations ? first(symbol.declarations) : undefined;
168+
return {
169+
node: declaration ?
170+
getNameOfDeclaration(declaration) || declaration :
171+
originalNode,
172+
name,
173+
kind,
174+
displayParts,
175+
declaration: getDeclarationForDeclarationSpan(declaration)
176+
};
127177
}
128178
case DefinitionKind.Label: {
129179
const { node } = def;
@@ -150,9 +200,21 @@ namespace ts.FindAllReferences {
150200
}
151201
})();
152202

153-
const { node, name, kind, displayParts } = info;
203+
const { node, name, kind, displayParts, declaration } = info;
154204
const sourceFile = node.getSourceFile();
155-
return { containerKind: ScriptElementKind.unknown, containerName: "", fileName: sourceFile.fileName, kind, name, textSpan: getTextSpan(isComputedPropertyName(node) ? node.expression : node, sourceFile), displayParts };
205+
const result: ReferencedSymbolDefinitionInfo = {
206+
containerKind: ScriptElementKind.unknown,
207+
containerName: "",
208+
fileName: sourceFile.fileName,
209+
kind,
210+
name,
211+
textSpan: getTextSpan(isComputedPropertyName(node) ? node.expression : node, sourceFile),
212+
displayParts
213+
};
214+
if (declaration) {
215+
result.declarationSpan = getTextSpan(declaration, sourceFile);
216+
}
217+
return result;
156218
}
157219

158220
function getDefinitionKindAndDisplayParts(symbol: Symbol, checker: TypeChecker, node: Node): { displayParts: SymbolDisplayPart[], kind: ScriptElementKind } {
@@ -168,14 +230,13 @@ namespace ts.FindAllReferences {
168230
}
169231

170232
export function toReferenceEntry(entry: Entry): ReferenceEntry {
171-
const { textSpan, fileName } = entryToDocumentSpan(entry);
233+
const documentSpan = entryToDocumentSpan(entry);
172234
if (entry.kind === EntryKind.Span) {
173-
return { textSpan, fileName, isWriteAccess: false, isDefinition: false };
235+
return { ...documentSpan, isWriteAccess: false, isDefinition: false };
174236
}
175237
const { kind, node } = entry;
176238
return {
177-
textSpan,
178-
fileName,
239+
...documentSpan,
179240
isWriteAccess: isWriteAccessForReference(node),
180241
isDefinition: node.kind === SyntaxKind.DefaultKeyword
181242
|| !!getDeclarationFromName(node)
@@ -190,7 +251,11 @@ namespace ts.FindAllReferences {
190251
}
191252
else {
192253
const sourceFile = entry.node.getSourceFile();
193-
return { textSpan: getTextSpan(entry.node, sourceFile), fileName: sourceFile.fileName };
254+
const result: DocumentSpan = { textSpan: getTextSpan(entry.node, sourceFile), fileName: sourceFile.fileName };
255+
if (entry.declaration) {
256+
result.declarationSpan = getTextSpan(entry.declaration, sourceFile);
257+
}
258+
return result;
194259
}
195260
}
196261

@@ -223,14 +288,16 @@ namespace ts.FindAllReferences {
223288
}
224289

225290
function toImplementationLocation(entry: Entry, checker: TypeChecker): ImplementationLocation {
291+
const documentSpan = entryToDocumentSpan(entry);
226292
if (entry.kind !== EntryKind.Span) {
227293
const { node } = entry;
228-
const sourceFile = node.getSourceFile();
229-
return { textSpan: getTextSpan(node, sourceFile), fileName: sourceFile.fileName, ...implementationKindDisplayParts(node, checker) };
294+
return {
295+
...documentSpan,
296+
...implementationKindDisplayParts(node, checker)
297+
};
230298
}
231299
else {
232-
const { textSpan, fileName } = entry;
233-
return { textSpan, fileName, kind: ScriptElementKind.unknown, displayParts: [] };
300+
return { ...documentSpan, kind: ScriptElementKind.unknown, displayParts: [] };
234301
}
235302
}
236303

@@ -257,20 +324,27 @@ namespace ts.FindAllReferences {
257324
}
258325

259326
export function toHighlightSpan(entry: Entry): { fileName: string, span: HighlightSpan } {
327+
const documentSpan = entryToDocumentSpan(entry);
260328
if (entry.kind === EntryKind.Span) {
261-
const { fileName, textSpan } = entry;
262-
return { fileName, span: { textSpan, kind: HighlightSpanKind.reference } };
329+
return {
330+
fileName: documentSpan.fileName,
331+
span: {
332+
textSpan: documentSpan.textSpan,
333+
kind: HighlightSpanKind.reference
334+
}
335+
};
263336
}
264337

265-
const { node, kind } = entry;
266-
const sourceFile = node.getSourceFile();
267-
const writeAccess = isWriteAccessForReference(node);
338+
const writeAccess = isWriteAccessForReference(entry.node);
268339
const span: HighlightSpan = {
269-
textSpan: getTextSpan(node, sourceFile),
340+
textSpan: documentSpan.textSpan,
270341
kind: writeAccess ? HighlightSpanKind.writtenReference : HighlightSpanKind.reference,
271-
isInString: kind === EntryKind.StringLiteral ? true : undefined,
342+
isInString: entry.kind === EntryKind.StringLiteral ? true : undefined,
272343
};
273-
return { fileName: sourceFile.fileName, span };
344+
if (documentSpan.declarationSpan) {
345+
span.declarationSpan = documentSpan.declarationSpan;
346+
}
347+
return { fileName: documentSpan.fileName, span };
274348
}
275349

276350
function getTextSpan(node: Node, sourceFile: SourceFile): TextSpan {

src/services/goToDefinition.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,11 @@ namespace ts.GoToDefinition {
279279
kind: symbolKind,
280280
name: symbolName,
281281
containerKind: undefined!, // TODO: GH#18217
282-
containerName
282+
containerName,
283+
declarationSpan: createTextSpanFromNode(
284+
FindAllReferences.getDeclarationForDeclarationSpan(declaration)!,
285+
sourceFile
286+
)
283287
};
284288
}
285289

src/services/services.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,13 +1544,22 @@ namespace ts {
15441544

15451545
/// References and Occurrences
15461546
function getOccurrencesAtPosition(fileName: string, position: number): ReadonlyArray<ReferenceEntry> | undefined {
1547-
return flatMap(getDocumentHighlights(fileName, position, [fileName]), entry => entry.highlightSpans.map<ReferenceEntry>(highlightSpan => ({
1548-
fileName: entry.fileName,
1549-
textSpan: highlightSpan.textSpan,
1550-
isWriteAccess: highlightSpan.kind === HighlightSpanKind.writtenReference,
1551-
isDefinition: false,
1552-
isInString: highlightSpan.isInString,
1553-
})));
1547+
return flatMap(
1548+
getDocumentHighlights(fileName, position, [fileName]),
1549+
entry => entry.highlightSpans.map(highlightSpan => {
1550+
const result: ReferenceEntry = {
1551+
fileName: entry.fileName,
1552+
textSpan: highlightSpan.textSpan,
1553+
isWriteAccess: highlightSpan.kind === HighlightSpanKind.writtenReference,
1554+
isDefinition: false,
1555+
isInString: highlightSpan.isInString,
1556+
};
1557+
if (highlightSpan.declarationSpan) {
1558+
result.declarationSpan = highlightSpan.declarationSpan;
1559+
}
1560+
return result;
1561+
})
1562+
);
15541563
}
15551564

15561565
function getDocumentHighlights(fileName: string, position: number, filesToSearch: ReadonlyArray<string>): DocumentHighlights[] | undefined {

src/services/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,13 @@ namespace ts {
613613
*/
614614
originalTextSpan?: TextSpan;
615615
originalFileName?: string;
616+
617+
/**
618+
* If DocumentSpan.textSpan is the span for name of the declaration,
619+
* then this is the span for relevant declaration
620+
*/
621+
declarationSpan?: TextSpan;
622+
originalDeclarationSpan?: TextSpan;
616623
}
617624

618625
export interface RenameLocation extends DocumentSpan {
@@ -647,6 +654,7 @@ namespace ts {
647654
fileName?: string;
648655
isInString?: true;
649656
textSpan: TextSpan;
657+
declarationSpan?: TextSpan;
650658
kind: HighlightSpanKind;
651659
}
652660

0 commit comments

Comments
 (0)