diff --git a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/ConsumerConfiguratorNoneToNone.java b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/ConsumerConfiguratorNoneToNone.java index de430d5eae6..3f4fb3313ab 100644 --- a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/ConsumerConfiguratorNoneToNone.java +++ b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/ConsumerConfiguratorNoneToNone.java @@ -39,4 +39,8 @@ public void withConsumer(Consumer consumer) { LOGGER.debug("Configuring incoming request binary: consumer for method: " + method); handlerManager.registerNoneToNone(method, consumer); } + + public void withRunnable(Runnable runnable) { + withConsumer(s -> runnable.run()); + } } diff --git a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/FunctionConfiguratorNoneToMany.java b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/FunctionConfiguratorNoneToMany.java index 9da2bbbfacd..0b1a7c65834 100644 --- a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/FunctionConfiguratorNoneToMany.java +++ b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/FunctionConfiguratorNoneToMany.java @@ -15,6 +15,7 @@ import java.util.List; import java.util.function.Function; +import java.util.function.Supplier; import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerManager; import org.slf4j.Logger; @@ -58,4 +59,13 @@ public void withFunction(Function> function) { handlerManager.registerNoneToMany(method, rClass, function); } + + /** + * Define a supplier to be applied + * + * @param supplier supplier + */ + public void withSupplier(Supplier> supplier) { + withFunction(s -> supplier.get()); + } } diff --git a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/FunctionConfiguratorNoneToOne.java b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/FunctionConfiguratorNoneToOne.java index 1e1e0bbcacb..340ae462172 100644 --- a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/FunctionConfiguratorNoneToOne.java +++ b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/FunctionConfiguratorNoneToOne.java @@ -14,6 +14,7 @@ import static org.slf4j.LoggerFactory.getLogger; import java.util.function.Function; +import java.util.function.Supplier; import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerManager; import org.slf4j.Logger; @@ -56,4 +57,13 @@ public void withFunction(Function function) { handlerManager.registerNoneToOne(method, rClass, function); } + + /** + * Define a supplier to be applied + * + * @param supplier supplier + */ + public void withSupplier(Supplier supplier) { + withFunction(s -> supplier.get()); + } } diff --git a/ide/che-core-ide-api/pom.xml b/ide/che-core-ide-api/pom.xml index 29143a0167c..253e208390d 100644 --- a/ide/che-core-ide-api/pom.xml +++ b/ide/che-core-ide-api/pom.xml @@ -171,6 +171,10 @@ org.eclipse.che.core che-core-commons-gwt + + org.vectomatic + lib-gwt-svg + com.google.gwt gwt-user diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/filetypes/FileType.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/filetypes/FileType.java index 38fce489dfb..8bc73bfa02a 100644 --- a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/filetypes/FileType.java +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/filetypes/FileType.java @@ -32,6 +32,18 @@ public class FileType { private String namePattern; + public void setImage(SVGResource image) { + this.image = image; + } + + public void setExtension(String extension) { + this.extension = extension; + } + + public void setNamePattern(String namePattern) { + this.namePattern = namePattern; + } + public FileType(SVGResource image, String extension) { this(image, extension, null); } diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/filetypes/FileTypeRegistry.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/filetypes/FileTypeRegistry.java index 6350b37ebf3..c6d0db4a4f2 100644 --- a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/filetypes/FileTypeRegistry.java +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/filetypes/FileTypeRegistry.java @@ -56,5 +56,5 @@ public interface FileTypeRegistry { * @return file type or default file type if no file type's name pattern matches the given file * name */ - FileType getFileTypeByNamePattern(String name); + FileType getFileTypeByFileName(String name); } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/filetypes/FileTypeRegistryImpl.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/filetypes/FileTypeRegistryImpl.java index 10d470001fe..c4444228497 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/filetypes/FileTypeRegistryImpl.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/filetypes/FileTypeRegistryImpl.java @@ -51,7 +51,7 @@ public List getRegisteredFileTypes() { @Override public FileType getFileTypeByFile(VirtualFile file) { - FileType fileType = getFileTypeByNamePattern(file.getName()); + FileType fileType = getFileTypeByFileName(file.getName()); if (fileType == unknownFileType) { fileType = getFileTypeByExtension(getFileExtension(file.getName())); } @@ -72,7 +72,7 @@ public FileType getFileTypeByExtension(String extension) { } @Override - public FileType getFileTypeByNamePattern(String name) { + public FileType getFileTypeByFileName(String name) { if (!Strings.isNullOrEmpty(name)) { for (FileType type : fileTypes) { if (type.getNamePattern() != null) { diff --git a/plugins/plugin-camel/che-plugin-camel-server/pom.xml b/plugins/plugin-camel/che-plugin-camel-server/pom.xml index b349edafd13..c6c070b15c8 100644 --- a/plugins/plugin-camel/che-plugin-camel-server/pom.xml +++ b/plugins/plugin-camel/che-plugin-camel-server/pom.xml @@ -24,6 +24,10 @@ false + + com.google.guava + guava + com.google.inject guice @@ -36,29 +40,13 @@ javax.inject javax.inject - - org.eclipse.che.core - che-core-api-core - org.eclipse.che.core che-core-api-languageserver - - org.eclipse.che.core - che-core-api-languageserver-shared - org.eclipse.che.core che-core-commons-inject - - org.eclipse.lsp4j - org.eclipse.lsp4j - - - org.eclipse.lsp4j - org.eclipse.lsp4j.jsonrpc - diff --git a/plugins/plugin-camel/che-plugin-camel-server/src/main/java/org/eclipse/che/plugin/camel/server/inject/CamelModule.java b/plugins/plugin-camel/che-plugin-camel-server/src/main/java/org/eclipse/che/plugin/camel/server/inject/CamelModule.java index ce646eafe1d..5f57f9c3c1b 100644 --- a/plugins/plugin-camel/che-plugin-camel-server/src/main/java/org/eclipse/che/plugin/camel/server/inject/CamelModule.java +++ b/plugins/plugin-camel/che-plugin-camel-server/src/main/java/org/eclipse/che/plugin/camel/server/inject/CamelModule.java @@ -10,33 +10,23 @@ */ package org.eclipse.che.plugin.camel.server.inject; -import static java.util.Arrays.asList; +import static com.google.inject.multibindings.MapBinder.newMapBinder; import com.google.inject.AbstractModule; -import com.google.inject.multibindings.Multibinder; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; +import org.eclipse.che.api.languageserver.LanguageServerConfig; import org.eclipse.che.inject.DynaModule; -import org.eclipse.che.plugin.camel.server.languageserver.CamelLanguageServerLauncher; +import org.eclipse.che.plugin.camel.server.languageserver.CamelLanguageServerConfig; /** Apache Camel module for server side of Camel Language Server */ @DynaModule public class CamelModule extends AbstractModule { public static final String LANGUAGE_ID = "LANGUAGE_ID_APACHE_CAMEL"; - private static final String[] EXTENSIONS = new String[] {"xml"}; - private static final String MIME_TYPE = "text/xml"; @Override protected void configure() { - Multibinder.newSetBinder(binder(), LanguageServerLauncher.class) - .addBinding() - .to(CamelLanguageServerLauncher.class); - LanguageDescription description = new LanguageDescription(); - description.setFileExtensions(asList(EXTENSIONS)); - description.setLanguageId(LANGUAGE_ID); - description.setMimeType(MIME_TYPE); - Multibinder.newSetBinder(binder(), LanguageDescription.class) - .addBinding() - .toInstance(description); + newMapBinder(binder(), String.class, LanguageServerConfig.class) + .addBinding("org.eclipse.che.plugin.camel.server.languageserver") + .to(CamelLanguageServerConfig.class) + .asEagerSingleton(); } } diff --git a/plugins/plugin-camel/che-plugin-camel-server/src/main/java/org/eclipse/che/plugin/camel/server/languageserver/CamelLanguageServerConfig.java b/plugins/plugin-camel/che-plugin-camel-server/src/main/java/org/eclipse/che/plugin/camel/server/languageserver/CamelLanguageServerConfig.java new file mode 100644 index 00000000000..fa299b7de2b --- /dev/null +++ b/plugins/plugin-camel/che-plugin-camel-server/src/main/java/org/eclipse/che/plugin/camel/server/languageserver/CamelLanguageServerConfig.java @@ -0,0 +1,82 @@ +/* + * 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.camel.server.languageserver; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.Set; +import javax.inject.Inject; +import javax.inject.Singleton; +import org.eclipse.che.api.languageserver.DefaultInstanceProvider; +import org.eclipse.che.api.languageserver.LanguageServerConfig; +import org.eclipse.che.api.languageserver.ProcessCommunicationProvider; +import org.eclipse.che.plugin.camel.server.inject.CamelModule; + +/** Launcher for Apache Camel Language Server */ +@Singleton +public class CamelLanguageServerConfig implements LanguageServerConfig { + private static final String REGEX = ".*\\.(xml)"; + + private final Path launchScript; + + @Inject + public CamelLanguageServerConfig() { + + this.launchScript = Paths.get(System.getenv("HOME"), "che/ls-camel/launch.sh"); + } + + @Override + public RegexProvider getRegexpProvider() { + return new RegexProvider() { + @Override + public Map getLanguageRegexes() { + return ImmutableMap.of(CamelModule.LANGUAGE_ID, REGEX); + } + + @Override + public Set getFileWatchPatterns() { + return ImmutableSet.of(); + } + }; + } + + @Override + public CommunicationProvider getCommunicationProvider() { + ProcessBuilder processBuilder = new ProcessBuilder(launchScript.toString()); + processBuilder.redirectInput(ProcessBuilder.Redirect.PIPE); + processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE); + + return new ProcessCommunicationProvider(processBuilder, CamelModule.LANGUAGE_ID); + } + + @Override + public InstanceProvider getInstanceProvider() { + return DefaultInstanceProvider.getInstance(); + } + + @Override + public InstallerStatusProvider getInstallerStatusProvider() { + return new InstallerStatusProvider() { + @Override + public boolean isSuccessfullyInstalled() { + return launchScript.toFile().exists(); + } + + @Override + public String getCause() { + return isSuccessfullyInstalled() ? null : "Launch script file does not exist"; + } + }; + } +} diff --git a/plugins/plugin-camel/che-plugin-camel-server/src/main/java/org/eclipse/che/plugin/camel/server/languageserver/CamelLanguageServerLauncher.java b/plugins/plugin-camel/che-plugin-camel-server/src/main/java/org/eclipse/che/plugin/camel/server/languageserver/CamelLanguageServerLauncher.java deleted file mode 100644 index d2cf0883859..00000000000 --- a/plugins/plugin-camel/che-plugin-camel-server/src/main/java/org/eclipse/che/plugin/camel/server/languageserver/CamelLanguageServerLauncher.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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.camel.server.languageserver; - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import javax.inject.Named; -import org.eclipse.che.api.core.rest.HttpJsonRequestFactory; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncherTemplate; -import org.eclipse.che.api.languageserver.registry.DocumentFilter; -import org.eclipse.che.api.languageserver.registry.LanguageServerDescription; -import org.eclipse.che.api.languageserver.registry.ServerInitializerObserver; -import org.eclipse.che.plugin.camel.server.inject.CamelModule; -import org.eclipse.lsp4j.ServerCapabilities; -import org.eclipse.lsp4j.jsonrpc.Launcher; -import org.eclipse.lsp4j.services.LanguageClient; -import org.eclipse.lsp4j.services.LanguageServer; - -/** Launcher for Apache Camel Language Server */ -@Singleton -public class CamelLanguageServerLauncher extends LanguageServerLauncherTemplate - implements ServerInitializerObserver { - - private static final String REGEX = ".*\\.(xml)"; - private static final LanguageServerDescription DESCRIPTION = createServerDescription(); - private static LanguageServer camelLanguageServer; - private final Path launchScript; - - @Inject - public CamelLanguageServerLauncher( - @Named("che.api") String apiUrl, HttpJsonRequestFactory requestFactory) { - launchScript = Paths.get(System.getenv("HOME"), "che/ls-camel/launch.sh"); - } - - @Override - public boolean isAbleToLaunch() { - return Files.exists(launchScript); - } - - protected LanguageServer connectToLanguageServer( - final Process languageServerProcess, LanguageClient client) { - Launcher launcher = - Launcher.createLauncher( - client, - LanguageServer.class, - languageServerProcess.getInputStream(), - languageServerProcess.getOutputStream()); - launcher.startListening(); - setCamelLanguageServer(launcher.getRemoteProxy()); - return launcher.getRemoteProxy(); - } - - protected Process startLanguageServerProcess(String projectPath) throws LanguageServerException { - ProcessBuilder processBuilder = new ProcessBuilder(launchScript.toString()); - processBuilder.redirectInput(ProcessBuilder.Redirect.PIPE); - processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE); - try { - return processBuilder.start(); - } catch (IOException e) { - throw new LanguageServerException("Can't start Apache Camel language server", e); - } - } - - protected static LanguageServer getCamelLanguageServer() { - return camelLanguageServer; - } - - protected static void setCamelLanguageServer(LanguageServer camelLanguageServer) { - CamelLanguageServerLauncher.camelLanguageServer = camelLanguageServer; - } - - public LanguageServerDescription getDescription() { - return DESCRIPTION; - } - - private static LanguageServerDescription createServerDescription() { - LanguageServerDescription description = - new LanguageServerDescription( - "org.eclipse.che.plugin.camel.server.languageserver", - null, - Arrays.asList(new DocumentFilter(CamelModule.LANGUAGE_ID, REGEX, null))); - return description; - } - - @Override - public void onServerInitialized( - LanguageServerLauncher arg0, LanguageServer arg1, ServerCapabilities arg2, String arg3) { - // nothing to do - } -} diff --git a/plugins/plugin-clangd/che-plugin-clangd-lang-server/pom.xml b/plugins/plugin-clangd/che-plugin-clangd-lang-server/pom.xml index b4ebe0225f5..0717a7a6f0f 100644 --- a/plugins/plugin-clangd/che-plugin-clangd-lang-server/pom.xml +++ b/plugins/plugin-clangd/che-plugin-clangd-lang-server/pom.xml @@ -24,6 +24,10 @@ false + + com.google.guava + guava + com.google.inject guice @@ -36,10 +40,6 @@ org.eclipse.che.core che-core-api-languageserver - - org.eclipse.che.core - che-core-api-languageserver-shared - org.eclipse.che.core che-core-api-project @@ -52,14 +52,6 @@ org.eclipse.che.plugin che-plugin-cpp-lang-server - - org.eclipse.lsp4j - org.eclipse.lsp4j - - - org.eclipse.lsp4j - org.eclipse.lsp4j.jsonrpc - org.slf4j slf4j-api diff --git a/plugins/plugin-clangd/che-plugin-clangd-lang-server/src/main/java/org/eclipse/plugin/clangd/inject/ClangModule.java b/plugins/plugin-clangd/che-plugin-clangd-lang-server/src/main/java/org/eclipse/plugin/clangd/inject/ClangModule.java index 0698a351430..c7b28c0d778 100644 --- a/plugins/plugin-clangd/che-plugin-clangd-lang-server/src/main/java/org/eclipse/plugin/clangd/inject/ClangModule.java +++ b/plugins/plugin-clangd/che-plugin-clangd-lang-server/src/main/java/org/eclipse/plugin/clangd/inject/ClangModule.java @@ -10,49 +10,33 @@ */ package org.eclipse.plugin.clangd.inject; -import static java.util.Arrays.asList; +import static com.google.inject.multibindings.MapBinder.newMapBinder; +import static com.google.inject.multibindings.Multibinder.newSetBinder; import com.google.inject.AbstractModule; -import com.google.inject.multibindings.Multibinder; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; +import org.eclipse.che.api.languageserver.LanguageServerConfig; import org.eclipse.che.api.project.server.type.ProjectTypeDef; import org.eclipse.che.inject.DynaModule; import org.eclipse.che.plugin.cpp.projecttype.CProjectType; import org.eclipse.che.plugin.cpp.projecttype.CppProjectType; -import org.eclipse.plugin.clangd.languageserver.ClangDLanguageServerLauncher; +import org.eclipse.plugin.clangd.languageserver.ClangDLanguageServerConfig; -/** @author Alexander Andrienko */ -/** @author Hanno Kolvenbach */ +/** + * @author Alexander Andrienko + * @author Hanno Kolvenbach + */ @DynaModule public class ClangModule extends AbstractModule { public static final String LANGUAGE_ID = "clangd"; - private static final String[] EXTENSIONS = - new String[] { - "c", "h", "cpp", "hpp", "cc", "hh", "hxx", "cxx", "C", "H", "CPP", "HPP", "CC", "HH", "CXX", - "HXX" - }; - private static final String MIME_TYPE = "text/x-cpp"; @Override protected void configure() { - Multibinder projectTypeMultibinder = - Multibinder.newSetBinder(binder(), ProjectTypeDef.class); - - projectTypeMultibinder.addBinding().to(CProjectType.class); - projectTypeMultibinder.addBinding().to(CppProjectType.class); - - Multibinder.newSetBinder(binder(), LanguageServerLauncher.class) - .addBinding() - .to(ClangDLanguageServerLauncher.class); - - LanguageDescription description = new LanguageDescription(); - description.setFileExtensions(asList(EXTENSIONS)); - description.setLanguageId(LANGUAGE_ID); - description.setMimeType(MIME_TYPE); + newSetBinder(binder(), ProjectTypeDef.class).addBinding().to(CProjectType.class); + newSetBinder(binder(), ProjectTypeDef.class).addBinding().to(CppProjectType.class); - Multibinder.newSetBinder(binder(), LanguageDescription.class) - .addBinding() - .toInstance(description); + newMapBinder(binder(), String.class, LanguageServerConfig.class) + .addBinding("org.eclipse.che.plugin.clangd.languageserver") + .to(ClangDLanguageServerConfig.class) + .asEagerSingleton(); } } diff --git a/plugins/plugin-clangd/che-plugin-clangd-lang-server/src/main/java/org/eclipse/plugin/clangd/languageserver/ClangDLanguageServerConfig.java b/plugins/plugin-clangd/che-plugin-clangd-lang-server/src/main/java/org/eclipse/plugin/clangd/languageserver/ClangDLanguageServerConfig.java new file mode 100644 index 00000000000..e3f405f38b4 --- /dev/null +++ b/plugins/plugin-clangd/che-plugin-clangd-lang-server/src/main/java/org/eclipse/plugin/clangd/languageserver/ClangDLanguageServerConfig.java @@ -0,0 +1,90 @@ +/* + * 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.plugin.clangd.languageserver; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.Set; +import org.eclipse.che.api.languageserver.DefaultInstanceProvider; +import org.eclipse.che.api.languageserver.LanguageServerConfig; +import org.eclipse.che.api.languageserver.ProcessCommunicationProvider; +import org.eclipse.plugin.clangd.inject.ClangModule; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Alexander Andrienko + * @author Hanno Kolvenbach + */ +@Singleton +public class ClangDLanguageServerConfig implements LanguageServerConfig { + private static final Logger LOG = LoggerFactory.getLogger(ClangDLanguageServerConfig.class); + + private static final String REGEX = ".*\\.(c|h|cc|hh|cpp|hpp|cxx|hxx|C|H|CC|HH|CPP|HPP|CXX|HXX)"; + + private final Path launchScript; + + @Inject + public ClangDLanguageServerConfig() { + launchScript = Paths.get(System.getenv("HOME"), "che/ls-clangd/launch.sh"); + } + + @Override + public RegexProvider getRegexpProvider() { + return new RegexProvider() { + @Override + public Map getLanguageRegexes() { + return ImmutableMap.of(ClangModule.LANGUAGE_ID, REGEX); + } + + @Override + public Set getFileWatchPatterns() { + return ImmutableSet.of(); + } + }; + } + + @Override + public CommunicationProvider getCommunicationProvider() { + ProcessBuilder processBuilder = new ProcessBuilder(); + + processBuilder.command(launchScript.toString()).inheritIO(); + processBuilder.redirectInput(ProcessBuilder.Redirect.PIPE); + processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE); + + return new ProcessCommunicationProvider(processBuilder, ClangModule.LANGUAGE_ID); + } + + @Override + public InstanceProvider getInstanceProvider() { + return DefaultInstanceProvider.getInstance(); + } + + @Override + public InstallerStatusProvider getInstallerStatusProvider() { + return new InstallerStatusProvider() { + @Override + public boolean isSuccessfullyInstalled() { + return launchScript.toFile().exists(); + } + + @Override + public String getCause() { + return isSuccessfullyInstalled() ? null : "Launch script file does not exist"; + } + }; + } +} diff --git a/plugins/plugin-clangd/che-plugin-clangd-lang-server/src/main/java/org/eclipse/plugin/clangd/languageserver/ClangDLanguageServerLauncher.java b/plugins/plugin-clangd/che-plugin-clangd-lang-server/src/main/java/org/eclipse/plugin/clangd/languageserver/ClangDLanguageServerLauncher.java deleted file mode 100644 index c6a4219f277..00000000000 --- a/plugins/plugin-clangd/che-plugin-clangd-lang-server/src/main/java/org/eclipse/plugin/clangd/languageserver/ClangDLanguageServerLauncher.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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.plugin.clangd.languageserver; - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncherTemplate; -import org.eclipse.che.api.languageserver.registry.DocumentFilter; -import org.eclipse.che.api.languageserver.registry.LanguageServerDescription; -import org.eclipse.che.api.languageserver.registry.ServerInitializerObserver; -import org.eclipse.che.api.languageserver.service.LanguageServiceUtils; -import org.eclipse.lsp4j.ServerCapabilities; -import org.eclipse.lsp4j.jsonrpc.Launcher; -import org.eclipse.lsp4j.services.LanguageClient; -import org.eclipse.lsp4j.services.LanguageServer; -import org.eclipse.plugin.clangd.inject.ClangModule; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** @author Alexander Andrienko */ -/** @author Hanno Kolvenbach */ -@Singleton -public class ClangDLanguageServerLauncher extends LanguageServerLauncherTemplate - implements ServerInitializerObserver { - - private static final LanguageServerDescription DESCRIPTION = createServerDescription(); - private static final String REGEX = ".*\\.(c|h|cc|hh|cpp|hpp|cxx|hxx|C|H|CC|HH|CPP|HPP|CXX|HXX)"; - private final Path launchScript; - - private static final Logger LOG = LoggerFactory.getLogger(ClangDLanguageServerLauncher.class); - - @Inject - public ClangDLanguageServerLauncher() { - launchScript = Paths.get(System.getenv("HOME"), "che/ls-clangd/launch.sh"); - } - - @Override - public boolean isAbleToLaunch() { - return Files.exists(launchScript); - } - - @Override - protected LanguageServer connectToLanguageServer( - Process languageServerProcess, LanguageClient client) throws LanguageServerException { - Launcher launcher = - Launcher.createLauncher( - client, - LanguageServer.class, - languageServerProcess.getInputStream(), - languageServerProcess.getOutputStream()); - launcher.startListening(); - return launcher.getRemoteProxy(); - } - - protected Process startLanguageServerProcess(String projectPath) throws LanguageServerException { - - ProcessBuilder processBuilder = new ProcessBuilder(); - - processBuilder.directory(new File(LanguageServiceUtils.removeUriScheme(projectPath))); - processBuilder.command(launchScript.toString()).inheritIO(); - processBuilder.redirectInput(ProcessBuilder.Redirect.PIPE); - - processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE); - try { - return processBuilder.start(); - } catch (IOException e) { - throw new LanguageServerException("Can't start ClangD language server", e); - } - } - - @Override - public void onServerInitialized( - LanguageServerLauncher launcher, - LanguageServer server, - ServerCapabilities capabilities, - String projectPath) { - LOG.debug(projectPath); - LOG.debug("clangd language server initialized"); - } - - @Override - public LanguageServerDescription getDescription() { - return DESCRIPTION; - } - - private static LanguageServerDescription createServerDescription() { - LanguageServerDescription description = - new LanguageServerDescription( - "org.eclipse.che.plugin.clangd.languageserver", - null, - Arrays.asList(new DocumentFilter(ClangModule.LANGUAGE_ID, REGEX, null))); - return description; - } -} diff --git a/plugins/plugin-cpp/che-plugin-cpp-lang-ide/pom.xml b/plugins/plugin-cpp/che-plugin-cpp-lang-ide/pom.xml index 90f1c8ed0ee..9bc7bcf265f 100644 --- a/plugins/plugin-cpp/che-plugin-cpp-lang-ide/pom.xml +++ b/plugins/plugin-cpp/che-plugin-cpp-lang-ide/pom.xml @@ -30,10 +30,18 @@ com.google.inject guice + + javax.inject + javax.inject + javax.validation validation-api + + org.eclipse.che.core + che-core-api-languageserver-shared + org.eclipse.che.core che-core-ide-api diff --git a/plugins/plugin-cpp/che-plugin-cpp-lang-ide/src/main/java/org/eclipse/che/plugin/cpp/ide/CppLanguageDescriptionProvider.java b/plugins/plugin-cpp/che-plugin-cpp-lang-ide/src/main/java/org/eclipse/che/plugin/cpp/ide/CppLanguageDescriptionProvider.java new file mode 100644 index 00000000000..cc0f688b7ff --- /dev/null +++ b/plugins/plugin-cpp/che-plugin-cpp-lang-ide/src/main/java/org/eclipse/che/plugin/cpp/ide/CppLanguageDescriptionProvider.java @@ -0,0 +1,35 @@ +/* + * 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.cpp.ide; + +import static java.util.Arrays.asList; + +import javax.inject.Provider; +import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; + +public class CppLanguageDescriptionProvider implements Provider { + private static final String LANGUAGE_ID = "cpp"; + private static final String[] EXTENSIONS = + new String[] { + "c", "h", "cpp", "hpp", "cc", "hh", "hxx", "cxx", "C", "H", "CPP", "HPP", "CC", "HH", "CXX", + "HXX" + }; + private static final String MIME_TYPE = "text/x-cpp"; + + @Override + public LanguageDescription get() { + LanguageDescription description = new LanguageDescription(); + description.setFileExtensions(asList(EXTENSIONS)); + description.setLanguageId(LANGUAGE_ID); + description.setMimeType(MIME_TYPE); + return description; + } +} diff --git a/plugins/plugin-cpp/che-plugin-cpp-lang-ide/src/main/java/org/eclipse/che/plugin/cpp/ide/inject/CppGinModule.java b/plugins/plugin-cpp/che-plugin-cpp-lang-ide/src/main/java/org/eclipse/che/plugin/cpp/ide/inject/CppGinModule.java index d93aa971d6f..4062fb31cb2 100644 --- a/plugins/plugin-cpp/che-plugin-cpp-lang-ide/src/main/java/org/eclipse/che/plugin/cpp/ide/inject/CppGinModule.java +++ b/plugins/plugin-cpp/che-plugin-cpp-lang-ide/src/main/java/org/eclipse/che/plugin/cpp/ide/inject/CppGinModule.java @@ -10,18 +10,20 @@ */ package org.eclipse.che.plugin.cpp.ide.inject; +import static com.google.gwt.inject.client.multibindings.GinMultibinder.newSetBinder; import static org.eclipse.che.plugin.cpp.shared.Constants.CPP_EXT; import static org.eclipse.che.plugin.cpp.shared.Constants.C_EXT; import static org.eclipse.che.plugin.cpp.shared.Constants.H_EXT; import com.google.gwt.inject.client.AbstractGinModule; -import com.google.gwt.inject.client.multibindings.GinMultibinder; import com.google.inject.Provides; import com.google.inject.Singleton; import com.google.inject.name.Named; +import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; import org.eclipse.che.ide.api.extension.ExtensionGinModule; import org.eclipse.che.ide.api.filetypes.FileType; import org.eclipse.che.ide.api.project.type.wizard.ProjectWizardRegistrar; +import org.eclipse.che.plugin.cpp.ide.CppLanguageDescriptionProvider; import org.eclipse.che.plugin.cpp.ide.CppResources; import org.eclipse.che.plugin.cpp.ide.project.CProjectWizardRegistrar; import org.eclipse.che.plugin.cpp.ide.project.CppProjectWizardRegistrar; @@ -33,12 +35,15 @@ public class CppGinModule extends AbstractGinModule { /** {@inheritDoc} */ @Override protected void configure() { - GinMultibinder.newSetBinder(binder(), ProjectWizardRegistrar.class) + newSetBinder(binder(), ProjectWizardRegistrar.class) .addBinding() .to(CppProjectWizardRegistrar.class); - GinMultibinder.newSetBinder(binder(), ProjectWizardRegistrar.class) + newSetBinder(binder(), ProjectWizardRegistrar.class) .addBinding() .to(CProjectWizardRegistrar.class); + newSetBinder(binder(), LanguageDescription.class) + .addBinding() + .toProvider(CppLanguageDescriptionProvider.class); } @Provides diff --git a/plugins/plugin-csharp/che-plugin-csharp-lang-ide/pom.xml b/plugins/plugin-csharp/che-plugin-csharp-lang-ide/pom.xml index 07eec2a179b..821a812e264 100644 --- a/plugins/plugin-csharp/che-plugin-csharp-lang-ide/pom.xml +++ b/plugins/plugin-csharp/che-plugin-csharp-lang-ide/pom.xml @@ -30,10 +30,18 @@ com.google.inject guice + + javax.inject + javax.inject + javax.validation validation-api + + org.eclipse.che.core + che-core-api-languageserver-shared + org.eclipse.che.core che-core-ide-api diff --git a/plugins/plugin-csharp/che-plugin-csharp-lang-ide/src/main/java/org/eclipse/che/plugin/csharp/ide/CSharpLanguageDescriptionProvider.java b/plugins/plugin-csharp/che-plugin-csharp-lang-ide/src/main/java/org/eclipse/che/plugin/csharp/ide/CSharpLanguageDescriptionProvider.java new file mode 100644 index 00000000000..b4f740aef9e --- /dev/null +++ b/plugins/plugin-csharp/che-plugin-csharp-lang-ide/src/main/java/org/eclipse/che/plugin/csharp/ide/CSharpLanguageDescriptionProvider.java @@ -0,0 +1,31 @@ +/* + * 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.csharp.ide; + +import static java.util.Arrays.asList; + +import javax.inject.Provider; +import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; + +public class CSharpLanguageDescriptionProvider implements Provider { + private static final String LANGUAGE_ID = "csharp"; + private static final String[] EXTENSIONS = new String[] {"cs", "csx"}; + private static final String MIME_TYPE = "text/x-csharp"; + + @Override + public LanguageDescription get() { + LanguageDescription description = new LanguageDescription(); + description.setFileExtensions(asList(EXTENSIONS)); + description.setLanguageId(LANGUAGE_ID); + description.setMimeType(MIME_TYPE); + return description; + } +} diff --git a/plugins/plugin-csharp/che-plugin-csharp-lang-ide/src/main/java/org/eclipse/che/plugin/csharp/ide/inject/CSharpGinModule.java b/plugins/plugin-csharp/che-plugin-csharp-lang-ide/src/main/java/org/eclipse/che/plugin/csharp/ide/inject/CSharpGinModule.java index 285cd632d85..1fb71bba08b 100644 --- a/plugins/plugin-csharp/che-plugin-csharp-lang-ide/src/main/java/org/eclipse/che/plugin/csharp/ide/inject/CSharpGinModule.java +++ b/plugins/plugin-csharp/che-plugin-csharp-lang-ide/src/main/java/org/eclipse/che/plugin/csharp/ide/inject/CSharpGinModule.java @@ -10,14 +10,17 @@ */ package org.eclipse.che.plugin.csharp.ide.inject; +import static com.google.gwt.inject.client.multibindings.GinMultibinder.newSetBinder; + import com.google.gwt.inject.client.AbstractGinModule; -import com.google.gwt.inject.client.multibindings.GinMultibinder; import com.google.inject.Provides; import com.google.inject.Singleton; import com.google.inject.name.Named; +import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; import org.eclipse.che.ide.api.extension.ExtensionGinModule; import org.eclipse.che.ide.api.filetypes.FileType; import org.eclipse.che.ide.api.project.type.wizard.ProjectWizardRegistrar; +import org.eclipse.che.plugin.csharp.ide.CSharpLanguageDescriptionProvider; import org.eclipse.che.plugin.csharp.ide.CSharpResources; import org.eclipse.che.plugin.csharp.ide.project.CSharpProjectWizardRegistrar; import org.eclipse.che.plugin.csharp.shared.Constants; @@ -29,9 +32,13 @@ public class CSharpGinModule extends AbstractGinModule { /** {@inheritDoc} */ @Override protected void configure() { - GinMultibinder.newSetBinder(binder(), ProjectWizardRegistrar.class) + newSetBinder(binder(), ProjectWizardRegistrar.class) .addBinding() .to(CSharpProjectWizardRegistrar.class); + + newSetBinder(binder(), LanguageDescription.class) + .addBinding() + .toProvider(CSharpLanguageDescriptionProvider.class); } @Provides diff --git a/plugins/plugin-csharp/che-plugin-csharp-lang-server/pom.xml b/plugins/plugin-csharp/che-plugin-csharp-lang-server/pom.xml index 907146641ae..c96704fc3ae 100644 --- a/plugins/plugin-csharp/che-plugin-csharp-lang-server/pom.xml +++ b/plugins/plugin-csharp/che-plugin-csharp-lang-server/pom.xml @@ -36,6 +36,10 @@ com.google.inject.extensions guice-multibindings + + javax.annotation + javax.annotation-api + javax.inject javax.inject @@ -48,10 +52,6 @@ org.eclipse.che.core che-core-api-languageserver - - org.eclipse.che.core - che-core-api-languageserver-shared - org.eclipse.che.core che-core-api-project @@ -68,14 +68,6 @@ org.eclipse.che.plugin che-plugin-csharp-lang-shared - - org.eclipse.lsp4j - org.eclipse.lsp4j - - - org.eclipse.lsp4j - org.eclipse.lsp4j.jsonrpc - org.slf4j slf4j-api diff --git a/plugins/plugin-csharp/che-plugin-csharp-lang-server/src/main/java/org/eclipse/che/plugin/csharp/inject/CSharpModule.java b/plugins/plugin-csharp/che-plugin-csharp-lang-server/src/main/java/org/eclipse/che/plugin/csharp/inject/CSharpModule.java index c2c13803ca1..18fb164b891 100644 --- a/plugins/plugin-csharp/che-plugin-csharp-lang-server/src/main/java/org/eclipse/che/plugin/csharp/inject/CSharpModule.java +++ b/plugins/plugin-csharp/che-plugin-csharp-lang-server/src/main/java/org/eclipse/che/plugin/csharp/inject/CSharpModule.java @@ -10,16 +10,15 @@ */ package org.eclipse.che.plugin.csharp.inject; -import static java.util.Arrays.asList; +import static com.google.inject.multibindings.MapBinder.newMapBinder; import com.google.inject.AbstractModule; import com.google.inject.multibindings.Multibinder; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; +import org.eclipse.che.api.languageserver.LanguageServerConfig; import org.eclipse.che.api.project.server.handlers.ProjectHandler; import org.eclipse.che.api.project.server.type.ProjectTypeDef; import org.eclipse.che.inject.DynaModule; -import org.eclipse.che.plugin.csharp.languageserver.CSharpLanguageServerLauncher; +import org.eclipse.che.plugin.csharp.languageserver.CSharpLanguageServerConfig; import org.eclipse.che.plugin.csharp.projecttype.CSharpProjectType; import org.eclipse.che.plugin.csharp.projecttype.CreateNetCoreProjectHandler; @@ -27,8 +26,6 @@ @DynaModule public class CSharpModule extends AbstractModule { public static final String LANGUAGE_ID = "csharp"; - private static final String[] EXTENSIONS = new String[] {"cs", "csx"}; - private static final String MIME_TYPE = "text/x-csharp"; @Override protected void configure() { @@ -40,17 +37,9 @@ protected void configure() { Multibinder.newSetBinder(binder(), ProjectHandler.class); projectHandlersMultibinder.addBinding().to(CreateNetCoreProjectHandler.class); - Multibinder.newSetBinder(binder(), LanguageServerLauncher.class) - .addBinding() - .to(CSharpLanguageServerLauncher.class); - - LanguageDescription description = new LanguageDescription(); - description.setFileExtensions(asList(EXTENSIONS)); - description.setLanguageId(LANGUAGE_ID); - description.setMimeType(MIME_TYPE); - - Multibinder.newSetBinder(binder(), LanguageDescription.class) - .addBinding() - .toInstance(description); + newMapBinder(binder(), String.class, LanguageServerConfig.class) + .addBinding("org.eclipse.che.plugin.csharp.languageserver") + .to(CSharpLanguageServerConfig.class) + .asEagerSingleton(); } } diff --git a/plugins/plugin-csharp/che-plugin-csharp-lang-server/src/main/java/org/eclipse/che/plugin/csharp/languageserver/CSharpLanguageServerConfig.java b/plugins/plugin-csharp/che-plugin-csharp-lang-server/src/main/java/org/eclipse/che/plugin/csharp/languageserver/CSharpLanguageServerConfig.java new file mode 100644 index 00000000000..c20a44959d7 --- /dev/null +++ b/plugins/plugin-csharp/che-plugin-csharp-lang-server/src/main/java/org/eclipse/che/plugin/csharp/languageserver/CSharpLanguageServerConfig.java @@ -0,0 +1,148 @@ +/* + * 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.csharp.languageserver; + +import static org.eclipse.che.api.fs.server.WsPathUtils.ROOT; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.Set; +import javax.annotation.PostConstruct; +import org.eclipse.che.api.core.notification.EventService; +import org.eclipse.che.api.fs.server.PathTransformer; +import org.eclipse.che.api.languageserver.DefaultInstanceProvider; +import org.eclipse.che.api.languageserver.LanguageServerConfig; +import org.eclipse.che.api.languageserver.LanguageServerException; +import org.eclipse.che.api.languageserver.LanguageServerInitializedEvent; +import org.eclipse.che.api.languageserver.ProcessCommunicationProvider; +import org.eclipse.che.commons.lang.IoUtil; +import org.eclipse.che.plugin.csharp.inject.CSharpModule; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** @author Evgen Vidolob */ +@Singleton +public class CSharpLanguageServerConfig implements LanguageServerConfig { + private static final Logger LOG = LoggerFactory.getLogger(CSharpLanguageServerConfig.class); + + private static final String REGEX = ".*\\.(cs|csx)"; + + private final EventService eventService; + private final PathTransformer pathTransformer; + + private final Path launchScript; + + @Inject + public CSharpLanguageServerConfig(EventService eventService, PathTransformer pathTransformer) { + this.eventService = eventService; + this.pathTransformer = pathTransformer; + + this.launchScript = Paths.get(System.getenv("HOME"), "che/ls-csharp/launch.sh"); + } + + @PostConstruct + private void subscribe() { + eventService.subscribe(this::onLSProxyInitialized, LanguageServerInitializedEvent.class); + } + + private void onLSProxyInitialized(LanguageServerInitializedEvent event) { + try { + if ("org.eclipse.che.plugin.csharp.languageserver".equals(event.getId())) { + restoreDependencies(pathTransformer.transform(ROOT)); + } + } catch (LanguageServerException e) { + LOG.error(e.getMessage(), e); + } + } + + private void restoreDependencies(Path workspaceRootFsPath) throws LanguageServerException { + File[] files = workspaceRootFsPath.toFile().listFiles(); + if (files == null) { + LOG.error("Something went wrong while listing workspace projects"); + return; + } + + for (File file : files) { + if (file.isDirectory()) { + if (!file.toPath().resolve("dotnet").toFile().exists()) { + LOG.warn("An executable 'dotnet' is not present at '{}'", file.toPath().toString()); + return; + } + + ProcessBuilder processBuilder = new ProcessBuilder("dotnet", "restore"); + processBuilder.directory(file); + try { + Process process = processBuilder.start(); + int resultCode = process.waitFor(); + if (resultCode != 0) { + String err = IoUtil.readStream(process.getErrorStream()); + String in = IoUtil.readStream(process.getInputStream()); + throw new LanguageServerException( + "Can't restore dependencies. Error: " + err + ". Output: " + in); + } + } catch (IOException | InterruptedException e) { + throw new LanguageServerException("Can't start CSharp language server", e); + } + } + } + } + + @Override + public RegexProvider getRegexpProvider() { + return new RegexProvider() { + @Override + public Map getLanguageRegexes() { + return ImmutableMap.of(CSharpModule.LANGUAGE_ID, REGEX); + } + + @Override + public Set getFileWatchPatterns() { + return ImmutableSet.of(); + } + }; + } + + @Override + public CommunicationProvider getCommunicationProvider() { + ProcessBuilder processBuilder = new ProcessBuilder(launchScript.toString()); + processBuilder.redirectInput(ProcessBuilder.Redirect.PIPE); + processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE); + + return new ProcessCommunicationProvider(processBuilder, CSharpModule.LANGUAGE_ID); + } + + @Override + public InstanceProvider getInstanceProvider() { + return DefaultInstanceProvider.getInstance(); + } + + @Override + public InstallerStatusProvider getInstallerStatusProvider() { + return new InstallerStatusProvider() { + @Override + public boolean isSuccessfullyInstalled() { + return launchScript.toFile().exists(); + } + + @Override + public String getCause() { + return isSuccessfullyInstalled() ? null : "Launch script file does not exist"; + } + }; + } +} diff --git a/plugins/plugin-csharp/che-plugin-csharp-lang-server/src/main/java/org/eclipse/che/plugin/csharp/languageserver/CSharpLanguageServerLauncher.java b/plugins/plugin-csharp/che-plugin-csharp-lang-server/src/main/java/org/eclipse/che/plugin/csharp/languageserver/CSharpLanguageServerLauncher.java deleted file mode 100644 index a7afb01b13e..00000000000 --- a/plugins/plugin-csharp/che-plugin-csharp-lang-server/src/main/java/org/eclipse/che/plugin/csharp/languageserver/CSharpLanguageServerLauncher.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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.csharp.languageserver; - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Collections; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncherTemplate; -import org.eclipse.che.api.languageserver.registry.DocumentFilter; -import org.eclipse.che.api.languageserver.registry.LanguageServerDescription; -import org.eclipse.che.api.languageserver.service.LanguageServiceUtils; -import org.eclipse.che.commons.lang.IoUtil; -import org.eclipse.che.plugin.csharp.inject.CSharpModule; -import org.eclipse.lsp4j.jsonrpc.Launcher; -import org.eclipse.lsp4j.services.LanguageClient; -import org.eclipse.lsp4j.services.LanguageServer; - -/** @author Evgen Vidolob */ -@Singleton -public class CSharpLanguageServerLauncher extends LanguageServerLauncherTemplate { - private static final String REGEX = ".*\\.(cs|csx)"; - - private static final LanguageServerDescription DESCRIPTION = createServerDescription(); - - private final Path launchScript; - - @Inject - public CSharpLanguageServerLauncher() { - launchScript = Paths.get(System.getenv("HOME"), "che/ls-csharp/launch.sh"); - } - - @Override - protected Process startLanguageServerProcess(String projectPath) throws LanguageServerException { - restoreDependencies(projectPath); - - ProcessBuilder processBuilder = new ProcessBuilder(launchScript.toString()); - processBuilder.redirectInput(ProcessBuilder.Redirect.PIPE); - processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE); - - try { - return processBuilder.start(); - } catch (IOException e) { - throw new LanguageServerException("Can't start CSharp language server", e); - } - } - - private void restoreDependencies(String projectPath) throws LanguageServerException { - ProcessBuilder processBuilder = new ProcessBuilder("dotnet", "restore"); - processBuilder.directory(new File(LanguageServiceUtils.removeUriScheme(projectPath))); - try { - Process process = processBuilder.start(); - int resultCode = process.waitFor(); - if (resultCode != 0) { - String err = IoUtil.readStream(process.getErrorStream()); - String in = IoUtil.readStream(process.getInputStream()); - throw new LanguageServerException( - "Can't restore dependencies. Error: " + err + ". Output: " + in); - } - } catch (IOException | InterruptedException e) { - throw new LanguageServerException("Can't start CSharp language server", e); - } - } - - @Override - protected LanguageServer connectToLanguageServer( - final Process languageServerProcess, LanguageClient client) { - Launcher launcher = - Launcher.createLauncher( - client, - LanguageServer.class, - languageServerProcess.getInputStream(), - languageServerProcess.getOutputStream()); - launcher.startListening(); - return launcher.getRemoteProxy(); - } - - @Override - public LanguageServerDescription getDescription() { - return DESCRIPTION; - } - - private static LanguageServerDescription createServerDescription() { - LanguageServerDescription description = - new LanguageServerDescription( - "org.eclipse.che.plugin.csharp.languageserver", - null, - Collections.singletonList(new DocumentFilter(CSharpModule.LANGUAGE_ID, REGEX, null))); - return description; - } - - @Override - public boolean isAbleToLaunch() { - return Files.exists(launchScript); - } -} diff --git a/plugins/plugin-json/che-plugin-json-server/pom.xml b/plugins/plugin-json/che-plugin-json-server/pom.xml index 3a3e45f2978..522334363ac 100644 --- a/plugins/plugin-json/che-plugin-json-server/pom.xml +++ b/plugins/plugin-json/che-plugin-json-server/pom.xml @@ -24,6 +24,10 @@ false + + com.google.guava + guava + com.google.inject guice @@ -33,20 +37,20 @@ guice-multibindings - org.eclipse.che.core - che-core-api-languageserver + javax.annotation + javax.annotation-api org.eclipse.che.core - che-core-api-languageserver-shared + che-core-api-core org.eclipse.che.core - che-core-commons-inject + che-core-api-languageserver - org.eclipse.lsp4j - org.eclipse.lsp4j + org.eclipse.che.core + che-core-commons-inject org.eclipse.lsp4j diff --git a/plugins/plugin-json/che-plugin-json-server/src/main/java/org/eclipse/che/plugin/json/inject/JsonModule.java b/plugins/plugin-json/che-plugin-json-server/src/main/java/org/eclipse/che/plugin/json/inject/JsonModule.java index 35602e3fe7a..72f11982a3a 100644 --- a/plugins/plugin-json/che-plugin-json-server/src/main/java/org/eclipse/che/plugin/json/inject/JsonModule.java +++ b/plugins/plugin-json/che-plugin-json-server/src/main/java/org/eclipse/che/plugin/json/inject/JsonModule.java @@ -10,34 +10,23 @@ */ package org.eclipse.che.plugin.json.inject; -import static java.util.Arrays.asList; +import static com.google.inject.multibindings.MapBinder.newMapBinder; import com.google.inject.AbstractModule; -import com.google.inject.multibindings.Multibinder; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; +import org.eclipse.che.api.languageserver.LanguageServerConfig; import org.eclipse.che.inject.DynaModule; -import org.eclipse.che.plugin.json.languageserver.JsonLanguageServerLauncher; +import org.eclipse.che.plugin.json.languageserver.JsonLanguageServerConfig; /** @author Anatolii Bazko */ @DynaModule public class JsonModule extends AbstractModule { public static final String LANGUAGE_ID = "json"; - private static final String[] EXTENSIONS = - new String[] {"json", "bowerrc", "jshintrc", "jscsrc", "eslintrc", "babelrc"}; - private static final String MIME_TYPE = "application/json"; @Override protected void configure() { - Multibinder.newSetBinder(binder(), LanguageServerLauncher.class) - .addBinding() - .to(JsonLanguageServerLauncher.class); - LanguageDescription description = new LanguageDescription(); - description.setFileExtensions(asList(EXTENSIONS)); - description.setLanguageId(LANGUAGE_ID); - description.setMimeType(MIME_TYPE); - Multibinder.newSetBinder(binder(), LanguageDescription.class) - .addBinding() - .toInstance(description); + newMapBinder(binder(), String.class, LanguageServerConfig.class) + .addBinding("org.eclipse.che.plugin.json.languageserver") + .to(JsonLanguageServerConfig.class) + .asEagerSingleton(); } } diff --git a/plugins/plugin-json/che-plugin-json-server/src/main/java/org/eclipse/che/plugin/json/languageserver/JsonLanguageServerConfig.java b/plugins/plugin-json/che-plugin-json-server/src/main/java/org/eclipse/che/plugin/json/languageserver/JsonLanguageServerConfig.java new file mode 100644 index 00000000000..dc96e15ce05 --- /dev/null +++ b/plugins/plugin-json/che-plugin-json-server/src/main/java/org/eclipse/che/plugin/json/languageserver/JsonLanguageServerConfig.java @@ -0,0 +1,116 @@ +/* + * 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.json.languageserver; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.Set; +import javax.annotation.PostConstruct; +import org.eclipse.che.api.core.notification.EventService; +import org.eclipse.che.api.languageserver.DefaultInstanceProvider; +import org.eclipse.che.api.languageserver.LanguageServerConfig; +import org.eclipse.che.api.languageserver.LanguageServerInitializedEvent; +import org.eclipse.che.api.languageserver.ProcessCommunicationProvider; +import org.eclipse.che.plugin.json.inject.JsonModule; +import org.eclipse.lsp4j.jsonrpc.Endpoint; +import org.eclipse.lsp4j.jsonrpc.services.ServiceEndpoints; + +/** + * @author Evgen Vidolob + * @author Anatolii Bazko + */ +@Singleton +public class JsonLanguageServerConfig implements LanguageServerConfig { + private static final String REGEX = ".*\\.(json|bowerrc|jshintrc|jscsrc|eslintrc|babelrc)"; + + private final EventService eventService; + private final Path launchScript; + + @Inject + public JsonLanguageServerConfig(EventService eventService) { + this.eventService = eventService; + + this.launchScript = Paths.get(System.getenv("HOME"), "che/ls-json/launch.sh"); + } + + @PostConstruct + private void subscribe() { + eventService.subscribe(this::onLanguageServerInitialized, LanguageServerInitializedEvent.class); + } + + private void onLanguageServerInitialized(LanguageServerInitializedEvent event) { + Endpoint endpoint = ServiceEndpoints.toEndpoint(event.getLanguageServer()); + JsonExtension serviceObject = ServiceEndpoints.toServiceObject(endpoint, JsonExtension.class); + + Map associations = + ImmutableMap.builder() + .put("/*.schema.json", new String[] {"http://json-schema.org/draft-04/schema#"}) + .put("/bower.json", new String[] {"http://json.schemastore.org/bower"}) + .put("/.bower.json", new String[] {"http://json.schemastore.org/bower"}) + .put("/.bowerrc", new String[] {"http://json.schemastore.org/bowerrc"}) + .put("/composer.json", new String[] {"https://getcomposer.org/schema.json"}) + .put("/package.json", new String[] {"http://json.schemastore.org/package"}) + .put("/jsconfig.json", new String[] {"http://json.schemastore.org/jsconfig"}) + .put("/tsconfig.json", new String[] {"http://json.schemastore.org/tsconfig"}) + .build(); + + serviceObject.jsonSchemaAssociation(associations); + } + + @Override + public RegexProvider getRegexpProvider() { + return new RegexProvider() { + @Override + public Map getLanguageRegexes() { + return ImmutableMap.of(JsonModule.LANGUAGE_ID, REGEX); + } + + @Override + public Set getFileWatchPatterns() { + return ImmutableSet.of(); + } + }; + } + + @Override + public CommunicationProvider getCommunicationProvider() { + ProcessBuilder processBuilder = new ProcessBuilder(launchScript.toString()); + processBuilder.redirectInput(ProcessBuilder.Redirect.PIPE); + processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE); + + return new ProcessCommunicationProvider(processBuilder, JsonModule.LANGUAGE_ID); + } + + @Override + public InstanceProvider getInstanceProvider() { + return DefaultInstanceProvider.getInstance(); + } + + @Override + public InstallerStatusProvider getInstallerStatusProvider() { + return new InstallerStatusProvider() { + @Override + public boolean isSuccessfullyInstalled() { + return launchScript.toFile().exists(); + } + + @Override + public String getCause() { + return isSuccessfullyInstalled() ? null : "Launch script file does not exist"; + } + }; + } +} diff --git a/plugins/plugin-json/che-plugin-json-server/src/main/java/org/eclipse/che/plugin/json/languageserver/JsonLanguageServerLauncher.java b/plugins/plugin-json/che-plugin-json-server/src/main/java/org/eclipse/che/plugin/json/languageserver/JsonLanguageServerLauncher.java deleted file mode 100644 index 7cb0f1a29c6..00000000000 --- a/plugins/plugin-json/che-plugin-json-server/src/main/java/org/eclipse/che/plugin/json/languageserver/JsonLanguageServerLauncher.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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.json.languageserver; - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncherTemplate; -import org.eclipse.che.api.languageserver.registry.DocumentFilter; -import org.eclipse.che.api.languageserver.registry.LanguageServerDescription; -import org.eclipse.che.api.languageserver.registry.ServerInitializerObserver; -import org.eclipse.che.plugin.json.inject.JsonModule; -import org.eclipse.lsp4j.ServerCapabilities; -import org.eclipse.lsp4j.jsonrpc.Endpoint; -import org.eclipse.lsp4j.jsonrpc.Launcher; -import org.eclipse.lsp4j.jsonrpc.services.ServiceEndpoints; -import org.eclipse.lsp4j.services.LanguageClient; -import org.eclipse.lsp4j.services.LanguageServer; - -/** - * @author Evgen Vidolob - * @author Anatolii Bazko - */ -@Singleton -public class JsonLanguageServerLauncher extends LanguageServerLauncherTemplate - implements ServerInitializerObserver { - private static final String REGEX = ".*\\.(json|bowerrc|jshintrc|jscsrc|eslintrc|babelrc)"; - private static final LanguageServerDescription DESCRIPTION = createServerDescription(); - - private final Path launchScript; - - @Inject - public JsonLanguageServerLauncher() { - launchScript = Paths.get(System.getenv("HOME"), "che/ls-json/launch.sh"); - } - - @Override - public boolean isAbleToLaunch() { - return Files.exists(launchScript); - } - - protected LanguageServer connectToLanguageServer( - final Process languageServerProcess, LanguageClient client) { - Launcher launcher = - Launcher.createLauncher( - client, - LanguageServer.class, - languageServerProcess.getInputStream(), - languageServerProcess.getOutputStream()); - launcher.startListening(); - return launcher.getRemoteProxy(); - } - - protected Process startLanguageServerProcess(String projectPath) throws LanguageServerException { - ProcessBuilder processBuilder = new ProcessBuilder(launchScript.toString()); - processBuilder.redirectInput(ProcessBuilder.Redirect.PIPE); - processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE); - try { - return processBuilder.start(); - } catch (IOException e) { - throw new LanguageServerException("Can't start JSON language server", e); - } - } - - @Override - public void onServerInitialized( - LanguageServerLauncher launcher, - LanguageServer server, - ServerCapabilities capabilities, - String projectPath) { - Endpoint endpoint = ServiceEndpoints.toEndpoint(server); - JsonExtension serviceObject = ServiceEndpoints.toServiceObject(endpoint, JsonExtension.class); - Map associations = new HashMap<>(); - associations.put("/*.schema.json", new String[] {"http://json-schema.org/draft-04/schema#"}); - associations.put("/bower.json", new String[] {"http://json.schemastore.org/bower"}); - associations.put("/.bower.json", new String[] {"http://json.schemastore.org/bower"}); - associations.put("/.bowerrc", new String[] {"http://json.schemastore.org/bowerrc"}); - associations.put("/composer.json", new String[] {"https://getcomposer.org/schema.json"}); - associations.put("/package.json", new String[] {"http://json.schemastore.org/package"}); - associations.put("/jsconfig.json", new String[] {"http://json.schemastore.org/jsconfig"}); - associations.put("/tsconfig.json", new String[] {"http://json.schemastore.org/tsconfig"}); - serviceObject.jsonSchemaAssociation(associations); - } - - public LanguageServerDescription getDescription() { - return DESCRIPTION; - } - - private static LanguageServerDescription createServerDescription() { - LanguageServerDescription description = - new LanguageServerDescription( - "org.eclipse.che.plugin.json.languageserver", - null, - Arrays.asList(new DocumentFilter(JsonModule.LANGUAGE_ID, REGEX, null))); - return description; - } -} diff --git a/plugins/plugin-languageserver/che-plugin-languageserver-ide/pom.xml b/plugins/plugin-languageserver/che-plugin-languageserver-ide/pom.xml index 86c006de1e6..72d3764f8bf 100644 --- a/plugins/plugin-languageserver/che-plugin-languageserver-ide/pom.xml +++ b/plugins/plugin-languageserver/che-plugin-languageserver-ide/pom.xml @@ -221,6 +221,7 @@ org.eclipse.lsp4j org.eclipse.che.api.languageserver.shared.event org.eclipse.che.api.languageserver.shared.model + org.eclipse.che.api.languageserver.shared.dto ${dto-generator-out-directory} org.eclipse.che.api.languageserver.shared.dto.DtoClientImpls diff --git a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/LanguageDescriptionInitializer.java b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/LanguageDescriptionInitializer.java new file mode 100644 index 00000000000..dc515576c69 --- /dev/null +++ b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/LanguageDescriptionInitializer.java @@ -0,0 +1,83 @@ +/* + * 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; + +import com.google.gwt.core.client.JsArrayString; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import java.util.Set; +import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; +import org.eclipse.che.ide.editor.orion.client.OrionContentTypeRegistrant; +import org.eclipse.che.ide.editor.orion.client.OrionHoverRegistrant; +import org.eclipse.che.ide.editor.orion.client.OrionOccurrencesRegistrant; +import org.eclipse.che.ide.editor.orion.client.jso.OrionContentTypeOverlay; +import org.eclipse.che.ide.editor.orion.client.jso.OrionHighlightingConfigurationOverlay; +import org.eclipse.che.plugin.languageserver.ide.highlighting.OccurrencesProvider; +import org.eclipse.che.plugin.languageserver.ide.hover.HoverProvider; + +/** @author Evgen Vidolob */ +@Singleton +public class LanguageDescriptionInitializer { + + private final OrionContentTypeRegistrant contentTypeRegistrant; + private final OrionHoverRegistrant orionHoverRegistrant; + private final OrionOccurrencesRegistrant orionOccurrencesRegistrant; + private final OccurrencesProvider occurrencesProvider; + private final Set languageDescriptions; + private final HoverProvider hoverProvider; + + @Inject + public LanguageDescriptionInitializer( + OrionContentTypeRegistrant contentTypeRegistrant, + OrionHoverRegistrant orionHoverRegistrant, + OrionOccurrencesRegistrant orionOccurrencesRegistrant, + HoverProvider hoverProvider, + OccurrencesProvider occurrencesProvider, + Set languageDescriptions) { + this.contentTypeRegistrant = contentTypeRegistrant; + this.orionHoverRegistrant = orionHoverRegistrant; + this.orionOccurrencesRegistrant = orionOccurrencesRegistrant; + this.hoverProvider = hoverProvider; + this.occurrencesProvider = occurrencesProvider; + this.languageDescriptions = languageDescriptions; + } + + void initialize() { + JsArrayString contentTypes = JsArrayString.createArray().cast(); + + for (LanguageDescription languageDescription : languageDescriptions) { + + String mimeType = languageDescription.getMimeType(); + contentTypes.push(mimeType); + OrionContentTypeOverlay contentType = OrionContentTypeOverlay.create(); + contentType.setId(mimeType); + contentType.setName(languageDescription.getLanguageId()); + contentType.setFileName( + languageDescription + .getFileNames() + .toArray(new String[languageDescription.getFileNames().size()])); + contentType.setExtension( + languageDescription + .getFileExtensions() + .toArray(new String[languageDescription.getFileExtensions().size()])); + contentType.setExtends("text/plain"); + + // highlighting + OrionHighlightingConfigurationOverlay config = OrionHighlightingConfigurationOverlay.create(); + config.setId(languageDescription.getLanguageId() + ".highlighting"); + config.setContentTypes(mimeType); + config.setPatterns(languageDescription.getHighlightingConfiguration()); + contentTypeRegistrant.registerFileType(contentType, config); + } + orionHoverRegistrant.registerHover(contentTypes, hoverProvider); + orionOccurrencesRegistrant.registerOccurrencesHandler(contentTypes, occurrencesProvider); + } +} diff --git a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/LanguageRegexesInitializer.java b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/LanguageRegexesInitializer.java new file mode 100644 index 00000000000..03df791e470 --- /dev/null +++ b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/LanguageRegexesInitializer.java @@ -0,0 +1,91 @@ +/* + * 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; + +import com.google.gwt.regexp.shared.RegExp; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import org.eclipse.che.ide.api.editor.EditorRegistry; +import org.eclipse.che.ide.api.filetypes.FileType; +import org.eclipse.che.ide.api.filetypes.FileTypeRegistry; +import org.eclipse.che.plugin.languageserver.ide.editor.LanguageServerEditorProvider; +import org.eclipse.che.plugin.languageserver.ide.registry.LanguageServerRegistry; +import org.eclipse.che.plugin.languageserver.ide.service.LanguageServerServiceClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Singleton +public class LanguageRegexesInitializer { + private static Logger LOGGER = LoggerFactory.getLogger(LanguageRegexesInitializer.class); + + private final LanguageServerRegistry lsRegistry; + private final LanguageServerResources resources; + private final EditorRegistry editorRegistry; + private final LanguageServerEditorProvider editorProvider; + private final LanguageServerServiceClient languageServerServiceClient; + private final FileTypeRegistry fileTypeRegistry; + + @Inject + public LanguageRegexesInitializer( + LanguageServerRegistry lsRegistry, + LanguageServerResources resources, + EditorRegistry editorRegistry, + LanguageServerEditorProvider editorProvider, + LanguageServerServiceClient languageServerServiceClient, + FileTypeRegistry fileTypeRegistry) { + this.lsRegistry = lsRegistry; + this.resources = resources; + this.editorRegistry = editorRegistry; + this.editorProvider = editorProvider; + this.languageServerServiceClient = languageServerServiceClient; + this.fileTypeRegistry = fileTypeRegistry; + } + + void initialize() { + languageServerServiceClient + .getLanguageRegexes() + .then( + languageRegexes -> { + languageRegexes.forEach( + languageRegex -> { + String namePattern = languageRegex.getNamePattern(); + + FileType fileTypeCandidate = null; + for (FileType fileType : fileTypeRegistry.getRegisteredFileTypes()) { + String extension = fileType.getExtension(); + if (extension != null && RegExp.compile(namePattern).test('.' + extension)) { + fileTypeCandidate = fileType; + } + + String namePatternCandidate = fileType.getNamePattern(); + if ((namePattern.equals(namePatternCandidate) + || RegExp.quote(namePattern).equals(namePatternCandidate))) { + fileTypeCandidate = fileType; + } + } + + if (fileTypeCandidate == null) { + fileTypeCandidate = new FileType(resources.file(), null, namePattern); + fileTypeRegistry.registerFileType(fileTypeCandidate); + } else { + fileTypeCandidate.setNamePattern(namePattern); + } + + lsRegistry.registerFileType(fileTypeCandidate, languageRegex); + editorRegistry.registerDefaultEditor(fileTypeCandidate, editorProvider); + }); + }) + .catchError( + promiseError -> { + LOGGER.error("Error", promiseError.getCause()); + }); + } +} 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 7c4862146b3..484e79bd1d2 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 @@ -55,7 +55,8 @@ public class LanguageServerExtension { @Inject public LanguageServerExtension( - LanguageServerFileTypeRegister languageServerFileTypeRegister, + LanguageRegexesInitializer languageRegexesInitializer, + LanguageDescriptionInitializer languageDescriptionInitializer, EventBus eventBus, AppContext appContext, ShowMessageJsonRpcReceiver showMessageJsonRpcReceiver, @@ -63,13 +64,15 @@ public LanguageServerExtension( eventBus.addHandler( WsAgentServerRunningEvent.TYPE, e -> { - languageServerFileTypeRegister.start(); + languageRegexesInitializer.initialize(); + languageDescriptionInitializer.initialize(); showMessageJsonRpcReceiver.subscribe(); publishDiagnosticsReceiver.subscribe(); }); if (appContext.getWorkspace().getStatus() == RUNNING) { - languageServerFileTypeRegister.start(); + languageRegexesInitializer.initialize(); + languageDescriptionInitializer.initialize(); showMessageJsonRpcReceiver.subscribe(); publishDiagnosticsReceiver.subscribe(); } @@ -156,7 +159,7 @@ protected void registerFileEventHandler( FileEvent.TYPE, event -> { Path location = event.getFile().getLocation(); - if (lsRegistry.getLanguageDescription(event.getFile()) == null) { + if (lsRegistry.getLanguageFilter(event.getFile()) == null) { return; } final TextDocumentIdentifier documentId = @@ -209,7 +212,7 @@ private void onOpen( documentItem.setVersion(LanguageServerEditorConfiguration.INITIAL_DOCUMENT_VERSION); documentItem.setText(text); documentItem.setLanguageId( - lsRegistry.getLanguageDescription(event.getFile()).getLanguageId()); + lsRegistry.getLanguageFilter(event.getFile()).getLanguageId()); DidOpenTextDocumentParams openEvent = dtoFactory.createDto(DidOpenTextDocumentParams.class); diff --git a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/LanguageServerFileTypeRegister.java b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/LanguageServerFileTypeRegister.java deleted file mode 100644 index 986f721dce3..00000000000 --- a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/LanguageServerFileTypeRegister.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * 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; - -import com.google.gwt.core.client.JsArrayString; -import com.google.gwt.regexp.shared.RegExp; -import com.google.inject.Inject; -import com.google.inject.Singleton; -import java.util.List; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; -import org.eclipse.che.api.promises.client.Operation; -import org.eclipse.che.api.promises.client.OperationException; -import org.eclipse.che.api.promises.client.Promise; -import org.eclipse.che.api.promises.client.PromiseError; -import org.eclipse.che.ide.api.editor.EditorRegistry; -import org.eclipse.che.ide.api.filetypes.FileType; -import org.eclipse.che.ide.editor.orion.client.OrionContentTypeRegistrant; -import org.eclipse.che.ide.editor.orion.client.OrionHoverRegistrant; -import org.eclipse.che.ide.editor.orion.client.OrionOccurrencesRegistrant; -import org.eclipse.che.ide.editor.orion.client.jso.OrionContentTypeOverlay; -import org.eclipse.che.ide.editor.orion.client.jso.OrionHighlightingConfigurationOverlay; -import org.eclipse.che.ide.util.loging.Log; -import org.eclipse.che.plugin.languageserver.ide.editor.LanguageServerEditorProvider; -import org.eclipse.che.plugin.languageserver.ide.highlighting.OccurrencesProvider; -import org.eclipse.che.plugin.languageserver.ide.hover.HoverProvider; -import org.eclipse.che.plugin.languageserver.ide.registry.LanguageServerRegistry; -import org.eclipse.che.plugin.languageserver.ide.service.LanguageServerRegistryServiceClient; - -/** @author Evgen Vidolob */ -@Singleton -public class LanguageServerFileTypeRegister { - - private final LanguageServerRegistryServiceClient serverLanguageRegistry; - private final LanguageServerRegistry lsRegistry; - private final LanguageServerResources resources; - private final EditorRegistry editorRegistry; - private final OrionContentTypeRegistrant contentTypeRegistrant; - private final OrionHoverRegistrant orionHoverRegistrant; - private final OrionOccurrencesRegistrant orionOccurrencesRegistrant; - private final LanguageServerEditorProvider editorProvider; - private final HoverProvider hoverProvider; - private final OccurrencesProvider occurrencesProvider; - - @Inject - public LanguageServerFileTypeRegister( - LanguageServerRegistryServiceClient serverLanguageRegistry, - LanguageServerRegistry lsRegistry, - LanguageServerResources resources, - EditorRegistry editorRegistry, - OrionContentTypeRegistrant contentTypeRegistrant, - OrionHoverRegistrant orionHoverRegistrant, - OrionOccurrencesRegistrant orionOccurrencesRegistrant, - LanguageServerEditorProvider editorProvider, - HoverProvider hoverProvider, - OccurrencesProvider occurrencesProvider) { - this.serverLanguageRegistry = serverLanguageRegistry; - this.lsRegistry = lsRegistry; - this.resources = resources; - this.editorRegistry = editorRegistry; - this.contentTypeRegistrant = contentTypeRegistrant; - this.orionHoverRegistrant = orionHoverRegistrant; - this.orionOccurrencesRegistrant = orionOccurrencesRegistrant; - this.editorProvider = editorProvider; - this.hoverProvider = hoverProvider; - this.occurrencesProvider = occurrencesProvider; - } - - void start() { - Promise> registeredLanguages = - serverLanguageRegistry.getSupportedLanguages(); - registeredLanguages - .then( - new Operation>() { - @Override - public void apply(List langs) throws OperationException { - if (!langs.isEmpty()) { - JsArrayString contentTypes = JsArrayString.createArray().cast(); - for (LanguageDescription lang : langs) { - for (String ext : lang.getFileExtensions()) { - final FileType fileType = new FileType(resources.file(), ext); - lsRegistry.registerFileType(fileType, lang); - editorRegistry.registerDefaultEditor(fileType, editorProvider); - } - for (String fileName : lang.getFileNames()) { - final FileType fileType = - new FileType(resources.file(), null, RegExp.quote(fileName)); - lsRegistry.registerFileType(fileType, lang); - editorRegistry.registerDefaultEditor(fileType, editorProvider); - } - String mimeType = lang.getMimeType(); - contentTypes.push(mimeType); - OrionContentTypeOverlay contentType = OrionContentTypeOverlay.create(); - contentType.setId(mimeType); - contentType.setName(lang.getLanguageId()); - contentType.setFileName( - lang.getFileNames().toArray(new String[lang.getFileNames().size()])); - contentType.setExtension( - lang.getFileExtensions() - .toArray(new String[lang.getFileExtensions().size()])); - contentType.setExtends("text/plain"); - - // highlighting - OrionHighlightingConfigurationOverlay config = - OrionHighlightingConfigurationOverlay.create(); - config.setId(lang.getLanguageId() + ".highlighting"); - config.setContentTypes(mimeType); - config.setPatterns(lang.getHighlightingConfiguration()); - contentTypeRegistrant.registerFileType(contentType, config); - } - orionHoverRegistrant.registerHover(contentTypes, hoverProvider); - orionOccurrencesRegistrant.registerOccurrencesHandler( - contentTypes, occurrencesProvider); - } - } - }) - .catchError( - new Operation() { - @Override - public void apply(PromiseError arg) throws OperationException { - Log.error(LanguageServerFileTypeRegister.this.getClass(), arg.getCause()); - } - }); - } -} diff --git a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/editor/LanguageServerEditorProvider.java b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/editor/LanguageServerEditorProvider.java index 992d0740529..1d1b5569a5e 100644 --- a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/editor/LanguageServerEditorProvider.java +++ b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/editor/LanguageServerEditorProvider.java @@ -12,7 +12,6 @@ import javax.inject.Inject; import org.eclipse.che.api.promises.client.Function; -import org.eclipse.che.api.promises.client.FunctionException; import org.eclipse.che.api.promises.client.Promise; import org.eclipse.che.ide.api.editor.AsyncEditorProvider; import org.eclipse.che.ide.api.editor.EditorPartPresenter; @@ -72,33 +71,29 @@ public TextEditor getEditor() { @Override public Promise createEditor(VirtualFile file) { - if (file instanceof File) { - File resource = (File) file; + if (!(file instanceof File)) { + return null; + } - Promise promise = - registry.getOrInitializeServer(resource.getProject().getPath(), file); - return promise.then( - new Function() { - @Override - public EditorPartPresenter apply(ServerCapabilities capabilities) - throws FunctionException { - if (editorBuilder == null) { - Log.debug( - AbstractTextEditorProvider.class, - "No builder registered for default editor type - giving up."); - return null; - } + return registry + .getOrInitializeServer(file) + .then( + (Function) + capabilities -> { + if (editorBuilder == null) { + Log.debug( + AbstractTextEditorProvider.class, + "No builder registered for default editor type - giving up."); + return null; + } - final TextEditor editor = editorBuilder.buildEditor(); - TextEditorConfiguration configuration = - capabilities == null - ? new DefaultTextEditorConfiguration() - : editorConfigurationFactory.build(editor, capabilities); - editor.initialize(configuration); - return editor; - } - }); - } - return null; + final TextEditor editor = editorBuilder.buildEditor(); + TextEditorConfiguration configuration = + capabilities == null + ? new DefaultTextEditorConfiguration() + : editorConfigurationFactory.build(editor, capabilities); + editor.initialize(configuration); + return editor; + }); } } diff --git a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/registry/LanguageServerRegistry.java b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/registry/LanguageServerRegistry.java index 325f3e1cc96..823180ccfbb 100644 --- a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/registry/LanguageServerRegistry.java +++ b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/registry/LanguageServerRegistry.java @@ -15,63 +15,64 @@ import com.google.inject.Inject; import com.google.inject.Singleton; -import com.google.web.bindery.event.shared.EventBus; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; +import org.eclipse.che.api.languageserver.shared.model.LanguageRegex; import org.eclipse.che.api.promises.client.Promise; +import org.eclipse.che.api.promises.client.PromiseProvider; import org.eclipse.che.ide.api.filetypes.FileType; import org.eclipse.che.ide.api.filetypes.FileTypeRegistry; import org.eclipse.che.ide.api.notification.NotificationManager; import org.eclipse.che.ide.api.resources.VirtualFile; import org.eclipse.che.ide.ui.loaders.request.LoaderFactory; import org.eclipse.che.ide.ui.loaders.request.MessageLoader; -import org.eclipse.che.plugin.languageserver.ide.service.LanguageServerRegistryJsonRpcClient; -import org.eclipse.che.plugin.languageserver.ide.service.LanguageServerRegistryServiceClient; +import org.eclipse.che.plugin.languageserver.ide.service.LanguageServerServiceClient; import org.eclipse.lsp4j.ServerCapabilities; /** @author Anatoliy Bazko */ @Singleton public class LanguageServerRegistry { - private final LanguageServerRegistryJsonRpcClient jsonRpcClient; private LoaderFactory loaderFactory; private NotificationManager notificationManager; - private final Map registeredFileTypes = new ConcurrentHashMap<>(); + private final Map registeredFileTypes = new ConcurrentHashMap<>(); private final FileTypeRegistry fileTypeRegistry; + private final LanguageServerServiceClient languageServerServiceClient; + private final PromiseProvider promiseProvider; @Inject public LanguageServerRegistry( - EventBus eventBus, LoaderFactory loaderFactory, NotificationManager notificationManager, - LanguageServerRegistryJsonRpcClient jsonRpcClient, - LanguageServerRegistryServiceClient client, - FileTypeRegistry fileTypeRegistry) { + FileTypeRegistry fileTypeRegistry, + LanguageServerServiceClient languageServerServiceClient, + PromiseProvider promiseProvider) { this.loaderFactory = loaderFactory; this.notificationManager = notificationManager; - this.jsonRpcClient = jsonRpcClient; this.fileTypeRegistry = fileTypeRegistry; + this.languageServerServiceClient = languageServerServiceClient; + this.promiseProvider = promiseProvider; } - public Promise getOrInitializeServer(String projectPath, VirtualFile file) { + public Promise getOrInitializeServer(VirtualFile file) { // call initialize service final MessageLoader loader = loaderFactory.newLoader("Initializing Language Server for " + file.getName()); loader.show(); - return jsonRpcClient - .initializeServer(file.getLocation().toString()) - .then( - (ServerCapabilities arg) -> { + String wsPath = file.getLocation().toString(); + return languageServerServiceClient + .initialize(wsPath) + .thenPromise( + serverCapabilities -> { loader.hide(); - return arg; + return promiseProvider.resolve(serverCapabilities); }) .catchError( - arg -> { + promiseError -> { notificationManager.notify( "Initializing Language Server for " + file.getName(), - arg.getMessage(), + promiseError.getMessage(), FAIL, EMERGE_MODE); loader.hide(); @@ -85,8 +86,7 @@ public Promise getOrInitializeServer(String projectPath, Vir * @param type * @param description */ - public void registerFileType(FileType type, LanguageDescription description) { - fileTypeRegistry.registerFileType(type); + public void registerFileType(FileType type, LanguageRegex description) { registeredFileTypes.put(type, description); } @@ -96,7 +96,7 @@ public void registerFileType(FileType type, LanguageDescription description) { * @param file * @return */ - public LanguageDescription getLanguageDescription(VirtualFile file) { + public LanguageRegex getLanguageFilter(VirtualFile file) { FileType fileType = fileTypeRegistry.getFileTypeByFile(file); if (fileType == null) { return null; diff --git a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/LanguageServerRegistryServiceClient.java b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/LanguageServerRegistryServiceClient.java deleted file mode 100644 index 88ff2baf3c6..00000000000 --- a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/LanguageServerRegistryServiceClient.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.MimeType.APPLICATION_JSON; -import static org.eclipse.che.ide.rest.HTTPHeader.ACCEPT; - -import com.google.inject.Inject; -import java.util.List; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; -import org.eclipse.che.api.promises.client.Promise; -import org.eclipse.che.ide.api.app.AppContext; -import org.eclipse.che.ide.rest.AsyncRequestFactory; -import org.eclipse.che.ide.rest.DtoUnmarshallerFactory; - -/** - * @author Sven Efftinge - * @author Anatolii Bazko - */ -public class LanguageServerRegistryServiceClient { - - public static final String BASE_URI = "/languageserver"; - - private final DtoUnmarshallerFactory unmarshallerFactory; - private final AsyncRequestFactory asyncRequestFactory; - private final AppContext appContext; - - @Inject - public LanguageServerRegistryServiceClient( - DtoUnmarshallerFactory unmarshallerFactory, - AppContext appContext, - AsyncRequestFactory asyncRequestFactory) { - this.unmarshallerFactory = unmarshallerFactory; - this.appContext = appContext; - this.asyncRequestFactory = asyncRequestFactory; - } - - /** @return all supported languages */ - public Promise> getSupportedLanguages() { - String requestUrl = appContext.getWsAgentServerApiEndpoint() + BASE_URI + "/supported"; - return asyncRequestFactory - .createGetRequest(requestUrl) - .header(ACCEPT, APPLICATION_JSON) - .send(unmarshallerFactory.newListUnmarshaller(LanguageDescription.class)); - } -} diff --git a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/LanguageServerRegistryJsonRpcClient.java b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/LanguageServerServiceClient.java similarity index 59% rename from plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/LanguageServerRegistryJsonRpcClient.java rename to plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/LanguageServerServiceClient.java index 48dba4292f1..d559b4aae23 100644 --- a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/LanguageServerRegistryJsonRpcClient.java +++ b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/LanguageServerServiceClient.java @@ -12,55 +12,54 @@ import static org.eclipse.che.ide.api.jsonrpc.Constants.WS_AGENT_JSON_RPC_ENDPOINT_ID; -import com.google.inject.Inject; -import com.google.inject.Singleton; +import java.util.List; import java.util.concurrent.TimeoutException; +import javax.inject.Inject; +import javax.inject.Singleton; import org.eclipse.che.api.core.jsonrpc.commons.JsonRpcError; import org.eclipse.che.api.core.jsonrpc.commons.JsonRpcException; import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter; +import org.eclipse.che.api.languageserver.shared.model.LanguageRegex; import org.eclipse.che.api.promises.client.Promise; import org.eclipse.che.api.promises.client.PromiseError; import org.eclipse.che.api.promises.client.js.Promises; import org.eclipse.lsp4j.ServerCapabilities; @Singleton -public class LanguageServerRegistryJsonRpcClient { +public class LanguageServerServiceClient { private final RequestTransmitter requestTransmitter; @Inject - public LanguageServerRegistryJsonRpcClient(RequestTransmitter requestTransmitter) { + public LanguageServerServiceClient(RequestTransmitter requestTransmitter) { this.requestTransmitter = requestTransmitter; } - public Promise initializeServer(String path) { + public Promise initialize(String wsPath) { return Promises.create( (resolve, reject) -> requestTransmitter .newRequest() .endpointId(WS_AGENT_JSON_RPC_ENDPOINT_ID) .methodName("languageServer/initialize") - .paramsAsString(path) - .sendAndReceiveResultAsDto(ServerCapabilities.class, 30000) + .paramsAsString(wsPath) + .sendAndReceiveResultAsDto(ServerCapabilities.class, 30_000) .onSuccess(resolve::apply) .onFailure(error -> reject.apply(getPromiseError(error))) - .onTimeout( - () -> { - final TimeoutException e = new TimeoutException(); - reject.apply( - new PromiseError() { - - @Override - public String getMessage() { - return "Timeout initializing error"; - } + .onTimeout(() -> reject.apply(getPromiseError()))); + } - @Override - public Throwable getCause() { - return e; - } - }); - })); + public Promise> getLanguageRegexes() { + return Promises.create( + (resolve, reject) -> + requestTransmitter + .newRequest() + .endpointId(WS_AGENT_JSON_RPC_ENDPOINT_ID) + .methodName("languageServer/getLanguageRegexes") + .noParams() + .sendAndReceiveResultAsListOfDto(LanguageRegex.class) + .onSuccess(resolve::apply) + .onFailure(error -> reject.apply(getPromiseError(error)))); } private PromiseError getPromiseError(JsonRpcError jsonRpcError) { @@ -76,4 +75,19 @@ public Throwable getCause() { } }; } + + private PromiseError getPromiseError() { + return new PromiseError() { + + @Override + public String getMessage() { + return "Timeout initializing error"; + } + + @Override + public Throwable getCause() { + return new TimeoutException(); + } + }; + } } diff --git a/plugins/plugin-languageserver/che-plugin-test-ls/pom.xml b/plugins/plugin-languageserver/che-plugin-test-ls/pom.xml index 5be94cc9693..49279c695e1 100644 --- a/plugins/plugin-languageserver/che-plugin-test-ls/pom.xml +++ b/plugins/plugin-languageserver/che-plugin-test-ls/pom.xml @@ -21,6 +21,10 @@ che-plugin-test-ls-server Che Plugin :: TEST LS :: Extension Server + + com.google.guava + guava + com.google.inject guice @@ -30,24 +34,16 @@ guice-multibindings - org.eclipse.che.core - che-core-api-languageserver + javax.inject + javax.inject org.eclipse.che.core - che-core-api-languageserver-shared + che-core-api-languageserver org.eclipse.che.core che-core-commons-inject - - org.eclipse.lsp4j - org.eclipse.lsp4j - - - org.eclipse.lsp4j - org.eclipse.lsp4j.jsonrpc - diff --git a/plugins/plugin-languageserver/che-plugin-test-ls/src/main/java/org/eclipse/che/plugin/test/ls/inject/TestLSModule.java b/plugins/plugin-languageserver/che-plugin-test-ls/src/main/java/org/eclipse/che/plugin/test/ls/inject/TestLSModule.java index be5e688a6ea..146c244a8f9 100644 --- a/plugins/plugin-languageserver/che-plugin-test-ls/src/main/java/org/eclipse/che/plugin/test/ls/inject/TestLSModule.java +++ b/plugins/plugin-languageserver/che-plugin-test-ls/src/main/java/org/eclipse/che/plugin/test/ls/inject/TestLSModule.java @@ -10,33 +10,23 @@ */ package org.eclipse.che.plugin.test.ls.inject; -import static java.util.Arrays.asList; +import static com.google.inject.multibindings.MapBinder.newMapBinder; import com.google.inject.AbstractModule; -import com.google.inject.multibindings.Multibinder; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; +import org.eclipse.che.api.languageserver.LanguageServerConfig; import org.eclipse.che.inject.DynaModule; -import org.eclipse.che.plugin.test.ls.languageserver.TestLanguageServerLauncher; +import org.eclipse.che.plugin.test.ls.languageserver.TestLanguageServerConfig; /** */ @DynaModule public class TestLSModule extends AbstractModule { public static final String LANGUAGE_ID = "testLS"; - private static final String[] EXTENSIONS = new String[] {"test"}; - private static final String MIME_TYPE = "application/ls-test"; @Override protected void configure() { - Multibinder.newSetBinder(binder(), LanguageServerLauncher.class) - .addBinding() - .to(TestLanguageServerLauncher.class); - LanguageDescription description = new LanguageDescription(); - description.setFileExtensions(asList(EXTENSIONS)); - description.setLanguageId(LANGUAGE_ID); - description.setMimeType(MIME_TYPE); - Multibinder.newSetBinder(binder(), LanguageDescription.class) - .addBinding() - .toInstance(description); + newMapBinder(binder(), String.class, LanguageServerConfig.class) + .addBinding("org.eclipse.che.plugin.test.languageserver") + .to(TestLanguageServerConfig.class) + .asEagerSingleton(); } } diff --git a/plugins/plugin-languageserver/che-plugin-test-ls/src/main/java/org/eclipse/che/plugin/test/ls/languageserver/TestLanguageServerConfig.java b/plugins/plugin-languageserver/che-plugin-test-ls/src/main/java/org/eclipse/che/plugin/test/ls/languageserver/TestLanguageServerConfig.java new file mode 100644 index 00000000000..13bded7e2d7 --- /dev/null +++ b/plugins/plugin-languageserver/che-plugin-test-ls/src/main/java/org/eclipse/che/plugin/test/ls/languageserver/TestLanguageServerConfig.java @@ -0,0 +1,83 @@ +/* + * 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.test.ls.languageserver; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.Set; +import javax.inject.Inject; +import javax.inject.Singleton; +import org.eclipse.che.api.languageserver.DefaultInstanceProvider; +import org.eclipse.che.api.languageserver.LanguageServerConfig; +import org.eclipse.che.api.languageserver.ProcessCommunicationProvider; +import org.eclipse.che.plugin.test.ls.inject.TestLSModule; + +/** + * Launcher for simple test Language server + * + * @author Evgen Vidolob + */ +@Singleton +public class TestLanguageServerConfig implements LanguageServerConfig { + private final Path launchScript; + + @Inject + public TestLanguageServerConfig() { + launchScript = Paths.get(System.getenv("HOME"), "che/test-ls/launch.sh"); + } + + @Override + public RegexProvider getRegexpProvider() { + return new RegexProvider() { + @Override + public Map getLanguageRegexes() { + return ImmutableMap.of(TestLSModule.LANGUAGE_ID, "_"); + } + + @Override + public Set getFileWatchPatterns() { + return ImmutableSet.of(); + } + }; + } + + @Override + public CommunicationProvider getCommunicationProvider() { + ProcessBuilder processBuilder = new ProcessBuilder(launchScript.toString()); + processBuilder.redirectInput(ProcessBuilder.Redirect.PIPE); + processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE); + + return new ProcessCommunicationProvider(processBuilder, TestLSModule.LANGUAGE_ID); + } + + @Override + public InstanceProvider getInstanceProvider() { + return DefaultInstanceProvider.getInstance(); + } + + @Override + public InstallerStatusProvider getInstallerStatusProvider() { + return new InstallerStatusProvider() { + @Override + public boolean isSuccessfullyInstalled() { + return launchScript.toFile().exists(); + } + + @Override + public String getCause() { + return isSuccessfullyInstalled() ? null : "Launch script file does not exist"; + } + }; + } +} diff --git a/plugins/plugin-languageserver/che-plugin-test-ls/src/main/java/org/eclipse/che/plugin/test/ls/languageserver/TestLanguageServerLauncher.java b/plugins/plugin-languageserver/che-plugin-test-ls/src/main/java/org/eclipse/che/plugin/test/ls/languageserver/TestLanguageServerLauncher.java deleted file mode 100644 index 6c6ef34bbbb..00000000000 --- a/plugins/plugin-languageserver/che-plugin-test-ls/src/main/java/org/eclipse/che/plugin/test/ls/languageserver/TestLanguageServerLauncher.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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.test.ls.languageserver; - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Collections; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncherTemplate; -import org.eclipse.che.api.languageserver.registry.DocumentFilter; -import org.eclipse.che.api.languageserver.registry.LanguageServerDescription; -import org.eclipse.che.plugin.test.ls.inject.TestLSModule; -import org.eclipse.lsp4j.jsonrpc.Launcher; -import org.eclipse.lsp4j.services.LanguageClient; -import org.eclipse.lsp4j.services.LanguageServer; - -/** - * Launcher for simple test Language server - * - * @author Evgen Vidolob - */ -@Singleton -public class TestLanguageServerLauncher extends LanguageServerLauncherTemplate { - private static final LanguageServerDescription DESCRIPTION = createServerDescription(); - - private final Path launchScript; - - @Inject - public TestLanguageServerLauncher() { - launchScript = Paths.get(System.getenv("HOME"), "che/test-ls/launch.sh"); - } - - private static LanguageServerDescription createServerDescription() { - return new LanguageServerDescription( - "org.eclipse.che.plugin.test.languageserver", - null, - Collections.singletonList(new DocumentFilter(TestLSModule.LANGUAGE_ID, null, null))); - } - - @Override - public boolean isAbleToLaunch() { - return Files.exists(launchScript); - } - - protected LanguageServer connectToLanguageServer( - final Process languageServerProcess, LanguageClient client) { - Launcher launcher = - Launcher.createLauncher( - client, - LanguageServer.class, - languageServerProcess.getInputStream(), - languageServerProcess.getOutputStream()); - launcher.startListening(); - return launcher.getRemoteProxy(); - } - - protected Process startLanguageServerProcess(String projectPath) throws LanguageServerException { - ProcessBuilder processBuilder = new ProcessBuilder(launchScript.toString()); - processBuilder.redirectInput(ProcessBuilder.Redirect.PIPE); - processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE); - try { - return processBuilder.start(); - } catch (IOException e) { - throw new LanguageServerException("Can't start Test language server", e); - } - } - - public LanguageServerDescription getDescription() { - return DESCRIPTION; - } -} diff --git a/plugins/plugin-maven/che-plugin-maven-ide/pom.xml b/plugins/plugin-maven/che-plugin-maven-ide/pom.xml index 34463b279bf..488e8ad78f7 100644 --- a/plugins/plugin-maven/che-plugin-maven-ide/pom.xml +++ b/plugins/plugin-maven/che-plugin-maven-ide/pom.xml @@ -53,6 +53,10 @@ org.eclipse.che.core che-core-api-core + + org.eclipse.che.core + che-core-api-languageserver-shared + org.eclipse.che.core che-core-api-project-shared diff --git a/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/MavenExtension.java b/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/MavenExtension.java index 74d97556644..951c060e8bd 100644 --- a/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/MavenExtension.java +++ b/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/MavenExtension.java @@ -118,7 +118,7 @@ private void registerFileType( FileTypeRegistry fileTypeRegistry, MavenResources mavenResources, EditorRegistry editorRegistry) { - FileType pomFile = new FileType(mavenResources.maven(), null, "pom\\.xml"); + FileType pomFile = new FileType(mavenResources.maven(), null, ".*[/\\\\]?pom\\.xml$"); fileTypeRegistry.registerFileType(pomFile); } } diff --git a/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/MavenLanguageDescriptionProvider.java b/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/MavenLanguageDescriptionProvider.java new file mode 100644 index 00000000000..ea39e8c8654 --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/MavenLanguageDescriptionProvider.java @@ -0,0 +1,27 @@ +/* + * 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.maven.client; + +import com.google.common.collect.ImmutableList; +import javax.inject.Provider; +import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; + +public class MavenLanguageDescriptionProvider implements Provider { + + @Override + public LanguageDescription get() { + LanguageDescription description = new LanguageDescription(); + description.setLanguageId("pom"); + description.setMimeType("application/pom"); + description.setFileNames(ImmutableList.of("pom.xml")); + return description; + } +} diff --git a/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/TestLSLanguageDescriptionProvider.java b/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/TestLSLanguageDescriptionProvider.java new file mode 100644 index 00000000000..60f51bd7cee --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/TestLSLanguageDescriptionProvider.java @@ -0,0 +1,31 @@ +/* + * 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.maven.client; + +import static java.util.Arrays.asList; + +import com.google.inject.Provider; +import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; + +public class TestLSLanguageDescriptionProvider implements Provider { + private static final String LANGUAGE_ID = "testLS"; + private static final String[] EXTENSIONS = new String[] {"test"}; + private static final String MIME_TYPE = "application/ls-test"; + + @Override + public LanguageDescription get() { + LanguageDescription description = new LanguageDescription(); + description.setFileExtensions(asList(EXTENSIONS)); + description.setLanguageId(LANGUAGE_ID); + description.setMimeType(MIME_TYPE); + return description; + } +} diff --git a/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/inject/MavenGinModule.java b/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/inject/MavenGinModule.java index 704c663cd8f..08d0cd54c62 100644 --- a/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/inject/MavenGinModule.java +++ b/plugins/plugin-maven/che-plugin-maven-ide/src/main/java/org/eclipse/che/plugin/maven/client/inject/MavenGinModule.java @@ -10,14 +10,17 @@ */ package org.eclipse.che.plugin.maven.client.inject; +import static com.google.gwt.inject.client.multibindings.GinMultibinder.newSetBinder; + import com.google.gwt.inject.client.AbstractGinModule; -import com.google.gwt.inject.client.multibindings.GinMultibinder; +import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; import org.eclipse.che.ide.api.command.CommandType; import org.eclipse.che.ide.api.extension.ExtensionGinModule; import org.eclipse.che.ide.api.preferences.PreferencePagePresenter; import org.eclipse.che.ide.api.project.type.wizard.ProjectWizardRegistrar; import org.eclipse.che.ide.api.resources.ResourceInterceptor; import org.eclipse.che.ide.project.ResolvingProjectStateHolder; +import org.eclipse.che.plugin.maven.client.MavenLanguageDescriptionProvider; import org.eclipse.che.plugin.maven.client.command.MavenCommandType; import org.eclipse.che.plugin.maven.client.preference.MavenPreferencePresenter; import org.eclipse.che.plugin.maven.client.project.ResolvingMavenProjectStateHolder; @@ -37,30 +40,30 @@ public class MavenGinModule extends AbstractGinModule { @Override protected void configure() { - GinMultibinder.newSetBinder(binder(), ProjectWizardRegistrar.class) + newSetBinder(binder(), ProjectWizardRegistrar.class) .addBinding() .to(MavenProjectWizardRegistrar.class); - GinMultibinder.newSetBinder(binder(), CommandType.class) - .addBinding() - .to(MavenCommandType.class); + newSetBinder(binder(), CommandType.class).addBinding().to(MavenCommandType.class); - GinMultibinder.newSetBinder(binder(), PreferencePagePresenter.class) + newSetBinder(binder(), PreferencePagePresenter.class) .addBinding() .to(MavenPreferencePresenter.class); - GinMultibinder.newSetBinder(binder(), ResourceInterceptor.class) + newSetBinder(binder(), ResourceInterceptor.class) .addBinding() .to(MavenSourceFolderInterceptor.class); - GinMultibinder.newSetBinder(binder(), ResourceInterceptor.class) - .addBinding() - .to(PomInterceptor.class); - GinMultibinder.newSetBinder(binder(), ResourceInterceptor.class) + newSetBinder(binder(), ResourceInterceptor.class).addBinding().to(PomInterceptor.class); + newSetBinder(binder(), ResourceInterceptor.class) .addBinding() .to(MavenProjectInterceptor.class); - GinMultibinder.newSetBinder(binder(), ResolvingProjectStateHolder.class) + newSetBinder(binder(), ResolvingProjectStateHolder.class) .addBinding() .to(ResolvingMavenProjectStateHolder.class); + + newSetBinder(binder(), LanguageDescription.class) + .addBinding() + .toProvider(MavenLanguageDescriptionProvider.class); } } diff --git a/plugins/plugin-maven/che-plugin-maven-server/pom.xml b/plugins/plugin-maven/che-plugin-maven-server/pom.xml index 6482caed50b..2af6dea2437 100644 --- a/plugins/plugin-maven/che-plugin-maven-server/pom.xml +++ b/plugins/plugin-maven/che-plugin-maven-server/pom.xml @@ -66,10 +66,6 @@ org.eclipse.che.core che-core-api-languageserver - - org.eclipse.che.core - che-core-api-languageserver-shared - org.eclipse.che.core che-core-api-project diff --git a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/lsp/MavenLanguageServer.java b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/lsp/MavenLanguageServer.java index 473d73ad19b..c720005e0d8 100644 --- a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/lsp/MavenLanguageServer.java +++ b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/lsp/MavenLanguageServer.java @@ -11,12 +11,12 @@ package org.eclipse.che.plugin.maven.lsp; import java.util.concurrent.CompletableFuture; -import org.eclipse.che.plugin.maven.server.core.reconcile.PomReconciler; +import javax.inject.Inject; +import javax.inject.Singleton; import org.eclipse.lsp4j.InitializeParams; import org.eclipse.lsp4j.InitializeResult; import org.eclipse.lsp4j.ServerCapabilities; import org.eclipse.lsp4j.TextDocumentSyncKind; -import org.eclipse.lsp4j.services.LanguageClient; import org.eclipse.lsp4j.services.LanguageServer; import org.eclipse.lsp4j.services.TextDocumentService; import org.eclipse.lsp4j.services.WorkspaceService; @@ -27,13 +27,14 @@ * * @author Thomas Mäder */ +@Singleton public class MavenLanguageServer implements LanguageServer { - private MavenTextDocumentService textDocumentService; - private PomReconciler reconciler; - public MavenLanguageServer(LanguageClient client, PomReconciler reconciler) { - this.textDocumentService = new MavenTextDocumentService(reconciler); - this.reconciler = reconciler; + private final MavenTextDocumentService textDocumentService; + + @Inject + public MavenLanguageServer(MavenTextDocumentService textDocumentService) { + this.textDocumentService = textDocumentService; } @Override @@ -60,8 +61,4 @@ public TextDocumentService getTextDocumentService() { @Override public void exit() {} - - public void reconcile(String pomPath, String projectPath) { - reconciler.reconcilePath(pomPath, projectPath); - } } diff --git a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/lsp/MavenLanguageServerConfig.java b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/lsp/MavenLanguageServerConfig.java new file mode 100644 index 00000000000..460e2c9b9a4 --- /dev/null +++ b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/lsp/MavenLanguageServerConfig.java @@ -0,0 +1,72 @@ +/* + * 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.maven.lsp; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import java.util.Map; +import java.util.Set; +import org.eclipse.che.api.languageserver.EmptyCommunicationProvider; +import org.eclipse.che.api.languageserver.LanguageServerConfig; +import org.eclipse.lsp4j.services.LanguageServer; + +@Singleton +public class MavenLanguageServerConfig implements LanguageServerConfig { + + private final LanguageServer mavenLanguageServer; + + @Inject + public MavenLanguageServerConfig(MavenLanguageServer mavenLanguageServer) { + this.mavenLanguageServer = mavenLanguageServer; + } + + @Override + public RegexProvider getRegexpProvider() { + return new RegexProvider() { + @Override + public Map getLanguageRegexes() { + return ImmutableMap.of("maven", ".*[/\\\\]?pom\\.xml$"); + } + + @Override + public Set getFileWatchPatterns() { + return ImmutableSet.of(); + } + }; + } + + @Override + public CommunicationProvider getCommunicationProvider() { + return EmptyCommunicationProvider.getInstance(); + } + + @Override + public InstanceProvider getInstanceProvider() { + return (client, in, out) -> mavenLanguageServer; + } + + @Override + public InstallerStatusProvider getInstallerStatusProvider() { + return new InstallerStatusProvider() { + @Override + public boolean isSuccessfullyInstalled() { + return true; + } + + @Override + public String getCause() { + return null; + } + }; + } +} diff --git a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/lsp/MavenLanguageServerLauncher.java b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/lsp/MavenLanguageServerLauncher.java deleted file mode 100644 index a7d0fdd90a4..00000000000 --- a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/lsp/MavenLanguageServerLauncher.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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.maven.lsp; - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import java.util.Collections; -import org.eclipse.che.api.core.notification.EventService; -import org.eclipse.che.api.editor.server.impl.EditorWorkingCopyManager; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.registry.LanguageServerDescription; -import org.eclipse.che.plugin.maven.server.core.MavenProjectManager; -import org.eclipse.che.plugin.maven.server.core.reconcile.PomReconciler; -import org.eclipse.lsp4j.services.LanguageClient; -import org.eclipse.lsp4j.services.LanguageServer; - -@Singleton -public class MavenLanguageServerLauncher implements LanguageServerLauncher { - - @Inject - public MavenLanguageServerLauncher( - MavenProjectManager mavenProjectManager, - EditorWorkingCopyManager editorWorkingCopyManager, - EventService eventService) { - this.mavenProjectManager = mavenProjectManager; - this.editorWorkingCopyManager = editorWorkingCopyManager; - this.eventService = eventService; - } - - private MavenProjectManager mavenProjectManager; - private EditorWorkingCopyManager editorWorkingCopyManager; - private EventService eventService; - - public LanguageServer launch(String projectPath, LanguageClient client) - throws LanguageServerException { - PomReconciler reconciler = - new PomReconciler(mavenProjectManager, editorWorkingCopyManager, eventService, client); - return new MavenLanguageServer(client, reconciler); - } - - public boolean isAbleToLaunch() { - return true; - } - - @Override - public LanguageServerDescription getDescription() { - return new LanguageServerDescription( - "org.eclipse.che.plugin.maven", Collections.singletonList("pom"), Collections.emptyList()); - } -} diff --git a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/lsp/MavenTextDocumentService.java b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/lsp/MavenTextDocumentService.java index bdf4831d92d..b9ad265392a 100644 --- a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/lsp/MavenTextDocumentService.java +++ b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/lsp/MavenTextDocumentService.java @@ -12,6 +12,8 @@ import java.util.List; import java.util.concurrent.CompletableFuture; +import javax.inject.Inject; +import javax.inject.Singleton; import org.eclipse.che.plugin.maven.server.core.reconcile.PomReconciler; import org.eclipse.lsp4j.CodeActionParams; import org.eclipse.lsp4j.CodeLens; @@ -40,9 +42,11 @@ import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.services.TextDocumentService; +@Singleton public class MavenTextDocumentService implements TextDocumentService { private PomReconciler reconciler; + @Inject public MavenTextDocumentService(PomReconciler reconciler) { this.reconciler = reconciler; } diff --git a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/reconcile/PomReconciler.java b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/reconcile/PomReconciler.java index 601fcc43002..a1b6cffa6d5 100644 --- a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/reconcile/PomReconciler.java +++ b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/core/reconcile/PomReconciler.java @@ -9,15 +9,6 @@ * Red Hat, Inc. - initial API and implementation */ package org.eclipse.che.plugin.maven.server.core.reconcile; -/** - * ***************************************************************************** Copyright (c) - * 2012-2017 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 - * ***************************************************************************** - */ import static com.google.common.base.Strings.isNullOrEmpty; import static java.lang.String.format; @@ -31,10 +22,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.SortedSet; import java.util.TreeSet; import java.util.stream.Collectors; import javax.annotation.PreDestroy; +import javax.inject.Inject; +import javax.inject.Singleton; import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.ServerException; import org.eclipse.che.api.core.notification.EventService; @@ -42,7 +36,8 @@ import org.eclipse.che.api.editor.server.impl.EditorWorkingCopy; import org.eclipse.che.api.editor.server.impl.EditorWorkingCopyManager; import org.eclipse.che.api.editor.server.impl.EditorWorkingCopyUpdatedEvent; -import org.eclipse.che.api.languageserver.service.LanguageServiceUtils; +import org.eclipse.che.api.languageserver.CheLanguageClientFactory; +import org.eclipse.che.api.languageserver.LanguageServiceUtils; import org.eclipse.che.api.project.shared.dto.EditorChangesDto; import org.eclipse.che.commons.xml.XMLTreeException; import org.eclipse.che.dto.server.DtoFactory; @@ -73,6 +68,7 @@ * * @author Roman Nikitenko */ +@Singleton public class PomReconciler { private static final Logger LOG = LoggerFactory.getLogger(PomReconciler.class); @@ -82,15 +78,16 @@ public class PomReconciler { private EventSubscriber editorContentUpdateEventSubscriber; private LanguageClient client; + @Inject public PomReconciler( MavenProjectManager mavenProjectManager, EditorWorkingCopyManager editorWorkingCopyManager, EventService eventService, - LanguageClient client) { + CheLanguageClientFactory cheLanguageClientFactory) { this.mavenProjectManager = mavenProjectManager; this.editorWorkingCopyManager = editorWorkingCopyManager; this.eventService = eventService; - this.client = client; + this.client = cheLanguageClientFactory.create("maven-language-server"); editorContentUpdateEventSubscriber = new EventSubscriber() { @@ -144,7 +141,7 @@ List reconcile(String pomPath, String projectPath, String pomContent) result.addAll(problemList); } catch (XMLTreeException exception) { Throwable cause = exception.getCause(); - if (cause != null && cause instanceof SAXParseException) { + if (cause instanceof SAXParseException) { result.add(createProblem(pomContent, (SAXParseException) cause)); } else { @@ -234,13 +231,11 @@ public void reconcileUri(String uri, String text) { private static List convertProblems(String text, List problems) { Map positions = mapPositions(text, problems); - List diagnostics = - problems - .stream() - .map((Problem p) -> convertProblem(positions, p)) - .filter(o -> o != null) - .collect(Collectors.toList()); - return diagnostics; + return problems + .stream() + .map(p -> convertProblem(positions, p)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); } private static Map mapPositions(String text, List problems) { diff --git a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/inject/MavenModule.java b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/inject/MavenModule.java index 6831e1450cf..1b5c5b8b732 100644 --- a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/inject/MavenModule.java +++ b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/inject/MavenModule.java @@ -10,6 +10,7 @@ */ package org.eclipse.che.plugin.maven.server.inject; +import static com.google.inject.multibindings.MapBinder.newMapBinder; import static com.google.inject.multibindings.Multibinder.newSetBinder; import com.google.inject.AbstractModule; @@ -17,16 +18,14 @@ import com.google.inject.multibindings.Multibinder; import com.google.inject.name.Names; import java.nio.file.PathMatcher; -import java.util.Collections; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; +import org.eclipse.che.api.languageserver.LanguageServerConfig; import org.eclipse.che.api.project.server.handlers.ProjectHandler; import org.eclipse.che.api.project.server.type.ProjectTypeDef; import org.eclipse.che.api.project.server.type.ValueProviderFactory; import org.eclipse.che.inject.DynaModule; import org.eclipse.che.maven.server.MavenTerminal; import org.eclipse.che.plugin.maven.generator.archetype.MavenArchetypeJsonRpcMessenger; -import org.eclipse.che.plugin.maven.lsp.MavenLanguageServerLauncher; +import org.eclipse.che.plugin.maven.lsp.MavenLanguageServerConfig; import org.eclipse.che.plugin.maven.server.PomModificationDetector; import org.eclipse.che.plugin.maven.server.core.MavenJsonRpcCommunication; import org.eclipse.che.plugin.maven.server.core.MavenProgressNotifier; @@ -86,18 +85,10 @@ protected void configure() { bind(PomChangeListener.class).asEagerSingleton(); bind(PomModificationDetector.class).asEagerSingleton(); - Multibinder.newSetBinder(binder(), LanguageServerLauncher.class) - .addBinding() - .to(MavenLanguageServerLauncher.class) - .asEagerSingleton(); - ; - LanguageDescription description = new LanguageDescription(); - description.setLanguageId("pom"); - description.setMimeType("application/pom"); - description.setFileNames(Collections.singletonList("pom.xml")); - Multibinder.newSetBinder(binder(), LanguageDescription.class) - .addBinding() - .toInstance(description); + newMapBinder(binder(), String.class, LanguageServerConfig.class) + .addBinding("org.eclipse.che.plugin.maven") + .to(MavenLanguageServerConfig.class) + .asEagerSingleton(); } } diff --git a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/rest/MavenServerService.java b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/rest/MavenServerService.java index fe859ba9750..bfa3b0885b9 100644 --- a/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/rest/MavenServerService.java +++ b/plugins/plugin-maven/che-plugin-maven-server/src/main/java/org/eclipse/che/plugin/maven/server/rest/MavenServerService.java @@ -21,7 +21,6 @@ import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; import java.io.File; -import java.util.Collection; import java.util.List; import java.util.stream.Collectors; import javax.ws.rs.GET; @@ -35,14 +34,9 @@ import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.ServerException; import org.eclipse.che.api.fs.server.FsManager; -import org.eclipse.che.api.fs.server.PathTransformer; -import org.eclipse.che.api.languageserver.registry.InitializedLanguageServer; -import org.eclipse.che.api.languageserver.registry.LanguageServerRegistry; -import org.eclipse.che.api.languageserver.service.LanguageServiceUtils; import org.eclipse.che.api.project.server.ProjectManager; import org.eclipse.che.api.project.server.impl.RegisteredProject; import org.eclipse.che.maven.server.MavenTerminal; -import org.eclipse.che.plugin.maven.lsp.MavenLanguageServer; import org.eclipse.che.plugin.maven.server.MavenServerWrapper; import org.eclipse.che.plugin.maven.server.MavenWrapperManager; import org.eclipse.che.plugin.maven.server.core.EclipseWorkspaceProvider; @@ -50,6 +44,7 @@ import org.eclipse.che.plugin.maven.server.core.MavenProjectManager; import org.eclipse.che.plugin.maven.server.core.MavenWorkspace; import org.eclipse.che.plugin.maven.server.core.classpath.ClasspathManager; +import org.eclipse.che.plugin.maven.server.core.reconcile.PomReconciler; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IWorkspace; @@ -65,8 +60,8 @@ public class MavenServerService { private final ProjectManager projectManager; private final MavenWorkspace mavenWorkspace; private final EclipseWorkspaceProvider eclipseWorkspaceProvider; - private final PathTransformer pathTransformer; private final FsManager fsManager; + private final PomReconciler pomReconciler; @Inject private MavenProgressNotifier notifier; @@ -76,23 +71,21 @@ public class MavenServerService { @Inject private ClasspathManager classpathManager; - @Inject private LanguageServerRegistry lsRegistry; - @Inject public MavenServerService( MavenWrapperManager wrapperManager, ProjectManager projectManager, MavenWorkspace mavenWorkspace, EclipseWorkspaceProvider eclipseWorkspaceProvider, - PathTransformer pathTransformer, - FsManager fsManager) { + FsManager fsManager, + PomReconciler pomReconciler) { this.wrapperManager = wrapperManager; this.projectManager = projectManager; this.mavenWorkspace = mavenWorkspace; this.eclipseWorkspaceProvider = eclipseWorkspaceProvider; - this.pathTransformer = pathTransformer; this.fsManager = fsManager; + this.pomReconciler = pomReconciler; } /** @@ -172,17 +165,6 @@ public void reconcile( String pomPath) throws ForbiddenException, ConflictException, NotFoundException, ServerException { String projectPath = new File(pomPath).getParent(); - List> languageServers = - lsRegistry.getApplicableLanguageServers(LanguageServiceUtils.prefixURI(pomPath)); - languageServers - .stream() - .flatMap(Collection::stream) - .map(InitializedLanguageServer::getServer) - .filter(ls -> ls instanceof MavenLanguageServer) - .findAny() - .ifPresent( - ls -> { - ((MavenLanguageServer) ls).reconcile(pomPath, projectPath); - }); + pomReconciler.reconcilePath(pomPath, projectPath); } } diff --git a/plugins/plugin-php/che-plugin-php-lang-ide/pom.xml b/plugins/plugin-php/che-plugin-php-lang-ide/pom.xml index 481831341a7..e69a15668dc 100644 --- a/plugins/plugin-php/che-plugin-php-lang-ide/pom.xml +++ b/plugins/plugin-php/che-plugin-php-lang-ide/pom.xml @@ -30,10 +30,18 @@ com.google.inject guice + + javax.inject + javax.inject + javax.validation validation-api + + org.eclipse.che.core + che-core-api-languageserver-shared + org.eclipse.che.core che-core-ide-api diff --git a/plugins/plugin-php/che-plugin-php-lang-ide/src/main/java/org/eclipse/che/plugin/php/ide/PhpLanguageDescriptionProvider.java b/plugins/plugin-php/che-plugin-php-lang-ide/src/main/java/org/eclipse/che/plugin/php/ide/PhpLanguageDescriptionProvider.java new file mode 100644 index 00000000000..f39d7da4a92 --- /dev/null +++ b/plugins/plugin-php/che-plugin-php-lang-ide/src/main/java/org/eclipse/che/plugin/php/ide/PhpLanguageDescriptionProvider.java @@ -0,0 +1,32 @@ +/* + * 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.php.ide; + +import static java.util.Arrays.asList; + +import javax.inject.Provider; +import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; + +public class PhpLanguageDescriptionProvider implements Provider { + private static final String LANGUAGE_ID = "php"; + private static final String[] EXTENSIONS = new String[] {"php"}; + private static final String MIME_TYPE = "text/x-php"; + + @Override + public LanguageDescription get() { + LanguageDescription description = new LanguageDescription(); + description.setFileExtensions(asList(EXTENSIONS)); + description.setLanguageId(LANGUAGE_ID); + description.setMimeType(MIME_TYPE); + + return description; + } +} diff --git a/plugins/plugin-php/che-plugin-php-lang-ide/src/main/java/org/eclipse/che/plugin/php/ide/inject/PhpGinModule.java b/plugins/plugin-php/che-plugin-php-lang-ide/src/main/java/org/eclipse/che/plugin/php/ide/inject/PhpGinModule.java index 3f7f539929c..7e9c0aff473 100644 --- a/plugins/plugin-php/che-plugin-php-lang-ide/src/main/java/org/eclipse/che/plugin/php/ide/inject/PhpGinModule.java +++ b/plugins/plugin-php/che-plugin-php-lang-ide/src/main/java/org/eclipse/che/plugin/php/ide/inject/PhpGinModule.java @@ -10,14 +10,17 @@ */ package org.eclipse.che.plugin.php.ide.inject; +import static com.google.gwt.inject.client.multibindings.GinMultibinder.newSetBinder; + import com.google.gwt.inject.client.AbstractGinModule; -import com.google.gwt.inject.client.multibindings.GinMultibinder; import com.google.inject.Provides; import com.google.inject.Singleton; import com.google.inject.name.Named; +import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; import org.eclipse.che.ide.api.extension.ExtensionGinModule; import org.eclipse.che.ide.api.filetypes.FileType; import org.eclipse.che.ide.api.project.type.wizard.ProjectWizardRegistrar; +import org.eclipse.che.plugin.php.ide.PhpLanguageDescriptionProvider; import org.eclipse.che.plugin.php.ide.PhpResources; import org.eclipse.che.plugin.php.ide.project.PhpProjectWizardRegistrar; import org.eclipse.che.plugin.php.shared.Constants; @@ -29,9 +32,13 @@ public class PhpGinModule extends AbstractGinModule { /** {@inheritDoc} */ @Override protected void configure() { - GinMultibinder.newSetBinder(binder(), ProjectWizardRegistrar.class) + newSetBinder(binder(), ProjectWizardRegistrar.class) .addBinding() .to(PhpProjectWizardRegistrar.class); + + newSetBinder(binder(), LanguageDescription.class) + .addBinding() + .toProvider(PhpLanguageDescriptionProvider.class); } @Provides diff --git a/plugins/plugin-php/che-plugin-php-lang-server/pom.xml b/plugins/plugin-php/che-plugin-php-lang-server/pom.xml index 75bc7ffa642..a48e8a50c46 100644 --- a/plugins/plugin-php/che-plugin-php-lang-server/pom.xml +++ b/plugins/plugin-php/che-plugin-php-lang-server/pom.xml @@ -24,6 +24,10 @@ false + + com.google.guava + guava + com.google.inject guice @@ -40,10 +44,6 @@ org.eclipse.che.core che-core-api-languageserver - - org.eclipse.che.core - che-core-api-languageserver-shared - org.eclipse.che.core che-core-api-project @@ -56,13 +56,5 @@ org.eclipse.che.plugin che-plugin-php-lang-shared - - org.eclipse.lsp4j - org.eclipse.lsp4j - - - org.eclipse.lsp4j - org.eclipse.lsp4j.jsonrpc - diff --git a/plugins/plugin-php/che-plugin-php-lang-server/src/main/java/org/eclipse/che/plugin/php/inject/PhpModule.java b/plugins/plugin-php/che-plugin-php-lang-server/src/main/java/org/eclipse/che/plugin/php/inject/PhpModule.java index 27ee383c2ed..0b3644f2214 100644 --- a/plugins/plugin-php/che-plugin-php-lang-server/src/main/java/org/eclipse/che/plugin/php/inject/PhpModule.java +++ b/plugins/plugin-php/che-plugin-php-lang-server/src/main/java/org/eclipse/che/plugin/php/inject/PhpModule.java @@ -10,17 +10,15 @@ */ package org.eclipse.che.plugin.php.inject; +import static com.google.inject.multibindings.MapBinder.newMapBinder; import static com.google.inject.multibindings.Multibinder.newSetBinder; -import static java.util.Arrays.asList; import com.google.inject.AbstractModule; -import com.google.inject.multibindings.Multibinder; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; +import org.eclipse.che.api.languageserver.LanguageServerConfig; import org.eclipse.che.api.project.server.handlers.ProjectHandler; import org.eclipse.che.api.project.server.type.ProjectTypeDef; import org.eclipse.che.inject.DynaModule; -import org.eclipse.che.plugin.php.languageserver.PhpLanguageServerLauncher; +import org.eclipse.che.plugin.php.languageserver.PhpLanguageServerConfig; import org.eclipse.che.plugin.php.projecttype.PhpProjectGenerator; import org.eclipse.che.plugin.php.projecttype.PhpProjectType; @@ -28,28 +26,16 @@ @DynaModule public class PhpModule extends AbstractModule { public static final String LANGUAGE_ID = "php"; - private static final String[] EXTENSIONS = new String[] {"php"}; - private static final String MIME_TYPE = "text/x-php"; @Override protected void configure() { - Multibinder projectTypeMultibinder = - Multibinder.newSetBinder(binder(), ProjectTypeDef.class); - projectTypeMultibinder.addBinding().to(PhpProjectType.class); + newSetBinder(binder(), ProjectTypeDef.class).addBinding().to(PhpProjectType.class); - Multibinder projectHandlerMultibinder = - newSetBinder(binder(), ProjectHandler.class); - projectHandlerMultibinder.addBinding().to(PhpProjectGenerator.class); + newSetBinder(binder(), ProjectHandler.class).addBinding().to(PhpProjectGenerator.class); - Multibinder.newSetBinder(binder(), LanguageServerLauncher.class) - .addBinding() - .to(PhpLanguageServerLauncher.class); - LanguageDescription description = new LanguageDescription(); - description.setFileExtensions(asList(EXTENSIONS)); - description.setLanguageId(LANGUAGE_ID); - description.setMimeType(MIME_TYPE); - Multibinder.newSetBinder(binder(), LanguageDescription.class) - .addBinding() - .toInstance(description); + newMapBinder(binder(), String.class, LanguageServerConfig.class) + .addBinding("org.eclipse.che.plugin.php.languageserver") + .to(PhpLanguageServerConfig.class) + .asEagerSingleton(); } } diff --git a/plugins/plugin-php/che-plugin-php-lang-server/src/main/java/org/eclipse/che/plugin/php/languageserver/PhpLanguageServerConfig.java b/plugins/plugin-php/che-plugin-php-lang-server/src/main/java/org/eclipse/che/plugin/php/languageserver/PhpLanguageServerConfig.java new file mode 100644 index 00000000000..14146e4d937 --- /dev/null +++ b/plugins/plugin-php/che-plugin-php-lang-server/src/main/java/org/eclipse/che/plugin/php/languageserver/PhpLanguageServerConfig.java @@ -0,0 +1,86 @@ +/* + * 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.php.languageserver; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.Set; +import org.eclipse.che.api.languageserver.DefaultInstanceProvider; +import org.eclipse.che.api.languageserver.LanguageServerConfig; +import org.eclipse.che.api.languageserver.ProcessCommunicationProvider; +import org.eclipse.che.plugin.php.inject.PhpModule; + +/** + * @author Evgen Vidolob + * @author Anatolii Bazko + * @author Kaloyan Raev + */ +@Singleton +public class PhpLanguageServerConfig implements LanguageServerConfig { + + private static final String REGEX = ".*\\.php"; + + private final Path launchScript; + + @Inject + public PhpLanguageServerConfig() { + this.launchScript = Paths.get(System.getenv("HOME"), "che/ls-php/launch.sh"); + } + + @Override + public RegexProvider getRegexpProvider() { + return new RegexProvider() { + @Override + public Map getLanguageRegexes() { + return ImmutableMap.of(PhpModule.LANGUAGE_ID, REGEX); + } + + @Override + public Set getFileWatchPatterns() { + return ImmutableSet.of(); + } + }; + } + + @Override + public CommunicationProvider getCommunicationProvider() { + ProcessBuilder processBuilder = new ProcessBuilder(launchScript.toString()); + processBuilder.redirectInput(ProcessBuilder.Redirect.PIPE); + processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE); + + return new ProcessCommunicationProvider(processBuilder, PhpModule.LANGUAGE_ID); + } + + @Override + public InstanceProvider getInstanceProvider() { + return DefaultInstanceProvider.getInstance(); + } + + @Override + public InstallerStatusProvider getInstallerStatusProvider() { + return new InstallerStatusProvider() { + @Override + public boolean isSuccessfullyInstalled() { + return launchScript.toFile().exists(); + } + + @Override + public String getCause() { + return isSuccessfullyInstalled() ? null : "Launch script file does not exist"; + } + }; + } +} diff --git a/plugins/plugin-php/che-plugin-php-lang-server/src/main/java/org/eclipse/che/plugin/php/languageserver/PhpLanguageServerLauncher.java b/plugins/plugin-php/che-plugin-php-lang-server/src/main/java/org/eclipse/che/plugin/php/languageserver/PhpLanguageServerLauncher.java deleted file mode 100644 index 567d6585bf0..00000000000 --- a/plugins/plugin-php/che-plugin-php-lang-server/src/main/java/org/eclipse/che/plugin/php/languageserver/PhpLanguageServerLauncher.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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.php.languageserver; - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncherTemplate; -import org.eclipse.che.api.languageserver.registry.DocumentFilter; -import org.eclipse.che.api.languageserver.registry.LanguageServerDescription; -import org.eclipse.che.plugin.php.inject.PhpModule; -import org.eclipse.lsp4j.jsonrpc.Launcher; -import org.eclipse.lsp4j.services.LanguageClient; -import org.eclipse.lsp4j.services.LanguageServer; - -/** - * @author Evgen Vidolob - * @author Anatolii Bazko - * @author Kaloyan Raev - */ -@Singleton -public class PhpLanguageServerLauncher extends LanguageServerLauncherTemplate { - - private static final String REGEX = ".*\\.php"; - - private final Path launchScript; - - private static final LanguageServerDescription DESCRIPTION = createServerDescription(); - - @Inject - public PhpLanguageServerLauncher() { - this.launchScript = Paths.get(System.getenv("HOME"), "che/ls-php/launch.sh"); - } - - @Override - public boolean isAbleToLaunch() { - return Files.exists(launchScript); - } - - protected LanguageServer connectToLanguageServer( - final Process languageServerProcess, LanguageClient client) { - Launcher launcher = - Launcher.createLauncher( - client, - LanguageServer.class, - languageServerProcess.getInputStream(), - languageServerProcess.getOutputStream()); - launcher.startListening(); - return launcher.getRemoteProxy(); - } - - protected Process startLanguageServerProcess(String projectPath) throws LanguageServerException { - ProcessBuilder processBuilder = new ProcessBuilder(launchScript.toString()); - processBuilder.redirectInput(ProcessBuilder.Redirect.PIPE); - processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE); - try { - return processBuilder.start(); - } catch (IOException e) { - throw new LanguageServerException("Can't start PHP language server", e); - } - } - - @Override - public LanguageServerDescription getDescription() { - return DESCRIPTION; - } - - private static LanguageServerDescription createServerDescription() { - LanguageServerDescription description = - new LanguageServerDescription( - "org.eclipse.che.plugin.php.languageserver", - null, - Arrays.asList(new DocumentFilter(PhpModule.LANGUAGE_ID, REGEX, null))); - return description; - } -} diff --git a/plugins/plugin-python/che-plugin-python-lang-ide/pom.xml b/plugins/plugin-python/che-plugin-python-lang-ide/pom.xml index 55adc670e6d..dce8dd56ba2 100644 --- a/plugins/plugin-python/che-plugin-python-lang-ide/pom.xml +++ b/plugins/plugin-python/che-plugin-python-lang-ide/pom.xml @@ -30,10 +30,18 @@ com.google.inject guice + + javax.inject + javax.inject + javax.validation validation-api + + org.eclipse.che.core + che-core-api-languageserver-shared + org.eclipse.che.core che-core-ide-api diff --git a/plugins/plugin-python/che-plugin-python-lang-ide/src/main/java/org/eclipse/che/plugin/python/ide/PythonLanguageDescriptionProvider.java b/plugins/plugin-python/che-plugin-python-lang-ide/src/main/java/org/eclipse/che/plugin/python/ide/PythonLanguageDescriptionProvider.java new file mode 100644 index 00000000000..daabf4db195 --- /dev/null +++ b/plugins/plugin-python/che-plugin-python-lang-ide/src/main/java/org/eclipse/che/plugin/python/ide/PythonLanguageDescriptionProvider.java @@ -0,0 +1,32 @@ +/* + * 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.python.ide; + +import static java.util.Arrays.asList; + +import javax.inject.Provider; +import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; +import org.eclipse.che.plugin.python.shared.ProjectAttributes; + +public class PythonLanguageDescriptionProvider implements Provider { + private static final String[] EXTENSIONS = new String[] {ProjectAttributes.PYTHON_EXT}; + private static final String MIME_TYPE = "text/x-python"; + + @Override + public LanguageDescription get() { + LanguageDescription description = new LanguageDescription(); + description.setFileExtensions(asList(EXTENSIONS)); + description.setLanguageId(ProjectAttributes.PYTHON_ID); + description.setMimeType(MIME_TYPE); + + return description; + } +} diff --git a/plugins/plugin-python/che-plugin-python-lang-ide/src/main/java/org/eclipse/che/plugin/python/ide/inject/PythonGinModule.java b/plugins/plugin-python/che-plugin-python-lang-ide/src/main/java/org/eclipse/che/plugin/python/ide/inject/PythonGinModule.java index 86d16ef003b..48bf47f9c90 100644 --- a/plugins/plugin-python/che-plugin-python-lang-ide/src/main/java/org/eclipse/che/plugin/python/ide/inject/PythonGinModule.java +++ b/plugins/plugin-python/che-plugin-python-lang-ide/src/main/java/org/eclipse/che/plugin/python/ide/inject/PythonGinModule.java @@ -10,16 +10,18 @@ */ package org.eclipse.che.plugin.python.ide.inject; +import static com.google.gwt.inject.client.multibindings.GinMultibinder.newSetBinder; import static org.eclipse.che.plugin.python.shared.ProjectAttributes.PYTHON_EXT; import com.google.gwt.inject.client.AbstractGinModule; -import com.google.gwt.inject.client.multibindings.GinMultibinder; import com.google.inject.Provides; import com.google.inject.Singleton; import com.google.inject.name.Named; +import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; import org.eclipse.che.ide.api.extension.ExtensionGinModule; import org.eclipse.che.ide.api.filetypes.FileType; import org.eclipse.che.ide.api.project.type.wizard.ProjectWizardRegistrar; +import org.eclipse.che.plugin.python.ide.PythonLanguageDescriptionProvider; import org.eclipse.che.plugin.python.ide.PythonResources; import org.eclipse.che.plugin.python.ide.project.PythonProjectWizardRegistrar; @@ -29,9 +31,13 @@ public class PythonGinModule extends AbstractGinModule { @Override protected void configure() { - GinMultibinder.newSetBinder(binder(), ProjectWizardRegistrar.class) + newSetBinder(binder(), ProjectWizardRegistrar.class) .addBinding() .to(PythonProjectWizardRegistrar.class); + + newSetBinder(binder(), LanguageDescription.class) + .addBinding() + .toProvider(PythonLanguageDescriptionProvider.class); } @Provides diff --git a/plugins/plugin-python/che-plugin-python-lang-server/pom.xml b/plugins/plugin-python/che-plugin-python-lang-server/pom.xml index baafab91fab..d1da9ef9156 100644 --- a/plugins/plugin-python/che-plugin-python-lang-server/pom.xml +++ b/plugins/plugin-python/che-plugin-python-lang-server/pom.xml @@ -24,6 +24,10 @@ false + + com.google.guava + guava + com.google.inject guice @@ -44,10 +48,6 @@ org.eclipse.che.core che-core-api-languageserver - - org.eclipse.che.core - che-core-api-languageserver-shared - org.eclipse.che.core che-core-api-project @@ -60,13 +60,5 @@ org.eclipse.che.plugin che-plugin-python-lang-shared - - org.eclipse.lsp4j - org.eclipse.lsp4j - - - org.eclipse.lsp4j - org.eclipse.lsp4j.jsonrpc - diff --git a/plugins/plugin-python/che-plugin-python-lang-server/src/main/java/org/eclipse/che/plugin/python/inject/PythonModule.java b/plugins/plugin-python/che-plugin-python-lang-server/src/main/java/org/eclipse/che/plugin/python/inject/PythonModule.java index 612f86f9aa7..cc5d716ee9e 100644 --- a/plugins/plugin-python/che-plugin-python-lang-server/src/main/java/org/eclipse/che/plugin/python/inject/PythonModule.java +++ b/plugins/plugin-python/che-plugin-python-lang-server/src/main/java/org/eclipse/che/plugin/python/inject/PythonModule.java @@ -10,46 +10,30 @@ */ package org.eclipse.che.plugin.python.inject; +import static com.google.inject.multibindings.MapBinder.newMapBinder; import static com.google.inject.multibindings.Multibinder.newSetBinder; -import static java.util.Arrays.asList; import com.google.inject.AbstractModule; -import com.google.inject.multibindings.Multibinder; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; +import org.eclipse.che.api.languageserver.LanguageServerConfig; import org.eclipse.che.api.project.server.handlers.ProjectHandler; import org.eclipse.che.api.project.server.type.ProjectTypeDef; import org.eclipse.che.inject.DynaModule; import org.eclipse.che.plugin.python.generator.PythonProjectGenerator; -import org.eclipse.che.plugin.python.languageserver.PythonLanguageSeverLauncher; +import org.eclipse.che.plugin.python.languageserver.PythonLanguageSeverConfig; import org.eclipse.che.plugin.python.projecttype.PythonProjectType; -import org.eclipse.che.plugin.python.shared.ProjectAttributes; /** @author Valeriy Svydenko */ @DynaModule public class PythonModule extends AbstractModule { - private static final String[] EXTENSIONS = new String[] {ProjectAttributes.PYTHON_EXT}; - private static final String MIME_TYPE = "text/x-python"; - @Override protected void configure() { - Multibinder projectTypeMultibinder = - newSetBinder(binder(), ProjectTypeDef.class); - projectTypeMultibinder.addBinding().to(PythonProjectType.class); + newSetBinder(binder(), ProjectTypeDef.class).addBinding().to(PythonProjectType.class); - Multibinder projectHandlerMultibinder = - newSetBinder(binder(), ProjectHandler.class); - projectHandlerMultibinder.addBinding().to(PythonProjectGenerator.class); + newSetBinder(binder(), ProjectHandler.class).addBinding().to(PythonProjectGenerator.class); - Multibinder.newSetBinder(binder(), LanguageServerLauncher.class) - .addBinding() - .to(PythonLanguageSeverLauncher.class); - LanguageDescription description = new LanguageDescription(); - description.setFileExtensions(asList(EXTENSIONS)); - description.setLanguageId(ProjectAttributes.PYTHON_ID); - description.setMimeType(MIME_TYPE); - Multibinder.newSetBinder(binder(), LanguageDescription.class) - .addBinding() - .toInstance(description); + newMapBinder(binder(), String.class, LanguageServerConfig.class) + .addBinding("org.eclipse.che.plugin.python.languageserver") + .to(PythonLanguageSeverConfig.class) + .asEagerSingleton(); } } diff --git a/plugins/plugin-python/che-plugin-python-lang-server/src/main/java/org/eclipse/che/plugin/python/languageserver/PythonLanguageSeverConfig.java b/plugins/plugin-python/che-plugin-python-lang-server/src/main/java/org/eclipse/che/plugin/python/languageserver/PythonLanguageSeverConfig.java new file mode 100644 index 00000000000..7bf7d8eb1bf --- /dev/null +++ b/plugins/plugin-python/che-plugin-python-lang-server/src/main/java/org/eclipse/che/plugin/python/languageserver/PythonLanguageSeverConfig.java @@ -0,0 +1,82 @@ +/* + * 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.python.languageserver; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.Set; +import javax.inject.Inject; +import javax.inject.Singleton; +import org.eclipse.che.api.languageserver.DefaultInstanceProvider; +import org.eclipse.che.api.languageserver.LanguageServerConfig; +import org.eclipse.che.api.languageserver.ProcessCommunicationProvider; +import org.eclipse.che.plugin.python.shared.ProjectAttributes; + +/** Launches language server for Python */ +@Singleton +public class PythonLanguageSeverConfig implements LanguageServerConfig { + + private static final String REGEX = ".*\\.py"; + + private final Path launchScript; + + @Inject + public PythonLanguageSeverConfig() { + launchScript = Paths.get(System.getenv("HOME"), "che/ls-python/launch.sh"); + } + + @Override + public RegexProvider getRegexpProvider() { + return new RegexProvider() { + @Override + public Map getLanguageRegexes() { + return ImmutableMap.of(ProjectAttributes.PYTHON_ID, REGEX); + } + + @Override + public Set getFileWatchPatterns() { + return ImmutableSet.of(); + } + }; + } + + @Override + public CommunicationProvider getCommunicationProvider() { + ProcessBuilder processBuilder = new ProcessBuilder(launchScript.toString()); + processBuilder.redirectInput(ProcessBuilder.Redirect.PIPE); + processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE); + + return new ProcessCommunicationProvider(processBuilder, ProjectAttributes.PYTHON_ID); + } + + @Override + public InstanceProvider getInstanceProvider() { + return DefaultInstanceProvider.getInstance(); + } + + @Override + public InstallerStatusProvider getInstallerStatusProvider() { + return new InstallerStatusProvider() { + @Override + public boolean isSuccessfullyInstalled() { + return launchScript.toFile().exists(); + } + + @Override + public String getCause() { + return isSuccessfullyInstalled() ? null : "Launch script file does not exist"; + } + }; + } +} diff --git a/plugins/plugin-python/che-plugin-python-lang-server/src/main/java/org/eclipse/che/plugin/python/languageserver/PythonLanguageSeverLauncher.java b/plugins/plugin-python/che-plugin-python-lang-server/src/main/java/org/eclipse/che/plugin/python/languageserver/PythonLanguageSeverLauncher.java deleted file mode 100644 index a4d6bc027b3..00000000000 --- a/plugins/plugin-python/che-plugin-python-lang-server/src/main/java/org/eclipse/che/plugin/python/languageserver/PythonLanguageSeverLauncher.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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.python.languageserver; - -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import javax.inject.Singleton; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncherTemplate; -import org.eclipse.che.api.languageserver.registry.DocumentFilter; -import org.eclipse.che.api.languageserver.registry.LanguageServerDescription; -import org.eclipse.che.plugin.python.shared.ProjectAttributes; -import org.eclipse.lsp4j.jsonrpc.Launcher; -import org.eclipse.lsp4j.services.LanguageClient; -import org.eclipse.lsp4j.services.LanguageServer; - -/** Launches language server for Python */ -@Singleton -public class PythonLanguageSeverLauncher extends LanguageServerLauncherTemplate { - - private static final LanguageServerDescription DESCRIPTION = createServerDescription(); - private static final String REGEX = ".*\\.py"; - - private final Path launchScript; - - public PythonLanguageSeverLauncher() { - launchScript = Paths.get(System.getenv("HOME"), "che/ls-python/launch.sh"); - } - - @Override - public boolean isAbleToLaunch() { - return launchScript.toFile().exists(); - } - - @Override - protected Process startLanguageServerProcess(String projectPath) throws LanguageServerException { - ProcessBuilder processBuilder = new ProcessBuilder(launchScript.toString()); - processBuilder.redirectInput(ProcessBuilder.Redirect.PIPE); - processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE); - - try { - return processBuilder.start(); - } catch (IOException e) { - throw new LanguageServerException("Can't start Python language server", e); - } - } - - @Override - protected LanguageServer connectToLanguageServer( - final Process languageServerProcess, LanguageClient client) { - Launcher launcher = - Launcher.createLauncher( - client, - LanguageServer.class, - languageServerProcess.getInputStream(), - languageServerProcess.getOutputStream()); - launcher.startListening(); - return launcher.getRemoteProxy(); - } - - @Override - public LanguageServerDescription getDescription() { - return DESCRIPTION; - } - - private static LanguageServerDescription createServerDescription() { - LanguageServerDescription description = - new LanguageServerDescription( - "org.eclipse.che.plugin.python.languageserver", - null, - Arrays.asList(new DocumentFilter(ProjectAttributes.PYTHON_ID, REGEX, null))); - return description; - } -} diff --git a/plugins/plugin-web/che-plugin-web-ext-server/pom.xml b/plugins/plugin-web/che-plugin-web-ext-server/pom.xml index 1914d6aa864..a8d7532a06d 100644 --- a/plugins/plugin-web/che-plugin-web-ext-server/pom.xml +++ b/plugins/plugin-web/che-plugin-web-ext-server/pom.xml @@ -20,6 +20,10 @@ che-plugin-web-ext-server + + com.google.guava + guava + com.google.inject guice @@ -36,10 +40,6 @@ org.eclipse.che.core che-core-api-languageserver - - org.eclipse.che.core - che-core-api-languageserver-shared - org.eclipse.che.core che-core-api-project @@ -52,14 +52,6 @@ org.eclipse.che.plugin che-plugin-web-ext-shared - - org.eclipse.lsp4j - org.eclipse.lsp4j - - - org.eclipse.lsp4j - org.eclipse.lsp4j.jsonrpc - org.testng testng diff --git a/plugins/plugin-web/che-plugin-web-ext-server/src/main/java/org/eclipse/che/plugin/web/inject/WebModule.java b/plugins/plugin-web/che-plugin-web-ext-server/src/main/java/org/eclipse/che/plugin/web/inject/WebModule.java index df635b1efc3..03d26aa855d 100644 --- a/plugins/plugin-web/che-plugin-web-ext-server/src/main/java/org/eclipse/che/plugin/web/inject/WebModule.java +++ b/plugins/plugin-web/che-plugin-web-ext-server/src/main/java/org/eclipse/che/plugin/web/inject/WebModule.java @@ -10,46 +10,26 @@ */ package org.eclipse.che.plugin.web.inject; -import static java.util.Arrays.asList; +import static com.google.inject.multibindings.MapBinder.newMapBinder; +import static com.google.inject.multibindings.Multibinder.newSetBinder; import com.google.inject.AbstractModule; -import com.google.inject.multibindings.Multibinder; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; +import org.eclipse.che.api.languageserver.LanguageServerConfig; import org.eclipse.che.api.project.server.type.ProjectTypeDef; import org.eclipse.che.inject.DynaModule; -import org.eclipse.che.plugin.web.shared.Constants; -import org.eclipse.che.plugin.web.typescript.TSLSLauncher; +import org.eclipse.che.plugin.web.typescript.TypeScriptLanguageServerConfig; import org.eclipse.che.plugin.web.typescript.TypeScriptProjectType; /** The module that contains configuration of the server side part of the Web plugin */ @DynaModule public class WebModule extends AbstractModule { - - private static final String[] EXTENSIONS = new String[] {Constants.TS_EXT}; - private static final String MIME_TYPE = Constants.TS_MIME_TYPE; - @Override protected void configure() { - Multibinder projectTypeMultibinder = - Multibinder.newSetBinder(binder(), ProjectTypeDef.class); - projectTypeMultibinder.addBinding().to(TypeScriptProjectType.class); + newSetBinder(binder(), ProjectTypeDef.class).addBinding().to(TypeScriptProjectType.class); - Multibinder.newSetBinder(binder(), LanguageServerLauncher.class) - .addBinding() - .to(TSLSLauncher.class); - LanguageDescription description = new LanguageDescription(); - description.setFileExtensions(asList(EXTENSIONS)); - description.setLanguageId(Constants.TS_LANG); - description.setMimeType(MIME_TYPE); - description.setHighlightingConfiguration( - "[\n" - + " {\"include\":\"orion.js\"},\n" - + " {\"match\":\"\\\\b(?:constructor|declare|module)\\\\b\",\"name\" :\"keyword.operator.typescript\"},\n" - + " {\"match\":\"\\\\b(?:any|boolean|number|string)\\\\b\",\"name\" : \"storage.type.typescript\"}\n" - + "]"); - Multibinder.newSetBinder(binder(), LanguageDescription.class) - .addBinding() - .toInstance(description); + newMapBinder(binder(), String.class, LanguageServerConfig.class) + .addBinding("org.eclipse.che.plugin.web.typescript") + .to(TypeScriptLanguageServerConfig.class) + .asEagerSingleton(); } } diff --git a/plugins/plugin-web/che-plugin-web-ext-server/src/main/java/org/eclipse/che/plugin/web/typescript/TSLSLauncher.java b/plugins/plugin-web/che-plugin-web-ext-server/src/main/java/org/eclipse/che/plugin/web/typescript/TSLSLauncher.java deleted file mode 100644 index 10321784e67..00000000000 --- a/plugins/plugin-web/che-plugin-web-ext-server/src/main/java/org/eclipse/che/plugin/web/typescript/TSLSLauncher.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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.web.typescript; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import javax.inject.Singleton; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncherTemplate; -import org.eclipse.che.api.languageserver.registry.DocumentFilter; -import org.eclipse.che.api.languageserver.registry.LanguageServerDescription; -import org.eclipse.che.plugin.web.shared.Constants; -import org.eclipse.lsp4j.jsonrpc.Launcher; -import org.eclipse.lsp4j.services.LanguageClient; -import org.eclipse.lsp4j.services.LanguageServer; - -/** Launcher for TypeScript Language Server */ -@Singleton -public class TSLSLauncher extends LanguageServerLauncherTemplate { - - private static final String REGEX = ".*\\.(ts|tsx)"; - private static final LanguageServerDescription DESCRIPTION = createServerDescription(); - - private final Path launchScript; - - public TSLSLauncher() { - launchScript = Paths.get(System.getenv("HOME"), "che/ls-typescript/launch.sh"); - } - - @Override - protected Process startLanguageServerProcess(String projectPath) throws LanguageServerException { - ProcessBuilder processBuilder = new ProcessBuilder(launchScript.toString()); - processBuilder.redirectInput(ProcessBuilder.Redirect.PIPE); - processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE); - try { - return processBuilder.start(); - } catch (IOException e) { - throw new LanguageServerException("Can't start TypeScript language server", e); - } - } - - @Override - protected LanguageServer connectToLanguageServer( - final Process languageServerProcess, LanguageClient client) { - Launcher launcher = - Launcher.createLauncher( - client, - LanguageServer.class, - languageServerProcess.getInputStream(), - languageServerProcess.getOutputStream()); - launcher.startListening(); - return launcher.getRemoteProxy(); - } - - @Override - public LanguageServerDescription getDescription() { - return DESCRIPTION; - } - - private static LanguageServerDescription createServerDescription() { - LanguageServerDescription description = - new LanguageServerDescription( - "org.eclipse.che.plugin.web.typescript", - null, - Arrays.asList(new DocumentFilter(Constants.TS_LANG, REGEX, null))); - return description; - } - - @Override - public boolean isAbleToLaunch() { - return Files.exists(launchScript); - } -} diff --git a/plugins/plugin-web/che-plugin-web-ext-server/src/main/java/org/eclipse/che/plugin/web/typescript/TypeScriptLanguageServerConfig.java b/plugins/plugin-web/che-plugin-web-ext-server/src/main/java/org/eclipse/che/plugin/web/typescript/TypeScriptLanguageServerConfig.java new file mode 100644 index 00000000000..3e482e25fae --- /dev/null +++ b/plugins/plugin-web/che-plugin-web-ext-server/src/main/java/org/eclipse/che/plugin/web/typescript/TypeScriptLanguageServerConfig.java @@ -0,0 +1,83 @@ +/* + * 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.web.typescript; + +import static org.eclipse.che.plugin.web.shared.Constants.TS_LANG; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.Set; +import javax.inject.Inject; +import javax.inject.Singleton; +import org.eclipse.che.api.languageserver.DefaultInstanceProvider; +import org.eclipse.che.api.languageserver.LanguageServerConfig; +import org.eclipse.che.api.languageserver.ProcessCommunicationProvider; + +/** Launcher for TypeScript Language Server */ +@Singleton +public class TypeScriptLanguageServerConfig implements LanguageServerConfig { + + private static final String REGEX = ".*\\.(ts|tsx)"; + + private final Path launchScript; + + @Inject + public TypeScriptLanguageServerConfig() { + launchScript = Paths.get(System.getenv("HOME"), "che/ls-typescript/launch.sh"); + } + + @Override + public RegexProvider getRegexpProvider() { + return new RegexProvider() { + @Override + public Map getLanguageRegexes() { + return ImmutableMap.of(TS_LANG, REGEX); + } + + @Override + public Set getFileWatchPatterns() { + return ImmutableSet.of(); + } + }; + } + + @Override + public CommunicationProvider getCommunicationProvider() { + ProcessBuilder processBuilder = new ProcessBuilder(launchScript.toString()); + processBuilder.redirectInput(ProcessBuilder.Redirect.PIPE); + processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE); + + return new ProcessCommunicationProvider(processBuilder, TS_LANG); + } + + @Override + public InstanceProvider getInstanceProvider() { + return DefaultInstanceProvider.getInstance(); + } + + @Override + public InstallerStatusProvider getInstallerStatusProvider() { + return new InstallerStatusProvider() { + @Override + public boolean isSuccessfullyInstalled() { + return launchScript.toFile().exists(); + } + + @Override + public String getCause() { + return isSuccessfullyInstalled() ? null : "Launch script file does not exist"; + } + }; + } +} diff --git a/plugins/plugin-web/che-plugin-web-ext-web/pom.xml b/plugins/plugin-web/che-plugin-web-ext-web/pom.xml index ae01ab73d9a..10678914550 100644 --- a/plugins/plugin-web/che-plugin-web-ext-web/pom.xml +++ b/plugins/plugin-web/che-plugin-web-ext-web/pom.xml @@ -33,6 +33,10 @@ javax.inject javax.inject + + org.eclipse.che.core + che-core-api-languageserver-shared + org.eclipse.che.core che-core-ide-api diff --git a/plugins/plugin-web/che-plugin-web-ext-web/src/main/java/org/eclipse/che/plugin/web/client/CamelLanguageDescriptionProvider.java b/plugins/plugin-web/che-plugin-web-ext-web/src/main/java/org/eclipse/che/plugin/web/client/CamelLanguageDescriptionProvider.java new file mode 100644 index 00000000000..34e5a4c9819 --- /dev/null +++ b/plugins/plugin-web/che-plugin-web-ext-web/src/main/java/org/eclipse/che/plugin/web/client/CamelLanguageDescriptionProvider.java @@ -0,0 +1,31 @@ +/* + * 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.web.client; + +import static java.util.Arrays.asList; + +import javax.inject.Provider; +import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; + +public class CamelLanguageDescriptionProvider implements Provider { + private static final String LANGUAGE_ID = "LANGUAGE_ID_APACHE_CAMEL"; + private static final String[] EXTENSIONS = new String[] {"xml"}; + private static final String MIME_TYPE = "text/xml"; + + @Override + public LanguageDescription get() { + LanguageDescription description = new LanguageDescription(); + description.setFileExtensions(asList(EXTENSIONS)); + description.setLanguageId(LANGUAGE_ID); + description.setMimeType(MIME_TYPE); + return description; + } +} diff --git a/plugins/plugin-web/che-plugin-web-ext-web/src/main/java/org/eclipse/che/plugin/web/client/JsonLanguageDescriptionProvider.java b/plugins/plugin-web/che-plugin-web-ext-web/src/main/java/org/eclipse/che/plugin/web/client/JsonLanguageDescriptionProvider.java new file mode 100644 index 00000000000..d93395cfc42 --- /dev/null +++ b/plugins/plugin-web/che-plugin-web-ext-web/src/main/java/org/eclipse/che/plugin/web/client/JsonLanguageDescriptionProvider.java @@ -0,0 +1,33 @@ +/* + * 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.web.client; + +import static java.util.Arrays.asList; + +import javax.inject.Provider; +import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; + +public class JsonLanguageDescriptionProvider implements Provider { + private static final String LANGUAGE_ID = "json"; + private static final String[] EXTENSIONS = + new String[] {"json", "bowerrc", "jshintrc", "jscsrc", "eslintrc", "babelrc"}; + private static final String MIME_TYPE = "application/json"; + + @Override + public LanguageDescription get() { + LanguageDescription description = new LanguageDescription(); + description.setFileExtensions(asList(EXTENSIONS)); + description.setLanguageId(LANGUAGE_ID); + description.setMimeType(MIME_TYPE); + + return description; + } +} diff --git a/plugins/plugin-web/che-plugin-web-ext-web/src/main/java/org/eclipse/che/plugin/web/client/TypeScriptLanguageDescriptionProvider.java b/plugins/plugin-web/che-plugin-web-ext-web/src/main/java/org/eclipse/che/plugin/web/client/TypeScriptLanguageDescriptionProvider.java new file mode 100644 index 00000000000..4f0c6334cb1 --- /dev/null +++ b/plugins/plugin-web/che-plugin-web-ext-web/src/main/java/org/eclipse/che/plugin/web/client/TypeScriptLanguageDescriptionProvider.java @@ -0,0 +1,39 @@ +/* + * 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.web.client; + +import static java.util.Arrays.asList; + +import javax.inject.Provider; +import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; +import org.eclipse.che.plugin.web.shared.Constants; + +public class TypeScriptLanguageDescriptionProvider implements Provider { + + private static final String[] EXTENSIONS = new String[] {Constants.TS_EXT}; + private static final String MIME_TYPE = Constants.TS_MIME_TYPE; + + @Override + public LanguageDescription get() { + LanguageDescription description = new LanguageDescription(); + description.setFileExtensions(asList(EXTENSIONS)); + description.setLanguageId(Constants.TS_LANG); + description.setMimeType(MIME_TYPE); + description.setHighlightingConfiguration( + "[\n" + + " {\"include\":\"orion.js\"},\n" + + " {\"match\":\"\\\\b(?:constructor|declare|module)\\\\b\",\"name\" :\"keyword.operator.typescript\"},\n" + + " {\"match\":\"\\\\b(?:any|boolean|number|string)\\\\b\",\"name\" : \"storage.type.typescript\"}\n" + + "]"); + + return description; + } +} diff --git a/plugins/plugin-web/che-plugin-web-ext-web/src/main/java/org/eclipse/che/plugin/web/client/inject/WebModule.java b/plugins/plugin-web/che-plugin-web-ext-web/src/main/java/org/eclipse/che/plugin/web/client/inject/WebModule.java index de8ed3083ae..d38d4b82975 100644 --- a/plugins/plugin-web/che-plugin-web-ext-web/src/main/java/org/eclipse/che/plugin/web/client/inject/WebModule.java +++ b/plugins/plugin-web/che-plugin-web-ext-web/src/main/java/org/eclipse/che/plugin/web/client/inject/WebModule.java @@ -10,14 +10,19 @@ */ package org.eclipse.che.plugin.web.client.inject; +import static com.google.gwt.inject.client.multibindings.GinMultibinder.newSetBinder; + import com.google.gwt.inject.client.AbstractGinModule; -import com.google.gwt.inject.client.multibindings.GinMultibinder; import com.google.inject.Provides; import com.google.inject.Singleton; import com.google.inject.name.Named; +import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; import org.eclipse.che.ide.api.extension.ExtensionGinModule; import org.eclipse.che.ide.api.filetypes.FileType; import org.eclipse.che.ide.api.project.type.wizard.ProjectWizardRegistrar; +import org.eclipse.che.plugin.web.client.CamelLanguageDescriptionProvider; +import org.eclipse.che.plugin.web.client.JsonLanguageDescriptionProvider; +import org.eclipse.che.plugin.web.client.TypeScriptLanguageDescriptionProvider; import org.eclipse.che.plugin.web.client.WebExtensionResource; import org.eclipse.che.plugin.web.client.typescript.TSProjectWizardRegistrar; @@ -31,9 +36,20 @@ public class WebModule extends AbstractGinModule { @Override protected void configure() { - GinMultibinder.newSetBinder(binder(), ProjectWizardRegistrar.class) + newSetBinder(binder(), ProjectWizardRegistrar.class) .addBinding() .to(TSProjectWizardRegistrar.class); + + newSetBinder(binder(), LanguageDescription.class) + .addBinding() + .toProvider(TypeScriptLanguageDescriptionProvider.class); + + newSetBinder(binder(), LanguageDescription.class) + .addBinding() + .toProvider(JsonLanguageDescriptionProvider.class); + newSetBinder(binder(), LanguageDescription.class) + .addBinding() + .toProvider(CamelLanguageDescriptionProvider.class); } @Provides diff --git a/plugins/plugin-yaml/che-plugin-yaml-ide/pom.xml b/plugins/plugin-yaml/che-plugin-yaml-ide/pom.xml index a153e240550..b481c5729f6 100644 --- a/plugins/plugin-yaml/che-plugin-yaml-ide/pom.xml +++ b/plugins/plugin-yaml/che-plugin-yaml-ide/pom.xml @@ -22,10 +22,18 @@ gwt-lib Che Plugin :: Yaml :: Ide + + javax.inject + javax.inject + javax.validation validation-api + + org.eclipse.che.core + che-core-api-languageserver-shared + org.eclipse.che.core che-core-commons-gwt diff --git a/plugins/plugin-yaml/che-plugin-yaml-ide/src/main/java/org/eclipse/che/plugin/yaml/ide/YamlDescriptionProvider.java b/plugins/plugin-yaml/che-plugin-yaml-ide/src/main/java/org/eclipse/che/plugin/yaml/ide/YamlDescriptionProvider.java new file mode 100644 index 00000000000..64bfd48a629 --- /dev/null +++ b/plugins/plugin-yaml/che-plugin-yaml-ide/src/main/java/org/eclipse/che/plugin/yaml/ide/YamlDescriptionProvider.java @@ -0,0 +1,32 @@ +/* + * 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.yaml.ide; + +import static java.util.Arrays.asList; + +import javax.inject.Provider; +import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; + +public class YamlDescriptionProvider implements Provider { + private static final String LANGUAGE_ID = "yaml"; + private static final String[] EXTENSIONS = new String[] {"yaml", "yml"}; + private static final String MIME_TYPE = "text/yaml"; + + @Override + public LanguageDescription get() { + LanguageDescription description = new LanguageDescription(); + description.setFileExtensions(asList(EXTENSIONS)); + description.setLanguageId(LANGUAGE_ID); + description.setMimeType(MIME_TYPE); + + return description; + } +} diff --git a/plugins/plugin-yaml/che-plugin-yaml-ide/src/main/java/org/eclipse/che/plugin/yaml/ide/inject/YamlGinModule.java b/plugins/plugin-yaml/che-plugin-yaml-ide/src/main/java/org/eclipse/che/plugin/yaml/ide/inject/YamlGinModule.java index 849a403370a..bd45729ef82 100644 --- a/plugins/plugin-yaml/che-plugin-yaml-ide/src/main/java/org/eclipse/che/plugin/yaml/ide/inject/YamlGinModule.java +++ b/plugins/plugin-yaml/che-plugin-yaml-ide/src/main/java/org/eclipse/che/plugin/yaml/ide/inject/YamlGinModule.java @@ -10,11 +10,14 @@ */ package org.eclipse.che.plugin.yaml.ide.inject; +import static com.google.gwt.inject.client.multibindings.GinMultibinder.newSetBinder; + import com.google.gwt.inject.client.AbstractGinModule; -import com.google.gwt.inject.client.multibindings.GinMultibinder; import com.google.inject.Singleton; +import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; import org.eclipse.che.ide.api.extension.ExtensionGinModule; import org.eclipse.che.ide.api.preferences.PreferencePagePresenter; +import org.eclipse.che.plugin.yaml.ide.YamlDescriptionProvider; import org.eclipse.che.plugin.yaml.ide.YamlServiceClient; import org.eclipse.che.plugin.yaml.ide.YamlServiceClientImpl; import org.eclipse.che.plugin.yaml.ide.preferences.YamlExtensionManagerPresenter; @@ -33,10 +36,14 @@ public class YamlGinModule extends AbstractGinModule { @Override protected void configure() { bind(YamlServiceClient.class).to(YamlServiceClientImpl.class).in(Singleton.class); - bind(YamlExtensionManagerView.class).to(YamlExtensionManagerViewImpl.class).in(Singleton.class); - GinMultibinder prefBinder = - GinMultibinder.newSetBinder(binder(), PreferencePagePresenter.class); - prefBinder.addBinding().to(YamlExtensionManagerPresenter.class); + + newSetBinder(binder(), PreferencePagePresenter.class) + .addBinding() + .to(YamlExtensionManagerPresenter.class); + + newSetBinder(binder(), LanguageDescription.class) + .addBinding() + .toProvider(YamlDescriptionProvider.class); } } diff --git a/plugins/plugin-yaml/che-plugin-yaml-server/pom.xml b/plugins/plugin-yaml/che-plugin-yaml-server/pom.xml index 806f9e6ff1e..37831f64680 100644 --- a/plugins/plugin-yaml/che-plugin-yaml-server/pom.xml +++ b/plugins/plugin-yaml/che-plugin-yaml-server/pom.xml @@ -36,6 +36,10 @@ com.google.inject.extensions guice-multibindings + + javax.annotation + javax.annotation-api + javax.inject javax.inject @@ -52,10 +56,6 @@ org.eclipse.che.core che-core-api-languageserver - - org.eclipse.che.core - che-core-api-languageserver-shared - org.eclipse.che.core che-core-commons-inject diff --git a/plugins/plugin-yaml/che-plugin-yaml-server/src/main/java/org/eclipse/che/plugin/yaml/server/inject/YamlModule.java b/plugins/plugin-yaml/che-plugin-yaml-server/src/main/java/org/eclipse/che/plugin/yaml/server/inject/YamlModule.java index 2d116ac4e7a..b3b60828f29 100644 --- a/plugins/plugin-yaml/che-plugin-yaml-server/src/main/java/org/eclipse/che/plugin/yaml/server/inject/YamlModule.java +++ b/plugins/plugin-yaml/che-plugin-yaml-server/src/main/java/org/eclipse/che/plugin/yaml/server/inject/YamlModule.java @@ -10,14 +10,12 @@ */ package org.eclipse.che.plugin.yaml.server.inject; -import static java.util.Arrays.asList; +import static com.google.inject.multibindings.MapBinder.newMapBinder; import com.google.inject.AbstractModule; -import com.google.inject.multibindings.Multibinder; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; +import org.eclipse.che.api.languageserver.LanguageServerConfig; import org.eclipse.che.inject.DynaModule; -import org.eclipse.che.plugin.yaml.server.languageserver.YamlLanguageServerLauncher; +import org.eclipse.che.plugin.yaml.server.languageserver.YamlLanguageServerConfig; import org.eclipse.che.plugin.yaml.server.languageserver.YamlService; /** @@ -28,21 +26,13 @@ @DynaModule public class YamlModule extends AbstractModule { public static final String LANGUAGE_ID = "yaml"; - private static final String[] EXTENSIONS = new String[] {"yaml", "yml"}; - private static final String MIME_TYPE = "text/yaml"; @Override protected void configure() { - Multibinder.newSetBinder(binder(), LanguageServerLauncher.class) - .addBinding() - .to(YamlLanguageServerLauncher.class); - LanguageDescription description = new LanguageDescription(); - description.setFileExtensions(asList(EXTENSIONS)); - description.setLanguageId(LANGUAGE_ID); - description.setMimeType(MIME_TYPE); - Multibinder.newSetBinder(binder(), LanguageDescription.class) - .addBinding() - .toInstance(description); + newMapBinder(binder(), String.class, LanguageServerConfig.class) + .addBinding("org.eclipse.che.plugin.yaml.server.languageserver") + .to(YamlLanguageServerConfig.class) + .asEagerSingleton(); bind(YamlService.class); } diff --git a/plugins/plugin-yaml/che-plugin-yaml-server/src/main/java/org/eclipse/che/plugin/yaml/server/languageserver/YamlLanguageServerLauncher.java b/plugins/plugin-yaml/che-plugin-yaml-server/src/main/java/org/eclipse/che/plugin/yaml/server/languageserver/YamlLanguageServerConfig.java similarity index 62% rename from plugins/plugin-yaml/che-plugin-yaml-server/src/main/java/org/eclipse/che/plugin/yaml/server/languageserver/YamlLanguageServerLauncher.java rename to plugins/plugin-yaml/che-plugin-yaml-server/src/main/java/org/eclipse/che/plugin/yaml/server/languageserver/YamlLanguageServerConfig.java index 98c7c45e231..1c650510757 100644 --- a/plugins/plugin-yaml/che-plugin-yaml-server/src/main/java/org/eclipse/che/plugin/yaml/server/languageserver/YamlLanguageServerLauncher.java +++ b/plugins/plugin-yaml/che-plugin-yaml-server/src/main/java/org/eclipse/che/plugin/yaml/server/languageserver/YamlLanguageServerConfig.java @@ -10,33 +10,32 @@ */ package org.eclipse.che.plugin.yaml.server.languageserver; +import static java.util.Collections.emptySet; +import static java.util.Collections.singletonMap; + import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import com.google.inject.Inject; import com.google.inject.Singleton; import java.io.IOException; import java.lang.reflect.Type; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; +import javax.annotation.PostConstruct; import javax.inject.Named; import org.eclipse.che.api.core.ApiException; +import org.eclipse.che.api.core.notification.EventService; import org.eclipse.che.api.core.rest.HttpJsonRequestFactory; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncherTemplate; -import org.eclipse.che.api.languageserver.registry.DocumentFilter; -import org.eclipse.che.api.languageserver.registry.LanguageServerDescription; -import org.eclipse.che.api.languageserver.registry.ServerInitializerObserver; +import org.eclipse.che.api.languageserver.DefaultInstanceProvider; +import org.eclipse.che.api.languageserver.LanguageServerConfig; +import org.eclipse.che.api.languageserver.LanguageServerInitializedEvent; +import org.eclipse.che.api.languageserver.ProcessCommunicationProvider; import org.eclipse.che.plugin.yaml.server.inject.YamlModule; import org.eclipse.che.plugin.yaml.shared.PreferenceHelper; import org.eclipse.che.plugin.yaml.shared.YamlPreference; -import org.eclipse.lsp4j.ServerCapabilities; import org.eclipse.lsp4j.jsonrpc.Endpoint; -import org.eclipse.lsp4j.jsonrpc.Launcher; import org.eclipse.lsp4j.jsonrpc.services.ServiceEndpoints; -import org.eclipse.lsp4j.services.LanguageClient; import org.eclipse.lsp4j.services.LanguageServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,53 +46,32 @@ * @author Joshua Pinkney */ @Singleton -public class YamlLanguageServerLauncher extends LanguageServerLauncherTemplate - implements ServerInitializerObserver { - - private static final Logger LOG = LoggerFactory.getLogger(YamlLanguageServerLauncher.class); +public class YamlLanguageServerConfig implements LanguageServerConfig { + private static final Logger LOG = LoggerFactory.getLogger(YamlLanguageServerConfig.class); private static final String REGEX = ".*\\.(yaml|yml)"; - private static final LanguageServerDescription DESCRIPTION = createServerDescription(); + private static LanguageServer yamlLanguageServer; + private final Path launchScript; - private HttpJsonRequestFactory requestFactory; - private String apiUrl; + private final EventService eventService; + private final HttpJsonRequestFactory requestFactory; + private final String apiUrl; @Inject - public YamlLanguageServerLauncher( - @Named("che.api") String apiUrl, HttpJsonRequestFactory requestFactory) { + public YamlLanguageServerConfig( + EventService eventService, + @Named("che.api") String apiUrl, + HttpJsonRequestFactory requestFactory) { + this.eventService = eventService; this.apiUrl = apiUrl; this.requestFactory = requestFactory; launchScript = Paths.get(System.getenv("HOME"), "che/ls-yaml/launch.sh"); } - @Override - public boolean isAbleToLaunch() { - return Files.exists(launchScript); - } - - protected LanguageServer connectToLanguageServer( - final Process languageServerProcess, LanguageClient client) { - Launcher launcher = - Launcher.createLauncher( - client, - LanguageServer.class, - languageServerProcess.getInputStream(), - languageServerProcess.getOutputStream()); - launcher.startListening(); - setYamlLanguageServer(launcher.getRemoteProxy()); - return launcher.getRemoteProxy(); - } - - protected Process startLanguageServerProcess(String projectPath) throws LanguageServerException { - ProcessBuilder processBuilder = new ProcessBuilder(launchScript.toString()); - processBuilder.redirectInput(ProcessBuilder.Redirect.PIPE); - processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE); - try { - return processBuilder.start(); - } catch (IOException e) { - throw new LanguageServerException("Can't start YAML language server", e); - } + @PostConstruct + private void subscribe() { + eventService.subscribe(this::onLanguageServerInitialized, LanguageServerInitializedEvent.class); } protected static LanguageServer getYamlLanguageServer() { @@ -101,21 +79,16 @@ protected static LanguageServer getYamlLanguageServer() { } protected static void setYamlLanguageServer(LanguageServer yamlLanguageServer) { - YamlLanguageServerLauncher.yamlLanguageServer = yamlLanguageServer; + YamlLanguageServerConfig.yamlLanguageServer = yamlLanguageServer; } - @Override - public void onServerInitialized( - LanguageServerLauncher launcher, - LanguageServer server, - ServerCapabilities capabilities, - String projectPath) { + public void onLanguageServerInitialized(LanguageServerInitializedEvent event) { try { Map preferences = requestFactory.fromUrl(apiUrl + "/preferences").useGetMethod().request().asProperties(); - Endpoint endpoint = ServiceEndpoints.toEndpoint(server); + Endpoint endpoint = ServiceEndpoints.toEndpoint(event.getLanguageServer()); YamlSchemaAssociations serviceObject = ServiceEndpoints.toServiceObject(endpoint, YamlSchemaAssociations.class); Map associations = @@ -163,7 +136,7 @@ private List jsonToYamlPreference(String jsonStr) { Type type = new TypeToken>() {}.getType(); Map yamlPreferenceMap = new Gson().fromJson(jsonStr, type); - if (yamlPreferenceMap == null || jsonStr == "") { + if (yamlPreferenceMap == null || "".equals(jsonStr)) { return new ArrayList(); } @@ -177,16 +150,47 @@ private List jsonToYamlPreference(String jsonStr) { return preferences; } - public LanguageServerDescription getDescription() { - return DESCRIPTION; + @Override + public RegexProvider getRegexpProvider() { + return new RegexProvider() { + @Override + public Map getLanguageRegexes() { + return singletonMap(YamlModule.LANGUAGE_ID, REGEX); + } + + @Override + public Set getFileWatchPatterns() { + return emptySet(); + } + }; + } + + @Override + public CommunicationProvider getCommunicationProvider() { + ProcessBuilder processBuilder = new ProcessBuilder(launchScript.toString()); + processBuilder.redirectInput(ProcessBuilder.Redirect.PIPE); + processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE); + + return new ProcessCommunicationProvider(processBuilder, YamlModule.LANGUAGE_ID); + } + + @Override + public InstanceProvider getInstanceProvider() { + return DefaultInstanceProvider.getInstance(); } - private static LanguageServerDescription createServerDescription() { - LanguageServerDescription description = - new LanguageServerDescription( - "org.eclipse.che.plugin.yaml.server.languageserver", - null, - Arrays.asList(new DocumentFilter(YamlModule.LANGUAGE_ID, REGEX, null))); - return description; + @Override + public InstallerStatusProvider getInstallerStatusProvider() { + return new InstallerStatusProvider() { + @Override + public boolean isSuccessfullyInstalled() { + return launchScript.toFile().exists(); + } + + @Override + public String getCause() { + return isSuccessfullyInstalled() ? null : "Launch script file does not exist"; + } + }; } } diff --git a/plugins/plugin-yaml/che-plugin-yaml-server/src/main/java/org/eclipse/che/plugin/yaml/server/languageserver/YamlService.java b/plugins/plugin-yaml/che-plugin-yaml-server/src/main/java/org/eclipse/che/plugin/yaml/server/languageserver/YamlService.java index fca02c00988..d822efc69fd 100644 --- a/plugins/plugin-yaml/che-plugin-yaml-server/src/main/java/org/eclipse/che/plugin/yaml/server/languageserver/YamlService.java +++ b/plugins/plugin-yaml/che-plugin-yaml-server/src/main/java/org/eclipse/che/plugin/yaml/server/languageserver/YamlService.java @@ -39,7 +39,7 @@ public class YamlService { @Consumes(MediaType.APPLICATION_JSON) public void putSchemas(YamlDTO yamlDto) throws ApiException { - LanguageServer yamlLS = YamlLanguageServerLauncher.getYamlLanguageServer(); + LanguageServer yamlLS = YamlLanguageServerConfig.getYamlLanguageServer(); if (yamlDto != null && yamlLS != null) { diff --git a/wsagent/che-core-api-languageserver-shared/pom.xml b/wsagent/che-core-api-languageserver-shared/pom.xml index d8cccade344..4a1a9f47b2b 100644 --- a/wsagent/che-core-api-languageserver-shared/pom.xml +++ b/wsagent/che-core-api-languageserver-shared/pom.xml @@ -25,6 +25,10 @@ false + + org.eclipse.che.core + che-core-api-dto + org.eclipse.lsp4j org.eclipse.lsp4j diff --git a/wsagent/che-core-api-languageserver-shared/src/main/java/org/eclipse/che/api/languageserver/shared/model/LanguageRegex.java b/wsagent/che-core-api-languageserver-shared/src/main/java/org/eclipse/che/api/languageserver/shared/model/LanguageRegex.java new file mode 100644 index 00000000000..4c1fe2e4a88 --- /dev/null +++ b/wsagent/che-core-api-languageserver-shared/src/main/java/org/eclipse/che/api/languageserver/shared/model/LanguageRegex.java @@ -0,0 +1,39 @@ +/* + * 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.api.languageserver.shared.model; + +import org.eclipse.che.dto.shared.DTO; + +/** + * Language regular expression definition is mostly used to match a workspace path to a language + * server instance. It is also may be used in some way to configure client file type registry. + */ +@DTO +public class LanguageRegex { + private String languageId; + private String namePattern; + + public String getLanguageId() { + return languageId; + } + + public void setLanguageId(String languageId) { + this.languageId = languageId; + } + + public String getNamePattern() { + return namePattern; + } + + public void setNamePattern(String namePattern) { + this.namePattern = namePattern; + } +} diff --git a/wsagent/che-core-api-languageserver/pom.xml b/wsagent/che-core-api-languageserver/pom.xml index 1b74c463324..e138865390e 100644 --- a/wsagent/che-core-api-languageserver/pom.xml +++ b/wsagent/che-core-api-languageserver/pom.xml @@ -45,6 +45,10 @@ com.google.inject.extensions guice-multibindings + + commons-io + commons-io + javax.annotation javax.annotation-api @@ -101,6 +105,10 @@ org.eclipse.text org.eclipse.text + + org.everrest + everrest-core + org.slf4j slf4j-api diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/CheLanguageClient.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/CheLanguageClient.java similarity index 98% rename from wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/CheLanguageClient.java rename to wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/CheLanguageClient.java index bf07c64e5ea..d8be994a1c0 100644 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/CheLanguageClient.java +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/CheLanguageClient.java @@ -8,7 +8,7 @@ * Contributors: * Red Hat, Inc. - initial API and implementation */ -package org.eclipse.che.api.languageserver.registry; +package org.eclipse.che.api.languageserver; import com.google.inject.assistedinject.Assisted; import java.util.concurrent.CompletableFuture; diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/CheLanguageClientFactory.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/CheLanguageClientFactory.java similarity index 90% rename from wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/CheLanguageClientFactory.java rename to wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/CheLanguageClientFactory.java index 53b391ee48f..df1f5f9c7cc 100644 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/CheLanguageClientFactory.java +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/CheLanguageClientFactory.java @@ -8,7 +8,7 @@ * Contributors: * Red Hat, Inc. - initial API and implementation */ -package org.eclipse.che.api.languageserver.registry; +package org.eclipse.che.api.languageserver; /** Factory for {@link CheLanguageClient} */ public interface CheLanguageClientFactory { diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/DefaultInstanceProvider.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/DefaultInstanceProvider.java new file mode 100644 index 00000000000..96c3b90e9fa --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/DefaultInstanceProvider.java @@ -0,0 +1,54 @@ +/* + * 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.api.languageserver; + +import static org.eclipse.lsp4j.jsonrpc.Launcher.createLauncher; + +import java.io.InputStream; +import java.io.OutputStream; +import org.eclipse.che.api.languageserver.LanguageServerConfig.InstanceProvider; +import org.eclipse.lsp4j.jsonrpc.Launcher; +import org.eclipse.lsp4j.services.LanguageClient; +import org.eclipse.lsp4j.services.LanguageServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The default way it is expected to construct language server instance + * + * @author Dmytro Kulieshov + */ +public class DefaultInstanceProvider implements InstanceProvider { + private static final Logger LOG = LoggerFactory.getLogger(DefaultInstanceProvider.class); + + private DefaultInstanceProvider() { + LOG.debug("Constructing default instance provider"); + } + + public static InstanceProvider getInstance() { + return InstanceHolder.instance; + } + + @Override + public LanguageServer get(LanguageClient client, InputStream in, OutputStream out) { + Launcher launcher = createLauncher(client, LanguageServer.class, in, out); + LOG.debug("Created launcher for language server"); + launcher.startListening(); + LOG.debug("Started listening"); + LanguageServer remoteProxy = launcher.getRemoteProxy(); + LOG.debug("Got remote proxy"); + return remoteProxy; + } + + private static class InstanceHolder { + private static final InstanceProvider instance = new DefaultInstanceProvider(); + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/EmptyCommunicationProvider.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/EmptyCommunicationProvider.java new file mode 100644 index 00000000000..82f31c8d79b --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/EmptyCommunicationProvider.java @@ -0,0 +1,86 @@ +/* + * 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.api.languageserver; + +import com.google.common.io.ByteStreams; +import java.io.InputStream; +import java.io.OutputStream; +import org.eclipse.che.api.languageserver.LanguageServerConfig.CommunicationProvider; +import org.everrest.core.tools.EmptyInputStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Empty communication provider must be used in cases where there is no need in a communication + * between language server instance and language server server in a default way (i.e. using + * input/output streams). The example of of such use case is a custom language server instance + * provider and the language server server that runs in the same virtual machine and does not use + * input/output streams for communication. + * + * @author Dmytro Kulieshov + */ +public class EmptyCommunicationProvider implements CommunicationProvider { + private static final Logger LOG = LoggerFactory.getLogger(EmptyCommunicationProvider.class); + + private InputStream inputStream; + private OutputStream outputStream; + private StatusChecker statusChecker; + + private EmptyCommunicationProvider() { + LOG.debug("Constructing empty communication provider"); + } + + public static CommunicationProvider getInstance() { + return InstanceHolder.instance; + } + + @Override + public InputStream getInputStream() { + if (inputStream == null) { + LOG.debug("Input stream is null, initializing with empty input stream"); + inputStream = new EmptyInputStream(); + } + return inputStream; + } + + @Override + public OutputStream getOutputStream() { + if (outputStream == null) { + LOG.debug("Output stream is null, initializing with null output stream"); + outputStream = ByteStreams.nullOutputStream(); + } + return outputStream; + } + + @Override + public StatusChecker getStatusChecker() { + if (statusChecker == null) { + LOG.debug("Status checker is null, initializing with default status checker"); + statusChecker = + new StatusChecker() { + @Override + public boolean isAlive() { + return true; + } + + @Override + public String getCause() { + return null; + } + }; + } + return statusChecker; + } + + private static class InstanceHolder { + private static final CommunicationProvider instance = new EmptyCommunicationProvider(); + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/ExtendedLanguageServer.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/ExtendedLanguageServer.java new file mode 100644 index 00000000000..e1f9304ef06 --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/ExtendedLanguageServer.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.api.languageserver; + +import javax.inject.Singleton; +import org.eclipse.lsp4j.ServerCapabilities; +import org.eclipse.lsp4j.services.LanguageServer; +import org.eclipse.lsp4j.services.TextDocumentService; +import org.eclipse.lsp4j.services.WorkspaceService; + +/** + * Language server wrapper extended with id and server capabilities + * + * @author Yevhen Vydolob + * @author Dmytro Kulieshov + */ +@Singleton +class ExtendedLanguageServer { + private final String id; + private final ServerCapabilities serverCapabilities; + private final LanguageServer languageServer; + + ExtendedLanguageServer( + String id, ServerCapabilities serverCapabilities, LanguageServer languageServer) { + this.id = id; + this.serverCapabilities = serverCapabilities; + this.languageServer = languageServer; + } + + String getId() { + return id; + } + + ServerCapabilities getCapabilities() { + return serverCapabilities; + } + + TextDocumentService getTextDocumentService() { + return languageServer.getTextDocumentService(); + } + + WorkspaceService getWorkspaceService() { + return languageServer.getWorkspaceService(); + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/FindId.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/FindId.java new file mode 100644 index 00000000000..77ed018dd01 --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/FindId.java @@ -0,0 +1,58 @@ +/* + * 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.api.languageserver; + +import com.google.common.collect.ImmutableSet; +import java.util.HashSet; +import java.util.Map.Entry; +import java.util.Set; +import java.util.regex.Pattern; +import javax.inject.Inject; +import javax.inject.Singleton; +import org.eclipse.che.api.languageserver.RegistryContainer.Registry; + +/** + * Utility class that simplifies finding language server id. + * + * @author Dmytro Kulieshov + */ +@Singleton +class FindId { + private final Registry> patterns; + + @Inject + FindId(RegistryContainer registryContainer) { + this.patterns = registryContainer.patternRegistry; + } + + /** + * Finds language server ids that corresponds to a specified workspace path + * + * @param wsPath absolute workspace path + * @return set of language server ids + */ + Set byPath(String wsPath) { + Set ids = new HashSet<>(); + + for (Entry> entry : patterns.getAll().entrySet()) { + String id = entry.getKey(); + Set patterns = entry.getValue(); + + for (Pattern pattern : patterns) { + if (pattern.matcher(wsPath).matches()) { + ids.add(id); + } + } + } + + return ImmutableSet.copyOf(ids); + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/FindServer.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/FindServer.java new file mode 100644 index 00000000000..8a3c377da50 --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/FindServer.java @@ -0,0 +1,69 @@ +/* + * 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.api.languageserver; + +import static java.util.stream.Collectors.toSet; + +import java.util.Objects; +import java.util.Set; +import javax.inject.Inject; +import javax.inject.Singleton; +import org.eclipse.che.api.languageserver.RegistryContainer.Registry; +import org.eclipse.lsp4j.ServerCapabilities; +import org.eclipse.lsp4j.services.LanguageServer; + +/** + * Utility class that simplifies finding of language server instances + * + * @author Dmytro Kulieshov + */ +@Singleton +class FindServer { + private final FindId findId; + private final Registry serverCapabilities; + private final Registry languageServers; + + @Inject + FindServer(RegistryContainer registryContainer, FindId findId) { + this.findId = findId; + this.serverCapabilities = registryContainer.serverCapabilitiesRegistry; + this.languageServers = registryContainer.languageServerRegistry; + } + + /** + * Finds initialized language server instances that correspond to a specified workspace path. + * + * @param wsPath absolute workspace path + * @return set of language server instances + */ + Set byPath(String wsPath) { + return findId.byPath(wsPath).stream().map(this::byId).filter(Objects::nonNull).collect(toSet()); + } + + /** + * Finds initialized language server instance that corresponds to a specified id. + * + * @param id language server id + * @return language server instance or null if no sever found + */ + ExtendedLanguageServer byId(String id) { + ServerCapabilities serverCapabilities = this.serverCapabilities.getOrNull(id); + if (serverCapabilities == null) { + return null; + } + + LanguageServer languageServer = languageServers.getOrNull(id); + if (languageServer == null) { + return null; + } + return new ExtendedLanguageServer(id, serverCapabilities, languageServer); + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/GuiceConfigProvider.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/GuiceConfigProvider.java new file mode 100644 index 00000000000..16314a3fb05 --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/GuiceConfigProvider.java @@ -0,0 +1,35 @@ +/* + * 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.api.languageserver; + +import java.util.Map; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Provides all language server configuration that is configured via Guice map binder. + * + * @author Dmytro Kulieshov + */ +@Singleton +class GuiceConfigProvider implements LanguageServerConfigProvider { + private final Map languageServerConfigs; + + @Inject + GuiceConfigProvider(Map languageServerConfigs) { + this.languageServerConfigs = languageServerConfigs; + } + + @Override + public Map getAll() { + return languageServerConfigs; + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/InitializeParamsProvider.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/InitializeParamsProvider.java new file mode 100644 index 00000000000..7ad723ea833 --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/InitializeParamsProvider.java @@ -0,0 +1,164 @@ +/* + * 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.api.languageserver; + +import static org.eclipse.che.api.fs.server.WsPathUtils.ROOT; + +import com.google.inject.Singleton; +import java.lang.management.ManagementFactory; +import java.net.URI; +import java.nio.file.Paths; +import javax.inject.Inject; +import org.eclipse.che.api.fs.server.PathTransformer; +import org.eclipse.che.api.languageserver.RegistryContainer.Registry; +import org.eclipse.lsp4j.ClientCapabilities; +import org.eclipse.lsp4j.CodeActionCapabilities; +import org.eclipse.lsp4j.CodeLensCapabilities; +import org.eclipse.lsp4j.CompletionCapabilities; +import org.eclipse.lsp4j.CompletionItemCapabilities; +import org.eclipse.lsp4j.DefinitionCapabilities; +import org.eclipse.lsp4j.DidChangeConfigurationCapabilities; +import org.eclipse.lsp4j.DidChangeWatchedFilesCapabilities; +import org.eclipse.lsp4j.DocumentHighlightCapabilities; +import org.eclipse.lsp4j.DocumentLinkCapabilities; +import org.eclipse.lsp4j.DocumentSymbolCapabilities; +import org.eclipse.lsp4j.ExecuteCommandCapabilities; +import org.eclipse.lsp4j.FormattingCapabilities; +import org.eclipse.lsp4j.HoverCapabilities; +import org.eclipse.lsp4j.InitializeParams; +import org.eclipse.lsp4j.OnTypeFormattingCapabilities; +import org.eclipse.lsp4j.RangeFormattingCapabilities; +import org.eclipse.lsp4j.ReferencesCapabilities; +import org.eclipse.lsp4j.RenameCapabilities; +import org.eclipse.lsp4j.SignatureHelpCapabilities; +import org.eclipse.lsp4j.SymbolCapabilities; +import org.eclipse.lsp4j.SynchronizationCapabilities; +import org.eclipse.lsp4j.TextDocumentClientCapabilities; +import org.eclipse.lsp4j.WorkspaceClientCapabilities; +import org.eclipse.lsp4j.WorkspaceEditCapabilities; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utility class that simplifies creation of a language server initialize parameters. + * + * @author Dmytro Kulieshov + */ +@Singleton +class InitializeParamsProvider { + private static final Logger LOG = LoggerFactory.getLogger(InitializeParamsProvider.class); + + private final Registry localityRegistry; + private final URI rootUri; + + @Inject + InitializeParamsProvider(PathTransformer pathTransformer, RegistryContainer registryContainer) { + this.rootUri = pathTransformer.transform(ROOT).toUri(); + this.localityRegistry = registryContainer.localityRegistry; + } + + InitializeParams get(String id) throws LanguageServerException { + InitializeParams initializeParams = new InitializeParams(); + LOG.debug("Initialize params constructing: started"); + + Boolean locality = localityRegistry.get(id); + LOG.debug("Locality: {}", locality); + + Integer processId = locality ? ProcessIdProvider.get() : null; + initializeParams.setProcessId(processId); + LOG.debug("Process id: {}", processId); + + String rootPath = Paths.get(rootUri).toAbsolutePath().toString(); + initializeParams.setRootPath(rootPath); + LOG.debug("Root path: {}", rootPath); + + String rootUri = this.rootUri.toString(); + initializeParams.setRootUri(rootUri); + LOG.debug("Root URI: {}", rootUri); + + ClientCapabilities capabilities = ClientCapabilitiesProvider.get(); + initializeParams.setCapabilities(capabilities); + LOG.debug("Client capabilities: {}", capabilities); + + String clientName = ClientCapabilitiesProvider.CLIENT_NAME; + initializeParams.setClientName(clientName); + LOG.debug("Client name: {}", clientName); + + LOG.debug("Initialize params constructing: finished"); + return initializeParams; + } + + /** Provides the id of current process */ + private static class ProcessIdProvider { + private static Integer processId; + + private static int get() { + if (processId == null) { + String name = ManagementFactory.getRuntimeMXBean().getName(); + int prefixEnd = name.indexOf('@'); + if (prefixEnd != -1) { + String prefix = name.substring(0, prefixEnd); + try { + processId = Integer.parseInt(prefix); + } catch (NumberFormatException e) { + LOG.error("Failed to recognize the pid of the process", e); + } + } else { + LOG.error("Failed to recognize the pid of the process"); + } + } + + return processId; + } + } + + /** Provides client capabilities */ + private static class ClientCapabilitiesProvider { + private static final String CLIENT_NAME = "EclipseChe"; + + private static ClientCapabilities clientCapabilities; + + private static ClientCapabilities get() { + if (clientCapabilities == null) { + clientCapabilities = new ClientCapabilities(); + + WorkspaceClientCapabilities workspace = new WorkspaceClientCapabilities(); + workspace.setApplyEdit(false); + workspace.setDidChangeConfiguration(new DidChangeConfigurationCapabilities()); + workspace.setDidChangeWatchedFiles(new DidChangeWatchedFilesCapabilities()); + workspace.setExecuteCommand(new ExecuteCommandCapabilities()); + workspace.setSymbol(new SymbolCapabilities()); + workspace.setWorkspaceEdit(new WorkspaceEditCapabilities()); + clientCapabilities.setWorkspace(workspace); + + TextDocumentClientCapabilities textDocument = new TextDocumentClientCapabilities(); + textDocument.setCodeAction(new CodeActionCapabilities()); + textDocument.setCodeLens(new CodeLensCapabilities()); + textDocument.setCompletion(new CompletionCapabilities(new CompletionItemCapabilities())); + textDocument.setDefinition(new DefinitionCapabilities()); + textDocument.setDocumentHighlight(new DocumentHighlightCapabilities()); + textDocument.setDocumentLink(new DocumentLinkCapabilities()); + textDocument.setDocumentSymbol(new DocumentSymbolCapabilities()); + textDocument.setFormatting(new FormattingCapabilities()); + textDocument.setHover(new HoverCapabilities()); + textDocument.setOnTypeFormatting(new OnTypeFormattingCapabilities()); + textDocument.setRangeFormatting(new RangeFormattingCapabilities()); + textDocument.setReferences(new ReferencesCapabilities()); + textDocument.setRename(new RenameCapabilities()); + textDocument.setSignatureHelp(new SignatureHelpCapabilities()); + textDocument.setSynchronization(new SynchronizationCapabilities(true, false, true)); + clientCapabilities.setTextDocument(textDocument); + } + + return clientCapabilities; + } + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerConfig.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerConfig.java new file mode 100644 index 00000000000..ccf7e3fcac4 --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerConfig.java @@ -0,0 +1,193 @@ +/* + * 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.api.languageserver; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Map; +import java.util.Set; +import org.eclipse.lsp4j.services.LanguageClient; +import org.eclipse.lsp4j.services.LanguageServer; + +/** + * Language server internal configuration that is used by corresponding components for the + * definition of language server instance launch, initialization, matching procedures, while it also + * specify language server specific configuration elements (like file watch patterns, etc.) + * + * @see LanguageServerConfigProvider + * @see LanguageServerConfigInitializer + * @author Dmytro Kulieshov + */ +public interface LanguageServerConfig { + + /** + * Get a language server regular expression provider. + * + * @return regular expression provider + */ + RegexProvider getRegexpProvider(); + + /** + * Get a language server communication provider. If there is no need for a custom communication + * provider, there is a default implementation {@link EmptyCommunicationProvider}. + * + * @return communication provider + * @see EmptyCommunicationProvider + */ + CommunicationProvider getCommunicationProvider(); + + /** + * Get a language server instance provider. If there is no need for a custom instance provider, + * there is a default implementation {@link DefaultInstanceProvider} + * + * @return instance provider + * @see DefaultInstanceProvider + */ + InstanceProvider getInstanceProvider(); + + /** + * Get an installer status provider. + * + * @return nullif a language server is not expected to be launched locally, instance + * of an installer status provider otherwise + */ + default InstallerStatusProvider getInstallerStatusProvider() { + return null; + } + + /** + * Instance provider is the definition of the way a language server instance is constructed. It is + * not restricted to provide an instance in any possible way, however the library that is uses + * lsp4j imposes its restrictions. The implementer must use {@link LanguageClient} + * and {@link LanguageServer} interfaces correspondingly. + * + * @see DefaultInstanceProvider + */ + interface InstanceProvider { + /** + * Get a new language server instance for specified communication means. + * + * @param client language server client + * @param in language server input stream + * @param out language server output stream + * @return language server instance + */ + LanguageServer get(LanguageClient client, InputStream in, OutputStream out); + } + + /** + * Provides some way we can communicate with the running language server. In general the + * implementer has no obligation as of how to launch/initialize local or remote language server + * process/server, it can be done separately and it is not required that this interface covers the + * procedure, however it is also not disallowed. + * + *

{@link SocketCommunicationProvider} and {@link ProcessCommunicationProvider} implementations + * of the interface, besides providing communication means establishes a socket connection and + * launches a language server process correspondingly. + * + * @see EmptyCommunicationProvider + * @see SocketCommunicationProvider + * @see ProcessCommunicationProvider + */ + interface CommunicationProvider { + /** + * Get input stream + * + * @return input stream + * @throws LanguageServerException if any issue happened trying to get an input stream + */ + InputStream getInputStream() throws LanguageServerException; + + /** + * Get output stream + * + * @return output stream + * @throws LanguageServerException if any issue happened trying to get an output stream + */ + OutputStream getOutputStream() throws LanguageServerException; + + /** + * Get status checker + * + * @return status checker + * @throws LanguageServerException if any issue happened trying to get a status checker + */ + StatusChecker getStatusChecker() throws LanguageServerException; + + /** + * The implementation must provide aliveness status for language server that we communicate to. + */ + interface StatusChecker { + /** + * Shows if a language server communication is alive + * + * @return true - if communication is alive, false - if communication is not alive + */ + boolean isAlive(); + + /** + * Get cause of language server communication not being alive. + * + * @return short description of why the communication is not alive, if the communication is + * alive (isAlive == true) returns null. + */ + String getCause(); + } + } + + /** Provides language server matching regular expression definitions. */ + interface RegexProvider { + /** + * Get a map of languageId->languageRegexp where languageId is the + * identifier of a language that is listed in the language server + * specification, while languageRegexp is a regular expression that matches all + * files that are related to a corresponding language. This map is used to determine which file + * belongs to which language server. + * + * @return map of language regexes + */ + Map getLanguageRegexes(); + + /** + * Get a file watch patterns for a {@link LanguageServerFileWatcher}, patterns should be + * compliant to DidChangeWatchedFiles + * notification format + * + * @return set of stringified file watch patterns + */ + Set getFileWatchPatterns(); + } + + /** + * Some language servers may be configured to be launched locally with the help of installers and + * process launchers. In order to be aware of installer's actions one must implement an installer + * status provider that will show if installer performed all required installations i.e. language + * server and all related dependencies has been properly installed. + */ + interface InstallerStatusProvider { + /** + * Shows if the installation is completed successfully + * + * @return true if successfully installed, false otherwise + */ + boolean isSuccessfullyInstalled(); + + /** + * Get the cause of unsuccessful installation. + * + * @return cause of unsuccessful installation or null if installation was + * successful + */ + String getCause(); + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerConfigInitializer.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerConfigInitializer.java new file mode 100644 index 00000000000..93ef014bf11 --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerConfigInitializer.java @@ -0,0 +1,120 @@ +/* + * 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.api.languageserver; + +import static java.util.stream.Collectors.toSet; + +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.PathMatcher; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.regex.Pattern; +import javax.inject.Inject; +import javax.inject.Singleton; +import org.eclipse.che.api.languageserver.LanguageServerConfig.CommunicationProvider; +import org.eclipse.che.api.languageserver.LanguageServerConfig.InstallerStatusProvider; +import org.eclipse.che.api.languageserver.LanguageServerConfig.InstanceProvider; +import org.eclipse.che.api.languageserver.RegistryContainer.Registry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Process all provided language server configuration and fill corresponding registries. + * + * @author Dmytro Kulieshov + */ +@Singleton +class LanguageServerConfigInitializer { + private static final Logger LOG = LoggerFactory.getLogger(LanguageServerConfigInitializer.class); + private final Registry idRegistry; + private final Registry> pathMatcherRegistry; + private final Registry> patternRegistry; + private final Registry instanceProviderRegistry; + private final Registry communicationProviderRegistry; + private final Registry localityRegistry; + private final Registry languageFilterRegistry; + private final Set providers; + + @Inject + LanguageServerConfigInitializer( + Set providers, RegistryContainer registryContainer) { + this.providers = providers; + this.idRegistry = registryContainer.idRegistry; + this.pathMatcherRegistry = registryContainer.pathMatcherRegistry; + this.patternRegistry = registryContainer.patternRegistry; + this.instanceProviderRegistry = registryContainer.instanceProviderRegistry; + this.communicationProviderRegistry = registryContainer.communicationProviderRegistry; + this.localityRegistry = registryContainer.localityRegistry; + this.languageFilterRegistry = registryContainer.languageFilterRegistry; + } + + void initialize() { + LOG.debug("Language server config processing: started"); + for (LanguageServerConfigProvider provider : providers) { + Map configs = provider.getAll(); + for (Entry entry : configs.entrySet()) { + String id = entry.getKey(); + LOG.debug("Processing for language server {}: started", id); + + if (idRegistry.contains(id)) { + LOG.debug("Language server with such id is already registered"); + continue; + } + + LanguageServerConfig config = entry.getValue(); + + InstallerStatusProvider installerStatusProvider = config.getInstallerStatusProvider(); + boolean isLocal = installerStatusProvider != null; + LOG.debug("Locality: {}", isLocal); + + if (isLocal && !installerStatusProvider.isSuccessfullyInstalled()) { + String cause = installerStatusProvider.getCause(); + LOG.debug("Installation for a language server with id '{}' is not performed", id); + LOG.debug("The cause: {}", cause); + LOG.debug("Skipping this language server configuration"); + + continue; + } + + CommunicationProvider communicationProvider = config.getCommunicationProvider(); + + InstanceProvider instanceProvider = config.getInstanceProvider(); + + Map languageRegexes = config.getRegexpProvider().getLanguageRegexes(); + LOG.debug("Language regexes: {}", languageRegexes); + + Set fileWatchPatterns = config.getRegexpProvider().getFileWatchPatterns(); + LOG.debug("File watch patterns: {}", fileWatchPatterns); + + Set patterns = + languageRegexes.values().stream().map(Pattern::compile).collect(toSet()); + + FileSystem fileSystem = FileSystems.getDefault(); + Set pathMatchers = + fileWatchPatterns.stream().map(fileSystem::getPathMatcher).collect(toSet()); + + idRegistry.add(id, id); + pathMatcherRegistry.add(id, pathMatchers); + patternRegistry.add(id, patterns); + instanceProviderRegistry.add(id, instanceProvider); + communicationProviderRegistry.add(id, communicationProvider); + localityRegistry.add(id, isLocal); + + languageRegexes.forEach(languageFilterRegistry::add); + + LOG.debug("Processing for language server {}: finished", id); + } + } + LOG.debug("Language server config processing: finished"); + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerConfigProvider.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerConfigProvider.java new file mode 100644 index 00000000000..cbd9b9f03a0 --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerConfigProvider.java @@ -0,0 +1,28 @@ +/* + * 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.api.languageserver; + +import java.util.Map; + +/** + * Provides a map of language server configurations, where the key is the language server id and the + * value is the configuration itself. + * + * @author Dmytro Kulieshov + */ +public interface LanguageServerConfigProvider { + /** + * Gets all language server configurations that are configured for this provider. + * + * @return map of language server configurations + */ + Map getAll(); +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/exception/LanguageServerException.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerException.java similarity index 85% rename from wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/exception/LanguageServerException.java rename to wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerException.java index f99e6ee0a50..1d51cf52554 100644 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/exception/LanguageServerException.java +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerException.java @@ -8,7 +8,7 @@ * Contributors: * Red Hat, Inc. - initial API and implementation */ -package org.eclipse.che.api.languageserver.exception; +package org.eclipse.che.api.languageserver; import org.eclipse.che.api.core.ServerException; @@ -22,4 +22,8 @@ public LanguageServerException(String message) { public LanguageServerException(String message, Throwable cause) { super(message, cause); } + + public LanguageServerException(Throwable cause) { + super(cause); + } } diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerFileWatcher.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerFileWatcher.java new file mode 100644 index 00000000000..f0f1bd4d1ad --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerFileWatcher.java @@ -0,0 +1,102 @@ +/* + * 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.api.languageserver; + +import static org.eclipse.che.api.languageserver.LanguageServiceUtils.prefixURI; + +import com.google.common.annotations.VisibleForTesting; +import java.nio.file.PathMatcher; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.inject.Inject; +import javax.inject.Singleton; +import org.eclipse.che.api.core.notification.EventService; +import org.eclipse.che.api.languageserver.RegistryContainer.Registry; +import org.eclipse.che.api.watcher.server.FileWatcherManager; +import org.eclipse.lsp4j.DidChangeWatchedFilesParams; +import org.eclipse.lsp4j.FileChangeType; +import org.eclipse.lsp4j.FileEvent; +import org.eclipse.lsp4j.services.LanguageServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Implement DidChangeWatchedFiles + * Notification + * + * @author Yevhen Vydolob + */ +@Singleton +class LanguageServerFileWatcher { + private static final Logger LOG = LoggerFactory.getLogger(LanguageServerFileWatcher.class); + + private final FileWatcherManager watcherManager; + private final EventService eventService; + private final CopyOnWriteArrayList watcherIds = new CopyOnWriteArrayList<>(); + private final Registry> pathMatcherRegistry; + + @Inject + LanguageServerFileWatcher( + FileWatcherManager watcherManager, + EventService eventService, + RegistryContainer registryContainer) { + this.watcherManager = watcherManager; + this.eventService = eventService; + this.pathMatcherRegistry = registryContainer.pathMatcherRegistry; + } + + @PostConstruct + private void subscribe() { + eventService.subscribe(this::onServerInitialized, LanguageServerInitializedEvent.class); + } + + private void send(LanguageServer server, String filePath, FileChangeType changeType) { + DidChangeWatchedFilesParams params = + new DidChangeWatchedFilesParams( + Collections.singletonList(new FileEvent(prefixURI(filePath), changeType))); + server.getWorkspaceService().didChangeWatchedFiles(params); + } + + @PreDestroy + @VisibleForTesting + void removeAllWatchers() { + for (Integer watcherId : watcherIds) { + watcherManager.unRegisterByMatcher(watcherId); + } + } + + private void onServerInitialized(LanguageServerInitializedEvent event) { + LOG.debug("Registering file watching operations for language server : {}", event.getId()); + + String id = event.getId(); + LanguageServer languageServer = event.getLanguageServer(); + + Set pathMatchers = pathMatcherRegistry.getOrNull(id); + if (pathMatchers == null) { + return; + } + + for (PathMatcher pathMatcher : pathMatchers) { + int watcherId = + watcherManager.registerByMatcher( + pathMatcher, + s -> send(languageServer, s, FileChangeType.Created), + s -> send(languageServer, s, FileChangeType.Changed), + s -> send(languageServer, s, FileChangeType.Deleted)); + + watcherIds.add(watcherId); + } + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerInitializedEvent.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerInitializedEvent.java new file mode 100644 index 00000000000..8f89fec3c3a --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerInitializedEvent.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.api.languageserver; + +import org.eclipse.che.api.core.notification.EventOrigin; +import org.eclipse.lsp4j.services.LanguageServer; + +/** + * Event is published when language server instance is properly initialized and ready. + * + * @author Dmytro Kulieshov + */ +@EventOrigin("languageServer") +public class LanguageServerInitializedEvent { + private final String id; + + private final LanguageServer languageServer; + + LanguageServerInitializedEvent(String id, LanguageServer languageServer) { + this.id = id; + this.languageServer = languageServer; + } + + /** + * Get initialized language server id + * + * @return language server id + */ + public String getId() { + return id; + } + + /** + * Get initialized language server instance + * + * @return language server instance + */ + public LanguageServer getLanguageServer() { + return languageServer; + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerInitializer.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerInitializer.java new file mode 100644 index 00000000000..0caa6eebb37 --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerInitializer.java @@ -0,0 +1,230 @@ +/* + * 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.api.languageserver; + +import static java.util.concurrent.CompletableFuture.supplyAsync; +import static java.util.concurrent.Executors.newCachedThreadPool; +import static java.util.concurrent.TimeUnit.SECONDS; +import static java.util.stream.Collectors.toSet; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.*; +import javax.inject.Inject; +import javax.inject.Singleton; +import org.eclipse.che.api.core.notification.EventService; +import org.eclipse.che.api.languageserver.LanguageServerConfig.CommunicationProvider; +import org.eclipse.che.api.languageserver.LanguageServerConfig.CommunicationProvider.StatusChecker; +import org.eclipse.che.api.languageserver.LanguageServerConfig.InstanceProvider; +import org.eclipse.che.api.languageserver.RegistryContainer.Registry; +import org.eclipse.che.commons.lang.Pair; +import org.eclipse.che.commons.lang.concurrent.LoggingUncaughtExceptionHandler; +import org.eclipse.lsp4j.InitializeParams; +import org.eclipse.lsp4j.InitializeResult; +import org.eclipse.lsp4j.ServerCapabilities; +import org.eclipse.lsp4j.services.LanguageServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Manages the whole language server initialization process that includes process launch/socket + * connection establishing, language server instance creation and initialization, language server + * capabilities accumulation. + * + * @author Dmytro Kulieshov + */ +@Singleton +class LanguageServerInitializer { + private static Logger LOG = LoggerFactory.getLogger(LanguageServerInitializer.class); + + private final ExecutorService executor; + + private final EventService eventService; + private final CheLanguageClientFactory cheLanguageClientFactory; + private final InitializeParamsProvider initializeParamsProvider; + private final ServerCapabilitiesAccumulator serverCapabilitiesAccumulator; + private final FindId findId; + + private final Registry idRegistry; + private final Registry languageServerRegistry; + private final Registry instanceProviderRegistry; + private final Registry serverCapabilitiesRegistry; + private final Registry> ioStreamRegistry; + private final Registry communicationProviderRegistry; + + @Inject + public LanguageServerInitializer( + ServerCapabilitiesAccumulator serverCapabilitiesAccumulator, + RegistryContainer registryContainer, + FindId findId, + EventService eventService, + CheLanguageClientFactory cheLanguageClientFactory, + InitializeParamsProvider initializeParamsProvider) { + this.executor = newCachedThreadPool(getFactory()); + + this.eventService = eventService; + this.cheLanguageClientFactory = cheLanguageClientFactory; + this.initializeParamsProvider = initializeParamsProvider; + this.serverCapabilitiesAccumulator = serverCapabilitiesAccumulator; + this.findId = findId; + + this.idRegistry = registryContainer.idRegistry; + this.languageServerRegistry = registryContainer.languageServerRegistry; + this.instanceProviderRegistry = registryContainer.instanceProviderRegistry; + this.serverCapabilitiesRegistry = registryContainer.serverCapabilitiesRegistry; + this.ioStreamRegistry = registryContainer.ioStreamRegistry; + this.communicationProviderRegistry = registryContainer.communicationProviderRegistry; + } + + private static ThreadFactory getFactory() { + return new ThreadFactoryBuilder() + .setUncaughtExceptionHandler(LoggingUncaughtExceptionHandler.getInstance()) + .setNameFormat(LanguageServerInitializer.class.getSimpleName()) + .setDaemon(true) + .build(); + } + + /** + * Initialize all language servers that match a specified workspace path. If some servers are + * already initialized does nothing. If some servers can't be initialized due to some errors at + * any point - skips them and proceeds with the rest. + * + * @param wsPath absolute workspace path + * @return accumulated server capabilities of all initialized language servers + * @throws CompletionException if no server initialized throws {@link LanguageServerException} + * wrapped by {@link CompletionException} + */ + CompletableFuture initialize(String wsPath) { + return supplyAsync( + () -> { + LOG.info("Started language servers initialization, file path '{}'", wsPath); + + Set serverCapabilitiesSet = + findId + .byPath(wsPath) + .stream() + .map(this::initializeIOStreams) + .filter(Objects::nonNull) + .map(this::createServerInstance) + .filter(Objects::nonNull) + .map(this::initializeServerInstance) + .filter(Objects::nonNull) + .map(serverCapabilitiesRegistry::getOrNull) + .filter(Objects::nonNull) + .collect(toSet()); + + LOG.info("Finished language servers initialization, file path '{}'", wsPath); + + LOG.debug("Calculating number of initialized servers and accumulating capabilities"); + if (serverCapabilitiesSet.isEmpty()) { + String message = String.format("Could not initialize any server for '%s'", wsPath); + LOG.error(message); + LanguageServerException cause = new LanguageServerException(message); + throw new CompletionException(cause); + } else { + return serverCapabilitiesSet + .stream() + .reduce(new ServerCapabilities(), serverCapabilitiesAccumulator); + } + }, + executor); + } + + private String initializeIOStreams(String id) { + try { + LOG.debug("Initializing of IO streams for server '{}': started", id); + synchronized (idRegistry.get(id)) { + if (ioStreamRegistry.contains(id)) { + LOG.debug("Already initialized"); + return id; + } + + CommunicationProvider communicationProvider = communicationProviderRegistry.get(id); + StatusChecker statusChecker = communicationProvider.getStatusChecker(); + InputStream inputStream = communicationProvider.getInputStream(); + OutputStream outputStream = communicationProvider.getOutputStream(); + + if (!statusChecker.isAlive()) { + throw new LanguageServerException(statusChecker.getCause()); + } + + LOG.debug("Initializing of IO streams for server '{}': finished", id); + return ioStreamRegistry.add(id, Pair.of(inputStream, outputStream)); + } + } catch (LanguageServerException e) { + LOG.error("Can't initialize IO streams for '{}'", id, e); + } + + return null; + } + + private String createServerInstance(String id) { + try { + LOG.debug("Creation of a language server instance for server '{}': started", id); + synchronized (idRegistry.get(id)) { + if (languageServerRegistry.contains(id)) { + LOG.debug("Already exists"); + return id; + } + + CheLanguageClient client = cheLanguageClientFactory.create(id); + InstanceProvider instanceProvider = instanceProviderRegistry.get(id); + InputStream inputStream = ioStreamRegistry.get(id).first; + OutputStream outputStream = ioStreamRegistry.get(id).second; + LanguageServer languageServer = instanceProvider.get(client, inputStream, outputStream); + + LOG.debug("Creation of a language server instance for server '{}': finished", id); + return languageServerRegistry.add(id, languageServer); + } + } catch (LanguageServerException e) { + LOG.error("Can't create language server for '{}'", id, e); + } + + return null; + } + + private String initializeServerInstance(String id) { + try { + LOG.debug("Initializing of a language server instance for server '{}': started", id); + synchronized (idRegistry.get(id)) { + if (serverCapabilitiesRegistry.contains(id)) { + LOG.debug("Already initialized"); + return id; + } + + LanguageServer languageServer = languageServerRegistry.get(id); + InitializeParams initializeParams = initializeParamsProvider.get(id); + InitializeResult initializeResult = + languageServer.initialize(initializeParams).get(30, SECONDS); + + LanguageServerInitializedEvent event = + new LanguageServerInitializedEvent(id, languageServer); + eventService.publish(event); + LOG.debug("Published a corresponding event: {}", event); + + LOG.debug("Initializing of a language server instance for server '{}': finished", id); + + LOG.info("Initialized language server '{}'", id); + return serverCapabilitiesRegistry.add(id, initializeResult.getCapabilities()); + } + } catch (LanguageServerException + | InterruptedException + | ExecutionException + | TimeoutException e) { + LOG.error("Can't initialize language server for '{}'", id, e); + } + + return null; + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerModule.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerModule.java index 7d7047a4e40..f65db99b128 100644 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerModule.java +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerModule.java @@ -10,48 +10,35 @@ */ package org.eclipse.che.api.languageserver; +import static com.google.inject.multibindings.MapBinder.newMapBinder; +import static com.google.inject.multibindings.Multibinder.newSetBinder; + import com.google.inject.AbstractModule; import com.google.inject.assistedinject.FactoryModuleBuilder; -import com.google.inject.multibindings.Multibinder; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; import org.eclipse.che.api.languageserver.messager.PublishDiagnosticsParamsJsonRpcTransmitter; import org.eclipse.che.api.languageserver.messager.ShowMessageJsonRpcTransmitter; -import org.eclipse.che.api.languageserver.registry.CheLanguageClientFactory; -import org.eclipse.che.api.languageserver.registry.DefaultLanguageRecognizer; -import org.eclipse.che.api.languageserver.registry.LanguageRecognizer; -import org.eclipse.che.api.languageserver.registry.LanguageServerFileWatcher; -import org.eclipse.che.api.languageserver.registry.LanguageServerRegistry; -import org.eclipse.che.api.languageserver.registry.LanguageServerRegistryImpl; -import org.eclipse.che.api.languageserver.registry.ServerInitializer; -import org.eclipse.che.api.languageserver.registry.ServerInitializerImpl; -import org.eclipse.che.api.languageserver.remote.LsRemoteModule; -import org.eclipse.che.api.languageserver.service.LanguageRegistryService; -import org.eclipse.che.api.languageserver.service.LanguageServerInitializationHandler; -import org.eclipse.che.api.languageserver.service.TextDocumentService; -import org.eclipse.che.api.languageserver.service.WorkspaceService; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; public class LanguageServerModule extends AbstractModule { @Override protected void configure() { - install(new LsRemoteModule()); - - bind(LanguageRecognizer.class).to(DefaultLanguageRecognizer.class); - bind(LanguageServerRegistry.class).to(LanguageServerRegistryImpl.class); - bind(ServerInitializer.class).to(ServerInitializerImpl.class); - bind(LanguageRegistryService.class); - Multibinder.newSetBinder(binder(), LanguageServerLauncher.class); - bind(WorkspaceService.class).asEagerSingleton(); bind(TextDocumentService.class).asEagerSingleton(); bind(PublishDiagnosticsParamsJsonRpcTransmitter.class).asEagerSingleton(); bind(ShowMessageJsonRpcTransmitter.class).asEagerSingleton(); - Multibinder.newSetBinder(binder(), LanguageDescription.class); - - bind(LanguageServerInitializationHandler.class).asEagerSingleton(); bind(LanguageServerFileWatcher.class).asEagerSingleton(); - bind(LanguageServerInitializationHandler.class).asEagerSingleton(); + bind(LanguageServerConfigInitializer.class).asEagerSingleton(); + bind(LanguageServerService.class).asEagerSingleton(); + install(new FactoryModuleBuilder().build(CheLanguageClientFactory.class)); + + newMapBinder(binder(), String.class, LanguageServerConfig.class); + + newSetBinder(binder(), LanguageServerConfigProvider.class) + .addBinding() + .to(WorkspaceConfigProvider.class); + newSetBinder(binder(), LanguageServerConfigProvider.class) + .addBinding() + .to(GuiceConfigProvider.class); } } diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerService.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerService.java new file mode 100644 index 00000000000..b449f58e4b9 --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerService.java @@ -0,0 +1,112 @@ +/* + * 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.api.languageserver; + +import static com.google.common.collect.Lists.newLinkedList; +import static java.util.concurrent.TimeUnit.SECONDS; + +import com.google.inject.Singleton; +import java.util.List; +import java.util.Map.Entry; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import javax.annotation.PostConstruct; +import javax.inject.Inject; +import org.eclipse.che.api.core.jsonrpc.commons.JsonRpcException; +import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator; +import org.eclipse.che.api.languageserver.RegistryContainer.Registry; +import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.LanguageRegexDto; +import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.ServerCapabilitiesDto; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Language server service that handles JSON-RPC requests related to language server initialization + * and matching. + * + * @author Dmytro Kulieshov + */ +@Singleton +class LanguageServerService { + private static final Logger LOG = LoggerFactory.getLogger(LanguageServerService.class); + + private final RequestHandlerConfigurator configurator; + private final LanguageServerInitializer languageServerInitializer; + private final Registry languageFilterRegistry; + private final LanguageServerConfigInitializer languageServerConfigInitializer; + + @Inject + LanguageServerService( + RequestHandlerConfigurator configurator, + LanguageServerInitializer languageServerInitializer, + RegistryContainer registryContainer, + LanguageServerConfigInitializer languageServerConfigInitializer) { + this.configurator = configurator; + this.languageServerInitializer = languageServerInitializer; + this.languageFilterRegistry = registryContainer.languageFilterRegistry; + this.languageServerConfigInitializer = languageServerConfigInitializer; + } + + @PostConstruct + private void configureMethodHandlers() { + configurator + .newConfiguration() + .methodName("languageServer/initialize") + .paramsAsString() + .resultAsDto(ServerCapabilitiesDto.class) + .withFunction(this::initialize); + + configurator + .newConfiguration() + .methodName("languageServer/getLanguageRegexes") + .noParams() + .resultAsListOfDto(LanguageRegexDto.class) + .withSupplier(this::getLanguageRegexes); + } + + private List getLanguageRegexes() { + LOG.debug("Received 'languageServer/getLanguageRegexes' request"); + List languageRegexes = newLinkedList(); + + languageServerConfigInitializer.initialize(); + + for (Entry entry : languageFilterRegistry.getAll().entrySet()) { + LanguageRegexDto languageRegexDto = new LanguageRegexDto(); + languageRegexDto.setNamePattern(entry.getValue()); + languageRegexDto.setLanguageId(entry.getKey()); + languageRegexes.add(languageRegexDto); + } + + LOG.debug("Responding: {}", languageRegexes); + return languageRegexes; + } + + private ServerCapabilitiesDto initialize(String wsPath) { + try { + LOG.debug("Received 'languageServer/initialize' request for path: {}", wsPath); + + languageServerConfigInitializer.initialize(); + + ServerCapabilitiesDto serverCapabilitiesDto = + new ServerCapabilitiesDto(languageServerInitializer.initialize(wsPath).get(30, SECONDS)); + + LOG.debug("Responding: {}", serverCapabilitiesDto); + return serverCapabilitiesDto; + } catch (CompletionException e) { + LOG.error("Language server initialization procedure failed", e.getCause()); + throw new JsonRpcException(-27000, e.getCause().getMessage()); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + LOG.error("Language server initialization procedure failed", e); + throw new JsonRpcException(-27000, e.getMessage()); + } + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/LanguageServiceUtils.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServiceUtils.java similarity index 86% rename from wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/LanguageServiceUtils.java rename to wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServiceUtils.java index fb03f86afc2..9bf581ac88c 100644 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/LanguageServiceUtils.java +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServiceUtils.java @@ -8,9 +8,16 @@ * Contributors: * Red Hat, Inc. - initial API and implementation */ -package org.eclipse.che.api.languageserver.service; +package org.eclipse.che.api.languageserver; -/** Language service service utilities */ +/** + * Language service service utilities + * + * @author Thomas Mäder + * @author Vitalii Parfonov + * @author Yevhen Vydolob + * @author Dmytro Kulieshov + */ public class LanguageServiceUtils { private static final String PROJECTS = "/projects"; diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/ProcessCommunicationProvider.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/ProcessCommunicationProvider.java new file mode 100644 index 00000000000..5d0075f7f08 --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/ProcessCommunicationProvider.java @@ -0,0 +1,102 @@ +/* + * 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.api.languageserver; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import org.apache.commons.io.IOUtils; +import org.eclipse.che.api.languageserver.LanguageServerConfig.CommunicationProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This implementation provides communication to a process that runs a language server. Besides that + * it also lazily starts the language server process. + * + * @author Dmytro Kulieshov + */ +public class ProcessCommunicationProvider implements CommunicationProvider { + private static final Logger LOG = LoggerFactory.getLogger(ProcessCommunicationProvider.class); + + private final ProcessBuilder processBuilder; + private final String languageServerName; + private final StatusChecker statusChecker; + + private Process process; + + public ProcessCommunicationProvider(ProcessBuilder processBuilder, String languageServerName) { + this.processBuilder = processBuilder; + this.languageServerName = languageServerName; + + this.statusChecker = + new StatusChecker() { + @Override + public boolean isAlive() { + return process != null && process.isAlive(); + } + + @Override + public String getCause() { + if (process == null) { + return "Process is NULL"; + } + + if (process.isAlive()) { + return null; + } + + try { + return IOUtils.toString(process.getErrorStream()); + } catch (IOException e) { + return e.getMessage(); + } + } + }; + } + + @Override + public InputStream getInputStream() throws LanguageServerException { + startProcessLazily(); + + return process.getInputStream(); + } + + @Override + public OutputStream getOutputStream() throws LanguageServerException { + startProcessLazily(); + + return process.getOutputStream(); + } + + @Override + public StatusChecker getStatusChecker() throws LanguageServerException { + startProcessLazily(); + + return statusChecker; + } + + private void startProcessLazily() throws LanguageServerException { + LOG.debug("Starting process lazily"); + if (process == null) { + LOG.debug("Process is not started, starting."); + try { + process = processBuilder.start(); + } catch (IOException e) { + String error = String.format("Can't start process for '%s'", languageServerName); + LOG.error(error, e); + throw new LanguageServerException(error, e); + } + } else { + LOG.debug("Process is started, skipping."); + } + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/RegistryContainer.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/RegistryContainer.java new file mode 100644 index 00000000000..e489b3735a5 --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/RegistryContainer.java @@ -0,0 +1,122 @@ +/* + * 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.api.languageserver; + +import static com.google.common.collect.Maps.newConcurrentMap; + +import com.google.common.collect.ImmutableMap; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.PathMatcher; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; +import javax.inject.Singleton; +import org.eclipse.che.api.languageserver.LanguageServerConfig.CommunicationProvider; +import org.eclipse.che.api.languageserver.LanguageServerConfig.InstanceProvider; +import org.eclipse.che.commons.lang.Pair; +import org.eclipse.lsp4j.ServerCapabilities; +import org.eclipse.lsp4j.services.LanguageServer; + +/** + * Instance contains all language server internal registries. + * + * @author Dmytro Kulieshov + */ +@Singleton +class RegistryContainer { + final Registry idRegistry = new Registry<>(); + final Registry serverCapabilitiesRegistry = new Registry<>(); + final Registry communicationProviderRegistry = new Registry<>(); + final Registry instanceProviderRegistry = new Registry<>(); + final Registry languageServerRegistry = new Registry<>(); + final Registry> ioStreamRegistry = new Registry<>(); + final Registry localityRegistry = new Registry<>(); + final Registry> pathMatcherRegistry = new Registry<>(); + final Registry> patternRegistry = new Registry<>(); + final Registry languageFilterRegistry = new Registry<>(); + + /** + * Simple parametrized registry class backed by map-based internal registry, where the key is the + * id of a language server. + * + * @param type of a registry element + */ + class Registry { + private final Map innerRegistry = newConcurrentMap(); + + /** + * Add an element to the registry + * + * @param id language server id + * @param t registry element + * @return language server id + */ + String add(String id, T t) { + innerRegistry.put(id, t); + return id; + } + + /** + * Checks if the registry already contain the value for the specified language server. + * + * @param id language server id + * @return true if contains, false if not + */ + boolean contains(String id) { + return innerRegistry.containsKey(id); + } + + /** + * Get registry element that corresponds to a specified language server. + * + * @param id language server id + * @return registry element or null is no value is stored for specified language + * server + */ + T getOrNull(String id) { + if (contains(id)) { + return innerRegistry.get(id); + } else { + return null; + } + } + + /** + * Get registry element that corresponds to a specified language server. + * + * @param id language server id + * @return registry element + * @throws LanguageServerException if no value is stored for specified language server + */ + T get(String id) throws LanguageServerException { + T t = getOrNull(id); + + if (t == null) { + String error = String.format("No element with id '%s'", id); + String explanation = "Inconsistent registry or wrong language server ID"; + String message = String.format("%s. %s", error, explanation); + throw new LanguageServerException(message); + } + + return t; + } + + /** + * Get an inner registry copy. + * + * @return copy of inner registry map. + */ + Map getAll() { + return ImmutableMap.copyOf(innerRegistry); + } + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/ServerCapabilitiesAccumulator.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/ServerCapabilitiesAccumulator.java new file mode 100644 index 00000000000..6bbd97c178f --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/ServerCapabilitiesAccumulator.java @@ -0,0 +1,219 @@ +/* + * 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.api.languageserver; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import javax.inject.Singleton; +import org.eclipse.lsp4j.CodeLensOptions; +import org.eclipse.lsp4j.CompletionOptions; +import org.eclipse.lsp4j.DocumentOnTypeFormattingOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.eclipse.lsp4j.SignatureHelpOptions; +import org.eclipse.lsp4j.TextDocumentSyncKind; +import org.eclipse.lsp4j.TextDocumentSyncOptions; +import org.eclipse.lsp4j.jsonrpc.messages.Either; + +/** Utility class to simplify the accumulation of server capabilities of multiple servers. */ +@Singleton +public class ServerCapabilitiesAccumulator implements BinaryOperator { + + @Override + public ServerCapabilities apply(ServerCapabilities left, ServerCapabilities right) { + if (left == null) { + return right; + } else { + return new ServerCapabilitiesOverlay(left, right).compute(); + } + } + + /** @author Thomas Mäder */ + private static class ServerCapabilitiesOverlay { + private ServerCapabilities left; + private ServerCapabilities right; + + private ServerCapabilitiesOverlay(ServerCapabilities left, ServerCapabilities right) { + this.left = left; + this.right = right; + } + + private CodeLensOptions getCodeLensProvider() { + CodeLensOptions leftOptions = left.getCodeLensProvider(); + CodeLensOptions rightOptions = right.getCodeLensProvider(); + if (leftOptions == null) { + return rightOptions; + } + if (rightOptions == null) { + return leftOptions; + } + CodeLensOptions result = new CodeLensOptions(); + if (leftOptions.isResolveProvider() || rightOptions.isResolveProvider()) { + result.setResolveProvider(true); + } + return result; + } + + private CompletionOptions getCompletionProvider() { + CompletionOptions leftOptions = left.getCompletionProvider(); + CompletionOptions rightOptions = right.getCompletionProvider(); + if (leftOptions == null) { + return rightOptions; + } + if (rightOptions == null) { + return leftOptions; + } + + CompletionOptions result = new CompletionOptions(); + List triggerChars = new ArrayList<>(); + + triggerChars.addAll(listish(leftOptions.getTriggerCharacters())); + triggerChars.addAll(listish(rightOptions.getTriggerCharacters())); + result.setTriggerCharacters(triggerChars); + return result; + } + + private DocumentOnTypeFormattingOptions getDocumentOnTypeFormattingProvider() { + DocumentOnTypeFormattingOptions leftOptions = left.getDocumentOnTypeFormattingProvider(); + DocumentOnTypeFormattingOptions rightOptions = right.getDocumentOnTypeFormattingProvider(); + if (leftOptions == null) { + return rightOptions; + } + if (rightOptions == null) { + return leftOptions; + } + + DocumentOnTypeFormattingOptions result = new DocumentOnTypeFormattingOptions(); + List triggerChars = new ArrayList<>(); + + result.setFirstTriggerCharacter(leftOptions.getFirstTriggerCharacter()); + triggerChars.addAll(listish(leftOptions.getMoreTriggerCharacter())); + triggerChars.addAll(listish(rightOptions.getMoreTriggerCharacter())); + result.setMoreTriggerCharacter(triggerChars); + return result; + } + + private SignatureHelpOptions getSignatureHelpProvider() { + SignatureHelpOptions leftOptions = left.getSignatureHelpProvider(); + SignatureHelpOptions rightOptions = right.getSignatureHelpProvider(); + if (leftOptions == null) { + return rightOptions; + } + if (rightOptions == null) { + return leftOptions; + } + SignatureHelpOptions result = new SignatureHelpOptions(); + + List triggerChars = new ArrayList<>(); + + triggerChars.addAll(listish(leftOptions.getTriggerCharacters())); + triggerChars.addAll(listish(rightOptions.getTriggerCharacters())); + result.setTriggerCharacters(triggerChars); + return result; + } + + private Either getTextDocumentSync() { + return mergeTextDocumentSync(left.getTextDocumentSync(), right.getTextDocumentSync()); + } + + private Either mergeTextDocumentSync( + Either left, + Either right) { + if (left == null) { + return right; + } + if (right == null) { + return left; + } + if (left.equals(right)) { + return left; + } + + if (left.isLeft() && left.getLeft() == TextDocumentSyncKind.Full) { + return left; + } + + if (left.isLeft() && left.getLeft() == TextDocumentSyncKind.Incremental) { + if (right.isLeft() && right.getLeft() == TextDocumentSyncKind.Full) { + return right; + } else { + return left; + } + } + + if (left.isRight() && right.isRight()) { + TextDocumentSyncOptions leftRight = left.getRight(); + TextDocumentSyncOptions rightRight = right.getRight(); + if (leftRight.getChange() == TextDocumentSyncKind.Full) { + return left; + } + + if (leftRight.getChange() == TextDocumentSyncKind.Incremental) { + if (rightRight.getChange() == TextDocumentSyncKind.Full) { + return right; + } else { + return left; + } + } + } + + if (left.isLeft() && right.isRight()) { + return right; + } + + if (left.isRight() && right.isLeft()) { + return left; + } + return right; + } + + private Boolean or(Function f) { + Boolean leftVal = f.apply(left); + Boolean rightVal = f.apply(right); + if (leftVal == null) { + return rightVal; + } + if (rightVal == null) { + return leftVal; + } + return leftVal || rightVal; + } + + private List listish(List list) { + return list == null ? Collections.emptyList() : list; + } + + private ServerCapabilities compute() { + + ServerCapabilities result = new ServerCapabilities(); + result.setCodeActionProvider(or(ServerCapabilities::getCodeActionProvider)); + result.setCodeLensProvider(getCodeLensProvider()); + result.setCompletionProvider(getCompletionProvider()); + result.setDefinitionProvider(or(ServerCapabilities::getDefinitionProvider)); + result.setDocumentFormattingProvider(or(ServerCapabilities::getDocumentFormattingProvider)); + result.setDocumentHighlightProvider(or(ServerCapabilities::getDocumentHighlightProvider)); + result.setDocumentOnTypeFormattingProvider(getDocumentOnTypeFormattingProvider()); + result.setDocumentRangeFormattingProvider( + or(ServerCapabilities::getDocumentRangeFormattingProvider)); + result.setDocumentSymbolProvider(or(ServerCapabilities::getDocumentSymbolProvider)); + result.setHoverProvider(or(ServerCapabilities::getHoverProvider)); + result.setReferencesProvider(or(ServerCapabilities::getReferencesProvider)); + result.setRenameProvider(or(ServerCapabilities::getRenameProvider)); + result.setSignatureHelpProvider(getSignatureHelpProvider()); + result.setTextDocumentSync(getTextDocumentSync()); + result.setWorkspaceSymbolProvider(or(ServerCapabilities::getWorkspaceSymbolProvider)); + + return result; + } + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/SocketCommunicationProvider.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/SocketCommunicationProvider.java new file mode 100644 index 00000000000..2f8d32f3ae8 --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/SocketCommunicationProvider.java @@ -0,0 +1,112 @@ +/* + * 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.api.languageserver; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.net.URI; +import org.eclipse.che.api.languageserver.LanguageServerConfig.CommunicationProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This implementation provides communication to a socket that corresponds to a server that runs a + * language server. Besides that it also lazily establishes a socket connection. + * + * @author Dmytro Kulieshov + */ +class SocketCommunicationProvider implements CommunicationProvider { + private static final Logger LOG = LoggerFactory.getLogger(SocketCommunicationProvider.class); + + private final String host; + private final int port; + private final StatusChecker statusChecker; + + private Socket socket; + + SocketCommunicationProvider(URI uri) { + this.host = uri.getHost(); + this.port = uri.getPort(); + + this.statusChecker = + new StatusChecker() { + @Override + public boolean isAlive() { + return socket != null && !socket.isInputShutdown() && !socket.isOutputShutdown(); + } + + @Override + public String getCause() { + if (socket == null) { + return "Socket is NULL"; + } else if (socket.isInputShutdown()) { + return "Socket input is shut down"; + } else if (socket.isOutputShutdown()) { + return "Socket output is shut down"; + } else { + return "Unknown reason"; + } + } + }; + } + + @Override + public InputStream getInputStream() throws LanguageServerException { + establishSocketConnectionLazily(); + + try { + return socket.getInputStream(); + } catch (IOException e) { + String error = String.format("Can't get socket input stream for: %s:%s", host, port); + LOG.error(error, e); + throw new LanguageServerException(error, e); + } + } + + @Override + public OutputStream getOutputStream() throws LanguageServerException { + establishSocketConnectionLazily(); + try { + return socket.getOutputStream(); + } catch (IOException e) { + String error = String.format("Can't get socket output stream for: %s:%s", host, port); + LOG.error(error, e); + throw new LanguageServerException(error, e); + } + } + + @Override + public StatusChecker getStatusChecker() throws LanguageServerException { + establishSocketConnectionLazily(); + + return statusChecker; + } + + private void establishSocketConnectionLazily() throws LanguageServerException { + LOG.debug("Establishing socket connection lazily"); + try { + if (socket == null) { + LOG.debug("Not established, establishing"); + + socket = new Socket(host, port); + socket.setKeepAlive(true); + } else { + LOG.debug("Established, skipping"); + } + } catch (IOException e) { + String error = String.format("Can't establish socket connection for: %s:%s", host, port); + LOG.error(error, e); + throw new LanguageServerException(error, e); + } + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/TextDocumentService.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/TextDocumentService.java new file mode 100644 index 00000000000..6eaf353643d --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/TextDocumentService.java @@ -0,0 +1,771 @@ +/* + * 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.api.languageserver; + +import static com.google.common.collect.Lists.newLinkedList; +import static java.util.Collections.emptyList; +import static org.eclipse.che.api.languageserver.LanguageServiceUtils.isStartWithProject; +import static org.eclipse.che.api.languageserver.LanguageServiceUtils.prefixProject; +import static org.eclipse.che.api.languageserver.LanguageServiceUtils.prefixURI; +import static org.eclipse.che.api.languageserver.LanguageServiceUtils.removePrefixUri; +import static org.eclipse.che.api.languageserver.LanguageServiceUtils.removeUriScheme; + +import com.google.inject.Singleton; +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; +import javax.annotation.PostConstruct; +import javax.inject.Inject; +import org.eclipse.che.api.core.jsonrpc.commons.JsonRpcException; +import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator; +import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.CommandDto; +import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.CompletionItemDto; +import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.DocumentHighlightDto; +import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.ExtendedCompletionItemDto; +import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.ExtendedCompletionListDto; +import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.HoverDto; +import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.LocationDto; +import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.RenameResultDto; +import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.SignatureHelpDto; +import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.SymbolInformationDto; +import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.TextEditDto; +import org.eclipse.che.api.languageserver.shared.model.*; +import org.eclipse.che.api.languageserver.util.LSOperation; +import org.eclipse.che.api.languageserver.util.OperationUtil; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.IRegion; +import org.eclipse.lsp4j.CodeActionParams; +import org.eclipse.lsp4j.Command; +import org.eclipse.lsp4j.CompletionItem; +import org.eclipse.lsp4j.CompletionList; +import org.eclipse.lsp4j.DidChangeTextDocumentParams; +import org.eclipse.lsp4j.DidCloseTextDocumentParams; +import org.eclipse.lsp4j.DidOpenTextDocumentParams; +import org.eclipse.lsp4j.DidSaveTextDocumentParams; +import org.eclipse.lsp4j.DocumentFormattingParams; +import org.eclipse.lsp4j.DocumentHighlight; +import org.eclipse.lsp4j.DocumentOnTypeFormattingParams; +import org.eclipse.lsp4j.DocumentRangeFormattingParams; +import org.eclipse.lsp4j.DocumentSymbolParams; +import org.eclipse.lsp4j.Hover; +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.ReferenceParams; +import org.eclipse.lsp4j.RenameParams; +import org.eclipse.lsp4j.SignatureHelp; +import org.eclipse.lsp4j.SymbolInformation; +import org.eclipse.lsp4j.TextDocumentEdit; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.eclipse.lsp4j.TextDocumentPositionParams; +import org.eclipse.lsp4j.TextEdit; +import org.eclipse.lsp4j.VersionedTextDocumentIdentifier; +import org.eclipse.lsp4j.WorkspaceEdit; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Json RPC API for the textDoc + * + *

Dispatches onto the {@link LanguageServerInitializer}. + * + * @author Thomas Mäder + */ +@Singleton +public class TextDocumentService { + private static final Logger LOG = LoggerFactory.getLogger(TextDocumentService.class); + + private final FindServer findServer; + private final RequestHandlerConfigurator requestHandler; + + @Inject + public TextDocumentService(FindServer findServer, RequestHandlerConfigurator requestHandler) { + this.findServer = findServer; + this.requestHandler = requestHandler; + } + + @PostConstruct + public void configureMethods() { + dtoToDtoList( + "definition", TextDocumentPositionParams.class, LocationDto.class, this::definition); + dtoToDtoList("codeAction", CodeActionParams.class, CommandDto.class, this::codeAction); + dtoToDtoList( + "documentSymbol", + DocumentSymbolParams.class, + SymbolInformationDto.class, + this::documentSymbol); + dtoToDtoList("formatting", DocumentFormattingParams.class, TextEditDto.class, this::formatting); + dtoToDtoList( + "rangeFormatting", + DocumentRangeFormattingParams.class, + TextEditDto.class, + this::rangeFormatting); + dtoToDtoList("references", ReferenceParams.class, LocationDto.class, this::references); + dtoToDtoList( + "onTypeFormatting", + DocumentOnTypeFormattingParams.class, + TextEditDto.class, + this::onTypeFormatting); + + dtoToDto( + "completionItem/resolve", + ExtendedCompletionItem.class, + ExtendedCompletionItemDto.class, + this::completionItemResolve); + dtoToDto( + "documentHighlight", + TextDocumentPositionParams.class, + DocumentHighlight.class, + this::documentHighlight); + dtoToDto( + "completion", + TextDocumentPositionParams.class, + ExtendedCompletionListDto.class, + this::completion); + dtoToDto("hover", TextDocumentPositionParams.class, HoverDto.class, this::hover); + dtoToDto( + "signatureHelp", + TextDocumentPositionParams.class, + SignatureHelpDto.class, + this::signatureHelp); + + dtoToDto("rename", RenameParams.class, RenameResultDto.class, this::rename); + + dtoToNothing("didChange", DidChangeTextDocumentParams.class, this::didChange); + dtoToNothing("didClose", DidCloseTextDocumentParams.class, this::didClose); + dtoToNothing("didOpen", DidOpenTextDocumentParams.class, this::didOpen); + dtoToNothing("didSave", DidSaveTextDocumentParams.class, this::didSave); + } + + private List codeAction(CodeActionParams params) { + TextDocumentIdentifier textDocument = params.getTextDocument(); + String wsPath = textDocument.getUri(); + String uri = prefixURI(wsPath); + textDocument.setUri(uri); + List result = new ArrayList<>(); + Set servers = findServer.byPath(wsPath); + LSOperation> op = + new LSOperation>() { + + @Override + public boolean canDo(ExtendedLanguageServer server) { + return truish(server.getCapabilities().getCodeActionProvider()); + } + + @Override + public CompletableFuture> start(ExtendedLanguageServer element) { + return element.getTextDocumentService().codeAction(params); + } + + @Override + public boolean handleResult(ExtendedLanguageServer element, List res) { + for (Command cmd : res) { + result.add(new CommandDto(cmd)); + } + return false; + } + }; + OperationUtil.doInParallel(servers, op, 10000); + return result; + } + + private ExtendedCompletionListDto completion( + TextDocumentPositionParams textDocumentPositionParams) { + TextDocumentIdentifier textDocument = textDocumentPositionParams.getTextDocument(); + String wsPath = textDocument.getUri(); + String uri = prefixURI(wsPath); + textDocument.setUri(uri); + textDocumentPositionParams.setUri(prefixURI(textDocumentPositionParams.getUri())); + + ExtendedCompletionListDto[] result = new ExtendedCompletionListDto[1]; + result[0] = new ExtendedCompletionListDto(); + result[0].setInComplete(true); + result[0].setItems(newLinkedList()); + + LSOperation, CompletionList>> lsOperation = + new LSOperation, CompletionList>>() { + @Override + public boolean canDo(ExtendedLanguageServer element) { + return element.getCapabilities().getCompletionProvider() != null; + } + + @Override + public CompletableFuture, CompletionList>> start( + ExtendedLanguageServer element) { + return element.getTextDocumentService().completion(textDocumentPositionParams); + } + + @Override + public boolean handleResult( + ExtendedLanguageServer element, Either, CompletionList> r) { + List items = newLinkedList(); + + List itemList; + if (r.isRight()) { + result[0].setInComplete(result[0].isInComplete() && r.getRight().isIncomplete()); + itemList = r.getRight().getItems(); + } else { + itemList = r.getLeft(); + } + + for (CompletionItem item : itemList) { + ExtendedCompletionItem exItem = new ExtendedCompletionItemDto(); + exItem.setItem(new CompletionItemDto(item)); + exItem.setLanguageServerId(element.getId()); + items.add(exItem); + } + + result[0].getItems().addAll(items); + return false; + } + }; + + Set languageServers = findServer.byPath(wsPath); + OperationUtil.doInSequence(languageServers, lsOperation, 10000); + + return result[0]; + } + + private List documentSymbol(DocumentSymbolParams documentSymbolParams) { + String wsPath = documentSymbolParams.getTextDocument().getUri(); + String uri = prefixURI(wsPath); + documentSymbolParams.getTextDocument().setUri(uri); + List result = new ArrayList<>(); + Set servers = findServer.byPath(wsPath); + + OperationUtil.doInParallel( + servers, + new LSOperation>() { + + @Override + public boolean canDo(ExtendedLanguageServer element) { + return truish(element.getCapabilities().getDocumentSymbolProvider()); + } + + @Override + public CompletableFuture> start( + ExtendedLanguageServer element) { + return element.getTextDocumentService().documentSymbol(documentSymbolParams); + } + + @Override + public boolean handleResult( + ExtendedLanguageServer element, List locations) { + locations.forEach( + o -> { + o.getLocation().setUri(removePrefixUri(o.getLocation().getUri())); + result.add(new SymbolInformationDto(o)); + }); + return true; + } + }, + 10000); + return result; + } + + private List references(ReferenceParams referenceParams) { + String wsPath = referenceParams.getTextDocument().getUri(); + String uri = prefixURI(wsPath); + referenceParams.getTextDocument().setUri(uri); + List result = new ArrayList<>(); + Set servers = findServer.byPath(wsPath); + OperationUtil.doInParallel( + servers, + new LSOperation>() { + + @Override + public boolean canDo(ExtendedLanguageServer element) { + return truish(element.getCapabilities().getReferencesProvider()); + } + + @Override + public CompletableFuture> start(ExtendedLanguageServer element) { + return element.getTextDocumentService().references(referenceParams); + } + + @Override + public boolean handleResult( + ExtendedLanguageServer element, List locations) { + locations.forEach( + o -> { + o.setUri(removePrefixUri(o.getUri())); + result.add(new LocationDto(o)); + }); + return true; + } + }, + 30000); + return result; + } + + private List definition(TextDocumentPositionParams textDocumentPositionParams) { + String wsPath = textDocumentPositionParams.getTextDocument().getUri(); + String uri = prefixURI(wsPath); + textDocumentPositionParams.getTextDocument().setUri(uri); + Set servers = findServer.byPath(wsPath); + List result = new ArrayList<>(); + OperationUtil.doInParallel( + servers, + new LSOperation>() { + + @Override + public boolean canDo(ExtendedLanguageServer element) { + return truish(element.getCapabilities().getDefinitionProvider()); + } + + @Override + public CompletableFuture> start(ExtendedLanguageServer element) { + return element.getTextDocumentService().definition(textDocumentPositionParams); + } + + @Override + public boolean handleResult( + ExtendedLanguageServer element, List locations) { + locations.forEach( + o -> { + o.setUri(removePrefixUri(o.getUri())); + result.add(new LocationDto(o)); + }); + return true; + } + }, + 30000); + return result; + } + + private ExtendedCompletionItemDto completionItemResolve(ExtendedCompletionItem unresolved) { + try { + ExtendedLanguageServer languageServer = findServer.byId(unresolved.getLanguageServerId()); + + if (languageServer == null) { + return new ExtendedCompletionItemDto(unresolved); + } else { + ExtendedCompletionItem res = new ExtendedCompletionItem(); + res.setItem( + languageServer + .getTextDocumentService() + .resolveCompletionItem(unresolved.getItem()) + .get()); + res.setLanguageServerId(unresolved.getLanguageServerId()); + return new ExtendedCompletionItemDto(res); + } + } catch (InterruptedException | ExecutionException e) { + throw new JsonRpcException(-27000, e.getMessage()); + } + } + + private HoverDto hover(TextDocumentPositionParams positionParams) { + String wsPath = positionParams.getTextDocument().getUri(); + String uri = prefixURI(wsPath); + positionParams.getTextDocument().setUri(uri); + positionParams.setUri(prefixURI(positionParams.getUri())); + HoverDto result = new HoverDto(); + result.setContents(new ArrayList<>()); + + Set servers = findServer.byPath(uri); + OperationUtil.doInParallel( + servers, + new LSOperation() { + + @Override + public boolean canDo(ExtendedLanguageServer element) { + return truish(element.getCapabilities().getHoverProvider()); + } + + @Override + public CompletableFuture start(ExtendedLanguageServer element) { + return element.getTextDocumentService().hover(positionParams); + } + + @Override + public boolean handleResult(ExtendedLanguageServer element, Hover hover) { + if (hover != null) { + HoverDto hoverDto = new HoverDto(hover); + result.getContents().addAll(hoverDto.getContents()); + } + return true; + } + }, + 10000); + return result; + } + + private SignatureHelpDto signatureHelp(TextDocumentPositionParams positionParams) { + String wsPath = positionParams.getTextDocument().getUri(); + String uri = prefixURI(wsPath); + positionParams.getTextDocument().setUri(uri); + positionParams.setUri(prefixURI(positionParams.getUri())); + SignatureHelpDto[] result = new SignatureHelpDto[1]; + Set servers = findServer.byPath(wsPath); + LSOperation op = + new LSOperation() { + + @Override + public boolean canDo(ExtendedLanguageServer element) { + return element.getCapabilities().getSignatureHelpProvider() != null; + } + + @Override + public CompletableFuture start(ExtendedLanguageServer element) { + return element.getTextDocumentService().signatureHelp(positionParams); + } + + @Override + public boolean handleResult(ExtendedLanguageServer element, SignatureHelp res) { + if (res != null && !res.getSignatures().isEmpty()) { + result[0] = new SignatureHelpDto(res); + return true; + } + return false; + } + }; + OperationUtil.doInSequence(servers, op, 10000); + return result[0]; + } + + private List formatting(DocumentFormattingParams documentFormattingParams) { + try { + String wsPath = documentFormattingParams.getTextDocument().getUri(); + String uri = prefixURI(wsPath); + documentFormattingParams.getTextDocument().setUri(uri); + Optional serverOptional = + findServer + .byPath(wsPath) + .stream() + .filter(s -> truish(s.getCapabilities().getDocumentFormattingProvider())) + .findFirst(); + if (serverOptional.isPresent()) { + return serverOptional + .get() + .getTextDocumentService() + .formatting(documentFormattingParams) + .get(5000, TimeUnit.MILLISECONDS) + .stream() + .map(TextEditDto::new) + .collect(Collectors.toList()); + } else { + return emptyList(); + } + } catch (InterruptedException | ExecutionException | TimeoutException e) { + throw new JsonRpcException(-27000, e.getMessage()); + } + } + + private List rangeFormatting( + DocumentRangeFormattingParams documentRangeFormattingParams) { + try { + String wsPath = documentRangeFormattingParams.getTextDocument().getUri(); + String uri = prefixURI(wsPath); + documentRangeFormattingParams.getTextDocument().setUri(uri); + Optional serverOptional = + findServer + .byPath(wsPath) + .stream() + .filter(s -> truish(s.getCapabilities().getDocumentRangeFormattingProvider())) + .findFirst(); + if (serverOptional.isPresent()) { + return serverOptional + .get() + .getTextDocumentService() + .rangeFormatting(documentRangeFormattingParams) + .get() + .stream() + .map(TextEditDto::new) + .collect(Collectors.toList()); + } else { + return emptyList(); + } + + } catch (InterruptedException | ExecutionException e) { + throw new JsonRpcException(-27000, e.getMessage()); + } + } + + private List onTypeFormatting( + DocumentOnTypeFormattingParams documentOnTypeFormattingParams) { + try { + String wsPath = documentOnTypeFormattingParams.getTextDocument().getUri(); + String uri = prefixURI(wsPath); + documentOnTypeFormattingParams.getTextDocument().setUri(uri); + Optional serverOptional = + findServer + .byPath(wsPath) + .stream() + .filter(it -> it.getCapabilities().getDocumentOnTypeFormattingProvider() != null) + .findFirst(); + if (serverOptional.isPresent()) { + return serverOptional + .get() + .getTextDocumentService() + .onTypeFormatting(documentOnTypeFormattingParams) + .get() + .stream() + .map(TextEditDto::new) + .collect(Collectors.toList()); + } else { + return emptyList(); + } + } catch (InterruptedException | ExecutionException e) { + throw new JsonRpcException(-27000, e.getMessage()); + } + } + + private void didChange(DidChangeTextDocumentParams didChangeTextDocumentParams) { + String wsPath = didChangeTextDocumentParams.getTextDocument().getUri(); + String uri = prefixURI(wsPath); + didChangeTextDocumentParams.getTextDocument().setUri(uri); + didChangeTextDocumentParams.setUri(prefixURI(didChangeTextDocumentParams.getUri())); + findServer + .byPath(wsPath) + .forEach(server -> server.getTextDocumentService().didChange(didChangeTextDocumentParams)); + } + + private void didOpen(DidOpenTextDocumentParams openTextDocumentParams) { + String wsPath = openTextDocumentParams.getTextDocument().getUri(); + String uri = prefixURI(wsPath); + openTextDocumentParams.getTextDocument().setUri(uri); + findServer + .byPath(wsPath) + .forEach(server -> server.getTextDocumentService().didOpen(openTextDocumentParams)); + } + + private void didClose(DidCloseTextDocumentParams didCloseTextDocumentParams) { + String wsPath = didCloseTextDocumentParams.getTextDocument().getUri(); + String uri = prefixURI(wsPath); + didCloseTextDocumentParams.getTextDocument().setUri(uri); + findServer + .byPath(wsPath) + .forEach(server -> server.getTextDocumentService().didClose(didCloseTextDocumentParams)); + } + + private void didSave(DidSaveTextDocumentParams didSaveTextDocumentParams) { + String wsPath = didSaveTextDocumentParams.getTextDocument().getUri(); + String uri = prefixURI(wsPath); + didSaveTextDocumentParams.getTextDocument().setUri(uri); + findServer + .byPath(wsPath) + .forEach(server -> server.getTextDocumentService().didSave(didSaveTextDocumentParams)); + } + + private DocumentHighlightDto documentHighlight( + TextDocumentPositionParams textDocumentPositionParams) { + String wsPath = textDocumentPositionParams.getTextDocument().getUri(); + String uri = prefixURI(wsPath); + textDocumentPositionParams.getTextDocument().setUri(uri); + @SuppressWarnings("unchecked") + List[] result = new List[1]; + LSOperation> op = + new LSOperation>() { + + @Override + public boolean canDo(ExtendedLanguageServer servers) { + return true; + } + + @Override + public CompletableFuture> start( + ExtendedLanguageServer element) { + return CompletableFuture.supplyAsync( + () -> { + List res = new ArrayList<>(); + LSOperation> op2 = + new LSOperation>() { + + @Override + public boolean canDo(ExtendedLanguageServer lsProxy) { + return truish(lsProxy.getCapabilities().getDocumentHighlightProvider()); + } + + @Override + public CompletableFuture> start( + ExtendedLanguageServer element) { + return element + .getTextDocumentService() + .documentHighlight(textDocumentPositionParams); + } + + @Override + public boolean handleResult( + ExtendedLanguageServer element, + List result) { + + return false; + } + }; + OperationUtil.doInParallel(Collections.singleton(element), op2, 10000); + + return res; + }); + } + + @Override + public boolean handleResult( + ExtendedLanguageServer element, List list) { + result[0] = list; + return !list.isEmpty(); + } + }; + OperationUtil.doInSequence(findServer.byPath(wsPath), op, 10000); + + if (!result[0].isEmpty()) { + return result[0].get(0); + } + return null; + } + + private RenameResultDto rename(RenameParams renameParams) { + String wsPath = renameParams.getTextDocument().getUri(); + String uri = prefixURI(wsPath); + renameParams.getTextDocument().setUri(uri); + Map edits = new ConcurrentHashMap<>(); + Set servers = findServer.byPath(wsPath); + LSOperation op = + new LSOperation() { + @Override + public boolean canDo(ExtendedLanguageServer server) { + Boolean renameProvider = server.getCapabilities().getRenameProvider(); + return renameProvider != null && renameProvider; + } + + @Override + public CompletableFuture start(ExtendedLanguageServer element) { + return element.getTextDocumentService().rename(renameParams); + } + + @Override + public boolean handleResult(ExtendedLanguageServer element, WorkspaceEdit result) { + + addRenameResult(edits, element.getId(), result); + return true; + } + }; + OperationUtil.doInParallel(servers, op, TimeUnit.SECONDS.toMillis(30)); + return new RenameResultDto(new RenameResult(edits)); + } + + private void addRenameResult( + Map map, String id, WorkspaceEdit workspaceEdit) { + + ExtendedWorkspaceEdit result = new ExtendedWorkspaceEdit(); + List edits = new ArrayList<>(); + if (workspaceEdit.getDocumentChanges() != null) { + for (TextDocumentEdit documentEdit : workspaceEdit.getDocumentChanges()) { + ExtendedTextDocumentEdit edit = new ExtendedTextDocumentEdit(); + edit.setTextDocument(documentEdit.getTextDocument()); + edit.getTextDocument().setUri(removePrefixUri(edit.getTextDocument().getUri())); + edit.setEdits( + convertToExtendedEdit( + documentEdit.getEdits(), removeUriScheme(documentEdit.getTextDocument().getUri()))); + edits.add(edit); + } + } else if (workspaceEdit.getChanges() != null) { + for (Entry> entry : workspaceEdit.getChanges().entrySet()) { + ExtendedTextDocumentEdit edit = new ExtendedTextDocumentEdit(); + VersionedTextDocumentIdentifier documentIdentifier = new VersionedTextDocumentIdentifier(); + documentIdentifier.setVersion(-1); + documentIdentifier.setUri(removePrefixUri(entry.getKey())); + edit.setTextDocument(documentIdentifier); + edit.setEdits(convertToExtendedEdit(entry.getValue(), removeUriScheme(entry.getKey()))); + edits.add(edit); + } + } + + if (!edits.isEmpty()) { + result.setDocumentChanges(edits); + map.put(id, result); + } + } + + private List convertToExtendedEdit(List edits, String filePath) { + try { + // for some reason C# LS sends ws related path, + if (!isStartWithProject(filePath)) { + filePath = prefixProject(filePath); + } + String fileContent = + com.google.common.io.Files.toString(new File(filePath), Charset.defaultCharset()); + Document document = new Document(fileContent); + return edits + .stream() + .map( + textEdit -> { + ExtendedTextEdit result = new ExtendedTextEdit(); + result.setRange(textEdit.getRange()); + result.setNewText(textEdit.getNewText()); + try { + IRegion lineInformation = + document.getLineInformation(textEdit.getRange().getStart().getLine()); + String lineText = + document.get(lineInformation.getOffset(), lineInformation.getLength()); + result.setLineText(lineText); + result.setInLineStart(textEdit.getRange().getStart().getCharacter()); + result.setInLineEnd(textEdit.getRange().getEnd().getCharacter()); + } catch (BadLocationException e) { + LOG.error("Can't read file line", e); + } + + return result; + }) + .collect(Collectors.toList()); + } catch (IOException e) { + LOG.error("Can't read file", e); + } + return emptyList(); + } + + private

void dtoToNothing(String name, Class

pClass, Consumer

consumer) { + requestHandler + .newConfiguration() + .methodName("textDocument/" + name) + .paramsAsDto(pClass) + .noResult() + .withConsumer(consumer); + } + + private void dtoToDtoList( + String name, Class

pClass, Class rClass, Function> function) { + requestHandler + .newConfiguration() + .methodName("textDocument/" + name) + .paramsAsDto(pClass) + .resultAsListOfDto(rClass) + .withFunction(function); + } + + private void dtoToDto( + String name, Class

pClass, Class rClass, Function function) { + requestHandler + .newConfiguration() + .methodName("textDocument/" + name) + .paramsAsDto(pClass) + .resultAsDto(rClass) + .withFunction(function); + } + + private boolean truish(Boolean b) { + return b != null && b; + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/WorkspaceConfigProvider.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/WorkspaceConfigProvider.java new file mode 100644 index 00000000000..04269aaf9c0 --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/WorkspaceConfigProvider.java @@ -0,0 +1,218 @@ +/* + * 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.api.languageserver; + +import static com.google.common.collect.Maps.newHashMap; +import static com.google.common.collect.Sets.newHashSet; +import static java.util.Collections.emptyMap; +import static java.util.Collections.emptySet; +import static java.util.stream.Collectors.toMap; +import static java.util.stream.Collectors.toSet; +import static javax.ws.rs.core.UriBuilder.fromUri; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import org.eclipse.che.api.core.ApiException; +import org.eclipse.che.api.core.model.workspace.Runtime; +import org.eclipse.che.api.core.model.workspace.Workspace; +import org.eclipse.che.api.core.model.workspace.runtime.Machine; +import org.eclipse.che.api.core.model.workspace.runtime.Server; +import org.eclipse.che.api.core.rest.HttpJsonRequestFactory; +import org.eclipse.che.api.languageserver.LanguageServerConfig.CommunicationProvider; +import org.eclipse.che.api.workspace.server.WorkspaceService; +import org.eclipse.che.api.workspace.shared.dto.WorkspaceDto; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides all language server configuration that is defined within the workspace configuration. + * + * @author Dmytro Kulieshov + */ +@Singleton +class WorkspaceConfigProvider implements LanguageServerConfigProvider { + private static final Logger LOG = LoggerFactory.getLogger(WorkspaceConfigProvider.class); + + private final String workspaceId; + private final WorkspaceProvider workspaceProvider; + private final ConfigExtractor configExtractor; + + @Inject + WorkspaceConfigProvider( + @Named("env.CHE_WORKSPACE_ID") String workspaceId, + @Named("che.api") String apiEndpoint, + HttpJsonRequestFactory httpJsonRequestFactory, + JsonParser jsonParser) { + this.workspaceId = workspaceId; + this.workspaceProvider = new WorkspaceProvider(apiEndpoint, httpJsonRequestFactory); + this.configExtractor = new ConfigExtractor(jsonParser); + } + + @Override + public Map getAll() { + Workspace workspace = workspaceProvider.get(workspaceId); + if (workspace == null) { + LOG.error("Can't get workspace"); + return ImmutableMap.of(); + } + + Runtime runtime = workspace.getRuntime(); + if (runtime == null) { + LOG.error("Can't get runtime"); + return ImmutableMap.of(); + } + + Map configs = newHashMap(); + + for (Entry machineEntry : runtime.getMachines().entrySet()) { + Map servers = machineEntry.getValue().getServers(); + + for (Entry serverEntry : servers.entrySet()) { + Server server = serverEntry.getValue(); + String serverUrl = server.getUrl(); + Map attributes = server.getAttributes(); + + if (!"ls".equals(attributes.get("type"))) { + continue; + } + + try { + String id = configExtractor.extractId(attributes); + Map languageRegexes = configExtractor.extractLanguageRegexes(attributes); + Set fileWatchPatterns = configExtractor.extractFileWatchPatterns(attributes); + CommunicationProvider communicationProvider = + new SocketCommunicationProvider(new URI(serverUrl)); + + configs.put( + id, + new LanguageServerConfig() { + @Override + public RegexProvider getRegexpProvider() { + return new RegexProvider() { + @Override + public Map getLanguageRegexes() { + return languageRegexes; + } + + @Override + public Set getFileWatchPatterns() { + return fileWatchPatterns; + } + }; + } + + @Override + public CommunicationProvider getCommunicationProvider() { + return communicationProvider; + } + + @Override + public InstanceProvider getInstanceProvider() { + return DefaultInstanceProvider.getInstance(); + } + }); + + } catch (URISyntaxException e) { + LOG.error("Can't parse server URI: {}, proceeding", serverUrl, e); + } + } + } + + return configs; + } + + private class ConfigExtractor { + private final JsonParser jsonParser; + + private ConfigExtractor(JsonParser jsonParser) { + this.jsonParser = jsonParser; + } + + private String extractId(Map attributes) { + return attributes.get("id"); + } + + private Map extractLanguageRegexes(Map attributes) { + String filtersAsString = attributes.get("languageRegexes"); + + if (filtersAsString == null) { + return emptyMap(); + } + + return newHashSet(jsonParser.parse(filtersAsString).getAsJsonArray()) + .stream() + .map(JsonElement::getAsJsonObject) + .collect(toMap(this::getLanguageId, this::getRegex)); + } + + private String getLanguageId(JsonObject it) { + return it.get("languageId").getAsString(); + } + + private String getRegex(JsonObject it) { + return it.get("regex").getAsString(); + } + + private Set extractFileWatchPatterns(Map attributes) { + String patternsAsString = attributes.get("fileWatchPatterns"); + + if (patternsAsString == null) { + return emptySet(); + } + + return newHashSet(jsonParser.parse(patternsAsString).getAsJsonArray()) + .stream() + .map(JsonElement::getAsString) + .collect(toSet()); + } + } + + private class WorkspaceProvider { + private final String apiEndpoint; + private final HttpJsonRequestFactory httpJsonRequestFactory; + + private WorkspaceProvider(String apiEndpoint, HttpJsonRequestFactory httpJsonRequestFactory) { + this.apiEndpoint = apiEndpoint; + this.httpJsonRequestFactory = httpJsonRequestFactory; + } + + private Workspace get(String workspaceId) { + try { + String href = + fromUri(apiEndpoint) + .path(WorkspaceService.class) + .path(WorkspaceService.class, "getByKey") + .queryParam("includeInternalServers", true) + .build(workspaceId) + .toString(); + return httpJsonRequestFactory + .fromUrl(href) + .useGetMethod() + .request() + .asDto(WorkspaceDto.class); + } catch (IOException | ApiException e) { + LOG.error("Did not manage to get workspace configuration: {}", workspaceId, e); + } + return null; + } + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/WorkspaceService.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/WorkspaceService.java similarity index 63% rename from wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/WorkspaceService.java rename to wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/WorkspaceService.java index 10a633ba819..1302fd5ad00 100644 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/WorkspaceService.java +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/WorkspaceService.java @@ -8,12 +8,11 @@ * Contributors: * Red Hat, Inc. - initial API and implementation */ -package org.eclipse.che.api.languageserver.service; +package org.eclipse.che.api.languageserver; import static org.eclipse.che.api.fs.server.WsPathUtils.absolutize; -import static org.eclipse.che.api.languageserver.service.LanguageServiceUtils.prefixURI; -import static org.eclipse.che.api.languageserver.service.LanguageServiceUtils.removePrefixUri; -import static org.eclipse.che.api.languageserver.service.LanguageServiceUtils.truish; +import static org.eclipse.che.api.languageserver.LanguageServiceUtils.removePrefixUri; +import static org.eclipse.che.api.languageserver.LanguageServiceUtils.truish; import com.google.inject.Inject; import com.google.inject.Singleton; @@ -21,8 +20,8 @@ import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.util.ArrayList; -import java.util.Collection; import java.util.List; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import javax.annotation.PostConstruct; @@ -32,10 +31,6 @@ import org.eclipse.che.api.core.jsonrpc.commons.JsonRpcException; import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator; import org.eclipse.che.api.fs.server.FsManager; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.registry.InitializedLanguageServer; -import org.eclipse.che.api.languageserver.registry.LanguageServerRegistry; -import org.eclipse.che.api.languageserver.registry.LanguageServerRegistryImpl; import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.SymbolInformationDto; import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.TextEditDto; import org.eclipse.che.api.languageserver.shared.model.ExtendedWorkspaceSymbolParams; @@ -51,7 +46,7 @@ /** * REST API for the workspace/* services defined in * https://github.com/Microsoft/vscode-languageserver-protocol Dispatches onto the {@link - * LanguageServerRegistryImpl}. + * LanguageServerInitializer}. * * @author Evgen Vidolob */ @@ -60,15 +55,13 @@ public class WorkspaceService { private static final Logger LOG = LoggerFactory.getLogger(WorkspaceService.class); private final FsManager fsManager; - private LanguageServerRegistry registry; - private RequestHandlerConfigurator requestHandler; + private final FindServer findServer; + private final RequestHandlerConfigurator requestHandler; @Inject public WorkspaceService( - LanguageServerRegistry registry, - RequestHandlerConfigurator requestHandler, - FsManager fsManager) { - this.registry = registry; + RequestHandlerConfigurator requestHandler, FsManager fsManager, FindServer findServer) { + this.findServer = findServer; this.requestHandler = requestHandler; this.fsManager = fsManager; } @@ -98,7 +91,7 @@ public void configureMethods() { @SuppressWarnings("deprecation") private List editFile(FileEditParams params) { try { - String path = LanguageServiceUtils.removePrefixUri(params.getUri()); + String path = removePrefixUri(params.getUri()); String wsPath = absolutize(path); if (fsManager.existsAsFile(wsPath)) { @@ -133,46 +126,35 @@ private List editFile(FileEditParams params) { private List symbol(ExtendedWorkspaceSymbolParams workspaceSymbolParams) { List result = new ArrayList<>(); - List servers; - try { - servers = - registry - .getApplicableLanguageServers(prefixURI(workspaceSymbolParams.getFileUri())) - .stream() - .flatMap(Collection::stream) - .collect(Collectors.toList()); - OperationUtil.doInParallel( - servers, - new LSOperation>() { + String wsPath = workspaceSymbolParams.getFileUri(); + Set servers = findServer.byPath(wsPath); + OperationUtil.doInParallel( + servers, + new LSOperation>() { - @Override - public boolean canDo(InitializedLanguageServer element) { - return truish( - element.getInitializeResult().getCapabilities().getWorkspaceSymbolProvider()); - } + @Override + public boolean canDo(ExtendedLanguageServer element) { + return truish(element.getCapabilities().getWorkspaceSymbolProvider()); + } - @Override - public CompletableFuture> start( - InitializedLanguageServer element) { - return element.getServer().getWorkspaceService().symbol(workspaceSymbolParams); - } + @Override + public CompletableFuture> start( + ExtendedLanguageServer element) { + return element.getWorkspaceService().symbol(workspaceSymbolParams); + } - @Override - public boolean handleResult( - InitializedLanguageServer element, List locations) { - locations.forEach( - o -> { - o.getLocation().setUri(removePrefixUri(o.getLocation().getUri())); - result.add(new SymbolInformationDto(o)); - }); - return true; - } - }, - 10000); - return result; - } catch (LanguageServerException e) { - LOG.error("error getting symbol", e); - throw new JsonRpcException(-27000, e.getMessage()); - } + @Override + public boolean handleResult( + ExtendedLanguageServer element, List locations) { + locations.forEach( + o -> { + o.getLocation().setUri(removePrefixUri(o.getLocation().getUri())); + result.add(new SymbolInformationDto(o)); + }); + return true; + } + }, + 10000); + return result; } } diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/launcher/LanguageServerLauncher.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/launcher/LanguageServerLauncher.java deleted file mode 100644 index 8efaf2d2542..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/launcher/LanguageServerLauncher.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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.api.languageserver.launcher; - -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.registry.LanguageServerDescription; -import org.eclipse.lsp4j.services.LanguageClient; -import org.eclipse.lsp4j.services.LanguageServer; - -/** @author Anatoliy Bazko */ -public interface LanguageServerLauncher { - - /** - * Initializes and starts a language server. - * - * @param projectPath absolute path to the project - * @param client an interface implementing handlers for server->client communication - */ - LanguageServer launch(String projectPath, LanguageClient client) throws LanguageServerException; - - /** Gets the language server description */ - LanguageServerDescription getDescription(); - - /** Indicates if language server is installed and is ready to be started. */ - boolean isAbleToLaunch(); - - /** - * Denotes if the language server will be launched in a local environment or remote (i.e. if - * isLocal returns true than the server is launched in the local - * physical/virtual machine, otherwise means that the server is launched in the remote machine) - */ - default boolean isLocal() { - return true; - } -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/launcher/LanguageServerLauncherTemplate.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/launcher/LanguageServerLauncherTemplate.java deleted file mode 100644 index 71f64502e8b..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/launcher/LanguageServerLauncherTemplate.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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.api.languageserver.launcher; - -import java.io.IOException; -import java.util.concurrent.TimeUnit; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.commons.lang.IoUtil; -import org.eclipse.lsp4j.services.LanguageClient; -import org.eclipse.lsp4j.services.LanguageServer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** @author Anatolii Bazko */ -public abstract class LanguageServerLauncherTemplate implements LanguageServerLauncher { - - private static Logger LOGGER = LoggerFactory.getLogger(LanguageServerLauncherTemplate.class); - - @Override - public final LanguageServer launch(String projectPath, LanguageClient client) - throws LanguageServerException { - Process languageServerProcess = startLanguageServerProcess(projectPath); - waitCheckProcess(languageServerProcess); - return connectToLanguageServer(languageServerProcess, client); - } - - /** - * Temporary solution, in future need to provide some service that can watch for LS process and - * notify user in case in some reason it stopped. For now we just check it once start it before - * connect to it. Ask with delay in 5 seconds this delay chose empirical in normal state should be - * enough for start or fail process. If after 5 seconds process not alive we notify client about - * problem. - * - * @param languageServerProcess - * @throws LanguageServerException - */ - private void waitCheckProcess(Process languageServerProcess) throws LanguageServerException { - long endTime = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(20); - - try { - while (!languageServerProcess.isAlive() && System.currentTimeMillis() < endTime) { - TimeUnit.MILLISECONDS.sleep(10); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - if (!languageServerProcess.isAlive()) { - final String error; - try { - error = IoUtil.readStream(languageServerProcess.getErrorStream()); - } catch (IOException e) { - LOGGER.error(e.getMessage()); - throw new LanguageServerException("Can't start language server process"); - } - LOGGER.error("Can't start language server process. Got error: {}", error); - throw new LanguageServerException(error); - } - } - - protected abstract Process startLanguageServerProcess(String projectPath) - throws LanguageServerException; - - protected abstract LanguageServer connectToLanguageServer( - Process languageServerProcess, LanguageClient client) throws LanguageServerException; -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/DefaultLanguageRecognizer.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/DefaultLanguageRecognizer.java deleted file mode 100644 index 4fed7200471..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/DefaultLanguageRecognizer.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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.api.languageserver.registry; - -import static org.eclipse.che.api.fs.server.WsPathUtils.nameOf; - -import java.util.Set; -import javax.inject.Inject; -import javax.inject.Singleton; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; - -/** - * Language recognizer based on default language set. Custom language descriptions created that are - * injected by guice are taken into account prior to default language set. Language recognition is - * based on file name or file extension, so may be improved further in future. - */ -@Singleton -public class DefaultLanguageRecognizer implements LanguageRecognizer { - - private final Set languages; - private final DefaultLanguages defaultLanguages; - - @Inject - public DefaultLanguageRecognizer( - Set languages, DefaultLanguages defaultLanguages) { - this.languages = languages; - this.defaultLanguages = defaultLanguages; - } - - @Override - public LanguageDescription recognizeByPath(String wsPath) { - Name name = new Name(wsPath); - Extension extension = new Extension(wsPath); - - for (LanguageDescription language : languages) { - if (name.matches(language) || extension.matches(language)) { - return language; - } - } - - for (LanguageDescription language : defaultLanguages.getAll()) { - if (name.matches(language) || extension.matches(language)) { - return language; - } - } - - return UNIDENTIFIED; - } - - @Override - public LanguageDescription recognizeById(String id) { - for (LanguageDescription language : languages) { - if (language.getLanguageId().equals(id)) { - return language; - } - } - - for (LanguageDescription language : defaultLanguages.getAll()) { - if (language.getLanguageId().equals(id)) { - return language; - } - } - - return UNIDENTIFIED; - } - - private static class Name { - private final String name; - - private Name(String wsPath) { - String nameAndExtension = nameOf(wsPath); - int lastDotIndex = nameAndExtension.lastIndexOf('.'); - - this.name = lastDotIndex < 0 ? nameAndExtension : nameAndExtension.substring(0, lastDotIndex); - } - - private boolean matches(LanguageDescription languageDescription) { - return languageDescription.getFileNames().contains(name); - } - } - - private static class Extension { - private final String extension; - - private Extension(String wsPath) { - String nameAndExtension = nameOf(wsPath); - int lastDotIndex = nameAndExtension.lastIndexOf('.'); - - this.extension = lastDotIndex < 0 ? "" : nameAndExtension.substring(lastDotIndex + 1); - } - - private boolean matches(LanguageDescription languageDescription) { - return languageDescription.getFileExtensions().contains(extension); - } - } -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/DefaultLanguages.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/DefaultLanguages.java deleted file mode 100644 index 14e05ac6a3e..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/DefaultLanguages.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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.api.languageserver.registry; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import javax.inject.Singleton; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; - -/** Contains set of default language descriptions */ -@Singleton -public class DefaultLanguages { - - private final Set languages = new HashSet<>(); - - public DefaultLanguages() { - register("bat", "bat", "text/plain"); - register("bibtex", "bib", "text/plain"); - register("clojure", "clj", "text/x-clojure"); - register("coffeescript", "coffee", "text/x-coffeescript"); - register("c", Arrays.asList("c", "h"), "text/x-csrc"); - register("cpp", Arrays.asList("cpp", "hpp", "cc", "hh"), "text/x-c++src"); - register("csharp", "cs", "text/x-csharp"); - register("css", "css", "text/css"); - register("diff", "diff", "text/x-diff"); - register("fsharp", "fs", "text/x-fsharp"); - register("go", "go", "text/x-go"); - register("groovy", "groovy", "text/x-groovy"); - register("handlebars", "hbs", "text/plain"); - register("html", "html", "text/html"); - register("ini", "ini", "text/plain"); - register("jade", "jade", "text/x-jade"); - register("java", "java", "text/x-java"); - register("javascript", "js", "text/javascript"); - register("json", "json", "application/json"); - register("scala", "scala", "text/x-scala"); - register("kt", "kotlin", "text/x-kotlin"); - register("latex", "tex", "text/x-latex"); - register("lisp", "lisp", "text/x-commonlisp"); - register("lua", "lua", "text/x-lua"); - register("makefile", "Makefile", "text/plain"); - register("markdown", "markdown", "text/x-markdown"); - register("objective-c", "m", "text/x-objective-c"); - register("objective-cpp", "mm", "text/x-objective-cpp"); - register("perl", "pl", "text/x-perl"); - register("php", "php", "text/x-php"); - register("powershell", "ps1", "text/plain"); - register("pascal", "pas", "text/x-pascal"); - register("python", "py", "text/x-python"); - register("r", "r", "text/x-rsrc"); - register("ruby", "rb", "text/x-ruby"); - register("rust", "rs", "text/x-rustsrc"); - register("shellscript", "sh", "text/x-sh"); - register("sql", "sql", "text/x-sql"); - register("swift", "swift", "text/x-swift"); - register("typescript", "ts", "application/typescript"); - register("xml", "xml", "application/xml"); - register("xsl", "xsl", "text/plain"); - register("yaml", "yaml", "text/x-yaml"); - - registerWithName("dockerfile", "Dockerfile", "text/x-dockerfile"); - } - - public Set getAll() { - return Collections.unmodifiableSet(languages); - } - - private void register(String id, String extension, String mimeType) { - LanguageDescription languageDescription = new LanguageDescription(); - languageDescription.setLanguageId(id); - languageDescription.setMimeType(mimeType); - languageDescription.setFileExtensions(Collections.singletonList(extension)); - languages.add(languageDescription); - } - - private void register(String id, List extensions, String mimeType) { - LanguageDescription languageDescription = new LanguageDescription(); - languageDescription.setLanguageId(id); - languageDescription.setMimeType(mimeType); - languageDescription.setFileExtensions(extensions); - languages.add(languageDescription); - } - - private void registerWithName(String id, String fileName, String mimeType) { - LanguageDescription languageDescription = new LanguageDescription(); - languageDescription.setLanguageId(id); - languageDescription.setMimeType(mimeType); - languageDescription.setFileNames(Collections.singletonList(fileName)); - languages.add(languageDescription); - } -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/DocumentFilter.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/DocumentFilter.java deleted file mode 100644 index 8a6c7094b46..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/DocumentFilter.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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.api.languageserver.registry; - -public class DocumentFilter { - - private final String pathRegex; - private final String languageId; - private final String scheme; - - public DocumentFilter(String languageId, String pathRegex, String scheme) { - this.pathRegex = pathRegex; - this.languageId = languageId; - this.scheme = scheme; - } - - public String getLanguageId() { - return languageId; - } - - public String getPathRegex() { - return pathRegex; - } - - public String getScheme() { - return scheme; - } -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/InitializedLanguageServer.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/InitializedLanguageServer.java deleted file mode 100644 index af4ed2027fa..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/InitializedLanguageServer.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.api.languageserver.registry; - -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.lsp4j.InitializeResult; -import org.eclipse.lsp4j.services.LanguageServer; - -/** - * Simple container for {@link LanguageServerLauncher}, {@link InitializeResult} and {@link - * LanguageServer} - * - * @author Evgen Vidolob - */ -public class InitializedLanguageServer { - private final String id; - private final LanguageServer server; - private final InitializeResult initializeResult; - private final LanguageServerLauncher launcher; - - public InitializedLanguageServer( - String id, - LanguageServer server, - InitializeResult initializeResult, - LanguageServerLauncher launcher) { - this.id = id; - this.server = server; - this.initializeResult = initializeResult; - this.launcher = launcher; - } - - public String getId() { - return id; - } - - public LanguageServer getServer() { - return server; - } - - public InitializeResult getInitializeResult() { - return initializeResult; - } - - public LanguageServerLauncher getLauncher() { - return launcher; - } -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/LanguageRecognizer.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/LanguageRecognizer.java deleted file mode 100644 index f037110a73e..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/LanguageRecognizer.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.api.languageserver.registry; - -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; - -/** - * The implementations of this interface are responsible for language recognition of a file under - * defined path. - */ -public interface LanguageRecognizer { - LanguageDescription UNIDENTIFIED = - new LanguageDescription() { - @Override - public String getLanguageId() { - return "unidentified"; - } - - @Override - public String getMimeType() { - return ""; - } - - @Override - public String getHighlightingConfiguration() { - return ""; - } - }; - - /** - * Recognize a language by file path. If language cannot be recognized for any reason than the - * implementation must return {@link LanguageRecognizer#UNIDENTIFIED}. - * - * @param wsPath workspace path of a file - * @return description of language that was recognized - */ - LanguageDescription recognizeByPath(String wsPath); - - /** - * Recognize a language by it's identifier. If language cannot be recognized for any reason than - * the implementation must return {@link LanguageRecognizer#UNIDENTIFIED}. - * - * @param id language identifier - * @return description of language that was recognized - */ - LanguageDescription recognizeById(String id); -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/LanguageServerDescription.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/LanguageServerDescription.java deleted file mode 100644 index 52b93a57d74..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/LanguageServerDescription.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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.api.languageserver.registry; - -import static java.util.Collections.emptyList; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class LanguageServerDescription { - private final String id; - private final List languageIds; - private final List documentFilters; - - /** - * The file name patters, format described there {@link - * java.nio.file.FileSystem#getPathMatcher(String)} - */ - private List fileWatchPatterns = emptyList(); - - public LanguageServerDescription( - String id, List languageIds, List documentFilters) { - this(id, languageIds, documentFilters, Collections.emptyList()); - } - - public LanguageServerDescription( - String id, - List languageIds, - List documentFilters, - List fileWatchPatterns) { - this.id = id; - this.languageIds = languageIds; - this.documentFilters = documentFilters; - this.fileWatchPatterns = fileWatchPatterns; - } - - public String getId() { - return id; - } - - public List getLanguageIds() { - return languageIds; - } - - public List getDocumentFilters() { - return documentFilters; - } - - public List getFileWatchPatterns() { - return fileWatchPatterns; - } - - /** @param fileWatchPatterns must not be null */ - public void setFileWatchPatterns(List fileWatchPatterns) { - this.fileWatchPatterns = new ArrayList<>(fileWatchPatterns); - } -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/LanguageServerFileWatcher.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/LanguageServerFileWatcher.java deleted file mode 100644 index 2477a9b89c9..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/LanguageServerFileWatcher.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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.api.languageserver.registry; - -import static org.eclipse.che.api.languageserver.service.LanguageServiceUtils.prefixURI; - -import com.google.common.annotations.VisibleForTesting; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.PathMatcher; -import java.util.Collections; -import java.util.concurrent.CopyOnWriteArrayList; -import javax.annotation.PreDestroy; -import javax.inject.Inject; -import javax.inject.Singleton; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.watcher.server.FileWatcherManager; -import org.eclipse.lsp4j.DidChangeWatchedFilesParams; -import org.eclipse.lsp4j.FileChangeType; -import org.eclipse.lsp4j.FileEvent; -import org.eclipse.lsp4j.ServerCapabilities; -import org.eclipse.lsp4j.services.LanguageServer; - -/** - * Implement DidChangeWatchedFiles - * Notification - */ -@Singleton -public class LanguageServerFileWatcher { - - private final FileWatcherManager watcherManager; - - private CopyOnWriteArrayList watcherIds = new CopyOnWriteArrayList<>(); - - @Inject - public LanguageServerFileWatcher( - FileWatcherManager watcherManager, ServerInitializer serverInitializer) { - this.watcherManager = watcherManager; - serverInitializer.addObserver(this::onServerInitialized); - } - - private void send(LanguageServer server, String filePath, FileChangeType changeType) { - DidChangeWatchedFilesParams params = - new DidChangeWatchedFilesParams( - Collections.singletonList(new FileEvent(prefixURI(filePath), changeType))); - server.getWorkspaceService().didChangeWatchedFiles(params); - } - - @PreDestroy - @VisibleForTesting - public void removeAllWatchers() { - for (Integer watcherId : watcherIds) { - watcherManager.unRegisterByMatcher(watcherId); - } - } - - private void onServerInitialized( - LanguageServerLauncher launcher, - LanguageServer server, - ServerCapabilities capabilities, - String projectPath) { - LanguageServerDescription description = launcher.getDescription(); - FileSystem fileSystem = FileSystems.getDefault(); - for (String pattern : description.getFileWatchPatterns()) { - PathMatcher matcher = fileSystem.getPathMatcher(pattern); - int watcherId = - watcherManager.registerByMatcher( - matcher, - s -> send(server, s, FileChangeType.Created), - s -> send(server, s, FileChangeType.Changed), - s -> send(server, s, FileChangeType.Deleted)); - - watcherIds.add(watcherId); - } - } -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/LanguageServerRegistry.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/LanguageServerRegistry.java deleted file mode 100644 index b5fd7ad7e0a..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/LanguageServerRegistry.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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.api.languageserver.registry; - -import java.util.Collection; -import java.util.List; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; -import org.eclipse.lsp4j.ServerCapabilities; - -/** @author Anatoliy Bazko */ -public interface LanguageServerRegistry { - /** - * Finds appropriate language servers according to file uri. - * - * @throws LanguageServerException - */ - List> getApplicableLanguageServers(String fileUri) - throws LanguageServerException; - - /** Returns all available servers. */ - List getSupportedLanguages(); - - /** - * Initialize the language servers that apply to this file - * - * @param fileUri - * @return - * @throws LanguageServerException - */ - ServerCapabilities initialize(String fileUri) throws LanguageServerException; - - ServerCapabilities getCapabilities(String fileUri) throws LanguageServerException; - - InitializedLanguageServer getServer(String id); -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/LanguageServerRegistryImpl.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/LanguageServerRegistryImpl.java deleted file mode 100644 index ae495891564..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/LanguageServerRegistryImpl.java +++ /dev/null @@ -1,409 +0,0 @@ -/* - * 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.api.languageserver.registry; - -import static javax.ws.rs.core.UriBuilder.fromUri; -import static org.eclipse.che.api.fs.server.WsPathUtils.absolutize; -import static org.eclipse.che.api.languageserver.registry.LanguageRecognizer.UNIDENTIFIED; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import javax.annotation.PreDestroy; -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Provider; -import javax.inject.Singleton; -import org.eclipse.che.api.core.ApiException; -import org.eclipse.che.api.core.model.workspace.Workspace; -import org.eclipse.che.api.core.notification.EventService; -import org.eclipse.che.api.core.rest.HttpJsonRequestFactory; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.remote.RemoteLsLauncherProvider; -import org.eclipse.che.api.languageserver.service.LanguageServiceUtils; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; -import org.eclipse.che.api.project.server.ProjectManager; -import org.eclipse.che.api.project.server.impl.RegisteredProject; -import org.eclipse.che.api.workspace.server.WorkspaceService; -import org.eclipse.che.api.workspace.shared.dto.WorkspaceDto; -import org.eclipse.lsp4j.MessageParams; -import org.eclipse.lsp4j.MessageType; -import org.eclipse.lsp4j.ServerCapabilities; -import org.eclipse.lsp4j.services.LanguageServer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@Singleton -public class LanguageServerRegistryImpl implements LanguageServerRegistry { - - private static final Logger LOG = LoggerFactory.getLogger(LanguageServerRegistryImpl.class); - - private final String workspaceId; - private final String apiEndpoint; - private final HttpJsonRequestFactory httpJsonRequestFactory; - private final Set launcherProviders; - private final List languages; - private final List launchers; - private final AtomicInteger serverId = new AtomicInteger(); - - /** Started {@link LanguageServer} by project. */ - private final Map> launchedServers; - - private final Map> initializedServers; - - private final Provider projectManagerProvider; - private final ServerInitializer initializer; - private EventService eventService; - private CheLanguageClientFactory clientFactory; - private LanguageRecognizer languageRecognizer; - private Workspace workspace; - - @Inject - public LanguageServerRegistryImpl( - @Named("env.CHE_WORKSPACE_ID") String workspaceId, - @Named("che.api") String apiEndpoint, - HttpJsonRequestFactory httpJsonRequestFactory, - Set launcherProviders, - Set languageServerLaunchers, - Set languages, - Provider projectManagerProvider, - ServerInitializer initializer, - EventService eventService, - CheLanguageClientFactory clientFactory, - LanguageRecognizer languageRecognizer) { - this.workspaceId = workspaceId; - this.apiEndpoint = apiEndpoint; - this.httpJsonRequestFactory = httpJsonRequestFactory; - this.launcherProviders = launcherProviders; - this.languages = new ArrayList<>(languages); - this.launchers = new ArrayList<>(languageServerLaunchers); - this.projectManagerProvider = projectManagerProvider; - this.initializer = initializer; - this.eventService = eventService; - this.clientFactory = clientFactory; - this.languageRecognizer = languageRecognizer; - this.launchedServers = new HashMap<>(); - this.initializedServers = new HashMap<>(); - } - - @Override - public ServerCapabilities getCapabilities(String fileUri) throws LanguageServerException { - return getApplicableLanguageServers(fileUri) - .stream() - .flatMap(Collection::stream) - .map(s -> s.getInitializeResult().getCapabilities()) - .reduce( - null, - (left, right) -> - left == null ? right : new ServerCapabilitiesOverlay(left, right).compute()); - } - - public ServerCapabilities initialize(String fileUri) throws LanguageServerException { - String projectPath = extractProjectPath(fileUri); - if (projectPath == null) { - return null; - } - List requiredToLaunch = findLaunchers(projectPath, fileUri); - // launchers is the set of things we need to have initialized - - for (LanguageServerLauncher launcher : new ArrayList<>(requiredToLaunch)) { - synchronized (initializedServers) { - List servers = - launchedServers.computeIfAbsent(projectPath, k -> new ArrayList<>()); - - if (!servers.contains(launcher)) { - servers.add(launcher); - String id = String.valueOf(serverId.incrementAndGet()); - initializer - .initialize(launcher, clientFactory.create(id), projectPath) - .thenAccept( - pair -> { - synchronized (initializedServers) { - List initialized = - initializedServers.computeIfAbsent(projectPath, k -> new ArrayList<>()); - initialized.add( - new InitializedLanguageServer(id, pair.first, pair.second, launcher)); - requiredToLaunch.remove(launcher); - initializedServers.notifyAll(); - } - }) - .exceptionally( - t -> { - eventService.publish( - new MessageParams( - MessageType.Error, - "Failed to initialized LS " - + launcher.getDescription().getId() - + ": " - + t.getMessage())); - LOG.error("Error launching language server " + launcher, t); - synchronized (initializedServers) { - requiredToLaunch.remove(launcher); - servers.remove(launcher); - initializedServers.notifyAll(); - } - return null; - }); - } - } - } - - // now wait for all launchers to arrive at initialized - // eventually, all launchers will either fail or succeed, regardless of - // which request thread started them. Thus the loop below will - // end. - synchronized (initializedServers) { - List initForProject = initializedServers.get(projectPath); - if (initForProject != null) { - for (InitializedLanguageServer initialized : initForProject) { - requiredToLaunch.remove(initialized.getLauncher()); - } - } - while (!requiredToLaunch.isEmpty()) { - try { - initializedServers.wait(); - initForProject = initializedServers.get(projectPath); - if (initForProject != null) { - for (InitializedLanguageServer initialized : initForProject) { - requiredToLaunch.remove(initialized.getLauncher()); - } - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return null; - } - } - } - return getCapabilities(fileUri); - } - - private List findLaunchers(String projectPath, String fileUri) { - String wsPath = absolutize(LanguageServiceUtils.removePrefixUri(fileUri)); - LanguageDescription language = languageRecognizer.recognizeByPath(wsPath); - if (language == null) { - return Collections.emptyList(); - } - List combinedLaunchers = new LinkedList<>(launchers); - initWorkspaceConfiguration(); - if (workspace != null) { - for (RemoteLsLauncherProvider launcherProvider : launcherProviders) { - combinedLaunchers.addAll(launcherProvider.getAll(workspace)); - } - } - - List result = new ArrayList<>(); - for (LanguageServerLauncher launcher : combinedLaunchers) { - if (launcher.isAbleToLaunch()) { - int score = matchScore(launcher.getDescription(), fileUri, language.getLanguageId()); - if (score > 0) { - result.add(launcher); - } - } - } - return result; - } - - @Override - public List getSupportedLanguages() { - initWorkspaceConfiguration(); - - if (workspace == null) { - return Collections.unmodifiableList(languages); - } - - List languageDescriptions = new LinkedList<>(languages); - - for (RemoteLsLauncherProvider launcherProvider : launcherProviders) { - for (LanguageServerLauncher launcher : launcherProvider.getAll(workspace)) { - for (String languageId : launcher.getDescription().getLanguageIds()) { - LanguageDescription language = languageRecognizer.recognizeById(languageId); - if (language.equals(UNIDENTIFIED)) { - continue; - } - languageDescriptions.add(language); - } - } - } - - return Collections.unmodifiableList(languageDescriptions); - } - - protected String extractProjectPath(String filePath) throws LanguageServerException { - if (!LanguageServiceUtils.isProjectUri(filePath)) { - throw new LanguageServerException("Project not found for " + filePath); - } - - String wsPath = absolutize(LanguageServiceUtils.removePrefixUri(filePath)); - RegisteredProject project = - projectManagerProvider - .get() - .getClosest(wsPath) - .orElseThrow(() -> new LanguageServerException("Project not found for " + filePath)); - - return LanguageServiceUtils.prefixURI(project.getPath()); - } - - public List> getApplicableLanguageServers(String fileUri) - throws LanguageServerException { - String projectPath = extractProjectPath(fileUri); - String wsPath = absolutize(LanguageServiceUtils.removePrefixUri(fileUri)); - LanguageDescription language = languageRecognizer.recognizeByPath(wsPath); - if (projectPath == null || language == null) { - return Collections.emptyList(); - } - - Map> result = new HashMap<>(); - - List servers = null; - synchronized (initializedServers) { - List list = initializedServers.get(projectPath); - if (list == null) { - return Collections.emptyList(); - } - servers = new ArrayList(list); - } - for (InitializedLanguageServer server : servers) { - int score = - matchScore(server.getLauncher().getDescription(), fileUri, language.getLanguageId()); - if (score > 0) { - List list = result.get(score); - if (list == null) { - list = new ArrayList<>(); - result.put(score, list); - } - list.add(server); - } - } - // sort lists highest score first - return result - .entrySet() - .stream() - .sorted((left, right) -> right.getKey() - left.getKey()) - .map(entry -> entry.getValue()) - .collect(Collectors.toList()); - } - - private int matchScore(LanguageServerDescription desc, String path, String languageId) { - int match = matchLanguageId(desc, languageId); - if (match == 10) { - return 10; - } - - for (DocumentFilter filter : desc.getDocumentFilters()) { - if (filter.getLanguageId() != null && filter.getLanguageId().length() > 0) { - match = Math.max(match, matchLanguageId(filter.getLanguageId(), languageId)); - if (match == 10) { - return 10; - } - } - if (filter.getScheme() != null && path.startsWith(filter.getScheme() + ":")) { - return 10; - } - String pattern = filter.getPathRegex(); - if (pattern != null) { - if (pattern.equals(path)) { - return 10; - } - Pattern regex = Pattern.compile(pattern); - if (regex.matcher(path).matches()) { - match = Math.max(match, 5); - } - } - } - return match; - } - - private int matchLanguageId(String id, String languageId) { - if (id.equals(languageId)) { - return 10; - } else if ("*".equals(id)) { - return 5; - } - return 0; - } - - private int matchLanguageId(LanguageServerDescription desc, String languageId) { - int match = 0; - List languageIds = desc.getLanguageIds(); - if (languageIds == null) { - return 0; - } - for (String id : languageIds) { - if (id.equals(languageId)) { - match = 10; - break; - } else if ("*".equals(id)) { - match = 5; - } - } - return match; - } - - @PreDestroy - protected void shutdown() { - List allServers; - synchronized (initializedServers) { - allServers = - initializedServers - .values() - .stream() - .flatMap(l -> l.stream()) - .map(s -> s.getServer()) - .collect(Collectors.toList()); - } - for (LanguageServer server : allServers) { - server.shutdown(); - server.exit(); - } - } - - @Override - public InitializedLanguageServer getServer(String id) { - for (List list : initializedServers.values()) { - for (InitializedLanguageServer initializedLanguageServer : list) { - if (initializedLanguageServer.getId().equals(id)) { - return initializedLanguageServer; - } - } - } - return null; - } - - private void initWorkspaceConfiguration() { - if (workspace != null) { - return; - } - - String href = - fromUri(apiEndpoint) - .path(WorkspaceService.class) - .path(WorkspaceService.class, "getByKey") - .queryParam("includeInternalServers", true) - .build(workspaceId) - .toString(); - try { - workspace = - httpJsonRequestFactory.fromUrl(href).useGetMethod().request().asDto(WorkspaceDto.class); - } catch (IOException | ApiException e) { - LOG.error("Did not manage to get workspace configuration: {}", workspaceId, e); - } - } -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/ServerCapabilitiesOverlay.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/ServerCapabilitiesOverlay.java deleted file mode 100644 index dd6ac70a877..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/ServerCapabilitiesOverlay.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * 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.api.languageserver.registry; - -import com.google.common.base.Function; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import org.eclipse.lsp4j.CodeLensOptions; -import org.eclipse.lsp4j.CompletionOptions; -import org.eclipse.lsp4j.DocumentOnTypeFormattingOptions; -import org.eclipse.lsp4j.ServerCapabilities; -import org.eclipse.lsp4j.SignatureHelpOptions; -import org.eclipse.lsp4j.TextDocumentSyncKind; -import org.eclipse.lsp4j.TextDocumentSyncOptions; -import org.eclipse.lsp4j.jsonrpc.messages.Either; - -public class ServerCapabilitiesOverlay { - private ServerCapabilities left; - private ServerCapabilities right; - - public ServerCapabilitiesOverlay(ServerCapabilities left, ServerCapabilities right) { - this.left = left; - this.right = right; - } - - public CodeLensOptions getCodeLensProvider() { - CodeLensOptions leftOptions = left.getCodeLensProvider(); - CodeLensOptions rightOptions = right.getCodeLensProvider(); - if (leftOptions == null) { - return rightOptions; - } - if (rightOptions == null) { - return leftOptions; - } - CodeLensOptions result = new CodeLensOptions(); - if (leftOptions != null && leftOptions.isResolveProvider() - || rightOptions != null && leftOptions.isResolveProvider()) { - result.setResolveProvider(true); - } - return result; - } - - public CompletionOptions getCompletionProvider() { - CompletionOptions leftOptions = left.getCompletionProvider(); - CompletionOptions rightOptions = right.getCompletionProvider(); - if (leftOptions == null) { - return rightOptions; - } - if (rightOptions == null) { - return leftOptions; - } - - CompletionOptions result = new CompletionOptions(); - List triggerChars = new ArrayList<>(); - - if (leftOptions != null) { - triggerChars.addAll(listish(leftOptions.getTriggerCharacters())); - } - if (rightOptions != null) { - triggerChars.addAll(listish(rightOptions.getTriggerCharacters())); - } - result.setTriggerCharacters(triggerChars); - return result; - } - - public DocumentOnTypeFormattingOptions getDocumentOnTypeFormattingProvider() { - DocumentOnTypeFormattingOptions leftOptions = left.getDocumentOnTypeFormattingProvider(); - DocumentOnTypeFormattingOptions rightOptions = right.getDocumentOnTypeFormattingProvider(); - if (leftOptions == null) { - return rightOptions; - } - if (rightOptions == null) { - return leftOptions; - } - - DocumentOnTypeFormattingOptions result = new DocumentOnTypeFormattingOptions(); - List triggerChars = new ArrayList<>(); - - if (leftOptions != null) { - result.setFirstTriggerCharacter(leftOptions.getFirstTriggerCharacter()); - triggerChars.addAll(listish(leftOptions.getMoreTriggerCharacter())); - } - if (rightOptions != null) { - triggerChars.addAll(listish(rightOptions.getMoreTriggerCharacter())); - } - result.setMoreTriggerCharacter(triggerChars); - return result; - } - - public SignatureHelpOptions getSignatureHelpProvider() { - SignatureHelpOptions leftOptions = left.getSignatureHelpProvider(); - SignatureHelpOptions rightOptions = right.getSignatureHelpProvider(); - if (leftOptions == null) { - return rightOptions; - } - if (rightOptions == null) { - return leftOptions; - } - SignatureHelpOptions result = new SignatureHelpOptions(); - - List triggerChars = new ArrayList<>(); - - triggerChars.addAll(listish(leftOptions.getTriggerCharacters())); - triggerChars.addAll(listish(rightOptions.getTriggerCharacters())); - result.setTriggerCharacters(triggerChars); - return result; - } - - public Either getTextDocumentSync() { - return mergeTextDocumentSync(left.getTextDocumentSync(), right.getTextDocumentSync()); - } - - private Either mergeTextDocumentSync( - Either left, - Either right) { - if (left == null) { - return right; - } - if (right == null) { - return left; - } - if (left.equals(right)) { - return left; - } - - if (left.isLeft() && left.getLeft() == TextDocumentSyncKind.Full) { - return left; - } - - if (left.isLeft() && left.getLeft() == TextDocumentSyncKind.Incremental) { - if (right.isLeft() && right.getLeft() == TextDocumentSyncKind.Full) { - return right; - } else { - return left; - } - } - - if (left.isRight() && right.isRight()) { - TextDocumentSyncOptions leftRight = left.getRight(); - TextDocumentSyncOptions rightRight = right.getRight(); - if (leftRight.getChange() == TextDocumentSyncKind.Full) { - return left; - } - - if (leftRight.getChange() == TextDocumentSyncKind.Incremental) { - if (rightRight.getChange() == TextDocumentSyncKind.Full) { - return right; - } else { - return left; - } - } - } - - if (left.isLeft() && right.isRight()) { - return right; - } - - if (left.isRight() && right.isLeft()) { - return left; - } - return right; - } - - private Boolean or(Function f) { - Boolean leftVal = f.apply(left); - Boolean rightVal = f.apply(right); - if (leftVal == null) { - return rightVal; - } - if (rightVal == null) { - return leftVal; - } - return leftVal || rightVal; - } - - private List listish(List list) { - return list == null ? Collections.emptyList() : list; - } - - public ServerCapabilities compute() { - - ServerCapabilities result = new ServerCapabilities(); - result.setCodeActionProvider(or(ServerCapabilities::getCodeActionProvider)); - result.setCodeLensProvider(getCodeLensProvider()); - result.setCompletionProvider(getCompletionProvider()); - result.setDefinitionProvider(or(ServerCapabilities::getDefinitionProvider)); - result.setDocumentFormattingProvider(or(ServerCapabilities::getDocumentFormattingProvider)); - result.setDocumentHighlightProvider(or(ServerCapabilities::getDocumentHighlightProvider)); - result.setDocumentOnTypeFormattingProvider(getDocumentOnTypeFormattingProvider()); - result.setDocumentRangeFormattingProvider( - or(ServerCapabilities::getDocumentRangeFormattingProvider)); - result.setDocumentSymbolProvider(or(ServerCapabilities::getDocumentSymbolProvider)); - result.setHoverProvider(or(ServerCapabilities::getHoverProvider)); - result.setReferencesProvider(or(ServerCapabilities::getReferencesProvider)); - result.setRenameProvider(or(ServerCapabilities::getRenameProvider)); - result.setSignatureHelpProvider(getSignatureHelpProvider()); - result.setTextDocumentSync(getTextDocumentSync()); - result.setWorkspaceSymbolProvider(or(ServerCapabilities::getWorkspaceSymbolProvider)); - - return result; - } -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/ServerInitializer.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/ServerInitializer.java deleted file mode 100644 index f385213d2cd..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/ServerInitializer.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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.api.languageserver.registry; - -import java.util.concurrent.CompletableFuture; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.commons.lang.Pair; -import org.eclipse.lsp4j.InitializeResult; -import org.eclipse.lsp4j.services.LanguageClient; -import org.eclipse.lsp4j.services.LanguageServer; - -/** - * Is responsible to start new {@link LanguageServer}. - * - * @author Anatoliy Bazko - */ -public interface ServerInitializer extends ServerInitializerObservable { - /** - * Initialize new {@link LanguageServer} with given project path. - * - * @return - */ - CompletableFuture> initialize( - LanguageServerLauncher launcher, LanguageClient client, String projectPath) - throws LanguageServerException; -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/ServerInitializerImpl.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/ServerInitializerImpl.java deleted file mode 100644 index eae7f12500e..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/ServerInitializerImpl.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * 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.api.languageserver.registry; - -import com.google.inject.Singleton; -import java.lang.management.ManagementFactory; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.service.LanguageServiceUtils; -import org.eclipse.che.commons.lang.Pair; -import org.eclipse.lsp4j.ClientCapabilities; -import org.eclipse.lsp4j.CodeActionCapabilities; -import org.eclipse.lsp4j.CodeLensCapabilities; -import org.eclipse.lsp4j.CompletionCapabilities; -import org.eclipse.lsp4j.CompletionItemCapabilities; -import org.eclipse.lsp4j.DefinitionCapabilities; -import org.eclipse.lsp4j.DidChangeConfigurationCapabilities; -import org.eclipse.lsp4j.DidChangeWatchedFilesCapabilities; -import org.eclipse.lsp4j.DocumentHighlightCapabilities; -import org.eclipse.lsp4j.DocumentLinkCapabilities; -import org.eclipse.lsp4j.DocumentSymbolCapabilities; -import org.eclipse.lsp4j.ExecuteCommandCapabilities; -import org.eclipse.lsp4j.FormattingCapabilities; -import org.eclipse.lsp4j.HoverCapabilities; -import org.eclipse.lsp4j.InitializeParams; -import org.eclipse.lsp4j.InitializeResult; -import org.eclipse.lsp4j.OnTypeFormattingCapabilities; -import org.eclipse.lsp4j.RangeFormattingCapabilities; -import org.eclipse.lsp4j.ReferencesCapabilities; -import org.eclipse.lsp4j.RenameCapabilities; -import org.eclipse.lsp4j.ServerCapabilities; -import org.eclipse.lsp4j.SignatureHelpCapabilities; -import org.eclipse.lsp4j.SymbolCapabilities; -import org.eclipse.lsp4j.SynchronizationCapabilities; -import org.eclipse.lsp4j.TextDocumentClientCapabilities; -import org.eclipse.lsp4j.WorkspaceClientCapabilities; -import org.eclipse.lsp4j.WorkspaceEditCapabilities; -import org.eclipse.lsp4j.services.LanguageClient; -import org.eclipse.lsp4j.services.LanguageServer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** @author Anatoliy Bazko */ -@Singleton -public class ServerInitializerImpl implements ServerInitializer { - private static final Logger LOG = LoggerFactory.getLogger(ServerInitializerImpl.class); - - private static final int PROCESS_ID = getProcessId(); - private static final String CLIENT_NAME = "EclipseChe"; - - public static ClientCapabilities CLIENT_CAPABILITIES; - - private final List observers = new ArrayList<>(); - - private static int getProcessId() { - String name = ManagementFactory.getRuntimeMXBean().getName(); - int prefixEnd = name.indexOf('@'); - if (prefixEnd != -1) { - String prefix = name.substring(0, prefixEnd); - try { - return Integer.parseInt(prefix); - } catch (NumberFormatException ignored) { - } - } - - LOG.error("Failed to recognize the pid of the process"); - return -1; - } - - @Override - public void addObserver(ServerInitializerObserver observer) { - observers.add(observer); - } - - @Override - public void removeObserver(ServerInitializerObserver observer) { - observers.remove(observer); - } - - @Override - public CompletableFuture> initialize( - LanguageServerLauncher launcher, LanguageClient client, String projectPath) - throws LanguageServerException { - InitializeParams initializeParams = prepareInitializeParams(launcher, projectPath); - String launcherId = launcher.getDescription().getId(); - CompletableFuture> result = - new CompletableFuture>(); - - LanguageServer server; - try { - server = launcher.launch(projectPath, client); - } catch (LanguageServerException e) { - result.completeExceptionally( - new LanguageServerException( - "Can't initialize Language Server " - + launcherId - + " on " - + projectPath - + ". " - + e.getMessage(), - e)); - return result; - } - registerCallbacks(server, launcher); - - CompletableFuture completableFuture = server.initialize(initializeParams); - completableFuture - .thenAccept( - (InitializeResult res) -> { - onServerInitialized(launcher, server, res.getCapabilities(), projectPath); - result.complete(Pair.of(server, res)); - LOG.info("Initialized Language Server {} on project {}", launcherId, projectPath); - }) - .exceptionally( - (Throwable e) -> { - server.shutdown(); - server.exit(); - result.completeExceptionally(e); - return null; - }); - return result; - } - - protected void registerCallbacks(LanguageServer server, LanguageServerLauncher launcher) { - if (server instanceof ServerInitializerObserver) { - addObserver((ServerInitializerObserver) server); - } - - if (launcher instanceof ServerInitializerObserver) { - addObserver((ServerInitializerObserver) launcher); - } - } - - private InitializeParams prepareInitializeParams( - LanguageServerLauncher launcher, String projectPath) { - InitializeParams initializeParams = new InitializeParams(); - - if (launcher.isLocal()) { - initializeParams.setProcessId(PROCESS_ID); - } else { - initializeParams.setProcessId(null); - } - - initializeParams.setRootPath(LanguageServiceUtils.removeUriScheme(projectPath)); - initializeParams.setRootUri(projectPath); - - if (CLIENT_CAPABILITIES == null) { - CLIENT_CAPABILITIES = new ClientCapabilities(); - WorkspaceClientCapabilities workspace = new WorkspaceClientCapabilities(); - workspace.setApplyEdit(false); // Change when support added - workspace.setDidChangeConfiguration(new DidChangeConfigurationCapabilities()); - workspace.setDidChangeWatchedFiles(new DidChangeWatchedFilesCapabilities()); - workspace.setExecuteCommand(new ExecuteCommandCapabilities()); - workspace.setSymbol(new SymbolCapabilities()); - workspace.setWorkspaceEdit(new WorkspaceEditCapabilities()); - CLIENT_CAPABILITIES.setWorkspace(workspace); - - TextDocumentClientCapabilities textDocument = new TextDocumentClientCapabilities(); - textDocument.setCodeAction(new CodeActionCapabilities()); - textDocument.setCodeLens(new CodeLensCapabilities()); - textDocument.setCompletion(new CompletionCapabilities(new CompletionItemCapabilities())); - textDocument.setDefinition(new DefinitionCapabilities()); - textDocument.setDocumentHighlight(new DocumentHighlightCapabilities()); - textDocument.setDocumentLink(new DocumentLinkCapabilities()); - textDocument.setDocumentSymbol(new DocumentSymbolCapabilities()); - textDocument.setFormatting(new FormattingCapabilities()); - textDocument.setHover(new HoverCapabilities()); - textDocument.setOnTypeFormatting(new OnTypeFormattingCapabilities()); - textDocument.setRangeFormatting(new RangeFormattingCapabilities()); - textDocument.setReferences(new ReferencesCapabilities()); - textDocument.setRename(new RenameCapabilities()); - textDocument.setSignatureHelp(new SignatureHelpCapabilities()); - textDocument.setSynchronization(new SynchronizationCapabilities(true, false, true)); - CLIENT_CAPABILITIES.setTextDocument(textDocument); - } - initializeParams.setCapabilities(CLIENT_CAPABILITIES); - initializeParams.setClientName(CLIENT_NAME); - return initializeParams; - } - - private void onServerInitialized( - LanguageServerLauncher launcher, - LanguageServer server, - ServerCapabilities capabilities, - String projectPath) { - observers.forEach( - observer -> observer.onServerInitialized(launcher, server, capabilities, projectPath)); - } -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/ServerInitializerObservable.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/ServerInitializerObservable.java deleted file mode 100644 index 1bbdc46ab90..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/ServerInitializerObservable.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 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.api.languageserver.registry; - -/** @author Anatoliy Bazko */ -public interface ServerInitializerObservable { - void addObserver(ServerInitializerObserver observer); - - void removeObserver(ServerInitializerObserver observer); -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/ServerInitializerObserver.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/ServerInitializerObserver.java deleted file mode 100644 index 714195cb5e4..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/ServerInitializerObserver.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.api.languageserver.registry; - -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.lsp4j.ServerCapabilities; -import org.eclipse.lsp4j.services.LanguageServer; - -/** @author Anatoliy Bazko */ -public interface ServerInitializerObserver { - - /** - * Notifies observers when server is initialized and ready to use. - * - * @param server the {@link LanguageServer} - * @param capabilities the supported capabilities by server - * @param languageDescription - * @param projectPath - */ - void onServerInitialized( - LanguageServerLauncher launcher, - LanguageServer server, - ServerCapabilities capabilities, - String projectPath); -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/remote/LsConfigurationDetector.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/remote/LsConfigurationDetector.java deleted file mode 100644 index fc761811b86..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/remote/LsConfigurationDetector.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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.api.languageserver.remote; - -import java.util.Map; -import javax.inject.Singleton; - -/** Detects if machine server attributes indicates that we are dealing with language server. */ -@Singleton -class LsConfigurationDetector { - /** - * Tests attributes for a language server indicator - * - * @param attributes map with machine server attributes - * @return true if language server is detected, false otherwise - */ - boolean isDetected(Map attributes) { - return "ls".equals(attributes.get("type")); - } -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/remote/LsConfigurationExtractor.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/remote/LsConfigurationExtractor.java deleted file mode 100644 index 450296fd25b..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/remote/LsConfigurationExtractor.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 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.api.languageserver.remote; - -import static java.util.Collections.emptyList; -import static java.util.Collections.unmodifiableList; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import javax.inject.Inject; -import javax.inject.Singleton; -import org.eclipse.che.api.languageserver.registry.DocumentFilter; -import org.eclipse.che.api.languageserver.registry.LanguageServerDescription; - -/** - * This class is responsible for language server description extraction out of server attributes - * map. It is expected that there will be specific attribute named config that will - * contain serialized json data that represents all language server configuration data. Structure of - * json corresponds to {@link LanguageServerDescription} class with all aggregated classes - */ -@Singleton -class LsConfigurationExtractor { - private final JsonParser jsonParser; - - @Inject - LsConfigurationExtractor(JsonParser jsonParser) { - this.jsonParser = jsonParser; - } - - LanguageServerDescription extract(Map attributes) { - String config = attributes.get("config"); - JsonObject configJsonObject = jsonParser.parse(config).getAsJsonObject(); - String id = getId(configJsonObject); - List languageIds = getLanguageIds(configJsonObject); - List fileWatchPatterns = getFileWatchPatterns(configJsonObject); - List documentFilters = getDocumentFilters(configJsonObject); - - if (languageIds.isEmpty()) { - throw new IllegalStateException( - "Language server is not properly configured in workspace configuration: language ids list is empty"); - } - return new LanguageServerDescription(id, languageIds, documentFilters, fileWatchPatterns); - } - - private String getId(JsonObject jsonObject) { - return !jsonObject.has("id") ? null : jsonObject.get("id").getAsString(); - } - - private List getLanguageIds(JsonObject jsonObject) { - if (!jsonObject.has("languageIds")) { - return emptyList(); - } - - JsonArray languageIdsJsonArray = jsonObject.get("languageIds").getAsJsonArray(); - int size = languageIdsJsonArray.size(); - List languageIds = new ArrayList<>(size); - for (int i = 0; i < size; i++) { - String languageId = languageIdsJsonArray.get(i).getAsString(); - languageIds.add(languageId); - } - - return unmodifiableList(languageIds); - } - - private List getFileWatchPatterns(JsonObject jsonObject) { - if (!jsonObject.has("fileWatchPatterns")) { - return emptyList(); - } - - JsonArray fileWatchPatternsJsonArray = jsonObject.get("fileWatchPatterns").getAsJsonArray(); - int size = fileWatchPatternsJsonArray.size(); - List fileWatchPatterns = new ArrayList<>(size); - for (int i = 0; i < size; i++) { - String fileWatchPattern = fileWatchPatternsJsonArray.get(i).getAsString(); - fileWatchPatterns.add(fileWatchPattern); - } - - return unmodifiableList(fileWatchPatterns); - } - - private List getDocumentFilters(JsonObject jsonObject) { - if (!jsonObject.has("documentFilters")) { - return emptyList(); - } - JsonArray documentFiltersJsonArray = jsonObject.get("documentFilters").getAsJsonArray(); - - int size = documentFiltersJsonArray.size(); - List documentFilters = new ArrayList<>(size); - for (int i = 0; i < size; i++) { - JsonObject documentFilterJsonObject = documentFiltersJsonArray.get(i).getAsJsonObject(); - - String pathRegex; - if (documentFilterJsonObject.has("pathRegex")) { - pathRegex = documentFilterJsonObject.get("pathRegex").getAsString(); - } else { - pathRegex = null; - } - - String languageId; - if (documentFilterJsonObject.has("languageId")) { - languageId = documentFilterJsonObject.get("languageId").getAsString(); - } else { - languageId = null; - } - - String schema; - if (documentFilterJsonObject.has("scheme")) { - schema = documentFilterJsonObject.get("scheme").getAsString(); - } else { - schema = null; - } - - DocumentFilter documentFilter = new DocumentFilter(languageId, pathRegex, schema); - documentFilters.add(documentFilter); - } - - return unmodifiableList(documentFilters); - } -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/remote/LsRemoteModule.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/remote/LsRemoteModule.java deleted file mode 100644 index 3eb5fb11674..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/remote/LsRemoteModule.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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.api.languageserver.remote; - -import com.google.inject.AbstractModule; -import com.google.inject.multibindings.Multibinder; - -public class LsRemoteModule extends AbstractModule { - - @Override - protected void configure() { - Multibinder.newSetBinder(binder(), RemoteLsLauncherProvider.class) - .addBinding() - .to(SocketLsLauncherProvider.class); - } -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/remote/RemoteLsLauncherProvider.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/remote/RemoteLsLauncherProvider.java deleted file mode 100644 index cc5afa4edcb..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/remote/RemoteLsLauncherProvider.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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.api.languageserver.remote; - -import java.util.Set; -import org.eclipse.che.api.core.model.workspace.Workspace; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; - -/** - * Provides remote language server launcher instances constructed according to workspace - * configuration. The launchers (depending on context) may in fact not physically launch the - * language servers but establish a remote connection to already launched servers. - */ -public interface RemoteLsLauncherProvider { - /** - * Get all remote language server launchers that are mentioned in a workspace configuration. - * - * @param workspace workspace configuration - * @return set of language server launchers - */ - Set getAll(Workspace workspace); -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/remote/SocketLsLauncherProvider.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/remote/SocketLsLauncherProvider.java deleted file mode 100644 index 5eb567ef764..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/remote/SocketLsLauncherProvider.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * 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.api.languageserver.remote; - -import static java.util.Collections.emptySet; -import static java.util.Collections.unmodifiableSet; -import static org.slf4j.LoggerFactory.getLogger; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import javax.inject.Inject; -import javax.inject.Singleton; -import org.eclipse.che.api.core.model.workspace.Runtime; -import org.eclipse.che.api.core.model.workspace.Workspace; -import org.eclipse.che.api.core.model.workspace.runtime.Machine; -import org.eclipse.che.api.core.model.workspace.runtime.Server; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.registry.LanguageServerDescription; -import org.eclipse.lsp4j.jsonrpc.Launcher; -import org.eclipse.lsp4j.services.LanguageClient; -import org.eclipse.lsp4j.services.LanguageServer; -import org.slf4j.Logger; - -/** Provides socket based language server launchers */ -@Singleton -class SocketLsLauncherProvider implements RemoteLsLauncherProvider { - private static final Logger LOG = getLogger(SocketLsLauncherProvider.class); - - private final LsConfigurationDetector lsConfigurationDetector; - private final LsConfigurationExtractor lsConfigurationExtractor; - - private final Map lslRegistry = new ConcurrentHashMap<>(); - - @Inject - public SocketLsLauncherProvider( - LsConfigurationDetector lsConfigurationDetector, - LsConfigurationExtractor lsConfigurationExtractor) { - this.lsConfigurationDetector = lsConfigurationDetector; - this.lsConfigurationExtractor = lsConfigurationExtractor; - } - - @Override - public Set getAll(Workspace workspace) { - Runtime runtime = workspace.getRuntime(); - if (runtime == null) { - return emptySet(); - } - - for (Map.Entry machineEntry : runtime.getMachines().entrySet()) { - String machineName = machineEntry.getKey(); - Machine machine = machineEntry.getValue(); - Map servers = machine.getServers(); - - for (Map.Entry serverEntry : servers.entrySet()) { - String serverName = serverEntry.getKey(); - Server server = serverEntry.getValue(); - String serverUrl = server.getUrl(); - Map serverAttributes = server.getAttributes(); - - if (lslRegistry.keySet().contains(machineName + serverName)) { - continue; - } - - if (!lsConfigurationDetector.isDetected(serverAttributes)) { - continue; - } - - LanguageServerDescription description = lsConfigurationExtractor.extract(serverAttributes); - - try { - URI uri = new URI(serverUrl); - String host = uri.getHost(); - int port = uri.getPort(); - - SocketLanguageServerLauncher launcher = - new SocketLanguageServerLauncher(description, host, port); - lslRegistry.put(machineName + serverName, launcher); - } catch (URISyntaxException e) { - LOG.error("Can't parse server url: {}", serverUrl, e); - } - } - } - - return unmodifiableSet(new HashSet<>(lslRegistry.values())); - } - - private static final class SocketLanguageServerLauncher implements LanguageServerLauncher { - - private final LanguageServerDescription languageServerDescription; - private final String host; - private final int port; - - SocketLanguageServerLauncher( - LanguageServerDescription languageServerDescription, String host, int port) { - this.languageServerDescription = languageServerDescription; - this.host = host; - this.port = port; - } - - @Override - public LanguageServer launch(String projectPath, LanguageClient client) - throws LanguageServerException { - try { - Socket socket = new Socket(host, port); - socket.setKeepAlive(true); - InputStream inputStream = socket.getInputStream(); - OutputStream outputStream = socket.getOutputStream(); - - Launcher launcher = - Launcher.createLauncher(client, LanguageServer.class, inputStream, outputStream); - - launcher.startListening(); - return launcher.getRemoteProxy(); - } catch (IOException e) { - throw new LanguageServerException( - "Can't launch language server for project: " + projectPath, e); - } - } - - @Override - public boolean isLocal() { - return false; - } - - @Override - public LanguageServerDescription getDescription() { - return languageServerDescription; - } - - @Override - public boolean isAbleToLaunch() { - return host != null && languageServerDescription != null; - } - } -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/LanguageRegistryService.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/LanguageRegistryService.java deleted file mode 100644 index 3c240c08edb..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/LanguageRegistryService.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.api.languageserver.service; - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import java.util.List; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.registry.LanguageServerRegistry; -import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.ServerCapabilitiesDto; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; -import org.eclipse.lsp4j.ServerCapabilities; - -@Singleton -@Path("languageserver") -public class LanguageRegistryService { - - private final LanguageServerRegistry registry; - - @Inject - public LanguageRegistryService(LanguageServerRegistry registry) { - this.registry = registry; - } - - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("supported") - public List getSupportedLanguages() { - return registry.getSupportedLanguages(); - } - - @POST - @Produces(MediaType.APPLICATION_JSON) - @Path("initialize") - public ServerCapabilitiesDto initialize(@QueryParam("path") String path) - throws LanguageServerException { - // in most cases starts new LS if not already started - ServerCapabilities capabilities = registry.initialize(LanguageServiceUtils.prefixURI(path)); - return capabilities == null ? null : new ServerCapabilitiesDto(capabilities); - } -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/LanguageServerInitializationHandler.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/LanguageServerInitializationHandler.java deleted file mode 100644 index 85b5f77e26e..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/LanguageServerInitializationHandler.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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.api.languageserver.service; - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import org.eclipse.che.api.core.jsonrpc.commons.JsonRpcException; -import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.registry.LanguageServerRegistry; -import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.ServerCapabilitiesDto; -import org.eclipse.lsp4j.ServerCapabilities; - -@Singleton -public class LanguageServerInitializationHandler { - - @Inject - public LanguageServerInitializationHandler( - RequestHandlerConfigurator requestHandlerConfigurator, LanguageServerRegistry registry) { - requestHandlerConfigurator - .newConfiguration() - .methodName("languageServer/initialize") - .paramsAsString() - .resultAsDto(ServerCapabilitiesDto.class) - .withFunction( - path -> { - try { - ServerCapabilities capabilities = - registry.initialize(LanguageServiceUtils.prefixURI(path)); - return capabilities == null ? null : new ServerCapabilitiesDto(capabilities); - } catch (LanguageServerException e) { - throw new JsonRpcException(-27000, e.getMessage()); - } - }); - } -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/TextDocumentService.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/TextDocumentService.java deleted file mode 100644 index 406c1aaa691..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/TextDocumentService.java +++ /dev/null @@ -1,944 +0,0 @@ -/* - * 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.api.languageserver.service; - -import static org.eclipse.che.api.languageserver.service.LanguageServiceUtils.isStartWithProject; -import static org.eclipse.che.api.languageserver.service.LanguageServiceUtils.prefixProject; -import static org.eclipse.che.api.languageserver.service.LanguageServiceUtils.prefixURI; -import static org.eclipse.che.api.languageserver.service.LanguageServiceUtils.removePrefixUri; -import static org.eclipse.che.api.languageserver.service.LanguageServiceUtils.removeUriScheme; - -import com.google.inject.Singleton; -import java.io.File; -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.stream.Collectors; -import javax.annotation.PostConstruct; -import javax.inject.Inject; -import org.eclipse.che.api.core.jsonrpc.commons.JsonRpcException; -import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.registry.InitializedLanguageServer; -import org.eclipse.che.api.languageserver.registry.LanguageServerRegistry; -import org.eclipse.che.api.languageserver.registry.LanguageServerRegistryImpl; -import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.CommandDto; -import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.CompletionItemDto; -import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.DocumentHighlightDto; -import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.ExtendedCompletionItemDto; -import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.ExtendedCompletionListDto; -import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.HoverDto; -import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.LocationDto; -import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.RenameResultDto; -import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.SignatureHelpDto; -import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.SymbolInformationDto; -import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.TextEditDto; -import org.eclipse.che.api.languageserver.shared.model.ExtendedCompletionItem; -import org.eclipse.che.api.languageserver.shared.model.ExtendedTextDocumentEdit; -import org.eclipse.che.api.languageserver.shared.model.ExtendedTextEdit; -import org.eclipse.che.api.languageserver.shared.model.ExtendedWorkspaceEdit; -import org.eclipse.che.api.languageserver.shared.model.RenameResult; -import org.eclipse.che.api.languageserver.util.LSOperation; -import org.eclipse.che.api.languageserver.util.OperationUtil; -import org.eclipse.jface.text.BadLocationException; -import org.eclipse.jface.text.Document; -import org.eclipse.jface.text.IRegion; -import org.eclipse.lsp4j.CodeActionParams; -import org.eclipse.lsp4j.Command; -import org.eclipse.lsp4j.CompletionItem; -import org.eclipse.lsp4j.CompletionList; -import org.eclipse.lsp4j.DidChangeTextDocumentParams; -import org.eclipse.lsp4j.DidCloseTextDocumentParams; -import org.eclipse.lsp4j.DidOpenTextDocumentParams; -import org.eclipse.lsp4j.DidSaveTextDocumentParams; -import org.eclipse.lsp4j.DocumentFormattingParams; -import org.eclipse.lsp4j.DocumentHighlight; -import org.eclipse.lsp4j.DocumentOnTypeFormattingParams; -import org.eclipse.lsp4j.DocumentRangeFormattingParams; -import org.eclipse.lsp4j.DocumentSymbolParams; -import org.eclipse.lsp4j.Hover; -import org.eclipse.lsp4j.Location; -import org.eclipse.lsp4j.ReferenceParams; -import org.eclipse.lsp4j.RenameParams; -import org.eclipse.lsp4j.SignatureHelp; -import org.eclipse.lsp4j.SymbolInformation; -import org.eclipse.lsp4j.TextDocumentEdit; -import org.eclipse.lsp4j.TextDocumentIdentifier; -import org.eclipse.lsp4j.TextDocumentPositionParams; -import org.eclipse.lsp4j.TextEdit; -import org.eclipse.lsp4j.VersionedTextDocumentIdentifier; -import org.eclipse.lsp4j.WorkspaceEdit; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Json RPC API for the textDoc - * - *

Dispatches onto the {@link LanguageServerRegistryImpl}. - */ -@Singleton -public class TextDocumentService { - private static final Logger LOG = LoggerFactory.getLogger(TextDocumentService.class); - - private final LanguageServerRegistry languageServerRegistry; - private final RequestHandlerConfigurator requestHandler; - - @Inject - public TextDocumentService( - LanguageServerRegistry languageServerRegistry, RequestHandlerConfigurator requestHandler) { - this.languageServerRegistry = languageServerRegistry; - this.requestHandler = requestHandler; - } - - @PostConstruct - public void configureMethods() { - dtoToDtoList( - "definition", TextDocumentPositionParams.class, LocationDto.class, this::definition); - dtoToDtoList("codeAction", CodeActionParams.class, CommandDto.class, this::codeAction); - dtoToDtoList( - "documentSymbol", - DocumentSymbolParams.class, - SymbolInformationDto.class, - this::documentSymbol); - dtoToDtoList("formatting", DocumentFormattingParams.class, TextEditDto.class, this::formatting); - dtoToDtoList( - "rangeFormatting", - DocumentRangeFormattingParams.class, - TextEditDto.class, - this::rangeFormatting); - dtoToDtoList("references", ReferenceParams.class, LocationDto.class, this::references); - dtoToDtoList( - "onTypeFormatting", - DocumentOnTypeFormattingParams.class, - TextEditDto.class, - this::onTypeFormatting); - - dtoToDto( - "completionItem/resolve", - ExtendedCompletionItem.class, - ExtendedCompletionItemDto.class, - this::completionItemResolve); - dtoToDto( - "documentHighlight", - TextDocumentPositionParams.class, - DocumentHighlight.class, - this::documentHighlight); - dtoToDto( - "completion", - TextDocumentPositionParams.class, - ExtendedCompletionListDto.class, - this::completion); - dtoToDto("hover", TextDocumentPositionParams.class, HoverDto.class, this::hover); - dtoToDto( - "signatureHelp", - TextDocumentPositionParams.class, - SignatureHelpDto.class, - this::signatureHelp); - - dtoToDto("rename", RenameParams.class, RenameResultDto.class, this::rename); - - dtoToNothing("didChange", DidChangeTextDocumentParams.class, this::didChange); - dtoToNothing("didClose", DidCloseTextDocumentParams.class, this::didClose); - dtoToNothing("didOpen", DidOpenTextDocumentParams.class, this::didOpen); - dtoToNothing("didSave", DidSaveTextDocumentParams.class, this::didSave); - } - - private List codeAction(CodeActionParams params) { - TextDocumentIdentifier textDocument = params.getTextDocument(); - String uri = prefixURI(textDocument.getUri()); - textDocument.setUri(uri); - List result = new ArrayList<>(); - try { - List servers = - languageServerRegistry - .getApplicableLanguageServers(uri) - .stream() - .flatMap(Collection::stream) - .collect(Collectors.toList()); - LSOperation> op = - new LSOperation>() { - - @Override - public boolean canDo(InitializedLanguageServer server) { - return truish(server.getInitializeResult().getCapabilities().getCodeActionProvider()); - } - - public CompletableFuture> start( - InitializedLanguageServer element) { - return element.getServer().getTextDocumentService().codeAction(params); - }; - - @Override - public boolean handleResult( - InitializedLanguageServer element, List res) { - for (Command cmd : res) { - result.add(new CommandDto(cmd)); - } - return false; - }; - }; - OperationUtil.doInParallel(servers, op, 10000); - return result; - } catch (LanguageServerException e) { - throw new JsonRpcException(-27000, e.getMessage()); - } - } - - private ExtendedCompletionListDto completion( - TextDocumentPositionParams textDocumentPositionParams) { - try { - TextDocumentIdentifier textDocument = textDocumentPositionParams.getTextDocument(); - String uri = prefixURI(textDocument.getUri()); - textDocument.setUri(uri); - textDocumentPositionParams.setUri(prefixURI(textDocumentPositionParams.getUri())); - ExtendedCompletionListDto[] result = new ExtendedCompletionListDto[1]; - - LSOperation, ExtendedCompletionListDto> op = - new LSOperation, ExtendedCompletionListDto>() { - - @Override - public boolean canDo(Collection servers) { - return true; - } - - @Override - public CompletableFuture start( - Collection element) { - return CompletableFuture.supplyAsync( - () -> { - ExtendedCompletionListDto res = new ExtendedCompletionListDto(); - List items = new ArrayList<>(); - res.setItems(items); - LSOperation< - InitializedLanguageServer, Either, CompletionList>> - op2 = - new LSOperation< - InitializedLanguageServer, - Either, CompletionList>>() { - - @Override - public boolean canDo(InitializedLanguageServer element) { - return element - .getInitializeResult() - .getCapabilities() - .getCompletionProvider() - != null; - } - - @Override - public CompletableFuture, CompletionList>> - start(InitializedLanguageServer element) { - return element - .getServer() - .getTextDocumentService() - .completion(textDocumentPositionParams); - } - - @Override - public boolean handleResult( - InitializedLanguageServer element, - Either, CompletionList> result) { - List itemList; - if (result.isRight()) { - res.setInComplete( - res.isInComplete() && result.getRight().isIncomplete()); - itemList = result.getRight().getItems(); - } else { - itemList = result.getLeft(); - } - - for (CompletionItem item : itemList) { - ExtendedCompletionItemDto exItem = - new ExtendedCompletionItemDto(); - exItem.setItem(new CompletionItemDto(item)); - exItem.setLanguageServerId(element.getId()); - items.add(exItem); - } - return false; - } - }; - OperationUtil.doInParallel(element, op2, 30000); - - return res; - }); - } - - @Override - public boolean handleResult( - Collection element, ExtendedCompletionListDto list) { - result[0] = list; - return !list.getItems().isEmpty(); - } - }; - OperationUtil.doInSequence( - languageServerRegistry.getApplicableLanguageServers(uri), op, 10000); - return result[0]; - } catch (LanguageServerException e) { - throw new JsonRpcException(-27000, e.getMessage()); - } - } - - private List documentSymbol(DocumentSymbolParams documentSymbolParams) { - String uri = prefixURI(documentSymbolParams.getTextDocument().getUri()); - documentSymbolParams.getTextDocument().setUri(uri); - List result = new ArrayList<>(); - try { - List servers = - languageServerRegistry - .getApplicableLanguageServers(uri) - .stream() - .flatMap(Collection::stream) - .collect(Collectors.toList()); - OperationUtil.doInParallel( - servers, - new LSOperation>() { - - @Override - public boolean canDo(InitializedLanguageServer element) { - return truish( - element.getInitializeResult().getCapabilities().getDocumentSymbolProvider()); - } - - @Override - public CompletableFuture> start( - InitializedLanguageServer element) { - return element - .getServer() - .getTextDocumentService() - .documentSymbol(documentSymbolParams); - } - - @Override - public boolean handleResult( - InitializedLanguageServer element, List locations) { - locations.forEach( - o -> { - o.getLocation().setUri(removePrefixUri(o.getLocation().getUri())); - result.add(new SymbolInformationDto(o)); - }); - return true; - } - }, - 10000); - return result; - - } catch (LanguageServerException e) { - throw new JsonRpcException(-27000, e.getMessage()); - } - } - - private List references(ReferenceParams referenceParams) { - String uri = prefixURI(referenceParams.getTextDocument().getUri()); - referenceParams.getTextDocument().setUri(uri); - List result = new ArrayList<>(); - try { - List servers = - languageServerRegistry - .getApplicableLanguageServers(uri) - .stream() - .flatMap(Collection::stream) - .collect(Collectors.toList()); - OperationUtil.doInParallel( - servers, - new LSOperation>() { - - @Override - public boolean canDo(InitializedLanguageServer element) { - return truish( - element.getInitializeResult().getCapabilities().getReferencesProvider()); - } - - @Override - public CompletableFuture> start( - InitializedLanguageServer element) { - return element.getServer().getTextDocumentService().references(referenceParams); - } - - @Override - public boolean handleResult( - InitializedLanguageServer element, List locations) { - locations.forEach( - o -> { - o.setUri(removePrefixUri(o.getUri())); - result.add(new LocationDto(o)); - }); - return true; - } - }, - 30000); - return result; - } catch (LanguageServerException e) { - throw new JsonRpcException(-27000, e.getMessage()); - } - } - - private List definition(TextDocumentPositionParams textDocumentPositionParams) { - String uri = prefixURI(textDocumentPositionParams.getTextDocument().getUri()); - textDocumentPositionParams.getTextDocument().setUri(uri); - try { - List servers = - languageServerRegistry - .getApplicableLanguageServers(uri) - .stream() - .flatMap(Collection::stream) - .collect(Collectors.toList()); - List result = new ArrayList<>(); - OperationUtil.doInParallel( - servers, - new LSOperation>() { - - @Override - public boolean canDo(InitializedLanguageServer element) { - return truish( - element.getInitializeResult().getCapabilities().getDefinitionProvider()); - } - - @Override - public CompletableFuture> start( - InitializedLanguageServer element) { - return element - .getServer() - .getTextDocumentService() - .definition(textDocumentPositionParams); - } - - @Override - public boolean handleResult( - InitializedLanguageServer element, List locations) { - locations.forEach( - o -> { - o.setUri(removePrefixUri(o.getUri())); - result.add(new LocationDto(o)); - }); - return true; - } - }, - 30000); - return result; - } catch (LanguageServerException e) { - throw new JsonRpcException(-27000, e.getMessage()); - } - } - - private ExtendedCompletionItemDto completionItemResolve(ExtendedCompletionItem unresolved) { - try { - InitializedLanguageServer server = - languageServerRegistry.getServer(unresolved.getLanguageServerId()); - - if (server != null) { - ExtendedCompletionItem res = new ExtendedCompletionItem(); - res.setItem( - server - .getServer() - .getTextDocumentService() - .resolveCompletionItem(unresolved.getItem()) - .get()); - res.setLanguageServerId(unresolved.getLanguageServerId()); - return new ExtendedCompletionItemDto(res); - } - return new ExtendedCompletionItemDto(unresolved); - } catch (InterruptedException | ExecutionException e) { - throw new JsonRpcException(-27000, e.getMessage()); - } - } - - private HoverDto hover(TextDocumentPositionParams positionParams) { - String uri = prefixURI(positionParams.getTextDocument().getUri()); - positionParams.getTextDocument().setUri(uri); - positionParams.setUri(prefixURI(positionParams.getUri())); - HoverDto result = new HoverDto(); - result.setContents(new ArrayList<>()); - try { - - List servers = - languageServerRegistry - .getApplicableLanguageServers(uri) - .stream() - .flatMap(Collection::stream) - .collect(Collectors.toList()); - OperationUtil.doInParallel( - servers, - new LSOperation() { - - @Override - public boolean canDo(InitializedLanguageServer element) { - return truish(element.getInitializeResult().getCapabilities().getHoverProvider()); - } - - @Override - public CompletableFuture start(InitializedLanguageServer element) { - return element.getServer().getTextDocumentService().hover(positionParams); - } - - @Override - public boolean handleResult(InitializedLanguageServer element, Hover hover) { - if (hover != null) { - HoverDto hoverDto = new HoverDto(hover); - result.getContents().addAll(hoverDto.getContents()); - } - return true; - } - }, - 10000); - return result; - } catch (LanguageServerException e) { - throw new JsonRpcException(-27000, e.getMessage()); - } - } - - private SignatureHelpDto signatureHelp(TextDocumentPositionParams positionParams) { - String uri = prefixURI(positionParams.getTextDocument().getUri()); - positionParams.getTextDocument().setUri(uri); - positionParams.setUri(prefixURI(positionParams.getUri())); - SignatureHelpDto[] result = new SignatureHelpDto[1]; - try { - List servers = - languageServerRegistry - .getApplicableLanguageServers(uri) - .stream() - .flatMap(Collection::stream) - .collect(Collectors.toList()); - LSOperation op = - new LSOperation() { - - @Override - public boolean canDo(InitializedLanguageServer element) { - return element.getInitializeResult().getCapabilities().getSignatureHelpProvider() - != null; - } - - @Override - public CompletableFuture start(InitializedLanguageServer element) { - return element.getServer().getTextDocumentService().signatureHelp(positionParams); - } - - @Override - public boolean handleResult(InitializedLanguageServer element, SignatureHelp res) { - if (res != null && !res.getSignatures().isEmpty()) { - result[0] = new SignatureHelpDto(res); - return true; - } - return false; - } - }; - OperationUtil.doInSequence(servers, op, 10000); - return result[0]; - } catch (LanguageServerException e) { - throw new JsonRpcException(-27000, e.getMessage()); - } - } - - private List formatting(DocumentFormattingParams documentFormattingParams) { - try { - String uri = prefixURI(documentFormattingParams.getTextDocument().getUri()); - documentFormattingParams.getTextDocument().setUri(uri); - InitializedLanguageServer server = - languageServerRegistry - .getApplicableLanguageServers(uri) - .stream() - .flatMap(Collection::stream) - .filter( - s -> - truish( - s.getInitializeResult() - .getCapabilities() - .getDocumentFormattingProvider())) - .findFirst() - .get(); - return server == null - ? Collections.emptyList() - : server - .getServer() - .getTextDocumentService() - .formatting(documentFormattingParams) - .get(5000, TimeUnit.MILLISECONDS) - .stream() - .map(TextEditDto::new) - .collect(Collectors.toList()); - - } catch (InterruptedException - | ExecutionException - | LanguageServerException - | TimeoutException e) { - throw new JsonRpcException(-27000, e.getMessage()); - } - } - - private List rangeFormatting( - DocumentRangeFormattingParams documentRangeFormattingParams) { - try { - String uri = prefixURI(documentRangeFormattingParams.getTextDocument().getUri()); - documentRangeFormattingParams.getTextDocument().setUri(uri); - InitializedLanguageServer server = - languageServerRegistry - .getApplicableLanguageServers(uri) - .stream() - .flatMap(Collection::stream) - .filter( - s -> - truish( - s.getInitializeResult() - .getCapabilities() - .getDocumentRangeFormattingProvider())) - .findFirst() - .get(); - return server == null - ? Collections.emptyList() - : server - .getServer() - .getTextDocumentService() - .rangeFormatting(documentRangeFormattingParams) - .get() - .stream() - .map(TextEditDto::new) - .collect(Collectors.toList()); - } catch (InterruptedException | ExecutionException | LanguageServerException e) { - throw new JsonRpcException(-27000, e.getMessage()); - } - } - - private List onTypeFormatting( - DocumentOnTypeFormattingParams documentOnTypeFormattingParams) { - try { - String uri = prefixURI(documentOnTypeFormattingParams.getTextDocument().getUri()); - documentOnTypeFormattingParams.getTextDocument().setUri(uri); - InitializedLanguageServer server = - languageServerRegistry - .getApplicableLanguageServers(uri) - .stream() - .flatMap(Collection::stream) - .filter( - s -> - s.getInitializeResult() - .getCapabilities() - .getDocumentOnTypeFormattingProvider() - != null) - .findFirst() - .get(); - return server == null - ? Collections.emptyList() - : server - .getServer() - .getTextDocumentService() - .onTypeFormatting(documentOnTypeFormattingParams) - .get() - .stream() - .map(TextEditDto::new) - .collect(Collectors.toList()); - } catch (InterruptedException | ExecutionException | LanguageServerException e) { - throw new JsonRpcException(-27000, e.getMessage()); - } - } - - private void didChange(DidChangeTextDocumentParams didChangeTextDocumentParams) { - try { - String uri = prefixURI(didChangeTextDocumentParams.getTextDocument().getUri()); - didChangeTextDocumentParams.getTextDocument().setUri(uri); - didChangeTextDocumentParams.setUri(prefixURI(didChangeTextDocumentParams.getUri())); - languageServerRegistry - .getApplicableLanguageServers(uri) - .stream() - .flatMap(Collection::stream) - .map(InitializedLanguageServer::getServer) - .forEach( - server -> { - server.getTextDocumentService().didChange(didChangeTextDocumentParams); - }); - } catch (LanguageServerException e) { - LOG.error("Error trying to process textDocument/didChange", e); - } - } - - private void didOpen(DidOpenTextDocumentParams openTextDocumentParams) { - try { - String uri = prefixURI(openTextDocumentParams.getTextDocument().getUri()); - openTextDocumentParams.getTextDocument().setUri(uri); - languageServerRegistry - .getApplicableLanguageServers(uri) - .stream() - .flatMap(Collection::stream) - .map(InitializedLanguageServer::getServer) - .forEach( - server -> { - server.getTextDocumentService().didOpen(openTextDocumentParams); - }); - } catch (LanguageServerException e) { - LOG.error("Error trying to process textDocument/didOpen", e); - } - } - - private void didClose(DidCloseTextDocumentParams didCloseTextDocumentParams) { - try { - String uri = prefixURI(didCloseTextDocumentParams.getTextDocument().getUri()); - didCloseTextDocumentParams.getTextDocument().setUri(uri); - languageServerRegistry - .getApplicableLanguageServers(uri) - .stream() - .flatMap(Collection::stream) - .map(InitializedLanguageServer::getServer) - .forEach( - server -> { - server.getTextDocumentService().didClose(didCloseTextDocumentParams); - }); - } catch (LanguageServerException e) { - LOG.error("Error trying to process textDocument/didOpen", e); - } - } - - private void didSave(DidSaveTextDocumentParams didSaveTextDocumentParams) { - try { - String uri = prefixURI(didSaveTextDocumentParams.getTextDocument().getUri()); - didSaveTextDocumentParams.getTextDocument().setUri(uri); - languageServerRegistry - .getApplicableLanguageServers(uri) - .stream() - .flatMap(Collection::stream) - .map(InitializedLanguageServer::getServer) - .forEach( - server -> { - server.getTextDocumentService().didSave(didSaveTextDocumentParams); - }); - } catch (LanguageServerException e) { - LOG.error("Error trying to process textDocument/didSave", e); - } - } - - private DocumentHighlightDto documentHighlight( - TextDocumentPositionParams textDocumentPositionParams) { - try { - String uri = prefixURI(textDocumentPositionParams.getTextDocument().getUri()); - textDocumentPositionParams.getTextDocument().setUri(uri); - @SuppressWarnings("unchecked") - List[] result = new List[1]; - LSOperation, List> op = - new LSOperation, List>() { - - @Override - public boolean canDo(Collection servers) { - return true; - } - - @Override - public CompletableFuture> start( - Collection element) { - return CompletableFuture.supplyAsync( - () -> { - List res = new ArrayList<>(); - LSOperation> op2 = - new LSOperation< - InitializedLanguageServer, List>() { - - @Override - public boolean canDo(InitializedLanguageServer element) { - return truish( - element - .getInitializeResult() - .getCapabilities() - .getDocumentHighlightProvider()); - } - - @Override - public CompletableFuture> start( - InitializedLanguageServer element) { - return element - .getServer() - .getTextDocumentService() - .documentHighlight(textDocumentPositionParams); - } - - @Override - public boolean handleResult( - InitializedLanguageServer element, - List result) { - - return false; - } - }; - OperationUtil.doInParallel(element, op2, 10000); - - return res; - }); - } - - @Override - public boolean handleResult( - Collection element, List list) { - result[0] = list; - return !list.isEmpty(); - } - }; - OperationUtil.doInSequence( - languageServerRegistry.getApplicableLanguageServers(uri), op, 10000); - - if (!result[0].isEmpty()) { - return result[0].get(0); - } - return null; - } catch (LanguageServerException e) { - throw new JsonRpcException(-27000, e.getMessage()); - } - } - - private RenameResultDto rename(RenameParams renameParams) { - String uri = prefixURI(renameParams.getTextDocument().getUri()); - renameParams.getTextDocument().setUri(uri); - Map edits = new ConcurrentHashMap<>(); - try { - List servers = - languageServerRegistry - .getApplicableLanguageServers(uri) - .stream() - .flatMap(Collection::stream) - .collect(Collectors.toList()); - LSOperation op = - new LSOperation() { - @Override - public boolean canDo(InitializedLanguageServer server) { - Boolean renameProvider = - server.getInitializeResult().getCapabilities().getRenameProvider(); - return renameProvider != null && renameProvider; - } - - @Override - public CompletableFuture start(InitializedLanguageServer element) { - return element.getServer().getTextDocumentService().rename(renameParams); - } - - @Override - public boolean handleResult(InitializedLanguageServer element, WorkspaceEdit result) { - - addRenameResult(edits, element.getLauncher().getDescription().getId(), result); - return true; - } - }; - OperationUtil.doInParallel(servers, op, TimeUnit.SECONDS.toMillis(30)); - } catch (LanguageServerException e) { - throw new JsonRpcException(-27000, e.getMessage()); - } - return new RenameResultDto(new RenameResult(edits)); - } - - private void addRenameResult( - Map map, String id, WorkspaceEdit workspaceEdit) { - - ExtendedWorkspaceEdit result = new ExtendedWorkspaceEdit(); - List edits = new ArrayList<>(); - if (workspaceEdit.getDocumentChanges() != null) { - for (TextDocumentEdit documentEdit : workspaceEdit.getDocumentChanges()) { - ExtendedTextDocumentEdit edit = new ExtendedTextDocumentEdit(); - edit.setTextDocument(documentEdit.getTextDocument()); - edit.getTextDocument().setUri(removePrefixUri(edit.getTextDocument().getUri())); - edit.setEdits( - convertToExtendedEdit( - documentEdit.getEdits(), removeUriScheme(documentEdit.getTextDocument().getUri()))); - edits.add(edit); - } - } else if (workspaceEdit.getChanges() != null) { - for (Entry> entry : workspaceEdit.getChanges().entrySet()) { - ExtendedTextDocumentEdit edit = new ExtendedTextDocumentEdit(); - VersionedTextDocumentIdentifier documentIdentifier = new VersionedTextDocumentIdentifier(); - documentIdentifier.setVersion(-1); - documentIdentifier.setUri(removePrefixUri(entry.getKey())); - edit.setTextDocument(documentIdentifier); - edit.setEdits(convertToExtendedEdit(entry.getValue(), removeUriScheme(entry.getKey()))); - edits.add(edit); - } - } - - if (!edits.isEmpty()) { - result.setDocumentChanges(edits); - map.put(id, result); - } - } - - private List convertToExtendedEdit(List edits, String filePath) { - try { - // for some reason C# LS sends ws related path, - if (!isStartWithProject(filePath)) { - filePath = prefixProject(filePath); - } - String fileContent = - com.google.common.io.Files.toString(new File(filePath), Charset.defaultCharset()); - Document document = new Document(fileContent); - return edits - .stream() - .map( - textEdit -> { - ExtendedTextEdit result = new ExtendedTextEdit(); - result.setRange(textEdit.getRange()); - result.setNewText(textEdit.getNewText()); - try { - IRegion lineInformation = - document.getLineInformation(textEdit.getRange().getStart().getLine()); - String lineText = - document.get(lineInformation.getOffset(), lineInformation.getLength()); - result.setLineText(lineText); - result.setInLineStart(textEdit.getRange().getStart().getCharacter()); - result.setInLineEnd(textEdit.getRange().getEnd().getCharacter()); - } catch (BadLocationException e) { - LOG.error("Can't read file line", e); - } - - return result; - }) - .collect(Collectors.toList()); - } catch (IOException e) { - LOG.error("Can't read file", e); - } - return Collections.emptyList(); - } - - private

void dtoToNothing(String name, Class

pClass, Consumer

consumer) { - requestHandler - .newConfiguration() - .methodName("textDocument/" + name) - .paramsAsDto(pClass) - .noResult() - .withConsumer(consumer); - } - - private void dtoToDtoList( - String name, Class

pClass, Class rClass, Function> function) { - requestHandler - .newConfiguration() - .methodName("textDocument/" + name) - .paramsAsDto(pClass) - .resultAsListOfDto(rClass) - .withFunction(function); - } - - private void dtoToDto( - String name, Class

pClass, Class rClass, Function function) { - requestHandler - .newConfiguration() - .methodName("textDocument/" + name) - .paramsAsDto(pClass) - .resultAsDto(rClass) - .withFunction(function); - } - - private boolean truish(Boolean b) { - return b != null && b; - } -} diff --git a/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/FindIdTest.java b/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/FindIdTest.java new file mode 100644 index 00000000000..6ec96810957 --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/FindIdTest.java @@ -0,0 +1,74 @@ +/* + * 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.api.languageserver; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import com.google.common.collect.ImmutableSet; +import java.util.Set; +import java.util.regex.Pattern; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +@Listeners(MockitoTestNGListener.class) +public class FindIdTest { + + private static final String ID_1 = "id-1"; + private static final String ID_2 = "id-2"; + + private RegistryContainer registryContainer; + + private FindId findId; + + @BeforeMethod + public void setUp() { + registryContainer = new RegistryContainer(); + findId = new FindId(registryContainer); + } + + @Test + public void shouldMatchPathWithSingleId() { + Set patterns_1 = ImmutableSet.of(Pattern.compile(".*[/\\\\]+name\\.extension")); + Set patterns_2 = ImmutableSet.of(Pattern.compile(".*[/\\\\]+name1\\.extension")); + + registryContainer.patternRegistry.add(ID_1, patterns_1); + registryContainer.patternRegistry.add(ID_2, patterns_2); + + Set ids = findId.byPath("/a/b/c/name.extension"); + + assertEquals(ids, ImmutableSet.of(ID_1)); + } + + @Test + public void shouldMatchPathWithTwoIds() { + Set patterns_1 = ImmutableSet.of(Pattern.compile(".*[/\\\\]+name\\.extension")); + Set patterns_2 = ImmutableSet.of(Pattern.compile(".*[/\\\\]+name\\.extension")); + registryContainer.patternRegistry.add(ID_1, patterns_1); + registryContainer.patternRegistry.add(ID_2, patterns_2); + + Set ids = findId.byPath("/a/b/c/name.extension"); + + assertEquals(ids, ImmutableSet.of(ID_1, ID_2)); + } + + @Test + public void shouldNotMatchPath() { + Set patterns_1 = ImmutableSet.of(Pattern.compile(".*[/\\\\]+name\\.extension")); + registryContainer.patternRegistry.add(ID_1, patterns_1); + + Set ids = findId.byPath("/a/b/c/notname.extension"); + + assertTrue(ids.isEmpty()); + } +} diff --git a/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/FindServerTest.java b/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/FindServerTest.java new file mode 100644 index 00000000000..29a8d2c629b --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/FindServerTest.java @@ -0,0 +1,121 @@ +/* + * 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.api.languageserver; + +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; + +import com.google.common.collect.ImmutableSet; +import java.util.Set; +import org.eclipse.lsp4j.ServerCapabilities; +import org.eclipse.lsp4j.services.LanguageServer; +import org.eclipse.lsp4j.services.TextDocumentService; +import org.eclipse.lsp4j.services.WorkspaceService; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +@Listeners(MockitoTestNGListener.class) +public class FindServerTest { + + private static final String ID_1 = "id-1"; + private static final String WS_PATH = "path"; + + @Mock private FindId findId; + + private RegistryContainer registryContainer; + + private FindServer findServer; + + @Mock private ServerCapabilities serverCapabilities; + @Mock private LanguageServer languageServer; + @Mock private TextDocumentService textDocumentService; + @Mock private WorkspaceService workspaceService; + + @BeforeMethod + public void setUp() { + registryContainer = new RegistryContainer(); + findServer = new FindServer(registryContainer, findId); + } + + @Test + public void shouldGetProperExtendedLanguageServerById() { + registryContainer.serverCapabilitiesRegistry.add(ID_1, serverCapabilities); + registryContainer.languageServerRegistry.add(ID_1, languageServer); + + when(languageServer.getTextDocumentService()).thenReturn(textDocumentService); + when(languageServer.getWorkspaceService()).thenReturn(workspaceService); + + ExtendedLanguageServer extendedLanguageServer = findServer.byId(ID_1); + + assertEquals(extendedLanguageServer.getCapabilities(), serverCapabilities); + assertEquals(extendedLanguageServer.getId(), ID_1); + assertEquals(extendedLanguageServer.getWorkspaceService(), workspaceService); + assertEquals(extendedLanguageServer.getTextDocumentService(), textDocumentService); + } + + @Test + public void shouldGetNullWhenNoCapabilities() { + registryContainer.languageServerRegistry.add(ID_1, languageServer); + + ExtendedLanguageServer extendedLanguageServer = findServer.byId(ID_1); + + assertNull(extendedLanguageServer); + } + + @Test + public void shouldGetNullWhenNoLanguageServer() { + registryContainer.serverCapabilitiesRegistry.add(ID_1, serverCapabilities); + + ExtendedLanguageServer extendedLanguageServer = findServer.byId(ID_1); + + assertNull(extendedLanguageServer); + } + + @Test + public void shouldGetProperExtendedServerSetByPath() { + when(findId.byPath(WS_PATH)).thenReturn(ImmutableSet.of(ID_1)); + + registryContainer.serverCapabilitiesRegistry.add(ID_1, serverCapabilities); + registryContainer.languageServerRegistry.add(ID_1, languageServer); + + when(languageServer.getTextDocumentService()).thenReturn(textDocumentService); + when(languageServer.getWorkspaceService()).thenReturn(workspaceService); + + Set extendedLanguageServers = findServer.byPath(WS_PATH); + + assertEquals(1, extendedLanguageServers.size()); + ExtendedLanguageServer extendedLanguageServer = extendedLanguageServers.iterator().next(); + + assertEquals(extendedLanguageServer.getCapabilities(), serverCapabilities); + assertEquals(extendedLanguageServer.getId(), ID_1); + assertEquals(extendedLanguageServer.getWorkspaceService(), workspaceService); + assertEquals(extendedLanguageServer.getTextDocumentService(), textDocumentService); + } + + @Test + public void shouldGetEmptyExtendedServerSetByPath() { + when(findId.byPath(WS_PATH)).thenReturn(ImmutableSet.of()); + + registryContainer.serverCapabilitiesRegistry.add(ID_1, serverCapabilities); + registryContainer.languageServerRegistry.add(ID_1, languageServer); + + when(languageServer.getTextDocumentService()).thenReturn(textDocumentService); + when(languageServer.getWorkspaceService()).thenReturn(workspaceService); + + Set extendedLanguageServers = findServer.byPath(WS_PATH); + + assertEquals(0, extendedLanguageServers.size()); + } +} diff --git a/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/LanguageServerFileWatcherTest.java b/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/LanguageServerFileWatcherTest.java deleted file mode 100644 index f078d3c8857..00000000000 --- a/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/LanguageServerFileWatcherTest.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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.api.languageserver; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.testng.Assert.assertTrue; - -import java.io.File; -import java.nio.file.PathMatcher; -import java.util.Collections; -import java.util.function.Consumer; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.registry.LanguageServerDescription; -import org.eclipse.che.api.languageserver.registry.LanguageServerFileWatcher; -import org.eclipse.che.api.languageserver.registry.ServerInitializer; -import org.eclipse.che.api.languageserver.registry.ServerInitializerObserver; -import org.eclipse.che.api.watcher.server.FileWatcherManager; -import org.eclipse.lsp4j.services.LanguageServer; -import org.eclipse.lsp4j.services.WorkspaceService; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.testng.MockitoTestNGListener; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.Listeners; -import org.testng.annotations.Test; - -/** */ -@Listeners(MockitoTestNGListener.class) -public class LanguageServerFileWatcherTest { - - @Mock private LanguageServerLauncher launcher; - @Mock private LanguageServer server; - @Mock private FileWatcherManager watcherManager; - @Mock private ServerInitializer initializer; - @Captor private ArgumentCaptor> changedCaptor; - - private LanguageServerFileWatcher watcher; - - @AfterMethod - public void tearDown() throws Exception { - if (watcher != null) { - watcher.removeAllWatchers(); - } - } - - @Test - public void testShouldAddObserver() throws Exception { - watcher = new LanguageServerFileWatcher(watcherManager, initializer); - verify(initializer).addObserver(any()); - } - - @Test - public void testRegisterFileWatcher() throws Exception { - ArgumentCaptor argumentCaptor = - ArgumentCaptor.forClass(ServerInitializerObserver.class); - watcher = new LanguageServerFileWatcher(watcherManager, initializer); - verify(initializer).addObserver(argumentCaptor.capture()); - ServerInitializerObserver value = argumentCaptor.getValue(); - - LanguageServerDescription description = - new LanguageServerDescription( - "foo", - Collections.singletonList("bar"), - Collections.emptyList(), - Collections.singletonList("glob:*.foo")); - when(launcher.getDescription()).thenReturn(description); - value.onServerInitialized(launcher, server, null, null); - - ArgumentCaptor pathMatcherCaptor = ArgumentCaptor.forClass(PathMatcher.class); - verify(watcherManager).registerByMatcher(pathMatcherCaptor.capture(), any(), any(), any()); - assertTrue(pathMatcherCaptor.getValue().matches(new File("bar.foo").toPath())); - } - - @Test - public void testSendNotification() throws Exception { - ArgumentCaptor argumentCaptor = - ArgumentCaptor.forClass(ServerInitializerObserver.class); - watcher = new LanguageServerFileWatcher(watcherManager, initializer); - verify(initializer).addObserver(argumentCaptor.capture()); - ServerInitializerObserver value = argumentCaptor.getValue(); - - LanguageServerDescription description = - new LanguageServerDescription( - "foo", - Collections.singletonList("bar"), - Collections.emptyList(), - Collections.singletonList("glob:*.foo")); - when(launcher.getDescription()).thenReturn(description); - - WorkspaceService workspaceService = mock(WorkspaceService.class); - when(server.getWorkspaceService()).thenReturn(workspaceService); - - value.onServerInitialized(launcher, server, null, null); - - verify(watcherManager).registerByMatcher(any(), any(), changedCaptor.capture(), any()); - - changedCaptor.getValue().accept("/p/bar.foo"); - - verify(workspaceService).didChangeWatchedFiles(any()); - } -} diff --git a/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/LocalTestLSLauncher.java b/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/LocalTestLSLauncher.java deleted file mode 100644 index 2538352ed6e..00000000000 --- a/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/LocalTestLSLauncher.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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.api.languageserver; - -import java.io.IOException; -import java.util.List; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncherTemplate; -import org.eclipse.che.api.languageserver.registry.LanguageServerDescription; -import org.eclipse.lsp4j.jsonrpc.Launcher; -import org.eclipse.lsp4j.services.LanguageClient; -import org.eclipse.lsp4j.services.LanguageServer; - -/** */ -public class LocalTestLSLauncher extends LanguageServerLauncherTemplate { - - private final LanguageServerDescription description; - private final List command; - - public LocalTestLSLauncher(List command, LanguageServerDescription description) { - this.command = command; - this.description = description; - } - - @Override - protected Process startLanguageServerProcess(String projectPath) throws LanguageServerException { - ProcessBuilder processBuilder = new ProcessBuilder(command); - processBuilder.redirectInput(ProcessBuilder.Redirect.PIPE); - processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE); - try { - return processBuilder.start(); - } catch (IOException e) { - throw new LanguageServerException("Can't start TypeScript language server", e); - } - } - - @Override - protected LanguageServer connectToLanguageServer( - Process languageServerProcess, LanguageClient client) throws LanguageServerException { - Launcher launcher = - Launcher.createLauncher( - client, - LanguageServer.class, - languageServerProcess.getInputStream(), - languageServerProcess.getOutputStream()); - launcher.startListening(); - return launcher.getRemoteProxy(); - } - - @Override - public LanguageServerDescription getDescription() { - return description; - } - - @Override - public boolean isAbleToLaunch() { - return true; - } -} diff --git a/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/ShowMessageRequestTest.java b/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/ShowMessageRequestTest.java deleted file mode 100644 index f5f7a60f2c1..00000000000 --- a/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/ShowMessageRequestTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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.api.languageserver; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import org.eclipse.che.api.core.notification.EventService; -import org.eclipse.che.api.languageserver.messager.ShowMessageJsonRpcTransmitter; -import org.eclipse.che.api.languageserver.registry.CheLanguageClient; -import org.eclipse.che.api.languageserver.registry.LanguageServerDescription; -import org.eclipse.che.api.languageserver.registry.ServerInitializerImpl; -import org.eclipse.che.commons.lang.Pair; -import org.eclipse.lsp4j.InitializeResult; -import org.eclipse.lsp4j.MessageActionItem; -import org.eclipse.lsp4j.MessageType; -import org.eclipse.lsp4j.ShowMessageRequestParams; -import org.eclipse.lsp4j.services.LanguageServer; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.testng.MockitoTestNGListener; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Listeners; -import org.testng.annotations.Test; - -/** */ -@Listeners(MockitoTestNGListener.class) -public class ShowMessageRequestTest { - - private LocalTestLSLauncher launcher; - - @Mock private EventService eventService; - - @Mock private ShowMessageJsonRpcTransmitter transmitter; - private LanguageServer server; - - @BeforeMethod - public void setUp() throws Exception { - List command = new ArrayList<>(); - command.add(ShowMessageRequestTest.class.getResource("/ls").getFile() + "/test-ls/server.sh"); - launcher = - new LocalTestLSLauncher( - command, - new LanguageServerDescription( - "foo", Collections.singletonList("aaa"), Collections.emptyList())); - } - - @AfterMethod - public void tearDown() throws Exception { - if (server != null) { - server.shutdown(); - server.exit(); - } - } - - @Test - public void testName() throws Exception { - CompletableFuture future = new CompletableFuture<>(); - when(transmitter.sendShowMessageRequest(any())).thenReturn(future); - - ServerInitializerImpl initializer = new ServerInitializerImpl(); - CheLanguageClient client = new CheLanguageClient(eventService, transmitter, "id"); - CompletableFuture> initialize = - initializer.initialize(launcher, client, "/tmp"); - Pair resultPair = initialize.get(); - server = resultPair.first; - ArgumentCaptor captor = - ArgumentCaptor.forClass(ShowMessageRequestParams.class); - verify(transmitter, timeout(1500)).sendShowMessageRequest(captor.capture()); - - ShowMessageRequestParams value = captor.getValue(); - assertNotNull(value); - assertEquals(value.getType(), MessageType.Error); - assertEquals(value.getMessage(), "Error Message!!!!"); - } -} diff --git a/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/registry/DefaultLanguageRecognizerTest.java b/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/registry/DefaultLanguageRecognizerTest.java deleted file mode 100644 index 9cc0fdebee8..00000000000 --- a/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/registry/DefaultLanguageRecognizerTest.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * 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.api.languageserver.registry; - -import static java.util.Collections.singletonList; -import static org.eclipse.che.api.languageserver.registry.LanguageRecognizer.UNIDENTIFIED; -import static org.mockito.Mockito.when; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotEquals; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Spy; -import org.mockito.testng.MockitoTestNGListener; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Listeners; -import org.testng.annotations.Test; - -/** Tests for {@link DefaultLanguageRecognizer} */ -@Listeners(MockitoTestNGListener.class) -public class DefaultLanguageRecognizerTest { - - @Spy private Set languages = new HashSet<>(); - @Mock private DefaultLanguages defaultLanguages; - - @InjectMocks private DefaultLanguageRecognizer recognizer; - - @BeforeMethod - public void setUp() { - languages.clear(); - } - - @Test - public void shouldRecognizeDefaultByName() throws Exception { - LanguageDescription expected = new LanguageDescription(); - expected.setFileNames(singletonList("Dockerfile")); - when(defaultLanguages.getAll()).thenReturn(Collections.singleton(expected)); - - LanguageDescription language = recognizer.recognizeByPath("/project/Dockerfile"); - - assertEquals(language, expected); - } - - @Test - public void shouldRecognizeDefaultByExtension() throws Exception { - LanguageDescription expected = new LanguageDescription(); - expected.setFileExtensions(singletonList("java")); - when(defaultLanguages.getAll()).thenReturn(Collections.singleton(expected)); - - LanguageDescription language = recognizer.recognizeByPath("/project/Main.java"); - - assertEquals(language, expected); - } - - @Test - public void shouldRecognizeOverriddenByName() throws Exception { - LanguageDescription expected = new LanguageDescription(); - expected.setFileNames(singletonList("Dockerfile")); - languages.add(expected); - - LanguageDescription language = recognizer.recognizeByPath("/project/Dockerfile"); - - assertEquals(language, expected); - } - - @Test - public void shouldRecognizeOverriddenByExtension() throws Exception { - LanguageDescription expected = new LanguageDescription(); - expected.setFileExtensions(singletonList("java")); - languages.add(expected); - - LanguageDescription language = recognizer.recognizeByPath("/project/Main.java"); - - assertEquals(language, expected); - } - - @Test - public void shouldNotRecognizeByNameWhenNoLanguageSet() throws Exception { - LanguageDescription language = recognizer.recognizeByPath("/project/Dockerfile"); - - assertEquals(language, UNIDENTIFIED); - } - - @Test - public void shouldNotRecognizeByExtensionWhenNoLanguageSet() throws Exception { - LanguageDescription language = recognizer.recognizeByPath("/project/Main.java"); - - assertEquals(language, UNIDENTIFIED); - } - - @Test - public void shouldRecognizeOverriddenPriorToDefaultByName() throws Exception { - LanguageDescription notExpected = new LanguageDescription(); - notExpected.setFileNames(singletonList("Dockerfile")); - notExpected.setLanguageId("expectedId"); - when(defaultLanguages.getAll()).thenReturn(Collections.singleton(notExpected)); - - LanguageDescription expected = new LanguageDescription(); - expected.setFileNames(singletonList("Dockerfile")); - expected.setLanguageId("notExpectedId"); - languages.add(expected); - - LanguageDescription language = recognizer.recognizeByPath("/project/Dockerfile"); - - assertEquals(language, expected); - assertNotEquals(language, notExpected); - } - - @Test - public void shouldRecognizeOverriddenPriorToDefaultByExtension() throws Exception { - LanguageDescription notExpected = new LanguageDescription(); - notExpected.setFileExtensions(singletonList("java")); - notExpected.setLanguageId("expectedId"); - when(defaultLanguages.getAll()).thenReturn(Collections.singleton(notExpected)); - - LanguageDescription expected = new LanguageDescription(); - expected.setFileExtensions(singletonList("java")); - expected.setLanguageId("notExpectedId"); - languages.add(expected); - - LanguageDescription language = recognizer.recognizeByPath("/project/Main.java"); - - assertEquals(language, expected); - assertNotEquals(language, notExpected); - } -} diff --git a/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/registry/LanguageServerRegistryImplTest.java b/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/registry/LanguageServerRegistryImplTest.java deleted file mode 100644 index bf270920089..00000000000 --- a/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/registry/LanguageServerRegistryImplTest.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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.api.languageserver.registry; - -import static java.util.concurrent.CompletableFuture.completedFuture; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; - -import java.util.Collections; -import java.util.HashSet; -import java.util.concurrent.CompletableFuture; -import javax.inject.Provider; -import org.eclipse.che.api.core.model.workspace.Workspace; -import org.eclipse.che.api.core.rest.HttpJsonRequest; -import org.eclipse.che.api.core.rest.HttpJsonRequestFactory; -import org.eclipse.che.api.core.rest.HttpJsonResponse; -import org.eclipse.che.api.languageserver.exception.LanguageServerException; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; -import org.eclipse.che.api.project.server.ProjectManager; -import org.eclipse.che.commons.lang.Pair; -import org.eclipse.lsp4j.InitializeParams; -import org.eclipse.lsp4j.InitializeResult; -import org.eclipse.lsp4j.ServerCapabilities; -import org.eclipse.lsp4j.services.LanguageClient; -import org.eclipse.lsp4j.services.LanguageServer; -import org.eclipse.lsp4j.services.TextDocumentService; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.testng.MockitoTestNGListener; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Listeners; -import org.testng.annotations.Test; - -/** @author Anatoliy Bazko */ -@Listeners(MockitoTestNGListener.class) -public class LanguageServerRegistryImplTest { - - private static final String PREFIX = "file://"; - private static final String FILE_PATH = "/projects/1/test.txt"; - private static final String PROJECT_PATH = "file:///projects/1"; - - @Mock private ServerInitializer initializer; - @Mock private LanguageServerLauncher languageServerLauncher; - @Mock private LanguageDescription languageDescription; - @Mock private LanguageServer languageServer; - @Mock private Provider pmp; - @Mock private ProjectManager pm; - @Mock private CheLanguageClientFactory clientFactory; - @Mock private CheLanguageClient languageClient; - @Mock private HttpJsonRequestFactory httpJsonRequestFactory; - @Mock private HttpJsonRequest httpJsonRequest; - @Mock private HttpJsonResponse httpJsonResponse; - @Mock private Workspace workspace; - @Mock private LanguageRecognizer languageRecognizer; - - private LanguageServerRegistryImpl registry; - private LanguageServerDescription serverDescription; - private InitializeResult initializeResult; - private CompletableFuture completableFuture; - private ServerCapabilities serverCapabilities; - - @BeforeMethod - public void setUp() throws Exception { - - this.serverCapabilities = new ServerCapabilities(); - serverDescription = - new LanguageServerDescription( - "foo", Collections.singletonList("id"), Collections.emptyList()); - initializeResult = new InitializeResult(serverCapabilities); - - completableFuture = completedFuture(initializeResult); - - when(languageServerLauncher.isAbleToLaunch()).thenReturn(true); - when(languageServerLauncher.getDescription()).thenReturn(serverDescription); - when(languageServerLauncher.isLocal()).thenReturn(true); - when(languageDescription.getLanguageId()).thenReturn("id"); - when(languageDescription.getFileExtensions()).thenReturn(Collections.singletonList("txt")); - when(languageDescription.getMimeType()).thenReturn("plain/text"); - - when(languageServer.getTextDocumentService()).thenReturn(mock(TextDocumentService.class)); - when(languageServer.initialize(any(InitializeParams.class))).thenReturn(completableFuture); - - when(languageRecognizer.recognizeByPath(anyString())).thenReturn(languageDescription); - when(languageRecognizer.recognizeById(anyString())).thenReturn(languageDescription); - - when(pmp.get()).thenReturn(pm); - - when(clientFactory.create(anyString())).thenReturn(languageClient); - - when(httpJsonRequestFactory.fromUrl(any(String.class))).thenReturn(httpJsonRequest); - when(httpJsonRequest.useGetMethod()).thenReturn(httpJsonRequest); - when(httpJsonRequest.request()).thenReturn(httpJsonResponse); - when(httpJsonResponse.asDto(any())).thenReturn(workspace); - - registry = - spy( - new LanguageServerRegistryImpl( - "", - "", - httpJsonRequestFactory, - new HashSet<>(), - Collections.singleton(languageServerLauncher), - Collections.singleton(languageDescription), - pmp, - initializer, - null, - clientFactory, - languageRecognizer) { - @Override - protected String extractProjectPath(String filePath) throws LanguageServerException { - return PROJECT_PATH; - } - }); - - when(initializer.initialize( - any(LanguageServerLauncher.class), any(LanguageClient.class), anyString())) - .thenAnswer(invocation -> completedFuture(Pair.of(languageServer, initializeResult))); - } - - @Test - public void testFindServer() throws Exception { - ServerCapabilities cap = registry.initialize(PREFIX + FILE_PATH); - - assertNotNull(cap); - assertEquals(cap, serverCapabilities); - Mockito.verify(initializer) - .initialize(eq(languageServerLauncher), any(LanguageClient.class), eq(PROJECT_PATH)); - } -} diff --git a/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/registry/ServerInitializerImplTest.java b/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/registry/ServerInitializerImplTest.java deleted file mode 100644 index 10e620f72ee..00000000000 --- a/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/registry/ServerInitializerImplTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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.api.languageserver.registry; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.testng.Assert.assertEquals; - -import java.util.concurrent.CompletableFuture; -import org.eclipse.che.api.core.notification.EventService; -import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; -import org.eclipse.che.commons.lang.Pair; -import org.eclipse.lsp4j.InitializeParams; -import org.eclipse.lsp4j.InitializeResult; -import org.eclipse.lsp4j.ServerCapabilities; -import org.eclipse.lsp4j.services.LanguageServer; -import org.mockito.Mock; -import org.mockito.testng.MockitoTestNGListener; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Listeners; -import org.testng.annotations.Test; - -/** @author Anatoliy Bazko */ -@Listeners(MockitoTestNGListener.class) -public class ServerInitializerImplTest { - - @Mock private ServerInitializerObserver observer; - @Mock private LanguageDescription languageDescription; - @Mock private LanguageServerDescription serverDescription; - @Mock private LanguageServerLauncher launcher; - @Mock private LanguageServer server; - @Mock private EventService eventService; - - private CompletableFuture completableFuture; - private ServerInitializerImpl initializer; - - @BeforeMethod - public void setUp() throws Exception { - initializer = spy(new ServerInitializerImpl()); - completableFuture = - CompletableFuture.completedFuture(new InitializeResult(new ServerCapabilities())); - } - - @Test - public void initializerShouldNotifyObservers() throws Exception { - when(languageDescription.getLanguageId()).thenReturn("languageId"); - when(server.initialize(any(InitializeParams.class))).thenReturn(completableFuture); - - when(launcher.launch(anyString(), any())).thenReturn(server); - when(launcher.getDescription()).thenReturn(serverDescription); - when(serverDescription.getId()).thenReturn("launcherId"); - doNothing().when(initializer).registerCallbacks(any(), any()); - - initializer.addObserver(observer); - Pair initResult = - initializer.initialize(launcher, null, "/path").get(); - - assertEquals(server, initResult.first); - verify(observer, timeout(2000)) - .onServerInitialized(eq(launcher), eq(server), any(ServerCapabilities.class), eq("/path")); - } -} diff --git a/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/remote/LsConfigurationExtractorTest.java b/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/remote/LsConfigurationExtractorTest.java deleted file mode 100644 index 2dad47c8f9a..00000000000 --- a/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/remote/LsConfigurationExtractorTest.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * 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.api.languageserver.remote; - -import static org.testng.Assert.*; - -import com.google.gson.JsonParser; -import java.util.Collections; -import java.util.Map; -import org.eclipse.che.api.languageserver.registry.LanguageServerDescription; -import org.mockito.testng.MockitoTestNGListener; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Listeners; -import org.testng.annotations.Test; - -/** Tests for {@link LsConfigurationExtractor} */ -@Listeners(MockitoTestNGListener.class) -public class LsConfigurationExtractorTest { - - private LsConfigurationExtractor lsConfigurationExtractor; - - @BeforeMethod - public void setUp() throws Exception { - lsConfigurationExtractor = new LsConfigurationExtractor(new JsonParser()); - } - - @Test - public void shouldExtractId() throws Exception { - Map attributes = - Collections.singletonMap( - "config", "{\"id\":\"testId\" ,\"languageIds\": [\"languageId\"]}"); - - LanguageServerDescription languageServerDescription = - lsConfigurationExtractor.extract(attributes); - - assertEquals(languageServerDescription.getId(), "testId"); - } - - @Test - public void shouldExtractNullWhenIdIsNotMentioned() throws Exception { - Map attributes = - Collections.singletonMap("config", "{\"languageIds\": [\"languageId\"]}"); - - LanguageServerDescription languageServerDescription = - lsConfigurationExtractor.extract(attributes); - - assertNull(languageServerDescription.getId()); - } - - @Test - public void shouldExtractLanguageIds() throws Exception { - Map attributes = - Collections.singletonMap("config", "{\"languageIds\": [\"languageId\"] }"); - - LanguageServerDescription languageServerDescription = - lsConfigurationExtractor.extract(attributes); - - assertEquals( - languageServerDescription.getLanguageIds(), Collections.singletonList("languageId")); - } - - @Test(expectedExceptions = IllegalStateException.class) - public void shouldThrowExceptionWhenExtractEmptyLanguageIdsForEmptyArray() throws Exception { - Map attributes = Collections.singletonMap("config", "{\"languageIds\": [] }"); - - LanguageServerDescription languageServerDescription = - lsConfigurationExtractor.extract(attributes); - - assertTrue(languageServerDescription.getLanguageIds().isEmpty()); - } - - @Test(expectedExceptions = IllegalStateException.class) - public void shouldThrowExceptionWhenExtractEmptyLanguageIdsWhenLanguageIdsAreNotMentioned() - throws Exception { - Map attributes = Collections.singletonMap("config", "{ }"); - - LanguageServerDescription languageServerDescription = - lsConfigurationExtractor.extract(attributes); - - assertTrue(languageServerDescription.getLanguageIds().isEmpty()); - } - - @Test - public void shouldExtractFileWatchPatterns() throws Exception { - Map attributes = - Collections.singletonMap( - "config", - "{\"fileWatchPatterns\": [\"fileWatchPattern\"], \"languageIds\": [\"languageId\"] }"); - - LanguageServerDescription languageServerDescription = - lsConfigurationExtractor.extract(attributes); - - assertEquals( - languageServerDescription.getFileWatchPatterns(), - Collections.singletonList("fileWatchPattern")); - } - - @Test - public void shouldExtractEmptyFileWatchPatternsForEmptyArray() throws Exception { - Map attributes = - Collections.singletonMap( - "config", "{\"fileWatchPatterns\": [] , \"languageIds\": [\"languageId\"]}"); - - LanguageServerDescription languageServerDescription = - lsConfigurationExtractor.extract(attributes); - - assertTrue(languageServerDescription.getFileWatchPatterns().isEmpty()); - } - - @Test - public void shouldExtractEmptyFileWatchPatternsWhenFileWatchPatternsAreNotMentioned() - throws Exception { - Map attributes = - Collections.singletonMap("config", "{\"languageIds\": [\"languageId\"] }"); - - LanguageServerDescription languageServerDescription = - lsConfigurationExtractor.extract(attributes); - - assertTrue(languageServerDescription.getFileWatchPatterns().isEmpty()); - } - - @Test - public void shouldExtractDocumentFilter() throws Exception { - Map attributes = - Collections.singletonMap( - "config", "{\"documentFilters\": [{}],\"languageIds\": [\"languageId\"] }"); - - LanguageServerDescription languageServerDescription = - lsConfigurationExtractor.extract(attributes); - - assertEquals(languageServerDescription.getDocumentFilters().size(), 1); - } - - @Test - public void shouldExtractEmptyDocumentFilterForEmptyArray() throws Exception { - Map attributes = - Collections.singletonMap( - "config", "{\"documentFilters\": [] ,\"languageIds\": [\"languageId\"]}"); - - LanguageServerDescription languageServerDescription = - lsConfigurationExtractor.extract(attributes); - - assertTrue(languageServerDescription.getDocumentFilters().isEmpty()); - } - - @Test - public void shouldExtractDocumentFilterWhenDocumentFilterAreNotMentioned() throws Exception { - Map attributes = - Collections.singletonMap("config", "{ \"languageIds\": [\"languageId\"]}"); - - LanguageServerDescription languageServerDescription = - lsConfigurationExtractor.extract(attributes); - - assertTrue(languageServerDescription.getDocumentFilters().isEmpty()); - } - - @Test - public void shouldExtractDocumentFilterLanguageId() throws Exception { - Map attributes = - Collections.singletonMap( - "config", - "{\"documentFilters\": [{\"languageId\":\"testId\"}] ,\"languageIds\": [\"languageId\"]}"); - - LanguageServerDescription languageServerDescription = - lsConfigurationExtractor.extract(attributes); - - assertEquals(languageServerDescription.getDocumentFilters().size(), 1); - assertEquals(languageServerDescription.getDocumentFilters().get(0).getLanguageId(), "testId"); - } - - @Test - public void shouldExtractDocumentFilterScheme() throws Exception { - Map attributes = - Collections.singletonMap( - "config", - "{\"documentFilters\": [{\"scheme\":\"testScheme\"}],\"languageIds\": [\"languageId\"] }"); - - LanguageServerDescription languageServerDescription = - lsConfigurationExtractor.extract(attributes); - - assertEquals(languageServerDescription.getDocumentFilters().size(), 1); - assertEquals(languageServerDescription.getDocumentFilters().get(0).getScheme(), "testScheme"); - } - - @Test - public void shouldExtractDocumentFilterPathRegex() throws Exception { - Map attributes = - Collections.singletonMap( - "config", - "{\"documentFilters\": [{\"pathRegex\":\"testPathRegex\"}] ,\"languageIds\": [\"languageId\"]}"); - - LanguageServerDescription languageServerDescription = - lsConfigurationExtractor.extract(attributes); - - assertEquals(languageServerDescription.getDocumentFilters().size(), 1); - assertEquals( - languageServerDescription.getDocumentFilters().get(0).getPathRegex(), "testPathRegex"); - } -}