private IEnumerable <PathData> EnumerateFiles(ExtendedState oldState, string rootDirectory, string directory) { if (Directory.Exists(directory)) { foreach (var d in Directory.EnumerateDirectories(directory)) { if (!this.IsCurrent(oldState)) { break; } foreach (var e in this.EnumerateFiles(oldState, rootDirectory, d)) { yield return(e); } } foreach (var f in Directory.EnumerateFiles(directory)) { if (!this.IsCurrent(oldState)) { break; } var path = new PathData(rootDirectory, f); if (oldState.PassesFilters(path.RelativePath)) { yield return(path); } } } }
private FileDifference CompareFiles(ExtendedState oldState, string left, string right) { try { using (var leftStream = new FileStream(left, FileMode.Open, FileAccess.Read, FileShare.Read)) { using (var rightStream = new FileStream(right, FileMode.Open, FileAccess.Read, FileShare.Read)) { bool areDifferentInWhiteSpace = false; bool leftWasWhiteSpace = true; bool rightWasWhiteSpace = true; bool skipRead = false; int l = -1; int r = -1; while (true) { if (!this.IsCurrent(oldState)) { return(FileDifference.Identical); } if (skipRead) { skipRead = false; } else { l = leftStream.ReadByte(); r = rightStream.ReadByte(); } if (l != r) { if (l == -1) { return(AreThereNonWhiteSpaceCharactersAtEndOfStream(r, rightStream) ? FileDifference.DifferentExcludingWhiteSpace : FileDifference.DifferentInWhiteSpaceOnly); } else if (r == -1) { return(AreThereNonWhiteSpaceCharactersAtEndOfStream(l, leftStream) ? FileDifference.DifferentExcludingWhiteSpace : FileDifference.DifferentInWhiteSpaceOnly); } areDifferentInWhiteSpace = true; bool leftIsWhiteSpace = char.IsWhiteSpace((char)l); bool rightIsWhiteSpace = char.IsWhiteSpace((char)r); if (!(leftIsWhiteSpace && rightIsWhiteSpace)) { if (leftIsWhiteSpace) { if (!rightWasWhiteSpace) { return(FileDifference.DifferentExcludingWhiteSpace); } skipRead = true; l = ConsumeWhitespace(leftStream); } else if (rightIsWhiteSpace) { if (!leftWasWhiteSpace) { return(FileDifference.DifferentExcludingWhiteSpace); } skipRead = true; r = ConsumeWhitespace(rightStream); } else { return(FileDifference.DifferentExcludingWhiteSpace); } } leftWasWhiteSpace = leftIsWhiteSpace; rightWasWhiteSpace = rightIsWhiteSpace; } else if (l == -1) { return(areDifferentInWhiteSpace ? FileDifference.DifferentInWhiteSpaceOnly : FileDifference.Identical); } else { leftWasWhiteSpace = rightWasWhiteSpace = char.IsWhiteSpace((char)l); } } } } } catch (Exception) { return(FileDifference.Identical); } }
private bool IsCurrent(ExtendedState state) { return(_state == state.State); }
private async Task CalculateDiffs() { var oldState = new ExtendedState(Volatile.Read(ref _state)); while (true) { await this.PostResults(null, "... <working> ..."); TreeNode root = new TreeNode(null, null, null, string.Empty, FileDifference.All); if (oldState.LeftDirectoryExists && oldState.RightDirectoryExists) { // Abandon the calculation if _state != oldState (which will cause the CompareExchange call below to fail and // we'll try and compute the differences on the new version of _state; var leftFiles = new List <PathData>(EnumerateFiles(oldState, oldState.LeftPath, oldState.LeftPath)); var rightFiles = new List <PathData>(EnumerateFiles(oldState, oldState.RightPath, oldState.RightPath)); var allRightFiles = new HashSet <string>(rightFiles.Select(p => p.RelativePath), StringComparer.OrdinalIgnoreCase); int count = 0; int leftIndex = 0; int rightIndex = 0; while (((leftIndex < leftFiles.Count) || (rightIndex < rightFiles.Count)) && this.IsCurrent(oldState)) { if (!this.IsCurrent(oldState)) { break; } var leftFile = (leftIndex < leftFiles.Count) ? leftFiles[leftIndex] : _empty; var rightFile = (rightIndex < rightFiles.Count) ? rightFiles[rightIndex] : _empty; if (StringComparer.OrdinalIgnoreCase.Equals(leftFile.RelativePath, rightFile.RelativePath)) { root.AddToTree(leftFiles[leftIndex].FullPath, rightFiles[rightIndex].FullPath, rightFile.RelativePath, CompareFiles(oldState, leftFiles[leftIndex].FullPath, rightFiles[rightIndex].FullPath), splitlabel: true); ++leftIndex; ++rightIndex; } else if ((leftIndex >= leftFiles.Count) || allRightFiles.Contains(leftFile.RelativePath)) { root.AddToTree(null, rightFiles[rightIndex].FullPath, rightFile.RelativePath + " (right only)", FileDifference.RightOnly, splitlabel: true); ++rightIndex; } else { root.AddToTree(leftFiles[leftIndex].FullPath, null, leftFile.RelativePath + " (left only)", FileDifference.LeftOnly, splitlabel: true); ++leftIndex; } if ((++count) % 100 == 0) { await this.PostResults(null, "... <working> (" + ((100 * (leftIndex + rightIndex)) / (leftFiles.Count + rightFiles.Count)).ToString() + "%) ..."); } } } else if (oldState.LeftFileExists && oldState.RightFileExists) { var leftFile = Path.GetFileName(oldState.LeftPath); var rightFile = Path.GetFileName(oldState.RightPath); var label = StringComparer.OrdinalIgnoreCase.Equals(leftFile, rightFile) ? rightFile : (leftFile + " " + rightFile); root.AddToTree(oldState.LeftPath, oldState.RightPath, label, CompareFiles(oldState, oldState.LeftPath, oldState.RightPath), splitlabel: true); } else { root.AddToTree(null, null, oldState.LeftPath + Suffix(oldState.LeftFileExists, oldState.LeftDirectoryExists), FileDifference.DifferentExcludingWhiteSpace, splitlabel: false); root.AddToTree(null, null, oldState.RightPath + Suffix(oldState.RightFileExists, oldState.RightDirectoryExists), FileDifference.DifferentExcludingWhiteSpace, splitlabel: false); } await this.PostResults(root, oldState.LeftPath + " .vs. " + oldState.RightPath); var result = Interlocked.CompareExchange(ref _state, null, oldState.State); if (result == oldState.State) { break; } oldState = new ExtendedState(result); } }
public ExtendedState(State state) { this.State = state; var leftPath = ExtendedState.GetFullPath(state.LeftPath); var rightPath = ExtendedState.GetFullPath(state.RightPath); if (Directory.Exists(leftPath)) { leftPath = ExtendedState.GetNormalizedDirectoryName(leftPath); this.LeftDirectoryExists = true; } else if (File.Exists(leftPath)) { this.LeftFileExists = true; } this.LeftPath = leftPath; if (Directory.Exists(rightPath)) { rightPath = ExtendedState.GetNormalizedDirectoryName(rightPath); this.RightDirectoryExists = true; } else if (File.Exists(rightPath)) { this.RightFileExists = true; } this.RightPath = rightPath; if (!string.IsNullOrWhiteSpace(state.Filters)) { foreach (var extension in state.Filters.Split(';')) { if (extension.Length != 0) { if (extension[0] == '-') { if (_excluded == null) { _excluded = new HashSet <string>(StringComparer.OrdinalIgnoreCase); } _excluded.Add(extension.Substring(1)); } else { if (_included == null) { _included = new HashSet <string>(StringComparer.OrdinalIgnoreCase); } _included.Add(extension); } } } } }