mirror of
				https://github.com/nsnail/spectre.console.git
				synced 2025-11-01 01:25:27 +08:00 
			
		
		
		
	Adds optional function to get the display string for TextPrompt choices
This commit is contained in:
		 Thomas Freudenberg
					Thomas Freudenberg
				
			
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			 GitHub
						GitHub
					
				
			
						parent
						
							041aea2ae5
						
					
				
				
					commit
					0bbf9b81a9
				
			| @@ -0,0 +1 @@ | ||||
| Favorite fruit? [Apple/Banana]: Banana | ||||
| @@ -1,5 +1,6 @@ | ||||
| using System; | ||||
| using System.Threading.Tasks; | ||||
| using Shouldly; | ||||
| using VerifyXunit; | ||||
| using Xunit; | ||||
|  | ||||
| @@ -168,5 +169,24 @@ namespace Spectre.Console.Tests.Unit | ||||
|             // Then | ||||
|             return Verifier.Verify(console.Output); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public Task Should_Use_Custom_Converter() | ||||
|         { | ||||
|             // Given | ||||
|             var console = new PlainConsole(); | ||||
|             console.Input.PushTextWithEnter("Banana"); | ||||
|  | ||||
|             // When | ||||
|             var result = console.Prompt( | ||||
|                 new TextPrompt<(int, string)>("Favorite fruit?") | ||||
|                     .AddChoice((1, "Apple")) | ||||
|                     .AddChoice((2, "Banana")) | ||||
|                     .WithConverter(testData => testData.Item2)); | ||||
|  | ||||
|             // Then | ||||
|             result.Item1.ShouldBe(2); | ||||
|             return Verifier.Verify(console.Output); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.ComponentModel; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Globalization; | ||||
| using System.Linq; | ||||
| @@ -15,6 +16,7 @@ namespace Spectre.Console | ||||
|     public sealed class TextPrompt<T> : IPrompt<T> | ||||
|     { | ||||
|         private readonly string _prompt; | ||||
|         private readonly StringComparer? _comparer; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets the prompt style. | ||||
| @@ -24,7 +26,7 @@ namespace Spectre.Console | ||||
|         /// <summary> | ||||
|         /// Gets the list of choices. | ||||
|         /// </summary> | ||||
|         public HashSet<T> Choices { get; } | ||||
|         public List<T> Choices { get; } = new List<T>(); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets the message for invalid choices. | ||||
| @@ -59,6 +61,12 @@ namespace Spectre.Console | ||||
|         /// </summary> | ||||
|         public bool AllowEmpty { get; set; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets the converter to get the display string for a choice. By default | ||||
|         /// the corresponding <see cref="TypeConverter"/> is used. | ||||
|         /// </summary> | ||||
|         public Func<T, string>? Converter { get; set; } = TypeConverterHelper.ConvertToString; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets the validator. | ||||
|         /// </summary> | ||||
| @@ -74,11 +82,10 @@ namespace Spectre.Console | ||||
|         /// </summary> | ||||
|         /// <param name="prompt">The prompt markup text.</param> | ||||
|         /// <param name="comparer">The comparer used for choices.</param> | ||||
|         public TextPrompt(string prompt, IEqualityComparer<T>? comparer = null) | ||||
|         public TextPrompt(string prompt, StringComparer? comparer = null) | ||||
|         { | ||||
|             _prompt = prompt; | ||||
|  | ||||
|             Choices = new HashSet<T>(comparer ?? EqualityComparer<T>.Default); | ||||
|             _comparer = comparer; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
| @@ -95,7 +102,9 @@ namespace Spectre.Console | ||||
|             } | ||||
|  | ||||
|             var promptStyle = PromptStyle ?? Style.Plain; | ||||
|             var choices = Choices.Select(choice => TypeConverterHelper.ConvertToString(choice)); | ||||
|             var converter = Converter ?? TypeConverterHelper.ConvertToString; | ||||
|             var choices = Choices.Select(choice => converter(choice)).ToList(); | ||||
|             var choiceMap = Choices.ToDictionary(choice => converter(choice), choice => choice, _comparer); | ||||
|  | ||||
|             WritePrompt(console); | ||||
|  | ||||
| @@ -108,7 +117,7 @@ namespace Spectre.Console | ||||
|                 { | ||||
|                     if (DefaultValue != null) | ||||
|                     { | ||||
|                         console.Write(TypeConverterHelper.ConvertToString(DefaultValue.Value), promptStyle); | ||||
|                         console.Write(converter(DefaultValue.Value), promptStyle); | ||||
|                         console.WriteLine(); | ||||
|                         return DefaultValue.Value; | ||||
|                     } | ||||
| @@ -121,17 +130,10 @@ namespace Spectre.Console | ||||
|  | ||||
|                 console.WriteLine(); | ||||
|  | ||||
|                 // Try convert the value to the expected type. | ||||
|                 if (!TypeConverterHelper.TryConvertFromString<T>(input, out var result) || result == null) | ||||
|                 { | ||||
|                     console.MarkupLine(ValidationErrorMessage); | ||||
|                     WritePrompt(console); | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
|                 T? result; | ||||
|                 if (Choices.Count > 0) | ||||
|                 { | ||||
|                     if (Choices.Contains(result)) | ||||
|                     if (choiceMap.TryGetValue(input, out result) && result != null) | ||||
|                     { | ||||
|                         return result; | ||||
|                     } | ||||
| @@ -142,6 +144,12 @@ namespace Spectre.Console | ||||
|                         continue; | ||||
|                     } | ||||
|                 } | ||||
|                 else if (!TypeConverterHelper.TryConvertFromString<T>(input, out result) || result == null) | ||||
|                 { | ||||
|                     console.MarkupLine(ValidationErrorMessage); | ||||
|                     WritePrompt(console); | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
|                 // Run all validators | ||||
|                 if (!ValidateResult(result, out var validationMessage)) | ||||
| @@ -171,7 +179,8 @@ namespace Spectre.Console | ||||
|  | ||||
|             if (ShowChoices && Choices.Count > 0) | ||||
|             { | ||||
|                 var choices = string.Join("/", Choices.Select(choice => TypeConverterHelper.ConvertToString(choice))); | ||||
|                 var converter = Converter ?? TypeConverterHelper.ConvertToString; | ||||
|                 var choices = string.Join("/", Choices.Select(choice => converter(choice))); | ||||
|                 builder.AppendFormat(CultureInfo.InvariantCulture, " [blue][[{0}]][/]", choices); | ||||
|             } | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
|  | ||||
| namespace Spectre.Console | ||||
| { | ||||
| @@ -246,6 +247,33 @@ namespace Spectre.Console | ||||
|             return obj; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Adds multiple choices to the prompt. | ||||
|         /// </summary> | ||||
|         /// <typeparam name="T">The prompt result type.</typeparam> | ||||
|         /// <param name="obj">The prompt.</param> | ||||
|         /// <param name="choices">The choices to add.</param> | ||||
|         /// <returns>The same instance so that multiple calls can be chained.</returns> | ||||
|         public static TextPrompt<T> AddChoices<T>(this TextPrompt<T> obj, IEnumerable<T> choices) | ||||
|         { | ||||
|             if (obj is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(obj)); | ||||
|             } | ||||
|  | ||||
|             if (choices is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(choices)); | ||||
|             } | ||||
|  | ||||
|             foreach (var choice in choices) | ||||
|             { | ||||
|                 obj.Choices.Add(choice); | ||||
|             } | ||||
|  | ||||
|             return obj; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Replaces prompt user input with asterixes in the console. | ||||
|         /// </summary> | ||||
| @@ -262,5 +290,23 @@ namespace Spectre.Console | ||||
|             obj.IsSecret = true; | ||||
|             return obj; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Sets the function to create a display string for a given choice. | ||||
|         /// </summary> | ||||
|         /// <typeparam name="T">The prompt type.</typeparam> | ||||
|         /// <param name="obj">The prompt.</param> | ||||
|         /// <param name="displaySelector">The function to get a display string for a given choice.</param> | ||||
|         /// <returns>The same instance so that multiple calls can be chained.</returns> | ||||
|         public static TextPrompt<T> WithConverter<T>(this TextPrompt<T> obj, Func<T, string>? displaySelector) | ||||
|         { | ||||
|             if (obj is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(obj)); | ||||
|             } | ||||
|  | ||||
|             obj.Converter = displaySelector; | ||||
|             return obj; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user