mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-07 05:41:22 -06:00
Demote priority of JS completions (#49716)
* Demote priority of JS completions Fixes #48498 Unchecked JS files gather identifier-based completions. Currently, this search happens instead of `getCompletionEntriesFromSymbols` for TS/checked JS files. However, identifier-based completions are much lower quality and can be ignored by some editors. Identifier-based completions should be gathered last, after gathering other completions. That's what this PR does. * Invert isUncheckedFile to avoid double negative * dedupe calls to getCompletionEntriesFromSymbols * Stop re-creating list of entry names * more deduping + fix lint
This commit is contained in:
parent
63d0574321
commit
cba184f69b
@ -507,87 +507,61 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
const entries = createSortedArray<CompletionEntry>();
|
||||
|
||||
if (isUncheckedFile(sourceFile, compilerOptions)) {
|
||||
const uniqueNames = getCompletionEntriesFromSymbols(
|
||||
symbols,
|
||||
entries,
|
||||
/*replacementToken*/ undefined,
|
||||
contextToken,
|
||||
location,
|
||||
sourceFile,
|
||||
host,
|
||||
program,
|
||||
getEmitScriptTarget(compilerOptions),
|
||||
log,
|
||||
completionKind,
|
||||
preferences,
|
||||
compilerOptions,
|
||||
formatContext,
|
||||
isTypeOnlyLocation,
|
||||
propertyAccessToConvert,
|
||||
isJsxIdentifierExpected,
|
||||
isJsxInitializer,
|
||||
importCompletionNode,
|
||||
recommendedCompletion,
|
||||
symbolToOriginInfoMap,
|
||||
symbolToSortTextMap,
|
||||
isJsxIdentifierExpected,
|
||||
isRightOfOpenTag,
|
||||
);
|
||||
getJSCompletionEntries(sourceFile, location.pos, uniqueNames, getEmitScriptTarget(compilerOptions), entries);
|
||||
}
|
||||
else {
|
||||
if (!isNewIdentifierLocation && (!symbols || symbols.length === 0) && keywordFilters === KeywordCompletionFilters.None) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getCompletionEntriesFromSymbols(
|
||||
symbols,
|
||||
entries,
|
||||
/*replacementToken*/ undefined,
|
||||
contextToken,
|
||||
location,
|
||||
sourceFile,
|
||||
host,
|
||||
program,
|
||||
getEmitScriptTarget(compilerOptions),
|
||||
log,
|
||||
completionKind,
|
||||
preferences,
|
||||
compilerOptions,
|
||||
formatContext,
|
||||
isTypeOnlyLocation,
|
||||
propertyAccessToConvert,
|
||||
isJsxIdentifierExpected,
|
||||
isJsxInitializer,
|
||||
importCompletionNode,
|
||||
recommendedCompletion,
|
||||
symbolToOriginInfoMap,
|
||||
symbolToSortTextMap,
|
||||
isJsxIdentifierExpected,
|
||||
isRightOfOpenTag,
|
||||
);
|
||||
const isChecked = isCheckedFile(sourceFile, compilerOptions);
|
||||
if (isChecked && !isNewIdentifierLocation && (!symbols || symbols.length === 0) && keywordFilters === KeywordCompletionFilters.None) {
|
||||
return undefined;
|
||||
}
|
||||
const uniqueNames = getCompletionEntriesFromSymbols(
|
||||
symbols,
|
||||
entries,
|
||||
/*replacementToken*/ undefined,
|
||||
contextToken,
|
||||
location,
|
||||
sourceFile,
|
||||
host,
|
||||
program,
|
||||
getEmitScriptTarget(compilerOptions),
|
||||
log,
|
||||
completionKind,
|
||||
preferences,
|
||||
compilerOptions,
|
||||
formatContext,
|
||||
isTypeOnlyLocation,
|
||||
propertyAccessToConvert,
|
||||
isJsxIdentifierExpected,
|
||||
isJsxInitializer,
|
||||
importCompletionNode,
|
||||
recommendedCompletion,
|
||||
symbolToOriginInfoMap,
|
||||
symbolToSortTextMap,
|
||||
isJsxIdentifierExpected,
|
||||
isRightOfOpenTag,
|
||||
);
|
||||
|
||||
if (keywordFilters !== KeywordCompletionFilters.None) {
|
||||
const entryNames = new Set(entries.map(e => e.name));
|
||||
for (const keywordEntry of getKeywordCompletions(keywordFilters, !insideJsDocTagTypeExpression && isSourceFileJS(sourceFile))) {
|
||||
if (isTypeOnlyLocation && isTypeKeyword(stringToToken(keywordEntry.name)!) || !entryNames.has(keywordEntry.name)) {
|
||||
if (isTypeOnlyLocation && isTypeKeyword(stringToToken(keywordEntry.name)!) || !uniqueNames.has(keywordEntry.name)) {
|
||||
uniqueNames.add(keywordEntry.name);
|
||||
insertSorted(entries, keywordEntry, compareCompletionEntries, /*allowDuplicates*/ true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const entryNames = new Set(entries.map(e => e.name));
|
||||
for (const keywordEntry of getContextualKeywords(contextToken, position)) {
|
||||
if (!entryNames.has(keywordEntry.name)) {
|
||||
if (!uniqueNames.has(keywordEntry.name)) {
|
||||
uniqueNames.add(keywordEntry.name);
|
||||
insertSorted(entries, keywordEntry, compareCompletionEntries, /*allowDuplicates*/ true);
|
||||
}
|
||||
}
|
||||
|
||||
for (const literal of literals) {
|
||||
insertSorted(entries, createCompletionEntryForLiteral(sourceFile, preferences, literal), compareCompletionEntries, /*allowDuplicates*/ true);
|
||||
const literalEntry = createCompletionEntryForLiteral(sourceFile, preferences, literal);
|
||||
uniqueNames.add(literalEntry.name);
|
||||
insertSorted(entries, literalEntry, compareCompletionEntries, /*allowDuplicates*/ true);
|
||||
}
|
||||
|
||||
if (!isChecked) {
|
||||
getJSCompletionEntries(sourceFile, location.pos, uniqueNames, getEmitScriptTarget(compilerOptions), entries);
|
||||
}
|
||||
|
||||
return {
|
||||
@ -601,8 +575,8 @@ namespace ts.Completions {
|
||||
};
|
||||
}
|
||||
|
||||
function isUncheckedFile(sourceFile: SourceFile, compilerOptions: CompilerOptions): boolean {
|
||||
return isSourceFileJS(sourceFile) && !isCheckJsEnabledForFile(sourceFile, compilerOptions);
|
||||
function isCheckedFile(sourceFile: SourceFile, compilerOptions: CompilerOptions): boolean {
|
||||
return !isSourceFileJS(sourceFile) || !!isCheckJsEnabledForFile(sourceFile, compilerOptions);
|
||||
}
|
||||
|
||||
function isMemberCompletionKind(kind: CompletionKind): boolean {
|
||||
@ -1942,7 +1916,7 @@ namespace ts.Completions {
|
||||
cancellationToken?: CancellationToken,
|
||||
): CompletionData | Request | undefined {
|
||||
const typeChecker = program.getTypeChecker();
|
||||
const inUncheckedFile = isUncheckedFile(sourceFile, compilerOptions);
|
||||
const inCheckedFile = isCheckedFile(sourceFile, compilerOptions);
|
||||
let start = timestamp();
|
||||
let currentToken = getTokenAtPosition(sourceFile, position); // TODO: GH#15853
|
||||
// We will check for jsdoc comments with insideComment and getJsDocTagAtPosition. (TODO: that seems rather inefficient to check the same thing so many times.)
|
||||
@ -2396,7 +2370,14 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
const propertyAccess = node.kind === SyntaxKind.ImportType ? node as ImportTypeNode : node.parent as PropertyAccessExpression | QualifiedName;
|
||||
if (inUncheckedFile) {
|
||||
if (inCheckedFile) {
|
||||
for (const symbol of type.getApparentProperties()) {
|
||||
if (typeChecker.isValidPropertyAccessForCompletions(propertyAccess, type, symbol)) {
|
||||
addPropertySymbol(symbol, /* insertAwait */ false, insertQuestionDot);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// In javascript files, for union types, we don't just get the members that
|
||||
// the individual types have in common, we also include all the members that
|
||||
// each individual type has. This is because we're going to add all identifiers
|
||||
@ -2404,13 +2385,6 @@ namespace ts.Completions {
|
||||
// of the individual types to a higher status since we know what they are.
|
||||
symbols.push(...filter(getPropertiesForCompletion(type, typeChecker), s => typeChecker.isValidPropertyAccessForCompletions(propertyAccess, type, s)));
|
||||
}
|
||||
else {
|
||||
for (const symbol of type.getApparentProperties()) {
|
||||
if (typeChecker.isValidPropertyAccessForCompletions(propertyAccess, type, symbol)) {
|
||||
addPropertySymbol(symbol, /* insertAwait */ false, insertQuestionDot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (insertAwait && preferences.includeCompletionsWithInsertText) {
|
||||
const promiseType = typeChecker.getPromisedTypeOfPromise(type);
|
||||
|
||||
@ -5,4 +5,4 @@
|
||||
/////** @type {function (new: string, string): string} */
|
||||
////var f = function () { return new/**/; }
|
||||
|
||||
verify.completions({ marker: "", includes: { name: "new", sortText: completion.SortText.JavascriptIdentifiers } });
|
||||
verify.completions({ marker: "", includes: { name: "new", sortText: completion.SortText.GlobalsOrKeywords } });
|
||||
|
||||
@ -4,4 +4,4 @@
|
||||
/////** @type {function (this: string, string): string} */
|
||||
////var f = function (s) { return this/**/; }
|
||||
|
||||
verify.completions({ marker: "", includes: { name: "this", sortText: completion.SortText.JavascriptIdentifiers } });
|
||||
verify.completions({ marker: "", includes: { name: "this", sortText: completion.SortText.GlobalsOrKeywords } });
|
||||
|
||||
20
tests/cases/fourslash/completionListClassThisJS.ts
Normal file
20
tests/cases/fourslash/completionListClassThisJS.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
// @Filename: completionListClassThisJS.js
|
||||
// @allowJs: true
|
||||
/////** @typedef {number} CallbackContext */
|
||||
////class Foo {
|
||||
//// bar() {
|
||||
//// this/**/
|
||||
//// }
|
||||
//// /** @param {function (this: CallbackContext): any} cb */
|
||||
//// baz(cb) {
|
||||
//// }
|
||||
////}
|
||||
|
||||
verify.completions({ marker: "", isNewIdentifierLocation: false, includes: [{
|
||||
name: "this",
|
||||
// kind: "warning",
|
||||
kind: "keyword",
|
||||
sortText: completion.SortText.GlobalsOrKeywords,
|
||||
// sortText: completion.SortText.JavascriptIdentifiers,
|
||||
}] });
|
||||
Loading…
x
Reference in New Issue
Block a user