Skip to content

Introducing script debugging support #26

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Nov 1, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,6 @@ ClientBin/
csx/

# Don't include ScriptAnalyzer binaries
PowerShellEditorServices/Microsoft.Windows.PowerShell.ScriptAnalyzer.dll
PowerShellEditorServices/Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.dll
PowerShellEditorServices/PSScriptAnalyzer.psd1
PowerShellEditorServices/ScriptAnalyzer.format.ps1xml
PowerShellEditorServices/ScriptAnalyzer.types.ps1xml
PowerShellEditorServices/**


7 changes: 6 additions & 1 deletion PowerShellEditorServices.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.31101.0
VisualStudioVersion = 12.0.40629.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F594E7FD-1E72-4E51-A496-B019C2BA3180}"
EndProject
Expand Down Expand Up @@ -34,6 +34,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{E51470
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerShellEditorServices.Test.Shared", "test\PowerShellEditorServices.Test.Shared\PowerShellEditorServices.Test.Shared.csproj", "{6A20B9E9-DE66-456E-B4F5-ACFD1A95C3CA}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{E2316F5C-A551-4A8D-8103-E42CA6D757E2}"
ProjectSection(SolutionItems) = preProject
scripts\AddCopyrightHeaders.ps1 = scripts\AddCopyrightHeaders.ps1
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down
34 changes: 34 additions & 0 deletions scripts/AddCopyrightHeaders.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#
# Copyright (c) Microsoft. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
#

$copyrightHeaderString =
@'
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
'@

$srcPath = Resolve-Path $PSScriptRoot\..\src
Push-Location $srcPath

$updateCount = 0;
$allSourceFiles = Get-ChildItem $srcPath -Recurse -Filter *.cs | ?{ $_.FullName -notmatch "\\obj\\?" }

foreach ($sourceFile in $allSourceFiles)
{
$fileContent = (Get-Content $sourceFile.FullName -Raw).TrimStart()

if ($fileContent.StartsWith($copyrightHeaderString) -eq $false)
{
# Add the copyright header to the file
Set-Content $sourceFile.FullName ($copyrightHeaderString + "`r`n`r`n" + $fileContent)
Write-Output ("Updated {0}" -f (Resolve-Path $sourceFile.FullName -Relative))
}
}

Write-Output "`r`nDone, $updateCount files updated."

Pop-Location
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,33 @@
//

using Microsoft.PowerShell.EditorServices;
using Microsoft.PowerShell.EditorServices.Console;
using Microsoft.PowerShell.EditorServices.Session;
using Microsoft.PowerShell.EditorServices.Transport.Stdio.Event;
using Microsoft.PowerShell.EditorServices.Transport.Stdio.Message;
using Microsoft.PowerShell.EditorServices.Transport.Stdio.Model;
using Microsoft.PowerShell.EditorServices.Transport.Stdio.Response;
using Nito.AsyncEx;
using System;
using System.IO;
using System.Management.Automation;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.PowerShell.EditorServices.Transport.Stdio
namespace Microsoft.PowerShell.EditorServices.Host
{
public class StdioHost : IHost
public class MessageLoop : IHost
{
#region Private Fields

private Stream inputStream;
private Stream outputStream;
private IConsoleHost consoleHost;
private EditorSession editorSession;
private SynchronizationContext syncContext;
private MessageReader messageReader;
private MessageWriter messageWriter;
private SynchronizationContext applicationSyncContext;
private SynchronizationContext messageLoopSyncContext;
private AsyncContextThread messageLoopThread;

#endregion
Expand All @@ -41,12 +47,8 @@ Version IHost.Version
get { throw new NotImplementedException(); }
}

void IHost.Start()
public void Start()
{
// Start a new EditorSession
// TODO: Allow multiple sessions?
this.editorSession = new EditorSession();

// Start the main message loop
AsyncContext.Run((Func<Task>)this.StartMessageLoop);
}
Expand All @@ -58,110 +60,54 @@ void IHost.Start()
private async Task StartMessageLoop()
{
// Hold on to the current synchronization context
this.syncContext = SynchronizationContext.Current;
this.applicationSyncContext = SynchronizationContext.Current;

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

//private async Task ListenForMessages()
//{
// // Ensure that the console is using UTF-8 encoding
// System.Console.InputEncoding = Encoding.UTF8;
// System.Console.OutputEncoding = Encoding.UTF8;

// // Set up the reader and writer
// MessageReader messageReader =
// new MessageReader(
// System.Console.In,
// MessageFormat.WithoutContentLength);

// MessageWriter messageWriter =
// new MessageWriter(
// System.Console.Out,
// MessageFormat.WithContentLength);

// this.ConsoleHost = new StdioConsoleHost(messageWriter);

// // Set up the PowerShell session
// // TODO: Do this elsewhere
// EditorSession editorSession = new EditorSession();
// editorSession.StartSession(this.ConsoleHost);

// // Send a "started" event
// messageWriter.WriteMessage(
// new Event<object>
// {
// EventType = "started"
// });

// // Run the message loop
// bool isRunning = true;
// while(isRunning)
// {
// // Read a message
// Message newMessage = await messageReader.ReadMessage();

// // Is the message a request?
// IMessageProcessor messageProcessor = newMessage as IMessageProcessor;
// if (messageProcessor != null)
// {
// // Process the request on the host thread
// messageProcessor.ProcessMessage(
// editorSession,
// messageWriter);
// }
// else
// {
// if (newMessage != null)
// {
// // Return an error response to keep the client moving
// messageWriter.WriteMessage(
// new Response<object>
// {
// Command = request != null ? request.Command : string.Empty,
// RequestSeq = newMessage.Seq,
// Success = false,
// });
// }
// }
// }
//}
async Task ListenForMessages()
{
this.messageLoopSyncContext = SynchronizationContext.Current;

// Ensure that the console is using UTF-8 encoding
System.Console.InputEncoding = Encoding.UTF8;
System.Console.OutputEncoding = Encoding.UTF8;

// Find all message types in this assembly
MessageTypeResolver messageTypeResolver = new MessageTypeResolver();
messageTypeResolver.ScanForMessageTypes(Assembly.GetExecutingAssembly());
messageTypeResolver.ScanForMessageTypes(typeof(StartedEvent).Assembly);

// Open the standard input/output streams
this.inputStream = System.Console.OpenStandardInput();
this.outputStream = System.Console.OpenStandardOutput();

// Set up the reader and writer
MessageReader messageReader =
this.messageReader =
new MessageReader(
System.Console.In,
MessageFormat.WithContentLength,
this.inputStream,
messageTypeResolver);

MessageWriter messageWriter =
this.messageWriter =
new MessageWriter(
System.Console.Out,
MessageFormat.WithContentLength,
this.outputStream,
messageTypeResolver);

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

// Set up the PowerShell session
// TODO: Do this elsewhere
EditorSession editorSession = new EditorSession();
editorSession.StartSession(this.consoleHost);
this.editorSession = new EditorSession();
this.editorSession.StartSession(this.consoleHost);

// Attach to events from the PowerShell session
this.editorSession.PowerShellSession.OutputWritten += PowerShellSession_OutputWritten;
this.editorSession.DebugService.DebuggerStopped += DebugService_DebuggerStopped;

// Send a "started" event
messageWriter.WriteMessage(
await this.messageWriter.WriteMessage(
new StartedEvent());

// Run the message loop
Expand All @@ -173,13 +119,13 @@ async Task ListenForMessages()
try
{
// Read a message from stdin
newMessage = await messageReader.ReadMessage();
newMessage = await this.messageReader.ReadMessage();
}
catch (MessageParseException e)
{
// Write an error response
messageWriter.WriteMessage(
MessageErrorResponse.CreateParseErrorResponse(e));
this.messageWriter.WriteMessage(
MessageErrorResponse.CreateParseErrorResponse(e)).Wait();

// Continue the loop
continue;
Expand All @@ -191,16 +137,16 @@ async Task ListenForMessages()
{
// Process the message. The processor will take care
// of writing responses throguh the messageWriter.
messageProcessor.ProcessMessage(
editorSession,
messageWriter);
await messageProcessor.ProcessMessage(
this.editorSession,
this.messageWriter);
}
else
{
if (newMessage != null)
{
// Return an error response to keep the client moving
messageWriter.WriteMessage(
await this.messageWriter.WriteMessage(
MessageErrorResponse.CreateUnhandledMessageResponse(
newMessage));
}
Expand All @@ -213,6 +159,48 @@ async Task ListenForMessages()
}
}

async void DebugService_DebuggerStopped(object sender, DebuggerStopEventArgs e)
{
// Push the write operation to the correct thread
this.messageLoopSyncContext.Post(
async (obj) =>
{
await this.messageWriter.WriteMessage(
new StoppedEvent
{
Body = new StoppedEventBody
{
Source = new Source
{
Path = e.InvocationInfo.ScriptName,
},
Line = e.InvocationInfo.ScriptLineNumber,
Column = e.InvocationInfo.OffsetInLine,
ThreadId = 1, // TODO: Change this based on context
Reason = "breakpoint" // TODO: Change this based on context
}
});
}, null);
}

void PowerShellSession_OutputWritten(object sender, OutputWrittenEventArgs e)
{
// TODO: change this to use the OutputEvent!

//await this.messageWriter.WriteMessage(
// new ReplWriteOutputEvent
// {
// Body = new ReplWriteOutputEventBody
// {
// LineContents = e.OutputText,
// LineType = e.OutputType,
// IncludeNewLine = e.IncludeNewLine,
// ForegroundColor = e.ForegroundColor,
// BackgroundColor = e.BackgroundColor
// }
// });
}

#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
<AssemblyName>Microsoft.PowerShell.EditorServices.Host</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
Expand All @@ -34,20 +36,36 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Nito.AsyncEx, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Nito.AsyncEx.Concurrent, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Concurrent.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Nito.AsyncEx.Enlightenment, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Enlightenment.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="System.Management.Automation" />
</ItemGroup>
<ItemGroup>
<Compile Include="MessageLoop.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="StdioConsoleHost.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\submodules\PSScriptAnalyzer\Engine\ScriptAnalyzerEngine.csproj">
Expand All @@ -68,6 +86,13 @@
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<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>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
Expand Down
Loading