diff --git a/packages/prettier-plugin-java/src/printers/packages-and-modules.js b/packages/prettier-plugin-java/src/printers/packages-and-modules.js index 5994102c..61bf6094 100644 --- a/packages/prettier-plugin-java/src/printers/packages-and-modules.js +++ b/packages/prettier-plugin-java/src/printers/packages-and-modules.js @@ -10,7 +10,8 @@ const { rejectAndJoinSeps, displaySemicolon, putIntoCurlyBraces, - getBlankLinesSeparator + getBlankLinesSeparator, + sortImports } = require("./printer-utils"); class PackagesAndModulesPrettierVisitor { @@ -22,17 +23,20 @@ class PackagesAndModulesPrettierVisitor { ordinaryCompilationUnit(ctx) { const packageDecl = this.visit(ctx.packageDeclaration); - // TODO: Should imports be sorted? Can imports in Java be safely sorted? - // TODO2: should the imports be grouped in some manner? - const importsDecl = this.mapVisit(ctx.importDeclaration); + + const sortedImportsDecl = sortImports(ctx.importDeclaration); + const nonStaticImports = this.mapVisit(sortedImportsDecl.nonStaticImports); + const staticImports = this.mapVisit(sortedImportsDecl.staticImports); + const typesDecl = this.mapVisit(ctx.typeDeclaration); // TODO: utility to add item+line (or multiple lines) but only if an item exists return rejectAndConcat([ - rejectAndJoin(concat([line, line]), [ + rejectAndJoin(concat([hardline, hardline]), [ packageDecl, - rejectAndJoin(line, importsDecl), - rejectAndJoin(concat([line, line]), typesDecl) + rejectAndJoin(hardline, staticImports), + rejectAndJoin(hardline, nonStaticImports), + rejectAndJoin(concat([hardline, hardline]), typesDecl) ]), line ]); diff --git a/packages/prettier-plugin-java/src/printers/printer-utils.js b/packages/prettier-plugin-java/src/printers/printer-utils.js index e59ea350..4adfc1c1 100644 --- a/packages/prettier-plugin-java/src/printers/printer-utils.js +++ b/packages/prettier-plugin-java/src/printers/printer-utils.js @@ -531,6 +531,63 @@ function isStatementEmptyStatement(statement) { ); } +function sortImports(imports) { + const staticImports = []; + const nonStaticImports = []; + + if (imports !== undefined) { + for (let i = 0; i < imports.length; i++) { + if (imports[i].children.Static !== undefined) { + staticImports.push(imports[i]); + } else if (imports[i].children.emptyStatement === undefined) { + nonStaticImports.push(imports[i]); + } + } + + // TODO: Could be optimized as we could expect that the array is already almost sorted + staticImports.sort((first, second) => + compareFqn( + first.children.packageOrTypeName[0], + second.children.packageOrTypeName[0] + ) + ); + nonStaticImports.sort((first, second) => + compareFqn( + first.children.packageOrTypeName[0], + second.children.packageOrTypeName[0] + ) + ); + } + + return { + staticImports, + nonStaticImports + }; +} + +function compareFqn(packageOrTypeNameFirst, packageOrTypeNameSecond) { + const identifiersFirst = packageOrTypeNameFirst.children.Identifier; + const identifiersSecond = packageOrTypeNameSecond.children.Identifier; + + const minParts = Math.min(identifiersFirst.length, identifiersSecond.length); + for (let i = 0; i < minParts; i++) { + const identifierComparison = identifiersFirst[i].image.localeCompare( + identifiersSecond[i].image + ); + if (identifierComparison !== 0) { + return identifierComparison; + } + } + + if (identifiersFirst.length < identifiersSecond.length) { + return -1; + } else if (identifiersFirst.length > identifiersSecond.length) { + return 1; + } + + return 0; +} + module.exports = { buildFqn, reject, @@ -556,5 +613,6 @@ module.exports = { retrieveNodesToken, buildOriginalText, getCSTNodeStartEndToken, - isStatementEmptyStatement + isStatementEmptyStatement, + sortImports }; diff --git a/packages/prettier-plugin-java/test/unit-test/comments/class/_output.java b/packages/prettier-plugin-java/test/unit-test/comments/class/_output.java index ac857a06..db4bed5a 100644 --- a/packages/prettier-plugin-java/test/unit-test/comments/class/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/comments/class/_output.java @@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkElementIndex; import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; diff --git a/packages/prettier-plugin-java/test/unit-test/package_and_imports/_input.java b/packages/prettier-plugin-java/test/unit-test/package_and_imports/_input.java index 4250eeb4..ea1ce6f6 100644 --- a/packages/prettier-plugin-java/test/unit-test/package_and_imports/_input.java +++ b/packages/prettier-plugin-java/test/unit-test/package_and_imports/_input.java @@ -4,5 +4,12 @@ import java.utils.*;;; import abc.def.Something; import abc.def.Another;;; +import abc.def; +import static abc.def; +import static something.Different; +import static java.utils.*;;; +import static abc.def.Something; +import static abc.def.Another;;; +import one.last;;; public class PackageAndImports {} diff --git a/packages/prettier-plugin-java/test/unit-test/package_and_imports/_output.java b/packages/prettier-plugin-java/test/unit-test/package_and_imports/_output.java index 7b983936..7930a330 100644 --- a/packages/prettier-plugin-java/test/unit-test/package_and_imports/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/package_and_imports/_output.java @@ -1,8 +1,16 @@ package my.own.pkg; -import something.Different; -import java.utils.*; -import abc.def.Something; +import static abc.def; +import static abc.def.Another; +import static abc.def.Something; +import static java.utils.*; +import static something.Different; + +import abc.def; import abc.def.Another; +import abc.def.Something; +import java.utils.*; +import one.last; +import something.Different; public class PackageAndImports {}