Skip to content

Commit

Permalink
Enhance autoformat logic for Groovy files
Browse files Browse the repository at this point in the history
  • Loading branch information
shanman190 committed Feb 16, 2024
1 parent 904ca30 commit 87835fa
Show file tree
Hide file tree
Showing 9 changed files with 649 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@
*/
package org.openrewrite.groovy;

import org.openrewrite.Cursor;
import org.openrewrite.SourceFile;
import org.openrewrite.groovy.format.MinimumViableSpacingVisitor;
import org.openrewrite.groovy.format.OmitParenthesesForLastArgumentLambdaVisitor;
import org.openrewrite.groovy.tree.*;
import org.openrewrite.internal.ListUtils;
import org.openrewrite.internal.lang.Nullable;
Expand Down Expand Up @@ -173,16 +170,4 @@ public <J2 extends J> JContainer<J2> visitContainer(JContainer<J2> container,
GContainer.Location loc, P p) {
return super.visitContainer(container, JContainer.Location.LANGUAGE_EXTENSION, p);
}

@Override
public <J2 extends J> J2 autoFormat(J2 j, @Nullable J stopAfter, P p, Cursor cursor) {
J after = super.autoFormat(j, stopAfter, p, cursor.fork());

after = new OmitParenthesesForLastArgumentLambdaVisitor<>(stopAfter).visitNonNull(after, p, cursor.fork());

after = new MinimumViableSpacingVisitor<>(stopAfter).visitNonNull(after, p, cursor.fork());

//noinspection unchecked
return (J2) after;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2024 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.groovy.format;

import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;

public class AutoFormat extends Recipe {
@Override
public String getDisplayName() {
return "Format Groovy code";
}

@Override
public String getDescription() {
return "Format Groovy code using a standard comprehensive set of Groovy formatting recipes.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new AutoFormatVisitor<>(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2024 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.groovy.format;

import org.openrewrite.Cursor;
import org.openrewrite.Tree;
import org.openrewrite.groovy.GroovyIsoVisitor;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.java.format.*;
import org.openrewrite.java.style.*;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaSourceFile;
import org.openrewrite.style.GeneralFormatStyle;

import java.util.Optional;

import static org.openrewrite.java.format.AutodetectGeneralFormatStyle.autodetectGeneralFormatStyle;

public class AutoFormatVisitor<P> extends GroovyIsoVisitor<P> {
@Nullable
private final Tree stopAfter;

@SuppressWarnings("unused")
public AutoFormatVisitor() {
this(null);
}

public AutoFormatVisitor(@Nullable Tree stopAfter) {
this.stopAfter = stopAfter;
}

@Override
public J visit(@Nullable Tree tree, P p, Cursor cursor) {
JavaSourceFile cu = (tree instanceof JavaSourceFile) ?
(JavaSourceFile) tree :
cursor.firstEnclosingOrThrow(JavaSourceFile.class);

J t = new MinimumViableSpacingVisitor<>(stopAfter).visit(tree, p, cursor.fork());

t = new BlankLinesVisitor<>(Optional.ofNullable(cu.getStyle(BlankLinesStyle.class))
.orElse(IntelliJ.blankLines()), stopAfter)
.visit(t, p, cursor.fork());

t = new NormalizeTabsOrSpacesVisitor<>(Optional.ofNullable(cu.getStyle(TabsAndIndentsStyle.class))
.orElse(IntelliJ.tabsAndIndents()), stopAfter)
.visit(t, p, cursor.fork());

t = new TabsAndIndentsVisitor<>(Optional.ofNullable(cu.getStyle(TabsAndIndentsStyle.class))
.orElse(IntelliJ.tabsAndIndents()), stopAfter)
.visit(t, p, cursor.fork());

t = new NormalizeLineBreaksVisitor<>(Optional.ofNullable(cu.getStyle(GeneralFormatStyle.class))
.orElse(autodetectGeneralFormatStyle(cu)), stopAfter)
.visit(t, p, cursor.fork());

t = new RemoveTrailingWhitespaceVisitor<>(stopAfter).visit(t, p, cursor.fork());

t = new OmitParenthesesForLastArgumentLambdaVisitor<>(stopAfter).visitNonNull(t, p, cursor.fork());

t = new MinimumViableSpacingVisitor<>(stopAfter).visitNonNull(t, p, cursor.fork());

return t;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2024 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.groovy.service;

import org.openrewrite.Tree;
import org.openrewrite.groovy.format.AutoFormatVisitor;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.service.AutoFormatService;

public class GroovyAutoFormatService extends AutoFormatService {
@Override
public <P> JavaVisitor<P> autoFormatVisitor(@Nullable Tree stopAfter) {
return new AutoFormatVisitor<>(stopAfter);
}
}
48 changes: 43 additions & 5 deletions rewrite-groovy/src/main/java/org/openrewrite/groovy/tree/G.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@
import org.openrewrite.groovy.GroovyPrinter;
import org.openrewrite.groovy.GroovyVisitor;
import org.openrewrite.groovy.internal.GroovyWhitespaceValidationService;
import org.openrewrite.groovy.service.GroovyAutoFormatService;
import org.openrewrite.internal.WhitespaceValidationService;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.java.internal.TypesInUse;
import org.openrewrite.java.service.AutoFormatService;
import org.openrewrite.java.tree.*;
import org.openrewrite.marker.Markers;

Expand Down Expand Up @@ -141,10 +143,22 @@ public G.CompilationUnit withPackageDeclaration(Package packageDeclaration) {
@SuppressWarnings("unchecked")
@Override
public <S, T extends S> T service(Class<S> service) {
if(WhitespaceValidationService.class.getName().equals(service.getName())) {
return (T) new GroovyWhitespaceValidationService();
String serviceName = service.getName();
try {
Class<S> serviceClass;
if (GroovyAutoFormatService.class.getName().equals(serviceName)) {
serviceClass = service;
} else if (AutoFormatService.class.getName().equals(serviceName)) {
serviceClass = (Class<S>) service.getClassLoader().loadClass(GroovyAutoFormatService.class.getName());
} else if (WhitespaceValidationService.class.getName().equals(serviceName)) {
serviceClass = (Class<S>) service.getClassLoader().loadClass(GroovyWhitespaceValidationService.class.getName());
} else {
return JavaSourceFile.super.service(service);
}
return (T) serviceClass.getConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
return JavaSourceFile.super.service(service);
}

List<JRightPadded<Statement>> statements;
Expand Down Expand Up @@ -271,7 +285,19 @@ public G.CompilationUnit withClasses(List<JRightPadded<ClassDeclaration>> classe
.map(i -> (JRightPadded<Statement>) (Object) i)
.collect(Collectors.toList()));

return t.getPadding().getClasses() == classes ? t : new G.CompilationUnit(t.id, t.shebang, t.prefix, t.markers, t.sourcePath, t.fileAttributes, t.charsetName, t.charsetBomMarked, t.checksum, t.packageDeclaration, statements, t.eof);
List<JRightPadded<ClassDeclaration>> originalClasses = t.getPadding().getClasses();
if (originalClasses.size() != classes.size()) {
return new G.CompilationUnit(t.id, t.shebang, t.prefix, t.markers, t.sourcePath, t.fileAttributes, t.charsetName, t.charsetBomMarked, t.checksum, t.packageDeclaration, statements, t.eof);
} else {
boolean hasChanges = false;
for (int i = 0; i < originalClasses.size(); i++) {
if (originalClasses.get(i) != classes.get(i)) {
hasChanges = true;
break;
}
}
return !hasChanges ? t : new G.CompilationUnit(t.id, t.shebang, t.prefix, t.markers, t.sourcePath, t.fileAttributes, t.charsetName, t.charsetBomMarked, t.checksum, t.packageDeclaration, statements, t.eof);
}
}

@Transient
Expand All @@ -294,7 +320,19 @@ public G.CompilationUnit withImports(List<JRightPadded<Import>> imports) {
.map(i -> (JRightPadded<Statement>) (Object) i)
.collect(Collectors.toList()));

return t.getPadding().getImports() == imports ? t : new G.CompilationUnit(t.id, t.shebang, t.prefix, t.markers, t.sourcePath, t.fileAttributes, t.charsetName, t.charsetBomMarked, t.checksum, t.packageDeclaration, statements, t.eof);
List<JRightPadded<Import>> originalImports = t.getPadding().getImports();
if (originalImports.size() != imports.size()) {
return new G.CompilationUnit(t.id, t.shebang, t.prefix, t.markers, t.sourcePath, t.fileAttributes, t.charsetName, t.charsetBomMarked, t.checksum, t.packageDeclaration, statements, t.eof);
} else {
boolean hasChanges = false;
for (int i = 0; i < originalImports.size(); i++) {
if (originalImports.get(i) != imports.get(i)) {
hasChanges = true;
break;
}
}
return !hasChanges ? t : new G.CompilationUnit(t.id, t.shebang, t.prefix, t.markers, t.sourcePath, t.fileAttributes, t.charsetName, t.charsetBomMarked, t.checksum, t.packageDeclaration, statements, t.eof);
}
}

public List<JRightPadded<Statement>> getStatements() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright 2024 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.groovy.format;

import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.groovy.Assertions.groovy;

class AutoFormatVisitorTest implements RewriteTest {

@Override
public void defaults(RecipeSpec spec) {
spec.recipe(new AutoFormat());
}

@DocumentExample
@Test
void keepMaximumBetweenHeaderAndPackage() {
rewriteRun(
groovy(
"""
/*
* This is a sample file.
*/
package com.intellij.samples
class Test {
}
""",
"""
/*
* This is a sample file.
*/
package com.intellij.samples
class Test {
}
"""
)
);
}
}
Loading

0 comments on commit 87835fa

Please sign in to comment.