From d1ca49a1c3b63cbd735c520b2eb7f58cf4b46189 Mon Sep 17 00:00:00 2001 From: Adam Driscoll <adam.r.driscoll@gmail.com> Date: Wed, 6 Mar 2019 22:55:17 -0700 Subject: [PATCH 1/9] Add attach to local runspace. --- .../DebugAdapter/AttachRequest.cs | 2 + .../LanguageServer/GetRunspaceRequest.cs | 25 ++++++++++++ .../Server/DebugAdapter.cs | 39 +++++++++++++++---- .../Server/LanguageServer.cs | 33 ++++++++++++++++ 4 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 src/PowerShellEditorServices.Protocol/LanguageServer/GetRunspaceRequest.cs diff --git a/src/PowerShellEditorServices.Protocol/DebugAdapter/AttachRequest.cs b/src/PowerShellEditorServices.Protocol/DebugAdapter/AttachRequest.cs index e9734e002..d88feba73 100644 --- a/src/PowerShellEditorServices.Protocol/DebugAdapter/AttachRequest.cs +++ b/src/PowerShellEditorServices.Protocol/DebugAdapter/AttachRequest.cs @@ -21,5 +21,7 @@ public class AttachRequestArguments public string ProcessId { get; set; } public int RunspaceId { get; set; } + + public string LocalRunspaceId { get; set; } } } diff --git a/src/PowerShellEditorServices.Protocol/LanguageServer/GetRunspaceRequest.cs b/src/PowerShellEditorServices.Protocol/LanguageServer/GetRunspaceRequest.cs new file mode 100644 index 000000000..2b94f59ae --- /dev/null +++ b/src/PowerShellEditorServices.Protocol/LanguageServer/GetRunspaceRequest.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 Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol; + +namespace Microsoft.PowerShell.EditorServices.Protocol.LanguageServer +{ + public class GetRunspaceRequest + { + public static readonly + RequestType<object, GetRunspaceResponse[], object, object> Type = + RequestType<object, GetRunspaceResponse[], object, object>.Create("powerShell/getRunspace"); + } + + public class GetRunspaceResponse + { + public int Id { get; set; } + + public string Name { get; set; } + + public string Availability { get; set; } + } +} diff --git a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs index 69a720f8e..e617d8300 100644 --- a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs +++ b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs @@ -403,7 +403,7 @@ await requestContext.SendErrorAsync( _isRemoteAttach = true; } - if (int.TryParse(attachParams.ProcessId, out int processId) && (processId > 0)) + if ((int.TryParse(attachParams.ProcessId, out int processId) && (processId > 0)) || attachParams.ProcessId == "current") { PowerShellVersionDetails runspaceVersion = _editorSession.PowerShellContext.CurrentRunspace.PowerShellVersion; @@ -416,16 +416,18 @@ await requestContext.SendErrorAsync( return; } - await _editorSession.PowerShellContext.ExecuteScriptStringAsync( + if (attachParams.ProcessId != "current") { + await _editorSession.PowerShellContext.ExecuteScriptStringAsync( $"Enter-PSHostProcess -Id {processId}", errorMessages); - if (errorMessages.Length > 0) - { - await requestContext.SendErrorAsync( - $"Could not attach to process '{processId}'"); + if (errorMessages.Length > 0) + { + await requestContext.SendErrorAsync( + $"Could not attach to process '{processId}'"); - return; + return; + } } // Clear any existing breakpoints before proceeding @@ -435,7 +437,28 @@ await requestContext.SendErrorAsync( // will block the debug adapter initialization process. The // InitializedEvent will be sent as soon as the RunspaceChanged // event gets fired with the attached runspace. - int runspaceId = attachParams.RunspaceId > 0 ? attachParams.RunspaceId : 1; + + int runspaceId = 1; + if (string.IsNullOrEmpty(attachParams.LocalRunspaceId)) + { + runspaceId = attachParams.RunspaceId > 0 ? attachParams.RunspaceId : 1; + } + else if (int.TryParse(attachParams.LocalRunspaceId, out int localRunspaceId)) + { + runspaceId = localRunspaceId; + } + else + { + Logger.Write( + LogLevel.Error, + $"Attach request failed, '{attachParams.LocalRunspaceId}' is an invalid value for the processId."); + + await requestContext.SendErrorAsync( + "A positive integer must be specified for the LocalRunspaceId field."); + + return; + } + _waitingForAttach = true; Task nonAwaitedTask = _editorSession.PowerShellContext diff --git a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs index d325b2d65..19534ec42 100644 --- a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs +++ b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs @@ -18,6 +18,7 @@ using System.Linq; using System.Management.Automation; using System.Management.Automation.Language; +using System.Management.Automation.Runspaces; using System.Text; using System.Text.RegularExpressions; using System.Threading; @@ -161,6 +162,8 @@ public void Start() this.messageHandlers.SetRequestHandler(GetPSHostProcessesRequest.Type, this.HandleGetPSHostProcessesRequestAsync); this.messageHandlers.SetRequestHandler(CommentHelpRequest.Type, this.HandleCommentHelpRequestAsync); + this.messageHandlers.SetRequestHandler(GetRunspaceRequest.Type, this.HandleGetRunspaceRequestAsync); + // Initialize the extension service // TODO: This should be made awaited once Initialize is async! this.editorSession.ExtensionService.InitializeAsync( @@ -1218,6 +1221,36 @@ protected async Task HandleCommentHelpRequestAsync( await requestContext.SendResultAsync(result); } + protected async Task HandleGetRunspaceRequestAsync( + object noParams, + RequestContext<GetRunspaceResponse[]> requestContext) + { + var runspaceResponses = new List<GetRunspaceResponse>(); + + if (this.editorSession.PowerShellContext.LocalPowerShellVersion.Version.Major >= 5) + { + var psCommand = new PSCommand(); + psCommand.AddCommand("Get-Runspace"); + + var runspaces = await editorSession.PowerShellContext.ExecuteCommandAsync<PSObject>(psCommand); + if (runspaces != null) + { + foreach (dynamic p in runspaces) //.Select(m => m.BaseObject as Runspace)) + { + runspaceResponses.Add( + new GetRunspaceResponse + { + Id = p.Id, + Name = p.Name, + Availability = p.RunspaceAvailability.ToString() + }); + } + } + } + + await requestContext.SendResultAsync(runspaceResponses.ToArray()); + } + private bool IsQueryMatch(string query, string symbolName) { return symbolName.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0; From c794be4f03f58faf37a50ecd04c6075b7f791dc4 Mon Sep 17 00:00:00 2001 From: Adam Driscoll <adam.r.driscoll@gmail.com> Date: Wed, 6 Mar 2019 23:15:23 -0700 Subject: [PATCH 2/9] Remove comment. --- src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs index 19534ec42..850257cba 100644 --- a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs +++ b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs @@ -1235,7 +1235,7 @@ protected async Task HandleGetRunspaceRequestAsync( var runspaces = await editorSession.PowerShellContext.ExecuteCommandAsync<PSObject>(psCommand); if (runspaces != null) { - foreach (dynamic p in runspaces) //.Select(m => m.BaseObject as Runspace)) + foreach (dynamic p in runspaces) { runspaceResponses.Add( new GetRunspaceResponse From 384b91a684b9a5e314d8fb58e541ee5bca1e9c38 Mon Sep 17 00:00:00 2001 From: Adam Driscoll <adam.r.driscoll@gmail.com> Date: Fri, 8 Mar 2019 23:08:11 -0700 Subject: [PATCH 3/9] Use single runspaceId property. --- .../DebugAdapter/AttachRequest.cs | 4 +--- .../Server/DebugAdapter.cs | 12 ++++-------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/PowerShellEditorServices.Protocol/DebugAdapter/AttachRequest.cs b/src/PowerShellEditorServices.Protocol/DebugAdapter/AttachRequest.cs index d88feba73..24fff17cf 100644 --- a/src/PowerShellEditorServices.Protocol/DebugAdapter/AttachRequest.cs +++ b/src/PowerShellEditorServices.Protocol/DebugAdapter/AttachRequest.cs @@ -20,8 +20,6 @@ public class AttachRequestArguments public string ProcessId { get; set; } - public int RunspaceId { get; set; } - - public string LocalRunspaceId { get; set; } + public string RunspaceId { get; set; } } } diff --git a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs index e617d8300..c91c43795 100644 --- a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs +++ b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs @@ -439,22 +439,18 @@ await requestContext.SendErrorAsync( // event gets fired with the attached runspace. int runspaceId = 1; - if (string.IsNullOrEmpty(attachParams.LocalRunspaceId)) + if (int.TryParse(attachParams.RunspaceId, out runspaceId)) { - runspaceId = attachParams.RunspaceId > 0 ? attachParams.RunspaceId : 1; - } - else if (int.TryParse(attachParams.LocalRunspaceId, out int localRunspaceId)) - { - runspaceId = localRunspaceId; + runspaceId = runspaceId > 0 ? runspaceId : 1; } else { Logger.Write( LogLevel.Error, - $"Attach request failed, '{attachParams.LocalRunspaceId}' is an invalid value for the processId."); + $"Attach request failed, '{attachParams.RunspaceId}' is an invalid value for the processId."); await requestContext.SendErrorAsync( - "A positive integer must be specified for the LocalRunspaceId field."); + "A positive integer must be specified for the RunspaceId field."); return; } From 70a2e129c800fda94354e50dca1f1336befd2126 Mon Sep 17 00:00:00 2001 From: Adam Driscoll <adam.r.driscoll@gmail.com> Date: Mon, 11 Mar 2019 14:19:01 -0600 Subject: [PATCH 4/9] Remove unnecessary processId check. --- .../Server/DebugAdapter.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs index 8db925081..d9207f2ec 100644 --- a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs +++ b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs @@ -417,18 +417,16 @@ await requestContext.SendErrorAsync( return; } - if (attachParams.ProcessId != "current") { - await _editorSession.PowerShellContext.ExecuteScriptStringAsync( + await _editorSession.PowerShellContext.ExecuteScriptStringAsync( $"Enter-PSHostProcess -Id {processId}", errorMessages); - if (errorMessages.Length > 0) - { - await requestContext.SendErrorAsync( - $"Could not attach to process '{processId}'"); + if (errorMessages.Length > 0) + { + await requestContext.SendErrorAsync( + $"Could not attach to process '{processId}'"); - return; - } + return; } } else if (customPipeNameIsSet) From cbe5b183d7ea7450fc5ec8712850aa45b02176cb Mon Sep 17 00:00:00 2001 From: Adam Driscoll <adam.r.driscoll@gmail.com> Date: Mon, 11 Mar 2019 14:28:36 -0600 Subject: [PATCH 5/9] Factored if. --- .../Server/DebugAdapter.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs index d9207f2ec..66c0f10f0 100644 --- a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs +++ b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs @@ -471,11 +471,7 @@ await requestContext.SendErrorAsync( // event gets fired with the attached runspace. int runspaceId = 1; - if (int.TryParse(attachParams.RunspaceId, out runspaceId)) - { - runspaceId = runspaceId > 0 ? runspaceId : 1; - } - else + if (!int.TryParse(attachParams.RunspaceId, out runspaceId) || runspaceId <= 0) { Logger.Write( LogLevel.Error, From 28f3840a884d91bbdddd3e2eb12cab25fed81bdc Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt <tylerl0706@gmail.com> Date: Mon, 11 Mar 2019 14:35:40 -0600 Subject: [PATCH 6/9] Update src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs It's neat that we can just commit a suggestion. Co-Authored-By: adamdriscoll <adamdriscoll@users.noreply.github.com> --- src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs index 66c0f10f0..5db738b80 100644 --- a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs +++ b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs @@ -470,7 +470,7 @@ await requestContext.SendErrorAsync( // InitializedEvent will be sent as soon as the RunspaceChanged // event gets fired with the attached runspace. - int runspaceId = 1; + var runspaceId = 1; if (!int.TryParse(attachParams.RunspaceId, out runspaceId) || runspaceId <= 0) { Logger.Write( From 1d7900232f89581edfc77917288dd890952fdf25 Mon Sep 17 00:00:00 2001 From: Adam Driscoll <adam.r.driscoll@gmail.com> Date: Mon, 11 Mar 2019 14:39:18 -0600 Subject: [PATCH 7/9] Use runspace rather than dynamic. --- .../Server/LanguageServer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs index 850257cba..2bafee10a 100644 --- a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs +++ b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs @@ -1232,10 +1232,10 @@ protected async Task HandleGetRunspaceRequestAsync( var psCommand = new PSCommand(); psCommand.AddCommand("Get-Runspace"); - var runspaces = await editorSession.PowerShellContext.ExecuteCommandAsync<PSObject>(psCommand); + IEnumerable<Runspace> runspaces = await editorSession.PowerShellContext.ExecuteCommandAsync<Runspace>(psCommand); if (runspaces != null) { - foreach (dynamic p in runspaces) + foreach (var p in runspaces) { runspaceResponses.Add( new GetRunspaceResponse From 015c1ef538103bc59c5c10e99c24f04bfbc11094 Mon Sep 17 00:00:00 2001 From: Adam Driscoll <adam.r.driscoll@gmail.com> Date: Tue, 12 Mar 2019 10:20:46 -0600 Subject: [PATCH 8/9] Accept process ID for GetRunspaceRequest. --- .../Server/LanguageServer.cs | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs index 2bafee10a..f508a9288 100644 --- a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs +++ b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs @@ -1222,17 +1222,27 @@ protected async Task HandleCommentHelpRequestAsync( } protected async Task HandleGetRunspaceRequestAsync( - object noParams, + object processId, RequestContext<GetRunspaceResponse[]> requestContext) { var runspaceResponses = new List<GetRunspaceResponse>(); if (this.editorSession.PowerShellContext.LocalPowerShellVersion.Version.Major >= 5) { + if (processId == null) { + processId = "current"; + } + var psCommand = new PSCommand(); + + if (processId != null && processId.ToString() != "current") { + psCommand.AddCommand("Enter-PSHostProcess").AddParameter("Id", processId).AddStatement(); + } + psCommand.AddCommand("Get-Runspace"); - IEnumerable<Runspace> runspaces = await editorSession.PowerShellContext.ExecuteCommandAsync<Runspace>(psCommand); + StringBuilder sb = new StringBuilder(); + IEnumerable<Runspace> runspaces = await editorSession.PowerShellContext.ExecuteCommandAsync<Runspace>(psCommand, sb); if (runspaces != null) { foreach (var p in runspaces) @@ -1246,6 +1256,12 @@ protected async Task HandleGetRunspaceRequestAsync( }); } } + + if (processId != null && processId.ToString() != "current") { + var exitCommand = new PSCommand(); + exitCommand.AddCommand("Exit-PSHostProcess"); + await editorSession.PowerShellContext.ExecuteCommandAsync(exitCommand); + } } await requestContext.SendResultAsync(runspaceResponses.ToArray()); From bb984a0d2f6505319f209d1b612614e17a7ecbc6 Mon Sep 17 00:00:00 2001 From: Adam Driscoll <adam.r.driscoll@gmail.com> Date: Tue, 12 Mar 2019 19:30:31 -0600 Subject: [PATCH 9/9] Clean up. --- .../LanguageServer/GetRunspaceRequest.cs | 4 ++-- .../Server/LanguageServer.cs | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/PowerShellEditorServices.Protocol/LanguageServer/GetRunspaceRequest.cs b/src/PowerShellEditorServices.Protocol/LanguageServer/GetRunspaceRequest.cs index 2b94f59ae..e151aa7f0 100644 --- a/src/PowerShellEditorServices.Protocol/LanguageServer/GetRunspaceRequest.cs +++ b/src/PowerShellEditorServices.Protocol/LanguageServer/GetRunspaceRequest.cs @@ -10,8 +10,8 @@ namespace Microsoft.PowerShell.EditorServices.Protocol.LanguageServer public class GetRunspaceRequest { public static readonly - RequestType<object, GetRunspaceResponse[], object, object> Type = - RequestType<object, GetRunspaceResponse[], object, object>.Create("powerShell/getRunspace"); + RequestType<string, GetRunspaceResponse[], object, object> Type = + RequestType<string, GetRunspaceResponse[], object, object>.Create("powerShell/getRunspace"); } public class GetRunspaceResponse diff --git a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs index f508a9288..37e5ae742 100644 --- a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs +++ b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs @@ -1222,7 +1222,7 @@ protected async Task HandleCommentHelpRequestAsync( } protected async Task HandleGetRunspaceRequestAsync( - object processId, + string processId, RequestContext<GetRunspaceResponse[]> requestContext) { var runspaceResponses = new List<GetRunspaceResponse>(); @@ -1233,9 +1233,11 @@ protected async Task HandleGetRunspaceRequestAsync( processId = "current"; } + var isNotCurrentProcess = processId != null && processId != "current"; + var psCommand = new PSCommand(); - if (processId != null && processId.ToString() != "current") { + if (isNotCurrentProcess) { psCommand.AddCommand("Enter-PSHostProcess").AddParameter("Id", processId).AddStatement(); } @@ -1257,7 +1259,7 @@ protected async Task HandleGetRunspaceRequestAsync( } } - if (processId != null && processId.ToString() != "current") { + if (isNotCurrentProcess) { var exitCommand = new PSCommand(); exitCommand.AddCommand("Exit-PSHostProcess"); await editorSession.PowerShellContext.ExecuteCommandAsync(exitCommand);