mirror of
https://github.com/spectreconsole/spectre.console.git
synced 2025-12-26 15:57:58 +01:00
Support J and K for navigating list prompts (#1877)
This commit is contained in:
@@ -63,6 +63,7 @@ internal sealed class ListPromptState<T>
|
|||||||
switch (keyInfo.Key)
|
switch (keyInfo.Key)
|
||||||
{
|
{
|
||||||
case ConsoleKey.UpArrow:
|
case ConsoleKey.UpArrow:
|
||||||
|
case ConsoleKey.K:
|
||||||
if (currentLeafIndex > 0)
|
if (currentLeafIndex > 0)
|
||||||
{
|
{
|
||||||
index = _leafIndexes[currentLeafIndex - 1];
|
index = _leafIndexes[currentLeafIndex - 1];
|
||||||
@@ -75,6 +76,7 @@ internal sealed class ListPromptState<T>
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ConsoleKey.DownArrow:
|
case ConsoleKey.DownArrow:
|
||||||
|
case ConsoleKey.J:
|
||||||
if (currentLeafIndex < _leafIndexes.Count - 1)
|
if (currentLeafIndex < _leafIndexes.Count - 1)
|
||||||
{
|
{
|
||||||
index = _leafIndexes[currentLeafIndex + 1];
|
index = _leafIndexes[currentLeafIndex + 1];
|
||||||
@@ -117,8 +119,8 @@ internal sealed class ListPromptState<T>
|
|||||||
{
|
{
|
||||||
index = keyInfo.Key switch
|
index = keyInfo.Key switch
|
||||||
{
|
{
|
||||||
ConsoleKey.UpArrow => Index - 1,
|
ConsoleKey.UpArrow or ConsoleKey.K => Index - 1,
|
||||||
ConsoleKey.DownArrow => Index + 1,
|
ConsoleKey.DownArrow or ConsoleKey.J => Index + 1,
|
||||||
ConsoleKey.Home => 0,
|
ConsoleKey.Home => 0,
|
||||||
ConsoleKey.End => ItemCount - 1,
|
ConsoleKey.End => ItemCount - 1,
|
||||||
ConsoleKey.PageUp => Index - PageSize,
|
ConsoleKey.PageUp => Index - PageSize,
|
||||||
|
|||||||
@@ -77,4 +77,35 @@ public sealed class InteractiveCommandTests
|
|||||||
result.ExitCode.ShouldBe(0);
|
result.ExitCode.ShouldBe(0);
|
||||||
result.Output.EndsWith("[Apple;Apricot;Spectre Console]");
|
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<InteractiveCommand>();
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = app.Run();
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ExitCode.ShouldBe(0);
|
||||||
|
result.Output.EndsWith("[Apple;Apricot;Spectre Console]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,16 +22,35 @@ public sealed class ListPromptStateTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(true)]
|
[InlineData(ConsoleKey.UpArrow)]
|
||||||
[InlineData(false)]
|
[InlineData(ConsoleKey.K)]
|
||||||
public void Should_Increase_Index(bool wrap)
|
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
|
// Given
|
||||||
var state = CreateListPromptState(100, 10, wrap, false);
|
var state = CreateListPromptState(100, 10, wrap, false);
|
||||||
var index = state.Index;
|
var index = state.Index;
|
||||||
|
|
||||||
// When
|
// When
|
||||||
state.Update(ConsoleKey.DownArrow.ToConsoleKeyInfo());
|
state.Update(key.ToConsoleKeyInfo());
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
state.Index.ShouldBe(index + 1);
|
state.Index.ShouldBe(index + 1);
|
||||||
@@ -52,42 +71,48 @@ public sealed class ListPromptStateTests
|
|||||||
state.Index.ShouldBe(99);
|
state.Index.ShouldBe(99);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public void Should_Clamp_Index_If_No_Wrap()
|
[InlineData(ConsoleKey.DownArrow)]
|
||||||
|
[InlineData(ConsoleKey.J)]
|
||||||
|
public void Should_Clamp_Index_If_No_Wrap(ConsoleKey key)
|
||||||
{
|
{
|
||||||
// Given
|
// Given
|
||||||
var state = CreateListPromptState(100, 10, false, false);
|
var state = CreateListPromptState(100, 10, false, false);
|
||||||
state.Update(ConsoleKey.End.ToConsoleKeyInfo());
|
state.Update(ConsoleKey.End.ToConsoleKeyInfo());
|
||||||
|
|
||||||
// When
|
// When
|
||||||
state.Update(ConsoleKey.DownArrow.ToConsoleKeyInfo());
|
state.Update(key.ToConsoleKeyInfo());
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
state.Index.ShouldBe(99);
|
state.Index.ShouldBe(99);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public void Should_Wrap_Index_If_Wrap()
|
[InlineData(ConsoleKey.DownArrow)]
|
||||||
|
[InlineData(ConsoleKey.J)]
|
||||||
|
public void Should_Wrap_Index_If_Wrap(ConsoleKey key)
|
||||||
{
|
{
|
||||||
// Given
|
// Given
|
||||||
var state = CreateListPromptState(100, 10, true, false);
|
var state = CreateListPromptState(100, 10, true, false);
|
||||||
state.Update(ConsoleKey.End.ToConsoleKeyInfo());
|
state.Update(ConsoleKey.End.ToConsoleKeyInfo());
|
||||||
|
|
||||||
// When
|
// When
|
||||||
state.Update(ConsoleKey.DownArrow.ToConsoleKeyInfo());
|
state.Update(key.ToConsoleKeyInfo());
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
state.Index.ShouldBe(0);
|
state.Index.ShouldBe(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public void Should_Wrap_Index_If_Wrap_And_Down()
|
[InlineData(ConsoleKey.UpArrow)]
|
||||||
|
[InlineData(ConsoleKey.K)]
|
||||||
|
public void Should_Wrap_Index_If_Wrap_And_Down(ConsoleKey key)
|
||||||
{
|
{
|
||||||
// Given
|
// Given
|
||||||
var state = CreateListPromptState(100, 10, true, false);
|
var state = CreateListPromptState(100, 10, true, false);
|
||||||
|
|
||||||
// When
|
// When
|
||||||
state.Update(ConsoleKey.UpArrow.ToConsoleKeyInfo());
|
state.Update(key.ToConsoleKeyInfo());
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
state.Index.ShouldBe(99);
|
state.Index.ShouldBe(99);
|
||||||
@@ -106,13 +131,15 @@ public sealed class ListPromptStateTests
|
|||||||
state.Index.ShouldBe(0);
|
state.Index.ShouldBe(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public void Should_Wrap_Index_If_Wrap_And_Offset_And_Page_Down()
|
[InlineData(ConsoleKey.UpArrow)]
|
||||||
|
[InlineData(ConsoleKey.K)]
|
||||||
|
public void Should_Wrap_Index_If_Wrap_And_Offset_And_Page_Down(ConsoleKey key)
|
||||||
{
|
{
|
||||||
// Given
|
// Given
|
||||||
var state = CreateListPromptState(10, 100, true, false);
|
var state = CreateListPromptState(10, 100, true, false);
|
||||||
state.Update(ConsoleKey.End.ToConsoleKeyInfo());
|
state.Update(ConsoleKey.End.ToConsoleKeyInfo());
|
||||||
state.Update(ConsoleKey.UpArrow.ToConsoleKeyInfo());
|
state.Update(key.ToConsoleKeyInfo());
|
||||||
|
|
||||||
// When
|
// When
|
||||||
state.Update(ConsoleKey.PageDown.ToConsoleKeyInfo());
|
state.Update(ConsoleKey.PageDown.ToConsoleKeyInfo());
|
||||||
|
|||||||
Reference in New Issue
Block a user