-
Notifications
You must be signed in to change notification settings - Fork 75
Support for 'xml/executeClientCommand' protocol extension #543
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
Changes from 1 commit
2e71167
22f9c78
afdde86
6079efd
207c6b6
9df9edf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
package org.eclipse.wildwebdeveloper.xml.internal; | ||
|
||
import static org.eclipse.lsp4e.command.LSPCommandHandler.LSP_COMMAND_PARAMETER_ID; | ||
import static org.eclipse.lsp4e.command.LSPCommandHandler.LSP_PATH_PARAMETER_ID; | ||
|
||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.concurrent.CompletableFuture; | ||
import java.util.function.Predicate; | ||
|
||
import org.eclipse.core.commands.Category; | ||
import org.eclipse.core.commands.ExecutionException; | ||
import org.eclipse.core.commands.IParameter; | ||
import org.eclipse.core.commands.IParameterValues; | ||
import org.eclipse.core.commands.ITypedParameter; | ||
import org.eclipse.core.commands.NotEnabledException; | ||
import org.eclipse.core.commands.NotHandledException; | ||
import org.eclipse.core.commands.ParameterType; | ||
import org.eclipse.core.commands.ParameterValuesException; | ||
import org.eclipse.core.commands.ParameterizedCommand; | ||
import org.eclipse.core.commands.common.NotDefinedException; | ||
import org.eclipse.core.resources.ResourcesPlugin; | ||
import org.eclipse.core.runtime.IPath; | ||
import org.eclipse.core.runtime.IStatus; | ||
import org.eclipse.core.runtime.Status; | ||
import org.eclipse.lsp4e.LanguageServiceAccessor; | ||
import org.eclipse.lsp4j.Command; | ||
import org.eclipse.lsp4j.ExecuteCommandOptions; | ||
import org.eclipse.lsp4j.ExecuteCommandParams; | ||
import org.eclipse.lsp4j.ServerCapabilities; | ||
import org.eclipse.lsp4j.services.LanguageServer; | ||
import org.eclipse.ui.IWorkbench; | ||
import org.eclipse.ui.PlatformUI; | ||
import org.eclipse.ui.commands.ICommandService; | ||
import org.eclipse.ui.handlers.IHandlerService; | ||
|
||
@SuppressWarnings("restriction") | ||
class CommandExecutor { | ||
|
||
private static final String LSP_COMMAND_CATEGORY_ID = "org.eclipse.lsp4e.commandCategory"; //$NON-NLS-1$ | ||
private static final String LSP_COMMAND_PARAMETER_TYPE_ID = "org.eclipse.lsp4e.commandParameterType"; //$NON-NLS-1$ | ||
private static final String LSP_PATH_PARAMETER_TYPE_ID = "org.eclipse.lsp4e.pathParameterType"; //$NON-NLS-1$ | ||
|
||
public CompletableFuture<Object> executeClientCommand(String id, Object... params) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like all that should be placed in LSP4E, and even that LSP4E does already have such code available. We should instead investigate a better way to expose the LSP4E command handling as API and consume it here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alex is a bit busy so I will continue to push this forwards. I just created a gerrit patch merging in this code with the lsp4e 'CommandExecutor'. See: https://git.eclipse.org/r/c/lsp4e/lsp4e/+/170859 I suggest we start by discussion / resolving that patch via gerrit. And once that is done I can then amend this PR here to remove the obsolete wild web copy of CommandExecutor and use the one from lsp4e instead. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: you will see that my patch exports internal package. This is probably not what we want in the end. But I wanted to avoid moving the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. okay so I just updated the PR. The CommandExecutor has been removed and now using the lsp4e CommandExecutor instead. |
||
List<LanguageServer> commandHandlers = LanguageServiceAccessor.getActiveLanguageServers(handlesCommand(id)); | ||
if (commandHandlers != null && !commandHandlers.isEmpty()) { | ||
if (commandHandlers.size() == 1) { | ||
LanguageServer handler = commandHandlers.get(0); | ||
return handler | ||
.getWorkspaceService() | ||
.executeCommand(new ExecuteCommandParams(id, Arrays.asList(params))); | ||
} else if (commandHandlers.size() > 1) { | ||
throw new IllegalStateException("Multiple language servers have registered to handle command '"+id+"'"); | ||
} | ||
} else { | ||
return executeCommandClientSide(new Command("Java LS command", id, Arrays.asList(params))); | ||
} | ||
return null; | ||
} | ||
|
||
private Predicate<ServerCapabilities> handlesCommand(String id) { | ||
return (serverCaps) -> { | ||
ExecuteCommandOptions executeCommandProvider = serverCaps.getExecuteCommandProvider(); | ||
if (executeCommandProvider != null) { | ||
return executeCommandProvider.getCommands().contains(id); | ||
} | ||
return false; | ||
}; | ||
} | ||
|
||
@SuppressWarnings("unused") // ECJ compiler for some reason thinks handlerService == null is always false | ||
private static CompletableFuture<Object> executeCommandClientSide(Command command) { | ||
IWorkbench workbench = PlatformUI.getWorkbench(); | ||
if (workbench == null) { | ||
return null; | ||
} | ||
IPath context = ResourcesPlugin.getWorkspace().getRoot().getLocation(); | ||
ParameterizedCommand parameterizedCommand = createEclipseCoreCommand(command, context, workbench); | ||
if (parameterizedCommand == null) { | ||
return null; | ||
} | ||
IHandlerService handlerService = workbench.getService(IHandlerService.class); | ||
if (handlerService == null) { | ||
return null; | ||
} | ||
return CompletableFuture.supplyAsync(() -> { | ||
try { | ||
return handlerService.executeCommand(parameterizedCommand, null); | ||
} catch (ExecutionException | NotDefinedException e) { | ||
Activator.getDefault().getLog().log( | ||
new Status(IStatus.ERROR, Activator.getDefault().getBundle().getSymbolicName(), e.getMessage(), e)); | ||
return null; | ||
} catch (NotEnabledException | NotHandledException e2) { | ||
return null; | ||
} | ||
}); | ||
} | ||
|
||
private static ParameterizedCommand createEclipseCoreCommand(Command command, IPath context, | ||
IWorkbench workbench) { | ||
// Usually commands are defined via extension point, but we synthesize one on | ||
// the fly for the command ID, since we do not want downstream users | ||
// having to define them. | ||
String commandId = command.getCommand(); | ||
ICommandService commandService = workbench.getService(ICommandService.class); | ||
org.eclipse.core.commands.Command coreCommand = commandService.getCommand(commandId); | ||
if (!coreCommand.isDefined()) { | ||
ParameterType commandParamType = commandService.getParameterType(LSP_COMMAND_PARAMETER_TYPE_ID); | ||
ParameterType pathParamType = commandService.getParameterType(LSP_PATH_PARAMETER_TYPE_ID); | ||
Category category = commandService.getCategory(LSP_COMMAND_CATEGORY_ID); | ||
IParameter[] parameters = { | ||
new CommandEventParameter(commandParamType, command.getTitle(), LSP_COMMAND_PARAMETER_ID), | ||
new CommandEventParameter(pathParamType, command.getTitle(), LSP_PATH_PARAMETER_ID)}; | ||
coreCommand.define(commandId, null, category, parameters); | ||
} | ||
|
||
Map<Object, Object> parameters = new HashMap<>(); | ||
parameters.put(LSP_COMMAND_PARAMETER_ID, command); | ||
parameters.put(LSP_PATH_PARAMETER_ID, context); | ||
ParameterizedCommand parameterizedCommand = ParameterizedCommand.generateCommand(coreCommand, parameters); | ||
return parameterizedCommand; | ||
} | ||
|
||
|
||
static class CommandEventParameter implements IParameter, ITypedParameter { | ||
|
||
private final ParameterType paramType; | ||
private final String name; | ||
private String id; | ||
|
||
public CommandEventParameter(ParameterType paramType, String name, String id) { | ||
super(); | ||
this.paramType = paramType; | ||
this.name = name; | ||
this.id = id; | ||
} | ||
|
||
@Override | ||
public ParameterType getParameterType() { | ||
return paramType; | ||
} | ||
|
||
@Override | ||
public String getId() { | ||
return id; | ||
} | ||
|
||
@Override | ||
public String getName() { | ||
return name; | ||
} | ||
|
||
@Override | ||
public IParameterValues getValues() throws ParameterValuesException { | ||
return () -> Collections.emptyMap(); | ||
} | ||
|
||
@Override | ||
public boolean isOptional() { | ||
return false; | ||
} | ||
|
||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package org.eclipse.wildwebdeveloper.xml.internal; | ||
|
||
import java.util.concurrent.CompletableFuture; | ||
|
||
import org.eclipse.lsp4j.ExecuteCommandParams; | ||
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; | ||
|
||
public interface XMLLanguageClient { | ||
|
||
@JsonRequest("xml/executeClientCommand") | ||
CompletableFuture<Object> executeClientCommand(ExecuteCommandParams params); | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package org.eclipse.wildwebdeveloper.xml.internal; | ||
|
||
import java.util.List; | ||
import java.util.concurrent.CompletableFuture; | ||
|
||
import org.eclipse.lsp4e.LanguageClientImpl; | ||
import org.eclipse.lsp4j.ExecuteCommandParams; | ||
|
||
@SuppressWarnings("restriction") | ||
public class XmlLanguageClientImpl extends LanguageClientImpl implements XMLLanguageClient{ | ||
|
||
private CommandExecutor commandExecutor = new CommandExecutor(); | ||
|
||
public CompletableFuture<Object> executeClientCommand(ExecuteCommandParams params) { | ||
String id = params.getCommand(); | ||
List<Object> args = params.getArguments(); | ||
return commandExecutor.executeClientCommand(id, args.toArray()); | ||
} | ||
|
||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add EPLv2 copyright header.