diff --git a/src/PowerShellEditorServices.Host/DebugAdapter.cs b/src/PowerShellEditorServices.Host/DebugAdapter.cs index c93a8bfc9..7f127df2b 100644 --- a/src/PowerShellEditorServices.Host/DebugAdapter.cs +++ b/src/PowerShellEditorServices.Host/DebugAdapter.cs @@ -101,7 +101,7 @@ protected async Task HandleLaunchRequest( // Execute the given PowerShell script and send the response. // Note that we aren't waiting for execution to complete here // because the debugger could stop while the script executes. - editorSession.PowerShellSession + editorSession.powerShellContext .ExecuteScriptAtPath(launchParams.Program) .ContinueWith( async (t) => @@ -138,18 +138,18 @@ protected Task HandleDisconnectRequest( handler = async (o, e) => { - if (e.NewSessionState == PowerShellSessionState.Ready) + if (e.NewSessionState == PowerShellContextState.Ready) { await requestContext.SendResult(null); - editorSession.PowerShellSession.SessionStateChanged -= handler; + editorSession.powerShellContext.SessionStateChanged -= handler; // TODO: Find a way to exit more gracefully! Environment.Exit(0); } }; - editorSession.PowerShellSession.SessionStateChanged += handler; - editorSession.PowerShellSession.AbortExecution(); + editorSession.powerShellContext.SessionStateChanged += handler; + editorSession.powerShellContext.AbortExecution(); return Task.FromResult(true); } diff --git a/src/PowerShellEditorServices.Host/LanguageServer.cs b/src/PowerShellEditorServices.Host/LanguageServer.cs index 7abf52cc4..0a78a90a9 100644 --- a/src/PowerShellEditorServices.Host/LanguageServer.cs +++ b/src/PowerShellEditorServices.Host/LanguageServer.cs @@ -177,16 +177,16 @@ protected Task HandleDidCloseTextDocumentNotification( } protected Task HandleDidChangeTextDocumentNotification( - DidChangeTextDocumentNotification[] textChangeParams, + DidChangeTextDocumentParams textChangeParams, EditorSession editorSession, EventContext eventContext) { List changedFiles = new List(); // A text change notification can batch multiple change requests - foreach (var textChange in textChangeParams) + foreach (var textChange in textChangeParams.ContentChanges) { - ScriptFile changedFile = editorSession.Workspace.GetFile(textChange.Uri); + ScriptFile changedFile = editorSession.Workspace.GetFile(textChangeParams.Uri); changedFile.ApplyChange( GetFileChangeDetails( @@ -366,7 +366,7 @@ protected async Task HandleCompletionResolveRequest( if (completionItem.Kind == CompletionItemKind.Function) { RunspaceHandle runspaceHandle = - await editorSession.PowerShellSession.GetRunspaceHandle(); + await editorSession.powerShellContext.GetRunspaceHandle(); // Get the documentation for the function CommandInfo commandInfo = diff --git a/src/PowerShellEditorServices.Host/MessageLoop.cs b/src/PowerShellEditorServices.Host/MessageLoop.cs index fc1fbff74..c6153d3a5 100644 --- a/src/PowerShellEditorServices.Host/MessageLoop.cs +++ b/src/PowerShellEditorServices.Host/MessageLoop.cs @@ -129,7 +129,7 @@ async Task ListenForMessages() // Set up the PowerShell session this.editorSession = new EditorSession(); this.editorSession.StartSession(this.consoleHost); - this.editorSession.PowerShellSession.OutputWritten += PowerShellSession_OutputWritten; + this.editorSession.powerShellContext.OutputWritten += powerShellContext_OutputWritten; if (this.runDebugAdapter) { @@ -191,7 +191,7 @@ await this.messageWriter.WriteEvent( }, null); } - async void PowerShellSession_OutputWritten(object sender, OutputWrittenEventArgs e) + async void powerShellContext_OutputWritten(object sender, OutputWrittenEventArgs e) { await this.messageWriter.WriteEvent( OutputEvent.Type, diff --git a/src/PowerShellEditorServices.Protocol/LanguageServer/CompletionMessages.cs b/src/PowerShellEditorServices.Protocol/LanguageServer/CompletionMessages.cs index 6a540b3df..d4b6651c2 100644 --- a/src/PowerShellEditorServices.Protocol/LanguageServer/CompletionMessages.cs +++ b/src/PowerShellEditorServices.Protocol/LanguageServer/CompletionMessages.cs @@ -75,6 +75,14 @@ public class CompletionItem public string InsertText { get; set; } public TextEdit TextEdit { get; set; } + + /// + /// Gets or sets a custom data field that allows the server to mark + /// each completion item with an identifier that will help correlate + /// the item to the previous completion request during a completion + /// resolve request. + /// + public object Data { get; set; } } } diff --git a/src/PowerShellEditorServices.Protocol/LanguageServer/References.cs b/src/PowerShellEditorServices.Protocol/LanguageServer/References.cs index 0d077b4e6..cec0eab0a 100644 --- a/src/PowerShellEditorServices.Protocol/LanguageServer/References.cs +++ b/src/PowerShellEditorServices.Protocol/LanguageServer/References.cs @@ -16,10 +16,10 @@ public static readonly public class ReferencesParams : TextDocumentPosition { - public ReferencesOptions Options { get; set; } + public ReferencesContext Context { get; set; } } - public class ReferencesOptions + public class ReferencesContext { public bool IncludeDeclaration { get; set; } } diff --git a/src/PowerShellEditorServices.Protocol/LanguageServer/TextDocumentMessages.cs b/src/PowerShellEditorServices.Protocol/LanguageServer/TextDocumentMessages.cs index 01f7b64cc..ad4156c42 100644 --- a/src/PowerShellEditorServices.Protocol/LanguageServer/TextDocumentMessages.cs +++ b/src/PowerShellEditorServices.Protocol/LanguageServer/TextDocumentMessages.cs @@ -49,12 +49,23 @@ public static readonly EventType.Create("textDocument/didClose"); } - public class DidChangeTextDocumentNotification : TextDocumentIdentifier + public class DidChangeTextDocumentNotification { public static readonly - EventType Type = - EventType.Create("textDocument/didChange"); + EventType Type = + EventType.Create("textDocument/didChange"); + } + public class DidChangeTextDocumentParams : TextDocumentIdentifier + { + /// + /// Gets or sets the list of changes to the document content. + /// + public TextDocumentChangeEvent[] ContentChanges { get; set; } + } + + public class TextDocumentChangeEvent + { /// /// Gets or sets the Range where the document was changed. Will /// be null if the server's TextDocumentSyncKind is Full. diff --git a/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageReader.cs b/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageReader.cs index b3b5fc022..5262aabb8 100644 --- a/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageReader.cs +++ b/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageReader.cs @@ -142,7 +142,18 @@ await this.inputStream.ReadAsync( this.bufferEndOffset += readLength; - return readLength >= 0; + if (readLength == 0) + { + // If ReadAsync returns 0 then it means that the stream was + // closed unexpectedly (usually due to the client application + // ending suddenly). For now, just terminate the language + // server immediately. + // TODO: Provide a more graceful shutdown path + throw new EndOfStreamException( + "MessageReader's input stream ended unexpectedly, terminating."); + } + + return true; } private bool TryReadMessageHeaders() diff --git a/src/PowerShellEditorServices/Analysis/AnalysisService.cs b/src/PowerShellEditorServices/Analysis/AnalysisService.cs index 1ec8e29e7..87ac6c0a1 100644 --- a/src/PowerShellEditorServices/Analysis/AnalysisService.cs +++ b/src/PowerShellEditorServices/Analysis/AnalysisService.cs @@ -32,7 +32,7 @@ public class AnalysisService : IDisposable /// public AnalysisService() { - // TODO: Share runspace with PowerShellSession? Probably should always + // TODO: Share runspace with PowerShellContext? Probably should always // run analysis in a local session. this.analysisRunspace = RunspaceFactory.CreateRunspace(InitialSessionState.CreateDefault2()); this.analysisRunspace.ApartmentState = ApartmentState.STA; diff --git a/src/PowerShellEditorServices/Debugging/DebugService.cs b/src/PowerShellEditorServices/Debugging/DebugService.cs index 8da15dc95..171e2836e 100644 --- a/src/PowerShellEditorServices/Debugging/DebugService.cs +++ b/src/PowerShellEditorServices/Debugging/DebugService.cs @@ -14,13 +14,13 @@ namespace Microsoft.PowerShell.EditorServices { /// /// Provides a high-level service for interacting with the - /// PowerShell debugger in the context of a PowerShellSession. + /// PowerShell debugger in the runspace managed by a PowerShellContext. /// public class DebugService { #region Fields - private PowerShellSession powerShellSession; + private PowerShellContext powerShellContext; // TODO: This needs to be managed per nested session private Dictionary> breakpointsPerFile = @@ -36,18 +36,18 @@ public class DebugService /// /// Initializes a new instance of the DebugService class and uses - /// the given PowerShellSession for all future operations. + /// the given PowerShellContext for all future operations. /// - /// - /// The PowerShellSession to use for all debugging operations. + /// + /// The PowerShellContext to use for all debugging operations. /// - public DebugService(PowerShellSession powerShellSession) + public DebugService(PowerShellContext powerShellContext) { - Validate.IsNotNull("powerShellSession", powerShellSession); + Validate.IsNotNull("powerShellContext", powerShellContext); - this.powerShellSession = powerShellSession; - this.powerShellSession.DebuggerStop += this.OnDebuggerStop; - this.powerShellSession.BreakpointUpdated += this.OnBreakpointUpdated; + this.powerShellContext = powerShellContext; + this.powerShellContext.DebuggerStop += this.OnDebuggerStop; + this.powerShellContext.BreakpointUpdated += this.OnBreakpointUpdated; } #endregion @@ -81,7 +81,7 @@ public async Task SetBreakpoints( psCommand.AddParameter("Line", lineNumbers.Length > 0 ? lineNumbers : null); resultBreakpoints = - await this.powerShellSession.ExecuteCommand( + await this.powerShellContext.ExecuteCommand( psCommand); return @@ -98,7 +98,7 @@ await this.powerShellSession.ExecuteCommand( /// public void Continue() { - this.powerShellSession.ResumeDebugger( + this.powerShellContext.ResumeDebugger( DebuggerResumeAction.Continue); } @@ -107,7 +107,7 @@ public void Continue() /// public void StepOver() { - this.powerShellSession.ResumeDebugger( + this.powerShellContext.ResumeDebugger( DebuggerResumeAction.StepOver); } @@ -116,7 +116,7 @@ public void StepOver() /// public void StepIn() { - this.powerShellSession.ResumeDebugger( + this.powerShellContext.ResumeDebugger( DebuggerResumeAction.StepInto); } @@ -125,7 +125,7 @@ public void StepIn() /// public void StepOut() { - this.powerShellSession.ResumeDebugger( + this.powerShellContext.ResumeDebugger( DebuggerResumeAction.StepOut); } @@ -137,16 +137,16 @@ public void StepOut() public void Break() { // Break execution in the debugger - this.powerShellSession.BreakExecution(); + this.powerShellContext.BreakExecution(); } /// /// Aborts execution of the debugger while it is running, even while - /// it is stopped. Equivalent to calling PowerShellSession.AbortExecution. + /// it is stopped. Equivalent to calling PowerShellContext.AbortExecution. /// public void Abort() { - this.powerShellSession.AbortExecution(); + this.powerShellContext.AbortExecution(); } /// @@ -238,7 +238,7 @@ public VariableDetails GetVariableFromExpression(string variableExpression, int /// /// Evaluates an expression in the context of the stopped /// debugger. This method will execute the specified expression - /// PowerShellSession. + /// PowerShellContext. /// /// The expression string to execute. /// The ID of the stack frame in which the expression should be executed. @@ -246,7 +246,7 @@ public VariableDetails GetVariableFromExpression(string variableExpression, int public async Task EvaluateExpression(string expressionString, int stackFrameId) { var results = - await this.powerShellSession.ExecuteScriptString( + await this.powerShellContext.ExecuteScriptString( expressionString); // Since this method should only be getting invoked in the debugger, @@ -302,7 +302,7 @@ private async Task ClearBreakpointsInFile(ScriptFile scriptFile) psCommand.AddCommand("Remove-PSBreakpoint"); psCommand.AddParameter("Breakpoint", breakpoints.ToArray()); - await this.powerShellSession.ExecuteCommand(psCommand); + await this.powerShellContext.ExecuteCommand(psCommand); // Clear the existing breakpoints list for the file breakpoints.Clear(); @@ -319,7 +319,7 @@ private async Task FetchVariables() psCommand.AddCommand("Get-Variable"); psCommand.AddParameter("Scope", "Local"); - var results = await this.powerShellSession.ExecuteCommand(psCommand); + var results = await this.powerShellContext.ExecuteCommand(psCommand); foreach (var variable in results) { @@ -336,7 +336,7 @@ private async Task FetchStackFrames() PSCommand psCommand = new PSCommand(); psCommand.AddCommand("Get-PSCallStack"); - var results = await this.powerShellSession.ExecuteCommand(psCommand); + var results = await this.powerShellContext.ExecuteCommand(psCommand); this.callStackFrames = results diff --git a/src/PowerShellEditorServices/Debugging/VariableDetails.cs b/src/PowerShellEditorServices/Debugging/VariableDetails.cs index b8d5eb661..6254f1ba7 100644 --- a/src/PowerShellEditorServices/Debugging/VariableDetails.cs +++ b/src/PowerShellEditorServices/Debugging/VariableDetails.cs @@ -181,62 +181,73 @@ private static VariableDetails[] GetChildren(object obj) IDictionary dictionary = obj as IDictionary; IEnumerable enumerable = obj as IEnumerable; - if (psObject != null) + try { - childVariables.AddRange( - psObject - .Properties - .Select(p => new VariableDetails(p))); - } - else if (dictionary != null) - { - childVariables.AddRange( - dictionary - .OfType() - .Select(e => new VariableDetails(e.Key.ToString(), e.Value))); - } - else if (enumerable != null && !(obj is string)) - { - int i = 0; - foreach (var item in enumerable) + if (psObject != null) { - childVariables.Add( - new VariableDetails( - string.Format("[{0}]", i), - item)); - - i++; + childVariables.AddRange( + psObject + .Properties + .Select(p => new VariableDetails(p))); } - } - else if (obj != null) - { - // Object must be a normal .NET type, pull all of its - // properties and their values - Type objectType = obj.GetType(); - var properties = - objectType.GetProperties( - BindingFlags.Public | BindingFlags.Instance); - - foreach (var property in properties) + else if (dictionary != null) + { + childVariables.AddRange( + dictionary + .OfType() + .Select(e => new VariableDetails(e.Key.ToString(), e.Value))); + } + else if (enumerable != null && !(obj is string)) { - try + int i = 0; + foreach (var item in enumerable) { childVariables.Add( new VariableDetails( - property.Name, - property.GetValue(obj))); + string.Format("[{0}]", i), + item)); + + i++; } - catch (Exception) + } + else if (obj != null) + { + // Object must be a normal .NET type, pull all of its + // properties and their values + Type objectType = obj.GetType(); + var properties = + objectType.GetProperties( + BindingFlags.Public | BindingFlags.Instance); + + foreach (var property in properties) { - // Some properties can throw exceptions, add the property - // name and empty string - childVariables.Add( - new VariableDetails( - property.Name, - string.Empty)); + try + { + childVariables.Add( + new VariableDetails( + property.Name, + property.GetValue(obj))); + } + catch (Exception) + { + // Some properties can throw exceptions, add the property + // name and empty string + childVariables.Add( + new VariableDetails( + property.Name, + string.Empty)); + } } } } + catch (GetValueInvocationException) + { + // This exception occurs when accessing the value of a + // variable causes a script to be executed. Right now + // we aren't loading children on the pipeline thread so + // this causes an exception to be raised. In this case, + // just return an empty list of children. + } return childVariables.ToArray(); } diff --git a/src/PowerShellEditorServices/Language/LanguageService.cs b/src/PowerShellEditorServices/Language/LanguageService.cs index 61c0ccd23..06c60386e 100644 --- a/src/PowerShellEditorServices/Language/LanguageService.cs +++ b/src/PowerShellEditorServices/Language/LanguageService.cs @@ -23,7 +23,7 @@ public class LanguageService #region Private Fields private bool areAliasesLoaded; - private PowerShellSession powerShellSession; + private PowerShellContext powerShellContext; private CompletionResults mostRecentCompletions; private int mostRecentRequestLine; private int mostRecentRequestOffest; @@ -39,14 +39,14 @@ public class LanguageService /// Constructs an instance of the LanguageService class and uses /// the given Runspace to execute language service operations. /// - /// - /// The PowerShellSession in which language service operations will be executed. + /// + /// The PowerShellContext in which language service operations will be executed. /// - public LanguageService(PowerShellSession powerShellSession) + public LanguageService(PowerShellContext powerShellContext) { - Validate.IsNotNull("powerShellSession", powerShellSession); + Validate.IsNotNull("powerShellContext", powerShellContext); - this.powerShellSession = powerShellSession; + this.powerShellContext = powerShellContext; this.CmdletToAliasDictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); this.AliasToCmdletDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -87,7 +87,7 @@ public async Task GetCompletionsInFile( columnNumber); RunspaceHandle runspaceHandle = - await this.powerShellSession.GetRunspaceHandle(); + await this.powerShellContext.GetRunspaceHandle(); CompletionResults completionResults = AstOperations.GetCompletions( @@ -182,7 +182,7 @@ public async Task FindSymbolDetailsAtLocation( if (symbolReference != null) { RunspaceHandle runspaceHandle = - await this.powerShellSession.GetRunspaceHandle(); + await this.powerShellContext.GetRunspaceHandle(); symbolReference.FilePath = scriptFile.FilePath; symbolDetails = new SymbolDetails(symbolReference, runspaceHandle.Runspace); @@ -432,7 +432,7 @@ private async Task GetAliases() { if (!this.areAliasesLoaded) { - RunspaceHandle runspaceHandle = await this.powerShellSession.GetRunspaceHandle(); + RunspaceHandle runspaceHandle = await this.powerShellContext.GetRunspaceHandle(); CommandInvocationIntrinsics invokeCommand = runspaceHandle.Runspace.SessionStateProxy.InvokeCommand; IEnumerable aliases = invokeCommand.GetCommands("*", CommandTypes.Alias, true); @@ -463,7 +463,7 @@ private async Task GetCommandInfo(string commandName) command.AddCommand("Get-Command"); command.AddArgument(commandName); - var results = await this.powerShellSession.ExecuteCommand(command); + var results = await this.powerShellContext.ExecuteCommand(command); return results.FirstOrDefault(); } diff --git a/src/PowerShellEditorServices/PowerShellEditorServices.csproj b/src/PowerShellEditorServices/PowerShellEditorServices.csproj index 38a959ddf..71968fdca 100644 --- a/src/PowerShellEditorServices/PowerShellEditorServices.csproj +++ b/src/PowerShellEditorServices/PowerShellEditorServices.csproj @@ -90,8 +90,8 @@ - - + + diff --git a/src/PowerShellEditorServices/Session/EditorSession.cs b/src/PowerShellEditorServices/Session/EditorSession.cs index 01af67663..8225480ea 100644 --- a/src/PowerShellEditorServices/Session/EditorSession.cs +++ b/src/PowerShellEditorServices/Session/EditorSession.cs @@ -23,9 +23,9 @@ public class EditorSession public Workspace Workspace { get; private set; } /// - /// Gets the PowerShellSession instance for this session. + /// Gets the PowerShellContext instance for this session. /// - public PowerShellSession PowerShellSession { get; private set; } + public PowerShellContext powerShellContext { get; private set; } /// /// Gets the LanguageService instance for this session. @@ -60,10 +60,10 @@ public void StartSession(IConsoleHost consoleHost) this.Workspace = new Workspace(); // Initialize all services - this.PowerShellSession = new PowerShellSession(); - this.LanguageService = new LanguageService(this.PowerShellSession); + this.powerShellContext = new PowerShellContext(); + this.LanguageService = new LanguageService(this.powerShellContext); this.AnalysisService = new AnalysisService(); - this.DebugService = new DebugService(this.PowerShellSession); + this.DebugService = new DebugService(this.powerShellContext); } #endregion @@ -82,10 +82,10 @@ public void Dispose() this.AnalysisService = null; } - if (this.PowerShellSession != null) + if (this.powerShellContext != null) { - this.PowerShellSession.Dispose(); - this.PowerShellSession = null; + this.powerShellContext.Dispose(); + this.powerShellContext = null; } } diff --git a/src/PowerShellEditorServices/Session/PowerShellSession.cs b/src/PowerShellEditorServices/Session/PowerShellContext.cs similarity index 95% rename from src/PowerShellEditorServices/Session/PowerShellSession.cs rename to src/PowerShellEditorServices/Session/PowerShellContext.cs index 81f768518..e629f2cc8 100644 --- a/src/PowerShellEditorServices/Session/PowerShellSession.cs +++ b/src/PowerShellEditorServices/Session/PowerShellContext.cs @@ -24,7 +24,7 @@ namespace Microsoft.PowerShell.EditorServices /// Handles nested PowerShell prompts and also manages execution of /// commands whether inside or outside of the debugger. /// - public class PowerShellSession : IDisposable, IConsoleHost + public class PowerShellContext : IDisposable, IConsoleHost { #region Fields @@ -62,7 +62,7 @@ public bool IsDebuggerStopped /// /// Gets the current state of the session. /// - public PowerShellSessionState SessionState + public PowerShellContextState SessionState { get; private set; @@ -73,10 +73,10 @@ public PowerShellSessionState SessionState #region Constructors /// - /// Initializes a new instance of the PowerShellSession class and + /// Initializes a new instance of the PowerShellContext class and /// opens a runspace to be used for the session. /// - public PowerShellSession() + public PowerShellContext() { this.initialSessionState = InitialSessionState.CreateDefault2(); @@ -92,11 +92,11 @@ public PowerShellSession() } /// - /// Initializes a new instance of the PowerShellSession class using + /// Initializes a new instance of the PowerShellContext class using /// an existing runspace for the session. /// /// - public PowerShellSession(Runspace initialRunspace) + public PowerShellContext(Runspace initialRunspace) { this.Initialize(initialRunspace); } @@ -105,7 +105,7 @@ private void Initialize(Runspace initialRunspace) { Validate.IsNotNull("initialRunspace", initialRunspace); - this.SessionState = PowerShellSessionState.NotStarted; + this.SessionState = PowerShellContextState.NotStarted; this.initialRunspace = initialRunspace; this.currentRunspace = initialRunspace; @@ -120,7 +120,7 @@ private void Initialize(Runspace initialRunspace) // TODO: Should this be configurable? this.SetExecutionPolicy(ExecutionPolicy.RemoteSigned); - this.SessionState = PowerShellSessionState.Ready; + this.SessionState = PowerShellContextState.Ready; } #endregion @@ -371,11 +371,11 @@ internal void ResumeDebugger(DebuggerResumeAction resumeAction) /// /// Disposes the runspace and any other resources being used - /// by this PowerShellSession. + /// by this PowerShellContext. /// public void Dispose() { - this.SessionState = PowerShellSessionState.Disposed; + this.SessionState = PowerShellContextState.Disposed; if (this.powerShell != null) { @@ -462,48 +462,48 @@ void powerShell_InvocationStateChanged(object sender, PSInvocationStateChangedEv private static SessionStateChangedEventArgs TranslateInvocationStateInfo(PSInvocationStateInfo invocationState) { - PowerShellSessionState newState = PowerShellSessionState.Unknown; + PowerShellContextState newState = PowerShellContextState.Unknown; PowerShellExecutionResult executionResult = PowerShellExecutionResult.NotFinished; switch (invocationState.State) { case PSInvocationState.NotStarted: - newState = PowerShellSessionState.NotStarted; + newState = PowerShellContextState.NotStarted; break; case PSInvocationState.Failed: - newState = PowerShellSessionState.Ready; + newState = PowerShellContextState.Ready; executionResult = PowerShellExecutionResult.Failed; break; case PSInvocationState.Disconnected: // TODO: Any extra work to do in this case? // TODO: Is this a unique state that can be re-connected? - newState = PowerShellSessionState.Disposed; + newState = PowerShellContextState.Disposed; executionResult = PowerShellExecutionResult.Stopped; break; case PSInvocationState.Running: - newState = PowerShellSessionState.Running; + newState = PowerShellContextState.Running; break; case PSInvocationState.Completed: - newState = PowerShellSessionState.Ready; + newState = PowerShellContextState.Ready; executionResult = PowerShellExecutionResult.Completed; break; case PSInvocationState.Stopping: // TODO: Collapse this so that the result shows that execution was aborted - newState = PowerShellSessionState.Aborting; + newState = PowerShellContextState.Aborting; break; case PSInvocationState.Stopped: - newState = PowerShellSessionState.Ready; + newState = PowerShellContextState.Ready; executionResult = PowerShellExecutionResult.Aborted; break; default: - newState = PowerShellSessionState.Unknown; + newState = PowerShellContextState.Unknown; break; } @@ -612,7 +612,7 @@ private void OnDebuggerStop(object sender, DebuggerStopEventArgs e) this.pipelineExecutionTask = new TaskCompletionSource(); // Update the session state - this.OnSessionStateChanged(this, new SessionStateChangedEventArgs(PowerShellSessionState.Ready, PowerShellExecutionResult.Stopped, null)); + this.OnSessionStateChanged(this, new SessionStateChangedEventArgs(PowerShellContextState.Ready, PowerShellExecutionResult.Stopped, null)); // Raise the event for the debugger service if (this.DebuggerStop != null) @@ -732,18 +732,18 @@ private interface IPipelineExecutionRequest /// The expected result type of the execution. private class PipelineExecutionRequest : IPipelineExecutionRequest { - PowerShellSession powerShellSession; + PowerShellContext powerShellContext; PSCommand psCommand; bool sendOutputToHost; public IEnumerable Results { get; private set; } public PipelineExecutionRequest( - PowerShellSession powerShellSession, + PowerShellContext powerShellContext, PSCommand psCommand, bool sendOutputToHost) { - this.powerShellSession = powerShellSession; + this.powerShellContext = powerShellContext; this.psCommand = psCommand; this.sendOutputToHost = sendOutputToHost; } @@ -751,7 +751,7 @@ public PipelineExecutionRequest( public async Task Execute() { this.Results = - await this.powerShellSession.ExecuteCommand( + await this.powerShellContext.ExecuteCommand( psCommand, sendOutputToHost); diff --git a/src/PowerShellEditorServices/Session/PowerShellSessionState.cs b/src/PowerShellEditorServices/Session/PowerShellContextState.cs similarity index 92% rename from src/PowerShellEditorServices/Session/PowerShellSessionState.cs rename to src/PowerShellEditorServices/Session/PowerShellContextState.cs index 54bec33ea..2075c04d5 100644 --- a/src/PowerShellEditorServices/Session/PowerShellSessionState.cs +++ b/src/PowerShellEditorServices/Session/PowerShellContextState.cs @@ -6,9 +6,9 @@ namespace Microsoft.PowerShell.EditorServices { /// - /// Enumerates the possible states for a PowerShellSession. + /// Enumerates the possible states for a PowerShellContext. /// - public enum PowerShellSessionState + public enum PowerShellContextState { /// /// Indicates an unknown, potentially uninitialized state. diff --git a/src/PowerShellEditorServices/Session/RunspaceHandle.cs b/src/PowerShellEditorServices/Session/RunspaceHandle.cs index c5e9738a8..0c9ae0ab5 100644 --- a/src/PowerShellEditorServices/Session/RunspaceHandle.cs +++ b/src/PowerShellEditorServices/Session/RunspaceHandle.cs @@ -10,11 +10,11 @@ namespace Microsoft.PowerShell.EditorServices { /// /// Provides a handle to the runspace that is managed by - /// a PowerShellSession. The holder of this handle. + /// a PowerShellContext. The holder of this handle. /// public class RunspaceHandle : IDisposable { - private PowerShellSession powerShellSession; + private PowerShellContext powerShellContext; /// /// Gets the runspace that is held by this handle. @@ -26,22 +26,22 @@ public class RunspaceHandle : IDisposable /// given runspace. /// /// The runspace instance which is temporarily owned by this handle. - /// The PowerShellSession instance which manages the runspace. - public RunspaceHandle(Runspace runspace, PowerShellSession powerShellSession) + /// The PowerShellContext instance which manages the runspace. + public RunspaceHandle(Runspace runspace, PowerShellContext powerShellContext) { this.Runspace = runspace; - this.powerShellSession = powerShellSession; + this.powerShellContext = powerShellContext; } /// /// Disposes the RunspaceHandle once the holder is done using it. - /// Causes the handle to be released back to the PowerShellSession. + /// Causes the handle to be released back to the PowerShellContext. /// public void Dispose() { // Release the handle and clear the runspace so that // no further operations can be performed on it. - this.powerShellSession.ReleaseRunspaceHandle(this); + this.powerShellContext.ReleaseRunspaceHandle(this); this.Runspace = null; } } diff --git a/src/PowerShellEditorServices/Session/SessionStateChangedEventArgs.cs b/src/PowerShellEditorServices/Session/SessionStateChangedEventArgs.cs index 86f616361..9a0fed0f3 100644 --- a/src/PowerShellEditorServices/Session/SessionStateChangedEventArgs.cs +++ b/src/PowerShellEditorServices/Session/SessionStateChangedEventArgs.cs @@ -8,14 +8,14 @@ namespace Microsoft.PowerShell.EditorServices { /// - /// Provides details about a change in state of a PowerShellSession. + /// Provides details about a change in state of a PowerShellContext. /// public class SessionStateChangedEventArgs { /// /// Gets the new state for the session. /// - public PowerShellSessionState NewSessionState { get; private set; } + public PowerShellContextState NewSessionState { get; private set; } /// /// Gets the execution result of the operation that caused @@ -35,7 +35,7 @@ public class SessionStateChangedEventArgs /// The result of the operation that caused the state change. /// An exception that describes the failure, if any. public SessionStateChangedEventArgs( - PowerShellSessionState newSessionState, + PowerShellContextState newSessionState, PowerShellExecutionResult executionResult, Exception errorException) { diff --git a/src/PowerShellEditorServices/Workspace/Workspace.cs b/src/PowerShellEditorServices/Workspace/Workspace.cs index 3cac4052e..779f64faa 100644 --- a/src/PowerShellEditorServices/Workspace/Workspace.cs +++ b/src/PowerShellEditorServices/Workspace/Workspace.cs @@ -6,6 +6,7 @@ using Microsoft.PowerShell.EditorServices.Utility; using System; using System.Collections.Generic; +using System.Linq; using System.IO; using System.Text; @@ -91,11 +92,13 @@ public ScriptFile GetFileBuffer(string filePath, string initialBuffer) return scriptFile; } + /// + /// Gets an array of all opened ScriptFiles in the workspace. + /// + /// An array of all opened ScriptFiles in the workspace. public ScriptFile[] GetOpenedFiles() { - var scriptFiles = new ScriptFile[workspaceFiles.Count]; - workspaceFiles.Values.CopyTo(scriptFiles, 0); - return scriptFiles; + return workspaceFiles.Values.ToArray(); } /// diff --git a/test/PowerShellEditorServices.Test/Console/ConsoleServiceTests.cs b/test/PowerShellEditorServices.Test/Console/ConsoleServiceTests.cs index e99a8aac5..9436cab4b 100644 --- a/test/PowerShellEditorServices.Test/Console/ConsoleServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Console/ConsoleServiceTests.cs @@ -13,12 +13,12 @@ namespace Microsoft.PowerShell.EditorServices.Test.Console public class ConsoleServiceTests : IDisposable { private TestConsoleHost consoleHost; - private PowerShellSession powerShellSession; + private PowerShellContext powerShellContext; public ConsoleServiceTests() { this.consoleHost = new TestConsoleHost(); - this.powerShellSession = new PowerShellSession(); + this.powerShellContext = new PowerShellContext(); } public void Dispose() @@ -38,7 +38,7 @@ public async Task ReceivesChoicePrompt() $response = $host.ui.PromptForChoice($caption, $message, $choices, 1) $response"; - await this.powerShellSession.ExecuteScriptString(choiceScript); + await this.powerShellContext.ExecuteScriptString(choiceScript); // TODO: Verify prompt info diff --git a/test/PowerShellEditorServices.Test/Console/PowerShellSessionTests.cs b/test/PowerShellEditorServices.Test/Console/PowerShellContextTests.cs similarity index 76% rename from test/PowerShellEditorServices.Test/Console/PowerShellSessionTests.cs rename to test/PowerShellEditorServices.Test/Console/PowerShellContextTests.cs index cf27d12e0..b193131f2 100644 --- a/test/PowerShellEditorServices.Test/Console/PowerShellSessionTests.cs +++ b/test/PowerShellEditorServices.Test/Console/PowerShellContextTests.cs @@ -3,15 +3,14 @@ using System.Collections.Generic; using System.Linq; using System.Management.Automation; -using System.Management.Automation.Runspaces; using System.Threading.Tasks; using Xunit; namespace Microsoft.PowerShell.EditorServices.Test.Console { - public class PowerShellSessionTests : IDisposable + public class PowerShellContextTests : IDisposable { - private PowerShellSession powerShellSession; + private PowerShellContext powerShellContext; private AsyncProducerConsumerQueue stateChangeQueue; private Dictionary outputPerType = new Dictionary(); @@ -21,18 +20,18 @@ public class PowerShellSessionTests : IDisposable private const string DebugTestFilePath = @"..\..\..\PowerShellEditorServices.Test.Shared\Debugging\DebugTest.ps1"; - public PowerShellSessionTests() + public PowerShellContextTests() { - this.powerShellSession = new PowerShellSession(); - this.powerShellSession.SessionStateChanged += OnSessionStateChanged; - this.powerShellSession.OutputWritten += OnOutputWritten; + this.powerShellContext = new PowerShellContext(); + this.powerShellContext.SessionStateChanged += OnSessionStateChanged; + this.powerShellContext.OutputWritten += OnOutputWritten; this.stateChangeQueue = new AsyncProducerConsumerQueue(); } public void Dispose() { - this.powerShellSession.Dispose(); - this.powerShellSession = null; + this.powerShellContext.Dispose(); + this.powerShellContext = null; } [Fact] @@ -42,10 +41,10 @@ public async Task CanExecutePSCommand() psCommand.AddScript("$a = \"foo\"; $a"); var executeTask = - this.powerShellSession.ExecuteCommand(psCommand); + this.powerShellContext.ExecuteCommand(psCommand); - await this.AssertStateChange(PowerShellSessionState.Running); - await this.AssertStateChange(PowerShellSessionState.Ready); + await this.AssertStateChange(PowerShellContextState.Running); + await this.AssertStateChange(PowerShellContextState.Ready); var result = await executeTask; Assert.Equal("foo", result.First()); @@ -55,14 +54,14 @@ public async Task CanExecutePSCommand() public async Task CanQueueParallelRunspaceRequests() { // Concurrently initiate 4 requests in the session - this.powerShellSession.ExecuteScriptString("$x = 100"); - Task handleTask = this.powerShellSession.GetRunspaceHandle(); - this.powerShellSession.ExecuteScriptString("$x += 200"); - this.powerShellSession.ExecuteScriptString("$x = $x / 100"); + this.powerShellContext.ExecuteScriptString("$x = 100"); + Task handleTask = this.powerShellContext.GetRunspaceHandle(); + this.powerShellContext.ExecuteScriptString("$x += 200"); + this.powerShellContext.ExecuteScriptString("$x = $x / 100"); PSCommand psCommand = new PSCommand(); psCommand.AddScript("$x"); - Task> resultTask = this.powerShellSession.ExecuteCommand(psCommand); + Task> resultTask = this.powerShellContext.ExecuteCommand(psCommand); // Wait for the requested runspace handle and then dispose it RunspaceHandle handle = await handleTask; @@ -83,14 +82,14 @@ public async Task CanAbortExecution() Task.Run( async () => { - var unusedTask = this.powerShellSession.ExecuteScriptAtPath(DebugTestFilePath); + var unusedTask = this.powerShellContext.ExecuteScriptAtPath(DebugTestFilePath); await Task.Delay(50); - this.powerShellSession.AbortExecution(); + this.powerShellContext.AbortExecution(); }); - await this.AssertStateChange(PowerShellSessionState.Running); - await this.AssertStateChange(PowerShellSessionState.Aborting); - await this.AssertStateChange(PowerShellSessionState.Ready); + await this.AssertStateChange(PowerShellContextState.Running); + await this.AssertStateChange(PowerShellContextState.Aborting); + await this.AssertStateChange(PowerShellContextState.Ready); await executeTask; } @@ -98,7 +97,7 @@ public async Task CanAbortExecution() [Fact] public async Task ReceivesNormalOutput() { - await this.powerShellSession.ExecuteScriptString( + await this.powerShellContext.ExecuteScriptString( string.Format( "\"{0}\"", TestOutputString)); @@ -111,7 +110,7 @@ await this.powerShellSession.ExecuteScriptString( [Fact] public async Task ReceivesErrorOutput() { - await this.powerShellSession.ExecuteScriptString( + await this.powerShellContext.ExecuteScriptString( string.Format( "Write-Error \"{0}\"", TestOutputString)); @@ -129,9 +128,9 @@ public async Task ReceivesVerboseOutput() // Since setting VerbosePreference causes other message to // be written out when we run our test, run a command preemptively // to flush out unwanted verbose messages - await this.powerShellSession.ExecuteScriptString("Write-Verbose \"Preloading\""); + await this.powerShellContext.ExecuteScriptString("Write-Verbose \"Preloading\""); - await this.powerShellSession.ExecuteScriptString( + await this.powerShellContext.ExecuteScriptString( string.Format( "$VerbosePreference = \"Continue\"; Write-Verbose \"{0}\"", TestOutputString)); @@ -147,9 +146,9 @@ public async Task ReceivesDebugOutput() // Since setting VerbosePreference causes other message to // be written out when we run our test, run a command preemptively // to flush out unwanted verbose messages - await this.powerShellSession.ExecuteScriptString("Write-Verbose \"Preloading\""); + await this.powerShellContext.ExecuteScriptString("Write-Verbose \"Preloading\""); - await this.powerShellSession.ExecuteScriptString( + await this.powerShellContext.ExecuteScriptString( string.Format( "$DebugPreference = \"Continue\"; Write-Debug \"{0}\"", TestOutputString)); @@ -162,7 +161,7 @@ await this.powerShellSession.ExecuteScriptString( [Fact] public async Task ReceivesWarningOutput() { - await this.powerShellSession.ExecuteScriptString( + await this.powerShellContext.ExecuteScriptString( string.Format( "Write-Warning \"{0}\"", TestOutputString)); @@ -183,7 +182,7 @@ public string GetOutputForType(OutputType outputLineType) return outputString; } - private async Task AssertStateChange(PowerShellSessionState expectedState) + private async Task AssertStateChange(PowerShellContextState expectedState) { SessionStateChangedEventArgs newState = await this.stateChangeQueue.DequeueAsync(); diff --git a/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs b/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs index 0f2b40690..576600684 100644 --- a/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs @@ -12,7 +12,7 @@ public class DebugServiceTests : IDisposable private Workspace workspace; private DebugService debugService; private ScriptFile debugScriptFile; - private PowerShellSession powerShellSession; + private PowerShellContext powerShellContext; private AsyncProducerConsumerQueue debuggerStoppedQueue = new AsyncProducerConsumerQueue(); @@ -28,18 +28,18 @@ public DebugServiceTests() this.workspace.GetFile( @"..\..\..\PowerShellEditorServices.Test.Shared\Debugging\DebugTest.ps1"); - this.powerShellSession = new PowerShellSession(); - this.powerShellSession.SessionStateChanged += powerShellSession_SessionStateChanged; + this.powerShellContext = new PowerShellContext(); + this.powerShellContext.SessionStateChanged += powerShellContext_SessionStateChanged; - this.debugService = new DebugService(this.powerShellSession); + this.debugService = new DebugService(this.powerShellContext); this.debugService.DebuggerStopped += debugService_DebuggerStopped; this.debugService.BreakpointUpdated += debugService_BreakpointUpdated; } - void powerShellSession_SessionStateChanged(object sender, SessionStateChangedEventArgs e) + void powerShellContext_SessionStateChanged(object sender, SessionStateChangedEventArgs e) { // Skip all transitions except those back to 'Ready' - if (e.NewSessionState == PowerShellSessionState.Ready) + if (e.NewSessionState == PowerShellContextState.Ready) { this.sessionStateQueue.Enqueue(e); } @@ -57,7 +57,7 @@ void debugService_DebuggerStopped(object sender, DebuggerStopEventArgs e) public void Dispose() { - this.powerShellSession.Dispose(); + this.powerShellContext.Dispose(); } [Fact] @@ -95,9 +95,9 @@ public async Task DebuggerStopsOnBreakpoints() await this.debugService.SetBreakpoints( this.debugScriptFile, new int[] { 5, 9 }); - await this.AssertStateChange(PowerShellSessionState.Ready); + await this.AssertStateChange(PowerShellContextState.Ready); - this.powerShellSession.ExecuteScriptAtPath( + this.powerShellContext.ExecuteScriptAtPath( this.debugScriptFile.FilePath); // Wait for a couple breakpoints @@ -109,14 +109,14 @@ await this.debugService.SetBreakpoints( // Abort script execution early and wait for completion this.debugService.Abort(); await this.AssertStateChange( - PowerShellSessionState.Ready, + PowerShellContextState.Ready, PowerShellExecutionResult.Aborted); } [Fact] public async Task DebuggerBreaksWhenRequested() { - this.powerShellSession.ExecuteScriptString( + this.powerShellContext.ExecuteScriptString( this.debugScriptFile.FilePath); // Break execution and wait for the debugger to stop @@ -124,35 +124,35 @@ public async Task DebuggerBreaksWhenRequested() await this.AssertDebuggerStopped( this.debugScriptFile.FilePath); await this.AssertStateChange( - PowerShellSessionState.Ready, + PowerShellContextState.Ready, PowerShellExecutionResult.Stopped); // Abort execution and wait for the debugger to exit this.debugService.Abort(); await this.AssertStateChange( - PowerShellSessionState.Ready, + PowerShellContextState.Ready, PowerShellExecutionResult.Aborted); } [Fact] public async Task DebuggerRunsCommandsWhileStopped() { - this.powerShellSession.ExecuteScriptString( + this.powerShellContext.ExecuteScriptString( this.debugScriptFile.FilePath); // Break execution and wait for the debugger to stop this.debugService.Break(); await this.AssertStateChange( - PowerShellSessionState.Ready, + PowerShellContextState.Ready, PowerShellExecutionResult.Stopped); // Try running a command from outside the pipeline thread - await this.powerShellSession.ExecuteScriptString("Get-Command Get-Process"); + await this.powerShellContext.ExecuteScriptString("Get-Command Get-Process"); // Abort execution and wait for the debugger to exit this.debugService.Abort(); await this.AssertStateChange( - PowerShellSessionState.Ready, + PowerShellContextState.Ready, PowerShellExecutionResult.Aborted); } @@ -168,7 +168,7 @@ await this.debugService.SetBreakpoints( new int[] { 14 }); // Execute the script and wait for the breakpoint to be hit - this.powerShellSession.ExecuteScriptString(variablesFile.FilePath); + this.powerShellContext.ExecuteScriptString(variablesFile.FilePath); await this.AssertDebuggerStopped(variablesFile.FilePath); VariableDetails[] variables = @@ -203,7 +203,7 @@ await this.debugService.SetBreakpoints( Assert.Equal(2, classChildren.Length); // Abort execution of the script - this.powerShellSession.AbortExecution(); + this.powerShellContext.AbortExecution(); } public async Task AssertDebuggerStopped( @@ -223,7 +223,7 @@ public async Task AssertDebuggerStopped( } private async Task AssertStateChange( - PowerShellSessionState expectedState, + PowerShellContextState expectedState, PowerShellExecutionResult expectedResult = PowerShellExecutionResult.Completed) { SessionStateChangedEventArgs newState = diff --git a/test/PowerShellEditorServices.Test/Language/LanguageServiceTests.cs b/test/PowerShellEditorServices.Test/Language/LanguageServiceTests.cs index 3538e22c9..33019f1a1 100644 --- a/test/PowerShellEditorServices.Test/Language/LanguageServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Language/LanguageServiceTests.cs @@ -24,19 +24,19 @@ public class LanguageServiceTests : IDisposable { private Workspace workspace; private LanguageService languageService; - private PowerShellSession powerShellSession; + private PowerShellContext powerShellContext; public LanguageServiceTests() { this.workspace = new Workspace(); - this.powerShellSession = new PowerShellSession(); - this.languageService = new LanguageService(this.powerShellSession); + this.powerShellContext = new PowerShellContext(); + this.languageService = new LanguageService(this.powerShellContext); } public void Dispose() { - this.powerShellSession.Dispose(); + this.powerShellContext.Dispose(); } [Fact] diff --git a/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj b/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj index b06ad14f5..a6095778a 100644 --- a/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj +++ b/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj @@ -75,7 +75,7 @@ - +