mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-03-16 06:28:12 -05:00
Support a 'recommended' completion entry (#20020)
* Support a 'recommended' completion entry * Code review * Restore duplicate comments
This commit is contained in:
@@ -44,7 +44,7 @@ namespace ts.Completions {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const { symbols, isGlobalCompletion, isMemberCompletion, allowStringLiteral, isNewIdentifierLocation, location, request, keywordFilters, symbolToOriginInfoMap } = completionData;
|
||||
const { symbols, isGlobalCompletion, isMemberCompletion, allowStringLiteral, isNewIdentifierLocation, location, request, keywordFilters, symbolToOriginInfoMap, recommendedCompletion } = completionData;
|
||||
|
||||
if (sourceFile.languageVariant === LanguageVariant.JSX &&
|
||||
location && location.parent && location.parent.kind === SyntaxKind.JsxClosingElement) {
|
||||
@@ -76,7 +76,7 @@ namespace ts.Completions {
|
||||
const entries: CompletionEntry[] = [];
|
||||
|
||||
if (isSourceFileJavaScript(sourceFile)) {
|
||||
const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true, typeChecker, compilerOptions.target, log, allowStringLiteral, symbolToOriginInfoMap);
|
||||
const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true, typeChecker, compilerOptions.target, log, allowStringLiteral, recommendedCompletion, symbolToOriginInfoMap);
|
||||
getJavaScriptCompletionEntries(sourceFile, location.pos, uniqueNames, compilerOptions.target, entries);
|
||||
}
|
||||
else {
|
||||
@@ -84,7 +84,7 @@ namespace ts.Completions {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true, typeChecker, compilerOptions.target, log, allowStringLiteral, symbolToOriginInfoMap);
|
||||
getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true, typeChecker, compilerOptions.target, log, allowStringLiteral, recommendedCompletion, symbolToOriginInfoMap);
|
||||
}
|
||||
|
||||
// TODO add filter for keyword based on type/value/namespace and also location
|
||||
@@ -137,6 +137,7 @@ namespace ts.Completions {
|
||||
target: ScriptTarget,
|
||||
allowStringLiteral: boolean,
|
||||
origin: SymbolOriginInfo | undefined,
|
||||
recommendedCompletion: Symbol | undefined,
|
||||
): CompletionEntry | undefined {
|
||||
// Try to get a valid display name for this symbol, if we could not find one, then ignore it.
|
||||
// We would like to only show things that can be added after a dot, so for instance numeric properties can
|
||||
@@ -160,10 +161,20 @@ namespace ts.Completions {
|
||||
kindModifiers: SymbolDisplay.getSymbolModifiers(symbol),
|
||||
sortText: "0",
|
||||
source: getSourceFromOrigin(origin),
|
||||
hasAction: origin === undefined ? undefined : true,
|
||||
hasAction: trueOrUndefined(origin !== undefined),
|
||||
isRecommended: trueOrUndefined(isRecommendedCompletionMatch(symbol, recommendedCompletion, typeChecker)),
|
||||
};
|
||||
}
|
||||
|
||||
function isRecommendedCompletionMatch(localSymbol: Symbol, recommendedCompletion: Symbol, checker: TypeChecker): boolean {
|
||||
return localSymbol === recommendedCompletion ||
|
||||
!!(localSymbol.flags & SymbolFlags.ExportValue) && checker.getExportSymbolOfSymbol(localSymbol) === recommendedCompletion;
|
||||
}
|
||||
|
||||
function trueOrUndefined(b: boolean): true | undefined {
|
||||
return b ? true : undefined;
|
||||
}
|
||||
|
||||
function getSourceFromOrigin(origin: SymbolOriginInfo | undefined): string | undefined {
|
||||
return origin && stripQuotes(origin.moduleSymbol.name);
|
||||
}
|
||||
@@ -177,6 +188,7 @@ namespace ts.Completions {
|
||||
target: ScriptTarget,
|
||||
log: Log,
|
||||
allowStringLiteral: boolean,
|
||||
recommendedCompletion?: Symbol,
|
||||
symbolToOriginInfoMap?: SymbolOriginInfoMap,
|
||||
): Map<true> {
|
||||
const start = timestamp();
|
||||
@@ -188,7 +200,7 @@ namespace ts.Completions {
|
||||
if (symbols) {
|
||||
for (const symbol of symbols) {
|
||||
const origin = symbolToOriginInfoMap ? symbolToOriginInfoMap[getSymbolId(symbol)] : undefined;
|
||||
const entry = createCompletionEntry(symbol, location, performCharacterChecks, typeChecker, target, allowStringLiteral, origin);
|
||||
const entry = createCompletionEntry(symbol, location, performCharacterChecks, typeChecker, target, allowStringLiteral, origin, recommendedCompletion);
|
||||
if (!entry) {
|
||||
continue;
|
||||
}
|
||||
@@ -540,9 +552,29 @@ namespace ts.Completions {
|
||||
request?: Request;
|
||||
keywordFilters: KeywordCompletionFilters;
|
||||
symbolToOriginInfoMap: SymbolOriginInfoMap;
|
||||
recommendedCompletion: Symbol | undefined;
|
||||
}
|
||||
type Request = { kind: "JsDocTagName" } | { kind: "JsDocTag" } | { kind: "JsDocParameterName", tag: JSDocParameterTag };
|
||||
|
||||
function getRecommendedCompletion(currentToken: Node, checker: TypeChecker/*, symbolToOriginInfoMap: SymbolOriginInfoMap*/): Symbol | undefined {
|
||||
const ty = checker.getContextualType(currentToken as Expression);
|
||||
const symbol = ty && ty.symbol;
|
||||
// Don't include make a recommended completion for an abstract class
|
||||
return symbol && (symbol.flags & SymbolFlags.Enum || symbol.flags & SymbolFlags.Class && !isAbstractConstructorSymbol(symbol))
|
||||
? getFirstSymbolInChain(symbol, currentToken, checker)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function getFirstSymbolInChain(symbol: Symbol, enclosingDeclaration: Node, checker: TypeChecker): Symbol | undefined {
|
||||
const chain = checker.getAccessibleSymbolChain(symbol, enclosingDeclaration, /*meaning*/ SymbolFlags.All, /*useOnlyExternalAliasing*/ false);
|
||||
if (chain) return first(chain);
|
||||
return isModuleSymbol(symbol.parent) ? symbol : symbol.parent && getFirstSymbolInChain(symbol.parent, enclosingDeclaration, checker);
|
||||
}
|
||||
|
||||
function isModuleSymbol(symbol: Symbol): boolean {
|
||||
return symbol.declarations.some(d => d.kind === SyntaxKind.SourceFile);
|
||||
}
|
||||
|
||||
function getCompletionData(
|
||||
typeChecker: TypeChecker,
|
||||
log: (message: string) => void,
|
||||
@@ -632,6 +664,7 @@ namespace ts.Completions {
|
||||
request,
|
||||
keywordFilters: KeywordCompletionFilters.None,
|
||||
symbolToOriginInfoMap: undefined,
|
||||
recommendedCompletion: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -771,7 +804,8 @@ namespace ts.Completions {
|
||||
|
||||
log("getCompletionData: Semantic work: " + (timestamp() - semanticStart));
|
||||
|
||||
return { symbols, isGlobalCompletion, isMemberCompletion, allowStringLiteral, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), request, keywordFilters, symbolToOriginInfoMap };
|
||||
const recommendedCompletion = getRecommendedCompletion(previousToken, typeChecker);
|
||||
return { symbols, isGlobalCompletion, isMemberCompletion, allowStringLiteral, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), request, keywordFilters, symbolToOriginInfoMap, recommendedCompletion };
|
||||
|
||||
type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag;
|
||||
|
||||
|
||||
@@ -714,19 +714,21 @@ namespace ts {
|
||||
entries: CompletionEntry[];
|
||||
}
|
||||
|
||||
// see comments in protocol.ts
|
||||
export interface CompletionEntry {
|
||||
name: string;
|
||||
kind: ScriptElementKind;
|
||||
kindModifiers: string; // see ScriptElementKindModifier, comma separated
|
||||
kindModifiers: string; // see ScriptElementKindModifier, comma separated
|
||||
sortText: string;
|
||||
/**
|
||||
* An optional span that indicates the text to be replaced by this completion item. It will be
|
||||
* set if the required span differs from the one generated by the default replacement behavior and should
|
||||
* be used in that case
|
||||
* An optional span that indicates the text to be replaced by this completion item.
|
||||
* If present, this span should be used instead of the default one.
|
||||
* It will be set if the required span differs from the one generated by the default replacement behavior.
|
||||
*/
|
||||
replacementSpan?: TextSpan;
|
||||
hasAction?: true;
|
||||
source?: string;
|
||||
isRecommended?: true;
|
||||
}
|
||||
|
||||
export interface CompletionEntryDetails {
|
||||
|
||||
Reference in New Issue
Block a user