Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LSP: fix performance of the textDocument/codeAction request #1814

Merged
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ All changes to the project will be documented in this file.
* Fixed return type in LSP completion handler ([#1864](https://github.com/OmniSharp/omnisharp-roslyn/issues/1864), PR: [#1869](https://github.com/OmniSharp/omnisharp-roslyn/pull/1869))
* Upgraded to the latest version of the csharp-language-server-protocol [#1815](https://github.com/OmniSharp/omnisharp-roslyn/pull/1815)
* Added support for Roslyn `EmbeddedLanguageCompletionProvider` which enables completions for string literals for `DateTime` and `Regex` ([#1871](https://github.com/OmniSharp/omnisharp-roslyn/pull/1871))
* Improve performance of the `textDocument/codeAction` request. (PR: [#1814](https://github.com/OmniSharp/omnisharp-roslyn/pull/1814))

## [1.35.4] - 2020-07-22
* Update to Roslyn `3.8.0-1.20357.3` (PR: [#1849](https://github.com/OmniSharp/omnisharp-roslyn/pull/1849))
Expand All @@ -23,7 +24,6 @@ All changes to the project will be documented in this file.
* Expose a custom LSP `omnisharp/client/findReferences` command via code lens (meant to be handled by LSP client). (PR: [#1807](https://github.com/OmniSharp/omnisharp-roslyn/pull/1807))
* Added `DirectoryDelete` option to `FileChangeType` allowing clients to report deleted directories that need to be removed (along all the files) from the workspace (PR: [#1821](https://github.com/OmniSharp/omnisharp-roslyn/pull/1821))
* Do not crash when plugin assembly cannot be loaded ([#1307](https://github.com/OmniSharp/omnisharp-roslyn/issues/1307), PR: [#1827](https://github.com/OmniSharp/omnisharp-roslyn/pull/1827))
* Update to Roslyn `3.7.0-4.20311.4` (PR: [#1832](https://github.com/OmniSharp/omnisharp-roslyn/pull/1832))

## [1.35.2] - 2020-05-20
* Added support for `WarningsAsErrors` in csproj files (PR: [#1779](https://github.com/OmniSharp/omnisharp-roslyn/pull/1779))
Expand Down
4 changes: 2 additions & 2 deletions build/Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@
<PackageReference Update="Nuget.ProjectModel" Version="$(NuGetPackageVersion)" />
<PackageReference Update="Nuget.Versioning" Version="$(NuGetPackageVersion)" />

<PackageReference Update="OmniSharp.Extensions.LanguageServer" Version="0.17.3" />
<PackageReference Update="OmniSharp.Extensions.LanguageProtocol.Testing" Version="0.17.3" />
<PackageReference Update="OmniSharp.Extensions.LanguageServer" Version="0.18.0-beta0003" />
<PackageReference Update="OmniSharp.Extensions.LanguageProtocol.Testing" Version="0.18.0-beta0003" />

<PackageReference Update="SQLitePCLRaw.bundle_green" Version="1.1.2" />
<PackageReference Update="System.Collections.Immutable" Version="1.4.0" />
Expand Down
1 change: 1 addition & 0 deletions src/OmniSharp.Host/CompositionHostBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public CompositionHost Build()
.WithProvider(MefValueProvider.From(loggerFactory))
.WithProvider(MefValueProvider.From(environment))
.WithProvider(MefValueProvider.From(options.CurrentValue))
.WithProvider(MefValueProvider.From(options))
.WithProvider(MefValueProvider.From(options.CurrentValue.FormattingOptions))
.WithProvider(MefValueProvider.From(assemblyLoader))
.WithProvider(MefValueProvider.From(analyzerAssemblyLoader))
Expand Down
1 change: 1 addition & 0 deletions src/OmniSharp.Host/WorkspaceInitializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public static void Initialize(IServiceProvider serviceProvider, CompositionHost
var projectSystems = compositionHost.GetExports<IProjectSystem>();

workspace.EditorConfigEnabled = options.CurrentValue.FormattingOptions.EnableEditorConfigSupport;
options.OnChange(x => workspace.EditorConfigEnabled = x.FormattingOptions.EnableEditorConfigSupport);

foreach (var projectSystem in projectSystems)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OmniSharp.Eventing;
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
using OmniSharp.LanguageServerProtocol.Handlers;
using OmniSharp.Models.Diagnostics;
using OmniSharp.Models.Events;

Expand All @@ -13,10 +15,12 @@ namespace OmniSharp.LanguageServerProtocol.Eventing
public class LanguageServerEventEmitter : IEventEmitter
{
private readonly ILanguageServer _server;
private readonly DocumentVersions _documentVersions;

public LanguageServerEventEmitter(ILanguageServer server)
{
_server = server;
_documentVersions = server.Services.GetRequiredService<DocumentVersions>();
}

public void Emit(string kind, object args)
Expand All @@ -34,6 +38,7 @@ public void Emit(string kind, object args)
_server.TextDocument.PublishDiagnostics(new PublishDiagnosticsParams()
{
Uri = group.Key,
Version = _documentVersions.GetVersion(group.Key),
Diagnostics = group
.SelectMany(z => z.Select(v => v.ToDiagnostic()))
.ToArray()
Expand Down
36 changes: 36 additions & 0 deletions src/OmniSharp.LanguageServerProtocol/Handlers/DocumentVersions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Collections.Concurrent;
using OmniSharp.Extensions.LanguageServer.Protocol;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;

namespace OmniSharp.LanguageServerProtocol.Handlers
{
public class DocumentVersions
{
private readonly ConcurrentDictionary<DocumentUri, int> _documentVersions = new ConcurrentDictionary<DocumentUri, int>();

public int? GetVersion(DocumentUri documentUri)
{
if (_documentVersions.TryGetValue(documentUri, out var version))
{
return version;
}

return null;
}

public void Update(VersionedTextDocumentIdentifier identifier)
{
_documentVersions.AddOrUpdate(identifier.Uri, identifier.Version ?? 0, (uri, i) => identifier.Version ?? 0);
}

public void Reset(TextDocumentIdentifier identifier)
{
_documentVersions.AddOrUpdate(identifier.Uri, 0, (uri, i) => 0);
}

public void Remove(TextDocumentIdentifier identifier)
{
_documentVersions.TryRemove(identifier.Uri, out _);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,36 +1,55 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using MediatR;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json.Linq;
using OmniSharp.Extensions.JsonRpc;
using OmniSharp.Extensions.LanguageServer.Protocol;
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
using OmniSharp.Models;
using OmniSharp.Models.V2.CodeActions;
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
using OmniSharp.Extensions.LanguageServer.Protocol.Workspace;
using OmniSharp.Models;
using Diagnostic = OmniSharp.Extensions.LanguageServer.Protocol.Models.Diagnostic;

namespace OmniSharp.LanguageServerProtocol.Handlers
{
internal sealed class OmniSharpCodeActionHandler : CodeActionHandler
internal sealed class OmniSharpCodeActionHandler : CodeActionHandler, IExecuteCommandHandler
{
public static IEnumerable<IJsonRpcHandler> Enumerate(RequestHandlers handlers)
public static IEnumerable<IJsonRpcHandler> Enumerate(
RequestHandlers handlers,
ISerializer serializer,
ILanguageServer mediator,
DocumentVersions versions)
{
foreach (var (selector, getActionsHandler, runActionHandler) in handlers
.OfType<Mef.IRequestHandler<GetCodeActionsRequest, GetCodeActionsResponse>,
Mef.IRequestHandler<RunCodeActionRequest, RunCodeActionResponse>>())
{
yield return new OmniSharpCodeActionHandler(getActionsHandler, runActionHandler, selector);
yield return new OmniSharpCodeActionHandler(getActionsHandler, runActionHandler, selector, serializer, mediator, versions);
}
}

private readonly Mef.IRequestHandler<GetCodeActionsRequest, GetCodeActionsResponse> _getActionsHandler;
private readonly Mef.IRequestHandler<RunCodeActionRequest, RunCodeActionResponse> _runActionHandler;
private readonly ExecuteCommandRegistrationOptions _executeCommandRegistrationOptions;
private ExecuteCommandCapability _executeCommandCapability;
private Mef.IRequestHandler<RunCodeActionRequest, RunCodeActionResponse> _runActionHandler;
private readonly ISerializer _serializer;
private readonly ILanguageServer _server;
private readonly DocumentVersions _documentVersions;

public OmniSharpCodeActionHandler(
Mef.IRequestHandler<GetCodeActionsRequest, GetCodeActionsResponse> getActionsHandler,
Mef.IRequestHandler<RunCodeActionRequest, RunCodeActionResponse> runActionHandler,
DocumentSelector documentSelector)
DocumentSelector documentSelector,
ISerializer serializer,
ILanguageServer server,
DocumentVersions documentVersions)
: base(new CodeActionRegistrationOptions()
{
DocumentSelector = documentSelector,
Expand All @@ -42,15 +61,22 @@ public OmniSharpCodeActionHandler(
{
_getActionsHandler = getActionsHandler;
_runActionHandler = runActionHandler;
_serializer = serializer;
_server = server;
_documentVersions = documentVersions;
_executeCommandRegistrationOptions = new ExecuteCommandRegistrationOptions()
{
Commands = new Container<string>("omnisharp/executeCodeAction"),
};
}

public async override Task<CommandOrCodeActionContainer> Handle(CodeActionParams request, CancellationToken cancellationToken)
public override async Task<CommandOrCodeActionContainer> Handle(CodeActionParams request, CancellationToken cancellationToken)
{
var omnisharpRequest = new GetCodeActionsRequest
{
FileName = Helpers.FromUri(request.TextDocument.Uri),
Column = (int)request.Range.Start.Character,
Line = (int)request.Range.Start.Line,
Column = request.Range.Start.Character,
Line = request.Range.Start.Line,
Selection = Helpers.FromRange(request.Range),
};

Expand All @@ -60,27 +86,6 @@ public async override Task<CommandOrCodeActionContainer> Handle(CodeActionParams

foreach (var ca in omnisharpResponse.CodeActions)
{
var omnisharpCaRequest = new RunCodeActionRequest
{
Identifier = ca.Identifier,
FileName = Helpers.FromUri(request.TextDocument.Uri),
Column = Convert.ToInt32(request.Range.Start.Character),
Line = Convert.ToInt32(request.Range.Start.Line),
Selection = Helpers.FromRange(request.Range),
ApplyTextChanges = false,
WantsTextChanges = true,
};

var omnisharpCaResponse = await _runActionHandler.Handle(omnisharpCaRequest);

var changes = omnisharpCaResponse.Changes.ToDictionary(
x => Helpers.ToUri(x.FileName),
x => ((ModifiedFileResponse)x).Changes.Select(edit => new TextEdit
{
NewText = edit.NewText,
Range = Helpers.ToRange((edit.StartColumn, edit.StartLine), (edit.EndColumn, edit.EndLine))
}));

CodeActionKind kind;
if (ca.Identifier.StartsWith("using ")) { kind = CodeActionKind.SourceOrganizeImports; }
else if (ca.Identifier.StartsWith("Inline ")) { kind = CodeActionKind.RefactorInline; }
Expand All @@ -94,12 +99,71 @@ public async override Task<CommandOrCodeActionContainer> Handle(CodeActionParams
Title = ca.Name,
Kind = kind,
Diagnostics = new Container<Diagnostic>(),
Edit = new WorkspaceEdit { Changes = changes, }
Edit = new WorkspaceEdit(),
Command = Command.Create("omnisharp/executeCodeAction")
.WithTitle(ca.Name)
.WithArguments(new CommandData()
{
Uri = request.TextDocument.Uri,
Identifier = ca.Identifier,
Name = ca.Name,
Range = request.Range,
})
});
}

return new CommandOrCodeActionContainer(
codeActions.Select(ca => new CommandOrCodeAction(ca)));
}

public async Task<Unit> Handle(ExecuteCommandParams request, CancellationToken cancellationToken)
{
Debug.Assert(request.Command == "omnisharp/executeCodeAction");
var data = request.Arguments[0].ToObject<CommandData>(_serializer.JsonSerializer);

var omnisharpCaRequest = new RunCodeActionRequest {
Identifier = data.Identifier,
FileName = data.Uri.GetFileSystemPath(),
Column = data.Range.Start.Character,
Line = data.Range.Start.Line,
Selection = Helpers.FromRange(data.Range),
ApplyTextChanges = false,
WantsTextChanges = true,
WantsAllCodeActionOperations = true
};

var omnisharpCaResponse = await _runActionHandler.Handle(omnisharpCaRequest);
if (omnisharpCaResponse.Changes != null)
{
var edit = Helpers.ToWorkspaceEdit(
omnisharpCaResponse.Changes,
_server.ClientSettings.Capabilities.Workspace.WorkspaceEdit.Value,
_documentVersions
);
;

await _server.Workspace.ApplyWorkspaceEdit(new ApplyWorkspaceEditParams()
{
Label = data.Name,
Edit = edit
}, cancellationToken);

// Do something with response?
//if (response.Applied)
}

return Unit.Value;
}

class CommandData
{
public DocumentUri Uri { get; set;}
public string Identifier { get; set;}
public string Name { get; set;}
public Range Range { get; set;}
}

ExecuteCommandRegistrationOptions IRegistration<ExecuteCommandRegistrationOptions> .GetRegistrationOptions() => _executeCommandRegistrationOptions;
void ICapability<ExecuteCommandCapability>.SetCapability(ExecuteCommandCapability capability) => _executeCommandCapability = capability;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,5 @@ private static void ToCodeLens(TextDocumentIdentifier textDocument, FileMemberEl
}
}
}

public override bool CanResolve(CodeLens value)
{
var textDocumentUri = value.Data.ToObject<Uri>();

return textDocumentUri != null &&
GetRegistrationOptions().DocumentSelector
.IsMatch(new TextDocumentAttributes(textDocumentUri, string.Empty));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,7 @@ public async override Task<CompletionList> Handle(CompletionParams request, Canc

public override Task<CompletionItem> Handle(CompletionItem request, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}

public override bool CanResolve(CompletionItem value)
{
return false;
return Task.FromResult(request);
}
}
}

This file was deleted.

Loading