Support find references on the new import/export syntax

This commit is contained in:
Mohamed Hegazy
2015-02-24 01:19:48 -08:00
parent 61e6b3258d
commit e93748ac58
3 changed files with 122 additions and 6 deletions

View File

@@ -3986,7 +3986,7 @@ module ts {
var searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), declarations);
// Get the text to search for, we need to normalize it as external module names will have quote
var declaredName = getDeclaredName(symbol);
var declaredName = getDeclaredName(symbol, node);
// Try to get the smallest valid scope that we can limit our search to;
// otherwise we'll need to search globally (i.e. include each file).
@@ -4003,7 +4003,7 @@ module ts {
getReferencesInNode(sourceFiles[0], symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result);
}
else {
var internedName = getInternedName(symbol, declarations)
var internedName = getInternedName(symbol, node, declarations)
forEach(sourceFiles, sourceFile => {
cancellationToken.throwIfCancellationRequested();
@@ -4023,13 +4023,51 @@ module ts {
return result;
function getDeclaredName(symbol: Symbol) {
function isImportOrExportSpecifierName(location: Node): boolean {
return location.parent &&
(location.parent.kind === SyntaxKind.ImportSpecifier || location.parent.kind === SyntaxKind.ExportSpecifier) &&
(<ImportOrExportSpecifier>location.parent).propertyName === location;
}
function isImportOrExportSpecifierImportSymbol(symbol: Symbol) {
return (symbol.flags & SymbolFlags.Import) && forEach(symbol.declarations, declaration => {
return declaration.kind === SyntaxKind.ImportSpecifier || declaration.kind === SyntaxKind.ExportSpecifier;
});
}
function getDeclaredName(symbol: Symbol, location: Node) {
// Special case for function expressions, whose names are solely local to their bodies.
var functionExpression = forEach(symbol.declarations, d => d.kind === SyntaxKind.FunctionExpression ? <FunctionExpression>d : undefined);
// When a name gets interned into a SourceFile's 'identifiers' Map,
// its name is escaped and stored in the same way its symbol name/identifier
// name should be stored. Function expressions, however, are a special case,
// because despite sometimes having a name, the binder unconditionally binds them
// to a symbol with the name "__function".
if (functionExpression && functionExpression.name) {
var name = functionExpression.name.text;
}
// If this is an export or import specifier it could have been renamed using the as syntax.
// if so we want to search for whatever under the cursor, the symbol is pointing to the alias (name)
// so check for the propertyName.
if (isImportOrExportSpecifierName(location)) {
return location.getText();
}
var name = typeInfoResolver.symbolToString(symbol);
return stripQuotes(name);
}
function getInternedName(symbol: Symbol, declarations: Declaration[]): string {
function getInternedName(symbol: Symbol, location: Node, declarations: Declaration[]): string {
// If this is an export or import specifier it could have been renamed using the as syntax.
// if so we want to search for whatever under the cursor, the symbol is pointing to the alias (name)
// so check for the propertyName.
if (isImportOrExportSpecifierName(location)) {
return location.getText();
}
// Special case for function expressions, whose names are solely local to their bodies.
var functionExpression = forEach(declarations, d => d.kind === SyntaxKind.FunctionExpression ? <FunctionExpression>d : undefined);
@@ -4058,16 +4096,22 @@ module ts {
function getSymbolScope(symbol: Symbol): Node {
// If this is private property or method, the scope is the containing class
if (symbol.getFlags() && (SymbolFlags.Property | SymbolFlags.Method)) {
if (symbol.flags & (SymbolFlags.Property | SymbolFlags.Method)) {
var privateDeclaration = forEach(symbol.getDeclarations(), d => (d.flags & NodeFlags.Private) ? d : undefined);
if (privateDeclaration) {
return getAncestor(privateDeclaration, SyntaxKind.ClassDeclaration);
}
}
// If the symbol is an import we would like to find it if we are looking for what it imports.
// So consider it visibile outside its declaration scope.
if (symbol.flags & SymbolFlags.Import) {
return undefined;
}
// if this symbol is visible from its parent container, e.g. exported, then bail out
// if symbol correspond to the union property - bail out
if (symbol.parent || (symbol.getFlags() & SymbolFlags.UnionProperty)) {
if (symbol.parent || (symbol.flags & SymbolFlags.UnionProperty)) {
return undefined;
}
@@ -4422,6 +4466,11 @@ module ts {
// The search set contains at least the current symbol
var result = [symbol];
// If the symbol is an alias, add what it alaises to the list
if (isImportOrExportSpecifierImportSymbol(symbol)) {
result.push(typeInfoResolver.getAliasedSymbol(symbol));
}
// If the location is in a context sensitive location (i.e. in an object literal) try
// to get a contextual type for it, and add the property symbol from the contextual
// type to the search set
@@ -4498,6 +4547,13 @@ module ts {
return true;
}
// If the reference symbol is an alias, check if what it is aliasing is one of the search
// symbols.
if (isImportOrExportSpecifierImportSymbol(referenceSymbol) &&
searchSymbols.indexOf(typeInfoResolver.getAliasedSymbol(referenceSymbol)) >= 0) {
return true;
}
// If the reference location is in an object literal, try to get the contextual type for the
// object literal, lookup the property symbol in the contextual type, and use this symbol to
// compare to our searchSymbol

View File

@@ -0,0 +1,29 @@
/// <reference path="fourslash.ts" />
//@Filename: a.ts
////export class /*1*/Class{
////}
//@Filename: b.ts
////import { /*2*/Class } from "a";
////
////var c = new /*3*/Class();
//@Filename: c.ts
////export { /*4*/Class } from "a";
goTo.file("a.ts");
goTo.marker("1");
verify.referencesCountIs(4);
goTo.file("b.ts");
goTo.marker("2");
verify.referencesCountIs(4);
goTo.marker("3");
verify.referencesCountIs(4);
goTo.file("c.ts");
goTo.marker("4");
verify.referencesCountIs(4);

View File

@@ -0,0 +1,31 @@
/// <reference path="fourslash.ts" />
//@Filename: a.ts
////export class /*1*/Class{
////}
//@Filename: b.ts
////import { /*2*/Class as /*3*/C2} from "a";
////
////var c = new C2();
//@Filename: c.ts
////export { /*4*/Class as /*5*/C3 } from "a";
goTo.file("a.ts");
goTo.marker("1");
verify.referencesCountIs(3);
goTo.file("b.ts");
goTo.marker("2");
verify.referencesCountIs(3);
goTo.marker("3");
verify.referencesCountIs(2);
goTo.file("c.ts");
goTo.marker("4");
verify.referencesCountIs(3);
goTo.marker("5");
verify.referencesCountIs(1);