Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tune FAR aggregation #49581

Merged
merged 4 commits into from
Jun 16, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 26 additions & 16 deletions src/server/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ namespace ts.server {
// correct results to all other projects.

const defaultProjectResults = perProjectResults.get(defaultProject);
if (defaultProjectResults?.[0].references[0]?.isDefinition === undefined) {
if (defaultProjectResults?.[0]?.references[0]?.isDefinition === undefined) {
// Clear all isDefinition properties
perProjectResults.forEach(projectResults => {
for (const referencedSymbol of projectResults) {
Expand Down Expand Up @@ -531,26 +531,35 @@ namespace ts.server {
defaultDefinition :
defaultProject.getLanguageService().getSourceMapper().tryGetSourcePosition(defaultDefinition!));

// Track which projects we have already searched so that we don't repeat searches.
// We store the project key, rather than the project, because that's what `loadAncestorProjectTree` wants.
// (For that same reason, we don't use `resultsMap` for this check.)
const searchedProjects = new Set<string>();
// The keys of resultsMap allow us to check which projects have already been searched, but we also
// maintain a set of strings because that's what `loadAncestorProjectTree` wants.
const searchedProjectKeys = new Set<string>();

onCancellation:
while (queue.length) {
while (queue.length) {
if (cancellationToken.isCancellationRequested()) break onCancellation;

let skipCount = 0;
for (; skipCount < queue.length && resultsMap.has(queue[skipCount].project); skipCount++);
amcasey marked this conversation as resolved.
Show resolved Hide resolved

if (skipCount === queue.length) {
queue.length = 0;
break;
}

if (skipCount > 0) {
queue.splice(0, skipCount);
}

// NB: we may still skip if it's a project reference redirect
const { project, location } = queue.shift()!;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make more sense long-term not to de-queue at all? Just iterate through and toss the array at the end?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess not, then it would just get longer and longer.


if (isLocationProjectReferenceRedirect(project, location)) continue;

if (!tryAddToSet(searchedProjects, getProjectKey(project))) continue;

const projectResults = searchPosition(project, location);
if (projectResults) {
resultsMap.set(project, projectResults);
}
resultsMap.set(project, projectResults ?? emptyArray);
searchedProjectKeys.add(getProjectKey(project));
}

// At this point, we know about all projects passed in as arguments and any projects in which
Expand All @@ -559,10 +568,10 @@ namespace ts.server {
// containing `initialLocation`.
if (defaultDefinition) {
// This seems to mean "load all projects downstream from any member of `seenProjects`".
projectService.loadAncestorProjectTree(searchedProjects);
projectService.loadAncestorProjectTree(searchedProjectKeys);
projectService.forEachEnabledProject(project => {
if (cancellationToken.isCancellationRequested()) return; // There's no mechanism for skipping the remaining projects
if (searchedProjects.has(getProjectKey(project))) return; // Can loop forever without this (enqueue here, dequeue above, repeat)
if (resultsMap.has(project)) return; // Can loop forever without this (enqueue here, dequeue above, repeat)
const location = mapDefinitionInProject(defaultDefinition, project, getGeneratedDefinition, getSourceDefinition);
if (location) {
queue.push({ project, location });
Expand All @@ -573,9 +582,10 @@ namespace ts.server {

// In the common case where there's only one project, return a simpler result to make
// it easier for the caller to skip post-processing.
if (searchedProjects.size === 1) {
if (resultsMap.size === 1) {
const it = resultsMap.values().next();
return it.done ? emptyArray : it.value; // There may not be any results at all
Debug.assert(!it.done);
return it.value;
}

return resultsMap;
Expand All @@ -593,7 +603,7 @@ namespace ts.server {
const originalScriptInfo = projectService.getScriptInfo(originalLocation.fileName)!;

for (const project of originalScriptInfo.containingProjects) {
if (!project.isOrphan()) {
if (!project.isOrphan() && !resultsMap.has(project)) { // Optimization: don't enqueue if will be discarded
queue.push({ project, location: originalLocation });
}
}
Expand All @@ -602,7 +612,7 @@ namespace ts.server {
if (symlinkedProjectsMap) {
symlinkedProjectsMap.forEach((symlinkedProjects, symlinkedPath) => {
for (const symlinkedProject of symlinkedProjects) {
if (!symlinkedProject.isOrphan()) {
if (!symlinkedProject.isOrphan() && !resultsMap.has(symlinkedProject)) { // Optimization: don't enqueue if will be discarded
queue.push({ project: symlinkedProject, location: { fileName: symlinkedPath as string, pos: originalLocation.pos } });
}
}
Expand Down