Skip to content

Commit 84a9fca

Browse files
committed
Refactor message pipeline to support two protocols
This change is a major refactoring of the JSON message pipeline that lives in PowerShellEditorServices.Transport.Stdio. A new language server protocol has been introduced which uses the JSON RPC message format. Previously oth the language server and debug adapter messages used the V8 message protocol but this change has brought the need for a modular message pipeline that can support multiple protocols. All message types have been completely redesigned and message handling functionality for all features has been moved into the PowerShellEditorServices.Host project.
1 parent ce7ea86 commit 84a9fca

File tree

127 files changed

+3658
-3291
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

127 files changed

+3658
-3291
lines changed
Lines changed: 374 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,374 @@
1+
using Microsoft.PowerShell.EditorServices.Protocol.DebugAdapter;
2+
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
3+
using Microsoft.PowerShell.EditorServices.Utility;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Threading.Tasks;
8+
9+
namespace Microsoft.PowerShell.EditorServices.Host
10+
{
11+
internal class DebugAdapter : IMessageProcessor
12+
{
13+
private MessageDispatcher<EditorSession> messageDispatcher;
14+
15+
public DebugAdapter()
16+
{
17+
this.messageDispatcher = new MessageDispatcher<EditorSession>();
18+
}
19+
20+
public void Initialize()
21+
{
22+
// Register all supported message types
23+
24+
this.AddRequestHandler(InitializeRequest.Type, this.HandleInitializeRequest);
25+
this.AddRequestHandler(LaunchRequest.Type, this.HandleLaunchRequest);
26+
this.AddRequestHandler(AttachRequest.Type, this.HandleAttachRequest);
27+
this.AddRequestHandler(DisconnectRequest.Type, this.HandleDisconnectRequest);
28+
29+
this.AddRequestHandler(SetBreakpointsRequest.Type, this.HandleSetBreakpointsRequest);
30+
this.AddRequestHandler(SetExceptionBreakpointsRequest.Type, this.HandleSetExceptionBreakpointsRequest);
31+
32+
this.AddRequestHandler(ContinueRequest.Type, this.HandleContinueRequest);
33+
this.AddRequestHandler(NextRequest.Type, this.HandleNextRequest);
34+
this.AddRequestHandler(StepInRequest.Type, this.HandleStepInRequest);
35+
this.AddRequestHandler(StepOutRequest.Type, this.HandleStepOutRequest);
36+
this.AddRequestHandler(PauseRequest.Type, this.HandlePauseRequest);
37+
38+
this.AddRequestHandler(ThreadsRequest.Type, this.HandleThreadsRequest);
39+
this.AddRequestHandler(StackTraceRequest.Type, this.HandleStackTraceRequest);
40+
this.AddRequestHandler(ScopesRequest.Type, this.HandleScopesRequest);
41+
this.AddRequestHandler(VariablesRequest.Type, this.HandleVariablesRequest);
42+
this.AddRequestHandler(SourceRequest.Type, this.HandleSourceRequest);
43+
this.AddRequestHandler(EvaluateRequest.Type, this.HandleEvaluateRequest);
44+
}
45+
46+
public void AddRequestHandler<TParams, TResult, TError>(
47+
RequestType<TParams, TResult, TError> requestType,
48+
Func<TParams, EditorSession, RequestContext<TResult, TError>, Task> requestHandler)
49+
{
50+
this.messageDispatcher.AddRequestHandler(
51+
requestType,
52+
requestHandler);
53+
}
54+
55+
public void AddEventHandler<TParams>(
56+
EventType<TParams> eventType,
57+
Func<TParams, EditorSession, EventContext, Task> eventHandler)
58+
{
59+
this.messageDispatcher.AddEventHandler(
60+
eventType,
61+
eventHandler);
62+
}
63+
64+
public async Task ProcessMessage(
65+
Message messageToProcess,
66+
EditorSession editorSession,
67+
MessageWriter messageWriter)
68+
{
69+
await this.messageDispatcher.DispatchMessage(
70+
messageToProcess,
71+
editorSession,
72+
messageWriter);
73+
}
74+
75+
#region Built-in Message Handlers
76+
77+
protected async Task HandleInitializeRequest(
78+
InitializeRequestArguments initializeParams,
79+
EditorSession editorSession,
80+
RequestContext<object, object> requestContext)
81+
{
82+
// Send the Initialized event first so that we get breakpoints
83+
await requestContext.SendEvent(
84+
InitializedEvent.Type,
85+
null);
86+
87+
// Now send the Initialize response to continue setup
88+
await requestContext.SendResult(new object());
89+
}
90+
91+
protected async Task HandleLaunchRequest(
92+
LaunchRequestArguments launchParams,
93+
EditorSession editorSession,
94+
RequestContext<object, object> requestContext)
95+
{
96+
// Execute the given PowerShell script and send the response.
97+
// Note that we aren't waiting for execution to complete here
98+
// because the debugger could stop while the script executes.
99+
editorSession.PowerShellSession
100+
.ExecuteScriptAtPath(launchParams.Program)
101+
.ContinueWith(
102+
async (t) =>
103+
{
104+
Logger.Write(LogLevel.Verbose, "Execution completed, terminating...");
105+
106+
await requestContext.SendEvent(
107+
TerminatedEvent.Type,
108+
null);
109+
110+
// TODO: Find a way to exit more gracefully!
111+
Environment.Exit(0);
112+
});
113+
114+
await requestContext.SendResult(null);
115+
}
116+
117+
protected Task HandleAttachRequest(
118+
AttachRequestArguments attachParams,
119+
EditorSession editorSession,
120+
RequestContext<object, object> requestContext)
121+
{
122+
// TODO: Implement this once we support attaching to processes
123+
throw new NotImplementedException();
124+
}
125+
126+
protected Task HandleDisconnectRequest(
127+
object disconnectParams,
128+
EditorSession editorSession,
129+
RequestContext<object, object> requestContext)
130+
{
131+
EventHandler<SessionStateChangedEventArgs> handler = null;
132+
133+
handler =
134+
async (o, e) =>
135+
{
136+
if (e.NewSessionState == PowerShellSessionState.Ready)
137+
{
138+
await requestContext.SendResult(null);
139+
editorSession.PowerShellSession.SessionStateChanged -= handler;
140+
141+
// TODO: Find a way to exit more gracefully!
142+
Environment.Exit(0);
143+
}
144+
};
145+
146+
editorSession.PowerShellSession.SessionStateChanged += handler;
147+
editorSession.PowerShellSession.AbortExecution();
148+
149+
return Task.FromResult(true);
150+
}
151+
152+
protected async Task HandleSetBreakpointsRequest(
153+
SetBreakpointsRequestArguments setBreakpointsParams,
154+
EditorSession editorSession,
155+
RequestContext<SetBreakpointsResponseBody, object> requestContext)
156+
{
157+
ScriptFile scriptFile =
158+
editorSession.Workspace.GetFile(
159+
setBreakpointsParams.Source.Path);
160+
161+
BreakpointDetails[] breakpoints =
162+
await editorSession.DebugService.SetBreakpoints(
163+
scriptFile,
164+
setBreakpointsParams.Lines);
165+
166+
await requestContext.SendResult(
167+
new SetBreakpointsResponseBody
168+
{
169+
Breakpoints =
170+
breakpoints
171+
.Select(Breakpoint.Create)
172+
.ToArray()
173+
});
174+
}
175+
176+
protected async Task HandleSetExceptionBreakpointsRequest(
177+
SetExceptionBreakpointsRequestArguments setExceptionBreakpointsParams,
178+
EditorSession editorSession,
179+
RequestContext<object, object> requestContext)
180+
{
181+
// TODO: Handle this appropriately
182+
183+
await requestContext.SendResult(null);
184+
}
185+
186+
protected async Task HandleContinueRequest(
187+
object continueParams,
188+
EditorSession editorSession,
189+
RequestContext<object, object> requestContext)
190+
{
191+
editorSession.DebugService.Continue();
192+
193+
await requestContext.SendResult(null);
194+
}
195+
196+
protected async Task HandleNextRequest(
197+
object nextParams,
198+
EditorSession editorSession,
199+
RequestContext<object, object> requestContext)
200+
{
201+
editorSession.DebugService.StepOver();
202+
203+
await requestContext.SendResult(null);
204+
}
205+
206+
protected Task HandlePauseRequest(
207+
object pauseParams,
208+
EditorSession editorSession,
209+
RequestContext<object, object> requestContext)
210+
{
211+
editorSession.DebugService.Break();
212+
213+
// This request is responded to by sending the "stopped" event
214+
return Task.FromResult(true);
215+
}
216+
217+
protected async Task HandleStepInRequest(
218+
object stepInParams,
219+
EditorSession editorSession,
220+
RequestContext<object, object> requestContext)
221+
{
222+
editorSession.DebugService.StepIn();
223+
224+
await requestContext.SendResult(null);
225+
}
226+
227+
protected async Task HandleStepOutRequest(
228+
object stepOutParams,
229+
EditorSession editorSession,
230+
RequestContext<object, object> requestContext)
231+
{
232+
editorSession.DebugService.StepOut();
233+
234+
await requestContext.SendResult(null);
235+
}
236+
237+
protected async Task HandleThreadsRequest(
238+
object threadsParams,
239+
EditorSession editorSession,
240+
RequestContext<ThreadsResponseBody, object> requestContext)
241+
{
242+
await requestContext.SendResult(
243+
new ThreadsResponseBody
244+
{
245+
Threads = new Thread[]
246+
{
247+
// TODO: What do I do with these?
248+
new Thread
249+
{
250+
Id = 1,
251+
Name = "Main Thread"
252+
}
253+
}
254+
});
255+
}
256+
257+
protected async Task HandleStackTraceRequest(
258+
StackTraceRequestArguments stackTraceParams,
259+
EditorSession editorSession,
260+
RequestContext<StackTraceResponseBody, object> requestContext)
261+
{
262+
StackFrameDetails[] stackFrames =
263+
editorSession.DebugService.GetStackFrames();
264+
265+
List<StackFrame> newStackFrames = new List<StackFrame>();
266+
267+
for (int i = 0; i < stackFrames.Length; i++)
268+
{
269+
// Create the new StackFrame object with an ID that can
270+
// be referenced back to the current list of stack frames
271+
newStackFrames.Add(
272+
StackFrame.Create(
273+
stackFrames[i],
274+
i + 1));
275+
}
276+
277+
await requestContext.SendResult(
278+
new StackTraceResponseBody
279+
{
280+
StackFrames = newStackFrames.ToArray()
281+
});
282+
}
283+
284+
protected async Task HandleScopesRequest(
285+
ScopesRequestArguments scopesParams,
286+
EditorSession editorSession,
287+
RequestContext<ScopesResponseBody, object> requestContext)
288+
{
289+
VariableScope[] variableScopes =
290+
editorSession.DebugService.GetVariableScopes(
291+
scopesParams.FrameId);
292+
293+
await requestContext.SendResult(
294+
new ScopesResponseBody
295+
{
296+
Scopes =
297+
variableScopes
298+
.Select(Scope.Create)
299+
.ToArray()
300+
});
301+
}
302+
303+
protected async Task HandleVariablesRequest(
304+
VariablesRequestArguments variablesParams,
305+
EditorSession editorSession,
306+
RequestContext<VariablesResponseBody, object> requestContext)
307+
{
308+
VariableDetails[] variables =
309+
editorSession.DebugService.GetVariables(
310+
variablesParams.VariablesReference);
311+
312+
VariablesResponseBody variablesResponse = null;
313+
314+
try
315+
{
316+
variablesResponse = new VariablesResponseBody
317+
{
318+
Variables =
319+
variables
320+
.Select(Variable.Create)
321+
.ToArray()
322+
};
323+
}
324+
catch (Exception)
325+
{
326+
// TODO: This shouldn't be so broad
327+
}
328+
329+
await requestContext.SendResult(variablesResponse);
330+
}
331+
332+
protected Task HandleSourceRequest(
333+
SourceRequestArguments sourceParams,
334+
EditorSession editorSession,
335+
RequestContext<SourceResponseBody, object> requestContext)
336+
{
337+
// TODO: Implement this message. For now, doesn't seem to
338+
// be a problem that it's missing.
339+
340+
return Task.FromResult(true);
341+
}
342+
343+
protected async Task HandleEvaluateRequest(
344+
EvaluateRequestArguments evaluateParams,
345+
EditorSession editorSession,
346+
RequestContext<EvaluateResponseBody, object> requestContext)
347+
{
348+
VariableDetails result =
349+
await editorSession.DebugService.EvaluateExpression(
350+
evaluateParams.Expression,
351+
evaluateParams.FrameId);
352+
353+
string valueString = null;
354+
int variableId = 0;
355+
356+
if (result != null)
357+
{
358+
valueString = result.ValueString;
359+
variableId =
360+
result.IsExpandable ?
361+
result.Id : 0;
362+
}
363+
364+
await requestContext.SendResult(
365+
new EvaluateResponseBody
366+
{
367+
Result = valueString,
368+
VariablesReference = variableId
369+
});
370+
}
371+
372+
#endregion
373+
}
374+
}
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
44
//
55

6+
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
67
using System.Threading.Tasks;
78

8-
namespace Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol
9+
namespace Microsoft.PowerShell.EditorServices.Host
910
{
1011
/// <summary>
1112
/// Provides an interface for classes that can process an incoming
12-
/// message of a given type.
13+
/// message of some type.
1314
/// </summary>
1415
public interface IMessageProcessor
1516
{
@@ -19,6 +20,7 @@ public interface IMessageProcessor
1920
/// <param name="editorSession"></param>
2021
/// <param name="messageWriter"></param>
2122
Task ProcessMessage(
23+
Message messageToProcess,
2224
EditorSession editorSession,
2325
MessageWriter messageWriter);
2426
}

0 commit comments

Comments
 (0)