From 3e01297224ef2a501686a2011044cf2d9876c2ce Mon Sep 17 00:00:00 2001 From: Jordan Kiesel Date: Sat, 26 Apr 2025 17:34:22 -0600 Subject: [PATCH 1/2] refactor: rename printer root files to TypeScript --- packages/prettier-plugin-java/src/{index.js => index.ts} | 0 packages/prettier-plugin-java/src/{options.js => options.ts} | 0 packages/prettier-plugin-java/src/{parser.js => parser.ts} | 0 packages/prettier-plugin-java/src/{printer.js => printer.ts} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename packages/prettier-plugin-java/src/{index.js => index.ts} (100%) rename packages/prettier-plugin-java/src/{options.js => options.ts} (100%) rename packages/prettier-plugin-java/src/{parser.js => parser.ts} (100%) rename packages/prettier-plugin-java/src/{printer.js => printer.ts} (100%) diff --git a/packages/prettier-plugin-java/src/index.js b/packages/prettier-plugin-java/src/index.ts similarity index 100% rename from packages/prettier-plugin-java/src/index.js rename to packages/prettier-plugin-java/src/index.ts diff --git a/packages/prettier-plugin-java/src/options.js b/packages/prettier-plugin-java/src/options.ts similarity index 100% rename from packages/prettier-plugin-java/src/options.js rename to packages/prettier-plugin-java/src/options.ts diff --git a/packages/prettier-plugin-java/src/parser.js b/packages/prettier-plugin-java/src/parser.ts similarity index 100% rename from packages/prettier-plugin-java/src/parser.js rename to packages/prettier-plugin-java/src/parser.ts diff --git a/packages/prettier-plugin-java/src/printer.js b/packages/prettier-plugin-java/src/printer.ts similarity index 100% rename from packages/prettier-plugin-java/src/printer.js rename to packages/prettier-plugin-java/src/printer.ts From 58c5cffdf73c0b0bb53bc109f8daaac31a888390 Mon Sep 17 00:00:00 2001 From: Jordan Kiesel Date: Sun, 18 May 2025 23:41:32 -0600 Subject: [PATCH 2/2] refactor: leverage Prettier's AstPath and comment attachment --- package.json | 2 +- packages/java-parser/api.d.ts | 283 ++- packages/java-parser/docs/comments.md | 58 - packages/java-parser/package.json | 4 +- packages/java-parser/src/comments.js | 308 ---- packages/java-parser/src/index.js | 14 +- packages/java-parser/src/parser.js | 14 - .../src/productions/expressions.js | 23 +- .../src/productions/lexical-structure.js | 24 + .../src/productions/packages-and-modules.js | 5 +- packages/prettier-plugin-java/.gitignore | 1 + packages/prettier-plugin-java/index.d.ts | 4 - packages/prettier-plugin-java/package.json | 9 +- .../src/base-cst-printer.ts | 86 - packages/prettier-plugin-java/src/comments.ts | 272 +++ .../prettier-plugin-java/src/cst-printer.ts | 45 - packages/prettier-plugin-java/src/index.ts | 103 +- packages/prettier-plugin-java/src/options.ts | 490 +++--- packages/prettier-plugin-java/src/parser.ts | 33 +- packages/prettier-plugin-java/src/printer.ts | 62 +- .../src/printers/arrays.ts | 51 +- .../src/printers/blocks-and-statements.ts | 972 ++++------- .../src/printers/classes.ts | 1522 ++++++---------- .../src/printers/comments/comments-utils.ts | 47 - .../src/printers/comments/format-comments.ts | 223 --- .../src/printers/comments/handle-comments.ts | 132 -- .../src/printers/expressions.ts | 1543 ++++++++--------- .../src/printers/helpers.ts | 420 +++++ .../src/printers/index.ts | 28 + .../src/printers/interfaces.ts | 539 +++--- .../src/printers/lexical-structure.ts | 71 +- .../src/printers/names.ts | 59 +- .../src/printers/packages-and-modules.ts | 452 +++-- .../src/printers/prettier-builder.ts | 66 - .../src/printers/printer-utils.ts | 848 --------- .../printers/types-values-and-variables.ts | 326 ++-- .../prettier-plugin-java/src/types/utils.ts | 47 - .../prettier-plugin-java/src/utils/index.ts | 2 - .../src/utils/isEmptyDoc.ts | 7 - .../src/utils/printArgumentListWithBraces.ts | 44 - .../prettier-plugin-java/test/test-utils.ts | 59 +- .../test/unit-test/arrays/_output.java | 4 +- .../binary_expressions-spec.ts | 12 +- .../{ => operator-position-end}/_input.java | 3 - .../{ => operator-position-end}/_output.java | 6 +- .../operator-position-start/_input.java | 135 ++ .../operator-position-start/_output.java | 248 +++ .../test/unit-test/blank_lines/_output.java | 1 - .../unit-test/comments/bug-fixes/_output.java | 6 +- .../complex/_output.java | 71 +- .../end-of-block/_output.java | 55 +- .../if-statement/_input.java | 5 + .../if-statement/_output.java | 27 +- .../labeled-statement/_output.java | 4 + .../unit-test/comments/interface/_output.java | 10 +- .../unit-test/comments/package/_output.java | 17 +- .../unit-test/empty_statement/_output.java | 46 +- .../test/unit-test/enum/_output.java | 9 +- .../test/unit-test/expressions/_output.java | 5 +- .../test/unit-test/for/_input.java | 4 + .../test/unit-test/for/_output.java | 6 + .../test/unit-test/instantiation/_input.java | 2 +- .../test/unit-test/instantiation/_output.java | 3 +- .../test/unit-test/lambda/_output.java | 17 +- .../test/unit-test/member_chain/_output.java | 23 +- .../test/unit-test/records/_output.java | 3 +- .../classes/variableDeclarator/test.spec.ts | 2 +- .../numericType/test.spec.ts | 25 - .../wildcard/test.spec.ts | 13 - .../wildcardBounds/test.spec.ts | 14 - .../test/unit-test/switch/_output.java | 9 +- .../template-expression/_output.java | 22 +- .../test/unit-test/text-blocks/_input.java | 8 + .../test/unit-test/text-blocks/_output.java | 17 +- .../test/unit-test/try_catch/_output.java | 2 +- .../_output.java | 9 +- .../test/unit-test/variables/_input.java | 2 + .../test/unit-test/variables/_output.java | 31 +- .../unit-test/yield-statement/_output.java | 1 + packages/prettier-plugin-java/tsconfig.json | 8 +- yarn.lock | 13 +- 81 files changed, 4253 insertions(+), 5943 deletions(-) delete mode 100644 packages/java-parser/docs/comments.md delete mode 100644 packages/java-parser/src/comments.js delete mode 100644 packages/prettier-plugin-java/index.d.ts delete mode 100644 packages/prettier-plugin-java/src/base-cst-printer.ts create mode 100644 packages/prettier-plugin-java/src/comments.ts delete mode 100644 packages/prettier-plugin-java/src/cst-printer.ts delete mode 100644 packages/prettier-plugin-java/src/printers/comments/comments-utils.ts delete mode 100644 packages/prettier-plugin-java/src/printers/comments/format-comments.ts delete mode 100644 packages/prettier-plugin-java/src/printers/comments/handle-comments.ts create mode 100644 packages/prettier-plugin-java/src/printers/helpers.ts create mode 100644 packages/prettier-plugin-java/src/printers/index.ts delete mode 100644 packages/prettier-plugin-java/src/printers/prettier-builder.ts delete mode 100644 packages/prettier-plugin-java/src/printers/printer-utils.ts delete mode 100644 packages/prettier-plugin-java/src/types/utils.ts delete mode 100644 packages/prettier-plugin-java/src/utils/index.ts delete mode 100644 packages/prettier-plugin-java/src/utils/isEmptyDoc.ts delete mode 100644 packages/prettier-plugin-java/src/utils/printArgumentListWithBraces.ts rename packages/prettier-plugin-java/test/unit-test/binary_expressions/{ => operator-position-end}/_input.java (97%) rename packages/prettier-plugin-java/test/unit-test/binary_expressions/{ => operator-position-end}/_output.java (98%) create mode 100644 packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-start/_input.java create mode 100644 packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-start/_output.java diff --git a/package.json b/package.json index a33baffaf..0286e682a 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "lerna": "8.1.9", "lint-staged": "15.2.10", "mocha": "10.8.2", - "prettier": "3.4.2", + "prettier": "^3.0.0", "sinon": "19.0.2" } } diff --git a/packages/java-parser/api.d.ts b/packages/java-parser/api.d.ts index 0273bfe2c..78feed690 100644 --- a/packages/java-parser/api.d.ts +++ b/packages/java-parser/api.d.ts @@ -1,4 +1,4 @@ -import { +import type { CstNode as ChevrotainCstNode, IToken as ChevrotainIToken, CstNodeLocation, @@ -7,15 +7,10 @@ import { export interface CstNode extends ChevrotainCstNode { children: CstChildrenDictionary; - leadingComments?: IToken[]; - trailingComments?: IToken[]; - ignore?: boolean; - location: CstNodeLocation; + location: Required; } export interface IToken extends ChevrotainIToken { - leadingComments?: IToken[]; - trailingComments?: IToken[]; startOffset: number; startLine: number; startColumn: number; @@ -24,7 +19,246 @@ export interface IToken extends ChevrotainIToken { endColumn: number; } -export type CstElement = IToken | CstNode; +export type CstElement = + | IToken + | AdditionalBoundCstNode + | AmbiguousNameCstNode + | AnnotationCstNode + | AnnotationInterfaceBodyCstNode + | AnnotationInterfaceDeclarationCstNode + | AnnotationInterfaceElementDeclarationCstNode + | AnnotationInterfaceElementModifierCstNode + | AnnotationInterfaceMemberDeclarationCstNode + | ArgumentListCstNode + | ArrayAccessSuffixCstNode + | ArrayCreationExpressionCstNode + | ArrayCreationExpressionWithoutInitializerSuffixCstNode + | ArrayCreationWithInitializerSuffixCstNode + | ArrayInitializerCstNode + | AssertStatementCstNode + | BasicForStatementCstNode + | BinaryExpressionCstNode + | BlockCstNode + | BlockStatementCstNode + | BlockStatementsCstNode + | BooleanLiteralCstNode + | BreakStatementCstNode + | CaseConstantCstNode + | CasePatternCstNode + | CastExpressionCstNode + | CatchClauseCstNode + | CatchesCstNode + | CatchFormalParameterCstNode + | CatchTypeCstNode + | ClassBodyCstNode + | ClassBodyDeclarationCstNode + | ClassDeclarationCstNode + | ClassExtendsCstNode + | ClassImplementsCstNode + | ClassLiteralSuffixCstNode + | ClassMemberDeclarationCstNode + | ClassModifierCstNode + | ClassOrInterfaceTypeCstNode + | ClassOrInterfaceTypeToInstantiateCstNode + | ClassPermitsCstNode + | ClassTypeCstNode + | CompactConstructorDeclarationCstNode + | CompilationUnitCstNode + | ComponentPatternCstNode + | ComponentPatternListCstNode + | ConciseLambdaParameterCstNode + | ConciseLambdaParameterListCstNode + | ConditionalExpressionCstNode + | ConstantDeclarationCstNode + | ConstantModifierCstNode + | ConstructorBodyCstNode + | ConstructorDeclarationCstNode + | ConstructorDeclaratorCstNode + | ConstructorModifierCstNode + | ContinueStatementCstNode + | DefaultValueCstNode + | DiamondCstNode + | DimExprCstNode + | DimExprsCstNode + | DimsCstNode + | DoStatementCstNode + | ElementValueArrayInitializerCstNode + | ElementValueCstNode + | ElementValueListCstNode + | ElementValuePairCstNode + | ElementValuePairListCstNode + | EmbeddedExpressionCstNode + | EmptyStatementCstNode + | EnhancedForStatementCstNode + | EnumBodyCstNode + | EnumBodyDeclarationsCstNode + | EnumConstantCstNode + | EnumConstantListCstNode + | EnumConstantModifierCstNode + | EnumDeclarationCstNode + | ExceptionTypeCstNode + | ExceptionTypeListCstNode + | ExplicitConstructorInvocationCstNode + | ExportsModuleDirectiveCstNode + | ExpressionCstNode + | ExpressionNameCstNode + | ExpressionStatementCstNode + | FieldDeclarationCstNode + | FieldModifierCstNode + | FinallyCstNode + | FloatingPointLiteralCstNode + | FloatingPointTypeCstNode + | ForInitCstNode + | FormalParameterCstNode + | FormalParameterListCstNode + | ForStatementCstNode + | ForUpdateCstNode + | FqnOrRefTypeCstNode + | FqnOrRefTypePartCommonCstNode + | FqnOrRefTypePartFirstCstNode + | FqnOrRefTypePartRestCstNode + | GuardCstNode + | IfStatementCstNode + | ImportDeclarationCstNode + | InstanceInitializerCstNode + | IntegerLiteralCstNode + | IntegralTypeCstNode + | InterfaceBodyCstNode + | InterfaceDeclarationCstNode + | InterfaceExtendsCstNode + | InterfaceMemberDeclarationCstNode + | InterfaceMethodDeclarationCstNode + | InterfaceMethodModifierCstNode + | InterfaceModifierCstNode + | InterfacePermitsCstNode + | InterfaceTypeCstNode + | InterfaceTypeListCstNode + | LabeledStatementCstNode + | LambdaBodyCstNode + | LambdaExpressionCstNode + | LambdaParameterListCstNode + | LambdaParametersCstNode + | LambdaParametersWithBracesCstNode + | LambdaParameterTypeCstNode + | LiteralCstNode + | LocalVariableDeclarationCstNode + | LocalVariableDeclarationStatementCstNode + | LocalVariableTypeCstNode + | MatchAllPatternCstNode + | MethodBodyCstNode + | MethodDeclarationCstNode + | MethodDeclaratorCstNode + | MethodHeaderCstNode + | MethodInvocationSuffixCstNode + | MethodModifierCstNode + | MethodNameCstNode + | MethodReferenceSuffixCstNode + | ModularCompilationUnitCstNode + | ModuleDeclarationCstNode + | ModuleDirectiveCstNode + | ModuleNameCstNode + | NewExpressionCstNode + | NormalClassDeclarationCstNode + | NormalInterfaceDeclarationCstNode + | NormalLambdaParameterCstNode + | NormalLambdaParameterListCstNode + | NumericTypeCstNode + | OpensModuleDirectiveCstNode + | OrdinaryCompilationUnitCstNode + | PackageDeclarationCstNode + | PackageModifierCstNode + | PackageNameCstNode + | PackageOrTypeNameCstNode + | ParenthesisExpressionCstNode + | PatternCstNode + | PrimaryCstNode + | PrimaryPrefixCstNode + | PrimarySuffixCstNode + | PrimitiveCastExpressionCstNode + | PrimitiveTypeCstNode + | ProvidesModuleDirectiveCstNode + | QualifiedExplicitConstructorInvocationCstNode + | ReceiverParameterCstNode + | RecordBodyCstNode + | RecordBodyDeclarationCstNode + | RecordComponentCstNode + | RecordComponentListCstNode + | RecordComponentModifierCstNode + | RecordDeclarationCstNode + | RecordHeaderCstNode + | RecordPatternCstNode + | ReferenceTypeCastExpressionCstNode + | ReferenceTypeCstNode + | RegularLambdaParameterCstNode + | RequiresModifierCstNode + | RequiresModuleDirectiveCstNode + | ResourceCstNode + | ResourceListCstNode + | ResourceSpecificationCstNode + | ResultCstNode + | ReturnStatementCstNode + | ShiftOperatorCstNode + | SimpleTypeNameCstNode + | StatementCstNode + | StatementExpressionCstNode + | StatementExpressionListCstNode + | StatementWithoutTrailingSubstatementCstNode + | StaticInitializerCstNode + | StringTemplateCstNode + | SwitchBlockCstNode + | SwitchBlockStatementGroupCstNode + | SwitchLabelCstNode + | SwitchRuleCstNode + | SwitchStatementCstNode + | SynchronizedStatementCstNode + | TemplateArgumentCstNode + | TemplateCstNode + | TextBlockTemplateCstNode + | ThrowsCstNode + | ThrowStatementCstNode + | TryStatementCstNode + | TryWithResourcesStatementCstNode + | TypeArgumentCstNode + | TypeArgumentListCstNode + | TypeArgumentsCstNode + | TypeArgumentsOrDiamondCstNode + | TypeBoundCstNode + | TypeDeclarationCstNode + | TypeIdentifierCstNode + | TypeNameCstNode + | TypeParameterCstNode + | TypeParameterListCstNode + | TypeParameterModifierCstNode + | TypeParametersCstNode + | TypePatternCstNode + | TypeVariableCstNode + | UnannClassOrInterfaceTypeCstNode + | UnannClassTypeCstNode + | UnannInterfaceTypeCstNode + | UnannPrimitiveTypeCstNode + | UnannPrimitiveTypeWithOptionalDimsSuffixCstNode + | UnannReferenceTypeCstNode + | UnannTypeCstNode + | UnannTypeVariableCstNode + | UnaryExpressionCstNode + | UnaryExpressionNotPlusMinusCstNode + | UnqualifiedClassInstanceCreationExpressionCstNode + | UnqualifiedExplicitConstructorInvocationCstNode + | UsesModuleDirectiveCstNode + | VariableAccessCstNode + | VariableArityParameterCstNode + | VariableArityRecordComponentCstNode + | VariableDeclaratorCstNode + | VariableDeclaratorIdCstNode + | VariableDeclaratorListCstNode + | VariableInitializerCstNode + | VariableInitializerListCstNode + | VariableModifierCstNode + | VariableParaRegularParameterCstNode + | WhileStatementCstNode + | WildcardBoundsCstNode + | WildcardCstNode + | YieldStatementCstNode; export declare type CstChildrenDictionary = { [identifier: string]: CstElement[]; @@ -38,7 +272,10 @@ export function lexAndParse( cst: CstNode; }; -export function parse(text: string, startProduction?: string): CstNode; +export function parse( + text: string, + startProduction?: string +): CstNode & { comments?: IToken[] }; export const BaseJavaCstVisitor: JavaCstVisitorConstructor; export const BaseJavaCstVisitorWithDefaults: JavaCstVisitorWithDefaultsConstructor< @@ -57,6 +294,7 @@ export abstract class JavaCstVisitor implements ICstVisitor { integerLiteral(ctx: IntegerLiteralCtx, param?: IN): OUT; floatingPointLiteral(ctx: FloatingPointLiteralCtx, param?: IN): OUT; booleanLiteral(ctx: BooleanLiteralCtx, param?: IN): OUT; + shiftOperator(cts: ShiftOperatorCtx, param?: IN): OUT; primitiveType(ctx: PrimitiveTypeCtx, param?: IN): OUT; numericType(ctx: NumericTypeCtx, param?: IN): OUT; integralType(ctx: IntegralTypeCtx, param?: IN): OUT; @@ -300,7 +538,7 @@ export abstract class JavaCstVisitor implements ICstVisitor { param?: IN ): OUT; normalLambdaParameterList(ctx: NormalLambdaParameterListCtx, param?: IN): OUT; - normalLambdaParameter(ctx: LambdaParameterCtx, param?: IN): OUT; + normalLambdaParameter(ctx: NormalLambdaParameterCtx, param?: IN): OUT; regularLambdaParameter(ctx: RegularLambdaParameterCtx, param?: IN): OUT; lambdaParameterType(ctx: LambdaParameterTypeCtx, param?: IN): OUT; conciseLambdaParameter(ctx: ConciseLambdaParameterCtx, param?: IN): OUT; @@ -385,6 +623,7 @@ export abstract class JavaCstVisitorWithDefaults integerLiteral(ctx: IntegerLiteralCtx, param?: IN): OUT; floatingPointLiteral(ctx: FloatingPointLiteralCtx, param?: IN): OUT; booleanLiteral(ctx: BooleanLiteralCtx, param?: IN): OUT; + shiftOperator(cts: ShiftOperatorCtx, param?: IN): OUT; primitiveType(ctx: PrimitiveTypeCtx, param?: IN): OUT; numericType(ctx: NumericTypeCtx, param?: IN): OUT; integralType(ctx: IntegralTypeCtx, param?: IN): OUT; @@ -628,7 +867,7 @@ export abstract class JavaCstVisitorWithDefaults param?: IN ): OUT; normalLambdaParameterList(ctx: NormalLambdaParameterListCtx, param?: IN): OUT; - normalLambdaParameter(ctx: LambdaParameterCtx, param?: IN): OUT; + normalLambdaParameter(ctx: NormalLambdaParameterCtx, param?: IN): OUT; regularLambdaParameter(ctx: RegularLambdaParameterCtx, param?: IN): OUT; lambdaParameterType(ctx: LambdaParameterTypeCtx, param?: IN): OUT; conciseLambdaParameter(ctx: ConciseLambdaParameterCtx, param?: IN): OUT; @@ -755,6 +994,16 @@ export type BooleanLiteralCtx = { False?: IToken[]; }; +export interface ShiftOperatorCstNode extends CstNode { + name: "shiftOperator"; + children: ShiftOperatorCtx; +} + +export type ShiftOperatorCtx = { + Less?: IToken[]; + Greater?: IToken[]; +}; + export interface PrimitiveTypeCstNode extends CstNode { name: "primitiveType"; children: PrimitiveTypeCtx; @@ -1801,17 +2050,16 @@ export interface CompilationUnitCstNode extends CstNode { export type AbstractOrdinaryCompilationUnitCtx = { ordinaryCompilationUnit: OrdinaryCompilationUnitCstNode[]; - EOF: IToken[]; }; export type AbstractModularCompilationUnitCtx = { modularCompilationUnit: OrdinaryCompilationUnitCstNode[]; - EOF: IToken[]; }; export type CompilationUnitCtx = | AbstractOrdinaryCompilationUnitCtx - | AbstractModularCompilationUnitCtx; + | AbstractModularCompilationUnitCtx + | { EOF: IToken[] }; export interface OrdinaryCompilationUnitCstNode extends CstNode { name: "ordinaryCompilationUnit"; @@ -2894,10 +3142,10 @@ export type NormalLambdaParameterListCtx = { export interface NormalLambdaParameterCstNode extends CstNode { name: "normalLambdaParameter"; - children: LambdaParameterCtx; + children: NormalLambdaParameterCtx; } -export type LambdaParameterCtx = { +export type NormalLambdaParameterCtx = { regularLambdaParameter?: RegularLambdaParameterCstNode[]; variableArityParameter?: VariableArityParameterCstNode[]; }; @@ -2967,8 +3215,7 @@ export type BinaryExpressionCtx = { referenceType?: ReferenceTypeCstNode[]; AssignmentOperator?: IToken[]; expression?: ExpressionCstNode[]; - Less?: IToken[]; - Greater?: IToken[]; + shiftOperator?: ShiftOperatorCstNode[]; BinaryOperator?: IToken[]; }; diff --git a/packages/java-parser/docs/comments.md b/packages/java-parser/docs/comments.md deleted file mode 100644 index e468ada01..000000000 --- a/packages/java-parser/docs/comments.md +++ /dev/null @@ -1,58 +0,0 @@ -# Handling comments - -## Objective - -Attach each comment to a CST node or token as either leading or trailing comment. - -### Attach comment to the highest node possible - -A comment could often be attached at several nodes or token. For instance, in the following code snippet: - -```java -// comment -public void t() {} -``` - -`// comment` could be attached to: - -- the `methodDeclaration` node -- the `methodModifier` node -- the `public` token - -We have made the decision to attach the comment to the highest node possible (the most enclosive one). It makes the more sense to us, and it ease handling comments in the prettier-plugin. - -### Trailing comments - -A comment should be considered as a trailing comment in very specific cases: - -- If it is on the same line as the node/token it followed, and not on the same line as the node/token it preceded. For instance, `//comment` is considered as a trailing comment in the following code snippet: - - ```java - int i = 1; // comment - int j = 2; - ``` - -- If it is at the end of the file - -### Consecutives comments - -If there are consecutive comments, each comment will be considered separately. For instance: - -```java -int i = 1; -// comment1 -// comment2 -int j = 2; -``` - -`// comment1` and `// comment2` will be both attached as leading comments to the second `localVariableDeclarationStatement` node. - -When in: - -```java -int i = 1; // comment1 -// comment2 -int j = 2; -``` - -`// comment1` will be attached as a trailing comment to the first `localVariableDeclarationStatement` node and `// comment2` will be attached as a leading comment to the second one. diff --git a/packages/java-parser/package.json b/packages/java-parser/package.json index ebe112a66..50309beff 100644 --- a/packages/java-parser/package.json +++ b/packages/java-parser/package.json @@ -14,7 +14,9 @@ "types": "./api.d.ts", "dependencies": { "chevrotain": "11.0.3", - "chevrotain-allstar": "0.3.1", + "chevrotain-allstar": "0.3.1" + }, + "devDependencies": { "lodash": "4.17.21" }, "scripts": { diff --git a/packages/java-parser/src/comments.js b/packages/java-parser/src/comments.js deleted file mode 100644 index 635073b70..000000000 --- a/packages/java-parser/src/comments.js +++ /dev/null @@ -1,308 +0,0 @@ -import findLast from "lodash/findLast.js"; - -/** - * Search where is the position of the comment in the token array by - * using dichotomic search. - * @param {*} tokens ordered array of tokens - * @param {*} comment comment token - * @return the position of the token next to the comment - */ -function findUpperBoundToken(tokens, comment) { - let diff; - let i; - let current; - - let len = tokens.length; - i = 0; - - while (len) { - diff = len >>> 1; - current = i + diff; - if (tokens[current].startOffset > comment.startOffset) { - len = diff; - } else { - i = current + 1; - len -= diff + 1; - } - } - return i; -} - -function isPrettierIgnoreComment(comment) { - return comment.image.match( - /(\/\/(\s*)prettier-ignore(\s*))|(\/\*(\s*)prettier-ignore(\s*)\*\/)/gm - ); -} - -function isFormatterOffOnComment(comment) { - return comment.image.match( - /(\/\/(\s*)@formatter:(off|on)(\s*))|(\/\*(\s*)@formatter:(off|on)(\s*)\*\/)/gm - ); -} - -/** - * Pre-processing of tokens in order to - * complete the parser's mostEnclosiveCstNodeByStartOffset and mostEnclosiveCstNodeByEndOffset structures. - * - * @param {ITokens[]} tokens - array of tokens - * @param {{[startOffset: number]: CSTNode}} mostEnclosiveCstNodeByStartOffset - * @param {{[endOffset: number]: CSTNode}} mostEnclosiveCstNodeByEndOffset - */ -function completeMostEnclosiveCSTNodeByOffset( - tokens, - mostEnclosiveCstNodeByStartOffset, - mostEnclosiveCstNodeByEndOffset -) { - tokens.forEach(token => { - if (mostEnclosiveCstNodeByStartOffset[token.startOffset] === undefined) { - mostEnclosiveCstNodeByStartOffset[token.startOffset] = token; - } - - if (mostEnclosiveCstNodeByEndOffset[token.endOffset] === undefined) { - mostEnclosiveCstNodeByEndOffset[token.endOffset] = token; - } - }); -} - -function extendRangeOffset(comments, tokens) { - let position; - comments.forEach(comment => { - position = findUpperBoundToken(tokens, comment); - - const extendedStartOffset = - position - 1 < 0 ? comment.startOffset : tokens[position - 1].endOffset; - const extendedEndOffset = - position == tokens.length - ? comment.endOffset - : tokens[position].startOffset; - comment.extendedOffset = { - startOffset: extendedStartOffset, - endOffset: extendedEndOffset - }; - }); -} - -/** - * Create two data structures we use to know at which offset a comment can be attached. - * - commentsByExtendedStartOffset: map a comment by the endOffset of the previous token. - * - commentsByExtendedEndOffset: map a comment by the startOffset of the next token - * - * @param {ITokens[]} tokens - array of tokens - * - * @return {{commentsByExtendedStartOffset: {[extendedStartOffset: number]: Comment[]}, commentsByExtendedEndOffset: {[extendedEndOffset: number]: Comment[]}}} - */ -function mapCommentsByExtendedRange(comments) { - const commentsByExtendedEndOffset = {}; - const commentsByExtendedStartOffset = {}; - - comments.forEach(comment => { - const extendedStartOffset = comment.extendedOffset.startOffset; - const extendedEndOffset = comment.extendedOffset.endOffset; - - if (commentsByExtendedEndOffset[extendedEndOffset] === undefined) { - commentsByExtendedEndOffset[extendedEndOffset] = [comment]; - } else { - commentsByExtendedEndOffset[extendedEndOffset].push(comment); - } - - if (commentsByExtendedStartOffset[extendedStartOffset] === undefined) { - commentsByExtendedStartOffset[extendedStartOffset] = [comment]; - } else { - commentsByExtendedStartOffset[extendedStartOffset].push(comment); - } - }); - - return { commentsByExtendedEndOffset, commentsByExtendedStartOffset }; -} - -/** - * Determine if a comment should be attached as a trailing comment to a specific node. - * A comment should be trailing if it is on the same line than the previous token and - * not on the same line than the next token - * - * @param {*} comment - * @param {CSTNode} node - * @param {{[startOffset: number]: CSTNode}} mostEnclosiveCstNodeByStartOffset - */ -function shouldAttachTrailingComments( - comment, - node, - mostEnclosiveCstNodeByStartOffset -) { - if (isPrettierIgnoreComment(comment)) { - return false; - } - - const nextNode = - mostEnclosiveCstNodeByStartOffset[comment.extendedOffset.endOffset]; - - // Last node of the file - if (nextNode === undefined) { - return true; - } - - const nodeEndLine = - node.location !== undefined ? node.location.endLine : node.endLine; - - if (comment.startLine !== nodeEndLine) { - return false; - } - - const nextNodeStartLine = - nextNode.location !== undefined - ? nextNode.location.startLine - : nextNode.startLine; - return comment.endLine !== nextNodeStartLine; -} - -/** - * Attach comments to the most enclosive CSTNode (node or token) - * - * @param {ITokens[]} tokens - * @param {*} comments - * @param {{[startOffset: number]: CSTNode}} mostEnclosiveCstNodeByStartOffset - * @param {{[endOffset: number]: CSTNode}} mostEnclosiveCstNodeByEndOffset - */ -export function attachComments( - tokens, - comments, - mostEnclosiveCstNodeByStartOffset, - mostEnclosiveCstNodeByEndOffset -) { - // Edge case: only comments in the file - if (tokens.length === 0) { - mostEnclosiveCstNodeByStartOffset[NaN].leadingComments = comments; - return; - } - - // Pre-processing phase to complete the data structures we need to attach - // a comment to the right place - completeMostEnclosiveCSTNodeByOffset( - tokens, - mostEnclosiveCstNodeByStartOffset, - mostEnclosiveCstNodeByEndOffset - ); - - extendRangeOffset(comments, tokens); - const { commentsByExtendedStartOffset, commentsByExtendedEndOffset } = - mapCommentsByExtendedRange(comments); - - /* - This set is here to ensure that we attach comments only once - If a comment is attached to a node or token, we remove it from this set - */ - const commentsToAttach = new Set(comments); - - // Attach comments as trailing comments if desirable - Object.keys(mostEnclosiveCstNodeByEndOffset).forEach(endOffset => { - // We look if some comments is directly following this node/token - if (commentsByExtendedStartOffset[endOffset] !== undefined) { - const nodeTrailingComments = commentsByExtendedStartOffset[ - endOffset - ].filter(comment => { - return ( - shouldAttachTrailingComments( - comment, - mostEnclosiveCstNodeByEndOffset[endOffset], - mostEnclosiveCstNodeByStartOffset - ) && commentsToAttach.has(comment) - ); - }); - - if (nodeTrailingComments.length > 0) { - mostEnclosiveCstNodeByEndOffset[endOffset].trailingComments = - nodeTrailingComments; - } - - nodeTrailingComments.forEach(comment => { - commentsToAttach.delete(comment); - }); - } - }); - - // Attach rest of comments as leading comments - Object.keys(mostEnclosiveCstNodeByStartOffset).forEach(startOffset => { - // We look if some comments is directly preceding this node/token - if (commentsByExtendedEndOffset[startOffset] !== undefined) { - const nodeLeadingComments = commentsByExtendedEndOffset[ - startOffset - ].filter(comment => commentsToAttach.has(comment)); - - if (nodeLeadingComments.length > 0) { - mostEnclosiveCstNodeByStartOffset[startOffset].leadingComments = - nodeLeadingComments; - } - - // prettier ignore support - for (let i = 0; i < nodeLeadingComments.length; i++) { - if (isPrettierIgnoreComment(nodeLeadingComments[i])) { - const node = mostEnclosiveCstNodeByStartOffset[startOffset]; - const ignoreNode = - node.name === "blockStatements" - ? node.children.blockStatement[0] - : node; - ignoreNode.ignore = true; - break; - } - } - } - }); -} - -/** - * Create pairs of formatter:off and formatter:on - * @param comments - * @returns pairs of formatter:off and formatter:on - */ -export function matchFormatterOffOnPairs(comments) { - const onOffComments = comments.filter(comment => - isFormatterOffOnComment(comment) - ); - - let isPreviousCommentOff = false; - let isCurrentCommentOff = true; - const pairs = []; - let paired = {}; - onOffComments.forEach(comment => { - isCurrentCommentOff = comment.image.slice(-3) === "off"; - - if (!isPreviousCommentOff) { - if (isCurrentCommentOff) { - paired.off = comment; - } - } else { - if (!isCurrentCommentOff) { - paired.on = comment; - pairs.push(paired); - paired = {}; - } - } - isPreviousCommentOff = isCurrentCommentOff; - }); - - if (onOffComments.length > 0 && isCurrentCommentOff) { - paired.on = undefined; - pairs.push(paired); - } - - return pairs; -} - -/** - * Check if the node is between formatter:off and formatter:on and change his ignore state - * @param node - * @param commentPairs - */ -export function shouldNotFormat(node, commentPairs) { - const matchingPair = findLast( - commentPairs, - comment => comment.off.endOffset < node.location.startOffset - ); - if ( - matchingPair !== undefined && - (matchingPair.on === undefined || - matchingPair.on.startOffset > node.location.endOffset) - ) { - node.ignore = true; - } -} diff --git a/packages/java-parser/src/index.js b/packages/java-parser/src/index.js index 3a8bf587a..df372dd76 100644 --- a/packages/java-parser/src/index.js +++ b/packages/java-parser/src/index.js @@ -1,6 +1,5 @@ import JavaLexer from "./lexer.js"; import JavaParser from "./parser.js"; -import { attachComments, matchFormatterOffOnPairs } from "./comments.js"; const parser = new JavaParser(); @@ -26,12 +25,6 @@ export function lexAndParse(inputText, entryPoint = "compilationUnit") { const tokens = lexResult.tokens; parser.input = tokens; - parser.mostEnclosiveCstNodeByStartOffset = {}; - parser.mostEnclosiveCstNodeByEndOffset = {}; - - parser.setOnOffCommentPairs( - matchFormatterOffOnPairs(lexResult.groups.comments) - ); // Automatic CST created when parsing const cst = parser[entryPoint](); @@ -50,12 +43,7 @@ export function lexAndParse(inputText, entryPoint = "compilationUnit") { ); } - attachComments( - tokens, - lexResult.groups.comments, - parser.mostEnclosiveCstNodeByStartOffset, - parser.mostEnclosiveCstNodeByEndOffset - ); + cst.comments = lexResult.groups.comments; return { cst, tokens }; } diff --git a/packages/java-parser/src/parser.js b/packages/java-parser/src/parser.js index f2157776c..5f3f3d64b 100644 --- a/packages/java-parser/src/parser.js +++ b/packages/java-parser/src/parser.js @@ -11,7 +11,6 @@ import * as arrays from "./productions/arrays.js"; import * as blocksStatements from "./productions/blocks-and-statements.js"; import * as expressions from "./productions/expressions.js"; import { getSkipValidations } from "./utils.js"; -import { shouldNotFormat } from "./comments.js"; /** * This parser attempts to strongly align with the specs style at: @@ -49,9 +48,6 @@ export default class JavaParser extends CstParser { const $ = this; - this.mostEnclosiveCstNodeByStartOffset = {}; - this.mostEnclosiveCstNodeByEndOffset = {}; - // --------------------- // Productions from ยง3 (Lexical Structure) // --------------------- @@ -83,12 +79,6 @@ export default class JavaParser extends CstParser { return; } super.cstPostNonTerminal(ruleCstResult, ruleName); - this.mostEnclosiveCstNodeByStartOffset[ruleCstResult.location.startOffset] = - ruleCstResult; - this.mostEnclosiveCstNodeByEndOffset[ruleCstResult.location.endOffset] = - ruleCstResult; - - shouldNotFormat(ruleCstResult, this.onOffCommentPairs); } BACKTRACK_LOOKAHEAD(production, errValue = false) { @@ -122,8 +112,4 @@ export default class JavaParser extends CstParser { } }); } - - setOnOffCommentPairs(onOffCommentPairs) { - this.onOffCommentPairs = onOffCommentPairs; - } } diff --git a/packages/java-parser/src/productions/expressions.js b/packages/java-parser/src/productions/expressions.js index 46d6980a6..0ee8fac5d 100644 --- a/packages/java-parser/src/productions/expressions.js +++ b/packages/java-parser/src/productions/expressions.js @@ -145,7 +145,6 @@ export function defineRules($, t) { }, // This is an example of why Java does not have a well designed grammar // See: https://manas.tech/blog/2008/10/12/why-java-generics-dont-have-problems-with-right-shift-operator.html - // TODO: ensure the LT/GT sequences have no whitespace between each other. { // TODO: this is a bug in Chevrotain lookahead calculation. the "BinaryOperator" token can match "Less" or "Greater" // as well, but because it is a **token Category** Chevrotain does not understand it need to looks two tokens ahead. @@ -153,27 +152,7 @@ export function defineRules($, t) { tokenMatcher($.LA(2).tokenType, t.Less) || tokenMatcher($.LA(2).tokenType, t.Greater), ALT: () => { - $.OR2([ - { - GATE: () => $.LA(1).startOffset + 1 === $.LA(2).startOffset, - ALT: () => { - $.CONSUME(t.Less); - $.CONSUME2(t.Less); - } - }, - { - GATE: () => $.LA(1).startOffset + 1 === $.LA(2).startOffset, - ALT: () => { - $.CONSUME(t.Greater); - $.CONSUME2(t.Greater); - $.OPTION({ - GATE: () => - $.LA(0).startOffset + 1 === $.LA(1).startOffset, - DEF: () => $.CONSUME3(t.Greater) - }); - } - } - ]); + $.SUBRULE($.shiftOperator); $.SUBRULE2($.unaryExpression); } }, diff --git a/packages/java-parser/src/productions/lexical-structure.js b/packages/java-parser/src/productions/lexical-structure.js index 6cdb6bda3..9b1bb21d8 100644 --- a/packages/java-parser/src/productions/lexical-structure.js +++ b/packages/java-parser/src/productions/lexical-structure.js @@ -34,4 +34,28 @@ export function defineRules($, t) { $.RULE("booleanLiteral", () => { $.OR([{ ALT: () => $.CONSUME(t.True) }, { ALT: () => $.CONSUME(t.False) }]); }); + + // https://docs.oracle.com/javase/specs/jls/se22/html/jls-3.html#jls-3.12 + $.RULE("shiftOperator", () => { + $.OR([ + { + GATE: () => $.LA(1).startOffset + 1 === $.LA(2).startOffset, + ALT: () => { + $.CONSUME(t.Less); + $.CONSUME2(t.Less); + } + }, + { + GATE: () => $.LA(1).startOffset + 1 === $.LA(2).startOffset, + ALT: () => { + $.CONSUME(t.Greater); + $.CONSUME2(t.Greater); + $.OPTION({ + GATE: () => $.LA(0).startOffset + 1 === $.LA(1).startOffset, + DEF: () => $.CONSUME3(t.Greater) + }); + } + } + ]); + }); } diff --git a/packages/java-parser/src/productions/packages-and-modules.js b/packages/java-parser/src/productions/packages-and-modules.js index f5733737a..ffddaba1e 100644 --- a/packages/java-parser/src/productions/packages-and-modules.js +++ b/packages/java-parser/src/productions/packages-and-modules.js @@ -12,10 +12,9 @@ export function defineRules($, t) { $.RULE("compilationUnit", () => { $.OR([ { ALT: () => $.SUBRULE($.ordinaryCompilationUnit) }, - { ALT: () => $.SUBRULE($.modularCompilationUnit) } + { ALT: () => $.SUBRULE($.modularCompilationUnit) }, + { ALT: () => $.CONSUME(EOF) } ]); - // https://github.com/jhipster/prettier-java/pull/217 - $.CONSUME(EOF); }); // https://docs.oracle.com/javase/specs/jls/se22/html/jls-7.html#jls-OrdinaryCompilationUnit diff --git a/packages/prettier-plugin-java/.gitignore b/packages/prettier-plugin-java/.gitignore index 20b1e5fa8..9e589ec97 100644 --- a/packages/prettier-plugin-java/.gitignore +++ b/packages/prettier-plugin-java/.gitignore @@ -1,3 +1,4 @@ +dist/ test-samples/**/* scripts/single-printer-run samples/**/* diff --git a/packages/prettier-plugin-java/index.d.ts b/packages/prettier-plugin-java/index.d.ts deleted file mode 100644 index 462b5f144..000000000 --- a/packages/prettier-plugin-java/index.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -import type { Plugin } from "prettier"; - -declare const plugin: Plugin; -export default plugin; diff --git a/packages/prettier-plugin-java/package.json b/packages/prettier-plugin-java/package.json index c29b05d96..7f011a03f 100644 --- a/packages/prettier-plugin-java/package.json +++ b/packages/prettier-plugin-java/package.json @@ -4,19 +4,17 @@ "description": "Prettier Java Plugin", "type": "module", "exports": { - "types": "./index.d.ts", + "types": "./dist/index.d.ts", "default": "./dist/index.js" }, "files": [ - "dist", - "index.d.ts" + "dist" ], "homepage": "https://jhipster.github.io/prettier-java/", "repository": "https://github.com/jhipster/prettier-java", "license": "Apache-2.0", "dependencies": { - "java-parser": "2.3.4", - "lodash": "4.17.21" + "java-parser": "2.3.4" }, "scripts": { "test": "yarn run test:unit && yarn run test:e2e-core", @@ -35,7 +33,6 @@ "@types/fs-extra": "11.0.4", "@types/jest": "29.5.14", "@types/klaw-sync": "6.0.5", - "@types/lodash": "4.17.13", "@types/node": "18.19.64", "@types/sinon": "17.0.3", "ts-node": "10.9.2", diff --git a/packages/prettier-plugin-java/src/base-cst-printer.ts b/packages/prettier-plugin-java/src/base-cst-printer.ts deleted file mode 100644 index d27f8fdf3..000000000 --- a/packages/prettier-plugin-java/src/base-cst-printer.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { - BaseJavaCstVisitor, - CstChildrenDictionary, - CstElement, - CstNode -} from "java-parser"; -import { Doc } from "prettier"; -import { printNodeWithComments } from "./printers/comments/format-comments.js"; - -export class BaseCstPrettierPrinter extends BaseJavaCstVisitor { - prettierOptions: any; - - mapVisit = (elements: CstNode[] | undefined, params?: any) => { - if (elements === undefined) { - // TODO: can optimize this by returning an immutable empty array singleton. - return []; - } - - return elements.map(element => this.visit(element, params)); - }; - - getSingle = (ctx: CstChildrenDictionary): CstElement => { - const ctxKeys = Object.keys(ctx); - if (ctxKeys.length !== 1) { - throw Error( - `Expecting single key CST ctx but found: <${ctxKeys.length}> keys` - ); - } - const singleElementKey = ctxKeys[0]; - const singleElementValues = ctx[singleElementKey]; - - if (singleElementValues?.length !== 1) { - throw Error( - `Expecting single item in CST ctx key but found: <${singleElementValues?.length}> items` - ); - } - - return singleElementValues[0]; - }; - - // @ts-ignore - orgVisit = this.visit; - visit = function (ctx: CstNode | CstNode[] | undefined, inParam?: any): Doc { - if (ctx === undefined) { - // empty Doc - return ""; - } - - const node = Array.isArray(ctx) ? ctx[0] : ctx; - - if (node.ignore) { - try { - const startOffset = - node.leadingComments !== undefined - ? node.leadingComments[0].startOffset - : node.location.startOffset; - const endOffset = ( - node.trailingComments !== undefined - ? node.trailingComments[node.trailingComments.length - 1].endOffset - : node.location.endOffset - ) as number; - - return this.prettierOptions.originalText.substring( - startOffset, - endOffset + 1 - ); - } catch (e) { - throw Error( - e + - "\nThere might be a problem with prettier-ignore, please report an issue on https://github.com/jhipster/prettier-java/issues" - ); - } - } - - return printNodeWithComments(node, this.orgVisit.call(this, node, inParam)); - }; - - visitSingle = function (ctx: CstChildrenDictionary, params?: any) { - const singleElement = this.getSingle(ctx); - return this.visit(singleElement, params); - }; - - constructor() { - super(); - } -} diff --git a/packages/prettier-plugin-java/src/comments.ts b/packages/prettier-plugin-java/src/comments.ts new file mode 100644 index 000000000..121ac0f51 --- /dev/null +++ b/packages/prettier-plugin-java/src/comments.ts @@ -0,0 +1,272 @@ +import type { IToken } from "java-parser"; +import { util, type AstPath } from "prettier"; +import parser from "./parser.js"; +import { + isEmptyStatement, + isNonTerminal, + isTerminal, + type JavaNode, + type JavaNonTerminal, + type JavaParserOptions +} from "./printers/helpers.js"; + +const formatterOffOnRangesByCst = new WeakMap< + JavaNode, + FormatterOffOnRange[] +>(); + +export function determineFormatterOffOnRanges(cst: JavaNonTerminal) { + const { comments } = cst; + if (!comments) { + return; + } + const ranges = comments + .filter(({ image }) => + /^(\/\/\s*@formatter:(off|on)\s*|\/\*\s*@formatter:(off|on)\s*\*\/)$/.test( + image + ) + ) + .reduce((ranges, { image, startOffset }) => { + const previous = ranges.at(-1); + if (image.endsWith("off")) { + if (previous?.on !== Infinity) { + ranges.push({ off: startOffset, on: Infinity }); + } + } else if (previous?.on === Infinity) { + previous.on = startOffset; + } + return ranges; + }, new Array()); + formatterOffOnRangesByCst.set(cst, ranges); +} + +export function isFullyBetweenFormatterOffOn(path: AstPath) { + const { node, root } = path; + const start = parser.locStart(node); + const end = parser.locEnd(node); + return ( + formatterOffOnRangesByCst + .get(root) + ?.some(range => range.off < start && end < range.on) === true + ); +} + +export function canAttachComment(node: JavaNode) { + if (isTerminal(node)) { + const { name, CATEGORIES } = node.tokenType; + return ( + name === "Identifier" || + CATEGORIES?.find(({ name }) => name === "BinaryOperator") !== undefined + ); + } + const { children, name } = node; + switch (name) { + case "argumentList": + case "blockStatements": + case "emptyStatement": + case "enumBodyDeclarations": + return false; + case "annotationInterfaceMemberDeclaration": + case "classMemberDeclaration": + case "interfaceMemberDeclaration": + case "methodBody": + return !children.Semicolon; + case "blockStatement": + return !children.statement || !isEmptyStatement(children.statement[0]); + case "classBodyDeclaration": + return !children.classMemberDeclaration?.[0].children.Semicolon; + case "recordBodyDeclaration": + return !children.classBodyDeclaration?.[0].children + .classMemberDeclaration?.[0].children.Semicolon; + case "statement": + return !isEmptyStatement(node); + case "statementWithoutTrailingSubstatement": + return !children.emptyStatement; + default: + return true; + } +} + +export function handleLineComment( + commentNode: JavaComment, + _: string, + options: JavaParserOptions +) { + return [ + handleBinaryExpressionComments, + handleFqnOrRefTypeComments, + handleIfStatementComments, + handleJumpStatementComments, + handleLabeledStatementComments, + handleNameComments + ].some(fn => fn(commentNode, options)); +} + +export function handleRemainingComment(commentNode: JavaComment) { + return [ + handleFqnOrRefTypeComments, + handleMethodDeclaratorComments, + handleNameComments, + handleJumpStatementComments + ].some(fn => fn(commentNode)); +} + +function handleBinaryExpressionComments( + commentNode: JavaComment, + options: JavaParserOptions +) { + const { enclosingNode, precedingNode, followingNode } = commentNode; + if ( + enclosingNode && + isNonTerminal(enclosingNode) && + enclosingNode.name === "binaryExpression" + ) { + if (isBinaryOperator(followingNode)) { + if (options.experimentalOperatorPosition === "start") { + util.addLeadingComment(followingNode, commentNode); + } else { + util.addTrailingComment(followingNode, commentNode); + } + return true; + } else if ( + options.experimentalOperatorPosition === "start" && + isBinaryOperator(precedingNode) + ) { + util.addLeadingComment(precedingNode, commentNode); + return true; + } + } + return false; +} + +function handleFqnOrRefTypeComments(commentNode: JavaComment) { + const { enclosingNode, followingNode } = commentNode; + if ( + enclosingNode && + isNonTerminal(enclosingNode) && + enclosingNode.name === "fqnOrRefType" && + followingNode + ) { + util.addLeadingComment(followingNode, commentNode); + return true; + } + return false; +} + +function handleIfStatementComments(commentNode: JavaComment) { + const { enclosingNode, precedingNode } = commentNode; + if ( + enclosingNode && + isNonTerminal(enclosingNode) && + enclosingNode.name === "ifStatement" && + precedingNode && + isNonTerminal(precedingNode) && + precedingNode.name === "statement" + ) { + util.addDanglingComment(enclosingNode, commentNode, undefined); + return true; + } + return false; +} + +function handleJumpStatementComments(commentNode: JavaComment) { + const { enclosingNode, precedingNode, followingNode } = commentNode; + if ( + enclosingNode && + !precedingNode && + !followingNode && + isNonTerminal(enclosingNode) && + ["breakStatement", "continueStatement", "returnStatement"].includes( + enclosingNode.name + ) + ) { + util.addTrailingComment(enclosingNode, commentNode); + return true; + } + return false; +} + +function handleLabeledStatementComments(commentNode: JavaComment) { + const { enclosingNode, precedingNode } = commentNode; + if ( + enclosingNode && + precedingNode && + isNonTerminal(enclosingNode) && + enclosingNode.name === "labeledStatement" && + isTerminal(precedingNode) && + precedingNode.tokenType.name === "Identifier" + ) { + util.addLeadingComment(precedingNode, commentNode); + return true; + } + return false; +} + +function handleMethodDeclaratorComments(commentNode: JavaComment) { + const { enclosingNode } = commentNode; + if ( + enclosingNode && + isNonTerminal(enclosingNode) && + enclosingNode.name === "methodDeclarator" && + !enclosingNode.children.receiverParameter && + !enclosingNode.children.formalParameterList && + enclosingNode.children.LBrace[0].startOffset < commentNode.startOffset && + commentNode.startOffset < enclosingNode.children.RBrace[0].startOffset + ) { + util.addDanglingComment(enclosingNode, commentNode, undefined); + return true; + } + return false; +} + +function handleNameComments(commentNode: JavaComment) { + const { enclosingNode, precedingNode } = commentNode; + if ( + enclosingNode && + precedingNode && + isNonTerminal(enclosingNode) && + isTerminal(precedingNode) && + precedingNode.tokenType.name === "Identifier" && + [ + "ambiguousName", + "classOrInterfaceTypeToInstantiate", + "expressionName", + "moduleDeclaration", + "moduleName", + "packageDeclaration", + "packageName", + "packageOrTypeName", + "typeName" + ].includes(enclosingNode.name) + ) { + util.addTrailingComment(precedingNode, commentNode); + return true; + } + return false; +} + +function isBinaryOperator(node?: JavaNode) { + return ( + node !== undefined && + (isNonTerminal(node) + ? node.name === "shiftOperator" + : node.tokenType.CATEGORIES?.some( + ({ name }) => name === "BinaryOperator" + )) + ); +} + +export type JavaComment = IToken & { + value: string; + leading: boolean; + trailing: boolean; + printed: boolean; + enclosingNode?: JavaNonTerminal; + precedingNode?: JavaNonTerminal; + followingNode?: JavaNonTerminal; +}; + +type FormatterOffOnRange = { + off: number; + on: number; +}; diff --git a/packages/prettier-plugin-java/src/cst-printer.ts b/packages/prettier-plugin-java/src/cst-printer.ts deleted file mode 100644 index 03572574f..000000000 --- a/packages/prettier-plugin-java/src/cst-printer.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { BaseCstPrettierPrinter } from "./base-cst-printer.js"; -import { ArraysPrettierVisitor } from "./printers/arrays.js"; -import { BlocksAndStatementPrettierVisitor } from "./printers/blocks-and-statements.js"; -import { ClassesPrettierVisitor } from "./printers/classes.js"; -import { ExpressionsPrettierVisitor } from "./printers/expressions.js"; -import { InterfacesPrettierVisitor } from "./printers/interfaces.js"; -import { LexicalStructurePrettierVisitor } from "./printers/lexical-structure.js"; -import { NamesPrettierVisitor } from "./printers/names.js"; -import { TypesValuesAndVariablesPrettierVisitor } from "./printers/types-values-and-variables.js"; -import { PackagesAndModulesPrettierVisitor } from "./printers/packages-and-modules.js"; - -// Mixins for the win -mixInMethods( - ArraysPrettierVisitor, - BlocksAndStatementPrettierVisitor, - ClassesPrettierVisitor, - ExpressionsPrettierVisitor, - InterfacesPrettierVisitor, - LexicalStructurePrettierVisitor, - NamesPrettierVisitor, - TypesValuesAndVariablesPrettierVisitor, - PackagesAndModulesPrettierVisitor -); - -function mixInMethods(...classesToMix: any[]) { - classesToMix.forEach(from => { - const fromMethodsNames = Object.getOwnPropertyNames(from.prototype); - const fromPureMethodsName = fromMethodsNames.filter( - methodName => methodName !== "constructor" - ); - fromPureMethodsName.forEach(methodName => { - // @ts-ignore - BaseCstPrettierPrinter.prototype[methodName] = from.prototype[methodName]; - }); - }); -} - -const prettyPrinter = new BaseCstPrettierPrinter(); - -// TODO: do we need the "path" and "print" arguments passed by prettier -// see https://github.com/prettier/prettier/issues/5747 -export function createPrettierDoc(cstNode: any, options: any) { - prettyPrinter.prettierOptions = options; - return prettyPrinter.visit(cstNode); -} diff --git a/packages/prettier-plugin-java/src/index.ts b/packages/prettier-plugin-java/src/index.ts index 6fedf7863..dca268bb7 100644 --- a/packages/prettier-plugin-java/src/index.ts +++ b/packages/prettier-plugin-java/src/index.ts @@ -1,77 +1,32 @@ -import parse from "./parser.js"; -import print from "./printer.js"; +import type { Plugin } from "prettier"; import options from "./options.js"; - -const languages = [ - { - name: "Java", - parsers: ["java"], - group: "Java", - tmScope: "text.html.vue", // FIXME - aceMode: "html", // FIXME - codemirrorMode: "clike", - codemirrorMimeType: "text/x-java", - extensions: [".java"], - linguistLanguageId: 181, - vscodeLanguageIds: ["java"] - } -]; - -function locStart(/* node */) { - return -1; -} - -function locEnd(/* node */) { - return -1; -} - -function hasPragma(text) { - return /^\/\*\*[\n][\t\s]+\*\s@(prettier|format)[\n][\t\s]+\*\//.test(text); -} - -const parsers = { - java: { - parse, - astFormat: "java", - locStart, - locEnd, - hasPragma - } -}; - -function canAttachComment(node) { - return node.ast_type && node.ast_type !== "comment"; -} - -function printComment(commentPath) { - const comment = commentPath.getValue(); - - switch (comment.ast_type) { - case "comment": - return comment.value; - default: - throw new Error("Not a comment: " + JSON.stringify(comment)); - } -} - -function clean(ast, newObj) { - delete newObj.lineno; - delete newObj.col_offset; -} - -const printers = { - java: { - print, - // hasPrettierIgnore, - printComment, - canAttachComment, - massageAstNode: clean - } -}; +import parser from "./parser.js"; +import printer from "./printer.js"; +import type { JavaNode } from "./printers/helpers.js"; export default { - languages, - printers, - parsers, - options -}; + languages: [ + { + name: "Java", + parsers: ["java"], + group: "Java", + tmScope: "source.java", + aceMode: "java", + codemirrorMode: "clike", + codemirrorMimeType: "text/x-java", + extensions: [".java"], + linguistLanguageId: 181, + vscodeLanguageIds: ["java"] + } + ], + parsers: { + java: parser + }, + printers: { + java: printer + }, + options, + defaultOptions: { + arrowParens: "avoid" + } +} satisfies Plugin; diff --git a/packages/prettier-plugin-java/src/options.ts b/packages/prettier-plugin-java/src/options.ts index 67595f7a6..e93a05943 100644 --- a/packages/prettier-plugin-java/src/options.ts +++ b/packages/prettier-plugin-java/src/options.ts @@ -1,3 +1,5 @@ +import type { SupportOptions } from "prettier"; + export default { entrypoint: { type: "choice", @@ -5,244 +7,248 @@ export default { default: "compilationUnit", // sed -nr 's/.*\.RULE\(([^,]+),.*/\1/p' $(ls path/to/java-parser/rules/folder/*) choices: [ - { value: "arrayInitializer" }, - { value: "variableInitializerList" }, - { value: "block" }, - { value: "blockStatements" }, - { value: "blockStatement" }, - { value: "localVariableDeclarationStatement" }, - { value: "localVariableDeclaration" }, - { value: "localVariableType" }, - { value: "statement" }, - { value: "statementWithoutTrailingSubstatement" }, - { value: "emptyStatement" }, - { value: "labeledStatement" }, - { value: "expressionStatement" }, - { value: "statementExpression" }, - { value: "ifStatement" }, - { value: "assertStatement" }, - { value: "switchStatement" }, - { value: "switchBlock" }, - { value: "switchBlockStatementGroup" }, - { value: "switchLabel" }, - { value: "switchRule" }, - { value: "caseConstant" }, - { value: "casePattern" }, - { value: "whileStatement" }, - { value: "doStatement" }, - { value: "forStatement" }, - { value: "basicForStatement" }, - { value: "forInit" }, - { value: "forUpdate" }, - { value: "statementExpressionList" }, - { value: "enhancedForStatement" }, - { value: "breakStatement" }, - { value: "continueStatement" }, - { value: "returnStatement" }, - { value: "throwStatement" }, - { value: "synchronizedStatement" }, - { value: "tryStatement" }, - { value: "catches" }, - { value: "catchClause" }, - { value: "catchFormalParameter" }, - { value: "catchType" }, - { value: "finally" }, - { value: "tryWithResourcesStatement" }, - { value: "resourceSpecification" }, - { value: "resourceList" }, - { value: "resource" }, - { value: "yieldStatement" }, - { value: "variableAccess" }, - { value: "classDeclaration" }, - { value: "normalClassDeclaration" }, - { value: "classModifier" }, - { value: "typeParameters" }, - { value: "typeParameterList" }, - { value: "classExtends" }, - { value: "classImplements" }, - { value: "interfaceTypeList" }, - { value: "classPermits" }, - { value: "classBody" }, - { value: "classBodyDeclaration" }, - { value: "classMemberDeclaration" }, - { value: "fieldDeclaration" }, - { value: "fieldModifier" }, - { value: "variableDeclaratorList" }, - { value: "variableDeclarator" }, - { value: "variableDeclaratorId" }, - { value: "variableInitializer" }, - { value: "unannType" }, - { value: "unannPrimitiveTypeWithOptionalDimsSuffix" }, - { value: "unannPrimitiveType" }, - { value: "unannReferenceType" }, - { value: "unannClassOrInterfaceType" }, - { value: "unannClassType" }, - { value: "unannInterfaceType" }, - { value: "unannTypeVariable" }, - { value: "methodDeclaration" }, - { value: "methodModifier" }, - { value: "methodHeader" }, - { value: "result" }, - { value: "methodDeclarator" }, - { value: "receiverParameter" }, - { value: "formalParameterList" }, - { value: "formalParameter" }, - { value: "variableParaRegularParameter" }, - { value: "variableArityParameter" }, - { value: "variableModifier" }, - { value: "throws" }, - { value: "exceptionTypeList" }, - { value: "exceptionType" }, - { value: "methodBody" }, - { value: "instanceInitializer" }, - { value: "staticInitializer" }, - { value: "constructorDeclaration" }, - { value: "constructorModifier" }, - { value: "constructorDeclarator" }, - { value: "simpleTypeName" }, - { value: "constructorBody" }, - { value: "explicitConstructorInvocation" }, - { value: "unqualifiedExplicitConstructorInvocation" }, - { value: "qualifiedExplicitConstructorInvocation" }, - { value: "enumDeclaration" }, - { value: "enumBody" }, - { value: "enumConstantList" }, - { value: "enumConstant" }, - { value: "enumConstantModifier" }, - { value: "enumBodyDeclarations" }, - { value: "recordDeclaration" }, - { value: "recordHeader" }, - { value: "recordComponentList" }, - { value: "recordComponent" }, - { value: "variableArityRecordComponent" }, - { value: "recordComponentModifier" }, - { value: "recordBody" }, - { value: "recordBodyDeclaration" }, - { value: "compactConstructorDeclaration" }, - { value: "isDims" }, - { value: "expression" }, - { value: "lambdaExpression" }, - { value: "lambdaParameters" }, - { value: "lambdaParametersWithBraces" }, - { value: "lambdaParameterList" }, - { value: "conciseLambdaParameterList" }, - { value: "normalLambdaParameterList" }, - { value: "normalLambdaParameter" }, - { value: "regularLambdaParameter" }, - { value: "lambdaParameterType" }, - { value: "conciseLambdaParameter" }, - { value: "lambdaBody" }, - { value: "conditionalExpression" }, - { value: "binaryExpression" }, - { value: "unaryExpression" }, - { value: "unaryExpressionNotPlusMinus" }, - { value: "primary" }, - { value: "primaryPrefix" }, - { value: "primarySuffix" }, - { value: "fqnOrRefType" }, - { value: "fqnOrRefTypePartRest" }, - { value: "fqnOrRefTypePartCommon" }, - { value: "fqnOrRefTypePartFirst" }, - { value: "parenthesisExpression" }, - { value: "castExpression" }, - { value: "primitiveCastExpression" }, - { value: "referenceTypeCastExpression" }, - { value: "newExpression" }, - { value: "unqualifiedClassInstanceCreationExpression" }, - { value: "classOrInterfaceTypeToInstantiate" }, - { value: "typeArgumentsOrDiamond" }, - { value: "diamond" }, - { value: "methodInvocationSuffix" }, - { value: "argumentList" }, - { value: "arrayCreationExpression" }, - { value: "arrayCreationExpressionWithoutInitializerSuffix" }, - { value: "arrayCreationWithInitializerSuffix" }, - { value: "dimExprs" }, - { value: "dimExpr" }, - { value: "classLiteralSuffix" }, - { value: "arrayAccessSuffix" }, - { value: "methodReferenceSuffix" }, - { value: "templateArgument" }, - { value: "template" }, - { value: "stringTemplate" }, - { value: "textBlockTemplate" }, - { value: "embeddedExpression" }, - { value: "pattern" }, - { value: "typePattern" }, - { value: "recordPattern" }, - { value: "componentPatternList" }, - { value: "componentPattern" }, - { value: "matchAllPattern" }, - { value: "guard" }, - { value: "isRefTypeInMethodRef" }, - { value: "interfaceDeclaration" }, - { value: "normalInterfaceDeclaration" }, - { value: "interfaceModifier" }, - { value: "interfaceExtends" }, - { value: "interfacePermits" }, - { value: "interfaceBody" }, - { value: "interfaceMemberDeclaration" }, - { value: "constantDeclaration" }, - { value: "constantModifier" }, - { value: "interfaceMethodDeclaration" }, - { value: "interfaceMethodModifier" }, - { value: "annotationInterfaceDeclaration" }, - { value: "annotationInterfaceBody" }, - { value: "annotationInterfaceMemberDeclaration" }, - { value: "annotationInterfaceElementDeclaration" }, - { value: "annotationInterfaceElementModifier" }, - { value: "defaultValue" }, - { value: "annotation" }, - { value: "elementValuePairList" }, - { value: "elementValuePair" }, - { value: "elementValue" }, - { value: "elementValueArrayInitializer" }, - { value: "elementValueList" }, - { value: "literal" }, - { value: "integerLiteral" }, - { value: "floatingPointLiteral" }, - { value: "booleanLiteral" }, - { value: "moduleName" }, - { value: "packageName" }, - { value: "typeName" }, - { value: "expressionName" }, - { value: "methodName" }, - { value: "packageOrTypeName" }, - { value: "ambiguousName" }, - { value: "compilationUnit" }, - { value: "ordinaryCompilationUnit" }, - { value: "modularCompilationUnit" }, - { value: "packageDeclaration" }, - { value: "packageModifier" }, - { value: "importDeclaration" }, - { value: "typeDeclaration" }, - { value: "moduleDeclaration" }, - { value: "moduleDirective" }, - { value: "requiresModuleDirective" }, - { value: "exportsModuleDirective" }, - { value: "opensModuleDirective" }, - { value: "usesModuleDirective" }, - { value: "providesModuleDirective" }, - { value: "requiresModifier" }, - { value: "primitiveType" }, - { value: "numericType" }, - { value: "integralType" }, - { value: "floatingPointType" }, - { value: "referenceType" }, - { value: "classOrInterfaceType" }, - { value: "classType" }, - { value: "interfaceType" }, - { value: "typeVariable" }, - { value: "dims" }, - { value: "typeParameter" }, - { value: "typeParameterModifier" }, - { value: "typeBound" }, - { value: "additionalBound" }, - { value: "typeArguments" }, - { value: "typeArgumentList" }, - { value: "typeArgument" }, - { value: "wildcard" }, - { value: "wildcardBounds" } + { value: "arrayInitializer", description: "" }, + { value: "variableInitializerList", description: "" }, + { value: "block", description: "" }, + { value: "blockStatements", description: "" }, + { value: "blockStatement", description: "" }, + { value: "localVariableDeclarationStatement", description: "" }, + { value: "localVariableDeclaration", description: "" }, + { value: "localVariableType", description: "" }, + { value: "statement", description: "" }, + { value: "statementWithoutTrailingSubstatement", description: "" }, + { value: "emptyStatement", description: "" }, + { value: "labeledStatement", description: "" }, + { value: "expressionStatement", description: "" }, + { value: "statementExpression", description: "" }, + { value: "ifStatement", description: "" }, + { value: "assertStatement", description: "" }, + { value: "switchStatement", description: "" }, + { value: "switchBlock", description: "" }, + { value: "switchBlockStatementGroup", description: "" }, + { value: "switchLabel", description: "" }, + { value: "switchRule", description: "" }, + { value: "caseConstant", description: "" }, + { value: "casePattern", description: "" }, + { value: "whileStatement", description: "" }, + { value: "doStatement", description: "" }, + { value: "forStatement", description: "" }, + { value: "basicForStatement", description: "" }, + { value: "forInit", description: "" }, + { value: "forUpdate", description: "" }, + { value: "statementExpressionList", description: "" }, + { value: "enhancedForStatement", description: "" }, + { value: "breakStatement", description: "" }, + { value: "continueStatement", description: "" }, + { value: "returnStatement", description: "" }, + { value: "throwStatement", description: "" }, + { value: "synchronizedStatement", description: "" }, + { value: "tryStatement", description: "" }, + { value: "catches", description: "" }, + { value: "catchClause", description: "" }, + { value: "catchFormalParameter", description: "" }, + { value: "catchType", description: "" }, + { value: "finally", description: "" }, + { value: "tryWithResourcesStatement", description: "" }, + { value: "resourceSpecification", description: "" }, + { value: "resourceList", description: "" }, + { value: "resource", description: "" }, + { value: "yieldStatement", description: "" }, + { value: "variableAccess", description: "" }, + { value: "classDeclaration", description: "" }, + { value: "normalClassDeclaration", description: "" }, + { value: "classModifier", description: "" }, + { value: "typeParameters", description: "" }, + { value: "typeParameterList", description: "" }, + { value: "classExtends", description: "" }, + { value: "classImplements", description: "" }, + { value: "interfaceTypeList", description: "" }, + { value: "classPermits", description: "" }, + { value: "classBody", description: "" }, + { value: "classBodyDeclaration", description: "" }, + { value: "classMemberDeclaration", description: "" }, + { value: "fieldDeclaration", description: "" }, + { value: "fieldModifier", description: "" }, + { value: "variableDeclaratorList", description: "" }, + { value: "variableDeclarator", description: "" }, + { value: "variableDeclaratorId", description: "" }, + { value: "variableInitializer", description: "" }, + { value: "unannType", description: "" }, + { value: "unannPrimitiveTypeWithOptionalDimsSuffix", description: "" }, + { value: "unannPrimitiveType", description: "" }, + { value: "unannReferenceType", description: "" }, + { value: "unannClassOrInterfaceType", description: "" }, + { value: "unannClassType", description: "" }, + { value: "unannInterfaceType", description: "" }, + { value: "unannTypeVariable", description: "" }, + { value: "methodDeclaration", description: "" }, + { value: "methodModifier", description: "" }, + { value: "methodHeader", description: "" }, + { value: "result", description: "" }, + { value: "methodDeclarator", description: "" }, + { value: "receiverParameter", description: "" }, + { value: "formalParameterList", description: "" }, + { value: "formalParameter", description: "" }, + { value: "variableParaRegularParameter", description: "" }, + { value: "variableArityParameter", description: "" }, + { value: "variableModifier", description: "" }, + { value: "throws", description: "" }, + { value: "exceptionTypeList", description: "" }, + { value: "exceptionType", description: "" }, + { value: "methodBody", description: "" }, + { value: "instanceInitializer", description: "" }, + { value: "staticInitializer", description: "" }, + { value: "constructorDeclaration", description: "" }, + { value: "constructorModifier", description: "" }, + { value: "constructorDeclarator", description: "" }, + { value: "simpleTypeName", description: "" }, + { value: "constructorBody", description: "" }, + { value: "explicitConstructorInvocation", description: "" }, + { value: "unqualifiedExplicitConstructorInvocation", description: "" }, + { value: "qualifiedExplicitConstructorInvocation", description: "" }, + { value: "enumDeclaration", description: "" }, + { value: "enumBody", description: "" }, + { value: "enumConstantList", description: "" }, + { value: "enumConstant", description: "" }, + { value: "enumConstantModifier", description: "" }, + { value: "enumBodyDeclarations", description: "" }, + { value: "recordDeclaration", description: "" }, + { value: "recordHeader", description: "" }, + { value: "recordComponentList", description: "" }, + { value: "recordComponent", description: "" }, + { value: "variableArityRecordComponent", description: "" }, + { value: "recordComponentModifier", description: "" }, + { value: "recordBody", description: "" }, + { value: "recordBodyDeclaration", description: "" }, + { value: "compactConstructorDeclaration", description: "" }, + { value: "isDims", description: "" }, + { value: "expression", description: "" }, + { value: "lambdaExpression", description: "" }, + { value: "lambdaParameters", description: "" }, + { value: "lambdaParametersWithBraces", description: "" }, + { value: "lambdaParameterList", description: "" }, + { value: "conciseLambdaParameterList", description: "" }, + { value: "normalLambdaParameterList", description: "" }, + { value: "normalLambdaParameter", description: "" }, + { value: "regularLambdaParameter", description: "" }, + { value: "lambdaParameterType", description: "" }, + { value: "conciseLambdaParameter", description: "" }, + { value: "lambdaBody", description: "" }, + { value: "conditionalExpression", description: "" }, + { value: "binaryExpression", description: "" }, + { value: "unaryExpression", description: "" }, + { value: "unaryExpressionNotPlusMinus", description: "" }, + { value: "primary", description: "" }, + { value: "primaryPrefix", description: "" }, + { value: "primarySuffix", description: "" }, + { value: "fqnOrRefType", description: "" }, + { value: "fqnOrRefTypePartRest", description: "" }, + { value: "fqnOrRefTypePartCommon", description: "" }, + { value: "fqnOrRefTypePartFirst", description: "" }, + { value: "parenthesisExpression", description: "" }, + { value: "castExpression", description: "" }, + { value: "primitiveCastExpression", description: "" }, + { value: "referenceTypeCastExpression", description: "" }, + { value: "newExpression", description: "" }, + { value: "unqualifiedClassInstanceCreationExpression", description: "" }, + { value: "classOrInterfaceTypeToInstantiate", description: "" }, + { value: "typeArgumentsOrDiamond", description: "" }, + { value: "diamond", description: "" }, + { value: "methodInvocationSuffix", description: "" }, + { value: "argumentList", description: "" }, + { value: "arrayCreationExpression", description: "" }, + { + value: "arrayCreationExpressionWithoutInitializerSuffix", + description: "" + }, + { value: "arrayCreationWithInitializerSuffix", description: "" }, + { value: "dimExprs", description: "" }, + { value: "dimExpr", description: "" }, + { value: "classLiteralSuffix", description: "" }, + { value: "arrayAccessSuffix", description: "" }, + { value: "methodReferenceSuffix", description: "" }, + { value: "templateArgument", description: "" }, + { value: "template", description: "" }, + { value: "stringTemplate", description: "" }, + { value: "textBlockTemplate", description: "" }, + { value: "embeddedExpression", description: "" }, + { value: "pattern", description: "" }, + { value: "typePattern", description: "" }, + { value: "recordPattern", description: "" }, + { value: "componentPatternList", description: "" }, + { value: "componentPattern", description: "" }, + { value: "matchAllPattern", description: "" }, + { value: "guard", description: "" }, + { value: "isRefTypeInMethodRef", description: "" }, + { value: "interfaceDeclaration", description: "" }, + { value: "normalInterfaceDeclaration", description: "" }, + { value: "interfaceModifier", description: "" }, + { value: "interfaceExtends", description: "" }, + { value: "interfacePermits", description: "" }, + { value: "interfaceBody", description: "" }, + { value: "interfaceMemberDeclaration", description: "" }, + { value: "constantDeclaration", description: "" }, + { value: "constantModifier", description: "" }, + { value: "interfaceMethodDeclaration", description: "" }, + { value: "interfaceMethodModifier", description: "" }, + { value: "annotationInterfaceDeclaration", description: "" }, + { value: "annotationInterfaceBody", description: "" }, + { value: "annotationInterfaceMemberDeclaration", description: "" }, + { value: "annotationInterfaceElementDeclaration", description: "" }, + { value: "annotationInterfaceElementModifier", description: "" }, + { value: "defaultValue", description: "" }, + { value: "annotation", description: "" }, + { value: "elementValuePairList", description: "" }, + { value: "elementValuePair", description: "" }, + { value: "elementValue", description: "" }, + { value: "elementValueArrayInitializer", description: "" }, + { value: "elementValueList", description: "" }, + { value: "literal", description: "" }, + { value: "integerLiteral", description: "" }, + { value: "floatingPointLiteral", description: "" }, + { value: "booleanLiteral", description: "" }, + { value: "shiftOperator", description: "" }, + { value: "moduleName", description: "" }, + { value: "packageName", description: "" }, + { value: "typeName", description: "" }, + { value: "expressionName", description: "" }, + { value: "methodName", description: "" }, + { value: "packageOrTypeName", description: "" }, + { value: "ambiguousName", description: "" }, + { value: "compilationUnit", description: "" }, + { value: "ordinaryCompilationUnit", description: "" }, + { value: "modularCompilationUnit", description: "" }, + { value: "packageDeclaration", description: "" }, + { value: "packageModifier", description: "" }, + { value: "importDeclaration", description: "" }, + { value: "typeDeclaration", description: "" }, + { value: "moduleDeclaration", description: "" }, + { value: "moduleDirective", description: "" }, + { value: "requiresModuleDirective", description: "" }, + { value: "exportsModuleDirective", description: "" }, + { value: "opensModuleDirective", description: "" }, + { value: "usesModuleDirective", description: "" }, + { value: "providesModuleDirective", description: "" }, + { value: "requiresModifier", description: "" }, + { value: "primitiveType", description: "" }, + { value: "numericType", description: "" }, + { value: "integralType", description: "" }, + { value: "floatingPointType", description: "" }, + { value: "referenceType", description: "" }, + { value: "classOrInterfaceType", description: "" }, + { value: "classType", description: "" }, + { value: "interfaceType", description: "" }, + { value: "typeVariable", description: "" }, + { value: "dims", description: "" }, + { value: "typeParameter", description: "" }, + { value: "typeParameterModifier", description: "" }, + { value: "typeBound", description: "" }, + { value: "additionalBound", description: "" }, + { value: "typeArguments", description: "" }, + { value: "typeArgumentList", description: "" }, + { value: "typeArgument", description: "" }, + { value: "wildcard", description: "" }, + { value: "wildcardBounds", description: "" } ], description: "Prettify from the entrypoint, allowing to use prettier on snippet." @@ -251,7 +257,11 @@ export default { type: "choice", category: "Java", default: "all", - choices: ["all", "es5", "none"], + choices: [ + { value: "all", description: "" }, + { value: "es5", description: "" }, + { value: "none", description: "" } + ], description: "Print trailing commas wherever possible when multi-line." } -}; +} satisfies SupportOptions; diff --git a/packages/prettier-plugin-java/src/parser.ts b/packages/prettier-plugin-java/src/parser.ts index fa5221662..b0fe26dde 100644 --- a/packages/prettier-plugin-java/src/parser.ts +++ b/packages/prettier-plugin-java/src/parser.ts @@ -1,5 +1,30 @@ -import javaParser from "java-parser"; +import { parse } from "java-parser"; +import type { Parser } from "prettier"; +import { determineFormatterOffOnRanges } from "./comments.js"; +import { + isTerminal, + type JavaNode, + type JavaNonTerminal, + type JavaParserOptions +} from "./printers/helpers.js"; -export default function parse(text, parsers, opts) { - return javaParser.parse(text, opts.entrypoint); -} +export default { + parse(text, options: JavaParserOptions) { + const cst = parse(text, options.entrypoint) as JavaNonTerminal; + cst.comments?.forEach(comment => { + comment.value = comment.image; + }); + determineFormatterOffOnRanges(cst); + return cst; + }, + astFormat: "java", + hasPragma(text) { + return /^\/\*\*\n\s+\*\s@(format|prettier)\n\s+\*\//.test(text); + }, + locStart(node) { + return isTerminal(node) ? node.startOffset : node.location.startOffset; + }, + locEnd(node) { + return (isTerminal(node) ? node.endOffset : node.location.endOffset) + 1; + } +} satisfies Parser; diff --git a/packages/prettier-plugin-java/src/printer.ts b/packages/prettier-plugin-java/src/printer.ts index 3110198f8..24198ab4c 100644 --- a/packages/prettier-plugin-java/src/printer.ts +++ b/packages/prettier-plugin-java/src/printer.ts @@ -1,7 +1,59 @@ -import { createPrettierDoc } from "./cst-printer.js"; +import type { AstPath, Printer } from "prettier"; +import { + canAttachComment, + handleLineComment, + handleRemainingComment, + isFullyBetweenFormatterOffOn +} from "./comments.js"; +import { + isNonTerminal, + isTerminal, + printComment, + type JavaNode, + type JavaTerminal +} from "./printers/helpers.js"; +import { printerForNodeType } from "./printers/index.js"; -// eslint-disable-next-line no-unused-vars -export default function genericPrint(path, options, print) { - const node = path.getValue(); - return createPrettierDoc(node, options); +export default { + print(path: DistributedAstPath, options, print, args) { + return hasTerminal(path) + ? path.node.image + : printerForNodeType(path.node.name)(path, print, options, args); + }, + hasPrettierIgnore(path) { + const { node } = path; + return ( + node.comments?.some(({ image }) => + /^(\/\/\s*prettier-ignore|\/\*\s*prettier-ignore\s*\*\/)$/.test(image) + ) === true || + (canAttachComment(node) && isFullyBetweenFormatterOffOn(path)) + ); + }, + canAttachComment, + isBlockComment(node) { + return isTerminal(node) && node.tokenType.name === "TraditionalComment"; + }, + printComment(commentPath) { + const { node } = commentPath; + if (isNonTerminal(node) || node.tokenType.GROUP !== "comments") { + throw new Error(`Not a comment: ${JSON.stringify(node)}`); + } + return printComment(node); + }, + getCommentChildNodes(node) { + return isNonTerminal(node) + ? Object.values(node.children).flatMap(child => child) + : []; + }, + handleComments: { + ownLine: handleLineComment, + endOfLine: handleLineComment, + remaining: handleRemainingComment + } +} satisfies Printer; + +function hasTerminal(path: AstPath): path is AstPath { + return isTerminal(path.node); } + +type DistributedAstPath = T extends any ? AstPath : never; diff --git a/packages/prettier-plugin-java/src/printers/arrays.ts b/packages/prettier-plugin-java/src/printers/arrays.ts index 0e0b2a0c4..d2ce4c509 100644 --- a/packages/prettier-plugin-java/src/printers/arrays.ts +++ b/packages/prettier-plugin-java/src/printers/arrays.ts @@ -1,41 +1,20 @@ import { - ArrayInitializerCtx, - VariableInitializerListCtx -} from "java-parser/api"; -import { - printArrayList, - rejectAndConcat, - rejectAndJoinSeps -} from "./printer-utils.js"; -import { builders } from "prettier/doc"; -import { BaseCstPrettierPrinter } from "../base-cst-printer.js"; - -const { line } = builders; + printArrayInitializer, + printList, + type JavaNodePrinters +} from "./helpers.js"; -export class ArraysPrettierVisitor extends BaseCstPrettierPrinter { - prettierOptions: any; - arrayInitializer(ctx: ArrayInitializerCtx) { - const optionalVariableInitializerList = this.visit( - ctx.variableInitializerList +export default { + arrayInitializer(path, print, options) { + return printArrayInitializer( + path, + print, + options, + "variableInitializerList" ); + }, - return printArrayList({ - list: optionalVariableInitializerList, - extraComma: ctx.Comma, - LCurly: ctx.LCurly[0], - RCurly: ctx.RCurly[0], - trailingComma: this.prettierOptions.trailingComma - }); - } - - variableInitializerList(ctx: VariableInitializerListCtx) { - const variableInitializers = this.mapVisit(ctx.variableInitializer); - const commas = ctx.Comma - ? ctx.Comma.map(comma => { - return rejectAndConcat([comma, line]); - }) - : []; - - return rejectAndJoinSeps(commas, variableInitializers); + variableInitializerList(path, print) { + return printList(path, print, "variableInitializer"); } -} +} satisfies Partial; diff --git a/packages/prettier-plugin-java/src/printers/blocks-and-statements.ts b/packages/prettier-plugin-java/src/printers/blocks-and-statements.ts index 023e1d095..aada5339c 100644 --- a/packages/prettier-plugin-java/src/printers/blocks-and-statements.ts +++ b/packages/prettier-plugin-java/src/printers/blocks-and-statements.ts @@ -1,618 +1,416 @@ +import type { Doc } from "prettier"; import { builders } from "prettier/doc"; -import { concat, dedent, group, indent, join } from "./prettier-builder.js"; -import { printTokenWithComments } from "./comments/format-comments.js"; import { - hasLeadingLineComments, - hasTrailingLineComments -} from "./comments/comments-utils.js"; -import { - displaySemicolon, - getBlankLinesSeparator, - isStatementEmptyStatement, - putIntoBraces, - rejectAndConcat, - rejectAndJoin, - rejectAndJoinSeps, - rejectSeparators, - sortModifiers -} from "./printer-utils.js"; -import { BaseCstPrettierPrinter } from "../base-cst-printer.js"; -import { - AssertStatementCtx, - BasicForStatementCtx, - BlockCtx, - BlockStatementCtx, - BlockStatementsCtx, - BreakStatementCtx, - CaseConstantCtx, - CasePatternCtx, - CatchClauseCtx, - CatchesCtx, - CatchFormalParameterCtx, - CatchTypeCtx, - ContinueStatementCtx, - DoStatementCtx, - EmptyStatementCtx, - EnhancedForStatementCtx, - ExpressionStatementCtx, - FinallyCtx, - ForInitCtx, - ForStatementCtx, - ForUpdateCtx, - IfStatementCtx, - IToken, - LabeledStatementCtx, - LocalVariableDeclarationCtx, - LocalVariableDeclarationStatementCtx, - LocalVariableTypeCtx, - ResourceCtx, - ResourceListCtx, - ResourceSpecificationCtx, - ReturnStatementCtx, - StatementCtx, - StatementExpressionCtx, - StatementExpressionListCtx, - StatementWithoutTrailingSubstatementCtx, - SwitchBlockCtx, - SwitchBlockStatementGroupCtx, - SwitchLabelCtx, - SwitchRuleCtx, - SwitchStatementCtx, - SynchronizedStatementCtx, - ThrowStatementCtx, - TryStatementCtx, - TryWithResourcesStatementCtx, - VariableAccessCtx, - WhileStatementCtx, - YieldStatementCtx -} from "java-parser"; -import { Doc } from "prettier"; - -const { line, softline, hardline } = builders; - -export class BlocksAndStatementPrettierVisitor extends BaseCstPrettierPrinter { - block(ctx: BlockCtx) { - const blockStatements = this.visit(ctx.blockStatements); - - return putIntoBraces( - blockStatements, + call, + definedKeys, + indentInParentheses, + isBinaryExpression, + isEmptyStatement, + lineEndWithComments, + lineStartWithComments, + map, + onlyDefinedKey, + printBlock, + printDanglingComments, + printSingle, + printWithModifiers, + type JavaNodePrinters +} from "./helpers.js"; + +const { group, hardline, ifBreak, indent, join, line, softline } = builders; + +export default { + block(path, print) { + const statements = path.node.children.blockStatements + ? (call(path, print, "blockStatements") as Doc[]) + : []; + return printBlock(path, statements.length ? [statements] : []); + }, + + blockStatements(path, print) { + return join( hardline, - ctx.LCurly[0], - ctx.RCurly[0] + map( + path, + statementPath => { + const { node, previous } = statementPath; + const statement = print(statementPath); + return previous && + lineStartWithComments(node) > lineEndWithComments(previous) + 1 + ? [hardline, statement] + : statement; + }, + "blockStatement" + ).filter(doc => doc !== "") ); - } + }, - blockStatements(ctx: BlockStatementsCtx) { - const blockStatement = this.mapVisit(ctx.blockStatement); + blockStatement: printSingle, - const separators = rejectSeparators( - getBlankLinesSeparator(ctx.blockStatement), - blockStatement - ); + localVariableDeclarationStatement(path, print) { + return [call(path, print, "localVariableDeclaration"), ";"]; + }, - return rejectAndJoinSeps(separators, blockStatement); - } - - blockStatement(ctx: BlockStatementCtx) { - return this.visitSingle(ctx); - } - - localVariableDeclarationStatement(ctx: LocalVariableDeclarationStatementCtx) { - const localVariableDeclaration = this.visit(ctx.localVariableDeclaration); - return rejectAndConcat([localVariableDeclaration, ctx.Semicolon[0]]); - } - - localVariableDeclaration(ctx: LocalVariableDeclarationCtx) { - const modifiers = sortModifiers(ctx.variableModifier); - const firstAnnotations = this.mapVisit(modifiers[0]); - const finalModifiers = this.mapVisit(modifiers[1]); - - const localVariableType = this.visit(ctx.localVariableType); - const variableDeclaratorList = this.visit(ctx.variableDeclaratorList); - return rejectAndJoin(hardline, [ - rejectAndJoin(hardline, firstAnnotations), - rejectAndJoin(" ", [ - rejectAndJoin(" ", finalModifiers), - localVariableType, - variableDeclaratorList - ]) + localVariableDeclaration(path, print) { + const declaration = join(" ", [ + call(path, print, "localVariableType"), + call(path, print, "variableDeclaratorList") ]); - } - - localVariableType(ctx: LocalVariableTypeCtx) { - if (ctx.unannType) { - return this.visitSingle(ctx); - } - - return printTokenWithComments(this.getSingle(ctx) as IToken); - } - - statement(ctx: StatementCtx, params: any) { - // handling Labeled statements comments - if (ctx.labeledStatement !== undefined) { - const newLabelStatement = { ...ctx.labeledStatement[0] }; - const newColon = { ...ctx.labeledStatement[0].children.Colon[0] }; - const newStatement = { - ...ctx.labeledStatement[0].children.statement[0] - }; - - const labeledStatementLeadingComments = []; - - if (newColon.trailingComments !== undefined) { - labeledStatementLeadingComments.push(...newColon.trailingComments); - delete newColon.trailingComments; - } - - if (newStatement.leadingComments !== undefined) { - labeledStatementLeadingComments.push(...newStatement.leadingComments); - delete newStatement.leadingComments; - } - - if (labeledStatementLeadingComments.length !== 0) { - newLabelStatement.leadingComments = labeledStatementLeadingComments; + return printWithModifiers(path, print, "variableModifier", declaration); + }, + + localVariableType: printSingle, + statement: printSingle, + statementWithoutTrailingSubstatement: printSingle, + + emptyStatement() { + return ""; + }, + + labeledStatement(path, print) { + return [ + call(path, print, "Identifier"), + ": ", + call(path, print, "statement") + ]; + }, + + expressionStatement(path, print) { + return [call(path, print, "statementExpression"), ";"]; + }, + + statementExpression: printSingle, + + ifStatement(path, print) { + const { children } = path.node; + const hasEmptyStatement = isEmptyStatement(children.statement[0]); + const statement: Doc[] = [ + "if ", + indentInParentheses(call(path, print, "expression")), + hasEmptyStatement ? ";" : [" ", call(path, print, "statement", 0)] + ]; + if (children.Else) { + const danglingComments = printDanglingComments(path); + if (danglingComments.length) { + statement.push(hardline, ...danglingComments, hardline); + } else { + const elseHasBlock = + children.statement[0].children + .statementWithoutTrailingSubstatement?.[0].children.block !== + undefined; + statement.push(elseHasBlock ? " " : hardline); } - newLabelStatement.children.Colon[0] = newColon; - newLabelStatement.children.statement[0] = newStatement; - - return this.visit([newLabelStatement]); - } - - return this.visitSingle(ctx, params); - } - - statementWithoutTrailingSubstatement( - ctx: StatementWithoutTrailingSubstatementCtx, - params: any - ) { - return this.visitSingle(ctx, params); - } - - emptyStatement(ctx: EmptyStatementCtx, params: any) { - return displaySemicolon(ctx.Semicolon[0], params); - } - - labeledStatement(ctx: LabeledStatementCtx) { - const identifier = ctx.Identifier[0]; - const statement = this.visit(ctx.statement); - - return concat([identifier, ctx.Colon[0], " ", statement]); - } - - expressionStatement(ctx: ExpressionStatementCtx) { - const statementExpression = this.visit(ctx.statementExpression); - return rejectAndConcat([statementExpression, ctx.Semicolon[0]]); - } - - statementExpression(ctx: StatementExpressionCtx) { - return this.visitSingle(ctx); - } - - ifStatement(ctx: IfStatementCtx) { - const expression = this.visit(ctx.expression); - - const ifStatement = this.visit(ctx.statement[0], { - allowEmptyStatement: true - }); - const ifSeparator = isStatementEmptyStatement(ifStatement) ? "" : " "; - - let elsePart: Doc = ""; - if (ctx.Else !== undefined) { - const elseStatement = this.visit(ctx.statement[1], { - allowEmptyStatement: true - }); - const elseSeparator = isStatementEmptyStatement(elseStatement) ? "" : " "; - - const elseOnSameLine = - hasTrailingLineComments(ctx.statement[0]) || - hasLeadingLineComments(ctx.Else[0]) || - !ctx.statement[0].children.statementWithoutTrailingSubstatement?.[0] - .children.block - ? hardline - : " "; - - elsePart = rejectAndJoin(elseSeparator, [ - concat([elseOnSameLine, ctx.Else[0]]), - elseStatement - ]); + const elseHasEmptyStatement = isEmptyStatement(children.statement[1]); + statement.push( + "else", + elseHasEmptyStatement ? ";" : [" ", call(path, print, "statement", 1)] + ); } - - return rejectAndConcat([ - rejectAndJoin(" ", [ - ctx.If[0], - concat([ - putIntoBraces(expression, softline, ctx.LBrace[0], ctx.RBrace[0]), - ifSeparator - ]) - ]), - ifStatement, - elsePart - ]); - } - - assertStatement(ctx: AssertStatementCtx) { - const expressions = this.mapVisit(ctx.expression); - const colon = ctx.Colon ? ctx.Colon[0] : ":"; - return rejectAndConcat([ - concat([ctx.Assert[0], " "]), - rejectAndJoin(concat([" ", colon, " "]), expressions), - ctx.Semicolon[0] + return statement; + }, + + assertStatement(path, print) { + return ["assert ", ...join([" : "], map(path, print, "expression")), ";"]; + }, + + switchStatement(path, print) { + return join(" ", [ + "switch", + indentInParentheses(call(path, print, "expression")), + call(path, print, "switchBlock") ]); - } + }, - switchStatement(ctx: SwitchStatementCtx) { - const expression = this.visit(ctx.expression); - const switchBlock = this.visit(ctx.switchBlock); - - return rejectAndJoin(" ", [ - ctx.Switch[0], - putIntoBraces(expression, softline, ctx.LBrace[0], ctx.RBrace[0]), - switchBlock + switchBlock(path, print) { + const { children } = path.node; + const caseKeys = definedKeys(children, [ + "switchBlockStatementGroup", + "switchRule" ]); - } - - switchBlock(ctx: SwitchBlockCtx) { - const switchCases = - ctx.switchBlockStatementGroup !== undefined - ? this.mapVisit(ctx.switchBlockStatementGroup) - : this.mapVisit(ctx.switchRule); - - return putIntoBraces( - rejectAndJoin(hardline, switchCases), - hardline, - ctx.LCurly[0], - ctx.RCurly[0] - ); - } - - switchBlockStatementGroup(ctx: SwitchBlockStatementGroupCtx) { - const switchLabel = this.visit(ctx.switchLabel); - const blockStatements = this.visit(ctx.blockStatements); - - const statements = ctx.blockStatements?.[0].children.blockStatement; - const hasSingleStatementBlock = - statements?.length === 1 && + const cases = caseKeys.length === 1 ? map(path, print, caseKeys[0]) : []; + return printBlock(path, cases); + }, + + switchBlockStatementGroup(path, print) { + const { children } = path.node; + const switchLabel = call(path, print, "switchLabel"); + if (!children.blockStatements) { + return [switchLabel, ":"]; + } + const blockStatements = call(path, print, "blockStatements"); + const statements = children.blockStatements[0].children.blockStatement; + const onlyStatementIsBlock = + statements.length === 1 && statements[0].children.statement?.[0].children .statementWithoutTrailingSubstatement?.[0].children.block !== undefined; - - return concat([ + return [ switchLabel, - ctx.Colon[0], - hasSingleStatementBlock - ? concat([" ", blockStatements]) - : blockStatements && indent([hardline, blockStatements]) - ]); - } - - switchLabel(ctx: SwitchLabelCtx) { - const Case = ctx.Case?.[0]; - const commas = ctx.Comma?.map(elt => concat([elt, line])); - if (ctx.caseConstant || ctx.Null) { - const caseConstants = ctx.Null - ? [ctx.Null[0], ctx.Default?.[0]] - : this.mapVisit(ctx.caseConstant); - return group( - indent(join(" ", [Case!, rejectAndJoinSeps(commas, caseConstants)])) - ); - } else if (ctx.casePattern) { - const casePatterns = this.mapVisit(ctx.casePattern); - const guard = this.visit(ctx.guard); - const multiplePatterns = ctx.casePattern.length > 1; - const separator = multiplePatterns ? line : " "; - const contents = join(separator, [ - Case!, - rejectAndJoinSeps(commas, casePatterns) - ]); - return group( - rejectAndJoin(separator, [ - multiplePatterns ? indent(contents) : contents, - guard - ]) - ); + ":", + onlyStatementIsBlock + ? [" ", blockStatements] + : indent([hardline, blockStatements]) + ]; + }, + + switchLabel(path, print) { + const { children } = path.node; + if (!(children.caseConstant ?? children.casePattern ?? children.Null)) { + return "default"; } - return printTokenWithComments(ctx.Default![0]); - } - - switchRule(ctx: SwitchRuleCtx) { - const switchLabel = this.visit(ctx.switchLabel); - - let caseInstruction; - if (ctx.throwStatement !== undefined) { - caseInstruction = this.visit(ctx.throwStatement); - } else if (ctx.block !== undefined) { - caseInstruction = this.visit(ctx.block); + const values: Doc[] = []; + if (children.Null) { + values.push("null"); + if (children.Default) { + values.push("default"); + } } else { - caseInstruction = concat([this.visit(ctx.expression), ctx.Semicolon![0]]); + const valuesKey = onlyDefinedKey(children, [ + "caseConstant", + "casePattern" + ]); + values.push(...map(path, print, valuesKey)); } - - return concat([switchLabel, " ", ctx.Arrow[0], " ", caseInstruction]); - } - - caseConstant(ctx: CaseConstantCtx) { - return this.visitSingle(ctx); - } - - casePattern(ctx: CasePatternCtx) { - return this.visitSingle(ctx); - } - - whileStatement(ctx: WhileStatementCtx) { - const expression = this.visit(ctx.expression); - const statement = this.visit(ctx.statement[0], { - allowEmptyStatement: true - }); - const statementSeparator = isStatementEmptyStatement(statement) ? "" : " "; - - return rejectAndJoin(" ", [ - ctx.While[0], - rejectAndJoin(statementSeparator, [ - putIntoBraces(expression, softline, ctx.LBrace[0], ctx.RBrace[0]), - statement - ]) - ]); - } - - doStatement(ctx: DoStatementCtx) { - const statement = this.visit(ctx.statement[0], { - allowEmptyStatement: true - }); - const statementSeparator = isStatementEmptyStatement(statement) ? "" : " "; - - const expression = this.visit(ctx.expression); - - return rejectAndJoin(" ", [ - rejectAndJoin(statementSeparator, [ctx.Do[0], statement]), - ctx.While[0], - rejectAndConcat([ - putIntoBraces(expression, softline, ctx.LBrace[0], ctx.RBrace[0]), - ctx.Semicolon[0] - ]) - ]); - } - - forStatement(ctx: ForStatementCtx) { - return this.visitSingle(ctx); - } - - basicForStatement(ctx: BasicForStatementCtx) { - const forInit = this.visit(ctx.forInit); - const expression = this.visit(ctx.expression); - const forUpdate = this.visit(ctx.forUpdate); - const statement = this.visit(ctx.statement[0], { - allowEmptyStatement: true - }); - const statementSeparator = isStatementEmptyStatement(statement) ? "" : " "; - - return rejectAndConcat([ - rejectAndJoin(" ", [ - ctx.For[0], - putIntoBraces( - rejectAndConcat([ - forInit, - rejectAndJoin(line, [ctx.Semicolon[0], expression]), - rejectAndJoin(line, [ctx.Semicolon[1], forUpdate]) - ]), - softline, - ctx.LBrace[0], - ctx.RBrace[0] - ) - ]), - statementSeparator, - statement + const hasMultipleValues = values.length > 1; + const label = hasMultipleValues + ? ["case", indent([line, ...join([",", line], values)])] + : ["case ", values[0]]; + return children.guard + ? [ + group([...label, hasMultipleValues ? line : " "]), + call(path, print, "guard") + ] + : group(label); + }, + + switchRule(path, print) { + const { children } = path.node; + const bodyKey = onlyDefinedKey(children, [ + "block", + "expression", + "throwStatement" ]); - } - - forInit(ctx: ForInitCtx) { - return this.visitSingle(ctx); - } - - forUpdate(ctx: ForUpdateCtx) { - return this.visitSingle(ctx); - } - - statementExpressionList(ctx: StatementExpressionListCtx) { - const statementExpressions = this.mapVisit(ctx.statementExpression); - const commas = ctx.Comma - ? ctx.Comma.map(elt => { - return concat([printTokenWithComments(elt), " "]); - }) - : []; - return rejectAndJoinSeps(commas, statementExpressions); - } - - enhancedForStatement(ctx: EnhancedForStatementCtx) { - const localVariableDeclaration = this.visit(ctx.localVariableDeclaration); - const expression = this.visit(ctx.expression); - const statement = this.visit(ctx.statement[0], { - allowEmptyStatement: true - }); - const statementSeparator = isStatementEmptyStatement(statement) ? "" : " "; - - return rejectAndConcat([ - rejectAndJoin(" ", [ctx.For[0], ctx.LBrace[0]]), - localVariableDeclaration, - concat([" ", ctx.Colon[0], " "]), - expression, - concat([ctx.RBrace[0], statementSeparator]), - statement - ]); - } - - breakStatement(ctx: BreakStatementCtx) { - if (ctx.Identifier) { - const identifier = ctx.Identifier[0]; - return rejectAndConcat([ - concat([ctx.Break[0], " "]), - identifier, - ctx.Semicolon[0] - ]); + const parts = [ + call(path, print, "switchLabel"), + " -> ", + call(path, print, bodyKey) + ]; + if (children.Semicolon) { + parts.push(";"); } - - return concat([ctx.Break[0], ctx.Semicolon[0]]); - } - - continueStatement(ctx: ContinueStatementCtx) { - if (ctx.Identifier) { - const identifier = ctx.Identifier[0]; - - return rejectAndConcat([ - concat([ctx.Continue[0], " "]), - identifier, - ctx.Semicolon[0] - ]); + return parts; + }, + + caseConstant: printSingle, + casePattern: printSingle, + + whileStatement(path, print) { + const statement = call(path, print, "statement"); + const hasEmptyStatement = isEmptyStatement(path.node.children.statement[0]); + return [ + "while ", + indentInParentheses(call(path, print, "expression")), + ...[hasEmptyStatement ? ";" : " ", statement] + ]; + }, + + doStatement(path, print) { + const hasEmptyStatement = isEmptyStatement(path.node.children.statement[0]); + return [ + "do", + hasEmptyStatement ? ";" : [" ", call(path, print, "statement")], + " while ", + indentInParentheses(call(path, print, "expression")), + ";" + ]; + }, + + forStatement: printSingle, + + basicForStatement(path, print) { + const { children } = path.node; + const danglingComments = printDanglingComments(path); + if (danglingComments.length) { + danglingComments.push(hardline); } - - return rejectAndConcat([ctx.Continue[0], ctx.Semicolon[0]]); - } - - returnStatement(ctx: ReturnStatementCtx) { - if (ctx.expression) { - const expression = this.visit(ctx.expression, { - addParenthesisToWrapStatement: true - }); - - return rejectAndConcat([ - concat([ctx.Return[0], " "]), - expression, - ctx.Semicolon[0] - ]); + const expressions = (["forInit", "expression", "forUpdate"] as const).map( + expressionKey => + expressionKey in children ? call(path, print, expressionKey) : "" + ); + const hasEmptyStatement = isEmptyStatement(children.statement[0]); + return [ + ...danglingComments, + "for ", + expressions.some(expression => expression !== "") + ? indentInParentheses(join([";", line], expressions)) + : "(;;)", + hasEmptyStatement ? ";" : [" ", call(path, print, "statement")] + ]; + }, + + forInit: printSingle, + forUpdate: printSingle, + + statementExpressionList(path, print) { + return group( + map(path, print, "statementExpression").map((expression, index) => + index === 0 ? expression : [",", indent([line, expression])] + ) + ); + }, + + enhancedForStatement(path, print) { + const statementNode = path.node.children.statement[0]; + const forStatement = [ + printDanglingComments(path), + "for ", + "(", + call(path, print, "localVariableDeclaration"), + " : ", + call(path, print, "expression"), + ")" + ]; + if (isEmptyStatement(statementNode)) { + forStatement.push(";"); + } else { + const hasStatementBlock = + statementNode.children.statementWithoutTrailingSubstatement?.[0] + .children.block !== undefined; + const statement = call(path, print, "statement"); + forStatement.push( + hasStatementBlock ? [" ", statement] : indent([line, statement]) + ); } - - return rejectAndConcat([ctx.Return[0], ctx.Semicolon[0]]); - } - - throwStatement(ctx: ThrowStatementCtx) { - const expression = this.visit(ctx.expression); - - return rejectAndConcat([ - concat([ctx.Throw[0], " "]), - expression, - ctx.Semicolon[0] - ]); - } - - synchronizedStatement(ctx: SynchronizedStatementCtx) { - const expression = this.visit(ctx.expression); - const block = this.visit(ctx.block); - - return rejectAndConcat([ - join(" ", [ - ctx.Synchronized[0], - concat([ - putIntoBraces(expression, softline, ctx.LBrace[0], ctx.RBrace[0]), - " " - ]) - ]), - block + return group(forStatement); + }, + + breakStatement(path, print) { + return path.node.children.Identifier + ? ["break ", call(path, print, "Identifier"), ";"] + : "break;"; + }, + + continueStatement(path, print) { + return path.node.children.Identifier + ? ["continue ", call(path, print, "Identifier"), ";"] + : "continue;"; + }, + + returnStatement(path, print) { + const { children } = path.node; + const statement: Doc[] = ["return"]; + if (children.expression) { + statement.push(" "); + const expression = call(path, print, "expression"); + if (isBinaryExpression(children.expression[0])) { + statement.push( + group([ + ifBreak("("), + indent([softline, expression]), + softline, + ifBreak(")") + ]) + ); + } else { + statement.push(expression); + } + } + statement.push(";"); + return statement; + }, + + throwStatement(path, print) { + return ["throw ", call(path, print, "expression"), ";"]; + }, + + synchronizedStatement(path, print) { + return [ + "synchronized ", + indentInParentheses(call(path, print, "expression")), + " ", + call(path, print, "block") + ]; + }, + + tryStatement(path, print) { + const { children } = path.node; + if (children.tryWithResourcesStatement) { + return call(path, print, "tryWithResourcesStatement"); + } + const blocks = ["try", call(path, print, "block")]; + if (children.catches) { + blocks.push(call(path, print, "catches")); + } + if (children.finally) { + blocks.push(call(path, print, "finally")); + } + return join(" ", blocks); + }, + + catches(path, print) { + return join(" ", map(path, print, "catchClause")); + }, + + catchClause(path, print) { + return [ + "catch ", + indentInParentheses(call(path, print, "catchFormalParameter")), + " ", + call(path, print, "block") + ]; + }, + + catchFormalParameter(path, print) { + return join(" ", [ + ...map(path, print, "variableModifier"), + call(path, print, "catchType"), + call(path, print, "variableDeclaratorId") ]); - } + }, - tryStatement(ctx: TryStatementCtx) { - if (ctx.tryWithResourcesStatement) { - return this.visit(ctx.tryWithResourcesStatement); + catchType(path, print) { + return join( + [line, "| "], + [call(path, print, "unannClassType"), ...map(path, print, "classType")] + ); + }, + + finally(path, print) { + return ["finally ", call(path, print, "block")]; + }, + + tryWithResourcesStatement(path, print) { + const { children } = path.node; + const blocks = [ + "try", + call(path, print, "resourceSpecification"), + call(path, print, "block") + ]; + if (children.catches) { + blocks.push(call(path, print, "catches")); } + if (children.finally) { + blocks.push(call(path, print, "finally")); + } + return join(" ", blocks); + }, - const block = this.visit(ctx.block); - const catches = this.visit(ctx.catches); - const finallyBlock = this.visit(ctx.finally); - - return rejectAndJoin(" ", [ctx.Try![0], block, catches, finallyBlock]); - } - - catches(ctx: CatchesCtx) { - const catchClauses = this.mapVisit(ctx.catchClause); - return rejectAndJoin(" ", catchClauses); - } - - catchClause(ctx: CatchClauseCtx) { - const catchFormalParameter = this.visit(ctx.catchFormalParameter); - const block = this.visit(ctx.block); - - return rejectAndConcat([ - group( - rejectAndConcat([ - rejectAndJoin(" ", [ctx.Catch[0], ctx.LBrace[0]]), - indent(rejectAndConcat([softline, catchFormalParameter])), - softline, - concat([ctx.RBrace[0], " "]) - ]) - ), - block - ]); - } + resourceSpecification(path, print, options) { + const resources = [call(path, print, "resourceList")]; + if (options.trailingComma !== "none") { + resources.push(ifBreak(";")); + } + return indentInParentheses(resources); + }, - catchFormalParameter(ctx: CatchFormalParameterCtx) { - const variableModifiers = this.mapVisit(ctx.variableModifier); - const catchType = this.visit(ctx.catchType); - const variableDeclaratorId = this.visit(ctx.variableDeclaratorId); + resourceList(path, print) { + return join([";", line], map(path, print, "resource")); + }, - return rejectAndJoin(" ", [ - rejectAndJoin(" ", variableModifiers), - catchType, - variableDeclaratorId - ]); - } - - catchType(ctx: CatchTypeCtx) { - const unannClassType = this.visit(ctx.unannClassType); - const classTypes = this.mapVisit(ctx.classType); - const ors = ctx.Or ? ctx.Or.map(elt => concat([line, elt, " "])) : []; - - return group(rejectAndJoinSeps(ors, [unannClassType, ...classTypes])); - } - - finally(ctx: FinallyCtx) { - const block = this.visit(ctx.block); - - return rejectAndJoin(" ", [ctx.Finally[0], block]); - } - - tryWithResourcesStatement(ctx: TryWithResourcesStatementCtx) { - const resourceSpecification = this.visit(ctx.resourceSpecification); - const block = this.visit(ctx.block); - const catches = this.visit(ctx.catches); - const finallyBlock = this.visit(ctx.finally); - - return rejectAndJoin(" ", [ - ctx.Try[0], - resourceSpecification, - block, - catches, - finallyBlock - ]); - } + resource: printSingle, - resourceSpecification(ctx: ResourceSpecificationCtx) { - const resourceList = this.visit(ctx.resourceList); - const optionalSemicolon = ctx.Semicolon ? ctx.Semicolon[0] : ""; + yieldStatement(path, print) { + return ["yield ", call(path, print, "expression"), ";"]; + }, - return putIntoBraces( - rejectAndConcat([resourceList, optionalSemicolon]), - softline, - ctx.LBrace[0], - ctx.RBrace[0] - ); - } - - resourceList(ctx: ResourceListCtx) { - const resources = this.mapVisit(ctx.resource); - const semicolons = ctx.Semicolon - ? ctx.Semicolon.map(elt => { - return concat([elt, line]); - }) - : [""]; - return rejectAndJoinSeps(semicolons, resources); - } - - resource(ctx: ResourceCtx) { - return this.visitSingle(ctx); - } - - yieldStatement(ctx: YieldStatementCtx) { - const expression = this.visit(ctx.expression); - return join(" ", [ctx.Yield[0], concat([expression, ctx.Semicolon[0]])]); - } - - variableAccess(ctx: VariableAccessCtx) { - return this.visitSingle(ctx); - } -} + variableAccess: printSingle +} satisfies Partial; diff --git a/packages/prettier-plugin-java/src/printers/classes.ts b/packages/prettier-plugin-java/src/printers/classes.ts index 5cde0be68..164a54180 100644 --- a/packages/prettier-plugin-java/src/printers/classes.ts +++ b/packages/prettier-plugin-java/src/printers/classes.ts @@ -1,1074 +1,598 @@ -import forEach from "lodash/forEach.js"; -import { - displaySemicolon, - getBlankLinesSeparator, - getClassBodyDeclarationsSeparator, - isStatementEmptyStatement, - putIntoBraces, - reject, - rejectAndConcat, - rejectAndJoin, - rejectAndJoinSeps, - sortClassTypeChildren, - sortModifiers -} from "./printer-utils.js"; -import { - concat, - group, - indent, - join, - indentIfBreak -} from "./prettier-builder.js"; -import { printTokenWithComments } from "./comments/format-comments.js"; -import { - hasLeadingComments, - hasLeadingLineComments -} from "./comments/comments-utils.js"; -import { handleCommentsParameters } from "./comments/handle-comments.js"; +import type { + ClassBodyCstNode, + EnumBodyDeclarationsCstNode +} from "java-parser"; +import type { AstPath, Doc } from "prettier"; import { builders } from "prettier/doc"; -import { BaseCstPrettierPrinter } from "../base-cst-printer.js"; import { - ClassBodyCtx, - ClassBodyDeclarationCtx, - ClassDeclarationCtx, - ClassExtendsCtx, - ClassImplementsCtx, - ClassMemberDeclarationCtx, - ClassModifierCtx, - ClassPermitsCtx, - CompactConstructorDeclarationCtx, - ConstructorBodyCtx, - ConstructorDeclarationCtx, - ConstructorDeclaratorCtx, - ConstructorModifierCtx, - EnumBodyCtx, - EnumBodyDeclarationsCtx, - EnumConstantCtx, - EnumConstantListCtx, - EnumConstantModifierCtx, - EnumDeclarationCtx, - ExceptionTypeCtx, - ExceptionTypeListCtx, - ExplicitConstructorInvocationCtx, - FieldDeclarationCtx, - FieldModifierCtx, - FormalParameterCtx, - FormalParameterListCtx, - InstanceInitializerCtx, - InterfaceTypeListCtx, - IToken, - MethodBodyCtx, - MethodDeclarationCtx, - MethodDeclaratorCtx, - MethodHeaderCtx, - MethodModifierCtx, - NormalClassDeclarationCtx, - QualifiedExplicitConstructorInvocationCtx, - ReceiverParameterCtx, - RecordBodyCtx, - RecordBodyDeclarationCtx, - RecordComponentCtx, - RecordComponentListCtx, - RecordComponentModifierCtx, - RecordDeclarationCtx, - RecordHeaderCtx, - ResultCtx, - SimpleTypeNameCtx, - StaticInitializerCtx, - ThrowsCtx, - TypeParameterListCtx, - TypeParametersCtx, - UnannClassOrInterfaceTypeCtx, - UnannClassTypeCtx, - UnannInterfaceTypeCtx, - UnannPrimitiveTypeCtx, - UnannPrimitiveTypeWithOptionalDimsSuffixCtx, - UnannReferenceTypeCtx, - UnannTypeCtx, - UnannTypeVariableCtx, - UnqualifiedExplicitConstructorInvocationCtx, - VariableArityParameterCtx, - VariableArityRecordComponentCtx, - VariableDeclaratorCtx, - VariableDeclaratorIdCtx, - VariableDeclaratorListCtx, - VariableInitializerCtx, - VariableModifierCtx, - VariableParaRegularParameterCtx -} from "java-parser"; -import { Doc } from "prettier"; -import { isAnnotationCstNode, isTypeArgumentsCstNode } from "../types/utils.js"; -import { printArgumentListWithBraces } from "../utils/index.js"; - -const { line, softline, hardline, lineSuffixBoundary } = builders; - -export class ClassesPrettierVisitor extends BaseCstPrettierPrinter { - classDeclaration(ctx: ClassDeclarationCtx) { - const modifiers = sortModifiers(ctx.classModifier); - const firstAnnotations = this.mapVisit(modifiers[0]); - const otherModifiers = this.mapVisit(modifiers[1]); - - let classCST; - if (ctx.normalClassDeclaration !== undefined) { - classCST = ctx.normalClassDeclaration; - } else if (ctx.enumDeclaration !== undefined) { - classCST = ctx.enumDeclaration; - } else { - classCST = ctx.recordDeclaration; - } - - const classDoc = this.visit(classCST); - - return rejectAndJoin(hardline, [ - rejectAndJoin(hardline, firstAnnotations), - rejectAndJoin(" ", [join(" ", otherModifiers), classDoc]) + call, + each, + hasDeclarationAnnotations, + hasLeadingComments, + indentInParentheses, + isBinaryExpression, + lineEndWithComments, + lineStartWithComments, + map, + onlyDefinedKey, + printBlock, + printClassPermits, + printClassType, + printDanglingComments, + printList, + printSingle, + printWithModifiers, + type JavaNodePrinters, + type JavaNonTerminal, + type JavaPrintFn +} from "./helpers.js"; + +const { group, hardline, indent, indentIfBreak, join, line, softline } = + builders; + +export default { + classDeclaration(path, print) { + const declarationKey = onlyDefinedKey(path.node.children, [ + "enumDeclaration", + "normalClassDeclaration", + "recordDeclaration" ]); - } - - normalClassDeclaration(ctx: NormalClassDeclarationCtx) { - const name = this.visit(ctx.typeIdentifier); - const optionalTypeParams = this.visit(ctx.typeParameters); - const optionalClassExtends = this.visit(ctx.classExtends); - const optionalClassImplements = this.visit(ctx.classImplements); - const optionalClassPermits = this.visit(ctx.classPermits); - const body = this.visit(ctx.classBody, { isNormalClassDeclaration: true }); - - let superClassesPart: Doc = ""; - if (optionalClassExtends) { - superClassesPart = indent(rejectAndConcat([line, optionalClassExtends])); + const declaration = call(path, print, declarationKey); + return printWithModifiers(path, print, "classModifier", declaration, true); + }, + + normalClassDeclaration(path, print) { + const { classExtends, classImplements, classPermits, typeParameters } = + path.node.children; + const header = ["class ", call(path, print, "typeIdentifier")]; + if (typeParameters) { + header.push(call(path, print, "typeParameters")); } - - let superInterfacesPart: Doc = ""; - if (optionalClassImplements) { - superInterfacesPart = indent( - rejectAndConcat([line, optionalClassImplements]) - ); + if (classExtends) { + header.push(indent([line, call(path, print, "classExtends")])); } - - let classPermits: Doc = ""; - if (optionalClassPermits) { - classPermits = indent(rejectAndConcat([line, optionalClassPermits])); + if (classImplements) { + header.push(indent([line, call(path, print, "classImplements")])); } - - return rejectAndJoin(" ", [ - group( - rejectAndConcat([ - rejectAndJoin(" ", [ctx.Class[0], name]), - optionalTypeParams, - superClassesPart, - superInterfacesPart, - classPermits - ]) - ), - body - ]); - } - - classModifier(ctx: ClassModifierCtx) { - if (ctx.annotation) { - return this.visit(ctx.annotation); + if (classPermits) { + header.push(indent([line, call(path, print, "classPermits")])); } - // public | protected | private | ... - return printTokenWithComments(this.getSingle(ctx) as IToken); - } + return [group(header), " ", call(path, print, "classBody")]; + }, - typeParameters(ctx: TypeParametersCtx) { - const typeParameterList = this.visit(ctx.typeParameterList); + classModifier: printSingle, - return putIntoBraces( - typeParameterList, + typeParameters(path, print) { + return group([ + "<", + indent([softline, call(path, print, "typeParameterList")]), softline, - ctx.Less[0], - ctx.Greater[0] - ); - } - - typeParameterList(ctx: TypeParameterListCtx) { - const typeParameter = this.mapVisit(ctx.typeParameter); - const commas = ctx.Comma ? ctx.Comma.map(elt => concat([elt, line])) : []; - - return group(rejectAndJoinSeps(commas, typeParameter)); - } - - classExtends(ctx: ClassExtendsCtx) { - return join(" ", [ctx.Extends[0], this.visit(ctx.classType)]); - } - - classImplements(ctx: ClassImplementsCtx) { - const interfaceTypeList = this.visit(ctx.interfaceTypeList); - - return group( - rejectAndConcat([ - ctx.Implements[0], - indent(rejectAndConcat([line, interfaceTypeList])) - ]) - ); - } - - classPermits(ctx: ClassPermitsCtx) { - const typeNames = this.mapVisit(ctx.typeName); - const commas = ctx.Comma ? ctx.Comma.map(elt => concat([elt, line])) : []; - - return group( - rejectAndConcat([ - ctx.Permits[0], - indent( - rejectAndConcat([line, group(rejectAndJoinSeps(commas, typeNames))]) - ) - ]) - ); - } - - interfaceTypeList(ctx: InterfaceTypeListCtx) { - const interfaceType = this.mapVisit(ctx.interfaceType); - const commas = ctx.Comma ? ctx.Comma.map(elt => concat([elt, line])) : []; - - return group(rejectAndJoinSeps(commas, interfaceType)); - } - - classBody(ctx: ClassBodyCtx, param: any) { - let content: Doc = ""; - if (ctx.classBodyDeclaration !== undefined) { - const classBodyDeclsVisited = reject( - this.mapVisit(ctx.classBodyDeclaration) - ); - - const separators = getClassBodyDeclarationsSeparator( - ctx.classBodyDeclaration - ); - - content = rejectAndJoinSeps(separators, classBodyDeclsVisited); - - // edge case when we have SemiColons - let shouldHardline = false; - ctx.classBodyDeclaration.forEach(elt => { - if ( - (elt.children.classMemberDeclaration && - !elt.children.classMemberDeclaration[0].children.Semicolon) || - elt.children.constructorDeclaration - ) { - shouldHardline = true; - } - }); - - if ( - (ctx.classBodyDeclaration[0].children.classMemberDeclaration || - ctx.classBodyDeclaration[0].children.constructorDeclaration) && - shouldHardline && - param && - param.isNormalClassDeclaration - ) { - content = rejectAndConcat([hardline, content]); - } - } - - return putIntoBraces(content, hardline, ctx.LCurly[0], ctx.RCurly[0]); - } - - classBodyDeclaration(ctx: ClassBodyDeclarationCtx) { - return this.visitSingle(ctx); - } - - classMemberDeclaration(ctx: ClassMemberDeclarationCtx) { - if (ctx.Semicolon) { - return displaySemicolon(ctx.Semicolon[0]); - } - - return this.visitSingle(ctx); - } - - fieldDeclaration(ctx: FieldDeclarationCtx) { - const modifiers = sortModifiers(ctx.fieldModifier); - const firstAnnotations = this.mapVisit(modifiers[0]); - const otherModifiers = this.mapVisit(modifiers[1]); - - const unannType = this.visit(ctx.unannType); - const variableDeclaratorList = this.visit(ctx.variableDeclaratorList); - - return rejectAndJoin(hardline, [ - rejectAndJoin(hardline, firstAnnotations), - rejectAndJoin(" ", [ - rejectAndJoin(" ", otherModifiers), - unannType, - concat([variableDeclaratorList, ctx.Semicolon[0]]) - ]) + ">" ]); - } + }, - fieldModifier(ctx: FieldModifierCtx) { - if (ctx.annotation) { - return this.visit(ctx.annotation); - } - // public | protected | private | ... - return printTokenWithComments(this.getSingle(ctx) as IToken); - } + typeParameterList(path, print) { + return printList(path, print, "typeParameter"); + }, - variableDeclaratorList(ctx: VariableDeclaratorListCtx) { - const variableDeclarators = this.mapVisit(ctx.variableDeclarator); - const commas = ctx.Comma ? ctx.Comma.map(elt => concat([elt, " "])) : []; - return rejectAndJoinSeps(commas, variableDeclarators); - } + classExtends(path, print) { + return ["extends ", call(path, print, "classType")]; + }, - variableDeclarator(ctx: VariableDeclaratorCtx) { - const variableDeclaratorId = this.visit(ctx.variableDeclaratorId); - if (ctx.Equals) { - const variableInitializer = this.visit(ctx.variableInitializer); + classImplements(path, print) { + return group([ + "implements", + indent([line, call(path, print, "interfaceTypeList")]) + ]); + }, - if (hasLeadingLineComments(ctx.variableInitializer![0])) { - return group( - indent( - rejectAndJoin(hardline, [ - rejectAndJoin(" ", [variableDeclaratorId, ctx.Equals[0]]), - variableInitializer - ]) - ) - ); - } + classPermits: printClassPermits, - if ( - // Array Initialisation - ctx.variableInitializer![0].children.arrayInitializer !== undefined || - // Lambda expression - ctx.variableInitializer![0].children.expression![0].children - .lambdaExpression !== undefined || - // Ternary Expression - (ctx.variableInitializer![0].children.expression![0].children - .conditionalExpression !== undefined && - ctx.variableInitializer![0].children.expression![0].children - .conditionalExpression[0].children.QuestionMark !== undefined) - ) { - const groupId = Symbol("assignment"); - return group([ - group(variableDeclaratorId), - " ", - ctx.Equals[0], - group(indent(line), { id: groupId }), - lineSuffixBoundary, - indentIfBreak(variableInitializer, { groupId }) - ]); - } + interfaceTypeList(path, print) { + return group(printList(path, print, "interfaceType")); + }, - if ( - ctx.variableInitializer![0].children.expression![0].children - .conditionalExpression !== undefined - ) { - const unaryExpressions = - ctx.variableInitializer![0].children.expression![0].children - .conditionalExpression[0].children.binaryExpression[0].children - .unaryExpression; - const firstPrimary = unaryExpressions[0].children.primary[0]; + classBody(path, print) { + return printBlock(path, printClassBodyDeclarations(path, print)); + }, - // Cast Expression - if ( - firstPrimary.children.primaryPrefix[0].children.castExpression !== - undefined && - unaryExpressions.length === 1 - ) { - const groupId = Symbol("assignment"); - return group([ - group(variableDeclaratorId), - " ", - ctx.Equals[0], - group(indent(line), { id: groupId }), - lineSuffixBoundary, - indentIfBreak(variableInitializer, { groupId }) - ]); - } + classBodyDeclaration: printSingle, - // New Expression - if ( - firstPrimary.children.primaryPrefix[0].children.newExpression !== - undefined - ) { - const groupId = Symbol("assignment"); - return group([ - group(variableDeclaratorId), - " ", - ctx.Equals[0], - group(indent(line), { id: groupId }), - lineSuffixBoundary, - indentIfBreak(variableInitializer, { groupId }) - ]); - } + classMemberDeclaration(path, print) { + const { children } = path.node; + return children.Semicolon + ? "" + : call(path, print, onlyDefinedKey(children)); + }, - // Method Invocation - const isMethodInvocation = - firstPrimary.children.primarySuffix !== undefined && - firstPrimary.children.primarySuffix[0].children - .methodInvocationSuffix !== undefined; - const isUniqueUnaryExpression = - ctx.variableInitializer![0].children.expression![0].children - .conditionalExpression[0].children.binaryExpression[0].children - .unaryExpression.length === 1; - - const isUniqueMethodInvocation = - isMethodInvocation && isUniqueUnaryExpression; - if (isUniqueMethodInvocation) { - const groupId = Symbol("assignment"); - return group([ - group(variableDeclaratorId), - " ", - ctx.Equals[0], - group(indent(line), { id: groupId }), - lineSuffixBoundary, - indentIfBreak(variableInitializer, { groupId }) - ]); - } - } - - return group( - indent( - rejectAndJoin(line, [ - rejectAndJoin(" ", [variableDeclaratorId, ctx.Equals[0]]), - variableInitializer - ]) - ) + fieldDeclaration(path, print) { + const declaration = [ + call(path, print, "unannType"), + " ", + call(path, print, "variableDeclaratorList"), + ";" + ]; + return printWithModifiers(path, print, "fieldModifier", declaration); + }, + + fieldModifier: printSingle, + + variableDeclaratorList(path, print) { + const declarators = map(path, print, "variableDeclarator"); + return declarators.length > 1 && + path.node.children.variableDeclarator.some( + ({ children }) => children.Equals + ) + ? group(indent(join([",", line], declarators)), { + shouldBreak: + (path.getNode(4) as JavaNonTerminal | null)?.name !== "forInit" + }) + : join(", ", declarators); + }, + + variableDeclarator(path, print) { + const { children } = path.node; + const variableInitializer = children.variableInitializer?.[0]; + const declaratorId = call(path, print, "variableDeclaratorId"); + if (!variableInitializer) { + return declaratorId; + } + const expression = variableInitializer.children.expression?.[0]; + const declarator = [declaratorId, " ", call(path, print, "Equals")]; + const initializer = call(path, print, "variableInitializer"); + if ( + hasLeadingComments(variableInitializer) || + (expression && isBinaryExpression(expression)) + ) { + declarator.push(group(indent([line, initializer]))); + } else { + const groupId = Symbol("assignment"); + declarator.push( + group(indent(line), { id: groupId }), + indentIfBreak(initializer, { groupId }) ); } - return variableDeclaratorId; - } + return group(declarator); + }, - variableDeclaratorId(ctx: VariableDeclaratorIdCtx) { - if (ctx.Underscore) { - return printTokenWithComments(ctx.Underscore[0]); + variableDeclaratorId(path, print) { + const { dims, Underscore } = path.node.children; + if (Underscore) { + return "_"; } - const identifier = ctx.Identifier![0]; - const dims = this.visit(ctx.dims); - - return rejectAndConcat([identifier, dims]); - } - - variableInitializer(ctx: VariableInitializerCtx) { - return this.visitSingle(ctx); - } - - unannType(ctx: UnannTypeCtx) { - return this.visitSingle(ctx); - } - - unannPrimitiveTypeWithOptionalDimsSuffix( - ctx: UnannPrimitiveTypeWithOptionalDimsSuffixCtx - ) { - const unannPrimitiveType = this.visit(ctx.unannPrimitiveType); - const dims = this.visit(ctx.dims); + const identifier = call(path, print, "Identifier"); + return dims ? [identifier, call(path, print, "dims")] : identifier; + }, + + variableInitializer: printSingle, + unannType: printSingle, + + unannPrimitiveTypeWithOptionalDimsSuffix(path, print) { + const type = call(path, print, "unannPrimitiveType"); + return path.node.children.dims ? [type, call(path, print, "dims")] : type; + }, + + unannPrimitiveType: printSingle, + + unannReferenceType(path, print) { + const type = call(path, print, "unannClassOrInterfaceType"); + return path.node.children.dims ? [type, call(path, print, "dims")] : type; + }, + + unannClassOrInterfaceType: printSingle, + unannClassType: printClassType, + unannInterfaceType: printSingle, + unannTypeVariable: printSingle, + + methodDeclaration(path, print) { + const declaration = [ + call(path, print, "methodHeader"), + path.node.children.methodBody[0].children.Semicolon ? "" : " ", + call(path, print, "methodBody") + ]; + return printWithModifiers(path, print, "methodModifier", declaration); + }, - return rejectAndConcat([unannPrimitiveType, dims]); - } + methodModifier: printSingle, - unannPrimitiveType(ctx: UnannPrimitiveTypeCtx) { - if (ctx.numericType) { - return this.visitSingle(ctx); + methodHeader(path, print) { + const { typeParameters, annotation, throws } = path.node.children; + const header: Doc[] = []; + if (typeParameters) { + header.push(call(path, print, "typeParameters")); } - return printTokenWithComments(this.getSingle(ctx) as IToken); - } - - unannReferenceType(ctx: UnannReferenceTypeCtx) { - const unannClassOrInterfaceType = this.visit(ctx.unannClassOrInterfaceType); - const dims = this.visit(ctx.dims); - - return rejectAndConcat([unannClassOrInterfaceType, dims]); - } - - unannClassOrInterfaceType(ctx: UnannClassOrInterfaceTypeCtx) { - return this.visit(ctx.unannClassType); - } - - unannClassType(ctx: UnannClassTypeCtx) { - const tokens = sortClassTypeChildren( - ctx.annotation, - ctx.typeArguments, - ctx.Identifier + if (annotation) { + header.push(join(line, map(path, print, "annotation"))); + } + header.push( + call(path, print, "result"), + call(path, print, "methodDeclarator") ); + return throws + ? group([ + ...join(" ", header), + group(indent([line, call(path, print, "throws")])) + ]) + : group(join(" ", header)); + }, - const segments: Doc[] = []; - let currentSegment: (Doc | IToken)[] = []; - - forEach(tokens, (token, i) => { - if (isTypeArgumentsCstNode(token)) { - currentSegment.push(this.visit([token])); - segments.push(rejectAndConcat(currentSegment)); - currentSegment = []; - } else if (isAnnotationCstNode(token)) { - currentSegment.push(this.visit([token])); - currentSegment.push(" "); - } else { - currentSegment.push(token as IToken); - if ( - (i + 1 < tokens.length && !isTypeArgumentsCstNode(tokens[i + 1])) || - i + 1 === tokens.length - ) { - segments.push(rejectAndConcat(currentSegment)); - currentSegment = []; - } - } - }); - - return rejectAndJoinSeps(ctx.Dot, segments); - } - - unannInterfaceType(ctx: UnannInterfaceTypeCtx) { - return this.visit(ctx.unannClassType); - } - - unannTypeVariable(ctx: UnannTypeVariableCtx) { - return printTokenWithComments(this.getSingle(ctx) as IToken); - } - - methodDeclaration(ctx: MethodDeclarationCtx) { - const modifiers = sortModifiers(ctx.methodModifier); - const firstAnnotations = this.mapVisit(modifiers[0]); - const otherModifiers = this.mapVisit(modifiers[1]); - - const header = this.visit(ctx.methodHeader); - const body = this.visit(ctx.methodBody); - - const headerBodySeparator = isStatementEmptyStatement(body) ? "" : " "; + result: printSingle, - return rejectAndJoin(hardline, [ - rejectAndJoin(hardline, firstAnnotations), - rejectAndJoin(" ", [ - rejectAndJoin(" ", otherModifiers), - rejectAndJoin(headerBodySeparator, [header, body]) - ]) - ]); - } - - methodModifier(ctx: MethodModifierCtx) { - if (ctx.annotation) { - return this.visit(ctx.annotation); + methodDeclarator(path, print) { + const { dims, formalParameterList, receiverParameter } = path.node.children; + const declarator = [call(path, print, "Identifier")]; + const parameters: Doc[] = []; + if (receiverParameter) { + parameters.push(call(path, print, "receiverParameter")); } - // public | protected | private | Synchronized | ... - return printTokenWithComments(this.getSingle(ctx) as IToken); - } - - methodHeader(ctx: MethodHeaderCtx) { - const typeParameters = this.visit(ctx.typeParameters); - const annotations = this.mapVisit(ctx.annotation); - const result = this.visit(ctx.result); - const declarator = this.visit(ctx.methodDeclarator); - const throws = this.visit(ctx.throws); - - return group( - concat([ - rejectAndJoin(" ", [ - typeParameters, - rejectAndJoin(line, annotations), - result, - declarator, - throws - ]) - ]) - ); - } - - result(ctx: ResultCtx) { - if (ctx.unannType) { - return this.visit(ctx.unannType); + if (formalParameterList) { + parameters.push(call(path, print, "formalParameterList")); } - // void - return printTokenWithComments(this.getSingle(ctx) as IToken); - } - - methodDeclarator(ctx: MethodDeclaratorCtx) { - const parameters = [ - ...(ctx.receiverParameter ?? []), - ...(ctx.formalParameterList?.[0].children.formalParameter ?? []) - ]; - handleCommentsParameters(ctx.LBrace[0], parameters, ctx.RBrace[0]); - const identifier = printTokenWithComments(ctx.Identifier[0]); - const receiverParameter = this.visit(ctx.receiverParameter); - const formalParameterList = this.visit(ctx.formalParameterList); - const dims = this.visit(ctx.dims); - - return rejectAndConcat([ - identifier, - putIntoBraces( - rejectAndJoin(line, [ - rejectAndConcat([receiverParameter, ctx.Comma?.[0]]), - formalParameterList - ]), - softline, - ctx.LBrace[0], - ctx.RBrace[0] - ), - dims - ]); - } - - receiverParameter(ctx: ReceiverParameterCtx) { - const annotations = this.mapVisit(ctx.annotation); - const unannType = this.visit(ctx.unannType); - - return rejectAndJoin(" ", [ - ...annotations, - unannType, - rejectAndConcat([ctx.Identifier?.[0], ctx.Dot?.[0], ctx.This[0]]) + const items = parameters.length + ? join([",", line], parameters) + : printDanglingComments(path); + declarator.push(items.length ? indentInParentheses(items) : "()"); + if (dims) { + declarator.push(call(path, print, "dims")); + } + return declarator; + }, + + receiverParameter(path, print) { + return join(" ", [ + ...map(path, print, "annotation"), + call(path, print, "unannType"), + path.node.children.Identifier + ? [call(path, print, "Identifier"), ".this"] + : "this" ]); - } + }, - formalParameterList(ctx: FormalParameterListCtx) { - const formalParameter = this.mapVisit(ctx.formalParameter); - const commas = ctx.Comma ? ctx.Comma.map(elt => concat([elt, line])) : []; - return rejectAndJoinSeps(commas, formalParameter); - } + formalParameterList(path, print) { + return printList(path, print, "formalParameter"); + }, - formalParameter(ctx: FormalParameterCtx) { - return this.visitSingle(ctx); - } - - variableParaRegularParameter(ctx: VariableParaRegularParameterCtx) { - const variableModifier = this.mapVisit(ctx.variableModifier); - const unannType = this.visit(ctx.unannType); - const variableDeclaratorId = this.visit(ctx.variableDeclaratorId); + formalParameter: printSingle, - return rejectAndJoin(" ", [ - rejectAndJoin(" ", variableModifier), - unannType, - variableDeclaratorId + variableParaRegularParameter(path, print) { + return join(" ", [ + ...map(path, print, "variableModifier"), + call(path, print, "unannType"), + call(path, print, "variableDeclaratorId") ]); - } - - variableArityParameter(ctx: VariableArityParameterCtx) { - const variableModifier = this.mapVisit(ctx.variableModifier); - const unannType = this.visit(ctx.unannType); - const annotations = this.mapVisit(ctx.annotation); - const identifier = ctx.Identifier[0]; + }, - const unannTypePrinted = - ctx.annotation === undefined - ? concat([unannType, ctx.DotDotDot[0]]) - : unannType; - const annotationsPrinted = - ctx.annotation === undefined - ? annotations - : concat([rejectAndJoin(" ", annotations), ctx.DotDotDot[0]]); - - return rejectAndJoin(" ", [ - join(" ", variableModifier), - unannTypePrinted, - annotationsPrinted, - identifier + variableArityParameter(path, print) { + const type = join(" ", [ + ...map(path, print, "variableModifier"), + call(path, print, "unannType"), + ...map(path, print, "annotation") ]); - } + return [type, "... ", call(path, print, "Identifier")]; + }, - variableModifier(ctx: VariableModifierCtx) { - if (ctx.annotation) { - return this.visit(ctx.annotation); - } - return printTokenWithComments(this.getSingle(ctx) as IToken); - } + variableModifier: printSingle, - throws(ctx: ThrowsCtx) { - const exceptionTypeList = this.visit(ctx.exceptionTypeList); - const throwsDeclaration = join(" ", [ctx.Throws[0], exceptionTypeList]); - return group(indent(rejectAndConcat([softline, throwsDeclaration]))); - } + throws(path, print) { + return ["throws ", call(path, print, "exceptionTypeList")]; + }, - exceptionTypeList(ctx: ExceptionTypeListCtx) { - const exceptionTypes = this.mapVisit(ctx.exceptionType); - const commas = ctx.Comma ? ctx.Comma.map(elt => concat([elt, " "])) : []; - return rejectAndJoinSeps(commas, exceptionTypes); - } + exceptionTypeList(path, print) { + return join(", ", map(path, print, "exceptionType")); + }, - exceptionType(ctx: ExceptionTypeCtx) { - return this.visitSingle(ctx); - } + exceptionType: printSingle, + methodBody: printSingle, + instanceInitializer: printSingle, - methodBody(ctx: MethodBodyCtx) { - if (ctx.block) { - return this.visit(ctx.block); - } + staticInitializer(path, print) { + return ["static ", call(path, print, "block")]; + }, - return printTokenWithComments(this.getSingle(ctx) as IToken); - } - - instanceInitializer(ctx: InstanceInitializerCtx) { - return this.visitSingle(ctx); - } - - staticInitializer(ctx: StaticInitializerCtx) { - const block = this.visit(ctx.block); - - return join(" ", [ctx.Static[0], block]); - } - - constructorDeclaration(ctx: ConstructorDeclarationCtx) { - const modifiers = sortModifiers(ctx.constructorModifier); - const firstAnnotations = this.mapVisit(modifiers[0]); - const otherModifiers = this.mapVisit(modifiers[1]); - - const constructorDeclarator = this.visit(ctx.constructorDeclarator); - const throws = this.visit(ctx.throws); - const constructorBody = this.visit(ctx.constructorBody); - - return rejectAndJoin(" ", [ - group( - rejectAndJoin(hardline, [ - rejectAndJoin(hardline, firstAnnotations), - rejectAndJoin(" ", [ - join(" ", otherModifiers), - constructorDeclarator, - throws - ]) - ]) - ), - constructorBody - ]); - } - - constructorModifier(ctx: ConstructorModifierCtx) { - if (ctx.annotation) { - return this.visit(ctx.annotation); + constructorDeclaration(path, print) { + const declaration = [call(path, print, "constructorDeclarator")]; + if (path.node.children.throws) { + declaration.push(group(indent([line, call(path, print, "throws")]))); } - // public | protected | private | Synchronized | ... - return printTokenWithComments(this.getSingle(ctx) as IToken); - } - - constructorDeclarator(ctx: ConstructorDeclaratorCtx) { - const parameters = - ctx.receiverParameter ?? - ctx.formalParameterList?.[0].children.formalParameter ?? - []; - handleCommentsParameters(ctx.LBrace[0], parameters, ctx.RBrace[0]); - const typeParameters = this.visit(ctx.typeParameters); - const simpleTypeName = this.visit(ctx.simpleTypeName); - const receiverParameter = this.visit(ctx.receiverParameter); - const formalParameterList = this.visit(ctx.formalParameterList); - - return rejectAndJoin(" ", [ - typeParameters, - concat([ - simpleTypeName, - putIntoBraces( - rejectAndJoin(line, [ - rejectAndConcat([receiverParameter, ctx.Comma?.[0]]), - formalParameterList - ]), - softline, - ctx.LBrace[0], - ctx.RBrace[0] - ) - ]) - ]); - } - - simpleTypeName(ctx: SimpleTypeNameCtx) { - return this.visitSingle(ctx); - } - - constructorBody(ctx: ConstructorBodyCtx) { - const explicitConstructorInvocation = this.visit( - ctx.explicitConstructorInvocation + declaration.push(" ", call(path, print, "constructorBody")); + return printWithModifiers( + path, + print, + "constructorModifier", + declaration, + true ); + }, - const blockStatements = this.visit(ctx.blockStatements); + constructorModifier: printSingle, - return putIntoBraces( - rejectAndJoin(hardline, [explicitConstructorInvocation, blockStatements]), - hardline, - ctx.LCurly[0], - ctx.RCurly[0] - ); - } - - explicitConstructorInvocation(ctx: ExplicitConstructorInvocationCtx) { - return this.visitSingle(ctx); - } - - unqualifiedExplicitConstructorInvocation( - ctx: UnqualifiedExplicitConstructorInvocationCtx - ) { - const typeArguments = this.visit(ctx.typeArguments); - const keyWord = ctx.This ? ctx.This[0] : ctx.Super![0]; - const argumentList = printArgumentListWithBraces.call( - this, - ctx.argumentList, - ctx.RBrace![0], - ctx.LBrace[0] - ); - return rejectAndConcat([ - typeArguments, - keyWord, - group(rejectAndConcat([argumentList, ctx.Semicolon[0]])) - ]); - } - - qualifiedExplicitConstructorInvocation( - ctx: QualifiedExplicitConstructorInvocationCtx - ) { - const expressionName = this.visit(ctx.expressionName); - const typeArguments = this.visit(ctx.typeArguments); - const argumentList = printArgumentListWithBraces.call( - this, - ctx.argumentList, - ctx.RBrace![0], - ctx.LBrace[0] + constructorDeclarator(path, print) { + const { children } = path.node; + const parameters: Doc[] = []; + if (children.receiverParameter) { + parameters.push(call(path, print, "receiverParameter")); + } + if (children.formalParameterList) { + parameters.push(call(path, print, "formalParameterList")); + } + const header = [call(path, print, "simpleTypeName")]; + header.push( + parameters.length + ? indentInParentheses(join([",", line], parameters)) + : "()" ); + return children.typeParameters + ? [call(path, print, "typeParameters"), " ", ...header] + : header; + }, + + simpleTypeName: printSingle, + + constructorBody(path, print) { + const { children } = path.node; + const statements: Doc[] = []; + if (children.explicitConstructorInvocation) { + statements.push(call(path, print, "explicitConstructorInvocation")); + } + if (children.blockStatements) { + statements.push(call(path, print, "blockStatements")); + } + return printBlock(path, statements); + }, - return rejectAndConcat([ - expressionName, - ctx.Dot[0], - typeArguments, - ctx.Super[0], - group(rejectAndConcat([argumentList, ctx.Semicolon[0]])) - ]); - } - - enumDeclaration(ctx: EnumDeclarationCtx) { - const classModifier = this.mapVisit(ctx.classModifier); - const typeIdentifier = this.visit(ctx.typeIdentifier); - const classImplements = this.visit(ctx.classImplements); - const enumBody = this.visit(ctx.enumBody); - - return rejectAndJoin(" ", [ - join(" ", classModifier), - ctx.Enum[0], - typeIdentifier, - classImplements, - enumBody - ]); - } - - enumBody(ctx: EnumBodyCtx) { - const enumConstantList = this.visit(ctx.enumConstantList); - const enumBodyDeclarations = this.visit(ctx.enumBodyDeclarations); - - const hasEnumConstants = ctx.enumConstantList !== undefined; - const hasNoClassBodyDeclarations = - ctx.enumBodyDeclarations === undefined || - ctx.enumBodyDeclarations[0].children.classBodyDeclaration === undefined; - - // edge case: https://github.com/jhipster/prettier-java/issues/383 - const handleEnumBodyDeclarationsLeadingComments = - !hasNoClassBodyDeclarations && - hasLeadingComments(ctx.enumBodyDeclarations![0]) - ? hardline - : ""; + explicitConstructorInvocation: printSingle, - let optionalComma; - if ( - hasEnumConstants && - hasNoClassBodyDeclarations && - this.prettierOptions.trailingComma !== "none" - ) { - optionalComma = ctx.Comma ? ctx.Comma[0] : ","; + unqualifiedExplicitConstructorInvocation(path, print) { + const { children } = path.node; + const invocation: Doc[] = []; + if (children.typeArguments) { + invocation.push(call(path, print, "typeArguments")); + } + invocation.push(children.Super ? "super" : "this"); + if (children.argumentList) { + invocation.push(group(["(", call(path, print, "argumentList"), ")"])); } else { - optionalComma = ctx.Comma ? { ...ctx.Comma[0], image: "" } : ""; + invocation.push( + indentInParentheses(printDanglingComments(path), { shouldBreak: true }) + ); } - - return putIntoBraces( - rejectAndConcat([ - enumConstantList, - optionalComma, - handleEnumBodyDeclarationsLeadingComments, - enumBodyDeclarations - ]), - hardline, - ctx.LCurly[0], - ctx.RCurly[0] - ); - } - - enumConstantList(ctx: EnumConstantListCtx) { - const enumConstants = this.mapVisit(ctx.enumConstant); - - const blankLineSeparators = getBlankLinesSeparator(ctx.enumConstant); - const commas = ctx.Comma - ? ctx.Comma.map((elt, index) => - concat([elt, blankLineSeparators![index]]) - ) - : []; - - return group(rejectAndJoinSeps(commas, enumConstants)); - } - - enumConstant(ctx: EnumConstantCtx) { - const modifiers = sortModifiers(ctx.enumConstantModifier); - const firstAnnotations = this.mapVisit(modifiers[0]); - const otherModifiers = this.mapVisit(modifiers[1]); - - const identifier = ctx.Identifier[0]; - const classBody = this.visit(ctx.classBody); - - const optionalBracesAndArgumentList = ctx.LBrace - ? printArgumentListWithBraces.call( - this, - ctx.argumentList, - ctx.RBrace![0], - ctx.LBrace[0] - ) - : ""; - - return rejectAndJoin(hardline, [ - rejectAndJoin(hardline, firstAnnotations), - rejectAndJoin(" ", [ - rejectAndJoin(" ", otherModifiers), - rejectAndConcat([identifier, optionalBracesAndArgumentList]), - classBody - ]) - ]); - } - - enumConstantModifier(ctx: EnumConstantModifierCtx) { - return this.visitSingle(ctx); - } - - enumBodyDeclarations(ctx: EnumBodyDeclarationsCtx) { - if (ctx.classBodyDeclaration !== undefined) { - const classBodyDeclaration = this.mapVisit(ctx.classBodyDeclaration); - - const separators = getClassBodyDeclarationsSeparator( - ctx.classBodyDeclaration + invocation.push(";"); + return invocation; + }, + + qualifiedExplicitConstructorInvocation(path, print) { + const { children } = path.node; + const invocation = [call(path, print, "expressionName"), "."]; + if (children.typeArguments) { + invocation.push(call(path, print, "typeArguments")); + } + invocation.push("super"); + if (children.argumentList) { + invocation.push(group(["(", call(path, print, "argumentList"), ")"])); + } else { + invocation.push( + indentInParentheses(printDanglingComments(path), { shouldBreak: true }) ); - - return rejectAndJoin(concat([hardline, hardline]), [ - ctx.Semicolon[0], - rejectAndJoinSeps(separators, classBodyDeclaration) - ]); } - - return printTokenWithComments({ ...ctx.Semicolon[0], image: "" }); - } - - recordDeclaration(ctx: RecordDeclarationCtx) { - const name = this.visit(ctx.typeIdentifier); - const optionalTypeParams = this.visit(ctx.typeParameters); - - const recordHeader = this.visit(ctx.recordHeader); - - let superInterfacesPart: Doc = ""; - const optionalClassImplements = this.visit(ctx.classImplements); - if (optionalClassImplements) { - superInterfacesPart = indent( - rejectAndConcat([line, optionalClassImplements]) + invocation.push(";"); + return invocation; + }, + + enumDeclaration(path, print) { + const header = ["enum", call(path, print, "typeIdentifier")]; + if (path.node.children.classImplements) { + header.push(call(path, print, "classImplements")); + } + return join(" ", [...header, call(path, print, "enumBody")]); + }, + + enumBody(path, print, options) { + const { children } = path.node; + const contents: Doc[] = []; + const hasNonEmptyDeclaration = (children.enumBodyDeclarations ?? []) + .flatMap(({ children }) => children.classBodyDeclaration ?? []) + .some( + ({ children }) => + !children.classMemberDeclaration?.[0].children.Semicolon ); + if (children.enumConstantList) { + contents.push(call(path, print, "enumConstantList")); + if (!hasNonEmptyDeclaration && options.trailingComma !== "none") { + contents.push(","); + } } - - const body = this.visit(ctx.recordBody); - - return rejectAndJoin(" ", [ - group( - rejectAndConcat([ - rejectAndJoin(" ", [ctx.Record[0], name]), - optionalTypeParams, - recordHeader, - superInterfacesPart - ]) - ), - body - ]); - } - recordHeader(ctx: RecordHeaderCtx) { - const recordComponents = - ctx.recordComponentList?.[0].children.recordComponent ?? []; - handleCommentsParameters(ctx.LBrace[0], recordComponents, ctx.RBrace[0]); - const recordComponentList = this.visit(ctx.recordComponentList); - return putIntoBraces( - recordComponentList, - softline, - ctx.LBrace[0], - ctx.RBrace[0] + if (hasNonEmptyDeclaration) { + contents.push(";", hardline, call(path, print, "enumBodyDeclarations")); + } + return printBlock(path, contents.length ? [contents] : []); + }, + + enumConstantList(path, print) { + return join( + [",", hardline], + map( + path, + constantPath => { + const constant = print(constantPath); + const { node, previous } = constantPath; + return !previous || + lineStartWithComments(node) <= lineEndWithComments(previous) + 1 + ? constant + : [hardline, constant]; + }, + "enumConstant" + ) ); - } - recordComponentList(ctx: RecordComponentListCtx) { - const recordComponents = this.mapVisit(ctx.recordComponent); + }, - const blankLineSeparators = getBlankLinesSeparator( - ctx.recordComponent, - line - ); - const commas = ctx.Comma - ? ctx.Comma.map((elt, index) => - concat([elt, blankLineSeparators![index]]) - ) - : []; + enumConstant(path, print) { + const { argumentList, classBody } = path.node.children; + const initializer = [call(path, print, "Identifier")]; + if (argumentList) { + initializer.push(group(["(", call(path, print, "argumentList"), ")"])); + } + if (classBody) { + initializer.push(" ", call(path, print, "classBody")); + } + return printWithModifiers(path, print, "enumConstantModifier", initializer); + }, - return rejectAndJoinSeps(commas, recordComponents); - } - recordComponent(ctx: RecordComponentCtx) { - const modifiers = this.mapVisit(ctx.recordComponentModifier); - const unannType = this.visit(ctx.unannType); + enumConstantModifier: printSingle, - if (ctx.Identifier !== undefined) { - return group( - rejectAndJoin(line, [ - join(line, modifiers), - join(" ", [unannType, ctx.Identifier[0]]) - ]) - ); - } + enumBodyDeclarations(path, print) { + return join(hardline, printClassBodyDeclarations(path, print)); + }, - const variableArityRecordComponent = this.visit( - ctx.variableArityRecordComponent + recordDeclaration(path, print) { + const { children } = path.node; + const header = ["record ", call(path, print, "typeIdentifier")]; + if (children.typeParameters) { + header.push(call(path, print, "typeParameters")); + } + header.push(call(path, print, "recordHeader")); + if (children.classImplements) { + header.push(" ", call(path, print, "classImplements")); + } + return [group(header), " ", call(path, print, "recordBody")]; + }, + + recordHeader(path, print) { + return path.node.children.recordComponentList + ? indentInParentheses(call(path, print, "recordComponentList")) + : indentInParentheses(printDanglingComments(path), { shouldBreak: true }); + }, + + recordComponentList(path, print) { + return join( + [",", line], + map( + path, + componentPath => { + const { node, previous } = componentPath; + const blankLine = + previous && + lineStartWithComments(node) > lineEndWithComments(previous) + 1; + const component = print(componentPath); + return blankLine ? [softline, component] : component; + }, + "recordComponent" + ) ); + }, + + recordComponent(path, print) { + const { children } = path.node; + const component = [call(path, print, "unannType")]; if ( - ctx.variableArityRecordComponent![0].children.annotation !== undefined + children.Identifier || + children.variableArityRecordComponent![0].children.annotation ) { - return group( - rejectAndJoin(line, [ - join(line, modifiers), - join(" ", [unannType, variableArityRecordComponent]) - ]) - ); + component.push(" "); } - + const suffixKey = onlyDefinedKey(children, [ + "Identifier", + "variableArityRecordComponent" + ]); + component.push(call(path, print, suffixKey)); return group( - rejectAndJoin(line, [ - join(line, modifiers), - concat([unannType, variableArityRecordComponent]) - ]) + join(line, [...map(path, print, "recordComponentModifier"), component]) ); - } - variableArityRecordComponent(ctx: VariableArityRecordComponentCtx) { - const annotations = this.mapVisit(ctx.annotation); - const identifier = ctx.Identifier[0]; + }, - return rejectAndJoin(" ", [ - rejectAndConcat([rejectAndJoin(" ", annotations), ctx.DotDotDot[0]]), - identifier - ]); - } - - recordComponentModifier(ctx: RecordComponentModifierCtx) { - return this.visitSingle(ctx); - } - - recordBody(ctx: RecordBodyCtx) { - return putIntoBraces( - rejectAndJoinSeps( - getBlankLinesSeparator(ctx.recordBodyDeclaration), - this.mapVisit(ctx.recordBodyDeclaration) - ), - hardline, - ctx.LCurly[0], - ctx.RCurly[0] + variableArityRecordComponent(path, print) { + return [ + ...join(" ", map(path, print, "annotation")), + "... ", + call(path, print, "Identifier") + ]; + }, + + recordComponentModifier: printSingle, + + recordBody(path, print) { + const declarations: Doc[] = []; + let previousRequiresPadding = false; + each( + path, + declarationPath => { + const declaration = print(declarationPath); + if (declaration === "") { + return; + } + const { node, previous } = declarationPath; + const fieldDeclaration = + node.children.classBodyDeclaration?.[0].children + .classMemberDeclaration?.[0].children.fieldDeclaration?.[0] + .children; + const currentRequiresPadding = + !fieldDeclaration || + hasDeclarationAnnotations(fieldDeclaration.fieldModifier ?? []); + const blankLine = + declarations.length > 0 && + (previousRequiresPadding || + currentRequiresPadding || + lineStartWithComments(node) > lineEndWithComments(previous!) + 1); + declarations.push(blankLine ? [hardline, declaration] : declaration); + previousRequiresPadding = currentRequiresPadding; + }, + "recordBodyDeclaration" ); - } - - recordBodyDeclaration(ctx: RecordBodyDeclarationCtx) { - return this.visitSingle(ctx); - } + return printBlock(path, declarations); + }, - compactConstructorDeclaration(ctx: CompactConstructorDeclarationCtx) { - const modifiers = sortModifiers(ctx.constructorModifier); - const firstAnnotations = this.mapVisit(modifiers[0]); - const otherModifiers = this.mapVisit(modifiers[1]); + recordBodyDeclaration: printSingle, - const name = this.visit(ctx.simpleTypeName); - const constructorBody = this.visit(ctx.constructorBody); - - return rejectAndJoin(" ", [ - group( - rejectAndJoin(hardline, [ - rejectAndJoin(hardline, firstAnnotations), - rejectAndJoin(" ", [join(" ", otherModifiers), name]) - ]) - ), - constructorBody - ]); - } - - isDims() { - return "isDims"; + compactConstructorDeclaration(path, print) { + const declaration = [ + call(path, print, "simpleTypeName"), + " ", + call(path, print, "constructorBody") + ]; + return printWithModifiers( + path, + print, + "constructorModifier", + declaration, + true + ); } +} satisfies Partial; + +function printClassBodyDeclarations( + path: AstPath, + print: JavaPrintFn +) { + if (!path.node.children.classBodyDeclaration) { + return []; + } + const declarations: Doc[] = []; + let previousRequiresPadding = + path.node.name === "enumBodyDeclarations" || + (path.grandparent as JavaNonTerminal | null)?.name === + "normalClassDeclaration"; + each( + path, + declarationPath => { + const declaration = print(declarationPath); + if (declaration === "") { + return; + } + const { node, previous } = declarationPath; + const fieldDeclaration = + node.children.classMemberDeclaration?.[0].children.fieldDeclaration?.[0] + .children; + const currentRequiresPadding = fieldDeclaration + ? hasDeclarationAnnotations(fieldDeclaration.fieldModifier ?? []) + : true; + const blankLine = + previousRequiresPadding || + (declarations.length > 0 && + (currentRequiresPadding || + lineStartWithComments(node) > lineEndWithComments(previous!) + 1)); + declarations.push(blankLine ? [hardline, declaration] : declaration); + previousRequiresPadding = currentRequiresPadding; + }, + "classBodyDeclaration" + ); + return declarations; } diff --git a/packages/prettier-plugin-java/src/printers/comments/comments-utils.ts b/packages/prettier-plugin-java/src/printers/comments/comments-utils.ts deleted file mode 100644 index 543f0ef93..000000000 --- a/packages/prettier-plugin-java/src/printers/comments/comments-utils.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { CstElement, IToken } from "java-parser"; - -type LeadingComments = T & { - leadingComments: IToken[]; -}; - -type TrailingComments = T & { - trailingComments: IToken[]; -}; - -export function hasLeadingComments( - token: CstElement -): token is LeadingComments { - return token.leadingComments !== undefined; -} - -export function hasTrailingComments( - token: CstElement -): token is TrailingComments { - return token.trailingComments !== undefined; -} - -export function hasLeadingLineComments( - token: CstElement -): token is LeadingComments { - return ( - token.leadingComments !== undefined && - token.leadingComments.length !== 0 && - token.leadingComments[token.leadingComments.length - 1].tokenType.name === - "LineComment" - ); -} - -export function hasTrailingLineComments( - token: CstElement -): token is TrailingComments { - return ( - token.trailingComments !== undefined && - token.trailingComments.length !== 0 && - token.trailingComments[token.trailingComments.length - 1].tokenType.name === - "LineComment" - ); -} - -export function hasComments(token: CstElement) { - return hasLeadingComments(token) || hasTrailingComments(token); -} diff --git a/packages/prettier-plugin-java/src/printers/comments/format-comments.ts b/packages/prettier-plugin-java/src/printers/comments/format-comments.ts deleted file mode 100644 index f30483681..000000000 --- a/packages/prettier-plugin-java/src/printers/comments/format-comments.ts +++ /dev/null @@ -1,223 +0,0 @@ -import { builders } from "prettier/doc"; -import { CstElement, CstNode, IToken } from "java-parser"; -import { CstNodeLocation } from "@chevrotain/types"; -import { isCstElementOrUndefinedIToken } from "../../types/utils.js"; -import { Doc, doc } from "prettier"; -import isEmptyDoc from "../../utils/isEmptyDoc.js"; - -const { hardline, lineSuffix, breakParent, literalline } = builders; - -/** - * Takes a token and return a doc with: - * - concatenated leading comments - * - the token image - * - concatenated trailing comments - * - * @param {IToken} token - * @return a doc with the token and its comments - */ -export function printTokenWithComments(token: IToken) { - return printWithComments( - token, - token.image.includes("\n") - ? doc.utils.replaceEndOfLine(token.image) - : token.image, - getTokenLeadingComments, - getTokenTrailingComments - ); -} - -/** - * Takes a node and return a doc with: - * - concatenated leading comments - * - the node doc value - * - concatenated trailing comments - * - * @param {CstNode} node - * @param {Doc} value - the converted node value - * @return a doc with the token and its comments - */ -export function printNodeWithComments(node: CstNode, value: Doc) { - return printWithComments( - node, - value, - getNodeLeadingComments, - getNodeTrailingComments - ); -} - -function printWithComments( - nodeOrToken: T, - value: Doc, - getLeadingComments: (token: T) => Doc[], - getTrailingComments: (token: T, value: Doc) => Doc[] -) { - const leadingComments = getLeadingComments(nodeOrToken); - const trailingComments = getTrailingComments(nodeOrToken, value); - - return leadingComments.length === 0 && trailingComments.length === 0 - ? value - : [...leadingComments, value, ...trailingComments]; -} - -/** - * @param {IToken} token - * @return an array containing processed leading comments and separators - */ -export function getTokenLeadingComments(token: IToken) { - return getLeadingComments(token, token); -} - -/** - * @param {CstNode} node - * @return an array containing processed leading comments and separators - */ -function getNodeLeadingComments(node: CstNode) { - return getLeadingComments(node, node.location); -} - -function getLeadingComments( - nodeOrToken: CstElement, - location: CstNodeLocation | IToken -): Doc[] { - const arr = []; - if (nodeOrToken.leadingComments !== undefined) { - let previousEndLine = nodeOrToken.leadingComments[0].endLine; - let step; - arr.push(formatComment(nodeOrToken.leadingComments[0])); - for (let i = 1; i < nodeOrToken.leadingComments.length; i++) { - step = nodeOrToken.leadingComments[i].startLine - previousEndLine; - if ( - step === 1 || - nodeOrToken.leadingComments[i].startOffset > location.startOffset - ) { - arr.push(hardline); - } else if (step > 1) { - arr.push(hardline, hardline); - } - - arr.push(formatComment(nodeOrToken.leadingComments[i])); - previousEndLine = nodeOrToken.leadingComments[i].endLine; - } - - step = location.startLine! - previousEndLine; - if ( - step === 1 || - nodeOrToken.leadingComments[nodeOrToken.leadingComments.length - 1] - .startOffset > location.startOffset - ) { - arr.push(hardline); - } else if (step > 1) { - arr.push(hardline, hardline); - } - } - - return arr; -} - -/** - * @param {IToken} token - * @return an array containing processed trailing comments and separators - */ -function getTokenTrailingComments(token: IToken) { - return getTrailingComments(token, token.image, token); -} - -/** - * @param {CstNode} node - * @param {string} value - * @return an array containing processed trailing comments and separators - */ -function getNodeTrailingComments(node: CstNode, value: Doc) { - return getTrailingComments(node, value, node.location); -} - -function getTrailingComments( - nodeOrToken: CstElement, - value: Doc, - location: CstNodeLocation | IToken -) { - const arr: Doc = []; - let previousEndLine = location.endLine; - if (nodeOrToken.trailingComments !== undefined) { - nodeOrToken.trailingComments.forEach((comment, idx) => { - let separator = ""; - - if (comment.startLine !== previousEndLine) { - arr.push(hardline); - } else if (!isEmptyDoc(value) && idx === 0) { - separator = " "; - } - - if (comment.tokenType.name === "LineComment") { - arr.push(lineSuffix([separator, formatComment(comment), breakParent])); - } else { - arr.push(formatComment(comment)); - } - - previousEndLine = comment.endLine; - }); - } - - return arr; -} - -function isJavaDoc(comment: IToken, lines: string[]) { - let isJavaDoc = true; - if (comment.tokenType.name === "TraditionalComment" && lines.length > 1) { - for (let i = 1; i < lines.length; i++) { - if (lines[i].trim().charAt(0) !== "*") { - isJavaDoc = false; - break; - } - } - } else { - isJavaDoc = false; - } - - return isJavaDoc; -} - -function formatJavaDoc(lines: string[]) { - const res: Doc[] = [lines[0].trim()]; - - for (let i = 1; i < lines.length; i++) { - res.push(hardline); - res.push(" " + lines[i].trim()); - } - - return res; -} - -function formatComment(comment: IToken) { - const res: Doc[] = []; - const lines = comment.image.split("\n"); - - if (isJavaDoc(comment, lines)) { - return formatJavaDoc(lines); - } - - lines.forEach(line => { - res.push(line); - res.push(literalline); - }); - res.pop(); - return res; -} - -export function processComments( - docs: (CstElement | undefined)[] | CstElement | undefined -) { - if (!Array.isArray(docs)) { - if (isCstElementOrUndefinedIToken(docs)) { - return printTokenWithComments(docs); - } - return docs; - } - return docs.map(elt => { - if (isCstElementOrUndefinedIToken(elt)) { - return printTokenWithComments(elt); - } - return elt; - }); -} diff --git a/packages/prettier-plugin-java/src/printers/comments/handle-comments.ts b/packages/prettier-plugin-java/src/printers/comments/handle-comments.ts deleted file mode 100644 index faf023141..000000000 --- a/packages/prettier-plugin-java/src/printers/comments/handle-comments.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { hasLeadingComments, hasTrailingComments } from "./comments-utils.js"; -import { - BinaryExpressionCtx, - CstNode, - IToken, - UnaryExpressionCstNode -} from "java-parser"; - -export function handleCommentsBinaryExpression(ctx: BinaryExpressionCtx) { - moveOperatorLeadingCommentsToNextExpression(ctx); - moveExpressionTrailingCommentsToNextOperator(ctx); -} - -export function handleCommentsParameters( - lBrace: IToken, - parameters: CstNode[] | IToken[], - rBrace: IToken -) { - const lBraceTrailingComments = lBrace.trailingComments; - const firstParameter = parameters.at(0); - if (lBraceTrailingComments && firstParameter) { - delete lBrace.trailingComments; - firstParameter.leadingComments = [ - ...lBraceTrailingComments, - ...(firstParameter.leadingComments ?? []) - ]; - } - const lastParameter = parameters.at(-1); - const rBraceLeadingComments = rBrace.leadingComments; - if (rBraceLeadingComments) { - delete rBrace.leadingComments; - if (lastParameter) { - lastParameter.trailingComments = [ - ...(lastParameter.trailingComments ?? []), - ...rBraceLeadingComments - ]; - } else { - lBrace.trailingComments = [ - ...(lBrace.trailingComments ?? []), - ...rBraceLeadingComments - ]; - } - } -} - -function moveOperatorLeadingCommentsToNextExpression(ctx: BinaryExpressionCtx) { - let unaryExpressionIndex = 1; - ctx.BinaryOperator?.forEach(binaryOperator => { - if (hasLeadingComments(binaryOperator)) { - while ( - ctx.unaryExpression[unaryExpressionIndex].location.startOffset < - binaryOperator.endOffset - ) { - unaryExpressionIndex++; - } - - // Adapt the position of the operator and its leading comments - const shiftUp = - binaryOperator.leadingComments[0].startLine - - 1 - - binaryOperator.startLine; - - if ( - binaryOperator.startLine !== - ctx.unaryExpression[unaryExpressionIndex].location.startLine - ) { - binaryOperator.leadingComments.forEach(comment => { - comment.startLine += 1; - comment.endLine += 1; - }); - } - binaryOperator.startLine += shiftUp; - binaryOperator.endLine += shiftUp; - - // Move binaryOperator's leading comments to the following - // unaryExpression - ctx.unaryExpression[unaryExpressionIndex].leadingComments = - ctx.unaryExpression[unaryExpressionIndex].leadingComments || []; - ctx.unaryExpression[unaryExpressionIndex].leadingComments!.unshift( - ...binaryOperator.leadingComments - ); - delete (binaryOperator as IToken).leadingComments; - } - }); -} - -function moveExpressionTrailingCommentsToNextOperator( - ctx: BinaryExpressionCtx -) { - const binaryOperators = ctx.BinaryOperator; - let binaryOperatorIndex = 0; - if (binaryOperators?.length) { - ctx.unaryExpression.forEach(unaryExpression => { - if (hasTrailingComments(unaryExpression)) { - while ( - binaryOperatorIndex < binaryOperators.length - 1 && - unaryExpression.location.endOffset && - binaryOperators[binaryOperatorIndex].startOffset < - unaryExpression.location.endOffset - ) { - binaryOperatorIndex++; - } - const binaryOperator = binaryOperators[binaryOperatorIndex]; - - // Adapt the position of the expression and its trailing comments - const shiftUp = - unaryExpression.trailingComments[0].startLine - - 1 - - unaryExpression.location.startLine!; - - if (unaryExpression.location.startLine !== binaryOperator.startLine) { - unaryExpression.trailingComments.forEach(comment => { - comment.startLine += 1; - comment.endLine += 1; - }); - } - unaryExpression.location.startLine! += shiftUp; - if (unaryExpression.location.endLine !== undefined) { - unaryExpression.location.endLine += shiftUp; - } - - // Move unaryExpression's trailing comments to the following - // binaryOperator - binaryOperator.trailingComments = binaryOperator.trailingComments ?? []; - binaryOperator.trailingComments.unshift( - ...unaryExpression.trailingComments - ); - delete (unaryExpression as UnaryExpressionCstNode).trailingComments; - } - }); - } -} diff --git a/packages/prettier-plugin-java/src/printers/expressions.ts b/packages/prettier-plugin-java/src/printers/expressions.ts index d07e93412..230e35aa2 100644 --- a/packages/prettier-plugin-java/src/printers/expressions.ts +++ b/packages/prettier-plugin-java/src/printers/expressions.ts @@ -1,901 +1,784 @@ -import { - ArgumentListCtx, - ArrayAccessSuffixCtx, - ArrayCreationExpressionCtx, - ArrayCreationExpressionWithoutInitializerSuffixCtx, - ArrayCreationWithInitializerSuffixCtx, - BinaryExpressionCtx, - CastExpressionCtx, - ClassLiteralSuffixCtx, - ClassOrInterfaceTypeToInstantiateCtx, - ComponentPatternCtx, - ComponentPatternListCtx, - ConciseLambdaParameterCtx, - ConciseLambdaParameterListCtx, - ConditionalExpressionCtx, - DiamondCtx, - DimExprCtx, - DimExprsCtx, - EmbeddedExpressionCtx, - ExpressionCstNode, - ExpressionCtx, +import type { FqnOrRefTypeCtx, - FqnOrRefTypePartCommonCtx, - FqnOrRefTypePartFirstCtx, - FqnOrRefTypePartRestCtx, - GuardCtx, - IToken, - LambdaBodyCtx, - LambdaExpressionCtx, - LambdaParameterCtx, - LambdaParameterListCtx, - LambdaParametersCtx, - LambdaParametersWithBracesCtx, - LambdaParameterTypeCtx, - MatchAllPatternCtx, - MethodInvocationSuffixCtx, - MethodReferenceSuffixCtx, - NewExpressionCtx, - NormalLambdaParameterListCtx, - ParenthesisExpressionCtx, - PatternCtx, - PrimaryCtx, - PrimaryPrefixCtx, - PrimarySuffixCtx, - PrimitiveCastExpressionCtx, - RecordPatternCtx, - ReferenceTypeCastExpressionCtx, - RegularLambdaParameterCtx, - StringTemplateCtx, - TemplateArgumentCtx, - TemplateCtx, - TextBlockTemplateCtx, - TypeArgumentsOrDiamondCtx, - TypePatternCtx, - UnaryExpressionCtx, - UnaryExpressionNotPlusMinusCtx, - UnqualifiedClassInstanceCreationExpressionCtx -} from "java-parser/api"; - -import forEach from "lodash/forEach.js"; -import type { Doc } from "prettier"; + StringTemplateCstNode, + TextBlockTemplateCstNode +} from "java-parser"; +import type { AstPath, Doc } from "prettier"; import { builders, utils } from "prettier/doc"; -import { BaseCstPrettierPrinter } from "../base-cst-printer.js"; -import { isAnnotationCstNode } from "../types/utils.js"; -import { printArgumentListWithBraces } from "../utils/index.js"; -import { hasLeadingComments } from "./comments/comments-utils.js"; -import { printTokenWithComments } from "./comments/format-comments.js"; -import { - handleCommentsBinaryExpression, - handleCommentsParameters -} from "./comments/handle-comments.js"; -import { - concat, - dedent, - group, - indent, - indentIfBreak -} from "./prettier-builder.js"; +import type { JavaComment } from "../comments.js"; import { - binary, - findDeepElementInPartsArray, - getOperators, - isExplicitLambdaParameter, - isUniqueMethodInvocation, - putIntoBraces, - rejectAndConcat, - rejectAndJoin, - rejectAndJoinSeps, - sortAnnotationIdentifier, - sortNodes, - sortTokens -} from "./printer-utils.js"; + call, + definedKeys, + each, + findBaseIndent, + flatMap, + indentInParentheses, + isBinaryExpression, + isNonTerminal, + isTerminal, + map, + onlyDefinedKey, + printDanglingComments, + printList, + printName, + printSingle, + type IterProperties, + type JavaNodePrinters, + type JavaNonTerminal, + type JavaPrintFn +} from "./helpers.js"; const { breakParent, conditionalGroup, + group, + hardline, ifBreak, - label, + indent, + indentIfBreak, + join, line, lineSuffixBoundary, softline } = builders; const { removeLines, willBreak } = utils; -export class ExpressionsPrettierVisitor extends BaseCstPrettierPrinter { - expression(ctx: ExpressionCtx, params: any) { - const expression = this.visitSingle(ctx, params); - return params?.hug && expression.label !== undefined - ? label(expression.label, expression) - : expression; - } - - lambdaExpression(ctx: LambdaExpressionCtx, params?: { hug?: boolean }) { - const lambdaParameters = group(this.visit(ctx.lambdaParameters)); - const lambdaBody = this.visit(ctx.lambdaBody); - - const isLambdaBodyABlock = ctx.lambdaBody[0].children.block !== undefined; - const suffix = [ - " ", - ctx.Arrow[0], - ...(isLambdaBodyABlock - ? [" ", lambdaBody] - : [group([indent([line, lambdaBody]), params?.hug ? softline : ""])]) - ]; - if (params?.hug) { - return willBreak(lambdaParameters) - ? label({ huggable: false }, concat([lambdaParameters, ...suffix])) - : concat([removeLines(lambdaParameters), ...suffix]); - } - return concat([lambdaParameters, ...suffix]); - } - - lambdaParameters(ctx: LambdaParametersCtx) { - if (ctx.lambdaParametersWithBraces) { - return this.visitSingle(ctx); +export default { + expression: printSingle, + + lambdaExpression(path, print, _, args = {}) { + const hug = (args as { hug?: boolean }).hug ?? false; + const parameters = call(path, print, "lambdaParameters"); + const expression = [hug ? removeLines(parameters) : parameters, " ->"]; + const lambdaExpression = + path.node.children.lambdaBody[0].children.expression; + const body = call(path, print, "lambdaBody"); + if (lambdaExpression) { + const suffix = indent([line, body]); + expression.push(group(hug ? [suffix, softline] : suffix)); + } else { + expression.push(" ", body); } - - return printTokenWithComments(this.getSingle(ctx) as IToken); - } - - lambdaParametersWithBraces(ctx: LambdaParametersWithBracesCtx) { - const lambdaParameters = - ctx.lambdaParameterList?.[0].children.normalLambdaParameterList?.[0] - .children.normalLambdaParameter ?? - ctx.lambdaParameterList?.[0].children.conciseLambdaParameterList?.[0] - .children.conciseLambdaParameter ?? - []; - handleCommentsParameters(ctx.LBrace[0], lambdaParameters, ctx.RBrace[0]); - const lambdaParameterList = this.visit(ctx.lambdaParameterList); - - if (findDeepElementInPartsArray(lambdaParameterList, ",")) { - return concat([ - ctx.LBrace[0], - indent([softline, lambdaParameterList]), - softline, - ctx.RBrace[0] - ]); + return expression; + }, + + lambdaParameters(path, print, options) { + const parameters = printSingle(path, print); + return !path.node.children.lambdaParametersWithBraces && + options.arrowParens === "always" + ? ["(", parameters, ")"] + : parameters; + }, + + lambdaParametersWithBraces(path, print, options) { + const { lambdaParameterList } = path.node.children; + if (!lambdaParameterList) { + return "()"; } - - // removing braces when only no comments attached - if ( - (ctx.LBrace && - ctx.RBrace && - (!lambdaParameterList || isExplicitLambdaParameter(ctx))) || - ctx.LBrace[0].leadingComments || - ctx.LBrace[0].trailingComments || - ctx.RBrace[0].leadingComments || - ctx.RBrace[0].trailingComments - ) { - return rejectAndConcat([ - ctx.LBrace[0], - lambdaParameterList, - ctx.RBrace[0] - ]); + const { conciseLambdaParameterList, normalLambdaParameterList } = + lambdaParameterList[0].children; + const parameterCount = (conciseLambdaParameterList?.[0].children + .conciseLambdaParameter ?? + normalLambdaParameterList?.[0].children.normalLambdaParameter)!.length; + const parameters = call(path, print, "lambdaParameterList"); + if (parameterCount > 1) { + return indentInParentheses(parameters); } + return conciseLambdaParameterList && options.arrowParens === "avoid" + ? parameters + : ["(", parameters, ")"]; + }, - return lambdaParameterList; - } - - lambdaParameterList(ctx: LambdaParameterListCtx) { - return this.visitSingle(ctx); - } - - conciseLambdaParameterList(ctx: ConciseLambdaParameterListCtx) { - const conciseLambdaParameters = this.mapVisit(ctx.conciseLambdaParameter); - const commas = ctx.Comma?.map(comma => concat([comma, line])); - return rejectAndJoinSeps(commas, conciseLambdaParameters); - } + lambdaParameterList: printSingle, - normalLambdaParameterList(ctx: NormalLambdaParameterListCtx) { - const normalLambdaParameter = this.mapVisit(ctx.normalLambdaParameter); - const commas = ctx.Comma?.map(comma => concat([comma, line])); - return rejectAndJoinSeps(commas, normalLambdaParameter); - } + conciseLambdaParameterList(path, print) { + return printList(path, print, "conciseLambdaParameter"); + }, - normalLambdaParameter(ctx: LambdaParameterCtx) { - return this.visitSingle(ctx); - } + normalLambdaParameterList(path, print) { + return printList(path, print, "normalLambdaParameter"); + }, - regularLambdaParameter(ctx: RegularLambdaParameterCtx) { - const variableModifier = this.mapVisit(ctx.variableModifier); - const lambdaParameterType = this.visit(ctx.lambdaParameterType); - const variableDeclaratorId = this.visit(ctx.variableDeclaratorId); + normalLambdaParameter: printSingle, - return rejectAndJoin(" ", [ - rejectAndJoin(" ", variableModifier), - lambdaParameterType, - variableDeclaratorId + regularLambdaParameter(path, print) { + return join(" ", [ + ...map(path, print, "variableModifier"), + call(path, print, "lambdaParameterType"), + call(path, print, "variableDeclaratorId") ]); - } + }, - lambdaParameterType(ctx: LambdaParameterTypeCtx) { - if (ctx.unannType) { - return this.visitSingle(ctx); - } - return printTokenWithComments(this.getSingle(ctx) as IToken); - } + lambdaParameterType: printSingle, + conciseLambdaParameter: printSingle, + lambdaBody: printSingle, - conciseLambdaParameter(ctx: ConciseLambdaParameterCtx) { - return printTokenWithComments(this.getSingle(ctx) as IToken); - } - - lambdaBody(ctx: LambdaBodyCtx) { - return this.visitSingle(ctx); - } - - conditionalExpression(ctx: ConditionalExpressionCtx, params: any) { - const binaryExpression = this.visit(ctx.binaryExpression, params); - if (ctx.QuestionMark) { - const expression1 = this.visit(ctx.expression![0]); - const expression2 = this.visit(ctx.expression![1]); - - return indent( - group( - rejectAndConcat([ - rejectAndJoin(line, [ + conditionalExpression(path, print) { + const binaryExpression = call(path, print, "binaryExpression"); + return path.node.children.QuestionMark + ? group( + indent( + join(line, [ binaryExpression, - rejectAndJoin(" ", [ctx.QuestionMark[0], expression1]), - rejectAndJoin(" ", [ctx.Colon![0], expression2]) + ["? ", call(path, print, "expression", 0)], + [": ", call(path, print, "expression", 1)] ]) - ]) + ) ) - ); - } - return binaryExpression; - } - - binaryExpression( - ctx: BinaryExpressionCtx, - params?: { addParenthesisToWrapStatement?: boolean } - ) { - handleCommentsBinaryExpression(ctx); - - const sortedNodes = sortNodes([ - ctx.pattern, - ctx.referenceType, - ctx.expression, - ctx.unaryExpression - ]); - const tokens = sortTokens(getOperators(ctx)); - const hasTokens = tokens.length > 0; - - const nodeParams = sortedNodes.length === 1 ? params : undefined; - const nodes: Doc[] = []; - for (let i = 0; i < sortedNodes.length; i++) { - const node = this.visit(sortedNodes[i], nodeParams); - const isAssignment = - tokens[i]?.tokenType.CATEGORIES?.find( - ({ name }) => name === "AssignmentOperator" - ) !== undefined; - if (!isAssignment) { - nodes.push(node); - continue; - } - const [equals] = tokens.splice(i, 1); - const expression = sortedNodes[++i] as ExpressionCstNode; - const nextNode = this.visit(expression); - const conditionalExpression = - expression.children.conditionalExpression?.[0].children; - const binaryExpression = - conditionalExpression?.binaryExpression?.[0].children; - const breakAfterOperator = - conditionalExpression?.QuestionMark === undefined && - binaryExpression !== undefined && - getOperators(binaryExpression).length > 0; - if (breakAfterOperator) { - nodes.push( - concat([node, " ", equals, group(indent([line, nextNode]))]) - ); - continue; - } - const groupId = Symbol("assignment"); - nodes.push( - concat([ - node, - " ", - equals, - indent(group(line, { id: groupId })), - indentIfBreak(nextNode, { groupId }) - ]) - ); - } - - const content = binary(nodes, tokens, true); - - return hasTokens && params?.addParenthesisToWrapStatement - ? group( - concat([ - ifBreak("("), - indent(concat([softline, content])), - softline, - ifBreak(")") - ]) - ) - : content; - } - - unaryExpression( - ctx: UnaryExpressionCtx, - params?: { addParenthesisToWrapStatement?: boolean } - ) { - const unaryPrefixOperator = ctx.UnaryPrefixOperator - ? ctx.UnaryPrefixOperator - : []; - const primary = this.visit(ctx.primary, params); - const unarySuffixOperator = ctx.UnarySuffixOperator - ? ctx.UnarySuffixOperator - : []; - return rejectAndConcat([ - rejectAndConcat(unaryPrefixOperator), - primary, - rejectAndConcat(unarySuffixOperator) - ]); - } - - unaryExpressionNotPlusMinus(ctx: UnaryExpressionNotPlusMinusCtx) { - const unaryPrefixOperatorNotPlusMinus = ctx.UnaryPrefixOperatorNotPlusMinus // changed when moved to TS - ? rejectAndJoin(" ", ctx.UnaryPrefixOperatorNotPlusMinus) // changed when moved to TS - : ""; - - const primary = this.visit(ctx.primary); - const unarySuffixOperator = ctx.UnarySuffixOperator // changed when moved to TS - ? rejectAndJoin(" ", ctx.UnarySuffixOperator) // changed when moved to TS - : ""; - - return rejectAndJoin(" ", [ - unaryPrefixOperatorNotPlusMinus, - primary, - unarySuffixOperator - ]); - } - - primary( - ctx: PrimaryCtx, - params?: { addParenthesisToWrapStatement?: boolean } - ) { - const countMethodInvocation = isUniqueMethodInvocation(ctx.primarySuffix); - const newExpression = - ctx.primaryPrefix[0].children.newExpression?.[0].children; - const isBreakableNewExpression = - countMethodInvocation <= 1 && - this.isBreakableNewExpression(newExpression); - const firstMethodInvocation = ctx.primarySuffix - ?.map(suffix => suffix.children.methodInvocationSuffix?.[0].children) - .find(methodInvocationSuffix => methodInvocationSuffix); - - const fqnOrRefType = - ctx.primaryPrefix[0].children.fqnOrRefType?.[0].children; - const hasFqnRefPart = fqnOrRefType?.fqnOrRefTypePartRest !== undefined; - const isCapitalizedIdentifier = this.isCapitalizedIdentifier(fqnOrRefType); - - const shouldBreakBeforeFirstMethodInvocation = - countMethodInvocation > 1 && hasFqnRefPart && !isCapitalizedIdentifier; - - const shouldBreakBeforeMethodInvocations = - shouldBreakBeforeFirstMethodInvocation || - countMethodInvocation > 2 || - (countMethodInvocation > 1 && !!newExpression) || - !firstMethodInvocation?.argumentList; - - const primaryPrefix = this.visit(ctx.primaryPrefix, { - ...params, - shouldBreakBeforeFirstMethodInvocation - }); - - const suffixes = this.getPrimarySuffixes( - ctx, - newExpression, - isBreakableNewExpression, - shouldBreakBeforeMethodInvocations + : binaryExpression; + }, + + binaryExpression(path, print, options) { + const { children } = path.node; + const operands = flatMap( + path, + print, + definedKeys(children, [ + "expression", + "pattern", + "referenceType", + "unaryExpression" + ]) ); - - if (!newExpression && countMethodInvocation === 1) { - return group( - rejectAndConcat([ - primaryPrefix, - suffixes[0], - indent(rejectAndConcat(suffixes.slice(1))) - ]) - ); - } - - const methodInvocation = - ctx.primarySuffix?.[0].children.methodInvocationSuffix; - const isMethodInvocationWithArguments = - methodInvocation?.[0].children.argumentList !== undefined; - const isUnqualifiedMethodInvocation = - methodInvocation !== undefined && !fqnOrRefType?.Dot; - return group( - rejectAndConcat([ - primaryPrefix, - isCapitalizedIdentifier || isUnqualifiedMethodInvocation - ? suffixes.shift() - : "", - !isBreakableNewExpression && - (shouldBreakBeforeMethodInvocations || !isMethodInvocationWithArguments) - ? indent(concat(suffixes)) - : concat(suffixes) + const operators = flatMap( + path, + operatorPath => { + const { node } = operatorPath; + let image: string; + if (isTerminal(node)) { + image = node.image; + } else if (node.children.Less) { + image = "<<"; + } else { + image = node.children.Greater!.length === 2 ? ">>" : ">>>"; + } + return { image, doc: print(operatorPath) }; + }, + definedKeys(children, [ + "AssignmentOperator", + "BinaryOperator", + "Instanceof", + "shiftOperator" ]) ); - } - - primaryPrefix(ctx: PrimaryPrefixCtx, params: any) { - if (ctx.This || ctx.Void) { - return printTokenWithComments(this.getSingle(ctx) as IToken); - } - - return this.visitSingle(ctx, params); - } + const hasNonAssignmentOperators = + (operators.length > 0 && !children.AssignmentOperator) || + (children.expression !== undefined && + isBinaryExpression(children.expression[0])); + return binary(operands, operators, { + hasNonAssignmentOperators, + isRoot: true, + operatorPosition: options.experimentalOperatorPosition + }); + }, - primarySuffix(ctx: PrimarySuffixCtx, params: any) { - if (ctx.Dot) { - if (ctx.This) { - return rejectAndConcat([ctx.Dot[0], ctx.This[0]]); - } else if (ctx.Identifier) { - const typeArguments = this.visit(ctx.typeArguments); - return rejectAndConcat([ctx.Dot[0], typeArguments, ctx.Identifier[0]]); - } + unaryExpression(path, print) { + return [ + ...map(path, print, "UnaryPrefixOperator"), + call(path, print, "primary"), + ...map(path, print, "UnarySuffixOperator") + ]; + }, - const suffix = this.visit( - ctx.unqualifiedClassInstanceCreationExpression ?? ctx.templateArgument - ); - return rejectAndConcat([ctx.Dot[0], suffix]); + unaryExpressionNotPlusMinus(path, print) { + const { children } = path.node; + const expression: Doc[] = []; + if (children.UnaryPrefixOperatorNotPlusMinus) { + expression.push(...map(path, print, "UnaryPrefixOperatorNotPlusMinus")); } - return this.visitSingle(ctx, params); - } - - fqnOrRefType(ctx: FqnOrRefTypeCtx, params: any) { - const fqnOrRefTypePartFirst = this.visit(ctx.fqnOrRefTypePartFirst); - const fqnOrRefTypePartRest = this.mapVisit(ctx.fqnOrRefTypePartRest); - const dims = this.visit(ctx.dims); - const dots = ctx.Dot - ? ctx.Dot.map(dot => { - if (hasLeadingComments(dot)) { - return concat([softline, dot]); - } - return dot; - }) - : []; - - if ( - params?.shouldBreakBeforeFirstMethodInvocation === true && - ctx.Dot !== undefined - ) { - dots[dots.length - 1] = concat([softline, ctx.Dot[ctx.Dot.length - 1]]); + expression.push(call(path, print, "primary")); + if (children.UnarySuffixOperator) { + expression.push(...map(path, print, "UnarySuffixOperator")); } + return join(" ", expression); + }, - return indent( - rejectAndConcat([ - rejectAndJoinSeps(dots, [ - fqnOrRefTypePartFirst, - ...fqnOrRefTypePartRest - ]), - dims - ]) - ); - } - - fqnOrRefTypePartFirst(ctx: FqnOrRefTypePartFirstCtx) { - const annotation = this.mapVisit(ctx.annotation); - const fqnOrRefTypeCommon = this.visit(ctx.fqnOrRefTypePartCommon); - - return rejectAndJoin(" ", [ - rejectAndJoin(" ", annotation), - fqnOrRefTypeCommon - ]); - } - - fqnOrRefTypePartRest(ctx: FqnOrRefTypePartRestCtx) { - const annotation = this.mapVisit(ctx.annotation); - const fqnOrRefTypeCommon = this.visit(ctx.fqnOrRefTypePartCommon); - - const typeArguments = this.visit(ctx.typeArguments); - - return rejectAndJoin(" ", [ - rejectAndJoin(" ", annotation), - rejectAndConcat([typeArguments, fqnOrRefTypeCommon]) - ]); - } - - fqnOrRefTypePartCommon(ctx: FqnOrRefTypePartCommonCtx) { - let keyWord = null; - if (ctx.Identifier) { - keyWord = ctx.Identifier[0]; - } else { - keyWord = ctx.Super![0]; + primary(path, print) { + const { children } = path.node; + if (!children.primarySuffix) { + return call(path, print, "primaryPrefix"); } - - const typeArguments = this.visit(ctx.typeArguments); - - return rejectAndConcat([keyWord, typeArguments]); - } - - parenthesisExpression( - ctx: ParenthesisExpressionCtx, - params?: { addParenthesisToWrapStatement?: boolean } - ) { - const expression = this.visit(ctx.expression); - const separator = params?.addParenthesisToWrapStatement ? softline : ""; - return putIntoBraces(expression, separator, ctx.LBrace[0], ctx.RBrace[0]); - } - - castExpression(ctx: CastExpressionCtx) { - return this.visitSingle(ctx); - } - - primitiveCastExpression(ctx: PrimitiveCastExpressionCtx) { - const primitiveType = this.visit(ctx.primitiveType); - const unaryExpression = this.visit(ctx.unaryExpression); - return rejectAndJoin(" ", [ - rejectAndConcat([ctx.LBrace[0], primitiveType, ctx.RBrace[0]]), - unaryExpression - ]); - } - - referenceTypeCastExpression(ctx: ReferenceTypeCastExpressionCtx) { - const referenceType = this.visit(ctx.referenceType); - const hasAdditionalBounds = ctx.additionalBound !== undefined; - const additionalBounds = rejectAndJoin( - line, - this.mapVisit(ctx.additionalBound) + const methodInvocations = children.primarySuffix + .filter(({ children }) => children.methodInvocationSuffix) + .map(({ children }) => children.methodInvocationSuffix![0].children); + const hasLambdaMethodParameter = methodInvocations.some( + ({ argumentList }) => + argumentList?.[0].children.expression.some( + ({ children }) => children.lambdaExpression + ) ); - - const expression = ctx.lambdaExpression - ? this.visit(ctx.lambdaExpression) - : this.visit(ctx.unaryExpressionNotPlusMinus); - - return rejectAndJoin(" ", [ - putIntoBraces( - rejectAndJoin(line, [referenceType, additionalBounds]), - hasAdditionalBounds ? softline : "", - ctx.LBrace[0], - ctx.RBrace[0] - ), - expression - ]); - } - - newExpression(ctx: NewExpressionCtx) { - return this.visitSingle(ctx); - } - - unqualifiedClassInstanceCreationExpression( - ctx: UnqualifiedClassInstanceCreationExpressionCtx - ) { - const typeArguments = this.visit(ctx.typeArguments); - const classOrInterfaceTypeToInstantiate = this.visit( - ctx.classOrInterfaceTypeToInstantiate + const prefixIsCallExpression = + children.primaryPrefix[0].children.newExpression; + const callExpressionCount = + methodInvocations.length + + (prefixIsCallExpression ? 1 : 0) + + children.primarySuffix.filter( + ({ children }) => children.unqualifiedClassInstanceCreationExpression + ).length; + const fqnOrRefType = + children.primaryPrefix[0].children.fqnOrRefType?.[0].children; + const prefixIsMethodInvocation = + fqnOrRefType?.fqnOrRefTypePartRest !== undefined && + children.primarySuffix?.[0].children.methodInvocationSuffix !== undefined; + const prefixIsStaticMethodInvocation = + prefixIsMethodInvocation && isCapitalizedIdentifier(fqnOrRefType); + const prefixIsInstanceMethodInvocation = + prefixIsMethodInvocation && !prefixIsStaticMethodInvocation; + const mustBreakForCallExpressions = + methodInvocations.length > 2 && hasLambdaMethodParameter; + const separator = mustBreakForCallExpressions ? hardline : softline; + const prefix = [ + call( + path, + prefixPath => + print(prefixPath, { + lastSeparator: + prefixIsStaticMethodInvocation || + (prefixIsInstanceMethodInvocation && callExpressionCount === 1) + ? "" + : separator + }), + "primaryPrefix" + ) + ]; + const canBreakForCallExpressions = + callExpressionCount > 2 || + (callExpressionCount === 2 && prefixIsInstanceMethodInvocation) || + willBreak(prefix); + const suffixes: Doc[] = []; + each( + path, + suffixPath => { + const { node, previous } = suffixPath; + const suffix = print(suffixPath); + if (node.children.Dot) { + if ( + (canBreakForCallExpressions && + ((!previous && prefixIsCallExpression) || + previous?.children.methodInvocationSuffix || + previous?.children + .unqualifiedClassInstanceCreationExpression)) || + (!node.children.templateArgument && willBreak(suffix)) + ) { + suffixes.push(separator); + } + suffixes.push(suffix); + } else if (previous) { + suffixes.push(suffix); + } else { + prefix.push( + prefixIsInstanceMethodInvocation && callExpressionCount >= 2 + ? indent(suffix) + : suffix + ); + } + }, + "primarySuffix" ); - - let content = printArgumentListWithBraces.call( - this, - ctx.argumentList, - ctx.RBrace[0], - ctx.LBrace[0] + return group( + canBreakForCallExpressions || willBreak(suffixes) + ? [prefix, indent(suffixes)] + : [prefix, ...suffixes] ); + }, - const classBody = this.visit(ctx.classBody); - - return rejectAndJoin(" ", [ - ctx.New[0], - rejectAndConcat([ - typeArguments, - classOrInterfaceTypeToInstantiate, - content - ]), - classBody - ]); - } - - classOrInterfaceTypeToInstantiate(ctx: ClassOrInterfaceTypeToInstantiateCtx) { - const tokens = sortAnnotationIdentifier(ctx.annotation, ctx.Identifier); + primaryPrefix: printSingle, - const segments: any[] = []; - let currentSegment: any[] = []; - - forEach(tokens, token => { - if (isAnnotationCstNode(token)) { - currentSegment.push(this.visit([token])); - } else { - currentSegment.push(token); - segments.push(rejectAndJoin(" ", currentSegment)); - currentSegment = []; + primarySuffix(path, print) { + const { children } = path.node; + if (!children.Dot) { + return printSingle(path, print); + } + const suffix: Doc[] = ["."]; + if (children.This) { + suffix.push("this"); + } else if (children.Identifier) { + if (children.typeArguments) { + suffix.push(call(path, print, "typeArguments")); } - }); - - const typeArgumentsOrDiamond = this.visit(ctx.typeArgumentsOrDiamond); - const dots = ctx.Dot ? ctx.Dot : []; - return rejectAndConcat([ - rejectAndJoinSeps(dots, segments), - typeArgumentsOrDiamond + suffix.push(call(path, print, "Identifier")); + } else { + const suffixKey = onlyDefinedKey(children, [ + "templateArgument", + "unqualifiedClassInstanceCreationExpression" + ]); + suffix.push(call(path, print, suffixKey)); + } + return suffix; + }, + + fqnOrRefType(path, print, _, args) { + const lastSeparator = (args as { lastSeparator?: Doc }).lastSeparator ?? ""; + const fqnOrRefType = [ + call(path, print, "fqnOrRefTypePartFirst"), + ...map( + path, + partPath => { + const part = print(partPath); + return partPath.isLast + ? [willBreak(part) ? hardline : lastSeparator, part] + : part; + }, + "fqnOrRefTypePartRest" + ) + ]; + fqnOrRefType.push(indent(fqnOrRefType.pop()!)); + return path.node.children.dims + ? [fqnOrRefType, call(path, print, "dims")] + : fqnOrRefType; + }, + + fqnOrRefTypePartFirst(path, print) { + return join(" ", [ + ...map(path, print, "annotation"), + call(path, print, "fqnOrRefTypePartCommon") ]); - } - - typeArgumentsOrDiamond(ctx: TypeArgumentsOrDiamondCtx) { - return this.visitSingle(ctx); - } + }, + + fqnOrRefTypePartRest(path, print) { + const common = call(path, print, "fqnOrRefTypePartCommon"); + const type = path.node.children.typeArguments + ? [call(path, print, "typeArguments"), common] + : common; + return [".", ...join(" ", [...map(path, print, "annotation"), type])]; + }, + + fqnOrRefTypePartCommon(path, print) { + const { children } = path.node; + const keywordKey = onlyDefinedKey(children, ["Identifier", "Super"]); + const keyword = call(path, print, keywordKey); + return children.typeArguments + ? [keyword, call(path, print, "typeArguments")] + : keyword; + }, + + parenthesisExpression(path, print) { + const expression = call(path, print, "expression"); + const ancestorName = (path.getNode(14) as JavaNonTerminal | null)?.name; + const binaryExpression = path.getNode(8) as JavaNonTerminal | null; + return ancestorName && + ["guard", "returnStatement"].includes(ancestorName) && + binaryExpression && + binaryExpression.name === "binaryExpression" && + Object.keys(binaryExpression.children).length === 1 + ? indentInParentheses(expression) + : ["(", indent(expression), ")"]; + }, + + castExpression: printSingle, + + primitiveCastExpression(path, print) { + return [ + "(", + call(path, print, "primitiveType"), + ") ", + call(path, print, "unaryExpression") + ]; + }, + + referenceTypeCastExpression(path, print) { + const { children } = path.node; + const type = call(path, print, "referenceType"); + const cast = children.additionalBound + ? indentInParentheses( + join(line, [type, ...map(path, print, "additionalBound")]) + ) + : ["(", type, ")"]; + const expressionKey = onlyDefinedKey(children, [ + "lambdaExpression", + "unaryExpressionNotPlusMinus" + ]); + return [cast, " ", call(path, print, expressionKey)]; + }, - diamond(ctx: DiamondCtx) { - return concat([ctx.Less[0], ctx.Greater[0]]); - } + newExpression: printSingle, - methodInvocationSuffix(ctx: MethodInvocationSuffixCtx) { - return printArgumentListWithBraces.call( - this, - ctx.argumentList, - ctx.RBrace[0], - ctx.LBrace[0] - ); - } - - argumentList(ctx: ArgumentListCtx) { - const headArgs = this.mapVisit(ctx.expression.slice(0, -1)).map( - (expression, index) => concat([expression, ctx.Comma![index], line]) + unqualifiedClassInstanceCreationExpression(path, print) { + const { children } = path.node; + const expression: Doc[] = ["new "]; + if (children.typeArguments) { + expression.push(call(path, print, "typeArguments")); + } + expression.push( + call(path, print, "classOrInterfaceTypeToInstantiate"), + children.argumentList + ? group(["(", call(path, print, "argumentList"), ")"]) + : "()" ); - const lastExpression = ctx.expression.at(-1); - const lastArg = this.visit(lastExpression); - - if (this.isArgumentListHuggable(ctx)) { - const huggedLastArg = this.visit(lastExpression, { hug: true }); - const lastArgNotHuggable = - typeof huggedLastArg === "object" && - !Array.isArray(huggedLastArg) && - huggedLastArg.type === "label" && - huggedLastArg.label?.huggable === false; - - if (lastArgNotHuggable || headArgs.some(willBreak)) { - return group([indent([line, ...headArgs, lastArg]), line], { - shouldBreak: true - }); - } - const hugged = [...headArgs, group(huggedLastArg, { shouldBreak: true })]; - const expanded = group([indent([line, ...headArgs, lastArg]), line], { - shouldBreak: true - }); - - return willBreak(huggedLastArg) - ? [breakParent, conditionalGroup([hugged, expanded])] - : conditionalGroup([[...headArgs, huggedLastArg], hugged, expanded]); + if (children.classBody) { + expression.push(" ", call(path, print, "classBody")); } - - return group([indent([softline, ...headArgs, lastArg]), softline]); - } - - arrayCreationExpression(ctx: ArrayCreationExpressionCtx) { - const type = ctx.primitiveType - ? this.visit(ctx.primitiveType) - : this.visit(ctx.classOrInterfaceType); - const suffix = ctx.arrayCreationExpressionWithoutInitializerSuffix - ? this.visit(ctx.arrayCreationExpressionWithoutInitializerSuffix) - : this.visit(ctx.arrayCreationWithInitializerSuffix); - - return rejectAndConcat([concat([ctx.New[0], " "]), type, suffix]); - } - - arrayCreationExpressionWithoutInitializerSuffix( - ctx: ArrayCreationExpressionWithoutInitializerSuffixCtx - ) { - const dimExprs = this.visit(ctx.dimExprs); - const dims = this.visit(ctx.dims); - return rejectAndConcat([dimExprs, dims]); - } - - arrayCreationWithInitializerSuffix( - ctx: ArrayCreationWithInitializerSuffixCtx - ) { - const dims = this.visit(ctx.dims); - const arrayInitializer = this.visit(ctx.arrayInitializer); - - return rejectAndJoin(" ", [dims, arrayInitializer]); - } - - dimExprs(ctx: DimExprsCtx) { - const dimExpr = this.mapVisit(ctx.dimExpr); - return rejectAndConcat(dimExpr); - } - - dimExpr(ctx: DimExprCtx) { - const annotations = this.mapVisit(ctx.annotation); - const expression = this.visit(ctx.expression); - - return rejectAndJoin(" ", [ - rejectAndJoin(" ", annotations), - rejectAndConcat([ctx.LSquare[0], expression, ctx.RSquare[0]]) - ]); - } - - classLiteralSuffix(ctx: ClassLiteralSuffixCtx) { - const squares = []; - if (ctx.LSquare) { - for (let i = 0; i < ctx.LSquare.length; i++) { - squares.push(concat([ctx.LSquare[i], ctx.RSquare![i]])); - } + return expression; + }, + + classOrInterfaceTypeToInstantiate(path, print) { + const { children } = path.node; + const type = children.annotation + ? flatMap( + path, + childPath => [ + print(childPath), + isNonTerminal(childPath.node) ? " " : "." + ], + ["annotation", "Identifier"] + ) + : printName(path, print); + if (children.typeArgumentsOrDiamond) { + type.push(call(path, print, "typeArgumentsOrDiamond")); } - return rejectAndConcat([...squares, ctx.Dot[0], ctx.Class[0]]); - } - - arrayAccessSuffix(ctx: ArrayAccessSuffixCtx) { - const expression = this.visit(ctx.expression); - return rejectAndConcat([ctx.LSquare[0], expression, ctx.RSquare[0]]); - } - - methodReferenceSuffix(ctx: MethodReferenceSuffixCtx) { - const typeArguments = this.visit(ctx.typeArguments); - const identifierOrNew = ctx.New ? ctx.New[0] : ctx.Identifier![0]; - return rejectAndConcat([ctx.ColonColon[0], typeArguments, identifierOrNew]); - } - - templateArgument(ctx: TemplateArgumentCtx) { - return ctx.template - ? this.visit(ctx.template) - : printTokenWithComments((ctx.StringLiteral ?? ctx.TextBlock)![0]); - } - - template(ctx: TemplateCtx) { - return this.visitSingle(ctx); - } - - stringTemplate(ctx: StringTemplateCtx) { - const embeddedExpressions = this.mapVisit(ctx.embeddedExpression).flatMap( - expression => - group([softline, expression, lineSuffixBoundary, dedent(softline)]) + return type; + }, + + typeArgumentsOrDiamond: printSingle, + + diamond() { + return "<>"; + }, + + methodInvocationSuffix(path, print) { + return path.node.children.argumentList + ? group(["(", call(path, print, "argumentList"), ")"]) + : indentInParentheses(printDanglingComments(path), { shouldBreak: true }); + }, + + argumentList(path, print) { + const expressions = path.node.children.expression; + const lastExpression = expressions.at( + -1 + ) as (typeof expressions)[number] & { comments?: JavaComment[] }; + const lastExpressionLambdaBodyExpression = + lastExpression.children.lambdaExpression?.[0].children.lambdaBody[0] + .children.expression?.[0].children; + const lastExpressionLambdaBodyTernaryExpression = + lastExpressionLambdaBodyExpression?.conditionalExpression?.[0].children; + const isHuggable = + !lastExpression.comments && + (!lastExpressionLambdaBodyExpression || + lastExpressionLambdaBodyTernaryExpression?.QuestionMark !== undefined || + lastExpressionLambdaBodyTernaryExpression?.binaryExpression?.[0] + .children.unaryExpression.length === 1) && + expressions.findIndex(({ children }) => children.lambdaExpression) === + expressions.length - 1; + const args = map(path, print, "expression"); + const allArgsExpandable = [ + indent([softline, ...join([",", line], args)]), + softline + ]; + if (!isHuggable || willBreak((args.at(-1) as Doc[])[0])) { + return allArgsExpandable; + } + const headArgs = args.slice(0, -1); + const huggedLastArg = call( + path, + argPath => print(argPath, { hug: true }), + "expression", + args.length - 1 ); - return concat([ - ctx.StringTemplateBegin[0], - rejectAndJoinSeps(ctx.StringTemplateMid, embeddedExpressions), - ctx.StringTemplateEnd[0] + const lastArgExpanded = join(", ", [ + ...headArgs, + group(huggedLastArg, { shouldBreak: true }) ]); - } - - textBlockTemplate(ctx: TextBlockTemplateCtx) { - const embeddedExpressions = this.mapVisit(ctx.embeddedExpression).flatMap( - expression => - group([softline, expression, lineSuffixBoundary, dedent(softline)]) - ); - return concat([ - ctx.TextBlockTemplateBegin[0], - rejectAndJoinSeps(ctx.TextBlockTemplateMid, embeddedExpressions), - ctx.TextBlockTemplateEnd[0] + if (willBreak(huggedLastArg)) { + return [ + breakParent, + conditionalGroup([lastArgExpanded, allArgsExpandable]) + ]; + } + return conditionalGroup([ + join(", ", [...headArgs, huggedLastArg]), + lastArgExpanded, + allArgsExpandable ]); - } - - embeddedExpression(ctx: EmbeddedExpressionCtx) { - return this.visit(ctx.expression); - } - - pattern(ctx: PatternCtx) { - return this.visitSingle(ctx); - } + }, - typePattern(ctx: TypePatternCtx) { - return this.visitSingle(ctx); - } - - recordPattern(ctx: RecordPatternCtx) { - const componentPatterns = - ctx.componentPatternList?.[0].children.componentPattern ?? []; - handleCommentsParameters(ctx.LBrace[0], componentPatterns, ctx.RBrace[0]); - const referenceType = this.visit(ctx.referenceType); - const componentPatternList = this.visit(ctx.componentPatternList); - return concat([ - referenceType, - putIntoBraces( - componentPatternList, - softline, - ctx.LBrace[0], - ctx.RBrace[0] - ) + arrayCreationExpression(path, print) { + const { children } = path.node; + const typeKey = onlyDefinedKey(children, [ + "classOrInterfaceType", + "primitiveType" ]); - } + const suffixKey = onlyDefinedKey(children, [ + "arrayCreationExpressionWithoutInitializerSuffix", + "arrayCreationWithInitializerSuffix" + ]); + return ["new ", call(path, print, typeKey), call(path, print, suffixKey)]; + }, - componentPatternList(ctx: ComponentPatternListCtx) { - const componentPatterns = this.mapVisit(ctx.componentPattern); - const commas = ctx.Comma?.map(elt => concat([elt, line])) ?? []; - return rejectAndJoinSeps(commas, componentPatterns); - } + arrayCreationExpressionWithoutInitializerSuffix(path, print) { + const expressions = call(path, print, "dimExprs"); + return path.node.children.dims + ? [expressions, call(path, print, "dims")] + : expressions; + }, - componentPattern(ctx: ComponentPatternCtx) { - return this.visitSingle(ctx); - } + arrayCreationWithInitializerSuffix(path, print) { + return [ + call(path, print, "dims"), + " ", + call(path, print, "arrayInitializer") + ]; + }, - matchAllPattern(ctx: MatchAllPatternCtx) { - return printTokenWithComments(ctx.Underscore[0]); - } + dimExprs(path, print) { + return map(path, print, "dimExpr"); + }, - guard(ctx: GuardCtx) { - const expression = this.visit(ctx.expression, { - addParenthesisToWrapStatement: true - }); + dimExpr(path, print) { + return join(" ", [ + ...map(path, print, "annotation"), + ["[", call(path, print, "expression"), "]"] + ]); + }, - return concat([ctx.When[0], " ", expression]); - } + classLiteralSuffix(path, print) { + const lSquares = map(path, print, "LSquare"); + const rSquares = map(path, print, "RSquare"); + return [ + ...lSquares.flatMap((lSquare, index) => [lSquare, rSquares[index]]), + ".class" + ]; + }, - isRefTypeInMethodRef() { - return "isRefTypeInMethodRef"; - } + arrayAccessSuffix(path, print) { + return ["[", call(path, print, "expression"), "]"]; + }, - private isArgumentListHuggable(argumentList: ArgumentListCtx) { - const expressions = argumentList.expression; - const lastArgument = expressions.at(-1); - const lastArgumentLambdaBodyExpression = - lastArgument?.children.lambdaExpression?.[0].children.lambdaBody[0] - .children.expression?.[0].children; - const lastArgumentLambdaBodyTernaryExpression = - lastArgumentLambdaBodyExpression?.conditionalExpression?.[0].children; - return ( - !lastArgument?.leadingComments && - !lastArgument?.trailingComments && - (!lastArgumentLambdaBodyExpression || - lastArgumentLambdaBodyTernaryExpression?.QuestionMark !== undefined || - lastArgumentLambdaBodyTernaryExpression?.binaryExpression[0].children - .unaryExpression.length === 1) && - expressions.findIndex(({ children }) => children.lambdaExpression) === - expressions.length - 1 + methodReferenceSuffix(path, print) { + const { children } = path.node; + const reference: Doc[] = ["::"]; + if (children.typeArguments) { + reference.push(call(path, print, "typeArguments")); + } + reference.push( + call(path, print, onlyDefinedKey(children, ["Identifier", "New"])) ); - } - - private isBreakableNewExpression(newExpression?: NewExpressionCtx) { - const arrayCreationExpression = - newExpression?.arrayCreationExpression?.[0].children; - const classInstanceCreationExpression = - newExpression?.unqualifiedClassInstanceCreationExpression?.[0].children; + return reference; + }, + + templateArgument: printSingle, + template: printSingle, + + stringTemplate(path, print) { + return printTemplate( + path, + print, + "StringTemplateBegin", + "StringTemplateMid", + "StringTemplateEnd" + ); + }, + + textBlockTemplate(path, print) { + return printTemplate( + path, + print, + "TextBlockTemplateBegin", + "TextBlockTemplateMid", + "TextBlockTemplateEnd" + ); + }, + + embeddedExpression: printSingle, + pattern: printSingle, + typePattern: printSingle, + + recordPattern(path, print) { + const patterns = path.node.children.componentPatternList + ? indentInParentheses(call(path, print, "componentPatternList")) + : "()"; + return [call(path, print, "referenceType"), patterns]; + }, + + componentPatternList(path, print) { + return printList(path, print, "componentPattern"); + }, + + componentPattern: printSingle, + matchAllPattern: printSingle, + + guard(path, print) { + const expression = call(path, print, "expression"); + const hasParentheses = + path.node.children.expression[0].children.conditionalExpression?.[0] + .children.binaryExpression[0].children.unaryExpression[0].children + .primary[0].children.primaryPrefix[0].children.parenthesisExpression !== + undefined; return [ - arrayCreationExpression?.classOrInterfaceType?.[0].children.classType[0] - .children.typeArguments, - arrayCreationExpression?.arrayCreationWithInitializerSuffix?.[0].children - .arrayInitializer[0].children.variableInitializerList, - classInstanceCreationExpression?.classOrInterfaceTypeToInstantiate[0] - .children.typeArgumentsOrDiamond?.[0].children.typeArguments, - classInstanceCreationExpression?.argumentList - ].some(breakablePart => breakablePart !== undefined); - } - - private isCapitalizedIdentifier(fqnOrRefType?: FqnOrRefTypeCtx) { - const fqnOrRefTypeParts = [ - fqnOrRefType?.fqnOrRefTypePartFirst[0], - ...(fqnOrRefType?.fqnOrRefTypePartRest ?? []) + "when ", + hasParentheses + ? expression + : group([ + ifBreak("("), + indent([softline, expression]), + softline, + ifBreak(")") + ]) ]; - const nextToLastIdentifier = - fqnOrRefTypeParts[fqnOrRefTypeParts.length - 2]?.children - .fqnOrRefTypePartCommon[0].children.Identifier?.[0].image; - return ( - !!nextToLastIdentifier && - /^\p{Uppercase_Letter}/u.test(nextToLastIdentifier) - ); } - - private getPrimarySuffixes( - ctx: PrimaryCtx, - newExpression: NewExpressionCtx | undefined, - isBreakableNewExpression: boolean, - shouldBreakBeforeMethodInvocations: NewExpressionCtx | boolean - ) { - if (ctx.primarySuffix === undefined) { - return []; - } - - const suffixes = []; - - if ( - newExpression && - !isBreakableNewExpression && - ctx.primarySuffix[0].children.Dot !== undefined - ) { - suffixes.push(softline); - } - suffixes.push(this.visit(ctx.primarySuffix[0])); - - for (let i = 1; i < ctx.primarySuffix.length; i++) { +} satisfies Partial; + +function binary( + operands: Doc[], + operators: { image: string; doc: Doc }[], + { + hasNonAssignmentOperators = false, + isRoot = false, + operatorPosition + }: { + hasNonAssignmentOperators?: boolean; + isRoot?: boolean; + operatorPosition: "end" | "start"; + } +): Doc { + let levelOperator: string | undefined; + let levelPrecedence: number | undefined; + let level: Doc[] = []; + while (operators.length) { + const nextOperator = operators[0].image; + const nextPrecedence = getOperatorPrecedence(nextOperator); + + if (levelPrecedence === undefined || nextPrecedence === levelPrecedence) { + const { image: operator, doc: operatorDoc } = operators.shift()!; + level.push(operands.shift()!); if ( - shouldBreakBeforeMethodInvocations && - ctx.primarySuffix[i].children.Dot !== undefined && - ctx.primarySuffix[i - 1].children.methodInvocationSuffix !== undefined + levelOperator !== undefined && + needsParentheses(levelOperator, operator) ) { - suffixes.push(softline); + level = [["(", group(indent(level)), ")"]]; + } + const parts = [" ", operatorDoc, line]; + if (operatorPosition === "start" && !isAssignmentOperator(operator)) { + parts.reverse(); + } + level.push(parts); + levelOperator = operator; + levelPrecedence = nextPrecedence; + } else if (nextPrecedence < levelPrecedence) { + if (!isRoot) { + break; } - suffixes.push(this.visit(ctx.primarySuffix[i])); + level.push(operands.shift()!); + const content = group(indent(level)); + operands.unshift( + levelOperator !== undefined && + needsParentheses(levelOperator, nextOperator) + ? ["(", content, ")"] + : content + ); + level = []; + levelOperator = undefined; + levelPrecedence = undefined; + } else { + const content = binary(operands, operators, { operatorPosition }); + operands.unshift( + levelOperator !== undefined && + needsParentheses(nextOperator, levelOperator) + ? ["(", indent(content), ")"] + : content + ); } - return suffixes; } + level.push(operands.shift()!); + if (!levelOperator || !isAssignmentOperator(levelOperator)) { + return group(level); + } + if (!isRoot || hasNonAssignmentOperators) { + return group(indent(level)); + } + const groupId = Symbol("assignment"); + return group([ + level[0], + group(indent(level[1]), { id: groupId }), + indentIfBreak(level[2], { groupId }) + ]); +} + +const precedencesByOperator = new Map( + [ + ["||"], + ["&&"], + ["|"], + ["^"], + ["&"], + ["==", "!="], + ["<", ">", "<=", ">=", "instanceof"], + ["<<", ">>", ">>>"], + ["+", "-"], + ["*", "/", "%"] + ].flatMap((operators, index) => operators.map(operator => [operator, index])) +); +function getOperatorPrecedence(operator: string) { + return precedencesByOperator.get(operator) ?? -1; +} + +function needsParentheses(operator: string, parentOperator: string) { + return ( + (operator === "&&" && parentOperator === "||") || + (["|", "^", "&", "<<", ">>", ">>>"].includes(parentOperator) && + getOperatorPrecedence(operator) > + getOperatorPrecedence(parentOperator)) || + [operator, parentOperator].every(o => ["==", "!="].includes(o)) || + [operator, parentOperator].every(o => ["<<", ">>", ">>>"].includes(o)) || + (operator === "*" && parentOperator === "/") || + (operator === "/" && parentOperator === "*") || + (operator === "%" && ["+", "-", "*", "/"].includes(parentOperator)) || + (["*", "/"].includes(operator) && parentOperator === "%") + ); +} + +const assignmentOperators = new Set([ + "=", + "*=", + "/=", + "%=", + "+=", + "-=", + "<<=", + ">>=", + ">>>=", + "&=", + "^=", + "|=" +]); +function isAssignmentOperator(operator: string) { + return assignmentOperators.has(operator); +} + +function isCapitalizedIdentifier(fqnOrRefType: FqnOrRefTypeCtx) { + const nextToLastIdentifier = [ + fqnOrRefType.fqnOrRefTypePartFirst[0], + ...(fqnOrRefType.fqnOrRefTypePartRest ?? []) + ].at(-2)?.children.fqnOrRefTypePartCommon[0].children.Identifier?.[0].image; + return /^\p{Uppercase_Letter}/u.test(nextToLastIdentifier ?? ""); +} + +function printTemplate< + T extends StringTemplateCstNode | TextBlockTemplateCstNode, + C extends Exclude, "embeddedExpression"> +>(path: AstPath, print: JavaPrintFn, beginKey: C, midKey: C, endKey: C) { + const { children } = path.node; + const begin = call(path, ({ node }) => node.image, beginKey); + const mids = map(path, ({ node }) => node.image, midKey); + const end = call(path, ({ node }) => node.image, endKey); + const lines = [begin, ...mids, end].join("").split("\n").slice(1); + const baseIndent = findBaseIndent(lines); + const prefix = "\n" + " ".repeat(baseIndent); + const parts = [begin, ...mids, end].map(image => + join(hardline, image.split(prefix)) + ); + return [ + parts[0], + ...map( + path, + (expressionPath, index) => { + const expression = group([ + indent([softline, print(expressionPath), lineSuffixBoundary]), + softline + ]); + return index === 0 ? expression : [parts[index], expression]; + }, + "embeddedExpression" as IterProperties + ), + parts.at(-1)! + ]; } diff --git a/packages/prettier-plugin-java/src/printers/helpers.ts b/packages/prettier-plugin-java/src/printers/helpers.ts new file mode 100644 index 000000000..8677cb152 --- /dev/null +++ b/packages/prettier-plugin-java/src/printers/helpers.ts @@ -0,0 +1,420 @@ +import type { + AnnotationCstNode, + ClassPermitsCstNode, + ClassTypeCtx, + CstElement, + CstNode, + ExpressionCstNode, + InterfacePermitsCstNode, + IToken, + StatementCstNode +} from "java-parser"; +import type { AstPath, Doc, ParserOptions } from "prettier"; +import { builders } from "prettier/doc"; +import type { JavaComment } from "../comments.js"; +import parser from "../parser.js"; + +const { group, hardline, ifBreak, indent, join, line, softline } = builders; + +export function onlyDefinedKey< + T extends Record, + K extends Key & string +>(obj: T, options?: K[]) { + const keys = definedKeys(obj, options); + if (keys.length === 1) { + return keys[0]; + } + throw new Error( + keys.length > 1 + ? `More than one defined key found: ${keys}` + : "No defined keys found" + ); +} + +export function definedKeys< + T extends Record, + K extends Key & string +>(obj: T, options?: K[]) { + return (options ?? (Object.keys(obj) as K[])).filter( + key => obj[key] !== undefined + ); +} + +const indexByModifier = [ + "public", + "protected", + "private", + "abstract", + "default", + "static", + "final", + "transient", + "volatile", + "synchronized", + "native", + "sealed", + "non-sealed", + "strictfp" +].reduce((map, name, index) => map.set(name, index), new Map()); + +export function printWithModifiers< + T extends CstNode, + P extends IterProperties +>( + path: AstPath, + print: JavaPrintFn, + modifierChild: P, + contents: Doc, + noTypeAnnotations = false +) { + const declarationAnnotations: Doc[] = []; + const otherModifiers: string[] = []; + const typeAnnotations: Doc[] = []; + each( + path, + modifierPath => { + const { children } = modifierPath.node as ModifierNode; + const modifier = print(modifierPath); + if (children.annotation) { + (otherModifiers.length ? typeAnnotations : declarationAnnotations).push( + modifier + ); + } else { + otherModifiers.push(modifier as string); + declarationAnnotations.push(...typeAnnotations); + typeAnnotations.length = 0; + } + }, + modifierChild + ); + if (noTypeAnnotations) { + declarationAnnotations.push(...typeAnnotations); + typeAnnotations.length = 0; + } + otherModifiers.sort( + (a, b) => indexByModifier.get(a)! - indexByModifier.get(b)! + ); + return join(hardline, [ + ...declarationAnnotations, + join(" ", [...otherModifiers, ...typeAnnotations, contents]) + ]); +} + +export function hasDeclarationAnnotations(modifiers: ModifierNode[]) { + let hasAnnotation = false; + let hasNonAnnotation = false; + for (const modifier of modifiers) { + if (modifier.children.annotation) { + hasAnnotation = true; + } else if (hasAnnotation) { + return true; + } else { + hasNonAnnotation = true; + } + } + return hasAnnotation && !hasNonAnnotation; +} + +export function call< + T extends CstNode, + U, + P extends IterProperties +>( + path: AstPath, + callback: MapCallback, P>, U>, + child: P, + index = 0 +) { + return path.map(callback, "children", child)[index]; +} + +export function each< + T extends CstNode, + P extends IterProperties +>( + path: AstPath, + callback: MapCallback, P>, void>, + child: P +) { + if (path.node.children[child]) { + path.each(callback, "children", child); + } +} + +export function map< + T extends CstNode, + U, + P extends IterProperties +>( + path: AstPath, + callback: MapCallback, P>, U>, + child: P +) { + return path.node.children[child] ? path.map(callback, "children", child) : []; +} + +export function flatMap< + T extends CstNode, + U, + P extends IterProperties +>( + path: AstPath, + callback: MapCallback, P>, U>, + children: P[] +) { + return children + .flatMap(child => + map(path, callback, child).map((doc, index) => { + const node = path.node.children[child][index]; + return { + doc, + startOffset: parser.locStart(node) + }; + }) + ) + .sort((a, b) => a.startOffset - b.startOffset) + .map(({ doc }) => doc); +} + +export function printSingle( + path: AstPath, + print: JavaPrintFn, + _?: JavaParserOptions, + args?: unknown +) { + return call( + path, + childPath => print(childPath, args), + onlyDefinedKey(path.node.children) + ); +} + +export function lineStartWithComments(node: JavaNonTerminal) { + const { comments, location } = node; + return comments + ? Math.min(location.startLine, comments[0].startLine) + : location.startLine; +} + +export function lineEndWithComments(node: JavaNonTerminal) { + const { comments, location } = node; + return comments + ? Math.max(location.endLine, comments.at(-1)!.endLine) + : location.endLine; +} + +export function printDanglingComments(path: AstPath) { + if (!path.node.comments) { + return []; + } + const comments: Doc[] = []; + path.each(commentPath => { + const comment = commentPath.node; + if (comment.leading || comment.trailing) { + return; + } + comment.printed = true; + comments.push(printComment(comment)); + }, "comments"); + return join(hardline, comments); +} + +export function printComment(node: JavaTerminal) { + const { image } = node; + const lines = image.split("\n").map(line => line.trim()); + return lines.length > 1 && + lines[0].startsWith("/*") && + lines.slice(1).every(line => line.startsWith("*")) && + lines.at(-1)!.endsWith("*/") + ? join( + hardline, + lines.map((line, index) => (index === 0 ? line : ` ${line}`)) + ) + : image; +} + +export function hasLeadingComments(node: JavaNode) { + return node.comments?.some(({ leading }) => leading); +} + +export function indentInParentheses( + contents: Doc, + opts?: { shouldBreak?: boolean } +) { + return !Array.isArray(contents) || contents.length + ? group(["(", indent([softline, contents]), softline, ")"], opts) + : "()"; +} + +export function printArrayInitializer< + T extends JavaNonTerminal, + P extends IterProperties +>(path: AstPath, print: JavaPrintFn, options: JavaParserOptions, child: P) { + const list: Doc[] = []; + if (child && child in path.node.children) { + list.push(call(path, print, child)); + if (options.trailingComma !== "none") { + list.push(ifBreak(",")); + } + } + list.push(...printDanglingComments(path)); + return list.length ? group(["{", indent([line, ...list]), line, "}"]) : "{}"; +} + +export function printBlock(path: AstPath, contents: Doc[]) { + if (!contents.length) { + const danglingComments = printDanglingComments(path); + return danglingComments.length + ? ["{", indent([hardline, ...danglingComments]), hardline, "}"] + : "{}"; + } + return group([ + "{", + indent([hardline, ...join(hardline, contents)]), + hardline, + "}" + ]); +} + +export function printName( + path: AstPath, + print: JavaPrintFn +) { + return join(".", map(path, print, "Identifier")); +} + +export function printList< + T extends JavaNonTerminal, + P extends IterProperties +>(path: AstPath, print: JavaPrintFn, child: P) { + return join([",", line], map(path, print, child)); +} + +export function printClassPermits( + path: AstPath, + print: JavaPrintFn +) { + return group([ + "permits", + indent([line, group(printList(path, print, "typeName"))]) + ]); +} + +export function printClassType( + path: AstPath, + print: JavaPrintFn +) { + return flatMap( + path, + childPath => { + const { node, isLast } = childPath; + const child = [print(childPath)]; + if (isTerminal(node)) { + if (!isLast) { + child.push("."); + } + } else if (node.name === "annotation") { + child.push(" "); + } + return child; + }, + definedKeys(path.node.children, [ + "annotation", + "Identifier", + "typeArguments" + ]) + ); +} + +export function isBinaryExpression(expression: ExpressionCstNode) { + const conditionalExpression = + expression.children.conditionalExpression?.[0].children; + if (!conditionalExpression) { + return false; + } + const isTernary = conditionalExpression.QuestionMark !== undefined; + if (isTernary) { + return false; + } + const hasNonAssignmentOperators = Object.values( + conditionalExpression.binaryExpression[0].children + ).some( + child => + isTerminal(child[0]) && + !child[0].tokenType.CATEGORIES?.some( + category => category.name === "AssignmentOperator" + ) + ); + return hasNonAssignmentOperators; +} + +export function findBaseIndent(lines: string[]) { + return lines.length + ? Math.min( + ...lines.map(line => line.search(/\S/)).filter(indent => indent >= 0) + ) + : 0; +} + +export function isEmptyStatement(statement: StatementCstNode) { + return ( + statement.children.statementWithoutTrailingSubstatement?.[0].children + .emptyStatement !== undefined + ); +} + +export function isNonTerminal(node: CstElement): node is JavaNonTerminal { + return !isTerminal(node); +} + +export function isTerminal(node: CstElement): node is IToken { + return "tokenType" in node; +} + +export type JavaNode = CstElement & { comments?: JavaComment[] }; +export type JavaNonTerminal = Exclude; +export type JavaTerminal = Exclude; +export type JavaNodePrinters = { + [T in JavaNonTerminal["name"]]: JavaNodePrinter; +}; +export type JavaNodePrinter = ( + path: AstPath>, + print: JavaPrintFn, + options: JavaParserOptions, + args?: unknown +) => Doc; +export type JavaPrintFn = (path: AstPath, args?: unknown) => Doc; +export type JavaParserOptions = ParserOptions & { + entrypoint?: string; +}; +export type IterProperties = T extends any[] + ? IndexProperties + : ArrayProperties; + +type Key = T extends T ? keyof T : never; +type ModifierNode = JavaNonTerminal & { + children: { annotation?: AnnotationCstNode[] }; +}; +type IsTuple = T extends [] + ? true + : T extends [infer First, ...infer Remain] + ? IsTuple + : false; +type IndexProperties = + IsTuple extends true ? Exclude["length"], T["length"]> : number; +type ArrayProperties = { + [K in keyof T]: NonNullable extends readonly any[] ? K : never; +}[keyof T]; +type ArrayElement = T extends Array ? E : never; +type MapCallback = ( + path: AstPath>, + index: number, + value: any +) => U; +type IndexValue = T extends any[] + ? P extends number + ? T[P] + : never + : P extends keyof T + ? T[P] + : never; diff --git a/packages/prettier-plugin-java/src/printers/index.ts b/packages/prettier-plugin-java/src/printers/index.ts new file mode 100644 index 000000000..e19181ab3 --- /dev/null +++ b/packages/prettier-plugin-java/src/printers/index.ts @@ -0,0 +1,28 @@ +import arrays from "./arrays.js"; +import blocksAndStatements from "./blocks-and-statements.js"; +import classes from "./classes.js"; +import expressions from "./expressions.js"; +import type { JavaNodePrinter, JavaNodePrinters } from "./helpers.js"; +import interfaces from "./interfaces.js"; +import lexicalStructure from "./lexical-structure.js"; +import names from "./names.js"; +import packagesAndModules from "./packages-and-modules.js"; +import typesValuesAndVariables from "./types-values-and-variables.js"; + +const printersByNodeType: JavaNodePrinters = { + ...arrays, + ...blocksAndStatements, + ...classes, + ...expressions, + ...interfaces, + ...lexicalStructure, + ...names, + ...packagesAndModules, + ...typesValuesAndVariables +}; + +export function printerForNodeType( + type: T +): JavaNodePrinter { + return printersByNodeType[type]; +} diff --git a/packages/prettier-plugin-java/src/printers/interfaces.ts b/packages/prettier-plugin-java/src/printers/interfaces.ts index 4bcd9bace..5c60ea63c 100644 --- a/packages/prettier-plugin-java/src/printers/interfaces.ts +++ b/packages/prettier-plugin-java/src/printers/interfaces.ts @@ -1,352 +1,231 @@ -import { concat, group, indent } from "./prettier-builder.js"; -import { printTokenWithComments } from "./comments/format-comments.js"; -import { handleCommentsParameters } from "./comments/handle-comments.js"; -import { - displaySemicolon, - getInterfaceBodyDeclarationsSeparator, - isStatementEmptyStatement, - printArrayList, - putIntoBraces, - rejectAndConcat, - rejectAndJoin, - rejectAndJoinSeps, - sortModifiers -} from "./printer-utils.js"; - +import type { Doc } from "prettier"; import { builders } from "prettier/doc"; -import { BaseCstPrettierPrinter } from "../base-cst-printer.js"; import { - AnnotationCtx, - AnnotationInterfaceBodyCtx, - AnnotationInterfaceDeclarationCtx, - AnnotationInterfaceElementDeclarationCtx, - AnnotationInterfaceElementModifierCtx, - AnnotationInterfaceMemberDeclarationCtx, - ConstantDeclarationCtx, - ConstantModifierCtx, - DefaultValueCtx, - ElementValueArrayInitializerCtx, - ElementValueCtx, - ElementValueListCtx, - ElementValuePairCtx, - ElementValuePairListCtx, - InterfaceBodyCtx, - InterfaceDeclarationCtx, - InterfaceExtendsCtx, - InterfaceMemberDeclarationCtx, - InterfaceMethodDeclarationCtx, - InterfaceMethodModifierCtx, - InterfaceModifierCtx, - InterfacePermitsCtx, - IToken, - NormalInterfaceDeclarationCtx -} from "java-parser"; -import Doc = builders.Doc; - -const { line, softline, hardline } = builders; - -export class InterfacesPrettierVisitor extends BaseCstPrettierPrinter { - interfaceDeclaration(ctx: InterfaceDeclarationCtx) { - const modifiers = sortModifiers(ctx.interfaceModifier); - const firstAnnotations = this.mapVisit(modifiers[0]); - const otherModifiers = this.mapVisit(modifiers[1]); - - const declaration = ctx.normalInterfaceDeclaration - ? this.visit(ctx.normalInterfaceDeclaration) - : this.visit(ctx.annotationInterfaceDeclaration); - - return rejectAndJoin(hardline, [ - rejectAndJoin(hardline, firstAnnotations), - rejectAndJoin(" ", [rejectAndJoin(" ", otherModifiers), declaration]) + call, + each, + hasDeclarationAnnotations, + indentInParentheses, + lineEndWithComments, + lineStartWithComments, + onlyDefinedKey, + printArrayInitializer, + printBlock, + printClassPermits, + printList, + printSingle, + printWithModifiers, + type JavaNodePrinters +} from "./helpers.js"; + +const { group, hardline, indent, join, line } = builders; + +export default { + interfaceDeclaration(path, print) { + const declarationKey = onlyDefinedKey(path.node.children, [ + "annotationInterfaceDeclaration", + "normalInterfaceDeclaration" ]); - } - - normalInterfaceDeclaration(ctx: NormalInterfaceDeclarationCtx) { - const typeIdentifier = this.visit(ctx.typeIdentifier); - const typeParameters = this.visit(ctx.typeParameters); - const interfaceExtends = this.visit(ctx.interfaceExtends); - const optionalInterfacePermits = this.visit(ctx.interfacePermits); - const interfaceBody = this.visit(ctx.interfaceBody); - - let interfaceExtendsPart: Doc = ""; - if (interfaceExtends) { - interfaceExtendsPart = indent( - rejectAndConcat([softline, interfaceExtends]) - ); + return printWithModifiers( + path, + print, + "interfaceModifier", + call(path, print, declarationKey), + true + ); + }, + + normalInterfaceDeclaration(path, print) { + const { interfaceExtends, interfacePermits, typeParameters } = + path.node.children; + const header = ["interface ", call(path, print, "typeIdentifier")]; + if (typeParameters) { + header.push(call(path, print, "typeParameters")); } - - let interfacePermits: Doc = ""; - if (optionalInterfacePermits) { - interfacePermits = indent( - rejectAndConcat([softline, optionalInterfacePermits]) - ); + if (interfaceExtends) { + header.push(indent([line, call(path, print, "interfaceExtends")])); } - - return rejectAndJoin(" ", [ - group( - rejectAndJoin(" ", [ - ctx.Interface[0], - concat([typeIdentifier, typeParameters]), - interfaceExtendsPart, - interfacePermits - ]) - ), - interfaceBody - ]); - } - - interfaceModifier(ctx: InterfaceModifierCtx) { - if (ctx.annotation) { - return this.visitSingle(ctx); + if (interfacePermits) { + header.push(indent([line, call(path, print, "interfacePermits")])); } - return printTokenWithComments(this.getSingle(ctx) as IToken); - } + return [group(header), " ", call(path, print, "interfaceBody")]; + }, - interfaceExtends(ctx: InterfaceExtendsCtx) { - const interfaceTypeList = this.visit(ctx.interfaceTypeList); + interfaceModifier: printSingle, - return group( - rejectAndConcat([ - ctx.Extends[0], - indent(rejectAndConcat([line, interfaceTypeList])) - ]) + interfaceExtends(path, print) { + return group([ + "extends", + indent([line, call(path, print, "interfaceTypeList")]) + ]); + }, + + interfacePermits: printClassPermits, + + interfaceBody(path, print) { + const declarations: Doc[] = []; + let previousRequiresPadding = false; + each( + path, + declarationPath => { + const declaration = print(declarationPath); + if (declaration === "") { + return; + } + const { node, previous } = declarationPath; + const constantDeclaration = + node.children.constantDeclaration?.[0].children; + const methodDeclaration = + node.children.interfaceMethodDeclaration?.[0].children; + const currentRequiresPadding = + (!constantDeclaration && !methodDeclaration) || + methodDeclaration?.methodBody[0].children.block !== undefined || + hasDeclarationAnnotations( + constantDeclaration?.constantModifier ?? + methodDeclaration?.interfaceMethodModifier ?? + [] + ); + const blankLine = + declarations.length > 0 && + (previousRequiresPadding || + currentRequiresPadding || + lineStartWithComments(node) > lineEndWithComments(previous!) + 1); + declarations.push(blankLine ? [hardline, declaration] : declaration); + previousRequiresPadding = currentRequiresPadding; + }, + "interfaceMemberDeclaration" ); - } - - interfacePermits(ctx: InterfacePermitsCtx) { - return this.classPermits(ctx); - } - - interfaceBody(ctx: InterfaceBodyCtx) { - let joinedInterfaceMemberDeclaration: Doc = ""; - - if (ctx.interfaceMemberDeclaration !== undefined) { - const interfaceMemberDeclaration = this.mapVisit( - ctx.interfaceMemberDeclaration - ); - - const separators = getInterfaceBodyDeclarationsSeparator( - ctx.interfaceMemberDeclaration - ); - - joinedInterfaceMemberDeclaration = rejectAndJoinSeps( - separators, - interfaceMemberDeclaration - ); - } - return putIntoBraces( - joinedInterfaceMemberDeclaration, - hardline, - ctx.LCurly[0], - ctx.RCurly[0] + return printBlock(path, declarations); + }, + + interfaceMemberDeclaration(path, print) { + const { children } = path.node; + return children.Semicolon + ? "" + : call(path, print, onlyDefinedKey(children)); + }, + + constantDeclaration(path, print) { + const declaration = [ + call(path, print, "unannType"), + " ", + call(path, print, "variableDeclaratorList"), + ";" + ]; + return printWithModifiers(path, print, "constantModifier", declaration); + }, + + constantModifier: printSingle, + + interfaceMethodDeclaration(path, print) { + const declaration = [ + call(path, print, "methodHeader"), + path.node.children.methodBody[0].children.Semicolon ? "" : " ", + call(path, print, "methodBody") + ]; + return printWithModifiers( + path, + print, + "interfaceMethodModifier", + declaration ); - } - - interfaceMemberDeclaration(ctx: InterfaceMemberDeclarationCtx) { - if (ctx.Semicolon) { - return displaySemicolon(ctx.Semicolon[0]); - } - return this.visitSingle(ctx); - } + }, - constantDeclaration(ctx: ConstantDeclarationCtx) { - const modifiers = sortModifiers(ctx.constantModifier); - const firstAnnotations = this.mapVisit(modifiers[0]); - const otherModifiers = this.mapVisit(modifiers[1]); + interfaceMethodModifier: printSingle, - const unannType = this.visit(ctx.unannType); - const variableDeclaratorList = this.visit(ctx.variableDeclaratorList); - - return rejectAndJoin(hardline, [ - rejectAndJoin(hardline, firstAnnotations), - rejectAndJoin(" ", [ - rejectAndJoin(" ", otherModifiers), - unannType, - rejectAndConcat([variableDeclaratorList, ctx.Semicolon[0]]) - ]) + annotationInterfaceDeclaration(path, print) { + return join(" ", [ + "@interface", + call(path, print, "typeIdentifier"), + call(path, print, "annotationInterfaceBody") ]); - } - - constantModifier(ctx: ConstantModifierCtx) { - if (ctx.annotation) { - return this.visitSingle(ctx); + }, + + annotationInterfaceBody(path, print) { + const declarations: Doc[] = []; + each( + path, + declarationPath => { + const declaration = print(declarationPath); + if (declaration === "") { + return; + } + declarations.push( + declarationPath.isFirst ? declaration : [hardline, declaration] + ); + }, + "annotationInterfaceMemberDeclaration" + ); + return printBlock(path, declarations); + }, + + annotationInterfaceMemberDeclaration(path, print) { + const { children } = path.node; + return children.Semicolon + ? "" + : call(path, print, onlyDefinedKey(children)); + }, + + annotationInterfaceElementDeclaration(path, print) { + const { dims, defaultValue } = path.node.children; + const declaration = [ + call(path, print, "unannType"), + " ", + call(path, print, "Identifier"), + "()" + ]; + if (dims) { + declaration.push(call(path, print, "dims")); } - return printTokenWithComments(this.getSingle(ctx) as IToken); - } - - interfaceMethodDeclaration(ctx: InterfaceMethodDeclarationCtx) { - const modifiers = sortModifiers(ctx.interfaceMethodModifier); - const firstAnnotations = this.mapVisit(modifiers[0]); - const otherModifiers = this.mapVisit(modifiers[1]); - - const methodHeader = this.visit(ctx.methodHeader); - const methodBody = this.visit(ctx.methodBody); - const separator = isStatementEmptyStatement(methodBody) ? "" : " "; - - return rejectAndJoin(hardline, [ - rejectAndJoin(hardline, firstAnnotations), - rejectAndJoin(" ", [ - rejectAndJoin(" ", otherModifiers), - rejectAndJoin(separator, [methodHeader, methodBody]) - ]) - ]); - } - - interfaceMethodModifier(ctx: InterfaceMethodModifierCtx) { - if (ctx.annotation) { - return this.visitSingle(ctx); + if (defaultValue) { + declaration.push(" ", call(path, print, "defaultValue")); } - return printTokenWithComments(this.getSingle(ctx) as IToken); - } - - annotationInterfaceDeclaration(ctx: AnnotationInterfaceDeclarationCtx) { - const typeIdentifier = this.visit(ctx.typeIdentifier); - const annotationInterfaceBody = this.visit(ctx.annotationInterfaceBody); - - return rejectAndJoin(" ", [ - concat([ctx.At[0], ctx.Interface[0]]), - typeIdentifier, - annotationInterfaceBody - ]); - } - - annotationInterfaceBody(ctx: AnnotationInterfaceBodyCtx) { - const annotationInterfaceMemberDeclaration = this.mapVisit( - ctx.annotationInterfaceMemberDeclaration + declaration.push(";"); + return printWithModifiers( + path, + print, + "annotationInterfaceElementModifier", + declaration ); - - return rejectAndJoin(line, [ - indent( - rejectAndJoin(line, [ - ctx.LCurly[0], - rejectAndJoin( - concat([line, line]), - annotationInterfaceMemberDeclaration - ) - ]) - ), - ctx.RCurly[0] - ]); - } - - annotationInterfaceMemberDeclaration( - ctx: AnnotationInterfaceMemberDeclarationCtx - ) { - if (ctx.Semicolon) { - return printTokenWithComments(this.getSingle(ctx) as IToken); + }, + + annotationInterfaceElementModifier: printSingle, + + defaultValue(path, print) { + return ["default ", call(path, print, "elementValue")]; + }, + + annotation(path, print) { + const { children } = path.node; + const annotation = ["@", call(path, print, "typeName")]; + if (children.elementValue || children.elementValuePairList) { + const valuesKey = onlyDefinedKey(children, [ + "elementValue", + "elementValuePairList" + ]); + annotation.push(indentInParentheses(call(path, print, valuesKey))); } - return this.visitSingle(ctx); - } - - annotationInterfaceElementDeclaration( - ctx: AnnotationInterfaceElementDeclarationCtx - ) { - const modifiers = sortModifiers(ctx.annotationInterfaceElementModifier); - const firstAnnotations = this.mapVisit(modifiers[0]); - const otherModifiers = this.mapVisit(modifiers[1]); - - const unannType = this.visit(ctx.unannType); - const identifier = ctx.Identifier[0]; - const dims = this.visit(ctx.dims); - const defaultValue = ctx.defaultValue - ? concat([" ", this.visit(ctx.defaultValue)]) - : ""; - - return rejectAndJoin(hardline, [ - rejectAndJoin(hardline, firstAnnotations), - rejectAndJoin(" ", [ - rejectAndJoin(" ", otherModifiers), - unannType, - rejectAndConcat([ - identifier, - concat([ctx.LBrace[0], ctx.RBrace[0]]), - dims, - defaultValue, - ctx.Semicolon[0] - ]) - ]) + return annotation; + }, + + elementValuePairList(path, print) { + return printList(path, print, "elementValuePair"); + }, + + elementValuePair(path, print) { + return join(" ", [ + call(path, print, "Identifier"), + call(path, print, "Equals"), + call(path, print, "elementValue") ]); - } - - annotationInterfaceElementModifier( - ctx: AnnotationInterfaceElementModifierCtx - ) { - if (ctx.annotation) { - return this.visitSingle(ctx); - } - return printTokenWithComments(this.getSingle(ctx) as IToken); - } + }, - defaultValue(ctx: DefaultValueCtx) { - const elementValue = this.visit(ctx.elementValue); - - return rejectAndJoin(" ", [ctx.Default[0], elementValue]); - } - - annotation(ctx: AnnotationCtx) { - const fqn = this.visit(ctx.typeName); - - let annoArgs: Doc = ""; - if (ctx.LBrace) { - const elementValues = - ctx.elementValuePairList?.[0].children.elementValuePair ?? - ctx.elementValue ?? - []; - handleCommentsParameters(ctx.LBrace[0], elementValues, ctx.RBrace![0]); - if (ctx.elementValuePairList) { - annoArgs = putIntoBraces( - this.visit(ctx.elementValuePairList), - softline, - ctx.LBrace[0], - ctx.RBrace![0] - ); - } else if (ctx.elementValue) { - annoArgs = putIntoBraces( - this.visit(ctx.elementValue), - softline, - ctx.LBrace[0], - ctx.RBrace![0] - ); - } - } - - return group(rejectAndConcat([ctx.At[0], fqn, annoArgs])); - } - - elementValuePairList(ctx: ElementValuePairListCtx) { - const elementValuePairs = this.mapVisit(ctx.elementValuePair); - const commas = ctx.Comma ? ctx.Comma.map(elt => concat([elt, line])) : []; - - return rejectAndJoinSeps(commas, elementValuePairs); - } - - elementValuePair(ctx: ElementValuePairCtx) { - const identifier = ctx.Identifier[0]; - const elementValue = this.visit(ctx.elementValue); - - return rejectAndJoin(" ", [identifier, ctx.Equals[0], elementValue]); - } - - elementValue(ctx: ElementValueCtx) { - return this.visitSingle(ctx); - } - - elementValueArrayInitializer(ctx: ElementValueArrayInitializerCtx) { - const elementValueList = this.visit(ctx.elementValueList); - - return printArrayList({ - list: elementValueList, - extraComma: ctx.Comma, - LCurly: ctx.LCurly[0], - RCurly: ctx.RCurly[0], - trailingComma: this.prettierOptions.trailingComma - }); - } + elementValue: printSingle, - elementValueList(ctx: ElementValueListCtx) { - const elementValues = this.mapVisit(ctx.elementValue); - const commas = ctx.Comma ? ctx.Comma.map(elt => concat([elt, line])) : []; + elementValueArrayInitializer(path, print, options) { + return printArrayInitializer(path, print, options, "elementValueList"); + }, - return group(rejectAndConcat([rejectAndJoinSeps(commas, elementValues)])); + elementValueList(path, print) { + return group(printList(path, print, "elementValue")); } -} +} satisfies Partial; diff --git a/packages/prettier-plugin-java/src/printers/lexical-structure.ts b/packages/prettier-plugin-java/src/printers/lexical-structure.ts index fe9bb71a5..2f67321c2 100644 --- a/packages/prettier-plugin-java/src/printers/lexical-structure.ts +++ b/packages/prettier-plugin-java/src/printers/lexical-structure.ts @@ -1,45 +1,40 @@ -import { printTokenWithComments } from "./comments/format-comments.js"; -import { join } from "./prettier-builder.js"; -import { BaseCstPrettierPrinter } from "../base-cst-printer.js"; -import { - BooleanLiteralCtx, - FloatingPointLiteralCtx, - IntegerLiteralCtx, - IToken, - LiteralCtx -} from "java-parser"; import { builders } from "prettier/doc"; +import { + findBaseIndent, + map, + onlyDefinedKey, + printSingle, + type JavaNodePrinters, + type JavaNonTerminal +} from "./helpers.js"; -const { hardline } = builders; +const { hardline, indent, join } = builders; -export class LexicalStructurePrettierVisitor extends BaseCstPrettierPrinter { - literal(ctx: LiteralCtx) { - if (ctx.TextBlock) { - const lines = ctx.TextBlock[0].image.split("\n"); - const open = lines.shift()!; - const baseIndent = Math.min( - ...lines.map(line => line.search(/\S/)).filter(indent => indent >= 0) - ); - return join(hardline, [ - open, - ...lines.map(line => line.slice(baseIndent)) - ]); +export default { + literal(path, print) { + const { TextBlock } = path.node.children; + if (!TextBlock) { + return printSingle(path, print); } - if (ctx.CharLiteral || ctx.StringLiteral || ctx.Null) { - return printTokenWithComments(this.getSingle(ctx) as IToken); - } - return this.visitSingle(ctx); - } + const [open, ...lines] = TextBlock[0].image.split("\n"); + const baseIndent = findBaseIndent(lines); + const textBlock = join(hardline, [ + open, + ...lines.map(line => line.slice(baseIndent)) + ]); + const ancestor = path.getNode(14) as JavaNonTerminal | null; + return ancestor?.name === "variableInitializer" || + (ancestor?.name === "binaryExpression" && + ancestor.children.AssignmentOperator) + ? indent(textBlock) + : textBlock; + }, - integerLiteral(ctx: IntegerLiteralCtx) { - return printTokenWithComments(this.getSingle(ctx) as IToken); - } - - floatingPointLiteral(ctx: FloatingPointLiteralCtx) { - return printTokenWithComments(this.getSingle(ctx) as IToken); - } + integerLiteral: printSingle, + floatingPointLiteral: printSingle, + booleanLiteral: printSingle, - booleanLiteral(ctx: BooleanLiteralCtx) { - return printTokenWithComments(this.getSingle(ctx) as IToken); + shiftOperator(path, print) { + return map(path, print, onlyDefinedKey(path.node.children)); } -} +} satisfies Partial; diff --git a/packages/prettier-plugin-java/src/printers/names.ts b/packages/prettier-plugin-java/src/printers/names.ts index d55acd6c1..b05580478 100644 --- a/packages/prettier-plugin-java/src/printers/names.ts +++ b/packages/prettier-plugin-java/src/printers/names.ts @@ -1,47 +1,12 @@ -import { buildFqn } from "./printer-utils.js"; -import { printTokenWithComments } from "./comments/format-comments.js"; -import { BaseCstPrettierPrinter } from "../base-cst-printer.js"; -import { - AmbiguousNameCtx, - ExpressionNameCtx, - MethodNameCtx, - ModuleNameCtx, - PackageNameCtx, - PackageOrTypeNameCtx, - TypeIdentifierCtx, - TypeNameCtx -} from "java-parser"; - -export class NamesPrettierVisitor extends BaseCstPrettierPrinter { - typeIdentifier(ctx: TypeIdentifierCtx) { - return printTokenWithComments(ctx.Identifier[0]); - } - - moduleName(ctx: ModuleNameCtx) { - return buildFqn(ctx.Identifier, ctx.Dot); - } - - packageName(ctx: PackageNameCtx) { - return buildFqn(ctx.Identifier, ctx.Dot); - } - - typeName(ctx: TypeNameCtx) { - return buildFqn(ctx.Identifier, ctx.Dot); - } - - expressionName(ctx: ExpressionNameCtx) { - return buildFqn(ctx.Identifier, ctx.Dot); - } - - methodName(ctx: MethodNameCtx) { - return printTokenWithComments(ctx.Identifier[0]); - } - - packageOrTypeName(ctx: PackageOrTypeNameCtx) { - return buildFqn(ctx.Identifier, ctx.Dot); - } - - ambiguousName(ctx: AmbiguousNameCtx) { - return buildFqn(ctx.Identifier, ctx.Dot); - } -} +import { printName, printSingle, type JavaNodePrinters } from "./helpers.js"; + +export default { + typeIdentifier: printSingle, + moduleName: printName, + packageName: printName, + typeName: printName, + expressionName: printName, + methodName: printSingle, + packageOrTypeName: printName, + ambiguousName: printName +} satisfies Partial; diff --git a/packages/prettier-plugin-java/src/printers/packages-and-modules.ts b/packages/prettier-plugin-java/src/printers/packages-and-modules.ts index 8698627f0..fba4d1a1c 100644 --- a/packages/prettier-plugin-java/src/printers/packages-and-modules.ts +++ b/packages/prettier-plugin-java/src/printers/packages-and-modules.ts @@ -1,259 +1,237 @@ -import { concat, join } from "./prettier-builder.js"; -import { printTokenWithComments } from "./comments/format-comments.js"; -import { - buildFqn, - displaySemicolon, - getBlankLinesSeparator, - putIntoBraces, - rejectAndConcat, - rejectAndJoin, - rejectAndJoinSeps, - sortImports -} from "./printer-utils.js"; -import { builders } from "prettier/doc"; -import { BaseCstPrettierPrinter } from "../base-cst-printer.js"; -import { - CompilationUnitCtx, - ExportsModuleDirectiveCtx, - ImportDeclarationCtx, +import type { + ExportsModuleDirectiveCstNode, + ImportDeclarationCstNode, IToken, - ModularCompilationUnitCtx, - ModuleDeclarationCtx, - ModuleDirectiveCtx, - OpensModuleDirectiveCtx, - OrdinaryCompilationUnitCtx, - PackageDeclarationCtx, - PackageModifierCtx, - ProvidesModuleDirectiveCtx, - RequiresModifierCtx, - RequiresModuleDirectiveCtx, - TypeDeclarationCtx, - UsesModuleDirectiveCtx + OpensModuleDirectiveCstNode } from "java-parser"; -import { isOrdinaryCompilationUnitCtx } from "../types/utils.js"; - -const { line, hardline, indent, group } = builders; - -export class PackagesAndModulesPrettierVisitor extends BaseCstPrettierPrinter { - compilationUnit(ctx: CompilationUnitCtx) { - const compilationUnit = isOrdinaryCompilationUnitCtx(ctx) - ? ctx.ordinaryCompilationUnit - : ctx.modularCompilationUnit; - - return concat([this.visit(compilationUnit[0]), line]); - } - - ordinaryCompilationUnit(ctx: OrdinaryCompilationUnitCtx) { - const packageDecl = this.visit(ctx.packageDeclaration); - - const sortedImportsDecl = sortImports(ctx.importDeclaration); - const nonStaticImports = this.mapVisit(sortedImportsDecl.nonStaticImports); - const staticImports = this.mapVisit(sortedImportsDecl.staticImports); - - const typesDecl = this.mapVisit(ctx.typeDeclaration); - // TODO: utility to add item+line (or multiple lines) but only if an item exists - return rejectAndConcat([ - rejectAndJoin(concat([hardline, hardline]), [ - packageDecl, - rejectAndJoin(hardline, staticImports), - rejectAndJoin(hardline, nonStaticImports), - rejectAndJoin(concat([hardline, hardline]), typesDecl) - ]) - ]); - } - - modularCompilationUnit(ctx: ModularCompilationUnitCtx) { - const sortedImportsDecl = sortImports(ctx.importDeclaration); - const nonStaticImports = this.mapVisit(sortedImportsDecl.nonStaticImports); - const staticImports = this.mapVisit(sortedImportsDecl.staticImports); - - const moduleDeclaration = this.visit(ctx.moduleDeclaration); - - return rejectAndConcat([ - rejectAndJoin(concat([hardline, hardline]), [ - rejectAndJoin(hardline, staticImports), - rejectAndJoin(hardline, nonStaticImports), - moduleDeclaration - ]) - ]); - } - - packageDeclaration(ctx: PackageDeclarationCtx) { - const modifiers = this.mapVisit(ctx.packageModifier); - const name = buildFqn(ctx.Identifier, ctx.Dot); - - return rejectAndJoin(hardline, [ - rejectAndJoin(hardline, modifiers), - concat([ctx.Package[0], " ", name, ctx.Semicolon[0]]) +import type { AstPath, Doc } from "prettier"; +import { builders } from "prettier/doc"; +import { + call, + lineEndWithComments, + lineStartWithComments, + map, + printBlock, + printDanglingComments, + printName, + printSingle, + type JavaNodePrinters, + type JavaPrintFn +} from "./helpers.js"; + +const { group, hardline, indent, join, line } = builders; + +export default { + compilationUnit(path, print) { + return [...printDanglingComments(path), printSingle(path, print), hardline]; + }, + + ordinaryCompilationUnit(path, print) { + const { children } = path.node; + const declarations: Doc[] = []; + if (children.packageDeclaration) { + declarations.push(call(path, print, "packageDeclaration")); + } + if (children.importDeclaration) { + const staticCount = sortImports(children.importDeclaration); + const importDeclarations = map(path, print, "importDeclaration").filter( + doc => doc !== "" + ); + const staticDeclarations = importDeclarations.slice(0, staticCount); + const nonStaticDeclarations = importDeclarations.slice(staticCount); + declarations.push( + ...[staticDeclarations, nonStaticDeclarations] + .filter(({ length }) => length) + .map(declarations => join(hardline, declarations)) + ); + } + if (children.typeDeclaration) { + declarations.push( + ...map(path, print, "typeDeclaration").filter( + declaration => declaration !== "" + ) + ); + } + return join([hardline, hardline], declarations); + }, + + modularCompilationUnit(path, print) { + const { children } = path.node; + const declarations: Doc[] = []; + if (children.importDeclaration) { + const staticCount = sortImports(children.importDeclaration); + const importDeclarations = map(path, print, "importDeclaration").filter( + doc => doc !== "" + ); + const staticDeclarations = importDeclarations.slice(0, staticCount); + const nonStaticDeclarations = importDeclarations.slice(staticCount); + declarations.push( + ...[staticDeclarations, nonStaticDeclarations] + .filter(({ length }) => length) + .map(declarations => join(hardline, declarations)) + ); + } + declarations.push(call(path, print, "moduleDeclaration")); + return join([hardline, hardline], declarations); + }, + + packageDeclaration(path, print) { + return join(hardline, [ + ...map(path, print, "packageModifier"), + ["package ", printName(path, print), ";"] ]); - } + }, - packageModifier(ctx: PackageModifierCtx) { - return this.visitSingle(ctx); - } + packageModifier: printSingle, - importDeclaration(ctx: ImportDeclarationCtx) { - if (ctx.emptyStatement !== undefined) { - return this.visit(ctx.emptyStatement); + importDeclaration(path, print) { + const { children } = path.node; + if (children.emptyStatement) { + return call(path, print, "emptyStatement"); } - - const optionalStatic = ctx.Static ? ctx.Static[0] : ""; - const packageOrTypeName = this.visit(ctx.packageOrTypeName); - - const optionalDotStar = ctx.Dot ? concat([ctx.Dot[0], ctx.Star![0]]) : ""; - - return rejectAndJoin(" ", [ - ctx.Import![0], - optionalStatic, - rejectAndConcat([packageOrTypeName, optionalDotStar, ctx.Semicolon![0]]) - ]); - } - - typeDeclaration(ctx: TypeDeclarationCtx) { - if (ctx.Semicolon) { - return displaySemicolon(ctx.Semicolon[0]); + const declaration: Doc[] = ["import "]; + if (children.Static) { + declaration.push("static "); } - return this.visitSingle(ctx); - } - - moduleDeclaration(ctx: ModuleDeclarationCtx) { - const annotations = this.mapVisit(ctx.annotation); - const optionalOpen = ctx.Open ? ctx.Open[0] : ""; - const name = buildFqn(ctx.Identifier, ctx.Dot); - const moduleDirectives = this.mapVisit(ctx.moduleDirective); - - const content = rejectAndJoinSeps( - getBlankLinesSeparator(ctx.moduleDirective), - moduleDirectives + declaration.push(call(path, print, "packageOrTypeName")); + if (children.Star) { + declaration.push(".*"); + } + declaration.push(";"); + return declaration; + }, + + typeDeclaration(path, print) { + return path.node.children.Semicolon ? "" : printSingle(path, print); + }, + + moduleDeclaration(path, print) { + const { annotation, Open } = path.node.children; + const prefix: Doc[] = []; + if (annotation) { + prefix.push(...map(path, print, "annotation")); + } + if (Open) { + prefix.push("open"); + } + const declarations = map( + path, + declarationPath => { + const declaration = print(declarationPath); + const { node, previous } = declarationPath; + return !previous || + lineStartWithComments(node) <= lineEndWithComments(previous) + 1 + ? declaration + : [hardline, declaration]; + }, + "moduleDirective" ); - - return rejectAndJoin(" ", [ - join(" ", annotations), - optionalOpen, - ctx.Module[0], - name, - putIntoBraces(content, hardline, ctx.LCurly[0], ctx.RCurly[0]) + return join(" ", [ + ...prefix, + "module", + printName(path, print), + printBlock(path, declarations) ]); - } - - moduleDirective(ctx: ModuleDirectiveCtx) { - return this.visitSingle(ctx); - } + }, - requiresModuleDirective(ctx: RequiresModuleDirectiveCtx) { - const modifiers = this.mapVisit(ctx.requiresModifier); - const moduleName = this.visit(ctx.moduleName); + moduleDirective: printSingle, - return rejectAndJoin(" ", [ - ctx.Requires[0], - join(" ", modifiers), - concat([moduleName, ctx.Semicolon[0]]) + requiresModuleDirective(path, print) { + return join(" ", [ + "requires", + ...map(path, print, "requiresModifier"), + [call(path, print, "moduleName"), ";"] ]); - } - - exportsModuleDirective(ctx: ExportsModuleDirectiveCtx) { - const packageName = this.visit(ctx.packageName); - const moduleNames = this.mapVisit(ctx.moduleName); - const commas = ctx.Comma ? ctx.Comma.map(elt => concat([elt, line])) : []; - - if (ctx.To) { - return group( - rejectAndConcat([ - indent( - rejectAndJoin(line, [ - rejectAndJoin(" ", [ctx.Exports[0], packageName]), - group( - indent( - rejectAndJoin(line, [ - ctx.To[0], - rejectAndJoinSeps(commas, moduleNames) - ]) - ) - ) - ]) - ), - ctx.Semicolon[0] + }, + + exportsModuleDirective(path, print) { + return printToModuleNamesDirective(path, print, "exports"); + }, + + opensModuleDirective(path, print) { + return printToModuleNamesDirective(path, print, "opens"); + }, + + usesModuleDirective(path, print) { + return ["uses ", call(path, print, "typeName"), ";"]; + }, + + providesModuleDirective(path, print) { + const [firstTypeName, ...restTypeNames] = map(path, print, "typeName"); + return [ + "provides ", + firstTypeName, + group( + indent([ + line, + group(indent(["with", line, ...join([",", line], restTypeNames)])) ]) - ); + ), + ";" + ]; + }, + + requiresModifier: printSingle +} satisfies Partial; + +function sortImports(importDeclarations: ImportDeclarationCstNode[]) { + importDeclarations.sort(({ children: a }, { children: b }) => { + if (a.Static && !b.Static) { + return -1; + } else if (b.Static && !a.Static) { + return 1; } - - return rejectAndConcat([ - concat([ctx.Exports[0], " "]), - packageName, - ctx.Semicolon[0] - ]); - } - - opensModuleDirective(ctx: OpensModuleDirectiveCtx) { - const packageName = this.visit(ctx.packageName); - const to = ctx.To ? ctx.To[0] : ""; - const moduleNames = this.mapVisit(ctx.moduleName); - const commas = ctx.Comma ? ctx.Comma.map(elt => concat([elt, line])) : []; - - if (ctx.To) { - return group( - rejectAndConcat([ - indent( - rejectAndJoin(line, [ - rejectAndJoin(" ", [ctx.Opens[0], packageName]), - group( - indent( - rejectAndJoin(line, [ - ctx.To[0], - rejectAndJoinSeps(commas, moduleNames) - ]) - ) - ) - ]) - ), - ctx.Semicolon[0] - ]) - ); + if (!b.packageOrTypeName) { + if (a.packageOrTypeName) { + return -1; + } + return 0; + } else if (!a.packageOrTypeName) { + return 1; } + return compareFqn(a.packageOrTypeName[0], b.packageOrTypeName[0]); + }); + + return importDeclarations.reduce( + (staticCount, importDeclaration) => + importDeclaration.children.Static ? staticCount + 1 : staticCount, + 0 + ); +} - return rejectAndConcat([ - concat([ctx.Opens[0], " "]), - packageName, - ctx.Semicolon[0] - ]); - } - - usesModuleDirective(ctx: UsesModuleDirectiveCtx) { - const typeName = this.visit(ctx.typeName); - - return rejectAndConcat([ - concat([ctx.Uses[0], " "]), - typeName, - ctx.Semicolon[0] - ]); +function compareFqn( + a: { children: { Identifier: IToken[] } }, + b: { children: { Identifier: IToken[] } } +) { + const identifiersA = a.children.Identifier; + const identifiersB = b.children.Identifier; + + const minParts = Math.min(identifiersA.length, identifiersB.length); + for (let i = 0; i < minParts; i++) { + const imageA = identifiersA[i].image; + const imageB = identifiersB[i].image; + if (imageA < imageB) { + return -1; + } else if (imageA > imageB) { + return 1; + } } - providesModuleDirective(ctx: ProvidesModuleDirectiveCtx) { - const firstTypeName = this.visit(ctx.typeName[0]); - const otherTypeNames = this.mapVisit(ctx.typeName.slice(1)); - const commas = ctx.Comma ? ctx.Comma.map(elt => concat([elt, line])) : []; + return identifiersA.length - identifiersB.length; +} - return group( - rejectAndConcat([ - indent( - rejectAndJoin(line, [ - rejectAndJoin(" ", [ctx.Provides[0], firstTypeName]), - group( - indent( - rejectAndJoin(line, [ - ctx.With[0], - rejectAndJoinSeps(commas, otherTypeNames) - ]) - ) - ) - ]) - ), - ctx.Semicolon[0] - ]) +function printToModuleNamesDirective( + path: AstPath, + print: JavaPrintFn, + prefix: string +) { + const directive = [prefix, " ", call(path, print, "packageName")]; + if (path.node.children.moduleName) { + const moduleNames = join([",", line], map(path, print, "moduleName")); + directive.push( + group(indent([line, group(indent(["to", line, ...moduleNames]))])) ); } - - requiresModifier(ctx: RequiresModifierCtx) { - return printTokenWithComments(this.getSingle(ctx) as IToken); - } + directive.push(";"); + return directive; } diff --git a/packages/prettier-plugin-java/src/printers/prettier-builder.ts b/packages/prettier-plugin-java/src/printers/prettier-builder.ts deleted file mode 100644 index 613c2dee4..000000000 --- a/packages/prettier-plugin-java/src/printers/prettier-builder.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { IToken } from "java-parser"; -import { builders } from "prettier/doc"; -import Doc = builders.Doc; -import * as formatComments from "./comments/format-comments.js"; - -const processComments = formatComments.processComments as any; -/* - * ------------------------------------------------------------------ - * Wraps the Prettier builder functions to print tokens with comments - * ------------------------------------------------------------------ - */ - -export function concat(docs: (Doc | IToken)[]): Doc { - const concatenation = processComments(docs); - - if (!Array.isArray(docs)) { - return ""; - } - - return concatenation; -} - -export function join(sep: any, docs: (Doc | IToken)[]): Doc { - return builders.join(processComments(sep), processComments(docs)); -} - -export function group(docs: Doc | IToken | (Doc | IToken)[], opts?: any) { - const group = builders.group(processComments(docs), opts); - return group.contents === undefined ? "" : group; -} - -export function fill(docs: (Doc | IToken)[]) { - return builders.fill(processComments(docs)); -} - -export function indent(doc: Doc | IToken) { - const processedDoc = processComments(doc); - if (processedDoc.length === 0) { - return ""; - } - - return builders.indent(processedDoc); -} - -export function dedent(doc: Doc | IToken) { - const processedDoc = processComments(doc); - if (processedDoc.length === 0) { - return ""; - } - - return builders.dedent(processComments(doc)); -} - -export function ifBreak( - breakContents: Doc | IToken, - flatContents: Doc | IToken -) { - return builders.ifBreak( - processComments(breakContents), - processComments(flatContents) - ); -} - -export function indentIfBreak(contents: Doc | IToken, opts?: any) { - return builders.indentIfBreak(processComments(contents), opts); -} diff --git a/packages/prettier-plugin-java/src/printers/printer-utils.ts b/packages/prettier-plugin-java/src/printers/printer-utils.ts deleted file mode 100644 index bf63941dd..000000000 --- a/packages/prettier-plugin-java/src/printers/printer-utils.ts +++ /dev/null @@ -1,848 +0,0 @@ -import { - AnnotationCstNode, - BinaryExpressionCtx, - ClassBodyDeclarationCstNode, - ConstantModifierCstNode, - CstElement, - CstNode, - FieldModifierCstNode, - ImportDeclarationCstNode, - InterfaceMemberDeclarationCstNode, - InterfaceMethodModifierCstNode, - IToken, - LambdaParametersWithBracesCtx, - MethodModifierCstNode, - TypeArgumentsCstNode -} from "java-parser"; -import findIndex from "lodash/findIndex.js"; -import findLastIndex from "lodash/findLastIndex.js"; -import forEach from "lodash/forEach.js"; -import forEachRight from "lodash/forEachRight.js"; -import includes from "lodash/includes.js"; -import { Doc, doc } from "prettier"; -import { builders } from "prettier/doc"; -import { isCstNode } from "../types/utils.js"; -import { isEmptyDoc } from "../utils/index.js"; -import { - hasComments, - hasLeadingComments, - hasTrailingComments -} from "./comments/comments-utils.js"; -import { - getTokenLeadingComments, - printTokenWithComments -} from "./comments/format-comments.js"; -import { concat, group, ifBreak, join } from "./prettier-builder.js"; - -const { indent, hardline, line, lineSuffixBoundary, softline } = builders; - -const orderedModifiers = [ - "Public", - "Protected", - "Private", - "Abstract", - "Default", - "Static", - "Final", - "Transient", - "Volatile", - "Synchronized", - "Native", - "Sealed", - "NonSealed", - "Strictfp" -]; - -export function buildFqn(tokens: IToken[], dots: IToken[] | undefined) { - return rejectAndJoinSeps(dots ? dots : [], tokens); -} - -export function rejectAndJoinSeps( - sepTokens: (IToken | Doc)[] | undefined, - elems: (Doc | IToken | undefined)[], - sep?: string -) { - if (!Array.isArray(sepTokens)) { - return rejectAndJoin(sepTokens, elems); - } - const actualElements = reject(elems); - const res = []; - - for (let i = 0; i < sepTokens.length; i++) { - res.push(actualElements[i], sepTokens[i]); - if (sep) { - res.push(sep); - } - } - res.push(...actualElements.slice(sepTokens.length)); - return concat(res); -} - -export function reject(elems: (IToken | Doc | undefined)[]): (IToken | Doc)[] { - return elems.filter(item => { - if (typeof item === "string") { - return item !== ""; - } - // eslint-ignore next - We want the conversion to boolean! - // @ts-ignore - return item != false && item !== undefined; - }) as (IToken | Doc)[]; -} - -export function rejectSeparators( - separators: Doc[] | undefined, - elems: (IToken | Doc | undefined)[] -) { - const realElements = reject(elems); - - const realSeparators = []; - for (let i = 0; i < realElements.length - 1; i++) { - if (realElements[i] !== "") { - realSeparators.push(separators![i]); - } - } - - return realSeparators; -} - -export function rejectAndJoin( - sep: Doc | IToken | undefined, - elems: (Doc | IToken | undefined)[] -) { - const actualElements = reject(elems); - - return join(sep, actualElements); -} - -export function rejectAndConcat(elems: (Doc | IToken | undefined)[]) { - const actualElements = reject(elems); - - return concat(actualElements); -} - -export function sortAnnotationIdentifier( - annotations: AnnotationCstNode[] | undefined, - identifiers: IToken[] -) { - let tokens: CstElement[] = [...identifiers]; - - if (annotations && annotations.length > 0) { - tokens = [...tokens, ...annotations]; - } - - return tokens.sort((a, b) => { - const startOffset1 = isCstNode(a) - ? (a.children.At[0] as IToken).startOffset - : a.startOffset; - const startOffset2 = isCstNode(b) - ? (b.children.At[0] as IToken).startOffset - : b.startOffset; - return startOffset1 - startOffset2; - }); -} - -export function sortTokens(values: (IToken[] | undefined)[]): IToken[] { - let tokens: IToken[] = []; - - forEach(values, argument => { - if (argument) { - tokens = tokens.concat(argument); - } - }); - - return tokens.sort((a, b) => { - return a.startOffset - b.startOffset; - }); -} - -export function sortNodes(values: (CstNode[] | undefined)[]) { - let nodes: CstNode[] = []; - - forEach(values, argument => { - if (argument) { - nodes = nodes.concat(argument); - } - }); - - return nodes.sort((a, b) => { - const aOffset = a.location.startOffset; - const bOffset = b.location.startOffset; - return aOffset - bOffset; - }); -} - -export function matchCategory(token: IToken, categoryName: string) { - const labels = (token.tokenType.CATEGORIES || []).map(category => { - return category.LABEL; - }); - - return labels.indexOf(categoryName) !== -1; -} - -export function sortClassTypeChildren( - annotations: AnnotationCstNode[] | undefined, - typeArguments: TypeArgumentsCstNode[] | undefined, - identifiers: IToken[], - dots?: IToken[] -) { - let tokens: CstElement[] = [...identifiers]; - - if (annotations && annotations.length > 0) { - tokens = [...tokens, ...annotations]; - } - - if (typeArguments && typeArguments.length > 0) { - tokens = [...tokens, ...typeArguments]; - } - - if (dots && dots.length > 0) { - tokens = [...tokens, ...dots]; - } - - return tokens.sort((a, b) => { - const startOffsetA = isCstNode(a) - ? a.children.At - ? (a.children.At[0] as IToken).startOffset - : (a.children.Less[0] as IToken).startOffset - : a.startOffset; - const startOffsetB = isCstNode(b) - ? b.children.At - ? (b.children.At[0] as IToken).startOffset - : (b.children.Less[0] as IToken).startOffset - : b.startOffset; - return startOffsetA - startOffsetB; - }); -} - -export function sortModifiers(modifiers: CstNode[] | undefined) { - let firstAnnotations: CstNode[] = []; - const otherModifiers: CstNode[] = []; - let lastAnnotations: CstNode[] = []; - let hasOtherModifier = false; - - /** - * iterate in reverse order because we special-case - * type annotations which come after all other - * modifiers - */ - forEachRight(modifiers, modifier => { - const isAnnotation = modifier.children.annotation !== undefined; - const isTypeAnnotation = - isAnnotation && - (modifier.name === "methodModifier" || - modifier.name === "interfaceMethodModifier" || - modifier.name === "fieldModifier"); - - if (isAnnotation) { - if (isTypeAnnotation && !hasOtherModifier) { - lastAnnotations.unshift(modifier); - } else { - firstAnnotations.unshift(modifier); - } - } else { - otherModifiers.unshift(modifier); - hasOtherModifier = true; - } - }); - - /** - * if there are only annotations, move everything from - * lastAnnotations to firstAnnotations - */ - if (!hasOtherModifier) { - firstAnnotations = firstAnnotations.concat(lastAnnotations); - lastAnnotations = []; - } - - otherModifiers.sort((a, b) => { - const modifierIndexA = orderedModifiers.indexOf(Object.keys(a.children)[0]); - const modifierIndexB = orderedModifiers.indexOf(Object.keys(b.children)[0]); - - return modifierIndexA - modifierIndexB; - }); - - return [firstAnnotations, otherModifiers.concat(lastAnnotations)]; -} - -export function findDeepElementInPartsArray(item: any, elt: any) { - if (Array.isArray(item)) { - if (includes(item, elt)) { - return true; - } - for (let i = 0; i < item.length; i++) { - if (findDeepElementInPartsArray(item[i], elt)) { - return true; - } - } - } else { - for (const key in item) { - if ( - typeof item[key] === "object" && - findDeepElementInPartsArray(item[key], elt) - ) { - return true; - } - } - } - - return false; -} - -export function displaySemicolon(token: IToken, params?: any) { - if (params !== undefined && params.allowEmptyStatement) { - return printTokenWithComments(token); - } - - if (!hasComments(token)) { - return ""; - } - - token.image = ""; - return printTokenWithComments(token); -} - -export function isExplicitLambdaParameter(ctx: LambdaParametersWithBracesCtx) { - return ( - ctx && - ctx.lambdaParameterList && - ctx.lambdaParameterList[0] && - ctx.lambdaParameterList[0].children && - ctx.lambdaParameterList[0].children.normalLambdaParameterList - ); -} - -export function getBlankLinesSeparator( - ctx: CstNode[] | undefined, - separator: builders.Line | builders.Hardline = hardline -): Doc[] { - if (ctx === undefined) { - return []; - } - - const separators: Doc[] = []; - for (let i = 0; i < ctx.length - 1; i++) { - const node = ctx[i]; - const previousRuleEndLineWithComment = hasTrailingComments(node) - ? node.trailingComments[node.trailingComments.length - 1].endLine - : (node.location.endLine as number); - - const nextNode = ctx[i + 1]; - const nextRuleStartLineWithComment = hasLeadingComments(nextNode) - ? nextNode.leadingComments[0].startLine - : nextNode.location.startLine; - - if (nextRuleStartLineWithComment! - previousRuleEndLineWithComment > 1) { - separators.push([hardline, hardline]); - } else { - separators.push(separator); - } - } - - return separators; -} - -const isTwoHardLine = (userBlankLinesSeparator: Doc): boolean => { - if (!Array.isArray(userBlankLinesSeparator)) { - return false; - } - - return ( - userBlankLinesSeparator.length === 2 && - userBlankLinesSeparator[0] === hardline && - userBlankLinesSeparator[1] === hardline - ); -}; - -function getDeclarationsSeparator< - Declaration extends - | ClassBodyDeclarationCstNode - | InterfaceMemberDeclarationCstNode ->( - declarations: Declaration[], - needLineDeclaration: (decl: Declaration) => boolean, - isSemicolon: (decl: Declaration) => boolean -) { - const declarationsWithoutEmptyStatements = declarations.filter( - declaration => !isSemicolon(declaration) - ); - - const userBlankLinesSeparators = getBlankLinesSeparator( - declarationsWithoutEmptyStatements - ); - const additionalBlankLines = - declarationsWithoutEmptyStatements.map(needLineDeclaration); - - const separators = []; - let indexNextNotEmptyDeclaration = 0; - for (let i = 0; i < declarations.length - 1; i++) { - // if the empty statement has comments - // we want to print them on their own line - if (isSemicolon(declarations[i])) { - if (hasComments(declarations[i])) { - separators.push(hardline); - } - } else if ( - indexNextNotEmptyDeclaration < - declarationsWithoutEmptyStatements.length - 1 - ) { - const isNextSeparatorTwoHardLine = isTwoHardLine( - userBlankLinesSeparators[indexNextNotEmptyDeclaration] - ); - const additionalSep = - !isNextSeparatorTwoHardLine && - (additionalBlankLines[indexNextNotEmptyDeclaration + 1] || - additionalBlankLines[indexNextNotEmptyDeclaration]) - ? hardline - : ""; - separators.push( - concat([ - userBlankLinesSeparators![indexNextNotEmptyDeclaration], - additionalSep - ]) - ); - - indexNextNotEmptyDeclaration += 1; - } - } - - return separators; -} - -function needLineClassBodyDeclaration( - declaration: ClassBodyDeclarationCstNode -) { - if (declaration.children.classMemberDeclaration === undefined) { - return true; - } - - const classMemberDeclaration = declaration.children.classMemberDeclaration[0]; - - if (classMemberDeclaration.children.fieldDeclaration !== undefined) { - const fieldDeclaration = - classMemberDeclaration.children.fieldDeclaration[0]; - if ( - fieldDeclaration.children.fieldModifier !== undefined && - hasAnnotation(fieldDeclaration.children.fieldModifier) && - hasNonTrailingAnnotation(fieldDeclaration.children.fieldModifier) - ) { - return true; - } - return false; - } else if (classMemberDeclaration.children.Semicolon !== undefined) { - return false; - } - - return true; -} - -function needLineInterfaceMemberDeclaration( - declaration: InterfaceMemberDeclarationCstNode -) { - if (declaration.children.constantDeclaration !== undefined) { - const constantDeclaration = declaration.children.constantDeclaration[0]; - if ( - constantDeclaration.children.constantModifier !== undefined && - hasAnnotation(constantDeclaration.children.constantModifier) && - hasNonTrailingAnnotation(constantDeclaration.children.constantModifier) - ) { - return true; - } - return false; - } else if (declaration.children.interfaceMethodDeclaration !== undefined) { - const interfaceMethodDeclaration = - declaration.children.interfaceMethodDeclaration[0]; - if ( - interfaceMethodDeclaration.children.interfaceMethodModifier !== - undefined && - hasNonTrailingAnnotation( - interfaceMethodDeclaration.children.interfaceMethodModifier - ) - ) { - return true; - } - return false; - } - - return true; -} - -function isClassBodyDeclarationASemicolon( - classBodyDeclaration: ClassBodyDeclarationCstNode -) { - if (classBodyDeclaration.children.classMemberDeclaration) { - if ( - classBodyDeclaration.children.classMemberDeclaration[0].children - .Semicolon !== undefined - ) { - return true; - } - } - return false; -} - -function isInterfaceMemberASemicolon( - interfaceMemberDeclaration: InterfaceMemberDeclarationCstNode -) { - return interfaceMemberDeclaration.children.Semicolon !== undefined; -} - -function hasAnnotation( - modifiers: ( - | MethodModifierCstNode - | InterfaceMethodModifierCstNode - | FieldModifierCstNode - | ConstantModifierCstNode - )[] -) { - return modifiers.some(modifier => modifier.children.annotation !== undefined); -} - -/** - * Return true if there is a modifier that does not come after all other modifiers - * It is useful to know if sortModifiers will add an annotation before other modifiers - * - * @param modifiers - * @returns {boolean} - */ -function hasNonTrailingAnnotation( - modifiers: ( - | MethodModifierCstNode - | InterfaceMethodModifierCstNode - | FieldModifierCstNode - | ConstantModifierCstNode - )[] -) { - const firstAnnotationIndex = findIndex( - modifiers, - modifier => modifier.children.annotation !== undefined - ); - const lastNonAnnotationIndex = findLastIndex( - modifiers, - modifier => modifier.children.annotation === undefined - ); - - return ( - firstAnnotationIndex < lastNonAnnotationIndex || - lastNonAnnotationIndex === -1 - ); -} - -export function getClassBodyDeclarationsSeparator( - classBodyDeclarationContext: ClassBodyDeclarationCstNode[] -) { - return getDeclarationsSeparator( - classBodyDeclarationContext, - needLineClassBodyDeclaration, - isClassBodyDeclarationASemicolon - ); -} - -export function getInterfaceBodyDeclarationsSeparator( - interfaceMemberDeclarationContext: InterfaceMemberDeclarationCstNode[] -) { - return getDeclarationsSeparator( - interfaceMemberDeclarationContext, - needLineInterfaceMemberDeclaration, - isInterfaceMemberASemicolon - ); -} - -function getAndRemoveLeadingComment(doc: IToken | Doc) { - const isTokenWithLeadingComment = - typeof doc !== "string" && "leadingComments" in doc; - if (!isTokenWithLeadingComment) { - return []; - } - - const leadingComments = getTokenLeadingComments(doc); - delete doc.leadingComments; - - return leadingComments; -} - -export function putIntoBraces( - argument: Doc, - separator: Doc, - LBrace: IToken, - RBrace: IToken | Doc -) { - const rightBraceLeadingComments = getAndRemoveLeadingComment(RBrace); - const lastBreakLine = - // check if last element of the array is a line - rightBraceLeadingComments.length !== 0 && - rightBraceLeadingComments[rightBraceLeadingComments.length - 1] === hardline - ? rightBraceLeadingComments.pop() - : separator; - - let contentInsideBraces; - - if (isEmptyDoc(argument)) { - if (rightBraceLeadingComments.length === 0) { - return group([ - indent(printTokenWithComments(LBrace)), - ...(LBrace.trailingComments ? [softline, lineSuffixBoundary] : []), - RBrace - ]); - } - contentInsideBraces = [separator, ...rightBraceLeadingComments]; - } else if (rightBraceLeadingComments.length !== 0) { - contentInsideBraces = [ - separator, - argument, - separator, - ...rightBraceLeadingComments - ]; - } else { - contentInsideBraces = [separator, argument]; - } - - return group( - rejectAndConcat([ - LBrace, - indent(concat(contentInsideBraces)), - lastBreakLine, - RBrace - ]) - ); -} - -export function binary(nodes: Doc[], tokens: IToken[], isRoot = false): Doc { - let levelOperator: string | undefined; - let levelPrecedence: number | undefined; - let level: Doc[] = []; - while (tokens.length) { - const nextOperator = getOperator(tokens); - const nextPrecedence = getOperatorPrecedence(nextOperator); - - if (levelPrecedence === undefined || nextPrecedence === levelPrecedence) { - const tokenLength = ["<<", ">>", ">>>"].includes(nextOperator) - ? nextOperator.length - : 1; - const operator = concat(tokens.splice(0, tokenLength)); - if ( - levelOperator !== undefined && - needsParentheses(levelOperator, nextOperator) - ) { - level.push(nodes.shift()!); - level = [ - concat(["(", group(indent(join(line, level))), ") ", operator]) - ]; - } else { - level.push(join(" ", [nodes.shift()!, operator])); - } - levelOperator = nextOperator; - levelPrecedence = nextPrecedence; - } else if (nextPrecedence < levelPrecedence) { - level.push(nodes.shift()!); - if (isRoot) { - const content = group(indent(join(line, level))); - nodes.unshift( - levelOperator !== undefined && - needsParentheses(levelOperator, nextOperator) - ? concat(["(", content, ")"]) - : content - ); - level = []; - levelOperator = undefined; - levelPrecedence = undefined; - } else { - return group(join(line, level)); - } - } else { - const content = binary(nodes, tokens); - nodes.unshift( - levelOperator !== undefined && - needsParentheses(nextOperator, levelOperator) - ? concat(["(", indent(content), ")"]) - : content - ); - } - } - level.push(nodes.shift()!); - return group(join(line, level)); -} - -export function getOperators(ctx: BinaryExpressionCtx) { - return [ - ctx.AssignmentOperator, - ctx.BinaryOperator, - ctx.Greater, - ctx.Instanceof, - ctx.Less - ].filter(token => token !== undefined); -} - -function getOperator(tokens: IToken[]) { - if (!tokens.length) { - return ""; - } - const [{ image, startOffset }] = tokens; - if (!["<", ">"].includes(image)) { - return image; - } - let repeatedTokenCount = 1; - for (let i = 1; i < Math.min(3, tokens.length); i++) { - const token = tokens[i]; - if (token.image !== image || token.startOffset !== startOffset + i) { - break; - } - repeatedTokenCount++; - } - if (repeatedTokenCount === 1) { - return image; - } - if (image === "<") { - return "<<"; - } else if (repeatedTokenCount == 2) { - return ">>"; - } else { - return ">>>"; - } -} - -const PRECEDENCES_BY_OPERATOR = new Map( - [ - ["||"], - ["&&"], - ["|"], - ["^"], - ["&"], - ["==", "!="], - ["<", ">", "<=", ">=", "instanceof"], - ["<<", ">>", ">>>"], - ["+", "-"], - ["*", "/", "%"] - ].flatMap((operators, index) => operators.map(operator => [operator, index])) -); -function getOperatorPrecedence(operator: string) { - return PRECEDENCES_BY_OPERATOR.get(operator) ?? -1; -} - -function needsParentheses(operator: string, parentOperator: string) { - return ( - (operator === "&&" && parentOperator === "||") || - (["|", "^", "&", "<<", ">>", ">>>"].includes(parentOperator) && - getOperatorPrecedence(operator) > - getOperatorPrecedence(parentOperator)) || - [operator, parentOperator].every(o => ["==", "!="].includes(o)) || - [operator, parentOperator].every(o => ["<<", ">>", ">>>"].includes(o)) || - (operator === "*" && parentOperator === "/") || - (operator === "/" && parentOperator === "*") || - (operator === "%" && ["+", "-", "*", "/"].includes(parentOperator)) || - (["*", "/"].includes(operator) && parentOperator === "%") - ); -} - -export function isStatementEmptyStatement(statement: Doc) { - return ( - statement === ";" || (Array.isArray(statement) && statement[0] === ";") - ); -} - -export function sortImports(imports: ImportDeclarationCstNode[] | undefined) { - const staticImports: ImportDeclarationCstNode[] = []; - const nonStaticImports: ImportDeclarationCstNode[] = []; - - if (imports !== undefined) { - for (let i = 0; i < imports.length; i++) { - if (imports[i].children.Static !== undefined) { - staticImports.push(imports[i]); - } else if (imports[i].children.emptyStatement === undefined) { - nonStaticImports.push(imports[i]); - } - } - - // TODO: Could be optimized as we could expect that the array is already almost sorted - const comparator = ( - first: ImportDeclarationCstNode, - second: ImportDeclarationCstNode - ) => - compareFqn( - first.children.packageOrTypeName![0], - second.children.packageOrTypeName![0] - ); - staticImports.sort(comparator); - nonStaticImports.sort(comparator); - } - - return { - staticImports, - nonStaticImports - }; -} - -function compareFqn( - packageOrTypeNameFirst: { children: { Identifier: IToken[] } }, - packageOrTypeNameSecond: { children: { Identifier: IToken[] } } -) { - const identifiersFirst = packageOrTypeNameFirst.children.Identifier; - const identifiersSecond = packageOrTypeNameSecond.children.Identifier; - - const minParts = Math.min(identifiersFirst.length, identifiersSecond.length); - for (let i = 0; i < minParts; i++) { - if (identifiersFirst[i].image < identifiersSecond[i].image) { - return -1; - } else if (identifiersFirst[i].image > identifiersSecond[i].image) { - return 1; - } - } - - if (identifiersFirst.length < identifiersSecond.length) { - return -1; - } else if (identifiersFirst.length > identifiersSecond.length) { - return 1; - } - - return 0; -} - -export function isUniqueMethodInvocation( - primarySuffixes: CstNode[] | undefined -) { - if (primarySuffixes === undefined) { - return 0; - } - - let count = 0; - primarySuffixes.forEach(primarySuffix => { - if (primarySuffix.children.methodInvocationSuffix !== undefined) { - count++; - - if (count > 1) { - return 2; - } - } - }); - - return count; -} - -export function printArrayList({ - list, - extraComma, - LCurly, - RCurly, - trailingComma -}: { - list: doc.builders.Doc; - extraComma: IToken[] | undefined; - LCurly: IToken; - RCurly: IToken; - trailingComma: any; -}) { - let optionalComma; - if (trailingComma !== "none" && list !== "") { - optionalComma = extraComma - ? ifBreak(extraComma[0], { ...extraComma[0], image: "" }) - : ifBreak(",", ""); - } else { - optionalComma = extraComma ? { ...extraComma[0], image: "" } : ""; - } - - return putIntoBraces( - rejectAndConcat([list, optionalComma]), - line, - LCurly, - RCurly - ); -} diff --git a/packages/prettier-plugin-java/src/printers/types-values-and-variables.ts b/packages/prettier-plugin-java/src/printers/types-values-and-variables.ts index c960a7312..1ea637b9a 100644 --- a/packages/prettier-plugin-java/src/printers/types-values-and-variables.ts +++ b/packages/prettier-plugin-java/src/printers/types-values-and-variables.ts @@ -1,241 +1,125 @@ -import forEach from "lodash/forEach.js"; import { builders } from "prettier/doc"; - -import { concat, group, indent, join } from "./prettier-builder.js"; -import { printTokenWithComments } from "./comments/format-comments.js"; -import { - putIntoBraces, - rejectAndConcat, - rejectAndJoin, - rejectAndJoinSeps, - sortClassTypeChildren -} from "./printer-utils.js"; import { - AdditionalBoundCtx, - AnnotationCstNode, - ClassOrInterfaceTypeCtx, - ClassTypeCtx, - CstNode, - DimsCtx, - FloatingPointTypeCtx, - IntegralTypeCtx, - InterfaceTypeCtx, - IToken, - NumericTypeCtx, - PrimitiveTypeCtx, - ReferenceTypeCtx, - TypeArgumentCtx, - TypeArgumentListCtx, - TypeArgumentsCtx, - TypeBoundCtx, - TypeParameterCtx, - TypeParameterModifierCtx, - TypeVariableCtx, - WildcardBoundsCtx, - WildcardCtx -} from "java-parser/api"; -import { BaseCstPrettierPrinter } from "../base-cst-printer.js"; -import { - isAnnotationCstNode, - isCstNode, - isTypeArgumentsCstNode -} from "../types/utils.js"; - -const { line, softline } = builders; - -export class TypesValuesAndVariablesPrettierVisitor extends BaseCstPrettierPrinter { - primitiveType(ctx: PrimitiveTypeCtx) { - const annotations = this.mapVisit(ctx.annotation); - const type = ctx.numericType - ? this.visit(ctx.numericType) - : (this.getSingle(ctx) as IToken); - - return rejectAndJoin(" ", [join(" ", annotations), type]); - } - - numericType(ctx: NumericTypeCtx) { - return this.visitSingle(ctx); - } - - integralType(ctx: IntegralTypeCtx) { - return printTokenWithComments(this.getSingle(ctx) as IToken); - } - - floatingPointType(ctx: FloatingPointTypeCtx) { - return printTokenWithComments(this.getSingle(ctx) as IToken); - } - - referenceType(ctx: ReferenceTypeCtx) { - const annotations = this.mapVisit(ctx.annotation); - - const type = ctx.primitiveType - ? this.visit(ctx.primitiveType) - : this.visit(ctx.classOrInterfaceType); + call, + definedKeys, + flatMap, + isNonTerminal, + map, + onlyDefinedKey, + printClassType, + printList, + printSingle, + type JavaNodePrinters +} from "./helpers.js"; + +const { group, indent, join, line, softline } = builders; + +export default { + primitiveType(path, print) { + const { children } = path.node; + const typeKey = onlyDefinedKey(children, ["Boolean", "numericType"]); + return join(" ", [ + ...map(path, print, "annotation"), + call(path, print, typeKey) + ]); + }, - const dims = this.visit(ctx.dims); + numericType: printSingle, + integralType: printSingle, + floatingPointType: printSingle, - return rejectAndJoin(" ", [join(" ", annotations), concat([type, dims])]); - } + referenceType(path, print) { + const { children } = path.node; + const typeKey = onlyDefinedKey(children, [ + "primitiveType", + "classOrInterfaceType" + ]); + const type = call(path, print, typeKey); + return join(" ", [ + ...map(path, print, "annotation"), + children.dims ? [type, call(path, print, "dims")] : type + ]); + }, - classOrInterfaceType(ctx: ClassOrInterfaceTypeCtx) { - return this.visitSingle(ctx); - } + classOrInterfaceType: printSingle, + classType: printClassType, + interfaceType: printSingle, - classType(ctx: ClassTypeCtx) { - const tokens = sortClassTypeChildren( - ctx.annotation, - ctx.typeArguments, - ctx.Identifier + typeVariable(path, print) { + return join(" ", [ + ...map(path, print, "annotation"), + call(path, print, "Identifier") + ]); + }, + + dims(path, print) { + return flatMap( + path, + childPath => { + const child = print(childPath); + return isNonTerminal(childPath.node) ? [child, " "] : child; + }, + definedKeys(path.node.children, ["annotation", "LSquare", "RSquare"]) ); - - const segments: any[] = []; - let currentSegment: any[] = []; - - forEach(tokens, (token, i) => { - if (isTypeArgumentsCstNode(token)) { - currentSegment.push(this.visit([token])); - segments.push(rejectAndConcat(currentSegment)); - currentSegment = []; - } else if (isAnnotationCstNode(token)) { - currentSegment.push(this.visit([token]), " "); - } else { - currentSegment.push(token); - if ( - (i + 1 < tokens.length && !isTypeArgumentsCstNode(tokens[i + 1])) || - i + 1 === tokens.length - ) { - segments.push(rejectAndConcat(currentSegment)); - currentSegment = []; - } - } - }); - - return rejectAndJoinSeps(ctx.Dot, segments); - } - - interfaceType(ctx: InterfaceTypeCtx) { - return this.visitSingle(ctx); - } - - typeVariable(ctx: TypeVariableCtx) { - const annotations = this.mapVisit(ctx.annotation); - const identifier = this.getSingle(ctx) as IToken; - - return rejectAndJoin(" ", [join(" ", annotations), identifier]); - } - - dims(ctx: DimsCtx) { - let tokens: (IToken | AnnotationCstNode)[] = [...ctx.LSquare]; - - if (ctx.annotation) { - tokens = [...tokens, ...ctx.annotation]; + }, + + typeParameter(path, print) { + const parameter = [ + ...map(path, print, "typeParameterModifier"), + call(path, print, "typeIdentifier") + ]; + if (path.node.children.typeBound) { + parameter.push(call(path, print, "typeBound")); } + return join(" ", parameter); + }, - tokens = tokens.sort((a, b) => { - const startOffset1 = isCstNode(a) - ? a.children.At[0].startOffset - : a.startOffset; - const startOffset2 = isCstNode(b) - ? b.children.At[0].startOffset - : b.startOffset; - return startOffset1 - startOffset2; - }); - - const segments: any[] = []; - let currentSegment: any[] = []; - - forEach(tokens, token => { - if (isCstNode(token)) { - currentSegment.push(this.visit([token])); - } else { - segments.push( - rejectAndConcat([ - rejectAndJoin(" ", currentSegment), - concat([ctx.LSquare[0], ctx.RSquare[0]]) - ]) - ); - currentSegment = []; - } - }); - - return rejectAndConcat(segments); - } - - typeParameter(ctx: TypeParameterCtx) { - const typeParameterModifiers = this.mapVisit(ctx.typeParameterModifier); - - const typeIdentifier = this.visit(ctx.typeIdentifier); - const typeBound = this.visit(ctx.typeBound); - - return rejectAndJoin(" ", [ - join(" ", typeParameterModifiers), - typeIdentifier, - typeBound - ]); - } - - typeParameterModifier(ctx: TypeParameterModifierCtx) { - return this.visitSingle(ctx); - } + typeParameterModifier: printSingle, - typeBound(ctx: TypeBoundCtx) { - const classOrInterfaceType = this.visit(ctx.classOrInterfaceType); - const additionalBound = this.mapVisit(ctx.additionalBound); - - return concat([ - rejectAndJoin(" ", [ctx.Extends[0], classOrInterfaceType]), - indent( + typeBound(path, print) { + const bound = ["extends ", call(path, print, "classOrInterfaceType")]; + if (path.node.children.additionalBound) { + bound.push( group( - concat([ - additionalBound.length ? line : "", - rejectAndJoin(line, additionalBound) - ]) + indent([line, ...join(line, map(path, print, "additionalBound"))]) ) - ) - ]); - } - - additionalBound(ctx: AdditionalBoundCtx) { - const interfaceType = this.visit(ctx.interfaceType); - - return join(" ", [ctx.And[0], interfaceType]); - } + ); + } + return bound; + }, - typeArguments(ctx: TypeArgumentsCtx) { - const typeArgumentList = this.visit(ctx.typeArgumentList); + additionalBound(path, print) { + return ["& ", call(path, print, "interfaceType")]; + }, - return putIntoBraces( - typeArgumentList, + typeArguments(path, print) { + return group([ + "<", + indent([softline, call(path, print, "typeArgumentList")]), softline, - ctx.Less[0], - ctx.Greater[0] - ); - } - - typeArgumentList(ctx: TypeArgumentListCtx) { - const typeArguments = this.mapVisit(ctx.typeArgument); - const commas = ctx.Comma ? ctx.Comma.map(elt => concat([elt, line])) : []; - return rejectAndJoinSeps(commas, typeArguments); - } + ">" + ]); + }, - typeArgument(ctx: TypeArgumentCtx) { - return this.visitSingle(ctx); - } + typeArgumentList(path, print) { + return printList(path, print, "typeArgument"); + }, - wildcard(ctx: WildcardCtx) { - const annotations = this.mapVisit(ctx.annotation); - const wildcardBounds = this.visit(ctx.wildcardBounds); + typeArgument: printSingle, - return rejectAndJoin(" ", [ - join(" ", annotations), - ctx.QuestionMark[0], - wildcardBounds - ]); - } + wildcard(path, print) { + const wildcard = [...map(path, print, "annotation"), "?"]; + if (path.node.children.wildcardBounds) { + wildcard.push(call(path, print, "wildcardBounds")); + } + return join(" ", wildcard); + }, - wildcardBounds(ctx: WildcardBoundsCtx) { - const keyWord = ctx.Extends ? ctx.Extends[0] : ctx.Super![0]; - const referenceType = this.visit(ctx.referenceType); - return join(" ", [keyWord, referenceType]); + wildcardBounds(path, print) { + return [ + path.node.children.Extends ? "extends" : "super", + " ", + call(path, print, "referenceType") + ]; } -} +} satisfies Partial; diff --git a/packages/prettier-plugin-java/src/types/utils.ts b/packages/prettier-plugin-java/src/types/utils.ts deleted file mode 100644 index fdb1b2459..000000000 --- a/packages/prettier-plugin-java/src/types/utils.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { - AbstractOrdinaryCompilationUnitCtx, - AnnotationCstNode, - CompilationUnitCtx, - CstElement, - CstNode, - IToken, - TypeArgumentsCstNode -} from "java-parser/api"; - -export function isCstNode(tokenOrNode: CstElement): tokenOrNode is CstNode { - return !isIToken(tokenOrNode); -} - -export function isIToken(tokenOrNode: CstElement): tokenOrNode is IToken { - return ( - (tokenOrNode as IToken).tokenType !== undefined && - (tokenOrNode as IToken).image !== undefined - ); -} - -export function isCstElementOrUndefinedIToken( - tokenOrNode: CstElement | undefined -): tokenOrNode is IToken { - return tokenOrNode !== undefined && isIToken(tokenOrNode); -} - -export const isTypeArgumentsCstNode = ( - cstElement: CstElement -): cstElement is TypeArgumentsCstNode => { - return (cstElement as CstNode).name === "typeArguments"; -}; - -export const isAnnotationCstNode = ( - cstElement: CstElement -): cstElement is AnnotationCstNode => { - return (cstElement as CstNode).name === "annotation"; -}; - -export const isOrdinaryCompilationUnitCtx = ( - ctx: CompilationUnitCtx -): ctx is AbstractOrdinaryCompilationUnitCtx => { - return ( - (ctx as AbstractOrdinaryCompilationUnitCtx).ordinaryCompilationUnit !== - undefined - ); -}; diff --git a/packages/prettier-plugin-java/src/utils/index.ts b/packages/prettier-plugin-java/src/utils/index.ts deleted file mode 100644 index a8dfd53cb..000000000 --- a/packages/prettier-plugin-java/src/utils/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default as printArgumentListWithBraces } from "./printArgumentListWithBraces.js"; -export { default as isEmptyDoc } from "./isEmptyDoc.js"; diff --git a/packages/prettier-plugin-java/src/utils/isEmptyDoc.ts b/packages/prettier-plugin-java/src/utils/isEmptyDoc.ts deleted file mode 100644 index 0cda517f7..000000000 --- a/packages/prettier-plugin-java/src/utils/isEmptyDoc.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Doc } from "prettier"; - -const isEmptyDoc = (argument: Doc) => { - return argument === "" || (Array.isArray(argument) && argument.length) === 0; -}; - -export default isEmptyDoc; diff --git a/packages/prettier-plugin-java/src/utils/printArgumentListWithBraces.ts b/packages/prettier-plugin-java/src/utils/printArgumentListWithBraces.ts deleted file mode 100644 index 957022862..000000000 --- a/packages/prettier-plugin-java/src/utils/printArgumentListWithBraces.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { ArgumentListCstNode, ArgumentListCtx, IToken } from "java-parser"; -import { builders } from "prettier/doc"; -import { handleCommentsParameters } from "../printers/comments/handle-comments.js"; -import { indent } from "../printers/prettier-builder.js"; -import { rejectAndConcat } from "../printers/printer-utils.js"; - -const { lineSuffixBoundary, softline } = builders; - -export default function printArgumentListWithBraces( - argumentListNodes: ArgumentListCstNode[] | undefined, - rBrace: IToken, - lBrace: IToken -) { - const argumentListNode = argumentListNodes?.[0]; - const expressions = argumentListNode?.children.expression ?? []; - if (argumentListNode) { - const { leadingComments, trailingComments } = argumentListNode; - delete argumentListNode.leadingComments; - delete argumentListNode.trailingComments; - if (leadingComments) { - const firstExpression = expressions[0]; - firstExpression.leadingComments = [ - ...leadingComments, - ...(firstExpression.leadingComments ?? []) - ]; - } - if (trailingComments) { - const lastExpression = expressions.at(-1)!; - lastExpression.trailingComments = [ - ...(lastExpression.trailingComments ?? []), - ...trailingComments - ]; - } - } - handleCommentsParameters(lBrace, expressions, rBrace); - - const argumentList = this.visit(argumentListNodes); - const contents = argumentList - ? [argumentList] - : lBrace.trailingComments - ? [softline, lineSuffixBoundary] - : []; - return rejectAndConcat([indent(lBrace), ...contents, rBrace]); -} diff --git a/packages/prettier-plugin-java/test/test-utils.ts b/packages/prettier-plugin-java/test/test-utils.ts index 46938be65..4537cb2cf 100644 --- a/packages/prettier-plugin-java/test/test-utils.ts +++ b/packages/prettier-plugin-java/test/test-utils.ts @@ -1,21 +1,17 @@ /*eslint no-console: ["error", { allow: ["error"] }] */ import { expect } from "chai"; +import { spawnSync } from "child_process"; import fs from "fs-extra"; -import { resolve, relative, basename, dirname } from "path"; import klawSync from "klaw-sync"; -import { spawnSync } from "child_process"; - -import { createPrettierDoc } from "../src/cst-printer.js"; -import { parse } from "java-parser"; -import { format, doc } from "prettier"; +import { basename, dirname, relative, resolve } from "path"; +import { format } from "prettier"; import url from "url"; +import plugin from "../src/index.js"; -const { printDocToString } = doc.printer; const { readFileSync, existsSync, removeSync, copySync } = fs; const __dirname = dirname(url.fileURLToPath(import.meta.url)); -const pluginPath = resolve(__dirname, "../src/index.js"); export function testSampleWithOptions({ testFolder, exclusive, @@ -40,26 +36,23 @@ export function testSampleWithOptions({ }); itOrItOnly(`can format <${relativeInputPath}>`, async () => { - const actual = await format(inputContents, { - parser: "java", - plugins: [pluginPath], - ...prettierOptions + const actual = await formatJavaSnippet({ + snippet: inputContents, + prettierOptions }); expect(actual).to.equal(expectedContents); }); it(`Performs a stable formatting for <${relativeInputPath}>`, async () => { - const onePass = await format(inputContents, { - parser: "java", - plugins: [pluginPath], - ...prettierOptions + const onePass = await formatJavaSnippet({ + snippet: inputContents, + prettierOptions }); - const secondPass = await format(onePass, { - parser: "java", - plugins: [pluginPath], - ...prettierOptions + const secondPass = await formatJavaSnippet({ + snippet: onePass, + prettierOptions }); expect(onePass).to.equal(secondPass); }); @@ -99,14 +92,8 @@ export function testRepositorySample( )}>`, async () => { const javaFileText = readFileSync(fileDesc.path, "utf8"); - const onePass = await format(javaFileText, { - parser: "java", - plugins: [pluginPath] - }); - const secondPass = await format(onePass, { - parser: "java", - plugins: [pluginPath] - }); + const onePass = await formatJavaSnippet({ snippet: javaFileText }); + const secondPass = await formatJavaSnippet({ snippet: onePass }); expect(onePass).to.equal(secondPass); }); }); @@ -135,19 +122,15 @@ export async function formatJavaSnippet({ prettierOptions = {} }: { snippet: string; - entryPoint: string; + entryPoint?: string; prettierOptions?: any; }) { - const node = parse(snippet, entryPoint); - const options = { - printWidth: 80, - tabWidth: 2, - trailingComma: "none", - useTabs: false, + return await format(snippet, { + parser: "java", + plugins: [plugin], + entrypoint: entryPoint, ...prettierOptions - }; - const doc = await createPrettierDoc(node, options); - return await printDocToString(doc, options).formatted; + }); } export async function expectSnippetToBeFormatted({ diff --git a/packages/prettier-plugin-java/test/unit-test/arrays/_output.java b/packages/prettier-plugin-java/test/unit-test/arrays/_output.java index dd745b23c..771a0867e 100644 --- a/packages/prettier-plugin-java/test/unit-test/arrays/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/arrays/_output.java @@ -5,8 +5,8 @@ class Array { Class aaaaaaaaaaaaaaaa = new Aaaaaaaaaaaaaaaa< Bbbbbbbbbbbbbbbb >[1].getClass(); - Class aaaaaaaaaaaaaaaa = new Aaaaaaaaaaaaaaaa[1111111111111111111] - .getClass(); + Class aaaaaaaaaaaaaaaa = + new Aaaaaaaaaaaaaaaa[1111111111111111111].getClass(); Class aaaaaaaaaaaaaaaa = new Aaaaaaaaaaaaaaaa[] { new Aaaaaaaaaaaaaaaa(), }.getClass(); diff --git a/packages/prettier-plugin-java/test/unit-test/binary_expressions/binary_expressions-spec.ts b/packages/prettier-plugin-java/test/unit-test/binary_expressions/binary_expressions-spec.ts index 90f36ba3f..003ccd460 100644 --- a/packages/prettier-plugin-java/test/unit-test/binary_expressions/binary_expressions-spec.ts +++ b/packages/prettier-plugin-java/test/unit-test/binary_expressions/binary_expressions-spec.ts @@ -1,9 +1,17 @@ import path from "path"; import url from "url"; -import { testSample } from "../../test-utils.js"; +import { testSample, testSampleWithOptions } from "../../test-utils.js"; const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); describe("prettier-java", () => { - testSample(__dirname); + testSampleWithOptions({ + testFolder: path.resolve(__dirname, "operator-position-start"), + prettierOptions: { experimentalOperatorPosition: "start" } + }); + testSampleWithOptions({ + testFolder: path.resolve(__dirname, "operator-position-end"), + prettierOptions: { experimentalOperatorPosition: "end" } + }); + testSample(path.resolve(__dirname, "operator-position-end")); }); diff --git a/packages/prettier-plugin-java/test/unit-test/binary_expressions/_input.java b/packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-end/_input.java similarity index 97% rename from packages/prettier-plugin-java/test/unit-test/binary_expressions/_input.java rename to packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-end/_input.java index 26d9004b9..94fc575ae 100644 --- a/packages/prettier-plugin-java/test/unit-test/binary_expressions/_input.java +++ b/packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-end/_input.java @@ -57,9 +57,6 @@ public boolean binaryOperationWithComments() { public void method() { new Foo(stuff, thing, "auaaaaaaaaa some very long stuff", "some more").bar(10); foo(stuff, thing, "some very longuuuuuuuuuuuuuu stuff", "some more").bar(10); - - // Issue 381 - new MethodWrappingFollowingContstructor().aLongEnoughMethodNameToForceThingsToWrap(); } public void binaryExpressionWithCast() { diff --git a/packages/prettier-plugin-java/test/unit-test/binary_expressions/_output.java b/packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-end/_output.java similarity index 98% rename from packages/prettier-plugin-java/test/unit-test/binary_expressions/_output.java rename to packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-end/_output.java index fe784cb4a..6877a4b99 100644 --- a/packages/prettier-plugin-java/test/unit-test/binary_expressions/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-end/_output.java @@ -53,9 +53,9 @@ public boolean binaryOperationWithComments() { boolean a = one || two >> 1 || // one - // five // two // three + // five // four three; @@ -83,10 +83,6 @@ public void method() { foo(stuff, thing, "some very longuuuuuuuuuuuuuu stuff", "some more").bar( 10 ); - - // Issue 381 - new MethodWrappingFollowingContstructor() - .aLongEnoughMethodNameToForceThingsToWrap(); } public void binaryExpressionWithCast() { diff --git a/packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-start/_input.java b/packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-start/_input.java new file mode 100644 index 000000000..94fc575ae --- /dev/null +++ b/packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-start/_input.java @@ -0,0 +1,135 @@ +public class BinaryOperations { + + public void binaryOperation() { + int alpha = (left) << right; + boolean beta = (left) < right; + } + + @Annotation("This operation with two very long string should break" + "in a very nice way") + public String binaryOperationThatShouldBreak() { + System.out.println("This operation with two very long string should break" + "in a very nice way"); + return "This operation with two very long string should break" + "in a very nice way"; + } + + @Annotation("This operation should" + "not break") + public String binaryOperationThatShouldNotBreak() { + System.out.println("This operation should" + "not break"); + return "This operation should" + "not break"; + } + + public int ternaryOperationThatShouldBreak() { + int shortInteger = thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne ? thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne : thisIsAShortInteger; + return thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne ? thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne : thisIsAShortInteger; + } + + public int ternaryOperationThatShouldBreak2() { + int shortInteger = thisIsAVeryLongInteger ? thisIsAnotherVeryLongOne : thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne; + return thisIsAVeryLongInteger ? thisIsAnotherVeryLongOne : thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne; + } + + public int ternaryOperationThatShouldNotBreak() { + int a = b ? b : c; + return b ? b : c; + } + + public boolean binaryOperationWithComments() { + boolean a = one || two >> 1 // one + // two + // three + || // five + // four + three; + + boolean b = one || two >> 1 // one + // two + // three + || + three; + + boolean c = one || two >> 1 // one + // two + // three + || three; + + return a || b || c; + } + + public void method() { + new Foo(stuff, thing, "auaaaaaaaaa some very long stuff", "some more").bar(10); + foo(stuff, thing, "some very longuuuuuuuuuuuuuu stuff", "some more").bar(10); + } + + public void binaryExpressionWithCast() { + double availability12 = (double) successfulCount / (successfulCount + failureCount); + availability12 = (double) successfulCount / (successfulCount + failureCount); + } + + void declarationVsAssignment() { + var lineLengthInAssignmentMoreThanPrintWidth = "1234567890" + "1234567890" + "1234567890" + "1234567890" + "1234567890" + "1234567890"; + lineLengthInAssignmentMoreThanPrintWidth = "1234567890" + "1234567890" + "1234567890" + "1234567890" + "1234567890" + "1234567890"; + + aaaaaaaaaa += bbbbbbbbbbb + ccccccccccc + ddddddddddd + eeeeeeeeee + ffffffffff + gggggggggg; + aaaaaaaaaa %= bbbbbbbbbbb + ccccccccccc + ddddddddddd + eeeeeeeeee + ffffffffff + gggggggggg; + aaaaaaaaaa <<= bbbbbbbbbbb + ccccccccccc + ddddddddddd + eeeeeeeeee + ffffffffff + gggggggggg; + aaaaaaaaaa &= bbbbbbbbbbb + ccccccccccc + ddddddddddd + eeeeeeeeee + ffffffffff + gggggggggg; + + var aaaaaaaaaa = bbbbbbbbbb || cccccccccc ? dddddddddd + eeeeeeeeee : ffffffffff + gggggggggg; + aaaaaaaaaa = bbbbbbbbbb || cccccccccc ? dddddddddd + eeeeeeeeee : ffffffffff + gggggggggg; + + var something = MyClass.staticFunction(aaaaaaaaaa, bbbbbbbbbbb, ccccccccccc, ddddddddddd); + something = MyClass.staticFunction(aaaaaaaaaa, bbbbbbbbbbb, ccccccccccc, ddddddddddd); + + var something = MyClass.staticFunction(aaaaaaaaaa, bbbbbbbbbbb, ccccccccccc, ddddddddddd) + 0; + something = MyClass.staticFunction(aaaaaaaaaa, bbbbbbbbbbb, ccccccccccc, ddddddddddd) + 0; + + var something12 = new MyClass(aaaaaaaaaa, bbbbbbbbbbb, ccccccccccc, ddddddddddd); + something12 = new MyClass(aaaaaaaaaa, bbbbbbbbbbb, ccccccccccc, ddddddddddd); + } + + void parentheses() { + var result = a + b >>> 1; + var sizeIndex = index - 1 >>> level & MASK; + var from = offset > left ? 0 : left - offset >> level; + var to = right - offset >> level + 1; + if (rawIndex < 1 << list._level + SHIFT) {} + var res = size < SIZE ? 0 : size - 1 >>> SHIFT << SHIFT; + sign = 1 - 2 * b[3] >> 7; + exponent = b[3] << 1 & 0xff | b[2] >> 7 - 127; + mantissa = b[2] & 0x7f << 16 | b[1] << 8 | b[0]; + + 2 / 3 * 10 / 2 + 2; + 2 * 3 * 10 / 2 + 2; + var rotateX = RANGE / rect.height * refY - RANGE / 2 * getXMultiplication(rect.width); + var rotateY = RANGE / rect.width * refX - RANGE / 2 * getYMultiplication(rect.width); + + a % 10 - 5; + a - 10 % 5; + a * b % 10; + a % b * 10; + a % 10 > 5; + a % 10 == 0; + + 1 << 2 >>> 3 >> 4; + 1 >>> 2 >> 3 << 4; + + 1 << 2 + 3; + 1 >> 2 - 3; + 1 >>> 2 * 3; + 1 / 2 << 3; + 1 + 2 >> 3; + 1 - 2 >>> 3; + + x == y == z; + x != y == z; + x == y != z; + x != y != z; + + 1 & 2 == 3; + + if (aaaaaaaaaa + bbbbbbbbbb == cccccccccc + dddddddddd && eeeeeeeeee + ffffffffff == gggggggggg + hhhhhhhhhh || iiiiiiiiii) {} + + if (a * b + c << d < e == f & g ^ h | i && j || k && l | m ^ n & o != p > q >> r - s / t) {} + + if (aaaaaaaaaa + bbbbbbbbbb == cccccccccc + dddddddddd && eeeeeeeeee + ffffffffff == gggggggggg + hhhhhhhhhh || iiiiiiiiii + jjjjjjjjjj == kkkkkkkkkk + llllllllll && mmmmmmmmmm + nnnnnnnnnn == oooooooooo + pppppppppp || qqqqqqqqqq + rrrrrrrrrr == ssssssssss + tttttttttt && uuuuuuuuuu + vvvvvvvvvv == wwwwwwwwww + xxxxxxxxxxx) {} + } +} diff --git a/packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-start/_output.java b/packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-start/_output.java new file mode 100644 index 000000000..43699f128 --- /dev/null +++ b/packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-start/_output.java @@ -0,0 +1,248 @@ +public class BinaryOperations { + + public void binaryOperation() { + int alpha = (left) << right; + boolean beta = (left) < right; + } + + @Annotation( + "This operation with two very long string should break" + + "in a very nice way" + ) + public String binaryOperationThatShouldBreak() { + System.out.println( + "This operation with two very long string should break" + + "in a very nice way" + ); + return ( + "This operation with two very long string should break" + + "in a very nice way" + ); + } + + @Annotation("This operation should" + "not break") + public String binaryOperationThatShouldNotBreak() { + System.out.println("This operation should" + "not break"); + return "This operation should" + "not break"; + } + + public int ternaryOperationThatShouldBreak() { + int shortInteger = thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne + ? thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne + : thisIsAShortInteger; + return thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne + ? thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne + : thisIsAShortInteger; + } + + public int ternaryOperationThatShouldBreak2() { + int shortInteger = thisIsAVeryLongInteger + ? thisIsAnotherVeryLongOne + : thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne; + return thisIsAVeryLongInteger + ? thisIsAnotherVeryLongOne + : thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne; + } + + public int ternaryOperationThatShouldNotBreak() { + int a = b ? b : c; + return b ? b : c; + } + + public boolean binaryOperationWithComments() { + boolean a = + one + || two >> 1 + // one + // two + // three + // five + // four + || three; + + boolean b = + one + || two >> 1 + // one + // two + // three + || three; + + boolean c = + one + || two >> 1 + // one + // two + // three + || three; + + return a || b || c; + } + + public void method() { + new Foo(stuff, thing, "auaaaaaaaaa some very long stuff", "some more").bar( + 10 + ); + foo(stuff, thing, "some very longuuuuuuuuuuuuuu stuff", "some more").bar( + 10 + ); + } + + public void binaryExpressionWithCast() { + double availability12 = + (double) successfulCount / (successfulCount + failureCount); + availability12 = + (double) successfulCount / (successfulCount + failureCount); + } + + void declarationVsAssignment() { + var lineLengthInAssignmentMoreThanPrintWidth = + "1234567890" + + "1234567890" + + "1234567890" + + "1234567890" + + "1234567890" + + "1234567890"; + lineLengthInAssignmentMoreThanPrintWidth = + "1234567890" + + "1234567890" + + "1234567890" + + "1234567890" + + "1234567890" + + "1234567890"; + + aaaaaaaaaa += + bbbbbbbbbbb + + ccccccccccc + + ddddddddddd + + eeeeeeeeee + + ffffffffff + + gggggggggg; + aaaaaaaaaa %= + bbbbbbbbbbb + + ccccccccccc + + ddddddddddd + + eeeeeeeeee + + ffffffffff + + gggggggggg; + aaaaaaaaaa <<= + bbbbbbbbbbb + + ccccccccccc + + ddddddddddd + + eeeeeeeeee + + ffffffffff + + gggggggggg; + aaaaaaaaaa &= + bbbbbbbbbbb + + ccccccccccc + + ddddddddddd + + eeeeeeeeee + + ffffffffff + + gggggggggg; + + var aaaaaaaaaa = bbbbbbbbbb || cccccccccc + ? dddddddddd + eeeeeeeeee + : ffffffffff + gggggggggg; + aaaaaaaaaa = bbbbbbbbbb || cccccccccc + ? dddddddddd + eeeeeeeeee + : ffffffffff + gggggggggg; + + var something = MyClass.staticFunction( + aaaaaaaaaa, + bbbbbbbbbbb, + ccccccccccc, + ddddddddddd + ); + something = MyClass.staticFunction( + aaaaaaaaaa, + bbbbbbbbbbb, + ccccccccccc, + ddddddddddd + ); + + var something = + MyClass.staticFunction(aaaaaaaaaa, bbbbbbbbbbb, ccccccccccc, ddddddddddd) + + 0; + something = + MyClass.staticFunction(aaaaaaaaaa, bbbbbbbbbbb, ccccccccccc, ddddddddddd) + + 0; + + var something12 = new MyClass( + aaaaaaaaaa, + bbbbbbbbbbb, + ccccccccccc, + ddddddddddd + ); + something12 = new MyClass( + aaaaaaaaaa, + bbbbbbbbbbb, + ccccccccccc, + ddddddddddd + ); + } + + void parentheses() { + var result = (a + b) >>> 1; + var sizeIndex = ((index - 1) >>> level) & MASK; + var from = offset > left ? 0 : (left - offset) >> level; + var to = (right - offset) >> (level + 1); + if (rawIndex < 1 << (list._level + SHIFT)) {} + var res = size < SIZE ? 0 : ((size - 1) >>> SHIFT) << SHIFT; + sign = (1 - 2 * b[3]) >> 7; + exponent = ((b[3] << 1) & 0xff) | (b[2] >> (7 - 127)); + mantissa = (b[2] & (0x7f << 16)) | (b[1] << 8) | b[0]; + + ((2 / 3) * 10) / 2 + 2; + (2 * 3 * 10) / 2 + 2; + var rotateX = + (RANGE / rect.height) * refY + - (RANGE / 2) * getXMultiplication(rect.width); + var rotateY = + (RANGE / rect.width) * refX + - (RANGE / 2) * getYMultiplication(rect.width); + + (a % 10) - 5; + a - (10 % 5); + (a * b) % 10; + (a % b) * 10; + a % 10 > 5; + a % 10 == 0; + + ((1 << 2) >>> 3) >> 4; + ((1 >>> 2) >> 3) << 4; + + 1 << (2 + 3); + 1 >> (2 - 3); + 1 >>> (2 * 3); + (1 / 2) << 3; + (1 + 2) >> 3; + (1 - 2) >>> 3; + + (x == y) == z; + (x != y) == z; + (x == y) != z; + (x != y) != z; + + 1 & (2 == 3); + + if ( + (aaaaaaaaaa + bbbbbbbbbb == cccccccccc + dddddddddd + && eeeeeeeeee + ffffffffff == gggggggggg + hhhhhhhhhh) + || iiiiiiiiii + ) {} + + if ( + (((((a * b + c) << d < e == f) & g) ^ h) | i && j) + || (k && l | (m ^ (n & (o != p > q >> (r - s / t))))) + ) {} + + if ( + (aaaaaaaaaa + bbbbbbbbbb == cccccccccc + dddddddddd + && eeeeeeeeee + ffffffffff == gggggggggg + hhhhhhhhhh) + || (iiiiiiiiii + jjjjjjjjjj == kkkkkkkkkk + llllllllll + && mmmmmmmmmm + nnnnnnnnnn == oooooooooo + pppppppppp) + || (qqqqqqqqqq + rrrrrrrrrr == ssssssssss + tttttttttt + && uuuuuuuuuu + vvvvvvvvvv == wwwwwwwwww + xxxxxxxxxxx) + ) {} + } +} diff --git a/packages/prettier-plugin-java/test/unit-test/blank_lines/_output.java b/packages/prettier-plugin-java/test/unit-test/blank_lines/_output.java index 5c7937322..00bcb2d80 100644 --- a/packages/prettier-plugin-java/test/unit-test/blank_lines/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/blank_lines/_output.java @@ -84,7 +84,6 @@ interface BlankLinesInInterfaces { private static String test(); private @Nullable String test(); - private static String test(); @Nullable diff --git a/packages/prettier-plugin-java/test/unit-test/comments/bug-fixes/_output.java b/packages/prettier-plugin-java/test/unit-test/comments/bug-fixes/_output.java index 6dc7a9d4d..8335dca26 100644 --- a/packages/prettier-plugin-java/test/unit-test/comments/bug-fixes/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/comments/bug-fixes/_output.java @@ -35,7 +35,8 @@ void t() {} public static final List XXXXXXXXXXXXXXXXXX = Collections.unmodifiableList( - Arrays.asList( // a + Arrays.asList( + // a // b // c // d @@ -44,7 +45,8 @@ void t() {} public static final List XXXXXXXXXXXXXXXXXX = Collections.unmodifiableList( - Arrays.asList( // a + Arrays.asList( + // a // b // c // d diff --git a/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/complex/_output.java b/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/complex/_output.java index c6cb6b880..aeeba5866 100644 --- a/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/complex/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/complex/_output.java @@ -5,76 +5,83 @@ public class PrettierTest { public void myFunction(int arg1) { try { // Empty Statement - } /*catch*/catch (EmptyStackException e) { + } /*catch*/ catch (EmptyStackException e) { throw new RuntimeException(e); - } /*multi-catch*/catch ( - /*1*/FirstException | /*2*/SecondException | /*3*/ThirdException e2 + } /*multi-catch*/ catch ( + /*1*/ FirstException + | /*2*/ SecondException + | /*3*/ ThirdException e2 ) { - throw /*throw an exception*/new /*don't forget new when throwing exceptions*/RuntimeException( + throw /*throw an exception*/ new /*don't forget new when throwing exceptions*/ RuntimeException( e2 ); - } /*is always executed no matter what*/finally { + } /*is always executed no matter what*/ finally { System.out.println("That's all folks !"); } } private void myFunction( - /* axis x */int arg1, - /* axis y */int arg2, - /* axis z */int arg3 + /* axis x */ int arg1, + /* axis y */ int arg2, + /* axis z */ int arg3 ) { if (arg1 == 0 && arg2 == 0 && arg == 3) throw new RuntimeException( "X Y Z cannot be all 0" ); - int /*variable name is of value var */var = arg1 + arg2 + arg3; - if /*true*/(var == 0) { + int /*variable name is of value var */ var = arg1 + arg2 + arg3; + if (/*true*/ var == 0) { System.out.println("The value is 0"); - } else /*false*/{ + } else /*false*/ { int[] arr = { - /*One*/1, - /*Two */2, - /*zero*/0, - /*One again*/1, - -1/*Minus One*/, + /*One*/ 1, + /*Two */ 2, + /*zero*/ 0, + /*One again*/ 1, + -1 /*Minus One*/, 0, 2, }; // Label statement + //foreach - loop: for (int num /* num is every number in arr*/: arr) { - /*switch*/switch (num) { //switch + loop: for (int num /* num is every number in arr*/ : arr) { + /*switch*/ switch (num) { + //switch case 1: System.out.println("One "); System.out.println("One "); System.out.println("One "); - /*just a break*/break; + /*just a break*/ break; case 2: System.out.println("Two "); break; case 0: System.out.println("Zero "); - continue /*labeled continued*/loop; - default/*def*/: - /*labeled break*/break loop; + continue /*labeled continued*/ loop; + default /*def*/: + /*labeled break*/ break loop; } } } } - private synchronized void myFunction(int arg1, int arg2/*overloading*/) { - for (int i = 0; i < /*=*/arg1; i++) do /*dodododo*/{ //do whiles + private synchronized void myFunction(int arg1, int arg2 /*overloading*/) { + for (int i = 0; i < /*=*/ arg1; i++) do /*dodododo*/ { + //do whiles //asserting - assert /*true*/true == true; + assert /*true*/ true == true; continue; - break/*dead code*/; - return/*dead code*/; - } /*at least one iteration !*/while (false); - synchronized /*declares synchronizd statement*/(this) { - while /*infinite*/(true) /*stop the program*/throw new RuntimeException(); + break; /*dead code*/ + return; /*dead code*/ + } /*at least one iteration !*/ while (false); + synchronized (/*declares synchronizd statement*/ this) { + while ( + /*infinite*/ true + ) /*stop the program*/ throw new RuntimeException(); } } } @@ -82,6 +89,6 @@ private synchronized void myFunction(int arg1, int arg2/*overloading*/) { //Additionnal enumeration enum Cards { //The Heart and the Spade - HEART/*the heart*/, - SPADES,/*and the spade*/ + HEART /*the heart*/, + SPADES /*and the spade*/, } diff --git a/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/end-of-block/_output.java b/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/end-of-block/_output.java index 89884e3d3..43ad9c332 100644 --- a/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/end-of-block/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/end-of-block/_output.java @@ -15,7 +15,8 @@ class D { /* gamma */ } -class E { // alpha +class E { + // alpha } class F { @@ -32,10 +33,12 @@ class H { // beta } -class I { // alpha +class I { + // alpha // beta int i; + // one // two /* three */ @@ -45,44 +48,36 @@ class J { void one() {} - void two( - // alpha - ) {} + void two() {} + // alpha - void three( - // alpha - // beta - ) {} + void three() {} + // alpha + // beta - void four( - // alpha - // beta - /* gamma */ - ) {} + void four() {} + // alpha + // beta + /* gamma */ - void five( // alpha - ) {} + void five() {} // alpha - void fiveBis( // alpha - ) { + void fiveBis() { // alpha int i; } void six(/* alpha */) {} - void seven( - /* alpha */ - /* beta */ - ) {} + void seven() {} + /* alpha */ + /* beta */ - void eight( - /* alpha */ - // beta - ) {} + void eight() {} + /* alpha */ + // beta - void nine( - /* alpha */ - ) {} + void nine() {} + /* alpha */ void one(String one) {} @@ -108,7 +103,7 @@ void five( String one // alpha ) {} - void six(String one/* alpha */) {} + void six(String one /* alpha */) {} void seven( /* alpha */ diff --git a/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/if-statement/_input.java b/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/if-statement/_input.java index d53f91789..9d2e63582 100644 --- a/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/if-statement/_input.java +++ b/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/if-statement/_input.java @@ -15,6 +15,11 @@ void commentsIfLineComment() { if ( // test t) { } + + if (true) // comment + { + System.out.println("Oops"); + } } void commentsIfBlockComment() { diff --git a/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/if-statement/_output.java b/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/if-statement/_output.java index 217808bd6..d70daf12f 100644 --- a/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/if-statement/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/if-statement/_output.java @@ -1,7 +1,8 @@ class IfStatements { void commentsIfLineComment() { - if ( // test + if ( + // test t ) {} @@ -11,31 +12,39 @@ void commentsIfLineComment() { if (t) {} // test - if ( // test + if ( + // test t ) {} + + if ( + true // comment + ) { + System.out.println("Oops"); + } } void commentsIfBlockComment() { - if (/* test */t) {} + if (/* test */ t) {} - if (t/* test */) {} + if (t /* test */) {} - if (t) /* test */{} + if (t) /* test */ {} - if /* test */(t) {} + if (/* test */ t) {} } void commentsElseLineComment() { - if (t) {} // test + if (t) {} + // test else {} if (t) {} else {} // test } void commentsElseBlockComment() { - if (t) {} /* test */else {} + if (t) {} /* test */ else {} - if (t) {} else /* test */{} + if (t) {} else /* test */ {} } } diff --git a/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/labeled-statement/_output.java b/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/labeled-statement/_output.java index d2c5ec58e..957d65c60 100644 --- a/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/labeled-statement/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/comments/comments-blocks-and-statements/labeled-statement/_output.java @@ -2,6 +2,7 @@ class LabeledStatements { void commentsLabeledStatementLineComment() { // Label statement + // comment1 loop: for (int num : arr) {} @@ -30,6 +31,7 @@ void commentsLabeledStatementLineComment() { void commentsLabeledStatementBlockComment() { /* Label statement */ + /* comment1 */ loop: for (int num : arr) {} @@ -58,10 +60,12 @@ void commentsLabeledStatementBlockComment() { void commentsLabeledStatementMixedComment() { // Label statement + /* comment1 */ loop: for (int num : arr) {} /* Label statement */ + // comment1 loop: for (int num : arr) {} diff --git a/packages/prettier-plugin-java/test/unit-test/comments/interface/_output.java b/packages/prettier-plugin-java/test/unit-test/comments/interface/_output.java index 06941e876..239c65199 100644 --- a/packages/prettier-plugin-java/test/unit-test/comments/interface/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/comments/interface/_output.java @@ -4,8 +4,8 @@ /** * This is the comment describing the interface */ -public /*a*/interface /*b*/MyInterface - /*a*/extends /*b*//*a*/OfferedI/*b*//*a*/, /*b*//*a*/RequiredI /*b*/{ +public /*a*/ interface /*b*/ MyInterface + /*a*/ extends /*b*/ /*a*/ OfferedI /*b*/ /*a*/, /*b*/ /*a*/ RequiredI /*b*/ { // comment /** * Javadoc @@ -15,8 +15,8 @@ * @throws RuntimeException RuntimeException comment */ public void myMethodInterface( - Param1 /*a*/p1/*b*//*a*/, - /*b*//*a*/Param2 /*b*//*a*/p2/*b*/, + Param1 /*a*/ p1 /*b*/ /*a*/, + /*b*/ /*a*/ Param2 /*b*/ /*a*/ p2 /*b*/, Param3 p3 - ) /*a*/throws /*b*/Exception/*a*/, /*b*/RuntimeException /*a*/;/*b*/ + ) /*a*/ throws /*b*/ Exception /*a*/, /*b*/ RuntimeException /*a*/; /*b*/ } diff --git a/packages/prettier-plugin-java/test/unit-test/comments/package/_output.java b/packages/prettier-plugin-java/test/unit-test/comments/package/_output.java index 04769f8b9..aa7a66efd 100644 --- a/packages/prettier-plugin-java/test/unit-test/comments/package/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/comments/package/_output.java @@ -1,14 +1,15 @@ -/*a*/open /*b*//*a*/module /*b*/soat/*a*/./*b*/vending.machine.gui { - /*a*/requires /*a*/java.desktopa/*a*/;/*b*/ +/*a*/ open module /*b*/ /*a*/ /*b*/ soat /*a*/ /*b*/.vending.machine.gui { +/*a*/ + requires /*a*/ java.desktopa /*a*/; /*b*/ requires soat.vending.machine.model; - requires /*a*/transitive /*b*/soat.core; - /*a*/exports /*b*/fr.soat.vending.machine.model - /*a*/to /*b*/another/*a*/, /*b*/again/*c*/, /*d*/ano/*a*/;/*b*/ + requires /*a*/ transitive /*b*/ soat.core; + /*a*/ exports /*b*/ fr.soat.vending.machine.model /*a*/ + to /*b*/ another /*a*/, /*b*/ again /*c*/, /*d*/ ano /*a*/; /*b*/ // opens - /*a*/opens /*b*/fr.soat.vending.machine.model - /*a*/to /*b*/another/*a*/, /*b*/again/*c*/, /*d*/ano/*a*/;/*b*/ + /*a*/ opens /*b*/ fr.soat.vending.machine.model /*a*/ + to /*b*/ another /*a*/, /*b*/ again /*c*/, /*d*/ ano /*a*/; /*b*/ // uses - /*a*/uses /*b*/fr.soat.vendinga/*a*/./*b*/machine.services.DrinksService/*a*/;/*b*/ + /*a*/ uses /*b*/ fr.soat.vendinga /*a*/ /*b*/.machine.services.DrinksService /*a*/; /*b*/ } diff --git a/packages/prettier-plugin-java/test/unit-test/empty_statement/_output.java b/packages/prettier-plugin-java/test/unit-test/empty_statement/_output.java index ccfa8b385..567ac3bef 100644 --- a/packages/prettier-plugin-java/test/unit-test/empty_statement/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/empty_statement/_output.java @@ -11,29 +11,33 @@ public void emptyStatementWithComment() { public void simpleForWithEmptyStatement() { for (;;); - for (;;) /*test*/; + /*test*/ + for (;;); - for (;;);/*test*/ + for (;;); /*test*/ - for (;;) /*test*/;/*test*/ + /*test*/ + for (;;); /*test*/ } public void simpleForWithEmptyStatement() { for (;;); - for (;;) /*test*/; + /*test*/ + for (;;); - for (;;);/*test*/ + for (;;); /*test*/ - for (;;) /*test*/;/*test*/ + /*test*/ + for (;;); /*test*/ } public void forEachWithEmptyStatement(List list) { for (String str : list); - for (String str : list) /*test*/; + for (String str : list /*test*/); - for (String str : list);/*test*/ + for (String str : list); /*test*/ } public void ifElseWithEmptyStatements() { @@ -51,43 +55,43 @@ public void ifElseWithEmptyStatements() { } public void ifElseWithEmptyStatementsWithComments() { - if (test) /*test*/; + if (test /*test*/); else { System.out.println("one"); } - if (test); - /*test*/else { + if (test /*test*/); + else { System.out.println("one"); } if (test) { System.out.println("two"); - } else /*test*/; + } /*test*/ else; if (test) { System.out.println("two"); - } else;/*test*/ + } else; /*test*/ - if (test); - /*test*/else;/*test*/ + if (test /*test*/); + else; /*test*/ - if (test) /*test*/; - else /*test*/; + if (test /*test*/ /*test*/); + else; } public void simpleWhileWithEmptyStatement(boolean one) { while (one); - while (one) /*test*/; + while (one /*test*/); - while (one);/*test*/ + while (one); /*test*/ } public void doWhileWithEmptyStatement(boolean one) { do; while (one); - do /*test*/; while (one); - do; /*test*/while (one); + do; while (/*test*/ one); + do; while (/*test*/ one); } } diff --git a/packages/prettier-plugin-java/test/unit-test/enum/_output.java b/packages/prettier-plugin-java/test/unit-test/enum/_output.java index 971418a28..e0f706742 100644 --- a/packages/prettier-plugin-java/test/unit-test/enum/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/enum/_output.java @@ -25,13 +25,13 @@ public enum EnumWithExtraCommaAndExtraSemicolon { public enum EnumWithExtraCommaAndComment { SOME_ENUM, ANOTHER_ENUM, - LAST_ENUM/* comment */, + LAST_ENUM /* comment */, } public enum EnumWithExtraSemicolonAndComment { SOME_ENUM, ANOTHER_ENUM, - LAST_ENUM,/* comment */ + LAST_ENUM /* comment */, } public enum EnumWithManyValues { @@ -139,13 +139,12 @@ public enum EmptyEnumWithComment { } enum PrettierErrors { - WE_LOVE_PRETTIER + WE_LOVE_PRETTIER; + // And I can't believe // it is free // why are people // so damn generous - ; - void printTest() { System.out.println("Hey there"); } diff --git a/packages/prettier-plugin-java/test/unit-test/expressions/_output.java b/packages/prettier-plugin-java/test/unit-test/expressions/_output.java index 5e81ddd8f..a42b67cf7 100644 --- a/packages/prettier-plugin-java/test/unit-test/expressions/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/expressions/_output.java @@ -122,7 +122,7 @@ public void printSwitch() { } public void printWhile() { - while /*infinite*/(true) /*stop the program*/throw new RuntimeException(); + while (/*infinite*/ true) /*stop the program*/ throw new RuntimeException(); while ( myValue == 42 || @@ -194,8 +194,7 @@ public void printSynchronized() { } public void longFullyQualifiedName() { - com.me.very.very.very.very.very.very.very.very.very.very.very.very.very.longg.fully.qualified.name.FullyQualifiedName.builder() - .build(); + com.me.very.very.very.very.very.very.very.very.very.very.very.very.very.longg.fully.qualified.name.FullyQualifiedName.builder().build(); com.FullyQualifiedName.builder(); } diff --git a/packages/prettier-plugin-java/test/unit-test/for/_input.java b/packages/prettier-plugin-java/test/unit-test/for/_input.java index f6f74454e..94e3d855c 100644 --- a/packages/prettier-plugin-java/test/unit-test/for/_input.java +++ b/packages/prettier-plugin-java/test/unit-test/for/_input.java @@ -36,4 +36,8 @@ public void continueWithIdentifier() { } } + void nested() { + for(SomeClass elem : elements) for(SomeClass elem : elements) for(SomeClass elem : elements) + doSomeThing(); + } } diff --git a/packages/prettier-plugin-java/test/unit-test/for/_output.java b/packages/prettier-plugin-java/test/unit-test/for/_output.java index fe269b6fc..5fc4dad65 100644 --- a/packages/prettier-plugin-java/test/unit-test/for/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/for/_output.java @@ -35,4 +35,10 @@ public void continueWithIdentifier() { System.out.println(i); } } + + void nested() { + for (SomeClass elem : elements) + for (SomeClass elem : elements) + for (SomeClass elem : elements) doSomeThing(); + } } diff --git a/packages/prettier-plugin-java/test/unit-test/instantiation/_input.java b/packages/prettier-plugin-java/test/unit-test/instantiation/_input.java index 407a9f903..a86f301f9 100644 --- a/packages/prettier-plugin-java/test/unit-test/instantiation/_input.java +++ b/packages/prettier-plugin-java/test/unit-test/instantiation/_input.java @@ -9,7 +9,7 @@ public void instantiation() { "should", "wrap" ); - new MethodWrappingFollowingContstructor().aLongEnoughMethodNameToForceThingsToWrap(); + new MethodFollowingConstructor().aLongEnoughMethodNameToExtendPastPrintWidth(); } } \ No newline at end of file diff --git a/packages/prettier-plugin-java/test/unit-test/instantiation/_output.java b/packages/prettier-plugin-java/test/unit-test/instantiation/_output.java index d751669a7..17f28bc63 100644 --- a/packages/prettier-plugin-java/test/unit-test/instantiation/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/instantiation/_output.java @@ -22,7 +22,6 @@ public void instantiation() { "wrap" ); - new MethodWrappingFollowingContstructor() - .aLongEnoughMethodNameToForceThingsToWrap(); + new MethodFollowingConstructor().aLongEnoughMethodNameToExtendPastPrintWidth(); } } diff --git a/packages/prettier-plugin-java/test/unit-test/lambda/_output.java b/packages/prettier-plugin-java/test/unit-test/lambda/_output.java index cb1bd434c..9b90eef12 100644 --- a/packages/prettier-plugin-java/test/unit-test/lambda/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/lambda/_output.java @@ -410,20 +410,13 @@ void huggableArguments() { ) -> e.f() ); - a(/* comment */(b, c, d) -> e.f()); + a(/* comment */ (b, c, d) -> e.f()); - a( - ( - /* comment */ - b, - c, - d - ) -> e.f() - ); + a((/* comment */ b, c, d) -> e.f()); - a((b, /* comment */c, d) -> e.f()); + a((b, /* comment */ c, d) -> e.f()); - a((b, c, d/* comment */) -> e.f()); + a((b, c, d /* comment */) -> e.f()); a( ( @@ -453,7 +446,7 @@ void huggableArguments() { ); aaaaaaaaaaaaaaaaaaaaaaaa( - /* comment */( + /* comment */ ( bbbbbbbbbbbbbbbbbbbbbbbb, cccccccccccccccccccccccc, dddddddddddddddddddddddd diff --git a/packages/prettier-plugin-java/test/unit-test/member_chain/_output.java b/packages/prettier-plugin-java/test/unit-test/member_chain/_output.java index a9d4abf4a..d21d7ac08 100644 --- a/packages/prettier-plugin-java/test/unit-test/member_chain/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/member_chain/_output.java @@ -32,18 +32,15 @@ public void doSomethingWithComment() { .something() .more(); - java - // comment - .averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong.Object.something() + java// comment + .averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong.Object.something() .more(); - java.averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong - // comment - .Object.something() + java.averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong// comment + .Object.something() .more(); - java.averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong.Object.something() - .more(); + java.averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong.Object.something().more(); Object.something() // comment @@ -54,9 +51,8 @@ public void doSomethingWithComment() { .util() .java.java(); - averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong - // comment - .java + averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong// comment + .java .util() .java.java(); @@ -65,12 +61,13 @@ public void doSomethingWithComment() { .util() .java.java(); - averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong.java/* comment */ + averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong.java + /* comment */ .util() .java.java(); averyveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylong.java - /* comment */.util() + /* comment */ .util() .java.java(); } diff --git a/packages/prettier-plugin-java/test/unit-test/records/_output.java b/packages/prettier-plugin-java/test/unit-test/records/_output.java index 4156415cd..50a7e1303 100644 --- a/packages/prettier-plugin-java/test/unit-test/records/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/records/_output.java @@ -122,8 +122,7 @@ record MySplitRecord( String param, String param, String param - ) - implements MyInterface {} + ) implements MyInterface {} } public record Record( diff --git a/packages/prettier-plugin-java/test/unit-test/snippets/classes/variableDeclarator/test.spec.ts b/packages/prettier-plugin-java/test/unit-test/snippets/classes/variableDeclarator/test.spec.ts index 7cb2cba63..c7016928d 100644 --- a/packages/prettier-plugin-java/test/unit-test/snippets/classes/variableDeclarator/test.spec.ts +++ b/packages/prettier-plugin-java/test/unit-test/snippets/classes/variableDeclarator/test.spec.ts @@ -27,7 +27,7 @@ describe("VariableDeclarator", () => { const formattedText = await formatJavaSnippet({ snippet, entryPoint }); const expectedContents = - "i = {\n alpha,\n beta,\n gamma,\n delta,\n epsilon,\n zeta,\n eta,\n theta,\n iota,\n kappa,\n lambda,\n mu,\n nu,\n xi\n}"; + "i = {\n alpha,\n beta,\n gamma,\n delta,\n epsilon,\n zeta,\n eta,\n theta,\n iota,\n kappa,\n lambda,\n mu,\n nu,\n xi,\n}"; expect(formattedText).to.equal(expectedContents); }); diff --git a/packages/prettier-plugin-java/test/unit-test/snippets/types-values-and-variables/numericType/test.spec.ts b/packages/prettier-plugin-java/test/unit-test/snippets/types-values-and-variables/numericType/test.spec.ts index 2301bcc34..9af2d2d4b 100644 --- a/packages/prettier-plugin-java/test/unit-test/snippets/types-values-and-variables/numericType/test.spec.ts +++ b/packages/prettier-plugin-java/test/unit-test/snippets/types-values-and-variables/numericType/test.spec.ts @@ -1,34 +1,11 @@ -import { BaseCstPrettierPrinter } from "../../../../../src/base-cst-printer.js"; -import { assert, spy } from "sinon"; import { formatJavaSnippet } from "../../../../test-utils.js"; describe("numericType", () => { - let integralTypeSpy: any; - let floatingPointTypeSpy: any; - - // eslint-disable-next-line no-undef - beforeEach(() => { - if (integralTypeSpy !== undefined) { - integralTypeSpy.restore(); - } - if (floatingPointTypeSpy !== undefined) { - floatingPointTypeSpy.restore(); - } - - integralTypeSpy = spy(BaseCstPrettierPrinter.prototype, "integralType"); - floatingPointTypeSpy = spy( - BaseCstPrettierPrinter.prototype, - "floatingPointType" - ); - }); - it("can format byte keyword", async () => { const snippet = "byte"; const entryPoint = "numericType"; await formatJavaSnippet({ snippet, entryPoint }); - assert.callCount(integralTypeSpy, 1); - assert.callCount(floatingPointTypeSpy, 0); }); it("can format double keyword", async () => { @@ -36,7 +13,5 @@ describe("numericType", () => { const entryPoint = "numericType"; await formatJavaSnippet({ snippet, entryPoint }); - assert.callCount(integralTypeSpy, 0); - assert.callCount(floatingPointTypeSpy, 1); }); }); diff --git a/packages/prettier-plugin-java/test/unit-test/snippets/types-values-and-variables/wildcard/test.spec.ts b/packages/prettier-plugin-java/test/unit-test/snippets/types-values-and-variables/wildcard/test.spec.ts index bc68588a4..5115368ab 100644 --- a/packages/prettier-plugin-java/test/unit-test/snippets/types-values-and-variables/wildcard/test.spec.ts +++ b/packages/prettier-plugin-java/test/unit-test/snippets/types-values-and-variables/wildcard/test.spec.ts @@ -1,18 +1,6 @@ -import { assert, spy } from "sinon"; -import { BaseCstPrettierPrinter } from "../../../../../src/base-cst-printer.js"; import { expectSnippetToBeFormatted } from "../../../../test-utils.js"; describe("Wildcard", () => { - let wildcardBoundsSpy: any; - - // eslint-disable-next-line no-undef - beforeEach(() => { - if (wildcardBoundsSpy !== undefined) { - wildcardBoundsSpy.restore(); - } - wildcardBoundsSpy = spy(BaseCstPrettierPrinter.prototype, "wildcardBounds"); - }); - it("can format a wildcard", async () => { await expectSnippetToBeFormatted({ snippet: "?", @@ -48,6 +36,5 @@ describe("Wildcard", () => { expectedOutput: "? extends int[]", entryPoint: "wildcard" }); - assert.calledTwice(wildcardBoundsSpy); }); }); diff --git a/packages/prettier-plugin-java/test/unit-test/snippets/types-values-and-variables/wildcardBounds/test.spec.ts b/packages/prettier-plugin-java/test/unit-test/snippets/types-values-and-variables/wildcardBounds/test.spec.ts index 41c6a870b..e4fc7c4ce 100644 --- a/packages/prettier-plugin-java/test/unit-test/snippets/types-values-and-variables/wildcardBounds/test.spec.ts +++ b/packages/prettier-plugin-java/test/unit-test/snippets/types-values-and-variables/wildcardBounds/test.spec.ts @@ -1,25 +1,12 @@ -import { assert, spy } from "sinon"; -import { BaseCstPrettierPrinter } from "../../../../../src/base-cst-printer.js"; import { expectSnippetToBeFormatted } from "../../../../test-utils.js"; describe("Wildcard Bounds", () => { - let referenceTypeSpy: any; - - // eslint-disable-next-line no-undef - beforeEach(() => { - if (referenceTypeSpy !== undefined) { - referenceTypeSpy.restore(); - } - referenceTypeSpy = spy(BaseCstPrettierPrinter.prototype, "referenceType"); - }); - it("can format a wildcardBounds with extends", async () => { await expectSnippetToBeFormatted({ snippet: "extends int[]", expectedOutput: "extends int[]", entryPoint: "wildcardBounds" }); - assert.callCount(referenceTypeSpy, 2); }); it("can format a wildcardBounds with super", async () => { @@ -28,6 +15,5 @@ describe("Wildcard Bounds", () => { expectedOutput: "super int[]", entryPoint: "wildcardBounds" }); - assert.callCount(referenceTypeSpy, 2); }); }); diff --git a/packages/prettier-plugin-java/test/unit-test/switch/_output.java b/packages/prettier-plugin-java/test/unit-test/switch/_output.java index 0b5085b7f..5ca93408f 100644 --- a/packages/prettier-plugin-java/test/unit-test/switch/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/switch/_output.java @@ -153,7 +153,8 @@ public void multipleCaseConstants(TestEnum testEnum) { switch (testEnum) { case FOO -> System.out.println("Foo!"); case BAR, BAZ -> System.out.println("Not Foo!"); - case BAR, + case + BAR, BAZ, BAZ, BAZ, @@ -178,9 +179,9 @@ public void multipleCaseConstants(TestEnum testEnum) { public void caseConstantsWithComments(TestEnum testEnum) { switch (testEnum) { - case BAR/* foo */, BAZ -> System.out.println("Not Foo!"); - case BAR/* foo */, /* bar */BAZ -> System.out.println("Not Foo!"); - case BAR, /* bar */BAZ -> System.out.println("Not Foo!"); + case BAR /* foo */, BAZ -> System.out.println("Not Foo!"); + case BAR /* foo */, /* bar */ BAZ -> System.out.println("Not Foo!"); + case BAR, /* bar */ BAZ -> System.out.println("Not Foo!"); } } diff --git a/packages/prettier-plugin-java/test/unit-test/template-expression/_output.java b/packages/prettier-plugin-java/test/unit-test/template-expression/_output.java index 38aaf61bb..3f27bee86 100644 --- a/packages/prettier-plugin-java/test/unit-test/template-expression/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/template-expression/_output.java @@ -6,11 +6,11 @@ class TemplateExpression { String s = STR."You have a \{getOfferType()} waiting for you!"; - String msg = - STR."The file \{filePath} \{file.exists() ? "does" : "does not"} exist"; + String msg = STR."The file \{filePath} \{ + file.exists() ? "does" : "does not" + } exist"; - String time = - STR."The time is \{ + String time = STR."The time is \{ // The java.time.format package is very useful DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalTime.now()) } right now"; @@ -19,8 +19,7 @@ class TemplateExpression { String s = STR."\{fruit[0]}, \{STR."\{fruit[1]}, \{fruit[2]}"}"; - String html = - STR.""" + String html = STR.""" \{title} @@ -31,8 +30,7 @@ class TemplateExpression { """; - String table = - STR.""" + String table = STR.""" Description Width Height Area \{zone[0].name} \{zone[0].width} \{zone[0].height} \{zone[0].area()} \{zone[1].name} \{zone[1].width} \{zone[1].height} \{zone[1].area()} @@ -40,8 +38,7 @@ class TemplateExpression { Total \{zone[0].area() + zone[1].area() + zone[2].area()} """; - String table = - FMT.""" + String table = FMT.""" Description Width Height Area %-12s\{zone[0].name} %7.2f\{zone[0].width} %7.2f\{ zone[0].height @@ -57,6 +54,7 @@ class TemplateExpression { } """; - PreparedStatement ps = - DB."SELECT * FROM Person p WHERE p.last_name = \{name}"; + PreparedStatement ps = DB."SELECT * FROM Person p WHERE p.last_name = \{ + name + }"; } diff --git a/packages/prettier-plugin-java/test/unit-test/text-blocks/_input.java b/packages/prettier-plugin-java/test/unit-test/text-blocks/_input.java index d404faf7e..0780d47ee 100644 --- a/packages/prettier-plugin-java/test/unit-test/text-blocks/_input.java +++ b/packages/prettier-plugin-java/test/unit-test/text-blocks/_input.java @@ -25,6 +25,14 @@ public void print(%s object) { \r """; + html = """ + \r + \r +

Hello, world

\r + \r + \r + """; + System.out.println( // leading comment """ diff --git a/packages/prettier-plugin-java/test/unit-test/text-blocks/_output.java b/packages/prettier-plugin-java/test/unit-test/text-blocks/_output.java index 89bff6d81..566eeff16 100644 --- a/packages/prettier-plugin-java/test/unit-test/text-blocks/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/text-blocks/_output.java @@ -1,8 +1,7 @@ public class TextBlock { void method() { - String myTextBlock = - """ + String myTextBlock = """ my text @@ -10,15 +9,21 @@ void method() { """; - String source = - """ + String source = """ public void print(%s object) { System.out.println(Objects.toString(object)); } """.formatted(type); - String html = - """ + String html = """ + \r + \r +

Hello, world

\r + \r + \r + """; + + html = """ \r \r

Hello, world

\r diff --git a/packages/prettier-plugin-java/test/unit-test/try_catch/_output.java b/packages/prettier-plugin-java/test/unit-test/try_catch/_output.java index e45188cfc..5ff25f01c 100644 --- a/packages/prettier-plugin-java/test/unit-test/try_catch/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/try_catch/_output.java @@ -61,7 +61,7 @@ void resourceTry() { void multiResourceTry() { try ( FirstResource firstResource = new FirstResource(); - SecondResource secondResource = new SecondResource() + SecondResource secondResource = new SecondResource(); ) { return br.readLine(); } catch (ArithmeticException | ArrayIndexOutOfBoundsException e) { diff --git a/packages/prettier-plugin-java/test/unit-test/unnamed-variables-and-patterns/_output.java b/packages/prettier-plugin-java/test/unit-test/unnamed-variables-and-patterns/_output.java index 1a2d4e53d..04fb2eaca 100644 --- a/packages/prettier-plugin-java/test/unit-test/unnamed-variables-and-patterns/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/unnamed-variables-and-patterns/_output.java @@ -2,7 +2,8 @@ class T { static int count(Iterable orders) { int total = 0; - for (Order _ : orders) total++; // Unnamed variable + for (Order _ : orders) // Unnamed variable + total++; return total; } @@ -29,7 +30,8 @@ void multipleAssignment() { void catchClause() { try { int i = Integer.parseInt(s); - } catch (NumberFormatException _) { // Unnamed variable + } catch (NumberFormatException _) { + // Unnamed variable System.out.println("Bad number: " + s); } } @@ -39,7 +41,8 @@ void multipleCatchClauses() { } void tryWithResources() { - try (var _ = ScopedContext.acquire()) { // Unnamed variable + try (var _ = ScopedContext.acquire()) { + // Unnamed variable } } diff --git a/packages/prettier-plugin-java/test/unit-test/variables/_input.java b/packages/prettier-plugin-java/test/unit-test/variables/_input.java index f315ea0f9..253b87977 100644 --- a/packages/prettier-plugin-java/test/unit-test/variables/_input.java +++ b/packages/prettier-plugin-java/test/unit-test/variables/_input.java @@ -205,5 +205,7 @@ void assignment() { foo = new Foo("aaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbb", "cccccccccccccccccccc", "dddddddddddddddddddd", "eeeeeeeeeeeeeeeeeeee", "ffffffffffffffffffff"); final Foo bar = new Foo("aaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbb", "cccccccccccccccccccc", "dddddddddddddddddddd", "eeeeeeeeeeeeeeeeeeee", "ffffffffffffffffffff"); + + int a, b, c = 1; } } diff --git a/packages/prettier-plugin-java/test/unit-test/variables/_output.java b/packages/prettier-plugin-java/test/unit-test/variables/_output.java index cc6941c3a..661776367 100644 --- a/packages/prettier-plugin-java/test/unit-test/variables/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/variables/_output.java @@ -30,12 +30,12 @@ public class Variables { Integer >(); - private Object variableWithComment1 /* comment */= new Object(); - private Object variableWithComment2 = /* comment */new Object(); - private Object variableWithComment3 /* very very very long comment */= + private Object variableWithComment1 /* comment */ = new Object(); + private Object variableWithComment2 = /* comment */ new Object(); + private Object variableWithComment3 /* very very very long comment */ = new Object(); private Object variableWithComment4 = - /* very very very long comment */new Object(); + /* very very very long comment */ new Object(); private Object[] arrayVariable1 = new Object[3]; private Object[][] arrayVariable2 = new Object[3][3]; @@ -52,8 +52,10 @@ public class Variables { private Range creator1 = this.dateRangeField.new Range(from, to); private Range creator2 = this.dateRangeField.new Range(from, to); - private Range creator3 = - this.dateRangeField.new Range<>(from, to); + private Range creator3 = this.dateRangeField.new Range<>( + from, + to + ); private Range creator3 = new Range<>(from, to); private int hexLiteral = 0x0000; @@ -326,13 +328,12 @@ public methodWithVariableInitializationWithComments() { } void assignment() { - fileSystemDetails = FileHandlerDetails.builder() - .fileSystemType( - EntityUtils.update( - entity.getFileSystemDetails().getFileSystemType(), - update.getFileSystemDetails().getFileSystemType() - ) - ); + fileSystemDetails = FileHandlerDetails.builder().fileSystemType( + EntityUtils.update( + entity.getFileSystemDetails().getFileSystemType(), + update.getFileSystemDetails().getFileSystemType() + ) + ); aaaaaaaaaaaaaaaaa = bbbbbbbbbbbbbbbbb ? ccccccccccccccccc @@ -360,5 +361,9 @@ void assignment() { "eeeeeeeeeeeeeeeeeeee", "ffffffffffffffffffff" ); + + int a, + b, + c = 1; } } diff --git a/packages/prettier-plugin-java/test/unit-test/yield-statement/_output.java b/packages/prettier-plugin-java/test/unit-test/yield-statement/_output.java index 55941a0f3..db4edb90c 100644 --- a/packages/prettier-plugin-java/test/unit-test/yield-statement/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/yield-statement/_output.java @@ -18,6 +18,7 @@ public int calculate(Day d) { yield len * len; } } + return; } diff --git a/packages/prettier-plugin-java/tsconfig.json b/packages/prettier-plugin-java/tsconfig.json index 4ea380ccd..907961b17 100644 --- a/packages/prettier-plugin-java/tsconfig.json +++ b/packages/prettier-plugin-java/tsconfig.json @@ -1,14 +1,16 @@ { "compilerOptions": { "target": "ES6", - "baseUrl": "src", "outDir": "dist", "strict": true, - "allowJs": true, + "declaration": true, "esModuleInterop": true, "noImplicitThis": false, "module": "ESNext", - "moduleResolution": "Node" + "moduleResolution": "Node", + "paths": { + "java-parser": ["../java-parser"] + } }, "include": ["src"] } diff --git a/yarn.lock b/yarn.lock index 27a9ad8b5..2916b2a2e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -914,11 +914,6 @@ dependencies: "@types/node" "*" -"@types/lodash@4.17.13": - version "4.17.13" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.13.tgz#786e2d67cfd95e32862143abe7463a7f90c300eb" - integrity sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg== - "@types/minimatch@^3.0.3": version "3.0.5" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" @@ -4270,10 +4265,10 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prettier@3.4.2: - version "3.4.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.4.2.tgz#a5ce1fb522a588bf2b78ca44c6e6fe5aa5a2b13f" - integrity sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ== +prettier@^3.0.0: + version "3.5.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.3.tgz#4fc2ce0d657e7a02e602549f053b239cb7dfe1b5" + integrity sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw== pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0"