using Spectre.Console; namespace AnchorCli { internal class InputProcessor { private static void DisplayText(int left, string buffer, int index = -1, string placeholder = "", int viewportOffset = 0) { Console.CursorLeft = left; if (buffer.Length == 0 && index == 0) { AnsiConsole.Markup($"[grey dim]{placeholder}{new string(' ', Console.WindowWidth - 1 - left - placeholder.Length)}[/]"); return; } var visibleWidth = Console.WindowWidth - left - 1; var displayStart = Math.Min(viewportOffset, Math.Max(0, buffer.Length - 1)); var displayEnd = Math.Min(displayStart + visibleWidth, buffer.Length); var displayBuffer = string.Concat(buffer.AsSpan(displayStart, displayEnd - displayStart), " "); for (var i = 0; i < displayBuffer.Length; i++) { var actualIndex = displayStart + i; if (index != -1 && actualIndex == index) { Console.ForegroundColor = ConsoleColor.Black; Console.BackgroundColor = ConsoleColor.White; } Console.Write(displayBuffer[i]); if (index != -1 && actualIndex == index) Console.ResetColor(); } // Fill remaining space with spaces var remainingSpaces = visibleWidth - displayBuffer.Length; if (remainingSpaces > 0) Console.Write(new string(' ', remainingSpaces)); } public static string ReadLine(string placeholder = "") { Console.CursorVisible = false; var buffer = string.Empty; var index = 0; var viewportOffset = 0; var left = Console.CursorLeft; DisplayText(left, buffer, index, placeholder, viewportOffset); while (true) { var inputKey = Console.ReadKey(intercept: true); switch (inputKey) { case { Key: ConsoleKey.Enter } when buffer.Length > 0: DisplayText(left, buffer); Console.WriteLine(); return buffer; case { Key: ConsoleKey.Backspace } when index > 0: index--; buffer = buffer.Remove(index, 1); break; case { Key: ConsoleKey.Delete } when index < buffer.Length: buffer = buffer.Remove(index, 1); break; case { Key: ConsoleKey.LeftArrow, Modifiers: ConsoleModifiers.Control }: while (index > 0 && buffer[index - 1] == ' ') index--; while (index > 0 && buffer[index - 1] != ' ') index--; break; case { Key: ConsoleKey.RightArrow, Modifiers: ConsoleModifiers.Control }: while (index < buffer.Length && buffer[index] == ' ') index++; while (index < buffer.Length && buffer[index] != ' ') index++; break; case { Key: ConsoleKey.LeftArrow } when index > 0: index--; break; case { Key: ConsoleKey.RightArrow } when index < buffer.Length: index++; break; case { Key: ConsoleKey.W, Modifiers: ConsoleModifiers.Control } when index > 0: var deleteStart = index; while (deleteStart > 0 && buffer[deleteStart - 1] == ' ') deleteStart--; while (deleteStart > 0 && buffer[deleteStart - 1] != ' ') deleteStart--; var charsToDelete = index - deleteStart; buffer = buffer.Remove(deleteStart, charsToDelete); index = deleteStart; break; default: var keyChar = inputKey.KeyChar; if (!(char.IsLetterOrDigit(keyChar) || char.IsWhiteSpace(keyChar) || char.IsPunctuation(keyChar) || char.IsSymbol(keyChar))) break; buffer = buffer.Insert(index, inputKey.KeyChar.ToString()); index++; break; } // Adjust viewport for scrolling var visibleWidth = Console.WindowWidth - left - 1; if (index < viewportOffset) viewportOffset = index; else if (index >= viewportOffset + visibleWidth) viewportOffset = index - visibleWidth + 1; DisplayText(left, buffer, index, placeholder, viewportOffset); } } } }