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

NeverInfixPattern: implement own matching method #3704

Merged
merged 1 commit into from
Nov 24, 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
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,60 @@ package org.scalafmt.config
import metaconfig._
import metaconfig.generic.Surface

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

case class NeverInfixPattern(
includeFilters: Seq[String],
excludeFilters: Seq[String]
private val includeFilters: Seq[NeverInfixPattern.Filter], // partial match
private val excludeFilters: Seq[NeverInfixPattern.Filter] // strict match
) {
lazy val matcher: FilterMatcher = new FilterMatcher(
FilterMatcher.mkRegexp(includeFilters),
FilterMatcher.mkRegexp(excludeFilters, true)
)

private[config] def forSbt: Option[NeverInfixPattern] =
// if the user customized these, we don't touch
if (excludeFilters ne NeverInfixPattern.default.excludeFilters) None
else Some(copy(excludeFilters = NeverInfixPattern.sbtExclude))

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

object NeverInfixPattern {

private[config] case class Filter(
op: Pattern
) {
private def pattern: String = {
op.pattern()
}
def matches(op: String)(
f: Matcher => Boolean
): Boolean =
f(this.op.matcher(op))
}

private object Filter {
implicit lazy val surface: Surface[Filter] = generic.deriveSurface
implicit lazy val encoder: ConfEncoder[Filter] =
ConfEncoder.instance(x => Conf.Str(x.pattern))
implicit lazy val decoder: ConfDecoderEx[Filter] =
ConfDecoderEx.fromPartial("String") { case (_, Conf.Str(x)) => parse(x) }

private def parse(str: String): Configured[Filter] = {
val op = str
if (op.isEmpty) Configured.error(s"empty infix op [$str]")
else {
Configured.Ok(Filter(op.r.pattern))
}
}

def apply(value: String): Filter = parse(value).get
}

implicit lazy val surface: Surface[NeverInfixPattern] =
generic.deriveSurface
implicit lazy val codec: ConfCodecEx[NeverInfixPattern] =
generic.deriveCodecEx(default).noTypos
val default = NeverInfixPattern(
Seq("[\\w\\d_]+"),
Seq("[\\w\\d_]+").map(Filter.apply),
Seq(
"until",
"to",
Expand Down Expand Up @@ -52,10 +84,10 @@ object NeverInfixPattern {
"allElementsOf",
"inOrderElementsOf",
"theSameElementsAs"
)
).map(Filter.apply)
)

private val sbtExclude = Seq(
"cross"
) ++ default.excludeFilters
).map(Filter.apply) ++ default.excludeFilters
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import scala.annotation.tailrec
import scala.meta._
import scala.meta.internal.trees.PlaceholderChecks.hasPlaceholder

import org.scalafmt.config.FilterMatcher
import org.scalafmt.config.RewriteSettings
import org.scalafmt.util.InfixApp

Expand All @@ -16,18 +15,11 @@ object AvoidInfix extends RewriteFactory {
override def create(implicit ctx: RewriteCtx): RewriteSession =
new AvoidInfix

def getMatcher(ctx: RewriteCtx): FilterMatcher =
ctx.style.rewrite.neverInfix.matcher

def getMatcherIfEnabled(ctx: RewriteCtx): Option[FilterMatcher] =
if (ctx.style.rewrite.rules.contains(AvoidInfix)) Some(getMatcher(ctx))
else None

}

class AvoidInfix(implicit ctx: RewriteCtx) extends RewriteSession {

private val matcher = AvoidInfix.getMatcher(ctx)
private val matcher = ctx.style.rewrite.neverInfix

// In a perfect world, we could just use
// Tree.transform {
Expand Down Expand Up @@ -88,7 +80,8 @@ class AvoidInfix(implicit ctx: RewriteCtx) extends RewriteSession {
@tailrec
private def checkMatchingInfix(ai: Term.ApplyInfix): Boolean = {
val op = ai.op.value
InfixApp.isLeftAssoc(op) && matcher.matches(op) && (ai.argClause match {
InfixApp.isLeftAssoc(op) && matcher.matches(op) &&
(ai.argClause match {
case ac @ Term.ArgClause(arg :: Nil, _) if !isWrapped(ac) =>
!hasPlaceholder(arg, ctx.style.rewrite.allowInfixPlaceholderArg)
case _ => true
Expand Down
Loading