Make generator function name a completion list blocker (#26640)

* Make generator function name a completion list blocker

* Improvements for class/object members

* Separate KeywordCompletionFilter.None and .All
This commit is contained in:
Andy 2018-08-29 10:53:32 -07:00 committed by GitHub
parent 9100047f84
commit 0feeb48783
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 42 additions and 21 deletions

View file

@ -23,7 +23,8 @@ namespace ts.Completions {
type SymbolOriginInfoMap = (SymbolOriginInfo | undefined)[]; type SymbolOriginInfoMap = (SymbolOriginInfo | undefined)[];
const enum KeywordCompletionFilters { const enum KeywordCompletionFilters {
None, None, // No keywords
All, // Every possible keyword (TODO: This is never appropriate)
ClassElementKeywords, // Keywords inside class body ClassElementKeywords, // Keywords inside class body
InterfaceElementKeywords, // Keywords inside interface body InterfaceElementKeywords, // Keywords inside interface body
ConstructorParameterKeywords, // Keywords at constructor parameter ConstructorParameterKeywords, // Keywords at constructor parameter
@ -143,21 +144,13 @@ namespace ts.Completions {
getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target!, log, completionKind, preferences, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap); getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target!, log, completionKind, preferences, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap);
} }
// TODO add filter for keyword based on type/value/namespace and also location addRange(entries, getKeywordCompletions(keywordFilters));
// Add all keywords if
// - this is not a member completion list (all the keywords)
// - other filters are enabled in required scenario so add those keywords
const isMemberCompletion = isMemberCompletionKind(completionKind);
if (keywordFilters !== KeywordCompletionFilters.None || !isMemberCompletion) {
addRange(entries, getKeywordCompletions(keywordFilters));
}
for (const literal of literals) { for (const literal of literals) {
entries.push(createCompletionEntryForLiteral(literal)); entries.push(createCompletionEntryForLiteral(literal));
} }
return { isGlobalCompletion: isInSnippetScope, isMemberCompletion, isNewIdentifierLocation, entries }; return { isGlobalCompletion: isInSnippetScope, isMemberCompletion: isMemberCompletionKind(completionKind), isNewIdentifierLocation, entries };
} }
function isUncheckedFile(sourceFile: SourceFile, compilerOptions: CompilerOptions): boolean { function isUncheckedFile(sourceFile: SourceFile, compilerOptions: CompilerOptions): boolean {
@ -1014,6 +1007,7 @@ namespace ts.Completions {
tryGetGlobalSymbols(); tryGetGlobalSymbols();
symbols = tagSymbols.concat(symbols); symbols = tagSymbols.concat(symbols);
completionKind = CompletionKind.MemberLike; completionKind = CompletionKind.MemberLike;
keywordFilters = KeywordCompletionFilters.None;
} }
else if (isStartingCloseTag) { else if (isStartingCloseTag) {
const tagName = (<JsxElement>contextToken.parent.parent).openingElement.tagName; const tagName = (<JsxElement>contextToken.parent.parent).openingElement.tagName;
@ -1022,6 +1016,7 @@ namespace ts.Completions {
symbols = [tagSymbol]; symbols = [tagSymbol];
} }
completionKind = CompletionKind.MemberLike; completionKind = CompletionKind.MemberLike;
keywordFilters = KeywordCompletionFilters.None;
} }
else { else {
// For JavaScript or TypeScript, if we're not after a dot, then just try to get the // For JavaScript or TypeScript, if we're not after a dot, then just try to get the
@ -1191,9 +1186,7 @@ namespace ts.Completions {
} }
function getGlobalCompletions(): void { function getGlobalCompletions(): void {
if (tryGetFunctionLikeBodyCompletionContainer(contextToken)) { keywordFilters = tryGetFunctionLikeBodyCompletionContainer(contextToken) ? KeywordCompletionFilters.FunctionLikeBodyKeywords : KeywordCompletionFilters.All;
keywordFilters = KeywordCompletionFilters.FunctionLikeBodyKeywords;
}
// Get all entities in the current scope. // Get all entities in the current scope.
completionKind = CompletionKind.Global; completionKind = CompletionKind.Global;
@ -1648,7 +1641,8 @@ namespace ts.Completions {
completionKind = CompletionKind.MemberLike; completionKind = CompletionKind.MemberLike;
// Declaring new property/method/accessor // Declaring new property/method/accessor
isNewIdentifierLocation = true; isNewIdentifierLocation = true;
keywordFilters = isClassLike(decl) ? KeywordCompletionFilters.ClassElementKeywords : KeywordCompletionFilters.InterfaceElementKeywords; keywordFilters = contextToken.kind === SyntaxKind.AsteriskToken ? KeywordCompletionFilters.None :
isClassLike(decl) ? KeywordCompletionFilters.ClassElementKeywords : KeywordCompletionFilters.InterfaceElementKeywords;
// If you're in an interface you don't want to repeat things from super-interface. So just stop here. // If you're in an interface you don't want to repeat things from super-interface. So just stop here.
if (!isClassLike(decl)) return GlobalsSearch.Success; if (!isClassLike(decl)) return GlobalsSearch.Success;
@ -1686,14 +1680,16 @@ namespace ts.Completions {
*/ */
function tryGetObjectLikeCompletionContainer(contextToken: Node): ObjectLiteralExpression | ObjectBindingPattern | undefined { function tryGetObjectLikeCompletionContainer(contextToken: Node): ObjectLiteralExpression | ObjectBindingPattern | undefined {
if (contextToken) { if (contextToken) {
const { parent } = contextToken;
switch (contextToken.kind) { switch (contextToken.kind) {
case SyntaxKind.OpenBraceToken: // const x = { | case SyntaxKind.OpenBraceToken: // const x = { |
case SyntaxKind.CommaToken: // const x = { a: 0, | case SyntaxKind.CommaToken: // const x = { a: 0, |
const parent = contextToken.parent;
if (isObjectLiteralExpression(parent) || isObjectBindingPattern(parent)) { if (isObjectLiteralExpression(parent) || isObjectBindingPattern(parent)) {
return parent; return parent;
} }
break; break;
case SyntaxKind.AsteriskToken:
return isMethodDeclaration(parent) ? tryCast(parent.parent, isObjectLiteralExpression) : undefined;
} }
} }
@ -1870,10 +1866,8 @@ namespace ts.Completions {
case SyntaxKind.GetKeyword: case SyntaxKind.GetKeyword:
case SyntaxKind.SetKeyword: case SyntaxKind.SetKeyword:
if (isFromObjectTypeDeclaration(contextToken)) { return !isFromObjectTypeDeclaration(contextToken);
return false;
}
// falls through
case SyntaxKind.ClassKeyword: case SyntaxKind.ClassKeyword:
case SyntaxKind.EnumKeyword: case SyntaxKind.EnumKeyword:
case SyntaxKind.InterfaceKeyword: case SyntaxKind.InterfaceKeyword:
@ -1885,6 +1879,9 @@ namespace ts.Completions {
case SyntaxKind.YieldKeyword: case SyntaxKind.YieldKeyword:
case SyntaxKind.TypeKeyword: // type htm| case SyntaxKind.TypeKeyword: // type htm|
return true; return true;
case SyntaxKind.AsteriskToken:
return isFunctionLike(contextToken.parent) && !isMethodDeclaration(contextToken.parent);
} }
// If the previous token is keyword correspoding to class member completion keyword // If the previous token is keyword correspoding to class member completion keyword
@ -2124,6 +2121,8 @@ namespace ts.Completions {
const kind = stringToToken(entry.name)!; const kind = stringToToken(entry.name)!;
switch (keywordFilter) { switch (keywordFilter) {
case KeywordCompletionFilters.None: case KeywordCompletionFilters.None:
return false;
case KeywordCompletionFilters.All:
return kind === SyntaxKind.AsyncKeyword || !isContextualKeyword(kind) && !isClassMemberCompletionKeyword(kind) || kind === SyntaxKind.DeclareKeyword || kind === SyntaxKind.ModuleKeyword return kind === SyntaxKind.AsyncKeyword || !isContextualKeyword(kind) && !isClassMemberCompletionKeyword(kind) || kind === SyntaxKind.DeclareKeyword || kind === SyntaxKind.ModuleKeyword
|| isTypeKeyword(kind) && kind !== SyntaxKind.UndefinedKeyword; || isTypeKeyword(kind) && kind !== SyntaxKind.UndefinedKeyword;
case KeywordCompletionFilters.ClassElementKeywords: case KeywordCompletionFilters.ClassElementKeywords:
@ -2236,7 +2235,7 @@ namespace ts.Completions {
default: default:
if (!isFromObjectTypeDeclaration(contextToken)) return undefined; if (!isFromObjectTypeDeclaration(contextToken)) return undefined;
const isValidKeyword = isClassLike(contextToken.parent.parent) ? isClassMemberCompletionKeyword : isInterfaceOrTypeLiteralCompletionKeyword; const isValidKeyword = isClassLike(contextToken.parent.parent) ? isClassMemberCompletionKeyword : isInterfaceOrTypeLiteralCompletionKeyword;
return (isValidKeyword(contextToken.kind) || isIdentifier(contextToken) && isValidKeyword(stringToToken(contextToken.text)!)) // TODO: GH#18217 return (isValidKeyword(contextToken.kind) || contextToken.kind === SyntaxKind.AsteriskToken || isIdentifier(contextToken) && isValidKeyword(stringToToken(contextToken.text)!)) // TODO: GH#18217
? contextToken.parent.parent as ObjectTypeDeclaration : undefined; ? contextToken.parent.parent as ObjectTypeDeclaration : undefined;
} }
} }

View file

@ -0,0 +1,22 @@
/// <reference path="fourslash.ts" />
////function /*a*/ ;
////function* /*b*/ ;
////interface I {
//// abstract baseMethod(): Iterable<number>;
////}
////class C implements I {
//// */*c*/ ;
//// public */*d*/
////}
////const o: I = {
//// */*e*/
////};
////1 * /*f*/
verify.completions(
{ marker: ["a", "b"], exact: undefined, isNewIdentifierLocation: true },
{ marker: ["c", "d"], exact: ["baseMethod"], isNewIdentifierLocation: true },
{ marker: "e", exact: ["baseMethod"] },
{ marker: "f", includes: ["Number"] },
);