Goto defintion for releases

This commit is contained in:
Kris De Volder
2017-07-19 15:33:02 -07:00
parent 9aa5ed36bb
commit 47dcb094c5
13 changed files with 317 additions and 60 deletions

View File

@@ -21,6 +21,7 @@ import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.Log;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
import org.springframework.ide.vscode.commons.yaml.ast.YamlAstCache;
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache;
@@ -36,14 +37,14 @@ public class ConcourseDefinitionFinder extends SimpleDefinitionFinder<ConcourseL
Flux<Location> handle(Node refNode, TextDocument doc, YamlFileAST ast);
}
private final ConcourseModel models;
private ASTTypeCache astTypes;
private final ASTTypeCache astTypes;
private Map<YType, Handler> handlers = new HashMap<>();
private final YamlAstCache asts;
public ConcourseDefinitionFinder(ConcourseLanguageServer server, ConcourseModel models, PipelineYmlSchema schema) {
super(server);
this.models = models;
this.astTypes = models.getAstTypeCache();
this.asts = models.getAstCache();
findByPath(schema.t_resource_name, ConcourseModel.RESOURCE_NAMES_PATH);
findByPath(schema.t_maybe_resource_name, ConcourseModel.RESOURCE_NAMES_PATH);
findByPath(schema.t_job_name, ConcourseModel.JOB_NAMES_PATH);
@@ -58,7 +59,7 @@ public class ConcourseDefinitionFinder extends SimpleDefinitionFinder<ConcourseL
* @param definitionsPath Path that points to all nodes within the same file corresponding
* to definitions of nodes of the given type.
*/
private void findByPath(YType refType, YamlPath definitionsPath) {
protected void findByPath(YType refType, YamlPath definitionsPath) {
astTypes.addInterestingType(refType);
Handler handler = (Node refNode, TextDocument doc, YamlFileAST ast) -> {
String name = NodeUtil.asScalar(refNode);
@@ -79,7 +80,7 @@ public class ConcourseDefinitionFinder extends SimpleDefinitionFinder<ConcourseL
try {
TextDocument doc = server.getTextDocumentService().get(params);
if (doc!=null) {
YamlFileAST ast = models.getSafeAst(doc, false);
YamlFileAST ast = asts.getSafeAst(doc, false);
if (ast!=null) {
Node refNode = ast.findNode(doc.toOffset(params.getPosition()));
if (refNode!=null) {

View File

@@ -49,7 +49,7 @@ public class ConcourseLanguageServer extends SimpleLanguageServer {
YamlStructureProvider structureProvider = YamlStructureProvider.DEFAULT;
SimpleTextDocumentService documents = getTextDocumentService();
ConcourseModel models = new ConcourseModel(this);
YamlASTProvider currentAsts = models.getAstProvider(false);
YamlASTProvider currentAsts = models.getAstCache().getAstProvider(false);
private SchemaSpecificPieces forPipelines;
private SchemaSpecificPieces forTasks;
private final YamlQuickfixes yamlQuickfixes;

View File

@@ -33,6 +33,7 @@ import org.springframework.ide.vscode.commons.util.StringUtil;
import org.springframework.ide.vscode.commons.util.text.IDocument;
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
import org.springframework.ide.vscode.commons.yaml.ast.YamlASTProvider;
import org.springframework.ide.vscode.commons.yaml.ast.YamlAstCache;
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
import org.springframework.ide.vscode.commons.yaml.ast.YamlParser;
import org.springframework.ide.vscode.commons.yaml.path.ASTRootCursor;
@@ -52,8 +53,8 @@ import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YBeanUnio
import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraint;
import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty;
import org.springframework.ide.vscode.commons.yaml.schema.YValueHint;
import org.springframework.ide.vscode.commons.yaml.util.StaleFallbackCache;
import org.springframework.ide.vscode.commons.yaml.util.Streams;
import org.springframework.ide.vscode.concourse.util.StaleFallbackCache;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.nodes.MappingNode;
@@ -112,7 +113,7 @@ public class ConcourseModel {
if (job!=null) {
//Only check if the job exists. Otherwise the extra checks will show 'redundant' errors (e.g.
// complaining that 'some-job' doesn't ineract with a resource (because the resource doesn't exist).
YamlFileAST root = this.getSafeAst(dc.getDocument());
YamlFileAST root = asts.getSafeAst(dc.getDocument());
if (root!=null) {
Node stepNode = path.dropLast().traverseToNode(root);
if (stepNode!=null) {
@@ -256,9 +257,7 @@ public class ConcourseModel {
valueAt("name")
);
private final YamlParser parser;
private final StaleFallbackCache<String, YamlFileAST> asts = new StaleFallbackCache<>();
private final YamlAstCache asts = new YamlAstCache();
private final ASTTypeCache astTypes = new ASTTypeCache();
private ResourceTypeRegistry resourceTypes;
@@ -268,8 +267,6 @@ public class ConcourseModel {
private YBeanUnionType stepType;
public ConcourseModel(SimpleLanguageServer languageServer) {
Yaml yaml = new Yaml();
this.parser = new YamlParser(yaml);
this.snippetBuilderFactory = languageServer::createSnippetBuilder;
}
@@ -368,7 +365,7 @@ public class ConcourseModel {
public Node getParentPropertyNode(String propName, DynamicSchemaContext dc) {
YamlPath path = dc.getPath();
if (path!=null) {
YamlFileAST root = this.getSafeAst(dc.getDocument());
YamlFileAST root = asts.getSafeAst(dc.getDocument());
if (root!=null) {
return path.dropLast().append(YamlPathSegment.valueAt(propName)).traverseToNode(root);
}
@@ -402,7 +399,7 @@ public class ConcourseModel {
if (doc!=null) {
String uri = doc.getUri();
if (uri!=null) {
YamlFileAST ast = getAst(doc, true);
YamlFileAST ast = asts.getAst(doc, true);
return astFunction.apply(ast);
}
}
@@ -414,36 +411,6 @@ public class ConcourseModel {
return null;
}
public YamlFileAST getSafeAst(IDocument doc) {
return getSafeAst(doc, true);
}
public YamlFileAST getAst(IDocument doc, boolean allowStaleAst) throws Exception {
return getAstProvider(allowStaleAst).getAST(doc);
}
public YamlASTProvider getAstProvider(boolean allowStaleAsts) {
return (IDocument doc) -> {
String uri = doc.getUri();
if (uri!=null) {
return asts.get(uri, doc.getVersion(), allowStaleAsts, () -> {
return parser.getAST(doc);
});
}
return null;
};
}
public YamlFileAST getSafeAst(IDocument doc, boolean allowStaleAst) {
if (doc!=null) {
try {
return getAst(doc, allowStaleAst);
} catch (Exception e) {
//ignored
}
}
return null;
}
public ASTTypeCache getAstTypeCache() {
return astTypes;
@@ -458,4 +425,8 @@ public class ConcourseModel {
this.stepType = step;
}
public YamlAstCache getAstCache() {
return this.asts;
}
}

View File

@@ -26,6 +26,7 @@ import org.springframework.ide.vscode.commons.util.ValueParser;
import org.springframework.ide.vscode.commons.util.ValueParsers;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
import org.springframework.ide.vscode.commons.yaml.ast.YamlAstCache;
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
@@ -134,6 +135,7 @@ public class PipelineYmlSchema implements YamlSchema {
private final ResourceTypeRegistry resourceTypes = new ResourceTypeRegistry();
private final ConcourseModel models;
private final YamlAstCache asts;
public final YType t_semver = f.yatomic("Semver")
.parseWith(ValueParsers.NE_STRING); //TODO: use real semver parser.
@@ -155,9 +157,9 @@ public class PipelineYmlSchema implements YamlSchema {
private List<YType> definitionTypes = new ArrayList<>();
public PipelineYmlSchema(ConcourseModel models) {
this.models = models;
this.asts = models.getAstCache();
models.setResourceTypeRegistry(resourceTypes);
TYPE_UTIL = f.TYPE_UTIL;
@@ -659,7 +661,7 @@ public class PipelineYmlSchema implements YamlSchema {
private String getSiblingPropertyValue(DynamicSchemaContext dc, String propName) {
YamlPath path = dc.getPath();
if (path!=null) {
YamlFileAST root = models.getSafeAst(dc.getDocument());
YamlFileAST root = asts.getSafeAst(dc.getDocument());
if (root!=null) {
return NodeUtil.asScalar(path.append(YamlPathSegment.valueAt(propName)).traverseToNode(root));
}

View File

@@ -1,135 +0,0 @@
/*******************************************************************************
* 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.concourse.util;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
/**
* A simple cache implementation that provides an option for lookups to fallback
* to a 'stale' cache entry when computing a current one fails.
*/
public class StaleFallbackCache<K, V>{
private static class Versioned<T> {
int version;
T it;
public Versioned(int version, T it) {
super();
this.version = version;
this.it = it;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((it == null) ? 0 : it.hashCode());
result = prime * result + version;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Versioned other = (Versioned) obj;
if (it == null) {
if (other.it != null)
return false;
} else if (!it.equals(other.it))
return false;
if (version != other.version)
return false;
return true;
}
@Override
public String toString() {
return "Versioned [version=" + version + ", it=" + it + "]";
}
}
Map<K, V> staleEntries = new HashMap<>();
Cache<K, Versioned<CompletableFuture<V>>> latestEntries = CacheBuilder.newBuilder().build();
public synchronized V get(K key, int version, boolean allowStaleEntries, Callable<? extends V> valueLoader) throws Exception {
Versioned<CompletableFuture<V>> latest = latestEntries.get(key, () -> new Versioned<>(version, load(valueLoader)));
if (latest.version!=version) {
latestEntries.invalidate(key);
keepStaleBackup(key, latest);
latest = latestEntries.get(key, () -> new Versioned<>(version, load(valueLoader)));
}
if (!allowStaleEntries) {
return future_get(version, latest);
} else {
if (latest.it.isCompletedExceptionally()) {
V staleValue = staleEntries.get(key);
if (staleValue!=null) {
return staleValue;
}
}
return future_get(latest.it);
}
}
/**
* Called when a stale entry is found in the 'latest' map. This method is
* responsible for determining if the entry should be kept as a staleBackup,
* and store it.
*/
private void keepStaleBackup(K key, Versioned<CompletableFuture<V>> latest) {
try {
staleEntries.put(key, latest.it.get());
} catch (InterruptedException | ExecutionException e) {
//ignore: This means its a 'bad' entry and so we don't want to keep it
// as a 'stale backup'.
}
}
private V future_get(int wantedVersion, Versioned<CompletableFuture<V>> versioned) throws Exception {
Assert.isLegal(wantedVersion==versioned.version);
return future_get(versioned.it);
}
private V future_get(CompletableFuture<V> f) throws Exception {
try {
return f.get();
} catch (InterruptedException e) {
throw e;
} catch (ExecutionException e) {
throw ExceptionUtil.exception(e.getCause());
}
}
private CompletableFuture<V> load(Callable<? extends V> valueLoader) {
CompletableFuture<V> future = new CompletableFuture<V>();
try {
V value = valueLoader.call();
Assert.isNotNull(value);
future.complete(value);
} catch (Throwable e) {
future.completeExceptionally(e);
}
return future;
}
}