Merge pull request #1566 from PowerShell/andschwa/chars
Fix PSReadLine custom key binding on Linux
This commit is contained in:
commit
0513425ff9
|
@ -6,9 +6,6 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
#if UNIX
|
||||
using System.Reflection;
|
||||
#endif
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.PowerShell.Internal;
|
||||
|
||||
|
@ -56,6 +53,7 @@ namespace Microsoft.PowerShell
|
|||
Stack<string> tokens = null;
|
||||
ConsoleModifiers modifiers = 0;
|
||||
ConsoleKey key = 0;
|
||||
char keyChar = '\u0000';
|
||||
|
||||
bool valid = !String.IsNullOrEmpty(sequence);
|
||||
|
||||
|
@ -81,6 +79,9 @@ namespace Microsoft.PowerShell
|
|||
// key should be first token to be popped
|
||||
if (key == 0)
|
||||
{
|
||||
// the keyChar is this token
|
||||
keyChar = token[0];
|
||||
|
||||
// Enum.TryParse accepts arbitrary integers. We shouldn't,
|
||||
// but single digits need to map to the correct key, e.g.
|
||||
// ConsoleKey.D1
|
||||
|
@ -152,8 +153,6 @@ namespace Microsoft.PowerShell
|
|||
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, PSReadLineResources.InvalidSequence, sequence));
|
||||
}
|
||||
|
||||
char keyChar = GetCharFromConsoleKey(key, modifiers);
|
||||
|
||||
return new ConsoleKeyInfo(keyChar, key,
|
||||
shift: ((modifiers & ConsoleModifiers.Shift) != 0),
|
||||
alt: ((modifiers & ConsoleModifiers.Alt) != 0),
|
||||
|
@ -164,6 +163,27 @@ namespace Microsoft.PowerShell
|
|||
{
|
||||
bool valid = false;
|
||||
|
||||
#if UNIX
|
||||
bool isShift;
|
||||
bool isCtrl;
|
||||
key = GetKeyFromCharValue(literal, out isShift, out isCtrl);
|
||||
|
||||
// Failure to get a key for the char just means that the key is not
|
||||
// special (in the ConsoleKey enum), so we return a default key.
|
||||
// Thus this never fails.
|
||||
valid = true;
|
||||
failReason = null;
|
||||
|
||||
if (isShift)
|
||||
{
|
||||
modifiers |= ConsoleModifiers.Shift;
|
||||
}
|
||||
if (isCtrl)
|
||||
{
|
||||
modifiers |= ConsoleModifiers.Control;
|
||||
}
|
||||
// alt is not possible to get
|
||||
#else
|
||||
// shift state will be in MSB
|
||||
short virtualKey = NativeMethods.VkKeyScan(literal);
|
||||
int hresult = Marshal.GetLastWin32Error();
|
||||
|
@ -206,30 +226,89 @@ namespace Microsoft.PowerShell
|
|||
Exception e = Marshal.GetExceptionForHR(hresult);
|
||||
failReason = e.Message;
|
||||
}
|
||||
#endif
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
#if UNIX
|
||||
// this is borrowed from the CoreFX internal System.IO.StdInReader class
|
||||
// https://github.com/dotnet/corefx/blob/5b2ae6aa485773cd5569f56f446698633c9ad945/src/System.Console/src/System/IO/StdInReader.cs#L222
|
||||
private static ConsoleKey GetKeyFromCharValue(char x, out bool isShift, out bool isCtrl)
|
||||
{
|
||||
isShift = false;
|
||||
isCtrl = false;
|
||||
|
||||
switch (x)
|
||||
{
|
||||
case '\b':
|
||||
return ConsoleKey.Backspace;
|
||||
|
||||
case '\t':
|
||||
return ConsoleKey.Tab;
|
||||
|
||||
case '\n':
|
||||
return ConsoleKey.Enter;
|
||||
|
||||
case (char)(0x1B):
|
||||
return ConsoleKey.Escape;
|
||||
|
||||
case '*':
|
||||
return ConsoleKey.Multiply;
|
||||
|
||||
case '+':
|
||||
return ConsoleKey.Add;
|
||||
|
||||
case '-':
|
||||
return ConsoleKey.Subtract;
|
||||
|
||||
case '/':
|
||||
return ConsoleKey.Divide;
|
||||
|
||||
case (char)(0x7F):
|
||||
return ConsoleKey.Delete;
|
||||
|
||||
case ' ':
|
||||
return ConsoleKey.Spacebar;
|
||||
|
||||
default:
|
||||
// 1. Ctrl A to Ctrl Z.
|
||||
if (x >= 1 && x <= 26)
|
||||
{
|
||||
isCtrl = true;
|
||||
return ConsoleKey.A + x - 1;
|
||||
}
|
||||
|
||||
// 2. Numbers from 0 to 9.
|
||||
if (x >= '0' && x <= '9')
|
||||
{
|
||||
return ConsoleKey.D0 + x - '0';
|
||||
}
|
||||
|
||||
//3. A to Z
|
||||
if (x >= 'A' && x <= 'Z')
|
||||
{
|
||||
isShift = true;
|
||||
return ConsoleKey.A + (x - 'A');
|
||||
}
|
||||
|
||||
// 4. a to z.
|
||||
if (x >= 'a' && x <= 'z')
|
||||
{
|
||||
return ConsoleKey.A + (x - 'a');
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return default(ConsoleKey);
|
||||
}
|
||||
#else
|
||||
internal static char GetCharFromConsoleKey(ConsoleKey key, ConsoleModifiers modifiers)
|
||||
{
|
||||
// default for unprintables and unhandled
|
||||
char keyChar = '\u0000';
|
||||
#if UNIX
|
||||
Type keyType = typeof (Keys);
|
||||
FieldInfo[] keyFields = keyType.GetFields();
|
||||
|
||||
foreach (FieldInfo field in keyFields)
|
||||
{
|
||||
if (field.FieldType == typeof(ConsoleKeyInfo))
|
||||
{
|
||||
ConsoleKeyInfo info = (ConsoleKeyInfo)field.GetValue(null);
|
||||
if (info.Key == key && info.Modifiers == modifiers)
|
||||
{
|
||||
return info.KeyChar;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
// emulate GetKeyboardState bitmap - set high order bit for relevant modifier virtual keys
|
||||
var state = new byte[256];
|
||||
state[NativeMethods.VK_SHIFT] = (byte)(((modifiers & ConsoleModifiers.Shift) != 0) ? 0x80 : 0);
|
||||
|
@ -252,8 +331,8 @@ namespace Microsoft.PowerShell
|
|||
{
|
||||
keyChar = chars[0];
|
||||
}
|
||||
#endif
|
||||
return keyChar;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -387,25 +387,12 @@ namespace Microsoft.PowerShell.Internal
|
|||
}
|
||||
|
||||
#if UNIX
|
||||
if (sb.Length > 0)
|
||||
sb.Append("+");
|
||||
// TODO: find better way to map these characters to something more friendly
|
||||
if ((key.Key >= ConsoleKey.D0 && key.Key <= ConsoleKey.D9)
|
||||
|| (key.Key >= ConsoleKey.Oem1 && key.Key <= ConsoleKey.Oem8))
|
||||
{
|
||||
sb.Append(key.KeyChar);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (key.Modifiers.HasFlag(ConsoleModifiers.Shift))
|
||||
{
|
||||
sb.Append("Shift+");
|
||||
}
|
||||
sb.Append(key.Key);
|
||||
}
|
||||
char c = key.KeyChar;
|
||||
#else
|
||||
// Windows cannot use KeyChar as some chords (like Ctrl+[) show up as control characters.
|
||||
char c = ConsoleKeyChordConverter.GetCharFromConsoleKey(key.Key,
|
||||
(mods & ConsoleModifiers.Shift) != 0 ? ConsoleModifiers.Shift : 0);
|
||||
#endif
|
||||
if (char.IsControl(c) || char.IsWhiteSpace(c))
|
||||
{
|
||||
if (key.Modifiers.HasFlag(ConsoleModifiers.Shift))
|
||||
|
@ -424,7 +411,6 @@ namespace Microsoft.PowerShell.Internal
|
|||
sb.Append("+");
|
||||
sb.Append(c);
|
||||
}
|
||||
#endif
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,9 +93,13 @@ namespace Microsoft.PowerShell
|
|||
{
|
||||
// Because a comparison of two ConsoleKeyInfo objects is a comparison of the
|
||||
// combination of the ConsoleKey and Modifiers, we must combine their hashes.
|
||||
// This is based on Tuple.GetHashCode
|
||||
int h1 = obj.Key.GetHashCode();
|
||||
// Note that if the ConsoleKey is default, we must fall back to the KeyChar,
|
||||
// otherwise every non-special key will compare as the same.
|
||||
int h1 = obj.Key == default(ConsoleKey)
|
||||
? obj.KeyChar.GetHashCode()
|
||||
: obj.Key.GetHashCode();
|
||||
int h2 = obj.Modifiers.GetHashCode();
|
||||
// This is based on Tuple.GetHashCode
|
||||
return unchecked(((h1 << 5) + h1) ^ h2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ Describe "PSReadLine" -tags "CI" {
|
|||
|
||||
It "Should use Windows Bindings on Windows" -skip:(-not $IsWindows) {
|
||||
(Get-PSReadLineOption).EditMode | Should Be Windows
|
||||
(Get-PSReadlineKeyHandler | where { $_.Key -eq "Ctrl+A" }).Function | Should Be SelectAll
|
||||
(Get-PSReadlineKeyHandler | where { $_.Key -eq "Ctrl+a" }).Function | Should Be SelectAll
|
||||
}
|
||||
|
||||
It "Should set the edit mode" {
|
||||
|
@ -31,6 +31,17 @@ Describe "PSReadLine" -tags "CI" {
|
|||
(Get-PSReadlineKeyHandler | where { $_.Key -eq "Ctrl+A" }).Function | Should Be BeginningOfLine
|
||||
}
|
||||
|
||||
It "Should allow custom bindings for plain keys" {
|
||||
Set-PSReadlineKeyHandler -Key '"' -Function SelfInsert
|
||||
(Get-PSReadLineKeyHandler | where { $_.Key -eq '"' }).Function | Should Be SelfInsert
|
||||
}
|
||||
|
||||
It "Should report Capitalized bindings correctly" {
|
||||
Set-PSReadlineOption -EditMode Emacs
|
||||
(Get-PSReadLineKeyHandler | where { $_.Key -ceq "Alt+b" }).Function | Should Be BackwardWord
|
||||
(Get-PSReadLineKeyHandler | where { $_.Key -ceq "Alt+B" }).Function | Should Be SelectBackwardWord
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
Remove-Module PSReadLine
|
||||
|
||||
|
|
Loading…
Reference in a new issue