From ce3715c1b5482bd92fee5b0b8abf78a9fe1f472b Mon Sep 17 00:00:00 2001 From: Stone_Red <56473591+Stone-Red-Code@users.noreply.github.com> Date: Wed, 27 Sep 2023 17:17:28 +0200 Subject: [PATCH] Reformatting and reimperilment escape codes --- YesNt.CodeEditor/Editor.cs | 446 +++++++------- YesNt.CodeEditor/InputHandler.cs | 552 +++++++++--------- YesNt.CodeEditor/Program.cs | 15 +- YesNt.CodeEditor/SyntaxHighlighter.cs | 13 +- YesNt.Interpreter.Tests/CodeFowTests.cs | 3 +- YesNt.Interpreter.Tests/YesNtAssert.cs | 22 + .../Statements/ProcessingStatements.cs | 27 +- .../Statements/VariableStatements.cs | 5 + .../Utilities/StringExtentions.cs | 51 +- 9 files changed, 591 insertions(+), 543 deletions(-) diff --git a/YesNt.CodeEditor/Editor.cs b/YesNt.CodeEditor/Editor.cs index 54c92cb..7c8c08b 100644 --- a/YesNt.CodeEditor/Editor.cs +++ b/YesNt.CodeEditor/Editor.cs @@ -4,265 +4,263 @@ using System.IO; using YesNt.Interpreter.Runtime; -namespace YesNt.CodeEditor +namespace YesNt.CodeEditor; + +internal class TextEditor { - internal class TextEditor + private readonly InputHandler inputHandler; + private readonly SyntaxHighlighter syntaxHighlighter; + private readonly List debugOutput = new(); + + private readonly Point oldSize = new Point(0, 0); + public YesNtInterpreter YesNtInterpreter { get; } = new(); + public int LineOffset { get; set; } = 0; + public List Lines { get; } = new(); + public Point CursorPosition { get; } = new(0, 0); + public Mode EditMode { get; set; } = Mode.Command; + public string CurrentPath { get; set; } = string.Empty; + + public TextEditor(string path) : this() { - private readonly InputHandler inputHandler; - private readonly SyntaxHighlighter syntaxHighlighter; - private readonly List debugOutput = new(); - - public YesNtInterpreter YesNtInterpreter { get; } = new(); - public int LineOffset { get; set; } = 0; - public List Lines { get; } = new(); - public Point CursorPosition { get; } = new(0, 0); - public Mode EditMode { get; set; } = Mode.Command; - public string CurrentPath { get; set; } = string.Empty; - - public TextEditor(string path) : this() + if (File.Exists(path)) { - if (File.Exists(path)) - { - _ = Load(path); - } + _ = Load(path); + } + } + + public TextEditor() + { + YesNtInterpreter.Initialize(); + YesNtInterpreter.OnDebugOutput += YesNtInterpreter_OnDebugOutput; + YesNtInterpreter.OnLineExecuted += YesNtInterpreter_OnLineExecuted; + syntaxHighlighter = new(YesNtInterpreter.StatementInformation); + inputHandler = new InputHandler(this); + Console.CancelKeyPress += Console_CancelKeyPress; + } + + public void Run() + { + Console.Clear(); + + Display(true); + do + { + Display(false); + } while (inputHandler.HandleInput()); + + Console.Clear(); + } + + public void Display(bool drawAll) + { + Console.CursorVisible = false; + Console.ForegroundColor = ConsoleColor.Gray; + Console.BackgroundColor = ConsoleColor.Black; + + Console.SetCursorPosition(0, 0); + + if (SizeChanged()) + { + drawAll = true; + InputHandler.WriteStatus(string.Empty); } - public TextEditor() + if (LineOffset < 0 || CursorPosition.Y < 0 || CursorPosition.Y < 0) { - YesNtInterpreter.Initialize(); - YesNtInterpreter.OnDebugOutput += YesNtInterpreter_OnDebugOutput; - YesNtInterpreter.OnLineExecuted += YesNtInterpreter_OnLineExecuted; - syntaxHighlighter = new(YesNtInterpreter.StatementInformation); - inputHandler = new InputHandler(this); - Console.CancelKeyPress += Console_CancelKeyPress; + LineOffset = 0; + CursorPosition.Y = 0; + CursorPosition.X = 0; } - public void Run() + for (int i = LineOffset; i < Console.WindowHeight + LineOffset - 2; i++) { - Console.Clear(); + Console.SetCursorPosition(0, i - LineOffset); - Display(true); - do + string lineCountString = $"{i + 1}".PadRight(GetSpacing(), ' ') + "| "; + if (i < Lines.Count) { - Display(false); - } while (inputHandler.HandleInput()); - - Console.Clear(); - } - - public void Display(bool drawAll) - { - Console.CursorVisible = false; - Console.ForegroundColor = ConsoleColor.Gray; - Console.BackgroundColor = ConsoleColor.Black; - - Console.SetCursorPosition(0, 0); - - if (SizeChanged()) - { - drawAll = true; - InputHandler.WriteStatus(string.Empty); - } - - if (LineOffset < 0 || CursorPosition.Y < 0 || CursorPosition.Y < 0) - { - LineOffset = 0; - CursorPosition.Y = 0; - CursorPosition.X = 0; - } - - for (int i = LineOffset; i < Console.WindowHeight + LineOffset - 2; i++) - { - Console.SetCursorPosition(0, i - LineOffset); - - string lineCountString = $"{i + 1}".PadRight(GetSpacing(), ' ') + "| "; - if (i < Lines.Count) + if (CursorPosition.Y == i || drawAll) { - if (CursorPosition.Y == i || drawAll) - { - Console.Write(lineCountString); - string printLine = $"{Lines[i][..Math.Min(Lines[i].Length, Console.WindowWidth)]}".TrimEnd(); - syntaxHighlighter.Write(printLine); - Console.Write(new string(' ', Math.Max(Console.WindowWidth - lineCountString.Length - printLine.Length, 0))); - } - } - else if (drawAll || CursorPosition.Y == i) - { - Console.Write(lineCountString + new string(' ', Console.WindowWidth - lineCountString.Length)); + Console.Write(lineCountString); + string printLine = $"{Lines[i][..Math.Min(Lines[i].Length, Console.WindowWidth)]}".TrimEnd(); + syntaxHighlighter.Write(printLine); + Console.Write(new string(' ', Math.Max(Console.WindowWidth - lineCountString.Length - printLine.Length, 0))); } } - - Console.SetCursorPosition(0, Console.WindowHeight - 3); - Console.Write(new string('-', Console.WindowWidth)); - Console.SetCursorPosition(0, Console.WindowHeight - 2); - Console.Write(">>>" + new string(' ', Console.WindowWidth - 3)); - - Console.SetCursorPosition(Math.Min(CursorPosition.X + GetSpacing() + 2, Console.WindowWidth - 1), CursorPosition.Y - LineOffset); - - Console.CursorVisible = true; + else if (drawAll || CursorPosition.Y == i) + { + Console.Write(lineCountString + new string(' ', Console.WindowWidth - lineCountString.Length)); + } } - public bool Load(string path) + Console.SetCursorPosition(0, Console.WindowHeight - 3); + Console.Write(new string('-', Console.WindowWidth)); + Console.SetCursorPosition(0, Console.WindowHeight - 2); + Console.Write(">>>" + new string(' ', Console.WindowWidth - 3)); + + Console.SetCursorPosition(Math.Min(CursorPosition.X + GetSpacing() + 2, Console.WindowWidth - 1), CursorPosition.Y - LineOffset); + + Console.CursorVisible = true; + } + + public bool Load(string path) + { + if (string.IsNullOrEmpty(Path.GetExtension(path))) { + path = Path.ChangeExtension(path, "ynt"); + } + + if (!File.Exists(path)) + { + InputHandler.WriteStatus("File does not exist!"); + return false; + } + + Lines.Clear(); + Lines.AddRange(File.ReadAllLines(path)); + CurrentPath = path; + InputHandler.WriteStatus("File Loaded!"); + return true; + } + + public bool Save(string input, bool loadIfExists) + { + string path; + if (input.Split(' ').Length == 2) + { + path = input.Split(' ')[1]; + + if (CurrentPath.Trim() != path.Trim() && loadIfExists) + { + return Load(path); + } if (string.IsNullOrEmpty(Path.GetExtension(path))) { path = Path.ChangeExtension(path, "ynt"); } - - if (!File.Exists(path)) - { - InputHandler.WriteStatus("File does not exist!"); - return false; - } - - Lines.Clear(); - Lines.AddRange(File.ReadAllLines(path)); CurrentPath = path; - InputHandler.WriteStatus("File Loaded!"); + } + else if (!string.IsNullOrWhiteSpace(CurrentPath)) + { + path = CurrentPath; + } + else if (input.Split(' ').Length > 2) + { + InputHandler.WriteStatus("Invalid arguments!"); + return false; + } + else + { + InputHandler.WriteStatus("File path is empty! (Save the file before you can use this command)"); + return false; + } + + if (string.IsNullOrEmpty(Path.GetExtension(path))) + { + path = Path.ChangeExtension(path, "ynt"); + } + + while (Lines.Count > 0 && string.IsNullOrWhiteSpace(Lines[^1])) + { + Lines.RemoveAt(Lines.Count - 1); + } + + try + { + File.WriteAllLines(path, Lines); + InputHandler.WriteStatus("File Saved!"); return true; } - - public bool Save(string input, bool loadIfExists) + catch (Exception ex) { - string path; - if (input.Split(' ').Length == 2) - { - path = input.Split(' ')[1]; - - if (CurrentPath.Trim() != path.Trim() && loadIfExists) - { - return Load(path); - } - if (string.IsNullOrEmpty(Path.GetExtension(path))) - { - path = Path.ChangeExtension(path, "ynt"); - } - CurrentPath = path; - } - else if (!string.IsNullOrWhiteSpace(CurrentPath)) - { - path = CurrentPath; - } - else if (input.Split(' ').Length > 2) - { - InputHandler.WriteStatus("Invalid arguments!"); - return false; - } - else - { - InputHandler.WriteStatus("File path is empty! (Save the file before you can use this command)"); - return false; - } - - if (string.IsNullOrEmpty(Path.GetExtension(path))) - { - path = Path.ChangeExtension(path, "ynt"); - } - - while (Lines.Count > 0 && string.IsNullOrWhiteSpace(Lines[^1])) - { - Lines.RemoveAt(Lines.Count - 1); - } - - try - { - File.WriteAllLines(path, Lines); - InputHandler.WriteStatus("File Saved!"); - return true; - } - catch (Exception ex) - { - InputHandler.WriteStatus(ex.Message); - return false; - } - } - - private void YesNtInterpreter_OnDebugOutput(string output) - { - debugOutput.Add(output); - } - - private void YesNtInterpreter_OnLineExecuted(Interpreter.Runtime.DebugEventArgs e) - { - lock (Console.Out) - { - if (e is not null) - { - Console.ForegroundColor = ConsoleColor.Magenta; - - string sharedString = (Console.CursorLeft != 0) ? Environment.NewLine : string.Empty; - sharedString += (e.IsTask ? $"[Task: {e.TaskId}]" : string.Empty) + $"[{e.LineNumber}]"; - - if (e.OriginalLine == e.CurrentLine) - { - Console.WriteLine($"{sharedString}[{e.CurrentLine}] ==>"); - } - else - { - Console.WriteLine($"{sharedString}[{e.OriginalLine}] => [{e.CurrentLine}] ==>"); - } - Console.ForegroundColor = ConsoleColor.Gray; - } - string[] outputs = debugOutput.ToArray(); - debugOutput.Clear(); - - foreach (string output in outputs) - { - Console.Write(output); - } - } - } - - private void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e) - { - e.Cancel = true; - switch (EditMode) - { - case Mode.Debug: - YesNtInterpreter.Stop(); - EditMode = Mode.Command; - break; - } - } - - public int GetSpacing() - { - int padding = (Console.WindowHeight + LineOffset - 3).ToString().Length; - padding = Math.Max(padding, Lines.Count.ToString().Length); - padding += 1; - return padding; - } - - private readonly Point oldSize = new Point(0, 0); - - private bool SizeChanged() - { - if (oldSize.X != Console.WindowWidth || oldSize.Y != Console.WindowHeight) - { - oldSize.X = Console.WindowWidth; - oldSize.Y = Console.WindowHeight; - return true; - } + InputHandler.WriteStatus(ex.Message); return false; } } - internal class Point + public int GetSpacing() { - public int X { get; set; } - public int Y { get; set; } + int padding = (Console.WindowHeight + LineOffset - 3).ToString().Length; + padding = Math.Max(padding, Lines.Count.ToString().Length); + padding += 1; + return padding; + } - public Point(int x, int y) + private void YesNtInterpreter_OnDebugOutput(string output) + { + debugOutput.Add(output); + } + + private void YesNtInterpreter_OnLineExecuted(Interpreter.Runtime.DebugEventArgs e) + { + lock (Console.Out) { - X = x; - Y = y; + if (e is not null) + { + Console.ForegroundColor = ConsoleColor.Magenta; + + string sharedString = (Console.CursorLeft != 0) ? Environment.NewLine : string.Empty; + sharedString += (e.IsTask ? $"[Task: {e.TaskId}]" : string.Empty) + $"[{e.LineNumber}]"; + + if (e.OriginalLine == e.CurrentLine) + { + Console.WriteLine($"{sharedString}[{e.CurrentLine}] ==>"); + } + else + { + Console.WriteLine($"{sharedString}[{e.OriginalLine}] => [{e.CurrentLine}] ==>"); + } + Console.ForegroundColor = ConsoleColor.Gray; + } + string[] outputs = debugOutput.ToArray(); + debugOutput.Clear(); + + foreach (string output in outputs) + { + Console.Write(output); + } } } - internal enum Mode + private void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e) { - Edit, - Command, - Debug + e.Cancel = true; + switch (EditMode) + { + case Mode.Debug: + YesNtInterpreter.Stop(); + EditMode = Mode.Command; + break; + } } + + private bool SizeChanged() + { + if (oldSize.X != Console.WindowWidth || oldSize.Y != Console.WindowHeight) + { + oldSize.X = Console.WindowWidth; + oldSize.Y = Console.WindowHeight; + return true; + } + return false; + } +} + +internal class Point +{ + public int X { get; set; } + public int Y { get; set; } + + public Point(int x, int y) + { + X = x; + Y = y; + } +} + +internal enum Mode +{ + Edit, + Command, + Debug } \ No newline at end of file diff --git a/YesNt.CodeEditor/InputHandler.cs b/YesNt.CodeEditor/InputHandler.cs index 3b31f46..7a6f6b1 100644 --- a/YesNt.CodeEditor/InputHandler.cs +++ b/YesNt.CodeEditor/InputHandler.cs @@ -1,313 +1,317 @@ using System; using System.Text; -namespace YesNt.CodeEditor +namespace YesNt.CodeEditor; + +internal class InputHandler { - internal class InputHandler + private readonly TextEditor textEditor; + + public InputHandler(TextEditor textEditor) { - private readonly TextEditor textEditor; + this.textEditor = textEditor; + } - public InputHandler(TextEditor textEditor) + public bool HandleInput() + { + while (Console.KeyAvailable) { - this.textEditor = textEditor; + _ = Console.ReadKey(true); } - - public bool HandleInput() + if (textEditor.EditMode == Mode.Edit) { - while (Console.KeyAvailable) - { - _ = Console.ReadKey(true); - } - if (textEditor.EditMode == Mode.Edit) - { - ConsoleKeyInfo keyInfo = Console.ReadKey(true); + ConsoleKeyInfo keyInfo = Console.ReadKey(true); - if ((ConsoleModifiers.Alt & keyInfo.Modifiers) == ConsoleModifiers.Alt) + if ((ConsoleModifiers.Alt & keyInfo.Modifiers) == ConsoleModifiers.Alt) + { + switch (keyInfo.Key) { - switch (keyInfo.Key) - { - case ConsoleKey.C: - textEditor.EditMode = Mode.Command; - return true; + 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); + 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; + textEditor.Display(true); + return true; - case ConsoleKey.T: - textEditor.CursorPosition.Y = 0; - textEditor.LineOffset = 0; + case ConsoleKey.T: + textEditor.CursorPosition.Y = 0; + textEditor.LineOffset = 0; - textEditor.Display(true); - return true; + textEditor.Display(true); + return true; - case ConsoleKey.S: - textEditor.CursorPosition.X = 0; - 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; - } + 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) + } + if (keyInfo.Key == ConsoleKey.DownArrow) + { + textEditor.CursorPosition.Y++; + if (textEditor.CursorPosition.Y - textEditor.LineOffset >= Console.WindowHeight - 3) { - 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.LineOffset--; textEditor.Display(true); } } - else if (keyInfo.Key == ConsoleKey.UpArrow) + } + else if (keyInfo.Key == ConsoleKey.LeftArrow) + { + if (textEditor.CursorPosition.X > 0) { - 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 == ConsoleKey.Backspace) - { - 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++; - } - } + textEditor.CursorPosition.X--; } } - else if (textEditor.EditMode == Mode.Command) + else if (keyInfo.Key == ConsoleKey.RightArrow) { - 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) + if (textEditor.CursorPosition.X + textEditor.GetSpacing() + 3 < Console.WindowWidth) { - case "edit": - WriteStatus(string.Empty); - textEditor.EditMode = Mode.Edit; - break; + 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, ""); - case "line": - WriteStatus(string.Empty); + string line = textEditor.Lines[textEditor.CursorPosition.Y + 1]; - bool success = false; - int lineNumber = 0; - if (input.Split(' ').Length == 2) - { - success = int.TryParse(input.Split(' ')[1], out lineNumber); - } + 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)..]; - 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; + textEditor.CursorPosition.X = 0; + textEditor.CursorPosition.Y++; - case "save": - _ = textEditor.Save(input, false); - break; - - case "run": - if (textEditor.Save(input, true)) - { - textEditor.EditMode = Mode.Debug; - Console.Clear(); - Console.CursorVisible = true; - textEditor.YesNtInterpreter.Execute(textEditor.CurrentPath); - while (Console.KeyAvailable) - { - _ = Console.ReadKey(true); - } - _ = Console.ReadKey(); - WriteStatus(string.Empty); - textEditor.EditMode = Mode.Command; - } - break; - - case "debug": - if (textEditor.Save(input, true)) - { - textEditor.EditMode = Mode.Debug; - Console.Clear(); - Console.CursorVisible = true; - textEditor.YesNtInterpreter.Execute(textEditor.CurrentPath, true); - while (Console.KeyAvailable) - { - _ = Console.ReadKey(true); - } - _ = Console.ReadKey(); - WriteStatus(string.Empty); - textEditor.EditMode = Mode.Command; - } - 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; + if (textEditor.CursorPosition.Y - textEditor.LineOffset >= Console.WindowHeight - 3) + { + textEditor.LineOffset++; } textEditor.Display(true); } - return true; - } + else + { + while (textEditor.Lines.Count <= textEditor.CursorPosition.Y) + { + textEditor.Lines.Add(""); + } - internal static void WriteStatus(string input) - { - Console.SetCursorPosition(0, Console.WindowHeight - 1); - Console.Write(input + new string(' ', Console.WindowWidth - input.Length - 1)); + 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": + if (textEditor.Save(input, true)) + { + textEditor.EditMode = Mode.Debug; + Console.Clear(); + Console.CursorVisible = true; + textEditor.YesNtInterpreter.Execute(textEditor.CurrentPath); + while (Console.KeyAvailable) + { + _ = Console.ReadKey(true); + } + _ = Console.ReadKey(); + WriteStatus(string.Empty); + textEditor.EditMode = Mode.Command; + } + break; + + case "debug": + if (textEditor.Save(input, true)) + { + textEditor.EditMode = Mode.Debug; + Console.Clear(); + Console.CursorVisible = true; + textEditor.YesNtInterpreter.Execute(textEditor.CurrentPath, true); + while (Console.KeyAvailable) + { + _ = Console.ReadKey(true); + } + _ = Console.ReadKey(); + WriteStatus(string.Empty); + textEditor.EditMode = Mode.Command; + } + 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)); } } \ No newline at end of file diff --git a/YesNt.CodeEditor/Program.cs b/YesNt.CodeEditor/Program.cs index ffa8ec7..5b5517b 100644 --- a/YesNt.CodeEditor/Program.cs +++ b/YesNt.CodeEditor/Program.cs @@ -1,11 +1,4 @@ -namespace YesNt.CodeEditor -{ - internal static class Program - { - private static void Main(string[] args) - { - TextEditor textEditor = args.Length > 0 ? new TextEditor(args[0]) : new TextEditor(); - textEditor.Run(); - } - } -} \ No newline at end of file +using YesNt.CodeEditor; + +TextEditor textEditor = args.Length > 0 ? new TextEditor(args[0]) : new TextEditor(); +textEditor.Run(); \ No newline at end of file diff --git a/YesNt.CodeEditor/SyntaxHighlighter.cs b/YesNt.CodeEditor/SyntaxHighlighter.cs index 7ef4d22..5e8a869 100644 --- a/YesNt.CodeEditor/SyntaxHighlighter.cs +++ b/YesNt.CodeEditor/SyntaxHighlighter.cs @@ -1,5 +1,6 @@ using System; using System.Collections.ObjectModel; +using System.Linq; using System.Text.RegularExpressions; using YesNt.Interpreter.Enums; @@ -11,10 +12,12 @@ namespace YesNt.CodeEditor; internal partial class SyntaxHighlighter { private readonly ReadOnlyCollection statementInformation; + private readonly string[] replacementValues; public SyntaxHighlighter(ReadOnlyCollection statementInformation) { this.statementInformation = statementInformation; + replacementValues = StringExtentions.ReplacementRules.Values.ToArray(); } public static string Base64Encode(string plainText) @@ -39,10 +42,9 @@ internal partial class SyntaxHighlighter } else { - MatchCollection matches = EscapeSequenceRegex().Matches(input); - for (int i = 0; i < matches.Count; i++) + for (int i = 0; i < replacementValues.Length; i++) { - input = AddColorInformation(input, matches[i].Value, ConsoleColor.DarkYellow, SearchMode.Contains); + input = AddColorInformation(input, replacementValues[i], ConsoleColor.Blue, SearchMode.Contains); } foreach (StatementInformation statement in statementInformation) @@ -110,7 +112,7 @@ internal partial class SyntaxHighlighter } } - matches = VariableRegex().Matches(input); + MatchCollection matches = VariableRegex().Matches(input); for (int i = 0; i < matches.Count; i++) { input = AddColorInformation(input, matches[i].Value, ConsoleColor.Cyan, SearchMode.Contains); @@ -171,9 +173,6 @@ internal partial class SyntaxHighlighter return reult; } - [GeneratedRegex("!!.")] - private static partial Regex EscapeSequenceRegex(); - [GeneratedRegex("^<[a-zA-Z0-9]+")] private static partial Regex VariableDeclarationRegex(); diff --git a/YesNt.Interpreter.Tests/CodeFowTests.cs b/YesNt.Interpreter.Tests/CodeFowTests.cs index 23a715a..438116b 100644 --- a/YesNt.Interpreter.Tests/CodeFowTests.cs +++ b/YesNt.Interpreter.Tests/CodeFowTests.cs @@ -38,7 +38,6 @@ public class CodeFlowTests [TestMethod] public void CalculationsTest() { - Assert.Inconclusive(); - YesNtAssert.IsLineEqual("10 * 10 !calc", 20.ToString()); + YesNtAssert.IsLineEqual("10 * 10 !calc", 100.ToString()); } } \ No newline at end of file diff --git a/YesNt.Interpreter.Tests/YesNtAssert.cs b/YesNt.Interpreter.Tests/YesNtAssert.cs index b7200a6..b3aa128 100644 --- a/YesNt.Interpreter.Tests/YesNtAssert.cs +++ b/YesNt.Interpreter.Tests/YesNtAssert.cs @@ -60,4 +60,26 @@ internal static class YesNtAssert Assert.AreEqual(expected, debugEventArgs.CurrentLine); } + + public static void IsLineNotEqual(string line, string expected, int timeout = 1000) + { + AutoResetEvent onDone = new AutoResetEvent(false); + List lines = new List() + { + line + }; + + DebugEventArgs debugEventArgs = new DebugEventArgs(); + yesNtInterpreter.OnLineExecuted += (er) => + { + debugEventArgs = er ?? debugEventArgs; + _ = onDone.Set(); + }; + + yesNtInterpreter.Execute(lines, true); + + _ = onDone.WaitOne(TimeSpan.FromMilliseconds(timeout)); + + Assert.AreNotEqual(expected, debugEventArgs.CurrentLine); + } } \ No newline at end of file diff --git a/YesNt.Interpreter/Statements/ProcessingStatements.cs b/YesNt.Interpreter/Statements/ProcessingStatements.cs index 0b5bd26..54cebf4 100644 --- a/YesNt.Interpreter/Statements/ProcessingStatements.cs +++ b/YesNt.Interpreter/Statements/ProcessingStatements.cs @@ -38,23 +38,6 @@ internal partial class ProcessingStatements : StatementRuntimeInformation RuntimeInfo.CurrentLine = args.FromSafeString(); } - [Statement("!!", SearchMode.Contains, SpaceAround.None, ConsoleColor.DarkYellow, Priority = Priority.PreProcessing, KeepStatementInArgs = true)] - public void DontEvaluate(string args) - { - int index; - while ((index = args.IndexOf("!!")) != -1) - { - args = args.Remove(index, 2); - if (index < args.Length) - { - char charToEscape = args[index]; - args = args.Remove(index, 1); - args = args.Insert(index, charToEscape.ToString().ToSafeString()); - } - } - RuntimeInfo.CurrentLine = args; - } - [Statement("!task", SearchMode.EndOfLine, SpaceAround.Start, ConsoleColor.DarkYellow, Priority = Priority.VeryHigh)] public void RunTask(string line) { @@ -77,8 +60,14 @@ internal partial class ProcessingStatements : StatementRuntimeInformation [Statement("slp", SearchMode.StartOfLine, SpaceAround.End, ConsoleColor.Magenta)] public void Sleep(string args) { - _ = int.TryParse(args, out int millisecondsTimeout); - ConsoleExtentions.Sleep(millisecondsTimeout, RuntimeInfo); + if (int.TryParse(args, out int millisecondsTimeout)) + { + ConsoleExtentions.Sleep(millisecondsTimeout, RuntimeInfo); + } + else + { + RuntimeInfo.Exit($"\"{args}\" is not a valid time-out value", true); + } } [Statement("len", SearchMode.StartOfLine, SpaceAround.End, ConsoleColor.Magenta)] diff --git a/YesNt.Interpreter/Statements/VariableStatements.cs b/YesNt.Interpreter/Statements/VariableStatements.cs index 490512d..5fd4c5d 100644 --- a/YesNt.Interpreter/Statements/VariableStatements.cs +++ b/YesNt.Interpreter/Statements/VariableStatements.cs @@ -91,6 +91,11 @@ internal partial class VariableStatements : StatementRuntimeInformation MatchCollection matches = VariableStatementRegex().Matches(RuntimeInfo.CurrentLine); + if (matches.Count <= 0) + { + RuntimeInfo.Exit("Invalid syntax", true); + } + for (int i = 0; i < matches.Count; i++) { string varName = matches[i].Value.Replace(">", string.Empty); diff --git a/YesNt.Interpreter/Utilities/StringExtentions.cs b/YesNt.Interpreter/Utilities/StringExtentions.cs index 06efe5f..ca042b8 100644 --- a/YesNt.Interpreter/Utilities/StringExtentions.cs +++ b/YesNt.Interpreter/Utilities/StringExtentions.cs @@ -1,11 +1,34 @@ using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Linq; using System.Text; namespace YesNt.Interpreter.Utilities; public static class StringExtentions { + private static readonly Dictionary reverseReplacementRules; + + public static Dictionary ReplacementRules { get; } = new() + { + {"~", "~til" }, + {" ", "~spc" }, + {"%", "~per" }, + {"<", "~let" }, + {">", "~grt" }, + {",", "~com" }, + {"!", "~exm" }, + {"|", "~pip" } + }; + + [SuppressMessage("Minor Code Smell", "S3963:\"static\" fields should be initialized inline", Justification = "Doesn't work because it throws a TypeInitializationException")] + static StringExtentions() + { + reverseReplacementRules = ReplacementRules.ToDictionary(x => x.Value, x => x.Key); + } + public static string ToSafeString(this string input) { StringBuilder output = new StringBuilder(); @@ -13,16 +36,32 @@ public static class StringExtentions { _ = output.Append($"\v{c}\v"); } - return output - .ToString() - .Replace(' ', '~'); + + return ReplaceOnce(output.ToString(), ReplacementRules); } public static string FromSafeString(this string input) { - return input - .Replace("\v", "") - .Replace('~', ' '); + return ReplaceOnce(input.Replace("\v", ""), reverseReplacementRules); + } + + public static string ReplaceOnce(string input, Dictionary replacementRules) + { + IEnumerable> matches = replacementRules.Where(rule => input.Contains(rule.Key)); + if (!matches.Any()) + { + return input; + } + + KeyValuePair match = matches.First(); + int startIndex = input.IndexOf(match.Key); + int endIndex = startIndex + match.Key.Length; + + string before = ReplaceOnce(input[..startIndex], replacementRules); + string replaced = match.Value; + string after = ReplaceOnce(input[endIndex..], replacementRules); + + return before + replaced + after; } public static bool ToStandardizedNumber(this string input, out double result)