Auto-imports: fix some exports being incorrectly stored as re-exports of others due to key conflict (#45792)
* Ensure symbol key unique when target is a local symbol exported elsewhere * Add test * Support targets without declarations * Best key yet * A-ha moment * Clean up types * Update API * Update unit test
This commit is contained in:
parent
f6c0231f08
commit
64b8172f06
|
@ -74,13 +74,9 @@ namespace ts.Completions {
|
||||||
|
|
||||||
interface SymbolOriginInfo {
|
interface SymbolOriginInfo {
|
||||||
kind: SymbolOriginInfoKind;
|
kind: SymbolOriginInfoKind;
|
||||||
symbolName?: string;
|
|
||||||
moduleSymbol?: Symbol;
|
|
||||||
isDefaultExport?: boolean;
|
isDefaultExport?: boolean;
|
||||||
isFromPackageJson?: boolean;
|
isFromPackageJson?: boolean;
|
||||||
exportName?: string;
|
|
||||||
fileName?: string;
|
fileName?: string;
|
||||||
moduleSpecifier?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SymbolOriginInfoExport extends SymbolOriginInfo {
|
interface SymbolOriginInfoExport extends SymbolOriginInfo {
|
||||||
|
@ -88,9 +84,13 @@ namespace ts.Completions {
|
||||||
moduleSymbol: Symbol;
|
moduleSymbol: Symbol;
|
||||||
isDefaultExport: boolean;
|
isDefaultExport: boolean;
|
||||||
exportName: string;
|
exportName: string;
|
||||||
|
exportMapKey: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SymbolOriginInfoResolvedExport extends SymbolOriginInfoExport {
|
interface SymbolOriginInfoResolvedExport extends SymbolOriginInfo {
|
||||||
|
symbolName: string;
|
||||||
|
moduleSymbol: Symbol;
|
||||||
|
exportName: string;
|
||||||
moduleSpecifier: string;
|
moduleSpecifier: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,6 +294,10 @@ namespace ts.Completions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function completionEntryDataIsResolved(data: CompletionEntryDataAutoImport | undefined): data is CompletionEntryDataResolved {
|
||||||
|
return !!data?.moduleSpecifier;
|
||||||
|
}
|
||||||
|
|
||||||
function continuePreviousIncompleteResponse(
|
function continuePreviousIncompleteResponse(
|
||||||
cache: IncompleteCompletionsCache,
|
cache: IncompleteCompletionsCache,
|
||||||
file: SourceFile,
|
file: SourceFile,
|
||||||
|
@ -308,9 +312,6 @@ namespace ts.Completions {
|
||||||
|
|
||||||
const lowerCaseTokenText = location.text.toLowerCase();
|
const lowerCaseTokenText = location.text.toLowerCase();
|
||||||
const exportMap = getExportInfoMap(file, host, program, cancellationToken);
|
const exportMap = getExportInfoMap(file, host, program, cancellationToken);
|
||||||
const checker = program.getTypeChecker();
|
|
||||||
const autoImportProvider = host.getPackageJsonAutoImportProvider?.();
|
|
||||||
const autoImportProviderChecker = autoImportProvider?.getTypeChecker();
|
|
||||||
const newEntries = resolvingModuleSpecifiers(
|
const newEntries = resolvingModuleSpecifiers(
|
||||||
"continuePreviousIncompleteResponse",
|
"continuePreviousIncompleteResponse",
|
||||||
host,
|
host,
|
||||||
|
@ -320,7 +321,7 @@ namespace ts.Completions {
|
||||||
/*isForImportStatementCompletion*/ false,
|
/*isForImportStatementCompletion*/ false,
|
||||||
context => {
|
context => {
|
||||||
const entries = mapDefined(previousResponse.entries, entry => {
|
const entries = mapDefined(previousResponse.entries, entry => {
|
||||||
if (!entry.hasAction || !entry.source || !entry.data || entry.data.moduleSpecifier) {
|
if (!entry.hasAction || !entry.source || !entry.data || completionEntryDataIsResolved(entry.data)) {
|
||||||
// Not an auto import or already resolved; keep as is
|
// Not an auto import or already resolved; keep as is
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
@ -329,13 +330,8 @@ namespace ts.Completions {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { symbol, origin } = Debug.checkDefined(getAutoImportSymbolFromCompletionEntryData(entry.name, entry.data, program, host));
|
const { origin } = Debug.checkDefined(getAutoImportSymbolFromCompletionEntryData(entry.name, entry.data, program, host));
|
||||||
const info = exportMap.get(
|
const info = exportMap.get(file.path, entry.data.exportMapKey);
|
||||||
file.path,
|
|
||||||
entry.name,
|
|
||||||
symbol,
|
|
||||||
origin.moduleSymbol.name,
|
|
||||||
origin.isFromPackageJson ? autoImportProviderChecker! : checker);
|
|
||||||
|
|
||||||
const result = info && context.tryResolve(info, !isExternalModuleNameRelative(stripQuotes(origin.moduleSymbol.name)));
|
const result = info && context.tryResolve(info, !isExternalModuleNameRelative(stripQuotes(origin.moduleSymbol.name)));
|
||||||
if (!result) return entry;
|
if (!result) return entry;
|
||||||
|
@ -760,14 +756,56 @@ namespace ts.Completions {
|
||||||
return text.replace(/\$/gm, "\\$");
|
return text.replace(/\$/gm, "\\$");
|
||||||
}
|
}
|
||||||
|
|
||||||
function originToCompletionEntryData(origin: SymbolOriginInfoExport): CompletionEntryData | undefined {
|
function originToCompletionEntryData(origin: SymbolOriginInfoExport | SymbolOriginInfoResolvedExport): CompletionEntryData | undefined {
|
||||||
return {
|
const ambientModuleName = origin.fileName ? undefined : stripQuotes(origin.moduleSymbol.name);
|
||||||
|
const isPackageJsonImport = origin.isFromPackageJson ? true : undefined;
|
||||||
|
if (originIsResolvedExport(origin)) {
|
||||||
|
const resolvedData: CompletionEntryDataResolved = {
|
||||||
exportName: origin.exportName,
|
exportName: origin.exportName,
|
||||||
|
moduleSpecifier: origin.moduleSpecifier,
|
||||||
|
ambientModuleName,
|
||||||
|
fileName: origin.fileName,
|
||||||
|
isPackageJsonImport,
|
||||||
|
};
|
||||||
|
return resolvedData;
|
||||||
|
}
|
||||||
|
const unresolvedData: CompletionEntryDataUnresolved = {
|
||||||
|
exportName: origin.exportName,
|
||||||
|
exportMapKey: origin.exportMapKey,
|
||||||
fileName: origin.fileName,
|
fileName: origin.fileName,
|
||||||
ambientModuleName: origin.fileName ? undefined : stripQuotes(origin.moduleSymbol.name),
|
ambientModuleName: origin.fileName ? undefined : stripQuotes(origin.moduleSymbol.name),
|
||||||
isPackageJsonImport: origin.isFromPackageJson ? true : undefined,
|
isPackageJsonImport: origin.isFromPackageJson ? true : undefined,
|
||||||
moduleSpecifier: originIsResolvedExport(origin) ? origin.moduleSpecifier : undefined,
|
|
||||||
};
|
};
|
||||||
|
return unresolvedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
function completionEntryDataToSymbolOriginInfo(data: CompletionEntryData, completionName: string, moduleSymbol: Symbol): SymbolOriginInfoExport | SymbolOriginInfoResolvedExport {
|
||||||
|
const isDefaultExport = data.exportName === InternalSymbolName.Default;
|
||||||
|
const isFromPackageJson = !!data.isPackageJsonImport;
|
||||||
|
if (completionEntryDataIsResolved(data)) {
|
||||||
|
const resolvedOrigin: SymbolOriginInfoResolvedExport = {
|
||||||
|
kind: SymbolOriginInfoKind.ResolvedExport,
|
||||||
|
exportName: data.exportName,
|
||||||
|
moduleSpecifier: data.moduleSpecifier,
|
||||||
|
symbolName: completionName,
|
||||||
|
fileName: data.fileName,
|
||||||
|
moduleSymbol,
|
||||||
|
isDefaultExport,
|
||||||
|
isFromPackageJson,
|
||||||
|
};
|
||||||
|
return resolvedOrigin;
|
||||||
|
}
|
||||||
|
const unresolvedOrigin: SymbolOriginInfoExport = {
|
||||||
|
kind: SymbolOriginInfoKind.Export,
|
||||||
|
exportName: data.exportName,
|
||||||
|
exportMapKey: data.exportMapKey,
|
||||||
|
symbolName: completionName,
|
||||||
|
fileName: data.fileName,
|
||||||
|
moduleSymbol,
|
||||||
|
isDefaultExport,
|
||||||
|
isFromPackageJson,
|
||||||
|
};
|
||||||
|
return unresolvedOrigin;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getInsertTextAndReplacementSpanForImportCompletion(name: string, importCompletionNode: Node, contextToken: Node | undefined, origin: SymbolOriginInfoResolvedExport, useSemicolons: boolean, options: CompilerOptions, preferences: UserPreferences) {
|
function getInsertTextAndReplacementSpanForImportCompletion(name: string, importCompletionNode: Node, contextToken: Node | undefined, origin: SymbolOriginInfoResolvedExport, useSemicolons: boolean, options: CompilerOptions, preferences: UserPreferences) {
|
||||||
|
@ -1762,21 +1800,37 @@ namespace ts.Completions {
|
||||||
const index = symbols.length;
|
const index = symbols.length;
|
||||||
symbols.push(firstAccessibleSymbol);
|
symbols.push(firstAccessibleSymbol);
|
||||||
const moduleSymbol = firstAccessibleSymbol.parent;
|
const moduleSymbol = firstAccessibleSymbol.parent;
|
||||||
if (!moduleSymbol || !isExternalModuleSymbol(moduleSymbol)) {
|
if (!moduleSymbol ||
|
||||||
|
!isExternalModuleSymbol(moduleSymbol) ||
|
||||||
|
typeChecker.tryGetMemberInModuleExportsAndProperties(firstAccessibleSymbol.name, moduleSymbol) !== firstAccessibleSymbol
|
||||||
|
) {
|
||||||
symbolToOriginInfoMap[index] = { kind: getNullableSymbolOriginInfoKind(SymbolOriginInfoKind.SymbolMemberNoExport) };
|
symbolToOriginInfoMap[index] = { kind: getNullableSymbolOriginInfoKind(SymbolOriginInfoKind.SymbolMemberNoExport) };
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const origin: SymbolOriginInfoExport = {
|
const fileName = isExternalModuleNameRelative(stripQuotes(moduleSymbol.name)) ? getSourceFileOfModule(moduleSymbol)?.fileName : undefined;
|
||||||
|
const { moduleSpecifier } = codefix.getModuleSpecifierForBestExportInfo([{
|
||||||
|
exportKind: ExportKind.Named,
|
||||||
|
moduleFileName: fileName,
|
||||||
|
isFromPackageJson: false,
|
||||||
|
moduleSymbol,
|
||||||
|
symbol: firstAccessibleSymbol,
|
||||||
|
targetFlags: skipAlias(firstAccessibleSymbol, typeChecker).flags,
|
||||||
|
}], sourceFile, program, host, preferences) || {};
|
||||||
|
|
||||||
|
if (moduleSpecifier) {
|
||||||
|
const origin: SymbolOriginInfoResolvedExport = {
|
||||||
kind: getNullableSymbolOriginInfoKind(SymbolOriginInfoKind.SymbolMemberExport),
|
kind: getNullableSymbolOriginInfoKind(SymbolOriginInfoKind.SymbolMemberExport),
|
||||||
moduleSymbol,
|
moduleSymbol,
|
||||||
isDefaultExport: false,
|
isDefaultExport: false,
|
||||||
symbolName: firstAccessibleSymbol.name,
|
symbolName: firstAccessibleSymbol.name,
|
||||||
exportName: firstAccessibleSymbol.name,
|
exportName: firstAccessibleSymbol.name,
|
||||||
fileName: isExternalModuleNameRelative(stripQuotes(moduleSymbol.name)) ? cast(moduleSymbol.valueDeclaration, isSourceFile).fileName : undefined,
|
fileName,
|
||||||
|
moduleSpecifier,
|
||||||
};
|
};
|
||||||
symbolToOriginInfoMap[index] = origin;
|
symbolToOriginInfoMap[index] = origin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (preferences.includeCompletionsWithInsertText) {
|
else if (preferences.includeCompletionsWithInsertText) {
|
||||||
addSymbolOriginInfo(symbol);
|
addSymbolOriginInfo(symbol);
|
||||||
addSymbolSortInfo(symbol);
|
addSymbolSortInfo(symbol);
|
||||||
|
@ -2034,7 +2088,7 @@ namespace ts.Completions {
|
||||||
preferences,
|
preferences,
|
||||||
!!importCompletionNode,
|
!!importCompletionNode,
|
||||||
context => {
|
context => {
|
||||||
exportInfo.forEach(sourceFile.path, (info, symbolName, isFromAmbientModule) => {
|
exportInfo.forEach(sourceFile.path, (info, symbolName, isFromAmbientModule, exportMapKey) => {
|
||||||
if (!isIdentifierText(symbolName, getEmitScriptTarget(host.getCompilationSettings()))) return;
|
if (!isIdentifierText(symbolName, getEmitScriptTarget(host.getCompilationSettings()))) return;
|
||||||
if (!detailsEntryId && isStringANonContextualKeyword(symbolName)) return;
|
if (!detailsEntryId && isStringANonContextualKeyword(symbolName)) return;
|
||||||
// `targetFlags` should be the same for each `info`
|
// `targetFlags` should be the same for each `info`
|
||||||
|
@ -2056,6 +2110,7 @@ namespace ts.Completions {
|
||||||
kind: moduleSpecifier ? SymbolOriginInfoKind.ResolvedExport : SymbolOriginInfoKind.Export,
|
kind: moduleSpecifier ? SymbolOriginInfoKind.ResolvedExport : SymbolOriginInfoKind.Export,
|
||||||
moduleSpecifier,
|
moduleSpecifier,
|
||||||
symbolName,
|
symbolName,
|
||||||
|
exportMapKey,
|
||||||
exportName: exportInfo.exportKind === ExportKind.ExportEquals ? InternalSymbolName.ExportEquals : exportInfo.symbol.name,
|
exportName: exportInfo.exportKind === ExportKind.ExportEquals ? InternalSymbolName.ExportEquals : exportInfo.symbol.name,
|
||||||
fileName: exportInfo.moduleFileName,
|
fileName: exportInfo.moduleFileName,
|
||||||
isDefaultExport,
|
isDefaultExport,
|
||||||
|
@ -2974,7 +3029,7 @@ namespace ts.Completions {
|
||||||
return { contextToken: previousToken as Node, previousToken: previousToken as Node };
|
return { contextToken: previousToken as Node, previousToken: previousToken as Node };
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAutoImportSymbolFromCompletionEntryData(name: string, data: CompletionEntryData, program: Program, host: LanguageServiceHost): { symbol: Symbol, origin: SymbolOriginInfoExport } | undefined {
|
function getAutoImportSymbolFromCompletionEntryData(name: string, data: CompletionEntryData, program: Program, host: LanguageServiceHost): { symbol: Symbol, origin: SymbolOriginInfoExport | SymbolOriginInfoResolvedExport } | undefined {
|
||||||
const containingProgram = data.isPackageJsonImport ? host.getPackageJsonAutoImportProvider!()! : program;
|
const containingProgram = data.isPackageJsonImport ? host.getPackageJsonAutoImportProvider!()! : program;
|
||||||
const checker = containingProgram.getTypeChecker();
|
const checker = containingProgram.getTypeChecker();
|
||||||
const moduleSymbol =
|
const moduleSymbol =
|
||||||
|
@ -2989,18 +3044,7 @@ namespace ts.Completions {
|
||||||
if (!symbol) return undefined;
|
if (!symbol) return undefined;
|
||||||
const isDefaultExport = data.exportName === InternalSymbolName.Default;
|
const isDefaultExport = data.exportName === InternalSymbolName.Default;
|
||||||
symbol = isDefaultExport && getLocalSymbolForExportDefault(symbol) || symbol;
|
symbol = isDefaultExport && getLocalSymbolForExportDefault(symbol) || symbol;
|
||||||
return {
|
return { symbol, origin: completionEntryDataToSymbolOriginInfo(data, name, moduleSymbol) };
|
||||||
symbol,
|
|
||||||
origin: {
|
|
||||||
kind: data.moduleSpecifier ? SymbolOriginInfoKind.ResolvedExport : SymbolOriginInfoKind.Export,
|
|
||||||
moduleSymbol,
|
|
||||||
symbolName: name,
|
|
||||||
isDefaultExport,
|
|
||||||
exportName: data.exportName,
|
|
||||||
fileName: data.fileName,
|
|
||||||
isFromPackageJson: !!data.isPackageJsonImport,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CompletionEntryDisplayNameForSymbol {
|
interface CompletionEntryDisplayNameForSymbol {
|
||||||
|
|
|
@ -46,8 +46,8 @@ namespace ts {
|
||||||
isUsableByFile(importingFile: Path): boolean;
|
isUsableByFile(importingFile: Path): boolean;
|
||||||
clear(): void;
|
clear(): void;
|
||||||
add(importingFile: Path, symbol: Symbol, key: __String, moduleSymbol: Symbol, moduleFile: SourceFile | undefined, exportKind: ExportKind, isFromPackageJson: boolean, scriptTarget: ScriptTarget, checker: TypeChecker): void;
|
add(importingFile: Path, symbol: Symbol, key: __String, moduleSymbol: Symbol, moduleFile: SourceFile | undefined, exportKind: ExportKind, isFromPackageJson: boolean, scriptTarget: ScriptTarget, checker: TypeChecker): void;
|
||||||
get(importingFile: Path, importedName: string, symbol: Symbol, moduleName: string, checker: TypeChecker): readonly SymbolExportInfo[] | undefined;
|
get(importingFile: Path, key: string): readonly SymbolExportInfo[] | undefined;
|
||||||
forEach(importingFile: Path, action: (info: readonly SymbolExportInfo[], name: string, isFromAmbientModule: boolean) => void): void;
|
forEach(importingFile: Path, action: (info: readonly SymbolExportInfo[], name: string, isFromAmbientModule: boolean, key: string) => void): void;
|
||||||
releaseSymbols(): void;
|
releaseSymbols(): void;
|
||||||
isEmpty(): boolean;
|
isEmpty(): boolean;
|
||||||
/** @returns Whether the change resulted in the cache being cleared */
|
/** @returns Whether the change resulted in the cache being cleared */
|
||||||
|
@ -87,11 +87,12 @@ namespace ts {
|
||||||
: getNameForExportedSymbol(namedSymbol, scriptTarget);
|
: getNameForExportedSymbol(namedSymbol, scriptTarget);
|
||||||
const moduleName = stripQuotes(moduleSymbol.name);
|
const moduleName = stripQuotes(moduleSymbol.name);
|
||||||
const id = exportInfoId++;
|
const id = exportInfoId++;
|
||||||
|
const target = skipAlias(symbol, checker);
|
||||||
const storedSymbol = symbol.flags & SymbolFlags.Transient ? undefined : symbol;
|
const storedSymbol = symbol.flags & SymbolFlags.Transient ? undefined : symbol;
|
||||||
const storedModuleSymbol = moduleSymbol.flags & SymbolFlags.Transient ? undefined : moduleSymbol;
|
const storedModuleSymbol = moduleSymbol.flags & SymbolFlags.Transient ? undefined : moduleSymbol;
|
||||||
if (!storedSymbol || !storedModuleSymbol) symbols.set(id, [symbol, moduleSymbol]);
|
if (!storedSymbol || !storedModuleSymbol) symbols.set(id, [symbol, moduleSymbol]);
|
||||||
|
|
||||||
exportInfo.add(key(importedName, symbol, moduleName, checker), {
|
exportInfo.add(key(importedName, symbol, isExternalModuleNameRelative(moduleName) ? undefined : moduleName, checker), {
|
||||||
id,
|
id,
|
||||||
symbolTableKey,
|
symbolTableKey,
|
||||||
symbolName: importedName,
|
symbolName: importedName,
|
||||||
|
@ -99,22 +100,22 @@ namespace ts {
|
||||||
moduleFile,
|
moduleFile,
|
||||||
moduleFileName: moduleFile?.fileName,
|
moduleFileName: moduleFile?.fileName,
|
||||||
exportKind,
|
exportKind,
|
||||||
targetFlags: skipAlias(symbol, checker).flags,
|
targetFlags: target.flags,
|
||||||
isFromPackageJson,
|
isFromPackageJson,
|
||||||
symbol: storedSymbol,
|
symbol: storedSymbol,
|
||||||
moduleSymbol: storedModuleSymbol,
|
moduleSymbol: storedModuleSymbol,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
get: (importingFile, importedName, symbol, moduleName, checker) => {
|
get: (importingFile, key) => {
|
||||||
if (importingFile !== usableByFileName) return;
|
if (importingFile !== usableByFileName) return;
|
||||||
const result = exportInfo.get(key(importedName, symbol, moduleName, checker));
|
const result = exportInfo.get(key);
|
||||||
return result?.map(rehydrateCachedInfo);
|
return result?.map(rehydrateCachedInfo);
|
||||||
},
|
},
|
||||||
forEach: (importingFile, action) => {
|
forEach: (importingFile, action) => {
|
||||||
if (importingFile !== usableByFileName) return;
|
if (importingFile !== usableByFileName) return;
|
||||||
exportInfo.forEach((info, key) => {
|
exportInfo.forEach((info, key) => {
|
||||||
const { symbolName, ambientModuleName } = parseKey(key);
|
const { symbolName, ambientModuleName } = parseKey(key);
|
||||||
action(info.map(rehydrateCachedInfo), symbolName, !!ambientModuleName);
|
action(info.map(rehydrateCachedInfo), symbolName, !!ambientModuleName, key);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
releaseSymbols: () => {
|
releaseSymbols: () => {
|
||||||
|
@ -183,29 +184,18 @@ namespace ts {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function key(importedName: string, symbol: Symbol, moduleName: string, checker: TypeChecker) {
|
function key(importedName: string, symbol: Symbol, ambientModuleName: string | undefined, checker: TypeChecker): string {
|
||||||
const unquoted = stripQuotes(moduleName);
|
const moduleKey = ambientModuleName || "";
|
||||||
const moduleKey = isExternalModuleNameRelative(unquoted) ? "/" : unquoted;
|
return `${importedName}|${getSymbolId(skipAlias(symbol, checker))}|${moduleKey}`;
|
||||||
const target = skipAlias(symbol, checker);
|
|
||||||
return `${importedName}|${createSymbolKey(target)}|${moduleKey}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseKey(key: string) {
|
function parseKey(key: string) {
|
||||||
const symbolName = key.substring(0, key.indexOf("|"));
|
const symbolName = key.substring(0, key.indexOf("|"));
|
||||||
const moduleKey = key.substring(key.lastIndexOf("|") + 1);
|
const moduleKey = key.substring(key.lastIndexOf("|") + 1);
|
||||||
const ambientModuleName = moduleKey === "/" ? undefined : moduleKey;
|
const ambientModuleName = moduleKey === "" ? undefined : moduleKey;
|
||||||
return { symbolName, ambientModuleName };
|
return { symbolName, ambientModuleName };
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSymbolKey(symbol: Symbol) {
|
|
||||||
let key = symbol.name;
|
|
||||||
while (symbol.parent) {
|
|
||||||
key += `,${symbol.parent.name}`;
|
|
||||||
symbol = symbol.parent;
|
|
||||||
}
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
function fileIsGlobalOnly(file: SourceFile) {
|
function fileIsGlobalOnly(file: SourceFile) {
|
||||||
return !file.commonJsModuleIndicator && !file.externalModuleIndicator && !file.moduleAugmentations && !file.ambientModuleNames;
|
return !file.commonJsModuleIndicator && !file.externalModuleIndicator && !file.moduleAugmentations && !file.ambientModuleNames;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1187,24 +1187,32 @@ namespace ts {
|
||||||
entries: CompletionEntry[];
|
entries: CompletionEntry[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CompletionEntryData {
|
export interface CompletionEntryDataAutoImport {
|
||||||
|
/**
|
||||||
|
* The name of the property or export in the module's symbol table. Differs from the completion name
|
||||||
|
* in the case of InternalSymbolName.ExportEquals and InternalSymbolName.Default.
|
||||||
|
*/
|
||||||
|
exportName: string;
|
||||||
|
moduleSpecifier?: string;
|
||||||
/** The file name declaring the export's module symbol, if it was an external module */
|
/** The file name declaring the export's module symbol, if it was an external module */
|
||||||
fileName?: string;
|
fileName?: string;
|
||||||
/** The module name (with quotes stripped) of the export's module symbol, if it was an ambient module */
|
/** The module name (with quotes stripped) of the export's module symbol, if it was an ambient module */
|
||||||
ambientModuleName?: string;
|
ambientModuleName?: string;
|
||||||
/** True if the export was found in the package.json AutoImportProvider */
|
/** True if the export was found in the package.json AutoImportProvider */
|
||||||
isPackageJsonImport?: true;
|
isPackageJsonImport?: true;
|
||||||
/**
|
|
||||||
* The name of the property or export in the module's symbol table. Differs from the completion name
|
|
||||||
* in the case of InternalSymbolName.ExportEquals and InternalSymbolName.Default.
|
|
||||||
*/
|
|
||||||
exportName: string;
|
|
||||||
/**
|
|
||||||
* Set for auto imports with eagerly resolved module specifiers.
|
|
||||||
*/
|
|
||||||
moduleSpecifier?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CompletionEntryDataUnresolved extends CompletionEntryDataAutoImport {
|
||||||
|
/** The key in the `ExportMapCache` where the completion entry's `SymbolExportInfo[]` is found */
|
||||||
|
exportMapKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CompletionEntryDataResolved extends CompletionEntryDataAutoImport {
|
||||||
|
moduleSpecifier: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CompletionEntryData = CompletionEntryDataUnresolved | CompletionEntryDataResolved;
|
||||||
|
|
||||||
// see comments in protocol.ts
|
// see comments in protocol.ts
|
||||||
export interface CompletionEntry {
|
export interface CompletionEntry {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
@ -42,8 +42,14 @@ namespace ts.projectSystem {
|
||||||
source: "/a",
|
source: "/a",
|
||||||
sourceDisplay: undefined,
|
sourceDisplay: undefined,
|
||||||
isSnippet: undefined,
|
isSnippet: undefined,
|
||||||
data: { exportName: "foo", fileName: "/a.ts", ambientModuleName: undefined, isPackageJsonImport: undefined, moduleSpecifier: undefined }
|
data: { exportName: "foo", fileName: "/a.ts", ambientModuleName: undefined, isPackageJsonImport: undefined }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// `data.exportMapKey` contains a SymbolId so should not be mocked up with an expected value here.
|
||||||
|
// Just assert that it's a string and then delete it so we can compare everything else with `deepEqual`.
|
||||||
|
const exportMapKey = (response?.entries[0].data as any)?.exportMapKey;
|
||||||
|
assert.isString(exportMapKey);
|
||||||
|
delete (response?.entries[0].data as any).exportMapKey;
|
||||||
assert.deepEqual<protocol.CompletionInfo | undefined>(response, {
|
assert.deepEqual<protocol.CompletionInfo | undefined>(response, {
|
||||||
isGlobalCompletion: true,
|
isGlobalCompletion: true,
|
||||||
isIncomplete: undefined,
|
isIncomplete: undefined,
|
||||||
|
@ -55,7 +61,7 @@ namespace ts.projectSystem {
|
||||||
|
|
||||||
const detailsRequestArgs: protocol.CompletionDetailsRequestArgs = {
|
const detailsRequestArgs: protocol.CompletionDetailsRequestArgs = {
|
||||||
...requestLocation,
|
...requestLocation,
|
||||||
entryNames: [{ name: "foo", source: "/a", data: { exportName: "foo", fileName: "/a.ts" } }],
|
entryNames: [{ name: "foo", source: "/a", data: { exportName: "foo", fileName: "/a.ts", exportMapKey } }],
|
||||||
};
|
};
|
||||||
|
|
||||||
const detailsResponse = executeSessionRequest<protocol.CompletionDetailsRequest, protocol.CompletionDetailsResponse>(session, protocol.CommandTypes.CompletionDetails, detailsRequestArgs);
|
const detailsResponse = executeSessionRequest<protocol.CompletionDetailsRequest, protocol.CompletionDetailsResponse>(session, protocol.CommandTypes.CompletionDetails, detailsRequestArgs);
|
||||||
|
|
|
@ -6367,23 +6367,28 @@ declare namespace ts {
|
||||||
isIncomplete?: true;
|
isIncomplete?: true;
|
||||||
entries: CompletionEntry[];
|
entries: CompletionEntry[];
|
||||||
}
|
}
|
||||||
interface CompletionEntryData {
|
interface CompletionEntryDataAutoImport {
|
||||||
|
/**
|
||||||
|
* The name of the property or export in the module's symbol table. Differs from the completion name
|
||||||
|
* in the case of InternalSymbolName.ExportEquals and InternalSymbolName.Default.
|
||||||
|
*/
|
||||||
|
exportName: string;
|
||||||
|
moduleSpecifier?: string;
|
||||||
/** The file name declaring the export's module symbol, if it was an external module */
|
/** The file name declaring the export's module symbol, if it was an external module */
|
||||||
fileName?: string;
|
fileName?: string;
|
||||||
/** The module name (with quotes stripped) of the export's module symbol, if it was an ambient module */
|
/** The module name (with quotes stripped) of the export's module symbol, if it was an ambient module */
|
||||||
ambientModuleName?: string;
|
ambientModuleName?: string;
|
||||||
/** True if the export was found in the package.json AutoImportProvider */
|
/** True if the export was found in the package.json AutoImportProvider */
|
||||||
isPackageJsonImport?: true;
|
isPackageJsonImport?: true;
|
||||||
/**
|
|
||||||
* The name of the property or export in the module's symbol table. Differs from the completion name
|
|
||||||
* in the case of InternalSymbolName.ExportEquals and InternalSymbolName.Default.
|
|
||||||
*/
|
|
||||||
exportName: string;
|
|
||||||
/**
|
|
||||||
* Set for auto imports with eagerly resolved module specifiers.
|
|
||||||
*/
|
|
||||||
moduleSpecifier?: string;
|
|
||||||
}
|
}
|
||||||
|
interface CompletionEntryDataUnresolved extends CompletionEntryDataAutoImport {
|
||||||
|
/** The key in the `ExportMapCache` where the completion entry's `SymbolExportInfo[]` is found */
|
||||||
|
exportMapKey: string;
|
||||||
|
}
|
||||||
|
interface CompletionEntryDataResolved extends CompletionEntryDataAutoImport {
|
||||||
|
moduleSpecifier: string;
|
||||||
|
}
|
||||||
|
type CompletionEntryData = CompletionEntryDataUnresolved | CompletionEntryDataResolved;
|
||||||
interface CompletionEntry {
|
interface CompletionEntry {
|
||||||
name: string;
|
name: string;
|
||||||
kind: ScriptElementKind;
|
kind: ScriptElementKind;
|
||||||
|
|
25
tests/baselines/reference/api/typescript.d.ts
vendored
25
tests/baselines/reference/api/typescript.d.ts
vendored
|
@ -6367,23 +6367,28 @@ declare namespace ts {
|
||||||
isIncomplete?: true;
|
isIncomplete?: true;
|
||||||
entries: CompletionEntry[];
|
entries: CompletionEntry[];
|
||||||
}
|
}
|
||||||
interface CompletionEntryData {
|
interface CompletionEntryDataAutoImport {
|
||||||
|
/**
|
||||||
|
* The name of the property or export in the module's symbol table. Differs from the completion name
|
||||||
|
* in the case of InternalSymbolName.ExportEquals and InternalSymbolName.Default.
|
||||||
|
*/
|
||||||
|
exportName: string;
|
||||||
|
moduleSpecifier?: string;
|
||||||
/** The file name declaring the export's module symbol, if it was an external module */
|
/** The file name declaring the export's module symbol, if it was an external module */
|
||||||
fileName?: string;
|
fileName?: string;
|
||||||
/** The module name (with quotes stripped) of the export's module symbol, if it was an ambient module */
|
/** The module name (with quotes stripped) of the export's module symbol, if it was an ambient module */
|
||||||
ambientModuleName?: string;
|
ambientModuleName?: string;
|
||||||
/** True if the export was found in the package.json AutoImportProvider */
|
/** True if the export was found in the package.json AutoImportProvider */
|
||||||
isPackageJsonImport?: true;
|
isPackageJsonImport?: true;
|
||||||
/**
|
|
||||||
* The name of the property or export in the module's symbol table. Differs from the completion name
|
|
||||||
* in the case of InternalSymbolName.ExportEquals and InternalSymbolName.Default.
|
|
||||||
*/
|
|
||||||
exportName: string;
|
|
||||||
/**
|
|
||||||
* Set for auto imports with eagerly resolved module specifiers.
|
|
||||||
*/
|
|
||||||
moduleSpecifier?: string;
|
|
||||||
}
|
}
|
||||||
|
interface CompletionEntryDataUnresolved extends CompletionEntryDataAutoImport {
|
||||||
|
/** The key in the `ExportMapCache` where the completion entry's `SymbolExportInfo[]` is found */
|
||||||
|
exportMapKey: string;
|
||||||
|
}
|
||||||
|
interface CompletionEntryDataResolved extends CompletionEntryDataAutoImport {
|
||||||
|
moduleSpecifier: string;
|
||||||
|
}
|
||||||
|
type CompletionEntryData = CompletionEntryDataUnresolved | CompletionEntryDataResolved;
|
||||||
interface CompletionEntry {
|
interface CompletionEntry {
|
||||||
name: string;
|
name: string;
|
||||||
kind: ScriptElementKind;
|
kind: ScriptElementKind;
|
||||||
|
|
35
tests/cases/fourslash/autoImportSameNameDefaultExported.ts
Normal file
35
tests/cases/fourslash/autoImportSameNameDefaultExported.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
/// <reference path="fourslash.ts" />
|
||||||
|
|
||||||
|
// @module: commonjs
|
||||||
|
|
||||||
|
// @Filename: /node_modules/antd/index.d.ts
|
||||||
|
//// declare function Table(): void;
|
||||||
|
//// export default Table;
|
||||||
|
|
||||||
|
// @Filename: /node_modules/rc-table/index.d.ts
|
||||||
|
//// declare function Table(): void;
|
||||||
|
//// export default Table;
|
||||||
|
|
||||||
|
// @Filename: /index.ts
|
||||||
|
//// Table/**/
|
||||||
|
|
||||||
|
verify.completions({
|
||||||
|
marker: "",
|
||||||
|
exact: completion.globalsPlus([{
|
||||||
|
name: "Table",
|
||||||
|
source: "antd",
|
||||||
|
sourceDisplay: "antd",
|
||||||
|
sortText: completion.SortText.AutoImportSuggestions,
|
||||||
|
hasAction: true,
|
||||||
|
}, {
|
||||||
|
name: "Table",
|
||||||
|
source: "rc-table",
|
||||||
|
sourceDisplay: "rc-table",
|
||||||
|
sortText: completion.SortText.AutoImportSuggestions,
|
||||||
|
hasAction: true,
|
||||||
|
}]),
|
||||||
|
preferences: {
|
||||||
|
includeCompletionsForModuleExports: true,
|
||||||
|
allowIncompleteCompletions: true,
|
||||||
|
}
|
||||||
|
});
|
33
tests/cases/fourslash/completionsImport_reexportTransient.ts
Normal file
33
tests/cases/fourslash/completionsImport_reexportTransient.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/// <reference path="fourslash.ts" />
|
||||||
|
|
||||||
|
// @esModuleInterop: true
|
||||||
|
|
||||||
|
// @Filename: /transient.d.ts
|
||||||
|
//// declare const map: { [K in "one"]: number };
|
||||||
|
//// export = map;
|
||||||
|
|
||||||
|
// @Filename: /r1.ts
|
||||||
|
//// export { one } from "./transient";
|
||||||
|
|
||||||
|
// @Filename: /r2.ts
|
||||||
|
//// export { one } from "./r1";
|
||||||
|
|
||||||
|
// @Filename: /index.ts
|
||||||
|
//// one/**/
|
||||||
|
|
||||||
|
goTo.marker("");
|
||||||
|
|
||||||
|
verify.completions({
|
||||||
|
marker: "",
|
||||||
|
exact: completion.globalsPlus([{
|
||||||
|
name: "one",
|
||||||
|
source: "./transient",
|
||||||
|
sourceDisplay: "./transient",
|
||||||
|
hasAction: true,
|
||||||
|
sortText: completion.SortText.AutoImportSuggestions,
|
||||||
|
}]),
|
||||||
|
preferences: {
|
||||||
|
includeCompletionsForModuleExports: true,
|
||||||
|
allowIncompleteCompletions: true,
|
||||||
|
}
|
||||||
|
});
|
Loading…
Reference in a new issue