mirror of
				https://github.com/nsnail/spectre.console.git
				synced 2025-11-04 18:40:50 +08:00 
			
		
		
		
	Add text prompt support
This commit is contained in:
		
				
					committed by
					
						
						Patrik Svensson
					
				
			
			
				
	
			
			
			
						parent
						
							380c6aca45
						
					
				
				
					commit
					0d209d8f18
				
			@@ -1,3 +1,4 @@
 | 
			
		||||
using System;
 | 
			
		||||
using Spectre.Console;
 | 
			
		||||
 | 
			
		||||
namespace InfoExample
 | 
			
		||||
@@ -12,6 +13,7 @@ namespace InfoExample
 | 
			
		||||
                .AddRow("[b]Color system[/]", $"{AnsiConsole.Capabilities.ColorSystem}")
 | 
			
		||||
                .AddRow("[b]Supports ansi?[/]", $"{YesNo(AnsiConsole.Capabilities.SupportsAnsi)}")
 | 
			
		||||
                .AddRow("[b]Legacy console?[/]", $"{YesNo(AnsiConsole.Capabilities.LegacyConsole)}")
 | 
			
		||||
                .AddRow("[b]Interactive?[/]", $"{YesNo(Environment.UserInteractive)}")
 | 
			
		||||
                .AddRow("[b]Buffer width[/]", $"{AnsiConsole.Console.Width}")
 | 
			
		||||
                .AddRow("[b]Buffer height[/]", $"{AnsiConsole.Console.Height}");
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										77
									
								
								examples/Prompt/Program.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								examples/Prompt/Program.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,77 @@
 | 
			
		||||
using Spectre.Console;
 | 
			
		||||
 | 
			
		||||
