diff --git a/PSReadLine/Cmdlets.cs b/PSReadLine/Cmdlets.cs index 241e8a9c..c3d569b6 100644 --- a/PSReadLine/Cmdlets.cs +++ b/PSReadLine/Cmdlets.cs @@ -805,11 +805,12 @@ public object GetDynamicParameters() } } - [Cmdlet("Get", "PSReadLineKeyHandler", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=528807")] + [Cmdlet("Get", "PSReadLineKeyHandler", DefaultParameterSetName = "FullListing", + HelpUri = "https://go.microsoft.com/fwlink/?LinkId=528807")] [OutputType(typeof(KeyHandler))] public class GetKeyHandlerCommand : PSCmdlet { - [Parameter] + [Parameter(ParameterSetName = "FullListing")] public SwitchParameter Bound { get => _bound.GetValueOrDefault(); @@ -817,7 +818,7 @@ public SwitchParameter Bound } private SwitchParameter? _bound; - [Parameter] + [Parameter(ParameterSetName = "FullListing")] public SwitchParameter Unbound { get => _unbound.GetValueOrDefault(); @@ -825,6 +826,11 @@ public SwitchParameter Unbound } private SwitchParameter? _unbound; + [Parameter(ParameterSetName = "SpecificBindings", Position = 0, Mandatory = true)] + [ValidateNotNullOrEmpty] + [Alias("Key")] + public string[] Chord { get; set; } + [ExcludeFromCodeCoverage] protected override void EndProcessing() { @@ -845,7 +851,18 @@ protected override void EndProcessing() bound = false; unbound = _unbound.Value.IsPresent; } - var groups = PSConsoleReadLine.GetKeyHandlers(bound, unbound).GroupBy(k => k.Group).OrderBy(g => g.Key); + + IEnumerable handlers; + if (ParameterSetName.Equals("FullListing", StringComparison.OrdinalIgnoreCase)) + { + handlers = PSConsoleReadLine.GetKeyHandlers(bound, unbound); + } + else + { + handlers = PSConsoleReadLine.GetKeyHandlers(Chord); + } + var groups = handlers.GroupBy(k => k.Group).OrderBy(g => g.Key); + foreach (var bindings in groups) { WriteObject(bindings.OrderBy(k => k.Function), true); diff --git a/PSReadLine/Options.cs b/PSReadLine/Options.cs index 91fc07e7..7ae53ac7 100644 --- a/PSReadLine/Options.cs +++ b/PSReadLine/Options.cs @@ -399,5 +399,88 @@ public static void RemoveKeyHandler(string[] key) } } } + + /// + /// Return key handlers bound to specified chords. + /// + /// + public static IEnumerable GetKeyHandlers(string[] Chord) + { + var boundFunctions = new HashSet(StringComparer.OrdinalIgnoreCase); + + if (Chord == null) yield break; + + foreach (string Key in Chord) + { + ConsoleKeyInfo[] consoleKeyChord = ConsoleKeyChordConverter.Convert(Key); + PSKeyInfo firstKey = PSKeyInfo.FromConsoleKeyInfo(consoleKeyChord[0]); + + if (_singleton._dispatchTable.TryGetValue(firstKey, out KeyHandler entry)) + { + if (consoleKeyChord.Length == 1) + { + yield return new PowerShell.KeyHandler + { + Key = firstKey.KeyStr, + Function = entry.BriefDescription, + Description = entry.LongDescription, + Group = GetDisplayGrouping(entry.BriefDescription), + }; + } + else + { + PSKeyInfo secondKey = PSKeyInfo.FromConsoleKeyInfo(consoleKeyChord[1]); + if (_singleton._chordDispatchTable.TryGetValue(firstKey, out var secondDispatchTable) && + secondDispatchTable.TryGetValue(secondKey, out entry)) + { + yield return new PowerShell.KeyHandler + { + Key = firstKey.KeyStr + "," + secondKey.KeyStr, + Function = entry.BriefDescription, + Description = entry.LongDescription, + Group = GetDisplayGrouping(entry.BriefDescription), + }; + } + } + } + + // If in Vi mode, also check Vi's command mode list. + if (PSConsoleReadLine.GetOptions().EditMode == EditMode.Vi) + { + if (_viCmdKeyMap.TryGetValue(firstKey, out entry)) + { + if (consoleKeyChord.Length == 1) + { + if (entry.BriefDescription == "Ignore") continue; + yield return new PowerShell.KeyHandler + { + Key = '<' + firstKey.KeyStr + '>', + Function = entry.BriefDescription, + Description = entry.LongDescription, + Group = GetDisplayGrouping(entry.BriefDescription), + }; + } + else + { + PSKeyInfo secondKey = PSKeyInfo.FromConsoleKeyInfo(consoleKeyChord[1]); + if (_viCmdChordTable.TryGetValue(firstKey, out var secondDispatchTable) && + secondDispatchTable.TryGetValue(secondKey, out entry)) + { + if (entry.BriefDescription == "Ignore") continue; + yield return new PowerShell.KeyHandler + { + Key = '<' + firstKey.KeyStr + "," + secondKey.KeyStr + '>', + Function = entry.BriefDescription, + Description = entry.LongDescription, + Group = GetDisplayGrouping(entry.BriefDescription), + }; + } + } + } + } + } + yield break; + + } } } diff --git a/docs/Get-PSReadLineKeyHandler.md b/docs/Get-PSReadLineKeyHandler.md index 712d0882..158db654 100644 --- a/docs/Get-PSReadLineKeyHandler.md +++ b/docs/Get-PSReadLineKeyHandler.md @@ -37,7 +37,7 @@ Include functions that are bound. ```yaml Type: switch -Parameter Sets: (All) +Parameter Sets: Set 1 Aliases: Required: False @@ -53,7 +53,7 @@ Include functions that are unbound. ```yaml Type: switch -Parameter Sets: (All) +Parameter Sets: Set 1 Aliases: Required: False @@ -63,6 +63,22 @@ Accept pipeline input: false Accept wildcard characters: False ``` +### -Chord + +Return only functions bound to specific keys or sequences. + +```yaml +Type: String[] +Parameter Sets: Set 2 +Aliases: Key + +Required: True +Position: 0 +Default value: +Accept pipeline input: false +Accept wildcard characters: False +``` + ## INPUTS ### None diff --git a/test/OptionsTest.VI.cs b/test/OptionsTest.VI.cs index d1413299..38025bc4 100644 --- a/test/OptionsTest.VI.cs +++ b/test/OptionsTest.VI.cs @@ -23,6 +23,20 @@ public void ViGetKeyHandlers() Assert.False(string.IsNullOrWhiteSpace(handler.Function)); Assert.False(string.IsNullOrWhiteSpace(handler.Description)); } + + var handlers = PSConsoleReadLine.GetKeyHandlers(Chord: new string[] { "home" }); + Assert.NotEmpty(handlers); + foreach (var handler in handlers) + { + Assert.Contains("Home", handler.Key); + } + + handlers = PSConsoleReadLine.GetKeyHandlers(Chord: new string[] { "d,0" }); + Assert.NotEmpty(handlers); + foreach (var handler in handlers) + { + Assert.Equal("", handler.Key); + } } } } diff --git a/test/OptionsTest.cs b/test/OptionsTest.cs index c2cdc0ea..3015ae62 100644 --- a/test/OptionsTest.cs +++ b/test/OptionsTest.cs @@ -58,6 +58,8 @@ public void ContinuationPrompt() [SkippableFact] public void GetKeyHandlers() { + System.Collections.Generic.IEnumerable handlers; + foreach (var keymode in new[] {KeyMode.Cmd, KeyMode.Emacs}) { TestSetup(keymode); @@ -75,6 +77,29 @@ public void GetKeyHandlers() Assert.False(string.IsNullOrWhiteSpace(handler.Function)); Assert.False(string.IsNullOrWhiteSpace(handler.Description)); } + + handlers = PSConsoleReadLine.GetKeyHandlers(Chord: new string[] { "home" }); + Assert.NotEmpty(handlers); + foreach (var handler in handlers) + { + Assert.Equal("Home", handler.Key); + } + } + + TestSetup(KeyMode.Emacs); + + handlers = PSConsoleReadLine.GetKeyHandlers(Chord: new string[] { "ctrl+x" }); + Assert.NotEmpty(handlers); + foreach (var handler in handlers) + { + Assert.Equal("Ctrl+x", handler.Key); + } + + handlers = PSConsoleReadLine.GetKeyHandlers(Chord: new string[] { "ctrl+x,ctrl+e" }); + Assert.NotEmpty(handlers); + foreach (var handler in handlers) + { + Assert.Equal("Ctrl+x,Ctrl+e", handler.Key); } }