Autoinsert single required proerty after '- '.

This commit is contained in:
Kris De Volder
2017-04-26 16:50:56 -07:00
parent 66704bec00
commit d47fa48e43
6 changed files with 143 additions and 37 deletions

View File

@@ -0,0 +1,88 @@
/*******************************************************************************
* 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.completion;
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.util.Streams;
import org.springframework.ide.vscode.commons.yaml.util.YamlIndentUtil;
/**
* Helper for building the auto-insertion text that is appended after a
* property completion, based on the {@link YType} that is expected for the
* property value.
*/
public class AppendTextBuilder {
private YTypeUtil typeUtil;
public AppendTextBuilder(YTypeUtil typeUtil) {
this.typeUtil = typeUtil;
}
public String buildFor(YType type) {
//Note that caller is responsible for proper indentation
//to align with the parent. The strings created here only need to contain
//indentation spaces to indent *more* than the parent node.
//Nevertheless, to support building sophisticated appendText recursively,
//we do need to keep track of the relative indentation level internally
//(or potentially do a lot of string copying)
StringBuilder text = new StringBuilder();
build(type, 0, text);
return text.toString();
}
private void build(YType type, int indent, StringBuilder text) {
if (type==null) {
//Assume its some kind of pojo bean
newline(text, indent+YamlIndentUtil.INDENT_BY);
} else if (typeUtil.isMap(type)) {
//ready to enter nested map key on next line
newline(text, indent+YamlIndentUtil.INDENT_BY);
} else if (typeUtil.isSequencable(type)) {
//ready to enter sequence element on next line
newline(text, indent);
text.append("- ");
singleRequiredProperty(typeUtil.getDomainType(type), indent+2, text);
//Yes using 2 here instead of YamlIndentUtil.INDENT_BY is deliberate. It's the same value (now),
// but the 2 used here is the width of the "- " which should determine nested indent level for things to
// line up properly.
} else if (typeUtil.isAtomic(type)) {
//ready to enter whatever on the same line
text.append(" ");
} else {
newline(text, indent+YamlIndentUtil.INDENT_BY);
}
}
private void singleRequiredProperty(YType type, int indent, StringBuilder text) {
if (type!=null) {
YTypedProperty requireProp = Streams.getSingle(typeUtil.getProperties(type).stream()
.filter(p -> p.isRequired()));
if (requireProp!=null) {
text.append(requireProp.getName());
text.append(':');
build(requireProp.getType(), indent+YamlIndentUtil.INDENT_BY, text);
}
}
}
private void newline(StringBuilder text, int indent) {
text.append("\n");
for (int i = 0; i < indent; i++) {
text.append(' ');
}
}
}

View File

