@@ -110,6 +110,9 @@ class HistoryItem
110110
111111 static KeyHandler MakeKeyHandler ( Action < ConsoleKeyInfo ? , object > action , string briefDescription , string longDescription = null )
112112 {
113+ if ( string . IsNullOrWhiteSpace ( longDescription ) )
114+ longDescription = PSReadLineResources . ResourceManager . GetString ( briefDescription + "Description" ) ;
115+
113116 return new KeyHandler
114117 {
115118 Action = action ,
@@ -476,6 +479,8 @@ static PSConsoleReadLine()
476479 { Keys . CtrlEnd , MakeKeyHandler ( ForwardDeleteLine , "ForwardDeleteLine" ) } ,
477480 { Keys . CtrlHome , MakeKeyHandler ( BackwardDeleteLine , "BackwardDeleteLine" ) } ,
478481 { Keys . CtrlRBracket , MakeKeyHandler ( GotoBrace , "GotoBrace" ) } ,
482+ { Keys . CtrlAltQuestion , MakeKeyHandler ( ShowKeyBindings , "ShowKeyBindings" ) } ,
483+ { Keys . AltQuestion , MakeKeyHandler ( WhatIsKey , "WhatIsKey" ) } ,
479484 { Keys . F3 , MakeKeyHandler ( CharacterSearch , "CharacterSearch" ) } ,
480485 { Keys . ShiftF3 , MakeKeyHandler ( CharacterSearchBackward , "CharacterSearchBackward" ) } ,
481486 } ;
@@ -536,6 +541,8 @@ static PSConsoleReadLine()
536541 { Keys . AltY , MakeKeyHandler ( YankPop , "YankPop" ) } ,
537542 { Keys . AltBackspace , MakeKeyHandler ( BackwardKillWord , "BackwardKillWord" ) } ,
538543 { Keys . AltEquals , MakeKeyHandler ( PossibleCompletions , "PossibleCompletions" ) } ,
544+ { Keys . CtrlAltQuestion , MakeKeyHandler ( ShowKeyBindings , "ShowKeyBindings" ) } ,
545+ { Keys . AltQuestion , MakeKeyHandler ( WhatIsKey , "WhatIsKey" ) } ,
539546 { Keys . AltSpace , MakeKeyHandler ( SetMark , "SetMark" ) } , // useless entry here for completeness - brings up system menu on Windows
540547 { Keys . AltPeriod , MakeKeyHandler ( YankLastArg , "YankLastArg" ) } ,
541548 { Keys . AltUnderbar , MakeKeyHandler ( YankLastArg , "YankLastArg" ) } ,
@@ -3066,55 +3073,57 @@ public static void Ding()
30663073 // The unit test framework redirects stdout - so it would see Console.WriteLine calls.
30673074 // Unfortunately, we are testing exact placement of characters on the screen, so redirection
30683075 // doesn't work for us.
3069- static private void WriteImpl ( string s )
3076+ static private void WriteLine ( string s )
30703077 {
3071- Debug . Assert ( s . Length <= Console . BufferWidth ) ;
3072-
30733078 var handle = NativeMethods . GetStdHandle ( ( uint ) StandardHandleId . Output ) ;
30743079
3075- var buffer = new CHAR_INFO [ s . Length ] ;
3076- for ( int i = 0 ; i < s . Length ; i ++ )
3080+ var buffer = new CHAR_INFO [ Console . BufferWidth ] ;
3081+ int i = 0 ;
3082+ int linesWritten = 0 ;
3083+ int startLine = Console . CursorTop ;
3084+ var space = new CHAR_INFO ( ' ' , Console . ForegroundColor , Console . BackgroundColor ) ;
3085+ while ( i < s . Length )
30773086 {
3078- Debug . Assert ( s [ i ] != '\n ' ) ;
3079- buffer [ i ] = new CHAR_INFO ( s [ i ] , Console . ForegroundColor , Console . BackgroundColor ) ;
3080- }
3087+ int j ;
3088+ for ( j = 0 ; j < buffer . Length && i < s . Length ; j ++ , i ++ )
3089+ {
3090+ if ( s [ i ] == '\n ' )
3091+ {
3092+ break ;
3093+ }
3094+ buffer [ j ] = new CHAR_INFO ( s [ i ] , Console . ForegroundColor , Console . BackgroundColor ) ;
3095+ }
30813096
3082- var bufferSize = new COORD
3083- {
3084- X = ( short ) s . Length ,
3085- Y = 1
3086- } ;
3087- var bufferCoord = new COORD { X = 0 , Y = 0 } ;
3088- var writeRegion = new SMALL_RECT
3089- {
3090- Top = ( short ) Console . CursorTop ,
3091- Left = 0 ,
3092- Bottom = ( short ) Console . CursorTop ,
3093- Right = ( short ) s . Length
3094- } ;
3095- NativeMethods . WriteConsoleOutput ( handle , buffer , bufferSize , bufferCoord , ref writeRegion ) ;
3096- }
3097+ if ( i < s . Length && s [ i ] == '\n ' )
3098+ {
3099+ i ++ ;
3100+ }
30973101
3098- static private void WriteLine ( string s )
3099- {
3100- Debug . Assert ( s . Length <= Console . BufferWidth ) ;
3102+ while ( j < buffer . Length )
3103+ {
3104+ buffer [ j ++ ] = space ;
3105+ }
31013106
3102- var spaces = Console . BufferWidth - s . Length ;
3103- if ( spaces > 0 )
3104- {
3105- s = s + new string ( ' ' , spaces ) ;
3107+ var bufferSize = new COORD
3108+ {
3109+ X = ( short ) Console . BufferWidth ,
3110+ Y = 1
3111+ } ;
3112+ var bufferCoord = new COORD { X = 0 , Y = 0 } ;
3113+ var writeRegion = new SMALL_RECT
3114+ {
3115+ Top = ( short ) ( startLine + linesWritten ) ,
3116+ Left = 0 ,
3117+ Bottom = ( short ) ( startLine + linesWritten ) ,
3118+ Right = ( short ) Console . BufferWidth
3119+ } ;
3120+ NativeMethods . WriteConsoleOutput ( handle , buffer , bufferSize , bufferCoord , ref writeRegion ) ;
3121+ linesWritten += 1 ;
31063122 }
3107- WriteImpl ( s ) ;
31083123
3109- _singleton . PlaceCursor ( 0 , Console . CursorTop + 1 ) ;
3124+ _singleton . PlaceCursor ( 0 , Console . CursorTop + linesWritten ) ;
31103125 }
31113126
3112- static private void Write ( string s )
3113- {
3114- WriteImpl ( s ) ;
3115-
3116- _singleton . PlaceCursor ( s . Length , Console . CursorTop ) ;
3117- }
31183127
31193128 private bool PromptYesOrNo ( string s )
31203129 {
@@ -3224,6 +3233,96 @@ private void ClearDemoWindow()
32243233
32253234#endregion Rendering
32263235
3236+ #region Miscellaneous bindable functions
3237+
3238+ /// <summary>
3239+ /// Show all bound keys
3240+ /// </summary>
3241+ public static void ShowKeyBindings ( ConsoleKeyInfo ? key = null , object arg = null )
3242+ {
3243+ var buffer = new StringBuilder ( ) ;
3244+ buffer . AppendFormat ( "{0,-20} {1,-24} {2}\n " , "Key" , "Function" , "Description" ) ;
3245+ buffer . AppendFormat ( "{0,-20} {1,-24} {2}\n " , "---" , "--------" , "-----------" ) ;
3246+ var boundKeys = GetKeyHandlers ( includeBound : true , includeUnbound : false ) ;
3247+ var maxDescriptionLength = Console . WindowWidth - 20 - 24 - 2 ;
3248+ foreach ( var boundKey in boundKeys )
3249+ {
3250+ var description = boundKey . Description ;
3251+ if ( description . Length >= maxDescriptionLength )
3252+ {
3253+ description = description . Substring ( 0 , maxDescriptionLength - 3 ) + "..." ;
3254+ }
3255+ buffer . AppendFormat ( "{0,-20} {1,-24} {2}\n " , boundKey . Key , boundKey . Function , description ) ;
3256+ }
3257+
3258+ // Don't overwrite any of the line - so move to first line after the end of our buffer.
3259+ var coords = _singleton . ConvertOffsetToCoordinates ( _singleton . _buffer . Length ) ;
3260+ _singleton . PlaceCursor ( 0 , coords . Y + 1 ) ;
3261+
3262+ WriteLine ( buffer . ToString ( ) ) ;
3263+ _singleton . _initialY = Console . CursorTop ;
3264+ _singleton . Render ( ) ;
3265+ }
3266+
3267+ /// <summary>
3268+ /// Read a key and tell me what the key is bound to.
3269+ /// </summary>
3270+ public static void WhatIsKey ( ConsoleKeyInfo ? key = null , object arg = null )
3271+ {
3272+ _singleton . _statusLinePrompt = "what-is-key: " ;
3273+ _singleton . Render ( ) ;
3274+ var toLookup = ReadKey ( ) ;
3275+ KeyHandler keyHandler ;
3276+ var buffer = new StringBuilder ( ) ;
3277+ _singleton . _dispatchTable . TryGetValue ( toLookup , out keyHandler ) ;
3278+ buffer . Append ( toLookup . ToGestureString ( ) ) ;
3279+ if ( keyHandler != null )
3280+ {
3281+ if ( keyHandler . BriefDescription == "ChordFirstKey" )
3282+ {
3283+ Dictionary < ConsoleKeyInfo , KeyHandler > secondKeyDispatchTable ;
3284+ if ( _singleton . _chordDispatchTable . TryGetValue ( toLookup , out secondKeyDispatchTable ) )
3285+ {
3286+ toLookup = ReadKey ( ) ;
3287+ secondKeyDispatchTable . TryGetValue ( toLookup , out keyHandler ) ;
3288+ buffer . Append ( "," ) ;
3289+ buffer . Append ( toLookup . ToGestureString ( ) ) ;
3290+ }
3291+ }
3292+ }
3293+ buffer . Append ( ": " ) ;
3294+ if ( keyHandler != null )
3295+ {
3296+ buffer . Append ( keyHandler . BriefDescription ) ;
3297+ if ( ! string . IsNullOrWhiteSpace ( keyHandler . LongDescription ) )
3298+ {
3299+ buffer . Append ( " - " ) ;
3300+ buffer . Append ( keyHandler . LongDescription ) ;
3301+ }
3302+ }
3303+ else if ( toLookup . KeyChar != 0 )
3304+ {
3305+ buffer . Append ( "SelfInsert" ) ;
3306+ buffer . Append ( " - " ) ;
3307+ buffer . Append ( PSReadLineResources . SelfInsertDescription ) ;
3308+ }
3309+ else
3310+ {
3311+ buffer . Append ( PSReadLineResources . KeyIsUnbound ) ;
3312+ }
3313+
3314+ _singleton . _statusLinePrompt = null ;
3315+ _singleton . Render ( ) ;
3316+
3317+ // Don't overwrite any of the line - so move to first line after the end of our buffer.
3318+ var coords = _singleton . ConvertOffsetToCoordinates ( _singleton . _buffer . Length ) ;
3319+ _singleton . PlaceCursor ( 0 , coords . Y + 1 ) ;
3320+
3321+ WriteLine ( buffer . ToString ( ) ) ;
3322+ _singleton . _initialY = Console . CursorTop ;
3323+ _singleton . Render ( ) ;
3324+ }
3325+
32273326 /// <summary>
32283327 /// Turn on demo mode (display events like keys pressed)
32293328 /// </summary>
@@ -3253,6 +3352,8 @@ public static void DisableDemoMode(ConsoleKeyInfo? key = null, object arg = null
32533352 _singleton . ClearDemoWindow ( ) ;
32543353 }
32553354
3355+ #endregion Miscellaneous bindable functions
3356+
32563357 private void SetOptionsInternal ( SetPSReadlineOption options )
32573358 {
32583359 if ( options . ContinuationPrompt != null )
0 commit comments