Implement 'goto symbol' within pipeline.yml files.

This commit is contained in:
Kris De Volder
2017-04-13 12:22:49 -07:00
parent d3b244d5de
commit 3ccb392e6f
7 changed files with 191 additions and 10 deletions

View File

@@ -0,0 +1,28 @@
/*******************************************************************************
* 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.commons.languageserver.util;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.SymbolInformation;
import com.google.common.collect.ImmutableList;
@FunctionalInterface
public interface DocumentSymbolHandler {
DocumentSymbolHandler NO_SYMBOLS = (params) -> CompletableFuture.completedFuture(ImmutableList.of());
CompletableFuture<List<? extends SymbolInformation>> handle(DocumentSymbolParams params);
}

View File

@@ -150,18 +150,23 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl
if (hasQuickFixes()) {
c.setCodeActionProvider(true);
}
if (hasDefinitionHandler()) {
c.setDefinitionProvider(true);
}
if (hasReferencesHandler()) {
c.setReferencesProvider(true);
}
if (hasDocumentSymbolHandler()) {
c.setDocumentSymbolProvider(true);
}
return c;
}
private boolean hasDocumentSymbolHandler() {
return getTextDocumentService().hasDocumentSymbolHandler();
}
private boolean hasReferencesHandler() {
return getTextDocumentService().hasReferencesHandler();
}

View File

@@ -79,6 +79,8 @@ public class SimpleTextDocumentService implements TextDocumentService {
private DefinitionHandler definitionHandler;
private ReferencesHandler referencesHandler;
private DocumentSymbolHandler documentSymbolHandler;
public SimpleTextDocumentService(SimpleLanguageServer server) {
this.server = server;
}
@@ -88,6 +90,11 @@ public class SimpleTextDocumentService implements TextDocumentService {
this.hoverHandler = h;
}
public synchronized void onDocumentSymbol(DocumentSymbolHandler h) {
Assert.isNull("A DocumentSymbolHandler is already set, multiple handlers not supported yet", documentSymbolHandler);
this.documentSymbolHandler = h;
}
public synchronized void onCompletion(CompletionHandler h) {
Assert.isNull("A completion handler is already set, multiple handlers not supported yet", completionHandler);
this.completionHandler = h;
@@ -273,6 +280,9 @@ public class SimpleTextDocumentService implements TextDocumentService {
@Override
public CompletableFuture<List<? extends SymbolInformation>> documentSymbol(DocumentSymbolParams params) {
if (documentSymbolHandler!=null) {
return documentSymbolHandler.handle(params);
}
return CompletableFuture.completedFuture(Collections.emptyList());
}
@@ -360,4 +370,8 @@ public class SimpleTextDocumentService implements TextDocumentService {
return this.referencesHandler!=null;
}
public boolean hasDocumentSymbolHandler() {
return this.documentSymbolHandler!=null;
}
}

View File

@@ -53,7 +53,7 @@ public class ASTTypeCache implements ITypeCollector {
}
@Override
public void endCollecting(YamlFileAST ast) {
public synchronized void endCollecting(YamlFileAST ast) {
Assert.isLegal(currentAst==ast);
String uri = ast.getDocument().getUri();
typeIndex.put(uri, currentTypes.build());
@@ -68,7 +68,7 @@ public class ASTTypeCache implements ITypeCollector {
}
}
public YType getType(YamlFileAST ast, Node node) {
public synchronized YType getType(YamlFileAST ast, Node node) {
ImmutableMap<Node, YType> types = typeIndex.get(ast.getDocument().getUri());
if (types!=null) {
return types.get(node);
@@ -84,5 +84,9 @@ public class ASTTypeCache implements ITypeCollector {
this.interestingTypes.add(type);
}
public synchronized ImmutableMap<Node, YType> getNodes(String uri) {
return typeIndex.get(uri);
}
}

View File

@@ -0,0 +1,96 @@
/*******************************************************************************
* 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.concourse;
import java.util.Collection;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.SymbolKind;
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
import org.springframework.ide.vscode.commons.languageserver.util.DocumentSymbolHandler;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
import org.springframework.ide.vscode.commons.util.Assert;
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.schema.YType;
import org.yaml.snakeyaml.nodes.Node;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableSet;
/**
* Finds symbols in a concourse document. This relies on type information cached
* during reconcile and stored in the {@link ConcourseModel}. Therefore,
* this handler only works if invoked after a reconcile.
*
* @author Kris De Volder
*/
public class ConcourseDocumentSymbolHandler implements DocumentSymbolHandler {
private ASTTypeCache astTypeCache;
private Set<YType> definitionTypes;
private SimpleTextDocumentService documents;
public ConcourseDocumentSymbolHandler(SimpleTextDocumentService documents, ASTTypeCache astTypeCache, Collection<YType> definitionTypes) {
Assert.isTrue(!definitionTypes.isEmpty()); // If there's no interesting types then you are better of using DocumentSymbolHandler.NO_SYMBOLS
this.documents = documents;
this.astTypeCache = astTypeCache;
this.definitionTypes = ImmutableSet.copyOf(definitionTypes);
for (YType yType : definitionTypes) {
astTypeCache.addInterestingType(yType);
}
}
@Override
public CompletableFuture<List<? extends SymbolInformation>> handle(DocumentSymbolParams params) {
Builder<SymbolInformation> builder = ImmutableList.builder();
TextDocument doc = documents.getDocument(params.getTextDocument().getUri());
for (Entry<Node, YType> entry : astTypeCache.getNodes(params.getTextDocument().getUri()).entrySet()) {
if (definitionTypes.contains(entry.getValue())) {
try {
builder.add(createSymbol(doc, entry.getKey(), entry.getValue()));
} catch (Exception e) {
Log.log(e);
}
}
}
return CompletableFuture.completedFuture(builder.build());
}
protected SymbolInformation createSymbol(TextDocument doc, Node node, YType type) throws BadLocationException {
DocumentRegion region = NodeUtil.region(doc, node);
Location location = new Location(doc.getUri(), doc.toRange(region.getStart(), region.getLength()));
SymbolInformation symbol = new SymbolInformation();
symbol.setName(region.toString());
symbol.setKind(symbolKind(type));
symbol.setLocation(location);
symbol.setContainerName(containerName(type));
return symbol;
}
protected String containerName(YType type) {
return type.toString().replaceAll("(\\s)*[Nn]ame", "");
}
protected SymbolKind symbolKind(YType type) {
return SymbolKind.String; //TODO: try to return something different for different types of symbols
}
}

