PT #159794215: Show wired beans for TypeDeclaration annotations hovers

This commit is contained in:
BoykoAlex
2018-08-16 18:23:58 -04:00
parent f82a072e6f
commit 638dcfa994
6 changed files with 126 additions and 96 deletions

View File

@@ -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<LiveBean> 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("` &rarr; ");
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<LiveBean> 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<LiveBean> autowiredBeans) {
hover.append("**Autowired `");
hover.append(definedBean.getId());
hover.append("` &rarr; ");
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<LiveBean> getRelevantAutowiredBeans(IJavaProject project, ASTNode declarationNode, SpringBootApp app, LiveBean definedBean) {
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean);
if (!relevantBeans.isEmpty()) {
List<LiveBean> allDependencyBeans = relevantBeans.stream()
.flatMap(b -> Arrays.stream(b.getDependencies())).distinct()
.flatMap(d -> beans.getBeansOfName(d).stream()).collect(Collectors.toList());
List<LiveBean> allDependencyBeans = LiveHoverUtils.findAllDependencyBeans(app, relevantBeans);
if (!allDependencyBeans.isEmpty()) {
@@ -216,7 +190,7 @@ public class AutowiredHoverProvider implements HoverProvider {
}
@SuppressWarnings("unchecked")
private List<LiveBean> findAutowiredBeans(IJavaProject project, ASTNode declarationNode, Collection<LiveBean> beans) {
private static List<LiveBean> findAutowiredBeans(IJavaProject project, ASTNode declarationNode, Collection<LiveBean> beans) {
if (declarationNode instanceof MethodDeclaration) {
MethodDeclaration methodDeclaration = (MethodDeclaration)declarationNode;
return ((List<Object>)methodDeclaration.parameters()).stream()
@@ -234,7 +208,7 @@ public class AutowiredHoverProvider implements HoverProvider {
return Collections.emptyList();
}
private List<LiveBean> matchBeans(IJavaProject project, Collection<LiveBean> beans, ITypeBinding type) {
private static List<LiveBean> matchBeans(IJavaProject project, Collection<LiveBean> beans, ITypeBinding type) {
List<LiveBean> relevant = Collections.emptyList();
if (type != null) {
String fqName = type.getQualifiedName();
@@ -257,7 +231,7 @@ public class AutowiredHoverProvider implements HoverProvider {
return relevant;
}
private List<LiveBean> matchBeans(IJavaProject project, Collection<LiveBean> beans, String fqName, boolean allDots) {
private static List<LiveBean> matchBeans(IJavaProject project, Collection<LiveBean> beans, String fqName, boolean allDots) {
if (fqName != null) {
if (allDots) {
return beans.stream().filter(b -> fqName.equals(b.getType(true).replace('$', '.'))).collect(Collectors.toList());

View File

@@ -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<Range> nameRange = ASTUtils.nameRange(doc, annotation);
if (nameRange.isPresent()) {
List<CodeLens> codeLenses = assembleCodeLenses(project, runningApps, definedBean, nameRange.get());
List<CodeLens> 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<Range> nameRange = ASTUtils.nameRange(doc, annotation);
if (nameRange.isPresent()) {
@@ -94,45 +96,36 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
return null;
}
protected List<CodeLens> assembleCodeLenses(IJavaProject project, SpringBootApp[] runningApps, LiveBean definedBean, Range range) {
protected List<CodeLens> assembleCodeLenses(IJavaProject project, SpringBootApp[] runningApps, LiveBean definedBean, Range range, boolean includeWiredBeans) {
List<CodeLens> codeLensList = new ArrayList<>();
for (SpringBootApp app : runningApps) {
for (SpringBootApp app : runningApps) {
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean);
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean);
if (!relevantBeans.isEmpty()) {
List<LiveBean> 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<LiveBean> injectedBeans = getRelevantInjectedIntoBeans(project, app, definedBean, relevantBeans);
ImmutableList.Builder<CodeLens> builder = ImmutableList.builder();
if (!injectedBeans.isEmpty()) {
// Break out of the loop. Just look for the first app with injected into beans
List<CodeLens> 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<LiveBean> allDependencyBeans = LiveHoverUtils.findAllDependencyBeans(app, relevantBeans);
List<CodeLens> 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<LiveBean> allDependencyBeans = LiveHoverUtils.findAllDependencyBeans(app, relevantBeans);
if (!allDependencyBeans.isEmpty()) {
AutowiredHoverProvider.createHoverContentForBeans(server, definedBean, project, hover, allDependencyBeans);
}
}
hover.append(LiveHoverUtils.niceAppName(app));
}

View File

@@ -110,7 +110,7 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
if (Stream.of(runningApps).anyMatch(app -> LiveHoverUtils.hasRelevantBeans(app, definedBean))) {
Optional<Range> nameRange = Optional.of(ASTUtils.nodeRegion(doc, typeDeclaration.getName()).asRange());
if (nameRange.isPresent()) {
List<CodeLens> codeLenses = assembleCodeLenses(project, runningApps, definedBean, nameRange.get());
List<CodeLens> 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;

View File

@@ -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<LiveBean> findAllDependencyBeans(SpringBootApp app, List<LiveBean> 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<CodeLens> createCodeLensesForBeans(Range range, Collection<LiveBean> 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;
}
}
}

View File

@@ -441,8 +441,7 @@ public class AutowiredHoverProviderTest {
" Type: `org.springframework.web.client.RestTemplate`"
);
editor.assertHoverContains("@Controller",
"**Injected `myController` &rarr; _not injected anywhere_** \n" +
"Process [PID=111, name=`the-app`]"
"**Injected `myController` &rarr; _not injected anywhere_** \n"
);
}

View File

@@ -499,6 +499,14 @@ public class ComponentInjectionsHoverProviderTest {
editor.assertHighlights("@Component", "AutowiredClass");
editor.assertTrimmedHover("@Component",
"**Injected `autowiredClass` &rarr; _not injected anywhere_** \n" +
"**Autowired `autowiredClass` &rarr; `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` &rarr; _not injected anywhere_** \n" +
"**Autowired `autowiredClass` &rarr; `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"
);
}