From 5d7447af0918707a1980e70b9b4fcd5982ba7d75 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 5 Jan 2022 17:43:51 -0800 Subject: [PATCH 1/6] Resurrect alias support for `FindReferences` In ancient times, commit 633e36b1ad837841e8ec180b5a66bebca0b8d8ba implemented support for aliases when finding references. This was removed during the OmniSharp rewrite because it needed the pipeline thread, which was not yet available. With that work complete, this can now be resurrected. --- .../Services/Symbols/SymbolsService.cs | 42 +++++++++++++++++-- ...indsReferencesOnBuiltInCommandWithAlias.cs | 12 ------ .../Language/SymbolsServiceTests.cs | 10 +---- 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs b/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs index e110b5ef2..7a237c41f 100644 --- a/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs +++ b/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs @@ -39,7 +39,8 @@ internal class SymbolsService private readonly ConcurrentDictionary _codeLensProviders; private readonly ConcurrentDictionary _documentSymbolProviders; - + private readonly Dictionary> _cmdletToAliasDictionary; + private readonly Dictionary _aliasToCmdletDictionary; #endregion #region Constructors @@ -84,6 +85,10 @@ public SymbolsService( { _documentSymbolProviders.TryAdd(documentSymbolProvider.ProviderId, documentSymbolProvider); } + + _cmdletToAliasDictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); + _aliasToCmdletDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + GetAliases(); } #endregion @@ -186,8 +191,6 @@ public List FindReferencesOfSymbol( return null; } - // NOTE: we use to make sure aliases were loaded but took it out because we needed the pipeline thread. - // We want to look for references first in referenced files, hence we use ordered dictionary // TODO: File system case-sensitivity is based on filesystem not OS, but OS is a much cheaper heuristic var fileMap = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) @@ -221,7 +224,8 @@ public List FindReferencesOfSymbol( IEnumerable references = AstOperations.FindReferencesOfSymbol( file.ScriptAst, foundSymbol, - needsAliases: false); + _cmdletToAliasDictionary, + _aliasToCmdletDictionary); foreach (SymbolReference reference in references) { @@ -490,6 +494,36 @@ await CommandHelpers.GetCommandInfoAsync( return foundDefinition; } + /// + /// Gets all aliases found in the runspace + /// + private async void GetAliases() + { + IEnumerable aliases = await _executionService.ExecuteDelegateAsync>( + nameof(GetAliases), + PowerShell.Execution.ExecutionOptions.Default, + (pwsh, _) => + { + CommandInvocationIntrinsics invokeCommand = pwsh.Runspace.SessionStateProxy.InvokeCommand; + return invokeCommand.GetCommands("*", CommandTypes.Alias, nameIsPattern: true); + }, + System.Threading.CancellationToken.None).ConfigureAwait(false); + + foreach (AliasInfo aliasInfo in aliases) + { + if (!_cmdletToAliasDictionary.ContainsKey(aliasInfo.Definition)) + { + _cmdletToAliasDictionary.Add(aliasInfo.Definition, new List() { aliasInfo.Name }); + } + else + { + _cmdletToAliasDictionary[aliasInfo.Definition].Add(aliasInfo.Name); + } + + _aliasToCmdletDictionary.Add(aliasInfo.Name, aliasInfo.Definition); + } + } + /// /// Gets a path from a dot-source symbol. /// diff --git a/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesOnBuiltInCommandWithAlias.cs b/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesOnBuiltInCommandWithAlias.cs index 1bc7ec8e6..2f39f29c6 100644 --- a/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesOnBuiltInCommandWithAlias.cs +++ b/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesOnBuiltInCommandWithAlias.cs @@ -17,16 +17,4 @@ public static class FindsReferencesOnBuiltInCommandWithAliasData endColumnNumber: 0, endOffset: 0); } - public static class FindsReferencesOnBuiltInAliasData - { - public static readonly ScriptRegion SourceDetails = new( - file: TestUtilities.NormalizePath("References/SimpleFile.ps1"), - text: string.Empty, - startLineNumber: 15, - startColumnNumber: 2, - startOffset: 0, - endLineNumber: 0, - endColumnNumber: 0, - endOffset: 0); - } } diff --git a/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs b/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs index d0d58aa17..3192fa8e3 100644 --- a/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs @@ -203,20 +203,12 @@ public void FindsOccurrencesOnParameter() Assert.Equal(3, occurrencesResult[occurrencesResult.Count - 1].ScriptRegion.StartLineNumber); } - [Fact(Skip = "TODO Fix this test. A possible bug in PSES product code.")] + [Fact] public void FindsReferencesOnCommandWithAlias() { List referencesResult = GetReferences(FindsReferencesOnBuiltInCommandWithAliasData.SourceDetails); Assert.Equal(4, referencesResult.Count); Assert.Equal("gci", referencesResult[1].SymbolName); - Assert.Equal("Get-ChildItem", referencesResult[referencesResult.Count - 1].SymbolName); - } - - [Fact(Skip = "TODO Fix this test. A possible bug in PSES product code.")] - public void FindsReferencesOnAlias() - { - List referencesResult = GetReferences(FindsReferencesOnBuiltInCommandWithAliasData.SourceDetails); - Assert.Equal(4, referencesResult.Count); Assert.Equal("dir", referencesResult[2].SymbolName); Assert.Equal("Get-ChildItem", referencesResult[referencesResult.Count - 1].SymbolName); } From 52a1fe363f249a7a3b935486db5415c9510f7020 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 6 Jan 2022 10:05:03 -0800 Subject: [PATCH 2/6] Clean up `CommandHelpers.cs` --- .../PowerShell/Utility/CommandHelpers.cs | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/PowerShellEditorServices/Services/PowerShell/Utility/CommandHelpers.cs b/src/PowerShellEditorServices/Services/PowerShell/Utility/CommandHelpers.cs index 15f6845ae..4a9ff4c47 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Utility/CommandHelpers.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Utility/CommandHelpers.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Collections.Concurrent; @@ -17,8 +17,8 @@ namespace Microsoft.PowerShell.EditorServices.Services.PowerShell.Utility /// internal static class CommandHelpers { - private static readonly HashSet s_nounExclusionList = new HashSet - { + private static readonly HashSet s_nounExclusionList = new() + { // PowerShellGet v2 nouns "CredsFromCredentialProvider", "DscResource", @@ -36,8 +36,8 @@ internal static class CommandHelpers }; // This is used when a noun exists in multiple modules (for example, "Command" is used in Microsoft.PowerShell.Core and also PowerShellGet) - private static readonly HashSet s_cmdletExclusionList = new HashSet - { + private static readonly HashSet s_cmdletExclusionList = new() + { // Commands in PowerShellGet with conflicting nouns "Find-Command", "Find-Module", @@ -49,17 +49,16 @@ internal static class CommandHelpers "Update-ModuleManifest", }; - private static readonly ConcurrentDictionary s_commandInfoCache = - new ConcurrentDictionary(); + private static readonly ConcurrentDictionary s_commandInfoCache = new(); - private static readonly ConcurrentDictionary s_synopsisCache = - new ConcurrentDictionary(); + private static readonly ConcurrentDictionary s_synopsisCache = new(); /// /// Gets the CommandInfo instance for a command with a particular name. /// /// The name of the command. - /// The PowerShellContext to use for running Get-Command. + /// The current runspace. + /// The execution service. /// A CommandInfo object with details about the specified command. public static async Task GetCommandInfoAsync( string commandName, @@ -97,7 +96,11 @@ public static async Task GetCommandInfoAsync( .AddArgument(commandName) .AddParameter("ErrorAction", "Ignore"); - CommandInfo commandInfo = (await executionService.ExecutePSCommandAsync(command, CancellationToken.None).ConfigureAwait(false)).FirstOrDefault(); + IReadOnlyList results = await executionService + .ExecutePSCommandAsync(command, CancellationToken.None) + .ConfigureAwait(false); + + CommandInfo commandInfo = results.Count > 0 ? results[0] : null; // Only cache CmdletInfos since they're exposed in binaries they are likely to not change throughout the session. if (commandInfo?.CommandType == CommandTypes.Cmdlet) @@ -112,8 +115,8 @@ public static async Task GetCommandInfoAsync( /// Gets the command's "Synopsis" documentation section. /// /// The CommandInfo instance for the command. - /// The PowerShellContext to use for getting command documentation. - /// + /// The exeuction service to use for getting command documentation. + /// The synopsis. public static async Task GetCommandSynopsisAsync( CommandInfo commandInfo, IInternalPowerShellExecutionService executionService) @@ -146,13 +149,13 @@ public static async Task GetCommandSynopsisAsync( .AddParameter("Online", false) .AddParameter("ErrorAction", "Ignore"); - IReadOnlyList results = await executionService.ExecutePSCommandAsync(command, CancellationToken.None).ConfigureAwait(false); - PSObject helpObject = results.FirstOrDefault(); + IReadOnlyList results = await executionService + .ExecutePSCommandAsync(command, CancellationToken.None) + .ConfigureAwait(false); // Extract the synopsis string from the object - string synopsisString = - (string)helpObject?.Properties["synopsis"].Value ?? - string.Empty; + PSObject helpObject = results.Count > 0 ? results[0] : null; + string synopsisString = (string)helpObject?.Properties["synopsis"].Value ?? string.Empty; // Only cache cmdlet infos because since they're exposed in binaries, the can never change throughout the session. if (commandInfo.CommandType == CommandTypes.Cmdlet) From a46c8c9f469df7f72152a6c9194fb4efeb50e2aa Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 6 Jan 2022 13:11:12 -0800 Subject: [PATCH 3/6] Move `GetAliases` into `CommandHelpers` --- .../PowerShell/Utility/CommandHelpers.cs | 44 ++++++++++++++++++- .../Services/Symbols/SymbolsService.cs | 42 ++---------------- 2 files changed, 46 insertions(+), 40 deletions(-) diff --git a/src/PowerShellEditorServices/Services/PowerShell/Utility/CommandHelpers.cs b/src/PowerShellEditorServices/Services/PowerShell/Utility/CommandHelpers.cs index 4a9ff4c47..e1dc242c4 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Utility/CommandHelpers.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Utility/CommandHelpers.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Collections.Concurrent; @@ -50,8 +50,9 @@ internal static class CommandHelpers }; private static readonly ConcurrentDictionary s_commandInfoCache = new(); - private static readonly ConcurrentDictionary s_synopsisCache = new(); + private static readonly ConcurrentDictionary> s_cmdletToAliasCache = new(System.StringComparer.OrdinalIgnoreCase); + private static readonly ConcurrentDictionary s_aliasToCmdletCache = new(System.StringComparer.OrdinalIgnoreCase); /// /// Gets the CommandInfo instance for a command with a particular name. @@ -171,5 +172,44 @@ public static async Task GetCommandSynopsisAsync( return synopsisString; } + + /// + /// Gets all aliases found in the runspace + /// + /// + public static async Task<(ConcurrentDictionary>, ConcurrentDictionary)> GetAliasesAsync(IInternalPowerShellExecutionService executionService) + { + Validate.IsNotNull(nameof(executionService), executionService); + + // TODO: Should we return the caches if they're not empty, or always update? + // if (!s_cmdletToAliasCache.IsEmpty || !s_aliasToCmdletCache.IsEmpty) + // { + // return (s_cmdletToAliasCache, s_aliasToCmdletCache); + // } + + IEnumerable aliases = await executionService.ExecuteDelegateAsync>( + nameof(GetAliasesAsync), + Execution.ExecutionOptions.Default, + (pwsh, _) => + { + CommandInvocationIntrinsics invokeCommand = pwsh.Runspace.SessionStateProxy.InvokeCommand; + return invokeCommand.GetCommands("*", CommandTypes.Alias, nameIsPattern: true); + }, + CancellationToken.None).ConfigureAwait(false); + + foreach (AliasInfo aliasInfo in aliases) + { + // TODO: When we move to netstandard2.1, we can use another overload which generates + // static delegates and thus reduces allocations. + s_cmdletToAliasCache.AddOrUpdate( + aliasInfo.Definition, + (_) => new List { aliasInfo.Name }, + (_, v) => { v.Add(aliasInfo.Name); return v; }); + + s_aliasToCmdletCache.TryAdd(aliasInfo.Name, aliasInfo.Definition); + } + + return (s_cmdletToAliasCache, s_aliasToCmdletCache); + } } } diff --git a/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs b/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs index 7a237c41f..6c970491b 100644 --- a/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs +++ b/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs @@ -39,8 +39,6 @@ internal class SymbolsService private readonly ConcurrentDictionary _codeLensProviders; private readonly ConcurrentDictionary _documentSymbolProviders; - private readonly Dictionary> _cmdletToAliasDictionary; - private readonly Dictionary _aliasToCmdletDictionary; #endregion #region Constructors @@ -85,10 +83,6 @@ public SymbolsService( { _documentSymbolProviders.TryAdd(documentSymbolProvider.ProviderId, documentSymbolProvider); } - - _cmdletToAliasDictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); - _aliasToCmdletDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); - GetAliases(); } #endregion @@ -191,6 +185,8 @@ public List FindReferencesOfSymbol( return null; } + (ConcurrentDictionary> cmdletToAliases, ConcurrentDictionary aliasToCmdlets) = await CommandHelpers.GetAliasesAsync(_executionService).ConfigureAwait(false); + // We want to look for references first in referenced files, hence we use ordered dictionary // TODO: File system case-sensitivity is based on filesystem not OS, but OS is a much cheaper heuristic var fileMap = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) @@ -224,8 +220,8 @@ public List FindReferencesOfSymbol( IEnumerable references = AstOperations.FindReferencesOfSymbol( file.ScriptAst, foundSymbol, - _cmdletToAliasDictionary, - _aliasToCmdletDictionary); + cmdletToAliases, + aliasToCmdlets); foreach (SymbolReference reference in references) { @@ -494,36 +490,6 @@ await CommandHelpers.GetCommandInfoAsync( return foundDefinition; } - /// - /// Gets all aliases found in the runspace - /// - private async void GetAliases() - { - IEnumerable aliases = await _executionService.ExecuteDelegateAsync>( - nameof(GetAliases), - PowerShell.Execution.ExecutionOptions.Default, - (pwsh, _) => - { - CommandInvocationIntrinsics invokeCommand = pwsh.Runspace.SessionStateProxy.InvokeCommand; - return invokeCommand.GetCommands("*", CommandTypes.Alias, nameIsPattern: true); - }, - System.Threading.CancellationToken.None).ConfigureAwait(false); - - foreach (AliasInfo aliasInfo in aliases) - { - if (!_cmdletToAliasDictionary.ContainsKey(aliasInfo.Definition)) - { - _cmdletToAliasDictionary.Add(aliasInfo.Definition, new List() { aliasInfo.Name }); - } - else - { - _cmdletToAliasDictionary[aliasInfo.Definition].Add(aliasInfo.Name); - } - - _aliasToCmdletDictionary.Add(aliasInfo.Name, aliasInfo.Definition); - } - } - /// /// Gets a path from a dot-source symbol. /// From 6d90c44ffa07ac04dc9412a4bd4ad27deed95f2a Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 6 Jan 2022 13:12:24 -0800 Subject: [PATCH 4/6] Clean up and fix `FindReferencesVisitor` This consolidated the constructors so we don't have some unused (and confusing) boolean differentiating them, and instead just provide one constructor with default values. Also clean up `AstOperationsTests`. --- .../Services/Symbols/SymbolsService.cs | 5 +- .../Services/Symbols/Vistors/AstOperations.cs | 38 ++--- .../Symbols/Vistors/FindReferencesVisitor.cs | 140 ++++++++---------- .../Services/Symbols/AstOperationsTests.cs | 16 +- 4 files changed, 80 insertions(+), 119 deletions(-) diff --git a/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs b/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs index 6c970491b..a26bc5812 100644 --- a/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs +++ b/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs @@ -264,10 +264,7 @@ public static IReadOnlyList FindOccurrencesInFile( return null; } - return AstOperations.FindReferencesOfSymbol( - file.ScriptAst, - foundSymbol, - needsAliases: false).ToArray(); + return AstOperations.FindReferencesOfSymbol(file.ScriptAst, foundSymbol).ToArray(); } /// diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs index f312352f1..b8a5da1e1 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -153,42 +154,21 @@ public static SymbolReference FindCommandAtPosition(Ast scriptAst, int lineNumbe /// /// The abstract syntax tree of the given script /// The symbol that we are looking for referneces of - /// Dictionary maping cmdlets to aliases for finding alias references - /// Dictionary maping aliases to cmdlets for finding alias references + /// Dictionary maping cmdlets to aliases for finding alias references + /// Dictionary maping aliases to cmdlets for finding alias references /// public static IEnumerable FindReferencesOfSymbol( Ast scriptAst, SymbolReference symbolReference, - Dictionary> CmdletToAliasDictionary, - Dictionary AliasToCmdletDictionary) + ConcurrentDictionary> cmdletToAliasDictionary = default, + ConcurrentDictionary aliasToCmdletDictionary = default) { // find the symbol evaluators for the node types we are handling - FindReferencesVisitor referencesVisitor = - new FindReferencesVisitor( - symbolReference, - CmdletToAliasDictionary, - AliasToCmdletDictionary); - scriptAst.Visit(referencesVisitor); + FindReferencesVisitor referencesVisitor = new( + symbolReference, + cmdletToAliasDictionary, + aliasToCmdletDictionary); - return referencesVisitor.FoundReferences; - } - - /// - /// Finds all references (not including aliases) in a script for the given symbol - /// - /// The abstract syntax tree of the given script - /// The symbol that we are looking for referneces of - /// If this reference search needs aliases. - /// This should always be false and used for occurence requests - /// A collection of SymbolReference objects that are refrences to the symbolRefrence - /// not including aliases - public static IEnumerable FindReferencesOfSymbol( - ScriptBlockAst scriptAst, - SymbolReference foundSymbol, - bool needsAliases) - { - FindReferencesVisitor referencesVisitor = - new FindReferencesVisitor(foundSymbol); scriptAst.Visit(referencesVisitor); return referencesVisitor.FoundReferences; diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs index a0dcb319b..409702882 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Management.Automation.Language; @@ -12,11 +13,11 @@ namespace Microsoft.PowerShell.EditorServices.Services.Symbols /// internal class FindReferencesVisitor : AstVisitor { - private SymbolReference symbolRef; - private Dictionary> CmdletToAliasDictionary; - private Dictionary AliasToCmdletDictionary; - private string symbolRefCommandName; - private bool needsAliases; + private readonly SymbolReference _symbolRef; + private readonly ConcurrentDictionary> _cmdletToAliasDictionary; + private readonly ConcurrentDictionary _aliasToCmdletDictionary; + private readonly string _symbolRefCommandName; + private readonly bool _needsAliases; public List FoundReferences { get; set; } @@ -24,36 +25,35 @@ internal class FindReferencesVisitor : AstVisitor /// Constructor used when searching for aliases is needed /// /// The found symbolReference that other symbols are being compared to - /// Dictionary maping cmdlets to aliases for finding alias references - /// Dictionary maping aliases to cmdlets for finding alias references + /// Dictionary maping cmdlets to aliases for finding alias references + /// Dictionary maping aliases to cmdlets for finding alias references public FindReferencesVisitor( SymbolReference symbolReference, - Dictionary> CmdletToAliasDictionary, - Dictionary AliasToCmdletDictionary) + ConcurrentDictionary> cmdletToAliasDictionary = default, + ConcurrentDictionary aliasToCmdletDictionary = default) { - this.symbolRef = symbolReference; - this.FoundReferences = new List(); - this.needsAliases = true; - this.CmdletToAliasDictionary = CmdletToAliasDictionary; - this.AliasToCmdletDictionary = AliasToCmdletDictionary; - - // Try to get the symbolReference's command name of an alias, - // if a command name does not exists (if the symbol isn't an alias to a command) - // set symbolRefCommandName to and empty string value - AliasToCmdletDictionary.TryGetValue(symbolReference.ScriptRegion.Text, out symbolRefCommandName); - if (symbolRefCommandName == null) { symbolRefCommandName = string.Empty; } + _symbolRef = symbolReference; + FoundReferences = new List(); - } + if (cmdletToAliasDictionary is null || aliasToCmdletDictionary is null) + { + _needsAliases = false; + return; + } - /// - /// Constructor used when searching for aliases is not needed - /// - /// The found symbolReference that other symbols are being compared to - public FindReferencesVisitor(SymbolReference foundSymbol) - { - this.symbolRef = foundSymbol; - this.FoundReferences = new List(); - this.needsAliases = false; + _needsAliases = true; + _cmdletToAliasDictionary = cmdletToAliasDictionary; + _aliasToCmdletDictionary = aliasToCmdletDictionary; + + // Try to get the symbolReference's command name of an alias. If a command name does not + // exists (if the symbol isn't an alias to a command) set symbolRefCommandName to an + // empty string. + aliasToCmdletDictionary.TryGetValue(symbolReference.ScriptRegion.Text, out _symbolRefCommandName); + + if (_symbolRefCommandName == null) + { + _symbolRefCommandName = string.Empty; + } } /// @@ -68,50 +68,44 @@ public override AstVisitAction VisitCommand(CommandAst commandAst) Ast commandNameAst = commandAst.CommandElements[0]; string commandName = commandNameAst.Extent.Text; - if(symbolRef.SymbolType.Equals(SymbolType.Function)) + if (_symbolRef.SymbolType.Equals(SymbolType.Function)) { - if (needsAliases) + if (_needsAliases) { - // Try to get the commandAst's name and aliases, - // if a command does not exists (if the symbol isn't an alias to a command) - // set command to and empty string value string command - // if the aliases do not exist (if the symvol isn't a command that has aliases) + // Try to get the commandAst's name and aliases. + // + // If a command does not exist (if the symbol isn't an alias to a command) set + // command to an empty string value string command. + // + // If the aliases do not exist (if the symbol isn't a command that has aliases) // set aliases to an empty List - string command; - List alaises; - CmdletToAliasDictionary.TryGetValue(commandName, out alaises); - AliasToCmdletDictionary.TryGetValue(commandName, out command); - if (alaises == null) { alaises = new List(); } + _cmdletToAliasDictionary.TryGetValue(commandName, out List aliases); + _aliasToCmdletDictionary.TryGetValue(commandName, out string command); + if (aliases == null) { aliases = new List(); } if (command == null) { command = string.Empty; } - if (symbolRef.SymbolType.Equals(SymbolType.Function)) + // Check if the found symbol's name is the same as the commandAst's name OR + // if the symbol's name is an alias for this commandAst's name (commandAst is a cmdlet) OR + // if the symbol's name is the same as the commandAst's cmdlet name (commandAst is a alias) + if (commandName.Equals(_symbolRef.SymbolName, StringComparison.OrdinalIgnoreCase) + // Note that PowerShell command names and aliases are case insensitive. + || aliases.Exists((match) => string.Equals(match, _symbolRef.ScriptRegion.Text, StringComparison.OrdinalIgnoreCase)) + || command.Equals(_symbolRef.ScriptRegion.Text, StringComparison.OrdinalIgnoreCase) + || (!string.IsNullOrEmpty(command) + && command.Equals(_symbolRefCommandName, StringComparison.OrdinalIgnoreCase))) { - // Check if the found symbol's name is the same as the commandAst's name OR - // if the symbol's name is an alias for this commandAst's name (commandAst is a cmdlet) OR - // if the symbol's name is the same as the commandAst's cmdlet name (commandAst is a alias) - if (commandName.Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase) || - alaises.Contains(symbolRef.ScriptRegion.Text.ToLower()) || - command.Equals(symbolRef.ScriptRegion.Text, StringComparison.CurrentCultureIgnoreCase) || - (!string.IsNullOrEmpty(command) && command.Equals(symbolRefCommandName, StringComparison.CurrentCultureIgnoreCase))) - { - this.FoundReferences.Add(new SymbolReference( - SymbolType.Function, - commandNameAst.Extent)); - } + FoundReferences.Add(new SymbolReference(SymbolType.Function, commandNameAst.Extent)); } - } else // search does not include aliases { - if (commandName.Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) + if (commandName.Equals(_symbolRef.SymbolName, StringComparison.OrdinalIgnoreCase)) { - this.FoundReferences.Add(new SymbolReference( - SymbolType.Function, - commandNameAst.Extent)); + FoundReferences.Add(new SymbolReference(SymbolType.Function, commandNameAst.Extent)); } } - } + return base.VisitCommand(commandAst); } @@ -135,12 +129,10 @@ public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst fun File = functionDefinitionAst.Extent.File }; - if (symbolRef.SymbolType.Equals(SymbolType.Function) && - nameExtent.Text.Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) + if (_symbolRef.SymbolType.Equals(SymbolType.Function) && + nameExtent.Text.Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) { - this.FoundReferences.Add(new SymbolReference( - SymbolType.Function, - nameExtent)); + FoundReferences.Add(new SymbolReference(SymbolType.Function, nameExtent)); } return base.VisitFunctionDefinition(functionDefinitionAst); } @@ -153,12 +145,10 @@ public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst fun /// A visit action that continues the search for references public override AstVisitAction VisitCommandParameter(CommandParameterAst commandParameterAst) { - if (symbolRef.SymbolType.Equals(SymbolType.Parameter) && - commandParameterAst.Extent.Text.Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) + if (_symbolRef.SymbolType.Equals(SymbolType.Parameter) && + commandParameterAst.Extent.Text.Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) { - this.FoundReferences.Add(new SymbolReference( - SymbolType.Parameter, - commandParameterAst.Extent)); + FoundReferences.Add(new SymbolReference(SymbolType.Parameter, commandParameterAst.Extent)); } return AstVisitAction.Continue; } @@ -171,12 +161,10 @@ public override AstVisitAction VisitCommandParameter(CommandParameterAst command /// A visit action that continues the search for references public override AstVisitAction VisitVariableExpression(VariableExpressionAst variableExpressionAst) { - if(symbolRef.SymbolType.Equals(SymbolType.Variable) && - variableExpressionAst.Extent.Text.Equals(symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) + if (_symbolRef.SymbolType.Equals(SymbolType.Variable) + && variableExpressionAst.Extent.Text.Equals(_symbolRef.SymbolName, StringComparison.CurrentCultureIgnoreCase)) { - this.FoundReferences.Add(new SymbolReference( - SymbolType.Variable, - variableExpressionAst.Extent)); + FoundReferences.Add(new SymbolReference(SymbolType.Variable, variableExpressionAst.Extent)); } return AstVisitAction.Continue; } @@ -186,7 +174,7 @@ private static (int, int) GetStartColumnAndLineNumbersFromAst(FunctionDefinition { int startColumnNumber = ast.Extent.StartColumnNumber; int startLineNumber = ast.Extent.StartLineNumber; - int astOffset = 0; + int astOffset; if (ast.IsFilter) { diff --git a/test/PowerShellEditorServices.Test/Services/Symbols/AstOperationsTests.cs b/test/PowerShellEditorServices.Test/Services/Symbols/AstOperationsTests.cs index 6d9d36e1c..c35bba787 100644 --- a/test/PowerShellEditorServices.Test/Services/Symbols/AstOperationsTests.cs +++ b/test/PowerShellEditorServices.Test/Services/Symbols/AstOperationsTests.cs @@ -7,13 +7,13 @@ using Microsoft.PowerShell.EditorServices.Services.Symbols; using OmniSharp.Extensions.LanguageServer.Protocol.Models; using Xunit; -using Xunit.Abstractions; namespace Microsoft.PowerShell.EditorServices.Test.Services.Symbols { + [Trait("Category", "AstOperations")] public class AstOperationsTests { - private static string s_scriptString = @"function BasicFunction {} + private const string s_scriptString = @"function BasicFunction {} BasicFunction function FunctionWithExtraSpace @@ -36,9 +36,8 @@ function FunctionWithExtraSpace FunctionNameOnDifferentLine "; - private static ScriptBlockAst s_ast = (ScriptBlockAst) ScriptBlock.Create(s_scriptString).Ast; + private static readonly ScriptBlockAst s_ast = (ScriptBlockAst) ScriptBlock.Create(s_scriptString).Ast; - [Trait("Category", "AstOperations")] [Theory] [InlineData(2, 3, "BasicFunction")] [InlineData(7, 18, "FunctionWithExtraSpace")] @@ -50,14 +49,13 @@ public void CanFindSymbolAtPostion(int lineNumber, int columnNumber, string expe Assert.Equal(expectedName, reference.SymbolName); } - [Trait("Category", "AstOperations")] [Theory] - [MemberData(nameof(FindReferencesOfSymbolAtPostionData), parameters: 3)] + [MemberData(nameof(FindReferencesOfSymbolAtPostionData))] public void CanFindReferencesOfSymbolAtPostion(int lineNumber, int columnNumber, Position[] positions) { SymbolReference symbol = AstOperations.FindSymbolAtPosition(s_ast, lineNumber, columnNumber); - IEnumerable references = AstOperations.FindReferencesOfSymbol(s_ast, symbol, needsAliases: false); + IEnumerable references = AstOperations.FindReferencesOfSymbol(s_ast, symbol); int positionsIndex = 0; foreach (SymbolReference reference in references) @@ -69,9 +67,7 @@ public void CanFindReferencesOfSymbolAtPostion(int lineNumber, int columnNumber, } } - public static object[][] FindReferencesOfSymbolAtPostionData => s_findReferencesOfSymbolAtPostionData; - - private static readonly object[][] s_findReferencesOfSymbolAtPostionData = new object[][] + public static object[][] FindReferencesOfSymbolAtPostionData { get; } = new object[][] { new object[] { 2, 3, new[] { new Position(1, 10), new Position(2, 1) } }, new object[] { 7, 18, new[] { new Position(4, 19), new Position(7, 3) } }, From 09416f9a9d1a1e0ce56c50ca41a469f53f36e50d Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 6 Jan 2022 13:19:11 -0800 Subject: [PATCH 5/6] Make `ResolveCodeLens` return a `Task` (again) Ironically this was clearly how it used to work, and was for some reason changed, even though it does need to be a task and was previously relying on synchronous side effects. --- .../Services/CodeLens/ICodeLensProvider.cs | 6 ++--- .../CodeLens/PesterCodeLensProvider.cs | 5 ++-- .../CodeLens/ReferencesCodeLensProvider.cs | 9 ++++--- .../Services/Symbols/SymbolsService.cs | 25 ++++++++++--------- .../TextDocument/Handlers/CodeLensHandlers.cs | 3 +-- .../Handlers/ReferencesHandler.cs | 8 +++--- .../Language/SymbolsServiceTests.cs | 22 ++++++++-------- 7 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/PowerShellEditorServices/Services/CodeLens/ICodeLensProvider.cs b/src/PowerShellEditorServices/Services/CodeLens/ICodeLensProvider.cs index 0221e5f1e..ff4ba33e1 100644 --- a/src/PowerShellEditorServices/Services/CodeLens/ICodeLensProvider.cs +++ b/src/PowerShellEditorServices/Services/CodeLens/ICodeLensProvider.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Threading.Tasks; using Microsoft.PowerShell.EditorServices.Services.TextDocument; using OmniSharp.Extensions.LanguageServer.Protocol.Models; @@ -34,13 +35,12 @@ internal interface ICodeLensProvider /// The CodeLens to resolve. /// /// - /// A CancellationToken which can be used to cancel the - /// request. + /// The ScriptFile to resolve it in (sometimes unused). /// /// /// A Task which returns the resolved CodeLens when completed. /// - CodeLens ResolveCodeLens( + Task ResolveCodeLens( CodeLens codeLens, ScriptFile scriptFile); } diff --git a/src/PowerShellEditorServices/Services/CodeLens/PesterCodeLensProvider.cs b/src/PowerShellEditorServices/Services/CodeLens/PesterCodeLensProvider.cs index da67d3aaf..dc6fc91e5 100644 --- a/src/PowerShellEditorServices/Services/CodeLens/PesterCodeLensProvider.cs +++ b/src/PowerShellEditorServices/Services/CodeLens/PesterCodeLensProvider.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; using System.Collections.Generic; +using System.Threading.Tasks; using Microsoft.PowerShell.EditorServices.Services; using Microsoft.PowerShell.EditorServices.Services.Symbols; using Microsoft.PowerShell.EditorServices.Services.TextDocument; @@ -133,11 +134,11 @@ public CodeLens[] ProvideCodeLenses(ScriptFile scriptFile) /// The code lens to resolve. /// The script file. /// The given CodeLens, wrapped in a task. - public CodeLens ResolveCodeLens(CodeLens codeLens, ScriptFile scriptFile) + public Task ResolveCodeLens(CodeLens codeLens, ScriptFile scriptFile) { // This provider has no specific behavior for // resolving CodeLenses. - return codeLens; + return Task.FromResult(codeLens); } } } diff --git a/src/PowerShellEditorServices/Services/CodeLens/ReferencesCodeLensProvider.cs b/src/PowerShellEditorServices/Services/CodeLens/ReferencesCodeLensProvider.cs index 84119ee81..e56dc1153 100644 --- a/src/PowerShellEditorServices/Services/CodeLens/ReferencesCodeLensProvider.cs +++ b/src/PowerShellEditorServices/Services/CodeLens/ReferencesCodeLensProvider.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Text; +using System.Threading.Tasks; using Microsoft.PowerShell.EditorServices.Services; using Microsoft.PowerShell.EditorServices.Services.Symbols; using Microsoft.PowerShell.EditorServices.Services.TextDocument; @@ -79,10 +80,10 @@ public CodeLens[] ProvideCodeLenses(ScriptFile scriptFile) /// Take a codelens and create a new codelens object with updated references. /// /// The old code lens to get updated references for. + /// /// A new code lens object describing the same data as the old one but with updated references. - public CodeLens ResolveCodeLens(CodeLens codeLens, ScriptFile scriptFile) + public async Task ResolveCodeLens(CodeLens codeLens, ScriptFile scriptFile) { - ScriptFile[] references = _workspaceService.ExpandScriptReferences( scriptFile); @@ -91,10 +92,10 @@ public CodeLens ResolveCodeLens(CodeLens codeLens, ScriptFile scriptFile) codeLens.Range.Start.Line + 1, codeLens.Range.Start.Character + 1); - List referencesResult = _symbolsService.FindReferencesOfSymbol( + List referencesResult = await _symbolsService.FindReferencesOfSymbol( foundSymbol, references, - _workspaceService); + _workspaceService).ConfigureAwait(false); Location[] referenceLocations; if (referencesResult == null) diff --git a/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs b/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs index a26bc5812..84da86b35 100644 --- a/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs +++ b/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs @@ -48,6 +48,10 @@ internal class SymbolsService /// the given Runspace to execute language service operations. /// /// An ILoggerFactory implementation used for writing log messages. + /// + /// + /// + /// public SymbolsService( ILoggerFactory factory, IRunspaceContext runspaceContext, @@ -175,7 +179,7 @@ public static SymbolReference FindSymbolAtLocation( /// An array of scriptFiles too search for references in /// The workspace that will be searched for symbols /// FindReferencesResult - public List FindReferencesOfSymbol( + public async Task> FindReferencesOfSymbol( SymbolReference foundSymbol, ScriptFile[] referencedFiles, WorkspaceService workspace) @@ -303,7 +307,7 @@ public static SymbolReference FindFunctionDefinitionAtLocation( /// The line number at which the symbol can be located. /// The column number at which the symbol can be located. /// - public async Task FindSymbolDetailsAtLocationAsync( + public Task FindSymbolDetailsAtLocationAsync( ScriptFile scriptFile, int lineNumber, int columnNumber) @@ -316,16 +320,14 @@ public async Task FindSymbolDetailsAtLocationAsync( if (symbolReference == null) { - return null; + return Task.FromResult(null); } symbolReference.FilePath = scriptFile.FilePath; - SymbolDetails symbolDetails = await SymbolDetails.CreateAsync( + return SymbolDetails.CreateAsync( symbolReference, _runspaceContext.CurrentRunspace, - _executionService).ConfigureAwait(false); - - return symbolDetails; + _executionService); } /// @@ -443,8 +445,7 @@ public async Task GetDefinitionOfSymbolAsync( if (foundDefinition == null) { // Get a list of all powershell files in the workspace path - IEnumerable allFiles = _workspaceService.EnumeratePSFiles(); - foreach (string file in allFiles) + foreach (string file in _workspaceService.EnumeratePSFiles()) { if (filesSearched.Contains(file)) { @@ -540,7 +541,7 @@ private ScriptFile[] GetBuiltinCommandScriptFiles( } string modPath = moduleInfo.Path; - List scriptFiles = new List(); + List scriptFiles = new(); ScriptFile newFile; // find any files where the moduleInfo's path ends with ps1 or psm1 @@ -595,7 +596,7 @@ public static FunctionDefinitionAst GetFunctionDefinitionForHelpComment( IEnumerable foundAsts = scriptFile.ScriptAst.FindAll( ast => { - if (!(ast is FunctionDefinitionAst fdAst)) + if (ast is not FunctionDefinitionAst fdAst) { return false; } @@ -605,7 +606,7 @@ public static FunctionDefinitionAst GetFunctionDefinitionForHelpComment( }, true); - if (foundAsts == null || !foundAsts.Any()) + if (foundAsts?.Any() != true) { helpLocation = null; return null; diff --git a/src/PowerShellEditorServices/Services/TextDocument/Handlers/CodeLensHandlers.cs b/src/PowerShellEditorServices/Services/TextDocument/Handlers/CodeLensHandlers.cs index 80850d8a5..a6a7c6dd7 100644 --- a/src/PowerShellEditorServices/Services/TextDocument/Handlers/CodeLensHandlers.cs +++ b/src/PowerShellEditorServices/Services/TextDocument/Handlers/CodeLensHandlers.cs @@ -75,8 +75,7 @@ public Task Handle(CodeLens request, CancellationToken cancellationTok _workspaceService.GetFile( codeLensData.Uri); - var resolvedCodeLens = originalProvider.ResolveCodeLens(request, scriptFile); - return Task.FromResult(resolvedCodeLens); + return originalProvider.ResolveCodeLens(request, scriptFile); } public void SetCapability(CodeLensCapability capability) diff --git a/src/PowerShellEditorServices/Services/TextDocument/Handlers/ReferencesHandler.cs b/src/PowerShellEditorServices/Services/TextDocument/Handlers/ReferencesHandler.cs index 0b8560644..ebce705d2 100644 --- a/src/PowerShellEditorServices/Services/TextDocument/Handlers/ReferencesHandler.cs +++ b/src/PowerShellEditorServices/Services/TextDocument/Handlers/ReferencesHandler.cs @@ -34,7 +34,7 @@ public PsesReferencesHandler(ILoggerFactory factory, SymbolsService symbolsServi DocumentSelector = LspUtils.PowerShellDocumentSelector }; - public override Task Handle(ReferenceParams request, CancellationToken cancellationToken) + public async override Task Handle(ReferenceParams request, CancellationToken cancellationToken) { ScriptFile scriptFile = _workspaceService.GetFile(request.TextDocument.Uri); @@ -45,10 +45,10 @@ public override Task Handle(ReferenceParams request, Cancella request.Position.Character + 1); List referencesResult = - _symbolsService.FindReferencesOfSymbol( + await _symbolsService.FindReferencesOfSymbol( foundSymbol, _workspaceService.ExpandScriptReferences(scriptFile), - _workspaceService); + _workspaceService).ConfigureAwait(false); var locations = new List(); @@ -64,7 +64,7 @@ public override Task Handle(ReferenceParams request, Cancella } } - return Task.FromResult(new LocationContainer(locations)); + return new LocationContainer(locations); } private static Range GetRangeFromScriptRegion(ScriptRegion scriptRegion) diff --git a/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs b/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs index 3192fa8e3..24d0a6fbe 100644 --- a/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs @@ -71,7 +71,7 @@ private Task GetDefinition(ScriptRegion scriptRegion) return symbolsService.GetDefinitionOfSymbolAsync(scriptFile, symbolReference); } - private List GetReferences(ScriptRegion scriptRegion) + private Task> GetReferences(ScriptRegion scriptRegion) { ScriptFile scriptFile = GetScriptFile(scriptRegion); @@ -126,9 +126,9 @@ public async Task FindsFunctionDefinition() } [Fact] - public void FindsReferencesOnFunction() + public async Task FindsReferencesOnFunction() { - List referencesResult = GetReferences(FindsReferencesOnFunctionData.SourceDetails); + List referencesResult = await GetReferences(FindsReferencesOnFunctionData.SourceDetails).ConfigureAwait(true); Assert.Equal(3, referencesResult.Count); Assert.Equal(1, referencesResult[0].ScriptRegion.StartLineNumber); Assert.Equal(10, referencesResult[0].ScriptRegion.StartColumnNumber); @@ -177,9 +177,9 @@ public async Task FindsVariableDefinition() } [Fact] - public void FindsReferencesOnVariable() + public async Task FindsReferencesOnVariable() { - List referencesResult = GetReferences(FindsReferencesOnVariableData.SourceDetails); + List referencesResult = await GetReferences(FindsReferencesOnVariableData.SourceDetails).ConfigureAwait(true); Assert.Equal(3, referencesResult.Count); Assert.Equal(10, referencesResult[referencesResult.Count - 1].ScriptRegion.StartLineNumber); Assert.Equal(13, referencesResult[referencesResult.Count - 1].ScriptRegion.StartColumnNumber); @@ -204,9 +204,9 @@ public void FindsOccurrencesOnParameter() } [Fact] - public void FindsReferencesOnCommandWithAlias() + public async Task FindsReferencesOnCommandWithAlias() { - List referencesResult = GetReferences(FindsReferencesOnBuiltInCommandWithAliasData.SourceDetails); + List referencesResult = await GetReferences(FindsReferencesOnBuiltInCommandWithAliasData.SourceDetails).ConfigureAwait(true); Assert.Equal(4, referencesResult.Count); Assert.Equal("gci", referencesResult[1].SymbolName); Assert.Equal("dir", referencesResult[2].SymbolName); @@ -214,16 +214,16 @@ public void FindsReferencesOnCommandWithAlias() } [Fact] - public void FindsReferencesOnFileWithReferencesFileB() + public async Task FindsReferencesOnFileWithReferencesFileB() { - List referencesResult = GetReferences(FindsReferencesOnFunctionMultiFileDotSourceFileB.SourceDetails); + List referencesResult = await GetReferences(FindsReferencesOnFunctionMultiFileDotSourceFileB.SourceDetails).ConfigureAwait(true); Assert.Equal(4, referencesResult.Count); } [Fact] - public void FindsReferencesOnFileWithReferencesFileC() + public async Task FindsReferencesOnFileWithReferencesFileC() { - List referencesResult = GetReferences(FindsReferencesOnFunctionMultiFileDotSourceFileC.SourceDetails); + List referencesResult = await GetReferences(FindsReferencesOnFunctionMultiFileDotSourceFileC.SourceDetails).ConfigureAwait(true); Assert.Equal(4, referencesResult.Count); } From 627e9155c26fd9039e3e71ae6366aed01f13855a Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Fri, 7 Jan 2022 13:21:16 -0800 Subject: [PATCH 6/6] Constrain use of `ConcurrentDictionary` to `CommandHelpers` Which is where it might be accessed concurrently as a cache, but we can use `IDictionary` (and return a copy of the caches) to the rest of the users. --- .../Services/PowerShell/Utility/CommandHelpers.cs | 11 +++-------- .../Services/Symbols/SymbolsService.cs | 2 +- .../Services/Symbols/Vistors/AstOperations.cs | 5 ++--- .../Services/Symbols/Vistors/FindReferencesVisitor.cs | 9 ++++----- 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/PowerShellEditorServices/Services/PowerShell/Utility/CommandHelpers.cs b/src/PowerShellEditorServices/Services/PowerShell/Utility/CommandHelpers.cs index e1dc242c4..9c83169b7 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Utility/CommandHelpers.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Utility/CommandHelpers.cs @@ -177,16 +177,10 @@ public static async Task GetCommandSynopsisAsync( /// Gets all aliases found in the runspace /// /// - public static async Task<(ConcurrentDictionary>, ConcurrentDictionary)> GetAliasesAsync(IInternalPowerShellExecutionService executionService) + public static async Task<(Dictionary>, Dictionary)> GetAliasesAsync(IInternalPowerShellExecutionService executionService) { Validate.IsNotNull(nameof(executionService), executionService); - // TODO: Should we return the caches if they're not empty, or always update? - // if (!s_cmdletToAliasCache.IsEmpty || !s_aliasToCmdletCache.IsEmpty) - // { - // return (s_cmdletToAliasCache, s_aliasToCmdletCache); - // } - IEnumerable aliases = await executionService.ExecuteDelegateAsync>( nameof(GetAliasesAsync), Execution.ExecutionOptions.Default, @@ -209,7 +203,8 @@ public static async Task GetCommandSynopsisAsync( s_aliasToCmdletCache.TryAdd(aliasInfo.Name, aliasInfo.Definition); } - return (s_cmdletToAliasCache, s_aliasToCmdletCache); + return (new Dictionary>(s_cmdletToAliasCache), + new Dictionary(s_aliasToCmdletCache)); } } } diff --git a/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs b/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs index 84da86b35..bf8e3e8c1 100644 --- a/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs +++ b/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs @@ -189,7 +189,7 @@ public async Task> FindReferencesOfSymbol( return null; } - (ConcurrentDictionary> cmdletToAliases, ConcurrentDictionary aliasToCmdlets) = await CommandHelpers.GetAliasesAsync(_executionService).ConfigureAwait(false); + (Dictionary> cmdletToAliases, Dictionary aliasToCmdlets) = await CommandHelpers.GetAliasesAsync(_executionService).ConfigureAwait(false); // We want to look for references first in referenced files, hence we use ordered dictionary // TODO: File system case-sensitivity is based on filesystem not OS, but OS is a much cheaper heuristic diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs index b8a5da1e1..a0cc38ac4 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/AstOperations.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -160,8 +159,8 @@ public static SymbolReference FindCommandAtPosition(Ast scriptAst, int lineNumbe public static IEnumerable FindReferencesOfSymbol( Ast scriptAst, SymbolReference symbolReference, - ConcurrentDictionary> cmdletToAliasDictionary = default, - ConcurrentDictionary aliasToCmdletDictionary = default) + IDictionary> cmdletToAliasDictionary = default, + IDictionary aliasToCmdletDictionary = default) { // find the symbol evaluators for the node types we are handling FindReferencesVisitor referencesVisitor = new( diff --git a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs index 409702882..7dae792a7 100644 --- a/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs +++ b/src/PowerShellEditorServices/Services/Symbols/Vistors/FindReferencesVisitor.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Management.Automation.Language; @@ -14,8 +13,8 @@ namespace Microsoft.PowerShell.EditorServices.Services.Symbols internal class FindReferencesVisitor : AstVisitor { private readonly SymbolReference _symbolRef; - private readonly ConcurrentDictionary> _cmdletToAliasDictionary; - private readonly ConcurrentDictionary _aliasToCmdletDictionary; + private readonly IDictionary> _cmdletToAliasDictionary; + private readonly IDictionary _aliasToCmdletDictionary; private readonly string _symbolRefCommandName; private readonly bool _needsAliases; @@ -29,8 +28,8 @@ internal class FindReferencesVisitor : AstVisitor /// Dictionary maping aliases to cmdlets for finding alias references public FindReferencesVisitor( SymbolReference symbolReference, - ConcurrentDictionary> cmdletToAliasDictionary = default, - ConcurrentDictionary aliasToCmdletDictionary = default) + IDictionary> cmdletToAliasDictionary = default, + IDictionary aliasToCmdletDictionary = default) { _symbolRef = symbolReference; FoundReferences = new List();