Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d919617
Breaking: LSP ISerializer interface is now deprecated, and marked obs…
david-driscoll Nov 18, 2020
9dff9b7
Refactored source generation enabling support for strategies
david-driscoll Nov 19, 2020
9c9da3e
Added second wave generators that can generate handler interfaces and…
david-driscoll Nov 20, 2020
f779fc5
fixed capabilities not working correctly
david-driscoll Nov 20, 2020
941c29e
more registration options changes in generation
david-driscoll Nov 20, 2020
c17f972
so many more things... consolidated down to a good feature set most o…
david-driscoll Nov 23, 2020
f578209
it builds!
david-driscoll Nov 24, 2020
10d11ae
All tests are fixed and working again
david-driscoll Nov 25, 2020
d8fbc87
finsihed consolidation
david-driscoll Nov 25, 2020
3971072
Started on documentation, fixed ICanHaveData not generating
david-driscoll Nov 25, 2020
0cb088c
Updated DAP to use new source generation as well, next is to source g…
david-driscoll Nov 25, 2020
b620a97
Added support for generation with AllowDerivedRequests
david-driscoll Nov 25, 2020
ce3fb54
moved around some of the generation bits
david-driscoll Nov 25, 2020
3aacc1f
Updated source generation documentation
david-driscoll Nov 25, 2020
78a9c6b
some more lsp docs
david-driscoll Nov 25, 2020
157a006
removed some additional files that were left
david-driscoll Nov 25, 2020
ebc3c06
Added errors for when a given class or interface must be partial
david-driscoll Nov 25, 2020
1e94e7e
merged with master
david-driscoll Nov 25, 2020
fb276ed
fixed failing unit tests
david-driscoll Nov 25, 2020
5f62e5a
Update cancellation timeout
david-driscoll Nov 25, 2020
bfa097d
fixed some bugs and hopefully addressed performance
david-driscoll Nov 26, 2020
b9aad49
only generate to file when using rider
david-driscoll Nov 26, 2020
7d7d5ae
Remove extra normalize whitespace calls.
david-driscoll Nov 26, 2020
dacd031
Added source generator caching... yes I know we're not supposed to do…
david-driscoll Nov 26, 2020
49df78f
disable source generators to disk
david-driscoll Nov 27, 2020
4a13b86
Some caching changes and fixes for working in rider
david-driscoll Nov 28, 2020
17a685e
Updated emit config
david-driscoll Nov 28, 2020
dac7e66
build failures
david-driscoll Nov 28, 2020
05b5eb8
disable generate files
david-driscoll Nov 28, 2020
122b357
disable generate files
david-driscoll Nov 28, 2020
3289b70
Added full client capabilities to registration options delegate
david-driscoll Nov 28, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 5 additions & 3 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<AllowedReferenceRelatedFileExtensions>$(AllowedReferenceRelatedFileExtensions);.pdb</AllowedReferenceRelatedFileExtensions>
</PropertyGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)/.tmp/packageicon.png" Condition="Exists('$(MSBuildThisFileDirectory)/.tmp/packageicon.png')" Pack="true" PackagePath="/images/"
Visible="false"/>
<None Include="$(MSBuildThisFileDirectory)/LICENSE" Pack="true" PackagePath="/" Visible="false"/>
<None Include="$(MSBuildThisFileDirectory)/.tmp/packageicon.png" Condition="Exists('$(MSBuildThisFileDirectory)/.tmp/packageicon.png')" Pack="true" PackagePath="/images/" Visible="false" />
<None Include="$(MSBuildThisFileDirectory)/LICENSE" Pack="true" PackagePath="/" Visible="false" />
</ItemGroup>
</Project>
4 changes: 0 additions & 4 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GeneratedFiles</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Condition="'$(DesignTimeBuild)' == 'true' and '$(IDEA_INITIAL_DIRECTORY)' != ''" Include="$(CompilerGeneratedFilesOutputPath)/**/*.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="Rocket.Surgery.MSBuild.CI" Version="1.1.0" PrivateAssets="All" />
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/Pipeline/Pipeline.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net472;netcoreapp2.1;netcoreapp3.1</TargetFrameworks>
<TargetFrameworks>net472;netcoreapp3.1</TargetFrameworks>
<OutputType>Exe</OutputType>
<IsPackable>false</IsPackable>
</PropertyGroup>
Expand Down
14 changes: 13 additions & 1 deletion docs/lsp.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,19 @@ The goal of this library is to implement [Language Server Protocol](https://micr

Included in this library is a full-fidelity `LanguageServer` but also full `LanguageClient` implementation that could be implemented in an editor, but mainly it is used to help make Unit Testing easier, more consistent (and maybe even fun!).

# Creating a JsonRpcServer
# Concepts
The language server is built oin a few concepts. At it's core is the [MediatR](https://github.com/jbogard/MediatR) library that you will build language specific handlers around. Around that core is a bunch of knowledge of the LSP protocol
with the goal of making it more ".NET" like and less protocol centric.

LSP revolves around features ( eg Completion, Hover, etc ) that define the inputs (request object) the outputs (response object) as well as Client Capabilities and Server Registration Options.

## Client Capabilities
These determine what features are supported by the client, and each has a different set of capabilities. The [specification](https://microsoft.github.io/language-server-protocol/) explains each feature and the requirements of each.

## Server Registration Options
The protocol defines two kinds of registration, static and dynamic. Dynamic registration, when it's supported, allows you to register features with the client on demand. Static registration is returned to the client during the initialization phase that explains what features the server supports. Dynamic and Static registration cannot be mixed. If you register something statically, you cannot register the same thing dynamically, otherwise the client will register both features twice.

# Creating a Language Server or Client
`LanguageServer` or `LanguageClient` can be created through two methods.

## Standalone
Expand Down
824 changes: 824 additions & 0 deletions docs/source-generation.md

Large diffs are not rendered by default.

4 changes: 1 addition & 3 deletions sample/SampleServer/DidChangeWatchedFilesHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ internal class DidChangeWatchedFilesHandler : IDidChangeWatchedFilesHandler

public Task<Unit> Handle(DidChangeWatchedFilesParams request, CancellationToken cancellationToken) => Unit.Task;

public void SetCapability(DidChangeWatchedFilesCapability capability)
{
}
public DidChangeWatchedFilesRegistrationOptions GetRegistrationOptions(DidChangeWatchedFilesCapability capability, ClientCapabilities clientCapabilities) => new DidChangeWatchedFilesRegistrationOptions();
}
}
6 changes: 3 additions & 3 deletions sample/SampleServer/FoldingRangeHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ CancellationToken cancellationToken
)
);

