Platform specific newline for multiline yaml completion

This commit is contained in:
BoykoAlex
2022-01-11 17:29:43 -05:00
parent c41db7c716
commit cb4056d8c2
7 changed files with 60 additions and 41 deletions

View File

@@ -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;

View File

@@ -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(' ');
}

View File

@@ -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())+"- "

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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(
@@ -1450,6 +1453,21 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
// => nothing
);
}
/**
* 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");
@@ -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<*>",
//=>