Merge pull request #30414 from Microsoft/jsSyntaxCompletions
Filter ts only keywords from js file completion
This commit is contained in:
commit
800f7a3447
4 changed files with 165 additions and 4 deletions
|
@ -4552,9 +4552,47 @@ namespace FourSlashInterface {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getInJsKeywords(keywords: ReadonlyArray<ExpectedCompletionEntryObject>): ReadonlyArray<ExpectedCompletionEntryObject> {
|
||||||
|
return keywords.filter(keyword => {
|
||||||
|
switch (keyword.name) {
|
||||||
|
case "enum":
|
||||||
|
case "interface":
|
||||||
|
case "implements":
|
||||||
|
case "private":
|
||||||
|
case "protected":
|
||||||
|
case "public":
|
||||||
|
case "abstract":
|
||||||
|
case "any":
|
||||||
|
case "boolean":
|
||||||
|
case "declare":
|
||||||
|
case "infer":
|
||||||
|
case "is":
|
||||||
|
case "keyof":
|
||||||
|
case "module":
|
||||||
|
case "namespace":
|
||||||
|
case "never":
|
||||||
|
case "readonly":
|
||||||
|
case "number":
|
||||||
|
case "object":
|
||||||
|
case "string":
|
||||||
|
case "symbol":
|
||||||
|
case "type":
|
||||||
|
case "unique":
|
||||||
|
case "unknown":
|
||||||
|
case "global":
|
||||||
|
case "bigint":
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export const classElementKeywords: ReadonlyArray<ExpectedCompletionEntryObject> =
|
export const classElementKeywords: ReadonlyArray<ExpectedCompletionEntryObject> =
|
||||||
["private", "protected", "public", "static", "abstract", "async", "constructor", "get", "readonly", "set"].map(keywordEntry);
|
["private", "protected", "public", "static", "abstract", "async", "constructor", "get", "readonly", "set"].map(keywordEntry);
|
||||||
|
|
||||||
|
export const classElementInJsKeywords = getInJsKeywords(classElementKeywords);
|
||||||
|
|
||||||
export const constructorParameterKeywords: ReadonlyArray<ExpectedCompletionEntryObject> =
|
export const constructorParameterKeywords: ReadonlyArray<ExpectedCompletionEntryObject> =
|
||||||
["private", "protected", "public", "readonly"].map((name): ExpectedCompletionEntryObject => ({ name, kind: "keyword" }));
|
["private", "protected", "public", "readonly"].map((name): ExpectedCompletionEntryObject => ({ name, kind: "keyword" }));
|
||||||
|
|
||||||
|
@ -4692,6 +4730,8 @@ namespace FourSlashInterface {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const statementInJsKeywords = getInJsKeywords(statementKeywords);
|
||||||
|
|
||||||
export const globalsVars: ReadonlyArray<ExpectedCompletionEntryObject> = [
|
export const globalsVars: ReadonlyArray<ExpectedCompletionEntryObject> = [
|
||||||
functionEntry("eval"),
|
functionEntry("eval"),
|
||||||
functionEntry("parseInt"),
|
functionEntry("parseInt"),
|
||||||
|
@ -4793,6 +4833,18 @@ namespace FourSlashInterface {
|
||||||
...globalKeywordsInsideFunction,
|
...globalKeywordsInsideFunction,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const globalInJsKeywordsInsideFunction = getInJsKeywords(globalKeywordsInsideFunction);
|
||||||
|
|
||||||
|
// 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
|
// TODO: many of these are inappropriate to always provide
|
||||||
export const globalKeywords: ReadonlyArray<ExpectedCompletionEntryObject> = [
|
export const globalKeywords: ReadonlyArray<ExpectedCompletionEntryObject> = [
|
||||||
"break",
|
"break",
|
||||||
|
@ -4871,6 +4923,8 @@ namespace FourSlashInterface {
|
||||||
"of",
|
"of",
|
||||||
].map(keywordEntry);
|
].map(keywordEntry);
|
||||||
|
|
||||||
|
export const globalInJsKeywords = getInJsKeywords(globalKeywords);
|
||||||
|
|
||||||
export const insideMethodKeywords: ReadonlyArray<ExpectedCompletionEntryObject> = [
|
export const insideMethodKeywords: ReadonlyArray<ExpectedCompletionEntryObject> = [
|
||||||
"break",
|
"break",
|
||||||
"case",
|
"case",
|
||||||
|
@ -4917,6 +4971,8 @@ namespace FourSlashInterface {
|
||||||
"await",
|
"await",
|
||||||
].map(keywordEntry);
|
].map(keywordEntry);
|
||||||
|
|
||||||
|
export const insideMethodInJsKeywords = getInJsKeywords(insideMethodKeywords);
|
||||||
|
|
||||||
export const globalKeywordsPlusUndefined: ReadonlyArray<ExpectedCompletionEntryObject> = (() => {
|
export const globalKeywordsPlusUndefined: ReadonlyArray<ExpectedCompletionEntryObject> = (() => {
|
||||||
const i = ts.findIndex(globalKeywords, x => x.name === "unique");
|
const i = ts.findIndex(globalKeywords, x => x.name === "unique");
|
||||||
return [...globalKeywords.slice(0, i), keywordEntry("undefined"), ...globalKeywords.slice(i)];
|
return [...globalKeywords.slice(0, i), keywordEntry("undefined"), ...globalKeywords.slice(i)];
|
||||||
|
@ -4929,6 +4985,13 @@ namespace FourSlashInterface {
|
||||||
...globalKeywords
|
...globalKeywords
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const globalsInJs: ReadonlyArray<ExpectedCompletionEntryObject> = [
|
||||||
|
{ name: "globalThis", kind: "module" },
|
||||||
|
...globalsVars,
|
||||||
|
{ name: "undefined", kind: "var" },
|
||||||
|
...globalInJsKeywords
|
||||||
|
];
|
||||||
|
|
||||||
export function globalsPlus(plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> {
|
export function globalsPlus(plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> {
|
||||||
return [
|
return [
|
||||||
{ name: "globalThis", kind: "module" },
|
{ name: "globalThis", kind: "module" },
|
||||||
|
@ -4937,6 +5000,15 @@ namespace FourSlashInterface {
|
||||||
{ name: "undefined", kind: "var" },
|
{ name: "undefined", kind: "var" },
|
||||||
...globalKeywords];
|
...globalKeywords];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function globalsInJsPlus(plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> {
|
||||||
|
return [
|
||||||
|
{ name: "globalThis", kind: "module" },
|
||||||
|
...globalsVars,
|
||||||
|
...plus,
|
||||||
|
{ name: "undefined", kind: "var" },
|
||||||
|
...globalInJsKeywords];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ReferenceGroup {
|
export interface ReferenceGroup {
|
||||||
|
|
|
@ -30,6 +30,7 @@ namespace ts.Completions {
|
||||||
ConstructorParameterKeywords, // Keywords at constructor parameter
|
ConstructorParameterKeywords, // Keywords at constructor parameter
|
||||||
FunctionLikeBodyKeywords, // Keywords at function like body
|
FunctionLikeBodyKeywords, // Keywords at function like body
|
||||||
TypeKeywords,
|
TypeKeywords,
|
||||||
|
Last = TypeKeywords
|
||||||
}
|
}
|
||||||
|
|
||||||
const enum GlobalsSearch { Continue, Success, Fail }
|
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 {
|
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)) {
|
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,
|
// 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) {
|
if (keywordFilters !== KeywordCompletionFilters.None) {
|
||||||
const entryNames = arrayToSet(entries, e => e.name);
|
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)) {
|
if (!entryNames.has(keywordEntry.name)) {
|
||||||
entries.push(keywordEntry);
|
entries.push(keywordEntry);
|
||||||
}
|
}
|
||||||
|
@ -510,6 +511,7 @@ namespace ts.Completions {
|
||||||
readonly recommendedCompletion: Symbol | undefined;
|
readonly recommendedCompletion: Symbol | undefined;
|
||||||
readonly previousToken: Node | undefined;
|
readonly previousToken: Node | undefined;
|
||||||
readonly isJsxInitializer: IsJsxInitializer;
|
readonly isJsxInitializer: IsJsxInitializer;
|
||||||
|
readonly insideJsDocTagTypeExpression: boolean;
|
||||||
}
|
}
|
||||||
type Request = { readonly kind: CompletionDataKind.JsDocTagName | CompletionDataKind.JsDocTag } | { readonly kind: CompletionDataKind.JsDocParameterName, tag: JSDocParameterTag };
|
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 literals = mapDefined(contextualType && (contextualType.isUnion() ? contextualType.types : [contextualType]), t => t.isLiteral() ? t.value : undefined);
|
||||||
|
|
||||||
const recommendedCompletion = previousToken && contextualType && getRecommendedCompletion(previousToken, contextualType, typeChecker);
|
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;
|
type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag;
|
||||||
|
|
||||||
|
@ -1929,7 +1946,18 @@ namespace ts.Completions {
|
||||||
}
|
}
|
||||||
return res;
|
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 => {
|
return _keywordCompletions[keywordFilter] || (_keywordCompletions[keywordFilter] = allKeywordsCompletions().filter(entry => {
|
||||||
const kind = stringToToken(entry.name)!;
|
const kind = stringToToken(entry.name)!;
|
||||||
switch (keywordFilter) {
|
switch (keywordFilter) {
|
||||||
|
@ -1954,6 +1982,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.ImplementsKeyword:
|
||||||
|
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.StringKeyword:
|
||||||
|
case SyntaxKind.SymbolKeyword:
|
||||||
|
case SyntaxKind.TypeKeyword:
|
||||||
|
case SyntaxKind.UniqueKeyword:
|
||||||
|
case SyntaxKind.UnknownKeyword:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function isInterfaceOrTypeLiteralCompletionKeyword(kind: SyntaxKind): boolean {
|
function isInterfaceOrTypeLiteralCompletionKeyword(kind: SyntaxKind): boolean {
|
||||||
return kind === SyntaxKind.ReadonlyKeyword;
|
return kind === SyntaxKind.ReadonlyKeyword;
|
||||||
}
|
}
|
||||||
|
|
20
tests/cases/fourslash/completionEntryInJsFile.ts
Normal file
20
tests/cases/fourslash/completionEntryInJsFile.ts
Normal 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"]) },
|
||||||
|
);
|
|
@ -650,22 +650,29 @@ declare var classification: typeof FourSlashInterface.classification;
|
||||||
declare namespace completion {
|
declare namespace completion {
|
||||||
type Entry = FourSlashInterface.ExpectedCompletionEntryObject;
|
type Entry = FourSlashInterface.ExpectedCompletionEntryObject;
|
||||||
export const globals: ReadonlyArray<Entry>;
|
export const globals: ReadonlyArray<Entry>;
|
||||||
|
export const globalsInJs: ReadonlyArray<Entry>;
|
||||||
export const globalKeywords: ReadonlyArray<Entry>;
|
export const globalKeywords: ReadonlyArray<Entry>;
|
||||||
|
export const globalInJsKeywords: ReadonlyArray<Entry>;
|
||||||
export const insideMethodKeywords: ReadonlyArray<Entry>;
|
export const insideMethodKeywords: ReadonlyArray<Entry>;
|
||||||
|
export const insideMethodInJsKeywords: ReadonlyArray<Entry>;
|
||||||
export const globalKeywordsPlusUndefined: ReadonlyArray<Entry>;
|
export const globalKeywordsPlusUndefined: ReadonlyArray<Entry>;
|
||||||
export const globalsVars: ReadonlyArray<Entry>;
|
export const globalsVars: ReadonlyArray<Entry>;
|
||||||
export function globalsInsideFunction(plus: ReadonlyArray<Entry>): 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 globalsPlus(plus: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>): ReadonlyArray<Entry>;
|
||||||
|
export function globalsInJsPlus(plus: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>): ReadonlyArray<Entry>;
|
||||||
export const keywordsWithUndefined: ReadonlyArray<Entry>;
|
export const keywordsWithUndefined: ReadonlyArray<Entry>;
|
||||||
export const keywords: ReadonlyArray<Entry>;
|
export const keywords: ReadonlyArray<Entry>;
|
||||||
export const typeKeywords: ReadonlyArray<Entry>;
|
export const typeKeywords: ReadonlyArray<Entry>;
|
||||||
export const globalTypes: ReadonlyArray<Entry>;
|
export const globalTypes: ReadonlyArray<Entry>;
|
||||||
export function globalTypesPlus(plus: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>): ReadonlyArray<Entry>;
|
export function globalTypesPlus(plus: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>): ReadonlyArray<Entry>;
|
||||||
export const classElementKeywords: ReadonlyArray<Entry>;
|
export const classElementKeywords: ReadonlyArray<Entry>;
|
||||||
|
export const classElementInJsKeywords: ReadonlyArray<Entry>;
|
||||||
export const constructorParameterKeywords: ReadonlyArray<Entry>;
|
export const constructorParameterKeywords: ReadonlyArray<Entry>;
|
||||||
export const functionMembers: ReadonlyArray<Entry>;
|
export const functionMembers: ReadonlyArray<Entry>;
|
||||||
export const stringMembers: ReadonlyArray<Entry>;
|
export const stringMembers: ReadonlyArray<Entry>;
|
||||||
export const functionMembersWithPrototype: ReadonlyArray<Entry>;
|
export const functionMembersWithPrototype: ReadonlyArray<Entry>;
|
||||||
export const statementKeywordsWithTypes: ReadonlyArray<Entry>;
|
export const statementKeywordsWithTypes: ReadonlyArray<Entry>;
|
||||||
export const statementKeywords: ReadonlyArray<Entry>;
|
export const statementKeywords: ReadonlyArray<Entry>;
|
||||||
|
export const statementInJsKeywords: ReadonlyArray<Entry>;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue