Skip to content

Commit f829f95

Browse files
committed
Filter ts only keywords from js file completion
Fixes #29212
1 parent da48790 commit f829f95

File tree

4 files changed

+314
-4
lines changed

4 files changed

+314
-4
lines changed

src/harness/fourslash.ts

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4555,6 +4555,9 @@ namespace FourSlashInterface {
45554555
export const classElementKeywords: ReadonlyArray<ExpectedCompletionEntryObject> =
45564556
["private", "protected", "public", "static", "abstract", "async", "constructor", "get", "readonly", "set"].map(keywordEntry);
45574557

4558+
export const classElementInJsKeywords: ReadonlyArray<ExpectedCompletionEntryObject> =
4559+
["async", "constructor", "get", "set"].map(keywordEntry);
4560+
45584561
export const constructorParameterKeywords: ReadonlyArray<ExpectedCompletionEntryObject> =
45594562
["private", "protected", "public", "readonly"].map((name): ExpectedCompletionEntryObject => ({ name, kind: "keyword" }));
45604563

@@ -4692,6 +4695,59 @@ namespace FourSlashInterface {
46924695
}
46934696
});
46944697

4698+
export const statementInJsKeywords: ReadonlyArray<ExpectedCompletionEntryObject> = [
4699+
"break",
4700+
"case",
4701+
"catch",
4702+
"class",
4703+
"const",
4704+
"continue",
4705+
"debugger",
4706+
"default",
4707+
"delete",
4708+
"do",
4709+
"else",
4710+
"enum",
4711+
"export",
4712+
"extends",
4713+
"false",
4714+
"finally",
4715+
"for",
4716+
"function",
4717+
"if",
4718+
"import",
4719+
"in",
4720+
"instanceof",
4721+
"new",
4722+
"null",
4723+
"return",
4724+
"super",
4725+
"switch",
4726+
"this",
4727+
"throw",
4728+
"true",
4729+
"try",
4730+
"typeof",
4731+
"var",
4732+
"void",
4733+
"while",
4734+
"with",
4735+
"implements",
4736+
"interface",
4737+
"let",
4738+
"package",
4739+
"yield",
4740+
"as",
4741+
"async",
4742+
"await",
4743+
"constructor",
4744+
"get",
4745+
"require",
4746+
"set",
4747+
"from",
4748+
"of",
4749+
].map(keywordEntry);
4750+
46954751
export const globalsVars: ReadonlyArray<ExpectedCompletionEntryObject> = [
46964752
functionEntry("eval"),
46974753
functionEntry("parseInt"),
@@ -4793,6 +4849,60 @@ namespace FourSlashInterface {
47934849
...globalKeywordsInsideFunction,
47944850
];
47954851

4852+
const globalInJsKeywordsInsideFunction: ReadonlyArray<ExpectedCompletionEntryObject> = [
4853+
"break",
4854+
"case",
4855+
"catch",
4856+
"class",
4857+
"const",
4858+
"continue",
4859+
"debugger",
4860+
"default",
4861+
"delete",
4862+
"do",
4863+
"else",
4864+
"export",
4865+
"extends",
4866+
"false",
4867+
"finally",
4868+
"for",
4869+
"function",
4870+
"if",
4871+
"import",
4872+
"in",
4873+
"instanceof",
4874+
"new",
4875+
"null",
4876+
"return",
4877+
"super",
4878+
"switch",
4879+
"this",
4880+
"throw",
4881+
"true",
4882+
"try",
4883+
"typeof",
4884+
"var",
4885+
"void",
4886+
"while",
4887+
"with",
4888+
"implements",
4889+
"let",
4890+
"package",
4891+
"yield",
4892+
"async",
4893+
"await",
4894+
].map(keywordEntry);
4895+
4896+
// TODO: many of these are inappropriate to always provide
4897+
export const globalsInJsInsideFunction = (plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> => [
4898+
{ name: "arguments", kind: "local var" },
4899+
{ name: "globalThis", kind: "module" },
4900+
...globalsVars,
4901+
...plus,
4902+
{ name: "undefined", kind: "var" },
4903+
...globalInJsKeywordsInsideFunction,
4904+
];
4905+
47964906
// TODO: many of these are inappropriate to always provide
47974907
export const globalKeywords: ReadonlyArray<ExpectedCompletionEntryObject> = [
47984908
"break",
@@ -4871,6 +4981,57 @@ namespace FourSlashInterface {
48714981
"of",
48724982
].map(keywordEntry);
48734983

4984+
export const globalInJsKeywords: ReadonlyArray<ExpectedCompletionEntryObject> = [
4985+
"break",
4986+
"case",
4987+
"catch",
4988+
"class",
4989+
"const",
4990+
"continue",
4991+
"debugger",
4992+
"default",
4993+
"delete",
4994+
"do",
4995+
"else",
4996+
"export",
4997+
"extends",
4998+
"false",
4999+
"finally",
5000+
"for",
5001+
"function",
5002+
"if",
5003+
"import",
5004+
"in",
5005+
"instanceof",
5006+
"new",
5007+
"null",
5008+
"return",
5009+
"super",
5010+
"switch",
5011+
"this",
5012+
"throw",
5013+
"true",
5014+
"try",
5015+
"typeof",
5016+
"var",
5017+
"void",
5018+
"while",
5019+
"with",
5020+
"implements",
5021+
"let",
5022+
"package",
5023+
"yield",
5024+
"as",
5025+
"async",
5026+
"await",
5027+
"constructor",
5028+
"get",
5029+
"require",
5030+
"set",
5031+
"from",
5032+
"of",
5033+
].map(keywordEntry);
5034+
48745035
export const insideMethodKeywords: ReadonlyArray<ExpectedCompletionEntryObject> = [
48755036
"break",
48765037
"case",
@@ -4917,6 +5078,50 @@ namespace FourSlashInterface {
49175078
"await",
49185079
].map(keywordEntry);
49195080

5081+
export const insideMethodInJsKeywords: ReadonlyArray<ExpectedCompletionEntryObject> = [
5082+
"break",
5083+
"case",
5084+
"catch",
5085+
"class",
5086+
"const",
5087+
"continue",
5088+
"debugger",
5089+
"default",
5090+
"delete",
5091+
"do",
5092+
"else",
5093+
"export",
5094+
"extends",
5095+
"false",
5096+
"finally",
5097+
"for",
5098+
"function",
5099+
"if",
5100+
"import",
5101+
"in",
5102+
"instanceof",
5103+
"new",
5104+
"null",
5105+
"return",
5106+
"super",
5107+
"switch",
5108+
"this",
5109+
"throw",
5110+
"true",
5111+
"try",
5112+
"typeof",
5113+
"var",
5114+
"void",
5115+
"while",
5116+
"with",
5117+
"implements",
5118+
"let",
5119+
"package",
5120+
"yield",
5121+
"async",
5122+
"await",
5123+
].map(keywordEntry);
5124+
49205125
export const globalKeywordsPlusUndefined: ReadonlyArray<ExpectedCompletionEntryObject> = (() => {
49215126
const i = ts.findIndex(globalKeywords, x => x.name === "unique");
49225127
return [...globalKeywords.slice(0, i), keywordEntry("undefined"), ...globalKeywords.slice(i)];
@@ -4929,6 +5134,13 @@ namespace FourSlashInterface {
49295134
...globalKeywords
49305135
];
49315136

5137+
export const globalsInJs: ReadonlyArray<ExpectedCompletionEntryObject> = [
5138+
{ name: "globalThis", kind: "module" },
5139+
...globalsVars,
5140+
{ name: "undefined", kind: "var" },
5141+
...globalInJsKeywords
5142+
];
5143+
49325144
export function globalsPlus(plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> {
49335145
return [
49345146
{ name: "globalThis", kind: "module" },
@@ -4937,6 +5149,15 @@ namespace FourSlashInterface {
49375149
{ name: "undefined", kind: "var" },
49385150
...globalKeywords];
49395151
}
5152+
5153+
export function globalsInJsPlus(plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> {
5154+
return [
5155+
{ name: "globalThis", kind: "module" },
5156+
...globalsVars,
5157+
...plus,
5158+
{ name: "undefined", kind: "var" },
5159+
...globalInJsKeywords];
5160+
}
49405161
}
49415162

49425163
export interface ReferenceGroup {

src/services/completions.ts

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ namespace ts.Completions {
3030
ConstructorParameterKeywords, // Keywords at constructor parameter
3131
FunctionLikeBodyKeywords, // Keywords at function like body
3232
TypeKeywords,
33+
Last = TypeKeywords
3334
}
3435

3536
const enum GlobalsSearch { Continue, Success, Fail }
@@ -77,7 +78,7 @@ namespace ts.Completions {
7778
}
7879

7980
function completionInfoFromData(sourceFile: SourceFile, typeChecker: TypeChecker, compilerOptions: CompilerOptions, log: Log, completionData: CompletionData, preferences: UserPreferences): CompletionInfo | undefined {
80-
const { symbols, completionKind, isInSnippetScope, isNewIdentifierLocation, location, propertyAccessToConvert, keywordFilters, literals, symbolToOriginInfoMap, recommendedCompletion, isJsxInitializer } = completionData;
81+
const { symbols, completionKind, isInSnippetScope, isNewIdentifierLocation, location, propertyAccessToConvert, keywordFilters, literals, symbolToOriginInfoMap, recommendedCompletion, isJsxInitializer, insideJsDocTagTypeExpression } = completionData;
8182

8283
if (location && location.parent && isJsxClosingElement(location.parent)) {
8384
// In the TypeScript JSX element, if such element is not defined. When users query for completion at closing tag,
@@ -113,7 +114,7 @@ namespace ts.Completions {
113114

114115
if (keywordFilters !== KeywordCompletionFilters.None) {
115116
const entryNames = arrayToSet(entries, e => e.name);
116-
for (const keywordEntry of getKeywordCompletions(keywordFilters)) {
117+
for (const keywordEntry of getKeywordCompletions(keywordFilters, !insideJsDocTagTypeExpression && isSourceFileJS(sourceFile))) {
117118
if (!entryNames.has(keywordEntry.name)) {
118119
entries.push(keywordEntry);
119120
}
@@ -510,6 +511,7 @@ namespace ts.Completions {
510511
readonly recommendedCompletion: Symbol | undefined;
511512
readonly previousToken: Node | undefined;
512513
readonly isJsxInitializer: IsJsxInitializer;
514+
readonly insideJsDocTagTypeExpression: boolean;
513515
}
514516
type Request = { readonly kind: CompletionDataKind.JsDocTagName | CompletionDataKind.JsDocTag } | { readonly kind: CompletionDataKind.JsDocParameterName, tag: JSDocParameterTag };
515517

@@ -837,7 +839,22 @@ namespace ts.Completions {
837839
const literals = mapDefined(contextualType && (contextualType.isUnion() ? contextualType.types : [contextualType]), t => t.isLiteral() ? t.value : undefined);
838840

839841
const recommendedCompletion = previousToken && contextualType && getRecommendedCompletion(previousToken, contextualType, typeChecker);
840-
return { kind: CompletionDataKind.Data, symbols, completionKind, isInSnippetScope, propertyAccessToConvert, isNewIdentifierLocation, location, keywordFilters, literals, symbolToOriginInfoMap, recommendedCompletion, previousToken, isJsxInitializer };
842+
return {
843+
kind: CompletionDataKind.Data,
844+
symbols,
845+
completionKind,
846+
isInSnippetScope,
847+
propertyAccessToConvert,
848+
isNewIdentifierLocation,
849+
location,
850+
keywordFilters,
851+
literals,
852+
symbolToOriginInfoMap,
853+
recommendedCompletion,
854+
previousToken,
855+
isJsxInitializer,
856+
insideJsDocTagTypeExpression
857+
};
841858

842859
type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag;
843860

@@ -1927,7 +1944,18 @@ namespace ts.Completions {
19271944
}
19281945
return res;
19291946
});
1930-
function getKeywordCompletions(keywordFilter: KeywordCompletionFilters): ReadonlyArray<CompletionEntry> {
1947+
1948+
function getKeywordCompletions(keywordFilter: KeywordCompletionFilters, filterOutTsOnlyKeywords: boolean): ReadonlyArray<CompletionEntry> {
1949+
if (!filterOutTsOnlyKeywords) return getTypescriptKeywordCompletions(keywordFilter);
1950+
1951+
const index = keywordFilter + KeywordCompletionFilters.Last + 1;
1952+
return _keywordCompletions[index] ||
1953+
(_keywordCompletions[index] = getTypescriptKeywordCompletions(keywordFilter)
1954+
.filter(entry => !isTypeScriptOnlyKeyword(stringToToken(entry.name)!))
1955+
);
1956+
}
1957+
1958+
function getTypescriptKeywordCompletions(keywordFilter: KeywordCompletionFilters): ReadonlyArray<CompletionEntry> {
19311959
return _keywordCompletions[keywordFilter] || (_keywordCompletions[keywordFilter] = allKeywordsCompletions().filter(entry => {
19321960
const kind = stringToToken(entry.name)!;
19331961
switch (keywordFilter) {
@@ -1952,6 +1980,40 @@ namespace ts.Completions {
19521980
}));
19531981
}
19541982

1983+
function isTypeScriptOnlyKeyword(kind: SyntaxKind) {
1984+
switch (kind) {
1985+
case SyntaxKind.AbstractKeyword:
1986+
case SyntaxKind.AnyKeyword:
1987+
case SyntaxKind.BigIntKeyword:
1988+
case SyntaxKind.BooleanKeyword:
1989+
case SyntaxKind.DeclareKeyword:
1990+
case SyntaxKind.EnumKeyword:
1991+
case SyntaxKind.GlobalKeyword:
1992+
case SyntaxKind.InferKeyword:
1993+
case SyntaxKind.InterfaceKeyword:
1994+
case SyntaxKind.IsKeyword:
1995+
case SyntaxKind.KeyOfKeyword:
1996+
case SyntaxKind.ModuleKeyword:
1997+
case SyntaxKind.NamespaceKeyword:
1998+
case SyntaxKind.NeverKeyword:
1999+
case SyntaxKind.NumberKeyword:
2000+
case SyntaxKind.ObjectKeyword:
2001+
case SyntaxKind.PrivateKeyword:
2002+
case SyntaxKind.ProtectedKeyword:
2003+
case SyntaxKind.PublicKeyword:
2004+
case SyntaxKind.ReadonlyKeyword:
2005+
case SyntaxKind.StaticKeyword:
2006+
case SyntaxKind.StringKeyword:
2007+
case SyntaxKind.SymbolKeyword:
2008+
case SyntaxKind.TypeKeyword:
2009+
case SyntaxKind.UniqueKeyword:
2010+
case SyntaxKind.UnknownKeyword:
2011+
return true;
2012+
default:
2013+
return false;
2014+
}
2015+
}
2016+
19552017
function isInterfaceOrTypeLiteralCompletionKeyword(kind: SyntaxKind): boolean {
19562018
return kind === SyntaxKind.ReadonlyKeyword;
19572019
}

0 commit comments

Comments
 (0)