Merge pull request #2126 from Microsoft/findAllRefsForImports

Find all refs for imports
This commit is contained in:
Mohamed Hegazy 2015-02-24 14:38:12 -08:00
commit eefbae6613
17 changed files with 412 additions and 14 deletions

View file

@ -178,7 +178,15 @@ module ts.BreakpointResolver {
case SyntaxKind.ImportEqualsDeclaration:
// import statement without including semicolon
return textSpan(node,(<ImportEqualsDeclaration>node).moduleReference);
return textSpan(node, (<ImportEqualsDeclaration>node).moduleReference);
case SyntaxKind.ImportDeclaration:
// import statement without including semicolon
return textSpan(node, (<ImportDeclaration>node).moduleSpecifier);
case SyntaxKind.ExportDeclaration:
// import statement without including semicolon
return textSpan(node, (<ExportDeclaration>node).moduleSpecifier);
case SyntaxKind.ModuleDeclaration:
// span on complete module if it is instantiated

View file

@ -35,9 +35,13 @@ module ts.formatting {
// Space after keyword but not before ; or : or ?
public NoSpaceBeforeSemicolon: Rule;
public NoSpaceBeforeColon: Rule;
public NoSpaceBeforeQMark: Rule;
public NoSpaceBeforeQuestionMark: Rule;
public SpaceAfterColon: Rule;
public SpaceAfterQMark: Rule;
// insert space after '?' only when it is used in conditional operator
public SpaceAfterQuestionMarkInConditionalOperator: Rule;
// in other cases there should be no space between '?' and next token
public NoSpaceAfterQuestionMark: Rule;
public SpaceAfterSemicolon: Rule;
// Space/new line after }.
@ -215,9 +219,10 @@ module ts.formatting {
// Space after keyword but not before ; or : or ?
this.NoSpaceBeforeSemicolon = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.SemicolonToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete));
this.NoSpaceBeforeColon = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.ColonToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsNotBinaryOpContext), RuleAction.Delete));
this.NoSpaceBeforeQMark = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.QuestionToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsNotBinaryOpContext), RuleAction.Delete));
this.NoSpaceBeforeQuestionMark = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.QuestionToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsNotBinaryOpContext), RuleAction.Delete));
this.SpaceAfterColon = new Rule(RuleDescriptor.create3(SyntaxKind.ColonToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsNotBinaryOpContext), RuleAction.Space));
this.SpaceAfterQMark = new Rule(RuleDescriptor.create3(SyntaxKind.QuestionToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsNotBinaryOpContext), RuleAction.Space));
this.SpaceAfterQuestionMarkInConditionalOperator = new Rule(RuleDescriptor.create3(SyntaxKind.QuestionToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsConditionalOperatorContext), RuleAction.Space));
this.NoSpaceAfterQuestionMark = new Rule(RuleDescriptor.create3(SyntaxKind.QuestionToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete));
this.SpaceAfterSemicolon = new Rule(RuleDescriptor.create3(SyntaxKind.SemicolonToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space));
// Space after }.
@ -341,7 +346,8 @@ module ts.formatting {
this.HighPriorityCommonRules =
[
this.IgnoreBeforeComment, this.IgnoreAfterLineComment,
this.NoSpaceBeforeColon, this.SpaceAfterColon, this.NoSpaceBeforeQMark, this.SpaceAfterQMark,
this.NoSpaceBeforeColon, this.SpaceAfterColon, this.NoSpaceBeforeQuestionMark, this.SpaceAfterQuestionMarkInConditionalOperator,
this.NoSpaceAfterQuestionMark,
this.NoSpaceBeforeDot, this.NoSpaceAfterDot,
this.NoSpaceAfterUnaryPrefixOperator,
this.NoSpaceAfterUnaryPreincrementOperator, this.NoSpaceAfterUnaryPredecrementOperator,
@ -475,6 +481,10 @@ module ts.formatting {
return !Rules.IsBinaryOpContext(context);
}
static IsConditionalOperatorContext(context: FormattingContext): boolean {
return context.contextNode.kind === SyntaxKind.ConditionalExpression;
}
static IsSameLineTokenOrBeforeMultilineBlockContext(context: FormattingContext): boolean {
//// This check is mainly used inside SpaceBeforeOpenBraceInControl and SpaceBeforeOpenBraceInFunction.
////

View file

@ -50,6 +50,38 @@ module ts.NavigationBar {
case SyntaxKind.ArrayBindingPattern:
forEach((<BindingPattern>node).elements, visit);
break;
case SyntaxKind.ExportDeclaration:
// Handle named exports case e.g.:
// export {a, b as B} from "mod";
if ((<ExportDeclaration>node).exportClause) {
forEach((<ExportDeclaration>node).exportClause.elements, visit);
}
break;
case SyntaxKind.ImportDeclaration:
var importClause = (<ImportDeclaration>node).importClause;
if (importClause) {
// Handle default import case e.g.:
// import d from "mod";
if (importClause.name) {
childNodes.push(importClause);
}
// Handle named bindings in imports e.g.:
// import * as NS from "mod";
// import {a, b as B} from "mod";
if (importClause.namedBindings) {
if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) {
childNodes.push(importClause.namedBindings);
}
else {
forEach((<NamedImports>importClause.namedBindings).elements, visit);
}
}
}
break;
case SyntaxKind.BindingElement:
case SyntaxKind.VariableDeclaration:
if (isBindingPattern((<VariableDeclaration>node).name)) {
@ -62,7 +94,11 @@ module ts.NavigationBar {
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.ImportSpecifier:
case SyntaxKind.ExportSpecifier:
childNodes.push(node);
break;
}
}
@ -291,9 +327,16 @@ module ts.NavigationBar {
else {
return createItem(node, getTextOfNode(name), ts.ScriptElementKind.variableElement);
}
case SyntaxKind.Constructor:
return createItem(node, "constructor", ts.ScriptElementKind.constructorImplementationElement);
case SyntaxKind.ExportSpecifier:
case SyntaxKind.ImportSpecifier:
case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.ImportClause:
case SyntaxKind.NamespaceImport:
return createItem(node, getTextOfNode((<Declaration>node).name), ts.ScriptElementKind.alias);
}
return undefined;

View file

@ -802,6 +802,11 @@ module ts {
case SyntaxKind.EnumDeclaration:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.ExportSpecifier:
case SyntaxKind.ImportSpecifier:
case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.ImportClause:
case SyntaxKind.NamespaceImport:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.TypeLiteral:
@ -841,6 +846,37 @@ module ts {
case SyntaxKind.PropertySignature:
namedDeclarations.push(<Declaration>node);
break;
case SyntaxKind.ExportDeclaration:
// Handle named exports case e.g.:
// export {a, b as B} from "mod";
if ((<ExportDeclaration>node).exportClause) {
forEach((<ExportDeclaration>node).exportClause.elements, visit);
}
break;
case SyntaxKind.ImportDeclaration:
var importClause = (<ImportDeclaration>node).importClause;
if (importClause) {
// Handle default import case e.g.:
// import d from "mod";
if (importClause.name) {
namedDeclarations.push(importClause);
}
// Handle named bindings in imports e.g.:
// import * as NS from "mod";
// import {a, b as B} from "mod";
if (importClause.namedBindings) {
if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) {
namedDeclarations.push(<NamespaceImport>importClause.namedBindings);
}
else {
forEach((<NamedImports>importClause.namedBindings).elements, visit);
}
}
}
break;
}
});
@ -2010,6 +2046,12 @@ module ts {
case SyntaxKind.TypeParameter: return ScriptElementKind.typeParameterElement;
case SyntaxKind.EnumMember: return ScriptElementKind.variableElement;
case SyntaxKind.Parameter: return (node.flags & NodeFlags.AccessibilityModifier) ? ScriptElementKind.memberVariableElement : ScriptElementKind.parameterElement;
case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.ImportSpecifier:
case SyntaxKind.ImportClause:
case SyntaxKind.ExportSpecifier:
case SyntaxKind.NamespaceImport:
return ScriptElementKind.alias;
}
return ScriptElementKind.unknown;
}
@ -3986,7 +4028,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 +4045,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 +4065,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 +4138,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 +4508,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 +4589,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,17 @@
1 >export * from "a";
~~~~~~~~~~~~~~~~~~~ => Pos: (0 to 18) SpanInfo: {"start":0,"length":17}
>export * from "a"
>:=> (line 1, col 0) to (line 1, col 17)
--------------------------------
2 >export {a as A} from "a";
~~~~~~~~~~~~~~~~~~~~~~~~~~ => Pos: (19 to 44) SpanInfo: {"start":19,"length":24}
>export {a as A} from "a"
>:=> (line 2, col 0) to (line 2, col 24)
--------------------------------
3 >export import e = require("a");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ => Pos: (45 to 75) SpanInfo: {"start":45,"length":30}
>export import e = require("a")
>:=> (line 3, col 0) to (line 3, col 30)

