From 97b20e56b8260a429dd55f13398d8a65c76d3cb3 Mon Sep 17 00:00:00 2001 From: Ivan Lopatin Date: Tue, 26 Jul 2022 19:43:05 +0300 Subject: [PATCH] Preserve the original source-level formatting when possible after exploding imports (#280) --- .../fix/ExplodeImportsFormatPreserving.scala | 12 ++++ .../fix/ExplodeImportsFormatPreserving.scala | 7 +++ .../src/main/scala/fix/OrganizeImports.scala | 38 +++++++----- .../src/main/scala/fix/ExplodeImports.scala | 60 +++++++++++++++++++ 4 files changed, 103 insertions(+), 14 deletions(-) create mode 100644 input/src/main/scala/fix/ExplodeImportsFormatPreserving.scala create mode 100644 output/src/main/scala/fix/ExplodeImportsFormatPreserving.scala create mode 100644 shared/src/main/scala/fix/ExplodeImports.scala diff --git a/input/src/main/scala/fix/ExplodeImportsFormatPreserving.scala b/input/src/main/scala/fix/ExplodeImportsFormatPreserving.scala new file mode 100644 index 0000000..8b769d1 --- /dev/null +++ b/input/src/main/scala/fix/ExplodeImportsFormatPreserving.scala @@ -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 diff --git a/output/src/main/scala/fix/ExplodeImportsFormatPreserving.scala b/output/src/main/scala/fix/ExplodeImportsFormatPreserving.scala new file mode 100644 index 0000000..3b4ca7e --- /dev/null +++ b/output/src/main/scala/fix/ExplodeImportsFormatPreserving.scala @@ -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 diff --git a/rules/src/main/scala/fix/OrganizeImports.scala b/rules/src/main/scala/fix/OrganizeImports.scala index ed2dbeb..e88c54f 100644 --- a/rules/src/main/scala/fix/OrganizeImports.scala +++ b/rules/src/main/scala/fix/OrganizeImports.scala @@ -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] = @@ -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.: // @@ -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: * diff --git a/shared/src/main/scala/fix/ExplodeImports.scala b/shared/src/main/scala/fix/ExplodeImports.scala new file mode 100644 index 0000000..834ad5f --- /dev/null +++ b/shared/src/main/scala/fix/ExplodeImports.scala @@ -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 + } + } +}