Filter ts only keywords from js file completion

Fixes #29212
This commit is contained in:
Sheetal Nandi 2019-03-14 13:58:57 -07:00
parent da48790dac
commit f829f958a2
4 changed files with 314 additions and 4 deletions

View file

@ -4555,6 +4555,9 @@ namespace FourSlashInterface {
export const classElementKeywords: ReadonlyArray<ExpectedCompletionEntryObject> =
["private", "protected", "public", "static", "abstract", "async", "constructor", "get", "readonly", "set"].map(keywordEntry);
export const classElementInJsKeywords: ReadonlyArray<ExpectedCompletionEntryObject> =
["async", "constructor", "get", "set"].map(keywordEntry);
export const constructorParameterKeywords: ReadonlyArray<ExpectedCompletionEntryObject> =
["private", "protected", "public", "readonly"].map((name): ExpectedCompletionEntryObject => ({ name, kind: "keyword" }));
@ -4692,6 +4695,59 @@ namespace FourSlashInterface {
}
});
export const statementInJsKeywords: ReadonlyArray<ExpectedCompletionEntryObject> = [
"break",
"case",
"catch",
"class",
"const",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"enum",
"export",
"extends",
"false",
"finally",
"for",
"function",
"if",
"import",
"in",
"instanceof",
"new",
"null",
"return",
"super",
"switch",
"this",
"throw",
"true",
"try",
"typeof",
"var",
"void",
"while",
"with",
"implements",
"interface",
"let",
"package",
"yield",
"as",
"async",
"await",
"constructor",
"get",
"require",
"set",
"from",
"of",
].map(keywordEntry);
export const globalsVars: ReadonlyArray<ExpectedCompletionEntryObject> = [
functionEntry("eval"),
functionEntry("parseInt"),
@ -4793,6 +4849,60 @@ namespace FourSlashInterface {
...globalKeywordsInsideFunction,
];
const globalInJsKeywordsInsideFunction: ReadonlyArray<ExpectedCompletionEntryObject> = [
"break",
"case",
"catch",
"class",
"const",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"export",
"extends",
"false",
"finally",
"for",
"function",
"if",
"import",
"in",
"instanceof",
"new",
"null",
"return",
"super",
"switch",
"this",
"throw",
"true",
"try",
"typeof",
"var",
"void",
"while",
"with",
"implements",
"let",
"package",
"yield",
"async",
"await",
].map(keywordEntry);
// TODO: many of these are inappropriate to always provide
export const globalsInJsInsideFunction = (plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> => [
{ name: "arguments", kind: "local var" },
{ name: "globalThis", kind: "module" },
...globalsVars,
...plus,
{ name: "undefined", kind: "var" },
...globalInJsKeywordsInsideFunction,
];
// TODO: many of these are inappropriate to always provide
export const globalKeywords: ReadonlyArray<ExpectedCompletionEntryObject> = [
"break",
@ -4871,6 +4981,57 @@ namespace FourSlashInterface {
"of",
].map(keywordEntry);
export const globalInJsKeywords: ReadonlyArray<ExpectedCompletionEntryObject> = [
"break",
"case",
"catch",
"class",
"const",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"export",
"extends",
"false",
"finally",
"for",
"function",
"if",
"import",
"in",
"instanceof",
"new",
"null",
"return",
"super",
"switch",
"this",
"throw",
"true",
"try",
"typeof",
"var",
"void",
"while",
"with",
"implements",
"let",
"package",
"yield",
"as",
"async",
"await",
"constructor",
"get",
"require",
"set",
"from",
"of",
].map(keywordEntry);
export const insideMethodKeywords: ReadonlyArray<ExpectedCompletionEntryObject> = [
"break",
"case",
@ -4917,6 +5078,50 @@ namespace FourSlashInterface {
"await",
].map(keywordEntry);
export const insideMethodInJsKeywords: ReadonlyArray<ExpectedCompletionEntryObject> = [
"break",
"case",
"catch",
"class",
"const",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"export",
"extends",
"false",
"finally",
"for",
"function",
"if",
"import",
"in",
"instanceof",
"new",
"null",
"return",
"super",
"switch",
"this",
"throw",
"true",
"try",
"typeof",
"var",
"void",
"while",
"with",
"implements",
"let",
"package",
"yield",
"async",
"await",
].map(keywordEntry);
export const globalKeywordsPlusUndefined: ReadonlyArray<ExpectedCompletionEntryObject> = (() => {
const i = ts.findIndex(globalKeywords, x => x.name === "unique");
return [...globalKeywords.slice(0, i), keywordEntry("undefined"), ...globalKeywords.slice(i)];
@ -4929,6 +5134,13 @@ namespace FourSlashInterface {
...globalKeywords
];
export const globalsInJs: ReadonlyArray<ExpectedCompletionEntryObject> = [
{ name: "globalThis", kind: "module" },
...globalsVars,
{ name: "undefined", kind: "var" },
...globalInJsKeywords
];
export function globalsPlus(plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> {
return [
{ name: "globalThis", kind: "module" },
@ -4937,6 +5149,15 @@ namespace FourSlashInterface {
{ name: "undefined", kind: "var" },
...globalKeywords];
}
export function globalsInJsPlus(plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> {
return [
{ name: "globalThis", kind: "module" },
...globalsVars,
...plus,
{ name: "undefined", kind: "var" },
...globalInJsKeywords];
}
}
export interface ReferenceGroup {

View file

@ -30,6 +30,7 @@ namespace ts.Completions {
ConstructorParameterKeywords, // Keywords at constructor parameter
FunctionLikeBodyKeywords, // Keywords at function like body
TypeKeywords,
Last = TypeKeywords
}
const enum GlobalsSearch { Continue, Success, Fail }
@ -77,7 +78,7 @@ namespace ts.Completions {
}
function completionInfoFromData(sourceFile: SourceFile, typeChecker: TypeChecker, compilerOptions: CompilerOptions, log: Log, completionData: CompletionData, preferences: UserPreferences): CompletionInfo | undefined {
const { symbols, completionKind, isInSnippetScope, isNewIdentifierLocation, location, propertyAccessToConvert, keywordFilters, literals, symbolToOriginInfoMap, recommendedCompletion, isJsxInitializer } = completionData;
const { symbols, completionKind, isInSnippetScope, isNewIdentifierLocation, location, propertyAccessToConvert, keywordFilters, literals, symbolToOriginInfoMap, recommendedCompletion, isJsxInitializer, insideJsDocTagTypeExpression } = completionData;
if (location && location.parent && isJsxClosingElement(location.parent)) {
// 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 {
if (keywordFilters !== KeywordCompletionFilters.None) {
const entryNames = arrayToSet(entries, e => e.name);
for (const keywordEntry of getKeywordCompletions(keywordFilters)) {
for (const keywordEntry of getKeywordCompletions(keywordFilters, !insideJsDocTagTypeExpression && isSourceFileJS(sourceFile))) {
if (!entryNames.has(keywordEntry.name)) {
entries.push(keywordEntry);
}
@ -510,6 +511,7 @@ namespace ts.Completions {
readonly recommendedCompletion: Symbol | undefined;
readonly previousToken: Node | undefined;
readonly isJsxInitializer: IsJsxInitializer;
readonly insideJsDocTagTypeExpression: boolean;
}
type Request = { readonly kind: CompletionDataKind.JsDocTagName | CompletionDataKind.JsDocTag } | { readonly kind: CompletionDataKind.JsDocParameterName, tag: JSDocParameterTag };
@ -837,7 +839,22 @@ namespace ts.Completions {
const literals = mapDefined(contextualType && (contextualType.isUnion() ? contextualType.types : [contextualType]), t => t.isLiteral() ? t.value : undefined);
const recommendedCompletion = previousToken && contextualType && getRecommendedCompletion(previousToken, contextualType, typeChecker);
return { kind: CompletionDataKind.Data, symbols, completionKind, isInSnippetScope, propertyAccessToConvert, isNewIdentifierLocation, location, keywordFilters, literals, symbolToOriginInfoMap, recommendedCompletion, previousToken, isJsxInitializer };
return {
kind: CompletionDataKind.Data,
symbols,
completionKind,
isInSnippetScope,
propertyAccessToConvert,
isNewIdentifierLocation,
location,
keywordFilters,
literals,
symbolToOriginInfoMap,
recommendedCompletion,
previousToken,
isJsxInitializer,
insideJsDocTagTypeExpression
};
type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag;
@ -1927,7 +1944,18 @@ namespace ts.Completions {
}
return res;
});
function getKeywordCompletions(keywordFilter: KeywordCompletionFilters): ReadonlyArray<CompletionEntry> {
function getKeywordCompletions(keywordFilter: KeywordCompletionFilters, filterOutTsOnlyKeywords: boolean): ReadonlyArray<CompletionEntry> {
if (!filterOutTsOnlyKeywords) return getTypescriptKeywordCompletions(keywordFilter);
const index = keywordFilter + KeywordCompletionFilters.Last + 1;
return _keywordCompletions[index] ||
(_keywordCompletions[index] = getTypescriptKeywordCompletions(keywordFilter)
.filter(entry => !isTypeScriptOnlyKeyword(stringToToken(entry.name)!))
);
}
function getTypescriptKeywordCompletions(keywordFilter: KeywordCompletionFilters): ReadonlyArray<CompletionEntry> {
return _keywordCompletions[keywordFilter] || (_keywordCompletions[keywordFilter] = allKeywordsCompletions().filter(entry => {
const kind = stringToToken(entry.name)!;
switch (keywordFilter) {
@ -1952,6 +1980,40 @@ namespace ts.Completions {
}));
}
function isTypeScriptOnlyKeyword(kind: SyntaxKind) {
switch (kind) {
case SyntaxKind.AbstractKeyword:
case SyntaxKind.AnyKeyword:
case SyntaxKind.BigIntKeyword:
case SyntaxKind.BooleanKeyword:
case SyntaxKind.DeclareKeyword:
case SyntaxKind.EnumKeyword:
case SyntaxKind.GlobalKeyword:
case SyntaxKind.InferKeyword:
case SyntaxKind.InterfaceKeyword:
case SyntaxKind.IsKeyword:
case SyntaxKind.KeyOfKeyword:
case SyntaxKind.ModuleKeyword:
case SyntaxKind.NamespaceKeyword:
case SyntaxKind.NeverKeyword:
case SyntaxKind.NumberKeyword:
case SyntaxKind.ObjectKeyword:
case SyntaxKind.PrivateKeyword:
case SyntaxKind.ProtectedKeyword:
case SyntaxKind.PublicKeyword:
case SyntaxKind.ReadonlyKeyword:
case SyntaxKind.StaticKeyword:
case SyntaxKind.StringKeyword:
case SyntaxKind.SymbolKeyword:
case SyntaxKind.TypeKeyword:
case SyntaxKind.UniqueKeyword:
case SyntaxKind.UnknownKeyword:
return true;
default:
return false;
}
}
function isInterfaceOrTypeLiteralCompletionKeyword(kind: SyntaxKind): boolean {
return kind === SyntaxKind.ReadonlyKeyword;
}

View file

@ -0,0 +1,20 @@
///<reference path="fourslash.ts" />
// @allowJs: true
// @Filename: /Foo.js
//// /*global*/
////class classA {
//// /*class*/
////}
////class Test7 {
//// constructor(/*constructorParameter*/){}
////}
////function foo() {
/////*insideFunction*/
////}
verify.completions(
{ marker: "global", exact: completion.globalsInJsPlus(["foo", "classA", "Test7"]) },
{ marker: "class", isNewIdentifierLocation: true, exact: ["classA", "Test7", "foo", ...completion.classElementInJsKeywords] },
{ marker: "constructorParameter", isNewIdentifierLocation: true, exact: ["classA", "Test7", "foo"] },
{ marker: "insideFunction", exact: completion.globalsInJsInsideFunction(["foo", "classA", "Test7"]) },
);

View file

@ -650,22 +650,29 @@ declare var classification: typeof FourSlashInterface.classification;
declare namespace completion {
type Entry = FourSlashInterface.ExpectedCompletionEntryObject;
export const globals: ReadonlyArray<Entry>;
export const globalsInJs: ReadonlyArray<Entry>;
export const globalKeywords: ReadonlyArray<Entry>;
export const globalInJsKeywords: ReadonlyArray<Entry>;
export const insideMethodKeywords: ReadonlyArray<Entry>;
export const insideMethodInJsKeywords: ReadonlyArray<Entry>;
export const globalKeywordsPlusUndefined: ReadonlyArray<Entry>;
export const globalsVars: ReadonlyArray<Entry>;
export function globalsInsideFunction(plus: ReadonlyArray<Entry>): ReadonlyArray<Entry>;
export function globalsInJsInsideFunction(plus: ReadonlyArray<Entry>): ReadonlyArray<Entry>;
export function globalsPlus(plus: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>): ReadonlyArray<Entry>;
export function globalsInJsPlus(plus: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>): ReadonlyArray<Entry>;
export const keywordsWithUndefined: ReadonlyArray<Entry>;
export const keywords: ReadonlyArray<Entry>;
export const typeKeywords: ReadonlyArray<Entry>;
export const globalTypes: ReadonlyArray<Entry>;
export function globalTypesPlus(plus: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>): ReadonlyArray<Entry>;
export const classElementKeywords: ReadonlyArray<Entry>;
export const classElementInJsKeywords: ReadonlyArray<Entry>;
export const constructorParameterKeywords: ReadonlyArray<Entry>;
export const functionMembers: ReadonlyArray<Entry>;
export const stringMembers: ReadonlyArray<Entry>;
export const functionMembersWithPrototype: ReadonlyArray<Entry>;
export const statementKeywordsWithTypes: ReadonlyArray<Entry>;
export const statementKeywords: ReadonlyArray<Entry>;
export const statementInJsKeywords: ReadonlyArray<Entry>;
}