Skip to content
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

Support pipe & socket flags for language server startup #5852

Merged
merged 4 commits into from
Feb 3, 2022
Merged
Show file tree
Hide file tree
Changes from 2 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
6 changes: 6 additions & 0 deletions src/Bicep.Cli.IntegrationTests/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@
"System.Xml.XmlDocument": "4.3.0"
}
},
"CommandLineParser": {
"type": "Transitive",
"resolved": "2.8.0",
"contentHash": "eco2HlKQBY4Joz9odHigzGpVzv6pjsXnY5lziioMveQxr+i2Z7xYcIOMeZTgYiqnMtMAbXMXsVhrNfWO5vJS8Q=="
},
"DiffPlex": {
"type": "Transitive",
"resolved": "1.7.0",
Expand Down Expand Up @@ -1775,6 +1780,7 @@
"type": "Project",
"dependencies": {
"Azure.Bicep.Core": "1.0.0",
"CommandLineParser": "2.8.0",
"OmniSharp.Extensions.LanguageServer": "0.19.2"
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/Bicep.Core.IntegrationTests/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@
"System.Xml.XmlDocument": "4.3.0"
}
},
"CommandLineParser": {
"type": "Transitive",
"resolved": "2.8.0",
"contentHash": "eco2HlKQBY4Joz9odHigzGpVzv6pjsXnY5lziioMveQxr+i2Z7xYcIOMeZTgYiqnMtMAbXMXsVhrNfWO5vJS8Q=="
},
"DiffPlex": {
"type": "Transitive",
"resolved": "1.7.0",
Expand Down Expand Up @@ -1765,6 +1770,7 @@
"type": "Project",
"dependencies": {
"Azure.Bicep.Core": "1.0.0",
"CommandLineParser": "2.8.0",
"OmniSharp.Extensions.LanguageServer": "0.19.2"
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/Bicep.Core.Samples/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@
"System.Xml.XmlDocument": "4.3.0"
}
},
"CommandLineParser": {
"type": "Transitive",
"resolved": "2.8.0",
"contentHash": "eco2HlKQBY4Joz9odHigzGpVzv6pjsXnY5lziioMveQxr+i2Z7xYcIOMeZTgYiqnMtMAbXMXsVhrNfWO5vJS8Q=="
},
"DiffPlex": {
"type": "Transitive",
"resolved": "1.7.0",
Expand Down Expand Up @@ -1753,6 +1758,7 @@
"type": "Project",
"dependencies": {
"Azure.Bicep.Core": "1.0.0",
"CommandLineParser": "2.8.0",
"OmniSharp.Extensions.LanguageServer": "0.19.2"
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/Bicep.Core.UnitTests/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,11 @@
"System.Xml.XmlDocument": "4.3.0"
}
},
"CommandLineParser": {
"type": "Transitive",
"resolved": "2.8.0",
"contentHash": "eco2HlKQBY4Joz9odHigzGpVzv6pjsXnY5lziioMveQxr+i2Z7xYcIOMeZTgYiqnMtMAbXMXsVhrNfWO5vJS8Q=="
},
"Json.More.Net": {
"type": "Transitive",
"resolved": "1.4.4",
Expand Down Expand Up @@ -1719,6 +1724,7 @@
"type": "Project",
"dependencies": {
"Azure.Bicep.Core": "1.0.0",
"CommandLineParser": "2.8.0",
"OmniSharp.Extensions.LanguageServer": "0.19.2"
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/Bicep.Decompiler.IntegrationTests/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@
"System.Xml.XmlDocument": "4.3.0"
}
},
"CommandLineParser": {
"type": "Transitive",
"resolved": "2.8.0",
"contentHash": "eco2HlKQBY4Joz9odHigzGpVzv6pjsXnY5lziioMveQxr+i2Z7xYcIOMeZTgYiqnMtMAbXMXsVhrNfWO5vJS8Q=="
},
"DiffPlex": {
"type": "Transitive",
"resolved": "1.7.0",
Expand Down Expand Up @@ -1738,6 +1743,7 @@
"type": "Project",
"dependencies": {
"Azure.Bicep.Core": "1.0.0",
"CommandLineParser": "2.8.0",
"OmniSharp.Extensions.LanguageServer": "0.19.2"
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/Bicep.Decompiler.UnitTests/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@
"System.Xml.XmlDocument": "4.3.0"
}
},
"CommandLineParser": {
"type": "Transitive",
"resolved": "2.8.0",
"contentHash": "eco2HlKQBY4Joz9odHigzGpVzv6pjsXnY5lziioMveQxr+i2Z7xYcIOMeZTgYiqnMtMAbXMXsVhrNfWO5vJS8Q=="
},
"DiffPlex": {
"type": "Transitive",
"resolved": "1.7.0",
Expand Down Expand Up @@ -1738,6 +1743,7 @@
"type": "Project",
"dependencies": {
"Azure.Bicep.Core": "1.0.0",
"CommandLineParser": "2.8.0",
"OmniSharp.Extensions.LanguageServer": "0.19.2"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ public static async Task<LanguageServerHelper> StartServerWithClientConnectionAs
ModuleRestoreScheduler = creationOptions.ModuleRestoreScheduler ?? BicepTestConstants.ModuleRestoreScheduler
};

var server = new Server(serverPipe.Reader, clientPipe.Writer, creationOptions);
var server = new Server(
creationOptions,
options => options
.WithInput(serverPipe.Reader)
.WithOutput(clientPipe.Writer));
var _ = server.RunAsync(CancellationToken.None); // do not wait on this async method, or you'll be waiting a long time!

var client = LanguageClient.PreInit(options =>
Expand Down
6 changes: 6 additions & 0 deletions src/Bicep.LangServer.IntegrationTests/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,11 @@
"System.Xml.XmlDocument": "4.3.0"
}
},
"CommandLineParser": {
"type": "Transitive",
"resolved": "2.8.0",
"contentHash": "eco2HlKQBY4Joz9odHigzGpVzv6pjsXnY5lziioMveQxr+i2Z7xYcIOMeZTgYiqnMtMAbXMXsVhrNfWO5vJS8Q=="
},
"DiffPlex": {
"type": "Transitive",
"resolved": "1.7.0",
Expand Down Expand Up @@ -1778,6 +1783,7 @@
"type": "Project",
"dependencies": {
"Azure.Bicep.Core": "1.0.0",
"CommandLineParser": "2.8.0",
"OmniSharp.Extensions.LanguageServer": "0.19.2"
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/Bicep.LangServer.UnitTests/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,11 @@
"System.Xml.XmlDocument": "4.3.0"
}
},
"CommandLineParser": {
"type": "Transitive",
"resolved": "2.8.0",
"contentHash": "eco2HlKQBY4Joz9odHigzGpVzv6pjsXnY5lziioMveQxr+i2Z7xYcIOMeZTgYiqnMtMAbXMXsVhrNfWO5vJS8Q=="
},
"DiffPlex": {
"type": "Transitive",
"resolved": "1.7.0",
Expand Down Expand Up @@ -1778,6 +1783,7 @@
"type": "Project",
"dependencies": {
"Azure.Bicep.Core": "1.0.0",
"CommandLineParser": "2.8.0",
"OmniSharp.Extensions.LanguageServer": "0.19.2"
}
},
Expand Down
1 change: 1 addition & 0 deletions src/Bicep.LangServer/Bicep.LangServer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

