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);