Add 'hoverinfo' to completions in manfest.yml editor

This commit is contained in:
Kris De Volder
2016-12-02 15:50:49 -08:00
parent 86b6e6d270
commit e508326b69
19 changed files with 161 additions and 29 deletions

View File

@@ -14,10 +14,18 @@ import org.eclipse.lsp4j.CompletionItemKind;
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
import org.springframework.ide.vscode.commons.languageserver.completion.ScoreableProposal;
import org.springframework.ide.vscode.commons.languageserver.util.IDocument;
import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.yaml.hover.YPropertyHoverInfo;
import org.springframework.ide.vscode.commons.yaml.schema.YType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil;
public abstract class AbstractPropertyProposal extends ScoreableProposal {
@Override
public String getDetail() {
return niceTypeName(getType());
}
protected final IDocument fDoc;
private final DocumentEdits proposalApplier;
private boolean isDeprecated = false;

View File

@@ -23,11 +23,13 @@ import org.springframework.ide.vscode.commons.languageserver.completion.IComplet
import org.springframework.ide.vscode.commons.languageserver.completion.ScoreableProposal;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.util.IDocument;
import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.yaml.hover.YPropertyHoverInfo;
import org.springframework.ide.vscode.commons.yaml.schema.YType;
public class PropertyCompletionFactory {
public ICompletionProposal valueProposal(String value, String query, Type type, double score, DocumentEdits edits, ValueHintHoverInfo info) {
public ICompletionProposal valueProposal(String value, String query, String niceTypeName, double score, DocumentEdits edits, Renderable info) {
return new ScoreableProposal() {
@Override
@@ -49,6 +51,16 @@ public class PropertyCompletionFactory {
public double getBaseScore() {
return score;
}
@Override
public String getDetail() {
return niceTypeName;
}
@Override
public Renderable getDocumentation() {
return info;
}
};
}
@@ -59,17 +71,10 @@ public class PropertyCompletionFactory {
public ScoreableProposal beanProperty(IDocument doc, final String contextProperty, final Type contextType, final String pattern, final TypedProperty property, final double score, DocumentEdits applier, final TypeUtil typeUtil) {
AbstractPropertyProposal proposal = new AbstractPropertyProposal(doc, applier) {
// private HoverInfo hoverInfo;
// @Override
// public HoverInfo getAdditionalProposalInfo(IProgressMonitor monitor) {
// if (hoverInfo==null) {
// String prefix = contextProperty==null?"":contextProperty+".";
// hoverInfo = new JavaTypeNavigationHoverInfo(prefix+property.getName(), property.getName(), contextType, property.getType(), typeUtil);
// }
// return hoverInfo;
// }
@Override
public Renderable getDocumentation() {
return YPropertyHoverInfo.create(contextProperty, contextType, property);
}
@Override
protected String getBaseDisplayString() {
@@ -100,6 +105,7 @@ public class PropertyCompletionFactory {
public String getLabel() {
return getBaseDisplayString() + " : " + typeUtil.niceTypeName(getType());
}
};
if (property.isDeprecated()) {
@@ -166,6 +172,13 @@ public class PropertyCompletionFactory {
public String getLabel() {
return getBaseDisplayString() + " : " + typeUtil.niceTypeName(getType());
}
@Override
public Renderable getDocumentation() {
//TODO: See org.springframework.ide.eclipse.boot.properties.editor.completions.SpringPropertyHoverInfo in old code on how
// this used to render docs.
return null;
}
}

View File

@@ -105,10 +105,7 @@ public class StsValueHint {
public Renderable getDescription() {
return description;
}
public Renderable getDescriptionProvider() {
return description;
}
private static Renderable javaDocSnippet(IJavaElement je) {
return Renderables.lazy(() -> {
IJavadoc jdoc = je.getJavaDoc();

View File

@@ -1,9 +1,22 @@
package org.springframework.ide.vscode.application.properties.metadata.hints;
import java.util.List;
import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.util.Renderables;
import static org.springframework.ide.vscode.commons.util.Renderables.*;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
public class ValueHintHoverInfo {
public ValueHintHoverInfo(StsValueHint hint) {
// TODO Auto-generated constructor stub
public static Renderable create(StsValueHint hint) {
Builder<Renderable> builder = ImmutableList.builder();
builder.add(bold(""+hint.getValue()));
builder.add(paragraph(hint.getDescription()));
return concat(builder.build());
}
}

View File

@@ -610,7 +610,7 @@ public class TypeUtil {
ArrayList<TypedProperty> properties = new ArrayList<>(keyHints.size());
for (StsValueHint hint : keyHints) {
String propName = hint.getValue();
properties.add(new TypedProperty(propName, valueType, hint.getDescriptionProvider(), hint.getDeprecation()));
properties.add(new TypedProperty(propName, valueType, hint.getDescription(), hint.getDeprecation()));
}
return properties;
}

View File

@@ -1,6 +1,7 @@
package org.springframework.ide.vscode.commons.languageserver.completion;
import org.eclipse.lsp4j.CompletionItemKind;
import org.springframework.ide.vscode.commons.util.Renderable;
/**
* Replaces STS/Eclipse's ICompletionProposal
@@ -16,4 +17,7 @@ public interface ICompletionProposal {
CompletionItemKind getKind();
DocumentEdits getTextEdit();
String getDetail();
Renderable getDocumentation();
}

View File

@@ -18,6 +18,7 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocu
import org.springframework.ide.vscode.commons.languageserver.util.SortKeys;
import org.springframework.ide.vscode.commons.languageserver.util.TextDocument;
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 reactor.core.publisher.Mono;
@@ -94,10 +95,19 @@ public class VscodeCompletionEngineAdapter implements VscodeCompletionEngine {
item.setKind(completion.getKind());
item.setSortText(sortkeys.next());
item.setFilterText(completion.getLabel());
item.setDetail(completion.getDetail());
item.setDocumentation(toMarkdown(completion.getDocumentation()));
adaptEdits(item, doc, completion.getTextEdit());
return item;
}
private String toMarkdown(Renderable r) {
if (r!=null) {
return r.toMarkdown();
}
return null;
}
private void adaptEdits(CompletionItem item, TextDocument doc, DocumentEdits edits) throws Exception {
TextReplace replaceEdit = edits.asReplacement(doc);
if (replaceEdit==null) {

View File

@@ -101,6 +101,13 @@ public class Renderables {
@Override
public void renderAsMarkdown(StringBuilder buffer) {
//TODO: This looks wrong. A paragraphs in markdown are created
// by separating the text between them with TWO newlines. So this isn't
// quite rigth as it provides no guarantees that there will be
// two newlines before or after the paragraph's text.
// The correct implementation should probably check wether text in buffer already
// ends with newline(s) and add more only if needed. Then it should
// also append double newline at its end.
buffer.append("\n");
text.renderAsMarkdown(buffer);
buffer.append("\n");
@@ -179,6 +186,10 @@ public class Renderables {
};
}
public static Renderable bold(String text) {
return bold(text(text));
}
public static Renderable bold(Renderable text) {
return new Renderable() {

View File

@@ -12,6 +12,6 @@ public interface CompletionFactory {
CompletionFactory DEFAULT = new DefaultCompletionFactory();
ICompletionProposal beanProperty(IDocument doc, String contextProperty, YType contextType, String query, YTypedProperty p, double score, DocumentEdits edits, YTypeUtil typeUtil);
ICompletionProposal valueProposal(String value, String query, String label, YType type, double score, DocumentEdits edits);
ICompletionProposal valueProposal(String value, String query, String label, YType type, double score, DocumentEdits edits, YTypeUtil typeUtil);
}

View File

@@ -1,10 +1,13 @@
package org.springframework.ide.vscode.commons.yaml.completion;
import org.apache.commons.lang3.reflect.TypeUtils;
import org.eclipse.lsp4j.CompletionItemKind;
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
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.IDocument;
import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.yaml.hover.YPropertyHoverInfo;
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;
@@ -53,6 +56,16 @@ public class DefaultCompletionFactory implements CompletionFactory {
public DocumentEdits getTextEdit() {
return edits;
}
@Override
public String getDetail() {
return typeUtil.niceTypeName(p.getType());
}
@Override
public Renderable getDocumentation() {
return YPropertyHoverInfo.create(contextProperty, contextType, p);
}
}
public class ValueProposal extends ScoreableProposal {
@@ -63,14 +76,16 @@ public class DefaultCompletionFactory implements CompletionFactory {
private YType type;
private double baseScore;
private DocumentEdits edits;
private YTypeUtil typeUtil;
public ValueProposal(String value, String query, String label, YType type, double score, DocumentEdits edits) {
public ValueProposal(String value, String query, String label, YType type, double score, DocumentEdits edits, YTypeUtil typeUtil) {
this.value = value;
this.query = query;
this.label = label;
this.type = type;
this.baseScore = score;
this.edits = edits;
this.typeUtil = typeUtil;
}
@Override
@@ -97,6 +112,16 @@ public class DefaultCompletionFactory implements CompletionFactory {
public String toString() {
return "ValueProposal("+value+")";
}
@Override
public String getDetail() {
return typeUtil.niceTypeName(type);
}
@Override
public Renderable getDocumentation() {
return null;
}
}
@Override
@@ -105,8 +130,8 @@ public class DefaultCompletionFactory implements CompletionFactory {
}
@Override
public ICompletionProposal valueProposal(String value, String query, String label, YType type, double score, DocumentEdits edits) {
return new ValueProposal(value, query, label, type, score, edits);
public ICompletionProposal valueProposal(String value, String query, String label, YType type, double score, DocumentEdits edits, YTypeUtil typeUtil) {
return new ValueProposal(value, query, label, type, score, edits, typeUtil);
}
}

View File

@@ -170,7 +170,7 @@ public class YTypeAssistContext extends AbstractYamlAssistContext {
DocumentEdits edits = new DocumentEdits(doc.getDocument());
edits.delete(offset-query.length(), offset);
edits.insert(offset, value.getValue());
completions.add(completionFactory().valueProposal(value.getValue(), query, value.getLabel(), type, score, edits));
completions.add(completionFactory().valueProposal(value.getValue(), query, value.getLabel(), type, score, edits, typeUtil));
}
}
return completions;

View File

@@ -24,8 +24,10 @@ import org.eclipse.lsp4j.PublishDiagnosticsParams;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextEdit;
import org.junit.Assert;
import org.springframework.ide.vscode.commons.util.Log;
import com.google.common.base.Strings;
import com.google.gson.internal.Streams;
public class Editor {
@@ -341,6 +343,22 @@ public class Editor {
assertContains(expectSnippet, hover.getContents().toString());
}
public void assertCompletionDetails(String expectLabel, String expectDetail, String expectDocSnippet) throws Exception {
CompletionItem it = harness.resolveCompletionItem(assertCompletionWithLabel(expectLabel));
if (expectDetail!=null) {
assertEquals(expectDetail, it.getDetail());
}
assertContains(expectDocSnippet, it.getDocumentation());
}
protected CompletionItem assertCompletionWithLabel(String expectLabel) throws Exception {
return getCompletions().stream()
.filter((item) -> item.getLabel().equals(expectLabel))
.findFirst()
.get();
}
public void setSelection(int start, int end) {
Assert.assertTrue(start>=0);
Assert.assertTrue(end>=start);

View File

@@ -252,7 +252,7 @@ public class LanguageServerHarness {
}
private CompletionItem resolveCompletionItem(CompletionItem unresolved) {
public CompletionItem resolveCompletionItem(CompletionItem unresolved) {
try {
return server.getTextDocumentService().resolveCompletionItem(unresolved).get();
} catch (Exception e) {

View File

@@ -109,9 +109,14 @@ public abstract class AbstractPropsEditorTest {
/**
* Checks that completions contains a completion with a given display string and that that completion
* has a info hover that contains a given snippet of text.
* <p>
* Deprecated: use assertCompletionDetails instead and also check add an expected 'detail' text of
* completion item.
*/
@Deprecated
public void assertCompletionWithInfoHover(String editorText, String expectLabel, String expectInfoSnippet) throws Exception {
notImplemented();
Editor editor = newEditor(editorText);
editor.assertCompletionDetails(expectLabel, null, expectInfoSnippet);
}
public boolean isEmptyMetadata() {

View File

@@ -271,8 +271,9 @@ public class PropertiesCompletionProposalsCalculator {
DocumentEdits edits = new DocumentEdits(doc);
edits.delete(startOfValue, offset);
edits.insert(offset, valueCandidate);
proposals.add(completionFactory.valueProposal(valueCandidate, query, getValueType(index, typeUtil, propertyName),
score, edits, new ValueHintHoverInfo(hint))
String valueTypeName = typeUtil.niceTypeName(getValueType(index, typeUtil, propertyName));
proposals.add(completionFactory.valueProposal(valueCandidate, query, valueTypeName,
score, edits, ValueHintHoverInfo.create(hint))
// new ValueProposal(startOfValue, valuePrefix,
// valueCandidate, i)
);

View File

@@ -14,6 +14,7 @@ import org.eclipse.lsp4j.CompletionItemKind;
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
import org.springframework.ide.vscode.commons.languageserver.quickfix.ProblemFixer;
import org.springframework.ide.vscode.commons.util.Renderable;
public class ReplaceDeprecatedPropertyQuickfix implements ICompletionProposal {
@@ -45,6 +46,14 @@ public class ReplaceDeprecatedPropertyQuickfix implements ICompletionProposal {
public DocumentEdits getTextEdit() {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public String getDetail() {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public Renderable getDocumentation() {
throw new UnsupportedOperationException("Not yet implemented");
}
// private final QuickfixContext context;
// private final SpringPropertyProblem problem;

View File

@@ -272,7 +272,7 @@ public abstract class ApplicationYamlAssistContext extends AbstractYamlAssistCon
edits.insert(offset, " ");
}
edits.insert(offset, YamlUtil.stringEscape(value));
completions.add(completionFactory.valueProposal(value, query, type, score, edits, new ValueHintHoverInfo(hint)));
completions.add(completionFactory.valueProposal(value, query, typeUtil.niceTypeName(type), score, edits, ValueHintHoverInfo.create(hint)));
}
}
return completions;

View File

@@ -14,6 +14,7 @@ import org.eclipse.lsp4j.CompletionItemKind;
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
import org.springframework.ide.vscode.commons.languageserver.quickfix.ProblemFixer;
import org.springframework.ide.vscode.commons.util.Renderable;
public class ReplaceDeprecatedYamlQuickfix implements ICompletionProposal {
@@ -45,6 +46,14 @@ public class ReplaceDeprecatedYamlQuickfix implements ICompletionProposal {
public DocumentEdits getTextEdit() {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public String getDetail() {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public Renderable getDocumentation() {
throw new UnsupportedOperationException("Not yet implemented");
}
// private final QuickfixContext context;
// private final SpringPropertyProblem problem;

View File

@@ -343,6 +343,15 @@ public class ManifestYamlEditorTest {
);
}
@Test
public void completionDetailsAndDocs() throws Exception {
Editor editor = harness.newEditor(
"applications:\n" +
"- build<*>"
);
editor.assertCompletionDetails("buildpack", "Buildpack", "If your application requires a custom buildpack");
}
@Test
public void valueCompletions() throws Exception {
assertCompletions("disk_quota: <*>",