<ItemGroup>
<PackageReference Include="OmniSharp.Extensions.LanguageServer" Version="0.19.2" />
<PackageReference Include="CommandLineParser" Version="2.8.0" />
</ItemGroup>

<ItemGroup>
Expand Down
97 changes: 88 additions & 9 deletions src/Bicep.LangServer/Program.cs
Original file line number Diff line number Diff line change
@@ -1,32 +1,111 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Linq;
using System.IO.Pipes;
using System.Net.Sockets;
using System.Runtime;
using System.Threading;
using System.Threading.Tasks;
using CommandLine;
using Bicep.Core.Utils;
using System.IO;
using System.Net;
using System.Diagnostics;

namespace Bicep.LanguageServer
{
public class Program
{
public static async Task Main()
public class CommandLineOptions
{
[Option("pipe", Required = false, HelpText = "The named pipe to connect to for LSP communication")]
public string? Pipe { get; set; }

[Option("socket", Required = false, HelpText = "The TCP socket to connect to for LSP communication")]
anthony-c-martin marked this conversation as resolved.
Show resolved Hide resolved
public string? Socket { get; set; }

[Option("stdio", Required = false, HelpText = "If set, use stdin/stdout for LSP communication")]
public bool Stdio { get; set; }

[Option("wait-for-debugger", Required = false, HelpText = "If set, wait for a dotnet debugger to be attached before starting the server")]
public bool WaitForDebugger { get; set; }
}

public static async Task Main(string[] args)
=> await RunWithCancellationAsync(async cancellationToken =>
{
string profilePath = DirHelper.GetTempPath();
var profilePath = DirHelper.GetTempPath();
ProfileOptimization.SetProfileRoot(profilePath);
ProfileOptimization.StartProfile("bicepserver.profile");

// the server uses JSON-RPC over stdin & stdout to communicate,
// so be careful not to use console for logging!
var server = new Server(
Console.OpenStandardInput(),
Console.OpenStandardOutput(),
new Server.CreationOptions());
var parser = new Parser(settings => {
settings.IgnoreUnknownArguments = true;
});

await server.RunAsync(cancellationToken);
await parser.ParseArguments<CommandLineOptions>(args)
.WithNotParsed((x) => Environment.Exit(1))
.WithParsedAsync(async options => await RunServer(options, cancellationToken));
});

private static async Task RunServer(CommandLineOptions options, CancellationToken cancellationToken)
{
if (options.WaitForDebugger)
{
while (!Debugger.IsAttached)
anthony-c-martin marked this conversation as resolved.
Show resolved Hide resolved
{
await Task.Delay(100, cancellationToken);
}

Debugger.Break();
}

Server server;
if (options.Pipe is not null)
{
var pipeName = options.Pipe;
if (pipeName.StartsWith(@"\\.\pipe\"))
{
pipeName = pipeName.Substring(@"\\.\pipe\".Length);
}

var clientPipe = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous);

await clientPipe.ConnectAsync(cancellationToken);

server = new(
new(),
options => options
.WithInput(clientPipe)
.WithOutput(clientPipe));
}
else if (options.Socket is not null)
{
var port = short.Parse(options.Socket);
anthony-c-martin marked this conversation as resolved.
Show resolved Hide resolved
var tcpClient = new TcpClient();

await tcpClient.ConnectAsync(IPAddress.Loopback, port, cancellationToken);
var tcpStream = tcpClient.GetStream();

server = new(
new(),
options => options
.WithInput(tcpStream)
.WithOutput(tcpStream)
.RegisterForDisposal(tcpClient));
}
else
{
server = new(
new(),
options => options
.WithInput(Console.OpenStandardInput())
.WithOutput(Console.OpenStandardOutput()));
}

await server.RunAsync(cancellationToken);
}

private static async Task RunWithCancellationAsync(Func<CancellationToken, Task> runFunc)
{
var cancellationTokenSource = new CancellationTokenSource();
Expand Down
12 changes: 1 addition & 11 deletions src/Bicep.LangServer/Server.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,7 @@ public record CreationOptions(

private readonly OmnisharpLanguageServer server;

public Server(PipeReader input, PipeWriter output, CreationOptions creationOptions)
: this(creationOptions, options => options.WithInput(input).WithOutput(output))
{
}

public Server(Stream input, Stream output, CreationOptions creationOptions)
: this(creationOptions, options => options.WithInput(input).WithOutput(output))
{
}

private Server(CreationOptions creationOptions, Action<LanguageServerOptions> onOptionsFunc)
public Server(CreationOptions creationOptions, Action<LanguageServerOptions> onOptionsFunc)
{
BicepDeploymentsInterop.Initialize();
server = OmnisharpLanguageServer.PreInit(options =>
Expand Down
6 changes: 6 additions & 0 deletions src/Bicep.LangServer/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
"version": 1,
"dependencies": {
"net6.0": {
"CommandLineParser": {
"type": "Direct",
"requested": "[2.8.0, )",
"resolved": "2.8.0",
"contentHash": "eco2HlKQBY4Joz9odHigzGpVzv6pjsXnY5lziioMveQxr+i2Z7xYcIOMeZTgYiqnMtMAbXMXsVhrNfWO5vJS8Q=="
},
"Microsoft.CodeAnalysis.BannedApiAnalyzers": {
"type": "Direct",
"requested": "[3.3.3, )",
Expand Down
6 changes: 5 additions & 1 deletion src/vscode-bicep/src/language/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ async function launchLanguageService(

const serverExecutable: lsp.Executable = {
command: dotnetCommandPath,
args: [languageServerPath],
args: [
languageServerPath,
// uncomment the next line to pause server startup until a dotnet debugger has been attached
// '--wait-for-debugger'
],
options: {
env: process.env,
},
Expand Down