From 8666ef9785dd10d607eecfcaedcb09dca8df6fc3 Mon Sep 17 00:00:00 2001 From: aboyko Date: Tue, 15 Aug 2023 20:49:11 -0400 Subject: [PATCH] Switch Rewrite code action handler to JDT reconcilers --- .../reconcile/BasicProblemCollector.java | 36 +++++++ .../ide/vscode/boot/app/RewriteConfig.java | 7 +- .../rewrite/RewriteCodeActionHandler.java | 101 +++++++----------- 3 files changed, 78 insertions(+), 66 deletions(-) create mode 100644 headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/BasicProblemCollector.java diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/BasicProblemCollector.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/BasicProblemCollector.java new file mode 100644 index 000000000..ebd4d1b98 --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/BasicProblemCollector.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2022, 2023 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 + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VMware, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.languageserver.reconcile; + +import java.util.Collection; + +public class BasicProblemCollector implements IProblemCollector { + + private final Collection problems; + + public BasicProblemCollector(Collection problems) { + this.problems = problems; + } + + @Override + public void beginCollecting() { + } + + @Override + public void endCollecting() { + } + + @Override + public void accept(ReconcileProblem problem) { + problems.add(problem); + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/RewriteConfig.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/RewriteConfig.java index 0934b1966..7ad97eaf7 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/RewriteConfig.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/RewriteConfig.java @@ -14,12 +14,13 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.ide.vscode.boot.java.reconcilers.JdtReconciler; import org.springframework.ide.vscode.boot.java.rewrite.RewriteCodeActionHandler; import org.springframework.ide.vscode.boot.java.rewrite.RewriteCompilationUnitCache; import org.springframework.ide.vscode.boot.java.rewrite.RewriteRecipeRepository; -import org.springframework.ide.vscode.boot.java.rewrite.RewriteReconciler; import org.springframework.ide.vscode.boot.java.rewrite.RewriteRefactorings; import org.springframework.ide.vscode.boot.java.rewrite.SpringBootUpgrade; +import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; @@ -42,8 +43,8 @@ public class RewriteConfig { } @ConditionalOnBean(RewriteRecipeRepository.class) - @Bean RewriteCodeActionHandler rewriteCodeActionHandler(RewriteCompilationUnitCache cuCache, RewriteRecipeRepository recipeRepo, BootJavaConfig config) { - return new RewriteCodeActionHandler(cuCache, recipeRepo, config); + @Bean RewriteCodeActionHandler rewriteCodeActionHandler(CompilationUnitCache cuCache, BootJavaConfig config, JdtReconciler jdtReconciler, SimpleLanguageServer server) { + return new RewriteCodeActionHandler(cuCache, config, jdtReconciler, server.getQuickfixRegistry()); } @ConditionalOnBean(RewriteRecipeRepository.class) diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteCodeActionHandler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteCodeActionHandler.java index 5f3d3ce9c..afb84a81f 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteCodeActionHandler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteCodeActionHandler.java @@ -24,24 +24,21 @@ import org.eclipse.lsp4j.CodeActionResolveSupportCapabilities; import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.jsonrpc.CancelChecker; import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.openrewrite.ExecutionContext; -import org.openrewrite.InMemoryExecutionContext; -import org.openrewrite.Tree; -import org.openrewrite.java.JavaIsoVisitor; -import org.openrewrite.java.tree.J; -import org.openrewrite.java.tree.J.CompilationUnit; -import org.openrewrite.marker.Range; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.boot.app.BootJavaConfig; import org.springframework.ide.vscode.boot.java.handlers.JavaCodeActionHandler; +import org.springframework.ide.vscode.boot.java.reconcilers.JdtReconciler; +import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache; import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix.QuickfixData; +import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry; +import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixType; +import org.springframework.ide.vscode.commons.languageserver.reconcile.BasicProblemCollector; +import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem; import org.springframework.ide.vscode.commons.languageserver.util.LspClient; import org.springframework.ide.vscode.commons.languageserver.util.LspClient.Client; -import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor; -import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker; import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor; -import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.util.text.IRegion; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -49,15 +46,18 @@ public class RewriteCodeActionHandler implements JavaCodeActionHandler { private static final Logger log = LoggerFactory.getLogger(RewriteCodeActionHandler.class); - final private RewriteCompilationUnitCache cuCache; - final private RewriteRecipeRepository recipeRepo; + final private CompilationUnitCache cuCache; + final private JdtReconciler jdtReconciler; private BootJavaConfig config; - public RewriteCodeActionHandler(RewriteCompilationUnitCache cuCache, RewriteRecipeRepository recipeRepo, BootJavaConfig config) { + private QuickfixRegistry quickfixRegistry; + + public RewriteCodeActionHandler(CompilationUnitCache cuCache, BootJavaConfig config, JdtReconciler jdtReconciler, QuickfixRegistry quickfixRegistry) { this.cuCache = cuCache; - this.recipeRepo = recipeRepo; this.config = config; + this.jdtReconciler = jdtReconciler; + this.quickfixRegistry = quickfixRegistry; } protected static boolean isResolve(CodeActionCapabilities capabilities, String property) { @@ -97,47 +97,33 @@ public class RewriteCodeActionHandler implements JavaCodeActionHandler { } try { - - List descriptors = recipeRepo.getCodeActionRecipeDescriptors().stream() - // If Recipe not present - don't show quick assist as it won't be handled without the Rewrite recipe present - .filter(d -> d.isApplicable(project)) - .collect(Collectors.toList()); - - if (isSupported(capabilities, context)) { - CompilationUnit cu = cuCache.getCU(project, URI.create(doc.getUri())); - if (cu != null) { - - cu = recipeRepo.mark(project, descriptors, cu); - - List codeActions = new ArrayList<>(); - new JavaIsoVisitor() { - public J visit(Tree tree, ExecutionContext ctx) { - if (tree == null) { - return null; - } - if (tree instanceof J) { - J node = (J) tree; - Range range = node.getMarkers().findFirst(Range.class).orElse(null); - if (range != null - && range.getStart().getOffset() <= region.getOffset() - && region.getOffset() + region.getLength() <= range.getEnd().getOffset()) { - - for (FixAssistMarker m : node.getMarkers().findAll(FixAssistMarker.class)) { - for (CodeAction ca : createCodeActions(doc, m, node)) { - codeActions.add(ca); - } + URI uri = URI.create(doc.getUri()); + final QuickfixType rewriteFixType = quickfixRegistry.getQuickfixType(RewriteRefactorings.REWRITE_RECIPE_QUICKFIX); + if (isSupported(capabilities, context) && rewriteFixType != null) { + List codeActions = cuCache.withCompilationUnit(project, uri, cu -> { + try { + List cas = new ArrayList<>(); + List problems = new ArrayList<>(); + BasicProblemCollector problemsCollector = new BasicProblemCollector(problems); + jdtReconciler.reconcile(project, uri, cu, problemsCollector, true); + for (ReconcileProblem p : problems) { + if (p.getOffset() <= region.getOffset() && p.getOffset() + p.getLength() >= region.getOffset() + region.getLength()) { + for (QuickfixData qf : p.getQuickfixes()) { + if (qf.params instanceof FixDescriptor) { + cas.add(createCodeActionFromScope((FixDescriptor) qf.params)); } - return super.visit(tree, ctx); - } else { - return (J) tree; } } - return (J) tree; - }; - }.visitNonNull(cu, new InMemoryExecutionContext()); - return codeActions.stream().map(ca -> Either.forRight(ca)).collect(Collectors.toList()); - } + } + return cas; + } catch (Exception e) { + log.error("", e); + } + return Collections.emptyList(); + }); + + return codeActions.stream().map(ca -> Either.forRight(ca)).collect(Collectors.toList()); } } catch (Exception e) { log.error("", e); @@ -145,18 +131,7 @@ public class RewriteCodeActionHandler implements JavaCodeActionHandler { return Collections.emptyList(); } - private CodeAction[] createCodeActions(IDocument doc, FixAssistMarker m, J astNode) { - if (astNode != null) { - return m.getFixes().stream() - .filter(d -> recipeRepo.getRecipe(d.getRecipeId()) != null) - .map(d -> createCodeActionFromScope(doc, d)) - .toArray(CodeAction[]::new); - } - return new CodeAction[0]; - } - - private CodeAction createCodeActionFromScope(IDocument doc, - FixDescriptor d) { + private CodeAction createCodeActionFromScope(FixDescriptor d) { CodeAction ca = new CodeAction(); ca.setKind(CodeActionKind.Refactor); ca.setTitle(d.getLabel());