Skip to content

Commit a094a1c

Browse files
committed
Add support for Esc key to cancel SelectionPrompt (Fixes #851)
1 parent 6ad814c commit a094a1c

File tree

5 files changed

+1170
-1
lines changed

5 files changed

+1170
-1
lines changed

src/Spectre.Console/Prompts/List/ListPrompt.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,18 @@ public async Task<ListPromptState<T>> Show(
6666

6767
var key = rawKey.Value;
6868
var result = _strategy.HandleInput(key, state);
69+
6970
if (result == ListPromptInputResult.Submit)
7071
{
7172
break;
7273
}
7374

75+
if (result == ListPromptInputResult.Cancel)
76+
{
77+
throw new OperationCanceledException("Prompt cancelled via ESC key.");
78+
}
79+
80+
7481
if (state.Update(key) || result == ListPromptInputResult.Refresh)
7582
{
7683
hook.Refresh();

src/Spectre.Console/Prompts/List/ListPromptInputResult.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ internal enum ListPromptInputResult
66
Refresh = 1,
77
Submit = 2,
88
Abort = 3,
9+
Cancel = 4,
910
}

src/Spectre.Console/Prompts/SelectionPrompt.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ public async Task<T> ShowAsync(IAnsiConsole console, CancellationToken cancellat
109109
/// <inheritdoc/>
110110
ListPromptInputResult IListPromptStrategy<T>.HandleInput(ConsoleKeyInfo key, ListPromptState<T> state)
111111
{
112+
if (key.Key == ConsoleKey.Escape)
113+
{
114+
return ListPromptInputResult.Cancel;
115+
}
116+
112117
if (key.Key == ConsoleKey.Enter || key.Key == ConsoleKey.Spacebar || key.Key == ConsoleKey.Packet)
113118
{
114119
// Selecting a non leaf in "leaf mode" is not allowed

src/Tests/Spectre.Console.Tests/Unit/Prompts/SelectionPromptTests.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ public void Should_Search_In_Remapped_Result()
115115
selection.ShouldBe(choices[1]);
116116
}
117117

118-
[Fact] public void Should_Throw_Meaningful_Exception_For_Empty_Prompt()
118+
[Fact]
119+
public void Should_Throw_Meaningful_Exception_For_Empty_Prompt()
119120
{
120121
// Given
121122
var console = new TestConsole();
@@ -130,6 +131,26 @@ [Fact] public void Should_Throw_Meaningful_Exception_For_Empty_Prompt()
130131
var exception = action.ShouldThrow<InvalidOperationException>();
131132
exception.Message.ShouldBe("Cannot show an empty selection prompt. Please call the AddChoice() method to configure the prompt.");
132133
}
134+
135+
[Fact]
136+
public void SelectionPrompt_Should_Cancel_On_Esc()
137+
{
138+
// Arrange
139+
var console = new TestConsole();
140+
console.Profile.Capabilities.Ansi = true;
141+
console.Profile.Capabilities.Interactive = true;
142+
console.Input.PushKey(new ConsoleKeyInfo('\0', ConsoleKey.Escape, false, false, false));
143+
144+
var prompt = new SelectionPrompt<string>();
145+
prompt.AddChoice("Option A");
146+
prompt.AddChoice("Option B");
147+
148+
// Act & Assert
149+
Assert.Throws<OperationCanceledException>(() =>
150+
{
151+
prompt.Show(console);
152+
});
153+
}
133154
}
134155

135156
file sealed class CustomSelectionItem
@@ -143,3 +164,5 @@ public CustomSelectionItem(int value, string name)
143164
Name = name ?? throw new ArgumentNullException(nameof(name));
144165
}
145166
}
167+
168+

src/Tests/Spectre.Console.Tests/sdk-9.0.202-linux-x64-binaries

Lines changed: 1133 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)