Skip to content

Commit 2a3a502

Browse files
author
Andy
committed
Add KeywordCompletionFilters.TypeKeywords (#21364)
1 parent 92d2a25 commit 2a3a502

File tree

3 files changed

+46
-51
lines changed

3 files changed

+46
-51
lines changed

src/services/completions.ts

Lines changed: 35 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ namespace ts.Completions {
2020
None,
2121
ClassElementKeywords, // Keywords at class keyword
2222
ConstructorParameterKeywords, // Keywords at constructor parameter
23-
FunctionLikeBodyKeywords // Keywords at function like body
23+
FunctionLikeBodyKeywords, // Keywords at function like body
24+
TypeKeywords,
2425
}
2526

2627
export function getCompletionsAtPosition(
@@ -565,7 +566,7 @@ namespace ts.Completions {
565566
}
566567
case "none": {
567568
// Didn't find a symbol with this name. See if we can find a keyword instead.
568-
if (some(getKeywordCompletions(KeywordCompletionFilters.None), c => c.name === name)) {
569+
if (allKeywordsCompletions().some(c => c.name === name)) {
569570
return {
570571
name,
571572
kind: ScriptElementKind.keyword,
@@ -1163,26 +1164,24 @@ namespace ts.Completions {
11631164
}
11641165

11651166
function filterGlobalCompletion(symbols: Symbol[]): void {
1167+
const isTypeCompletion = insideJsDocTagTypeExpression || !isContextTokenValueLocation(contextToken) && (isPartOfTypeNode(location) || isContextTokenTypeLocation(contextToken));
1168+
if (isTypeCompletion) keywordFilters = KeywordCompletionFilters.TypeKeywords;
1169+
11661170
filterMutate(symbols, symbol => {
11671171
if (!isSourceFile(location)) {
11681172
// export = /**/ here we want to get all meanings, so any symbol is ok
11691173
if (isExportAssignment(location.parent)) {
11701174
return true;
11711175
}
11721176

1173-
// This is an alias, follow what it aliases
1174-
if (symbol && symbol.flags & SymbolFlags.Alias) {
1175-
symbol = typeChecker.getAliasedSymbol(symbol);
1176-
}
1177+
symbol = skipAlias(symbol, typeChecker);
11771178

11781179
// import m = /**/ <-- It can only access namespace (if typing import = x. this would get member symbols and not namespace)
11791180
if (isInRightSideOfInternalImportEqualsDeclaration(location)) {
11801181
return !!(symbol.flags & SymbolFlags.Namespace);
11811182
}
11821183

1183-
if (insideJsDocTagTypeExpression ||
1184-
(!isContextTokenValueLocation(contextToken) &&
1185-
(isPartOfTypeNode(location) || isContextTokenTypeLocation(contextToken)))) {
1184+
if (isTypeCompletion) {
11861185
// Its a type, but you can reach it by namespace.type as well
11871186
return symbolCanBeReferencedAtTypeLocation(symbol);
11881187
}
@@ -1199,7 +1198,7 @@ namespace ts.Completions {
11991198
contextToken.parent.kind === SyntaxKind.TypeQuery;
12001199
}
12011200

1202-
function isContextTokenTypeLocation(contextToken: Node) {
1201+
function isContextTokenTypeLocation(contextToken: Node): boolean {
12031202
if (contextToken) {
12041203
const parentKind = contextToken.parent.kind;
12051204
switch (contextToken.kind) {
@@ -1217,6 +1216,7 @@ namespace ts.Completions {
12171216
return parentKind === SyntaxKind.AsExpression;
12181217
}
12191218
}
1219+
return false;
12201220
}
12211221

12221222
function symbolCanBeReferencedAtTypeLocation(symbol: Symbol): boolean {
@@ -2130,51 +2130,38 @@ namespace ts.Completions {
21302130
}
21312131

21322132
// A cache of completion entries for keywords, these do not change between sessions
2133-
const _keywordCompletions: CompletionEntry[][] = [];
2134-
function getKeywordCompletions(keywordFilter: KeywordCompletionFilters): CompletionEntry[] {
2135-
const completions = _keywordCompletions[keywordFilter];
2136-
if (completions) {
2137-
return completions;
2133+
const _keywordCompletions: ReadonlyArray<CompletionEntry>[] = [];
2134+
const allKeywordsCompletions: () => ReadonlyArray<CompletionEntry> = ts.memoize(() => {
2135+
const res: CompletionEntry[] = [];
2136+
for (let i = SyntaxKind.FirstKeyword; i <= SyntaxKind.LastKeyword; i++) {
2137+
res.push({
2138+
name: tokenToString(i),
2139+
kind: ScriptElementKind.keyword,
2140+
kindModifiers: ScriptElementKindModifier.none,
2141+
sortText: "0"
2142+
});
21382143
}
2139-
return _keywordCompletions[keywordFilter] = generateKeywordCompletions(keywordFilter);
2140-
2141-
type FilterKeywordCompletions = (entryName: string) => boolean;
2142-
function generateKeywordCompletions(keywordFilter: KeywordCompletionFilters): CompletionEntry[] {
2144+
return res;
2145+
});
2146+
function getKeywordCompletions(keywordFilter: KeywordCompletionFilters): ReadonlyArray<CompletionEntry> {
2147+
return _keywordCompletions[keywordFilter] || (_keywordCompletions[keywordFilter] = allKeywordsCompletions().filter(entry => {
2148+
const kind = stringToToken(entry.name);
21432149
switch (keywordFilter) {
21442150
case KeywordCompletionFilters.None:
2145-
return getAllKeywordCompletions();
2151+
// "undefined" is a global variable, so don't need a keyword completion for it.
2152+
return kind !== SyntaxKind.UndefinedKeyword;
21462153
case KeywordCompletionFilters.ClassElementKeywords:
2147-
return getFilteredKeywordCompletions(isClassMemberCompletionKeywordText);
2154+
return isClassMemberCompletionKeyword(kind);
21482155
case KeywordCompletionFilters.ConstructorParameterKeywords:
2149-
return getFilteredKeywordCompletions(isConstructorParameterCompletionKeywordText);
2156+
return isConstructorParameterCompletionKeyword(kind);
21502157
case KeywordCompletionFilters.FunctionLikeBodyKeywords:
2151-
return getFilteredKeywordCompletions(isFunctionLikeBodyCompletionKeywordText);
2158+
return isFunctionLikeBodyCompletionKeyword(kind);
2159+
case KeywordCompletionFilters.TypeKeywords:
2160+
return isTypeKeyword(kind);
21522161
default:
2153-
Debug.assertNever(keywordFilter);
2162+
return Debug.assertNever(keywordFilter);
21542163
}
2155-
}
2156-
2157-
function getAllKeywordCompletions() {
2158-
const allKeywordsCompletions: CompletionEntry[] = [];
2159-
for (let i = SyntaxKind.FirstKeyword; i <= SyntaxKind.LastKeyword; i++) {
2160-
// "undefined" is a global variable, so don't need a keyword completion for it.
2161-
if (i === SyntaxKind.UndefinedKeyword) continue;
2162-
allKeywordsCompletions.push({
2163-
name: tokenToString(i),
2164-
kind: ScriptElementKind.keyword,
2165-
kindModifiers: ScriptElementKindModifier.none,
2166-
sortText: "0"
2167-
});
2168-
}
2169-
return allKeywordsCompletions;
2170-
}
2171-
2172-
function getFilteredKeywordCompletions(filterFn: FilterKeywordCompletions) {
2173-
return filter(
2174-
getKeywordCompletions(KeywordCompletionFilters.None),
2175-
entry => filterFn(entry.name)
2176-
);
2177-
}
2164+
}));
21782165
}
21792166

21802167
function isClassMemberCompletionKeyword(kind: SyntaxKind) {
@@ -2222,15 +2209,12 @@ namespace ts.Completions {
22222209
case SyntaxKind.AbstractKeyword:
22232210
case SyntaxKind.GetKeyword:
22242211
case SyntaxKind.SetKeyword:
2212+
case SyntaxKind.UndefinedKeyword:
22252213
return false;
22262214
}
22272215
return true;
22282216
}
22292217

2230-
function isFunctionLikeBodyCompletionKeywordText(text: string) {
2231-
return isFunctionLikeBodyCompletionKeyword(stringToToken(text));
2232-
}
2233-
22342218
function isEqualityOperatorKind(kind: ts.SyntaxKind): kind is EqualityOperator {
22352219
switch (kind) {
22362220
case ts.SyntaxKind.EqualsEqualsEqualsToken:

src/services/utilities.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,12 +1070,16 @@ namespace ts {
10701070
export const typeKeywords: ReadonlyArray<SyntaxKind> = [
10711071
SyntaxKind.AnyKeyword,
10721072
SyntaxKind.BooleanKeyword,
1073+
SyntaxKind.KeyOfKeyword,
10731074
SyntaxKind.NeverKeyword,
1075+
SyntaxKind.NullKeyword,
10741076
SyntaxKind.NumberKeyword,
10751077
SyntaxKind.ObjectKeyword,
10761078
SyntaxKind.StringKeyword,
10771079
SyntaxKind.SymbolKeyword,
10781080
SyntaxKind.VoidKeyword,
1081+
SyntaxKind.UndefinedKeyword,
1082+
SyntaxKind.UniqueKeyword,
10791083
];
10801084

10811085
export function isTypeKeyword(kind: SyntaxKind): boolean {
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////type T = /**/
4+
5+
goTo.marker();
6+
verify.completionListContains("undefined", "undefined", undefined, "keyword");
7+
verify.not.completionListContains("await");

0 commit comments

Comments
 (0)