Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AvoidInfixSettings: consolidate AvoidInfix params #3717

Merged
merged 1 commit into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -2852,22 +2852,27 @@ This rule replaces infix expressions `a op b` with proper method calls `a.op(b)`
> NB: The rule currently does not support right-associative operators (i.e.,
> those which end in `:`) which would have had to be rewritten as `b.op(a)`.
The rule takes the following parameters:
The rule takes the following parameters under `rewrite.avoidInfix`:

- `rewrite.neverInfix` parameter group which consists of `includeFilters` and `excludeFilters`,
two lists of regular expressions, which determine which operators are eligible for this rewrite
- (since 3.7.18) if a regular expression contains `\\.`, matching will be against
- `includeFilters` and `excludeFilters`, two lists of regular expressions, which
determine which operators are eligible for this rewrite
- for this rule to be enabled (that is, for the rewrite to be applied), an infix
expression must match `includeFilters` and not match `excludeFilters`
- (since 3.8.0) if a regular expression contains `\\.`, matching will be against
not only the infix operator but also its left-hand-side expression (with the
non-empty operator part following the last `\\.` in the pattern)
- (since 3.4.4) `rewrite.allowInfixPlaceholderArg` (default: `true`) will not rewrite infix
- (before 3.8.0) these two parameters were nested under `rewrite.neverInfix`
- `excludePlaceholderArg` (default: `true`) will not rewrite infix
expressions if the argument is a solo placeholder (`_` or `(_: Type)`)
- this parameter does not control any other cases with the infix argument containing a
placeholder character; some of them will never be rewritten as adding parentheses will
change their syntactic meaning, and others will be rewritten as usual
- (before 3.8.0 and since 3.4.4) this parameter was named
`rewrite.allowInfixPlaceholderArg`

```scala mdoc:scalafmt
rewrite.rules = [AvoidInfix]
rewrite.neverInfix.excludeFilters."+" = [ "map" ]
rewrite.avoidInfix.excludeFilters."+" = [ "map" ]
---
a success b
a error (b, c)
Expand All @@ -2885,7 +2890,7 @@ future recover {

```scala mdoc:scalafmt
rewrite.rules = [AvoidInfix]
rewrite.allowInfixPlaceholderArg = false
rewrite.avoidInfix.excludePlaceholderArg = false
---
_ foo _
_ bar (_: Int)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,22 @@ import metaconfig.generic.Surface

import java.util.regex.{Matcher, Pattern}

