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

@@ -0,0 +1,127 @@
/*******************************************************************************
* Copyright (c) 2017 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.bosh;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.lang3.tuple.Pair;
import org.eclipse.lsp4j.Location;
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.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.reconcile.ASTTypeCache;
import org.springframework.ide.vscode.commons.yaml.schema.YType;
import org.yaml.snakeyaml.nodes.Node;
import reactor.core.publisher.Flux;
public class BoshDefintionFinder extends SimpleDefinitionFinder<BoshLanguageServer> {
//TODO: lots of common code between BoshDefintionFinder and ConcourseDefinitionFinder.
// should be possible to pull up into a common super class.
private final YamlAstCache asts;
private final ASTTypeCache astTypes;
private final BoshDeploymentManifestSchema schema;
private Map<YType, Handler> handlers = new HashMap<>();
@FunctionalInterface
private interface Handler {
Flux<Location> handle(Node refNode, TextDocument doc, YamlFileAST ast);
}
public BoshDefintionFinder(BoshLanguageServer server, BoshDeploymentManifestSchema schema, YamlAstCache asts, ASTTypeCache astTypes) {
super(server);
this.schema = schema;
this.asts = asts;
this.astTypes = astTypes;
for (Pair<YType, YType> defAndRef : schema.getDefAndRefTypes()) {
YType def = defAndRef.getLeft();
if (def!=null) {
YType ref = defAndRef.getRight();
if (ref!=null) {
findByType(def, ref);
}
}
}
}
@Override
protected Flux<Location> findDefinitions(TextDocumentPositionParams params) {
try {
TextDocument doc = server.getTextDocumentService().get(params);
if (doc!=null) {
YamlFileAST ast = asts.getSafeAst(doc, false);
if (ast!=null) {
Node refNode = ast.findNode(doc.toOffset(params.getPosition()));
if (refNode!=null) {
YType type = astTypes.getType(ast, refNode);
if (type!=null) {
Handler handler = handlers.get(type);
if (handler!=null) {
return handler.handle(refNode, doc, ast);
}
}
}
}
}
} catch (Exception e) {
Log.log(e);
}
return Flux.empty();
}
/**
* Add a handler that finds the definitions for a target node within the same document
* by retrieving nodes of a given type as candidates.
*/
protected void findByType(YType def, YType ref) {
astTypes.addInterestingType(def);
astTypes.addInterestingType(ref);
Handler handler = (Node refNode, TextDocument doc, YamlFileAST ast) -> {
String uri = doc.getUri();
if (uri!=null) {
String name = NodeUtil.asScalar(refNode);
if (name!=null) {
Collection<Node> candidates = astTypes.getNodes(uri, def);
return Flux.fromIterable(candidates)
.filter((node) -> name.equals(NodeUtil.asScalar(node)))
.map((node) -> toLocation(doc, node))
.filter(Optional::isPresent)
.map(Optional::get);
}
}
return Flux.empty();
};
handlers.put(ref, handler);
}
protected Optional<Location> toLocation(TextDocument doc, Node node) {
int start = node.getStartMark().getIndex();
int end = node.getEndMark().getIndex();
try {
return Optional.of(new Location(doc.getUri(), doc.toRange(start, end-start)));
} catch (BadLocationException e) {
Log.log(e);
return Optional.empty();
}
}
}

View File

