diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/project/node/ProjectClasspathChangedEvent.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/project/node/ProjectClasspathChangedEvent.java new file mode 100644 index 00000000000..320affd4353 --- /dev/null +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/project/node/ProjectClasspathChangedEvent.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2012-2018 Red Hat, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.ide.project.node; + +import com.google.gwt.event.shared.EventHandler; +import com.google.gwt.event.shared.GwtEvent; + +/** @author V. Rubezhny */ +public class ProjectClasspathChangedEvent + extends GwtEvent { + + private String projectPath; + + public interface ProjectClasspathChangedHandler extends EventHandler { + void onProjectClasspathChanged(ProjectClasspathChangedEvent event); + } + + private static Type TYPE; + + public static Type getType() { + if (TYPE == null) { + TYPE = new Type<>(); + } + return TYPE; + } + + public ProjectClasspathChangedEvent(String projectPath) { + this.projectPath = projectPath; + } + + public String getProject() { + return projectPath; + } + + @Override + public Type getAssociatedType() { + return TYPE; + } + + /** {@inheritDoc} */ + @Override + protected void dispatch(ProjectClasspathChangedHandler handler) { + handler.onProjectClasspathChanged(this); + } +} diff --git a/plugins/plugin-java/che-plugin-java-ext-lang-client/src/main/java/org/eclipse/che/ide/ext/java/client/tree/library/LibrariesNode.java b/plugins/plugin-java/che-plugin-java-ext-lang-client/src/main/java/org/eclipse/che/ide/ext/java/client/tree/library/LibrariesNode.java index e1ae19ab448..677278b9b66 100644 --- a/plugins/plugin-java/che-plugin-java-ext-lang-client/src/main/java/org/eclipse/che/ide/ext/java/client/tree/library/LibrariesNode.java +++ b/plugins/plugin-java/che-plugin-java-ext-lang-client/src/main/java/org/eclipse/che/ide/ext/java/client/tree/library/LibrariesNode.java @@ -29,6 +29,8 @@ import org.eclipse.che.ide.ext.java.client.JavaResources; import org.eclipse.che.ide.ext.java.client.service.JavaLanguageExtensionServiceClient; import org.eclipse.che.ide.ext.java.client.tree.JavaNodeFactory; +import org.eclipse.che.ide.project.node.ProjectClasspathChangedEvent; +import org.eclipse.che.ide.project.node.ProjectClasspathChangedEvent.ProjectClasspathChangedHandler; import org.eclipse.che.ide.project.node.SyntheticNode; import org.eclipse.che.ide.project.node.SyntheticNodeUpdateEvent; import org.eclipse.che.ide.resource.Path; @@ -40,7 +42,8 @@ /** @author Vlad Zhukovskiy */ @Beta -public class LibrariesNode extends SyntheticNode implements ResourceChangedHandler { +public class LibrariesNode extends SyntheticNode + implements ResourceChangedHandler, ProjectClasspathChangedHandler { private final JavaNodeFactory nodeFactory; private DtoFactory dtoFactory; @@ -65,6 +68,7 @@ public LibrariesNode( this.eventBus = eventBus; eventBus.addHandler(ResourceChangedEvent.getType(), this); + eventBus.addHandler(ProjectClasspathChangedEvent.getType(), this); } @Override @@ -112,6 +116,15 @@ public void onResourceChanged(ResourceChangedEvent event) { } } + @Override + public void onProjectClasspathChanged(ProjectClasspathChangedEvent event) { + final Path project = new Path(event.getProject()); + + if (getProject().equals(project)) { + eventBus.fireEvent(new SyntheticNodeUpdateEvent(LibrariesNode.this)); + } + } + @Override public Path getProject() { return getData(); diff --git a/plugins/plugin-java/che-plugin-java-ext-lang-shared/src/main/java/org/eclipse/che/ide/ext/java/shared/Constants.java b/plugins/plugin-java/che-plugin-java-ext-lang-shared/src/main/java/org/eclipse/che/ide/ext/java/shared/Constants.java index 340b778610d..667700a7765 100644 --- a/plugins/plugin-java/che-plugin-java-ext-lang-shared/src/main/java/org/eclipse/che/ide/ext/java/shared/Constants.java +++ b/plugins/plugin-java/che-plugin-java-ext-lang-shared/src/main/java/org/eclipse/che/ide/ext/java/shared/Constants.java @@ -62,6 +62,12 @@ public final class Constants { public static final String VALIDATE_RENAMED_NAME = "java/validateRenamedName"; public static final String GET_LINKED_MODEL = "java/getLinkedModel"; + public static final String EXECUTE_CLIENT_COMMAND = "workspace/executeClientCommand"; + public static final String EXECUTE_CLIENT_COMMAND_UNSUBSCRIBE = + "workspace/executeClientCommand/unsubscribe"; + public static final String EXECUTE_CLIENT_COMMAND_SUBSCRIBE = + "workspace/executeClientCommand/subscribe"; + private Constants() { throw new UnsupportedOperationException("Unused constructor."); } diff --git a/plugins/plugin-java/che-plugin-java-server/src/main/java/org/eclipse/che/plugin/java/languageserver/ExecuteClientCommandJsonRpcTransmitter.java b/plugins/plugin-java/che-plugin-java-server/src/main/java/org/eclipse/che/plugin/java/languageserver/ExecuteClientCommandJsonRpcTransmitter.java new file mode 100644 index 00000000000..fd3484cf0de --- /dev/null +++ b/plugins/plugin-java/che-plugin-java-server/src/main/java/org/eclipse/che/plugin/java/languageserver/ExecuteClientCommandJsonRpcTransmitter.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2012-2018 Red Hat, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.plugin.java.languageserver; + +import static org.eclipse.che.ide.ext.java.shared.Constants.EXECUTE_CLIENT_COMMAND; +import static org.eclipse.che.ide.ext.java.shared.Constants.EXECUTE_CLIENT_COMMAND_SUBSCRIBE; +import static org.eclipse.che.ide.ext.java.shared.Constants.EXECUTE_CLIENT_COMMAND_UNSUBSCRIBE; + +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CopyOnWriteArraySet; +import javax.inject.Inject; +import javax.inject.Singleton; +import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator; +import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter; +import org.eclipse.che.api.core.notification.EventService; +import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls; +import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.ExecuteCommandParamsDto; +import org.eclipse.lsp4j.ExecuteCommandParams; + +/** Transmits 'workspace/executeClientCommand' over the JSON-RPC */ +@Singleton +public class ExecuteClientCommandJsonRpcTransmitter { + private final Set endpointIds = new CopyOnWriteArraySet<>(); + + private final RequestTransmitter requestTransmitter; + + @Inject + public ExecuteClientCommandJsonRpcTransmitter(RequestTransmitter requestTransmitter) { + this.requestTransmitter = requestTransmitter; + } + + @Inject + private void subscribe(EventService eventService, RequestTransmitter requestTransmitter) { + eventService.subscribe( + event -> + endpointIds.forEach( + endpointId -> + requestTransmitter + .newRequest() + .endpointId(endpointId) + .methodName(EXECUTE_CLIENT_COMMAND) + .paramsAsDto(new ExecuteCommandParamsDto(event)) + .sendAndSkipResult()), + ExecuteCommandParams.class); + } + + @Inject + private void configureSubscribeHandler(RequestHandlerConfigurator requestHandler) { + requestHandler + .newConfiguration() + .methodName(EXECUTE_CLIENT_COMMAND_SUBSCRIBE) + .noParams() + .noResult() + .withConsumer(endpointIds::add); + } + + @Inject + private void configureUnSubscribeHandler(RequestHandlerConfigurator requestHandler) { + requestHandler + .newConfiguration() + .methodName(EXECUTE_CLIENT_COMMAND_UNSUBSCRIBE) + .noParams() + .noResult() + .withConsumer(endpointIds::remove); + } + + public CompletableFuture executeClientCommand(ExecuteCommandParams requestParams) { + ExecuteCommandParamsDto paramsDto = + (ExecuteCommandParamsDto) DtoServerImpls.makeDto(requestParams); + + CompletableFuture result = new CompletableFuture<>(); + for (String endpointId : endpointIds) { + requestTransmitter + .newRequest() + .endpointId(endpointId) + .methodName(EXECUTE_CLIENT_COMMAND) + .paramsAsDto(paramsDto) + .sendAndSkipResult(); + } + result.complete(null); + return result; + } +} diff --git a/plugins/plugin-java/che-plugin-java-server/src/main/java/org/eclipse/che/plugin/java/languageserver/JavaLanguageClient.java b/plugins/plugin-java/che-plugin-java-server/src/main/java/org/eclipse/che/plugin/java/languageserver/JavaLanguageClient.java index 11b3279ec97..f47c8aa56b8 100644 --- a/plugins/plugin-java/che-plugin-java-server/src/main/java/org/eclipse/che/plugin/java/languageserver/JavaLanguageClient.java +++ b/plugins/plugin-java/che-plugin-java-server/src/main/java/org/eclipse/che/plugin/java/languageserver/JavaLanguageClient.java @@ -10,7 +10,10 @@ */ package org.eclipse.che.plugin.java.languageserver; +import java.util.concurrent.CompletableFuture; +import org.eclipse.lsp4j.ExecuteCommandParams; import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; public interface JavaLanguageClient { /** @@ -28,4 +31,7 @@ public interface JavaLanguageClient { */ @JsonNotification("language/progressReport") void sendProgressReport(ProgressReport report); + + @JsonRequest("workspace/executeClientCommand") + CompletableFuture executeClientCommand(ExecuteCommandParams params); } diff --git a/plugins/plugin-java/che-plugin-java-server/src/main/java/org/eclipse/che/plugin/java/languageserver/JavaLanguageServerLauncher.java b/plugins/plugin-java/che-plugin-java-server/src/main/java/org/eclipse/che/plugin/java/languageserver/JavaLanguageServerLauncher.java index 5c8414b5662..b25b7c3b554 100644 --- a/plugins/plugin-java/che-plugin-java-server/src/main/java/org/eclipse/che/plugin/java/languageserver/JavaLanguageServerLauncher.java +++ b/plugins/plugin-java/che-plugin-java-server/src/main/java/org/eclipse/che/plugin/java/languageserver/JavaLanguageServerLauncher.java @@ -10,6 +10,10 @@ */ package org.eclipse.che.plugin.java.languageserver; +import static org.eclipse.che.api.languageserver.LanguageServiceUtils.removePrefixUri; +import static org.eclipse.che.api.languageserver.util.JsonUtil.convertToJson; +import static org.eclipse.che.jdt.ls.extension.api.Commands.CLIENT_UPDATE_PROJECTS_CLASSPATH; + import com.google.inject.Inject; import com.google.inject.Singleton; import java.io.InputStream; @@ -18,14 +22,18 @@ import java.lang.reflect.Proxy; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CompletableFuture; import org.eclipse.che.api.languageserver.LanguageServerConfig; import org.eclipse.che.api.languageserver.ProcessCommunicationProvider; import org.eclipse.che.api.languageserver.service.FileContentAccess; import org.eclipse.che.api.languageserver.util.DynamicWrapper; +import org.eclipse.lsp4j.ExecuteCommandParams; import org.eclipse.lsp4j.jsonrpc.Launcher; import org.eclipse.lsp4j.services.LanguageClient; import org.eclipse.lsp4j.services.LanguageServer; @@ -43,10 +51,14 @@ public class JavaLanguageServerLauncher implements LanguageServerConfig { private final Path launchScript; private ProcessorJsonRpcCommunication processorJsonRpcCommunication; + private ExecuteClientCommandJsonRpcTransmitter executeCliendCommandTransmitter; @Inject - public JavaLanguageServerLauncher(ProcessorJsonRpcCommunication processorJsonRpcCommunication) { + public JavaLanguageServerLauncher( + ProcessorJsonRpcCommunication processorJsonRpcCommunication, + ExecuteClientCommandJsonRpcTransmitter executeCliendCommandTransmitter) { this.processorJsonRpcCommunication = processorJsonRpcCommunication; + this.executeCliendCommandTransmitter = executeCliendCommandTransmitter; launchScript = Paths.get(System.getenv("HOME"), "che/ls-java/launch.sh"); } @@ -64,6 +76,21 @@ public void sendProgressReport(ProgressReport report) { processorJsonRpcCommunication.sendProgressNotification(report); } + public CompletableFuture executeClientCommand(ExecuteCommandParams params) { + return executeCliendCommandTransmitter.executeClientCommand(convertParams(params)); + } + + private ExecuteCommandParams convertParams(ExecuteCommandParams params) { + if (CLIENT_UPDATE_PROJECTS_CLASSPATH.equals(params.getCommand())) { + List fixedPathList = new ArrayList<>(); + for (Object uri : params.getArguments()) { + fixedPathList.add(removePrefixUri(convertToJson(uri).getAsString())); + } + params.setArguments(fixedPathList); + } + return params; + } + @Override public RegexProvider getRegexpProvider() { return new RegexProvider() { diff --git a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/LanguageServerExtension.java b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/LanguageServerExtension.java index 0625f16ab01..2849cf1d757 100644 --- a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/LanguageServerExtension.java +++ b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/LanguageServerExtension.java @@ -40,6 +40,7 @@ import org.eclipse.che.plugin.languageserver.ide.navigation.workspace.FindSymbolAction; import org.eclipse.che.plugin.languageserver.ide.registry.LanguageServerRegistry; import org.eclipse.che.plugin.languageserver.ide.rename.LSRenameAction; +import org.eclipse.che.plugin.languageserver.ide.service.ExecuteClientCommandReceiver; import org.eclipse.che.plugin.languageserver.ide.service.PublishDiagnosticsReceiver; import org.eclipse.che.plugin.languageserver.ide.service.ShowMessageJsonRpcReceiver; import org.eclipse.che.plugin.languageserver.ide.service.TextDocumentServiceClient; @@ -63,7 +64,8 @@ public LanguageServerExtension( EventBus eventBus, AppContext appContext, ShowMessageJsonRpcReceiver showMessageJsonRpcReceiver, - PublishDiagnosticsReceiver publishDiagnosticsReceiver) { + PublishDiagnosticsReceiver publishDiagnosticsReceiver, + ExecuteClientCommandReceiver executeClientCommandReceiver) { eventBus.addHandler( WsAgentServerRunningEvent.TYPE, e -> { @@ -73,6 +75,7 @@ public LanguageServerExtension( defaultHoverProviderInitializer.initialize(); showMessageJsonRpcReceiver.subscribe(); publishDiagnosticsReceiver.subscribe(); + executeClientCommandReceiver.subscribe(); }); if (appContext.getWorkspace().getStatus() == RUNNING) { @@ -82,6 +85,7 @@ public LanguageServerExtension( defaultHoverProviderInitializer.initialize(); showMessageJsonRpcReceiver.subscribe(); publishDiagnosticsReceiver.subscribe(); + executeClientCommandReceiver.subscribe(); } } diff --git a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/inject/LanguageServerGinModule.java b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/inject/LanguageServerGinModule.java index f1445b8d22e..d8710247f1a 100644 --- a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/inject/LanguageServerGinModule.java +++ b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/inject/LanguageServerGinModule.java @@ -28,6 +28,7 @@ import org.eclipse.che.plugin.languageserver.ide.editor.signature.LanguageServerSignatureHelpFactory; import org.eclipse.che.plugin.languageserver.ide.location.OpenLocationPresenterFactory; import org.eclipse.che.plugin.languageserver.ide.rename.node.RenameNodeFactory; +import org.eclipse.che.plugin.languageserver.ide.service.ExecuteClientCommandReceiver; import org.eclipse.che.plugin.languageserver.ide.service.PublishDiagnosticsReceiver; import org.eclipse.che.plugin.languageserver.ide.service.ShowMessageJsonRpcReceiver; @@ -52,6 +53,7 @@ protected void configure() { bind(PublishDiagnosticsReceiver.class).asEagerSingleton(); bind(ShowMessageJsonRpcReceiver.class).asEagerSingleton(); + bind(ExecuteClientCommandReceiver.class).asEagerSingleton(); GinMultibinder.newSetBinder(binder(), LanguageDescription.class); diff --git a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/ExecuteClientCommandProcessor.java b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/ExecuteClientCommandProcessor.java new file mode 100644 index 00000000000..ee297cbb0ec --- /dev/null +++ b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/ExecuteClientCommandProcessor.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2012-2018 Red Hat, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.plugin.languageserver.ide.service; + +import com.google.gwt.json.client.JSONString; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.google.web.bindery.event.shared.EventBus; +import org.eclipse.che.ide.project.node.ProjectClasspathChangedEvent; +import org.eclipse.lsp4j.ExecuteCommandParams; + +/** + * A processor for incoming workspace/ClasspathChanged notifications sent by a language + * server. + * + * @author V. Rubezhny + */ +@Singleton +public class ExecuteClientCommandProcessor { + private static final String CLIENT_UPDATE_PROJECTS_CLASSPATH = + "che.jdt.ls.extension.workspace.clientUpdateProjectsClasspath"; + + private EventBus eventBus; + + @Inject + public ExecuteClientCommandProcessor(EventBus eventBus) { + this.eventBus = eventBus; + } + + public void execute(ExecuteCommandParams params) { + if (CLIENT_UPDATE_PROJECTS_CLASSPATH.equals(params.getCommand())) { + for (Object project : params.getArguments()) { + eventBus.fireEvent(new ProjectClasspathChangedEvent(stringValue(project))); + } + } + } + + private String stringValue(Object value) { + return value instanceof JSONString ? ((JSONString) value).stringValue() : String.valueOf(value); + } +} diff --git a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/ExecuteClientCommandReceiver.java b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/ExecuteClientCommandReceiver.java new file mode 100644 index 00000000000..3348f45757e --- /dev/null +++ b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/ExecuteClientCommandReceiver.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2012-2018 Red Hat, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.plugin.languageserver.ide.service; + +import static org.eclipse.che.ide.api.jsonrpc.Constants.WS_AGENT_JSON_RPC_ENDPOINT_ID; + +import javax.inject.Inject; +import javax.inject.Provider; +import javax.inject.Singleton; +import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator; +import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter; +import org.eclipse.lsp4j.ExecuteCommandParams; + +/** + * Subscribes and receives JSON-RPC messages related to ClassPath Notification + * 'workspace/executeClientCommand' events + */ +@Singleton +public class ExecuteClientCommandReceiver { + public static final String EXECUTE_CLIENT_COMMAND = "workspace/executeClientCommand"; + public static final String EXECUTE_CLIENT_COMMAND_SUBSCRIBE = + "workspace/executeClientCommand/subscribe"; + private final RequestTransmitter transmitter; + + @Inject + public ExecuteClientCommandReceiver(RequestTransmitter transmitter) { + this.transmitter = transmitter; + } + + public void subscribe() { + subscribe(transmitter); + } + + @Inject + private void configureReceiver( + Provider provider, RequestHandlerConfigurator configurator) { + configurator + .newConfiguration() + .methodName(EXECUTE_CLIENT_COMMAND) + .paramsAsDto(ExecuteCommandParams.class) + .noResult() + .withConsumer(params -> provider.get().execute(params)); + } + + private void subscribe(RequestTransmitter transmitter) { + transmitter + .newRequest() + .endpointId(WS_AGENT_JSON_RPC_ENDPOINT_ID) + .methodName(EXECUTE_CLIENT_COMMAND_SUBSCRIBE) + .noParams() + .sendAndSkipResult(); + } +}