Skip to content

Commit

Permalink
OrganizeImports: support Scala3 syntax as output
Browse files Browse the repository at this point in the history
  • Loading branch information
bjaglin committed Jan 28, 2024
1 parent 1dca258 commit 6415ed1
Show file tree
Hide file tree
Showing 75 changed files with 373 additions and 45 deletions.
22 changes: 18 additions & 4 deletions docs/rules/OrganizeImports.md
Original file line number Diff line number Diff line change
Expand Up @@ -1342,16 +1342,18 @@ 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).
provided). This is safe only for sources that are not cross-compiled and
therefore it is NOT the default value (see rationale below).

#### `Scala2`

Use `_` as wildcard and `=>` for renames.
Use `_` as wildcard and `=>` for renames. Curly braces are striped for importers
with a single regular or wildcard importee.

#### `Scala3`

Use `*` as wildcard and `as` for renames.
Use `*` as wildcard and `as` for renames. Curly braces are striped for importers
with a single importee.

### Default value

Expand All @@ -1374,11 +1376,16 @@ OrganizeImports {
Before:

```scala
import scala.collection.immutable.{List => L}
import scala.collection.mutable.{Map, Buffer => _, Seq => S, _}
```

After:

```scala
import scala.collection.immutable.{List => L}
import scala.collection.mutable.Map
import scala.collection.mutable.{Buffer => _, Seq => S, _}
```

#### `Scala3`
Expand All @@ -1392,10 +1399,17 @@ OrganizeImports {
Before:

```scala
import scala.collection.immutable.{List => L}
import scala.collection.mutable.{Map, Buffer => _, Seq => S, _}
import scala.concurrent.ExecutionContext.Implicits.{given scala.concurrent.ExecutionContext}
```

After:

```scala
import scala.collection.immutable.List as L
import scala.collection.mutable.Map
import scala.collection.mutable.{Buffer as _, Seq as S, *}
import scala.concurrent.ExecutionContext.Implicits.given scala.concurrent.ExecutionContext
```

Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,12 @@ class OrganizeImports(

importer match {
case Importer(_, Importee.Wildcard() :: Nil) =>
syntax.patch(syntax.lastIndexOfSlice("._"), ".\u0001", 2)
val wildcardSyntax = Importee.Wildcard().syntax
syntax.patch(
syntax.lastIndexOfSlice(s".$wildcardSyntax"),
".\u0001",
2
)

case _ if importer.isCurlyBraced =>
syntax
Expand Down Expand Up @@ -714,9 +719,66 @@ class OrganizeImports(
private def importerSyntax(importer: Importer): String =
importer.pos match {
case pos: Position.Range =>
// Position found, implies that `importer` was directly parsed from the source code. Returns
// the original parsed text to preserve the original source level formatting.
pos.text
// Position found, implies that `importer` was directly parsed from the source code. Rewrite
// importees to ensure they follow the target dialect. For importers with a single importee,
// strip enclosing braces if they exist (or add/preserve them for Rename & Unimport on Scala 2).
val syntax = new StringBuilder(pos.text)
def patchSyntax(
t: Tree,
newSyntax: String,
// None: don't touch
// Some(true): add if not there, preserve if there
// Some(false): strip if there, preserve if not there
maybeForcePresenceOrAbsenceOfBraces: Option[Boolean] = None
) = {
val start = t.pos.start - pos.start
syntax.replace(start, t.pos.end - pos.start, newSyntax)

maybeForcePresenceOrAbsenceOfBraces.foreach {
forcePresenceOrAbsenceOfBraces =>
val end = t.pos.start - pos.start + newSyntax.length
(
syntax.take(start).lastIndexOf('{'),
syntax.indexOf('}', end),
forcePresenceOrAbsenceOfBraces
) match {
case (-1, -1, true) =>
// braces requested but not detected
syntax.append('}')
syntax.insert(start, '{')
case (opening, closing, false)
if opening != -1 && closing != -1 =>
// braces detected but not requested
println(syntax.toString() + " " + end + " " + closing)
syntax.delete(end, closing + 1)
syntax.delete(opening, start)
case _ =>
}
}
}

val Importer(_, importees) = importer

val singleImporteeFalse =
Option
.when(importees.length == 1)(false)
val singleImporteeDialect =
Option
.when(importees.length == 1)(!targetDialect.allowAsForImportRename)

// traverse & patch backwards to avoid shifting indices
importees.reverse.foreach {
case i @ Importee.Rename(_, _) =>
patchSyntax(i, i.copy().syntax, singleImporteeDialect)
case i @ Importee.Unimport(_) =>
patchSyntax(i, i.copy().syntax, singleImporteeDialect)
case i @ Importee.Wildcard() =>
patchSyntax(i, i.copy().syntax, singleImporteeFalse)
case i =>
patchSyntax(i, i.syntax, singleImporteeFalse)
}

syntax.toString

case Position.None =>
// Position not found, implies that `importer` is derived from certain existing import
Expand All @@ -738,11 +800,15 @@ class OrganizeImports(

implicit private class ImporterExtension(importer: Importer) {

/** Checks whether the `Importer` is curly-braced when pretty-printed. */
/**
* Checks whether the `Importer` should be curly-braced when pretty-printed.
*/
def isCurlyBraced: Boolean = {
val importees @ Importees(_, renames, unimports, _, _, _) =
importer.importees
renames.nonEmpty || unimports.nonEmpty || importees.length > 1

importees.length > 1 ||
((renames.length == 1 || unimports.length == 1) && !targetDialect.allowAsForImportRename)
}

/**
Expand Down Expand Up @@ -984,7 +1050,7 @@ object OrganizeImports {
* - Names, e.g., `Seq`, `Option`, etc.
* - Renames, e.g., `{Long => JLong}`, `Duration as D`, etc.
* - Unimports, e.g., `{Foo => _}` or `Foo as _`.
* - Givens, e.g., `{given Foo}`.
* - Givens, e.g., `given Foo`.
* - GivenAll, i.e., `given`.
* - Wildcard, i.e., `_` or `*`.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
rules = [OrganizeImports]
OrganizeImports {
removeUnused = false
targetDialect = Auto
}
*/
package test.organizeImports

import scala.collection.immutable.{List => L}
import scala.collection.mutable._

object TargetDialectAuto
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
rules = [OrganizeImports]
OrganizeImports.removeUnused = false
OrganizeImports.targetDialect = Auto
*/
package test.organizeImports

import java.util.concurrent.{ BlockingDeque }
import java.util.concurrent.{ConcurrentHashMap}
import scala.collection.mutable.{_}

object TargetDialectSingleImporteeStripCurlybraces
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ OrganizeImports {
groupedImports = Keep
coalesceToWildcardImportThreshold = 2
removeUnused = false
targetDialect = Scala3
}
*/
package test.organizeImports
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ OrganizeImports {
groupedImports = Keep
coalesceToWildcardImportThreshold = 2
removeUnused = false
targetDialect = Scala3
}
*/
package test.organizeImports
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ OrganizeImports {
groupedImports = Keep
coalesceToWildcardImportThreshold = 2
removeUnused = false
targetDialect = Scala3
}
*/
package test.organizeImports
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ OrganizeImports {
groupedImports = Keep
coalesceToWildcardImportThreshold = 2
removeUnused = false
targetDialect = Scala3
}
*/
package test.organizeImports
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
rules = [OrganizeImports]
OrganizeImports.removeUnused = false
OrganizeImports.targetDialect = Scala3
*/
package test.organizeImports

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
/*
rules = [OrganizeImports]
OrganizeImports.removeUnused = false
OrganizeImports.targetDialect = Scala3
*/
package test.organizeImports

import test.organizeImports.GivenImports.Beta
import test.organizeImports.GivenImports.Alpha
import test.organizeImports.GivenImports.{given Beta, given Alpha}
import test.organizeImports.GivenImports.{given Beta, given test.organizeImports.GivenImports.Gamma, given Alpha}
import scala.util.Either

object ExpandGiven
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
rules = [OrganizeImports]
OrganizeImports.removeUnused = false
OrganizeImports.targetDialect = Scala3
*/
package test.organizeImports

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@
rules = [OrganizeImports]
OrganizeImports.removeUnused = false
OrganizeImports.groupedImports = Merge
OrganizeImports.targetDialect = Scala3
*/
package test.organizeImports

import test.organizeImports.GivenImports._
import test.organizeImports.GivenImports.{alpha => _, given}
import test.organizeImports.GivenImports.{given Beta}
import test.organizeImports.GivenImports.{gamma => _, given}
import test.organizeImports.GivenImports.{given Zeta}
import test.organizeImports.GivenImports.*
import test.organizeImports.GivenImports.{alpha as _, given}
import test.organizeImports.GivenImports.given Beta
import test.organizeImports.GivenImports.{gamma as _, given}
import test.organizeImports.GivenImports.given Zeta

import test.organizeImports.GivenImports2.{alpha => _}
import test.organizeImports.GivenImports2.{beta => _}
import test.organizeImports.GivenImports2.{given Gamma}
import test.organizeImports.GivenImports2.{given Zeta}
import test.organizeImports.GivenImports2.alpha as _
import test.organizeImports.GivenImports2.beta as _
import test.organizeImports.GivenImports2.given Gamma
import test.organizeImports.GivenImports2.given Zeta

object GroupedGivenImportsMergeUnimports
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
rules = [OrganizeImports]
OrganizeImports.removeUnused = false
OrganizeImports.groupedImports = AggressiveMerge
OrganizeImports.targetDialect = Scala3
*/
package test.organizeImports

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
rules = [OrganizeImports]
OrganizeImports.removeUnused = false
OrganizeImports.groupedImports = Merge
OrganizeImports.targetDialect = Scala3
*/
package test.organizeImports

import test.organizeImports.GivenImports.Beta
import test.organizeImports.GivenImports.Alpha
import test.organizeImports.GivenImports.{given Beta}
import test.organizeImports.GivenImports.{given Alpha}
import test.organizeImports.GivenImports.given Beta
import test.organizeImports.GivenImports.given test.organizeImports.GivenImports.Gamma
import test.organizeImports.GivenImports.given Alpha
import scala.util.Either

object MergeGiven
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
rules = [OrganizeImports]
OrganizeImports.removeUnused = false
OrganizeImports.groupedImports = Merge
OrganizeImports.targetDialect = Scala3
*/

package test.organizeImports

import test.organizeImports.GivenImports.*
import test.organizeImports.GivenImports.{ given Alpha, given Beta }

object MergeImportsFormatPreservingGiven
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
rules = [OrganizeImports]
OrganizeImports.removeUnused = false
OrganizeImports.targetDialect = Scala2
*/
package test.organizeImports

import scala.util.Either as E
import scala.util.Try as _

object TargetDialectDowngrade
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
rules = [OrganizeImports]
OrganizeImports.removeUnused = false
OrganizeImports.targetDialect = Auto
*/
package test.organizeImports

import java.util.concurrent.{ BlockingDeque }
import java.util.concurrent.{ConcurrentHashMap}
import scala.collection.mutable.{*}
import scala.collection.mutable.{ListMap as _}
import scala.util.{ Try as T }

import GivenImports.{given GivenImports.Alpha}
import GivenImports.{given}

object TargetDialectSingleImporteeStripCurlybraces
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
rules = [OrganizeImports]
OrganizeImports.removeUnused = false
OrganizeImports.groupedImports = Merge
OrganizeImports.targetDialect = Auto
*/
package test.organizeImports

import scala.collection.mutable.{
ArrayBuffer,
Map,
Queue,
Set
Set => S
}

object AlreadyOrganized
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ OrganizeImports {
groupedImports = Keep
coalesceToWildcardImportThreshold = 3
removeUnused = false
targetDialect = Auto
}
*/
package test.organizeImports
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
/*
rules = [OrganizeImports]
OrganizeImports.removeUnused = false
OrganizeImports.targetDialect = Auto
*/
package test.organizeImports

import scala.collection.{Map}
import scala.collection.{Seq => _}
import scala.collection.{Set => ImmutableSet}

object CurlyBracedSingleImportee
Loading

0 comments on commit 6415ed1

Please sign in to comment.