@@ -16,6 +16,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -41,6 +42,7 @@ import org.springframework.ide.vscode.commons.yaml.schema.YValueHint;
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.util.Streams;
import org.springframework.ide.vscode.commons.yaml.util.YamlIndentUtil;
import org.springframework.ide.vscode.commons.yaml.completion.DefaultCompletionFactory.ValueProposal;
@@ -140,30 +142,14 @@ public class YTypeAssistContext extends AbstractYamlAssistContext {
return Collections.emptyList();
}
/**
* Computes the text that should be appended at the end of a completion
* proposal depending on what type of value is expected.
*/
protected String appendTextFor(YType type) {
//Note that proper indentation after each \n" is added automatically
//to align with the parent. The strings created here only need to contain
//indentation spaces to indent *more* than the parent node.
if (type==null) {
//Assume its some kind of pojo bean
return "\n"+YamlIndentUtil.INDENT_STR;
} else if (typeUtil.isMap(type)) {
//ready to enter nested map key on next line
return "\n"+YamlIndentUtil.INDENT_STR;
} if (typeUtil.isSequencable(type)) {
//ready to enter sequence element on next line
return "\n- ";
} else if (typeUtil.isAtomic(type)) {
//ready to enter whatever on the same line
return " ";
} else {
//Assume its some kind of pojo bean
return "\n"+YamlIndentUtil.INDENT_STR;
}
return new AppendTextBuilder(typeUtil).buildFor(type);
}
private List<ICompletionProposal> getValueCompletions(YamlDocument doc, SNode node, int offset, String query) {

View File

@@ -11,10 +11,23 @@
package org.springframework.ide.vscode.commons.yaml.util;
import java.util.ArrayList;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Streams {
/**
* Returns the element in the stream, if the stream has exactly one element.
* Otherwise returns null
*/
public static <T> T getSingle(Stream<T> stream) {
ArrayList<T> els = stream
.limit(2) //Don't need more than 2 to know there is more than 1
.collect(Collectors.toCollection(() -> new ArrayList<>(2)));
return els.size()==1 ? els.get(0) : null;
}
/**
* Like java.util.Stream.of but returns Stream.empty if the element is null
*/

View File

@@ -102,16 +102,13 @@ public class ConcourseModel {
* Get the job with given name. If there is no such job, or if there is more than one, this will return null.
*/
private JobModel getJob(IDocument doc, String jobName) {
List<JobModel> jobs = getFromAst(doc, ast ->
JOBS_PATH.traverseAmbiguously(ast)
.filter(node -> jobName.equals(NodeUtil.getScalarProperty(node, "name")))
.map(JobModel::new)
)
.limit(2) //We only need 2 elements at most to determine if there is more than one
.collect(Collectors.toList());
return jobs.size()==1
? jobs.get(0)
: null;
return Streams.getSingle(
getFromAst(doc, ast ->
JOBS_PATH.traverseAmbiguously(ast)
.filter(node -> jobName.equals(NodeUtil.getScalarProperty(node, "name")))
.map(JobModel::new)
)
);
}
public StepModel newStep(Node _node) {

View File

@@ -452,7 +452,7 @@ public class ConcourseEditorTest {
editor = harness.newEditor(CURSOR);
editor.assertCompletions(
"groups:\n" +
"- <*>"
"- name: <*>"
, // --------------
"jobs:\n" +
"- <*>"
@@ -2552,10 +2552,10 @@ public class ConcourseEditorTest {
" <*>"
,
"inputs:\n" +
"- <*>"
"- name: <*>"
,
"outputs:\n" +
"- <*>"
"- name: <*>"
,
"params:\n" +
" <*>"

View File

@@ -233,6 +233,12 @@ public class ManifestYamlEditorTest {
editor = harness.newEditor("appl<*>");
editor.assertCompletions(
"applications:\n"+
"- name: <*>"
);
editor = harness.newEditor("serv<*>");
editor.assertCompletions(
"services:\n"+
"- <*>"
);
}
@@ -243,7 +249,7 @@ public class ManifestYamlEditorTest {
editor = harness.newEditor("<*>");
editor.assertCompletions(
"applications:\n"+
"- <*>",
"- name: <*>",
// ---------------
"buildpack: <*>",
// ---------------
@@ -285,7 +291,7 @@ public class ManifestYamlEditorTest {
"random-route: <*>",
// ---------------
"routes:\n"+
"- <*>",
"- route: <*>",
// ---------------
"services:\n"+
"- <*>",
@@ -366,7 +372,7 @@ public class ManifestYamlEditorTest {
// ---------------
"applications:\n" +
"- routes:\n"+
" - <*>",
" - route: <*>",
// ---------------
"applications:\n" +
"- services:\n"+
@@ -678,7 +684,7 @@ public class ManifestYamlEditorTest {
// ---------------
"applications:\n" +
"- routes:\n"+
" - <*>",
" - route: <*>",
// ---------------
"applications:\n" +
"- services:\n"+
@@ -770,7 +776,7 @@ public class ManifestYamlEditorTest {
, // ---------------------
"applications:\n" +
"- routes:\n" +
" - <*>\n" +
" - route: <*>\n" +
"- name: test"
,// ---------------------
"applications:\n" +
@@ -1243,6 +1249,22 @@ public class ManifestYamlEditorTest {
);
}
@Test public void autoInsertRouteProperty() throws Exception {
Editor editor;
editor = harness.newEditor(
"applications:\n" +
"- name: foo\n" +
" ro<*>"
);
editor.assertCompletions(c -> c.getLabel().contains("routes"),
"applications:\n" +
"- name: foo\n" +
" routes:\n"+
" - route: <*>"
);
}
//////////////////////////////////////////////////////////////////////////////
private List<CompletionItem> assertCompletions(String textBefore, String... textAfter) throws Exception {