/// <summary> /// Enumerate directories and files under the project path of |projet|. /// </summary> private IEnumerable <TraversedDirectoryEntry> TraverseFileSystem( ProjectProcessingData data, DirectoryName startDirectoryName, bool isSymLink) { var stack = new Stack <DirectoryData>(); stack.Push(new DirectoryData(startDirectoryName, isSymLink)); while (stack.Count > 0) { var head = stack.Pop(); if (head.DirectoryName.IsAbsoluteName || data.Project.DirectoryFilter.Include(head.DirectoryName.RelativePath)) { var childEntries = _fileSystem.GetDirectoryEntries(data.Project.RootPath.Combine(head.DirectoryName.RelativePath)); var childFileNames = new List <FileName>(); // Note: Use "for" loop to avoid memory allocations. for (var i = 0; i < childEntries.Count; i++) { DirectoryEntry entry = childEntries[i]; if (entry.IsDirectory) { stack.Push(new DirectoryData(data.FileSystemNameFactory.CreateDirectoryName(head.DirectoryName, entry.Name), entry.IsSymLink)); } else if (entry.IsFile) { childFileNames.Add(data.FileSystemNameFactory.CreateFileName(head.DirectoryName, entry.Name)); } } yield return(new TraversedDirectoryEntry(head, childFileNames)); } } }
public FileSystemTreeSnapshot Compute( IFileSystemNameFactory fileNameFactory, FileSystemTreeSnapshot oldSnapshot, FullPathChanges pathChanges /* may be null */, List <FullPath> rootFiles, int version) { using (var progress = _progressTrackerFactory.CreateIndeterminateTracker()) { var projectRoots = rootFiles .Select(filename => _projectDiscovery.GetProject(filename)) .Where(project => project != null) .Distinct(new ProjectPathComparer()) .Select(project => { var data = new ProjectProcessingData { FileSystemNameFactory = fileNameFactory, Project = project, Progress = progress, OldSnapshot = oldSnapshot, PathChanges = (pathChanges == null ? null : new ProjectPathChanges(project.RootPath, pathChanges.Entries)), }; var rootSnapshot = ProcessProject(data); return(new ProjectRootSnapshot(project, rootSnapshot)); }) .OrderBy(projectRoot => projectRoot.Directory.DirectoryName) .ToReadOnlyCollection(); return(new FileSystemTreeSnapshot(version, projectRoots)); } }
private DirectorySnapshot ProcessProject(ProjectProcessingData data) { if (data.PathChanges != null) { // If we have a project with the same root in the old snapshot, use that // snapshot instead of traversing the file system. var oldRoot = data.OldSnapshot.ProjectRoots .FirstOrDefault(x => x.Project.RootPath.Equals(data.Project.RootPath)); if (oldRoot != null) { return(ApplyDirectorySnapshotDelta(data, oldRoot.Directory)); } } var projectPath = data.FileSystemNameFactory.CreateAbsoluteDirectoryName(data.Project.RootPath); return(CreateDirectorySnapshot(data, projectPath, false)); }
private DirectorySnapshot CreateDirectorySnapshot( ProjectProcessingData data, DirectoryName directory, bool isSymLink) { // Create list of pairs (DirectoryName, List[FileNames]) var directoriesWithFiles = TraverseFileSystem(data, directory, isSymLink) .AsParallel() .WithExecutionMode(ParallelExecutionMode.ForceParallelism) .Select(traversedDirectoryEntry => { var directoryName = traversedDirectoryEntry.DirectoryData.DirectoryName; if (data.Progress.Step()) { data.Progress.DisplayProgress((i, n) => string.Format("Traversing directory: {0}\\{1}", data.Project.RootPath.Value, directoryName.RelativePath.Value)); } var fileNames = traversedDirectoryEntry.ChildFileNames .Where(childFilename => data.Project.FileFilter.Include(childFilename.RelativePath)) .OrderBy(x => x.RelativePath) .ToReadOnlyCollection(); return(KeyValuePair.Create(traversedDirectoryEntry.DirectoryData, fileNames)); }) .ToList(); // We sort entries by directory name *descending* to make sure we process // directories bottom up, so that we know // 1) it is safe to skip DirectoryEntry instances where "Entries.Count" == 0, // 2) we create instances of child directories before their parent. directoriesWithFiles.Sort((x, y) => - x.Key.DirectoryName.RelativePath.CompareTo(y.Key.DirectoryName.RelativePath)); // Build map from parent directory -> list of child directories var directoriesToChildDirectories = new Dictionary <DirectoryName, List <DirectoryName> >(); directoriesWithFiles.ForAll(x => { var directoryName = x.Key; // Ignore root project directory name if (directoryName.DirectoryName.IsAbsoluteName) { return; } GetOrCreateList(directoriesToChildDirectories, directoryName.DirectoryName.Parent).Add(directoryName.DirectoryName); }); // Build directory snapshots for each directory entry, using an // intermediate map to enable connecting snapshots to their parent. var directoriesToSnapshot = new Dictionary <DirectoryName, DirectorySnapshot>(); var directorySnapshots = directoriesWithFiles.Select(entry => { var directoryElement = entry.Key; var childFilenames = entry.Value; var childDirectories = GetOrEmptyList(directoriesToChildDirectories, directoryElement.DirectoryName) .Select(x => directoriesToSnapshot[x]) .OrderBy(x => x.DirectoryName.RelativePath) .ToReadOnlyCollection(); // TODO(rpaquay): Not clear the lines below are a perf win, even though // they do not hurt correctness. // Remove children since we processed them //GetOrEmptyList(directoriesToChildDirectories, directoryName) // .ForAll(x => directoriesToSnapshot.Remove(x)); var result = new DirectorySnapshot(directoryElement, childDirectories, childFilenames); directoriesToSnapshot.Add(directoryElement.DirectoryName, result); return(result); }) .ToList(); // Since we sort directories by name descending, the last entry is always the // entry correcsponding to the project root. Debug.Assert(directorySnapshots.Count >= 1); Debug.Assert(directorySnapshots.Last().DirectoryName.Equals(directory)); return(directorySnapshots.Last()); }
private DirectorySnapshot ApplyDirectorySnapshotDelta( ProjectProcessingData data, DirectorySnapshot oldDirectory) { var oldDirectoryPath = oldDirectory.DirectoryName.RelativePath; // Create lists of created dirs and files. We havet to access the file system to know // if each path is a file or a directory. List <IFileInfoSnapshot> createDirs = null; List <IFileInfoSnapshot> createdFiles = null; foreach (var path in data.PathChanges.GetCreatedEntries(oldDirectoryPath).ToForeachEnum()) { var info = _fileSystem.GetFileInfoSnapshot(data.Project.RootPath.Combine(path)); if (info.IsDirectory) { if (createDirs == null) { createDirs = new List <IFileInfoSnapshot>(); } createDirs.Add(info); } else if (info.IsFile) { if (createdFiles == null) { createdFiles = new List <IFileInfoSnapshot>(); } createdFiles.Add(info); } } // Recursively create new directory entires for previous (non deleted) // entries. var childDirectories = oldDirectory.ChildDirectories .Where(dir => !data.PathChanges.IsDeleted(dir.DirectoryName.RelativePath)) .Select(dir => ApplyDirectorySnapshotDelta(data, dir)) .ToList(); // Add created directories if (createDirs != null) { foreach (var info in createDirs.ToForeachEnum()) { var name = data.FileSystemNameFactory.CreateDirectoryName(oldDirectory.DirectoryName, info.Path.FileName); var childSnapshot = CreateDirectorySnapshot(data, name, info.IsSymLink); // Note: File system change notifications are not always 100% // reliable. We may get a "create" event for directory we already know // about. var index = childDirectories.FindIndex(x => SystemPathComparer.Instance.StringComparer.Equals(x.DirectoryName.RelativePath.FileName, name.RelativePath.FileName)); if (index >= 0) { childDirectories.RemoveAt(index); } childDirectories.Add(childSnapshot); } // We need to re-sort the array since we added new entries childDirectories.Sort((x, y) => SystemPathComparer.Instance.StringComparer.Compare(x.DirectoryName.RelativePath.FileName, y.DirectoryName.RelativePath.FileName)); } // Match non deleted files // Sepcial case: if no file deleted or created, just re-use the list. IList <FileName> newFileList; if (data.PathChanges.GetDeletedEntries(oldDirectoryPath).Count == 0 && createdFiles == null) { newFileList = oldDirectory.ChildFiles; } else { // Copy the list of previous children, minus deleted files. var newFileListTemp = oldDirectory.ChildFiles .Where(x => !data.PathChanges.IsDeleted(x.RelativePath)) .ToList(); // Add created files if (createdFiles != null) { foreach (var info in createdFiles.ToForeachEnum()) { var name = data.FileSystemNameFactory.CreateFileName(oldDirectory.DirectoryName, info.Path.FileName); newFileListTemp.Add(name); } // We need to re-sort the array since we added new entries newFileListTemp.Sort((x, y) => SystemPathComparer.Instance.StringComparer.Compare(x.RelativePath.FileName, y.RelativePath.FileName)); // Note: File system change notifications are not always 100% // reliable. We may get a "create" event for files we already know // about. ArrayUtilities.RemoveDuplicates(newFileListTemp, (x, y) => SystemPathComparer.Instance.StringComparer.Equals(x.RelativePath.FileName, y.RelativePath.FileName)); } newFileList = newFileListTemp; } var newData = new DirectoryData(oldDirectory.DirectoryName, oldDirectory.IsSymLink); return(new DirectorySnapshot( newData, childDirectories.ToReadOnlyCollection(), newFileList.ToReadOnlyCollection())); }