public FileSystemSnapshot Compute(IFileSystemNameFactory fileNameFactory,
                                          FileSystemSnapshot oldSnapshot,
                                          FullPathChanges pathChanges /* may be null */,
                                          IList <IProject> projects,
                                          int version,
                                          CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested(); // cancellation
            using (var progress = _progressTrackerFactory.CreateIndeterminateTracker()) {
                // Clear file name factory intern tables if no projects. We could be
                // more aggressive at the expense of decreasing string interning.
                if (projects.Count == 0)
                {
                    fileNameFactory.ClearInternedStrings();
                }

                var projectRoots =
                    projects
                    .Distinct(new ProjectPathComparer())
                    .Select(project => {
                    cancellationToken.ThrowIfCancellationRequested(); // cancellation

                    var projectSnapshotBuilder = new ProjectRootSnapshotBuilder(
                        _fileSystem, fileNameFactory, oldSnapshot, project, progress,
                        (pathChanges == null ? null : new ProjectPathChanges(project.RootPath, pathChanges.Entries)),
                        cancellationToken);

                    var rootSnapshot = projectSnapshotBuilder.Build();

                    return(new ProjectRootSnapshot(project, rootSnapshot));
                })
                    .OrderBy(projectRoot => projectRoot.Directory.DirectoryName)
                    .ToReadOnlyCollection();

                return(new FileSystemSnapshot(version, projectRoots));
            }
        }