Skip to content
This repository has been archived by the owner on Jul 14, 2023. It is now read-only.

Preserve the original source-level formatting when possible after exploding imports #280

Merged
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
12 changes: 12 additions & 0 deletions input/src/main/scala/fix/ExplodeImportsFormatPreserving.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
rules = [OrganizeImports]
OrganizeImports.removeUnused = false
OrganizeImports.groupedImports = Explode
*/

package fix

import fix.ExplodeImports.FormatPreserving.g1.{ a, b }
import fix.ExplodeImports.FormatPreserving.g2.{ c => C, _ }

object ExplodeImportsFormatPreserving
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package fix

import fix.ExplodeImports.FormatPreserving.g1.a
import fix.ExplodeImports.FormatPreserving.g1.b
import fix.ExplodeImports.FormatPreserving.g2.{ c => C, _ }

object ExplodeImportsFormatPreserving
38 changes: 24 additions & 14 deletions rules/src/main/scala/fix/OrganizeImports.scala
Original file line number Diff line number Diff line change
Expand Up @@ -499,17 +499,7 @@ class OrganizeImports(config: OrganizeImportsConfig) extends SemanticRule("Organ
}
}

// Issue #127: After merging imports within an importer group, checks whether there are any
// input importers left untouched. For those importers, returns the original importer
// instance to preserve the original source level formatting.
locally {
val importerSyntaxMap = group.map { i => i.copy().syntax -> i }.toMap

newImporteeListsWithGivens filter (_.nonEmpty) map { importees =>
val newImporter = Importer(ref, importees)
importerSyntaxMap.getOrElse(newImporter.syntax, newImporter)
}
}
preserveOriginalImportersFormatting(group, newImporteeListsWithGivens, ref)
}

private def sortImportersSymbolsFirst(importers: Seq[Importer]): Seq[Importer] =
Expand Down Expand Up @@ -786,8 +776,10 @@ object OrganizeImports {
// source level formatting.
importer :: Nil

case Importer(ref, Importees(names, renames, unimports, givens, givenAll, wildcard))
if givenAll.isDefined || wildcard.isDefined =>
case importer @ Importer(
ref,
Importees(names, renames, unimports, givens, givenAll, wildcard)
) if givenAll.isDefined || wildcard.isDefined =>
// When a wildcard exists, all renames, unimports, and the wildcard must appear in the same
// importer, e.g.:
//
Expand All @@ -799,12 +791,30 @@ object OrganizeImports {
// import p.E
val importeesList =
(names ++ givens).map(_ :: Nil) :+ (renames ++ unimports ++ wildcard ++ givenAll)
importeesList filter (_.nonEmpty) map (Importer(ref, _))
preserveOriginalImportersFormatting(Seq(importer), importeesList, ref)

case importer =>
importer.importees map (i => importer.copy(importees = i :: Nil))
}

/**
* Issue #127: After merging or exploding imports, checks whether there are any input importers
* left untouched. For those importers, returns the original importer instance to preserve the
* original source level formatting.
*/
private def preserveOriginalImportersFormatting(
importers: Seq[Importer],
newImporteeLists: Seq[List[Importee]],
newImporterRef: Term.Ref
) = {
val importerSyntaxMap = importers.map { i => i.copy().syntax -> i }.toMap

newImporteeLists filter (_.nonEmpty) map { importees =>
val newImporter = Importer(newImporterRef, importees)
importerSyntaxMap.getOrElse(newImporter.syntax, newImporter)
}
}

/**
* Categorizes a list of `Importee`s into the following four groups:
*
Expand Down
60 changes: 60 additions & 0 deletions shared/src/main/scala/fix/ExplodeImports.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package fix

object ExplodeImports {
object Wildcard1 {
object a
object b
object c
object d
}

object Wildcard2 {
object a
object b
}

object Unimport1 {
object a
object b
object c
object d
}

object Unimport2 {
object a
object b
object c
object d
}

object Rename1 {
object a
object b
object c
object d
}

object Rename2 {
object a
object b
object c
}

object Dedup {
object a
object b
object c
}

object FormatPreserving {
object g1 {
object a
object b
}

object g2 {
object c
object d
}
}
}