Goto Defintion working for Job Names

This commit is contained in:
Kris De Volder
2017-01-25 13:07:22 -08:00
parent 9403958569
commit cbb361f989
5 changed files with 84 additions and 43 deletions

View File

@@ -80,7 +80,7 @@ public class ASTTypeCache implements ITypeCollector {
* Declares a given YType as 'interesting'. This means that nodes of this type will be
* added to the index.
*/
public void addInterestingType(YAtomicType type) {
public void addInterestingType(YType type) {
this.interestingTypes.add(type);
}

View File

@@ -10,6 +10,8 @@
*******************************************************************************/
package org.springframework.ide.vscode.concourse;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.eclipse.lsp4j.Location;
@@ -17,9 +19,11 @@ import org.eclipse.lsp4j.TextDocumentPositionParams;
import org.springframework.ide.vscode.commons.languageserver.definition.SimpleDefinitionFinder;
import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.Log;
import org.springframework.ide.vscode.commons.util.text.IDocument;
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.YamlFileAST;
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
import org.springframework.ide.vscode.commons.yaml.schema.YType;
import org.yaml.snakeyaml.nodes.Node;
@@ -27,16 +31,45 @@ import reactor.core.publisher.Flux;
public class ConcourseDefinitionFinder extends SimpleDefinitionFinder<ConcourseLanguageServer> {
@FunctionalInterface
private interface Handler {
Flux<Location> handle(Node refNode, TextDocument doc, YamlFileAST ast);
}
private final ConcourseModel models;
private final PipelineYmlSchema schema;
private ASTTypeCache astTypes;
private Map<YType, Handler> findersByType = new HashMap<>();
public ConcourseDefinitionFinder(ConcourseLanguageServer server, ConcourseModel models, PipelineYmlSchema schema) {
super(server);
this.models = models;
this.schema = schema;
this.astTypes = models.getAstTypeCache();
astTypes.addInterestingType(schema.t_resource_name);
findByPath(schema.t_resource_name, ConcourseModel.RESOURCE_NAMES_PATH);
findByPath(schema.t_job_name, ConcourseModel.JOB_NAMES_PATH);
}
/**
* Add a handler that finds the definitions for a target node within the same document
* by following a {@link YamlPath} to find candidate nodes.
*
* @param refType the type inferred by the reconciler for the target node.
* @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) {
astTypes.addInterestingType(refType);
Handler handler = (Node refNode, TextDocument doc, YamlFileAST ast) -> {
String name = NodeUtil.asScalar(refNode);
if (name!=null) {
return Flux.fromStream(definitionsPath.traverseAmbiguously(ast))
.filter((node) -> name.equals(NodeUtil.asScalar(node)))
.map((node) -> toLocation(doc, node))
.filter(Optional::isPresent)
.map(Optional::get);
}
return Flux.empty();
};
findersByType.put(refType, handler);
}
@Override
@@ -48,12 +81,11 @@ public class ConcourseDefinitionFinder extends SimpleDefinitionFinder<ConcourseL
Node refNode = ast.findNode(doc.toOffset(params.getPosition()));
if (refNode!=null) {
YType type = astTypes.getType(ast, refNode);
if (schema.t_resource_name==type) {
String name = NodeUtil.asScalar(refNode);
return Flux.fromStream(models.getResourceDefinitionNodes(ast, name))
.map((node) -> toLocation(doc, node))
.filter(Optional::isPresent)
.map(Optional::get);
if (type!=null) {
Handler handler = findersByType.get(type);
if (handler!=null) {
return handler.handle(refNode, doc, ast);
}
}
}
}

View File

@@ -15,11 +15,7 @@ import static org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment.v
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
import org.springframework.ide.vscode.commons.languageserver.util.TextDocumentContentChange;
@@ -32,7 +28,6 @@ import org.springframework.ide.vscode.commons.yaml.ast.YamlParser;
import org.springframework.ide.vscode.commons.yaml.path.ASTRootCursor;
import org.springframework.ide.vscode.commons.yaml.path.NodeCursor;
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.schema.YTypeFactory;
import org.springframework.ide.vscode.commons.yaml.schema.YValueHint;
import org.springframework.ide.vscode.concourse.util.CollectorUtil;
@@ -41,14 +36,10 @@ import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.nodes.Node;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableMultiset.Builder;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multiset;
import reactor.core.publisher.Flux;
/**
* ConcourseModels is responsible for extracting various bits of information
* out of .yml documents and caching them for use by various tools (reconcile engine
@@ -56,30 +47,30 @@ import reactor.core.publisher.Flux;
*/
public class ConcourseModel {
private static final YamlPath RESOURCE_NAMES_PATH = new YamlPath(
valueAt("resources"),
public static final YamlPath JOB_NAMES_PATH = new YamlPath(
anyChild(),
valueAt("name")
);
private static final YamlPath JOB_NAMES_PATH = new YamlPath(
valueAt("jobs"),
anyChild(),
valueAt("name")
);
private static final YamlPath RESOURCE_TYPE_NAMES_PATH = new YamlPath(
valueAt("resource_types"),
anyChild(),
valueAt("name")
public static final YamlPath RESOURCE_TYPE_NAMES_PATH = new YamlPath(
anyChild(),
valueAt("resource_types"),
anyChild(),
valueAt("name")
);
private static final YamlPath RESOURCES_PATH = new YamlPath(
public static final YamlPath RESOURCES_PATH = new YamlPath(
anyChild(), // skip over the root node which contains multiple doces
valueAt("resources"),
anyChild()
);
public static final YamlPath RESOURCE_NAMES_PATH = RESOURCES_PATH.append(
valueAt("name")
);
private final YamlParser parser;
private final StaleFallbackCache<String, YamlFileAST> asts = new StaleFallbackCache<>();
@@ -150,9 +141,8 @@ public class ConcourseModel {
private Multiset<String> getStringsFromAst(IDocument doc, YamlPath path) {
return getFromAst(doc, (ast) -> {
Node root = ast.get(0);
return path
.traverseAmbiguously(root)
.traverseAmbiguously(ast)
.map(NodeUtil::asScalar)
.filter((string) -> string!=null)
.collect(CollectorUtil.toMultiset());
@@ -231,10 +221,4 @@ public class ConcourseModel {
return astTypes;
}
public Stream<Node> getResourceDefinitionNodes(YamlFileAST ast, String name) {
return RESOURCE_NAMES_PATH.prepend(YamlPathSegment.anyChild())
.traverseAmbiguously(ast)
.filter(node -> name.equals(NodeUtil.asScalar(node)));
}
}

View File

@@ -92,9 +92,11 @@ public class PipelineYmlSchema implements YamlSchema {
.parseWith(ValueParsers.integerAtLeast(1));
public final YAtomicType t_resource_name;
public final YAtomicType t_job_name;
private final ResourceTypeRegistry resourceTypes = new ResourceTypeRegistry();
public PipelineYmlSchema(ConcourseModel models) {
TYPE_UTIL = f.TYPE_UTIL;
@@ -119,7 +121,7 @@ public class PipelineYmlSchema implements YamlSchema {
}
);
this.t_resource_name = f.yenum("Resource Name",
t_resource_name = f.yenum("Resource Name",
(parseString, validValues) -> {
return "The '"+parseString+"' resource does not exist. Existing resources: "+validValues;
},
@@ -128,7 +130,7 @@ public class PipelineYmlSchema implements YamlSchema {
}
);
YType jobName = f.yenum("Job Name",
t_job_name = f.yenum("Job Name",
(parseString, validValues) -> {
return "The '"+parseString+"' Job does not exist. Existing jobs: "+validValues;
},
@@ -146,7 +148,7 @@ public class PipelineYmlSchema implements YamlSchema {
addProp(getStep, "get", t_resource_name);
addProp(getStep, "resource", t_string);
addProp(getStep, "version", t_version);
addProp(getStep, "passed", f.yseq(jobName));
addProp(getStep, "passed", f.yseq(t_job_name));
addProp(getStep, "params", f.contextAware("GetParams", (dc) ->
resourceTypes.getInParamsType(getResourceType("get", models, dc))
));
@@ -154,7 +156,7 @@ public class PipelineYmlSchema implements YamlSchema {
YBeanType putStep = f.ybean("PutStep");
addProp(putStep, "put", t_resource_name);
addProp(putStep, "resource", jobName);
addProp(putStep, "resource", t_job_name);
addProp(putStep, "params", f.contextAware("PutParams", (dc) ->
resourceTypes.getOutParamsType(getResourceType("put", models, dc))
));
@@ -226,7 +228,7 @@ public class PipelineYmlSchema implements YamlSchema {
YBeanType group = f.ybean("Group");
addProp(group, "name", t_ne_string).isRequired(true);
addProp(group, "resources", f.yseq(t_resource_name));
addProp(group, "jobs", f.yseq(jobName));
addProp(group, "jobs", f.yseq(t_job_name));
addProp(TOPLEVEL_TYPE, "resources", f.yseq(resource));
addProp(TOPLEVEL_TYPE, "jobs", f.yseq(job));

View File

@@ -1315,6 +1315,29 @@ public class PipelineYamlEditorTest {
);
}
@Test
public void gotoJobDefinition() throws Exception {
Editor editor = harness.newEditor(
"resources:\n" +
"- name: my-git\n" +
" type: git\n" +
"jobs:\n" +
"- name: prepare-stuff\n" +
" plan:\n" +
" - get: my-git\n" +
" - task: preparations\n" +
" file: my-git/ci/tasks/preparations.yml\n" +
"- name: do-stuff\n" +
" plan:\n" +
" - get: my-git\n" +
" passed:\n" +
" - prepare-stuff\n"
);
editor.assertGotoDefinition(editor.positionOf("- prepare-stuff", "prepare-stuff"),
editor.rangeOf("- name: prepare-stuff", "prepare-stuff")
);
}
@Test public void reconcileResourceTypeNames() throws Exception {
String userDefinedResourceTypesSnippet =
"resource_types:\n" +