Skip to content

Commit

Permalink
getFileReferences request should work like find all references
Browse files Browse the repository at this point in the history
  • Loading branch information
sheetalkamat committed Sep 12, 2024
1 parent 4c7280c commit 639210a
Show file tree
Hide file tree
Showing 8 changed files with 655 additions and 810 deletions.
79 changes: 52 additions & 27 deletions src/server/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,8 @@ function getRenameLocationsWorker(
projects,
defaultProject,
initialLocation,
/*isForRename*/ true,
getDefinitionLocation(defaultProject, initialLocation, /*isForRename*/ true),
mapDefinitionInProject,
(project, position) => project.getLanguageService().findRenameLocations(position.fileName, position.pos, findInStrings, findInComments, preferences),
(renameLocation, cb) => cb(documentSpanLocation(renameLocation)),
);
Expand Down Expand Up @@ -570,7 +571,8 @@ function getReferencesWorker(
projects,
defaultProject,
initialLocation,
/*isForRename*/ false,
getDefinitionLocation(defaultProject, initialLocation, /*isForRename*/ false),
mapDefinitionInProject,
(project, position) => {
logger.info(`Finding references to ${position.fileName} position ${position.pos} in project ${project.getProjectName()}`);
return project.getLanguageService().findReferences(position.fileName, position.pos);
Expand Down Expand Up @@ -710,9 +712,15 @@ function getPerProjectReferences<TResult>(
projects: Projects,
defaultProject: Project,
initialLocation: DocumentPosition,
isForRename: boolean,
defaultDefinition: DocumentPosition | undefined,
mapDefinitionInProject: (
definition: DocumentPosition,
project: Project,
getGeneratedDefinition: () => DocumentPosition | undefined,
getSourceDefinition: () => DocumentPosition | undefined,
) => DocumentPosition | undefined,
getResultsForPosition: (project: Project, location: DocumentPosition) => readonly TResult[] | undefined,
forPositionInResult: (result: TResult, cb: (location: DocumentPosition) => void) => void,
forPositionInResult?: (result: TResult, cb: (location: DocumentPosition) => void) => void,
): readonly TResult[] | Map<Project, readonly TResult[]> {
// If `getResultsForPosition` returns results for a project, they go in here
const resultsMap = new Map<Project, readonly TResult[]>();
Expand All @@ -734,8 +742,6 @@ function getPerProjectReferences<TResult>(
const projectService = defaultProject.projectService;
const cancellationToken = defaultProject.getCancellationToken();

const defaultDefinition = getDefinitionLocation(defaultProject, initialLocation, isForRename);

// Don't call these unless !!defaultDefinition
const getGeneratedDefinition = memoize(() =>
defaultProject.isSourceOfProjectReferenceRedirect(defaultDefinition!.fileName) ?
Expand Down Expand Up @@ -801,7 +807,7 @@ function getPerProjectReferences<TResult>(

function searchPosition(project: Project, location: DocumentPosition): readonly TResult[] | undefined {
const projectResults = getResultsForPosition(project, location);
if (!projectResults) return undefined;
if (!projectResults || !forPositionInResult) return projectResults;

for (const result of projectResults) {
forPositionInResult(result, position => {
Expand Down Expand Up @@ -834,26 +840,35 @@ function getPerProjectReferences<TResult>(
}
}

function mapDefinitionInProject(
function mapDefinitionInProjectIfFileInProject(
definition: DocumentPosition,
project: Project,
getGeneratedDefinition: () => DocumentPosition | undefined,
getSourceDefinition: () => DocumentPosition | undefined,
): DocumentPosition | undefined {
) {
// If the definition is actually from the project, definition is correct as is
if (
project.containsFile(toNormalizedPath(definition.fileName)) &&
!isLocationProjectReferenceRedirect(project, definition)
) {
return definition;
}
}

function mapDefinitionInProject(
definition: DocumentPosition,
project: Project,
getGeneratedDefinition: () => DocumentPosition | undefined,
getSourceDefinition: () => DocumentPosition | undefined,
): DocumentPosition | undefined {
// If the definition is actually from the project, definition is correct as is
const result = mapDefinitionInProjectIfFileInProject(definition, project);
if (result) return result;
const generatedDefinition = getGeneratedDefinition();
if (generatedDefinition && project.containsFile(toNormalizedPath(generatedDefinition.fileName))) return generatedDefinition;
const sourceDefinition = getSourceDefinition();
return sourceDefinition && project.containsFile(toNormalizedPath(sourceDefinition.fileName)) ? sourceDefinition : undefined;
}

function isLocationProjectReferenceRedirect(project: Project, location: DocumentPosition | undefined) {
function isLocationProjectReferenceRedirect(project: Project, location: Pick<DocumentPosition, "fileName"> | undefined) {
if (!location) return false;
const program = project.getLanguageService().getProgram();
if (!program) return false;
Expand Down Expand Up @@ -2231,28 +2246,38 @@ export class Session<TMessage = string> implements EventSender {

private getFileReferences(args: protocol.FileRequestArgs, simplifiedResult: boolean): protocol.FileReferencesResponseBody | readonly ReferenceEntry[] {
const projects = this.getProjects(args);
const fileName = args.file;
const preferences = this.getPreferences(toNormalizedPath(fileName));

const references: ReferenceEntry[] = [];
const seen = createDocumentSpanSet(this.host.useCaseSensitiveFileNames);

// TODO:: sheetal: Are we suppose to do like find All references loading more projects:
// Eg test getFileReferences_deduplicate.ts and getFileReferences_server2.ts checks for file references from two projects
// but we dont open those projects by default any more for opening tsconfig.json file
forEachProjectInProjects(projects, /*path*/ undefined, project => {
if (project.getCancellationToken().isCancellationRequested()) return;
const fileName = toNormalizedPath(args.file);
const preferences = this.getPreferences(fileName);
const initialLocation: DocumentPosition = { fileName, pos: 0 };
const perProjectResults = getPerProjectReferences(
projects,
this.getDefaultProject(args),
initialLocation,
initialLocation,
mapDefinitionInProjectIfFileInProject,
project => {
this.logger.info(`Finding references to file ${fileName} in project ${project.getProjectName()}`);
return project.getLanguageService().getFileReferences(fileName);
},
);

const projectOutputs = project.getLanguageService().getFileReferences(fileName);
if (projectOutputs) {
// No re-mapping or isDefinition updatses are required if there's exactly one project
let references: ReferenceEntry[];
if (isArray(perProjectResults)) {
references = perProjectResults as ReferenceEntry[];
}
else {
references = [];
const seen = createDocumentSpanSet(this.host.useCaseSensitiveFileNames);
perProjectResults.forEach(projectOutputs => {
for (const referenceEntry of projectOutputs) {
if (!seen.has(referenceEntry)) {
references.push(referenceEntry);
seen.add(referenceEntry);
}
}
}
});
});
}

if (!simplifiedResult) return references;
const refs = references.map(entry => referenceEntryToReferencesResponseItem(this.projectService, entry, preferences));
Expand Down
Loading

0 comments on commit 639210a

Please sign in to comment.