PT #159307292: Autowired reports optics, content specific to AST node
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"jarUrl": "https://s3-us-west-1.amazonaws.com/s3-test.spring.io/sts4/fatjars/snapshots/spring-boot-language-server-0.2.1-201805111942.jar"
|
||||
"jarUrl": "https://s3-us-west-1.amazonaws.com/s3-test.spring.io/sts4/fatjars/snapshots/spring-boot-language-server-0.4.0-201807190033.jar"
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Spring Boot Language Server
|
||||
Bundle-Vendor: Pivotal, Inc.
|
||||
Bundle-SymbolicName: org.springframework.tooling.boot.ls;singleton:=true
|
||||
Bundle-Version: 0.3.2.qualifier
|
||||
Bundle-Version: 0.4.0.qualifier
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Require-Bundle: org.eclipse.jdt.launching;bundle-version="3.9.0",
|
||||
org.eclipse.core.runtime;bundle-version="3.12.0",
|
||||
|
||||
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Name: BOSH Manifest Language Server
|
||||
Bundle-Vendor: Pivotal, Inc.
|
||||
Bundle-SymbolicName: org.springframework.tooling.bosh.ls;singleton:=true
|
||||
Bundle-Version: 0.3.2.qualifier
|
||||
Bundle-Version: 0.4.0.qualifier
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Require-Bundle: org.eclipse.jdt.launching;bundle-version="3.9.0",
|
||||
org.eclipse.core.runtime;bundle-version="3.12.0",
|
||||
|
||||
@@ -16,6 +16,6 @@ package org.springframework.tooling.bosh.ls;
|
||||
public class Constants {
|
||||
|
||||
public static final String PLUGIN_ID = "org.springframework.tooling.bosh.ls";
|
||||
public static final String LANGUAGE_SERVER_VERSION = "0.3.2-SNAPSHOT";
|
||||
public static final String LANGUAGE_SERVER_VERSION = "0.4.0-SNAPSHOT";
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Cloud Foundry Manifest Language Server
|
||||
Bundle-Vendor: Pivotal, Inc.
|
||||
Bundle-SymbolicName: org.springframework.tooling.cloudfoundry.manifest.ls;singleton:=true
|
||||
Bundle-Version: 0.3.2.qualifier
|
||||
Bundle-Version: 0.4.0.qualifier
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Require-Bundle: org.eclipse.jdt.launching;bundle-version="3.8.0",
|
||||
org.eclipse.core.runtime;bundle-version="3.12.0",
|
||||
|
||||
@@ -16,6 +16,6 @@ package org.springframework.tooling.cloudfoundry.manifest.ls;
|
||||
public class Constants {
|
||||
|
||||
public static final String PLUGIN_ID = "org.springframework.tooling.cloudfoundry.manifest.ls";
|
||||
public static final String LANGUAGE_SERVER_VERSION = "0.3.2-SNAPSHOT.jar";
|
||||
public static final String LANGUAGE_SERVER_VERSION = "0.4.0-SNAPSHOT.jar";
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Concourse Pipeline Language Server
|
||||
Bundle-Vendor: Pivotal, Inc.
|
||||
Bundle-SymbolicName: org.springframework.tooling.concourse.ls;singleton:=true
|
||||
Bundle-Version: 0.3.2.qualifier
|
||||
Bundle-Version: 0.4.0.qualifier
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Require-Bundle: org.eclipse.jdt.launching;bundle-version="3.9.0",
|
||||
org.eclipse.core.runtime;bundle-version="3.12.0",
|
||||
|
||||
@@ -16,6 +16,6 @@ package org.springframework.tooling.concourse.ls;
|
||||
public class Constants {
|
||||
|
||||
public static final String PLUGIN_ID = "org.springframework.tooling.concourse.ls";
|
||||
public static final String LANGUAGE_SERVER_VERSION = "0.3.2-SNAPSHOT";
|
||||
public static final String LANGUAGE_SERVER_VERSION = "0.4.0-SNAPSHOT";
|
||||
|
||||
}
|
||||
|
||||
@@ -81,7 +81,9 @@ public class LocalSpringBootApp extends AbstractSpringBootApp {
|
||||
|
||||
@Override
|
||||
public String getProcessName() {
|
||||
return vmd.displayName();
|
||||
String rawName = vmd.displayName();
|
||||
int firstSpace = rawName.indexOf(' ');
|
||||
return firstSpace < 0 ? rawName : rawName.substring(0, firstSpace);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,8 +16,12 @@ import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -174,4 +178,40 @@ public final class JandexClasspath implements ClasspathIndex {
|
||||
.collect(CollectorUtil.toImmutableList());
|
||||
}
|
||||
|
||||
private static void updateQueue(Queue<String> queue, Set<String> exclusion, IType type) {
|
||||
for (String t : type.getSuperInterfaceNames()) {
|
||||
if (!exclusion.contains(t)) {
|
||||
queue.add(t);
|
||||
exclusion.add(t);
|
||||
}
|
||||
}
|
||||
String superClass = type.getSuperclassName();
|
||||
if (superClass != null && !exclusion.contains(superClass)) {
|
||||
queue.add(superClass);
|
||||
exclusion.add(superClass);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<IType> allSuperTypesOf(IType type) {
|
||||
Queue<String> queue = new LinkedList<>();
|
||||
HashSet<String> visited = new HashSet<>();
|
||||
updateQueue(queue, visited, type);
|
||||
return Flux.generate(() -> queue, (state, sink) -> {
|
||||
IType nextType = null;
|
||||
while (nextType == null && state.peek() != null) {
|
||||
String typeName = state.poll();
|
||||
nextType = findType(typeName);
|
||||
if (nextType != null) {
|
||||
sink.next(nextType);
|
||||
updateQueue(state, visited, nextType);
|
||||
}
|
||||
}
|
||||
if (state.peek() == null) {
|
||||
sink.complete();
|
||||
}
|
||||
return state;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016-2017 Pivotal, Inc.
|
||||
* Copyright (c) 2016, 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
|
||||
@@ -163,4 +163,15 @@ class TypeImpl implements IType {
|
||||
return info.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSuperclassName() {
|
||||
DotName name = info.superName();
|
||||
return name == null ? null : name.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSuperInterfaceNames() {
|
||||
return info.interfaceNames().stream().map(DotName::toString).toArray(String[]::new);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ public interface ClasspathIndex extends Disposable {
|
||||
Flux<Tuple2<IType, Double>> fuzzySearchTypes(String searchTerm, Predicate<IType> typeFilter);
|
||||
Flux<Tuple2<String, Double>> fuzzySearchPackages(String searchTerm);
|
||||
Flux<IType> allSubtypesOf(IType type);
|
||||
Flux<IType> allSuperTypesOf(IType type);
|
||||
Optional<File> findClasspathResourceContainer(String fqName);
|
||||
|
||||
//Maybe the stuff below is another interface? Something that provides operations
|
||||
|
||||
@@ -41,6 +41,10 @@ public interface IJavaProject {
|
||||
return getIndex().allSubtypesOf(targetType);
|
||||
}
|
||||
|
||||
default Flux<IType> allSuperTypesOf(IType targetType) {
|
||||
return getIndex().allSuperTypesOf(targetType);
|
||||
}
|
||||
|
||||
default Flux<Tuple2<IType, Double>> fuzzySearchTypes(String searchTerm, Predicate<IType> typeFilter) {
|
||||
return getIndex().fuzzySearchTypes(searchTerm, typeFilter);
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public interface IType extends IMember {
|
||||
IField getField(String name);
|
||||
|
||||
/**
|
||||
* Returns the fields declared by this type in the order in which they appear
|
||||
* Returns the fields declared by this type in the order in which they appear
|
||||
* in the source or class file. For binary types, this includes synthetic fields.
|
||||
*
|
||||
* @return the fields declared by this type
|
||||
@@ -71,7 +71,7 @@ public interface IType extends IMember {
|
||||
* The type signatures may be either unresolved (for source types)
|
||||
* or resolved (for binary types), and either basic (for basic types)
|
||||
* or rich (for parameterized types). See {@link Signature} for details.
|
||||
* Note that the parameter type signatures for binary methods are expected
|
||||
* Note that the parameter type signatures for binary methods are expected
|
||||
* to be dot-based.
|
||||
* </p>
|
||||
*
|
||||
@@ -86,13 +86,17 @@ public interface IType extends IMember {
|
||||
* For binary types, this may include the special <code><clinit></code> method
|
||||
* and synthetic methods.
|
||||
* <p>
|
||||
* The results are listed in the order in which they appear in the source or class file.
|
||||
* The results are listed in the order in which they appear in the source or class file.
|
||||
* </p>
|
||||
*
|
||||
* @return the methods and constructors declared by this type
|
||||
*/
|
||||
Stream<IMethod> getMethods();
|
||||
|
||||
String getSuperclassName();
|
||||
|
||||
String[] getSuperInterfaceNames();
|
||||
|
||||
// /**
|
||||
// * Resolves the given type name within the context of this type (depending on the type hierarchy
|
||||
// * and its imports).
|
||||
|
||||
@@ -20,9 +20,12 @@ import static org.springframework.ide.vscode.languageserver.testharness.Classpat
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -164,4 +167,24 @@ public class JavaIndexTest {
|
||||
assertTrue(file.get().exists());
|
||||
assertEquals(getOutputFolder(project).toString(), file.get().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindAllSuperTypes() throws Exception {
|
||||
MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file");
|
||||
IType type = project.findType("java.util.ArrayList");
|
||||
assertNotNull(type);
|
||||
Set<String> actual = project.allSuperTypesOf(type).map(t -> t.getFullyQualifiedName()).collect(Collectors.toSet()).block();
|
||||
Set<String> expected = new HashSet<>(Arrays.asList(
|
||||
"java.util.List",
|
||||
"java.util.RandomAccess",
|
||||
"java.lang.Cloneable",
|
||||
"java.io.Serializable",
|
||||
"java.util.AbstractList",
|
||||
"java.util.Collection",
|
||||
"java.lang.Object",
|
||||
"java.util.AbstractCollection",
|
||||
"java.lang.Iterable"
|
||||
));
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,18 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.autowired;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.FieldDeclaration;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Hover;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
@@ -34,6 +39,7 @@ 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.StringUtil;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
@@ -41,11 +47,15 @@ import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
* @author Alex Boyko
|
||||
*/
|
||||
public class AutowiredHoverProvider implements HoverProvider {
|
||||
|
||||
final static Logger log = LoggerFactory.getLogger(AutowiredHoverProvider.class);
|
||||
|
||||
private static final int MAX_INLINE_BEANS_STRING_LENGTH = 50;
|
||||
private static final String INLINE_BEANS_STRING_SEPARATOR = " ";
|
||||
|
||||
private BootJavaLanguageServerComponents server;
|
||||
|
||||
public AutowiredHoverProvider(BootJavaLanguageServerComponents server) {
|
||||
@@ -94,30 +104,61 @@ public class AutowiredHoverProvider implements HoverProvider {
|
||||
LiveBean definedBean = getDefinedBean(annotation);
|
||||
if (definedBean != null) {
|
||||
|
||||
hover.append("**Injection report for " + LiveHoverUtils.showBean(definedBean) + "**\n\n");
|
||||
|
||||
boolean hasInterestingApp = false;
|
||||
boolean hasAutowiring = false;
|
||||
boolean hasContent = false;
|
||||
|
||||
for (SpringBootApp app : runningApps) {
|
||||
LiveBeansModel beans = app.getBeans();
|
||||
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean).collect(Collectors.toList());
|
||||
|
||||
if (!relevantBeans.isEmpty()) {
|
||||
if (!hasInterestingApp) {
|
||||
hasInterestingApp = true;
|
||||
} else {
|
||||
hover.append("\n\n");
|
||||
}
|
||||
hover.append(LiveHoverUtils.niceAppName(app) + ":");
|
||||
List<LiveBean> allDependencyBeans = relevantBeans.stream()
|
||||
.flatMap(b -> Arrays.stream(b.getDependencies()))
|
||||
.distinct()
|
||||
.flatMap(d -> beans.getBeansOfName(d).stream())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (LiveBean bean : relevantBeans) {
|
||||
hover.append("\n\n");
|
||||
hasAutowiring |= addAutomaticallyWired(hover, annotation, beans, bean, project);
|
||||
if (!allDependencyBeans.isEmpty()) {
|
||||
|
||||
// parent is marker node, grandparent is some field, method, variable declaration node.
|
||||
ASTNode declarationNode = node.getParent().getParent();
|
||||
List<LiveBean> autowiredBeans = findAutowiredBeans(project, declarationNode, allDependencyBeans);
|
||||
if (autowiredBeans.isEmpty()) {
|
||||
// Show all relevant dependency beans
|
||||
autowiredBeans = allDependencyBeans;
|
||||
}
|
||||
|
||||
if (!autowiredBeans.isEmpty()) {
|
||||
if (!hasContent) {
|
||||
hasContent = true;
|
||||
} else {
|
||||
hover.append(" \n \n");
|
||||
}
|
||||
hover.append("**Autowired → ");
|
||||
if (LiveHoverUtils.doBeansFitInline(autowiredBeans, MAX_INLINE_BEANS_STRING_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(" beans**\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");
|
||||
hover.append(LiveHoverUtils.niceAppName(app));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (hasInterestingApp && hasAutowiring) {
|
||||
if (hasContent) {
|
||||
return new Hover(ImmutableList.of(Either.forLeft(hover.toString())));
|
||||
}
|
||||
}
|
||||
@@ -125,6 +166,53 @@ public class AutowiredHoverProvider implements HoverProvider {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<LiveBean> findAutowiredBeans(IJavaProject project, ASTNode declarationNode, Collection<LiveBean> beans) {
|
||||
if (declarationNode instanceof MethodDeclaration) {
|
||||
MethodDeclaration methodDeclaration = (MethodDeclaration)declarationNode;
|
||||
return ((List<Object>)methodDeclaration.parameters()).stream()
|
||||
.filter(p -> p instanceof SingleVariableDeclaration)
|
||||
.map(p -> (SingleVariableDeclaration)p)
|
||||
.flatMap(p -> findAutowiredBeans(project, p, beans).stream())
|
||||
.collect(Collectors.toList());
|
||||
} else if (declarationNode instanceof FieldDeclaration) {
|
||||
FieldDeclaration fieldDeclaration = (FieldDeclaration)declarationNode;
|
||||
return matchBeans(project, beans, fieldDeclaration.getType().resolveBinding());
|
||||
} else if (declarationNode instanceof SingleVariableDeclaration) {
|
||||
SingleVariableDeclaration singleVariableDeclaration = (SingleVariableDeclaration)declarationNode;
|
||||
return matchBeans(project, beans, singleVariableDeclaration.getType().resolveBinding());
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private List<LiveBean> matchBeans(IJavaProject project, Collection<LiveBean> beans, ITypeBinding type) {
|
||||
List<LiveBean> relevant = Collections.emptyList();
|
||||
if (type != null) {
|
||||
String fqName = type.getQualifiedName();
|
||||
if (fqName != null) {
|
||||
relevant = matchBeans(project, beans, fqName);
|
||||
if (relevant.isEmpty()) {
|
||||
IType indexType = project.findType(fqName);
|
||||
if (indexType != null) {
|
||||
relevant = project.allSubtypesOf(indexType)
|
||||
.map(subType -> matchBeans(project, beans, subType.getFullyQualifiedName()))
|
||||
.filter(relevantBeans -> !relevantBeans.isEmpty())
|
||||
.blockFirst();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return relevant;
|
||||
}
|
||||
|
||||
private List<LiveBean> matchBeans(IJavaProject project, Collection<LiveBean> beans, String fqName) {
|
||||
if (fqName != null) {
|
||||
return beans.stream().filter(b -> fqName.equals(b.getType(true))).collect(Collectors.toList());
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
private LiveBean getDefinedBean(Annotation autowiredAnnotation) {
|
||||
TypeDeclaration declaringType = ASTUtils.findDeclaringType(autowiredAnnotation);
|
||||
if (declaringType != null) {
|
||||
@@ -150,29 +238,6 @@ public class AutowiredHoverProvider implements HoverProvider {
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean addAutomaticallyWired(StringBuilder hover, Annotation annotation, LiveBeansModel beans, LiveBean bean, IJavaProject project) {
|
||||
boolean result = false;
|
||||
String[] dependencies = bean.getDependencies();
|
||||
|
||||
if (dependencies != null && dependencies.length > 0) {
|
||||
result = true;
|
||||
hover.append(LiveHoverUtils.showBean(bean) + " got autowired with:\n\n");
|
||||
|
||||
boolean firstDependency = true;
|
||||
for (String injectedBean : dependencies) {
|
||||
if (!firstDependency) {
|
||||
hover.append("\n");
|
||||
}
|
||||
List<LiveBean> dependencyBeans = beans.getBeansOfName(injectedBean);
|
||||
for (LiveBean dependencyBean : dependencyBeans) {
|
||||
hover.append("- " + LiveHoverUtils.showBeanWithResource(server, dependencyBean, " ", project));
|
||||
}
|
||||
firstDependency = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset,
|
||||
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.livehover;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -21,7 +22,6 @@ 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.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.util.Renderables;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
|
||||
@@ -43,7 +43,9 @@ public class LiveHoverUtils {
|
||||
String type = bean.getType(true);
|
||||
|
||||
StringBuilder buf = new StringBuilder("Bean: ");
|
||||
buf.append('`');
|
||||
buf.append(bean.getId());
|
||||
buf.append('`');
|
||||
SourceLinks sourceLinks = SourceLinkFactory.createSourceLinks(server);
|
||||
if (type != null) {
|
||||
// Try creating a URL link to open source for the type
|
||||
@@ -65,6 +67,65 @@ public class LiveHoverUtils {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String showBeanInline(BootJavaLanguageServerComponents server, IJavaProject project, LiveBean bean) {
|
||||
String id = bean.getId();
|
||||
String type = bean.getType(true);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('`');
|
||||
sb.append(id);
|
||||
sb.append('`');
|
||||
String displayId = sb.toString();
|
||||
SourceLinks sourceLinks = SourceLinkFactory.createSourceLinks(server);
|
||||
if (type != null) {
|
||||
Optional<String> url = sourceLinks.sourceLinkUrlForFQName(project, type);
|
||||
if (url.isPresent()) {
|
||||
return Renderables.link(displayId, url.get()).toMarkdown();
|
||||
}
|
||||
}
|
||||
return displayId;
|
||||
}
|
||||
|
||||
public static boolean doBeansFitInline(Collection<LiveBean> beans, int maxLength, String delimiter) {
|
||||
int length = 0;
|
||||
for (LiveBean bean : beans) {
|
||||
if (length != 0) {
|
||||
length += delimiter.length();
|
||||
}
|
||||
length += bean.getId().length();
|
||||
if (length > maxLength) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static String showBeanIdAndTypeInline(BootJavaLanguageServerComponents server, IJavaProject project, LiveBean bean) {
|
||||
String id = bean.getId();
|
||||
String type = bean.getType(true);
|
||||
SourceLinks sourceLinks = SourceLinkFactory.createSourceLinks(server);
|
||||
String displayType = type;
|
||||
if (type != null) {
|
||||
int lastDotIdx = type.lastIndexOf('.');
|
||||
if (lastDotIdx >= 0 && lastDotIdx < type.length() - 1) {
|
||||
displayType = "`" + type.substring(lastDotIdx + 1) + "`";
|
||||
}
|
||||
Optional<String> url = sourceLinks.sourceLinkUrlForFQName(project, type);
|
||||
if (url.isPresent()) {
|
||||
displayType = Renderables.link(displayType, url.get()).toMarkdown();
|
||||
}
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('`');
|
||||
sb.append(id);
|
||||
sb.append('`');
|
||||
if (displayType != null) {
|
||||
sb.append(' ');
|
||||
sb.append(displayType);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
public static String showResource(SourceLinks sourceLinks, String resource, IJavaProject project) {
|
||||
return new SpringResource(sourceLinks, resource, project).toMarkdown();
|
||||
}
|
||||
|
||||
@@ -156,14 +156,7 @@ public class RequestMappingHoverProvider implements HoverProvider {
|
||||
|
||||
List<Renderable> renderableUrls = Arrays.stream(mappingMethod.getT1().getSplitPath()).flatMap(path -> {
|
||||
String url = UrlUtil.createUrl(host, port, path);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[");
|
||||
builder.append(url);
|
||||
builder.append("]");
|
||||
builder.append("(");
|
||||
builder.append(url);
|
||||
builder.append(")");
|
||||
return Stream.of(Renderables.text(builder.toString()), Renderables.lineBreak());
|
||||
return Stream.of(Renderables.link(url, url), Renderables.lineBreak());
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
|
||||
@@ -33,6 +33,34 @@ import org.springframework.ide.vscode.project.harness.ProjectsHarness.ProjectCus
|
||||
*/
|
||||
public class AutowiredHoverProviderTest {
|
||||
|
||||
private static final String FOO_IMPL_CONTENTS = "package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.beans.factory.annotation.Autowired;\n" +
|
||||
"import org.springframework.scheduling.TaskScheduler;\n" +
|
||||
"import org.springframework.stereotype.Component;\n" +
|
||||
"\n" +
|
||||
"@Component(\"defaultFoo\")\n" +
|
||||
"public class FooImplementation implements Foo {\n" +
|
||||
" \n" +
|
||||
" private TaskScheduler scheduler;\n" +
|
||||
" \n" +
|
||||
" @Autowired Foo self;\n" +
|
||||
" \n" +
|
||||
" @Override\n" +
|
||||
" public void doSomeFoo() {\n" +
|
||||
" scheduler.scheduleWithFixedDelay(() -> {\n" +
|
||||
" System.out.println(\"Doo Done done!\");\n" +
|
||||
" }, 1000);\n" +
|
||||
" System.out.println(\"Foo do do do do!\");\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" @Autowired\n" +
|
||||
" public void setScheduler(TaskScheduler scheduler) {\n" +
|
||||
" this.scheduler = scheduler;\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
"}";
|
||||
|
||||
private static final ProjectCustomizer FOO_INTERFACE = (CustomizableProjectContent p) -> {
|
||||
p.createType("com.examle.Foo",
|
||||
"package com.example;\n" +
|
||||
@@ -55,6 +83,8 @@ public class AutowiredHoverProviderTest {
|
||||
"public class DependencyB {\n" +
|
||||
"}\n"
|
||||
);
|
||||
|
||||
p.createType("com.example.FooImplementation", FOO_IMPL_CONTENTS);
|
||||
};
|
||||
|
||||
private BootJavaLanguageServerHarness harness;
|
||||
@@ -73,7 +103,7 @@ public class AutowiredHoverProviderTest {
|
||||
|
||||
MavenJavaProject jp = projects.mavenProject("empty-boot-15-web-app", FOO_INTERFACE);
|
||||
assertTrue(jp.findType("com.example.Foo").exists());
|
||||
harness.useProject(projects.mavenProject("empty-boot-15-web-app"));
|
||||
harness.useProject(jp);
|
||||
harness.intialize(null);
|
||||
}
|
||||
|
||||
@@ -116,15 +146,12 @@ public class AutowiredHoverProviderTest {
|
||||
|
||||
editor.assertHighlights("@Component", "@Inject");
|
||||
editor.assertTrimmedHover("@Inject",
|
||||
"**Injection report for Bean [id: autowiredClass, type: `com.example.AutowiredClass`]**\n" +
|
||||
"\n" +
|
||||
"Process [PID=111, name=`the-app`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: autowiredClass, type: `com.example.AutowiredClass`] got autowired with:\n" +
|
||||
"\n" +
|
||||
"- Bean: dependencyA \n" +
|
||||
"**Autowired → `dependencyA`**\n" +
|
||||
"- Bean: `dependencyA` \n" +
|
||||
" Type: `com.example.DependencyA` \n" +
|
||||
" Resource: `" + Paths.get("com/example/DependencyA.class") + "`"
|
||||
" Resource: `" + Paths.get("com/example/DependencyA.class") + "`\n" +
|
||||
" \n" +
|
||||
"Process [PID=111, name=`the-app`]"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -174,18 +201,16 @@ public class AutowiredHoverProviderTest {
|
||||
|
||||
editor.assertHighlights("@Component", "@Autowired");
|
||||
editor.assertTrimmedHover("@Autowired",
|
||||
"**Injection report for Bean [id: autowiredClass, type: `com.example.AutowiredClass`]**\n" +
|
||||
"\n" +
|
||||
"Process [PID=111, name=`the-app`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: autowiredClass, type: `com.example.AutowiredClass`] got autowired with:\n" +
|
||||
"\n" +
|
||||
"- Bean: dependencyA \n" +
|
||||
"**Autowired → `dependencyA` `dependencyB`**\n" +
|
||||
"- Bean: `dependencyA` \n" +
|
||||
" Type: `com.example.DependencyA` \n" +
|
||||
" Resource: `" + Paths.get("com/example/DependencyA.class") + "`\n" +
|
||||
"- Bean: dependencyB \n" +
|
||||
"- Bean: `dependencyB` \n" +
|
||||
" Type: `com.example.DependencyB` \n" +
|
||||
" Resource: `com/example/DependencyB.class`\n"
|
||||
" Resource: `com/example/DependencyB.class`\n" +
|
||||
" \n" +
|
||||
"Process [PID=111, name=`the-app`]\n"
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
@@ -302,13 +327,17 @@ public class AutowiredHoverProviderTest {
|
||||
.add(LiveBean.builder()
|
||||
.id("defaultFoo")
|
||||
.type("com.example.FooImplementation")
|
||||
.dependencies("defaultFoo")
|
||||
.dependencies("otherBean")
|
||||
.dependencies("superBean", "scheduler")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("otherBean")
|
||||
.type("com.example.DependencyA")
|
||||
.id("superBean")
|
||||
.type("com.example.FooImplementation")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("scheduler")
|
||||
.type("org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
@@ -320,43 +349,16 @@ public class AutowiredHoverProviderTest {
|
||||
.build();
|
||||
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.beans.factory.annotation.Autowired;\n" +
|
||||
"import org.springframework.scheduling.TaskScheduler;\n" +
|
||||
"import org.springframework.stereotype.Component;\n" +
|
||||
"\n" +
|
||||
"@Component(\"defaultFoo\")\n" +
|
||||
"public class FooImplementation implements Foo {\n" +
|
||||
" \n" +
|
||||
" private TaskScheduler scheduler;\n" +
|
||||
" \n" +
|
||||
" @Autowired Foo self;\n" +
|
||||
" \n" +
|
||||
" @Override\n" +
|
||||
" public void doSomeFoo() {\n" +
|
||||
" scheduler.scheduleWithFixedDelay(() -> {\n" +
|
||||
" System.out.println(\"Doo Done done!\");\n" +
|
||||
" }, 1000);\n" +
|
||||
" System.out.println(\"Foo do do do do!\");\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" @Autowired\n" +
|
||||
" public void setScheduler(TaskScheduler scheduler) {\n" +
|
||||
" this.scheduler = scheduler;\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
"}"
|
||||
);
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA, FOO_IMPL_CONTENTS);
|
||||
editor.assertHighlights("@Component", "@Autowired", "@Autowired");
|
||||
for (int i = 1; i <= 2; i++) {
|
||||
editor.assertHoverContains("@Autowired", i,
|
||||
"Bean [id: defaultFoo, type: `com.example.FooImplementation`] got autowired with:\n" +
|
||||
"\n" +
|
||||
"- Bean: otherBean \n" +
|
||||
" Type: `com.example.DependencyA`");
|
||||
}
|
||||
editor.assertHoverContains("@Autowired", 1,
|
||||
"**Autowired → `superBean`**\n" +
|
||||
"- Bean: `superBean` \n" +
|
||||
" Type: `com.example.FooImplementation`");
|
||||
editor.assertHoverContains("@Autowired", 2,
|
||||
"**Autowired → `scheduler`**\n" +
|
||||
"- Bean: `scheduler` \n" +
|
||||
" Type: `org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler`");
|
||||
}
|
||||
|
||||
@Test public void bug_152621242_autowired_constructor_on_a_controller() throws Exception {
|
||||
@@ -404,9 +406,8 @@ public class AutowiredHoverProviderTest {
|
||||
);
|
||||
editor.assertHighlights("@Controller", "@Autowired");
|
||||
editor.assertHoverContains("@Autowired",
|
||||
"Bean [id: myController, type: `com.example.MyController`] got autowired with:\n" +
|
||||
"\n" +
|
||||
"- Bean: restTemplate \n" +
|
||||
"**Autowired → `restTemplate`**\n" +
|
||||
"- Bean: `restTemplate` \n" +
|
||||
" Type: `org.springframework.web.client.RestTemplate`"
|
||||
);
|
||||
editor.assertHoverContains("@Controller",
|
||||
|
||||
@@ -215,7 +215,7 @@ public class BeanInjectedIntoHoverProviderTest {
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `hello.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: myController \n" +
|
||||
"- Bean: `myController` \n" +
|
||||
" Type: `hello.MyController`\n"
|
||||
);
|
||||
}
|
||||
@@ -275,7 +275,7 @@ public class BeanInjectedIntoHoverProviderTest {
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `hello.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: myController \n" +
|
||||
"- Bean: `myController` \n" +
|
||||
" Type: `hello.MyController`\n"
|
||||
);
|
||||
}
|
||||
@@ -329,7 +329,7 @@ public class BeanInjectedIntoHoverProviderTest {
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `hello.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: myController \n" +
|
||||
"- Bean: `myController` \n" +
|
||||
" Type: `hello.MyController` \n" +
|
||||
" Resource: `" + Paths.get("hello/MyController.class") + "`"
|
||||
);
|
||||
@@ -383,7 +383,7 @@ public class BeanInjectedIntoHoverProviderTest {
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `hello.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: myController \n" +
|
||||
"- Bean: `myController` \n" +
|
||||
" Type: `hello.MyController` \n" +
|
||||
" Resource: `hello/MyController.class`"
|
||||
);
|
||||
@@ -441,9 +441,9 @@ public class BeanInjectedIntoHoverProviderTest {
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `hello.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: myController \n" +
|
||||
"- Bean: `myController` \n" +
|
||||
" Type: `hello.MyController`\n" +
|
||||
"- Bean: otherBean \n" +
|
||||
"- Bean: `otherBean` \n" +
|
||||
" Type: `hello.OtherBean`\n"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ public class BeansByTypeHoverProviderTest {
|
||||
"\n" +
|
||||
"Bean [id: scannedRandomClass, type: `com.example.ScannedRandomClass`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: randomOtherBean \n" +
|
||||
"- Bean: `randomOtherBean` \n" +
|
||||
" Type: `randomOtherBeanType`"
|
||||
);
|
||||
}
|
||||
@@ -196,11 +196,11 @@ public class BeansByTypeHoverProviderTest {
|
||||
"\n" +
|
||||
"Bean [id: scannedFunctionClass, type: `com.example.ScannedFunctionClass`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration \n" +
|
||||
"- Bean: `org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration` \n" +
|
||||
" Type: `org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration`"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void generalBeanLiveHoverAvoidOverlapWithAnnotation() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
|
||||
@@ -165,7 +165,7 @@ public class ComponentInjectionsHoverProviderTest {
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `com.example.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: myController \n" +
|
||||
"- Bean: `myController` \n" +
|
||||
" Type: `com.example.MyController`"
|
||||
);
|
||||
}
|
||||
@@ -220,9 +220,9 @@ public class ComponentInjectionsHoverProviderTest {
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `com.example.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: myController \n" +
|
||||
"- Bean: `myController` \n" +
|
||||
" Type: `com.example.MyController`\n" +
|
||||
"- Bean: otherBean \n" +
|
||||
"- Bean: `otherBean` \n" +
|
||||
" Type: `com.example.OtherBean`"
|
||||
);
|
||||
}
|
||||
@@ -279,18 +279,18 @@ public class ComponentInjectionsHoverProviderTest {
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `com.example.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: myController \n" +
|
||||
"- Bean: `myController` \n" +
|
||||
" Type: `com.example.MyController`\n" +
|
||||
"- Bean: otherBean \n" +
|
||||
"- Bean: `otherBean` \n" +
|
||||
" Type: `com.example.OtherBean`\n" +
|
||||
"\n" +
|
||||
"Process [PID=1002, name=`app-instance-2`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `com.example.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: myController \n" +
|
||||
"- Bean: `myController` \n" +
|
||||
" Type: `com.example.MyController`\n" +
|
||||
"- Bean: otherBean \n" +
|
||||
"- Bean: `otherBean` \n" +
|
||||
" Type: `com.example.OtherBean`\n"
|
||||
);
|
||||
}
|
||||
@@ -350,7 +350,7 @@ public class ComponentInjectionsHoverProviderTest {
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `com.example.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: myController \n" +
|
||||
"- Bean: `myController` \n" +
|
||||
" Type: `com.example.MyController`"
|
||||
);
|
||||
}
|
||||
@@ -410,7 +410,7 @@ public class ComponentInjectionsHoverProviderTest {
|
||||
"\n" +
|
||||
"Bean [id: alternateFooImplementation, type: `com.example.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: otherBean \n" +
|
||||
"- Bean: `otherBean` \n" +
|
||||
" Type: `com.example.OtherBean`\n"
|
||||
);
|
||||
}
|
||||
@@ -526,10 +526,10 @@ public class ComponentInjectionsHoverProviderTest {
|
||||
"\n\n" +
|
||||
"Bean [id: autowiredClass, type: `com.example.AutowiredClass`] got autowired with:\n" +
|
||||
"\n" +
|
||||
"- Bean: dependencyA \n" +
|
||||
"- Bean: `dependencyA` \n" +
|
||||
" Type: `com.example.DependencyA` \n" +
|
||||
" Resource: `" + Paths.get("com/example/DependencyA.class") + "`\n" +
|
||||
"- Bean: dependencyB \n" +
|
||||
"- Bean: `dependencyB` \n" +
|
||||
" Type: `com.example.DependencyB` \n" +
|
||||
" Resource: `com/example/DependencyB.class`"
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user