Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8: Formatting lambda with newlines and indentation

What I would like to achieve with lambda indentation is the following:

Multi-line statement:

String[] ppl = new String[] { "Karen (F)", "Kevin (M)", "Lee (M)", "Joan (F)", "Des (M)", "Rick (M)" }; List<String> strings = Arrays.stream(ppl)                          .filter(                              (x) ->                               {                                  return x.contains("(M)");                              }                          ).collect(Collectors.toList()); strings.stream().forEach(System.out::println); 

Single-line statement:

List<String> strings = Arrays.stream(ppl)                          .map((x) -> x.toUpperCase())                          .filter((x) -> x.contains("(M)"))                          .collect(Collectors.toList()); 



Currently, Eclipse is auto-formatting to the following:

Multi-line statement:

String[] ppl = new String[] { "Karen (F)", "Kevin (M)", "Lee (M)", "Joan (F)", "Des (M)", "Rick (M)" }; List<String> strings = Arrays.stream(ppl).filter((x) -> {     return x.contains("(M)"); }).collect(Collectors.toList()); strings.stream().forEach(System.out::println); 

Single-line statement:

String[] ppl = new String[] { "Karen (F)", "Kevin (M)", "Lee (M)", "Joan (F)", "Des(M)", "Rick (M)" }; List<String> strings = Arrays.stream(ppl).map((x) -> x.toUpperCase())         .filter((x) -> x.contains("(M)")).collect(Collectors.toList()); strings.stream().forEach(System.out::println); 

And I find this really messy, because of how the collect call is directly underneath the return and there's no space inbetween at all. I would prefer it if I could start the lambda in a new line indented, and so that the .filter( call would be right above the .collect( call. However, the only thing that can be customized with standard Java-8 Eclipse Formatter is the brace at the start of the lambda body, but nothing for the () brackets beforehand, nor the indentation.

And in the case of single-line calls, it just uses the basic line-wrap and makes it be a chained mess. I don't think I need to explain why this is hard to decrypt afterwards.

Is there any way to somehow customize the formatting more and achieve the first formatting type in Eclipse? (Or, optionally, in another IDE like IntelliJ IDEA.)



EDIT: The closest I could get was with IntelliJ IDEA 13 Community Edition (read: free edition :P) which was the following (defined by continuous indentation which in this case is 8):

public static void main(String[] args) {     int[] x = new int[] {1, 2, 3, 4, 5, 6, 7};     int sum = Arrays.stream(x)             .map((n) -> n * 5)             .filter((n) -> {                 System.out.println("Filtering: " + n);                 return n % 3 != 0;             })             .reduce(0, Integer::sum);      List<Integer> list = Arrays.stream(x)             .filter((n) -> n % 2 == 0)             .map((n) -> n * 4)             .boxed()             .collect(Collectors.toList());     list.forEach(System.out::println);     System.out.println(sum);     

It also allows to "align" the chained method invocation like this:

    int sum = Arrays.stream(x)                     .map((n) -> n * 5)                     .filter((n) -> {                         System.out.println("Filtering: " + n);                         return n % 3 != 0;                     })                     .reduce(0, Integer::sum);       List<Integer> list = Arrays.stream(x)                                .filter((n) -> n % 2 == 0)                                .map((n) -> n * 4)                                .boxed()                                .collect(Collectors.toList());     list.forEach(System.out::println);     System.out.println(sum); } 

I personally find that while it makes more sense, the second version pushes it far too away, so I prefer the first one.

The setup responsible for the first setup is the following:

<?xml version="1.0" encoding="UTF-8"?> <code_scheme name="Zhuinden">   <option name="JD_ALIGN_PARAM_COMMENTS" value="false" />   <option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />   <option name="JD_ADD_BLANK_AFTER_PARM_COMMENTS" value="true" />   <option name="JD_ADD_BLANK_AFTER_RETURN" value="true" />   <option name="JD_P_AT_EMPTY_LINES" value="false" />   <option name="JD_PARAM_DESCRIPTION_ON_NEW_LINE" value="true" />   <option name="WRAP_COMMENTS" value="true" />   <codeStyleSettings language="JAVA">     <option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />     <option name="BRACE_STYLE" value="2" />     <option name="CLASS_BRACE_STYLE" value="2" />     <option name="METHOD_BRACE_STYLE" value="2" />     <option name="ELSE_ON_NEW_LINE" value="true" />     <option name="WHILE_ON_NEW_LINE" value="true" />     <option name="CATCH_ON_NEW_LINE" value="true" />     <option name="FINALLY_ON_NEW_LINE" value="true" />     <option name="ALIGN_MULTILINE_PARAMETERS" value="false" />     <option name="SPACE_WITHIN_BRACES" value="true" />     <option name="SPACE_BEFORE_IF_PARENTHESES" value="false" />     <option name="SPACE_BEFORE_WHILE_PARENTHESES" value="false" />     <option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />     <option name="SPACE_BEFORE_TRY_PARENTHESES" value="false" />     <option name="SPACE_BEFORE_CATCH_PARENTHESES" value="false" />     <option name="SPACE_BEFORE_SWITCH_PARENTHESES" value="false" />     <option name="SPACE_BEFORE_SYNCHRONIZED_PARENTHESES" value="false" />     <option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true" />     <option name="METHOD_PARAMETERS_WRAP" value="1" />     <option name="EXTENDS_LIST_WRAP" value="1" />     <option name="THROWS_LIST_WRAP" value="1" />     <option name="EXTENDS_KEYWORD_WRAP" value="1" />     <option name="THROWS_KEYWORD_WRAP" value="1" />     <option name="METHOD_CALL_CHAIN_WRAP" value="2" />     <option name="BINARY_OPERATION_WRAP" value="1" />     <option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />     <option name="ASSIGNMENT_WRAP" value="1" />     <option name="IF_BRACE_FORCE" value="3" />     <option name="DOWHILE_BRACE_FORCE" value="3" />     <option name="WHILE_BRACE_FORCE" value="3" />     <option name="FOR_BRACE_FORCE" value="3" />     <option name="PARAMETER_ANNOTATION_WRAP" value="1" />     <option name="VARIABLE_ANNOTATION_WRAP" value="1" />     <option name="ENUM_CONSTANTS_WRAP" value="2" />   </codeStyleSettings> </code_scheme> 

