Skip to content

Commit

Permalink
LSP: update OmniSharpCodeHandler so we support codeAction/resolve
Browse files Browse the repository at this point in the history
This feature was introduced in LSP
3.16.0 https://microsoft.github.io/language-server-protocol/specification#codeAction_resolve
and allows us to drop the hack where we were introducing a special
"omnisharp/executeCodeAction" command to delay the resolution of code
action changesets until user actually selets that code
action: see OmniSharp#1814.

Related to:
- OmniSharp#2068
- OmniSharp#1814
  • Loading branch information
Saulius Menkevicius authored and razzmatazz committed May 2, 2021
1 parent d52c76d commit e143515
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 56 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using OmniSharp.Extensions.LanguageServer.Protocol;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;

namespace OmniSharp.LanguageServerProtocol.Handlers
{
internal class CodeActionCommandData
{
public DocumentUri Uri { get; set; }
public string Identifier { get; set; }
public string Name { get; set; }
public Range Range { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -1,67 +1,51 @@
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.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 : CodeActionHandlerBase, IExecuteCommandHandler
internal sealed class OmniSharpCodeActionHandler : CodeActionHandlerBase
{
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, serializer, mediator, versions);
yield return new OmniSharpCodeActionHandler(getActionsHandler, runActionHandler, selector, mediator, versions);
}
}

private readonly Mef.IRequestHandler<GetCodeActionsRequest, GetCodeActionsResponse> _getActionsHandler;
private readonly ExecuteCommandRegistrationOptions _executeCommandRegistrationOptions;
private ExecuteCommandCapability _executeCommandCapability;
private Mef.IRequestHandler<RunCodeActionRequest, RunCodeActionResponse> _runActionHandler;
private readonly Mef.IRequestHandler<RunCodeActionRequest, RunCodeActionResponse> _runActionHandler;
private readonly DocumentSelector _documentSelector;
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,
ISerializer serializer,
ILanguageServer server,
DocumentVersions documentVersions)
{
_getActionsHandler = getActionsHandler;
_runActionHandler = runActionHandler;
_documentSelector = documentSelector;
_serializer = serializer;
_server = server;
_documentVersions = documentVersions;
_executeCommandRegistrationOptions = new ExecuteCommandRegistrationOptions()
{
Commands = new Container<string>("omnisharp/executeCodeAction"),
};
}

public override async Task<CommandOrCodeActionContainer> Handle(CodeActionParams request, CancellationToken cancellationToken)
Expand Down Expand Up @@ -93,34 +77,28 @@ public override async Task<CommandOrCodeActionContainer> Handle(CodeActionParams
Title = ca.Name,
Kind = kind,
Diagnostics = new Container<Diagnostic>(),
Edit = new WorkspaceEdit(),
Command = Command.Create("omnisharp/executeCodeAction")
.WithArguments(new CommandData()
Edit = null,
Data = JObject.FromObject(
new CodeActionCommandData()
{
Uri = request.TextDocument.Uri,
Identifier = ca.Identifier,
Name = ca.Name,
Range = request.Range,
})
with { Title = ca.Name }
});
}

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

public override Task<CodeAction> Handle(CodeAction request, CancellationToken cancellationToken)
{
return Task.FromResult(request);
}

public async Task<Unit> Handle(ExecuteCommandParams request, CancellationToken cancellationToken)
public override async Task<CodeAction> Handle(CodeAction request, CancellationToken cancellationToken)
{
Debug.Assert(request.Command == "omnisharp/executeCodeAction");
var data = request.ExtractArguments<CommandData>(_serializer);
var data = request.Data.ToObject<CodeActionCommandData>();

var omnisharpCaRequest = new RunCodeActionRequest {
var omnisharpCaRequest = new RunCodeActionRequest
{
Identifier = data.Identifier,
FileName = data.Uri.GetFileSystemPath(),
Column = data.Range.Start.Character,
Expand All @@ -139,33 +117,16 @@ public async Task<Unit> Handle(ExecuteCommandParams request, CancellationToken c
_server.ClientSettings.Capabilities.Workspace!.WorkspaceEdit.Value,
_documentVersions
);
;

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

// Do something with response?
//if (response.Applied)
Edit = edit,
};
}
else
{
return new CodeAction();
}

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, ExecuteCommandCapability>.GetRegistrationOptions(ExecuteCommandCapability capability, ClientCapabilities clientCapabilities)
{
_executeCommandCapability = capability;
return _executeCommandRegistrationOptions;
}

protected override CodeActionRegistrationOptions CreateRegistrationOptions(CodeActionCapability capability, ClientCapabilities clientCapabilities)
Expand All @@ -177,6 +138,7 @@ protected override CodeActionRegistrationOptions CreateRegistrationOptions(CodeA
CodeActionKind.SourceOrganizeImports,
CodeActionKind.Refactor,
CodeActionKind.RefactorExtract),
ResolveProvider = true,
};
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using OmniSharp.Extensions.JsonRpc;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
using OmniSharp.Models.V2.CodeActions;
using OmniSharp.Extensions.LanguageServer.Protocol.Document;

namespace OmniSharp.LanguageServerProtocol.Handlers
{
internal sealed class OmniSharpCodeActionResolveHandler : CodeActionResolveHandlerBase
{
public static IEnumerable<IJsonRpcHandler> Enumerate(
RequestHandlers handlers,
ILanguageServer mediator,
DocumentVersions versions)
{
foreach (var (_, runActionHandler) in handlers
.OfType<Mef.IRequestHandler<RunCodeActionRequest, RunCodeActionResponse>>())
{
yield return new OmniSharpCodeActionResolveHandler(runActionHandler, mediator, versions);
}
}

private readonly Mef.IRequestHandler<RunCodeActionRequest, RunCodeActionResponse> _runActionHandler;
private readonly ILanguageServer _server;
private readonly DocumentVersions _documentVersions;

public OmniSharpCodeActionResolveHandler(
Mef.IRequestHandler<RunCodeActionRequest, RunCodeActionResponse> runActionHandler,
ILanguageServer server,
DocumentVersions documentVersions)
{
_runActionHandler = runActionHandler;
_server = server;
_documentVersions = documentVersions;
}

public override async Task<CodeAction> Handle(CodeAction request, CancellationToken cancellationToken)
{
var data = request.Data.ToObject<CodeActionCommandData>();

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
);

return new CodeAction
{
Edit = edit,
};
}
else
{
return new CodeAction();
}
}
}
}
1 change: 1 addition & 0 deletions src/OmniSharp.LanguageServerProtocol/LanguageServerHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ internal static void RegisterHandlers(ILanguageServer server, CompositionHost co
.Concat(OmniSharpImplementationHandler.Enumerate(handlers))
.Concat(OmniSharpCodeLensHandler.Enumerate(handlers))
.Concat(OmniSharpCodeActionHandler.Enumerate(handlers, serializer, server, documentVersions))
.Concat(OmniSharpCodeActionResolveHandler.Enumerate(handlers, serializer, server, documentVersions))
.Concat(OmniSharpDocumentFormattingHandler.Enumerate(handlers))
.Concat(OmniSharpDocumentFormatRangeHandler.Enumerate(handlers))
.Concat(OmniSharpDocumentOnTypeFormattingHandler.Enumerate(handlers)))
Expand Down

0 comments on commit e143515

Please sign in to comment.