Skip to content

Commit 746c6fa

Browse files
committed
Merge pull request #26 from PowerShell/daviwil/debug-support
Introducing script debugging support
2 parents 237dc0e + f6ffea4 commit 746c6fa

File tree

172 files changed

+5122
-1024
lines changed

Some content is hidden

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

172 files changed

+5122
-1024
lines changed

.gitignore

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,6 @@ ClientBin/
4444
csx/
4545

4646
# Don't include ScriptAnalyzer binaries
47-
PowerShellEditorServices/Microsoft.Windows.PowerShell.ScriptAnalyzer.dll
48-
PowerShellEditorServices/Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.dll
49-
PowerShellEditorServices/PSScriptAnalyzer.psd1
50-
PowerShellEditorServices/ScriptAnalyzer.format.ps1xml
51-
PowerShellEditorServices/ScriptAnalyzer.types.ps1xml
47+
PowerShellEditorServices/**
48+
5249

PowerShellEditorServices.sln

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio 2013
4-
VisualStudioVersion = 12.0.31101.0
4+
VisualStudioVersion = 12.0.40629.0
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F594E7FD-1E72-4E51-A496-B019C2BA3180}"
77
EndProject
@@ -34,6 +34,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{E51470
3434
EndProject
3535
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerShellEditorServices.Test.Shared", "test\PowerShellEditorServices.Test.Shared\PowerShellEditorServices.Test.Shared.csproj", "{6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA}"
3636
EndProject
37+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{E2316F5C-A551-4A8D-8103-E42CA6D757E2}"
38+
ProjectSection(SolutionItems) = preProject
39+
scripts\AddCopyrightHeaders.ps1 = scripts\AddCopyrightHeaders.ps1
40+
EndProjectSection
41+
EndProject
3742
Global
3843
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3944
Debug|Any CPU = Debug|Any CPU

scripts/AddCopyrightHeaders.ps1

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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+
$copyrightHeaderString =
7+
@'
8+
//
9+
// Copyright (c) Microsoft. All rights reserved.
10+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
11+
//
12+
'@
13+
14+
$srcPath = Resolve-Path $PSScriptRoot\..\src
15+
Push-Location $srcPath
16+
17+
$updateCount = 0;
18+
$allSourceFiles = Get-ChildItem $srcPath -Recurse -Filter *.cs | ?{ $_.FullName -notmatch "\\obj\\?" }
19+
20+
foreach ($sourceFile in $allSourceFiles)
21+
{
22+
$fileContent = (Get-Content $sourceFile.FullName -Raw).TrimStart()
23+
24+
if ($fileContent.StartsWith($copyrightHeaderString) -eq $false)
25+
{
26+
# Add the copyright header to the file
27+
Set-Content $sourceFile.FullName ($copyrightHeaderString + "`r`n`r`n" + $fileContent)
28+
Write-Output ("Updated {0}" -f (Resolve-Path $sourceFile.FullName -Relative))
29+
}
30+
}
31+
32+
Write-Output "`r`nDone, $updateCount files updated."
33+
34+
Pop-Location

src/PowerShellEditorServices.Transport.Stdio/StdioHost.cs renamed to src/PowerShellEditorServices.Host/MessageLoop.cs

Lines changed: 80 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,33 @@
44
//
55

66
using Microsoft.PowerShell.EditorServices;
7-
using Microsoft.PowerShell.EditorServices.Console;
8-
using Microsoft.PowerShell.EditorServices.Session;
97
using Microsoft.PowerShell.EditorServices.Transport.Stdio.Event;
108
using Microsoft.PowerShell.EditorServices.Transport.Stdio.Message;
9+
using Microsoft.PowerShell.EditorServices.Transport.Stdio.Model;
1110
using Microsoft.PowerShell.EditorServices.Transport.Stdio.Response;
1211
using Nito.AsyncEx;
1312
using System;
13+
using System.IO;
14+
using System.Management.Automation;
1415
using System.Reflection;
1516
using System.Text;
1617
using System.Threading;
1718
using System.Threading.Tasks;
1819

19-
namespace Microsoft.PowerShell.EditorServices.Transport.Stdio
20+
namespace Microsoft.PowerShell.EditorServices.Host
2021
{
21-
public class StdioHost : IHost
22+
public class MessageLoop : IHost
2223
{
2324
#region Private Fields
2425

26+
private Stream inputStream;
27+
private Stream outputStream;
2528
private IConsoleHost consoleHost;
2629
private EditorSession editorSession;
27-
private SynchronizationContext syncContext;
30+
private MessageReader messageReader;
31+
private MessageWriter messageWriter;
32+
private SynchronizationContext applicationSyncContext;
33+
private SynchronizationContext messageLoopSyncContext;
2834
private AsyncContextThread messageLoopThread;
2935

3036
#endregion
@@ -41,12 +47,8 @@ Version IHost.Version
4147
get { throw new NotImplementedException(); }
4248
}
4349

44-
void IHost.Start()
50+
public void Start()
4551
{
46-
// Start a new EditorSession
47-
// TODO: Allow multiple sessions?
48-
this.editorSession = new EditorSession();
49-
5052
// Start the main message loop
5153
AsyncContext.Run((Func<Task>)this.StartMessageLoop);
5254
}
@@ -58,110 +60,54 @@ void IHost.Start()
5860
private async Task StartMessageLoop()
5961
{
6062
// Hold on to the current synchronization context
61-
this.syncContext = SynchronizationContext.Current;
63+
this.applicationSyncContext = SynchronizationContext.Current;
6264

6365
// Start the message listener on another thread
6466
this.messageLoopThread = new AsyncContextThread(true);
6567
await this.messageLoopThread.Factory.Run(() => this.ListenForMessages());
6668
}
6769

68-
//private async Task ListenForMessages()
69-
//{
70-
// // Ensure that the console is using UTF-8 encoding
71-
// System.Console.InputEncoding = Encoding.UTF8;
72-
// System.Console.OutputEncoding = Encoding.UTF8;
73-
74-
// // Set up the reader and writer
75-
// MessageReader messageReader =
76-
// new MessageReader(
77-
// System.Console.In,
78-
// MessageFormat.WithoutContentLength);
79-
80-
// MessageWriter messageWriter =
81-
// new MessageWriter(
82-
// System.Console.Out,
83-
// MessageFormat.WithContentLength);
84-
85-
// this.ConsoleHost = new StdioConsoleHost(messageWriter);
86-
87-
// // Set up the PowerShell session
88-
// // TODO: Do this elsewhere
89-
// EditorSession editorSession = new EditorSession();
90-
// editorSession.StartSession(this.ConsoleHost);
91-
92-
// // Send a "started" event
93-
// messageWriter.WriteMessage(
94-
// new Event<object>
95-
// {
96-
// EventType = "started"
97-
// });
98-
99-
// // Run the message loop
100-
// bool isRunning = true;
101-
// while(isRunning)
102-
// {
103-
// // Read a message
104-
// Message newMessage = await messageReader.ReadMessage();
105-
106-
// // Is the message a request?
107-
// IMessageProcessor messageProcessor = newMessage as IMessageProcessor;
108-
// if (messageProcessor != null)
109-
// {
110-
// // Process the request on the host thread
111-
// messageProcessor.ProcessMessage(
112-
// editorSession,
113-
// messageWriter);
114-
// }
115-
// else
116-
// {
117-
// if (newMessage != null)
118-
// {
119-
// // Return an error response to keep the client moving
120-
// messageWriter.WriteMessage(
121-
// new Response<object>
122-
// {
123-
// Command = request != null ? request.Command : string.Empty,
124-
// RequestSeq = newMessage.Seq,
125-
// Success = false,
126-
// });
127-
// }
128-
// }
129-
// }
130-
//}
13170
async Task ListenForMessages()
13271
{
72+
this.messageLoopSyncContext = SynchronizationContext.Current;
73+
13374
// Ensure that the console is using UTF-8 encoding
13475
System.Console.InputEncoding = Encoding.UTF8;
13576
System.Console.OutputEncoding = Encoding.UTF8;
13677

13778
// Find all message types in this assembly
13879
MessageTypeResolver messageTypeResolver = new MessageTypeResolver();
139-
messageTypeResolver.ScanForMessageTypes(Assembly.GetExecutingAssembly());
80+
messageTypeResolver.ScanForMessageTypes(typeof(StartedEvent).Assembly);
81+
82+
// Open the standard input/output streams
83+
this.inputStream = System.Console.OpenStandardInput();
84+
this.outputStream = System.Console.OpenStandardOutput();
14085

14186
// Set up the reader and writer
142-
MessageReader messageReader =
87+
this.messageReader =
14388
new MessageReader(
144-
System.Console.In,
145-
MessageFormat.WithContentLength,
89+
this.inputStream,
14690
messageTypeResolver);
14791

148-
MessageWriter messageWriter =
92+
this.messageWriter =
14993
new MessageWriter(
150-
System.Console.Out,
151-
MessageFormat.WithContentLength,
94+
this.outputStream,
15295
messageTypeResolver);
15396

15497
// Set up the console host which will send events
15598
// through the MessageWriter
15699
this.consoleHost = new StdioConsoleHost(messageWriter);
157100

158101
// Set up the PowerShell session
159-
// TODO: Do this elsewhere
160-
EditorSession editorSession = new EditorSession();
161-
editorSession.StartSession(this.consoleHost);
102+
this.editorSession = new EditorSession();
103+
this.editorSession.StartSession(this.consoleHost);
104+
105+
// Attach to events from the PowerShell session
106+
this.editorSession.PowerShellSession.OutputWritten += PowerShellSession_OutputWritten;
107+
this.editorSession.DebugService.DebuggerStopped += DebugService_DebuggerStopped;
162108

163109
// Send a "started" event
164-
messageWriter.WriteMessage(
110+
await this.messageWriter.WriteMessage(
165111
new StartedEvent());
166112

167113
// Run the message loop
@@ -173,13 +119,13 @@ async Task ListenForMessages()
173119
try
174120
{
175121
// Read a message from stdin
176-
newMessage = await messageReader.ReadMessage();
122+
newMessage = await this.messageReader.ReadMessage();
177123
}
178124
catch (MessageParseException e)
179125
{
180126
// Write an error response
181-
messageWriter.WriteMessage(
182-
MessageErrorResponse.CreateParseErrorResponse(e));
127+
this.messageWriter.WriteMessage(
128+
MessageErrorResponse.CreateParseErrorResponse(e)).Wait();
183129

184130
// Continue the loop
185131
continue;
@@ -191,16 +137,16 @@ async Task ListenForMessages()
191137
{
192138
// Process the message. The processor will take care
193139
// of writing responses throguh the messageWriter.
194-
messageProcessor.ProcessMessage(
195-
editorSession,
196-
messageWriter);
140+
await messageProcessor.ProcessMessage(
141+
this.editorSession,
142+
this.messageWriter);
197143
}
198144
else
199145
{
200146
if (newMessage != null)
201147
{
202148
// Return an error response to keep the client moving
203-
messageWriter.WriteMessage(
149+
await this.messageWriter.WriteMessage(
204150
MessageErrorResponse.CreateUnhandledMessageResponse(
205151
newMessage));
206152
}
@@ -213,6 +159,48 @@ async Task ListenForMessages()
213159
}
214160
}
215161

162+
async void DebugService_DebuggerStopped(object sender, DebuggerStopEventArgs e)
163+
{
164+
// Push the write operation to the correct thread
165+
this.messageLoopSyncContext.Post(
166+
async (obj) =>
167+
{
168+
await this.messageWriter.WriteMessage(
169+
new StoppedEvent
170+
{
171+
Body = new StoppedEventBody
172+
{
173+
Source = new Source
174+
{
175+
Path = e.InvocationInfo.ScriptName,
176+
},
177+
Line = e.InvocationInfo.ScriptLineNumber,
178+
Column = e.InvocationInfo.OffsetInLine,
179+
ThreadId = 1, // TODO: Change this based on context
180+
Reason = "breakpoint" // TODO: Change this based on context
181+
}
182+
});
183+
}, null);
184+
}
185+
186+
void PowerShellSession_OutputWritten(object sender, OutputWrittenEventArgs e)
187+
{
188+
// TODO: change this to use the OutputEvent!
189+
190+
//await this.messageWriter.WriteMessage(
191+
// new ReplWriteOutputEvent
192+
// {
193+
// Body = new ReplWriteOutputEventBody
194+
// {
195+
// LineContents = e.OutputText,
196+
// LineType = e.OutputType,
197+
// IncludeNewLine = e.IncludeNewLine,
198+
// ForegroundColor = e.ForegroundColor,
199+
// BackgroundColor = e.BackgroundColor
200+
// }
201+
// });
202+
}
203+
216204
#endregion
217205
}
218206
}

src/PowerShellEditorServices.Host/PowerShellEditorServices.Host.csproj

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
<AssemblyName>Microsoft.PowerShell.EditorServices.Host</AssemblyName>
1212
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
1313
<FileAlignment>512</FileAlignment>
14+
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
15+
<RestorePackages>true</RestorePackages>
1416
</PropertyGroup>
1517
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
1618
<PlatformTarget>AnyCPU</PlatformTarget>
@@ -34,20 +36,36 @@
3436
<WarningLevel>4</WarningLevel>
3537
</PropertyGroup>
3638
<ItemGroup>
39+
<Reference Include="Nito.AsyncEx, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
40+
<HintPath>..\..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.dll</HintPath>
41+
<Private>True</Private>
42+
</Reference>
43+
<Reference Include="Nito.AsyncEx.Concurrent, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
44+
<HintPath>..\..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Concurrent.dll</HintPath>
45+
<Private>True</Private>
46+
</Reference>
47+
<Reference Include="Nito.AsyncEx.Enlightenment, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
48+
<HintPath>..\..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Enlightenment.dll</HintPath>
49+
<Private>True</Private>
50+
</Reference>
3751
<Reference Include="System" />
3852
<Reference Include="System.Core" />
3953
<Reference Include="System.Xml.Linq" />
4054
<Reference Include="System.Data.DataSetExtensions" />
4155
<Reference Include="Microsoft.CSharp" />
4256
<Reference Include="System.Data" />
4357
<Reference Include="System.Xml" />
58+
<Reference Include="System.Management.Automation" />
4459
</ItemGroup>
4560
<ItemGroup>
61+
<Compile Include="MessageLoop.cs" />
4662
<Compile Include="Program.cs" />
4763
<Compile Include="Properties\AssemblyInfo.cs" />
64+
<Compile Include="StdioConsoleHost.cs" />
4865
</ItemGroup>
4966
<ItemGroup>
5067
<None Include="App.config" />
68+
<None Include="packages.config" />
5169
</ItemGroup>
5270
<ItemGroup>
5371
<ProjectReference Include="..\..\submodules\PSScriptAnalyzer\Engine\ScriptAnalyzerEngine.csproj">
@@ -68,6 +86,13 @@
6886
</ProjectReference>
6987
</ItemGroup>
7088
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
89+
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
90+
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
91+
<PropertyGroup>
92+
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
93+
</PropertyGroup>
94+
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
95+
</Target>
7196
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
7297
Other similar extension points exist, see Microsoft.Common.targets.
7398
<Target Name="BeforeBuild">

0 commit comments

Comments
 (0)