From 54504e48549d90bde057ebd86a3f225a6fa2ddf0 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Mon, 19 Aug 2019 18:22:25 -0700 Subject: [PATCH 1/6] handle log messages --- test/PowerShellEditorServices.Test.E2E/TestsFixture.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/PowerShellEditorServices.Test.E2E/TestsFixture.cs b/test/PowerShellEditorServices.Test.E2E/TestsFixture.cs index 092a8799a..14a35079f 100644 --- a/test/PowerShellEditorServices.Test.E2E/TestsFixture.cs +++ b/test/PowerShellEditorServices.Test.E2E/TestsFixture.cs @@ -83,6 +83,11 @@ public async Task InitializeAsync() DirectoryInfo testdir = Directory.CreateDirectory(Path.Combine(s_binDir, Path.GetRandomFileName())); + LanguageClient.Window.OnLogMessage((message, messageType) => + { + Console.WriteLine($"{messageType.ToString()}: {message}"); + }); + await LanguageClient.Initialize(testdir.FullName); // Make sure Script Analysis is enabled because we'll need it in the tests. From b520ac0dbd1a7294187b262e1b1293a57a2be680 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Tue, 20 Aug 2019 10:30:03 -0700 Subject: [PATCH 2/6] switch to using xUnit output helper --- test/PowerShellEditorServices.Test.E2E/TestsFixture.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/PowerShellEditorServices.Test.E2E/TestsFixture.cs b/test/PowerShellEditorServices.Test.E2E/TestsFixture.cs index 14a35079f..092a8799a 100644 --- a/test/PowerShellEditorServices.Test.E2E/TestsFixture.cs +++ b/test/PowerShellEditorServices.Test.E2E/TestsFixture.cs @@ -83,11 +83,6 @@ public async Task InitializeAsync() DirectoryInfo testdir = Directory.CreateDirectory(Path.Combine(s_binDir, Path.GetRandomFileName())); - LanguageClient.Window.OnLogMessage((message, messageType) => - { - Console.WriteLine($"{messageType.ToString()}: {message}"); - }); - await LanguageClient.Initialize(testdir.FullName); // Make sure Script Analysis is enabled because we'll need it in the tests. From 0f8572b720a327ef94d4d8a823202bee2894e1c1 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Tue, 20 Aug 2019 16:14:28 -0700 Subject: [PATCH 3/6] add hover handler --- .../LanguageServer/OmnisharpLanguageServer.cs | 1 + .../Services/Symbols/SymbolDetails.cs | 96 ++++++++++++++++ .../Services/Symbols/SymbolsService.cs | 35 ++++++ .../TextDocument/Handlers/HoverHandler.cs | 105 ++++++++++++++++++ .../LanguageServerProtocolMessageTests.cs | 20 ++++ 5 files changed, 257 insertions(+) create mode 100644 src/PowerShellEditorServices.Engine/Services/Symbols/SymbolDetails.cs create mode 100644 src/PowerShellEditorServices.Engine/Services/TextDocument/Handlers/HoverHandler.cs diff --git a/src/PowerShellEditorServices.Engine/LanguageServer/OmnisharpLanguageServer.cs b/src/PowerShellEditorServices.Engine/LanguageServer/OmnisharpLanguageServer.cs index 807b5bcaa..6908cc766 100644 --- a/src/PowerShellEditorServices.Engine/LanguageServer/OmnisharpLanguageServer.cs +++ b/src/PowerShellEditorServices.Engine/LanguageServer/OmnisharpLanguageServer.cs @@ -111,6 +111,7 @@ public async Task StartAsync() .WithHandler() .WithHandler() .WithHandler() + .WithHandler() .OnInitialize( async (languageServer, request) => { diff --git a/src/PowerShellEditorServices.Engine/Services/Symbols/SymbolDetails.cs b/src/PowerShellEditorServices.Engine/Services/Symbols/SymbolDetails.cs new file mode 100644 index 000000000..9a4f099fc --- /dev/null +++ b/src/PowerShellEditorServices.Engine/Services/Symbols/SymbolDetails.cs @@ -0,0 +1,96 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Diagnostics; +using System.Management.Automation; +using System.Threading.Tasks; +using Microsoft.PowerShell.EditorServices.Symbols; + +namespace Microsoft.PowerShell.EditorServices +{ + /// + /// Provides detailed information for a given symbol. + /// + [DebuggerDisplay("SymbolReference = {SymbolReference.SymbolType}/{SymbolReference.SymbolName}, DisplayString = {DisplayString}")] + public class SymbolDetails + { + #region Properties + + /// + /// Gets the original symbol reference which was used to gather details. + /// + public SymbolReference SymbolReference { get; private set; } + + /// + /// Gets the display string for this symbol. + /// + public string DisplayString { get; private set; } + + /// + /// Gets the documentation string for this symbol. Returns an + /// empty string if the symbol has no documentation. + /// + public string Documentation { get; private set; } + + #endregion + + #region Constructors + + static internal async Task CreateAsync( + SymbolReference symbolReference, + PowerShellContextService powerShellContext) + { + SymbolDetails symbolDetails = new SymbolDetails + { + SymbolReference = symbolReference + }; + + switch (symbolReference.SymbolType) + { + case SymbolType.Function: + CommandInfo commandInfo = await CommandHelpers.GetCommandInfoAsync( + symbolReference.SymbolName, + powerShellContext); + + if (commandInfo != null) + { + symbolDetails.Documentation = + await CommandHelpers.GetCommandSynopsisAsync( + commandInfo, + powerShellContext); + + if (commandInfo.CommandType == CommandTypes.Application) + { + symbolDetails.DisplayString = "(application) " + symbolReference.SymbolName; + } + else + { + symbolDetails.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. + symbolDetails.DisplayString = "function " + symbolReference.SymbolName; + } + + break; + case SymbolType.Parameter: + // TODO: Get parameter help + symbolDetails.DisplayString = "(parameter) " + symbolReference.SymbolName; + break; + case SymbolType.Variable: + symbolDetails.DisplayString = symbolReference.SymbolName; + break; + } + + return symbolDetails; + } + + #endregion + } +} diff --git a/src/PowerShellEditorServices.Engine/Services/Symbols/SymbolsService.cs b/src/PowerShellEditorServices.Engine/Services/Symbols/SymbolsService.cs index c9ab22463..b86afae70 100644 --- a/src/PowerShellEditorServices.Engine/Services/Symbols/SymbolsService.cs +++ b/src/PowerShellEditorServices.Engine/Services/Symbols/SymbolsService.cs @@ -8,6 +8,7 @@ using System.Collections.Specialized; using System.Linq; using System.Runtime.InteropServices; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.PowerShell.EditorServices.Symbols; @@ -200,6 +201,7 @@ public IReadOnlyList FindOccurrencesInFile( needsAliases: false).ToArray(); } + /// /// Finds a function definition in the script given a file location /// /// The details and contents of a open script file @@ -227,5 +229,38 @@ public SymbolReference FindFunctionDefinitionAtLocation( return symbolReference; } + + /// + /// Finds the details of the symbol at the given script file location. + /// + /// The ScriptFile in which the symbol can be located. + /// The line number at which the symbol can be located. + /// The column number at which the symbol can be located. + /// + public async Task FindSymbolDetailsAtLocationAsync( + ScriptFile scriptFile, + int lineNumber, + int columnNumber, + PowerShellContextService powerShellContext) + { + SymbolReference symbolReference = + AstOperations.FindSymbolAtPosition( + scriptFile.ScriptAst, + lineNumber, + columnNumber); + + if (symbolReference == null) + { + // TODO #21: Return Result + return null; + } + + symbolReference.FilePath = scriptFile.FilePath; + SymbolDetails symbolDetails = await SymbolDetails.CreateAsync( + symbolReference, + powerShellContext); + + return symbolDetails; + } } } diff --git a/src/PowerShellEditorServices.Engine/Services/TextDocument/Handlers/HoverHandler.cs b/src/PowerShellEditorServices.Engine/Services/TextDocument/Handlers/HoverHandler.cs new file mode 100644 index 000000000..98f6482f3 --- /dev/null +++ b/src/PowerShellEditorServices.Engine/Services/TextDocument/Handlers/HoverHandler.cs @@ -0,0 +1,105 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.PowerShell.EditorServices; +using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Server; + +namespace PowerShellEditorServices.Engine.Services.Handlers +{ + public class HoverHandler : IHoverHandler + { + private readonly DocumentSelector _documentSelector = new DocumentSelector( + new DocumentFilter() + { + Pattern = "**/*.ps*1" + } + ); + + private readonly ILogger _logger; + private readonly SymbolsService _symbolsService; + private readonly WorkspaceService _workspaceService; + private readonly PowerShellContextService _powerShellContextService; + + private HoverCapability _capability; + + public HoverHandler( + ILoggerFactory factory, + SymbolsService symbolsService, + WorkspaceService workspaceService, + PowerShellContextService powerShellContextService) + { + _logger = factory.CreateLogger(); + _symbolsService = symbolsService; + _workspaceService = workspaceService; + _powerShellContextService = powerShellContextService; + } + + public TextDocumentRegistrationOptions GetRegistrationOptions() + { + return new TextDocumentRegistrationOptions() + { + DocumentSelector = _documentSelector, + }; + } + + public async Task Handle(HoverParams request, CancellationToken cancellationToken) + { + ScriptFile scriptFile = + _workspaceService.GetFile( + request.TextDocument.Uri.ToString()); + + SymbolDetails symbolDetails = + await _symbolsService.FindSymbolDetailsAtLocationAsync( + scriptFile, + (int) request.Position.Line + 1, + (int) request.Position.Character + 1, + _powerShellContextService); + + List symbolInfo = new List(); + Range symbolRange = null; + + if (symbolDetails != null) + { + symbolInfo.Add(new MarkedString("PowerShell", symbolDetails.DisplayString)); + + if (!string.IsNullOrEmpty(symbolDetails.Documentation)) + { + symbolInfo.Add(new MarkedString("markdown", symbolDetails.Documentation)); + } + + symbolRange = GetRangeFromScriptRegion(symbolDetails.SymbolReference.ScriptRegion); + } + + return new Hover + { + Contents = new MarkedStringsOrMarkupContent(symbolInfo), + Range = symbolRange + }; + } + + public void SetCapability(HoverCapability capability) + { + _capability = capability; + } + + private static Range GetRangeFromScriptRegion(ScriptRegion scriptRegion) + { + return new Range + { + Start = new Position + { + Line = scriptRegion.StartLineNumber - 1, + Character = scriptRegion.StartColumnNumber - 1 + }, + End = new Position + { + Line = scriptRegion.EndLineNumber - 1, + Character = scriptRegion.EndColumnNumber - 1 + } + }; + } + } +} diff --git a/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs b/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs index a29563f70..c7593dc4e 100644 --- a/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs +++ b/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs @@ -644,5 +644,25 @@ public async Task CanSendCompletionAndCompletionResolveRequest() Assert.Contains("Writes customized output to a host", updatedCompletionItem.Documentation.String); } + + [Fact] + public async Task CanSendHoverRequest() + { + string filePath = NewTestFile("Write-Host"); + + Hover hover = await LanguageClient.TextDocument.Hover(filePath, line: 0, column: 1); + + Assert.True(hover.Contents.HasMarkedStrings); + Assert.Collection(hover.Contents.MarkedStrings, + str1 => + { + Assert.Equal("function Write-Host", str1.Value); + }, + str2 => + { + Assert.Equal("markdown", str2.Language); + Assert.Equal("Writes customized output to a host.", str2.Value); + }); + } } } From 8d8feae5500439db7b2052ad0d3265a9ccaab5ef Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Wed, 21 Aug 2019 14:43:08 -0700 Subject: [PATCH 4/6] move to language=powershell --- .../Services/TextDocument/Handlers/HoverHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PowerShellEditorServices.Engine/Services/TextDocument/Handlers/HoverHandler.cs b/src/PowerShellEditorServices.Engine/Services/TextDocument/Handlers/HoverHandler.cs index 98f6482f3..7a9d3aef3 100644 --- a/src/PowerShellEditorServices.Engine/Services/TextDocument/Handlers/HoverHandler.cs +++ b/src/PowerShellEditorServices.Engine/Services/TextDocument/Handlers/HoverHandler.cs @@ -14,7 +14,7 @@ public class HoverHandler : IHoverHandler private readonly DocumentSelector _documentSelector = new DocumentSelector( new DocumentFilter() { - Pattern = "**/*.ps*1" + Language = "powershell" } ); From 24084abb1ae2b3f609e7d589d55bf4691b6d8707 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Wed, 21 Aug 2019 14:58:52 -0700 Subject: [PATCH 5/6] refactoring for feedback --- .../Services/Symbols/SymbolDetails.cs | 26 +++++++------------ .../Services/Symbols/SymbolsService.cs | 1 - 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/PowerShellEditorServices.Engine/Services/Symbols/SymbolDetails.cs b/src/PowerShellEditorServices.Engine/Services/Symbols/SymbolDetails.cs index 9a4f099fc..3604481cc 100644 --- a/src/PowerShellEditorServices.Engine/Services/Symbols/SymbolDetails.cs +++ b/src/PowerShellEditorServices.Engine/Services/Symbols/SymbolDetails.cs @@ -64,31 +64,25 @@ await CommandHelpers.GetCommandSynopsisAsync( if (commandInfo.CommandType == CommandTypes.Application) { symbolDetails.DisplayString = "(application) " + symbolReference.SymbolName; + return symbolDetails; } - else - { - symbolDetails.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. - symbolDetails.DisplayString = "function " + symbolReference.SymbolName; } - break; + symbolDetails.DisplayString = "function " + symbolReference.SymbolName; + return symbolDetails; + case SymbolType.Parameter: // TODO: Get parameter help symbolDetails.DisplayString = "(parameter) " + symbolReference.SymbolName; - break; + return symbolDetails; + case SymbolType.Variable: symbolDetails.DisplayString = symbolReference.SymbolName; - break; - } + return symbolDetails; - return symbolDetails; + default: + return symbolDetails; + } } #endregion diff --git a/src/PowerShellEditorServices.Engine/Services/Symbols/SymbolsService.cs b/src/PowerShellEditorServices.Engine/Services/Symbols/SymbolsService.cs index b86afae70..b12bf5423 100644 --- a/src/PowerShellEditorServices.Engine/Services/Symbols/SymbolsService.cs +++ b/src/PowerShellEditorServices.Engine/Services/Symbols/SymbolsService.cs @@ -251,7 +251,6 @@ public async Task FindSymbolDetailsAtLocationAsync( if (symbolReference == null) { - // TODO #21: Return Result return null; } From 9da6a48f79d8b469ea7ce4e69fd4419a7533617a Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Wed, 21 Aug 2019 15:17:15 -0700 Subject: [PATCH 6/6] codacy --- .../Services/Symbols/SymbolsService.cs | 11 ++++++----- .../Services/TextDocument/Handlers/HoverHandler.cs | 7 +++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/PowerShellEditorServices.Engine/Services/Symbols/SymbolsService.cs b/src/PowerShellEditorServices.Engine/Services/Symbols/SymbolsService.cs index b12bf5423..e0c1e57be 100644 --- a/src/PowerShellEditorServices.Engine/Services/Symbols/SymbolsService.cs +++ b/src/PowerShellEditorServices.Engine/Services/Symbols/SymbolsService.cs @@ -23,7 +23,7 @@ public class SymbolsService #region Private Fields private readonly ILogger _logger; - + private readonly PowerShellContextService _powerShellContextService; private readonly IDocumentSymbolProvider[] _documentSymbolProviders; #endregion @@ -36,9 +36,11 @@ public class SymbolsService /// /// An ILoggerFactory implementation used for writing log messages. public SymbolsService( - ILoggerFactory factory) + ILoggerFactory factory, + PowerShellContextService powerShellContextService) { _logger = factory.CreateLogger(); + _powerShellContextService = powerShellContextService; _documentSymbolProviders = new IDocumentSymbolProvider[] { new ScriptDocumentSymbolProvider(VersionUtils.PSVersion), @@ -240,8 +242,7 @@ public SymbolReference FindFunctionDefinitionAtLocation( public async Task FindSymbolDetailsAtLocationAsync( ScriptFile scriptFile, int lineNumber, - int columnNumber, - PowerShellContextService powerShellContext) + int columnNumber) { SymbolReference symbolReference = AstOperations.FindSymbolAtPosition( @@ -257,7 +258,7 @@ public async Task FindSymbolDetailsAtLocationAsync( symbolReference.FilePath = scriptFile.FilePath; SymbolDetails symbolDetails = await SymbolDetails.CreateAsync( symbolReference, - powerShellContext); + _powerShellContextService); return symbolDetails; } diff --git a/src/PowerShellEditorServices.Engine/Services/TextDocument/Handlers/HoverHandler.cs b/src/PowerShellEditorServices.Engine/Services/TextDocument/Handlers/HoverHandler.cs index 7a9d3aef3..633ef2900 100644 --- a/src/PowerShellEditorServices.Engine/Services/TextDocument/Handlers/HoverHandler.cs +++ b/src/PowerShellEditorServices.Engine/Services/TextDocument/Handlers/HoverHandler.cs @@ -12,7 +12,7 @@ namespace PowerShellEditorServices.Engine.Services.Handlers public class HoverHandler : IHoverHandler { private readonly DocumentSelector _documentSelector = new DocumentSelector( - new DocumentFilter() + new DocumentFilter { Language = "powershell" } @@ -39,7 +39,7 @@ public HoverHandler( public TextDocumentRegistrationOptions GetRegistrationOptions() { - return new TextDocumentRegistrationOptions() + return new TextDocumentRegistrationOptions { DocumentSelector = _documentSelector, }; @@ -55,8 +55,7 @@ public async Task Handle(HoverParams request, CancellationToken cancellat await _symbolsService.FindSymbolDetailsAtLocationAsync( scriptFile, (int) request.Position.Line + 1, - (int) request.Position.Character + 1, - _powerShellContextService); + (int) request.Position.Character + 1); List symbolInfo = new List(); Range symbolRange = null;