5252/** A {@code Level} inside a {@link Doc}. */
5353public final class Level extends Doc {
5454 /**
55- * How many branches we are allowed to take (i.e how many times we can consider breaking vs not breaking the current
56- * level) before we stop branching and always break, which is the google-java-format default behaviour.
55+ * The depth of nested levels in the current level tree from which we explore both breaking vs not breaking the current level.
5756 */
58- private static final int MAX_BRANCHING_COEFFICIENT = 20 ;
57+ private static final int LAST_LEVELS_TO_EXPLORE = 8 ;
5958
6059 private static final Collector <Level , ?, Optional <Level >> GET_LAST_COLLECTOR = Collectors .reducing ((u , v ) -> v );
6160
@@ -64,6 +63,10 @@ public final class Level extends Doc {
6463 @ SuppressWarnings ("Immutable" ) // Effectively immutable
6564 private final ImmutableSupplier <SplitsBreaks > memoizedSplitsBreaks =
6665 Suppliers .memoize (() -> splitByBreaks (docs ))::get ;
66+
67+ @ SuppressWarnings ("Immutable" ) // Effectively immutable
68+ private final ImmutableSupplier <Integer > memoizedMaxDepth = Suppliers .memoize (() -> computeMaxDepth (docs ))::get ;
69+
6770 /** The immutable characteristics of this level determined before the level contents are available. */
6871 private final OpenOp openOp ;
6972
@@ -130,7 +133,7 @@ public State computeBreaks(CommentsHelper commentsHelper, int maxWidth, State st
130133 * into account the level's {@link #getColumnLimitBeforeLastBreak()}.
131134 *
132135 * @return the width after fitting it onto one line, if it was possible. This is guaranteed to be less than
133- * {@code maxWidth}
136+ * {@code maxWidth}
134137 */
135138 private Optional <Integer > tryToFitOnOneLine (int maxWidth , State state , Iterable <Doc > docs ) {
136139 int column = state .column ();
@@ -189,16 +192,15 @@ public State breakThisLevel() {
189192
190193 @ Override
191194 public State preferBreakingLastInnerLevel (boolean _keepIndentWhenInlined ) {
192- // Try both breaking and not breaking. Choose the better one based on LOC, preferring
193- // breaks if the outcome is the same.
194- State state = this .state .withNewBranch ();
195-
196- Obs .Exploration broken = breakNormally (state );
197-
198- if (state .branchingCoefficient () < MAX_BRANCHING_COEFFICIENT ) {
195+ int maxDepth = getMaxDepth ();
196+ // Explore both breaking and not breaking if we're in the last LAST_LEVELS_TO_EXPLORE levels. Choose the
197+ // better one based on LOC, preferring breaks if the outcome is the same.
198+ if (maxDepth <= LAST_LEVELS_TO_EXPLORE ) {
199+ State state = this .state .withNewBranch ();
200+ Obs .Exploration broken = breakNormally (state );
199201 State state1 = state .withNoIndent ();
200202 Optional <Obs .Exploration > lastLevelBroken = levelNode .maybeExplore (
201- "tryBreakLastLevel" ,
203+ "tryBreakLastLevel" + maxDepth ,
202204 state1 ,
203205 explorationNode -> tryBreakLastLevel (commentsHelper , maxWidth , state1 , explorationNode , true ));
204206
@@ -208,8 +210,20 @@ public State preferBreakingLastInnerLevel(boolean _keepIndentWhenInlined) {
208210 return lastLevelBroken .get ().markAccepted ();
209211 }
210212 }
213+ return broken .markAccepted ();
214+ } else {
215+ State state1 = state .withNoIndent ();
216+ Optional <Obs .Exploration > lastLevelBroken = levelNode .maybeExplore (
217+ "tryBreakLastLevel only" + maxDepth ,
218+ state1 ,
219+ explorationNode -> tryBreakLastLevel (commentsHelper , maxWidth , state1 , explorationNode , true ));
220+
221+ if (lastLevelBroken .isPresent ()) {
222+ return lastLevelBroken .get ().markAccepted ();
223+ }
224+ // falling back to breaking normally
211225 }
212- return broken .markAccepted ();
226+ return breakNormally ( state ) .markAccepted ();
213227 }
214228
215229 @ Override
@@ -242,7 +256,7 @@ public State inlineSuffix() {
242256 private Exploration breakNormally (State state , LevelNode levelNode , CommentsHelper commentsHelper , int maxWidth ) {
243257 State stateForBroken = state .withIndentIncrementedBy (getPlusIndent ());
244258 return levelNode .explore (
245- "breaking normally" ,
259+ "breaking normally " + getMaxDepth () ,
246260 stateForBroken ,
247261 explorationNode -> computeBroken (commentsHelper , maxWidth , stateForBroken , explorationNode ));
248262 }
@@ -258,7 +272,7 @@ private Exploration breakNormally(State state, LevelNode levelNode, CommentsHelp
258272 * </ul>
259273 *
260274 * @param brokenState is expected to be the state resulting from {@link Level#breakNormally}, but is passed in
261- * for efficiency reasons
275+ * for efficiency reasons
262276 */
263277 private Optional <State > handle_breakOnlyIfInnerLevelsThenFitOnOneLine (
264278 CommentsHelper commentsHelper ,
@@ -540,17 +554,9 @@ private static Optional<State> tryBreakInnerLevel_checkInner(
540554 keepIndentWhenInlined ,
541555 explorationNode );
542556 case ACCEPT_INLINE_CHAIN_IF_SIMPLE_OTHERWISE_CHECK_INNER :
543- // We will allow inlining this kind of level but only if the level itself is
544- // not simple.
545- if (lastLevel2 .openOp .complexity () == Complexity .SIMPLE ) {
546- return Optional .empty ();
547- }
548- return innerLevel .tryInlinePrefixOntoCurrentLine (
549- commentsHelper ,
550- maxWidth ,
551- state1 ,
552- keepIndentWhenInlined ,
553- explorationNode );
557+ // specific to lambda body expressions - falls back to `breakNormally` in
558+ // `preferBreakingLastInnerLevel`
559+ return Optional .empty ();
554560 default :
555561 throw new RuntimeException ("Unknown breakabilityIfLastLevel: " + lastLevel2 );
556562 }
@@ -736,6 +742,39 @@ public String representation(State state) {
736742 return new LevelDelimitedFlatValueDocVisitor (state ).visit (this );
737743 }
738744
745+ /**
746+ * Get the approximation of the maximum depth of nested levels in this level tree (memoized).
747+ */
748+ private int getMaxDepth () {
749+ return memoizedMaxDepth .get ();
750+ }
751+
752+ /**
753+ * Calculates the maximum depth of nested levels in this level tree.
754+ * This computation is expensive, so it's memoized via {@link #getMaxDepth()}.
755+ */
756+ private int computeMaxDepth (Iterable <Doc > docs ) {
757+ int maxChildDepth = 0 ;
758+ for (Doc doc : docs ) {
759+ if (doc instanceof Level ) {
760+ Level childLevel = (Level ) doc ;
761+ maxChildDepth = Math .max (maxChildDepth , childLevel .getMaxDepth ());
762+ }
763+ }
764+
765+ // We use the presence of breaks as a proxy for meaningful structural nesting.
766+ boolean hasBreaks = false ;
767+ for (Doc doc : docs ) {
768+ if (doc instanceof Break ) {
769+ hasBreaks = true ;
770+ break ;
771+ }
772+ }
773+
774+ // Only count levels that have breaks (which can actually be formatted across multiple lines)
775+ return hasBreaks ? 1 + maxChildDepth : maxChildDepth ;
776+ }
777+
739778 /**
740779 * Get the width of a sequence of {@link Doc}s.
741780 *
@@ -774,6 +813,7 @@ public String toString() {
774813 interface SplitsBreaks {
775814 /** Groups of {@link Doc}s that are children of the current {@link Level}, separated by {@link Break}s. */
776815 ImmutableList <ImmutableList <Doc >> splits ();
816+
777817 /** {@link Break}s between {@link Doc}s in the current {@link Level}. */
778818 ImmutableList <Break > breaks ();
779819 }
0 commit comments