public void NoLineMatchesFromNoLines() { var chunks = new DiffChunk[0]; var lines = new DiffLine[0]; var line = DiffUtilities.Match(chunks, lines); Assert.Null(line); }
IPullRequestSessionManager CreateSessionManager( string commitSha = "COMMIT", string relativePath = RelativePath, IPullRequestSession session = null, PullRequestTextBufferInfo textBufferInfo = null) { var thread = CreateThread(10, "Existing comment"); var diff = new DiffChunk { DiffLine = 10, OldLineNumber = 1, NewLineNumber = 1, }; for (var i = 0; i < 10; ++i) { diff.Lines.Add(new DiffLine { NewLineNumber = i, DiffLineNumber = i + 10, Type = i < 5 ? DiffChangeType.Delete : DiffChangeType.Add, }); } var file = Substitute.For <IPullRequestSessionFile>(); file.CommitSha.Returns(commitSha); file.Diff.Returns(new[] { diff }); file.InlineCommentThreads.Returns(new[] { thread }); file.LinesChanged.Returns(new Subject <IReadOnlyList <Tuple <int, DiffSide> > >()); session = session ?? CreateSession(); if (textBufferInfo != null) { session.GetFile(textBufferInfo.RelativePath, textBufferInfo.CommitSha).Returns(file); } var result = Substitute.For <IPullRequestSessionManager>(); result.CurrentSession.Returns(session); result.GetLiveFile(relativePath, Arg.Any <ITextView>(), Arg.Any <ITextBuffer>()).Returns(file); result.GetRelativePath(Arg.Any <ITextBuffer>()).Returns(relativePath); result.GetTextBufferInfo(Arg.Any <ITextBuffer>()).Returns(textBufferInfo); return(result); }
static IPullRequestSessionFile CreateSessionFile() { var diffChunk = new DiffChunk { Lines = { // Line numbers here are 1-based. There is an add diff entry on line 11 // and a delete entry on line 13. new DiffLine { Type = DiffChangeType.Add, NewLineNumber = 11 + 1 }, new DiffLine { Type = DiffChangeType.Delete, OldLineNumber = 13 + 1 }, } }; var diff = new List <DiffChunk> { diffChunk }; var rhsThread = Substitute.For <IInlineCommentThreadModel>(); rhsThread.DiffLineType.Returns(DiffChangeType.Add); rhsThread.LineNumber.Returns(10); var lhsThread = Substitute.For <IInlineCommentThreadModel>(); lhsThread.DiffLineType.Returns(DiffChangeType.Delete); lhsThread.LineNumber.Returns(12); // We have a comment to display on the right-hand-side of the diff view on line // 11 and a comment to display on line 13 on the left-hand-side. var threads = new List <IInlineCommentThreadModel> { rhsThread, lhsThread }; var file = Substitute.For <IPullRequestSessionFile>(); file.Diff.Returns(diff); file.InlineCommentThreads.Returns(threads); file.LinesChanged.Returns(new Subject <IReadOnlyList <Tuple <int, DiffSide> > >()); return(file); }
IPullRequestSessionManager CreateSessionManager(string commitSha = "COMMIT") { var thread = CreateThread(10, "Existing comment"); var diff = new DiffChunk { DiffLine = 10, OldLineNumber = 1, NewLineNumber = 1, }; for (var i = 0; i < 10; ++i) { diff.Lines.Add(new DiffLine { NewLineNumber = i, DiffLineNumber = i + 10, Type = i < 5 ? DiffChangeType.Delete : DiffChangeType.Add, }); } var file = Substitute.For <IPullRequestSessionFile>(); file.CommitSha.Returns(commitSha); file.Diff.Returns(new[] { diff }); file.InlineCommentThreads.Returns(new[] { thread }); file.LinesChanged.Returns(new Subject <IReadOnlyList <Tuple <int, DiffSide> > >()); var session = Substitute.For <IPullRequestSession>(); session.LocalRepository.CloneUrl.Returns(new UriString("https://foo.bar")); var result = Substitute.For <IPullRequestSessionManager>(); result.CurrentSession.Returns(session); result.GetLiveFile(RelativePath, Arg.Any <ITextView>(), Arg.Any <ITextBuffer>()).Returns(file); result.GetRelativePath(Arg.Any <ITextBuffer>()).Returns(RelativePath); return(result); }
/// <summary> /// Get a list of the operations that would make a terminal window look like this snapshot /// if you assume that beforehand it looked like the older snapshot you pass in. /// </summary> /// <param name="older">the older snapshot of a screen to diff from</param> /// <returns>the string that if output in order, will give you the desired changes</returns> public string DiffFrom(IScreenSnapShot older) { StringBuilder output = new StringBuilder(); int verticalScroll = TopRow - older.TopRow; int trackCursorColumn = older.CursorColumn; // track the movements that will occur as the outputs happen. int trackCursorRow = older.CursorRow; // track the movements that will occur as the outputs happen. // First, output the command to make the terminal scroll to match: if (verticalScroll > 0) // scrolling text up (eyeballs panning down) output.Append(new String((char)UnicodeCommand.SCROLLSCREENUPONE, verticalScroll)); // A run of scrollup chars else if (verticalScroll < 0) // scrolling text down (eyeballs panning up) output.Append(new String((char)UnicodeCommand.SCROLLSCREENDOWNONE, -verticalScroll)); // A run of scrolldown chars // Check each row: for (int newRowNum = 0 ; newRowNum < RowCount ; ++newRowNum) { // Account for the diff due to the scrolling: int oldRowNum = newRowNum + verticalScroll; IScreenBufferLine newRow = Buffer[newRowNum]; bool oldRowExists = (oldRowNum >= 0 && oldRowNum < older.Buffer.Count); IScreenBufferLine olderRow = oldRowExists ? older.Buffer[oldRowNum] : new ScreenBufferLine(0); // if new row is an empty dummy, then pad it out so it gets properly diffed against the old row: if (newRow.Length == 0) newRow = new ScreenBufferLine(olderRow.Length); // If the old row is a dummy pad or if the new row is newer than the old row, then it needs checking for diffs: if (olderRow.Length == 0 || newRow.LastChangeTick > olderRow.LastChangeTick) { List<DiffChunk> diffs = new List<DiffChunk>(); for (int newCol = 0 ; newCol < newRow.Length ; ++newCol) { // Check if they differ, but in a way that treats ' ' and 0x00 as identical, and treats a shorter // old row as if it has been padded with spaces: bool oldRowTooShort = newCol >= olderRow.Length; char newChar = (newRow[newCol] == '\0' ? ' ' : newRow[newCol]); char oldChar = (oldRowTooShort ? ' ' : (olderRow[newCol] == '\0' ? ' ' : olderRow[newCol])); if (newChar != oldChar) { // Start a new diff chunk if there isn't one yet, or the diff is a long enough distance from the existing one: if (diffs.Count == 0 || diffs[diffs.Count-1].EndCol < newCol - JOIN_DIFF_DIST) { DiffChunk newChunk = new DiffChunk { StartCol = newCol, EndCol = newCol }; diffs.Add(newChunk); } else // stretch the existing diff chunk to here. { diffs[diffs.Count-1].EndCol = newCol; } } } string newRowText = newRow.ToString(); foreach (DiffChunk diff in diffs) { /* comment-out ----------------- // If we're lucky enough that the current cursor happens to be right where we need it to // be, some of these are more efficient. Else it does TELEPORTCURSOR's: // Just one char removed to the left of current cursor pos - do a backspace instead of the ugly work: if (trackCursorRow == newRowNum && (diff.EndCol == trackCursorColumn - 1 && diff.StartCol == diff.EndCol)) { output.Append(String.Format("{0}{1}{2}", (char)0x08, newRowText.Substring(diff.StartCol,1), (char)0x08)); trackCursorColumn = diff.EndCol; trackCursorRow = newRowNum; } else { --------------------------- */ // If the change starts right where the cursor happens to be (i.e. when typing, one char // at current cursor position will typically be the only diff from one update to the next), // then don't bother moving the cursor first, else move it to the new pos: string moveString; if (trackCursorRow == newRowNum && diff.StartCol == trackCursorColumn) moveString = ""; else moveString = String.Format("{0}{1}{2}", (char)UnicodeCommand.TELEPORTCURSOR, (char)diff.StartCol, (char)newRowNum); // content = the bit of string to print at this location, with nulls made into spaces so the // telnet terminal will print correctly. string content = newRowText.Substring(diff.StartCol, diff.EndCol - diff.StartCol + 1).Replace('\0',' '); output.Append(String.Format("{0}{1}", moveString, content)); trackCursorColumn = diff.EndCol+1; trackCursorRow = newRowNum; /* comment-out ----------------- } --------------------------- */ } } } // Now set the cursor back to the right spot one more time, unless it's already there: if (trackCursorRow != CursorRow || trackCursorColumn != CursorColumn) { output.Append(String.Format("{0}{1}{2}", (char)UnicodeCommand.TELEPORTCURSOR, (char)CursorColumn, (char)CursorRow)); } return output.ToString(); }
static IPullRequestSessionFile CreateSessionFile(bool withComments = true, bool withAnnotations = false) { var diffChunk = new DiffChunk { Lines = { // Line numbers here are 1-based. There is an add diff entry on lines 11 and 21 // and a delete entry on line 13. new DiffLine { Type = DiffChangeType.Add, NewLineNumber = 11 + 1 }, new DiffLine { Type = DiffChangeType.Delete, OldLineNumber = 13 + 1 }, new DiffLine { Type = DiffChangeType.Add, NewLineNumber = 21 + 1 }, } }; var diff = new List <DiffChunk> { diffChunk }; var file = Substitute.For <IPullRequestSessionFile>(); file.Diff.Returns(diff); if (withComments) { var rhsThread = Substitute.For <IInlineCommentThreadModel>(); rhsThread.DiffLineType.Returns(DiffChangeType.Add); rhsThread.LineNumber.Returns(10); var lhsThread = Substitute.For <IInlineCommentThreadModel>(); lhsThread.DiffLineType.Returns(DiffChangeType.Delete); lhsThread.LineNumber.Returns(12); // We have a comment to display on the right-hand-side of the diff view on line // 11 and a comment to display on line 13 on the left-hand-side. var threads = new List <IInlineCommentThreadModel> { rhsThread, lhsThread }; file.InlineCommentThreads.Returns(threads); } if (withAnnotations) { var annotation1 = new InlineAnnotationModel(new CheckSuiteModel(), new CheckRunModel(), new CheckRunAnnotationModel() { EndLine = 11 }); var annotation2 = new InlineAnnotationModel(new CheckSuiteModel(), new CheckRunModel(), new CheckRunAnnotationModel() { EndLine = 21 }); var annotation3 = new InlineAnnotationModel(new CheckSuiteModel(), new CheckRunModel(), new CheckRunAnnotationModel() { EndLine = 21 }); var annotations = new List <InlineAnnotationModel> { annotation1, annotation2, annotation3 }; file.InlineAnnotations.Returns(annotations); } file.LinesChanged.Returns(new Subject <IReadOnlyList <Tuple <int, DiffSide> > >()); return(file); }
/// <summary> /// Get a list of the operations that would make a terminal window look like this snapshot /// if you assume that beforehand it looked like the older snapshot you pass in. /// </summary> /// <param name="older">the older snapshot of a screen to diff from</param> /// <returns>the string that if output in order, will give you the desired changes</returns> public string DiffFrom(IScreenSnapShot older) { StringBuilder output = new StringBuilder(); int verticalScroll = TopRow - older.TopRow; int trackCursorColumn = older.CursorColumn; // track the movements that will occur as the outputs happen. int trackCursorRow = older.CursorRow; // track the movements that will occur as the outputs happen. // First, output the command to make the terminal scroll to match: if (verticalScroll > 0) // scrolling text up (eyeballs panning down) { output.Append(new String((char)UnicodeCommand.SCROLLSCREENUPONE, verticalScroll)); // A run of scrollup chars } else if (verticalScroll < 0) // scrolling text down (eyeballs panning up) { output.Append(new String((char)UnicodeCommand.SCROLLSCREENDOWNONE, -verticalScroll)); // A run of scrolldown chars } // Check each row: for (int newRowNum = 0; newRowNum < RowCount; ++newRowNum) { // Account for the diff due to the scrolling: int oldRowNum = newRowNum + verticalScroll; IScreenBufferLine newRow = Buffer[newRowNum]; bool oldRowExists = (oldRowNum >= 0 && oldRowNum < older.Buffer.Count); IScreenBufferLine olderRow = oldRowExists ? older.Buffer[oldRowNum] : new ScreenBufferLine(0); // if new row is an empty dummy, then pad it out so it gets properly diffed against the old row: if (newRow.Length == 0) { newRow = new ScreenBufferLine(olderRow.Length); } // If the old row is a dummy pad or if the new row is newer than the old row, then it needs checking for diffs: if (olderRow.Length == 0 || newRow.LastChangeTick > olderRow.LastChangeTick) { List <DiffChunk> diffs = new List <DiffChunk>(); for (int newCol = 0; newCol < newRow.Length; ++newCol) { // Check if they differ, but in a way that treats ' ' and 0x00 as identical, and treats a shorter // old row as if it has been padded with spaces: bool oldRowTooShort = newCol >= olderRow.Length; char newChar = (newRow[newCol] == '\0' ? ' ' : newRow[newCol]); char oldChar = (oldRowTooShort ? ' ' : (olderRow[newCol] == '\0' ? ' ' : olderRow[newCol])); if (newChar != oldChar) { // Start a new diff chunk if there isn't one yet, or the diff is a long enough distance from the existing one: if (diffs.Count == 0 || diffs[diffs.Count - 1].EndCol < newCol - JOIN_DIFF_DIST) { DiffChunk newChunk = new DiffChunk { StartCol = newCol, EndCol = newCol }; diffs.Add(newChunk); } else // stretch the existing diff chunk to here. { diffs[diffs.Count - 1].EndCol = newCol; } } } string newRowText = newRow.ToString(); foreach (DiffChunk diff in diffs) { /* comment-out ----------------- * // If we're lucky enough that the current cursor happens to be right where we need it to * // be, some of these are more efficient. Else it does TELEPORTCURSOR's: * * // Just one char removed to the left of current cursor pos - do a backspace instead of the ugly work: * if (trackCursorRow == newRowNum && (diff.EndCol == trackCursorColumn - 1 && diff.StartCol == diff.EndCol)) * { * output.Append(String.Format("{0}{1}{2}", (char)0x08, newRowText.Substring(diff.StartCol,1), (char)0x08)); * trackCursorColumn = diff.EndCol; * trackCursorRow = newRowNum; * } * else * { * --------------------------- */ // If the change starts right where the cursor happens to be (i.e. when typing, one char // at current cursor position will typically be the only diff from one update to the next), // then don't bother moving the cursor first, else move it to the new pos: string moveString; if (trackCursorRow == newRowNum && diff.StartCol == trackCursorColumn) { moveString = ""; } else { moveString = String.Format("{0}{1}{2}", (char)UnicodeCommand.TELEPORTCURSOR, (char)diff.StartCol, (char)newRowNum); } // content = the bit of string to print at this location, with nulls made into spaces so the // telnet terminal will print correctly. string content = newRowText.Substring(diff.StartCol, diff.EndCol - diff.StartCol + 1).Replace('\0', ' '); output.Append(String.Format("{0}{1}", moveString, content)); trackCursorColumn = diff.EndCol + 1; trackCursorRow = newRowNum; /* comment-out ----------------- * } * --------------------------- */ } } } // Now set the cursor back to the right spot one more time, unless it's already there: if (trackCursorRow != CursorRow || trackCursorColumn != CursorColumn) { output.Append(String.Format("{0}{1}{2}", (char)UnicodeCommand.TELEPORTCURSOR, (char)CursorColumn, (char)CursorRow)); } return(output.ToString()); }