diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatTokens.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatTokens.scala index f91f85b264..64db04daaf 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatTokens.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatTokens.scala @@ -158,14 +158,17 @@ class FormatTokens(leftTok2tok: Map[TokenHash, Int])(val arr: Array[FormatToken] getClosingIfInParens(last)(getHead(tokens, tree)).getOrElse(last) } - @tailrec final def findTokenWith[A](ft: FormatToken, iter: FormatToken => FormatToken)( f: FormatToken => Option[A], + ): Either[FormatToken, A] = findTokenEx(ft)(xft => f(xft).toRight(iter(xft))) + + @tailrec + final def findTokenEx[A](ft: FormatToken)( + f: FormatToken => Either[FormatToken, A], ): Either[FormatToken, A] = f(ft) match { - case Some(a) => Right(a) - case _ => - val nextFt = iter(ft) - if (nextFt eq ft) Left(ft) else findTokenWith(nextFt, iter)(f) + case null => Left(ft) + case Left(nextFt) if nextFt ne ft => findTokenEx(nextFt)(f) + case x => x } final def findToken(ft: FormatToken, iter: FormatToken => FormatToken)( diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Router.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Router.scala index 2cb1803625..61bd0e6784 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Router.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Router.scala @@ -1205,9 +1205,6 @@ class Router(formatOps: FormatOps) { flags.literalArgList val nlOnly = nlOpen && !singleLineOnly - def findComma(ft: FormatToken) = findFirstOnRight[T.Comma](ft, close) - .map(_.right) - val binPack = style.binPack.callSiteFor(open) val oneline = binPack.isOneline val afterFirstArgOneline = @@ -1266,7 +1263,15 @@ class Router(formatOps: FormatOps) { else Seq(indent) val nextComma = - if (!oneline) findComma(ft) + if (firstArg.isEmpty) None + else if (!oneline) tokens.findTokenEx(ft) { xft => + xft.right match { + case `close` | _: T.RightBrace | _: T.RightArrow => null + case x: T.Comma => Right(x) + case x: T.LeftBrace => Left(tokens(matching(x))) + case _ => Left(next(xft)) + } + }.toOption else if (isSingleArg) None else afterFirstArgOneline.map(_.right) val opt = nextComma.getOrElse(scalaJsOptClose(beforeClose, flags)) diff --git a/scalafmt-tests/shared/src/test/resources/scalajs/Apply.stat b/scalafmt-tests/shared/src/test/resources/scalajs/Apply.stat index e9358512d3..a27ae548f4 100644 --- a/scalafmt-tests/shared/src/test/resources/scalajs/Apply.stat +++ b/scalafmt-tests/shared/src/test/resources/scalajs/Apply.stat @@ -1403,3 +1403,15 @@ object a { varDefs.take(prevArgsCount).toList.map(_.ref))( sym, index, formalArgsRegistry, param, static, captures = Nil) } +<<< #4133 overflow with nested lambda and braces 1 +Block(norm.tail.map(sym => DefDef(sym, { vparamss: List[List[Symbol]] => EmptyTree })), ddef) +>>> +Block(norm.tail.map(sym => + DefDef(sym, { vparamss: List[List[Symbol]] => EmptyTree })), ddef) +<<< #4133 overflow with nested lambda and braces 2 +other.exists(tpe match { case TypeRef(_, r, _) => r.isTypeParameter case _ => false }) +>>> +other.exists(tpe match { + case TypeRef(_, r, _) => r.isTypeParameter + case _ => false +})