mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-03-06 12:32:12 -06:00
Merge pull request #3615 from Microsoft/completionsInObjectPatterns
Enable completions in object binding patterns
This commit is contained in:
commit
816cfe3ec6
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 => {
|
||||
|
||||
@ -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");
|
||||
@ -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");
|
||||
@ -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();
|
||||
@ -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");
|
||||
@ -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");
|
||||
@ -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();
|
||||
@ -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");
|
||||
@ -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");
|
||||
@ -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");
|
||||
@ -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");
|
||||
@ -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");
|
||||
@ -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");
|
||||
Loading…
x
Reference in New Issue
Block a user