namespace tgcli.Tests; public class MessagePaging { [Theory] [InlineData(0)] [InlineData(76)] [InlineData(77)] [InlineData(147)] [InlineData(148)] [InlineData(218)] [InlineData(219)] [InlineData(289)] public void Test1(int offset) { const string testMessage = "this is a test string please ignore 1, this is a test string please ignore 2, this is a test string please ignore 3, this is a test string please ignore 4, this is a test string please ignore 5, this is a test string please ignore 6, this is a test string please ignore 7, this is a test str."; const int testBufferWidth = 80; Assert.Equal(ReferenceMethods.GetViewIntoMessageBuffer(testMessage, offset, testBufferWidth), Util.GetViewIntoMessageBuffer(testMessage, offset, testBufferWidth)); } private static class ReferenceMethods { internal static (string messageBuffer, int relativeCursorPosition) GetViewIntoMessageBuffer(string message, int absoluteCursorPosition, int bufferWidth) { const int wraparoundOffsetPre = 2; // number of "untouchable" characters moving the cursor onto will cause a wrap on the right screen edge const int wraparoundOffsetPost = 5; // number of "untouchable" characters moving the cursor onto will cause a wrap on the left screen edge const int wraparoundOffsetPreW = wraparoundOffsetPre + 1; // offset + 1 (character on the edge), for easier calculations const int wraparoundOffsetPostW = wraparoundOffsetPost + 1; // offset + 1 (character on the edge), for easier calculations if (absoluteCursorPosition > message.Length) throw new ArgumentOutOfRangeException(); if (message.Length < bufferWidth) return (message, absoluteCursorPosition); if (absoluteCursorPosition < bufferWidth - wraparoundOffsetPre - 1) return (Util.TruncateString(message, bufferWidth, $"{Util.Ansi.Inverse}>{Util.Ansi.InverseOff}"), absoluteCursorPosition); // now we can be sure the message needs at least one wrap // first wrap // get rid of the content shown on the zeroth wrap, which is buf width minus wraparoundPreW (respects > character on screen edge) var finalMessage = message[(bufferWidth - wraparoundOffsetPreW - wraparoundOffsetPost)..]; var finalCursorPos = absoluteCursorPosition - bufferWidth + wraparoundOffsetPreW + wraparoundOffsetPostW; // successive wraps // repeat above steps (but counting the new < character) until the string fits into the buffer // it fits into the buffer when cursorPos >= bufferwidth minus wraparound (this time respecting > character absent on first wrap) while (finalCursorPos >= bufferWidth - wraparoundOffsetPreW) { finalMessage = finalMessage[(bufferWidth - wraparoundOffsetPreW - wraparoundOffsetPostW)..]; finalCursorPos = finalCursorPos - bufferWidth + wraparoundOffsetPreW + wraparoundOffsetPostW; } finalMessage = Util.TruncateString(finalMessage, bufferWidth - 1, $"{Util.Ansi.Inverse}>{Util.Ansi.InverseOff}"); return ($"{Util.Ansi.Inverse}<{Util.Ansi.InverseOff}" + finalMessage, finalCursorPos); } } }