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

Imports: implement contiguousGroups logic #2719

Merged
merged 2 commits into from
Sep 3, 2021
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
24 changes: 15 additions & 9 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -2650,15 +2650,6 @@ import foo._

#### Imports: `groups`

This rule will separate all import statements into groups. If sorting is
enabled (i.e., not `none`), imports will also be sorted within each group.

The `groups` parameter defines several sets of regular expressions; each
set defines a single group, and the groups are output in the order they
are configured (imports not matching any of the regexes will form their own
group at the end). Regular expressions are applied to the entire parent domain
of the import statement, up to and including the final dot.

> Keep in mind that this functionality should be used very carefully if
> hierarchical (relative) imports are allowed in your codebase. Groups
> should only refer to typical top-level domains such as `java`, `org`,
Expand All @@ -2668,6 +2659,21 @@ of the import statement, up to and including the final dot.
> rule like `OrganizeImports`. However, on a large codebase, the overhead
> of using semantic `scalafix` rules might be substantial.

This rule will separate all import statements into groups. If sorting is
enabled (i.e., not `none`), imports will also be sorted within each group.

The rule accepts the following parameters:

- `rewrite.imports.groups`: defines several sets of regular expressions; each
set defines a single group, and the groups are output in the order they
are configured (imports not matching any of the regexes will form their own
group at the end). Regular expressions are applied to the entire parent domain
of the import statement, up to and including the final dot.
- `rewrite.imports.contiguousGroups` (since v3.0.2):
- if `only` (default), only consecutive import statements will be grouped
- if `no`, grouping will happen on all imports within the same container
(source, package, template etc.)

```scala mdoc:scalafmt
rewrite.rules = [Imports]
rewrite.imports.sort = ascii
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ object Imports extends RewriteFactory {
case class Settings(
sort: Sort = Sort.none,
expand: Boolean = false,
contiguousGroups: ContiguousGroups = ContiguousGroups.only,
groups: Seq[Seq[String]] = Nil
) {
private lazy val regex = groups.map(_.map(Pattern.compile))
Expand All @@ -40,6 +41,15 @@ object Imports extends RewriteFactory {
generic.deriveCodecEx(new Settings).noTypos
}

sealed abstract class ContiguousGroups
object ContiguousGroups {
case object no extends ContiguousGroups
case object only extends ContiguousGroups

implicit val codec: ConfCodecEx[ContiguousGroups] =
ReaderUtil.oneOf(only, no)
}

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

Expand Down Expand Up @@ -475,6 +485,8 @@ object Imports extends RewriteFactory {
): Unit =
if (settings.groups.isEmpty && settings.sort.eq(Sort.none))
processEachLine(stats)
else if (settings.contiguousGroups eq ContiguousGroups.only)
processEachGroup(stats)
else
processAllGroups(stats)

Expand All @@ -494,6 +506,13 @@ object Imports extends RewriteFactory {
processTokenRanges(importString, getTokenRange(group))
}

private def processEachGroup(stats: Seq[Seq[ImportExportStat]]): Unit =
stats.foreach { group =>
val tokenRange = getTokenRange(group)
val importString = processImports(group)
processTokenRanges(importString, tokenRange)
}

private def processAllGroups(stats: Seq[Seq[ImportExportStat]]): Unit = {
val tokenRanges = stats.map(getTokenRange)
val importString = processImports(stats.flatten)
Expand Down
51 changes: 51 additions & 0 deletions scalafmt-tests/src/test/resources/rewrite/Imports.source
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
rewrite.rules = [Imports]
rewrite.imports.contiguousGroups = no
runner.dialect = scala3
<<< basic with wildcard
rewrite.imports.expand = true
Expand Down Expand Up @@ -669,6 +670,56 @@ class A {
class B {
import foo._
}
<<< #2718 contiguousGroups = only
rewrite.imports.groups = [["""scala\..*"""]]
rewrite.imports.contiguousGroups = only
===
object Main {
// c1
import Foo._ // c2
// c3
val bar: Bar = getBar
// c4
import bar.ec // c5

def getBar: Foo.Bar = ???
}
>>>
object Main {
// c1
import Foo._ // c2
// c3
val bar: Bar = getBar
// c4
import bar.ec // c5

def getBar: Foo.Bar = ???
}
<<< #2718 contiguousGroups = no
rewrite.imports.groups = [["""scala\..*"""]]
rewrite.imports.contiguousGroups = no
===
object Main {
// c1
import Foo._ // c2
// c3
val bar: Bar = getBar
// c4
import bar.ec // c5

def getBar: Foo.Bar = ???
}
>>>
object Main {
// c1
import Foo._ // c2
// c4
import bar.ec // c5
// c3
val bar: Bar = getBar

def getBar: Foo.Bar = ???
}
<<< #2720 expand
rewrite.imports.expand = true
===
Expand Down