@@ -182,7 +182,8 @@ void AppendPart(string str)
182
182
sb . Append ( str ) ;
183
183
}
184
184
185
- switch ( key . Key )
185
+ var consoleKey = key . Key ;
186
+ switch ( consoleKey )
186
187
{
187
188
// Keys we definitely bind or might bind.
188
189
case ConsoleKey . PageUp : case ConsoleKey . PageDown :
@@ -221,22 +222,36 @@ void AppendPart(string str)
221
222
if ( isShift ) { AppendPart ( "Shift" ) ; }
222
223
if ( isCtrl ) { AppendPart ( "Ctrl" ) ; }
223
224
if ( isAlt ) { AppendPart ( "Alt" ) ; }
224
- AppendPart ( key . Key . ToString ( ) ) ;
225
+ AppendPart ( consoleKey . ToString ( ) ) ;
225
226
return sb . ToString ( ) ;
226
227
}
227
228
228
229
var c = key . KeyChar ;
229
230
var isDeadKey = false ;
230
- if ( char . IsControl ( c ) )
231
+ if ( char . IsControl ( c ) )
231
232
{
232
233
// We have the virtual key code and Windows has a handy api to map that to the non-control
233
234
// character that use for a friendly UI.
234
235
//
235
236
// If this fails, we rely on some hard coded control characters below.
236
237
if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
237
238
{
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
+ }
240
255
}
241
256
}
242
257
else if ( isAlt && isCtrl )
@@ -281,10 +296,9 @@ void AppendPart(string str)
281
296
// This could be a dead key for a particular keyboard layout in Windows console.
282
297
// The dead key is not an issue when there is tty involved, so on non-Windows, `isDeadKey` is always false.
283
298
//
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.
286
300
// Otherwise, we use `@` in case `key.KeyChar = '\0'`. This is ugly but familiar.
287
- s = isDeadKey ? key . Key . ToString ( ) : "@" ;
301
+ s = isDeadKey ? c . ToString ( ) : "@" ;
288
302
break ;
289
303
290
304
case char _ when ( c >= 1 && c <= 26 ) :
0 commit comments