Skip to content

Commit f8ed477

Browse files
committed
Fixes #173 - implementation for function breakpoints
This commit also introduces support for the configurationDone request. In fact, due to the order in which set*Breakpoints requests and the launch request come in, we now defer the execution of the script until we receive the configurationDone request. When we executed from the launch request, the debug host would appear to hang. It did not like executing Set/Remove-PSBreakpoint commands when the host debugger was running (not in a stopped state) AFAICT. This commit has a known issue with VSCode mixing up which breakpoint has a condition associated with it when you add/remove a breakpoint (see microsoft/vscode#3558). The next commit will fix this issue.
1 parent 639aba2 commit f8ed477

File tree

15 files changed

+494
-103
lines changed

15 files changed

+494
-103
lines changed

src/PowerShellEditorServices.Protocol/Client/DebugAdapterClientBase.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@ public DebugAdapterClient(ChannelBase clientChannel)
1717
{
1818
}
1919

20-
public Task LaunchScript(string scriptFilePath)
20+
public async Task LaunchScript(string scriptFilePath)
2121
{
22-
return this.SendRequest(
22+
await this.SendRequest(
2323
LaunchRequest.Type,
24-
new LaunchRequestArguments
25-
{
24+
new LaunchRequestArguments {
2625
Program = scriptFilePath
2726
});
27+
28+
await this.SendRequest(ConfigurationDoneRequest.Type, null);
2829
}
2930

3031
protected override async Task OnStart()

src/PowerShellEditorServices.Protocol/DebugAdapter/Breakpoint.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public class Breakpoint
2121

2222
public string Source { get; set; }
2323

24-
public int Line { get; set; }
24+
public int? Line { get; set; }
2525

2626
public int? Column { get; set; }
2727

@@ -41,5 +41,14 @@ public static Breakpoint Create(
4141
Column = breakpointDetails.ColumnNumber
4242
};
4343
}
44+
45+
public static Breakpoint Create(
46+
FunctionBreakpointDetails breakpointDetails)
47+
{
48+
return new Breakpoint {
49+
Verified = breakpointDetails.Verified,
50+
Message = breakpointDetails.Message
51+
};
52+
}
4453
}
4554
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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.DebugAdapter
9+
{
10+
public class ConfigurationDoneRequest
11+
{
12+
public static readonly
13+
RequestType<object, object> Type =
14+
RequestType<object, object>.Create("configurationDone");
15+
}
16+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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.DebugAdapter
9+
{
10+
public class SetFunctionBreakpointsRequest
11+
{
12+
public static readonly
13+
RequestType<SetFunctionBreakpointsRequestArguments, SetBreakpointsResponseBody> Type =
14+
RequestType<SetFunctionBreakpointsRequestArguments, SetBreakpointsResponseBody>.Create("setFunctionBreakpoints");
15+
}
16+
17+
public class SetFunctionBreakpointsRequestArguments
18+
{
19+
public FunctionBreakpoint[] Breakpoints { get; set; }
20+
}
21+
22+
public class FunctionBreakpoint
23+
{
24+
/// <summary>
25+
/// Gets or sets the name of the function to break on when it is invoked.
26+
/// </summary>
27+
public string Name { get; set; }
28+
29+
public string Condition { get; set; }
30+
}
31+
}

src/PowerShellEditorServices.Protocol/PowerShellEditorServices.Protocol.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@
5050
<ItemGroup>
5151
<Compile Include="DebugAdapter\AttachRequest.cs" />
5252
<Compile Include="DebugAdapter\Breakpoint.cs" />
53+
<Compile Include="DebugAdapter\ConfigurationDoneRequest.cs" />
5354
<Compile Include="DebugAdapter\ContinueRequest.cs" />
55+
<Compile Include="DebugAdapter\SetFunctionBreakpointsRequest.cs" />
5456
<Compile Include="LanguageServer\FindModuleRequest.cs" />
5557
<Compile Include="LanguageServer\InstallModuleRequest.cs" />
5658
<Compile Include="MessageProtocol\IMessageSender.cs" />

src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ public class DebugAdapter : DebugAdapterBase
2020
{
2121
private EditorSession editorSession;
2222
private OutputDebouncer outputDebouncer;
23+
private string scriptPathToLaunch;
24+
private string arguments;
2325

2426
public DebugAdapter() : this(new StdioServerChannel())
2527
{
@@ -42,10 +44,12 @@ protected override void Initialize()
4244

4345
this.SetRequestHandler(LaunchRequest.Type, this.HandleLaunchRequest);
4446
this.SetRequestHandler(AttachRequest.Type, this.HandleAttachRequest);
47+
this.SetRequestHandler(ConfigurationDoneRequest.Type, this.HandleConfigurationDoneRequest);
4548
this.SetRequestHandler(DisconnectRequest.Type, this.HandleDisconnectRequest);
4649

4750
this.SetRequestHandler(SetBreakpointsRequest.Type, this.HandleSetBreakpointsRequest);
4851
this.SetRequestHandler(SetExceptionBreakpointsRequest.Type, this.HandleSetExceptionBreakpointsRequest);
52+
this.SetRequestHandler(SetFunctionBreakpointsRequest.Type, this.HandleSetFunctionBreakpointsRequest);
4953

5054
this.SetRequestHandler(ContinueRequest.Type, this.HandleContinueRequest);
5155
this.SetRequestHandler(NextRequest.Type, this.HandleNextRequest);
@@ -110,12 +114,35 @@ protected async Task HandleLaunchRequest(
110114
Logger.Write(LogLevel.Verbose, "Script arguments are: " + arguments);
111115
}
112116

117+
// NOTE: We don't actually launch the script in response to this
118+
// request. We wait until we receive the configurationDone request
119+
// to actually launch the script under the debugger. This gives
120+
// us and VSCode a chance to finish configuring all the types of
121+
// breakpoints.
122+
this.scriptPathToLaunch = launchParams.Program;
123+
this.arguments = arguments;
124+
125+
await requestContext.SendResult(null);
126+
}
127+
128+
protected Task HandleAttachRequest(
129+
AttachRequestArguments attachParams,
130+
RequestContext<object> requestContext)
131+
{
132+
// TODO: Implement this once we support attaching to processes
133+
throw new NotImplementedException();
134+
}
135+
136+
protected async Task HandleConfigurationDoneRequest(
137+
object args,
138+
RequestContext<object> requestContext)
139+
{
113140
// Execute the given PowerShell script and send the response.
114141
// Note that we aren't waiting for execution to complete here
115142
// because the debugger could stop while the script executes.
116143
Task executeTask =
117144
editorSession.PowerShellContext
118-
.ExecuteScriptAtPath(launchParams.Program, arguments)
145+
.ExecuteScriptAtPath(this.scriptPathToLaunch, this.arguments)
119146
.ContinueWith(
120147
async (t) => {
121148
Logger.Write(LogLevel.Verbose, "Execution completed, terminating...");
@@ -131,14 +158,6 @@ await requestContext.SendEvent(
131158
await requestContext.SendResult(null);
132159
}
133160

134-
protected Task HandleAttachRequest(
135-
AttachRequestArguments attachParams,
136-
RequestContext<object> requestContext)
137-
{
138-
// TODO: Implement this once we support attaching to processes
139-
throw new NotImplementedException();
140-
}
141-
142161
protected Task HandleDisconnectRequest(
143162
object disconnectParams,
144163
RequestContext<object> requestContext)
@@ -198,6 +217,32 @@ await requestContext.SendResult(
198217
});
199218
}
200219

220+
protected async Task HandleSetFunctionBreakpointsRequest(
221+
SetFunctionBreakpointsRequestArguments setBreakpointsParams,
222+
RequestContext<SetBreakpointsResponseBody> requestContext)
223+
{
224+
var breakpointDetails = new FunctionBreakpointDetails[setBreakpointsParams.Breakpoints.Length];
225+
for (int i = 0; i < breakpointDetails.Length; i++)
226+
{
227+
FunctionBreakpoint funcBreakpoint = setBreakpointsParams.Breakpoints[i];
228+
breakpointDetails[i] = FunctionBreakpointDetails.Create(
229+
funcBreakpoint.Name,
230+
funcBreakpoint.Condition);
231+
}
232+
233+
FunctionBreakpointDetails[] breakpoints =
234+
await editorSession.DebugService.SetFunctionBreakpoints(
235+
breakpointDetails);
236+
237+
await requestContext.SendResult(
238+
new SetBreakpointsResponseBody {
239+
Breakpoints =
240+
breakpoints
241+
.Select(Protocol.DebugAdapter.Breakpoint.Create)
242+
.ToArray()
243+
});
244+
}
245+
201246
protected async Task HandleSetExceptionBreakpointsRequest(
202247
SetExceptionBreakpointsRequestArguments setExceptionBreakpointsParams,
203248
RequestContext<object> requestContext)

src/PowerShellEditorServices.Protocol/Server/DebugAdapterBase.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ await requestContext.SendEvent(
6363
await requestContext.SendResult(
6464
new InitializeResponseBody
6565
{
66+
SupportsConfigurationDoneRequest = true,
6667
SupportsConditionalBreakpoints = true,
68+
SupportsFunctionBreakpoints = true
6769
});
6870
}
6971
}

src/PowerShellEditorServices/Debugging/BreakpointDetails.cs

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,8 @@ namespace Microsoft.PowerShell.EditorServices
1313
/// Provides details about a breakpoint that is set in the
1414
/// PowerShell debugger.
1515
/// </summary>
16-
public class BreakpointDetails
16+
public class BreakpointDetails : BreakpointDetailsBase
1717
{
18-
/// <summary>
19-
/// Gets or sets a boolean indicator that if true, breakpoint could be set
20-
/// (but not necessarily at the desired location).
21-
/// </summary>
22-
public bool Verified { get; set; }
23-
24-
/// <summary>
25-
/// Gets or set an optional message about the state of the breakpoint. This is shown to the user
26-
/// and can be used to explain why a breakpoint could not be verified.
27-
/// </summary>
28-
public string Message { get; set; }
29-
3018
/// <summary>
3119
/// Gets the source where the breakpoint is located. Used only for debug purposes.
3220
/// </summary>
@@ -42,11 +30,6 @@ public class BreakpointDetails
4230
/// </summary>
4331
public int? ColumnNumber { get; private set; }
4432

45-
/// <summary>
46-
/// Gets the breakpoint condition string.
47-
/// </summary>
48-
public string Condition { get; private set; }
49-
5033
private BreakpointDetails()
5134
{
5235
}
@@ -91,7 +74,7 @@ public static BreakpointDetails Create(Breakpoint breakpoint)
9174
if (lineBreakpoint == null)
9275
{
9376
throw new ArgumentException(
94-
"Expected breakpoint type:" + breakpoint.GetType().Name);
77+
"Unexpected breakpoint type: " + breakpoint.GetType().Name);
9578
}
9679

9780
var breakpointDetails = new BreakpointDetails
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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+
namespace Microsoft.PowerShell.EditorServices
7+
{
8+
/// <summary>
9+
/// Provides details about a breakpoint that is set in the
10+
/// PowerShell debugger.
11+
/// </summary>
12+
public abstract class BreakpointDetailsBase
13+
{
14+
/// <summary>
15+
/// Gets or sets a boolean indicator that if true, breakpoint could be set
16+
/// (but not necessarily at the desired location).
17+
/// </summary>
18+
public bool Verified { get; set; }
19+
20+
/// <summary>
21+
/// Gets or set an optional message about the state of the breakpoint. This is shown to the user
22+
/// and can be used to explain why a breakpoint could not be verified.
23+
/// </summary>
24+
public string Message { get; set; }
25+
26+
/// <summary>
27+
/// Gets the breakpoint condition string.
28+
/// </summary>
29+
public string Condition { get; protected set; }
30+
}
31+
}

0 commit comments

Comments
 (0)