Skip to content

Commit

Permalink
Added a fix for #141 with test validating that it works
Browse files Browse the repository at this point in the history
  • Loading branch information
david-driscoll committed Jul 13, 2019
1 parent 59204cd commit 53d33d9
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 18 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ tools/*/
coverage.*.xml
coverage.json
coverage.info
/codealike.json
4 changes: 2 additions & 2 deletions LSP.sln
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2003
# Visual Studio Version 16
VisualStudioVersion = 16.0.29025.244
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D764E024-3D3F-4112-B932-2DB722A1BACC}"
ProjectSection(SolutionItems) = preProject
Expand Down
14 changes: 8 additions & 6 deletions src/JsonRpc/IRequestRouter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@

namespace OmniSharp.Extensions.JsonRpc
{
public interface IRequestRouter<TDescriptor>
public interface IRequestRouter
{
Task RouteNotification(Notification notification, CancellationToken token);
Task<ErrorResponse> RouteRequest(Request request, CancellationToken token);
void CancelRequest(object id);
}

public interface IRequestRouter<TDescriptor> : IRequestRouter
{
TDescriptor GetDescriptor(Notification notification);
TDescriptor GetDescriptor(Request request);

Task RouteNotification(Notification notification, CancellationToken token);
Task RouteNotification(TDescriptor descriptor, Notification notification, CancellationToken token);
Task<ErrorResponse> RouteRequest(Request request, CancellationToken token);
Task<ErrorResponse> RouteRequest(TDescriptor descriptor, Request request, CancellationToken token);

void CancelRequest(object id);
}
}
4 changes: 2 additions & 2 deletions src/JsonRpc/RequestRouterBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,12 @@ private string GetId(object id)
return id?.ToString();
}

Task IRequestRouter<TDescriptor>.RouteNotification(Notification notification, CancellationToken token)
Task IRequestRouter.RouteNotification(Notification notification, CancellationToken token)
{
return RouteNotification(GetDescriptor(notification), notification, token);
}

Task<ErrorResponse> IRequestRouter<TDescriptor>.RouteRequest(Request request, CancellationToken token)
Task<ErrorResponse> IRequestRouter.RouteRequest(Request request, CancellationToken token)
{
return RouteRequest(GetDescriptor(request), request, token);
}
Expand Down
51 changes: 46 additions & 5 deletions src/Server/LanguageServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,14 @@ public static Task<ILanguageServer> From(Action<LanguageServerOptions> optionsAc
{
var options = new LanguageServerOptions();
optionsAction(options);
return From(options);
return From(options, token);
}

public static ILanguageServer PreInit(Action<LanguageServerOptions> optionsAction)
{
var options = new LanguageServerOptions();
optionsAction(options);
return PreInit(options);
}

public static async Task<ILanguageServer> From(LanguageServerOptions options, CancellationToken token)
Expand Down Expand Up @@ -91,6 +98,38 @@ public static async Task<ILanguageServer> From(LanguageServerOptions options, Ca
return server;
}

/// <summary>
/// Create the server without connecting to the client
///
/// Mainly used for unit testing
/// </summary>
/// <param name="options"></param>
/// <returns></returns>
public static ILanguageServer PreInit(LanguageServerOptions options)
{
var server = new LanguageServer(
options.Input,
options.Output,
options.Reciever,
options.RequestProcessIdentifier,
options.LoggerFactory,
options.Serializer,
options.Services,
options.HandlerTypes.Select(x => x.Assembly)
.Distinct().Concat(options.HandlerAssemblies),
options.Handlers,
options.NamedHandlers,
options.NamedServiceHandlers,
options.InitializeDelegates,
options.InitializedDelegates
);

if (options.AddDefaultLoggingProvider)
options.LoggerFactory.AddProvider(new LanguageServerLoggerProvider(server));

return server;
}

internal LanguageServer(
Stream input,
Stream output,
Expand Down Expand Up @@ -132,7 +171,9 @@ internal LanguageServer(
services.AddSingleton<ILanguageServer>(this);
services.AddTransient<IHandlerMatcher, ExecuteCommandMatcher>();
services.AddTransient<IHandlerMatcher, ResolveCommandMatcher>();
services.AddSingleton<IRequestRouter<ILspHandlerDescriptor>, LspRequestRouter>();
services.AddSingleton<LspRequestRouter>();
services.AddSingleton<IRequestRouter<ILspHandlerDescriptor>>(_ => _.GetRequiredService<LspRequestRouter>());
services.AddSingleton<IRequestRouter<IHandlerDescriptor>>(_ => _.GetRequiredService<LspRequestRouter>());
services.AddSingleton<IResponseRouter, ResponseRouter>();
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ResolveCommandPipeline<,>));

Expand Down Expand Up @@ -285,7 +326,7 @@ async Task<InitializeResult> IRequestHandler<InitializeParams, InitializeResult>
MinimumLogLevel = LogLevel.Trace;
}

_clientVersion = request.Capabilities.GetClientVersion();
_clientVersion = request.Capabilities?.GetClientVersion() ?? ClientVersion.Lsp2;
_serializer.SetClientCapabilities(_clientVersion.Value, request.Capabilities);

var supportedCapabilities = new List<ISupports>();
Expand All @@ -312,8 +353,8 @@ async Task<InitializeResult> IRequestHandler<InitializeParams, InitializeResult>

await Task.WhenAll(_initializeDelegates.Select(c => c(this, request)));

var textDocumentCapabilities = ClientSettings.Capabilities.TextDocument;
var workspaceCapabilities = ClientSettings.Capabilities.Workspace;
var textDocumentCapabilities = ClientSettings.Capabilities?.TextDocument ?? new TextDocumentClientCapabilities();
var workspaceCapabilities = ClientSettings.Capabilities?.Workspace ?? new WorkspaceClientCapabilities();

var ccp = new ClientCapabilityProvider(_collection);

Expand Down
16 changes: 14 additions & 2 deletions src/Server/LspRequestRouter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

namespace OmniSharp.Extensions.LanguageServer.Server
{
internal class LspRequestRouter : RequestRouterBase<ILspHandlerDescriptor>
internal class LspRequestRouter : RequestRouterBase<ILspHandlerDescriptor>, IRequestRouter<IHandlerDescriptor>
{
private readonly IEnumerable<ILspHandlerDescriptor> _collection;
private readonly IEnumerable<IHandlerMatcher> _handlerMatchers;
Expand Down Expand Up @@ -68,6 +68,18 @@ private ILspHandlerDescriptor FindDescriptor(string method, JToken @params)

return _handlerMatchers.SelectMany(strat => strat.FindHandler(paramsValue, lspHandlerDescriptors)).FirstOrDefault() ?? descriptor;
}


IHandlerDescriptor IRequestRouter<IHandlerDescriptor>.GetDescriptor(Notification notification) => GetDescriptor(notification);
IHandlerDescriptor IRequestRouter<IHandlerDescriptor>.GetDescriptor(Request request) => GetDescriptor(request);
Task IRequestRouter<IHandlerDescriptor>.RouteNotification(IHandlerDescriptor descriptor, Notification notification, CancellationToken token) =>
RouteNotification(
descriptor is ILspHandlerDescriptor d ? d : throw new Exception("This should really never happen, seriously, only hand this correct descriptors"),
notification,
token);
Task<ErrorResponse> IRequestRouter<IHandlerDescriptor>.RouteRequest(IHandlerDescriptor descriptor, Request request, CancellationToken token) =>
RouteRequest(
descriptor is ILspHandlerDescriptor d ? d : throw new Exception("This should really never happen, seriously, only hand this correct descriptors"),
request,
token);
}
}
25 changes: 24 additions & 1 deletion test/Lsp.Tests/LanguageServerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using NSubstitute;
using OmniSharp.Extensions.Embedded.MediatR;
using OmniSharp.Extensions.LanguageServer.Client;
using OmniSharp.Extensions.LanguageServer.Client.Processes;
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
using OmniSharp.Extensions.LanguageServer.Server;
using Xunit;
Expand All @@ -19,7 +23,7 @@ public LanguageServerTests(ITestOutputHelper testOutputHelper) : base(testOutput
{
}

[Fact(Skip="Disabled to see if build passes on ci")]
[Fact(Skip = "Disabled to see if build passes on ci")]
public async Task Works_With_IWorkspaceSymbolsHandler()
{
var process = new NamedPipeServerProcess(Guid.NewGuid().ToString("N"), LoggerFactory);
Expand Down Expand Up @@ -50,5 +54,24 @@ await Task.WhenAll(
var server = await serverStart;
server.AddHandlers(handler);
}

[Fact]
public async Task GH141_CrashesWithEmptyInitializeParams()
{
var process = new NamedPipeServerProcess(Guid.NewGuid().ToString("N"), LoggerFactory);
await process.Start();
var server = LanguageServer.PreInit(x => x
.WithInput(process.ClientOutputStream)
.WithOutput(process.ClientInputStream)
.WithLoggerFactory(LoggerFactory)
.AddDefaultLoggingProvider()
.WithMinimumLogLevel(LogLevel.Trace)
) as IRequestHandler<InitializeParams, InitializeResult>;

var handler = server as IRequestHandler<InitializeParams, InitializeResult>;

Func<Task> a = async () => await handler.Handle(new InitializeParams() { }, CancellationToken.None);
a.Should().NotThrow();
}
}
}

0 comments on commit 53d33d9

Please sign in to comment.