diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/format/TabsAndIndentsTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/format/TabsAndIndentsTest.java index 18b386bbcd..83595e1ac7 100644 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/format/TabsAndIndentsTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/format/TabsAndIndentsTest.java @@ -2385,8 +2385,8 @@ void recordComponents() { java( """ public record RenameRequest( - @NotBlank - @JsonProperty("name") String name) { + @NotBlank + @JsonProperty("name") String name) { } """ ) diff --git a/rewrite-java/src/main/java/org/openrewrite/java/format/SpacesVisitor.java b/rewrite-java/src/main/java/org/openrewrite/java/format/SpacesVisitor.java index 01236cff1c..44c5a15fa6 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/format/SpacesVisitor.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/format/SpacesVisitor.java @@ -239,6 +239,30 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, P ) ); } + + if (c.getPrimaryConstructor() != null) { + c = c.withPrimaryConstructor(ListUtils.map(c.getPrimaryConstructor(), (ix, param) -> { + if (param.getPrefix().getLastWhitespace().contains("\n")) { + return param; + } + if (ix == 0 && param.getComments().isEmpty()) { + if (!style.getWithin().getRecordHeader()) { + return param.withPrefix(param.getPrefix().withWhitespace("")); + } + } + return param.withPrefix(param.getPrefix().withWhitespace(" ")); + })); + JContainer.Padding padding = c.getPadding().getPrimaryConstructor().getPadding(); + c = c.getPadding().withPrimaryConstructor( + padding.withElements(ListUtils.mapLast(padding.getElements(), last -> { + if (!last.getAfter().getLastWhitespace().contains("\n")) { + return last.withAfter(last.getAfter().withWhitespace(!style.getWithin().getRecordHeader() ? "" : " ")); + } + return last; + } + ))); + } + return c; } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/format/TabsAndIndentsVisitor.java b/rewrite-java/src/main/java/org/openrewrite/java/format/TabsAndIndentsVisitor.java index b48b60c03c..7ebb6da5a4 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/format/TabsAndIndentsVisitor.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/format/TabsAndIndentsVisitor.java @@ -28,6 +28,7 @@ import java.util.Iterator; import java.util.List; +import static java.util.Collections.emptyList; import static org.openrewrite.java.format.ColumnPositionCalculator.computeColumnPosition; public class TabsAndIndentsVisitor

extends JavaIsoVisitor

