Skip to content

Commit eb09401

Browse files
committed
Merge pull request microsoft#2134 from Microsoft/completionForExports
Completion for exports
2 parents a0eff60 + 0d781d8 commit eb09401

13 files changed

+146
-13
lines changed

src/compiler/checker.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ module ts {
5656
isImplementationOfOverload,
5757
getAliasedSymbol: resolveImport,
5858
getEmitResolver,
59+
getExportsOfExternalModule,
5960
};
6061

6162
var undefinedSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "undefined");
@@ -2754,6 +2755,19 @@ module ts {
27542755
return result;
27552756
}
27562757

2758+
function getExportsOfExternalModule(node: ImportDeclaration): Symbol[]{
2759+
if (!node.moduleSpecifier) {
2760+
return emptyArray;
2761+
}
2762+
2763+
var module = resolveExternalModuleName(node, node.moduleSpecifier);
2764+
if (!module || !module.exports) {
2765+
return emptyArray;
2766+
}
2767+
2768+
return mapToArray(getExportsOfModule(module))
2769+
}
2770+
27572771
function getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature {
27582772
var links = getNodeLinks(declaration);
27592773
if (!links.resolvedSignature) {

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,7 @@ module ts {
11001100
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
11011101
isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean;
11021102
getAliasedSymbol(symbol: Symbol): Symbol;
1103+
getExportsOfExternalModule(node: ImportDeclaration): Symbol[];
11031104

11041105
// Should not be called directly. Should only be accessed through the Program instance.
11051106
/* @internal */ getDiagnostics(sourceFile?: SourceFile): Diagnostic[];

src/services/formatting/smartIndenter.ts

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
module ts.formatting {
44
export module SmartIndenter {
5+
6+
const enum Value {
7+
Unknown = -1
8+
}
9+
510
export function getIndentation(position: number, sourceFile: SourceFile, options: EditorOptions): number {
611
if (position > sourceFile.text.length) {
712
return 0; // past EOF
@@ -29,7 +34,7 @@ module ts.formatting {
2934
if (precedingToken.kind === SyntaxKind.CommaToken && precedingToken.parent.kind !== SyntaxKind.BinaryExpression) {
3035
// previous token is comma that separates items in list - find the previous item and try to derive indentation from it
3136
var actualIndentation = getActualIndentationForListItemBeforeComma(precedingToken, sourceFile, options);
32-
if (actualIndentation !== -1) {
37+
if (actualIndentation !== Value.Unknown) {
3338
return actualIndentation;
3439
}
3540
}
@@ -57,7 +62,7 @@ module ts.formatting {
5762

5863
// check if current node is a list item - if yes, take indentation from it
5964
var actualIndentation = getActualIndentationForListItem(current, sourceFile, options);
60-
if (actualIndentation !== -1) {
65+
if (actualIndentation !== Value.Unknown) {
6166
return actualIndentation;
6267
}
6368

@@ -101,7 +106,7 @@ module ts.formatting {
101106
if (useActualIndentation) {
102107
// check if current node is a list item - if yes, take indentation from it
103108
var actualIndentation = getActualIndentationForListItem(current, sourceFile, options);
104-
if (actualIndentation !== -1) {
109+
if (actualIndentation !== Value.Unknown) {
105110
return actualIndentation + indentationDelta;
106111
}
107112
}
@@ -113,7 +118,7 @@ module ts.formatting {
113118
if (useActualIndentation) {
114119
// try to fetch actual indentation for current node from source text
115120
var actualIndentation = getActualIndentationForNode(current, parent, currentStart, parentAndChildShareLine, sourceFile, options);
116-
if (actualIndentation !== -1) {
121+
if (actualIndentation !== Value.Unknown) {
117122
return actualIndentation + indentationDelta;
118123
}
119124
}
@@ -142,18 +147,22 @@ module ts.formatting {
142147
}
143148

144149
/*
145-
* Function returns -1 if indentation cannot be determined
150+
* Function returns Value.Unknown if indentation cannot be determined
146151
*/
147152
function getActualIndentationForListItemBeforeComma(commaToken: Node, sourceFile: SourceFile, options: EditorOptions): number {
148153
// previous token is comma that separates items in list - find the previous item and try to derive indentation from it
149154
var commaItemInfo = findListItemInfo(commaToken);
150-
Debug.assert(commaItemInfo && commaItemInfo.listItemIndex > 0);
151-
// The item we're interested in is right before the comma
152-
return deriveActualIndentationFromList(commaItemInfo.list.getChildren(), commaItemInfo.listItemIndex - 1, sourceFile, options);
155+
if (commaItemInfo && commaItemInfo.listItemIndex > 0) {
156+
return deriveActualIndentationFromList(commaItemInfo.list.getChildren(), commaItemInfo.listItemIndex - 1, sourceFile, options);
157+
}
158+
else {
159+
// handle broken code gracefully
160+
return Value.Unknown;
161+
}
153162
}
154163

155164
/*
156-
* Function returns -1 if actual indentation for node should not be used (i.e because node is nested expression)
165+
* Function returns Value.Unknown if actual indentation for node should not be used (i.e because node is nested expression)
157166
*/
158167
function getActualIndentationForNode(current: Node,
159168
parent: Node,
@@ -170,7 +179,7 @@ module ts.formatting {
170179
(parent.kind === SyntaxKind.SourceFile || !parentAndChildShareLine);
171180

172181
if (!useActualIndentation) {
173-
return -1;
182+
return Value.Unknown;
174183
}
175184

176185
return findColumnForFirstNonWhitespaceCharacterInLine(currentLineAndChar, sourceFile, options);
@@ -271,11 +280,11 @@ module ts.formatting {
271280

272281
function getActualIndentationForListItem(node: Node, sourceFile: SourceFile, options: EditorOptions): number {
273282
var containingList = getContainingList(node, sourceFile);
274-
return containingList ? getActualIndentationFromList(containingList) : -1;
283+
return containingList ? getActualIndentationFromList(containingList) : Value.Unknown;
275284

276285
function getActualIndentationFromList(list: Node[]): number {
277286
var index = indexOf(list, node);
278-
return index !== -1 ? deriveActualIndentationFromList(list, index, sourceFile, options) : -1;
287+
return index !== -1 ? deriveActualIndentationFromList(list, index, sourceFile, options) : Value.Unknown;
279288
}
280289
}
281290

@@ -298,7 +307,7 @@ module ts.formatting {
298307

299308
lineAndCharacter = getStartLineAndCharacterForNode(list[i], sourceFile);
300309
}
301-
return -1;
310+
return Value.Unknown;
302311
}
303312

304313
function findColumnForFirstNonWhitespaceCharacterInLine(lineAndCharacter: LineAndCharacter, sourceFile: SourceFile, options: EditorOptions): number {

src/services/services.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2403,6 +2403,19 @@ module ts {
24032403
getCompletionEntriesFromSymbols(filteredMembers, activeCompletionSession);
24042404
}
24052405
}
2406+
else if (getAncestor(previousToken, SyntaxKind.ImportClause)) {
2407+
// cursor is in import clause
2408+
// try to show exported member for imported module
2409+
isMemberCompletion = true;
2410+
isNewIdentifierLocation = true;
2411+
if (showCompletionsInImportsClause(previousToken)) {
2412+
var importDeclaration = <ImportDeclaration>getAncestor(previousToken, SyntaxKind.ImportDeclaration);
2413+
Debug.assert(importDeclaration !== undefined);
2414+
var exports = typeInfoResolver.getExportsOfExternalModule(importDeclaration);
2415+
var filteredExports = filterModuleExports(exports, importDeclaration);
2416+
getCompletionEntriesFromSymbols(filteredExports, activeCompletionSession);
2417+
}
2418+
}
24062419
else {
24072420
// Get scope members
24082421
isMemberCompletion = false;
@@ -2453,6 +2466,18 @@ module ts {
24532466
return result;
24542467
}
24552468

2469+
function showCompletionsInImportsClause(node: Node): boolean {
2470+
if (node) {
2471+
// import {|
2472+
// import {a,|
2473+
if (node.kind === SyntaxKind.OpenBraceToken || node.kind === SyntaxKind.CommaToken) {
2474+
return node.parent.kind === SyntaxKind.NamedImports;
2475+
}
2476+
}
2477+
2478+
return false;
2479+
}
2480+
24562481
function isNewIdentifierDefinitionLocation(previousToken: Node): boolean {
24572482
if (previousToken) {
24582483
var containingNodeKind = previousToken.parent.kind;
@@ -2664,6 +2689,28 @@ module ts {
26642689
return false;
26652690
}
26662691

2692+
function filterModuleExports(exports: Symbol[], importDeclaration: ImportDeclaration): Symbol[] {
2693+
var exisingImports: Map<boolean> = {};
2694+
2695+
if (!importDeclaration.importClause) {
2696+
return exports;
2697+
}
2698+
2699+
if (importDeclaration.importClause.namedBindings &&
2700+
importDeclaration.importClause.namedBindings.kind === SyntaxKind.NamedImports) {
2701+
2702+
forEach((<NamedImports>importDeclaration.importClause.namedBindings).elements, el => {
2703+
var name = el.propertyName || el.name;
2704+
exisingImports[name.text] = true;
2705+
});
2706+
}
2707+
2708+
if (isEmpty(exisingImports)) {
2709+
return exports;
2710+
}
2711+
return filter(exports, e => !lookUp(exisingImports, e.name));
2712+
}
2713+
26672714
function filterContextualMembersList(contextualMemberSymbols: Symbol[], existingMembers: Declaration[]): Symbol[] {
26682715
if (!existingMembers || existingMembers.length === 0) {
26692716
return contextualMemberSymbols;
@@ -4694,6 +4741,8 @@ module ts {
46944741
return SemanticMeaning.Namespace;
46954742
}
46964743

4744+
case SyntaxKind.NamedImports:
4745+
case SyntaxKind.ImportSpecifier:
46974746
case SyntaxKind.ImportEqualsDeclaration:
46984747
return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace;
46994748

tests/baselines/reference/APISample_compile.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,7 @@ declare module "typescript" {
869869
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
870870
isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean;
871871
getAliasedSymbol(symbol: Symbol): Symbol;
872+
getExportsOfExternalModule(node: ImportDeclaration): Symbol[];
872873
}
873874
interface SymbolDisplayBuilder {
874875
buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;

tests/baselines/reference/APISample_compile.types

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2718,6 +2718,12 @@ declare module "typescript" {
27182718
>getAliasedSymbol : (symbol: Symbol) => Symbol
27192719
>symbol : Symbol
27202720
>Symbol : Symbol
2721+
>Symbol : Symbol
2722+
2723+
getExportsOfExternalModule(node: ImportDeclaration): Symbol[];
2724+
>getExportsOfExternalModule : (node: ImportDeclaration) => Symbol[]
2725+
>node : ImportDeclaration
2726+
>ImportDeclaration : ImportDeclaration
27212727
>Symbol : Symbol
27222728
}
27232729
interface SymbolDisplayBuilder {

tests/baselines/reference/APISample_linter.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,7 @@ declare module "typescript" {
900900
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
901901
isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean;
902902
getAliasedSymbol(symbol: Symbol): Symbol;
903+
getExportsOfExternalModule(node: ImportDeclaration): Symbol[];
903904
}
904905
interface SymbolDisplayBuilder {
905906
buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;

tests/baselines/reference/APISample_linter.types

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2864,6 +2864,12 @@ declare module "typescript" {
28642864
>getAliasedSymbol : (symbol: Symbol) => Symbol
28652865
>symbol : Symbol
28662866
>Symbol : Symbol
2867+
>Symbol : Symbol
2868+
2869+
getExportsOfExternalModule(node: ImportDeclaration): Symbol[];
2870+
>getExportsOfExternalModule : (node: ImportDeclaration) => Symbol[]
2871+
>node : ImportDeclaration
2872+
>ImportDeclaration : ImportDeclaration
28672873
>Symbol : Symbol
28682874
}
28692875
interface SymbolDisplayBuilder {

tests/baselines/reference/APISample_transform.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,7 @@ declare module "typescript" {
901901
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
902902
isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean;
903903
getAliasedSymbol(symbol: Symbol): Symbol;
904+
getExportsOfExternalModule(node: ImportDeclaration): Symbol[];
904905
}
905906
interface SymbolDisplayBuilder {
906907
buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;

tests/baselines/reference/APISample_transform.types

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2814,6 +2814,12 @@ declare module "typescript" {
28142814
>getAliasedSymbol : (symbol: Symbol) => Symbol
28152815
>symbol : Symbol
28162816
>Symbol : Symbol
2817+
>Symbol : Symbol
2818+
2819+
getExportsOfExternalModule(node: ImportDeclaration): Symbol[];
2820+
>getExportsOfExternalModule : (node: ImportDeclaration) => Symbol[]
2821+
>node : ImportDeclaration
2822+
>ImportDeclaration : ImportDeclaration
28172823
>Symbol : Symbol
28182824
}
28192825
interface SymbolDisplayBuilder {

0 commit comments

Comments
 (0)