Skip to content

Commit 838be84

Browse files
Added support for ILanguageServerConfiguration (#207)
* fixed last skipped test * Added support for configuration to be loaded from the client and reloaded from the client * Added sample configuration items to demo loading from the client * Updated the interface to return IDisposableConfiguration instead of just IDisposable * fixed unit tests * disabled tests that fail on ci * fixed build errors * Minor tweaks to progress reporting * renamed IDisposableConfguration to IScopedConfiguration
1 parent 1b6d19a commit 838be84

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+839
-316
lines changed

Directory.Build.targets

+7-5
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@
1212
<PackageReference Update="Rocket.Surgery.Nuke.DotNetCore" Version="0.13.4" />
1313
</ItemGroup>
1414
<ItemGroup>
15-
<PackageReference Update="Microsoft.Extensions.Logging" Version="2.0.0" />
16-
<PackageReference Update="Microsoft.Extensions.Logging.Abstractions" Version="2.0.0" />
17-
<PackageReference Update="Microsoft.Extensions.Logging.Debug" Version="2.0.0" />
18-
<PackageReference Update="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
19-
<PackageReference Update="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.0.0" />
15+
<PackageReference Update="Microsoft.Extensions.Logging" Version="3.1.0" />
16+
<PackageReference Update="Microsoft.Extensions.Logging.Abstractions" Version="3.1.0" />
17+
<PackageReference Update="Microsoft.Extensions.Logging.Debug" Version="3.1.0" />
18+
<PackageReference Update="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.0" />
19+
<PackageReference Update="Microsoft.Extensions.Configuration" Version="3.1.0" />
20+
<PackageReference Update="Microsoft.Extensions.DependencyInjection" Version="3.1.0" />
21+
<PackageReference Update="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.0" />
2022
<PackageReference Update="Newtonsoft.Json" Version="11.0.2" />
2123
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="16.5.0" />
2224
<PackageReference Update="xunit.runner.visualstudio" Version="2.4.1" />

LSP.sln

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".config", ".config", "{AE4D
2323
build.sh = build.sh
2424
Directory.Build.props = Directory.Build.props
2525
nuget.config = nuget.config
26+
Directory.Build.targets = Directory.Build.targets
2627
EndProjectSection
2728
EndProject
2829
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonRpc", "src\JsonRpc\JsonRpc.csproj", "{9AF43FA2-EF35-435E-B59E-724877E44DDA}"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System.Threading;
2+
using System.Threading.Tasks;
3+
using MediatR;
4+
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
5+
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
6+
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
7+
8+
namespace SampleServer
9+
{
10+
class DidChangeWatchedFilesHandler : IDidChangeWatchedFilesHandler
11+
{
12+
private DidChangeWatchedFilesCapability _capability;
13+
14+
public DidChangeWatchedFilesRegistrationOptions GetRegistrationOptions()
15+
{
16+
return new DidChangeWatchedFilesRegistrationOptions();
17+
}
18+
19+
public Task<Unit> Handle(DidChangeWatchedFilesParams request, CancellationToken cancellationToken)
20+
{
21+
return Unit.Task;
22+
}
23+
24+
public void SetCapability(DidChangeWatchedFilesCapability capability)
25+
{
26+
_capability = capability;
27+
}
28+
}
29+
}
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System.Threading;
2+
using System.Threading.Tasks;
3+
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
4+
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
5+
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
6+
7+
namespace SampleServer
8+
{
9+
class FoldingRangeHandler : IFoldingRangeHandler
10+
{
11+
private FoldingRangeCapability _capability;
12+
13+
public FoldingRangeRegistrationOptions GetRegistrationOptions()
14+
{
15+
return new FoldingRangeRegistrationOptions() {
16+
DocumentSelector = DocumentSelector.ForLanguage("csharp")
17+
};
18+
}
19+
20+
public Task<Container<FoldingRange>> Handle(FoldingRangeRequestParam request,
21+
CancellationToken cancellationToken)
22+
{
23+
return Task.FromResult(new Container<FoldingRange>(new FoldingRange() {
24+
StartLine = 10,
25+
EndLine = 20,
26+
Kind = FoldingRangeKind.Region,
27+
EndCharacter = 0,
28+
StartCharacter = 0
29+
}));
30+
}
31+
32+
public void SetCapability(FoldingRangeCapability capability)
33+
{
34+
_capability = capability;
35+
}
36+
}
37+
}

sample/SampleServer/Program.cs

+88-17
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Diagnostics;
4+
using System.Globalization;
5+
using System.Linq;
6+
using System.Threading;
37
using System.Threading.Tasks;
8+
using MediatR;
9+
using Microsoft.Extensions.Configuration;
410
using Microsoft.Extensions.DependencyInjection;
511
using Microsoft.Extensions.Logging;
12+
using Newtonsoft.Json;
13+
using Newtonsoft.Json.Linq;
614
using OmniSharp.Extensions.LanguageServer;
15+
using OmniSharp.Extensions.LanguageServer.Protocol.Client;
16+
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
17+
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
718
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
819
using OmniSharp.Extensions.LanguageServer.Server;
920
using Serilog;
21+
using ILanguageServer = OmniSharp.Extensions.LanguageServer.Server.ILanguageServer;
1022

1123
namespace SampleServer
1224
{
@@ -19,20 +31,22 @@ static void Main(string[] args)
1931

2032
static async Task MainAsync(string[] args)
2133
{
22-
//Debugger.Launch();
23-
//while (!System.Diagnostics.Debugger.IsAttached)
24-
//{
25-
// await Task.Delay(100);
26-
//}
34+
// Debugger.Launch();
35+
// while (!System.Diagnostics.Debugger.IsAttached)
36+
// {
37+
// await Task.Delay(100);
38+
// }
2739

2840
Log.Logger = new LoggerConfiguration()
29-
.Enrich.FromLogContext()
30-
.WriteTo.File("log.txt", rollingInterval: RollingInterval.Day)
41+
.Enrich.FromLogContext()
42+
.WriteTo.File("log.txt", rollingInterval: RollingInterval.Day)
3143
.MinimumLevel.Verbose()
3244
.CreateLogger();
3345

3446
Log.Logger.Information("This only goes file...");
3547

48+
IObserver<WorkDoneProgressReport> workDone = null;
49+
3650
var server = await LanguageServer.From(options =>
3751
options
3852
.WithInput(Console.OpenStandardInput())
@@ -47,25 +61,82 @@ static async Task MainAsync(string[] args)
4761
.WithHandler<MyWorkspaceSymbolsHandler>()
4862
.WithHandler<MyDocumentSymbolHandler>()
4963
.WithServices(x => x.AddLogging(b => b.SetMinimumLevel(LogLevel.Trace)))
50-
.WithServices(services =>
51-
{
52-
services.AddSingleton<Foo>(provider =>
53-
{
64+
.WithServices(services => {
65+
services.AddSingleton(provider => {
5466
var loggerFactory = provider.GetService<ILoggerFactory>();
5567
var logger = loggerFactory.CreateLogger<Foo>();
5668

5769
logger.LogInformation("Configuring");
5870

5971
return new Foo(logger);
6072
});
61-
}).OnInitialize((s, request) =>
62-
{
63-
var serviceProvider = s.Services;
64-
var foo = serviceProvider.GetService<Foo>();
73+
services.AddSingleton(new ConfigurationItem() {
74+
Section = "typescript",
75+
}).AddSingleton(new ConfigurationItem() {
76+
Section = "terminal",
77+
});
78+
})
79+
.OnInitialize(new InitializeDelegate(async (server, request) => {
80+
var manager = server.ProgressManager.WorkDone(request, new WorkDoneProgressBegin() {
81+
Title = "Server is starting...",
82+
Percentage = 10,
83+
});
84+
workDone = manager;
85+
86+
await Task.Delay(2000);
87+
88+
manager.OnNext(new WorkDoneProgressReport() {
89+
Percentage = 20,
90+
Message = "loading in progress"
91+
});
92+
}))
93+
.OnInitialized(new InitializedDelegate(async (server, request, response) => {
94+
workDone.OnNext(new WorkDoneProgressReport() {
95+
Percentage = 40,
96+
Message = "loading almost done",
97+
});
98+
99+
await Task.Delay(2000);
100+
101+
workDone.OnNext(new WorkDoneProgressReport() {
102+
Message = "loading done",
103+
Percentage = 100,
104+
});
105+
}))
106+
.OnStarted(async (languageServer, result) => {
107+
using var manager = await languageServer.ProgressManager.Create(new WorkDoneProgressBegin() { Title = "Doing some work..." });
108+
109+
manager.OnNext(new WorkDoneProgressReport() { Message = "doing things..." });
110+
await Task.Delay(10000);
111+
manager.OnNext(new WorkDoneProgressReport() { Message = "doing things... 1234" });
112+
await Task.Delay(10000);
113+
manager.OnNext(new WorkDoneProgressReport() { Message = "doing things... 56789" });
114+
115+
var logger = languageServer.Services.GetService<ILogger<Foo>>();
116+
var configuration = await languageServer.Configuration.GetConfiguration(
117+
new ConfigurationItem() {
118+
Section = "typescript",
119+
}, new ConfigurationItem() {
120+
Section = "terminal",
121+
});
122+
123+
var baseConfig = new JObject();
124+
foreach (var config in languageServer.Configuration.AsEnumerable())
125+
{
126+
baseConfig.Add(config.Key, config.Value);
127+
}
128+
129+
logger.LogInformation("Base Config: {Config}", baseConfig);
130+
131+
var scopedConfig = new JObject();
132+
foreach (var config in configuration.AsEnumerable())
133+
{
134+
scopedConfig.Add(config.Key, config.Value);
135+
}
65136

66-
return Task.CompletedTask;
137+
logger.LogInformation("Scoped Config: {Config}", scopedConfig);
67138
})
68-
);
139+
);
69140

70141
await server.WaitForExit;
71142
}

sample/SampleServer/TextDocumentHandler.cs

+13-36
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ namespace SampleServer
1717
class TextDocumentHandler : ITextDocumentSyncHandler
1818
{
1919
private readonly ILogger<TextDocumentHandler> _logger;
20+
private readonly ILanguageServerConfiguration _configuration;
2021

2122
private readonly DocumentSelector _documentSelector = new DocumentSelector(
2223
new DocumentFilter()
@@ -27,9 +28,11 @@ class TextDocumentHandler : ITextDocumentSyncHandler
2728

2829
private SynchronizationCapability _capability;
2930

30-
public TextDocumentHandler(ILogger<TextDocumentHandler> logger, Foo foo)
31+
public TextDocumentHandler(ILogger<TextDocumentHandler> logger, Foo foo,
32+
ILanguageServerConfiguration configuration)
3133
{
3234
_logger = logger;
35+
_configuration = configuration;
3336
foo.SayFoo();
3437
}
3538

@@ -44,7 +47,8 @@ public Task<Unit> Handle(DidChangeTextDocumentParams notification, CancellationT
4447
return Unit.Task;
4548
}
4649

47-
TextDocumentChangeRegistrationOptions IRegistration<TextDocumentChangeRegistrationOptions>.GetRegistrationOptions()
50+
TextDocumentChangeRegistrationOptions IRegistration<TextDocumentChangeRegistrationOptions>.
51+
GetRegistrationOptions()
4852
{
4953
return new TextDocumentChangeRegistrationOptions()
5054
{
@@ -62,6 +66,7 @@ public async Task<Unit> Handle(DidOpenTextDocumentParams notification, Cancellat
6266
{
6367
await Task.Yield();
6468
_logger.LogInformation("Hello world!");
69+
await _configuration.GetScopedConfiguration(notification.TextDocument.Uri);
6570
return Unit.Value;
6671
}
6772

@@ -75,6 +80,11 @@ TextDocumentRegistrationOptions IRegistration<TextDocumentRegistrationOptions>.G
7580

7681
public Task<Unit> Handle(DidCloseTextDocumentParams notification, CancellationToken token)
7782
{
83+
if (_configuration.TryGetScopedConfiguration(notification.TextDocument.Uri, out var disposable))
84+
{
85+
disposable.Dispose();
86+
}
87+
7888
return Unit.Task;
7989
}
8090

@@ -91,34 +101,13 @@ TextDocumentSaveRegistrationOptions IRegistration<TextDocumentSaveRegistrationOp
91101
IncludeText = true
92102
};
93103
}
104+
94105
public TextDocumentAttributes GetTextDocumentAttributes(Uri uri)
95106
{
96107
return new TextDocumentAttributes(uri, "csharp");
97108
}
98109
}
99110

100-
class FoldingRangeHandler : OmniSharp.Extensions.LanguageServer.Protocol.Server.FoldingRangeHandler
101-
{
102-
public FoldingRangeHandler(ProgressManager progressManager) : base(new FoldingRangeRegistrationOptions()
103-
{
104-
DocumentSelector = DocumentSelector.ForLanguage("csharp")
105-
}, progressManager)
106-
{
107-
}
108-
109-
public override Task<Container<FoldingRange>> Handle(FoldingRangeRequestParam request, CancellationToken cancellationToken)
110-
{
111-
return Task.FromResult(new Container<FoldingRange>(new FoldingRange()
112-
{
113-
StartLine = 10,
114-
EndLine = 20,
115-
Kind = FoldingRangeKind.Region,
116-
EndCharacter = 0,
117-
StartCharacter = 0
118-
}));
119-
}
120-
}
121-
122111
class MyDocumentSymbolHandler : OmniSharp.Extensions.LanguageServer.Protocol.Server.DocumentSymbolHandler
123112
{
124113
public MyDocumentSymbolHandler(ProgressManager progressManager) : base(new DocumentSymbolRegistrationOptions()
@@ -236,16 +225,4 @@ public async override Task<Container<SymbolInformation>> Handle(WorkspaceSymbolP
236225
}
237226
}
238227
}
239-
240-
class DidChangeWatchedFilesHandler : OmniSharp.Extensions.LanguageServer.Protocol.Server.DidChangeWatchedFilesHandler
241-
{
242-
public DidChangeWatchedFilesHandler() : base(new DidChangeWatchedFilesRegistrationOptions() { })
243-
{
244-
}
245-
246-
public override Task<Unit> Handle(DidChangeWatchedFilesParams request, CancellationToken cancellationToken)
247-
{
248-
return Unit.Task;
249-
}
250-
}
251228
}

sample/SampleServer/log20200130.txt

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2020-01-30 20:58:48.519 -05:00 [INF] This only goes file...
2+
2020-01-30 20:58:49.045 -05:00 [INF] Configuring
3+
2020-01-30 20:58:49.047 -05:00 [INF] inside ctor
4+
2020-01-30 20:58:49.055 -05:00 [INF] Fooooo!

src/JsonRpc/Connection.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class Connection : IDisposable
1212
public Connection(
1313
Stream input,
1414
IOutputHandler outputHandler,
15-
IReciever reciever,
15+
IReceiver receiver,
1616
IRequestProcessIdentifier requestProcessIdentifier,
1717
IRequestRouter<IHandlerDescriptor> requestRouter,
1818
IResponseRouter responseRouter,
@@ -24,7 +24,7 @@ public Connection(
2424
_inputHandler = new InputHandler(
2525
input,
2626
outputHandler,
27-
reciever,
27+
receiver,
2828
requestProcessIdentifier,
2929
requestRouter,
3030
responseRouter,

src/JsonRpc/DapReciever.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
namespace OmniSharp.Extensions.JsonRpc
88
{
9-
public class DapReciever : IReciever
9+
public class DapReceiver : IReceiver
1010
{
1111
public (IEnumerable<Renor> results, bool hasResponse) GetRequests(JToken container)
1212
{

src/JsonRpc/IReciever.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace OmniSharp.Extensions.JsonRpc
66
{
7-
public interface IReciever
7+
public interface IReceiver
88
{
99
(IEnumerable<Renor> results, bool hasResponse) GetRequests(JToken container);
1010
bool IsValid(JToken container);

0 commit comments

Comments
 (0)