Skip to content

Commit e93748a

Browse files
committed
Support find references on the new import/export syntax
1 parent 61e6b32 commit e93748a

3 files changed

Lines changed: 122 additions & 6 deletions

File tree

src/services/services.ts

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3986,7 +3986,7 @@ module ts {
39863986
var searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), declarations);
39873987

39883988
// Get the text to search for, we need to normalize it as external module names will have quote
3989-
var declaredName = getDeclaredName(symbol);
3989+
var declaredName = getDeclaredName(symbol, node);
39903990

39913991
// Try to get the smallest valid scope that we can limit our search to;
39923992
// otherwise we'll need to search globally (i.e. include each file).
@@ -4003,7 +4003,7 @@ module ts {
40034003
getReferencesInNode(sourceFiles[0], symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result);
40044004
}
40054005
else {
4006-
var internedName = getInternedName(symbol, declarations)
4006+
var internedName = getInternedName(symbol, node, declarations)
40074007
forEach(sourceFiles, sourceFile => {
40084008
cancellationToken.throwIfCancellationRequested();
40094009

@@ -4023,13 +4023,51 @@ module ts {
40234023

40244024
return result;
40254025

4026-
function getDeclaredName(symbol: Symbol) {
4026+
function isImportOrExportSpecifierName(location: Node): boolean {
4027+
return location.parent &&
4028+
(location.parent.kind === SyntaxKind.ImportSpecifier || location.parent.kind === SyntaxKind.ExportSpecifier) &&
4029+
(<ImportOrExportSpecifier>location.parent).propertyName === location;
4030+
}
4031+
4032+
function isImportOrExportSpecifierImportSymbol(symbol: Symbol) {
4033+
return (symbol.flags & SymbolFlags.Import) && forEach(symbol.declarations, declaration => {
4034+
return declaration.kind === SyntaxKind.ImportSpecifier || declaration.kind === SyntaxKind.ExportSpecifier;
4035+
});
4036+
}
4037+
4038+
function getDeclaredName(symbol: Symbol, location: Node) {
4039+
// Special case for function expressions, whose names are solely local to their bodies.
4040+
var functionExpression = forEach(symbol.declarations, d => d.kind === SyntaxKind.FunctionExpression ? <FunctionExpression>d : undefined);
4041+
4042+
// When a name gets interned into a SourceFile's 'identifiers' Map,
4043+
// its name is escaped and stored in the same way its symbol name/identifier
4044+
// name should be stored. Function expressions, however, are a special case,
4045+
// because despite sometimes having a name, the binder unconditionally binds them
4046+
// to a symbol with the name "__function".
4047+
if (functionExpression && functionExpression.name) {
4048+
var name = functionExpression.name.text;
4049+
}
4050+
4051+
// If this is an export or import specifier it could have been renamed using the as syntax.
4052+
// if so we want to search for whatever under the cursor, the symbol is pointing to the alias (name)
4053+
// so check for the propertyName.
4054+
if (isImportOrExportSpecifierName(location)) {
4055+
return location.getText();
4056+
}
4057+
40274058
var name = typeInfoResolver.symbolToString(symbol);
40284059

40294060
return stripQuotes(name);
40304061
}
40314062

4032-
function getInternedName(symbol: Symbol, declarations: Declaration[]): string {
4063+
function getInternedName(symbol: Symbol, location: Node, declarations: Declaration[]): string {
4064+
// If this is an export or import specifier it could have been renamed using the as syntax.
4065+
// if so we want to search for whatever under the cursor, the symbol is pointing to the alias (name)
4066+
// so check for the propertyName.
4067+
if (isImportOrExportSpecifierName(location)) {
4068+
return location.getText();
4069+
}
4070+
40334071
// Special case for function expressions, whose names are solely local to their bodies.
40344072
var functionExpression = forEach(declarations, d => d.kind === SyntaxKind.FunctionExpression ? <FunctionExpression>d : undefined);
40354073

@@ -4058,16 +4096,22 @@ module ts {
40584096

40594097
function getSymbolScope(symbol: Symbol): Node {
40604098
// If this is private property or method, the scope is the containing class
4061-
if (symbol.getFlags() && (SymbolFlags.Property | SymbolFlags.Method)) {
4099+
if (symbol.flags & (SymbolFlags.Property | SymbolFlags.Method)) {
40624100
var privateDeclaration = forEach(symbol.getDeclarations(), d => (d.flags & NodeFlags.Private) ? d : undefined);
40634101
if (privateDeclaration) {
40644102
return getAncestor(privateDeclaration, SyntaxKind.ClassDeclaration);
40654103
}
40664104
}
40674105

4106+
// If the symbol is an import we would like to find it if we are looking for what it imports.
4107+
// So consider it visibile outside its declaration scope.
4108+
if (symbol.flags & SymbolFlags.Import) {
4109+
return undefined;
4110+
}
4111+
40684112
// if this symbol is visible from its parent container, e.g. exported, then bail out
40694113
// if symbol correspond to the union property - bail out
4070-
if (symbol.parent || (symbol.getFlags() & SymbolFlags.UnionProperty)) {
4114+
if (symbol.parent || (symbol.flags & SymbolFlags.UnionProperty)) {
40714115
return undefined;
40724116
}
40734117

@@ -4422,6 +4466,11 @@ module ts {
44224466
// The search set contains at least the current symbol
44234467
var result = [symbol];
44244468

4469+
// If the symbol is an alias, add what it alaises to the list
4470+
if (isImportOrExportSpecifierImportSymbol(symbol)) {
4471+
result.push(typeInfoResolver.getAliasedSymbol(symbol));
4472+
}
4473+
44254474
// If the location is in a context sensitive location (i.e. in an object literal) try
44264475
// to get a contextual type for it, and add the property symbol from the contextual
44274476
// type to the search set
@@ -4498,6 +4547,13 @@ module ts {
44984547
return true;
44994548
}
45004549

4550+
// If the reference symbol is an alias, check if what it is aliasing is one of the search
4551+
// symbols.
4552+
if (isImportOrExportSpecifierImportSymbol(referenceSymbol) &&
4553+
searchSymbols.indexOf(typeInfoResolver.getAliasedSymbol(referenceSymbol)) >= 0) {
4554+
return true;
4555+
}
4556+
45014557
// If the reference location is in an object literal, try to get the contextual type for the
45024558
// object literal, lookup the property symbol in the contextual type, and use this symbol to
45034559
// compare to our searchSymbol
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
//@Filename: a.ts
4+
////export class /*1*/Class{
5+
////}
6+
7+
//@Filename: b.ts
8+
////import { /*2*/Class } from "a";
9+
////
10+
////var c = new /*3*/Class();
11+
12+
//@Filename: c.ts
13+
////export { /*4*/Class } from "a";
14+
15+
goTo.file("a.ts");
16+
goTo.marker("1");
17+
verify.referencesCountIs(4);
18+
19+
goTo.file("b.ts");
20+
goTo.marker("2");
21+
verify.referencesCountIs(4);
22+
23+
goTo.marker("3");
24+
verify.referencesCountIs(4);
25+
26+
goTo.file("c.ts");
27+
goTo.marker("4");
28+
verify.referencesCountIs(4);
29+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
//@Filename: a.ts
4+
////export class /*1*/Class{
5+
////}
6+
7+
//@Filename: b.ts
8+
////import { /*2*/Class as /*3*/C2} from "a";
9+
////
10+
////var c = new C2();
11+
12+
//@Filename: c.ts
13+
////export { /*4*/Class as /*5*/C3 } from "a";
14+
15+
goTo.file("a.ts");
16+
goTo.marker("1");
17+
verify.referencesCountIs(3);
18+
19+
goTo.file("b.ts");
20+
goTo.marker("2");
21+
verify.referencesCountIs(3);
22+
23+
goTo.marker("3");
24+
verify.referencesCountIs(2);
25+
26+
goTo.file("c.ts");
27+
goTo.marker("4");
28+
verify.referencesCountIs(3);
29+
30+
goTo.marker("5");
31+
verify.referencesCountIs(1);

0 commit comments

Comments
 (0)