diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/autowired/AutowiredHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/autowired/AutowiredHoverProvider.java index b0decf648..dc3087bf4 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/autowired/AutowiredHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/autowired/AutowiredHoverProvider.java @@ -10,7 +10,6 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.autowired; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -26,7 +25,6 @@ import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.lsp4j.CodeLens; -import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.Hover; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.jsonrpc.messages.Either; @@ -41,7 +39,6 @@ import org.springframework.ide.vscode.boot.java.livehover.LiveHoverUtils; import org.springframework.ide.vscode.boot.java.utils.ASTUtils; 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.java.IType; import org.springframework.ide.vscode.commons.util.BadLocationException; @@ -56,6 +53,8 @@ import com.google.common.collect.ImmutableList; */ public class AutowiredHoverProvider implements HoverProvider { + public static final String BEANS_PREFIX = "\u21D0 "; + final static Logger log = LoggerFactory.getLogger(AutowiredHoverProvider.class); private static final int MAX_INLINE_BEANS_STRING_LENGTH = 60; @@ -88,28 +87,7 @@ public class AutowiredHoverProvider implements HoverProvider { if (declarationNode != null && definedBean != null) { for (SpringBootApp app : runningApps) { List relevantBeans = getRelevantAutowiredBeans(project, declarationNode, app, definedBean); - if (!relevantBeans.isEmpty()) { - CodeLens codeLens = new CodeLens(); - codeLens.setRange(range); - StringBuilder sb = new StringBuilder("\u21D0 "); - if (LiveHoverUtils.doBeansFitInline(relevantBeans, MAX_INLINE_BEANS_STRING_LENGTH, INLINE_BEANS_STRING_SEPARATOR)) { - sb.append(relevantBeans.stream().map(LiveHoverUtils::getShortDisplayType).collect(Collectors.joining(INLINE_BEANS_STRING_SEPARATOR))); - } else { - sb.append(relevantBeans.size()); - sb.append(" bean"); - if (relevantBeans.size() > 1) { - sb.append("s"); - } - } - codeLens.setData(sb.toString()); - Command cmd = new Command(); - cmd.setTitle(sb.toString()); - cmd.setCommand("org.springframework.showHoverAtPosition"); - cmd.setArguments(ImmutableList.of(range.getStart())); - codeLens.setCommand(cmd); - - return ImmutableList.of(codeLens); - } + return LiveHoverUtils.createCodeLensesForBeans(range, relevantBeans, BEANS_PREFIX, MAX_INLINE_BEANS_STRING_LENGTH, INLINE_BEANS_STRING_SEPARATOR); } } return null; @@ -153,32 +131,7 @@ public class AutowiredHoverProvider implements HoverProvider { } else { hover.append(" \n \n"); } - hover.append("**Autowired `"); - hover.append(definedBean.getId()); - hover.append("` → "); - if (LiveHoverUtils.doBeansFitInline(autowiredBeans, MAX_INLINE_BEANS_STRING_LENGTH - definedBean.getId().length(), - INLINE_BEANS_STRING_SEPARATOR)) { - hover.append(autowiredBeans.stream().map(b -> LiveHoverUtils.showBeanInline(server, project, b)) - .collect(Collectors.joining(INLINE_BEANS_STRING_SEPARATOR))); - hover.append("**\n"); - } else { - hover.append(autowiredBeans.size()); - hover.append(" bean"); - if (autowiredBeans.size() > 1) { - hover.append('s'); - } - hover.append("**\n"); - } -// if (autowiredBeans.size() == 1) { -// hover.append(LiveHoverUtils.showBeanIdAndTypeInline(server, project, autowiredBeans.get(0))); -// } else { -// hover.append(autowiredBeans.size()); -// hover.append(" beans**\n"); -// } - hover.append(autowiredBeans.stream() - .map(b -> "- " + LiveHoverUtils.showBeanWithResource(server, b, " ", project)) - .collect(Collectors.joining("\n"))); - hover.append("\n \n"); + createHoverContentForBeans(server, definedBean, project, hover, autowiredBeans); hover.append(LiveHoverUtils.niceAppName(app)); } @@ -190,14 +143,35 @@ public class AutowiredHoverProvider implements HoverProvider { return null; } - private List getRelevantAutowiredBeans(IJavaProject project, ASTNode declarationNode, SpringBootApp app, LiveBean definedBean) { - LiveBeansModel beans = app.getBeans(); + public static void createHoverContentForBeans(BootJavaLanguageServerComponents server, LiveBean definedBean, IJavaProject project, StringBuilder hover, + List autowiredBeans) { + hover.append("**Autowired `"); + hover.append(definedBean.getId()); + hover.append("` → "); + if (LiveHoverUtils.doBeansFitInline(autowiredBeans, MAX_INLINE_BEANS_STRING_LENGTH - definedBean.getId().length(), + INLINE_BEANS_STRING_SEPARATOR)) { + hover.append(autowiredBeans.stream().map(b -> LiveHoverUtils.showBeanInline(server, project, b)) + .collect(Collectors.joining(INLINE_BEANS_STRING_SEPARATOR))); + hover.append("**\n"); + } else { + hover.append(autowiredBeans.size()); + hover.append(" bean"); + if (autowiredBeans.size() > 1) { + hover.append('s'); + } + hover.append("**\n"); + } + hover.append(autowiredBeans.stream() + .map(b -> "- " + LiveHoverUtils.showBeanWithResource(server, b, " ", project)) + .collect(Collectors.joining("\n"))); + hover.append("\n \n"); + } + + public static List getRelevantAutowiredBeans(IJavaProject project, ASTNode declarationNode, SpringBootApp app, LiveBean definedBean) { List relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean); if (!relevantBeans.isEmpty()) { - List allDependencyBeans = relevantBeans.stream() - .flatMap(b -> Arrays.stream(b.getDependencies())).distinct() - .flatMap(d -> beans.getBeansOfName(d).stream()).collect(Collectors.toList()); + List allDependencyBeans = LiveHoverUtils.findAllDependencyBeans(app, relevantBeans); if (!allDependencyBeans.isEmpty()) { @@ -216,7 +190,7 @@ public class AutowiredHoverProvider implements HoverProvider { } @SuppressWarnings("unchecked") - private List findAutowiredBeans(IJavaProject project, ASTNode declarationNode, Collection beans) { + private static List findAutowiredBeans(IJavaProject project, ASTNode declarationNode, Collection beans) { if (declarationNode instanceof MethodDeclaration) { MethodDeclaration methodDeclaration = (MethodDeclaration)declarationNode; return ((List)methodDeclaration.parameters()).stream() @@ -234,7 +208,7 @@ public class AutowiredHoverProvider implements HoverProvider { return Collections.emptyList(); } - private List matchBeans(IJavaProject project, Collection beans, ITypeBinding type) { + private static List matchBeans(IJavaProject project, Collection beans, ITypeBinding type) { List relevant = Collections.emptyList(); if (type != null) { String fqName = type.getQualifiedName(); @@ -257,7 +231,7 @@ public class AutowiredHoverProvider implements HoverProvider { return relevant; } - private List matchBeans(IJavaProject project, Collection beans, String fqName, boolean allDots) { + private static List matchBeans(IJavaProject project, Collection beans, String fqName, boolean allDots) { if (fqName != null) { if (allDots) { return beans.stream().filter(b -> fqName.equals(b.getType(true).replace('$', '.'))).collect(Collectors.toList()); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/AbstractInjectedIntoHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/AbstractInjectedIntoHoverProvider.java index 812fc1e3a..984e2238e 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/AbstractInjectedIntoHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/AbstractInjectedIntoHoverProvider.java @@ -22,13 +22,13 @@ import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.lsp4j.CodeLens; -import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.Hover; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents; +import org.springframework.ide.vscode.boot.java.autowired.AutowiredHoverProvider; import org.springframework.ide.vscode.boot.java.handlers.HoverProvider; import org.springframework.ide.vscode.boot.java.utils.ASTUtils; import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; @@ -41,6 +41,8 @@ import com.google.common.collect.ImmutableList; public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider { + private static final String BEANS_PREFIX = "\u21D2 "; + private static Logger LOG = LoggerFactory.getLogger(AbstractInjectedIntoHoverProvider.class); private static final int MAX_INLINE_BEANS_STRING_LENGTH = 60; @@ -62,7 +64,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider if (Stream.of(runningApps).anyMatch(app -> LiveHoverUtils.hasRelevantBeans(app, definedBean))) { Optional nameRange = ASTUtils.nameRange(doc, annotation); if (nameRange.isPresent()) { - List codeLenses = assembleCodeLenses(project, runningApps, definedBean, nameRange.get()); + List codeLenses = assembleCodeLenses(project, runningApps, definedBean, nameRange.get(), ASTUtils.getAnnotatedType(annotation) != null); return codeLenses.isEmpty() ? ImmutableList.of(new CodeLens(nameRange.get())) : codeLenses; } } @@ -81,7 +83,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider LiveBean definedBean = getDefinedBean(annotation); if (definedBean != null) { - Hover hover = assembleHover(project, runningApps, definedBean); + Hover hover = assembleHover(project, runningApps, definedBean, ASTUtils.getAnnotatedType(annotation) != null); if (hover != null) { Optional nameRange = ASTUtils.nameRange(doc, annotation); if (nameRange.isPresent()) { @@ -94,45 +96,36 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider return null; } - protected List assembleCodeLenses(IJavaProject project, SpringBootApp[] runningApps, LiveBean definedBean, Range range) { + protected List assembleCodeLenses(IJavaProject project, SpringBootApp[] runningApps, LiveBean definedBean, Range range, boolean includeWiredBeans) { List codeLensList = new ArrayList<>(); - for (SpringBootApp app : runningApps) { + for (SpringBootApp app : runningApps) { - List relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean); + List relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean); - if (!relevantBeans.isEmpty()) { - List injectedBeans = getRelevantInjectedIntoBeans(project, app, definedBean, relevantBeans); - if (!injectedBeans.isEmpty()) { - CodeLens codeLens = new CodeLens(); - codeLens.setRange(range); - StringBuilder sb = new StringBuilder("\u21D2 "); - if (LiveHoverUtils.doBeansFitInline(relevantBeans, MAX_INLINE_BEANS_STRING_LENGTH, INLINE_BEANS_STRING_SEPARATOR)) { - sb.append(relevantBeans.stream().map(LiveHoverUtils::getShortDisplayType).collect(Collectors.joining(INLINE_BEANS_STRING_SEPARATOR))); - } else { - sb.append(relevantBeans.size()); - sb.append(" bean"); - if (relevantBeans.size() > 1) { - sb.append("s"); + if (!relevantBeans.isEmpty()) { + List injectedBeans = getRelevantInjectedIntoBeans(project, app, definedBean, relevantBeans); + ImmutableList.Builder builder = ImmutableList.builder(); + if (!injectedBeans.isEmpty()) { + // Break out of the loop. Just look for the first app with injected into beans + List injectedCodeLenses = LiveHoverUtils.createCodeLensesForBeans(range, injectedBeans, BEANS_PREFIX, MAX_INLINE_BEANS_STRING_LENGTH, INLINE_BEANS_STRING_SEPARATOR); + builder.addAll(injectedCodeLenses == null ? ImmutableList.of(new CodeLens(range)) : injectedCodeLenses); + } + if (includeWiredBeans) { + List allDependencyBeans = LiveHoverUtils.findAllDependencyBeans(app, relevantBeans); + List wiredCodeLenses = LiveHoverUtils.createCodeLensesForBeans(range, allDependencyBeans, + AutowiredHoverProvider.BEANS_PREFIX, MAX_INLINE_BEANS_STRING_LENGTH, + INLINE_BEANS_STRING_SEPARATOR); + if (wiredCodeLenses != null) { + builder.addAll(wiredCodeLenses); } } - codeLens.setData(sb.toString()); - Command cmd = new Command(); - cmd.setTitle(sb.toString()); - cmd.setCommand("org.springframework.showHoverAtPosition"); - cmd.setArguments(ImmutableList.of(range.getStart())); - codeLens.setCommand(cmd); - - codeLensList.add(codeLens); - - // Break out of the loop. Just look for the first app with injected into beans - break; + return builder.build(); } - } } return codeLensList; } - protected Hover assembleHover(IJavaProject project, SpringBootApp[] runningApps, LiveBean definedBean) { + protected Hover assembleHover(IJavaProject project, SpringBootApp[] runningApps, LiveBean definedBean, boolean includeWiredBeans) { StringBuilder hover = new StringBuilder(); boolean hasContent = false; @@ -176,6 +169,14 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider .collect(Collectors.joining("\n"))); hover.append("\n \n"); } + + if (includeWiredBeans) { + List allDependencyBeans = LiveHoverUtils.findAllDependencyBeans(app, relevantBeans); + if (!allDependencyBeans.isEmpty()) { + AutowiredHoverProvider.createHoverContentForBeans(server, definedBean, project, hover, allDependencyBeans); + } + } + hover.append(LiveHoverUtils.niceAppName(app)); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java index b2a913d9a..82e01636f 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java @@ -110,7 +110,7 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP if (Stream.of(runningApps).anyMatch(app -> LiveHoverUtils.hasRelevantBeans(app, definedBean))) { Optional nameRange = Optional.of(ASTUtils.nodeRegion(doc, typeDeclaration.getName()).asRange()); if (nameRange.isPresent()) { - List codeLenses = assembleCodeLenses(project, runningApps, definedBean, nameRange.get()); + List codeLenses = assembleCodeLenses(project, runningApps, definedBean, nameRange.get(), true); return codeLenses.isEmpty() ? ImmutableList.of(new CodeLens(nameRange.get())) : codeLenses; } } @@ -130,7 +130,7 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP LiveBean definedBean = getDefinedBeanForType(typeDeclaration, null); if (definedBean != null) { - Hover hover = assembleHover(project, runningApps, definedBean); + Hover hover = assembleHover(project, runningApps, definedBean, true); if (hover != null) { SimpleName name = typeDeclaration.getName(); try { @@ -150,7 +150,9 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP for (Object modifier : modifiers) { if (modifier instanceof Annotation) { ITypeBinding typeBinding = ((Annotation) modifier).resolveTypeBinding(); - return isComponentAnnotation(typeBinding); + if (isComponentAnnotation(typeBinding)) { + return true; + } } } return false; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/LiveHoverUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/LiveHoverUtils.java index b15823d89..21fadba36 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/LiveHoverUtils.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/LiveHoverUtils.java @@ -10,12 +10,16 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.livehover; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +import org.eclipse.lsp4j.CodeLens; +import org.eclipse.lsp4j.Command; +import org.eclipse.lsp4j.Range; import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents; import org.springframework.ide.vscode.boot.java.links.SourceLinkFactory; import org.springframework.ide.vscode.boot.java.links.SourceLinks; @@ -27,6 +31,8 @@ import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.util.Renderables; import org.springframework.ide.vscode.commons.util.StringUtil; +import com.google.common.collect.ImmutableList; + public class LiveHoverUtils { public static String showBean(LiveBean bean) { @@ -160,6 +166,14 @@ public class LiveHoverUtils { return Collections.emptyList(); } + public static List findAllDependencyBeans(SpringBootApp app, List relevantBeans) { + LiveBeansModel beans = app.getBeans(); + return relevantBeans.stream() + .flatMap(b -> Arrays.stream(b.getDependencies())).distinct() + .flatMap(d -> beans.getBeansOfName(d).stream()).collect(Collectors.toList()); + + } + public static String niceAppName(SpringBootApp app) { try { return niceAppName(app.getProcessID(), app.getProcessName()); @@ -173,5 +187,31 @@ public class LiveHoverUtils { return "Process [PID="+processId+", name=`"+processName+"`]"; } + public static List createCodeLensesForBeans(Range range, Collection relevantBeans, String prefix, int maxInlineBeansStringLength, String beansSeparator) { + if (!relevantBeans.isEmpty()) { + CodeLens codeLens = new CodeLens(); + codeLens.setRange(range); + StringBuilder sb = new StringBuilder(prefix); + if (LiveHoverUtils.doBeansFitInline(relevantBeans, maxInlineBeansStringLength, beansSeparator)) { + sb.append(relevantBeans.stream().map(LiveHoverUtils::getShortDisplayType).collect(Collectors.joining(beansSeparator))); + } else { + sb.append(relevantBeans.size()); + sb.append(" bean"); + if (relevantBeans.size() > 1) { + sb.append("s"); + } + } + codeLens.setData(sb.toString()); + Command cmd = new Command(); + cmd.setTitle(sb.toString()); + cmd.setCommand("org.springframework.showHoverAtPosition"); + cmd.setArguments(ImmutableList.of(range.getStart())); + codeLens.setCommand(cmd); + return ImmutableList.of(codeLens); + } else { + return null; + } + + } } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java index fed26d0a7..70e23dc92 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java @@ -441,8 +441,7 @@ public class AutowiredHoverProviderTest { " Type: `org.springframework.web.client.RestTemplate`" ); editor.assertHoverContains("@Controller", - "**Injected `myController` → _not injected anywhere_** \n" + - "Process [PID=111, name=`the-app`]" + "**Injected `myController` → _not injected anywhere_** \n" ); } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ComponentInjectionsHoverProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ComponentInjectionsHoverProviderTest.java index 49e1e07e1..d8beba88f 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ComponentInjectionsHoverProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ComponentInjectionsHoverProviderTest.java @@ -499,6 +499,14 @@ public class ComponentInjectionsHoverProviderTest { editor.assertHighlights("@Component", "AutowiredClass"); editor.assertTrimmedHover("@Component", "**Injected `autowiredClass` → _not injected anywhere_** \n" + + "**Autowired `autowiredClass` → `dependencyA` `dependencyB`**\n" + + "- Bean: `dependencyA` \n" + + " Type: `com.example.DependencyA` \n" + + " Resource: `com/example/DependencyA.class`\n" + + "- Bean: `dependencyB` \n" + + " Type: `com.example.DependencyB` \n" + + " Resource: `com/example/DependencyB.class`\n" + + " \n" + "Process [PID=111, name=`the-app`]\n" ); } @@ -547,6 +555,12 @@ public class ComponentInjectionsHoverProviderTest { editor.assertHighlights("@Component", "@Autowired"); editor.assertTrimmedHover("@Component", "**Injected `autowiredClass` → _not injected anywhere_** \n" + + "**Autowired `autowiredClass` → `dependencyA` `dependencyB`**\n" + + "- Bean: `dependencyA` \n" + + " Type: `com.example.DependencyA`\n" + + "- Bean: `dependencyB` \n" + + " Type: `com.example.DependencyB`\n" + + " \n" + "Process [PID=111, name=`the-app`]\n" ); }