/// <summary> /// Pretty print the specified object. /// </summary> /// <param name="obj">Object.</param> public static void Write(IodineObject obj) { ANSI.Write(Format(obj)); }
string ReadUserInput() { // Declare variables var accum = new StringBuilder(); string lastLine = string.Empty; bool editingFinished = false; bool correctIndent = false; bool correctIndentExtra = false; // special unindent for matched grouping int foldCount = 0; int foldBracketDiff = 0; int foldBraceDiff = 0; int foldParenDiff = 0; int line = 1; int indent = 0; // Define getFoldCount function var getFoldCount = new Func <string, int> (str => { var lexer = new Iodine.Compiler.Tokenizer( new Iodine.Compiler.ErrorSink(), new Iodine.Compiler.SourceReader(str, "__anonymous__") ); IEnumerable <Iodine.Compiler.Token> tokens; try { tokens = lexer.Scan(); } catch { foldBracketDiff = 0; foldParenDiff = 0; foldBraceDiff = 0; return(0); } foldBracketDiff = ( tokens.Count(t => t.Class == global::Iodine.Compiler.TokenClass.OpenBracket) - tokens.Count(t => t.Class == global::Iodine.Compiler.TokenClass.CloseBracket) ); foldBraceDiff = ( tokens.Count(t => t.Class == global::Iodine.Compiler.TokenClass.OpenBrace) - tokens.Count(t => t.Class == global::Iodine.Compiler.TokenClass.CloseBrace) ); foldParenDiff = ( tokens.Count(t => t.Class == global::Iodine.Compiler.TokenClass.OpenParan) - tokens.Count(t => t.Class == global::Iodine.Compiler.TokenClass.CloseParan) ); return(foldBracketDiff + foldBraceDiff + foldParenDiff); }); // Define getPrompt function var getPrompt = new Func <int, string> (lineNum => { if (Conf.UsePowerlines) { var powerline = Powerline() .Segment($"IoX {AssemblyVersion.ToString (2)} ", fg: DarkCyan, bg: White) .Segment($"{lineNum}", fg: White, bg: DarkCyan) .ToAnsiString(); return(powerline); } return($"{lineNum} λ"); }); // Define rewriteIndent function var rewriteIndent = new Action(() => { // Save cursor state var currentCursorTop = Console.CursorTop; var targetCursorTop = Math.Max(0, Console.CursorTop - 1); // Rewrite last line Console.CursorTop = targetCursorTop; Console.CursorLeft = 0; Console.Write("".PadRight(Console.WindowWidth)); Console.CursorTop = targetCursorTop; Console.CursorLeft = 0; Prompt.Push(getPrompt(Math.Max(0, line - 1))); ANSI.Write(Prompt.ToString()); Prompt.Pop(); Console.Write(string.Empty.PadLeft((correctIndentExtra ? Math.Max(0, indent - 1) : indent) * 2)); Console.Write(lastLine); // Restore cursor state Console.CursorTop = currentCursorTop; Console.CursorLeft = 0; }); // Read more lines while (!editingFinished) { // Test if this is the first line if (line == 1) { // Duplicate prompt Prompt.Dup(); } else { // Push new prompt to visually indicate multi-line editing Prompt.Push(getPrompt(line)); } // Test if the indentation of the previous line should be rewritten if (correctIndent) { // Rewrite line rewriteIndent(); // Do not correct the indentation again correctIndent = false; correctIndentExtra = false; } // Test if the prompt of the previous line should be rewritten if (line == 2) { // Save cursor state var currentCursorTop = Console.CursorTop; var targetCursorTop = Math.Max(0, Console.CursorTop - 1); // Rewrite last line Console.CursorTop = targetCursorTop; Console.CursorLeft = 0; Console.Write("".PadRight(Console.WindowWidth)); Console.CursorTop = targetCursorTop; Console.CursorLeft = 0; Prompt.Push(getPrompt(1)); ANSI.Write(Prompt.ToString()); Prompt.Pop(); Console.Write(lastLine); // Restore cursor state Console.CursorTop = currentCursorTop; Console.CursorLeft = 0; } // Modify prompt Prompt.Push($"{Prompt.ToString ().Trim ()}{string.Empty.PadLeft (indent * 2)}"); // Read line lastLine = Hinter.Edit(Prompt.ToString()) ?? string.Empty; // Restore prompt Prompt.Pop(); //lastLine = Hinter.ReadHintedLine ( // hintSource: Iodine.Engine.Context.Globals.Concat (Iodine.Engine.Context.InteractiveLocals), // hintField: attr => attr.Key, // hintColor: Gray //).Trim (); // lastLine = Console.ReadLine ().Trim (); // Update state var localFoldCount = getFoldCount(lastLine); foldCount += localFoldCount; // Auto-indent based on local fold count if (localFoldCount > 0) { indent += 1; } else if (localFoldCount < 0) { correctIndent = true; indent = Math.Max(0, indent - 1); } // Auto-indent based on total fold count if (foldCount == 0) { indent = 0; } // Auto-indent based on matched close-open grouping operators if (lastLine.Count(c => new [] { '{', '}', '[', ']', '(', ')' }.Contains(c)) >= 2 && lastLine.IndexOfAny(new [] { '}', ']', ')' }) < lastLine.IndexOfAny(new [] { '{', '[', '(' })) { correctIndentExtra = true; correctIndent = true; } editingFinished = foldCount == 0; line += 1; // Test for negative (unfixable) grouping mismatch if (foldCount < 0) { // Clear buffer accum.Clear(); // Output error ANSI.WriteLine($"{Red}Mismatched bracket, brace, or parenthesis group!"); editingFinished = true; } else { // Append line to buffer accum.AppendLine(lastLine); } // Restore prompt Prompt.Pop(); } // Rewrite indent if fold count is 0 if (correctIndent) { rewriteIndent(); } // Return buffer return(accum.ToString()); }
public static string ReadHintedLine <T, TResult> (IEnumerable <T> hintSource, Func <T, TResult> hintField, ANSIColor hintColor, string inputRegex = ".*") { ConsoleKeyInfo input; var editComplete = true; var accum = string.Empty; var lastWord = string.Empty; var userInput = string.Empty; var suggestion = string.Empty; var initialCursorTop = Console.CursorTop; var initialCursorLeft = Console.CursorLeft; var lastInitialCursorTop = Console.CursorTop; var lastInitialCursorLeft = Console.CursorLeft; var localInitialCursorTop = Console.CursorTop; var localInitialCursorLeft = Console.CursorLeft; #if DEBUG var __DEBUG_TRD = new System.Threading.Thread(() => { while (true) { Console.Title = ($"ACCUM: {accum} | USRIN: {userInput}"); System.Threading.Thread.Sleep(100); } }); __DEBUG_TRD.Start(); #endif // Read next key while (ConsoleKey.Enter != (input = Console.ReadKey(intercept: true)).Key) { // Prepare state if (editComplete) { lastWord = string.Empty; userInput = string.Empty; suggestion = string.Empty; editComplete = false; lastInitialCursorTop = localInitialCursorTop; lastInitialCursorLeft = localInitialCursorLeft; localInitialCursorTop = Console.CursorTop; localInitialCursorLeft = Console.CursorLeft; } // Handle backspace if (input.Key == ConsoleKey.Backspace) { if (userInput.Any()) { userInput = userInput.Any() ? userInput.Remove(userInput.Length - 1, 1) : string.Empty; } else { accum = accum.Remove(Math.Max(0, accum.Length - 1), accum.Length > 0 ? 1 : 0); editComplete = true; // Clear line Console.SetCursorPosition(initialCursorLeft, initialCursorTop); Console.Write(string.Empty.PadLeft(Console.WindowWidth - initialCursorLeft)); Console.SetCursorPosition(initialCursorLeft, initialCursorTop); // Write finished line Console.Write(accum); continue; } } // Handle member access else if (input.Key == ConsoleKey.OemPeriod) { editComplete = true; userInput += input.KeyChar; } // Handle space else if (input.Key == ConsoleKey.Spacebar) { editComplete = true; userInput += input.KeyChar; } // Handle tab (accept suggestion) else if (input.Key == ConsoleKey.Tab) { editComplete = true; userInput = suggestion ?? userInput; } // Test if keychar is not alphanumeric else if (!char.IsLetterOrDigit(input.KeyChar)) { editComplete = true; userInput += input.KeyChar; } // Test if keychar matches input regex else if (Regex.IsMatch(input.KeyChar.ToString(), inputRegex)) { Console.CursorLeft++; userInput += input.KeyChar; } // Get the suggestion suggestion = ( hintSource.Select(item => hintField(item).ToString()) .FirstOrDefault( item => ( item.Length > userInput.Length && item.Substring(0, userInput.Length) == userInput ) ) ); // Get line lastWord = suggestion ?? userInput; // Clear line Console.SetCursorPosition(localInitialCursorLeft, localInitialCursorTop); Console.Write(string.Empty.PadLeft(Console.WindowWidth - localInitialCursorLeft)); Console.SetCursorPosition(localInitialCursorLeft, localInitialCursorTop); // Write user input ANSI.Write($"{(suggestion != null ? White : Default)}{userInput}"); // Write suggestion if (userInput.Any()) { ANSI.Write($"{hintColor}{lastWord.Substring (userInput.Length, lastWord.Length - userInput.Length)}"); if (editComplete) { // Clear line Console.SetCursorPosition(initialCursorLeft, initialCursorTop); Console.Write(string.Empty.PadLeft(Console.WindowWidth - initialCursorLeft)); Console.SetCursorPosition(initialCursorLeft, initialCursorTop); // Write finished line accum += lastWord; Console.Write(accum); } } continue; } if (!editComplete) { accum += userInput; } // Clear line Console.SetCursorPosition(initialCursorLeft, initialCursorTop); Console.Write(string.Empty.PadLeft(Console.WindowWidth - initialCursorLeft)); Console.SetCursorPosition(initialCursorLeft, initialCursorTop); // Write finished line Console.WriteLine(accum); #if DEBUG __DEBUG_TRD.Abort(); #endif // Return read line return(accum); }