Skip to content

Fix the conversion from ConsoleKeyInfo/PSKeyInfo to PSKeyInfo/ConsoleKeyInfo #933

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions PSReadLine/Keys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public static extern int ToUnicode(

static readonly ThreadLocal<char[]> toUnicodeBuffer = new ThreadLocal<char[]>(() => new char[2]);
static readonly ThreadLocal<byte[]> toUnicodeStateBuffer = new ThreadLocal<byte[]>(() => new byte[256]);
internal static void TryGetCharFromConsoleKey(ConsoleKeyInfo key, ref char result)
internal static void TryGetCharFromConsoleKey(ConsoleKeyInfo key, ref char result, ref bool isDeadKey)
{
var modifiers = key.Modifiers;
var virtualKey = key.Key;
Expand All @@ -149,11 +149,20 @@ internal static void TryGetCharFromConsoleKey(ConsoleKeyInfo key, ref char resul
}
int charCount = ToUnicode(virtualKey, scanCode, state, chars, chars.Length, flags);

// TODO: support diacriticals (charCount == 2)
if (charCount == 1)
{
result = chars[0];
}
else if (charCount == -1 || charCount >=2)
{
// Quoted from https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-tounicode#return-value:
// "Return value -1 --
// The specified virtual key is a dead-key character (accent or diacritic).
// Return value >=2 --
// Two or more characters were written to the buffer specified by pwszBuff. The most common cause for this is that a dead-key character
// (accent or diacritic) stored in the keyboard layout could not be combined with the specified virtual key to form a single character."
isDeadKey = true;
}
}

static readonly ThreadLocal<StringBuilder> keyInfoStringBuilder = new ThreadLocal<StringBuilder>(() => new StringBuilder());
Expand Down Expand Up @@ -217,6 +226,7 @@ void AppendPart(string str)
}

var c = key.KeyChar;
var isDeadKey = false;
if (char.IsControl(c) )
{
// We have the virtual key code and Windows has a handy api to map that to the non-control
Expand All @@ -226,7 +236,7 @@ void AppendPart(string str)
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var keySansControl = new ConsoleKeyInfo(key.KeyChar, key.Key, isShift, isAlt, control: false);
TryGetCharFromConsoleKey(keySansControl, ref c);
TryGetCharFromConsoleKey(keySansControl, ref c, ref isDeadKey);
}
}
else if (isAlt && isCtrl)
Expand Down Expand Up @@ -268,8 +278,13 @@ void AppendPart(string str)
break;

case '\0':
// This is ugly but familiar.
s = "@";
// This could be a dead key for a particular keyboard layout in Windows console.
// The dead key is not an issue when there is tty involved, so on non-Windows, `isDeadKey` is always false.
//
// When we believe it's a dead key, we use the text form of the virtual key so the resulted PSKeyInfo can be
// converted back to ConsoleKeyInfo correctly later on, and be properly ignored during rendering.
// Otherwise, we use `@` in case `key.KeyChar = '\0'`. This is ugly but familiar.
s = isDeadKey ? key.Key.ToString() : "@";
break;

case char _ when (c >= 1 && c <= 26):
Expand Down
19 changes: 19 additions & 0 deletions test/DeadKeyTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using Xunit;

namespace Test
{
public partial class ReadLine
{
[SkippableFact]
public void DeadKeyShouldBeIgnored()
{
Skip.If(this.Fixture.Lang != "fr-FR", "The dead key test requires Keyboard layout to be set to 'fr-FR'");
TestSetup(KeyMode.Cmd);

Test("aa", Keys("aa", _.DeadKey_Caret));
Test("aab", Keys("aa", _.DeadKey_Caret, 'b'));
Test("aaâ", Keys("aa", _.DeadKey_Caret_A));
}
}
}
14 changes: 14 additions & 0 deletions test/KeyInfo-fr-FR-windows.json
Original file line number Diff line number Diff line change
Expand Up @@ -1636,5 +1636,19 @@
"ConsoleKey": "Z",
"Modifiers": "0",
"Investigate": false
},
{
"Key": "DeadKey_Caret",
"KeyChar": "\u0000",
"ConsoleKey": "Oem6",
"Modifiers": "0",
"Investigate": false
},
{
"Key": "DeadKey_Caret+A",
"KeyChar": "\u00e2",
"ConsoleKey": "A",
"Modifiers": "0",
"Investigate": false
}
]
2 changes: 1 addition & 1 deletion test/KeyboardLayouts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public string KeyAsPropertyName()
}
var key = (alt != null)
? Key.Substring(0, Key.Length - 1) + alt
: Key;
: Key;
return key.Replace('+', '_');
}

Expand Down