Skip to content

Commit

Permalink
Add support for new C# language server (lsp-roslyn)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ruin0x11 committed Apr 16, 2024
1 parent e67008b commit 09f9837
Show file tree
Hide file tree
Showing 5 changed files with 511 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.org
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
* Changelog
** Unreleased 9.0.1
* Add support for GNAT Project (~gpr-mode~, ~gpr-ts-mode~).
* Add support for C# via the [[https://github.com/dotnet/roslyn/tree/main/src/Features/LanguageServer][Roslyn language server]].

** 9.0.0
* Add language server config for QML (Qt Modeling Language) using qmlls.
Expand Down
136 changes: 136 additions & 0 deletions clients/lsp-roslyn-stdpipe.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
param (
[string]$ServerName = ".",
[Parameter(Mandatory=$true)][string]$PipeName
)

# Use named pipe as stdin/out

$Source = @"
using System;
using System.Text;
using System.IO;
using System.IO.Pipes;
using System.Threading.Tasks;
public static class StdPipe
{
public static void RouteToPipe(string pipeServer, string pipeName)
{
var pipeClient = new NamedPipeClientStream(pipeServer, pipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
pipeClient.Connect();
var pipeReader = new BufferedStream(pipeClient);
var pipeWriter = new BufferedStream(pipeClient);
var stdin = new BufferedStream(Console.OpenStandardInput());
var stdout = new BufferedStream(Console.OpenStandardOutput());
var tasks = new Task<byte[]>[2]
{
ReadHeaderDelimitedAsync(pipeReader),
ReadHeaderDelimitedAsync(stdin)
};
while (true)
{
var doneIdx = Task.WaitAny(tasks);
var bytesRead = tasks[doneIdx].Result;
if (doneIdx == 0)
{
// pipe in -> stdout
if (bytesRead.Length == 0)
{
// pipe was closed
break;
}
stdout.Write(bytesRead, 0, bytesRead.Length);
stdout.Flush();
tasks[doneIdx] = ReadHeaderDelimitedAsync(pipeReader);
}
else
{
// stdin -> pipe out
pipeWriter.Write(bytesRead, 0, bytesRead.Length);
pipeWriter.Flush();
tasks[doneIdx] = ReadHeaderDelimitedAsync(stdin);
}
}
}
private static async Task<byte[]> ReadHeaderDelimitedAsync(Stream stream)
{
// Assigning new tasks with this function blocks the thread
// unless this is awaited first.
await Task.Yield();
var idx = 0;
var header = new byte[64];
int b = 0;
do
{
var bytesRead = await stream.ReadAsync(header, idx, 1);
if (bytesRead == 0)
continue;
b = header[idx++];
} while (b != '\r');
var colonPos = Array.IndexOf(header, (byte)':');
if (colonPos == -1)
{
return new byte[0];
}
var headerName = new byte[colonPos];
Array.Copy(header, headerName, colonPos);
var headerContent = new byte[idx - colonPos - 1];
Array.Copy(header, colonPos + 2, headerContent, 0, headerContent.Length - 2);
if (Encoding.ASCII.GetString(headerName) != "Content-Length")
{
return new byte[0];
}
if (headerContent.Length > 20)
{
return new byte[0];
}
int contentLength;
try
{
contentLength = int.Parse(Encoding.ASCII.GetString(headerContent));
}
catch (Exception)
{
return new byte[0];
}
var buffer = new byte[contentLength + idx + 3];
var c = 0;
for (var i = 0; i < idx; i++)
{
buffer[c++] = header[i];
}
// LF, CRLF
var bytesToRead = contentLength + 3;
while (bytesToRead > 0)
{
var bytesRead = await stream.ReadAsync(buffer, c, bytesToRead);
bytesToRead -= bytesRead;
c += bytesRead;
}
return buffer;
}
}
"@

Add-Type -TypeDefinition $Source -Language CSharp

try {
[StdPipe]::RouteToPipe($ServerName, $PipeName)
} catch [System.AggregateException] {
Write-Error $error[0].exception.innerexception
throw $error[0].exception.innerexception
}

0 comments on commit 09f9837

Please sign in to comment.