Skip to content

The entire Debug Adapter moved over... #1043

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
32 changes: 32 additions & 0 deletions src/PowerShellEditorServices.Engine/Hosting/EditorServicesHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.PowerShell.EditorServices.Engine.Server;
using Microsoft.PowerShell.EditorServices.Engine.Services;
using Microsoft.PowerShell.EditorServices.Utility;
using Serilog;

Expand Down Expand Up @@ -328,6 +330,36 @@ public void StartDebugService(

break;

case EditorServiceTransportType.Stdio:
_debugServer = new PsesDebugServer(
_factory,
Console.OpenStandardInput(),
Console.OpenStandardOutput());

Task.Run(async () =>
{
_logger.LogInformation("Starting debug server");

IServiceProvider serviceProvider = useExistingSession
? _languageServer.LanguageServer.Services
: new ServiceCollection().AddSingleton<PowerShellContextService>(
(provider) => PowerShellContextService.Create(
_factory,
provider.GetService<OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServer>(),
profilePaths,
_featureFlags,
_enableConsoleRepl,
_internalHost,
_hostDetails,
_additionalModules))
.BuildServiceProvider();

await _debugServer.StartAsync(serviceProvider);
_logger.LogInformation(
$"Debug service started, type = {config.TransportType}, endpoint = {config.Endpoint}");
});
break;

default:
throw new NotSupportedException("not supported");
}
Expand Down
61 changes: 8 additions & 53 deletions src/PowerShellEditorServices.Engine/Server/PsesLanguageServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,15 @@ public async Task StartAsync()
.AddSingleton<ConfigurationService>()
.AddSingleton<PowerShellContextService>(
(provider) =>
GetFullyInitializedPowerShellContext(
PowerShellContextService.Create(
LoggerFactory,
provider.GetService<OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServer>(),
_profilePaths))
_profilePaths,
_featureFlags,
_enableConsoleRepl,
_internalHost,
_hostDetails,
_additionalModules))
.AddSingleton<TemplateService>()
.AddSingleton<EditorOperationsService>()
.AddSingleton<ExtensionService>(
Expand Down Expand Up @@ -158,57 +164,6 @@ public async Task WaitForShutdown()
await LanguageServer.WaitForExit;
}

private PowerShellContextService GetFullyInitializedPowerShellContext(
OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServer languageServer,
ProfilePaths profilePaths)
{
var logger = LoggerFactory.CreateLogger<PowerShellContextService>();

// PSReadLine can only be used when -EnableConsoleRepl is specified otherwise
// issues arise when redirecting stdio.
var powerShellContext = new PowerShellContextService(
logger,
languageServer,
_featureFlags.Contains("PSReadLine") && _enableConsoleRepl);

EditorServicesPSHostUserInterface hostUserInterface =
_enableConsoleRepl
? (EditorServicesPSHostUserInterface)new TerminalPSHostUserInterface(powerShellContext, logger, _internalHost)
: new ProtocolPSHostUserInterface(languageServer, powerShellContext, logger);

EditorServicesPSHost psHost =
new EditorServicesPSHost(
powerShellContext,
_hostDetails,
hostUserInterface,
logger);

Runspace initialRunspace = PowerShellContextService.CreateRunspace(psHost);
powerShellContext.Initialize(profilePaths, initialRunspace, true, hostUserInterface);

powerShellContext.ImportCommandsModuleAsync(
Path.Combine(
Path.GetDirectoryName(this.GetType().GetTypeInfo().Assembly.Location),
@"..\Commands"));

// TODO: This can be moved to the point after the $psEditor object
// gets initialized when that is done earlier than LanguageServer.Initialize
foreach (string module in this._additionalModules)
{
var command =
new PSCommand()
.AddCommand("Microsoft.PowerShell.Core\\Import-Module")
.AddParameter("Name", module);

powerShellContext.ExecuteCommandAsync<PSObject>(
command,
sendOutputToHost: false,
sendErrorToHost: true);
}

return powerShellContext;
}

