OmniSharp / OmniSharp/csharp-language-server-protocol
Feature Request: Support handling LSP command line options for input/output
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 638
- Forks
- 109
- Avg merge
- 1m
- Merged PRs (30d)
- 2
Description
The language server protocol documents how to configure input/output via command line options.
It would be great if an extension method were available that handled command line options in this format and configured input/output accordingly.
Here's an example of what it might look like:
using OmniSharp.Extensions.LanguageServer.Server;
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO.Pipes;
using System.Net;
using System.Net.Sockets;
static class LanguageServerOptionsCommandLineExtensions
{
public static LanguageServerOptions WithCommandLineCommunicationChannel(this LanguageServerOptions options,
string[] args)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
CommandLineOptions commandLineOptions = CommandLineOptions.Parse(args);
switch (commandLineOptions.CommunicationChannel)
{
case CommunicationChannel.ConsoleInputOutput:
options.WithInput(Console.OpenStandardInput());
options.WithOutput(Console.OpenStandardOutput());
break;
case CommunicationChannel.Pipe:
NamedPipeClientStream pipe = new NamedPipeClientStream(".", commandLineOptions.PipeName,
PipeDirection.InOut, PipeOptions.Asynchronous);
// TODO: Make async? How to do that inside LanguageServer.From(Action<LanguageServerOptions>)?
//await pipe.ConnectAsync(cancellationToken);
pipe.Connect();
options.WithInput(pipe);
options.WithOutput(pipe);
break;
default:
Debug.Assert(commandLineOptions.CommunicationChannel == CommunicationChannel.Socket);
TcpClient client = new TcpClient();
options.RegisterForDisposal(client);
// TODO: Make async? How to do that inside LanguageServer.From(Action<LanguageServerOptions>)?
//await client.ConnectAsync(IPAddress.Loopback, commandLineOptions.Port, cancellationToken);
client.Connect(IPAddress.Loopback, commandLineOptions.Port);
NetworkStream stream = client.GetStream();
options.WithInput(stream);
options.WithOutput(stream);
break;
}
return options;
}
enum CommunicationChannel
{
ConsoleInputOutput,
Pipe,
Socket
}
struct CommandLineOptions
{
public CommunicationChannel CommunicationChannel { get; init; }
public string PipeName { get; init; }
public int Port { get; init; }
public static CommandLineOptions Parse(string[] args)
{
if (args == null || args.Length == 0)
{
return new CommandLineOptions { CommunicationChannel = CommunicationChannel.ConsoleInputOutput };
}
if (args.Length <= 2)
{
string firstArgument = args[0];
string secondArgument = args.Length > 1 ? args[1] : null;
if (firstArgument == "--stdio" && secondArgument == null)
{
return new CommandLineOptions { CommunicationChannel = CommunicationChannel.ConsoleInputOutput };
}
else if (firstArgument.StartsWith("--pipe"))
{
// TODO: Handle --pipe appropriately when not running on Windows.
if (firstArgument == "--pipe" && secondArgument != null && secondArgument.StartsWith(@"\\.\pipe\"))
{
return new CommandLineOptions
{
CommunicationChannel = CommunicationChannel.Pipe,
PipeName = secondArgument.Substring(@"\\.\pipe\".Length)
};
}
else if (firstArgument.StartsWith(@"--pipe=\\.\pipe\") && secondArgument == null)
{
string pipeName = firstArgument.Substring(@"--pipe=\\.\pipe\".Length);
return new CommandLineOptions
{
CommunicationChannel = CommunicationChannel.Pipe,
PipeName = pipeName
};
}
}
else if (firstArgument.StartsWith("--socket"))
{
if (firstArgument == "--socket" && secondArgument != null)
{
return new CommandLineOptions
{
CommunicationChannel = CommunicationChannel.Socket,
Port = int.Parse(secondArgument, NumberStyles.None, CultureInfo.InvariantCulture)
};
}
else if (firstArgument.StartsWith("--socket=") && secondArgument == null)
{
int port = int.Parse(firstArgument.Substring("--socket=".Length), NumberStyles.None,
CultureInfo.InvariantCulture);
return new CommandLineOptions
{
CommunicationChannel = CommunicationChannel.Socket,
Port = port
};
}
}
}
throw new ArgumentException("Invalid command line communication channel arguments.", nameof(args));
}
}
}
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with LanguageServerOptions and LanguageServer.From, then inspect the existing WithInput, WithOutput, and RegisterForDisposal entry points. Compare their lifecycle and connection behavior with the proposed --stdio, --pipe, and --socket forms. Done means an extension parses these options and configures the corresponding input and output channel, including the documented invalid-argument case.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- api, devtools
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100