Skip to content

Commit 1d8489f

Browse files
Added sample server, and example vscode extension, that are both works in progress
1 parent 7fa2e4e commit 1d8489f

30 files changed

+476
-42
lines changed

LSP.sln

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonRpc.Tests", "test\JsonR
2020
EndProject
2121
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lsp.Tests", "test\Lsp.Tests\Lsp.Tests.csproj", "{482B180B-FD5C-4705-BBE1-094C905F1E1F}"
2222
EndProject
23+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleServer", "sample\SampleServer\SampleServer.csproj", "{F2067F5F-FA4E-4990-B301-E7898FC4C45F}"
24+
EndProject
25+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{A316FCEC-81AD-45FB-93EE-C62CA09300DC}"
26+
EndProject
2327
Global
2428
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2529
Debug|Any CPU = Debug|Any CPU
@@ -42,6 +46,10 @@ Global
4246
{482B180B-FD5C-4705-BBE1-094C905F1E1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
4347
{482B180B-FD5C-4705-BBE1-094C905F1E1F}.Release|Any CPU.ActiveCfg = Release|Any CPU
4448
{482B180B-FD5C-4705-BBE1-094C905F1E1F}.Release|Any CPU.Build.0 = Release|Any CPU
49+
{F2067F5F-FA4E-4990-B301-E7898FC4C45F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
50+
{F2067F5F-FA4E-4990-B301-E7898FC4C45F}.Debug|Any CPU.Build.0 = Debug|Any CPU
51+
{F2067F5F-FA4E-4990-B301-E7898FC4C45F}.Release|Any CPU.ActiveCfg = Release|Any CPU
52+
{F2067F5F-FA4E-4990-B301-E7898FC4C45F}.Release|Any CPU.Build.0 = Release|Any CPU
4553
EndGlobalSection
4654
GlobalSection(SolutionProperties) = preSolution
4755
HideSolutionNode = FALSE
@@ -51,5 +59,6 @@ Global
5159
{50EA648A-67D3-4127-9517-EC7C1426E2E7} = {D764E024-3D3F-4112-B932-2DB722A1BACC}
5260
{35F9B883-36D0-4F3B-A191-9BBD05B798A7} = {2F323ED5-EBF8-45E1-B9D3-C014561B3DDA}
5361
{482B180B-FD5C-4705-BBE1-094C905F1E1F} = {2F323ED5-EBF8-45E1-B9D3-C014561B3DDA}
62+
{F2067F5F-FA4E-4990-B301-E7898FC4C45F} = {A316FCEC-81AD-45FB-93EE-C62CA09300DC}
5463
EndGlobalSection
5564
EndGlobal

sample/SampleServer/Program.cs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.Threading.Tasks;
4+
using JsonRpc;
5+
using Lsp;
6+
using Lsp.Capabilities.Client;
7+
using Lsp.Capabilities.Server;
8+
using Lsp.Models;
9+
using Lsp.Protocol;
10+
11+
namespace SampleServer
12+
{
13+
class Program
14+
{
15+
static void Main(string[] args)
16+
{
17+
MainAsync(args).Wait();
18+
}
19+
20+
static async Task MainAsync(string[] args)
21+
{
22+
while (!System.Diagnostics.Debugger.IsAttached)
23+
{
24+
await Task.Delay(100);
25+
}
26+
27+
var server = new LanguageServer(Console.In, Console.Out);
28+
29+
server.AddHandler(new TextDocumentHandler(server));
30+
31+
await server.Initialize();
32+
33+
await new TaskCompletionSource<object>().Task;
34+
}
35+
}
36+
37+
class TextDocumentHandler : ITextDocumentSyncHandler
38+
{
39+
private readonly ILanguageServer _router;
40+
41+
private readonly DocumentSelector _documentSelector = new DocumentSelector(
42+
new DocumentFilter() { Pattern = "*.cs" }
43+
);
44+
45+
private SynchronizationCapability _capability;
46+
47+
public TextDocumentHandler(ILanguageServer router)
48+
{
49+
_router = router;
50+
}
51+
52+
public TextDocumentSyncOptions Options { get; } = new TextDocumentSyncOptions() {
53+
WillSaveWaitUntil = false,
54+
WillSave = false,
55+
Change = TextDocumentSyncKind.None,
56+
Save = new SaveOptions() {
57+
IncludeText = false
58+
},
59+
OpenClose = false
60+
};
61+
62+
public Task Handle(DidChangeTextDocumentParams notification)
63+
{
64+
_router.LogMessage(new LogMessageParams() {
65+
Type = MessageType.Log,
66+
Message = "Hello World!!!!"
67+
});
68+
return Task.CompletedTask;
69+
}
70+
71+
TextDocumentChangeRegistrationOptions IRegistration<TextDocumentChangeRegistrationOptions>.GetRegistrationOptions()
72+
{
73+
return new TextDocumentChangeRegistrationOptions() {
74+
DocumentSelector = _documentSelector,
75+
SyncKind = Options.Change
76+
};
77+
}
78+
79+
public void SetCapability(SynchronizationCapability capability)
80+
{
81+
_capability = capability;
82+
}
83+
84+
public async Task Handle(DidOpenTextDocumentParams notification)
85+
{
86+
_router.LogMessage(new LogMessageParams() {
87+
Type = MessageType.Log,
88+
Message = "Hello World!!!!"
89+
});
90+
}
91+
92+
TextDocumentRegistrationOptions IRegistration<TextDocumentRegistrationOptions>.GetRegistrationOptions()
93+
{
94+
return new TextDocumentRegistrationOptions() {
95+
DocumentSelector = _documentSelector,
96+
};
97+
}
98+
99+
public Task Handle(DidCloseTextDocumentParams notification)
100+
{
101+
return Task.CompletedTask;
102+
}
103+
104+
public Task Handle(DidSaveTextDocumentParams notification)
105+
{
106+
return Task.CompletedTask;
107+
}
108+
109+
TextDocumentSaveRegistrationOptions IRegistration<TextDocumentSaveRegistrationOptions>.GetRegistrationOptions()
110+
{
111+
return new TextDocumentSaveRegistrationOptions() {
112+
DocumentSelector = _documentSelector,
113+
IncludeText = Options.Save.IncludeText
114+
};
115+
}
116+
public TextDocumentAttributes GetTextDocumentAttributes(Uri uri)
117+
{
118+
return new TextDocumentAttributes(uri, "csharp");
119+
}
120+
}
121+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>netcoreapp1.1</TargetFramework>
6+
<RuntimeIdentifier>win7-x64</RuntimeIdentifier>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<ProjectReference Include="../../src/Lsp/Lsp.csproj" />
11+
</ItemGroup>
12+
13+
</Project>

src/JsonRpc/ErrorResponse.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public ErrorResponse(Response response)
2121

2222
public bool IsError => Error != null;
2323
public Error Error { get; }
24+
public object Value => IsResponse ? (object)Response : IsError ? Error : null;
2425

2526
public static implicit operator ErrorResponse(Response response)
2627
{

src/JsonRpc/IResponseRouter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
using System.Threading.Tasks;
1+
using System.Threading.Tasks;
22
using Newtonsoft.Json.Linq;
33

44
namespace JsonRpc
55
{
66
public interface IResponseRouter
77
{
8-
Task SendNotification<T>(string method, T @params);
8+
void SendNotification<T>(string method, T @params);
99
Task<TResponse> SendRequest<T, TResponse>(string method, T @params);
1010
Task SendRequest<T>(string method, T @params);
1111
TaskCompletionSource<JToken> GetRequest(long id);

src/JsonRpc/InputHandler.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,12 @@ private async void ProcessInputStream()
7979
{
8080
if (_inputThread == null) return;
8181

82-
var buffer = new char[200];
82+
var buffer = new char[300];
8383
var current = await _input.ReadBlockAsync(buffer, 0, MinBuffer);
8484
while (current < MinBuffer || buffer[current - 4] != CR || buffer[current - 3] != LF ||
8585
buffer[current - 2] != CR || buffer[current - 1] != LF)
8686
{
87-
current += await _input.ReadBlockAsync(buffer, 0, 1);
87+
current += await _input.ReadBlockAsync(buffer, current, 1);
8888
}
8989

9090
var headersContent = Encoding.ASCII.GetString(Encoding.ASCII.GetBytes(buffer, 0, current));
@@ -154,7 +154,8 @@ private void HandleRequest(string request)
154154
type,
155155
async () => {
156156
var result = await _requestRouter.RouteRequest(item.Request);
157-
_outputHandler.Send(result);
157+
158+
_outputHandler.Send(result.Value);
158159
}
159160
));
160161
}

src/JsonRpc/ResponseRouter.cs

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,12 @@ public ResponseRouter(IOutputHandler outputHandler)
1818
_outputHandler = outputHandler;
1919
}
2020

21-
public Task SendNotification<T>(string method, T @params)
21+
public void SendNotification<T>(string method, T @params)
2222
{
23-
long nextId;
24-
lock (_lock)
25-
{
26-
nextId = _id++;
27-
}
28-
29-
var tcs = new TaskCompletionSource<JToken>();
30-
_requests.TryAdd(nextId, tcs);
31-
3223
_outputHandler.Send(new Client.Notification() {
3324
Method = method,
3425
Params = @params
3526
});
36-
37-
return tcs.Task.ContinueWith(x => _requests.TryRemove(nextId, out var _));
3827
}
3928

4029
public Task<TResponse> SendRequest<T, TResponse>(string method, T @params)

src/Lsp/HandlerDescriptor.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public HandlerDescriptor(string method, IJsonRpcHandler handler, Type handlerTyp
3535
public Registration Registration
3636
{
3737
get {
38+
if (!HasRegistration) return null;
3839
if (_registration != null) return _registration;
3940

4041
// TODO: Cache this

src/Lsp/LanguageServer.cs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ namespace Lsp
1818
{
1919
public class LanguageServer : IInitializeHandler, ILanguageServer, IDisposable
2020
{
21-
private readonly ITextDocumentSyncHandler _textDocumentSyncHandler;
2221
private readonly Connection _connection;
2322
private readonly LspRequestRouter _requestRouter;
2423
private readonly ShutdownHandler _shutdownHandler = new ShutdownHandler();
@@ -30,22 +29,20 @@ public class LanguageServer : IInitializeHandler, ILanguageServer, IDisposable
3029
private readonly TaskCompletionSource<InitializeResult> _initializeComplete = new TaskCompletionSource<InitializeResult>();
3130
private readonly CompositeDisposable _disposable = new CompositeDisposable();
3231

33-
public LanguageServer(TextReader input, TextWriter output, ITextDocumentSyncHandler textDocumentSyncHandler)
34-
: this(input, new OutputHandler(output), textDocumentSyncHandler, new LspReciever(), new RequestProcessIdentifier())
32+
public LanguageServer(TextReader input, TextWriter output)
33+
: this(input, new OutputHandler(output), new LspReciever(), new RequestProcessIdentifier())
3534
{
3635
}
3736

3837
internal LanguageServer(
3938
TextReader input,
4039
IOutputHandler output,
41-
ITextDocumentSyncHandler textDocumentSyncHandler,
4240
LspReciever reciever,
4341
IRequestProcessIdentifier requestProcessIdentifier
4442
)
4543
{
46-
_textDocumentSyncHandler = textDocumentSyncHandler;
4744
_reciever = reciever;
48-
_requestRouter = new LspRequestRouter(_collection, textDocumentSyncHandler);
45+
_requestRouter = new LspRequestRouter(_collection);
4946
_responseRouter = new ResponseRouter(output);
5047
_connection = new Connection(input, output, reciever, requestProcessIdentifier, _requestRouter, _responseRouter);
5148

@@ -55,7 +52,6 @@ IRequestProcessIdentifier requestProcessIdentifier
5552
AddHandler(this),
5653
AddHandler(_shutdownHandler),
5754
AddHandler(_exitHandler),
58-
AddHandler(_textDocumentSyncHandler),
5955
AddHandler(new CancelRequestHandler(_requestRouter))
6056
);
6157
}
@@ -219,9 +215,9 @@ public event ExitEventHandler Exit
219215
remove => _exitHandler.Exit -= value;
220216
}
221217

222-
public Task SendNotification<T>(string method, T @params)
218+
public void SendNotification<T>(string method, T @params)
223219
{
224-
return _responseRouter.SendNotification(method, @params);
220+
_responseRouter.SendNotification(method, @params);
225221
}
226222

227223
public Task<TResponse> SendRequest<T, TResponse>(string method, T @params)

src/Lsp/LspRequestRouter.cs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,12 @@ namespace Lsp
2020
class LspRequestRouter : IRequestRouter
2121
{
2222
private readonly IHandlerCollection _collection;
23-
private readonly ITextDocumentSyncHandler _textDocumentSyncHandler;
23+
private ITextDocumentSyncHandler _textDocumentSyncHandler;
2424
private readonly ConcurrentDictionary<string, CancellationTokenSource> _requests = new ConcurrentDictionary<string, CancellationTokenSource>();
2525

26-
public LspRequestRouter(IHandlerCollection collection, ITextDocumentSyncHandler textDocumentSyncHandler)
26+
public LspRequestRouter(IHandlerCollection collection)
2727
{
2828
_collection = collection;
29-
_textDocumentSyncHandler = textDocumentSyncHandler;
3029
}
3130

3231
private string GetId(object id)
@@ -49,6 +48,8 @@ private ILspHandlerDescriptor FindDescriptor(string method, JToken @params)
4948
var descriptor = _collection.FirstOrDefault(x => x.Method == method);
5049
if (descriptor is null) return null;
5150

51+
if (_textDocumentSyncHandler is null) return descriptor;
52+
5253
if (typeof(ITextDocumentIdentifierParams).GetTypeInfo().IsAssignableFrom(descriptor.Params))
5354
{
5455
var textDocumentIdentifierParams = @params.ToObject(descriptor.Params) as ITextDocumentIdentifierParams;
@@ -85,6 +86,7 @@ private ILspHandlerDescriptor GetHandler(string method, TextDocumentAttributes a
8586
public async void RouteNotification(Notification notification)
8687
{
8788
var handler = FindDescriptor(notification.Method, notification.Params);
89+
if (handler is null) { return; }
8890

8991
Task result;
9092
if (handler.Params is null)
@@ -162,7 +164,16 @@ public async Task<ErrorResponse> RouteRequest(Request request)
162164

163165
public IDisposable Add(IJsonRpcHandler handler)
164166
{
165-
return _collection.Add(handler);
167+
// ITextDocumentSyncHandler textDocumentSyncHandler
168+
var result = _collection.Add(handler);
169+
170+
if (_textDocumentSyncHandler == null)
171+
{
172+
_textDocumentSyncHandler = _collection
173+
.Select(x => x.Handler is ITextDocumentSyncHandler r ? r : null)
174+
.FirstOrDefault(x => x != null);
175+
}
176+
return result;
166177
}
167178

168179
public void CancelRequest(object id)

src/Lsp/Protocol/Document/PublishDiagnosticsExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ namespace Lsp.Protocol
77
{
88
public static class PublishDiagnosticsExtensions
99
{
10-
public static Task PublishDiagnostics(this ILanguageServer mediator, PublishDiagnosticsParams @params)
10+
public static void PublishDiagnostics(this ILanguageServer mediator, PublishDiagnosticsParams @params)
1111
{
12-
return mediator.SendNotification("textDocument/publishDiagnostics", @params);
12+
mediator.SendNotification("textDocument/publishDiagnostics", @params);
1313
}
1414
}
1515
}

src/Lsp/Protocol/Window/LogMessageExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ namespace Lsp.Protocol
77
{
88
public static class LogMessageExtensions
99
{
10-
public static Task LogMessage(this ILanguageServer mediator, LogMessageParams @params)
10+
public static void LogMessage(this ILanguageServer mediator, LogMessageParams @params)
1111
{
12-
return mediator.SendNotification("window/logMessage", @params);
12+
mediator.SendNotification("window/logMessage", @params);
1313
}
1414
}
1515
}

src/Lsp/Protocol/Window/SendTelemetryExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ namespace Lsp.Protocol
77
{
88
public static class SendTelemetryExtensions
99
{
10-
public static Task SendTelemetry(this ILanguageServer mediator, object @params)
10+
public static void SendTelemetry(this ILanguageServer mediator, object @params)
1111
{
12-
return mediator.SendNotification("telemetry/event", @params);
12+
mediator.SendNotification("telemetry/event", @params);
1313
}
1414
}
1515
}

0 commit comments

Comments
 (0)