From ca987b53c0914cca47a65f8effe45229bca073eb Mon Sep 17 00:00:00 2001 From: Patrick Meinecke Date: Tue, 21 Aug 2018 20:14:57 -0400 Subject: [PATCH 1/5] PSReadLine integration (#672) * Add infrastructure for managing context Adds classes that manage the state of the prompt, nested contexts, and multiple ReadLine implementations of varying complexity. (cherry picked from commit 7ca8b9b11019a73336992f3a337d9b7cd3a83d49) * Console related classes changes Change ReadLine method to call out to PowerShellContext. This lets the PowerShellContext determine which ReadLine implementation to use based on available modules. Also includes some changes to the System.Console proxy classes to account for PSReadLine. (cherry picked from commit 59bfa3b00e2c8562b7f764bb13d2a4b5b8f6322d) * Rewrite command invocation operations for PSRL Refactor PowerShellContext to have a more robust system for tracking the context in which commands are invoked. This is a significant change in that all interactions with the runspace must be done through methods in PowerShellContext. These changes also greatly increase stability. (cherry picked from commit 21e6b5f932a8e2325f1a8dab45200ae1b247c19c) * Rewrite direct SessionStateProxy calls All interactions with the runspace must be done through PowerShellContext now that nested PowerShell instances are encountered frequently. Also fix a bunch of race conditions that were made more obvious with the changes. (cherry picked from commit fa2faba3a8160fa378809cf5e35eae6e59947290) * Pass feature flags to Start-EditorServicesHost * Address feedback and fix travis build error - Address feedback from @bergmeister - Fix a few other similar mistakes I found - Fix travis build failing due to missing documentation comment tag * Fix all tests except ServiceLoadsProfileOnDemand - Fix an issue where intellisense wouldn't finish if PSReadLine was not running - Fix a crash that would occur if the PSHost was not set up for input like the one used in our tests - Fix a compile error when building against PSv3/4 - Fix a hang that occurred when the PromptNest was disposed during a debug session - Fix some XML documentation comment syntax errors * Fix extra new lines outputted after each command Removed a call to WriteOutput where it wasn't required. This was creating extra new lines which failed tests (and obviously didn't look right). * Remove unused field from InvocationEventQueue And also fix spacing between the other fields. * Remove copying of PDB's in build script @rjmholt did a better job of this in a different PR that we can merge into 2.0.0 later. It also doesn't make sense in this PR. * Add AppVeyor tracking to branch 2.0.0 * Fix ambiguous method crash on CoreCLR Simplify delegate creation in PSReadLineProxy and fix the immediate ambiguous method crash the complicated code caused on CoreCLR. * first round of feedback changes * Some more feedback changes * add a bunch of copyright headers I missed * remove KeyAvailable query * Get the latest PSReadLine module installed * Add PSReadLine installation to build script * the file should be downloaded as a .zip * Address remaining feedback * Attempt to fix issue with native apps and input On Unix like platforms some native applications do not work properly if our event subscriber is active. I suspect this is due to PSReadLine querying cursor position prior to checking for events. I believe the cursor position response emitted is being read as input. I've attempted to fix this by hooking into PSHost.NotifyBeginApplication to temporarly remove the event subscriber, and PSHost.NotifyEndApplication to recreate it afterwards. * Revert "Attempt to fix issue with native apps and input" This reverts commit 16824105fff8cb8d2d49839ed4b496efe33bc301. * Fix build failure Use latest PSReadLine (#745) * Use PSRL release and filter out previous betas * Use fully qualified type name for broad PowerShell support * Fix CI module installation --- .gitignore | 1 + PowerShellEditorServices.build.ps1 | 1 + appveyor.yml | 8 +- .../Start-EditorServices.ps1 | 11 +- modules.json | 5 + scripts/travis.ps1 | 9 +- .../EditorServicesHost.cs | 6 +- .../Server/DebugAdapter.cs | 27 +- .../Server/LanguageServer.cs | 9 + .../Console/ConsoleProxy.cs | 95 ++ .../Console/ConsoleReadLine.cs | 56 +- .../Console/IConsoleOperations.cs | 97 ++ .../Console/UnixConsoleOperations.cs | 233 ++++- .../Console/WindowsConsoleOperations.cs | 24 +- .../Debugging/DebugService.cs | 157 +++- .../Language/AstOperations.cs | 115 +-- .../Language/LanguageService.cs | 73 +- .../Session/ExecutionOptions.cs | 33 + .../Session/ExecutionTarget.cs | 28 + .../Session/Host/EditorServicesPSHost.cs | 6 +- .../Host/EditorServicesPSHostUserInterface.cs | 147 +-- .../Session/IPromptContext.cs | 67 ++ .../Session/IVersionSpecificOperations.cs | 7 + .../Session/InvocationEventQueue.cs | 263 ++++++ .../Session/LegacyReadLineContext.cs | 56 ++ .../Session/PSReadLinePromptContext.cs | 205 ++++ .../Session/PSReadLineProxy.cs | 119 +++ .../Session/PipelineExecutionRequest.cs | 80 ++ .../Session/PowerShell3Operations.cs | 22 + .../Session/PowerShell4Operations.cs | 28 + .../Session/PowerShell5Operations.cs | 10 + .../Session/PowerShellContext.cs | 889 +++++++++++++----- .../Session/PromptNest.cs | 564 +++++++++++ .../Session/PromptNestFrame.cs | 137 +++ .../Session/PromptNestFrameType.cs | 21 + .../Session/RunspaceHandle.cs | 7 + .../Session/ThreadController.cs | 131 +++ .../Utility/AsyncLock.cs | 25 + .../Utility/AsyncQueue.cs | 71 +- .../Utility/AsyncUtils.cs | 25 + .../Debugging/DebugServiceTests.cs | 19 +- .../PowerShellContextFactory.cs | 2 +- 42 files changed, 3416 insertions(+), 473 deletions(-) create mode 100644 src/PowerShellEditorServices/Console/ConsoleProxy.cs create mode 100644 src/PowerShellEditorServices/Session/ExecutionTarget.cs create mode 100644 src/PowerShellEditorServices/Session/IPromptContext.cs create mode 100644 src/PowerShellEditorServices/Session/InvocationEventQueue.cs create mode 100644 src/PowerShellEditorServices/Session/LegacyReadLineContext.cs create mode 100644 src/PowerShellEditorServices/Session/PSReadLinePromptContext.cs create mode 100644 src/PowerShellEditorServices/Session/PSReadLineProxy.cs create mode 100644 src/PowerShellEditorServices/Session/PipelineExecutionRequest.cs create mode 100644 src/PowerShellEditorServices/Session/PromptNest.cs create mode 100644 src/PowerShellEditorServices/Session/PromptNestFrame.cs create mode 100644 src/PowerShellEditorServices/Session/PromptNestFrameType.cs create mode 100644 src/PowerShellEditorServices/Session/ThreadController.cs create mode 100644 src/PowerShellEditorServices/Utility/AsyncUtils.cs diff --git a/.gitignore b/.gitignore index 088159d3d..91c573d9c 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ registered_data.ini .dotnet/ module/Plaster module/PSScriptAnalyzer +module/PSReadLine docs/_site/ docs/_repo/ docs/metadata/ diff --git a/PowerShellEditorServices.build.ps1 b/PowerShellEditorServices.build.ps1 index 409eb5ee6..02dddb7fa 100644 --- a/PowerShellEditorServices.build.ps1 +++ b/PowerShellEditorServices.build.ps1 @@ -368,6 +368,7 @@ task RestorePsesModules -After Build { Save-Module @splatParameters } + Write-Host "`n" } diff --git a/appveyor.yml b/appveyor.yml index 4e1d1ba15..62c45d204 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -6,6 +6,7 @@ skip_tags: true branches: only: - master + - 2.0.0 environment: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true # Don't download unneeded packages @@ -13,7 +14,12 @@ environment: install: - ps: | - Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force | Out-Null + Get-Module PowerShellGet,PackageManagement | Remove-Module -Force -Verbose + powershell -Command { Install-Module -Name PowershellGet -MinimumVersion 1.6 -force -confirm:$false -verbose } + powershell -Command { Install-Module -Name PackageManagement -MinimumVersion 1.1.7.0 -Force -Confirm:$false -Verbose } + Import-Module -Name PowerShellGet -MinimumVersion 1.6 -Force + Import-Module -Name PackageManagement -MinimumVersion 1.1.7.0 -Force + Install-PackageProvider -Name NuGet -Force | Out-Null Import-PackageProvider NuGet -Force | Out-Null Set-PSRepository -Name PSGallery -InstallationPolicy Trusted | Out-Null Install-Module InvokeBuild -MaximumVersion 5.1.0 -Scope CurrentUser -Force | Out-Null diff --git a/module/PowerShellEditorServices/Start-EditorServices.ps1 b/module/PowerShellEditorServices/Start-EditorServices.ps1 index 32135d7af..5618759e9 100644 --- a/module/PowerShellEditorServices/Start-EditorServices.ps1 +++ b/module/PowerShellEditorServices/Start-EditorServices.ps1 @@ -349,7 +349,7 @@ try { "status" = "not started"; "languageServiceTransport" = $PSCmdlet.ParameterSetName; "debugServiceTransport" = $PSCmdlet.ParameterSetName; - }; + } # Create the Editor Services host Log "Invoking Start-EditorServicesHost" @@ -368,7 +368,8 @@ try { -BundledModulesPath $BundledModulesPath ` -EnableConsoleRepl:$EnableConsoleRepl.IsPresent ` -DebugServiceOnly:$DebugServiceOnly.IsPresent ` - -WaitForDebugger:$WaitForDebugger.IsPresent + -WaitForDebugger:$WaitForDebugger.IsPresent ` + -FeatureFlags $FeatureFlags break } @@ -392,7 +393,8 @@ try { -BundledModulesPath $BundledModulesPath ` -EnableConsoleRepl:$EnableConsoleRepl.IsPresent ` -DebugServiceOnly:$DebugServiceOnly.IsPresent ` - -WaitForDebugger:$WaitForDebugger.IsPresent + -WaitForDebugger:$WaitForDebugger.IsPresent ` + -FeatureFlags $FeatureFlags Set-PipeFileResult $resultDetails "languageServiceReadPipeName" $LanguageServiceInPipeName Set-PipeFileResult $resultDetails "languageServiceWritePipeName" $LanguageServiceOutPipeName @@ -417,7 +419,8 @@ try { -BundledModulesPath $BundledModulesPath ` -EnableConsoleRepl:$EnableConsoleRepl.IsPresent ` -DebugServiceOnly:$DebugServiceOnly.IsPresent ` - -WaitForDebugger:$WaitForDebugger.IsPresent + -WaitForDebugger:$WaitForDebugger.IsPresent ` + -FeatureFlags $FeatureFlags Set-PipeFileResult $resultDetails "languageServicePipeName" $LanguageServicePipeName Set-PipeFileResult $resultDetails "debugServicePipeName" $DebugServicePipeName diff --git a/modules.json b/modules.json index e4555e5de..621655464 100644 --- a/modules.json +++ b/modules.json @@ -8,5 +8,10 @@ "MinimumVersion":"1.0", "MaximumVersion":"1.99", "AllowPrerelease":false + }, + "PSReadLine":{ + "MinimumVersion":"2.0.0-beta3", + "MaximumVersion":"2.1", + "AllowPrerelease":true } } diff --git a/scripts/travis.ps1 b/scripts/travis.ps1 index 5af67b0dd..51dd4859f 100644 --- a/scripts/travis.ps1 +++ b/scripts/travis.ps1 @@ -1,4 +1,11 @@ - +Get-Module PowerShellGet,PackageManagement | Remove-Module -Force -Verbose +powershell -Command { Install-Module -Name PowershellGet -MinimumVersion 1.6 -Scope CurrentUser -force -confirm:$false -verbose } +powershell -Command { Install-Module -Name PackageManagement -MinimumVersion 1.1.7.0 -Scope CurrentUser -Force -Confirm:$false -Verbose } +Import-Module -Name PowerShellGet -MinimumVersion 1.6 -Force +Import-Module -Name PackageManagement -MinimumVersion 1.1.7.0 -Force +Install-PackageProvider -Name NuGet -Force | Out-Null +Import-PackageProvider NuGet -Force | Out-Null +Set-PSRepository -Name PSGallery -InstallationPolicy Trusted | Out-Null # Install InvokeBuild Install-Module InvokeBuild -MaximumVersion 5.1.0 -Scope CurrentUser -Force diff --git a/src/PowerShellEditorServices.Host/EditorServicesHost.cs b/src/PowerShellEditorServices.Host/EditorServicesHost.cs index 516b0bba9..c723427d4 100644 --- a/src/PowerShellEditorServices.Host/EditorServicesHost.cs +++ b/src/PowerShellEditorServices.Host/EditorServicesHost.cs @@ -378,7 +378,7 @@ private EditorSession CreateSession( bool enableConsoleRepl) { EditorSession editorSession = new EditorSession(this.logger); - PowerShellContext powerShellContext = new PowerShellContext(this.logger); + PowerShellContext powerShellContext = new PowerShellContext(this.logger, this.featureFlags.Contains("PSReadLine")); EditorServicesPSHostUserInterface hostUserInterface = enableConsoleRepl @@ -418,7 +418,9 @@ private EditorSession CreateDebugSession( bool enableConsoleRepl) { EditorSession editorSession = new EditorSession(this.logger); - PowerShellContext powerShellContext = new PowerShellContext(this.logger); + PowerShellContext powerShellContext = new PowerShellContext( + this.logger, + this.featureFlags.Contains("PSReadLine")); EditorServicesPSHostUserInterface hostUserInterface = enableConsoleRepl diff --git a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs index a43f5a7d3..87c21d3ba 100644 --- a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs +++ b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs @@ -112,6 +112,17 @@ protected Task LaunchScript(RequestContext requestContext, string script private async Task OnExecutionCompleted(Task executeTask) { + try + { + await executeTask; + } + catch (Exception e) + { + Logger.Write( + LogLevel.Error, + "Exception occurred while awaiting debug launch task.\n\n" + e.ToString()); + } + Logger.Write(LogLevel.Verbose, "Execution completed, terminating..."); _executionCompleted = true; @@ -462,7 +473,7 @@ protected async Task HandleDisconnectRequest( if (_executionCompleted == false) { _disconnectRequestContext = requestContext; - _editorSession.PowerShellContext.AbortExecution(); + _editorSession.PowerShellContext.AbortExecution(shouldAbortDebugSession: true); if (_isInteractiveDebugSession) { @@ -730,6 +741,20 @@ protected async Task HandleStackTraceRequest( StackFrameDetails[] stackFrames = _editorSession.DebugService.GetStackFrames(); + // Handle a rare race condition where the adapter requests stack frames before they've + // begun building. + if (stackFrames == null) + { + await requestContext.SendResult( + new StackTraceResponseBody + { + StackFrames = new StackFrame[0], + TotalFrames = 0 + }); + + return; + } + List newStackFrames = new List(); int startFrameIndex = stackTraceParams.StartFrame ?? 0; diff --git a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs index 0432fa2ee..33385e959 100644 --- a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs +++ b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs @@ -1653,6 +1653,15 @@ private static async Task DelayThenInvokeDiagnostics( catch (TaskCanceledException) { // If the task is cancelled, exit directly + foreach (var script in filesToAnalyze) + { + await PublishScriptDiagnostics( + script, + script.SyntaxMarkers, + correctionIndex, + eventSender); + } + return; } diff --git a/src/PowerShellEditorServices/Console/ConsoleProxy.cs b/src/PowerShellEditorServices/Console/ConsoleProxy.cs new file mode 100644 index 000000000..bd7488169 --- /dev/null +++ b/src/PowerShellEditorServices/Console/ConsoleProxy.cs @@ -0,0 +1,95 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.PowerShell.EditorServices.Console +{ + /// + /// Provides asynchronous implementations of the API's as well as + /// synchronous implementations that work around platform specific issues. + /// + internal static class ConsoleProxy + { + private static IConsoleOperations s_consoleProxy; + + static ConsoleProxy() + { + // Maybe we should just include the RuntimeInformation package for FullCLR? +#if CoreCLR + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + s_consoleProxy = new WindowsConsoleOperations(); + return; + } + + s_consoleProxy = new UnixConsoleOperations(); +#else + s_consoleProxy = new WindowsConsoleOperations(); +#endif + } + + public static Task ReadKeyAsync(CancellationToken cancellationToken) => + s_consoleProxy.ReadKeyAsync(cancellationToken); + + public static int GetCursorLeft() => + s_consoleProxy.GetCursorLeft(); + + public static int GetCursorLeft(CancellationToken cancellationToken) => + s_consoleProxy.GetCursorLeft(cancellationToken); + + public static Task GetCursorLeftAsync() => + s_consoleProxy.GetCursorLeftAsync(); + + public static Task GetCursorLeftAsync(CancellationToken cancellationToken) => + s_consoleProxy.GetCursorLeftAsync(cancellationToken); + + public static int GetCursorTop() => + s_consoleProxy.GetCursorTop(); + + public static int GetCursorTop(CancellationToken cancellationToken) => + s_consoleProxy.GetCursorTop(cancellationToken); + + public static Task GetCursorTopAsync() => + s_consoleProxy.GetCursorTopAsync(); + + public static Task GetCursorTopAsync(CancellationToken cancellationToken) => + s_consoleProxy.GetCursorTopAsync(cancellationToken); + + /// + /// On Unix platforms this method is sent to PSReadLine as a work around for issues + /// with the System.Console implementation for that platform. Functionally it is the + /// same as System.Console.ReadKey, with the exception that it will not lock the + /// standard input stream. + /// + /// + /// Determines whether to display the pressed key in the console window. + /// true to not display the pressed key; otherwise, false. + /// + /// + /// The that can be used to cancel the request. + /// + /// + /// An object that describes the ConsoleKey constant and Unicode character, if any, + /// that correspond to the pressed console key. The ConsoleKeyInfo object also describes, + /// in a bitwise combination of ConsoleModifiers values, whether one or more Shift, Alt, + /// or Ctrl modifier keys was pressed simultaneously with the console key. + /// + internal static ConsoleKeyInfo UnixReadKey(bool intercept, CancellationToken cancellationToken) + { + try + { + return ((UnixConsoleOperations)s_consoleProxy).ReadKey(intercept, cancellationToken); + } + catch (OperationCanceledException) + { + return default(ConsoleKeyInfo); + } + } + } +} diff --git a/src/PowerShellEditorServices/Console/ConsoleReadLine.cs b/src/PowerShellEditorServices/Console/ConsoleReadLine.cs index 7c518a718..afe5c6b8f 100644 --- a/src/PowerShellEditorServices/Console/ConsoleReadLine.cs +++ b/src/PowerShellEditorServices/Console/ConsoleReadLine.cs @@ -6,7 +6,6 @@ using System.Collections.ObjectModel; using System.Linq; using System.Text; -using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -20,23 +19,11 @@ namespace Microsoft.PowerShell.EditorServices.Console internal class ConsoleReadLine { #region Private Field - private static IConsoleOperations s_consoleProxy; - private PowerShellContext powerShellContext; #endregion #region Constructors - static ConsoleReadLine() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - s_consoleProxy = new WindowsConsoleOperations(); - return; - } - - s_consoleProxy = new UnixConsoleOperations(); - } public ConsoleReadLine(PowerShellContext powerShellContext) { @@ -49,20 +36,20 @@ public ConsoleReadLine(PowerShellContext powerShellContext) public Task ReadCommandLine(CancellationToken cancellationToken) { - return this.ReadLine(true, cancellationToken); + return this.ReadLineAsync(true, cancellationToken); } public Task ReadSimpleLine(CancellationToken cancellationToken) { - return this.ReadLine(false, cancellationToken); + return this.ReadLineAsync(false, cancellationToken); } public async Task ReadSecureLine(CancellationToken cancellationToken) { SecureString secureString = new SecureString(); - int initialPromptRow = Console.CursorTop; - int initialPromptCol = Console.CursorLeft; + int initialPromptRow = await ConsoleProxy.GetCursorTopAsync(cancellationToken); + int initialPromptCol = await ConsoleProxy.GetCursorLeftAsync(cancellationToken); int previousInputLength = 0; Console.TreatControlCAsInput = true; @@ -109,7 +96,8 @@ public async Task ReadSecureLine(CancellationToken cancellationTok } else if (previousInputLength > 0 && currentInputLength < previousInputLength) { - int row = Console.CursorTop, col = Console.CursorLeft; + int row = await ConsoleProxy.GetCursorTopAsync(cancellationToken); + int col = await ConsoleProxy.GetCursorLeftAsync(cancellationToken); // Back up the cursor before clearing the character col--; @@ -141,10 +129,30 @@ public async Task ReadSecureLine(CancellationToken cancellationTok private static async Task ReadKeyAsync(CancellationToken cancellationToken) { - return await s_consoleProxy.ReadKeyAsync(cancellationToken); + return await ConsoleProxy.ReadKeyAsync(cancellationToken); + } + + private async Task ReadLineAsync(bool isCommandLine, CancellationToken cancellationToken) + { + return await this.powerShellContext.InvokeReadLineAsync(isCommandLine, cancellationToken); } - private async Task ReadLine(bool isCommandLine, CancellationToken cancellationToken) + /// + /// Invokes a custom ReadLine method that is similar to but more basic than PSReadLine. + /// This method should be used when PSReadLine is disabled, either by user settings or + /// unsupported PowerShell versions. + /// + /// + /// Indicates whether ReadLine should act like a command line. + /// + /// + /// The cancellation token that will be checked prior to completing the returned task. + /// + /// + /// A task object representing the asynchronus operation. The Result property on + /// the task object returns the user input string. + /// + internal async Task InvokeLegacyReadLineAsync(bool isCommandLine, CancellationToken cancellationToken) { string inputBeforeCompletion = null; string inputAfterCompletion = null; @@ -155,8 +163,8 @@ private async Task ReadLine(bool isCommandLine, CancellationToken cancel StringBuilder inputLine = new StringBuilder(); - int initialCursorCol = Console.CursorLeft; - int initialCursorRow = Console.CursorTop; + int initialCursorCol = await ConsoleProxy.GetCursorLeftAsync(cancellationToken); + int initialCursorRow = await ConsoleProxy.GetCursorTopAsync(cancellationToken); int initialWindowLeft = Console.WindowLeft; int initialWindowTop = Console.WindowTop; @@ -487,8 +495,8 @@ private int CalculateIndexFromCursor( int consoleWidth) { return - ((Console.CursorTop - promptStartRow) * consoleWidth) + - Console.CursorLeft - promptStartCol; + ((ConsoleProxy.GetCursorTop() - promptStartRow) * consoleWidth) + + ConsoleProxy.GetCursorLeft() - promptStartCol; } private void CalculateCursorFromIndex( diff --git a/src/PowerShellEditorServices/Console/IConsoleOperations.cs b/src/PowerShellEditorServices/Console/IConsoleOperations.cs index 721ae8ff7..a5556eda5 100644 --- a/src/PowerShellEditorServices/Console/IConsoleOperations.cs +++ b/src/PowerShellEditorServices/Console/IConsoleOperations.cs @@ -1,3 +1,8 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + using System; using System.Threading; using System.Threading.Tasks; @@ -18,5 +23,97 @@ public interface IConsoleOperations /// A task that will complete with a result of the key pressed by the user. /// Task ReadKeyAsync(CancellationToken cancellationToken); + + /// + /// Obtains the horizontal position of the console cursor. Use this method + /// instead of to avoid triggering + /// pending calls to + /// on Unix platforms. + /// + /// The horizontal position of the console cursor. + int GetCursorLeft(); + + /// + /// Obtains the horizontal position of the console cursor. Use this method + /// instead of to avoid triggering + /// pending calls to + /// on Unix platforms. + /// + /// The to observe. + /// The horizontal position of the console cursor. + int GetCursorLeft(CancellationToken cancellationToken); + + /// + /// Obtains the horizontal position of the console cursor. Use this method + /// instead of to avoid triggering + /// pending calls to + /// on Unix platforms. + /// + /// + /// A representing the asynchronous operation. The + /// property will return the horizontal position + /// of the console cursor. + /// + Task GetCursorLeftAsync(); + + /// + /// Obtains the horizontal position of the console cursor. Use this method + /// instead of to avoid triggering + /// pending calls to + /// on Unix platforms. + /// + /// The to observe. + /// + /// A representing the asynchronous operation. The + /// property will return the horizontal position + /// of the console cursor. + /// + Task GetCursorLeftAsync(CancellationToken cancellationToken); + + /// + /// Obtains the vertical position of the console cursor. Use this method + /// instead of to avoid triggering + /// pending calls to + /// on Unix platforms. + /// + /// The vertical position of the console cursor. + int GetCursorTop(); + + /// + /// Obtains the vertical position of the console cursor. Use this method + /// instead of to avoid triggering + /// pending calls to + /// on Unix platforms. + /// + /// The to observe. + /// The vertical position of the console cursor. + int GetCursorTop(CancellationToken cancellationToken); + + /// + /// Obtains the vertical position of the console cursor. Use this method + /// instead of to avoid triggering + /// pending calls to + /// on Unix platforms. + /// + /// + /// A representing the asynchronous operation. The + /// property will return the vertical position + /// of the console cursor. + /// + Task GetCursorTopAsync(); + + /// + /// Obtains the vertical position of the console cursor. Use this method + /// instead of to avoid triggering + /// pending calls to + /// on Unix platforms. + /// + /// The to observe. + /// + /// A representing the asynchronous operation. The + /// property will return the vertical position + /// of the console cursor. + /// + Task GetCursorTopAsync(CancellationToken cancellationToken); } } diff --git a/src/PowerShellEditorServices/Console/UnixConsoleOperations.cs b/src/PowerShellEditorServices/Console/UnixConsoleOperations.cs index ab5cccfd6..df5ec2460 100644 --- a/src/PowerShellEditorServices/Console/UnixConsoleOperations.cs +++ b/src/PowerShellEditorServices/Console/UnixConsoleOperations.cs @@ -1,19 +1,33 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + using System; using System.Threading; using System.Threading.Tasks; +using Microsoft.PowerShell.EditorServices.Utility; using UnixConsoleEcho; namespace Microsoft.PowerShell.EditorServices.Console { internal class UnixConsoleOperations : IConsoleOperations { - private const int LONG_READ_DELAY = 300; + private const int LongWaitForKeySleepTime = 300; + + private const int ShortWaitForKeyTimeout = 5000; + + private const int ShortWaitForKeySpinUntilSleepTime = 30; + + private static readonly ManualResetEventSlim s_waitHandle = new ManualResetEventSlim(); - private const int SHORT_READ_TIMEOUT = 5000; + private static readonly SemaphoreSlim s_readKeyHandle = AsyncUtils.CreateSimpleLockingSemaphore(); - private static readonly ManualResetEventSlim _waitHandle = new ManualResetEventSlim(); + private static readonly SemaphoreSlim s_stdInHandle = AsyncUtils.CreateSimpleLockingSemaphore(); - private SemaphoreSlim _readKeyHandle = new SemaphoreSlim(1, 1); + private Func WaitForKeyAvailable; + + private Func> WaitForKeyAvailableAsync; internal UnixConsoleOperations() { @@ -21,65 +35,250 @@ internal UnixConsoleOperations() // user has recently (last 5 seconds) pressed a key to avoid preventing // the CPU from entering low power mode. WaitForKeyAvailable = LongWaitForKey; + WaitForKeyAvailableAsync = LongWaitForKeyAsync; + } + + internal ConsoleKeyInfo ReadKey(bool intercept, CancellationToken cancellationToken) + { + s_readKeyHandle.Wait(cancellationToken); + + // On Unix platforms System.Console.ReadKey has an internal lock on stdin. Because + // of this, if a ReadKey call is pending in one thread and in another thread + // Console.CursorLeft is called, both threads block until a key is pressed. + + // To work around this we wait for a key to be pressed before actually calling Console.ReadKey. + // However, any pressed keys during this time will be echoed to the console. To get around + // this we use the UnixConsoleEcho package to disable echo prior to waiting. + InputEcho.Disable(); + try + { + // The WaitForKeyAvailable delegate switches between a long delay between waits and + // a short timeout depending on how recently a key has been pressed. This allows us + // to let the CPU enter low power mode without compromising responsiveness. + while (!WaitForKeyAvailable(cancellationToken)); + } + finally + { + InputEcho.Disable(); + s_readKeyHandle.Release(); + } + + // A key has been pressed, so aquire a lock on our internal stdin handle. This is done + // so any of our calls to cursor position API's do not release ReadKey. + s_stdInHandle.Wait(cancellationToken); + try + { + return System.Console.ReadKey(intercept); + } + finally + { + s_stdInHandle.Release(); + } } public async Task ReadKeyAsync(CancellationToken cancellationToken) { - await _readKeyHandle.WaitAsync(cancellationToken); + await s_readKeyHandle.WaitAsync(cancellationToken); // I tried to replace this library with a call to `stty -echo`, but unfortunately // the library also sets up allowing backspace to trigger `Console.KeyAvailable`. InputEcho.Disable(); try { - while (!await WaitForKeyAvailable(cancellationToken)); + while (!await WaitForKeyAvailableAsync(cancellationToken)); } finally { InputEcho.Enable(); - _readKeyHandle.Release(); + s_readKeyHandle.Release(); + } + + await s_stdInHandle.WaitAsync(cancellationToken); + try + { + return System.Console.ReadKey(intercept: true); + } + finally + { + s_stdInHandle.Release(); } + } - return System.Console.ReadKey(intercept: true); + public int GetCursorLeft() + { + return GetCursorLeft(CancellationToken.None); } - private Func> WaitForKeyAvailable; + public int GetCursorLeft(CancellationToken cancellationToken) + { + s_stdInHandle.Wait(cancellationToken); + try + { + return System.Console.CursorLeft; + } + finally + { + s_stdInHandle.Release(); + } + } + + public async Task GetCursorLeftAsync() + { + return await GetCursorLeftAsync(CancellationToken.None); + } - private async Task LongWaitForKey(CancellationToken cancellationToken) + public async Task GetCursorLeftAsync(CancellationToken cancellationToken) { - while (!System.Console.KeyAvailable) + await s_stdInHandle.WaitAsync(cancellationToken); + try + { + return System.Console.CursorLeft; + } + finally { - await Task.Delay(LONG_READ_DELAY, cancellationToken); + s_stdInHandle.Release(); } + } + + public int GetCursorTop() + { + return GetCursorTop(CancellationToken.None); + } + public int GetCursorTop(CancellationToken cancellationToken) + { + s_stdInHandle.Wait(cancellationToken); + try + { + return System.Console.CursorTop; + } + finally + { + s_stdInHandle.Release(); + } + } + + public async Task GetCursorTopAsync() + { + return await GetCursorTopAsync(CancellationToken.None); + } + + public async Task GetCursorTopAsync(CancellationToken cancellationToken) + { + await s_stdInHandle.WaitAsync(cancellationToken); + try + { + return System.Console.CursorTop; + } + finally + { + s_stdInHandle.Release(); + } + } + + private bool LongWaitForKey(CancellationToken cancellationToken) + { + // Wait for a key to be buffered (in other words, wait for Console.KeyAvailable to become + // true) with a long delay between checks. + while (!IsKeyAvailable(cancellationToken)) + { + s_waitHandle.Wait(LongWaitForKeySleepTime, cancellationToken); + } + + // As soon as a key is buffered, return true and switch the wait logic to be more + // responsive, but also more expensive. WaitForKeyAvailable = ShortWaitForKey; return true; } - private async Task ShortWaitForKey(CancellationToken cancellationToken) + private async Task LongWaitForKeyAsync(CancellationToken cancellationToken) + { + while (!await IsKeyAvailableAsync(cancellationToken)) + { + await Task.Delay(LongWaitForKeySleepTime, cancellationToken); + } + + WaitForKeyAvailableAsync = ShortWaitForKeyAsync; + return true; + } + + private bool ShortWaitForKey(CancellationToken cancellationToken) { - if (await SpinUntilKeyAvailable(SHORT_READ_TIMEOUT, cancellationToken)) + // Check frequently for a new key to be buffered. + if (SpinUntilKeyAvailable(ShortWaitForKeyTimeout, cancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); return true; } + // If the user has not pressed a key before the end of the SpinUntil timeout then + // the user is idle and we can switch back to long delays between KeyAvailable checks. cancellationToken.ThrowIfCancellationRequested(); WaitForKeyAvailable = LongWaitForKey; return false; } - private async Task SpinUntilKeyAvailable(int millisecondsTimeout, CancellationToken cancellationToken) + private async Task ShortWaitForKeyAsync(CancellationToken cancellationToken) + { + if (await SpinUntilKeyAvailableAsync(ShortWaitForKeyTimeout, cancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + return true; + } + + cancellationToken.ThrowIfCancellationRequested(); + WaitForKeyAvailableAsync = LongWaitForKeyAsync; + return false; + } + + private bool SpinUntilKeyAvailable(int millisecondsTimeout, CancellationToken cancellationToken) + { + return SpinWait.SpinUntil( + () => + { + s_waitHandle.Wait(ShortWaitForKeySpinUntilSleepTime, cancellationToken); + return IsKeyAvailable(cancellationToken); + }, + millisecondsTimeout); + } + + private async Task SpinUntilKeyAvailableAsync(int millisecondsTimeout, CancellationToken cancellationToken) { return await Task.Factory.StartNew( () => SpinWait.SpinUntil( () => { // The wait handle is never set, it's just used to enable cancelling the wait. - _waitHandle.Wait(30, cancellationToken); - return System.Console.KeyAvailable || cancellationToken.IsCancellationRequested; + s_waitHandle.Wait(ShortWaitForKeySpinUntilSleepTime, cancellationToken); + return IsKeyAvailable(cancellationToken); }, millisecondsTimeout)); } + + private bool IsKeyAvailable(CancellationToken cancellationToken) + { + s_stdInHandle.Wait(cancellationToken); + try + { + return System.Console.KeyAvailable; + } + finally + { + s_stdInHandle.Release(); + } + } + + private async Task IsKeyAvailableAsync(CancellationToken cancellationToken) + { + await s_stdInHandle.WaitAsync(cancellationToken); + try + { + return System.Console.KeyAvailable; + } + finally + { + s_stdInHandle.Release(); + } + } } } diff --git a/src/PowerShellEditorServices/Console/WindowsConsoleOperations.cs b/src/PowerShellEditorServices/Console/WindowsConsoleOperations.cs index 3158c87c4..86c543123 100644 --- a/src/PowerShellEditorServices/Console/WindowsConsoleOperations.cs +++ b/src/PowerShellEditorServices/Console/WindowsConsoleOperations.cs @@ -1,6 +1,12 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + using System; using System.Threading; using System.Threading.Tasks; +using Microsoft.PowerShell.EditorServices.Utility; namespace Microsoft.PowerShell.EditorServices.Console { @@ -8,7 +14,23 @@ internal class WindowsConsoleOperations : IConsoleOperations { private ConsoleKeyInfo? _bufferedKey; - private SemaphoreSlim _readKeyHandle = new SemaphoreSlim(1, 1); + private SemaphoreSlim _readKeyHandle = AsyncUtils.CreateSimpleLockingSemaphore(); + + public int GetCursorLeft() => System.Console.CursorLeft; + + public int GetCursorLeft(CancellationToken cancellationToken) => System.Console.CursorLeft; + + public Task GetCursorLeftAsync() => Task.FromResult(System.Console.CursorLeft); + + public Task GetCursorLeftAsync(CancellationToken cancellationToken) => Task.FromResult(System.Console.CursorLeft); + + public int GetCursorTop() => System.Console.CursorTop; + + public int GetCursorTop(CancellationToken cancellationToken) => System.Console.CursorTop; + + public Task GetCursorTopAsync() => Task.FromResult(System.Console.CursorTop); + + public Task GetCursorTopAsync(CancellationToken cancellationToken) => Task.FromResult(System.Console.CursorTop); public async Task ReadKeyAsync(CancellationToken cancellationToken) { diff --git a/src/PowerShellEditorServices/Debugging/DebugService.cs b/src/PowerShellEditorServices/Debugging/DebugService.cs index 0eae58dd2..8226ab9be 100644 --- a/src/PowerShellEditorServices/Debugging/DebugService.cs +++ b/src/PowerShellEditorServices/Debugging/DebugService.cs @@ -15,6 +15,7 @@ using Microsoft.PowerShell.EditorServices.Utility; using Microsoft.PowerShell.EditorServices.Session; using Microsoft.PowerShell.EditorServices.Session.Capabilities; +using System.Threading; namespace Microsoft.PowerShell.EditorServices { @@ -47,6 +48,7 @@ public class DebugService private static int breakpointHitCounter = 0; + private SemaphoreSlim debugInfoHandle = AsyncUtils.CreateSimpleLockingSemaphore(); #endregion #region Properties @@ -350,7 +352,7 @@ public void Break() /// public void Abort() { - this.powerShellContext.AbortExecution(); + this.powerShellContext.AbortExecution(shouldAbortDebugSession: true); } /// @@ -362,33 +364,40 @@ public void Abort() public VariableDetailsBase[] GetVariables(int variableReferenceId) { VariableDetailsBase[] childVariables; - - if ((variableReferenceId < 0) || (variableReferenceId >= this.variables.Count)) + this.debugInfoHandle.Wait(); + try { - logger.Write(LogLevel.Warning, $"Received request for variableReferenceId {variableReferenceId} that is out of range of valid indices."); - return new VariableDetailsBase[0]; - } + if ((variableReferenceId < 0) || (variableReferenceId >= this.variables.Count)) + { + logger.Write(LogLevel.Warning, $"Received request for variableReferenceId {variableReferenceId} that is out of range of valid indices."); + return new VariableDetailsBase[0]; + } - VariableDetailsBase parentVariable = this.variables[variableReferenceId]; - if (parentVariable.IsExpandable) - { - childVariables = parentVariable.GetChildren(this.logger); - foreach (var child in childVariables) + VariableDetailsBase parentVariable = this.variables[variableReferenceId]; + if (parentVariable.IsExpandable) { - // Only add child if it hasn't already been added. - if (child.Id < 0) + childVariables = parentVariable.GetChildren(this.logger); + foreach (var child in childVariables) { - child.Id = this.nextVariableId++; - this.variables.Add(child); + // Only add child if it hasn't already been added. + if (child.Id < 0) + { + child.Id = this.nextVariableId++; + this.variables.Add(child); + } } } + else + { + childVariables = new VariableDetailsBase[0]; + } + + return childVariables; } - else + finally { - childVariables = new VariableDetailsBase[0]; + this.debugInfoHandle.Release(); } - - return childVariables; } /// @@ -410,7 +419,18 @@ public VariableDetailsBase GetVariableFromExpression(string variableExpression, string[] variablePathParts = variableExpression.Split('.'); VariableDetailsBase resolvedVariable = null; - IEnumerable variableList = this.variables; + IEnumerable variableList; + + // Ensure debug info isn't currently being built. + this.debugInfoHandle.Wait(); + try + { + variableList = this.variables; + } + finally + { + this.debugInfoHandle.Release(); + } foreach (var variableName in variablePathParts) { @@ -491,9 +511,18 @@ await this.powerShellContext.ExecuteCommand( // OK, now we have a PS object from the supplied value string (expression) to assign to a variable. // Get the variable referenced by variableContainerReferenceId and variable name. - VariableContainerDetails variableContainer = (VariableContainerDetails)this.variables[variableContainerReferenceId]; - VariableDetailsBase variable = variableContainer.Children[name]; + VariableContainerDetails variableContainer = null; + await this.debugInfoHandle.WaitAsync(); + try + { + variableContainer = (VariableContainerDetails)this.variables[variableContainerReferenceId]; + } + finally + { + this.debugInfoHandle.Release(); + } + VariableDetailsBase variable = variableContainer.Children[name]; // Determine scope in which the variable lives. This is required later for the call to Get-Variable -Scope. string scope = null; if (variableContainerReferenceId == this.scriptScopeVariables.Id) @@ -507,9 +536,10 @@ await this.powerShellContext.ExecuteCommand( else { // Determine which stackframe's local scope the variable is in. - for (int i = 0; i < this.stackFrameDetails.Length; i++) + StackFrameDetails[] stackFrames = await this.GetStackFramesAsync(); + for (int i = 0; i < stackFrames.Length; i++) { - var stackFrame = this.stackFrameDetails[i]; + var stackFrame = stackFrames[i]; if (stackFrame.LocalVariables.ContainsVariable(variable.Id)) { scope = i.ToString(); @@ -637,7 +667,54 @@ await this.powerShellContext.ExecuteScriptString( /// public StackFrameDetails[] GetStackFrames() { - return this.stackFrameDetails; + this.debugInfoHandle.Wait(); + try + { + return this.stackFrameDetails; + } + finally + { + this.debugInfoHandle.Release(); + } + } + + internal StackFrameDetails[] GetStackFrames(CancellationToken cancellationToken) + { + this.debugInfoHandle.Wait(cancellationToken); + try + { + return this.stackFrameDetails; + } + finally + { + this.debugInfoHandle.Release(); + } + } + + internal async Task GetStackFramesAsync() + { + await this.debugInfoHandle.WaitAsync(); + try + { + return this.stackFrameDetails; + } + finally + { + this.debugInfoHandle.Release(); + } + } + + internal async Task GetStackFramesAsync(CancellationToken cancellationToken) + { + await this.debugInfoHandle.WaitAsync(cancellationToken); + try + { + return this.stackFrameDetails; + } + finally + { + this.debugInfoHandle.Release(); + } } /// @@ -648,8 +725,9 @@ public StackFrameDetails[] GetStackFrames() /// The list of VariableScope instances which describe the available variable scopes. public VariableScope[] GetVariableScopes(int stackFrameId) { - int localStackFrameVariableId = this.stackFrameDetails[stackFrameId].LocalVariables.Id; - int autoVariablesId = this.stackFrameDetails[stackFrameId].AutoVariables.Id; + var stackFrames = this.GetStackFrames(); + int localStackFrameVariableId = stackFrames[stackFrameId].LocalVariables.Id; + int autoVariablesId = stackFrames[stackFrameId].AutoVariables.Id; return new VariableScope[] { @@ -709,16 +787,24 @@ private async Task ClearCommandBreakpoints() private async Task FetchStackFramesAndVariables(string scriptNameOverride) { - this.nextVariableId = VariableDetailsBase.FirstVariableId; - this.variables = new List(); + await this.debugInfoHandle.WaitAsync(); + try + { + this.nextVariableId = VariableDetailsBase.FirstVariableId; + this.variables = new List(); - // Create a dummy variable for index 0, should never see this. - this.variables.Add(new VariableDetails("Dummy", null)); + // Create a dummy variable for index 0, should never see this. + this.variables.Add(new VariableDetails("Dummy", null)); - // Must retrieve global/script variales before stack frame variables - // as we check stack frame variables against globals. - await FetchGlobalAndScriptVariables(); - await FetchStackFrames(scriptNameOverride); + // Must retrieve global/script variales before stack frame variables + // as we check stack frame variables against globals. + await FetchGlobalAndScriptVariables(); + await FetchStackFrames(scriptNameOverride); + } + finally + { + this.debugInfoHandle.Release(); + } } private async Task FetchGlobalAndScriptVariables() @@ -851,6 +937,7 @@ private async Task FetchStackFrames(string scriptNameOverride) var results = await this.powerShellContext.ExecuteCommand(psCommand); var callStackFrames = results.ToArray(); + this.stackFrameDetails = new StackFrameDetails[callStackFrames.Length]; for (int i = 0; i < callStackFrames.Length; i++) diff --git a/src/PowerShellEditorServices/Language/AstOperations.cs b/src/PowerShellEditorServices/Language/AstOperations.cs index 9076b1cb9..218462c1c 100644 --- a/src/PowerShellEditorServices/Language/AstOperations.cs +++ b/src/PowerShellEditorServices/Language/AstOperations.cs @@ -17,6 +17,7 @@ namespace Microsoft.PowerShell.EditorServices { using System.Management.Automation; + using System.Management.Automation.Language; /// /// Provides common operations for the syntax tree of a parsed script. @@ -29,6 +30,8 @@ internal static class AstOperations .GetType("System.Management.Automation.Language.InternalScriptPosition") .GetMethod("CloneWithNewOffset", BindingFlags.Instance | BindingFlags.NonPublic); + private static readonly SemaphoreSlim s_completionHandle = AsyncUtils.CreateSimpleLockingSemaphore(); + /// /// Gets completions for the symbol found in the Ast at /// the given file offset. @@ -61,74 +64,80 @@ static public async Task GetCompletions( ILogger logger, CancellationToken cancellationToken) { + if (!s_completionHandle.Wait(0)) + { + return null; + } - IScriptPosition cursorPosition = (IScriptPosition)s_extentCloneWithNewOffset.Invoke( + try + { + IScriptPosition cursorPosition = (IScriptPosition)s_extentCloneWithNewOffset.Invoke( scriptAst.Extent.StartScriptPosition, new object[] { fileOffset }); - logger.Write( - LogLevel.Verbose, - string.Format( - "Getting completions at offset {0} (line: {1}, column: {2})", - fileOffset, - cursorPosition.LineNumber, - cursorPosition.ColumnNumber)); + logger.Write( + LogLevel.Verbose, + string.Format( + "Getting completions at offset {0} (line: {1}, column: {2})", + fileOffset, + cursorPosition.LineNumber, + cursorPosition.ColumnNumber)); - CommandCompletion commandCompletion = null; - if (powerShellContext.IsDebuggerStopped) - { - PSCommand command = new PSCommand(); - command.AddCommand("TabExpansion2"); - command.AddParameter("Ast", scriptAst); - command.AddParameter("Tokens", currentTokens); - command.AddParameter("PositionOfCursor", cursorPosition); - command.AddParameter("Options", null); - - PSObject outputObject = - (await powerShellContext.ExecuteCommand(command, false, false)) - .FirstOrDefault(); - - if (outputObject != null) + if (!powerShellContext.IsAvailable) { - ErrorRecord errorRecord = outputObject.BaseObject as ErrorRecord; - if (errorRecord != null) - { - logger.WriteException( - "Encountered an error while invoking TabExpansion2 in the debugger", - errorRecord.Exception); - } - else + return null; + } + + var stopwatch = new Stopwatch(); + + // If the current runspace is out of process we can use + // CommandCompletion.CompleteInput because PSReadLine won't be taking up the + // main runspace. + if (powerShellContext.IsCurrentRunspaceOutOfProcess()) + { + using (RunspaceHandle runspaceHandle = await powerShellContext.GetRunspaceHandle(cancellationToken)) + using (PowerShell powerShell = PowerShell.Create()) { - commandCompletion = outputObject.BaseObject as CommandCompletion; + powerShell.Runspace = runspaceHandle.Runspace; + stopwatch.Start(); + try + { + return CommandCompletion.CompleteInput( + scriptAst, + currentTokens, + cursorPosition, + options: null, + powershell: powerShell); + } + finally + { + stopwatch.Stop(); + logger.Write(LogLevel.Verbose, $"IntelliSense completed in {stopwatch.ElapsedMilliseconds}ms."); + } } } - } - else if (powerShellContext.CurrentRunspace.Runspace.RunspaceAvailability == - RunspaceAvailability.Available) - { - using (RunspaceHandle runspaceHandle = await powerShellContext.GetRunspaceHandle(cancellationToken)) - using (PowerShell powerShell = PowerShell.Create()) - { - powerShell.Runspace = runspaceHandle.Runspace; - Stopwatch stopwatch = new Stopwatch(); - stopwatch.Start(); - - commandCompletion = - CommandCompletion.CompleteInput( + CommandCompletion commandCompletion = null; + await powerShellContext.InvokeOnPipelineThread( + pwsh => + { + stopwatch.Start(); + commandCompletion = CommandCompletion.CompleteInput( scriptAst, currentTokens, cursorPosition, - null, - powerShell); + options: null, + powershell: pwsh); + }); + stopwatch.Stop(); + logger.Write(LogLevel.Verbose, $"IntelliSense completed in {stopwatch.ElapsedMilliseconds}ms."); - stopwatch.Stop(); - - logger.Write(LogLevel.Verbose, $"IntelliSense completed in {stopwatch.ElapsedMilliseconds}ms."); - } + return commandCompletion; + } + finally + { + s_completionHandle.Release(); } - - return commandCompletion; } /// diff --git a/src/PowerShellEditorServices/Language/LanguageService.cs b/src/PowerShellEditorServices/Language/LanguageService.cs index d5c58ea83..05c47bea3 100644 --- a/src/PowerShellEditorServices/Language/LanguageService.cs +++ b/src/PowerShellEditorServices/Language/LanguageService.cs @@ -40,6 +40,8 @@ public class LanguageService private readonly IDocumentSymbolProvider[] _documentSymbolProviders; + private readonly SemaphoreSlim _aliasHandle = AsyncUtils.CreateSimpleLockingSemaphore(); + private bool _areAliasesLoaded; private CompletionResults _mostRecentCompletions; @@ -353,26 +355,35 @@ public async Task FindReferencesOfSymbol( foreach (object fileName in fileMap.Keys) { var file = (ScriptFile)fileMap[fileName]; + IEnumerable symbolReferencesinFile; + await _aliasHandle.WaitAsync(); + try + { - IEnumerable references = AstOperations.FindReferencesOfSymbol( - file.ScriptAst, - foundSymbol, - _cmdletToAliasDictionary, - _aliasToCmdletDictionary); + IEnumerable references = AstOperations.FindReferencesOfSymbol( + file.ScriptAst, + foundSymbol, + _cmdletToAliasDictionary, + _aliasToCmdletDictionary); - foreach (SymbolReference reference in references) - { - try - { - reference.SourceLine = file.GetLine(reference.ScriptRegion.StartLineNumber); - } - catch (ArgumentOutOfRangeException e) + foreach (SymbolReference reference in references) { - reference.SourceLine = string.Empty; - _logger.WriteException("Found reference is out of range in script file", e); + try + { + reference.SourceLine = file.GetLine(reference.ScriptRegion.StartLineNumber); + } + catch (ArgumentOutOfRangeException e) + { + reference.SourceLine = string.Empty; + _logger.WriteException("Found reference is out of range in script file", e); + } + reference.FilePath = file.FilePath; + symbolReferences.Add(reference); } - reference.FilePath = file.FilePath; - symbolReferences.Add(reference); + } + finally + { + _aliasHandle.Release(); } } @@ -724,19 +735,31 @@ private async Task GetAliases() return; } + await _aliasHandle.WaitAsync(); try { - RunspaceHandle runspaceHandle = - await _powerShellContext.GetRunspaceHandle( - new CancellationTokenSource(DefaultWaitTimeoutMilliseconds).Token); - - CommandInvocationIntrinsics invokeCommand = runspaceHandle.Runspace.SessionStateProxy.InvokeCommand; - IEnumerable aliases = invokeCommand.GetCommands("*", CommandTypes.Alias, true); + if (_powerShellContext.IsCurrentRunspaceOutOfProcess()) + { + _areAliasesLoaded = true; + return; + } - runspaceHandle.Dispose(); + var aliases = await _powerShellContext.ExecuteCommand( + new PSCommand() + .AddCommand("Microsoft.PowerShell.Core\\Get-Command") + .AddParameter("CommandType", CommandTypes.Alias), + sendOutputToHost: false, + sendErrorToHost: false); foreach (AliasInfo aliasInfo in aliases) { + // Using Get-Command will obtain aliases from modules not yet loaded, + // these aliases will not have a definition. + if (string.IsNullOrEmpty(aliasInfo.Definition)) + { + continue; + } + if (!_cmdletToAliasDictionary.ContainsKey(aliasInfo.Definition)) { _cmdletToAliasDictionary.Add(aliasInfo.Definition, new List { aliasInfo.Name }); @@ -764,6 +787,10 @@ await _powerShellContext.GetRunspaceHandle( { // The wait for a RunspaceHandle has timed out, skip aliases for now } + finally + { + _aliasHandle.Release(); + } } private ScriptFile[] GetBuiltinCommandScriptFiles( diff --git a/src/PowerShellEditorServices/Session/ExecutionOptions.cs b/src/PowerShellEditorServices/Session/ExecutionOptions.cs index 3372c7556..a1071606f 100644 --- a/src/PowerShellEditorServices/Session/ExecutionOptions.cs +++ b/src/PowerShellEditorServices/Session/ExecutionOptions.cs @@ -10,6 +10,8 @@ namespace Microsoft.PowerShell.EditorServices /// public class ExecutionOptions { + private bool? _shouldExecuteInOriginalRunspace; + #region Properties /// @@ -38,6 +40,36 @@ public class ExecutionOptions /// public bool InterruptCommandPrompt { get; set; } + /// + /// Gets or sets a value indicating whether the text of the command + /// should be written to the host as if it was ran interactively. + /// + public bool WriteInputToHost { get; set; } + + /// + /// Gets or sets a value indicating whether the command to + /// be executed is a console input prompt, such as the + /// PSConsoleHostReadLine function. + /// + internal bool IsReadLine { get; set; } + + /// + /// Gets or sets a value indicating whether the command should + /// be invoked in the original runspace. In the majority of cases + /// this should remain unset. + /// + internal bool ShouldExecuteInOriginalRunspace + { + get + { + return _shouldExecuteInOriginalRunspace ?? IsReadLine; + } + set + { + _shouldExecuteInOriginalRunspace = value; + } + } + #endregion #region Constructors @@ -50,6 +82,7 @@ public ExecutionOptions() { this.WriteOutputToHost = true; this.WriteErrorsToHost = true; + this.WriteInputToHost = false; this.AddToHistory = false; this.InterruptCommandPrompt = false; } diff --git a/src/PowerShellEditorServices/Session/ExecutionTarget.cs b/src/PowerShellEditorServices/Session/ExecutionTarget.cs new file mode 100644 index 000000000..70ec3cb6f --- /dev/null +++ b/src/PowerShellEditorServices/Session/ExecutionTarget.cs @@ -0,0 +1,28 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +namespace Microsoft.PowerShell.EditorServices.Session +{ + /// + /// Represents the different API's available for executing commands. + /// + internal enum ExecutionTarget + { + /// + /// Indicates that the command should be invoked through the PowerShell debugger. + /// + Debugger, + + /// + /// Indicates that the command should be invoked via an instance of the PowerShell class. + /// + PowerShell, + + /// + /// Indicates that the command should be invoked through the PowerShell engine's event manager. + /// + InvocationEvent + } +} diff --git a/src/PowerShellEditorServices/Session/Host/EditorServicesPSHost.cs b/src/PowerShellEditorServices/Session/Host/EditorServicesPSHost.cs index 33925f044..7c32acdb3 100644 --- a/src/PowerShellEditorServices/Session/Host/EditorServicesPSHost.cs +++ b/src/PowerShellEditorServices/Session/Host/EditorServicesPSHost.cs @@ -26,6 +26,7 @@ public class EditorServicesPSHost : PSHost, IHostSupportsInteractiveSession private Guid instanceId = Guid.NewGuid(); private EditorServicesPSHostUserInterface hostUserInterface; private IHostSupportsInteractiveSession hostSupportsInteractiveSession; + private PowerShellContext powerShellContext; #endregion @@ -55,6 +56,7 @@ public EditorServicesPSHost( this.hostDetails = hostDetails; this.hostUserInterface = hostUserInterface; this.hostSupportsInteractiveSession = powerShellContext; + this.powerShellContext = powerShellContext; } #endregion @@ -251,7 +253,7 @@ public override PSHostUserInterface UI /// public override void EnterNestedPrompt() { - Logger.Write(LogLevel.Verbose, "EnterNestedPrompt() called."); + this.powerShellContext.EnterNestedPrompt(); } /// @@ -259,7 +261,7 @@ public override void EnterNestedPrompt() /// public override void ExitNestedPrompt() { - Logger.Write(LogLevel.Verbose, "ExitNestedPrompt() called."); + this.powerShellContext.ExitNestedPrompt(); } /// diff --git a/src/PowerShellEditorServices/Session/Host/EditorServicesPSHostUserInterface.cs b/src/PowerShellEditorServices/Session/Host/EditorServicesPSHostUserInterface.cs index 2aceaaf10..2cbf29365 100644 --- a/src/PowerShellEditorServices/Session/Host/EditorServicesPSHostUserInterface.cs +++ b/src/PowerShellEditorServices/Session/Host/EditorServicesPSHostUserInterface.cs @@ -112,7 +112,10 @@ public EditorServicesPSHostUserInterface( #region Public Methods - void IHostInput.StartCommandLoop() + /// + /// Starts the host's interactive command loop. + /// + public void StartCommandLoop() { if (!this.IsCommandLoopRunning) { @@ -121,7 +124,10 @@ void IHostInput.StartCommandLoop() } } - void IHostInput.StopCommandLoop() + /// + /// Stops the host's interactive command loop. + /// + public void StopCommandLoop() { if (this.IsCommandLoopRunning) { @@ -636,10 +642,28 @@ public Collection PromptForChoice( #region Private Methods - private async Task WritePromptStringToHost() + private Coordinates lastPromptLocation; + + private async Task WritePromptStringToHost(CancellationToken cancellationToken) { + try + { + if (this.lastPromptLocation != null && + this.lastPromptLocation.X == await ConsoleProxy.GetCursorLeftAsync(cancellationToken) && + this.lastPromptLocation.Y == await ConsoleProxy.GetCursorTopAsync(cancellationToken)) + { + return; + } + } + // When output is redirected (like when running tests) attempting to get + // the cursor position will throw. + catch (System.IO.IOException) + { + } + PSCommand promptCommand = new PSCommand().AddScript("prompt"); + cancellationToken.ThrowIfCancellationRequested(); string promptString = (await this.powerShellContext.ExecuteCommand(promptCommand, false, false)) .Select(pso => pso.BaseObject) @@ -669,8 +693,13 @@ private async Task WritePromptStringToHost() promptString); } + cancellationToken.ThrowIfCancellationRequested(); + // Write the prompt string this.WriteOutput(promptString, false); + this.lastPromptLocation = new Coordinates( + await ConsoleProxy.GetCursorLeftAsync(cancellationToken), + await ConsoleProxy.GetCursorTopAsync(cancellationToken)); } private void WriteDebuggerBanner(DebuggerStopEventArgs eventArgs) @@ -707,14 +736,23 @@ private void WriteDebuggerBanner(DebuggerStopEventArgs eventArgs) private async Task StartReplLoop(CancellationToken cancellationToken) { - do + while (!cancellationToken.IsCancellationRequested) { string commandString = null; + int originalCursorTop = 0; - await this.WritePromptStringToHost(); + try + { + await this.WritePromptStringToHost(cancellationToken); + } + catch (OperationCanceledException) + { + break; + } try { + originalCursorTop = await ConsoleProxy.GetCursorTopAsync(cancellationToken); commandString = await this.ReadCommandLine(cancellationToken); } catch (PipelineStoppedException) @@ -739,29 +777,29 @@ private async Task StartReplLoop(CancellationToken cancellationToken) Logger.WriteException("Caught exception while reading command line", e); } - - if (commandString != null) + finally { - if (!string.IsNullOrWhiteSpace(commandString)) - { - var unusedTask = - this.powerShellContext - .ExecuteScriptString( - commandString, - false, - true, - true) - .ConfigureAwait(false); - - break; - } - else + if (!cancellationToken.IsCancellationRequested && + originalCursorTop == await ConsoleProxy.GetCursorTopAsync(cancellationToken)) { - this.WriteOutput(string.Empty); + this.WriteLine(); } } + + if (!string.IsNullOrWhiteSpace(commandString)) + { + var unusedTask = + this.powerShellContext + .ExecuteScriptString( + commandString, + writeInputToHost: false, + writeOutputToHost: true, + addToHistory: true) + .ConfigureAwait(continueOnCapturedContext: false); + + break; + } } - while (!cancellationToken.IsCancellationRequested); } private InputPromptHandler CreateInputPromptHandler() @@ -856,6 +894,12 @@ private void WaitForPromptCompletion( private void PowerShellContext_DebuggerStop(object sender, System.Management.Automation.DebuggerStopEventArgs e) { + if (!this.IsCommandLoopRunning) + { + StartCommandLoop(); + return; + } + // Cancel any existing prompt first this.CancelCommandPrompt(); @@ -871,45 +915,42 @@ private void PowerShellContext_DebuggerResumed(object sender, System.Management. private void PowerShellContext_ExecutionStatusChanged(object sender, ExecutionStatusChangedEventArgs eventArgs) { // The command loop should only be manipulated if it's already started - if (this.IsCommandLoopRunning) + if (eventArgs.ExecutionStatus == ExecutionStatus.Aborted) { - if (eventArgs.ExecutionStatus == ExecutionStatus.Aborted) + // When aborted, cancel any lingering prompts + if (this.activePromptHandler != null) { - // When aborted, cancel any lingering prompts - if (this.activePromptHandler != null) - { - this.activePromptHandler.CancelPrompt(); - this.WriteOutput(string.Empty); - } + this.activePromptHandler.CancelPrompt(); + this.WriteOutput(string.Empty); } - else if ( - eventArgs.ExecutionOptions.WriteOutputToHost || - eventArgs.ExecutionOptions.InterruptCommandPrompt) + } + else if ( + eventArgs.ExecutionOptions.WriteOutputToHost || + eventArgs.ExecutionOptions.InterruptCommandPrompt) + { + // Any command which writes output to the host will affect + // the display of the prompt + if (eventArgs.ExecutionStatus != ExecutionStatus.Running) { - // Any command which writes output to the host will affect - // the display of the prompt - if (eventArgs.ExecutionStatus != ExecutionStatus.Running) - { - // Execution has completed, start the input prompt - this.ShowCommandPrompt(); - } - else - { - // A new command was started, cancel the input prompt - this.CancelCommandPrompt(); - this.WriteOutput(string.Empty); - } + // Execution has completed, start the input prompt + this.ShowCommandPrompt(); + StartCommandLoop(); } - else if ( - eventArgs.ExecutionOptions.WriteErrorsToHost && - (eventArgs.ExecutionStatus == ExecutionStatus.Failed || - eventArgs.HadErrors)) + else { + // A new command was started, cancel the input prompt + StopCommandLoop(); this.CancelCommandPrompt(); - this.WriteOutput(string.Empty); - this.ShowCommandPrompt(); } } + else if ( + eventArgs.ExecutionOptions.WriteErrorsToHost && + (eventArgs.ExecutionStatus == ExecutionStatus.Failed || + eventArgs.HadErrors)) + { + this.WriteOutput(string.Empty, true); + var unusedTask = this.WritePromptStringToHost(CancellationToken.None); + } } #endregion diff --git a/src/PowerShellEditorServices/Session/IPromptContext.cs b/src/PowerShellEditorServices/Session/IPromptContext.cs new file mode 100644 index 000000000..157715e7d --- /dev/null +++ b/src/PowerShellEditorServices/Session/IPromptContext.cs @@ -0,0 +1,67 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.PowerShell.EditorServices.Session +{ + /// + /// Provides methods for interacting with implementations of ReadLine. + /// + public interface IPromptContext + { + /// + /// Read a string that has been input by the user. + /// + /// Indicates if ReadLine should act like a command REPL. + /// + /// The cancellation token can be used to cancel reading user input. + /// + /// + /// A task object that represents the completion of reading input. The Result property will + /// return the input string. + /// + Task InvokeReadLineAsync(bool isCommandLine, CancellationToken cancellationToken); + + /// + /// Performs any additional actions required to cancel the current ReadLine invocation. + /// + void AbortReadLine(); + + /// + /// Creates a task that completes when the current ReadLine invocation has been aborted. + /// + /// + /// A task object that represents the abortion of the current ReadLine invocation. + /// + Task AbortReadLineAsync(); + + /// + /// Blocks until the current ReadLine invocation has exited. + /// + void WaitForReadLineExit(); + + /// + /// Creates a task that completes when the current ReadLine invocation has exited. + /// + /// + /// A task object that represents the exit of the current ReadLine invocation. + /// + Task WaitForReadLineExitAsync(); + + /// + /// Adds the specified command to the history managed by the ReadLine implementation. + /// + /// The command to record. + void AddToHistory(string command); + + /// + /// Forces the prompt handler to trigger PowerShell event handling, reliquishing control + /// of the pipeline thread during event processing. + /// + void ForcePSEventHandling(); + } +} diff --git a/src/PowerShellEditorServices/Session/IVersionSpecificOperations.cs b/src/PowerShellEditorServices/Session/IVersionSpecificOperations.cs index d47264478..55540ba9d 100644 --- a/src/PowerShellEditorServices/Session/IVersionSpecificOperations.cs +++ b/src/PowerShellEditorServices/Session/IVersionSpecificOperations.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Management.Automation; +using System.Management.Automation.Host; using System.Management.Automation.Runspaces; namespace Microsoft.PowerShell.EditorServices.Session @@ -21,6 +22,12 @@ IEnumerable ExecuteCommandInDebugger( PSCommand psCommand, bool sendOutputToHost, out DebuggerResumeAction? debuggerResumeAction); + + void StopCommandInDebugger(PowerShellContext powerShellContext); + + bool IsDebuggerStopped(PromptNest promptNest, Runspace runspace); + + void ExitNestedPrompt(PSHost host); } } diff --git a/src/PowerShellEditorServices/Session/InvocationEventQueue.cs b/src/PowerShellEditorServices/Session/InvocationEventQueue.cs new file mode 100644 index 000000000..77d85bf23 --- /dev/null +++ b/src/PowerShellEditorServices/Session/InvocationEventQueue.cs @@ -0,0 +1,263 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; +using System.Collections.Generic; +using System.Management.Automation.Runspaces; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using System.Threading; +using Microsoft.PowerShell.EditorServices.Utility; + +namespace Microsoft.PowerShell.EditorServices.Session +{ + using System.Management.Automation; + + /// + /// Provides the ability to take over the current pipeline in a runspace. + /// + internal class InvocationEventQueue + { + private const string ShouldProcessInExecutionThreadPropertyName = "ShouldProcessInExecutionThread"; + + private static readonly PropertyInfo s_shouldProcessInExecutionThreadProperty = + typeof(PSEventSubscriber) + .GetProperty( + ShouldProcessInExecutionThreadPropertyName, + BindingFlags.Instance | BindingFlags.NonPublic); + + private readonly PromptNest _promptNest; + + private readonly Runspace _runspace; + + private readonly PowerShellContext _powerShellContext; + + private InvocationRequest _invocationRequest; + + private SemaphoreSlim _lock = AsyncUtils.CreateSimpleLockingSemaphore(); + + private InvocationEventQueue(PowerShellContext powerShellContext, PromptNest promptNest) + { + _promptNest = promptNest; + _powerShellContext = powerShellContext; + _runspace = powerShellContext.CurrentRunspace.Runspace; + } + + internal static InvocationEventQueue Create(PowerShellContext powerShellContext, PromptNest promptNest) + { + var eventQueue = new InvocationEventQueue(powerShellContext, promptNest); + eventQueue.CreateInvocationSubscriber(); + return eventQueue; + } + + /// + /// Executes a command on the main pipeline thread through + /// eventing. A event subscriber will + /// be created that creates a nested PowerShell instance for + /// to utilize. + /// + /// + /// Avoid using this method directly if possible. + /// will route commands + /// through this method if required. + /// + /// The expected result type. + /// The to be executed. + /// + /// Error messages from PowerShell will be written to the . + /// + /// Specifies options to be used when executing this command. + /// + /// An awaitable which will provide results once the command + /// execution completes. + /// + internal async Task> ExecuteCommandOnIdle( + PSCommand psCommand, + StringBuilder errorMessages, + ExecutionOptions executionOptions) + { + var request = new PipelineExecutionRequest( + _powerShellContext, + psCommand, + errorMessages, + executionOptions); + + await SetInvocationRequestAsync( + new InvocationRequest( + pwsh => request.Execute().GetAwaiter().GetResult())); + + try + { + return await request.Results; + } + finally + { + await SetInvocationRequestAsync(request: null); + } + } + + /// + /// Marshals a to run on the pipeline thread. A new + /// will be created for the invocation. + /// + /// + /// The to invoke on the pipeline thread. The nested + /// instance for the created + /// will be passed as an argument. + /// + /// + /// An awaitable that the caller can use to know when execution completes. + /// + internal async Task InvokeOnPipelineThread(Action invocationAction) + { + var request = new InvocationRequest(pwsh => + { + using (_promptNest.GetRunspaceHandle(CancellationToken.None, isReadLine: false)) + { + pwsh.Runspace = _runspace; + invocationAction(pwsh); + } + }); + + await SetInvocationRequestAsync(request); + try + { + await request.Task; + } + finally + { + await SetInvocationRequestAsync(null); + } + } + + private async Task WaitForExistingRequestAsync() + { + InvocationRequest existingRequest; + await _lock.WaitAsync(); + try + { + existingRequest = _invocationRequest; + if (existingRequest == null || existingRequest.Task.IsCompleted) + { + return; + } + } + finally + { + _lock.Release(); + } + + await existingRequest.Task; + } + + private async Task SetInvocationRequestAsync(InvocationRequest request) + { + await WaitForExistingRequestAsync(); + await _lock.WaitAsync(); + try + { + _invocationRequest = request; + } + finally + { + _lock.Release(); + } + + _powerShellContext.ForcePSEventHandling(); + } + + private void OnPowerShellIdle(object sender, EventArgs e) + { + if (!_lock.Wait(0)) + { + return; + } + + InvocationRequest currentRequest = null; + try + { + if (_invocationRequest == null) + { + return; + } + + currentRequest = _invocationRequest; + } + finally + { + _lock.Release(); + } + + _promptNest.PushPromptContext(); + try + { + currentRequest.Invoke(_promptNest.GetPowerShell()); + } + finally + { + _promptNest.PopPromptContext(); + } + } + + private PSEventSubscriber CreateInvocationSubscriber() + { + PSEventSubscriber subscriber = _runspace.Events.SubscribeEvent( + source: null, + eventName: PSEngineEvent.OnIdle, + sourceIdentifier: PSEngineEvent.OnIdle, + data: null, + handlerDelegate: OnPowerShellIdle, + supportEvent: true, + forwardEvent: false); + + SetSubscriberExecutionThreadWithReflection(subscriber); + + subscriber.Unsubscribed += OnInvokerUnsubscribed; + + return subscriber; + } + + private void OnInvokerUnsubscribed(object sender, PSEventUnsubscribedEventArgs e) + { + CreateInvocationSubscriber(); + } + + private void SetSubscriberExecutionThreadWithReflection(PSEventSubscriber subscriber) + { + // We need to create the PowerShell object in the same thread so we can get a nested + // PowerShell. This is the only way to consistently take control of the pipeline. The + // alternative is to make the subscriber a script block and have that create and process + // the PowerShell object, but that puts us in a different SessionState and is a lot slower. + s_shouldProcessInExecutionThreadProperty.SetValue(subscriber, true); + } + + private class InvocationRequest : TaskCompletionSource + { + private readonly Action _invocationAction; + + internal InvocationRequest(Action invocationAction) + { + _invocationAction = invocationAction; + } + + internal void Invoke(PowerShell pwsh) + { + try + { + _invocationAction(pwsh); + + // Ensure the result is set in another thread otherwise the caller + // may take over the pipeline thread. + System.Threading.Tasks.Task.Run(() => SetResult(true)); + } + catch (Exception e) + { + System.Threading.Tasks.Task.Run(() => SetException(e)); + } + } + } + } +} diff --git a/src/PowerShellEditorServices/Session/LegacyReadLineContext.cs b/src/PowerShellEditorServices/Session/LegacyReadLineContext.cs new file mode 100644 index 000000000..ad68d0512 --- /dev/null +++ b/src/PowerShellEditorServices/Session/LegacyReadLineContext.cs @@ -0,0 +1,56 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.PowerShell.EditorServices.Console; + +namespace Microsoft.PowerShell.EditorServices.Session +{ + internal class LegacyReadLineContext : IPromptContext + { + private readonly ConsoleReadLine _legacyReadLine; + + internal LegacyReadLineContext(PowerShellContext powerShellContext) + { + _legacyReadLine = new ConsoleReadLine(powerShellContext); + } + + public Task AbortReadLineAsync() + { + return Task.FromResult(true); + } + + public async Task InvokeReadLineAsync(bool isCommandLine, CancellationToken cancellationToken) + { + return await _legacyReadLine.InvokeLegacyReadLineAsync(isCommandLine, cancellationToken); + } + + public Task WaitForReadLineExitAsync() + { + return Task.FromResult(true); + } + + public void AddToHistory(string command) + { + // Do nothing, history is managed completely by the PowerShell engine in legacy ReadLine. + } + + public void AbortReadLine() + { + // Do nothing, no additional actions are needed to cancel ReadLine. + } + + public void WaitForReadLineExit() + { + // Do nothing, ReadLine cancellation is instant or not appliciable. + } + + public void ForcePSEventHandling() + { + // Do nothing, the pipeline thread is not occupied by legacy ReadLine. + } + } +} diff --git a/src/PowerShellEditorServices/Session/PSReadLinePromptContext.cs b/src/PowerShellEditorServices/Session/PSReadLinePromptContext.cs new file mode 100644 index 000000000..c0707d026 --- /dev/null +++ b/src/PowerShellEditorServices/Session/PSReadLinePromptContext.cs @@ -0,0 +1,205 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using System; +using System.Management.Automation.Runspaces; +using Microsoft.PowerShell.EditorServices.Console; +using Microsoft.PowerShell.EditorServices.Utility; + +namespace Microsoft.PowerShell.EditorServices.Session { + using System.Management.Automation; + + internal class PSReadLinePromptContext : IPromptContext { + private const string ReadLineScript = @" + [System.Diagnostics.DebuggerHidden()] + [System.Diagnostics.DebuggerStepThrough()] + param() + return [Microsoft.PowerShell.PSConsoleReadLine, Microsoft.PowerShell.PSReadLine2, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null]::ReadLine( + $Host.Runspace, + $ExecutionContext, + $args[0])"; + + private const string ReadLineInitScript = @" + [System.Diagnostics.DebuggerHidden()] + [System.Diagnostics.DebuggerStepThrough()] + param() + end { + $module = Get-Module -ListAvailable PSReadLine | + Where-Object Version -eq '2.0.0' | + Where-Object { $_.PrivateData.PSData.Prerelease -notin 'beta1','beta2' } | + Sort-Object -Descending Version | + Select-Object -First 1 + if (-not $module) { + return + } + + Import-Module -ModuleInfo $module + return [Microsoft.PowerShell.PSConsoleReadLine, Microsoft.PowerShell.PSReadLine2, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null] + }"; + + private readonly PowerShellContext _powerShellContext; + + private PromptNest _promptNest; + + private InvocationEventQueue _invocationEventQueue; + + private ConsoleReadLine _consoleReadLine; + + private CancellationTokenSource _readLineCancellationSource; + + private PSReadLineProxy _readLineProxy; + + internal PSReadLinePromptContext( + PowerShellContext powerShellContext, + PromptNest promptNest, + InvocationEventQueue invocationEventQueue, + PSReadLineProxy readLineProxy) + { + _promptNest = promptNest; + _powerShellContext = powerShellContext; + _invocationEventQueue = invocationEventQueue; + _consoleReadLine = new ConsoleReadLine(powerShellContext); + _readLineProxy = readLineProxy; + +#if CoreCLR + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return; + } + + _readLineProxy.OverrideReadKey( + intercept => ConsoleProxy.UnixReadKey( + intercept, + _readLineCancellationSource.Token)); +#endif + } + + internal static bool TryGetPSReadLineProxy( + ILogger logger, + Runspace runspace, + out PSReadLineProxy readLineProxy) + { + readLineProxy = null; + using (var pwsh = PowerShell.Create()) + { + pwsh.Runspace = runspace; + var psReadLineType = pwsh + .AddScript(ReadLineInitScript) + .Invoke() + .FirstOrDefault(); + + if (psReadLineType == null) + { + return false; + } + + try + { + readLineProxy = new PSReadLineProxy(psReadLineType, logger); + } + catch (InvalidOperationException) + { + // The Type we got back from PowerShell doesn't have the members we expected. + // Could be an older version, a custom build, or something a newer version with + // breaking changes. + return false; + } + } + + return true; + } + + public async Task InvokeReadLineAsync(bool isCommandLine, CancellationToken cancellationToken) + { + _readLineCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + var localTokenSource = _readLineCancellationSource; + if (localTokenSource.Token.IsCancellationRequested) + { + throw new TaskCanceledException(); + } + + try + { + if (!isCommandLine) + { + return await _consoleReadLine.InvokeLegacyReadLineAsync( + false, + _readLineCancellationSource.Token); + } + + var result = (await _powerShellContext.ExecuteCommand( + new PSCommand() + .AddScript(ReadLineScript) + .AddArgument(_readLineCancellationSource.Token), + null, + new ExecutionOptions() + { + WriteErrorsToHost = false, + WriteOutputToHost = false, + InterruptCommandPrompt = false, + AddToHistory = false, + IsReadLine = isCommandLine + })) + .FirstOrDefault(); + + return cancellationToken.IsCancellationRequested + ? string.Empty + : result; + } + finally + { + _readLineCancellationSource = null; + } + } + + public void AbortReadLine() + { + if (_readLineCancellationSource == null) + { + return; + } + + _readLineCancellationSource.Cancel(); + + WaitForReadLineExit(); + } + + public async Task AbortReadLineAsync() { + if (_readLineCancellationSource == null) + { + return; + } + + _readLineCancellationSource.Cancel(); + + await WaitForReadLineExitAsync(); + } + + public void WaitForReadLineExit() + { + using (_promptNest.GetRunspaceHandle(CancellationToken.None, isReadLine: true)) + { } + } + + public async Task WaitForReadLineExitAsync () { + using (await _promptNest.GetRunspaceHandleAsync(CancellationToken.None, isReadLine: true)) + { } + } + + public void AddToHistory(string command) + { + _readLineProxy.AddToHistory(command); + } + + public void ForcePSEventHandling() + { + _readLineProxy.ForcePSEventHandling(); + } + } +} diff --git a/src/PowerShellEditorServices/Session/PSReadLineProxy.cs b/src/PowerShellEditorServices/Session/PSReadLineProxy.cs new file mode 100644 index 000000000..50aaf4af3 --- /dev/null +++ b/src/PowerShellEditorServices/Session/PSReadLineProxy.cs @@ -0,0 +1,119 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; +using System.Reflection; +using Microsoft.PowerShell.EditorServices.Utility; + +namespace Microsoft.PowerShell.EditorServices.Session +{ + internal class PSReadLineProxy + { + private const string FieldMemberType = "field"; + + private const string MethodMemberType = "method"; + + private const string AddToHistoryMethodName = "AddToHistory"; + + private const string SetKeyHandlerMethodName = "SetKeyHandler"; + + private const string ReadKeyOverrideFieldName = "_readKeyOverride"; + + private const string VirtualTerminalTypeName = "Microsoft.PowerShell.Internal.VirtualTerminal"; + + private const string ForcePSEventHandlingMethodName = "ForcePSEventHandling"; + + private static readonly Type[] s_setKeyHandlerTypes = + { + typeof(string[]), + typeof(Action), + typeof(string), + typeof(string) + }; + + private static readonly Type[] s_addToHistoryTypes = { typeof(string) }; + + private readonly FieldInfo _readKeyOverrideField; + + internal PSReadLineProxy(Type psConsoleReadLine, ILogger logger) + { + ForcePSEventHandling = + (Action)psConsoleReadLine.GetMethod( + ForcePSEventHandlingMethodName, + BindingFlags.Static | BindingFlags.NonPublic) + ?.CreateDelegate(typeof(Action)); + + AddToHistory = (Action)psConsoleReadLine.GetMethod( + AddToHistoryMethodName, + s_addToHistoryTypes) + ?.CreateDelegate(typeof(Action)); + + SetKeyHandler = + (Action, string, string>)psConsoleReadLine.GetMethod( + SetKeyHandlerMethodName, + s_setKeyHandlerTypes) + ?.CreateDelegate(typeof(Action, string, string>)); + + _readKeyOverrideField = psConsoleReadLine.GetTypeInfo().Assembly + .GetType(VirtualTerminalTypeName) + ?.GetField(ReadKeyOverrideFieldName, BindingFlags.Static | BindingFlags.NonPublic); + + if (_readKeyOverrideField == null) + { + throw NewInvalidPSReadLineVersionException( + FieldMemberType, + ReadKeyOverrideFieldName, + logger); + } + + if (SetKeyHandler == null) + { + throw NewInvalidPSReadLineVersionException( + MethodMemberType, + SetKeyHandlerMethodName, + logger); + } + + if (AddToHistory == null) + { + throw NewInvalidPSReadLineVersionException( + MethodMemberType, + AddToHistoryMethodName, + logger); + } + + if (ForcePSEventHandling == null) + { + throw NewInvalidPSReadLineVersionException( + MethodMemberType, + ForcePSEventHandlingMethodName, + logger); + } + } + + internal Action AddToHistory { get; } + + internal Action, object>, string, string> SetKeyHandler { get; } + + internal Action ForcePSEventHandling { get; } + + internal void OverrideReadKey(Func readKeyFunc) + { + _readKeyOverrideField.SetValue(null, readKeyFunc); + } + + private static InvalidOperationException NewInvalidPSReadLineVersionException( + string memberType, + string memberName, + ILogger logger) + { + logger.Write( + LogLevel.Error, + $"The loaded version of PSReadLine is not supported. The {memberType} \"{memberName}\" was not found."); + + return new InvalidOperationException(); + } + } +} diff --git a/src/PowerShellEditorServices/Session/PipelineExecutionRequest.cs b/src/PowerShellEditorServices/Session/PipelineExecutionRequest.cs new file mode 100644 index 000000000..cb1d66073 --- /dev/null +++ b/src/PowerShellEditorServices/Session/PipelineExecutionRequest.cs @@ -0,0 +1,80 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Collections.Generic; +using System.Management.Automation; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.PowerShell.EditorServices.Session +{ + internal interface IPipelineExecutionRequest + { + Task Execute(); + + Task WaitTask { get; } + } + + /// + /// Contains details relating to a request to execute a + /// command on the PowerShell pipeline thread. + /// + /// The expected result type of the execution. + internal class PipelineExecutionRequest : IPipelineExecutionRequest + { + private PowerShellContext _powerShellContext; + private PSCommand _psCommand; + private StringBuilder _errorMessages; + private ExecutionOptions _executionOptions; + private TaskCompletionSource> _resultsTask; + + public Task> Results + { + get { return this._resultsTask.Task; } + } + + public Task WaitTask { get { return Results; } } + + public PipelineExecutionRequest( + PowerShellContext powerShellContext, + PSCommand psCommand, + StringBuilder errorMessages, + bool sendOutputToHost) + : this( + powerShellContext, + psCommand, + errorMessages, + new ExecutionOptions() + { + WriteOutputToHost = sendOutputToHost + }) + { } + + + public PipelineExecutionRequest( + PowerShellContext powerShellContext, + PSCommand psCommand, + StringBuilder errorMessages, + ExecutionOptions executionOptions) + { + _powerShellContext = powerShellContext; + _psCommand = psCommand; + _errorMessages = errorMessages; + _executionOptions = executionOptions; + _resultsTask = new TaskCompletionSource>(); + } + + public async Task Execute() + { + var results = + await _powerShellContext.ExecuteCommand( + _psCommand, + _errorMessages, + _executionOptions); + + var unusedTask = Task.Run(() => _resultsTask.SetResult(results)); + } + } +} diff --git a/src/PowerShellEditorServices/Session/PowerShell3Operations.cs b/src/PowerShellEditorServices/Session/PowerShell3Operations.cs index 2199e1839..eb6cf0252 100644 --- a/src/PowerShellEditorServices/Session/PowerShell3Operations.cs +++ b/src/PowerShellEditorServices/Session/PowerShell3Operations.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using System.Management.Automation; +using System.Management.Automation.Host; using System.Management.Automation.Runspaces; namespace Microsoft.PowerShell.EditorServices.Session @@ -69,6 +70,27 @@ public IEnumerable ExecuteCommandInDebugger( return executionResult; } + + public void StopCommandInDebugger(PowerShellContext powerShellContext) + { + } + + public bool IsDebuggerStopped(PromptNest promptNest, Runspace runspace) + { + return promptNest.IsInDebugger; + } + + public void ExitNestedPrompt(PSHost host) + { + try + { + host.ExitNestedPrompt(); + } + // FlowControlException is not accessible in PSv3 + catch (Exception) + { + } + } } } diff --git a/src/PowerShellEditorServices/Session/PowerShell4Operations.cs b/src/PowerShellEditorServices/Session/PowerShell4Operations.cs index ea4070225..d9060ed2f 100644 --- a/src/PowerShellEditorServices/Session/PowerShell4Operations.cs +++ b/src/PowerShellEditorServices/Session/PowerShell4Operations.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using System.Management.Automation; +using System.Management.Automation.Host; using System.Management.Automation.Runspaces; namespace Microsoft.PowerShell.EditorServices.Session @@ -79,6 +80,33 @@ public IEnumerable ExecuteCommandInDebugger( return results; } + + public void StopCommandInDebugger(PowerShellContext powerShellContext) + { +#if !PowerShellv3 + powerShellContext.CurrentRunspace.Runspace.Debugger.StopProcessCommand(); +#endif + } + + public virtual bool IsDebuggerStopped(PromptNest promptNest, Runspace runspace) + { + return promptNest.IsInDebugger; + } + + public void ExitNestedPrompt(PSHost host) + { +#if !PowerShellv3 + try + { + host.ExitNestedPrompt(); + } + catch (FlowControlException) + { + } +#else + throw new NotSupportedException(); +#endif + } } } diff --git a/src/PowerShellEditorServices/Session/PowerShell5Operations.cs b/src/PowerShellEditorServices/Session/PowerShell5Operations.cs index 54f434cb8..e27c3b14e 100644 --- a/src/PowerShellEditorServices/Session/PowerShell5Operations.cs +++ b/src/PowerShellEditorServices/Session/PowerShell5Operations.cs @@ -16,6 +16,16 @@ public override void PauseDebugger(Runspace runspace) { runspace.Debugger.SetDebuggerStepMode(true); } +#endif + } + + public override bool IsDebuggerStopped(PromptNest promptNest, Runspace runspace) + { +#if !PowerShellv3 && !PowerShellv4 + return runspace.Debugger.InBreakpoint || + (promptNest.IsRemote && promptNest.IsInDebugger); +#else + throw new System.NotSupportedException(); #endif } } diff --git a/src/PowerShellEditorServices/Session/PowerShellContext.cs b/src/PowerShellEditorServices/Session/PowerShellContext.cs index cf1206b0f..c67eb11f2 100644 --- a/src/PowerShellEditorServices/Session/PowerShellContext.cs +++ b/src/PowerShellEditorServices/Session/PowerShellContext.cs @@ -3,27 +3,27 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -using Microsoft.PowerShell.EditorServices.Console; -using Microsoft.PowerShell.EditorServices.Utility; using System; -using System.Globalization; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Globalization; +using System.IO; using System.Linq; +using System.Management.Automation.Host; +using System.Management.Automation.Remoting; +using System.Management.Automation.Runspaces; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using Microsoft.PowerShell.EditorServices.Session; +using Microsoft.PowerShell.EditorServices.Session.Capabilities; +using Microsoft.PowerShell.EditorServices.Utility; namespace Microsoft.PowerShell.EditorServices { - using Session; - using System.Management.Automation; - using System.Management.Automation.Host; - using System.Management.Automation.Runspaces; - using Microsoft.PowerShell.EditorServices.Session.Capabilities; - using System.IO; using System.ComponentModel; + using System.Management.Automation; /// /// Manages the lifetime and usage of a PowerShell session. @@ -34,6 +34,9 @@ public class PowerShellContext : IDisposable, IHostSupportsInteractiveSession { #region Fields + private readonly SemaphoreSlim resumeRequestHandle = AsyncUtils.CreateSimpleLockingSemaphore(); + + private bool isPSReadLineEnabled; private ILogger logger; private PowerShell powerShell; private bool ownsInitialRunspace; @@ -44,32 +47,32 @@ public class PowerShellContext : IDisposable, IHostSupportsInteractiveSession private IVersionSpecificOperations versionSpecificOperations; - private int pipelineThreadId; - private TaskCompletionSource debuggerStoppedTask; - private TaskCompletionSource pipelineExecutionTask; - - private object runspaceMutex = new object(); - private AsyncQueue runspaceWaitQueue = new AsyncQueue(); - private Stack runspaceStack = new Stack(); + private int isCommandLoopRestarterSet; + #endregion #region Properties + private IPromptContext PromptContext { get; set; } + + private PromptNest PromptNest { get; set; } + + private InvocationEventQueue InvocationEventQueue { get; set; } + + private EngineIntrinsics EngineIntrinsics { get; set; } + + private PSHost ExternalHost { get; set; } + /// /// Gets a boolean that indicates whether the debugger is currently stopped, /// either at a breakpoint or because the user broke execution. /// - public bool IsDebuggerStopped - { - get - { - return - this.debuggerStoppedTask != null && - this.CurrentRunspace.Runspace.RunspaceAvailability != RunspaceAvailability.Available; - } - } + public bool IsDebuggerStopped => + this.versionSpecificOperations.IsDebuggerStopped( + PromptNest, + CurrentRunspace.Runspace); /// /// Gets the current state of the session. @@ -95,6 +98,8 @@ public PowerShellVersionDetails LocalPowerShellVersion /// private IHostOutput ConsoleWriter { get; set; } + private IHostInput ConsoleReader { get; set; } + /// /// Gets details pertaining to the current runspace. /// @@ -104,6 +109,12 @@ public RunspaceDetails CurrentRunspace private set; } + /// + /// Gets a value indicating whether the current runspace + /// is ready for a command + /// + public bool IsAvailable => this.SessionState == PowerShellContextState.Ready; + /// /// Gets the working directory path the PowerShell context was inititially set when the debugger launches. /// This path is used to determine whether a script in the call stack is an "external" script. @@ -118,9 +129,13 @@ public RunspaceDetails CurrentRunspace /// /// /// An ILogger implementation used for writing log messages. - public PowerShellContext(ILogger logger) + /// + /// Indicates whether PSReadLine should be used if possible + /// + public PowerShellContext(ILogger logger, bool isPSReadLineEnabled) { this.logger = logger; + this.isPSReadLineEnabled = isPSReadLineEnabled; } /// @@ -141,6 +156,7 @@ public static Runspace CreateRunspace( { var psHost = new EditorServicesPSHost(powerShellContext, hostDetails, hostUserInterface, logger); powerShellContext.ConsoleWriter = hostUserInterface; + powerShellContext.ConsoleReader = hostUserInterface; return CreateRunspace(psHost); } @@ -197,6 +213,7 @@ public void Initialize( this.ownsInitialRunspace = ownsInitialRunspace; this.SessionState = PowerShellContextState.NotStarted; this.ConsoleWriter = consoleHost; + this.ConsoleReader = consoleHost as IHostInput; // Get the PowerShell runtime version this.LocalPowerShellVersion = @@ -265,13 +282,48 @@ public void Initialize( } // Now that initialization is complete we can watch for InvocationStateChanged - this.powerShell.InvocationStateChanged += powerShell_InvocationStateChanged; - this.SessionState = PowerShellContextState.Ready; + // EngineIntrinsics is used in some instances to interact with the initial + // runspace without having to wait for PSReadLine to check for events. + this.EngineIntrinsics = + initialRunspace + .SessionStateProxy + .PSVariable + .GetValue("ExecutionContext") + as EngineIntrinsics; + + // The external host is used to properly exit from a nested prompt that + // was entered by the user. + this.ExternalHost = + initialRunspace + .SessionStateProxy + .PSVariable + .GetValue("Host") + as PSHost; + // Now that the runspace is ready, enqueue it for first use - RunspaceHandle runspaceHandle = new RunspaceHandle(this); - this.runspaceWaitQueue.EnqueueAsync(runspaceHandle).Wait(); + this.PromptNest = new PromptNest( + this, + this.powerShell, + this.ConsoleReader, + this.versionSpecificOperations); + this.InvocationEventQueue = InvocationEventQueue.Create(this, this.PromptNest); + + if (powerShellVersion.Major >= 5 && + this.isPSReadLineEnabled && + PSReadLinePromptContext.TryGetPSReadLineProxy(logger, initialRunspace, out PSReadLineProxy proxy)) + { + this.PromptContext = new PSReadLinePromptContext( + this, + this.PromptNest, + this.InvocationEventQueue, + proxy); + } + else + { + this.PromptContext = new LegacyReadLineContext(this); + } } /// @@ -340,7 +392,7 @@ private void CleanupRunspace(RunspaceDetails runspaceDetails) /// A RunspaceHandle instance that gives access to the session's runspace. public Task GetRunspaceHandle() { - return this.GetRunspaceHandle(CancellationToken.None); + return this.GetRunspaceHandleImpl(CancellationToken.None, isReadLine: false); } /// @@ -352,7 +404,7 @@ public Task GetRunspaceHandle() /// A RunspaceHandle instance that gives access to the session's runspace. public Task GetRunspaceHandle(CancellationToken cancellationToken) { - return this.runspaceWaitQueue.DequeueAsync(cancellationToken); + return this.GetRunspaceHandleImpl(cancellationToken, isReadLine: false); } /// @@ -435,28 +487,56 @@ public async Task> ExecuteCommand( StringBuilder errorMessages, ExecutionOptions executionOptions) { + // Add history to PSReadLine before cancelling, otherwise it will be restored as the + // cancelled prompt when it's called again. + if (executionOptions.AddToHistory) + { + this.PromptContext.AddToHistory(psCommand.Commands[0].CommandText); + } + bool hadErrors = false; RunspaceHandle runspaceHandle = null; + ExecutionTarget executionTarget = ExecutionTarget.PowerShell; IEnumerable executionResult = Enumerable.Empty(); + var shouldCancelReadLine = + executionOptions.InterruptCommandPrompt || + executionOptions.WriteOutputToHost; // If the debugger is active and the caller isn't on the pipeline // thread, send the command over to that thread to be executed. - if (Thread.CurrentThread.ManagedThreadId != this.pipelineThreadId && - this.pipelineExecutionTask != null) + // Determine if execution should take place in a different thread + // using the following criteria: + // 1. The current frame in the prompt nest has a thread controller + // (meaning it is a nested prompt or is in the debugger) + // 2. We aren't already on the thread in question + // 3. The command is not a candidate for background invocation + // via PowerShell eventing + // 4. The command cannot be for a PSReadLine pipeline while we + // are currently in a out of process runspace + var threadController = PromptNest.GetThreadController(); + if (!(threadController == null || + !threadController.IsPipelineThread || + threadController.IsCurrentThread() || + this.ShouldExecuteWithEventing(executionOptions) || + (PromptNest.IsRemote && executionOptions.IsReadLine))) { this.logger.Write(LogLevel.Verbose, "Passing command execution to pipeline thread."); - PipelineExecutionRequest executionRequest = + if (shouldCancelReadLine && PromptNest.IsReadLineBusy()) + { + // If a ReadLine pipeline is running in the debugger then we'll hang here + // if we don't cancel it. Typically we can rely on OnExecutionStatusChanged but + // the pipeline request won't even start without clearing the current task. + this.ConsoleReader?.StopCommandLoop(); + } + + // Send the pipeline execution request to the pipeline thread + return await threadController.RequestPipelineExecution( new PipelineExecutionRequest( this, psCommand, errorMessages, - executionOptions.WriteOutputToHost); - - // Send the pipeline execution request to the pipeline thread - this.pipelineExecutionTask.SetResult(executionRequest); - - return await executionRequest.Results; + executionOptions)); } else { @@ -474,73 +554,127 @@ public async Task> ExecuteCommand( endOfStatement: false)); } - this.OnExecutionStatusChanged( - ExecutionStatus.Running, - executionOptions, - false); + executionTarget = GetExecutionTarget(executionOptions); + + // If a ReadLine pipeline is running we can still execute commands that + // don't write output (e.g. command completion) + if (executionTarget == ExecutionTarget.InvocationEvent) + { + return (await this.InvocationEventQueue.ExecuteCommandOnIdle( + psCommand, + errorMessages, + executionOptions)); + } + + // Prompt is stopped and started based on the execution status, so naturally + // we don't want PSReadLine pipelines to factor in. + if (!executionOptions.IsReadLine) + { + this.OnExecutionStatusChanged( + ExecutionStatus.Running, + executionOptions, + false); + } + + runspaceHandle = await this.GetRunspaceHandle(executionOptions.IsReadLine); + if (executionOptions.WriteInputToHost) + { + this.WriteOutput(psCommand.Commands[0].CommandText, true); + } - if (this.CurrentRunspace.Runspace.RunspaceAvailability == RunspaceAvailability.AvailableForNestedCommand || - this.debuggerStoppedTask != null) + if (executionTarget == ExecutionTarget.Debugger) { - executionResult = - this.ExecuteCommandInDebugger( + // Manually change the session state for debugger commands because + // we don't have an invocation state event to attach to. + if (!executionOptions.IsReadLine) + { + this.OnSessionStateChanged( + this, + new SessionStateChangedEventArgs( + PowerShellContextState.Running, + PowerShellExecutionResult.NotFinished, + null)); + } + try + { + return this.ExecuteCommandInDebugger( psCommand, executionOptions.WriteOutputToHost); + } + catch (Exception e) + { + logger.Write( + LogLevel.Error, + "Exception occurred while executing debugger command:\r\n\r\n" + e.ToString()); + } + finally + { + if (!executionOptions.IsReadLine) + { + this.OnSessionStateChanged( + this, + new SessionStateChangedEventArgs( + PowerShellContextState.Ready, + PowerShellExecutionResult.Stopped, + null)); + } + } } - else + + var invocationSettings = new PSInvocationSettings() + { + AddToHistory = executionOptions.AddToHistory + }; + + this.logger.Write( + LogLevel.Verbose, + string.Format( + "Attempting to execute command(s):\r\n\r\n{0}", + GetStringForPSCommand(psCommand))); + + + PowerShell shell = this.PromptNest.GetPowerShell(executionOptions.IsReadLine); + shell.Commands = psCommand; + + // Don't change our SessionState for ReadLine. + if (!executionOptions.IsReadLine) + { + shell.InvocationStateChanged += powerShell_InvocationStateChanged; + } + + shell.Runspace = executionOptions.ShouldExecuteInOriginalRunspace + ? this.initialRunspace.Runspace + : this.CurrentRunspace.Runspace; + try { - this.logger.Write( - LogLevel.Verbose, - string.Format( - "Attempting to execute command(s):\r\n\r\n{0}", - GetStringForPSCommand(psCommand))); - - // Set the runspace - runspaceHandle = await this.GetRunspaceHandle(); - if (runspaceHandle.Runspace.RunspaceAvailability != RunspaceAvailability.AvailableForNestedCommand) + // Nested PowerShell instances can't be invoked asynchronously. This occurs + // in nested prompts and pipeline requests from eventing. + if (shell.IsNested) { - this.powerShell.Runspace = runspaceHandle.Runspace; + return shell.Invoke(null, invocationSettings); } - // Invoke the pipeline on a background thread - // TODO: Use built-in async invocation! - executionResult = - await Task.Factory.StartNew>( - () => - { - Collection result = null; - try - { - this.powerShell.Commands = psCommand; - - PSInvocationSettings invocationSettings = new PSInvocationSettings(); - invocationSettings.AddToHistory = executionOptions.AddToHistory; - result = this.powerShell.Invoke(null, invocationSettings); - } - catch (RemoteException e) - { - if (!e.SerializedRemoteException.TypeNames[0].EndsWith("PipelineStoppedException")) - { - // Rethrow anything that isn't a PipelineStoppedException - throw e; - } - } - - return result; - }, - CancellationToken.None, // Might need a cancellation token - TaskCreationOptions.None, - TaskScheduler.Default - ); - - if (this.powerShell.HadErrors) + return await Task.Factory.StartNew>( + () => shell.Invoke(null, invocationSettings), + CancellationToken.None, // Might need a cancellation token + TaskCreationOptions.None, + TaskScheduler.Default); + } + finally + { + if (!executionOptions.IsReadLine) + { + shell.InvocationStateChanged -= powerShell_InvocationStateChanged; + } + + if (shell.HadErrors) { var strBld = new StringBuilder(1024); strBld.AppendFormat("Execution of the following command(s) completed with errors:\r\n\r\n{0}\r\n", GetStringForPSCommand(psCommand)); int i = 1; - foreach (var error in this.powerShell.Streams.Error) + foreach (var error in shell.Streams.Error) { if (i > 1) strBld.Append("\r\n\r\n"); strBld.Append($"Error #{i++}:\r\n"); @@ -557,7 +691,7 @@ await Task.Factory.StartNew>( } // We've reported these errors, clear them so they don't keep showing up. - this.powerShell.Streams.Error.Clear(); + shell.Streams.Error.Clear(); var errorMessage = strBld.ToString(); @@ -574,6 +708,14 @@ await Task.Factory.StartNew>( } } } + catch (PSRemotingDataStructureException e) + { + this.logger.Write( + LogLevel.Error, + "Pipeline stopped while executing command:\r\n\r\n" + e.ToString()); + + errorMessages?.Append(e.Message); + } catch (PipelineStoppedException e) { this.logger.Write( @@ -614,7 +756,11 @@ await Task.Factory.StartNew>( SessionDetails sessionDetails = null; // Get the SessionDetails and then write the prompt - if (this.CurrentRunspace.Runspace.RunspaceAvailability == RunspaceAvailability.Available) + if (executionTarget == ExecutionTarget.Debugger) + { + sessionDetails = this.GetSessionDetailsInDebugger(); + } + else if (this.CurrentRunspace.Runspace.RunspaceAvailability == RunspaceAvailability.Available) { // This state can happen if the user types a command that causes the // debugger to exit before we reach this point. No RunspaceHandle @@ -626,10 +772,6 @@ await Task.Factory.StartNew>( sessionDetails = this.GetSessionDetailsInRunspace(runspaceHandle.Runspace); } - else if (this.IsDebuggerStopped) - { - sessionDetails = this.GetSessionDetailsInDebugger(); - } else { sessionDetails = this.GetSessionDetailsInNestedPipeline(); @@ -644,14 +786,14 @@ await Task.Factory.StartNew>( { runspaceHandle.Dispose(); } + + this.OnExecutionStatusChanged( + ExecutionStatus.Completed, + executionOptions, + hadErrors); } } - this.OnExecutionStatusChanged( - ExecutionStatus.Completed, - executionOptions, - hadErrors); - return executionResult; } @@ -741,23 +883,15 @@ public async Task> ExecuteScriptString( bool writeOutputToHost, bool addToHistory) { - // Get rid of leading and trailing whitespace and newlines - scriptString = scriptString.Trim(); - - if (writeInputToHost) - { - this.WriteOutput(scriptString, false); - } - - PSCommand psCommand = new PSCommand(); - psCommand.AddScript(scriptString); - - return - await this.ExecuteCommand( - psCommand, - errorMessages, - writeOutputToHost, - addToHistory: addToHistory); + return await this.ExecuteCommand( + new PSCommand().AddScript(scriptString.Trim()), + errorMessages, + new ExecutionOptions() + { + WriteOutputToHost = writeOutputToHost, + AddToHistory = addToHistory, + WriteInputToHost = writeInputToHost + }); } /// @@ -778,8 +912,15 @@ public async Task ExecuteScriptWithArgs(string script, string arguments = null, try { // Assume we can only debug scripts from the FileSystem provider - string workingDir = - this.CurrentRunspace.Runspace.SessionStateProxy.Path.CurrentFileSystemLocation.ProviderPath; + string workingDir = (await ExecuteCommand( + new PSCommand() + .AddCommand("Microsoft.PowerShell.Management\\Get-Location") + .AddParameter("PSProvider", "FileSystem"), + false, + false)) + .FirstOrDefault() + .ProviderPath; + workingDir = workingDir.TrimEnd(Path.DirectorySeparatorChar); scriptAbsPath = workingDir + Path.DirectorySeparatorChar + script; } @@ -792,10 +933,10 @@ public async Task ExecuteScriptWithArgs(string script, string arguments = null, var strBld = new StringBuilder(); - // The script parameter can refer to either a "script path" or a "command name". If it is a + // The script parameter can refer to either a "script path" or a "command name". If it is a // script path, we can determine that by seeing if the path exists. If so, we always single // quote that path in case it includes special PowerShell characters like ', &, (, ), [, ] and - // . Any embedded single quotes are escaped. + // . Any embedded single quotes are escaped. // If the provided path is already quoted, then File.Exists will not find it. // This keeps us from quoting an already quoted path. // Related to issue #123. @@ -838,6 +979,57 @@ await this.ExecuteCommand( addToHistory: true); } + /// + /// Forces the to trigger PowerShell event handling, + /// reliquishing control of the pipeline thread during event processing. + /// + /// + /// This method is called automatically by and + /// . Consider using them instead of this method directly when + /// possible. + /// + internal void ForcePSEventHandling() + { + PromptContext.ForcePSEventHandling(); + } + + /// + /// Marshals a to run on the pipeline thread. A new + /// will be created for the invocation. + /// + /// + /// The to invoke on the pipeline thread. The nested + /// instance for the created + /// will be passed as an argument. + /// + /// + /// An awaitable that the caller can use to know when execution completes. + /// + /// + /// This method is called automatically by . Consider using + /// that method instead of calling this directly when possible. + /// + internal async Task InvokeOnPipelineThread(Action invocationAction) + { + if (this.PromptNest.IsReadLineBusy()) + { + await this.InvocationEventQueue.InvokeOnPipelineThread(invocationAction); + return; + } + + // If this is invoked when ReadLine isn't busy then there shouldn't be any running + // pipelines. Right now this method is only used by command completion which doesn't + // actually require running on the pipeline thread, as long as nothing else is running. + invocationAction.Invoke(this.PromptNest.GetPowerShell()); + } + + internal async Task InvokeReadLineAsync(bool isCommandLine, CancellationToken cancellationToken) + { + return await PromptContext.InvokeReadLineAsync( + isCommandLine, + cancellationToken); + } + internal static TResult ExecuteScriptAndGetItem(string scriptToExecute, Runspace runspace, TResult defaultValue = default(TResult)) { Pipeline pipeline = null; @@ -907,26 +1099,50 @@ public async Task LoadHostProfiles() } /// - /// Causes the current execution to be aborted no matter what state + /// Causes the most recent execution to be aborted no matter what state /// it is currently in. /// public void AbortExecution() + { + this.AbortExecution(shouldAbortDebugSession: false); + } + + /// + /// Causes the most recent execution to be aborted no matter what state + /// it is currently in. + /// + /// + /// A value indicating whether a debug session should be aborted if one + /// is currently active. + /// + public void AbortExecution(bool shouldAbortDebugSession) { if (this.SessionState != PowerShellContextState.Aborting && this.SessionState != PowerShellContextState.Disposed) { this.logger.Write(LogLevel.Verbose, "Execution abort requested..."); - // Clean up the debugger - if (this.IsDebuggerStopped) + if (shouldAbortDebugSession) { - this.ResumeDebugger(DebuggerResumeAction.Stop); - this.debuggerStoppedTask = null; - this.pipelineExecutionTask = null; + this.ExitAllNestedPrompts(); } - // Stop the running pipeline - this.powerShell.BeginStop(null, null); + if (this.PromptNest.IsInDebugger) + { + if (shouldAbortDebugSession) + { + this.versionSpecificOperations.StopCommandInDebugger(this); + this.ResumeDebugger(DebuggerResumeAction.Stop); + } + else + { + this.versionSpecificOperations.StopCommandInDebugger(this); + } + } + else + { + this.PromptNest.GetPowerShell(isReadLine: false).BeginStop(null, null); + } this.SessionState = PowerShellContextState.Aborting; @@ -944,6 +1160,33 @@ public void AbortExecution() } } + /// + /// Exit all consecutive nested prompts that the user has entered. + /// + internal void ExitAllNestedPrompts() + { + while (this.PromptNest.IsNestedPrompt) + { + this.PromptNest.WaitForCurrentFrameExit(frame => this.ExitNestedPrompt()); + this.versionSpecificOperations.ExitNestedPrompt(ExternalHost); + } + } + + /// + /// Exit all consecutive nested prompts that the user has entered. + /// + /// + /// A task object that represents all nested prompts being exited + /// + internal async Task ExitAllNestedPromptsAsync() + { + while (this.PromptNest.IsNestedPrompt) + { + await this.PromptNest.WaitForCurrentFrameExitAsync(frame => this.ExitNestedPrompt()); + this.versionSpecificOperations.ExitNestedPrompt(ExternalHost); + } + } + /// /// Causes the debugger to break execution wherever it currently is. /// This method is internal because the real Break API is provided @@ -960,22 +1203,56 @@ internal void BreakExecution() internal void ResumeDebugger(DebuggerResumeAction resumeAction) { - if (this.debuggerStoppedTask != null) + ResumeDebugger(resumeAction, shouldWaitForExit: true); + } + + private void ResumeDebugger(DebuggerResumeAction resumeAction, bool shouldWaitForExit) + { + resumeRequestHandle.Wait(); + try { - // Set the result so that the execution thread resumes. - // The execution thread will clean up the task. - if (!this.debuggerStoppedTask.TrySetResult(resumeAction)) + if (this.PromptNest.IsNestedPrompt) + { + this.ExitAllNestedPrompts(); + } + + if (this.PromptNest.IsInDebugger) + { + // Set the result so that the execution thread resumes. + // The execution thread will clean up the task. + if (shouldWaitForExit) + { + this.PromptNest.WaitForCurrentFrameExit( + frame => + { + frame.ThreadController.StartThreadExit(resumeAction); + this.ConsoleReader?.StopCommandLoop(); + if (this.SessionState != PowerShellContextState.Ready) + { + this.versionSpecificOperations.StopCommandInDebugger(this); + } + }); + } + else + { + this.PromptNest.GetThreadController().StartThreadExit(resumeAction); + this.ConsoleReader?.StopCommandLoop(); + if (this.SessionState != PowerShellContextState.Ready) + { + this.versionSpecificOperations.StopCommandInDebugger(this); + } + } + } + else { this.logger.Write( LogLevel.Error, - $"Tried to resume debugger with action {resumeAction} but the task was already completed."); + $"Tried to resume debugger with action {resumeAction} but there was no debuggerStoppedTask."); } } - else + finally { - this.logger.Write( - LogLevel.Error, - $"Tried to resume debugger with action {resumeAction} but there was no debuggerStoppedTask."); + resumeRequestHandle.Release(); } } @@ -985,22 +1262,9 @@ internal void ResumeDebugger(DebuggerResumeAction resumeAction) /// public void Dispose() { - // Do we need to abort a running execution? - if (this.SessionState == PowerShellContextState.Running || - this.IsDebuggerStopped) - { - this.AbortExecution(); - } - + this.PromptNest.Dispose(); this.SessionState = PowerShellContextState.Disposed; - if (this.powerShell != null) - { - this.powerShell.InvocationStateChanged -= this.powerShell_InvocationStateChanged; - this.powerShell.Dispose(); - this.powerShell = null; - } - // Clean up the active runspace this.CleanupRunspace(this.CurrentRunspace); @@ -1029,6 +1293,57 @@ public void Dispose() this.initialRunspace = null; } + private async Task GetRunspaceHandle(bool isReadLine) + { + return await this.GetRunspaceHandleImpl(CancellationToken.None, isReadLine); + } + + private async Task GetRunspaceHandleImpl(CancellationToken cancellationToken, bool isReadLine) + { + return await this.PromptNest.GetRunspaceHandleAsync(cancellationToken, isReadLine); + } + + private ExecutionTarget GetExecutionTarget(ExecutionOptions options = null) + { + if (options == null) + { + options = new ExecutionOptions(); + } + + var noBackgroundInvocation = + options.InterruptCommandPrompt || + options.WriteOutputToHost || + options.IsReadLine || + PromptNest.IsRemote; + + // Take over the pipeline if PSReadLine is running, we aren't trying to run PSReadLine, and + // we aren't in a remote session. + if (!noBackgroundInvocation && PromptNest.IsReadLineBusy() && PromptNest.IsMainThreadBusy()) + { + return ExecutionTarget.InvocationEvent; + } + + // We can't take the pipeline from PSReadLine if it's in a remote session, so we need to + // invoke locally in that case. + if (IsDebuggerStopped && PromptNest.IsInDebugger && !(options.IsReadLine && PromptNest.IsRemote)) + { + return ExecutionTarget.Debugger; + } + + return ExecutionTarget.PowerShell; + } + + private bool ShouldExecuteWithEventing(ExecutionOptions executionOptions) + { + return + this.PromptNest.IsReadLineBusy() && + this.PromptNest.IsMainThreadBusy() && + !(executionOptions.IsReadLine || + executionOptions.InterruptCommandPrompt || + executionOptions.WriteOutputToHost || + IsCurrentRunspaceOutOfProcess()); + } + private void CloseRunspace(RunspaceDetails runspaceDetails) { string exitCommand = null; @@ -1093,20 +1408,101 @@ internal void ReleaseRunspaceHandle(RunspaceHandle runspaceHandle) { Validate.IsNotNull("runspaceHandle", runspaceHandle); - if (this.runspaceWaitQueue.IsEmpty) + if (PromptNest.IsMainThreadBusy() || (runspaceHandle.IsReadLine && PromptNest.IsReadLineBusy())) { - var newRunspaceHandle = new RunspaceHandle(this); - this.runspaceWaitQueue.EnqueueAsync(newRunspaceHandle).Wait(); + var unusedTask = PromptNest + .ReleaseRunspaceHandleAsync(runspaceHandle) + .ConfigureAwait(false); } else { // Write the situation to the log since this shouldn't happen this.logger.Write( LogLevel.Error, - "The PowerShellContext.runspaceWaitQueue has more than one item"); + "ReleaseRunspaceHandle was called when the main thread was not busy."); } } + /// + /// Determines if the current runspace is out of process. + /// + /// + /// A value indicating whether the current runspace is out of process. + /// + internal bool IsCurrentRunspaceOutOfProcess() + { + return + CurrentRunspace.Context == RunspaceContext.EnteredProcess || + CurrentRunspace.Context == RunspaceContext.DebuggedRunspace || + CurrentRunspace.Location == RunspaceLocation.Remote; + } + + /// + /// Called by the external PSHost when $Host.EnterNestedPrompt is called. + /// + internal void EnterNestedPrompt() + { + if (this.IsCurrentRunspaceOutOfProcess()) + { + throw new NotSupportedException(); + } + + this.PromptNest.PushPromptContext(PromptNestFrameType.NestedPrompt); + var localThreadController = this.PromptNest.GetThreadController(); + this.OnSessionStateChanged( + this, + new SessionStateChangedEventArgs( + PowerShellContextState.Ready, + PowerShellExecutionResult.Stopped, + null)); + + // Reset command loop mainly for PSReadLine + this.ConsoleReader?.StopCommandLoop(); + this.ConsoleReader?.StartCommandLoop(); + + var localPipelineExecutionTask = localThreadController.TakeExecutionRequest(); + var localDebuggerStoppedTask = localThreadController.Exit(); + + // Wait for off-thread pipeline requests and/or ExitNestedPrompt + while (true) + { + int taskIndex = Task.WaitAny( + localPipelineExecutionTask, + localDebuggerStoppedTask); + + if (taskIndex == 0) + { + var localExecutionTask = localPipelineExecutionTask.GetAwaiter().GetResult(); + localPipelineExecutionTask = localThreadController.TakeExecutionRequest(); + localExecutionTask.Execute().GetAwaiter().GetResult(); + continue; + } + + this.ConsoleReader?.StopCommandLoop(); + this.PromptNest.PopPromptContext(); + break; + } + } + + /// + /// Called by the external PSHost when $Host.ExitNestedPrompt is called. + /// + internal void ExitNestedPrompt() + { + if (this.PromptNest.NestedPromptLevel == 1 || !this.PromptNest.IsNestedPrompt) + { + this.logger.Write( + LogLevel.Error, + "ExitNestedPrompt was called outside of a nested prompt."); + return; + } + + // Stop the command input loop so PSReadLine isn't invoked between ExitNestedPrompt + // being invoked and EnterNestedPrompt getting the message to exit. + this.ConsoleReader?.StopCommandLoop(); + this.PromptNest.GetThreadController().StartThreadExit(DebuggerResumeAction.Stop); + } + /// /// Sets the current working directory of the powershell context. The path should be /// unescaped before calling this method. @@ -1126,15 +1522,17 @@ public async Task SetWorkingDirectory(string path, bool isPathAlreadyEscaped) { this.InitialWorkingDirectory = path; - using (RunspaceHandle runspaceHandle = await this.GetRunspaceHandle()) + if (!isPathAlreadyEscaped) { - if (!isPathAlreadyEscaped) - { - path = WildcardEscapePath(path); - } - - runspaceHandle.Runspace.SessionStateProxy.Path.SetLocation(path); + path = WildcardEscapePath(path); } + + await ExecuteCommand( + new PSCommand().AddCommand("Set-Location").AddParameter("Path", path), + null, + sendOutputToHost: false, + sendErrorToHost: false, + addToHistory: false); } /// @@ -1367,7 +1765,9 @@ private IEnumerable ExecuteCommandInDebugger(PSCommand psComma if (debuggerResumeAction.HasValue) { // Resume the debugger with the specificed action - this.ResumeDebugger(debuggerResumeAction.Value); + this.ResumeDebugger( + debuggerResumeAction.Value, + shouldWaitForExit: false); } return output; @@ -1530,11 +1930,11 @@ private Command GetOutputCommand(bool endOfStatement) { Command outputCommand = new Command( - command: this.IsDebuggerStopped ? "Out-String" : "Out-Default", + command: this.PromptNest.IsInDebugger ? "Out-String" : "Out-Default", isScript: false, useLocalScope: true); - if (this.IsDebuggerStopped) + if (this.PromptNest.IsInDebugger) { // Out-String needs the -Stream parameter added outputCommand.Parameters.Add("Stream"); @@ -1641,6 +2041,12 @@ private SessionDetails GetSessionDetails(Func invokeAction) LogLevel.Verbose, "Runtime exception occurred while gathering runspace info:\r\n\r\n" + e.ToString()); } + catch (ArgumentNullException) + { + this.logger.Write( + LogLevel.Error, + "Could not retrieve session details but no exception was thrown."); + } // TODO: Return a harmless object if necessary this.mostRecentSessionDetails = null; @@ -1781,21 +2187,46 @@ private void HandleRunspaceStateChanged(object sender, RunspaceStateEventArgs ar /// public event EventHandler DebuggerResumed; + private void StartCommandLoopOnRunspaceAvailable() + { + if (Interlocked.CompareExchange(ref this.isCommandLoopRestarterSet, 1, 1) == 1) + { + return; + } + + EventHandler handler = null; + handler = (runspace, eventArgs) => + { + if (eventArgs.RunspaceAvailability != RunspaceAvailability.Available || + this.versionSpecificOperations.IsDebuggerStopped(this.PromptNest, (Runspace)runspace)) + { + return; + } + + ((Runspace)runspace).AvailabilityChanged -= handler; + Interlocked.Exchange(ref this.isCommandLoopRestarterSet, 0); + this.ConsoleReader?.StartCommandLoop(); + }; + + this.CurrentRunspace.Runspace.AvailabilityChanged += handler; + Interlocked.Exchange(ref this.isCommandLoopRestarterSet, 1); + } + private void OnDebuggerStop(object sender, DebuggerStopEventArgs e) { - this.logger.Write(LogLevel.Verbose, "Debugger stopped execution."); + if (CurrentRunspace.Context == RunspaceContext.Original) + { + StartCommandLoopOnRunspaceAvailable(); + } - // Set the task so a result can be set - this.debuggerStoppedTask = - new TaskCompletionSource(); + this.logger.Write(LogLevel.Verbose, "Debugger stopped execution."); - // Save the pipeline thread ID and create the pipeline execution task - this.pipelineThreadId = Thread.CurrentThread.ManagedThreadId; - this.pipelineExecutionTask = new TaskCompletionSource(); + PromptNest.PushPromptContext( + IsCurrentRunspaceOutOfProcess() + ? PromptNestFrameType.Debug | PromptNestFrameType.Remote + : PromptNestFrameType.Debug); - // Hold on to local task vars so that the fields can be cleared independently - Task localDebuggerStoppedTask = this.debuggerStoppedTask.Task; - Task localPipelineExecutionTask = this.pipelineExecutionTask.Task; + ThreadController localThreadController = PromptNest.GetThreadController(); // Update the session state this.OnSessionStateChanged( @@ -1805,18 +2236,35 @@ private void OnDebuggerStop(object sender, DebuggerStopEventArgs e) PowerShellExecutionResult.Stopped, null)); - // Get the session details and push the current - // runspace if the session has changed - var sessionDetails = this.GetSessionDetailsInDebugger(); + // Get the session details and push the current + // runspace if the session has changed + SessionDetails sessionDetails = null; + try + { + sessionDetails = this.GetSessionDetailsInDebugger(); + } + catch (InvalidOperationException) + { + this.logger.Write( + LogLevel.Verbose, + "Attempting to get session details failed, most likely due to a running pipeline that is attempting to stop."); + } - // Push the current runspace if the session has changed - this.UpdateRunspaceDetailsIfSessionChanged(sessionDetails, isDebuggerStop: true); + if (!localThreadController.FrameExitTask.Task.IsCompleted) + { + // Push the current runspace if the session has changed + this.UpdateRunspaceDetailsIfSessionChanged(sessionDetails, isDebuggerStop: true); - // Raise the event for the debugger service - this.DebuggerStop?.Invoke(sender, e); + // Raise the event for the debugger service + this.DebuggerStop?.Invoke(sender, e); + } this.logger.Write(LogLevel.Verbose, "Starting pipeline thread message loop..."); + Task localPipelineExecutionTask = + localThreadController.TakeExecutionRequest(); + Task localDebuggerStoppedTask = + localThreadController.Exit(); while (true) { int taskIndex = @@ -1829,7 +2277,7 @@ private void OnDebuggerStop(object sender, DebuggerStopEventArgs e) // Write a new output line before continuing this.WriteOutput("", true); - e.ResumeAction = localDebuggerStoppedTask.Result; + e.ResumeAction = localDebuggerStoppedTask.GetAwaiter().GetResult(); this.logger.Write(LogLevel.Verbose, "Received debugger resume action " + e.ResumeAction.ToString()); // Notify listeners that the debugger has resumed @@ -1860,15 +2308,14 @@ private void OnDebuggerStop(object sender, DebuggerStopEventArgs e) this.logger.Write(LogLevel.Verbose, "Received pipeline thread execution request."); IPipelineExecutionRequest executionRequest = localPipelineExecutionTask.Result; - - this.pipelineExecutionTask = new TaskCompletionSource(); - localPipelineExecutionTask = this.pipelineExecutionTask.Task; - - executionRequest.Execute().Wait(); + localPipelineExecutionTask = localThreadController.TakeExecutionRequest(); + executionRequest.Execute().GetAwaiter().GetResult(); this.logger.Write(LogLevel.Verbose, "Pipeline thread execution completed."); - if (this.CurrentRunspace.Runspace.RunspaceAvailability == RunspaceAvailability.Available) + if (!this.versionSpecificOperations.IsDebuggerStopped( + this.PromptNest, + this.CurrentRunspace.Runspace)) { if (this.CurrentRunspace.Context == RunspaceContext.DebuggedRunspace) { @@ -1890,9 +2337,7 @@ private void OnDebuggerStop(object sender, DebuggerStopEventArgs e) } } - // Clear the task so that it won't be used again - this.debuggerStoppedTask = null; - this.pipelineExecutionTask = null; + PromptNest.PopPromptContext(); } // NOTE: This event is 'internal' because the DebugService provides @@ -1908,56 +2353,6 @@ private void OnBreakpointUpdated(object sender, BreakpointUpdatedEventArgs e) #region Nested Classes - private interface IPipelineExecutionRequest - { - Task Execute(); - } - - /// - /// Contains details relating to a request to execute a - /// command on the PowerShell pipeline thread. - /// - /// The expected result type of the execution. - private class PipelineExecutionRequest : IPipelineExecutionRequest - { - PowerShellContext powerShellContext; - PSCommand psCommand; - StringBuilder errorMessages; - bool sendOutputToHost; - TaskCompletionSource> resultsTask; - - public Task> Results - { - get { return this.resultsTask.Task; } - } - - public PipelineExecutionRequest( - PowerShellContext powerShellContext, - PSCommand psCommand, - StringBuilder errorMessages, - bool sendOutputToHost) - { - this.powerShellContext = powerShellContext; - this.psCommand = psCommand; - this.errorMessages = errorMessages; - this.sendOutputToHost = sendOutputToHost; - this.resultsTask = new TaskCompletionSource>(); - } - - public async Task Execute() - { - var results = - await this.powerShellContext.ExecuteCommand( - psCommand, - errorMessages, - sendOutputToHost); - - this.resultsTask.SetResult(results); - - // TODO: Deal with errors? - } - } - private void ConfigureRunspaceCapabilities(RunspaceDetails runspaceDetails) { DscBreakpointCapability.CheckForCapability(this.CurrentRunspace, this, this.logger); diff --git a/src/PowerShellEditorServices/Session/PromptNest.cs b/src/PowerShellEditorServices/Session/PromptNest.cs new file mode 100644 index 000000000..9cf4437f2 --- /dev/null +++ b/src/PowerShellEditorServices/Session/PromptNest.cs @@ -0,0 +1,564 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.PowerShell.EditorServices.Utility; + +namespace Microsoft.PowerShell.EditorServices.Session +{ + using System; + using System.Management.Automation; + + /// + /// Represents the stack of contexts in which PowerShell commands can be invoked. + /// + internal class PromptNest : IDisposable + { + private ConcurrentStack _frameStack; + + private PromptNestFrame _readLineFrame; + + private IHostInput _consoleReader; + + private PowerShellContext _powerShellContext; + + private IVersionSpecificOperations _versionSpecificOperations; + + private bool _isDisposed; + + private object _syncObject = new object(); + + private object _disposeSyncObject = new object(); + + /// + /// Initializes a new instance of the class. + /// + /// + /// The to track prompt status for. + /// + /// + /// The instance for the first frame. + /// + /// + /// The input handler. + /// + /// + /// The for the calling + /// instance. + /// + /// + /// This constructor should only be called when + /// is set to the initial runspace. + /// + internal PromptNest( + PowerShellContext powerShellContext, + PowerShell initialPowerShell, + IHostInput consoleReader, + IVersionSpecificOperations versionSpecificOperations) + { + _versionSpecificOperations = versionSpecificOperations; + _consoleReader = consoleReader; + _powerShellContext = powerShellContext; + _frameStack = new ConcurrentStack(); + _frameStack.Push( + new PromptNestFrame( + initialPowerShell, + NewHandleQueue())); + + var readLineShell = PowerShell.Create(); + readLineShell.Runspace = powerShellContext.CurrentRunspace.Runspace; + _readLineFrame = new PromptNestFrame( + readLineShell, + new AsyncQueue()); + + ReleaseRunspaceHandleImpl(isReadLine: true); + } + + /// + /// Gets a value indicating whether the current frame was created by a debugger stop event. + /// + internal bool IsInDebugger => CurrentFrame.FrameType.HasFlag(PromptNestFrameType.Debug); + + /// + /// Gets a value indicating whether the current frame was created for an out of process runspace. + /// + internal bool IsRemote => CurrentFrame.FrameType.HasFlag(PromptNestFrameType.Remote); + + /// + /// Gets a value indicating whether the current frame was created by PSHost.EnterNestedPrompt(). + /// + internal bool IsNestedPrompt => CurrentFrame.FrameType.HasFlag(PromptNestFrameType.NestedPrompt); + + /// + /// Gets a value indicating the current number of frames managed by this PromptNest. + /// + internal int NestedPromptLevel => _frameStack.Count; + + private PromptNestFrame CurrentFrame + { + get + { + _frameStack.TryPeek(out PromptNestFrame currentFrame); + return _isDisposed ? _readLineFrame : currentFrame; + } + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + lock (_disposeSyncObject) + { + if (_isDisposed || !disposing) + { + return; + } + + while (NestedPromptLevel > 1) + { + _consoleReader?.StopCommandLoop(); + var currentFrame = CurrentFrame; + if (currentFrame.FrameType.HasFlag(PromptNestFrameType.Debug)) + { + _versionSpecificOperations.StopCommandInDebugger(_powerShellContext); + currentFrame.ThreadController.StartThreadExit(DebuggerResumeAction.Stop); + currentFrame.WaitForFrameExit(CancellationToken.None); + continue; + } + + if (currentFrame.FrameType.HasFlag(PromptNestFrameType.NestedPrompt)) + { + _powerShellContext.ExitAllNestedPrompts(); + continue; + } + + currentFrame.PowerShell.BeginStop(null, null); + currentFrame.WaitForFrameExit(CancellationToken.None); + } + + _consoleReader?.StopCommandLoop(); + _readLineFrame.Dispose(); + CurrentFrame.Dispose(); + _frameStack.Clear(); + _powerShellContext = null; + _consoleReader = null; + _isDisposed = true; + } + } + + /// + /// Gets the for the current frame. + /// + /// + /// The for the current frame, or + /// if the current frame does not have one. + /// + internal ThreadController GetThreadController() + { + if (_isDisposed) + { + return null; + } + + return CurrentFrame.IsThreadController ? CurrentFrame.ThreadController : null; + } + + /// + /// Create a new and set it as the current frame. + /// + internal void PushPromptContext() + { + if (_isDisposed) + { + return; + } + + PushPromptContext(PromptNestFrameType.Normal); + } + + /// + /// Create a new and set it as the current frame. + /// + /// The frame type. + internal void PushPromptContext(PromptNestFrameType frameType) + { + if (_isDisposed) + { + return; + } + + _frameStack.Push( + new PromptNestFrame( + frameType.HasFlag(PromptNestFrameType.Remote) + ? PowerShell.Create() + : PowerShell.Create(RunspaceMode.CurrentRunspace), + NewHandleQueue(), + frameType)); + } + + /// + /// Dispose of the current and revert to the previous frame. + /// + internal void PopPromptContext() + { + PromptNestFrame currentFrame; + lock (_syncObject) + { + if (_isDisposed || _frameStack.Count == 1) + { + return; + } + + _frameStack.TryPop(out currentFrame); + } + + currentFrame.Dispose(); + } + + /// + /// Get the instance for the current + /// . + /// + /// Indicates whether this is for a PSReadLine command. + /// The instance for the current frame. + internal PowerShell GetPowerShell(bool isReadLine = false) + { + if (_isDisposed) + { + return null; + } + + // Typically we want to run PSReadLine on the current nest frame. + // The exception is when the current frame is remote, in which + // case we need to run it in it's own frame because we can't take + // over a remote pipeline through event invocation. + if (NestedPromptLevel > 1 && !IsRemote) + { + return CurrentFrame.PowerShell; + } + + return isReadLine ? _readLineFrame.PowerShell : CurrentFrame.PowerShell; + } + + /// + /// Get the for the current . + /// + /// + /// The that can be used to cancel the request. + /// + /// Indicates whether this is for a PSReadLine command. + /// The for the current frame. + internal RunspaceHandle GetRunspaceHandle(CancellationToken cancellationToken, bool isReadLine) + { + if (_isDisposed) + { + return null; + } + + // Also grab the main runspace handle if this is for a ReadLine pipeline and the runspace + // is in process. + if (isReadLine && !_powerShellContext.IsCurrentRunspaceOutOfProcess()) + { + GetRunspaceHandleImpl(cancellationToken, isReadLine: false); + } + + return GetRunspaceHandleImpl(cancellationToken, isReadLine); + } + + + /// + /// Get the for the current . + /// + /// + /// The that will be checked prior to + /// completing the returned task. + /// + /// Indicates whether this is for a PSReadLine command. + /// + /// A object representing the asynchronous operation. + /// The property will return the + /// for the current frame. + /// + internal async Task GetRunspaceHandleAsync(CancellationToken cancellationToken, bool isReadLine) + { + if (_isDisposed) + { + return null; + } + + // Also grab the main runspace handle if this is for a ReadLine pipeline and the runspace + // is in process. + if (isReadLine && !_powerShellContext.IsCurrentRunspaceOutOfProcess()) + { + await GetRunspaceHandleImplAsync(cancellationToken, isReadLine: false); + } + + return await GetRunspaceHandleImplAsync(cancellationToken, isReadLine); + } + + /// + /// Releases control of the runspace aquired via the . + /// + /// + /// The representing the control to release. + /// + internal void ReleaseRunspaceHandle(RunspaceHandle runspaceHandle) + { + if (_isDisposed) + { + return; + } + + ReleaseRunspaceHandleImpl(runspaceHandle.IsReadLine); + if (runspaceHandle.IsReadLine && !_powerShellContext.IsCurrentRunspaceOutOfProcess()) + { + ReleaseRunspaceHandleImpl(isReadLine: false); + } + } + + /// + /// Releases control of the runspace aquired via the . + /// + /// + /// The representing the control to release. + /// + /// + /// A object representing the release of the + /// . + /// + internal async Task ReleaseRunspaceHandleAsync(RunspaceHandle runspaceHandle) + { + if (_isDisposed) + { + return; + } + + await ReleaseRunspaceHandleImplAsync(runspaceHandle.IsReadLine); + if (runspaceHandle.IsReadLine && !_powerShellContext.IsCurrentRunspaceOutOfProcess()) + { + await ReleaseRunspaceHandleImplAsync(isReadLine: false); + } + } + + /// + /// Determines if the current frame is unavailable for commands. + /// + /// + /// A value indicating whether the current frame is unavailable for commands. + /// + internal bool IsMainThreadBusy() + { + return !_isDisposed && CurrentFrame.Queue.IsEmpty; + } + + /// + /// Determines if a PSReadLine command is currently running. + /// + /// + /// A value indicating whether a PSReadLine command is currently running. + /// + internal bool IsReadLineBusy() + { + return !_isDisposed && _readLineFrame.Queue.IsEmpty; + } + + /// + /// Blocks until the current frame has been disposed. + /// + /// + /// A delegate that when invoked initates the exit of the current frame. + /// + internal void WaitForCurrentFrameExit(Action initiator) + { + if (_isDisposed) + { + return; + } + + var currentFrame = CurrentFrame; + try + { + initiator.Invoke(currentFrame); + } + finally + { + currentFrame.WaitForFrameExit(CancellationToken.None); + } + } + + /// + /// Blocks until the current frame has been disposed. + /// + internal void WaitForCurrentFrameExit() + { + if (_isDisposed) + { + return; + } + + CurrentFrame.WaitForFrameExit(CancellationToken.None); + } + + /// + /// Blocks until the current frame has been disposed. + /// + /// + /// The used the exit the block prior to + /// the current frame being disposed. + /// + internal void WaitForCurrentFrameExit(CancellationToken cancellationToken) + { + if (_isDisposed) + { + return; + } + + CurrentFrame.WaitForFrameExit(cancellationToken); + } + + /// + /// Creates a task that is completed when the current frame has been disposed. + /// + /// + /// A delegate that when invoked initates the exit of the current frame. + /// + /// + /// A object representing the current frame being disposed. + /// + internal async Task WaitForCurrentFrameExitAsync(Func initiator) + { + if (_isDisposed) + { + return; + } + + var currentFrame = CurrentFrame; + try + { + await initiator.Invoke(currentFrame); + } + finally + { + await currentFrame.WaitForFrameExitAsync(CancellationToken.None); + } + } + + /// + /// Creates a task that is completed when the current frame has been disposed. + /// + /// + /// A delegate that when invoked initates the exit of the current frame. + /// + /// + /// A object representing the current frame being disposed. + /// + internal async Task WaitForCurrentFrameExitAsync(Action initiator) + { + if (_isDisposed) + { + return; + } + + var currentFrame = CurrentFrame; + try + { + initiator.Invoke(currentFrame); + } + finally + { + await currentFrame.WaitForFrameExitAsync(CancellationToken.None); + } + } + + /// + /// Creates a task that is completed when the current frame has been disposed. + /// + /// + /// A object representing the current frame being disposed. + /// + internal async Task WaitForCurrentFrameExitAsync() + { + if (_isDisposed) + { + return; + } + + await WaitForCurrentFrameExitAsync(CancellationToken.None); + } + + /// + /// Creates a task that is completed when the current frame has been disposed. + /// + /// + /// The used the exit the block prior to the current frame being disposed. + /// + /// + /// A object representing the current frame being disposed. + /// + internal async Task WaitForCurrentFrameExitAsync(CancellationToken cancellationToken) + { + if (_isDisposed) + { + return; + } + + await CurrentFrame.WaitForFrameExitAsync(cancellationToken); + } + + private AsyncQueue NewHandleQueue() + { + var queue = new AsyncQueue(); + queue.Enqueue(new RunspaceHandle(_powerShellContext)); + return queue; + } + + private RunspaceHandle GetRunspaceHandleImpl(CancellationToken cancellationToken, bool isReadLine) + { + if (isReadLine) + { + return _readLineFrame.Queue.Dequeue(cancellationToken); + } + + return CurrentFrame.Queue.Dequeue(cancellationToken); + } + + private async Task GetRunspaceHandleImplAsync(CancellationToken cancellationToken, bool isReadLine) + { + if (isReadLine) + { + return await _readLineFrame.Queue.DequeueAsync(cancellationToken); + } + + return await CurrentFrame.Queue.DequeueAsync(cancellationToken); + } + + private void ReleaseRunspaceHandleImpl(bool isReadLine) + { + if (isReadLine) + { + _readLineFrame.Queue.Enqueue(new RunspaceHandle(_powerShellContext, true)); + return; + } + + CurrentFrame.Queue.Enqueue(new RunspaceHandle(_powerShellContext, false)); + } + + private async Task ReleaseRunspaceHandleImplAsync(bool isReadLine) + { + if (isReadLine) + { + await _readLineFrame.Queue.EnqueueAsync(new RunspaceHandle(_powerShellContext, true)); + return; + } + + await CurrentFrame.Queue.EnqueueAsync(new RunspaceHandle(_powerShellContext, false)); + } + } +} diff --git a/src/PowerShellEditorServices/Session/PromptNestFrame.cs b/src/PowerShellEditorServices/Session/PromptNestFrame.cs new file mode 100644 index 000000000..cae7dfb8a --- /dev/null +++ b/src/PowerShellEditorServices/Session/PromptNestFrame.cs @@ -0,0 +1,137 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.PowerShell.EditorServices.Utility; + +namespace Microsoft.PowerShell.EditorServices.Session +{ + using System.Management.Automation; + + /// + /// Represents a single frame in the . + /// + internal class PromptNestFrame : IDisposable + { + private const PSInvocationState IndisposableStates = PSInvocationState.Stopping | PSInvocationState.Running; + + private SemaphoreSlim _frameExited = new SemaphoreSlim(initialCount: 0); + + private bool _isDisposed = false; + + /// + /// Gets the instance. + /// + internal PowerShell PowerShell { get; } + + /// + /// Gets the queue that controls command invocation order. + /// + internal AsyncQueue Queue { get; } + + /// + /// Gets the frame type. + /// + internal PromptNestFrameType FrameType { get; } + + /// + /// Gets the . + /// + internal ThreadController ThreadController { get; } + + /// + /// Gets a value indicating whether the frame requires command invocations + /// to be routed to a specific thread. + /// + internal bool IsThreadController { get; } + + internal PromptNestFrame(PowerShell powerShell, AsyncQueue handleQueue) + : this(powerShell, handleQueue, PromptNestFrameType.Normal) + { } + + internal PromptNestFrame( + PowerShell powerShell, + AsyncQueue handleQueue, + PromptNestFrameType frameType) + { + PowerShell = powerShell; + Queue = handleQueue; + FrameType = frameType; + IsThreadController = (frameType & (PromptNestFrameType.Debug | PromptNestFrameType.NestedPrompt)) != 0; + if (!IsThreadController) + { + return; + } + + ThreadController = new ThreadController(this); + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (_isDisposed) + { + return; + } + + if (disposing) + { + if (IndisposableStates.HasFlag(PowerShell.InvocationStateInfo.State)) + { + PowerShell.BeginStop( + asyncResult => + { + PowerShell.Runspace = null; + PowerShell.Dispose(); + }, + state: null); + } + else + { + PowerShell.Runspace = null; + PowerShell.Dispose(); + } + + _frameExited.Release(); + } + + _isDisposed = true; + } + + /// + /// Blocks until the frame has been disposed. + /// + /// + /// The that will exit the block when cancelled. + /// + internal void WaitForFrameExit(CancellationToken cancellationToken) + { + _frameExited.Wait(cancellationToken); + _frameExited.Release(); + } + + /// + /// Creates a task object that is completed when the frame has been disposed. + /// + /// + /// The that will be checked prior to completing + /// the returned task. + /// + /// + /// A object that represents this frame being disposed. + /// + internal async Task WaitForFrameExitAsync(CancellationToken cancellationToken) + { + await _frameExited.WaitAsync(cancellationToken); + _frameExited.Release(); + } + } +} diff --git a/src/PowerShellEditorServices/Session/PromptNestFrameType.cs b/src/PowerShellEditorServices/Session/PromptNestFrameType.cs new file mode 100644 index 000000000..b42b42098 --- /dev/null +++ b/src/PowerShellEditorServices/Session/PromptNestFrameType.cs @@ -0,0 +1,21 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; + +namespace Microsoft.PowerShell.EditorServices.Session +{ + [Flags] + internal enum PromptNestFrameType + { + Normal = 0, + + NestedPrompt = 1, + + Debug = 2, + + Remote = 4 + } +} diff --git a/src/PowerShellEditorServices/Session/RunspaceHandle.cs b/src/PowerShellEditorServices/Session/RunspaceHandle.cs index b7fc0e8f1..4947eadbe 100644 --- a/src/PowerShellEditorServices/Session/RunspaceHandle.cs +++ b/src/PowerShellEditorServices/Session/RunspaceHandle.cs @@ -28,14 +28,21 @@ public Runspace Runspace } } + internal bool IsReadLine { get; } + /// /// Initializes a new instance of the RunspaceHandle class using the /// given runspace. /// /// The PowerShellContext instance which manages the runspace. public RunspaceHandle(PowerShellContext powerShellContext) + : this(powerShellContext, false) + { } + + internal RunspaceHandle(PowerShellContext powerShellContext, bool isReadLine) { this.powerShellContext = powerShellContext; + this.IsReadLine = isReadLine; } /// diff --git a/src/PowerShellEditorServices/Session/ThreadController.cs b/src/PowerShellEditorServices/Session/ThreadController.cs new file mode 100644 index 000000000..9a5583f5b --- /dev/null +++ b/src/PowerShellEditorServices/Session/ThreadController.cs @@ -0,0 +1,131 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Collections.Generic; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.PowerShell.EditorServices.Utility; + +namespace Microsoft.PowerShell.EditorServices.Session +{ + /// + /// Provides the ability to route PowerShell command invocations to a specific thread. + /// + internal class ThreadController + { + private PromptNestFrame _nestFrame; + + internal AsyncQueue PipelineRequestQueue { get; } + + internal TaskCompletionSource FrameExitTask { get; } + + internal int ManagedThreadId { get; } + + internal bool IsPipelineThread { get; } + + /// + /// Initializes an new instance of the ThreadController class. This constructor should only + /// ever been called from the thread it is meant to control. + /// + /// The parent PromptNestFrame object. + internal ThreadController(PromptNestFrame nestFrame) + { + _nestFrame = nestFrame; + PipelineRequestQueue = new AsyncQueue(); + FrameExitTask = new TaskCompletionSource(); + ManagedThreadId = Thread.CurrentThread.ManagedThreadId; + + // If the debugger stop is triggered on a thread with no default runspace we + // shouldn't attempt to route commands to it. + IsPipelineThread = Runspace.DefaultRunspace != null; + } + + /// + /// Determines if the caller is already on the thread that this object maintains. + /// + /// + /// A value indicating if the caller is already on the thread maintained by this object. + /// + internal bool IsCurrentThread() + { + return Thread.CurrentThread.ManagedThreadId == ManagedThreadId; + } + + /// + /// Requests the invocation of a PowerShell command on the thread maintained by this object. + /// + /// The execution request to send. + /// + /// A task object representing the asynchronous operation. The Result property will return + /// the output of the command invocation. + /// + internal async Task> RequestPipelineExecution( + PipelineExecutionRequest executionRequest) + { + await PipelineRequestQueue.EnqueueAsync(executionRequest); + return await executionRequest.Results; + } + + /// + /// Retrieves the first currently queued execution request. If there are no pending + /// execution requests then the task will be completed when one is requested. + /// + /// + /// A task object representing the asynchronous operation. The Result property will return + /// the retrieved pipeline execution request. + /// + internal async Task TakeExecutionRequest() + { + return await PipelineRequestQueue.DequeueAsync(); + } + + /// + /// Marks the thread to be exited. + /// + /// + /// The resume action for the debugger. If the frame is not a debugger frame this parameter + /// is ignored. + /// + internal void StartThreadExit(DebuggerResumeAction action) + { + StartThreadExit(action, waitForExit: false); + } + + /// + /// Marks the thread to be exited. + /// + /// + /// The resume action for the debugger. If the frame is not a debugger frame this parameter + /// is ignored. + /// + /// + /// Indicates whether the method should block until the exit is completed. + /// + internal void StartThreadExit(DebuggerResumeAction action, bool waitForExit) + { + Task.Run(() => FrameExitTask.TrySetResult(action)); + if (!waitForExit) + { + return; + } + + _nestFrame.WaitForFrameExit(CancellationToken.None); + } + + /// + /// Creates a task object that completes when the thread has be marked for exit. + /// + /// + /// A task object representing the frame receiving a request to exit. The Result property + /// will return the DebuggerResumeAction supplied with the request. + /// + internal async Task Exit() + { + return await FrameExitTask.Task.ConfigureAwait(false); + } + } +} diff --git a/src/PowerShellEditorServices/Utility/AsyncLock.cs b/src/PowerShellEditorServices/Utility/AsyncLock.cs index eee894d9c..5eba1b24f 100644 --- a/src/PowerShellEditorServices/Utility/AsyncLock.cs +++ b/src/PowerShellEditorServices/Utility/AsyncLock.cs @@ -74,6 +74,31 @@ public Task LockAsync(CancellationToken cancellationToken) TaskScheduler.Default); } + /// + /// Obtains or waits for a lock which can be used to synchronize + /// access to a resource. + /// + /// + public IDisposable Lock() + { + return Lock(CancellationToken.None); + } + + /// + /// Obtains or waits for a lock which can be used to synchronize + /// access to a resource. The wait may be cancelled with the + /// given CancellationToken. + /// + /// + /// A CancellationToken which can be used to cancel the lock. + /// + /// + public IDisposable Lock(CancellationToken cancellationToken) + { + lockSemaphore.Wait(cancellationToken); + return this.lockReleaseTask.Result; + } + #endregion #region Private Classes diff --git a/src/PowerShellEditorServices/Utility/AsyncQueue.cs b/src/PowerShellEditorServices/Utility/AsyncQueue.cs index 98c00dc8e..85bbc1592 100644 --- a/src/PowerShellEditorServices/Utility/AsyncQueue.cs +++ b/src/PowerShellEditorServices/Utility/AsyncQueue.cs @@ -87,13 +87,38 @@ public async Task EnqueueAsync(T item) return; } } - + // No more requests waiting, queue the item for a later request this.itemQueue.Enqueue(item); this.IsEmpty = false; } } + /// + /// Enqueues an item onto the end of the queue. + /// + /// The item to be added to the queue. + public void Enqueue(T item) + { + using (queueLock.Lock()) + { + while (this.requestQueue.Count > 0) + { + var requestTaskSource = this.requestQueue.Dequeue(); + if (requestTaskSource.Task.IsCanceled) + { + continue; + } + + requestTaskSource.SetResult(item); + return; + } + } + + this.itemQueue.Enqueue(item); + this.IsEmpty = false; + } + /// /// Dequeues an item from the queue or waits asynchronously /// until an item is available. @@ -149,6 +174,50 @@ public async Task DequeueAsync(CancellationToken cancellationToken) return await requestTask; } + /// + /// Dequeues an item from the queue or waits asynchronously + /// until an item is available. + /// + /// + public T Dequeue() + { + return Dequeue(CancellationToken.None); + } + + /// + /// Dequeues an item from the queue or waits asynchronously + /// until an item is available. The wait can be cancelled + /// using the given CancellationToken. + /// + /// + /// A CancellationToken with which a dequeue wait can be cancelled. + /// + /// + public T Dequeue(CancellationToken cancellationToken) + { + TaskCompletionSource requestTask; + using (queueLock.Lock(cancellationToken)) + { + if (this.itemQueue.Count > 0) + { + T item = this.itemQueue.Dequeue(); + this.IsEmpty = this.itemQueue.Count == 0; + + return item; + } + + requestTask = new TaskCompletionSource(); + this.requestQueue.Enqueue(requestTask); + + if (cancellationToken.CanBeCanceled) + { + cancellationToken.Register(() => requestTask.TrySetCanceled()); + } + } + + return requestTask.Task.GetAwaiter().GetResult(); + } + #endregion } } diff --git a/src/PowerShellEditorServices/Utility/AsyncUtils.cs b/src/PowerShellEditorServices/Utility/AsyncUtils.cs new file mode 100644 index 000000000..8da21b942 --- /dev/null +++ b/src/PowerShellEditorServices/Utility/AsyncUtils.cs @@ -0,0 +1,25 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Threading; + +namespace Microsoft.PowerShell.EditorServices.Utility +{ + /// + /// Provides utility methods for common asynchronous operations. + /// + internal static class AsyncUtils + { + /// + /// Creates a with an handle initial and + /// max count of one. + /// + /// A simple single handle . + internal static SemaphoreSlim CreateSimpleLockingSemaphore() + { + return new SemaphoreSlim(initialCount: 1, maxCount: 1); + } + } +} diff --git a/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs b/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs index 18ce3cf30..11aecad3f 100644 --- a/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs @@ -72,9 +72,12 @@ void debugService_BreakpointUpdated(object sender, BreakpointUpdatedEventArgs e) // TODO: Needed? } - async void debugService_DebuggerStopped(object sender, DebuggerStoppedEventArgs e) + void debugService_DebuggerStopped(object sender, DebuggerStoppedEventArgs e) { - await this.debuggerStoppedQueue.EnqueueAsync(e); + // We need to ensure this is run on a different thread than the one it's + // called on because it can cause PowerShellContext.OnDebuggerStopped to + // never hit the while loop. + Task.Run(() => this.debuggerStoppedQueue.Enqueue(e)); } public void Dispose() @@ -491,9 +494,10 @@ await this.AssertStateChange( // Abort execution and wait for the debugger to exit this.debugService.Abort(); + await this.AssertStateChange( PowerShellContextState.Ready, - PowerShellExecutionResult.Aborted); + PowerShellExecutionResult.Stopped); } [Fact] @@ -514,9 +518,10 @@ await this.AssertStateChange( // Abort execution and wait for the debugger to exit this.debugService.Abort(); + await this.AssertStateChange( PowerShellContextState.Ready, - PowerShellExecutionResult.Aborted); + PowerShellExecutionResult.Stopped); } [Fact] @@ -908,7 +913,7 @@ public async Task AssertDebuggerPaused() SynchronizationContext syncContext = SynchronizationContext.Current; DebuggerStoppedEventArgs eventArgs = - await this.debuggerStoppedQueue.DequeueAsync(); + await this.debuggerStoppedQueue.DequeueAsync(new CancellationTokenSource(5000).Token); Assert.Equal(0, eventArgs.OriginalEvent.Breakpoints.Count); } @@ -920,7 +925,7 @@ public async Task AssertDebuggerStopped( SynchronizationContext syncContext = SynchronizationContext.Current; DebuggerStoppedEventArgs eventArgs = - await this.debuggerStoppedQueue.DequeueAsync(); + await this.debuggerStoppedQueue.DequeueAsync(new CancellationTokenSource(5000).Token); @@ -936,7 +941,7 @@ private async Task AssertStateChange( PowerShellExecutionResult expectedResult = PowerShellExecutionResult.Completed) { SessionStateChangedEventArgs newState = - await this.sessionStateQueue.DequeueAsync(); + await this.sessionStateQueue.DequeueAsync(new CancellationTokenSource(5000).Token); Assert.Equal(expectedState, newState.NewSessionState); Assert.Equal(expectedResult, newState.ExecutionResult); diff --git a/test/PowerShellEditorServices.Test/PowerShellContextFactory.cs b/test/PowerShellEditorServices.Test/PowerShellContextFactory.cs index 806a935b3..22a41d2e9 100644 --- a/test/PowerShellEditorServices.Test/PowerShellContextFactory.cs +++ b/test/PowerShellEditorServices.Test/PowerShellContextFactory.cs @@ -19,7 +19,7 @@ internal static class PowerShellContextFactory { public static PowerShellContext Create(ILogger logger) { - PowerShellContext powerShellContext = new PowerShellContext(logger); + PowerShellContext powerShellContext = new PowerShellContext(logger, isPSReadLineEnabled: false); powerShellContext.Initialize( PowerShellContextTests.TestProfilePaths, PowerShellContext.CreateRunspace( From 449cc21d30ccfc0a7b14fe01e7cf7d778c4f8307 Mon Sep 17 00:00:00 2001 From: Robert Holt Date: Mon, 1 Oct 2018 17:15:47 -0700 Subject: [PATCH 2/5] Migrate to netstandard2.0 and PSStandard (#741) * Move PSES builds to netstandard and use PSStandard APIs * Add shim modules for Windows PowerShell and PowerShell Core 6.0 * Migrate tests to run on .NET Core and on *nix * Make build file more declarative * Use latest xunit * Exclude host integration tests * Clean out csproj files * Fix up Travis build * Retire compatibility APIs for PowerShell v3/4 * Introduce test hook for using InitialSessionState.CreateDefault2() when running on PowerShell Core SDK * Fix session details gathering in nested prompts To create a nested PowerShell instance you can't set the runspace because the target runspace is assumed to be the runspace in TLS. [Ignore] Fix test typos [Ignore] Use correct AppVeyor version --- .travis.yml | 13 +- PowerShellEditorServices.Common.props | 3 +- PowerShellEditorServices.build.ps1 | 305 ++++++++++++------ appveyor.yml | 2 +- .../PowerShellEditorServices.VSCode.psm1 | 7 +- .../PowerShellEditorServices.psd1 | 2 +- .../PowerShellEditorServices.psm1 | 21 +- scripts/travis.ps1 | 9 +- .../EditorServicesHost.cs | 22 +- .../PowerShellEditorServices.Host.csproj | 25 +- .../Channel/NamedPipeClientChannel.cs | 17 - .../Channel/StdioServerChannel.cs | 14 +- .../PowerShellEditorServices.Protocol.csproj | 22 +- .../Server/DebugAdapter.cs | 5 - .../PowerShellEditorServices.VSCode.csproj | 12 +- .../Analysis/AnalysisService.cs | 7 +- .../Console/ConsoleProxy.cs | 5 - .../Language/CompletionResults.cs | 2 - .../Language/FindSymbolsVisitor2.cs | 3 - .../Language/LanguageService.cs | 1 - .../PowerShellEditorServices.csproj | 59 +--- .../Session/InvocationEventQueue.cs | 5 + .../Session/PSReadLinePromptContext.cs | 2 - .../Session/PowerShell3Operations.cs | 96 ------ .../Session/PowerShell4Operations.cs | 112 ------- .../Session/PowerShell5Operations.cs | 92 +++++- .../Session/PowerShellContext.cs | 83 ++--- .../Workspace/ScriptFile.cs | 12 +- .../DebugAdapterTests.cs | 7 +- .../LanguageServerTests.cs | 81 +++-- .../PowerShellEditorServices.Test.Host.csproj | 35 +- .../ServerTestsBase.cs | 21 +- ...erShellEditorServices.Test.Protocol.csproj | 34 +- .../Server/OutputDebouncerTests.cs | 9 +- .../Completion/CompleteAttributeValue.cs | 4 +- .../Completion/CompleteCommandFromModule.cs | 17 +- .../Completion/CompleteCommandInFile.cs | 4 +- .../Completion/CompleteFilePath.cs | 6 +- .../Completion/CompleteVariableInFile.cs | 4 +- .../Completion/CompletionExamples.psm1 | 4 +- .../Debugging/VariableTest.ps1 | 2 +- .../Definition/FindsDotSourcedFile.cs | 2 +- .../Definition/FindsFunctionDefinition.cs | 2 +- ...sFunctionDefinitionInDotSourceReference.cs | 2 +- .../FindsFunctionDefinitionInWorkspace.cs | 2 +- .../Definition/FindsVariableDefinition.cs | 2 +- .../Occurrences/FindOccurrencesOnParameter.cs | 2 +- .../Occurrences/FindsOccurrencesOnFunction.cs | 2 +- .../FindsParameterSetsOnCommand.cs | 2 +- .../FindsParameterSetsOnCommandWithSpaces.cs | 2 +- ...owerShellEditorServices.Test.Shared.csproj | 17 +- .../References/FileWithReferences.ps1 | 2 +- ...indsReferencesOnBuiltInCommandWithAlias.cs | 4 +- .../References/FindsReferencesOnFunction.cs | 2 +- ...sReferencesOnFunctionMultiFileDotSource.cs | 4 +- .../References/FindsReferencesonVariable.cs | 2 +- .../References/ReferenceFileA.ps1 | 4 +- .../References/ReferenceFileC.ps1 | 4 +- .../References/SimpleFile.ps1 | 4 +- .../FindsDetailsForBuiltInCommand.cs | 2 +- .../Symbols/FindSymbolsInMultiSymbolFile.cs | 2 +- .../Symbols/FindSymbolsInNoSymbolsFile.cs | 2 +- .../Symbols/FindSymbolsInPSDFile.cs | 2 +- .../Symbols/FindSymbolsInPesterFile.cs | 2 +- .../TestUtilities/TestUtilities.cs | 90 ++++++ .../Debugging/DebugServiceTests.cs | 58 ++-- .../Extensions/ExtensionServiceTests.cs | 16 +- .../Language/LanguageServiceTests.cs | 34 +- .../PowerShellEditorServices.Test.csproj | 40 ++- .../Session/PowerShellContextTests.cs | 11 +- .../Session/ScriptFileTests.cs | 73 +++-- .../Session/WorkspaceTests.cs | 76 +++-- .../Utility/AsyncDebouncerTests.cs | 6 +- .../Utility/LoggerTests.cs | 3 +- 74 files changed, 811 insertions(+), 853 deletions(-) delete mode 100644 src/PowerShellEditorServices/Session/PowerShell3Operations.cs delete mode 100644 src/PowerShellEditorServices/Session/PowerShell4Operations.cs create mode 100644 test/PowerShellEditorServices.Test.Shared/TestUtilities/TestUtilities.cs diff --git a/.travis.yml b/.travis.yml index 8209d54c8..015e2dae1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,12 +3,13 @@ language: cpp git: depth: 1000 -os: - - linux - - osx -sudo: required -dist: trusty -osx_image: xcode8.3 +matrix: + include: + - os: linux + dist: trusty + sudo: required + - os: osx + osx_image: xcode9.4 before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then diff --git a/PowerShellEditorServices.Common.props b/PowerShellEditorServices.Common.props index baaba85ea..751a5f07e 100644 --- a/PowerShellEditorServices.Common.props +++ b/PowerShellEditorServices.Common.props @@ -1,6 +1,6 @@ - 1.10.3 + 2.0.0 Microsoft © Microsoft Corporation. All rights reserved. PowerShell;editor;development;language;debugging @@ -9,6 +9,5 @@ git https://github.com/PowerShell/PowerShellEditorServices portable - 1.0.3 diff --git a/PowerShellEditorServices.build.ps1 b/PowerShellEditorServices.build.ps1 index 02dddb7fa..0b36d5176 100644 --- a/PowerShellEditorServices.build.ps1 +++ b/PowerShellEditorServices.build.ps1 @@ -20,10 +20,96 @@ param( $script:IsCIBuild = $env:APPVEYOR -ne $null $script:IsUnix = $PSVersionTable.PSEdition -and $PSVersionTable.PSEdition -eq "Core" -and !$IsWindows -$script:TargetFrameworksParam = "/p:TargetFrameworks=\`"$(if (!$script:IsUnix) { "net452;" })netstandard1.6\`"" -$script:SaveModuleSupportsAllowPrerelease = (Get-Command Save-Module).Parameters.ContainsKey("AllowPrerelease") +$script:TargetPlatform = "netstandard2.0" +$script:TargetFrameworksParam = "/p:TargetFrameworks=`"$script:TargetPlatform`"" +$script:RequiredSdkVersion = "2.1.402" +$script:NugetApiUriBase = 'https://www.nuget.org/api/v2/package' +$script:ModuleBinPath = "$PSScriptRoot/module/PowerShellEditorServices/bin/" +$script:VSCodeModuleBinPath = "$PSScriptRoot/module/PowerShellEditorServices.VSCode/bin/" +$script:WindowsPowerShellFrameworkTarget = 'net461' +$script:NetFrameworkPlatformId = 'win' +$script:NetCoreTestingFrameworkVersion = '2.1.4' $script:BuildInfoPath = [System.IO.Path]::Combine($PSScriptRoot, "src", "PowerShellEditorServices.Host", "BuildInfo", "BuildInfo.cs") +$script:PSCoreModulePath = $null + +$script:TestRuntime = @{ + 'Core' = 'netcoreapp2.1' + 'Desktop' = 'net461' +} + +<# +Declarative specification of binary assets produced +in the build that need to be binplaced in the module. +Schema is: +{ + : { + : [ + + ] + } +} +#> +$script:RequiredBuildAssets = @{ + $script:ModuleBinPath = @{ + 'PowerShellEditorServices' = @( + 'publish/Serilog.dll', + 'publish/Serilog.Sinks.Async.dll', + 'publish/Serilog.Sinks.Console.dll', + 'publish/Serilog.Sinks.File.dll', + 'Microsoft.PowerShell.EditorServices.dll', + 'Microsoft.PowerShell.EditorServices.pdb' + ) + + 'PowerShellEditorServices.Host' = @( + 'publish/UnixConsoleEcho.dll', + 'publish/runtimes/osx-64/native/libdisablekeyecho.dylib', + 'publish/runtimes/linux-64/native/libdisablekeyecho.so', + 'publish/Newtonsoft.Json.dll', + 'Microsoft.PowerShell.EditorServices.Host.dll', + 'Microsoft.PowerShell.EditorServices.Host.pdb' + ) + + 'PowerShellEditorServices.Protocol' = @( + 'Microsoft.PowerShell.EditorServices.Protocol.dll', + 'Microsoft.PowerShell.EditorServices.Protocol.pdb' + ) + } + + $script:VSCodeModuleBinPath = @{ + 'PowerShellEditorServices.VSCode' = @( + 'Microsoft.PowerShell.EditorServices.VSCode.dll', + 'Microsoft.PowerShell.EditorServices.VSCode.pdb' + ) + } +} + +<# +Declares the binary shims we need to make the netstandard DLLs hook into .NET Framework. +Schema is: +{ + : [{ + 'PackageName': , + 'PackageVersion': , + 'TargetRuntime': , + 'DllName'?: + }] +} +#> +$script:RequiredNugetBinaries = @{ + 'Desktop' = @( + @{ PackageName = 'System.Security.Principal.Windows'; PackageVersion = '4.5.0'; TargetRuntime = 'net461' }, + @{ PackageName = 'System.Security.AccessControl'; PackageVersion = '4.5.0'; TargetRuntime = 'net461' }, + @{ PackageName = 'System.IO.Pipes.AccessControl'; PackageVersion = '4.5.1'; TargetRuntime = 'net461' } + ) + + '6.0' = @( + @{ PackageName = 'System.Security.Principal.Windows'; PackageVersion = '4.5.0'; TargetRuntime = 'netcoreapp2.0' }, + @{ PackageName = 'System.Security.AccessControl'; PackageVersion = '4.5.0'; TargetRuntime = 'netcoreapp2.0' }, + @{ PackageName = 'System.IO.Pipes.AccessControl'; PackageVersion = '4.5.1'; TargetRuntime = 'netstandard2.0' } + ) +} + if (Get-Command git -ErrorAction SilentlyContinue) { # ignore changes to this file git update-index --assume-unchanged "$PSScriptRoot/src/PowerShellEditorServices.Host/BuildInfo/BuildInfo.cs" @@ -33,9 +119,65 @@ if ($PSVersionTable.PSEdition -ne "Core") { Add-Type -Assembly System.IO.Compression.FileSystem } -task SetupDotNet -Before Clean, Build, TestHost, TestServer, TestProtocol, TestPowerShellApi, PackageNuGet { +function Restore-NugetAsmForRuntime { + param( + [ValidateNotNull()][string]$PackageName, + [ValidateNotNull()][string]$PackageVersion, + [string]$DllName, + [string]$DestinationPath, + [string]$TargetPlatform = $script:NetFrameworkPlatformId, + [string]$TargetRuntime = $script:WindowsPowerShellFrameworkTarget + ) + + $tmpDir = [System.IO.Path]::GetTempPath() - $requiredSdkVersion = "2.0.0" + if (-not $DllName) { + $DllName = "$PackageName.dll" + } + + if ($DestinationPath -eq $null) { + $DestinationPath = Join-Path $tmpDir $DllName + } elseif (Test-Path $DestinationPath -PathType Container) { + $DestinationPath = Join-Path $DestinationPath $DllName + } + + $packageDirPath = Join-Path $tmpDir "$PackageName.$PackageVersion" + if (-not (Test-Path $packageDirPath)) { + $guid = New-Guid + $tmpNupkgPath = Join-Path $tmpDir "$guid.zip" + if (Test-Path $tmpNupkgPath) { + Remove-Item -Force $tmpNupkgPath + } + + try { + $packageUri = "$script:NugetApiUriBase/$PackageName/$PackageVersion" + Invoke-WebRequest -Uri $packageUri -OutFile $tmpNupkgPath + Expand-Archive -Path $tmpNupkgPath -DestinationPath $packageDirPath + } finally { + Remove-Item -Force $tmpNupkgPath -ErrorAction SilentlyContinue + } + } + + $internalPath = [System.IO.Path]::Combine($packageDirPath, 'runtimes', $TargetPlatform, 'lib', $TargetRuntime, $DllName) + + Copy-Item -Path $internalPath -Destination $DestinationPath -Force + + return $DestinationPath +} + +function Invoke-WithCreateDefaultHook { + param([scriptblock]$ScriptBlock) + + try + { + $env:PSES_TEST_USE_CREATE_DEFAULT = 1 + & $ScriptBlock + } finally { + Remove-Item env:PSES_TEST_USE_CREATE_DEFAULT + } +} + +task SetupDotNet -Before Clean, Build, TestHost, TestServer, TestProtocol, PackageNuGet { $dotnetPath = "$PSScriptRoot/.dotnet" $dotnetExePath = if ($script:IsUnix) { "$dotnetPath/dotnet" } else { "$dotnetPath/dotnet.exe" } @@ -56,7 +198,7 @@ task SetupDotNet -Before Clean, Build, TestHost, TestServer, TestProtocol, TestP # dotnet --version can return a semver that System.Version can't handle # e.g.: 2.1.300-preview-01. The replace operator is used to remove any build suffix. $version = (& $dotnetExePath --version) -replace '[+-].*$','' - if ([version]$version -ge [version]$requiredSdkVersion) { + if ([version]$version -ge [version]$script:RequiredSdkVersion) { $script:dotnetExe = $dotnetExePath } else { @@ -71,21 +213,21 @@ task SetupDotNet -Before Clean, Build, TestHost, TestServer, TestProtocol, TestP if ($script:dotnetExe -eq $null) { - Write-Host "`n### Installing .NET CLI $requiredSdkVersion...`n" -ForegroundColor Green + Write-Host "`n### Installing .NET CLI $script:RequiredSdkVersion...`n" -ForegroundColor Green # The install script is platform-specific $installScriptExt = if ($script:IsUnix) { "sh" } else { "ps1" } # Download the official installation script and run it $installScriptPath = "$([System.IO.Path]::GetTempPath())dotnet-install.$installScriptExt" - Invoke-WebRequest "https://raw.githubusercontent.com/dotnet/cli/v2.0.0/scripts/obtain/dotnet-install.$installScriptExt" -OutFile $installScriptPath + Invoke-WebRequest "https://raw.githubusercontent.com/dotnet/cli/v$script:RequiredSdkVersion/scripts/obtain/dotnet-install.$installScriptExt" -OutFile $installScriptPath $env:DOTNET_INSTALL_DIR = "$PSScriptRoot/.dotnet" if (!$script:IsUnix) { - & $installScriptPath -Version $requiredSdkVersion -InstallDir "$env:DOTNET_INSTALL_DIR" + & $installScriptPath -Version $script:RequiredSdkVersion -InstallDir "$env:DOTNET_INSTALL_DIR" } else { - & /bin/bash $installScriptPath -Version $requiredSdkVersion -InstallDir "$env:DOTNET_INSTALL_DIR" + & /bin/bash $installScriptPath -Version $script:RequiredSdkVersion -InstallDir "$env:DOTNET_INSTALL_DIR" $env:PATH = $dotnetExeDir + [System.IO.Path]::PathSeparator + $env:PATH } @@ -106,6 +248,7 @@ task SetupDotNet -Before Clean, Build, TestHost, TestServer, TestProtocol, TestP } task Clean { + exec { & $script:dotnetExe restore } exec { & $script:dotnetExe clean } Remove-Item $PSScriptRoot\module\PowerShellEditorServices\bin -Recurse -Force -ErrorAction Ignore Remove-Item $PSScriptRoot\module\PowerShellEditorServices.VSCode\bin -Recurse -Force -ErrorAction Ignore @@ -136,20 +279,6 @@ task GetProductVersion -Before PackageNuGet, PackageModule, UploadArtifacts { Write-Host "`n### Product Version: $script:FullVersion`n" -ForegroundColor Green } -function BuildForPowerShellVersion($version) { - Write-Host -ForegroundColor Green "`n### Testing API usage for PowerShell $version...`n" - exec { & $script:dotnetExe build -f net452 .\src\PowerShellEditorServices\PowerShellEditorServices.csproj /p:PowerShellVersion=$version } -} - -task TestPowerShellApi -If { !$script:IsUnix } { - BuildForPowerShellVersion v3 - BuildForPowerShellVersion v4 - BuildForPowerShellVersion v5r1 - - # Do a final restore to put everything back to normal - exec { & $script:dotnetExe restore .\src\PowerShellEditorServices\PowerShellEditorServices.csproj } -} - task CreateBuildInfo -Before Build { $buildVersion = "" $buildOrigin = "" @@ -196,15 +325,9 @@ namespace Microsoft.PowerShell.EditorServices.Host } task Build { - exec { & $script:dotnetExe publish -c $Configuration .\src\PowerShellEditorServices.Host\PowerShellEditorServices.Host.csproj -f netstandard1.6 } - if (!$script:IsUnix) { - exec { & $script:dotnetExe publish -c $Configuration .\src\PowerShellEditorServices.Host\PowerShellEditorServices.Host.csproj -f net452 } - } + exec { & $script:dotnetExe publish -c $Configuration .\src\PowerShellEditorServices\PowerShellEditorServices.csproj -f $script:TargetPlatform } + exec { & $script:dotnetExe publish -c $Configuration .\src\PowerShellEditorServices.Host\PowerShellEditorServices.Host.csproj -f $script:TargetPlatform } exec { & $script:dotnetExe build -c $Configuration .\src\PowerShellEditorServices.VSCode\PowerShellEditorServices.VSCode.csproj $script:TargetFrameworksParam } - exec { & $script:dotnetExe publish -c $Configuration .\src\PowerShellEditorServices\PowerShellEditorServices.csproj -f netstandard1.6 } - Copy-Item $PSScriptRoot\src\PowerShellEditorServices\bin\$Configuration\netstandard1.6\publish\UnixConsoleEcho.dll -Destination $PSScriptRoot\src\PowerShellEditorServices.Host\bin\$Configuration\netstandard1.6 - Copy-Item $PSScriptRoot\src\PowerShellEditorServices\bin\$Configuration\netstandard1.6\publish\runtimes\osx-64\native\libdisablekeyecho.dylib -Destination $PSScriptRoot\src\PowerShellEditorServices.Host\bin\$Configuration\netstandard1.6 - Copy-Item $PSScriptRoot\src\PowerShellEditorServices\bin\$Configuration\netstandard1.6\publish\runtimes\linux-64\native\libdisablekeyecho.so -Destination $PSScriptRoot\src\PowerShellEditorServices.Host\bin\$Configuration\netstandard1.6 } function UploadTestLogs { @@ -232,22 +355,40 @@ function XunitTraitFilter { task Test TestServer,TestProtocol -task TestServer -If { !$script:IsUnix } { +task TestServer { Set-Location .\test\PowerShellEditorServices.Test\ - exec { & $script:dotnetExe build -c $Configuration -f net452 } - exec { & $script:dotnetExe xunit -configuration $Configuration -framework net452 -verbose -nobuild (XunitTraitFilter) } + + if (-not $script:IsUnix) { + exec { & $script:dotnetExe test -f $script:TestRuntime.Desktop } + } + + Invoke-WithCreateDefaultHook -NewModulePath $script:PSCoreModulePath { + exec { & $script:dotnetExe test -f $script:TestRuntime.Core } + } } -task TestProtocol -If { !$script:IsUnix } { +task TestProtocol { Set-Location .\test\PowerShellEditorServices.Test.Protocol\ - exec { & $script:dotnetExe build -c $Configuration -f net452 } - exec { & $script:dotnetExe xunit -configuration $Configuration -framework net452 -verbose -nobuild (XunitTraitFilter) } + + if (-not $script:IsUnix) { + exec { & $script:dotnetExe test -f $script:TestRuntime.Desktop } + } + + Invoke-WithCreateDefaultHook { + exec { & $script:dotnetExe test -f $script:TestRuntime.Core } + } } -task TestHost -If { !$script:IsUnix } { +task TestHost { Set-Location .\test\PowerShellEditorServices.Test.Host\ - exec { & $script:dotnetExe build -c $Configuration -f net452 } - exec { & $script:dotnetExe xunit -configuration $Configuration -framework net452 -verbose -nobuild (XunitTraitFilter) } + + if (-not $script:IsUnix) { + exec { & $script:dotnetExe build -f $script:TestRuntime.Desktop } + exec { & $script:dotnetExe test -f $script:TestRuntime.Desktop } + } + + exec { & $script:dotnetExe build -c $Configuration -f $script:TestRuntime.Core } + exec { & $script:dotnetExe test -f $script:TestRuntime.Core } } task CITest ?Test, { @@ -260,52 +401,40 @@ task CITest ?Test, { } task LayoutModule -After Build { - # Lay out the PowerShellEditorServices module's binaries - New-Item -Force $PSScriptRoot\module\PowerShellEditorServices\bin\ -Type Directory | Out-Null - New-Item -Force $PSScriptRoot\module\PowerShellEditorServices\bin\Desktop -Type Directory | Out-Null - New-Item -Force $PSScriptRoot\module\PowerShellEditorServices\bin\Core -Type Directory | Out-Null - - Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices\bin\$Configuration\netstandard1.6\publish\Serilog*.dll -Destination $PSScriptRoot\module\PowerShellEditorServices\bin\Core\ - Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices\bin\$Configuration\netstandard1.6\publish\System.Runtime.InteropServices.RuntimeInformation.dll -Destination $PSScriptRoot\module\PowerShellEditorServices\bin\Core\ - - Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices.Host\bin\$Configuration\netstandard1.6\* -Filter Microsoft.PowerShell.EditorServices*.dll -Destination $PSScriptRoot\module\PowerShellEditorServices\bin\Core\ - Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices.Host\bin\$Configuration\netstandard1.6\UnixConsoleEcho.dll -Destination $PSScriptRoot\module\PowerShellEditorServices\bin\Core\ - Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices.Host\bin\$Configuration\netstandard1.6\libdisablekeyecho.* -Destination $PSScriptRoot\module\PowerShellEditorServices\bin\Core\ - Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices.Host\bin\$Configuration\netstandard1.6\publish\runtimes\win\lib\netstandard1.3\* -Filter System.IO.Pipes*.dll -Destination $PSScriptRoot\module\PowerShellEditorServices\bin\Core\ - - if (!$script:IsUnix) { - Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices\bin\$Configuration\net452\Serilog*.dll -Destination $PSScriptRoot\module\PowerShellEditorServices\bin\Desktop - Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices\bin\$Configuration\net452\System.Runtime.InteropServices.RuntimeInformation.dll -Destination $PSScriptRoot\module\PowerShellEditorServices\bin\Desktop\ - - Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices.Host\bin\$Configuration\net452\* -Filter Microsoft.PowerShell.EditorServices*.dll -Destination $PSScriptRoot\module\PowerShellEditorServices\bin\Desktop\ - Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices.Host\bin\$Configuration\net452\Newtonsoft.Json.dll -Destination $PSScriptRoot\module\PowerShellEditorServices\bin\Desktop\ - Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices.Host\bin\$Configuration\net452\UnixConsoleEcho.dll -Destination $PSScriptRoot\module\PowerShellEditorServices\bin\Desktop\ - } - # Copy Third Party Notices.txt to module folder Copy-Item -Force -Path "$PSScriptRoot\Third Party Notices.txt" -Destination $PSScriptRoot\module\PowerShellEditorServices - # Lay out the PowerShellEditorServices.VSCode module's binaries - New-Item -Force $PSScriptRoot\module\PowerShellEditorServices.VSCode\bin\ -Type Directory | Out-Null - New-Item -Force $PSScriptRoot\module\PowerShellEditorServices.VSCode\bin\Desktop -Type Directory | Out-Null - New-Item -Force $PSScriptRoot\module\PowerShellEditorServices.VSCode\bin\Core -Type Directory | Out-Null - - Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices.VSCode\bin\$Configuration\netstandard1.6\* -Filter Microsoft.PowerShell.EditorServices.VSCode*.dll -Destination $PSScriptRoot\module\PowerShellEditorServices.VSCode\bin\Core\ - if (!$script:IsUnix) { - Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices.VSCode\bin\$Configuration\net452\* -Filter Microsoft.PowerShell.EditorServices.VSCode*.dll -Destination $PSScriptRoot\module\PowerShellEditorServices.VSCode\bin\Desktop\ + # Lay out the PowerShellEditorServices module's binaries + # For each binplace destination + foreach ($destDir in $script:RequiredBuildAssets.Keys) { + # Create the destination dir + $null = New-Item -Force $destDir -Type Directory + + # For each PSES subproject + foreach ($projectName in $script:RequiredBuildAssets[$destDir].Keys) { + # Get the project build dir path + $basePath = [System.IO.Path]::Combine($PSScriptRoot, 'src', $projectName, 'bin', $Configuration, $script:TargetPlatform) + + # For each asset in the subproject + foreach ($bin in $script:RequiredBuildAssets[$destDir][$projectName]) { + # Get the asset path + $binPath = Join-Path $basePath $bin + + # Binplace the asset + Copy-Item -Force -Verbose $binPath $destDir + } + } } - if ($Configuration -eq "Debug") { - Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices.VSCode\bin\$Configuration\netstandard1.6\Microsoft.PowerShell.EditorServices.VSCode.pdb -Destination $PSScriptRoot\module\PowerShellEditorServices.VSCode\bin\Core\ - Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices\bin\$Configuration\netstandard1.6\Microsoft.PowerShell.EditorServices.pdb -Destination $PSScriptRoot\module\PowerShellEditorServices\bin\Core\ - Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices.Host\bin\$Configuration\netstandard1.6\Microsoft.PowerShell.EditorServices.Host.pdb -Destination $PSScriptRoot\module\PowerShellEditorServices\bin\Core\ - Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices.Protocol\bin\$Configuration\netstandard1.6\Microsoft.PowerShell.EditorServices.Protocol.pdb -Destination $PSScriptRoot\module\PowerShellEditorServices\bin\Core\ + # Get and place the shim bins for net461 + foreach ($binDestinationDir in $script:RequiredNugetBinaries.Keys) { + $binDestPath = Join-Path $script:ModuleBinPath $binDestinationDir + if (-not (Test-Path $binDestPath)) { + New-Item -Path $binDestPath -ItemType Directory + } - if (!$script:IsUnix) { - Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices.VSCode\bin\$Configuration\net452\Microsoft.PowerShell.EditorServices.VSCode.pdb -Destination $PSScriptRoot\module\PowerShellEditorServices.VSCode\bin\Desktop\ - Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices\bin\$Configuration\net452\Microsoft.PowerShell.EditorServices.pdb -Destination $PSScriptRoot\module\PowerShellEditorServices\bin\Desktop\ - Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices.Host\bin\$Configuration\net452\Microsoft.PowerShell.EditorServices.Host.pdb -Destination $PSScriptRoot\module\PowerShellEditorServices\bin\Desktop\ - Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices.Protocol\bin\$Configuration\net452\Microsoft.PowerShell.EditorServices.Protocol.pdb -Destination $PSScriptRoot\module\PowerShellEditorServices\bin\Desktop\ + foreach ($packageDetails in $script:RequiredNugetBinaries[$binDestinationDir]) { + Restore-NugetAsmForRuntime -DestinationPath $binDestPath @packageDetails } } } @@ -323,6 +452,7 @@ task RestorePsesModules -After Build { Name = $name MinimumVersion = $_.Value.MinimumVersion MaximumVersion = $_.Value.MaximumVersion + AllowPrerelease = $_.Value.AllowPrerelease Repository = if ($_.Value.Repository) { $_.Value.Repository } else { $DefaultModuleRepository } Path = $submodulePath } @@ -332,11 +462,6 @@ task RestorePsesModules -After Build { throw "EditorServices module listed without name in '$ModulesJsonPath'" } - if ($script:SaveModuleSupportsAllowPrerelease) - { - $body += @{ AllowPrerelease = $_.Value.AllowPrerelease } - } - $moduleInfos.Add($name, $body) } @@ -355,16 +480,12 @@ task RestorePsesModules -After Build { Name = $moduleName MinimumVersion = $moduleInstallDetails.MinimumVersion MaximumVersion = $moduleInstallDetails.MaximumVersion + AllowPrerelease = $moduleInstallDetails.AllowPrerelease Repository = if ($moduleInstallDetails.Repository) { $moduleInstallDetails.Repository } else { $DefaultModuleRepository } Path = $submodulePath } - if ($script:SaveModuleSupportsAllowPrerelease) - { - $splatParameters += @{ AllowPrerelease = $moduleInstallDetails.AllowPrerelease } - } - - Write-Host "`tInstalling module: ${moduleName}" + Write-Host "`tInstalling module: ${moduleName} with arguments $(ConvertTo-Json $splatParameters)" Save-Module @splatParameters } @@ -400,4 +521,4 @@ task UploadArtifacts -If ($script:IsCIBuild) { } # The default task is to run the entire CI build -task . GetProductVersion, Clean, Build, TestPowerShellApi, CITest, BuildCmdletHelp, PackageNuGet, PackageModule, UploadArtifacts +task . GetProductVersion, Clean, Build, CITest, BuildCmdletHelp, PackageNuGet, PackageModule, UploadArtifacts diff --git a/appveyor.yml b/appveyor.yml index 62c45d204..c66710822 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: '1.10.3{build}' +version: '2.0.0-{build}' image: Visual Studio 2017 clone_depth: 10 skip_tags: true diff --git a/module/PowerShellEditorServices.VSCode/PowerShellEditorServices.VSCode.psm1 b/module/PowerShellEditorServices.VSCode/PowerShellEditorServices.VSCode.psm1 index f5d53be4c..e7b34e076 100644 --- a/module/PowerShellEditorServices.VSCode/PowerShellEditorServices.VSCode.psm1 +++ b/module/PowerShellEditorServices.VSCode/PowerShellEditorServices.VSCode.psm1 @@ -3,12 +3,7 @@ # Licensed under the MIT license. See LICENSE file in the project root for full license information. # -if (!$PSVersionTable.PSEdition -or $PSVersionTable.PSEdition -eq "Desktop") { - Microsoft.PowerShell.Utility\Add-Type -Path "$PSScriptRoot/bin/Desktop/Microsoft.PowerShell.EditorServices.VSCode.dll" -} -else { - Microsoft.PowerShell.Utility\Add-Type -Path "$PSScriptRoot/bin/Core/Microsoft.PowerShell.EditorServices.VSCode.dll" -} +Microsoft.PowerShell.Utility\Add-Type -Path "$PSScriptRoot/bin/Microsoft.PowerShell.EditorServices.VSCode.dll" if ($psEditor -is [Microsoft.PowerShell.EditorServices.Extensions.EditorObject]) { [Microsoft.PowerShell.EditorServices.VSCode.ComponentRegistration]::Register($psEditor.Components) diff --git a/module/PowerShellEditorServices/PowerShellEditorServices.psd1 b/module/PowerShellEditorServices/PowerShellEditorServices.psd1 index 236d68520..7a03e12be 100644 --- a/module/PowerShellEditorServices/PowerShellEditorServices.psd1 +++ b/module/PowerShellEditorServices/PowerShellEditorServices.psd1 @@ -12,7 +12,7 @@ RootModule = 'PowerShellEditorServices.psm1' # Version number of this module. -ModuleVersion = '1.10.3' +ModuleVersion = '2.0.0' # ID used to uniquely identify this module GUID = '9ca15887-53a2-479a-9cda-48d26bcb6c47' diff --git a/module/PowerShellEditorServices/PowerShellEditorServices.psm1 b/module/PowerShellEditorServices/PowerShellEditorServices.psm1 index dd8f463a6..d0378f0af 100644 --- a/module/PowerShellEditorServices/PowerShellEditorServices.psm1 +++ b/module/PowerShellEditorServices/PowerShellEditorServices.psm1 @@ -3,16 +3,21 @@ # Licensed under the MIT license. See LICENSE file in the project root for full license information. # -if (!$PSVersionTable.PSEdition -or $PSVersionTable.PSEdition -eq "Desktop") { - Microsoft.PowerShell.Utility\Add-Type -Path "$PSScriptRoot/bin/Desktop/Microsoft.PowerShell.EditorServices.dll" - Microsoft.PowerShell.Utility\Add-Type -Path "$PSScriptRoot/bin/Desktop/Microsoft.PowerShell.EditorServices.Host.dll" -} -else { - Microsoft.PowerShell.Utility\Add-Type -Path "$PSScriptRoot/bin/Core/Microsoft.PowerShell.EditorServices.dll" - Microsoft.PowerShell.Utility\Add-Type -Path "$PSScriptRoot/bin/Core/Microsoft.PowerShell.EditorServices.Protocol.dll" - Microsoft.PowerShell.Utility\Add-Type -Path "$PSScriptRoot/bin/Core/Microsoft.PowerShell.EditorServices.Host.dll" +# Need to load pipe handling shim assemblies in Windows PowerShell and PSCore 6.0 because they don't have WCP +if ($PSEdition -eq 'Desktop') { + Microsoft.PowerShell.Utility\Add-Type -Path "$PSScriptRoot/bin/Desktop/System.IO.Pipes.AccessControl.dll" + Microsoft.PowerShell.Utility\Add-Type -Path "$PSScriptRoot/bin/Desktop/System.Security.AccessControl.dll" + Microsoft.PowerShell.Utility\Add-Type -Path "$PSScriptRoot/bin/Desktop/System.Security.Principal.Windows.dll" +} elseif ($PSVersionTable.PSVersion -ge '6.0' -and $PSVersionTable.PSVersion -lt '6.1' -and $IsWindows) { + Microsoft.PowerShell.Utility\Add-Type -Path "$PSScriptRoot/bin/6.0/System.IO.Pipes.AccessControl.dll" + Microsoft.PowerShell.Utility\Add-Type -Path "$PSScriptRoot/bin/6.0/System.Security.AccessControl.dll" + Microsoft.PowerShell.Utility\Add-Type -Path "$PSScriptRoot/bin/6.0/System.Security.Principal.Windows.dll" } +Microsoft.PowerShell.Utility\Add-Type -Path "$PSScriptRoot/bin/Microsoft.PowerShell.EditorServices.dll" +Microsoft.PowerShell.Utility\Add-Type -Path "$PSScriptRoot/bin/Microsoft.PowerShell.EditorServices.Host.dll" +Microsoft.PowerShell.Utility\Add-Type -Path "$PSScriptRoot/bin/Microsoft.PowerShell.EditorServices.Protocol.dll" + function Start-EditorServicesHost { [CmdletBinding()] param( diff --git a/scripts/travis.ps1 b/scripts/travis.ps1 index 51dd4859f..abcb12fe2 100644 --- a/scripts/travis.ps1 +++ b/scripts/travis.ps1 @@ -1,11 +1,4 @@ -Get-Module PowerShellGet,PackageManagement | Remove-Module -Force -Verbose -powershell -Command { Install-Module -Name PowershellGet -MinimumVersion 1.6 -Scope CurrentUser -force -confirm:$false -verbose } -powershell -Command { Install-Module -Name PackageManagement -MinimumVersion 1.1.7.0 -Scope CurrentUser -Force -Confirm:$false -Verbose } -Import-Module -Name PowerShellGet -MinimumVersion 1.6 -Force -Import-Module -Name PackageManagement -MinimumVersion 1.1.7.0 -Force -Install-PackageProvider -Name NuGet -Force | Out-Null -Import-PackageProvider NuGet -Force | Out-Null -Set-PSRepository -Name PSGallery -InstallationPolicy Trusted | Out-Null +$ErrorActionPreference = 'Stop' # Install InvokeBuild Install-Module InvokeBuild -MaximumVersion 5.1.0 -Scope CurrentUser -Force diff --git a/src/PowerShellEditorServices.Host/EditorServicesHost.cs b/src/PowerShellEditorServices.Host/EditorServicesHost.cs index c723427d4..51e4a2db3 100644 --- a/src/PowerShellEditorServices.Host/EditorServicesHost.cs +++ b/src/PowerShellEditorServices.Host/EditorServicesHost.cs @@ -125,9 +125,7 @@ public EditorServicesHost( #endif // Catch unhandled exceptions for logging purposes -#if !CoreCLR AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; -#endif } #endregion @@ -146,11 +144,8 @@ public void StartLogging(string logFilePath, LogLevel logLevel) .AddLogFile(logFilePath) .Build(); -#if CoreCLR - FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(this.GetType().GetTypeInfo().Assembly.Location); -#else - FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(this.GetType().Assembly.Location); -#endif + FileVersionInfo fileVersionInfo = + FileVersionInfo.GetVersionInfo(this.GetType().GetTypeInfo().Assembly.Location); string osVersion = RuntimeInformation.OSDescription; @@ -239,7 +234,7 @@ private async void OnLanguageServiceClientConnect( await this.editorSession.PowerShellContext.ImportCommandsModule( Path.Combine( Path.GetDirectoryName(this.GetType().GetTypeInfo().Assembly.Location), - @"..\..\Commands")); + @"..\Commands")); this.languageServer.Start(); @@ -454,19 +449,13 @@ private void ProtocolEndpoint_UnhandledException(object sender, Exception e) this.serverCompletedTask.SetException(e); } -#if !CoreCLR private void CurrentDomain_UnhandledException( object sender, UnhandledExceptionEventArgs e) { // Log the exception - this.logger.Write( - LogLevel.Error, - string.Format( - "FATAL UNHANDLED EXCEPTION:\r\n\r\n{0}", - e.ExceptionObject.ToString())); + this.logger.Write(LogLevel.Error, $"FATAL UNHANDLED EXCEPTION: {e.ExceptionObject}"); } -#endif private IServerListener CreateServiceListener(MessageProtocolType protocol, EditorServiceTransportConfig config) { @@ -504,7 +493,6 @@ private IServerListener CreateServiceListener(MessageProtocolType protocol, Edit /// private string GetOSArchitecture() { -#if !CoreCLR // If on win7 (version 6.1.x), avoid System.Runtime.InteropServices.RuntimeInformation if (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version < new Version(6, 2)) { @@ -515,7 +503,7 @@ private string GetOSArchitecture() return "X86"; } -#endif + return RuntimeInformation.OSArchitecture.ToString(); } diff --git a/src/PowerShellEditorServices.Host/PowerShellEditorServices.Host.csproj b/src/PowerShellEditorServices.Host/PowerShellEditorServices.Host.csproj index b2d3e934f..309bf484a 100644 --- a/src/PowerShellEditorServices.Host/PowerShellEditorServices.Host.csproj +++ b/src/PowerShellEditorServices.Host/PowerShellEditorServices.Host.csproj @@ -4,30 +4,19 @@ PowerShell Editor Services Host Process Provides a process for hosting the PowerShell Editor Services library exposed by a JSON message protocol. - netstandard1.6;net452 + netstandard2.0 Microsoft.PowerShell.EditorServices.Host + + + - - - 10.0.3 - - - 6.0.0-alpha13 - - - - - $(DefineConstants);CoreCLR - - - - - - diff --git a/src/PowerShellEditorServices.Protocol/MessageProtocol/Channel/NamedPipeClientChannel.cs b/src/PowerShellEditorServices.Protocol/MessageProtocol/Channel/NamedPipeClientChannel.cs index 3fd6d5b5e..8a40b6a1b 100644 --- a/src/PowerShellEditorServices.Protocol/MessageProtocol/Channel/NamedPipeClientChannel.cs +++ b/src/PowerShellEditorServices.Protocol/MessageProtocol/Channel/NamedPipeClientChannel.cs @@ -69,24 +69,7 @@ public static async Task Connect( PipeDirection.InOut, PipeOptions.Asynchronous); -#if CoreCLR await pipeClient.ConnectAsync(); -#else - while (!pipeClient.IsConnected) - { - try - { - // Wait for 500 milliseconds so that we don't tie up the thread - pipeClient.Connect(500); - } - catch (TimeoutException) - { - // Connect timed out, wait and try again - await Task.Delay(1000); - continue; - } - } -#endif var clientChannel = new NamedPipeClientChannel(pipeClient, logger); clientChannel.Start(messageProtocolType); diff --git a/src/PowerShellEditorServices.Protocol/MessageProtocol/Channel/StdioServerChannel.cs b/src/PowerShellEditorServices.Protocol/MessageProtocol/Channel/StdioServerChannel.cs index 119f42110..95318d860 100644 --- a/src/PowerShellEditorServices.Protocol/MessageProtocol/Channel/StdioServerChannel.cs +++ b/src/PowerShellEditorServices.Protocol/MessageProtocol/Channel/StdioServerChannel.cs @@ -27,11 +27,15 @@ public StdioServerChannel(ILogger logger) protected override void Initialize(IMessageSerializer messageSerializer) { -#if !CoreCLR - // Ensure that the console is using UTF-8 encoding - System.Console.InputEncoding = Encoding.UTF8; - System.Console.OutputEncoding = Encoding.UTF8; -#endif + if (System.Console.InputEncoding != Encoding.UTF8) + { + System.Console.InputEncoding = Encoding.UTF8; + } + + if (System.Console.OutputEncoding != Encoding.UTF8) + { + System.Console.OutputEncoding = Encoding.UTF8; + } // Open the standard input/output streams this.inputStream = System.Console.OpenStandardInput(); diff --git a/src/PowerShellEditorServices.Protocol/PowerShellEditorServices.Protocol.csproj b/src/PowerShellEditorServices.Protocol/PowerShellEditorServices.Protocol.csproj index d33b55ee2..5d663043d 100644 --- a/src/PowerShellEditorServices.Protocol/PowerShellEditorServices.Protocol.csproj +++ b/src/PowerShellEditorServices.Protocol/PowerShellEditorServices.Protocol.csproj @@ -3,25 +3,19 @@ PowerShell Editor Services Host Protocol Library Provides message types and client/server APIs for the PowerShell Editor Services JSON protocol. - netstandard1.6;net452; + netstandard2.0 Microsoft.PowerShell.EditorServices.Protocol + - - - - - - - - - $(DefineConstants);CoreCLR - - - - + + + diff --git a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs index 87c21d3ba..96f04ba09 100644 --- a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs +++ b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs @@ -290,12 +290,7 @@ protected async Task HandleLaunchRequest( // pick some reasonable default. if (string.IsNullOrEmpty(workingDir) && launchParams.CreateTemporaryIntegratedConsole) { -#if CoreCLR - //TODO: RKH 2018-06-26 .NET standard 2.0 has added Environment.CurrentDirectory - let's use it. - workingDir = AppContext.BaseDirectory; -#else workingDir = Environment.CurrentDirectory; -#endif } // At this point, we will either have a working dir that should be set to cwd in diff --git a/src/PowerShellEditorServices.VSCode/PowerShellEditorServices.VSCode.csproj b/src/PowerShellEditorServices.VSCode/PowerShellEditorServices.VSCode.csproj index e3726be1f..c50e535ea 100644 --- a/src/PowerShellEditorServices.VSCode/PowerShellEditorServices.VSCode.csproj +++ b/src/PowerShellEditorServices.VSCode/PowerShellEditorServices.VSCode.csproj @@ -4,12 +4,12 @@ PowerShell Editor Services, Visual Studio Code Extensions Provides added functionality to PowerShell Editor Services for the Visual Studio Code editor. - netstandard1.6;net452 + netstandard2.0 Microsoft.PowerShell.EditorServices.VSCode - + 1591,1573,1572 bin\$(TargetFramework)\$(Configuration)\Microsoft.PowerShell.EditorServices.VSCode.xml @@ -20,12 +20,4 @@ - - $(DefineConstants);CoreCLR - - - - - - diff --git a/src/PowerShellEditorServices/Analysis/AnalysisService.cs b/src/PowerShellEditorServices/Analysis/AnalysisService.cs index 40b6327e5..e089cd87b 100644 --- a/src/PowerShellEditorServices/Analysis/AnalysisService.cs +++ b/src/PowerShellEditorServices/Analysis/AnalysisService.cs @@ -611,7 +611,12 @@ private static RunspacePool CreatePssaRunspacePool(out PSModuleInfo pssaModuleIn } // Create a base session state with PSScriptAnalyzer loaded - InitialSessionState sessionState = InitialSessionState.CreateDefault2(); + InitialSessionState sessionState; + if (Environment.GetEnvironmentVariable("PSES_TEST_USE_CREATE_DEFAULT") == "1") { + sessionState = InitialSessionState.CreateDefault(); + } else { + sessionState = InitialSessionState.CreateDefault2(); + } sessionState.ImportPSModule(new [] { pssaModuleInfo.ModuleBase }); // RunspacePool takes care of queuing commands for us so we do not diff --git a/src/PowerShellEditorServices/Console/ConsoleProxy.cs b/src/PowerShellEditorServices/Console/ConsoleProxy.cs index bd7488169..3956f6df9 100644 --- a/src/PowerShellEditorServices/Console/ConsoleProxy.cs +++ b/src/PowerShellEditorServices/Console/ConsoleProxy.cs @@ -20,8 +20,6 @@ internal static class ConsoleProxy static ConsoleProxy() { - // Maybe we should just include the RuntimeInformation package for FullCLR? -#if CoreCLR if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { s_consoleProxy = new WindowsConsoleOperations(); @@ -29,9 +27,6 @@ static ConsoleProxy() } s_consoleProxy = new UnixConsoleOperations(); -#else - s_consoleProxy = new WindowsConsoleOperations(); -#endif } public static Task ReadKeyAsync(CancellationToken cancellationToken) => diff --git a/src/PowerShellEditorServices/Language/CompletionResults.cs b/src/PowerShellEditorServices/Language/CompletionResults.cs index b41394146..fc8d2eb00 100644 --- a/src/PowerShellEditorServices/Language/CompletionResults.cs +++ b/src/PowerShellEditorServices/Language/CompletionResults.cs @@ -305,10 +305,8 @@ private static CompletionType ConvertCompletionResultType( case CompletionResultType.Type: return CompletionType.Type; -#if !PowerShellv3 case CompletionResultType.Keyword: return CompletionType.Keyword; -#endif case CompletionResultType.ProviderContainer: return CompletionType.Folder; diff --git a/src/PowerShellEditorServices/Language/FindSymbolsVisitor2.cs b/src/PowerShellEditorServices/Language/FindSymbolsVisitor2.cs index 226b96035..03628ee3e 100644 --- a/src/PowerShellEditorServices/Language/FindSymbolsVisitor2.cs +++ b/src/PowerShellEditorServices/Language/FindSymbolsVisitor2.cs @@ -8,8 +8,6 @@ namespace Microsoft.PowerShell.EditorServices { -#if PowerShellv5 - // TODO: Restore this when we figure out how to support multiple // PS versions in the new PSES-as-a-module world (issue #276) @@ -78,6 +76,5 @@ namespace Microsoft.PowerShell.EditorServices // return AstVisitAction.Continue; // } //} -#endif } diff --git a/src/PowerShellEditorServices/Language/LanguageService.cs b/src/PowerShellEditorServices/Language/LanguageService.cs index 05c47bea3..64ff08bd6 100644 --- a/src/PowerShellEditorServices/Language/LanguageService.cs +++ b/src/PowerShellEditorServices/Language/LanguageService.cs @@ -355,7 +355,6 @@ public async Task FindReferencesOfSymbol( foreach (object fileName in fileMap.Keys) { var file = (ScriptFile)fileMap[fileName]; - IEnumerable symbolReferencesinFile; await _aliasHandle.WaitAsync(); try { diff --git a/src/PowerShellEditorServices/PowerShellEditorServices.csproj b/src/PowerShellEditorServices/PowerShellEditorServices.csproj index d7b44185b..787f5315a 100644 --- a/src/PowerShellEditorServices/PowerShellEditorServices.csproj +++ b/src/PowerShellEditorServices/PowerShellEditorServices.csproj @@ -3,63 +3,17 @@ PowerShell Editor Services Provides common PowerShell editor capabilities as a .NET library. - netstandard1.6;net452 + netstandard2.0 Microsoft.PowerShell.EditorServices - $(PackageTargetFallback);dnxcore50;portable-net45+win8 - - - - - - - - - $(DefineConstants);PowerShellv3 - $(DefineConstants);PowerShellv4 - $(DefineConstants);PowerShellv5r1;PowerShellv5 - $(DefineConstants);PowerShellv5r2;PowerShellv5 - - - + 1591,1573,1572 bin\$(TargetFramework)\$(Configuration)\Microsoft.PowerShell.EditorServices.xml - - - - - - - - - - - - - - - - - - - - 6.0.0-alpha13 - - - - - - - - - - - - - + + @@ -68,11 +22,6 @@ - - - $(DefineConstants);CoreCLR - - $(DefineConstants);RELEASE diff --git a/src/PowerShellEditorServices/Session/InvocationEventQueue.cs b/src/PowerShellEditorServices/Session/InvocationEventQueue.cs index 77d85bf23..89184e930 100644 --- a/src/PowerShellEditorServices/Session/InvocationEventQueue.cs +++ b/src/PowerShellEditorServices/Session/InvocationEventQueue.cs @@ -225,6 +225,11 @@ private void OnInvokerUnsubscribed(object sender, PSEventUnsubscribedEventArgs e CreateInvocationSubscriber(); } + private void OnInvokerUnsubscribed(object sender, PSEventArgs e) + { + CreateInvocationSubscriber(); + } + private void SetSubscriberExecutionThreadWithReflection(PSEventSubscriber subscriber) { // We need to create the PowerShell object in the same thread so we can get a nested diff --git a/src/PowerShellEditorServices/Session/PSReadLinePromptContext.cs b/src/PowerShellEditorServices/Session/PSReadLinePromptContext.cs index c0707d026..23c312fef 100644 --- a/src/PowerShellEditorServices/Session/PSReadLinePromptContext.cs +++ b/src/PowerShellEditorServices/Session/PSReadLinePromptContext.cs @@ -67,7 +67,6 @@ internal PSReadLinePromptContext( _consoleReadLine = new ConsoleReadLine(powerShellContext); _readLineProxy = readLineProxy; -#if CoreCLR if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return; @@ -77,7 +76,6 @@ internal PSReadLinePromptContext( intercept => ConsoleProxy.UnixReadKey( intercept, _readLineCancellationSource.Token)); -#endif } internal static bool TryGetPSReadLineProxy( diff --git a/src/PowerShellEditorServices/Session/PowerShell3Operations.cs b/src/PowerShellEditorServices/Session/PowerShell3Operations.cs deleted file mode 100644 index eb6cf0252..000000000 --- a/src/PowerShellEditorServices/Session/PowerShell3Operations.cs +++ /dev/null @@ -1,96 +0,0 @@ -// -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Management.Automation; -using System.Management.Automation.Host; -using System.Management.Automation.Runspaces; - -namespace Microsoft.PowerShell.EditorServices.Session -{ - internal class PowerShell3Operations : IVersionSpecificOperations - { - public void ConfigureDebugger(Runspace runspace) - { - // The debugger has no SetDebugMode in PowerShell v3. - } - - public void PauseDebugger(Runspace runspace) - { - // The debugger cannot be paused in PowerShell v3. - throw new NotSupportedException("Debugger cannot be paused in PowerShell v3"); - } - - public IEnumerable ExecuteCommandInDebugger( - PowerShellContext powerShellContext, - Runspace currentRunspace, - PSCommand psCommand, - bool sendOutputToHost, - out DebuggerResumeAction? debuggerResumeAction) - { - IEnumerable executionResult = null; - - using (var nestedPipeline = currentRunspace.CreateNestedPipeline()) - { - foreach (var command in psCommand.Commands) - { - nestedPipeline.Commands.Add(command); - } - - var results = nestedPipeline.Invoke(); - - if (typeof(TResult) != typeof(PSObject)) - { - executionResult = - results - .Select(pso => pso.BaseObject) - .Cast(); - } - else - { - executionResult = results.Cast(); - } - } - - // Write the output to the host if necessary - if (sendOutputToHost) - { - foreach (var line in executionResult) - { - powerShellContext.WriteOutput(line.ToString(), true); - } - } - - // No DebuggerResumeAction result for PowerShell v3 - debuggerResumeAction = null; - - return executionResult; - } - - public void StopCommandInDebugger(PowerShellContext powerShellContext) - { - } - - public bool IsDebuggerStopped(PromptNest promptNest, Runspace runspace) - { - return promptNest.IsInDebugger; - } - - public void ExitNestedPrompt(PSHost host) - { - try - { - host.ExitNestedPrompt(); - } - // FlowControlException is not accessible in PSv3 - catch (Exception) - { - } - } - } -} - diff --git a/src/PowerShellEditorServices/Session/PowerShell4Operations.cs b/src/PowerShellEditorServices/Session/PowerShell4Operations.cs deleted file mode 100644 index d9060ed2f..000000000 --- a/src/PowerShellEditorServices/Session/PowerShell4Operations.cs +++ /dev/null @@ -1,112 +0,0 @@ -// -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Management.Automation; -using System.Management.Automation.Host; -using System.Management.Automation.Runspaces; - -namespace Microsoft.PowerShell.EditorServices.Session -{ - internal class PowerShell4Operations : IVersionSpecificOperations - { - public void ConfigureDebugger(Runspace runspace) - { -#if !PowerShellv3 - if (runspace.Debugger != null) - { - runspace.Debugger.SetDebugMode(DebugModes.LocalScript | DebugModes.RemoteScript); - } -#endif - } - - public virtual void PauseDebugger(Runspace runspace) - { - // The debugger cannot be paused in PowerShell v4. - throw new NotSupportedException("Debugger cannot be paused in PowerShell v4"); - } - - public IEnumerable ExecuteCommandInDebugger( - PowerShellContext powerShellContext, - Runspace currentRunspace, - PSCommand psCommand, - bool sendOutputToHost, - out DebuggerResumeAction? debuggerResumeAction) - { - debuggerResumeAction = null; - PSDataCollection outputCollection = new PSDataCollection(); - -#if !PowerShellv3 - if (sendOutputToHost) - { - outputCollection.DataAdded += - (obj, e) => - { - for (int i = e.Index; i < outputCollection.Count; i++) - { - powerShellContext.WriteOutput( - outputCollection[i].ToString(), - true); - } - }; - } - - DebuggerCommandResults commandResults = - currentRunspace.Debugger.ProcessCommand( - psCommand, - outputCollection); - - // Pass along the debugger's resume action if the user's - // command caused one to be returned - debuggerResumeAction = commandResults.ResumeAction; -#endif - - IEnumerable results = null; - if (typeof(TResult) != typeof(PSObject)) - { - results = - outputCollection - .Select(pso => pso.BaseObject) - .Cast(); - } - else - { - results = outputCollection.Cast(); - } - - return results; - } - - public void StopCommandInDebugger(PowerShellContext powerShellContext) - { -#if !PowerShellv3 - powerShellContext.CurrentRunspace.Runspace.Debugger.StopProcessCommand(); -#endif - } - - public virtual bool IsDebuggerStopped(PromptNest promptNest, Runspace runspace) - { - return promptNest.IsInDebugger; - } - - public void ExitNestedPrompt(PSHost host) - { -#if !PowerShellv3 - try - { - host.ExitNestedPrompt(); - } - catch (FlowControlException) - { - } -#else - throw new NotSupportedException(); -#endif - } - } -} - diff --git a/src/PowerShellEditorServices/Session/PowerShell5Operations.cs b/src/PowerShellEditorServices/Session/PowerShell5Operations.cs index e27c3b14e..e91c0edc8 100644 --- a/src/PowerShellEditorServices/Session/PowerShell5Operations.cs +++ b/src/PowerShellEditorServices/Session/PowerShell5Operations.cs @@ -3,30 +3,100 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Host; using System.Management.Automation.Runspaces; namespace Microsoft.PowerShell.EditorServices.Session { - internal class PowerShell5Operations : PowerShell4Operations + internal class PowerShell5Operations : IVersionSpecificOperations { - public override void PauseDebugger(Runspace runspace) + public void ConfigureDebugger(Runspace runspace) + { + if (runspace.Debugger != null) + { + runspace.Debugger.SetDebugMode(DebugModes.LocalScript | DebugModes.RemoteScript); + } + } + + public virtual void PauseDebugger(Runspace runspace) { -#if !PowerShellv3 && !PowerShellv4 if (runspace.Debugger != null) { runspace.Debugger.SetDebuggerStepMode(true); } -#endif } - public override bool IsDebuggerStopped(PromptNest promptNest, Runspace runspace) + public virtual bool IsDebuggerStopped(PromptNest promptNest, Runspace runspace) + { + return runspace.Debugger.InBreakpoint || (promptNest.IsRemote && promptNest.IsInDebugger); + } + + public IEnumerable ExecuteCommandInDebugger( + PowerShellContext powerShellContext, + Runspace currentRunspace, + PSCommand psCommand, + bool sendOutputToHost, + out DebuggerResumeAction? debuggerResumeAction) + { + debuggerResumeAction = null; + PSDataCollection outputCollection = new PSDataCollection(); + + if (sendOutputToHost) + { + outputCollection.DataAdded += + (obj, e) => + { + for (int i = e.Index; i < outputCollection.Count; i++) + { + powerShellContext.WriteOutput( + outputCollection[i].ToString(), + true); + } + }; + } + + DebuggerCommandResults commandResults = + currentRunspace.Debugger.ProcessCommand( + psCommand, + outputCollection); + + // Pass along the debugger's resume action if the user's + // command caused one to be returned + debuggerResumeAction = commandResults.ResumeAction; + + IEnumerable results = null; + if (typeof(TResult) != typeof(PSObject)) + { + results = + outputCollection + .Select(pso => pso.BaseObject) + .Cast(); + } + else + { + results = outputCollection.Cast(); + } + + return results; + } + + public void StopCommandInDebugger(PowerShellContext powerShellContext) { -#if !PowerShellv3 && !PowerShellv4 - return runspace.Debugger.InBreakpoint || - (promptNest.IsRemote && promptNest.IsInDebugger); -#else - throw new System.NotSupportedException(); -#endif + powerShellContext.CurrentRunspace.Runspace.Debugger.StopProcessCommand(); + } + + public void ExitNestedPrompt(PSHost host) + { + try + { + host.ExitNestedPrompt(); + } + catch (FlowControlException) + { + } } } } diff --git a/src/PowerShellEditorServices/Session/PowerShellContext.cs b/src/PowerShellEditorServices/Session/PowerShellContext.cs index c67eb11f2..cf21554b0 100644 --- a/src/PowerShellEditorServices/Session/PowerShellContext.cs +++ b/src/PowerShellEditorServices/Session/PowerShellContext.cs @@ -167,12 +167,14 @@ public static Runspace CreateRunspace( /// public static Runspace CreateRunspace(PSHost psHost) { - var initialSessionState = InitialSessionState.CreateDefault2(); + InitialSessionState initialSessionState; + if (Environment.GetEnvironmentVariable("PSES_TEST_USE_CREATE_DEFAULT") == "1") { + initialSessionState = InitialSessionState.CreateDefault(); + } else { + initialSessionState = InitialSessionState.CreateDefault2(); + } Runspace runspace = RunspaceFactory.CreateRunspace(psHost, initialSessionState); -#if !CoreCLR - runspace.ApartmentState = ApartmentState.STA; -#endif runspace.ThreadOptions = PSThreadOptions.ReuseThread; runspace.Open(); @@ -247,14 +249,6 @@ public void Initialize( { this.versionSpecificOperations = new PowerShell5Operations(); } - else if (powerShellVersion.Major == 4) - { - this.versionSpecificOperations = new PowerShell4Operations(); - } - else if (powerShellVersion.Major == 3) - { - this.versionSpecificOperations = new PowerShell3Operations(); - } else { throw new NotSupportedException( @@ -1032,44 +1026,11 @@ internal async Task InvokeReadLineAsync(bool isCommandLine, Cancellation internal static TResult ExecuteScriptAndGetItem(string scriptToExecute, Runspace runspace, TResult defaultValue = default(TResult)) { - Pipeline pipeline = null; - - try - { - if (runspace.RunspaceAvailability == RunspaceAvailability.AvailableForNestedCommand) - { - pipeline = runspace.CreateNestedPipeline(scriptToExecute, false); - } - else - { - pipeline = runspace.CreatePipeline(scriptToExecute, false); - } - - Collection results = pipeline.Invoke(); - - if (results.Count == 0 || results.FirstOrDefault() == null) - { - return defaultValue; - } - - if (typeof(TResult) != typeof(PSObject)) - { - return results - .Select(pso => pso.BaseObject) - .OfType() - .FirstOrDefault(); - } - else - { - return - results - .OfType() - .FirstOrDefault(); - } - } - finally + using (PowerShell pwsh = PowerShell.Create()) { - pipeline.Dispose(); + pwsh.Runspace = runspace; + IEnumerable results = pwsh.AddScript(scriptToExecute).Invoke(); + return results.DefaultIfEmpty(defaultValue).First(); } } @@ -2098,20 +2059,18 @@ private SessionDetails GetSessionDetailsInDebugger() private SessionDetails GetSessionDetailsInNestedPipeline() { - using (var pipeline = this.CurrentRunspace.Runspace.CreateNestedPipeline()) - { - return this.GetSessionDetails( - command => + // We don't need to check what thread we're on here. If it's a local + // nested pipeline then we will already be on the correct thread, and + // non-debugger nested pipelines aren't supported in remote runspaces. + return this.GetSessionDetails( + command => + { + using (var localPwsh = PowerShell.Create(RunspaceMode.CurrentRunspace)) { - pipeline.Commands.Clear(); - pipeline.Commands.Add(command.Commands[0]); - - return - pipeline - .Invoke() - .FirstOrDefault(); - }); - } + localPwsh.Commands = command; + return localPwsh.Invoke().FirstOrDefault(); + } + }); } private void SetProfileVariableInCurrentRunspace(ProfilePaths profilePaths) diff --git a/src/PowerShellEditorServices/Workspace/ScriptFile.cs b/src/PowerShellEditorServices/Workspace/ScriptFile.cs index 0e0d4d6e7..f4dd3de26 100644 --- a/src/PowerShellEditorServices/Workspace/ScriptFile.cs +++ b/src/PowerShellEditorServices/Workspace/ScriptFile.cs @@ -602,10 +602,9 @@ private void ParseFileContents() { Token[] scriptTokens; -#if PowerShellv5r2 // This overload appeared with Windows 10 Update 1 - if (this.powerShellVersion.Major >= 5 && - this.powerShellVersion.Build >= 10586) + if (this.powerShellVersion.Major >= 6 || + (this.powerShellVersion.Major == 5 && this.powerShellVersion.Build >= 10586)) { // Include the file path so that module relative // paths are evaluated correctly @@ -624,13 +623,6 @@ private void ParseFileContents() out scriptTokens, out parseErrors); } -#else - this.ScriptAst = - Parser.ParseInput( - this.Contents, - out scriptTokens, - out parseErrors); -#endif this.ScriptTokens = scriptTokens; } diff --git a/test/PowerShellEditorServices.Test.Host/DebugAdapterTests.cs b/test/PowerShellEditorServices.Test.Host/DebugAdapterTests.cs index a07e79501..7a2ccd197 100644 --- a/test/PowerShellEditorServices.Test.Host/DebugAdapterTests.cs +++ b/test/PowerShellEditorServices.Test.Host/DebugAdapterTests.cs @@ -7,6 +7,7 @@ using Microsoft.PowerShell.EditorServices.Protocol.DebugAdapter; using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol; using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.Channel; +using Microsoft.PowerShell.EditorServices.Test.Shared; using Microsoft.PowerShell.EditorServices.Utility; using System; using System.IO; @@ -20,17 +21,13 @@ public class DebugAdapterTests : ServerTestsBase, IAsyncLifetime private ILogger logger; private DebugAdapterClient debugAdapterClient; private string DebugScriptPath = - Path.GetFullPath(@"..\..\..\..\PowerShellEditorServices.Test.Shared\Debugging\DebugTest.ps1"); + Path.GetFullPath(TestUtilities.NormalizePath("../../../../PowerShellEditorServices.Test.Shared/Debugging/DebugTest.ps1")); public async Task InitializeAsync() { string testLogPath = Path.Combine( -#if CoreCLR AppContext.BaseDirectory, -#else - AppDomain.CurrentDomain.BaseDirectory, -#endif "logs", this.GetType().Name, Guid.NewGuid().ToString().Substring(0, 8)); diff --git a/test/PowerShellEditorServices.Test.Host/LanguageServerTests.cs b/test/PowerShellEditorServices.Test.Host/LanguageServerTests.cs index 27daf6fae..50ec35f7f 100644 --- a/test/PowerShellEditorServices.Test.Host/LanguageServerTests.cs +++ b/test/PowerShellEditorServices.Test.Host/LanguageServerTests.cs @@ -11,6 +11,7 @@ using Microsoft.PowerShell.EditorServices.Protocol.Messages; using Microsoft.PowerShell.EditorServices.Protocol.Server; using Microsoft.PowerShell.EditorServices.Session; +using Microsoft.PowerShell.EditorServices.Test.Shared; using Microsoft.PowerShell.EditorServices.Utility; using System; using System.IO; @@ -29,11 +30,7 @@ public async Task InitializeAsync() { string testLogPath = Path.Combine( -#if CoreCLR AppContext.BaseDirectory, -#else - AppDomain.CurrentDomain.BaseDirectory, -#endif "logs", this.GetType().Name, Guid.NewGuid().ToString().Substring(0, 8)); @@ -77,7 +74,7 @@ public async Task DisposeAsync() public async Task ServiceReturnsSyntaxErrors() { // Send the 'didOpen' event - await this.SendOpenFileEvent("TestFiles\\SimpleSyntaxError.ps1", false); + await this.SendOpenFileEvent(TestUtilities.NormalizePath("TestFiles/SimpleSyntaxError.ps1"), false); // Wait for the diagnostic event PublishDiagnosticsNotification diagnostics = @@ -90,11 +87,11 @@ await this.WaitForEvent( string.IsNullOrEmpty(diagnostics.Diagnostics[0].Message)); } - [Fact(Skip = "Skipping until Script Analyzer integration is added back")] + [Fact] public async Task ServiceReturnsSemanticMarkers() { // Send the 'didOpen' event - await this.SendOpenFileEvent("TestFiles\\SimpleSemanticError.ps1", false); + await this.SendOpenFileEvent(TestUtilities.NormalizePath("TestFiles/SimpleSemanticError.ps1"), false); // Wait for the diagnostic event PublishDiagnosticsNotification diagnostics = @@ -110,7 +107,7 @@ await this.WaitForEvent( public async Task ServiceReturnsNoErrorsForUsingRelativeModulePaths() { // Send the 'didOpen' event - await this.SendOpenFileEvent("TestFiles\\Module.psm1", false); + await this.SendOpenFileEvent(TestUtilities.NormalizePath("TestFiles/Module.psm1"), false); // Wait for the diagnostic event PublishDiagnosticsNotification diagnostics = @@ -124,7 +121,7 @@ await this.WaitForEvent( [Fact] public async Task ServiceCompletesFunctionName() { - await this.SendOpenFileEvent("TestFiles\\CompleteFunctionName.ps1"); + await this.SendOpenFileEvent(TestUtilities.NormalizePath("TestFiles/CompleteFunctionName.ps1")); CompletionItem[] completions = await this.SendRequest( @@ -133,7 +130,7 @@ await this.SendRequest( { TextDocument = new TextDocumentIdentifier { - Uri = "TestFiles\\CompleteFunctionName.ps1", + Uri = TestUtilities.NormalizePath("TestFiles/CompleteFunctionName.ps1"), }, Position = new Position { @@ -151,7 +148,7 @@ await this.SendRequest( [Fact] public async Task CompletesDetailOnVariableSuggestion() { - await this.SendOpenFileEvent("TestFiles\\CompleteFunctionName.ps1"); + await this.SendOpenFileEvent(TestUtilities.NormalizePath("TestFiles/CompleteFunctionName.ps1")); CompletionItem[] completions = await this.SendRequest( @@ -160,7 +157,7 @@ await this.SendRequest( { TextDocument = new TextDocumentIdentifier { - Uri = "TestFiles\\CompleteFunctionName.ps1" + Uri = TestUtilities.NormalizePath("TestFiles/CompleteFunctionName.ps1") }, Position = new Position { @@ -181,7 +178,7 @@ await this.SendRequest( [Fact(Skip = "Skipped until variable documentation gathering is added back.")] public async Task CompletesDetailOnVariableDocSuggestion() { - await this.SendOpenFileEvent("TestFiles\\CompleteFunctionName.ps1"); + await this.SendOpenFileEvent(TestUtilities.NormalizePath("TestFiles/CompleteFunctionName.ps1")); await this.SendRequest( CompletionRequest.Type, @@ -189,7 +186,7 @@ await this.SendRequest( { TextDocument = new TextDocumentIdentifier { - Uri = "TestFiles\\CompleteFunctionName.ps1" + Uri = TestUtilities.NormalizePath("TestFiles/CompleteFunctionName.ps1") }, Position = new Position { @@ -223,7 +220,7 @@ await this.SendRequest( [Fact] public async Task CompletesDetailOnCommandSuggestion() { - await this.SendOpenFileEvent("TestFiles\\CompleteFunctionName.ps1"); + await this.SendOpenFileEvent(TestUtilities.NormalizePath("TestFiles/CompleteFunctionName.ps1")); CompletionItem[] completions = await this.SendRequest( @@ -232,7 +229,7 @@ await this.SendRequest( { TextDocument = new TextDocumentIdentifier { - Uri = "TestFiles\\CompleteFunctionName.ps1" + Uri = TestUtilities.NormalizePath("TestFiles/CompleteFunctionName.ps1") }, Position = new Position { @@ -263,7 +260,7 @@ await this.SendRequest( [Fact] public async Task FindsReferencesOfVariable() { - await this.SendOpenFileEvent("TestFiles\\FindReferences.ps1"); + await this.SendOpenFileEvent(TestUtilities.NormalizePath("TestFiles/FindReferences.ps1")); Location[] locations = await this.SendRequest( @@ -272,7 +269,7 @@ await this.SendRequest( { TextDocument = new TextDocumentIdentifier { - Uri = "TestFiles\\FindReferences.ps1" + Uri = TestUtilities.NormalizePath("TestFiles/FindReferences.ps1") }, Position = new Position { @@ -295,7 +292,7 @@ await this.SendRequest( [Fact] public async Task FindsNoReferencesOfEmptyLine() { - await this.SendOpenFileEvent("TestFiles\\FindReferences.ps1"); + await this.SendOpenFileEvent(TestUtilities.NormalizePath("TestFiles/FindReferences.ps1")); Location[] locations = await this.SendRequest( @@ -304,7 +301,7 @@ await this.SendRequest( { TextDocument = new TextDocumentIdentifier { - Uri = "TestFiles\\FindReferences.ps1" + Uri = TestUtilities.NormalizePath("TestFiles/FindReferences.ps1") }, Position = new Position { @@ -320,7 +317,7 @@ await this.SendRequest( [Fact] public async Task FindsReferencesOnFunctionDefinition() { - await this.SendOpenFileEvent("TestFiles\\FindReferences.ps1"); + await this.SendOpenFileEvent(TestUtilities.NormalizePath("TestFiles/FindReferences.ps1")); Location[] locations = await this.SendRequest( @@ -329,7 +326,7 @@ await this.SendRequest( { TextDocument = new TextDocumentIdentifier { - Uri = "TestFiles\\FindReferences.ps1" + Uri = TestUtilities.NormalizePath("TestFiles/FindReferences.ps1") }, Position = new Position { @@ -352,7 +349,7 @@ await this.SendRequest( [Fact] public async Task FindsReferencesOnCommand() { - await this.SendOpenFileEvent("TestFiles\\FindReferences.ps1"); + await this.SendOpenFileEvent(TestUtilities.NormalizePath("TestFiles/FindReferences.ps1")); Location[] locations = await this.SendRequest( @@ -361,7 +358,7 @@ await this.SendRequest( { TextDocument = new TextDocumentIdentifier { - Uri = "TestFiles\\FindReferences.ps1" + Uri = TestUtilities.NormalizePath("TestFiles/FindReferences.ps1") }, Position = new Position { @@ -384,7 +381,7 @@ await this.SendRequest( [Fact] public async Task FindsDefinitionOfCommand() { - await this.SendOpenFileEvent("TestFiles\\FindReferences.ps1"); + await this.SendOpenFileEvent(TestUtilities.NormalizePath("TestFiles/FindReferences.ps1")); Location[] locations = await this.SendRequest( @@ -393,7 +390,7 @@ await this.SendRequest( { TextDocument = new TextDocumentIdentifier { - Uri = "TestFiles\\FindReferences.ps1", + Uri = TestUtilities.NormalizePath("TestFiles/FindReferences.ps1"), }, Position = new Position { @@ -411,7 +408,7 @@ await this.SendRequest( [Fact] public async Task FindsNoDefinitionOfBuiltinCommand() { - await this.SendOpenFileEvent("TestFiles\\FindReferences.ps1"); + await this.SendOpenFileEvent(TestUtilities.NormalizePath("TestFiles/FindReferences.ps1")); Location[] locations = await this.SendRequest( @@ -420,7 +417,7 @@ await this.SendRequest( { TextDocument = new TextDocumentIdentifier { - Uri = "TestFiles\\FindReferences.ps1" + Uri = TestUtilities.NormalizePath("TestFiles/FindReferences.ps1") }, Position = new Position { @@ -436,7 +433,7 @@ await this.SendRequest( [Fact] public async Task FindsDefinitionOfVariable() { - await this.SendOpenFileEvent("TestFiles\\FindReferences.ps1"); + await this.SendOpenFileEvent(TestUtilities.NormalizePath("TestFiles/FindReferences.ps1")); Location[] locations = await this.SendRequest( @@ -445,7 +442,7 @@ await this.SendRequest( { TextDocument = new TextDocumentIdentifier { - Uri = "TestFiles\\FindReferences.ps1" + Uri = TestUtilities.NormalizePath("TestFiles/FindReferences.ps1") }, Position = new Position { @@ -465,7 +462,7 @@ await this.SendRequest( [Fact] public async Task FindsDefinitionOfVariableInOtherFile() { - await this.SendOpenFileEvent("TestFiles\\FindReferences.ps1"); + await this.SendOpenFileEvent(TestUtilities.NormalizePath("TestFiles/FindReferences.ps1")); Location[] locations = await this.SendRequest( @@ -474,7 +471,7 @@ await this.SendRequest( { TextDocument = new TextDocumentIdentifier { - Uri = "TestFiles\\FindReferences.ps1" + Uri = TestUtilities.NormalizePath("TestFiles/FindReferences.ps1") }, Position = new Position { @@ -495,7 +492,7 @@ await this.SendRequest( [Fact] public async Task FindDefinitionOfVariableWithSpecialChars() { - await this.SendOpenFileEvent("TestFiles\\FindReferences.ps1"); + await this.SendOpenFileEvent(TestUtilities.NormalizePath("TestFiles/FindReferences.ps1")); Location[] locations = await this.SendRequest( @@ -504,7 +501,7 @@ await this.SendRequest( { TextDocument = new TextDocumentIdentifier { - Uri = "TestFiles\\FindReferences.ps1" + Uri = TestUtilities.NormalizePath("TestFiles/FindReferences.ps1") }, Position = new Position { @@ -525,7 +522,7 @@ await this.SendRequest( [Fact] public async Task FindsOccurencesOnFunctionDefinition() { - await this.SendOpenFileEvent("TestFiles\\FindReferences.ps1"); + await this.SendOpenFileEvent(TestUtilities.NormalizePath("TestFiles/FindReferences.ps1")); DocumentHighlight[] highlights = await this.SendRequest( @@ -534,7 +531,7 @@ await this.SendRequest( { TextDocument = new TextDocumentIdentifier { - Uri = "TestFiles\\FindReferences.ps1" + Uri = TestUtilities.NormalizePath("TestFiles/FindReferences.ps1") }, Position = new Position { @@ -548,10 +545,10 @@ await this.SendRequest( Assert.Equal(2, highlights[1].Range.Start.Line); } - [Fact(Skip = "This test hangs in VSTS for some reason...")] + [Fact] public async Task GetsParameterHintsOnCommand() { - await this.SendOpenFileEvent("TestFiles\\FindReferences.ps1"); + await this.SendOpenFileEvent(TestUtilities.NormalizePath("TestFiles/FindReferences.ps1")); SignatureHelp signatureHelp = await this.SendRequest( @@ -560,7 +557,7 @@ await this.SendRequest( { TextDocument = new TextDocumentIdentifier { - Uri = "TestFiles\\FindReferences.ps1" + Uri = TestUtilities.NormalizePath("TestFiles/FindReferences.ps1") }, Position = new Position { @@ -603,9 +600,9 @@ public async Task ServiceExpandsAliases() string expandedText = await this.SendRequest( ExpandAliasRequest.Type, - "gci\r\npwd"); + TestUtilities.NormalizeNewlines("gci\npwd")); - Assert.Equal("Get-ChildItem\r\nGet-Location", expandedText); + Assert.Equal(TestUtilities.NormalizeNewlines("Get-ChildItem\nGet-Location"), expandedText); } [Fact] @@ -757,7 +754,7 @@ public async Task ServiceLoadsProfilesOnDemand() string testProfilePath = Path.Combine( Path.GetFullPath( - @"..\..\..\..\PowerShellEditorServices.Test.Shared\Profile\"), + TestUtilities.NormalizePath("../../../../PowerShellEditorServices.Test.Shared/Profile/")), profileName); string currentUserCurrentHostPath = diff --git a/test/PowerShellEditorServices.Test.Host/PowerShellEditorServices.Test.Host.csproj b/test/PowerShellEditorServices.Test.Host/PowerShellEditorServices.Test.Host.csproj index 8d7f65ba2..c183c3da3 100644 --- a/test/PowerShellEditorServices.Test.Host/PowerShellEditorServices.Test.Host.csproj +++ b/test/PowerShellEditorServices.Test.Host/PowerShellEditorServices.Test.Host.csproj @@ -1,42 +1,35 @@  - - netcoreapp2.0;net452 + netcoreapp2.1;net461 Microsoft.PowerShell.EditorServices.Test.Host - PreserveNewest - + - - - - 6.0.0-alpha13 - - - - - - - + $(DefineConstants);CoreCLR - - - - - - + + + + + + + + + + + diff --git a/test/PowerShellEditorServices.Test.Host/ServerTestsBase.cs b/test/PowerShellEditorServices.Test.Host/ServerTestsBase.cs index f8eef1062..c126be90a 100644 --- a/test/PowerShellEditorServices.Test.Host/ServerTestsBase.cs +++ b/test/PowerShellEditorServices.Test.Host/ServerTestsBase.cs @@ -4,6 +4,7 @@ // using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol; +using Microsoft.PowerShell.EditorServices.Test.Shared; using Microsoft.PowerShell.EditorServices.Utility; using Newtonsoft.Json.Linq; using System; @@ -11,6 +12,7 @@ using System.Diagnostics; using System.IO; using System.Reflection; +using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -34,19 +36,15 @@ protected async Task> LaunchService( string logPath, bool waitForDebugger = false) { - string modulePath = Path.GetFullPath(@"..\..\..\..\..\module"); - string scriptPath = Path.GetFullPath(Path.Combine(modulePath, @"PowerShellEditorServices\Start-EditorServices.ps1")); + string modulePath = Path.GetFullPath(TestUtilities.NormalizePath("../../../../../module")); + string scriptPath = Path.GetFullPath(Path.Combine(modulePath, "PowerShellEditorServices", "Start-EditorServices.ps1")); if (!File.Exists(scriptPath)) { throw new IOException(String.Format("Bad start script path: '{0}'", scriptPath)); } -#if CoreCLR Assembly assembly = this.GetType().GetTypeInfo().Assembly; -#else - Assembly assembly = this.GetType().Assembly; -#endif string assemblyPath = new Uri(assembly.CodeBase).LocalPath; FileVersionInfo fileVersionInfo = @@ -98,7 +96,7 @@ protected async Task> LaunchService( { StartInfo = new ProcessStartInfo { - FileName = "powershell.exe", + FileName = GetPwshExeName(), Arguments = string.Join(" ", args), CreateNoWindow = true, UseShellExecute = false, @@ -305,5 +303,14 @@ protected async Task>> WaitForRequest - - netcoreapp2.0;net452 + netcoreapp2.1;net461 Microsoft.PowerShell.EditorServices.Test.Protocol - + + + + + + + - - - - 10.0.3 - - - 6.0.0-alpha13 - - - + + + + - - + $(DefineConstants);CoreCLR - - - - - - diff --git a/test/PowerShellEditorServices.Test.Protocol/Server/OutputDebouncerTests.cs b/test/PowerShellEditorServices.Test.Protocol/Server/OutputDebouncerTests.cs index 50aad110e..9e3db950f 100644 --- a/test/PowerShellEditorServices.Test.Protocol/Server/OutputDebouncerTests.cs +++ b/test/PowerShellEditorServices.Test.Protocol/Server/OutputDebouncerTests.cs @@ -6,6 +6,7 @@ using Microsoft.PowerShell.EditorServices.Protocol.DebugAdapter; using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol; using Microsoft.PowerShell.EditorServices.Protocol.Server; +using Microsoft.PowerShell.EditorServices.Test.Shared; using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -39,7 +40,7 @@ public async Task OutputDebouncerAggregatesOutputEvents() // Assert that there's only one event with the expected string Assert.Equal(1, messageSender.OutputEvents.Count); Assert.Equal( - "This is a test\r\nAnother line", + TestUtilities.NormalizeNewlines("This is a test\nAnother line"), messageSender.OutputEvents[0].Output); // Wait for the next output to be flushed @@ -48,7 +49,7 @@ public async Task OutputDebouncerAggregatesOutputEvents() // Assert that there's only one event with the expected string Assert.Equal(2, messageSender.OutputEvents.Count); Assert.Equal( - "Another test line\r\nfor great justice", + TestUtilities.NormalizeNewlines("Another test line\nfor great justice"), messageSender.OutputEvents[1].Output); } @@ -76,8 +77,8 @@ public async Task OutputDebouncerDoesNotDuplicateOutput() // Ensure that the two events start with the correct lines Assert.Equal(2, messageSender.OutputEvents.Count); - Assert.Equal("Output 1", messageSender.OutputEvents[0].Output.Split('\r')[0]); - Assert.Equal("Output 26", messageSender.OutputEvents[1].Output.Split('\r')[0]); + Assert.Equal("Output 1", messageSender.OutputEvents[0].Output.Split('\n')[0].Trim('\r')); + Assert.Equal("Output 26", messageSender.OutputEvents[1].Output.Split('\n')[0].Trim('\r')); } private static Task SendOutput( diff --git a/test/PowerShellEditorServices.Test.Shared/Completion/CompleteAttributeValue.cs b/test/PowerShellEditorServices.Test.Shared/Completion/CompleteAttributeValue.cs index 1b5319ac2..43bf5bc86 100644 --- a/test/PowerShellEditorServices.Test.Shared/Completion/CompleteAttributeValue.cs +++ b/test/PowerShellEditorServices.Test.Shared/Completion/CompleteAttributeValue.cs @@ -7,10 +7,10 @@ namespace Microsoft.PowerShell.EditorServices.Test.Shared.Completion { public class CompleteAttributeValue { - public static readonly ScriptRegion SourceDetails = + public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"Completion\CompletionExamples.psm1", + File = TestUtilities.NormalizePath("Completion/CompletionExamples.psm1"), StartLineNumber = 16, StartColumnNumber = 38 }; diff --git a/test/PowerShellEditorServices.Test.Shared/Completion/CompleteCommandFromModule.cs b/test/PowerShellEditorServices.Test.Shared/Completion/CompleteCommandFromModule.cs index 3177dcbd5..5b03f9183 100644 --- a/test/PowerShellEditorServices.Test.Shared/Completion/CompleteCommandFromModule.cs +++ b/test/PowerShellEditorServices.Test.Shared/Completion/CompleteCommandFromModule.cs @@ -3,24 +3,31 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using System; using Microsoft.PowerShell.EditorServices; namespace Microsoft.PowerShell.EditorServices.Test.Shared.Completion { public class CompleteCommandFromModule { - public static readonly ScriptRegion SourceDetails = + private static readonly string[] s_getRandomParamSets = { + "Get-Random [[-Maximum] ] [-SetSeed ] [-Minimum ] []", + "Get-Random [-InputObject] [-SetSeed ] [-Count ] []" + }; + + public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"Completion\CompletionExamples.psm1", + File = TestUtilities.NormalizePath("Completion/CompletionExamples.psm1"), StartLineNumber = 13, - StartColumnNumber = 11 + StartColumnNumber = 8 }; public static readonly CompletionDetails ExpectedCompletion = CompletionDetails.Create( - "Install-Module", + "Get-Random", CompletionType.Command, - "Install-Module"); + string.Join(Environment.NewLine + Environment.NewLine, s_getRandomParamSets) + ); } } diff --git a/test/PowerShellEditorServices.Test.Shared/Completion/CompleteCommandInFile.cs b/test/PowerShellEditorServices.Test.Shared/Completion/CompleteCommandInFile.cs index 3c201cd9d..93bfb739d 100644 --- a/test/PowerShellEditorServices.Test.Shared/Completion/CompleteCommandInFile.cs +++ b/test/PowerShellEditorServices.Test.Shared/Completion/CompleteCommandInFile.cs @@ -9,10 +9,10 @@ namespace Microsoft.PowerShell.EditorServices.Test.Shared.Completion { public class CompleteCommandInFile { - public static readonly ScriptRegion SourceDetails = + public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"Completion\CompletionExamples.psm1", + File = TestUtilities.NormalizePath("Completion/CompletionExamples.psm1"), StartLineNumber = 8, StartColumnNumber = 7 }; diff --git a/test/PowerShellEditorServices.Test.Shared/Completion/CompleteFilePath.cs b/test/PowerShellEditorServices.Test.Shared/Completion/CompleteFilePath.cs index 4bc71b8bc..1d2a77863 100644 --- a/test/PowerShellEditorServices.Test.Shared/Completion/CompleteFilePath.cs +++ b/test/PowerShellEditorServices.Test.Shared/Completion/CompleteFilePath.cs @@ -13,12 +13,12 @@ namespace Microsoft.PowerShell.EditorServices.Test.Shared.Completion { public class CompleteFilePath { - public static readonly ScriptRegion SourceDetails = + public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"Completion\CompletionExamples.psm1", + File = TestUtilities.NormalizePath("Completion/CompletionExamples.psm1"), StartLineNumber = 19, - StartColumnNumber = 25 + StartColumnNumber = 15 }; public static readonly BufferRange ExpectedRange = diff --git a/test/PowerShellEditorServices.Test.Shared/Completion/CompleteVariableInFile.cs b/test/PowerShellEditorServices.Test.Shared/Completion/CompleteVariableInFile.cs index 9a728fa69..3a7bd4b40 100644 --- a/test/PowerShellEditorServices.Test.Shared/Completion/CompleteVariableInFile.cs +++ b/test/PowerShellEditorServices.Test.Shared/Completion/CompleteVariableInFile.cs @@ -9,10 +9,10 @@ namespace Microsoft.PowerShell.EditorServices.Test.Shared.Completion { public class CompleteVariableInFile { - public static readonly ScriptRegion SourceDetails = + public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"Completion\CompletionExamples.psm1", + File = TestUtilities.NormalizePath("Completion/CompletionExamples.psm1"), StartLineNumber = 10, StartColumnNumber = 9 }; diff --git a/test/PowerShellEditorServices.Test.Shared/Completion/CompletionExamples.psm1 b/test/PowerShellEditorServices.Test.Shared/Completion/CompletionExamples.psm1 index f34690cca..ee3dd9098 100644 --- a/test/PowerShellEditorServices.Test.Shared/Completion/CompletionExamples.psm1 +++ b/test/PowerShellEditorServices.Test.Shared/Completion/CompletionExamples.psm1 @@ -10,10 +10,10 @@ Get-So $testVar Import-Module PowerShellGet -Install-Mo +Get-Rand function Test-Completion { param([Parameter(Mandatory, Value)]) } -Get-ChildItem c:\Program +Get-ChildItem / diff --git a/test/PowerShellEditorServices.Test.Shared/Debugging/VariableTest.ps1 b/test/PowerShellEditorServices.Test.Shared/Debugging/VariableTest.ps1 index 7398d8c01..fc6896990 100644 --- a/test/PowerShellEditorServices.Test.Shared/Debugging/VariableTest.ps1 +++ b/test/PowerShellEditorServices.Test.Shared/Debugging/VariableTest.ps1 @@ -18,4 +18,4 @@ function Test-Variables { Write-Output "Done" } -Test-Variables \ No newline at end of file +Test-Variables diff --git a/test/PowerShellEditorServices.Test.Shared/Definition/FindsDotSourcedFile.cs b/test/PowerShellEditorServices.Test.Shared/Definition/FindsDotSourcedFile.cs index 20160081a..8147334c8 100644 --- a/test/PowerShellEditorServices.Test.Shared/Definition/FindsDotSourcedFile.cs +++ b/test/PowerShellEditorServices.Test.Shared/Definition/FindsDotSourcedFile.cs @@ -12,7 +12,7 @@ public class FindsDotSourcedFile public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"References\DotSources.ps1", + File = TestUtilities.NormalizeNewlines("References/DotSources.ps1"), StartLineNumber = 1, StartColumnNumber = 3 }; diff --git a/test/PowerShellEditorServices.Test.Shared/Definition/FindsFunctionDefinition.cs b/test/PowerShellEditorServices.Test.Shared/Definition/FindsFunctionDefinition.cs index 164d5798c..387e8e58b 100644 --- a/test/PowerShellEditorServices.Test.Shared/Definition/FindsFunctionDefinition.cs +++ b/test/PowerShellEditorServices.Test.Shared/Definition/FindsFunctionDefinition.cs @@ -10,7 +10,7 @@ public class FindsFunctionDefinition public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"References\SimpleFile.ps1", + File = TestUtilities.NormalizePath("References/SimpleFile.ps1"), StartLineNumber = 3, StartColumnNumber = 12 }; diff --git a/test/PowerShellEditorServices.Test.Shared/Definition/FindsFunctionDefinitionInDotSourceReference.cs b/test/PowerShellEditorServices.Test.Shared/Definition/FindsFunctionDefinitionInDotSourceReference.cs index bba3f2b27..e8fb1f3b5 100644 --- a/test/PowerShellEditorServices.Test.Shared/Definition/FindsFunctionDefinitionInDotSourceReference.cs +++ b/test/PowerShellEditorServices.Test.Shared/Definition/FindsFunctionDefinitionInDotSourceReference.cs @@ -12,7 +12,7 @@ public class FindsFunctionDefinitionInDotSourceReference public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"References\FileWithReferences.ps1", + File = TestUtilities.NormalizePath("References/FileWithReferences.ps1"), StartLineNumber = 3, StartColumnNumber = 6 }; diff --git a/test/PowerShellEditorServices.Test.Shared/Definition/FindsFunctionDefinitionInWorkspace.cs b/test/PowerShellEditorServices.Test.Shared/Definition/FindsFunctionDefinitionInWorkspace.cs index b32a6159f..1c48cfefe 100644 --- a/test/PowerShellEditorServices.Test.Shared/Definition/FindsFunctionDefinitionInWorkspace.cs +++ b/test/PowerShellEditorServices.Test.Shared/Definition/FindsFunctionDefinitionInWorkspace.cs @@ -12,7 +12,7 @@ public class FindsFunctionDefinitionInWorkspace public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"References\ReferenceFileD.ps1", + File = TestUtilities.NormalizePath("References/ReferenceFileD.ps1"), StartLineNumber = 1, StartColumnNumber = 2 }; diff --git a/test/PowerShellEditorServices.Test.Shared/Definition/FindsVariableDefinition.cs b/test/PowerShellEditorServices.Test.Shared/Definition/FindsVariableDefinition.cs index 6dab3ddad..1c785be21 100644 --- a/test/PowerShellEditorServices.Test.Shared/Definition/FindsVariableDefinition.cs +++ b/test/PowerShellEditorServices.Test.Shared/Definition/FindsVariableDefinition.cs @@ -16,7 +16,7 @@ public class FindsVariableDefinition public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"References\SimpleFile.ps1", + File = TestUtilities.NormalizePath("References/SimpleFile.ps1"), StartLineNumber = 8, StartColumnNumber = 3 }; diff --git a/test/PowerShellEditorServices.Test.Shared/Occurrences/FindOccurrencesOnParameter.cs b/test/PowerShellEditorServices.Test.Shared/Occurrences/FindOccurrencesOnParameter.cs index 62fc82716..7b3c368b7 100644 --- a/test/PowerShellEditorServices.Test.Shared/Occurrences/FindOccurrencesOnParameter.cs +++ b/test/PowerShellEditorServices.Test.Shared/Occurrences/FindOccurrencesOnParameter.cs @@ -10,7 +10,7 @@ public class FindOccurrencesOnParameter public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"References\SimpleFile.ps1", + File = TestUtilities.NormalizePath("References/SimpleFile.ps1"), StartLineNumber = 1, StartColumnNumber = 31 }; diff --git a/test/PowerShellEditorServices.Test.Shared/Occurrences/FindsOccurrencesOnFunction.cs b/test/PowerShellEditorServices.Test.Shared/Occurrences/FindsOccurrencesOnFunction.cs index 2b8258410..72c2adf95 100644 --- a/test/PowerShellEditorServices.Test.Shared/Occurrences/FindsOccurrencesOnFunction.cs +++ b/test/PowerShellEditorServices.Test.Shared/Occurrences/FindsOccurrencesOnFunction.cs @@ -12,7 +12,7 @@ public class FindsOccurrencesOnFunction public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"References\SimpleFile.ps1", + File = TestUtilities.NormalizePath("References/SimpleFile.ps1"), StartLineNumber = 1, StartColumnNumber = 17 }; diff --git a/test/PowerShellEditorServices.Test.Shared/ParameterHints/FindsParameterSetsOnCommand.cs b/test/PowerShellEditorServices.Test.Shared/ParameterHints/FindsParameterSetsOnCommand.cs index fcb4e88f0..797d93bd1 100644 --- a/test/PowerShellEditorServices.Test.Shared/ParameterHints/FindsParameterSetsOnCommand.cs +++ b/test/PowerShellEditorServices.Test.Shared/ParameterHints/FindsParameterSetsOnCommand.cs @@ -12,7 +12,7 @@ public class FindsParameterSetsOnCommand public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"ParameterHints\ParamHints.ps1", + File = TestUtilities.NormalizePath("ParameterHints/ParamHints.ps1"), StartLineNumber = 1, StartColumnNumber = 14 }; diff --git a/test/PowerShellEditorServices.Test.Shared/ParameterHints/FindsParameterSetsOnCommandWithSpaces.cs b/test/PowerShellEditorServices.Test.Shared/ParameterHints/FindsParameterSetsOnCommandWithSpaces.cs index 920530ec3..e7d40489e 100644 --- a/test/PowerShellEditorServices.Test.Shared/ParameterHints/FindsParameterSetsOnCommandWithSpaces.cs +++ b/test/PowerShellEditorServices.Test.Shared/ParameterHints/FindsParameterSetsOnCommandWithSpaces.cs @@ -10,7 +10,7 @@ public class FindsParameterSetsOnCommandWithSpaces public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"ParameterHints\ParamHints.ps1", + File = TestUtilities.NormalizePath("ParameterHints/ParamHints.ps1"), StartLineNumber = 9, StartColumnNumber = 31 }; diff --git a/test/PowerShellEditorServices.Test.Shared/PowerShellEditorServices.Test.Shared.csproj b/test/PowerShellEditorServices.Test.Shared/PowerShellEditorServices.Test.Shared.csproj index cee19e4f2..84bf41ab8 100644 --- a/test/PowerShellEditorServices.Test.Shared/PowerShellEditorServices.Test.Shared.csproj +++ b/test/PowerShellEditorServices.Test.Shared/PowerShellEditorServices.Test.Shared.csproj @@ -1,22 +1,15 @@  - 0.9.0-beta - netstandard1.6;net452 + netstandard2.0 Microsoft.PowerShell.EditorServices.Test.Shared - - + - - - $(DefineConstants);CoreCLR + + true + true - - - - - diff --git a/test/PowerShellEditorServices.Test.Shared/References/FileWithReferences.ps1 b/test/PowerShellEditorServices.Test.Shared/References/FileWithReferences.ps1 index fb39070e8..7a4e4cfe6 100644 --- a/test/PowerShellEditorServices.Test.Shared/References/FileWithReferences.ps1 +++ b/test/PowerShellEditorServices.Test.Shared/References/FileWithReferences.ps1 @@ -1,3 +1,3 @@ -. .\SimpleFile.ps1 +. ./SimpleFile.ps1 My-Function "test" diff --git a/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesOnBuiltInCommandWithAlias.cs b/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesOnBuiltInCommandWithAlias.cs index 2a22b13f2..c06e2452c 100644 --- a/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesOnBuiltInCommandWithAlias.cs +++ b/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesOnBuiltInCommandWithAlias.cs @@ -10,7 +10,7 @@ public class FindsReferencesOnBuiltInCommandWithAlias public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"References\SimpleFile.ps1", + File = TestUtilities.NormalizePath("References/SimpleFile.ps1"), StartLineNumber = 14, StartColumnNumber = 3 }; @@ -20,7 +20,7 @@ public class FindsReferencesOnBuiltInAlias public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"References\SimpleFile.ps1", + File = TestUtilities.NormalizePath("References/SimpleFile.ps1"), StartLineNumber = 15, StartColumnNumber = 2 }; diff --git a/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesOnFunction.cs b/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesOnFunction.cs index 6a53d69f2..28c3249fd 100644 --- a/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesOnFunction.cs +++ b/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesOnFunction.cs @@ -10,7 +10,7 @@ public class FindsReferencesOnFunction public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"References\SimpleFile.ps1", + File = TestUtilities.NormalizePath("References/SimpleFile.ps1"), StartLineNumber = 3, StartColumnNumber = 8 }; diff --git a/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesOnFunctionMultiFileDotSource.cs b/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesOnFunctionMultiFileDotSource.cs index 5624edf02..872df6ff1 100644 --- a/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesOnFunctionMultiFileDotSource.cs +++ b/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesOnFunctionMultiFileDotSource.cs @@ -12,7 +12,7 @@ public class FindsReferencesOnFunctionMultiFileDotSourceFileB public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"References\ReferenceFileB.ps1", + File = TestUtilities.NormalizePath("References/ReferenceFileB.ps1"), StartLineNumber = 5, StartColumnNumber = 8 }; @@ -22,7 +22,7 @@ public class FindsReferencesOnFunctionMultiFileDotSourceFileC public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"References\ReferenceFileC.ps1", + File = TestUtilities.NormalizePath("References/ReferenceFileC.ps1"), StartLineNumber = 4, StartColumnNumber = 10 }; diff --git a/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesonVariable.cs b/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesonVariable.cs index b5ae61af4..1860e56d9 100644 --- a/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesonVariable.cs +++ b/test/PowerShellEditorServices.Test.Shared/References/FindsReferencesonVariable.cs @@ -12,7 +12,7 @@ public class FindsReferencesOnVariable public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"References\SimpleFile.ps1", + File = TestUtilities.NormalizePath("References/SimpleFile.ps1"), StartLineNumber = 10, StartColumnNumber = 17 }; diff --git a/test/PowerShellEditorServices.Test.Shared/References/ReferenceFileA.ps1 b/test/PowerShellEditorServices.Test.Shared/References/ReferenceFileA.ps1 index a43f59fb2..31ef35600 100644 --- a/test/PowerShellEditorServices.Test.Shared/References/ReferenceFileA.ps1 +++ b/test/PowerShellEditorServices.Test.Shared/References/ReferenceFileA.ps1 @@ -1,9 +1,9 @@ . .\ReferenceFileA.ps1 -. .\ReferenceFileB.ps1 +. ./ReferenceFileB.ps1 . .\ReferenceFileC.ps1 function My-Function ($myInput) { My-Function $myInput } -Get-ChildItem \ No newline at end of file +Get-ChildItem diff --git a/test/PowerShellEditorServices.Test.Shared/References/ReferenceFileC.ps1 b/test/PowerShellEditorServices.Test.Shared/References/ReferenceFileC.ps1 index e17fd096f..6e1ee3131 100644 --- a/test/PowerShellEditorServices.Test.Shared/References/ReferenceFileC.ps1 +++ b/test/PowerShellEditorServices.Test.Shared/References/ReferenceFileC.ps1 @@ -1,4 +1,4 @@ -. .\ReferenceFileA.ps1 +. ./ReferenceFileA.ps1 Get-ChildItem -My-Function "testc" \ No newline at end of file +My-Function "testc" diff --git a/test/PowerShellEditorServices.Test.Shared/References/SimpleFile.ps1 b/test/PowerShellEditorServices.Test.Shared/References/SimpleFile.ps1 index 7e9022cf2..527045b1e 100644 --- a/test/PowerShellEditorServices.Test.Shared/References/SimpleFile.ps1 +++ b/test/PowerShellEditorServices.Test.Shared/References/SimpleFile.ps1 @@ -12,9 +12,7 @@ My-Function $things Write-Output "Hello World"; Get-ChildItem -ls gci dir -LS Write-Host -Get-ChildItem \ No newline at end of file +Get-ChildItem diff --git a/test/PowerShellEditorServices.Test.Shared/SymbolDetails/FindsDetailsForBuiltInCommand.cs b/test/PowerShellEditorServices.Test.Shared/SymbolDetails/FindsDetailsForBuiltInCommand.cs index c3526938b..d46206bcb 100644 --- a/test/PowerShellEditorServices.Test.Shared/SymbolDetails/FindsDetailsForBuiltInCommand.cs +++ b/test/PowerShellEditorServices.Test.Shared/SymbolDetails/FindsDetailsForBuiltInCommand.cs @@ -10,7 +10,7 @@ public class FindsDetailsForBuiltInCommand public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"SymbolDetails\SymbolDetails.ps1", + File = TestUtilities.NormalizePath("SymbolDetails/SymbolDetails.ps1"), StartLineNumber = 1, StartColumnNumber = 10 }; diff --git a/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInMultiSymbolFile.cs b/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInMultiSymbolFile.cs index 7f3dc68e8..ab3ce87b3 100644 --- a/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInMultiSymbolFile.cs +++ b/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInMultiSymbolFile.cs @@ -9,7 +9,7 @@ public class FindSymbolsInMultiSymbolFile { public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"Symbols\MultipleSymbols.ps1" + File = TestUtilities.NormalizePath("Symbols/MultipleSymbols.ps1") }; } } diff --git a/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInNoSymbolsFile.cs b/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInNoSymbolsFile.cs index cf1692030..c8c814ca0 100644 --- a/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInNoSymbolsFile.cs +++ b/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInNoSymbolsFile.cs @@ -9,7 +9,7 @@ public class FindSymbolsInNoSymbolsFile { public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"Symbols\NoSymbols.ps1" + File = TestUtilities.NormalizePath("Symbols/NoSymbols.ps1") }; } } diff --git a/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInPSDFile.cs b/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInPSDFile.cs index 8d96f49d6..6343b79cc 100644 --- a/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInPSDFile.cs +++ b/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInPSDFile.cs @@ -10,7 +10,7 @@ public class FindSymbolsInPSDFile public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"Symbols\PowerShellDataFile.psd1" + File = TestUtilities.NormalizePath("Symbols/PowerShellDataFile.psd1") }; } } diff --git a/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInPesterFile.cs b/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInPesterFile.cs index cebcae3aa..19a7c6519 100644 --- a/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInPesterFile.cs +++ b/test/PowerShellEditorServices.Test.Shared/Symbols/FindSymbolsInPesterFile.cs @@ -10,7 +10,7 @@ public class FindSymbolsInPesterFile public static readonly ScriptRegion SourceDetails = new ScriptRegion { - File = @"Symbols\PesterFile.tests.ps1" + File = TestUtilities.NormalizePath("Symbols/PesterFile.tests.ps1") }; } } diff --git a/test/PowerShellEditorServices.Test.Shared/TestUtilities/TestUtilities.cs b/test/PowerShellEditorServices.Test.Shared/TestUtilities/TestUtilities.cs new file mode 100644 index 000000000..dd6cc1211 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/TestUtilities/TestUtilities.cs @@ -0,0 +1,90 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Microsoft.PowerShell.EditorServices.Test.Shared +{ + /// + /// Convenience class to simplify cross-platform testing + /// + public static class TestUtilities + { + private static readonly char[] s_unixPathSeparators = new [] { '/' }; + + private static readonly char[] s_unixNewlines = new [] { '\n' }; + + /// + /// Takes a UNIX-style path and converts it to the path appropriate to the platform. + /// + /// A forward-slash separated path. + /// A path with directories separated by the appropriate separator. + public static string NormalizePath(string unixPath) + { + if (unixPath == null) + { + return unixPath; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return unixPath.Replace('/', Path.DirectorySeparatorChar); + } + + return unixPath; + } + + /// + /// Take a string with UNIX newlines and replaces them with platform-appropriate newlines. + /// + /// The string with UNIX-style newlines. + /// The platform-newline-normalized string. + public static string NormalizeNewlines(string unixString) + { + if (unixString == null) + { + return unixString; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return String.Join(Environment.NewLine, unixString.Split(s_unixNewlines)); + } + + return unixString; + } + + /// + /// Platform-normalize a string -- takes a UNIX-style string and gives it platform-appropriate newlines and path separators. + /// + /// The string to normalize for the platform, given with UNIX-specific separators. + /// The same string but separated by platform-appropriate directory and newline separators. + public static string PlatformNormalize(string unixString) + { + return NormalizeNewlines(NormalizePath(unixString)); + } + + /// + /// Not for use in production -- convenience code for debugging tests. + /// + public static void AWAIT_DEBUGGER_HERE( + [CallerMemberName] string callerName = null, + [CallerFilePath] string callerPath = null, + [CallerLineNumber] int callerLine = -1) + { + if (Debugger.IsAttached) + { + return; + } + + System.Console.WriteLine(); + System.Console.WriteLine("===== AWAITING DEBUGGER ====="); + System.Console.WriteLine($" PID: {Process.GetCurrentProcess().Id}"); + System.Console.WriteLine($" Waiting at {callerPath} line {callerLine} ({callerName})"); + System.Console.WriteLine(" PRESS ANY KEY TO CONTINUE"); + System.Console.WriteLine("============================="); + System.Console.ReadKey(); + } + } +} diff --git a/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs b/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs index 11aecad3f..4a5d53e82 100644 --- a/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs @@ -5,6 +5,7 @@ using Microsoft.PowerShell.EditorServices.Debugging; using Microsoft.PowerShell.EditorServices.Utility; +using Microsoft.PowerShell.EditorServices.Test.Shared; using System; using System.Collections.Generic; using System.Linq; @@ -41,11 +42,11 @@ public DebugServiceTests() // Load the test debug file this.debugScriptFile = this.workspace.GetFile( - @"..\..\..\..\PowerShellEditorServices.Test.Shared\Debugging\DebugTest.ps1"); + TestUtilities.NormalizePath("../../../../PowerShellEditorServices.Test.Shared/Debugging/VariableTest.ps1")); this.variableScriptFile = this.workspace.GetFile( - @"..\..\..\..\PowerShellEditorServices.Test.Shared\Debugging\VariableTest.ps1"); + TestUtilities.NormalizePath("../../../../PowerShellEditorServices.Test.Shared/Debugging/VariableTest.ps1")); this.debugService = new DebugService(this.powerShellContext, logger); this.debugService.DebuggerStopped += debugService_DebuggerStopped; @@ -55,7 +56,7 @@ public DebugServiceTests() // Load the test debug file this.debugScriptFile = this.workspace.GetFile( - @"..\..\..\..\PowerShellEditorServices.Test.Shared\Debugging\DebugTest.ps1"); + TestUtilities.NormalizePath("../../../../PowerShellEditorServices.Test.Shared/Debugging/DebugTest.ps1")); } async void powerShellContext_SessionStateChanged(object sender, SessionStateChangedEventArgs e) @@ -108,7 +109,7 @@ public async Task DebuggerAcceptsScriptArgs(string[] args) // it should not escape already escaped chars. ScriptFile debugWithParamsFile = this.workspace.GetFile( - @"..\..\..\..\PowerShellEditorServices.Test.Shared\Debugging\Debug` W&ith Params `[Test].ps1"); + TestUtilities.NormalizePath("../../../../PowerShellEditorServices.Test.Shared/Debugging/Debug` W&ith Params `[Test].ps1")); await this.debugService.SetLineBreakpoints( debugWithParamsFile, @@ -638,7 +639,7 @@ await this.debugService.SetLineBreakpoints( // Test set of global scope int variable (not strongly typed) VariableScope globalScope = scopes.FirstOrDefault(s => s.Name == VariableContainerDetails.GlobalScopeName); string newGlobalIntValue = "4242"; - string setGlobalIntValue = await debugService.SetVariable(globalScope.Id, "$MaximumAliasCount", newGlobalIntValue); + string setGlobalIntValue = await debugService.SetVariable(globalScope.Id, "$MaximumHistoryCount", newGlobalIntValue); Assert.Equal(newGlobalIntValue, setGlobalIntValue); // The above just tests that the debug service returns the correct new value string. @@ -664,7 +665,7 @@ await this.debugService.SetLineBreakpoints( // Test set of global scope int variable (not strongly typed) globalScope = scopes.FirstOrDefault(s => s.Name == VariableContainerDetails.GlobalScopeName); variables = debugService.GetVariables(globalScope.Id); - var intGlobalVar = variables.FirstOrDefault(v => v.Name == "$MaximumAliasCount"); + var intGlobalVar = variables.FirstOrDefault(v => v.Name == "$MaximumHistoryCount"); Assert.Equal(newGlobalIntValue, intGlobalVar.ValueString); // Abort execution of the script @@ -789,17 +790,26 @@ await this.debugService.SetLineBreakpoints( VariableDetailsBase[] variables = debugService.GetVariables(stackFrames[0].LocalVariables.Id); - var var = variables.FirstOrDefault(v => v.Name == "$assocArrVar"); + VariableDetailsBase var = variables.FirstOrDefault(v => v.Name == "$assocArrVar"); Assert.NotNull(var); Assert.Equal("[Hashtable: 2]", var.ValueString); Assert.True(var.IsExpandable); - var childVars = debugService.GetVariables(var.Id); + VariableDetailsBase[] childVars = debugService.GetVariables(var.Id); Assert.Equal(9, childVars.Length); Assert.Equal("[0]", childVars[0].Name); - Assert.Equal("[secondChild, 42]", childVars[0].ValueString); Assert.Equal("[1]", childVars[1].Name); - Assert.Equal("[firstChild, \"Child\"]", childVars[1].ValueString); + + var childVarStrs = new HashSet(childVars.Select(v => v.ValueString)); + var expectedVars = new [] { + "[firstChild, \"Child\"]", + "[secondChild, 42]" + }; + + foreach (string expectedVar in expectedVars) + { + Assert.Contains(expectedVar, childVarStrs); + } // Abort execution of the script this.powerShellContext.AbortExecution(); @@ -824,17 +834,17 @@ await this.debugService.SetLineBreakpoints( VariableDetailsBase[] variables = debugService.GetVariables(stackFrames[0].LocalVariables.Id); - var var = variables.FirstOrDefault(v => v.Name == "$psObjVar"); - Assert.NotNull(var); - Assert.Equal("@{Age=75; Name=John}", var.ValueString); - Assert.True(var.IsExpandable); + var psObjVar = variables.FirstOrDefault(v => v.Name == "$psObjVar"); + Assert.NotNull(psObjVar); + Assert.True("@{Age=75; Name=John}".Equals(psObjVar.ValueString) || "@{Name=John; Age=75}".Equals(psObjVar.ValueString)); + Assert.True(psObjVar.IsExpandable); - var childVars = debugService.GetVariables(var.Id); - Assert.Equal(2, childVars.Length); - Assert.Equal("Age", childVars[0].Name); - Assert.Equal("75", childVars[0].ValueString); - Assert.Equal("Name", childVars[1].Name); - Assert.Equal("\"John\"", childVars[1].ValueString); + IDictionary childVars = debugService.GetVariables(psObjVar.Id).ToDictionary(v => v.Name, v => v.ValueString); + Assert.Equal(2, childVars.Count); + Assert.Contains("Age", childVars.Keys); + Assert.Contains("Name", childVars.Keys); + Assert.Equal(childVars["Age"], "75"); + Assert.Equal(childVars["Name"], "\"John\""); // Abort execution of the script this.powerShellContext.AbortExecution(); @@ -875,9 +885,15 @@ await this.debugService.SetLineBreakpoints( this.powerShellContext.AbortExecution(); } +// TODO: Make this test cross platform by using the PowerShell process +// (the only process we can guarantee cross-platform) +#if CoreCLR + [Fact(Skip = "Need to use the PowerShell process in a cross-platform way for this test to work")] +#else // Verifies fix for issue #86, $proc = Get-Process foo displays just the // ETS property set and not all process properties. [Fact] +#endif public async Task DebuggerVariableProcessObjDisplaysCorrectly() { await this.debugService.SetLineBreakpoints( @@ -906,7 +922,7 @@ await this.debugService.SetLineBreakpoints( // Abort execution of the script this.powerShellContext.AbortExecution(); - } + } public async Task AssertDebuggerPaused() { diff --git a/test/PowerShellEditorServices.Test/Extensions/ExtensionServiceTests.cs b/test/PowerShellEditorServices.Test/Extensions/ExtensionServiceTests.cs index e56656a91..435d16ecb 100644 --- a/test/PowerShellEditorServices.Test/Extensions/ExtensionServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Extensions/ExtensionServiceTests.cs @@ -5,6 +5,7 @@ using Microsoft.PowerShell.EditorServices.Components; using Microsoft.PowerShell.EditorServices.Extensions; +using Microsoft.PowerShell.EditorServices.Test.Shared; using Microsoft.PowerShell.EditorServices.Utility; using System; using System.Collections.Generic; @@ -38,7 +39,8 @@ public async Task InitializeAsync() { var logger = Logging.NullLogger; this.powerShellContext = PowerShellContextFactory.Create(logger); - await this.powerShellContext.ImportCommandsModule(@"..\..\..\..\..\module\PowerShellEditorServices\Commands"); + await this.powerShellContext.ImportCommandsModule( + TestUtilities.NormalizePath("../../../../../module/PowerShellEditorServices/Commands")); this.extensionService = new ExtensionService(this.powerShellContext); this.editorOperations = new TestEditorOperations(); @@ -51,7 +53,7 @@ await this.extensionService.Initialize( this.editorOperations, new ComponentRegistry()); - var filePath = @"c:\Test\Test.ps1"; + var filePath = TestUtilities.NormalizePath("c:/Test/Test.ps1"); this.currentFile = new ScriptFile(filePath, filePath, "This is a test file", new Version("5.0")); this.commandContext = new EditorContext( @@ -71,7 +73,7 @@ public Task DisposeAsync() public async Task CanRegisterAndInvokeCommandWithCmdletName() { await extensionService.PowerShellContext.ExecuteScriptString( - "function Invoke-Extension { $global:extensionValue = 5 }\r\n" + + TestUtilities.NormalizeNewlines("function Invoke-Extension { $global:extensionValue = 5 }\n") + "Register-EditorCommand -Name \"test.function\" -DisplayName \"Function extension\" -Function \"Invoke-Extension\""); // Wait for the add event @@ -110,10 +112,10 @@ await extensionService.PowerShellContext.ExecuteScriptString( public async Task CanUpdateRegisteredCommand() { // Register a command and then update it - await extensionService.PowerShellContext.ExecuteScriptString( - "function Invoke-Extension { Write-Output \"Extension output!\" }\r\n" + - "Register-EditorCommand -Name \"test.function\" -DisplayName \"Function extension\" -Function \"Invoke-Extension\"\r\n" + - "Register-EditorCommand -Name \"test.function\" -DisplayName \"Updated Function extension\" -Function \"Invoke-Extension\""); + await extensionService.PowerShellContext.ExecuteScriptString(TestUtilities.NormalizeNewlines( + "function Invoke-Extension { Write-Output \"Extension output!\" }\n" + + "Register-EditorCommand -Name \"test.function\" -DisplayName \"Function extension\" -Function \"Invoke-Extension\"\n" + + "Register-EditorCommand -Name \"test.function\" -DisplayName \"Updated Function extension\" -Function \"Invoke-Extension\"")); // Wait for the add and update events await this.AssertExtensionEvent(EventType.Add, "test.function"); diff --git a/test/PowerShellEditorServices.Test/Language/LanguageServiceTests.cs b/test/PowerShellEditorServices.Test/Language/LanguageServiceTests.cs index a432fb73f..1d4972053 100644 --- a/test/PowerShellEditorServices.Test/Language/LanguageServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Language/LanguageServiceTests.cs @@ -16,6 +16,8 @@ using System.Threading.Tasks; using Xunit; using Microsoft.PowerShell.EditorServices.Utility; +using Microsoft.PowerShell.EditorServices.Test.Shared; +using System.Runtime.InteropServices; namespace Microsoft.PowerShell.EditorServices.Test.Language { @@ -24,7 +26,8 @@ public class LanguageServiceTests : IDisposable private Workspace workspace; private LanguageService languageService; private PowerShellContext powerShellContext; - private const string baseSharedScriptPath = @"..\..\..\..\PowerShellEditorServices.Test.Shared\"; + private static readonly string s_baseSharedScriptPath = + TestUtilities.NormalizePath("../../../../PowerShellEditorServices.Test.Shared/"); public LanguageServiceTests() { @@ -52,7 +55,7 @@ await this.GetCompletionResults( completionResults.Completions[0]); } - [Fact(Skip = "This test does not run correctly on AppVeyor, need to investigate.")] + [Fact] public async Task LanguageServiceCompletesCommandFromModule() { CompletionResults completionResults = @@ -99,9 +102,14 @@ await this.GetCompletionResults( CompleteFilePath.SourceDetails); Assert.NotEqual(0, completionResults.Completions.Length); - Assert.Equal( - CompleteFilePath.ExpectedRange, - completionResults.ReplacedRange); + // TODO: Since this is a path completion, this test will need to be + // platform specific. Probably something like: + // - Windows: C:\Program + // - macOS: /User + // - Linux: /hom + //Assert.Equal( + // CompleteFilePath.ExpectedRange, + // completionResults.ReplacedRange); } [Fact] @@ -183,7 +191,7 @@ await this.GetDefinition( FindsFunctionDefinitionInWorkspace.SourceDetails, new Workspace(this.powerShellContext.LocalPowerShellVersion.Version, Logging.NullLogger) { - WorkspacePath = Path.Combine(baseSharedScriptPath, @"References") + WorkspacePath = Path.Combine(s_baseSharedScriptPath, @"References") }); var definition = definitionResult.FoundDefinition; Assert.EndsWith("ReferenceFileE.ps1", definition.FilePath); @@ -234,9 +242,10 @@ public async Task LanguageServiceFindsReferencesOnCommandWithAlias() await this.GetReferences( FindsReferencesOnBuiltInCommandWithAlias.SourceDetails); - Assert.Equal(6, refsResult.FoundReferences.Count()); - Assert.Equal("Get-ChildItem", refsResult.FoundReferences.Last().SymbolName); - Assert.Equal("ls", refsResult.FoundReferences.ToArray()[1].SymbolName); + SymbolReference[] foundRefs = refsResult.FoundReferences.ToArray(); + Assert.Equal(4, foundRefs.Length); + Assert.Equal("gci", foundRefs[1].SymbolName); + Assert.Equal("Get-ChildItem", foundRefs[foundRefs.Length - 1].SymbolName); } [Fact] @@ -246,10 +255,9 @@ public async Task LanguageServiceFindsReferencesOnAlias() await this.GetReferences( FindsReferencesOnBuiltInCommandWithAlias.SourceDetails); - Assert.Equal(6, refsResult.FoundReferences.Count()); + Assert.Equal(4, refsResult.FoundReferences.Count()); + Assert.Equal("dir", refsResult.FoundReferences.ToArray()[2].SymbolName); Assert.Equal("Get-ChildItem", refsResult.FoundReferences.Last().SymbolName); - Assert.Equal("gci", refsResult.FoundReferences.ToArray()[2].SymbolName); - Assert.Equal("LS", refsResult.FoundReferences.ToArray()[4].SymbolName); } [Fact] @@ -346,7 +354,7 @@ private ScriptFile GetScriptFile(ScriptRegion scriptRegion) { string resolvedPath = Path.Combine( - baseSharedScriptPath, + s_baseSharedScriptPath, scriptRegion.File); return diff --git a/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj b/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj index 579ee55e0..383baffae 100644 --- a/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj +++ b/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj @@ -1,37 +1,33 @@  - - netcoreapp2.0;net452 + netcoreapp2.1;net461 Microsoft.PowerShell.EditorServices.Test - + + true + true + - - - - 6.0.0-alpha13 - - - - + + - - - $(DefineConstants);CoreCLR - - - - - - - + + + + + + + + - + + $(DefineConstants);CoreCLR + diff --git a/test/PowerShellEditorServices.Test/Session/PowerShellContextTests.cs b/test/PowerShellEditorServices.Test/Session/PowerShellContextTests.cs index ef07ae730..dcb0e738a 100644 --- a/test/PowerShellEditorServices.Test/Session/PowerShellContextTests.cs +++ b/test/PowerShellEditorServices.Test/Session/PowerShellContextTests.cs @@ -4,6 +4,7 @@ // using Microsoft.PowerShell.EditorServices.Session; +using Microsoft.PowerShell.EditorServices.Test.Shared; using Microsoft.PowerShell.EditorServices.Utility; using System; using System.Collections.Generic; @@ -20,8 +21,8 @@ public class PowerShellContextTests : IDisposable private PowerShellContext powerShellContext; private AsyncQueue stateChangeQueue; - private const string DebugTestFilePath = - @"..\..\..\..\PowerShellEditorServices.Test.Shared\Debugging\DebugTest.ps1"; + private static readonly string s_debugTestFilePath = + TestUtilities.NormalizePath("../../../../PowerShellEditorServices.Test.Shared/Debugging/DebugTest.ps1"); public static readonly HostDetails TestHostDetails = new HostDetails( @@ -36,9 +37,9 @@ public class PowerShellContextTests : IDisposable new ProfilePaths( TestHostDetails.ProfileId, Path.GetFullPath( - @"..\..\..\..\PowerShellEditorServices.Test.Shared\Profile"), + TestUtilities.NormalizePath("../../../../PowerShellEditorServices.Test.Shared/Profile")), Path.GetFullPath( - @"..\..\..\..\PowerShellEditorServices.Test.Shared")); + TestUtilities.NormalizePath("../../../../PowerShellEditorServices.Test.Shared"))); public PowerShellContextTests() { @@ -104,7 +105,7 @@ public async Task CanAbortExecution() Task.Run( async () => { - var unusedTask = this.powerShellContext.ExecuteScriptWithArgs(DebugTestFilePath); + var unusedTask = this.powerShellContext.ExecuteScriptWithArgs(s_debugTestFilePath); await Task.Delay(50); this.powerShellContext.AbortExecution(); }); diff --git a/test/PowerShellEditorServices.Test/Session/ScriptFileTests.cs b/test/PowerShellEditorServices.Test/Session/ScriptFileTests.cs index 993e56999..15be76c9d 100644 --- a/test/PowerShellEditorServices.Test/Session/ScriptFileTests.cs +++ b/test/PowerShellEditorServices.Test/Session/ScriptFileTests.cs @@ -4,6 +4,7 @@ // using Microsoft.PowerShell.EditorServices; +using Microsoft.PowerShell.EditorServices.Test.Shared; using System; using System.IO; using System.Linq; @@ -13,7 +14,12 @@ namespace PSLanguageService.Test { public class ScriptFileChangeTests { - private static readonly Version PowerShellVersion = new Version("5.0"); + +#if CoreCLR + private static readonly Version PowerShellVersion = new Version(6, 1); +#else + private static readonly Version PowerShellVersion = new Version(5, 1); +#endif [Fact] public void CanApplySingleLineInsert() @@ -67,15 +73,15 @@ public void CanApplySingleLineDelete() public void CanApplyMultiLineInsert() { this.AssertFileChange( - "first\r\nsecond\r\nfifth", - "first\r\nsecond\r\nthird\r\nfourth\r\nfifth", + TestUtilities.NormalizeNewlines("first\nsecond\nfifth"), + TestUtilities.NormalizeNewlines("first\nsecond\nthird\nfourth\nfifth"), new FileChange { Line = 3, EndLine = 3, Offset = 1, EndOffset = 1, - InsertString = "third\r\nfourth\r\n" + InsertString = TestUtilities.NormalizeNewlines("third\nfourth\n") }); } @@ -83,15 +89,15 @@ public void CanApplyMultiLineInsert() public void CanApplyMultiLineReplace() { this.AssertFileChange( - "first\r\nsecoXX\r\nXXfth", - "first\r\nsecond\r\nthird\r\nfourth\r\nfifth", + TestUtilities.NormalizeNewlines("first\nsecoXX\nXXfth"), + TestUtilities.NormalizeNewlines("first\nsecond\nthird\nfourth\nfifth"), new FileChange { Line = 2, EndLine = 3, Offset = 5, EndOffset = 3, - InsertString = "nd\r\nthird\r\nfourth\r\nfi" + InsertString = TestUtilities.NormalizeNewlines("nd\nthird\nfourth\nfi") }); } @@ -99,15 +105,15 @@ public void CanApplyMultiLineReplace() public void CanApplyMultiLineReplaceWithRemovedLines() { this.AssertFileChange( - "first\r\nsecoXX\r\nREMOVE\r\nTHESE\r\nLINES\r\nXXfth", - "first\r\nsecond\r\nthird\r\nfourth\r\nfifth", + TestUtilities.NormalizeNewlines("first\nsecoXX\nREMOVE\nTHESE\nLINES\nXXfth"), + TestUtilities.NormalizeNewlines("first\nsecond\nthird\nfourth\nfifth"), new FileChange { Line = 2, EndLine = 6, Offset = 5, EndOffset = 3, - InsertString = "nd\r\nthird\r\nfourth\r\nfi" + InsertString = TestUtilities.NormalizeNewlines("nd\nthird\nfourth\nfi") }); } @@ -115,8 +121,8 @@ public void CanApplyMultiLineReplaceWithRemovedLines() public void CanApplyMultiLineDelete() { this.AssertFileChange( - "first\r\nsecond\r\nREMOVE\r\nTHESE\r\nLINES\r\nthird", - "first\r\nsecond\r\nthird", + TestUtilities.NormalizeNewlines("first\nsecond\nREMOVE\nTHESE\nLINES\nthird"), + TestUtilities.NormalizeNewlines("first\nsecond\nthird"), new FileChange { Line = 3, @@ -131,15 +137,15 @@ public void CanApplyMultiLineDelete() public void CanApplyEditsToEndOfFile() { this.AssertFileChange( - "line1\r\nline2\r\nline3\r\n\r\n", - "line1\r\nline2\r\nline3\r\n\r\n\r\n\r\n", + TestUtilities.NormalizeNewlines("line1\nline2\nline3\n\n"), + TestUtilities.NormalizeNewlines("line1\nline2\nline3\n\n\n\n"), new FileChange { Line = 5, EndLine = 5, Offset = 1, EndOffset = 1, - InsertString = "\r\n\r\n" + InsertString = Environment.NewLine + Environment.NewLine }); } @@ -147,15 +153,15 @@ public void CanApplyEditsToEndOfFile() public void CanAppendToEndOfFile() { this.AssertFileChange( - "line1\r\nline2\r\nline3", - "line1\r\nline2\r\nline3\r\nline4\r\nline5", + TestUtilities.NormalizeNewlines("line1\nline2\nline3"), + TestUtilities.NormalizeNewlines("line1\nline2\nline3\nline4\nline5"), new FileChange { Line = 4, EndLine = 5, Offset = 1, EndOffset = 1, - InsertString = "line4\r\nline5" + InsertString = $"line4{Environment.NewLine}line5" } ); } @@ -163,12 +169,12 @@ public void CanAppendToEndOfFile() [Fact] public void FindsDotSourcedFiles() { - string exampleScriptContents = - @". .\athing.ps1" + "\r\n" + - @". .\somefile.ps1" + "\r\n" + - @". .\somefile.ps1" + "\r\n" + - @"Do-Stuff $uri" + "\r\n" + - @". simpleps.ps1"; + string exampleScriptContents = TestUtilities.PlatformNormalize( + ". ./athing.ps1\n"+ + ". ./somefile.ps1\n"+ + ". ./somefile.ps1\n"+ + "Do-Stuff $uri\n"+ + ". simpleps.ps1"); using (StringReader stringReader = new StringReader(exampleScriptContents)) { @@ -181,7 +187,7 @@ public void FindsDotSourcedFiles() Assert.Equal(3, scriptFile.ReferencedFiles.Length); System.Console.Write("a" + scriptFile.ReferencedFiles[0]); - Assert.Equal(@".\athing.ps1", scriptFile.ReferencedFiles[0]); + Assert.Equal(TestUtilities.NormalizePath("./athing.ps1"), scriptFile.ReferencedFiles[0]); } } @@ -193,8 +199,8 @@ public void ThrowsExceptionWithEditOutsideOfRange() () => { this.AssertFileChange( - "first\r\nsecond\r\nREMOVE\r\nTHESE\r\nLINES\r\nthird", - "first\r\nsecond\r\nthird", + TestUtilities.NormalizeNewlines("first\nsecond\nREMOVE\nTHESE\nLINES\nthird"), + TestUtilities.NormalizeNewlines("first\nsecond\nthird"), new FileChange { Line = 3, @@ -210,8 +216,8 @@ public void ThrowsExceptionWithEditOutsideOfRange() public void CanDeleteFromEndOfFile() { this.AssertFileChange( - "line1\r\nline2\r\nline3\r\nline4", - "line1\r\nline2", + TestUtilities.NormalizeNewlines("line1\nline2\nline3\nline4"), + TestUtilities.NormalizeNewlines("line1\nline2"), new FileChange { Line = 3, @@ -255,11 +261,13 @@ private void AssertFileChange( public class ScriptFileGetLinesTests { - private const string TestString_NoTrailingNewline = "Line One\r\nLine Two\r\nLine Three\r\nLine Four\r\nLine Five"; + private static readonly string TestString_NoTrailingNewline = TestUtilities.NormalizeNewlines( + "Line One\nLine Two\nLine Three\nLine Four\nLine Five"); - private const string TestString_TrailingNewline = TestString_NoTrailingNewline + "\r\n"; + private static readonly string TestString_TrailingNewline = TestUtilities.NormalizeNewlines( + TestString_NoTrailingNewline + "\n"); - private static readonly string[] s_newLines = new string[] { "\r\n" }; + private static readonly string[] s_newLines = new string[] { Environment.NewLine }; private static readonly string[] s_testStringLines_noTrailingNewline = TestString_NoTrailingNewline.Split(s_newLines, StringSplitOptions.None); @@ -269,7 +277,6 @@ public class ScriptFileGetLinesTests private ScriptFile _scriptFile_noTrailingNewline; - public ScriptFileGetLinesTests() { _scriptFile_noTrailingNewline = ScriptFileChangeTests.CreateScriptFile( diff --git a/test/PowerShellEditorServices.Test/Session/WorkspaceTests.cs b/test/PowerShellEditorServices.Test/Session/WorkspaceTests.cs index b93d796d0..cb9203ca1 100644 --- a/test/PowerShellEditorServices.Test/Session/WorkspaceTests.cs +++ b/test/PowerShellEditorServices.Test/Session/WorkspaceTests.cs @@ -4,7 +4,10 @@ // using System; +using System.Collections.Generic; using System.IO; +using System.Runtime.InteropServices; +using Microsoft.PowerShell.EditorServices.Test.Shared; using Microsoft.PowerShell.EditorServices.Utility; using Xunit; @@ -17,31 +20,34 @@ public class WorkspaceTests [Fact] public void CanResolveWorkspaceRelativePath() { - string workspacePath = @"c:\Test\Workspace\"; - string testPathInside = @"c:\Test\Workspace\SubFolder\FilePath.ps1"; - string testPathOutside = @"c:\Test\PeerPath\FilePath.ps1"; - string testPathAnotherDrive = @"z:\TryAndFindMe\FilePath.ps1"; + string workspacePath = TestUtilities.NormalizePath("c:/Test/Workspace/"); + string testPathInside = TestUtilities.NormalizePath("c:/Test/Workspace/SubFolder/FilePath.ps1"); + string testPathOutside = TestUtilities.NormalizePath("c:/Test/PeerPath/FilePath.ps1"); + string testPathAnotherDrive = TestUtilities.NormalizePath("z:/TryAndFindMe/FilePath.ps1"); Workspace workspace = new Workspace(PowerShellVersion, Logging.NullLogger); // Test without a workspace path Assert.Equal(testPathOutside, workspace.GetRelativePath(testPathOutside)); + string expectedInsidePath = TestUtilities.NormalizePath("SubFolder/FilePath.ps1"); + string expectedOutsidePath = TestUtilities.NormalizePath("../PeerPath/FilePath.ps1"); + // Test with a workspace path workspace.WorkspacePath = workspacePath; - Assert.Equal(@"SubFolder\FilePath.ps1", workspace.GetRelativePath(testPathInside)); - Assert.Equal(@"..\PeerPath\FilePath.ps1", workspace.GetRelativePath(testPathOutside)); + Assert.Equal(expectedInsidePath, workspace.GetRelativePath(testPathInside)); + Assert.Equal(expectedOutsidePath, workspace.GetRelativePath(testPathOutside)); Assert.Equal(testPathAnotherDrive, workspace.GetRelativePath(testPathAnotherDrive)); } [Fact] public void CanDetermineIsPathInMemory() { - var tempDir = Environment.GetEnvironmentVariable("TEMP"); - var shortDirPath = Path.Combine(tempDir, "GitHub", "PowerShellEditorServices"); - var shortFilePath = Path.Combine(shortDirPath, "foo.ps1"); - var shortUriForm = "git:/c%3A/Users/Keith/GitHub/dahlbyk/posh-git/src/PoshGitTypes.ps1?%7B%22path%22%3A%22c%3A%5C%5CUsers%5C%5CKeith%5C%5CGitHub%5C%5Cdahlbyk%5C%5Cposh-git%5C%5Csrc%5C%5CPoshGitTypes.ps1%22%2C%22ref%22%3A%22~%22%7D"; - var longUriForm = "gitlens-git:c%3A%5CUsers%5CKeith%5CGitHub%5Cdahlbyk%5Cposh-git%5Csrc%5CPoshGitTypes%3Ae0022701.ps1?%7B%22fileName%22%3A%22src%2FPoshGitTypes.ps1%22%2C%22repoPath%22%3A%22c%3A%2FUsers%2FKeith%2FGitHub%2Fdahlbyk%2Fposh-git%22%2C%22sha%22%3A%22e0022701fa12e0bc22d0458673d6443c942b974a%22%7D"; + string tempDir = Path.GetTempPath(); + string shortDirPath = Path.Combine(tempDir, "GitHub", "PowerShellEditorServices"); + string shortFilePath = Path.Combine(shortDirPath, "foo.ps1"); + string shortUriForm = "git:/c%3A/Users/Keith/GitHub/dahlbyk/posh-git/src/PoshGitTypes.ps1?%7B%22path%22%3A%22c%3A%5C%5CUsers%5C%5CKeith%5C%5CGitHub%5C%5Cdahlbyk%5C%5Cposh-git%5C%5Csrc%5C%5CPoshGitTypes.ps1%22%2C%22ref%22%3A%22~%22%7D"; + string longUriForm = "gitlens-git:c%3A%5CUsers%5CKeith%5CGitHub%5Cdahlbyk%5Cposh-git%5Csrc%5CPoshGitTypes%3Ae0022701.ps1?%7B%22fileName%22%3A%22src%2FPoshGitTypes.ps1%22%2C%22repoPath%22%3A%22c%3A%2FUsers%2FKeith%2FGitHub%2Fdahlbyk%2Fposh-git%22%2C%22sha%22%3A%22e0022701fa12e0bc22d0458673d6443c942b974a%22%7D"; var testCases = new[] { // Test short file absolute paths @@ -52,7 +58,7 @@ public void CanDetermineIsPathInMemory() // Test short file relative paths - not sure we'll ever get these but just in case new { IsInMemory = false, Path = "foo.ps1" }, - new { IsInMemory = false, Path = ".." + Path.DirectorySeparatorChar + "foo.ps1" }, + new { IsInMemory = false, Path = Path.Combine(new [] { "..", "foo.ps1" }) }, // Test short non-file paths new { IsInMemory = true, Path = "untitled:untitled-1" }, @@ -72,15 +78,7 @@ public void CanDetermineIsPathInMemory() } [Theory()] - [InlineData("file:///C%3A/banana/", @"C:\banana\")] - [InlineData("file:///C%3A/banana/ex.ps1", @"C:\banana\ex.ps1")] - [InlineData("file:///E%3A/Path/to/awful%23path", @"E:\Path\to\awful#path")] - [InlineData("file:///path/with/no/drive", @"C:\path\with\no\drive")] - [InlineData("file:///path/wi[th]/squ[are/brackets/", @"C:\path\wi[th]\squ[are\brackets\")] - [InlineData("file:///Carrots/A%5Ere/Good/", @"C:\Carrots\A^re\Good\")] - [InlineData("file:///Users/barnaby/%E8%84%9A%E6%9C%AC/Reduce-Directory", @"C:\Users\barnaby\脚本\Reduce-Directory")] - [InlineData("file:///C%3A/Program%20Files%20%28x86%29/PowerShell/6/pwsh.exe", @"C:\Program Files (x86)\PowerShell\6\pwsh.exe")] - [InlineData("file:///home/maxim/test%20folder/%D0%9F%D0%B0%D0%BF%D0%BA%D0%B0/helloworld.ps1", @"C:\home\maxim\test folder\Папка\helloworld.ps1")] + [MemberData(nameof(PathsToResolve), parameters: 2)] public void CorrectlyResolvesPaths(string givenPath, string expectedPath) { Workspace workspace = new Workspace(PowerShellVersion, Logging.NullLogger); @@ -89,5 +87,41 @@ public void CorrectlyResolvesPaths(string givenPath, string expectedPath) Assert.Equal(expectedPath, resolvedPath); } + + public static IEnumerable PathsToResolve + { + get + { + return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? s_winPathsToResolve + : s_unixPathsToResolve; + } + } + + private static object[][] s_winPathsToResolve = new object[][] + { + new object[] { "file:///C%3A/banana/", @"C:\banana\" }, + new object[] { "file:///C%3A/banana/ex.ps1", @"C:\banana\ex.ps1" }, + new object[] { "file:///E%3A/Path/to/awful%23path", @"E:\Path\to\awful#path" }, + new object[] { "file:///path/with/no/drive", @"C:\path\with\no\drive" }, + new object[] { "file:///path/wi[th]/squ[are/brackets/", @"C:\path\wi[th]\squ[are\brackets\" }, + new object[] { "file:///Carrots/A%5Ere/Good/", @"C:\Carrots\A^re\Good\" }, + new object[] { "file:///Users/barnaby/%E8%84%9A%E6%9C%AC/Reduce-Directory", @"C:\Users\barnaby\脚本\Reduce-Directory" }, + new object[] { "file:///C%3A/Program%20Files%20%28x86%29/PowerShell/6/pwsh.exe", @"C:\Program Files (x86)\PowerShell\6\pwsh.exe" }, + new object[] { "file:///home/maxim/test%20folder/%D0%9F%D0%B0%D0%BF%D0%BA%D0%B0/helloworld.ps1", @"C:\home\maxim\test folder\Папка\helloworld.ps1" } + }; + + private static object[][] s_unixPathsToResolve = new object[][] + { + new object[] { "file:///banana/", @"/banana/" }, + new object[] { "file:///banana/ex.ps1", @"/banana/ex.ps1" }, + new object[] { "file:///Path/to/awful%23path", @"/Path/to/awful#path" }, + new object[] { "file:///path/with/no/drive", @"/path/with/no/drive" }, + new object[] { "file:///path/wi[th]/squ[are/brackets/", @"/path/wi[th]/squ[are/brackets/" }, + new object[] { "file:///Carrots/A%5Ere/Good/", @"/Carrots/A^re/Good/" }, + new object[] { "file:///Users/barnaby/%E8%84%9A%E6%9C%AC/Reduce-Directory", @"/Users/barnaby/脚本/Reduce-Directory" }, + new object[] { "file:///Program%20Files%20%28x86%29/PowerShell/6/pwsh.exe", @"/Program Files (x86)/PowerShell/6/pwsh.exe" }, + new object[] { "file:///home/maxim/test%20folder/%D0%9F%D0%B0%D0%BF%D0%BA%D0%B0/helloworld.ps1", @"/home/maxim/test folder/Папка/helloworld.ps1" } + }; } } diff --git a/test/PowerShellEditorServices.Test/Utility/AsyncDebouncerTests.cs b/test/PowerShellEditorServices.Test/Utility/AsyncDebouncerTests.cs index c87d2e43b..443dc1ba1 100644 --- a/test/PowerShellEditorServices.Test/Utility/AsyncDebouncerTests.cs +++ b/test/PowerShellEditorServices.Test/Utility/AsyncDebouncerTests.cs @@ -14,7 +14,7 @@ namespace Microsoft.PowerShell.EditorServices.Test.Utility { public class AsyncDebouncerTests { - [Fact(Skip = "TODO: This test fails in the new build system, need to investigate!")] + [Fact(Skip = "AsyncDebouncer not flushing within the interval")] public async Task AsyncDebouncerFlushesAfterInterval() { TestAsyncDebouncer debouncer = new TestAsyncDebouncer(); @@ -31,7 +31,7 @@ public async Task AsyncDebouncerFlushesAfterInterval() Assert.Equal(new List { 1, 2, 3 }, debouncer.FlushedBuffer); Assert.True( - debouncer.TimeToFlush > + debouncer.TimeToFlush > TimeSpan.FromMilliseconds(TestAsyncDebouncer.Interval), "Debouncer flushed before interval lapsed."); @@ -40,7 +40,7 @@ public async Task AsyncDebouncerFlushesAfterInterval() Assert.Equal(new List { 4, 5, 6 }, debouncer.FlushedBuffer); } - [Fact(Skip = "TODO: This test fails in the new build system, need to investigate!")] + [Fact] public async Task AsyncDebouncerRestartsAfterInvoke() { TestAsyncRestartDebouncer debouncer = new TestAsyncRestartDebouncer(); diff --git a/test/PowerShellEditorServices.Test/Utility/LoggerTests.cs b/test/PowerShellEditorServices.Test/Utility/LoggerTests.cs index cdb2e2237..ca0558a54 100644 --- a/test/PowerShellEditorServices.Test/Utility/LoggerTests.cs +++ b/test/PowerShellEditorServices.Test/Utility/LoggerTests.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Microsoft.PowerShell.EditorServices.Test.Shared; using Microsoft.PowerShell.EditorServices.Utility; using System; using System.IO; @@ -128,7 +129,7 @@ private string ReadLogContents() { return string.Join( - "\r\n", + Environment.NewLine, File.ReadAllLines( logFilePath, Encoding.UTF8)); From 575ae89938e795a8ecc96be8324dfdde55329516 Mon Sep 17 00:00:00 2001 From: Robert Holt Date: Fri, 12 Oct 2018 11:53:11 -0700 Subject: [PATCH 3/5] Set Runspaces to use STA when running in Windows PowerShell (#769) Update PSStandard dependency (#771) --- .../PowerShellEditorServices.csproj | 2 +- .../Session/InvocationEventQueue.cs | 5 ---- .../Session/PowerShellContext.cs | 25 +++++++++++++++++++ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/PowerShellEditorServices/PowerShellEditorServices.csproj b/src/PowerShellEditorServices/PowerShellEditorServices.csproj index 787f5315a..7de370b8c 100644 --- a/src/PowerShellEditorServices/PowerShellEditorServices.csproj +++ b/src/PowerShellEditorServices/PowerShellEditorServices.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/PowerShellEditorServices/Session/InvocationEventQueue.cs b/src/PowerShellEditorServices/Session/InvocationEventQueue.cs index 89184e930..77d85bf23 100644 --- a/src/PowerShellEditorServices/Session/InvocationEventQueue.cs +++ b/src/PowerShellEditorServices/Session/InvocationEventQueue.cs @@ -225,11 +225,6 @@ private void OnInvokerUnsubscribed(object sender, PSEventUnsubscribedEventArgs e CreateInvocationSubscriber(); } - private void OnInvokerUnsubscribed(object sender, PSEventArgs e) - { - CreateInvocationSubscriber(); - } - private void SetSubscriberExecutionThreadWithReflection(PSEventSubscriber subscriber) { // We need to create the PowerShell object in the same thread so we can get a nested diff --git a/src/PowerShellEditorServices/Session/PowerShellContext.cs b/src/PowerShellEditorServices/Session/PowerShellContext.cs index cf21554b0..698d742fc 100644 --- a/src/PowerShellEditorServices/Session/PowerShellContext.cs +++ b/src/PowerShellEditorServices/Session/PowerShellContext.cs @@ -8,10 +8,12 @@ using System.Collections.ObjectModel; using System.Globalization; using System.IO; +using System.Runtime.InteropServices; using System.Linq; using System.Management.Automation.Host; using System.Management.Automation.Remoting; using System.Management.Automation.Runspaces; +using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading; @@ -32,6 +34,21 @@ namespace Microsoft.PowerShell.EditorServices /// public class PowerShellContext : IDisposable, IHostSupportsInteractiveSession { + private const string DotNetFrameworkDescription = ".NET Framework"; + + private static readonly Action s_runspaceApartmentStateSetter; + + static PowerShellContext() + { + // PowerShell ApartmentState APIs aren't available in PSStandard, so we need to use reflection + if (RuntimeInformation.FrameworkDescription.Equals(DotNetFrameworkDescription)) + { + MethodInfo setterInfo = typeof(Runspace).GetProperty("ApartmentState").GetSetMethod(); + Delegate setter = Delegate.CreateDelegate(typeof(Action), firstArgument: null, method: setterInfo); + s_runspaceApartmentStateSetter = (Action)setter; + } + } + #region Fields private readonly SemaphoreSlim resumeRequestHandle = AsyncUtils.CreateSimpleLockingSemaphore(); @@ -175,6 +192,14 @@ public static Runspace CreateRunspace(PSHost psHost) } Runspace runspace = RunspaceFactory.CreateRunspace(psHost, initialSessionState); + + // Windows PowerShell must be hosted in STA mode + // This must be set on the runspace *before* it is opened + if (RuntimeInformation.FrameworkDescription.Equals(DotNetFrameworkDescription)) + { + s_runspaceApartmentStateSetter(runspace, ApartmentState.STA); + } + runspace.ThreadOptions = PSThreadOptions.ReuseThread; runspace.Open(); From 00f28050a5242785d35d3953433b48e4dbd20bb6 Mon Sep 17 00:00:00 2001 From: corbob <30301021+corbob@users.noreply.github.com> Date: Wed, 24 Oct 2018 09:15:37 -0700 Subject: [PATCH 4/5] Removed ShowOnlineHelp Message (#775) --- .../LanguageServer/ShowHelpRequest.cs | 8 -------- .../Server/LanguageServer.cs | 14 -------------- 2 files changed, 22 deletions(-) diff --git a/src/PowerShellEditorServices.Protocol/LanguageServer/ShowHelpRequest.cs b/src/PowerShellEditorServices.Protocol/LanguageServer/ShowHelpRequest.cs index ae2922ebd..0d73074d2 100644 --- a/src/PowerShellEditorServices.Protocol/LanguageServer/ShowHelpRequest.cs +++ b/src/PowerShellEditorServices.Protocol/LanguageServer/ShowHelpRequest.cs @@ -9,14 +9,6 @@ namespace Microsoft.PowerShell.EditorServices.Protocol.LanguageServer { - [Obsolete("This class is deprecated. Use ShowHelpRequest instead.")] - public class ShowOnlineHelpRequest - { - public static readonly - RequestType Type = - RequestType.Create("powerShell/showOnlineHelp"); - } - public class ShowHelpRequest { public static readonly diff --git a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs index 33385e959..fe30cd1b2 100644 --- a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs +++ b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs @@ -136,7 +136,6 @@ public void Start() this.HandleDocumentRangeFormattingRequest); this.messageHandlers.SetRequestHandler(FoldingRangeRequest.Type, this.HandleFoldingRangeRequestAsync); - this.messageHandlers.SetRequestHandler(ShowOnlineHelpRequest.Type, this.HandleShowOnlineHelpRequest); this.messageHandlers.SetRequestHandler(ShowHelpRequest.Type, this.HandleShowHelpRequest); this.messageHandlers.SetRequestHandler(ExpandAliasRequest.Type, this.HandleExpandAliasRequest); @@ -297,19 +296,6 @@ protected async Task HandleShowHelpRequest( await requestContext.SendResult(null); } - protected async Task HandleShowOnlineHelpRequest( - string helpParams, - RequestContext requestContext - ) - { - PSCommand commandDeprecated = new PSCommand() - .AddCommand("Microsoft.PowerShell.Utility\\Write-Verbose") - .AddParameter("Message", "'powerShell/showOnlineHelp' has been deprecated. Use 'powerShell/showHelp' instead."); - - await editorSession.PowerShellContext.ExecuteCommand(commandDeprecated, sendOutputToHost: true); - await this.HandleShowHelpRequest(helpParams, requestContext); - } - private async Task HandleSetPSSARulesRequest( object param, RequestContext requestContext) From 91f9b1ad0661b7e6982c8872a443e19e55202c89 Mon Sep 17 00:00:00 2001 From: Dominic Date: Tue, 13 Nov 2018 13:41:34 -0500 Subject: [PATCH 5/5] Add Async suffix to async methods (#792) [Ignore] no need for package sources (#803) [Ignore] add null check for version --- NuGet.Config | 8 +- PowerShellEditorServices.build.ps1 | 2 +- .../WebsocketClientChannel.cs | 12 +- .../WebsocketServerChannel.cs | 28 +- .../CodeLens/CodeLensFeature.cs | 14 +- .../CodeLens/ReferencesCodeLensProvider.cs | 2 +- .../EditorServicesHost.cs | 8 +- .../PSHost/PromptHandlers.cs | 10 +- .../PSHost/ProtocolPSHostUserInterface.cs | 6 +- .../Symbols/DocumentSymbolFeature.cs | 6 +- .../Client/DebugAdapterClientBase.cs | 22 +- .../Client/LanguageClientBase.cs | 26 +- .../Client/LanguageServiceClient.cs | 8 +- .../Channel/NamedPipeClientChannel.cs | 2 +- .../MessageProtocol/EventContext.cs | 4 +- .../MessageProtocol/IMessageSender.cs | 6 +- .../MessageProtocol/MessageDispatcher.cs | 2 +- .../MessageProtocol/MessageReader.cs | 6 +- .../MessageProtocol/MessageWriter.cs | 14 +- .../MessageProtocol/ProtocolEndpoint.cs | 26 +- .../MessageProtocol/RequestContext.cs | 12 +- .../Server/DebugAdapter.cs | 248 ++++++------ .../Server/IMessageDispatcher.cs | 2 +- .../Server/LanguageServer.cs | 368 ++++++++++-------- .../Server/LanguageServerEditorOperations.cs | 52 +-- .../Server/OutputDebouncer.cs | 8 +- .../CustomViews/CustomViewBase.cs | 8 +- .../CustomViews/HtmlContentView.cs | 12 +- .../CustomViews/HtmlContentViewsFeature.cs | 4 +- .../CustomViews/IHtmlContentView.cs | 6 +- .../CustomViews/IHtmlContentViews.cs | 2 +- .../Analysis/AnalysisService.cs | 2 +- .../Console/ChoicePromptHandler.cs | 20 +- .../Console/ConsoleReadLine.cs | 12 +- .../Console/InputPromptHandler.cs | 22 +- .../Console/TerminalChoicePromptHandler.cs | 4 +- .../Console/TerminalInputPromptHandler.cs | 8 +- .../Debugging/DebugService.cs | 70 ++-- .../Extensions/EditorContext.cs | 2 +- .../Extensions/EditorObject.cs | 2 +- .../Extensions/EditorWindow.cs | 14 +- .../Extensions/EditorWorkspace.cs | 6 +- .../Extensions/ExtensionService.cs | 8 +- .../Extensions/FileContext.cs | 6 +- .../Extensions/IEditorOperations.cs | 26 +- .../Language/AstOperations.cs | 6 +- .../Language/CommandHelpers.cs | 8 +- .../Language/LanguageService.cs | 24 +- .../Language/SymbolDetails.cs | 8 +- .../Capabilities/DscBreakpointCapability.cs | 4 +- .../Host/EditorServicesPSHostUserInterface.cs | 30 +- .../Host/TerminalPSHostUserInterface.cs | 4 +- .../Session/InvocationEventQueue.cs | 10 +- .../Session/PSReadLinePromptContext.cs | 4 +- .../Session/PipelineExecutionRequest.cs | 6 +- .../Session/PowerShellContext.cs | 106 ++--- .../Session/RemoteFileManager.cs | 28 +- .../Session/ThreadController.cs | 4 +- .../Templates/TemplateService.cs | 14 +- .../Utility/AsyncDebouncer.cs | 18 +- .../DebugAdapterTests.cs | 6 +- .../LanguageServerTests.cs | 10 +- .../ServerTestsBase.cs | 4 +- .../Message/MessageReaderWriterTests.cs | 8 +- .../Server/OutputDebouncerTests.cs | 8 +- .../Console/ChoicePromptHandlerTests.cs | 8 +- .../Console/InputPromptHandlerTests.cs | 10 +- .../Debugging/DebugServiceTests.cs | 96 ++--- .../Extensions/ExtensionServiceTests.cs | 52 +-- .../Language/LanguageServiceTests.cs | 10 +- .../PowerShellContextFactory.cs | 2 +- .../Session/PowerShellContextTests.cs | 18 +- .../Utility/AsyncDebouncerTests.cs | 28 +- .../Utility/AsyncQueueTests.cs | 12 +- 74 files changed, 850 insertions(+), 832 deletions(-) diff --git a/NuGet.Config b/NuGet.Config index 45f5379b9..6efc7f7b9 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -3,10 +3,4 @@ - - - - - - - \ No newline at end of file + diff --git a/PowerShellEditorServices.build.ps1 b/PowerShellEditorServices.build.ps1 index 0b36d5176..8f543c3a4 100644 --- a/PowerShellEditorServices.build.ps1 +++ b/PowerShellEditorServices.build.ps1 @@ -198,7 +198,7 @@ task SetupDotNet -Before Clean, Build, TestHost, TestServer, TestProtocol, Packa # dotnet --version can return a semver that System.Version can't handle # e.g.: 2.1.300-preview-01. The replace operator is used to remove any build suffix. $version = (& $dotnetExePath --version) -replace '[+-].*$','' - if ([version]$version -ge [version]$script:RequiredSdkVersion) { + if ($version -and [version]$version -ge [version]$script:RequiredSdkVersion) { $script:dotnetExe = $dotnetExePath } else { diff --git a/src/PowerShellEditorServices.Channel.WebSocket/WebsocketClientChannel.cs b/src/PowerShellEditorServices.Channel.WebSocket/WebsocketClientChannel.cs index ea1142f76..edf2fd52f 100644 --- a/src/PowerShellEditorServices.Channel.WebSocket/WebsocketClientChannel.cs +++ b/src/PowerShellEditorServices.Channel.WebSocket/WebsocketClientChannel.cs @@ -39,7 +39,7 @@ public WebsocketClientChannel(string url) this.serverUrl = url; } - public override async Task WaitForConnection() + public override async Task WaitForConnectionAsync() { try { @@ -52,7 +52,7 @@ public override async Task WaitForConnection() { Logger.Write(LogLevel.Warning, string.Format("Failed to connect to WebSocket server. Error was '{0}'", wsException.Message)); - + } throw; @@ -99,7 +99,7 @@ protected override void Shutdown() } /// - /// Extension of that sends data to a WebSocket during FlushAsync + /// Extension of that sends data to a WebSocket during FlushAsync /// and reads during WriteAsync. /// internal class ClientWebSocketStream : MemoryStream @@ -110,7 +110,7 @@ internal class ClientWebSocketStream : MemoryStream /// Constructor /// /// - /// It is expected that the socket is in an Open state. + /// It is expected that the socket is in an Open state. /// /// public ClientWebSocketStream(ClientWebSocket socket) @@ -119,7 +119,7 @@ public ClientWebSocketStream(ClientWebSocket socket) } /// - /// Reads from the WebSocket. + /// Reads from the WebSocket. /// /// /// @@ -138,7 +138,7 @@ public override async Task ReadAsync(byte[] buffer, int offset, int count, { result = await socket.ReceiveAsync(new ArraySegment(buffer, offset, count), cancellationToken); } while (!result.EndOfMessage); - + if (result.MessageType == WebSocketMessageType.Close) { await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", cancellationToken); diff --git a/src/PowerShellEditorServices.Channel.WebSocket/WebsocketServerChannel.cs b/src/PowerShellEditorServices.Channel.WebSocket/WebsocketServerChannel.cs index 07acf953a..c3cb907de 100644 --- a/src/PowerShellEditorServices.Channel.WebSocket/WebsocketServerChannel.cs +++ b/src/PowerShellEditorServices.Channel.WebSocket/WebsocketServerChannel.cs @@ -17,8 +17,8 @@ namespace Microsoft.PowerShell.EditorServices.Channel.WebSocket { /// - /// Implementation of that implements the streams necessary for - /// communicating via OWIN WebSockets. + /// Implementation of that implements the streams necessary for + /// communicating via OWIN WebSockets. /// public class WebSocketServerChannel : ChannelBase { @@ -42,22 +42,22 @@ protected override void Initialize(IMessageSerializer messageSerializer) this.MessageWriter = new MessageWriter( - new WebSocketStream(socketConnection), + new WebSocketStream(socketConnection), messageSerializer); } /// - /// Dispatches data received during calls to OnMessageReceived in the class. + /// Dispatches data received during calls to OnMessageReceivedAsync in the class. /// /// - /// This method calls an overriden version of the that dispatches messages on - /// demand rather than running on a background thread. + /// This method calls an overriden version of the that dispatches messages on + /// demand rather than running on a background thread. /// /// /// - public async Task Dispatch(ArraySegment message) + public async Task DispatchAsync(ArraySegment message) { - //Clear our stream + //Clear our stream inStream.SetLength(0); //Write data and dispatch to handlers @@ -70,7 +70,7 @@ protected override void Shutdown() this.socketConnection.Close(WebSocketCloseStatus.NormalClosure, "Server shutting down"); } - public override Task WaitForConnection() + public override Task WaitForConnectionAsync() { // TODO: Need to update behavior here return Task.FromResult(true); @@ -78,11 +78,11 @@ public override Task WaitForConnection() } /// - /// Overriden that sends data through a during the FlushAsync call. + /// Overriden that sends data through a during the FlushAsync call. /// /// /// FlushAsync will send data via the SendBinary method of the class. The memory streams length will - /// then be set to 0 to reset the stream for additional data to be written. + /// then be set to 0 to reset the stream for additional data to be written. /// internal class WebSocketStream : MemoryStream { @@ -106,7 +106,7 @@ public override async Task FlushAsync(CancellationToken cancellationToken) /// public abstract class EditorServiceWebSocketConnection : WebSocketConnection { - protected EditorServiceWebSocketConnection() + protected EditorServiceWebSocketConnection() { Channel = new WebSocketServerChannel(this); } @@ -120,9 +120,9 @@ public override void OnOpen() Server.Start(); } - public override async Task OnMessageReceived(ArraySegment message, WebSocketMessageType type) + public override async Task OnMessageReceivedAsync(ArraySegment message, WebSocketMessageType type) { - await Channel.Dispatch(message); + await Channel.DispatchAsync(message); } public override Task OnCloseAsync(WebSocketCloseStatus? closeStatus, string closeStatusDescription) diff --git a/src/PowerShellEditorServices.Host/CodeLens/CodeLensFeature.cs b/src/PowerShellEditorServices.Host/CodeLens/CodeLensFeature.cs index ac5f388a3..e7e2f6910 100644 --- a/src/PowerShellEditorServices.Host/CodeLens/CodeLensFeature.cs +++ b/src/PowerShellEditorServices.Host/CodeLens/CodeLensFeature.cs @@ -49,11 +49,11 @@ public static CodeLensFeature Create( messageHandlers.SetRequestHandler( CodeLensRequest.Type, - codeLenses.HandleCodeLensRequest); + codeLenses.HandleCodeLensRequestAsync); messageHandlers.SetRequestHandler( CodeLensResolveRequest.Type, - codeLenses.HandleCodeLensResolveRequest); + codeLenses.HandleCodeLensResolveRequestAsync); codeLenses.Providers.Add( new ReferencesCodeLensProvider( @@ -111,7 +111,7 @@ public CodeLens[] ProvideCodeLenses(ScriptFile scriptFile) /// /// Parameters on the CodeLens request that was received. /// - private async Task HandleCodeLensRequest( + private async Task HandleCodeLensRequestAsync( CodeLensRequest codeLensParams, RequestContext requestContext) { @@ -132,7 +132,7 @@ private async Task HandleCodeLensRequest( _jsonSerializer); } - await requestContext.SendResult(codeLensResponse); + await requestContext.SendResultAsync(codeLensResponse); } /// @@ -140,7 +140,7 @@ private async Task HandleCodeLensRequest( /// /// The CodeLens to be resolved/updated. /// - private async Task HandleCodeLensResolveRequest( + private async Task HandleCodeLensResolveRequestAsync( LanguageServer.CodeLens codeLens, RequestContext requestContext) { @@ -178,13 +178,13 @@ await originalProvider.ResolveCodeLensAsync( originalCodeLens, CancellationToken.None); - await requestContext.SendResult( + await requestContext.SendResultAsync( resolvedCodeLens.ToProtocolCodeLens( _jsonSerializer)); } else { - await requestContext.SendError( + await requestContext.SendErrorAsync( $"Could not find provider for the original CodeLens: {codeLensData.ProviderId}"); } } diff --git a/src/PowerShellEditorServices.Host/CodeLens/ReferencesCodeLensProvider.cs b/src/PowerShellEditorServices.Host/CodeLens/ReferencesCodeLensProvider.cs index c833aec27..6a5312a93 100644 --- a/src/PowerShellEditorServices.Host/CodeLens/ReferencesCodeLensProvider.cs +++ b/src/PowerShellEditorServices.Host/CodeLens/ReferencesCodeLensProvider.cs @@ -82,7 +82,7 @@ public async Task ResolveCodeLensAsync( codeLens.ScriptExtent.StartLineNumber, codeLens.ScriptExtent.StartColumnNumber); - FindReferencesResult referencesResult = await _editorSession.LanguageService.FindReferencesOfSymbol( + FindReferencesResult referencesResult = await _editorSession.LanguageService.FindReferencesOfSymbolAsync( foundSymbol, references, _editorSession.Workspace); diff --git a/src/PowerShellEditorServices.Host/EditorServicesHost.cs b/src/PowerShellEditorServices.Host/EditorServicesHost.cs index 51e4a2db3..2f3275bf3 100644 --- a/src/PowerShellEditorServices.Host/EditorServicesHost.cs +++ b/src/PowerShellEditorServices.Host/EditorServicesHost.cs @@ -191,7 +191,7 @@ public void StartLanguageService( this.languageServiceListener = CreateServiceListener(MessageProtocolType.LanguageServer, config); - this.languageServiceListener.ClientConnect += this.OnLanguageServiceClientConnect; + this.languageServiceListener.ClientConnect += this.OnLanguageServiceClientConnectAsync; this.languageServiceListener.Start(); this.logger.Write( @@ -201,7 +201,7 @@ public void StartLanguageService( config.TransportType, config.Endpoint)); } - private async void OnLanguageServiceClientConnect( + private async void OnLanguageServiceClientConnectAsync( object sender, ChannelBase serverChannel) { @@ -231,7 +231,7 @@ private async void OnLanguageServiceClientConnect( this.serverCompletedTask, this.logger); - await this.editorSession.PowerShellContext.ImportCommandsModule( + await this.editorSession.PowerShellContext.ImportCommandsModuleAsync( Path.Combine( Path.GetDirectoryName(this.GetType().GetTypeInfo().Assembly.Location), @"..\Commands")); @@ -247,7 +247,7 @@ await this.editorSession.PowerShellContext.ImportCommandsModule( .AddCommand("Microsoft.PowerShell.Core\\Import-Module") .AddParameter("Name", module); - await this.editorSession.PowerShellContext.ExecuteCommand( + await this.editorSession.PowerShellContext.ExecuteCommandAsync( command, sendOutputToHost: false, sendErrorToHost: true); diff --git a/src/PowerShellEditorServices.Host/PSHost/PromptHandlers.cs b/src/PowerShellEditorServices.Host/PSHost/PromptHandlers.cs index bae68616f..d8ec88c4e 100644 --- a/src/PowerShellEditorServices.Host/PSHost/PromptHandlers.cs +++ b/src/PowerShellEditorServices.Host/PSHost/PromptHandlers.cs @@ -37,7 +37,7 @@ protected override void ShowPrompt(PromptStyle promptStyle) base.ShowPrompt(promptStyle); messageSender - .SendRequest( + .SendRequestAsync( ShowChoicePromptRequest.Type, new ShowChoicePromptRequest { @@ -51,7 +51,7 @@ protected override void ShowPrompt(PromptStyle promptStyle) .ConfigureAwait(false); } - protected override Task ReadInputString(CancellationToken cancellationToken) + protected override Task ReadInputStringAsync(CancellationToken cancellationToken) { this.readLineTask = new TaskCompletionSource(); return this.readLineTask.Task; @@ -120,7 +120,7 @@ protected override void ShowFieldPrompt(FieldDetails fieldDetails) base.ShowFieldPrompt(fieldDetails); messageSender - .SendRequest( + .SendRequestAsync( ShowInputPromptRequest.Type, new ShowInputPromptRequest { @@ -131,7 +131,7 @@ protected override void ShowFieldPrompt(FieldDetails fieldDetails) .ConfigureAwait(false); } - protected override Task ReadInputString(CancellationToken cancellationToken) + protected override Task ReadInputStringAsync(CancellationToken cancellationToken) { this.readLineTask = new TaskCompletionSource(); return this.readLineTask.Task; @@ -176,7 +176,7 @@ private void HandlePromptResponse( this.readLineTask = null; } - protected override Task ReadSecureString(CancellationToken cancellationToken) + protected override Task ReadSecureStringAsync(CancellationToken cancellationToken) { // TODO: Write a message to the console throw new NotImplementedException(); diff --git a/src/PowerShellEditorServices.Host/PSHost/ProtocolPSHostUserInterface.cs b/src/PowerShellEditorServices.Host/PSHost/ProtocolPSHostUserInterface.cs index c9a07e4a6..76b1f7252 100644 --- a/src/PowerShellEditorServices.Host/PSHost/ProtocolPSHostUserInterface.cs +++ b/src/PowerShellEditorServices.Host/PSHost/ProtocolPSHostUserInterface.cs @@ -47,7 +47,7 @@ public void Dispose() // Make sure remaining output is flushed before exiting if (this.outputDebouncer != null) { - this.outputDebouncer.Flush().Wait(); + this.outputDebouncer.FlushAsync().Wait(); this.outputDebouncer = null; } } @@ -82,7 +82,7 @@ public override void WriteOutput( ConsoleColor backgroundColor) { // TODO: This should use a synchronous method! - this.outputDebouncer.Invoke( + this.outputDebouncer.InvokeAsync( new OutputWrittenEventArgs( outputString, includeNewLine, @@ -102,7 +102,7 @@ protected override void UpdateProgress( { } - protected override Task ReadCommandLine(CancellationToken cancellationToken) + protected override Task ReadCommandLineAsync(CancellationToken cancellationToken) { // This currently does nothing because the "evaluate" request // will cancel the current prompt and execute the user's diff --git a/src/PowerShellEditorServices.Host/Symbols/DocumentSymbolFeature.cs b/src/PowerShellEditorServices.Host/Symbols/DocumentSymbolFeature.cs index 3db2a8dc8..da3402bde 100644 --- a/src/PowerShellEditorServices.Host/Symbols/DocumentSymbolFeature.cs +++ b/src/PowerShellEditorServices.Host/Symbols/DocumentSymbolFeature.cs @@ -33,7 +33,7 @@ public DocumentSymbolFeature( messageHandlers.SetRequestHandler( DocumentSymbolRequest.Type, - this.HandleDocumentSymbolRequest); + this.HandleDocumentSymbolRequestAsync); } public static DocumentSymbolFeature Create( @@ -69,7 +69,7 @@ public IEnumerable ProvideDocumentSymbols( .SelectMany(r => r); } - protected async Task HandleDocumentSymbolRequest( + protected async Task HandleDocumentSymbolRequestAsync( DocumentSymbolParams documentSymbolParams, RequestContext requestContext) { @@ -109,7 +109,7 @@ protected async Task HandleDocumentSymbolRequest( symbols = new SymbolInformation[0]; } - await requestContext.SendResult(symbols); + await requestContext.SendResultAsync(symbols); } } } diff --git a/src/PowerShellEditorServices.Protocol/Client/DebugAdapterClientBase.cs b/src/PowerShellEditorServices.Protocol/Client/DebugAdapterClientBase.cs index c90730a6d..b226fd0c4 100644 --- a/src/PowerShellEditorServices.Protocol/Client/DebugAdapterClientBase.cs +++ b/src/PowerShellEditorServices.Protocol/Client/DebugAdapterClientBase.cs @@ -28,12 +28,12 @@ public DebugAdapterClient(ChannelBase clientChannel, ILogger logger) logger); } - public async Task Start() + public async Task StartAsync() { this.protocolEndpoint.Start(); // Initialize the debug adapter - await this.SendRequest( + await this.SendRequestAsync( InitializeRequest.Type, new InitializeRequestArguments { @@ -48,34 +48,34 @@ public void Stop() this.protocolEndpoint.Stop(); } - public async Task LaunchScript(string scriptFilePath) + public async Task LaunchScriptAsync(string scriptFilePath) { - await this.SendRequest( + await this.SendRequestAsync( LaunchRequest.Type, new LaunchRequestArguments { Script = scriptFilePath }, true); - await this.SendRequest( + await this.SendRequestAsync( ConfigurationDoneRequest.Type, null, true); } - public Task SendEvent(NotificationType eventType, TParams eventParams) + public Task SendEventAsync(NotificationType eventType, TParams eventParams) { - return ((IMessageSender)protocolEndpoint).SendEvent(eventType, eventParams); + return ((IMessageSender)protocolEndpoint).SendEventAsync(eventType, eventParams); } - public Task SendRequest(RequestType requestType, TParams requestParams, bool waitForResponse) + public Task SendRequestAsync(RequestType requestType, TParams requestParams, bool waitForResponse) { - return ((IMessageSender)protocolEndpoint).SendRequest(requestType, requestParams, waitForResponse); + return ((IMessageSender)protocolEndpoint).SendRequestAsync(requestType, requestParams, waitForResponse); } - public Task SendRequest(RequestType0 requestType0) + public Task SendRequestAsync(RequestType0 requestType0) { - return ((IMessageSender)protocolEndpoint).SendRequest(requestType0); + return ((IMessageSender)protocolEndpoint).SendRequestAsync(requestType0); } public void SetRequestHandler(RequestType requestType, Func, Task> requestHandler) diff --git a/src/PowerShellEditorServices.Protocol/Client/LanguageClientBase.cs b/src/PowerShellEditorServices.Protocol/Client/LanguageClientBase.cs index c8dc383dc..0ccb3d5b5 100644 --- a/src/PowerShellEditorServices.Protocol/Client/LanguageClientBase.cs +++ b/src/PowerShellEditorServices.Protocol/Client/LanguageClientBase.cs @@ -41,46 +41,46 @@ public Task Start() this.protocolEndpoint.Start(); // Initialize the implementation class - return this.Initialize(); + return this.InitializeAsync(); } - public async Task Stop() + public async Task StopAsync() { - await this.OnStop(); + await this.OnStopAsync(); // First, notify the language server that we're stopping var response = - await this.SendRequest( + await this.SendRequestAsync( ShutdownRequest.Type); - await this.SendEvent(ExitNotification.Type, new object()); + await this.SendEventAsync(ExitNotification.Type, new object()); this.protocolEndpoint.Stop(); } - protected virtual Task OnStop() + protected virtual Task OnStopAsync() { return Task.FromResult(true); } - protected virtual Task Initialize() + protected virtual Task InitializeAsync() { return Task.FromResult(true); } - public Task SendEvent(NotificationType eventType, TParams eventParams) + public Task SendEventAsync(NotificationType eventType, TParams eventParams) { - return ((IMessageSender)protocolEndpoint).SendEvent(eventType, eventParams); + return ((IMessageSender)protocolEndpoint).SendEventAsync(eventType, eventParams); } - public Task SendRequest(RequestType requestType, TParams requestParams, bool waitForResponse) + public Task SendRequestAsync(RequestType requestType, TParams requestParams, bool waitForResponse) { - return ((IMessageSender)protocolEndpoint).SendRequest(requestType, requestParams, waitForResponse); + return ((IMessageSender)protocolEndpoint).SendRequestAsync(requestType, requestParams, waitForResponse); } - public Task SendRequest(RequestType0 requestType0) + public Task SendRequestAsync(RequestType0 requestType0) { - return ((IMessageSender)protocolEndpoint).SendRequest(requestType0); + return ((IMessageSender)protocolEndpoint).SendRequestAsync(requestType0); } public void SetRequestHandler(RequestType requestType, Func, Task> requestHandler) diff --git a/src/PowerShellEditorServices.Protocol/Client/LanguageServiceClient.cs b/src/PowerShellEditorServices.Protocol/Client/LanguageServiceClient.cs index 8878814dd..e2b1491fd 100644 --- a/src/PowerShellEditorServices.Protocol/Client/LanguageServiceClient.cs +++ b/src/PowerShellEditorServices.Protocol/Client/LanguageServiceClient.cs @@ -24,10 +24,10 @@ public LanguageServiceClient(ChannelBase clientChannel, ILogger logger) { } - protected override Task Initialize() + protected override Task InitializeAsync() { // Add handlers for common events - this.SetEventHandler(PublishDiagnosticsNotification.Type, HandlePublishDiagnosticsEvent); + this.SetEventHandler(PublishDiagnosticsNotification.Type, HandlePublishDiagnosticsEventAsync); // Send the 'initialize' request and wait for the response var initializeParams = new InitializeParams @@ -36,7 +36,7 @@ protected override Task Initialize() Capabilities = new ClientCapabilities() }; - return this.SendRequest( + return this.SendRequestAsync( InitializeRequest.Type, initializeParams, true); @@ -58,7 +58,7 @@ protected void OnDiagnosticsReceived(string filePath) #region Private Methods - private Task HandlePublishDiagnosticsEvent( + private Task HandlePublishDiagnosticsEventAsync( PublishDiagnosticsNotification diagnostics, EventContext eventContext) { diff --git a/src/PowerShellEditorServices.Protocol/MessageProtocol/Channel/NamedPipeClientChannel.cs b/src/PowerShellEditorServices.Protocol/MessageProtocol/Channel/NamedPipeClientChannel.cs index 8a40b6a1b..07f2cfe39 100644 --- a/src/PowerShellEditorServices.Protocol/MessageProtocol/Channel/NamedPipeClientChannel.cs +++ b/src/PowerShellEditorServices.Protocol/MessageProtocol/Channel/NamedPipeClientChannel.cs @@ -49,7 +49,7 @@ protected override void Shutdown() } } - public static async Task Connect( + public static async Task ConnectAsync( string pipeFile, MessageProtocolType messageProtocolType, ILogger logger) diff --git a/src/PowerShellEditorServices.Protocol/MessageProtocol/EventContext.cs b/src/PowerShellEditorServices.Protocol/MessageProtocol/EventContext.cs index 7af362f4e..4da757913 100644 --- a/src/PowerShellEditorServices.Protocol/MessageProtocol/EventContext.cs +++ b/src/PowerShellEditorServices.Protocol/MessageProtocol/EventContext.cs @@ -21,11 +21,11 @@ public EventContext(MessageWriter messageWriter) this.messageWriter = messageWriter; } - public async Task SendEvent( + public async Task SendEventAsync( NotificationType eventType, TParams eventParams) { - await this.messageWriter.WriteEvent( + await this.messageWriter.WriteEventAsync( eventType, eventParams); } diff --git a/src/PowerShellEditorServices.Protocol/MessageProtocol/IMessageSender.cs b/src/PowerShellEditorServices.Protocol/MessageProtocol/IMessageSender.cs index e32556cdc..804bbe74c 100644 --- a/src/PowerShellEditorServices.Protocol/MessageProtocol/IMessageSender.cs +++ b/src/PowerShellEditorServices.Protocol/MessageProtocol/IMessageSender.cs @@ -9,16 +9,16 @@ namespace Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol { public interface IMessageSender { - Task SendEvent( + Task SendEventAsync( NotificationType eventType, TParams eventParams); - Task SendRequest( + Task SendRequestAsync( RequestType requestType, TParams requestParams, bool waitForResponse); - Task SendRequest( + Task SendRequestAsync( RequestType0 requestType0); } } diff --git a/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageDispatcher.cs b/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageDispatcher.cs index a81ee4446..ff7ae487e 100644 --- a/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageDispatcher.cs +++ b/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageDispatcher.cs @@ -115,7 +115,7 @@ public void SetEventHandler( #region Private Methods - public async Task DispatchMessage( + public async Task DispatchMessageAsync( Message messageToDispatch, MessageWriter messageWriter) { diff --git a/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageReader.cs b/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageReader.cs index dcf7aaa94..fec0d175b 100644 --- a/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageReader.cs +++ b/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageReader.cs @@ -75,12 +75,12 @@ public MessageReader( #region Public Methods - public async Task ReadMessage() + public async Task ReadMessageAsync() { string messageContent = null; // Do we need to read more data or can we process the existing buffer? - while (!this.needsMoreData || await this.ReadNextChunk()) + while (!this.needsMoreData || await this.ReadNextChunkAsync()) { // Clear the flag since we should have what we need now this.needsMoreData = false; @@ -144,7 +144,7 @@ public async Task ReadMessage() #region Private Methods - private async Task ReadNextChunk() + private async Task ReadNextChunkAsync() { // Do we need to resize the buffer? See if less than 1/4 of the space is left. if (((double)(this.messageBuffer.Length - this.bufferEndOffset) / this.messageBuffer.Length) < 0.25) diff --git a/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageWriter.cs b/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageWriter.cs index f2082efba..5baa40038 100644 --- a/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageWriter.cs +++ b/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageWriter.cs @@ -49,7 +49,7 @@ public MessageWriter( // TODO: This method should be made protected or private - public async Task WriteMessage(Message messageToWrite) + public async Task WriteMessageAsync(Message messageToWrite) { Validate.IsNotNull("messageToWrite", messageToWrite); @@ -111,7 +111,7 @@ public async Task WriteMessage(Message messageToWrite) } } - public async Task WriteRequest( + public async Task WriteRequestAsync( RequestType requestType, TParams requestParams, int requestId) @@ -122,14 +122,14 @@ public async Task WriteRequest( JToken.FromObject(requestParams, contentSerializer) : null; - await this.WriteMessage( + await this.WriteMessageAsync( Message.Request( requestId.ToString(), requestType.Method, contentObject)); } - public async Task WriteResponse(TResult resultContent, string method, string requestId) + public async Task WriteResponseAsync(TResult resultContent, string method, string requestId) { // Allow null content JToken contentObject = @@ -137,14 +137,14 @@ public async Task WriteResponse(TResult resultContent, string method, s JToken.FromObject(resultContent, contentSerializer) : null; - await this.WriteMessage( + await this.WriteMessageAsync( Message.Response( requestId, method, contentObject)); } - public async Task WriteEvent(NotificationType eventType, TParams eventParams) + public async Task WriteEventAsync(NotificationType eventType, TParams eventParams) { // Allow null content JToken contentObject = @@ -152,7 +152,7 @@ public async Task WriteEvent(NotificationType SendRequest( + public Task SendRequestAsync( RequestType0 requestType0) { - return this.SendRequest( + return this.SendRequestAsync( RequestType.ConvertToRequestType(requestType0), null); } @@ -138,14 +138,14 @@ public Task SendRequest( /// /// /// - public Task SendRequest( + public Task SendRequestAsync( RequestType requestType, TParams requestParams) { - return this.SendRequest(requestType, requestParams, true); + return this.SendRequestAsync(requestType, requestParams, true); } - public async Task SendRequest( + public async Task SendRequestAsync( RequestType requestType, TParams requestParams, bool waitForResponse) @@ -170,7 +170,7 @@ public async Task SendRequest( + await this.protocolChannel.MessageWriter.WriteRequestAsync( requestType, requestParams, this.currentMessageId); @@ -198,7 +198,7 @@ await this.protocolChannel.MessageWriter.WriteRequestThe type of event being sent. /// The event parameters being sent. /// A Task that tracks completion of the send operation. - public Task SendEvent( + public Task SendEventAsync( NotificationType eventType, TParams eventParams) { @@ -221,7 +221,7 @@ public Task SendEvent( this.SynchronizationContext.Post( async (obj) => { - await this.protocolChannel.MessageWriter.WriteEvent( + await this.protocolChannel.MessageWriter.WriteEventAsync( eventType, eventParams); @@ -232,7 +232,7 @@ await this.protocolChannel.MessageWriter.WriteEvent( } else { - return this.protocolChannel.MessageWriter.WriteEvent( + return this.protocolChannel.MessageWriter.WriteEventAsync( eventType, eventParams); } @@ -261,7 +261,7 @@ private void StartMessageLoop() this.messageLoopThread = new AsyncContextThread("Message Dispatcher"); this.messageLoopThread .Run( - () => this.ListenForMessages(this.messageLoopCancellationToken.Token), + () => this.ListenForMessagesAsync(this.messageLoopCancellationToken.Token), this.Logger) .ContinueWith(this.OnListenTaskCompleted); } @@ -311,7 +311,7 @@ private void MessageDispatcher_UnhandledException(object sender, Exception e) #region Private Methods - private async Task ListenForMessages(CancellationToken cancellationToken) + private async Task ListenForMessagesAsync(CancellationToken cancellationToken) { this.SynchronizationContext = SynchronizationContext.Current; @@ -324,7 +324,7 @@ private async Task ListenForMessages(CancellationToken cancellationToken) try { // Read a message from the channel - newMessage = await this.protocolChannel.MessageReader.ReadMessage(); + newMessage = await this.protocolChannel.MessageReader.ReadMessageAsync(); } catch (MessageParseException e) { @@ -376,7 +376,7 @@ private async Task ListenForMessages(CancellationToken cancellationToken) else { // Process the message - await this.messageDispatcher.DispatchMessage( + await this.messageDispatcher.DispatchMessageAsync( newMessage, this.protocolChannel.MessageWriter); } diff --git a/src/PowerShellEditorServices.Protocol/MessageProtocol/RequestContext.cs b/src/PowerShellEditorServices.Protocol/MessageProtocol/RequestContext.cs index 55c473f01..94c9264b7 100644 --- a/src/PowerShellEditorServices.Protocol/MessageProtocol/RequestContext.cs +++ b/src/PowerShellEditorServices.Protocol/MessageProtocol/RequestContext.cs @@ -19,24 +19,24 @@ public RequestContext(Message requestMessage, MessageWriter messageWriter) this.messageWriter = messageWriter; } - public async Task SendResult(TResult resultDetails) + public async Task SendResultAsync(TResult resultDetails) { - await this.messageWriter.WriteResponse( + await this.messageWriter.WriteResponseAsync( resultDetails, requestMessage.Method, requestMessage.Id); } - public async Task SendEvent(NotificationType eventType, TParams eventParams) + public async Task SendEventAsync(NotificationType eventType, TParams eventParams) { - await this.messageWriter.WriteEvent( + await this.messageWriter.WriteEventAsync( eventType, eventParams); } - public async Task SendError(object errorDetails) + public async Task SendErrorAsync(object errorDetails) { - await this.messageWriter.WriteMessage( + await this.messageWriter.WriteMessageAsync( Message.ResponseError( requestMessage.Id, requestMessage.Method, diff --git a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs index 96f04ba09..69a720f8e 100644 --- a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs +++ b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs @@ -63,33 +63,33 @@ public DebugAdapter( public void Start() { // Register all supported message types - _messageHandlers.SetRequestHandler(InitializeRequest.Type, HandleInitializeRequest); - - _messageHandlers.SetRequestHandler(LaunchRequest.Type, HandleLaunchRequest); - _messageHandlers.SetRequestHandler(AttachRequest.Type, HandleAttachRequest); - _messageHandlers.SetRequestHandler(ConfigurationDoneRequest.Type, HandleConfigurationDoneRequest); - _messageHandlers.SetRequestHandler(DisconnectRequest.Type, HandleDisconnectRequest); - - _messageHandlers.SetRequestHandler(SetBreakpointsRequest.Type, HandleSetBreakpointsRequest); - _messageHandlers.SetRequestHandler(SetExceptionBreakpointsRequest.Type, HandleSetExceptionBreakpointsRequest); - _messageHandlers.SetRequestHandler(SetFunctionBreakpointsRequest.Type, HandleSetFunctionBreakpointsRequest); - - _messageHandlers.SetRequestHandler(ContinueRequest.Type, HandleContinueRequest); - _messageHandlers.SetRequestHandler(NextRequest.Type, HandleNextRequest); - _messageHandlers.SetRequestHandler(StepInRequest.Type, HandleStepInRequest); - _messageHandlers.SetRequestHandler(StepOutRequest.Type, HandleStepOutRequest); - _messageHandlers.SetRequestHandler(PauseRequest.Type, HandlePauseRequest); - - _messageHandlers.SetRequestHandler(ThreadsRequest.Type, HandleThreadsRequest); - _messageHandlers.SetRequestHandler(StackTraceRequest.Type, HandleStackTraceRequest); - _messageHandlers.SetRequestHandler(ScopesRequest.Type, HandleScopesRequest); - _messageHandlers.SetRequestHandler(VariablesRequest.Type, HandleVariablesRequest); - _messageHandlers.SetRequestHandler(SetVariableRequest.Type, HandleSetVariablesRequest); - _messageHandlers.SetRequestHandler(SourceRequest.Type, HandleSourceRequest); - _messageHandlers.SetRequestHandler(EvaluateRequest.Type, HandleEvaluateRequest); + _messageHandlers.SetRequestHandler(InitializeRequest.Type, HandleInitializeRequestAsync); + + _messageHandlers.SetRequestHandler(LaunchRequest.Type, HandleLaunchRequestAsync); + _messageHandlers.SetRequestHandler(AttachRequest.Type, HandleAttachRequestAsync); + _messageHandlers.SetRequestHandler(ConfigurationDoneRequest.Type, HandleConfigurationDoneRequestAsync); + _messageHandlers.SetRequestHandler(DisconnectRequest.Type, HandleDisconnectRequestAsync); + + _messageHandlers.SetRequestHandler(SetBreakpointsRequest.Type, HandleSetBreakpointsRequestAsync); + _messageHandlers.SetRequestHandler(SetExceptionBreakpointsRequest.Type, HandleSetExceptionBreakpointsRequestAsync); + _messageHandlers.SetRequestHandler(SetFunctionBreakpointsRequest.Type, HandleSetFunctionBreakpointsRequestAsync); + + _messageHandlers.SetRequestHandler(ContinueRequest.Type, HandleContinueRequestAsync); + _messageHandlers.SetRequestHandler(NextRequest.Type, HandleNextRequestAsync); + _messageHandlers.SetRequestHandler(StepInRequest.Type, HandleStepInRequestAsync); + _messageHandlers.SetRequestHandler(StepOutRequest.Type, HandleStepOutRequestAsync); + _messageHandlers.SetRequestHandler(PauseRequest.Type, HandlePauseRequestAsync); + + _messageHandlers.SetRequestHandler(ThreadsRequest.Type, HandleThreadsRequestAsync); + _messageHandlers.SetRequestHandler(StackTraceRequest.Type, HandleStackTraceRequestAsync); + _messageHandlers.SetRequestHandler(ScopesRequest.Type, HandleScopesRequestAsync); + _messageHandlers.SetRequestHandler(VariablesRequest.Type, HandleVariablesRequestAsync); + _messageHandlers.SetRequestHandler(SetVariableRequest.Type, HandleSetVariablesRequestAsync); + _messageHandlers.SetRequestHandler(SourceRequest.Type, HandleSourceRequestAsync); + _messageHandlers.SetRequestHandler(EvaluateRequest.Type, HandleEvaluateRequestAsync); } - protected Task LaunchScript(RequestContext requestContext, string scriptToLaunch) + protected Task LaunchScriptAsync(RequestContext requestContext, string scriptToLaunch) { // Is this an untitled script? Task launchTask = null; @@ -99,18 +99,18 @@ protected Task LaunchScript(RequestContext requestContext, string script ScriptFile untitledScript = _editorSession.Workspace.GetFile(scriptToLaunch); launchTask = _editorSession.PowerShellContext - .ExecuteScriptString(untitledScript.Contents, true, true); + .ExecuteScriptStringAsync(untitledScript.Contents, true, true); } else { launchTask = _editorSession.PowerShellContext - .ExecuteScriptWithArgs(scriptToLaunch, _arguments, writeInputToHost: true); + .ExecuteScriptWithArgsAsync(scriptToLaunch, _arguments, writeInputToHost: true); } - return launchTask.ContinueWith(OnExecutionCompleted); + return launchTask.ContinueWith(OnExecutionCompletedAsync); } - private async Task OnExecutionCompleted(Task executeTask) + private async Task OnExecutionCompletedAsync(Task executeTask) { try { @@ -136,12 +136,12 @@ private async Task OnExecutionCompleted(Task executeTask) { try { - await _editorSession.PowerShellContext.ExecuteScriptString("Exit-PSHostProcess"); + await _editorSession.PowerShellContext.ExecuteScriptStringAsync("Exit-PSHostProcess"); if (_isRemoteAttach && _editorSession.PowerShellContext.CurrentRunspace.Location == RunspaceLocation.Remote) { - await _editorSession.PowerShellContext.ExecuteScriptString("Exit-PSSession"); + await _editorSession.PowerShellContext.ExecuteScriptStringAsync("Exit-PSSession"); } } catch (Exception e) @@ -156,12 +156,12 @@ private async Task OnExecutionCompleted(Task executeTask) if (_disconnectRequestContext != null) { // Respond to the disconnect request and stop the server - await _disconnectRequestContext.SendResult(null); + await _disconnectRequestContext.SendResultAsync(null); Stop(); } else { - await _messageSender.SendEvent( + await _messageSender.SendEventAsync( TerminatedEvent.Type, new TerminatedEvent()); } @@ -173,9 +173,9 @@ protected void Stop() if (_editorSession != null) { - _editorSession.PowerShellContext.RunspaceChanged -= powerShellContext_RunspaceChanged; - _editorSession.DebugService.DebuggerStopped -= DebugService_DebuggerStopped; - _editorSession.PowerShellContext.DebuggerResumed -= powerShellContext_DebuggerResumed; + _editorSession.PowerShellContext.RunspaceChanged -= powerShellContext_RunspaceChangedAsync; + _editorSession.DebugService.DebuggerStopped -= DebugService_DebuggerStoppedAsync; + _editorSession.PowerShellContext.DebuggerResumed -= powerShellContext_DebuggerResumedAsync; if (_ownsEditorSession) { @@ -190,15 +190,15 @@ protected void Stop() #region Built-in Message Handlers - private async Task HandleInitializeRequest( + private async Task HandleInitializeRequestAsync( object shutdownParams, RequestContext requestContext) { // Clear any existing breakpoints before proceeding - await ClearSessionBreakpoints(); + await ClearSessionBreakpointsAsync(); // Now send the Initialize response to continue setup - await requestContext.SendResult( + await requestContext.SendResultAsync( new InitializeResponseBody { SupportsConfigurationDoneRequest = true, SupportsFunctionBreakpoints = true, @@ -208,7 +208,7 @@ await requestContext.SendResult( }); } - protected async Task HandleConfigurationDoneRequest( + protected async Task HandleConfigurationDoneRequestAsync( object args, RequestContext requestContext) { @@ -219,7 +219,7 @@ protected async Task HandleConfigurationDoneRequest( if (_editorSession.PowerShellContext.SessionState == PowerShellContextState.Ready) { // Configuration is done, launch the script - var nonAwaitedTask = LaunchScript(requestContext, _scriptToLaunch) + var nonAwaitedTask = LaunchScriptAsync(requestContext, _scriptToLaunch) .ConfigureAwait(continueOnCapturedContext: false); } else @@ -230,7 +230,7 @@ protected async Task HandleConfigurationDoneRequest( } } - await requestContext.SendResult(null); + await requestContext.SendResultAsync(null); if (_isInteractiveDebugSession) { @@ -245,14 +245,14 @@ protected async Task HandleConfigurationDoneRequest( { // If this is an interactive session and there's a pending breakpoint, // send that information along to the debugger client - DebugService_DebuggerStopped( + DebugService_DebuggerStoppedAsync( this, _editorSession.DebugService.CurrentDebuggerStoppedEventArgs); } } } - protected async Task HandleLaunchRequest( + protected async Task HandleLaunchRequestAsync( LaunchRequestArguments launchParams, RequestContext requestContext) { @@ -298,7 +298,7 @@ protected async Task HandleLaunchRequest( // the working dir should not be changed. if (!string.IsNullOrEmpty(workingDir)) { - await _editorSession.PowerShellContext.SetWorkingDirectory(workingDir, isPathAlreadyEscaped: false); + await _editorSession.PowerShellContext.SetWorkingDirectoryAsync(workingDir, isPathAlreadyEscaped: false); } Logger.Write(LogLevel.Verbose, $"Working dir " + (string.IsNullOrEmpty(workingDir) ? "not set." : $"set to '{workingDir}'")); @@ -329,7 +329,7 @@ protected async Task HandleLaunchRequest( _editorSession.PowerShellContext.CurrentRunspace); } - await requestContext.SendResult(null); + await requestContext.SendResultAsync(null); // If no script is being launched, mark this as an interactive // debugging session @@ -337,12 +337,12 @@ protected async Task HandleLaunchRequest( // Send the InitializedEvent so that the debugger will continue // sending configuration requests - await _messageSender.SendEvent( + await _messageSender.SendEventAsync( InitializedEvent.Type, null); } - protected async Task HandleAttachRequest( + protected async Task HandleAttachRequestAsync( AttachRequestArguments attachParams, RequestContext requestContext) { @@ -360,7 +360,7 @@ protected async Task HandleAttachRequest( LogLevel.Normal, $"Attach request aborted, received {attachParams.ProcessId} for processId."); - await requestContext.SendError( + await requestContext.SendErrorAsync( "User aborted attach to PowerShell host process."); return; @@ -375,26 +375,26 @@ await requestContext.SendError( if (runspaceVersion.Version.Major < 4) { - await requestContext.SendError( + await requestContext.SendErrorAsync( $"Remote sessions are only available with PowerShell 4 and higher (current session is {runspaceVersion.Version})."); return; } else if (_editorSession.PowerShellContext.CurrentRunspace.Location == RunspaceLocation.Remote) { - await requestContext.SendError( + await requestContext.SendErrorAsync( $"Cannot attach to a process in a remote session when already in a remote session."); return; } - await _editorSession.PowerShellContext.ExecuteScriptString( + await _editorSession.PowerShellContext.ExecuteScriptStringAsync( $"Enter-PSSession -ComputerName \"{attachParams.ComputerName}\"", errorMessages); if (errorMessages.Length > 0) { - await requestContext.SendError( + await requestContext.SendErrorAsync( $"Could not establish remote session to computer '{attachParams.ComputerName}'"); return; @@ -410,26 +410,26 @@ await requestContext.SendError( if (runspaceVersion.Version.Major < 5) { - await requestContext.SendError( + await requestContext.SendErrorAsync( $"Attaching to a process is only available with PowerShell 5 and higher (current session is {runspaceVersion.Version})."); return; } - await _editorSession.PowerShellContext.ExecuteScriptString( + await _editorSession.PowerShellContext.ExecuteScriptStringAsync( $"Enter-PSHostProcess -Id {processId}", errorMessages); if (errorMessages.Length > 0) { - await requestContext.SendError( + await requestContext.SendErrorAsync( $"Could not attach to process '{processId}'"); return; } // Clear any existing breakpoints before proceeding - await ClearSessionBreakpoints(); + await ClearSessionBreakpointsAsync(); // Execute the Debug-Runspace command but don't await it because it // will block the debug adapter initialization process. The @@ -439,8 +439,8 @@ await requestContext.SendError( _waitingForAttach = true; Task nonAwaitedTask = _editorSession.PowerShellContext - .ExecuteScriptString($"\nDebug-Runspace -Id {runspaceId}") - .ContinueWith(OnExecutionCompleted); + .ExecuteScriptStringAsync($"\nDebug-Runspace -Id {runspaceId}") + .ContinueWith(OnExecutionCompletedAsync); } else { @@ -448,16 +448,16 @@ await requestContext.SendError( LogLevel.Error, $"Attach request failed, '{attachParams.ProcessId}' is an invalid value for the processId."); - await requestContext.SendError( + await requestContext.SendErrorAsync( "A positive integer must be specified for the processId field."); return; } - await requestContext.SendResult(null); + await requestContext.SendResultAsync(null); } - protected async Task HandleDisconnectRequest( + protected async Task HandleDisconnectRequestAsync( object disconnectParams, RequestContext requestContext) { @@ -472,20 +472,20 @@ protected async Task HandleDisconnectRequest( if (_isInteractiveDebugSession) { - await OnExecutionCompleted(null); + await OnExecutionCompletedAsync(null); } } else { UnregisterEventHandlers(); - await requestContext.SendResult(null); + await requestContext.SendResultAsync(null); Stop(); } } } - protected async Task HandleSetBreakpointsRequest( + protected async Task HandleSetBreakpointsRequestAsync( SetBreakpointsRequestArguments setBreakpointsParams, RequestContext requestContext) { @@ -504,7 +504,7 @@ protected async Task HandleSetBreakpointsRequest( srcBkpt, setBreakpointsParams.Source.Path, message, verified: _noDebug)); // Return non-verified breakpoint message. - await requestContext.SendResult( + await requestContext.SendResultAsync( new SetBreakpointsResponseBody { Breakpoints = srcBreakpoints.ToArray() }); @@ -527,7 +527,7 @@ await requestContext.SendResult( srcBkpt, setBreakpointsParams.Source.Path, message, verified: _noDebug)); // Return non-verified breakpoint message. - await requestContext.SendResult( + await requestContext.SendResultAsync( new SetBreakpointsResponseBody { Breakpoints = srcBreakpoints.ToArray() @@ -558,7 +558,7 @@ await requestContext.SendResult( try { updatedBreakpointDetails = - await _editorSession.DebugService.SetLineBreakpoints( + await _editorSession.DebugService.SetLineBreakpointsAsync( scriptFile, breakpointDetails); } @@ -573,7 +573,7 @@ await _editorSession.DebugService.SetLineBreakpoints( } } - await requestContext.SendResult( + await requestContext.SendResultAsync( new SetBreakpointsResponseBody { Breakpoints = updatedBreakpointDetails @@ -582,7 +582,7 @@ await requestContext.SendResult( }); } - protected async Task HandleSetFunctionBreakpointsRequest( + protected async Task HandleSetFunctionBreakpointsRequestAsync( SetFunctionBreakpointsRequestArguments setBreakpointsParams, RequestContext requestContext) { @@ -605,7 +605,7 @@ protected async Task HandleSetFunctionBreakpointsRequest( try { updatedBreakpointDetails = - await _editorSession.DebugService.SetCommandBreakpoints( + await _editorSession.DebugService.SetCommandBreakpointsAsync( breakpointDetails); } catch (Exception e) @@ -619,7 +619,7 @@ await _editorSession.DebugService.SetCommandBreakpoints( } } - await requestContext.SendResult( + await requestContext.SendResultAsync( new SetBreakpointsResponseBody { Breakpoints = updatedBreakpointDetails @@ -628,7 +628,7 @@ await requestContext.SendResult( }); } - protected async Task HandleSetExceptionBreakpointsRequest( + protected async Task HandleSetExceptionBreakpointsRequestAsync( SetExceptionBreakpointsRequestArguments setExceptionBreakpointsParams, RequestContext requestContext) { @@ -654,28 +654,28 @@ protected async Task HandleSetExceptionBreakpointsRequest( // } //} - await requestContext.SendResult(null); + await requestContext.SendResultAsync(null); } - protected async Task HandleContinueRequest( + protected async Task HandleContinueRequestAsync( object continueParams, RequestContext requestContext) { _editorSession.DebugService.Continue(); - await requestContext.SendResult(null); + await requestContext.SendResultAsync(null); } - protected async Task HandleNextRequest( + protected async Task HandleNextRequestAsync( object nextParams, RequestContext requestContext) { _editorSession.DebugService.StepOver(); - await requestContext.SendResult(null); + await requestContext.SendResultAsync(null); } - protected Task HandlePauseRequest( + protected Task HandlePauseRequestAsync( object pauseParams, RequestContext requestContext) { @@ -685,36 +685,36 @@ protected Task HandlePauseRequest( } catch (NotSupportedException e) { - return requestContext.SendError(e.Message); + return requestContext.SendErrorAsync(e.Message); } // This request is responded to by sending the "stopped" event return Task.FromResult(true); } - protected async Task HandleStepInRequest( + protected async Task HandleStepInRequestAsync( object stepInParams, RequestContext requestContext) { _editorSession.DebugService.StepIn(); - await requestContext.SendResult(null); + await requestContext.SendResultAsync(null); } - protected async Task HandleStepOutRequest( + protected async Task HandleStepOutRequestAsync( object stepOutParams, RequestContext requestContext) { _editorSession.DebugService.StepOut(); - await requestContext.SendResult(null); + await requestContext.SendResultAsync(null); } - protected async Task HandleThreadsRequest( + protected async Task HandleThreadsRequestAsync( object threadsParams, RequestContext requestContext) { - await requestContext.SendResult( + await requestContext.SendResultAsync( new ThreadsResponseBody { Threads = new Thread[] @@ -729,7 +729,7 @@ await requestContext.SendResult( }); } - protected async Task HandleStackTraceRequest( + protected async Task HandleStackTraceRequestAsync( StackTraceRequestArguments stackTraceParams, RequestContext requestContext) { @@ -740,7 +740,7 @@ protected async Task HandleStackTraceRequest( // begun building. if (stackFrames == null) { - await requestContext.SendResult( + await requestContext.SendResultAsync( new StackTraceResponseBody { StackFrames = new StackFrame[0], @@ -773,7 +773,7 @@ await requestContext.SendResult( i)); } - await requestContext.SendResult( + await requestContext.SendResultAsync( new StackTraceResponseBody { StackFrames = newStackFrames.ToArray(), @@ -781,7 +781,7 @@ await requestContext.SendResult( }); } - protected async Task HandleScopesRequest( + protected async Task HandleScopesRequestAsync( ScopesRequestArguments scopesParams, RequestContext requestContext) { @@ -789,7 +789,7 @@ protected async Task HandleScopesRequest( _editorSession.DebugService.GetVariableScopes( scopesParams.FrameId); - await requestContext.SendResult( + await requestContext.SendResultAsync( new ScopesResponseBody { Scopes = @@ -799,7 +799,7 @@ await requestContext.SendResult( }); } - protected async Task HandleVariablesRequest( + protected async Task HandleVariablesRequestAsync( VariablesRequestArguments variablesParams, RequestContext requestContext) { @@ -824,17 +824,17 @@ protected async Task HandleVariablesRequest( // TODO: This shouldn't be so broad } - await requestContext.SendResult(variablesResponse); + await requestContext.SendResultAsync(variablesResponse); } - protected async Task HandleSetVariablesRequest( + protected async Task HandleSetVariablesRequestAsync( SetVariableRequestArguments setVariableParams, RequestContext requestContext) { try { string updatedValue = - await _editorSession.DebugService.SetVariable( + await _editorSession.DebugService.SetVariableAsync( setVariableParams.VariablesReference, setVariableParams.Name, setVariableParams.Value); @@ -844,7 +844,7 @@ await _editorSession.DebugService.SetVariable( Value = updatedValue }; - await requestContext.SendResult(setVariableResponse); + await requestContext.SendResultAsync(setVariableResponse); } catch (Exception ex) when (ex is ArgumentTransformationMetadataException || ex is InvalidPowerShellExpressionException || @@ -852,18 +852,18 @@ ex is InvalidPowerShellExpressionException || { // Catch common, innocuous errors caused by the user supplying a value that can't be converted or the variable is not settable. Logger.Write(LogLevel.Verbose, $"Failed to set variable: {ex.Message}"); - await requestContext.SendError(ex.Message); + await requestContext.SendErrorAsync(ex.Message); } catch (Exception ex) { Logger.Write(LogLevel.Error, $"Unexpected error setting variable: {ex.Message}"); string msg = $"Unexpected error: {ex.GetType().Name} - {ex.Message} Please report this error to the PowerShellEditorServices project on GitHub."; - await requestContext.SendError(msg); + await requestContext.SendErrorAsync(msg); } } - protected Task HandleSourceRequest( + protected Task HandleSourceRequestAsync( SourceRequestArguments sourceParams, RequestContext requestContext) { @@ -873,7 +873,7 @@ protected Task HandleSourceRequest( return Task.FromResult(true); } - protected async Task HandleEvaluateRequest( + protected async Task HandleEvaluateRequestAsync( EvaluateRequestArguments evaluateParams, RequestContext requestContext) { @@ -891,7 +891,7 @@ protected async Task HandleEvaluateRequest( var notAwaited = _editorSession .PowerShellContext - .ExecuteScriptString(evaluateParams.Expression, false, true) + .ExecuteScriptStringAsync(evaluateParams.Expression, false, true) .ConfigureAwait(false); } else @@ -910,7 +910,7 @@ protected async Task HandleEvaluateRequest( if (result == null) { result = - await _editorSession.DebugService.EvaluateExpression( + await _editorSession.DebugService.EvaluateExpressionAsync( evaluateParams.Expression, evaluateParams.FrameId, isFromRepl); @@ -926,7 +926,7 @@ await _editorSession.DebugService.EvaluateExpression( } } - await requestContext.SendResult( + await requestContext.SendResultAsync( new EvaluateResponseBody { Result = valueString, @@ -934,9 +934,9 @@ await requestContext.SendResult( }); } - private async Task WriteUseIntegratedConsoleMessage() + private async Task WriteUseIntegratedConsoleMessageAsync() { - await _messageSender.SendEvent( + await _messageSender.SendEventAsync( OutputEvent.Type, new OutputEventBody { @@ -947,25 +947,25 @@ await _messageSender.SendEvent( private void RegisterEventHandlers() { - _editorSession.PowerShellContext.RunspaceChanged += powerShellContext_RunspaceChanged; - _editorSession.DebugService.BreakpointUpdated += DebugService_BreakpointUpdated; - _editorSession.DebugService.DebuggerStopped += DebugService_DebuggerStopped; - _editorSession.PowerShellContext.DebuggerResumed += powerShellContext_DebuggerResumed; + _editorSession.PowerShellContext.RunspaceChanged += powerShellContext_RunspaceChangedAsync; + _editorSession.DebugService.BreakpointUpdated += DebugService_BreakpointUpdatedAsync; + _editorSession.DebugService.DebuggerStopped += DebugService_DebuggerStoppedAsync; + _editorSession.PowerShellContext.DebuggerResumed += powerShellContext_DebuggerResumedAsync; } private void UnregisterEventHandlers() { - _editorSession.PowerShellContext.RunspaceChanged -= powerShellContext_RunspaceChanged; - _editorSession.DebugService.BreakpointUpdated -= DebugService_BreakpointUpdated; - _editorSession.DebugService.DebuggerStopped -= DebugService_DebuggerStopped; - _editorSession.PowerShellContext.DebuggerResumed -= powerShellContext_DebuggerResumed; + _editorSession.PowerShellContext.RunspaceChanged -= powerShellContext_RunspaceChangedAsync; + _editorSession.DebugService.BreakpointUpdated -= DebugService_BreakpointUpdatedAsync; + _editorSession.DebugService.DebuggerStopped -= DebugService_DebuggerStoppedAsync; + _editorSession.PowerShellContext.DebuggerResumed -= powerShellContext_DebuggerResumedAsync; } - private async Task ClearSessionBreakpoints() + private async Task ClearSessionBreakpointsAsync() { try { - await _editorSession.DebugService.ClearAllBreakpoints(); + await _editorSession.DebugService.ClearAllBreakpointsAsync(); } catch (Exception e) { @@ -977,7 +977,7 @@ private async Task ClearSessionBreakpoints() #region Event Handlers - async void DebugService_DebuggerStopped(object sender, DebuggerStoppedEventArgs e) + async void DebugService_DebuggerStoppedAsync(object sender, DebuggerStoppedEventArgs e) { // Provide the reason for why the debugger has stopped script execution. // See https://github.com/Microsoft/vscode/issues/3648 @@ -994,7 +994,7 @@ e.OriginalEvent.Breakpoints[0] is CommandBreakpoint : "breakpoint"; } - await _messageSender.SendEvent( + await _messageSender.SendEventAsync( StoppedEvent.Type, new StoppedEventBody { @@ -1007,7 +1007,7 @@ await _messageSender.SendEvent( }); } - async void powerShellContext_RunspaceChanged(object sender, RunspaceChangedEventArgs e) + async void powerShellContext_RunspaceChangedAsync(object sender, RunspaceChangedEventArgs e) { if (_waitingForAttach && e.ChangeAction == RunspaceChangeAction.Enter && @@ -1016,7 +1016,7 @@ async void powerShellContext_RunspaceChanged(object sender, RunspaceChangedEvent // Send the InitializedEvent so that the debugger will continue // sending configuration requests _waitingForAttach = false; - await _messageSender.SendEvent(InitializedEvent.Type, null); + await _messageSender.SendEventAsync(InitializedEvent.Type, null); } else if ( e.ChangeAction == RunspaceChangeAction.Exit && @@ -1026,7 +1026,7 @@ async void powerShellContext_RunspaceChanged(object sender, RunspaceChangedEvent // Exited the session while the debugger is stopped, // send a ContinuedEvent so that the client changes the // UI to appear to be running again - await _messageSender.SendEvent( + await _messageSender.SendEventAsync( ContinuedEvent.Type, new ContinuedEvent { @@ -1036,9 +1036,9 @@ await _messageSender.SendEvent( } } - private async void powerShellContext_DebuggerResumed(object sender, DebuggerResumeAction e) + private async void powerShellContext_DebuggerResumedAsync(object sender, DebuggerResumeAction e) { - await _messageSender.SendEvent( + await _messageSender.SendEventAsync( ContinuedEvent.Type, new ContinuedEvent { @@ -1047,7 +1047,7 @@ await _messageSender.SendEvent( }); } - private async void DebugService_BreakpointUpdated(object sender, BreakpointUpdatedEventArgs e) + private async void DebugService_BreakpointUpdatedAsync(object sender, BreakpointUpdatedEventArgs e) { string reason = "changed"; @@ -1088,7 +1088,7 @@ private async void DebugService_BreakpointUpdated(object sender, BreakpointUpdat breakpoint.Verified = e.UpdateType != BreakpointUpdateType.Disabled; - await _messageSender.SendEvent( + await _messageSender.SendEventAsync( BreakpointEvent.Type, new BreakpointEvent { diff --git a/src/PowerShellEditorServices.Protocol/Server/IMessageDispatcher.cs b/src/PowerShellEditorServices.Protocol/Server/IMessageDispatcher.cs index 45342a21b..156acb9b2 100644 --- a/src/PowerShellEditorServices.Protocol/Server/IMessageDispatcher.cs +++ b/src/PowerShellEditorServices.Protocol/Server/IMessageDispatcher.cs @@ -10,7 +10,7 @@ namespace Microsoft.PowerShell.EditorServices.Protocol { public interface IMessageDispatcher { - Task DispatchMessage( + Task DispatchMessageAsync( Message messageToDispatch, MessageWriter messageWriter); } diff --git a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs index fe30cd1b2..d325b2d65 100644 --- a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs +++ b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs @@ -80,13 +80,13 @@ public LanguageServer( this.editorSession = editorSession; this.serverCompletedTask = serverCompletedTask; // Attach to the underlying PowerShell context to listen for changes in the runspace or execution status - this.editorSession.PowerShellContext.RunspaceChanged += PowerShellContext_RunspaceChanged; - this.editorSession.PowerShellContext.ExecutionStatusChanged += PowerShellContext_ExecutionStatusChanged; + this.editorSession.PowerShellContext.RunspaceChanged += PowerShellContext_RunspaceChangedAsync; + this.editorSession.PowerShellContext.ExecutionStatusChanged += PowerShellContext_ExecutionStatusChangedAsync; // Attach to ExtensionService events - this.editorSession.ExtensionService.CommandAdded += ExtensionService_ExtensionAdded; - this.editorSession.ExtensionService.CommandUpdated += ExtensionService_ExtensionUpdated; - this.editorSession.ExtensionService.CommandRemoved += ExtensionService_ExtensionRemoved; + this.editorSession.ExtensionService.CommandAdded += ExtensionService_ExtensionAddedAsync; + this.editorSession.ExtensionService.CommandUpdated += ExtensionService_ExtensionUpdatedAsync; + this.editorSession.ExtensionService.CommandRemoved += ExtensionService_ExtensionRemovedAsync; this.messageSender = messageSender; this.messageHandlers = messageHandlers; @@ -98,7 +98,7 @@ public LanguageServer( this.messageSender); this.editorSession.StartDebugService(this.editorOperations); - this.editorSession.DebugService.DebuggerStopped += DebugService_DebuggerStopped; + this.editorSession.DebugService.DebuggerStopped += DebugService_DebuggerStoppedAsync; } /// @@ -109,61 +109,61 @@ public void Start() { // Register all supported message types - this.messageHandlers.SetRequestHandler(ShutdownRequest.Type, this.HandleShutdownRequest); - this.messageHandlers.SetEventHandler(ExitNotification.Type, this.HandleExitNotification); - - this.messageHandlers.SetRequestHandler(InitializeRequest.Type, this.HandleInitializeRequest); - this.messageHandlers.SetEventHandler(InitializedNotification.Type, this.HandleInitializedNotification); - - this.messageHandlers.SetEventHandler(DidOpenTextDocumentNotification.Type, this.HandleDidOpenTextDocumentNotification); - this.messageHandlers.SetEventHandler(DidCloseTextDocumentNotification.Type, this.HandleDidCloseTextDocumentNotification); - this.messageHandlers.SetEventHandler(DidSaveTextDocumentNotification.Type, this.HandleDidSaveTextDocumentNotification); - this.messageHandlers.SetEventHandler(DidChangeTextDocumentNotification.Type, this.HandleDidChangeTextDocumentNotification); - this.messageHandlers.SetEventHandler(DidChangeConfigurationNotification.Type, this.HandleDidChangeConfigurationNotification); - - this.messageHandlers.SetRequestHandler(DefinitionRequest.Type, this.HandleDefinitionRequest); - this.messageHandlers.SetRequestHandler(ReferencesRequest.Type, this.HandleReferencesRequest); - this.messageHandlers.SetRequestHandler(CompletionRequest.Type, this.HandleCompletionRequest); - this.messageHandlers.SetRequestHandler(CompletionResolveRequest.Type, this.HandleCompletionResolveRequest); - this.messageHandlers.SetRequestHandler(SignatureHelpRequest.Type, this.HandleSignatureHelpRequest); - this.messageHandlers.SetRequestHandler(DocumentHighlightRequest.Type, this.HandleDocumentHighlightRequest); - this.messageHandlers.SetRequestHandler(HoverRequest.Type, this.HandleHoverRequest); - this.messageHandlers.SetRequestHandler(WorkspaceSymbolRequest.Type, this.HandleWorkspaceSymbolRequest); - this.messageHandlers.SetRequestHandler(CodeActionRequest.Type, this.HandleCodeActionRequest); - this.messageHandlers.SetRequestHandler(DocumentFormattingRequest.Type, this.HandleDocumentFormattingRequest); + this.messageHandlers.SetRequestHandler(ShutdownRequest.Type, this.HandleShutdownRequestAsync); + this.messageHandlers.SetEventHandler(ExitNotification.Type, this.HandleExitNotificationAsync); + + this.messageHandlers.SetRequestHandler(InitializeRequest.Type, this.HandleInitializeRequestAsync); + this.messageHandlers.SetEventHandler(InitializedNotification.Type, this.HandleInitializedNotificationAsync); + + this.messageHandlers.SetEventHandler(DidOpenTextDocumentNotification.Type, this.HandleDidOpenTextDocumentNotificationAsync); + this.messageHandlers.SetEventHandler(DidCloseTextDocumentNotification.Type, this.HandleDidCloseTextDocumentNotificationAsync); + this.messageHandlers.SetEventHandler(DidSaveTextDocumentNotification.Type, this.HandleDidSaveTextDocumentNotificationAsync); + this.messageHandlers.SetEventHandler(DidChangeTextDocumentNotification.Type, this.HandleDidChangeTextDocumentNotificationAsync); + this.messageHandlers.SetEventHandler(DidChangeConfigurationNotification.Type, this.HandleDidChangeConfigurationNotificationAsync); + + this.messageHandlers.SetRequestHandler(DefinitionRequest.Type, this.HandleDefinitionRequestAsync); + this.messageHandlers.SetRequestHandler(ReferencesRequest.Type, this.HandleReferencesRequestAsync); + this.messageHandlers.SetRequestHandler(CompletionRequest.Type, this.HandleCompletionRequestAsync); + this.messageHandlers.SetRequestHandler(CompletionResolveRequest.Type, this.HandleCompletionResolveRequestAsync); + this.messageHandlers.SetRequestHandler(SignatureHelpRequest.Type, this.HandleSignatureHelpRequestAsync); + this.messageHandlers.SetRequestHandler(DocumentHighlightRequest.Type, this.HandleDocumentHighlightRequestAsync); + this.messageHandlers.SetRequestHandler(HoverRequest.Type, this.HandleHoverRequestAsync); + this.messageHandlers.SetRequestHandler(WorkspaceSymbolRequest.Type, this.HandleWorkspaceSymbolRequestAsync); + this.messageHandlers.SetRequestHandler(CodeActionRequest.Type, this.HandleCodeActionRequestAsync); + this.messageHandlers.SetRequestHandler(DocumentFormattingRequest.Type, this.HandleDocumentFormattingRequestAsync); this.messageHandlers.SetRequestHandler( DocumentRangeFormattingRequest.Type, - this.HandleDocumentRangeFormattingRequest); + this.HandleDocumentRangeFormattingRequestAsync); this.messageHandlers.SetRequestHandler(FoldingRangeRequest.Type, this.HandleFoldingRangeRequestAsync); - this.messageHandlers.SetRequestHandler(ShowHelpRequest.Type, this.HandleShowHelpRequest); + this.messageHandlers.SetRequestHandler(ShowHelpRequest.Type, this.HandleShowHelpRequestAsync); - this.messageHandlers.SetRequestHandler(ExpandAliasRequest.Type, this.HandleExpandAliasRequest); + this.messageHandlers.SetRequestHandler(ExpandAliasRequest.Type, this.HandleExpandAliasRequestAsync); this.messageHandlers.SetRequestHandler(GetCommandRequest.Type, this.HandleGetCommandRequestAsync); - this.messageHandlers.SetRequestHandler(FindModuleRequest.Type, this.HandleFindModuleRequest); - this.messageHandlers.SetRequestHandler(InstallModuleRequest.Type, this.HandleInstallModuleRequest); + this.messageHandlers.SetRequestHandler(FindModuleRequest.Type, this.HandleFindModuleRequestAsync); + this.messageHandlers.SetRequestHandler(InstallModuleRequest.Type, this.HandleInstallModuleRequestAsync); - this.messageHandlers.SetRequestHandler(InvokeExtensionCommandRequest.Type, this.HandleInvokeExtensionCommandRequest); + this.messageHandlers.SetRequestHandler(InvokeExtensionCommandRequest.Type, this.HandleInvokeExtensionCommandRequestAsync); - this.messageHandlers.SetRequestHandler(PowerShellVersionRequest.Type, this.HandlePowerShellVersionRequest); + this.messageHandlers.SetRequestHandler(PowerShellVersionRequest.Type, this.HandlePowerShellVersionRequestAsync); - this.messageHandlers.SetRequestHandler(NewProjectFromTemplateRequest.Type, this.HandleNewProjectFromTemplateRequest); - this.messageHandlers.SetRequestHandler(GetProjectTemplatesRequest.Type, this.HandleGetProjectTemplatesRequest); + this.messageHandlers.SetRequestHandler(NewProjectFromTemplateRequest.Type, this.HandleNewProjectFromTemplateRequestAsync); + this.messageHandlers.SetRequestHandler(GetProjectTemplatesRequest.Type, this.HandleGetProjectTemplatesRequestAsync); - this.messageHandlers.SetRequestHandler(DebugAdapterMessages.EvaluateRequest.Type, this.HandleEvaluateRequest); + this.messageHandlers.SetRequestHandler(DebugAdapterMessages.EvaluateRequest.Type, this.HandleEvaluateRequestAsync); - this.messageHandlers.SetRequestHandler(GetPSSARulesRequest.Type, this.HandleGetPSSARulesRequest); - this.messageHandlers.SetRequestHandler(SetPSSARulesRequest.Type, this.HandleSetPSSARulesRequest); + this.messageHandlers.SetRequestHandler(GetPSSARulesRequest.Type, this.HandleGetPSSARulesRequestAsync); + this.messageHandlers.SetRequestHandler(SetPSSARulesRequest.Type, this.HandleSetPSSARulesRequestAsync); - this.messageHandlers.SetRequestHandler(ScriptRegionRequest.Type, this.HandleGetFormatScriptRegionRequest); + this.messageHandlers.SetRequestHandler(ScriptRegionRequest.Type, this.HandleGetFormatScriptRegionRequestAsync); - this.messageHandlers.SetRequestHandler(GetPSHostProcessesRequest.Type, this.HandleGetPSHostProcessesRequest); - this.messageHandlers.SetRequestHandler(CommentHelpRequest.Type, this.HandleCommentHelpRequest); + this.messageHandlers.SetRequestHandler(GetPSHostProcessesRequest.Type, this.HandleGetPSHostProcessesRequestAsync); + this.messageHandlers.SetRequestHandler(CommentHelpRequest.Type, this.HandleCommentHelpRequestAsync); // Initialize the extension service // TODO: This should be made awaited once Initialize is async! - this.editorSession.ExtensionService.Initialize( + this.editorSession.ExtensionService.InitializeAsync( this.editorOperations, this.editorSession.Components).Wait(); } @@ -180,15 +180,15 @@ protected Task Stop() #region Built-in Message Handlers - private async Task HandleShutdownRequest( + private async Task HandleShutdownRequestAsync( RequestContext requestContext) { // Allow the implementor to shut down gracefully - await requestContext.SendResult(new object()); + await requestContext.SendResultAsync(new object()); } - private async Task HandleExitNotification( + private async Task HandleExitNotificationAsync( object exitParams, EventContext eventContext) { @@ -196,14 +196,14 @@ private async Task HandleExitNotification( await this.Stop(); } - private Task HandleInitializedNotification(InitializedParams initializedParams, + private Task HandleInitializedNotificationAsync(InitializedParams initializedParams, EventContext eventContext) { // Can do dynamic registration of capabilities in this notification handler return Task.FromResult(true); } - protected async Task HandleInitializeRequest( + protected async Task HandleInitializeRequestAsync( InitializeParams initializeParams, RequestContext requestContext) { @@ -213,12 +213,12 @@ protected async Task HandleInitializeRequest( // Set the working directory of the PowerShell session to the workspace path if (editorSession.Workspace.WorkspacePath != null) { - await editorSession.PowerShellContext.SetWorkingDirectory( + await editorSession.PowerShellContext.SetWorkingDirectoryAsync( editorSession.Workspace.WorkspacePath, isPathAlreadyEscaped: false); } - await requestContext.SendResult( + await requestContext.SendResultAsync( new InitializeResult { Capabilities = new ServerCapabilities @@ -249,7 +249,7 @@ await requestContext.SendResult( }); } - protected async Task HandleShowHelpRequest( + protected async Task HandleShowHelpRequestAsync( string helpParams, RequestContext requestContext) { @@ -292,11 +292,11 @@ protected async Task HandleShowHelpRequest( // TODO: Rather than print the help in the console, we should send the string back // to VSCode to display in a help pop-up (or similar) - await editorSession.PowerShellContext.ExecuteCommand(checkHelpPSCommand, sendOutputToHost: true); - await requestContext.SendResult(null); + await editorSession.PowerShellContext.ExecuteCommandAsync(checkHelpPSCommand, sendOutputToHost: true); + await requestContext.SendResultAsync(null); } - private async Task HandleSetPSSARulesRequest( + private async Task HandleSetPSSARulesRequestAsync( object param, RequestContext requestContext) { @@ -316,16 +316,16 @@ private async Task HandleSetPSSARulesRequest( editorSession.AnalysisService.ActiveRules = activeRules.ToArray(); } - var sendresult = requestContext.SendResult(null); + var sendresult = requestContext.SendResultAsync(null); var scripFile = editorSession.Workspace.GetFile((string)dynParams.filepath); - await RunScriptDiagnostics( + await RunScriptDiagnosticsAsync( new ScriptFile[] { scripFile }, editorSession, - this.messageSender.SendEvent); + this.messageSender.SendEventAsync); await sendresult; } - private async Task HandleGetFormatScriptRegionRequest( + private async Task HandleGetFormatScriptRegionRequestAsync( ScriptRegionRequestParams requestParams, RequestContext requestContext) { @@ -361,13 +361,13 @@ private async Task HandleGetFormatScriptRegionRequest( break; } - await requestContext.SendResult(new ScriptRegionRequestResult + await requestContext.SendResultAsync(new ScriptRegionRequestResult { scriptRegion = scriptRegion }); } - private async Task HandleGetPSSARulesRequest( + private async Task HandleGetPSSARulesRequestAsync( object param, RequestContext requestContext) { @@ -384,10 +384,10 @@ private async Task HandleGetPSSARulesRequest( } } - await requestContext.SendResult(rules); + await requestContext.SendResultAsync(rules); } - private async Task HandleInstallModuleRequest( + private async Task HandleInstallModuleRequestAsync( string moduleName, RequestContext requestContext ) @@ -395,15 +395,15 @@ RequestContext requestContext var script = string.Format("Install-Module -Name {0} -Scope CurrentUser", moduleName); var executeTask = - editorSession.PowerShellContext.ExecuteScriptString( + editorSession.PowerShellContext.ExecuteScriptStringAsync( script, true, true).ConfigureAwait(false); - await requestContext.SendResult(null); + await requestContext.SendResultAsync(null); } - private Task HandleInvokeExtensionCommandRequest( + private Task HandleInvokeExtensionCommandRequestAsync( InvokeExtensionCommandRequest commandDetails, RequestContext requestContext) { @@ -416,29 +416,29 @@ private Task HandleInvokeExtensionCommandRequest( commandDetails.Context); Task commandTask = - this.editorSession.ExtensionService.InvokeCommand( + this.editorSession.ExtensionService.InvokeCommandAsync( commandDetails.Name, editorContext); commandTask.ContinueWith(t => { - return requestContext.SendResult(null); + return requestContext.SendResultAsync(null); }); return Task.FromResult(true); } - private Task HandleNewProjectFromTemplateRequest( + private Task HandleNewProjectFromTemplateRequestAsync( NewProjectFromTemplateRequest newProjectArgs, RequestContext requestContext) { // Don't await the Task here so that we don't block the session this.editorSession.TemplateService - .CreateFromTemplate(newProjectArgs.TemplatePath, newProjectArgs.DestinationPath) + .CreateFromTemplateAsync(newProjectArgs.TemplatePath, newProjectArgs.DestinationPath) .ContinueWith( async task => { - await requestContext.SendResult( + await requestContext.SendResultAsync( new NewProjectFromTemplateResponse { CreationSuccessful = task.Result @@ -448,19 +448,19 @@ await requestContext.SendResult( return Task.FromResult(true); } - private async Task HandleGetProjectTemplatesRequest( + private async Task HandleGetProjectTemplatesRequestAsync( GetProjectTemplatesRequest requestArgs, RequestContext requestContext) { - bool plasterInstalled = await this.editorSession.TemplateService.ImportPlasterIfInstalled(); + bool plasterInstalled = await this.editorSession.TemplateService.ImportPlasterIfInstalledAsync(); if (plasterInstalled) { var availableTemplates = - await this.editorSession.TemplateService.GetAvailableTemplates( + await this.editorSession.TemplateService.GetAvailableTemplatesAsync( requestArgs.IncludeInstalledModules); - await requestContext.SendResult( + await requestContext.SendResultAsync( new GetProjectTemplatesResponse { Templates = availableTemplates @@ -468,7 +468,7 @@ await requestContext.SendResult( } else { - await requestContext.SendResult( + await requestContext.SendResultAsync( new GetProjectTemplatesResponse { NeedsModuleInstall = true, @@ -477,7 +477,7 @@ await requestContext.SendResult( } } - private async Task HandleExpandAliasRequest( + private async Task HandleExpandAliasRequestAsync( string content, RequestContext requestContext) { @@ -506,13 +506,13 @@ function __Expand-Alias { }"; var psCommand = new PSCommand(); psCommand.AddScript(script); - await this.editorSession.PowerShellContext.ExecuteCommand(psCommand); + await this.editorSession.PowerShellContext.ExecuteCommandAsync(psCommand); psCommand = new PSCommand(); psCommand.AddCommand("__Expand-Alias").AddArgument(content); - var result = await this.editorSession.PowerShellContext.ExecuteCommand(psCommand); + var result = await this.editorSession.PowerShellContext.ExecuteCommandAsync(psCommand); - await requestContext.SendResult(result.First().ToString()); + await requestContext.SendResultAsync(result.First().ToString()); } private async Task HandleGetCommandRequestAsync( @@ -536,9 +536,10 @@ private async Task HandleGetCommandRequestAsync( .AddCommand("Microsoft.PowerShell.Utility\\Sort-Object") .AddParameter("Property", "Name"); } - IEnumerable result = await this.editorSession.PowerShellContext.ExecuteCommand(psCommand); - var commandList = new List(); + IEnumerable result = await this.editorSession.PowerShellContext.ExecuteCommandAsync(psCommand); + + var commandList = new List(); if (result != null) { foreach (dynamic command in result) @@ -554,17 +555,17 @@ private async Task HandleGetCommandRequestAsync( } } - await requestContext.SendResult(commandList); + await requestContext.SendResultAsync(commandList); } - private async Task HandleFindModuleRequest( + private async Task HandleFindModuleRequestAsync( object param, RequestContext requestContext) { var psCommand = new PSCommand(); psCommand.AddScript("Find-Module | Select Name, Description"); - var modules = await editorSession.PowerShellContext.ExecuteCommand(psCommand); + var modules = await editorSession.PowerShellContext.ExecuteCommandAsync(psCommand); var moduleList = new List(); @@ -576,10 +577,10 @@ private async Task HandleFindModuleRequest( } } - await requestContext.SendResult(moduleList); + await requestContext.SendResultAsync(moduleList); } - protected Task HandleDidOpenTextDocumentNotification( + protected Task HandleDidOpenTextDocumentNotificationAsync( DidOpenTextDocumentParams openParams, EventContext eventContext) { @@ -589,7 +590,7 @@ protected Task HandleDidOpenTextDocumentNotification( openParams.TextDocument.Text); // TODO: Get all recently edited files in the workspace - this.RunScriptDiagnostics( + this.RunScriptDiagnosticsAsync( new ScriptFile[] { openedFile }, editorSession, eventContext); @@ -599,7 +600,7 @@ protected Task HandleDidOpenTextDocumentNotification( return Task.FromResult(true); } - protected async Task HandleDidCloseTextDocumentNotification( + protected async Task HandleDidCloseTextDocumentNotificationAsync( DidCloseTextDocumentParams closeParams, EventContext eventContext) { @@ -609,12 +610,12 @@ protected async Task HandleDidCloseTextDocumentNotification( if (fileToClose != null) { editorSession.Workspace.CloseFile(fileToClose); - await ClearMarkers(fileToClose, eventContext); + await ClearMarkersAsync(fileToClose, eventContext); } Logger.Write(LogLevel.Verbose, "Finished closing document."); } - protected async Task HandleDidSaveTextDocumentNotification( + protected async Task HandleDidSaveTextDocumentNotificationAsync( DidSaveTextDocumentParams saveParams, EventContext eventContext) { @@ -626,13 +627,13 @@ protected async Task HandleDidSaveTextDocumentNotification( { if (this.editorSession.RemoteFileManager.IsUnderRemoteTempPath(savedFile.FilePath)) { - await this.editorSession.RemoteFileManager.SaveRemoteFile( + await this.editorSession.RemoteFileManager.SaveRemoteFileAsync( savedFile.FilePath); } } } - protected Task HandleDidChangeTextDocumentNotification( + protected Task HandleDidChangeTextDocumentNotificationAsync( DidChangeTextDocumentParams textChangeParams, EventContext eventContext) { @@ -652,7 +653,7 @@ protected Task HandleDidChangeTextDocumentNotification( } // TODO: Get all recently edited files in the workspace - this.RunScriptDiagnostics( + this.RunScriptDiagnosticsAsync( changedFiles.ToArray(), editorSession, eventContext); @@ -660,7 +661,7 @@ protected Task HandleDidChangeTextDocumentNotification( return Task.FromResult(true); } - protected async Task HandleDidChangeConfigurationNotification( + protected async Task HandleDidChangeConfigurationNotificationAsync( DidChangeConfigurationParams configChangeParams, EventContext eventContext) { @@ -679,7 +680,7 @@ protected async Task HandleDidChangeConfigurationNotification( this.currentSettings.EnableProfileLoading && oldLoadProfiles != this.currentSettings.EnableProfileLoading) { - await this.editorSession.PowerShellContext.LoadHostProfiles(); + await this.editorSession.PowerShellContext.LoadHostProfilesAsync(); this.profilesLoaded = true; } @@ -713,18 +714,18 @@ protected async Task HandleDidChangeConfigurationNotification( { foreach (var scriptFile in editorSession.Workspace.GetOpenedFiles()) { - await ClearMarkers(scriptFile, eventContext); + await ClearMarkersAsync(scriptFile, eventContext); } } - await this.RunScriptDiagnostics( + await this.RunScriptDiagnosticsAsync( this.editorSession.Workspace.GetOpenedFiles(), this.editorSession, eventContext); } } - protected async Task HandleDefinitionRequest( + protected async Task HandleDefinitionRequestAsync( TextDocumentPositionParams textDocumentPosition, RequestContext requestContext) { @@ -744,7 +745,7 @@ protected async Task HandleDefinitionRequest( if (foundSymbol != null) { definition = - await editorSession.LanguageService.GetDefinitionOfSymbol( + await editorSession.LanguageService.GetDefinitionOfSymbolAsync( scriptFile, foundSymbol, editorSession.Workspace); @@ -760,10 +761,10 @@ await editorSession.LanguageService.GetDefinitionOfSymbol( } } - await requestContext.SendResult(definitionLocations.ToArray()); + await requestContext.SendResultAsync(definitionLocations.ToArray()); } - protected async Task HandleReferencesRequest( + protected async Task HandleReferencesRequestAsync( ReferencesParams referencesParams, RequestContext requestContext) { @@ -778,7 +779,7 @@ protected async Task HandleReferencesRequest( referencesParams.Position.Character + 1); FindReferencesResult referencesResult = - await editorSession.LanguageService.FindReferencesOfSymbol( + await editorSession.LanguageService.FindReferencesOfSymbolAsync( foundSymbol, editorSession.Workspace.ExpandScriptReferences(scriptFile), editorSession.Workspace); @@ -799,10 +800,10 @@ await editorSession.LanguageService.FindReferencesOfSymbol( referenceLocations = locations.ToArray(); } - await requestContext.SendResult(referenceLocations); + await requestContext.SendResultAsync(referenceLocations); } - protected async Task HandleCompletionRequest( + protected async Task HandleCompletionRequestAsync( TextDocumentPositionParams textDocumentPositionParams, RequestContext requestContext) { @@ -814,7 +815,7 @@ protected async Task HandleCompletionRequest( textDocumentPositionParams.TextDocument.Uri); CompletionResults completionResults = - await editorSession.LanguageService.GetCompletionsInFile( + await editorSession.LanguageService.GetCompletionsInFileAsync( scriptFile, cursorLine, cursorColumn); @@ -830,10 +831,10 @@ await editorSession.LanguageService.GetCompletionsInFile( } } - await requestContext.SendResult(completionItems); + await requestContext.SendResultAsync(completionItems); } - protected async Task HandleCompletionResolveRequest( + protected async Task HandleCompletionResolveRequestAsync( CompletionItem completionItem, RequestContext requestContext) { @@ -841,24 +842,24 @@ protected async Task HandleCompletionResolveRequest( { // Get the documentation for the function CommandInfo commandInfo = - await CommandHelpers.GetCommandInfo( + await CommandHelpers.GetCommandInfoAsync( completionItem.Label, this.editorSession.PowerShellContext); if (commandInfo != null) { completionItem.Documentation = - await CommandHelpers.GetCommandSynopsis( + await CommandHelpers.GetCommandSynopsisAsync( commandInfo, this.editorSession.PowerShellContext); } } // Send back the updated CompletionItem - await requestContext.SendResult(completionItem); + await requestContext.SendResultAsync(completionItem); } - protected async Task HandleSignatureHelpRequest( + protected async Task HandleSignatureHelpRequestAsync( TextDocumentPositionParams textDocumentPositionParams, RequestContext requestContext) { @@ -867,7 +868,7 @@ protected async Task HandleSignatureHelpRequest( textDocumentPositionParams.TextDocument.Uri); ParameterSetSignatures parameterSets = - await editorSession.LanguageService.FindParameterSetsInFile( + await editorSession.LanguageService.FindParameterSetsInFileAsync( scriptFile, textDocumentPositionParams.Position.Line + 1, textDocumentPositionParams.Position.Character + 1); @@ -896,7 +897,7 @@ await editorSession.LanguageService.FindParameterSetsInFile( } } - await requestContext.SendResult( + await requestContext.SendResultAsync( new SignatureHelp { Signatures = signatures, @@ -905,7 +906,7 @@ await requestContext.SendResult( }); } - protected async Task HandleDocumentHighlightRequest( + protected async Task HandleDocumentHighlightRequestAsync( TextDocumentPositionParams textDocumentPositionParams, RequestContext requestContext) { @@ -935,10 +936,10 @@ protected async Task HandleDocumentHighlightRequest( documentHighlights = highlights.ToArray(); } - await requestContext.SendResult(documentHighlights); + await requestContext.SendResultAsync(documentHighlights); } - protected async Task HandleHoverRequest( + protected async Task HandleHoverRequestAsync( TextDocumentPositionParams textDocumentPositionParams, RequestContext requestContext) { @@ -949,7 +950,7 @@ protected async Task HandleHoverRequest( SymbolDetails symbolDetails = await editorSession .LanguageService - .FindSymbolDetailsAtLocation( + .FindSymbolDetailsAtLocationAsync( scriptFile, textDocumentPositionParams.Position.Line + 1, textDocumentPositionParams.Position.Character + 1); @@ -979,7 +980,7 @@ await editorSession symbolRange = GetRangeFromScriptRegion(symbolDetails.SymbolReference.ScriptRegion); } - await requestContext.SendResult( + await requestContext.SendResultAsync( new Hover { Contents = symbolInfo.ToArray(), @@ -987,7 +988,7 @@ await requestContext.SendResult( }); } - protected async Task HandleDocumentSymbolRequest( + protected async Task HandleDocumentSymbolRequestAsync( DocumentSymbolParams documentSymbolParams, RequestContext requestContext) { @@ -1024,7 +1025,7 @@ protected async Task HandleDocumentSymbolRequest( symbols = symbolAcc.ToArray(); } - await requestContext.SendResult(symbols); + await requestContext.SendResultAsync(symbols); } public static SymbolKind GetSymbolKind(SymbolType symbolType) @@ -1055,7 +1056,7 @@ public static string GetDecoratedSymbolName(SymbolReference symbolReference) return name; } - protected async Task HandleWorkspaceSymbolRequest( + protected async Task HandleWorkspaceSymbolRequestAsync( WorkspaceSymbolParams workspaceSymbolParams, RequestContext requestContext) { @@ -1096,19 +1097,19 @@ protected async Task HandleWorkspaceSymbolRequest( } } - await requestContext.SendResult(symbols.ToArray()); + await requestContext.SendResultAsync(symbols.ToArray()); } - protected async Task HandlePowerShellVersionRequest( + protected async Task HandlePowerShellVersionRequestAsync( object noParams, RequestContext requestContext) { - await requestContext.SendResult( + await requestContext.SendResultAsync( new PowerShellVersion( this.editorSession.PowerShellContext.LocalPowerShellVersion)); } - protected async Task HandleGetPSHostProcessesRequest( + protected async Task HandleGetPSHostProcessesRequestAsync( object noParams, RequestContext requestContext) { @@ -1124,7 +1125,7 @@ protected async Task HandleGetPSHostProcessesRequest( .AddParameter("NE") .AddParameter("Value", processId.ToString()); - var processes = await editorSession.PowerShellContext.ExecuteCommand(psCommand); + var processes = await editorSession.PowerShellContext.ExecuteCommandAsync(psCommand); if (processes != null) { foreach (dynamic p in processes) @@ -1141,10 +1142,10 @@ protected async Task HandleGetPSHostProcessesRequest( } } - await requestContext.SendResult(psHostProcesses.ToArray()); + await requestContext.SendResultAsync(psHostProcesses.ToArray()); } - protected async Task HandleCommentHelpRequest( + protected async Task HandleCommentHelpRequestAsync( CommentHelpRequestParams requestParams, RequestContext requestContext) { @@ -1153,7 +1154,7 @@ protected async Task HandleCommentHelpRequest( ScriptFile scriptFile; if (!this.editorSession.Workspace.TryGetFile(requestParams.DocumentUri, out scriptFile)) { - await requestContext.SendResult(result); + await requestContext.SendResultAsync(result); return; } @@ -1167,7 +1168,7 @@ protected async Task HandleCommentHelpRequest( if (functionDefinitionAst == null) { - await requestContext.SendResult(result); + await requestContext.SendResultAsync(result); return; } @@ -1200,7 +1201,7 @@ protected async Task HandleCommentHelpRequest( if (helpText == null) { - await requestContext.SendResult(result); + await requestContext.SendResultAsync(result); return; } @@ -1214,7 +1215,7 @@ protected async Task HandleCommentHelpRequest( result.Content = result.Content.Skip(1).ToArray(); } - await requestContext.SendResult(result); + await requestContext.SendResultAsync(result); } private bool IsQueryMatch(string query, string symbolName) @@ -1223,7 +1224,7 @@ private bool IsQueryMatch(string query, string symbolName) } // https://microsoft.github.io/language-server-protocol/specification#textDocument_codeAction - protected async Task HandleCodeActionRequest( + protected async Task HandleCodeActionRequestAsync( CodeActionParams codeActionParams, RequestContext requestContext) { @@ -1282,19 +1283,20 @@ protected async Task HandleCodeActionRequest( } } - await requestContext.SendResult(codeActionCommands.ToArray()); + await requestContext.SendResultAsync( + codeActionCommands.ToArray()); } - protected async Task HandleDocumentFormattingRequest( + protected async Task HandleDocumentFormattingRequestAsync( DocumentFormattingParams formattingParams, RequestContext requestContext) { - var result = await Format( + var result = await FormatAsync( formattingParams.TextDocument.Uri, formattingParams.options, null); - await requestContext.SendResult(new TextEdit[1] + await requestContext.SendResultAsync(new TextEdit[1] { new TextEdit { @@ -1304,16 +1306,16 @@ await requestContext.SendResult(new TextEdit[1] }); } - protected async Task HandleDocumentRangeFormattingRequest( + protected async Task HandleDocumentRangeFormattingRequestAsync( DocumentRangeFormattingParams formattingParams, RequestContext requestContext) { - var result = await Format( + var result = await FormatAsync( formattingParams.TextDocument.Uri, formattingParams.Options, formattingParams.Range); - await requestContext.SendResult(new TextEdit[1] + await requestContext.SendResultAsync(new TextEdit[1] { new TextEdit { @@ -1327,10 +1329,10 @@ protected async Task HandleFoldingRangeRequestAsync( FoldingRangeParams foldingParams, RequestContext requestContext) { - await requestContext.SendResult(Fold(foldingParams.TextDocument.Uri)); + await requestContext.SendResultAsync(Fold(foldingParams.TextDocument.Uri)); } - protected Task HandleEvaluateRequest( + protected Task HandleEvaluateRequestAsync( DebugAdapterMessages.EvaluateRequestArguments evaluateParams, RequestContext requestContext) { @@ -1339,7 +1341,7 @@ protected Task HandleEvaluateRequest( // is executing. This important in cases where the pipeline thread // gets blocked by something in the script like a prompt to the user. var executeTask = - this.editorSession.PowerShellContext.ExecuteScriptString( + this.editorSession.PowerShellContext.ExecuteScriptStringAsync( evaluateParams.Expression, writeInputToHost: true, writeOutputToHost: true, @@ -1353,7 +1355,7 @@ protected Task HandleEvaluateRequest( // Return an empty result since the result value is irrelevant // for this request in the LanguageServer return - requestContext.SendResult( + requestContext.SendResultAsync( new DebugAdapterMessages.EvaluateResponseBody { Result = "", @@ -1400,7 +1402,7 @@ private FoldingRange[] Fold(string documentUri) return result.ToArray(); } - private async Task> Format( + private async Task> FormatAsync( string documentUri, FormattingOptions options, Range range) @@ -1435,7 +1437,7 @@ private async Task> Format( } }; - formattedScript = await editorSession.AnalysisService.Format( + formattedScript = await editorSession.AnalysisService.FormatAsync( scriptFile.Contents, pssaSettings, rangeList); @@ -1443,9 +1445,9 @@ private async Task> Format( return Tuple.Create(formattedScript, editRange); } - private async void PowerShellContext_RunspaceChanged(object sender, Session.RunspaceChangedEventArgs e) + private async void PowerShellContext_RunspaceChangedAsync(object sender, Session.RunspaceChangedEventArgs e) { - await this.messageSender.SendEvent( + await this.messageSender.SendEventAsync( RunspaceChangedEvent.Type, new Protocol.LanguageServer.RunspaceDetails(e.NewRunspace)); } @@ -1455,16 +1457,16 @@ await this.messageSender.SendEvent( /// /// the PowerShell context sending the execution event /// details of the execution status change - private async void PowerShellContext_ExecutionStatusChanged(object sender, ExecutionStatusChangedEventArgs e) + private async void PowerShellContext_ExecutionStatusChangedAsync(object sender, ExecutionStatusChangedEventArgs e) { - await this.messageSender.SendEvent( + await this.messageSender.SendEventAsync( ExecutionStatusChangedEvent.Type, e); } - private async void ExtensionService_ExtensionAdded(object sender, EditorCommand e) + private async void ExtensionService_ExtensionAddedAsync(object sender, EditorCommand e) { - await this.messageSender.SendEvent( + await this.messageSender.SendEventAsync( ExtensionCommandAddedNotification.Type, new ExtensionCommandAddedNotification { @@ -1473,9 +1475,9 @@ await this.messageSender.SendEvent( }); } - private async void ExtensionService_ExtensionUpdated(object sender, EditorCommand e) + private async void ExtensionService_ExtensionUpdatedAsync(object sender, EditorCommand e) { - await this.messageSender.SendEvent( + await this.messageSender.SendEventAsync( ExtensionCommandUpdatedNotification.Type, new ExtensionCommandUpdatedNotification { @@ -1483,9 +1485,9 @@ await this.messageSender.SendEvent( }); } - private async void ExtensionService_ExtensionRemoved(object sender, EditorCommand e) + private async void ExtensionService_ExtensionRemovedAsync(object sender, EditorCommand e) { - await this.messageSender.SendEvent( + await this.messageSender.SendEventAsync( ExtensionCommandRemovedNotification.Type, new ExtensionCommandRemovedNotification { @@ -1493,11 +1495,11 @@ await this.messageSender.SendEvent( }); } - private async void DebugService_DebuggerStopped(object sender, DebuggerStoppedEventArgs e) + private async void DebugService_DebuggerStoppedAsync(object sender, DebuggerStoppedEventArgs e) { if (!this.editorSession.DebugService.IsClientAttached) { - await this.messageSender.SendEvent( + await this.messageSender.SendEventAsync( StartDebuggerEvent.Type, new StartDebuggerEvent()); } @@ -1550,15 +1552,15 @@ private static FileChange GetFileChangeDetails(Range changeRange, string insertS }; } - private Task RunScriptDiagnostics( + private Task RunScriptDiagnosticsAsync( ScriptFile[] filesToAnalyze, EditorSession editorSession, EventContext eventContext) { - return RunScriptDiagnostics(filesToAnalyze, editorSession, this.messageSender.SendEvent); + return RunScriptDiagnosticsAsync(filesToAnalyze, editorSession, this.messageSender.SendEventAsync); } - private Task RunScriptDiagnostics( + private Task RunScriptDiagnosticsAsync( ScriptFile[] filesToAnalyze, EditorSession editorSession, Func, PublishDiagnosticsNotification, Task> eventSender) @@ -1604,7 +1606,7 @@ private Task RunScriptDiagnostics( s_existingRequestCancellation = new CancellationTokenSource(); Task.Factory.StartNew( () => - DelayThenInvokeDiagnostics( + DelayThenInvokeDiagnosticsAsync( 750, filesToAnalyze, this.currentSettings.ScriptAnalysis?.Enable.Value ?? false, @@ -1620,7 +1622,29 @@ private Task RunScriptDiagnostics( return Task.FromResult(true); } - private static async Task DelayThenInvokeDiagnostics( + private static async Task DelayThenInvokeDiagnosticsAsync( + int delayMilliseconds, + ScriptFile[] filesToAnalyze, + bool isScriptAnalysisEnabled, + Dictionary> correctionIndex, + EditorSession editorSession, + EventContext eventContext, + ILogger Logger, + CancellationToken cancellationToken) + { + await DelayThenInvokeDiagnosticsAsync( + delayMilliseconds, + filesToAnalyze, + isScriptAnalysisEnabled, + correctionIndex, + editorSession, + eventContext.SendEventAsync, + Logger, + cancellationToken); + } + + + private static async Task DelayThenInvokeDiagnosticsAsync( int delayMilliseconds, ScriptFile[] filesToAnalyze, bool isScriptAnalysisEnabled, @@ -1641,7 +1665,7 @@ private static async Task DelayThenInvokeDiagnostics( // If the task is cancelled, exit directly foreach (var script in filesToAnalyze) { - await PublishScriptDiagnostics( + await PublishScriptDiagnosticsAsync( script, script.SyntaxMarkers, correctionIndex, @@ -1676,7 +1700,7 @@ await PublishScriptDiagnostics( semanticMarkers = new ScriptFileMarker[0]; } - await PublishScriptDiagnostics( + await PublishScriptDiagnosticsAsync( scriptFile, // Concat script analysis errors to any existing parse errors scriptFile.SyntaxMarkers.Concat(semanticMarkers).ToArray(), @@ -1685,30 +1709,30 @@ await PublishScriptDiagnostics( } } - private async Task ClearMarkers(ScriptFile scriptFile, EventContext eventContext) + private async Task ClearMarkersAsync(ScriptFile scriptFile, EventContext eventContext) { // send empty diagnostic markers to clear any markers associated with the given file - await PublishScriptDiagnostics( + await PublishScriptDiagnosticsAsync( scriptFile, new ScriptFileMarker[0], this.codeActionsPerFile, eventContext); } - private static async Task PublishScriptDiagnostics( + private static async Task PublishScriptDiagnosticsAsync( ScriptFile scriptFile, ScriptFileMarker[] markers, Dictionary> correctionIndex, EventContext eventContext) { - await PublishScriptDiagnostics( + await PublishScriptDiagnosticsAsync( scriptFile, markers, correctionIndex, - eventContext.SendEvent); + eventContext.SendEventAsync); } - private static async Task PublishScriptDiagnostics( + private static async Task PublishScriptDiagnosticsAsync( ScriptFile scriptFile, ScriptFileMarker[] markers, Dictionary> correctionIndex, diff --git a/src/PowerShellEditorServices.Protocol/Server/LanguageServerEditorOperations.cs b/src/PowerShellEditorServices.Protocol/Server/LanguageServerEditorOperations.cs index 8639f5ee3..5df247bd5 100644 --- a/src/PowerShellEditorServices.Protocol/Server/LanguageServerEditorOperations.cs +++ b/src/PowerShellEditorServices.Protocol/Server/LanguageServerEditorOperations.cs @@ -26,10 +26,10 @@ public LanguageServerEditorOperations( this.messageSender = messageSender; } - public async Task GetEditorContext() + public async Task GetEditorContextAsync() { ClientEditorContext clientContext = - await this.messageSender.SendRequest( + await this.messageSender.SendRequestAsync( GetEditorContextRequest.Type, new GetEditorContextRequest(), true); @@ -37,9 +37,9 @@ await this.messageSender.SendRequest( return this.ConvertClientEditorContext(clientContext); } - public async Task InsertText(string filePath, string text, BufferRange insertRange) + public async Task InsertTextAsync(string filePath, string text, BufferRange insertRange) { - await this.messageSender.SendRequest( + await this.messageSender.SendRequestAsync( InsertTextRequest.Type, new InsertTextRequest { @@ -64,9 +64,9 @@ await this.messageSender.SendRequest( // TODO: Set the last param back to true! } - public Task SetSelection(BufferRange selectionRange) + public Task SetSelectionAsync(BufferRange selectionRange) { - return this.messageSender.SendRequest( + return this.messageSender.SendRequestAsync( SetSelectionRequest.Type, new SetSelectionRequest { @@ -109,19 +109,19 @@ public EditorContext ConvertClientEditorContext( clientContext.CurrentFileLanguage); } - public Task NewFile() + public Task NewFileAsync() { return - this.messageSender.SendRequest( + this.messageSender.SendRequestAsync( NewFileRequest.Type, null, true); } - public Task OpenFile(string filePath) + public Task OpenFileAsync(string filePath) { return - this.messageSender.SendRequest( + this.messageSender.SendRequestAsync( OpenFileRequest.Type, new OpenFileDetails { @@ -131,10 +131,10 @@ public Task OpenFile(string filePath) true); } - public Task OpenFile(string filePath, bool preview) + public Task OpenFileAsync(string filePath, bool preview) { return - this.messageSender.SendRequest( + this.messageSender.SendRequestAsync( OpenFileRequest.Type, new OpenFileDetails { @@ -144,24 +144,24 @@ public Task OpenFile(string filePath, bool preview) true); } - public Task CloseFile(string filePath) + public Task CloseFileAsync(string filePath) { return - this.messageSender.SendRequest( + this.messageSender.SendRequestAsync( CloseFileRequest.Type, filePath, true); } - public Task SaveFile(string filePath) + public Task SaveFileAsync(string filePath) { - return SaveFile(filePath, null); + return SaveFileAsync(filePath, null); } - public Task SaveFile(string currentPath, string newSavePath) + public Task SaveFileAsync(string currentPath, string newSavePath) { return - this.messageSender.SendRequest( + this.messageSender.SendRequestAsync( SaveFileRequest.Type, new SaveFileDetails { @@ -181,37 +181,37 @@ public string GetWorkspaceRelativePath(string filePath) return this.editorSession.Workspace.GetRelativePath(filePath); } - public Task ShowInformationMessage(string message) + public Task ShowInformationMessageAsync(string message) { return - this.messageSender.SendRequest( + this.messageSender.SendRequestAsync( ShowInformationMessageRequest.Type, message, true); } - public Task ShowErrorMessage(string message) + public Task ShowErrorMessageAsync(string message) { return - this.messageSender.SendRequest( + this.messageSender.SendRequestAsync( ShowErrorMessageRequest.Type, message, true); } - public Task ShowWarningMessage(string message) + public Task ShowWarningMessageAsync(string message) { return - this.messageSender.SendRequest( + this.messageSender.SendRequestAsync( ShowWarningMessageRequest.Type, message, true); } - public Task SetStatusBarMessage(string message, int? timeout) + public Task SetStatusBarMessageAsync(string message, int? timeout) { return - this.messageSender.SendRequest( + this.messageSender.SendRequestAsync( SetStatusBarMessageRequest.Type, new StatusBarMessageDetails { diff --git a/src/PowerShellEditorServices.Protocol/Server/OutputDebouncer.cs b/src/PowerShellEditorServices.Protocol/Server/OutputDebouncer.cs index 20866655a..079183a26 100644 --- a/src/PowerShellEditorServices.Protocol/Server/OutputDebouncer.cs +++ b/src/PowerShellEditorServices.Protocol/Server/OutputDebouncer.cs @@ -47,7 +47,7 @@ public OutputDebouncer(IMessageSender messageSender) #region Private Methods - protected override async Task OnInvoke(OutputWrittenEventArgs output) + protected override async Task OnInvokeAsync(OutputWrittenEventArgs output) { bool outputIsError = output.OutputType == OutputType.Error; @@ -56,7 +56,7 @@ protected override async Task OnInvoke(OutputWrittenEventArgs output) if (this.currentOutputString != null) { // Flush the output - await this.OnFlush(); + await this.OnFlushAsync(); } this.currentOutputString = string.Empty; @@ -77,13 +77,13 @@ protected override async Task OnInvoke(OutputWrittenEventArgs output) string.Empty); } - protected override async Task OnFlush() + protected override async Task OnFlushAsync() { // Only flush output if there is some to flush if (this.currentOutputString != null) { // Send an event for the current output - await this.messageSender.SendEvent( + await this.messageSender.SendEventAsync( OutputEvent.Type, new OutputEventBody { diff --git a/src/PowerShellEditorServices.VSCode/CustomViews/CustomViewBase.cs b/src/PowerShellEditorServices.VSCode/CustomViews/CustomViewBase.cs index f212ee968..1fdb6ddf0 100644 --- a/src/PowerShellEditorServices.VSCode/CustomViews/CustomViewBase.cs +++ b/src/PowerShellEditorServices.VSCode/CustomViews/CustomViewBase.cs @@ -34,10 +34,10 @@ public CustomViewBase( this.logger = logger; } - internal Task Create() + internal Task CreateAsync() { return - this.messageSender.SendRequest( + this.messageSender.SendRequestAsync( NewCustomViewRequest.Type, new NewCustomViewRequest { @@ -50,7 +50,7 @@ internal Task Create() public Task Show(ViewColumn viewColumn) { return - this.messageSender.SendRequest( + this.messageSender.SendRequestAsync( ShowCustomViewRequest.Type, new ShowCustomViewRequest { @@ -62,7 +62,7 @@ public Task Show(ViewColumn viewColumn) public Task Close() { return - this.messageSender.SendRequest( + this.messageSender.SendRequestAsync( CloseCustomViewRequest.Type, new CloseCustomViewRequest { diff --git a/src/PowerShellEditorServices.VSCode/CustomViews/HtmlContentView.cs b/src/PowerShellEditorServices.VSCode/CustomViews/HtmlContentView.cs index 1ab72e04b..c7a553707 100644 --- a/src/PowerShellEditorServices.VSCode/CustomViews/HtmlContentView.cs +++ b/src/PowerShellEditorServices.VSCode/CustomViews/HtmlContentView.cs @@ -26,10 +26,10 @@ public HtmlContentView( { } - public Task SetContent(string htmlBodyContent) + public Task SetContentAsync(string htmlBodyContent) { return - this.messageSender.SendRequest( + this.messageSender.SendRequestAsync( SetHtmlContentViewRequest.Type, new SetHtmlContentViewRequest { @@ -38,7 +38,7 @@ public Task SetContent(string htmlBodyContent) }, true); } - public Task SetContent(HtmlContent htmlContent) + public Task SetContentAsync(HtmlContent htmlContent) { HtmlContent validatedContent = new HtmlContent() @@ -49,7 +49,7 @@ public Task SetContent(HtmlContent htmlContent) }; return - this.messageSender.SendRequest( + this.messageSender.SendRequestAsync( SetHtmlContentViewRequest.Type, new SetHtmlContentViewRequest { @@ -58,10 +58,10 @@ public Task SetContent(HtmlContent htmlContent) }, true); } - public Task AppendContent(string appendedHtmlBodyContent) + public Task AppendContentAsync(string appendedHtmlBodyContent) { return - this.messageSender.SendRequest( + this.messageSender.SendRequestAsync( AppendHtmlContentViewRequest.Type, new AppendHtmlContentViewRequest { diff --git a/src/PowerShellEditorServices.VSCode/CustomViews/HtmlContentViewsFeature.cs b/src/PowerShellEditorServices.VSCode/CustomViews/HtmlContentViewsFeature.cs index 4ae093c2e..a520fa610 100644 --- a/src/PowerShellEditorServices.VSCode/CustomViews/HtmlContentViewsFeature.cs +++ b/src/PowerShellEditorServices.VSCode/CustomViews/HtmlContentViewsFeature.cs @@ -19,7 +19,7 @@ public HtmlContentViewsFeature( { } - public async Task CreateHtmlContentView(string viewTitle) + public async Task CreateHtmlContentViewAsync(string viewTitle) { HtmlContentView htmlView = new HtmlContentView( @@ -27,7 +27,7 @@ public async Task CreateHtmlContentView(string viewTitle) this.messageSender, this.logger); - await htmlView.Create(); + await htmlView.CreateAsync(); this.AddView(htmlView); return htmlView; diff --git a/src/PowerShellEditorServices.VSCode/CustomViews/IHtmlContentView.cs b/src/PowerShellEditorServices.VSCode/CustomViews/IHtmlContentView.cs index 0d58f2719..c12bc531b 100644 --- a/src/PowerShellEditorServices.VSCode/CustomViews/IHtmlContentView.cs +++ b/src/PowerShellEditorServices.VSCode/CustomViews/IHtmlContentView.cs @@ -21,7 +21,7 @@ public interface IHtmlContentView : ICustomView /// The HTML content that is placed inside of the page's body tag. /// /// A Task which can be awaited for completion. - Task SetContent(string htmlBodyContent); + Task SetContentAsync(string htmlBodyContent); /// /// Sets the HTML content of the view. @@ -30,7 +30,7 @@ public interface IHtmlContentView : ICustomView /// The HTML content that is placed inside of the page's body tag. /// /// A Task which can be awaited for completion. - Task SetContent(HtmlContent htmlContent); + Task SetContentAsync(HtmlContent htmlContent); /// /// Appends HTML body content to the view. @@ -39,6 +39,6 @@ public interface IHtmlContentView : ICustomView /// The HTML fragment to be appended to the output stream. /// /// A Task which can be awaited for completion. - Task AppendContent(string appendedHtmlBodyContent); + Task AppendContentAsync(string appendedHtmlBodyContent); } } diff --git a/src/PowerShellEditorServices.VSCode/CustomViews/IHtmlContentViews.cs b/src/PowerShellEditorServices.VSCode/CustomViews/IHtmlContentViews.cs index 5b42b4540..396ddae4c 100644 --- a/src/PowerShellEditorServices.VSCode/CustomViews/IHtmlContentViews.cs +++ b/src/PowerShellEditorServices.VSCode/CustomViews/IHtmlContentViews.cs @@ -21,6 +21,6 @@ public interface IHtmlContentViews /// /// A Task to await for completion, returns the IHtmlContentView instance. /// - Task CreateHtmlContentView(string viewTitle); + Task CreateHtmlContentViewAsync(string viewTitle); } } diff --git a/src/PowerShellEditorServices/Analysis/AnalysisService.cs b/src/PowerShellEditorServices/Analysis/AnalysisService.cs index e089cd87b..d87226377 100644 --- a/src/PowerShellEditorServices/Analysis/AnalysisService.cs +++ b/src/PowerShellEditorServices/Analysis/AnalysisService.cs @@ -323,7 +323,7 @@ public IEnumerable GetPSScriptAnalyzerRules() /// ScriptAnalyzer settings /// The range within which formatting should be applied. /// The formatted script text. - public async Task Format( + public async Task FormatAsync( string scriptDefinition, Hashtable settings, int[] rangeList) diff --git a/src/PowerShellEditorServices/Console/ChoicePromptHandler.cs b/src/PowerShellEditorServices/Console/ChoicePromptHandler.cs index 3511d4483..579d5b708 100644 --- a/src/PowerShellEditorServices/Console/ChoicePromptHandler.cs +++ b/src/PowerShellEditorServices/Console/ChoicePromptHandler.cs @@ -110,7 +110,7 @@ public ChoicePromptHandler(ILogger logger) : base(logger) /// A Task instance that can be monitored for completion to get /// the user's choice. /// - public async Task PromptForChoice( + public async Task PromptForChoiceAsync( string promptCaption, string promptMessage, ChoiceDetails[] choices, @@ -132,8 +132,8 @@ public async Task PromptForChoice( cancellationToken.Register(this.CancelPrompt, true); // Convert the int[] result to int - return await this.WaitForTask( - this.StartPromptLoop(this.promptCancellationTokenSource.Token) + return await this.WaitForTaskAsync( + this.StartPromptLoopAsync(this.promptCancellationTokenSource.Token) .ContinueWith( task => { @@ -173,7 +173,7 @@ public async Task PromptForChoice( /// A Task instance that can be monitored for completion to get /// the user's choices. /// - public async Task PromptForChoice( + public async Task PromptForChoiceAsync( string promptCaption, string promptMessage, ChoiceDetails[] choices, @@ -191,12 +191,12 @@ public async Task PromptForChoice( // Cancel the TaskCompletionSource if the caller cancels the task cancellationToken.Register(this.CancelPrompt, true); - return await this.WaitForTask( - this.StartPromptLoop( + return await this.WaitForTaskAsync( + this.StartPromptLoopAsync( this.promptCancellationTokenSource.Token)); } - private async Task WaitForTask(Task taskToWait) + private async Task WaitForTaskAsync(Task taskToWait) { Task finishedTask = await Task.WhenAny( @@ -211,7 +211,7 @@ await Task.WhenAny( return taskToWait.Result; } - private async Task StartPromptLoop( + private async Task StartPromptLoopAsync( CancellationToken cancellationToken) { int[] choiceIndexes = null; @@ -221,7 +221,7 @@ private async Task StartPromptLoop( while (!cancellationToken.IsCancellationRequested) { - string responseString = await this.ReadInputString(cancellationToken); + string responseString = await this.ReadInputStringAsync(cancellationToken); if (responseString == null) { // If the response string is null, the prompt has been cancelled @@ -334,7 +334,7 @@ protected override void OnPromptCancelled() /// A Task instance that can be monitored for completion to get /// the user's input. /// - protected abstract Task ReadInputString(CancellationToken cancellationToken); + protected abstract Task ReadInputStringAsync(CancellationToken cancellationToken); #endregion diff --git a/src/PowerShellEditorServices/Console/ConsoleReadLine.cs b/src/PowerShellEditorServices/Console/ConsoleReadLine.cs index afe5c6b8f..e2824d635 100644 --- a/src/PowerShellEditorServices/Console/ConsoleReadLine.cs +++ b/src/PowerShellEditorServices/Console/ConsoleReadLine.cs @@ -34,17 +34,17 @@ public ConsoleReadLine(PowerShellContext powerShellContext) #region Public Methods - public Task ReadCommandLine(CancellationToken cancellationToken) + public Task ReadCommandLineAsync(CancellationToken cancellationToken) { return this.ReadLineAsync(true, cancellationToken); } - public Task ReadSimpleLine(CancellationToken cancellationToken) + public Task ReadSimpleLineAsync(CancellationToken cancellationToken) { return this.ReadLineAsync(false, cancellationToken); } - public async Task ReadSecureLine(CancellationToken cancellationToken) + public async Task ReadSecureLineAsync(CancellationToken cancellationToken) { SecureString secureString = new SecureString(); @@ -208,13 +208,13 @@ internal async Task InvokeLegacyReadLineAsync(bool isCommandLine, Cancel command.AddParameter("Options", null); var results = - await this.powerShellContext.ExecuteCommand(command, false, false); + await this.powerShellContext.ExecuteCommandAsync(command, false, false); currentCompletion = results.FirstOrDefault(); } else { - using (RunspaceHandle runspaceHandle = await this.powerShellContext.GetRunspaceHandle()) + using (RunspaceHandle runspaceHandle = await this.powerShellContext.GetRunspaceHandleAsync()) using (PowerShell powerShell = PowerShell.Create()) { powerShell.Runspace = runspaceHandle.Runspace; @@ -326,7 +326,7 @@ internal async Task InvokeLegacyReadLineAsync(bool isCommandLine, Cancel command.AddCommand("Get-History"); currentHistory = - await this.powerShellContext.ExecuteCommand( + await this.powerShellContext.ExecuteCommandAsync( command, false, false) as Collection; diff --git a/src/PowerShellEditorServices/Console/InputPromptHandler.cs b/src/PowerShellEditorServices/Console/InputPromptHandler.cs index 0b8e4ff22..7d626c9ab 100644 --- a/src/PowerShellEditorServices/Console/InputPromptHandler.cs +++ b/src/PowerShellEditorServices/Console/InputPromptHandler.cs @@ -58,11 +58,11 @@ public InputPromptHandler(ILogger logger) : base(logger) /// A Task instance that can be monitored for completion to get /// the user's input. /// - public Task PromptForInput( + public Task PromptForInputAsync( CancellationToken cancellationToken) { Task> innerTask = - this.PromptForInput( + this.PromptForInputAsync( null, null, new FieldDetails[] { new FieldDetails("", "", typeof(string), false, "") }, @@ -106,7 +106,7 @@ public Task PromptForInput( /// A Task instance that can be monitored for completion to get /// the user's input. /// - public async Task> PromptForInput( + public async Task> PromptForInputAsync( string promptCaption, string promptMessage, FieldDetails[] fields, @@ -120,7 +120,7 @@ public async Task> PromptForInput( this.ShowPromptMessage(promptCaption, promptMessage); Task> promptTask = - this.StartPromptLoop(this.promptCancellationTokenSource.Token); + this.StartPromptLoopAsync(this.promptCancellationTokenSource.Token); Task finishedTask = await Task.WhenAny( @@ -142,11 +142,11 @@ await Task.WhenAny( /// A Task instance that can be monitored for completion to get /// the user's input. /// - public Task PromptForSecureInput( + public Task PromptForSecureInputAsync( CancellationToken cancellationToken) { Task> innerTask = - this.PromptForInput( + this.PromptForInputAsync( null, null, new FieldDetails[] { new FieldDetails("", "", typeof(SecureString), false, "") }, @@ -209,7 +209,7 @@ protected override void OnPromptCancelled() /// A Task instance that can be monitored for completion to get /// the user's input. /// - protected abstract Task ReadInputString(CancellationToken cancellationToken); + protected abstract Task ReadInputStringAsync(CancellationToken cancellationToken); /// /// Reads a SecureString asynchronously from the console. @@ -221,7 +221,7 @@ protected override void OnPromptCancelled() /// A Task instance that can be monitored for completion to get /// the user's input. /// - protected abstract Task ReadSecureString(CancellationToken cancellationToken); + protected abstract Task ReadSecureStringAsync(CancellationToken cancellationToken); /// /// Called when an error should be displayed, such as when the @@ -237,7 +237,7 @@ protected override void OnPromptCancelled() #region Private Methods - private async Task> StartPromptLoop( + private async Task> StartPromptLoopAsync( CancellationToken cancellationToken) { this.GetNextField(); @@ -255,13 +255,13 @@ private async Task> StartPromptLoop( // Read input depending on field type if (this.currentField.FieldType == typeof(SecureString)) { - SecureString secureString = await this.ReadSecureString(cancellationToken); + SecureString secureString = await this.ReadSecureStringAsync(cancellationToken); responseValue = secureString; enteredValue = secureString != null; } else { - responseString = await this.ReadInputString(cancellationToken); + responseString = await this.ReadInputStringAsync(cancellationToken); responseValue = responseString; enteredValue = responseString != null && responseString.Length > 0; diff --git a/src/PowerShellEditorServices/Console/TerminalChoicePromptHandler.cs b/src/PowerShellEditorServices/Console/TerminalChoicePromptHandler.cs index b99b959cd..1bf5a5cc4 100644 --- a/src/PowerShellEditorServices/Console/TerminalChoicePromptHandler.cs +++ b/src/PowerShellEditorServices/Console/TerminalChoicePromptHandler.cs @@ -52,9 +52,9 @@ public TerminalChoicePromptHandler( /// /// A CancellationToken that can be used to cancel the prompt. /// A Task that can be awaited to get the user's response. - protected override async Task ReadInputString(CancellationToken cancellationToken) + protected override async Task ReadInputStringAsync(CancellationToken cancellationToken) { - string inputString = await this.consoleReadLine.ReadSimpleLine(cancellationToken); + string inputString = await this.consoleReadLine.ReadSimpleLineAsync(cancellationToken); this.hostOutput.WriteOutput(string.Empty); return inputString; diff --git a/src/PowerShellEditorServices/Console/TerminalInputPromptHandler.cs b/src/PowerShellEditorServices/Console/TerminalInputPromptHandler.cs index e77bf2f9a..67b58bc55 100644 --- a/src/PowerShellEditorServices/Console/TerminalInputPromptHandler.cs +++ b/src/PowerShellEditorServices/Console/TerminalInputPromptHandler.cs @@ -54,9 +54,9 @@ public TerminalInputPromptHandler( /// /// A CancellationToken that can be used to cancel the prompt. /// A Task that can be awaited to get the user's response. - protected override async Task ReadInputString(CancellationToken cancellationToken) + protected override async Task ReadInputStringAsync(CancellationToken cancellationToken) { - string inputString = await this.consoleReadLine.ReadSimpleLine(cancellationToken); + string inputString = await this.consoleReadLine.ReadSimpleLineAsync(cancellationToken); this.hostOutput.WriteOutput(string.Empty); return inputString; @@ -67,9 +67,9 @@ protected override async Task ReadInputString(CancellationToken cancella /// /// A CancellationToken that can be used to cancel the prompt. /// A Task that can be awaited to get the user's response. - protected override async Task ReadSecureString(CancellationToken cancellationToken) + protected override async Task ReadSecureStringAsync(CancellationToken cancellationToken) { - SecureString secureString = await this.consoleReadLine.ReadSecureLine(cancellationToken); + SecureString secureString = await this.consoleReadLine.ReadSecureLineAsync(cancellationToken); this.hostOutput.WriteOutput(string.Empty); return secureString; diff --git a/src/PowerShellEditorServices/Debugging/DebugService.cs b/src/PowerShellEditorServices/Debugging/DebugService.cs index 8226ab9be..98d47d7af 100644 --- a/src/PowerShellEditorServices/Debugging/DebugService.cs +++ b/src/PowerShellEditorServices/Debugging/DebugService.cs @@ -108,7 +108,7 @@ public DebugService( this.logger = logger; this.powerShellContext = powerShellContext; - this.powerShellContext.DebuggerStop += this.OnDebuggerStop; + this.powerShellContext.DebuggerStop += this.OnDebuggerStopAsync; this.powerShellContext.DebuggerResumed += this.OnDebuggerResumed; this.powerShellContext.BreakpointUpdated += this.OnBreakpointUpdated; @@ -133,7 +133,7 @@ public DebugService( /// BreakpointDetails for each breakpoint that will be set. /// If true, causes all existing breakpoints to be cleared before setting new ones. /// An awaitable Task that will provide details about the breakpoints that were set. - public async Task SetLineBreakpoints( + public async Task SetLineBreakpointsAsync( ScriptFile scriptFile, BreakpointDetails[] breakpoints, bool clearExisting = true) @@ -186,7 +186,7 @@ public async Task SetLineBreakpoints( { if (clearExisting) { - await this.ClearBreakpointsInFile(scriptFile); + await this.ClearBreakpointsInFileAsync(scriptFile); } foreach (BreakpointDetails breakpoint in breakpoints) @@ -224,7 +224,7 @@ public async Task SetLineBreakpoints( } IEnumerable configuredBreakpoints = - await this.powerShellContext.ExecuteCommand(psCommand); + await this.powerShellContext.ExecuteCommandAsync(psCommand); // The order in which the breakpoints are returned is significant to the // VSCode client and should match the order in which they are passed in. @@ -235,7 +235,7 @@ public async Task SetLineBreakpoints( else { resultBreakpointDetails = - await dscBreakpoints.SetLineBreakpoints( + await dscBreakpoints.SetLineBreakpointsAsync( this.powerShellContext, escapedScriptPath, breakpoints); @@ -250,7 +250,7 @@ await dscBreakpoints.SetLineBreakpoints( /// CommandBreakpointDetails for each command breakpoint that will be set. /// If true, causes all existing function breakpoints to be cleared before setting new ones. /// An awaitable Task that will provide details about the breakpoints that were set. - public async Task SetCommandBreakpoints( + public async Task SetCommandBreakpointsAsync( CommandBreakpointDetails[] breakpoints, bool clearExisting = true) { @@ -258,7 +258,7 @@ public async Task SetCommandBreakpoints( if (clearExisting) { - await this.ClearCommandBreakpoints(); + await this.ClearCommandBreakpointsAsync(); } if (breakpoints.Length > 0) @@ -287,7 +287,7 @@ public async Task SetCommandBreakpoints( } IEnumerable configuredBreakpoints = - await this.powerShellContext.ExecuteCommand(psCommand); + await this.powerShellContext.ExecuteCommandAsync(psCommand); // The order in which the breakpoints are returned is significant to the // VSCode client and should match the order in which they are passed in. @@ -470,7 +470,7 @@ public VariableDetailsBase GetVariableFromExpression(string variableExpression, /// The new string value. This value must not be null. If you want to set the variable to $null /// pass in the string "$null". /// The string representation of the value the variable was set to. - public async Task SetVariable(int variableContainerReferenceId, string name, string value) + public async Task SetVariableAsync(int variableContainerReferenceId, string name, string value) { Validate.IsNotNull(nameof(name), name); Validate.IsNotNull(nameof(value), value); @@ -488,7 +488,7 @@ public async Task SetVariable(int variableContainerReferenceId, string n psCommand.AddScript(value); var errorMessages = new StringBuilder(); var results = - await this.powerShellContext.ExecuteCommand( + await this.powerShellContext.ExecuteCommandAsync( psCommand, errorMessages, false, @@ -561,7 +561,7 @@ await this.powerShellContext.ExecuteCommand( psCommand.AddParameter("Name", name.TrimStart('$')); psCommand.AddParameter("Scope", scope); - IEnumerable result = await this.powerShellContext.ExecuteCommand(psCommand, sendErrorToHost: false); + IEnumerable result = await this.powerShellContext.ExecuteCommandAsync(psCommand, sendErrorToHost: false); PSVariable psVariable = result.FirstOrDefault(); if (psVariable == null) { @@ -590,7 +590,7 @@ await this.powerShellContext.ExecuteCommand( errorMessages.Clear(); var getExecContextResults = - await this.powerShellContext.ExecuteCommand( + await this.powerShellContext.ExecuteCommandAsync( psCommand, errorMessages, sendErrorToHost: false); @@ -628,13 +628,13 @@ await this.powerShellContext.ExecuteCommand( /// If true, writes the expression result as host output rather than returning the results. /// In this case, the return value of this function will be null. /// A VariableDetails object containing the result. - public async Task EvaluateExpression( + public async Task EvaluateExpressionAsync( string expressionString, int stackFrameId, bool writeResultAsOutput) { var results = - await this.powerShellContext.ExecuteScriptString( + await this.powerShellContext.ExecuteScriptStringAsync( expressionString, false, writeResultAsOutput); @@ -741,20 +741,20 @@ public VariableScope[] GetVariableScopes(int stackFrameId) /// /// Clears all breakpoints in the current session. /// - public async Task ClearAllBreakpoints() + public async Task ClearAllBreakpointsAsync() { PSCommand psCommand = new PSCommand(); psCommand.AddCommand(@"Microsoft.PowerShell.Utility\Get-PSBreakpoint"); psCommand.AddCommand(@"Microsoft.PowerShell.Utility\Remove-PSBreakpoint"); - await this.powerShellContext.ExecuteCommand(psCommand); + await this.powerShellContext.ExecuteCommandAsync(psCommand); } #endregion #region Private Methods - private async Task ClearBreakpointsInFile(ScriptFile scriptFile) + private async Task ClearBreakpointsInFileAsync(ScriptFile scriptFile) { List breakpoints = null; @@ -767,7 +767,7 @@ private async Task ClearBreakpointsInFile(ScriptFile scriptFile) psCommand.AddCommand(@"Microsoft.PowerShell.Utility\Remove-PSBreakpoint"); psCommand.AddParameter("Id", breakpoints.Select(b => b.Id).ToArray()); - await this.powerShellContext.ExecuteCommand(psCommand); + await this.powerShellContext.ExecuteCommandAsync(psCommand); // Clear the existing breakpoints list for the file breakpoints.Clear(); @@ -775,17 +775,17 @@ private async Task ClearBreakpointsInFile(ScriptFile scriptFile) } } - private async Task ClearCommandBreakpoints() + private async Task ClearCommandBreakpointsAsync() { PSCommand psCommand = new PSCommand(); psCommand.AddCommand(@"Microsoft.PowerShell.Utility\Get-PSBreakpoint"); psCommand.AddParameter("Type", "Command"); psCommand.AddCommand(@"Microsoft.PowerShell.Utility\Remove-PSBreakpoint"); - await this.powerShellContext.ExecuteCommand(psCommand); + await this.powerShellContext.ExecuteCommandAsync(psCommand); } - private async Task FetchStackFramesAndVariables(string scriptNameOverride) + private async Task FetchStackFramesAndVariablesAsync(string scriptNameOverride) { await this.debugInfoHandle.WaitAsync(); try @@ -798,8 +798,8 @@ private async Task FetchStackFramesAndVariables(string scriptNameOverride) // Must retrieve global/script variales before stack frame variables // as we check stack frame variables against globals. - await FetchGlobalAndScriptVariables(); - await FetchStackFrames(scriptNameOverride); + await FetchGlobalAndScriptVariablesAsync(); + await FetchStackFramesAsync(scriptNameOverride); } finally { @@ -807,17 +807,17 @@ private async Task FetchStackFramesAndVariables(string scriptNameOverride) } } - private async Task FetchGlobalAndScriptVariables() + private async Task FetchGlobalAndScriptVariablesAsync() { // Retrieve globals first as script variable retrieval needs to search globals. this.globalScopeVariables = - await FetchVariableContainer(VariableContainerDetails.GlobalScopeName, null); + await FetchVariableContainerAsync(VariableContainerDetails.GlobalScopeName, null); this.scriptScopeVariables = - await FetchVariableContainer(VariableContainerDetails.ScriptScopeName, null); + await FetchVariableContainerAsync(VariableContainerDetails.ScriptScopeName, null); } - private async Task FetchVariableContainer( + private async Task FetchVariableContainerAsync( string scope, VariableContainerDetails autoVariables) { @@ -829,7 +829,7 @@ private async Task FetchVariableContainer( new VariableContainerDetails(this.nextVariableId++, "Scope: " + scope); this.variables.Add(scopeVariableContainer); - var results = await this.powerShellContext.ExecuteCommand(psCommand, sendErrorToHost: false); + var results = await this.powerShellContext.ExecuteCommandAsync(psCommand, sendErrorToHost: false); if (results != null) { foreach (PSObject psVariableObject in results) @@ -923,7 +923,7 @@ private bool AddToAutoVariables(PSObject psvariable, string scope) return true; } - private async Task FetchStackFrames(string scriptNameOverride) + private async Task FetchStackFramesAsync(string scriptNameOverride) { PSCommand psCommand = new PSCommand(); @@ -934,7 +934,7 @@ private async Task FetchStackFrames(string scriptNameOverride) var callStackVarName = $"$global:{PsesGlobalVariableNamePrefix}CallStack"; psCommand.AddScript($"{callStackVarName} = Get-PSCallStack; {callStackVarName}"); - var results = await this.powerShellContext.ExecuteCommand(psCommand); + var results = await this.powerShellContext.ExecuteCommandAsync(psCommand); var callStackFrames = results.ToArray(); @@ -950,7 +950,7 @@ private async Task FetchStackFrames(string scriptNameOverride) this.variables.Add(autoVariables); VariableContainerDetails localVariables = - await FetchVariableContainer(i.ToString(), autoVariables); + await FetchVariableContainerAsync(i.ToString(), autoVariables); // When debugging, this is the best way I can find to get what is likely the workspace root. // This is controlled by the "cwd:" setting in the launch config. @@ -1190,7 +1190,7 @@ private string TrimScriptListingLine(PSObject scriptLineObj, ref int prefixLengt /// public event EventHandler DebuggerStopped; - private async void OnDebuggerStop(object sender, DebuggerStopEventArgs e) + private async void OnDebuggerStopAsync(object sender, DebuggerStopEventArgs e) { bool noScriptName = false; string localScriptPath = e.InvocationInfo.ScriptName; @@ -1203,7 +1203,7 @@ private async void OnDebuggerStop(object sender, DebuggerStopEventArgs e) command.AddScript($"list 1 {int.MaxValue}"); IEnumerable scriptListingLines = - await this.powerShellContext.ExecuteCommand( + await this.powerShellContext.ExecuteCommandAsync( command, false, false); if (scriptListingLines != null) @@ -1238,7 +1238,7 @@ await this.powerShellContext.ExecuteCommand( } // Get call stack and variables. - await this.FetchStackFramesAndVariables( + await this.FetchStackFramesAndVariablesAsync( noScriptName ? localScriptPath : null); // If this is a remote connection and the debugger stopped at a line @@ -1248,7 +1248,7 @@ await this.FetchStackFramesAndVariables( !noScriptName) { localScriptPath = - await this.remoteFileManager.FetchRemoteFile( + await this.remoteFileManager.FetchRemoteFileAsync( e.InvocationInfo.ScriptName, this.powerShellContext.CurrentRunspace); } diff --git a/src/PowerShellEditorServices/Extensions/EditorContext.cs b/src/PowerShellEditorServices/Extensions/EditorContext.cs index c29c60fcc..c669acd19 100644 --- a/src/PowerShellEditorServices/Extensions/EditorContext.cs +++ b/src/PowerShellEditorServices/Extensions/EditorContext.cs @@ -107,7 +107,7 @@ public void SetSelection( public void SetSelection(BufferRange selectionRange) { this.editorOperations - .SetSelection(selectionRange) + .SetSelectionAsync(selectionRange) .Wait(); } diff --git a/src/PowerShellEditorServices/Extensions/EditorObject.cs b/src/PowerShellEditorServices/Extensions/EditorObject.cs index e8f50d589..0013da684 100644 --- a/src/PowerShellEditorServices/Extensions/EditorObject.cs +++ b/src/PowerShellEditorServices/Extensions/EditorObject.cs @@ -105,7 +105,7 @@ public EditorCommand[] GetCommands() /// A instance of the EditorContext class. public EditorContext GetEditorContext() { - return this.editorOperations.GetEditorContext().Result; + return this.editorOperations.GetEditorContextAsync().Result; } } } diff --git a/src/PowerShellEditorServices/Extensions/EditorWindow.cs b/src/PowerShellEditorServices/Extensions/EditorWindow.cs index 0ffd125f0..8d241559a 100644 --- a/src/PowerShellEditorServices/Extensions/EditorWindow.cs +++ b/src/PowerShellEditorServices/Extensions/EditorWindow.cs @@ -28,7 +28,7 @@ internal EditorWindow(IEditorOperations editorOperations) this.editorOperations = editorOperations; } - #endregion + #endregion #region Public Methods @@ -38,7 +38,7 @@ internal EditorWindow(IEditorOperations editorOperations) /// The message to be shown. public void ShowInformationMessage(string message) { - this.editorOperations.ShowInformationMessage(message).Wait(); + this.editorOperations.ShowInformationMessageAsync(message).Wait(); } /// @@ -47,7 +47,7 @@ public void ShowInformationMessage(string message) /// The message to be shown. public void ShowErrorMessage(string message) { - this.editorOperations.ShowErrorMessage(message).Wait(); + this.editorOperations.ShowErrorMessageAsync(message).Wait(); } /// @@ -56,7 +56,7 @@ public void ShowErrorMessage(string message) /// The message to be shown. public void ShowWarningMessage(string message) { - this.editorOperations.ShowWarningMessage(message).Wait(); + this.editorOperations.ShowWarningMessageAsync(message).Wait(); } /// @@ -65,7 +65,7 @@ public void ShowWarningMessage(string message) /// The message to be shown. public void SetStatusBarMessage(string message) { - this.editorOperations.SetStatusBarMessage(message, null).Wait(); + this.editorOperations.SetStatusBarMessageAsync(message, null).Wait(); } /// @@ -75,9 +75,9 @@ public void SetStatusBarMessage(string message) /// A timeout in milliseconds for how long the message should remain visible. public void SetStatusBarMessage(string message, int timeout) { - this.editorOperations.SetStatusBarMessage(message, timeout).Wait(); + this.editorOperations.SetStatusBarMessageAsync(message, timeout).Wait(); } - #endregion + #endregion } } diff --git a/src/PowerShellEditorServices/Extensions/EditorWorkspace.cs b/src/PowerShellEditorServices/Extensions/EditorWorkspace.cs index 9bdd2c043..ad679f371 100644 --- a/src/PowerShellEditorServices/Extensions/EditorWorkspace.cs +++ b/src/PowerShellEditorServices/Extensions/EditorWorkspace.cs @@ -45,7 +45,7 @@ internal EditorWorkspace(IEditorOperations editorOperations) /// public void NewFile() { - this.editorOperations.NewFile().Wait(); + this.editorOperations.NewFileAsync().Wait(); } /// @@ -55,7 +55,7 @@ public void NewFile() /// The path to the file to be opened. public void OpenFile(string filePath) { - this.editorOperations.OpenFile(filePath).Wait(); + this.editorOperations.OpenFileAsync(filePath).Wait(); } /// @@ -67,7 +67,7 @@ public void OpenFile(string filePath) /// Determines wether the file is opened as a preview or as a durable editor. public void OpenFile(string filePath, bool preview) { - this.editorOperations.OpenFile(filePath, preview).Wait(); + this.editorOperations.OpenFileAsync(filePath, preview).Wait(); } #endregion diff --git a/src/PowerShellEditorServices/Extensions/ExtensionService.cs b/src/PowerShellEditorServices/Extensions/ExtensionService.cs index d9defe6a3..cc4e01af4 100644 --- a/src/PowerShellEditorServices/Extensions/ExtensionService.cs +++ b/src/PowerShellEditorServices/Extensions/ExtensionService.cs @@ -71,7 +71,7 @@ public ExtensionService(PowerShellContext powerShellContext) /// An IEditorOperations implementation. /// An IComponentRegistry instance which provides components in the session. /// A Task that can be awaited for completion. - public async Task Initialize( + public async Task InitializeAsync( IEditorOperations editorOperations, IComponentRegistry componentRegistry) { @@ -83,7 +83,7 @@ public async Task Initialize( // Register the editor object in the runspace PSCommand variableCommand = new PSCommand(); - using (RunspaceHandle handle = await this.PowerShellContext.GetRunspaceHandle()) + using (RunspaceHandle handle = await this.PowerShellContext.GetRunspaceHandleAsync()) { handle.Runspace.SessionStateProxy.PSVariable.Set( "psEditor", @@ -97,7 +97,7 @@ public async Task Initialize( /// The unique name of the command to be invoked. /// The context in which the command is being invoked. /// A Task that can be awaited for completion. - public async Task InvokeCommand(string commandName, EditorContext editorContext) + public async Task InvokeCommandAsync(string commandName, EditorContext editorContext) { EditorCommand editorCommand; @@ -108,7 +108,7 @@ public async Task InvokeCommand(string commandName, EditorContext editorContext) executeCommand.AddParameter("ScriptBlock", editorCommand.ScriptBlock); executeCommand.AddParameter("ArgumentList", new object[] { editorContext }); - await this.PowerShellContext.ExecuteCommand( + await this.PowerShellContext.ExecuteCommandAsync( executeCommand, !editorCommand.SuppressOutput, true); diff --git a/src/PowerShellEditorServices/Extensions/FileContext.cs b/src/PowerShellEditorServices/Extensions/FileContext.cs index 1b563dcb2..3fc5ac120 100644 --- a/src/PowerShellEditorServices/Extensions/FileContext.cs +++ b/src/PowerShellEditorServices/Extensions/FileContext.cs @@ -237,7 +237,7 @@ public void InsertText( public void InsertText(string textToInsert, BufferRange insertRange) { this.editorOperations - .InsertText(this.scriptFile.ClientFilePath, textToInsert, insertRange) + .InsertTextAsync(this.scriptFile.ClientFilePath, textToInsert, insertRange) .Wait(); } @@ -250,7 +250,7 @@ public void InsertText(string textToInsert, BufferRange insertRange) /// public void Save() { - this.editorOperations.SaveFile(this.scriptFile.FilePath); + this.editorOperations.SaveFileAsync(this.scriptFile.FilePath); } /// @@ -272,7 +272,7 @@ public void SaveAs(string newFilePath) throw new IOException(String.Format("The file '{0}' already exists", absolutePath)); } - this.editorOperations.SaveFile(this.scriptFile.FilePath, newFilePath); + this.editorOperations.SaveFileAsync(this.scriptFile.FilePath, newFilePath); } #endregion diff --git a/src/PowerShellEditorServices/Extensions/IEditorOperations.cs b/src/PowerShellEditorServices/Extensions/IEditorOperations.cs index 51d0ac5b7..7ef6bdf0e 100644 --- a/src/PowerShellEditorServices/Extensions/IEditorOperations.cs +++ b/src/PowerShellEditorServices/Extensions/IEditorOperations.cs @@ -18,7 +18,7 @@ public interface IEditorOperations /// Gets the EditorContext for the editor's current state. /// /// A new EditorContext object. - Task GetEditorContext(); + Task GetEditorContextAsync(); /// /// Gets the path to the editor's active workspace. @@ -37,7 +37,7 @@ public interface IEditorOperations /// Causes a new untitled file to be created in the editor. /// /// A task that can be awaited for completion. - Task NewFile(); + Task NewFileAsync(); /// /// Causes a file to be opened in the editor. If the file is @@ -45,7 +45,7 @@ public interface IEditorOperations /// /// The path of the file to be opened. /// A Task that can be tracked for completion. - Task OpenFile(string filePath); + Task OpenFileAsync(string filePath); /// /// Causes a file to be opened in the editor. If the file is @@ -55,21 +55,21 @@ public interface IEditorOperations /// The path of the file to be opened. /// Determines wether the file is opened as a preview or as a durable editor. /// A Task that can be tracked for completion. - Task OpenFile(string filePath, bool preview); + Task OpenFileAsync(string filePath, bool preview); /// /// Causes a file to be closed in the editor. /// /// The path of the file to be closed. /// A Task that can be tracked for completion. - Task CloseFile(string filePath); + Task CloseFileAsync(string filePath); /// /// Causes a file to be saved in the editor. /// /// The path of the file to be saved. /// A Task that can be tracked for completion. - Task SaveFile(string filePath); + Task SaveFileAsync(string filePath); /// /// Causes a file to be saved as a new file in a new editor window. @@ -77,7 +77,7 @@ public interface IEditorOperations /// the path of the current file being saved /// the path of the new file where the current window content will be saved /// - Task SaveFile(string oldFilePath, string newFilePath); + Task SaveFileAsync(string oldFilePath, string newFilePath); /// /// Inserts text into the specified range for the file at the specified path. @@ -86,35 +86,35 @@ public interface IEditorOperations /// The text to insert into the file. /// The range in the file to be replaced. /// A Task that can be tracked for completion. - Task InsertText(string filePath, string insertText, BufferRange insertRange); + Task InsertTextAsync(string filePath, string insertText, BufferRange insertRange); /// /// Causes the selection to be changed in the editor's active file buffer. /// /// The range over which the selection will be made. /// A Task that can be tracked for completion. - Task SetSelection(BufferRange selectionRange); + Task SetSelectionAsync(BufferRange selectionRange); /// /// Shows an informational message to the user. /// /// The message to be shown. /// A Task that can be tracked for completion. - Task ShowInformationMessage(string message); + Task ShowInformationMessageAsync(string message); /// /// Shows an error message to the user. /// /// The message to be shown. /// A Task that can be tracked for completion. - Task ShowErrorMessage(string message); + Task ShowErrorMessageAsync(string message); /// /// Shows a warning message to the user. /// /// The message to be shown. /// A Task that can be tracked for completion. - Task ShowWarningMessage(string message); + Task ShowWarningMessageAsync(string message); /// /// Sets the status bar message in the editor UI (if applicable). @@ -122,7 +122,7 @@ public interface IEditorOperations /// The message to be shown. /// If non-null, a timeout in milliseconds for how long the message should remain visible. /// A Task that can be tracked for completion. - Task SetStatusBarMessage(string message, int? timeout); + Task SetStatusBarMessageAsync(string message, int? timeout); } } diff --git a/src/PowerShellEditorServices/Language/AstOperations.cs b/src/PowerShellEditorServices/Language/AstOperations.cs index 218462c1c..354e5f188 100644 --- a/src/PowerShellEditorServices/Language/AstOperations.cs +++ b/src/PowerShellEditorServices/Language/AstOperations.cs @@ -56,7 +56,7 @@ internal static class AstOperations /// A CommandCompletion instance that contains completions for the /// symbol at the given offset. /// - static public async Task GetCompletions( + static public async Task GetCompletionsAsync( Ast scriptAst, Token[] currentTokens, int fileOffset, @@ -95,7 +95,7 @@ static public async Task GetCompletions( // main runspace. if (powerShellContext.IsCurrentRunspaceOutOfProcess()) { - using (RunspaceHandle runspaceHandle = await powerShellContext.GetRunspaceHandle(cancellationToken)) + using (RunspaceHandle runspaceHandle = await powerShellContext.GetRunspaceHandleAsync(cancellationToken)) using (PowerShell powerShell = PowerShell.Create()) { powerShell.Runspace = runspaceHandle.Runspace; @@ -118,7 +118,7 @@ static public async Task GetCompletions( } CommandCompletion commandCompletion = null; - await powerShellContext.InvokeOnPipelineThread( + await powerShellContext.InvokeOnPipelineThreadAsync( pwsh => { stopwatch.Start(); diff --git a/src/PowerShellEditorServices/Language/CommandHelpers.cs b/src/PowerShellEditorServices/Language/CommandHelpers.cs index 1a834c410..9c72e0eef 100644 --- a/src/PowerShellEditorServices/Language/CommandHelpers.cs +++ b/src/PowerShellEditorServices/Language/CommandHelpers.cs @@ -36,7 +36,7 @@ public class CommandHelpers /// The name of the command. /// The PowerShellContext to use for running Get-Command. /// A CommandInfo object with details about the specified command. - public static async Task GetCommandInfo( + public static async Task GetCommandInfoAsync( string commandName, PowerShellContext powerShellContext) { @@ -59,7 +59,7 @@ public static async Task GetCommandInfo( return (await powerShellContext - .ExecuteCommand(command, false, false)) + .ExecuteCommandAsync(command, false, false)) .Select(o => o.BaseObject) .OfType() .FirstOrDefault(); @@ -71,7 +71,7 @@ public static async Task GetCommandInfo( /// The CommandInfo instance for the command. /// The PowerShellContext to use for getting command documentation. /// - public static async Task GetCommandSynopsis( + public static async Task GetCommandSynopsisAsync( CommandInfo commandInfo, PowerShellContext powerShellContext) { @@ -89,7 +89,7 @@ public static async Task GetCommandSynopsis( command.AddArgument(commandInfo); command.AddParameter("ErrorAction", "Ignore"); - var results = await powerShellContext.ExecuteCommand(command, false, false); + var results = await powerShellContext.ExecuteCommandAsync(command, false, false); helpObject = results.FirstOrDefault(); if (helpObject != null) diff --git a/src/PowerShellEditorServices/Language/LanguageService.cs b/src/PowerShellEditorServices/Language/LanguageService.cs index 64ff08bd6..486306b58 100644 --- a/src/PowerShellEditorServices/Language/LanguageService.cs +++ b/src/PowerShellEditorServices/Language/LanguageService.cs @@ -103,7 +103,7 @@ public LanguageService( /// /// A CommandCompletion instance completions for the identified statement. /// - public async Task GetCompletionsInFile( + public async Task GetCompletionsInFileAsync( ScriptFile scriptFile, int lineNumber, int columnNumber) @@ -118,7 +118,7 @@ public async Task GetCompletionsInFile( columnNumber); CommandCompletion commandCompletion = - await AstOperations.GetCompletions( + await AstOperations.GetCompletionsAsync( scriptFile.ScriptAst, scriptFile.ScriptTokens, fileOffset, @@ -249,7 +249,7 @@ public 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 FindSymbolDetailsAtLocation( + public async Task FindSymbolDetailsAtLocationAsync( ScriptFile scriptFile, int lineNumber, int columnNumber) @@ -269,7 +269,7 @@ public async Task FindSymbolDetailsAtLocation( symbolReference.FilePath = scriptFile.FilePath; symbolDetails = - await SymbolDetails.Create( + await SymbolDetails.CreateAsync( symbolReference, _powerShellContext); @@ -309,7 +309,7 @@ public FindOccurrencesResult FindSymbolsInFile(ScriptFile scriptFile) /// An array of scriptFiles too search for references in /// The workspace that will be searched for symbols /// FindReferencesResult - public async Task FindReferencesOfSymbol( + public async Task FindReferencesOfSymbolAsync( SymbolReference foundSymbol, ScriptFile[] referencedFiles, Workspace workspace) @@ -324,7 +324,7 @@ public async Task FindReferencesOfSymbol( foundSymbol.ScriptRegion.StartColumnNumber); // Make sure aliases have been loaded - await GetAliases(); + await GetAliasesAsync(); // 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 @@ -402,7 +402,7 @@ public async Task FindReferencesOfSymbol( /// The symbol for which a definition will be found. /// The Workspace to which the ScriptFile belongs. /// The resulting GetDefinitionResult for the symbol's definition. - public async Task GetDefinitionOfSymbol( + public async Task GetDefinitionOfSymbolAsync( ScriptFile sourceFile, SymbolReference foundSymbol, Workspace workspace) @@ -481,7 +481,7 @@ public async Task GetDefinitionOfSymbol( if (foundDefinition == null) { CommandInfo cmdInfo = - await CommandHelpers.GetCommandInfo( + await CommandHelpers.GetCommandInfoAsync( foundSymbol.SymbolName, _powerShellContext); @@ -556,7 +556,7 @@ public FindOccurrencesResult FindOccurrencesInFile( /// The line number of the cursor for the given script /// The coulumn number of the cursor for the given script /// ParameterSetSignatures - public async Task FindParameterSetsInFile( + public async Task FindParameterSetsInFileAsync( ScriptFile file, int lineNumber, int columnNumber) @@ -573,7 +573,7 @@ public async Task FindParameterSetsInFile( } CommandInfo commandInfo = - await CommandHelpers.GetCommandInfo( + await CommandHelpers.GetCommandInfoAsync( foundSymbol.SymbolName, _powerShellContext); @@ -727,7 +727,7 @@ public FunctionDefinitionAst GetFunctionDefinitionForHelpComment( /// /// Gets all aliases found in the runspace /// - private async Task GetAliases() + private async Task GetAliasesAsync() { if (_areAliasesLoaded) { @@ -743,7 +743,7 @@ private async Task GetAliases() return; } - var aliases = await _powerShellContext.ExecuteCommand( + var aliases = await _powerShellContext.ExecuteCommandAsync( new PSCommand() .AddCommand("Microsoft.PowerShell.Core\\Get-Command") .AddParameter("CommandType", CommandTypes.Alias), diff --git a/src/PowerShellEditorServices/Language/SymbolDetails.cs b/src/PowerShellEditorServices/Language/SymbolDetails.cs index 02f027415..e0971c2ec 100644 --- a/src/PowerShellEditorServices/Language/SymbolDetails.cs +++ b/src/PowerShellEditorServices/Language/SymbolDetails.cs @@ -38,8 +38,8 @@ public class SymbolDetails #region Constructors - static internal async Task Create( - SymbolReference symbolReference, + static internal async Task CreateAsync( + SymbolReference symbolReference, PowerShellContext powerShellContext) { SymbolDetails symbolDetails = new SymbolDetails(); @@ -49,14 +49,14 @@ static internal async Task Create( if (symbolReference.SymbolType == SymbolType.Function) { CommandInfo commandInfo = - await CommandHelpers.GetCommandInfo( + await CommandHelpers.GetCommandInfoAsync( symbolReference.SymbolName, powerShellContext); if (commandInfo != null) { symbolDetails.Documentation = - await CommandHelpers.GetCommandSynopsis( + await CommandHelpers.GetCommandSynopsisAsync( commandInfo, powerShellContext); diff --git a/src/PowerShellEditorServices/Session/Capabilities/DscBreakpointCapability.cs b/src/PowerShellEditorServices/Session/Capabilities/DscBreakpointCapability.cs index a5907b0d5..998b58a79 100644 --- a/src/PowerShellEditorServices/Session/Capabilities/DscBreakpointCapability.cs +++ b/src/PowerShellEditorServices/Session/Capabilities/DscBreakpointCapability.cs @@ -21,7 +21,7 @@ internal class DscBreakpointCapability : IRunspaceCapability private Dictionary breakpointsPerFile = new Dictionary(); - public async Task> SetLineBreakpoints( + public async Task> SetLineBreakpointsAsync( PowerShellContext powerShellContext, string scriptPath, BreakpointDetails[] breakpoints) @@ -52,7 +52,7 @@ public async Task> SetLineBreakpoints( // Run Enable-DscDebug as a script because running it as a PSCommand // causes an error which states that the Breakpoint parameter has not // been passed. - await powerShellContext.ExecuteScriptString( + await powerShellContext.ExecuteScriptStringAsync( hashtableString.Length > 0 ? $"Enable-DscDebug -Breakpoint {hashtableString}" : "Disable-DscDebug", diff --git a/src/PowerShellEditorServices/Session/Host/EditorServicesPSHostUserInterface.cs b/src/PowerShellEditorServices/Session/Host/EditorServicesPSHostUserInterface.cs index 2cbf29365..a1d17547f 100644 --- a/src/PowerShellEditorServices/Session/Host/EditorServicesPSHostUserInterface.cs +++ b/src/PowerShellEditorServices/Session/Host/EditorServicesPSHostUserInterface.cs @@ -146,7 +146,7 @@ private void ShowCommandPrompt() Task.Factory.StartNew( async () => { - await this.StartReplLoop(this.commandLoopCancellationToken.Token); + await this.StartReplLoopAsync(this.commandLoopCancellationToken.Token); }); } else @@ -198,7 +198,7 @@ public void SendControlC() /// A CancellationToken used to cancel the command line request. /// /// A Task that can be awaited for the resulting input string. - protected abstract Task ReadCommandLine(CancellationToken cancellationToken); + protected abstract Task ReadCommandLineAsync(CancellationToken cancellationToken); /// /// Creates an InputPrompt handle to use for displaying input @@ -278,7 +278,7 @@ public override Dictionary Prompt( CancellationTokenSource cancellationToken = new CancellationTokenSource(); Task> promptTask = this.CreateInputPromptHandler() - .PromptForInput( + .PromptForInputAsync( promptCaption, promptMessage, fields, @@ -333,7 +333,7 @@ public override int PromptForChoice( CancellationTokenSource cancellationToken = new CancellationTokenSource(); Task promptTask = this.CreateChoicePromptHandler() - .PromptForChoice( + .PromptForChoiceAsync( promptCaption, promptMessage, choices, @@ -372,7 +372,7 @@ public override PSCredential PromptForCredential( Task> promptTask = this.CreateInputPromptHandler() - .PromptForInput( + .PromptForInputAsync( promptCaption, promptMessage, new FieldDetails[] { new CredentialFieldDetails("Credential", "Credential", userName) }, @@ -446,7 +446,7 @@ public override string ReadLine() Task promptTask = this.CreateInputPromptHandler() - .PromptForInput(cancellationToken.Token); + .PromptForInputAsync(cancellationToken.Token); // Run the prompt task and wait for it to return this.WaitForPromptCompletion( @@ -467,7 +467,7 @@ public override SecureString ReadLineAsSecureString() Task promptTask = this.CreateInputPromptHandler() - .PromptForSecureInput(cancellationToken.Token); + .PromptForSecureInputAsync(cancellationToken.Token); // Run the prompt task and wait for it to return this.WaitForPromptCompletion( @@ -621,7 +621,7 @@ public Collection PromptForChoice( CancellationTokenSource cancellationToken = new CancellationTokenSource(); Task promptTask = this.CreateChoicePromptHandler() - .PromptForChoice( + .PromptForChoiceAsync( promptCaption, promptMessage, choices, @@ -644,7 +644,7 @@ public Collection PromptForChoice( private Coordinates lastPromptLocation; - private async Task WritePromptStringToHost(CancellationToken cancellationToken) + private async Task WritePromptStringToHostAsync(CancellationToken cancellationToken) { try { @@ -665,7 +665,7 @@ private async Task WritePromptStringToHost(CancellationToken cancellationToken) cancellationToken.ThrowIfCancellationRequested(); string promptString = - (await this.powerShellContext.ExecuteCommand(promptCommand, false, false)) + (await this.powerShellContext.ExecuteCommandAsync(promptCommand, false, false)) .Select(pso => pso.BaseObject) .OfType() .FirstOrDefault() ?? "PS> "; @@ -734,7 +734,7 @@ private void WriteDebuggerBanner(DebuggerStopEventArgs eventArgs) internal ConsoleColor ProgressForegroundColor { get; set; } = ConsoleColor.Yellow; internal ConsoleColor ProgressBackgroundColor { get; set; } = ConsoleColor.DarkCyan; - private async Task StartReplLoop(CancellationToken cancellationToken) + private async Task StartReplLoopAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { @@ -743,7 +743,7 @@ private async Task StartReplLoop(CancellationToken cancellationToken) try { - await this.WritePromptStringToHost(cancellationToken); + await this.WritePromptStringToHostAsync(cancellationToken); } catch (OperationCanceledException) { @@ -753,7 +753,7 @@ private async Task StartReplLoop(CancellationToken cancellationToken) try { originalCursorTop = await ConsoleProxy.GetCursorTopAsync(cancellationToken); - commandString = await this.ReadCommandLine(cancellationToken); + commandString = await this.ReadCommandLineAsync(cancellationToken); } catch (PipelineStoppedException) { @@ -790,7 +790,7 @@ private async Task StartReplLoop(CancellationToken cancellationToken) { var unusedTask = this.powerShellContext - .ExecuteScriptString( + .ExecuteScriptStringAsync( commandString, writeInputToHost: false, writeOutputToHost: true, @@ -949,7 +949,7 @@ private void PowerShellContext_ExecutionStatusChanged(object sender, ExecutionSt eventArgs.HadErrors)) { this.WriteOutput(string.Empty, true); - var unusedTask = this.WritePromptStringToHost(CancellationToken.None); + var unusedTask = this.WritePromptStringToHostAsync(CancellationToken.None); } } diff --git a/src/PowerShellEditorServices/Session/Host/TerminalPSHostUserInterface.cs b/src/PowerShellEditorServices/Session/Host/TerminalPSHostUserInterface.cs index 3752010f6..0d2e839ca 100644 --- a/src/PowerShellEditorServices/Session/Host/TerminalPSHostUserInterface.cs +++ b/src/PowerShellEditorServices/Session/Host/TerminalPSHostUserInterface.cs @@ -69,9 +69,9 @@ public TerminalPSHostUserInterface( /// A CancellationToken used to cancel the command line request. /// /// A Task that can be awaited for the resulting input string. - protected override Task ReadCommandLine(CancellationToken cancellationToken) + protected override Task ReadCommandLineAsync(CancellationToken cancellationToken) { - return this.consoleReadLine.ReadCommandLine(cancellationToken); + return this.consoleReadLine.ReadCommandLineAsync(cancellationToken); } /// diff --git a/src/PowerShellEditorServices/Session/InvocationEventQueue.cs b/src/PowerShellEditorServices/Session/InvocationEventQueue.cs index 77d85bf23..b6e9a9c0b 100644 --- a/src/PowerShellEditorServices/Session/InvocationEventQueue.cs +++ b/src/PowerShellEditorServices/Session/InvocationEventQueue.cs @@ -57,11 +57,11 @@ internal static InvocationEventQueue Create(PowerShellContext powerShellContext, /// Executes a command on the main pipeline thread through /// eventing. A event subscriber will /// be created that creates a nested PowerShell instance for - /// to utilize. + /// to utilize. /// /// /// Avoid using this method directly if possible. - /// will route commands + /// will route commands /// through this method if required. /// /// The expected result type. @@ -74,7 +74,7 @@ internal static InvocationEventQueue Create(PowerShellContext powerShellContext, /// An awaitable which will provide results once the command /// execution completes. /// - internal async Task> ExecuteCommandOnIdle( + internal async Task> ExecuteCommandOnIdleAsync( PSCommand psCommand, StringBuilder errorMessages, ExecutionOptions executionOptions) @@ -87,7 +87,7 @@ internal async Task> ExecuteCommandOnIdle( await SetInvocationRequestAsync( new InvocationRequest( - pwsh => request.Execute().GetAwaiter().GetResult())); + pwsh => request.ExecuteAsync().GetAwaiter().GetResult())); try { @@ -111,7 +111,7 @@ await SetInvocationRequestAsync( /// /// An awaitable that the caller can use to know when execution completes. /// - internal async Task InvokeOnPipelineThread(Action invocationAction) + internal async Task InvokeOnPipelineThreadAsync(Action invocationAction) { var request = new InvocationRequest(pwsh => { diff --git a/src/PowerShellEditorServices/Session/PSReadLinePromptContext.cs b/src/PowerShellEditorServices/Session/PSReadLinePromptContext.cs index 23c312fef..0b59e230b 100644 --- a/src/PowerShellEditorServices/Session/PSReadLinePromptContext.cs +++ b/src/PowerShellEditorServices/Session/PSReadLinePromptContext.cs @@ -131,7 +131,7 @@ public async Task InvokeReadLineAsync(bool isCommandLine, CancellationTo _readLineCancellationSource.Token); } - var result = (await _powerShellContext.ExecuteCommand( + var result = (await _powerShellContext.ExecuteCommandAsync( new PSCommand() .AddScript(ReadLineScript) .AddArgument(_readLineCancellationSource.Token), @@ -185,7 +185,7 @@ public void WaitForReadLineExit() { } } - public async Task WaitForReadLineExitAsync () { + public async Task WaitForReadLineExitAsync() { using (await _promptNest.GetRunspaceHandleAsync(CancellationToken.None, isReadLine: true)) { } } diff --git a/src/PowerShellEditorServices/Session/PipelineExecutionRequest.cs b/src/PowerShellEditorServices/Session/PipelineExecutionRequest.cs index cb1d66073..1c69f6a15 100644 --- a/src/PowerShellEditorServices/Session/PipelineExecutionRequest.cs +++ b/src/PowerShellEditorServices/Session/PipelineExecutionRequest.cs @@ -12,7 +12,7 @@ namespace Microsoft.PowerShell.EditorServices.Session { internal interface IPipelineExecutionRequest { - Task Execute(); + Task ExecuteAsync(); Task WaitTask { get; } } @@ -66,10 +66,10 @@ public PipelineExecutionRequest( _resultsTask = new TaskCompletionSource>(); } - public async Task Execute() + public async Task ExecuteAsync() { var results = - await _powerShellContext.ExecuteCommand( + await _powerShellContext.ExecuteCommandAsync( _psCommand, _errorMessages, _executionOptions); diff --git a/src/PowerShellEditorServices/Session/PowerShellContext.cs b/src/PowerShellEditorServices/Session/PowerShellContext.cs index 698d742fc..f5eb09494 100644 --- a/src/PowerShellEditorServices/Session/PowerShellContext.cs +++ b/src/PowerShellEditorServices/Session/PowerShellContext.cs @@ -351,7 +351,7 @@ public void Initialize( /// /// /// - public Task ImportCommandsModule(string moduleBasePath) + public Task ImportCommandsModuleAsync(string moduleBasePath) { PSCommand importCommand = new PSCommand(); importCommand @@ -361,7 +361,7 @@ public Task ImportCommandsModule(string moduleBasePath) moduleBasePath, "PowerShellEditorServices.Commands.psd1")); - return this.ExecuteCommand(importCommand, false, false); + return this.ExecuteCommandAsync(importCommand, false, false); } private static bool CheckIfRunspaceNeedsEventHandlers(RunspaceDetails runspaceDetails) @@ -409,9 +409,9 @@ private void CleanupRunspace(RunspaceDetails runspaceDetails) /// so that commands can be executed against it directly. /// /// A RunspaceHandle instance that gives access to the session's runspace. - public Task GetRunspaceHandle() + public Task GetRunspaceHandleAsync() { - return this.GetRunspaceHandleImpl(CancellationToken.None, isReadLine: false); + return this.GetRunspaceHandleImplAsync(CancellationToken.None, isReadLine: false); } /// @@ -421,9 +421,9 @@ public Task GetRunspaceHandle() /// /// A CancellationToken that can be used to cancel the request. /// A RunspaceHandle instance that gives access to the session's runspace. - public Task GetRunspaceHandle(CancellationToken cancellationToken) + public Task GetRunspaceHandleAsync(CancellationToken cancellationToken) { - return this.GetRunspaceHandleImpl(cancellationToken, isReadLine: false); + return this.GetRunspaceHandleImplAsync(cancellationToken, isReadLine: false); } /// @@ -442,12 +442,12 @@ public Task GetRunspaceHandle(CancellationToken cancellationToke /// An awaitable Task which will provide results once the command /// execution completes. /// - public async Task> ExecuteCommand( + public async Task> ExecuteCommandAsync( PSCommand psCommand, bool sendOutputToHost = false, bool sendErrorToHost = true) { - return await ExecuteCommand(psCommand, null, sendOutputToHost, sendErrorToHost); + return await ExecuteCommandAsync(psCommand, null, sendOutputToHost, sendErrorToHost); } /// @@ -470,7 +470,7 @@ public async Task> ExecuteCommand( /// An awaitable Task which will provide results once the command /// execution completes. /// - public Task> ExecuteCommand( + public Task> ExecuteCommandAsync( PSCommand psCommand, StringBuilder errorMessages, bool sendOutputToHost = false, @@ -478,7 +478,7 @@ public Task> ExecuteCommand( bool addToHistory = false) { return - this.ExecuteCommand( + this.ExecuteCommandAsync( psCommand, errorMessages, new ExecutionOptions @@ -501,7 +501,7 @@ public Task> ExecuteCommand( /// An awaitable Task which will provide results once the command /// execution completes. /// - public async Task> ExecuteCommand( + public async Task> ExecuteCommandAsync( PSCommand psCommand, StringBuilder errorMessages, ExecutionOptions executionOptions) @@ -550,7 +550,7 @@ public async Task> ExecuteCommand( } // Send the pipeline execution request to the pipeline thread - return await threadController.RequestPipelineExecution( + return await threadController.RequestPipelineExecutionAsync( new PipelineExecutionRequest( this, psCommand, @@ -579,7 +579,7 @@ public async Task> ExecuteCommand( // don't write output (e.g. command completion) if (executionTarget == ExecutionTarget.InvocationEvent) { - return (await this.InvocationEventQueue.ExecuteCommandOnIdle( + return (await this.InvocationEventQueue.ExecuteCommandOnIdleAsync( psCommand, errorMessages, executionOptions)); @@ -595,7 +595,7 @@ public async Task> ExecuteCommand( false); } - runspaceHandle = await this.GetRunspaceHandle(executionOptions.IsReadLine); + runspaceHandle = await this.GetRunspaceHandleAsync(executionOptions.IsReadLine); if (executionOptions.WriteInputToHost) { this.WriteOutput(psCommand.Commands[0].CommandText, true); @@ -786,7 +786,7 @@ public async Task> ExecuteCommand( // will exist already so we need to create one and then use it if (runspaceHandle == null) { - runspaceHandle = await this.GetRunspaceHandle(); + runspaceHandle = await this.GetRunspaceHandleAsync(); } sessionDetails = this.GetSessionDetailsInRunspace(runspaceHandle.Runspace); @@ -825,9 +825,9 @@ public async Task> ExecuteCommand( /// An awaitable Task that the caller can use to know when /// execution completes. /// - public Task ExecuteCommand(PSCommand psCommand) + public Task ExecuteCommandAsync(PSCommand psCommand) { - return this.ExecuteCommand(psCommand); + return this.ExecuteCommandAsync(psCommand); } /// @@ -835,10 +835,10 @@ public Task ExecuteCommand(PSCommand psCommand) /// /// The script string to execute. /// A Task that can be awaited for the script completion. - public Task> ExecuteScriptString( + public Task> ExecuteScriptStringAsync( string scriptString) { - return this.ExecuteScriptString(scriptString, false, true); + return this.ExecuteScriptStringAsync(scriptString, false, true); } /// @@ -847,11 +847,11 @@ public Task> ExecuteScriptString( /// The script string to execute. /// Error messages from PowerShell will be written to the StringBuilder. /// A Task that can be awaited for the script completion. - public Task> ExecuteScriptString( + public Task> ExecuteScriptStringAsync( string scriptString, StringBuilder errorMessages) { - return this.ExecuteScriptString(scriptString, errorMessages, false, true, false); + return this.ExecuteScriptStringAsync(scriptString, errorMessages, false, true, false); } /// @@ -861,12 +861,12 @@ public Task> ExecuteScriptString( /// If true, causes the script string to be written to the host. /// If true, causes the script output to be written to the host. /// A Task that can be awaited for the script completion. - public Task> ExecuteScriptString( + public Task> ExecuteScriptStringAsync( string scriptString, bool writeInputToHost, bool writeOutputToHost) { - return this.ExecuteScriptString(scriptString, null, writeInputToHost, writeOutputToHost, false); + return this.ExecuteScriptStringAsync(scriptString, null, writeInputToHost, writeOutputToHost, false); } /// @@ -877,13 +877,13 @@ public Task> ExecuteScriptString( /// If true, causes the script output to be written to the host. /// If true, adds the command to the user's command history. /// A Task that can be awaited for the script completion. - public Task> ExecuteScriptString( + public Task> ExecuteScriptStringAsync( string scriptString, bool writeInputToHost, bool writeOutputToHost, bool addToHistory) { - return this.ExecuteScriptString(scriptString, null, writeInputToHost, writeOutputToHost, addToHistory); + return this.ExecuteScriptStringAsync(scriptString, null, writeInputToHost, writeOutputToHost, addToHistory); } /// @@ -895,14 +895,14 @@ public Task> ExecuteScriptString( /// If true, causes the script output to be written to the host. /// If true, adds the command to the user's command history. /// A Task that can be awaited for the script completion. - public async Task> ExecuteScriptString( + public async Task> ExecuteScriptStringAsync( string scriptString, StringBuilder errorMessages, bool writeInputToHost, bool writeOutputToHost, bool addToHistory) { - return await this.ExecuteCommand( + return await this.ExecuteCommandAsync( new PSCommand().AddScript(scriptString.Trim()), errorMessages, new ExecutionOptions() @@ -920,7 +920,7 @@ public async Task> ExecuteScriptString( /// Arguments to pass to the script. /// Writes the executed script path and arguments to the host. /// A Task that can be awaited for completion. - public async Task ExecuteScriptWithArgs(string script, string arguments = null, bool writeInputToHost = false) + public async Task ExecuteScriptWithArgsAsync(string script, string arguments = null, bool writeInputToHost = false) { PSCommand command = new PSCommand(); @@ -931,7 +931,7 @@ public async Task ExecuteScriptWithArgs(string script, string arguments = null, try { // Assume we can only debug scripts from the FileSystem provider - string workingDir = (await ExecuteCommand( + string workingDir = (await ExecuteCommandAsync( new PSCommand() .AddCommand("Microsoft.PowerShell.Management\\Get-Location") .AddParameter("PSProvider", "FileSystem"), @@ -991,7 +991,7 @@ public async Task ExecuteScriptWithArgs(string script, string arguments = null, true); } - await this.ExecuteCommand( + await this.ExecuteCommandAsync( command, null, sendOutputToHost: true, @@ -1003,8 +1003,8 @@ await this.ExecuteCommand( /// reliquishing control of the pipeline thread during event processing. /// /// - /// This method is called automatically by and - /// . Consider using them instead of this method directly when + /// This method is called automatically by and + /// . Consider using them instead of this method directly when /// possible. /// internal void ForcePSEventHandling() @@ -1025,14 +1025,14 @@ internal void ForcePSEventHandling() /// An awaitable that the caller can use to know when execution completes. /// /// - /// This method is called automatically by . Consider using + /// This method is called automatically by . Consider using /// that method instead of calling this directly when possible. /// - internal async Task InvokeOnPipelineThread(Action invocationAction) + internal async Task InvokeOnPipelineThreadAsync(Action invocationAction) { if (this.PromptNest.IsReadLineBusy()) { - await this.InvocationEventQueue.InvokeOnPipelineThread(invocationAction); + await this.InvocationEventQueue.InvokeOnPipelineThreadAsync(invocationAction); return; } @@ -1065,7 +1065,7 @@ internal async Task InvokeReadLineAsync(bool isCommandLine, Cancellation /// loaded. /// /// A Task that can be awaited for completion. - public async Task LoadHostProfiles() + public async Task LoadHostProfilesAsync() { if (this.profilePaths != null) { @@ -1075,12 +1075,12 @@ public async Task LoadHostProfiles() { command = new PSCommand(); command.AddCommand(profilePath, false); - await this.ExecuteCommand(command, true, true); + await this.ExecuteCommandAsync(command, true, true); } // Gather the session details (particularly the prompt) after // loading the user's profiles. - await this.GetSessionDetailsInRunspace(); + await this.GetSessionDetailsInRunspaceAsync(); } } @@ -1279,12 +1279,12 @@ public void Dispose() this.initialRunspace = null; } - private async Task GetRunspaceHandle(bool isReadLine) + private async Task GetRunspaceHandleAsync(bool isReadLine) { - return await this.GetRunspaceHandleImpl(CancellationToken.None, isReadLine); + return await this.GetRunspaceHandleImplAsync(CancellationToken.None, isReadLine); } - private async Task GetRunspaceHandleImpl(CancellationToken cancellationToken, bool isReadLine) + private async Task GetRunspaceHandleImplAsync(CancellationToken cancellationToken, bool isReadLine) { return await this.PromptNest.GetRunspaceHandleAsync(cancellationToken, isReadLine); } @@ -1446,7 +1446,7 @@ internal void EnterNestedPrompt() this.ConsoleReader?.StopCommandLoop(); this.ConsoleReader?.StartCommandLoop(); - var localPipelineExecutionTask = localThreadController.TakeExecutionRequest(); + var localPipelineExecutionTask = localThreadController.TakeExecutionRequestAsync(); var localDebuggerStoppedTask = localThreadController.Exit(); // Wait for off-thread pipeline requests and/or ExitNestedPrompt @@ -1459,8 +1459,8 @@ internal void EnterNestedPrompt() if (taskIndex == 0) { var localExecutionTask = localPipelineExecutionTask.GetAwaiter().GetResult(); - localPipelineExecutionTask = localThreadController.TakeExecutionRequest(); - localExecutionTask.Execute().GetAwaiter().GetResult(); + localPipelineExecutionTask = localThreadController.TakeExecutionRequestAsync(); + localExecutionTask.ExecuteAsync().GetAwaiter().GetResult(); continue; } @@ -1494,9 +1494,9 @@ internal void ExitNestedPrompt() /// unescaped before calling this method. /// /// - public async Task SetWorkingDirectory(string path) + public async Task SetWorkingDirectoryAsync(string path) { - await this.SetWorkingDirectory(path, true); + await this.SetWorkingDirectoryAsync(path, true); } /// @@ -1504,7 +1504,7 @@ public async Task SetWorkingDirectory(string path) /// /// /// Specify false to have the path escaped, otherwise specify true if the path has already been escaped. - public async Task SetWorkingDirectory(string path, bool isPathAlreadyEscaped) + public async Task SetWorkingDirectoryAsync(string path, bool isPathAlreadyEscaped) { this.InitialWorkingDirectory = path; @@ -1513,7 +1513,7 @@ public async Task SetWorkingDirectory(string path, bool isPathAlreadyEscaped) path = WildcardEscapePath(path); } - await ExecuteCommand( + await ExecuteCommandAsync( new PSCommand().AddCommand("Set-Location").AddParameter("Path", path), null, sendOutputToHost: false, @@ -2039,9 +2039,9 @@ private SessionDetails GetSessionDetails(Func invokeAction) return this.mostRecentSessionDetails; } - private async Task GetSessionDetailsInRunspace() + private async Task GetSessionDetailsInRunspaceAsync() { - using (RunspaceHandle runspaceHandle = await this.GetRunspaceHandle()) + using (RunspaceHandle runspaceHandle = await this.GetRunspaceHandleAsync()) { return this.GetSessionDetailsInRunspace(runspaceHandle.Runspace); } @@ -2246,7 +2246,7 @@ private void OnDebuggerStop(object sender, DebuggerStopEventArgs e) this.logger.Write(LogLevel.Verbose, "Starting pipeline thread message loop..."); Task localPipelineExecutionTask = - localThreadController.TakeExecutionRequest(); + localThreadController.TakeExecutionRequestAsync(); Task localDebuggerStoppedTask = localThreadController.Exit(); while (true) @@ -2292,8 +2292,8 @@ private void OnDebuggerStop(object sender, DebuggerStopEventArgs e) this.logger.Write(LogLevel.Verbose, "Received pipeline thread execution request."); IPipelineExecutionRequest executionRequest = localPipelineExecutionTask.Result; - localPipelineExecutionTask = localThreadController.TakeExecutionRequest(); - executionRequest.Execute().GetAwaiter().GetResult(); + localPipelineExecutionTask = localThreadController.TakeExecutionRequestAsync(); + executionRequest.ExecuteAsync().GetAwaiter().GetResult(); this.logger.Write(LogLevel.Verbose, "Pipeline thread execution completed."); diff --git a/src/PowerShellEditorServices/Session/RemoteFileManager.cs b/src/PowerShellEditorServices/Session/RemoteFileManager.cs index e2d47dd88..c9885b84f 100644 --- a/src/PowerShellEditorServices/Session/RemoteFileManager.cs +++ b/src/PowerShellEditorServices/Session/RemoteFileManager.cs @@ -253,7 +253,7 @@ public RemoteFileManager( this.logger = logger; this.powerShellContext = powerShellContext; - this.powerShellContext.RunspaceChanged += HandleRunspaceChanged; + this.powerShellContext.RunspaceChanged += HandleRunspaceChangedAsync; this.editorOperations = editorOperations; @@ -287,7 +287,7 @@ public RemoteFileManager( /// /// The local file path where the remote file's contents have been stored. /// - public async Task FetchRemoteFile( + public async Task FetchRemoteFileAsync( string remoteFilePath, RunspaceDetails runspaceDetails) { @@ -313,7 +313,7 @@ public async Task FetchRemoteFile( command.AddParameter("Encoding", "Byte"); byte[] fileContent = - (await this.powerShellContext.ExecuteCommand(command, false, false)) + (await this.powerShellContext.ExecuteCommandAsync(command, false, false)) .FirstOrDefault(); if (fileContent != null) @@ -350,7 +350,7 @@ public async Task FetchRemoteFile( /// file to disk before this method is called. /// /// A Task to be awaited for completion. - public async Task SaveRemoteFile(string localFilePath) + public async Task SaveRemoteFileAsync(string localFilePath) { string remoteFilePath = this.GetMappedPath( @@ -383,7 +383,7 @@ public async Task SaveRemoteFile(string localFilePath) StringBuilder errorMessages = new StringBuilder(); - await this.powerShellContext.ExecuteCommand( + await this.powerShellContext.ExecuteCommandAsync( saveCommand, errorMessages, false, @@ -509,7 +509,7 @@ private RemotePathMappings GetPathMappings(RunspaceDetails runspaceDetails) return remotePathMappings; } - private async void HandleRunspaceChanged(object sender, RunspaceChangedEventArgs e) + private async void HandleRunspaceChangedAsync(object sender, RunspaceChangedEventArgs e) { if (e.ChangeAction == RunspaceChangeAction.Enter) { @@ -530,7 +530,7 @@ private async void HandleRunspaceChanged(object sender, RunspaceChangedEventArgs { foreach (string remotePath in remotePathMappings.OpenedPaths) { - await this.editorOperations?.CloseFile(remotePath); + await this.editorOperations?.CloseFileAsync(remotePath); } } } @@ -542,7 +542,7 @@ private async void HandleRunspaceChanged(object sender, RunspaceChangedEventArgs } } - private async void HandlePSEventReceived(object sender, PSEventArgs args) + private async void HandlePSEventReceivedAsync(object sender, PSEventArgs args) { if (string.Equals(RemoteSessionOpenFile, args.SourceIdentifier, StringComparison.CurrentCultureIgnoreCase)) { @@ -591,8 +591,8 @@ private async void HandlePSEventReceived(object sender, PSEventArgs args) } else { - await this.editorOperations?.NewFile(); - EditorContext context = await this.editorOperations?.GetEditorContext(); + await this.editorOperations?.NewFileAsync(); + EditorContext context = await this.editorOperations?.GetEditorContextAsync(); context?.CurrentFile.InsertText(Encoding.UTF8.GetString(fileContent, 0, fileContent.Length)); } } @@ -605,7 +605,7 @@ private async void HandlePSEventReceived(object sender, PSEventArgs args) } // Open the file in the editor - this.editorOperations?.OpenFile(localFilePath, preview); + this.editorOperations?.OpenFileAsync(localFilePath, preview); } } catch (NullReferenceException e) @@ -622,7 +622,7 @@ private void RegisterPSEditFunction(RunspaceDetails runspaceDetails) { try { - runspaceDetails.Runspace.Events.ReceivedEvents.PSEventReceived += HandlePSEventReceived; + runspaceDetails.Runspace.Events.ReceivedEvents.PSEventReceived += HandlePSEventReceivedAsync; PSCommand createCommand = new PSCommand(); createCommand @@ -631,7 +631,7 @@ private void RegisterPSEditFunction(RunspaceDetails runspaceDetails) if (runspaceDetails.Context == RunspaceContext.DebuggedRunspace) { - this.powerShellContext.ExecuteCommand(createCommand).Wait(); + this.powerShellContext.ExecuteCommandAsync(createCommand).Wait(); } else { @@ -659,7 +659,7 @@ private void RemovePSEditFunction(RunspaceDetails runspaceDetails) { if (runspaceDetails.Runspace.Events != null) { - runspaceDetails.Runspace.Events.ReceivedEvents.PSEventReceived -= HandlePSEventReceived; + runspaceDetails.Runspace.Events.ReceivedEvents.PSEventReceived -= HandlePSEventReceivedAsync; } if (runspaceDetails.Runspace.RunspaceStateInfo.State == RunspaceState.Opened) diff --git a/src/PowerShellEditorServices/Session/ThreadController.cs b/src/PowerShellEditorServices/Session/ThreadController.cs index 9a5583f5b..8720e3fa7 100644 --- a/src/PowerShellEditorServices/Session/ThreadController.cs +++ b/src/PowerShellEditorServices/Session/ThreadController.cs @@ -63,7 +63,7 @@ internal bool IsCurrentThread() /// A task object representing the asynchronous operation. The Result property will return /// the output of the command invocation. /// - internal async Task> RequestPipelineExecution( + internal async Task> RequestPipelineExecutionAsync( PipelineExecutionRequest executionRequest) { await PipelineRequestQueue.EnqueueAsync(executionRequest); @@ -78,7 +78,7 @@ internal async Task> RequestPipelineExecution( /// A task object representing the asynchronous operation. The Result property will return /// the retrieved pipeline execution request. /// - internal async Task TakeExecutionRequest() + internal async Task TakeExecutionRequestAsync() { return await PipelineRequestQueue.DequeueAsync(); } diff --git a/src/PowerShellEditorServices/Templates/TemplateService.cs b/src/PowerShellEditorServices/Templates/TemplateService.cs index 8ee3da496..18f58085c 100644 --- a/src/PowerShellEditorServices/Templates/TemplateService.cs +++ b/src/PowerShellEditorServices/Templates/TemplateService.cs @@ -50,7 +50,7 @@ public TemplateService(PowerShellContext powerShellContext, ILogger logger) /// Checks if Plaster is installed on the user's machine. /// /// A Task that can be awaited until the check is complete. The result will be true if Plaster is installed. - public async Task ImportPlasterIfInstalled() + public async Task ImportPlasterIfInstalledAsync() { if (!this.isPlasterInstalled.HasValue) { @@ -73,7 +73,7 @@ public async Task ImportPlasterIfInstalled() this.logger.Write(LogLevel.Verbose, "Checking if Plaster is installed..."); var getResult = - await this.powerShellContext.ExecuteCommand( + await this.powerShellContext.ExecuteCommandAsync( psCommand, false, false); PSObject moduleObject = getResult.First(); @@ -98,7 +98,7 @@ await this.powerShellContext.ExecuteCommand( .AddParameter("PassThru"); var importResult = - await this.powerShellContext.ExecuteCommand( + await this.powerShellContext.ExecuteCommandAsync( psCommand, false, false); this.isPlasterLoaded = importResult.Any(); @@ -124,7 +124,7 @@ await this.powerShellContext.ExecuteCommand( /// included templates. /// /// A Task which can be awaited for the TemplateDetails list to be returned. - public async Task GetAvailableTemplates( + public async Task GetAvailableTemplatesAsync( bool includeInstalledModules) { if (!this.isPlasterLoaded) @@ -141,7 +141,7 @@ public async Task GetAvailableTemplates( } var templateObjects = - await this.powerShellContext.ExecuteCommand( + await this.powerShellContext.ExecuteCommandAsync( psCommand, false, false); this.logger.Write( @@ -162,7 +162,7 @@ await this.powerShellContext.ExecuteCommand( /// The folder path containing the template. /// The folder path where the files will be created. /// A boolean-returning Task which communicates success or failure. - public async Task CreateFromTemplate( + public async Task CreateFromTemplateAsync( string templatePath, string destinationPath) { @@ -176,7 +176,7 @@ public async Task CreateFromTemplate( command.AddParameter("DestinationPath", destinationPath); var errorString = new System.Text.StringBuilder(); - await this.powerShellContext.ExecuteCommand( + await this.powerShellContext.ExecuteCommandAsync( command, errorString, new ExecutionOptions diff --git a/src/PowerShellEditorServices/Utility/AsyncDebouncer.cs b/src/PowerShellEditorServices/Utility/AsyncDebouncer.cs index f8bbd2422..9644eff77 100644 --- a/src/PowerShellEditorServices/Utility/AsyncDebouncer.cs +++ b/src/PowerShellEditorServices/Utility/AsyncDebouncer.cs @@ -59,12 +59,12 @@ public AsyncDebouncer(int flushInterval, bool restartOnInvoke) /// The argument for this implementation's Invoke method. /// /// A Task to be awaited until the Invoke is queued. - public async Task Invoke(TInvokeArgs invokeArgument) + public async Task InvokeAsync(TInvokeArgs invokeArgument) { using (await this.asyncLock.LockAsync()) { // Invoke the implementor - await this.OnInvoke(invokeArgument); + await this.OnInvokeAsync(invokeArgument); // If there's no timer, start one if (this.currentTimerTask == null) @@ -88,7 +88,7 @@ public async Task Invoke(TInvokeArgs invokeArgument) /// deadlocks could occur. /// /// A Task to be awaited until Flush completes. - public async Task Flush() + public async Task FlushAsync() { using (await this.asyncLock.LockAsync()) { @@ -96,7 +96,7 @@ public async Task Flush() this.CancelTimer(); // Flush the current output - await this.OnFlush(); + await this.OnFlushAsync(); } } @@ -112,13 +112,13 @@ public async Task Flush() /// The argument for this implementation's OnInvoke method. /// /// A Task to be awaited for the invoke to complete. - protected abstract Task OnInvoke(TInvokeArgs invokeArgument); + protected abstract Task OnInvokeAsync(TInvokeArgs invokeArgument); /// /// Implemented by the subclass to complete the current operation. /// /// A Task to be awaited for the operation to complete. - protected abstract Task OnFlush(); + protected abstract Task OnFlushAsync(); #endregion @@ -135,7 +135,7 @@ private void StartTimer() { if (!t.IsCanceled) { - return this.Flush(); + return this.FlushAsync(); } else { @@ -153,8 +153,8 @@ private bool CancelTimer() } // Was the task cancelled? - bool wasCancelled = - this.currentTimerTask == null || + bool wasCancelled = + this.currentTimerTask == null || this.currentTimerTask.IsCanceled; // Clear the current task so that another may be created diff --git a/test/PowerShellEditorServices.Test.Host/DebugAdapterTests.cs b/test/PowerShellEditorServices.Test.Host/DebugAdapterTests.cs index 7a2ccd197..14c6171a6 100644 --- a/test/PowerShellEditorServices.Test.Host/DebugAdapterTests.cs +++ b/test/PowerShellEditorServices.Test.Host/DebugAdapterTests.cs @@ -48,7 +48,7 @@ await this.LaunchService( this.debugAdapterClient = new DebugAdapterClient( - await NamedPipeClientChannel.Connect( + await NamedPipeClientChannel.ConnectAsync( pipeNames.Item2, MessageProtocolType.DebugAdapter, this.logger), @@ -57,7 +57,7 @@ await NamedPipeClientChannel.Connect( this.messageSender = this.debugAdapterClient; this.messageHandlers = this.debugAdapterClient; - await this.debugAdapterClient.Start(); + await this.debugAdapterClient.StartAsync(); } public Task DisposeAsync() @@ -136,7 +136,7 @@ public async Task DebugAdapterReceivesOutputEvents() private async Task LaunchScript(string scriptPath) { - await this.debugAdapterClient.LaunchScript(scriptPath); + await this.debugAdapterClient.LaunchScriptAsync(scriptPath); } } } diff --git a/test/PowerShellEditorServices.Test.Host/LanguageServerTests.cs b/test/PowerShellEditorServices.Test.Host/LanguageServerTests.cs index 50ec35f7f..5d25fccd3 100644 --- a/test/PowerShellEditorServices.Test.Host/LanguageServerTests.cs +++ b/test/PowerShellEditorServices.Test.Host/LanguageServerTests.cs @@ -51,7 +51,7 @@ await this.LaunchService( this.languageServiceClient = new LanguageServiceClient( - await NamedPipeClientChannel.Connect( + await NamedPipeClientChannel.ConnectAsync( pipeNames.Item1, MessageProtocolType.LanguageServer, this.logger), @@ -65,7 +65,7 @@ await NamedPipeClientChannel.Connect( public async Task DisposeAsync() { - await this.languageServiceClient.Stop(); + await this.languageServiceClient.StopAsync(); this.KillService(); } @@ -644,7 +644,7 @@ public async Task ServiceExecutesReplCommandAndReceivesChoicePrompt() Assert.Equal(1, showChoicePromptRequest.DefaultChoices[0]); // Respond to the prompt request - await requestContext.SendResult( + await requestContext.SendResultAsync( new ShowChoicePromptResponse { ResponseText = "a" @@ -695,7 +695,7 @@ public async Task ServiceExecutesReplCommandAndReceivesInputPrompt() Assert.Equal("Name", showInputPromptRequest.Name); // Respond to the prompt request - await requestContext.SendResult( + await requestContext.SendResultAsync( new ShowInputPromptResponse { ResponseText = "John" @@ -865,7 +865,7 @@ private async Task SendConfigurationRequest( bool enableProfileLoading = false) { // Send the configuration change to cause profiles to be loaded - await this.languageServiceClient.SendEvent( + await this.languageServiceClient.SendEventAsync( DidChangeConfigurationNotification.Type, new DidChangeConfigurationParams { diff --git a/test/PowerShellEditorServices.Test.Host/ServerTestsBase.cs b/test/PowerShellEditorServices.Test.Host/ServerTestsBase.cs index c126be90a..e805d85cd 100644 --- a/test/PowerShellEditorServices.Test.Host/ServerTestsBase.cs +++ b/test/PowerShellEditorServices.Test.Host/ServerTestsBase.cs @@ -170,7 +170,7 @@ protected Task SendRequest SendRequest(NotificationType eventType, TParams eventParams) { return - this.messageSender.SendEvent( + this.messageSender.SendEventAsync( eventType, eventParams); } diff --git a/test/PowerShellEditorServices.Test.Protocol/Message/MessageReaderWriterTests.cs b/test/PowerShellEditorServices.Test.Protocol/Message/MessageReaderWriterTests.cs index 5212a7a1c..9d637007d 100644 --- a/test/PowerShellEditorServices.Test.Protocol/Message/MessageReaderWriterTests.cs +++ b/test/PowerShellEditorServices.Test.Protocol/Message/MessageReaderWriterTests.cs @@ -42,7 +42,7 @@ public async Task WritesMessage() // Write the message and then roll back the stream to be read // TODO: This will need to be redone! - await messageWriter.WriteMessage(Message.Event("testEvent", null)); + await messageWriter.WriteMessageAsync(Message.Event("testEvent", null)); outputStream.Seek(0, SeekOrigin.Begin); string expectedHeaderString = @@ -87,7 +87,7 @@ public void ReadsMessage() inputStream.Flush(); inputStream.Seek(0, SeekOrigin.Begin); - Message messageResult = messageReader.ReadMessage().Result; + Message messageResult = messageReader.ReadMessageAsync().Result; Assert.Equal("testEvent", messageResult.Method); inputStream.Dispose(); @@ -123,7 +123,7 @@ public void ReadsManyBufferedMessages() // Read the written messages from the stream for (int i = 0; i < overflowMessageCount; i++) { - Message messageResult = messageReader.ReadMessage().Result; + Message messageResult = messageReader.ReadMessageAsync().Result; Assert.Equal("testEvent", messageResult.Method); } @@ -152,7 +152,7 @@ public void ReaderResizesBufferForLargeMessages() inputStream.Flush(); inputStream.Seek(0, SeekOrigin.Begin); - Message messageResult = messageReader.ReadMessage().Result; + Message messageResult = messageReader.ReadMessageAsync().Result; Assert.Equal("testEvent", messageResult.Method); inputStream.Dispose(); diff --git a/test/PowerShellEditorServices.Test.Protocol/Server/OutputDebouncerTests.cs b/test/PowerShellEditorServices.Test.Protocol/Server/OutputDebouncerTests.cs index 9e3db950f..fa70bd631 100644 --- a/test/PowerShellEditorServices.Test.Protocol/Server/OutputDebouncerTests.cs +++ b/test/PowerShellEditorServices.Test.Protocol/Server/OutputDebouncerTests.cs @@ -86,7 +86,7 @@ private static Task SendOutput( string outputText, bool includeNewLine = false) { - return debouncer.Invoke( + return debouncer.InvokeAsync( new OutputWrittenEventArgs( outputText, includeNewLine, @@ -100,7 +100,7 @@ internal class TestMessageSender : IMessageSender { public List OutputEvents { get; } = new List(); - public Task SendEvent( + public Task SendEventAsync( NotificationType eventType, TParams eventParams) { @@ -114,7 +114,7 @@ public Task SendEvent( return Task.FromResult(true); } - public Task SendRequest( + public Task SendRequestAsync( RequestType requestType, TParams requestParams, bool waitForResponse) { @@ -122,7 +122,7 @@ public Task SendRequest throw new NotImplementedException(); } - public Task SendRequest(RequestType0 requestType0) + public Task SendRequestAsync(RequestType0 requestType0) { // Legitimately not implemented for these tests. throw new NotImplementedException(); diff --git a/test/PowerShellEditorServices.Test/Console/ChoicePromptHandlerTests.cs b/test/PowerShellEditorServices.Test/Console/ChoicePromptHandlerTests.cs index 2fba1ea5e..55f8b60e9 100644 --- a/test/PowerShellEditorServices.Test/Console/ChoicePromptHandlerTests.cs +++ b/test/PowerShellEditorServices.Test/Console/ChoicePromptHandlerTests.cs @@ -28,7 +28,7 @@ public void ChoicePromptReturnsCorrectIdForChoice() { TestChoicePromptHandler choicePromptHandler = new TestChoicePromptHandler(); Task promptTask = - choicePromptHandler.PromptForChoice( + choicePromptHandler.PromptForChoiceAsync( "Test prompt", "Message is irrelevant", Choices, @@ -50,7 +50,7 @@ public void ChoicePromptReturnsCorrectIdForHotKey() { TestChoicePromptHandler choicePromptHandler = new TestChoicePromptHandler(); Task promptTask = - choicePromptHandler.PromptForChoice( + choicePromptHandler.PromptForChoiceAsync( "Test prompt", "Message is irrelevant", Choices, @@ -75,7 +75,7 @@ public void ChoicePromptRepromptsOnInvalidInput() new TestChoicePromptHandler(); Task promptTask = - choicePromptHandler.PromptForChoice( + choicePromptHandler.PromptForChoiceAsync( "Test prompt", "Message is irrelevant", Choices, @@ -105,7 +105,7 @@ public void ReturnInputString(string inputString) this.linePromptTask.SetResult(inputString); } - protected override Task ReadInputString(CancellationToken cancellationToken) + protected override Task ReadInputStringAsync(CancellationToken cancellationToken) { this.linePromptTask = new TaskCompletionSource(); return this.linePromptTask.Task; diff --git a/test/PowerShellEditorServices.Test/Console/InputPromptHandlerTests.cs b/test/PowerShellEditorServices.Test/Console/InputPromptHandlerTests.cs index 04bed7314..689ae1c8b 100644 --- a/test/PowerShellEditorServices.Test/Console/InputPromptHandlerTests.cs +++ b/test/PowerShellEditorServices.Test/Console/InputPromptHandlerTests.cs @@ -39,7 +39,7 @@ public void InputPromptHandlerReturnsValuesOfCorrectType() { TestInputPromptHandler inputPromptHandler = new TestInputPromptHandler(); Task> promptTask = - inputPromptHandler.PromptForInput( + inputPromptHandler.PromptForInputAsync( "Test Prompt", "Message is irrelevant", Fields, @@ -71,7 +71,7 @@ public void InputPromptHandlerAcceptsArrayOfNonStringValues() { TestInputPromptHandler inputPromptHandler = new TestInputPromptHandler(); Task> promptTask = - inputPromptHandler.PromptForInput( + inputPromptHandler.PromptForInputAsync( "Test Prompt", "Message is irrelevant", new FieldDetails[] @@ -95,7 +95,7 @@ public void InputPromptRetriesWhenCannotCastValue() { TestInputPromptHandler inputPromptHandler = new TestInputPromptHandler(); Task> promptTask = - inputPromptHandler.PromptForInput( + inputPromptHandler.PromptForInputAsync( "Test Prompt", "Message is irrelevant", Fields, @@ -146,13 +146,13 @@ public void ReturnSecureString(SecureString secureString) this.securePromptTask.SetResult(secureString); } - protected override Task ReadInputString(CancellationToken cancellationToken) + protected override Task ReadInputStringAsync(CancellationToken cancellationToken) { this.linePromptTask = new TaskCompletionSource(); return this.linePromptTask.Task; } - protected override Task ReadSecureString(CancellationToken cancellationToken) + protected override Task ReadSecureStringAsync(CancellationToken cancellationToken) { this.securePromptTask = new TaskCompletionSource(); return this.securePromptTask.Task; diff --git a/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs b/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs index 4a5d53e82..da956a640 100644 --- a/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs @@ -111,7 +111,7 @@ public async Task DebuggerAcceptsScriptArgs(string[] args) this.workspace.GetFile( TestUtilities.NormalizePath("../../../../PowerShellEditorServices.Test.Shared/Debugging/Debug` W&ith Params `[Test].ps1")); - await this.debugService.SetLineBreakpoints( + await this.debugService.SetLineBreakpointsAsync( debugWithParamsFile, new[] { BreakpointDetails.Create("", 3) }); @@ -119,7 +119,7 @@ await this.debugService.SetLineBreakpoints( // Execute the script and wait for the breakpoint to be hit Task executeTask = - this.powerShellContext.ExecuteScriptWithArgs( + this.powerShellContext.ExecuteScriptWithArgsAsync( debugWithParamsFile.FilePath, arguments); await this.AssertDebuggerStopped(debugWithParamsFile.FilePath); @@ -164,7 +164,7 @@ await this.debugService.SetLineBreakpoints( public async Task DebuggerSetsAndClearsFunctionBreakpoints() { CommandBreakpointDetails[] breakpoints = - await this.debugService.SetCommandBreakpoints( + await this.debugService.SetCommandBreakpointsAsync( new[] { CommandBreakpointDetails.Create("Write-Host"), CommandBreakpointDetails.Create("Get-Date") @@ -175,14 +175,14 @@ await this.debugService.SetCommandBreakpoints( Assert.Equal("Get-Date", breakpoints[1].Name); breakpoints = - await this.debugService.SetCommandBreakpoints( + await this.debugService.SetCommandBreakpointsAsync( new[] { CommandBreakpointDetails.Create("Get-Host") }); Assert.Equal(1, breakpoints.Length); Assert.Equal("Get-Host", breakpoints[0].Name); breakpoints = - await this.debugService.SetCommandBreakpoints( + await this.debugService.SetCommandBreakpointsAsync( new CommandBreakpointDetails[] {}); Assert.Equal(0, breakpoints.Length); @@ -192,7 +192,7 @@ await this.debugService.SetCommandBreakpoints( public async Task DebuggerStopsOnFunctionBreakpoints() { CommandBreakpointDetails[] breakpoints = - await this.debugService.SetCommandBreakpoints( + await this.debugService.SetCommandBreakpointsAsync( new[] { CommandBreakpointDetails.Create("Write-Host") }); @@ -200,7 +200,7 @@ await this.debugService.SetCommandBreakpoints( await this.AssertStateChange(PowerShellContextState.Ready); Task executeTask = - this.powerShellContext.ExecuteScriptWithArgs( + this.powerShellContext.ExecuteScriptWithArgsAsync( this.debugScriptFile.FilePath); // Wait for function breakpoint to hit @@ -238,7 +238,7 @@ await this.debugService.SetCommandBreakpoints( public async Task DebuggerSetsAndClearsLineBreakpoints() { BreakpointDetails[] breakpoints = - await this.debugService.SetLineBreakpoints( + await this.debugService.SetLineBreakpointsAsync( this.debugScriptFile, new[] { BreakpointDetails.Create("", 5), @@ -252,7 +252,7 @@ await this.debugService.SetLineBreakpoints( Assert.Equal(10, breakpoints[1].LineNumber); breakpoints = - await this.debugService.SetLineBreakpoints( + await this.debugService.SetLineBreakpointsAsync( this.debugScriptFile, new[] { BreakpointDetails.Create("", 2) }); @@ -261,7 +261,7 @@ await this.debugService.SetLineBreakpoints( Assert.Equal(1, confirmedBreakpoints.Count()); Assert.Equal(2, breakpoints[0].LineNumber); - await this.debugService.SetLineBreakpoints( + await this.debugService.SetLineBreakpointsAsync( this.debugScriptFile, new[] { BreakpointDetails.Create("", 0) }); @@ -276,7 +276,7 @@ await this.debugService.SetLineBreakpoints( public async Task DebuggerStopsOnLineBreakpoints() { BreakpointDetails[] breakpoints = - await this.debugService.SetLineBreakpoints( + await this.debugService.SetLineBreakpointsAsync( this.debugScriptFile, new[] { BreakpointDetails.Create("", 5), @@ -286,7 +286,7 @@ await this.debugService.SetLineBreakpoints( await this.AssertStateChange(PowerShellContextState.Ready); Task executeTask = - this.powerShellContext.ExecuteScriptWithArgs( + this.powerShellContext.ExecuteScriptWithArgsAsync( this.debugScriptFile.FilePath); // Wait for a couple breakpoints @@ -307,7 +307,7 @@ public async Task DebuggerStopsOnConditionalBreakpoints() const int breakpointValue2 = 20; BreakpointDetails[] breakpoints = - await this.debugService.SetLineBreakpoints( + await this.debugService.SetLineBreakpointsAsync( this.debugScriptFile, new[] { BreakpointDetails.Create("", 7, null, $"$i -eq {breakpointValue1} -or $i -eq {breakpointValue2}"), @@ -316,7 +316,7 @@ await this.debugService.SetLineBreakpoints( await this.AssertStateChange(PowerShellContextState.Ready); Task executeTask = - this.powerShellContext.ExecuteScriptWithArgs( + this.powerShellContext.ExecuteScriptWithArgsAsync( this.debugScriptFile.FilePath); // Wait for conditional breakpoint to hit @@ -357,7 +357,7 @@ public async Task DebuggerStopsOnHitConditionBreakpoint() const int hitCount = 5; BreakpointDetails[] breakpoints = - await this.debugService.SetLineBreakpoints( + await this.debugService.SetLineBreakpointsAsync( this.debugScriptFile, new[] { BreakpointDetails.Create("", 6, null, null, $"{hitCount}"), @@ -366,7 +366,7 @@ await this.debugService.SetLineBreakpoints( await this.AssertStateChange(PowerShellContextState.Ready); Task executeTask = - this.powerShellContext.ExecuteScriptWithArgs( + this.powerShellContext.ExecuteScriptWithArgsAsync( this.debugScriptFile.FilePath); // Wait for conditional breakpoint to hit @@ -393,7 +393,7 @@ public async Task DebuggerStopsOnConditionalAndHitConditionBreakpoint() const int hitCount = 5; BreakpointDetails[] breakpoints = - await this.debugService.SetLineBreakpoints( + await this.debugService.SetLineBreakpointsAsync( this.debugScriptFile, new[] { BreakpointDetails.Create("", 6, null, $"$i % 2 -eq 0", $"{hitCount}"), @@ -402,7 +402,7 @@ await this.debugService.SetLineBreakpoints( await this.AssertStateChange(PowerShellContextState.Ready); Task executeTask = - this.powerShellContext.ExecuteScriptWithArgs( + this.powerShellContext.ExecuteScriptWithArgsAsync( this.debugScriptFile.FilePath); // Wait for conditional breakpoint to hit @@ -428,7 +428,7 @@ await this.debugService.SetLineBreakpoints( public async Task DebuggerProvidesMessageForInvalidConditionalBreakpoint() { BreakpointDetails[] breakpoints = - await this.debugService.SetLineBreakpoints( + await this.debugService.SetLineBreakpointsAsync( this.debugScriptFile, new[] { BreakpointDetails.Create("", 5), @@ -450,7 +450,7 @@ await this.debugService.SetLineBreakpoints( public async Task DebuggerFindsParseableButInvalidSimpleBreakpointConditions() { BreakpointDetails[] breakpoints = - await this.debugService.SetLineBreakpoints( + await this.debugService.SetLineBreakpointsAsync( this.debugScriptFile, new[] { BreakpointDetails.Create("", 5, column: null, condition: "$i == 100"), @@ -482,7 +482,7 @@ await this.AssertStateChange( "Unexpected breakpoint found in script file"); Task executeTask = - this.powerShellContext.ExecuteScriptString( + this.powerShellContext.ExecuteScriptStringAsync( this.debugScriptFile.FilePath); // Break execution and wait for the debugger to stop @@ -505,7 +505,7 @@ await this.AssertStateChange( public async Task DebuggerRunsCommandsWhileStopped() { Task executeTask = - this.powerShellContext.ExecuteScriptString( + this.powerShellContext.ExecuteScriptStringAsync( this.debugScriptFile.FilePath); // Break execution and wait for the debugger to stop @@ -515,7 +515,7 @@ await this.AssertStateChange( PowerShellExecutionResult.Stopped); // Try running a command from outside the pipeline thread - await this.powerShellContext.ExecuteScriptString("Get-Command Get-Process"); + await this.powerShellContext.ExecuteScriptStringAsync("Get-Command Get-Process"); // Abort execution and wait for the debugger to exit this.debugService.Abort(); @@ -528,13 +528,13 @@ await this.AssertStateChange( [Fact] public async Task DebuggerVariableStringDisplaysCorrectly() { - await this.debugService.SetLineBreakpoints( + await this.debugService.SetLineBreakpointsAsync( this.variableScriptFile, new[] { BreakpointDetails.Create("", 18) }); // Execute the script and wait for the breakpoint to be hit Task executeTask = - this.powerShellContext.ExecuteScriptString( + this.powerShellContext.ExecuteScriptStringAsync( this.variableScriptFile.FilePath); await this.AssertDebuggerStopped(this.variableScriptFile.FilePath); @@ -556,13 +556,13 @@ await this.debugService.SetLineBreakpoints( [Fact] public async Task DebuggerGetsVariables() { - await this.debugService.SetLineBreakpoints( + await this.debugService.SetLineBreakpointsAsync( this.variableScriptFile, new[] { BreakpointDetails.Create("", 14) }); // Execute the script and wait for the breakpoint to be hit Task executeTask = - this.powerShellContext.ExecuteScriptString( + this.powerShellContext.ExecuteScriptStringAsync( this.variableScriptFile.FilePath); await this.AssertDebuggerStopped(this.variableScriptFile.FilePath); @@ -606,13 +606,13 @@ await this.debugService.SetLineBreakpoints( [Fact] public async Task DebuggerSetsVariablesNoConversion() { - await this.debugService.SetLineBreakpoints( + await this.debugService.SetLineBreakpointsAsync( this.variableScriptFile, new[] { BreakpointDetails.Create("", 14) }); // Execute the script and wait for the breakpoint to be hit Task executeTask = - this.powerShellContext.ExecuteScriptString( + this.powerShellContext.ExecuteScriptStringAsync( this.variableScriptFile.FilePath); await this.AssertDebuggerStopped(this.variableScriptFile.FilePath); @@ -624,7 +624,7 @@ await this.debugService.SetLineBreakpoints( // Test set of a local string variable (not strongly typed) string newStrValue = "\"Goodbye\""; - string setStrValue = await debugService.SetVariable(stackFrames[0].LocalVariables.Id, "$strVar", newStrValue); + string setStrValue = await debugService.SetVariableAsync(stackFrames[0].LocalVariables.Id, "$strVar", newStrValue); Assert.Equal(newStrValue, setStrValue); VariableScope[] scopes = this.debugService.GetVariableScopes(0); @@ -633,13 +633,13 @@ await this.debugService.SetLineBreakpoints( VariableScope scriptScope = scopes.FirstOrDefault(s => s.Name == VariableContainerDetails.ScriptScopeName); string newIntValue = "49"; string newIntExpr = "7 * 7"; - string setIntValue = await debugService.SetVariable(scriptScope.Id, "$scriptInt", newIntExpr); + string setIntValue = await debugService.SetVariableAsync(scriptScope.Id, "$scriptInt", newIntExpr); Assert.Equal(newIntValue, setIntValue); // Test set of global scope int variable (not strongly typed) VariableScope globalScope = scopes.FirstOrDefault(s => s.Name == VariableContainerDetails.GlobalScopeName); string newGlobalIntValue = "4242"; - string setGlobalIntValue = await debugService.SetVariable(globalScope.Id, "$MaximumHistoryCount", newGlobalIntValue); + string setGlobalIntValue = await debugService.SetVariableAsync(globalScope.Id, "$MaximumHistoryCount", newGlobalIntValue); Assert.Equal(newGlobalIntValue, setGlobalIntValue); // The above just tests that the debug service returns the correct new value string. @@ -675,13 +675,13 @@ await this.debugService.SetLineBreakpoints( [Fact] public async Task DebuggerSetsVariablesWithConversion() { - await this.debugService.SetLineBreakpoints( + await this.debugService.SetLineBreakpointsAsync( this.variableScriptFile, new[] { BreakpointDetails.Create("", 14) }); // Execute the script and wait for the breakpoint to be hit Task executeTask = - this.powerShellContext.ExecuteScriptString( + this.powerShellContext.ExecuteScriptStringAsync( this.variableScriptFile.FilePath); await this.AssertDebuggerStopped(this.variableScriptFile.FilePath); @@ -694,7 +694,7 @@ await this.debugService.SetLineBreakpoints( // Test set of a local string variable (not strongly typed but force conversion) string newStrValue = "\"False\""; string newStrExpr = "$false"; - string setStrValue = await debugService.SetVariable(stackFrames[0].LocalVariables.Id, "$strVar2", newStrExpr); + string setStrValue = await debugService.SetVariableAsync(stackFrames[0].LocalVariables.Id, "$strVar2", newStrExpr); Assert.Equal(newStrValue, setStrValue); VariableScope[] scopes = this.debugService.GetVariableScopes(0); @@ -703,14 +703,14 @@ await this.debugService.SetLineBreakpoints( VariableScope scriptScope = scopes.FirstOrDefault(s => s.Name == VariableContainerDetails.ScriptScopeName); string newBoolValue = "$true"; string newBoolExpr = "1"; - string setBoolValue = await debugService.SetVariable(scriptScope.Id, "$scriptBool", newBoolExpr); + string setBoolValue = await debugService.SetVariableAsync(scriptScope.Id, "$scriptBool", newBoolExpr); Assert.Equal(newBoolValue, setBoolValue); // Test set of global scope ActionPreference variable (strongly typed) VariableScope globalScope = scopes.FirstOrDefault(s => s.Name == VariableContainerDetails.GlobalScopeName); string newGlobalValue = "Continue"; string newGlobalExpr = "'Continue'"; - string setGlobalValue = await debugService.SetVariable(globalScope.Id, "$VerbosePreference", newGlobalExpr); + string setGlobalValue = await debugService.SetVariableAsync(globalScope.Id, "$VerbosePreference", newGlobalExpr); Assert.Equal(newGlobalValue, setGlobalValue); // The above just tests that the debug service returns the correct new value string. @@ -746,13 +746,13 @@ await this.debugService.SetLineBreakpoints( [Fact] public async Task DebuggerVariableEnumDisplaysCorrectly() { - await this.debugService.SetLineBreakpoints( + await this.debugService.SetLineBreakpointsAsync( this.variableScriptFile, new[] { BreakpointDetails.Create("", 18) }); // Execute the script and wait for the breakpoint to be hit Task executeTask = - this.powerShellContext.ExecuteScriptString( + this.powerShellContext.ExecuteScriptStringAsync( this.variableScriptFile.FilePath); await this.AssertDebuggerStopped(this.variableScriptFile.FilePath); @@ -774,13 +774,13 @@ await this.debugService.SetLineBreakpoints( [Fact] public async Task DebuggerVariableHashtableDisplaysCorrectly() { - await this.debugService.SetLineBreakpoints( + await this.debugService.SetLineBreakpointsAsync( this.variableScriptFile, new[] { BreakpointDetails.Create("", 18) }); // Execute the script and wait for the breakpoint to be hit Task executeTask = - this.powerShellContext.ExecuteScriptString( + this.powerShellContext.ExecuteScriptStringAsync( this.variableScriptFile.FilePath); await this.AssertDebuggerStopped(this.variableScriptFile.FilePath); @@ -818,13 +818,13 @@ await this.debugService.SetLineBreakpoints( [Fact] public async Task DebuggerVariablePSObjectDisplaysCorrectly() { - await this.debugService.SetLineBreakpoints( + await this.debugService.SetLineBreakpointsAsync( this.variableScriptFile, new[] { BreakpointDetails.Create("", 18) }); // Execute the script and wait for the breakpoint to be hit Task executeTask = - this.powerShellContext.ExecuteScriptString( + this.powerShellContext.ExecuteScriptStringAsync( this.variableScriptFile.FilePath); await this.AssertDebuggerStopped(this.variableScriptFile.FilePath); @@ -853,13 +853,13 @@ await this.debugService.SetLineBreakpoints( [Fact] public async Task DebuggerVariablePSCustomObjectDisplaysCorrectly() { - await this.debugService.SetLineBreakpoints( + await this.debugService.SetLineBreakpointsAsync( this.variableScriptFile, new[] { BreakpointDetails.Create("", 18) }); // Execute the script and wait for the breakpoint to be hit Task executeTask = - this.powerShellContext.ExecuteScriptString( + this.powerShellContext.ExecuteScriptStringAsync( this.variableScriptFile.FilePath); await this.AssertDebuggerStopped(this.variableScriptFile.FilePath); @@ -896,13 +896,13 @@ await this.debugService.SetLineBreakpoints( #endif public async Task DebuggerVariableProcessObjDisplaysCorrectly() { - await this.debugService.SetLineBreakpoints( + await this.debugService.SetLineBreakpointsAsync( this.variableScriptFile, new[] { BreakpointDetails.Create("", 18) }); // Execute the script and wait for the breakpoint to be hit Task executeTask = - this.powerShellContext.ExecuteScriptString( + this.powerShellContext.ExecuteScriptStringAsync( this.variableScriptFile.FilePath); await this.AssertDebuggerStopped(this.variableScriptFile.FilePath); @@ -966,7 +966,7 @@ private async Task AssertStateChange( private async Task> GetConfirmedBreakpoints(ScriptFile scriptFile) { return - await this.powerShellContext.ExecuteCommand( + await this.powerShellContext.ExecuteCommandAsync( new PSCommand() .AddCommand("Get-PSBreakpoint") .AddParameter("Script", scriptFile.FilePath)); diff --git a/test/PowerShellEditorServices.Test/Extensions/ExtensionServiceTests.cs b/test/PowerShellEditorServices.Test/Extensions/ExtensionServiceTests.cs index 435d16ecb..60e3095b0 100644 --- a/test/PowerShellEditorServices.Test/Extensions/ExtensionServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Extensions/ExtensionServiceTests.cs @@ -39,7 +39,7 @@ public async Task InitializeAsync() { var logger = Logging.NullLogger; this.powerShellContext = PowerShellContextFactory.Create(logger); - await this.powerShellContext.ImportCommandsModule( + await this.powerShellContext.ImportCommandsModuleAsync( TestUtilities.NormalizePath("../../../../../module/PowerShellEditorServices/Commands")); this.extensionService = new ExtensionService(this.powerShellContext); @@ -49,7 +49,7 @@ await this.powerShellContext.ImportCommandsModule( this.extensionService.CommandUpdated += ExtensionService_ExtensionUpdated; this.extensionService.CommandRemoved += ExtensionService_ExtensionRemoved; - await this.extensionService.Initialize( + await this.extensionService.InitializeAsync( this.editorOperations, new ComponentRegistry()); @@ -72,7 +72,7 @@ public Task DisposeAsync() [Fact] public async Task CanRegisterAndInvokeCommandWithCmdletName() { - await extensionService.PowerShellContext.ExecuteScriptString( + await extensionService.PowerShellContext.ExecuteScriptStringAsync( TestUtilities.NormalizeNewlines("function Invoke-Extension { $global:extensionValue = 5 }\n") + "Register-EditorCommand -Name \"test.function\" -DisplayName \"Function extension\" -Function \"Invoke-Extension\""); @@ -80,31 +80,31 @@ await extensionService.PowerShellContext.ExecuteScriptString( EditorCommand command = await this.AssertExtensionEvent(EventType.Add, "test.function"); // Invoke the command - await extensionService.InvokeCommand("test.function", this.commandContext); + await extensionService.InvokeCommandAsync("test.function", this.commandContext); // Assert the expected value PSCommand psCommand = new PSCommand(); psCommand.AddScript("$global:extensionValue"); - var results = await powerShellContext.ExecuteCommand(psCommand); + var results = await powerShellContext.ExecuteCommandAsync(psCommand); Assert.Equal(5, results.FirstOrDefault()); } [Fact] public async Task CanRegisterAndInvokeCommandWithScriptBlock() { - await extensionService.PowerShellContext.ExecuteScriptString( + await extensionService.PowerShellContext.ExecuteScriptStringAsync( "Register-EditorCommand -Name \"test.scriptblock\" -DisplayName \"ScriptBlock extension\" -ScriptBlock { $global:extensionValue = 10 }"); // Wait for the add event EditorCommand command = await this.AssertExtensionEvent(EventType.Add, "test.scriptblock"); // Invoke the command - await extensionService.InvokeCommand("test.scriptblock", this.commandContext); + await extensionService.InvokeCommandAsync("test.scriptblock", this.commandContext); // Assert the expected value PSCommand psCommand = new PSCommand(); psCommand.AddScript("$global:extensionValue"); - var results = await powerShellContext.ExecuteCommand(psCommand); + var results = await powerShellContext.ExecuteCommandAsync(psCommand); Assert.Equal(10, results.FirstOrDefault()); } @@ -112,7 +112,7 @@ await extensionService.PowerShellContext.ExecuteScriptString( public async Task CanUpdateRegisteredCommand() { // Register a command and then update it - await extensionService.PowerShellContext.ExecuteScriptString(TestUtilities.NormalizeNewlines( + await extensionService.PowerShellContext.ExecuteScriptStringAsync(TestUtilities.NormalizeNewlines( "function Invoke-Extension { Write-Output \"Extension output!\" }\n" + "Register-EditorCommand -Name \"test.function\" -DisplayName \"Function extension\" -Function \"Invoke-Extension\"\n" + "Register-EditorCommand -Name \"test.function\" -DisplayName \"Updated Function extension\" -Function \"Invoke-Extension\"")); @@ -128,19 +128,19 @@ await extensionService.PowerShellContext.ExecuteScriptString(TestUtilities.Norma public async Task CanUnregisterCommand() { // Add the command and wait for the add event - await extensionService.PowerShellContext.ExecuteScriptString( + await extensionService.PowerShellContext.ExecuteScriptStringAsync( "Register-EditorCommand -Name \"test.scriptblock\" -DisplayName \"ScriptBlock extension\" -ScriptBlock { Write-Output \"Extension output!\" }"); await this.AssertExtensionEvent(EventType.Add, "test.scriptblock"); // Remove the command and wait for the remove event - await extensionService.PowerShellContext.ExecuteScriptString( + await extensionService.PowerShellContext.ExecuteScriptStringAsync( "Unregister-EditorCommand -Name \"test.scriptblock\""); await this.AssertExtensionEvent(EventType.Remove, "test.scriptblock"); // Ensure that the command has been unregistered await Assert.ThrowsAsync( typeof(KeyNotFoundException), - () => extensionService.InvokeCommand("test.scriptblock", this.commandContext)); + () => extensionService.InvokeCommandAsync("test.scriptblock", this.commandContext)); } private async Task AssertExtensionEvent(EventType expectedEventType, string expectedExtensionName) @@ -187,67 +187,67 @@ public string GetWorkspaceRelativePath(string filePath) throw new NotImplementedException(); } - public Task NewFile() + public Task NewFileAsync() { throw new NotImplementedException(); } - public Task OpenFile(string filePath) + public Task OpenFileAsync(string filePath) { throw new NotImplementedException(); } - public Task OpenFile(string filePath, bool preview) + public Task OpenFileAsync(string filePath, bool preview) { throw new NotImplementedException(); } - public Task CloseFile(string filePath) + public Task CloseFileAsync(string filePath) { throw new NotImplementedException(); } - public Task SaveFile(string filePath) + public Task SaveFileAsync(string filePath) { - return SaveFile(filePath, null); + return SaveFileAsync(filePath, null); } - public Task SaveFile(string filePath, string newSavePath) + public Task SaveFileAsync(string filePath, string newSavePath) { throw new NotImplementedException(); } - public Task InsertText(string filePath, string text, BufferRange insertRange) + public Task InsertTextAsync(string filePath, string text, BufferRange insertRange) { throw new NotImplementedException(); } - public Task SetSelection(BufferRange selectionRange) + public Task SetSelectionAsync(BufferRange selectionRange) { throw new NotImplementedException(); } - public Task GetEditorContext() + public Task GetEditorContextAsync() { throw new NotImplementedException(); } - public Task ShowInformationMessage(string message) + public Task ShowInformationMessageAsync(string message) { throw new NotImplementedException(); } - public Task ShowErrorMessage(string message) + public Task ShowErrorMessageAsync(string message) { throw new NotImplementedException(); } - public Task ShowWarningMessage(string message) + public Task ShowWarningMessageAsync(string message) { throw new NotImplementedException(); } - public Task SetStatusBarMessage(string message, int? timeout) + public Task SetStatusBarMessageAsync(string message, int? timeout) { throw new NotImplementedException(); } diff --git a/test/PowerShellEditorServices.Test/Language/LanguageServiceTests.cs b/test/PowerShellEditorServices.Test/Language/LanguageServiceTests.cs index 1d4972053..656e5153b 100644 --- a/test/PowerShellEditorServices.Test/Language/LanguageServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Language/LanguageServiceTests.cs @@ -283,7 +283,7 @@ await this.GetReferences( public async Task LanguageServiceFindsDetailsForBuiltInCommand() { SymbolDetails symbolDetails = - await this.languageService.FindSymbolDetailsAtLocation( + await this.languageService.FindSymbolDetailsAtLocationAsync( this.GetScriptFile(FindsDetailsForBuiltInCommand.SourceDetails), FindsDetailsForBuiltInCommand.SourceDetails.StartLineNumber, FindsDetailsForBuiltInCommand.SourceDetails.StartColumnNumber); @@ -366,7 +366,7 @@ private async Task GetCompletionResults(ScriptRegion scriptRe { // Run the completions request return - await this.languageService.GetCompletionsInFile( + await this.languageService.GetCompletionsInFileAsync( GetScriptFile(scriptRegion), scriptRegion.StartLineNumber, scriptRegion.StartColumnNumber); @@ -375,7 +375,7 @@ await this.languageService.GetCompletionsInFile( private async Task GetParamSetSignatures(ScriptRegion scriptRegion) { return - await this.languageService.FindParameterSetsInFile( + await this.languageService.FindParameterSetsInFileAsync( GetScriptFile(scriptRegion), scriptRegion.StartLineNumber, scriptRegion.StartColumnNumber); @@ -394,7 +394,7 @@ private async Task GetDefinition(ScriptRegion scriptRegion, Assert.NotNull(symbolReference); return - await this.languageService.GetDefinitionOfSymbol( + await this.languageService.GetDefinitionOfSymbolAsync( scriptFile, symbolReference, workspace); @@ -418,7 +418,7 @@ private async Task GetReferences(ScriptRegion scriptRegion Assert.NotNull(symbolReference); return - await this.languageService.FindReferencesOfSymbol( + await this.languageService.FindReferencesOfSymbolAsync( symbolReference, this.workspace.ExpandScriptReferences(scriptFile), this.workspace); diff --git a/test/PowerShellEditorServices.Test/PowerShellContextFactory.cs b/test/PowerShellEditorServices.Test/PowerShellContextFactory.cs index 22a41d2e9..6b9848251 100644 --- a/test/PowerShellEditorServices.Test/PowerShellContextFactory.cs +++ b/test/PowerShellEditorServices.Test/PowerShellContextFactory.cs @@ -59,7 +59,7 @@ protected override InputPromptHandler OnCreateInputPromptHandler() throw new NotImplementedException(); } - protected override Task ReadCommandLine(CancellationToken cancellationToken) + protected override Task ReadCommandLineAsync(CancellationToken cancellationToken) { return Task.FromResult("USER COMMAND"); } diff --git a/test/PowerShellEditorServices.Test/Session/PowerShellContextTests.cs b/test/PowerShellEditorServices.Test/Session/PowerShellContextTests.cs index dcb0e738a..88776c407 100644 --- a/test/PowerShellEditorServices.Test/Session/PowerShellContextTests.cs +++ b/test/PowerShellEditorServices.Test/Session/PowerShellContextTests.cs @@ -61,7 +61,7 @@ public async Task CanExecutePSCommand() psCommand.AddScript("$a = \"foo\"; $a"); var executeTask = - this.powerShellContext.ExecuteCommand(psCommand); + this.powerShellContext.ExecuteCommandAsync(psCommand); await this.AssertStateChange(PowerShellContextState.Running); await this.AssertStateChange(PowerShellContextState.Ready); @@ -74,14 +74,14 @@ public async Task CanExecutePSCommand() public async Task CanQueueParallelRunspaceRequests() { // Concurrently initiate 4 requests in the session - Task taskOne = this.powerShellContext.ExecuteScriptString("$x = 100"); - Task handleTask = this.powerShellContext.GetRunspaceHandle(); - Task taskTwo = this.powerShellContext.ExecuteScriptString("$x += 200"); - Task taskThree = this.powerShellContext.ExecuteScriptString("$x = $x / 100"); + Task taskOne = this.powerShellContext.ExecuteScriptStringAsync("$x = 100"); + Task handleTask = this.powerShellContext.GetRunspaceHandleAsync(); + Task taskTwo = this.powerShellContext.ExecuteScriptStringAsync("$x += 200"); + Task taskThree = this.powerShellContext.ExecuteScriptStringAsync("$x = $x / 100"); PSCommand psCommand = new PSCommand(); psCommand.AddScript("$x"); - Task> resultTask = this.powerShellContext.ExecuteCommand(psCommand); + Task> resultTask = this.powerShellContext.ExecuteCommandAsync(psCommand); // Wait for the requested runspace handle and then dispose it RunspaceHandle handle = await handleTask; @@ -105,7 +105,7 @@ public async Task CanAbortExecution() Task.Run( async () => { - var unusedTask = this.powerShellContext.ExecuteScriptWithArgs(s_debugTestFilePath); + var unusedTask = this.powerShellContext.ExecuteScriptWithArgsAsync(s_debugTestFilePath); await Task.Delay(50); this.powerShellContext.AbortExecution(); }); @@ -130,7 +130,7 @@ public async Task CanResolveAndLoadProfilesForHostId() }; // Load the profiles for the test host name - await this.powerShellContext.LoadHostProfiles(); + await this.powerShellContext.LoadHostProfilesAsync(); // Ensure that all the paths are set in the correct variables // and that the current user's host profile got loaded @@ -143,7 +143,7 @@ public async Task CanResolveAndLoadProfilesForHostId() "$(Assert-ProfileLoaded)\""); var result = - await this.powerShellContext.ExecuteCommand( + await this.powerShellContext.ExecuteCommandAsync( psCommand); string expectedString = diff --git a/test/PowerShellEditorServices.Test/Utility/AsyncDebouncerTests.cs b/test/PowerShellEditorServices.Test/Utility/AsyncDebouncerTests.cs index 443dc1ba1..9c40a351d 100644 --- a/test/PowerShellEditorServices.Test/Utility/AsyncDebouncerTests.cs +++ b/test/PowerShellEditorServices.Test/Utility/AsyncDebouncerTests.cs @@ -19,15 +19,15 @@ public async Task AsyncDebouncerFlushesAfterInterval() { TestAsyncDebouncer debouncer = new TestAsyncDebouncer(); - await debouncer.Invoke(1); - await debouncer.Invoke(2); - await debouncer.Invoke(3); + await debouncer.InvokeAsync(1); + await debouncer.InvokeAsync(2); + await debouncer.InvokeAsync(3); await Task.Delay(TestAsyncDebouncer.Interval + 100); // Add a few more items to ensure they are added after the initial interval - await debouncer.Invoke(4); - await debouncer.Invoke(5); - await debouncer.Invoke(6); + await debouncer.InvokeAsync(4); + await debouncer.InvokeAsync(5); + await debouncer.InvokeAsync(6); Assert.Equal(new List { 1, 2, 3 }, debouncer.FlushedBuffer); Assert.True( @@ -41,18 +41,18 @@ public async Task AsyncDebouncerFlushesAfterInterval() } [Fact] - public async Task AsyncDebouncerRestartsAfterInvoke() + public async Task AsyncDebouncerRestartsAfterInvokeAsync() { TestAsyncRestartDebouncer debouncer = new TestAsyncRestartDebouncer(); // Invoke the debouncer and wait a bit between each // invoke to make sure the debouncer isn't flushed // until after the last invoke. - await debouncer.Invoke(1); + await debouncer.InvokeAsync(1); await Task.Delay(TestAsyncRestartDebouncer.Interval - 100); - await debouncer.Invoke(2); + await debouncer.InvokeAsync(2); await Task.Delay(TestAsyncRestartDebouncer.Interval - 100); - await debouncer.Invoke(3); + await debouncer.InvokeAsync(3); await Task.Delay(TestAsyncRestartDebouncer.Interval + 100); // The only item flushed should be 3 since its interval has lapsed @@ -77,7 +77,7 @@ public TestAsyncDebouncer() : base(Interval, false) { } - protected override Task OnInvoke(int args) + protected override Task OnInvokeAsync(int args) { if (!this.firstInvoke.HasValue) { @@ -89,7 +89,7 @@ protected override Task OnInvoke(int args) return Task.FromResult(true); } - protected override Task OnFlush() + protected override Task OnFlushAsync() { // Mark the flush time this.TimeToFlush = DateTime.Now - this.firstInvoke.Value; @@ -118,13 +118,13 @@ public TestAsyncRestartDebouncer() : base(Interval, true) { } - protected override Task OnInvoke(int args) + protected override Task OnInvokeAsync(int args) { this.lastInvokeInt = args; return Task.FromResult(true); } - protected override Task OnFlush() + protected override Task OnFlushAsync() { this.FlushedBuffer.Add(this.lastInvokeInt); diff --git a/test/PowerShellEditorServices.Test/Utility/AsyncQueueTests.cs b/test/PowerShellEditorServices.Test/Utility/AsyncQueueTests.cs index fe161c8ca..af67e88f3 100644 --- a/test/PowerShellEditorServices.Test/Utility/AsyncQueueTests.cs +++ b/test/PowerShellEditorServices.Test/Utility/AsyncQueueTests.cs @@ -26,11 +26,11 @@ public async Task AsyncQueueSynchronizesAccess() { // Start 5 consumers await Task.WhenAll( - Task.Run(() => ConsumeItems(inputQueue, outputItems, cancellationTokenSource.Token)), - Task.Run(() => ConsumeItems(inputQueue, outputItems, cancellationTokenSource.Token)), - Task.Run(() => ConsumeItems(inputQueue, outputItems, cancellationTokenSource.Token)), - Task.Run(() => ConsumeItems(inputQueue, outputItems, cancellationTokenSource.Token)), - Task.Run(() => ConsumeItems(inputQueue, outputItems, cancellationTokenSource.Token)), + Task.Run(() => ConsumeItemsAsync(inputQueue, outputItems, cancellationTokenSource.Token)), + Task.Run(() => ConsumeItemsAsync(inputQueue, outputItems, cancellationTokenSource.Token)), + Task.Run(() => ConsumeItemsAsync(inputQueue, outputItems, cancellationTokenSource.Token)), + Task.Run(() => ConsumeItemsAsync(inputQueue, outputItems, cancellationTokenSource.Token)), + Task.Run(() => ConsumeItemsAsync(inputQueue, outputItems, cancellationTokenSource.Token)), Task.Run( async () => { @@ -76,7 +76,7 @@ public async Task AsyncQueueSkipsCancelledTasks() Assert.Equal(1, taskTwo.Result); } - private async Task ConsumeItems( + private async Task ConsumeItemsAsync( AsyncQueue inputQueue, ConcurrentBag outputItems, CancellationToken cancellationToken)