prettier-java-next-line 2.10.4 → 2.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -35,6 +35,33 @@ Set `braceStyle` only when a project needs the inherited same-line style:
35
35
  }
36
36
  ```
37
37
 
38
+ With next-line braces, array initializers put every element on its own line by default. Set `arrayInitializerLayout` to `fit` to keep an array initializer on one line when it fits, and break it into the next-line layout only when it doesn't. When it fits, `bracketSpacing` decides whether to print `{ 1, 2 }` or `{1, 2}`.
39
+
40
+ ```json
41
+ {
42
+ "plugins": ["prettier-java-next-line"],
43
+ "arrayInitializerLayout": "fit"
44
+ }
45
+ ```
46
+
47
+ Set `closingBracket` to `same-line` to keep the closing `)` or `>` of a wrapped list on its last line instead of on its own line. This matches Checkstyle's `SeparatorWrap` check on `RPAREN` with the `eol` option:
48
+
49
+ ```json
50
+ {
51
+ "plugins": ["prettier-java-next-line"],
52
+ "closingBracket": "same-line"
53
+ }
54
+ ```
55
+
56
+ Set `importOrder` to group imports the way Spotless's `importOrder` step does. Groups separated by `,` get blank lines between them, prefixes joined by `|` share a group, `#` marks a static prefix, and an empty prefix catches everything else. Each import belongs to its longest matching prefix. Module imports (`import module`) always come first in their own group. When `importOrder` is empty, static imports follow them, then all others.
57
+
58
+ ```json
59
+ {
60
+ "plugins": ["prettier-java-next-line"],
61
+ "importOrder": "java,javax,,#java|#javax,#"
62
+ }
63
+ ```
64
+
38
65
  To use the matching Prettier runtime fork too, install it under the name `prettier` with an npm alias:
39
66
 
40
67
  ```bash
package/dist/index.cjs CHANGED
@@ -61,6 +61,38 @@ var options_default = {
61
61
  description: "Put opening braces on new lines (Allman style)"
62
62
  }],
63
63
  description: "Placement of opening braces."
64
+ },
65
+ arrayInitializerLayout: {
66
+ type: "choice",
67
+ category: "Java",
68
+ default: "always-break",
69
+ choices: [{
70
+ value: "always-break",
71
+ description: "Put each array element on its own line"
72
+ }, {
73
+ value: "fit",
74
+ description: "Keep array initializers on one line when they fit"
75
+ }],
76
+ description: "Layout of array initializers when braceStyle is next-line."
77
+ },
78
+ closingBracket: {
79
+ type: "choice",
80
+ category: "Java",
81
+ default: "next-line",
82
+ choices: [{
83
+ value: "next-line",
84
+ description: "Put the closing parenthesis or angle bracket of a wrapped list on its own line"
85
+ }, {
86
+ value: "same-line",
87
+ description: "Keep the closing parenthesis or angle bracket on the last line of a wrapped list"
88
+ }],
89
+ description: "Placement of the closing parenthesis or angle bracket when a list wraps."
90
+ },
91
+ importOrder: {
92
+ type: "string",
93
+ category: "Java",
94
+ default: "",
95
+ description: "Import groups in Spotless's format, such as `java,javax,,#java|#javax,#`. Module imports always come first. Empty puts static imports next, then all others."
64
96
  }
65
97
  };
66
98
  //#endregion
