Skip to content

Commit

Permalink
bean completion proposal implementation using rewrite recipes
Browse files Browse the repository at this point in the history
  • Loading branch information
vudayani committed Oct 3, 2024
1 parent 53dbdea commit 87dd369
Show file tree
Hide file tree
Showing 14 changed files with 2,353 additions and 14 deletions.
6 changes: 6 additions & 0 deletions headless-services/commons/commons-rewrite/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@
<artifactId>rewrite-spring</artifactId>
</dependency>

<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.ide.vscode</groupId>
<artifactId>commons-maven</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.eclipse.lsp4j.DeleteFile;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.ResourceOperation;
import org.eclipse.lsp4j.TextDocumentEdit;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4j.VersionedTextDocumentIdentifier;
Expand Down Expand Up @@ -72,6 +73,44 @@ public static Optional<DocumentEdits> computeEdits(IDocument doc, Result result)

}

public static Optional<DocumentEdits> computeDocumentEdits(WorkspaceEdit we, IDocument doc) {
if (!we.getDocumentChanges().isEmpty()) {
DocumentEdits edits = new DocumentEdits(doc, false);
List<Either<TextDocumentEdit, ResourceOperation>> changes = we.getDocumentChanges();
for (Either<TextDocumentEdit, ResourceOperation> change : changes) {
if (change.isLeft()) {
TextDocumentEdit textDocumentEdit = change.getLeft();
List<TextEdit> textEdits = textDocumentEdit.getEdits();
for (TextEdit textEdit : textEdits) {
Range range = textEdit.getRange();
Position start = range.getStart();
Position end = range.getEnd();
String newText = textEdit.getNewText();

try {
int startOffset = doc.getLineOffset(start.getLine()) + start.getCharacter();
int endOffset = doc.getLineOffset(end.getLine()) + end.getCharacter();

if (startOffset == endOffset) {
edits.insert(startOffset, newText);
} else if (newText.isEmpty()) {
edits.delete(startOffset, endOffset);
} else {
edits.replace(startOffset, endOffset, newText);
}
} catch (BadLocationException ex) {
log.error("Failed to apply text edit", ex);
}
}
}
}

return Optional.of(edits);
}
return Optional.empty();

}

public static Optional<TextDocumentEdit> computeTextDocEdit(TextDocument doc, String oldContent, String newContent, String changeAnnotationId) {
TextDocument newDoc = new TextDocument(null, LanguageId.PLAINTEXT, 0, newContent);

Expand Down Expand Up @@ -161,7 +200,7 @@ public static Optional<WorkspaceEdit> createWorkspaceEdit(SimpleTextDocumentServ
}
WorkspaceEdit we = new WorkspaceEdit();
we.setDocumentChanges(new ArrayList<>());
for (Result result : results) {
for (Result result : results) {
if (result.getBefore() == null) {
String docUri = result.getAfter().getSourcePath().toUri().toASCIIString();
createNewFileEdit(docUri, result.getAfter().printAll(), changeAnnotationId, we);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*******************************************************************************
* Copyright (c) 2017, 2024 Broadcom, 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:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.rewrite.java;

import org.openrewrite.Cursor;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.internal.lang.NonNull;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaType;
import org.openrewrite.java.tree.TypeUtils;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

/**
* @author Udayani V
*/
public class AddFieldRecipe extends Recipe {

@Override
public String getDisplayName() {
return "Add field";
}

@Override
public String getDescription() {
return "Add field desccription.";
}

@NonNull
@Nullable
String fullyQualifiedName;

@NonNull
@Nullable
String classFqName;

@JsonCreator
public AddFieldRecipe(@NonNull @JsonProperty("fullyQualifiedClassName") String fullyQualifiedName, @NonNull @JsonProperty("classFqName") String classFqName) {
this.fullyQualifiedName = fullyQualifiedName;
this.classFqName = classFqName;
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {

return new JavaIsoVisitor<ExecutionContext>() {

JavaType.FullyQualified fullyQualifiedType = JavaType.ShallowClass.build(fullyQualifiedName);
String fieldType = getFieldType(fullyQualifiedType);
String fieldName = getFieldName(fullyQualifiedType);

private final JavaTemplate fieldTemplate = JavaTemplate.builder("private final %s %s;"
.formatted(fieldType, fieldName))
.javaParser(JavaParser.fromJavaVersion()
.dependsOn(
"""
package %s;
public interface %s {}
""".formatted(fullyQualifiedType.getPackageName(), fullyQualifiedType.getClassName()),
"""
package %s;
public class A {
public class %s {
}
}
""".formatted(fullyQualifiedType.getPackageName(), fullyQualifiedType.getClassName()))
)
.imports(fullyQualifiedType.getFullyQualifiedName())
.contextSensitive()
.build();

@Override
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
if (TypeUtils.isOfClassType(classDecl.getType(), classFqName)) {

// Check if the class already has the field
boolean hasOwnerRepoField = classDecl.getBody().getStatements().stream()
.filter(J.VariableDeclarations.class::isInstance).map(J.VariableDeclarations.class::cast)
.anyMatch(varDecl -> varDecl.getTypeExpression() != null
&& varDecl.getTypeExpression().toString().equals(fieldType));

if (!hasOwnerRepoField) {
classDecl = classDecl.withBody(fieldTemplate.apply(new Cursor(getCursor(), classDecl.getBody()),
classDecl.getBody().getCoordinates().firstStatement()));

maybeAddImport(fullyQualifiedType.getFullyQualifiedName(), false);
}
return classDecl;
}
classDecl = (J.ClassDeclaration) super.visitClassDeclaration(classDecl, ctx);
return classDecl;
}
};
}

private static String getFieldName(JavaType.FullyQualified fullyQualifiedType) {
return Character.toLowerCase(fullyQualifiedType.getClassName().charAt(0)) + fullyQualifiedType.getClassName().substring(1);
}

private static String getFieldType(JavaType.FullyQualified fullyQualifiedType) {
if(fullyQualifiedType.getOwningClass() != null) {
String[] parts = fullyQualifiedType.getFullyQualifiedName().split("\\.");
if (parts.length < 2) {
return fullyQualifiedType.getClassName();
}
return parts[parts.length - 2] + "." + parts[parts.length - 1];
}

return fullyQualifiedType.getClassName();
}
}
Loading

0 comments on commit 87dd369

Please sign in to comment.