Skip to content

Commit e9874a2

Browse files
committed
Merge pull request microsoft#2121 from Microsoft/importGotoDef
Support for goto def on new Import/Export syntax
2 parents eb09401 + eefbae6 commit e9874a2

28 files changed

+629
-23
lines changed

src/compiler/checker.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10292,11 +10292,12 @@ module ts {
1029210292

1029310293
case SyntaxKind.StringLiteral:
1029410294
// External module name in an import declaration
10295-
if (isExternalModuleImportEqualsDeclaration(node.parent.parent) &&
10296-
getExternalModuleImportEqualsDeclarationExpression(node.parent.parent) === node) {
10297-
var importSymbol = getSymbolOfNode(node.parent.parent);
10298-
var moduleType = getTypeOfSymbol(importSymbol);
10299-
return moduleType ? moduleType.symbol : undefined;
10295+
var moduleName: Expression;
10296+
if ((isExternalModuleImportEqualsDeclaration(node.parent.parent) &&
10297+
getExternalModuleImportEqualsDeclarationExpression(node.parent.parent) === node) ||
10298+
((node.parent.kind === SyntaxKind.ImportDeclaration || node.parent.kind === SyntaxKind.ExportDeclaration) &&
10299+
(<ImportDeclaration>node.parent).moduleSpecifier === node)) {
10300+
return resolveExternalModuleName(node, <LiteralExpression>node);
1030010301
}
1030110302

1030210303
// Intentional fall-through

src/compiler/utilities.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,12 @@ module ts {
745745
}
746746

747747
var parent = name.parent;
748+
if (parent.kind === SyntaxKind.ImportSpecifier || parent.kind === SyntaxKind.ExportSpecifier) {
749+
if ((<ImportOrExportSpecifier>parent).propertyName) {
750+
return true;
751+
}
752+
}
753+
748754
if (isDeclaration(parent) || parent.kind === SyntaxKind.FunctionExpression) {
749755
return (<Declaration>parent).name === name;
750756
}

src/services/breakpoints.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,15 @@ module ts.BreakpointResolver {
178178

179179
case SyntaxKind.ImportEqualsDeclaration:
180180
// import statement without including semicolon
181-
return textSpan(node,(<ImportEqualsDeclaration>node).moduleReference);
181+
return textSpan(node, (<ImportEqualsDeclaration>node).moduleReference);
182+
183+
case SyntaxKind.ImportDeclaration:
184+
// import statement without including semicolon
185+
return textSpan(node, (<ImportDeclaration>node).moduleSpecifier);
186+
187+
case SyntaxKind.ExportDeclaration:
188+
// import statement without including semicolon
189+
return textSpan(node, (<ExportDeclaration>node).moduleSpecifier);
182190

183191
case SyntaxKind.ModuleDeclaration:
184192
// span on complete module if it is instantiated

src/services/navigationBar.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,38 @@ module ts.NavigationBar {
5050
case SyntaxKind.ArrayBindingPattern:
5151
forEach((<BindingPattern>node).elements, visit);
5252
break;
53+
54+
case SyntaxKind.ExportDeclaration:
55+
// Handle named exports case e.g.:
56+
// export {a, b as B} from "mod";
57+
if ((<ExportDeclaration>node).exportClause) {
58+
forEach((<ExportDeclaration>node).exportClause.elements, visit);
59+
}
60+
break;
61+
62+
case SyntaxKind.ImportDeclaration:
63+
var importClause = (<ImportDeclaration>node).importClause;
64+
if (importClause) {
65+
// Handle default import case e.g.:
66+
// import d from "mod";
67+
if (importClause.name) {
68+
childNodes.push(importClause);
69+
}
70+
71+
// Handle named bindings in imports e.g.:
72+
// import * as NS from "mod";
73+
// import {a, b as B} from "mod";
74+
if (importClause.namedBindings) {
75+
if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) {
76+
childNodes.push(importClause.namedBindings);
77+
}
78+
else {
79+
forEach((<NamedImports>importClause.namedBindings).elements, visit);
80+
}
81+
}
82+
}
83+
break;
84+
5385
case SyntaxKind.BindingElement:
5486
case SyntaxKind.VariableDeclaration:
5587
if (isBindingPattern((<VariableDeclaration>node).name)) {
@@ -62,7 +94,11 @@ module ts.NavigationBar {
6294
case SyntaxKind.InterfaceDeclaration:
6395
case SyntaxKind.ModuleDeclaration:
6496
case SyntaxKind.FunctionDeclaration:
97+
case SyntaxKind.ImportEqualsDeclaration:
98+
case SyntaxKind.ImportSpecifier:
99+
case SyntaxKind.ExportSpecifier:
65100
childNodes.push(node);
101+
break;
66102
}
67103
}
68104

@@ -291,9 +327,16 @@ module ts.NavigationBar {
291327
else {
292328
return createItem(node, getTextOfNode(name), ts.ScriptElementKind.variableElement);
293329
}
294-
330+
295331
case SyntaxKind.Constructor:
296332
return createItem(node, "constructor", ts.ScriptElementKind.constructorImplementationElement);
333+
334+
case SyntaxKind.ExportSpecifier:
335+
case SyntaxKind.ImportSpecifier:
336+
case SyntaxKind.ImportEqualsDeclaration:
337+
case SyntaxKind.ImportClause:
338+
case SyntaxKind.NamespaceImport:
339+
return createItem(node, getTextOfNode((<Declaration>node).name), ts.ScriptElementKind.alias);
297340
}
298341

299342
return undefined;

src/services/services.ts

Lines changed: 121 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,11 @@ module ts {
802802
case SyntaxKind.EnumDeclaration:
803803
case SyntaxKind.ModuleDeclaration:
804804
case SyntaxKind.ImportEqualsDeclaration:
805+
case SyntaxKind.ExportSpecifier:
806+
case SyntaxKind.ImportSpecifier:
807+
case SyntaxKind.ImportEqualsDeclaration:
808+
case SyntaxKind.ImportClause:
809+
case SyntaxKind.NamespaceImport:
805810
case SyntaxKind.GetAccessor:
806811
case SyntaxKind.SetAccessor:
807812
case SyntaxKind.TypeLiteral:
@@ -841,6 +846,37 @@ module ts {
841846
case SyntaxKind.PropertySignature:
842847
namedDeclarations.push(<Declaration>node);
843848
break;
849+
850+
case SyntaxKind.ExportDeclaration:
851+
// Handle named exports case e.g.:
852+
// export {a, b as B} from "mod";
853+
if ((<ExportDeclaration>node).exportClause) {
854+
forEach((<ExportDeclaration>node).exportClause.elements, visit);
855+
}
856+
break;
857+
858+
case SyntaxKind.ImportDeclaration:
859+
var importClause = (<ImportDeclaration>node).importClause;
860+
if (importClause) {
861+
// Handle default import case e.g.:
862+
// import d from "mod";
863+
if (importClause.name) {
864+
namedDeclarations.push(importClause);
865+
}
866+
867+
// Handle named bindings in imports e.g.:
868+
// import * as NS from "mod";
869+
// import {a, b as B} from "mod";
870+
if (importClause.namedBindings) {
871+
if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) {
872+
namedDeclarations.push(<NamespaceImport>importClause.namedBindings);
873+
}
874+
else {
875+
forEach((<NamedImports>importClause.namedBindings).elements, visit);
876+
}
877+
}
878+
}
879+
break;
844880
}
845881
});
846882

@@ -2010,6 +2046,12 @@ module ts {
20102046
case SyntaxKind.TypeParameter: return ScriptElementKind.typeParameterElement;
20112047
case SyntaxKind.EnumMember: return ScriptElementKind.variableElement;
20122048
case SyntaxKind.Parameter: return (node.flags & NodeFlags.AccessibilityModifier) ? ScriptElementKind.memberVariableElement : ScriptElementKind.parameterElement;
2049+
case SyntaxKind.ImportEqualsDeclaration:
2050+
case SyntaxKind.ImportSpecifier:
2051+
case SyntaxKind.ImportClause:
2052+
case SyntaxKind.ExportSpecifier:
2053+
case SyntaxKind.NamespaceImport:
2054+
return ScriptElementKind.alias;
20132055
}
20142056
return ScriptElementKind.unknown;
20152057
}
@@ -3292,6 +3334,17 @@ module ts {
32923334
return undefined;
32933335
}
32943336

