Snippet generator mostly working, still needs some tweaks / bug fixes.

This commit is contained in:
Kris De Volder
2017-08-01 12:05:25 -07:00
parent b1fa77603c
commit d9f2047bcf
13 changed files with 374 additions and 49 deletions

View File

@@ -25,13 +25,13 @@ import org.springframework.ide.vscode.bosh.models.ReleasesModel;
import org.springframework.ide.vscode.bosh.models.StemcellData;
import org.springframework.ide.vscode.bosh.models.StemcellModel;
import org.springframework.ide.vscode.bosh.models.StemcellsModel;
import org.springframework.ide.vscode.bosh.snippets.SchemaBasedSnippetGenerator;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.CollectorUtil;
import org.springframework.ide.vscode.commons.util.PartialCollection;
import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.util.Renderables;
import org.springframework.ide.vscode.commons.util.StringUtil;
import org.springframework.ide.vscode.commons.util.ValueParser;
import org.springframework.ide.vscode.commons.util.ValueParsers;
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
import org.springframework.ide.vscode.commons.yaml.ast.YamlAstCache;
@@ -40,7 +40,6 @@ import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache;
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
import org.springframework.ide.vscode.commons.yaml.schema.SchemaContextAware;
import org.springframework.ide.vscode.commons.yaml.schema.YType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.AbstractType;
@@ -53,7 +52,6 @@ import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty;
import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema;
import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraints;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -328,7 +326,6 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
return new StemcellModel(path.dropLast().traverseToNode(ast));
}
private String getCurrentEntityProperty(DynamicSchemaContext dc, String propName) {
YamlPath path = dc.getPath();
YamlFileAST ast = asts.getSafeAst(dc.getDocument(), true);

View File

@@ -14,6 +14,7 @@ import org.springframework.ide.vscode.bosh.models.CloudConfigModel;
import org.springframework.ide.vscode.bosh.models.DynamicModelProvider;
import org.springframework.ide.vscode.bosh.models.ReleasesModel;
import org.springframework.ide.vscode.bosh.models.StemcellsModel;
import org.springframework.ide.vscode.bosh.snippets.SchemaBasedSnippetGenerator;
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfoProvider;
import org.springframework.ide.vscode.commons.languageserver.hover.VscodeHoverEngine;
@@ -23,7 +24,6 @@ import org.springframework.ide.vscode.commons.languageserver.util.Settings;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
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.Log;
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.YamlAstCache;
@@ -41,6 +41,7 @@ import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureProvid
public class BoshLanguageServer extends SimpleLanguageServer {
private final VscodeCompletionEngineAdapter completionEngine;
private BoshDeploymentManifestSchema schema;
public BoshLanguageServer(BoshCliConfig cliConfig,
DynamicModelProvider<CloudConfigModel> cloudConfigProvider,
@@ -52,10 +53,11 @@ public class BoshLanguageServer extends SimpleLanguageServer {
SimpleTextDocumentService documents = getTextDocumentService();
ASTTypeCache astTypeCache = new ASTTypeCache();
BoshDeploymentManifestSchema schema = new BoshDeploymentManifestSchema(asts, astTypeCache, cloudConfigProvider, stemcellsProvider, releasesProvider);
schema = new BoshDeploymentManifestSchema(asts, astTypeCache, cloudConfigProvider, stemcellsProvider, releasesProvider);
YamlStructureProvider structureProvider = YamlStructureProvider.DEFAULT;
YamlAssistContextProvider contextProvider = new SchemaBasedYamlAssistContextProvider(schema);
enableSnippets(true);
YamlCompletionEngine yamlCompletionEngine = new YamlCompletionEngine(structureProvider, contextProvider, YamlCompletionEngineOptions.DEFAULT);
completionEngine = createCompletionEngineAdapter(this, yamlCompletionEngine);
HoverInfoProvider infoProvider = new YamlHoverInfoProvider(asts.getAstProvider(true), structureProvider, contextProvider);
@@ -87,6 +89,14 @@ public class BoshLanguageServer extends SimpleLanguageServer {
}
}
public void enableSnippets(boolean enable) {
if (enable) {
schema.f.setSnippetProvider(new SchemaBasedSnippetGenerator(schema.getTypeUtil(), this::createSnippetBuilder));
} else {
schema.f.setSnippetProvider(null);
}
}
public BoshLanguageServer setMaxCompletions(int maxCompletions) {
completionEngine.setMaxCompletions(maxCompletions);
return this;

View File

@@ -0,0 +1,152 @@
/*******************************************************************************
* Copyright (c) 2017 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.bosh.snippets;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import org.springframework.ide.vscode.commons.languageserver.util.SnippetBuilder;
import org.springframework.ide.vscode.commons.util.Log;
import org.springframework.ide.vscode.commons.yaml.schema.YType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil;
import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty;
import org.springframework.ide.vscode.commons.yaml.snippet.Snippet;
import org.springframework.ide.vscode.commons.yaml.snippet.TypeBasedSnippetProvider;
import org.springframework.ide.vscode.commons.yaml.util.YamlIndentUtil;
import com.google.common.base.Supplier;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
/**
* An implementation of {@link TypeBasedSnippetProvider} that generates snippets
* automatically from schema types.
*/
public class SchemaBasedSnippetGenerator implements TypeBasedSnippetProvider {
private YTypeUtil typeUtil;
private Supplier<SnippetBuilder> snippetBuilderFactory;
public SchemaBasedSnippetGenerator(YTypeUtil typeUtil, Supplier<SnippetBuilder> snippetBuilderFactory) {
super();
this.typeUtil = typeUtil;
this.snippetBuilderFactory = snippetBuilderFactory;
}
private Cache<YType, Collection<Snippet>> cache = CacheBuilder.newBuilder().build();
private int maxNesting = Integer.MAX_VALUE;
@Override
public Collection<Snippet> getSnippets(YType type) {
try {
return cache.get(type, () -> generateSnippets(type));
} catch (ExecutionException e) {
Log.log(e);
return ImmutableList.of();
}
}
private Collection<Snippet> generateSnippets(YType type) {
ImmutableList.Builder<Snippet> snippets = ImmutableList.builder();
//Generate a 'full' snippet that defines all required properties of the current type.
Snippet snippet = generateFullSnippet(type, 0);
if (snippet!=null) {
snippets.add(snippet);
}
//Generate single property snippets that only define a single properties (with 'mega snippets' for nested types)
for (YTypedProperty p : typeUtil.getProperties(type)) {
String propName = p.getName();
SnippetBuilder builder = snippetBuilderFactory.get();
generateBeanSnippet(ImmutableList.of(p), builder, 0, maxNesting);
if (builder.getPlaceholderCount()>=2) {
snippets.add(new Snippet(p.getName()+" Snippet", builder.toString(), (dc) ->
!dc.getDefinedProperties().contains(propName)
));
}
}
return snippets.build();
}
private Snippet generateFullSnippet(YType type, int indent) {
if (typeUtil.isBean(type)) {
SnippetBuilder builder = snippetBuilderFactory.get();
List<YTypedProperty> requiredProps = typeUtil.getProperties(type).stream()
.filter(p -> p.isPrimary() || p.isRequired())
.collect(Collectors.toList());
if (!requiredProps.isEmpty()) {
generateBeanSnippet(requiredProps, builder, indent, maxNesting);
}
if (builder.getPlaceholderCount()>=2) {
//place holder count is a good indicator of snippet complexity and allows us to
// avoid creating trivial snippets (which aren't very useful).
ImmutableSet<YTypedProperty> vetoProps = ImmutableSet.copyOf(requiredProps);
// Do not suggest snippet if it contains properties that are already defined.
return new Snippet(typeUtil.niceTypeName(type)+" Snippet", builder.toString(), (dc) ->
dc.getDefinedProperties().stream().noneMatch(vetoProps::contains)
);
}
}
return null;
}
private void generateBeanSnippet(List<YTypedProperty> props, SnippetBuilder builder, int indent, int nestingLimit) {
if (nestingLimit>0) {
boolean first = true;
for (YTypedProperty p : props) {
if (!first) {
builder.newline(indent);
}
builder.text(p.getName());
builder.text(":");
generateNestedSnippet(false, p.getType(), builder, indent, nestingLimit-1);
first = false;
}
} else {
//reached the limit of bean number of nested property expansions allowed.
builder.placeHolder();
}
}
private void generateNestedSnippet(boolean parentIsSeq, YType type, SnippetBuilder builder, int indent, int nestingLimit) {
if (type==null) {
//Assume its some kind of pojo bean
builder.newline(indent+YamlIndentUtil.INDENT_BY);
builder.placeHolder();
} else if (typeUtil.isBean(type) || typeUtil.isMap(type)) {
if (!parentIsSeq) {
//ready to enter nested keys on next line
indent += YamlIndentUtil.INDENT_BY;
builder.newline(indent);
}
//Insert required keys
List<YTypedProperty> requiredProps = typeUtil.getProperties(type).stream()
.filter(p -> p.isPrimary() || p.isRequired())
.collect(Collectors.toList());
generateBeanSnippet(requiredProps, builder, indent, nestingLimit);
} else if (typeUtil.isSequencable(type)) {
//ready to enter sequence element on next line
builder.newline(indent);
builder.text("- ");
indent += YamlIndentUtil.INDENT_BY;
generateNestedSnippet(true, typeUtil.getDomainType(type), builder, indent, nestingLimit);
} else { //Treat like atomic
//ready to enter whatever on the same line
builder.text(" ");
builder.placeHolder();
}
}
}

View File

@@ -45,7 +45,7 @@ import com.google.common.collect.ImmutableSet;
public class BoshEditorTest {
LanguageServerHarness harness;
LanguageServerHarness<BoshLanguageServer> harness;
private BoshCliConfig cliConfig = new BoshCliConfig();
private MockCloudConfigProvider cloudConfigProvider = new MockCloudConfigProvider(cliConfig);
@@ -53,7 +53,7 @@ public class BoshEditorTest {
private DynamicModelProvider<ReleasesModel> releasesProvider = mock(DynamicModelProvider.class);
@Before public void setup() throws Exception {
harness = new LanguageServerHarness(() -> {
harness = new LanguageServerHarness<BoshLanguageServer>(() -> {
return new BoshLanguageServer(cliConfig, cloudConfigProvider,
(dc) -> stemcellsProvider.getModel(dc),
(dc) -> releasesProvider.getModel(dc)
@@ -156,6 +156,7 @@ public class BoshEditorTest {
}
@Test public void toplevelPropertyCompletions() throws Exception {
harness.getServer().enableSnippets(false);
Editor editor = harness.newEditor(
"<*>"
);
@@ -200,6 +201,7 @@ public class BoshEditorTest {
}
@Test public void stemcellCompletions() throws Exception {
harness.getServer().enableSnippets(false);
Editor editor = harness.newEditor(
"stemcells:\n" +
"- <*>"
@@ -253,6 +255,7 @@ public class BoshEditorTest {
@Test public void releasesBlockCompletions() throws Exception {
harness.getServer().enableSnippets(false);
Editor editor = harness.newEditor(
"releases:\n" +
"- <*>"
@@ -518,6 +521,7 @@ public class BoshEditorTest {
}
@Test public void updateBlockCompletions() throws Exception {
harness.getServer().enableSnippets(false);
Editor editor = harness.newEditor(
"update:\n" +
" <*>"
@@ -557,6 +561,7 @@ public class BoshEditorTest {
}
@Test public void variablesBlockCompletions() throws Exception {
harness.getServer().enableSnippets(false);
Editor editor = harness.newEditor(
"variables:\n" +
"- <*>"

View File

@@ -0,0 +1,36 @@
/*******************************************************************************
* Copyright (c) 2017 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.bosh;
import org.junit.Test;
import org.springframework.ide.vscode.bosh.snippets.SchemaBasedSnippetGenerator;
import org.springframework.ide.vscode.commons.languageserver.util.SnippetBuilder;
import org.springframework.ide.vscode.commons.yaml.ast.YamlAstCache;
import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache;
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
import org.springframework.ide.vscode.commons.yaml.schema.YType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil;
public class SchemaBasedSnippetGeneratorTest {
private ASTTypeCache astTypes = new ASTTypeCache();
private YamlAstCache asts = new YamlAstCache();
private BoshDeploymentManifestSchema schema = new BoshDeploymentManifestSchema(asts, astTypes, (dc) -> null, (dc) -> null, (dc) -> null);
private YTypeUtil typeUtil = schema.getTypeUtil();
private SchemaBasedSnippetGenerator generator = new SchemaBasedSnippetGenerator(typeUtil, SnippetBuilder::new);
@Test
public void toplevelSnippet() throws Exception {
YType v2Schema = typeUtil.inferMoreSpecificType(schema.getTopLevelType(), DynamicSchemaContext.NULL);
System.out.println(generator.getSnippets(v2Schema).iterator().next());
}
}

View File

@@ -35,11 +35,7 @@ public class SnippetBuilder {
* be overridden by subclasses to support other formats.
* <p>
* The default implementation creates place holder strings that
* match the undocumented format vscode currently supports.
* <p>
* Note: this format is explicitly different from what the LSP
* specifies. So it is very likely we should change this implementation
* in the near future.
* match format specified by LSP 3.0.
*/
protected String createPlaceHolder(int id) {
return "$"+id;
@@ -50,4 +46,24 @@ public class SnippetBuilder {
return buf.toString();
}
public void newline(int indent) {
buf.append("\n");
for (int i = 0; i < indent; i++) {
buf.append(' ');
}
}
public void ensureSpace() {
if (buf.length()>0 && !Character.isWhitespace(buf.charAt(buf.length()-1))) {
buf.append(' ');
}
}
/**
* @return The number of placeholder that where inserted in the snippet.
*/
public int getPlaceholderCount() {
return nextPlaceHolderId-1;
}
}

View File

@@ -45,6 +45,8 @@ import org.springframework.ide.vscode.commons.yaml.schema.YType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil;
import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty;
import org.springframework.ide.vscode.commons.yaml.schema.YValueHint;
import org.springframework.ide.vscode.commons.yaml.snippet.Snippet;
import org.springframework.ide.vscode.commons.yaml.snippet.TypeBasedSnippetProvider;
import org.springframework.ide.vscode.commons.yaml.structure.YamlDocument;
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SNode;
import org.springframework.ide.vscode.commons.yaml.util.YamlIndentUtil;
@@ -98,6 +100,29 @@ public class YTypeAssistContext extends AbstractYamlAssistContext {
List<ICompletionProposal> completions = getValueCompletions(doc, node, offset, query);
if (completions.isEmpty()) {
completions = getKeyCompletions(doc, offset, query);
TypeBasedSnippetProvider snippetProvider = typeUtil.getSnippetProvider();
if (snippetProvider!=null) {
Collection<Snippet> snippets = snippetProvider.getSnippets(type);
YamlIndentUtil indenter = new YamlIndentUtil(doc);
for (Snippet snippet : snippets) {
String snippetName = snippet.getName();
double score = FuzzyMatcher.matchScore(query, snippetName);
if (score!=0.0) {
String textBeforeQuery = doc.getLineTextBefore(offset);
DocumentEdits edits = new DocumentEdits(doc.getDocument());
int start = offset - query.length();
edits.delete(start, query);
int referenceIndent = textBeforeQuery.length();
boolean needsSpace = start > 0 && !Character.isWhitespace(doc.getChar(offset-1));
if (needsSpace) {
referenceIndent++;
edits.insert(start, " ");
}
edits.insert(start, indenter.applyIndentation(snippet.getSnippet(), referenceIndent));
completions.add(completionFactory().valueProposal(snippetName, query, snippetName, type, null, score, edits, typeUtil));
}
}
}
}
if (typeUtil.isSequencable(type)) {
completions = new ArrayList<>(completions);

View File

@@ -13,15 +13,15 @@ package org.springframework.ide.vscode.commons.yaml.completion;
public interface YamlCompletionEngineOptions {
/**
* Whether the completion engine includes 'less indented' proposals (i.e. proposals
* that aren't valid at the current CA position, but are valid if we delete
* some spaces in front of the cursor first.
* that aren't valid at the current CA position, but are valid if we delete
* some spaces in front of the cursor first.
*/
default boolean includeDeindentedProposals() {
default boolean includeDeindentedProposals() {
//Disabled by default for now because of bug introduced in VSCode 1.12:
//https://github.com/Microsoft/vscode/issues/26096
return true;
}
YamlCompletionEngineOptions DEFAULT = new YamlCompletionEngineOptions() {};
YamlCompletionEngineOptions TEST_DEFAULT = new YamlCompletionEngineOptions() {
@Override public boolean includeDeindentedProposals() { return true; }

View File

@@ -16,7 +16,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -31,8 +30,6 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.Replaceme
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.CollectorUtil;
import org.springframework.ide.vscode.commons.util.EnumValueParser;
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
import org.springframework.ide.vscode.commons.util.Log;
import org.springframework.ide.vscode.commons.util.PartialCollection;
import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.util.Renderables;
@@ -40,8 +37,8 @@ import org.springframework.ide.vscode.commons.util.ValueParser;
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraint;
import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraints;
import org.springframework.ide.vscode.commons.yaml.snippet.TypeBasedSnippetProvider;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableMap;
@@ -60,6 +57,7 @@ public class YTypeFactory {
private boolean enableTieredOptionalPropertyProposals = true;
private boolean suggestDeprecatedProperties = true;
private TypeBasedSnippetProvider snippetProvider = null;
private static class Deprecation {
final String errorMsg;
@@ -239,6 +237,11 @@ public class YTypeFactory {
return ((AbstractType)type).getCustomContentAssistant();
}
@Override
public TypeBasedSnippetProvider getSnippetProvider() {
return snippetProvider;
}
@Override
public boolean tieredOptionalPropertyProposals() {
return enableTieredOptionalPropertyProposals;
@@ -980,5 +983,10 @@ public class YTypeFactory {
return this;
}
public YTypeFactory setSnippetProvider(TypeBasedSnippetProvider snippetProvider) {
this.snippetProvider = snippetProvider;
return this;
}
}

View File

@@ -10,12 +10,15 @@
*******************************************************************************/
package org.springframework.ide.vscode.commons.yaml.schema;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.springframework.ide.vscode.commons.util.PartialCollection;
import org.springframework.ide.vscode.commons.util.ValueParser;
import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraint;
import org.springframework.ide.vscode.commons.yaml.snippet.Snippet;
import org.springframework.ide.vscode.commons.yaml.snippet.TypeBasedSnippetProvider;
/**
* An implementation of YTypeUtil provides implementations of various
@@ -51,6 +54,14 @@ public interface YTypeUtil {
ISubCompletionEngine getCustomContentAssistant(YType type);
/**
* Config option for type-bases complection enging. Snippets can be
* associated with schema types. These snippets will be suggested as
* additional completions based on the type of value expected in
* a context.
*/
TypeBasedSnippetProvider getSnippetProvider();
/**
* Config option for type-based completion engine. This enables the
* 'tiered' proposals feature (so that optional properties are not

View File

@@ -0,0 +1,40 @@
/*******************************************************************************
* Copyright (c) 2017 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.yaml.snippet;
import java.util.function.Predicate;
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
public class Snippet {
private final String name;
private final String snippet;
private final Predicate<DynamicSchemaContext> applicability;
public Snippet(String name, String snippet, Predicate<DynamicSchemaContext> applicability) {
super();
this.name = name;
this.snippet = snippet;
this.applicability = applicability;
}
public String getName() {
return name;
}
public String getSnippet() {
return snippet;
}
@Override
public String toString() {
return "Snippet [ name="+name+",\n" +snippet +"\n]";
}
}

View File

@@ -0,0 +1,21 @@
/*******************************************************************************
* Copyright (c) 2017 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.yaml.snippet;
import java.util.Collection;
import org.springframework.ide.vscode.commons.yaml.schema.YType;
public interface TypeBasedSnippetProvider {
Collection<Snippet> getSnippets(YType contextType);
}

View File

@@ -92,16 +92,16 @@ import com.google.common.collect.ImmutableList;
import reactor.core.publisher.Mono;
public class LanguageServerHarness {
public class LanguageServerHarness<S extends SimpleLanguageServer> {
//Warning this 'harness' is incomplete. Growing it as needed.
private Random random = new Random();
private Callable<? extends SimpleLanguageServer> factory;
private Callable<S> factory;
private LanguageId defaultLanguageId;
private SimpleLanguageServer server;
private S server;
private InitializeResult initResult;
@@ -110,12 +110,12 @@ public class LanguageServerHarness {
private List<Editor> activeEditors = new ArrayList<>();
public LanguageServerHarness(Callable<? extends SimpleLanguageServer> factory, LanguageId defaultLanguageId) {
public LanguageServerHarness(Callable<S> factory, LanguageId defaultLanguageId) {
this.factory = factory;
this.defaultLanguageId = defaultLanguageId;
}
public LanguageServerHarness(Callable<? extends SimpleLanguageServer> factory) throws Exception {
public LanguageServerHarness(Callable<S> factory) throws Exception {
this(factory, LanguageId.PLAINTEXT);
}
@@ -184,9 +184,9 @@ public class LanguageServerHarness {
workspaceCap.setExecuteCommand(exeCap);
clientCap.setWorkspace(workspaceCap);
initParams.setCapabilities(clientCap);
initResult = server.initialize(initParams).get();
if (server instanceof LanguageClientAware) {
((LanguageClientAware) server).connect(new STS4LanguageClient() {
initResult = getServer().initialize(initParams).get();
if (getServer() instanceof LanguageClientAware) {
((LanguageClientAware) getServer()).connect(new STS4LanguageClient() {
@Override
public void telemetryEvent(Object object) {
// TODO Auto-generated method stub
@@ -248,15 +248,15 @@ public class LanguageServerHarness {
});
}
server.initialized();
getServer().initialized();
return initResult;
}
public TextDocumentInfo openDocument(TextDocumentInfo documentInfo) throws Exception {
DidOpenTextDocumentParams didOpen = new DidOpenTextDocumentParams();
didOpen.setTextDocument(documentInfo.getDocument());
if (server!=null) {
server.getTextDocumentService().didOpen(didOpen);
if (getServer()!=null) {
getServer().getTextDocumentService().didOpen(didOpen);
}
return documentInfo;
}
@@ -295,8 +295,8 @@ public class LanguageServerHarness {
default:
throw new IllegalStateException("Unkown SYNC mode: "+getDocumentSyncMode());
}
if (server!=null) {
server.getTextDocumentService().didChange(didChange);
if (getServer()!=null) {
getServer().getTextDocumentService().didChange(didChange);
}
return documents.get(uri);
}
@@ -320,8 +320,8 @@ public class LanguageServerHarness {
default:
throw new IllegalStateException("Unkown SYNC mode: "+getDocumentSyncMode());
}
if (server!=null) {
server.getTextDocumentService().didChange(didChange);
if (getServer()!=null) {
getServer().getTextDocumentService().didChange(didChange);
}
return documents.get(uri);
}
@@ -339,7 +339,7 @@ public class LanguageServerHarness {
}
public PublishDiagnosticsParams getDiagnostics(TextDocumentInfo doc) throws Exception {
this.server.waitForReconcile();
this.getServer().waitForReconcile();
return diagnostics.get(doc.getUri());
}
@@ -376,8 +376,8 @@ public class LanguageServerHarness {
TextDocumentPositionParams params = new TextDocumentPositionParams();
params.setPosition(cursor);
params.setTextDocument(doc.getId());
server.waitForReconcile();
Either<List<CompletionItem>, CompletionList> completions = server.getTextDocumentService().completion(params).get();
getServer().waitForReconcile();
Either<List<CompletionItem>, CompletionList> completions = getServer().getTextDocumentService().completion(params).get();
if (completions.isLeft()) {
List<CompletionItem> list = completions.getLeft();
return new CompletionList(false, list);
@@ -391,14 +391,14 @@ public class LanguageServerHarness {
TextDocumentPositionParams params = new TextDocumentPositionParams();
params.setPosition(cursor);
params.setTextDocument(document.getId());
return server.getTextDocumentService().hover(params ).get();
return getServer().getTextDocumentService().hover(params ).get();
}
public CompletionItem resolveCompletionItem(CompletionItem maybeUnresolved) {
if (server.hasLazyCompletionResolver()) {
if (getServer().hasLazyCompletionResolver()) {
try {
return server.getTextDocumentService().resolveCompletionItem(maybeUnresolved).get();
return getServer().getTextDocumentService().resolveCompletionItem(maybeUnresolved).get();
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -477,14 +477,14 @@ public class LanguageServerHarness {
}
public List<? extends Location> getDefinitions(TextDocumentPositionParams params) throws Exception {
server.waitForReconcile(); //goto definitions relies on reconciler infos! Must wait or race condition breaking tests occasionally.
return server.getTextDocumentService().definition(params).get();
getServer().waitForReconcile(); //goto definitions relies on reconciler infos! Must wait or race condition breaking tests occasionally.
return getServer().getTextDocumentService().definition(params).get();
}
public List<CodeAction> getCodeActions(TextDocumentInfo doc, Diagnostic problem) throws Exception {
CodeActionContext context = new CodeActionContext(ImmutableList.of(problem));
List<? extends Command> actions =
server.getTextDocumentService().codeAction(new CodeActionParams(doc.getId(), problem.getRange(), context)).get();
getServer().getTextDocumentService().codeAction(new CodeActionParams(doc.getId(), problem.getRange(), context)).get();
return actions.stream()
.map((command) -> new CodeAction(this, command))
.collect(Collectors.toList());
@@ -498,7 +498,7 @@ public class LanguageServerHarness {
//Note convert the params to a 'typeless' Object because that is more representative on how it will be
// received when we get it in a real client/server setting (i.e. parsed from json).
List untypedParams = mapper.convertValue(args, List.class);
server.getWorkspaceService()
getServer().getWorkspaceService()
.executeCommand(new ExecuteCommandParams(command.getCommand(), untypedParams))
.get();
}
@@ -544,9 +544,9 @@ public class LanguageServerHarness {
}
public List<? extends SymbolInformation> getDocumentSymbols(TextDocumentInfo document) throws Exception {
server.waitForReconcile(); //TODO: if the server works properly this shouldn't be needed it should do that internally itself somehow.
getServer().waitForReconcile(); //TODO: if the server works properly this shouldn't be needed it should do that internally itself somehow.
DocumentSymbolParams params = new DocumentSymbolParams(document.getId());
return server.getTextDocumentService().documentSymbol(params).get();
return getServer().getTextDocumentService().documentSymbol(params).get();
}
/**
@@ -554,7 +554,7 @@ public class LanguageServerHarness {
*/
public SynchronizationPoint reconcilerThreadStart() {
CompletableFuture<Void> blocker = new CompletableFuture<>();
server.setTestListener(new LanguageServerTestListener() {
getServer().setTestListener(new LanguageServerTestListener() {
@Override
public void reconcileStarted(String uri, int version) {
try {
@@ -585,4 +585,8 @@ public class LanguageServerHarness {
return newEditor(IOUtil.toString(is));
}
}
public S getServer() {
return server;
}
}