From 0feeb487832189f75ba90392747dd7bdea6b7db7 Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 29 Aug 2018 10:53:32 -0700 Subject: [PATCH] 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 --- src/services/completions.ts | 41 +++++++++---------- .../completionsGeneratorFunctions.ts | 22 ++++++++++ 2 files changed, 42 insertions(+), 21 deletions(-) create mode 100644 tests/cases/fourslash/completionsGeneratorFunctions.ts diff --git a/src/services/completions.ts b/src/services/completions.ts index 7f82c9e748..a82c4e0288 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -23,7 +23,8 @@ namespace ts.Completions { type SymbolOriginInfoMap = (SymbolOriginInfo | undefined)[]; const enum KeywordCompletionFilters { - None, + None, // No keywords + All, // Every possible keyword (TODO: This is never appropriate) ClassElementKeywords, // Keywords inside class body InterfaceElementKeywords, // Keywords inside interface body 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); } - // TODO add filter for keyword based on type/value/namespace and also location - - // 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)); - } + addRange(entries, getKeywordCompletions(keywordFilters)); for (const literal of literals) { 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 { @@ -1014,6 +1007,7 @@ namespace ts.Completions { tryGetGlobalSymbols(); symbols = tagSymbols.concat(symbols); completionKind = CompletionKind.MemberLike; + keywordFilters = KeywordCompletionFilters.None; } else if (isStartingCloseTag) { const tagName = (contextToken.parent.parent).openingElement.tagName; @@ -1022,6 +1016,7 @@ namespace ts.Completions { symbols = [tagSymbol]; } completionKind = CompletionKind.MemberLike; + keywordFilters = KeywordCompletionFilters.None; } else { // 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 { - if (tryGetFunctionLikeBodyCompletionContainer(contextToken)) { - keywordFilters = KeywordCompletionFilters.FunctionLikeBodyKeywords; - } + keywordFilters = tryGetFunctionLikeBodyCompletionContainer(contextToken) ? KeywordCompletionFilters.FunctionLikeBodyKeywords : KeywordCompletionFilters.All; // Get all entities in the current scope. completionKind = CompletionKind.Global; @@ -1648,7 +1641,8 @@ namespace ts.Completions { completionKind = CompletionKind.MemberLike; // Declaring new property/method/accessor 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 (!isClassLike(decl)) return GlobalsSearch.Success; @@ -1686,14 +1680,16 @@ namespace ts.Completions { */ function tryGetObjectLikeCompletionContainer(contextToken: Node): ObjectLiteralExpression | ObjectBindingPattern | undefined { if (contextToken) { + const { parent } = contextToken; switch (contextToken.kind) { case SyntaxKind.OpenBraceToken: // const x = { | case SyntaxKind.CommaToken: // const x = { a: 0, | - const parent = contextToken.parent; if (isObjectLiteralExpression(parent) || isObjectBindingPattern(parent)) { return parent; } break; + case SyntaxKind.AsteriskToken: + return isMethodDeclaration(parent) ? tryCast(parent.parent, isObjectLiteralExpression) : undefined; } } @@ -1870,10 +1866,8 @@ namespace ts.Completions { case SyntaxKind.GetKeyword: case SyntaxKind.SetKeyword: - if (isFromObjectTypeDeclaration(contextToken)) { - return false; - } - // falls through + return !isFromObjectTypeDeclaration(contextToken); + case SyntaxKind.ClassKeyword: case SyntaxKind.EnumKeyword: case SyntaxKind.InterfaceKeyword: @@ -1885,6 +1879,9 @@ namespace ts.Completions { case SyntaxKind.YieldKeyword: case SyntaxKind.TypeKeyword: // type htm| return true; + + case SyntaxKind.AsteriskToken: + return isFunctionLike(contextToken.parent) && !isMethodDeclaration(contextToken.parent); } // If the previous token is keyword correspoding to class member completion keyword @@ -2124,6 +2121,8 @@ namespace ts.Completions { const kind = stringToToken(entry.name)!; switch (keywordFilter) { case KeywordCompletionFilters.None: + return false; + case KeywordCompletionFilters.All: return kind === SyntaxKind.AsyncKeyword || !isContextualKeyword(kind) && !isClassMemberCompletionKeyword(kind) || kind === SyntaxKind.DeclareKeyword || kind === SyntaxKind.ModuleKeyword || isTypeKeyword(kind) && kind !== SyntaxKind.UndefinedKeyword; case KeywordCompletionFilters.ClassElementKeywords: @@ -2236,7 +2235,7 @@ namespace ts.Completions { default: if (!isFromObjectTypeDeclaration(contextToken)) return undefined; 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; } } diff --git a/tests/cases/fourslash/completionsGeneratorFunctions.ts b/tests/cases/fourslash/completionsGeneratorFunctions.ts new file mode 100644 index 0000000000..1aea1eb6e4 --- /dev/null +++ b/tests/cases/fourslash/completionsGeneratorFunctions.ts @@ -0,0 +1,22 @@ +/// + +////function /*a*/ ; +////function* /*b*/ ; +////interface I { +//// abstract baseMethod(): Iterable; +////} +////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"] }, +);