enable live hovers for inner classes
This commit is contained in:
@@ -92,6 +92,13 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
|
||||
return ASTUtils.getAttribute(annotation, "value").flatMap(ASTUtils::getFirstString)
|
||||
.orElseGet(() -> {
|
||||
String typeName = beanType.getName();
|
||||
|
||||
ITypeBinding declaringClass = beanType.getDeclaringClass();
|
||||
while (declaringClass != null) {
|
||||
typeName = declaringClass.getName() + "." + typeName;
|
||||
declaringClass = declaringClass.getDeclaringClass();
|
||||
}
|
||||
|
||||
if (StringUtil.hasText(typeName)) {
|
||||
return Character.toLowerCase(typeName.charAt(0)) + typeName.substring(1);
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ public class LiveHoverUtils {
|
||||
|
||||
public static String showBean(LiveBean bean) {
|
||||
StringBuilder buf = new StringBuilder("Bean [id: " + bean.getId());
|
||||
String type = bean.getType();
|
||||
if (type!=null) {
|
||||
buf.append(", type: `"+type+"`");
|
||||
String type = bean.getType(true);
|
||||
if (type != null) {
|
||||
buf.append(", type: `" + type + "`");
|
||||
}
|
||||
buf.append(']');
|
||||
return buf.toString();
|
||||
@@ -35,10 +35,10 @@ public class LiveHoverUtils {
|
||||
String newline = " \n"+indentStr; //Note: the double space before newline makes markdown see it as a real line break
|
||||
StringBuilder buf = new StringBuilder("Bean: ");
|
||||
buf.append(bean.getId());
|
||||
String type = bean.getType();
|
||||
if (type!=null) {
|
||||
String type = bean.getType(true);
|
||||
if (type != null) {
|
||||
buf.append(newline);
|
||||
buf.append("Type: `"+type+"`");
|
||||
buf.append("Type: `" + type + "`");
|
||||
}
|
||||
String resource = bean.getResource();
|
||||
if (StringUtil.hasText(resource)) {
|
||||
|
||||
@@ -119,19 +119,28 @@ public class RequestMappingHoverProvider implements HoverProvider {
|
||||
private boolean methodMatchesAnnotation(Annotation annotation, RequestMapping rm) {
|
||||
String rqClassName = rm.getFullyQualifiedClassName();
|
||||
|
||||
ASTNode parent = annotation.getParent();
|
||||
if (parent instanceof MethodDeclaration) {
|
||||
MethodDeclaration methodDec = (MethodDeclaration) parent;
|
||||
IMethodBinding binding = methodDec.resolveBinding();
|
||||
return binding.getDeclaringClass().getQualifiedName().equals(rqClassName)
|
||||
&& binding.getName().equals(rm.getMethodName())
|
||||
&& Arrays.equals(Arrays.stream(binding.getParameterTypes())
|
||||
.map(t -> t.getTypeDeclaration().getQualifiedName())
|
||||
.toArray(String[]::new),
|
||||
rm.getMethodParameters());
|
||||
// } else if (parent instanceof TypeDeclaration) {
|
||||
// TypeDeclaration typeDec = (TypeDeclaration) parent;
|
||||
// return typeDec.resolveBinding().getQualifiedName().equals(rqClassName);
|
||||
if (rqClassName != null) {
|
||||
int chop = rqClassName.indexOf("$$EnhancerBySpringCGLIB$$");
|
||||
if (chop >= 0) {
|
||||
rqClassName = rqClassName.substring(0, chop);
|
||||
}
|
||||
|
||||
rqClassName = rqClassName.replace('$', '.');
|
||||
|
||||
ASTNode parent = annotation.getParent();
|
||||
if (parent instanceof MethodDeclaration) {
|
||||
MethodDeclaration methodDec = (MethodDeclaration) parent;
|
||||
IMethodBinding binding = methodDec.resolveBinding();
|
||||
return binding.getDeclaringClass().getQualifiedName().equals(rqClassName)
|
||||
&& binding.getName().equals(rm.getMethodName())
|
||||
&& Arrays.equals(Arrays.stream(binding.getParameterTypes())
|
||||
.map(t -> t.getTypeDeclaration().getQualifiedName())
|
||||
.toArray(String[]::new),
|
||||
rm.getMethodParameters());
|
||||
// } else if (parent instanceof TypeDeclaration) {
|
||||
// TypeDeclaration typeDec = (TypeDeclaration) parent;
|
||||
// return typeDec.resolveBinding().getQualifiedName().equals(rqClassName);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -219,6 +219,66 @@ public class BeanInjectedIntoHoverProviderTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanFromInnerClassWithOneInjection() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("fooImplementation")
|
||||
.type("hello.FooImplementation")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("myController")
|
||||
.type("hello.MyController")
|
||||
.dependencies("fooImplementation")
|
||||
.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 hello;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.context.annotation.Bean;\n" +
|
||||
"import org.springframework.context.annotation.Configuration;\n" +
|
||||
"import org.springframework.context.annotation.Profile;\n" +
|
||||
"\n" +
|
||||
"public class OuterClass {\n" +
|
||||
" \n" +
|
||||
" @Configuration\n" +
|
||||
" public static class InnerClass {\n" +
|
||||
" \n" +
|
||||
" @Bean(\"fooImplementation\")\n" +
|
||||
" Foo someFoo() {\n" +
|
||||
" return new FooImplementation();\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}"
|
||||
);
|
||||
editor.assertHighlights("@Bean");
|
||||
editor.assertTrimmedHover("@Bean",
|
||||
"**Injection report for Bean [id: fooImplementation]**\n" +
|
||||
"\n" +
|
||||
"Process [PID=111, name=`the-app`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `hello.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: myController \n" +
|
||||
" Type: `hello.MyController`\n"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanWithFileResource() throws Exception {
|
||||
Path of = harness.getOutputFolder();
|
||||
|
||||
@@ -585,7 +585,8 @@ public class ComponentInjectionsHoverProviderTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void bug_153072942_SpringBootApplication__withCGLib_is_a_Component() throws Exception {
|
||||
@Test
|
||||
public void bug_153072942_SpringBootApplication__withCGLib_is_a_Component() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("demoApplication")
|
||||
@@ -623,4 +624,88 @@ public class ComponentInjectionsHoverProviderTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void componentFromInnerClass() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("demoApplication.InnerClass")
|
||||
.type("com.example.DemoApplication$InnerClass")
|
||||
.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.boot.SpringApplication;\n" +
|
||||
"import org.springframework.boot.autoconfigure.SpringBootApplication;\n" +
|
||||
"import org.springframework.context.annotation.Bean;\n" +
|
||||
"import org.springframework.web.client.RestTemplate;\n" +
|
||||
"\n" +
|
||||
"public class DemoApplication {\n" +
|
||||
" \n" +
|
||||
" @SpringBootApplication\n" +
|
||||
" public static class InnerClass {\n" +
|
||||
" \n" +
|
||||
" public static void main(String[] args) {\n" +
|
||||
" SpringApplication.run(DemoApplication.InnerClass.class, args);\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
editor.assertHighlights("@SpringBootApplication");
|
||||
editor.assertHoverContains("@SpringBootApplication",
|
||||
"**Injection report for Bean [id: demoApplication.InnerClass, type: `com.example.DemoApplication.InnerClass`]**"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void componentFromInnerInnerClass() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("demoApplication.InnerClass.InnerInnerClass")
|
||||
.type("com.example.DemoApplication$InnerClass$InnerInnerClass")
|
||||
.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.boot.SpringApplication;\n" +
|
||||
"import org.springframework.boot.autoconfigure.SpringBootApplication;\n" +
|
||||
"import org.springframework.context.annotation.Bean;\n" +
|
||||
"import org.springframework.web.client.RestTemplate;\n" +
|
||||
"\n" +
|
||||
"public class DemoApplication {\n" +
|
||||
" \n" +
|
||||
" public static class InnerClass {\n" +
|
||||
" \n" +
|
||||
" @SpringBootApplication\n" +
|
||||
" public static class InnerInnerClass {\n" +
|
||||
" \n" +
|
||||
" public static void main(String[] args) {\n" +
|
||||
" SpringApplication.run(DemoApplication.InnerClass.InnerInnerClass.class, args);\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
editor.assertHighlights("@SpringBootApplication");
|
||||
editor.assertHoverContains("@SpringBootApplication",
|
||||
"**Injection report for Bean [id: demoApplication.InnerClass.InnerInnerClass, type: `com.example.DemoApplication.InnerClass.InnerInnerClass`]**"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ public class RequestMappingLiveHoverTest {
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
|
||||
editor.assertHighlights("@RequestMapping(method=RequestMethod.GET)");
|
||||
editor.assertHoverContains("@RequestMapping(method=RequestMethod.GET)", "[http://cfapps.io:1111/hello-world](http://cfapps.io:1111/hello-world)\n" +
|
||||
"\n" +
|
||||
"Process [PID=22022, name=`test-request-mapping-live-hover`]");
|
||||
@@ -94,6 +95,8 @@ public class RequestMappingLiveHoverTest {
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
|
||||
editor.assertHighlights("@RequestMapping(\"/hello\")", "@RequestMapping(\"/goodbye\")");
|
||||
|
||||
editor.assertHoverContains("@RequestMapping(\"/hello\")", "[http://cfapps.io:999/hello](http://cfapps.io:999/hello)\n" +
|
||||
"\n" +
|
||||
"Process [PID=76543, name=`test-request-mapping-live-hover`]");
|
||||
@@ -214,12 +217,8 @@ public class RequestMappingLiveHoverTest {
|
||||
docUri);
|
||||
|
||||
editor.assertNoHover("@DeleteMapping(\"/greetings\")");
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testNoHoverHintMethod() throws Exception {
|
||||
|
||||
@@ -261,7 +260,6 @@ public class RequestMappingLiveHoverTest {
|
||||
docUri);
|
||||
|
||||
editor.assertNoHover("@PutMapping(\"/greetings\")");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -756,4 +754,39 @@ public class RequestMappingLiveHoverTest {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMappingHoversInInnerClasses() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
|
||||
String docUri = "file://" +directory.getAbsolutePath() + "/src/main/java/example/InnerClassController.java";
|
||||
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.port("1111")
|
||||
.processId("22022")
|
||||
.host("cfapps.io")
|
||||
.processName("test-request-mapping-live-hover")
|
||||
// Ugly, but this is real JSON copied from a real live running app. We want the
|
||||
// mock app to return realistic results if possible
|
||||
.requestMappings(
|
||||
"{\"/webjars/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**/favicon.ico\":{\"bean\":\"faviconHandlerMapping\"},\"{[/hello-world],methods=[GET]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public example.Greeting example.HelloWorldController.sayHello(java.lang.String)\"},\"{[/inner-inner-class]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.InnerClassController$InnerController$InnerInnerController.saySomethingSuperInnerClass()\"},\"{[/inner-class]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.InnerClassController$InnerController.saySomething()\"},\"{[/person/{name}],methods=[GET]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.getMapping(java.lang.String)\"},\"{[/delete/{id}],methods=[DELETE]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.removeMe(int)\"},\"{[/goodbye]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.goodbye()\"},\"{[/hello]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.hello()\"},\"{[/postHello],methods=[POST]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.postMethod(java.lang.String)\"},\"{[/put/{id}],methods=[PUT]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.putMethod(int,java.lang.String)\"},\"{[/error]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)\"},\"{[/error],produces=[text/html]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)\"}}")
|
||||
.build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
|
||||
editor.assertHighlights("@RequestMapping(\"/inner-class\")", "@RequestMapping(\"/inner-inner-class\")");
|
||||
|
||||
editor.assertHoverContains("@RequestMapping(\"/inner-class\")", "[http://cfapps.io:1111/inner-class](http://cfapps.io:1111/inner-class)\n" +
|
||||
"\n" +
|
||||
"Process [PID=22022, name=`test-request-mapping-live-hover`]");
|
||||
|
||||
editor.assertHoverContains("@RequestMapping(\"/inner-inner-class\")", "[http://cfapps.io:1111/inner-inner-class](http://cfapps.io:1111/inner-inner-class)\n" +
|
||||
"\n" +
|
||||
"Process [PID=22022, name=`test-request-mapping-live-hover`]");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package example;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
public class InnerClassController {
|
||||
|
||||
@RestController
|
||||
public static class InnerController {
|
||||
|
||||
@RequestMapping("/inner-class")
|
||||
public String saySomething() {
|
||||
return "Hello from Inner-Class";
|
||||
}
|
||||
|
||||
@RestController
|
||||
public static class InnerInnerController {
|
||||
|
||||
@RequestMapping("/inner-inner-class")
|
||||
public String saySomethingSuperInnerClass() {
|
||||
return "Hello from Inner-Inner-Class";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user