Skip to content

Commit f2f5531

Browse files
authored
Avoid rendering dead keys (2nd attempt) (#945)
1 parent b87cb3a commit f2f5531

File tree

1 file changed

+22
-8
lines changed

1 file changed

+22
-8
lines changed

PSReadLine/Keys.cs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,8 @@ void AppendPart(string str)
182182
sb.Append(str);
183183
}
184184

185-
switch (key.Key)
185+
var consoleKey = key.Key;
186+
switch (consoleKey)
186187
{
187188
// Keys we definitely bind or might bind.
188189
case ConsoleKey.PageUp: case ConsoleKey.PageDown:
@@ -221,22 +222,36 @@ void AppendPart(string str)
221222
if (isShift) { AppendPart("Shift"); }
222223
if (isCtrl) { AppendPart("Ctrl"); }
223224
if (isAlt) { AppendPart("Alt"); }
224-
AppendPart(key.Key.ToString());
225+
AppendPart(consoleKey.ToString());
225226
return sb.ToString();
226227
}
227228

228229
var c = key.KeyChar;
229230
var isDeadKey = false;
230-
if (char.IsControl(c) )
231+
if (char.IsControl(c))
231232
{
232233
// We have the virtual key code and Windows has a handy api to map that to the non-control
233234
// character that use for a friendly UI.
234235
//
235236
// If this fails, we rely on some hard coded control characters below.
236237
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
237238
{
238-
var keySansControl = new ConsoleKeyInfo(key.KeyChar, key.Key, isShift, isAlt, control: false);
239-
TryGetCharFromConsoleKey(keySansControl, ref c, ref isDeadKey);
239+
// A heuristic to check for dead keys --
240+
// We got an 'OemXXX' ConsoleKey, '\0' key char, and no 'Ctrl' modifier. It's very likely generated by a dead key.
241+
// We check for 'Ctrl' modifier because it's easy to generate '\0' KeyChar and 'OemXXX' by combinding 'Ctrl' with
242+
// another special key, such as 'Ctrl+?' and 'Ctrl+;'.
243+
isDeadKey = (c == '\0') && (consoleKey >= ConsoleKey.Oem1 && consoleKey <= ConsoleKey.Oem102) && !isCtrl;
244+
245+
if (!isDeadKey)
246+
{
247+
// A dead key could pass the above heuristic check, such as 'Shift+6' in US-INTL keyboard, which represents the
248+
// diacritic '^' and generates 'D6' ConsoleKey, '\0' key char and 'Shift' modifier.
249+
// For those dead keys we try again to identify them by calling the Win32 API 'ToUnicode'. This API doesn't work
250+
// well with keyboards that are not natively supported by Windows, such as the Neo keyboard layout. Hopefully,
251+
// dead keys of the non-natively-supported keyboard layouts are captured by our heuristic check above.
252+
var keySansControl = new ConsoleKeyInfo(key.KeyChar, consoleKey, isShift, isAlt, control: false);
253+
TryGetCharFromConsoleKey(keySansControl, ref c, ref isDeadKey);
254+
}
240255
}
241256
}
242257
else if (isAlt && isCtrl)
@@ -281,10 +296,9 @@ void AppendPart(string str)
281296
// This could be a dead key for a particular keyboard layout in Windows console.
282297
// The dead key is not an issue when there is tty involved, so on non-Windows, `isDeadKey` is always false.
283298
//
284-
// When we believe it's a dead key, we use the text form of the virtual key so the resulted PSKeyInfo can be
285-
// converted back to ConsoleKeyInfo correctly later on, and be properly ignored during rendering.
299+
// When we believe it's a dead key, we use '\0' so it can be properly ignored during rendering.
286300
// Otherwise, we use `@` in case `key.KeyChar = '\0'`. This is ugly but familiar.
287-
s = isDeadKey ? key.Key.ToString() : "@";
301+
s = isDeadKey ? c.ToString() : "@";
288302
break;
289303

290304
case char _ when (c >= 1 && c <= 26):

0 commit comments

Comments
 (0)