From ae5e8614cd169421fc798cc3d6d1dd57cfb15d3b Mon Sep 17 00:00:00 2001 From: David Wilson Date: Fri, 20 Nov 2015 08:36:03 -0800 Subject: [PATCH 1/7] Fix #46: Clear-Host causes error while debugging This change updates the SessionPSHostRawUserInterface class to get rid of most instances of throwing NotImplementedException. When debugging, if a script invokes a method that causes the raw UI interface to be touched, those methods shouldn't cause errors to appear. This change reduces the possibility of the user seeing errors due to this reason. --- .../Session/SessionPSHostRawUserInterface.cs | 71 +++++++++---------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/src/PowerShellEditorServices/Session/SessionPSHostRawUserInterface.cs b/src/PowerShellEditorServices/Session/SessionPSHostRawUserInterface.cs index 921e61446..8c36a5661 100644 --- a/src/PowerShellEditorServices/Session/SessionPSHostRawUserInterface.cs +++ b/src/PowerShellEditorServices/Session/SessionPSHostRawUserInterface.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Microsoft.PowerShell.EditorServices.Utility; using System; using System.Management.Automation.Host; @@ -80,14 +81,8 @@ public override Size BufferSize /// public override Coordinates CursorPosition { - get - { - throw new System.NotImplementedException(); - } - set - { - throw new System.NotImplementedException(); - } + get; + set; } /// @@ -95,14 +90,8 @@ public override Coordinates CursorPosition /// public override int CursorSize { - get - { - throw new System.NotImplementedException(); - } - set - { - throw new System.NotImplementedException(); - } + get; + set; } /// @@ -110,14 +99,8 @@ public override int CursorSize /// public override Coordinates WindowPosition { - get - { - throw new System.NotImplementedException(); - } - set - { - throw new System.NotImplementedException(); - } + get; + set; } /// @@ -125,14 +108,8 @@ public override Coordinates WindowPosition /// public override Size WindowSize { - get - { - throw new System.NotImplementedException(); - } - set - { - throw new System.NotImplementedException(); - } + get; + set; } /// @@ -149,7 +126,7 @@ public override string WindowTitle /// public override bool KeyAvailable { - get { throw new System.NotImplementedException(); } + get { return false; } } /// @@ -157,7 +134,7 @@ public override bool KeyAvailable /// public override Size MaxPhysicalWindowSize { - get { throw new System.NotImplementedException(); } + get { return new Size(80, 20); } } /// @@ -165,7 +142,7 @@ public override Size MaxPhysicalWindowSize /// public override Size MaxWindowSize { - get { throw new System.NotImplementedException(); } + get { return new Size(80, 20); } } /// @@ -175,6 +152,10 @@ public override Size MaxWindowSize /// A KeyInfo struct with details about the current keypress. public override KeyInfo ReadKey(ReadKeyOptions options) { + Logger.Write( + LogLevel.Warning, + "PSHostRawUserInterface.ReadKey was called"); + throw new System.NotImplementedException(); } @@ -183,7 +164,9 @@ public override KeyInfo ReadKey(ReadKeyOptions options) /// public override void FlushInputBuffer() { - throw new System.NotImplementedException(); + Logger.Write( + LogLevel.Warning, + "PSHostRawUserInterface.FlushInputBuffer was called"); } /// @@ -193,6 +176,10 @@ public override void FlushInputBuffer() /// A BufferCell array with the requested buffer contents. public override BufferCell[,] GetBufferContents(Rectangle rectangle) { + Logger.Write( + LogLevel.Warning, + "PSHostRawUserInterface.GetBufferContents was called"); + throw new System.NotImplementedException(); } @@ -209,7 +196,9 @@ public override void ScrollBufferContents( Rectangle clip, BufferCell fill) { - throw new System.NotImplementedException(); + Logger.Write( + LogLevel.Warning, + "PSHostRawUserInterface.ScrollBufferContents was called"); } /// @@ -221,7 +210,9 @@ public override void SetBufferContents( Rectangle rectangle, BufferCell fill) { - throw new System.NotImplementedException(); + Logger.Write( + LogLevel.Warning, + "PSHostRawUserInterface.SetBufferContents was called"); } /// @@ -233,7 +224,9 @@ public override void SetBufferContents( Coordinates origin, BufferCell[,] contents) { - throw new System.NotImplementedException(); + Logger.Write( + LogLevel.Warning, + "PSHostRawUserInterface.SetBufferContents was called"); } #endregion From be44edc627b169a57ee59aedc22a8ff0c0411161 Mon Sep 17 00:00:00 2001 From: David Wilson Date: Fri, 20 Nov 2015 13:54:29 -0800 Subject: [PATCH 2/7] Add handling of configuration events from client This change introduces the 'workspace/didChangeConfiguration' notification which the client will send to the language server when the user changes their settings. It also introduces a new configuration section for script analysis settings so that the user can turn on/off script analysis. More settings will be added later. --- .../LanguageServer.cs | 76 +++++++++++++++---- .../LanguageServerSettings.cs | 52 +++++++++++++ .../PowerShellEditorServices.Host.csproj | 1 + .../LanguageServer/Configuration.cs | 21 +++++ .../PowerShellEditorServices.Protocol.csproj | 1 + 5 files changed, 138 insertions(+), 13 deletions(-) create mode 100644 src/PowerShellEditorServices.Host/LanguageServerSettings.cs create mode 100644 src/PowerShellEditorServices.Protocol/LanguageServer/Configuration.cs diff --git a/src/PowerShellEditorServices.Host/LanguageServer.cs b/src/PowerShellEditorServices.Host/LanguageServer.cs index aed69f167..9554861cc 100644 --- a/src/PowerShellEditorServices.Host/LanguageServer.cs +++ b/src/PowerShellEditorServices.Host/LanguageServer.cs @@ -25,6 +25,7 @@ internal class LanguageServer : IMessageProcessor private static CancellationTokenSource existingRequestCancellation; private MessageDispatcher messageDispatcher; + private LanguageServerSettings currentSettings = new LanguageServerSettings(); public LanguageServer() { @@ -42,6 +43,7 @@ public void Initialize() this.AddEventHandler(DidOpenTextDocumentNotification.Type, this.HandleDidOpenTextDocumentNotification); this.AddEventHandler(DidCloseTextDocumentNotification.Type, this.HandleDidCloseTextDocumentNotification); this.AddEventHandler(DidChangeTextDocumentNotification.Type, this.HandleDidChangeTextDocumentNotification); + this.AddEventHandler(DidChangeConfigurationNotification.Type, this.HandleDidChangeConfigurationNotification); this.AddRequestHandler(DefinitionRequest.Type, this.HandleDefinitionRequest); this.AddRequestHandler(ReferencesRequest.Type, this.HandleReferencesRequest); @@ -226,6 +228,36 @@ protected Task HandleDidChangeTextDocumentNotification( return Task.FromResult(true); } + protected async Task HandleDidChangeConfigurationNotification( + DidChangeConfigurationParams configChangeParams, + EditorSession editorSession, + EventContext eventContext) + { + bool oldScriptAnalysisEnabled = + this.currentSettings.ScriptAnalysis.Enable.HasValue; + + this.currentSettings.Update( + configChangeParams.Settings.Powershell); + + if (oldScriptAnalysisEnabled != this.currentSettings.ScriptAnalysis.Enable) + { + // If the user just turned off script analysis, send a diagnostics + // event to clear the analysis markers that they already have + if (!this.currentSettings.ScriptAnalysis.Enable.Value) + { + ScriptFileMarker[] emptyAnalysisDiagnostics = new ScriptFileMarker[0]; + + foreach (var scriptFile in editorSession.Workspace.GetOpenedFiles()) + { + await PublishScriptDiagnostics( + scriptFile, + emptyAnalysisDiagnostics, + eventContext); + } + } + } + } + protected async Task HandleDefinitionRequest( TextDocumentPosition textDocumentPosition, EditorSession editorSession, @@ -744,6 +776,12 @@ private Task RunScriptDiagnostics( EditorSession editorSession, EventContext eventContext) { + if (!this.currentSettings.ScriptAnalysis.Enable.Value) + { + // If the user has disabled script analysis, skip it entirely + return TaskConstants.Completed; + } + // If there's an existing task, attempt to cancel it try { @@ -827,24 +865,36 @@ private static async Task DelayThenInvokeDiagnostics( var allMarkers = scriptFile.SyntaxMarkers.Concat(semanticMarkers); - // Always send syntax and semantic errors. We want to - // make sure no out-of-date markers are being displayed. - await eventContext.SendEvent( - PublishDiagnosticsNotification.Type, - new PublishDiagnosticsNotification - { - Uri = scriptFile.ClientFilePath, - Diagnostics = - allMarkers - .Select(GetDiagnosticFromMarker) - .ToArray() - }); - + await PublishScriptDiagnostics( + scriptFile, + semanticMarkers, + eventContext); } Logger.Write(LogLevel.Verbose, "Analysis complete."); } + private async static Task PublishScriptDiagnostics( + ScriptFile scriptFile, + ScriptFileMarker[] semanticMarkers, + EventContext eventContext) + { + var allMarkers = scriptFile.SyntaxMarkers.Concat(semanticMarkers); + + // Always send syntax and semantic errors. We want to + // make sure no out-of-date markers are being displayed. + await eventContext.SendEvent( + PublishDiagnosticsNotification.Type, + new PublishDiagnosticsNotification + { + Uri = scriptFile.ClientFilePath, + Diagnostics = + allMarkers + .Select(GetDiagnosticFromMarker) + .ToArray() + }); + } + private static Diagnostic GetDiagnosticFromMarker(ScriptFileMarker scriptFileMarker) { return new Diagnostic diff --git a/src/PowerShellEditorServices.Host/LanguageServerSettings.cs b/src/PowerShellEditorServices.Host/LanguageServerSettings.cs new file mode 100644 index 000000000..5d46c9f46 --- /dev/null +++ b/src/PowerShellEditorServices.Host/LanguageServerSettings.cs @@ -0,0 +1,52 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +namespace Microsoft.PowerShell.EditorServices.Host +{ + internal class LanguageServerSettings + { + public ScriptAnalysisSettings ScriptAnalysis { get; set; } + + public LanguageServerSettings() + { + this.ScriptAnalysis = new ScriptAnalysisSettings(); + } + + public void Update(LanguageServerSettings settings) + { + if (settings != null) + { + this.ScriptAnalysis.Update(settings.ScriptAnalysis); + } + } + } + + internal class ScriptAnalysisSettings + { + public bool? Enable { get; set; } + + public ScriptAnalysisSettings() + { + this.Enable = true; + } + + public void Update(ScriptAnalysisSettings settings) + { + if (settings != null) + { + this.Enable = settings.Enable; + } + } + } + + internal class SettingsWrapper + { + // NOTE: This property is capitalized as 'Powershell' because the + // mode name sent from the client is written as 'powershell' and + // JSON.net is using camelCasing. + + public LanguageServerSettings Powershell { get; set; } + } +} diff --git a/src/PowerShellEditorServices.Host/PowerShellEditorServices.Host.csproj b/src/PowerShellEditorServices.Host/PowerShellEditorServices.Host.csproj index 308931b12..fb6a09099 100644 --- a/src/PowerShellEditorServices.Host/PowerShellEditorServices.Host.csproj +++ b/src/PowerShellEditorServices.Host/PowerShellEditorServices.Host.csproj @@ -62,6 +62,7 @@ + diff --git a/src/PowerShellEditorServices.Protocol/LanguageServer/Configuration.cs b/src/PowerShellEditorServices.Protocol/LanguageServer/Configuration.cs new file mode 100644 index 000000000..d4f57781d --- /dev/null +++ b/src/PowerShellEditorServices.Protocol/LanguageServer/Configuration.cs @@ -0,0 +1,21 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol; + +namespace Microsoft.PowerShell.EditorServices.Protocol.LanguageServer +{ + public class DidChangeConfigurationNotification + { + public static readonly + EventType> Type = + EventType>.Create("workspace/didChangeConfiguration"); + } + + public class DidChangeConfigurationParams + { + public TConfig Settings { get; set; } + } +} diff --git a/src/PowerShellEditorServices.Protocol/PowerShellEditorServices.Protocol.csproj b/src/PowerShellEditorServices.Protocol/PowerShellEditorServices.Protocol.csproj index 25c0b2ec2..38400eb2f 100644 --- a/src/PowerShellEditorServices.Protocol/PowerShellEditorServices.Protocol.csproj +++ b/src/PowerShellEditorServices.Protocol/PowerShellEditorServices.Protocol.csproj @@ -89,6 +89,7 @@ + From dccf461d248aea610a65da1db5d331b16a99089e Mon Sep 17 00:00:00 2001 From: David Wilson Date: Fri, 20 Nov 2015 14:01:35 -0800 Subject: [PATCH 3/7] Fix #50: High CPU usage on complete/hover app path This change fixes an issue which causes high CPU usage and a delay when an application path is completed or hovered over. This is due to an unnecesary call to Get-Help which takes some time before it returns in this case. --- .../LanguageServer.cs | 11 ++---- .../Language/CommandHelpers.cs | 38 +++++++++++-------- .../Language/SymbolDetails.cs | 27 ++++++++++--- 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/src/PowerShellEditorServices.Host/LanguageServer.cs b/src/PowerShellEditorServices.Host/LanguageServer.cs index 9554861cc..622459791 100644 --- a/src/PowerShellEditorServices.Host/LanguageServer.cs +++ b/src/PowerShellEditorServices.Host/LanguageServer.cs @@ -427,13 +427,10 @@ protected async Task HandleCompletionResolveRequest( completionItem.Label, runspaceHandle.Runspace); - if (commandInfo != null) - { - completionItem.Documentation = - CommandHelpers.GetCommandSynopsis( - commandInfo, - runspaceHandle.Runspace); - } + completionItem.Documentation = + CommandHelpers.GetCommandSynopsis( + commandInfo, + runspaceHandle.Runspace); runspaceHandle.Dispose(); } diff --git a/src/PowerShellEditorServices/Language/CommandHelpers.cs b/src/PowerShellEditorServices/Language/CommandHelpers.cs index e4dcac094..aa695d199 100644 --- a/src/PowerShellEditorServices/Language/CommandHelpers.cs +++ b/src/PowerShellEditorServices/Language/CommandHelpers.cs @@ -52,25 +52,31 @@ public static string GetCommandSynopsis( PSObject helpObject = null; - using (PowerShell powerShell = PowerShell.Create()) - { - powerShell.Runspace = runspace; - powerShell.AddCommand("Get-Help"); - powerShell.AddArgument(commandInfo); - helpObject = powerShell.Invoke().FirstOrDefault(); - } - - if (helpObject != null) + if (commandInfo != null && + (commandInfo.CommandType == CommandTypes.Cmdlet || + commandInfo.CommandType == CommandTypes.Function || + commandInfo.CommandType == CommandTypes.Filter)) { - // Extract the synopsis string from the object - synopsisString = - (string)helpObject.Properties["synopsis"].Value ?? - string.Empty; + using (PowerShell powerShell = PowerShell.Create()) + { + powerShell.Runspace = runspace; + powerShell.AddCommand("Get-Help"); + powerShell.AddArgument(commandInfo); + helpObject = powerShell.Invoke().FirstOrDefault(); + } - // Ignore the placeholder value for this field - if (string.Equals(synopsisString, "SHORT DESCRIPTION", System.StringComparison.InvariantCultureIgnoreCase)) + if (helpObject != null) { - synopsisString = string.Empty; + // Extract the synopsis string from the object + synopsisString = + (string)helpObject.Properties["synopsis"].Value ?? + string.Empty; + + // Ignore the placeholder value for this field + if (string.Equals(synopsisString, "SHORT DESCRIPTION", System.StringComparison.InvariantCultureIgnoreCase)) + { + synopsisString = string.Empty; + } } } diff --git a/src/PowerShellEditorServices/Language/SymbolDetails.cs b/src/PowerShellEditorServices/Language/SymbolDetails.cs index 41b677acf..203575f6a 100644 --- a/src/PowerShellEditorServices/Language/SymbolDetails.cs +++ b/src/PowerShellEditorServices/Language/SymbolDetails.cs @@ -51,12 +51,29 @@ internal SymbolDetails( symbolReference.SymbolName, runspace); - this.Documentation = - CommandHelpers.GetCommandSynopsis( - commandInfo, - runspace); + if (commandInfo != null) + { + this.Documentation = + CommandHelpers.GetCommandSynopsis( + commandInfo, + runspace); - this.DisplayString = "function " + symbolReference.SymbolName; + if (commandInfo.CommandType == CommandTypes.Application) + { + this.DisplayString = "(application) " + symbolReference.SymbolName; + } + else + { + this.DisplayString = "function " + symbolReference.SymbolName; + } + } + else + { + // Command information can't be loaded. This is likely due to + // the symbol being a function that is defined in a file that + // hasn't been loaded in the runspace yet. + this.DisplayString = "function " + symbolReference.SymbolName; + } } else if (symbolReference.SymbolType == SymbolType.Parameter) { From c4d31cf1743d2e767bb486bb5bf0c4b665c998a7 Mon Sep 17 00:00:00 2001 From: David Wilson Date: Fri, 20 Nov 2015 13:57:53 -0800 Subject: [PATCH 4/7] Fix #44: Enable lang features for in-memory files This change enables language features for in-memory files such as untitled files and diff comparison files which show up in VS Code's Git diff viewer. When a file is sent in with an 'inmemory' or 'untitled' URI, it gets treated like a normal file. --- .../LanguageServer.cs | 3 + .../Workspace/ScriptFile.cs | 7 ++ .../Workspace/Workspace.cs | 73 +++++++++++++++---- 3 files changed, 68 insertions(+), 15 deletions(-) diff --git a/src/PowerShellEditorServices.Host/LanguageServer.cs b/src/PowerShellEditorServices.Host/LanguageServer.cs index 622459791..c451c0c5c 100644 --- a/src/PowerShellEditorServices.Host/LanguageServer.cs +++ b/src/PowerShellEditorServices.Host/LanguageServer.cs @@ -96,6 +96,9 @@ protected async Task HandleInitializeRequest( EditorSession editorSession, RequestContext requestContext) { + // Grab the workspace path from the parameters + editorSession.Workspace.WorkspacePath = initializeParams.RootPath; + await requestContext.SendResult( new InitializeResult { diff --git a/src/PowerShellEditorServices/Workspace/ScriptFile.cs b/src/PowerShellEditorServices/Workspace/ScriptFile.cs index 9d572850f..54b88b613 100644 --- a/src/PowerShellEditorServices/Workspace/ScriptFile.cs +++ b/src/PowerShellEditorServices/Workspace/ScriptFile.cs @@ -53,6 +53,12 @@ public string Id /// internal bool IsAnalysisEnabled { get; set; } + /// + /// Gets a boolean that determines whether this file is + /// in-memory or not (either unsaved or non-file content). + /// + public bool IsInMemory { get; private set; } + /// /// Gets a string containing the full contents of the file. /// @@ -125,6 +131,7 @@ public ScriptFile(string filePath, string clientFilePath, TextReader textReader) this.FilePath = filePath; this.ClientFilePath = clientFilePath; this.IsAnalysisEnabled = true; + this.IsInMemory = Workspace.IsPathInMemory(filePath); this.SetFileContents(textReader.ReadToEnd()); } diff --git a/src/PowerShellEditorServices/Workspace/Workspace.cs b/src/PowerShellEditorServices/Workspace/Workspace.cs index 779f64faa..ac857f4fb 100644 --- a/src/PowerShellEditorServices/Workspace/Workspace.cs +++ b/src/PowerShellEditorServices/Workspace/Workspace.cs @@ -24,6 +24,15 @@ public class Workspace #endregion + #region Properties + + /// + /// Gets or sets the root path of the workspace. + /// + public string WorkspacePath { get; set; } + + #endregion + #region Public Methods /// @@ -155,15 +164,20 @@ private void RecursivelyFindReferences( ScriptFile scriptFile, Dictionary referencedScriptFiles) { + // Get the base path of the current script for use in resolving relative paths + string baseFilePath = + GetBaseFilePath( + scriptFile.FilePath); + ScriptFile referencedFile; foreach (string referencedFileName in scriptFile.ReferencedFiles) { string resolvedScriptPath = this.ResolveRelativeScriptPath( - scriptFile.FilePath, + baseFilePath, referencedFileName); - // make sure file exists before trying to get the file + // Make sure file exists before trying to get the file if (File.Exists(resolvedScriptPath)) { // Get the referenced file if it's not already in referencedScriptFiles @@ -183,33 +197,62 @@ private void RecursivelyFindReferences( private string ResolveFilePath(string filePath) { - if (filePath.StartsWith(@"file://")) + if (!IsPathInMemory(filePath)) { - // Client sent the path in URI format, extract the local path and trim - // any extraneous slashes - Uri fileUri = new Uri(filePath); - filePath = fileUri.LocalPath.TrimStart('/'); - } + if (filePath.StartsWith(@"file://")) + { + // Client sent the path in URI format, extract the local path and trim + // any extraneous slashes + Uri fileUri = new Uri(filePath); + filePath = fileUri.LocalPath.TrimStart('/'); + } - // Some clients send paths with UNIX-style slashes, replace those if necessary - filePath = filePath.Replace('/', '\\'); + // Some clients send paths with UNIX-style slashes, replace those if necessary + filePath = filePath.Replace('/', '\\'); + + // Get the absolute file path + filePath = Path.GetFullPath(filePath); + } Logger.Write(LogLevel.Verbose, "Resolved path: " + filePath); - return Path.GetFullPath(filePath); + return filePath; } - private string ResolveRelativeScriptPath(string originalScriptPath, string relativePath) + internal static bool IsPathInMemory(string filePath) { - if (!Path.IsPathRooted(originalScriptPath)) + // When viewing PowerShell files in the Git diff viewer, VS Code + // sends the contents of the file at HEAD with a URI that starts + // with 'inmemory'. Untitled files which have been marked of + // type PowerShell have a path starting with 'untitled'. + return + filePath.StartsWith("inmemory") || + filePath.StartsWith("untitled"); + } + + private string GetBaseFilePath(string filePath) + { + if (IsPathInMemory(filePath)) + { + // If the file is in memory, use the workspace path + return this.WorkspacePath; + } + + if (!Path.IsPathRooted(filePath)) { // TODO: Assert instead? throw new InvalidOperationException( string.Format( "Must provide a full path for originalScriptPath: {0}", - originalScriptPath)); + filePath)); } + // Get the directory of the file path + return Path.GetDirectoryName(filePath); + } + + private string ResolveRelativeScriptPath(string baseFilePath, string relativePath) + { if (Path.IsPathRooted(relativePath)) { return relativePath; @@ -220,7 +263,7 @@ private string ResolveRelativeScriptPath(string originalScriptPath, string relat string combinedPath = Path.GetFullPath( Path.Combine( - Path.GetDirectoryName(originalScriptPath), + baseFilePath, relativePath)); return combinedPath; From ac82a4b9bf7d452a436ad063cd565aa292a405ef Mon Sep 17 00:00:00 2001 From: David Wilson Date: Fri, 20 Nov 2015 15:36:10 -0800 Subject: [PATCH 5/7] Fix bad rename of EditorServices.PowerShellContext In a previous commit, PowerShellSession was renamed to PowerShellContext. EditorServices.PowerShellSession was accidentally renamed to EditorServices.powerShellContext with a camel-cased name. This change corrects the casing on that property. --- src/PowerShellEditorServices.Host/DebugAdapter.cs | 8 ++++---- .../LanguageServer.cs | 4 ++-- src/PowerShellEditorServices.Host/MessageLoop.cs | 2 +- .../Session/EditorSession.cs | 14 +++++++------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/PowerShellEditorServices.Host/DebugAdapter.cs b/src/PowerShellEditorServices.Host/DebugAdapter.cs index 7f127df2b..d18395a38 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.powerShellContext + editorSession.PowerShellContext .ExecuteScriptAtPath(launchParams.Program) .ContinueWith( async (t) => @@ -141,15 +141,15 @@ protected Task HandleDisconnectRequest( if (e.NewSessionState == PowerShellContextState.Ready) { await requestContext.SendResult(null); - editorSession.powerShellContext.SessionStateChanged -= handler; + editorSession.PowerShellContext.SessionStateChanged -= handler; // TODO: Find a way to exit more gracefully! Environment.Exit(0); } }; - editorSession.powerShellContext.SessionStateChanged += handler; - editorSession.powerShellContext.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 c451c0c5c..fb6989fcb 100644 --- a/src/PowerShellEditorServices.Host/LanguageServer.cs +++ b/src/PowerShellEditorServices.Host/LanguageServer.cs @@ -147,7 +147,7 @@ protected async Task HandleShowOnlineHelpRequest( psCommand.AddScript(script); - var result = await editorSession.powerShellContext.ExecuteCommand( + var result = await editorSession.PowerShellContext.ExecuteCommand( psCommand); await requestContext.SendResult(null); @@ -422,7 +422,7 @@ protected async Task HandleCompletionResolveRequest( if (completionItem.Kind == CompletionItemKind.Function) { RunspaceHandle runspaceHandle = - await editorSession.powerShellContext.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 c6153d3a5..ced331b14 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.powerShellContext.OutputWritten += powerShellContext_OutputWritten; + this.editorSession.PowerShellContext.OutputWritten += powerShellContext_OutputWritten; if (this.runDebugAdapter) { diff --git a/src/PowerShellEditorServices/Session/EditorSession.cs b/src/PowerShellEditorServices/Session/EditorSession.cs index 8225480ea..e3c452a54 100644 --- a/src/PowerShellEditorServices/Session/EditorSession.cs +++ b/src/PowerShellEditorServices/Session/EditorSession.cs @@ -25,7 +25,7 @@ public class EditorSession /// /// Gets the PowerShellContext instance for this session. /// - public PowerShellContext powerShellContext { 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.powerShellContext = new PowerShellContext(); - this.LanguageService = new LanguageService(this.powerShellContext); + this.PowerShellContext = new PowerShellContext(); + this.LanguageService = new LanguageService(this.PowerShellContext); this.AnalysisService = new AnalysisService(); - this.DebugService = new DebugService(this.powerShellContext); + this.DebugService = new DebugService(this.PowerShellContext); } #endregion @@ -82,10 +82,10 @@ public void Dispose() this.AnalysisService = null; } - if (this.powerShellContext != null) + if (this.PowerShellContext != null) { - this.powerShellContext.Dispose(); - this.powerShellContext = null; + this.PowerShellContext.Dispose(); + this.PowerShellContext = null; } } From 61006aaa5e268bd74713855f7502143eb90dff54 Mon Sep 17 00:00:00 2001 From: David Wilson Date: Fri, 20 Nov 2015 16:13:42 -0800 Subject: [PATCH 6/7] Refine online help feature This change gives a namespace to the 'showonlinehelp' message type, making it 'powerShell/showOnlineHelp'. Also changes the request parameter type to a string and uses PSCommand.AddCommand, etc to avoid script injection attacks. --- .../LanguageServer.cs | 15 +++++++-------- .../LanguageServer/ShowOnlineHelpRequest.cs | 4 +++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/PowerShellEditorServices.Host/LanguageServer.cs b/src/PowerShellEditorServices.Host/LanguageServer.cs index fb6989fcb..47b3a7a2f 100644 --- a/src/PowerShellEditorServices.Host/LanguageServer.cs +++ b/src/PowerShellEditorServices.Host/LanguageServer.cs @@ -135,20 +135,19 @@ protected Task HandleShutdownRequest( } protected async Task HandleShowOnlineHelpRequest( - object helpParams, + string helpParams, EditorSession editorSession, RequestContext requestContext) { - var psCommand = new PSCommand(); - if (helpParams == null) { helpParams = "get-help"; } - var script = string.Format("get-help {0} -Online", helpParams); - - psCommand.AddScript(script); + var psCommand = new PSCommand(); + psCommand.AddCommand("Get-Help"); + psCommand.AddArgument(helpParams); + psCommand.AddParameter("Online"); - var result = await editorSession.PowerShellContext.ExecuteCommand( - psCommand); + await editorSession.PowerShellContext.ExecuteCommand( + psCommand); await requestContext.SendResult(null); } diff --git a/src/PowerShellEditorServices.Protocol/LanguageServer/ShowOnlineHelpRequest.cs b/src/PowerShellEditorServices.Protocol/LanguageServer/ShowOnlineHelpRequest.cs index 22378ed98..40235b5ae 100644 --- a/src/PowerShellEditorServices.Protocol/LanguageServer/ShowOnlineHelpRequest.cs +++ b/src/PowerShellEditorServices.Protocol/LanguageServer/ShowOnlineHelpRequest.cs @@ -9,6 +9,8 @@ namespace Microsoft.PowerShell.EditorServices.Protocol.LanguageServer { public class ShowOnlineHelpRequest { - public static readonly RequestType Type = RequestType.Create("showonlinehelp"); + public static readonly + RequestType Type = + RequestType.Create("powerShell/showOnlineHelp"); } } From 33f47ae95ac8026e93679211782a31cf55207a4e Mon Sep 17 00:00:00 2001 From: David Wilson Date: Fri, 20 Nov 2015 16:45:10 -0800 Subject: [PATCH 7/7] Bump version to 0.2.0 --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 45b26e3c7..732f2c921 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 0.1.0.{build} +version: 0.2.0.{build} os: Unstable configuration: Release clone_depth: 10 @@ -10,7 +10,7 @@ branches: assembly_info: patch: true file: '**\AssemblyInfo.*' - assembly_version: '0.1.0' # This version number should always have 0 for the last section + assembly_version: '0.2.0' # This version number should always have 0 for the last section assembly_file_version: '{version}' assembly_informational_version: '{version}'