using System; using System.Text; namespace YesNt.CodeEditor; internal class InputHandler(TextEditor textEditor) { private readonly TextEditor textEditor = textEditor; public bool HandleInput() { while (Console.KeyAvailable) { _ = Console.ReadKey(true); } if (textEditor.EditMode == Mode.Edit) { ConsoleKeyInfo keyInfo = Console.ReadKey(true); if ((ConsoleModifiers.Alt & keyInfo.Modifiers) == ConsoleModifiers.Alt) { switch (keyInfo.Key) { case ConsoleKey.C: textEditor.EditMode = Mode.Command; return true; case ConsoleKey.B: int position = textEditor.Lines.Count; textEditor.CursorPosition.Y = position - 1; textEditor.LineOffset = Math.Max(position - Console.WindowHeight + 3, 0); textEditor.Display(true); return true; case ConsoleKey.T: textEditor.CursorPosition.Y = 0; textEditor.LineOffset = 0; textEditor.Display(true); return true; case ConsoleKey.S: textEditor.CursorPosition.X = 0; return true; case ConsoleKey.E: textEditor.CursorPosition.X = textEditor.Lines.Count > textEditor.CursorPosition.Y ? textEditor.Lines[textEditor.CursorPosition.Y].TrimEnd().Length : 0; return true; } } if (keyInfo.Key == ConsoleKey.DownArrow) { textEditor.CursorPosition.Y++; if (textEditor.CursorPosition.Y - textEditor.LineOffset >= Console.WindowHeight - 3) { textEditor.LineOffset++; textEditor.Display(true); } } else if (keyInfo.Key == ConsoleKey.UpArrow) { if (textEditor.CursorPosition.Y > 0) { textEditor.CursorPosition.Y--; if (textEditor.CursorPosition.Y - textEditor.LineOffset < 0 && textEditor.LineOffset > 0) { textEditor.LineOffset--; textEditor.Display(true); } } } else if (keyInfo.Key == ConsoleKey.LeftArrow) { if (textEditor.CursorPosition.X > 0) { textEditor.CursorPosition.X--; } } else if (keyInfo.Key == ConsoleKey.RightArrow) { if (textEditor.CursorPosition.X + textEditor.GetSpacing() + 3 < Console.WindowWidth) { textEditor.CursorPosition.X++; } } else if (keyInfo.Key == ConsoleKey.Enter) { while (textEditor.Lines.Count <= textEditor.CursorPosition.Y) { textEditor.Lines.Add(""); } textEditor.Lines.Insert(textEditor.CursorPosition.Y, ""); string line = textEditor.Lines[textEditor.CursorPosition.Y + 1]; textEditor.Lines[textEditor.CursorPosition.Y] = line[..Math.Min(textEditor.CursorPosition.X, line.Length)]; textEditor.Lines[textEditor.CursorPosition.Y + 1] = line[Math.Min(textEditor.CursorPosition.X, line.Length)..]; textEditor.CursorPosition.X = 0; textEditor.CursorPosition.Y++; if (textEditor.CursorPosition.Y - textEditor.LineOffset >= Console.WindowHeight - 3) { textEditor.LineOffset++; } textEditor.Display(true); } else { while (textEditor.Lines.Count <= textEditor.CursorPosition.Y) { textEditor.Lines.Add(""); } StringBuilder lineBuilder = new StringBuilder(textEditor.Lines[textEditor.CursorPosition.Y]); while (lineBuilder.Length <= textEditor.CursorPosition.X) { _ = lineBuilder.Append(' '); } textEditor.Lines[textEditor.CursorPosition.Y] = lineBuilder.ToString(); if (keyInfo.Key is ConsoleKey.Backspace or ConsoleKey.Delete) { if (keyInfo.Key == ConsoleKey.Delete) { textEditor.CursorPosition.X++; } if (textEditor.CursorPosition.X > 0) { if (textEditor.Lines[textEditor.CursorPosition.Y][textEditor.CursorPosition.X - 1] == ' ' && textEditor.CursorPosition.X > textEditor.Lines[textEditor.CursorPosition.Y].TrimEnd().Length) { textEditor.Lines[textEditor.CursorPosition.Y] = textEditor.Lines[textEditor.CursorPosition.Y].TrimEnd(); textEditor.CursorPosition.X = textEditor.Lines[textEditor.CursorPosition.Y].Length; } else { textEditor.Lines[textEditor.CursorPosition.Y] = textEditor.Lines[textEditor.CursorPosition.Y].Remove(textEditor.CursorPosition.X - 1, 1); textEditor.CursorPosition.X--; } } else { if (textEditor.CursorPosition.Y > 0) { if (string.IsNullOrWhiteSpace(textEditor.Lines[textEditor.CursorPosition.Y - 1])) { textEditor.Lines.RemoveAt(--textEditor.CursorPosition.Y); } else { textEditor.CursorPosition.Y--; textEditor.CursorPosition.X = textEditor.Lines[textEditor.CursorPosition.Y].TrimEnd().Length; textEditor.Lines[textEditor.CursorPosition.Y] += textEditor.Lines[textEditor.CursorPosition.Y + 1]; textEditor.Lines.RemoveAt(textEditor.CursorPosition.Y + 1); } if (textEditor.CursorPosition.Y - textEditor.LineOffset < 0 && textEditor.LineOffset > 0) { textEditor.LineOffset--; } textEditor.Display(true); } } while (textEditor.Lines.Count > 0 && string.IsNullOrWhiteSpace(textEditor.Lines[^1])) { textEditor.Lines.RemoveAt(textEditor.Lines.Count - 1); } } else if (textEditor.CursorPosition.X + textEditor.GetSpacing() + 3 < Console.WindowWidth) { char input = keyInfo.KeyChar; if (!char.IsControl(input)) { textEditor.Lines[textEditor.CursorPosition.Y] = textEditor.Lines[textEditor.CursorPosition.Y].Insert(textEditor.CursorPosition.X, input.ToString()); textEditor.CursorPosition.X++; } } } } else if (textEditor.EditMode == Mode.Command) { Console.SetCursorPosition(3, Console.WindowHeight - 2); string input = Console.ReadLine() ?? string.Empty; string command = input.Split(' ')[0].Trim(); string path; Console.CursorVisible = false; switch (command) { case "edit": WriteStatus(string.Empty); textEditor.EditMode = Mode.Edit; break; case "line": WriteStatus(string.Empty); bool success = false; int lineNumber = 0; if (input.Split(' ').Length == 2) { success = int.TryParse(input.Split(' ')[1], out lineNumber); } if (success && lineNumber > 0) { textEditor.CursorPosition.Y = lineNumber - 1; textEditor.CursorPosition.X = 0; textEditor.LineOffset = lineNumber - 1; textEditor.EditMode = Mode.Edit; } else { WriteStatus("Invalid line number!"); } break; case "save": _ = textEditor.Save(input, false); break; case "run": ExecuteWithDebugScreen(input, false, false); break; case "debug": if (TryParseDebugCommand(input, out bool stepMode, out string parsedPath)) { string saveInput = string.IsNullOrWhiteSpace(parsedPath) ? "debug" : $"debug {parsedPath}"; ExecuteWithDebugScreen(saveInput, true, stepMode); } else { WriteStatus("Invalid arguments!"); } break; case "load": if (input.Split(' ').Length == 2) { path = input.Split(' ')[1]; } else { WriteStatus("Invalid arguments!"); break; } try { _ = textEditor.Load(path); } catch (Exception ex) { WriteStatus(ex.Message); } textEditor.LineOffset = 0; textEditor.CursorPosition.X = 0; textEditor.CursorPosition.Y = 0; break; case "new": textEditor.LineOffset = 0; textEditor.CursorPosition.X = 0; textEditor.CursorPosition.Y = 0; textEditor.CurrentPath = string.Empty; textEditor.Lines.Clear(); WriteStatus(string.Empty); break; case "exit": return false; default: WriteStatus("Command not found!"); break; } textEditor.Display(true); } return true; } internal static void WriteStatus(string input) { Console.SetCursorPosition(0, Console.WindowHeight - 1); Console.Write(input + new string(' ', Console.WindowWidth - input.Length - 1)); } private static bool TryParseDebugCommand(string input, out bool stepMode, out string path) { stepMode = false; path = string.Empty; string[] parts = input.Split(' ', StringSplitOptions.RemoveEmptyEntries); if (parts.Length == 0 || !parts[0].Equals("debug", StringComparison.OrdinalIgnoreCase)) { return false; } for (int i = 1; i < parts.Length; i++) { string token = parts[i]; if (token.Equals("step", StringComparison.OrdinalIgnoreCase)) { if (stepMode) { return false; } stepMode = true; continue; } if (string.IsNullOrWhiteSpace(path)) { path = token; continue; } return false; } return true; } private void ExecuteWithDebugScreen(string saveInput, bool debugMode, bool stepMode) { if (textEditor.Save(saveInput, true)) { textEditor.EditMode = Mode.Debug; textEditor.IsStepDebugMode = stepMode; Console.Clear(); Console.CursorVisible = true; if (debugMode) { textEditor.YesNtInterpreter.Execute(textEditor.CurrentPath, true); } else { textEditor.YesNtInterpreter.Execute(textEditor.CurrentPath); } while (Console.KeyAvailable) { _ = Console.ReadKey(true); } _ = Console.ReadKey(); WriteStatus(string.Empty); textEditor.IsStepDebugMode = false; textEditor.EditMode = Mode.Command; } } }