BootLanguageServer created by composing support for props / java together

This commit is contained in:
Kris De Volder
2018-02-15 15:18:17 -08:00
parent c03a5474ce
commit 837aaff897
13 changed files with 228 additions and 18 deletions

View File

@@ -13,6 +13,7 @@ package org.springframework.ide.vscode.boot;
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents;
import org.springframework.ide.vscode.boot.properties.BootPropertiesLanguageServerComponents;
import org.springframework.ide.vscode.commons.languageserver.composable.ComposableLanguageServer;
import org.springframework.ide.vscode.commons.languageserver.composable.CompositeLanguageServerComponents;
import org.springframework.ide.vscode.commons.languageserver.composable.LanguageServerComponents;
import org.springframework.ide.vscode.commons.languageserver.util.LSFactory;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
@@ -23,9 +24,13 @@ public class BootLanguageServer<C extends LanguageServerComponents> extends Comp
super(extensionId, _components);
}
public static ComposableLanguageServer<BootPropertiesLanguageServerComponents> create(LSFactory<BootLanguageServerParams> params) {
//TODO compose properties and java
return new ComposableLanguageServer<>("vscode-boot", s -> new BootPropertiesLanguageServerComponents(s, params));
public static ComposableLanguageServer<CompositeLanguageServerComponents> create(LSFactory<BootLanguageServerParams> params) {
return new ComposableLanguageServer<>("vscode-boot", s -> {
CompositeLanguageServerComponents.Builder components = new CompositeLanguageServerComponents.Builder();
components.add(new BootPropertiesLanguageServerComponents(s, params));
components.add(new BootJavaLanguageServerComponents(s, params));
return components.build(s);
});
}
public static ComposableLanguageServer<BootPropertiesLanguageServerComponents> createProperties(LSFactory<BootLanguageServerParams> params) {

View File

@@ -35,6 +35,7 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguage
import org.springframework.ide.vscode.commons.maven.MavenCore;
import org.springframework.ide.vscode.commons.maven.java.MavenProjectCache;
import org.springframework.ide.vscode.commons.maven.java.MavenProjectFinder;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.text.IDocument;
/**
@@ -66,6 +67,7 @@ public class BootLanguageServerParams {
Duration watchDogInterval
) {
super();
Assert.isNotNull(projectObserver); // null is bad should be ProjectObserver.NULL
this.projectFinder = projectFinder;
this.projectObserver = projectObserver;
this.indexProvider = indexProvider;

View File

@@ -13,6 +13,8 @@ package org.springframework.ide.vscode.boot.java;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@@ -59,6 +61,7 @@ import org.springframework.ide.vscode.commons.languageserver.completion.IComplet
import org.springframework.ide.vscode.commons.languageserver.composable.LanguageServerComponents;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
import org.springframework.ide.vscode.commons.languageserver.util.HoverHandler;
import org.springframework.ide.vscode.commons.languageserver.util.LSFactory;
import org.springframework.ide.vscode.commons.languageserver.util.ReferencesHandler;
@@ -66,9 +69,11 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguage
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServerWrapper;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleWorkspaceService;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
/**
* Language Server for Spring Boot Application Properties files
@@ -77,6 +82,8 @@ import com.google.common.collect.ImmutableList;
*/
public class BootJavaLanguageServerComponents implements LanguageServerComponents {
private static final Set<LanguageId> LANGUAGES = ImmutableSet.of(LanguageId.JAVA);
private final SimpleLanguageServer server;
private final BootLanguageServerParams serverParams;
private final SpringIndexer indexer;
@@ -328,4 +335,10 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
public BootLanguageServerParams getServerParams() {
return this.serverParams;
}
@Override
public Set<LanguageId> getInterestingLanguages() {
return LANGUAGES;
}
}

View File

@@ -10,6 +10,9 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.properties;
import java.util.Optional;
import java.util.Set;
import org.springframework.ide.vscode.boot.BootLanguageServerParams;
import org.springframework.ide.vscode.boot.common.PropertyCompletionFactory;
import org.springframework.ide.vscode.boot.common.RelaxedNameConfig;
@@ -22,7 +25,6 @@ import org.springframework.ide.vscode.boot.properties.reconcile.SpringProperties
import org.springframework.ide.vscode.boot.yaml.completions.ApplicationYamlAssistContext;
import org.springframework.ide.vscode.boot.yaml.reconcile.ApplicationYamlReconcileEngine;
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine;
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter;
import org.springframework.ide.vscode.commons.languageserver.composable.LanguageServerComponents;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfoProvider;
import org.springframework.ide.vscode.commons.languageserver.hover.VscodeHoverEngineAdapter;
@@ -34,6 +36,7 @@ import org.springframework.ide.vscode.commons.languageserver.util.LSFactory;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.util.FuzzyMap;
import org.springframework.ide.vscode.commons.util.text.IDocument;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import org.springframework.ide.vscode.commons.yaml.ast.YamlASTProvider;
import org.springframework.ide.vscode.commons.yaml.ast.YamlParser;
@@ -47,6 +50,7 @@ import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureProvid
import org.yaml.snakeyaml.Yaml;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
/**
* Language Server for Spring Boot Application Properties files
@@ -58,6 +62,11 @@ public class BootPropertiesLanguageServerComponents implements LanguageServerCom
private static final String YML = ".yml";
private static final String PROPERTIES = ".properties";
private static final Set<LanguageId> LANGUAGES = ImmutableSet.of(
LanguageId.BOOT_PROPERTIES,
LanguageId.BOOT_PROPERTIES_YAML
);
private static final YamlCompletionEngineOptions COMPLETION_OPTIONS = new YamlCompletionEngineOptions() {
@Override
@@ -100,6 +109,11 @@ public class BootPropertiesLanguageServerComponents implements LanguageServerCom
}
@Override
public Set<LanguageId> getInterestingLanguages() {
return LANGUAGES;
}
@Override
public ICompletionEngine getCompletionEngine() {
ICompletionEngine propertiesCompletions = new SpringPropertiesCompletionEngine(indexProvider, typeUtilProvider, javaProjectFinder);
@@ -137,11 +151,11 @@ public class BootPropertiesLanguageServerComponents implements LanguageServerCom
}
@Override
public IReconcileEngine getReconcileEngine() {
public Optional<IReconcileEngine> getReconcileEngine() {
IReconcileEngine propertiesReconciler = new SpringPropertiesReconcileEngine(indexProvider, typeUtilProvider);
IReconcileEngine ymlReconciler = new ApplicationYamlReconcileEngine(parser, indexProvider, typeUtilProvider);
return (doc, problemCollector) -> {
return Optional.of((doc, problemCollector) -> {
String uri = doc.getUri();
if (uri!=null) {
if (uri.endsWith(PROPERTIES)) {
@@ -155,10 +169,11 @@ public class BootPropertiesLanguageServerComponents implements LanguageServerCom
//No real reconciler is applicable. So tell the problemCollector there are no problems.
problemCollector.beginCollecting();
problemCollector.endCollecting();
};
});
}
public SpringPropertyIndexProvider getPropertiesIndexProvider() {
return indexProvider;
}
}

View File

@@ -34,6 +34,7 @@ import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFin
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
import org.springframework.ide.vscode.commons.util.text.IDocument;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import org.springframework.ide.vscode.languageserver.testharness.Editor;
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
@@ -77,6 +78,10 @@ public abstract class AbstractPropsEditorTest {
public void setup() throws Exception {
md = new PropertyIndexHarness();
harness = new LanguageServerHarness(this::newLanguageServer) {
@Override
protected LanguageId getDefaultLanguageId() {
return AbstractPropsEditorTest.this.getLanguageId();
}
protected String getFileExtension() {
return AbstractPropsEditorTest.this.getFileExtension();
}
@@ -84,6 +89,8 @@ public abstract class AbstractPropsEditorTest {
harness.intialize(null);
}
protected abstract LanguageId getLanguageId();
/**
* Determines the extension used to create temporary uris for editor contents documents.
* Tests need to control this if the language server behavior they are testing varies depending on the

View File

@@ -35,8 +35,10 @@ import org.springframework.ide.vscode.boot.metadata.PropertiesLoader;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.IType;
import org.springframework.ide.vscode.commons.languageserver.composable.ComposableLanguageServer;
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import org.springframework.ide.vscode.languageserver.testharness.Editor;
import com.google.common.collect.ImmutableList;
@@ -1592,7 +1594,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
ComposableLanguageServer<?> server = BootLanguageServer.create(
s -> new BootLanguageServerParams(
javaProjectFinder,
null,
ProjectObserver.NULL,
md.getIndexProvider(),
typeUtilProvider,
RunningAppProvider.NULL,
@@ -1635,4 +1637,9 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
return ".properties";
}
@Override
protected LanguageId getLanguageId() {
return LanguageId.BOOT_PROPERTIES;
}
}

View File

@@ -32,8 +32,10 @@ import org.springframework.ide.vscode.boot.metadata.PropertyInfo;
import org.springframework.ide.vscode.boot.properties.BootPropertiesLanguageServerComponents;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.composable.ComposableLanguageServer;
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.util.StringUtil;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import org.springframework.ide.vscode.languageserver.testharness.Editor;
/**
@@ -3712,7 +3714,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
ComposableLanguageServer<?> server = BootLanguageServer.create(
s -> new BootLanguageServerParams(
javaProjectFinder,
null,
ProjectObserver.NULL,
md.getIndexProvider(),
typeUtilProvider,
RunningAppProvider.NULL,
@@ -3728,4 +3730,9 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
return ".yml";
}
@Override
protected LanguageId getLanguageId() {
return LanguageId.BOOT_PROPERTIES_YAML;
}
}

View File

@@ -26,6 +26,7 @@ import org.springframework.ide.vscode.boot.metadata.DefaultSpringPropertyIndexPr
import org.springframework.ide.vscode.boot.properties.BootPropertiesLanguageServerComponents;
import org.springframework.ide.vscode.commons.languageserver.ProgressService;
import org.springframework.ide.vscode.commons.languageserver.composable.ComposableLanguageServer;
import org.springframework.ide.vscode.commons.languageserver.composable.CompositeLanguageServerComponents;
import org.springframework.ide.vscode.commons.maven.MavenCore;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -40,7 +41,7 @@ import org.springframework.ide.vscode.project.harness.ProjectsHarness;
*/
public class SpringPropertiesIndexTest {
private LanguageServerHarness<ComposableLanguageServer<BootPropertiesLanguageServerComponents>> harness;
private LanguageServerHarness<ComposableLanguageServer<CompositeLanguageServerComponents>> harness;
private DefaultSpringPropertyIndexProvider propertyIndexProvider;
@@ -52,7 +53,9 @@ public class SpringPropertiesIndexTest {
@Test
public void testPropertiesIndexRefreshOnProjectChange() throws Exception {
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/boot-1.2.0-properties-live-metadta/").toURI()));
propertyIndexProvider = (DefaultSpringPropertyIndexProvider) harness.getServerWrapper().getComponents().getPropertiesIndexProvider();
propertyIndexProvider = (DefaultSpringPropertyIndexProvider) harness.getServerWrapper()
.getComponents().get(BootPropertiesLanguageServerComponents.class)
.getPropertiesIndexProvider();
File directory = new File(ProjectsHarness.class.getResource("/test-projects/boot-1.2.0-properties-live-metadta/").toURI());

View File

@@ -40,13 +40,12 @@ public class ComposableLanguageServer<C extends LanguageServerComponents> implem
SimpleTextDocumentService documents = server.getTextDocumentService();
IReconcileEngine reconcileEngine = components.getReconcileEngine();
if (reconcileEngine!=null) {
components.getReconcileEngine().ifPresent(reconcileEngine -> {
documents.onDidChangeContent(params -> {
TextDocument doc = params.getDocument();
server.validateWith(doc.getId(), reconcileEngine);
});
}
});
ICompletionEngine completionEngine = components.getCompletionEngine();
if (completionEngine!=null) {

View File

@@ -0,0 +1,148 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, 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:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.languageserver.composable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.TextDocumentPositionParams;
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine;
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
import org.springframework.ide.vscode.commons.languageserver.util.HoverHandler;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.text.IDocument;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
public class CompositeLanguageServerComponents implements LanguageServerComponents {
public static class Builder {
private Map<LanguageId, LanguageServerComponents> componentsByLanguageId = new HashMap<>();
public void add(LanguageServerComponents components) {
for (LanguageId language : components.getInterestingLanguages()) {
//Multiple associations to a single language id not yet supported.
Assert.isLegal(!componentsByLanguageId.containsKey(language));
componentsByLanguageId.put(language, components);
}
}
public CompositeLanguageServerComponents build(SimpleLanguageServer server) {
return new CompositeLanguageServerComponents(server, this);
}
}
private final Map<LanguageId, LanguageServerComponents> componentsByLanguageId;
private final ICompletionEngine completionEngine;
private final IReconcileEngine reconcileEngine;
private final HoverHandler hoverHandler;
public CompositeLanguageServerComponents(SimpleLanguageServer server, Builder builder) {
this.componentsByLanguageId = ImmutableMap.copyOf(builder.componentsByLanguageId);
//Create composite Completion engine
this.completionEngine = new ICompletionEngine() {
@Override
public Collection<ICompletionProposal> getCompletions(TextDocument document, int offset) throws Exception {
LanguageId language = document.getLanguageId();
LanguageServerComponents subComponents = componentsByLanguageId.get(language);
if (subComponents!=null) {
ICompletionEngine subEngine = subComponents.getCompletionEngine();
if (subEngine!=null) {
return subEngine.getCompletions(document, offset);
}
}
return ImmutableList.of();
}
};
//Create composite Reconcile engine
if (componentsByLanguageId.values().stream().map(LanguageServerComponents::getReconcileEngine).anyMatch(Optional::isPresent)) {
this.reconcileEngine = new IReconcileEngine() {
@Override
public void reconcile(IDocument document, IProblemCollector problemCollector) {
LanguageId language = document.getLanguageId();
LanguageServerComponents subComponents = componentsByLanguageId.get(language);
if (subComponents!=null) {
Optional<IReconcileEngine> subEngine = subComponents.getReconcileEngine();
if (subEngine.isPresent()) {
subEngine.get().reconcile(document, problemCollector);
return;
}
}
//No applicable subEngine... but we still have to obey the IReconcileEngine contract!
IReconcileEngine.NULL.reconcile(document, problemCollector);
}
};
} else {
this.reconcileEngine = null;
}
//Create composite hover handler
this.hoverHandler = new HoverHandler() {
@Override
public CompletableFuture<Hover> handle(TextDocumentPositionParams params) {
TextDocument doc = server.getTextDocumentService().get(params);
LanguageId language = doc.getLanguageId();
LanguageServerComponents subComponents = componentsByLanguageId.get(language);
if (subComponents!=null) {
HoverHandler subEngine = subComponents.getHoverProvider();
if (subEngine!=null) {
return subEngine.handle(params);
}
}
//No applicable subEngine...
return SimpleTextDocumentService.NO_HOVER;
}
};
}
@Override
public Set<LanguageId> getInterestingLanguages() {
return componentsByLanguageId.keySet();
}
@Override
public ICompletionEngine getCompletionEngine() {
return completionEngine;
}
@Override
public HoverHandler getHoverProvider() {
return hoverHandler;
}
@Override
public Optional<IReconcileEngine> getReconcileEngine() {
return Optional.ofNullable(reconcileEngine);
}
@SuppressWarnings("unchecked")
public <C extends LanguageServerComponents> C get(Class<C> subComponentsType) {
for (LanguageServerComponents subcomp : this.componentsByLanguageId.values()) {
if (subComponentsType.isInstance(subcomp)) {
return (C) subcomp;
}
}
return null;
}
}

View File

@@ -10,15 +10,18 @@
*******************************************************************************/
package org.springframework.ide.vscode.commons.languageserver.composable;
import java.util.Optional;
import java.util.Set;
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
import org.springframework.ide.vscode.commons.languageserver.util.HoverHandler;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
public interface LanguageServerComponents {
default IReconcileEngine getReconcileEngine() {
return null;
}
Set<LanguageId> getInterestingLanguages();
default Optional<IReconcileEngine> getReconcileEngine() { return Optional.empty(); }
ICompletionEngine getCompletionEngine();
HoverHandler getHoverProvider();
}

View File

@@ -27,6 +27,8 @@ public class LanguageId {
public static final LanguageId BOSH_DEPLOYMENT = of("bosh-deployment-manifest");
public static final LanguageId BOSH_CLOUD_CONFIG = of("bosh-cloud-config");
public static final LanguageId BOOT_PROPERTIES = of("spring-boot-properties");
public static final LanguageId BOOT_PROPERTIES_YAML = of("spring-boot-properties-yaml");
private final String id;

View File

@@ -27,7 +27,6 @@ public class Main {
public static void main(String[] args) throws IOException, InterruptedException {
String serverName = "boot-language-server";
LogRedirect.redirectToFile(serverName);
//TODO: wrap both BootProperties and BootJavaLanguageServers into a composite of some kind.
LaunguageServerApp.start(serverName,
() -> BootLanguageServer.create(BootLanguageServerParams.createDefault()).getServer()
);