View file

@ -0,0 +1,35 @@
1 >import * as NS from "a";
~~~~~~~~~~~~~~~~~~~~~~~~~ => Pos: (0 to 24) SpanInfo: {"start":0,"length":23}
>import * as NS from "a"
>:=> (line 1, col 0) to (line 1, col 23)
--------------------------------
2 >import {a as A} from "a";
~~~~~~~~~~~~~~~~~~~~~~~~~~ => Pos: (25 to 50) SpanInfo: {"start":25,"length":24}
>import {a as A} from "a"
>:=> (line 2, col 0) to (line 2, col 24)
--------------------------------
3 > import d from "a";
~~~~~~~~~~~~~~~~~~~~ => Pos: (51 to 70) SpanInfo: {"start":52,"length":17}
>import d from "a"
>:=> (line 3, col 1) to (line 3, col 18)
--------------------------------
4 >import d2, {c, d as D} from "a";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ => Pos: (71 to 103) SpanInfo: {"start":71,"length":31}
>import d2, {c, d as D} from "a"
>:=> (line 4, col 0) to (line 4, col 31)
--------------------------------
5 >import "a";
~~~~~~~~~~~~ => Pos: (104 to 115) SpanInfo: {"start":104,"length":10}
>import "a"
>:=> (line 5, col 0) to (line 5, col 10)
--------------------------------
6 >import e = require("a");
~~~~~~~~~~~~~~~~~~~~~~~~ => Pos: (116 to 139) SpanInfo: {"start":116,"length":23}
>import e = require("a")
>:=> (line 6, col 0) to (line 6, col 23)