public void SetCapability(FoldingRangeCapability capability)
{
}
public FoldingRangeRegistrationOptions GetRegistrationOptions(FoldingRangeCapability capability, ClientCapabilities clientCapabilities) => new FoldingRangeRegistrationOptions {
DocumentSelector = DocumentSelector.ForLanguage("csharp")
};
}
}
25 changes: 14 additions & 11 deletions sample/SampleServer/SemanticTokensHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,7 @@ public class SemanticTokensHandler : SemanticTokensHandlerBase
{
private readonly ILogger _logger;

public SemanticTokensHandler(ILogger<SemanticTokensHandler> logger) : base(
new SemanticTokensRegistrationOptions {
DocumentSelector = DocumentSelector.ForLanguage("csharp"),
Legend = new SemanticTokensLegend(),
Full = new SemanticTokensCapabilityRequestFull {
Delta = true
},
Range = true
}
) =>
public SemanticTokensHandler(ILogger<SemanticTokensHandler> logger) =>
_logger = logger;

public override async Task<SemanticTokens?> Handle(
Expand Down Expand Up @@ -83,7 +74,7 @@ CancellationToken cancellationToken

protected override Task<SemanticTokensDocument>
GetSemanticTokensDocument(ITextDocumentIdentifierParams @params, CancellationToken cancellationToken) =>
Task.FromResult(new SemanticTokensDocument(GetRegistrationOptions().Legend));
Task.FromResult(new SemanticTokensDocument(RegistrationOptions.Legend));


private IEnumerable<T> RotateEnum<T>(IEnumerable<T> values)
Expand All @@ -94,6 +85,18 @@ private IEnumerable<T> RotateEnum<T>(IEnumerable<T> values)
yield return item;
}
}

