From 8e20a90d40894183044bad3dc2647916adfb1a0b Mon Sep 17 00:00:00 2001 From: aKandzior Date: Fri, 8 May 2026 11:38:59 +0200 Subject: [PATCH 1/2] Use Collator when sorting Eclipse JDT members The Eclipse JDT member sorter compares method names and parameter type signatures with a java.text.Collator. Spotless' DefaultJavaElementComparator used String.compareTo instead, which can produce a different member order than Eclipse save actions for names that differ by case or locale-sensitive collation. Update DefaultJavaElementComparator to use Collator for member name comparison and parameter type signature comparison so Spotless matches Eclipse JDT behavior more closely. Adds a regression fixture which exposes the case-sensitive ordering difference. Test: ./gradlew :lib-extra:test --tests com.diffplug.spotless.extra.java.EclipseJdtFormatterStepSpecialCaseTest --no-daemon --no-configuration-cache --console=plain --- .../glue/jdt/DefaultJavaElementComparator.java | 9 ++++++--- .../EclipseJdtFormatterStepSpecialCaseTest.java | 8 ++++++++ .../java/eclipse/SortExample.collator.clean | 15 +++++++++++++++ .../java/eclipse/SortExample.collator.test | 15 +++++++++++++++ 4 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 testlib/src/main/resources/java/eclipse/SortExample.collator.clean create mode 100644 testlib/src/main/resources/java/eclipse/SortExample.collator.test diff --git a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/DefaultJavaElementComparator.java b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/DefaultJavaElementComparator.java index 6c1a576d58..624cd141b4 100644 --- a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/DefaultJavaElementComparator.java +++ b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/DefaultJavaElementComparator.java @@ -15,6 +15,7 @@ */ package com.diffplug.spotless.extra.glue.jdt; +import java.text.Collator; import java.util.Comparator; import java.util.List; import java.util.StringTokenizer; @@ -61,6 +62,7 @@ class DefaultJavaElementComparator implements Comparator { private final int[] memberCategoryOffsets; private final boolean sortByVisibility; private final int[] visibilityOffsets; + private final Collator collator; static DefaultJavaElementComparator of( boolean doNotSortFields, @@ -95,6 +97,7 @@ static DefaultJavaElementComparator of( this.memberCategoryOffsets = memberCategoryOffsets; this.sortByVisibility = sortByVisibility; this.visibilityOffsets = visibilityOffsets; + this.collator = Collator.getInstance(); } @SuppressFBWarnings(value = "SF_SWITCH_NO_DEFAULT", justification = "we only accept valid tokens in the order string, otherwise we fall back to default value") @@ -271,7 +274,7 @@ public int compare(BodyDeclaration bodyDeclaration1, BodyDeclaration bodyDeclara String name2 = method2.getName().getIdentifier(); // method declarations (constructors) are sorted by name - int cmp = name1.compareTo(name2); + int cmp = collator.compare(name1, name2); if (cmp != 0) { return cmp; } @@ -286,7 +289,7 @@ public int compare(BodyDeclaration bodyDeclaration1, BodyDeclaration bodyDeclara for (int i = 0; i < len; i++) { SingleVariableDeclaration param1 = parameters1.get(i); SingleVariableDeclaration param2 = parameters2.get(i); - cmp = buildSignature(param1.getType()).compareTo(buildSignature(param2.getType())); + cmp = collator.compare(buildSignature(param1.getType()), buildSignature(param2.getType())); if (cmp != 0) { return cmp; } @@ -377,7 +380,7 @@ private int preserveRelativeOrder(BodyDeclaration bodyDeclaration1, BodyDeclarat } private int compareNames(BodyDeclaration bodyDeclaration1, BodyDeclaration bodyDeclaration2, String name1, String name2) { - int cmp = name1.compareTo(name2); + int cmp = collator.compare(name1, name2); if (cmp != 0) { return cmp; } diff --git a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepSpecialCaseTest.java b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepSpecialCaseTest.java index ade103df17..25860fd7a4 100644 --- a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepSpecialCaseTest.java +++ b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepSpecialCaseTest.java @@ -107,4 +107,12 @@ void sort_members_local_enabled_true() { StepHarness.forStep(builder.build()) .testResource("java/eclipse/SortExample.localEnabledTrue.test", "java/eclipse/SortExample.localEnabledTrue.clean"); } + + @Test + void sort_members_uses_collator() { + EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral(), TestP2Provisioner.defaultProvisioner()); + builder.sortMembersEnabled(true); + StepHarness.forStep(builder.build()) + .testResource("java/eclipse/SortExample.collator.test", "java/eclipse/SortExample.collator.clean"); + } } diff --git a/testlib/src/main/resources/java/eclipse/SortExample.collator.clean b/testlib/src/main/resources/java/eclipse/SortExample.collator.clean new file mode 100644 index 0000000000..e2218f47cf --- /dev/null +++ b/testlib/src/main/resources/java/eclipse/SortExample.collator.clean @@ -0,0 +1,15 @@ +package com.diffplug.spotless.extra.glue.jdt; + +public class SortExample { + private static void mab() { + } + + private static void mAc() { + } + + private static void mp(String a, String b, int a) { + } + + private static void mp(String a, String b, String p) { + } +} diff --git a/testlib/src/main/resources/java/eclipse/SortExample.collator.test b/testlib/src/main/resources/java/eclipse/SortExample.collator.test new file mode 100644 index 0000000000..1eeabc989d --- /dev/null +++ b/testlib/src/main/resources/java/eclipse/SortExample.collator.test @@ -0,0 +1,15 @@ +package com.diffplug.spotless.extra.glue.jdt; + +public class SortExample { + private static void mAc() { + } + + private static void mab() { + } + + private static void mp(String a, String b, String p) { + } + + private static void mp(String a, String b, int a) { + } +} From 920e4b1e261b18723ea28be725b122652f335699 Mon Sep 17 00:00:00 2001 From: aKandzior Date: Fri, 8 May 2026 14:53:18 +0200 Subject: [PATCH 2/2] Update changelog for Eclipse JDT member sort fix --- CHANGES.md | 1 + plugin-gradle/CHANGES.md | 1 + plugin-maven/CHANGES.md | 1 + 3 files changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 3bb2ae6f56..54cfb9408d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,6 +13,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ### Added - Add `javaparserVersion` option to the Cleanthat step, allowing callers to override the JavaParser version pulled in transitively by Cleanthat. ([#2903](https://github.com/diffplug/spotless/pull/2903)) ### Changes +- Use Eclipse JDT's collator-based comparison when sorting Java members to better match Eclipse save actions. ([#2920](https://github.com/diffplug/spotless/pull/2920)) - Bump default `cleanthat` version `2.24` -> `2.25`. ([#2903](https://github.com/diffplug/spotless/pull/2903)) - Bump default `eclipse-jdt` version from `4.35` to `4.39`. ([#2912](https://github.com/diffplug/spotless/pull/2912)) diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index e939b70b70..2b010aa82f 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -9,6 +9,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ### Fixed - Fix `tableTestFormatter` editorconfig cache not honoring `.editorconfig` changes across Gradle daemon runs due to a shared static `EditorConfigProvider`. ([#2893](https://github.com/diffplug/spotless/pull/2893)) ### Changes +- Use Eclipse JDT's collator-based comparison when sorting Java members to better match Eclipse save actions. ([#2920](https://github.com/diffplug/spotless/pull/2920)) - Bump default `cleanthat` version `2.24` -> `2.25`. ([#2903](https://github.com/diffplug/spotless/pull/2903)) - Bump default `eclipse-jdt` version from `4.35` to `4.39`. ([#2912](https://github.com/diffplug/spotless/pull/2912)) diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index 70f0ec99c7..7e30d0bde0 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -6,6 +6,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ### Added - Add `` option to ``, allowing users to override the JavaParser version pulled in transitively by Cleanthat. ([#2903](https://github.com/diffplug/spotless/pull/2903)) ### Changes +- Use Eclipse JDT's collator-based comparison when sorting Java members to better match Eclipse save actions. ([#2920](https://github.com/diffplug/spotless/pull/2920)) - Bump default `cleanthat` version `2.24` -> `2.25`. ([#2903](https://github.com/diffplug/spotless/pull/2903)) - Bump default `eclipse-jdt` version from `4.35` to `4.39`. ([#2912](https://github.com/diffplug/spotless/pull/2912))