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

Update LanguageServerHost to use NamedPipeServerStream #69918

Merged
merged 3 commits into from
Sep 13, 2023
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
@@ -0,0 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Runtime.Serialization;

namespace Microsoft.CodeAnalysis.LanguageServer;

[DataContract]
internal record NamedPipeInformation(
[property: DataMember(Name = "pipeName")] string PipeName);
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@
using System.Collections.Immutable;
using System.CommandLine;
using System.Diagnostics;
using System.IO.Pipes;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis.Contracts.Telemetry;
using Microsoft.CodeAnalysis.LanguageServer;
using Microsoft.CodeAnalysis.LanguageServer.BrokeredServices;
using Microsoft.CodeAnalysis.LanguageServer.BrokeredServices.Services.HelloWorld;
using Microsoft.CodeAnalysis.LanguageServer.HostWorkspace;
using Microsoft.CodeAnalysis.LanguageServer.LanguageServer;
using Microsoft.CodeAnalysis.LanguageServer.Logging;
using Microsoft.CodeAnalysis.LanguageServer.StarredSuggestions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Console;
using Newtonsoft.Json;

// Setting the title can fail if the process is run without a window, such
// as when launched detached from nodejs
Expand Down Expand Up @@ -44,7 +45,7 @@ static async Task RunAsync(ServerConfiguration serverConfiguration, Cancellation
LoggerFactory.Create(builder =>
{
builder.SetMinimumLevel(serverConfiguration.MinimumLogLevel);
builder.AddConsole(options => options.LogToStandardErrorThreshold = LogLevel.Trace);
builder.AddConsole();
// The console logger outputs control characters on unix for colors which don't render correctly in VSCode.
builder.AddSimpleConsole(formatterOptions => formatterOptions.ColorBehavior = LoggerColorBehavior.Disabled);
})
Expand Down Expand Up @@ -96,11 +97,25 @@ static async Task RunAsync(ServerConfiguration serverConfiguration, Cancellation

var serviceBrokerFactory = exportProvider.GetExportedValue<ServiceBrokerFactory>();
StarredCompletionAssemblyHelper.InitializeInstance(serverConfiguration.StarredCompletionsPath, loggerFactory, serviceBrokerFactory);

// TODO: Remove, the path should match exactly. Workaround for https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1830914.
Microsoft.CodeAnalysis.EditAndContinue.EditAndContinueMethodDebugInfoReader.IgnoreCaseWhenComparingDocumentNames = Path.DirectorySeparatorChar == '\\';

var server = new LanguageServerHost(Console.OpenStandardInput(), Console.OpenStandardOutput(), exportProvider, loggerFactory.CreateLogger(nameof(LanguageServerHost)));
var languageServerLogger = loggerFactory.CreateLogger(nameof(LanguageServerHost));

var (clientPipeName, serverPipeName) = CreateNewPipeNames();
var pipeServer = new NamedPipeServerStream(serverPipeName,
PipeDirection.InOut,
maxNumberOfServerInstances: 1,
PipeTransmissionMode.Byte,
PipeOptions.CurrentUserOnly | PipeOptions.Asynchronous);

// Send the named pipe connection info to the client
Console.WriteLine(JsonConvert.SerializeObject(new NamedPipeInformation(clientPipeName)));

// Wait for connection from client
await pipeServer.WaitForConnectionAsync(cancellationToken);

var server = new LanguageServerHost(pipeServer, pipeServer, exportProvider, languageServerLogger);
server.Start();

try
Expand Down Expand Up @@ -206,7 +221,24 @@ static CliRootCommand CreateCommandLineParser()

return RunAsync(serverConfiguration, cancellationToken);
});

return rootCommand;
}

static (string clientPipe, string serverPipe) CreateNewPipeNames()
{
// On windows, .NET and Nodejs use different formats for the pipe name
const string WINDOWS_NODJS_PREFIX = @"\\.\pipe\";
const string WINDOWS_DOTNET_PREFIX = @"\\.\";

var pipeName = Guid.NewGuid().ToString();

return RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? (WINDOWS_NODJS_PREFIX + pipeName, WINDOWS_DOTNET_PREFIX + pipeName)
: (GetUnixTypePipeName(pipeName), GetUnixTypePipeName(pipeName));
}

static string GetUnixTypePipeName(string pipeName)
{
// Unix-type pipes are actually writing to a file
return Path.Combine(Path.GetTempPath(), pipeName + ".sock");
}