Runtime load recipes yml/jar. Offscreen doc quick fix.

This commit is contained in:
aboyko
2022-09-16 18:52:48 -04:00
parent 01008b3375
commit 1a708a44e8
46 changed files with 1145 additions and 148 deletions

View File

@@ -13,6 +13,7 @@ package org.springframework.ide.vscode.boot.app;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import org.slf4j.Logger;
@@ -162,7 +163,15 @@ public class BootJavaConfig implements InitializingBean {
public void afterPropertiesSet() throws Exception {
workspace.onDidChangeConfiguraton(this::handleConfigurationChange);
}
public Set<String> getRecipeDirectories() {
return settings.getStringSet("boot-java", "rewrite", "scan-directories");
}
public Set<String> getRecipeFiles() {
return settings.getStringSet("boot-java", "rewrite", "scan-files");
}
public Settings getRawSettings() {
return settings;
}

View File

@@ -317,7 +317,7 @@ public class BootLanguageServerBootApp {
};
}
@Bean RewriteRecipeRepository rewriteRecipesRepository(SimpleLanguageServer server, JavaProjectFinder projectFinder) {
return new RewriteRecipeRepository(server, projectFinder);
@Bean RewriteRecipeRepository rewriteRecipesRepository(SimpleLanguageServer server, JavaProjectFinder projectFinder, BootJavaConfig config) {
return new RewriteRecipeRepository(server, projectFinder, config);
}
}

View File

