diff --git a/.gitignore b/.gitignore index 4e31936dd..975283fbe 100644 --- a/.gitignore +++ b/.gitignore @@ -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/** + diff --git a/PowerShellEditorServices.sln b/PowerShellEditorServices.sln index 29fd673a6..3d69e55a0 100644 --- a/PowerShellEditorServices.sln +++ b/PowerShellEditorServices.sln @@ -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 @@ -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 diff --git a/scripts/AddCopyrightHeaders.ps1 b/scripts/AddCopyrightHeaders.ps1 new file mode 100644 index 000000000..a11756eb2 --- /dev/null +++ b/scripts/AddCopyrightHeaders.ps1 @@ -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 \ No newline at end of file diff --git a/src/PowerShellEditorServices.Transport.Stdio/StdioHost.cs b/src/PowerShellEditorServices.Host/MessageLoop.cs similarity index 51% rename from src/PowerShellEditorServices.Transport.Stdio/StdioHost.cs rename to src/PowerShellEditorServices.Host/MessageLoop.cs index dcaf81863..f08fc17a4 100644 --- a/src/PowerShellEditorServices.Transport.Stdio/StdioHost.cs +++ b/src/PowerShellEditorServices.Host/MessageLoop.cs @@ -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 @@ -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)this.StartMessageLoop); } @@ -58,97 +60,38 @@ 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 - // { - // 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 - // { - // 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 @@ -156,12 +99,15 @@ async Task ListenForMessages() 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 @@ -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; @@ -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)); } @@ -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 } } diff --git a/src/PowerShellEditorServices.Host/PowerShellEditorServices.Host.csproj b/src/PowerShellEditorServices.Host/PowerShellEditorServices.Host.csproj index e6ceeb9fa..3f1f8f1e6 100644 --- a/src/PowerShellEditorServices.Host/PowerShellEditorServices.Host.csproj +++ b/src/PowerShellEditorServices.Host/PowerShellEditorServices.Host.csproj @@ -11,6 +11,8 @@ Microsoft.PowerShell.EditorServices.Host v4.5 512 + ..\..\ + true AnyCPU @@ -34,6 +36,18 @@ 4 + + ..\..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.dll + True + + + ..\..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Concurrent.dll + True + + + ..\..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Enlightenment.dll + True + @@ -41,13 +55,17 @@ + + + + @@ -68,6 +86,13 @@ + + + + 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}. + + +