diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala index d523067bc7..4fcb426418 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala @@ -650,7 +650,8 @@ class FormatOps(val tree: Tree, baseStyle: ScalafmtConfig) { ft: FormatToken, newStmtModOrBody: Either[Modification, Tree] )(implicit style: ScalafmtConfig): Seq[Split] = { - val fullInfixTreeOpt = findTreeWithParentSimple(lhsApp.all)(!isInfixApp(_)) + val fullInfixTreeOpt = + findTreeWithParentSimple(lhsApp.all, false)(isInfixApp) val fullInfix = fullInfixTreeOpt.flatMap(asInfixApp).getOrElse(lhsApp) val app = findLeftInfix(fullInfix) newStmtModOrBody.fold( diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/State.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/State.scala index 1ca0142e99..ef696cff98 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/State.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/State.scala @@ -217,9 +217,10 @@ final case class State( if (startsWithLeft(lo)) Some(lo) else None } owner.map { x => - val y = - if (!x.parent.exists(startsWithLeft)) None - else TreeOps.findTreeWithParentSimple(x)(!startsWithLeft(_)) + val y = x.parent.flatMap { p => + if (!startsWithLeft(p)) None + else TreeOps.findTreeWithParentSimple(p, false)(startsWithLeft) + } (ft, y.getOrElse(x)) } } @@ -235,13 +236,13 @@ final case class State( getLineStartOwner(isComment).flatMap { case (lineFt, lineOwner) => val ft = fops.tokens(depth) - val ok = (ft.meta.leftOwner eq lineOwner) || { + val ok = { // comment could be preceded by a comma isComment && ft.left.is[Token.Comma] && (fops.prev(ft).meta.leftOwner eq lineOwner) } || TreeOps - .findTreeWithParentSimple(ft.meta.leftOwner)(_ eq lineOwner) + .findTreeOrParentSimple(ft.meta.leftOwner)(_ eq lineOwner) .isDefined if (ok) Some(lineFt) else None } @@ -389,7 +390,6 @@ object State { @inline private def isWithinInterpolation(tree: meta.Tree): Boolean = - isInterpolation(tree) || - TreeOps.findTreeWithParentSimple(tree)(isInterpolation).isDefined + TreeOps.findTreeOrParentSimple(tree)(isInterpolation).isDefined } diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/util/TokenOps.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/util/TokenOps.scala index a4a7380e6c..a261a68614 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/util/TokenOps.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/util/TokenOps.scala @@ -1,7 +1,7 @@ package org.scalafmt.util import scala.meta.classifiers.Classifier -import scala.meta.{Defn, Pkg, Template, Tree} +import scala.meta.{Defn, Pkg, Source, Template, Term, Tree} import scala.meta.tokens.Token import scala.meta.tokens.Token._ import scala.meta.tokens.Tokens @@ -54,8 +54,13 @@ object TokenOps { style.optIn.forceNewlineBeforeDocstringSummary && ft.right.is[Token.Comment] && !ft.left.is[Token.Comment] && ft.meta.right.text.startsWith("/**") && - !ft.meta.leftOwner.is[meta.Mod] && - !TreeOps.existsParentOfType[meta.Mod](ft.meta.leftOwner) + TreeOps + .findTreeOrParent(ft.meta.leftOwner) { + case _: Pkg | _: Source | _: Template | _: Term.Block => Some(false) + case t if t.pos.end > ft.right.start => Some(true) + case _ => None + } + .isEmpty // 2.13 implements SeqOps.findLast def findLast[A](seq: Seq[A])(cond: A => Boolean): Option[A] = diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/util/TreeOps.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/util/TreeOps.scala index 9fff244bee..b8abad822b 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/util/TreeOps.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/util/TreeOps.scala @@ -229,6 +229,28 @@ object TreeOps { case _ => cnt } + /** + * Returns first ancestor which matches the given predicate. + */ + @tailrec + def findTreeOrParent( + tree: Tree + )(pred: Tree => Option[Boolean]): Option[Tree] = + pred(tree) match { + case Some(true) => Some(tree) + case Some(false) => None + case None => + tree.parent match { + case None => None + case Some(p) => findTreeOrParent(p)(pred) + } + } + def findTreeOrParentSimple( + tree: Tree, + flag: Boolean = true + )(pred: Tree => Boolean): Option[Tree] = + findTreeOrParent(tree)(x => if (pred(x) == flag) Some(true) else None) + /** * Returns first ancestor whose parent matches the given predicate. */ @@ -246,9 +268,10 @@ object TreeOps { } } def findTreeWithParentSimple( - tree: Tree + tree: Tree, + flag: Boolean = true )(pred: Tree => Boolean): Option[Tree] = - findTreeWithParent(tree)(x => if (pred(x)) Some(true) else None) + findTreeWithParent(tree)(x => if (pred(x) == flag) Some(true) else None) /** * Returns first ancestor with a parent of a given type. diff --git a/scalafmt-tests/src/test/resources/test/JavaDoc.stat b/scalafmt-tests/src/test/resources/test/JavaDoc.stat index 1bd3260036..e4e1543656 100644 --- a/scalafmt-tests/src/test/resources/test/JavaDoc.stat +++ b/scalafmt-tests/src/test/resources/test/JavaDoc.stat @@ -1259,3 +1259,19 @@ object a { /** alias for [[literal]] */ /** alias for [[literal]] */ } +<<< #2043 +object Day extends Enumeration { + type Day = Value + + val + /** Monday */ + MON = Value +} +>>> +object Day extends Enumeration { + type Day = Value + + val + /** Monday */ + MON = Value +}