From c64b6353b2499451fb2cfb0d7eecf2ed8c91ce3f Mon Sep 17 00:00:00 2001 From: Jordan Kiesel Date: Tue, 9 Jun 2026 23:35:11 -0600 Subject: [PATCH] fix: inline single simple type arguments --- src/printers/classes.ts | 13 +- src/printers/helpers.ts | 58 +++ src/printers/types-values-and-variables.ts | 21 +- test/unit-test/arrays/_output.java | 5 +- test/unit-test/member_chain/_input.java | 432 +++++++++--------- test/unit-test/member_chain/_output.java | 53 ++- .../wildcard/test.spec.ts | 2 +- test/unit-test/variables/_output.java | 8 +- 8 files changed, 346 insertions(+), 246 deletions(-) diff --git a/src/printers/classes.ts b/src/printers/classes.ts index a50fe3c9..b622033e 100644 --- a/src/printers/classes.ts +++ b/src/printers/classes.ts @@ -10,13 +10,13 @@ import { printBlockStatements, printBodyDeclarations, printModifiers, + printTypeParameters, printValue, printVariableDeclaration, type NamedNodePrinters } from "./helpers.ts"; -const { group, hardline, indent, indentIfBreak, join, line, softline } = - builders; +const { group, hardline, indent, indentIfBreak, join, line } = builders; export default { class_declaration(path, print) { @@ -58,14 +58,7 @@ export default { ]; }, - type_parameters(path, print) { - return [ - "<", - indent([softline, join([",", line], path.map(print, "namedChildren"))]), - softline, - ">" - ]; - }, + type_parameters: printTypeParameters, superclass(path, print) { return ["extends ", path.call(print, "namedChildren", 0)]; diff --git a/src/printers/helpers.ts b/src/printers/helpers.ts index a1da3add..db682bca 100644 --- a/src/printers/helpers.ts +++ b/src/printers/helpers.ts @@ -283,6 +283,36 @@ export function printBodyDeclarations( }, "namedChildren"); } +export function printTypeParameters( + path: NamedNodePath, + print: PrintFunction +) { + const parameters = path.node.namedChildren; + + const shouldInline = + parameters.length === 0 || + (parameters.length === 1 && + isSimpleType(parameters[0]) && + !parameters.some( + ({ comments }) => + comments?.length && + comments.some(({ type }) => type === SyntaxType.LineComment) + )); + + if (shouldInline) { + return ["<", join(", ", path.map(print, "namedChildren")), ">"]; + } + + const parts = [ + "<", + indent([softline, join([",", line], path.map(print, "namedChildren"))]), + softline, + ">" + ]; + + return path.node.type === SyntaxType.TypeArguments ? group(parts) : parts; +} + export function printVariableDeclaration( path: NamedNodePath< | SyntaxType.ConstantDeclaration @@ -364,6 +394,34 @@ export function textBlockContents(node: NamedNode) { .slice(0, -3); } +function isSimpleType(node: NamedNode): boolean { + const { type, children, namedChildren } = node; + const lastNamedChild = namedChildren.at(-1); + + return ( + [ + SyntaxType.BooleanType, + SyntaxType.FloatingPointType, + SyntaxType.IntegralType, + SyntaxType.TypeIdentifier, + SyntaxType.VoidType + ].includes(type) || + (type === SyntaxType.AnnotatedType && + lastNamedChild && + isSimpleType(lastNamedChild)) || + (type === SyntaxType.ArrayType && isSimpleType(node.elementNode)) || + (type === SyntaxType.ScopedTypeIdentifier && + lastNamedChild && + isSimpleType(namedChildren[0]) && + isSimpleType(lastNamedChild)) || + (type === SyntaxType.TypeParameter && + (lastNamedChild?.type !== SyntaxType.TypeBound || + lastNamedChild.namedChildren.length === 1)) || + (type === SyntaxType.Wildcard && + (children.at(-1)!.type === "?" || isSimpleType(namedChildren.at(-1)!))) + ); +} + function findBaseIndent(lines: string[]) { return Math.min( ...lines.map(line => line.search(/\S/)).filter(indent => indent >= 0) diff --git a/src/printers/types-values-and-variables.ts b/src/printers/types-values-and-variables.ts index a9a0ab16..ecb36a36 100644 --- a/src/printers/types-values-and-variables.ts +++ b/src/printers/types-values-and-variables.ts @@ -1,7 +1,11 @@ import { builders } from "prettier/doc"; -import { printValue, type NamedNodePrinters } from "./helpers.ts"; +import { + printTypeParameters, + printValue, + type NamedNodePrinters +} from "./helpers.ts"; -const { group, indent, join, line, softline } = builders; +const { group, indent, join, line } = builders; export default { boolean_type: printValue, @@ -42,18 +46,7 @@ export default { return bound; }, - type_arguments(path, print) { - const types = path.map(print, "namedChildren"); - - return types.length - ? group([ - "<", - indent([softline, join([",", line], types)]), - softline, - ">" - ]) - : "<>"; - }, + type_arguments: printTypeParameters, wildcard(path, print) { return join(" ", path.map(print, "children")); diff --git a/test/unit-test/arrays/_output.java b/test/unit-test/arrays/_output.java index e41f4c96..a3164a08 100644 --- a/test/unit-test/arrays/_output.java +++ b/test/unit-test/arrays/_output.java @@ -2,9 +2,8 @@ class Array { boolean[] skip = new boolean[candidates.length]; - Class aaaaaaaaaaaaaaaa = new Aaaaaaaaaaaaaaaa< - Bbbbbbbbbbbbbbbb - >[1].getClass(); + Class aaaaaaaaaaaaaaaa = + new Aaaaaaaaaaaaaaaa[1].getClass(); Class aaaaaaaaaaaaaaaa = new Aaaaaaaaaaaaaaaa[1111111111111111111].getClass(); Class aaaaaaaaaaaaaaaa = new Aaaaaaaaaaaaaaaa[] { diff --git a/test/unit-test/member_chain/_input.java b/test/unit-test/member_chain/_input.java index 5f076d31..5ee97241 100644 --- a/test/unit-test/member_chain/_input.java +++ b/test/unit-test/member_chain/_input.java @@ -1,213 +1,225 @@ public class BreakLongFunctionCall { - public void doSomething() { - return new Object().something().more(); - } - - public void doSomethingNewWithComment() { - // comment - new Object().something().more(); - - new Object() // comment - .something().more(); - - new Object() - // comment - .something().more(); - - new Object().something() // comment - .more(); - - new Object().something() - // comment - .more(); - - new Object().something().more(); // comment - - new Object().something().more(); - // comment - } - - public void doSomethingWithComment() { - Object - // comment - .something().more(); - - java.Object - // comment - .something().more(); - - java.averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong.Object - // comment - .something().more(); - - java - // comment - .averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong.Object - .something().more(); - - java - .averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong - // comment - .Object - .something().more(); - - java.averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong.Object - .something().more(); - - Object.something() - // comment - .more(); - - averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong.java - // comment - .util() - .java.java(); - - averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong - // comment - .java - .util() - .java.java(); - - averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong.java - /* comment */ - .util() - .java.java(); - - averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong.java/* comment */ - .util() - .java.java(); - - averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong.java - /* comment */.util() - .java.java(); - } - - public void doSomethingWithComment() { - object - // comment - .something().more(); - - object.something() - // comment - .more(); - } - - public void doSomethingNewWithComment() { - return new Object() - /* comment */ - .something().more(); - } - - public void doSomethingWithComment() { - return Object - /* comment */ - .something().more(); - } - - public void doSomethingWithComment() { - return object - /* comment */ - .something().more(); - } - - public void genericMethodWithLeadingComment() { - a - // 1 - .b() - // 2 - .c(); - } - - public void doSomethingLongNew() { - return something().more().and().that().as().well().but().not().something().something(); - } - - public void doSomethingLongWithArgument() { - return something().more(firstArgument, secondArgument).and(firstArgument, secondArgument, thirdArgument, fourthArgument, fifthArgument); - } - - public void doSomethingLongNew2() { - return new Object().something().more().and().that().as().well().but().not().something(); - } - - public void doSomethingLongStatic() { - return Object.something().more().and().that().as().well().but().not().something(); - } - - public void singleInvocationOnNewExpression() { - new Instance(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa).invocation(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa); - } - - public void multipleInvocationsOnNewExpression() { - new Instance(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa).invocation(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa).andAnother(); - } - - void methodReferences() { - dtoEntities.stream().map(UserDto::toString).forEach(LOGGER::info); - } - - void argumentComment() { - a( - // comment - b.c - ); - - a( - // comment - b.c[0] - ); - - a( - // comment - b.c() - ); - - a( - b.c // comment - ); - - a( - b.c[0] // comment - ); - - a( - b.c() // comment - ); - - a( - b, // comment - c.d - ); - - a( - b, // comment - c.d[0] - ); - - a( - b, // comment - c.d() - ); - } - - void prettierIgnore() { - a -> - // prettier-ignore - b - .c().d() - .e().f(); - } - - void complexArguments() { - "SOME TEXT" - .replace("SOME", "OTHER") - .replace("TEXT", "OTHER") - .replace( - "SOME", - """ - FOO""" - ); - } + public void doSomething() { + return new Object().something().more(); + } + + public void doSomethingNewWithComment() { + // comment + new Object().something().more(); + + new Object() // comment + .something().more(); + + new Object() + // comment + .something().more(); + + new Object().something() // comment + .more(); + + new Object().something() + // comment + .more(); + + new Object().something().more(); // comment + + new Object().something().more(); + // comment + } + + public void doSomethingWithComment() { + Object + // comment + .something().more(); + + java.Object + // comment + .something().more(); + + java.averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong.Object + // comment + .something().more(); + + java + // comment + .averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong.Object + .something().more(); + + java + .averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong + // comment + .Object + .something().more(); + + java.averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong.Object + .something().more(); + + Object.something() + // comment + .more(); + + averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong.java + // comment + .util() + .java.java(); + + averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong + // comment + .java + .util() + .java.java(); + + averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong.java + /* comment */ + .util() + .java.java(); + + averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong.java/* comment */ + .util() + .java.java(); + + averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong.java + /* comment */.util() + .java.java(); + } + + public void doSomethingWithComment() { + object + // comment + .something().more(); + + object.something() + // comment + .more(); + } + + public void doSomethingNewWithComment() { + return new Object() + /* comment */ + .something().more(); + } + + public void doSomethingWithComment() { + return Object + /* comment */ + .something().more(); + } + + public void doSomethingWithComment() { + return object + /* comment */ + .something().more(); + } + + public void genericMethodWithLeadingComment() { + a + // 1 + .b() + // 2 + .c(); + } + + public void doSomethingLongNew() { + return something().more().and().that().as().well().but().not().something().something(); + } + + public void doSomethingLongWithArgument() { + return something().more(firstArgument, secondArgument).and(firstArgument, secondArgument, thirdArgument, fourthArgument, fifthArgument); + } + + public void doSomethingLongNew2() { + return new Object().something().more().and().that().as().well().but().not().something(); + } + + public void doSomethingLongStatic() { + return Object.something().more().and().that().as().well().but().not().something(); + } + + public void singleInvocationOnNewExpression() { + new Instance(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa).invocation(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa); + } + + public void multipleInvocationsOnNewExpression() { + new Instance(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa).invocation(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa).andAnother(); + } + + void methodReferences() { + dtoEntities.stream().map(UserDto::toString).forEach(LOGGER::info); + } + + void argumentComment() { + a( + // comment + b.c + ); + + a( + // comment + b.c[0] + ); + + a( + // comment + b.c() + ); + + a( + b.c // comment + ); + + a( + b.c[0] // comment + ); + + a( + b.c() // comment + ); + + a( + b, // comment + c.d + ); + + a( + b, // comment + c.d[0] + ); + + a( + b, // comment + c.d() + ); + } + + void prettierIgnore() { + a -> + // prettier-ignore + b + .c().d() + .e().f(); + } + + void complexArguments() { + "SOME TEXT" + .replace("SOME", "OTHER") + .replace("TEXT", "OTHER") + .replace( + "SOME", + """ + FOO""" + ); + } + + void typeArguments() { + a().b().dddddddddd("eeeeeeeeee", "ffffffffff", "gggggggggg", "hhhhhhhhhh"); + + a().b().dddddddddddddddddddddddddddddddddddddddd("eeeeeeeeee"); + + a().b().hhhhhhhhhh("iiiiiiiiii"); + + a().b().iiiiiiiiii("jjjjjjjjjj"); + + a().b().iiiiiiiiii("jjjjjjjjjj", "kkkkkkkkkk", "llllllllll", "mmmmmmmmmm", "nnnnnnnnnn"); + } } diff --git a/test/unit-test/member_chain/_output.java b/test/unit-test/member_chain/_output.java index 96b1b5b3..78a37510 100644 --- a/test/unit-test/member_chain/_output.java +++ b/test/unit-test/member_chain/_output.java @@ -251,8 +251,8 @@ void prettierIgnore() { a -> // prettier-ignore b - .c().d() - .e().f(); + .c().d() + .e().f(); } void complexArguments() { @@ -265,4 +265,53 @@ void complexArguments() { FOO""" ); } + + void typeArguments() { + a() + .b() + .dddddddddd( + "eeeeeeeeee", + "ffffffffff", + "gggggggggg", + "hhhhhhhhhh" + ); + + a() + .b() + .dddddddddddddddddddddddddddddddddddddddd( + "eeeeeeeeee" + ); + + a() + .b() + .< + Cccccccccc, + Dddddddddd, + Eeeeeeeeee, + Ffffffffff, + Gggggggggg + >hhhhhhhhhh("iiiiiiiiii"); + + a() + .b() + .< + Cccccccccc, + Dddddddddd, + Eeeeeeeeee, + Ffffffffff, + Gggggggggg, + Hhhhhhhhhh + >iiiiiiiiii("jjjjjjjjjj"); + + a() + .b() + .< + Cccccccccc, + Dddddddddd, + Eeeeeeeeee, + Ffffffffff, + Gggggggggg, + Hhhhhhhhhh + >iiiiiiiiii("jjjjjjjjjj", "kkkkkkkkkk", "llllllllll", "mmmmmmmmmm", "nnnnnnnnnn"); + } } diff --git a/test/unit-test/snippets/types-values-and-variables/wildcard/test.spec.ts b/test/unit-test/snippets/types-values-and-variables/wildcard/test.spec.ts index 7a8bed5f..6d8c652c 100644 --- a/test/unit-test/snippets/types-values-and-variables/wildcard/test.spec.ts +++ b/test/unit-test/snippets/types-values-and-variables/wildcard/test.spec.ts @@ -19,7 +19,7 @@ describe("Wildcard", () => { const snippet = "List<@Annotation1 @Annotation2 @Annotation3 @Annotation4 @Annotation5 @Annotation6 @Annotation7 ?>l;"; const expectedOutput = - "List<\n @Annotation1 @Annotation2 @Annotation3 @Annotation4 @Annotation5 @Annotation6 @Annotation7 ?\n> l;\n"; + "List<@Annotation1 @Annotation2 @Annotation3 @Annotation4 @Annotation5 @Annotation6 @Annotation7 ?> l;\n"; await expectSnippetToBeFormatted({ snippet, diff --git a/test/unit-test/variables/_output.java b/test/unit-test/variables/_output.java index 308d6975..3d0eb773 100644 --- a/test/unit-test/variables/_output.java +++ b/test/unit-test/variables/_output.java @@ -268,9 +268,7 @@ public void breakAfterEquals() { & ExtremelyLongAndObnoxiousInterfaceName & ExtremelyLongAndObnoxiousInterfaceName > void breakOnTypeArguments( - ExtremelyLongAndObnoxiousClassName< - ExtremelyLongAndObnoxiousClassName - > parameter, + ExtremelyLongAndObnoxiousClassName parameter, ExtremelyLongAndObnoxiousClassName< ExtremelyLongAndObnoxiousClassName< ExtremelyLongAndObnoxiousClassName, @@ -279,9 +277,7 @@ > void breakOnTypeArguments( ExtremelyLongAndObnoxiousClassName > parameter ) { - ExtremelyLongAndObnoxiousClassName< - ExtremelyLongAndObnoxiousClassName - > variable; + ExtremelyLongAndObnoxiousClassName variable; ExtremelyLongAndObnoxiousClassName< ExtremelyLongAndObnoxiousClassName<