Platform specific newline for multiline yaml completion
This commit is contained in:
@@ -28,7 +28,7 @@ import javolution.text.Text;
|
||||
public class TextDocument implements IDocument {
|
||||
|
||||
ILineTracker lineTracker = new DefaultLineTracker();
|
||||
private static final Pattern NEWLINE = Pattern.compile("\\r|\\n|\\r\\n|\\n\\r");
|
||||
private static final Pattern NEWLINE = Pattern.compile("\\n|\\r\\n");
|
||||
|
||||
private final LanguageId languageId;
|
||||
private final String uri;
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.springframework.ide.vscode.commons.util.Streams;
|
||||
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.structure.YamlDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.util.YamlIndentUtil;
|
||||
|
||||
/**
|
||||
@@ -29,7 +30,7 @@ public class AppendTextBuilder {
|
||||
this.typeUtil = typeUtil;
|
||||
}
|
||||
|
||||
public String buildFor(YType type) {
|
||||
public String buildFor(YType type, YamlDocument doc) {
|
||||
//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.
|
||||
@@ -39,22 +40,22 @@ public class AppendTextBuilder {
|
||||
//(or potentially do a lot of string copying)
|
||||
|
||||
StringBuilder text = new StringBuilder();
|
||||
build(type, 0, text);
|
||||
build(type, 0, text, doc);
|
||||
return text.toString();
|
||||
}
|
||||
|
||||
private void build(YType type, int indent, StringBuilder text) {
|
||||
private void build(YType type, int indent, StringBuilder text, YamlDocument doc) {
|
||||
if (type==null) {
|
||||
//Assume its some kind of pojo bean
|
||||
newline(text, indent+YamlIndentUtil.INDENT_BY);
|
||||
newline(text, indent+YamlIndentUtil.INDENT_BY, doc);
|
||||
} else if (typeUtil.isMap(type)) {
|
||||
//ready to enter nested map key on next line
|
||||
newline(text, indent+YamlIndentUtil.INDENT_BY);
|
||||
newline(text, indent+YamlIndentUtil.INDENT_BY, doc);
|
||||
} else if (typeUtil.isSequencable(type)) {
|
||||
//ready to enter sequence element on next line
|
||||
newline(text, indent);
|
||||
newline(text, indent, doc);
|
||||
text.append("- ");
|
||||
singleMostImportantProperty(typeUtil.getDomainType(type), indent+2, text);
|
||||
singleMostImportantProperty(typeUtil.getDomainType(type), indent+2, text, doc);
|
||||
//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.
|
||||
@@ -62,11 +63,11 @@ public class AppendTextBuilder {
|
||||
//ready to enter whatever on the same line
|
||||
text.append(" ");
|
||||
} else {
|
||||
newline(text, indent+YamlIndentUtil.INDENT_BY);
|
||||
newline(text, indent+YamlIndentUtil.INDENT_BY, doc);
|
||||
}
|
||||
}
|
||||
|
||||
private void singleMostImportantProperty(YType type, int indent, StringBuilder text) {
|
||||
private void singleMostImportantProperty(YType type, int indent, StringBuilder text, YamlDocument doc) {
|
||||
if (type!=null) {
|
||||
YTypedProperty singleProp = Streams.getSingle(typeUtil.getProperties(type).stream()
|
||||
.filter(p -> p.isPrimary()));
|
||||
@@ -77,13 +78,13 @@ public class AppendTextBuilder {
|
||||
if (singleProp!=null) {
|
||||
text.append(singleProp.getName());
|
||||
text.append(':');
|
||||
build(singleProp.getType(), indent+YamlIndentUtil.INDENT_BY, text);
|
||||
build(singleProp.getType(), indent+YamlIndentUtil.INDENT_BY, text, doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void newline(StringBuilder text, int indent) {
|
||||
text.append("\n");
|
||||
private void newline(StringBuilder text, int indent, YamlDocument doc) {
|
||||
text.append(doc == null || doc.getDocument() == null ? System.lineSeparator() : doc.getDocument().getDefaultLineDelimiter());
|
||||
for (int i = 0; i < indent; i++) {
|
||||
text.append(' ');
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ public class YTypeAssistContext extends AbstractYamlAssistContext {
|
||||
}
|
||||
snippet.append(p.getName());
|
||||
snippet.append(":");
|
||||
snippet.append(appendTextFor(YType));
|
||||
snippet.append(appendTextFor(YType, doc));
|
||||
edits.insert(queryOffset, indenter.applyIndentation(snippet.toString(), referenceIndent));
|
||||
}
|
||||
ICompletionProposal completion = completionFactory().beanProperty(doc.getDocument(),
|
||||
@@ -262,9 +262,10 @@ public class YTypeAssistContext extends AbstractYamlAssistContext {
|
||||
/**
|
||||
* Computes the text that should be appended at the end of a completion
|
||||
* proposal depending on what type of value is expected.
|
||||
* @param doc
|
||||
*/
|
||||
protected String appendTextFor(YType type) {
|
||||
return new AppendTextBuilder(typeUtil).buildFor(type);
|
||||
protected String appendTextFor(YType type, YamlDocument doc) {
|
||||
return new AppendTextBuilder(typeUtil).buildFor(type, doc);
|
||||
}
|
||||
|
||||
private List<ICompletionProposal> getValueCompletions(YamlDocument doc, SNode node, int offset, String query) {
|
||||
@@ -461,7 +462,7 @@ public class YTypeAssistContext extends AbstractYamlAssistContext {
|
||||
@Override
|
||||
protected DocumentEdits transformEdit(DocumentEdits textEdit) {
|
||||
textEdit.transformFirstNonWhitespaceEdit((Integer offset, String insertText) -> {
|
||||
YamlIndentUtil indenter = new YamlIndentUtil("\n");
|
||||
YamlIndentUtil indenter = new YamlIndentUtil(doc);
|
||||
if (needNewline(textEdit)) {
|
||||
return insertText.substring(0, offset)
|
||||
+ "\n" +Strings.repeat(" ", node.getIndent())+"- "
|
||||
|
||||
@@ -114,7 +114,7 @@ public class YamlCompletionEngine implements ICompletionEngine {
|
||||
protected Collection<? extends ICompletionProposal> getRelaxedCompletions(int offset, YamlDocument doc, SNode current, SNode contextNode, int baseIndent, double deempasizeBy) {
|
||||
try {
|
||||
return fixIndentations(getBaseCompletions(offset, doc, current, contextNode),
|
||||
current, contextNode, baseIndent, deempasizeBy);
|
||||
current, contextNode, baseIndent, deempasizeBy, doc);
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
@@ -122,14 +122,14 @@ public class YamlCompletionEngine implements ICompletionEngine {
|
||||
}
|
||||
|
||||
protected Collection<? extends ICompletionProposal> fixIndentations(Collection<ICompletionProposal> completions, SNode currentNode,
|
||||
SNode contextNode, int baseIndent, double deempasizeBy) {
|
||||
SNode contextNode, int baseIndent, double deempasizeBy, YamlDocument doc) {
|
||||
if (!completions.isEmpty()) {
|
||||
int dashyIndent = getTargetIndent(contextNode, currentNode, true);
|
||||
int plainIndent = getTargetIndent(contextNode, currentNode, false);
|
||||
List<ICompletionProposal> transformed = new ArrayList<>();
|
||||
for (ICompletionProposal p : completions) {
|
||||
int targetIndent = p.getLabel().startsWith("- ") ? dashyIndent : plainIndent;
|
||||
ScoreableProposal p_fixed = indentFix((ScoreableProposal)p, targetIndent - baseIndent, currentNode, contextNode);
|
||||
ScoreableProposal p_fixed = indentFix((ScoreableProposal)p, targetIndent - baseIndent, currentNode, contextNode, doc);
|
||||
if (p_fixed!=null) {
|
||||
p_fixed.deemphasize(deempasizeBy);
|
||||
transformed.add(p_fixed);
|
||||
@@ -140,12 +140,12 @@ public class YamlCompletionEngine implements ICompletionEngine {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
protected ScoreableProposal indentFix(ScoreableProposal p, int fixIndentBy, SNode currentNode, SNode contextNode) {
|
||||
protected ScoreableProposal indentFix(ScoreableProposal p, int fixIndentBy, SNode currentNode, SNode contextNode, YamlDocument doc) {
|
||||
if (fixIndentBy==0) {
|
||||
return p;
|
||||
} else if (fixIndentBy>0) {
|
||||
if (isExtraIndentRelaxable(contextNode, fixIndentBy)) {
|
||||
return indented(p, Strings.repeat(" ", fixIndentBy));
|
||||
return indented(p, Strings.repeat(" ", fixIndentBy), doc);
|
||||
}
|
||||
} else { // fixIndentBy < 0
|
||||
if (isLesserIndentRelaxable(currentNode, contextNode)) {
|
||||
@@ -228,7 +228,7 @@ public class YamlCompletionEngine implements ICompletionEngine {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ScoreableProposal indented(ICompletionProposal proposal, String indentStr) {
|
||||
public ScoreableProposal indented(ICompletionProposal proposal, String indentStr, YamlDocument doc) {
|
||||
int numArrows = (indentStr.length()+1)/2;
|
||||
ScoreableProposal transformed = new TransformedCompletion(proposal) {
|
||||
@Override public String tranformLabel(String originalLabel) {
|
||||
@@ -236,7 +236,7 @@ public class YamlCompletionEngine implements ICompletionEngine {
|
||||
}
|
||||
@Override public DocumentEdits transformEdit(DocumentEdits originalEdit) {
|
||||
// originalEdit.indentFirstEdit(indentStr);
|
||||
YamlIndentUtil indenter = new YamlIndentUtil("\n");
|
||||
YamlIndentUtil indenter = new YamlIndentUtil(doc);
|
||||
if (originalEdit.hasRelativeIndents()) {
|
||||
originalEdit.transformFirstNonWhitespaceEdit((Integer offset, String insertText) -> {
|
||||
String prefix = insertText.substring(0, offset);
|
||||
|
||||
@@ -40,7 +40,7 @@ public class YamlIndentUtil {
|
||||
*/
|
||||
public final String NEWLINE;
|
||||
|
||||
public YamlIndentUtil(String newline) {
|
||||
private YamlIndentUtil(String newline) {
|
||||
this.NEWLINE = newline;
|
||||
Assert.isNotNull(NEWLINE);
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@ public class Editor {
|
||||
if (selectionStart<selectionEnd) {
|
||||
text = text.substring(0,selectionStart) + CURSOR + text.substring(selectionStart);
|
||||
}
|
||||
return deWindowsify(text);
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(String content) throws Exception {
|
||||
@@ -270,11 +270,6 @@ public class Editor {
|
||||
return doc.getText(range);
|
||||
}
|
||||
|
||||
private String deWindowsify(String text) {
|
||||
return text.replaceAll("\\r\\n", "\n");
|
||||
}
|
||||
|
||||
|
||||
private boolean matchProblem(Diagnostic problem, String expect) {
|
||||
String[] parts = expect.split("\\|");
|
||||
assertEquals(2, parts.length);
|
||||
|
||||
@@ -506,8 +506,9 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
|
||||
Editor editor;
|
||||
|
||||
editor = newEditor("");
|
||||
editor.assertContextualCompletions("springactcloti",
|
||||
editor = newEditor("<*>\n");
|
||||
editor.assertContextualCompletions(ci -> ci.getLabel().contains("spring.activemq.close-timeout"),
|
||||
"springactcloti<*>",
|
||||
"spring:\n" +
|
||||
" activemq:\n" +
|
||||
" close-timeout: <*>"
|
||||
@@ -1423,7 +1424,9 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
|
||||
@Test public void testContentAssistSimple() throws Exception {
|
||||
defaultTestData();
|
||||
assertCompletion("port<*>",
|
||||
Editor editor = newEditor("<*>\n");
|
||||
editor.assertContextualCompletions(ci -> ci.getLabel().contains("server.port"),
|
||||
"port<*>",
|
||||
"server:\n"+
|
||||
" port: <*>");
|
||||
assertCompletion(
|
||||
@@ -1451,6 +1454,21 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* https://github.com/spring-projects/sts4/issues/709
|
||||
*/
|
||||
@Test public void testGH709() throws Exception {
|
||||
data("server.port", "java.lang.Integer", null, "Server http port");
|
||||
|
||||
Editor editor = newEditor("logging:\r\n"
|
||||
+ " enabled: true\r\n"
|
||||
+ "ser<*>");
|
||||
editor.assertCompletions("logging:\r\n"
|
||||
+ " enabled: true\r\n"
|
||||
+ "server:\r\n"
|
||||
+ " port: <*>");
|
||||
}
|
||||
|
||||
@Test public void testContentAssistNested() throws Exception {
|
||||
data("server.port", "java.lang.Integer", null, "Server http port");
|
||||
data("server.address", "java.lang.String", "localhost", "Server host address");
|
||||
@@ -1881,7 +1899,8 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
defaultTestData();
|
||||
|
||||
//Ensure this test is not trivially passing because of missing test data
|
||||
assertCompletion(
|
||||
Editor editor = newEditor("<*>\n");
|
||||
editor.assertContextualCompletions(ci -> ci.getLabel().contains("server.port"),
|
||||
"po<*>"
|
||||
,
|
||||
"server:\n"+
|
||||
@@ -2237,7 +2256,9 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
useProject(createPredefinedMavenProject("enums-boot-1.3.2-app"));
|
||||
data("foo.color", "demo.Color", null, "A foonky colour");
|
||||
|
||||
assertCompletion("foo.c<*>",
|
||||
Editor editor = newEditor("<*>\n");
|
||||
editor.assertContextualCompletions(ci -> ci.getLabel().equals("foo.color"),
|
||||
"foo.c<*>",
|
||||
"foo:\n" +
|
||||
" color: <*>" //Should complete on same line because enums are 'simple' values.
|
||||
);
|
||||
@@ -2381,11 +2402,12 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
|
||||
//Map Enum -> Pojo:
|
||||
assertCompletions("foo.coldat<*>",
|
||||
Editor editor = newEditor("<*>\n");
|
||||
editor.assertContextualCompletions("foo.coldat<*>",
|
||||
"foo:\n" +
|
||||
" color-data:\n" +
|
||||
" <*>");
|
||||
assertCompletions(
|
||||
editor.assertContextualCompletions(
|
||||
"foo:\n" +
|
||||
" color-data:\n" +
|
||||
" <*>",
|
||||
@@ -2403,7 +2425,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
" red:\n" +
|
||||
" <*>"
|
||||
);
|
||||
assertCompletions(
|
||||
editor.assertContextualCompletions(
|
||||
"foo:\n" +
|
||||
" color-data:\n" +
|
||||
" B<*>",
|
||||
@@ -2414,7 +2436,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
" <*>"
|
||||
);
|
||||
|
||||
assertCompletions(
|
||||
editor.assertContextualCompletions(
|
||||
"foo:\n" +
|
||||
" color-data:\n" +
|
||||
" b<*>",
|
||||
@@ -2425,7 +2447,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
" <*>"
|
||||
);
|
||||
|
||||
assertCompletions(
|
||||
editor.assertContextualCompletions(
|
||||
"foo:\n" +
|
||||
" color-data: b<*>",
|
||||
//=>
|
||||
|
||||
Reference in New Issue
Block a user