diff --git a/tgcli.Tests/MessagePaging.cs b/tgcli.Tests/MessagePaging.cs new file mode 100644 index 0000000..75dcdee --- /dev/null +++ b/tgcli.Tests/MessagePaging.cs @@ -0,0 +1,59 @@ +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); + } + } +} diff --git a/tgcli.Tests/Usings.cs b/tgcli.Tests/Usings.cs new file mode 100644 index 0000000..8c927eb --- /dev/null +++ b/tgcli.Tests/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/tgcli.Tests/tgcli.Tests.csproj b/tgcli.Tests/tgcli.Tests.csproj new file mode 100644 index 0000000..8d938a3 --- /dev/null +++ b/tgcli.Tests/tgcli.Tests.csproj @@ -0,0 +1,28 @@ + + + + net7.0 + enable + enable + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/tgcli.sln b/tgcli.sln index 54ce9b7..1024b21 100644 --- a/tgcli.sln +++ b/tgcli.sln @@ -1,7 +1,10 @@  Microsoft Visual Studio Solution File, Format Version 12.00 +# Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tgcli", "tgcli\tgcli.csproj", "{26C5A85E-DDBB-4358-84A7-4A6A577428CB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tgcli.Tests", "tgcli.Tests\tgcli.Tests.csproj", "{2A95F0DD-72BF-4B43-BB81-E143A7C800FB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -12,5 +15,9 @@ Global {26C5A85E-DDBB-4358-84A7-4A6A577428CB}.Debug|Any CPU.Build.0 = Debug|Any CPU {26C5A85E-DDBB-4358-84A7-4A6A577428CB}.Release|Any CPU.ActiveCfg = Release|Any CPU {26C5A85E-DDBB-4358-84A7-4A6A577428CB}.Release|Any CPU.Build.0 = Release|Any CPU + {2A95F0DD-72BF-4B43-BB81-E143A7C800FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2A95F0DD-72BF-4B43-BB81-E143A7C800FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2A95F0DD-72BF-4B43-BB81-E143A7C800FB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2A95F0DD-72BF-4B43-BB81-E143A7C800FB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal