diff --git a/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/ConcourseModel.java b/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/ConcourseModel.java index 261018ddb..f1b5b6e77 100644 --- a/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/ConcourseModel.java +++ b/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/ConcourseModel.java @@ -13,7 +13,9 @@ package org.springframework.ide.vscode.concourse; import static org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment.anyChild; import static org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment.valueAt; +import java.util.Collection; import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; @@ -42,9 +44,15 @@ public class ConcourseModel { anyChild(), valueAt("name") ); + + private static final YamlPath JOB_NAMES_PATH = new YamlPath( + valueAt("jobs"), + anyChild(), + valueAt("name") + ); private final YamlParser parser; - private StaleFallbackCache asts = new StaleFallbackCache<>(); + private final StaleFallbackCache asts = new StaleFallbackCache<>(); public ConcourseModel(SimpleTextDocumentService documents) { Yaml yaml = new Yaml(); @@ -69,17 +77,41 @@ public class ConcourseModel { * can not be parsed). */ public Set getResourceNames(IDocument doc) { + return getStringsFromAst(doc, RESOURCE_NAMES_PATH); + } + + + /** + * Returns the job names that are defined by given IDocument. If the contents + * of IDocument is not currently parseable then this may return stale information + * retained from a previous successful parse. + *

+ * It may also return null if its not currently possible to obtain the list of resource + * names (e.g. because there hasn't been a successful parse yet and current document contents + * can not be parsed). + */ + public Set getJobNames(IDocument doc) { + return getStringsFromAst(doc, JOB_NAMES_PATH); + } + + private Set getStringsFromAst(IDocument doc, YamlPath path) { + return getFromAst(doc, (ast) -> { + Node root = ast.get(0); + return path + .traverseAmbiguously(root) + .map(NodeUtil::asScalar) + .filter((string) -> string!=null) + .collect(Collectors.toSet()); + }); + } + + private T getFromAst(IDocument doc, Function getResourceNames) { try { if (doc!=null) { String uri = doc.getUri(); if (uri!=null) { YamlFileAST ast = getAst(doc); - Node root = ast.get(0); - return RESOURCE_NAMES_PATH - .traverseAmbiguously(root) - .map(NodeUtil::asScalar) - .filter((string) -> string!=null) - .collect(Collectors.toSet()); + return getResourceNames.apply(ast); } } } catch (YAMLException e) { @@ -106,4 +138,5 @@ public class ConcourseModel { }; } + } diff --git a/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java b/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java index d36b8aa8c..ba3501813 100644 --- a/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java +++ b/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java @@ -89,7 +89,7 @@ public class PipelineYmlSchema implements YamlSchema { // The vagrant-cloud r ); - YType resourceName = f.yenum("ResourceName", + YType resourceName = f.yenum("Resource Name", (parseString, validValues) -> { return "The '"+parseString+"' resource does not exist. Existing resources: "+validValues; }, @@ -98,17 +98,26 @@ public class PipelineYmlSchema implements YamlSchema { } ); + YType jobName = f.yenum("Job Name", + (parseString, validValues) -> { + return "The '"+parseString+"' resource does not exist. Existing resources: "+validValues; + }, + (DynamicSchemaContext dc) -> { + return models.getJobNames(dc.getDocument()); + } + ); + YBeanType getStep = f.ybean("GetStep"); prop(getStep, "get", resourceName); prop(getStep, "resource", t_string); prop(getStep, "version", t_version); - prop(getStep, "passed", t_strings); + prop(getStep, "passed", f.yseq(jobName)); prop(getStep, "params", t_params); prop(getStep, "trigger", t_boolean); YBeanType putStep = f.ybean("PutStep"); prop(putStep, "put", resourceName); - prop(putStep, "resource", t_string); + prop(putStep, "resource", jobName); prop(putStep, "params", t_params); prop(putStep, "get_params", t_params); diff --git a/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/concourse/PipelineYamlEditorTest.java b/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/concourse/PipelineYamlEditorTest.java index 33fd4f62b..a802a761b 100644 --- a/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/concourse/PipelineYamlEditorTest.java +++ b/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/concourse/PipelineYamlEditorTest.java @@ -511,6 +511,54 @@ public class PipelineYamlEditorTest { "type|Duplicate key" ); } + + @Test + public void reconcileJobNames() throws Exception { + Editor editor = harness.newEditor( + "resources:\n" + + "- name: git-repo\n" + + "- name: build-artefact\n" + + "jobs:\n" + + "- name: build\n" + + " plan:\n" + + " - get: git-repo\n" + + " - task: run-build\n" + + " - put: build-artefact\n" + + "- name: test\n" + + " plan:\n" + + " - get: git-repo\n" + + " passed:\n" + + " - not-a-job\n" + + " - build\n" + ); + + editor.assertProblems("not-a-job|does not exist"); + } + + @Test + public void contentAssistJobNames() throws Exception { + assertContextualCompletions( + "resources:\n" + + "- name: git-repo\n" + + "- name: build-artefact\n" + + "jobs:\n" + + "- name: build\n" + + " plan:\n" + + " - get: git-repo\n" + + " - task: run-build\n" + + " - put: build-artefact\n" + + "- name: test\n" + + " plan:\n" + + " - get: git-repo\n" + + " passed:\n" + + " - <*>\n" + , /////////////////////////// + "<*>" + , // => + "build<*>", + "test<*>" + ); + } //////////////////////////////////////////////////////////////////////////////