diff --git a/src/Spectre.Console/Prompts/List/ListPromptState.cs b/src/Spectre.Console/Prompts/List/ListPromptState.cs index fdbe3c803..29a66768c 100644 --- a/src/Spectre.Console/Prompts/List/ListPromptState.cs +++ b/src/Spectre.Console/Prompts/List/ListPromptState.cs @@ -63,6 +63,7 @@ public bool Update(ConsoleKeyInfo keyInfo) switch (keyInfo.Key) { case ConsoleKey.UpArrow: + case ConsoleKey.K: if (currentLeafIndex > 0) { index = _leafIndexes[currentLeafIndex - 1]; @@ -75,6 +76,7 @@ public bool Update(ConsoleKeyInfo keyInfo) break; case ConsoleKey.DownArrow: + case ConsoleKey.J: if (currentLeafIndex < _leafIndexes.Count - 1) { index = _leafIndexes[currentLeafIndex + 1]; @@ -117,8 +119,8 @@ public bool Update(ConsoleKeyInfo keyInfo) { index = keyInfo.Key switch { - ConsoleKey.UpArrow => Index - 1, - ConsoleKey.DownArrow => Index + 1, + ConsoleKey.UpArrow or ConsoleKey.K => Index - 1, + ConsoleKey.DownArrow or ConsoleKey.J => Index + 1, ConsoleKey.Home => 0, ConsoleKey.End => ItemCount - 1, ConsoleKey.PageUp => Index - PageSize, diff --git a/src/Tests/Spectre.Console.Cli.Tests/Unit/Testing/InteractiveCommandTests.cs b/src/Tests/Spectre.Console.Cli.Tests/Unit/Testing/InteractiveCommandTests.cs index ec33c3898..98879e191 100644 --- a/src/Tests/Spectre.Console.Cli.Tests/Unit/Testing/InteractiveCommandTests.cs +++ b/src/Tests/Spectre.Console.Cli.Tests/Unit/Testing/InteractiveCommandTests.cs @@ -77,4 +77,35 @@ public void InteractiveCommand_WithMockedUserInputs_ProducesExpectedOutput() result.ExitCode.ShouldBe(0); result.Output.EndsWith("[Apple;Apricot;Spectre Console]"); } + + [Fact] + public void InteractiveCommand_WithMockedUserInputs_VimMotions_ProducesExpectedOutput() + { + // Given + TestConsole console = new(); + console.Interactive(); + + // Your mocked inputs must always end with "Enter" for each prompt! + + // Multi selection prompt: Choose first option + console.Input.PushKey(ConsoleKey.Spacebar); + console.Input.PushKey(ConsoleKey.Enter); + + // Selection prompt: Choose second option + console.Input.PushKey(ConsoleKey.J); + console.Input.PushKey(ConsoleKey.Enter); + + // Ask text prompt: Enter name + console.Input.PushTextWithEnter("Spectre Console"); + + var app = new CommandAppTester(null, new CommandAppTesterSettings(), console); + app.SetDefaultCommand(); + + // When + var result = app.Run(); + + // Then + result.ExitCode.ShouldBe(0); + result.Output.EndsWith("[Apple;Apricot;Spectre Console]"); + } } diff --git a/src/Tests/Spectre.Console.Tests/Unit/Prompts/ListPromptStateTests.cs b/src/Tests/Spectre.Console.Tests/Unit/Prompts/ListPromptStateTests.cs index 2a45b66bc..ff03df29b 100644 --- a/src/Tests/Spectre.Console.Tests/Unit/Prompts/ListPromptStateTests.cs +++ b/src/Tests/Spectre.Console.Tests/Unit/Prompts/ListPromptStateTests.cs @@ -22,16 +22,35 @@ public void Should_Have_Start_Index_Zero() } [Theory] - [InlineData(true)] - [InlineData(false)] - public void Should_Increase_Index(bool wrap) + [InlineData(ConsoleKey.UpArrow)] + [InlineData(ConsoleKey.K)] + public void Should_Decrease_Index(ConsoleKey key) + { + // Given + var state = CreateListPromptState(100, 10, false, false); + state.Update(ConsoleKey.End.ToConsoleKeyInfo()); + var index = state.Index; + + // When + state.Update(key.ToConsoleKeyInfo()); + + // Then + state.Index.ShouldBe(index - 1); + } + + [Theory] + [InlineData(ConsoleKey.DownArrow, true)] + [InlineData(ConsoleKey.DownArrow, false)] + [InlineData(ConsoleKey.J, true)] + [InlineData(ConsoleKey.J, false)] + public void Should_Increase_Index(ConsoleKey key, bool wrap) { // Given var state = CreateListPromptState(100, 10, wrap, false); var index = state.Index; // When - state.Update(ConsoleKey.DownArrow.ToConsoleKeyInfo()); + state.Update(key.ToConsoleKeyInfo()); // Then state.Index.ShouldBe(index + 1); @@ -52,42 +71,48 @@ public void Should_Go_To_End(bool wrap) state.Index.ShouldBe(99); } - [Fact] - public void Should_Clamp_Index_If_No_Wrap() + [Theory] + [InlineData(ConsoleKey.DownArrow)] + [InlineData(ConsoleKey.J)] + public void Should_Clamp_Index_If_No_Wrap(ConsoleKey key) { // Given var state = CreateListPromptState(100, 10, false, false); state.Update(ConsoleKey.End.ToConsoleKeyInfo()); // When - state.Update(ConsoleKey.DownArrow.ToConsoleKeyInfo()); + state.Update(key.ToConsoleKeyInfo()); // Then state.Index.ShouldBe(99); } - [Fact] - public void Should_Wrap_Index_If_Wrap() + [Theory] + [InlineData(ConsoleKey.DownArrow)] + [InlineData(ConsoleKey.J)] + public void Should_Wrap_Index_If_Wrap(ConsoleKey key) { // Given var state = CreateListPromptState(100, 10, true, false); state.Update(ConsoleKey.End.ToConsoleKeyInfo()); // When - state.Update(ConsoleKey.DownArrow.ToConsoleKeyInfo()); + state.Update(key.ToConsoleKeyInfo()); // Then state.Index.ShouldBe(0); } - [Fact] - public void Should_Wrap_Index_If_Wrap_And_Down() + [Theory] + [InlineData(ConsoleKey.UpArrow)] + [InlineData(ConsoleKey.K)] + public void Should_Wrap_Index_If_Wrap_And_Down(ConsoleKey key) { // Given var state = CreateListPromptState(100, 10, true, false); // When - state.Update(ConsoleKey.UpArrow.ToConsoleKeyInfo()); + state.Update(key.ToConsoleKeyInfo()); // Then state.Index.ShouldBe(99); @@ -106,13 +131,15 @@ public void Should_Wrap_Index_If_Wrap_And_Page_Up() state.Index.ShouldBe(0); } - [Fact] - public void Should_Wrap_Index_If_Wrap_And_Offset_And_Page_Down() + [Theory] + [InlineData(ConsoleKey.UpArrow)] + [InlineData(ConsoleKey.K)] + public void Should_Wrap_Index_If_Wrap_And_Offset_And_Page_Down(ConsoleKey key) { // Given var state = CreateListPromptState(10, 100, true, false); state.Update(ConsoleKey.End.ToConsoleKeyInfo()); - state.Update(ConsoleKey.UpArrow.ToConsoleKeyInfo()); + state.Update(key.ToConsoleKeyInfo()); // When state.Update(ConsoleKey.PageDown.ToConsoleKeyInfo());