Hierchical document symbols for concourse pipeline
This commit is contained in:
@@ -144,4 +144,8 @@ public class NodeUtil {
|
||||
return new DocumentRegion(doc, node.getStartMark().getIndex(), node.getEndMark().getIndex());
|
||||
}
|
||||
|
||||
public static DocumentRegion region(IDocument doc, NodeTuple tup) {
|
||||
return new DocumentRegion(doc, tup.getKeyNode().getStartMark().getIndex(), tup.getValueNode().getEndMark().getIndex());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -310,4 +310,34 @@ public class YamlPath extends AbstractYamlTraversal {
|
||||
return isEmpty();
|
||||
}
|
||||
|
||||
public boolean startsWith(YamlPath prefix) {
|
||||
if (this.size()>=prefix.size()) {
|
||||
YamlPath chopped = this.dropLast(this.size() - prefix.size());
|
||||
return chopped.equals(prefix);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + Arrays.hashCode(segments);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
YamlPath other = (YamlPath) obj;
|
||||
if (!Arrays.equals(segments, other.segments))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -231,7 +231,6 @@ public abstract class YamlPathSegment extends AbstractYamlTraversal {
|
||||
protected char getTypeCode() {
|
||||
return '&';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2019 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.commons.yaml.reconcile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.eclipse.lsp4j.DocumentSymbol;
|
||||
import org.eclipse.lsp4j.DocumentSymbolParams;
|
||||
import org.eclipse.lsp4j.SymbolInformation;
|
||||
import org.eclipse.lsp4j.SymbolKind;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.HierarchicalDocumentSymbolHandler;
|
||||
import org.springframework.ide.vscode.commons.util.Assert;
|
||||
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.YamlFileAST;
|
||||
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.path.YamlPathSegment.YamlPathSegmentType;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YType;
|
||||
import org.yaml.snakeyaml.nodes.MappingNode;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
import org.yaml.snakeyaml.nodes.NodeTuple;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMap.Builder;
|
||||
|
||||
public class TypeBasedYamlHierarchicalSymbolHandler implements HierarchicalDocumentSymbolHandler, ITypeCollector {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(TypeBasedYamlHierarchicalSymbolHandler.class);
|
||||
|
||||
public static class HierarchicalDefType {
|
||||
/**
|
||||
* A yaml node of this type constitutes a definion. This should identify the 'whole' definition not just the
|
||||
* part of the node that contains the name of the defined entity.
|
||||
*/
|
||||
public final YType defType;
|
||||
|
||||
/**
|
||||
* A yaml optional path that points to the part of the node where the defined entity's name can be found.
|
||||
*/
|
||||
public final YamlPath namePath;
|
||||
|
||||
public final SymbolKind kind;
|
||||
|
||||
public final String detail;
|
||||
|
||||
public HierarchicalDefType(YType defType, YamlPath namePath, SymbolKind kind, String detail) {
|
||||
super();
|
||||
this.defType = defType;
|
||||
this.namePath = namePath;
|
||||
this.kind = kind;
|
||||
this.detail = detail;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HierarchicalDefType [defType=" + defType + ", namePath=" + namePath + "]";
|
||||
}
|
||||
public DocumentSymbol createSymbol(YamlFileAST currentAst, Node node, YType type, YamlPath path) {
|
||||
try {
|
||||
IDocument doc = currentAst.getDocument();
|
||||
if (namePath!=null) {
|
||||
Node nameNode = namePath.traverseNode(node);
|
||||
return new DocumentSymbol(NodeUtil.asScalar(nameNode), kind,
|
||||
NodeUtil.region(doc, node).asRange(),
|
||||
NodeUtil.region(doc, nameNode).asRange(),
|
||||
detail
|
||||
);
|
||||
} else {
|
||||
//If there's no 'namePath' then we will assume the node we found is the value of a map entry...
|
||||
//and use the map's key as the symbol's name
|
||||
MappingNode map = NodeUtil.asMapping(path.dropLast().traverseToNode(currentAst));
|
||||
if (map!=null) {
|
||||
YamlPathSegment segment = path.getLastSegment();
|
||||
if (segment.getType()==YamlPathSegmentType.VAL_AT_KEY) {
|
||||
String key = segment.toPropString();
|
||||
for (NodeTuple entry : map.getValue()) {
|
||||
if (key.equals(NodeUtil.asScalar(entry.getKeyNode()))) {
|
||||
return new DocumentSymbol(key, kind,
|
||||
NodeUtil.region(doc, entry).asRange(),
|
||||
NodeUtil.region(doc, entry.getKeyNode()).asRange(),
|
||||
detail
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Item {
|
||||
YamlPath path;
|
||||
DocumentSymbol symbol;
|
||||
Item(YamlPath path, DocumentSymbol symbol) {
|
||||
super();
|
||||
this.path = path;
|
||||
this.symbol = symbol;
|
||||
}
|
||||
public void addChild(DocumentSymbol sym) {
|
||||
if (symbol.getChildren()==null) {
|
||||
symbol.setChildren(new ArrayList<DocumentSymbol>());
|
||||
}
|
||||
symbol.getChildren().add(sym);
|
||||
}
|
||||
}
|
||||
|
||||
private TypeBasedYamlSymbolHandler baseHandler;
|
||||
Map<YType, HierarchicalDefType> hierarchicalDefinitionTypes;
|
||||
|
||||
Map<String, List<DocumentSymbol>> outlineByUri = new HashMap<>();
|
||||
private YamlFileAST currentAst;
|
||||
|
||||
private Stack<Item> stack = new Stack<>();
|
||||
private ImmutableList.Builder<DocumentSymbol> rootSymbols;
|
||||
|
||||
public TypeBasedYamlHierarchicalSymbolHandler(TypeBasedYamlSymbolHandler baseHandler,
|
||||
List<HierarchicalDefType> hierarchicalDefinitionTypes) {
|
||||
this.baseHandler = baseHandler;
|
||||
Builder<YType, HierarchicalDefType> builder = ImmutableMap.builder();
|
||||
for (HierarchicalDefType hdt : hierarchicalDefinitionTypes) {
|
||||
builder.put(hdt.defType, hdt);
|
||||
}
|
||||
this.hierarchicalDefinitionTypes = builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends SymbolInformation> handle(DocumentSymbolParams params) {
|
||||
return baseHandler.handle(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends DocumentSymbol> handleHierarchic(DocumentSymbolParams params) {
|
||||
return outlineByUri.get(params.getTextDocument().getUri());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginCollecting(YamlFileAST ast) {
|
||||
Assert.isNull("Session already active", currentAst);
|
||||
this.currentAst = ast;
|
||||
this.rootSymbols = ImmutableList.builder();
|
||||
this.stack = new Stack<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Node node, YType type, YamlPath path) {
|
||||
HierarchicalDefType def = hierarchicalDefinitionTypes.get(type);
|
||||
if (def!=null) {
|
||||
Item parent = findParent(path);
|
||||
DocumentSymbol sym = def.createSymbol(currentAst, node, type, path);
|
||||
if (parent!=null) {
|
||||
parent.addChild(sym);
|
||||
} else {
|
||||
rootSymbols.add(sym);
|
||||
}
|
||||
stack.push(new Item(path, sym));
|
||||
}
|
||||
}
|
||||
|
||||
private Item findParent(YamlPath path) {
|
||||
if (stack.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Item item = stack.peek();
|
||||
while (!path.startsWith(item.path)) {
|
||||
stack.pop();
|
||||
if (stack.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
item = stack.peek();
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endCollecting(YamlFileAST ast) {
|
||||
Assert.isLegal(this.currentAst == ast);
|
||||
String uri = currentAst.getDocument().getUri();
|
||||
this.outlineByUri.put(uri, rootSymbols.build());
|
||||
this.rootSymbols = null;
|
||||
this.currentAst = null;
|
||||
this.stack = null;
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,8 @@ import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
|
||||
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;
|
||||
|
||||
|
||||
@@ -13,10 +13,12 @@ package org.springframework.ide.vscode.concourse;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.HierarchicalDocumentSymbolHandler;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
||||
import org.springframework.ide.vscode.commons.util.LogRedirect;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.TypeBasedYamlHierarchicalSymbolHandler;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.TypeBasedYamlSymbolHandler;
|
||||
import org.springframework.ide.vscode.concourse.github.DefaultGithubInfoProvider;
|
||||
import org.springframework.ide.vscode.concourse.github.GithubInfoProvider;
|
||||
@@ -47,8 +49,9 @@ public class ConcourseLanguageServerBootApp {
|
||||
return new ASTTypeCache();
|
||||
}
|
||||
|
||||
@Bean TypeBasedYamlSymbolHandler documentSymbolHandler(SimpleTextDocumentService documents, ASTTypeCache astTypeCache, PipelineYmlSchema schema) {
|
||||
return new TypeBasedYamlSymbolHandler(documents, astTypeCache, schema.getDefinitionTypes());
|
||||
@Bean HierarchicalDocumentSymbolHandler documentSymbolHandler(SimpleTextDocumentService documents, ASTTypeCache astTypeCache, PipelineYmlSchema schema) {
|
||||
TypeBasedYamlSymbolHandler baseHandler = new TypeBasedYamlSymbolHandler(documents, astTypeCache, schema.getDefinitionTypes());
|
||||
return new TypeBasedYamlHierarchicalSymbolHandler(baseHandler, schema.getHierarchicalDefinitionTypes());
|
||||
}
|
||||
|
||||
@Bean PipelineYmlSchema pipelineYmlSchema(ConcourseModel models, GithubInfoProvider github) {
|
||||
|
||||
@@ -37,11 +37,11 @@ import org.springframework.ide.vscode.commons.yaml.quickfix.YamlQuickfixes;
|
||||
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.reconcile.TypeBasedYamlHierarchicalSymbolHandler.HierarchicalDefType;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YType;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema;
|
||||
import org.springframework.ide.vscode.commons.yaml.snippet.SchemaBasedSnippetGenerator;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureProvider;
|
||||
import org.springframework.ide.vscode.concourse.PipelineYmlSchema.HierarchicalDefType;
|
||||
import org.springframework.ide.vscode.concourse.github.GithubInfoProvider;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.lsp4j.SymbolKind;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.util.MimeTypes;
|
||||
import org.springframework.ide.vscode.commons.util.PartialCollection;
|
||||
@@ -32,6 +33,7 @@ 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.reconcile.YamlSchemaProblems;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaValueParsers;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.TypeBasedYamlHierarchicalSymbolHandler.HierarchicalDefType;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.BasicYValueHint;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YType;
|
||||
@@ -40,6 +42,7 @@ import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.AbstractT
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YAtomicType;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YBeanType;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YBeanUnionType;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YSeqType;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YTypedPropertyImpl;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty;
|
||||
@@ -61,30 +64,6 @@ import com.google.common.collect.ImmutableList;
|
||||
*/
|
||||
public class PipelineYmlSchema implements YamlSchema {
|
||||
|
||||
public static class HierarchicalDefType {
|
||||
/**
|
||||
* A yaml node of this type constitutes a definion. This should identify the 'whole' definition not just the
|
||||
* part of the node that contains the name of the defined entity.
|
||||
*/
|
||||
public final YType defType;
|
||||
|
||||
/**
|
||||
* A yaml path that points to the part of the node where the defined entity's name can be found.
|
||||
*/
|
||||
public final YamlPath nameNode;
|
||||
|
||||
public HierarchicalDefType(YType defType, YamlPath nameNode) {
|
||||
super();
|
||||
this.defType = defType;
|
||||
this.nameNode = nameNode;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HierarchicalDefType [defType=" + defType + ", nameNode=" + nameNode + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//TODO: the infos for composing this should probably be integrated somehow in the ResourceTypeRegistry so
|
||||
// we only have a list of built-in resource types in a single place.
|
||||
public static final YValueHint[] BUILT_IN_RESOURCE_TYPES = {
|
||||
@@ -189,6 +168,7 @@ public class PipelineYmlSchema implements YamlSchema {
|
||||
);
|
||||
|
||||
private List<YType> definitionTypes = new ArrayList<>();
|
||||
private List<HierarchicalDefType> hierarchicDefinitions;
|
||||
|
||||
private GithubInfoProvider github;
|
||||
|
||||
@@ -436,10 +416,14 @@ public class PipelineYmlSchema implements YamlSchema {
|
||||
addProp(group, "resources", f.yseq(t_resource_name));
|
||||
addProp(group, "jobs", f.yseq(t_job_name));
|
||||
|
||||
addProp(TOPLEVEL_TYPE, "resources", f.yseq(t_resource));
|
||||
addProp(TOPLEVEL_TYPE, "jobs", f.yseq(job));
|
||||
addProp(TOPLEVEL_TYPE, "resource_types", f.yseq(resourceType));
|
||||
addProp(TOPLEVEL_TYPE, "groups", f.yseq(group).require(models::jobAssignmentIsComplete));
|
||||
YSeqType t_resources = f.yseq(t_resource);
|
||||
YSeqType t_jobs = f.yseq(job);
|
||||
YSeqType t_resourceTypes = f.yseq(resourceType);
|
||||
AbstractType t_groups = f.yseq(group).require(models::jobAssignmentIsComplete);
|
||||
addProp(TOPLEVEL_TYPE, "resources", t_resources);
|
||||
addProp(TOPLEVEL_TYPE, "jobs", t_jobs);
|
||||
addProp(TOPLEVEL_TYPE, "resource_types", t_resourceTypes);
|
||||
addProp(TOPLEVEL_TYPE, "groups", t_groups);
|
||||
|
||||
definitionTypes = ImmutableList.of(
|
||||
jobNameDef,
|
||||
@@ -447,6 +431,19 @@ public class PipelineYmlSchema implements YamlSchema {
|
||||
t_resource_name_def,
|
||||
t_group_name_def
|
||||
);
|
||||
hierarchicDefinitions = ImmutableList.of(
|
||||
new HierarchicalDefType(t_resources, null, SymbolKind.File, "Resources"),
|
||||
new HierarchicalDefType(t_resource, YamlPath.fromSimpleProperty("name"), SymbolKind.File, "Resource"),
|
||||
|
||||
new HierarchicalDefType(t_jobs, null, SymbolKind.Method, "Jobs"),
|
||||
new HierarchicalDefType(job, YamlPath.fromSimpleProperty("name"), SymbolKind.Method, "Job"),
|
||||
|
||||
new HierarchicalDefType(t_resourceTypes, null, SymbolKind.Interface, "Resource Types"),
|
||||
new HierarchicalDefType(resourceType, YamlPath.fromSimpleProperty("name"), SymbolKind.Interface, "Resource Type"),
|
||||
|
||||
new HierarchicalDefType(t_groups, null, SymbolKind.Package, "Groups"),
|
||||
new HierarchicalDefType(group, YamlPath.fromSimpleProperty("name"), SymbolKind.Package, "Groups")
|
||||
);
|
||||
|
||||
initializeDefaultResourceTypes();
|
||||
}
|
||||
@@ -833,8 +830,7 @@ public class PipelineYmlSchema implements YamlSchema {
|
||||
}
|
||||
|
||||
public List<HierarchicalDefType> getHierarchicalDefinitionTypes() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return hierarchicDefinitions;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user