Allow exceptions in hint providers to be handled by the framework
Changed the hint providers to throw exception when resolving hints, and the framework now handles the errors by creating a suitable error completion.
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
package org.springframework.ide.vscode.commons.util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import javax.inject.Provider;
|
||||
|
||||
@@ -32,15 +33,18 @@ public class EnumValueParser implements ValueParser {
|
||||
}
|
||||
|
||||
public EnumValueParser(String typeName, Collection<String> values) {
|
||||
this(typeName, () -> values);
|
||||
this(typeName, provider(values));
|
||||
}
|
||||
public EnumValueParser(String typeName, Callable<Collection<String>> values) {
|
||||
this(typeName, provider(values));
|
||||
}
|
||||
|
||||
public EnumValueParser(String typeName, Provider<Collection<String>> values) {
|
||||
this.typeName = typeName;
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
public Object parse(String str) {
|
||||
|
||||
public Object parse(String str) {
|
||||
// IMPORTANT: check the text FIRST before fetching values
|
||||
// from the hints provider, as the hints provider may be expensive when resolving values
|
||||
if (!StringUtil.hasText(str)) {
|
||||
@@ -48,6 +52,7 @@ public class EnumValueParser implements ValueParser {
|
||||
}
|
||||
|
||||
Collection<String> values = this.values.get();
|
||||
|
||||
//If values is not known (null) then just assume the str is acceptable.
|
||||
if (values==null || values.contains(str)) {
|
||||
return str;
|
||||
@@ -63,5 +68,21 @@ public class EnumValueParser implements ValueParser {
|
||||
protected String createErrorMessage(String parseString, Collection<String> values) {
|
||||
return "'"+parseString+"' is not valid for Enum '"+typeName+"'. Valid values are: "+values;
|
||||
}
|
||||
|
||||
|
||||
private static <T> Provider<T> provider(T values) {
|
||||
return () -> values;
|
||||
}
|
||||
|
||||
private static <T> Provider<T> provider(Callable<T> values) {
|
||||
return () -> {
|
||||
try {
|
||||
return values.call();
|
||||
} catch (Exception e) {
|
||||
// Ignore
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,5 +24,17 @@ public interface CompletionFactory {
|
||||
|
||||
ICompletionProposal beanProperty(IDocument doc, String contextProperty, YType contextType, String query, YTypedProperty p, double score, DocumentEdits edits, YTypeUtil typeUtil);
|
||||
ICompletionProposal valueProposal(String value, String query, String label, YType type, double score, DocumentEdits edits, YTypeUtil typeUtil);
|
||||
|
||||
|
||||
/**
|
||||
* Creates a completion with an EMPTY value and a display message indicating some error condition. The purpose of this is
|
||||
* to show the user a completion with some meaningful information on why completion values were not fetched.
|
||||
*
|
||||
* @param message the error message to display to the user in the completion
|
||||
* @param query
|
||||
* @param type
|
||||
* @param edits
|
||||
* @param typeUtil
|
||||
* @return
|
||||
*/
|
||||
ICompletionProposal errorMessage(String message, String query, YType type, DocumentEdits edits, YTypeUtil typeUtil);
|
||||
}
|
||||
|
||||
@@ -11,11 +11,11 @@
|
||||
|
||||
package org.springframework.ide.vscode.commons.yaml.completion;
|
||||
|
||||
import org.apache.commons.lang3.reflect.TypeUtils;
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ScoreableProposal;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMatcher;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.hover.YPropertyInfoTemplates;
|
||||
@@ -145,4 +145,11 @@ public class DefaultCompletionFactory implements CompletionFactory {
|
||||
return new ValueProposal(value, query, label, type, score, edits, typeUtil);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICompletionProposal errorMessage(String message, String query, YType type, DocumentEdits edits,
|
||||
YTypeUtil typeUtil) {
|
||||
String value = ""; // Empty value for the proposal. Purpose is to show a message with no value to fill in.
|
||||
double score = FuzzyMatcher.matchScore(query, value);
|
||||
return new ValueProposal(value, query, message, type, score, edits, typeUtil);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.springframework.ide.vscode.commons.languageserver.completion.Document
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.util.CollectionUtil;
|
||||
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMatcher;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
@@ -41,6 +42,8 @@ import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SNode;
|
||||
import org.springframework.ide.vscode.commons.yaml.util.YamlIndentUtil;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class YTypeAssistContext extends AbstractYamlAssistContext {
|
||||
|
||||
final static Logger logger = LoggerFactory.getLogger(YTypeAssistContext.class);
|
||||
@@ -147,7 +150,13 @@ public class YTypeAssistContext extends AbstractYamlAssistContext {
|
||||
}
|
||||
|
||||
private List<ICompletionProposal> getValueCompletions(YamlDocument doc, int offset, String query) {
|
||||
YValueHint[] values = typeUtil.getHintValues(type, getSchemaContext());
|
||||
YValueHint[] values=null;
|
||||
try {
|
||||
values = typeUtil.getHintValues(type, getSchemaContext());
|
||||
} catch (Exception e) {
|
||||
DocumentEdits edits = new DocumentEdits(doc.getDocument());
|
||||
return ImmutableList.of(completionFactory().errorMessage(ExceptionUtil.getMessage(e), query, type, edits, typeUtil));
|
||||
}
|
||||
if (values!=null) {
|
||||
ArrayList<ICompletionProposal> completions = new ArrayList<>();
|
||||
for (YValueHint value : values) {
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
package org.springframework.ide.vscode.commons.yaml.schema;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -20,11 +19,10 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Provider;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.Assert;
|
||||
import org.springframework.ide.vscode.commons.util.EnumValueParser;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
@@ -101,7 +99,7 @@ public class YTypeFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public YValueHint[] getHintValues(YType type, DynamicSchemaContext dc) {
|
||||
public YValueHint[] getHintValues(YType type, DynamicSchemaContext dc) throws Exception {
|
||||
return ((AbstractType)type).getHintValues(dc);
|
||||
}
|
||||
|
||||
@@ -147,7 +145,7 @@ public class YTypeFactory {
|
||||
private List<YTypedProperty> propertyList = new ArrayList<>();
|
||||
private final List<YValueHint> hints = new ArrayList<>();
|
||||
private Map<String, YTypedProperty> cachedPropertyMap;
|
||||
private SchemaContextAware<Collection<YValueHint>> hintProvider;
|
||||
private SchemaContextAware<Callable<Collection<YValueHint>>> hintProvider;
|
||||
|
||||
public boolean isSequenceable() {
|
||||
return false;
|
||||
@@ -169,17 +167,17 @@ public class YTypeFactory {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addHintProvider(Provider<Collection<YValueHint>> hintProvider) {
|
||||
addHintProvider((DynamicSchemaContext dc) -> hintProvider.get());
|
||||
public void addHintProvider(Callable<Collection<YValueHint>> hintProvider) {
|
||||
addHintProvider((DynamicSchemaContext dc) -> hintProvider);
|
||||
}
|
||||
|
||||
public void addHintProvider(SchemaContextAware<Collection<YValueHint>> hintProvider) {
|
||||
public void addHintProvider(SchemaContextAware<Callable<Collection<YValueHint>>> hintProvider) {
|
||||
this.hintProvider = hintProvider;
|
||||
}
|
||||
|
||||
public YValueHint[] getHintValues(DynamicSchemaContext dc) {
|
||||
Collection<YValueHint> providerHints = hintProvider != null ? hintProvider.withContext(dc) : null;
|
||||
|
||||
public YValueHint[] getHintValues(DynamicSchemaContext dc) throws Exception {
|
||||
Collection<YValueHint> providerHints=getProviderHints(dc);
|
||||
|
||||
if (providerHints == null || providerHints.isEmpty()) {
|
||||
return hints.toArray(new YValueHint[hints.size()]);
|
||||
} else {
|
||||
@@ -199,6 +197,16 @@ public class YTypeFactory {
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<YValueHint> getProviderHints(DynamicSchemaContext dc) throws Exception {
|
||||
if (hintProvider != null) {
|
||||
Callable<Collection<YValueHint>> withContext = hintProvider.withContext(dc);
|
||||
if (withContext != null) {
|
||||
return withContext.call();
|
||||
}
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
public List<YTypedProperty> getProperties(DynamicSchemaContext dc) {
|
||||
return Collections.unmodifiableList(propertyList);
|
||||
}
|
||||
@@ -627,7 +635,7 @@ public class YTypeFactory {
|
||||
Collection<String> strings = values.withContext(dc);
|
||||
return strings==null
|
||||
? null
|
||||
: strings.stream()
|
||||
: () -> strings.stream()
|
||||
.map((s) -> new BasicYValueHint(s))
|
||||
.collect(Collectors.toSet());
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ public interface YTypeUtil {
|
||||
boolean isSequencable(YType type);
|
||||
boolean isBean(YType type);
|
||||
YType getDomainType(YType type);
|
||||
YValueHint[] getHintValues(YType yType, DynamicSchemaContext dc);
|
||||
YValueHint[] getHintValues(YType yType, DynamicSchemaContext dc) throws Exception;
|
||||
String niceTypeName(YType type);
|
||||
YType getKeyType(YType type);
|
||||
ValueParser getValueParser(YType type, DynamicSchemaContext dc);
|
||||
|
||||
@@ -14,11 +14,10 @@ import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Provider;
|
||||
|
||||
import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.CFTarget;
|
||||
import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.CFTargetCache;
|
||||
import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.NoTargetsException;
|
||||
@@ -27,7 +26,7 @@ import org.springframework.ide.vscode.commons.util.ExceptionUtil;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.BasicYValueHint;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YValueHint;
|
||||
|
||||
public abstract class AbstractCFHintsProvider implements Provider<Collection<YValueHint>> {
|
||||
public abstract class AbstractCFHintsProvider implements Callable<Collection<YValueHint>> {
|
||||
|
||||
public static final String EMPTY_VALUE = "";
|
||||
protected final CFTargetCache targetCache;
|
||||
@@ -40,7 +39,7 @@ public abstract class AbstractCFHintsProvider implements Provider<Collection<YVa
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<YValueHint> get() {
|
||||
public Collection<YValueHint> call() throws Exception {
|
||||
Collection<YValueHint> hints = new ArrayList<>();
|
||||
// TODO: Probably not the most ideal thing to do, but for now show any
|
||||
// CF errors
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
package org.springframework.ide.vscode.manifest.yaml;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.inject.Provider;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.eclipse.lsp4j.CompletionOptions;
|
||||
import org.eclipse.lsp4j.ServerCapabilities;
|
||||
@@ -56,8 +55,8 @@ public class ManifestYamlLanguageServer extends SimpleLanguageServer {
|
||||
|
||||
YamlASTProvider parser = new YamlParser(yaml);
|
||||
|
||||
Provider<Collection<YValueHint>> buildPacksProvider = getBuildpacksProvider();
|
||||
Provider<Collection<YValueHint>> servicesProvider = getServicesProvider();
|
||||
Callable<Collection<YValueHint>> buildPacksProvider = getBuildpacksProvider();
|
||||
Callable<Collection<YValueHint>> servicesProvider = getServicesProvider();
|
||||
|
||||
schema = new ManifestYmlSchema(buildPacksProvider, servicesProvider);
|
||||
|
||||
@@ -100,11 +99,11 @@ public class ManifestYamlLanguageServer extends SimpleLanguageServer {
|
||||
return cfTargetCache;
|
||||
}
|
||||
|
||||
private Provider<Collection<YValueHint>> getBuildpacksProvider() {
|
||||
private Callable<Collection<YValueHint>> getBuildpacksProvider() {
|
||||
return new ManifestYamlCFBuildpacksProvider(getCfTargetCache());
|
||||
}
|
||||
|
||||
private Provider<Collection<YValueHint>> getServicesProvider() {
|
||||
private Callable<Collection<YValueHint>> getServicesProvider() {
|
||||
return new ManifestYamlCFServicesProvider(getCfTargetCache());
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,7 @@ package org.springframework.ide.vscode.manifest.yaml;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Provider;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
import org.springframework.ide.vscode.commons.util.Renderables;
|
||||
@@ -35,13 +34,13 @@ public class ManifestYmlSchema implements YamlSchema {
|
||||
|
||||
private final YBeanType TOPLEVEL_TYPE;
|
||||
private final YTypeUtil TYPE_UTIL;
|
||||
private final Provider<Collection<YValueHint>> buildpackProvider;
|
||||
private final Callable<Collection<YValueHint>> buildpackProvider;
|
||||
|
||||
private static final Set<String> TOPLEVEL_EXCLUDED = ImmutableSet.of(
|
||||
"name", "host", "hosts"
|
||||
);
|
||||
|
||||
public ManifestYmlSchema(Provider<Collection<YValueHint>> buildpackProvider, Provider<Collection<YValueHint>> servicesProvider) {
|
||||
public ManifestYmlSchema(Callable<Collection<YValueHint>> buildpackProvider, Callable<Collection<YValueHint>> servicesProvider) {
|
||||
this.buildpackProvider = buildpackProvider;
|
||||
YTypeFactory f = new YTypeFactory();
|
||||
TYPE_UTIL = f.TYPE_UTIL;
|
||||
|
||||
@@ -12,8 +12,7 @@ package org.springframework.ide.vscode.manifest.yaml;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Provider;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.Assert;
|
||||
import org.springframework.ide.vscode.commons.util.EnumValueParser;
|
||||
@@ -93,9 +92,9 @@ public class ManifestYmlValueParsers {
|
||||
};
|
||||
}
|
||||
|
||||
public static ValueParser fromHints(String typeName, Provider<Collection<YValueHint>> hintProvider) {
|
||||
Provider<Collection<String>> values= () -> {
|
||||
Collection<YValueHint> hints = hintProvider.get();
|
||||
public static ValueParser fromHints(String typeName, Callable<Collection<YValueHint>> hintProvider) {
|
||||
Callable<Collection<String>> values= () -> {
|
||||
Collection<YValueHint> hints = hintProvider.call();
|
||||
if (hints != null) {
|
||||
Builder<String> builder = ImmutableSet.builder();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user