@@ -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