Skip to content

Last LSP messages #1016

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ public async Task StartAsync()
.WithHandler<TemplateHandlers>()
.WithHandler<GetCommentHelpHandler>()
.WithHandler<EvaluateHandler>()
.WithHandler<GetCommandHandler>()
.WithHandler<ShowHelpHandler>()
.WithHandler<ExpandAliasHandler>()
.OnInitialize(
async (languageServer, request) =>
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

using System.Linq;
using System.Management.Automation;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.PowerShell.EditorServices;
using OmniSharp.Extensions.Embedded.MediatR;
using OmniSharp.Extensions.JsonRpc;

namespace PowerShellEditorServices.Engine.Services.Handlers
{
[Serial, Method("powerShell/expandAlias")]
public interface IExpandAliasHandler : IJsonRpcRequestHandler<ExpandAliasParams, ExpandAliasResult> { }

public class ExpandAliasParams : IRequest<ExpandAliasResult>
{
public string Text { get; set; }
}

public class ExpandAliasResult
{
public string Text { get; set; }
}

public class ExpandAliasHandler : IExpandAliasHandler
{
private readonly ILogger _logger;
private readonly PowerShellContextService _powerShellContextService;

public ExpandAliasHandler(ILoggerFactory factory, PowerShellContextService powerShellContextService)
{
_logger = factory.CreateLogger<ExpandAliasHandler>();
_powerShellContextService = powerShellContextService;
}

public async Task<ExpandAliasResult> Handle(ExpandAliasParams request, CancellationToken cancellationToken)
{
const string script = @"
function __Expand-Alias {

param($targetScript)

[ref]$errors=$null

$tokens = [System.Management.Automation.PsParser]::Tokenize($targetScript, $errors).Where({$_.type -eq 'command'}) |
Sort-Object Start -Descending

foreach ($token in $tokens) {
$definition=(Get-Command ('`'+$token.Content) -CommandType Alias -ErrorAction SilentlyContinue).Definition

if($definition) {
$lhs=$targetScript.Substring(0, $token.Start)
$rhs=$targetScript.Substring($token.Start + $token.Length)

$targetScript=$lhs + $definition + $rhs
}
}

$targetScript
}";

// TODO: Refactor to not rerun the function definition every time.
var psCommand = new PSCommand();
psCommand
.AddScript(script)
.AddStatement()
.AddCommand("__Expand-Alias")
.AddArgument(request.Text);
var result = await _powerShellContextService.ExecuteCommandAsync<string>(psCommand);

return new ExpandAliasResult
{
Text = result.First()
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

using System.Collections.Generic;
using System.Management.Automation;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.PowerShell.EditorServices;
using OmniSharp.Extensions.Embedded.MediatR;
using OmniSharp.Extensions.JsonRpc;

namespace PowerShellEditorServices.Engine.Services.Handlers
{
[Serial, Method("powerShell/getCommand")]
public interface IGetCommandHandler : IJsonRpcRequestHandler<GetCommandParams, List<PSCommandMessage>> { }

public class GetCommandParams : IRequest<List<PSCommandMessage>> { }

/// <summary>
/// Describes the message to get the details for a single PowerShell Command
/// from the current session
/// </summary>
public class PSCommandMessage
{
public string Name { get; set; }
public string ModuleName { get; set; }
public string DefaultParameterSet { get; set; }
public Dictionary<string, ParameterMetadata> Parameters { get; set; }
public System.Collections.ObjectModel.ReadOnlyCollection<CommandParameterSetInfo> ParameterSets { get; set; }
}

public class GetCommandHandler : IGetCommandHandler
{
private readonly ILogger<GetCommandHandler> _logger;
private readonly PowerShellContextService _powerShellContextService;

public GetCommandHandler(ILoggerFactory factory, PowerShellContextService powerShellContextService)
{
_logger = factory.CreateLogger<GetCommandHandler>();
_powerShellContextService = powerShellContextService;
}

public async Task<List<PSCommandMessage>> Handle(GetCommandParams request, CancellationToken cancellationToken)
{
PSCommand psCommand = new PSCommand();

// Executes the following:
// Get-Command -CommandType Function,Cmdlet,ExternalScript | Select-Object -Property Name,ModuleName | Sort-Object -Property Name
psCommand
.AddCommand("Microsoft.PowerShell.Core\\Get-Command")
.AddParameter("CommandType", new[] { "Function", "Cmdlet", "ExternalScript" })
.AddCommand("Microsoft.PowerShell.Utility\\Select-Object")
.AddParameter("Property", new[] { "Name", "ModuleName" })
.AddCommand("Microsoft.PowerShell.Utility\\Sort-Object")
.AddParameter("Property", "Name");

IEnumerable<PSObject> result = await _powerShellContextService.ExecuteCommandAsync<PSObject>(psCommand);

var commandList = new List<PSCommandMessage>();
if (result != null)
{
foreach (dynamic command in result)
{
commandList.Add(new PSCommandMessage
{
Name = command.Name,
ModuleName = command.ModuleName,
Parameters = command.Parameters,
ParameterSets = command.ParameterSets,
DefaultParameterSet = command.DefaultParameterSet
});
}
}

return commandList;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public GetVersionHandler(ILoggerFactory factory)
_logger = factory.CreateLogger<GetVersionHandler>();
}

public Task<PowerShellVersionDetails> Handle(GetVersionParams request, CancellationToken cancellationToken)
public Task<PowerShellVersion> Handle(GetVersionParams request, CancellationToken cancellationToken)
{
var architecture = PowerShellProcessArchitecture.Unknown;
// This should be changed to using a .NET call sometime in the future... but it's just for logging purposes.
Expand All @@ -32,7 +32,8 @@ public Task<PowerShellVersionDetails> Handle(GetVersionParams request, Cancellat
}
}

return Task.FromResult(new PowerShellVersionDetails {
return Task.FromResult(new PowerShellVersion
{
Version = VersionUtils.PSVersion.ToString(),
Edition = VersionUtils.PSEdition,
DisplayVersion = VersionUtils.PSVersion.ToString(2),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,43 @@
using Microsoft.PowerShell.EditorServices.Session;
using OmniSharp.Extensions.Embedded.MediatR;
using OmniSharp.Extensions.JsonRpc;

namespace PowerShellEditorServices.Engine.Services.Handlers
{
[Serial, Method("powerShell/getVersion")]
public interface IGetVersionHandler : IJsonRpcRequestHandler<GetVersionParams, PowerShellVersionDetails> { }
public interface IGetVersionHandler : IJsonRpcRequestHandler<GetVersionParams, PowerShellVersion> { }

public class GetVersionParams : IRequest<PowerShellVersionDetails> { }
public class GetVersionParams : IRequest<PowerShellVersion> { }

public class PowerShellVersionDetails {
public class PowerShellVersion
{
public string Version { get; set; }
public string DisplayVersion { get; set; }
public string Edition { get; set; }
public string Architecture { get; set; }

public PowerShellVersion()
{
}

public PowerShellVersion(PowerShellVersionDetails versionDetails)
{
this.Version = versionDetails.VersionString;
this.DisplayVersion = $"{versionDetails.Version.Major}.{versionDetails.Version.Minor}";
this.Edition = versionDetails.Edition;

switch (versionDetails.Architecture)
{
case PowerShellProcessArchitecture.X64:
this.Architecture = "x64";
break;
case PowerShellProcessArchitecture.X86:
this.Architecture = "x86";
break;
default:
this.Architecture = "Architecture Unknown";
break;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

using System.Management.Automation;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.PowerShell.EditorServices;
using OmniSharp.Extensions.Embedded.MediatR;
using OmniSharp.Extensions.JsonRpc;

namespace PowerShellEditorServices.Engine.Services.Handlers
{
[Serial, Method("powerShell/showHelp")]
public interface IShowHelpHandler : IJsonRpcNotificationHandler<ShowHelpParams> { }

public class ShowHelpParams : IRequest
{
public string Text { get; set; }
}

public class ShowHelpHandler : IShowHelpHandler
{
private readonly ILogger _logger;
private readonly PowerShellContextService _powerShellContextService;

public ShowHelpHandler(ILoggerFactory factory, PowerShellContextService powerShellContextService)
{
_logger = factory.CreateLogger<ShowHelpHandler>();
_powerShellContextService = powerShellContextService;
}

public async Task<Unit> Handle(ShowHelpParams request, CancellationToken cancellationToken)
{
const string CheckHelpScript = @"
[CmdletBinding()]
param (
[String]$CommandName
)
try {
$command = Microsoft.PowerShell.Core\Get-Command $CommandName -ErrorAction Stop
} catch [System.Management.Automation.CommandNotFoundException] {
$PSCmdlet.ThrowTerminatingError($PSItem)
}
try {
$helpUri = [Microsoft.PowerShell.Commands.GetHelpCodeMethods]::GetHelpUri($command)

$oldSslVersion = [System.Net.ServicePointManager]::SecurityProtocol
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12

# HEAD means we don't need the content itself back, just the response header
$status = (Microsoft.PowerShell.Utility\Invoke-WebRequest -Method Head -Uri $helpUri -TimeoutSec 5 -ErrorAction Stop).StatusCode
if ($status -lt 400) {
$null = Microsoft.PowerShell.Core\Get-Help $CommandName -Online
return
}
} catch {
# Ignore - we want to drop out to Get-Help -Full
} finally {
[System.Net.ServicePointManager]::SecurityProtocol = $oldSslVersion
}

return Microsoft.PowerShell.Core\Get-Help $CommandName -Full
";

string helpParams = request.Text;
if (string.IsNullOrEmpty(helpParams)) { helpParams = "Get-Help"; }

PSCommand checkHelpPSCommand = new PSCommand()
.AddScript(CheckHelpScript, useLocalScope: true)
.AddArgument(helpParams);

// TODO: Rather than print the help in the console, we should send the string back
// to VSCode to display in a help pop-up (or similar)
await _powerShellContextService.ExecuteCommandAsync<PSObject>(checkHelpPSCommand, sendOutputToHost: true);
return Unit.Value;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using Microsoft.PowerShell.EditorServices.Engine;
using Microsoft.PowerShell.EditorServices.Session;
using Microsoft.PowerShell.EditorServices.Utility;
using PowerShellEditorServices.Engine.Services.Handlers;

namespace Microsoft.PowerShell.EditorServices
{
Expand Down Expand Up @@ -155,6 +156,7 @@ public PowerShellContextService(
this.logger = logger;
this.isPSReadLineEnabled = isPSReadLineEnabled;

RunspaceChanged += PowerShellContext_RunspaceChangedAsync;
ExecutionStatusChanged += PowerShellContext_ExecutionStatusChangedAsync;
}

Expand Down Expand Up @@ -1726,6 +1728,39 @@ private void OnExecutionStatusChanged(
hadErrors));
}

private void PowerShellContext_RunspaceChangedAsync(object sender, RunspaceChangedEventArgs e)
{
_languageServer.SendNotification(
"powerShell/runspaceChanged",
new MinifiedRunspaceDetails(e.NewRunspace));
}


// TODO: Refactor this, RunspaceDetails, PowerShellVersion, and PowerShellVersionDetails
// It's crazy that this is 4 different types.
// P.S. MinifiedRunspaceDetails use to be called RunspaceDetails... as in, there were 2 DIFFERENT
// RunspaceDetails types in this codebase but I've changed it to be minified since the type is
// slightly simpler than the other RunspaceDetails.
public class MinifiedRunspaceDetails
{
public PowerShellVersion PowerShellVersion { get; set; }

public RunspaceLocation RunspaceType { get; set; }

public string ConnectionString { get; set; }

public MinifiedRunspaceDetails()
{
}

public MinifiedRunspaceDetails(RunspaceDetails eventArgs)
{
this.PowerShellVersion = new PowerShellVersion(eventArgs.PowerShellVersion);
this.RunspaceType = eventArgs.Location;
this.ConnectionString = eventArgs.ConnectionString;
}
}

/// <summary>
/// Event hook on the PowerShell context to listen for changes in script execution status
/// </summary>
Expand Down
Loading