diff --git a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java index bbf559ee8..827b24136 100644 --- a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java +++ b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java @@ -631,6 +631,14 @@ public class Editor { .map(cl -> "'%s'".formatted(cl.getCommand().getTitle())).collect(Collectors.joining(", ")), codeLensText)); } } + + public List getCodeLenses(String over, int occurrence) throws Exception { + int offset = getHoverPosition(over, occurrence); + return harness.getCodeLenses(doc).stream() + .filter(cl -> doc.toOffset(cl.getRange().getStart()) <= offset + && doc.toOffset(cl.getRange().getEnd()) >= offset) + .collect(Collectors.toList()); + } public void assertLiveCodeLensContains(String codeLensOver, int occurrence, String snippet) throws Exception { int cmPosition = getHoverPosition(codeLensOver, occurrence); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerBootApp.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerBootApp.java index 6bc6f9ec9..764e16bd9 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerBootApp.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerBootApp.java @@ -420,7 +420,7 @@ public class BootLanguageServerBootApp { new NamedDefinitionProvider(springIndex), new DataQueryParameterDefinitionProvider(server.getTextDocumentService(), qurySemanticTokens), new SpelDefinitionProvider(springIndex, cuCache), - new GenAotQueryMethodDefinitionProvider(cuCache, server.getTextDocumentService()))); + new GenAotQueryMethodDefinitionProvider(server, cuCache, projectFinder))); } @Bean diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java index f73391844..efb18bc21 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java @@ -64,6 +64,8 @@ import com.google.gson.JsonElement; @Component public class BootLanguageServerInitializer implements InitializingBean { + public static final String CMD_SHOW_DOC = "sts/show/document"; + @Autowired SimpleLanguageServer server; @Autowired BootLanguageServerParams params; @Autowired SourceLinks sourceLinks; @@ -165,7 +167,7 @@ public class BootLanguageServerInitializer implements InitializingBean { startListening(); - server.onCommand("sts/show/document", p -> { + server.onCommand(CMD_SHOW_DOC, p -> { ShowDocumentParams showDocParams = new Gson().fromJson((JsonElement)p.getArguments().get(0), ShowDocumentParams.class); return server.getClient().showDocument(showDocParams).thenApply(r -> { if (!r.isSuccess()) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositoryAotMetadataCodeLensProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositoryAotMetadataCodeLensProvider.java index a2aeef6f1..dda316f53 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositoryAotMetadataCodeLensProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositoryAotMetadataCodeLensProvider.java @@ -20,17 +20,17 @@ import java.util.stream.Collectors; import org.apache.commons.text.StringEscapeUtils; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.core.dom.IAnnotationBinding; import org.eclipse.jdt.core.dom.IMethodBinding; -import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.lsp4j.CodeLens; import org.eclipse.lsp4j.Command; +import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.jsonrpc.CancelChecker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.boot.java.Annotations; +import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies; import org.springframework.ide.vscode.boot.java.handlers.CodeLensProvider; import org.springframework.ide.vscode.boot.java.rewrite.RewriteRefactorings; import org.springframework.ide.vscode.commons.java.IJavaProject; @@ -46,7 +46,7 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; */ public class DataRepositoryAotMetadataCodeLensProvider implements CodeLensProvider { - private static final String COVERT_TO_QUERY_LABEL = "Add @Query"; + private static final String COVERT_TO_QUERY_LABEL = "Turn into @Query"; private static final Logger log = LoggerFactory.getLogger(DataRepositoryAotMetadataCodeLensProvider.class); @@ -71,23 +71,12 @@ public class DataRepositoryAotMetadataCodeLensProvider implements CodeLensProvid }); } - static boolean isDataQuaryNonAnnotatedMethodCandidate(IMethodBinding methodBinding) { + static boolean isValidMethodBinding(IMethodBinding methodBinding) { if (methodBinding == null || methodBinding.getDeclaringClass() == null || methodBinding.getMethodDeclaration() == null - || methodBinding.getDeclaringClass().getBinaryName() == null - || methodBinding.getMethodDeclaration().toString() == null) { + || methodBinding.getDeclaringClass().getBinaryName() == null) { return false; } - - // Don't show CodeLens if annotated with `@Query` or `@NativeQuery` - for (IAnnotationBinding a : methodBinding.getAnnotations()) { - ITypeBinding t = a.getAnnotationType(); - if (t != null - && (Annotations.DATA_JPA_QUERY.equals(t.getQualifiedName()) || Annotations.DATA_JPA_NATIVE_QUERY.equals(t.getQualifiedName()))) { - return false; - } - } - return true; } @@ -115,7 +104,7 @@ public class DataRepositoryAotMetadataCodeLensProvider implements CodeLensProvid IMethodBinding methodBinding = node.resolveBinding(); - if (isDataQuaryNonAnnotatedMethodCandidate(methodBinding)) { + if (isValidMethodBinding(methodBinding)) { cancelToken.checkCanceled(); getDataQuery(repositoryMetadataService, project, methodBinding) .map(queryStatement -> createCodeLenses(node, document, queryStatement)) @@ -127,12 +116,30 @@ public class DataRepositoryAotMetadataCodeLensProvider implements CodeLensProvid List codeLenses = new ArrayList<>(2); try { IMethodBinding mb = node.resolveBinding(); - Range range = document.toRange(node.getName().getStartPosition(), node.getName().getLength()); - Command queryTitle = new Command(); - queryTitle.setTitle(queryStatement); - codeLenses.add(new CodeLens(range, queryTitle, null)); - if (mb != null) { - codeLenses.add(new CodeLens(range, refactorings.createFixCommand(COVERT_TO_QUERY_LABEL, createFixDescriptor(mb, document.getUri(), queryStatement)), null)); + Position startPos = document.toPosition(node.getStartPosition()); + Position endPos = document.toPosition(node.getName().getStartPosition() + node.getName().getLength()); + Range range = new Range(startPos, endPos); + AnnotationHierarchies hierarchyAnnot = AnnotationHierarchies.get(node); + if (mb != null && hierarchyAnnot != null) { + boolean isQueryAnnotated = hierarchyAnnot.isAnnotatedWith(mb, Annotations.DATA_JPA_QUERY); + if (!isQueryAnnotated) { + codeLenses.add(new CodeLens(range, refactorings.createFixCommand(COVERT_TO_QUERY_LABEL, createFixDescriptor(mb, document.getUri(), queryStatement)), null)); + } + + Command impl = new Command("Implementation", GenAotQueryMethodDefinitionProvider.CMD_NAVIGATE_TO_IMPL, List.of(new GenAotQueryMethodDefinitionProvider.GoToImplParams( + document.getId(), + mb.getDeclaringClass().getQualifiedName(), + mb.getName(), + Arrays.stream(mb.getParameterTypes()).map(p -> p.getQualifiedName()).toArray(String[]::new), + null + ))); + codeLenses.add(new CodeLens(range, impl, null)); + + if (!isQueryAnnotated) { + Command queryTitle = new Command(); + queryTitle.setTitle(queryStatement); + codeLenses.add(new CodeLens(range, queryTitle, null)); + } } } catch (BadLocationException e) { log.error("bad location while calculating code lens for data repository query method", e); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/GenAotQueryMethodDefinitionProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/GenAotQueryMethodDefinitionProvider.java index a4bb25904..e56cb0152 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/GenAotQueryMethodDefinitionProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/GenAotQueryMethodDefinitionProvider.java @@ -22,7 +22,9 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @@ -35,6 +37,7 @@ import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.lsp4j.LocationLink; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.ShowDocumentParams; import org.eclipse.lsp4j.TextDocumentIdentifier; import org.eclipse.lsp4j.jsonrpc.CancelChecker; import org.slf4j.Logger; @@ -46,19 +49,29 @@ import org.springframework.ide.vscode.commons.Version; import org.springframework.ide.vscode.commons.java.IClasspathUtil; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.java.SpringProjectUtil; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; import org.springframework.ide.vscode.commons.util.BadLocationException; +import com.google.gson.Gson; +import com.google.gson.JsonElement; + public class GenAotQueryMethodDefinitionProvider implements IJavaDefinitionProvider { private static Logger log = LoggerFactory.getLogger(GenAotQueryMethodDefinitionProvider.class); + public static final String CMD_NAVIGATE_TO_IMPL = "sts/boot/open-data-query-method-aot-definition"; + private final CompilationUnitCache cuCache; private final SimpleTextDocumentService docService; + private final JavaProjectFinder projectFinder; - public GenAotQueryMethodDefinitionProvider(CompilationUnitCache cuCache, SimpleTextDocumentService docService) { + public GenAotQueryMethodDefinitionProvider(SimpleLanguageServer server, CompilationUnitCache cuCache, JavaProjectFinder projectFinder) { this.cuCache = cuCache; - this.docService = docService; + this.docService = server.getTextDocumentService(); + this.projectFinder = projectFinder; + registerCommands(server); } @Override @@ -73,17 +86,29 @@ public class GenAotQueryMethodDefinitionProvider implements IJavaDefinitionProvi && methodBinding.getDeclaringClass() != null && ASTUtils.isAnyTypeInHierarchy(methodBinding.getDeclaringClass(), List.of(Constants.REPOSITORY_TYPE))) { - String genRepoFqn = methodBinding.getDeclaringClass().getQualifiedName() + "Impl__Aot"; - Path relativeGenSourcePath = Paths.get("%s.java".formatted(genRepoFqn.replace('.', '/'))); - List defs = findInSourceFolder(project, relativeGenSourcePath, docId, md, methodBinding, genRepoFqn); - return defs.isEmpty() ? findInBuildFolder(project, relativeGenSourcePath, docId, md, methodBinding, genRepoFqn) : defs; + + try { + Range originRange = docService.getLatestSnapshot(docId.getUri()).toRange(md.getName().getStartPosition(), md.getName().getLength()); + GoToImplParams params = new GoToImplParams(docId, methodBinding.getDeclaringClass().getQualifiedName(), methodBinding.getName(), Arrays.stream(methodBinding.getParameterTypes()).map(b -> b.getQualifiedName()).toArray(String[]::new), originRange); + return findDefinitions(project, params); + } catch (BadLocationException e) { + log.error("", e); + } + } } } return List.of(); } - private List getLocationInGenFile(IJavaProject project, TextDocumentIdentifier docId, MethodDeclaration md, IMethodBinding methodBinding, Path genRepoSourcePath, String genRepoFqn) { + private List findDefinitions(IJavaProject project, GoToImplParams implParams) { + String genRepoFqn = implParams.repoFqName() + "Impl__Aot"; + Path relativeGenSourcePath = Paths.get("%s.java".formatted(genRepoFqn.replace('.', '/'))); + List defs = findInSourceFolder(project, relativeGenSourcePath, genRepoFqn, implParams); + return defs.isEmpty() ? findInBuildFolder(project, relativeGenSourcePath, genRepoFqn, implParams) : defs; + } + + private List getLocationInGenFile(IJavaProject project, Path genRepoSourcePath, String genRepoFqn, GoToImplParams params) { if (Files.exists(genRepoSourcePath)) { URI genUri = genRepoSourcePath.toUri(); return cuCache.withCompilationUnit(project, genUri, genCu -> { @@ -94,21 +119,18 @@ public class GenAotQueryMethodDefinitionProvider implements IJavaDefinitionProvi public boolean visit(MethodDeclaration node) { IMethodBinding genBinding = node.resolveBinding(); if (genBinding != null - && genBinding.getName().equals(methodBinding.getName()) - && Arrays.equals(Arrays.stream(genBinding.getParameterTypes()).map(b -> b.getQualifiedName()).toArray(), Arrays.stream(methodBinding.getParameterTypes()).map(b -> b.getQualifiedName()).toArray() ) + && genBinding.getName().equals(params.queryMethodName()) + && Arrays.equals(Arrays.stream(genBinding.getParameterTypes()).map(b -> b.getQualifiedName()).toArray(), params.paramTypes) && genRepoFqn.equals(genBinding.getDeclaringClass().getQualifiedName())) { LocationLink ll = new LocationLink(); ll.setTargetUri(genUri.toASCIIString()); - try { - ll.setOriginSelectionRange(docService.getLatestSnapshot(docId.getUri()).toRange(md.getName().getStartPosition(), md.getName().getLength())); - } catch (BadLocationException e) { - log.error("", e); - } + ll.setOriginSelectionRange(params.originSelection()); SimpleName genName = node.getName(); int startLine = genCu.getLineNumber(genName.getStartPosition()); - Position targetStartPosition = new Position(startLine, genName.getStartPosition() - genCu.getPosition(startLine, 0)); + // LSP line are 0-based hence -1 from line number when building LSP Range/Position + Position targetStartPosition = new Position(startLine - 1, genName.getStartPosition() - genCu.getPosition(startLine, 0)); int endLine = genCu.getLineNumber(genName.getStartPosition() + genName.getLength()); - Position targetEndPosition = new Position(endLine, genName.getStartPosition() + genName.getLength() - genCu.getPosition(endLine, 0)); + Position targetEndPosition = new Position(endLine - 1, genName.getStartPosition() + genName.getLength() - genCu.getPosition(endLine, 0)); Range targetRange = new Range(targetStartPosition, targetEndPosition); ll.setTargetRange(targetRange); ll.setTargetSelectionRange(targetRange); @@ -124,15 +146,15 @@ public class GenAotQueryMethodDefinitionProvider implements IJavaDefinitionProvi return List.of(); } - private List findInSourceFolder(IJavaProject project, Path relativeGenSourcePath, TextDocumentIdentifier docId, MethodDeclaration md, IMethodBinding methodBinding, String genRepoFqn) { + private List findInSourceFolder(IJavaProject project, Path relativeGenSourcePath, String genRepoFqn, GoToImplParams params) { for (File f : IClasspathUtil.getSourceFolders(project.getClasspath()).collect(Collectors.toSet())) { Path genRepoSourcePath = f.toPath().resolve(relativeGenSourcePath); - return getLocationInGenFile(project, docId, md, methodBinding, genRepoSourcePath, genRepoFqn); + return getLocationInGenFile(project, genRepoSourcePath, genRepoFqn, params); } return List.of(); } - private List findInBuildFolder(IJavaProject project, Path relativeGenSourcePath, TextDocumentIdentifier docId, MethodDeclaration md, IMethodBinding methodBinding, String genRepoFqn) { + private List findInBuildFolder(IJavaProject project, Path relativeGenSourcePath, String genRepoFqn, GoToImplParams params) { Path buildDirRelativePath = null; Path projectPath = Paths.get(project.getLocationUri()); Set outputFolders = IClasspathUtil.getOutputFolders(project.getClasspath()).map(f -> f.toPath()).collect(Collectors.toSet()); @@ -182,7 +204,30 @@ public class GenAotQueryMethodDefinitionProvider implements IJavaDefinitionProvi } catch (IOException e) { log.error("", e); } - return genSourceFilePathRef.get() == null ? List.of() : getLocationInGenFile(project, docId, md, methodBinding, genSourceFilePathRef.get(), genRepoFqn); + return genSourceFilePathRef.get() == null ? List.of() : getLocationInGenFile(project, genSourceFilePathRef.get(), genRepoFqn, params); } + private void registerCommands(SimpleLanguageServer server) { + server.onCommand(CMD_NAVIGATE_TO_IMPL, params -> { + return CompletableFuture.supplyAsync(() -> { + GoToImplParams implParams = new Gson().fromJson((JsonElement) params.getArguments().get(0), GoToImplParams.class); + Optional project = projectFinder.find(implParams.docId()); + if (project.isEmpty()) { + return List.of(); + } + return findDefinitions(project.get(), implParams); + }).thenCompose(links -> { + if (links.isEmpty()) { + return CompletableFuture.completedFuture(null); + } else { + ShowDocumentParams showDocParams = new ShowDocumentParams(links.get(0).getTargetUri()); + showDocParams.setSelection(links.get(0).getTargetRange()); + return server.getClient().showDocument(showDocParams); + } + }); + }); + } + + public record GoToImplParams(TextDocumentIdentifier docId, String repoFqName, String queryMethodName, String[] paramTypes, Range originSelection) {} + } \ No newline at end of file diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/QueryMethodCodeActionProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/QueryMethodCodeActionProvider.java index 1ec12a9eb..0c5878432 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/QueryMethodCodeActionProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/QueryMethodCodeActionProvider.java @@ -19,6 +19,8 @@ import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.CodeActionKind; import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import org.springframework.ide.vscode.boot.java.Annotations; +import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies; import org.springframework.ide.vscode.boot.java.codeaction.JdtAstCodeActionProvider; import org.springframework.ide.vscode.boot.java.rewrite.RewriteRefactorings; import org.springframework.ide.vscode.commons.Version; @@ -30,7 +32,7 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; public class QueryMethodCodeActionProvider implements JdtAstCodeActionProvider { - private static final String TITLE = "Convert into `@Query`"; + private static final String TITLE = "Add `@Query`"; private final DataRepositoryAotMetadataService repositoryMetadataService; private final RewriteRefactorings refactorings; @@ -55,11 +57,12 @@ public class QueryMethodCodeActionProvider implements JdtAstCodeActionProvider { public boolean visit(MethodDeclaration node) { cancelToken.checkCanceled(); if (node.getStartPosition() <= region.getStart() && node.getStartPosition() + node.getLength() >= region.getEnd()) { - int offset = node.getName().getStartPosition(); - int length = node.getName().getLength(); - if (offset <= region.getStart() && offset + length >= region.getEnd()) { + int start = node.getStartPosition(); + int end = node.getName().getStartPosition() + node.getName().getLength(); + if (start <= region.getStart() && end >= region.getEnd()) { IMethodBinding binding = node.resolveBinding(); - if (DataRepositoryAotMetadataCodeLensProvider.isDataQuaryNonAnnotatedMethodCandidate(binding)) { + AnnotationHierarchies hierarchyAnnot = AnnotationHierarchies.get(node); + if (hierarchyAnnot != null && !hierarchyAnnot.isAnnotatedWith(binding, Annotations.DATA_JPA_QUERY)) { DataRepositoryAotMetadataCodeLensProvider.getDataQuery(repositoryMetadataService, project, binding) .map(query -> createCodeAction(binding, docURI, query)).ifPresent(collector::accept); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/GenerationsValidator.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/GenerationsValidator.java index 0ec82bf2a..46f6f1bbd 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/GenerationsValidator.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/GenerationsValidator.java @@ -19,6 +19,7 @@ import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.ShowDocumentParams; +import org.springframework.ide.vscode.boot.app.BootLanguageServerInitializer; import org.springframework.ide.vscode.boot.validation.generations.json.Generation; import org.springframework.ide.vscode.boot.validation.generations.json.ResolvedSpringProject; import org.springframework.ide.vscode.boot.validation.generations.preferences.VersionValidationProblemType; @@ -148,7 +149,7 @@ public class GenerationsValidator extends AbstractDiagnosticValidator { showDocumentParams.setExternal(true); showDocumentParams.setTakeFocus(true); showDocumentParams.setSelection(new Range()); - commercialSupportLink.setCommand(new Command("Get commercial Spring Boot support via Tanzu Spring Runtime", "sts/show/document", + commercialSupportLink.setCommand(new Command("Get commercial Spring Boot support via Tanzu Spring Runtime", BootLanguageServerInitializer.CMD_SHOW_DOC, ImmutableList.of(showDocumentParams))); return commercialSupportLink; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/UpdateBootVersion.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/UpdateBootVersion.java index 355c452a7..c3d1050d1 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/UpdateBootVersion.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/UpdateBootVersion.java @@ -21,6 +21,7 @@ import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.ShowDocumentParams; +import org.springframework.ide.vscode.boot.app.BootLanguageServerInitializer; import org.springframework.ide.vscode.boot.java.rewrite.SpringBootUpgrade; import org.springframework.ide.vscode.boot.validation.generations.preferences.VersionValidationProblemType; import org.springframework.ide.vscode.commons.Version; @@ -157,7 +158,7 @@ public class UpdateBootVersion extends AbstractDiagnosticValidator { showDocumentParams.setExternal(true); showDocumentParams.setTakeFocus(true); showDocumentParams.setSelection(new Range()); - releaseNoteLink.setCommand(new Command("Release Notes for Spring Boot " + version.toString(), "sts/show/document", + releaseNoteLink.setCommand(new Command("Release Notes for Spring Boot " + version.toString(), BootLanguageServerInitializer.CMD_SHOW_DOC, ImmutableList.of(showDocumentParams))); return releaseNoteLink; } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/data/test/DataRepositoryAotMetadataCodeLensProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/data/test/DataRepositoryAotMetadataCodeLensProviderTest.java index bdfc776b8..c440d234f 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/data/test/DataRepositoryAotMetadataCodeLensProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/data/test/DataRepositoryAotMetadataCodeLensProviderTest.java @@ -10,13 +10,17 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.data.test; +import static org.junit.Assert.assertEquals; + import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; +import org.eclipse.lsp4j.CodeLens; import org.eclipse.lsp4j.TextDocumentIdentifier; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -64,8 +68,10 @@ public class DataRepositoryAotMetadataCodeLensProviderTest { .resolve("src/main/java/example/springdata/aot/UserRepository.java"); Editor editor = harness.newEditor(LanguageId.JAVA, new String(Files.readAllBytes(filePath), StandardCharsets.UTF_8), filePath.toUri().toASCIIString()); - editor.assertCodeLens("findUserByUsername", 1, "SELECT u FROM example.springdata.aot.User u WHERE u.username = :username"); - editor.assertCodeLens("findUserByUsername", 1, "Add @Query"); + List cls = editor.getCodeLenses("findUserByUsername", 1); + assertEquals("Turn into @Query", cls.get(0).getCommand().getTitle()); + assertEquals("Implementation", cls.get(1).getCommand().getTitle()); + assertEquals("SELECT u FROM example.springdata.aot.User u WHERE u.username = :username", cls.get(2).getCommand().getTitle()); } @Test @@ -74,6 +80,9 @@ public class DataRepositoryAotMetadataCodeLensProviderTest { .resolve("src/main/java/example/springdata/aot/UserRepository.java"); Editor editor = harness.newEditor(LanguageId.JAVA, new String(Files.readAllBytes(filePath), StandardCharsets.UTF_8), filePath.toUri().toASCIIString()); - editor.assertCodeLens("usersWithUsernamesStartingWith", 1, null); + List cls = editor.getCodeLenses("usersWithUsernamesStartingWith", 1); + assertEquals(1, cls.size()); + assertEquals("Implementation", cls.get(0).getCommand().getTitle()); + assertEquals(1, cls.get(0).getCommand().getArguments().size()); } } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/data/test/GenAotQueryMethodDefinitionProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/data/test/GenAotQueryMethodDefinitionProviderTest.java index 03891d407..e90832d2b 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/data/test/GenAotQueryMethodDefinitionProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/data/test/GenAotQueryMethodDefinitionProviderTest.java @@ -73,8 +73,8 @@ public class GenAotQueryMethodDefinitionProviderTest { .resolve("target/spring-aot/main/sources/example/springdata/aot/UserRepositoryImpl__Aot.java").toUri() .toASCIIString()); ll.setOriginSelectionRange(new Range(new Position(43, 15), new Position(43, 61))); - ll.setTargetRange(new Range(new Position(145, 20), new Position(145, 66))); - ll.setTargetSelectionRange(new Range(new Position(145, 20), new Position(145, 66))); + ll.setTargetRange(new Range(new Position(144, 20), new Position(144, 66))); + ll.setTargetSelectionRange(new Range(new Position(144, 20), new Position(144, 66))); editor.assertLinkTargets("findUserByLastnameStartingWith", List.of(ll)); } @@ -89,8 +89,8 @@ public class GenAotQueryMethodDefinitionProviderTest { .resolve("target/spring-aot/main/sources/example/springdata/aot/UserRepositoryImpl__Aot.java").toUri() .toASCIIString()); ll.setOriginSelectionRange(new Range(new Position(54, 15), new Position(54, 45))); - ll.setTargetRange(new Range(new Position(191, 20), new Position(191, 50))); - ll.setTargetSelectionRange(new Range(new Position(191, 20), new Position(191, 50))); + ll.setTargetRange(new Range(new Position(190, 20), new Position(190, 50))); + ll.setTargetSelectionRange(new Range(new Position(190, 20), new Position(190, 50))); editor.assertLinkTargets("usersWithUsernamesStartingWith", List.of(ll)); } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/data/test/QueryMethodCodeActionProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/data/test/QueryMethodCodeActionProviderTest.java index 05c9cb1fc..e01a4d3c5 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/data/test/QueryMethodCodeActionProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/data/test/QueryMethodCodeActionProviderTest.java @@ -79,7 +79,7 @@ public class QueryMethodCodeActionProviderTest { List codeActions = editor.getCodeActions("findUserByLastnameStartingWith", 1); assertEquals(1, codeActions.size()); CodeAction ca = codeActions.get(0); - assertEquals("Convert into `@Query`", ca.getLabel()); + assertEquals("Add `@Query`", ca.getLabel()); Command cmd = ca.getCommand(); assertEquals(RewriteRefactorings.REWRITE_RECIPE_QUICKFIX, cmd.getArguments().get(0)); WorkspaceEdit edit = refactorings.createEdit((JsonElement) cmd.getArguments().get(1)).get(5, TimeUnit.SECONDS);