From 2dda8924d19bde3cb21bc2a8db8e0c1d472fae57 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Mon, 31 Oct 2016 11:57:16 -0700 Subject: [PATCH 1/3] Move VscodeCompletionEngineAdapter to commons --- .../yaml/completion/ScoreableProposal.java | 107 ++++++++++++++++++ .../ide/vscode/manifest/yaml/SortKeys.java | 0 .../manifest/yaml/VscodeCompletionEngine.java | 0 .../yaml/VscodeCompletionEngineAdapter.java | 10 +- .../ide/vscode/commons/util/StringUtil.java | 44 +++++++ .../completion/DefaultCompletionFactory.java | 106 ----------------- .../yaml/structure/YamlStructureParser.java | 38 +------ .../yaml/ApplicationYamlEditorTest.java | 2 +- 8 files changed, 158 insertions(+), 149 deletions(-) create mode 100644 vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/ScoreableProposal.java rename vscode-extensions/{vscode-manifest-yaml => commons/commons-language-server}/src/main/java/org/springframework/ide/vscode/manifest/yaml/SortKeys.java (100%) rename vscode-extensions/{vscode-manifest-yaml => commons/commons-language-server}/src/main/java/org/springframework/ide/vscode/manifest/yaml/VscodeCompletionEngine.java (100%) rename vscode-extensions/{vscode-manifest-yaml => commons/commons-language-server}/src/main/java/org/springframework/ide/vscode/manifest/yaml/VscodeCompletionEngineAdapter.java (93%) diff --git a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/ScoreableProposal.java b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/ScoreableProposal.java new file mode 100644 index 000000000..e9cf3d364 --- /dev/null +++ b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/ScoreableProposal.java @@ -0,0 +1,107 @@ +package org.springframework.ide.vscode.commons.yaml.completion; + +import java.util.Comparator; + +import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal; + +public abstract class ScoreableProposal implements ICompletionProposal { + private static final double DEEMP_VALUE = 100000; // should be large enough to move deemphasized stuff to bottom of list. + + private double deemphasizedBy = 0.0; + + /** + * A sorter suitable for sorting ScoreableProposals based on their score. + */ + public static final Comparator COMPARATOR = new Comparator() { + public int compare(ICompletionProposal p1, ICompletionProposal p2) { + if (p1 instanceof ScoreableProposal && p2 instanceof ScoreableProposal) { + double s1 = ((ScoreableProposal)p1).getScore(); + double s2 = ((ScoreableProposal)p2).getScore(); + if (s1==s2) { + String name1 = ((ScoreableProposal)p1).getLabel(); + String name2 = ((ScoreableProposal)p2).getLabel(); + return name1.compareTo(name2); + } else { + return Double.compare(s2, s1); + } + } + return 0; + } + }; + public abstract double getBaseScore(); + public final double getScore() { + return getBaseScore() - deemphasizedBy; + } + public ScoreableProposal deemphasize() { + deemphasizedBy+= DEEMP_VALUE; + return this; + } + public boolean isDeemphasized() { + return deemphasizedBy > 0; + } + +// @Override +// public boolean isAutoInsertable() { +// return !isDeemphasized(); +// } + +// public StyledString getStyledDisplayString() { +// StyledString result = new StyledString(); +// highlightPattern(getHighlightPattern(), getBaseDisplayString(), result); +// return result; +// } + +// private void highlightPattern(String pattern, String data, StyledString result) { +// Styler highlightStyle = CompletionFactory.HIGHLIGHT; +// Styler plainStyle = isDeemphasized()?CompletionFactory.DEEMPHASIZE:CompletionFactory.NULL_STYLER; +// if (isDeprecated()) { +// highlightStyle = CompletionFactory.compose(highlightStyle, CompletionFactory.DEPRECATE); +// plainStyle = CompletionFactory.compose(plainStyle, CompletionFactory.DEPRECATE); +// } +// if (StringUtils.hasText(pattern)) { +// int dataPos = 0; int dataLen = data.length(); +// int patternPos = 0; int patternLen = pattern.length(); +// +// while (dataPos completions = new ArrayList<>(engine.getCompletions(doc, offset)); - Collections.sort(completions, DefaultCompletionFactory.COMPARATOR); + Collections.sort(completions, ScoreableProposal.COMPARATOR); CompletionListImpl list = new CompletionListImpl(); list.setIncomplete(false); List items = new ArrayList<>(completions.size()); @@ -105,7 +105,7 @@ public class VscodeCompletionEngineAdapter implements VscodeCompletionEngine { //Vscode applies some magic indent to a multi-line edit text. We do everything ourself so we have adjust for the magic // and do some kind of 'inverse magic' here. int vscodeMagicIndent = start.getCharacter(); - return YamlStructureParser.stripIndentation(vscodeMagicIndent, newText); + return StringUtil.stripIndentation(vscodeMagicIndent, newText); } @Override diff --git a/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/StringUtil.java b/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/StringUtil.java index 7583d37a1..ff14493f8 100644 --- a/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/StringUtil.java +++ b/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/StringUtil.java @@ -131,4 +131,48 @@ public class StringUtil { return f.format(d); } + private static final Pattern NEWLINE = Pattern.compile("(\\n|\\r)+"); + + /** + * Removes a given number of spaces from all lines of text in a String, + * except for the first line. + *

+ * Note: this method only deals with spaces its not suitable for strings + * which use tabs for indentation. + */ + public static String stripIndentation(int indent, String indentedText) { + StringBuilder out = new StringBuilder(); + boolean first = true; + Matcher matcher = NEWLINE.matcher(indentedText); + int pos = 0; + while (matcher.find()) { + int newline = matcher.start(); + int newline_end = matcher.end(); + String line = indentedText.substring(pos, newline); + if (first) { + first = false; + } else { + line = stripIndentationFromLine(indent, line); + } + out.append(line); + out.append(indentedText.substring(newline, newline_end)); + pos = newline_end; + } + String line = indentedText.substring(pos); + if (!first) { + line = stripIndentationFromLine(indent, line); + } + out.append(line); + return out.toString(); + } + + public static String stripIndentationFromLine(int indent, String line) { + int start = 0; + while (start COMPARATOR = new Comparator() { - public int compare(ICompletionProposal p1, ICompletionProposal p2) { - if (p1 instanceof ScoreableProposal && p2 instanceof ScoreableProposal) { - double s1 = ((ScoreableProposal)p1).getScore(); - double s2 = ((ScoreableProposal)p2).getScore(); - if (s1==s2) { - String name1 = ((ScoreableProposal)p1).getLabel(); - String name2 = ((ScoreableProposal)p2).getLabel(); - return name1.compareTo(name2); - } else { - return Double.compare(s2, s1); - } - } - return 0; - } - }; - - - public static abstract class ScoreableProposal implements ICompletionProposal { - private static final double DEEMP_VALUE = 100000; // should be large enough to move deemphasized stuff to bottom of list. - - private double deemphasizedBy = 0.0; - public abstract double getBaseScore(); - public final double getScore() { - return getBaseScore() - deemphasizedBy; - } - public ScoreableProposal deemphasize() { - deemphasizedBy+= DEEMP_VALUE; - return this; - } - public boolean isDeemphasized() { - return deemphasizedBy > 0; - } - -// @Override -// public boolean isAutoInsertable() { -// return !isDeemphasized(); -// } - -// public StyledString getStyledDisplayString() { -// StyledString result = new StyledString(); -// highlightPattern(getHighlightPattern(), getBaseDisplayString(), result); -// return result; -// } - -// private void highlightPattern(String pattern, String data, StyledString result) { -// Styler highlightStyle = CompletionFactory.HIGHLIGHT; -// Styler plainStyle = isDeemphasized()?CompletionFactory.DEEMPHASIZE:CompletionFactory.NULL_STYLER; -// if (isDeprecated()) { -// highlightStyle = CompletionFactory.compose(highlightStyle, CompletionFactory.DEPRECATE); -// plainStyle = CompletionFactory.compose(plainStyle, CompletionFactory.DEPRECATE); -// } -// if (StringUtils.hasText(pattern)) { -// int dataPos = 0; int dataLen = data.length(); -// int patternPos = 0; int patternLen = pattern.length(); -// -// while (dataPos0) { - return stripIndentation(indent, indentedText); + return StringUtil.stripIndentation(indent, indentedText); } return indentedText; } @@ -702,39 +701,4 @@ public class YamlStructureParser { return keyAliases.getKeyAliases(key); } - public static String stripIndentation(int indent, String indentedText) { - StringBuilder out = new StringBuilder(); - Pattern NEWLINE = Pattern.compile("(\\n|\\r)+"); - boolean first = true; - Matcher matcher = NEWLINE.matcher(indentedText); - int pos = 0; - while (matcher.find()) { - int newline = matcher.start(); - int newline_end = matcher.end(); - String line = indentedText.substring(pos, newline); - if (first) { - first = false; - } else { - line = stripIndentationFromLine(indent, line); - } - out.append(line); - out.append(indentedText.substring(newline, newline_end)); - pos = newline_end; - } - String line = indentedText.substring(pos); - if (!first) { - line = stripIndentationFromLine(indent, line); - } - out.append(line); - return out.toString(); - } - - private static String stripIndentationFromLine(int indent, String line) { - int start = 0; - while (start", "server:\n"+ From e42737bd38b9b891ef81381876d945b92c0a8457 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Mon, 31 Oct 2016 14:28:02 -0700 Subject: [PATCH 2/3] Porting ApplicationYamlCompletionEngine from old STS --- .../properties/metadata/PropertyInfo.java | 57 +- .../metadata/ValueProviderRegistry.java | 2 +- .../completions/AbstractPropertyProposal.java | 109 ++++ .../PropertyCompletionFactory.java | 163 ++++++ .../completions/RelaxedNameConfig.java | 42 ++ .../metadata/hints/BasicHintProvider.java | 85 +++ .../metadata/hints/HintProvider.java | 24 + .../metadata/hints/HintProviders.java | 222 ++++++++ .../{types => hints}/StsValueHint.java | 3 +- .../metadata/hints/ValueHintHoverInfo.java | 9 + .../properties/metadata/types/TypeUtil.java | 1 + .../completion/ScoreableProposal.java | 4 +- .../completion}/VscodeCompletionEngine.java | 2 +- .../VscodeCompletionEngineAdapter.java | 7 +- .../languageserver/hover/HoverInfo.java | 8 + .../languageserver/util}/SortKeys.java | 2 +- .../completion/DefaultCompletionFactory.java | 1 + .../ApplicationYamlAssistContext.java | 509 ++++++++++++++++++ .../ApplicationYamlCompletionEngine.java | 49 ++ .../yaml/completions/LazyProposalApplier.java | 21 + .../ApplicationYamlProblems.java | 2 +- .../ApplicationYamlReconcileEngine.java | 3 +- .../yaml/ManifestYamlLanguageServer.java | 2 + 23 files changed, 1286 insertions(+), 41 deletions(-) create mode 100644 vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/completions/AbstractPropertyProposal.java create mode 100644 vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/completions/PropertyCompletionFactory.java create mode 100644 vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/completions/RelaxedNameConfig.java create mode 100644 vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/BasicHintProvider.java create mode 100644 vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/HintProvider.java create mode 100644 vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/HintProviders.java rename vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/{types => hints}/StsValueHint.java (97%) create mode 100644 vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/ValueHintHoverInfo.java rename vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/{yaml => languageserver}/completion/ScoreableProposal.java (95%) rename vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/{manifest/yaml => commons/languageserver/completion}/VscodeCompletionEngine.java (88%) rename vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/{manifest/yaml => commons/languageserver/completion}/VscodeCompletionEngineAdapter.java (92%) create mode 100644 vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/hover/HoverInfo.java rename vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/{manifest/yaml => commons/languageserver/util}/SortKeys.java (89%) create mode 100644 vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/completions/ApplicationYamlAssistContext.java create mode 100644 vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/completions/ApplicationYamlCompletionEngine.java create mode 100644 vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/completions/LazyProposalApplier.java rename vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/{ => reconcile}/ApplicationYamlProblems.java (94%) diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/PropertyInfo.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/PropertyInfo.java index 53a3a1ded..7870b7ab9 100644 --- a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/PropertyInfo.java +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/PropertyInfo.java @@ -20,6 +20,11 @@ import org.springframework.boot.configurationmetadata.Deprecation; import org.springframework.boot.configurationmetadata.ValueHint; import org.springframework.boot.configurationmetadata.ValueProvider; import org.springframework.ide.vscode.application.properties.metadata.ValueProviderRegistry.ValueProviderStrategy; +import org.springframework.ide.vscode.application.properties.metadata.hints.HintProvider; +import org.springframework.ide.vscode.application.properties.metadata.hints.HintProviders; +import org.springframework.ide.vscode.application.properties.metadata.types.Type; +import org.springframework.ide.vscode.application.properties.metadata.types.TypeParser; +import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtil; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; @@ -134,32 +139,32 @@ public class PropertyInfo { return description; } -// public HintProvider getHints(TypeUtil typeUtil, boolean dimensionAware) { -// Type type = TypeParser.parse(this.type); -// if (TypeUtil.isMap(type)) { -// return HintProviders.forMap(keyHints(typeUtil), valueHints(typeUtil), TypeUtil.getDomainType(type), dimensionAware); -// } else if (TypeUtil.isSequencable(type)) { -// if (dimensionAware) { -// if (TypeUtil.isSequencable(type)) { -// return HintProviders.forDomainAt(valueHints(typeUtil), TypeUtil.getDimensionality(type)); -// } else { -// return HintProviders.forHere(valueHints(typeUtil)); -// } -// } else { -// return HintProviders.forAllValueContexts(valueHints(typeUtil)); -// } -// } else { -// return HintProviders.forHere(valueHints(typeUtil)); -// } -// } -// -// private HintProvider keyHints(TypeUtil typeUtil) { -// return HintProviders.basic(typeUtil.getJavaProject(), keyHints, keyProvider); -// } -// -// private HintProvider valueHints(TypeUtil typeUtil) { -// return HintProviders.basic(typeUtil.getJavaProject(), valueHints, valueProvider); -// } + public HintProvider getHints(TypeUtil typeUtil, boolean dimensionAware) { + Type type = TypeParser.parse(this.type); + if (TypeUtil.isMap(type)) { + return HintProviders.forMap(keyHints(typeUtil), valueHints(typeUtil), TypeUtil.getDomainType(type), dimensionAware); + } else if (TypeUtil.isSequencable(type)) { + if (dimensionAware) { + if (TypeUtil.isSequencable(type)) { + return HintProviders.forDomainAt(valueHints(typeUtil), TypeUtil.getDimensionality(type)); + } else { + return HintProviders.forHere(valueHints(typeUtil)); + } + } else { + return HintProviders.forAllValueContexts(valueHints(typeUtil)); + } + } else { + return HintProviders.forHere(valueHints(typeUtil)); + } + } + + private HintProvider keyHints(TypeUtil typeUtil) { + return HintProviders.basic(typeUtil.getJavaProject(), keyHints, keyProvider); + } + + private HintProvider valueHints(TypeUtil typeUtil) { + return HintProviders.basic(typeUtil.getJavaProject(), valueHints, valueProvider); + } public List getSources() { if (sources!=null) { diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/ValueProviderRegistry.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/ValueProviderRegistry.java index 7c24b6324..49c404567 100644 --- a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/ValueProviderRegistry.java +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/ValueProviderRegistry.java @@ -17,7 +17,7 @@ import java.util.Map; import java.util.function.Function; import org.springframework.boot.configurationmetadata.ValueProvider; -import org.springframework.ide.vscode.application.properties.metadata.types.StsValueHint; +import org.springframework.ide.vscode.application.properties.metadata.hints.StsValueHint; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.util.CollectionUtil; diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/completions/AbstractPropertyProposal.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/completions/AbstractPropertyProposal.java new file mode 100644 index 000000000..c7f4675fb --- /dev/null +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/completions/AbstractPropertyProposal.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 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.application.properties.metadata.completions; + +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.yaml.schema.YType; + +import io.typefox.lsapi.CompletionItemKind; + +public abstract class AbstractPropertyProposal extends ScoreableProposal { + + protected final IDocument fDoc; + private final DocumentEdits proposalApplier; + private boolean isDeprecated = false; + + public AbstractPropertyProposal(IDocument doc, DocumentEdits applier) { + this.proposalApplier = applier; + this.fDoc = doc; + } + + @Override + public String getLabel() { + return getBaseDisplayString(); + } + +// public IRegion getSelection(IDocument document) { +// try { +// return proposalApplier.getSelection(document); +// } catch (Exception e) { +// Log.log(e); +// return null; +// } +// } + + +// public String getDisplayString() { +// StyledString styledText = getStyledDisplayString(); +// return styledText.getString(); +// } + +// public Image getImage() { +// return null; +// } + +// public IContextInformation getContextInformation() { +// return null; +// } + +// @Override +// public StyledString getStyledDisplayString() { +// StyledString result = new StyledString(); +// result = result.append(super.getStyledDisplayString()); +// YType type = getType(); +// if (type!=null) { +// String typeStr = niceTypeName(type); +// result.append(" : "+typeStr, StyledString.DECORATIONS_STYLER); +// } +// return result; +// } + + protected boolean isDeprecated() { + return isDeprecated; + } + public void deprecate() { + if (!isDeprecated()) { + deemphasize(); + deemphasize(); + isDeprecated = true; + } + } + protected abstract YType getType(); + protected abstract String getHighlightPattern(); + protected abstract String getBaseDisplayString(); + protected abstract String niceTypeName(YType type); + + @Override + public CompletionItemKind getKind() { + return CompletionItemKind.Field; + } + + @Override + public String toString() { + return getBaseDisplayString(); + } + + @Override + public final DocumentEdits getTextEdit() { + return this.proposalApplier; + } + +// @Override +// public void apply(IDocument document) { +// try { +// proposalApplier.apply(document); +// } catch (Exception e) { +// EditorSupportActivator.log(e); +// } +// } +} \ No newline at end of file diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/completions/PropertyCompletionFactory.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/completions/PropertyCompletionFactory.java new file mode 100644 index 000000000..8682fbb2e --- /dev/null +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/completions/PropertyCompletionFactory.java @@ -0,0 +1,163 @@ +/******************************************************************************* + * Copyright (c) 2014-2016 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.application.properties.metadata.completions; + +import org.springframework.ide.vscode.application.properties.metadata.PropertyInfo; +import org.springframework.ide.vscode.application.properties.metadata.hints.ValueHintHoverInfo; +import org.springframework.ide.vscode.application.properties.metadata.types.Type; +import org.springframework.ide.vscode.application.properties.metadata.types.TypeParser; +import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtil; +import org.springframework.ide.vscode.application.properties.metadata.types.TypedProperty; +import org.springframework.ide.vscode.application.properties.metadata.util.FuzzyMap.Match; +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.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.util.IDocument; +import org.springframework.ide.vscode.commons.yaml.schema.YType; + +import io.typefox.lsapi.CompletionItemKind; + +public class PropertyCompletionFactory { + + + public ICompletionProposal valueProposal(String value, String query, Type type, double score, DocumentEdits edits, ValueHintHoverInfo info) { + return new ScoreableProposal() { + + @Override + public DocumentEdits getTextEdit() { + return edits; + } + + @Override + public String getLabel() { + return value; + } + + @Override + public CompletionItemKind getKind() { + return CompletionItemKind.Value; + } + + @Override + public double getBaseScore() { + return score; + } + }; + } + + public ScoreableProposal property(IDocument doc, DocumentEdits applier, Match prop, TypeUtil typeUtil) { + return new PropertyProposal(doc, applier, prop, typeUtil); + } + + 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 + protected String getBaseDisplayString() { + return property.getName(); + } + + @Override + protected String getHighlightPattern() { + return pattern; + } + + @Override + protected Type getType() { + return property.getType(); + } + + @Override + public double getBaseScore() { + return score; + } + + @Override + protected String niceTypeName(YType type) { + return typeUtil.niceTypeName((Type) type); + } + + }; + if (property.isDeprecated()) { + proposal.deprecate(); + } + return proposal; + } + + private JavaProjectFinder documentContextFinder; + + public PropertyCompletionFactory(JavaProjectFinder documentContextFinder) { + this.documentContextFinder = documentContextFinder; + } + + private class PropertyProposal extends AbstractPropertyProposal { + private Match match; + private Type type; + private TypeUtil typeUtil; + + public PropertyProposal(IDocument doc, DocumentEdits applier, Match match, + TypeUtil typeUtil) { + super(doc, applier); + this.typeUtil = typeUtil; + this.match = match; + if (match.data.isDeprecated()) { + deprecate(); + } + } + +// @Override +// public HoverInfo getAdditionalProposalInfo(IProgressMonitor monitor) { +// return new SpringPropertyHoverInfo(documentContextFinder.getJavaProject(fDoc), match.data); +// } + + @Override + protected String getBaseDisplayString() { + return match.data.getId(); + } + + @Override + public double getBaseScore() { + return match.score; + } + + @Override + protected Type getType() { + if (type==null) { + type = TypeParser.parse(match.data.getType()); + } + return type; + } + + @Override + protected String getHighlightPattern() { + return match.getPattern(); + } + + @Override + protected String niceTypeName(YType type) { + return typeUtil.niceTypeName(((Type)type)); + } + } + +} diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/completions/RelaxedNameConfig.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/completions/RelaxedNameConfig.java new file mode 100644 index 000000000..1518e8be2 --- /dev/null +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/completions/RelaxedNameConfig.java @@ -0,0 +1,42 @@ +package org.springframework.ide.vscode.application.properties.metadata.completions; + +import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtil.BeanPropertyNameMode; +import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtil.EnumCaseMode; + +/** + * Config object that determines some aspects of how the freedom of 'relaxed name binding' + * are taken into account when generating content-assist completions. + * + * @author Kris De Volder + */ +public class RelaxedNameConfig { + + public static final RelaxedNameConfig ALIASSED = new RelaxedNameConfig(EnumCaseMode.ALIASED, BeanPropertyNameMode.ALIASED); + public static final RelaxedNameConfig COMPLETION_DEFAULTS = new RelaxedNameConfig(EnumCaseMode.LOWER_CASE, BeanPropertyNameMode.HYPHENATED); + + private EnumCaseMode enumMode = EnumCaseMode.LOWER_CASE; + private BeanPropertyNameMode beanMode = BeanPropertyNameMode.HYPHENATED; + + public RelaxedNameConfig(EnumCaseMode enumMode, BeanPropertyNameMode beanMode) { + this.enumMode = enumMode; + this.beanMode = beanMode; + } + public EnumCaseMode getEnumMode() { + return enumMode; + } + public void setEnumMode(EnumCaseMode preferredEnumCompletions) { + this.enumMode = preferredEnumCompletions; + } + public BeanPropertyNameMode getBeanMode() { + return beanMode; + } + public void setBeanMode(BeanPropertyNameMode preferredBeanCompletions) { + this.beanMode = preferredBeanCompletions; + } + + @Override + public String toString() { + return "RelaxedNameConfig("+enumMode+", "+beanMode+")"; + } + +} diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/BasicHintProvider.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/BasicHintProvider.java new file mode 100644 index 000000000..82f17bf54 --- /dev/null +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/BasicHintProvider.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2016 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.application.properties.metadata.hints; + +import java.util.Collection; +import java.util.List; + +import org.springframework.boot.configurationmetadata.ValueHint; +import org.springframework.ide.vscode.application.properties.metadata.ValueProviderRegistry.ValueProviderStrategy; +import org.springframework.ide.vscode.application.properties.metadata.types.TypedProperty; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.util.CollectionUtil; +import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; + +/** + * A single hint provider that combines the two kinds of 'hint' metadata that spring boot + * may contain (namely 'values' data and 'valueProvider' data). + *

+ * This basic hint provider is not context-aware and returns the same value hints regardless of the + * yaml context. + *

+ * This hint provider doesn't provide 'property' hints because property hints typically require + * context information (i.e the type of the parent context). + *

+ * To make this provider 'context aware' it can be wrapped in an adapter created by calling + * one of the static methods in the {@link HintProviders} class. + * + * @author Kris De Volder + */ +public class BasicHintProvider implements HintProvider { + + private IJavaProject javaProject; + private ImmutableList valueHints; + private ValueProviderStrategy valueProvider; + + public BasicHintProvider(IJavaProject javaProject, + ImmutableList valueHints, + ValueProviderStrategy valueProvider) { + this.javaProject = javaProject; + this.valueHints = valueHints; + this.valueProvider = valueProvider; + } + + @Override + public HintProvider traverse(YamlPathSegment s) throws Exception { + //since this provider is not context sensitive it just returns itself (So hints provides in 'sub-contexts' + // are exaclty the same as hints in the parent context. + return this; + } + + @Override + public List getValueHints(String query) { + Builder builder = ImmutableList.builder(); + if (CollectionUtil.hasElements(valueHints)) { + for (ValueHint hint : valueHints) { + builder.add(StsValueHint.create(hint)); + } + } + if (valueProvider!=null) { + Collection provided = valueProvider.getValuesNow(javaProject, query); + if (CollectionUtil.hasElements(provided)) { + builder.addAll(provided); + } + } + return builder.build(); + } + + @Override + public List getPropertyHints(String query) { + return ImmutableList.of(); + } + + +} diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/HintProvider.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/HintProvider.java new file mode 100644 index 000000000..4613057d5 --- /dev/null +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/HintProvider.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2016 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.application.properties.metadata.hints; + +import java.util.List; + +import org.springframework.ide.vscode.application.properties.metadata.types.TypedProperty; +import org.springframework.ide.vscode.commons.yaml.path.YamlNavigable; + +/** + * @author Kris De Volder + */ +public interface HintProvider extends YamlNavigable { + List getValueHints(String query); + List getPropertyHints(String query); +} diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/HintProviders.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/HintProviders.java new file mode 100644 index 000000000..897e55c67 --- /dev/null +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/HintProviders.java @@ -0,0 +1,222 @@ +/******************************************************************************* + * Copyright (c) 2016 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.application.properties.metadata.hints; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.boot.configurationmetadata.ValueHint; +import org.springframework.ide.vscode.application.properties.metadata.ValueProviderRegistry.ValueProviderStrategy; +import org.springframework.ide.vscode.application.properties.metadata.types.Type; +import org.springframework.ide.vscode.application.properties.metadata.types.TypedProperty; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.util.CollectionUtil; +import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment; + +import com.google.common.collect.ImmutableList; + +/** + * Methods for creating hints providers that provide hint in specific kind of context. + * + * @author Kris De Volder + */ +public class HintProviders { + + /** + * HintProvider that never returns any hints. This should be used + * instead of null pointer. + */ + public static final HintProvider NULL = new HintProvider() { + + @Override + public HintProvider traverse(YamlPathSegment s) throws Exception { + return NULL; + } + + @Override + public List getValueHints(String query) { + return ImmutableList.of(); + } + + @Override + public List getPropertyHints(String query) { + return ImmutableList.of(); + } + }; + + /** + * Creates a non-context-aware hint provider. Typically a hint provider is created by composing the result of + * this with one or more of the other methods in this class to wrap the basic provider so it becomes context-aware. + */ + public static HintProvider basic(IJavaProject jp, final ImmutableList valueHints, final ValueProviderStrategy valueProvider) { + if (!CollectionUtil.hasElements(valueHints) && valueProvider==null) { + return NULL; + } + return new BasicHintProvider(jp, valueHints, valueProvider); + } + + /** + * Create a hint provider that will return the given hints in the context following + * a traversal that goes down into a 'domain of' context a given number of times. + */ + public static HintProvider forDomainAt(final HintProvider valueHints, final int dim) { + if (isNull(valueHints)) { + return NULL; + } + if (dim==0) { + return forHere(valueHints); + } + return new HintProvider() { + public HintProvider traverse(YamlPathSegment s) throws Exception { + switch (s.getType()) { + case VAL_AT_INDEX: + case VAL_AT_KEY: + return forDomainAt(valueHints, dim-1); + default: + return NULL; + } + } + + public List getValueHints(String query) { + return ImmutableList.of(); + } + + @Override + public List getPropertyHints(String query) { + return ImmutableList.of(); + } + }; + } + + /** + * Only returns the given hints in this context but not one of its 'sub contexts'. + */ + public static HintProvider forHere(final HintProvider valueHints) { + if (isNull(valueHints)) { + return NULL; + } + return new HintProvider() { + + @Override + public HintProvider traverse(YamlPathSegment s) throws Exception { + return NULL; + } + + @Override + public List getValueHints(String query) { + return valueHints.getValueHints(query); + } + + @Override + public List getPropertyHints(String query) { + return ImmutableList.of(); + } + }; + } + + /** + * REturns the given hints in this context and any of its subcontexts that expect values. + */ + public static HintProvider forAllValueContexts(final HintProvider valueProvider) { + if (isNull(valueProvider)) { + return NULL; + } + return new HintProvider() { + @Override + public HintProvider traverse(YamlPathSegment s) throws Exception { + switch (s.getType()) { + case VAL_AT_INDEX: + case VAL_AT_KEY: + return this; + default: + return NULL; + } + } + + @Override + public List getValueHints(String query) { + return valueProvider.getValueHints(query); + } + + @Override + public List getPropertyHints(String query) { + return ImmutableList.of(); + } + }; + } + + public static boolean isNull(HintProvider p) { + //If everyone is nice and doesn't ever use null pointers then the p==null check is + // not needed. But just in case. + return p == NULL || p==null; + } + + public static HintProvider forMap(HintProvider _keyProvider, HintProvider _valueProvider, final Type valueType, final boolean dimensionAware) { + final HintProvider keyProvider = notNull(_keyProvider); + final HintProvider valueProvider = notNull(_valueProvider); + if (isNull(keyProvider) && isNull(valueProvider)) { + return NULL; + } + return new HintProvider() { + + @Override + public HintProvider traverse(YamlPathSegment s) throws Exception { + switch (s.getType()) { + case VAL_AT_INDEX: + case VAL_AT_KEY: + if (dimensionAware) { + return forHere(valueProvider); + } else { + return forAllValueContexts(valueProvider); + } + default: + return NULL; + } + } + + @Override + public List getValueHints(String query) { + if (dimensionAware) { + //pickier, completions only suggested in the domain of map, but not for map itself. + return ImmutableList.of(); + } else { + return valueProvider.getValueHints(query); + } + } + + @Override + public List getPropertyHints(String query) { + List keyHints = keyProvider.getValueHints(query); + if (CollectionUtil.hasElements(keyHints)) { + List props = new ArrayList<>(keyHints.size()); + for (StsValueHint keyHint : keyHints) { + Object key = keyHint.getValue(); + if (key instanceof String) { + props.add(new TypedProperty((String)key, valueType, null)); + } + } + return props; + } + return ImmutableList.of(); + } + }; + } + + /** + * Protection against bad code passing us null pointers. + */ + private static HintProvider notNull(HintProvider p) { + if (p==null) { + return NULL; + } + return p; + } +} diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/types/StsValueHint.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/StsValueHint.java similarity index 97% rename from vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/types/StsValueHint.java rename to vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/StsValueHint.java index cd325ad4a..7d5909824 100644 --- a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/types/StsValueHint.java +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/StsValueHint.java @@ -1,4 +1,4 @@ -package org.springframework.ide.vscode.application.properties.metadata.types; +package org.springframework.ide.vscode.application.properties.metadata.hints; import static org.springframework.ide.vscode.application.properties.metadata.util.DeprecationUtil.*; @@ -6,6 +6,7 @@ import javax.inject.Provider; import org.springframework.boot.configurationmetadata.Deprecation; import org.springframework.boot.configurationmetadata.ValueHint; +import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtil; import org.springframework.ide.vscode.application.properties.metadata.util.DeprecationUtil; import org.springframework.ide.vscode.commons.java.IJavaElement; import org.springframework.ide.vscode.commons.java.IJavaProject; diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/ValueHintHoverInfo.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/ValueHintHoverInfo.java new file mode 100644 index 000000000..3bfb87b07 --- /dev/null +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/ValueHintHoverInfo.java @@ -0,0 +1,9 @@ +package org.springframework.ide.vscode.application.properties.metadata.hints; + +public class ValueHintHoverInfo { + + public ValueHintHoverInfo(StsValueHint hint) { + // TODO Auto-generated constructor stub + } + +} diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/types/TypeUtil.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/types/TypeUtil.java index f432aa4c0..02d0ad5aa 100644 --- a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/types/TypeUtil.java +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/types/TypeUtil.java @@ -25,6 +25,7 @@ import javax.inject.Provider; import org.springframework.boot.configurationmetadata.Deprecation; import org.springframework.ide.vscode.application.properties.metadata.ValueProviderRegistry.ValueProviderStrategy; +import org.springframework.ide.vscode.application.properties.metadata.hints.StsValueHint; import org.springframework.ide.vscode.application.properties.metadata.util.DeprecationUtil; import org.springframework.ide.vscode.commons.java.Flags; import org.springframework.ide.vscode.commons.java.IField; diff --git a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/ScoreableProposal.java b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/ScoreableProposal.java similarity index 95% rename from vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/ScoreableProposal.java rename to vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/ScoreableProposal.java index e9cf3d364..95eaa314d 100644 --- a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/ScoreableProposal.java +++ b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/ScoreableProposal.java @@ -1,9 +1,7 @@ -package org.springframework.ide.vscode.commons.yaml.completion; +package org.springframework.ide.vscode.commons.languageserver.completion; import java.util.Comparator; -import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal; - public abstract class ScoreableProposal implements ICompletionProposal { private static final double DEEMP_VALUE = 100000; // should be large enough to move deemphasized stuff to bottom of list. diff --git a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/VscodeCompletionEngine.java b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/VscodeCompletionEngine.java similarity index 88% rename from vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/VscodeCompletionEngine.java rename to vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/VscodeCompletionEngine.java index eca70ff84..3b31ce17c 100644 --- a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/VscodeCompletionEngine.java +++ b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/VscodeCompletionEngine.java @@ -1,4 +1,4 @@ -package org.springframework.ide.vscode.manifest.yaml; +package org.springframework.ide.vscode.commons.languageserver.completion; import java.util.concurrent.CompletableFuture; diff --git a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/VscodeCompletionEngineAdapter.java b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/VscodeCompletionEngineAdapter.java similarity index 92% rename from vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/VscodeCompletionEngineAdapter.java rename to vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/VscodeCompletionEngineAdapter.java index d5cbad59c..aa67c4b20 100644 --- a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/VscodeCompletionEngineAdapter.java +++ b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/VscodeCompletionEngineAdapter.java @@ -1,4 +1,4 @@ -package org.springframework.ide.vscode.manifest.yaml; +package org.springframework.ide.vscode.commons.languageserver.completion; import java.util.ArrayList; import java.util.Collections; @@ -7,16 +7,13 @@ import java.util.concurrent.CompletableFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits.TextReplace; -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.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.languageserver.util.TextDocument; import org.springframework.ide.vscode.commons.util.Futures; import org.springframework.ide.vscode.commons.util.StringUtil; -import org.springframework.ide.vscode.commons.yaml.completion.ScoreableProposal; import io.typefox.lsapi.CompletionItem; import io.typefox.lsapi.CompletionList; diff --git a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/hover/HoverInfo.java b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/hover/HoverInfo.java new file mode 100644 index 000000000..3673eb44a --- /dev/null +++ b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/hover/HoverInfo.java @@ -0,0 +1,8 @@ +package org.springframework.ide.vscode.commons.languageserver.hover; + +/** + * Placeholder. Still need to figure out what exactly we should do with this in vscode. + */ +public interface HoverInfo { + +} diff --git a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/SortKeys.java b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SortKeys.java similarity index 89% rename from vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/SortKeys.java rename to vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SortKeys.java index 80c3e782d..7cf2128d0 100644 --- a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/SortKeys.java +++ b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SortKeys.java @@ -1,4 +1,4 @@ -package org.springframework.ide.vscode.manifest.yaml; +package org.springframework.ide.vscode.commons.languageserver.util; import java.util.Iterator; diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/DefaultCompletionFactory.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/DefaultCompletionFactory.java index c534a1ade..3465ac071 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/DefaultCompletionFactory.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/DefaultCompletionFactory.java @@ -2,6 +2,7 @@ package org.springframework.ide.vscode.commons.yaml.completion; 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.yaml.schema.YType; import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil; diff --git a/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/completions/ApplicationYamlAssistContext.java b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/completions/ApplicationYamlAssistContext.java new file mode 100644 index 000000000..5bdbb3fb6 --- /dev/null +++ b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/completions/ApplicationYamlAssistContext.java @@ -0,0 +1,509 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 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.application.yaml.completions; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.springframework.ide.vscode.application.properties.metadata.IndexNavigator; +import org.springframework.ide.vscode.application.properties.metadata.PropertyInfo; +import org.springframework.ide.vscode.application.properties.metadata.completions.PropertyCompletionFactory; +import org.springframework.ide.vscode.application.properties.metadata.completions.RelaxedNameConfig; +import org.springframework.ide.vscode.application.properties.metadata.hints.HintProvider; +import org.springframework.ide.vscode.application.properties.metadata.hints.StsValueHint; +import org.springframework.ide.vscode.application.properties.metadata.hints.ValueHintHoverInfo; +import org.springframework.ide.vscode.application.properties.metadata.types.Type; +import org.springframework.ide.vscode.application.properties.metadata.types.TypeParser; +import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtil; +import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtil.BeanPropertyNameMode; +import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtil.EnumCaseMode; +import org.springframework.ide.vscode.application.properties.metadata.types.TypedProperty; +import org.springframework.ide.vscode.application.properties.metadata.util.FuzzyMap; +import org.springframework.ide.vscode.application.properties.metadata.util.FuzzyMap.Match; +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.ProposalApplier; +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.CollectionUtil; +import org.springframework.ide.vscode.commons.util.FuzzyMatcher; +import org.springframework.ide.vscode.commons.util.Log; +import org.springframework.ide.vscode.commons.util.StringUtil; +import org.springframework.ide.vscode.commons.yaml.completion.AbstractYamlAssistContext; +import org.springframework.ide.vscode.commons.yaml.completion.TopLevelAssistContext; +import org.springframework.ide.vscode.commons.yaml.completion.YamlAssistContext; +import org.springframework.ide.vscode.commons.yaml.completion.YamlPathEdits; +import org.springframework.ide.vscode.commons.yaml.path.YamlPath; +import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment; +import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment.YamlPathSegmentType; +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.SKeyNode; +import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SNode; +import org.springframework.ide.vscode.commons.yaml.util.YamlIndentUtil; +import org.springframework.ide.vscode.commons.yaml.util.YamlUtil; + +/** + * Represents a context insied a "application.yml" file relative to which we can provide + * content assistance. + */ +public abstract class ApplicationYamlAssistContext extends AbstractYamlAssistContext { + + protected final RelaxedNameConfig conf; + +// This may prove useful later but we don't need it for now + // /** + // * AssistContextKind is an classification of the different kinds of + // * syntactic context that CA can be invoked from. + // */ + // public static enum Kind { + // SKEY_KEY, /* CA called from a SKeyNode and node.isInKey(cursor)==true */ + // SKEY_VALUE, /* CA called from a SKeyNode and node.isInKey(cursor)==false */ + // SRAW /* CA called from a SRawNode */ + // } + // protected final Kind contextKind; + + public final TypeUtil typeUtil; + + public ApplicationYamlAssistContext(int documentSelector, YamlPath contextPath, TypeUtil typeUtil, RelaxedNameConfig conf) { + super(documentSelector, contextPath); + this.typeUtil = typeUtil; + this.conf = conf; + } + + /** + * 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(Type type) { + //Note that proper indentation after each \n" is added automatically + //so the strings created here do not need to contain indentation spaces + 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 the type expected at this context, may return null if unknown. + */ + protected abstract Type getType(); + + public static ApplicationYamlAssistContext subdocument(int documentSelector, FuzzyMap index, PropertyCompletionFactory completionFactory, TypeUtil typeUtil, RelaxedNameConfig conf) { + return new IndexContext(documentSelector, YamlPath.EMPTY, IndexNavigator.with(index), completionFactory, typeUtil, conf); + } + + public static YamlAssistContext forPath(YamlPath contextPath, FuzzyMap index, PropertyCompletionFactory completionFactory, TypeUtil typeUtil, RelaxedNameConfig conf) { + try { + YamlPathSegment documentSelector = contextPath.getSegment(0); + if (documentSelector!=null) { + contextPath = contextPath.dropFirst(1); + YamlAssistContext context = ApplicationYamlAssistContext.subdocument(documentSelector.toIndex(), index, completionFactory, typeUtil, conf); + for (YamlPathSegment s : contextPath.getSegments()) { + if (context==null) return null; + context = context.traverse(s); + } + return context; + } + } catch (Exception e) { + Log.log(e); + } + return null; + } + + @Override + abstract public YamlAssistContext traverse(YamlPathSegment s) throws Exception; + + private static class TypeContext extends ApplicationYamlAssistContext { + + private PropertyCompletionFactory completionFactory; + private Type type; + private ApplicationYamlAssistContext parent; + private HintProvider hints; + + public TypeContext(ApplicationYamlAssistContext parent, YamlPath contextPath, Type type, + PropertyCompletionFactory completionFactory, TypeUtil typeUtil, RelaxedNameConfig conf, HintProvider hints) { + super(parent.documentSelector, contextPath, typeUtil, conf); + this.parent = parent; + this.completionFactory = completionFactory; + this.type = type; + this.hints = hints; + } + + private HintProvider getHintProvider() { + return hints; + } + + @Override + public Collection getCompletions(YamlDocument doc, SNode node, int offset) throws Exception { + String query = getPrefix(doc, node, offset); + EnumCaseMode enumCaseMode = enumCaseMode(query); + BeanPropertyNameMode beanMode = conf.getBeanMode(); + List valueCompletions = getValueCompletions(doc, offset, query, enumCaseMode); + if (!valueCompletions.isEmpty()) { + return valueCompletions; + } + return getKeyCompletions(doc, offset, query, enumCaseMode, beanMode); + } + + private EnumCaseMode enumCaseMode(String query) { + if (query.isEmpty()) { + return conf.getEnumMode(); + } else { + return EnumCaseMode.ALIASED; // will match candidates from both lower and original based on what user typed + } + } + + public List getKeyCompletions(YamlDocument doc, int offset, String query, + EnumCaseMode enumCaseMode, BeanPropertyNameMode beanMode) throws Exception { + int queryOffset = offset - query.length(); + List properties = getProperties(query, enumCaseMode, beanMode); + if (CollectionUtil.hasElements(properties)) { + ArrayList proposals = new ArrayList(properties.size()); + SNode contextNode = getContextNode(doc); + Set definedProps = getDefinedProperties(contextNode); + for (TypedProperty p : properties) { + String name = p.getName(); + double score = FuzzyMatcher.matchScore(query, name); + if (score!=0) { + YamlPath relativePath = YamlPath.fromSimpleProperty(name); + YamlPathEdits edits = new YamlPathEdits(doc); + if (!definedProps.contains(name)) { + //property not yet defined + Type type = p.getType(); + edits.delete(queryOffset, query); + edits.createPathInPlace(contextNode, relativePath, queryOffset, appendTextFor(type)); + proposals.add(completionFactory.beanProperty(doc.getDocument(), + contextPath.toPropString(), getType(), + 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() //deemphasize because it already exists + ); + } + } + } + return proposals; + } + return Collections.emptyList(); + } + + protected List getProperties(String query, EnumCaseMode enumCaseMode, BeanPropertyNameMode beanMode) { + ArrayList props = new ArrayList<>(); + List fromType = typeUtil.getProperties(type, enumCaseMode, beanMode); + if (CollectionUtil.hasElements(fromType)) { + props.addAll(fromType); + } + HintProvider hints = getHintProvider(); + if (hints!=null) { + List fromHints = hints.getPropertyHints(query); + if (CollectionUtil.hasElements(fromHints)) { + props.addAll(fromHints); + } + } + return props; + } + + private Set getDefinedProperties(SNode contextNode) { + try { + if (contextNode instanceof SChildBearingNode) { + List children = ((SChildBearingNode)contextNode).getChildren(); + if (CollectionUtil.hasElements(children)) { + Set keys = new HashSet(children.size()); + for (SNode c : children) { + if (c instanceof SKeyNode) { + keys.add(((SKeyNode) c).getKey()); + } + } + return keys; + } + } + } catch (Exception e) { + Log.log(e); + } + return Collections.emptySet(); + } + + private List getValueCompletions(YamlDocument doc, int offset, String query, EnumCaseMode enumCaseMode) { + Collection hints = getHintValues(query, doc, offset, enumCaseMode); + if (hints!=null) { + ArrayList completions = new ArrayList(); + for (StsValueHint hint : hints) { + String value = hint.getValue(); + double score = FuzzyMatcher.matchScore(query, value); + if (score!=0 && !value.equals(query)) { + DocumentEdits edits = new DocumentEdits(doc.getDocument()); + int valueStart = offset-query.length(); + edits.delete(valueStart, offset); + if (doc.getChar(valueStart-1)==':') { + edits.insert(offset, " "); + } + edits.insert(offset, YamlUtil.stringEscape(value)); + completions.add(completionFactory.valueProposal(value, query, type, score, edits, new ValueHintHoverInfo(hint))); + } + } + return completions; + } + return Collections.emptyList(); + } + +// @Override +// public HoverInfo getValueHoverInfo(YamlDocument doc, DocumentRegion valueRegion) { +// String value = valueRegion.toString(); +// +// if (TypeUtil.isClass(type)) { +// //Special case. We want hovers/hyperlinks even if the class is not a valid hint (as long as it is a class) +// StsValueHint hint = StsValueHint.className(value.toString(), typeUtil); +// if (hint!=null) { +// return new ValueHintHoverInfo(hint); +// } +// } +// +// Collection hints = getHintValues(value, doc, valueRegion.getEnd(), EnumCaseMode.ALIASED); +// //The hints where found by fuzzy match so they may not actually match exactly! +// for (StsValueHint h : hints) { +// if (value.equals(h.getValue())) { +// return new ValueHintHoverInfo(h); +// } +// } +// return super.getValueHoverInfo(doc, valueRegion); +// } + + protected Collection getHintValues( + String query, + YamlDocument doc, int offset, + EnumCaseMode enumCaseMode + ) { + Collection allHints = new ArrayList<>(); + { + Collection hints = typeUtil.getHintValues(type, query, enumCaseMode); + if (CollectionUtil.hasElements(hints)) { + allHints.addAll(hints); + } + } + { + HintProvider hintProvider = getHintProvider(); + if (hintProvider!=null) { + allHints.addAll(hintProvider.getValueHints(query)); + } + } + return allHints; + } + + @Override + public YamlAssistContext traverse(YamlPathSegment s) { + if (s.getType()==YamlPathSegmentType.VAL_AT_KEY) { + if (TypeUtil.isSequencable(type) || TypeUtil.isMap(type)) { + return contextWith(s, TypeUtil.getDomainType(type)); + } + String key = s.toPropString(); + Map subproperties = typeUtil.getPropertiesMap(type, EnumCaseMode.ALIASED, BeanPropertyNameMode.ALIASED); + if (subproperties!=null) { + return contextWith(s, TypedProperty.typeOf(subproperties.get(key))); + } + } else if (s.getType()==YamlPathSegmentType.VAL_AT_INDEX) { + if (TypeUtil.isSequencable(type)) { + return contextWith(s, TypeUtil.getDomainType(type)); + } + } + return null; + } + + private AbstractYamlAssistContext contextWith(YamlPathSegment s, Type nextType) { + if (nextType!=null) { + return new TypeContext(this, contextPath.append(s), nextType, completionFactory, typeUtil, conf, + new YamlPath(s).traverse(hints)); + } + return null; + } + + + @Override + public String toString() { + return "TypeContext("+contextPath.toPropString()+"::"+type+")"; + } + + +// @Override +// public HoverInfo getHoverInfo() { +// if (parent instanceof IndexContext) { +// //this context is in fact an 'alias' of its parent, representing the +// // point in the context hierarchy where a we transition from navigating +// // the index to navigating type/bean properties +// return parent.getHoverInfo(); +// } else { +// return new JavaTypeNavigationHoverInfo(contextPath.toPropString(), contextPath.getBeanPropertyName(), parent.getType(), getType(), typeUtil); +// } +// } + + @Override + protected Type getType() { + return type; + } + + } + + private static class IndexContext extends ApplicationYamlAssistContext { + + private IndexNavigator indexNav; + PropertyCompletionFactory completionFactory; + + public IndexContext(int documentSelector, YamlPath contextPath, IndexNavigator indexNav, + PropertyCompletionFactory completionFactory, TypeUtil typeUtil, RelaxedNameConfig conf) { + super(documentSelector, contextPath, typeUtil, conf); + this.indexNav = indexNav; + this.completionFactory = completionFactory; + } + + @Override + public Collection getCompletions(YamlDocument doc, SNode node, int offset) throws Exception { + String query = getPrefix(doc, node, offset); + Collection> matchingProps = indexNav.findMatching(query); + if (!matchingProps.isEmpty()) { + ArrayList completions = new ArrayList(); + for (Match match : matchingProps) { + DocumentEdits edits = createEdits(doc, offset, query, match); + ScoreableProposal completion = completionFactory.property( + doc.getDocument(), edits, match, typeUtil + ); + if (getContextRoot(doc).exists(YamlPath.fromProperty(match.data.getId()))) { + completion.deemphasize(); + } + completions.add(completion); + } + return completions; + } + return Collections.emptyList(); + } + + protected DocumentEdits createEdits(final YamlDocument file, + final int offset, final String query, final Match match) + throws Exception { + //Edits created lazyly as they are somwehat expensive to compute and mostly + // we need only the edits for the one proposal that user picks. + return LazyProposalApplier.from(() -> { + YamlPathEdits edits = new YamlPathEdits(file); + + int queryOffset = offset-query.length(); + edits.delete(queryOffset, query); + + YamlPath propertyPath = YamlPath.fromProperty(match.data.getId()); + YamlPath relativePath = propertyPath.dropFirst(contextPath.size()); + YamlPathSegment nextSegment = relativePath.getSegment(0); + SNode contextNode = getContextNode(file); + //To determine if this completion is 'in place' or needs to be inserted + // elsewhere in the tree, we check whether a node already exists in our + // context. If it doesn't we can create it as any child of the context + // so that includes, right at place the user is typing now. + SNode existingNode = contextNode.traverse(nextSegment); + String appendText = appendTextFor(TypeParser.parse(match.data.getType())); + if (existingNode==null) { + edits.createPathInPlace(contextNode, relativePath, queryOffset, appendText); + } else { + String wholeLine = file.getLineTextAtOffset(queryOffset); + if (wholeLine.trim().equals(query.trim())) { + edits.deleteLineBackwardAtOffset(queryOffset); + } + edits.createPath(getContextRoot(file), YamlPath.fromProperty(match.data.getId()), appendText); + } + return edits; + }); + } + + @Override + public AbstractYamlAssistContext traverse(YamlPathSegment s) { + if (s.getType()==YamlPathSegmentType.VAL_AT_KEY) { + String key = s.toPropString(); + IndexNavigator subIndex = indexNav.selectSubProperty(key); + if (subIndex.isEmpty()) { + //Nothing found for actual key... maybe its a 'camelCased' alias of real key? + String keyAlias = StringUtil.camelCaseToHyphens(key); + if (!keyAlias.equals(key)) { // no point checking alias is the same (likely key was not camelCased) + IndexNavigator aliasedSubIndex = indexNav.selectSubProperty(keyAlias); + if (!aliasedSubIndex.isEmpty()) { + subIndex = aliasedSubIndex; + } + } + } + if (subIndex.getExtensionCandidate()!=null) { + return new IndexContext(documentSelector, contextPath.append(s), subIndex, completionFactory, typeUtil, conf); + } else if (subIndex.getExactMatch()!=null) { + IndexContext asIndexContext = new IndexContext(documentSelector, contextPath.append(s), subIndex, completionFactory, typeUtil, conf); + PropertyInfo prop = subIndex.getExactMatch(); + return new TypeContext(asIndexContext, contextPath.append(s), TypeParser.parse(prop.getType()), completionFactory, typeUtil, conf, prop.getHints(typeUtil, true)); + } + } + //Unsuported navigation => no context for assist + return null; + } + + @Override + public String toString() { + return "YamlAssistIndexContext("+indexNav+")"; + } + + @Override + protected Type getType() { + PropertyInfo match = indexNav.getExactMatch(); + if (match!=null) { + return TypeParser.parse(match.getType()); + } + return null; + } + +// @Override +// public HoverInfo getHoverInfo() { +// PropertyInfo prop = indexNav.getExactMatch(); +// if (prop!=null) { +// return new SpringPropertyHoverInfo(typeUtil.getJavaProject(), prop); +// } +// return null; +// } + } + +// public abstract HoverInfo getHoverInfo(); +// public HoverInfo getHoverInfo(YamlPathSegment s) { +// //ApplicationYamlAssistContext implements getHoverInfo directly. so this is not needed. +// return null; +// } + + public static YamlAssistContext global(final FuzzyMap index, final PropertyCompletionFactory completionFactory, final TypeUtil typeUtil, final RelaxedNameConfig conf) { + return new TopLevelAssistContext() { + @Override + protected YamlAssistContext getDocumentContext(int documentSelector) { + return subdocument(documentSelector, index, completionFactory, typeUtil, conf); + } + }; + } +} diff --git a/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/completions/ApplicationYamlCompletionEngine.java b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/completions/ApplicationYamlCompletionEngine.java new file mode 100644 index 000000000..3d351aac9 --- /dev/null +++ b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/completions/ApplicationYamlCompletionEngine.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2015 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.application.yaml.completions; + +import org.springframework.ide.vscode.application.properties.metadata.PropertyInfo; +import org.springframework.ide.vscode.application.properties.metadata.SpringPropertyIndexProvider; +import org.springframework.ide.vscode.application.properties.metadata.completions.PropertyCompletionFactory; +import org.springframework.ide.vscode.application.properties.metadata.completions.RelaxedNameConfig; +import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtilProvider; +import org.springframework.ide.vscode.application.properties.metadata.util.FuzzyMap; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.util.IDocument; +import org.springframework.ide.vscode.commons.yaml.completion.YamlAssistContext; +import org.springframework.ide.vscode.commons.yaml.completion.YamlAssistContextProvider; +import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngine; +import org.springframework.ide.vscode.commons.yaml.structure.YamlDocument; +import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureProvider; + +/** + * @author Kris De Volder + */ +public class ApplicationYamlCompletionEngine { + public static YamlCompletionEngine create( + final SpringPropertyIndexProvider indexProvider, + final JavaProjectFinder documentContextFinder, + final YamlStructureProvider structureProvider, + final TypeUtilProvider typeUtilProvider, + final RelaxedNameConfig conf + ) { + final PropertyCompletionFactory completionFactory = new PropertyCompletionFactory(documentContextFinder); + YamlAssistContextProvider contextProvider = new YamlAssistContextProvider() { + @Override + public YamlAssistContext getGlobalAssistContext(YamlDocument ydoc) { + IDocument doc = ydoc.getDocument(); + FuzzyMap index = indexProvider.getIndex(doc); + return ApplicationYamlAssistContext.global(index, completionFactory, typeUtilProvider.getTypeUtil(doc), conf); + } + }; + return new YamlCompletionEngine(structureProvider, contextProvider); + } +} diff --git a/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/completions/LazyProposalApplier.java b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/completions/LazyProposalApplier.java new file mode 100644 index 000000000..fc655926e --- /dev/null +++ b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/completions/LazyProposalApplier.java @@ -0,0 +1,21 @@ +package org.springframework.ide.vscode.application.yaml.completions; + +import java.util.concurrent.Callable; + +import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; +import org.springframework.ide.vscode.commons.util.Log; + +/** + * Temprary placeholder which sort of replaces the LazyProposalApplier from old STS. + * It really does nothing right now. Somehow this should be tied into LS protocol so + * that the edits are not computed until a completion is being resolved. + *

+ * Right now this is not lazy at all and the completion edits are just computed immediatly. + */ +public class LazyProposalApplier { + + public static DocumentEdits from(Callable createEdits) throws Exception { + return createEdits.call(); + } + +} diff --git a/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/ApplicationYamlProblems.java b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/ApplicationYamlProblems.java similarity index 94% rename from vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/ApplicationYamlProblems.java rename to vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/ApplicationYamlProblems.java index 5436fe9a0..5df78ecef 100644 --- a/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/ApplicationYamlProblems.java +++ b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/ApplicationYamlProblems.java @@ -1,4 +1,4 @@ -package org.springframework.ide.vscode.application.yaml; +package org.springframework.ide.vscode.application.yaml.reconcile; import static org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity.ERROR; diff --git a/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/ApplicationYamlReconcileEngine.java b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/ApplicationYamlReconcileEngine.java index 5c82c9350..fc7258adf 100644 --- a/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/ApplicationYamlReconcileEngine.java +++ b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/ApplicationYamlReconcileEngine.java @@ -10,14 +10,13 @@ *******************************************************************************/ package org.springframework.ide.vscode.application.yaml.reconcile; -import static org.springframework.ide.vscode.application.yaml.ApplicationYamlProblems.Type.YAML_SYNTAX_ERROR; +import static org.springframework.ide.vscode.application.yaml.reconcile.ApplicationYamlProblems.Type.YAML_SYNTAX_ERROR; import org.springframework.ide.vscode.application.properties.metadata.IndexNavigator; import org.springframework.ide.vscode.application.properties.metadata.PropertyInfo; import org.springframework.ide.vscode.application.properties.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtilProvider; import org.springframework.ide.vscode.application.properties.metadata.util.FuzzyMap; -import org.springframework.ide.vscode.application.yaml.ApplicationYamlProblems; import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector; import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem; import org.springframework.ide.vscode.commons.languageserver.util.IDocument; diff --git a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServer.java b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServer.java index 26c4e0845..4fc912c15 100644 --- a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServer.java +++ b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServer.java @@ -4,6 +4,8 @@ import java.util.Collection; import javax.inject.Provider; +import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngine; +import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter; import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; From 54035b0a39c5f56d11dcbe65e4aea34e37d1fa1f Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Tue, 1 Nov 2016 10:55:30 -0700 Subject: [PATCH 3/3] Content assist in app-yml partially working --- .../DefaultSpringPropertyIndexProvider.java | 6 +- .../completion/DocumentEdits.java | 5 +- .../VscodeCompletionEngineAdapter.java | 8 ++ .../util/SimpleTextDocumentService.java | 1 + .../yaml/completion/YamlPathEdits.java | 3 +- .../testharness/LanguageServerHarness.java | 5 +- .../test/harness/AbstractPropsEditorTest.java | 7 ++ .../vscode/application/properties/Main.java | 7 +- .../yaml/ApplicationYamlLanguageServer.java | 79 +++++-------------- .../ide/vscode/application/yaml/Main.java | 11 +-- .../ApplicationYamlStructureProvider.java | 34 ++++++++ .../yaml/ApplicationYamlEditorTest.java | 5 +- .../ApplicationYamlLanguageServerTests.java | 2 +- 13 files changed, 97 insertions(+), 76 deletions(-) create mode 100644 vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/completions/ApplicationYamlStructureProvider.java diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/DefaultSpringPropertyIndexProvider.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/DefaultSpringPropertyIndexProvider.java index c5507a29e..f17923ac1 100644 --- a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/DefaultSpringPropertyIndexProvider.java +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/DefaultSpringPropertyIndexProvider.java @@ -7,9 +7,13 @@ import org.springframework.ide.vscode.commons.languageserver.util.IDocument; public class DefaultSpringPropertyIndexProvider implements SpringPropertyIndexProvider { - private JavaProjectFinder javaProjectFinder = JavaProjectFinder.DEFAULT; + private JavaProjectFinder javaProjectFinder; private SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager(ValueProviderRegistry.getDefault()); + public DefaultSpringPropertyIndexProvider(JavaProjectFinder javaProjectFinder) { + this.javaProjectFinder = javaProjectFinder; + } + @Override public FuzzyMap getIndex(IDocument doc) { IJavaProject jp = javaProjectFinder.find(doc); diff --git a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/DocumentEdits.java b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/DocumentEdits.java index 876b7b720..5a667bba7 100644 --- a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/DocumentEdits.java +++ b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/DocumentEdits.java @@ -16,6 +16,7 @@ import org.springframework.ide.vscode.commons.languageserver.util.BadLocationExc import org.springframework.ide.vscode.commons.languageserver.util.IDocument; import org.springframework.ide.vscode.commons.languageserver.util.IRegion; import org.springframework.ide.vscode.commons.languageserver.util.Region; +import org.springframework.ide.vscode.commons.languageserver.util.TextDocument; import org.springframework.ide.vscode.commons.util.Assert; import io.typefox.lsapi.TextEdit; @@ -322,12 +323,12 @@ public class DocumentEdits implements ProposalApplier { return null; } - public TextReplace asReplacement(IDocument doc) throws BadLocationException { + public TextReplace asReplacement(TextDocument doc) throws BadLocationException { if (!edits.isEmpty()) { int start = edits.stream().mapToInt(Edit::getStart).min().getAsInt(); int end = edits.stream().mapToInt(Edit::getEnd).max().getAsInt(); - DocumentState state = new DocumentState(doc); + DocumentState state = new DocumentState(doc.copy()); for (Edit edit : edits) { edit.apply(state); } diff --git a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/VscodeCompletionEngineAdapter.java b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/VscodeCompletionEngineAdapter.java index aa67c4b20..ef10ea366 100644 --- a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/VscodeCompletionEngineAdapter.java +++ b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/VscodeCompletionEngineAdapter.java @@ -28,6 +28,8 @@ import io.typefox.lsapi.impl.TextEditImpl; */ public class VscodeCompletionEngineAdapter implements VscodeCompletionEngine { + final private int MAX_COMPLETIONS = 10; + final static Logger logger = LoggerFactory.getLogger(VscodeCompletionEngineAdapter.class); public static final String VS_CODE_CURSOR_MARKER = "{{}}"; @@ -56,7 +58,13 @@ public class VscodeCompletionEngineAdapter implements VscodeCompletionEngine { list.setIncomplete(false); List items = new ArrayList<>(completions.size()); SortKeys sortkeys = new SortKeys(); + int count = 0; for (ICompletionProposal c : completions) { + count++; + if (count>MAX_COMPLETIONS) { + list.setIncomplete(true); + break; + } try { items.add(adaptItem(doc, c, sortkeys)); } catch (Exception e) { diff --git a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java index 686413344..29eeaa5c8 100644 --- a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java +++ b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java @@ -133,6 +133,7 @@ public class SimpleTextDocumentService implements TextDocumentService { @Override public void didClose(DidCloseTextDocumentParams params) { + System.out.println("closing: "+params.getTextDocument().getUri()); String url = params.getTextDocument().getUri(); if (url!=null) { documents.remove(url); diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/YamlPathEdits.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/YamlPathEdits.java index f4577d3f2..2802fc47c 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/YamlPathEdits.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/YamlPathEdits.java @@ -105,8 +105,9 @@ public class YamlPathEdits extends DocumentEdits { buf.append(":"); if (i completions = editor.getCompletions(); + assertNotNull(completions); + assertFalse(completions.isEmpty()); CompletionItem completion = editor.getFirstCompletion(); editor.apply(completion); assertEquals(expectTextAfter, editor.getText()); diff --git a/vscode-extensions/commons/properties-editor-test-harness/src/main/java/org/springframework/ide/vscode/properties/editor/test/harness/AbstractPropsEditorTest.java b/vscode-extensions/commons/properties-editor-test-harness/src/main/java/org/springframework/ide/vscode/properties/editor/test/harness/AbstractPropsEditorTest.java index 6c1751821..15a4813f6 100644 --- a/vscode-extensions/commons/properties-editor-test-harness/src/main/java/org/springframework/ide/vscode/properties/editor/test/harness/AbstractPropsEditorTest.java +++ b/vscode-extensions/commons/properties-editor-test-harness/src/main/java/org/springframework/ide/vscode/properties/editor/test/harness/AbstractPropsEditorTest.java @@ -14,6 +14,7 @@ import org.springframework.ide.vscode.application.properties.metadata.SpringProp import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtil; import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtilProvider; import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.util.IDocument; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; @@ -32,6 +33,8 @@ public abstract class AbstractPropsEditorTest { private ProjectsHarness projects = ProjectsHarness.INSTANCE; protected PropertyIndexHarness md; + protected final JavaProjectFinder javaProjectFinder = (doc) -> getTestProject(); + private LanguageServerHarness harness; private IJavaProject testProject; private TypeUtil typeUtil; @@ -47,6 +50,10 @@ public abstract class AbstractPropsEditorTest { return harness.newEditor(contents); } + private IJavaProject getTestProject() { + return testProject; + } + @Before public void setup() throws Exception { md = new PropertyIndexHarness(); diff --git a/vscode-extensions/vscode-application-properties/src/main/java/org/springframework/ide/vscode/application/properties/Main.java b/vscode-extensions/vscode-application-properties/src/main/java/org/springframework/ide/vscode/application/properties/Main.java index f26f2ae5f..0f81bb463 100644 --- a/vscode-extensions/vscode-application-properties/src/main/java/org/springframework/ide/vscode/application/properties/Main.java +++ b/vscode-extensions/vscode-application-properties/src/main/java/org/springframework/ide/vscode/application/properties/Main.java @@ -24,6 +24,7 @@ import org.springframework.ide.vscode.application.properties.metadata.DefaultSpr import org.springframework.ide.vscode.application.properties.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtil; import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtilProvider; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.util.IDocument; import org.springframework.ide.vscode.commons.languageserver.util.LoggingFormat; @@ -130,9 +131,9 @@ public class Main { * When the request stream is closed, wait for 5s for all outstanding responses to compute, then return. */ public static void run(Connection connection) { - SpringPropertyIndexProvider indexProvider = new DefaultSpringPropertyIndexProvider(); - TypeUtil typeUtil = new TypeUtil(null); - TypeUtilProvider typeUtilProvider = (IDocument doc) -> typeUtil; + JavaProjectFinder javaProjectFinder = JavaProjectFinder.DEFAULT; + SpringPropertyIndexProvider indexProvider = new DefaultSpringPropertyIndexProvider(javaProjectFinder); + TypeUtilProvider typeUtilProvider = (IDocument doc) -> new TypeUtil(javaProjectFinder.find(doc)); ApplicationPropertiesLanguageServer server = new ApplicationPropertiesLanguageServer(indexProvider, typeUtilProvider); diff --git a/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/ApplicationYamlLanguageServer.java b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/ApplicationYamlLanguageServer.java index a8b451889..cbc075b41 100644 --- a/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/ApplicationYamlLanguageServer.java +++ b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/ApplicationYamlLanguageServer.java @@ -1,27 +1,24 @@ package org.springframework.ide.vscode.application.yaml; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; - import org.springframework.ide.vscode.application.properties.metadata.SpringPropertyIndexProvider; +import org.springframework.ide.vscode.application.properties.metadata.completions.RelaxedNameConfig; import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtilProvider; +import org.springframework.ide.vscode.application.yaml.completions.ApplicationYamlCompletionEngine; +import org.springframework.ide.vscode.application.yaml.completions.ApplicationYamlStructureProvider; import org.springframework.ide.vscode.application.yaml.reconcile.ApplicationYamlReconcileEngine; +import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngine; +import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine; 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.TextDocument; -import org.springframework.ide.vscode.commons.util.Futures; import org.springframework.ide.vscode.commons.yaml.ast.YamlASTProvider; import org.springframework.ide.vscode.commons.yaml.ast.YamlParser; +import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngine; import org.yaml.snakeyaml.Yaml; -import io.typefox.lsapi.CompletionItem; -import io.typefox.lsapi.CompletionItemKind; -import io.typefox.lsapi.CompletionList; import io.typefox.lsapi.TextDocumentSyncKind; -import io.typefox.lsapi.impl.CompletionItemImpl; -import io.typefox.lsapi.impl.CompletionListImpl; import io.typefox.lsapi.impl.CompletionOptionsImpl; import io.typefox.lsapi.impl.ServerCapabilitiesImpl; @@ -32,7 +29,7 @@ public class ApplicationYamlLanguageServer extends SimpleLanguageServer { private SpringPropertyIndexProvider indexProvider; private TypeUtilProvider typeUtilProvider; - public ApplicationYamlLanguageServer(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider) { + public ApplicationYamlLanguageServer(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider, JavaProjectFinder javaProjectFinder) { this.indexProvider = indexProvider; this.typeUtilProvider = typeUtilProvider; SimpleTextDocumentService documents = getTextDocumentService(); @@ -54,54 +51,16 @@ public class ApplicationYamlLanguageServer extends SimpleLanguageServer { // } // }); - documents.onCompletion(params -> { - CompletableFuture promise = new CompletableFuture<>(); - CompletionListImpl completions = new CompletionListImpl(); - completions.setIncomplete(false); - List items = new ArrayList<>(); - { -// { -// label: 'TypeScript', -// kind: CompletionItemKind.Text, -// data: 1 -// }, - CompletionItemImpl item = new CompletionItemImpl(); - item.setLabel("TypeScript"); - item.setKind(CompletionItemKind.Text); - item.setData(1); - items.add(item); - } - - { -// { -// label: 'JavaScript', -// kind: CompletionItemKind.Text, -// data: 2 -// } - CompletionItemImpl item = new CompletionItemImpl(); - item.setLabel("JavaScript"); - item.setKind(CompletionItemKind.Text); - item.setData(2); - items.add(item); - } - completions.setItems(items); - - promise.complete(completions); - return promise; - }); - - documents.onCompletionResolve((_item) -> { - CompletionItemImpl item = (CompletionItemImpl) _item; - Object data = item.getData(); - if (Integer.valueOf(1).equals(data)) { - item.setDetail("TypeScript details"); - item.setDocumentation("TypeScript docs"); - } else { - item.setDetail("JavaScript details"); - item.setDocumentation("JavaScript docs"); - } - return Futures.of((CompletionItem)item); - }); + YamlCompletionEngine yamlCompletionEngine = ApplicationYamlCompletionEngine.create( + indexProvider, + javaProjectFinder, + ApplicationYamlStructureProvider.INSTANCE, + typeUtilProvider, + RelaxedNameConfig.COMPLETION_DEFAULTS + ); + VscodeCompletionEngine completionEngine = new VscodeCompletionEngineAdapter(this, yamlCompletionEngine); + documents.onCompletion(completionEngine::getCompletions); + documents.onCompletionResolve(completionEngine::resolveCompletion); } protected IReconcileEngine getReconcileEngine() { @@ -115,7 +74,7 @@ public class ApplicationYamlLanguageServer extends SimpleLanguageServer { c.setTextDocumentSync(TextDocumentSyncKind.Full); CompletionOptionsImpl completionProvider = new CompletionOptionsImpl(); - completionProvider.setResolveProvider(true); + completionProvider.setResolveProvider(false); c.setCompletionProvider(completionProvider); return c; diff --git a/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/Main.java b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/Main.java index 970377472..97c853dac 100644 --- a/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/Main.java +++ b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/Main.java @@ -13,6 +13,7 @@ import org.springframework.ide.vscode.application.properties.metadata.DefaultSpr import org.springframework.ide.vscode.application.properties.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtil; import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtilProvider; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.util.IDocument; import org.springframework.ide.vscode.commons.languageserver.util.LoggingFormat; @@ -83,14 +84,14 @@ public class Main { * When the request stream is closed, wait for 5s for all outstanding responses to compute, then return. */ public static void run(Connection connection) { - //TODO: proper TypeUtilProvider and IndexProvider that somehow determine classpath that should be + JavaProjectFinder javaProjectFinder = JavaProjectFinder.DEFAULT; + //TODO: proper TypeUtilProvider and IndexProvider that somehow determine classpath that should be // in effect for given IDocument and provide TypeUtil or SpringPropertyIndex parsed from that classpath. // Note that the provider is responsible for doing some kind of sensible caching so that indexes are not // rebuilt every time the index is being used. - SpringPropertyIndexProvider indexProvider = new DefaultSpringPropertyIndexProvider(); - TypeUtil typeUtil = new TypeUtil(null); - TypeUtilProvider typeUtilProvider = (IDocument doc) -> typeUtil; - ApplicationYamlLanguageServer server = new ApplicationYamlLanguageServer(indexProvider, typeUtilProvider); + SpringPropertyIndexProvider indexProvider = new DefaultSpringPropertyIndexProvider(javaProjectFinder); + TypeUtilProvider typeUtilProvider = (IDocument doc) -> new TypeUtil(javaProjectFinder.find(doc)); + ApplicationYamlLanguageServer server = new ApplicationYamlLanguageServer(indexProvider, typeUtilProvider, javaProjectFinder); LoggingJsonAdapter jsonServer = new LoggingJsonAdapter(server); jsonServer.setMessageLog(new PrintWriter(System.out)); diff --git a/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/completions/ApplicationYamlStructureProvider.java b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/completions/ApplicationYamlStructureProvider.java new file mode 100644 index 000000000..7505635a3 --- /dev/null +++ b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/completions/ApplicationYamlStructureProvider.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 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.application.yaml.completions; + +import java.util.Collections; + +import org.springframework.ide.vscode.commons.util.StringUtil; +import org.springframework.ide.vscode.commons.yaml.path.KeyAliases; +import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureProvider; + +public class ApplicationYamlStructureProvider { + + private static final KeyAliases KEY_ALIASES = new KeyAliases() { + @Override + public Iterable getKeyAliases(String base) { + String camelCased = StringUtil.hyphensToCamelCase(base, false); + if (!camelCased.equals(base)) { + return Collections.singletonList(camelCased); + } + return Collections.emptyList(); + } + }; + + public static final YamlStructureProvider INSTANCE = YamlStructureProvider.withAliases(KEY_ALIASES); + +} diff --git a/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/application/yaml/ApplicationYamlEditorTest.java b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/application/yaml/ApplicationYamlEditorTest.java index 540df4736..77ebaac6e 100644 --- a/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/application/yaml/ApplicationYamlEditorTest.java +++ b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/application/yaml/ApplicationYamlEditorTest.java @@ -21,6 +21,7 @@ import org.junit.Test; import org.springframework.ide.vscode.application.properties.metadata.CachingValueProvider; import org.springframework.ide.vscode.application.properties.metadata.PropertyInfo; import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.util.StringUtil; import org.springframework.ide.vscode.languageserver.testharness.Editor; @@ -41,6 +42,8 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { //////////////////////////////////////////////////////////////////////////////////////// + private JavaProjectFinder javaProjectFinder; + @Test public void linterRunsOnDocumentOpenAndChange() throws Exception { Editor editor = newEditor( "somemap: val\n"+ @@ -3519,7 +3522,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { @Override protected SimpleLanguageServer newLanguageServer() { - return new ApplicationYamlLanguageServer(md.getIndexProvider(), typeUtilProvider); + return new ApplicationYamlLanguageServer(md.getIndexProvider(), typeUtilProvider, javaProjectFinder); } } diff --git a/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/application/yaml/ApplicationYamlLanguageServerTests.java b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/application/yaml/ApplicationYamlLanguageServerTests.java index 883b73ab2..5d6cc2a5e 100644 --- a/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/application/yaml/ApplicationYamlLanguageServerTests.java +++ b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/application/yaml/ApplicationYamlLanguageServerTests.java @@ -22,7 +22,7 @@ public class ApplicationYamlLanguageServerTests { } private LanguageServerHarness newHarness() throws Exception { - Callable f = () -> new ApplicationYamlLanguageServer((d) -> null, (d) -> null); + Callable f = () -> new ApplicationYamlLanguageServer((d) -> null, (d) -> null, (d) -> null); return new LanguageServerHarness(f); }