@@ -128,11 +160,14 @@ function hasLeadingComments(node) {
128
160
  function hasDanglingComments(node) {
129
161
  return node.comments?.some(({ leading, trailing }) => !leading && !trailing) ?? false;
130
162
  }
131
- function indentInParentheses(contents) {
163
+ function closingBracketLine(options, lineDoc = softline$4) {
164
+ return options?.closingBracket === "same-line" ? "" : lineDoc;
165
+ }
166
+ function indentInParentheses(contents, options) {
132
167
  return contents && !Array.isArray(contents) || contents.length ? [
133
168
  "(",
134
169
  indent$7([softline$4, contents]),
135
- softline$4,
170
+ closingBracketLine(options),
136
171
  ")"
137
172
  ] : "()";
138
173
  }
@@ -151,6 +186,16 @@ function printArrayInitializer(path, print, options) {
151
186
  const list = join$7([",", line$7], path.map(print, "namedChildren"));
152
187
  if (list.length && options.trailingComma !== "none") list.push(ifBreak$2(","));
153
188
  if (options.braceStyle === "next-line" && path.node.type === "array_initializer") {
189
+ if (options.arrayInitializerLayout === "fit") {
190
+ const bracketLine = options.bracketSpacing ? line$7 : softline$4;
191
+ const block = [
192
+ "{",
193
+ indent$7([bracketLine, ...list]),
194
+ bracketLine,
195
+ "}"
196
+ ];
197
+ return group$7(isNestedArray(path) ? block : [softline$4, ...block]);
198
+ }
154
199
  const block = [
155
200
  "{",
156
201
  indent$7([line$7, ...list]),
@@ -213,12 +258,18 @@ function printBlock(path, contents, options) {
213
258
  }
214
259
  function printBlockNextLine(path, contents) {
215
260
  const parentType = path.parent?.type;
216
- const prefix = parentType != null && [
261
+ const isChildOfBodyDeclaration = parentType != null && [
217
262
  "annotation_type_body",
218
263
  "class_body",
219
264
  "enum_body",
220
265
  "interface_body"
221
- ].includes(parentType) ? [] : [hardline$7];
266
+ ].includes(parentType);
267
+ const isBlockStatement = parentType != null && [
268
+ "block",
269
+ "constructor_body",
270
+ "switch_block_statement_group"
271
+ ].includes(parentType);
272
+ const prefix = isChildOfBodyDeclaration || isBlockStatement ? [] : [hardline$7];
222
273
  if (contents.length) return [
223
274
  ...prefix,
224
275
  "{",
@@ -269,7 +320,7 @@ function printBodyDeclarations(path, print, padFirst = false) {
269
320
  return blankLine ? [separator, declaration] : declaration;
270
321
  }, "namedChildren");
271
322
  }
272
- function printTypeParameters(path, print) {
323
+ function printTypeParameters(path, print, options) {
273
324
  const parameters = path.node.namedChildren;
274
325
  if (parameters.length === 0 || parameters.length === 1 && isSimpleType(parameters[0]) && !parameters.some(({ comments }) => comments?.length && comments.some(({ type }) => type === "line_comment"))) return [
275
326
  "<",
@@ -279,7 +330,7 @@ function printTypeParameters(path, print) {
279
330
  const parts = [
280
331
  "<",
281
332
  indent$7([softline$4, join$7([",", line$7], path.map(print, "namedChildren"))]),
282
- softline$4,
333
+ closingBracketLine(options),
283
334
  ">"
284
335
  ];
285
336
  return path.node.type === "type_arguments" ? group$7(parts) : parts;
@@ -309,6 +360,12 @@ function printAssignment(leftDoc, operator, rightDoc, rightNode) {
309
360
  indentIfBreak$2(rightDoc, { groupId })
310
361
  ]);
311
362
  }
363
+ function getLanguageByLanguageName(languages, languageName) {
364
+ return languages.find(({ name }) => name.toLowerCase() === languageName) ?? languages.find(({ aliases }) => aliases?.includes(languageName)) ?? languages.find(({ extensions }) => extensions?.includes(`.${languageName}`));
365
+ }
366
+ function inferParser(options, language) {
367
+ return getLanguageByLanguageName(options.plugins?.filter((plugin) => typeof plugin !== "string" && !(plugin instanceof URL)).reverse().flatMap((plugin) => plugin.languages ?? []) ?? [], language)?.parsers[0];
368
+ }
312
369
  function printTextBlock(contents) {
313
370
  return [
314
371
  "\"\"\"",
@@ -317,13 +374,15 @@ function printTextBlock(contents) {
317
374
  "\"\"\""
318
375
  ];
319
376
  }
320
- function embedTextBlock(path) {
377
+ function embedTextBlock(path, options) {
321
378
  if (path.node.namedChildren.some(({ type }) => type === "string_interpolation") || path.node.children[0].value === "\"") return null;
322
379
  const language = findEmbeddedLanguage(path);
323
380
  if (!language) return null;
381
+ const parser = inferParser(options, language);
382
+ if (!parser) return null;
324
383
  const text = unescapeTextBlockContents(textBlockContents(path.node));
325
384
  return async (textToDoc) => {
326
- return printTextBlock([escapeDocForTextBlock(await textToDoc(text, { parser: language })), hardline$7]);
385
+ return printTextBlock([escapeDocForTextBlock(await textToDoc(text, { parser })), hardline$7]);
327
386
  };
328
387
  }
329
388
  function textBlockContents(node) {
@@ -620,7 +679,7 @@ var blocks_and_statements_default = {
620
679
  ";"
621
680
  ];
622
681
  },
623
- for_statement(path, print) {
682
+ for_statement(path, print, options) {
624
683
  const danglingComments = printDanglingComments(path);
625
684
  const hasInit = path.node.initNodes.length > 0;
626
685
  const hasCondition = hasChild(path, "conditionNode");
@@ -628,13 +687,13 @@ var blocks_and_statements_default = {
628
687
  const expressions = [
629
688
  !hasInit ? ";" : path.node.initNodes[0].type === "local_variable_declaration" ? path.call(print, "initNodes", 0) : [printExpressionList(path.map(print, "initNodes")), ";"],
630
689
  hasCondition ? [path.call(print, "conditionNode"), ";"] : ";",
631
- hasUpdate ? printExpressionList(path.map(print, "updateNodes")) : ""
690
+ hasUpdate ? [line$6, printExpressionList(path.map(print, "updateNodes"))] : ifBreak$1("", " ")
632
691
  ];
633
692
  const hasEmptyStatement = path.node.bodyNode.type === ";";
634
693
  return [
635
694
  danglingComments && [danglingComments, hardline$6],
636
695
  "for ",
637
- hasInit || hasCondition || hasUpdate ? group$6(indentInParentheses(join$6(line$6, expressions))) : "(;;)",
696
+ hasInit || hasCondition || hasUpdate ? group$6(indentInParentheses([join$6(line$6, expressions.slice(0, 2)), expressions[2]], options)) : "(;;)",
638
697
  hasEmptyStatement ? ";" : [" ", path.call(print, "bodyNode")]
639
698
  ];
640
699
  },
@@ -697,7 +756,7 @@ var blocks_and_statements_default = {
697
756
  },
698
757
  catch_clause(path, print, options) {
699
758
  const catchFormalParameterIndex = path.node.namedChildren.findIndex(({ type }) => type === "catch_formal_parameter");
700
- const params = group$6(indentInParentheses(path.call(print, "namedChildren", catchFormalParameterIndex)));
759
+ const params = group$6(indentInParentheses(path.call(print, "namedChildren", catchFormalParameterIndex), options));
701
760
  const body = path.call(print, "bodyNode");
702
761
  if (options?.braceStyle === "next-line") return [
703
762
  hardline$6,
@@ -750,7 +809,7 @@ var blocks_and_statements_default = {
750
809
  ...clauses
751
810
  ]);
752
811
  },
753
- resource_specification(path, print) {
812
+ resource_specification(path, print, options) {
754
813
  const resources = [];
755
814
  let hasTrailingSemicolon = false;
756
815
  path.each((child) => {
@@ -761,7 +820,7 @@ var blocks_and_statements_default = {
761
820
  }, "children");
762
821
  const parts = join$6([";", line$6], resources);
763
822
  if (hasTrailingSemicolon) parts.push(ifBreak$1(";"));
764
- return group$6(indentInParentheses(parts));
823
+ return group$6(indentInParentheses(parts, options));
765
824
  },
766
825
  resource(path, print) {
767
826
  if (hasChild(path, "typeNode") && hasChild(path, "nameNode") && hasChild(path, "valueNode")) {
@@ -791,28 +850,28 @@ var blocks_and_statements_default = {
791
850
  function printExpressionList(expressions) {
792
851
  return group$6(expressions.map((expression, index) => index === 0 ? expression : [",", indent$6([line$6, expression])]));
793
852
  }
794
- function printReturnOrThrowArgument(path, print) {
853
+ function printReturnOrThrowArgument(path, print, options) {
795
854
  const { node } = path;
796
855
  const argumentDoc = print(path);
797
856
  if (returnArgumentHasLeadingComment(node)) return [
798
857
  "(",
799
858
  indent$6([hardline$6, argumentDoc]),
800
- hardline$6,
859
+ closingBracketLine(options, hardline$6),
801
860
  ")"
802
861
  ];
803
862
  if (node.type === "binary_expression") return group$6([
804
863
  ifBreak$1("("),
805
864
  indent$6([softline$3, argumentDoc]),
806
- softline$3,
865
+ closingBracketLine(options),
807
866
  ifBreak$1(")")
808
867
  ]);
809
868
  return argumentDoc;
810
869
  }
811
- function printReturnOrThrowStatement(path, print) {
870
+ function printReturnOrThrowStatement(path, print, options) {
812
871
  const { node } = path;
813
872
  return [
814
873
  node.type === "throw_statement" ? "throw" : "return",
815
- node.namedChildren.length ? [" ", path.call(() => printReturnOrThrowArgument(path, print), "namedChildren", 0)] : "",
874
+ node.namedChildren.length ? [" ", path.call(() => printReturnOrThrowArgument(path, print, options), "namedChildren", 0)] : "",
816
875
  ";"
817
876
  ];
818
877
  }
@@ -830,7 +889,7 @@ var classes_default = {
830
889
  const hasMultipleClauses = definedClauses.length > 1;
831
890
  if (hasChild(path, "type_parametersNode")) {
832
891
  const typeParameters = path.call(print, "type_parametersNode");
833
- parts.push(hasMultipleClauses ? group$5(indent$5(typeParameters)) : typeParameters);
892
+ parts.push(hasMultipleClauses ? group$5(indent$5(typeParameters)) : options?.braceStyle === "next-line" ? group$5(typeParameters) : typeParameters);
834
893
  }
835
894
  if (definedClauses.length) {
836
895
  const clauses = definedClauses.flatMap((clause) => {
@@ -896,14 +955,14 @@ var classes_default = {
896
955
  receiver_parameter(path, print) {
897
956
  return path.map((child) => child.isLast ? print(child) : [print(child), child.node.type === "identifier" ? "." : " "], "namedChildren");
898
957
  },
899
- formal_parameters(path, print) {
958
+ formal_parameters(path, print, options) {
900
959
  const parameters = path.parent?.type === "record_declaration" ? printBodyDeclarations(path, print) : path.map(print, "namedChildren");
901
960
  if (parameters.length === 0) return [
902
961
  "(",
903
962
  printDanglingCommentsInList(path),
904
963
  ")"
905
964
  ];
906
- return indentInParentheses(join$5([",", line$5], parameters));
965
+ return indentInParentheses(join$5([",", line$5], parameters), options);
907
966
  },
908
967
  formal_parameter(path, print) {
909
968
  const parameter = printModifiers(path, print, path.grandparent?.type === "record_declaration" ? "avoidBreak" : "noBreak");
@@ -972,12 +1031,12 @@ var classes_default = {
972
1031
  if (modifiers.length || typeAnnotations.length) parts.push(join$5(" ", [...modifiers, ...typeAnnotations]));
973
1032
  return join$5(annotationMode === "avoidBreak" ? line$5 : annotationMode === "noBreak" ? " " : hardline$5, parts);
974
1033
  },
975
- enum_declaration(path, print) {
1034
+ enum_declaration(path, print, options) {
976
1035
  const modifiers = printModifiers(path, print, "declarationOnly");
977
1036
  const parts = ["enum ", path.call(print, "nameNode")];
978
1037
  if (hasChild(path, "interfacesNode")) {
979
1038
  const hasBody = path.node.bodyNode.namedChildren.length > 0;
980
- parts.push(indent$5([hardline$5, path.call(print, "interfacesNode")]), hasBody ? line$5 : " ");
1039
+ parts.push(indent$5([hardline$5, path.call(print, "interfacesNode")]), options?.braceStyle === "next-line" ? "" : hasBody ? line$5 : " ");
981
1040
  } else parts.push(" ");
982
1041
  return [
983
1042
  ...modifiers,
@@ -1086,7 +1145,7 @@ var expressions_default = {
1086
1145
  const shouldPutBodyOnSameLine = !functionBody.comments?.some((comment) => comment.leading && hasNewline$1(options.originalText, comment.end.index)) && mayBreakAfterShortPrefix(functionBody);
1087
1146
  const chainGroupId = Symbol("arrow-chain");
1088
1147
  const signaturesDoc = printArrowFunctionSignatures(path, { signatureDocs });
1089
- bodyDoc = printArrowFunctionBody(path, args, {
1148
+ bodyDoc = printArrowFunctionBody(path, args, options, {
1090
1149
  bodyDoc,
1091
1150
  bodyComments,
1092
1151
  shouldPutBodyOnSameLine
@@ -1104,7 +1163,7 @@ var expressions_default = {
1104
1163
  }, "namedChildren");
1105
1164
  if (!identifiers.length) return "()";
1106
1165
  const parameters = join$4([",", line$4], identifiers);
1107
- if (identifiers.length > 1 || willBreak(identifiers)) return group$4(indentInParentheses(parameters));
1166
+ if (identifiers.length > 1 || willBreak(identifiers)) return group$4(indentInParentheses(parameters, options));
1108
1167
  return options.arrowParens === "avoid" ? parameters : [
1109
1168
  "(",
1110
1169
  ...parameters,
@@ -1145,9 +1204,9 @@ var expressions_default = {
1145
1204
  const result = maybeGroup([
1146
1205
  printTernaryTest(path, print),
1147
1206
  forceNoIndent ? parts : indent$4(parts),
1148
- breakClosingParen && !shouldExtraIndent ? softline$1 : ""
1207
+ breakClosingParen && !shouldExtraIndent ? closingBracketLine(options) : ""
1149
1208
  ]);
1150
- return isParentTest || shouldExtraIndent ? group$4([indent$4([softline$1, result]), softline$1]) : result;
1209
+ return isParentTest || shouldExtraIndent ? group$4([indent$4([softline$1, result]), closingBracketLine(options)]) : result;
1151
1210
  },
1152
1211
  assignment_expression(path, print) {
1153
1212
  const { node } = path;
@@ -1160,7 +1219,7 @@ var expressions_default = {
1160
1219
  const isInsideParentheses = (grandparent?.type === "if_statement" || grandparent?.type === "while_statement" || grandparent?.type === "switch_expression" || grandparent?.type === "do_statement") && grandparent.conditionNode === parent || grandparent?.type === "synchronized_statement" && grandparent.bodyNode !== parent;
1161
1220
  const parts = printBinaryExpressions(path, print, options, isInsideParentheses);
1162
1221
  if (isInsideParentheses) return parts;
1163
- if (parent?.type === "unary_expression" && !node.comments || (parent?.type === "explicit_constructor_invocation" || parent?.type === "field_access" || parent?.type === "method_invocation") && parent.objectNode === node || parent?.type === "method_reference" || parent?.type === "object_creation_expression") return group$4([indent$4([softline$1, ...parts]), softline$1]);
1222
+ if (parent?.type === "unary_expression" && !node.comments || (parent?.type === "explicit_constructor_invocation" || parent?.type === "field_access" || parent?.type === "method_invocation") && parent.objectNode === node || parent?.type === "method_reference" || parent?.type === "object_creation_expression") return group$4([indent$4([softline$1, ...parts]), closingBracketLine(options)]);
1164
1223
  if (isReturnOrThrowStatement(parent) || parent?.type === "assignment_expression" || parent?.type === "variable_declarator" || parent?.type === "guard" || parent?.type === "lambda_expression" && parent.bodyNode === node || parent?.type === "for_statement" && parent.conditionNode === node || parent?.type === "ternary_expression" && !isReturnOrThrowStatement(grandparent) && grandparent?.type !== "parenthesized_expression" && grandparent?.type !== "argument_list" || parent?.type === "unary_expression" && parent.operandNode === node) return group$4(parts);
1165
1224
  if (parts.length === 0) return "";
1166
1225
  const firstGroupIndex = parts.findIndex((part) => typeof part !== "string" && !Array.isArray(part) && part.type === "group");
@@ -1175,8 +1234,12 @@ var expressions_default = {
1175
1234
  return [(options.experimentalOperatorPosition === "start" ? child.node.type === "instanceof" : child.previous.type === "instanceof") ? line$4 : " ", doc];
1176
1235
  }, "children")));
1177
1236
  },
1178
- unary_expression(path, print) {
1179
- return path.map(print, "children");
1237
+ unary_expression(path, print, options) {
1238
+ const parts = [path.call(print, "operatorNode")];
1239
+ const operandDoc = path.call(print, "operandNode");
1240
+ if (path.node.operandNode.comments) parts.push(group$4(indentInParentheses(operandDoc, options)));
1241
+ else parts.push(operandDoc);
1242
+ return parts;
1180
1243
  },
1181
1244
  field_access: printMemberChain,
1182
1245
  generic_type(path, print) {
@@ -1184,7 +1247,7 @@ var expressions_default = {
1184
1247
  const typeArgumentsIndex = path.node.namedChildren.findIndex(({ type }) => type === "type_arguments");
1185
1248
  return [path.call(print, "namedChildren", typeIdentifierIndex), path.call(print, "namedChildren", typeArgumentsIndex)];
1186
1249
  },
1187
- parenthesized_expression(path, print) {
1250
+ parenthesized_expression(path, print, options) {
1188
1251
  const expression = path.call(print, "namedChildren", 0);
1189
1252
  const parentType = path.parent?.type;
1190
1253
  const grandparentType = path.grandparent?.type;
@@ -1193,12 +1256,12 @@ var expressions_default = {
1193
1256
  const hasTernary = expressionType === "ternary_expression";
1194
1257
  const hasSuffix = parentType && (parentType === "array_access" || parentType === "explicit_constructor_invocation" || parentType === "field_access" || parentType === "method_invocation" || parentType === "method_reference" || parentType === "object_creation_expression");
1195
1258
  const isAssignment = parentType && (parentType === "assignment_expression" || parentType === "variable_declarator") || hasSuffix && (grandparentType === "assignment_expression" || grandparentType === "variable_declarator");
1196
- if (!hasLambda && hasSuffix && (!hasTernary || isAssignment)) return group$4(indentInParentheses(hasTernary ? group$4(expression) : expression));
1197
- else if (parentType && (parentType === "guard" || parentType === "return_statement" || parentType === "unary_expression" && grandparentType === "return_statement" || path.node.fieldName === "condition" && (parentType === "do_statement" || parentType === "if_statement" || parentType === "switch_expression" || parentType === "while_statement") || path.node.fieldName !== "body" && parentType === "synchronized_statement")) return group$4(indentInParentheses(group$4(expression)));
1259
+ if (!hasLambda && hasSuffix && (!hasTernary || isAssignment)) return group$4(indentInParentheses(hasTernary ? group$4(expression) : expression, options));
1260
+ else if (parentType && (parentType === "guard" || parentType === "return_statement" || parentType === "unary_expression" && grandparentType === "return_statement" || path.node.fieldName === "condition" && (parentType === "do_statement" || parentType === "if_statement" || parentType === "switch_expression" || parentType === "while_statement") || path.node.fieldName !== "body" && parentType === "synchronized_statement")) return group$4(indentInParentheses(group$4(expression), options));
1198
1261
  else if (hasTernary && hasSuffix && !isAssignment) return group$4([
1199
1262
  "(",
1200
1263
  expression,
1201
- softline$1,
1264
+ closingBracketLine(options),
1202
1265
  ")"
1203
1266
  ]);
1204
1267
  else return group$4([
@@ -1241,7 +1304,7 @@ var expressions_default = {
1241
1304
  const args = path.node.namedChildren;
1242
1305
  if (args.length === 0) {
1243
1306
  const shouldBreak = path.node.comments?.some(({ type, leading, trailing }) => !leading && !trailing && type === "line_comment");
1244
- return group$4(indentInParentheses(printDanglingComments(path)), { shouldBreak });
1307
+ return group$4(indentInParentheses(printDanglingComments(path), options), { shouldBreak });
1245
1308
  }
1246
1309
  const lastArgIndex = args.length - 1;
1247
1310
  let anyArgEmptyLine = false;
@@ -1267,7 +1330,7 @@ var expressions_default = {
1267
1330
  return group$4([
1268
1331
  "(",
1269
1332
  indent$4([line$4, ...printedArguments]),
1270
- line$4,
1333
+ closingBracketLine(options, line$4),
1271
1334
  ")"
1272
1335
  ], { shouldBreak: true });
1273
1336
  }
@@ -1339,7 +1402,7 @@ var expressions_default = {
1339
1402
  allArgsBrokenOut()
1340
1403
  ]);
1341
1404
  }
1342
- return group$4(indentInParentheses(printedArguments), { shouldBreak: printedArguments.some(willBreak) || anyArgEmptyLine });
1405
+ return group$4(indentInParentheses(printedArguments, options), { shouldBreak: printedArguments.some(willBreak) || anyArgEmptyLine });
1343
1406
  },
1344
1407
  array_creation_expression(path, print) {
1345
1408
  const parts = ["new "];
@@ -1380,8 +1443,8 @@ var expressions_default = {
1380
1443
  record_pattern(path, print) {
1381
1444
  return path.map(print, "children");
1382
1445
  },
1383
- record_pattern_body(path, print) {
1384
- return group$4(indentInParentheses(join$4([",", line$4], path.map(print, "namedChildren"))));
1446
+ record_pattern_body(path, print, options) {
1447
+ return group$4(indentInParentheses(join$4([",", line$4], path.map(print, "namedChildren")), options));
1385
1448
  },
1386
1449
  record_pattern_component(path, print) {
1387
1450
  return join$4(" ", path.map(print, "children"));
@@ -1433,8 +1496,8 @@ function printArrowFunctionSignatures(path, { signatureDocs }) {
1433
1496
  if (node.fieldName === "object" && parent?.type === "method_invocation") return group$4(join$4([" ->", line$4], signatureDocs));
1434
1497
  return group$4(indent$4(join$4([" ->", line$4], signatureDocs)));
1435
1498
  }
1436
- function printArrowFunctionBody(path, args, { bodyDoc, bodyComments, shouldPutBodyOnSameLine }) {
1437
- const trailingSpace = args && typeof args === "object" && "expandLastArg" in args && args.expandLastArg && !path.node.comments ? softline$1 : "";
1499
+ function printArrowFunctionBody(path, args, options, { bodyDoc, bodyComments, shouldPutBodyOnSameLine }) {
1500
+ const trailingSpace = args && typeof args === "object" && "expandLastArg" in args && args.expandLastArg && !path.node.comments ? closingBracketLine(options) : "";
1438
1501
  return shouldPutBodyOnSameLine ? [
1439
1502
  " ",
1440
1503
  bodyDoc,
@@ -1791,7 +1854,7 @@ var interfaces_default = {
1791
1854
  const hasMultipleClauses = hasExtendsInterfaces && hasPermits;
1792
1855
  if (hasChild(path, "type_parametersNode")) {
1793
1856
  const typeParameters = path.call(print, "type_parametersNode");
1794
- parts.push(hasMultipleClauses ? group$3(indent$3(typeParameters)) : typeParameters);
1857
+ parts.push(hasMultipleClauses ? group$3(indent$3(typeParameters)) : options?.braceStyle === "next-line" ? group$3(typeParameters) : typeParameters);
1795
1858
  }
1796
1859
  if (hasExtendsInterfaces || hasPermits) {
1797
1860
  const clauses = [];
@@ -1841,13 +1904,13 @@ var interfaces_default = {
1841
1904
  marker_annotation(path, print) {
1842
1905
  return ["@", path.call(print, "nameNode")];
1843
1906
  },
1844
- annotation_argument_list(path, print) {
1907
+ annotation_argument_list(path, print, options) {
1845
1908
  const args = path.map(print, "namedChildren");
1846
1909
  return args.length === 1 && path.node.namedChildren[0].type === "element_value_array_initializer" ? [
1847
1910
  "(",
1848
1911
  args[0],
1849
1912
  ")"
1850
- ] : group$3(indentInParentheses(join$3([",", line$3], args)));
1913
+ ] : group$3(indentInParentheses(join$3([",", line$3], args), options));
1851
1914
  },
1852
1915
  element_value_pair(path, print) {
1853
1916
  return group$3([
@@ -1918,11 +1981,11 @@ var names_default = {
1918
1981
  //#region src/printers/packages-and-modules.ts
1919
1982
  const { group: group$1, hardline: hardline$1, indent: indent$1, join: join$1, line: line$2 } = prettier_doc.builders;
1920
1983
  var packages_and_modules_default = {
1921
- program(path, print) {
1984
+ program(path, print, options) {
1922
1985
  if (!path.node.namedChildren.length) return [printDanglingComments(path), hardline$1];
1923
1986
  const parts = [];
1924
1987
  if (path.node.namedChildren[0].type !== "import_declaration") parts.push(path.call(print, "namedChildren", 0));
1925
- const staticImports = [];
1988
+ const moduleImports = [];
1926
1989
  const imports = [];
1927
1990
  const otherDeclarations = [];
1928
1991
  path.each((child) => {
@@ -1930,13 +1993,15 @@ var packages_and_modules_default = {
1930
1993
  if (child.node.type === "import_declaration") {
1931
1994
  const names = extractNames(child.node.namedChildren[0]);
1932
1995
  if (child.node.namedChildren.some(({ type }) => type === "asterisk")) names.push("*");
1933
- (child.node.children[1].type === "static" ? staticImports : imports).push({
1996
+ const modifier = child.node.children[1].type;
1997
+ (modifier === "module" ? moduleImports : imports).push({
1934
1998
  doc,
1935
- names
1999
+ names,
2000
+ isStatic: modifier === "static"
1936
2001
  });
1937
2002
  } else if (!child.isFirst) otherDeclarations.push(doc);
1938
2003
  }, "namedChildren");
1939
- parts.push(...[staticImports, imports].filter((imports) => imports.length).map((imports) => join$1(hardline$1, imports.sort(compareFqn).map(({ doc }) => doc))), ...otherDeclarations);
2004
+ parts.push(...[moduleImports.sort(compareFqn), ...groupImports(imports, options.importOrder)].filter((group) => group.length).map((group) => join$1(hardline$1, group.map(({ doc }) => doc))), ...otherDeclarations);
1940
2005
  return [...join$1([hardline$1, hardline$1], parts), hardline$1];
1941
2006
  },
1942
2007
  package_declaration(path, print) {
@@ -1960,7 +2025,8 @@ var packages_and_modules_default = {
1960
2025
  },
1961
2026
  import_declaration(path, print) {
1962
2027
  const declaration = ["import "];
1963
- if (path.node.children.some(({ type }) => type === "static")) declaration.push("static ");
2028
+ const modifier = path.node.children[1].type;
2029
+ if (modifier === "module" || modifier === "static") declaration.push(modifier, " ");
1964
2030
  const identifierIndex = path.node.namedChildren.findIndex(({ type }) => type === "identifier" || type === "scoped_identifier");
1965
2031
  declaration.push(path.call(print, "namedChildren", identifierIndex));
1966
2032
  if (path.node.namedChildren.some(({ type }) => type === "asterisk")) declaration.push(".*");
@@ -2014,6 +2080,46 @@ var packages_and_modules_default = {
2014
2080
  function extractNames(node) {
2015
2081
  return node.type === "identifier" ? [node.value] : [...extractNames(node.scopeNode), node.nameNode.value];
2016
2082
  }
2083
+ const STATIC_PREFIX = "static ";
2084
+ const CATCH_ALL_PREFIX = "";
2085
+ function groupImports(imports, importOrder) {
2086
+ if (!importOrder) return [imports.filter(({ isStatic }) => isStatic), imports.filter(({ isStatic }) => !isStatic)].filter((group) => group.length).map((group) => group.sort(compareFqn));
2087
+ const groups = parseImportOrder(importOrder);
2088
+ const prefixes = groups.flat();
2089
+ const importsByPrefix = /* @__PURE__ */ new Map();
2090
+ for (const entry of imports) {
2091
+ const key = importKey(entry);
2092
+ const prefix = prefixes.filter((candidate) => key.startsWith(candidate)).reduce((best, candidate) => candidate.length > best.length ? candidate : best);
2093
+ importsByPrefix.set(prefix, [...importsByPrefix.get(prefix) ?? [], {
2094
+ key,
2095
+ entry
2096
+ }]);
2097
+ }
2098
+ return groups.map((group) => group.flatMap((prefix) => (importsByPrefix.get(prefix) ?? []).sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0).map(({ entry }) => entry))).filter((group) => group.length);
2099
+ }
2100
+ /**
2101
+ * Parses an import order in Spotless's format: groups separated by `,` are
2102
+ * printed with blank lines between them, prefixes joined by `|` share a group,
2103
+ * `#` marks a static prefix, and an empty prefix catches everything else.
2104
+ * Each import belongs to its longest matching prefix.
2105
+ */
2106
+ function parseImportOrder(importOrder) {
2107
+ const knownPrefixes = /* @__PURE__ */ new Set();
2108
+ const groups = importOrder.split(",").map((group) => group.split("|").map((prefix) => prefix.startsWith("#") ? STATIC_PREFIX + prefix.slice(1) : prefix).filter((prefix) => {
2109
+ if (knownPrefixes.has(prefix)) return false;
2110
+ knownPrefixes.add(prefix);
2111
+ return true;
2112
+ }));
2113
+ if (!knownPrefixes.has(STATIC_PREFIX)) {
2114
+ const lastStaticGroupIndex = groups.findLastIndex((group) => group.some((prefix) => prefix.startsWith(STATIC_PREFIX)));
2115
+ groups.splice(Math.max(lastStaticGroupIndex, 0), 0, [STATIC_PREFIX]);
2116
+ }
2117
+ if (!knownPrefixes.has(CATCH_ALL_PREFIX)) groups.push([CATCH_ALL_PREFIX]);
2118
+ return groups;
2119
+ }
2120
+ function importKey({ names, isStatic }) {
2121
+ return (isStatic ? STATIC_PREFIX : "") + names.join(".");
2122
+ }
2017
2123
  function compareFqn({ names: a }, { names: b }) {
2018
2124
  const minParts = Math.min(a.length, b.length);
2019
2125
  for (let i = 0; i < minParts; i++) {
@@ -2114,8 +2220,8 @@ var printer_default = {
2114
2220
  ")"
2115
2221
  ] : doc;
2116
2222
  },
2117
- embed(path) {
2118
- return hasType(path, "string_literal") ? embedTextBlock(path) : null;
2223
+ embed(path, options) {
2224
+ return hasType(path, "string_literal") ? embedTextBlock(path, options) : null;
2119
2225
  },
2120
2226
  hasPrettierIgnore(path) {
2121
2227
  return path.node.comments?.some(isPrettierIgnore) === true || canAttachComment(path.node, path.parent ? [path.parent] : []) && isFullyBetweenPrettierIgnore(path);
package/dist/index.d.cts CHANGED
@@ -1575,7 +1575,7 @@ declare const _default: {
1575
1575
  printers: {
1576
1576
  java: {
1577
1577
  print(path: import("prettier").AstPath<SyntaxNode>, options: import("prettier").ParserOptions<SyntaxNode>, print: (selector?: string | number | import("prettier").AstPath<SyntaxNode> | (string | number)[] | undefined, args?: unknown) => import("prettier").Doc, args: unknown): import("prettier/doc.js").builders.Doc;
1578
- embed(path: import("prettier").AstPath<any>): ((textToDoc: (text: string, options: import("prettier").Options) => Promise<import("prettier").Doc>) => Promise<import("prettier/doc.js").builders.Doc[]>) | null;
1578
+ embed(path: import("prettier").AstPath<any>, options: import("prettier").Options): ((textToDoc: (text: string, options: import("prettier").Options) => Promise<import("prettier").Doc>) => Promise<import("prettier/doc.js").builders.Doc[]>) | null;
1579
1579
  hasPrettierIgnore(path: import("prettier").AstPath<SyntaxNode>): boolean;
1580
1580
  canAttachComment: typeof canAttachComment;
1581
1581
  isBlockComment(node: SyntaxNode): boolean;
@@ -1631,6 +1631,32 @@ declare const _default: {
1631
1631
  }[];
1632
1632
  description: string;
1633
1633
  };
1634
+ arrayInitializerLayout: {
1635
+ type: "choice";
1636
+ category: string;
1637
+ default: string;
1638
+ choices: {
1639
+ value: string;
1640
+ description: string;
1641
+ }[];
1642
+ description: string;
1643
+ };
1644
+ closingBracket: {
1645
+ type: "choice";
1646
+ category: string;
1647
+ default: string;
1648
+ choices: {
1649
+ value: string;
1650
+ description: string;
1651
+ }[];
1652
+ description: string;
1653
+ };
1654
+ importOrder: {
1655
+ type: "string";
1656
+ category: string;
1657
+ default: string;
1658
+ description: string;
1659
+ };
1634
1660
  };
1635
1661
  defaultOptions: {
1636
1662
  arrowParens: "avoid";
package/dist/index.d.mts CHANGED
@@ -1575,7 +1575,7 @@ declare const _default: {
1575
1575
  printers: {
1576
1576
  java: {
1577
1577
  print(path: import("prettier").AstPath<SyntaxNode>, options: import("prettier").ParserOptions<SyntaxNode>, print: (selector?: string | number | import("prettier").AstPath<SyntaxNode> | (string | number)[] | undefined, args?: unknown) => import("prettier").Doc, args: unknown): import("prettier/doc.js").builders.Doc;
1578
- embed(path: import("prettier").AstPath<any>): ((textToDoc: (text: string, options: import("prettier").Options) => Promise<import("prettier").Doc>) => Promise<import("prettier/doc.js").builders.Doc[]>) | null;
1578
+ embed(path: import("prettier").AstPath<any>, options: import("prettier").Options): ((textToDoc: (text: string, options: import("prettier").Options) => Promise<import("prettier").Doc>) => Promise<import("prettier/doc.js").builders.Doc[]>) | null;
1579
1579
  hasPrettierIgnore(path: import("prettier").AstPath<SyntaxNode>): boolean;
1580
1580
  canAttachComment: typeof canAttachComment;
1581
1581
  isBlockComment(node: SyntaxNode): boolean;
@@ -1631,6 +1631,32 @@ declare const _default: {
1631
1631
  }[];
1632
1632
  description: string;
1633
1633
  };
1634
+ arrayInitializerLayout: {
1635
+ type: "choice";
1636
+ category: string;
1637
+ default: string;
1638
+ choices: {
1639
+ value: string;
1640
+ description: string;
1641
+ }[];
1642
+ description: string;
1643
+ };
1644
+ closingBracket: {
1645
+ type: "choice";
1646
+ category: string;
1647
+ default: string;
1648
+ choices: {
1649
+ value: string;
1650
+ description: string;
1651
+ }[];
1652
+ description: string;
1653
+ };
1654
+ importOrder: {
1655
+ type: "string";
1656
+ category: string;
1657
+ default: string;
1658
+ description: string;
1659
+ };
1634
1660
  };
1635
1661
  defaultOptions: {
1636
1662
  arrowParens: "avoid";
package/dist/index.mjs CHANGED
@@ -61,6 +61,38 @@ var options_default = {
61
61
  description: "Put opening braces on new lines (Allman style)"
62
62
  }],
63
63
  description: "Placement of opening braces."
64
+ },
65
+ arrayInitializerLayout: {
66
+ type: "choice",
67
+ category: "Java",
68
+ default: "always-break",
69
+ choices: [{
70
+ value: "always-break",
71
+ description: "Put each array element on its own line"
72
+ }, {
73
+ value: "fit",
74
+ description: "Keep array initializers on one line when they fit"
75
+ }],
76
+ description: "Layout of array initializers when braceStyle is next-line."
77
+ },
78
+ closingBracket: {
79
+ type: "choice",
80
+ category: "Java",
81
+ default: "next-line",
82
+ choices: [{
83
+ value: "next-line",
84
+ description: "Put the closing parenthesis or angle bracket of a wrapped list on its own line"
85
+ }, {
86
+ value: "same-line",
87
+ description: "Keep the closing parenthesis or angle bracket on the last line of a wrapped list"
88
+ }],
89
+ description: "Placement of the closing parenthesis or angle bracket when a list wraps."
90
+ },
91
+ importOrder: {
92
+ type: "string",
93
+ category: "Java",
94
+ default: "",
95
+ description: "Import groups in Spotless's format, such as `java,javax,,#java|#javax,#`. Module imports always come first. Empty puts static imports next, then all others."
64
96
  }
65
97
  };
66
98
  //#endregion
@@ -128,11 +160,14 @@ function hasLeadingComments(node) {
128
160
  function hasDanglingComments(node) {
129
161
  return node.comments?.some(({ leading, trailing }) => !leading && !trailing) ?? false;
130
162
  }
131
- function indentInParentheses(contents) {
163
+ function closingBracketLine(options, lineDoc = softline$4) {
164
+ return options?.closingBracket === "same-line" ? "" : lineDoc;
165
+ }
166
+ function indentInParentheses(contents, options) {
132
167
  return contents && !Array.isArray(contents) || contents.length ? [
133
168
  "(",
134
169
  indent$7([softline$4, contents]),
135
- softline$4,
170
+ closingBracketLine(options),
136
171
  ")"
137
172
  ] : "()";
138
173
  }
@@ -151,6 +186,16 @@ function printArrayInitializer(path, print, options) {
151
186
  const list = join$7([",", line$7], path.map(print, "namedChildren"));
152
187
  if (list.length && options.trailingComma !== "none") list.push(ifBreak$2(","));
153
188
  if (options.braceStyle === "next-line" && path.node.type === "array_initializer") {
189
+ if (options.arrayInitializerLayout === "fit") {
190
+ const bracketLine = options.bracketSpacing ? line$7 : softline$4;
191
+ const block = [
192
+ "{",
193
+ indent$7([bracketLine, ...list]),
194
+ bracketLine,
195
+ "}"
196
+ ];
197
+ return group$7(isNestedArray(path) ? block : [softline$4, ...block]);
198
+ }
154
199
  const block = [
155
200
  "{",
156
201
  indent$7([line$7, ...list]),
@@ -213,12 +258,18 @@ function printBlock(path, contents, options) {
213
258
  }
214
259
  function printBlockNextLine(path, contents) {
215
260
  const parentType = path.parent?.type;
216
- const prefix = parentType != null && [
261
+ const isChildOfBodyDeclaration = parentType != null && [
217
262
  "annotation_type_body",
218
263
  "class_body",
219
264
  "enum_body",
220
265
  "interface_body"
221
- ].includes(parentType) ? [] : [hardline$7];
266
+ ].includes(parentType);
267
+ const isBlockStatement = parentType != null && [
268
+ "block",
269
+ "constructor_body",
270
+ "switch_block_statement_group"
271
+ ].includes(parentType);
272
+ const prefix = isChildOfBodyDeclaration || isBlockStatement ? [] : [hardline$7];
222
273
  if (contents.length) return [
223
274
  ...prefix,
224
275
  "{",
@@ -269,7 +320,7 @@ function printBodyDeclarations(path, print, padFirst = false) {
269
320
  return blankLine ? [separator, declaration] : declaration;
270
321
  }, "namedChildren");
271
322
  }
272
- function printTypeParameters(path, print) {
323
+ function printTypeParameters(path, print, options) {
273
324
  const parameters = path.node.namedChildren;
274
325
  if (parameters.length === 0 || parameters.length === 1 && isSimpleType(parameters[0]) && !parameters.some(({ comments }) => comments?.length && comments.some(({ type }) => type === "line_comment"))) return [
275
326
  "<",
@@ -279,7 +330,7 @@ function printTypeParameters(path, print) {
279
330
  const parts = [
280
331
  "<",
281
332
  indent$7([softline$4, join$7([",", line$7], path.map(print, "namedChildren"))]),
282
- softline$4,
333
+ closingBracketLine(options),
283
334
  ">"
284
335
  ];
285
336
  return path.node.type === "type_arguments" ? group$7(parts) : parts;
@@ -309,6 +360,12 @@ function printAssignment(leftDoc, operator, rightDoc, rightNode) {
309
360
  indentIfBreak$2(rightDoc, { groupId })
310
361
  ]);
311
362
  }
363
+ function getLanguageByLanguageName(languages, languageName) {
364
+ return languages.find(({ name }) => name.toLowerCase() === languageName) ?? languages.find(({ aliases }) => aliases?.includes(languageName)) ?? languages.find(({ extensions }) => extensions?.includes(`.${languageName}`));
365
+ }
366
+ function inferParser(options, language) {
367
+ return getLanguageByLanguageName(options.plugins?.filter((plugin) => typeof plugin !== "string" && !(plugin instanceof URL)).reverse().flatMap((plugin) => plugin.languages ?? []) ?? [], language)?.parsers[0];
368
+ }
312
369
  function printTextBlock(contents) {
313
370
  return [
314
371
  "\"\"\"",
@@ -317,13 +374,15 @@ function printTextBlock(contents) {
317
374
  "\"\"\""
318
375
  ];
319
376
  }
320
- function embedTextBlock(path) {
377
+ function embedTextBlock(path, options) {
321
378
  if (path.node.namedChildren.some(({ type }) => type === "string_interpolation") || path.node.children[0].value === "\"") return null;
322
379
  const language = findEmbeddedLanguage(path);
323
380
  if (!language) return null;
381
+ const parser = inferParser(options, language);
382
+ if (!parser) return null;
324
383
  const text = unescapeTextBlockContents(textBlockContents(path.node));
325
384
  return async (textToDoc) => {
326
- return printTextBlock([escapeDocForTextBlock(await textToDoc(text, { parser: language })), hardline$7]);
385
+ return printTextBlock([escapeDocForTextBlock(await textToDoc(text, { parser })), hardline$7]);
327
386
  };
328
387
  }
329
388
  function textBlockContents(node) {
@@ -620,7 +679,7 @@ var blocks_and_statements_default = {
620
679
  ";"
621
680
  ];
622
681
  },
623
- for_statement(path, print) {
682
+ for_statement(path, print, options) {
624
683
  const danglingComments = printDanglingComments(path);
625
684
  const hasInit = path.node.initNodes.length > 0;
626
685
  const hasCondition = hasChild(path, "conditionNode");
@@ -628,13 +687,13 @@ var blocks_and_statements_default = {
628
687
  const expressions = [
629
688
  !hasInit ? ";" : path.node.initNodes[0].type === "local_variable_declaration" ? path.call(print, "initNodes", 0) : [printExpressionList(path.map(print, "initNodes")), ";"],
630
689
  hasCondition ? [path.call(print, "conditionNode"), ";"] : ";",
631
- hasUpdate ? printExpressionList(path.map(print, "updateNodes")) : ""
690
+ hasUpdate ? [line$6, printExpressionList(path.map(print, "updateNodes"))] : ifBreak$1("", " ")
632
691
  ];
633
692
  const hasEmptyStatement = path.node.bodyNode.type === ";";
634
693
  return [
635
694
  danglingComments && [danglingComments, hardline$6],
636
695
  "for ",
637
- hasInit || hasCondition || hasUpdate ? group$6(indentInParentheses(join$6(line$6, expressions))) : "(;;)",
696
+ hasInit || hasCondition || hasUpdate ? group$6(indentInParentheses([join$6(line$6, expressions.slice(0, 2)), expressions[2]], options)) : "(;;)",
638
697
  hasEmptyStatement ? ";" : [" ", path.call(print, "bodyNode")]
639
698
  ];
640
699
  },
@@ -697,7 +756,7 @@ var blocks_and_statements_default = {
697
756
  },
698
757
  catch_clause(path, print, options) {
699
758
  const catchFormalParameterIndex = path.node.namedChildren.findIndex(({ type }) => type === "catch_formal_parameter");
700
- const params = group$6(indentInParentheses(path.call(print, "namedChildren", catchFormalParameterIndex)));
759
+ const params = group$6(indentInParentheses(path.call(print, "namedChildren", catchFormalParameterIndex), options));
701
760
  const body = path.call(print, "bodyNode");
702
761
  if (options?.braceStyle === "next-line") return [
703
762
  hardline$6,
@@ -750,7 +809,7 @@ var blocks_and_statements_default = {
750
809
  ...clauses
751
810
  ]);
752
811
  },
753
- resource_specification(path, print) {
812
+ resource_specification(path, print, options) {
754
813
  const resources = [];
755
814
  let hasTrailingSemicolon = false;
756
815
  path.each((child) => {
@@ -761,7 +820,7 @@ var blocks_and_statements_default = {
761
820
  }, "children");
762
821
  const parts = join$6([";", line$6], resources);
763
822
  if (hasTrailingSemicolon) parts.push(ifBreak$1(";"));
764
- return group$6(indentInParentheses(parts));
823
+ return group$6(indentInParentheses(parts, options));
765
824
  },
766
825
  resource(path, print) {
767
826
  if (hasChild(path, "typeNode") && hasChild(path, "nameNode") && hasChild(path, "valueNode")) {
@@ -791,28 +850,28 @@ var blocks_and_statements_default = {
791
850
  function printExpressionList(expressions) {
792
851
  return group$6(expressions.map((expression, index) => index === 0 ? expression : [",", indent$6([line$6, expression])]));
793
852
  }
794
- function printReturnOrThrowArgument(path, print) {
853
+ function printReturnOrThrowArgument(path, print, options) {
795
854
  const { node } = path;
796
855
  const argumentDoc = print(path);
797
856
  if (returnArgumentHasLeadingComment(node)) return [
798
857
  "(",
799
858
  indent$6([hardline$6, argumentDoc]),
800
- hardline$6,
859
+ closingBracketLine(options, hardline$6),
801
860
  ")"
802
861
  ];
803
862
  if (node.type === "binary_expression") return group$6([
804
863
  ifBreak$1("("),
805
864
  indent$6([softline$3, argumentDoc]),
806
- softline$3,
865
+ closingBracketLine(options),
807
866
  ifBreak$1(")")
808
867
  ]);
809
868
  return argumentDoc;
810
869
  }
811
- function printReturnOrThrowStatement(path, print) {
870
+ function printReturnOrThrowStatement(path, print, options) {
812
871
  const { node } = path;
813
872
  return [
814
873
  node.type === "throw_statement" ? "throw" : "return",
815
- node.namedChildren.length ? [" ", path.call(() => printReturnOrThrowArgument(path, print), "namedChildren", 0)] : "",
874
+ node.namedChildren.length ? [" ", path.call(() => printReturnOrThrowArgument(path, print, options), "namedChildren", 0)] : "",
816
875
  ";"
817
876
  ];
818
877
  }
@@ -830,7 +889,7 @@ var classes_default = {
830
889
  const hasMultipleClauses = definedClauses.length > 1;
831
890
  if (hasChild(path, "type_parametersNode")) {
832
891
  const typeParameters = path.call(print, "type_parametersNode");
833
- parts.push(hasMultipleClauses ? group$5(indent$5(typeParameters)) : typeParameters);
892
+ parts.push(hasMultipleClauses ? group$5(indent$5(typeParameters)) : options?.braceStyle === "next-line" ? group$5(typeParameters) : typeParameters);
834
893
  }
835
894
  if (definedClauses.length) {
836
895
  const clauses = definedClauses.flatMap((clause) => {
@@ -896,14 +955,14 @@ var classes_default = {
896
955
  receiver_parameter(path, print) {
897
956
  return path.map((child) => child.isLast ? print(child) : [print(child), child.node.type === "identifier" ? "." : " "], "namedChildren");
898
957
  },
899
- formal_parameters(path, print) {
958
+ formal_parameters(path, print, options) {
900
959
  const parameters = path.parent?.type === "record_declaration" ? printBodyDeclarations(path, print) : path.map(print, "namedChildren");
901
960
  if (parameters.length === 0) return [
902
961
  "(",
903
962
  printDanglingCommentsInList(path),
904
963
  ")"
905
964
  ];
906
- return indentInParentheses(join$5([",", line$5], parameters));
965
+ return indentInParentheses(join$5([",", line$5], parameters), options);
907
966
  },
908
967
  formal_parameter(path, print) {
909
968
  const parameter = printModifiers(path, print, path.grandparent?.type === "record_declaration" ? "avoidBreak" : "noBreak");
@@ -972,12 +1031,12 @@ var classes_default = {
972
1031
  if (modifiers.length || typeAnnotations.length) parts.push(join$5(" ", [...modifiers, ...typeAnnotations]));
973
1032
  return join$5(annotationMode === "avoidBreak" ? line$5 : annotationMode === "noBreak" ? " " : hardline$5, parts);
974
1033
  },
975
- enum_declaration(path, print) {
1034
+ enum_declaration(path, print, options) {
976
1035
  const modifiers = printModifiers(path, print, "declarationOnly");
977
1036
  const parts = ["enum ", path.call(print, "nameNode")];
978
1037
  if (hasChild(path, "interfacesNode")) {
979
1038
  const hasBody = path.node.bodyNode.namedChildren.length > 0;
980
- parts.push(indent$5([hardline$5, path.call(print, "interfacesNode")]), hasBody ? line$5 : " ");
1039
+ parts.push(indent$5([hardline$5, path.call(print, "interfacesNode")]), options?.braceStyle === "next-line" ? "" : hasBody ? line$5 : " ");
981
1040
  } else parts.push(" ");
982
1041
  return [
983
1042
  ...modifiers,
@@ -1086,7 +1145,7 @@ var expressions_default = {
1086
1145
  const shouldPutBodyOnSameLine = !functionBody.comments?.some((comment) => comment.leading && hasNewline$1(options.originalText, comment.end.index)) && mayBreakAfterShortPrefix(functionBody);
1087
1146
  const chainGroupId = Symbol("arrow-chain");
1088
1147
  const signaturesDoc = printArrowFunctionSignatures(path, { signatureDocs });
1089
- bodyDoc = printArrowFunctionBody(path, args, {
1148
+ bodyDoc = printArrowFunctionBody(path, args, options, {
1090
1149
  bodyDoc,
1091
1150
  bodyComments,
1092
1151
  shouldPutBodyOnSameLine
@@ -1104,7 +1163,7 @@ var expressions_default = {
1104
1163
  }, "namedChildren");
1105
1164
  if (!identifiers.length) return "()";
1106
1165
  const parameters = join$4([",", line$4], identifiers);
1107
- if (identifiers.length > 1 || willBreak(identifiers)) return group$4(indentInParentheses(parameters));
1166
+ if (identifiers.length > 1 || willBreak(identifiers)) return group$4(indentInParentheses(parameters, options));
1108
1167
  return options.arrowParens === "avoid" ? parameters : [
1109
1168
  "(",
1110
1169
  ...parameters,
@@ -1145,9 +1204,9 @@ var expressions_default = {
1145
1204
  const result = maybeGroup([
1146
1205
  printTernaryTest(path, print),
1147
1206
  forceNoIndent ? parts : indent$4(parts),
1148
- breakClosingParen && !shouldExtraIndent ? softline$1 : ""
1207
+ breakClosingParen && !shouldExtraIndent ? closingBracketLine(options) : ""
1149
1208
  ]);
1150
- return isParentTest || shouldExtraIndent ? group$4([indent$4([softline$1, result]), softline$1]) : result;
1209
+ return isParentTest || shouldExtraIndent ? group$4([indent$4([softline$1, result]), closingBracketLine(options)]) : result;
1151
1210
  },
1152
1211
  assignment_expression(path, print) {
1153
1212
  const { node } = path;
@@ -1160,7 +1219,7 @@ var expressions_default = {
1160
1219
  const isInsideParentheses = (grandparent?.type === "if_statement" || grandparent?.type === "while_statement" || grandparent?.type === "switch_expression" || grandparent?.type === "do_statement") && grandparent.conditionNode === parent || grandparent?.type === "synchronized_statement" && grandparent.bodyNode !== parent;
1161
1220
  const parts = printBinaryExpressions(path, print, options, isInsideParentheses);
1162
1221
  if (isInsideParentheses) return parts;
1163
- if (parent?.type === "unary_expression" && !node.comments || (parent?.type === "explicit_constructor_invocation" || parent?.type === "field_access" || parent?.type === "method_invocation") && parent.objectNode === node || parent?.type === "method_reference" || parent?.type === "object_creation_expression") return group$4([indent$4([softline$1, ...parts]), softline$1]);
1222
+ if (parent?.type === "unary_expression" && !node.comments || (parent?.type === "explicit_constructor_invocation" || parent?.type === "field_access" || parent?.type === "method_invocation") && parent.objectNode === node || parent?.type === "method_reference" || parent?.type === "object_creation_expression") return group$4([indent$4([softline$1, ...parts]), closingBracketLine(options)]);
1164
1223
  if (isReturnOrThrowStatement(parent) || parent?.type === "assignment_expression" || parent?.type === "variable_declarator" || parent?.type === "guard" || parent?.type === "lambda_expression" && parent.bodyNode === node || parent?.type === "for_statement" && parent.conditionNode === node || parent?.type === "ternary_expression" && !isReturnOrThrowStatement(grandparent) && grandparent?.type !== "parenthesized_expression" && grandparent?.type !== "argument_list" || parent?.type === "unary_expression" && parent.operandNode === node) return group$4(parts);
1165
1224
  if (parts.length === 0) return "";
1166
1225
  const firstGroupIndex = parts.findIndex((part) => typeof part !== "string" && !Array.isArray(part) && part.type === "group");
@@ -1175,8 +1234,12 @@ var expressions_default = {
1175
1234
  return [(options.experimentalOperatorPosition === "start" ? child.node.type === "instanceof" : child.previous.type === "instanceof") ? line$4 : " ", doc];
1176
1235
  }, "children")));
1177
1236
  },
1178
- unary_expression(path, print) {
1179
- return path.map(print, "children");
1237
+ unary_expression(path, print, options) {
1238
+ const parts = [path.call(print, "operatorNode")];
1239
+ const operandDoc = path.call(print, "operandNode");
1240
+ if (path.node.operandNode.comments) parts.push(group$4(indentInParentheses(operandDoc, options)));
1241
+ else parts.push(operandDoc);
1242
+ return parts;
1180
1243
  },
1181
1244
  field_access: printMemberChain,
1182
1245
  generic_type(path, print) {
@@ -1184,7 +1247,7 @@ var expressions_default = {
1184
1247
  const typeArgumentsIndex = path.node.namedChildren.findIndex(({ type }) => type === "type_arguments");
1185
1248
  return [path.call(print, "namedChildren", typeIdentifierIndex), path.call(print, "namedChildren", typeArgumentsIndex)];
1186
1249
  },
1187
- parenthesized_expression(path, print) {
1250
+ parenthesized_expression(path, print, options) {
1188
1251
  const expression = path.call(print, "namedChildren", 0);
1189
1252
  const parentType = path.parent?.type;
1190
1253
  const grandparentType = path.grandparent?.type;
@@ -1193,12 +1256,12 @@ var expressions_default = {
1193
1256
  const hasTernary = expressionType === "ternary_expression";
1194
1257
  const hasSuffix = parentType && (parentType === "array_access" || parentType === "explicit_constructor_invocation" || parentType === "field_access" || parentType === "method_invocation" || parentType === "method_reference" || parentType === "object_creation_expression");
1195
1258
  const isAssignment = parentType && (parentType === "assignment_expression" || parentType === "variable_declarator") || hasSuffix && (grandparentType === "assignment_expression" || grandparentType === "variable_declarator");
1196
- if (!hasLambda && hasSuffix && (!hasTernary || isAssignment)) return group$4(indentInParentheses(hasTernary ? group$4(expression) : expression));
1197
- else if (parentType && (parentType === "guard" || parentType === "return_statement" || parentType === "unary_expression" && grandparentType === "return_statement" || path.node.fieldName === "condition" && (parentType === "do_statement" || parentType === "if_statement" || parentType === "switch_expression" || parentType === "while_statement") || path.node.fieldName !== "body" && parentType === "synchronized_statement")) return group$4(indentInParentheses(group$4(expression)));
1259
+ if (!hasLambda && hasSuffix && (!hasTernary || isAssignment)) return group$4(indentInParentheses(hasTernary ? group$4(expression) : expression, options));
1260
+ else if (parentType && (parentType === "guard" || parentType === "return_statement" || parentType === "unary_expression" && grandparentType === "return_statement" || path.node.fieldName === "condition" && (parentType === "do_statement" || parentType === "if_statement" || parentType === "switch_expression" || parentType === "while_statement") || path.node.fieldName !== "body" && parentType === "synchronized_statement")) return group$4(indentInParentheses(group$4(expression), options));
1198
1261
  else if (hasTernary && hasSuffix && !isAssignment) return group$4([
1199
1262
  "(",
1200
1263
  expression,
1201
- softline$1,
1264
+ closingBracketLine(options),
1202
1265
  ")"
1203
1266
  ]);
1204
1267
  else return group$4([
@@ -1241,7 +1304,7 @@ var expressions_default = {
1241
1304
  const args = path.node.namedChildren;
1242
1305
  if (args.length === 0) {
1243
1306
  const shouldBreak = path.node.comments?.some(({ type, leading, trailing }) => !leading && !trailing && type === "line_comment");
1244
- return group$4(indentInParentheses(printDanglingComments(path)), { shouldBreak });
1307
+ return group$4(indentInParentheses(printDanglingComments(path), options), { shouldBreak });
1245
1308
  }
1246
1309
  const lastArgIndex = args.length - 1;
1247
1310
  let anyArgEmptyLine = false;
@@ -1267,7 +1330,7 @@ var expressions_default = {
1267
1330
  return group$4([
1268
1331
  "(",
1269
1332
  indent$4([line$4, ...printedArguments]),
1270
- line$4,
1333
+ closingBracketLine(options, line$4),
1271
1334
  ")"
1272
1335
  ], { shouldBreak: true });
1273
1336
  }
@@ -1339,7 +1402,7 @@ var expressions_default = {
1339
1402
  allArgsBrokenOut()
1340
1403
  ]);
1341
1404
  }
1342
- return group$4(indentInParentheses(printedArguments), { shouldBreak: printedArguments.some(willBreak) || anyArgEmptyLine });
1405
+ return group$4(indentInParentheses(printedArguments, options), { shouldBreak: printedArguments.some(willBreak) || anyArgEmptyLine });
1343
1406
  },
1344
1407
  array_creation_expression(path, print) {
1345
1408
  const parts = ["new "];
@@ -1380,8 +1443,8 @@ var expressions_default = {
1380
1443
  record_pattern(path, print) {
1381
1444
  return path.map(print, "children");
1382
1445
  },
1383
- record_pattern_body(path, print) {
1384
- return group$4(indentInParentheses(join$4([",", line$4], path.map(print, "namedChildren"))));
1446
+ record_pattern_body(path, print, options) {
1447
+ return group$4(indentInParentheses(join$4([",", line$4], path.map(print, "namedChildren")), options));
1385
1448
  },
1386
1449
  record_pattern_component(path, print) {
1387
1450
  return join$4(" ", path.map(print, "children"));
@@ -1433,8 +1496,8 @@ function printArrowFunctionSignatures(path, { signatureDocs }) {
1433
1496
  if (node.fieldName === "object" && parent?.type === "method_invocation") return group$4(join$4([" ->", line$4], signatureDocs));
1434
1497
  return group$4(indent$4(join$4([" ->", line$4], signatureDocs)));
1435
1498
  }
1436
- function printArrowFunctionBody(path, args, { bodyDoc, bodyComments, shouldPutBodyOnSameLine }) {
1437
- const trailingSpace = args && typeof args === "object" && "expandLastArg" in args && args.expandLastArg && !path.node.comments ? softline$1 : "";
1499
+ function printArrowFunctionBody(path, args, options, { bodyDoc, bodyComments, shouldPutBodyOnSameLine }) {
1500
+ const trailingSpace = args && typeof args === "object" && "expandLastArg" in args && args.expandLastArg && !path.node.comments ? closingBracketLine(options) : "";
1438
1501
  return shouldPutBodyOnSameLine ? [
1439
1502
  " ",
1440
1503
  bodyDoc,
@@ -1791,7 +1854,7 @@ var interfaces_default = {
1791
1854
  const hasMultipleClauses = hasExtendsInterfaces && hasPermits;
1792
1855
  if (hasChild(path, "type_parametersNode")) {
1793
1856
  const typeParameters = path.call(print, "type_parametersNode");
1794
- parts.push(hasMultipleClauses ? group$3(indent$3(typeParameters)) : typeParameters);
1857
+ parts.push(hasMultipleClauses ? group$3(indent$3(typeParameters)) : options?.braceStyle === "next-line" ? group$3(typeParameters) : typeParameters);
1795
1858
  }
1796
1859
  if (hasExtendsInterfaces || hasPermits) {
1797
1860
  const clauses = [];
@@ -1841,13 +1904,13 @@ var interfaces_default = {
1841
1904
  marker_annotation(path, print) {
1842
1905
  return ["@", path.call(print, "nameNode")];
1843
1906
  },
1844
- annotation_argument_list(path, print) {
1907
+ annotation_argument_list(path, print, options) {
1845
1908
  const args = path.map(print, "namedChildren");
1846
1909
  return args.length === 1 && path.node.namedChildren[0].type === "element_value_array_initializer" ? [
1847
1910
  "(",
1848
1911
  args[0],
1849
1912
  ")"
1850
- ] : group$3(indentInParentheses(join$3([",", line$3], args)));
1913
+ ] : group$3(indentInParentheses(join$3([",", line$3], args), options));
1851
1914
  },
1852
1915
  element_value_pair(path, print) {
1853
1916
  return group$3([
@@ -1918,11 +1981,11 @@ var names_default = {
1918
1981
  //#region src/printers/packages-and-modules.ts
1919
1982
  const { group: group$1, hardline: hardline$1, indent: indent$1, join: join$1, line: line$2 } = builders;
1920
1983
  var packages_and_modules_default = {
1921
- program(path, print) {
1984
+ program(path, print, options) {
1922
1985
  if (!path.node.namedChildren.length) return [printDanglingComments(path), hardline$1];
1923
1986
  const parts = [];
1924
1987
  if (path.node.namedChildren[0].type !== "import_declaration") parts.push(path.call(print, "namedChildren", 0));
1925
- const staticImports = [];
1988
+ const moduleImports = [];
1926
1989
  const imports = [];
1927
1990
  const otherDeclarations = [];
1928
1991
  path.each((child) => {
@@ -1930,13 +1993,15 @@ var packages_and_modules_default = {
1930
1993
  if (child.node.type === "import_declaration") {
1931
1994
  const names = extractNames(child.node.namedChildren[0]);
1932
1995
  if (child.node.namedChildren.some(({ type }) => type === "asterisk")) names.push("*");
1933
- (child.node.children[1].type === "static" ? staticImports : imports).push({
1996
+ const modifier = child.node.children[1].type;
1997
+ (modifier === "module" ? moduleImports : imports).push({
1934
1998
  doc,
1935
- names
1999
+ names,
2000
+ isStatic: modifier === "static"
1936
2001
  });
1937
2002
  } else if (!child.isFirst) otherDeclarations.push(doc);
1938
2003
  }, "namedChildren");
1939
- parts.push(...[staticImports, imports].filter((imports) => imports.length).map((imports) => join$1(hardline$1, imports.sort(compareFqn).map(({ doc }) => doc))), ...otherDeclarations);
2004
+ parts.push(...[moduleImports.sort(compareFqn), ...groupImports(imports, options.importOrder)].filter((group) => group.length).map((group) => join$1(hardline$1, group.map(({ doc }) => doc))), ...otherDeclarations);
1940
2005
  return [...join$1([hardline$1, hardline$1], parts), hardline$1];
1941
2006
  },
1942
2007
  package_declaration(path, print) {
@@ -1960,7 +2025,8 @@ var packages_and_modules_default = {
1960
2025
  },
1961
2026
  import_declaration(path, print) {
1962
2027
  const declaration = ["import "];
1963
- if (path.node.children.some(({ type }) => type === "static")) declaration.push("static ");
2028
+ const modifier = path.node.children[1].type;
2029
+ if (modifier === "module" || modifier === "static") declaration.push(modifier, " ");
1964
2030
  const identifierIndex = path.node.namedChildren.findIndex(({ type }) => type === "identifier" || type === "scoped_identifier");
1965
2031
  declaration.push(path.call(print, "namedChildren", identifierIndex));
1966
2032
  if (path.node.namedChildren.some(({ type }) => type === "asterisk")) declaration.push(".*");
@@ -2014,6 +2080,46 @@ var packages_and_modules_default = {
2014
2080
  function extractNames(node) {
2015
2081
  return node.type === "identifier" ? [node.value] : [...extractNames(node.scopeNode), node.nameNode.value];
2016
2082
  }
2083
+ const STATIC_PREFIX = "static ";
2084
+ const CATCH_ALL_PREFIX = "";
2085
+ function groupImports(imports, importOrder) {
2086
+ if (!importOrder) return [imports.filter(({ isStatic }) => isStatic), imports.filter(({ isStatic }) => !isStatic)].filter((group) => group.length).map((group) => group.sort(compareFqn));
2087
+ const groups = parseImportOrder(importOrder);
2088
+ const prefixes = groups.flat();
2089
+ const importsByPrefix = /* @__PURE__ */ new Map();
2090
+ for (const entry of imports) {
2091
+ const key = importKey(entry);
2092
+ const prefix = prefixes.filter((candidate) => key.startsWith(candidate)).reduce((best, candidate) => candidate.length > best.length ? candidate : best);
2093
+ importsByPrefix.set(prefix, [...importsByPrefix.get(prefix) ?? [], {
2094
+ key,
2095
+ entry
2096
+ }]);
2097
+ }
2098
+ return groups.map((group) => group.flatMap((prefix) => (importsByPrefix.get(prefix) ?? []).sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0).map(({ entry }) => entry))).filter((group) => group.length);
2099
+ }
2100
+ /**
2101
+ * Parses an import order in Spotless's format: groups separated by `,` are
2102
+ * printed with blank lines between them, prefixes joined by `|` share a group,
2103
+ * `#` marks a static prefix, and an empty prefix catches everything else.
2104
+ * Each import belongs to its longest matching prefix.
2105
+ */
2106
+ function parseImportOrder(importOrder) {
2107
+ const knownPrefixes = /* @__PURE__ */ new Set();
2108
+ const groups = importOrder.split(",").map((group) => group.split("|").map((prefix) => prefix.startsWith("#") ? STATIC_PREFIX + prefix.slice(1) : prefix).filter((prefix) => {
2109
+ if (knownPrefixes.has(prefix)) return false;
2110
+ knownPrefixes.add(prefix);
2111
+ return true;
2112
+ }));
2113
+ if (!knownPrefixes.has(STATIC_PREFIX)) {
2114
+ const lastStaticGroupIndex = groups.findLastIndex((group) => group.some((prefix) => prefix.startsWith(STATIC_PREFIX)));
2115
+ groups.splice(Math.max(lastStaticGroupIndex, 0), 0, [STATIC_PREFIX]);
2116
+ }
2117
+ if (!knownPrefixes.has(CATCH_ALL_PREFIX)) groups.push([CATCH_ALL_PREFIX]);
2118
+ return groups;
2119
+ }
2120
+ function importKey({ names, isStatic }) {
2121
+ return (isStatic ? STATIC_PREFIX : "") + names.join(".");
2122
+ }
2017
2123
  function compareFqn({ names: a }, { names: b }) {
2018
2124
  const minParts = Math.min(a.length, b.length);
2019
2125
  for (let i = 0; i < minParts; i++) {
@@ -2114,8 +2220,8 @@ var printer_default = {
2114
2220
  ")"
2115
2221
  ] : doc;
2116
2222
  },
2117
- embed(path) {
2118
- return hasType(path, "string_literal") ? embedTextBlock(path) : null;
2223
+ embed(path, options) {
2224
+ return hasType(path, "string_literal") ? embedTextBlock(path, options) : null;
2119
2225
  },
2120
2226
  hasPrettierIgnore(path) {
2121
2227
  return path.node.comments?.some(isPrettierIgnore) === true || canAttachComment(path.node, path.parent ? [path.parent] : []) && isFullyBetweenPrettierIgnore(path);
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prettier-java-next-line",
3
- "version": "2.10.4",
3
+ "version": "2.11.1",
4
4
  "description": "Prettier Java plugin with configurable same-line and next-line braces",
5
5
  "homepage": "https://github.com/motlin/prettier-java-next-line",
6
6
  "repository": {
@@ -39,6 +39,7 @@
39
39
  "lerna:version": "lerna version --exact --no-private",
40
40
  "lint": "eslint src test",
41
41
  "prepare": "husky",
42
+ "prepublishOnly": "npm run build",
42
43
  "test": "yarn run test:unit && yarn run test:e2e-core",
43
44
  "test:all": "yarn run test && yarn run test:e2e-jhipster1 && yarn run test:e2e-jhipster2",
44
45
  "test:e2e-core": "node scripts/clone-samples e2e-core && mocha \"test/repository-test/core-test.ts\"",
@@ -77,7 +78,7 @@
77
78
  "postcss": "^8.5.15",
78
79
  "prettier": "^3.8.1",
79
80
  "prettier-plugin-css-order": "^2.2.0",
80
- "tree-sitter-java-orchard": "0.5.18",
81
+ "tree-sitter-java-orchard": "0.5.22",
81
82
  "tsdown": "^0.23.0",
82
83
  "typescript": "^6.0.3",
83
84
  "typescript-eslint": "^8.57.1"