This commit is contained in:
Laura Hausmann 2023-01-23 17:55:50 +01:00
parent 65d8141d18
commit f3372f39e4
Signed by: zotan
GPG key ID: D044E84C5BE01605
3 changed files with 52 additions and 57 deletions

View file

@ -10,45 +10,43 @@ public class PagedMessageInput {
[InlineData(218)]
[InlineData(219)]
[InlineData(289)]
public void Test1(int offset) {
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.GetViewIntoMessageBuffer(testMessage, offset, testBufferWidth),
Util.GetViewIntoMessageBuffer(testMessage, offset, testBufferWidth));
Assert.Equal(ReferenceMethods.GetPagedMessageInputLine(testMessage, offset, testBufferWidth), Util.GetPagedMessageInputLine(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
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 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
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 (absoluteCursorPosition > message.Length)
throw new ArgumentOutOfRangeException();
if (absCursorPos > message.Length)
throw new ArgumentOutOfRangeException(nameof(absCursorPos), "Cursor position exceeds message length");
if (message.Length < bufferWidth)
return (message, absoluteCursorPosition);
return (message, absCursorPos);
if (absoluteCursorPosition < bufferWidth - wraparoundOffsetPre - 1)
return (Util.TruncateString(message, bufferWidth, $"{Util.Ansi.Inverse}>{Util.Ansi.InverseOff}"), absoluteCursorPosition);
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 - wraparoundOffsetPreW - wraparoundOffsetPost)..];
var finalCursorPos = absoluteCursorPosition - bufferWidth + wraparoundOffsetPreW + wraparoundOffsetPostW;
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 - wraparoundOffsetPreW) {
finalMessage = finalMessage[(bufferWidth - wraparoundOffsetPreW - wraparoundOffsetPostW)..];
finalCursorPos = finalCursorPos - bufferWidth + wraparoundOffsetPreW + wraparoundOffsetPostW;
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}");

View file