@@ -32,6 +32,7 @@ import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents
import org.springframework.ide.vscode.boot.java.links.JavaElementLocationProvider;
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveDataProvider;
import org.springframework.ide.vscode.boot.java.rewrite.RewriteRecipeRepository;
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
import org.springframework.ide.vscode.boot.java.utils.SymbolCache;
import org.springframework.ide.vscode.boot.metadata.ProjectBasedPropertyIndexProvider;
@@ -87,6 +88,7 @@ public class BootLanguageServerInitializer implements InitializingBean {
@Autowired SpringProjectsValidations springProjectsValidations;
@Autowired private JavaProjectFinder projectFinder;
@Autowired private LanguageServerProperties configProps;
@Autowired(required = false) private RewriteRecipeRepository recipesRepo;
@Qualifier("adHocProperties") @Autowired ProjectBasedPropertyIndexProvider adHocProperties;
@@ -170,21 +172,27 @@ public class BootLanguageServerInitializer implements InitializingBean {
components.getCodeActionProvider().ifPresent(documents::onCodeAction);
config.addListener(evt -> {
components.getReconcileEngine().ifPresent(reconciler -> {
log.info("A configuration changed, triggering reconcile on all open documents");
for (TextDocument doc : server.getTextDocumentService().getAll()) {
server.validateWith(doc.getId(), reconciler);
}
params.projectFinder.all().forEach(p -> validateProject(p, reconciler));
});
});
config.addListener(evt -> reconcile());
if (recipesRepo != null) {
recipesRepo.onRecipesLoaded(v -> reconcile());
}
addSpringProjectsVersionValidation(params);
server.getWorkspaceService().getFileObserver().onFilesChanged(FILES_TO_WATCH_GLOB, this::handleFiles);
server.getWorkspaceService().getFileObserver().onFilesCreated(FILES_TO_WATCH_GLOB, this::handleFiles);
}
private void reconcile() {
components.getReconcileEngine().ifPresent(reconciler -> {
log.info("A configuration changed, triggering reconcile on all open documents");
for (TextDocument doc : server.getTextDocumentService().getAll()) {
server.validateWith(doc.getId(), reconciler);
}
params.projectFinder.all().forEach(p -> validateProject(p, reconciler));
});
}
public CompositeLanguageServerComponents getComponents() {
Assert.notNull(components, "Not yet initialized, can't get components yet.");

View File

@@ -27,9 +27,6 @@ public class RewriteConfig implements InitializingBean {
@Autowired
private SimpleLanguageServer server;
@Autowired
private RewriteRecipeRepository recipeRepo;
@Autowired
private RewriteRefactorings rewriteRefactorings;
@@ -41,15 +38,7 @@ public class RewriteConfig implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
QuickfixRegistry registry = server.getQuickfixRegistry();
recipeRepo.loaded.thenAccept(v ->
recipeRepo.getProblemRecipeDescriptors().forEach(d -> {
if (recipeRepo.getRecipe(d.getRecipeId()).isPresent()) {
registry.register(d.getRecipeId(), rewriteRefactorings);
}
})
);
registry.register(RewriteRefactorings.REWRITE_RECIPE_QUICKFIX, rewriteRefactorings);
}
}

View File

@@ -0,0 +1,49 @@
/*******************************************************************************
* Copyright (c) 2022 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.boot.java.rewrite;
import java.util.List;
import org.springframework.ide.vscode.boot.java.rewrite.codeaction.AutowiredFieldIntoConstructorParameterCodeAction;
import org.springframework.ide.vscode.boot.java.rewrite.codeaction.BeanMethodsNotPublicCodeAction;
import org.springframework.ide.vscode.boot.java.rewrite.codeaction.NoRequestMappingAnnotationCodeAction;
import org.springframework.ide.vscode.boot.java.rewrite.codeaction.UnnecessarySpringExtensionCodeAction;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.BeanMethodNotPublicProblem;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.NoAutowiredOnConstructorProblem;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.PreciseBeanTypeProblem;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.UnnecessarySpringExtensionProblem;
import org.springframework.ide.vscode.commons.rewrite.config.CodeActionRepository;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeSpringJavaProblemDescriptor;
public class BootCodeActionRepository extends CodeActionRepository {
@Override
public List<RecipeCodeActionDescriptor> getCodeActionDescriptors() {
return List.of(
new AutowiredFieldIntoConstructorParameterCodeAction(),
new BeanMethodsNotPublicCodeAction(),
new NoRequestMappingAnnotationCodeAction(),
new UnnecessarySpringExtensionCodeAction()
);
}
@Override
public List<RecipeSpringJavaProblemDescriptor> getProblemDescriptors() {
return List.of(
new BeanMethodNotPublicProblem(),
new NoAutowiredOnConstructorProblem(),
new UnnecessarySpringExtensionProblem(),
new PreciseBeanTypeProblem()
);
}
}

View File

@@ -1,40 +0,0 @@
/*******************************************************************************
* Copyright (c) 2022 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.boot.java.rewrite;
import org.openrewrite.ExecutionContext;
import org.openrewrite.java.JavaVisitor;
import org.springframework.ide.vscode.commons.java.IJavaProject;
public interface RecipeCodeActionDescriptor {
String getRecipeId();
String getLabel(RecipeScope s);
RecipeScope[] getScopes();
JavaVisitor<ExecutionContext> getMarkerVisitor();
boolean isApplicable(IJavaProject project);
static String buildLabel(String label, RecipeScope s) {
switch (s) {
case FILE:
return label + " in file";
case PROJECT:
return label + " in project";
default:
return label;
}
}
}

View File

@@ -1,17 +0,0 @@
/*******************************************************************************
* Copyright (c) 2022 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.boot.java.rewrite;
public enum RecipeScope {
NODE,
FILE,
PROJECT
}

View File

@@ -37,10 +37,12 @@ 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.rewrite.reconcile.RecipeSpringJavaProblemDescriptor;
import org.springframework.ide.vscode.commons.java.IJavaProject;
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.config.RecipeScope;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeSpringJavaProblemDescriptor;
import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker;
import org.springframework.ide.vscode.commons.util.text.IDocument;
import org.springframework.ide.vscode.commons.util.text.IRegion;
@@ -99,9 +101,6 @@ public class RewriteCodeActionHandler implements JavaCodeActionHandler {
try {
// Wait for recipe repo to load if not loaded - should be loaded by the time we get here.
recipeRepo.loaded.get();
List<RecipeCodeActionDescriptor> 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 -> recipeRepo.getRecipe(d.getRecipeId()).isPresent())
@@ -177,7 +176,7 @@ public class RewriteCodeActionHandler implements JavaCodeActionHandler {
m.getRecipeId(),
doc.getUri(),
s,
m.getScope() == null ? null : m.getScope().toString(),
m.getScope() == null ? null : m.getScope(),
m.getParameters() == null ? Collections.emptyMap() : m.getParameters()
));
return ca;

View File

@@ -11,6 +11,10 @@
package org.springframework.ide.vscode.boot.java.rewrite;
import java.io.File;
import java.io.FileInputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
@@ -20,9 +24,11 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -40,31 +46,27 @@ import org.openrewrite.Result;
import org.openrewrite.SourceFile;
import org.openrewrite.TreeVisitor;
import org.openrewrite.Validated;
import org.openrewrite.config.Environment;
import org.openrewrite.config.RecipeDescriptor;
import org.openrewrite.config.YamlResourceLoader;
import org.openrewrite.internal.RecipeIntrospectionUtils;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.tree.J.CompilationUnit;
import org.openrewrite.maven.MavenParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.rewrite.codeaction.AutowiredFieldIntoConstructorParameterCodeAction;
import org.springframework.ide.vscode.boot.java.rewrite.codeaction.BeanMethodsNotPublicCodeAction;
import org.springframework.ide.vscode.boot.java.rewrite.codeaction.NoRequestMappingAnnotationCodeAction;
import org.springframework.ide.vscode.boot.java.rewrite.codeaction.UnnecessarySpringExtensionCodeAction;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.BeanMethodNotPublicProblem;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.PreciseBeanTypeProblem;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.NoAutowiredOnConstructorProblem;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.RecipeSpringJavaProblemDescriptor;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.UnnecessarySpringExtensionProblem;
import org.springframework.ide.vscode.boot.app.BootJavaConfig;
import org.springframework.ide.vscode.commons.java.IClasspath;
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.util.ListenerList;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.rewrite.LoadUtils;
import org.springframework.ide.vscode.commons.rewrite.LoadUtils.DurationTypeConverter;
import org.springframework.ide.vscode.commons.rewrite.ORDocUtils;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeSpringJavaProblemDescriptor;
import org.springframework.ide.vscode.commons.rewrite.config.StsEnvironment;
import org.springframework.ide.vscode.commons.rewrite.maven.MavenProjectParser;
import com.google.common.collect.ImmutableList;
@@ -76,6 +78,9 @@ import com.google.gson.JsonElement;
public class RewriteRecipeRepository {
private static final String CMD_REWRITE_RELOAD = "sts/rewrite/reload";
private static final String CMD_REWRITE_EXECUTE = "sts/rewrite/execute";
private static final String CMD_REWRITE_LIST = "sts/rewrite/list";
private static final Logger log = LoggerFactory.getLogger(RewriteRecipeRepository.class);
private static final String WORKSPACE_EXECUTE_COMMAND = "workspace/executeCommand";
@@ -84,11 +89,21 @@ public class RewriteRecipeRepository {
final private SimpleLanguageServer server;
final private Map<String, Recipe> recipes;
final private List<Recipe> globalCommandRecipes;
final private JavaProjectFinder projectFinder;
final public CompletableFuture<Void> loaded;
final private List<RecipeCodeActionDescriptor> codeActionDescriptors;
final private List<RecipeSpringJavaProblemDescriptor> javaProblemDescriptors;
final private ListenerList<Void> loadListeners;
private CompletableFuture<Void> loaded;
private Set<String> scanFiles = Collections.emptySet();
private Set<String> scanDirs = Collections.emptySet();
static final Set<String> TOP_LEVEL_RECIPES = Set.of(
"org.openrewrite.java.spring.boot2.SpringBoot2JUnit4to5Migration",
@@ -104,40 +119,62 @@ public class RewriteRecipeRepository {
.registerTypeAdapter(Duration.class, new DurationTypeConverter())
.create();
private List<RecipeCodeActionDescriptor> codeActionDescriptors = List.of(
new AutowiredFieldIntoConstructorParameterCodeAction(),
new BeanMethodsNotPublicCodeAction(),
new NoRequestMappingAnnotationCodeAction(),
new UnnecessarySpringExtensionCodeAction()
);
private List<RecipeSpringJavaProblemDescriptor> javaProblemDescriptors = List.of(
new BeanMethodNotPublicProblem(),
new NoAutowiredOnConstructorProblem(),
new UnnecessarySpringExtensionProblem(),
new PreciseBeanTypeProblem()
);
public RewriteRecipeRepository(SimpleLanguageServer server, JavaProjectFinder projectFinder) {
public RewriteRecipeRepository(SimpleLanguageServer server, JavaProjectFinder projectFinder, BootJavaConfig config) {
this.server = server;
this.projectFinder = projectFinder;
this.recipes = new HashMap<>();
this.globalCommandRecipes = new ArrayList<>();
this.loaded = CompletableFuture.runAsync(this::loadRecipes);
this.codeActionDescriptors = new ArrayList<>();
this.javaProblemDescriptors = new ArrayList<>();
this.loadListeners = new ListenerList<>();
server.doOnInitialized(() -> {
this.scanDirs = config.getRecipeDirectories();
this.scanFiles = config.getRecipeFiles();
load().thenAccept(v -> registerCommands());
config.addListener(l -> {
if (!scanDirs.equals(config.getRecipeDirectories())
|| !scanFiles.equals(config.getRecipeFiles())) {
// Eclipse client sends one event for init and the other config changed event due to remote app value expr listener.
// Therefore it is best to store the scanDirs here right after it is received, not during scan process or anything else done async
scanDirs = config.getRecipeDirectories();
scanFiles = config.getRecipeFiles();
load();
}
});
});
}
private void loadRecipes() {
public CompletableFuture<Void> load() {
return this.loaded = CompletableFuture.runAsync(() -> {
clearRecipes();
loadRecipes();
loadListeners.fire(null);
});
}
private synchronized void clearRecipes() {
recipes.clear();
globalCommandRecipes.clear();
codeActionDescriptors.clear();
javaProblemDescriptors.clear();
}
private synchronized void loadRecipes() {
try {
server.getProgressService().progressBegin(RECIPES_LOADING_PROGRESS, "Loading Rewrite Recipes", null);
log.info("Loading Rewrite Recipes...");
for (Recipe r : Environment.builder().scanRuntimeClasspath().build().listRecipes()) {
StsEnvironment env = createRewriteEnvironment();
for (Recipe r : env.listRecipes()) {
if (r.getName() != null) {
if (recipes.containsKey(r.getName())) {
log.error("Duplicate ids: '" + r.getName() + "'");
}
recipes.put(r.getName(), r);
if (TOP_LEVEL_RECIPES.contains(r.getName())) {
if (TOP_LEVEL_RECIPES.contains(r.getName()) || r.getName().startsWith("rewrite.test.")
|| r.getName().startsWith("org.rewrite.java.security")
|| r.getName().startsWith("org.springframework.rewrite.test")) {
Validated validation = Validated.invalid(null, null, null);
try {
validation = r.validate();
@@ -150,14 +187,47 @@ public class RewriteRecipeRepository {
}
}
}
javaProblemDescriptors.addAll(env.listProblemDescriptors());
codeActionDescriptors.addAll(env.listCodeActionDescriptors());
log.info("Done loading Rewrite Recipes");
server.doOnInitialized(() -> registerCommands());
} catch (Throwable t) {
server.getProgressService().progressDone(RECIPES_LOADING_PROGRESS);
log.error("", t);
} finally {
server.getProgressService().progressDone(RECIPES_LOADING_PROGRESS);
}
}
private StsEnvironment createRewriteEnvironment() {
StsEnvironment.Builder builder = (StsEnvironment.Builder) StsEnvironment.builder().scanRuntimeClasspath();
for (String p : scanFiles) {
try {
Path f = Path.of(p);
String pathStr = f.toString();
if (pathStr.endsWith(".jar")) {
URLClassLoader classLoader = new URLClassLoader(new URL[] { f.toUri().toURL() },
getClass().getClassLoader());
builder.scanJar(f, classLoader);
} else if (pathStr.endsWith(".yml") || pathStr.endsWith(".yaml")) {
builder.load(new YamlResourceLoader(new FileInputStream(f.toFile()), f.toUri(), new Properties()));
}
} catch (Exception e) {
log.error("Skipping folder " + p, e);
}
}
for (String p : scanDirs) {
try {
Path d = Path.of(p);
if (Files.isDirectory(d)) {
URLClassLoader classLoader = new URLClassLoader(new URL[] { d.toUri().toURL()}, getClass().getClassLoader());
builder.scanPath(d, classLoader);
}
} catch (Exception e) {
log.error("Skipping folder " + p, e);
}
}
return (StsEnvironment) builder.build();
}
public Optional<Recipe> getRecipe(String name) {
return Optional.ofNullable(recipes.get(name));
}
@@ -223,7 +293,7 @@ public class RewriteRecipeRepository {
Builder<Object> listBuilder = ImmutableList.builder();
server.onCommand("sts/rewrite/list", params -> {
server.onCommand(CMD_REWRITE_LIST, params -> {
JsonElement uri = (JsonElement) params.getArguments().get(0);
return loaded.thenApply(v -> {
if (uri == null) {
@@ -233,9 +303,9 @@ public class RewriteRecipeRepository {
}
});
});
listBuilder.add("sts/rewrite/list");
listBuilder.add(CMD_REWRITE_LIST);
server.onCommand("sts/rewrite/execute", params -> {
server.onCommand(CMD_REWRITE_EXECUTE, params -> {
String uri = ((JsonElement) params.getArguments().get(0)).getAsString();
JsonElement recipesJson = ((JsonElement) params.getArguments().get(1));
@@ -254,7 +324,10 @@ public class RewriteRecipeRepository {
return apply(aggregateRecipe, uri, progressToken);
}
});
listBuilder.add("sts/rewrite/execute");
listBuilder.add(CMD_REWRITE_EXECUTE);
server.onCommand(CMD_REWRITE_RELOAD, params -> load().thenApply((v) -> "executed"));
listBuilder.add(CMD_REWRITE_RELOAD);
for (Recipe r : globalCommandRecipes) {
listBuilder.add(createGlobalCommand(r));
@@ -271,7 +344,6 @@ public class RewriteRecipeRepository {
server.getClient().registerCapability(params).thenAccept((v) -> {
server.onShutdown(() -> server.getClient().unregisterCapability(new UnregistrationParams(List.of(new Unregistration(registrationId, WORKSPACE_EXECUTE_COMMAND)))));
log.info("Done registering commands for rewrite recipes");
server.getProgressService().progressDone(RECIPES_LOADING_PROGRESS);
});
}
@@ -370,6 +442,10 @@ public class RewriteRecipeRepository {
}
}
public void onRecipesLoaded(Consumer<Void> l) {
loadListeners.add(l);
}
// private static Recipe convert(Recipe r, RecipeDescriptor d) {
// try {
// if (d.selected) {

View File

@@ -14,7 +14,6 @@ import java.io.ByteArrayInputStream;
import java.net.URI;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -39,7 +38,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.app.BootJavaConfig;
import org.springframework.ide.vscode.boot.java.reconcilers.JavaReconciler;
import org.springframework.ide.vscode.boot.java.rewrite.RewriteRefactorings.Data;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.RecipeSpringJavaProblemDescriptor;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix.QuickfixData;
@@ -49,6 +47,8 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemC
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeSpringJavaProblemDescriptor;
import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker;
import org.springframework.ide.vscode.commons.rewrite.java.ORAstUtils;
import org.springframework.ide.vscode.commons.util.text.IDocument;
@@ -103,24 +103,26 @@ public class RewriteReconciler implements JavaReconciler {
if (range != null) {
RecipeSpringJavaProblemDescriptor recipeFixDescriptor = recipeRepo.getProblemRecipeDescriptor(m.getRecipeId());
if (recipeFixDescriptor != null && recipeFixDescriptor.getScopes() != null && recipeRepo.getRecipe(recipeFixDescriptor.getRecipeId()).isPresent()) {
return Arrays.stream(recipeFixDescriptor.getScopes()).map(s -> createProblemFromScope(doc, recipeFixDescriptor, s, m, range)).collect(Collectors.toList());
return List.of(createProblemFromScope(doc, recipeFixDescriptor, m, range));
}
}
}
return Collections.emptyList();
}
private ReconcileProblemImpl createProblemFromScope(IDocument doc, RecipeSpringJavaProblemDescriptor recipeFixDescriptor, RecipeScope s,
private ReconcileProblemImpl createProblemFromScope(IDocument doc, RecipeSpringJavaProblemDescriptor recipeFixDescriptor,
FixAssistMarker m, Range range) {
ProblemType problemType = recipeFixDescriptor.getProblemType();
ReconcileProblemImpl problem = new ReconcileProblemImpl(problemType, problemType.getLabel(), range.getStart().getOffset(), range.getEnd().getOffset() - range.getStart().getOffset());
QuickfixType quickfixType = quickfixRegistry.getQuickfixType(m.getRecipeId());
if (quickfixType != null) {
problem.addQuickfix(new QuickfixData<>(
quickfixType,
new Data(m.getRecipeId(), doc.getUri(), s, m.getScope().toString(), m.getParameters()),
recipeFixDescriptor.getLabel(s)
));
QuickfixType quickfixType = quickfixRegistry.getQuickfixType(RewriteRefactorings.REWRITE_RECIPE_QUICKFIX);
if (quickfixType != null && m.getRecipeId() != null) {
for (RecipeScope s : recipeFixDescriptor.getScopes()) {
problem.addQuickfix(new QuickfixData<>(
quickfixType,
new Data(m.getRecipeId(), doc.getUri(), s, m.getScope(), m.getParameters()),
recipeFixDescriptor.getLabel(s)
));
}
}
return problem;
}
@@ -159,10 +161,6 @@ public class RewriteReconciler implements JavaReconciler {
private List<RecipeSpringJavaProblemDescriptor> getProblemRecipeDescriptors(IJavaProject project)
throws InterruptedException, ExecutionException {
// Wait for recipe repo to load if not loaded - should be loaded by the time we
// get here.
recipeRepo.loaded.get();
return recipeRepo.getProblemRecipeDescriptors().stream().filter(d -> d.getProblemType() != null).filter(d -> {
switch (config.getProblemApplicability(d.getProblemType())) {
case ON:

View File

@@ -16,7 +16,6 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import org.eclipse.lsp4j.CodeAction;
@@ -31,6 +30,7 @@ import org.openrewrite.config.DeclarativeRecipe;
import org.openrewrite.internal.RecipeIntrospectionUtils;
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.commons.java.IJavaProject;
@@ -40,6 +40,7 @@ import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixHa
import org.springframework.ide.vscode.commons.languageserver.util.CodeActionResolver;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
import org.springframework.ide.vscode.commons.rewrite.ORDocUtils;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
import org.springframework.ide.vscode.commons.rewrite.java.ORAstUtils;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -50,6 +51,8 @@ import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
public class RewriteRefactorings implements CodeActionResolver, QuickfixHandler {
public static final String REWRITE_RECIPE_QUICKFIX = "org.openrewrite.rewrite";
private static final Logger log = LoggerFactory.getLogger(RewriteRefactorings.class);
@@ -163,11 +166,18 @@ public class RewriteRefactorings implements CodeActionResolver, QuickfixHandler
}
}
if (d.recipeScope == RecipeScope.NODE) {
UUID astNodeId = UUID.fromString(d.scope);
if (astNodeId == null) {
if (d.scope == null) {
throw new IllegalArgumentException("Missing scope AST node!");
} else {
r = ORAstUtils.nodeRecipe(r, j -> j != null && astNodeId.equals(j.getId()));
r = ORAstUtils.nodeRecipe(r, j -> {
if (j != null) {
Range range = j.getMarkers().findFirst(Range.class).orElse(null);
if (range != null) {
return d.scope.getStart().getOffset() <= range.getStart().getOffset() && range.getEnd().getOffset() <= d.scope.getEnd().getOffset();
}
}
return false;
});
}
}
return r;
@@ -177,9 +187,9 @@ public class RewriteRefactorings implements CodeActionResolver, QuickfixHandler
public String id;
public String docUri;
public RecipeScope recipeScope;
public String scope;
public Range scope;
public Map<String, Object> params;
public Data(String id, String docUri, RecipeScope recipeScope, String scope, Map<String, Object> params) {
public Data(String id, String docUri, RecipeScope recipeScope, Range scope, Map<String, Object> params) {
this.id = id;
this.docUri = docUri;
this.recipeScope = recipeScope;

View File

@@ -24,9 +24,10 @@ import org.openrewrite.java.tree.J.ClassDeclaration;
import org.openrewrite.java.tree.J.VariableDeclarations;
import org.openrewrite.java.tree.JavaType.FullyQualified;
import org.openrewrite.java.tree.TypeUtils;
import org.springframework.ide.vscode.boot.java.rewrite.RecipeCodeActionDescriptor;
import org.springframework.ide.vscode.boot.java.rewrite.RecipeScope;
import org.openrewrite.marker.Range;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
import org.springframework.ide.vscode.commons.rewrite.java.AnnotationHierarchies;
import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker;
@@ -68,7 +69,7 @@ public class AutowiredFieldIntoConstructorParameterCodeAction implements RecipeC
if (fqType != null && isApplicableType(fqType)) {
m = m.withMarkers(m.getMarkers().add(new FixAssistMarker(Tree.randomId())
.withRecipeId(getRecipeId())
.withScope(classDeclaration.getId())
.withScope(classDeclaration.getMarkers().findFirst(Range.class).get())
.withParameters(Map.of("classFqName", fqType.getFullyQualifiedName(), "fieldName", multiVariable.getVariables().get(0).getSimpleName()))));
}
}

View File

@@ -18,10 +18,11 @@ import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.TypeUtils;
import org.springframework.ide.vscode.boot.java.rewrite.RecipeCodeActionDescriptor;
import org.springframework.ide.vscode.boot.java.rewrite.RecipeScope;
import org.openrewrite.marker.Range;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker;
public class BeanMethodsNotPublicCodeAction implements RecipeCodeActionDescriptor {
@@ -60,7 +61,7 @@ public class BeanMethodsNotPublicCodeAction implements RecipeCodeActionDescripto
// mark public modifier
FixAssistMarker fixAssistMarker = new FixAssistMarker(Tree.randomId())
.withRecipeId(ID)
.withScope(m.getId());
.withScope(m.getMarkers().findFirst(Range.class).get());
m = m.withModifiers(ListUtils.map(m.getModifiers(), modifier -> {
if (modifier.getType() == J.Modifier.Type.Public) {
return modifier.withMarkers(modifier.getMarkers().add(fixAssistMarker));

View File

@@ -18,9 +18,10 @@ import org.openrewrite.java.AnnotationMatcher;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.tree.J;
import org.springframework.ide.vscode.boot.java.rewrite.RecipeCodeActionDescriptor;
import org.springframework.ide.vscode.boot.java.rewrite.RecipeScope;
import org.openrewrite.marker.Range;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker;
public class NoRequestMappingAnnotationCodeAction implements RecipeCodeActionDescriptor {
@@ -53,7 +54,7 @@ public class NoRequestMappingAnnotationCodeAction implements RecipeCodeActionDes
if (REQUEST_MAPPING_ANNOTATION_MATCHER.matches(a) && getCursor().getParentOrThrow().getValue() instanceof J.MethodDeclaration) {
FixAssistMarker fixAssistMarker = new FixAssistMarker(Tree.randomId())
.withRecipeId(getRecipeId())
.withScope(a.getId());
.withScope(a.getMarkers().findFirst(Range.class).get());
a = a.withMarkers(a.getMarkers().add(fixAssistMarker));
}
return a;

View File

@@ -14,7 +14,6 @@ import static org.springframework.ide.vscode.commons.java.SpringProjectUtil.spri
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Tree;
@@ -22,12 +21,13 @@ import org.openrewrite.internal.ListUtils;
import org.openrewrite.java.AnnotationMatcher;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.tree.TypeUtils;
import org.openrewrite.java.tree.J.ClassDeclaration;
import org.openrewrite.java.tree.JavaType.FullyQualified;
import org.springframework.ide.vscode.boot.java.rewrite.RecipeCodeActionDescriptor;
import org.springframework.ide.vscode.boot.java.rewrite.RecipeScope;
import org.openrewrite.java.tree.TypeUtils;
import org.openrewrite.marker.Range;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker;
public class UnnecessarySpringExtensionCodeAction implements RecipeCodeActionDescriptor {
@@ -80,10 +80,10 @@ public class UnnecessarySpringExtensionCodeAction implements RecipeCodeActionDes
FullyQualified fq = TypeUtils.asFullyQualified(a.getType());
return fq != null && SPRING_BOOT_TEST_ANNOTATIONS.contains(fq.getFullyQualifiedName());
})) {
UUID id = c.getId();
Range range = c.getMarkers().findFirst(Range.class).get();
c = c.withLeadingAnnotations(ListUtils.map(c.getLeadingAnnotations(), a -> {
if (SPRING_EXTENSION_ANNOTATIN_MATCHER.matches(a)) {
return a.withMarkers(a.getMarkers().add(new FixAssistMarker(Tree.randomId()).withRecipeId(ID).withScope(id)));
return a.withMarkers(a.getMarkers().add(new FixAssistMarker(Tree.randomId()).withRecipeId(ID).withScope(range)));
}
return a;
}));

View File

@@ -11,8 +11,9 @@
package org.springframework.ide.vscode.boot.java.rewrite.reconcile;
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
import org.springframework.ide.vscode.boot.java.rewrite.RecipeScope;
import org.springframework.ide.vscode.boot.java.rewrite.codeaction.BeanMethodsNotPublicCodeAction;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeSpringJavaProblemDescriptor;
public class BeanMethodNotPublicProblem extends BeanMethodsNotPublicCodeAction implements RecipeSpringJavaProblemDescriptor {

View File

@@ -21,12 +21,14 @@ import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.J.ClassDeclaration;
import org.openrewrite.java.tree.J.MethodDeclaration;
import org.openrewrite.java.tree.JavaType.FullyQualified;
import org.openrewrite.marker.Range;
import org.openrewrite.java.tree.Statement;
import org.openrewrite.java.tree.TypeUtils;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
import org.springframework.ide.vscode.boot.java.rewrite.RecipeScope;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeSpringJavaProblemDescriptor;
import org.springframework.ide.vscode.commons.rewrite.java.AnnotationHierarchies;
import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker;
@@ -76,7 +78,7 @@ public class NoAutowiredOnConstructorProblem implements RecipeSpringJavaProblemD
MethodDeclaration constructor = (MethodDeclaration) s;
FixAssistMarker fixAssistMarker = new FixAssistMarker(Tree.randomId())
.withRecipeId(ID)
.withScope(getCursor().firstEnclosing(ClassDeclaration.class).getId());
.withScope(getCursor().firstEnclosing(ClassDeclaration.class).getMarkers().findFirst(Range.class).get());
constructor = constructor.withLeadingAnnotations(ListUtils.map(constructor.getLeadingAnnotations(), a -> {
if (TypeUtils.isOfClassType(a.getType(), Annotations.AUTOWIRED)) {
a = a.withMarkers(a.getMarkers().add(fixAssistMarker));

View File

@@ -19,12 +19,14 @@ import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.J.Return;
import org.openrewrite.marker.Range;
import org.openrewrite.java.tree.JavaType;
import org.openrewrite.java.tree.TypeUtils;
import org.springframework.ide.vscode.boot.java.Boot3JavaProblemType;
import org.springframework.ide.vscode.boot.java.rewrite.RecipeCodeActionDescriptor;
import org.springframework.ide.vscode.boot.java.rewrite.RecipeScope;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeSpringJavaProblemDescriptor;
import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker;
public class PreciseBeanTypeProblem implements RecipeSpringJavaProblemDescriptor {
@@ -60,7 +62,7 @@ public class PreciseBeanTypeProblem implements RecipeSpringJavaProblemDescriptor
if ((o instanceof JavaType.FullyQualified && m.getReturnTypeExpression().getType() instanceof JavaType.FullyQualified)
|| (o instanceof JavaType.Array && m.getReturnTypeExpression().getType() instanceof JavaType.Array)) {
m = m.withReturnTypeExpression(m.getReturnTypeExpression().withMarkers(m.getReturnTypeExpression().getMarkers().add(
new FixAssistMarker(Tree.randomId()).withScope(m.getId()).withRecipeId(getRecipeId()))));
new FixAssistMarker(Tree.randomId()).withScope(m.getMarkers().findFirst(Range.class).get()).withRecipeId(getRecipeId()))));
}
}
}

View File

@@ -1,20 +0,0 @@
/*******************************************************************************
* Copyright (c) 2022 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.boot.java.rewrite.reconcile;
import org.springframework.ide.vscode.boot.java.rewrite.RecipeCodeActionDescriptor;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
public interface RecipeSpringJavaProblemDescriptor extends RecipeCodeActionDescriptor {
ProblemType getProblemType();
}

View File

@@ -11,8 +11,9 @@
package org.springframework.ide.vscode.boot.java.rewrite.reconcile;
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
import org.springframework.ide.vscode.boot.java.rewrite.RecipeScope;
import org.springframework.ide.vscode.boot.java.rewrite.codeaction.UnnecessarySpringExtensionCodeAction;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeSpringJavaProblemDescriptor;
public class UnnecessarySpringExtensionProblem extends UnnecessarySpringExtensionCodeAction
implements RecipeSpringJavaProblemDescriptor {