Merge pull request #3615 from Microsoft/completionsInObjectPatterns

Enable completions in object binding patterns
This commit is contained in:
Daniel Rosenwasser 2015-06-24 22:53:53 -04:00
commit 816cfe3ec6
15 changed files with 239 additions and 33 deletions

View File

@ -12386,7 +12386,7 @@ namespace ts {
return resolveExternalModuleName(node, <LiteralExpression>node);
}
// Intentional fall-through
// fall through
case SyntaxKind.NumericLiteral:
// index access
if (node.parent.kind === SyntaxKind.ElementAccessExpression && (<ElementAccessExpression>node.parent).argumentExpression === node) {
@ -12453,6 +12453,10 @@ namespace ts {
return symbol && getTypeOfSymbol(symbol);
}
if (isBindingPattern(node)) {
return getTypeForVariableLikeDeclaration(<VariableLikeDeclaration>node.parent);
}
if (isInRightSideOfImportOrExportAssignment(<Identifier>node)) {
let symbol = getSymbolInfo(node);
let declaredType = symbol && getDeclaredTypeOfSymbol(symbol);

View File

@ -660,8 +660,7 @@ module FourSlash {
}
errorMsg += "]\n";
Harness.IO.log(errorMsg);
this.raiseError("Member list is not empty at Caret");
this.raiseError("Member list is not empty at Caret: " + errorMsg);
}
}

View File