View file

@ -0,0 +1,9 @@
/// <reference path='fourslash.ts' />
// @BaselineFile: bpSpan_exports.baseline
// @Filename: bpSpan_exports.ts
////export * from "a";
////export {a as A} from "a";
////export import e = require("a");
verify.baselineCurrentFileBreakpointLocations();

View file

@ -0,0 +1,12 @@
/// <reference path='fourslash.ts' />
// @BaselineFile: bpSpan_imports.baseline
// @Filename: bpSpan_imports.ts
////import * as NS from "a";
////import {a as A} from "a";
//// import d from "a";
////import d2, {c, d as D} from "a";
////import "a";
////import e = require("a");
verify.baselineCurrentFileBreakpointLocations();

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);

View file

@ -0,0 +1,6 @@
/// <reference path='fourslash.ts'/>
////var x=true?1:2
format.document();
goTo.bof();
verify.currentLineContentIs("var x = true ? 1 : 2");;

View file

@ -0,0 +1,12 @@
/// <reference path='fourslash.ts'/>
////interface A {
/////*1*/ foo? ();
/////*2*/ foo? <T>();
////}
format.document();
goTo.marker("1");
verify.currentLineContentIs(" foo?();");
goTo.marker("2");
verify.currentLineContentIs(" foo?<T>();");

