Skip to content

Commit

Permalink
Introduce the importsOrder configuration (scalacenter#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
liancheng authored Apr 30, 2020
1 parent 53be009 commit 2d8d5d1
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 8 deletions.
93 changes: 92 additions & 1 deletion README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ import scala.collection.mutable.{ArrayBuffer, Buffer, StringBuilder}
----
--

[[groups]]
=== `groups`

==== Description
Expand Down Expand Up @@ -401,7 +402,7 @@ import sun.misc.BASE64Encoder

==== Description

Sort import selectors within a single import expression by the specified order.
Specifies the order of grouped import selectors within a single import expression.

==== Value type

Expand Down Expand Up @@ -481,6 +482,96 @@ import foo.{~>, `symbol`, bar, Random}
----
--

=== `importsOrder`

==== Description

Specifies the order of import statements within import groups defined by the <<groups,`OrganizeImports.groups`>> option.

==== Value type

Enum: `Ascii | SymbolsFirst`

`Ascii`::
Sort import statements by ASCII codes.

`SymbolsFirst`::
Put wildcard imports and grouped imports with braces first, otherwise same as `Ascii`. This is also the sorting order the IntelliJ IDEA Scala plugin picks.

==== Deafult value

`Ascii`

==== Example

`Ascii`::
+
--
Configuration:

[source,hocon]
----
OrganizeImports {
groupedImports = Keep
importsOrder = Ascii
}
----

Before:

[source,scala]
----
import scala.concurrent._
import scala.concurrent.{Future, Promise}
import scala.concurrent.ExecutionContext.Implicits._
import scala.concurrent.duration
----

After:

[source,scala]
----
import scala.concurrent.ExecutionContext.Implicits._
import scala.concurrent._
import scala.concurrent.duration
import scala.concurrent.{Promise, Future}
----
--

`SymbolsFirst`::
+
--
Configuration:

[source,hocon]
----
OrganizeImports {
groupedImports = Keep
importsOrder = SymbolsFirst
}
----

Before:

[source,scala]
----
import scala.concurrent.ExecutionContext.Implicits._
import scala.concurrent._
import scala.concurrent.duration
import scala.concurrent.{Promise, Future}
----

After:

[source,scala]
----
import scala.concurrent._
import scala.concurrent.{Future, Promise}
import scala.concurrent.ExecutionContext.Implicits._
import scala.concurrent.duration
----
--

[[remove-unused]]
=== `removeUnused`

Expand Down
13 changes: 13 additions & 0 deletions input/src/main/scala/fix/ImportsOrderSymbolsFirst.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
rules = [OrganizeImports]
OrganizeImports.groupedImports = Keep
OrganizeImports.importsOrder = SymbolsFirst
*/
package fix

import scala.concurrent.ExecutionContext.Implicits._
import scala.concurrent._
import scala.concurrent.duration
import scala.concurrent.{Promise, Future}

object ImportsOrderSymbolsFirst
8 changes: 8 additions & 0 deletions output/src/main/scala/fix/ImportsOrderSymbolsFirst.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package fix

import scala.concurrent._
import scala.concurrent.{Future, Promise}
import scala.concurrent.ExecutionContext.Implicits._
import scala.concurrent.duration

object ImportsOrderSymbolsFirst
28 changes: 22 additions & 6 deletions rules/src/main/scala/fix/OrganizeImports.scala
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,30 @@ class OrganizeImports(config: OrganizeImportsConfig) extends SemanticRule("Organ

private def organizeImporters(importers: Seq[Importer]): Seq[Importer] = {
import GroupedImports._
import ImportsOrder._

val xs = config.groupedImports match {
case Merge => mergeImportersWithCommonPrefix(importers)
case Explode => explodeGroupedImportees(importers)
case Keep => importers
val importeesSorted = {
config.groupedImports match {
case Merge => mergeImportersWithCommonPrefix(importers)
case Explode => explodeGroupedImportees(importers)
case Keep => importers
}
} map sortImportees

config.importsOrder match {
case Ascii =>
importeesSorted sortBy (_.syntax)

case SymbolsFirst =>
// Hack: This is a quick-n-dirty way to achieve a the import ordering provided by the
// IntelliJ IDEA Scala plugin. This implementation does not cover cases like quoted
// identifiers containg "._" and/or braces.
importeesSorted sortBy {
_.syntax
.replaceAll("\\._$", ".\0")
.replaceAll("[{}]", "\1")
}
}

xs map sortImportees sortBy (_.syntax)
}

// Returns the index of the group to which the given importer belongs.
Expand Down
14 changes: 13 additions & 1 deletion rules/src/main/scala/fix/OrganizeImportsConfig.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ import metaconfig.generic.deriveDecoder
import metaconfig.generic.deriveSurface
import scalafix.internal.config.ReaderUtil

sealed trait ImportsOrder

object ImportsOrder {
case object Ascii extends ImportsOrder
case object SymbolsFirst extends ImportsOrder

implicit def reader: ConfDecoder[ImportsOrder] = ReaderUtil.fromMap {
List(Ascii, SymbolsFirst) groupBy (_.toString) mapValues (_.head)
}
}

sealed trait ImportSelectorsOrder

object ImportSelectorsOrder {
Expand All @@ -32,9 +43,10 @@ object GroupedImports {

final case class OrganizeImportsConfig(
expandRelative: Boolean = false,
importSelectorsOrder: ImportSelectorsOrder = ImportSelectorsOrder.Ascii,
groupedImports: GroupedImports = GroupedImports.Explode,
groups: Seq[String] = Seq("re:javax?\\.", "scala.", "*"),
importSelectorsOrder: ImportSelectorsOrder = ImportSelectorsOrder.Ascii,
importsOrder: ImportsOrder = ImportsOrder.Ascii,
removeUnused: Boolean = true
)

Expand Down

0 comments on commit 2d8d5d1

Please sign in to comment.