mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Build new functionality into go-to-implementation
This commit is contained in:
@@ -334,26 +334,6 @@ namespace ts.server {
|
||||
}));
|
||||
}
|
||||
|
||||
getSourceDefinitionAndBoundSpan(fileName: string, position: number): DefinitionInfoAndBoundSpan {
|
||||
const args: protocol.FileLocationRequestArgs = this.createFileLocationRequestArgs(fileName, position);
|
||||
const request = this.processRequest<protocol.SourceDefinitionAndBoundSpanRequest>(CommandNames.SourceDefinitionAndBoundSpan, args);
|
||||
const response = this.processResponse<protocol.DefinitionInfoAndBoundSpanResponse>(request);
|
||||
const body = Debug.checkDefined(response.body); // TODO: GH#18217
|
||||
|
||||
return {
|
||||
definitions: body.definitions.map(entry => ({
|
||||
containerKind: ScriptElementKind.unknown,
|
||||
containerName: "",
|
||||
fileName: entry.file,
|
||||
textSpan: this.decodeSpan(entry),
|
||||
kind: ScriptElementKind.unknown,
|
||||
name: "",
|
||||
unverified: entry.unverified,
|
||||
})),
|
||||
textSpan: this.decodeSpan(body.textSpan, request.arguments.file)
|
||||
};
|
||||
}
|
||||
|
||||
getImplementationAtPosition(fileName: string, position: number): ImplementationLocation[] {
|
||||
const args = this.createFileLocationRequestArgs(fileName, position);
|
||||
|
||||
|
||||
@@ -697,13 +697,6 @@ namespace FourSlash {
|
||||
this.verifyGoToX(arg0, endMarkerNames, () => this.getGoToDefinitionAndBoundSpan());
|
||||
}
|
||||
|
||||
public verifyGoToSourceDefinition(startMarkerNames: ArrayOrSingle<string>, end?: ArrayOrSingle<string | { marker: string, unverified?: boolean }> | { file: string, unverified?: boolean }) {
|
||||
if (this.testType !== FourSlashTestType.Server) {
|
||||
this.raiseError("goToSourceDefinition may only be used in fourslash/server tests.");
|
||||
}
|
||||
this.verifyGoToX(startMarkerNames, end, () => (this.languageService as ts.server.SessionClient).getSourceDefinitionAndBoundSpan(this.activeFile.fileName, this.currentCaretPosition)!);
|
||||
}
|
||||
|
||||
private getGoToDefinition(): readonly ts.DefinitionInfo[] {
|
||||
return this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition)!;
|
||||
}
|
||||
@@ -2447,7 +2440,12 @@ namespace FourSlash {
|
||||
assert.isTrue(implementations && implementations.length > 0, "Expected at least one implementation but got 0");
|
||||
}
|
||||
else {
|
||||
assert.isUndefined(implementations, "Expected implementation list to be empty but implementations returned");
|
||||
if (this.testType === FourSlashTestType.Server) {
|
||||
assert.deepEqual(implementations, [], "Expected implementation list to be empty but implementations returned");
|
||||
}
|
||||
else {
|
||||
assert.isUndefined(implementations, "Expected implementation list to be empty but implementations returned");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -324,10 +324,6 @@ namespace FourSlashInterface {
|
||||
this.state.verifyGoToType(arg0, endMarkerName);
|
||||
}
|
||||
|
||||
public goToSourceDefinition(startMarkerNames: ArrayOrSingle<string>, end: { file: string } | ArrayOrSingle<string>) {
|
||||
this.state.verifyGoToSourceDefinition(startMarkerNames, end);
|
||||
}
|
||||
|
||||
public goToDefinitionForMarkers(...markerNames: string[]) {
|
||||
this.state.verifyGoToDefinitionForMarkers(markerNames);
|
||||
}
|
||||
|
||||
@@ -83,9 +83,6 @@ namespace ts.server.protocol {
|
||||
SignatureHelp = "signatureHelp",
|
||||
/* @internal */
|
||||
SignatureHelpFull = "signatureHelp-full",
|
||||
SourceDefinitionAndBoundSpan = "sourceDefinitionAndBoundSpan",
|
||||
/* @internal */
|
||||
SourceDefinitionAndBoundSpanFull = "sourceDefinitionAndBoundSpan-full",
|
||||
Status = "status",
|
||||
TypeDefinition = "typeDefinition",
|
||||
ProjectInfo = "projectInfo",
|
||||
@@ -907,10 +904,6 @@ namespace ts.server.protocol {
|
||||
readonly command: CommandTypes.DefinitionAndBoundSpan;
|
||||
}
|
||||
|
||||
export interface SourceDefinitionAndBoundSpanRequest extends FileLocationRequest {
|
||||
readonly command: CommandTypes.SourceDefinitionAndBoundSpan;
|
||||
}
|
||||
|
||||
export interface DefinitionAndBoundSpanResponse extends Response {
|
||||
readonly body: DefinitionInfoAndBoundSpan;
|
||||
}
|
||||
|
||||
+110
-141
@@ -1270,141 +1270,6 @@ namespace ts.server {
|
||||
};
|
||||
}
|
||||
|
||||
private getSourceDefinitionAndBoundSpan(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.DefinitionInfoAndBoundSpan | DefinitionInfoAndBoundSpan {
|
||||
const { file, project } = this.getFileAndProject(args);
|
||||
const position = this.getPositionInFile(args, file);
|
||||
const scriptInfo = Debug.checkDefined(project.getScriptInfo(file));
|
||||
|
||||
const unmappedDefinitionAndBoundSpan = project.getLanguageService().getDefinitionAndBoundSpan(file, position);
|
||||
|
||||
if (!unmappedDefinitionAndBoundSpan || !unmappedDefinitionAndBoundSpan.definitions) {
|
||||
return {
|
||||
definitions: emptyArray,
|
||||
textSpan: undefined! // TODO: GH#18217
|
||||
};
|
||||
}
|
||||
|
||||
let definitions = this.mapDefinitionInfoLocations(unmappedDefinitionAndBoundSpan.definitions, project).slice();
|
||||
const needsJsResolution = !some(definitions, d => !!d.isAliasTarget && !d.isAmbient) || some(definitions, d => !!d.failedAliasResolution);
|
||||
if (needsJsResolution) {
|
||||
project.withAuxiliaryProjectForFiles([file], auxiliaryProject => {
|
||||
const ls = auxiliaryProject.getLanguageService();
|
||||
const jsDefinitions = ls.getDefinitionAndBoundSpan(file, position, /*aliasesOnly*/ true);
|
||||
if (some(jsDefinitions?.definitions)) {
|
||||
for (const jsDefinition of jsDefinitions!.definitions) {
|
||||
pushIfUnique(definitions, jsDefinition, (a, b) => a.fileName === b.fileName && a.textSpan.start === b.textSpan.start);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const ambientCandidates = definitions.filter(d => d.isAliasTarget && d.isAmbient);
|
||||
for (const candidate of ambientCandidates) {
|
||||
const candidateFileName = getEffectiveFileNameOfDefinition(candidate, project.getLanguageService().getProgram()!);
|
||||
if (candidateFileName) {
|
||||
const fileNameToSearch = findImplementationFileFromDtsFileName(candidateFileName, file, auxiliaryProject);
|
||||
const scriptInfo = fileNameToSearch ? auxiliaryProject.getScriptInfo(fileNameToSearch) : undefined;
|
||||
if (!scriptInfo) {
|
||||
continue;
|
||||
}
|
||||
if (!auxiliaryProject.containsScriptInfo(scriptInfo)) {
|
||||
auxiliaryProject.addRoot(scriptInfo);
|
||||
}
|
||||
const auxiliaryProgram = auxiliaryProject.getLanguageService().getProgram()!;
|
||||
const fileToSearch = Debug.checkDefined(auxiliaryProgram.getSourceFile(fileNameToSearch!));
|
||||
const matches = FindAllReferences.Core.getTopMostDeclarationsInFile(candidate.name, fileToSearch);
|
||||
for (const match of matches) {
|
||||
const symbol = match.symbol || auxiliaryProgram.getTypeChecker().getSymbolAtLocation(match);
|
||||
if (symbol) {
|
||||
pushIfUnique(definitions, GoToDefinition.createDefinitionInfo(match, auxiliaryProgram.getTypeChecker(), symbol, match));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
definitions = definitions.filter(d => !d.isAmbient && !d.failedAliasResolution);
|
||||
const { textSpan } = unmappedDefinitionAndBoundSpan;
|
||||
|
||||
if (simplifiedResult) {
|
||||
return {
|
||||
definitions: this.mapDefinitionInfo(definitions, project),
|
||||
textSpan: toProtocolTextSpan(textSpan, scriptInfo)
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
definitions: definitions.map(Session.mapToOriginalLocation),
|
||||
textSpan,
|
||||
};
|
||||
|
||||
function getEffectiveFileNameOfDefinition(definition: DefinitionInfo, program: Program) {
|
||||
const sourceFile = program.getSourceFile(definition.fileName)!;
|
||||
const checker = program.getTypeChecker();
|
||||
const symbol = checker.getSymbolAtLocation(getTouchingPropertyName(sourceFile, definition.textSpan.start));
|
||||
if (symbol) {
|
||||
let parent = symbol.parent;
|
||||
while (parent && !isExternalModuleSymbol(parent)) {
|
||||
parent = parent.parent;
|
||||
}
|
||||
if (parent?.declarations && some(parent.declarations, isExternalModuleAugmentation)) {
|
||||
// Always CommonJS right now, but who knows in the future
|
||||
const mode = getModeForUsageLocation(sourceFile, find(parent.declarations, isExternalModuleAugmentation)!.name as StringLiteral);
|
||||
const fileName = sourceFile.resolvedModules?.get(stripQuotes(parent.name), mode)?.resolvedFileName;
|
||||
if (fileName) {
|
||||
return fileName;
|
||||
}
|
||||
}
|
||||
const fileName = tryCast(parent?.valueDeclaration, isSourceFile)?.fileName;
|
||||
if (fileName) {
|
||||
return fileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function findImplementationFileFromDtsFileName(fileName: string, resolveFromFile: string, auxiliaryProject: Project) {
|
||||
const nodeModulesPathParts = getNodeModulePathParts(fileName);
|
||||
if (nodeModulesPathParts && fileName.lastIndexOf(nodeModulesPathPart) === nodeModulesPathParts.topLevelNodeModulesIndex) {
|
||||
// Second check ensures the fileName only contains one `/node_modules/`. If there's more than one I give up.
|
||||
const packageDirectory = fileName.substring(0, nodeModulesPathParts.packageRootIndex);
|
||||
const packageJsonCache = project.getModuleResolutionCache()?.getPackageJsonInfoCache();
|
||||
const compilerOptions = project.getCompilationSettings();
|
||||
const packageJson = getPackageScopeForPath(project.toPath(packageDirectory + "/package.json"), packageJsonCache, project, compilerOptions);
|
||||
if (!packageJson) return undefined;
|
||||
// Use fake options instead of actual compiler options to avoid following export map if the project uses node12 or nodenext -
|
||||
// Mapping from an export map entry across packages is out of scope for now. Returned entrypoints will only be what can be
|
||||
// resolved from the package root under --moduleResolution node
|
||||
const entrypoints = getEntrypointsFromPackageJsonInfo(
|
||||
packageJson,
|
||||
{ moduleResolution: ModuleResolutionKind.NodeJs },
|
||||
project,
|
||||
project.getModuleResolutionCache());
|
||||
// This substring is correct only because we checked for a single `/node_modules/` at the top.
|
||||
const packageNamePathPart = fileName.substring(
|
||||
nodeModulesPathParts.topLevelPackageNameIndex + 1,
|
||||
nodeModulesPathParts.packageRootIndex);
|
||||
const packageName = getPackageNameFromTypesPackageName(unmangleScopedPackageName(packageNamePathPart));
|
||||
const path = project.toPath(fileName);
|
||||
if (entrypoints && some(entrypoints, e => project.toPath(e) === path)) {
|
||||
// This file was the main entrypoint of a package. Try to resolve that same package name with
|
||||
// the auxiliary project that only resolves to implementation files.
|
||||
const [implementationResolution] = auxiliaryProject.resolveModuleNames([packageName], resolveFromFile);
|
||||
return implementationResolution?.resolvedFileName;
|
||||
}
|
||||
else {
|
||||
// It wasn't the main entrypoint but we are in node_modules. Try a subpath into the package.
|
||||
const pathToFileInPackage = fileName.substring(nodeModulesPathParts.packageRootIndex + 1);
|
||||
const specifier = `${packageName}/${removeFileExtension(pathToFileInPackage)}`;
|
||||
const [implementationResolution] = auxiliaryProject.resolveModuleNames([specifier], resolveFromFile);
|
||||
return implementationResolution?.resolvedFileName;
|
||||
}
|
||||
}
|
||||
// We're not in node_modules, and we only get to this function if non-dts module resolution failed.
|
||||
// I'm not sure what else I can do here that isn't already covered by that module resolution.
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private getEmitOutput(args: protocol.EmitOutputRequestArgs): EmitOutput | protocol.EmitOutput {
|
||||
const { file, project } = this.getFileAndProject(args);
|
||||
if (!project.shouldEmitFile(project.getScriptInfo(file))) {
|
||||
@@ -1517,9 +1382,119 @@ namespace ts.server {
|
||||
const { file, project } = this.getFileAndProject(args);
|
||||
const position = this.getPositionInFile(args, file);
|
||||
const implementations = this.mapImplementationLocations(project.getLanguageService().getImplementationAtPosition(file, position) || emptyArray, project);
|
||||
|
||||
// const needsJsResolution = !some(definitions, d => !!d.isAliasTarget && !d.isAmbient) || some(definitions, d => !!d.failedAliasResolution);
|
||||
const needsJsResolution = !some(implementations);
|
||||
|
||||
if (needsJsResolution) {
|
||||
project.withAuxiliaryProjectForFiles([file], auxiliaryProject => {
|
||||
const ls = auxiliaryProject.getLanguageService();
|
||||
const jsDefinitions = ls.getDefinitionAndBoundSpan(file, position, /*aliasesOnly*/ true);
|
||||
if (some(jsDefinitions?.definitions)) {
|
||||
for (const jsDefinition of jsDefinitions!.definitions) {
|
||||
pushIfUnique(implementations, jsDefinition, (a, b) => a.fileName === b.fileName && a.textSpan.start === b.textSpan.start);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const ambientDefinitions = this.mapDefinitionInfoLocations(
|
||||
project.getLanguageService().getDefinitionAndBoundSpan(file, position, /*aliasesOnly*/ true)?.definitions || emptyArray,
|
||||
project,
|
||||
).filter(d => d.isAmbient && d.isAliasTarget);
|
||||
for (const candidate of ambientDefinitions) {
|
||||
const candidateFileName = getEffectiveFileNameOfDefinition(candidate, project.getLanguageService().getProgram()!);
|
||||
if (candidateFileName) {
|
||||
const fileNameToSearch = findImplementationFileFromDtsFileName(candidateFileName, file, auxiliaryProject);
|
||||
const scriptInfo = fileNameToSearch ? auxiliaryProject.getScriptInfo(fileNameToSearch) : undefined;
|
||||
if (!scriptInfo) {
|
||||
continue;
|
||||
}
|
||||
if (!auxiliaryProject.containsScriptInfo(scriptInfo)) {
|
||||
auxiliaryProject.addRoot(scriptInfo);
|
||||
}
|
||||
const auxiliaryProgram = auxiliaryProject.getLanguageService().getProgram()!;
|
||||
const fileToSearch = Debug.checkDefined(auxiliaryProgram.getSourceFile(fileNameToSearch!));
|
||||
const matches = FindAllReferences.Core.getTopMostDeclarationsInFile(candidate.name, fileToSearch);
|
||||
for (const match of matches) {
|
||||
const symbol = match.symbol || auxiliaryProgram.getTypeChecker().getSymbolAtLocation(match);
|
||||
if (symbol) {
|
||||
pushIfUnique(implementations, GoToDefinition.createDefinitionInfo(match, auxiliaryProgram.getTypeChecker(), symbol, match));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return simplifiedResult ?
|
||||
implementations.map(({ fileName, textSpan, contextSpan }) => this.toFileSpanWithContext(fileName, textSpan, contextSpan, project)) :
|
||||
implementations.map(Session.mapToOriginalLocation);
|
||||
|
||||
function getEffectiveFileNameOfDefinition(definition: DefinitionInfo, program: Program) {
|
||||
const sourceFile = program.getSourceFile(definition.fileName)!;
|
||||
const checker = program.getTypeChecker();
|
||||
const symbol = checker.getSymbolAtLocation(getTouchingPropertyName(sourceFile, definition.textSpan.start));
|
||||
if (symbol) {
|
||||
let parent = symbol.parent;
|
||||
while (parent && !isExternalModuleSymbol(parent)) {
|
||||
parent = parent.parent;
|
||||
}
|
||||
if (parent?.declarations && some(parent.declarations, isExternalModuleAugmentation)) {
|
||||
// Always CommonJS right now, but who knows in the future
|
||||
const mode = getModeForUsageLocation(sourceFile, find(parent.declarations, isExternalModuleAugmentation)!.name as StringLiteral);
|
||||
const fileName = sourceFile.resolvedModules?.get(stripQuotes(parent.name), mode)?.resolvedFileName;
|
||||
if (fileName) {
|
||||
return fileName;
|
||||
}
|
||||
}
|
||||
const fileName = tryCast(parent?.valueDeclaration, isSourceFile)?.fileName;
|
||||
if (fileName) {
|
||||
return fileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function findImplementationFileFromDtsFileName(fileName: string, resolveFromFile: string, auxiliaryProject: Project) {
|
||||
const nodeModulesPathParts = getNodeModulePathParts(fileName);
|
||||
if (nodeModulesPathParts && fileName.lastIndexOf(nodeModulesPathPart) === nodeModulesPathParts.topLevelNodeModulesIndex) {
|
||||
// Second check ensures the fileName only contains one `/node_modules/`. If there's more than one I give up.
|
||||
const packageDirectory = fileName.substring(0, nodeModulesPathParts.packageRootIndex);
|
||||
const packageJsonCache = project.getModuleResolutionCache()?.getPackageJsonInfoCache();
|
||||
const compilerOptions = project.getCompilationSettings();
|
||||
const packageJson = getPackageScopeForPath(project.toPath(packageDirectory + "/package.json"), packageJsonCache, project, compilerOptions);
|
||||
if (!packageJson) return undefined;
|
||||
// Use fake options instead of actual compiler options to avoid following export map if the project uses node12 or nodenext -
|
||||
// Mapping from an export map entry across packages is out of scope for now. Returned entrypoints will only be what can be
|
||||
// resolved from the package root under --moduleResolution node
|
||||
const entrypoints = getEntrypointsFromPackageJsonInfo(
|
||||
packageJson,
|
||||
{ moduleResolution: ModuleResolutionKind.NodeJs },
|
||||
project,
|
||||
project.getModuleResolutionCache());
|
||||
// This substring is correct only because we checked for a single `/node_modules/` at the top.
|
||||
const packageNamePathPart = fileName.substring(
|
||||
nodeModulesPathParts.topLevelPackageNameIndex + 1,
|
||||
nodeModulesPathParts.packageRootIndex);
|
||||
const packageName = getPackageNameFromTypesPackageName(unmangleScopedPackageName(packageNamePathPart));
|
||||
const path = project.toPath(fileName);
|
||||
if (entrypoints && some(entrypoints, e => project.toPath(e) === path)) {
|
||||
// This file was the main entrypoint of a package. Try to resolve that same package name with
|
||||
// the auxiliary project that only resolves to implementation files.
|
||||
const [implementationResolution] = auxiliaryProject.resolveModuleNames([packageName], resolveFromFile);
|
||||
return implementationResolution?.resolvedFileName;
|
||||
}
|
||||
else {
|
||||
// It wasn't the main entrypoint but we are in node_modules. Try a subpath into the package.
|
||||
const pathToFileInPackage = fileName.substring(nodeModulesPathParts.packageRootIndex + 1);
|
||||
const specifier = `${packageName}/${removeFileExtension(pathToFileInPackage)}`;
|
||||
const [implementationResolution] = auxiliaryProject.resolveModuleNames([specifier], resolveFromFile);
|
||||
return implementationResolution?.resolvedFileName;
|
||||
}
|
||||
}
|
||||
// We're not in node_modules, and we only get to this function if non-dts module resolution failed.
|
||||
// I'm not sure what else I can do here that isn't already covered by that module resolution.
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private getOccurrences(args: protocol.FileLocationRequestArgs): readonly protocol.OccurrencesResponseItem[] {
|
||||
@@ -2829,12 +2804,6 @@ namespace ts.server {
|
||||
[CommandNames.DefinitionAndBoundSpanFull]: (request: protocol.DefinitionAndBoundSpanRequest) => {
|
||||
return this.requiredResponse(this.getDefinitionAndBoundSpan(request.arguments, /*simplifiedResult*/ false));
|
||||
},
|
||||
[CommandNames.SourceDefinitionAndBoundSpan]: (request: protocol.SourceDefinitionAndBoundSpanRequest) => {
|
||||
return this.requiredResponse(this.getSourceDefinitionAndBoundSpan(request.arguments, /*simplifiedResult*/ true));
|
||||
},
|
||||
[CommandNames.SourceDefinitionAndBoundSpanFull]: (request: protocol.SourceDefinitionAndBoundSpanRequest) => {
|
||||
return this.requiredResponse(this.getSourceDefinitionAndBoundSpan(request.arguments, /*simplifiedResult*/ false));
|
||||
},
|
||||
[CommandNames.EmitOutput]: (request: protocol.EmitOutputRequest) => {
|
||||
return this.requiredResponse(this.getEmitOutput(request.arguments));
|
||||
},
|
||||
|
||||
@@ -347,7 +347,7 @@ namespace ts.CallHierarchy {
|
||||
return [];
|
||||
}
|
||||
const location = getCallHierarchyDeclarationReferenceNode(declaration);
|
||||
const calls = filter(FindAllReferences.findReferenceOrRenameEntries(program, cancellationToken, program.getSourceFiles(), location, /*position*/ 0, { use: FindAllReferences.FindReferencesUse.References }, convertEntryToCallSite), isDefined);
|
||||
const calls = filter(FindAllReferences.findReferenceOrRenameEntries(program, cancellationToken, program.getSourceFiles(), location, /*position*/ 0, /*sourceMapper*/ undefined, { use: FindAllReferences.FindReferencesUse.References }, convertEntryToCallSite), isDefined);
|
||||
return calls ? group(calls, getCallSiteGroupKey, entries => convertCallSiteGroupToIncomingCall(program, entries)) : [];
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace ts {
|
||||
|
||||
function getSemanticDocumentHighlights(position: number, node: Node, program: Program, cancellationToken: CancellationToken, sourceFilesToSearch: readonly SourceFile[]): DocumentHighlights[] | undefined {
|
||||
const sourceFilesSet = new Set(sourceFilesToSearch.map(f => f.fileName));
|
||||
const referenceEntries = FindAllReferences.getReferenceEntriesForNode(position, node, program, sourceFilesToSearch, cancellationToken, /*options*/ undefined, sourceFilesSet);
|
||||
const referenceEntries = FindAllReferences.getReferenceEntriesForNode(position, node, program, sourceFilesToSearch, cancellationToken, /*sourceMapper*/ undefined, /*options*/ undefined, sourceFilesSet);
|
||||
if (!referenceEntries) return undefined;
|
||||
const map = arrayToMultiMap(referenceEntries.map(FindAllReferences.toHighlightSpan), e => e.fileName, e => e.span);
|
||||
const getCanonicalFileName = createGetCanonicalFileName(program.useCaseSensitiveFileNames());
|
||||
|
||||
@@ -204,9 +204,9 @@ namespace ts.FindAllReferences {
|
||||
readonly providePrefixAndSuffixTextForRename?: boolean;
|
||||
}
|
||||
|
||||
export function findReferencedSymbols(program: Program, cancellationToken: CancellationToken, sourceFiles: readonly SourceFile[], sourceFile: SourceFile, position: number): ReferencedSymbol[] | undefined {
|
||||
export function findReferencedSymbols(program: Program, sourceMapper: SourceMapper, cancellationToken: CancellationToken, sourceFiles: readonly SourceFile[], sourceFile: SourceFile, position: number): ReferencedSymbol[] | undefined {
|
||||
const node = getTouchingPropertyName(sourceFile, position);
|
||||
const referencedSymbols = Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, { use: FindReferencesUse.References });
|
||||
const referencedSymbols = Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, sourceMapper, { use: FindReferencesUse.References });
|
||||
const checker = program.getTypeChecker();
|
||||
const symbol = checker.getSymbolAtLocation(node);
|
||||
return !referencedSymbols || !referencedSymbols.length ? undefined : mapDefined<SymbolAndEntries, ReferencedSymbol>(referencedSymbols, ({ definition, references }) =>
|
||||
@@ -217,10 +217,10 @@ namespace ts.FindAllReferences {
|
||||
});
|
||||
}
|
||||
|
||||
export function getImplementationsAtPosition(program: Program, cancellationToken: CancellationToken, sourceFiles: readonly SourceFile[], sourceFile: SourceFile, position: number): ImplementationLocation[] | undefined {
|
||||
export function getImplementationsAtPosition(program: Program, sourceMapper: SourceMapper, cancellationToken: CancellationToken, sourceFiles: readonly SourceFile[], sourceFile: SourceFile, position: number): ImplementationLocation[] | undefined {
|
||||
const node = getTouchingPropertyName(sourceFile, position);
|
||||
let referenceEntries: Entry[] | undefined;
|
||||
const entries = getImplementationReferenceEntries(program, cancellationToken, sourceFiles, node, position);
|
||||
const entries = getImplementationReferenceEntries(program, sourceMapper, cancellationToken, sourceFiles, node, position);
|
||||
|
||||
if (
|
||||
node.parent.kind === SyntaxKind.PropertyAccessExpression
|
||||
@@ -239,7 +239,7 @@ namespace ts.FindAllReferences {
|
||||
continue;
|
||||
}
|
||||
referenceEntries = append(referenceEntries, entry);
|
||||
const entries = getImplementationReferenceEntries(program, cancellationToken, sourceFiles, entry.node, entry.node.pos);
|
||||
const entries = getImplementationReferenceEntries(program, sourceMapper, cancellationToken, sourceFiles, entry.node, entry.node.pos);
|
||||
if (entries) {
|
||||
queue.push(...entries);
|
||||
}
|
||||
@@ -249,7 +249,7 @@ namespace ts.FindAllReferences {
|
||||
return map(referenceEntries, entry => toImplementationLocation(entry, checker));
|
||||
}
|
||||
|
||||
function getImplementationReferenceEntries(program: Program, cancellationToken: CancellationToken, sourceFiles: readonly SourceFile[], node: Node, position: number): readonly Entry[] | undefined {
|
||||
function getImplementationReferenceEntries(program: Program, sourceMapper: SourceMapper, cancellationToken: CancellationToken, sourceFiles: readonly SourceFile[], node: Node, position: number): readonly Entry[] | undefined {
|
||||
if (node.kind === SyntaxKind.SourceFile) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -270,15 +270,15 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
else {
|
||||
// Perform "Find all References" and retrieve only those that are implementations
|
||||
return getReferenceEntriesForNode(position, node, program, sourceFiles, cancellationToken, { implementations: true, use: FindReferencesUse.References });
|
||||
return getReferenceEntriesForNode(position, node, program, sourceFiles, cancellationToken, sourceMapper, { implementations: true, use: FindReferencesUse.References });
|
||||
}
|
||||
}
|
||||
|
||||
export function findReferenceOrRenameEntries<T>(
|
||||
program: Program, cancellationToken: CancellationToken, sourceFiles: readonly SourceFile[], node: Node, position: number, options: Options | undefined,
|
||||
program: Program, cancellationToken: CancellationToken, sourceFiles: readonly SourceFile[], node: Node, position: number, sourceMapper: SourceMapper | undefined, options: Options | undefined,
|
||||
convertEntry: ToReferenceOrRenameEntry<T>,
|
||||
): T[] | undefined {
|
||||
return map(flattenEntries(Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, options)), entry => convertEntry(entry, node, program.getTypeChecker()));
|
||||
return map(flattenEntries(Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, sourceMapper, options)), entry => convertEntry(entry, node, program.getTypeChecker()));
|
||||
}
|
||||
|
||||
export type ToReferenceOrRenameEntry<T> = (entry: Entry, originalNode: Node, checker: TypeChecker) => T;
|
||||
@@ -289,10 +289,11 @@ namespace ts.FindAllReferences {
|
||||
program: Program,
|
||||
sourceFiles: readonly SourceFile[],
|
||||
cancellationToken: CancellationToken,
|
||||
sourceMapper?: SourceMapper,
|
||||
options: Options = {},
|
||||
sourceFilesSet: ReadonlySet<string> = new Set(sourceFiles.map(f => f.fileName)),
|
||||
): readonly Entry[] | undefined {
|
||||
return flattenEntries(Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, options, sourceFilesSet));
|
||||
return flattenEntries(Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, sourceMapper, options, sourceFilesSet));
|
||||
}
|
||||
|
||||
function flattenEntries(referenceSymbols: readonly SymbolAndEntries[] | undefined): readonly Entry[] | undefined {
|
||||
@@ -621,7 +622,7 @@ namespace ts.FindAllReferences {
|
||||
/** Encapsulates the core find-all-references algorithm. */
|
||||
export namespace Core {
|
||||
/** Core find-all-references algorithm. Handles special cases before delegating to `getReferencedSymbolsForSymbol`. */
|
||||
export function getReferencedSymbolsForNode(position: number, node: Node, program: Program, sourceFiles: readonly SourceFile[], cancellationToken: CancellationToken, options: Options = {}, sourceFilesSet: ReadonlySet<string> = new Set(sourceFiles.map(f => f.fileName))): readonly SymbolAndEntries[] | undefined {
|
||||
export function getReferencedSymbolsForNode(position: number, node: Node, program: Program, sourceFiles: readonly SourceFile[], cancellationToken: CancellationToken, sourceMapper?: SourceMapper, options: Options = {}, sourceFilesSet: ReadonlySet<string> = new Set(sourceFiles.map(f => f.fileName))): readonly SymbolAndEntries[] | undefined {
|
||||
if (options.use === FindReferencesUse.References) {
|
||||
node = getAdjustedReferenceLocation(node);
|
||||
}
|
||||
@@ -682,16 +683,16 @@ namespace ts.FindAllReferences {
|
||||
return getReferencedSymbolsForModule(program, symbol.parent!, /*excludeImportTypeOfExportEquals*/ false, sourceFiles, sourceFilesSet);
|
||||
}
|
||||
|
||||
const moduleReferences = getReferencedSymbolsForModuleIfDeclaredBySourceFile(symbol, program, sourceFiles, cancellationToken, options, sourceFilesSet);
|
||||
const moduleReferences = getReferencedSymbolsForModuleIfDeclaredBySourceFile(symbol, program, sourceFiles, sourceMapper, cancellationToken, options, sourceFilesSet);
|
||||
if (moduleReferences && !(symbol.flags & SymbolFlags.Transient)) {
|
||||
return moduleReferences;
|
||||
}
|
||||
|
||||
const aliasedSymbol = getMergedAliasedSymbolOfNamespaceExportDeclaration(node, symbol, checker);
|
||||
const moduleReferencesOfExportTarget = aliasedSymbol &&
|
||||
getReferencedSymbolsForModuleIfDeclaredBySourceFile(aliasedSymbol, program, sourceFiles, cancellationToken, options, sourceFilesSet);
|
||||
getReferencedSymbolsForModuleIfDeclaredBySourceFile(aliasedSymbol, program, sourceFiles, sourceMapper, cancellationToken, options, sourceFilesSet);
|
||||
|
||||
const references = getReferencedSymbolsForSymbol(symbol, node, sourceFiles, sourceFilesSet, checker, cancellationToken, options);
|
||||
const references = getReferencedSymbolsForSymbol(symbol, node, sourceFiles, sourceFilesSet, checker, cancellationToken, sourceMapper, options);
|
||||
return mergeReferences(program, moduleReferences, references, moduleReferencesOfExportTarget);
|
||||
}
|
||||
|
||||
@@ -735,7 +736,7 @@ namespace ts.FindAllReferences {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getReferencedSymbolsForModuleIfDeclaredBySourceFile(symbol: Symbol, program: Program, sourceFiles: readonly SourceFile[], cancellationToken: CancellationToken, options: Options, sourceFilesSet: ReadonlySet<string>) {
|
||||
function getReferencedSymbolsForModuleIfDeclaredBySourceFile(symbol: Symbol, program: Program, sourceFiles: readonly SourceFile[], sourceMapper: SourceMapper | undefined, cancellationToken: CancellationToken, options: Options, sourceFilesSet: ReadonlySet<string>) {
|
||||
const moduleSourceFile = (symbol.flags & SymbolFlags.Module) && symbol.declarations && find(symbol.declarations, isSourceFile);
|
||||
if (!moduleSourceFile) return undefined;
|
||||
const exportEquals = symbol.exports!.get(InternalSymbolName.ExportEquals);
|
||||
@@ -745,7 +746,7 @@ namespace ts.FindAllReferences {
|
||||
// Continue to get references to 'export ='.
|
||||
const checker = program.getTypeChecker();
|
||||
symbol = skipAlias(exportEquals, checker);
|
||||
return mergeReferences(program, moduleReferences, getReferencedSymbolsForSymbol(symbol, /*node*/ undefined, sourceFiles, sourceFilesSet, checker, cancellationToken, options));
|
||||
return mergeReferences(program, moduleReferences, getReferencedSymbolsForSymbol(symbol, /*node*/ undefined, sourceFiles, sourceFilesSet, checker, cancellationToken, sourceMapper, options));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -918,13 +919,13 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
|
||||
/** Core find-all-references algorithm for a normal symbol. */
|
||||
function getReferencedSymbolsForSymbol(originalSymbol: Symbol, node: Node | undefined, sourceFiles: readonly SourceFile[], sourceFilesSet: ReadonlySet<string>, checker: TypeChecker, cancellationToken: CancellationToken, options: Options): SymbolAndEntries[] {
|
||||
function getReferencedSymbolsForSymbol(originalSymbol: Symbol, node: Node | undefined, sourceFiles: readonly SourceFile[], sourceFilesSet: ReadonlySet<string>, checker: TypeChecker, cancellationToken: CancellationToken, sourceMapper: SourceMapper | undefined, options: Options): SymbolAndEntries[] {
|
||||
const symbol = node && skipPastExportOrImportSpecifierOrUnion(originalSymbol, node, checker, /*useLocalSymbolForExportSpecifier*/ !isForRenameWithPrefixAndSuffixText(options)) || originalSymbol;
|
||||
|
||||
// Compute the meaning from the location and the symbol it references
|
||||
const searchMeaning = node ? getIntersectingMeaningFromDeclarations(node, symbol) : SemanticMeaning.All;
|
||||
const result: SymbolAndEntries[] = [];
|
||||
const state = new State(sourceFiles, sourceFilesSet, node ? getSpecialSearchKind(node) : SpecialSearchKind.None, checker, cancellationToken, searchMeaning, options, result);
|
||||
const state = new State(sourceFiles, sourceFilesSet, node ? getSpecialSearchKind(node) : SpecialSearchKind.None, checker, cancellationToken, sourceMapper, searchMeaning, options, result);
|
||||
|
||||
const exportSpecifier = !isForRenameWithPrefixAndSuffixText(options) || !symbol.declarations ? undefined : find(symbol.declarations, isExportSpecifier);
|
||||
if (exportSpecifier) {
|
||||
@@ -1065,6 +1066,7 @@ namespace ts.FindAllReferences {
|
||||
readonly specialSearchKind: SpecialSearchKind,
|
||||
readonly checker: TypeChecker,
|
||||
readonly cancellationToken: CancellationToken,
|
||||
readonly sourceMapper: SourceMapper | undefined,
|
||||
readonly searchMeaning: SemanticMeaning,
|
||||
readonly options: Options,
|
||||
private readonly result: Push<SymbolAndEntries>) {
|
||||
@@ -1803,7 +1805,7 @@ namespace ts.FindAllReferences {
|
||||
|
||||
function addImplementationReferences(refNode: Node, addReference: (node: Node) => void, state: State): void {
|
||||
// Check if we found a function/propertyAssignment/method with an implementation or initializer
|
||||
if (isDeclarationName(refNode) && isImplementation(refNode.parent)) {
|
||||
if (isDeclarationName(refNode) && isImplementation(refNode.parent, state.sourceMapper)) {
|
||||
addReference(refNode);
|
||||
return;
|
||||
}
|
||||
@@ -2295,8 +2297,14 @@ namespace ts.FindAllReferences {
|
||||
return meaning;
|
||||
}
|
||||
|
||||
function isImplementation(node: Node): boolean {
|
||||
return !(node.flags & NodeFlags.Ambient) && (
|
||||
function isImplementation(node: Node, sourceMapper: SourceMapper | undefined): boolean {
|
||||
if (node.flags & NodeFlags.Ambient) {
|
||||
if (!sourceMapper) return false;
|
||||
if (!isVariableLike(node) && !isFunctionLikeDeclaration(node) && !isClassLike(node) && !isModuleOrEnumDeclaration(node)) return false;
|
||||
const source = sourceMapper.tryGetSourcePosition({ fileName: node.getSourceFile().fileName, pos: node.pos });
|
||||
return !!source && !isDeclarationFileName(source.fileName);
|
||||
}
|
||||
return (
|
||||
(isVariableLike(node) ? hasInitializer(node) :
|
||||
isFunctionLikeDeclaration(node) ? !!node.body :
|
||||
isClassLike(node) || isModuleOrEnumDeclaration(node)));
|
||||
|
||||
@@ -1765,7 +1765,7 @@ namespace ts {
|
||||
|
||||
function getImplementationAtPosition(fileName: string, position: number): ImplementationLocation[] | undefined {
|
||||
synchronizeHostData();
|
||||
return FindAllReferences.getImplementationsAtPosition(program, cancellationToken, program.getSourceFiles(), getValidSourceFile(fileName), position);
|
||||
return FindAllReferences.getImplementationsAtPosition(program, sourceMapper, cancellationToken, program.getSourceFiles(), getValidSourceFile(fileName), position);
|
||||
}
|
||||
|
||||
/// References and Occurrences
|
||||
@@ -1827,12 +1827,12 @@ namespace ts {
|
||||
? program.getSourceFiles().filter(sourceFile => !program.isSourceFileDefaultLibrary(sourceFile))
|
||||
: program.getSourceFiles();
|
||||
|
||||
return FindAllReferences.findReferenceOrRenameEntries(program, cancellationToken, sourceFiles, node, position, options, cb);
|
||||
return FindAllReferences.findReferenceOrRenameEntries(program, cancellationToken, sourceFiles, node, position, sourceMapper, options, cb);
|
||||
}
|
||||
|
||||
function findReferences(fileName: string, position: number): ReferencedSymbol[] | undefined {
|
||||
synchronizeHostData();
|
||||
return FindAllReferences.findReferencedSymbols(program, cancellationToken, program.getSourceFiles(), getValidSourceFile(fileName), position);
|
||||
return FindAllReferences.findReferencedSymbols(program, sourceMapper, cancellationToken, program.getSourceFiles(), getValidSourceFile(fileName), position);
|
||||
}
|
||||
|
||||
function getFileReferences(fileName: string): ReferenceEntry[] {
|
||||
|
||||
@@ -317,8 +317,6 @@ declare namespace FourSlashInterface {
|
||||
goToDefinition(startsAndEnds: { [startMarkerName: string]: ArrayOrSingle<string> }): void;
|
||||
/** Verifies goToDefinition for each `${markerName}Reference` -> `${markerName}Definition` */
|
||||
goToDefinitionForMarkers(...markerNames: string[]): void;
|
||||
goToSourceDefinition(startMarkerNames: ArrayOrSingle<string>, fileResult: { file: string }): void;
|
||||
goToSourceDefinition(startMarkerNames: ArrayOrSingle<string>, endMarkerNames: ArrayOrSingle<string>): void;
|
||||
goToType(startsAndEnds: { [startMarkerName: string]: ArrayOrSingle<string> }): void;
|
||||
goToType(startMarkerNames: ArrayOrSingle<string>, endMarkerNames: ArrayOrSingle<string>): void;
|
||||
verifyGetEmitOutputForCurrentFile(expected: string): void;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/// <reference path="../fourslash.ts" />
|
||||
|
||||
// @Filename: /a.js
|
||||
//// export const /*end*/a = "a";
|
||||
//// export const [|a|] = "a";
|
||||
|
||||
// @Filename: /a.d.ts
|
||||
//// export declare const a: string;
|
||||
|
||||
// @Filename: /index.ts
|
||||
//// import { a } from "./a";
|
||||
//// [|a/*start*/|]
|
||||
//// a/*start*/
|
||||
|
||||
verify.goToSourceDefinition("start", "end");
|
||||
verify.allRangesAppearInImplementationList("start");
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
//// { "name": "foo", "version": "1.0.0", "main": "./lib/main.js", "types": "./types/main.d.ts" }
|
||||
|
||||
// @Filename: /node_modules/foo/lib/main.js
|
||||
//// export const /*end*/a = "a";
|
||||
//// export const [|a|] = "a";
|
||||
|
||||
// @Filename: /node_modules/foo/types/main.d.ts
|
||||
//// export declare const a: string;
|
||||
|
||||
// @Filename: /index.ts
|
||||
//// import { a } from "foo";
|
||||
//// [|a/*start*/|]
|
||||
//// a/*start*/
|
||||
|
||||
verify.goToSourceDefinition("start", "end");
|
||||
verify.allRangesAppearInImplementationList("start");
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
//// { "name": "foo", "version": "1.0.0", "main": "./lib/main.js" }
|
||||
|
||||
// @Filename: /node_modules/foo/lib/main.js
|
||||
//// export const /*end*/a = "a";
|
||||
//// export const [|a|] = "a";
|
||||
|
||||
// @Filename: /node_modules/@types/foo/package.json
|
||||
//// { "name": "@types/foo", "version": "1.0.0", "types": "./index.d.ts" }
|
||||
@@ -16,6 +16,6 @@
|
||||
|
||||
// @Filename: /index.ts
|
||||
//// import { a } from "foo";
|
||||
//// [|a/*start*/|]
|
||||
//// a/*start*/
|
||||
|
||||
verify.goToSourceDefinition("start", "end");
|
||||
verify.allRangesAppearInImplementationList("start");
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/// <reference path="../fourslash.ts" />
|
||||
|
||||
// @Filename: /index.ts
|
||||
//// import { a/*end*/ } from "./a";
|
||||
//// [|a/*start*/|]
|
||||
//// import { a } from "./a";
|
||||
//// a/*start*/
|
||||
|
||||
verify.goToDefinition("start", "end");
|
||||
verify.goToSourceDefinition("start", "end");
|
||||
goTo.marker("start");
|
||||
verify.implementationListIsEmpty();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/// <reference path="../fourslash.ts" />
|
||||
|
||||
// @Filename: /a.ts
|
||||
//// export const /*end*/a = 'a';
|
||||
//// export const [|a|] = 'a';
|
||||
|
||||
// @Filename: /a.d.ts
|
||||
//// export declare const a: string;
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
// @Filename: /b.ts
|
||||
//// import { a } from './a';
|
||||
//// [|a/*start*/|]
|
||||
//// a/*start*/
|
||||
|
||||
verify.goToDefinition("start", "end");
|
||||
verify.goToSourceDefinition("start", "end");
|
||||
verify.allRangesAppearInImplementationList("start");
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//// { "name": "foo", "version": "1.2.3", "typesVersions": { "*": { "*": ["./types/*"] } } }
|
||||
|
||||
// @Filename: /node_modules/foo/src/a.ts
|
||||
//// export const /*end*/a = 'a';
|
||||
//// export const [|a|] = 'a';
|
||||
|
||||
// @Filename: /node_modules/foo/types/a.d.ts
|
||||
//// export declare const a: string;
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
// @Filename: /b.ts
|
||||
//// import { a } from 'foo/a';
|
||||
//// [|a/*start*/|]
|
||||
//// a/*start*/
|
||||
|
||||
verify.goToDefinition("start", "end");
|
||||
verify.goToSourceDefinition("start", "end");
|
||||
verify.allRangesAppearInImplementationList("start");
|
||||
|
||||
@@ -15,19 +15,19 @@
|
||||
//// }
|
||||
|
||||
// @Filename: /node_modules/react/cjs/react.production.min.js
|
||||
//// 'use strict';exports./*production*/useState=function(a){};exports.version='16.8.6';
|
||||
//// 'use strict';exports.[|useState|]=function(a){};exports.version='16.8.6';
|
||||
|
||||
// @Filename: /node_modules/react/cjs/react.development.js
|
||||
//// 'use strict';
|
||||
//// if (process.env.NODE_ENV !== 'production') {
|
||||
//// (function() {
|
||||
//// function useState(initialState) {}
|
||||
//// exports./*development*/useState = useState;
|
||||
//// exports.[|useState|] = useState;
|
||||
//// exports.version = '16.8.6';
|
||||
//// }());
|
||||
//// }
|
||||
|
||||
// @Filename: /index.ts
|
||||
//// import { [|/*start*/useState|] } from 'react';
|
||||
//// import { /*start*/useState } from 'react';
|
||||
|
||||
verify.goToSourceDefinition("start", ["production", "development"]);
|
||||
verify.allRangesAppearInImplementationList("start");
|
||||
|
||||
@@ -22,12 +22,12 @@
|
||||
//// * _.add(6, 4);
|
||||
//// * // => 10
|
||||
//// */
|
||||
//// var [|/*variable*/add|] = createMathOperation(function(augend, addend) {
|
||||
//// var [|add|] = createMathOperation(function(augend, addend) {
|
||||
//// return augend + addend;
|
||||
//// }, 0);
|
||||
////
|
||||
//// function lodash(value) {}
|
||||
//// lodash.[|/*property*/add|] = add;
|
||||
//// lodash.[|add|] = add;
|
||||
////
|
||||
//// /** Detect free variable `global` from Node.js. */
|
||||
//// var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
|
||||
@@ -72,6 +72,6 @@
|
||||
//// }
|
||||
|
||||
// @Filename: /index.ts
|
||||
//// import { [|/*start*/add|] } from 'lodash';
|
||||
//// import { /*start*/add } from 'lodash';
|
||||
|
||||
verify.goToSourceDefinition("start", ["variable", "property"]);
|
||||
verify.allRangesAppearInImplementationList("start");
|
||||
|
||||
Reference in New Issue
Block a user