Merge pull request #2134 from Microsoft/completionForExports
Completion for exports
This commit is contained in:
commit
eb09401df7
|
@ -56,6 +56,7 @@ module ts {
|
|||
isImplementationOfOverload,
|
||||
getAliasedSymbol: resolveImport,
|
||||
getEmitResolver,
|
||||
getExportsOfExternalModule,
|
||||
};
|
||||
|
||||
var undefinedSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "undefined");
|
||||
|
@ -2754,6 +2755,19 @@ module ts {
|
|||
return result;
|
||||
}
|
||||
|
||||
function getExportsOfExternalModule(node: ImportDeclaration): Symbol[]{
|
||||
if (!node.moduleSpecifier) {
|
||||
return emptyArray;
|
||||
}
|
||||
|
||||
var module = resolveExternalModuleName(node, node.moduleSpecifier);
|
||||
if (!module || !module.exports) {
|
||||
return emptyArray;
|
||||
}
|
||||
|
||||
return mapToArray(getExportsOfModule(module))
|
||||
}
|
||||
|
||||
function getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature {
|
||||
var links = getNodeLinks(declaration);
|
||||
if (!links.resolvedSignature) {
|
||||
|
|
|
@ -1100,6 +1100,7 @@ module ts {
|
|||
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
|
||||
isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean;
|
||||
getAliasedSymbol(symbol: Symbol): Symbol;
|
||||
getExportsOfExternalModule(node: ImportDeclaration): Symbol[];
|
||||
|
||||
// Should not be called directly. Should only be accessed through the Program instance.
|
||||
/* @internal */ getDiagnostics(sourceFile?: SourceFile): Diagnostic[];
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
module ts.formatting {
|
||||
export module SmartIndenter {
|
||||
|
||||
const enum Value {
|
||||
Unknown = -1
|
||||
}
|
||||
|
||||
export function getIndentation(position: number, sourceFile: SourceFile, options: EditorOptions): number {
|
||||
if (position > sourceFile.text.length) {
|
||||
return 0; // past EOF
|
||||
|
@ -29,7 +34,7 @@ module ts.formatting {
|
|||
if (precedingToken.kind === SyntaxKind.CommaToken && precedingToken.parent.kind !== SyntaxKind.BinaryExpression) {
|
||||
// previous token is comma that separates items in list - find the previous item and try to derive indentation from it
|
||||
var actualIndentation = getActualIndentationForListItemBeforeComma(precedingToken, sourceFile, options);
|
||||
if (actualIndentation !== -1) {
|
||||
if (actualIndentation !== Value.Unknown) {
|
||||
return actualIndentation;
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +62,7 @@ module ts.formatting {
|
|||
|
||||
// check if current node is a list item - if yes, take indentation from it
|
||||
var actualIndentation = getActualIndentationForListItem(current, sourceFile, options);
|
||||
if (actualIndentation !== -1) {
|
||||
if (actualIndentation !== Value.Unknown) {
|
||||
return actualIndentation;
|
||||
}
|
||||
|
||||
|
@ -101,7 +106,7 @@ module ts.formatting {
|
|||
if (useActualIndentation) {
|
||||
// check if current node is a list item - if yes, take indentation from it
|
||||
var actualIndentation = getActualIndentationForListItem(current, sourceFile, options);
|
||||
if (actualIndentation !== -1) {
|
||||
if (actualIndentation !== Value.Unknown) {
|
||||
return actualIndentation + indentationDelta;
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +118,7 @@ module ts.formatting {
|
|||
if (useActualIndentation) {
|
||||
// try to fetch actual indentation for current node from source text
|
||||
var actualIndentation = getActualIndentationForNode(current, parent, currentStart, parentAndChildShareLine, sourceFile, options);
|
||||
if (actualIndentation !== -1) {
|
||||
if (actualIndentation !== Value.Unknown) {
|
||||
return actualIndentation + indentationDelta;
|
||||
}
|
||||
}
|
||||
|
@ -142,18 +147,22 @@ module ts.formatting {
|
|||
}
|
||||
|
||||
/*
|
||||
* Function returns -1 if indentation cannot be determined
|
||||
* Function returns Value.Unknown if indentation cannot be determined
|
||||
*/
|
||||
function getActualIndentationForListItemBeforeComma(commaToken: Node, sourceFile: SourceFile, options: EditorOptions): number {
|
||||
// previous token is comma that separates items in list - find the previous item and try to derive indentation from it
|
||||
var commaItemInfo = findListItemInfo(commaToken);
|
||||
Debug.assert(commaItemInfo && commaItemInfo.listItemIndex > 0);
|
||||
// The item we're interested in is right before the comma
|
||||
return deriveActualIndentationFromList(commaItemInfo.list.getChildren(), commaItemInfo.listItemIndex - 1, sourceFile, options);
|
||||
if (commaItemInfo && commaItemInfo.listItemIndex > 0) {
|
||||
return deriveActualIndentationFromList(commaItemInfo.list.getChildren(), commaItemInfo.listItemIndex - 1, sourceFile, options);
|
||||
}
|
||||
else {
|
||||
// handle broken code gracefully
|
||||
return Value.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Function returns -1 if actual indentation for node should not be used (i.e because node is nested expression)
|
||||
* Function returns Value.Unknown if actual indentation for node should not be used (i.e because node is nested expression)
|
||||
*/
|
||||
function getActualIndentationForNode(current: Node,
|
||||
parent: Node,
|
||||
|
@ -170,7 +179,7 @@ module ts.formatting {
|
|||
(parent.kind === SyntaxKind.SourceFile || !parentAndChildShareLine);
|
||||
|
||||
if (!useActualIndentation) {
|
||||
return -1;
|
||||
return Value.Unknown;
|
||||
}
|
||||
|
||||
return findColumnForFirstNonWhitespaceCharacterInLine(currentLineAndChar, sourceFile, options);
|
||||
|
@ -271,11 +280,11 @@ module ts.formatting {
|
|||
|
||||
function getActualIndentationForListItem(node: Node, sourceFile: SourceFile, options: EditorOptions): number {
|
||||
var containingList = getContainingList(node, sourceFile);
|
||||
return containingList ? getActualIndentationFromList(containingList) : -1;
|
||||
return containingList ? getActualIndentationFromList(containingList) : Value.Unknown;
|
||||
|
||||
function getActualIndentationFromList(list: Node[]): number {
|
||||
var index = indexOf(list, node);
|
||||
return index !== -1 ? deriveActualIndentationFromList(list, index, sourceFile, options) : -1;
|
||||
return index !== -1 ? deriveActualIndentationFromList(list, index, sourceFile, options) : Value.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -298,7 +307,7 @@ module ts.formatting {
|
|||
|
||||
lineAndCharacter = getStartLineAndCharacterForNode(list[i], sourceFile);
|
||||
}
|
||||
return -1;
|
||||
return Value.Unknown;
|
||||
}
|
||||
|
||||
function findColumnForFirstNonWhitespaceCharacterInLine(lineAndCharacter: LineAndCharacter, sourceFile: SourceFile, options: EditorOptions): number {
|
||||
|
|
|
@ -2403,6 +2403,19 @@ module ts {
|
|||
getCompletionEntriesFromSymbols(filteredMembers, activeCompletionSession);
|
||||
}
|
||||
}
|
||||
else if (getAncestor(previousToken, SyntaxKind.ImportClause)) {
|
||||
// cursor is in import clause
|
||||
// try to show exported member for imported module
|
||||
isMemberCompletion = true;
|
||||
isNewIdentifierLocation = true;
|
||||
if (showCompletionsInImportsClause(previousToken)) {
|
||||
var importDeclaration = <ImportDeclaration>getAncestor(previousToken, SyntaxKind.ImportDeclaration);
|
||||
Debug.assert(importDeclaration !== undefined);
|
||||
var exports = typeInfoResolver.getExportsOfExternalModule(importDeclaration);
|
||||
var filteredExports = filterModuleExports(exports, importDeclaration);
|
||||
getCompletionEntriesFromSymbols(filteredExports, activeCompletionSession);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Get scope members
|
||||
isMemberCompletion = false;
|
||||
|
@ -2453,6 +2466,18 @@ module ts {
|
|||
return result;
|
||||
}
|
||||
|
||||
function showCompletionsInImportsClause(node: Node): boolean {
|
||||
if (node) {
|
||||
// import {|
|
||||
// import {a,|
|
||||
if (node.kind === SyntaxKind.OpenBraceToken || node.kind === SyntaxKind.CommaToken) {
|
||||
return node.parent.kind === SyntaxKind.NamedImports;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function isNewIdentifierDefinitionLocation(previousToken: Node): boolean {
|
||||
if (previousToken) {
|
||||
var containingNodeKind = previousToken.parent.kind;
|
||||
|
@ -2664,6 +2689,28 @@ module ts {
|
|||
return false;
|
||||
}
|
||||
|
||||
function filterModuleExports(exports: Symbol[], importDeclaration: ImportDeclaration): Symbol[] {
|
||||
var exisingImports: Map<boolean> = {};
|
||||
|
||||
if (!importDeclaration.importClause) {
|
||||
return exports;
|
||||
}
|
||||
|
||||
if (importDeclaration.importClause.namedBindings &&
|
||||
importDeclaration.importClause.namedBindings.kind === SyntaxKind.NamedImports) {
|
||||
|
||||
forEach((<NamedImports>importDeclaration.importClause.namedBindings).elements, el => {
|
||||
var name = el.propertyName || el.name;
|
||||
exisingImports[name.text] = true;
|
||||
});
|
||||
}
|
||||
|
||||
if (isEmpty(exisingImports)) {
|
||||
return exports;
|
||||
}
|
||||
return filter(exports, e => !lookUp(exisingImports, e.name));
|
||||
}
|
||||
|
||||
function filterContextualMembersList(contextualMemberSymbols: Symbol[], existingMembers: Declaration[]): Symbol[] {
|
||||
if (!existingMembers || existingMembers.length === 0) {
|
||||
return contextualMemberSymbols;
|
||||
|
@ -4694,6 +4741,8 @@ module ts {
|
|||
return SemanticMeaning.Namespace;
|
||||
}
|
||||
|
||||
case SyntaxKind.NamedImports:
|
||||
case SyntaxKind.ImportSpecifier:
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace;
|
||||
|
||||
|
|
|
@ -869,6 +869,7 @@ declare module "typescript" {
|
|||
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
|
||||
isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean;
|
||||
getAliasedSymbol(symbol: Symbol): Symbol;
|
||||
getExportsOfExternalModule(node: ImportDeclaration): Symbol[];
|
||||
}
|
||||
interface SymbolDisplayBuilder {
|
||||
buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
|
||||
|
|
|
@ -2718,6 +2718,12 @@ declare module "typescript" {
|
|||
>getAliasedSymbol : (symbol: Symbol) => Symbol
|
||||
>symbol : Symbol
|
||||
>Symbol : Symbol
|
||||
>Symbol : Symbol
|
||||
|
||||
getExportsOfExternalModule(node: ImportDeclaration): Symbol[];
|
||||
>getExportsOfExternalModule : (node: ImportDeclaration) => Symbol[]
|
||||
>node : ImportDeclaration
|
||||
>ImportDeclaration : ImportDeclaration
|
||||
>Symbol : Symbol
|
||||
}
|
||||
interface SymbolDisplayBuilder {
|
||||
|
|
|
@ -900,6 +900,7 @@ declare module "typescript" {
|
|||
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
|
||||
isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean;
|
||||
getAliasedSymbol(symbol: Symbol): Symbol;
|
||||
getExportsOfExternalModule(node: ImportDeclaration): Symbol[];
|
||||
}
|
||||
interface SymbolDisplayBuilder {
|
||||
buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
|
||||
|
|
|
@ -2864,6 +2864,12 @@ declare module "typescript" {
|
|||
>getAliasedSymbol : (symbol: Symbol) => Symbol
|
||||
>symbol : Symbol
|
||||
>Symbol : Symbol
|
||||
>Symbol : Symbol
|
||||
|
||||
getExportsOfExternalModule(node: ImportDeclaration): Symbol[];
|
||||
>getExportsOfExternalModule : (node: ImportDeclaration) => Symbol[]
|
||||
>node : ImportDeclaration
|
||||
>ImportDeclaration : ImportDeclaration
|
||||
>Symbol : Symbol
|
||||
}
|
||||
interface SymbolDisplayBuilder {
|
||||
|
|
|
@ -901,6 +901,7 @@ declare module "typescript" {
|
|||
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
|
||||
isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean;
|
||||
getAliasedSymbol(symbol: Symbol): Symbol;
|
||||
getExportsOfExternalModule(node: ImportDeclaration): Symbol[];
|
||||
}
|
||||
interface SymbolDisplayBuilder {
|
||||
buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
|
||||
|
|
|
@ -2814,6 +2814,12 @@ declare module "typescript" {
|
|||
>getAliasedSymbol : (symbol: Symbol) => Symbol
|
||||
>symbol : Symbol
|
||||
>Symbol : Symbol
|
||||
>Symbol : Symbol
|
||||
|
||||
getExportsOfExternalModule(node: ImportDeclaration): Symbol[];
|
||||
>getExportsOfExternalModule : (node: ImportDeclaration) => Symbol[]
|
||||
>node : ImportDeclaration
|
||||
>ImportDeclaration : ImportDeclaration
|
||||
>Symbol : Symbol
|
||||
}
|
||||
interface SymbolDisplayBuilder {
|
||||
|
|
|
@ -938,6 +938,7 @@ declare module "typescript" {
|
|||
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
|
||||
isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean;
|
||||
getAliasedSymbol(symbol: Symbol): Symbol;
|
||||
getExportsOfExternalModule(node: ImportDeclaration): Symbol[];
|
||||
}
|
||||
interface SymbolDisplayBuilder {
|
||||
buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
|
||||
|
|
|
@ -2987,6 +2987,12 @@ declare module "typescript" {
|
|||
>getAliasedSymbol : (symbol: Symbol) => Symbol
|
||||
>symbol : Symbol
|
||||
>Symbol : Symbol
|
||||
>Symbol : Symbol
|
||||
|
||||
getExportsOfExternalModule(node: ImportDeclaration): Symbol[];
|
||||
>getExportsOfExternalModule : (node: ImportDeclaration) => Symbol[]
|
||||
>node : ImportDeclaration
|
||||
>ImportDeclaration : ImportDeclaration
|
||||
>Symbol : Symbol
|
||||
}
|
||||
interface SymbolDisplayBuilder {
|
||||
|
|
32
tests/cases/fourslash/completionForExports.ts
Normal file
32
tests/cases/fourslash/completionForExports.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// @Filename: m1.ts
|
||||
////export var foo: number = 1;
|
||||
////export function bar() { return 10; }
|
||||
////export function baz() { return 10; }
|
||||
|
||||
// @Filename: m2.ts
|
||||
////import {/*1*/, /*2*/ from "m1"
|
||||
////import {/*3*/} from "m1"
|
||||
////import {foo,/*4*/ from "m1"
|
||||
////import {bar as /*5*/, /*6*/ from "m1"
|
||||
////import {foo, bar, baz as b,/*7*/} from "m1"
|
||||
function verifyCompletionAtMarker(marker: string, ...completions: string[]) {
|
||||
goTo.marker(marker);
|
||||
if (completions.length) {
|
||||
for (var i = 0; i < completions.length; ++i) {
|
||||
verify.completionListContains(completions[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
verify.completionListIsEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
verifyCompletionAtMarker("1", "foo", "bar", "baz");
|
||||
verifyCompletionAtMarker("2", "foo", "bar", "baz");
|
||||
verifyCompletionAtMarker("3", "foo", "bar", "baz");
|
||||
verifyCompletionAtMarker("4", "bar", "baz");
|
||||
verifyCompletionAtMarker("5");
|
||||
verifyCompletionAtMarker("6", "foo", "baz");
|
||||
verifyCompletionAtMarker("7");
|
Loading…
Reference in a new issue