Skip to content

Commit

Permalink
OrganizeImports: prepare support for specific syntax as output
Browse files Browse the repository at this point in the history
  • Loading branch information
bjaglin committed Jan 21, 2024
1 parent 11a854e commit 1dca258
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 5 deletions.
71 changes: 71 additions & 0 deletions docs/rules/OrganizeImports.md
Original file line number Diff line number Diff line change
Expand Up @@ -1328,3 +1328,74 @@ object RemoveUnused {
val long: JLong = JLong.parseLong("0")
}
```

`targetDialect`
--------------

Specifies the [wildcards and renames](https://docs.scala-lang.org/scala3/reference/changed-features/imports.html)
syntax used when imports are coalesced or rewritten.

### Value type

Enum: `Auto | Scala2 | Scala3`

#### `Auto`

Infer the dialect from compilation settings (Scala version or `-Xsource` when
provided). This is safe only for sources that are not cross-compiled (see
rationale below).

#### `Scala2`

Use `_` as wildcard and `=>` for renames.

#### `Scala3`

Use `*` as wildcard and `as` for renames.

### Default value

`Scala2`

Rationale: `Auto` is not a safe default for projects cross-compiling with
Scala 3.x and Scala 2.x without `-Xsource:3`, as the Scala 3.x Scalafix run
would introduce Scala 2.x compilation errors.

### Examples

#### `Scala2`

```conf
OrganizeImports {
targetDialect = Scala2
}
```

Before:

```scala
```

After:

```scala
```

#### `Scala3`

```conf
OrganizeImports {
targetDialect = Scala3
}
```

Before:

```scala
```

After:

```scala
```

Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import scala.meta.Tree
import scala.meta.XtensionClassifiable
import scala.meta.XtensionCollectionLikeUI
import scala.meta.XtensionSyntax
import scala.meta.dialects
import scala.meta.inputs.Position
import scala.meta.tokens.Token

Expand All @@ -29,6 +30,7 @@ import metaconfig.ConfEncoder
import metaconfig.ConfOps
import metaconfig.Configured
import metaconfig.internal.ConfGet
import scalafix.internal.config.ScalaVersion
import scalafix.internal.rule.ImportMatcher.*
import scalafix.internal.rule.ImportMatcher.---
import scalafix.internal.rule.ImportMatcher.parse
Expand Down Expand Up @@ -801,9 +803,37 @@ object OrganizeImports {
}
}

val targetDialect = conf.targetDialect match {
case TargetDialect.Auto =>
ScalaVersion
.from(scalaVersion)
.map { scalaVersion =>
def extractSuffixForScalacOption(prefix: String) = {
scalacOptions
.filter(_.startsWith(prefix))
.lastOption
.map(_.stripPrefix(prefix))
}

// We only lookup the Scala 2 option (Scala 3 is `-source`), as the latest Scala 3
// dialect is used no matter what the actual minor version is anyway, and as of now,
// the pretty printer is just more permissive with the latest dialect.
val sourceScalaVersion =
extractSuffixForScalacOption("-Xsource:")
.flatMap(ScalaVersion.from(_).toOption)

scalaVersion.dialect(sourceScalaVersion)
}
.getOrElse(Dialect.current)
case TargetDialect.Scala2 =>
dialects.Scala212
case TargetDialect.Scala3 =>
dialects.Scala3
}

if (!conf.removeUnused || hasWarnUnused)
Configured.ok(
new OrganizeImports(conf, Dialect.current)
new OrganizeImports(conf, targetDialect)
)
else if (hasCompilerSupport)
Configured.error(
Expand Down Expand Up @@ -952,11 +982,11 @@ object OrganizeImports {
* Categorizes a list of `Importee`s into the following four groups:
*
* - Names, e.g., `Seq`, `Option`, etc.
* - Renames, e.g., `{Long => JLong}`, `{Duration => D}`, etc.
* - Unimports, e.g., `{Foo => _}`.
* - Renames, e.g., `{Long => JLong}`, `Duration as D`, etc.
* - Unimports, e.g., `{Foo => _}` or `Foo as _`.
* - Givens, e.g., `{given Foo}`.
* - GivenAll, i.e., `given`.
* - Wildcard, i.e., `_`.
* - Wildcard, i.e., `_` or `*`.
*/
object Importees {
def unapply(importees: Seq[Importee]): Option[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,18 @@ object Preset {
ConfEncoder.instance(v => Conf.Str(v.toString))
}

sealed trait TargetDialect
object TargetDialect {
case object Auto extends TargetDialect
case object Scala2 extends TargetDialect
case object Scala3 extends TargetDialect

implicit def reader: ConfDecoder[TargetDialect] =
ReaderUtil.oneOf(Auto, Scala2, Scala3)
implicit def writer: ConfEncoder[TargetDialect] =
ConfEncoder.instance(v => Conf.Str(v.toString))
}

final case class OrganizeImportsConfig(
blankLines: BlankLines = BlankLines.Auto,
coalesceToWildcardImportThreshold: Option[Int] = None,
Expand All @@ -88,7 +100,8 @@ final case class OrganizeImportsConfig(
importSelectorsOrder: ImportSelectorsOrder = ImportSelectorsOrder.Ascii,
importsOrder: ImportsOrder = ImportsOrder.Ascii,
preset: Preset = Preset.DEFAULT,
removeUnused: Boolean = true
removeUnused: Boolean = true,
targetDialect: TargetDialect = TargetDialect.Scala2
)

object OrganizeImportsConfig {
Expand Down

0 comments on commit 1dca258

Please sign in to comment.