case class NeverInfixPattern(
private val includeFilters: Seq[NeverInfixPattern.Filter], // partial match
private val excludeFilters: Seq[NeverInfixPattern.Filter] // strict match
case class AvoidInfixSettings(
private val includeFilters: Seq[AvoidInfixSettings.Filter], // partial match
private val excludeFilters: Seq[AvoidInfixSettings.Filter], // strict match
excludePlaceholderArg: Option[Boolean] = None
) {
private[config] def forSbt: Option[NeverInfixPattern] =
private[config] def forSbt: Option[AvoidInfixSettings] =
// if the user customized these, we don't touch
if (excludeFilters ne NeverInfixPattern.default.excludeFilters) None
else Some(copy(excludeFilters = NeverInfixPattern.sbtExclude))
if (excludeFilters ne AvoidInfixSettings.default.excludeFilters) None
else Some(copy(excludeFilters = AvoidInfixSettings.sbtExclude))

def matches(lhs: String, op: String): Boolean =
includeFilters.forall(_.matches(lhs, op)(_.find())) &&
!excludeFilters.exists(_.matches(lhs, op)(_.matches()))
}

object NeverInfixPattern {
object AvoidInfixSettings {

private[config] case class Filter(
lhs: Option[Pattern],
Expand Down Expand Up @@ -55,11 +56,11 @@ object NeverInfixPattern {
def apply(value: String): Filter = parse(value).get
}

implicit lazy val surface: Surface[NeverInfixPattern] =
implicit lazy val surface: Surface[AvoidInfixSettings] =
generic.deriveSurface
implicit lazy val codec: ConfCodecEx[NeverInfixPattern] =
implicit lazy val codec: ConfCodecEx[AvoidInfixSettings] =
generic.deriveCodecEx(default).noTypos
val default = NeverInfixPattern(
val default = AvoidInfixSettings(
Seq("[\\w\\d_]+").map(Filter.apply),
Seq(
"until",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,18 @@ case class RewriteSettings(
imports: Imports.Settings = Imports.Settings(),
preferCurlyFors: PreferCurlyFors.Settings = PreferCurlyFors.Settings(),
trailingCommas: TrailingCommas = TrailingCommas(),
allowInfixPlaceholderArg: Boolean = true,
neverInfix: NeverInfixPattern = NeverInfixPattern.default
@annotation.DeprecatedName(
"allowInfixPlaceholderArg",
"Use `avoidInfix.excludePlaceholderArg` instead",
"3.8.0"
)
private val allowInfixPlaceholderArg: Boolean = true,
@annotation.ExtraName("neverInfix")
avoidInfix: AvoidInfixSettings = AvoidInfixSettings.default
) {
def isAllowInfixPlaceholderArg: Boolean =
avoidInfix.excludePlaceholderArg.getOrElse(allowInfixPlaceholderArg)

def withoutRewrites: RewriteSettings =
copy(rules = Nil, trailingCommas = trailingCommas.withoutRewrites)

Expand All @@ -33,7 +42,7 @@ case class RewriteSettings(
}

private[config] def forSbt: RewriteSettings =
neverInfix.forSbt.fold(this)(x => copy(neverInfix = x))
avoidInfix.forSbt.fold(this)(x => copy(avoidInfix = x))
}

object RewriteSettings {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import org.scalafmt.util.InfixApp
object AvoidInfix extends RewriteFactory {

override def hasChanged(v1: RewriteSettings, v2: RewriteSettings): Boolean =
v2.neverInfix ne v1.neverInfix
v2.avoidInfix ne v1.avoidInfix

override def create(implicit ctx: RewriteCtx): RewriteSession =
new AvoidInfix
Expand All @@ -20,7 +20,7 @@ object AvoidInfix extends RewriteFactory {

class AvoidInfix(implicit ctx: RewriteCtx) extends RewriteSession {

private val matcher = ctx.style.rewrite.neverInfix
private val matcher = ctx.style.rewrite.avoidInfix

// In a perfect world, we could just use
// Tree.transform {
Expand Down Expand Up @@ -88,7 +88,7 @@ class AvoidInfix(implicit ctx: RewriteCtx) extends RewriteSession {
InfixApp.isLeftAssoc(op) && matcher.matches(lhs, op) &&
(ai.argClause match {
case ac @ Term.ArgClause(arg :: Nil, _) if !isWrapped(ac) =>
!hasPlaceholder(arg, ctx.style.rewrite.allowInfixPlaceholderArg)
!hasPlaceholder(arg, ctx.style.rewrite.isAllowInfixPlaceholderArg)
case _ => true
}) && (ai.lhs match {
case lhs: Term.ApplyInfix if hasPlaceholder(lhs, true) =>
Expand Down
8 changes: 4 additions & 4 deletions scalafmt-tests/src/test/resources/rewrite/AvoidInfix.stat
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,8 @@ object a {
_.foo(bar).baz(qux)
}
<<< in lambda 2, !allowInfixPlaceholderArg
rewrite.allowInfixPlaceholderArg = false
rewrite.neverInfix {
rewrite.avoidInfix {
excludePlaceholderArg = false
includeFilters = [ "[^*]+" ]
excludeFilters = []
}
Expand Down Expand Up @@ -219,8 +219,8 @@ object a {
b.c(1./(2).+(3))
}
<<< in lambda 2, allowInfixPlaceholderArg
rewrite.allowInfixPlaceholderArg = true
rewrite.neverInfix {
rewrite.avoidInfix {
excludePlaceholderArg = true
includeFilters = [ "[^*]+" ]
excludeFilters = []
}
Expand Down
Loading