diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/HierarchicalDocumentSymbolHandler.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/HierarchicalDocumentSymbolHandler.java new file mode 100644 index 000000000..cd942e305 --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/HierarchicalDocumentSymbolHandler.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * 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.languageserver.util; + +import java.util.List; + +import org.eclipse.lsp4j.DocumentSymbol; +import org.eclipse.lsp4j.DocumentSymbolParams; +import org.eclipse.lsp4j.SymbolInformation; + +import com.google.common.collect.ImmutableList; + +/** + * Note if you implement HierarchicalDocumentSymbolHandler you must also implement the 'legacy' + * non-hierarchical handler because this is used as a fallback when client doesn't support + * hierarchical symbols. + */ +public interface HierarchicalDocumentSymbolHandler extends DocumentSymbolHandler { + + HierarchicalDocumentSymbolHandler NO_SYMBOLS = new HierarchicalDocumentSymbolHandler() { + @Override + public List handle(DocumentSymbolParams params) { + return ImmutableList.of(); + } + + @Override + public List handleHierarchic(DocumentSymbolParams params) { + return ImmutableList.of(); + } + }; + + List handleHierarchic(DocumentSymbolParams params); + +} diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java index 910a1dca8..2e05caaf6 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java @@ -425,9 +425,6 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC public final boolean hasLazyCompletionResolver() { return completionResolver!=null; } - public boolean hasHierarchicalDocumentSymbolSupport() { - return hasHierarchicalDocumentSymbolSupport; - } private boolean hasDocumentSymbolHandler() { return getTextDocumentService().hasDocumentSymbolHandler(); @@ -713,4 +710,7 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC this.completionTriggerCharacters = completionTriggerCharacters; } + public boolean hasHierarchicalDocumentSymbolSupport() { + return hasHierarchicalDocumentSymbolSupport; + } } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java index cca028f38..f80aa4375 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java @@ -84,6 +84,7 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE private HoverHandler hoverHandler = null; private DefinitionHandler definitionHandler; private ReferencesHandler referencesHandler; + private DocumentSymbolHandler documentSymbolHandler; private DocumentHighlightHandler documentHighlightHandler; @@ -93,6 +94,7 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE private List> documentSaveListeners = ImmutableList.of(); private AsyncRunner async; + public SimpleTextDocumentService(SimpleLanguageServer server) { this.server = server; this.async = server.getAsync(); @@ -349,18 +351,28 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE @Override public CompletableFuture>> documentSymbol(DocumentSymbolParams params) { - return async.invoke(() -> { - DocumentSymbolHandler documentSymbolHandler = this.documentSymbolHandler; - if (documentSymbolHandler==null) { + return async.invoke(() -> { + DocumentSymbolHandler h = this.documentSymbolHandler; + if (h!=null) { + server.waitForReconcile(); + if (server.hasHierarchicalDocumentSymbolSupport() && h instanceof HierarchicalDocumentSymbolHandler) { + List r = ((HierarchicalDocumentSymbolHandler)h).handleHierarchic(params); + //handle it when symbolHandler is sloppy and returns null instead of empty list. + return r == null + ? ImmutableList.of() + : r.stream().map(symbolInfo -> Either.forRight(symbolInfo)) + .collect(Collectors.toList()); + } else { + List r = h.handle(params); + //handle it when symbolHandler is sloppy and returns null instead of empty list. + return r == null + ? ImmutableList.of() + : r.stream().map(symbolInfo -> Either.forLeft(symbolInfo)) + .collect(Collectors.toList()); + } + } return ImmutableList.of(); - } - server.waitForReconcile(); - List r = documentSymbolHandler.handle(params); - //handle it when symbolHandler is sloppy and returns null instead of empty list. - return r == null ? ImmutableList.of() - : r.stream().map(symbolInfo -> Either.forLeft(symbolInfo)) - .collect(Collectors.toList()); - }); + }); } @Override diff --git a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/ASTTypeCache.java b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/ASTTypeCache.java index e7dc85c16..569de2900 100644 --- a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/ASTTypeCache.java +++ b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/ASTTypeCache.java @@ -22,6 +22,7 @@ import org.springframework.ide.vscode.commons.util.StringUtil; 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.schema.DynamicSchemaContext; import org.springframework.ide.vscode.commons.yaml.schema.YType; import org.yaml.snakeyaml.nodes.Node; @@ -52,10 +53,10 @@ public class ASTTypeCache implements ITypeCollector { */ private static class NodeTypesImpl implements NodeTypes { - private ImmutableMap node2type; + private final ImmutableMap node2type; private Multimap type2node = null; //lazy initialized when used. - public NodeTypesImpl(ImmutableMap node2type) { + public NodeTypesImpl(YamlFileAST ast, ImmutableMap node2type) { this.node2type = node2type; } @@ -82,6 +83,8 @@ public class ASTTypeCache implements ITypeCollector { */ private YamlFileAST currentAst = null; + public ASTTypeCache() {} + /** * Collects types for the current session. */ @@ -101,14 +104,15 @@ public class ASTTypeCache implements ITypeCollector { public synchronized void endCollecting(YamlFileAST ast) { Assert.isLegal(currentAst==ast); String uri = ast.getDocument().getUri(); - typeIndex.put(uri, new NodeTypesImpl(currentTypes.build())); + typeIndex.put(uri, new NodeTypesImpl(currentAst, currentTypes.build())); this.currentAst = null; this.currentTypes = null; } @Override - public void accept(Node node, YType type) { + public void accept(Node node, YType type, YamlPath path) { if (interestingTypes.contains(type)) { + System.out.println(path.toPropString() + " = " +NodeUtil.asScalar(node) +" :: "+type); currentTypes.put(node, type); } } diff --git a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/ITypeCollector.java b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/ITypeCollector.java index ec3e1301b..fdf11f9d3 100644 --- a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/ITypeCollector.java +++ b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/ITypeCollector.java @@ -11,6 +11,7 @@ package org.springframework.ide.vscode.commons.yaml.reconcile; 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; @@ -23,6 +24,6 @@ import org.yaml.snakeyaml.nodes.Node; */ public interface ITypeCollector { void beginCollecting(YamlFileAST ast); - void accept(Node node, YType type); + void accept(Node node, YType type, YamlPath path); void endCollecting(YamlFileAST ast); } diff --git a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/SchemaBasedYamlASTReconciler.java b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/SchemaBasedYamlASTReconciler.java index bd95a4ccb..77058f2a2 100644 --- a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/SchemaBasedYamlASTReconciler.java +++ b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/SchemaBasedYamlASTReconciler.java @@ -145,7 +145,7 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler { DynamicSchemaContext schemaContext = new ASTDynamicSchemaContext(ast, path, node); YType type = typeUtil.inferMoreSpecificType(_type, schemaContext); if (typeCollector!=null) { - typeCollector.accept(node, type); + typeCollector.accept(node, type, path); } checkConstraints(parent, node, type, schemaContext); switch (getNodeId(node)) { diff --git a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/TypeBasedYamlSymbolHandler.java b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/TypeBasedYamlSymbolHandler.java index 9622ddfb5..c5622258f 100644 --- a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/TypeBasedYamlSymbolHandler.java +++ b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/TypeBasedYamlSymbolHandler.java @@ -15,9 +15,6 @@ import java.util.List; import java.util.Map.Entry; import java.util.Set; -import javax.inject.Provider; - -import org.eclipse.lsp4j.DocumentSymbol; import org.eclipse.lsp4j.DocumentSymbolParams; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.SymbolInformation; @@ -28,14 +25,12 @@ import org.springframework.ide.vscode.commons.languageserver.util.DocumentSymbol 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.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.schema.YType; import org.yaml.snakeyaml.nodes.Node; -import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; import com.google.common.collect.ImmutableSet; @@ -55,14 +50,12 @@ public class TypeBasedYamlSymbolHandler implements DocumentSymbolHandler { private ASTTypeCache astTypeCache; private Set definitionTypes; private SimpleTextDocumentService documents; - private Supplier hiearchicalSymbolSupport; - public TypeBasedYamlSymbolHandler(SimpleTextDocumentService documents, ASTTypeCache astTypeCache, Collection definitionTypes, Supplier hasHierarchicalSymbolSupport) { + public TypeBasedYamlSymbolHandler(SimpleTextDocumentService documents, ASTTypeCache astTypeCache, Collection 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); - this.hiearchicalSymbolSupport = hasHierarchicalSymbolSupport; for (YType yType : definitionTypes) { astTypeCache.addInterestingType(yType); } diff --git a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/YamlSchemaBasedReconcileEngine.java b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/YamlSchemaBasedReconcileEngine.java index 32a96712b..1b13f500b 100644 --- a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/YamlSchemaBasedReconcileEngine.java +++ b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/YamlSchemaBasedReconcileEngine.java @@ -11,12 +11,22 @@ package org.springframework.ide.vscode.commons.yaml.reconcile; +import java.util.Collection; + +import org.springframework.context.ApplicationContext; import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector; import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem; +import org.springframework.ide.vscode.commons.util.CollectionUtil; import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.yaml.ast.YamlASTProvider; +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.quickfix.YamlQuickfixes; +import org.springframework.ide.vscode.commons.yaml.schema.YType; import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema; +import org.yaml.snakeyaml.nodes.Node; + +import com.google.common.collect.ImmutableList; /** * @author Kris De Volder @@ -24,18 +34,15 @@ import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema; public final class YamlSchemaBasedReconcileEngine extends YamlReconcileEngine { private final YamlSchema schema; - /** - * An optional type collector can be added. It will notified about all the types - * the reconciler infers when reconciling an AST. - */ - private ITypeCollector typeCollector; - private YamlQuickfixes quickfixes; - public YamlSchemaBasedReconcileEngine(YamlASTProvider parser, YamlSchema schema, YamlQuickfixes quickfixes) { + private ApplicationContext appContext; + + public YamlSchemaBasedReconcileEngine(YamlASTProvider parser, YamlSchema schema, YamlQuickfixes quickfixes, ApplicationContext appContext) { super(parser); this.schema = schema; this.quickfixes = quickfixes; + this.appContext = appContext; } @Override @@ -45,14 +52,34 @@ public final class YamlSchemaBasedReconcileEngine extends YamlReconcileEngine { @Override protected YamlASTReconciler getASTReconciler(IDocument doc, IProblemCollector problems) { + Collection typeCollectors = appContext.getBeansOfType(ITypeCollector.class).values(); + ITypeCollector typeCollector = null; + if (CollectionUtil.hasElements(typeCollectors)) { + typeCollector = new ITypeCollector() { + + @Override + public void endCollecting(YamlFileAST ast) { + for (ITypeCollector c : typeCollectors) { + c.endCollecting(ast); + } + } + + @Override + public void beginCollecting(YamlFileAST ast) { + for (ITypeCollector c : typeCollectors) { + c.beginCollecting(ast); + } + } + + @Override + public void accept(Node node, YType type, YamlPath path) { + for (ITypeCollector c : typeCollectors) { + c.accept(node, type, path); + } + } + }; + } return new SchemaBasedYamlASTReconciler(problems, schema, typeCollector, quickfixes); } - public ITypeCollector getTypeCollector() { - return typeCollector; - } - - public void setTypeCollector(ITypeCollector typeCollector) { - this.typeCollector = typeCollector; - } } \ No newline at end of file diff --git a/headless-services/commons/language-server-starter/src/main/java/org/springframework/ide/vscode/languageserver/starter/LanguageServerAutoConf.java b/headless-services/commons/language-server-starter/src/main/java/org/springframework/ide/vscode/languageserver/starter/LanguageServerAutoConf.java index 5c7df8496..ac87b12ab 100644 --- a/headless-services/commons/language-server-starter/src/main/java/org/springframework/ide/vscode/languageserver/starter/LanguageServerAutoConf.java +++ b/headless-services/commons/language-server-starter/src/main/java/org/springframework/ide/vscode/languageserver/starter/LanguageServerAutoConf.java @@ -26,11 +26,12 @@ import org.springframework.ide.vscode.commons.languageserver.config.LanguageServ import org.springframework.ide.vscode.commons.languageserver.config.LanguageServerProperties; import org.springframework.ide.vscode.commons.languageserver.reconcile.DiagnosticSeverityProvider; import org.springframework.ide.vscode.commons.languageserver.util.DefinitionHandler; +import org.springframework.ide.vscode.commons.languageserver.util.DocumentSymbolHandler; import org.springframework.ide.vscode.commons.languageserver.util.LanguageSpecific; import org.springframework.ide.vscode.commons.languageserver.util.LspClient; +import org.springframework.ide.vscode.commons.languageserver.util.LspClient.Client; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; -import org.springframework.ide.vscode.commons.languageserver.util.LspClient.Client; import org.springframework.ide.vscode.commons.util.text.LanguageId; import org.springframework.ide.vscode.commons.util.text.TextDocument; import org.springframework.util.Assert; @@ -100,4 +101,12 @@ public class LanguageServerAutoConf { } } + @ConditionalOnBean(DocumentSymbolHandler.class) + @Bean + InitializingBean registerDocumentSymbolHandler(SimpleTextDocumentService documents, DocumentSymbolHandler handler) { + return () -> { + documents.onDocumentSymbol(handler); + }; + } + } diff --git a/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseDefinitionFinder.java b/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseDefinitionFinder.java index 28c5072df..2793f8624 100644 --- a/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseDefinitionFinder.java +++ b/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseDefinitionFinder.java @@ -44,9 +44,9 @@ public class ConcourseDefinitionFinder extends SimpleDefinitionFinder handlers = new HashMap<>(); private final YamlAstCache asts; - public ConcourseDefinitionFinder(SimpleLanguageServer server, ConcourseModel models, PipelineYmlSchema schema) { + public ConcourseDefinitionFinder(SimpleLanguageServer server, ConcourseModel models, PipelineYmlSchema schema, ASTTypeCache astTypeCache) { super(server); - this.astTypes = models.getAstTypeCache(); + this.astTypes = astTypeCache; this.asts = models.getAstCache(); findByPath(schema.t_resource_name, ConcourseModel.RESOURCE_NAMES_PATH); findByPath(schema.t_maybe_resource_name, ConcourseModel.RESOURCE_NAMES_PATH); diff --git a/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseLanguageServerBootApp.java b/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseLanguageServerBootApp.java index 6f1d0fca5..d3b15d8b2 100644 --- a/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseLanguageServerBootApp.java +++ b/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseLanguageServerBootApp.java @@ -13,7 +13,11 @@ 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.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.TypeBasedYamlSymbolHandler; import org.springframework.ide.vscode.concourse.github.DefaultGithubInfoProvider; import org.springframework.ide.vscode.concourse.github.GithubInfoProvider; @@ -34,4 +38,20 @@ public class ConcourseLanguageServerBootApp { @Bean GithubInfoProvider github() { return new DefaultGithubInfoProvider(); } + + @Bean ConcourseModel concourseModel(SimpleLanguageServer server, ASTTypeCache astTypeCache) { + return new ConcourseModel(server, astTypeCache); + } + + @Bean ASTTypeCache astTypeCache() { + return new ASTTypeCache(); + } + + @Bean TypeBasedYamlSymbolHandler documentSymbolHandler(SimpleTextDocumentService documents, ASTTypeCache astTypeCache, PipelineYmlSchema schema) { + return new TypeBasedYamlSymbolHandler(documents, astTypeCache, schema.getDefinitionTypes()); + } + + @Bean PipelineYmlSchema pipelineYmlSchema(ConcourseModel models, GithubInfoProvider github) { + return new PipelineYmlSchema(models, github); + } } diff --git a/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseLanguageServerInitializer.java b/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseLanguageServerInitializer.java index ad29d8b29..1c509dc1c 100644 --- a/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseLanguageServerInitializer.java +++ b/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseLanguageServerInitializer.java @@ -12,9 +12,12 @@ package org.springframework.ide.vscode.concourse; import java.util.List; +import javax.annotation.PostConstruct; + import org.eclipse.lsp4j.CompletionList; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter; import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfoProvider; import org.springframework.ide.vscode.commons.languageserver.hover.VscodeHoverEngineAdapter; @@ -31,12 +34,14 @@ import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngi import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngineOptions; import org.springframework.ide.vscode.commons.yaml.hover.YamlHoverInfoProvider; 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.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; @@ -45,12 +50,11 @@ import com.google.common.collect.ImmutableList; import reactor.core.publisher.Mono; @Component -public class ConcourseLanguageServerInitializer implements InitializingBean { +public class ConcourseLanguageServerInitializer { private final YamlCompletionEngineOptions COMPLETION_OPTIONS = YamlCompletionEngineOptions.DEFAULT; private final YamlStructureProvider structureProvider = YamlStructureProvider.DEFAULT; - private ConcourseModel models; private SchemaSpecificPieces forPipelines; private SchemaSpecificPieces forTasks; @@ -59,15 +63,19 @@ public class ConcourseLanguageServerInitializer implements InitializingBean { @Autowired private SimpleLanguageServer server; @Autowired private GithubInfoProvider github; + @Autowired private ASTTypeCache astTypeCache; + @Autowired private ApplicationContext appContext; + @Autowired private ConcourseModel models; + @Autowired private PipelineYmlSchema pipelineSchema; private class SchemaSpecificPieces { final VscodeCompletionEngineAdapter completionEngine; final VscodeHoverEngineAdapter hoverEngine; final YamlSchemaBasedReconcileEngine reconcileEngine; - final DocumentSymbolHandler symbolHandler; +// final DocumentSymbolHandler symbolHandler; - SchemaSpecificPieces(YamlSchema schema, List definitionTypes) { + SchemaSpecificPieces(YamlSchema schema, List definitionTypes, List hierarchicalDefinitions) { SchemaBasedYamlAssistContextProvider contextProvider = new SchemaBasedYamlAssistContextProvider(schema); YamlCompletionEngine yamlCompletionEngine = new YamlCompletionEngine(structureProvider, contextProvider, COMPLETION_OPTIONS); this.completionEngine = server.createCompletionEngineAdapter(yamlCompletionEngine); @@ -75,12 +83,11 @@ public class ConcourseLanguageServerInitializer implements InitializingBean { HoverInfoProvider infoProvider = new YamlHoverInfoProvider(currentAsts, structureProvider, contextProvider); this.hoverEngine = new VscodeHoverEngineAdapter(server, infoProvider); - this.reconcileEngine = new YamlSchemaBasedReconcileEngine(currentAsts, schema, yamlQuickfixes); - reconcileEngine.setTypeCollector(models.getAstTypeCache()); + this.reconcileEngine = new YamlSchemaBasedReconcileEngine(currentAsts, schema, yamlQuickfixes, appContext); - this.symbolHandler = CollectionUtil.hasElements(definitionTypes) - ? new TypeBasedYamlSymbolHandler(server.getTextDocumentService(), models.getAstTypeCache(), definitionTypes, server::hasHierarchicalDocumentSymbolSupport) - : DocumentSymbolHandler.NO_SYMBOLS; +// this.symbolHandler = CollectionUtil.hasElements(definitionTypes) +// ? new TypeBasedYamlSymbolHandler(server.getTextDocumentService(), astTypeCache, definitionTypes) +// : DocumentSymbolHandler.NO_SYMBOLS; } public void setMaxCompletions(int max) { @@ -89,6 +96,7 @@ public class ConcourseLanguageServerInitializer implements InitializingBean { } public void enableSnippets(PipelineYmlSchema schema, boolean enable) { + //TODO: move to where schema bean is defined? if (enable) { schema.f.setSnippetProvider(new SchemaBasedSnippetGenerator(schema.getTypeUtil(), server::createSnippetBuilder)); } else { @@ -96,18 +104,16 @@ public class ConcourseLanguageServerInitializer implements InitializingBean { } } - @Override + @PostConstruct public void afterPropertiesSet() throws Exception { - this.models = new ConcourseModel(server); this.currentAsts = models.getAstCache().getAstProvider(false); - PipelineYmlSchema pipelineSchema = new PipelineYmlSchema(models, github); enableSnippets(pipelineSchema, true); SimpleTextDocumentService documents = server.getTextDocumentService(); this.yamlQuickfixes = new YamlQuickfixes(server.getQuickfixRegistry(), documents, structureProvider); - this.forPipelines = new SchemaSpecificPieces(pipelineSchema, pipelineSchema.getDefinitionTypes()); - this.forTasks = new SchemaSpecificPieces(pipelineSchema.getTaskSchema(), null); - ConcourseDefinitionFinder definitionFinder = new ConcourseDefinitionFinder(server, models, pipelineSchema); + this.forPipelines = new SchemaSpecificPieces(pipelineSchema, pipelineSchema.getDefinitionTypes(), pipelineSchema.getHierarchicalDefinitionTypes()); + this.forTasks = new SchemaSpecificPieces(pipelineSchema.getTaskSchema(), null, null); + ConcourseDefinitionFinder definitionFinder = new ConcourseDefinitionFinder(server, models, pipelineSchema, astTypeCache); // SimpleWorkspaceService workspace = getWorkspaceService(); documents.onDidChangeContent(params -> { @@ -159,18 +165,18 @@ public class ConcourseLanguageServerInitializer implements InitializingBean { 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 (LanguageId.CONCOURSE_PIPELINE.equals(doc.getLanguageId())) { - handler = forPipelines.symbolHandler; - } else if (LanguageId.CONCOURSE_TASK.equals(doc.getLanguageId())) { - handler = forTasks.symbolHandler; - } - } - return handler.handle(params); - }); +// documents.onDocumentSymbol((params) -> { +// DocumentSymbolHandler handler = DocumentSymbolHandler.NO_SYMBOLS; +// TextDocument doc = documents.getDocument(params.getTextDocument().getUri()); +// if (doc!=null) { +// if (LanguageId.CONCOURSE_PIPELINE.equals(doc.getLanguageId())) { +// handler = forPipelines.symbolHandler; +// } else if (LanguageId.CONCOURSE_TASK.equals(doc.getLanguageId())) { +// handler = forTasks.symbolHandler; +// } +// } +// return handler.handle(params); +// }); } // @Override diff --git a/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseModel.java b/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseModel.java index 1a4866160..cf0d3468d 100644 --- a/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseModel.java +++ b/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/ConcourseModel.java @@ -76,13 +76,13 @@ public class ConcourseModel { * that same type of something). */ public Constraint isUsed(YType refType, String entityTypeName) { - getAstTypeCache().addInterestingType(refType); //ensure the type is tracked in the type-cache + astTypeCache.addInterestingType(refType); //ensure the type is tracked in the type-cache return new Constraint() { @Override public void verify(DynamicSchemaContext dc, Node parent, Node node, YType type, IProblemCollector problems) { String defName = NodeUtil.asScalar(node); if (StringUtil.hasText(defName)) { //Avoid silly 'not used' errors for empty names (will have an other error already). - NodeTypes nodeTypes = getAstTypeCache().getNodeTypes(dc.getDocument().getUri()); + NodeTypes nodeTypes = astTypeCache.getNodeTypes(dc.getDocument().getUri()); if (nodeTypes!=null) { Optional reference = nodeTypes.getNodes(refType).stream() .filter(refNode -> defName.equals(NodeUtil.asScalar(refNode))) @@ -286,8 +286,9 @@ public class ConcourseModel { valueAt("name") ); + private final ASTTypeCache astTypeCache; + private final YamlAstCache asts = new YamlAstCache(); - private final ASTTypeCache astTypes = new ASTTypeCache(); private ResourceTypeRegistry resourceTypes; @@ -295,7 +296,8 @@ public class ConcourseModel { private YBeanUnionType stepType; - public ConcourseModel(SimpleLanguageServer languageServer) { + public ConcourseModel(SimpleLanguageServer languageServer, ASTTypeCache astTypeCache) { + this.astTypeCache = astTypeCache; this.snippetBuilderFactory = languageServer::createSnippetBuilder; } @@ -448,10 +450,6 @@ public class ConcourseModel { } - public ASTTypeCache getAstTypeCache() { - return astTypes; - } - public void setResourceTypeRegistry(ResourceTypeRegistry resourceTypes) { this.resourceTypes = resourceTypes; } diff --git a/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java b/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java index efd65aa73..779f0049f 100644 --- a/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java +++ b/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java @@ -61,6 +61,30 @@ 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 = { @@ -807,4 +831,10 @@ public class PipelineYmlSchema implements YamlSchema { public List getDefinitionTypes() { return definitionTypes; } + + public List getHierarchicalDefinitionTypes() { + // TODO Auto-generated method stub + return null; + } + } diff --git a/headless-services/concourse-language-server/src/test/java/org/springframework/ide/vscode/concourse/ConcourseEditorTest.java b/headless-services/concourse-language-server/src/test/java/org/springframework/ide/vscode/concourse/ConcourseEditorTest.java index 9930a27e1..9d0ab1994 100644 --- a/headless-services/concourse-language-server/src/test/java/org/springframework/ide/vscode/concourse/ConcourseEditorTest.java +++ b/headless-services/concourse-language-server/src/test/java/org/springframework/ide/vscode/concourse/ConcourseEditorTest.java @@ -3684,7 +3684,7 @@ public class ConcourseEditorTest { "- name: group-two\n" ); - editor.assertDocumentSymbols( + editor.assertDocumentSymbols( "some-resource-type|ResourceType", "foo-resource|Resource", "bar-resource|Resource", diff --git a/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServerBootApp.java b/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServerBootApp.java index 8bbab3df7..1c89d2203 100644 --- a/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServerBootApp.java +++ b/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServerBootApp.java @@ -16,6 +16,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter.CompletionFilter; import org.springframework.ide.vscode.commons.util.LogRedirect; import org.springframework.ide.vscode.commons.util.Unicodes; +import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache; @SpringBootApplication public class ManifestYamlLanguageServerBootApp { @@ -44,4 +45,8 @@ public class ManifestYamlLanguageServerBootApp { return true; }; } + + @Bean ASTTypeCache astTypeCache() { + return new ASTTypeCache(); + } } diff --git a/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServerInitializer.java b/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServerInitializer.java index 4e552f1f6..37222b5de 100644 --- a/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServerInitializer.java +++ b/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServerInitializer.java @@ -13,12 +13,12 @@ package org.springframework.ide.vscode.manifest.yaml; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Optional; import java.util.concurrent.Callable; import java.util.stream.Collectors; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; import org.springframework.ide.vscode.commons.cloudfoundry.client.ClientTimeouts; import org.springframework.ide.vscode.commons.cloudfoundry.client.CloudFoundryClientFactory; import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.CFTargetCache; @@ -29,7 +29,6 @@ import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.Clien import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.NoTargetsException; import org.springframework.ide.vscode.commons.cloudfoundry.client.v2.DefaultCloudFoundryClientFactoryV2; import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter; -import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter.CompletionFilter; import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfoProvider; import org.springframework.ide.vscode.commons.languageserver.hover.VscodeHoverEngineAdapter; import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine; @@ -69,7 +68,9 @@ public class ManifestYamlLanguageServerInitializer implements InitializingBean { private CloudFoundryClientFactory cfClientFactory; ClientParamsProvider defaultClientParamsProvider; + @Autowired private ApplicationContext appContext; @Autowired private SimpleLanguageServer server; + @Autowired private ASTTypeCache astTypeCache; @Override public void afterPropertiesSet() throws Exception { @@ -89,11 +90,9 @@ public class ManifestYamlLanguageServerInitializer implements InitializingBean { HoverInfoProvider infoProvider = new YamlHoverInfoProvider(parser, structureProvider, contextProvider); HoverHandler hoverEngine = new VscodeHoverEngineAdapter(server, infoProvider); YamlQuickfixes quickfixes = new YamlQuickfixes(server.getQuickfixRegistry(), server.getTextDocumentService(), structureProvider); - YamlSchemaBasedReconcileEngine engine = new YamlSchemaBasedReconcileEngine(parser, schema, quickfixes); + YamlSchemaBasedReconcileEngine engine = new YamlSchemaBasedReconcileEngine(parser, schema, quickfixes, appContext); - ASTTypeCache astTypeCache = new ASTTypeCache(); - engine.setTypeCollector(astTypeCache); - documents.onDocumentSymbol(new TypeBasedYamlSymbolHandler(documents, astTypeCache, schema.getDefinitionTypes(), server::hasHierarchicalDocumentSymbolSupport)); + documents.onDocumentSymbol(new TypeBasedYamlSymbolHandler(documents, astTypeCache, schema.getDefinitionTypes())); documents.onDidChangeContent(params -> { validateOnDocumentChange(engine, params.getDocument());