a93233456f
- keep parsed Kitty release events available to runtime listeners - filter key release before focused component dispatch unless component opts in - cover default filtering, opt-in dispatch, and keybinding non-match behavior
158 lines
5.7 KiB
C#
158 lines
5.7 KiB
C#
using TinyTUI.Input;
|
|
|
|
namespace TinyTUI.Tests.Input;
|
|
|
|
public sealed class InputInfrastructureTests
|
|
{
|
|
[Fact]
|
|
public void StdinBufferSplitsBatchedEscapeSequencesAndPaste()
|
|
{
|
|
var buffer = new StdinBuffer();
|
|
|
|
Assert.Empty(buffer.Process("\e["));
|
|
Assert.Equal([new StdinBufferEvent(StdinBufferEventKind.Data, "\e[A")], buffer.Process("A"));
|
|
|
|
Assert.Equal(
|
|
[
|
|
new StdinBufferEvent(StdinBufferEventKind.Data, "\e[97u"),
|
|
new StdinBufferEvent(StdinBufferEventKind.Data, "\e[97;1:3u"),
|
|
],
|
|
buffer.Process("\e[97u\e[97;1:3u"));
|
|
|
|
Assert.Empty(buffer.Process("\e[200~hello "));
|
|
Assert.Equal([new StdinBufferEvent(StdinBufferEventKind.Paste, "hello world")], buffer.Process("world\e[201~"));
|
|
}
|
|
|
|
[Fact]
|
|
public void StdinBufferFlushesIncompleteSequences()
|
|
{
|
|
var buffer = new StdinBuffer();
|
|
Assert.Empty(buffer.Process("\e[<35"));
|
|
Assert.Equal([new StdinBufferEvent(StdinBufferEventKind.Data, "\e[<35")], buffer.Flush());
|
|
}
|
|
|
|
[Fact]
|
|
public void DefaultInputParserParsesCommonKeysAndPaste()
|
|
{
|
|
var parser = new DefaultInputParser();
|
|
|
|
Assert.Equal([new TuiInputEvent(TuiInputEventKind.Key, KeyNames.Up)], parser.Parse("\e[A"));
|
|
Assert.Equal([new TuiInputEvent(TuiInputEventKind.Key, KeyNames.Shift(KeyNames.Up))], parser.Parse("\e[1;2A"));
|
|
Assert.Equal([new TuiInputEvent(TuiInputEventKind.Key, KeyNames.Ctrl("c"))], parser.Parse("\x03"));
|
|
Assert.Equal([new TuiInputEvent(TuiInputEventKind.Text, "abc")], parser.Parse("abc"));
|
|
Assert.Equal([new TuiInputEvent(TuiInputEventKind.Paste, "pasted")], parser.Parse("\e[200~pasted\e[201~"));
|
|
}
|
|
|
|
[Fact]
|
|
public void KeybindingRegistryReportsUserConflictsAndNormalizesAliases()
|
|
{
|
|
var registry = KeybindingRegistry.CreateDefault();
|
|
registry.SetUserBindings(new Dictionary<string, IEnumerable<KeyId>>
|
|
{
|
|
[TuiKeybindings.InputSubmit] = [KeyNames.PageUp],
|
|
[TuiKeybindings.InputCancel] = ["pageUp"],
|
|
});
|
|
|
|
var conflict = Assert.Single(registry.Conflicts);
|
|
Assert.Equal(KeyNames.PageUp, conflict.Key.Value);
|
|
Assert.Contains(TuiKeybindings.InputSubmit, conflict.Actions);
|
|
Assert.Contains(TuiKeybindings.InputCancel, conflict.Actions);
|
|
}
|
|
|
|
[Fact]
|
|
public void DefaultInputParserParsesShiftTabAndAltLetters()
|
|
{
|
|
var parser = new DefaultInputParser();
|
|
|
|
Assert.Equal(
|
|
[new TuiInputEvent(TuiInputEventKind.Key, KeyNames.Shift(KeyNames.Tab))],
|
|
parser.Parse("\e[Z"));
|
|
Assert.Equal(
|
|
[new TuiInputEvent(TuiInputEventKind.Key, KeyNames.Alt("a"))],
|
|
parser.Parse("\ea"));
|
|
}
|
|
|
|
[Fact]
|
|
public void DefaultInputParserParsesKittyCsiUKeys()
|
|
{
|
|
var parser = new DefaultInputParser();
|
|
|
|
Assert.Equal(
|
|
[new TuiInputEvent(TuiInputEventKind.Key, "a", RawSequence: "\e[97u")],
|
|
parser.Parse("\e[97u"));
|
|
Assert.Equal(
|
|
[new TuiInputEvent(TuiInputEventKind.Key, KeyNames.Ctrl("a"), RawSequence: "\e[97;5u")],
|
|
parser.Parse("\e[97;5u"));
|
|
Assert.Equal(
|
|
[new TuiInputEvent(TuiInputEventKind.Key, KeyNames.Shift("e"), RawSequence: "\e[69;2u")],
|
|
parser.Parse("\e[69;2u"));
|
|
|
|
var repeat = Assert.Single(parser.Parse("\e[97;1:2u"));
|
|
Assert.Equal(TuiInputEventKind.Key, repeat.Kind);
|
|
Assert.Equal("a", repeat.Value);
|
|
Assert.Equal(TuiKeyEventType.Repeat, repeat.EventType);
|
|
Assert.Equal("\e[97;1:2u", repeat.RawSequence);
|
|
}
|
|
|
|
[Fact]
|
|
public void DefaultInputParserCarriesKittyAlternateKeyMetadata()
|
|
{
|
|
var parser = new DefaultInputParser();
|
|
|
|
var input = Assert.Single(parser.Parse("\e[1089::99;5u"));
|
|
|
|
Assert.Equal(TuiInputEventKind.Key, input.Kind);
|
|
Assert.Equal(KeyNames.Ctrl("c"), input.Value);
|
|
Assert.Null(input.AlternateKey);
|
|
Assert.Equal("\e[1089::99;5u", input.RawSequence);
|
|
|
|
var shifted = Assert.Single(parser.Parse("\e[99:67:99;2u"));
|
|
Assert.Equal(KeyNames.Shift("c"), shifted.Value);
|
|
Assert.Equal("C", shifted.AlternateKey);
|
|
}
|
|
|
|
[Fact]
|
|
public void DefaultInputParserParsesModifyOtherKeysCombinations()
|
|
{
|
|
var parser = new DefaultInputParser();
|
|
|
|
Assert.Equal(
|
|
[new TuiInputEvent(TuiInputEventKind.Key, KeyNames.Ctrl("c"), RawSequence: "\e[27;5;99~")],
|
|
parser.Parse("\e[27;5;99~"));
|
|
Assert.Equal(
|
|
[new TuiInputEvent(TuiInputEventKind.Key, KeyNames.Shift(KeyNames.Tab), RawSequence: "\e[27;2;9~")],
|
|
parser.Parse("\e[27;2;9~"));
|
|
Assert.Equal(
|
|
[new TuiInputEvent(TuiInputEventKind.Key, KeyNames.Alt(KeyNames.Enter), RawSequence: "\e[27;3;13~")],
|
|
parser.Parse("\e[27;3;13~"));
|
|
}
|
|
|
|
[Fact]
|
|
public void DefaultInputParserKeepsKittyReleaseEvents()
|
|
{
|
|
var parser = new DefaultInputParser();
|
|
|
|
var release = Assert.Single(parser.Parse("\e[97;1:3u"));
|
|
|
|
Assert.Equal(TuiInputEventKind.Key, release.Kind);
|
|
Assert.Equal("a", release.Value);
|
|
Assert.Equal(TuiKeyEventType.Release, release.EventType);
|
|
Assert.Equal("\e[97;1:3u", release.RawSequence);
|
|
}
|
|
|
|
[Fact]
|
|
public void KeybindingRegistryDoesNotMatchReleaseEvents()
|
|
{
|
|
var registry = KeybindingRegistry.CreateDefault();
|
|
registry.SetUserBinding(TuiKeybindings.InputSubmit, KeyNames.Enter);
|
|
|
|
var release = new TuiInputEvent(
|
|
TuiInputEventKind.Key,
|
|
KeyNames.Enter,
|
|
TuiKeyEventType.Release,
|
|
RawSequence: "\e[13;1:3u");
|
|
|
|
Assert.False(registry.Matches(release, TuiKeybindings.InputSubmit));
|
|
}
|
|
}
|