View File

@@ -10,13 +10,11 @@
*******************************************************************************/
package org.springframework.ide.vscode.concourse;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.CompletionList;
import org.eclipse.lsp4j.CompletionOptions;
import org.eclipse.lsp4j.DiagnosticSeverity;
import org.eclipse.lsp4j.ServerCapabilities;
import org.eclipse.lsp4j.TextDocumentSyncKind;
import org.springframework.ide.vscode.commons.languageserver.LanguageIds;
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfoProvider;
@@ -24,8 +22,10 @@ import org.springframework.ide.vscode.commons.languageserver.hover.VscodeHoverEn
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem;
import org.springframework.ide.vscode.commons.languageserver.util.DocumentSymbolHandler;
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.CollectionUtil;
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.completion.SchemaBasedYamlAssistContextProvider;
@@ -34,6 +34,7 @@ import org.springframework.ide.vscode.commons.yaml.hover.YamlHoverInfoProvider;
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlQuickfixes;
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaBasedReconcileEngine;
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
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.structure.YamlStructureProvider;
@@ -54,8 +55,9 @@ public class ConcourseLanguageServer extends SimpleLanguageServer {
final VscodeCompletionEngineAdapter completionEngine;
final VscodeHoverEngineAdapter hoverEngine;
final YamlSchemaBasedReconcileEngine reconcileEngine;
final DocumentSymbolHandler symbolHandler;
SchemaSpecificPieces(YamlSchema schema) {
SchemaSpecificPieces(YamlSchema schema, List<YType> definitionTypes) {
SchemaBasedYamlAssistContextProvider contextProvider = new SchemaBasedYamlAssistContextProvider(schema);
YamlCompletionEngine yamlCompletionEngine = new YamlCompletionEngine(structureProvider, contextProvider);
this.completionEngine = new VscodeCompletionEngineAdapter(ConcourseLanguageServer.this, yamlCompletionEngine);
@@ -65,6 +67,10 @@ public class ConcourseLanguageServer extends SimpleLanguageServer {
this.reconcileEngine = new YamlSchemaBasedReconcileEngine(currentAsts, schema, yamlQuickfixes);
reconcileEngine.setTypeCollector(models.getAstTypeCache());
this.symbolHandler = CollectionUtil.hasElements(definitionTypes)
? new ConcourseDocumentSymbolHandler(documents, models.getAstTypeCache(), definitionTypes)
: DocumentSymbolHandler.NO_SYMBOLS;
}
public void setMaxCompletions(int max) {
@@ -77,8 +83,8 @@ public class ConcourseLanguageServer extends SimpleLanguageServer {
PipelineYmlSchema pipelineSchema = new PipelineYmlSchema(models);
this.yamlQuickfixes = new YamlQuickfixes(getQuickfixRegistry(), documents, structureProvider);
this.forPipelines = new SchemaSpecificPieces(pipelineSchema);
this.forTasks = new SchemaSpecificPieces(pipelineSchema.getTaskSchema());
this.forPipelines = new SchemaSpecificPieces(pipelineSchema, pipelineSchema.getDefinitionTypes());
this.forTasks = new SchemaSpecificPieces(pipelineSchema.getTaskSchema(), null);
ConcourseDefinitionFinder definitionFinder = new ConcourseDefinitionFinder(this, models, pipelineSchema);
// SimpleWorkspaceService workspace = getWorkspaceService();
@@ -132,6 +138,18 @@ public class ConcourseLanguageServer extends SimpleLanguageServer {
return SimpleTextDocumentService.NO_HOVER;
});
documents.onDefinition(definitionFinder);
documents.onDocumentSymbol((params) -> {
DocumentSymbolHandler handler = DocumentSymbolHandler.NO_SYMBOLS;
TextDocument doc = documents.getDocument(params.getTextDocument().getUri());
if (doc!=null) {
if (LanguageIds.CONCOURSE_PIPELINE.equals(doc.getLanguageId())) {
handler = forPipelines.symbolHandler;
} else if (LanguageIds.CONCOURSE_TASK.equals(doc.getLanguageId())) {
handler = forTasks.symbolHandler;
}
}
return handler.handle(params);
});
}
@Override

View File

@@ -11,6 +11,8 @@
package org.springframework.ide.vscode.concourse;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@@ -47,6 +49,8 @@ import org.springframework.ide.vscode.concourse.ConcourseModel.StepModel;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import com.google.common.collect.ImmutableList;
import reactor.core.publisher.Flux;
/**
@@ -155,6 +159,8 @@ public class PipelineYmlSchema implements YamlSchema {
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
);
private List<YType> definitionTypes = new ArrayList<>();
public PipelineYmlSchema(ConcourseModel models) {
this.models = models;
TYPE_UTIL = f.TYPE_UTIL;
@@ -366,6 +372,12 @@ public class PipelineYmlSchema implements YamlSchema {
addProp(TOPLEVEL_TYPE, "resource_types", f.yseq(resourceType));
addProp(TOPLEVEL_TYPE, "groups", f.yseq(group));
definitionTypes = ImmutableList.of(
jobNameDef,
resourceTypeNameDef,
resourceNameDef
);
initializeDefaultResourceTypes();
}
@@ -700,4 +712,8 @@ public class PipelineYmlSchema implements YamlSchema {
}
};
}
public List<YType> getDefinitionTypes() {
return definitionTypes;
}
}