diff --git a/PSReadLine/Keys.cs b/PSReadLine/Keys.cs index a1ad81cf..b08b14a3 100644 --- a/PSReadLine/Keys.cs +++ b/PSReadLine/Keys.cs @@ -125,7 +125,7 @@ public static extern int ToUnicode( static readonly ThreadLocal toUnicodeBuffer = new ThreadLocal(() => new char[2]); static readonly ThreadLocal toUnicodeStateBuffer = new ThreadLocal(() => 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; @@ -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 keyInfoStringBuilder = new ThreadLocal(() => new StringBuilder()); @@ -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 @@ -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) @@ -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): diff --git a/test/DeadKeyTest.cs b/test/DeadKeyTest.cs new file mode 100644 index 00000000..bf0c85b7 --- /dev/null +++ b/test/DeadKeyTest.cs @@ -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)); + } + } +} diff --git a/test/KeyInfo-fr-FR-windows.json b/test/KeyInfo-fr-FR-windows.json index e6a39bc0..0aad3a2b 100644 --- a/test/KeyInfo-fr-FR-windows.json +++ b/test/KeyInfo-fr-FR-windows.json @@ -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 } ] diff --git a/test/KeyboardLayouts.cs b/test/KeyboardLayouts.cs index bccf3989..29e4629a 100644 --- a/test/KeyboardLayouts.cs +++ b/test/KeyboardLayouts.cs @@ -87,7 +87,7 @@ public string KeyAsPropertyName() } var key = (alt != null) ? Key.Substring(0, Key.Length - 1) + alt - : Key; + : Key; return key.Replace('+', '_'); }