I tried to make sure everything is reasonable, but I might have messed something up, so it might need minor adjustments.

If you're Hungarian like me and you're using a Hungarian layout, then this keymap might be of use to you, so that you don't end up not being able to use AltGR+F, AltGR+G, AltGR+B, AltGR+N and AltGR+M (which correspond to Ctrl+Alt).

<?xml version="1.0" encoding="UTF-8"?> <keymap version="1" name="Default copy" parent="$default">   <action id="ExtractMethod">     <keyboard-shortcut first-keystroke="shift control M" />   </action>   <action id="GotoImplementation">     <mouse-shortcut keystroke="control alt button1" />   </action>   <action id="GotoLine">     <keyboard-shortcut first-keystroke="shift control G" />   </action>   <action id="Inline">     <keyboard-shortcut first-keystroke="shift control O" />   </action>   <action id="IntroduceField">     <keyboard-shortcut first-keystroke="shift control D" />   </action>   <action id="Mvc.RunTarget">     <keyboard-shortcut first-keystroke="shift control P" />   </action>   <action id="StructuralSearchPlugin.StructuralReplaceAction" />   <action id="Synchronize">     <keyboard-shortcut first-keystroke="shift control Y" />   </action> </keymap> 

While IntelliJ doesn't seem to provide a way to put the opening brace of the lambda in a new line, otherwise it's a fairly reasonable way of formatting, so I'll mark this as accepted.

like image 323
EpicPandaForce Avatar asked Jul 09 '14 09:07

EpicPandaForce


2 Answers

In Eclipse, for the single-line statements:

In your project or global preferences, go to Java -> Code Style -> Formatter -> Edit -> Line Wrapping -> Function Calls -> Qualified Invocations, set Wrap all elements, except first if not necessary and tick Force split, even if line shorter than maximum line width.

like image 93
Arend Avatar answered Sep 21 '22 17:09

Arend


Out of the box IntelliJ 13 will probably work for you.

If I write it this way:

// Mulit-Line Statement String[] ppl = new String[] { "Karen (F)", "Kevin (M)", "Lee (M)", "Joan (F)", "Des (M)", "Rick (M)" }; List<String> strings = Arrays.stream(ppl)         .filter(                 (x) ->                 {                     return x.contains("(M)");                 }         ).collect(Collectors.toList()); strings.stream().forEach(System.out::println); 

And then apply the auto formatter (no changes):

// Mulit-Line Statement String[] ppl = new String[]{"Karen (F)", "Kevin (M)", "Lee (M)", "Joan (F)", "Des (M)", "Rick (M)"}; List<String> strings = Arrays.stream(ppl)         .filter(                 (x) ->                 {                     return x.contains("(M)");                 }         ).collect(Collectors.toList()); strings.stream().forEach(System.out::println); 

The same is true for your single line statement. It has been my experience that IntelliJ is more flexible in how its auto formatting is applied. IntelliJ is less likely to remove or add line returns, if you put it there then it assumes you meant to put it there. IntelliJ will happily adjust your tab-space for you.


IntelliJ can also be configured to do some of this for you. Under "settings" -> "code style" -> "java", in the "Wrapping and Braces" tab you can set "chain method calls" to "wrap always".

Before Auto-Formatting

// Mulit-Line Statement List<String> strings = Arrays.stream(ppl).filter((x) -> { return x.contains("(M)"); }).collect(Collectors.toList());  // Single-Line Statement List<String> strings = Arrays.stream(ppl).map((x) -> x.toUpperCase()).filter((x) -> x.contains("(M)")).collect(Collectors.toList()); 

After Auto-Formatting

// Mulit-Line Statement List<String> strings = Arrays.stream(ppl)         .filter((x) -> {             return x.contains("(M)");         })         .collect(Collectors.toList());  // Single-Line Statement List<String> strings = Arrays.stream(ppl)         .map((x) -> x.toUpperCase())         .filter((x) -> x.contains("(M)"))         .collect(Collectors.toList()); 
like image 34
Mike Rylander Avatar answered Sep 22 '22 17:09

Mike Rylander