Refactor
This commit is contained in:
parent
65d8141d18
commit
f3372f39e4
|
@ -10,45 +10,43 @@ public class PagedMessageInput {
|
||||||
[InlineData(218)]
|
[InlineData(218)]
|
||||||
[InlineData(219)]
|
[InlineData(219)]
|
||||||
[InlineData(289)]
|
[InlineData(289)]
|
||||||
public void Test1(int offset) {
|
public void TestGetPagedMessageInput(int offset) {
|
||||||
const string testMessage =
|
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.";
|
"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;
|
const int testBufferWidth = 80;
|
||||||
Assert.Equal(ReferenceMethods.GetViewIntoMessageBuffer(testMessage, offset, testBufferWidth),
|
Assert.Equal(ReferenceMethods.GetPagedMessageInputLine(testMessage, offset, testBufferWidth), Util.GetPagedMessageInputLine(testMessage, offset, testBufferWidth));
|
||||||
Util.GetViewIntoMessageBuffer(testMessage, offset, testBufferWidth));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ReferenceMethods {
|
private static class ReferenceMethods {
|
||||||
internal static (string messageBuffer, int relativeCursorPosition)
|
internal static (string messageBuffer, int relCursorPos) GetPagedMessageInputLine(string message, int absCursorPos, int bufferWidth) {
|
||||||
GetViewIntoMessageBuffer(string message, int absoluteCursorPosition, 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 wraparoundOffsetPre = 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 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 wrapOffsetPreI = wrapOffsetPre + 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 wrapOffsetPostI = wrapOffsetPost + 1; // offset + 1 (character on the edge), for easier calculations
|
||||||
|
|
||||||
if (absoluteCursorPosition > message.Length)
|
if (absCursorPos > message.Length)
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException(nameof(absCursorPos), "Cursor position exceeds message length");
|
||||||
|
|
||||||
if (message.Length < bufferWidth)
|
if (message.Length < bufferWidth)
|
||||||
return (message, absoluteCursorPosition);
|
return (message, absCursorPos);
|
||||||
|
|
||||||
if (absoluteCursorPosition < bufferWidth - wraparoundOffsetPre - 1)
|
if (absCursorPos < bufferWidth - wrapOffsetPre - 1)
|
||||||
return (Util.TruncateString(message, bufferWidth, $"{Util.Ansi.Inverse}>{Util.Ansi.InverseOff}"), absoluteCursorPosition);
|
return (Util.TruncateString(message, bufferWidth, $"{Util.Ansi.Inverse}>{Util.Ansi.InverseOff}"), absCursorPos);
|
||||||
|
|
||||||
// now we can be sure the message needs at least one wrap
|
// now we can be sure the message needs at least one wrap
|
||||||
|
|
||||||
// first wrap
|
// first wrap
|
||||||
// get rid of the content shown on the zeroth wrap, which is buf width minus wraparoundPreW (respects > character on screen edge)
|
// 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 finalMessage = message[(bufferWidth - wrapOffsetPreI - wrapOffsetPost)..];
|
||||||
var finalCursorPos = absoluteCursorPosition - bufferWidth + wraparoundOffsetPreW + wraparoundOffsetPostW;
|
var finalCursorPos = absCursorPos - bufferWidth + wrapOffsetPreI + wrapOffsetPostI;
|
||||||
|
|
||||||
// successive wraps
|
// successive wraps
|
||||||
// repeat above steps (but counting the new < character) until the string fits into the buffer
|
// 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)
|
// it fits into the buffer when cursorPos >= bufferwidth minus wraparound (this time respecting > character absent on first wrap)
|
||||||
while (finalCursorPos >= bufferWidth - wraparoundOffsetPreW) {
|
while (finalCursorPos >= bufferWidth - wrapOffsetPreI) {
|
||||||
finalMessage = finalMessage[(bufferWidth - wraparoundOffsetPreW - wraparoundOffsetPostW)..];
|
finalMessage = finalMessage[(bufferWidth - wrapOffsetPreI - wrapOffsetPostI)..];
|
||||||
finalCursorPos = finalCursorPos - bufferWidth + wraparoundOffsetPreW + wraparoundOffsetPostW;
|
finalCursorPos = finalCursorPos - bufferWidth + wrapOffsetPreI + wrapOffsetPostI;
|
||||||
}
|
}
|
||||||
|
|
||||||
finalMessage = Util.TruncateString(finalMessage, bufferWidth - 1, $"{Util.Ansi.Inverse}>{Util.Ansi.InverseOff}");
|
finalMessage = Util.TruncateString(finalMessage, bufferWidth - 1, $"{Util.Ansi.Inverse}>{Util.Ansi.InverseOff}");
|
||||||
|
|
|
@ -219,7 +219,7 @@ public static class Util {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (key.Key == ConsoleKey.Backspace && pass.Length > 0) {
|
if (key.Key == ConsoleKey.Backspace && pass.Length > 0) {
|
||||||
pass = pass.Substring(0, (pass.Length - 1));
|
pass = pass[..^1];
|
||||||
Console.Write("\b \b");
|
Console.Write("\b \b");
|
||||||
}
|
}
|
||||||
else if (key.Key == ConsoleKey.Enter) {
|
else if (key.Key == ConsoleKey.Enter) {
|
||||||
|
@ -238,9 +238,7 @@ public static class Util {
|
||||||
|
|
||||||
Emojis.ForEach(em => message = message.Replace(em.Item1, em.Item2));
|
Emojis.ForEach(em => message = message.Replace(em.Item1, em.Item2));
|
||||||
client.ExecuteAsync(new SendMessage {
|
client.ExecuteAsync(new SendMessage {
|
||||||
ChatId = chatId,
|
ChatId = chatId, InputMessageContent = new InputMessageContent.InputMessageText { Text = new FormattedText { Text = message } }, ReplyToMessageId = replyTo,
|
||||||
InputMessageContent = new InputMessageContent.InputMessageText { Text = new FormattedText { Text = message } },
|
|
||||||
ReplyToMessageId = replyTo,
|
|
||||||
});
|
});
|
||||||
currentUserRead = false;
|
currentUserRead = false;
|
||||||
}
|
}
|
||||||
|
@ -271,8 +269,7 @@ public static class Util {
|
||||||
var results = client.ExecuteAsync(new SearchChats { Query = query, Limit = 5 }).Result;
|
var results = client.ExecuteAsync(new SearchChats { Query = query, Limit = 5 }).Result;
|
||||||
|
|
||||||
return query.StartsWith("@")
|
return query.StartsWith("@")
|
||||||
? results.ChatIds.First(p => GetChat(p).Type is ChatType.ChatTypePrivate type
|
? results.ChatIds.First(p => GetChat(p).Type is ChatType.ChatTypePrivate type && GetUser(type.UserId).Usernames.ActiveUsernames.Contains(query[1..]))
|
||||||
&& GetUser(type.UserId).Usernames.ActiveUsernames.Contains(query[1..]))
|
|
||||||
: results.ChatIds.First(p => !(GetChat(p).Type is ChatType.ChatTypeSecret));
|
: results.ChatIds.First(p => !(GetChat(p).Type is ChatType.ChatTypeSecret));
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
|
@ -369,31 +366,31 @@ public static class Util {
|
||||||
public static string TruncateString(string input, int maxLen, string truncateMarker = "~") {
|
public static string TruncateString(string input, int maxLen, string truncateMarker = "~") {
|
||||||
if (maxLen < 2)
|
if (maxLen < 2)
|
||||||
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) {
|
public static (string messageBuffer, int relCursorPos) GetPagedMessageInputLine(string message, int absCursorPos, 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 wrapdOffsetPre = 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 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 wrapOffsetPreI = wrapdOffsetPre + 1; // offset + 1 (indicator on the edge), for easier calculations
|
||||||
const int wraparoundOffsetPostW = wraparoundOffsetPost + 1; // offset + 1 (character on the edge), for easier calculations
|
const int wrapOffsetPostI = wrapOffsetPost + 1; // offset + 1 (indicator on the edge), for easier calculations
|
||||||
|
|
||||||
if (absoluteCursorPosition > message.Length)
|
if (absCursorPos > message.Length)
|
||||||
throw new ArgumentOutOfRangeException(nameof(absoluteCursorPosition), "Cursor position exceeds message length");
|
throw new ArgumentOutOfRangeException(nameof(absCursorPos), "Cursor position exceeds message length");
|
||||||
|
|
||||||
if (message.Length < bufferWidth) // entire message fits in buffer
|
if (message.Length < bufferWidth) // entire message fits in buffer
|
||||||
return (message, absoluteCursorPosition); // return input as-is
|
return (message, absCursorPos); // return input as-is
|
||||||
|
|
||||||
if (absoluteCursorPosition < bufferWidth - wraparoundOffsetPre - 1) // message is longer than buffer but we're on the first page
|
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}"), absoluteCursorPosition); // return input as-is but truncated and with a > indicator
|
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 wraps = (absCursorPos - wrapOffsetPostI) / (bufferWidth - wrapOffsetPreI - wrapOffsetPostI); // black magic
|
||||||
var finalCursorPos = absoluteCursorPosition - bufferWidth + wraparoundOffsetPreW + wraparoundOffsetPostW * wraparounds;
|
var finalCursorPos = absCursorPos - bufferWidth + wrapOffsetPreI + wrapOffsetPostI * wraps; // respect the special case of the first page & add one post offset per wrap
|
||||||
finalCursorPos %= bufferWidth - wraparoundOffsetPreW;
|
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 messageOffset = (bufferWidth - wrapOffsetPreI - wrapOffsetPostI) * wraps + 1; // +1 to account for the first wrap not having a < indicator
|
||||||
var finalMessage = message[messageOffset..];
|
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
|
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) {
|
public static void InsertToInputLine(string strToInsert) {
|
||||||
var part1 = currentInputLine.Substring(0, currentInputPos);
|
var part1 = currentInputLine[..currentInputPos];
|
||||||
var part2 = currentInputLine.Substring(currentInputPos);
|
var part2 = currentInputLine[currentInputPos..];
|
||||||
currentInputLine = part1 + strToInsert + part2;
|
currentInputLine = part1 + strToInsert + part2;
|
||||||
currentInputPos += strToInsert.Length;
|
currentInputPos += strToInsert.Length;
|
||||||
}
|
}
|
||||||
|
@ -425,14 +422,14 @@ public static class Util {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RemoveFromInputLine(bool word = false) {
|
public static void RemoveFromInputLine(bool word = false) {
|
||||||
var part1 = currentInputLine.Substring(0, currentInputPos);
|
var part1 = currentInputLine[..currentInputPos];
|
||||||
var oldlen = part1.Length;
|
var oldlen = part1.Length;
|
||||||
var part2 = currentInputLine.Substring(currentInputPos);
|
var part2 = currentInputLine[currentInputPos..];
|
||||||
if (word) {
|
if (word) {
|
||||||
var lastIndex = part1.TrimEnd().LastIndexOf(" ", StringComparison.Ordinal);
|
var lastIndex = part1.TrimEnd().LastIndexOf(" ", StringComparison.Ordinal);
|
||||||
if (lastIndex < 0)
|
if (lastIndex < 0)
|
||||||
lastIndex = 0;
|
lastIndex = 0;
|
||||||
part1 = part1.Substring(0, lastIndex);
|
part1 = part1[..lastIndex];
|
||||||
if (lastIndex != 0)
|
if (lastIndex != 0)
|
||||||
part1 += " ";
|
part1 += " ";
|
||||||
//if (part1.EndsWith("⏎"))
|
//if (part1.EndsWith("⏎"))
|
||||||
|
@ -445,18 +442,18 @@ public static class Util {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentInputLine = part1.Substring(0, part1.Length - 1) + part2;
|
currentInputLine = part1[..^1] + part2;
|
||||||
currentInputPos--;
|
currentInputPos--;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RemoveFromInputLineForward(bool word = false) {
|
public static void RemoveFromInputLineForward(bool word = false) {
|
||||||
var part1 = currentInputLine.Substring(0, currentInputPos);
|
var part1 = currentInputLine[..currentInputPos];
|
||||||
var part2 = currentInputLine.Substring(currentInputPos).TrimStart();
|
var part2 = currentInputLine[currentInputPos..].TrimStart();
|
||||||
if (word) {
|
if (word) {
|
||||||
var index = part2.IndexOf(" ", StringComparison.Ordinal);
|
var index = part2.IndexOf(" ", StringComparison.Ordinal);
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
index = part2.Length - 1;
|
index = part2.Length - 1;
|
||||||
part2 = part2.Substring(index + 1);
|
part2 = part2[(index + 1)..];
|
||||||
if (index != 0)
|
if (index != 0)
|
||||||
part2 = " " + part2;
|
part2 = " " + part2;
|
||||||
//if (part2.StartsWith("⏎"))
|
//if (part2.StartsWith("⏎"))
|
||||||
|
@ -466,7 +463,7 @@ public static class Util {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentInputLine = part1 + part2.Substring(1);
|
currentInputLine = part1 + part2[1..];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly List<ConsoleKey> SpecialKeys = new() {
|
public static readonly List<ConsoleKey> SpecialKeys = new() {
|
||||||
|
|
|
@ -190,8 +190,8 @@ public static class tgcli {
|
||||||
else
|
else
|
||||||
output += "]";
|
output += "]";
|
||||||
output += " > ";
|
output += " > ";
|
||||||
var prefixlen = GetActualStringWidth(output);
|
var prefixlen = GetActualStringWidth(output);
|
||||||
var inputLine = GetViewIntoMessageBuffer(currentInputLine, currentInputPos, Console.LargestWindowWidth - prefixlen);
|
var inputLine = GetPagedMessageInputLine(currentInputLine, currentInputPos, Console.LargestWindowWidth - prefixlen);
|
||||||
output += inputLine.messageBuffer;
|
output += inputLine.messageBuffer;
|
||||||
|
|
||||||
ClearCurrentConsoleLine();
|
ClearCurrentConsoleLine();
|
||||||
|
@ -200,7 +200,7 @@ public static class tgcli {
|
||||||
Console.Write("\a"); //ring terminal bell
|
Console.Write("\a"); //ring terminal bell
|
||||||
messageQueue.Clear();
|
messageQueue.Clear();
|
||||||
Console.Write(output);
|
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();
|
ScreenUpdate();
|
||||||
break;
|
break;
|
||||||
case ConsoleKey.Enter when currentInputLine.StartsWith("/"): {
|
case ConsoleKey.Enter when currentInputLine.StartsWith("/"): {
|
||||||
var command = currentInputLine.Substring(1);
|
var command = currentInputLine[1..];
|
||||||
SetInputLine("");
|
SetInputLine("");
|
||||||
HandleCommand(command);
|
HandleCommand(command);
|
||||||
ScreenUpdate();
|
ScreenUpdate();
|
||||||
|
@ -257,7 +257,7 @@ public static class tgcli {
|
||||||
if (currentInputPos == 0)
|
if (currentInputPos == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
var part1 = currentInputLine.Substring(0, currentInputPos);
|
var part1 = currentInputLine[..currentInputPos];
|
||||||
var lastIndex = part1.TrimEnd().LastIndexOf(" ", StringComparison.Ordinal);
|
var lastIndex = part1.TrimEnd().LastIndexOf(" ", StringComparison.Ordinal);
|
||||||
if (lastIndex < 0)
|
if (lastIndex < 0)
|
||||||
lastIndex = 0;
|
lastIndex = 0;
|
||||||
|
|
Loading…
Reference in a new issue