protected abstract (Stream input, Stream output) GetInputOutputStreams();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,64 @@ public PowerShellContextService(
ExecutionStatusChanged += PowerShellContext_ExecutionStatusChangedAsync;
}

public static PowerShellContextService Create(
ILoggerFactory factory,
OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServer languageServer,
ProfilePaths profilePaths,
HashSet<string> featureFlags,
bool enableConsoleRepl,
PSHost internalHost,
HostDetails hostDetails,
string[] additionalModules
)
{
var logger = factory.CreateLogger<PowerShellContextService>();

// PSReadLine can only be used when -EnableConsoleRepl is specified otherwise
// issues arise when redirecting stdio.
var powerShellContext = new PowerShellContextService(
logger,
languageServer,
featureFlags.Contains("PSReadLine") && enableConsoleRepl);

EditorServicesPSHostUserInterface hostUserInterface =
enableConsoleRepl
? (EditorServicesPSHostUserInterface)new TerminalPSHostUserInterface(powerShellContext, logger, internalHost)
: new ProtocolPSHostUserInterface(languageServer, powerShellContext, logger);

EditorServicesPSHost psHost =
new EditorServicesPSHost(
powerShellContext,
hostDetails,
hostUserInterface,
logger);

Runspace initialRunspace = PowerShellContextService.CreateRunspace(psHost);
powerShellContext.Initialize(profilePaths, initialRunspace, true, hostUserInterface);

powerShellContext.ImportCommandsModuleAsync(
Path.Combine(
Path.GetDirectoryName(typeof(PowerShellContextService).GetTypeInfo().Assembly.Location),
@"..\Commands"));

// TODO: This can be moved to the point after the $psEditor object
// gets initialized when that is done earlier than LanguageServer.Initialize
foreach (string module in additionalModules)
{
var command =
new PSCommand()
.AddCommand("Microsoft.PowerShell.Core\\Import-Module")
.AddParameter("Name", module);

powerShellContext.ExecuteCommandAsync<PSObject>(
command,
sendOutputToHost: false,
sendErrorToHost: true);
}

return powerShellContext;
}

/// <summary>
///
/// </summary>
Expand Down
55 changes: 55 additions & 0 deletions test/PowerShellEditorServices.Test.E2E/LSPTestsFixures.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using OmniSharp.Extensions.LanguageServer.Client;
using OmniSharp.Extensions.LanguageServer.Client.Processes;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;