protected override SemanticTokensRegistrationOptions CreateRegistrationOptions(SemanticTokensCapability capability, ClientCapabilities clientCapabilities) => new SemanticTokensRegistrationOptions {
DocumentSelector = DocumentSelector.ForLanguage("csharp"),
Legend = new SemanticTokensLegend() {
TokenModifiers = capability.TokenModifiers,
TokenTypes = capability.TokenTypes
},
Full = new SemanticTokensCapabilityRequestFull {
Delta = true
},
Range = true
};
}
#pragma warning restore 618
}
69 changes: 24 additions & 45 deletions sample/SampleServer/TextDocumentHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@
using OmniSharp.Extensions.LanguageServer.Protocol.Server.Capabilities;
using OmniSharp.Extensions.LanguageServer.Protocol.Server.WorkDone;
using OmniSharp.Extensions.LanguageServer.Protocol.Workspace;

#pragma warning disable CS0618

namespace SampleServer
{
internal class TextDocumentHandler : ITextDocumentSyncHandler
internal class TextDocumentHandler : TextDocumentSyncHandlerBase
{
private readonly ILogger<TextDocumentHandler> _logger;
private readonly ILanguageServerConfiguration _configuration;
Expand All @@ -28,12 +29,7 @@ internal class TextDocumentHandler : ITextDocumentSyncHandler
}
);

private SynchronizationCapability _capability;

public TextDocumentHandler(
ILogger<TextDocumentHandler> logger, Foo foo,
ILanguageServerConfiguration configuration
)
public TextDocumentHandler(ILogger<TextDocumentHandler> logger, Foo foo, ILanguageServerConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
Expand All @@ -42,7 +38,7 @@ ILanguageServerConfiguration configuration

public TextDocumentSyncKind Change { get; } = TextDocumentSyncKind.Full;

public Task<Unit> Handle(DidChangeTextDocumentParams notification, CancellationToken token)
public override Task<Unit> Handle(DidChangeTextDocumentParams notification, CancellationToken token)
{
_logger.LogCritical("Critical");
_logger.LogDebug("Debug");
Expand All @@ -51,29 +47,15 @@ public Task<Unit> Handle(DidChangeTextDocumentParams notification, CancellationT
return Unit.Task;
}

TextDocumentChangeRegistrationOptions IRegistration<TextDocumentChangeRegistrationOptions>.
GetRegistrationOptions() =>
new TextDocumentChangeRegistrationOptions {
DocumentSelector = _documentSelector,
SyncKind = Change
};

public void SetCapability(SynchronizationCapability capability) => _capability = capability;

public async Task<Unit> Handle(DidOpenTextDocumentParams notification, CancellationToken token)
public override async Task<Unit> Handle(DidOpenTextDocumentParams notification, CancellationToken token)
{
await Task.Yield();
_logger.LogInformation("Hello world!");
await _configuration.GetScopedConfiguration(notification.TextDocument.Uri, token);
return Unit.Value;
}

TextDocumentRegistrationOptions IRegistration<TextDocumentRegistrationOptions>.GetRegistrationOptions() =>
new TextDocumentRegistrationOptions {
DocumentSelector = _documentSelector,
};

public Task<Unit> Handle(DidCloseTextDocumentParams notification, CancellationToken token)
public override Task<Unit> Handle(DidCloseTextDocumentParams notification, CancellationToken token)
{
if (_configuration.TryGetScopedConfiguration(notification.TextDocument.Uri, out var disposable))
{
Expand All @@ -83,28 +65,20 @@ public Task<Unit> Handle(DidCloseTextDocumentParams notification, CancellationTo
return Unit.Task;
}

public Task<Unit> Handle(DidSaveTextDocumentParams notification, CancellationToken token) => Unit.Task;
public override Task<Unit> Handle(DidSaveTextDocumentParams notification, CancellationToken token) => Unit.Task;

TextDocumentSaveRegistrationOptions IRegistration<TextDocumentSaveRegistrationOptions>.GetRegistrationOptions() =>
new TextDocumentSaveRegistrationOptions {
DocumentSelector = _documentSelector,
IncludeText = true
};
protected override TextDocumentSyncRegistrationOptions CreateRegistrationOptions(SynchronizationCapability capability, ClientCapabilities clientCapabilities) => new TextDocumentSyncRegistrationOptions() {
DocumentSelector = _documentSelector,
SyncKind = Change,
IncludeText = true
};

public TextDocumentAttributes GetTextDocumentAttributes(DocumentUri uri) => new TextDocumentAttributes(uri, "csharp");
public override TextDocumentAttributes GetTextDocumentAttributes(DocumentUri uri) => new TextDocumentAttributes(uri, "csharp");
}

internal class MyDocumentSymbolHandler : DocumentSymbolHandler
internal class MyDocumentSymbolHandler : IDocumentSymbolHandler
{
public MyDocumentSymbolHandler() : base(
new DocumentSymbolRegistrationOptions {
DocumentSelector = DocumentSelector.ForLanguage("csharp")
}
)
{
}

public override async Task<SymbolInformationOrDocumentSymbolContainer> Handle(
public async Task<SymbolInformationOrDocumentSymbolContainer> Handle(
DocumentSymbolParams request,
CancellationToken cancellationToken
)
Expand Down Expand Up @@ -151,23 +125,26 @@ CancellationToken cancellationToken
// await Task.Delay(2000, cancellationToken);
return symbols;
}

public DocumentSymbolRegistrationOptions GetRegistrationOptions(DocumentSymbolCapability capability, ClientCapabilities clientCapabilities) => new DocumentSymbolRegistrationOptions {
DocumentSelector = DocumentSelector.ForLanguage("csharp")
};
}

internal class MyWorkspaceSymbolsHandler : WorkspaceSymbolsHandler
internal class MyWorkspaceSymbolsHandler : IWorkspaceSymbolsHandler
{
private readonly IServerWorkDoneManager _serverWorkDoneManager;
private readonly IProgressManager _progressManager;
private readonly ILogger<MyWorkspaceSymbolsHandler> _logger;

public MyWorkspaceSymbolsHandler(IServerWorkDoneManager serverWorkDoneManager, IProgressManager progressManager, ILogger<MyWorkspaceSymbolsHandler> logger) :
base(new WorkspaceSymbolRegistrationOptions())
public MyWorkspaceSymbolsHandler(IServerWorkDoneManager serverWorkDoneManager, IProgressManager progressManager, ILogger<MyWorkspaceSymbolsHandler> logger)
{
_serverWorkDoneManager = serverWorkDoneManager;
_progressManager = progressManager;
_logger = logger;
}

public override async Task<Container<SymbolInformation>> Handle(
public async Task<Container<SymbolInformation>> Handle(
WorkspaceSymbolParams request,
CancellationToken cancellationToken
)
Expand Down Expand Up @@ -272,5 +249,7 @@ CancellationToken cancellationToken
);
}
}

public WorkspaceSymbolRegistrationOptions GetRegistrationOptions(WorkspaceSymbolCapability capability, ClientCapabilities clientCapabilities) => new WorkspaceSymbolRegistrationOptions();
}
}
18 changes: 11 additions & 7 deletions src/Client/LanguageClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@

