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> =
|
||||
["private", "protected", "public", "static", "abstract", "async", "constructor", "get", "readonly", "set"].map(keywordEntry);
|
||||
|
||||
export const classElementInJsKeywords = getInJsKeywords(classElementKeywords);
|
||||
|
||||
export const constructorParameterKeywords: ReadonlyArray<ExpectedCompletionEntryObject> =
|
||||
["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> = [
|
||||
functionEntry("eval"),
|
||||
functionEntry("parseInt"),
|
||||
|
@ -4793,6 +4833,18 @@ namespace FourSlashInterface {
|
|||
...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
|
||||
export const globalKeywords: ReadonlyArray<ExpectedCompletionEntryObject> = [
|
||||
"break",
|
||||
|
@ -4871,6 +4923,8 @@ namespace FourSlashInterface {
|
|||
"of",
|
||||
].map(keywordEntry);
|
||||
|
||||
export const globalInJsKeywords = getInJsKeywords(globalKeywords);
|
||||
|
||||
export const insideMethodKeywords: ReadonlyArray<ExpectedCompletionEntryObject> = [
|
||||
"break",
|
||||
"case",
|
||||
|
@ -4917,6 +4971,8 @@ namespace FourSlashInterface {
|
|||
"await",
|
||||
].map(keywordEntry);
|
||||
|
||||
export const insideMethodInJsKeywords = getInJsKeywords(insideMethodKeywords);
|
||||
|
||||
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 +4985,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 +5000,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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -1929,7 +1946,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) {
|
||||
|
@ -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 {
|
||||
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 {
|
||||
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>;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue