Missing property quickfix now uses snippet generator
This commit is contained in:
@@ -26,7 +26,6 @@ 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;
|
||||
@@ -52,6 +51,7 @@ 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.YamlSchema;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraints;
|
||||
import org.springframework.ide.vscode.commons.yaml.snippet.SchemaBasedSnippetGenerator;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
@@ -15,7 +15,6 @@ 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;
|
||||
@@ -37,6 +36,7 @@ import org.springframework.ide.vscode.commons.yaml.quickfix.YamlQuickfixes;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.TypeBasedYamlSymbolHandler;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaBasedReconcileEngine;
|
||||
import org.springframework.ide.vscode.commons.yaml.snippet.SchemaBasedSnippetGenerator;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureProvider;
|
||||
|
||||
public class BoshLanguageServer extends SimpleLanguageServer {
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.springframework.ide.vscode.bosh.models.StemcellsModel;
|
||||
import org.springframework.ide.vscode.commons.util.ExternalCommand;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.CodeAction;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
|
||||
|
||||
@@ -2475,6 +2476,52 @@ public class BoshEditorTest {
|
||||
);
|
||||
editor.assertContextualCompletions(PLAIN_COMPLETION, "<*>",
|
||||
"cloud_properties:\n <*>");
|
||||
}
|
||||
|
||||
@Test public void missingPropertiesQuickfix() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"name: blah\n" +
|
||||
"stemcells:\n" +
|
||||
"- alias: ubuntu\n" +
|
||||
" os: ubuntu-trusty\n" +
|
||||
" version: 3421.11\n" +
|
||||
"- alias: centos\n" +
|
||||
" os: centos-7\n" +
|
||||
" version: latest"
|
||||
);
|
||||
Diagnostic problem = editor.assertProblems("t|Properties [instance_groups, releases, update] are required").get(0);
|
||||
CodeAction quickfix = editor.assertCodeAction(problem);
|
||||
assertEquals("Add properties: [instance_groups, releases, update]", quickfix.getLabel());
|
||||
quickfix.perform();
|
||||
editor.assertText(
|
||||
"name: blah\n" +
|
||||
"stemcells:\n" +
|
||||
"- alias: ubuntu\n" +
|
||||
" os: ubuntu-trusty\n" +
|
||||
" version: 3421.11\n" +
|
||||
"- alias: centos\n" +
|
||||
" os: centos-7\n" +
|
||||
" version: latest\n" +
|
||||
"releases:\n" +
|
||||
"- name: <*>\n" +
|
||||
" version: \n" +
|
||||
"update:\n" +
|
||||
" canaries: \n" +
|
||||
" max_in_flight: \n" +
|
||||
" canary_watch_time: \n" +
|
||||
" update_watch_time: \n" +
|
||||
"instance_groups:\n" +
|
||||
"- name: \n" +
|
||||
" azs:\n" +
|
||||
" - \n" +
|
||||
" instances: \n" +
|
||||
" jobs:\n" +
|
||||
" - name: \n" +
|
||||
" release: \n" +
|
||||
" vm_type: \n" +
|
||||
" stemcell: \n" +
|
||||
" networks:\n" +
|
||||
" - name: "
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,11 @@ import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.bosh.models.BoshModels;
|
||||
import org.springframework.ide.vscode.bosh.snippets.SchemaBasedSnippetGenerator;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SnippetBuilder;
|
||||
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;
|
||||
import org.springframework.ide.vscode.commons.yaml.snippet.SchemaBasedSnippetGenerator;
|
||||
|
||||
public class SchemaBasedSnippetGeneratorTest {
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*******************************************************************************
|
||||
* 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.languageserver.util;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.CollectionUtil;
|
||||
import org.springframework.ide.vscode.commons.util.text.IRegion;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
/**
|
||||
* Represents a string with placeholder inside. Provides methods to retrieve
|
||||
* the location of a placeholder given its 'id'.
|
||||
*/
|
||||
public class PlaceHolderString {
|
||||
|
||||
public static class PlaceHolder {
|
||||
public final Object id; // object used to identify the placeholder
|
||||
public final IRegion location;
|
||||
|
||||
public PlaceHolder(Object id, IRegion location) {
|
||||
super();
|
||||
this.id = id;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public int getOffset() {
|
||||
return location.getOffset();
|
||||
}
|
||||
|
||||
public int getEnd() {
|
||||
return location.getOffset() + location.getLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((id == null) ? 0 : id.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
PlaceHolder other = (PlaceHolder) obj;
|
||||
if (id == null) {
|
||||
if (other.id != null)
|
||||
return false;
|
||||
} else if (!id.equals(other.id))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final ImmutableMap<Object, PlaceHolder> placeHolders;
|
||||
private final String string;
|
||||
|
||||
public PlaceHolderString(Map<Object, PlaceHolder> placeHolders, String string) {
|
||||
super();
|
||||
this.placeHolders = ImmutableMap.copyOf(placeHolders);
|
||||
this.string = string;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (placeHolders.size()==1) {
|
||||
PlaceHolder placeHolder = CollectionUtil.getAny(placeHolders.values());
|
||||
if (string.length()==placeHolder.getEnd()) {
|
||||
return string.substring(0, placeHolder.getOffset());
|
||||
}
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
public PlaceHolder getPlaceHolder(Object id) {
|
||||
return placeHolders.get(id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,11 +10,31 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.languageserver.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.PlaceHolderString.PlaceHolder;
|
||||
import org.springframework.ide.vscode.commons.util.text.Region;
|
||||
|
||||
public class SnippetBuilder {
|
||||
|
||||
/**
|
||||
* Create a 'gimped' snippetbuilder which generates snippets without
|
||||
* the '$' placeholders. This is useful to provide some snippet-like
|
||||
* support in contexts that don't provide snippet support.
|
||||
*/
|
||||
public static SnippetBuilder gimped() {
|
||||
return new SnippetBuilder() {
|
||||
@Override
|
||||
protected String createPlaceHolder(int id) {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static final int FIRST_PLACE_HOLDER_ID = 1;
|
||||
private int nextPlaceHolderId = FIRST_PLACE_HOLDER_ID;
|
||||
private StringBuilder buf = new StringBuilder();
|
||||
private HashMap<Object, PlaceHolder> placeHolders = new HashMap<>();
|
||||
|
||||
public SnippetBuilder text(String text) {
|
||||
buf.append(text);
|
||||
@@ -25,7 +45,11 @@ public class SnippetBuilder {
|
||||
* Create a new `placeholder` and appends it to the snippet.
|
||||
*/
|
||||
public SnippetBuilder placeHolder() {
|
||||
buf.append(createPlaceHolder(nextPlaceHolderId++));
|
||||
int offset = buf.length();
|
||||
int id = nextPlaceHolderId++;
|
||||
buf.append(createPlaceHolder(id));
|
||||
int end = buf.length();
|
||||
placeHolders.put(id, new PlaceHolderString.PlaceHolder(id, new Region(offset, end-offset)));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -42,6 +66,10 @@ public class SnippetBuilder {
|
||||
return "$"+id;
|
||||
}
|
||||
|
||||
public PlaceHolderString build() {
|
||||
return new PlaceHolderString(placeHolders, buf.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String str = buf.toString();
|
||||
|
||||
@@ -166,7 +166,7 @@ public class YTypeAssistContext extends AbstractYamlAssistContext {
|
||||
DocumentEdits edits;
|
||||
if (snippetProvider!=null) {
|
||||
// Generate edits from snippet
|
||||
Snippet snippet = snippetProvider.getSnippet(type, p);
|
||||
Snippet snippet = snippetProvider.getSnippet(p);
|
||||
edits = createEditFromSnippet(doc, node, offset, query, indenter, snippet);
|
||||
} else {
|
||||
//Generate edits the old-fashioned way
|
||||
|
||||
@@ -112,7 +112,7 @@ public class YamlPathEdits extends DocumentEdits {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private int getNewPathInsertionOffset(SChildBearingNode parent) throws Exception {
|
||||
public int getNewPathInsertionOffset(SChildBearingNode parent) throws Exception {
|
||||
int insertAfterLine = doc.getLineOfOffset(parent.getTreeEnd());
|
||||
while (insertAfterLine>=0 && doc.getLineIndentation(insertAfterLine)==-1) {
|
||||
insertAfterLine--;
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.ide.vscode.commons.yaml.structure.YamlDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SChildBearingNode;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SNode;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureProvider;
|
||||
import org.springframework.ide.vscode.commons.yaml.util.YamlIndentUtil;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -58,11 +59,22 @@ public class YamlQuickfixes {
|
||||
YamlPath path = YamlPath.decode(params.getPath());
|
||||
SNode _target = path.traverse(root);
|
||||
if (_target instanceof SChildBearingNode) {
|
||||
YamlPathEdits edits = new YamlPathEdits(doc);
|
||||
YamlIndentUtil indenter = new YamlIndentUtil(doc);
|
||||
SChildBearingNode target = (SChildBearingNode) _target;
|
||||
for (String prop : params.getProps()) {
|
||||
edits.createPath(target, new YamlPath(YamlPathSegment.valueAt(prop)), " ");
|
||||
edits.freezeCursor();
|
||||
YamlPathEdits edits = new YamlPathEdits(doc);
|
||||
int insertAt = edits.getNewPathInsertionOffset(target);
|
||||
int indentBy = YamlIndentUtil.getNewChildKeyIndent(target);
|
||||
boolean first = true;
|
||||
String propSnippet = params.getSnippet();
|
||||
int cursorOffset = params.getCursorOffset();
|
||||
{
|
||||
edits.insert(insertAt, indenter.newlineWithIndent(indentBy));
|
||||
edits.insert(insertAt, indenter.applyIndentation(propSnippet.substring(0,cursorOffset), indentBy));
|
||||
if (first) {
|
||||
edits.freezeCursor();
|
||||
first = false;
|
||||
}
|
||||
edits.insert(insertAt, indenter.applyIndentation(propSnippet.substring(cursorOffset), indentBy));
|
||||
}
|
||||
TextReplace replaceEdit = edits.asReplacement(_doc);
|
||||
if (replaceEdit!=null) {
|
||||
@@ -94,14 +106,13 @@ public class YamlQuickfixes {
|
||||
ImmutableMap.of(params.getUri(), ImmutableList.of(params.getEdit())),
|
||||
null
|
||||
),
|
||||
null //TODO: compute end of the range after applying the edit
|
||||
null //TODO: compute end of the range after applying the edit
|
||||
);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
//Something went wrong. Return empty edit object.
|
||||
//Something went wrong. Return empty edit object.
|
||||
return NULL_FIX;
|
||||
});
|
||||
}
|
||||
@@ -113,7 +124,7 @@ public class YamlQuickfixes {
|
||||
//There is probably a more efficient way to compute the new cursor position. But its tricky...
|
||||
//... because we need to compute line/char coordinate, in terms of lines in the *new* document.
|
||||
//So we have to take into account how newlines have been inserted or shifted around by the edits.
|
||||
//Doing that without actually applying the edits is... difficult.
|
||||
//Doing that without actually applying the edits is... difficult.
|
||||
TextDocument doc = _doc.copy();
|
||||
edits.apply(doc);
|
||||
return doc.toPosition(newSelection.getOffset());
|
||||
|
||||
@@ -12,20 +12,43 @@ package org.springframework.ide.vscode.commons.yaml.reconcile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class MissingPropertiesData {
|
||||
|
||||
private String uri;
|
||||
|
||||
/**
|
||||
* Yaml path (encoded segments) pointing to the node where
|
||||
* the missing properties should be added (as children of that node).
|
||||
*/
|
||||
private List<String> path;
|
||||
|
||||
/**
|
||||
* The properties that are missing and should be added.
|
||||
*/
|
||||
private List<String> props;
|
||||
|
||||
public MissingPropertiesData(String uri, List<String> path, List<String> props) {
|
||||
/**
|
||||
* Snippet to insert when applying the quickfix.
|
||||
*/
|
||||
private String snippet;
|
||||
|
||||
/**
|
||||
* Offset where to place cursor relative to snippet start.
|
||||
*/
|
||||
private int cursorOffset;
|
||||
|
||||
public MissingPropertiesData() {
|
||||
}
|
||||
|
||||
public MissingPropertiesData(String uri, List<String> path, List<String> props, String snippet, int cursorOffset) {
|
||||
super();
|
||||
this.uri = uri;
|
||||
this.path = path;
|
||||
this.props = props;
|
||||
}
|
||||
|
||||
public MissingPropertiesData() {
|
||||
this.snippet = snippet;
|
||||
this.cursorOffset = cursorOffset;
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
@@ -47,8 +70,24 @@ public class MissingPropertiesData {
|
||||
this.props = props;
|
||||
}
|
||||
|
||||
public String getSnippet() {
|
||||
return snippet;
|
||||
}
|
||||
|
||||
public void setSnippet(String snippet) {
|
||||
this.snippet = snippet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MissingPropertiesData [uri=" + uri + ", path=" + path + ", props=" + props + "]";
|
||||
}
|
||||
|
||||
public int getCursorOffset() {
|
||||
return cursorOffset;
|
||||
}
|
||||
|
||||
public void setCursorOffset(int cursorOffset) {
|
||||
this.cursorOffset = cursorOffset;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.Reconcile
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReplacementQuickfix;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SnippetBuilder;
|
||||
import org.springframework.ide.vscode.commons.util.CollectorUtil;
|
||||
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
|
||||
import org.springframework.ide.vscode.commons.util.IntegerRange;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
@@ -51,6 +53,9 @@ 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.YamlSchema;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraint;
|
||||
import org.springframework.ide.vscode.commons.yaml.snippet.SchemaBasedSnippetGenerator;
|
||||
import org.springframework.ide.vscode.commons.yaml.snippet.Snippet;
|
||||
import org.springframework.ide.vscode.commons.yaml.snippet.TypeBasedSnippetProvider;
|
||||
import org.yaml.snakeyaml.nodes.MappingNode;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
import org.yaml.snakeyaml.nodes.NodeId;
|
||||
@@ -285,21 +290,25 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
|
||||
//Don't check for missing properties if some properties look like they might be spelled incorrectly.
|
||||
if (allPropertiesKnown) {
|
||||
//Check for missing required properties:
|
||||
Set<String> missingProps = beanProperties.values().stream()
|
||||
List<YTypedProperty> missingProps = beanProperties.values().stream()
|
||||
.filter(YTypedProperty::isRequired)
|
||||
.filter(prop -> !foundProps.contains(prop.getName()))
|
||||
.collect(CollectorUtil.toImmutableList());
|
||||
Set<String> missingPropNames = missingProps.stream()
|
||||
.map(YTypedProperty::getName)
|
||||
.filter((required) -> !foundProps.contains(required))
|
||||
.collect(Collectors.toCollection(TreeSet::new));
|
||||
if (!missingProps.isEmpty()) {
|
||||
if (!missingPropNames.isEmpty()) {
|
||||
String message;
|
||||
if (missingProps.size()==1) {
|
||||
if (missingPropNames.size()==1) {
|
||||
// slightly more specific message when only one missing property
|
||||
String missing = missingProps.stream().findFirst().get();
|
||||
String missing = missingPropNames.stream().findFirst().get();
|
||||
message = "Property '"+missing+"' is required for '"+type+"'";
|
||||
} else {
|
||||
message = "Properties "+missingProps+" are required for '"+type+"'";
|
||||
message = "Properties "+missingPropNames+" are required for '"+type+"'";
|
||||
}
|
||||
problems.accept(YamlSchemaProblems.missingProperties(message, dc, missingProps, parent, map, quickfixes.MISSING_PROP_FIX));
|
||||
SchemaBasedSnippetGenerator snippetProvider = new SchemaBasedSnippetGenerator(typeUtil, SnippetBuilder::gimped);
|
||||
Snippet snippet = snippetProvider.getSnippet(missingProps);
|
||||
problems.accept(YamlSchemaProblems.missingProperties(message, dc, missingPropNames, snippet.getSnippet(), snippet.getPlaceHolder(1).getOffset(), parent, map, quickfixes.MISSING_PROP_FIX));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.Reconcile
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.util.CollectionUtil;
|
||||
import org.springframework.ide.vscode.commons.util.CollectorUtil;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
|
||||
@@ -30,6 +31,7 @@ import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
|
||||
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.YTypedProperty;
|
||||
import org.springframework.ide.vscode.commons.yaml.snippet.Snippet;
|
||||
import org.yaml.snakeyaml.error.Mark;
|
||||
import org.yaml.snakeyaml.nodes.MappingNode;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
@@ -138,7 +140,7 @@ public class YamlSchemaProblems {
|
||||
return problem(MISSING_PROPERTY, msg, underline);
|
||||
}
|
||||
|
||||
public static ReconcileProblem missingProperties(String msg, DynamicSchemaContext dc, Set<String> missingProps, Node parent, MappingNode map, QuickfixType quickfixType) {
|
||||
public static ReconcileProblem missingProperties(String msg, DynamicSchemaContext dc, Set<String> missingProps, String snippet, int cursorOffset, Node parent, MappingNode map, QuickfixType quickfixType) {
|
||||
YamlPath contextPath = dc.getPath();
|
||||
List<String> segments = Stream.of(contextPath.getSegments())
|
||||
.map(YamlPathSegment::encode)
|
||||
@@ -152,7 +154,9 @@ public class YamlSchemaProblems {
|
||||
new MissingPropertiesData(
|
||||
dc.getDocument().getUri(),
|
||||
segments,
|
||||
ImmutableList.copyOf(missingProps)
|
||||
ImmutableList.copyOf(missingProps),
|
||||
snippet,
|
||||
cursorOffset
|
||||
),
|
||||
fixTitle
|
||||
);
|
||||
|
||||
@@ -8,26 +8,21 @@
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.bosh.snippets;
|
||||
package org.springframework.ide.vscode.commons.yaml.snippet;
|
||||
|
||||
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.PlaceHolderString;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SnippetBuilder;
|
||||
import org.springframework.ide.vscode.commons.util.CollectorUtil;
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -45,19 +40,11 @@ public class SchemaBasedSnippetGenerator implements TypeBasedSnippetProvider {
|
||||
this.snippetBuilderFactory = snippetBuilderFactory;
|
||||
}
|
||||
|
||||
private Cache<YType, Collection<Snippet>> cache = CacheBuilder.newBuilder()
|
||||
.weakKeys()
|
||||
.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();
|
||||
}
|
||||
return generateSnippets(type);
|
||||
}
|
||||
|
||||
private Collection<Snippet> generateSnippets(YType type) {
|
||||
@@ -76,7 +63,7 @@ public class SchemaBasedSnippetGenerator implements TypeBasedSnippetProvider {
|
||||
generateBeanSnippet(requiredProps, builder, indent, maxNesting);
|
||||
}
|
||||
if (builder.getPlaceholderCount()>=2) {
|
||||
return new Snippet(typeUtil.niceTypeName(type)+" Snippet", builder.toString(), (dc) ->
|
||||
return new Snippet(typeUtil.niceTypeName(type)+" Snippet", builder.build(), (dc) ->
|
||||
requiredProps.stream().noneMatch(p -> dc.getDefinedProperties().contains(p.getName()))
|
||||
);
|
||||
}
|
||||
@@ -103,16 +90,21 @@ public class SchemaBasedSnippetGenerator implements TypeBasedSnippetProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Snippet getSnippet(YType contextType, YTypedProperty p) {
|
||||
//TODO: cache?
|
||||
public Snippet getSnippet(List<YTypedProperty> props) {
|
||||
SnippetBuilder builder = snippetBuilderFactory.get();
|
||||
generateBeanSnippet(ImmutableList.of(p), builder, 0, maxNesting);
|
||||
String propName = p.getName();
|
||||
return new Snippet(propName, builder.toString(), (dc) ->
|
||||
!dc.getDefinedProperties().contains(propName)
|
||||
generateBeanSnippet(props, builder, 0, maxNesting);
|
||||
String snippetName;
|
||||
if (props.size()==1) {
|
||||
snippetName = props.get(0).getName();
|
||||
} else {
|
||||
snippetName = props.stream().map(p -> p.getName()).collect(Collectors.toList()).toString() + " Snippet";
|
||||
}
|
||||
return new Snippet(snippetName, builder.build(), (dc) ->
|
||||
props.stream().allMatch(p -> !dc.getDefinedProperties().contains(p.getName()))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private void generateNestedSnippet(boolean parentIsSeq, YType type, SnippetBuilder builder, int indent, int nestingLimit) {
|
||||
if (type==null) {
|
||||
//Assume its some kind of pojo bean
|
||||
@@ -12,16 +12,18 @@ package org.springframework.ide.vscode.commons.yaml.snippet;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.PlaceHolderString;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.PlaceHolderString.PlaceHolder;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty;
|
||||
|
||||
public class Snippet {
|
||||
|
||||
private final String name;
|
||||
private final String snippet;
|
||||
private final PlaceHolderString snippet;
|
||||
private final Predicate<DynamicSchemaContext> applicability;
|
||||
|
||||
public Snippet(String name, String snippet, Predicate<DynamicSchemaContext> applicability) {
|
||||
public Snippet(String name, PlaceHolderString snippet, Predicate<DynamicSchemaContext> applicability) {
|
||||
super();
|
||||
this.name = name;
|
||||
this.snippet = snippet;
|
||||
@@ -31,7 +33,7 @@ public class Snippet {
|
||||
return name;
|
||||
}
|
||||
public String getSnippet() {
|
||||
return snippet;
|
||||
return snippet.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,4 +46,7 @@ public class Snippet {
|
||||
public boolean isApplicable(DynamicSchemaContext dc) {
|
||||
return applicability==null || applicability.test(dc);
|
||||
}
|
||||
public PlaceHolder getPlaceHolder(Object id) {
|
||||
return snippet.getPlaceHolder(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,19 @@
|
||||
package org.springframework.ide.vscode.commons.yaml.snippet;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YType;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public interface TypeBasedSnippetProvider {
|
||||
|
||||
Collection<Snippet> getSnippets(YType contextType);
|
||||
Snippet getSnippet(YType contextType, YTypedProperty p);
|
||||
default Snippet getSnippet(YTypedProperty p) {
|
||||
return getSnippet(ImmutableList.of(p));
|
||||
}
|
||||
Snippet getSnippet(List<YTypedProperty> props);
|
||||
|
||||
}
|
||||
|
||||
@@ -84,6 +84,12 @@ public class YamlIndentUtil {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public String indentString(int indent) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
addIndent(indent, buf);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a certain level of indentation to all new lines in the given text. Newlines
|
||||
* are expressed by '\n' characters in the text will be replaced by the appropriate
|
||||
|
||||
@@ -100,9 +100,9 @@ public class ConcourseEditorTest {
|
||||
" type: pool\n" +
|
||||
" source:\n" +
|
||||
" username: someone\n" +
|
||||
" branch: <*>\n" +
|
||||
" pool: \n" +
|
||||
" uri: \n"
|
||||
" uri: <*>\n" +
|
||||
" branch: \n" +
|
||||
" pool: \n"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user