{ @@ -201,6 +202,7 @@ public Space visitSpace(Space space, Space.Location loc, P p) { Space after; int indent = getCursor().getNearestMessage("lastIndent", 0); + IndentType indentType = getCursor().getParent().getNearestMessage("indentType", IndentType.ALIGN); if (right.getElement() instanceof J) { J elem = (J) right.getElement(); if ((right.getAfter().getLastWhitespace().contains("\n") || @@ -232,27 +234,52 @@ public Space visitSpace(Space space, Space.Location loc, P p) { JContainer container = getCursor().getParentOrThrow().getValue(); List elements = container.getElements(); J lastArg = elements.get(elements.size() - 1); - if (elements.size() > 1 && style.getMethodDeclarationParameters().getAlignWhenMultiple()) { - J.MethodDeclaration method = getCursor().firstEnclosing(J.MethodDeclaration.class); - if (method != null) { - int alignTo = computeFirstParameterColumn(method); - if (alignTo != -1) { - getCursor().getParentOrThrow().putMessage("lastIndent", alignTo - style.getContinuationIndent()); - elem = visitAndCast(elem, p); - getCursor().getParentOrThrow().putMessage("lastIndent", indent); - after = indentTo(right.getAfter(), t == lastArg ? indent : alignTo, loc.getAfterLocation()); + //noinspection ConstantConditions + J tree = null; + if (loc == JRightPadded.Location.METHOD_DECLARATION_PARAMETER) { + tree = getCursor().firstEnclosing(J.MethodDeclaration.class); + } else { + tree = getCursor().firstEnclosing(J.ClassDeclaration.class); + getCursor().getParentOrThrow().putMessage("indentType", IndentType.CONTINUATION_INDENT); + } + if (elements.size() > 1) { + try { + if ((loc == JRightPadded.Location.METHOD_DECLARATION_PARAMETER && style.getMethodDeclarationParameters().getAlignWhenMultiple()) || + (loc == JRightPadded.Location.RECORD_STATE_VECTOR && style.getRecordComponents().getAlignWhenMultiple())) { + if (tree != null) { + int alignTo = computeFirstParameterColumn(tree); + if (alignTo != -1) { + getCursor().getParentOrThrow().putMessage("lastIndent", alignTo - style.getContinuationIndent()); + elem = visitAndCast(elem, p); + getCursor().getParentOrThrow().putMessage("lastIndent", indent); + after = indentTo(right.getAfter(), t == lastArg ? indent : alignTo, loc.getAfterLocation()); + } else { + after = right.getAfter(); + } + } else { + after = right.getAfter(); + } } else { - after = right.getAfter(); + elem = visitAndCast(elem, p); + after = indentTo(right.getAfter(), t == lastArg ? indent : style.getContinuationIndent(), loc.getAfterLocation()); } - } else { - after = right.getAfter(); + } catch (NoSuchMethodError error) { + // style.getRecordComponents introduction might give NoSuchMethodError depending on the runtime, the lst build date... + elem = visitAndCast(elem, p); + after = indentTo(right.getAfter(), t == lastArg ? indent : style.getContinuationIndent(), loc.getAfterLocation()); } - } else if (elements.size() > 1) { - elem = visitAndCast(elem, p); - after = indentTo(right.getAfter(), t == lastArg ? indent : style.getContinuationIndent(), loc.getAfterLocation()); } else { + if (elem.getPrefix().getLastWhitespace().contains("\n") && tree != null) { + int alignTo = computeFirstParameterColumn(tree); + if (alignTo != -1) { + getCursor().getParentTreeCursor().putMessage("lastIndent", alignTo - style.getContinuationIndent()); + elem = visitAndCast(elem, p); + getCursor().getParentTreeCursor().putMessage("lastIndent", indent); + } + } after = right.getAfter(); } + getCursor().getParentOrThrow().putMessage("indentType", indentType); break; } case METHOD_INVOCATION_ARGUMENT: @@ -339,6 +366,8 @@ public Space visitSpace(Space space, Space.Location loc, P p) { switch (loc) { case NEW_CLASS_ARGUMENTS: case METHOD_INVOCATION_ARGUMENT: + case RECORD_STATE_VECTOR: + case METHOD_DECLARATION_PARAMETER: if (!elem.getPrefix().getLastWhitespace().contains("\n")) { JContainer args = getCursor().getParentOrThrow().getValue(); boolean anyOtherArgOnOwnLine = false; @@ -389,23 +418,27 @@ public Space visitSpace(Space space, Space.Location loc, P p) { return (after == right.getAfter() && t == right.getElement()) ? right : new JRightPadded<>(t, after, right.getMarkers()); } - private int computeFirstParameterColumn(J.MethodDeclaration method) { - List> arguments = method.getPadding().getParameters().getPadding().getElements(); + private int computeFirstParameterColumn(J tree) { + List> arguments; + if (tree instanceof J.MethodDeclaration) { + arguments = ((J.MethodDeclaration) tree).getPadding().getParameters().getPadding().getElements(); + } else if (tree instanceof J.ClassDeclaration) { + JContainer primaryConstructorArgs = ((J.ClassDeclaration) tree).getPadding().getPrimaryConstructor(); + arguments = primaryConstructorArgs == null ? emptyList() : primaryConstructorArgs.getPadding().getElements(); + } else { + return -1; + } J firstArg = arguments.isEmpty() ? null : arguments.get(0).getElement(); if (firstArg == null || firstArg instanceof J.Empty) { return -1; } if (firstArg.getPrefix().getLastWhitespace().contains("\n")) { - int declPrefixLength = getLengthOfWhitespace(method.getPrefix().getLastWhitespace()); - int argPrefixLength = getLengthOfWhitespace(firstArg.getPrefix().getLastWhitespace()); - //noinspection ConstantConditions to be backwards compatible with older style versions - if (declPrefixLength >= argPrefixLength) { - return declPrefixLength + style.getContinuationIndent(); - } - return argPrefixLength; + int declPrefixLength = getLengthOfWhitespace(tree.getPrefix().getLastWhitespace()); + + return declPrefixLength + style.getContinuationIndent(); } else { - return computeColumnPosition(method, firstArg, getCursor()); + return computeColumnPosition(tree, firstArg, getCursor()); } } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/style/Autodetect.java b/rewrite-java/src/main/java/org/openrewrite/java/style/Autodetect.java index 2c333b7e80..b18131a0bc 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/style/Autodetect.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/style/Autodetect.java @@ -277,7 +277,8 @@ public TabsAndIndentsStyle getTabsAndIndentsStyle() { continuationIndent, false, new TabsAndIndentsStyle.MethodDeclarationParameters( - multilineAlignedToFirstArgument >= multilineNotAlignedToFirstArgument) + multilineAlignedToFirstArgument >= multilineNotAlignedToFirstArgument), + new TabsAndIndentsStyle.RecordComponents(multilineAlignedToFirstArgument >= multilineNotAlignedToFirstArgument) ); } } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/style/IntelliJ.java b/rewrite-java/src/main/java/org/openrewrite/java/style/IntelliJ.java index 25ec3dc39b..70dee68791 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/style/IntelliJ.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/style/IntelliJ.java @@ -72,7 +72,7 @@ public static ImportLayoutStyle importLayout() { public static TabsAndIndentsStyle tabsAndIndents() { return new TabsAndIndentsStyle(false, 4, 4, 8, false, - new TabsAndIndentsStyle.MethodDeclarationParameters(true)); + new TabsAndIndentsStyle.MethodDeclarationParameters(true), new TabsAndIndentsStyle.RecordComponents(true)); } public static BlankLinesStyle blankLines() { diff --git a/rewrite-java/src/main/java/org/openrewrite/java/style/SpacesStyle.java b/rewrite-java/src/main/java/org/openrewrite/java/style/SpacesStyle.java index ef696d0edf..4e96cb7dce 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/style/SpacesStyle.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/style/SpacesStyle.java @@ -120,9 +120,6 @@ public static class Within { Boolean typeCastParentheses; Boolean annotationParentheses; Boolean angleBrackets; - - // Records are a java 14 preview feature (and still a preview language feature in java 15) - // rewrite does not consult this style setting for now Boolean recordHeader; } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/style/TabsAndIndentsStyle.java b/rewrite-java/src/main/java/org/openrewrite/java/style/TabsAndIndentsStyle.java index 0662cad30a..cb39c9eee6 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/style/TabsAndIndentsStyle.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/style/TabsAndIndentsStyle.java @@ -34,6 +34,7 @@ public class TabsAndIndentsStyle implements JavaStyle { Boolean indentsRelativeToExpressionStart; MethodDeclarationParameters methodDeclarationParameters; + RecordComponents recordComponents; @Value @With @@ -41,8 +42,19 @@ public static class MethodDeclarationParameters { Boolean alignWhenMultiple; } + @Value + @With + public static class RecordComponents { + Boolean alignWhenMultiple; + } + @Override public Style applyDefaults() { return StyleHelper.merge(IntelliJ.tabsAndIndents(), this); } + + public RecordComponents getRecordComponents() { + //noinspection ConstantConditions + return recordComponents == null ? new RecordComponents(true) : recordComponents; + } } diff --git a/rewrite-java/src/test/java/org/openrewrite/java/format/AutoFormatTest.java b/rewrite-java/src/test/java/org/openrewrite/java/format/AutoFormatTest.java index d04adbccd2..400372c0d7 100644 --- a/rewrite-java/src/test/java/org/openrewrite/java/format/AutoFormatTest.java +++ b/rewrite-java/src/test/java/org/openrewrite/java/format/AutoFormatTest.java @@ -42,7 +42,7 @@ public void defaults(RecipeSpec spec) { spec .parser(JavaParser.fromJavaVersion().dependsOn(""" package com.example; - + public class MyObject { public static Builder builder() { return new Builder(); } public static Builder newBuilder() { return new Builder(); } @@ -53,6 +53,9 @@ public static class Builder { Builder nested(MyObject nested) { return this; } MyObject build() { return new MyObject(); } } + + public static void outerMethod(String a, String b, String c) {} + public static String innerMethod(String x, String y, String z) { return ""; } } """)) .recipe(toRecipe(() -> new AutoFormatVisitor<>(null, @@ -584,7 +587,6 @@ void annotationWrappingModifiersAlreadyCorrect() { """, SourceSpec::skip), java( - """ @Foo @Foo @@ -1787,4 +1789,381 @@ MyObject process(List items) { ) ); } + + @Test + void recordSingleVariableDeclarationsIndented() { + rewriteRun( + java( + """ + import java.lang.annotation.Repeatable; + + @Repeatable(Foo.Foos.class) + @interface Foo { + @interface Foos { + Foo[] value(); + } + } + """, + SourceSpec::skip), + java( + """ + record someRecord1( + @Foo @Foo String name) { + } + """, + """ + record someRecord1( + @Foo @Foo String name) { + } + """ + ), + java( + """ + record someRecord2( + @Foo @Foo String name) { + } + """, + """ + record someRecord2( + @Foo @Foo String name) { + } + """ + ), + java( + """ + record someRecord3(@Foo @Foo String name) { + } + """ + ), + java( + """ + record someRecord4( @Foo @Foo String name) { + } + """, + """ + record someRecord4(@Foo @Foo String name) { + } + """ + ), + java( + """ + record someRecord5( @Foo @Foo String name + ) { + } + """, + """ + record someRecord5(@Foo @Foo String name + ) { + } + """ + ) + ); + } + + @Test + void recordMultipleVariableDeclarationsIndented() { + rewriteRun( + java( + """ + import java.lang.annotation.Repeatable; + + @Repeatable(Foo.Foos.class) + @interface Foo { + @interface Foos { + Foo[] value(); + } + } + """, + SourceSpec::skip), + java( + """ + record someRecord1( + @Foo @Foo String name, + int age) { + } + """, + """ + record someRecord1( + @Foo @Foo String name, + int age) { + } + """ + ), + java( + """ + record someRecord2( + @Foo @Foo String name, + int age) { + } + """, + """ + record someRecord2( + @Foo @Foo String name, + int age) { + } + """ + ), + java( + """ + record someRecord3(@Foo @Foo String name, int age) { + } + """ + ), + java( + """ + record someRecord4( @Foo @Foo String name, int age) { + } + """, + """ + record someRecord4(@Foo @Foo String name, int age) { + } + """ + ), + java( + """ + record someRecord5( @Foo @Foo String name, int age + ) { + } + """, + """ + record someRecord5(@Foo @Foo String name, int age + ) { + } + """ + ) + ); + } + + @Test + void methodSingleParameterIndented() { + rewriteRun( + java( + """ + class Test1 { + void someMethod1( + String name) { + } + } + """, + """ + class Test1 { + void someMethod1( + String name) { + } + } + """ + ), + java( + """ + class Test2 { + void someMethod2( + String name) { + } + } + """, + """ + class Test2 { + void someMethod2( + String name) { + } + } + """ + ), + java( + """ + class Test3 { + void someMethod3(String name) { + } + } + """ + ), + java( + """ + class Test4 { + void someMethod4( String name) { + } + } + """, + """ + class Test4 { + void someMethod4(String name) { + } + } + """ + ), + java( + """ + class Test5 { + void someMethod5( String name + ) { + } + } + """, + """ + class Test5 { + void someMethod5(String name + ) { + } + } + """ + ) + ); + } + + @Test + void methodMultipleParametersIndented() { + rewriteRun( + java( + """ + class Test1 { + void someMethod1( + String name, + int age) { + } + } + """, + """ + class Test1 { + void someMethod1( + String name, + int age) { + } + } + """ + ), + java( + """ + class Test2 { + void someMethod2( + String name, + int age) { + } + } + """, + """ + class Test2 { + void someMethod2( + String name, + int age) { + } + } + """ + ), + java( + """ + class Test3 { + void someMethod3(String name, int age) { + } + } + """ + ), + java( + """ + class Test4 { + void someMethod4( String name, int age) { + } + } + """, + """ + class Test4 { + void someMethod4(String name, int age) { + } + } + """ + ), + java( + """ + class Test5 { + void someMethod5( String name, int age + ) { + } + } + """, + """ + class Test5 { + void someMethod5(String name, int age + ) { + } + } + """ + ) + ); + } + + @Test + void nestedMethodInvocationWithMultipleArguments() { + rewriteRun( + java( + """ + package com.example; + + class Test1 { + void test() { + MyObject.outerMethod("arg1", + MyObject.innerMethod("nested1", "nested2", "nested3"), + "arg3"); + } + } + """, + """ + package com.example; + + class Test1 { + void test() { + MyObject.outerMethod("arg1", + MyObject.innerMethod("nested1", "nested2", "nested3"), + "arg3"); + } + } + """ + ), + java( + """ + package com.example; + + class Test2 { + void test() { + MyObject.outerMethod( + "arg1", + MyObject.innerMethod( + "nested1", + "nested2", + "nested3" + ), + "arg3" + ); + } + } + """, + """ + package com.example; + + class Test2 { + void test() { + MyObject.outerMethod( + "arg1", + MyObject.innerMethod( + "nested1", + "nested2", + "nested3" + ), + "arg3" + ); + } + } + """ + ), + java( + """ + package com.example; + + class Test3 { + void test() { + MyObject.outerMethod("arg1", MyObject.innerMethod("nested1", "nested2", "nested3"), "arg3"); + } + } + """ + ) + ); + } } diff --git a/rewrite-java/src/test/java/org/openrewrite/java/style/StyleHelperTest.java b/rewrite-java/src/test/java/org/openrewrite/java/style/StyleHelperTest.java index 3e81020b11..f0c1a5d4e9 100755 --- a/rewrite-java/src/test/java/org/openrewrite/java/style/StyleHelperTest.java +++ b/rewrite-java/src/test/java/org/openrewrite/java/style/StyleHelperTest.java @@ -25,12 +25,13 @@ class StyleHelperTest { @Test void mergeTabsAndIndentsStyles() { var merged = StyleHelper.merge(IntelliJ.tabsAndIndents(), - new TabsAndIndentsStyle(true, 1, 1, 2, true, new TabsAndIndentsStyle.MethodDeclarationParameters(true))); + new TabsAndIndentsStyle(true, 1, 1, 2, true, new TabsAndIndentsStyle.MethodDeclarationParameters(true), new TabsAndIndentsStyle.RecordComponents(true))); assertThat(merged.getUseTabCharacter()).isTrue(); assertThat(merged.getTabSize()).isEqualTo(1); assertThat(merged.getIndentSize()).isEqualTo(1); assertThat(merged.getContinuationIndent()).isEqualTo(2); assertThat(merged.getMethodDeclarationParameters().getAlignWhenMultiple()).isTrue(); + assertThat(merged.getRecordComponents().getAlignWhenMultiple()).isTrue(); assertThat(merged.getIndentsRelativeToExpressionStart()).isTrue(); }