namespace PowerShellEditorServices.Test.E2E
{
public class LSPTestsFixture : TestsFixture
{
public override bool IsDebugAdapterTests => false;

public LanguageClient LanguageClient { get; private set; }
public List<Diagnostic> Diagnostics { get; set; }

public async override Task CustomInitializeAsync(
ILoggerFactory factory,
StdioServerProcess process)
{
LanguageClient = new LanguageClient(factory, process);

DirectoryInfo testdir =
Directory.CreateDirectory(Path.Combine(s_binDir, Path.GetRandomFileName()));

await LanguageClient.Initialize(testdir.FullName);

// Make sure Script Analysis is enabled because we'll need it in the tests.
LanguageClient.Workspace.DidChangeConfiguration(JObject.Parse(@"
{
""PowerShell"": {
""ScriptAnalysis"": {
""Enable"": true
}
}
}
"));

Diagnostics = new List<Diagnostic>();
LanguageClient.TextDocument.OnPublishDiagnostics((uri, diagnostics) =>
{
Diagnostics.AddRange(diagnostics);
});
}

public override async Task DisposeAsync()
{
await LanguageClient.Shutdown();
await _psesProcess.Stop();
LanguageClient?.Dispose();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

namespace PowerShellEditorServices.Test.E2E
{
public class LanguageServerProtocolMessageTests : IClassFixture<TestsFixture>, IDisposable
public class LanguageServerProtocolMessageTests : IClassFixture<LSPTestsFixture>, IDisposable
{
private readonly static string s_binDir =
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
Expand All @@ -33,7 +33,7 @@ public class LanguageServerProtocolMessageTests : IClassFixture<TestsFixture>, I
private readonly string PwshExe;
private readonly ITestOutputHelper _output;

public LanguageServerProtocolMessageTests(ITestOutputHelper output, TestsFixture data)
public LanguageServerProtocolMessageTests(ITestOutputHelper output, LSPTestsFixture data)
{
Diagnostics = new List<Diagnostic>();
LanguageClient = data.LanguageClient;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.0.0-preview8.19405.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="OmniSharp.Extensions.LanguageClient" Version="0.13.2-*" />
<PackageReference Include="OmniSharp.Extensions.LanguageClient" Version="1.0.0" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
</ItemGroup>
Expand Down
50 changes: 18 additions & 32 deletions test/PowerShellEditorServices.Test.E2E/TestsFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@

namespace PowerShellEditorServices.Test.E2E
{
public class TestsFixture : IAsyncLifetime
public abstract class TestsFixture : IAsyncLifetime
{
private readonly static string s_binDir =
protected readonly static string s_binDir =
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

private readonly static string s_bundledModulePath = new FileInfo(Path.Combine(
Expand All @@ -38,11 +38,11 @@ public class TestsFixture : IAsyncLifetime
const string s_hostVersion = "1.0.0";
readonly static string[] s_additionalModules = { "PowerShellEditorServices.VSCode" };

private StdioServerProcess _psesProcess;
protected StdioServerProcess _psesProcess;

public static string PwshExe { get; } = Environment.GetEnvironmentVariable("PWSH_EXE_NAME") ?? "pwsh";
public LanguageClient LanguageClient { get; private set; }
public List<Diagnostic> Diagnostics { get; set; }

public virtual bool IsDebugAdapterTests { get; set; }

public async Task InitializeAsync()
{
Expand All @@ -56,7 +56,8 @@ public async Task InitializeAsync()
processStartInfo.ArgumentList.Add("-NoProfile");
processStartInfo.ArgumentList.Add("-EncodedCommand");

string[] args = {
List<string> args = new List<string>
{
Path.Combine(s_bundledModulePath, "PowerShellEditorServices", "Start-EditorServices.ps1"),
"-LogPath", s_logPath,
"-LogLevel", s_logLevel,
Expand All @@ -70,6 +71,11 @@ public async Task InitializeAsync()
"-Stdio"
};

if (IsDebugAdapterTests)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is intended for a particular subclass, ideally the superclass shouldn't know about it at all. But in test classes, not such a big deal

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah this isn't final. There's more work I need to do wrt testing.

{
args.Add("-DebugServiceOnly");
}

string base64Str = Convert.ToBase64String(
System.Text.Encoding.Unicode.GetBytes(string.Join(' ', args)));

Expand All @@ -78,36 +84,16 @@ public async Task InitializeAsync()
_psesProcess = new StdioServerProcess(factory, processStartInfo);
await _psesProcess.Start();

LanguageClient = new LanguageClient(factory, _psesProcess);

DirectoryInfo testdir =
Directory.CreateDirectory(Path.Combine(s_binDir, Path.GetRandomFileName()));

await LanguageClient.Initialize(testdir.FullName);

// Make sure Script Analysis is enabled because we'll need it in the tests.
LanguageClient.Workspace.DidChangeConfiguration(JObject.Parse(@"
{
""PowerShell"": {
""ScriptAnalysis"": {
""Enable"": true
await CustomInitializeAsync(factory, _psesProcess);
}
}
}
"));

Diagnostics = new List<Diagnostic>();
LanguageClient.TextDocument.OnPublishDiagnostics((uri, diagnostics) =>
{
Diagnostics.AddRange(diagnostics);
});
}

public async Task DisposeAsync()
public virtual async Task DisposeAsync()
{
await LanguageClient.Shutdown();
await _psesProcess.Stop();
LanguageClient?.Dispose();
}

public abstract Task CustomInitializeAsync(
ILoggerFactory factory,
StdioServerProcess process);
}
}