internal static (string cmd, List <string> args) Run(string yourPrompt, BasePromptApp yourApp) { // for result var cmd = ""; var args = new List <string>(); // for history var inputHistoryPosition = inputHistory.Count; // for auto completer var argOrder = 0; var completeTarget = ""; IEnumerator <string> matchedList = null; // for input Console.ForegroundColor = PromptActive.Theme.PromptColor; Console.Write($"{Environment.NewLine}{yourPrompt} "); var input = new List <char>(); var inputPosition = 0; CurLeft = Console.CursorLeft; CurTop = Console.CursorTop; int startLeft = CurLeft; ReWrite(startLeft, input); Console.SetCursorPosition(startLeft, CurTop); var key = new ConsoleKeyInfo(); var isRunning = true; while (isRunning) { key = Console.ReadKey(true); switch (key.Key) { case ConsoleKey.LeftArrow: if (CurLeft - 1 >= startLeft) { inputPosition--; CurLeft--; Console.SetCursorPosition(CurLeft, CurTop); } break; case ConsoleKey.RightArrow: if (inputPosition + 1 <= input.Count) { inputPosition++; CurLeft++; Console.SetCursorPosition(CurLeft, CurTop); } break; case ConsoleKey.Backspace: if (CurLeft - 1 >= startLeft) { inputPosition--; CurLeft--; input.RemoveAt(inputPosition); ReWrite(startLeft, input); } break; case ConsoleKey.Delete: if (CurLeft - startLeft < input.Count) { input.RemoveAt(CurLeft - startLeft); ReWrite(startLeft, input); } break; case ConsoleKey.Home: { inputPosition = 0; CurLeft = startLeft; Console.SetCursorPosition(CurLeft, CurTop); } break; case ConsoleKey.End: { inputPosition = input.Count; CurLeft = startLeft + inputPosition; Console.SetCursorPosition(CurLeft, CurTop); } break; case ConsoleKey.UpArrow: if (inputHistory.Count > 0 && inputHistoryPosition - 1 >= 0) { // history inputHistoryPosition--; input = inputHistory[inputHistoryPosition].ToList(); inputPosition = input.Count; CurLeft = startLeft + inputPosition; ReWrite(startLeft, input); } break; case ConsoleKey.DownArrow: if (inputHistoryPosition + 1 < inputHistory.Count) { // history inputHistoryPosition++; input = inputHistory[inputHistoryPosition].ToList(); inputPosition = input.Count; CurLeft = startLeft + inputPosition; ReWrite(startLeft, input); } else if (inputHistoryPosition + 1 == inputHistory.Count) { inputHistoryPosition = inputHistory.Count; input.Clear(); inputPosition = input.Count; CurLeft = startLeft + inputPosition; ReWrite(startLeft, input); } break; case ConsoleKey.Tab: { args = string.Concat(input).TrimStart().Split(' ').ToList(); argOrder = args.Count - 1; cmd = argOrder == 0 ? "" : args[0]; // autocomplete if (lastKey.Key != ConsoleKey.Tab) { if (yourApp.AutoCompleteList.ContainsKey(cmd) && yourApp.AutoCompleteList[cmd].ContainsKey(argOrder)) { var thisCompleteList = yourApp.AutoCompleteList[cmd][argOrder]; completeTarget = args[argOrder]; matchedList = GetMatch(thisCompleteList, completeTarget).GetEnumerator(); } else { matchedList = null; } } var before = ""; for (int i = 0; i < args.Count - 1; i++) { before = $"{before}{args[i]} "; } if (matchedList != null) { matchedList.MoveNext(); while (string.IsNullOrEmpty(matchedList.Current)) { matchedList.MoveNext(); } input = $"{before}{matchedList.Current}".ToCharArray().ToList(); } inputPosition = input.Count; CurLeft = startLeft + inputPosition; ReWrite(startLeft, input); } break; case ConsoleKey.Escape: { if (lastKey.Key == ConsoleKey.Escape) { throw new InvalidOperationException("Escape"); } else { throw new OperationCanceledException("Escape"); } } case ConsoleKey.Enter: { // input insert args = string.Concat(input).Trim().Split(' ').ToList(); argOrder = args.Count - 1; cmd = args[0]; if (!string.IsNullOrWhiteSpace(cmd)) { inputHistory.Add(input); } isRunning = false; } break; case ConsoleKey.Spacebar: { if (lastKey.Key != ConsoleKey.Spacebar) { // input insert input.Insert(inputPosition++, key.KeyChar); CurLeft++; ReWrite(startLeft, input); } // show hint args = string.Concat(input).Trim().Split(' ').ToList(); argOrder = args.Count - 1; cmd = args[0]; if (argOrder == 0 && yourApp.HintList.ContainsKey(cmd)) { var hint = yourApp.HintList[cmd]; if (!string.IsNullOrEmpty(hint)) { ReWrite(startLeft, input); Console.ForegroundColor = PromptActive.Theme.HintColor; Console.Write(hint); Console.SetCursorPosition(CurLeft, CurTop); } } } break; default: { // input insert input.Insert(inputPosition++, key.KeyChar); CurLeft++; ReWrite(startLeft, input); } break; } // maxbuffer check if (startLeft + input.Count >= Console.BufferWidth) { throw new OverflowException("Console"); } lastKey = key; } return(cmd, args); }
//private static void Setup<T>(T app) where T : BasePromptApp private static void Setup(BasePromptApp yourApp) { // 1) Create CommandList var kvm = yourApp.GetType().GetMethods() ?.Where(x => x.GetCustomAttribute <PromptCommandAttribute>() != null) ?.Select(x => new KeyValuePair <string, MethodInfo>(x.Name, x)); if (kvm != null) { foreach (var kv in kvm) { yourApp.AutoCompleteList[""][0].Add(kv.Key); yourApp.CommandList.Add(kv.Key, kv.Value); } } // 2) Create HintList foreach (var item in yourApp.CommandList) { // 2-1) Description list var description = item.Value.GetCustomAttribute <DescriptionAttribute>()?.Description; description = string.IsNullOrEmpty(description) ? "Not defined." : description; yourApp.DescriptionList.Add(item.Key, description); // 2-2) Custim hint available var customhint = item.Value.GetCustomAttribute <CustomHintAttribute>()?.Hint; if (!string.IsNullOrEmpty(customhint)) { yourApp.HintList.Add(item.Key, customhint); continue; } // 2-2) Hint list var args = item.Value.GetParameters(); var sb = ""; if (args != null) { for (int i = 0; i < args.Count(); i++) { var type = args[i].ParameterType; var type_s = ""; if (type.IsEnum) { // enum // Autocomplete add var enumNames = type.GetEnumNames().ToList(); if (!yourApp.AutoCompleteList.ContainsKey(item.Key)) { yourApp.AutoCompleteList.Add(item.Key, new Dictionary <int, List <string> >()); } yourApp.AutoCompleteList[item.Key][i + 1] = enumNames; foreach (var e in enumNames) { type_s = $"{type_s}|{e}"; } type_s = new string(type_s.Skip(1).ToArray()); } else { type_s = type.Name; } if (args[i].IsOptional) { // optional sb = $"{sb}[{args[i].Name}<{type_s}>] "; } else { sb = $"{sb}{args[i].Name}<{type_s}> "; } } } yourApp.HintList.Add(item.Key, sb); } // 3) Create Autocomplete list var kvp = yourApp.GetType().GetProperties(); foreach (var item in kvp) { var ps = item.GetCustomAttributes <AutocompleteAttribute>(); foreach (var p in ps) { if (!yourApp.AutoCompleteList.ContainsKey(p.Function)) { yourApp.AutoCompleteList.Add(p.Function, new Dictionary <int, List <string> >()); } yourApp.AutoCompleteList[p.Function][p.Order] = item.GetValue(yourApp) as List <string>; } } }