Skip to content

Commit

Permalink
Support for 'xml/executeClientCommand` access to server from extension
Browse files Browse the repository at this point in the history
Signed-off-by: BoykoAlex <aboyko@pivotal.io>
  • Loading branch information
BoykoAlex committed Oct 9, 2020
1 parent 7fe05f1 commit 0d2042a
Show file tree
Hide file tree
Showing 9 changed files with 396 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@
import static org.eclipse.lsp4j.jsonrpc.CompletableFutures.computeAsync;

import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import org.eclipse.lemminx.client.ExtendedClientCapabilities;
import org.eclipse.lemminx.commons.ModelTextDocument;
Expand All @@ -36,10 +39,10 @@
import org.eclipse.lemminx.logs.LogHelper;
import org.eclipse.lemminx.services.IXMLDocumentProvider;
import org.eclipse.lemminx.services.IXMLNotificationService;
import org.eclipse.lemminx.services.IXMLValidationService;
import org.eclipse.lemminx.services.XMLLanguageService;
import org.eclipse.lemminx.settings.AllXMLSettings;
import org.eclipse.lemminx.settings.InitializationOptionsSettings;
import org.eclipse.lemminx.settings.LogsSettings;
import org.eclipse.lemminx.settings.ServerSettings;
import org.eclipse.lemminx.settings.SharedSettings;
import org.eclipse.lemminx.settings.XMLCodeLensSettings;
Expand Down Expand Up @@ -71,7 +74,8 @@
*
*/
public class XMLLanguageServer
implements ProcessLanguageServer, XMLLanguageServerAPI, IXMLDocumentProvider, IXMLNotificationService {
implements ProcessLanguageServer, XMLLanguageServerAPI, IXMLDocumentProvider,
IXMLNotificationService, IXMLValidationService {

private static final Logger LOGGER = Logger.getLogger(XMLLanguageServer.class.getName());

Expand All @@ -81,14 +85,18 @@ public class XMLLanguageServer
private XMLLanguageClientAPI languageClient;
private final ScheduledExecutorService delayer;
private Integer parentProcessId;
public XMLCapabilityManager capabilityManager;
private XMLCapabilityManager capabilityManager;

public XMLLanguageServer() {
xmlTextDocumentService = new XMLTextDocumentService(this);
xmlWorkspaceService = new XMLWorkspaceService(this);

xmlLanguageService = new XMLLanguageService();
xmlLanguageService.setDocumentProvider(this);
xmlLanguageService.setNotificationService(this);
xmlTextDocumentService = new XMLTextDocumentService(this);
xmlWorkspaceService = new XMLWorkspaceService(this);
xmlLanguageService.setCommandService(xmlWorkspaceService);
xmlLanguageService.setValidationService(this);

delayer = Executors.newScheduledThreadPool(1);
}

Expand Down Expand Up @@ -297,4 +305,22 @@ public void sendNotification(String message, MessageType messageType, Command...
public SharedSettings getSharedSettings() {
return xmlTextDocumentService.getSharedSettings();
}

@Override
public Collection<DOMDocument> getAllDocuments() {
return xmlTextDocumentService.allDocuments().stream()
.map(m -> m.getModel().getNow(null))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}

@Override
public void validate(DOMDocument document) {
xmlTextDocumentService.validate(document);
}

public XMLCapabilityManager getCapabilityManager() {
return capabilityManager;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ private void triggerValidationFor(TextDocument document) {
});
}

private void validate(DOMDocument xmlDocument) throws CancellationException {
void validate(DOMDocument xmlDocument) throws CancellationException {
CancelChecker cancelChecker = xmlDocument.getCancelChecker();
cancelChecker.checkCanceled();
getXMLLanguageService().publishDiagnostics(xmlDocument,
Expand Down Expand Up @@ -525,6 +525,10 @@ public SharedSettings getSharedSettings() {
public ModelTextDocument<DOMDocument> getDocument(String uri) {
return documents.get(uri);
}

public Collection<ModelTextDocument<DOMDocument>> allDocuments() {
return documents.all();
}

public boolean documentIsOpen(String uri) {
ModelTextDocument<DOMDocument> document = getDocument(uri);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,68 @@
*/
package org.eclipse.lemminx;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

import org.eclipse.lemminx.services.IXMLCommandService;
import org.eclipse.lsp4j.DidChangeConfigurationParams;
import org.eclipse.lsp4j.DidChangeWatchedFilesParams;
import org.eclipse.lsp4j.ExecuteCommandParams;
import org.eclipse.lsp4j.FileEvent;
import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.WorkspaceSymbolParams;
import org.eclipse.lsp4j.jsonrpc.ResponseErrorException;
import org.eclipse.lsp4j.jsonrpc.messages.ResponseError;
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 {
public class XMLWorkspaceService implements WorkspaceService, IXMLCommandService {

private static final String WORKSPACE_EXECUTE_COMMAND = "workspace/executeCommand";

private final XMLLanguageServer xmlLanguageServer;


private final Map<String, IDelegateCommandHandler> commands;

private final Map<String, String> commandRegistrations;

public XMLWorkspaceService(XMLLanguageServer xmlLanguageServer) {
this.xmlLanguageServer = xmlLanguageServer;
this.commands = new HashMap<>();
this.commandRegistrations = new HashMap<>();
}

@Override
public CompletableFuture<List<? extends SymbolInformation>> symbol(WorkspaceSymbolParams params) {
return null;
}



@Override
public CompletableFuture<Object> executeCommand(ExecuteCommandParams params) {
synchronized (commands) {
IDelegateCommandHandler handler = commands.get(params.getCommand());
try {
return handler.executeCommand(params);
} catch (Exception e) {
throw new ResponseErrorException(new ResponseError(ResponseErrorCode.InternalError, e.getMessage(), e));
}
}
}

@Override
public void didChangeConfiguration(DidChangeConfigurationParams params) {
xmlLanguageServer.updateSettings(params.getSettings());
xmlLanguageServer.capabilityManager.syncDynamicCapabilitiesWithPreferences();
xmlLanguageServer.getCapabilityManager().syncDynamicCapabilitiesWithPreferences();
}

@Override
Expand All @@ -54,4 +86,33 @@ public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) {
}
}
}

@Override
public void registerCommand(String commandId, IDelegateCommandHandler handler) {
synchronized (commands) {
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);
}
}

@Override
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);
}
}
}

@Override
public CompletableFuture<Object> executeClientCommand(ExecuteCommandParams command) {
return xmlLanguageServer.getLanguageClient().executeClientCommand(command);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
*/
package org.eclipse.lemminx.customservice;

import java.util.concurrent.CompletableFuture;

import org.eclipse.lsp4j.ExecuteCommandParams;
import org.eclipse.lsp4j.jsonrpc.services.JsonNotification;
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
import org.eclipse.lsp4j.jsonrpc.services.JsonSegment;
import org.eclipse.lsp4j.services.LanguageClient;

Expand All @@ -28,8 +32,21 @@ public interface XMLLanguageClientAPI extends LanguageClient {
*
* @param command
*/
@JsonNotification
@JsonNotification("actionableNotification")
default void actionableNotification(ActionableNotification command) {
throw new UnsupportedOperationException();
}

/**
* Executes the command on the client which gives an opportunity to execute a
* command registered by a different Language Server
*
* @param params command execution parameters
* @return the result of the command execution
*/
@JsonRequest("executeClientCommand")
default CompletableFuture<Object> executeClientCommand(ExecuteCommandParams params) {
throw new UnsupportedOperationException();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright (c) 2020 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.lemminx.services;

import java.util.concurrent.CompletableFuture;

import org.eclipse.lsp4j.ExecuteCommandParams;

/**
* 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 {

CompletableFuture<Object> executeCommand(ExecuteCommandParams params) throws Exception;

}

/**
* Registers a command with the language server
*
* @param commandId unique id of the command
* @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
*
* @param command the LSP command
* @return the result of the command
*/
CompletableFuture<Object> executeClientCommand(ExecuteCommandParams command);


}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
*/
package org.eclipse.lemminx.services;

import java.util.Collection;
import java.util.Collections;

import org.eclipse.lemminx.dom.DOMDocument;

/**
Expand All @@ -31,4 +34,12 @@ public interface IXMLDocumentProvider {
* null otherwise.
*/
DOMDocument getDocument(String uri);

/**
* All known documents XML server is working with at the moment
* @return XML documents
*/
default Collection<DOMDocument> getAllDocuments() {
return Collections.emptyList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright (c) 2020 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.lemminx.services;

import org.eclipse.lemminx.dom.DOMDocument;

/**
* XML Document validation service available to XML LS extensions
*
* @author Alex Boyko
*
*/
public interface IXMLValidationService {

/**
* Performs XML document validation
* @param document the XML document
*/
void validate(DOMDocument document);

}
Loading

0 comments on commit 0d2042a

Please sign in to comment.