@@ -11,13 +11,16 @@
package org.springframework.ide.vscode.bosh;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.ide.vscode.bosh.models.CachingModelProvider;
import org.springframework.ide.vscode.bosh.models.CloudConfigModel;
import org.springframework.ide.vscode.bosh.models.DynamicModelProvider;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.CollectorUtil;
import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.util.Renderables;
import org.springframework.ide.vscode.commons.util.ValueParsers;
@@ -81,6 +84,7 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
private YType t_var_name_def;
private final ASTTypeCache astTypes;
private DynamicModelProvider<CloudConfigModel> cloudConfigProvider;
private List<Pair<YType, YType>> defAndRefTypes;
public BoshDeploymentManifestSchema(ASTTypeCache astTypes, DynamicModelProvider<CloudConfigModel> cloudConfigProvider) {
this.astTypes = astTypes;
@@ -280,14 +284,28 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
public Collection<YType> getDefinitionTypes() {
if (definitionTypes==null) {
definitionTypes = ImmutableList.of(
t_instance_group_name_def,
t_stemcell_alias_def,
t_release_name_def,
t_var_name_def
);
definitionTypes = getDefAndRefTypes().stream()
.map(pair -> pair.getLeft())
.collect(CollectorUtil.toImmutableList());
}
return definitionTypes;
}
/**
* @return Pairs of types. Each pair contains a 'def' type and a 'ref' type. Nodes with the ref-type
* shall be interpreted as reference to a corresponding node with the 'def' type if the def and ref node
* contain the same scalar value.
*/
public Collection<Pair<YType, YType>> getDefAndRefTypes() {
if (defAndRefTypes==null) {
defAndRefTypes = ImmutableList.of(
Pair.of(t_instance_group_name_def, null),
Pair.of(t_stemcell_alias_def, null),
Pair.of(t_release_name_def, t_release_name_ref),
Pair.of(t_var_name_def, null)
);
}
return defAndRefTypes;
}
}

View File

@@ -23,7 +23,7 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocu
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
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.ast.YamlAstCache;
import org.springframework.ide.vscode.commons.yaml.completion.SchemaBasedYamlAssistContextProvider;
import org.springframework.ide.vscode.commons.yaml.completion.YamlAssistContextProvider;
import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngine;
@@ -34,18 +34,18 @@ import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache;
import org.springframework.ide.vscode.commons.yaml.reconcile.TypeBasedYamlSymbolHandler;
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaBasedReconcileEngine;
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureProvider;
import org.yaml.snakeyaml.Yaml;
public class BoshLanguageServer extends SimpleLanguageServer {
private Yaml yaml = new Yaml();
private final LazyCompletionResolver completionResolver = new LazyCompletionResolver(); //Set to null to disable lazy resolving
private final VscodeCompletionEngineAdapter completionEngine;
public BoshLanguageServer(DynamicModelProvider<CloudConfigModel> cloudConfigProvider) {
super("vscode-bosh");
YamlASTProvider parser = new YamlParser(yaml);
YamlAstCache asts = new YamlAstCache();
YamlASTProvider parser = asts.getAstProvider(false);
SimpleTextDocumentService documents = getTextDocumentService();
ASTTypeCache astTypeCache = new ASTTypeCache();
BoshDeploymentManifestSchema schema = new BoshDeploymentManifestSchema(astTypeCache, cloudConfigProvider);
@@ -67,6 +67,7 @@ public class BoshLanguageServer extends SimpleLanguageServer {
documents.onCompletion(completionEngine::getCompletions);
documents.onCompletionResolve(completionEngine::resolveCompletion);
documents.onHover(hoverEngine ::getHover);
documents.onDefinition(new BoshDefintionFinder(this, schema, asts, astTypeCache));
}
private void validateOnDocumentChange(IReconcileEngine engine, TextDocument doc) {

View File

@@ -26,7 +26,6 @@ import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
import org.springframework.ide.vscode.languageserver.testharness.Editor;
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
public class BoshEditorTest {
LanguageServerHarness harness;
@@ -920,4 +919,34 @@ public class BoshEditorTest {
assertContains("Reading cloud config timed out", completion.getDocumentation());
}
@Test public void gotoReleaseDefinition() throws Exception {
Editor editor = harness.newEditor(
"name: foo\n" +
"instance_groups: \n" +
"- name: some-server\n" +
" jobs:\n" +
" - release: some-release\n" +
"- name: some-other-server\n" +
" jobs:\n" +
" - release: bogus-release\n" +
"releases:\n" +
"- name: some-release\n" +
" url: https://release-hub.info/some-release.tar.gz?version=99.3.2\n" +
" sha1: asddsfsd\n" +
"- name: other-release\n" +
" url: https://release-hub.info/other-release.tar.gz?version=99.3.2\n" +
" sha1: asddsfsd\n"
);
editor.assertGotoDefinition(editor.positionOf("some-release"),
editor.rangeOf(
"releases:\n" +
"- name: some-release\n"
,
"some-release"
)
);
}
}