namespace OmniSharp.Extensions.LanguageServer.Client
{
[BuiltIn]
public class LanguageClient : JsonRpcServerBase, ILanguageClient
{
private readonly Connection _connection;
private readonly ClientInfo _clientInfo;
private readonly ILspClientReceiver _receiver;
private readonly TextDocumentIdentifiers _textDocumentIdentifiers;

private readonly IHandlerCollection _collection;
private readonly SharedHandlerCollection _collection;

// private readonly IEnumerable<InitializeDelegate> _initializeDelegates;
// private readonly IEnumerable<InitializedDelegate> _initializedDelegates;
Expand Down Expand Up @@ -143,8 +144,10 @@ internal LanguageClient(
IProgressManager progressManager,
IClientWorkDoneManager clientWorkDoneManager,
IRegistrationManager registrationManager,
ILanguageClientWorkspaceFoldersManager languageClientWorkspaceFoldersManager, IEnumerable<OnLanguageClientInitializeDelegate> initializeDelegates,
IEnumerable<IOnLanguageClientInitialize> initializeHandlers, IEnumerable<OnLanguageClientInitializedDelegate> initializedDelegates,
ILanguageClientWorkspaceFoldersManager languageClientWorkspaceFoldersManager,
IEnumerable<OnLanguageClientInitializeDelegate> initializeDelegates,
IEnumerable<IOnLanguageClientInitialize> initializeHandlers,
IEnumerable<OnLanguageClientInitializedDelegate> initializedDelegates,
IEnumerable<IOnLanguageClientInitialized> initializedHandlers,
LspSerializer serializer,
InstanceHasStarted instanceHasStarted
Expand Down Expand Up @@ -250,12 +253,9 @@ public async Task Initialize(CancellationToken token)
_serializer.JsonSerializer.Populate(reader, _clientCapabilities);
}

_collection.Initialize();
RegisterCapabilities(_clientCapabilities);

WorkDoneManager.Initialize(@params.Capabilities.Window);

ClientSettings = @params;

await LanguageProtocolEventingHelper.Run(
_initializeDelegates,
(handler, ct) => handler(this, @params, ct),
Expand All @@ -265,6 +265,10 @@ await LanguageProtocolEventingHelper.Run(
token
).ConfigureAwait(false);

WorkDoneManager.Initialize(@params.Capabilities.Window);

ClientSettings = @params;

_connection.Open();
var serverParams = await SendRequest(ClientSettings, token).ConfigureAwait(false);

Expand Down
3 changes: 2 additions & 1 deletion src/Client/LanguageClientRegistrationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@

namespace OmniSharp.Extensions.LanguageServer.Client
{
[BuiltIn]
internal class LanguageClientRegistrationManager : IRegisterCapabilityHandler, IUnregisterCapabilityHandler, IRegistrationManager, IDisposable
{
private readonly ISerializer _serializer;
private readonly ILspHandlerTypeDescriptorProvider _handlerTypeDescriptorProvider;
private readonly ILogger<LanguageClientRegistrationManager> _logger;
private readonly ConcurrentDictionary<string, Registration> _registrations;
private ReplaySubject<IEnumerable<Registration>> _registrationSubject = new ReplaySubject<IEnumerable<Registration>>(1);
private readonly ReplaySubject<IEnumerable<Registration>> _registrationSubject = new ReplaySubject<IEnumerable<Registration>>(1);

public LanguageClientRegistrationManager(
ISerializer serializer,
Expand Down
1 change: 1 addition & 0 deletions src/Client/LanguageClientWorkspaceFoldersManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

namespace OmniSharp.Extensions.LanguageServer.Client
{
[BuiltIn]
internal class LanguageClientWorkspaceFoldersManager : ILanguageClientWorkspaceFoldersManager, IDisposable
{
private readonly IWorkspaceLanguageClient _client;
Expand Down
23 changes: 23 additions & 0 deletions src/Dap.Protocol/AbstractHandlers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Threading;
using System.Threading.Tasks;
using MediatR;
using OmniSharp.Extensions.JsonRpc;

namespace OmniSharp.Extensions.DebugAdapter.Protocol
{
public static class AbstractHandlers
{
public abstract class Request<TParams, TResult> :
IJsonRpcRequestHandler<TParams, TResult>
where TParams : IRequest<TResult>
{
public abstract Task<TResult> Handle(TParams request, CancellationToken cancellationToken);
}

public abstract class Notification<TParams> : IJsonRpcRequestHandler<TParams>
where TParams : IRequest
{
public abstract Task<Unit> Handle(TParams request, CancellationToken cancellationToken);
}
}
}
18 changes: 0 additions & 18 deletions src/Dap.Protocol/Dap.Protocol.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,6 @@
</ItemGroup>

<ItemGroup>
<Compile Update="Events\IProgressEndHandler.cs">
<Generator></Generator>
</Compile>
<Compile Update="Events\IProgressStartHandler.cs">
<Generator></Generator>
</Compile>
<Compile Update="Events\IProgressUpdateHandler.cs">
<Generator></Generator>
</Compile>
<Compile Update="Events\ProgressEndEvent.cs">
<Generator></Generator>
</Compile>
<Compile Update="Events\ProgressStartEvent.cs">
<Generator></Generator>
</Compile>
<Compile Update="Events\ProgressUpdateEvent.cs">
<Generator></Generator>
</Compile>
<Compile Update="Client\IDebugAdapterClient.cs">
<Generator>MSBuild:GenerateCodeFromAttributes</Generator>
</Compile>
Expand Down
Loading