namespace tgcli.Tests; public class PagedMessageInput { [Theory] [InlineData(0)] [InlineData(76)] [InlineData(77)] [InlineData(147)] [InlineData(148)] [InlineData(218)] [InlineData(219)] [InlineData(289)] public void TestGetPagedMessageInput(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.GetPagedMessageInputLine(testMessage, offset, testBufferWidth), Util.GetPagedMessageInputLine(testMessage, offset, testBufferWidth)); } private static class ReferenceMethods { internal static (string messageBuffer, int relCursorPos) GetPagedMessageInputLine(string message, int absCursorPos, int bufferWidth) { const int wrapOffsetPre = 2; // number of "untouchable" characters moving the cursor onto will cause a wrap on the right screen edge const int wrapOffsetPost = 5; // number of "untouchable" characters moving the cursor onto will cause a wrap on the left screen edge const int wrapOffsetPreI = wrapOffsetPre + 1; // offset + 1 (character on the edge), for easier calculations const int wrapOffsetPostI = wrapOffsetPost + 1; // offset + 1 (character on the edge), for easier calculations if (absCursorPos > message.Length) throw new ArgumentOutOfRangeException(nameof(absCursorPos), "Cursor position exceeds message length"); if (message.Length < bufferWidth) return (message, absCursorPos); if (absCursorPos < bufferWidth - wrapOffsetPre - 1) return (Util.TruncateString(message, bufferWidth, $"{Util.Ansi.Inverse}>{Util.Ansi.InverseOff}"), absCursorPos); // 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 - wrapOffsetPreI - wrapOffsetPost)..]; var finalCursorPos = absCursorPos - bufferWidth + wrapOffsetPreI + wrapOffsetPostI; // 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 - wrapOffsetPreI) { finalMessage = finalMessage[(bufferWidth - wrapOffsetPreI - wrapOffsetPostI)..]; finalCursorPos = finalCursorPos - bufferWidth + wrapOffsetPreI + wrapOffsetPostI; } finalMessage = Util.TruncateString(finalMessage, bufferWidth - 1, $"{Util.Ansi.Inverse}>{Util.Ansi.InverseOff}"); return ($"{Util.Ansi.Inverse}<{Util.Ansi.InverseOff}" + finalMessage, finalCursorPos); } } }