Skip to content

Commit 56cf0df

Browse files
author
Kapil Borle
committed
Fix merge conflict with develop
2 parents 8cf2c42 + a4db350 commit 56cf0df

File tree

12 files changed

+337
-71
lines changed

12 files changed

+337
-71
lines changed

README.md

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,10 @@ how to use this project. You can also read our plans for future feature developm
3939

4040
## Cloning the Code
4141

42-
To clone the repository and initialize all the submodules at once you can run:
42+
To clone the repository execute:
4343

4444
```
45-
git clone --recursive https://github.com/PowerShell/PowerShellEditorServices.git
46-
```
47-
48-
If you have already cloned the repository without `--recursive` option, you can run following commands to initialize the submodules:
49-
50-
```
51-
git submodule init
52-
git submodule update
45+
git clone https://github.com/PowerShell/PowerShellEditorServices.git
5346
```
5447

5548
## Contributions Welcome!
@@ -65,4 +58,4 @@ contribute code, documentation, tests, or bug reports, please read our [Contribu
6558

6659
## License
6760

68-
This project is [licensed under the MIT License](LICENSE).
61+
This project is [licensed under the MIT License](LICENSE).

src/PowerShellEditorServices.Protocol/DebugAdapter/AttachRequest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public class AttachRequestArguments
1818
{
1919
public string ComputerName { get; set; }
2020

21-
public int ProcessId { get; set; }
21+
public string ProcessId { get; set; }
2222

2323
public int RunspaceId { get; set; }
2424
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// Copyright (c) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
4+
//
5+
6+
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
7+
8+
namespace Microsoft.PowerShell.EditorServices.Protocol.LanguageServer
9+
{
10+
public class GetPSHostProcessesRequest
11+
{
12+
public static readonly
13+
RequestType<object, GetPSHostProcessesResponse[]> Type =
14+
RequestType<object, GetPSHostProcessesResponse[]>.Create("powerShell/getPSHostProcesses");
15+
}
16+
17+
public class GetPSHostProcessesResponse
18+
{
19+
public string ProcessName { get; set; }
20+
21+
public int ProcessId { get; set; }
22+
23+
public string AppDomainName { get; set; }
24+
25+
public string MainWindowTitle { get; set; }
26+
}
27+
}

src/PowerShellEditorServices.Protocol/MessageProtocol/Serializers/V8MessageSerializer.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,11 @@ public JObject SerializeMessage(Message message)
3434
}
3535
else if (message.MessageType == MessageType.Response)
3636
{
37+
int messageId = 0;
38+
int.TryParse(message.Id, out messageId);
39+
3740
messageObject.Add("type", JToken.FromObject("response"));
38-
messageObject.Add("request_seq", JToken.FromObject(message.Id));
41+
messageObject.Add("request_seq", JToken.FromObject(messageId));
3942
messageObject.Add("command", message.Method);
4043

4144
if (message.Error != null)

src/PowerShellEditorServices.Protocol/PowerShellEditorServices.Protocol.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
<Compile Include="DebugAdapter\ContinueRequest.cs" />
5757
<Compile Include="DebugAdapter\SetFunctionBreakpointsRequest.cs" />
5858
<Compile Include="DebugAdapter\SetVariableRequest.cs" />
59+
<Compile Include="LanguageServer\GetPSHostProcessesRequest.cs" />
5960
<Compile Include="LanguageServer\GetPSSARulesRequest.cs" />
6061
<Compile Include="LanguageServer\EditorCommands.cs" />
6162
<Compile Include="LanguageServer\CodeAction.cs" />

src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,22 @@ protected async Task HandleAttachRequest(
207207
AttachRequestArguments attachParams,
208208
RequestContext<object> requestContext)
209209
{
210+
// If there are no host processes to attach to or the user cancels selection, we get a null for the process id.
211+
// This is not an error, just a request to stop the original "attach to" request.
212+
// Testing against "undefined" is a HACK because I don't know how to make "Cancel" on quick pick loading
213+
// to cancel on the VSCode side without sending an attachRequest with processId set to "undefined".
214+
if (string.IsNullOrEmpty(attachParams.ProcessId) || (attachParams.ProcessId == "undefined"))
215+
{
216+
Logger.Write(
217+
LogLevel.Normal,
218+
$"Attach request aborted, received {attachParams.ProcessId} for processId.");
219+
220+
await requestContext.SendError(
221+
"User aborted attach to PowerShell host process.");
222+
223+
return;
224+
}
225+
210226
StringBuilder errorMessages = new StringBuilder();
211227

212228
if (attachParams.ComputerName != null)
@@ -235,7 +251,8 @@ await requestContext.SendError(
235251
}
236252
}
237253

238-
if (attachParams.ProcessId > 0)
254+
int processId;
255+
if (int.TryParse(attachParams.ProcessId, out processId) && (processId > 0))
239256
{
240257
PowerShellVersionDetails runspaceVersion =
241258
this.editorSession.PowerShellContext.CurrentRunspace.PowerShellVersion;
@@ -249,13 +266,13 @@ await requestContext.SendError(
249266
}
250267

251268
await this.editorSession.PowerShellContext.ExecuteScriptString(
252-
$"Enter-PSHostProcess -Id {attachParams.ProcessId}",
269+
$"Enter-PSHostProcess -Id {processId}",
253270
errorMessages);
254271

255272
if (errorMessages.Length > 0)
256273
{
257274
await requestContext.SendError(
258-
$"Could not attach to process '{attachParams.ProcessId}'");
275+
$"Could not attach to process '{processId}'");
259276

260277
return;
261278
}
@@ -272,6 +289,10 @@ await requestContext.SendError(
272289
}
273290
else
274291
{
292+
Logger.Write(
293+
LogLevel.Error,
294+
$"Attach request failed, '{attachParams.ProcessId}' is an invalid value for the processId.");
295+
275296
await requestContext.SendError(
276297
"A positive integer must be specified for the processId field.");
277298

src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ protected override void Initialize()
126126

127127
this.SetRequestHandler(ScriptFileMarkersRequest.Type, this.HandleScriptFileMarkersRequest);
128128

129+
this.SetRequestHandler(GetPSHostProcessesRequest.Type, this.HandleGetPSHostProcessesRequest);
130+
129131
// Initialize the extension service
130132
// TODO: This should be made awaited once Initialize is async!
131133
this.editorSession.ExtensionService.Initialize(
@@ -939,6 +941,42 @@ await requestContext.SendResult(
939941
this.editorSession.PowerShellContext.LocalPowerShellVersion));
940942
}
941943

944+
protected async Task HandleGetPSHostProcessesRequest(
945+
object noParams,
946+
RequestContext<GetPSHostProcessesResponse[]> requestContext)
947+
{
948+
var psHostProcesses = new List<GetPSHostProcessesResponse>();
949+
950+
if (this.editorSession.PowerShellContext.LocalPowerShellVersion.Version.Major >= 5)
951+
{
952+
int processId = System.Diagnostics.Process.GetCurrentProcess().Id;
953+
var psCommand = new PSCommand();
954+
psCommand.AddCommand("Get-PSHostProcessInfo");
955+
psCommand.AddCommand("Where-Object")
956+
.AddParameter("Property", "ProcessId")
957+
.AddParameter("NE")
958+
.AddParameter("Value", processId.ToString());
959+
960+
var processes = await editorSession.PowerShellContext.ExecuteCommand<PSObject>(psCommand);
961+
if (processes != null)
962+
{
963+
foreach (dynamic p in processes)
964+
{
965+
psHostProcesses.Add(
966+
new GetPSHostProcessesResponse
967+
{
968+
ProcessName = p.ProcessName,
969+
ProcessId = p.ProcessId,
970+
AppDomainName = p.AppDomainName,
971+
MainWindowTitle = p.MainWindowTitle
972+
});
973+
}
974+
}
975+
}
976+
977+
await requestContext.SendResult(psHostProcesses.ToArray());
978+
}
979+
942980
private bool IsQueryMatch(string query, string symbolName)
943981
{
944982
return symbolName.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;

src/PowerShellEditorServices/Debugging/DebugService.cs

Lines changed: 97 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
using System.Threading.Tasks;
1313
using Microsoft.PowerShell.EditorServices.Debugging;
1414
using Microsoft.PowerShell.EditorServices.Utility;
15-
using System.IO;
1615
using Microsoft.PowerShell.EditorServices.Session;
1716

1817
namespace Microsoft.PowerShell.EditorServices
@@ -26,6 +25,7 @@ public class DebugService
2625
#region Fields
2726

2827
private const string PsesGlobalVariableNamePrefix = "__psEditorServices_";
28+
private const string TemporaryScriptFileName = "TemporaryScript.ps1";
2929

3030
private PowerShellContext powerShellContext;
3131
private RemoteFileManager remoteFileManager;
@@ -35,6 +35,7 @@ public class DebugService
3535
new Dictionary<string, List<Breakpoint>>();
3636

3737
private int nextVariableId;
38+
private string temporaryScriptListingPath;
3839
private List<VariableDetailsBase> variables;
3940
private VariableContainerDetails globalScopeVariables;
4041
private VariableContainerDetails scriptScopeVariables;
@@ -93,8 +94,8 @@ public DebugService(
9394
/// <param name="clearExisting">If true, causes all existing breakpoints to be cleared before setting new ones.</param>
9495
/// <returns>An awaitable Task that will provide details about the breakpoints that were set.</returns>
9596
public async Task<BreakpointDetails[]> SetLineBreakpoints(
96-
ScriptFile scriptFile,
97-
BreakpointDetails[] breakpoints,
97+
ScriptFile scriptFile,
98+
BreakpointDetails[] breakpoints,
9899
bool clearExisting = true)
99100
{
100101
var resultBreakpointDetails = new List<BreakpointDetails>();
@@ -111,22 +112,32 @@ public async Task<BreakpointDetails[]> SetLineBreakpoints(
111112
if (this.powerShellContext.CurrentRunspace.Location == RunspaceLocation.Remote &&
112113
this.remoteFileManager != null)
113114
{
114-
string mappedPath =
115-
this.remoteFileManager.GetMappedPath(
116-
scriptPath,
117-
this.powerShellContext.CurrentRunspace);
118-
119-
if (mappedPath == null)
115+
if (!this.remoteFileManager.IsUnderRemoteTempPath(scriptPath))
120116
{
121117
Logger.Write(
122-
LogLevel.Error,
123-
$"Could not map local path '{scriptPath}' to a remote path.");
118+
LogLevel.Verbose,
119+
$"Could not set breakpoints for local path '{scriptPath}' in a remote session.");
124120

125121
return resultBreakpointDetails.ToArray();
126122
}
127123

124+
string mappedPath =
125+
this.remoteFileManager.GetMappedPath(
126+
scriptPath,
127+
this.powerShellContext.CurrentRunspace);
128+
128129
scriptPath = mappedPath;
129130
}
131+
else if (
132+
this.temporaryScriptListingPath != null &&
133+
this.temporaryScriptListingPath.Equals(scriptPath, StringComparison.CurrentCultureIgnoreCase))
134+
{
135+
Logger.Write(
136+
LogLevel.Verbose,
137+
$"Could not set breakpoint on temporary script listing path '{scriptPath}'.");
138+
139+
return resultBreakpointDetails.ToArray();
140+
}
130141

131142
// Fix for issue #123 - file paths that contain wildcard chars [ and ] need to
132143
// quoted and have those wildcard chars escaped.
@@ -622,7 +633,7 @@ private async Task ClearCommandBreakpoints()
622633
await this.powerShellContext.ExecuteCommand<object>(psCommand);
623634
}
624635

625-
private async Task FetchStackFramesAndVariables()
636+
private async Task FetchStackFramesAndVariables(string scriptNameOverride)
626637
{
627638
this.nextVariableId = VariableDetailsBase.FirstVariableId;
628639
this.variables = new List<VariableDetailsBase>();
@@ -633,7 +644,7 @@ private async Task FetchStackFramesAndVariables()
633644
// Must retrieve global/script variales before stack frame variables
634645
// as we check stack frame variables against globals.
635646
await FetchGlobalAndScriptVariables();
636-
await FetchStackFrames();
647+
await FetchStackFrames(scriptNameOverride);
637648
}
638649

639650
private async Task FetchGlobalAndScriptVariables()
@@ -750,7 +761,7 @@ private bool AddToAutoVariables(PSObject psvariable, string scope)
750761
return true;
751762
}
752763

753-
private async Task FetchStackFrames()
764+
private async Task FetchStackFrames(string scriptNameOverride)
754765
{
755766
PSCommand psCommand = new PSCommand();
756767

@@ -782,7 +793,12 @@ private async Task FetchStackFrames()
782793
StackFrameDetails.Create(callStackFrames[i], autoVariables, localVariables);
783794

784795
string stackFrameScriptPath = this.stackFrameDetails[i].ScriptPath;
785-
if (this.powerShellContext.CurrentRunspace.Location == RunspaceLocation.Remote &&
796+
if (scriptNameOverride != null &&
797+
string.Equals(stackFrameScriptPath, StackFrameDetails.NoFileScriptPath))
798+
{
799+
this.stackFrameDetails[i].ScriptPath = scriptNameOverride;
800+
}
801+
else if (this.powerShellContext.CurrentRunspace.Location == RunspaceLocation.Remote &&
786802
this.remoteFileManager != null &&
787803
!string.Equals(stackFrameScriptPath, StackFrameDetails.NoFileScriptPath))
788804
{
@@ -979,6 +995,25 @@ private string FormatInvalidBreakpointConditionMessage(string condition, string
979995
return $"'{condition}' is not a valid PowerShell expression. {message}";
980996
}
981997

998+
private string TrimScriptListingLine(PSObject scriptLineObj, ref int prefixLength)
999+
{
1000+
string scriptLine = scriptLineObj.ToString();
1001+
1002+
if (!string.IsNullOrWhiteSpace(scriptLine))
1003+
{
1004+
if (prefixLength == 0)
1005+
{
1006+
// The prefix is a padded integer ending with ':', an asterisk '*'
1007+
// if this is the current line, and one character of padding
1008+
prefixLength = scriptLine.IndexOf(':') + 2;
1009+
}
1010+
1011+
return scriptLine.Substring(prefixLength);
1012+
}
1013+
1014+
return null;
1015+
}
1016+
9821017
#endregion
9831018

9841019
#region Events
@@ -990,11 +1025,56 @@ private string FormatInvalidBreakpointConditionMessage(string condition, string
9901025

9911026
private async void OnDebuggerStop(object sender, DebuggerStopEventArgs e)
9921027
{
1028+
bool noScriptName = false;
1029+
string localScriptPath = e.InvocationInfo.ScriptName;
1030+
1031+
// If there's no ScriptName, get the "list" of the current source
1032+
if (this.remoteFileManager != null && string.IsNullOrEmpty(localScriptPath))
1033+
{
1034+
// Get the current script listing and create the buffer
1035+
PSCommand command = new PSCommand();
1036+
command.AddScript($"list 1 {int.MaxValue}");
1037+
1038+
IEnumerable<PSObject> scriptListingLines =
1039+
await this.powerShellContext.ExecuteCommand<PSObject>(
1040+
command, false, false);
1041+
1042+
if (scriptListingLines != null)
1043+
{
1044+
int linePrefixLength = 0;
1045+
1046+
string scriptListing =
1047+
string.Join(
1048+
Environment.NewLine,
1049+
scriptListingLines
1050+
.Select(o => this.TrimScriptListingLine(o, ref linePrefixLength))
1051+
.Where(s => s != null));
1052+
1053+
this.temporaryScriptListingPath =
1054+
this.remoteFileManager.CreateTemporaryFile(
1055+
TemporaryScriptFileName,
1056+
scriptListing,
1057+
this.powerShellContext.CurrentRunspace);
1058+
1059+
localScriptPath =
1060+
this.temporaryScriptListingPath
1061+
?? StackFrameDetails.NoFileScriptPath;
1062+
1063+
noScriptName = localScriptPath != null;
1064+
}
1065+
else
1066+
{
1067+
Logger.Write(
1068+
LogLevel.Warning,
1069+
$"Could not load script context");
1070+
}
1071+
}
1072+
9931073
// Get call stack and variables.
994-
await this.FetchStackFramesAndVariables();
1074+
await this.FetchStackFramesAndVariables(
1075+
noScriptName ? localScriptPath : null);
9951076

9961077
// If this is a remote connection, get the file content
997-
string localScriptPath = e.InvocationInfo.ScriptName;
9981078
if (this.powerShellContext.CurrentRunspace.Location == RunspaceLocation.Remote &&
9991079
this.remoteFileManager != null)
10001080
{

0 commit comments

Comments
 (0)