3337+
// If this is an alias, and the request came at the declaration location
3338+
// get the aliased symbol instead. This allows for goto def on an import e.g.
3339+
// import {A, B} from "mod";
3340+
// to jump to the implementation directelly.
3341+
if (symbol.flags & SymbolFlags.Import) {
3342+
var declaration = symbol.declarations[0];
3343+
if (node.kind === SyntaxKind.Identifier && node.parent === declaration) {
3344+
symbol = typeInfoResolver.getAliasedSymbol(symbol);
3345+
}
3346+
}
3347+
32953348
var result: DefinitionInfo[] = [];
32963349

32973350
// Because name in short-hand property assignment has two different meanings: property name and property value,
@@ -4022,7 +4075,7 @@ module ts {
40224075
var searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), declarations);
40234076

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

40274080
// Try to get the smallest valid scope that we can limit our search to;
40284081
// otherwise we'll need to search globally (i.e. include each file).
@@ -4039,7 +4092,7 @@ module ts {
40394092
getReferencesInNode(sourceFiles[0], symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result);
40404093
}
40414094
else {
4042-
var internedName = getInternedName(symbol, declarations)
4095+
var internedName = getInternedName(symbol, node, declarations)
40434096
forEach(sourceFiles, sourceFile => {
40444097
cancellationToken.throwIfCancellationRequested();
40454098

@@ -4059,13 +4112,51 @@ module ts {
40594112

40604113
return result;
40614114

4062-
function getDeclaredName(symbol: Symbol) {
4115+
function isImportOrExportSpecifierName(location: Node): boolean {
4116+
return location.parent &&
4117+
(location.parent.kind === SyntaxKind.ImportSpecifier || location.parent.kind === SyntaxKind.ExportSpecifier) &&
4118+
(<ImportOrExportSpecifier>location.parent).propertyName === location;
4119+
}
4120+
4121+
function isImportOrExportSpecifierImportSymbol(symbol: Symbol) {
4122+
return (symbol.flags & SymbolFlags.Import) && forEach(symbol.declarations, declaration => {
4123+
return declaration.kind === SyntaxKind.ImportSpecifier || declaration.kind === SyntaxKind.ExportSpecifier;
4124+
});
4125+
}
4126+
4127+
function getDeclaredName(symbol: Symbol, location: Node) {
4128+
// Special case for function expressions, whose names are solely local to their bodies.
4129+
var functionExpression = forEach(symbol.declarations, d => d.kind === SyntaxKind.FunctionExpression ? <FunctionExpression>d : undefined);
4130+
4131+
// When a name gets interned into a SourceFile's 'identifiers' Map,
4132+
// its name is escaped and stored in the same way its symbol name/identifier
4133+
// name should be stored. Function expressions, however, are a special case,
4134+
// because despite sometimes having a name, the binder unconditionally binds them
4135+
// to a symbol with the name "__function".
4136+
if (functionExpression && functionExpression.name) {
4137+
var name = functionExpression.name.text;
4138+
}
4139+
4140+
// If this is an export or import specifier it could have been renamed using the as syntax.
4141+
// if so we want to search for whatever under the cursor, the symbol is pointing to the alias (name)
4142+
// so check for the propertyName.
4143+
if (isImportOrExportSpecifierName(location)) {
4144+
return location.getText();
4145+
}
4146+
40634147
var name = typeInfoResolver.symbolToString(symbol);
40644148

40654149
return stripQuotes(name);
40664150
}
40674151

4068-
function getInternedName(symbol: Symbol, declarations: Declaration[]): string {
4152+
function getInternedName(symbol: Symbol, location: Node, declarations: Declaration[]): string {
4153+
// If this is an export or import specifier it could have been renamed using the as syntax.
4154+
// if so we want to search for whatever under the cursor, the symbol is pointing to the alias (name)
4155+
// so check for the propertyName.
4156+
if (isImportOrExportSpecifierName(location)) {
4157+
return location.getText();
4158+
}
4159+
40694160
// Special case for function expressions, whose names are solely local to their bodies.
40704161
var functionExpression = forEach(declarations, d => d.kind === SyntaxKind.FunctionExpression ? <FunctionExpression>d : undefined);
40714162

@@ -4094,16 +4185,22 @@ module ts {
40944185

40954186
function getSymbolScope(symbol: Symbol): Node {
40964187
// If this is private property or method, the scope is the containing class
4097-
if (symbol.getFlags() && (SymbolFlags.Property | SymbolFlags.Method)) {
4188+
if (symbol.flags & (SymbolFlags.Property | SymbolFlags.Method)) {
40984189
var privateDeclaration = forEach(symbol.getDeclarations(), d => (d.flags & NodeFlags.Private) ? d : undefined);
40994190
if (privateDeclaration) {
41004191
return getAncestor(privateDeclaration, SyntaxKind.ClassDeclaration);
41014192
}
41024193
}
41034194

4195+
// If the symbol is an import we would like to find it if we are looking for what it imports.
4196+
// So consider it visibile outside its declaration scope.
4197+
if (symbol.flags & SymbolFlags.Import) {
4198+
return undefined;
4199+
}
4200+
41044201
// if this symbol is visible from its parent container, e.g. exported, then bail out
41054202
// if symbol correspond to the union property - bail out
4106-
if (symbol.parent || (symbol.getFlags() & SymbolFlags.UnionProperty)) {
4203+
if (symbol.parent || (symbol.flags & SymbolFlags.UnionProperty)) {
41074204
return undefined;
41084205
}
41094206

@@ -4458,6 +4555,11 @@ module ts {
44584555
// The search set contains at least the current symbol
44594556
var result = [symbol];
44604557

4558+
// If the symbol is an alias, add what it alaises to the list
4559+
if (isImportOrExportSpecifierImportSymbol(symbol)) {
4560+
result.push(typeInfoResolver.getAliasedSymbol(symbol));
4561+
}
4562+
44614563
// If the location is in a context sensitive location (i.e. in an object literal) try
44624564
// to get a contextual type for it, and add the property symbol from the contextual
44634565
// type to the search set
@@ -4534,6 +4636,13 @@ module ts {
45344636
return true;
45354637
}
45364638

4639+
// If the reference symbol is an alias, check if what it is aliasing is one of the search
4640+
// symbols.
4641+
if (isImportOrExportSpecifierImportSymbol(referenceSymbol) &&
4642+
searchSymbols.indexOf(typeInfoResolver.getAliasedSymbol(referenceSymbol)) >= 0) {
4643+
return true;
4644+
}
4645+
45374646
// If the reference location is in an object literal, try to get the contextual type for the
45384647
// object literal, lookup the property symbol in the contextual type, and use this symbol to
45394648
// compare to our searchSymbol
@@ -4744,12 +4853,18 @@ module ts {
47444853
case SyntaxKind.NamedImports:
47454854
case SyntaxKind.ImportSpecifier:
47464855
case SyntaxKind.ImportEqualsDeclaration:
4856+
case SyntaxKind.ImportDeclaration:
4857+
case SyntaxKind.ExportAssignment:
4858+
case SyntaxKind.ExportDeclaration:
47474859
return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace;
47484860

47494861
// An external module can be a Value
47504862
case SyntaxKind.SourceFile:
47514863
return SemanticMeaning.Namespace | SemanticMeaning.Value;
47524864
}
4865+
4866+
return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace;
4867+
47534868
Debug.fail("Unknown declaration type");
47544869
}
47554870

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
1 >export * from "a";
3+
4+
~~~~~~~~~~~~~~~~~~~ => Pos: (0 to 18) SpanInfo: {"start":0,"length":17}
5+
>export * from "a"
6+
>:=> (line 1, col 0) to (line 1, col 17)
7+
--------------------------------
8+
2 >export {a as A} from "a";
9+
10+
~~~~~~~~~~~~~~~~~~~~~~~~~~ => Pos: (19 to 44) SpanInfo: {"start":19,"length":24}
11+
>export {a as A} from "a"
12+
>:=> (line 2, col 0) to (line 2, col 24)
13+
--------------------------------
14+
3 >export import e = require("a");
15+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ => Pos: (45 to 75) SpanInfo: {"start":45,"length":30}
16+
>export import e = require("a")
17+
>:=> (line 3, col 0) to (line 3, col 30)

0 commit comments

Comments
 (0)