Skip to content

Commit 0258858

Browse files
Merge pull request #48 from RLittlesII/execute-commands
Modified ExecuteCommandHandler parsing on the LSPRequestRouter
2 parents a77fb5d + 0ef1b0a commit 0258858

26 files changed

+675
-141
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,5 @@ buildlog
3535
# Build folder
3636
tools/*/
3737
/tools/packages.config.md5sum
38+
39+
.idea
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
using OmniSharp.Extensions.JsonRpc;
8+
9+
namespace OmniSharp.Extensions.LanguageServer.Abstractions
10+
{
11+
public interface IHandlerMatcherCollection : IEnumerable<IHandlerMatcher>
12+
{
13+
IDisposable Add(IHandlerMatcher handler);
14+
}
15+
}

src/Lsp/Abstractions/IHandlerMatch.cs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System.Collections.Generic;
2+
using OmniSharp.Extensions.JsonRpc.Server;
3+
4+
namespace OmniSharp.Extensions.LanguageServer.Abstractions
5+
{
6+
public interface IHandlerMatcher
7+
{
8+
/// <summary>
9+
/// Finds the first handler that matches the parameters.
10+
/// </summary>
11+
/// <param name="parameters">The parameters.</param>
12+
/// <param name="descriptors">The descriptors.</param>
13+
/// <returns></returns>
14+
IEnumerable<ILspHandlerDescriptor> FindHandler(object parameters, IEnumerable<ILspHandlerDescriptor> descriptors);
15+
}
16+
}

src/Lsp/Capabilities/Server/ExecuteCommandOptions.cs

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using Newtonsoft.Json;
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using Newtonsoft.Json;
24
using Newtonsoft.Json.Serialization;
35
using OmniSharp.Extensions.LanguageServer.Models;
46

@@ -15,9 +17,9 @@ public class ExecuteCommandOptions : IExecuteCommandOptions
1517
/// </summary>
1618
public Container<string> Commands { get; set; }
1719

18-
public static ExecuteCommandOptions Of(IExecuteCommandOptions options)
20+
public static ExecuteCommandOptions Of(IEnumerable<IExecuteCommandOptions> options)
1921
{
20-
return new ExecuteCommandOptions() { Commands = options.Commands };
22+
return new ExecuteCommandOptions() { Commands = options.SelectMany(x => x.Commands).ToArray() };
2123
}
2224
}
23-
}
25+
}

src/Lsp/ClientCapabilityProvider.cs

+32
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
34
using System.Reflection;
45
using OmniSharp.Extensions.JsonRpc;
@@ -42,8 +43,25 @@ public IOptionsGetter GetStaticOptions<T>(Supports<T> capability)
4243

4344
public interface IOptionsGetter
4445
{
46+
/// <summary>
47+
/// Gets a single option from a given interface.
48+
/// </summary>
49+
/// <param name="action"></param>
50+
/// <typeparam name="TInterface"></typeparam>
51+
/// <typeparam name="TOptions"></typeparam>
52+
/// <returns></returns>
4553
TOptions Get<TInterface, TOptions>(Func<TInterface, TOptions> action)
4654
where TOptions : class;
55+
56+
/// <summary>
57+
/// Reduces the options from multiple interfaces to a single option.
58+
/// </summary>
59+
/// <typeparam name="TInterface"></typeparam>
60+
/// <typeparam name="TOptions"></typeparam>
61+
/// <param name="action"></param>
62+
/// <returns></returns>
63+
TOptions Reduce<TInterface, TOptions>(Func<IEnumerable<TInterface>, TOptions> action)
64+
where TOptions : class;
4765
}
4866

4967
private class NullOptionsGetter : IOptionsGetter
@@ -53,6 +71,12 @@ public TOptions Get<TInterface, TOptions>(Func<TInterface, TOptions> action)
5371
{
5472
return null;
5573
}
74+
75+
public TOptions Reduce<TInterface, TOptions>(Func<IEnumerable<TInterface>, TOptions> action)
76+
where TOptions : class
77+
{
78+
return null;
79+
}
5680
}
5781

5882
private class OptionsGetter : IOptionsGetter
@@ -71,6 +95,14 @@ public TOptions Get<TInterface, TOptions>(Func<TInterface, TOptions> action)
7195
.Select(x => x.Registration?.RegisterOptions is TInterface cl ? action(cl) : null)
7296
.FirstOrDefault(x => x != null);
7397
}
98+
99+
public TOptions Reduce<TInterface, TOptions>(Func<IEnumerable<TInterface>, TOptions> action)
100+
where TOptions : class
101+
{
102+
return action(_collection
103+
.Select(x => x.Registration?.RegisterOptions is TInterface cl ? cl : default)
104+
.Where(x => x != null));
105+
}
74106
}
75107
}
76108
}

src/Lsp/HandlerMatcherCollection.cs

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
using OmniSharp.Extensions.LanguageServer.Abstractions;
8+
9+
namespace OmniSharp.Extensions.LanguageServer
10+
{
11+
public class HandlerMatcherCollection : IHandlerMatcherCollection
12+
{
13+
internal readonly HashSet<IHandlerMatcher> _handlers = new HashSet<IHandlerMatcher>();
14+
15+
public IEnumerator<IHandlerMatcher> GetEnumerator()
16+
{
17+
return _handlers.GetEnumerator();
18+
}
19+
20+
public IDisposable Add(IHandlerMatcher handler)
21+
{
22+
_handlers.Add(handler);
23+
return new Disposable(() => _handlers.Remove(handler));
24+
}
25+
26+
IEnumerator IEnumerable.GetEnumerator()
27+
{
28+
return GetEnumerator();
29+
}
30+
}
31+
}

src/Lsp/LanguageServer.cs

+15-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using OmniSharp.Extensions.LanguageServer.Models;
1616
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
1717
using Microsoft.Extensions.Logging;
18+
using OmniSharp.Extensions.LanguageServer.Matchers;
1819

1920
namespace OmniSharp.Extensions.LanguageServer
2021
{
@@ -32,6 +33,7 @@ public class LanguageServer : ILanguageServer, IInitializeHandler, IInitializedH
3233
private readonly ILoggerFactory _loggerFactory;
3334
private readonly TaskCompletionSource<InitializeResult> _initializeComplete = new TaskCompletionSource<InitializeResult>();
3435
private readonly CompositeDisposable _disposable = new CompositeDisposable();
36+
private readonly HandlerMatcherCollection _handlerMactherCollection;
3537

3638
public LanguageServer(Stream input, Stream output, ILoggerFactory loggerFactory)
3739
: this(input, new OutputHandler(output), new LspReciever(), new RequestProcessIdentifier(), loggerFactory)
@@ -45,7 +47,13 @@ internal LanguageServer(Stream input, IOutputHandler output, LspReciever recieve
4547

4648
_reciever = reciever;
4749
_loggerFactory = loggerFactory;
48-
_requestRouter = new LspRequestRouter(_collection, loggerFactory);
50+
_handlerMactherCollection = new HandlerMatcherCollection
51+
{
52+
new TextDocumentMatcher(_loggerFactory.CreateLogger<TextDocumentMatcher>()),
53+
new ExecuteCommandMatcher(_loggerFactory.CreateLogger<ExecuteCommandMatcher>())
54+
};
55+
56+
_requestRouter = new LspRequestRouter(_collection, loggerFactory, _handlerMactherCollection);
4957
_responseRouter = new ResponseRouter(output);
5058
_connection = new Connection(input, output, reciever, requestProcessIdentifier, _requestRouter, _responseRouter, loggerFactory);
5159

@@ -110,6 +118,11 @@ public IDisposable AddHandlers(params IJsonRpcHandler[] handlers)
110118
}));
111119
}
112120

121+
public IDisposable AddHandlerMatcher(IHandlerMatcher handlerMatcher)
122+
{
123+
return _handlerMactherCollection.Add(handlerMatcher);
124+
}
125+
113126
public async Task Initialize()
114127
{
115128
_connection.Open();
@@ -160,7 +173,7 @@ async Task<InitializeResult> IRequestHandler<InitializeParams, InitializeResult>
160173
DocumentOnTypeFormattingProvider = ccp.GetStaticOptions(textDocumentCapabilities.OnTypeFormatting).Get<IDocumentOnTypeFormattingOptions, DocumentOnTypeFormattingOptions>(DocumentOnTypeFormattingOptions.Of),
161174
DocumentRangeFormattingProvider = ccp.HasStaticHandler(textDocumentCapabilities.RangeFormatting),
162175
DocumentSymbolProvider = ccp.HasStaticHandler(textDocumentCapabilities.DocumentSymbol),
163-
ExecuteCommandProvider = ccp.GetStaticOptions(workspaceCapabilities.ExecuteCommand).Get<IExecuteCommandOptions, ExecuteCommandOptions>(ExecuteCommandOptions.Of),
176+
ExecuteCommandProvider = ccp.GetStaticOptions(workspaceCapabilities.ExecuteCommand).Reduce<IExecuteCommandOptions, ExecuteCommandOptions>(ExecuteCommandOptions.Of),
164177
HoverProvider = ccp.HasStaticHandler(textDocumentCapabilities.Hover),
165178
ReferencesProvider = ccp.HasStaticHandler(textDocumentCapabilities.References),
166179
RenameProvider = ccp.HasStaticHandler(textDocumentCapabilities.Rename),
@@ -314,4 +327,3 @@ public void Dispose()
314327
public IDictionary<string, JToken> Experimental { get; } = new Dictionary<string, JToken>();
315328
}
316329
}
317-

src/Lsp/Lsp.csproj

+13-12
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
<Project Sdk="Microsoft.NET.Sdk">
2-
<PropertyGroup>
3-
<TargetFrameworks>net46;netstandard1.6</TargetFrameworks>
4-
<PlatformTarget>AnyCPU</PlatformTarget>
5-
<AssemblyName>OmniSharp.Extensions.LanguageServerProtocol</AssemblyName>
6-
<RootNamespace>OmniSharp.Extensions.LanguageServer</RootNamespace>
7-
</PropertyGroup>
8-
<ItemGroup>
9-
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
10-
<PackageReference Include="System.ValueTuple" Version="4.4.0" />
11-
<ProjectReference Include="..\JsonRpc\JsonRpc.csproj" />
12-
</ItemGroup>
13-
</Project>
2+
<PropertyGroup>
3+
<TargetFrameworks>net46;netstandard1.6</TargetFrameworks>
4+
<PlatformTarget>AnyCPU</PlatformTarget>
5+
<AssemblyName>OmniSharp.Extensions.LanguageServerProtocol</AssemblyName>
6+
<RootNamespace>OmniSharp.Extensions.LanguageServer</RootNamespace>
7+
<LangVersion>latest</LangVersion>
8+
</PropertyGroup>
9+
<ItemGroup>
10+
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
11+
<PackageReference Include="System.ValueTuple" Version="4.4.0" />
12+
<ProjectReference Include="..\JsonRpc\JsonRpc.csproj" />
13+
</ItemGroup>
14+
</Project>

src/Lsp/LspRequestRouter.cs

+11-72
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,26 @@
1111
using OmniSharp.Extensions.JsonRpc.Server;
1212
using OmniSharp.Extensions.JsonRpc.Server.Messages;
1313
using OmniSharp.Extensions.LanguageServer.Abstractions;
14+
using OmniSharp.Extensions.LanguageServer.Capabilities.Server;
1415
using OmniSharp.Extensions.LanguageServer.Messages;
1516
using OmniSharp.Extensions.LanguageServer.Models;
1617
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
1718

1819
namespace OmniSharp.Extensions.LanguageServer
1920
{
20-
class LspRequestRouter : IRequestRouter
21+
internal class LspRequestRouter : IRequestRouter
2122
{
2223
private readonly IHandlerCollection _collection;
24+
private readonly IEnumerable<IHandlerMatcher> _routeMatchers;
2325
private readonly ConcurrentDictionary<string, CancellationTokenSource> _requests = new ConcurrentDictionary<string, CancellationTokenSource>();
2426
private readonly ILogger<LspRequestRouter> _logger;
2527

26-
public LspRequestRouter(IHandlerCollection collection, ILoggerFactory loggerFactory)
28+
public LspRequestRouter(IHandlerCollection collection,
29+
ILoggerFactory loggerFactory,
30+
IHandlerMatcherCollection routeMatchers)
2731
{
2832
_collection = collection;
33+
_routeMatchers = routeMatchers;
2934
_logger = loggerFactory.CreateLogger<LspRequestRouter>();
3035
}
3136

@@ -59,78 +64,13 @@ private ILspHandlerDescriptor FindDescriptor(string method, JToken @params)
5964
return null;
6065
}
6166

62-
if (@params != null && descriptor.Params != null)
63-
{
64-
var paramsValue = @params.ToObject(descriptor.Params);
65-
if (paramsValue is ITextDocumentIdentifierParams textDocumentIdentifierParams)
66-
{
67-
var attributes = GetTextDocumentAttributes(textDocumentIdentifierParams.TextDocument.Uri);
68-
69-
_logger.LogTrace("Found attributes {Count}, {Attributes}", attributes.Count, attributes.Select(x => $"{x.LanguageId}:{x.Scheme}:{x.Uri}"));
70-
71-
return GetHandler(method, attributes);
72-
}
73-
else if (paramsValue is DidOpenTextDocumentParams openTextDocumentParams)
74-
{
75-
var attributes = new TextDocumentAttributes(openTextDocumentParams.TextDocument.Uri, openTextDocumentParams.TextDocument.LanguageId);
76-
77-
_logger.LogTrace("Created attribute {Attribute}", $"{attributes.LanguageId}:{attributes.Scheme}:{attributes.Uri}");
67+
if (@params == null || descriptor.Params == null) return descriptor;
7868

79-
return GetHandler(method, attributes);
80-
}
81-
else if (paramsValue is DidChangeTextDocumentParams didChangeDocumentParams)
82-
{
83-
// TODO: Do something with document version here?
84-
var attributes = GetTextDocumentAttributes(didChangeDocumentParams.TextDocument.Uri);
69+
var paramsValue = @params.ToObject(descriptor.Params);
8570

86-
_logger.LogTrace("Found attributes {Count}, {Attributes}", attributes.Count, attributes.Select(x => $"{x.LanguageId}:{x.Scheme}:{x.Uri}"));
71+
var lspHandlerDescriptors = _collection.Where(handler => handler.Method == method).ToList();
8772

88-
return GetHandler(method, attributes);
89-
}
90-
}
91-
92-
// TODO: How to split these
93-
// Do they fork and join?
94-
return descriptor;
95-
}
96-
97-
private List<TextDocumentAttributes> GetTextDocumentAttributes(Uri uri)
98-
{
99-
var textDocumentSyncHandlers = _collection
100-
.Select(x => x.Handler is ITextDocumentSyncHandler r ? r : null)
101-
.Where(x => x != null)
102-
.Distinct();
103-
return textDocumentSyncHandlers
104-
.Select(x => x.GetTextDocumentAttributes(uri))
105-
.Where(x => x != null)
106-
.Distinct()
107-
.ToList();
108-
}
109-
110-
private ILspHandlerDescriptor GetHandler(string method, IEnumerable<TextDocumentAttributes> attributes)
111-
{
112-
return attributes
113-
.Select(x => GetHandler(method, x))
114-
.FirstOrDefault(x => x != null);
115-
}
116-
117-
private ILspHandlerDescriptor GetHandler(string method, TextDocumentAttributes attributes)
118-
{
119-
_logger.LogTrace("Looking for handler for method {Method}", method);
120-
foreach (var handler in _collection.Where(x => x.Method == method))
121-
{
122-
_logger.LogTrace("Checking handler {Method}:{Handler}", method, handler.Handler.GetType().FullName);
123-
var registrationOptions = handler.Registration.RegisterOptions as TextDocumentRegistrationOptions;
124-
125-
_logger.LogTrace("Registration options {OptionsName}", registrationOptions.GetType().FullName);
126-
_logger.LogTrace("Document Selector {DocumentSelector}", registrationOptions.DocumentSelector.ToString());
127-
if (registrationOptions.DocumentSelector == null || registrationOptions.DocumentSelector.IsMatch(attributes))
128-
{
129-
_logger.LogTrace("Handler Selected: {Handler} via {DocumentSelector} (targeting {HandlerInterface})", handler.Handler.GetType().FullName, registrationOptions.DocumentSelector.ToString(), handler.HandlerType.GetType().FullName);
130-
return handler;
131-
}
132-
}
133-
return null;
73+
return _routeMatchers.SelectMany(strat => strat.FindHandler(paramsValue, lspHandlerDescriptors)).FirstOrDefault() ?? descriptor;
13474
}
13575

13676
public async Task RouteNotification(IHandlerDescriptor handler, Notification notification)
@@ -192,7 +132,6 @@ public async Task<ErrorResponse> RouteRequest(IHandlerDescriptor descriptor, Req
192132

193133
await result.ConfigureAwait(false);
194134

195-
196135
object responseValue = null;
197136
if (result.GetType().GetTypeInfo().IsGenericType)
198137
{

0 commit comments

Comments
 (0)