diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4f1d2b042..8f174da10 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -29,7 +29,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install dotnet uses: actions/setup-dotnet@v3 diff --git a/.github/workflows/emacs-test.yml b/.github/workflows/emacs-test.yml index 338b7c1b7..5aa591e24 100644 --- a/.github/workflows/emacs-test.yml +++ b/.github/workflows/emacs-test.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install dotnet uses: actions/setup-dotnet@v3 diff --git a/.github/workflows/vim-test.yml b/.github/workflows/vim-test.yml index e529f3b14..fdeefd348 100644 --- a/.github/workflows/vim-test.yml +++ b/.github/workflows/vim-test.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install dotnet uses: actions/setup-dotnet@v3 @@ -31,13 +31,13 @@ jobs: id: vim - name: Checkout vim-ps1 - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: PProvost/vim-ps1 path: vim-ps1 - name: Checkout LanguageClient-neovim - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: autozimu/LanguageClient-neovim path: LanguageClient-neovim @@ -47,7 +47,7 @@ jobs: working-directory: LanguageClient-neovim - name: Checkout Themis - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: thinca/vim-themis path: vim-themis diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fa58c805..7be7b19f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # PowerShell Editor Services Release History +## v3.11.0 +### Wednesday, September 13, 2023 + +- 🐛 💭 [vscode-powershell #4735](https://github.com/PowerShell/PowerShellEditorServices/pull/2066) - Add `None` to `PsesLogLevel` enum. +- ⚡️ 💭 [PowerShellEditorServices #2065](https://github.com/PowerShell/PowerShellEditorServices/pull/2065) - Remove unused telemetry. +- 🐛 🔍 [vscode-powershell #3904](https://github.com/PowerShell/PowerShellEditorServices/pull/2064) - Fix debugging script blocks that aren't in files. +- 🐛 🚂 [vscode-powershell #3971](https://github.com/PowerShell/PowerShellEditorServices/pull/2062) - Import `PSDesiredStateConfiguration` by name. + ## v3.10.0 ### Friday, August 25, 2023 diff --git a/PowerShellEditorServices.Common.props b/PowerShellEditorServices.Common.props index d381bfc84..92df95ada 100644 --- a/PowerShellEditorServices.Common.props +++ b/PowerShellEditorServices.Common.props @@ -1,6 +1,6 @@ - 3.10.0 + 3.11.0 Microsoft © Microsoft Corporation. diff --git a/module/PowerShellEditorServices/PowerShellEditorServices.psd1 b/module/PowerShellEditorServices/PowerShellEditorServices.psd1 index c6a713b40..fe8759ec5 100644 --- a/module/PowerShellEditorServices/PowerShellEditorServices.psd1 +++ b/module/PowerShellEditorServices/PowerShellEditorServices.psd1 @@ -19,7 +19,7 @@ RootModule = if ($PSEdition -eq 'Core') } # Version number of this module. -ModuleVersion = '3.10.0' +ModuleVersion = '3.11.0' # ID used to uniquely identify this module GUID = '9ca15887-53a2-479a-9cda-48d26bcb6c47' diff --git a/src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs b/src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs index 10de11254..4589b9a44 100644 --- a/src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs +++ b/src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs @@ -301,7 +301,7 @@ private void RemovePSReadLineForStartup() { _logger.Log(PsesLogLevel.Verbose, "Removing PSReadLine"); using SMA.PowerShell pwsh = SMA.PowerShell.Create(RunspaceMode.CurrentRunspace); - bool hasPSReadLine = pwsh.AddCommand(new CmdletInfo("Microsoft.PowerShell.Core\\Get-Module", typeof(GetModuleCommand))) + bool hasPSReadLine = pwsh.AddCommand(new CmdletInfo(@"Microsoft.PowerShell.Core\Get-Module", typeof(GetModuleCommand))) .AddParameter("Name", "PSReadLine") .Invoke() .Count > 0; @@ -310,7 +310,7 @@ private void RemovePSReadLineForStartup() { pwsh.Commands.Clear(); - pwsh.AddCommand(new CmdletInfo("Microsoft.PowerShell.Core\\Remove-Module", typeof(RemoveModuleCommand))) + pwsh.AddCommand(new CmdletInfo(@"Microsoft.PowerShell.Core\Remove-Module", typeof(RemoveModuleCommand))) .AddParameter("Name", "PSReadLine") .AddParameter("ErrorAction", "SilentlyContinue"); diff --git a/src/PowerShellEditorServices.Hosting/Configuration/HostLogger.cs b/src/PowerShellEditorServices.Hosting/Configuration/HostLogger.cs index 79ac55516..7ce6f66a9 100644 --- a/src/PowerShellEditorServices.Hosting/Configuration/HostLogger.cs +++ b/src/PowerShellEditorServices.Hosting/Configuration/HostLogger.cs @@ -26,6 +26,7 @@ public enum PsesLogLevel Normal = 2, Warning = 3, Error = 4, + None = 5 } /// diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/DebugService.cs b/src/PowerShellEditorServices/Services/DebugAdapter/DebugService.cs index 0439f4484..9328e5440 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/DebugService.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/DebugService.cs @@ -930,13 +930,24 @@ internal async void OnDebuggerStopAsync(object sender, DebuggerStopEventArgs e) if (_remoteFileManager is not null && string.IsNullOrEmpty(localScriptPath)) { // Get the current script listing and create the buffer - PSCommand command = new PSCommand().AddScript($"list 1 {int.MaxValue}"); + IReadOnlyList scriptListingLines; + await debugInfoHandle.WaitAsync().ConfigureAwait(false); + try + { + // This command must be run through `ExecuteInDebugger`! + PSCommand psCommand = new PSCommand().AddScript($"list 1 {int.MaxValue}"); - IReadOnlyList scriptListingLines = - await _executionService.ExecutePSCommandAsync( - command, CancellationToken.None).ConfigureAwait(false); + scriptListingLines = + await _executionService.ExecutePSCommandAsync( + psCommand, + CancellationToken.None).ConfigureAwait(false); + } + finally + { + debugInfoHandle.Release(); + } - if (scriptListingLines is not null) + if (scriptListingLines.Count > 0) { int linePrefixLength = 0; diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs index 8b5b4dee6..ac6490c7b 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs @@ -378,9 +378,9 @@ await _executionService.ExecutePSCommandAsync( if (request.RunspaceName != null) { PSCommand getRunspaceIdCommand = new PSCommand() - .AddCommand("Microsoft.PowerShell.Utility\\Get-Runspace") + .AddCommand(@"Microsoft.PowerShell.Utility\Get-Runspace") .AddParameter("Name", request.RunspaceName) - .AddCommand("Microsoft.PowerShell.Utility\\Select-Object") + .AddCommand(@"Microsoft.PowerShell.Utility\Select-Object") .AddParameter("ExpandProperty", "Id"); try diff --git a/src/PowerShellEditorServices/Services/PowerShell/Debugging/DscBreakpointCapability.cs b/src/PowerShellEditorServices/Services/PowerShell/Debugging/DscBreakpointCapability.cs index 3bbd166ed..da486ccbc 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Debugging/DscBreakpointCapability.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Debugging/DscBreakpointCapability.cs @@ -92,8 +92,8 @@ public static async Task GetDscCapabilityAsync( if (!isDscInstalled.HasValue) { PSCommand psCommand = new PSCommand() - .AddCommand("Import-Module") - .AddArgument(@"C:\Program Files\DesiredStateConfiguration\1.0.0.0\Modules\PSDesiredStateConfiguration\PSDesiredStateConfiguration.psd1") + .AddCommand(@"Microsoft.PowerShell.Core\Import-Module") + .AddParameter("-Name", "PSDesiredStateConfiguration") .AddParameter("PassThru") .AddParameter("ErrorAction", ActionPreference.Ignore); @@ -101,7 +101,8 @@ public static async Task GetDscCapabilityAsync( await psesHost.ExecutePSCommandAsync( psCommand, CancellationToken.None, - new PowerShellExecutionOptions { ThrowOnError = false }).ConfigureAwait(false); + new PowerShellExecutionOptions { ThrowOnError = false }) + .ConfigureAwait(false); isDscInstalled = dscModule.Count > 0; logger.LogTrace("Side-by-side DSC module found: " + isDscInstalled.Value); diff --git a/src/PowerShellEditorServices/Services/PowerShell/Execution/SynchronousPowerShellTask.cs b/src/PowerShellEditorServices/Services/PowerShell/Execution/SynchronousPowerShellTask.cs index c7a983bd8..7c05b3da2 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Execution/SynchronousPowerShellTask.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Execution/SynchronousPowerShellTask.cs @@ -73,9 +73,12 @@ public override IReadOnlyList Run(CancellationToken cancellationToken) // state that we sync with the LSP debugger. The former commands we want to send // through PowerShell's `Debugger.ProcessCommand` so that they work as expected, but // the latter we must not send through it else they pollute the history as this - // PowerShell API does not let us exclude them from it. + // PowerShell API does not let us exclude them from it. Notably we also need to send + // the `prompt` command and our special `list 1 ` through the debugger too. + // The former needs the context in order to show `DBG 1>` etc., and the latter is + // used to gather the lines when debugging a script that isn't in a file. return _pwsh.Runspace.Debugger.InBreakpoint - && (PowerShellExecutionOptions.AddToHistory || IsPromptCommand(_psCommand) || _pwsh.Runspace.RunspaceIsRemote) + && (PowerShellExecutionOptions.AddToHistory || IsPromptOrListCommand(_psCommand) || _pwsh.Runspace.RunspaceIsRemote) ? ExecuteInDebugger(cancellationToken) : ExecuteNormally(cancellationToken); } @@ -87,7 +90,7 @@ public override IReadOnlyList Run(CancellationToken cancellationToken) public override string ToString() => _psCommand.GetInvocationText(); - private static bool IsPromptCommand(PSCommand command) + private static bool IsPromptOrListCommand(PSCommand command) { if (command.Commands.Count is not 1 || command.Commands[0] is { IsScript: false } or { Parameters.Count: > 0 }) @@ -96,7 +99,8 @@ private static bool IsPromptCommand(PSCommand command) } string commandText = command.Commands[0].CommandText; - return commandText.Equals("prompt", StringComparison.OrdinalIgnoreCase); + return commandText.Equals("prompt", StringComparison.OrdinalIgnoreCase) + || commandText.Equals($"list 1 {int.MaxValue}", StringComparison.OrdinalIgnoreCase); } private IReadOnlyList ExecuteNormally(CancellationToken cancellationToken) @@ -301,11 +305,10 @@ private IReadOnlyList ExecuteInDebugger(CancellationToken cancellationT return Array.Empty(); } - // If we've been asked for a PSObject, no need to allocate a new collection - if (typeof(TResult) == typeof(PSObject) - && outputCollection is IReadOnlyList resultCollection) + // If we've been asked for a PSObject, no need to convert + if (typeof(TResult) == typeof(PSObject)) { - return resultCollection; + return new List(outputCollection) as IReadOnlyList; } // Otherwise, convert things over diff --git a/src/PowerShellEditorServices/Services/PowerShell/Handlers/GetCommandHandler.cs b/src/PowerShellEditorServices/Services/PowerShell/Handlers/GetCommandHandler.cs index 12df83713..a9f48ce52 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Handlers/GetCommandHandler.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Handlers/GetCommandHandler.cs @@ -42,9 +42,9 @@ public async Task> Handle(GetCommandParams request, Cance // Executes the following: // Get-Command -CommandType Function,Cmdlet,ExternalScript | Sort-Object -Property Name psCommand - .AddCommand("Microsoft.PowerShell.Core\\Get-Command") + .AddCommand(@"Microsoft.PowerShell.Core\Get-Command") .AddParameter("CommandType", new[] { "Function", "Cmdlet", "ExternalScript" }) - .AddCommand("Microsoft.PowerShell.Utility\\Sort-Object") + .AddCommand(@"Microsoft.PowerShell.Utility\Sort-Object") .AddParameter("Property", "Name"); IEnumerable result = await _executionService.ExecutePSCommandAsync(psCommand, cancellationToken).ConfigureAwait(false); diff --git a/src/PowerShellEditorServices/Services/PowerShell/Handlers/PSHostProcessAndRunspaceHandlers.cs b/src/PowerShellEditorServices/Services/PowerShell/Handlers/PSHostProcessAndRunspaceHandlers.cs index fcf58cce3..bff586d44 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Handlers/PSHostProcessAndRunspaceHandlers.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Handlers/PSHostProcessAndRunspaceHandlers.cs @@ -74,11 +74,11 @@ public async Task Handle(GetRunspaceParams request, Cancella rs.Open(); ps.Runspace = rs; // Returns deserialized Runspaces. For simpler code, we use PSObject and rely on dynamic later. - runspaces = ps.AddCommand("Microsoft.PowerShell.Utility\\Get-Runspace").Invoke(); + runspaces = ps.AddCommand(@"Microsoft.PowerShell.Utility\Get-Runspace").Invoke(); } else { - PSCommand psCommand = new PSCommand().AddCommand("Microsoft.PowerShell.Utility\\Get-Runspace"); + PSCommand psCommand = new PSCommand().AddCommand(@"Microsoft.PowerShell.Utility\Get-Runspace"); // returns (not deserialized) Runspaces. For simpler code, we use PSObject and rely on dynamic later. runspaces = await _executionService.ExecutePSCommandAsync(psCommand, cancellationToken).ConfigureAwait(false); } diff --git a/src/PowerShellEditorServices/Services/PowerShell/Utility/CommandHelpers.cs b/src/PowerShellEditorServices/Services/PowerShell/Utility/CommandHelpers.cs index 38a581bec..6532c531a 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Utility/CommandHelpers.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Utility/CommandHelpers.cs @@ -237,7 +237,7 @@ public static async Task GetAliasesAsync( // our PSRL on idle handler. IReadOnlyList aliases = await executionService.ExecutePSCommandAsync( new PSCommand() - .AddCommand("Microsoft.PowerShell.Core\\Get-Command") + .AddCommand(@"Microsoft.PowerShell.Core\Get-Command") .AddParameter("ListImported", true) .AddParameter("CommandType", CommandTypes.Alias), cancellationToken).ConfigureAwait(false); diff --git a/src/PowerShellEditorServices/Services/PowerShell/Utility/PowerShellExtensions.cs b/src/PowerShellEditorServices/Services/PowerShell/Utility/PowerShellExtensions.cs index d7e85f3e4..1032e70f5 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Utility/PowerShellExtensions.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Utility/PowerShellExtensions.cs @@ -139,7 +139,7 @@ public static void SetCorrectExecutionPolicy(this PowerShell pwsh, ILogger logge // We want to get the list hierarchy of execution policies // Calling the cmdlet is the simplest way to do that IReadOnlyList policies = pwsh - .AddCommand("Microsoft.PowerShell.Security\\Get-ExecutionPolicy") + .AddCommand(@"Microsoft.PowerShell.Security\Get-ExecutionPolicy") .AddParameter("-List") .InvokeAndClear(); @@ -181,7 +181,7 @@ public static void SetCorrectExecutionPolicy(this PowerShell pwsh, ILogger logge logger.LogTrace("Setting execution policy to {Policy}", policyToSet); try { - pwsh.AddCommand("Microsoft.PowerShell.Security\\Set-ExecutionPolicy") + pwsh.AddCommand(@"Microsoft.PowerShell.Security\Set-ExecutionPolicy") .AddParameter("Scope", ExecutionPolicyScope.Process) .AddParameter("ExecutionPolicy", policyToSet) .AddParameter("Force") @@ -222,7 +222,7 @@ public static void LoadProfiles(this PowerShell pwsh, ProfilePathInfo profilePat public static void ImportModule(this PowerShell pwsh, string moduleNameOrPath) { - pwsh.AddCommand("Microsoft.PowerShell.Core\\Import-Module") + pwsh.AddCommand(@"Microsoft.PowerShell.Core\Import-Module") .AddParameter("-Name", moduleNameOrPath) .InvokeAndClear(); } diff --git a/src/PowerShellEditorServices/Services/Template/TemplateService.cs b/src/PowerShellEditorServices/Services/Template/TemplateService.cs index 4de0de520..e7295ec8c 100644 --- a/src/PowerShellEditorServices/Services/Template/TemplateService.cs +++ b/src/PowerShellEditorServices/Services/Template/TemplateService.cs @@ -74,7 +74,10 @@ public async Task ImportPlasterIfInstalledAsync() IReadOnlyList moduleObject = await _executionService.ExecutePSCommandAsync( - psCommand, CancellationToken.None).ConfigureAwait(false); + psCommand, + CancellationToken.None, + new PowerShellExecutionOptions { ThrowOnError = false }) + .ConfigureAwait(false); isPlasterInstalled = moduleObject.Count > 0; _logger.LogTrace("Plaster installed: " + isPlasterInstalled.Value); @@ -86,13 +89,19 @@ await _executionService.ExecutePSCommandAsync( psCommand = new PSCommand(); psCommand - .AddCommand("Import-Module") + .AddCommand(@"Microsoft.PowerShell.Core\Import-Module") .AddParameter("ModuleInfo", (PSModuleInfo)moduleObject[0].ImmediateBaseObject) - .AddParameter("PassThru"); + .AddParameter("PassThru") + .AddParameter("ErrorAction", ActionPreference.Ignore); - IReadOnlyList importResult = await _executionService.ExecutePSCommandAsync(psCommand, CancellationToken.None).ConfigureAwait(false); + IReadOnlyList plasterModule = + await _executionService.ExecutePSCommandAsync( + psCommand, + CancellationToken.None, + new PowerShellExecutionOptions { ThrowOnError = false }) + .ConfigureAwait(false); - isPlasterLoaded = importResult.Count > 0; + isPlasterLoaded = plasterModule.Count > 0; _logger.LogTrace("Plaster loaded: " + isPlasterLoaded); } } diff --git a/src/PowerShellEditorServices/Services/Workspace/Handlers/ConfigurationHandler.cs b/src/PowerShellEditorServices/Services/Workspace/Handlers/ConfigurationHandler.cs index 230a82d58..a7f36aab1 100644 --- a/src/PowerShellEditorServices/Services/Workspace/Handlers/ConfigurationHandler.cs +++ b/src/PowerShellEditorServices/Services/Workspace/Handlers/ConfigurationHandler.cs @@ -7,13 +7,9 @@ using System.Threading.Tasks; using MediatR; using Microsoft.Extensions.Logging; -using Microsoft.PowerShell.EditorServices.Logging; using Microsoft.PowerShell.EditorServices.Services; using Microsoft.PowerShell.EditorServices.Services.Configuration; -using Newtonsoft.Json.Linq; using OmniSharp.Extensions.LanguageServer.Protocol.Models; -using OmniSharp.Extensions.LanguageServer.Protocol.Server; -using OmniSharp.Extensions.LanguageServer.Protocol.Window; using OmniSharp.Extensions.LanguageServer.Protocol.Workspace; namespace Microsoft.PowerShell.EditorServices.Handlers @@ -23,19 +19,16 @@ internal class PsesConfigurationHandler : DidChangeConfigurationHandlerBase private readonly ILogger _logger; private readonly WorkspaceService _workspaceService; private readonly ConfigurationService _configurationService; - private readonly ILanguageServerFacade _languageServer; public PsesConfigurationHandler( ILoggerFactory factory, WorkspaceService workspaceService, AnalysisService analysisService, ConfigurationService configurationService, - ILanguageServerFacade languageServer, SymbolsService symbolsService) { _logger = factory.CreateLogger(); _workspaceService = workspaceService; _configurationService = configurationService; - _languageServer = languageServer; ConfigurationUpdated += analysisService.OnConfigurationUpdated; ConfigurationUpdated += symbolsService.OnConfigurationUpdated; @@ -51,8 +44,6 @@ public override async Task Handle(DidChangeConfigurationParams request, Ca return await Unit.Task.ConfigureAwait(false); } - SendFeatureChangesTelemetry(incomingSettings); - bool oldScriptAnalysisEnabled = _configurationService.CurrentSettings.ScriptAnalysis.Enable; string oldScriptAnalysisSettingsPath = _configurationService.CurrentSettings.ScriptAnalysis?.SettingsPath; @@ -109,59 +100,6 @@ public override async Task Handle(DidChangeConfigurationParams request, Ca return await Unit.Task.ConfigureAwait(false); } - private void SendFeatureChangesTelemetry(LanguageServerSettingsWrapper incomingSettings) - { - if (incomingSettings is null) - { - _logger.LogTrace("Incoming settings were null"); - return; - } - - Dictionary configChanges = new(); - // Send telemetry if the user opted-out of ScriptAnalysis - if (!incomingSettings.Powershell.ScriptAnalysis.Enable && - _configurationService.CurrentSettings.ScriptAnalysis.Enable != incomingSettings.Powershell.ScriptAnalysis.Enable) - { - configChanges["ScriptAnalysis"] = incomingSettings.Powershell.ScriptAnalysis.Enable; - } - - // Send telemetry if the user opted-out of CodeFolding - if (!incomingSettings.Powershell.CodeFolding.Enable && - _configurationService.CurrentSettings.CodeFolding.Enable != incomingSettings.Powershell.CodeFolding.Enable) - { - configChanges["CodeFolding"] = incomingSettings.Powershell.CodeFolding.Enable; - } - - // Send telemetry if the user opted-out of Profile loading - if (!incomingSettings.Powershell.EnableProfileLoading && - _configurationService.CurrentSettings.EnableProfileLoading != incomingSettings.Powershell.EnableProfileLoading) - { - configChanges["ProfileLoading"] = incomingSettings.Powershell.EnableProfileLoading; - } - - // Send telemetry if the user opted-in to Pester 5+ CodeLens - if (!incomingSettings.Powershell.Pester.UseLegacyCodeLens && - _configurationService.CurrentSettings.Pester.UseLegacyCodeLens != incomingSettings.Powershell.Pester.UseLegacyCodeLens) - { - // From our perspective we want to see how many people are opting in to this so we flip the value - configChanges["Pester5CodeLens"] = !incomingSettings.Powershell.Pester.UseLegacyCodeLens; - } - - // No need to send any telemetry since nothing changed - if (configChanges.Count == 0) - { - return; - } - - _languageServer.Window.SendTelemetryEvent(new TelemetryEventParams - { - ExtensionData = new PsesTelemetryEvent - { - EventName = "NonDefaultPsesFeatureConfiguration", - Data = JObject.FromObject(configChanges) - } - }); - } public event EventHandler ConfigurationUpdated; } diff --git a/src/PowerShellEditorServices/Services/Workspace/RemoteFileManagerService.cs b/src/PowerShellEditorServices/Services/Workspace/RemoteFileManagerService.cs index 0a7ed4c74..a3e46f4f1 100644 --- a/src/PowerShellEditorServices/Services/Workspace/RemoteFileManagerService.cs +++ b/src/PowerShellEditorServices/Services/Workspace/RemoteFileManagerService.cs @@ -316,7 +316,7 @@ public async Task FetchRemoteFileAsync( { // Load the file contents from the remote machine and create the buffer PSCommand command = new PSCommand() - .AddCommand("Microsoft.PowerShell.Management\\Get-Content") + .AddCommand(@"Microsoft.PowerShell.Management\Get-Content") .AddParameter("Path", remoteFilePath) .AddParameter("Raw"); diff --git a/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs b/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs index fd4854f04..1a227278d 100644 --- a/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs +++ b/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs @@ -12,7 +12,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.PowerShell.EditorServices.Handlers; -using Microsoft.PowerShell.EditorServices.Logging; using Microsoft.PowerShell.EditorServices.Services.Configuration; using Microsoft.PowerShell.EditorServices.Services.PowerShell; using Microsoft.PowerShell.EditorServices.Services.Template; @@ -40,7 +39,6 @@ public class LanguageServerProtocolMessageTests : IClassFixture private readonly ILanguageClient PsesLanguageClient; private readonly List Messages; private readonly List Diagnostics; - private readonly List TelemetryEvents; private readonly string PwshExe; public LanguageServerProtocolMessageTests(ITestOutputHelper output, LSPTestsFixture data) @@ -51,15 +49,12 @@ public LanguageServerProtocolMessageTests(ITestOutputHelper output, LSPTestsFixt Messages.Clear(); Diagnostics = data.Diagnostics; Diagnostics.Clear(); - TelemetryEvents = data.TelemetryEvents; - TelemetryEvents.Clear(); PwshExe = PsesStdioProcess.PwshExe; } public void Dispose() { Diagnostics.Clear(); - TelemetryEvents.Clear(); GC.SuppressFinalize(this); } @@ -91,26 +86,12 @@ private async Task WaitForDiagnosticsAsync() // Wait for PSSA to finish. for (int i = 0; Diagnostics.Count == 0; i++) { - if (i >= 10) + if (i >= 30) { throw new InvalidDataException("No diagnostics showed up after 20s."); } - await Task.Delay(2000).ConfigureAwait(true); - } - } - - private async Task WaitForTelemetryEventsAsync() - { - // Wait for PSSA to finish. - for (int i = 0; TelemetryEvents.Count == 0; i++) - { - if (i >= 10) - { - throw new InvalidDataException("No telemetry events showed up after 20s."); - } - - await Task.Delay(2000).ConfigureAwait(true); + await Task.Delay(1000).ConfigureAwait(true); } } @@ -233,12 +214,6 @@ public async Task CanReceiveDiagnosticsFromConfigurationChangeAsync() Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, "Windows PowerShell doesn't trust PSScriptAnalyzer by default so it won't load."); - NewTestFile("gci | % { $_ }"); - await WaitForDiagnosticsAsync().ConfigureAwait(true); - - // NewTestFile doesn't clear diagnostic notifications so we need to do that for this test. - Diagnostics.Clear(); - PsesLanguageClient.SendNotification("workspace/didChangeConfiguration", new DidChangeConfigurationParams { @@ -256,18 +231,12 @@ public async Task CanReceiveDiagnosticsFromConfigurationChangeAsync() }) }); - await WaitForTelemetryEventsAsync().ConfigureAwait(true); - PsesTelemetryEvent telemetryEvent = Assert.Single(TelemetryEvents); - Assert.Equal("NonDefaultPsesFeatureConfiguration", telemetryEvent.EventName); - Assert.False((bool)telemetryEvent.Data.GetValue("ScriptAnalysis")); + string filePath = NewTestFile("$a = 4"); - // We also shouldn't get any Diagnostics because ScriptAnalysis is disabled. + // Wait a bit to make sure no diagnostics came through + await Task.Delay(2000).ConfigureAwait(true); Assert.Empty(Diagnostics); - // Clear telemetry events so we can test to make sure telemetry doesn't - // come through with default settings. - TelemetryEvents.Clear(); - // Restore default configuration PsesLanguageClient.SendNotification("workspace/didChangeConfiguration", new DidChangeConfigurationParams @@ -280,10 +249,22 @@ public async Task CanReceiveDiagnosticsFromConfigurationChangeAsync() }) }); - // Wait a bit to make sure no telemetry events came through - await Task.Delay(2000).ConfigureAwait(true); - // Since we have default settings we should not get any telemetry events about - Assert.Empty(TelemetryEvents.Where(e => e.EventName == "NonDefaultPsesFeatureConfiguration")); + // That notification does not trigger re-analyzing open files. For that we have to send + // a textDocument/didChange notification. + PsesLanguageClient.SendNotification("textDocument/didChange", new DidChangeTextDocumentParams + { + ContentChanges = new Container(), + TextDocument = new OptionalVersionedTextDocumentIdentifier + { + Version = 4, + Uri = new Uri(filePath) + } + }); + + await WaitForDiagnosticsAsync().ConfigureAwait(true); + + Diagnostic diagnostic = Assert.Single(Diagnostics); + Assert.Equal("PSUseDeclaredVarsMoreThanAssignments", diagnostic.Code); } [Fact] diff --git a/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj b/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj index 7267cf110..5f4d99362 100644 --- a/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj +++ b/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj @@ -8,7 +8,7 @@ - + diff --git a/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs b/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs index bfdac2774..294901362 100644 --- a/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs @@ -1140,7 +1140,7 @@ await debugService.SetCommandBreakpointsAsync( VariableDetailsBase var = Array.Find(variables, v => v.Name == "$file"); VariableDetailsBase[] childVars = await debugService.GetVariables(var.Id, CancellationToken.None).ConfigureAwait(true); Assert.Contains(childVars, i => i.Name is "PSPath"); - Assert.Contains(childVars, i => i.Name is "PSProvider" && i.ValueString is "Microsoft.PowerShell.Core\\FileSystem"); + Assert.Contains(childVars, i => i.Name is "PSProvider" && i.ValueString is @"Microsoft.PowerShell.Core\FileSystem"); Assert.Contains(childVars, i => i.Name is "Exists" && i.ValueString is "$true"); Assert.Contains(childVars, i => i.Name is "LastAccessTime"); } diff --git a/test/PowerShellEditorServices.Test/Extensions/ExtensionCommandTests.cs b/test/PowerShellEditorServices.Test/Extensions/ExtensionCommandTests.cs index 430687f93..613202094 100644 --- a/test/PowerShellEditorServices.Test/Extensions/ExtensionCommandTests.cs +++ b/test/PowerShellEditorServices.Test/Extensions/ExtensionCommandTests.cs @@ -51,7 +51,7 @@ public void Dispose() [Fact] public async Task CanRegisterAndInvokeCommandWithCmdletName() { - string filePath = TestUtilities.NormalizePath("C:\\Temp\\Test.ps1"); + string filePath = TestUtilities.NormalizePath(@"C:\Temp\Test.ps1"); ScriptFile currentFile = new(new Uri(filePath), "This is a test file", new Version("7.0")); EditorContext editorContext = new( editorOperations: null, @@ -87,7 +87,7 @@ await psesHost.ExecutePSCommandAsync( [Fact] public async Task CanRegisterAndInvokeCommandWithScriptBlock() { - string filePath = TestUtilities.NormalizePath("C:\\Temp\\Test.ps1"); + string filePath = TestUtilities.NormalizePath(@"C:\Temp\Test.ps1"); ScriptFile currentFile = new(new Uri(filePath), "This is a test file", new Version("7.0")); EditorContext editorContext = new( editorOperations: null, @@ -149,7 +149,7 @@ await psesHost.ExecutePSCommandAsync( [Fact] public async Task CanUnregisterCommand() { - string filePath = TestUtilities.NormalizePath("C:\\Temp\\Test.ps1"); + string filePath = TestUtilities.NormalizePath(@"C:\Temp\Test.ps1"); ScriptFile currentFile = new(new Uri(filePath), "This is a test file", new Version("7.0")); EditorContext editorContext = new( editorOperations: null, diff --git a/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj b/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj index e3e8ae1d7..8a8a21be2 100644 --- a/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj +++ b/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj @@ -32,7 +32,7 @@ - + diff --git a/test/PowerShellEditorServices.Test/Session/PathEscapingTests.cs b/test/PowerShellEditorServices.Test/Session/PathEscapingTests.cs index ed591a395..3d4f0d479 100644 --- a/test/PowerShellEditorServices.Test/Session/PathEscapingTests.cs +++ b/test/PowerShellEditorServices.Test/Session/PathEscapingTests.cs @@ -12,17 +12,17 @@ public class PathEscapingTests [Theory] [InlineData("DebugTest.ps1", "DebugTest.ps1")] [InlineData("../../DebugTest.ps1", "../../DebugTest.ps1")] - [InlineData("C:\\Users\\me\\Documents\\DebugTest.ps1", "C:\\Users\\me\\Documents\\DebugTest.ps1")] + [InlineData(@"C:\Users\me\Documents\DebugTest.ps1", @"C:\Users\me\Documents\DebugTest.ps1")] [InlineData("/home/me/Documents/weird&folder/script.ps1", "/home/me/Documents/weird&folder/script.ps1")] [InlineData("./path/with some/spaces", "./path/with some/spaces")] - [InlineData("C:\\path\\with[some]brackets\\file.ps1", "C:\\path\\with`[some`]brackets\\file.ps1")] - [InlineData("C:\\look\\an*\\here.ps1", "C:\\look\\an`*\\here.ps1")] + [InlineData(@"C:\path\with[some]brackets\file.ps1", @"C:\path\with`[some`]brackets\file.ps1")] + [InlineData(@"C:\look\an*\here.ps1", @"C:\look\an`*\here.ps1")] [InlineData("/Users/me/Documents/?here.ps1", "/Users/me/Documents/`?here.ps1")] [InlineData("/Brackets [and s]paces/path.ps1", "/Brackets `[and s`]paces/path.ps1")] [InlineData("/CJK.chars/脚本/hello.ps1", "/CJK.chars/脚本/hello.ps1")] [InlineData("/CJK.chars/脚本/[hello].ps1", "/CJK.chars/脚本/`[hello`].ps1")] - [InlineData("C:\\Animals\\утка\\quack.ps1", "C:\\Animals\\утка\\quack.ps1")] - [InlineData("C:\\&nimals\\утка\\qu*ck?.ps1", "C:\\&nimals\\утка\\qu`*ck`?.ps1")] + [InlineData(@"C:\Animals\утка\quack.ps1", @"C:\Animals\утка\quack.ps1")] + [InlineData(@"C:\&nimals\утка\qu*ck?.ps1", @"C:\&nimals\утка\qu`*ck`?.ps1")] public void CorrectlyWildcardEscapesPathsNoSpaces(string unescapedPath, string escapedPath) { string extensionEscapedPath = PathUtils.WildcardEscapePath(unescapedPath); @@ -33,17 +33,17 @@ public void CorrectlyWildcardEscapesPathsNoSpaces(string unescapedPath, string e [Theory] [InlineData("DebugTest.ps1", "DebugTest.ps1")] [InlineData("../../DebugTest.ps1", "../../DebugTest.ps1")] - [InlineData("C:\\Users\\me\\Documents\\DebugTest.ps1", "C:\\Users\\me\\Documents\\DebugTest.ps1")] + [InlineData(@"C:\Users\me\Documents\DebugTest.ps1", @"C:\Users\me\Documents\DebugTest.ps1")] [InlineData("/home/me/Documents/weird&folder/script.ps1", "/home/me/Documents/weird&folder/script.ps1")] [InlineData("./path/with some/spaces", "./path/with` some/spaces")] - [InlineData("C:\\path\\with[some]brackets\\file.ps1", "C:\\path\\with`[some`]brackets\\file.ps1")] - [InlineData("C:\\look\\an*\\here.ps1", "C:\\look\\an`*\\here.ps1")] + [InlineData(@"C:\path\with[some]brackets\file.ps1", @"C:\path\with`[some`]brackets\file.ps1")] + [InlineData(@"C:\look\an*\here.ps1", @"C:\look\an`*\here.ps1")] [InlineData("/Users/me/Documents/?here.ps1", "/Users/me/Documents/`?here.ps1")] [InlineData("/Brackets [and s]paces/path.ps1", "/Brackets` `[and` s`]paces/path.ps1")] [InlineData("/CJK chars/脚本/hello.ps1", "/CJK` chars/脚本/hello.ps1")] [InlineData("/CJK chars/脚本/[hello].ps1", "/CJK` chars/脚本/`[hello`].ps1")] - [InlineData("C:\\Animal s\\утка\\quack.ps1", "C:\\Animal` s\\утка\\quack.ps1")] - [InlineData("C:\\&nimals\\утка\\qu*ck?.ps1", "C:\\&nimals\\утка\\qu`*ck`?.ps1")] + [InlineData(@"C:\Animal s\утка\quack.ps1", @"C:\Animal` s\утка\quack.ps1")] + [InlineData(@"C:\&nimals\утка\qu*ck?.ps1", @"C:\&nimals\утка\qu`*ck`?.ps1")] public void CorrectlyWildcardEscapesPathsSpaces(string unescapedPath, string escapedPath) { string extensionEscapedPath = PathUtils.WildcardEscapePath(unescapedPath, escapeSpaces: true); diff --git a/test/PowerShellEditorServices.Test/Session/PsesInternalHostTests.cs b/test/PowerShellEditorServices.Test/Session/PsesInternalHostTests.cs index 76f303681..1e141bbc9 100644 --- a/test/PowerShellEditorServices.Test/Session/PsesInternalHostTests.cs +++ b/test/PowerShellEditorServices.Test/Session/PsesInternalHostTests.cs @@ -223,7 +223,7 @@ await psesHost.ExecutePSCommandAsync( [Theory] [InlineData("")] // Regression test for "unset" path. - [InlineData("C:\\Some\\Bad\\Directory")] // Non-existent directory. + [InlineData(@"C:\Some\Bad\Directory")] // Non-existent directory. [InlineData("testhost.dll")] // Existent file. public async Task CanHandleBadInitialWorkingDirectory(string path) { diff --git a/test/PowerShellEditorServices.Test/Session/ScriptFileTests.cs b/test/PowerShellEditorServices.Test/Session/ScriptFileTests.cs index 4839eacfa..11c27c4cf 100644 --- a/test/PowerShellEditorServices.Test/Session/ScriptFileTests.cs +++ b/test/PowerShellEditorServices.Test/Session/ScriptFileTests.cs @@ -654,7 +654,7 @@ public void DocumentUriReturnsCorrectStringForAbsolutePath() scriptFile = new ScriptFile(DocumentUri.FromFileSystemPath(path), emptyStringReader, PowerShellVersion); Assert.Equal("file:///home/NaomiNagata/projects/Rocinate/Proto%3AMole%3Acule.ps1", scriptFile.DocumentUri); - path = "/home/JamesHolden/projects/Rocinate/Proto:Mole\\cule.ps1"; + path = @"/home/JamesHolden/projects/Rocinate/Proto:Mole\cule.ps1"; scriptFile = new ScriptFile(DocumentUri.FromFileSystemPath(path), emptyStringReader, PowerShellVersion); Assert.Equal("file:///home/JamesHolden/projects/Rocinate/Proto%3AMole%5Ccule.ps1", scriptFile.DocumentUri); } @@ -662,12 +662,13 @@ public void DocumentUriReturnsCorrectStringForAbsolutePath() [Trait("Category", "ScriptFile")] [Theory] - [InlineData("C:\\Users\\me\\Documents\\test.ps1", false)] + [InlineData(@"C:\Users\me\Documents\test.ps1", false)] [InlineData("/Users/me/Documents/test.ps1", false)] [InlineData("vscode-notebook-cell:/Users/me/Documents/test.ps1#0001", true)] [InlineData("https://microsoft.com", true)] [InlineData("Untitled:Untitled-1", true)] - [InlineData("'a log statement' > 'c:\\Users\\me\\Documents\\test.txt'\r\n", false)] + [InlineData(@"'a log statement' > 'c:\Users\me\Documents\test.txt' +", false)] public void IsUntitledFileIsCorrect(string path, bool expected) => Assert.Equal(expected, ScriptFile.IsUntitledPath(path)); } }