diff --git a/src/PowerShellEditorServices.Host/CodeLens/CodeLensFeature.cs b/src/PowerShellEditorServices.Host/CodeLens/CodeLensFeature.cs index e7e2f6910..3f9c11c88 100644 --- a/src/PowerShellEditorServices.Host/CodeLens/CodeLensFeature.cs +++ b/src/PowerShellEditorServices.Host/CodeLens/CodeLensFeature.cs @@ -8,8 +8,6 @@ using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol; using Microsoft.PowerShell.EditorServices.Utility; using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -99,11 +97,11 @@ private CodeLensFeature( /// /// The PowerShell script file to get CodeLenses for. /// All generated CodeLenses for the given script file. - public CodeLens[] ProvideCodeLenses(ScriptFile scriptFile) + public async Task ProvideCodeLenses(ScriptFile scriptFile) { - return InvokeProviders(provider => provider.ProvideCodeLenses(scriptFile)) - .SelectMany(codeLens => codeLens) - .ToArray(); + var providers = await Task.WhenAll(InvokeProviders(async provider => await provider.ProvideCodeLenses(scriptFile))); + return providers.SelectMany(codeLens => codeLens) + .ToArray(); } /// @@ -118,7 +116,7 @@ private async Task HandleCodeLensRequestAsync( ScriptFile scriptFile = _editorSession.Workspace.GetFile( codeLensParams.TextDocument.Uri); - CodeLens[] codeLensResults = ProvideCodeLenses(scriptFile); + CodeLens[] codeLensResults = await ProvideCodeLenses(scriptFile); var codeLensResponse = new LanguageServer.CodeLens[codeLensResults.Length]; for (int i = 0; i < codeLensResults.Length; i++) diff --git a/src/PowerShellEditorServices.Host/CodeLens/PesterCodeLensProvider.cs b/src/PowerShellEditorServices.Host/CodeLens/PesterCodeLensProvider.cs index 619ca0a49..83829896e 100644 --- a/src/PowerShellEditorServices.Host/CodeLens/PesterCodeLensProvider.cs +++ b/src/PowerShellEditorServices.Host/CodeLens/PesterCodeLensProvider.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Management.Automation; using System.Threading; using System.Threading.Tasks; @@ -25,6 +26,12 @@ internal class PesterCodeLensProvider : FeatureProviderBase, ICodeLensProvider /// private IDocumentSymbolProvider _symbolProvider; + /// + /// Pester 4.6.0 introduced a new ScriptblockFilter parameter to be able to run a test based on a line, + /// therefore knowing this information is important. + /// + private readonly Lazy> _pesterV4_6_0_OrHigherAvailable; + /// /// Create a new Pester CodeLens provider for a given editor session. /// @@ -33,6 +40,7 @@ public PesterCodeLensProvider(EditorSession editorSession) { _editorSession = editorSession; _symbolProvider = new PesterDocumentSymbolProvider(); + _pesterV4_6_0_OrHigherAvailable = new Lazy>(async () => await DeterminePesterVersion()); } /// @@ -41,10 +49,13 @@ public PesterCodeLensProvider(EditorSession editorSession) /// The Pester symbol to get CodeLenses for. /// The script file the Pester symbol comes from. /// All CodeLenses for the given Pester symbol. - private CodeLens[] GetPesterLens( + private async Task GetPesterLens( PesterSymbolReference pesterSymbol, ScriptFile scriptFile) { + // A value of null is a signal to PSES that the available Pester version does not support + // running Describe blocks by name (the test name will used instead then). + int? describeBlockLineNumber = await _pesterV4_6_0_OrHigherAvailable.Value ? (int?)pesterSymbol.ScriptRegion.StartLineNumber : null; var codeLensResults = new CodeLens[] { new CodeLens( @@ -54,7 +65,7 @@ private CodeLens[] GetPesterLens( new ClientCommand( "PowerShell.RunPesterTests", "Run tests", - new object[] { scriptFile.ClientFilePath, false /* No debug */, pesterSymbol.TestName })), + new object[] { scriptFile.ClientFilePath, false /* No debug */, pesterSymbol.TestName, describeBlockLineNumber })), new CodeLens( this, @@ -63,18 +74,45 @@ private CodeLens[] GetPesterLens( new ClientCommand( "PowerShell.RunPesterTests", "Debug tests", - new object[] { scriptFile.ClientFilePath, true /* Run in debugger */, pesterSymbol.TestName })), + new object[] { scriptFile.ClientFilePath, true /* Run in debugger */, pesterSymbol.TestName, describeBlockLineNumber })), }; return codeLensResults; } + /// + /// Used to determine the value of as a background task. + /// + private async Task DeterminePesterVersion() + { + var powerShell = new PSCommand() + .AddCommand("Get-Command") + .AddParameter("Name", "Invoke-Pester"); + + IEnumerable result = await _editorSession.PowerShellContext.ExecuteCommandAsync(powerShell); + var pesterCommand = result.FirstOrDefault(); + if (pesterCommand == null) + { + return false; + } + + if (pesterCommand.BaseObject is FunctionInfo invokePesterFunction) + { + var pesterVersion = invokePesterFunction.Version; + if (pesterVersion.Major >= 4 && pesterVersion.Minor >= 6) + { + return true; + } + } + return false; + } + /// /// Get all Pester CodeLenses for a given script file. /// /// The script file to get Pester CodeLenses for. /// All Pester CodeLenses for the given script file. - public CodeLens[] ProvideCodeLenses(ScriptFile scriptFile) + public async Task ProvideCodeLenses(ScriptFile scriptFile) { var lenses = new List(); foreach (SymbolReference symbol in _symbolProvider.ProvideDocumentSymbols(scriptFile)) @@ -86,7 +124,7 @@ public CodeLens[] ProvideCodeLenses(ScriptFile scriptFile) continue; } - lenses.AddRange(GetPesterLens(pesterSymbol, scriptFile)); + lenses.AddRange(await GetPesterLens(pesterSymbol, scriptFile)); } } diff --git a/src/PowerShellEditorServices.Host/CodeLens/ReferencesCodeLensProvider.cs b/src/PowerShellEditorServices.Host/CodeLens/ReferencesCodeLensProvider.cs index 6a5312a93..fa99bca71 100644 --- a/src/PowerShellEditorServices.Host/CodeLens/ReferencesCodeLensProvider.cs +++ b/src/PowerShellEditorServices.Host/CodeLens/ReferencesCodeLensProvider.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -50,7 +49,7 @@ public ReferencesCodeLensProvider(EditorSession editorSession) /// /// The PowerShell script file to get code lenses for. /// An array of CodeLenses describing all functions in the given script file. - public CodeLens[] ProvideCodeLenses(ScriptFile scriptFile) + public async Task ProvideCodeLenses(ScriptFile scriptFile) { var acc = new List(); foreach (SymbolReference sym in _symbolProvider.ProvideDocumentSymbols(scriptFile)) diff --git a/src/PowerShellEditorServices/CodeLenses/ICodeLensProvider.cs b/src/PowerShellEditorServices/CodeLenses/ICodeLensProvider.cs index 71a1c0243..76245b969 100644 --- a/src/PowerShellEditorServices/CodeLenses/ICodeLensProvider.cs +++ b/src/PowerShellEditorServices/CodeLenses/ICodeLensProvider.cs @@ -3,9 +3,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -using Microsoft.PowerShell.EditorServices.Utility; -using System.Collections.Generic; -using System.Management.Automation.Language; using System.Threading; using System.Threading.Tasks; @@ -24,7 +21,7 @@ public interface ICodeLensProvider : IFeatureProvider /// The document for which CodeLenses should be provided. /// /// An array of CodeLenses. - CodeLens[] ProvideCodeLenses(ScriptFile scriptFile); + Task ProvideCodeLenses(ScriptFile scriptFile); /// /// Resolves a CodeLens that was created without a Command. diff --git a/src/PowerShellEditorServices/CodeLenses/ICodeLenses.cs b/src/PowerShellEditorServices/CodeLenses/ICodeLenses.cs index f63319c1c..e69a05fed 100644 --- a/src/PowerShellEditorServices/CodeLenses/ICodeLenses.cs +++ b/src/PowerShellEditorServices/CodeLenses/ICodeLenses.cs @@ -3,8 +3,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; namespace Microsoft.PowerShell.EditorServices.CodeLenses @@ -29,6 +27,6 @@ public interface ICodeLenses /// The document for which CodeLenses should be provided. /// /// An array of CodeLenses. - CodeLens[] ProvideCodeLenses(ScriptFile scriptFile); + Task ProvideCodeLenses(ScriptFile scriptFile); } }