From 69b9515d0e3161ea7573e819de155710e7e3997f Mon Sep 17 00:00:00 2001 From: Albert Meltzer <7529386+kitbellew@users.noreply.github.com> Date: Wed, 26 Jan 2022 11:56:28 -0800 Subject: [PATCH] FormatOps: implement indentOperator.exemptScope --- .../org/scalafmt/config/IndentOperator.scala | 8 ++++++- .../org/scalafmt/internal/FormatOps.scala | 24 +++++++++++++++++-- .../test/resources/test/IndentOperator.stat | 12 +++++----- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/config/IndentOperator.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/config/IndentOperator.scala index f9cead6006..56bdfcbd02 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/config/IndentOperator.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/config/IndentOperator.scala @@ -53,7 +53,7 @@ case class IndentOperator( "Use indentOperator.exemptScope instead (true->topLevelOnly, false->all)", "3.4.0" ) - topLevelOnly: Boolean = true, + private val topLevelOnly: Boolean = true, private val exemptScope: Option[IndentOperator.Exempt] = None, @annotation.ExtraName("include") includeRegex: String = ".*", @@ -65,6 +65,12 @@ case class IndentOperator( def noindent(op: String): Boolean = excludeRegexp.matcher(op).find() || !includeRegexp.matcher(op).find() + + lazy val getExemptScope: IndentOperator.Exempt = + exemptScope.getOrElse( + if (topLevelOnly) IndentOperator.Exempt.topLevel + else IndentOperator.Exempt.all + ) } object IndentOperator { 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 5c3ad02c71..e88dfad769 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 @@ -3,6 +3,7 @@ package org.scalafmt.internal import org.scalafmt.Error.UnexpectedTree import org.scalafmt.config.{ BinPack, + IndentOperator, Newlines, ScalafmtConfig, ScalafmtRunner, @@ -583,8 +584,27 @@ class FormatOps( case t: Case => t.pat.eq(child) || t.body.eq(child) case _ => false } - val allowNoIndent = - !style.indentOperator.topLevelOnly || isTopLevel(getChild) + def isEnclosedAlone(child: Tree) = child.parent.exists { + case Case(`child`, _, _) => true + case Term.If(`child`, _, _) => true + case Term.While(`child`, _) => true + case Term.Block(List(`child`)) => true + case SplitCallIntoParts(_, Left(Seq(`child`))) => true + case fun: Term.FunctionTerm => isBlockFunction(fun) + case _ => + tokens.getHeadOpt(child).exists { headFt => + val last = tokens.getLast(child) + matchingOpt(headFt.left) + .contains(prevNonComment(last).left) || + matchingOpt(prevNonComment(prev(headFt)).left) + .contains(nextNonComment(last).right) + } + } + val allowNoIndent = style.indentOperator.getExemptScope match { + case IndentOperator.Exempt.all => true + case IndentOperator.Exempt.topLevel => isTopLevel(getChild) + case IndentOperator.Exempt.enclosedAlone => isEnclosedAlone(getChild) + } def isInfixTopLevelMatch(op: String, noindent: Boolean): Boolean = { noindent == style.indentOperator.noindent(op) && noindent == allowNoIndent diff --git a/scalafmt-tests/src/test/resources/test/IndentOperator.stat b/scalafmt-tests/src/test/resources/test/IndentOperator.stat index aac5b2bba6..c7b0c16338 100644 --- a/scalafmt-tests/src/test/resources/test/IndentOperator.stat +++ b/scalafmt-tests/src/test/resources/test/IndentOperator.stat @@ -2930,7 +2930,7 @@ object a { b } else a && - b + b } <<< ONLY enclosedAloneOnly, while indentOperator.exemptScope = enclosedAlone @@ -2964,7 +2964,7 @@ object a { b ) a && - b + b } <<< ONLY enclosedAloneOnly, apply indentOperator.exemptScope = enclosedAlone @@ -2996,7 +2996,7 @@ object a { object a { foo( a && - b + b ) foo { a && @@ -3010,10 +3010,10 @@ object a { ) foo( a && - b + b )( a && - b + b ) } <<< ONLY enclosedAloneOnly, case @@ -3032,7 +3032,7 @@ a match { a match { case foo => a && - b + b case bar => { a && b