diff --git a/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/annotations/AnnotationHierarchies.java b/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/annotations/AnnotationHierarchies.java index 7de211d63..e08738be7 100644 --- a/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/annotations/AnnotationHierarchies.java +++ b/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/annotations/AnnotationHierarchies.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Pivotal, Inc. + * Copyright (c) 2017, 2018 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 @@ -14,6 +14,7 @@ import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.function.Predicate; +import java.util.stream.Collectors; import java.util.stream.Stream; import org.eclipse.jdt.core.dom.Annotation; @@ -60,7 +61,7 @@ public abstract class AnnotationHierarchies { public static Set getTransitiveSuperAnnotations(ITypeBinding typeBinding) { Set seen = new HashSet<>(); - findTransitiveSupers(typeBinding, seen); + findTransitiveSupers(typeBinding, seen).collect(Collectors.toList()); return seen; } diff --git a/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java b/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java index 8c8694c91..6dab248b3 100644 --- a/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java +++ b/headless-services/commons/commons-boot/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java @@ -13,6 +13,7 @@ package org.springframework.ide.vscode.boot.java.livehover; import java.util.Collection; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -27,21 +28,18 @@ import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.springframework.ide.vscode.boot.java.Annotations; import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents; +import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies; import org.springframework.ide.vscode.boot.java.utils.ASTUtils; -import org.springframework.ide.vscode.boot.java.utils.FunctionUtils; import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean; import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.util.Log; import org.springframework.ide.vscode.commons.util.StringUtil; -import org.springframework.ide.vscode.commons.util.text.DocumentRegion; import org.springframework.ide.vscode.commons.util.text.TextDocument; import com.google.common.collect.ImmutableList; -import reactor.util.function.Tuple3; - public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverProvider { public ComponentInjectionsHoverProvider(BootJavaLanguageServerComponents server) { @@ -135,8 +133,7 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP @Override public Collection getLiveHoverHints(TypeDeclaration typeDeclaration, TextDocument doc, SpringBootApp[] runningApps) { - Tuple3 functionBean = FunctionUtils.getFunctionBean(typeDeclaration, doc); - if (functionBean != null && runningApps.length > 0) { + if (runningApps.length > 0 && !isComponentAnnotatedType(typeDeclaration)) { try { LiveBean definedBean = getDefinedBeanForType(typeDeclaration, null); if (definedBean != null) { @@ -157,8 +154,8 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP @Override public Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) { - Tuple3 functionBean = FunctionUtils.getFunctionBean(typeDeclaration, doc); - if (functionBean != null && runningApps.length > 0) { + + if (runningApps.length > 0 && !isComponentAnnotatedType(typeDeclaration)) { LiveBean definedBean = getDefinedBeanForType(typeDeclaration, null); if (definedBean != null) { @@ -191,4 +188,26 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP return null; } + private boolean isComponentAnnotatedType(TypeDeclaration typeDeclaration) { + List modifiers = typeDeclaration.modifiers(); + for (Object modifier : modifiers) { + if (modifier instanceof Annotation) { + ITypeBinding typeBinding = ((Annotation) modifier).resolveTypeBinding(); + return isComponentAnnotation(typeBinding); + } + } + return false; + } + + private boolean isComponentAnnotation(ITypeBinding type) { + Set transitiveSuperAnnotations = AnnotationHierarchies.getTransitiveSuperAnnotations(type); + for (String annotationType : transitiveSuperAnnotations) { + if (Annotations.COMPONENT.equals(annotationType)) { + return true; + } + } + + return false; + } + } diff --git a/headless-services/commons/commons-boot/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/FunctionInjectionsHoverProviderTest.java b/headless-services/commons/commons-boot/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeansByTypeHoverProviderTest.java similarity index 62% rename from headless-services/commons/commons-boot/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/FunctionInjectionsHoverProviderTest.java rename to headless-services/commons/commons-boot/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeansByTypeHoverProviderTest.java index ec1cc4c5c..77020029a 100644 --- a/headless-services/commons/commons-boot/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/FunctionInjectionsHoverProviderTest.java +++ b/headless-services/commons/commons-boot/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeansByTypeHoverProviderTest.java @@ -23,7 +23,7 @@ import org.springframework.ide.vscode.project.harness.BootJavaLanguageServerHarn import org.springframework.ide.vscode.project.harness.MockRunningAppProvider; import org.springframework.ide.vscode.project.harness.ProjectsHarness; -public class FunctionInjectionsHoverProviderTest { +public class BeansByTypeHoverProviderTest { private BootJavaLanguageServerHarness harness; private ProjectsHarness projects = ProjectsHarness.INSTANCE; @@ -45,7 +45,53 @@ public class FunctionInjectionsHoverProviderTest { } @Test - public void typeButNotAFunction() throws Exception { + public void typeButNotABean() throws Exception { + LiveBeansModel beans = LiveBeansModel.builder() + .add(LiveBean.builder() + .id("scannedRandomClass") + .type("com.example.ScannedRandomClass") + .build() + ) + .add(LiveBean.builder() + .id("randomOtherBean") + .type("randomOtherBeanType") + .dependencies("scannedRandomClass") + .build() + ) + .add(LiveBean.builder() + .id("irrelevantBean") + .type("com.example.IrrelevantBean") + .dependencies("myController") + .build() + ) + .build(); + mockAppProvider.builder() + .isSpringBootApp(true) + .processId("111") + .processName("the-app") + .beans(beans) + .build(); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import java.io.Serializable;\n" + + "\n" + + "public class ClassNoBean implements Serializable {\n" + + "\n" + + " public String apply(String t) {\n" + + " return t.toUpperCase();\n" + + " }\n" + + "\n" + + "}\n" + + "" + ); + editor.assertHighlights(); + editor.assertNoHover("ClassNoBean"); + } + + @Test + public void typeWithGeneralBean() throws Exception { LiveBeansModel beans = LiveBeansModel.builder() .add(LiveBean.builder() .id("scannedRandomClass") @@ -86,8 +132,17 @@ public class FunctionInjectionsHoverProviderTest { "}\n" + "" ); - editor.assertHighlights(); - editor.assertNoHover("ScannedRandomClass"); + editor.assertHighlights("ScannedRandomClass"); + editor.assertTrimmedHover("ScannedRandomClass", + "**Injection report for Bean [id: scannedRandomClass, type: `com.example.ScannedRandomClass`]**\n" + + "\n" + + "Process [PID=111, name=`the-app`]:\n" + + "\n" + + "Bean [id: scannedRandomClass, type: `com.example.ScannedRandomClass`] injected into:\n" + + "\n" + + "- Bean: randomOtherBean \n" + + " Type: `randomOtherBeanType`" + ); } @Test @@ -145,4 +200,46 @@ public class FunctionInjectionsHoverProviderTest { " Type: `org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration`" ); } + + @Test + public void generalBeanLiveHoverAvoidOverlapWithAnnotation() throws Exception { + LiveBeansModel beans = LiveBeansModel.builder() + .add(LiveBean.builder() + .id("fooImplementation") + .type("com.example.FooImplementation") + .build() + ) + .build(); + mockAppProvider.builder() + .isSpringBootApp(true) + .processId("111") + .processName("the-app") + .beans(beans) + .build(); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import org.springframework.stereotype.Component;\n" + + "\n" + + "@Component\n" + + "public class FooImplementation implements Foo {\n" + + "\n" + + " @Override\n" + + " public void doSomeFoo() {\n" + + " System.out.println(\"Foo do do do!\");\n" + + " }\n" + + "}\n" + ); + editor.assertHighlights("@Component"); + editor.assertTrimmedHover("@Component", + "**Injection report for Bean [id: fooImplementation, type: `com.example.FooImplementation`]**\n" + + "\n" + + "Process [PID=111, name=`the-app`]:\n" + + "\n" + + "Bean [id: fooImplementation, type: `com.example.FooImplementation`] exists but is **Not injected anywhere**\n" + ); + } + + } diff --git a/vscode-extensions/vscode-concourse/developer-notes.md b/vscode-extensions/vscode-concourse/developer-notes.md index eace28c93..337510c31 100644 --- a/vscode-extensions/vscode-concourse/developer-notes.md +++ b/vscode-extensions/vscode-concourse/developer-notes.md @@ -14,6 +14,10 @@ To install it in vscode follow these steps: - Press `CTRL-SHIFT-P` and type 'vsix' in the search box - Select the `Extensions: Install from vsix file` command - Install the `.vsix` you downloaded earlier. + +**IMPORTANT**: Take care not to install multiple versions of the extension at once. +Vscode will not complain but it leads to unpredictable outcomes. So if you have +a prior version already installed make sure to uninstall it first! ## Bulding and Running