GH-1287: to go definition functionality added for classpath resources in value annotations
This commit is contained in:
@@ -67,7 +67,7 @@ import org.springframework.ide.vscode.boot.java.reconcilers.JavaReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.JdtAstReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.JdtReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
|
||||
import org.springframework.ide.vscode.boot.java.value.PropertyValueAnnotationDefProvider;
|
||||
import org.springframework.ide.vscode.boot.java.value.ValueDefinitionProvider;
|
||||
import org.springframework.ide.vscode.boot.jdt.ls.JavaProjectsService;
|
||||
import org.springframework.ide.vscode.boot.jdt.ls.JdtLsProjectCache;
|
||||
import org.springframework.ide.vscode.boot.metadata.AdHocSpringPropertyIndexProvider;
|
||||
@@ -395,7 +395,7 @@ public class BootLanguageServerBootApp {
|
||||
@Bean
|
||||
JavaDefinitionHandler javaDefinitionHandler(CompilationUnitCache cuCache, JavaProjectFinder projectFinder, SpringMetamodelIndex springIndex) {
|
||||
return new JavaDefinitionHandler(cuCache, projectFinder, List.of(
|
||||
new PropertyValueAnnotationDefProvider(),
|
||||
new ValueDefinitionProvider(),
|
||||
new DependsOnDefinitionProvider(springIndex),
|
||||
new QualifierDefinitionProvider(springIndex)));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2023 VMware, Inc.
|
||||
* Copyright (c) 2023, 2024 VMware, 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
|
||||
@@ -10,7 +10,10 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.value;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -42,9 +45,9 @@ import org.yaml.snakeyaml.nodes.Node;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableList.Builder;
|
||||
|
||||
public class PropertyValueAnnotationDefProvider implements IJavaDefinitionProvider {
|
||||
public class ValueDefinitionProvider implements IJavaDefinitionProvider {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PropertyValueAnnotationDefProvider.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(ValueDefinitionProvider.class);
|
||||
|
||||
private static final String PARAM_VALUE = "value";
|
||||
private static final String PARAM_NAME = "name";
|
||||
@@ -77,61 +80,76 @@ public class PropertyValueAnnotationDefProvider implements IJavaDefinitionProvid
|
||||
);
|
||||
|
||||
@Override
|
||||
public List<LocationLink> getDefinitions(CancelChecker cancelToken, IJavaProject project, CompilationUnit cu,
|
||||
ASTNode n) {
|
||||
public List<LocationLink> getDefinitions(CancelChecker cancelToken, IJavaProject project, CompilationUnit cu, ASTNode n) {
|
||||
|
||||
if (n instanceof StringLiteral) {
|
||||
StringLiteral valueNode = (StringLiteral) n;
|
||||
String propertyKey = null;
|
||||
|
||||
ASTNode parent = valueNode.getParent();
|
||||
if (parent instanceof Annotation) {
|
||||
Annotation a = (Annotation) parent;
|
||||
IAnnotationBinding binding = a.resolveAnnotationBinding();
|
||||
if (binding != null && binding.getAnnotationType() != null) {
|
||||
PropertyKeyExtractor propertyExtractor = annotationToPropertyKeyExtractor.get(binding.getAnnotationType().getQualifiedName());
|
||||
if (propertyExtractor != null) {
|
||||
propertyKey = propertyExtractor.extract(a, null, valueNode);
|
||||
}
|
||||
String literalValue = valueNode.getLiteralValue();
|
||||
if (literalValue != null) {
|
||||
if (literalValue.startsWith("classpath")) {
|
||||
return getDefinitionForClasspathResource(project, cu, valueNode, literalValue);
|
||||
}
|
||||
} else if (parent instanceof MemberValuePair
|
||||
&& parent.getParent() instanceof Annotation) {
|
||||
MemberValuePair pair = (MemberValuePair) parent;
|
||||
Annotation a = (Annotation) parent.getParent();
|
||||
IAnnotationBinding binding = a.resolveAnnotationBinding();
|
||||
if (binding != null && binding.getAnnotationType() != null) {
|
||||
PropertyKeyExtractor propertyExtractor = annotationToPropertyKeyExtractor.get(binding.getAnnotationType().getQualifiedName());
|
||||
if (propertyExtractor != null) {
|
||||
propertyKey = propertyExtractor.extract(a, pair, valueNode);
|
||||
}
|
||||
else {
|
||||
return getDefinitionForProperty(project, cu, valueNode);
|
||||
}
|
||||
}
|
||||
|
||||
if (propertyKey != null) {
|
||||
Builder<LocationLink> builder = ImmutableList.builder();
|
||||
Map<Location, Range> targetRanges = new HashMap<>();
|
||||
|
||||
Position startPosition = new Position(cu.getLineNumber(valueNode.getStartPosition()) - 1,
|
||||
cu.getColumnNumber(valueNode.getStartPosition()));
|
||||
Position endPosition = new Position(
|
||||
cu.getLineNumber(valueNode.getStartPosition() + valueNode.getLength()) - 1,
|
||||
cu.getColumnNumber(valueNode.getStartPosition() + valueNode.getLength()));
|
||||
Range originRange = new Range(startPosition, endPosition);
|
||||
|
||||
|
||||
for (Location location : findValueReferences(project, propertyKey, targetRanges)) {
|
||||
LocationLink ll = new LocationLink();
|
||||
ll.setTargetUri(location.getUri());
|
||||
ll.setTargetSelectionRange(location.getRange());
|
||||
ll.setTargetRange(targetRanges.get(location));
|
||||
ll.setOriginSelectionRange(originRange);
|
||||
builder.add(ll);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private List<LocationLink> getDefinitionForProperty(IJavaProject project, CompilationUnit cu, StringLiteral valueNode) {
|
||||
String propertyKey = null;
|
||||
|
||||
ASTNode parent = valueNode.getParent();
|
||||
if (parent instanceof Annotation) {
|
||||
Annotation a = (Annotation) parent;
|
||||
IAnnotationBinding binding = a.resolveAnnotationBinding();
|
||||
if (binding != null && binding.getAnnotationType() != null) {
|
||||
PropertyKeyExtractor propertyExtractor = annotationToPropertyKeyExtractor.get(binding.getAnnotationType().getQualifiedName());
|
||||
if (propertyExtractor != null) {
|
||||
propertyKey = propertyExtractor.extract(a, null, valueNode);
|
||||
}
|
||||
}
|
||||
} else if (parent instanceof MemberValuePair
|
||||
&& parent.getParent() instanceof Annotation) {
|
||||
MemberValuePair pair = (MemberValuePair) parent;
|
||||
Annotation a = (Annotation) parent.getParent();
|
||||
IAnnotationBinding binding = a.resolveAnnotationBinding();
|
||||
if (binding != null && binding.getAnnotationType() != null) {
|
||||
PropertyKeyExtractor propertyExtractor = annotationToPropertyKeyExtractor.get(binding.getAnnotationType().getQualifiedName());
|
||||
if (propertyExtractor != null) {
|
||||
propertyKey = propertyExtractor.extract(a, pair, valueNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (propertyKey != null) {
|
||||
Builder<LocationLink> builder = ImmutableList.builder();
|
||||
Map<Location, Range> targetRanges = new HashMap<>();
|
||||
|
||||
Position startPosition = new Position(cu.getLineNumber(valueNode.getStartPosition()) - 1,
|
||||
cu.getColumnNumber(valueNode.getStartPosition()));
|
||||
Position endPosition = new Position(
|
||||
cu.getLineNumber(valueNode.getStartPosition() + valueNode.getLength()) - 1,
|
||||
cu.getColumnNumber(valueNode.getStartPosition() + valueNode.getLength()));
|
||||
Range originRange = new Range(startPosition, endPosition);
|
||||
|
||||
|
||||
for (Location location : findValueReferences(project, propertyKey, targetRanges)) {
|
||||
LocationLink ll = new LocationLink();
|
||||
ll.setTargetUri(location.getUri());
|
||||
ll.setTargetSelectionRange(location.getRange());
|
||||
ll.setTargetRange(targetRanges.get(location));
|
||||
ll.setOriginSelectionRange(originRange);
|
||||
builder.add(ll);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private List<Location> findValueReferences(IJavaProject project, String propertyKey, Map<Location, Range> targetRanges) {
|
||||
Builder<Location> links = ImmutableList.builder();
|
||||
IClasspathUtil.getClasspathResourcesFullPaths(project.getClasspath()).forEach(path -> {
|
||||
@@ -229,4 +247,42 @@ public class PropertyValueAnnotationDefProvider implements IJavaDefinitionProvid
|
||||
String extract(Annotation a, MemberValuePair pair, StringLiteral v);
|
||||
}
|
||||
|
||||
private List<LocationLink> getDefinitionForClasspathResource(IJavaProject project, CompilationUnit cu, StringLiteral valueNode, String literalValue) {
|
||||
literalValue = literalValue.substring("classpath:".length());
|
||||
|
||||
String[] resources = findResources(project, literalValue);
|
||||
|
||||
List<LocationLink> result = new ArrayList<>();
|
||||
|
||||
for (String resource : resources) {
|
||||
String uri = "file://" + resource;
|
||||
|
||||
Position startPosition = new Position(cu.getLineNumber(valueNode.getStartPosition()) - 1,
|
||||
cu.getColumnNumber(valueNode.getStartPosition()));
|
||||
Position endPosition = new Position(
|
||||
cu.getLineNumber(valueNode.getStartPosition() + valueNode.getLength()) - 1,
|
||||
cu.getColumnNumber(valueNode.getStartPosition() + valueNode.getLength()));
|
||||
Range nodeRange = new Range(startPosition, endPosition);
|
||||
|
||||
LocationLink locationLink = new LocationLink(uri,
|
||||
new Range(new Position(0, 0), new Position(0, 0)), new Range(new Position(0, 0), new Position(0, 0)),
|
||||
nodeRange);
|
||||
|
||||
result.add(locationLink);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private String[] findResources(IJavaProject project, String resource) {
|
||||
String[] resources = IClasspathUtil.getClasspathResourcesFullPaths(project.getClasspath())
|
||||
.filter(path -> path.toString().endsWith(resource))
|
||||
.map(path -> path.toString())
|
||||
.toArray(String[]::new);
|
||||
|
||||
return resources;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2023 VMware, Inc.
|
||||
* Copyright (c) 2023, 2024 VMware, 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
|
||||
@@ -40,7 +40,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@BootLanguageServerTest
|
||||
@Import({ AdHocPropertyHarnessTestConf.class, ValueCompletionTest.TestConf.class })
|
||||
public class PropertyValueAnnotationDefProviderTest {
|
||||
public class ValueDefinitionProviderTest {
|
||||
|
||||
@Autowired
|
||||
private BootLanguageServerHarness harness;
|
||||
@@ -207,4 +207,29 @@ public class PropertyValueAnnotationDefProviderTest {
|
||||
|
||||
editor.assertLinkTargets("prop", List.of(expectedLocation));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindClasspathResource() throws Exception {
|
||||
Path randomResourceFilePath = projectFile("src/main/resources/random-resource.md", "");
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA, """
|
||||
package org.test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
public class TestValueCompletion {
|
||||
|
||||
@Value("classpath:random-resource.md")
|
||||
private String value1;
|
||||
}""");
|
||||
|
||||
LocationLink expectedLocation = new LocationLink(randomResourceFilePath.toUri().toASCIIString(),
|
||||
new Range(new Position(0, 0), new Position(0, 0)),
|
||||
new Range(new Position(0, 0), new Position(0, 0)),
|
||||
new Range(new Position(6, 8), new Position(6, 38)));
|
||||
|
||||
editor.assertLinkTargets("classpath:random-resource.md", List.of(expectedLocation));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user