@@ -17,8 +17,8 @@ namespace Microsoft.PowerShell.EditorServices.Services.PowerShell.Utility
1717 /// </summary>
1818 internal static class CommandHelpers
1919 {
20- private static readonly HashSet < string > s_nounExclusionList = new HashSet < string >
21- {
20+ private static readonly HashSet < string > s_nounExclusionList = new ( )
21+ {
2222 // PowerShellGet v2 nouns
2323 "CredsFromCredentialProvider" ,
2424 "DscResource" ,
@@ -36,8 +36,8 @@ internal static class CommandHelpers
3636 } ;
3737
3838 // This is used when a noun exists in multiple modules (for example, "Command" is used in Microsoft.PowerShell.Core and also PowerShellGet)
39- private static readonly HashSet < string > s_cmdletExclusionList = new HashSet < string >
40- {
39+ private static readonly HashSet < string > s_cmdletExclusionList = new ( )
40+ {
4141 // Commands in PowerShellGet with conflicting nouns
4242 "Find-Command" ,
4343 "Find-Module" ,
@@ -49,17 +49,17 @@ internal static class CommandHelpers
4949 "Update-ModuleManifest" ,
5050 } ;
5151
52- private static readonly ConcurrentDictionary < string , CommandInfo > s_commandInfoCache =
53- new ConcurrentDictionary < string , CommandInfo > ( ) ;
54-
55- private static readonly ConcurrentDictionary < string , string > s_synopsisCache =
56- new ConcurrentDictionary < string , string > ( ) ;
52+ private static readonly ConcurrentDictionary < string , CommandInfo > s_commandInfoCache = new ( ) ;
53+ private static readonly ConcurrentDictionary < string , string > s_synopsisCache = new ( ) ;
54+ private static readonly ConcurrentDictionary < string , List < string > > s_cmdletToAliasCache = new ( System . StringComparer . OrdinalIgnoreCase ) ;
55+ private static readonly ConcurrentDictionary < string , string > s_aliasToCmdletCache = new ( System . StringComparer . OrdinalIgnoreCase ) ;
5756
5857 /// <summary>
5958 /// Gets the CommandInfo instance for a command with a particular name.
6059 /// </summary>
6160 /// <param name="commandName">The name of the command.</param>
62- /// <param name="powerShellContext">The PowerShellContext to use for running Get-Command.</param>
61+ /// <param name="currentRunspace">The current runspace.</param>
62+ /// <param name="executionService">The execution service.</param>
6363 /// <returns>A CommandInfo object with details about the specified command.</returns>
6464 public static async Task < CommandInfo > GetCommandInfoAsync (
6565 string commandName ,
@@ -97,7 +97,11 @@ public static async Task<CommandInfo> GetCommandInfoAsync(
9797 . AddArgument ( commandName )
9898 . AddParameter ( "ErrorAction" , "Ignore" ) ;
9999
100- CommandInfo commandInfo = ( await executionService . ExecutePSCommandAsync < CommandInfo > ( command , CancellationToken . None ) . ConfigureAwait ( false ) ) . FirstOrDefault ( ) ;
100+ IReadOnlyList < CommandInfo > results = await executionService
101+ . ExecutePSCommandAsync < CommandInfo > ( command , CancellationToken . None )
102+ . ConfigureAwait ( false ) ;
103+
104+ CommandInfo commandInfo = results . Count > 0 ? results [ 0 ] : null ;
101105
102106 // Only cache CmdletInfos since they're exposed in binaries they are likely to not change throughout the session.
103107 if ( commandInfo ? . CommandType == CommandTypes . Cmdlet )
@@ -112,8 +116,8 @@ public static async Task<CommandInfo> GetCommandInfoAsync(
112116 /// Gets the command's "Synopsis" documentation section.
113117 /// </summary>
114118 /// <param name="commandInfo">The CommandInfo instance for the command.</param>
115- /// <param name="executionService">The PowerShellContext to use for getting command documentation.</param>
116- /// <returns></returns>
119+ /// <param name="executionService">The exeuction service to use for getting command documentation.</param>
120+ /// <returns>The synopsis. </returns>
117121 public static async Task < string > GetCommandSynopsisAsync (
118122 CommandInfo commandInfo ,
119123 IInternalPowerShellExecutionService executionService )
@@ -146,13 +150,13 @@ public static async Task<string> GetCommandSynopsisAsync(
146150 . AddParameter ( "Online" , false )
147151 . AddParameter ( "ErrorAction" , "Ignore" ) ;
148152
149- IReadOnlyList < PSObject > results = await executionService . ExecutePSCommandAsync < PSObject > ( command , CancellationToken . None ) . ConfigureAwait ( false ) ;
150- PSObject helpObject = results . FirstOrDefault ( ) ;
153+ IReadOnlyList < PSObject > results = await executionService
154+ . ExecutePSCommandAsync < PSObject > ( command , CancellationToken . None )
155+ . ConfigureAwait ( false ) ;
151156
152157 // Extract the synopsis string from the object
153- string synopsisString =
154- ( string ) helpObject ? . Properties [ "synopsis" ] . Value ??
155- string . Empty ;
158+ PSObject helpObject = results . Count > 0 ? results [ 0 ] : null ;
159+ string synopsisString = ( string ) helpObject ? . Properties [ "synopsis" ] . Value ?? string . Empty ;
156160
157161 // Only cache cmdlet infos because since they're exposed in binaries, the can never change throughout the session.
158162 if ( commandInfo . CommandType == CommandTypes . Cmdlet )
@@ -168,5 +172,39 @@ public static async Task<string> GetCommandSynopsisAsync(
168172
169173 return synopsisString ;
170174 }
175+
176+ /// <summary>
177+ /// Gets all aliases found in the runspace
178+ /// </summary>
179+ /// <param name="executionService"></param>
180+ public static async Task < ( Dictionary < string , List < string > > , Dictionary < string , string > ) > GetAliasesAsync ( IInternalPowerShellExecutionService executionService )
181+ {
182+ Validate . IsNotNull ( nameof ( executionService ) , executionService ) ;
183+
184+ IEnumerable < CommandInfo > aliases = await executionService . ExecuteDelegateAsync < IEnumerable < CommandInfo > > (
185+ nameof ( GetAliasesAsync ) ,
186+ Execution . ExecutionOptions . Default ,
187+ ( pwsh , _ ) =>
188+ {
189+ CommandInvocationIntrinsics invokeCommand = pwsh . Runspace . SessionStateProxy . InvokeCommand ;
190+ return invokeCommand . GetCommands ( "*" , CommandTypes . Alias , nameIsPattern : true ) ;
191+ } ,
192+ CancellationToken . None ) . ConfigureAwait ( false ) ;
193+
194+ foreach ( AliasInfo aliasInfo in aliases )
195+ {
196+ // TODO: When we move to netstandard2.1, we can use another overload which generates
197+ // static delegates and thus reduces allocations.
198+ s_cmdletToAliasCache . AddOrUpdate (
199+ aliasInfo . Definition ,
200+ ( _ ) => new List < string > { aliasInfo . Name } ,
201+ ( _ , v ) => { v . Add ( aliasInfo . Name ) ; return v ; } ) ;
202+
203+ s_aliasToCmdletCache . TryAdd ( aliasInfo . Name , aliasInfo . Definition ) ;
204+ }
205+
206+ return ( new Dictionary < string , List < string > > ( s_cmdletToAliasCache ) ,
207+ new Dictionary < string , string > ( s_aliasToCmdletCache ) ) ;
208+ }
171209 }
172210}
0 commit comments