@ -2983,21 +2983,32 @@ namespace ts {
}
function tryGetGlobalSymbols(): boolean {
let containingObjectLiteral = getContainingObjectLiteralApplicableForCompletion(contextToken);
if (containingObjectLiteral) {
let objectLikeContainer = tryGetObjectLikeCompletionContainer(contextToken);
if (objectLikeContainer) {
// Object literal expression, look up possible property names from contextual type
isMemberCompletion = true;
isNewIdentifierLocation = true;
let contextualType = typeChecker.getContextualType(containingObjectLiteral);
if (!contextualType) {
let typeForObject: Type;
let existingMembers: Declaration[];
if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) {
typeForObject = typeChecker.getContextualType(<ObjectLiteralExpression>objectLikeContainer);
existingMembers = (<ObjectLiteralExpression>objectLikeContainer).properties;
}
else {
typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer);
existingMembers = (<BindingPattern>objectLikeContainer).elements;
}
if (!typeForObject) {
return false;
}
let contextualTypeMembers = typeChecker.getPropertiesOfType(contextualType);
if (contextualTypeMembers && contextualTypeMembers.length > 0) {
let typeMembers = typeChecker.getPropertiesOfType(typeForObject);
if (typeMembers && typeMembers.length > 0) {
// Add filtered items to the completion list
symbols = filterContextualMembersList(contextualTypeMembers, containingObjectLiteral.properties);
symbols = filterObjectMembersList(typeMembers, existingMembers);
}
}
else if (getAncestor(contextToken, SyntaxKind.ImportClause)) {
@ -3185,17 +3196,18 @@ namespace ts {
return false;
}
function getContainingObjectLiteralApplicableForCompletion(previousToken: Node): ObjectLiteralExpression {
// The locations in an object literal expression that are applicable for completion are property name definition locations.
if (previousToken) {
let parent = previousToken.parent;
switch (previousToken.kind) {
/**
* Returns the immediate owning object literal or binding pattern of a context token,
* on the condition that one exists and that the context implies completion should be given.
*/
function tryGetObjectLikeCompletionContainer(contextToken: Node): ObjectLiteralExpression | BindingPattern {
if (contextToken) {
switch (contextToken.kind) {
case SyntaxKind.OpenBraceToken: // let x = { |
case SyntaxKind.CommaToken: // let x = { a: 0, |
if (parent && parent.kind === SyntaxKind.ObjectLiteralExpression) {
return <ObjectLiteralExpression>parent;
let parent = contextToken.parent;
if (parent && (parent.kind === SyntaxKind.ObjectLiteralExpression || parent.kind === SyntaxKind.ObjectBindingPattern)) {
return <ObjectLiteralExpression | BindingPattern>parent;
}
break;
}
@ -3234,8 +3246,7 @@ namespace ts {
containingNodeKind === SyntaxKind.ClassDeclaration || // class A<T, |
containingNodeKind === SyntaxKind.FunctionDeclaration || // function A<T, |
containingNodeKind === SyntaxKind.InterfaceDeclaration || // interface A<T, |
containingNodeKind === SyntaxKind.ArrayBindingPattern || // var [x, y|
containingNodeKind === SyntaxKind.ObjectBindingPattern; // function func({ x, y|
containingNodeKind === SyntaxKind.ArrayBindingPattern; // var [x, y|
case SyntaxKind.DotToken:
return containingNodeKind === SyntaxKind.ArrayBindingPattern; // var [.|
@ -3253,8 +3264,7 @@ namespace ts {
case SyntaxKind.OpenBraceToken:
return containingNodeKind === SyntaxKind.EnumDeclaration || // enum a { |
containingNodeKind === SyntaxKind.InterfaceDeclaration || // interface a { |
containingNodeKind === SyntaxKind.TypeLiteral || // let x : { |
containingNodeKind === SyntaxKind.ObjectBindingPattern; // function func({ x|
containingNodeKind === SyntaxKind.TypeLiteral; // let x : { |
case SyntaxKind.SemicolonToken:
return containingNodeKind === SyntaxKind.PropertySignature &&
@ -3346,26 +3356,39 @@ namespace ts {
return filter(exports, e => !lookUp(exisingImports, e.name));
}
function filterContextualMembersList(contextualMemberSymbols: Symbol[], existingMembers: Declaration[]): Symbol[] {
function filterObjectMembersList(contextualMemberSymbols: Symbol[], existingMembers: Declaration[]): Symbol[] {
if (!existingMembers || existingMembers.length === 0) {
return contextualMemberSymbols;
}
let existingMemberNames: Map<boolean> = {};
forEach(existingMembers, m => {
if (m.kind !== SyntaxKind.PropertyAssignment && m.kind !== SyntaxKind.ShorthandPropertyAssignment) {
// Ignore omitted expressions for missing members in the object literal
return;
for (let m of existingMembers) {
// Ignore omitted expressions for missing members
if (m.kind !== SyntaxKind.PropertyAssignment &&
m.kind !== SyntaxKind.ShorthandPropertyAssignment &&
m.kind !== SyntaxKind.BindingElement) {
continue;
}
// If this is the current item we are editing right now, do not filter it out
if (m.getStart() <= position && position <= m.getEnd()) {
// If this is the current item we are editing right now, do not filter it out
return;
continue;
}
// TODO(jfreeman): Account for computed property name
existingMemberNames[(<Identifier>m.name).text] = true;
});
let existingName: string;
if (m.kind === SyntaxKind.BindingElement && (<BindingElement>m).propertyName) {
existingName = (<BindingElement>m).propertyName.text;
}
else {
// TODO(jfreeman): Account for computed property name
// NOTE: if one only performs this step when m.name is an identifier,
// things like '__proto__' are not filtered out.
existingName = (<Identifier>m.name).text;
}
existingMemberNames[existingName] = true;
}
let filteredMembers: Symbol[] = [];
forEach(contextualMemberSymbols, s => {

View File

@ -0,0 +1,13 @@
/// <reference path='fourslash.ts'/>
////interface I {
//// property1: number;
//// property2: string;
////}
////
////var foo: I;
////var { /**/ } = foo;
goTo.marker();
verify.completionListContains("property1");
verify.completionListContains("property2");

View File

@ -0,0 +1,13 @@
/// <reference path='fourslash.ts'/>
////interface I {
//// property1: number;
//// property2: string;
////}
////
////var foo: I;
////var { property1, /**/ } = foo;
goTo.marker();
verify.completionListContains("property2");
verify.not.completionListContains("property1");

View File

@ -0,0 +1,13 @@
/// <reference path='fourslash.ts'/>
////interface I {
//// property1: number;
//// property2: string;
////}
////
////var foo: I;
////var { property1: /**/ } = foo;
goTo.marker();
verify.completionListAllowsNewIdentifier();
verify.completionListIsEmpty();

View File

@ -0,0 +1,13 @@
/// <reference path='fourslash.ts'/>
////interface I {
//// property1: number;
//// property2: string;
////}
////
////var foo: I;
////var { prope/**/ } = foo;
goTo.marker();
verify.completionListContains("property1");
verify.completionListContains("property2");

View File

@ -0,0 +1,12 @@
/// <reference path='fourslash.ts'/>
////interface I {
//// property1: number;
//// property2: string;
////}
////
////var foo: I;
////var { property1/**/ } = foo;
goTo.marker();
verify.completionListContains("property1");

View File

@ -0,0 +1,12 @@
/// <reference path='fourslash.ts'/>
////interface I {
//// property1: number;
//// property2: string;
////}
////
////var foo: I;
////var { property1, property2, /**/ } = foo;
goTo.marker();
verify.completionListIsEmpty();

View File

@ -0,0 +1,18 @@
/// <reference path='fourslash.ts'/>
////interface I {
//// propertyOfI_1: number;
//// propertyOfI_2: string;
////}
////interface J {
//// property1: I;
//// property2: string;
////}
////
////var foo: J;
////var { property1: { /**/ } } = foo;
goTo.marker();
verify.completionListContains("propertyOfI_1");
verify.completionListContains("propertyOfI_2");
verify.not.completionListContains("property2");

View File

@ -0,0 +1,18 @@
/// <reference path='fourslash.ts'/>
////interface I {
//// propertyOfI_1: number;
//// propertyOfI_2: string;
////}
////interface J {
//// property1: I;
//// property2: string;
////}
////
////var foo: J;
////var { property1: { propertyOfI_1, /**/ } } = foo;
goTo.marker();
verify.completionListContains("propertyOfI_2");
verify.not.completionListContains("propertyOfI_1");
verify.not.completionListContains("property2");

View File

@ -0,0 +1,19 @@
/// <reference path='fourslash.ts'/>
////interface I {
//// propertyOfI_1: number;
//// propertyOfI_2: string;
////}
////interface J {
//// property1: I;
//// property2: string;
////}
////
////var foo: J;
////var { property1: { propertyOfI_1, }, /**/ } = foo;
goTo.marker();
verify.completionListContains("property2");
verify.not.completionListContains("property1");
verify.not.completionListContains("propertyOfI_2");
verify.not.completionListContains("propertyOfI_1");

View File

@ -0,0 +1,23 @@
/// <reference path='fourslash.ts'/>
////interface I {
//// propertyOfI_1: number;
//// propertyOfI_2: string;
////}
////interface J {
//// property1: I;
//// property2: string;
////}
////
////var foo: J[];
////var [{ property1: { propertyOfI_1, }, /*1*/ }, { /*2*/ }] = foo;
goTo.marker("1");
verify.completionListContains("property2");
verify.not.completionListContains("property1");
verify.not.completionListContains("propertyOfI_2");
verify.not.completionListContains("propertyOfI_1");
goTo.marker("2");
verify.completionListContains("property1");
verify.completionListContains("property2");

View File

@ -0,0 +1,13 @@
/// <reference path='fourslash.ts'/>
////interface I {
//// property1: number;
//// property2: string;
////}
////
////var { property1: prop1, /**/ }: I;
goTo.marker("");
verify.completionListContains("property2");
verify.not.completionListContains("property1");
verify.not.completionListContains("prop1");

View File

@ -0,0 +1,13 @@
/// <reference path='fourslash.ts'/>
////interface I {
//// property1: number;
//// property2: string;
////}
////
////function f({ property1, /**/ }: I): void {
////}
goTo.marker("");
verify.completionListContains("property2");
verify.not.completionListContains("property1");