mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-06-19 13:28:16 +08:00

committed by
Patrik Svensson

parent
e280b82679
commit
1cf30f62fc
@ -1,4 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace Spectre.Console
|
||||
{
|
||||
@ -7,7 +10,7 @@ namespace Spectre.Console
|
||||
/// </summary>
|
||||
public static partial class AnsiConsoleExtensions
|
||||
{
|
||||
internal static string ReadLine(this IAnsiConsole console, Style? style, bool secret)
|
||||
internal static string ReadLine(this IAnsiConsole console, Style? style, bool secret, IEnumerable<string>? items = null)
|
||||
{
|
||||
if (console is null)
|
||||
{
|
||||
@ -15,35 +18,83 @@ namespace Spectre.Console
|
||||
}
|
||||
|
||||
style ??= Style.Plain;
|
||||
var text = string.Empty;
|
||||
|
||||
var autocomplete = new List<string>(items ?? Enumerable.Empty<string>());
|
||||
|
||||
var result = string.Empty;
|
||||
while (true)
|
||||
{
|
||||
var key = console.Input.ReadKey(true);
|
||||
|
||||
if (key.Key == ConsoleKey.Enter)
|
||||
{
|
||||
return result;
|
||||
return text;
|
||||
}
|
||||
|
||||
if (key.Key == ConsoleKey.Tab && autocomplete.Count > 0)
|
||||
{
|
||||
var replace = AutoComplete(autocomplete, text);
|
||||
if (!string.IsNullOrEmpty(replace))
|
||||
{
|
||||
// Render the suggestion
|
||||
console.Write("\b \b".Repeat(text.Length), style);
|
||||
console.Write(replace);
|
||||
text = replace;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (key.Key == ConsoleKey.Backspace)
|
||||
{
|
||||
if (result.Length > 0)
|
||||
if (text.Length > 0)
|
||||
{
|
||||
result = result.Substring(0, result.Length - 1);
|
||||
text = text.Substring(0, text.Length - 1);
|
||||
console.Write("\b \b");
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
result += key.KeyChar.ToString();
|
||||
|
||||
if (!char.IsControl(key.KeyChar))
|
||||
{
|
||||
text += key.KeyChar.ToString();
|
||||
console.Write(secret ? "*" : key.KeyChar.ToString(), style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string AutoComplete(List<string> autocomplete, string text)
|
||||
{
|
||||
var found = autocomplete.Find(i => i == text);
|
||||
var replace = string.Empty;
|
||||
|
||||
if (found == null)
|
||||
{
|
||||
// Get the closest match
|
||||
var next = autocomplete.Find(i => i.StartsWith(text, true, CultureInfo.InvariantCulture));
|
||||
if (next != null)
|
||||
{
|
||||
replace = next;
|
||||
}
|
||||
else if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
// Use the first item
|
||||
replace = autocomplete[0];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the next match
|
||||
var index = autocomplete.IndexOf(found) + 1;
|
||||
if (index >= autocomplete.Count)
|
||||
{
|
||||
index = 0;
|
||||
}
|
||||
|
||||
replace = autocomplete[index];
|
||||
}
|
||||
|
||||
return replace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,266 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Spectre.Console
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains extension methods for <see cref="TextPrompt{T}"/>.
|
||||
/// </summary>
|
||||
public static class TextPromptExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Allow empty input.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The prompt result type.</typeparam>
|
||||
/// <param name="obj">The prompt.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TextPrompt<T> AllowEmpty<T>(this TextPrompt<T> obj)
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
}
|
||||
|
||||
obj.AllowEmpty = true;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the prompt style.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The prompt result type.</typeparam>
|
||||
/// <param name="obj">The prompt.</param>
|
||||
/// <param name="style">The prompt style.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TextPrompt<T> PromptStyle<T>(this TextPrompt<T> obj, Style style)
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
}
|
||||
|
||||
if (style is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(style));
|
||||
}
|
||||
|
||||
obj.PromptStyle = style;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show or hide choices.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The prompt result type.</typeparam>
|
||||
/// <param name="obj">The prompt.</param>
|
||||
/// <param name="show">Whether or not choices should be visible.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TextPrompt<T> ShowChoices<T>(this TextPrompt<T> obj, bool show)
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
}
|
||||
|
||||
obj.ShowChoices = show;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows choices.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The prompt result type.</typeparam>
|
||||
/// <param name="obj">The prompt.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TextPrompt<T> ShowChoices<T>(this TextPrompt<T> obj)
|
||||
{
|
||||
return ShowChoices(obj, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides choices.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The prompt result type.</typeparam>
|
||||
/// <param name="obj">The prompt.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TextPrompt<T> HideChoices<T>(this TextPrompt<T> obj)
|
||||
{
|
||||
return ShowChoices(obj, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show or hide the default value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The prompt result type.</typeparam>
|
||||
/// <param name="obj">The prompt.</param>
|
||||
/// <param name="show">Whether or not the default value should be visible.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TextPrompt<T> ShowDefaultValue<T>(this TextPrompt<T> obj, bool show)
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
}
|
||||
|
||||
obj.ShowDefaultValue = show;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the default value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The prompt result type.</typeparam>
|
||||
/// <param name="obj">The prompt.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TextPrompt<T> ShowDefaultValue<T>(this TextPrompt<T> obj)
|
||||
{
|
||||
return ShowDefaultValue(obj, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides the default value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The prompt result type.</typeparam>
|
||||
/// <param name="obj">The prompt.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TextPrompt<T> HideDefaultValue<T>(this TextPrompt<T> obj)
|
||||
{
|
||||
return ShowDefaultValue(obj, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the validation error message for the prompt.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The prompt result type.</typeparam>
|
||||
/// <param name="obj">The prompt.</param>
|
||||
/// <param name="message">The validation error message.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TextPrompt<T> ValidationErrorMessage<T>(this TextPrompt<T> obj, string message)
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
}
|
||||
|
||||
obj.ValidationErrorMessage = message;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the "invalid choice" message for the prompt.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The prompt result type.</typeparam>
|
||||
/// <param name="obj">The prompt.</param>
|
||||
/// <param name="message">The "invalid choice" message.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TextPrompt<T> InvalidChoiceMessage<T>(this TextPrompt<T> obj, string message)
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
}
|
||||
|
||||
obj.InvalidChoiceMessage = message;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default value of the prompt.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The prompt result type.</typeparam>
|
||||
/// <param name="obj">The prompt.</param>
|
||||
/// <param name="value">The default value.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TextPrompt<T> DefaultValue<T>(this TextPrompt<T> obj, T value)
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
}
|
||||
|
||||
obj.DefaultValue = new TextPrompt<T>.DefaultValueContainer(value);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the validation criteria for the prompt.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The prompt result type.</typeparam>
|
||||
/// <param name="obj">The prompt.</param>
|
||||
/// <param name="validator">The validation criteria.</param>
|
||||
/// <param name="message">The validation error message.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TextPrompt<T> Validate<T>(this TextPrompt<T> obj, Func<T, bool> validator, string? message = null)
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
}
|
||||
|
||||
obj.Validator = result =>
|
||||
{
|
||||
if (validator(result))
|
||||
{
|
||||
return ValidationResult.Success();
|
||||
}
|
||||
|
||||
return ValidationResult.Error(message);
|
||||
};
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the validation criteria for the prompt.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The prompt result type.</typeparam>
|
||||
/// <param name="obj">The prompt.</param>
|
||||
/// <param name="validator">The validation criteria.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TextPrompt<T> Validate<T>(this TextPrompt<T> obj, Func<T, ValidationResult> validator)
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
}
|
||||
|
||||
obj.Validator = validator;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a choice to the prompt.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The prompt result type.</typeparam>
|
||||
/// <param name="obj">The prompt.</param>
|
||||
/// <param name="choice">The choice to add.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TextPrompt<T> AddChoice<T>(this TextPrompt<T> obj, T choice)
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
}
|
||||
|
||||
obj.Choices.Add(choice);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces prompt user input with asterixes in the console.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The prompt type.</typeparam>
|
||||
/// <param name="obj">The prompt.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TextPrompt<T> Secret<T>(this TextPrompt<T> obj)
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
}
|
||||
|
||||
obj.IsSecret = true;
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user