diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/config/LanguageServerProperties.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/config/LanguageServerProperties.java index 4802dbd20..784d70449 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/config/LanguageServerProperties.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/config/LanguageServerProperties.java @@ -56,7 +56,7 @@ public class LanguageServerProperties { * Reconcile sources regardless whether source is opened in an editor or not. * If on only opened documents will be reconciled */ - private boolean reconcileOnlyOpenedDocs = true; + private boolean reconcileOnlyOpenedDocs = false; public boolean isStandalone() { return standalone; diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java index 4e639ecb9..22fe8f73f 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java @@ -455,8 +455,11 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE int start = doc.toOffset(params.getRange().getStart()); int end = doc.toOffset(params.getRange().getEnd()); listBuilder.addAll(codeActionHandler.handle(cancelToken, capabilities, context, doc, new Region(start, end - start))); + } catch (BadLocationException e) { + // ignore bad location. Might come from stale doc version + log.debug("Stale range", e); } catch (Exception e) { - log.error("Failed to compute quick refactorings", e); + log.error("Failed to compute quick refactorings", e); } } diff --git a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/ConvertAutowiredFieldIntoConstructorParameter.java b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/ConvertAutowiredFieldIntoConstructorParameter.java index 4d6685444..f6c58efc5 100644 --- a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/ConvertAutowiredFieldIntoConstructorParameter.java +++ b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/ConvertAutowiredFieldIntoConstructorParameter.java @@ -10,32 +10,11 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.rewrite.java; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.openrewrite.Cursor; import org.openrewrite.ExecutionContext; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; -import org.openrewrite.java.AnnotationMatcher; -import org.openrewrite.java.JavaIsoVisitor; -import org.openrewrite.java.JavaTemplate; -import org.openrewrite.java.JavaVisitor; -import org.openrewrite.java.RemoveAnnotationVisitor; import org.openrewrite.java.search.UsesType; -import org.openrewrite.java.tree.J; -import org.openrewrite.java.tree.J.Block; -import org.openrewrite.java.tree.J.ClassDeclaration; -import org.openrewrite.java.tree.J.Empty; -import org.openrewrite.java.tree.J.MethodDeclaration; -import org.openrewrite.java.tree.J.VariableDeclarations; -import org.openrewrite.java.tree.JavaType.FullyQualified; -import org.openrewrite.java.tree.Statement; -import org.openrewrite.java.tree.TypeTree; -import org.openrewrite.java.tree.TypeUtils; +import org.openrewrite.java.spring.AutowiredFieldIntoConstructorParameterVisitor; public class ConvertAutowiredFieldIntoConstructorParameter extends Recipe { @@ -61,146 +40,7 @@ public class ConvertAutowiredFieldIntoConstructorParameter extends Recipe { @Override protected TreeVisitor getVisitor() { - return new JavaVisitor() { - - @Override - public J visitClassDeclaration(ClassDeclaration classDecl, ExecutionContext p) { - if (classFqName.equals(classDecl.getType().getFullyQualifiedName())) { - return super.visitClassDeclaration(classDecl, p); - } - return classDecl; - } - - @Override - public J visitVariableDeclarations(VariableDeclarations multiVariable, ExecutionContext p) { - Cursor blockCursor = getCursor().dropParentUntil(Block.class::isInstance); - VariableDeclarations mv = multiVariable; - if (blockCursor != null && blockCursor.getParent().getValue() instanceof ClassDeclaration - && multiVariable.getVariables().size() == 1 - && fieldName.equals(multiVariable.getVariables().get(0).getSimpleName())) { - - mv = (VariableDeclarations) new RemoveAnnotationVisitor(new AnnotationMatcher("@" + AUTOWIRED)).visit(multiVariable, p); - doAfterVisit(new AddContructorParameterVisitor(classFqName, fieldName, multiVariable.getTypeExpression())); - } - return mv; - } - - }; + return new AutowiredFieldIntoConstructorParameterVisitor(classFqName, fieldName); } - private static class AddContructorParameterVisitor extends JavaVisitor { - - private String classFqName; - private String fieldName; - private TypeTree type; - - public AddContructorParameterVisitor(String classFqName, String fieldName, TypeTree type) { - super(); - this.classFqName = classFqName; - this.fieldName = fieldName; - this.type = type; - } - - @Override - public J visitClassDeclaration(ClassDeclaration classDecl, ExecutionContext p) { - ClassDeclaration c = classDecl; - if (classFqName.equals(c.getType().getFullyQualifiedName())) { - List constructors = ORAstUtils.getMethods(c).stream().filter(m -> m.isConstructor()).collect(Collectors.toList()); - if (constructors.isEmpty()) { - doAfterVisit(new AddConstructorVisitor(c.getSimpleName(), fieldName, type)); - } else { - Optional autowiredConstructor = constructors.stream().filter(constr -> constr.getLeadingAnnotations().stream() - .map(a -> TypeUtils.asFullyQualified(a.getType())) - .filter(Objects::nonNull) - .map(fq -> fq.getFullyQualifiedName()) - .filter(fqn -> AUTOWIRED.equals(fqn)) - .findFirst() - .isPresent() - ) - .findFirst(); - if (autowiredConstructor.isPresent()) { - // Autowired constructor found - add argument to it - doAfterVisit(new AddMethodParameter(autowiredConstructor.get(), fieldName, type)); - } else { - if (constructors.size() == 1) { - doAfterVisit(new AddMethodParameter(constructors.get(0), fieldName, type)); - } - } - } - } - return c; - } - - } - - private static class AddConstructorVisitor extends JavaVisitor { - private String className; - private String fieldName; - private TypeTree type; - - public AddConstructorVisitor(String className, String fieldName, TypeTree type) { - this.className = className; - this.fieldName = fieldName; - this.type = type; - } - - @Override - public J visitBlock(Block block, ExecutionContext p) { - if (getCursor().getParent() != null) { - Object n = getCursor().getParent().getValue(); - if (n instanceof ClassDeclaration) { - ClassDeclaration classDecl = (ClassDeclaration) n; - if (classDecl.getKind() == ClassDeclaration.Kind.Type.Class && className.equals(classDecl.getSimpleName())) { - JavaTemplate.Builder template = JavaTemplate.builder(() -> getCursor(), "" - + classDecl.getSimpleName() + "(" + type.printTrimmed() + " " + fieldName + ") {\n" - + "this." + fieldName + " = " + fieldName + ";\n" - + "}\n" - ); - FullyQualified fq = TypeUtils.asFullyQualified(type.getType()); - if (fq != null) { - template.imports(fq.getFullyQualifiedName()); - maybeAddImport(fq); - } - Optional firstMethod = block.getStatements().stream().filter(MethodDeclaration.class::isInstance).findFirst(); - if (firstMethod.isPresent()) { - return block.withTemplate(template.build(), firstMethod.get().getCoordinates().before()); - } else { - return block.withTemplate(template.build(), block.getCoordinates().lastStatement()); - } - } - } - } - return block; - } - } - - private static class AddMethodParameter extends JavaIsoVisitor { - - private MethodDeclaration method; - private String fieldName; - private TypeTree type; - - public AddMethodParameter(MethodDeclaration method, String fieldName, TypeTree type) { - this.method = method; - this.fieldName = fieldName; - this.type = type; - } - - @Override - public MethodDeclaration visitMethodDeclaration(MethodDeclaration method, ExecutionContext p) { - if (method == this.method) { - String paramsStr = Stream.concat(method.getParameters().stream().filter(s -> !Empty.class.isInstance(s)).map(s -> s.printTrimmed()), Stream.of(type.printTrimmed() + " " + fieldName)).collect(Collectors.joining(", ")); - JavaTemplate.Builder paramsTemplate = JavaTemplate.builder(() -> getCursor(), paramsStr); - JavaTemplate.Builder statementTemplate = JavaTemplate.builder(() -> getCursor(), "this." + fieldName + " = " + fieldName + ";\n"); - return method - .withTemplate(paramsTemplate.build(), method.getCoordinates().replaceParameters()) - .withTemplate(statementTemplate.build(), method.getBody().getCoordinates().lastStatement()); - } - return method; - } - - - - } - } \ No newline at end of file diff --git a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/ORAstUtils.java b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/ORAstUtils.java index b38b6b089..108c04ce5 100644 --- a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/ORAstUtils.java +++ b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/ORAstUtils.java @@ -24,6 +24,7 @@ import org.openrewrite.ExecutionContext; import org.openrewrite.InMemoryExecutionContext; import org.openrewrite.Parser; import org.openrewrite.Recipe; +import org.openrewrite.RecipeRun; import org.openrewrite.Result; import org.openrewrite.Tree; import org.openrewrite.TreeVisitor; @@ -226,7 +227,8 @@ public class ORAstUtils { synchronized(parser) { cus = parser.parse(sourceFiles, null, ctx); } - List results = new UpdateSourcePositions()/*.doNext(new MarkParentRecipe())*/.run(cus); + RecipeRun reciperun = new UpdateSourcePositions()/*.doNext(new MarkParentRecipe())*/.run(cus); + List results = reciperun.getResults(); return results.stream().map(r -> r.getAfter() == null ? r.getBefore() : r.getAfter()).map(CompilationUnit.class::cast).collect(Collectors.toList()); } @@ -237,7 +239,8 @@ public class ORAstUtils { synchronized (parser) { cus = parser.parseInputs(inputs, null, ctx); } - List results = new UpdateSourcePositions()/*.doNext(new MarkParentRecipe())*/.run(cus); + RecipeRun reciperun = new UpdateSourcePositions()/*.doNext(new MarkParentRecipe())*/.run(cus); + List results = reciperun.getResults(); return results.stream().map(r -> r.getAfter() == null ? r.getBefore() : r.getAfter()).map(CompilationUnit.class::cast).collect(Collectors.toList()); } diff --git a/headless-services/commons/pom.xml b/headless-services/commons/pom.xml index e7864465b..25eb43733 100644 --- a/headless-services/commons/pom.xml +++ b/headless-services/commons/pom.xml @@ -109,10 +109,10 @@ 1.13 - 7.26.1 - 4.23.0 - 1.8.0 - 2.13.2 + 7.30.0 + 4.27.0 + 1.11.0 + 2.13.3 true vmware diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRecipeRepository.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRecipeRepository.java index 657e083bb..9d3a3279b 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRecipeRepository.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRecipeRepository.java @@ -42,6 +42,7 @@ import org.eclipse.lsp4j.WorkspaceEdit; import org.openrewrite.ExecutionContext; import org.openrewrite.InMemoryExecutionContext; import org.openrewrite.Recipe; +import org.openrewrite.RecipeRun; import org.openrewrite.Result; import org.openrewrite.SourceFile; import org.openrewrite.TreeVisitor; @@ -399,7 +400,8 @@ public class RewriteRecipeRepository { new InMemoryExecutionContext()); List sources = projectParser.parse(absoluteProjectDir, getClasspathEntries(project)); server.getProgressService().progressEvent(r.getName(), "Computing changes..."); - List results = r.run(sources, new InMemoryExecutionContext(e -> log.error("", e))); + RecipeRun reciperun = r.run(sources, new InMemoryExecutionContext(e -> log.error("", e))); + List results = reciperun.getResults(); return ORDocUtils.createWorkspaceEdit(absoluteProjectDir, server.getTextDocumentService(), results); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRefactorings.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRefactorings.java index 6bd20f55f..b230daa7a 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRefactorings.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRefactorings.java @@ -25,6 +25,7 @@ import org.eclipse.lsp4j.TextDocumentIdentifier; import org.eclipse.lsp4j.WorkspaceEdit; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.openrewrite.Recipe; +import org.openrewrite.RecipeRun; import org.openrewrite.Result; import org.openrewrite.config.DeclarativeRecipe; import org.openrewrite.internal.RecipeIntrospectionUtils; @@ -114,7 +115,8 @@ public class RewriteRefactorings implements CodeActionResolver, QuickfixHandler } private WorkspaceEdit applyRecipe(Recipe r, IJavaProject project, List cus) { - List results = r.run(cus); + RecipeRun reciperun = r.run(cus); + List results = reciperun.getResults(); List> edits = results.stream().filter(res -> res.getAfter() != null).map(res -> { URI docUri = res.getAfter().getSourcePath().isAbsolute() ? res.getAfter().getSourcePath().toUri() : project.getLocationUri().resolve(res.getAfter().getSourcePath().toString()); TextDocument doc = documents.getLatestSnapshot(docUri.toString()); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/AutowiredFieldIntoConstructorParameterCodeAction.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/AutowiredFieldIntoConstructorParameterCodeAction.java index e5bb1f2d1..67f2a14cc 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/AutowiredFieldIntoConstructorParameterCodeAction.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/AutowiredFieldIntoConstructorParameterCodeAction.java @@ -12,15 +12,20 @@ package org.springframework.ide.vscode.boot.java.rewrite.codeaction; import static org.springframework.ide.vscode.commons.java.SpringProjectUtil.springBootVersionGreaterOrEqual; +import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; import org.openrewrite.Cursor; import org.openrewrite.ExecutionContext; import org.openrewrite.Tree; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaVisitor; +import org.openrewrite.java.spring.AutowiredFieldIntoConstructorParameterVisitor; import org.openrewrite.java.tree.J.Block; import org.openrewrite.java.tree.J.ClassDeclaration; +import org.openrewrite.java.tree.J.MethodDeclaration; import org.openrewrite.java.tree.J.VariableDeclarations; import org.openrewrite.java.tree.JavaType.FullyQualified; import org.openrewrite.java.tree.TypeUtils; @@ -30,6 +35,7 @@ import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDes 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; +import org.springframework.ide.vscode.commons.rewrite.java.ORAstUtils; public class AutowiredFieldIntoConstructorParameterCodeAction implements RecipeCodeActionDescriptor { @@ -67,10 +73,29 @@ public class AutowiredFieldIntoConstructorParameterCodeAction implements RecipeC ClassDeclaration classDeclaration = (ClassDeclaration) blockCursor.getParent().getValue(); FullyQualified fqType = TypeUtils.asFullyQualified(classDeclaration.getType()); if (fqType != null && isApplicableType(fqType)) { - m = m.withMarkers(m.getMarkers().add(new FixAssistMarker(Tree.randomId()) + List constructors = ORAstUtils.getMethods(classDeclaration).stream().filter(c -> c.isConstructor()).limit(2).collect(Collectors.toList()); + String fieldName = multiVariable.getVariables().get(0).getSimpleName(); + FixAssistMarker marker = new FixAssistMarker(Tree.randomId()) .withRecipeId(getRecipeId()) .withScope(classDeclaration.getMarkers().findFirst(Range.class).get()) - .withParameters(Map.of("classFqName", fqType.getFullyQualifiedName(), "fieldName", multiVariable.getVariables().get(0).getSimpleName())))); + .withParameters(Map.of("classFqName", fqType.getFullyQualifiedName(), "fieldName", fieldName)); + if (constructors.size() == 0) { + m = m.withMarkers(m.getMarkers().add(marker)); + } else if (constructors.size() == 1 && !AutowiredFieldIntoConstructorParameterVisitor.isConstructorInitializingField(constructors.get(0), fieldName)) { + m = m.withMarkers(m.getMarkers().add(marker)); + } else { + List autowiredConstructors = constructors.stream().filter(constr -> constr.getLeadingAnnotations().stream() + .map(a -> TypeUtils.asFullyQualified(a.getType())) + .filter(Objects::nonNull) + .map(FullyQualified::getFullyQualifiedName) + .anyMatch(AUTOWIRED::equals) + ) + .limit(2) + .collect(Collectors.toList()); + if (autowiredConstructors.size() == 1 && !AutowiredFieldIntoConstructorParameterVisitor.isConstructorInitializingField(autowiredConstructors.get(0), fieldName)) { + m = m.withMarkers(m.getMarkers().add(marker)); + } + } } } return m;