diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/XMLWorkspaceService.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/XMLWorkspaceService.java index 007a802b8..ca7c0dd4f 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/XMLWorkspaceService.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/XMLWorkspaceService.java @@ -12,10 +12,10 @@ */ package org.eclipse.lemminx; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.UUID; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; @@ -32,40 +32,33 @@ import org.eclipse.lsp4j.jsonrpc.messages.ResponseErrorCode; import org.eclipse.lsp4j.services.WorkspaceService; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; /** * XML workspace service. * */ public class XMLWorkspaceService implements WorkspaceService, IXMLCommandService { - - private static final String WORKSPACE_EXECUTE_COMMAND = "workspace/executeCommand"; private final XMLLanguageServer xmlLanguageServer; - + private final Map commands; - - private final Map commandRegistrations; - + public XMLWorkspaceService(XMLLanguageServer xmlLanguageServer) { this.xmlLanguageServer = xmlLanguageServer; this.commands = new HashMap<>(); - this.commandRegistrations = new HashMap<>(); } @Override public CompletableFuture> symbol(WorkspaceSymbolParams params) { return null; } - - + @Override public CompletableFuture executeCommand(ExecuteCommandParams params) { synchronized (commands) { IDelegateCommandHandler handler = commands.get(params.getCommand()); if (handler == null) { - throw new ResponseErrorException(new ResponseError(ResponseErrorCode.InternalError, "No command handler for the command: " + params.getCommand(), null)); + throw new ResponseErrorException(new ResponseError(ResponseErrorCode.InternalError, + "No command handler for the command: " + params.getCommand(), null)); } return CompletableFutures.computeAsync(cancelChecker -> { try { @@ -82,7 +75,7 @@ public CompletableFuture executeCommand(ExecuteCommandParams params) { }); } } - + @Override public void didChangeConfiguration(DidChangeConfigurationParams params) { xmlLanguageServer.updateSettings(params.getSettings()); @@ -91,9 +84,10 @@ public void didChangeConfiguration(DidChangeConfigurationParams params) { @Override public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) { - XMLTextDocumentService xmlTextDocumentService = (XMLTextDocumentService) xmlLanguageServer.getTextDocumentService(); + XMLTextDocumentService xmlTextDocumentService = (XMLTextDocumentService) xmlLanguageServer + .getTextDocumentService(); List changes = params.getChanges(); - for (FileEvent change: changes) { + for (FileEvent change : changes) { if (!xmlTextDocumentService.documentIsOpen(change.getUri())) { xmlTextDocumentService.doSave(change.getUri()); } @@ -106,9 +100,6 @@ public void registerCommand(String commandId, IDelegateCommandHandler handler) { if (commands.containsKey(commandId)) { throw new IllegalArgumentException("Command with id '" + commandId + "' is already registered"); } - String registrationId = UUID.randomUUID().toString(); - xmlLanguageServer.getCapabilityManager().registerCapability(registrationId, WORKSPACE_EXECUTE_COMMAND, ImmutableMap.of("commands", ImmutableList.of(commandId))); - commandRegistrations.put(commandId, registrationId); commands.put(commandId, handler); } } @@ -117,10 +108,6 @@ public void registerCommand(String commandId, IDelegateCommandHandler handler) { public void unregisterCommand(String commandId) { synchronized (commands) { commands.remove(commandId); - String registrationId = commandRegistrations.remove(commandId); - if (registrationId != null) { - xmlLanguageServer.getCapabilityManager().unregisterCapability(registrationId, WORKSPACE_EXECUTE_COMMAND); - } } } @@ -128,4 +115,11 @@ public void unregisterCommand(String commandId) { public CompletableFuture executeClientCommand(ExecuteCommandParams command) { return xmlLanguageServer.getLanguageClient().executeClientCommand(command); } + + @Override + public void endCommandsRegistration() { + if (!commands.isEmpty()) { + xmlLanguageServer.getCapabilityManager().registerExecuteCommand(new ArrayList<>(commands.keySet())); + } + } } diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/XMLExtensionsRegistry.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/XMLExtensionsRegistry.java index a48bd6077..ae77e8072 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/XMLExtensionsRegistry.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/XMLExtensionsRegistry.java @@ -117,7 +117,8 @@ public void initializeParams(InitializeParams params) { public void doSave(ISaveContext saveContext) { if (initialized) { extensions.stream().forEach(extension -> extension.doSave(saveContext)); - } else if (this.initialSaveContext == null || (saveContext != null && saveContext.getType() == SaveContextType.SETTINGS)) { + } else if (this.initialSaveContext == null + || (saveContext != null && saveContext.getType() == SaveContextType.SETTINGS)) { // capture initial configuration iff: // 1. the saveContext is for configuration, not document save // 2. we haven't captured settings before @@ -208,11 +209,17 @@ private synchronized void initialize() { return; } + if (commandService != null) { + commandService.beginCommandsRegistration(); + } ServiceLoader extensions = ServiceLoader.load(IXMLExtension.class); extensions.forEach(extension -> { registerExtension(extension); }); initialized = true; + if (commandService != null) { + commandService.endCommandsRegistration(); + } } void registerExtension(IXMLExtension extension) { @@ -389,10 +396,10 @@ public IXMLNotificationService getNotificationService() { public void setNotificationService(IXMLNotificationService notificationService) { this.notificationService = notificationService; } - + /** * Returns the XML document validation service - * + * * @return the validation service */ public IXMLValidationService getValidationService() { diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/commands/IXMLCommandService.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/commands/IXMLCommandService.java index 4f7fb9add..3940660de 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/commands/IXMLCommandService.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/commands/IXMLCommandService.java @@ -18,54 +18,64 @@ import org.eclipse.lsp4j.jsonrpc.CancelChecker; /** - * Service available to XML LS extensions to add/remove/execute commands via the XML LS + * Service available to XML LS extensions to add/remove/execute commands via the + * XML LS * * * @author Alex Boyko * */ public interface IXMLCommandService { - + /** * Command handler to register with the workspace service */ @FunctionalInterface public interface IDelegateCommandHandler { - + /** * Executes a command - * @param params command execution parameters + * + * @param params command execution parameters * @param cancelChecker check if cancel has been requested * @return the result of the command * @throws Exception the unhandled exception will be wrapped in * org.eclipse.lsp4j.jsonrpc.ResponseErrorException * and be wired back to the JSON-RPC protocol caller */ - Object executeCommand(ExecuteCommandParams params, CancelChecker cancelChecker) throws Exception; + Object executeCommand(ExecuteCommandParams params, CancelChecker cancelChecker) throws Exception; } - + /** * Registers a command with the language server * * @param commandId unique id of the command - * @param handler command handler function + * @param handler command handler function */ void registerCommand(String commandId, IDelegateCommandHandler handler); - + /** * Unregisters the command from the language server * * @param commandId unique id of the command to unregister */ void unregisterCommand(String commandId); - + /** - * Executes a command via the client. The command can be registered by any language server + * Executes a command via the client. The command can be registered by any + * language server * - * @param command the LSP command + * @param command the LSP command * @return the result of the command */ CompletableFuture executeClientCommand(ExecuteCommandParams command); - + + default void beginCommandsRegistration() { + + } + + default void endCommandsRegistration() { + + } } diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/ServerCapabilitiesConstants.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/ServerCapabilitiesConstants.java index 2f19e1a74..b42ae1af7 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/ServerCapabilitiesConstants.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/ServerCapabilitiesConstants.java @@ -61,7 +61,7 @@ private ServerCapabilitiesConstants() { public static final String CODE_LENS_ID = UUID.randomUUID().toString(); public static final String SIGNATURE_HELP_ID = UUID.randomUUID().toString(); public static final String RENAME_ID = UUID.randomUUID().toString(); - public static final String EXECUTE_COMMAND_ID = UUID.randomUUID().toString(); + public static final String WORKSPACE_EXECUTE_COMMAND_ID = UUID.randomUUID().toString(); public static final String WORKSPACE_SYMBOL_ID = UUID.randomUUID().toString(); public static final String DOCUMENT_SYMBOL_ID = UUID.randomUUID().toString(); public static final String CODE_ACTION_ID = UUID.randomUUID().toString(); diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/XMLCapabilityManager.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/XMLCapabilityManager.java index b9e4398f1..a9db905d6 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/XMLCapabilityManager.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/XMLCapabilityManager.java @@ -40,6 +40,8 @@ import static org.eclipse.lemminx.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_RENAME; import static org.eclipse.lemminx.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_TYPEDEFINITION; import static org.eclipse.lemminx.settings.capabilities.ServerCapabilitiesConstants.TYPEDEFINITION_ID; +import static org.eclipse.lemminx.settings.capabilities.ServerCapabilitiesConstants.WORKSPACE_EXECUTE_COMMAND; +import static org.eclipse.lemminx.settings.capabilities.ServerCapabilitiesConstants.WORKSPACE_EXECUTE_COMMAND_ID; import static org.eclipse.lemminx.settings.capabilities.ServerCapabilitiesConstants.WORKSPACE_WATCHED_FILES; import static org.eclipse.lemminx.settings.capabilities.ServerCapabilitiesConstants.WORKSPACE_WATCHED_FILES_ID; @@ -56,6 +58,7 @@ import org.eclipse.lemminx.settings.XMLSymbolSettings; import org.eclipse.lsp4j.ClientCapabilities; import org.eclipse.lsp4j.DidChangeWatchedFilesRegistrationOptions; +import org.eclipse.lsp4j.ExecuteCommandOptions; import org.eclipse.lsp4j.FileSystemWatcher; import org.eclipse.lsp4j.Registration; import org.eclipse.lsp4j.RegistrationParams; @@ -181,6 +184,11 @@ private void registerWatchedFiles() { registerCapability(WORKSPACE_WATCHED_FILES_ID, WORKSPACE_WATCHED_FILES, options); } + public void registerExecuteCommand(List commands) { + registerCapability(WORKSPACE_EXECUTE_COMMAND_ID, WORKSPACE_EXECUTE_COMMAND, + new ExecuteCommandOptions(commands)); + } + /** * Registers(indicates the servers ability to support the service) all * capabilities that have the ability to be turned on/off on the client side