GH-1298: added bean name code completion and navigation support for name attribute of resource annotation
This commit is contained in:
@@ -28,6 +28,7 @@ import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchie
|
||||
import org.springframework.ide.vscode.boot.java.beans.DependsOnCompletionProcessor;
|
||||
import org.springframework.ide.vscode.boot.java.beans.ProfileCompletionProvider;
|
||||
import org.springframework.ide.vscode.boot.java.beans.QualifierCompletionProvider;
|
||||
import org.springframework.ide.vscode.boot.java.beans.ResourceCompletionProvider;
|
||||
import org.springframework.ide.vscode.boot.java.data.DataRepositoryCompletionProcessor;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.BootJavaCompletionEngine;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.CompletionProvider;
|
||||
@@ -121,6 +122,9 @@ public class BootJavaCompletionEngineConfigurer {
|
||||
providers.put(Annotations.QUALIFIER, new AnnotationAttributeCompletionProcessor(javaProjectFinder, Map.of("value", new QualifierCompletionProvider(springIndex))));
|
||||
providers.put(Annotations.PROFILE, new AnnotationAttributeCompletionProcessor(javaProjectFinder, Map.of("value", new ProfileCompletionProvider(springIndex))));
|
||||
|
||||
providers.put(Annotations.RESOURCE_JAVAX, new AnnotationAttributeCompletionProcessor(javaProjectFinder, Map.of("name", new ResourceCompletionProvider(springIndex))));
|
||||
providers.put(Annotations.RESOURCE_JAKARTA, new AnnotationAttributeCompletionProcessor(javaProjectFinder, Map.of("name", new ResourceCompletionProvider(springIndex))));
|
||||
|
||||
return new BootJavaCompletionEngine(cuCache, providers, snippetManager);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ import org.springframework.ide.vscode.boot.index.cache.IndexCacheVoid;
|
||||
import org.springframework.ide.vscode.boot.java.JavaDefinitionHandler;
|
||||
import org.springframework.ide.vscode.boot.java.beans.DependsOnDefinitionProvider;
|
||||
import org.springframework.ide.vscode.boot.java.beans.QualifierDefinitionProvider;
|
||||
import org.springframework.ide.vscode.boot.java.beans.ResourceDefinitionProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.BootJavaCodeActionProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.BootJavaReconcileEngine;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.JavaCodeActionHandler;
|
||||
@@ -397,6 +398,7 @@ public class BootLanguageServerBootApp {
|
||||
return new JavaDefinitionHandler(cuCache, projectFinder, List.of(
|
||||
new ValueDefinitionProvider(),
|
||||
new DependsOnDefinitionProvider(springIndex),
|
||||
new ResourceDefinitionProvider(springIndex),
|
||||
new QualifierDefinitionProvider(springIndex)));
|
||||
}
|
||||
|
||||
|
||||
@@ -61,39 +61,41 @@ public class AnnotationAttributeCompletionProcessor implements CompletionProvide
|
||||
|
||||
// case: @Qualifier(<*>)
|
||||
if (node == annotation && doc.get(offset - 1, 2).endsWith("()")) {
|
||||
createCompletionProposals(project, doc, node, completions, offset, offset, "", (beanName) -> "\"" + beanName + "\"");
|
||||
createCompletionProposals(project, doc, node, "value", completions, offset, offset, "", (beanName) -> "\"" + beanName + "\"");
|
||||
}
|
||||
// case: @Qualifier(prefix<*>)
|
||||
else if (node instanceof SimpleName && node.getParent() instanceof Annotation) {
|
||||
computeProposalsForSimpleName(project, node, completions, offset, doc);
|
||||
computeProposalsForSimpleName(project, node, "value", completions, offset, doc);
|
||||
}
|
||||
// case: @Qualifier(value=<*>)
|
||||
else if (node instanceof SimpleName && node.getParent() instanceof MemberValuePair
|
||||
&& "value".equals(((MemberValuePair)node.getParent()).getName().toString())) {
|
||||
computeProposalsForSimpleName(project, node, completions, offset, doc);
|
||||
&& completionProviders.containsKey(((MemberValuePair)node.getParent()).getName().toString())) {
|
||||
String attributeName = ((MemberValuePair)node.getParent()).getName().toString();
|
||||
computeProposalsForSimpleName(project, node, attributeName, completions, offset, doc);
|
||||
}
|
||||
// case: @Qualifier("prefix<*>")
|
||||
else if (node instanceof StringLiteral && node.getParent() instanceof Annotation) {
|
||||
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
|
||||
computeProposalsForStringLiteral(project, node, completions, offset, doc);
|
||||
computeProposalsForStringLiteral(project, node, "value", completions, offset, doc);
|
||||
}
|
||||
}
|
||||
// case: @Qualifier({"prefix<*>"})
|
||||
else if (node instanceof StringLiteral && node.getParent() instanceof ArrayInitializer) {
|
||||
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
|
||||
computeProposalsForInsideArrayInitializer(project, node, completions, offset, doc);
|
||||
computeProposalsForInsideArrayInitializer(project, node, "value", completions, offset, doc);
|
||||
}
|
||||
}
|
||||
// case: @Qualifier(value="prefix<*>")
|
||||
else if (node instanceof StringLiteral && node.getParent() instanceof MemberValuePair
|
||||
&& "value".equals(((MemberValuePair)node.getParent()).getName().toString())) {
|
||||
&& completionProviders.containsKey(((MemberValuePair)node.getParent()).getName().toString())) {
|
||||
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
|
||||
computeProposalsForStringLiteral(project, node, completions, offset, doc);
|
||||
String attributeName = ((MemberValuePair)node.getParent()).getName().toString();
|
||||
computeProposalsForStringLiteral(project, node, attributeName, completions, offset, doc);
|
||||
}
|
||||
}
|
||||
// case: @Qualifier({<*>})
|
||||
else if (node instanceof ArrayInitializer && node.getParent() instanceof Annotation) {
|
||||
computeProposalsForArrayInitializr(project, (ArrayInitializer) node, completions, offset, doc);
|
||||
computeProposalsForArrayInitializr(project, (ArrayInitializer) node, "value", completions, offset, doc);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
@@ -104,12 +106,12 @@ public class AnnotationAttributeCompletionProcessor implements CompletionProvide
|
||||
/**
|
||||
* create the concrete completion proposal
|
||||
*/
|
||||
private void createCompletionProposals(IJavaProject project, TextDocument doc, ASTNode node, Collection<ICompletionProposal> completions, int startOffset, int endOffset,
|
||||
private void createCompletionProposals(IJavaProject project, TextDocument doc, ASTNode node, String attributeName, Collection<ICompletionProposal> completions, int startOffset, int endOffset,
|
||||
String filterPrefix, Function<String, String> createReplacementText) {
|
||||
|
||||
Set<String> alreadyMentionedValues = alreadyMentionedValues(node);
|
||||
|
||||
AnnotationAttributeCompletionProvider completionProvider = this.completionProviders.get("value");
|
||||
AnnotationAttributeCompletionProvider completionProvider = this.completionProviders.get(attributeName);
|
||||
if (completionProvider != null) {
|
||||
List<String> candidates = completionProvider.getCompletionCandidates(project);
|
||||
|
||||
@@ -135,7 +137,7 @@ public class AnnotationAttributeCompletionProcessor implements CompletionProvide
|
||||
// internal computation of the right positions, prefixes, etc.
|
||||
//
|
||||
|
||||
private void computeProposalsForSimpleName(IJavaProject project, ASTNode node, Collection<ICompletionProposal> completions, int offset, TextDocument doc) {
|
||||
private void computeProposalsForSimpleName(IJavaProject project, ASTNode node, String attributeName, Collection<ICompletionProposal> completions, int offset, TextDocument doc) {
|
||||
String prefix = identifyPropertyPrefix(node.toString(), offset - node.getStartPosition());
|
||||
|
||||
int startOffset = node.getStartPosition();
|
||||
@@ -144,30 +146,30 @@ public class AnnotationAttributeCompletionProcessor implements CompletionProvide
|
||||
String proposalPrefix = "\"";
|
||||
String proposalPostfix = "\"";
|
||||
|
||||
createCompletionProposals(project, doc, node, completions, startOffset, endOffset, prefix, (beanName) -> proposalPrefix + beanName + proposalPostfix);
|
||||
createCompletionProposals(project, doc, node, attributeName, completions, startOffset, endOffset, prefix, (beanName) -> proposalPrefix + beanName + proposalPostfix);
|
||||
}
|
||||
|
||||
private void computeProposalsForStringLiteral(IJavaProject project, ASTNode node, Collection<ICompletionProposal> completions, int offset, TextDocument doc) throws BadLocationException {
|
||||
private void computeProposalsForStringLiteral(IJavaProject project, ASTNode node, String attributeName, Collection<ICompletionProposal> completions, int offset, TextDocument doc) throws BadLocationException {
|
||||
int length = offset - (node.getStartPosition() + 1);
|
||||
|
||||
String prefix = identifyPropertyPrefix(doc.get(node.getStartPosition() + 1, length), length);
|
||||
int startOffset = offset - prefix.length();
|
||||
int endOffset = node.getStartPosition() + node.getLength() - 1;
|
||||
|
||||
createCompletionProposals(project, doc, node, completions, startOffset, endOffset, prefix, (beanName) -> beanName);
|
||||
createCompletionProposals(project, doc, node, attributeName, completions, startOffset, endOffset, prefix, (beanName) -> beanName);
|
||||
}
|
||||
|
||||
private void computeProposalsForArrayInitializr(IJavaProject project, ArrayInitializer node, Collection<ICompletionProposal> completions, int offset, TextDocument doc) {
|
||||
createCompletionProposals(project, doc, node, completions, offset, offset, "", (beanName) -> "\"" + beanName + "\"");
|
||||
private void computeProposalsForArrayInitializr(IJavaProject project, ArrayInitializer node, String attributeName, Collection<ICompletionProposal> completions, int offset, TextDocument doc) {
|
||||
createCompletionProposals(project, doc, node, attributeName, completions, offset, offset, "", (beanName) -> "\"" + beanName + "\"");
|
||||
}
|
||||
|
||||
private void computeProposalsForInsideArrayInitializer(IJavaProject project, ASTNode node, Collection<ICompletionProposal> completions, int offset, TextDocument doc) throws BadLocationException {
|
||||
private void computeProposalsForInsideArrayInitializer(IJavaProject project, ASTNode node, String attributeName, Collection<ICompletionProposal> completions, int offset, TextDocument doc) throws BadLocationException {
|
||||
int length = offset - (node.getStartPosition() + 1);
|
||||
if (length >= 0) {
|
||||
computeProposalsForStringLiteral(project, node, completions, offset, doc);
|
||||
computeProposalsForStringLiteral(project, node, attributeName, completions, offset, doc);
|
||||
}
|
||||
else {
|
||||
createCompletionProposals(project, doc, node, completions, offset, offset, "", (beanName) -> "\"" + beanName + "\",");
|
||||
createCompletionProposals(project, doc, node, attributeName, completions, offset, offset, "", (beanName) -> "\"" + beanName + "\",");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2024 Broadcom
|
||||
* 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
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Broadcom - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.beans;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
|
||||
import org.springframework.ide.vscode.boot.java.annotations.AnnotationAttributeCompletionProvider;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class ResourceCompletionProvider implements AnnotationAttributeCompletionProvider {
|
||||
|
||||
private final SpringMetamodelIndex springIndex;
|
||||
|
||||
public ResourceCompletionProvider(SpringMetamodelIndex springIndex) {
|
||||
this.springIndex = springIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getCompletionCandidates(IJavaProject project) {
|
||||
|
||||
Bean[] beans = this.springIndex.getBeansOfProject(project.getElementName());
|
||||
|
||||
return Arrays.stream(beans).map(bean -> bean.getName())
|
||||
.distinct()
|
||||
.toList();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2024 Broadcom
|
||||
* 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
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Broadcom - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.beans;
|
||||
|
||||
import java.util.Arrays;
|
||||
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.CompilationUnit;
|
||||
import org.eclipse.jdt.core.dom.IAnnotationBinding;
|
||||
import org.eclipse.jdt.core.dom.StringLiteral;
|
||||
import org.eclipse.lsp4j.LocationLink;
|
||||
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
|
||||
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.IJavaDefinitionProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class ResourceDefinitionProvider implements IJavaDefinitionProvider {
|
||||
|
||||
private final SpringMetamodelIndex springIndex;
|
||||
|
||||
public ResourceDefinitionProvider(SpringMetamodelIndex springIndex) {
|
||||
this.springIndex = springIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LocationLink> getDefinitions(CancelChecker cancelToken, IJavaProject project, CompilationUnit cu, ASTNode n) {
|
||||
if (n instanceof StringLiteral) {
|
||||
StringLiteral valueNode = (StringLiteral) n;
|
||||
|
||||
ASTNode parent = ASTUtils.getNearestAnnotationParent(valueNode);
|
||||
|
||||
if (parent != null && parent instanceof Annotation) {
|
||||
Annotation a = (Annotation) parent;
|
||||
IAnnotationBinding binding = a.resolveAnnotationBinding();
|
||||
if (binding != null && binding.getAnnotationType() != null
|
||||
&& (Annotations.RESOURCE_JAVAX.equals(binding.getAnnotationType().getQualifiedName())
|
||||
|| Annotations.RESOURCE_JAKARTA.equals(binding.getAnnotationType().getQualifiedName()))) {
|
||||
String beanName = valueNode.getLiteralValue();
|
||||
|
||||
if (beanName != null && beanName.length() > 0) {
|
||||
return findBeansWithName(project, beanName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private List<LocationLink> findBeansWithName(IJavaProject project, String beanName) {
|
||||
Bean[] beans = this.springIndex.getBeansWithName(project.getElementName(), beanName);
|
||||
|
||||
return Arrays.stream(beans)
|
||||
.map(bean -> {
|
||||
return new LocationLink(bean.getLocation().getUri(), bean.getLocation().getRange(), bean.getLocation().getRange());
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2024 Broadcom
|
||||
* 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
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Broadcom - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.beans.test;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.lsp4j.CompletionItem;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.Position;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
|
||||
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
|
||||
import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf;
|
||||
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@BootLanguageServerTest
|
||||
@Import(SymbolProviderTestConf.class)
|
||||
public class ResourceCompletionProviderTest {
|
||||
|
||||
@Autowired private BootLanguageServerHarness harness;
|
||||
@Autowired private JavaProjectFinder projectFinder;
|
||||
@Autowired private SpringMetamodelIndex springIndex;
|
||||
@Autowired private SpringSymbolIndex indexer;
|
||||
|
||||
private File directory;
|
||||
private IJavaProject project;
|
||||
private Bean[] indexedBeans;
|
||||
private String tempJavaDocUri;
|
||||
private Bean bean1;
|
||||
private Bean bean2;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws Exception {
|
||||
harness.intialize(null);
|
||||
|
||||
directory = new File(ProjectsHarness.class.getResource("/test-projects/test-spring-indexing/").toURI());
|
||||
|
||||
String projectDir = directory.toURI().toString();
|
||||
project = projectFinder.find(new TextDocumentIdentifier(projectDir)).get();
|
||||
|
||||
CompletableFuture<Void> initProject = indexer.waitOperation();
|
||||
initProject.get(5, TimeUnit.SECONDS);
|
||||
|
||||
indexedBeans = springIndex.getBeansOfProject(project.getElementName());
|
||||
|
||||
tempJavaDocUri = directory.toPath().resolve("src/main/java/org/test/TempClass.java").toUri().toString();
|
||||
AnnotationMetadata annotationBean1 = new AnnotationMetadata("org.springframework.beans.factory.annotation.Qualifier", false, Map.of("value", new String[] {"quali1"}));
|
||||
AnnotationMetadata annotationBean2 = new AnnotationMetadata("org.springframework.beans.factory.annotation.Qualifier", false, Map.of("value", new String[] {"quali2"}));
|
||||
|
||||
bean1 = new Bean("bean1", "type1", new Location(tempJavaDocUri, new Range(new Position(1,1), new Position(1, 20))), null, null, new AnnotationMetadata[] {annotationBean1});
|
||||
bean2 = new Bean("bean2", "type2", new Location(tempJavaDocUri, new Range(new Position(1,1), new Position(1, 20))), null, null, new AnnotationMetadata[] {annotationBean2});
|
||||
|
||||
springIndex.updateBeans(project.getElementName(), new Bean[] {bean1, bean2});
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void restoreIndexState() {
|
||||
this.springIndex.updateBeans(project.getElementName(), indexedBeans);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceNameCompletionWithoutQuotesWithAttributeName() throws Exception {
|
||||
assertCompletions("@Resource(name=<*>)", new String[] {"bean1", "bean2"}, 0, "@Resource(name=\"bean1\"<*>)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceCompletionInsideOfQuotesWithoutPrefix() throws Exception {
|
||||
assertCompletions("@Resource(name=\"<*>\")", new String[] {"bean1", "bean2"}, 0, "@Resource(name=\"bean1<*>\")");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceCompletionInsideOfQuotesWithPrefix() throws Exception {
|
||||
assertCompletions("@Resource(name=\"be<*>\")", new String[] {"bean1", "bean2"}, 0, "@Resource(name=\"bean1<*>\")");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceCompletionInsideOfQuotesWithCompletePrefix() throws Exception {
|
||||
assertCompletions("@Resource(name=\"bean1<*>\")", new String[] {"bean1"}, 0, "@Resource(name=\"bean1<*>\")");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceCompletionInsideOfQuotesWithPrefixButWithoutMatches() throws Exception {
|
||||
assertCompletions("@Resource(name=\"XXX<*>\")", 0, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceCompletionOutsideOfAnnotation1() throws Exception {
|
||||
assertCompletions("@Resource(name=\"XXX\")<*>", 0, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceCompletionOutsideOfAnnotation2() throws Exception {
|
||||
assertCompletions("@Resource<*>(name=\"XXX\")", 0, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQualifierCompletionInsideOfQuotesWithPrefixAndReplacedPostfix() throws Exception {
|
||||
assertCompletions("@Resource(name=\"be<*>xxx\")", 2, "@Resource(name=\"bean1<*>\")");
|
||||
}
|
||||
|
||||
private void assertCompletions(String completionLine, int noOfExpectedCompletions, String expectedCompletedLine) throws Exception {
|
||||
assertCompletions(completionLine, noOfExpectedCompletions, null, 0, expectedCompletedLine);
|
||||
}
|
||||
|
||||
private void assertCompletions(String completionLine, String[] expectedCompletions, int chosenCompletion, String expectedCompletedLine) throws Exception {
|
||||
assertCompletions(completionLine, expectedCompletions.length, expectedCompletions, chosenCompletion, expectedCompletedLine);
|
||||
}
|
||||
|
||||
private void assertCompletions(String completionLine, int noOfExcpectedCompletions, String[] expectedCompletions, int chosenCompletion, String expectedCompletedLine) throws Exception {
|
||||
String editorContent = """
|
||||
package org.test;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@Component
|
||||
public class TestDependsOnClass {
|
||||
|
||||
""" +
|
||||
completionLine + "\n" +
|
||||
"""
|
||||
public void setTestBean(Object testBean) {}
|
||||
|
||||
}
|
||||
""";
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA, editorContent, tempJavaDocUri);
|
||||
|
||||
List<CompletionItem> completions = editor.getCompletions();
|
||||
assertEquals(noOfExcpectedCompletions, completions.size());
|
||||
|
||||
if (expectedCompletions != null) {
|
||||
String[] completionItems = completions.stream()
|
||||
.map(item -> item.getLabel())
|
||||
.toArray(size -> new String[size]);
|
||||
|
||||
assertArrayEquals(expectedCompletions, completionItems);
|
||||
}
|
||||
|
||||
if (noOfExcpectedCompletions > 0) {
|
||||
editor.apply(completions.get(chosenCompletion));
|
||||
assertEquals("""
|
||||
package org.test;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@Component
|
||||
public class TestDependsOnClass {
|
||||
|
||||
""" +
|
||||
expectedCompletedLine + "\n" +
|
||||
"""
|
||||
public void setTestBean(Object testBean) {}
|
||||
|
||||
}
|
||||
""", editor.getText());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2024 Broadcom
|
||||
* 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
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Broadcom - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.beans.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.LocationLink;
|
||||
import org.eclipse.lsp4j.Position;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
|
||||
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
|
||||
import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf;
|
||||
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@BootLanguageServerTest
|
||||
@Import(SymbolProviderTestConf.class)
|
||||
public class ResourceDefinitionProviderTest {
|
||||
|
||||
@Autowired private BootLanguageServerHarness harness;
|
||||
@Autowired private JavaProjectFinder projectFinder;
|
||||
@Autowired private SpringMetamodelIndex springIndex;
|
||||
@Autowired private SpringSymbolIndex indexer;
|
||||
|
||||
private File directory;
|
||||
private IJavaProject project;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws Exception {
|
||||
harness.intialize(null);
|
||||
|
||||
directory = new File(ProjectsHarness.class.getResource("/test-projects/test-spring-indexing/").toURI());
|
||||
|
||||
String projectDir = directory.toURI().toString();
|
||||
project = projectFinder.find(new TextDocumentIdentifier(projectDir)).get();
|
||||
|
||||
CompletableFuture<Void> initProject = indexer.waitOperation();
|
||||
initProject.get(5, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleDependsOnBeanDefinitionLink() throws Exception {
|
||||
String tempJavaDocUri = directory.toPath().resolve("src/main/java/org/test/TempClass.java").toUri().toString();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA, """
|
||||
package org.test;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@Component
|
||||
@Resource(name="bean1")
|
||||
public class TestDependsOnClass {
|
||||
}""", tempJavaDocUri);
|
||||
|
||||
String expectedDefinitionUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
|
||||
|
||||
Bean[] beans = springIndex.getBeansWithName(project.getElementName(), "bean1");
|
||||
assertEquals(1, beans.length);
|
||||
|
||||
LocationLink expectedLocation = new LocationLink(expectedDefinitionUri,
|
||||
beans[0].getLocation().getRange(), beans[0].getLocation().getRange(),
|
||||
null);
|
||||
|
||||
editor.assertLinkTargets("bean1", List.of(expectedLocation));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDependsOnWithMultipleBeanDefinitionLinks() throws Exception {
|
||||
String tempJavaDocUri = directory.toPath().resolve("src/main/java/org/test/TempClass.java").toUri().toString();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA, """
|
||||
package org.test;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@Component
|
||||
@Resource(name="bean1")
|
||||
public class TestDependsOnClass {
|
||||
}""", tempJavaDocUri);
|
||||
|
||||
String expectedDefinitionUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
|
||||
|
||||
List<Bean> beansOfDoc = new ArrayList<>(List.of(springIndex.getBeansOfDocument(expectedDefinitionUri)));
|
||||
beansOfDoc.add(new Bean("bean1", "type", new Location(expectedDefinitionUri, new Range(new Position(20, 1), new Position(20, 10))), null, null, null));
|
||||
springIndex.updateBeans(project.getElementName(), expectedDefinitionUri, beansOfDoc.toArray(new Bean[0]));
|
||||
|
||||
Bean[] beans = springIndex.getBeansWithName(project.getElementName(), "bean1");
|
||||
assertEquals(2, beans.length);
|
||||
|
||||
LocationLink expectedLocation1 = new LocationLink(expectedDefinitionUri,
|
||||
beans[0].getLocation().getRange(), beans[0].getLocation().getRange(),
|
||||
null);
|
||||
|
||||
LocationLink expectedLocation2 = new LocationLink(expectedDefinitionUri,
|
||||
beans[1].getLocation().getRange(), beans[1].getLocation().getRange(),
|
||||
null);
|
||||
|
||||
editor.assertLinkTargets("bean1", List.of(expectedLocation1, expectedLocation2));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user