@ -219,7 +219,7 @@ public static class Util {
}
else {
if (key.Key == ConsoleKey.Backspace && pass.Length > 0) {
pass = pass.Substring(0, (pass.Length - 1));
pass = pass[..^1];
Console.Write("\b \b");
}
else if (key.Key == ConsoleKey.Enter) {
@ -238,9 +238,7 @@ public static class Util {
Emojis.ForEach(em => message = message.Replace(em.Item1, em.Item2));
client.ExecuteAsync(new SendMessage {
ChatId = chatId,
InputMessageContent = new InputMessageContent.InputMessageText { Text = new FormattedText { Text = message } },
ReplyToMessageId = replyTo,
ChatId = chatId, InputMessageContent = new InputMessageContent.InputMessageText { Text = new FormattedText { Text = message } }, ReplyToMessageId = replyTo,
});
currentUserRead = false;
}
@ -271,8 +269,7 @@ public static class Util {
var results = client.ExecuteAsync(new SearchChats { Query = query, Limit = 5 }).Result;
return query.StartsWith("@")
? results.ChatIds.First(p => GetChat(p).Type is ChatType.ChatTypePrivate type
&& GetUser(type.UserId).Usernames.ActiveUsernames.Contains(query[1..]))
? results.ChatIds.First(p => GetChat(p).Type is ChatType.ChatTypePrivate type && GetUser(type.UserId).Usernames.ActiveUsernames.Contains(query[1..]))
: results.ChatIds.First(p => !(GetChat(p).Type is ChatType.ChatTypeSecret));
}
catch {
@ -369,31 +366,31 @@ public static class Util {
public static string TruncateString(string input, int maxLen, string truncateMarker = "~") {
if (maxLen < 2)
maxLen = 2;
return input.Length <= maxLen ? input : input.Substring(0, maxLen - 1) + truncateMarker;
return input.Length <= maxLen ? input : input[..(maxLen - 1)] + truncateMarker;
}
public 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
public static (string messageBuffer, int relCursorPos) GetPagedMessageInputLine(string message, int absCursorPos, int bufferWidth) {
const int wrapdOffsetPre = 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 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
const int wrapOffsetPreI = wrapdOffsetPre + 1; // offset + 1 (indicator on the edge), for easier calculations
const int wrapOffsetPostI = wrapOffsetPost + 1; // offset + 1 (indicator on the edge), for easier calculations
if (absoluteCursorPosition > message.Length)
throw new ArgumentOutOfRangeException(nameof(absoluteCursorPosition), "Cursor position exceeds message length");
if (absCursorPos > message.Length)
throw new ArgumentOutOfRangeException(nameof(absCursorPos), "Cursor position exceeds message length");
if (message.Length < bufferWidth) // entire message fits in buffer
return (message, absoluteCursorPosition); // return input as-is
if (message.Length < bufferWidth) // entire message fits in buffer
return (message, absCursorPos); // return input as-is
if (absoluteCursorPosition < bufferWidth - wraparoundOffsetPre - 1) // message is longer than buffer but we're on the first page
return (TruncateString(message, bufferWidth, $"{Ansi.Inverse}>{Ansi.InverseOff}"), absoluteCursorPosition); // return input as-is but truncated and with a > indicator
if (absCursorPos < bufferWidth - wrapdOffsetPre - 1) // message is longer than buffer but we're on the first page
return (TruncateString(message, bufferWidth, $"{Ansi.Inverse}>{Ansi.InverseOff}"), absCursorPos); // return input as-is but truncated and with a > indicator
var wraparounds = (absoluteCursorPosition - wraparoundOffsetPostW) / (bufferWidth - wraparoundOffsetPreW - wraparoundOffsetPostW);
var finalCursorPos = absoluteCursorPosition - bufferWidth + wraparoundOffsetPreW + wraparoundOffsetPostW * wraparounds;
finalCursorPos %= bufferWidth - wraparoundOffsetPreW;
var wraps = (absCursorPos - wrapOffsetPostI) / (bufferWidth - wrapOffsetPreI - wrapOffsetPostI); // black magic
var finalCursorPos = absCursorPos - bufferWidth + wrapOffsetPreI + wrapOffsetPostI * wraps; // respect the special case of the first page & add one post offset per wrap
finalCursorPos %= bufferWidth - wrapOffsetPreI; // make sure the final cursor position is within the acceptable range (between zero and bufWidth - wrapOffsetPreI)
var messageOffset = (bufferWidth - wraparoundOffsetPreW - wraparoundOffsetPostW) * wraparounds + 1; // +1 to account for the first wrap not having a < indicator
var finalMessage = message[messageOffset..];
var messageOffset = (bufferWidth - wrapOffsetPreI - wrapOffsetPostI) * wraps + 1; // +1 to account for the first wrap not having a < indicator
var finalMessage = message[messageOffset..]; // we only care about the message starting from the current page
finalMessage = TruncateString(finalMessage, bufferWidth - 1, $"{Ansi.Inverse}>{Ansi.InverseOff}"); // replace the last character with a > indicator if required
@ -413,8 +410,8 @@ public static class Util {
};
public static void InsertToInputLine(string strToInsert) {
var part1 = currentInputLine.Substring(0, currentInputPos);
var part2 = currentInputLine.Substring(currentInputPos);
var part1 = currentInputLine[..currentInputPos];
var part2 = currentInputLine[currentInputPos..];
currentInputLine = part1 + strToInsert + part2;
currentInputPos += strToInsert.Length;
}
@ -425,14 +422,14 @@ public static class Util {
}
public static void RemoveFromInputLine(bool word = false) {
var part1 = currentInputLine.Substring(0, currentInputPos);
var part1 = currentInputLine[..currentInputPos];
var oldlen = part1.Length;
var part2 = currentInputLine.Substring(currentInputPos);
var part2 = currentInputLine[currentInputPos..];
if (word) {
var lastIndex = part1.TrimEnd().LastIndexOf(" ", StringComparison.Ordinal);
if (lastIndex < 0)
lastIndex = 0;
part1 = part1.Substring(0, lastIndex);
part1 = part1[..lastIndex];
if (lastIndex != 0)
part1 += " ";
//if (part1.EndsWith("⏎"))
@ -445,18 +442,18 @@ public static class Util {
return;
}
currentInputLine = part1.Substring(0, part1.Length - 1) + part2;
currentInputLine = part1[..^1] + part2;
currentInputPos--;
}
public static void RemoveFromInputLineForward(bool word = false) {
var part1 = currentInputLine.Substring(0, currentInputPos);
var part2 = currentInputLine.Substring(currentInputPos).TrimStart();
var part1 = currentInputLine[..currentInputPos];
var part2 = currentInputLine[currentInputPos..].TrimStart();
if (word) {
var index = part2.IndexOf(" ", StringComparison.Ordinal);
if (index < 0)
index = part2.Length - 1;
part2 = part2.Substring(index + 1);
part2 = part2[(index + 1)..];
if (index != 0)
part2 = " " + part2;
//if (part2.StartsWith("⏎"))
@ -466,7 +463,7 @@ public static class Util {
return;
}
currentInputLine = part1 + part2.Substring(1);
currentInputLine = part1 + part2[1..];
}
public static readonly List<ConsoleKey> SpecialKeys = new() {

View file

@ -190,8 +190,8 @@ public static class tgcli {
else
output += "]";
output += " > ";
var prefixlen = GetActualStringWidth(output);
var inputLine = GetViewIntoMessageBuffer(currentInputLine, currentInputPos, Console.LargestWindowWidth - prefixlen);
var prefixlen = GetActualStringWidth(output);
var inputLine = GetPagedMessageInputLine(currentInputLine, currentInputPos, Console.LargestWindowWidth - prefixlen);
output += inputLine.messageBuffer;
ClearCurrentConsoleLine();
@ -200,7 +200,7 @@ public static class tgcli {
Console.Write("\a"); //ring terminal bell
messageQueue.Clear();
Console.Write(output);
Console.Write($"\u001b[{inputLine.relativeCursorPosition + prefixlen + 1}G");
Console.Write($"\u001b[{inputLine.relCursorPos + prefixlen + 1}G");
}
}
@ -212,7 +212,7 @@ public static class tgcli {
ScreenUpdate();
break;
case ConsoleKey.Enter when currentInputLine.StartsWith("/"): {
var command = currentInputLine.Substring(1);
var command = currentInputLine[1..];
SetInputLine("");
HandleCommand(command);
ScreenUpdate();
@ -257,7 +257,7 @@ public static class tgcli {
if (currentInputPos == 0)
break;
var part1 = currentInputLine.Substring(0, currentInputPos);
var part1 = currentInputLine[..currentInputPos];
var lastIndex = part1.TrimEnd().LastIndexOf(" ", StringComparison.Ordinal);
if (lastIndex < 0)
lastIndex = 0;