View file

@ -30,4 +30,4 @@ goTo.marker("inNewSignature");
verify.currentLineContentIs(" new <T>(a: T);");
goTo.marker("inOptionalMethodSignature");
verify.currentLineContentIs(" op? <T, M>(a: T, b: M);");
verify.currentLineContentIs(" op?<T, M>(a: T, b: M);");

View file

@ -0,0 +1,20 @@
/// <reference path="fourslash.ts" />
////export { {| "itemName": "a", "kind": "alias", "parentName": "" |}a } from "a";
////
////export { {| "itemName": "B", "kind": "alias", "parentName": "" |}b as B } from "a";
////
////export { {| "itemName": "c", "kind": "alias", "parentName": "" |}c,
//// {| "itemName": "D", "kind": "alias", "parentName": "" |}d as D } from "a";
////
////{| "itemName": "f", "kind": "alias", "parentName": "" |}export import f = require("a");
test.markers().forEach(marker => {
verify.navigationItemsListContains(
marker.data.itemName,
marker.data.kind,
marker.data.itemName,
"exact",
marker.fileName,
marker.data.parentName);
});

View file

@ -0,0 +1,25 @@
/// <reference path="fourslash.ts" />
////import {| "itemName": "ns", "kind": "alias", "parentName": "" |}* as ns from "a";
////
////import { {| "itemName": "a", "kind": "alias", "parentName": "" |}a } from "a";
////
////import { {| "itemName": "B", "kind": "alias", "parentName": "" |}b as B } from "a";
////
////import { {| "itemName": "c", "kind": "alias", "parentName": "" |}c,
//// {| "itemName": "D", "kind": "alias", "parentName": "" |}d as D } from "a";
////
////import {| "itemName": "d1", "kind": "alias", "parentName": "" |}d1, {
//// {| "itemName": "e", "kind": "alias", "parentName": "" |}e } from "a";
////
////{| "itemName": "f", "kind": "alias", "parentName": "" |}import f = require("a");
test.markers().forEach(marker => {
verify.navigationItemsListContains(
marker.data.itemName,
marker.data.kind,
marker.data.itemName,
"exact",
marker.fileName,
marker.data.parentName);
});

View file

@ -0,0 +1,18 @@
/// <reference path="fourslash.ts"/>
////export { {| "itemName": "a", "kind": "alias", "parentName": "" |}a } from "a";
////
////export { {| "itemName": "B", "kind": "alias", "parentName": "" |}b as B } from "a"
////
////{| "itemName": "e", "kind": "alias", "parentName": "" |} export import e = require("a");
////
////export * from "a"; // no bindings here
test.markers().forEach((marker) => {
if (marker.data) {
verify.getScriptLexicalStructureListContains(marker.data.itemName, marker.data.kind, marker.fileName, marker.data.parentName);
}
});
verify.getScriptLexicalStructureListCount(4);

View file

@ -0,0 +1,25 @@
/// <reference path="fourslash.ts"/>
////import {| "itemName": "d1", "kind": "alias", "parentName": "" |}d1 from "a";
////
////import { {| "itemName": "a", "kind": "alias", "parentName": "" |}a } from "a";
////
////import { {| "itemName": "B", "kind": "alias", "parentName": "" |}b as B } from "a"
////
////import {| "itemName": "d2", "kind": "alias", "parentName": "" |}d2,
//// { {| "itemName": "c", "kind": "alias", "parentName": "" |}c,
//// {| "itemName": "D", "kind": "alias", "parentName": "" |} d as D } from "a"
////
////{| "itemName": "e", "kind": "alias", "parentName": "" |}import e = require("a");
////
////import {| "itemName": "ns", "kind": "alias", "parentName": "" |}* as ns from "a";
test.markers().forEach((marker) => {
if (marker.data) {
verify.getScriptLexicalStructureListContains(marker.data.itemName, marker.data.kind, marker.fileName, marker.data.parentName);
}
});
verify.getScriptLexicalStructureListCount(9);