Draft: Add KeySequences add-on prototype#5458
Conversation
ea037e1 to
9f0135c
Compare
Key Sequences Deep DiveSee Also
Use key sequences to build command grammars that need more than a single shortcut, such as leader-key commands, operator-plus-motion commands, repeat counts, and persistent command modes. ConceptsA key sequence has three parts:
Leader mode captures a sequence after a configured leader key. Persistent mode captures sequence keys while command mode is active. Leader ModeTo define leader-key commands, attach using Terminal.Gui.Input;
using Terminal.Gui.KeySequences;
using Terminal.Gui.ViewBase;
using Terminal.Gui.Views;
TextView editor = new ();
IDisposable registration = editor.UseKeySequences (bindings =>
{
bindings.Add ("; m <count> k", context =>
{
for (int i = 0; i < context.Count; i++)
{
context.Target.InvokeCommand (Command.Up);
}
return true;
});
});The pattern
The sequence Operator And Motion CommandsTo model Vim-style operator commands, use literal keys for the operator and motion and a count token for the repeat count. editor.UseKeySequences (bindings =>
{
bindings.Add ("; d <count> d", context =>
{
DeleteLines (context.Count);
return true;
});
});The sequence Persistent Command ModePersistent command mode works like a small Vim command mode. The user presses one key to enter command mode, and subsequent keys are interpreted as sequence commands until the exit key is pressed. using Terminal.Gui.App;
using Terminal.Gui.Input;
using Terminal.Gui.KeySequences;
using Terminal.Gui.Views;
IApplication app = Application.Create ().Init ();
TextView editor = new ();
app.Keyboard.KeyBindings.Remove (Key.Esc);
IDisposable registration = editor.UseKeySequences (
bindings =>
{
bindings.Mode = KeySequenceMode.Persistent;
bindings.EnterModeKey = Key.Esc;
bindings.ExitModeKey = 'i';
bindings.AddMode ("<count> k", context =>
{
MoveUp (context.Count);
return true;
});
bindings.AddMode ("d <count> d", context =>
{
DeleteLines (context.Count);
return true;
});
bindings.AddMode (": q", _ =>
{
app.RequestStop ();
return true;
});
},
KeySequenceInterceptionMode.Preemptive);To use Use Pattern SyntaxPattern strings are split on spaces.
To build a pattern in code, use KeySequencePattern pattern = KeySequencePattern
.Leader (';')
.Then ('m')
.Count ()
.Then ('k');
bindings.Add (pattern, context =>
{
MoveUp (context.Count);
return true;
});To create a persistent-mode pattern in code, use KeySequencePattern pattern = KeySequencePattern
.CommandMode ()
.Then ('d')
.Count ()
.Then ('d');Counts
If the user omits the count, KeySequencePattern pattern = KeySequenceParser.Parse ("; g <count> g");
pattern.AllowZeroCount = true;
bindings.Add (pattern, context =>
{
GoToLine (context.Count);
return true;
});Capturing And RoutingView registrations can run in two interception modes:
IDisposable registration = editor.UseKeySequences (
ConfigureBindings,
KeySequenceInterceptionMode.Preemptive);Application registrations attach to IDisposable registration = app.UseKeySequences (bindings =>
{
bindings.Add ("; q", _ =>
{
app.RequestStop ();
return true;
});
});Some views handle character input before public State And FeedbackSubscribe to bindings.StateChanged += (_, args) =>
{
modeLabel.Text = args.IsCommandMode ? "COMMAND" : string.Empty;
countLabel.Text = args.CountText;
};
Timeouts And Cancellation
bindings.Timeout = TimeSpan.FromSeconds (2);
bindings.CancelKey = Key.Esc;Leader mode resets after a match, rejection, cancellation, or timeout. Persistent mode resets the current sequence after a match, rejection, cancellation, or timeout, but stays in command mode until the exit key is pressed. Disposal
IDisposable registration = editor.UseKeySequences (ConfigureBindings);
// Later:
registration.Dispose ();The registration removes event handlers. The view can then use normal keyboard handling again. |
|
This is really neat. I've lost count of the number of times I've mastered vi/vim and subsequent completely forgotten how to use it. lol. I think a great proof point of this would be a version of ted that enabled it, or a config setting. If it's a separate version it could be named tim. |
Haha me as well! Except I've never mastered it lol.
I'll do that over the next few days if I have time after work. |
Summary
Adds a prototype
Terminal.Gui.KeySequencesadd-on for Vim-style multi-key command sequences.This PR is intended as a demonstration and proof of concept for discussion. The final version could live in a separate repository, similar to the Editor add-on, rather than adding to the Terminal.Gui core.
Closes #5457
What This Demonstrates
; m 4 k; d 2 dEsci:qquit command in the UICatalog demo<count>,<char>, and<key>tokensNotes
This intentionally does not add APIs to the core
Terminal.Guiassembly. The add-on composes with existing keyboard routing through public events.The UICatalog scenario (Key Sequences) is meant to make the behavior easy to inspect, not to define a complete Vim editor mode.
Verification
dotnet test --project Terminal.Gui.KeySequences\Tests\Terminal.Gui.KeySequences.Tests.csproj --filter-class "*KeySequence*"dotnet build Terminal.Gui.KeySequences\Terminal.Gui.KeySequences.csproj --no-restore