namespace Cursor
 | 
			
		||||
{
 | 
			
		||||
    public static class Program
 | 
			
		||||
    {
 | 
			
		||||
        public static void Main(string[] args)
 | 
			
		||||
        {
 | 
			
		||||
            // Confirmation
 | 
			
		||||
            if (!AnsiConsole.Confirm("Run prompt example?"))
 | 
			
		||||
            {
 | 
			
		||||
                AnsiConsole.MarkupLine("Ok... :(");
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // String
 | 
			
		||||
            AnsiConsole.WriteLine();
 | 
			
		||||
            AnsiConsole.Render(new Rule("[yellow]Strings[/]").RuleStyle("grey").LeftAligned());
 | 
			
		||||
            var name = AnsiConsole.Ask<string>("What's your [green]name[/]?");
 | 
			
		||||
 | 
			
		||||
            // String with choices
 | 
			
		||||
            AnsiConsole.WriteLine();
 | 
			
		||||
            AnsiConsole.Render(new Rule("[yellow]Choices[/]").RuleStyle("grey").LeftAligned());
 | 
			
		||||
            var fruit = AnsiConsole.Prompt(
 | 
			
		||||
                new TextPrompt<string>("What's your [green]favorite fruit[/]?")
 | 
			
		||||
                    .InvalidChoiceMessage("[red]That's not a valid fruit[/]")
 | 
			
		||||
                    .DefaultValue("Orange")
 | 
			
		||||
                    .AddChoice("Apple")
 | 
			
		||||
                    .AddChoice("Banana")
 | 
			
		||||
                    .AddChoice("Orange"));
 | 
			
		||||
 | 
			
		||||
            // Integer
 | 
			
		||||
            AnsiConsole.WriteLine();
 | 
			
		||||
            AnsiConsole.Render(new Rule("[yellow]Integers[/]").RuleStyle("grey").LeftAligned());
 | 
			
		||||
            var age = AnsiConsole.Prompt(
 | 
			
		||||
                new TextPrompt<int>("How [green]old[/] are you?")
 | 
			
		||||
                    .PromptStyle("green")
 | 
			
		||||
                    .ValidationErrorMessage("[red]That's not a valid age[/]")
 | 
			
		||||
                    .Validate(age =>
 | 
			
		||||
                    {
 | 
			
		||||
                        return age switch
 | 
			
		||||
                        {
 | 
			
		||||
                            <= 0 => ValidationResult.Error("[red]You must at least be 1 years old[/]"),
 | 
			
		||||
                            >= 123 => ValidationResult.Error("[red]You must be younger than the oldest person alive[/]"),
 | 
			
		||||
                            _ => ValidationResult.Success(),
 | 
			
		||||
                        };
 | 
			
		||||
                    }));
 | 
			
		||||
 | 
			
		||||
            // Secret
 | 
			
		||||
            AnsiConsole.WriteLine();
 | 
			
		||||
            AnsiConsole.Render(new Rule("[yellow]Secrets[/]").RuleStyle("grey").LeftAligned());
 | 
			
		||||
            var password = AnsiConsole.Prompt(
 | 
			
		||||
                new TextPrompt<string>("Enter [green]password[/]?")
 | 
			
		||||
                    .PromptStyle("red")
 | 
			
		||||
                    .Secret());
 | 
			
		||||
 | 
			
		||||
            // Optional
 | 
			
		||||
            AnsiConsole.WriteLine();
 | 
			
		||||
            AnsiConsole.Render(new Rule("[yellow]Optional[/]").RuleStyle("grey").LeftAligned());
 | 
			
		||||
            var color = AnsiConsole.Prompt(
 | 
			
		||||
                new TextPrompt<string>("[grey][[Optional]][/] What is your [green]favorite color[/]?")
 | 
			
		||||
                    .AllowEmpty());
 | 
			
		||||
 | 
			
		||||
            // Summary
 | 
			
		||||
            AnsiConsole.WriteLine();
 | 
			
		||||
            AnsiConsole.Render(new Rule("[yellow]Results[/]").RuleStyle("grey").LeftAligned());
 | 
			
		||||
            AnsiConsole.Render(new Table().AddColumns("[grey]Question[/]", "[grey]Answer[/]")
 | 
			
		||||
                .RoundedBorder()
 | 
			
		||||
                .BorderColor(Color.Grey)
 | 
			
		||||
                .AddRow("[grey]Name[/]", name)
 | 
			
		||||
                .AddRow("[grey]Favorite fruit[/]", fruit)
 | 
			
		||||
                .AddRow("[grey]Age[/]", age.ToString())
 | 
			
		||||
                .AddRow("[grey]Password[/]", password)
 | 
			
		||||
                .AddRow("[grey]Favorite color[/]", string.IsNullOrEmpty(color) ? "Unknown" : color));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								examples/Prompt/Prompt.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								examples/Prompt/Prompt.csproj
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <OutputType>Exe</OutputType>
 | 
			
		||||
    <TargetFramework>netcoreapp3.1</TargetFramework>
 | 
			
		||||
    <LangVersion>9</LangVersion>
 | 
			
		||||
    <IsPackable>false</IsPackable>
 | 
			
		||||
    <Title>Prompt</Title>
 | 
			
		||||
    <Description>Demonstrates how to get input from a user.</Description>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
</Project>
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<Project>
 | 
			
		||||
  <PropertyGroup Label="Settings">
 | 
			
		||||
    <Deterministic>true</Deterministic>
 | 
			
		||||
    <LangVersion>8.0</LangVersion>
 | 
			
		||||
    <LangVersion>9.0</LangVersion>
 | 
			
		||||
    <DebugSymbols>true</DebugSymbols>
 | 
			
		||||
    <DebugType>embedded</DebugType>
 | 
			
		||||
    <MinVerSkip Condition="'$(Configuration)' == 'Debug'">true</MinVerSkip>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,50 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using Spectre.Console.Rendering;
 | 
			
		||||
 | 
			
		||||
namespace Spectre.Console.Tests.Tools
 | 
			
		||||
{
 | 
			
		||||
    public sealed class MarkupConsoleFixture : IDisposable, IAnsiConsole
 | 
			
		||||
    {
 | 
			
		||||
        private readonly StringWriter _writer;
 | 
			
		||||
        private readonly IAnsiConsole _console;
 | 
			
		||||
 | 
			
		||||
        public string Output => _writer.ToString().TrimEnd('\n');
 | 
			
		||||
 | 
			
		||||
        public Capabilities Capabilities => _console.Capabilities;
 | 
			
		||||
        public Encoding Encoding => _console.Encoding;
 | 
			
		||||
        public IAnsiConsoleCursor Cursor => _console.Cursor;
 | 
			
		||||
        public int Width { get; }
 | 
			
		||||
        public int Height => _console.Height;
 | 
			
		||||
 | 
			
		||||
        public MarkupConsoleFixture(ColorSystem system, AnsiSupport ansi = AnsiSupport.Yes, int width = 80)
 | 
			
		||||
        {
 | 
			
		||||
            _writer = new StringWriter();
 | 
			
		||||
            _console = AnsiConsole.Create(new AnsiConsoleSettings
 | 
			
		||||
            {
 | 
			
		||||
                Ansi = ansi,
 | 
			
		||||
                ColorSystem = (ColorSystemSupport)system,
 | 
			
		||||
                Out = _writer,
 | 
			
		||||
                LinkIdentityGenerator = new TestLinkIdentityGenerator(),
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            Width = width;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Dispose()
 | 
			
		||||
        {
 | 
			
		||||
            _writer?.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Clear(bool home)
 | 
			
		||||
        {
 | 
			
		||||
            _console.Clear(home);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Write(Segment segment)
 | 
			
		||||
        {
 | 
			
		||||
            _console.Write(segment);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -12,10 +12,13 @@ namespace Spectre.Console.Tests
 | 
			
		||||
        public Capabilities Capabilities { get; }
 | 
			
		||||
        public Encoding Encoding { get; }
 | 
			
		||||
        public IAnsiConsoleCursor Cursor => throw new NotSupportedException();
 | 
			
		||||
        public TestableConsoleInput Input { get; }
 | 
			
		||||
 | 
			
		||||
        public int Width { get; }
 | 
			
		||||
        public int Height { get; }
 | 
			
		||||
 | 
			
		||||
        IAnsiConsoleInput IAnsiConsole.Input => Input;
 | 
			
		||||
 | 
			
		||||
        public Decoration Decoration { get; set; }
 | 
			
		||||
        public Color Foreground { get; set; }
 | 
			
		||||
        public Color Background { get; set; }
 | 
			
		||||
@@ -36,6 +39,7 @@ namespace Spectre.Console.Tests
 | 
			
		||||
            Width = width;
 | 
			
		||||
            Height = height;
 | 
			
		||||
            Writer = new StringWriter();
 | 
			
		||||
            Input = new TestableConsoleInput();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Dispose()
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,9 @@ namespace Spectre.Console.Tests
 | 
			
		||||
        public int Width { get; }
 | 
			
		||||
        public int Height => _console.Height;
 | 
			
		||||
        public IAnsiConsoleCursor Cursor => _console.Cursor;
 | 
			
		||||
        public TestableConsoleInput Input { get; }
 | 
			
		||||
 | 
			
		||||
        IAnsiConsoleInput IAnsiConsole.Input => Input;
 | 
			
		||||
 | 
			
		||||
        public TestableAnsiConsole(ColorSystem system, AnsiSupport ansi = AnsiSupport.Yes, int width = 80)
 | 
			
		||||
        {
 | 
			
		||||
@@ -31,6 +34,7 @@ namespace Spectre.Console.Tests
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            Width = width;
 | 
			
		||||
            Input = new TestableConsoleInput();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Dispose()
 | 
			
		||||
							
								
								
									
										51
									
								
								src/Spectre.Console.Tests/Tools/TestableConsoleInput.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/Spectre.Console.Tests/Tools/TestableConsoleInput.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Spectre.Console.Tests
 | 
			
		||||
{
 | 
			
		||||
    public sealed class TestableConsoleInput : IAnsiConsoleInput
 | 
			
		||||
    {
 | 
			
		||||
        private readonly Queue<ConsoleKeyInfo> _input;
 | 
			
		||||
 | 
			
		||||
        public TestableConsoleInput()
 | 
			
		||||
        {
 | 
			
		||||
            _input = new Queue<ConsoleKeyInfo>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void PushText(string input)
 | 
			
		||||
        {
 | 
			
		||||
            if (input is null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentNullException(nameof(input));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            foreach (var character in input)
 | 
			
		||||
            {
 | 
			
		||||
                PushCharacter(character);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            PushKey(ConsoleKey.Enter);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void PushCharacter(char character)
 | 
			
		||||
        {
 | 
			
		||||
            var control = char.IsUpper(character);
 | 
			
		||||
            _input.Enqueue(new ConsoleKeyInfo(character, (ConsoleKey)character, false, false, control));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void PushKey(ConsoleKey key)
 | 
			
		||||
        {
 | 
			
		||||
            _input.Enqueue(new ConsoleKeyInfo((char)key, key, false, false, false));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ConsoleKeyInfo ReadKey(bool intercept)
 | 
			
		||||
        {
 | 
			
		||||
            if (_input.Count == 0)
 | 
			
		||||
            {
 | 
			
		||||
                throw new InvalidOperationException("No input available.");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return _input.Dequeue();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										126
									
								
								src/Spectre.Console.Tests/Unit/PromptTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								src/Spectre.Console.Tests/Unit/PromptTests.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,126 @@
 | 
			
		||||
using System;
 | 
			
		||||
using Shouldly;
 | 
			
		||||
using Xunit;
 | 
			
		||||
 | 
			
		||||
namespace Spectre.Console.Tests.Unit
 | 
			
		||||
{
 | 
			
		||||
    public sealed class PromptTests
 | 
			
		||||
    {
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void Should_Return_Validation_Error_If_Value_Cannot_Be_Converted()
 | 
			
		||||
        {
 | 
			
		||||
            // Given
 | 
			
		||||
            var console = new PlainConsole();
 | 
			
		||||
            console.Input.PushText("ninety-nine");
 | 
			
		||||
            console.Input.PushText("99");
 | 
			
		||||
 | 
			
		||||
            // When
 | 
			
		||||
            console.Prompt(new TextPrompt<int>("Age?"));
 | 
			
		||||
 | 
			
		||||
            // Then
 | 
			
		||||
            console.Lines.Count.ShouldBe(3);
 | 
			
		||||
            console.Lines[0].ShouldBe("Age? ninety-nine");
 | 
			
		||||
            console.Lines[1].ShouldBe("Invalid input");
 | 
			
		||||
            console.Lines[2].ShouldBe("Age? 99");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void Should_Chose_Default_Value_If_Nothing_Is_Entered()
 | 
			
		||||
        {
 | 
			
		||||
            // Given
 | 
			
		||||
            var console = new PlainConsole();
 | 
			
		||||
            console.Input.PushKey(ConsoleKey.Enter);
 | 
			
		||||
 | 
			
		||||
            // When
 | 
			
		||||
            console.Prompt(
 | 
			
		||||
                new TextPrompt<string>("Favorite fruit?")
 | 
			
		||||
                    .AddChoice("Banana")
 | 
			
		||||
                    .AddChoice("Orange")
 | 
			
		||||
                    .DefaultValue("Banana"));
 | 
			
		||||
 | 
			
		||||
            // Then
 | 
			
		||||
            console.Lines.Count.ShouldBe(1);
 | 
			
		||||
            console.Lines[0].ShouldBe("Favorite fruit? [Banana/Orange] (Banana): Banana");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void Should_Return_Error_If_An_Invalid_Choice_Is_Made()
 | 
			
		||||
        {
 | 
			
		||||
            // Given
 | 
			
		||||
            var console = new PlainConsole();
 | 
			
		||||
            console.Input.PushText("Apple");
 | 
			
		||||
            console.Input.PushText("Banana");
 | 
			
		||||
 | 
			
		||||
            // When
 | 
			
		||||
            console.Prompt(
 | 
			
		||||
                new TextPrompt<string>("Favorite fruit?")
 | 
			
		||||
                    .AddChoice("Banana")
 | 
			
		||||
                    .AddChoice("Orange")
 | 
			
		||||
                    .DefaultValue("Banana"));
 | 
			
		||||
 | 
			
		||||
            // Then
 | 
			
		||||
            console.Lines.Count.ShouldBe(3);
 | 
			
		||||
            console.Lines[0].ShouldBe("Favorite fruit? [Banana/Orange] (Banana): Apple");
 | 
			
		||||
            console.Lines[1].ShouldBe("Please select one of the available options");
 | 
			
		||||
            console.Lines[2].ShouldBe("Favorite fruit? [Banana/Orange] (Banana): Banana");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void Should_Accept_Choice_In_List()
 | 
			
		||||
        {
 | 
			
		||||
            // Given
 | 
			
		||||
            var console = new PlainConsole();
 | 
			
		||||
            console.Input.PushText("Orange");
 | 
			
		||||
 | 
			
		||||
            // When
 | 
			
		||||
            console.Prompt(
 | 
			
		||||
                new TextPrompt<string>("Favorite fruit?")
 | 
			
		||||
                    .AddChoice("Banana")
 | 
			
		||||
                    .AddChoice("Orange")
 | 
			
		||||
                    .DefaultValue("Banana"));
 | 
			
		||||
 | 
			
		||||
            // Then
 | 
			
		||||
            console.Lines.Count.ShouldBe(1);
 | 
			
		||||
            console.Lines[0].ShouldBe("Favorite fruit? [Banana/Orange] (Banana): Orange");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void Should_Return_Error_If_Custom_Validation_Fails()
 | 
			
		||||
        {
 | 
			
		||||
            // Given
 | 
			
		||||
            var console = new PlainConsole();
 | 
			
		||||
            console.Input.PushText("22");
 | 
			
		||||
            console.Input.PushText("102");
 | 
			
		||||
            console.Input.PushText("ABC");
 | 
			
		||||
            console.Input.PushText("99");
 | 
			
		||||
 | 
			
		||||
            // When
 | 
			
		||||
            console.Prompt(
 | 
			
		||||
                new TextPrompt<int>("Guess number:")
 | 
			
		||||
                    .ValidationErrorMessage("Invalid input")
 | 
			
		||||
                    .Validate(age =>
 | 
			
		||||
                    {
 | 
			
		||||
                        if (age < 99)
 | 
			
		||||
                        {
 | 
			
		||||
                            return ValidationResult.Error("Too low");
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (age > 99)
 | 
			
		||||
                        {
 | 
			
		||||
                            return ValidationResult.Error("Too high");
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        return ValidationResult.Success();
 | 
			
		||||
                    }));
 | 
			
		||||
 | 
			
		||||
            // Then
 | 
			
		||||
            console.Lines.Count.ShouldBe(7);
 | 
			
		||||
            console.Lines[0].ShouldBe("Guess number: 22");
 | 
			
		||||
            console.Lines[1].ShouldBe("Too low");
 | 
			
		||||
            console.Lines[2].ShouldBe("Guess number: 102");
 | 
			
		||||
            console.Lines[3].ShouldBe("Too high");
 | 
			
		||||
            console.Lines[4].ShouldBe("Guess number: ABC");
 | 
			
		||||
            console.Lines[5].ShouldBe("Invalid input");
 | 
			
		||||
            console.Lines[6].ShouldBe("Guess number: 99");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -50,6 +50,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rules", "..\examples\Rules\
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cursor", "..\examples\Cursor\Cursor.csproj", "{75C608C3-ABB4-4168-A229-7F8250B946D1}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prompt", "..\examples\Prompt\Prompt.csproj", "{6351C70F-F368-46DB-BAED-9B87CCD69353}"
 | 
			
		||||
EndProject
 | 
			
		||||
Global
 | 
			
		||||
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 | 
			
		||||
		Debug|Any CPU = Debug|Any CPU
 | 
			
		||||
@@ -240,6 +242,18 @@ Global
 | 
			
		||||
		{75C608C3-ABB4-4168-A229-7F8250B946D1}.Release|x64.Build.0 = Release|Any CPU
 | 
			
		||||
		{75C608C3-ABB4-4168-A229-7F8250B946D1}.Release|x86.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{75C608C3-ABB4-4168-A229-7F8250B946D1}.Release|x86.Build.0 = Release|Any CPU
 | 
			
		||||
		{6351C70F-F368-46DB-BAED-9B87CCD69353}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{6351C70F-F368-46DB-BAED-9B87CCD69353}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{6351C70F-F368-46DB-BAED-9B87CCD69353}.Debug|x64.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{6351C70F-F368-46DB-BAED-9B87CCD69353}.Debug|x64.Build.0 = Debug|Any CPU
 | 
			
		||||
		{6351C70F-F368-46DB-BAED-9B87CCD69353}.Debug|x86.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{6351C70F-F368-46DB-BAED-9B87CCD69353}.Debug|x86.Build.0 = Debug|Any CPU
 | 
			
		||||
		{6351C70F-F368-46DB-BAED-9B87CCD69353}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{6351C70F-F368-46DB-BAED-9B87CCD69353}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{6351C70F-F368-46DB-BAED-9B87CCD69353}.Release|x64.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{6351C70F-F368-46DB-BAED-9B87CCD69353}.Release|x64.Build.0 = Release|Any CPU
 | 
			
		||||
		{6351C70F-F368-46DB-BAED-9B87CCD69353}.Release|x86.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{6351C70F-F368-46DB-BAED-9B87CCD69353}.Release|x86.Build.0 = Release|Any CPU
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
	GlobalSection(SolutionProperties) = preSolution
 | 
			
		||||
		HideSolutionNode = FALSE
 | 
			
		||||
@@ -259,6 +273,7 @@ Global
 | 
			
		||||
		{57691C7D-683D-46E6-AA4F-57A8C5F65D25} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
 | 
			
		||||
		{8622A261-02C6-40CA-9797-E3F01ED87D6B} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
 | 
			
		||||
		{75C608C3-ABB4-4168-A229-7F8250B946D1} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
 | 
			
		||||
		{6351C70F-F368-46DB-BAED-9B87CCD69353} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
	GlobalSection(ExtensibilityGlobals) = postSolution
 | 
			
		||||
		SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										47
									
								
								src/Spectre.Console/AnsiConsole.Prompt.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/Spectre.Console/AnsiConsole.Prompt.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Spectre.Console
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// A console capable of writing ANSI escape sequences.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static partial class AnsiConsole
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Displays a prompt to the user.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <typeparam name="T">The prompt result type.</typeparam>
 | 
			
		||||
        /// <param name="prompt">The prompt to display.</param>
 | 
			
		||||
        /// <returns>The prompt input result.</returns>
 | 
			
		||||
        public static T Prompt<T>(IPrompt<T> prompt)
 | 
			
		||||
        {
 | 
			
		||||
            if (prompt is null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentNullException(nameof(prompt));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return prompt.Show(Console);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Displays a prompt to the user.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <typeparam name="T">The prompt result type.</typeparam>
 | 
			
		||||
        /// <param name="prompt">The prompt markup text.</param>
 | 
			
		||||
        /// <returns>The prompt input result.</returns>
 | 
			
		||||
        public static T Ask<T>(string prompt)
 | 
			
		||||
        {
 | 
			
		||||
            return new TextPrompt<T>(prompt).Show(Console);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Displays a prompt with two choices, yes or no.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="prompt">The prompt markup text.</param>
 | 
			
		||||
        /// <returns><c>true</c> if the user selected "yes", otherwise <c>false</c>.</returns>
 | 
			
		||||
        public static bool Confirm(string prompt)
 | 
			
		||||
        {
 | 
			
		||||
            return new ConfirmationPrompt(prompt).Show(Console);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										62
									
								
								src/Spectre.Console/ConfirmationPrompt.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/Spectre.Console/ConfirmationPrompt.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
			
		||||
namespace Spectre.Console
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// A prompt that is answered with a yes or no.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public sealed class ConfirmationPrompt : IPrompt<bool>
 | 
			
		||||
    {
 | 
			
		||||
        private readonly string _prompt;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the character that represents "yes".
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public char Yes { get; set; } = 'y';
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the character that represents "no".
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public char No { get; set; } = 'n';
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the message for invalid choices.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string InvalidChoiceMessage { get; set; } = "[red]Please select one of the available options[/]";
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets a value indicating whether or not
 | 
			
		||||
        /// choices should be shown.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public bool ShowChoices { get; set; } = true;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets a value indicating whether or not
 | 
			
		||||
        /// default values should be shown.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public bool ShowDefaultValue { get; set; } = true;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Initializes a new instance of the <see cref="ConfirmationPrompt"/> class.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="prompt">The prompt markup text.</param>
 | 
			
		||||
        public ConfirmationPrompt(string prompt)
 | 
			
		||||
        {
 | 
			
		||||
            _prompt = prompt ?? throw new System.ArgumentNullException(nameof(prompt));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public bool Show(IAnsiConsole console)
 | 
			
		||||
        {
 | 
			
		||||
            var prompt = new TextPrompt<char>(_prompt)
 | 
			
		||||
                .InvalidChoiceMessage(InvalidChoiceMessage)
 | 
			
		||||
                .ValidationErrorMessage(InvalidChoiceMessage)
 | 
			
		||||
                .ShowChoices(ShowChoices)
 | 
			
		||||
                .ShowDefaultValue(ShowDefaultValue)
 | 
			
		||||
                .DefaultValue(Yes)
 | 
			
		||||
                .AddChoice(Yes)
 | 
			
		||||
                .AddChoice(No);
 | 
			
		||||
 | 
			
		||||
            var result = prompt.Show(console);
 | 
			
		||||
            return result == Yes;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,49 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Spectre.Console
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Contains extension methods for <see cref="IAnsiConsole"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static partial class AnsiConsoleExtensions
 | 
			
		||||
    {
 | 
			
		||||
        internal static string ReadLine(this IAnsiConsole console, Style? style, bool secret)
 | 
			
		||||
        {
 | 
			
		||||
            if (console is null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentNullException(nameof(console));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            style ??= Style.Plain;
 | 
			
		||||
 | 
			
		||||
            var result = string.Empty;
 | 
			
		||||
            while (true)
 | 
			
		||||
            {
 | 
			
		||||
                var key = console.Input.ReadKey(true);
 | 
			
		||||
 | 
			
		||||
                if (key.Key == ConsoleKey.Enter)
 | 
			
		||||
                {
 | 
			
		||||
                    return result;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (key.Key == ConsoleKey.Backspace)
 | 
			
		||||
                {
 | 
			
		||||
                    if (result.Length > 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        result = result.Substring(0, result.Length - 1);
 | 
			
		||||
                        console.Write("\b \b");
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                result += key.KeyChar.ToString();
 | 
			
		||||
 | 
			
		||||
                if (!char.IsControl(key.KeyChar))
 | 
			
		||||
                {
 | 
			
		||||
                    console.Write(secret ? "*" : key.KeyChar.ToString(), style);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,50 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Spectre.Console
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Contains extension methods for <see cref="IAnsiConsole"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static partial class AnsiConsoleExtensions
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Displays a prompt to the user.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <typeparam name="T">The prompt result type.</typeparam>
 | 
			
		||||
        /// <param name="console">The console.</param>
 | 
			
		||||
        /// <param name="prompt">The prompt to display.</param>
 | 
			
		||||
        /// <returns>The prompt input result.</returns>
 | 
			
		||||
        public static T Prompt<T>(this IAnsiConsole console, IPrompt<T> prompt)
 | 
			
		||||
        {
 | 
			
		||||
            if (prompt is null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentNullException(nameof(prompt));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return prompt.Show(console);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Displays a prompt to the user.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <typeparam name="T">The prompt result type.</typeparam>
 | 
			
		||||
        /// <param name="console">The console.</param>
 | 
			
		||||
        /// <param name="prompt">The prompt markup text.</param>
 | 
			
		||||
        /// <returns>The prompt input result.</returns>
 | 
			
		||||
        public static T Ask<T>(this IAnsiConsole console, string prompt)
 | 
			
		||||
        {
 | 
			
		||||
            return new TextPrompt<T>(prompt).Show(console);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Displays a prompt with two choices, yes or no.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="console">The console.</param>
 | 
			
		||||
        /// <param name="prompt">The prompt markup text.</param>
 | 
			
		||||
        /// <returns><c>true</c> if the user selected "yes", otherwise <c>false</c>.</returns>
 | 
			
		||||
        public static bool Confirm(this IAnsiConsole console, string prompt)
 | 
			
		||||
        {
 | 
			
		||||
            return new ConfirmationPrompt(prompt).Show(console);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -18,6 +18,16 @@ namespace Spectre.Console
 | 
			
		||||
            return new Recorder(console);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Writes the specified string value to the console.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="console">The console to write to.</param>
 | 
			
		||||
        /// <param name="text">The text to write.</param>
 | 
			
		||||
        public static void Write(this IAnsiConsole console, string text)
 | 
			
		||||
        {
 | 
			
		||||
            Write(console, text, Style.Plain);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Writes the specified string value to the console.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
@@ -31,6 +41,11 @@ namespace Spectre.Console
 | 
			
		||||
                throw new ArgumentNullException(nameof(console));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (text is null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentNullException(nameof(text));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            console.Write(new Segment(text, style));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -48,6 +63,16 @@ namespace Spectre.Console
 | 
			
		||||
            console.Write(Environment.NewLine, Style.Plain);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Writes the specified string value, followed by the current line terminator, to the console.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="console">The console to write to.</param>
 | 
			
		||||
        /// <param name="text">The text to write.</param>
 | 
			
		||||
        public static void WriteLine(this IAnsiConsole console, string text)
 | 
			
		||||
        {
 | 
			
		||||
            WriteLine(console, text, Style.Plain);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Writes the specified string value, followed by the current line terminator, to the console.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
@@ -61,6 +86,11 @@ namespace Spectre.Console
 | 
			
		||||
                throw new ArgumentNullException(nameof(console));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (text is null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentNullException(nameof(text));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            console.Write(new Segment(text, style));
 | 
			
		||||
            console.WriteLine();
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										135
									
								
								src/Spectre.Console/Extensions/ConfirmationPromptExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								src/Spectre.Console/Extensions/ConfirmationPromptExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,135 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Spectre.Console
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Contains extension methods for <see cref="ConfirmationPrompt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static class ConfirmationPromptExtensions
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Show or hide choices.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="obj">The prompt.</param>
 | 
			
		||||
        /// <param name="show">Whether or not the choices should be visible.</param>
 | 
			
		||||
        /// <returns>The same instance so that multiple calls can be chained.</returns>
 | 
			
		||||
        public static ConfirmationPrompt ShowChoices(this ConfirmationPrompt obj, bool show)
 | 
			
		||||
        {
 | 
			
		||||
            if (obj is null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentNullException(nameof(obj));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            obj.ShowChoices = show;
 | 
			
		||||
            return obj;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Shows choices.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="obj">The prompt.</param>
 | 
			
		||||
        /// <returns>The same instance so that multiple calls can be chained.</returns>
 | 
			
		||||
        public static ConfirmationPrompt ShowChoices(this ConfirmationPrompt obj)
 | 
			
		||||
        {
 | 
			
		||||
            return ShowChoices(obj, true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Hides choices.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="obj">The prompt.</param>
 | 
			
		||||
        /// <returns>The same instance so that multiple calls can be chained.</returns>
 | 
			
		||||
        public static ConfirmationPrompt HideChoices(this ConfirmationPrompt obj)
 | 
			
		||||
        {
 | 
			
		||||
            return ShowChoices(obj, false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Show or hide the default value.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <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 ConfirmationPrompt ShowDefaultValue(this ConfirmationPrompt obj, bool show)
 | 
			
		||||
        {
 | 
			
		||||
            if (obj is null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentNullException(nameof(obj));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            obj.ShowDefaultValue = show;
 | 
			
		||||
            return obj;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Shows the default value.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="obj">The prompt.</param>
 | 
			
		||||
        /// <returns>The same instance so that multiple calls can be chained.</returns>
 | 
			
		||||
        public static ConfirmationPrompt ShowDefaultValue(this ConfirmationPrompt obj)
 | 
			
		||||
        {
 | 
			
		||||
            return ShowDefaultValue(obj, true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Hides the default value.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="obj">The prompt.</param>
 | 
			
		||||
        /// <returns>The same instance so that multiple calls can be chained.</returns>
 | 
			
		||||
        public static ConfirmationPrompt HideDefaultValue(this ConfirmationPrompt obj)
 | 
			
		||||
        {
 | 
			
		||||
            return ShowDefaultValue(obj, false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Sets the "invalid choice" message for the prompt.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <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 ConfirmationPrompt InvalidChoiceMessage(this ConfirmationPrompt obj, string message)
 | 
			
		||||
        {
 | 
			
		||||
            if (obj is null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentNullException(nameof(obj));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            obj.InvalidChoiceMessage = message;
 | 
			
		||||
            return obj;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Sets the character to interpret as "yes".
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="obj">The confirmation prompt.</param>
 | 
			
		||||
        /// <param name="character">The character to interpret as "yes".</param>
 | 
			
		||||
        /// <returns>The same instance so that multiple calls can be chained.</returns>
 | 
			
		||||
        public static ConfirmationPrompt Yes(this ConfirmationPrompt obj, char character)
 | 
			
		||||
        {
 | 
			
		||||
            if (obj is null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentNullException(nameof(obj));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            obj.Yes = character;
 | 
			
		||||
            return obj;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Sets the character to interpret as "no".
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="obj">The confirmation prompt.</param>
 | 
			
		||||
        /// <param name="character">The character to interpret as "no".</param>
 | 
			
		||||
        /// <returns>The same instance so that multiple calls can be chained.</returns>
 | 
			
		||||
        public static ConfirmationPrompt No(this ConfirmationPrompt obj, char character)
 | 
			
		||||
        {
 | 
			
		||||
            if (obj is null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentNullException(nameof(obj));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            obj.No = character;
 | 
			
		||||
            return obj;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										266
									
								
								src/Spectre.Console/Extensions/TextPromptExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										266
									
								
								src/Spectre.Console/Extensions/TextPromptExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,266 @@
 | 
			
		||||
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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -23,6 +23,11 @@ namespace Spectre.Console
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        IAnsiConsoleCursor Cursor { get; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets the console input.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        IAnsiConsoleInput Input { get; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets the buffer width of the console.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								src/Spectre.Console/IAnsiConsoleInput.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/Spectre.Console/IAnsiConsoleInput.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Spectre.Console
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the console's input mechanism.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public interface IAnsiConsoleInput
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Reads a key from the console.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="intercept">Whether or not to intercept the key.</param>
 | 
			
		||||
        /// <returns>The key that was read.</returns>
 | 
			
		||||
        ConsoleKeyInfo ReadKey(bool intercept);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								src/Spectre.Console/IPrompt.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/Spectre.Console/IPrompt.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
namespace Spectre.Console
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents a prompt.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="T">The prompt result type.</typeparam>
 | 
			
		||||
    public interface IPrompt<T>
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Shows the prompt.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="console">The console.</param>
 | 
			
		||||
        /// <returns>The prompt input result.</returns>
 | 
			
		||||
        T Show(IAnsiConsole console);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -10,10 +10,12 @@ namespace Spectre.Console.Internal
 | 
			
		||||
        private readonly TextWriter _out;
 | 
			
		||||
        private readonly AnsiBuilder _ansiBuilder;
 | 
			
		||||
        private readonly AnsiCursor _cursor;
 | 
			
		||||
        private readonly ConsoleInput _input;
 | 
			
		||||
 | 
			
		||||
        public Capabilities Capabilities { get; }
 | 
			
		||||
        public Encoding Encoding { get; }
 | 
			
		||||
        public IAnsiConsoleCursor Cursor => _cursor;
 | 
			
		||||
        public IAnsiConsoleInput Input => _input;
 | 
			
		||||
 | 
			
		||||
        public int Width
 | 
			
		||||
        {
 | 
			
		||||
@@ -50,6 +52,7 @@ namespace Spectre.Console.Internal
 | 
			
		||||
 | 
			
		||||
            _ansiBuilder = new AnsiBuilder(Capabilities, linkHasher);
 | 
			
		||||
            _cursor = new AnsiCursor(this);
 | 
			
		||||
            _input = new ConsoleInput();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Clear(bool home)
 | 
			
		||||
 
 | 
			
		||||
@@ -9,11 +9,13 @@ namespace Spectre.Console.Internal
 | 
			
		||||
    {
 | 
			
		||||
        private readonly ColorSystem _system;
 | 
			
		||||
        private readonly FallbackCursor _cursor;
 | 
			
		||||
        private readonly ConsoleInput _input;
 | 
			
		||||
        private Style? _lastStyle;
 | 
			
		||||
 | 
			
		||||
        public Capabilities Capabilities { get; }
 | 
			
		||||
        public Encoding Encoding { get; }
 | 
			
		||||
        public IAnsiConsoleCursor Cursor => _cursor;
 | 
			
		||||
        public IAnsiConsoleInput Input => _input;
 | 
			
		||||
 | 
			
		||||
        public int Width
 | 
			
		||||
        {
 | 
			
		||||
@@ -34,6 +36,7 @@ namespace Spectre.Console.Internal
 | 
			
		||||
 | 
			
		||||
            _system = capabilities.ColorSystem;
 | 
			
		||||
            _cursor = new FallbackCursor();
 | 
			
		||||
            _input = new ConsoleInput();
 | 
			
		||||
 | 
			
		||||
            if (@out != System.Console.Out)
 | 
			
		||||
            {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								src/Spectre.Console/Internal/ConsoleInput.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/Spectre.Console/Internal/ConsoleInput.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Spectre.Console.Internal
 | 
			
		||||
{
 | 
			
		||||
    internal sealed class ConsoleInput : IAnsiConsoleInput
 | 
			
		||||
    {
 | 
			
		||||
        public ConsoleKeyInfo ReadKey(bool intercept)
 | 
			
		||||
        {
 | 
			
		||||
            if (!Environment.UserInteractive)
 | 
			
		||||
            {
 | 
			
		||||
                throw new InvalidOperationException("Failed to read input in non-interactive mode.");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return System.Console.ReadKey(intercept);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -22,6 +22,9 @@ namespace Spectre.Console
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public IAnsiConsoleCursor Cursor => _console.Cursor;
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public IAnsiConsoleInput Input => _console.Input;
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public int Width => _console.Width;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,27 +10,6 @@
 | 
			
		||||
    <None Include="../../resources/gfx/small-logo.png" Pack="true" PackagePath="\" Link="Properties/small-logo.png" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <Compile Update="AnsiConsole.*.cs">
 | 
			
		||||
      <DependentUpon>AnsiConsole.cs</DependentUpon>
 | 
			
		||||
    </Compile>
 | 
			
		||||
    <Compile Update="Border.*.cs">
 | 
			
		||||
      <DependentUpon>Border.cs</DependentUpon>
 | 
			
		||||
    </Compile>
 | 
			
		||||
    <Compile Update="BoxBorder.*.cs">
 | 
			
		||||
      <DependentUpon>BoxBorder.cs</DependentUpon>
 | 
			
		||||
    </Compile>
 | 
			
		||||
    <Compile Update="Color.*.cs">
 | 
			
		||||
      <DependentUpon>Color.cs</DependentUpon>
 | 
			
		||||
    </Compile>
 | 
			
		||||
    <Compile Update="Emoji.*.cs">
 | 
			
		||||
      <DependentUpon>Emoji.cs</DependentUpon>
 | 
			
		||||
    </Compile>
 | 
			
		||||
    <Compile Update="Extensions/AnsiConsoleExtensions.*.cs">
 | 
			
		||||
      <DependentUpon>Extensions/AnsiConsoleExtensions.cs</DependentUpon>
 | 
			
		||||
    </Compile>
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <PackageReference Include="TunnelVisionLabs.ReferenceAssemblyAnnotator" Version="1.0.0-alpha.160" PrivateAssets="all" />
 | 
			
		||||
    <PackageDownload Include="Microsoft.NETCore.App.Ref" Version="[$(AnnotatedReferenceAssemblyVersion)]" />
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										284
									
								
								src/Spectre.Console/TextPrompt.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										284
									
								
								src/Spectre.Console/TextPrompt.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,284 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
using System.Diagnostics.CodeAnalysis;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Spectre.Console
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents a prompt.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="T">The prompt result type.</typeparam>
 | 
			
		||||
    public sealed class TextPrompt<T> : IPrompt<T>
 | 
			
		||||
    {
 | 
			
		||||
        private readonly string _prompt;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the prompt style.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public Style? PromptStyle { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets the list of choices.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public HashSet<T> Choices { get; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the message for invalid choices.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string InvalidChoiceMessage { get; set; } = "[red]Please select one of the available options[/]";
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets a value indicating whether input should
 | 
			
		||||
        /// be hidden in the console.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public bool IsSecret { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the validation error message.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string ValidationErrorMessage { get; set; } = "[red]Invalid input[/]";
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets a value indicating whether or not
 | 
			
		||||
        /// choices should be shown.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public bool ShowChoices { get; set; } = true;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets a value indicating whether or not
 | 
			
		||||
        /// default values should be shown.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public bool ShowDefaultValue { get; set; } = true;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets a value indicating whether or not an empty result is valid.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public bool AllowEmpty { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the validator.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public Func<T, ValidationResult>? Validator { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the default value.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        internal DefaultValueContainer? DefaultValue { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// A nullable container for a default value.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        internal sealed class DefaultValueContainer
 | 
			
		||||
        {
 | 
			
		||||
            /// <summary>
 | 
			
		||||
            /// Gets the default value.
 | 
			
		||||
            /// </summary>
 | 
			
		||||
            public T Value { get; }
 | 
			
		||||
 | 
			
		||||
            /// <summary>
 | 
			
		||||
            /// Initializes a new instance of the <see cref="DefaultValueContainer"/> class.
 | 
			
		||||
            /// </summary>
 | 
			
		||||
            /// <param name="value">The default value.</param>
 | 
			
		||||
            public DefaultValueContainer(T value)
 | 
			
		||||
            {
 | 
			
		||||
                Value = value;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Initializes a new instance of the <see cref="TextPrompt{T}"/> class.
 | 
			
		||||
        /// </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)
 | 
			
		||||
        {
 | 
			
		||||
            _prompt = prompt;
 | 
			
		||||
 | 
			
		||||
            Choices = new HashSet<T>(comparer ?? EqualityComparer<T>.Default);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Shows the prompt and requests input from the user.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="console">The console to show the prompt in.</param>
 | 
			
		||||
        /// <returns>The user input converted to the expected type.</returns>
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public T Show(IAnsiConsole console)
 | 
			
		||||
        {
 | 
			
		||||
            if (console is null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentNullException(nameof(console));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var promptStyle = PromptStyle ?? Style.Plain;
 | 
			
		||||
 | 
			
		||||
            WritePrompt(console);
 | 
			
		||||
 | 
			
		||||
            while (true)
 | 
			
		||||
            {
 | 
			
		||||
                var input = console.ReadLine(promptStyle, IsSecret);
 | 
			
		||||
 | 
			
		||||
                // Nothing entered?
 | 
			
		||||
                if (string.IsNullOrWhiteSpace(input))
 | 
			
		||||
                {
 | 
			
		||||
                    if (DefaultValue != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        console.Write(TextPrompt<T>.GetTypeConverter().ConvertToInvariantString(DefaultValue.Value), promptStyle);
 | 
			
		||||
                        console.WriteLine();
 | 
			
		||||
                        return DefaultValue.Value;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (!AllowEmpty)
 | 
			
		||||
                    {
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                console.WriteLine();
 | 
			
		||||
 | 
			
		||||
                // Try convert the value to the expected type.
 | 
			
		||||
                if (!TextPrompt<T>.TryConvert(input, out var result) || result == null)
 | 
			
		||||
                {
 | 
			
		||||
                    console.MarkupLine(ValidationErrorMessage);
 | 
			
		||||
                    WritePrompt(console);
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (Choices.Count > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    if (Choices.Contains(result))
 | 
			
		||||
                    {
 | 
			
		||||
                        return result;
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        console.MarkupLine(InvalidChoiceMessage);
 | 
			
		||||
                        WritePrompt(console);
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Run all validators
 | 
			
		||||
                if (!ValidateResult(result, out var validationMessage))
 | 
			
		||||
                {
 | 
			
		||||
                    console.MarkupLine(validationMessage);
 | 
			
		||||
                    WritePrompt(console);
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return result;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Writes the prompt to the console.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="console">The console to write the prompt to.</param>
 | 
			
		||||
        private void WritePrompt(IAnsiConsole console)
 | 
			
		||||
        {
 | 
			
		||||
            if (console is null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentNullException(nameof(console));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var builder = new StringBuilder();
 | 
			
		||||
            builder.Append(_prompt.TrimEnd());
 | 
			
		||||
 | 
			
		||||
            if (ShowChoices && Choices.Count > 0)
 | 
			
		||||
            {
 | 
			
		||||
                var choices = string.Join("/", Choices.Select(choice => TextPrompt<T>.GetTypeConverter().ConvertToInvariantString(choice)));
 | 
			
		||||
                builder.AppendFormat(CultureInfo.InvariantCulture, " [blue][[{0}]][/]", choices);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (ShowDefaultValue && DefaultValue != null)
 | 
			
		||||
            {
 | 
			
		||||
                builder.AppendFormat(
 | 
			
		||||
                    CultureInfo.InvariantCulture,
 | 
			
		||||
                    " [green]({0})[/]",
 | 
			
		||||
                    TextPrompt<T>.GetTypeConverter().ConvertToInvariantString(DefaultValue.Value));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var markup = builder.ToString().Trim();
 | 
			
		||||
            if (!markup.EndsWith("?", StringComparison.OrdinalIgnoreCase) &&
 | 
			
		||||
                !markup.EndsWith(":", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            {
 | 
			
		||||
                markup += ":";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            console.Markup(markup + " ");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Tries to convert the input string to <typeparamref name="T"/>.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="input">The input to convert.</param>
 | 
			
		||||
        /// <param name="result">The result.</param>
 | 
			
		||||
        /// <returns><c>true</c> if the conversion succeeded, otherwise <c>false</c>.</returns>
 | 
			
		||||
        [SuppressMessage("Design", "CA1031:Do not catch general exception types")]
 | 
			
		||||
        private static bool TryConvert(string input, [MaybeNull] out T result)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                result = (T)TextPrompt<T>.GetTypeConverter().ConvertFromInvariantString(input);
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
#pragma warning disable CS8601 // Possible null reference assignment.
 | 
			
		||||
                result = default;
 | 
			
		||||
#pragma warning restore CS8601 // Possible null reference assignment.
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets the type converter that's used to convert values.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <returns>The type converter that's used to convert values.</returns>
 | 
			
		||||
        private static TypeConverter GetTypeConverter()
 | 
			
		||||
        {
 | 
			
		||||
            var converter = TypeDescriptor.GetConverter(typeof(T));
 | 
			
		||||
            if (converter != null)
 | 
			
		||||
            {
 | 
			
		||||
                return converter;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var attribute = typeof(T).GetCustomAttribute<TypeConverterAttribute>();
 | 
			
		||||
            if (attribute != null)
 | 
			
		||||
            {
 | 
			
		||||
                var type = Type.GetType(attribute.ConverterTypeName, false, false);
 | 
			
		||||
                if (type != null)
 | 
			
		||||
                {
 | 
			
		||||
                    converter = Activator.CreateInstance(type) as TypeConverter;
 | 
			
		||||
                    if (converter != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        return converter;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            throw new InvalidOperationException("Could not find type converter");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private bool ValidateResult(T value, [NotNullWhen(false)] out string? message)
 | 
			
		||||
        {
 | 
			
		||||
            if (Validator != null)
 | 
			
		||||
            {
 | 
			
		||||
                var result = Validator(value);
 | 
			
		||||
                if (!result.Successful)
 | 
			
		||||
                {
 | 
			
		||||
                    message = result.Message ?? ValidationErrorMessage;
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            message = null;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								src/Spectre.Console/ValidationResult.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/Spectre.Console/ValidationResult.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
namespace Spectre.Console
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents a validation result.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public sealed class ValidationResult
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets a value indicating whether or not validation was successful.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public bool Successful { get; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets the validation error message.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string? Message { get; }
 | 
			
		||||
 | 
			
		||||
        private ValidationResult(bool successful, string? message)
 | 
			
		||||
        {
 | 
			
		||||
            Successful = successful;
 | 
			
		||||
            Message = message;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Returns a <see cref="ValidationResult"/> representing successful validation.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <returns>The validation result.</returns>
 | 
			
		||||
        public static ValidationResult Success()
 | 
			
		||||
        {
 | 
			
		||||
            return new ValidationResult(true, null);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Returns a <see cref="ValidationResult"/> representing a validation error.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="message">The validation error message, or <c>null</c> to show the default validation error message.</param>
 | 
			
		||||
        /// <returns>The validation result.</returns>
 | 
			
		||||
        public static ValidationResult Error(string? message = null)
 | 
			
		||||
        {
 | 
			
		||||
            return new ValidationResult(false, message);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user