Relaxed CA with less indentation
This commit is contained in:
@@ -447,6 +447,10 @@ public class DocumentEdits implements ProposalApplier {
|
||||
}
|
||||
}
|
||||
|
||||
public void firstDelete(int start, int end) {
|
||||
edits.add(0, new Deletion(grabCursor, start, end));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find first non-whitepace insertion edit and transform its contents.
|
||||
* @param transformFun receives the insertion text of the target edit and the offset of the first non-whitespace character
|
||||
@@ -478,5 +482,4 @@ public class DocumentEdits implements ProposalApplier {
|
||||
public void freezeCursor() {
|
||||
this.grabCursor = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ import org.springframework.ide.vscode.commons.languageserver.completion.Document
|
||||
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.SortKeys;
|
||||
import org.springframework.ide.vscode.commons.util.Futures;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
@@ -154,6 +153,6 @@ public class VscodeCompletionEngineAdapter implements VscodeCompletionEngine {
|
||||
//TODO: item is pre-resoved so we don't do anything, but we really should somehow defer some work, such as
|
||||
// for example computing docs and edits to resolve time.
|
||||
//The tricky part is that we have to probably remember infos about the unresolved elements somehow so we can resolve later.
|
||||
return Futures.of(unresolved);
|
||||
return CompletableFuture.completedFuture(unresolved);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public abstract class TransformedCompletion extends ScoreableProposal {
|
||||
protected final ICompletionProposal original;
|
||||
|
||||
private DocumentEdits transformedEdit = null;
|
||||
|
||||
|
||||
public TransformedCompletion(ICompletionProposal proposal) {
|
||||
this.original = proposal;
|
||||
}
|
||||
@@ -73,4 +73,9 @@ public abstract class TransformedCompletion extends ScoreableProposal {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilterText() {
|
||||
return original.getFilterText();
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,6 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.yaml.completion;
|
||||
|
||||
import static org.springframework.ide.vscode.commons.languageserver.completion.ScoreableProposal.DEEMP_EXISTS;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -43,7 +41,6 @@ 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.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.YamlIndentUtil;
|
||||
|
||||
@@ -132,17 +129,18 @@ public class YTypeAssistContext extends AbstractYamlAssistContext {
|
||||
query, p, score, edits, typeUtil)
|
||||
);
|
||||
} else {
|
||||
//property already defined
|
||||
// instead of filtering, navigate to the place where its defined.
|
||||
deleteQueryAndLine(doc, query, queryOffset, edits);
|
||||
//Cast to SChildBearingNode cannot fail because otherwise definedProps would be the empty set.
|
||||
edits.createPath((SChildBearingNode) contextNode, relativePath, "");
|
||||
proposals.add(
|
||||
completionFactory().beanProperty(doc.getDocument(),
|
||||
contextPath.toPropString(), getType(),
|
||||
query, p, score, edits, typeUtil)
|
||||
.deemphasize(DEEMP_EXISTS) //deemphasize because it already exists
|
||||
);
|
||||
// This piece below deactivated becuase moving cursor like this doesn't work in vscode
|
||||
// //property already defined
|
||||
// // instead of filtering, navigate to the place where its defined.
|
||||
// deleteQueryAndLine(doc, query, queryOffset, edits);
|
||||
// //Cast to SChildBearingNode cannot fail because otherwise definedProps would be the empty set.
|
||||
// edits.createPath((SChildBearingNode) contextNode, relativePath, "");
|
||||
// proposals.add(
|
||||
// completionFactory().beanProperty(doc.getDocument(),
|
||||
// contextPath.toPropString(), getType(),
|
||||
// query, p, score, edits, typeUtil)
|
||||
// .deemphasize(DEEMP_EXISTS) //deemphasize because it already exists
|
||||
// );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.yaml.completion;
|
||||
|
||||
import static org.springframework.ide.vscode.commons.languageserver.completion.ScoreableProposal.DEEMP_DEDENTED_PROPOSAL;
|
||||
import static org.springframework.ide.vscode.commons.languageserver.completion.ScoreableProposal.DEEMP_INDENTED_PROPOSAL;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -17,6 +18,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -24,6 +26,7 @@ import org.springframework.ide.vscode.commons.languageserver.completion.Document
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ScoreableProposal;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.util.Assert;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.Unicodes;
|
||||
@@ -49,6 +52,8 @@ import com.google.common.collect.ImmutableList;
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class YamlCompletionEngine implements ICompletionEngine {
|
||||
|
||||
Pattern SPACES = Pattern.compile("[ ]+");
|
||||
|
||||
final static Logger logger = LoggerFactory.getLogger(YamlCompletionEngine.class);
|
||||
|
||||
@@ -117,9 +122,9 @@ public class YamlCompletionEngine implements ICompletionEngine {
|
||||
for (ICompletionProposal p : completions) {
|
||||
ICompletionProposal p_fixed = null;
|
||||
if (p.getLabel().startsWith("- ")) {
|
||||
p_fixed = indentFix(p, dashyIndent - baseIndent, contextNode);
|
||||
p_fixed = indentFix(p, dashyIndent - baseIndent, currentNode, contextNode);
|
||||
} else {
|
||||
p_fixed = indentFix(p, plainIndent - baseIndent, contextNode);
|
||||
p_fixed = indentFix(p, plainIndent - baseIndent, currentNode, contextNode);
|
||||
}
|
||||
if (p_fixed!=null) {
|
||||
transformed.add(p_fixed);
|
||||
@@ -130,14 +135,31 @@ public class YamlCompletionEngine implements ICompletionEngine {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
protected ICompletionProposal indentFix(ICompletionProposal p, int fixIndentBy, SNode contextNode) {
|
||||
protected ICompletionProposal indentFix(ICompletionProposal p, int fixIndentBy, SNode currentNode, SNode contextNode) {
|
||||
if (fixIndentBy==0) {
|
||||
return p;
|
||||
} else if (fixIndentBy>0 && isExtraIndentRelaxable(contextNode)) {
|
||||
return indented(p, Strings.repeat(" ", fixIndentBy));
|
||||
} else if (fixIndentBy>0) {
|
||||
if (isExtraIndentRelaxable(contextNode)) {
|
||||
return indented(p, Strings.repeat(" ", fixIndentBy));
|
||||
}
|
||||
} else { // fixIndentBy < 0
|
||||
return null;
|
||||
if (isLesserIndentRelaxable(currentNode, contextNode)) {
|
||||
return dedented(p, -fixIndentBy, contextNode.getDocument());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isLesserIndentRelaxable(final SNode currentNode, final SNode contextNode) {
|
||||
SChildBearingNode parent = currentNode.getParent();
|
||||
while (parent!=null && parent!=contextNode) {
|
||||
SNode lastChild = parent.getLastRealChild();
|
||||
if (lastChild!=null && lastChild.getStart()>=currentNode.getNodeEnd()) {
|
||||
return false;
|
||||
}
|
||||
parent = parent.getParent();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,6 +182,39 @@ public class YamlCompletionEngine implements ICompletionEngine {
|
||||
: contextNode.getIndent() + YamlIndentUtil.INDENT_BY;
|
||||
}
|
||||
|
||||
public ICompletionProposal dedented(ICompletionProposal proposal, int numSpacesToRemove, IDocument doc) {
|
||||
Assert.isLegal(numSpacesToRemove>0);
|
||||
int spacesEnd = proposal.getTextEdit().getFirstEditStart();
|
||||
int spacesStart = spacesEnd-numSpacesToRemove;
|
||||
int numArrows = numSpacesToRemove / YamlIndentUtil.INDENT_BY;
|
||||
String spaces = new DocumentRegion(doc, spacesStart, spacesEnd).toString();
|
||||
if (spaces.length()==numSpacesToRemove && SPACES.matcher(spaces).matches()) {
|
||||
ScoreableProposal transformed = new TransformedCompletion(proposal) {
|
||||
@Override public String tranformLabel(String originalLabel) {
|
||||
return Strings.repeat(Unicodes.LEFT_ARROW+" ", numArrows) + originalLabel;
|
||||
}
|
||||
@Override public DocumentEdits transformEdit(DocumentEdits originalEdit) {
|
||||
originalEdit.firstDelete(spacesStart, spacesEnd);
|
||||
return originalEdit;
|
||||
}
|
||||
@Override
|
||||
public String getFilterText() {
|
||||
//If we don't add the spaces, vscode won't show the completions.
|
||||
// Presumably this is because it matches the filtter text to the text it thinks its going
|
||||
// to replace. Since we are replacing these removed spaces, they must be part of the filtertext
|
||||
return spaces + super.getFilterText();
|
||||
}
|
||||
};
|
||||
transformed.deemphasize(DEEMP_DEDENTED_PROPOSAL*numArrows);
|
||||
return transformed;
|
||||
}
|
||||
// we can't dedent the proposal by the requested amount of space. So err on the safe
|
||||
// side and ignore the proposal. (Otherwise me might end up deleting non-space chars
|
||||
// in our attempt to de-dent.)
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public ICompletionProposal indented(ICompletionProposal proposal, String indentStr) {
|
||||
ScoreableProposal transformed = new TransformedCompletion(proposal) {
|
||||
@Override public String tranformLabel(String originalLabel) {
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.ide.vscode.commons.yaml.util.YamlIndentUtil;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableList.Builder;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.MultimapBuilder;
|
||||
|
||||
@@ -581,6 +582,15 @@ public class YamlStructureParser {
|
||||
return null;
|
||||
}
|
||||
|
||||
public SNode getLastRealChild() {
|
||||
for (SNode c : Lists.reverse(getChildren())) {
|
||||
if (c.getIndent()>=0) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public abstract class SLeafNode extends SNode {
|
||||
|
||||
@@ -46,7 +46,6 @@ import org.eclipse.lsp4j.TextDocumentPositionParams;
|
||||
import org.eclipse.lsp4j.TextEdit;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.junit.Assert;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -346,6 +345,16 @@ public class Editor {
|
||||
}
|
||||
}
|
||||
|
||||
public void assertNoCompletionsWithLabel(Predicate<String> labelPredicate) throws Exception {
|
||||
List<String> found = getCompletions().stream()
|
||||
.map(c -> c.getLabel())
|
||||
.filter(labelPredicate)
|
||||
.collect(Collectors.toList());
|
||||
if (!found.isEmpty()) {
|
||||
fail("Found but not expected: "+found);
|
||||
}
|
||||
}
|
||||
|
||||
public void assertDoesNotContainCompletions(String... notToBeFound) throws Exception {
|
||||
StringBuilder actual = new StringBuilder();
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.commons.util.IOUtil;
|
||||
import org.springframework.ide.vscode.commons.util.Unicodes;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.CodeAction;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
@@ -207,8 +208,6 @@ public class ConcourseEditorTest {
|
||||
editor.assertProblems(
|
||||
"a-resource|does not exist"
|
||||
);
|
||||
|
||||
//TODO: Add more test cases for structural problem?
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -840,7 +839,8 @@ public class ConcourseEditorTest {
|
||||
"- name: every5minutes\n" +
|
||||
" type: time\n" +
|
||||
" source:\n" +
|
||||
" <*>"
|
||||
" <*>\n" +
|
||||
" blah: blah"
|
||||
, // ======================
|
||||
"<*>"
|
||||
, // =>
|
||||
@@ -973,7 +973,8 @@ public class ConcourseEditorTest {
|
||||
"- name: the-repo\n" +
|
||||
" type: git\n" +
|
||||
" source:\n" +
|
||||
" <*>"
|
||||
" <*>\n" +
|
||||
" blah: blah"
|
||||
, //================
|
||||
"<*>"
|
||||
, // ==>
|
||||
@@ -1075,7 +1076,8 @@ public class ConcourseEditorTest {
|
||||
" plan:\n" +
|
||||
" - get: my-git\n" +
|
||||
" params:\n" +
|
||||
" <*>";
|
||||
" <*>\n" +
|
||||
" blah: blah";
|
||||
|
||||
assertContextualCompletions(context,
|
||||
"<*>"
|
||||
@@ -1149,7 +1151,8 @@ public class ConcourseEditorTest {
|
||||
" plan:\n" +
|
||||
" - put: my-git\n" +
|
||||
" params:\n" +
|
||||
" <*>";
|
||||
" <*>\n" +
|
||||
" blah: blah";
|
||||
|
||||
assertContextualCompletions(context,
|
||||
"<*>"
|
||||
@@ -1975,7 +1978,8 @@ public class ConcourseEditorTest {
|
||||
"- name: version\n" +
|
||||
" type: semver\n" +
|
||||
" source:\n" +
|
||||
"<*>";
|
||||
"<*>\n" +
|
||||
" blah: blah";
|
||||
assertContextualCompletions(conText,
|
||||
" driver: git\n" +
|
||||
" <*>"
|
||||
@@ -2003,8 +2007,6 @@ public class ConcourseEditorTest {
|
||||
,
|
||||
" driver: git\n" +
|
||||
" username: <*>"
|
||||
,
|
||||
" driver: git<*>"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2874,9 +2876,9 @@ public class ConcourseEditorTest {
|
||||
editor.assertCompletionLabels(
|
||||
//For the 'exact' context:
|
||||
"check_every",
|
||||
"name",
|
||||
"source",
|
||||
"type",
|
||||
//"name", exists
|
||||
//"source", exists
|
||||
//"type", exists
|
||||
//For the nested context:
|
||||
"→ branch",
|
||||
"→ commit_verification_key_ids",
|
||||
@@ -2891,7 +2893,16 @@ public class ConcourseEditorTest {
|
||||
"→ skip_ssl_verification",
|
||||
"→ tag_filter",
|
||||
"→ uri",
|
||||
"→ username"
|
||||
"→ username",
|
||||
// For the top-level context:
|
||||
"← groups",
|
||||
"← jobs",
|
||||
"← resource_types",
|
||||
// For the 'next job' context:
|
||||
"← - check_every",
|
||||
"← - name",
|
||||
"← - source",
|
||||
"← - type"
|
||||
);
|
||||
|
||||
editor.assertCompletionWithLabel("check_every",
|
||||
@@ -2965,9 +2976,9 @@ public class ConcourseEditorTest {
|
||||
"max_in_flight",
|
||||
"serial",
|
||||
"serial_groups",
|
||||
"name",
|
||||
"plan",
|
||||
"public",
|
||||
//"name", exists
|
||||
//"plan", exists
|
||||
//"public", exists
|
||||
//Completions with '-'
|
||||
"- aggregate",
|
||||
"- do",
|
||||
@@ -2989,7 +3000,18 @@ public class ConcourseEditorTest {
|
||||
"→ privileged",
|
||||
"→ tags",
|
||||
"→ timeout",
|
||||
"→ task"
|
||||
//"→ task" exists
|
||||
"← groups\n" +
|
||||
"← resource_types\n" +
|
||||
"← resources\n" +
|
||||
"← - build_logs_to_retain\n" +
|
||||
"← - disable_manual_trigger\n" +
|
||||
"← - max_in_flight\n" +
|
||||
"← - name\n" +
|
||||
"← - plan\n" +
|
||||
"← - public\n" +
|
||||
"← - serial\n" +
|
||||
"← - serial_groups"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3375,7 +3397,6 @@ public class ConcourseEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore // Doesn't work yet. Enable once implemented.
|
||||
@Test public void relaxedContentAssistLessSpaces() throws Exception {
|
||||
Editor editor;
|
||||
|
||||
@@ -3429,7 +3450,7 @@ public class ConcourseEditorTest {
|
||||
" <*>\n" +
|
||||
" trigger: true\n"
|
||||
);
|
||||
editor.assertCompletions("blah");
|
||||
editor.assertNoCompletionsWithLabel(label -> label.startsWith(Unicodes.LEFT_ARROW+" "));;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Reference in New Issue
Block a user