Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,32 @@
using System.Reflection;

namespace Spectre.Console.Tests.Unit;

public sealed class MultiSelectionPromptTests
{
[Fact]
public void Should_Show_Cursor_When_Hiding_It_Throws()
{
// Given
var console = new TestConsole();
console.Profile.Capabilities.Interactive = true;
console.Profile.Capabilities.Ansi = true;

var cursor = new ThrowingCursor();
var setCursorMethod = typeof(TestConsole).GetMethod("SetCursor", BindingFlags.Instance | BindingFlags.NonPublic);
setCursorMethod!.Invoke(console, [cursor]);

var prompt = new MultiSelectionPrompt<string>();
prompt.AddChoices(["A", "B", "C"]);

// When
Action action = () => prompt.Show(console);

// Then
action.ShouldThrow<InvalidOperationException>();
cursor.Calls.ShouldContain("show");
}

[Fact]
public void Should_Not_Mark_Item_As_Selected_By_Default()
{
Expand Down
26 changes: 26 additions & 0 deletions src/Spectre.Console.Tests/Unit/Prompts/ThrowingCursor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace Spectre.Console.Tests.Unit;

internal sealed class ThrowingCursor : IAnsiConsoleCursor
{
public List<string> Calls { get; } = [];

public void Show(bool show)
{
Calls.Add(show ? "show" : "hide");

if (show == false)
{
throw new InvalidOperationException("boom");
}
}

public void SetPosition(int column, int line)
{
Calls.Add($"set:{column}:{line}");
}

public void Move(CursorDirection direction, int steps)
{
Calls.Add($"move:{direction}:{steps}");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -965,13 +965,13 @@ private static readonly Dictionary<string, string> _emojis
{ "pig_nose", Emoji.Known.PigNose },
{ "pile_of_poo", Emoji.Known.PileOfPoo },
{ "pill", Emoji.Known.Pill },
{ "piñata", Emoji.Known.Piñata },
{ "pinched_fingers", Emoji.Known.PinchedFingers },
{ "pinching_hand", Emoji.Known.PinchingHand },
{ "pineapple", Emoji.Known.Pineapple },
{ "pine_decoration", Emoji.Known.PineDecoration },
{ "ping_pong", Emoji.Known.PingPong },
{ "pink_heart", Emoji.Known.PinkHeart },
{ "piñata", Emoji.Known.Piñata },
{ "pisces", Emoji.Known.Pisces },
{ "pizza", Emoji.Known.Pizza },
{ "placard", Emoji.Known.Placard },
Expand Down Expand Up @@ -9016,14 +9016,6 @@ public static class Known
/// </remarks>
public const string Pill = "\U0001F48A";

/// <summary>
/// Gets the "Piñata" emoji. 🪅
/// </summary>
/// <remarks>
/// Lookup: <c>piñata</c>
/// </remarks>
public const string Piñata = "\U0001FA85";

/// <summary>
/// Gets the "Pinched fingers" emoji. 🤌
/// </summary>
Expand Down Expand Up @@ -9072,6 +9064,14 @@ public static class Known
/// </remarks>
public const string PinkHeart = "\U0001FA77";

/// <summary>
/// Gets the "Piñata" emoji. 🪅
/// </summary>
/// <remarks>
/// Lookup: <c>piñata</c>
/// </remarks>
public const string Piñata = "\U0001FA85";

/// <summary>
/// Gets the "Pisces" emoji. ♓️
/// </summary>
Expand Down
70 changes: 43 additions & 27 deletions src/Spectre.Console/Prompts/List/ListPrompt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,43 +53,58 @@ public async Task<ListPromptState<T>> Show(
skipUnselectableItems,
searchEnabled,
_strategy.CalculateInitialIndex(nodes));

var hook = new ListPromptRenderHook<T>(_console, () => BuildRenderable(state));

using (new RenderHookScope(_console, hook))
try
{
_console.Cursor.Hide();
hook.Refresh();

while (true)
using (new RenderHookScope(_console, hook))
{
cancellationToken.ThrowIfCancellationRequested();
var rawKey = await _console.Input.ReadKeyAsync(true, cancellationToken).ConfigureAwait(false);
if (rawKey == null)
try
{
continue;
}
_console.Cursor.Hide();
hook.Refresh();

var key = rawKey.Value;
var result = _strategy.HandleInput(key, state);
if (result == ListPromptInputResult.Submit)
{
break;
}
else if (result == ListPromptInputResult.Abort)
{
state.Cancel();
break;
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
var rawKey = await _console.Input.ReadKeyAsync(true, cancellationToken).ConfigureAwait(false);
if (rawKey == null)
{
continue;
}

var key = rawKey.Value;

var result = _strategy.HandleInput(key, state);

if (result == ListPromptInputResult.Submit)
{
break;
}
else if (result == ListPromptInputResult.Abort)
{
state.Cancel();
break;
}
var stateUpdated = state.Update(key);

if (stateUpdated || result == ListPromptInputResult.Refresh)
{
hook.Refresh();
}
}
}

if (state.Update(key) || result == ListPromptInputResult.Refresh)
finally
{
hook.Refresh();
_console.Cursor.Show();
}
}
}

hook.Clear();
_console.Cursor.Show();
finally
{
hook.Clear();
}

return state;
}
Expand Down Expand Up @@ -127,7 +142,8 @@ private IRenderable BuildRenderable(ListPromptState<T> state)
// Build the renderable
return _strategy.Render(
_console,
scrollable, cursorIndex,
scrollable,
cursorIndex,
state.Items.Skip(skip).Take(take)
.Select((node, index) => (index, node)),
state.SkipUnselectableItems,
Expand Down