From 2a2960563fd2830ec96a751b2576c35f98c31311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Thu, 24 Oct 2024 23:01:42 +0200 Subject: [PATCH 01/20] Add Sort Members functionality to Eclipse formatter --- lib-extra/build.gradle | 2 +- .../jdt/DefaultJavaElementComparator.java | 369 ++++++++++++++++++ .../glue/jdt/EclipseJdtFormatterStepImpl.java | 20 +- .../extra/glue/jdt/EclipseJdtSortMembers.java | 202 ++++++++++ .../spotless/extra/glue/jdt/JdtFlags.java | 76 ++++ .../spotless/extra/EquoBasedStepBuilder.java | 32 +- .../extra/cpp/EclipseCdtFormatterStep.java | 3 +- .../extra/groovy/GrEclipseFormatterStep.java | 3 +- .../extra/java/EclipseJdtFormatterStep.java | 70 +++- ...clipseJdtFormatterStepSpecialCaseTest.java | 30 +- .../gradle/spotless/JavaExtension.java | 26 +- .../eclipse/SortExample.sortMembers.clean | 33 ++ .../SortExample.sortMembersByVisibility.clean | 33 ++ .../SortExample.sortMembersNoFields.clean | 33 ++ .../resources/java/eclipse/SortExample.test | 28 ++ 15 files changed, 921 insertions(+), 39 deletions(-) create mode 100644 lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/DefaultJavaElementComparator.java create mode 100644 lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java create mode 100644 lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/JdtFlags.java create mode 100644 testlib/src/main/resources/java/eclipse/SortExample.sortMembers.clean create mode 100644 testlib/src/main/resources/java/eclipse/SortExample.sortMembersByVisibility.clean create mode 100644 testlib/src/main/resources/java/eclipse/SortExample.sortMembersNoFields.clean create mode 100644 testlib/src/main/resources/java/eclipse/SortExample.test diff --git a/lib-extra/build.gradle b/lib-extra/build.gradle index 9984ac696a..7bb686dac5 100644 --- a/lib-extra/build.gradle +++ b/lib-extra/build.gradle @@ -87,6 +87,7 @@ p2deps { into 'jdtCompileOnly', { p2repo 'https://download.eclipse.org/eclipse/updates/4.26/' install 'org.eclipse.jdt.core' + install 'org.eclipse.jdt.core.manipulation' } } @@ -95,4 +96,3 @@ spotbugs { // LOW|MEDIUM|DEFAULT|HIGH (low = sensitive to even minor mistakes). reportLevel = com.github.spotbugs.snom.Confidence.valueOf('LOW') } - 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 new file mode 100644 index 0000000000..c42e09f5fe --- /dev/null +++ b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/DefaultJavaElementComparator.java @@ -0,0 +1,369 @@ +package com.diffplug.spotless.extra.glue.jdt; + +import java.util.Comparator; +import java.util.List; +import java.util.StringTokenizer; + +import org.eclipse.jdt.internal.corext.dom.ASTNodes; + +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; +import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration; +import org.eclipse.jdt.core.dom.BodyDeclaration; +import org.eclipse.jdt.core.dom.EnumConstantDeclaration; +import org.eclipse.jdt.core.dom.FieldDeclaration; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.Modifier; +import org.eclipse.jdt.core.dom.SingleVariableDeclaration; +import org.eclipse.jdt.core.dom.Type; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; +import org.eclipse.jdt.core.util.CompilationUnitSorter; + +class DefaultJavaElementComparator implements Comparator { + + static final int TYPE_INDEX = 0; + static final int CONSTRUCTORS_INDEX = 1; + static final int METHOD_INDEX = 2; + static final int FIELDS_INDEX = 3; + static final int INIT_INDEX = 4; + static final int STATIC_FIELDS_INDEX = 5; + static final int STATIC_INIT_INDEX = 6; + static final int STATIC_METHODS_INDEX = 7; + static final int ENUM_CONSTANTS_INDEX = 8; + static final int N_CATEGORIES = 9; + static final int PUBLIC_INDEX = 0; + static final int PRIVATE_INDEX = 1; + static final int PROTECTED_INDEX = 2; + static final int DEFAULT_INDEX = 3; + static final int N_VISIBILITIES = 4; + + private final boolean doNotSortFields; + private final int[] memberCategoryOffsets; + private final boolean sortByVisibility; + private final int[] visibilityOffsets; + + static DefaultJavaElementComparator of( + boolean doNotSortFields, + String memberCategoryPreferences, + boolean sortByVisibility, + String visibilityPreferences) { + + int[] memberCategoryOffsets = new int[9]; + boolean success = fillMemberCategoryOffsets(memberCategoryPreferences, memberCategoryOffsets); + if (!success) { + String defaultValue = "T,SF,SI,SM,F,I,C,M"; + fillMemberCategoryOffsets(defaultValue, memberCategoryOffsets); + } + + int[] visibilityOffsets = new int[4]; + boolean success2 = fillVisibilityOffsets(visibilityPreferences, visibilityOffsets); + if (!success2) { + String defaultValue = "B,V,R,D"; + fillVisibilityOffsets(defaultValue, visibilityOffsets); + } + + return new DefaultJavaElementComparator(doNotSortFields, memberCategoryOffsets, sortByVisibility, visibilityOffsets); + } + + DefaultJavaElementComparator( + boolean doNotSortFields, + int[] memberCategoryOffsets, + boolean sortByVisibility, + int[] visibilityOffsets) { + + this.doNotSortFields = doNotSortFields; + this.memberCategoryOffsets = memberCategoryOffsets; + this.sortByVisibility = sortByVisibility; + this.visibilityOffsets = visibilityOffsets; + } + + static boolean fillVisibilityOffsets(String preferencesString, int[] offsets) { + StringTokenizer tokenizer = new StringTokenizer(preferencesString, ","); + int i = 0; + while (tokenizer.hasMoreTokens()) { + String token = tokenizer.nextToken(); + if (token != null) { + switch (token) { + case "B": + offsets[PUBLIC_INDEX] = i++; + break; + case "D": + offsets[DEFAULT_INDEX] = i++; + break; + case "R": + offsets[PROTECTED_INDEX] = i++; + break; + case "V": + offsets[PRIVATE_INDEX] = i++; + } + } + } + return i == N_VISIBILITIES; + } + + static boolean fillMemberCategoryOffsets(String preferencesString, int[] offsets) { + StringTokenizer tokenizer = new StringTokenizer(preferencesString, ","); + int i = 0; + offsets[8] = i++; + while (tokenizer.hasMoreTokens()) { + String token = tokenizer.nextToken(); + if (token != null) { + switch (token) { + case "C": + offsets[CONSTRUCTORS_INDEX] = i++; + break; + case "F": + offsets[FIELDS_INDEX] = i++; + break; + case "I": + offsets[INIT_INDEX] = i++; + break; + case "M": + offsets[METHOD_INDEX] = i++; + break; + case "T": + offsets[TYPE_INDEX] = i++; + break; + case "SF": + offsets[STATIC_FIELDS_INDEX] = i++; + break; + case "SI": + offsets[STATIC_INIT_INDEX] = i++; + break; + case "SM": + offsets[STATIC_METHODS_INDEX] = i++; + } + } + } + return i == N_CATEGORIES; + } + + private int category(BodyDeclaration bodyDeclaration) { + switch (bodyDeclaration.getNodeType()) { + case ASTNode.METHOD_DECLARATION: { + MethodDeclaration method = (MethodDeclaration) bodyDeclaration; + if (method.isConstructor()) { + return CONSTRUCTORS_INDEX; + } + int flags = method.getModifiers(); + if (Modifier.isStatic(flags)) + return STATIC_METHODS_INDEX; + else + return METHOD_INDEX; + } + case ASTNode.FIELD_DECLARATION: { + if (JdtFlags.isStatic(bodyDeclaration)) + return STATIC_FIELDS_INDEX; + else + return FIELDS_INDEX; + } + case ASTNode.INITIALIZER: { + int flags = bodyDeclaration.getModifiers(); + if (Modifier.isStatic(flags)) + return STATIC_INIT_INDEX; + else + return INIT_INDEX; + } + case ASTNode.TYPE_DECLARATION: + case ASTNode.ENUM_DECLARATION: + case ASTNode.ANNOTATION_TYPE_DECLARATION: + return TYPE_INDEX; + case ASTNode.ENUM_CONSTANT_DECLARATION: + return ENUM_CONSTANTS_INDEX; + case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION: + return METHOD_INDEX; // reusing the method index + + } + return 0; // should never happen + } + + private int getCategoryIndex(int category) { + return memberCategoryOffsets[category]; + } + + private int getVisibilityIndex(int modifierFlags) { + int kind = 3; + if (Flags.isPublic(modifierFlags)) { + kind = 0; + } else if (Flags.isProtected(modifierFlags)) { + kind = 2; + } else if (Flags.isPrivate(modifierFlags)) { + kind = 1; + } + return this.visibilityOffsets[kind]; + } + + /** + * This comparator follows the contract defined in CompilationUnitSorter.sort. + * + * @see Comparator#compare(java.lang.Object, java.lang.Object) + * @see CompilationUnitSorter#sort(int, org.eclipse.jdt.core.ICompilationUnit, int[], java.util.Comparator, int, org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + public int compare(BodyDeclaration bodyDeclaration1, BodyDeclaration bodyDeclaration2) { + boolean preserved1 = doNotSortFields && isSortPreserved(bodyDeclaration1); + boolean preserved2 = doNotSortFields && isSortPreserved(bodyDeclaration2); + + // Bug 407759: need to use a common category for all isSortPreserved members that are to be sorted in the same group: + int cat1 = category(bodyDeclaration1); + if (preserved1) { + cat1 = sortPreservedCategory(cat1); + } + int cat2 = category(bodyDeclaration2); + if (preserved2) { + cat2 = sortPreservedCategory(cat2); + } + + if (cat1 != cat2) { + return getCategoryIndex(cat1) - getCategoryIndex(cat2); + } + + // cat1 == cat2 implies preserved1 == preserved2 + + if (preserved1) { + return preserveRelativeOrder(bodyDeclaration1, bodyDeclaration2); + } + + if (sortByVisibility) { + int flags1 = JdtFlags.getVisibilityCode(bodyDeclaration1); + int flags2 = JdtFlags.getVisibilityCode(bodyDeclaration2); + int vis = getVisibilityIndex(flags1) - getVisibilityIndex(flags2); + if (vis != 0) { + return vis; + } + } + + switch (bodyDeclaration1.getNodeType()) { + case ASTNode.METHOD_DECLARATION: { + MethodDeclaration method1 = (MethodDeclaration) bodyDeclaration1; + MethodDeclaration method2 = (MethodDeclaration) bodyDeclaration2; + + if (sortByVisibility) { + int vis = getVisibilityIndex(method1.getModifiers()) - getVisibilityIndex(method2.getModifiers()); + if (vis != 0) { + return vis; + } + } + + String name1 = method1.getName().getIdentifier(); + String name2 = method2.getName().getIdentifier(); + + // method declarations (constructors) are sorted by name + int cmp = name1.compareTo(name2); + if (cmp != 0) { + return cmp; + } + + // if names equal, sort by parameter types + List parameters1 = method1.parameters(); + List parameters2 = method2.parameters(); + int length1 = parameters1.size(); + int length2 = parameters2.size(); + + int len = Math.min(length1, length2); + 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())); + if (cmp != 0) { + return cmp; + } + } + if (length1 != length2) { + return length1 - length2; + } + return preserveRelativeOrder(bodyDeclaration1, bodyDeclaration2); + } + case ASTNode.FIELD_DECLARATION: { + FieldDeclaration field1 = (FieldDeclaration) bodyDeclaration1; + FieldDeclaration field2 = (FieldDeclaration) bodyDeclaration2; + + String name1 = ((VariableDeclarationFragment) field1.fragments().get(0)).getName().getIdentifier(); + String name2 = ((VariableDeclarationFragment) field2.fragments().get(0)).getName().getIdentifier(); + + // field declarations are sorted by name + return compareNames(bodyDeclaration1, bodyDeclaration2, name1, name2); + } + case ASTNode.INITIALIZER: { + // preserve relative order + return preserveRelativeOrder(bodyDeclaration1, bodyDeclaration2); + } + case ASTNode.TYPE_DECLARATION: + case ASTNode.ENUM_DECLARATION: + case ASTNode.ANNOTATION_TYPE_DECLARATION: { + AbstractTypeDeclaration type1 = (AbstractTypeDeclaration) bodyDeclaration1; + AbstractTypeDeclaration type2 = (AbstractTypeDeclaration) bodyDeclaration2; + + String name1 = type1.getName().getIdentifier(); + String name2 = type2.getName().getIdentifier(); + + // typedeclarations are sorted by name + return compareNames(bodyDeclaration1, bodyDeclaration2, name1, name2); + } + case ASTNode.ENUM_CONSTANT_DECLARATION: { + EnumConstantDeclaration decl1 = (EnumConstantDeclaration) bodyDeclaration1; + EnumConstantDeclaration decl2 = (EnumConstantDeclaration) bodyDeclaration2; + + String name1 = decl1.getName().getIdentifier(); + String name2 = decl2.getName().getIdentifier(); + + // enum constants declarations are sorted by name + return compareNames(bodyDeclaration1, bodyDeclaration2, name1, name2); + } + case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION: { + AnnotationTypeMemberDeclaration decl1 = (AnnotationTypeMemberDeclaration) bodyDeclaration1; + AnnotationTypeMemberDeclaration decl2 = (AnnotationTypeMemberDeclaration) bodyDeclaration2; + + String name1 = decl1.getName().getIdentifier(); + String name2 = decl2.getName().getIdentifier(); + + // enum constants declarations are sorted by name + return compareNames(bodyDeclaration1, bodyDeclaration2, name1, name2); + } + } + return 0; + } + + private static int sortPreservedCategory(int category) { + switch (category) { + case STATIC_FIELDS_INDEX: + case STATIC_INIT_INDEX: + return STATIC_FIELDS_INDEX; + case FIELDS_INDEX: + case INIT_INDEX: + return FIELDS_INDEX; + default: + return category; + } + } + + private boolean isSortPreserved(BodyDeclaration bodyDeclaration) { + switch (bodyDeclaration.getNodeType()) { + case ASTNode.FIELD_DECLARATION: + case ASTNode.ENUM_CONSTANT_DECLARATION: + case ASTNode.INITIALIZER: + return true; + default: + return false; + } + } + + private int preserveRelativeOrder(BodyDeclaration bodyDeclaration1, BodyDeclaration bodyDeclaration2) { + int value1 = ((Integer) bodyDeclaration1.getProperty(CompilationUnitSorter.RELATIVE_ORDER)); + int value2 = ((Integer) bodyDeclaration2.getProperty(CompilationUnitSorter.RELATIVE_ORDER)); + return value1 - value2; + } + + private int compareNames(BodyDeclaration bodyDeclaration1, BodyDeclaration bodyDeclaration2, String name1, String name2) { + int cmp = name1.compareTo(name2); + if (cmp != 0) { + return cmp; + } + return preserveRelativeOrder(bodyDeclaration1, bodyDeclaration2); + } + + private String buildSignature(Type type) { + return ASTNodes.asString(type); + } +} diff --git a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtFormatterStepImpl.java b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtFormatterStepImpl.java index b394b6278d..76228a4011 100644 --- a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtFormatterStepImpl.java +++ b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtFormatterStepImpl.java @@ -34,18 +34,21 @@ public class EclipseJdtFormatterStepImpl { public static final String LINE_DELIMITER = "\n"; private final CodeFormatter codeFormatter; + private final EclipseJdtSortMembers.SortProperties sortProperties; - public EclipseJdtFormatterStepImpl(Properties settings) { - Map options = settings.entrySet().stream().collect(Collectors.toMap( - e -> String.valueOf(e.getKey()), - e -> String.valueOf(e.getValue()), - (prev, next) -> next, - HashMap::new)); + public EclipseJdtFormatterStepImpl(Properties formatterSettings, Map sortProperties) { + Map options = formatterSettings.entrySet().stream().collect(Collectors.toMap( + e -> String.valueOf(e.getKey()), + e -> String.valueOf(e.getValue()), + (prev, next) -> next, + HashMap::new)); this.codeFormatter = new DefaultCodeFormatter(options); + this.sortProperties = EclipseJdtSortMembers.SortProperties.from(sortProperties); } /** Formatting Java string, distinguishing module-info and compilation unit by file name */ public String format(String raw, File file) throws Exception { + raw = sort(raw); int kind = (file.getName().equals(IModule.MODULE_INFO_JAVA) ? CodeFormatter.K_MODULE_INFO : CodeFormatter.K_COMPILATION_UNIT) | CodeFormatter.F_INCLUDE_COMMENTS; TextEdit edit = codeFormatter.format(kind, raw, 0, raw.length(), 0, LINE_DELIMITER); @@ -57,4 +60,9 @@ public String format(String raw, File file) throws Exception { return doc.get(); } } + + /** Sort members in Java string */ + public String sort(String raw) { + return EclipseJdtSortMembers.sortMember(raw, sortProperties); + } } diff --git a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java new file mode 100644 index 0000000000..0ceb3e8284 --- /dev/null +++ b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java @@ -0,0 +1,202 @@ +package com.diffplug.spotless.extra.glue.jdt; + +import java.util.Comparator; +import java.util.Map; + +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IBuffer; +import org.eclipse.jdt.core.IBufferChangedListener; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IOpenable; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.dom.AST; +import org.eclipse.jdt.internal.core.CompilationUnit; +import org.eclipse.jdt.internal.core.JavaProject; +import org.eclipse.jdt.internal.core.SortElementsOperation; + +public class EclipseJdtSortMembers { + + private static CompilationUnit compilationUnit(String code, Map options, Map compilerOptions) { + return new CompilationUnit(null, null, null) { + private final Buffer buffer = new Buffer(code); + + @Override + public IBuffer getBuffer() { + return buffer; + } + + @Override + public JavaProject getJavaProject() { + return new JavaProject(null, null) { + @Override + public Map getOptions(boolean inheritJavaCoreOptions) { + return compilerOptions; + } + }; + } + + @Override + public Map getOptions(boolean inheritJavaCoreOptions) { + return options; + } + + @Override + public ICompilationUnit getPrimary() { + return this; + } + }; + } + + static String sortMember(String code, SortProperties properties) { + if (!properties.enabled) { + return code; + } + + try { + CompilationUnit compilationUnit = compilationUnit(code, properties.compilationUnitOptions, properties.compilerOptions); + DefaultJavaElementComparator comparator = DefaultJavaElementComparator.of(properties.doNotSortFields, properties.membersOrder, properties.sortByVisibility, properties.visibilityOrder); + new Sorter(AST.getJLSLatest(), compilationUnit, null, comparator).sort(); + String content = compilationUnit.getBuffer().getContents(); + if (content != null) { + code = content; + } + } catch (CoreException e) { + throw new RuntimeException(e); + } + return code; + } + + private static class Buffer implements IBuffer { + + private String contents; + + Buffer(String contents) { + this.contents = contents; + } + + public void addBufferChangedListener(IBufferChangedListener listener) { + } + + public void append(char[] text) { + } + + public void append(String text) { + } + + public void close() { + } + + public char getChar(int position) { + return '\u0000'; + } + + public char[] getCharacters() { + return contents.toCharArray(); + } + + public String getContents() { + return contents; + } + + public int getLength() { + return 0; + } + + public IOpenable getOwner() { + return null; + } + + public String getText(int offset, int length) { + return null; + } + + public IResource getUnderlyingResource() { + return null; + } + + public boolean hasUnsavedChanges() { + return false; + } + + public boolean isClosed() { + return false; + } + + public boolean isReadOnly() { + return true; + } + + public void removeBufferChangedListener(IBufferChangedListener listener) { + } + + public void replace(int position, int length, char[] text) { + } + + public void replace(int position, int length, String text) { + } + + public void save(IProgressMonitor progress, boolean force) { + } + + public void setContents(char[] contents) { + } + + public void setContents(String contents) { + this.contents = contents; + } + } + + private static class Sorter extends SortElementsOperation { + + Sorter(int level, CompilationUnit compilationUnit, int[] positions, Comparator comparator) { + super(level, new IJavaElement[]{compilationUnit}, positions, comparator); + } + + void sort() throws JavaModelException { + executeOperation(); + } + } + + static class SortProperties { + final Map compilationUnitOptions; + final Map compilerOptions; + final boolean doNotSortFields; + final boolean enabled; + final String membersOrder; + final boolean sortByVisibility; + final String visibilityOrder; + + SortProperties( + boolean enabled, + String membersOrder, + boolean doNotSortFields, + boolean sortByVisibility, + String visibilityOrder, + Map compilationUnitOptions, + Map compilerOptions + ) { + this.enabled = enabled; + this.membersOrder = membersOrder; + this.doNotSortFields = doNotSortFields; + this.sortByVisibility = sortByVisibility; + this.visibilityOrder = visibilityOrder; + this.compilationUnitOptions = compilationUnitOptions; + this.compilerOptions = compilerOptions; + } + + static SortProperties from(Map properties) { + boolean enabled = Boolean.parseBoolean(properties.getOrDefault("members.order.enabled", "false")); + String membersOrder = properties.getOrDefault("members.order", ""); + boolean doNotSortFields = Boolean.parseBoolean(properties.getOrDefault("members.doNotSortFields", "true")); + boolean sortByVisibility = Boolean.parseBoolean(properties.getOrDefault("visibility.order.enabled", "false")); + String visibilityOrder = properties.getOrDefault("visibility.order", ""); + // At the moment we see no need for the following options, but they may become important, idk. + Map compilationUnitOptions = Map.of(); + Map compilerOptions = Map.of(); + return new SortProperties(enabled, membersOrder, doNotSortFields, sortByVisibility, visibilityOrder, compilationUnitOptions, compilerOptions); + } + } +} diff --git a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/JdtFlags.java b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/JdtFlags.java new file mode 100644 index 0000000000..032c7f1d84 --- /dev/null +++ b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/JdtFlags.java @@ -0,0 +1,76 @@ +package com.diffplug.spotless.extra.glue.jdt; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; +import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; +import org.eclipse.jdt.core.dom.BodyDeclaration; +import org.eclipse.jdt.core.dom.EnumConstantDeclaration; +import org.eclipse.jdt.core.dom.EnumDeclaration; +import org.eclipse.jdt.core.dom.Modifier; +import org.eclipse.jdt.core.dom.TypeDeclaration; + +class JdtFlags { + + static final int VISIBILITY_CODE_INVALID= -1; + + static boolean isStatic(BodyDeclaration bodyDeclaration) { + if (isNestedInterfaceOrAnnotation(bodyDeclaration)) + return true; + int nodeType= bodyDeclaration.getNodeType(); + if (nodeType != ASTNode.METHOD_DECLARATION + && nodeType != ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION + && isInterfaceOrAnnotationMember(bodyDeclaration)) + return true; + if (bodyDeclaration instanceof EnumConstantDeclaration) + return true; + if (bodyDeclaration instanceof EnumDeclaration && bodyDeclaration.getParent() instanceof AbstractTypeDeclaration) + return true; + return Modifier.isStatic(bodyDeclaration.getModifiers()); + } + + private static boolean isPackageVisible(BodyDeclaration bodyDeclaration) { + return (! isPrivate(bodyDeclaration) && ! isProtected(bodyDeclaration) && ! isPublic(bodyDeclaration)); + } + + private static boolean isPrivate(BodyDeclaration bodyDeclaration) { + return Modifier.isPrivate(bodyDeclaration.getModifiers()); + } + + private static boolean isProtected(BodyDeclaration bodyDeclaration) { + return Modifier.isProtected(bodyDeclaration.getModifiers()); + } + + private static boolean isPublic(BodyDeclaration bodyDeclaration) { + if (isInterfaceOrAnnotationMember(bodyDeclaration)) + return true; + return Modifier.isPublic(bodyDeclaration.getModifiers()); + } + + private static boolean isInterfaceOrAnnotationMember(BodyDeclaration bodyDeclaration) { + return isInterfaceOrAnnotation(bodyDeclaration.getParent()); + } + + private static boolean isInterfaceOrAnnotation(ASTNode node) { + boolean isInterface= (node instanceof TypeDeclaration) && ((TypeDeclaration) node).isInterface(); + boolean isAnnotation= node instanceof AnnotationTypeDeclaration; + return isInterface || isAnnotation; + } + + private static boolean isNestedInterfaceOrAnnotation(BodyDeclaration bodyDeclaration) { + return bodyDeclaration.getParent() instanceof AbstractTypeDeclaration && isInterfaceOrAnnotation(bodyDeclaration); + } + + static int getVisibilityCode(BodyDeclaration bodyDeclaration) { + if (isPublic(bodyDeclaration)) + return Modifier.PUBLIC; + else if (isProtected(bodyDeclaration)) + return Modifier.PROTECTED; + else if (isPackageVisible(bodyDeclaration)) + return Modifier.NONE; + else if (isPrivate(bodyDeclaration)) + return Modifier.PRIVATE; + Assert.isTrue(false); + return VISIBILITY_CODE_INVALID; + } +} diff --git a/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java b/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java index 4f54e1e1bc..293797578d 100644 --- a/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java +++ b/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java @@ -28,6 +28,7 @@ import javax.annotation.Nullable; +import com.diffplug.common.collect.ImmutableMap; import com.diffplug.spotless.FileSignature; import com.diffplug.spotless.FormatterFunc; import com.diffplug.spotless.FormatterProperties; @@ -50,17 +51,25 @@ public abstract class EquoBasedStepBuilder { private final String formatterName; private final Provisioner mavenProvisioner; private final SerializedFunction stateToFormatter; + private final ImmutableMap.Builder stepProperties; private String formatterVersion; private Iterable settingsFiles = new ArrayList<>(); private Map p2Mirrors = Map.of(); private File cacheDirectory; /** Initialize valid default configuration, taking latest version */ - public EquoBasedStepBuilder(String formatterName, Provisioner mavenProvisioner, @Nullable String defaultVersion, SerializedFunction stateToFormatter) { + public EquoBasedStepBuilder( + String formatterName, + Provisioner mavenProvisioner, + @Nullable String defaultVersion, + SerializedFunction stateToFormatter, + ImmutableMap.Builder stepProperties) { + this.formatterName = formatterName; this.mavenProvisioner = mavenProvisioner; this.formatterVersion = defaultVersion; this.stateToFormatter = stateToFormatter; + this.stepProperties = stepProperties; } public void setVersion(String version) { @@ -125,7 +134,7 @@ public FormatterStep build() { classpath.add(nested.getValue()); } return JarState.preserveOrder(classpath); - })); + }), stepProperties.build()); return FormatterStep.create(formatterName, roundtrippableState, EquoStep::state, stateToFormatter); } @@ -157,15 +166,22 @@ static class EquoStep implements Serializable { private final String semanticVersion; private final FileSignature.Promised settingsPromise; private final JarState.Promised jarPromise; + private final ImmutableMap stepProperties; + + EquoStep( + String semanticVersion, + FileSignature.Promised settingsPromise, + JarState.Promised jarPromise, + ImmutableMap stepProperties) { - EquoStep(String semanticVersion, FileSignature.Promised settingsPromise, JarState.Promised jarPromise) { this.semanticVersion = semanticVersion; this.settingsPromise = settingsPromise; this.jarPromise = jarPromise; + this.stepProperties = stepProperties; } private State state() { - return new State(semanticVersion, jarPromise.get(), settingsPromise.get()); + return new State(semanticVersion, jarPromise.get(), settingsPromise.get(), stepProperties); } } @@ -178,11 +194,13 @@ public static class State implements Serializable { final String semanticVersion; final JarState jarState; final FileSignature settingsFiles; + final ImmutableMap stepProperties; - public State(String semanticVersion, JarState jarState, FileSignature settingsFiles) { + public State(String semanticVersion, JarState jarState, FileSignature settingsFiles, ImmutableMap stepProperties) { this.semanticVersion = semanticVersion; this.jarState = jarState; this.settingsFiles = settingsFiles; + this.stepProperties = stepProperties; } public JarState getJarState() { @@ -196,5 +214,9 @@ public String getSemanticVersion() { public Properties getPreferences() { return FormatterProperties.from(settingsFiles.files()).getProperties(); } + + public ImmutableMap getStepProperties() { + return stepProperties; + } } } diff --git a/lib-extra/src/main/java/com/diffplug/spotless/extra/cpp/EclipseCdtFormatterStep.java b/lib-extra/src/main/java/com/diffplug/spotless/extra/cpp/EclipseCdtFormatterStep.java index 8f4660b686..9fcd595aa8 100644 --- a/lib-extra/src/main/java/com/diffplug/spotless/extra/cpp/EclipseCdtFormatterStep.java +++ b/lib-extra/src/main/java/com/diffplug/spotless/extra/cpp/EclipseCdtFormatterStep.java @@ -18,6 +18,7 @@ import java.lang.reflect.InvocationTargetException; import java.util.Properties; +import com.diffplug.common.collect.ImmutableMap; import com.diffplug.spotless.FormatterFunc; import com.diffplug.spotless.Jvm; import com.diffplug.spotless.Provisioner; @@ -45,7 +46,7 @@ public static String defaultVersion() { /** Provides default configuration */ public static EquoBasedStepBuilder createBuilder(Provisioner provisioner) { - return new EquoBasedStepBuilder(NAME, provisioner, defaultVersion(), EclipseCdtFormatterStep::apply) { + return new EquoBasedStepBuilder(NAME, provisioner, defaultVersion(), EclipseCdtFormatterStep::apply, ImmutableMap.builder()) { @Override protected P2Model model(String version) { var model = new P2Model(); diff --git a/lib-extra/src/main/java/com/diffplug/spotless/extra/groovy/GrEclipseFormatterStep.java b/lib-extra/src/main/java/com/diffplug/spotless/extra/groovy/GrEclipseFormatterStep.java index 4e0a98effd..4e35dceac5 100644 --- a/lib-extra/src/main/java/com/diffplug/spotless/extra/groovy/GrEclipseFormatterStep.java +++ b/lib-extra/src/main/java/com/diffplug/spotless/extra/groovy/GrEclipseFormatterStep.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Properties; +import com.diffplug.common.collect.ImmutableMap; import com.diffplug.spotless.FormatterFunc; import com.diffplug.spotless.Jvm; import com.diffplug.spotless.Provisioner; @@ -39,7 +40,7 @@ public static String defaultVersion() { } public static EquoBasedStepBuilder createBuilder(Provisioner provisioner) { - return new EquoBasedStepBuilder(NAME, provisioner, defaultVersion(), GrEclipseFormatterStep::apply) { + return new EquoBasedStepBuilder(NAME, provisioner, defaultVersion(), GrEclipseFormatterStep::apply, ImmutableMap.builder()) { @Override protected P2Model model(String version) { if (!version.startsWith("4.")) { diff --git a/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java b/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java index b7a53bde0c..9dc463b49f 100644 --- a/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java +++ b/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java @@ -16,11 +16,14 @@ package com.diffplug.spotless.extra.java; import java.io.File; +import java.util.Map; import java.util.Properties; +import com.diffplug.common.collect.ImmutableMap; import com.diffplug.spotless.FormatterFunc; import com.diffplug.spotless.Jvm; import com.diffplug.spotless.Provisioner; +import com.diffplug.spotless.SerializedFunction; import com.diffplug.spotless.extra.EquoBasedStepBuilder; import dev.equo.solstice.p2.P2Model; @@ -37,34 +40,59 @@ public static String defaultVersion() { return JVM_SUPPORT.getRecommendedFormatterVersion(); } - public static EquoBasedStepBuilder createBuilder(Provisioner provisioner) { - return new EquoBasedStepBuilder(NAME, provisioner, defaultVersion(), EclipseJdtFormatterStep::apply) { - @Override - protected P2Model model(String version) { - var model = new P2Model(); - addPlatformRepo(model, version); - model.getInstall().add("org.eclipse.jdt.core"); - return model; - } - - @Override - public void setVersion(String version) { - if (version.endsWith(".0")) { - String newVersion = version.substring(0, version.length() - 2); - System.err.println("Recommend replacing '" + version + "' with '" + newVersion + "' for Eclipse JDT"); - version = newVersion; - } - super.setVersion(version); - } - }; + public static EclipseJdtFormatterStep.Builder createBuilder(Provisioner provisioner) { + return new EclipseJdtFormatterStep.Builder(NAME, provisioner, defaultVersion(), EclipseJdtFormatterStep::apply, ImmutableMap.builder()); } private static FormatterFunc apply(EquoBasedStepBuilder.State state) throws Exception { JVM_SUPPORT.assertFormatterSupported(state.getSemanticVersion()); Class formatterClazz = state.getJarState().getClassLoader().loadClass("com.diffplug.spotless.extra.glue.jdt.EclipseJdtFormatterStepImpl"); - var formatter = formatterClazz.getConstructor(Properties.class).newInstance(state.getPreferences()); + var formatter = formatterClazz.getConstructor(Properties.class, Map.class).newInstance(state.getPreferences(), state.getStepProperties()); var method = formatterClazz.getMethod("format", String.class, File.class); FormatterFunc formatterFunc = (FormatterFunc.NeedsFile) (input, file) -> (String) method.invoke(formatter, input, file); return JVM_SUPPORT.suggestLaterVersionOnError(state.getSemanticVersion(), formatterFunc); } + + public static class Builder extends EquoBasedStepBuilder { + private final ImmutableMap.Builder stepProperties; + + Builder( + String formatterName, + Provisioner mavenProvisioner, + String defaultVersion, + SerializedFunction stateToFormatter, + ImmutableMap.Builder stepProperties) { + super(formatterName, mavenProvisioner, defaultVersion, stateToFormatter, stepProperties); + this.stepProperties = stepProperties; + } + + @Override + protected P2Model model(String version) { + var model = new P2Model(); + addPlatformRepo(model, version); + model.getInstall().add("org.eclipse.jdt.core"); + return model; + } + + public void setMembersOrdering(String order, boolean doNotSortFields) { + stepProperties.put("members.order.enabled", "true"); + stepProperties.put("members.order", order); + stepProperties.put("members.doNotSortFields", Boolean.toString(doNotSortFields)); + } + + public void setVisibilityOrdering(String order) { + stepProperties.put("visibility.order.enabled", "true"); + stepProperties.put("visibility.order", order); + } + + @Override + public void setVersion(String version) { + if (version.endsWith(".0")) { + String newVersion = version.substring(0, version.length() - 2); + System.err.println("Recommend replacing '" + version + "' with '" + newVersion + "' for Eclipse JDT"); + version = newVersion; + } + super.setVersion(version); + } + } } 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 21ec889ed6..ff328e1ab6 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 @@ -33,6 +33,34 @@ public void issue_1638() { EquoBasedStepBuilder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); builder.setPreferences(List.of(file)); StepHarness.forStep(builder.build()) - .testResource("java/eclipse/AbstractType.test", "java/eclipse/AbstractType.clean"); + .testResource("java/eclipse/AbstractType.test", "java/eclipse/AbstractType.clean"); + } + + @Test + public void sort_members_no_fields() { + ClassLoader classLoader = getClass().getClassLoader(); + EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); + builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", true); + StepHarness.forStep(builder.build()) + .testResource("java/eclipse/SortExample.test", "java/eclipse/SortExample.sortMembersNoFields.clean"); + } + + @Test + public void sort_members() { + ClassLoader classLoader = getClass().getClassLoader(); + EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); + builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", false); + StepHarness.forStep(builder.build()) + .testResource("java/eclipse/SortExample.test", "java/eclipse/SortExample.sortMembers.clean"); + } + + @Test + public void sort_members_and_by_visibility() { + ClassLoader classLoader = getClass().getClassLoader(); + EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); + builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", false); + builder.setVisibilityOrdering("B,R,D,V"); + StepHarness.forStep(builder.build()) + .testResource("java/eclipse/SortExample.test", "java/eclipse/SortExample.sortMembersByVisibility.clean"); } } diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java index db869dc4e6..223de370f9 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java @@ -282,7 +282,7 @@ private FormatterStep createStep() { } public EclipseConfig eclipse() { - return new EclipseConfig(EclipseJdtFormatterStep.defaultVersion()); + return eclipse(EclipseJdtFormatterStep.defaultVersion()); } public EclipseConfig eclipse(String version) { @@ -290,7 +290,7 @@ public EclipseConfig eclipse(String version) { } public class EclipseConfig { - private final EquoBasedStepBuilder builder; + private final EclipseJdtFormatterStep.Builder builder; EclipseConfig(String version) { builder = EclipseJdtFormatterStep.createBuilder(provisioner()); @@ -298,11 +298,31 @@ public class EclipseConfig { addStep(builder.build()); } - public void configFile(Object... configFiles) { + public EclipseConfig configFile(Object... configFiles) { requireElementsNonNull(configFiles); Project project = getProject(); builder.setPreferences(project.files(configFiles).getFiles()); replaceStep(builder.build()); + return this; + } + + public EclipseConfig sortMembers(String memberCategoryOrder, boolean doNotSortFields) { + requireElementsNonNull(memberCategoryOrder); + builder.setMembersOrdering(memberCategoryOrder, doNotSortFields); + replaceStep(builder.build()); + return this; + } + + public EclipseConfig sortMembers( + String memberCategoryOrder, + boolean doNotSortFields, + String visibilityOrder) { + requireElementsNonNull(memberCategoryOrder); + requireElementsNonNull(visibilityOrder); + builder.setMembersOrdering(memberCategoryOrder, doNotSortFields); + builder.setVisibilityOrdering(visibilityOrder); + replaceStep(builder.build()); + return this; } public EclipseConfig withP2Mirrors(Map mirrors) { diff --git a/testlib/src/main/resources/java/eclipse/SortExample.sortMembers.clean b/testlib/src/main/resources/java/eclipse/SortExample.sortMembers.clean new file mode 100644 index 0000000000..b129e6862e --- /dev/null +++ b/testlib/src/main/resources/java/eclipse/SortExample.sortMembers.clean @@ -0,0 +1,33 @@ +package com.diffplug.spotless.extra.glue.jdt; + +public class SortExample { + private static final int A; + public static final int Z; + static { + Z = 1; + A = 0; + } + final boolean _1 = false; + final int a = 1; + public final int b = 1; + private final int c = 1; + protected final int d = 1; + protected final int e = 1; + public final int f = 1; + final int z = 1; + class Nested { + public static final String B = "B"; + private static final String C = "C"; + protected static final String D = "D"; + void a() { + } + public void b() { + } + private void c() { + } + protected void d() { + } + void z() { + } + } +} diff --git a/testlib/src/main/resources/java/eclipse/SortExample.sortMembersByVisibility.clean b/testlib/src/main/resources/java/eclipse/SortExample.sortMembersByVisibility.clean new file mode 100644 index 0000000000..aebaacb3f1 --- /dev/null +++ b/testlib/src/main/resources/java/eclipse/SortExample.sortMembersByVisibility.clean @@ -0,0 +1,33 @@ +package com.diffplug.spotless.extra.glue.jdt; + +public class SortExample { + public static final int Z; + private static final int A; + static { + Z = 1; + A = 0; + } + public final int b = 1; + public final int f = 1; + protected final int d = 1; + protected final int e = 1; + final boolean _1 = false; + final int a = 1; + final int z = 1; + private final int c = 1; + class Nested { + public static final String B = "B"; + protected static final String D = "D"; + private static final String C = "C"; + public void b() { + } + protected void d() { + } + void a() { + } + void z() { + } + private void c() { + } + } +} diff --git a/testlib/src/main/resources/java/eclipse/SortExample.sortMembersNoFields.clean b/testlib/src/main/resources/java/eclipse/SortExample.sortMembersNoFields.clean new file mode 100644 index 0000000000..fdad821a6c --- /dev/null +++ b/testlib/src/main/resources/java/eclipse/SortExample.sortMembersNoFields.clean @@ -0,0 +1,33 @@ +package com.diffplug.spotless.extra.glue.jdt; + +public class SortExample { + public static final int Z; + private static final int A; + static { + Z = 1; + A = 0; + } + final int z = 1; + final int a = 1; + private final int c = 1; + protected final int e = 1; + protected final int d = 1; + public final int f = 1; + public final int b = 1; + final boolean _1 = false; + class Nested { + private static final String C = "C"; + protected static final String D = "D"; + public static final String B = "B"; + void a() { + } + public void b() { + } + private void c() { + } + protected void d() { + } + void z() { + } + } +} diff --git a/testlib/src/main/resources/java/eclipse/SortExample.test b/testlib/src/main/resources/java/eclipse/SortExample.test new file mode 100644 index 0000000000..88421f35f8 --- /dev/null +++ b/testlib/src/main/resources/java/eclipse/SortExample.test @@ -0,0 +1,28 @@ +package com.diffplug.spotless.extra.glue.jdt; + +public class SortExample { + final int z = 1; + final int a = 1; + private final int c = 1; + protected final int e = 1; + protected final int d = 1; + public final int f = 1; + public final int b = 1; + final boolean _1 = false; + class Nested { + void z() {} + private static final String C = "C"; + protected static final String D = "D"; + public static final String B = "B"; + void a() {} + private void c() {} + protected void d() {} + public void b() {} + } + public static final int Z; + private static final int A; + static { + Z = 1; + A = 0; + } +} From 91b8222c6b3e4acdaaf05f52fc2ac5f46aa9a1f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Fri, 25 Oct 2024 08:53:48 +0200 Subject: [PATCH 02/20] Update CHANGES.md --- plugin-gradle/CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index f175fc25cf..7ab9a6c86e 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -12,6 +12,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( * New `suppressLintsFor` DSL ([docs](https://github.com/diffplug/spotless/tree/main/plugin-gradle#linting)) ([#2307](https://github.com/diffplug/spotless/pull/2307)) * `ignoreErrorForStep` and `ignoreErrorForPath` are now deprecated aliases of `suppressLintsFor` * Spotless is still a formatter not a linter, it just models formatting failures as lints rather than stopping execution (resolves [#287](https://github.com/diffplug/spotless/issues/287)) +* Add _Sort Members_ feature based on Eclipse JDT implementation. ([#2312](https://github.com/diffplug/spotless/pull/2312)) ### Fixed * `ktlint` steps now read from the `string` instead of the `file` so they don't clobber earlier steps. (fixes [#1599](https://github.com/diffplug/spotless/issues/1599)) From 51f49889274bc9c3be969eb0c0ae3e8aa699fe15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Fri, 25 Oct 2024 08:54:48 +0200 Subject: [PATCH 03/20] Remove unused properties regarding Sort Members --- .../extra/glue/jdt/EclipseJdtSortMembers.java | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java index 0ceb3e8284..f15c63295e 100644 --- a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java +++ b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java @@ -19,7 +19,7 @@ public class EclipseJdtSortMembers { - private static CompilationUnit compilationUnit(String code, Map options, Map compilerOptions) { + private static CompilationUnit compilationUnit(String code) { return new CompilationUnit(null, null, null) { private final Buffer buffer = new Buffer(code); @@ -33,14 +33,14 @@ public JavaProject getJavaProject() { return new JavaProject(null, null) { @Override public Map getOptions(boolean inheritJavaCoreOptions) { - return compilerOptions; + return Map.of(); } }; } @Override public Map getOptions(boolean inheritJavaCoreOptions) { - return options; + return Map.of(); } @Override @@ -56,8 +56,12 @@ static String sortMember(String code, SortProperties properties) { } try { - CompilationUnit compilationUnit = compilationUnit(code, properties.compilationUnitOptions, properties.compilerOptions); - DefaultJavaElementComparator comparator = DefaultJavaElementComparator.of(properties.doNotSortFields, properties.membersOrder, properties.sortByVisibility, properties.visibilityOrder); + CompilationUnit compilationUnit = compilationUnit(code); + DefaultJavaElementComparator comparator = DefaultJavaElementComparator.of( + properties.doNotSortFields, + properties.membersOrder, + properties.sortByVisibility, + properties.visibilityOrder); new Sorter(AST.getJLSLatest(), compilationUnit, null, comparator).sort(); String content = compilationUnit.getBuffer().getContents(); if (content != null) { @@ -161,8 +165,6 @@ void sort() throws JavaModelException { } static class SortProperties { - final Map compilationUnitOptions; - final Map compilerOptions; final boolean doNotSortFields; final boolean enabled; final String membersOrder; @@ -174,17 +176,13 @@ static class SortProperties { String membersOrder, boolean doNotSortFields, boolean sortByVisibility, - String visibilityOrder, - Map compilationUnitOptions, - Map compilerOptions + String visibilityOrder ) { this.enabled = enabled; this.membersOrder = membersOrder; this.doNotSortFields = doNotSortFields; this.sortByVisibility = sortByVisibility; this.visibilityOrder = visibilityOrder; - this.compilationUnitOptions = compilationUnitOptions; - this.compilerOptions = compilerOptions; } static SortProperties from(Map properties) { @@ -196,7 +194,7 @@ static SortProperties from(Map properties) { // At the moment we see no need for the following options, but they may become important, idk. Map compilationUnitOptions = Map.of(); Map compilerOptions = Map.of(); - return new SortProperties(enabled, membersOrder, doNotSortFields, sortByVisibility, visibilityOrder, compilationUnitOptions, compilerOptions); + return new SortProperties(enabled, membersOrder, doNotSortFields, sortByVisibility, visibilityOrder); } } } From 893f42574cca04f78e35c9be72c74d2dc5909547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Fri, 25 Oct 2024 09:26:26 +0200 Subject: [PATCH 04/20] Apply spotless and document origin --- .../jdt/DefaultJavaElementComparator.java | 338 +++++++++--------- .../glue/jdt/EclipseJdtFormatterStepImpl.java | 10 +- .../extra/glue/jdt/EclipseJdtSortMembers.java | 61 ++-- .../spotless/extra/glue/jdt/JdtFlags.java | 28 +- .../spotless/extra/EquoBasedStepBuilder.java | 18 +- .../extra/java/EclipseJdtFormatterStep.java | 10 +- ...clipseJdtFormatterStepSpecialCaseTest.java | 10 +- .../gradle/spotless/JavaExtension.java | 7 +- 8 files changed, 261 insertions(+), 221 deletions(-) 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 c42e09f5fe..ad4dcc1a68 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 @@ -1,11 +1,24 @@ +/* + * Copyright 2024 DiffPlug + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.diffplug.spotless.extra.glue.jdt; import java.util.Comparator; import java.util.List; import java.util.StringTokenizer; -import org.eclipse.jdt.internal.corext.dom.ASTNodes; - import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; @@ -19,7 +32,11 @@ import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.util.CompilationUnitSorter; +import org.eclipse.jdt.internal.corext.dom.ASTNodes; +/** + * This class is derived and adapted code from the Eclipse JDT project (Derivative Works according to EPL 2.0 license). + */ class DefaultJavaElementComparator implements Comparator { static final int TYPE_INDEX = 0; @@ -32,6 +49,7 @@ class DefaultJavaElementComparator implements Comparator { static final int STATIC_METHODS_INDEX = 7; static final int ENUM_CONSTANTS_INDEX = 8; static final int N_CATEGORIES = 9; + static final int PUBLIC_INDEX = 0; static final int PRIVATE_INDEX = 1; static final int PROTECTED_INDEX = 2; @@ -44,10 +62,10 @@ class DefaultJavaElementComparator implements Comparator { private final int[] visibilityOffsets; static DefaultJavaElementComparator of( - boolean doNotSortFields, - String memberCategoryPreferences, - boolean sortByVisibility, - String visibilityPreferences) { + boolean doNotSortFields, + String memberCategoryPreferences, + boolean sortByVisibility, + String visibilityPreferences) { int[] memberCategoryOffsets = new int[9]; boolean success = fillMemberCategoryOffsets(memberCategoryPreferences, memberCategoryOffsets); @@ -67,10 +85,10 @@ static DefaultJavaElementComparator of( } DefaultJavaElementComparator( - boolean doNotSortFields, - int[] memberCategoryOffsets, - boolean sortByVisibility, - int[] visibilityOffsets) { + boolean doNotSortFields, + int[] memberCategoryOffsets, + boolean sortByVisibility, + int[] visibilityOffsets) { this.doNotSortFields = doNotSortFields; this.memberCategoryOffsets = memberCategoryOffsets; @@ -85,17 +103,17 @@ static boolean fillVisibilityOffsets(String preferencesString, int[] offsets) { String token = tokenizer.nextToken(); if (token != null) { switch (token) { - case "B": - offsets[PUBLIC_INDEX] = i++; - break; - case "D": - offsets[DEFAULT_INDEX] = i++; - break; - case "R": - offsets[PROTECTED_INDEX] = i++; - break; - case "V": - offsets[PRIVATE_INDEX] = i++; + case "B": + offsets[PUBLIC_INDEX] = i++; + break; + case "D": + offsets[DEFAULT_INDEX] = i++; + break; + case "R": + offsets[PROTECTED_INDEX] = i++; + break; + case "V": + offsets[PRIVATE_INDEX] = i++; } } } @@ -110,29 +128,29 @@ static boolean fillMemberCategoryOffsets(String preferencesString, int[] offsets String token = tokenizer.nextToken(); if (token != null) { switch (token) { - case "C": - offsets[CONSTRUCTORS_INDEX] = i++; - break; - case "F": - offsets[FIELDS_INDEX] = i++; - break; - case "I": - offsets[INIT_INDEX] = i++; - break; - case "M": - offsets[METHOD_INDEX] = i++; - break; - case "T": - offsets[TYPE_INDEX] = i++; - break; - case "SF": - offsets[STATIC_FIELDS_INDEX] = i++; - break; - case "SI": - offsets[STATIC_INIT_INDEX] = i++; - break; - case "SM": - offsets[STATIC_METHODS_INDEX] = i++; + case "C": + offsets[CONSTRUCTORS_INDEX] = i++; + break; + case "F": + offsets[FIELDS_INDEX] = i++; + break; + case "I": + offsets[INIT_INDEX] = i++; + break; + case "M": + offsets[METHOD_INDEX] = i++; + break; + case "T": + offsets[TYPE_INDEX] = i++; + break; + case "SF": + offsets[STATIC_FIELDS_INDEX] = i++; + break; + case "SI": + offsets[STATIC_INIT_INDEX] = i++; + break; + case "SM": + offsets[STATIC_METHODS_INDEX] = i++; } } } @@ -141,38 +159,38 @@ static boolean fillMemberCategoryOffsets(String preferencesString, int[] offsets private int category(BodyDeclaration bodyDeclaration) { switch (bodyDeclaration.getNodeType()) { - case ASTNode.METHOD_DECLARATION: { - MethodDeclaration method = (MethodDeclaration) bodyDeclaration; - if (method.isConstructor()) { - return CONSTRUCTORS_INDEX; - } - int flags = method.getModifiers(); - if (Modifier.isStatic(flags)) - return STATIC_METHODS_INDEX; - else - return METHOD_INDEX; + case ASTNode.METHOD_DECLARATION: { + MethodDeclaration method = (MethodDeclaration) bodyDeclaration; + if (method.isConstructor()) { + return CONSTRUCTORS_INDEX; } - case ASTNode.FIELD_DECLARATION: { - if (JdtFlags.isStatic(bodyDeclaration)) - return STATIC_FIELDS_INDEX; - else - return FIELDS_INDEX; - } - case ASTNode.INITIALIZER: { - int flags = bodyDeclaration.getModifiers(); - if (Modifier.isStatic(flags)) - return STATIC_INIT_INDEX; - else - return INIT_INDEX; - } - case ASTNode.TYPE_DECLARATION: - case ASTNode.ENUM_DECLARATION: - case ASTNode.ANNOTATION_TYPE_DECLARATION: - return TYPE_INDEX; - case ASTNode.ENUM_CONSTANT_DECLARATION: - return ENUM_CONSTANTS_INDEX; - case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION: - return METHOD_INDEX; // reusing the method index + int flags = method.getModifiers(); + if (Modifier.isStatic(flags)) + return STATIC_METHODS_INDEX; + else + return METHOD_INDEX; + } + case ASTNode.FIELD_DECLARATION: { + if (JdtFlags.isStatic(bodyDeclaration)) + return STATIC_FIELDS_INDEX; + else + return FIELDS_INDEX; + } + case ASTNode.INITIALIZER: { + int flags = bodyDeclaration.getModifiers(); + if (Modifier.isStatic(flags)) + return STATIC_INIT_INDEX; + else + return INIT_INDEX; + } + case ASTNode.TYPE_DECLARATION: + case ASTNode.ENUM_DECLARATION: + case ASTNode.ANNOTATION_TYPE_DECLARATION: + return TYPE_INDEX; + case ASTNode.ENUM_CONSTANT_DECLARATION: + return ENUM_CONSTANTS_INDEX; + case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION: + return METHOD_INDEX; // reusing the method index } return 0; // should never happen @@ -235,117 +253,117 @@ public int compare(BodyDeclaration bodyDeclaration1, BodyDeclaration bodyDeclara } switch (bodyDeclaration1.getNodeType()) { - case ASTNode.METHOD_DECLARATION: { - MethodDeclaration method1 = (MethodDeclaration) bodyDeclaration1; - MethodDeclaration method2 = (MethodDeclaration) bodyDeclaration2; - - if (sortByVisibility) { - int vis = getVisibilityIndex(method1.getModifiers()) - getVisibilityIndex(method2.getModifiers()); - if (vis != 0) { - return vis; - } + case ASTNode.METHOD_DECLARATION: { + MethodDeclaration method1 = (MethodDeclaration) bodyDeclaration1; + MethodDeclaration method2 = (MethodDeclaration) bodyDeclaration2; + + if (sortByVisibility) { + int vis = getVisibilityIndex(method1.getModifiers()) - getVisibilityIndex(method2.getModifiers()); + if (vis != 0) { + return vis; } + } - String name1 = method1.getName().getIdentifier(); - String name2 = method2.getName().getIdentifier(); + String name1 = method1.getName().getIdentifier(); + String name2 = method2.getName().getIdentifier(); - // method declarations (constructors) are sorted by name - int cmp = name1.compareTo(name2); + // method declarations (constructors) are sorted by name + int cmp = name1.compareTo(name2); + if (cmp != 0) { + return cmp; + } + + // if names equal, sort by parameter types + List parameters1 = method1.parameters(); + List parameters2 = method2.parameters(); + int length1 = parameters1.size(); + int length2 = parameters2.size(); + + int len = Math.min(length1, length2); + 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())); if (cmp != 0) { return cmp; } - - // if names equal, sort by parameter types - List parameters1 = method1.parameters(); - List parameters2 = method2.parameters(); - int length1 = parameters1.size(); - int length2 = parameters2.size(); - - int len = Math.min(length1, length2); - 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())); - if (cmp != 0) { - return cmp; - } - } - if (length1 != length2) { - return length1 - length2; - } - return preserveRelativeOrder(bodyDeclaration1, bodyDeclaration2); } - case ASTNode.FIELD_DECLARATION: { - FieldDeclaration field1 = (FieldDeclaration) bodyDeclaration1; - FieldDeclaration field2 = (FieldDeclaration) bodyDeclaration2; + if (length1 != length2) { + return length1 - length2; + } + return preserveRelativeOrder(bodyDeclaration1, bodyDeclaration2); + } + case ASTNode.FIELD_DECLARATION: { + FieldDeclaration field1 = (FieldDeclaration) bodyDeclaration1; + FieldDeclaration field2 = (FieldDeclaration) bodyDeclaration2; - String name1 = ((VariableDeclarationFragment) field1.fragments().get(0)).getName().getIdentifier(); - String name2 = ((VariableDeclarationFragment) field2.fragments().get(0)).getName().getIdentifier(); + String name1 = ((VariableDeclarationFragment) field1.fragments().get(0)).getName().getIdentifier(); + String name2 = ((VariableDeclarationFragment) field2.fragments().get(0)).getName().getIdentifier(); - // field declarations are sorted by name - return compareNames(bodyDeclaration1, bodyDeclaration2, name1, name2); - } - case ASTNode.INITIALIZER: { - // preserve relative order - return preserveRelativeOrder(bodyDeclaration1, bodyDeclaration2); - } - case ASTNode.TYPE_DECLARATION: - case ASTNode.ENUM_DECLARATION: - case ASTNode.ANNOTATION_TYPE_DECLARATION: { - AbstractTypeDeclaration type1 = (AbstractTypeDeclaration) bodyDeclaration1; - AbstractTypeDeclaration type2 = (AbstractTypeDeclaration) bodyDeclaration2; + // field declarations are sorted by name + return compareNames(bodyDeclaration1, bodyDeclaration2, name1, name2); + } + case ASTNode.INITIALIZER: { + // preserve relative order + return preserveRelativeOrder(bodyDeclaration1, bodyDeclaration2); + } + case ASTNode.TYPE_DECLARATION: + case ASTNode.ENUM_DECLARATION: + case ASTNode.ANNOTATION_TYPE_DECLARATION: { + AbstractTypeDeclaration type1 = (AbstractTypeDeclaration) bodyDeclaration1; + AbstractTypeDeclaration type2 = (AbstractTypeDeclaration) bodyDeclaration2; - String name1 = type1.getName().getIdentifier(); - String name2 = type2.getName().getIdentifier(); + String name1 = type1.getName().getIdentifier(); + String name2 = type2.getName().getIdentifier(); - // typedeclarations are sorted by name - return compareNames(bodyDeclaration1, bodyDeclaration2, name1, name2); - } - case ASTNode.ENUM_CONSTANT_DECLARATION: { - EnumConstantDeclaration decl1 = (EnumConstantDeclaration) bodyDeclaration1; - EnumConstantDeclaration decl2 = (EnumConstantDeclaration) bodyDeclaration2; + // typedeclarations are sorted by name + return compareNames(bodyDeclaration1, bodyDeclaration2, name1, name2); + } + case ASTNode.ENUM_CONSTANT_DECLARATION: { + EnumConstantDeclaration decl1 = (EnumConstantDeclaration) bodyDeclaration1; + EnumConstantDeclaration decl2 = (EnumConstantDeclaration) bodyDeclaration2; - String name1 = decl1.getName().getIdentifier(); - String name2 = decl2.getName().getIdentifier(); + String name1 = decl1.getName().getIdentifier(); + String name2 = decl2.getName().getIdentifier(); - // enum constants declarations are sorted by name - return compareNames(bodyDeclaration1, bodyDeclaration2, name1, name2); - } - case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION: { - AnnotationTypeMemberDeclaration decl1 = (AnnotationTypeMemberDeclaration) bodyDeclaration1; - AnnotationTypeMemberDeclaration decl2 = (AnnotationTypeMemberDeclaration) bodyDeclaration2; + // enum constants declarations are sorted by name + return compareNames(bodyDeclaration1, bodyDeclaration2, name1, name2); + } + case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION: { + AnnotationTypeMemberDeclaration decl1 = (AnnotationTypeMemberDeclaration) bodyDeclaration1; + AnnotationTypeMemberDeclaration decl2 = (AnnotationTypeMemberDeclaration) bodyDeclaration2; - String name1 = decl1.getName().getIdentifier(); - String name2 = decl2.getName().getIdentifier(); + String name1 = decl1.getName().getIdentifier(); + String name2 = decl2.getName().getIdentifier(); - // enum constants declarations are sorted by name - return compareNames(bodyDeclaration1, bodyDeclaration2, name1, name2); - } + // enum constants declarations are sorted by name + return compareNames(bodyDeclaration1, bodyDeclaration2, name1, name2); + } } return 0; } private static int sortPreservedCategory(int category) { switch (category) { - case STATIC_FIELDS_INDEX: - case STATIC_INIT_INDEX: - return STATIC_FIELDS_INDEX; - case FIELDS_INDEX: - case INIT_INDEX: - return FIELDS_INDEX; - default: - return category; + case STATIC_FIELDS_INDEX: + case STATIC_INIT_INDEX: + return STATIC_FIELDS_INDEX; + case FIELDS_INDEX: + case INIT_INDEX: + return FIELDS_INDEX; + default: + return category; } } private boolean isSortPreserved(BodyDeclaration bodyDeclaration) { switch (bodyDeclaration.getNodeType()) { - case ASTNode.FIELD_DECLARATION: - case ASTNode.ENUM_CONSTANT_DECLARATION: - case ASTNode.INITIALIZER: - return true; - default: - return false; + case ASTNode.FIELD_DECLARATION: + case ASTNode.ENUM_CONSTANT_DECLARATION: + case ASTNode.INITIALIZER: + return true; + default: + return false; } } diff --git a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtFormatterStepImpl.java b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtFormatterStepImpl.java index 76228a4011..39a493e63b 100644 --- a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtFormatterStepImpl.java +++ b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtFormatterStepImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 DiffPlug + * Copyright 2016-2024 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,10 +38,10 @@ public class EclipseJdtFormatterStepImpl { public EclipseJdtFormatterStepImpl(Properties formatterSettings, Map sortProperties) { Map options = formatterSettings.entrySet().stream().collect(Collectors.toMap( - e -> String.valueOf(e.getKey()), - e -> String.valueOf(e.getValue()), - (prev, next) -> next, - HashMap::new)); + e -> String.valueOf(e.getKey()), + e -> String.valueOf(e.getValue()), + (prev, next) -> next, + HashMap::new)); this.codeFormatter = new DefaultCodeFormatter(options); this.sortProperties = EclipseJdtSortMembers.SortProperties.from(sortProperties); } diff --git a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java index f15c63295e..d42b518be4 100644 --- a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java +++ b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java @@ -1,3 +1,18 @@ +/* + * Copyright 2024 DiffPlug + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.diffplug.spotless.extra.glue.jdt; import java.util.Comparator; @@ -58,10 +73,10 @@ static String sortMember(String code, SortProperties properties) { try { CompilationUnit compilationUnit = compilationUnit(code); DefaultJavaElementComparator comparator = DefaultJavaElementComparator.of( - properties.doNotSortFields, - properties.membersOrder, - properties.sortByVisibility, - properties.visibilityOrder); + properties.doNotSortFields, + properties.membersOrder, + properties.sortByVisibility, + properties.visibilityOrder); new Sorter(AST.getJLSLatest(), compilationUnit, null, comparator).sort(); String content = compilationUnit.getBuffer().getContents(); if (content != null) { @@ -81,17 +96,13 @@ private static class Buffer implements IBuffer { this.contents = contents; } - public void addBufferChangedListener(IBufferChangedListener listener) { - } + public void addBufferChangedListener(IBufferChangedListener listener) {} - public void append(char[] text) { - } + public void append(char[] text) {} - public void append(String text) { - } + public void append(String text) {} - public void close() { - } + public void close() {} public char getChar(int position) { return '\u0000'; @@ -133,20 +144,15 @@ public boolean isReadOnly() { return true; } - public void removeBufferChangedListener(IBufferChangedListener listener) { - } + public void removeBufferChangedListener(IBufferChangedListener listener) {} - public void replace(int position, int length, char[] text) { - } + public void replace(int position, int length, char[] text) {} - public void replace(int position, int length, String text) { - } + public void replace(int position, int length, String text) {} - public void save(IProgressMonitor progress, boolean force) { - } + public void save(IProgressMonitor progress, boolean force) {} - public void setContents(char[] contents) { - } + public void setContents(char[] contents) {} public void setContents(String contents) { this.contents = contents; @@ -172,12 +178,11 @@ static class SortProperties { final String visibilityOrder; SortProperties( - boolean enabled, - String membersOrder, - boolean doNotSortFields, - boolean sortByVisibility, - String visibilityOrder - ) { + boolean enabled, + String membersOrder, + boolean doNotSortFields, + boolean sortByVisibility, + String visibilityOrder) { this.enabled = enabled; this.membersOrder = membersOrder; this.doNotSortFields = doNotSortFields; diff --git a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/JdtFlags.java b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/JdtFlags.java index 032c7f1d84..819168013f 100644 --- a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/JdtFlags.java +++ b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/JdtFlags.java @@ -1,3 +1,18 @@ +/* + * Copyright 2024 DiffPlug + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.diffplug.spotless.extra.glue.jdt; import org.eclipse.core.runtime.Assert; @@ -10,14 +25,17 @@ import org.eclipse.jdt.core.dom.Modifier; import org.eclipse.jdt.core.dom.TypeDeclaration; +/** + * This class is derived and adapted code from the Eclipse JDT project (Derivative Works according to EPL 2.0 license). + */ class JdtFlags { - static final int VISIBILITY_CODE_INVALID= -1; + static final int VISIBILITY_CODE_INVALID = -1; static boolean isStatic(BodyDeclaration bodyDeclaration) { if (isNestedInterfaceOrAnnotation(bodyDeclaration)) return true; - int nodeType= bodyDeclaration.getNodeType(); + int nodeType = bodyDeclaration.getNodeType(); if (nodeType != ASTNode.METHOD_DECLARATION && nodeType != ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION && isInterfaceOrAnnotationMember(bodyDeclaration)) @@ -30,7 +48,7 @@ && isInterfaceOrAnnotationMember(bodyDeclaration)) } private static boolean isPackageVisible(BodyDeclaration bodyDeclaration) { - return (! isPrivate(bodyDeclaration) && ! isProtected(bodyDeclaration) && ! isPublic(bodyDeclaration)); + return (!isPrivate(bodyDeclaration) && !isProtected(bodyDeclaration) && !isPublic(bodyDeclaration)); } private static boolean isPrivate(BodyDeclaration bodyDeclaration) { @@ -52,8 +70,8 @@ private static boolean isInterfaceOrAnnotationMember(BodyDeclaration bodyDeclara } private static boolean isInterfaceOrAnnotation(ASTNode node) { - boolean isInterface= (node instanceof TypeDeclaration) && ((TypeDeclaration) node).isInterface(); - boolean isAnnotation= node instanceof AnnotationTypeDeclaration; + boolean isInterface = (node instanceof TypeDeclaration) && ((TypeDeclaration) node).isInterface(); + boolean isAnnotation = node instanceof AnnotationTypeDeclaration; return isInterface || isAnnotation; } diff --git a/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java b/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java index 293797578d..0ac526338c 100644 --- a/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java +++ b/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java @@ -59,11 +59,11 @@ public abstract class EquoBasedStepBuilder { /** Initialize valid default configuration, taking latest version */ public EquoBasedStepBuilder( - String formatterName, - Provisioner mavenProvisioner, - @Nullable String defaultVersion, - SerializedFunction stateToFormatter, - ImmutableMap.Builder stepProperties) { + String formatterName, + Provisioner mavenProvisioner, + @Nullable String defaultVersion, + SerializedFunction stateToFormatter, + ImmutableMap.Builder stepProperties) { this.formatterName = formatterName; this.mavenProvisioner = mavenProvisioner; @@ -169,10 +169,10 @@ static class EquoStep implements Serializable { private final ImmutableMap stepProperties; EquoStep( - String semanticVersion, - FileSignature.Promised settingsPromise, - JarState.Promised jarPromise, - ImmutableMap stepProperties) { + String semanticVersion, + FileSignature.Promised settingsPromise, + JarState.Promised jarPromise, + ImmutableMap stepProperties) { this.semanticVersion = semanticVersion; this.settingsPromise = settingsPromise; diff --git a/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java b/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java index 9dc463b49f..aeb20d27ee 100644 --- a/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java +++ b/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java @@ -57,11 +57,11 @@ public static class Builder extends EquoBasedStepBuilder { private final ImmutableMap.Builder stepProperties; Builder( - String formatterName, - Provisioner mavenProvisioner, - String defaultVersion, - SerializedFunction stateToFormatter, - ImmutableMap.Builder stepProperties) { + String formatterName, + Provisioner mavenProvisioner, + String defaultVersion, + SerializedFunction stateToFormatter, + ImmutableMap.Builder stepProperties) { super(formatterName, mavenProvisioner, defaultVersion, stateToFormatter, stepProperties); this.stepProperties = stepProperties; } 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 ff328e1ab6..f37bfe9e74 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 @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 DiffPlug + * Copyright 2016-2024 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,7 @@ public void issue_1638() { EquoBasedStepBuilder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); builder.setPreferences(List.of(file)); StepHarness.forStep(builder.build()) - .testResource("java/eclipse/AbstractType.test", "java/eclipse/AbstractType.clean"); + .testResource("java/eclipse/AbstractType.test", "java/eclipse/AbstractType.clean"); } @Test @@ -42,7 +42,7 @@ public void sort_members_no_fields() { EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", true); StepHarness.forStep(builder.build()) - .testResource("java/eclipse/SortExample.test", "java/eclipse/SortExample.sortMembersNoFields.clean"); + .testResource("java/eclipse/SortExample.test", "java/eclipse/SortExample.sortMembersNoFields.clean"); } @Test @@ -51,7 +51,7 @@ public void sort_members() { EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", false); StepHarness.forStep(builder.build()) - .testResource("java/eclipse/SortExample.test", "java/eclipse/SortExample.sortMembers.clean"); + .testResource("java/eclipse/SortExample.test", "java/eclipse/SortExample.sortMembers.clean"); } @Test @@ -61,6 +61,6 @@ public void sort_members_and_by_visibility() { builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", false); builder.setVisibilityOrdering("B,R,D,V"); StepHarness.forStep(builder.build()) - .testResource("java/eclipse/SortExample.test", "java/eclipse/SortExample.sortMembersByVisibility.clean"); + .testResource("java/eclipse/SortExample.test", "java/eclipse/SortExample.sortMembersByVisibility.clean"); } } diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java index 223de370f9..f098486fd6 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java @@ -33,7 +33,6 @@ import org.gradle.api.tasks.SourceSet; import com.diffplug.spotless.FormatterStep; -import com.diffplug.spotless.extra.EquoBasedStepBuilder; import com.diffplug.spotless.extra.java.EclipseJdtFormatterStep; import com.diffplug.spotless.generic.LicenseHeaderStep; import com.diffplug.spotless.java.CleanthatJavaStep; @@ -314,9 +313,9 @@ public EclipseConfig sortMembers(String memberCategoryOrder, boolean doNotSortFi } public EclipseConfig sortMembers( - String memberCategoryOrder, - boolean doNotSortFields, - String visibilityOrder) { + String memberCategoryOrder, + boolean doNotSortFields, + String visibilityOrder) { requireElementsNonNull(memberCategoryOrder); requireElementsNonNull(visibilityOrder); builder.setMembersOrdering(memberCategoryOrder, doNotSortFields); From 8aaf0e933c9509e9f601df79638c0fa3a8dab47b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Fri, 25 Oct 2024 17:00:25 +0200 Subject: [PATCH 05/20] Remove unused test code --- .../extra/java/EclipseJdtFormatterStepSpecialCaseTest.java | 3 --- 1 file changed, 3 deletions(-) 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 f37bfe9e74..20c11f6010 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 @@ -38,7 +38,6 @@ public void issue_1638() { @Test public void sort_members_no_fields() { - ClassLoader classLoader = getClass().getClassLoader(); EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", true); StepHarness.forStep(builder.build()) @@ -47,7 +46,6 @@ public void sort_members_no_fields() { @Test public void sort_members() { - ClassLoader classLoader = getClass().getClassLoader(); EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", false); StepHarness.forStep(builder.build()) @@ -56,7 +54,6 @@ public void sort_members() { @Test public void sort_members_and_by_visibility() { - ClassLoader classLoader = getClass().getClassLoader(); EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", false); builder.setVisibilityOrdering("B,R,D,V"); From d41e7af0008f3acbf5ceb877ae1a5910b6e3b656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Fri, 25 Oct 2024 19:56:32 +0200 Subject: [PATCH 06/20] Remove unnecessary modifiers from test class --- .../java/EclipseJdtFormatterStepSpecialCaseTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 20c11f6010..572fe1fc7e 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 @@ -24,10 +24,10 @@ import com.diffplug.spotless.TestProvisioner; import com.diffplug.spotless.extra.EquoBasedStepBuilder; -public class EclipseJdtFormatterStepSpecialCaseTest { +class EclipseJdtFormatterStepSpecialCaseTest { /** https://github.com/diffplug/spotless/issues/1638 */ @Test - public void issue_1638() { + void issue_1638() { ClassLoader classLoader = getClass().getClassLoader(); File file = new File(classLoader.getResource("eclipse_formatter_issue_1638.xml").getFile()); EquoBasedStepBuilder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); @@ -37,7 +37,7 @@ public void issue_1638() { } @Test - public void sort_members_no_fields() { + void sort_members_no_fields() { EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", true); StepHarness.forStep(builder.build()) @@ -45,7 +45,7 @@ public void sort_members_no_fields() { } @Test - public void sort_members() { + void sort_members() { EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", false); StepHarness.forStep(builder.build()) @@ -53,7 +53,7 @@ public void sort_members() { } @Test - public void sort_members_and_by_visibility() { + void sort_members_by_visibility() { EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", false); builder.setVisibilityOrdering("B,R,D,V"); From 17b3e1417011d6138c0076b39caf683fdd758c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Fri, 25 Oct 2024 20:17:28 +0200 Subject: [PATCH 07/20] Simplify `ASTNode` rendering to String --- lib-extra/build.gradle | 1 - .../extra/glue/jdt/DefaultJavaElementComparator.java | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib-extra/build.gradle b/lib-extra/build.gradle index 7bb686dac5..d7e5a7ab2e 100644 --- a/lib-extra/build.gradle +++ b/lib-extra/build.gradle @@ -87,7 +87,6 @@ p2deps { into 'jdtCompileOnly', { p2repo 'https://download.eclipse.org/eclipse/updates/4.26/' install 'org.eclipse.jdt.core' - install 'org.eclipse.jdt.core.manipulation' } } 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 ad4dcc1a68..529b8265a1 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 @@ -32,7 +32,7 @@ import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.util.CompilationUnitSorter; -import org.eclipse.jdt.internal.corext.dom.ASTNodes; +import org.eclipse.jdt.internal.core.dom.NaiveASTFlattener; /** * This class is derived and adapted code from the Eclipse JDT project (Derivative Works according to EPL 2.0 license). @@ -382,6 +382,8 @@ private int compareNames(BodyDeclaration bodyDeclaration1, BodyDeclaration bodyD } private String buildSignature(Type type) { - return ASTNodes.asString(type); + NaiveASTFlattener flattener = new NaiveASTFlattener(); + type.accept(flattener); + return flattener.getResult(); } } From 7856b0fbd3920dc048aea6bf6d693e1a27e0cc49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Fri, 25 Oct 2024 20:21:33 +0200 Subject: [PATCH 08/20] Add more member types to Sort Members test cases --- .../java/eclipse/SortExample.sortMembers.clean | 18 +++++++++++++++++- .../SortExample.sortMembersByVisibility.clean | 18 +++++++++++++++++- .../SortExample.sortMembersNoFields.clean | 18 +++++++++++++++++- .../resources/java/eclipse/SortExample.test | 11 ++++++++++- 4 files changed, 61 insertions(+), 4 deletions(-) diff --git a/testlib/src/main/resources/java/eclipse/SortExample.sortMembers.clean b/testlib/src/main/resources/java/eclipse/SortExample.sortMembers.clean index b129e6862e..e6f3c1dc0e 100644 --- a/testlib/src/main/resources/java/eclipse/SortExample.sortMembers.clean +++ b/testlib/src/main/resources/java/eclipse/SortExample.sortMembers.clean @@ -2,11 +2,21 @@ package com.diffplug.spotless.extra.glue.jdt; public class SortExample { private static final int A; + static final int B = 1; + protected static final int C = 2; public static final int Z; static { - Z = 1; + Z = 9; A = 0; } + static void s1() { + } + private static void s2() { + } + public static void s3() { + } + protected static void s4() { + } final boolean _1 = false; final int a = 1; public final int b = 1; @@ -15,6 +25,12 @@ public class SortExample { protected final int e = 1; public final int f = 1; final int z = 1; + SortExample() { + } + SortExample(int a) { + } + SortExample(int b, int c) { + } class Nested { public static final String B = "B"; private static final String C = "C"; diff --git a/testlib/src/main/resources/java/eclipse/SortExample.sortMembersByVisibility.clean b/testlib/src/main/resources/java/eclipse/SortExample.sortMembersByVisibility.clean index aebaacb3f1..e5d1528ba1 100644 --- a/testlib/src/main/resources/java/eclipse/SortExample.sortMembersByVisibility.clean +++ b/testlib/src/main/resources/java/eclipse/SortExample.sortMembersByVisibility.clean @@ -2,11 +2,21 @@ package com.diffplug.spotless.extra.glue.jdt; public class SortExample { public static final int Z; + protected static final int C = 2; + static final int B = 1; private static final int A; static { - Z = 1; + Z = 9; A = 0; } + public static void s3() { + } + protected static void s4() { + } + static void s1() { + } + private static void s2() { + } public final int b = 1; public final int f = 1; protected final int d = 1; @@ -15,6 +25,12 @@ public class SortExample { final int a = 1; final int z = 1; private final int c = 1; + SortExample() { + } + SortExample(int a) { + } + SortExample(int b, int c) { + } class Nested { public static final String B = "B"; protected static final String D = "D"; diff --git a/testlib/src/main/resources/java/eclipse/SortExample.sortMembersNoFields.clean b/testlib/src/main/resources/java/eclipse/SortExample.sortMembersNoFields.clean index fdad821a6c..499f0ef413 100644 --- a/testlib/src/main/resources/java/eclipse/SortExample.sortMembersNoFields.clean +++ b/testlib/src/main/resources/java/eclipse/SortExample.sortMembersNoFields.clean @@ -3,10 +3,20 @@ package com.diffplug.spotless.extra.glue.jdt; public class SortExample { public static final int Z; private static final int A; + static final int B = 1; + protected static final int C = 2; static { - Z = 1; + Z = 9; A = 0; } + static void s1() { + } + private static void s2() { + } + public static void s3() { + } + protected static void s4() { + } final int z = 1; final int a = 1; private final int c = 1; @@ -15,6 +25,12 @@ public class SortExample { public final int f = 1; public final int b = 1; final boolean _1 = false; + SortExample() { + } + SortExample(int a) { + } + SortExample(int b, int c) { + } class Nested { private static final String C = "C"; protected static final String D = "D"; diff --git a/testlib/src/main/resources/java/eclipse/SortExample.test b/testlib/src/main/resources/java/eclipse/SortExample.test index 88421f35f8..421248e7b5 100644 --- a/testlib/src/main/resources/java/eclipse/SortExample.test +++ b/testlib/src/main/resources/java/eclipse/SortExample.test @@ -9,6 +9,13 @@ public class SortExample { public final int f = 1; public final int b = 1; final boolean _1 = false; + static void s1() {} + private static void s2() {} + public static void s3() {} + protected static void s4() {} + SortExample(int b, int c) {} + SortExample(int a) {} + SortExample() {} class Nested { void z() {} private static final String C = "C"; @@ -21,8 +28,10 @@ public class SortExample { } public static final int Z; private static final int A; + static final int B =1; + protected static final int C= 2; static { - Z = 1; + Z = 9; A = 0; } } From bc47f37a8b1313ea5864c92551307027b5d0e07f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Fri, 25 Oct 2024 20:56:50 +0200 Subject: [PATCH 09/20] Update README.md --- plugin-gradle/README.md | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index 90d7d6ef56..bfa17ebcf8 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -264,10 +264,31 @@ spotless { eclipse('4.26').configFile('eclipse-prefs.xml') // if the access to the p2 repositories is restricted, mirrors can be // specified using a URI prefix map as follows: - eclipse().withP2Mirrors(['https://download.eclipse.org/eclipse/updates/4.29/':'https://some.internal.mirror/4-29-updates-p2/']) - + eclipse().withP2Mirrors(['https://download.eclipse.org/eclipse/updates/4.29/':'https://some.internal.mirror/4-29-updates-p2/']) ``` +Not only can you format your code with Eclipse JDT, but you can also sort the members as you know it from Eclipse IDE. +This ensures that the methods are always in sorted order (and thus reduces the likelihood of collisions in a version +control system). It is turned off by default, but you might want to consider enabling it when setting coding standards +for the rest of the team. + +The format to specify the sort order follows the `outlinesortoption` and `org.eclipse.jdt.ui.visibility.order` +properties that can be found in the workspace folder of your Eclipse IDE (look up the +file `.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs` in your workspace directory). + +```gradle +spotless { + java { + // specify the sort order of the member categories + // SF,SI,SM,F,I,C,M,T = Static Fields, Static Initializers, Static Methods, Fields, Initializers, Constructors, Methods, (Nested) Types + val memberCategoryOrder = "SF,SI,SM,F,I,C,M,T" + val doNotSortFields = true + eclipse().sortMembers(memberCategoryOrder, doNotSortFields) + // optional: specify ordering of members of the same category by the visibility within the category + // B,R,D,V = Public, Protected, Package, Private + val visibilityOrder = "B,R,D,V" + eclipse().sortMembers(membersSortOrder, doNotSortFields, visibilityOrder) +``` ### formatAnnotations From 12f47f7640e2b521926c2e165463008a1a2a0f76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Fri, 25 Oct 2024 21:04:48 +0200 Subject: [PATCH 10/20] Update CHANGES.md --- CHANGES.md | 1 + plugin-gradle/CHANGES.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 9e4d6a5de4..d1ead78c40 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Changed * Bump default `ktlint` version to latest `1.3.0` -> `1.4.0`. ([#2314](https://github.com/diffplug/spotless/pull/2314)) +* Add _Sort Members_ feature based on [Eclipse JDT](plugin-gradle/README.md#eclipse-jdt) implementation. ([#2312](https://github.com/diffplug/spotless/pull/2312)) ## [3.0.0.BETA4] - 2024-10-24 ### Added diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index 7ab9a6c86e..f1cdbe001b 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -12,7 +12,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( * New `suppressLintsFor` DSL ([docs](https://github.com/diffplug/spotless/tree/main/plugin-gradle#linting)) ([#2307](https://github.com/diffplug/spotless/pull/2307)) * `ignoreErrorForStep` and `ignoreErrorForPath` are now deprecated aliases of `suppressLintsFor` * Spotless is still a formatter not a linter, it just models formatting failures as lints rather than stopping execution (resolves [#287](https://github.com/diffplug/spotless/issues/287)) -* Add _Sort Members_ feature based on Eclipse JDT implementation. ([#2312](https://github.com/diffplug/spotless/pull/2312)) +* Add _Sort Members_ feature based on [Eclipse JDT](README.md#eclipse-jdt) implementation. ([#2312](https://github.com/diffplug/spotless/pull/2312)) ### Fixed * `ktlint` steps now read from the `string` instead of the `file` so they don't clobber earlier steps. (fixes [#1599](https://github.com/diffplug/spotless/issues/1599)) From 9ea2d8c1fab8e216d6051a053133b0c58d2a6092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Fri, 25 Oct 2024 21:09:24 +0200 Subject: [PATCH 11/20] Remove unused code --- .../spotless/extra/glue/jdt/EclipseJdtSortMembers.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java index d42b518be4..feeb904ec2 100644 --- a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java +++ b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java @@ -196,9 +196,6 @@ static SortProperties from(Map properties) { boolean doNotSortFields = Boolean.parseBoolean(properties.getOrDefault("members.doNotSortFields", "true")); boolean sortByVisibility = Boolean.parseBoolean(properties.getOrDefault("visibility.order.enabled", "false")); String visibilityOrder = properties.getOrDefault("visibility.order", ""); - // At the moment we see no need for the following options, but they may become important, idk. - Map compilationUnitOptions = Map.of(); - Map compilerOptions = Map.of(); return new SortProperties(enabled, membersOrder, doNotSortFields, sortByVisibility, visibilityOrder); } } From 231b67c95554d4a080653e7040eae97aa1a57186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Fri, 25 Oct 2024 21:29:55 +0200 Subject: [PATCH 12/20] Fix spotbugs issues --- .../jdt/DefaultJavaElementComparator.java | 84 +++++++++---------- .../extra/glue/jdt/EclipseJdtSortMembers.java | 74 ++++++++-------- .../extra/glue/jdt/SuppressFBWarnings.java | 26 ++++++ 3 files changed, 108 insertions(+), 76 deletions(-) create mode 100644 lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/SuppressFBWarnings.java 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 529b8265a1..78791e240b 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 @@ -37,6 +37,7 @@ /** * This class is derived and adapted code from the Eclipse JDT project (Derivative Works according to EPL 2.0 license). */ +@SuppressFBWarnings(value = "SE_COMPARATOR_SHOULD_BE_SERIALIZABLE", justification = "this comparator is not meant to be serialized") class DefaultJavaElementComparator implements Comparator { static final int TYPE_INDEX = 0; @@ -96,62 +97,60 @@ static DefaultJavaElementComparator of( this.visibilityOffsets = visibilityOffsets; } + @SuppressFBWarnings(value = "SF_SWITCH_NO_DEFAULT", justification = "we only accept valid tokens in the order string, otherwise we fall back to default value") static boolean fillVisibilityOffsets(String preferencesString, int[] offsets) { StringTokenizer tokenizer = new StringTokenizer(preferencesString, ","); int i = 0; while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); - if (token != null) { - switch (token) { - case "B": - offsets[PUBLIC_INDEX] = i++; - break; - case "D": - offsets[DEFAULT_INDEX] = i++; - break; - case "R": - offsets[PROTECTED_INDEX] = i++; - break; - case "V": - offsets[PRIVATE_INDEX] = i++; - } + switch (token) { + case "B": + offsets[PUBLIC_INDEX] = i++; + break; + case "D": + offsets[DEFAULT_INDEX] = i++; + break; + case "R": + offsets[PROTECTED_INDEX] = i++; + break; + case "V": + offsets[PRIVATE_INDEX] = i++; } } return i == N_VISIBILITIES; } - static boolean fillMemberCategoryOffsets(String preferencesString, int[] offsets) { - StringTokenizer tokenizer = new StringTokenizer(preferencesString, ","); + @SuppressFBWarnings(value = "SF_SWITCH_NO_DEFAULT", justification = "we only accept valid tokens in the order string, otherwise we fall back to default value") + static boolean fillMemberCategoryOffsets(String orderString, int[] offsets) { + StringTokenizer tokenizer = new StringTokenizer(orderString, ","); int i = 0; offsets[8] = i++; while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); - if (token != null) { - switch (token) { - case "C": - offsets[CONSTRUCTORS_INDEX] = i++; - break; - case "F": - offsets[FIELDS_INDEX] = i++; - break; - case "I": - offsets[INIT_INDEX] = i++; - break; - case "M": - offsets[METHOD_INDEX] = i++; - break; - case "T": - offsets[TYPE_INDEX] = i++; - break; - case "SF": - offsets[STATIC_FIELDS_INDEX] = i++; - break; - case "SI": - offsets[STATIC_INIT_INDEX] = i++; - break; - case "SM": - offsets[STATIC_METHODS_INDEX] = i++; - } + switch (token) { + case "C": + offsets[CONSTRUCTORS_INDEX] = i++; + break; + case "F": + offsets[FIELDS_INDEX] = i++; + break; + case "I": + offsets[INIT_INDEX] = i++; + break; + case "M": + offsets[METHOD_INDEX] = i++; + break; + case "T": + offsets[TYPE_INDEX] = i++; + break; + case "SF": + offsets[STATIC_FIELDS_INDEX] = i++; + break; + case "SI": + offsets[STATIC_INIT_INDEX] = i++; + break; + case "SM": + offsets[STATIC_METHODS_INDEX] = i++; } } return i == N_CATEGORIES; @@ -219,6 +218,7 @@ private int getVisibilityIndex(int modifierFlags) { * @see CompilationUnitSorter#sort(int, org.eclipse.jdt.core.ICompilationUnit, int[], java.util.Comparator, int, org.eclipse.core.runtime.IProgressMonitor) */ @Override + @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST", justification = "when switching to a more recent Java version, we can avoid those unconfirmed casts") public int compare(BodyDeclaration bodyDeclaration1, BodyDeclaration bodyDeclaration2) { boolean preserved1 = doNotSortFields && isSortPreserved(bodyDeclaration1); boolean preserved2 = doNotSortFields && isSortPreserved(bodyDeclaration2); diff --git a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java index feeb904ec2..9743724013 100644 --- a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java +++ b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java @@ -28,50 +28,17 @@ import org.eclipse.jdt.core.IOpenable; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.AST; -import org.eclipse.jdt.internal.core.CompilationUnit; -import org.eclipse.jdt.internal.core.JavaProject; import org.eclipse.jdt.internal.core.SortElementsOperation; public class EclipseJdtSortMembers { - private static CompilationUnit compilationUnit(String code) { - return new CompilationUnit(null, null, null) { - private final Buffer buffer = new Buffer(code); - - @Override - public IBuffer getBuffer() { - return buffer; - } - - @Override - public JavaProject getJavaProject() { - return new JavaProject(null, null) { - @Override - public Map getOptions(boolean inheritJavaCoreOptions) { - return Map.of(); - } - }; - } - - @Override - public Map getOptions(boolean inheritJavaCoreOptions) { - return Map.of(); - } - - @Override - public ICompilationUnit getPrimary() { - return this; - } - }; - } - static String sortMember(String code, SortProperties properties) { if (!properties.enabled) { return code; } try { - CompilationUnit compilationUnit = compilationUnit(code); + CompilationUnit compilationUnit = new CompilationUnit(code); DefaultJavaElementComparator comparator = DefaultJavaElementComparator.of( properties.doNotSortFields, properties.membersOrder, @@ -159,6 +126,45 @@ public void setContents(String contents) { } } + @SuppressFBWarnings(value = "EQ_DOESNT_OVERRIDE_EQUALS", justification = "the equals method shouldn't be called in the sort members use case") + private static class CompilationUnit extends org.eclipse.jdt.internal.core.CompilationUnit { + private final Buffer buffer; + + CompilationUnit(String code) { + super(null, null, null); + buffer = new Buffer(code); + } + + @Override + public IBuffer getBuffer() { + return buffer; + } + + @Override + public JavaProject getJavaProject() { + return JavaProject.INSTANCE; + } + + @Override + public Map getOptions(boolean inheritJavaCoreOptions) { + return Map.of(); + } + + @Override + public ICompilationUnit getPrimary() { + return this; + } + } + + private static class JavaProject extends org.eclipse.jdt.internal.core.JavaProject { + static final JavaProject INSTANCE = new JavaProject(); + + @Override + public Map getOptions(boolean inheritJavaCoreOptions) { + return Map.of(); + } + } + private static class Sorter extends SortElementsOperation { Sorter(int level, CompilationUnit compilationUnit, int[] positions, Comparator comparator) { diff --git a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/SuppressFBWarnings.java b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/SuppressFBWarnings.java new file mode 100644 index 0000000000..bfcad72a23 --- /dev/null +++ b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/SuppressFBWarnings.java @@ -0,0 +1,26 @@ +/* + * Copyright 2024 DiffPlug + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.diffplug.spotless.extra.glue.jdt; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.CLASS) +@interface SuppressFBWarnings { + String[] value() default {}; + + String justification() default ""; +} From cd045fbd8f9da516a6d0e56883b11c601523e3aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Fri, 25 Oct 2024 21:38:24 +0200 Subject: [PATCH 13/20] Update README.md --- plugin-gradle/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index bfa17ebcf8..d771075740 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -287,7 +287,7 @@ spotless { // optional: specify ordering of members of the same category by the visibility within the category // B,R,D,V = Public, Protected, Package, Private val visibilityOrder = "B,R,D,V" - eclipse().sortMembers(membersSortOrder, doNotSortFields, visibilityOrder) + eclipse().sortMembers(memberCategoryOrder, doNotSortFields, visibilityOrder) ``` ### formatAnnotations From 5977dbd9cb458fce7bd697b18a92f244052bc065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Fri, 25 Oct 2024 22:00:55 +0200 Subject: [PATCH 14/20] Fix test failures --- .../spotless/extra/glue/jdt/EclipseJdtSortMembers.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java index 9743724013..3c6fd49010 100644 --- a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java +++ b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java @@ -159,6 +159,10 @@ public ICompilationUnit getPrimary() { private static class JavaProject extends org.eclipse.jdt.internal.core.JavaProject { static final JavaProject INSTANCE = new JavaProject(); + JavaProject() { + super(null, null); + } + @Override public Map getOptions(boolean inheritJavaCoreOptions) { return Map.of(); From 8422374c368121642c86d3477fb490199d3b3a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Fri, 25 Oct 2024 22:02:53 +0200 Subject: [PATCH 15/20] Update README.md --- plugin-gradle/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index d771075740..5c481ffbf8 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -270,7 +270,7 @@ spotless { Not only can you format your code with Eclipse JDT, but you can also sort the members as you know it from Eclipse IDE. This ensures that the methods are always in sorted order (and thus reduces the likelihood of collisions in a version control system). It is turned off by default, but you might want to consider enabling it when setting coding standards -for the rest of the team. +for a project. The format to specify the sort order follows the `outlinesortoption` and `org.eclipse.jdt.ui.visibility.order` properties that can be found in the workspace folder of your Eclipse IDE (look up the From 9171810a5448e34feeb2c2189938111c7d789bb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Fri, 25 Oct 2024 22:03:50 +0200 Subject: [PATCH 16/20] Update README.md --- plugin-gradle/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index 5c481ffbf8..ee89335b3b 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -273,8 +273,8 @@ control system). It is turned off by default, but you might want to consider ena for a project. The format to specify the sort order follows the `outlinesortoption` and `org.eclipse.jdt.ui.visibility.order` -properties that can be found in the workspace folder of your Eclipse IDE (look up the -file `.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs` in your workspace directory). +properties that can be found in the workspace folder of your Eclipse IDE. Look up the +file `.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs` in your workspace directory. ```gradle spotless { From 8b8ae6eae8cc7ba865ccd2c3285feb643f0353c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Mon, 28 Oct 2024 13:05:22 +0100 Subject: [PATCH 17/20] Add local properties to enable/disable certain features of the Sort Members functionality --- .../extra/glue/jdt/EclipseJdtSortMembers.java | 47 ++++++++++++++--- ...clipseJdtFormatterStepSpecialCaseTest.java | 45 ++++++++++++++--- plugin-gradle/README.md | 11 ++++ .../SortExample.localDoNotSortFields.clean | 50 +++++++++++++++++++ .../SortExample.localDoNotSortFields.test | 38 ++++++++++++++ .../SortExample.localEnabledFalse.clean | 50 +++++++++++++++++++ .../SortExample.localEnabledFalse.test | 38 ++++++++++++++ .../SortExample.localEnabledTrue.clean | 50 +++++++++++++++++++ .../eclipse/SortExample.localEnabledTrue.test | 38 ++++++++++++++ .../SortExample.localSortByVisibility.clean | 50 +++++++++++++++++++ .../SortExample.localSortByVisibility.test | 38 ++++++++++++++ 11 files changed, 443 insertions(+), 12 deletions(-) create mode 100644 testlib/src/main/resources/java/eclipse/SortExample.localDoNotSortFields.clean create mode 100644 testlib/src/main/resources/java/eclipse/SortExample.localDoNotSortFields.test create mode 100644 testlib/src/main/resources/java/eclipse/SortExample.localEnabledFalse.clean create mode 100644 testlib/src/main/resources/java/eclipse/SortExample.localEnabledFalse.test create mode 100644 testlib/src/main/resources/java/eclipse/SortExample.localEnabledTrue.clean create mode 100644 testlib/src/main/resources/java/eclipse/SortExample.localEnabledTrue.test create mode 100644 testlib/src/main/resources/java/eclipse/SortExample.localSortByVisibility.clean create mode 100644 testlib/src/main/resources/java/eclipse/SortExample.localSortByVisibility.test diff --git a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java index 3c6fd49010..b3eecbf597 100644 --- a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java +++ b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java @@ -17,6 +17,9 @@ import java.util.Comparator; import java.util.Map; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; @@ -32,18 +35,41 @@ public class EclipseJdtSortMembers { - static String sortMember(String code, SortProperties properties) { - if (!properties.enabled) { + private static final Pattern PATTERN_DO_NOT_SORT_FIELDS = Pattern.compile("@SortMembers:doNotSortFields\\s*=\\s*(false|true)"); + private static final Pattern PATTERN_ENABLED = Pattern.compile("@SortMembers:enabled\\s*=\\s*(false|true)"); + private static final Pattern PATTERN_SORT_BY_VISIBILITY = Pattern.compile("@SortMembers:sortByVisibility\\s*=\\s*(false|true)"); + + static SortProperties localProperties(SortProperties globalProperties, String code) { + Optional localDoNotSortFields = testOverwriteProperty(PATTERN_DO_NOT_SORT_FIELDS, code); + Optional localEnabled = testOverwriteProperty(PATTERN_ENABLED, code); + Optional localSortByVisibility = testOverwriteProperty(PATTERN_SORT_BY_VISIBILITY, code); + if (localDoNotSortFields.isEmpty() && localEnabled.isEmpty() && localSortByVisibility.isEmpty()) { + return globalProperties; + } + boolean doNotSortFields = localDoNotSortFields.orElse(globalProperties.doNotSortFields); + boolean enabled = localEnabled.orElse(globalProperties.enabled); + boolean sortByVisibility = localSortByVisibility.orElse(globalProperties.sortByVisibility); + return new SortProperties( + enabled, + globalProperties.membersOrder, + doNotSortFields, + sortByVisibility, + globalProperties.visibilityOrder); + } + + static String sortMember(String code, SortProperties globalProperties) { + SortProperties localProperties = localProperties(globalProperties, code); + if (!localProperties.enabled) { return code; } try { CompilationUnit compilationUnit = new CompilationUnit(code); DefaultJavaElementComparator comparator = DefaultJavaElementComparator.of( - properties.doNotSortFields, - properties.membersOrder, - properties.sortByVisibility, - properties.visibilityOrder); + localProperties.doNotSortFields, + localProperties.membersOrder, + localProperties.sortByVisibility, + localProperties.visibilityOrder); new Sorter(AST.getJLSLatest(), compilationUnit, null, comparator).sort(); String content = compilationUnit.getBuffer().getContents(); if (content != null) { @@ -55,6 +81,15 @@ static String sortMember(String code, SortProperties properties) { return code; } + static Optional testOverwriteProperty(Pattern pattern, String code) { + Matcher matcher = pattern.matcher(code); + if (matcher.find()) { + String flag = matcher.group(1); + return Optional.of(Boolean.valueOf(flag)); + } + return Optional.empty(); + } + private static class Buffer implements IBuffer { private String contents; 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 572fe1fc7e..1cc116be3a 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 @@ -37,15 +37,16 @@ void issue_1638() { } @Test - void sort_members_no_fields() { + void sort_members_global_by_visibility() { EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); - builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", true); + builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", false); + builder.setVisibilityOrdering("B,R,D,V"); StepHarness.forStep(builder.build()) - .testResource("java/eclipse/SortExample.test", "java/eclipse/SortExample.sortMembersNoFields.clean"); + .testResource("java/eclipse/SortExample.test", "java/eclipse/SortExample.sortMembersByVisibility.clean"); } @Test - void sort_members() { + void sort_members_global_enabled() { EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", false); StepHarness.forStep(builder.build()) @@ -53,11 +54,43 @@ void sort_members() { } @Test - void sort_members_by_visibility() { + void sort_members_global_no_fields() { + EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); + builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", true); + StepHarness.forStep(builder.build()) + .testResource("java/eclipse/SortExample.test", "java/eclipse/SortExample.sortMembersNoFields.clean"); + } + + @Test + void sort_members_local_by_visibility() { EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", false); builder.setVisibilityOrdering("B,R,D,V"); StepHarness.forStep(builder.build()) - .testResource("java/eclipse/SortExample.test", "java/eclipse/SortExample.sortMembersByVisibility.clean"); + .testResource("java/eclipse/SortExample.localSortByVisibility.test", "java/eclipse/SortExample.localSortByVisibility.clean"); + } + + @Test + void sort_members_local_enabled_false() { + EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); + builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", false); + StepHarness.forStep(builder.build()) + .testResource("java/eclipse/SortExample.localEnabledFalse.test", "java/eclipse/SortExample.localEnabledFalse.clean"); + } + + @Test + void sort_members_local_no_fields() { + EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); + builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", false); + StepHarness.forStep(builder.build()) + .testResource("java/eclipse/SortExample.localDoNotSortFields.test", "java/eclipse/SortExample.localDoNotSortFields.clean"); + } + + + @Test + void sort_members_local_enabled_true() { + EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); + StepHarness.forStep(builder.build()) + .testResource("java/eclipse/SortExample.localEnabledTrue.test", "java/eclipse/SortExample.localEnabledTrue.clean"); } } diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index ee89335b3b..ef9db402fb 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -267,6 +267,8 @@ spotless { eclipse().withP2Mirrors(['https://download.eclipse.org/eclipse/updates/4.29/':'https://some.internal.mirror/4-29-updates-p2/']) ``` +#### Sort Members + Not only can you format your code with Eclipse JDT, but you can also sort the members as you know it from Eclipse IDE. This ensures that the methods are always in sorted order (and thus reduces the likelihood of collisions in a version control system). It is turned off by default, but you might want to consider enabling it when setting coding standards @@ -276,6 +278,8 @@ The format to specify the sort order follows the `outlinesortoption` and `org.ec properties that can be found in the workspace folder of your Eclipse IDE. Look up the file `.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs` in your workspace directory. +###### Define Sort Members settings on project level + ```gradle spotless { java { @@ -290,6 +294,13 @@ spotless { eclipse().sortMembers(memberCategoryOrder, doNotSortFields, visibilityOrder) ``` +###### Overwrite Sort Members settings on file level + +You can enable/disable the sort properties on file level by adding the following comments: +- `// @SortMembers:enabled=false` - disable the Sort Members feature for this file +- `// @SortMembers:doNotSortFields=true` - disable the sorting of static and instance fields +- `// @SortMembers:sortByVisibility=false` - don't sort members by its visibility modifier + ### formatAnnotations Type annotations should be on the same line as the type that they qualify. diff --git a/testlib/src/main/resources/java/eclipse/SortExample.localDoNotSortFields.clean b/testlib/src/main/resources/java/eclipse/SortExample.localDoNotSortFields.clean new file mode 100644 index 0000000000..d1000f3b19 --- /dev/null +++ b/testlib/src/main/resources/java/eclipse/SortExample.localDoNotSortFields.clean @@ -0,0 +1,50 @@ +package com.diffplug.spotless.extra.glue.jdt; + +// @SortMembers:doNotSortFields = true +public class SortExample { + public static final int Z; + private static final int A; + static final int B = 1; + protected static final int C = 2; + static { + Z = 9; + A = 0; + } + static void s1() { + } + private static void s2() { + } + public static void s3() { + } + protected static void s4() { + } + final int z = 1; + final int a = 1; + private final int c = 1; + protected final int e = 1; + protected final int d = 1; + public final int f = 1; + public final int b = 1; + final boolean _1 = false; + SortExample() { + } + SortExample(int a) { + } + SortExample(int b, int c) { + } + class Nested { + private static final String C = "C"; + protected static final String D = "D"; + public static final String B = "B"; + void a() { + } + public void b() { + } + private void c() { + } + protected void d() { + } + void z() { + } + } +} diff --git a/testlib/src/main/resources/java/eclipse/SortExample.localDoNotSortFields.test b/testlib/src/main/resources/java/eclipse/SortExample.localDoNotSortFields.test new file mode 100644 index 0000000000..9af5447b7f --- /dev/null +++ b/testlib/src/main/resources/java/eclipse/SortExample.localDoNotSortFields.test @@ -0,0 +1,38 @@ +package com.diffplug.spotless.extra.glue.jdt; + +// @SortMembers:doNotSortFields = true +public class SortExample { + final int z = 1; + final int a = 1; + private final int c = 1; + protected final int e = 1; + protected final int d = 1; + public final int f = 1; + public final int b = 1; + final boolean _1 = false; + static void s1() {} + private static void s2() {} + public static void s3() {} + protected static void s4() {} + SortExample(int b, int c) {} + SortExample(int a) {} + SortExample() {} + class Nested { + void z() {} + private static final String C = "C"; + protected static final String D = "D"; + public static final String B = "B"; + void a() {} + private void c() {} + protected void d() {} + public void b() {} + } + public static final int Z; + private static final int A; + static final int B =1; + protected static final int C= 2; + static { + Z = 9; + A = 0; + } +} diff --git a/testlib/src/main/resources/java/eclipse/SortExample.localEnabledFalse.clean b/testlib/src/main/resources/java/eclipse/SortExample.localEnabledFalse.clean new file mode 100644 index 0000000000..050e6b47e8 --- /dev/null +++ b/testlib/src/main/resources/java/eclipse/SortExample.localEnabledFalse.clean @@ -0,0 +1,50 @@ +package com.diffplug.spotless.extra.glue.jdt; + +// @SortMembers:enabled = false +public class SortExample { + final int z = 1; + final int a = 1; + private final int c = 1; + protected final int e = 1; + protected final int d = 1; + public final int f = 1; + public final int b = 1; + final boolean _1 = false; + static void s1() { + } + private static void s2() { + } + public static void s3() { + } + protected static void s4() { + } + SortExample(int b, int c) { + } + SortExample(int a) { + } + SortExample() { + } + class Nested { + void z() { + } + private static final String C = "C"; + protected static final String D = "D"; + public static final String B = "B"; + void a() { + } + private void c() { + } + protected void d() { + } + public void b() { + } + } + public static final int Z; + private static final int A; + static final int B = 1; + protected static final int C = 2; + static { + Z = 9; + A = 0; + } +} diff --git a/testlib/src/main/resources/java/eclipse/SortExample.localEnabledFalse.test b/testlib/src/main/resources/java/eclipse/SortExample.localEnabledFalse.test new file mode 100644 index 0000000000..9c544d3250 --- /dev/null +++ b/testlib/src/main/resources/java/eclipse/SortExample.localEnabledFalse.test @@ -0,0 +1,38 @@ +package com.diffplug.spotless.extra.glue.jdt; + +// @SortMembers:enabled = false +public class SortExample { + final int z = 1; + final int a = 1; + private final int c = 1; + protected final int e = 1; + protected final int d = 1; + public final int f = 1; + public final int b = 1; + final boolean _1 = false; + static void s1() {} + private static void s2() {} + public static void s3() {} + protected static void s4() {} + SortExample(int b, int c) {} + SortExample(int a) {} + SortExample() {} + class Nested { + void z() {} + private static final String C = "C"; + protected static final String D = "D"; + public static final String B = "B"; + void a() {} + private void c() {} + protected void d() {} + public void b() {} + } + public static final int Z; + private static final int A; + static final int B =1; + protected static final int C= 2; + static { + Z = 9; + A = 0; + } +} diff --git a/testlib/src/main/resources/java/eclipse/SortExample.localEnabledTrue.clean b/testlib/src/main/resources/java/eclipse/SortExample.localEnabledTrue.clean new file mode 100644 index 0000000000..565c5ca99e --- /dev/null +++ b/testlib/src/main/resources/java/eclipse/SortExample.localEnabledTrue.clean @@ -0,0 +1,50 @@ +package com.diffplug.spotless.extra.glue.jdt; + +// @SortMembers:enabled = true +public class SortExample { + class Nested { + private static final String C = "C"; + protected static final String D = "D"; + public static final String B = "B"; + void a() { + } + public void b() { + } + private void c() { + } + protected void d() { + } + void z() { + } + } + public static final int Z; + private static final int A; + static final int B = 1; + protected static final int C = 2; + static { + Z = 9; + A = 0; + } + static void s1() { + } + private static void s2() { + } + public static void s3() { + } + protected static void s4() { + } + final int z = 1; + final int a = 1; + private final int c = 1; + protected final int e = 1; + protected final int d = 1; + public final int f = 1; + public final int b = 1; + final boolean _1 = false; + SortExample() { + } + SortExample(int a) { + } + SortExample(int b, int c) { + } +} diff --git a/testlib/src/main/resources/java/eclipse/SortExample.localEnabledTrue.test b/testlib/src/main/resources/java/eclipse/SortExample.localEnabledTrue.test new file mode 100644 index 0000000000..40735750ee --- /dev/null +++ b/testlib/src/main/resources/java/eclipse/SortExample.localEnabledTrue.test @@ -0,0 +1,38 @@ +package com.diffplug.spotless.extra.glue.jdt; + +// @SortMembers:enabled = true +public class SortExample { + final int z = 1; + final int a = 1; + private final int c = 1; + protected final int e = 1; + protected final int d = 1; + public final int f = 1; + public final int b = 1; + final boolean _1 = false; + static void s1() {} + private static void s2() {} + public static void s3() {} + protected static void s4() {} + SortExample(int b, int c) {} + SortExample(int a) {} + SortExample() {} + class Nested { + void z() {} + private static final String C = "C"; + protected static final String D = "D"; + public static final String B = "B"; + void a() {} + private void c() {} + protected void d() {} + public void b() {} + } + public static final int Z; + private static final int A; + static final int B =1; + protected static final int C= 2; + static { + Z = 9; + A = 0; + } +} diff --git a/testlib/src/main/resources/java/eclipse/SortExample.localSortByVisibility.clean b/testlib/src/main/resources/java/eclipse/SortExample.localSortByVisibility.clean new file mode 100644 index 0000000000..6224e90517 --- /dev/null +++ b/testlib/src/main/resources/java/eclipse/SortExample.localSortByVisibility.clean @@ -0,0 +1,50 @@ +package com.diffplug.spotless.extra.glue.jdt; + +// @SortMembers:sortByVisibility = false +public class SortExample { + private static final int A; + static final int B = 1; + protected static final int C = 2; + public static final int Z; + static { + Z = 9; + A = 0; + } + static void s1() { + } + private static void s2() { + } + public static void s3() { + } + protected static void s4() { + } + final boolean _1 = false; + final int a = 1; + public final int b = 1; + private final int c = 1; + protected final int d = 1; + protected final int e = 1; + public final int f = 1; + final int z = 1; + SortExample() { + } + SortExample(int a) { + } + SortExample(int b, int c) { + } + class Nested { + public static final String B = "B"; + private static final String C = "C"; + protected static final String D = "D"; + void a() { + } + public void b() { + } + private void c() { + } + protected void d() { + } + void z() { + } + } +} diff --git a/testlib/src/main/resources/java/eclipse/SortExample.localSortByVisibility.test b/testlib/src/main/resources/java/eclipse/SortExample.localSortByVisibility.test new file mode 100644 index 0000000000..e3cfb77617 --- /dev/null +++ b/testlib/src/main/resources/java/eclipse/SortExample.localSortByVisibility.test @@ -0,0 +1,38 @@ +package com.diffplug.spotless.extra.glue.jdt; + +// @SortMembers:sortByVisibility = false +public class SortExample { + final int z = 1; + final int a = 1; + private final int c = 1; + protected final int e = 1; + protected final int d = 1; + public final int f = 1; + public final int b = 1; + final boolean _1 = false; + static void s1() {} + private static void s2() {} + public static void s3() {} + protected static void s4() {} + SortExample(int b, int c) {} + SortExample(int a) {} + SortExample() {} + class Nested { + void z() {} + private static final String C = "C"; + protected static final String D = "D"; + public static final String B = "B"; + void a() {} + private void c() {} + protected void d() {} + public void b() {} + } + public static final int Z; + private static final int A; + static final int B =1; + protected static final int C= 2; + static { + Z = 9; + A = 0; + } +} From 7a4dc775c0f85e2e1c948947af05a3b853cc3e04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Mon, 28 Oct 2024 16:02:42 +0100 Subject: [PATCH 18/20] Fix code formatting in `EclipseJdtFormatterStepSpecialCaseTest` --- .../extra/java/EclipseJdtFormatterStepSpecialCaseTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 1cc116be3a..16afa733c2 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 @@ -86,11 +86,10 @@ void sort_members_local_no_fields() { .testResource("java/eclipse/SortExample.localDoNotSortFields.test", "java/eclipse/SortExample.localDoNotSortFields.clean"); } - @Test void sort_members_local_enabled_true() { EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); StepHarness.forStep(builder.build()) - .testResource("java/eclipse/SortExample.localEnabledTrue.test", "java/eclipse/SortExample.localEnabledTrue.clean"); + .testResource("java/eclipse/SortExample.localEnabledTrue.test", "java/eclipse/SortExample.localEnabledTrue.clean"); } } From e0306b3a079284cf617622da628dd91e773227c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Mon, 28 Oct 2024 16:22:25 +0100 Subject: [PATCH 19/20] Rework step mapping of Eclipse JDT formatter --- .../extra/glue/jdt/EclipseJdtSortMembers.java | 10 ++--- .../extra/java/EclipseJdtFormatterStep.java | 32 +++++++++----- ...clipseJdtFormatterStepSpecialCaseTest.java | 30 +++++++++---- plugin-gradle/README.md | 26 ++++++----- .../gradle/spotless/JavaExtension.java | 35 ++++++++++----- plugin-maven/README.md | 43 ++++++++++++++++++- .../diffplug/spotless/maven/java/Eclipse.java | 36 +++++++++++++++- 7 files changed, 162 insertions(+), 50 deletions(-) diff --git a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java index b3eecbf597..05498b1caf 100644 --- a/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java +++ b/lib-extra/src/jdt/java/com/diffplug/spotless/extra/glue/jdt/EclipseJdtSortMembers.java @@ -236,11 +236,11 @@ static class SortProperties { } static SortProperties from(Map properties) { - boolean enabled = Boolean.parseBoolean(properties.getOrDefault("members.order.enabled", "false")); - String membersOrder = properties.getOrDefault("members.order", ""); - boolean doNotSortFields = Boolean.parseBoolean(properties.getOrDefault("members.doNotSortFields", "true")); - boolean sortByVisibility = Boolean.parseBoolean(properties.getOrDefault("visibility.order.enabled", "false")); - String visibilityOrder = properties.getOrDefault("visibility.order", ""); + boolean enabled = Boolean.parseBoolean(properties.getOrDefault("sp_cleanup.sort_members", "false")); + String membersOrder = properties.getOrDefault("outlinesortoption", ""); + boolean doNotSortFields = !Boolean.parseBoolean(properties.getOrDefault("sp_cleanup.sort_members_all", "false")); + boolean sortByVisibility = Boolean.parseBoolean(properties.getOrDefault("org.eclipse.jdt.ui.enable.visibility.order", "false")); + String visibilityOrder = properties.getOrDefault("org.eclipse.jdt.ui.visibility.order", ""); return new SortProperties(enabled, membersOrder, doNotSortFields, sortByVisibility, visibilityOrder); } } diff --git a/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java b/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java index aeb20d27ee..2e3e190773 100644 --- a/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java +++ b/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java @@ -74,17 +74,6 @@ protected P2Model model(String version) { return model; } - public void setMembersOrdering(String order, boolean doNotSortFields) { - stepProperties.put("members.order.enabled", "true"); - stepProperties.put("members.order", order); - stepProperties.put("members.doNotSortFields", Boolean.toString(doNotSortFields)); - } - - public void setVisibilityOrdering(String order) { - stepProperties.put("visibility.order.enabled", "true"); - stepProperties.put("visibility.order", order); - } - @Override public void setVersion(String version) { if (version.endsWith(".0")) { @@ -94,5 +83,26 @@ public void setVersion(String version) { } super.setVersion(version); } + + public void sortMembersDoNotSortFields(boolean doNotSortFields) { + boolean sortAllMembers = !doNotSortFields; + stepProperties.put("sp_cleanup.sort_members_all", String.valueOf(sortAllMembers)); + } + + public void sortMembersEnabled(boolean enabled) { + stepProperties.put("sp_cleanup.sort_members", String.valueOf(enabled)); + } + + public void sortMembersOrder(String order) { + stepProperties.put("outlinesortoption", order); + } + + public void sortMembersVisibilityOrder(String order) { + stepProperties.put("org.eclipse.jdt.ui.visibility.order", order); + } + + public void sortMembersVisibilityOrderEnabled(boolean enabled) { + stepProperties.put("org.eclipse.jdt.ui.enable.visibility.order", String.valueOf(enabled)); + } } } 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 16afa733c2..027e562291 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 @@ -39,8 +39,11 @@ void issue_1638() { @Test void sort_members_global_by_visibility() { EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); - builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", false); - builder.setVisibilityOrdering("B,R,D,V"); + builder.sortMembersEnabled(true); + builder.sortMembersOrder("SF,SI,SM,F,I,C,M,T"); + builder.sortMembersDoNotSortFields(false); + builder.sortMembersVisibilityOrderEnabled(true); + builder.sortMembersVisibilityOrder("B,R,D,V"); StepHarness.forStep(builder.build()) .testResource("java/eclipse/SortExample.test", "java/eclipse/SortExample.sortMembersByVisibility.clean"); } @@ -48,7 +51,9 @@ void sort_members_global_by_visibility() { @Test void sort_members_global_enabled() { EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); - builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", false); + builder.sortMembersEnabled(true); + builder.sortMembersOrder("SF,SI,SM,F,I,C,M,T"); + builder.sortMembersDoNotSortFields(false); StepHarness.forStep(builder.build()) .testResource("java/eclipse/SortExample.test", "java/eclipse/SortExample.sortMembers.clean"); } @@ -56,7 +61,9 @@ void sort_members_global_enabled() { @Test void sort_members_global_no_fields() { EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); - builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", true); + builder.sortMembersEnabled(true); + builder.sortMembersOrder("SF,SI,SM,F,I,C,M,T"); + builder.sortMembersDoNotSortFields(true); StepHarness.forStep(builder.build()) .testResource("java/eclipse/SortExample.test", "java/eclipse/SortExample.sortMembersNoFields.clean"); } @@ -64,8 +71,11 @@ void sort_members_global_no_fields() { @Test void sort_members_local_by_visibility() { EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); - builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", false); - builder.setVisibilityOrdering("B,R,D,V"); + builder.sortMembersEnabled(true); + builder.sortMembersOrder("SF,SI,SM,F,I,C,M,T"); + builder.sortMembersDoNotSortFields(false); + builder.sortMembersVisibilityOrderEnabled(true); + builder.sortMembersVisibilityOrder("B,R,D,V"); StepHarness.forStep(builder.build()) .testResource("java/eclipse/SortExample.localSortByVisibility.test", "java/eclipse/SortExample.localSortByVisibility.clean"); } @@ -73,7 +83,9 @@ void sort_members_local_by_visibility() { @Test void sort_members_local_enabled_false() { EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); - builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", false); + builder.sortMembersEnabled(true); + builder.sortMembersOrder("SF,SI,SM,F,I,C,M,T"); + builder.sortMembersDoNotSortFields(false); StepHarness.forStep(builder.build()) .testResource("java/eclipse/SortExample.localEnabledFalse.test", "java/eclipse/SortExample.localEnabledFalse.clean"); } @@ -81,7 +93,9 @@ void sort_members_local_enabled_false() { @Test void sort_members_local_no_fields() { EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral()); - builder.setMembersOrdering("SF,SI,SM,F,I,C,M,T", false); + builder.sortMembersEnabled(true); + builder.sortMembersOrder("SF,SI,SM,F,I,C,M,T"); + builder.sortMembersDoNotSortFields(false); StepHarness.forStep(builder.build()) .testResource("java/eclipse/SortExample.localDoNotSortFields.test", "java/eclipse/SortExample.localDoNotSortFields.clean"); } diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index ef9db402fb..2bfc70ecba 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -275,7 +275,7 @@ control system). It is turned off by default, but you might want to consider ena for a project. The format to specify the sort order follows the `outlinesortoption` and `org.eclipse.jdt.ui.visibility.order` -properties that can be found in the workspace folder of your Eclipse IDE. Look up the +properties that can be found in the workspace folder of your Eclipse IDE. You can look at the file `.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs` in your workspace directory. ###### Define Sort Members settings on project level @@ -283,20 +283,24 @@ file `.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs` in y ```gradle spotless { java { - // specify the sort order of the member categories - // SF,SI,SM,F,I,C,M,T = Static Fields, Static Initializers, Static Methods, Fields, Initializers, Constructors, Methods, (Nested) Types - val memberCategoryOrder = "SF,SI,SM,F,I,C,M,T" - val doNotSortFields = true - eclipse().sortMembers(memberCategoryOrder, doNotSortFields) - // optional: specify ordering of members of the same category by the visibility within the category - // B,R,D,V = Public, Protected, Package, Private - val visibilityOrder = "B,R,D,V" - eclipse().sortMembers(memberCategoryOrder, doNotSortFields, visibilityOrder) + eclipse() + // Optional: Enable the Sort Members feature globally. (default: false) + .sortMembersEnabled(true) + // Optional: Specify the sort order of the member categories. (default: T,SF,SI,SM,F,I,C,M) + // SF,SI,SM,F,I,C,M,T = Static Fields, Static Initializers, Static Methods, Fields, Initializers, Constructors, Methods, (Nested) Types + .sortMembersOrder("SF,SI,SM,F,I,C,M,T") + // Optional: Enable the reordering of fields, enum constants, and initializers. (default: true) + .sortMembersDoNotSortFields(false) + // Optional: Enable reordering of members of the same category by the visibility within the category. (default: false) + .sortMembersVisibilityOrderEnabled(true) + // Optional: Specify the ordering of members of the same category by the visibility within the category. (default: B,V,R,D) + // B,R,D,V = Public, Protected, Package, Private + .sortMembersVisibilityOrder("B,R,D,V") ``` ###### Overwrite Sort Members settings on file level -You can enable/disable the sort properties on file level by adding the following comments: +You can enable/disable the globally defined sort properties on file level by adding the following comments: - `// @SortMembers:enabled=false` - disable the Sort Members feature for this file - `// @SortMembers:doNotSortFields=true` - disable the sorting of static and instance fields - `// @SortMembers:sortByVisibility=false` - don't sort members by its visibility modifier diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java index f098486fd6..c7495ec396 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java @@ -305,21 +305,34 @@ public EclipseConfig configFile(Object... configFiles) { return this; } - public EclipseConfig sortMembers(String memberCategoryOrder, boolean doNotSortFields) { - requireElementsNonNull(memberCategoryOrder); - builder.setMembersOrdering(memberCategoryOrder, doNotSortFields); + public EclipseConfig sortMembersDoNotSortFields(boolean doNotSortFields) { + builder.sortMembersDoNotSortFields(doNotSortFields); replaceStep(builder.build()); return this; } - public EclipseConfig sortMembers( - String memberCategoryOrder, - boolean doNotSortFields, - String visibilityOrder) { - requireElementsNonNull(memberCategoryOrder); - requireElementsNonNull(visibilityOrder); - builder.setMembersOrdering(memberCategoryOrder, doNotSortFields); - builder.setVisibilityOrdering(visibilityOrder); + public EclipseConfig sortMembersEnabled(boolean enabled) { + builder.sortMembersEnabled(enabled); + replaceStep(builder.build()); + return this; + } + + public EclipseConfig sortMembersOrder(String order) { + requireElementsNonNull(order); + builder.sortMembersOrder(order); + replaceStep(builder.build()); + return this; + } + + public EclipseConfig sortMembersVisibilityOrder(String order) { + requireElementsNonNull(order); + builder.sortMembersVisibilityOrder(order); + replaceStep(builder.build()); + return this; + } + + public EclipseConfig sortMembersVisibilityOrderEnabled(boolean enabled) { + builder.sortMembersVisibilityOrderEnabled(enabled); replaceStep(builder.build()); return this; } diff --git a/plugin-maven/README.md b/plugin-maven/README.md index a4787f0c45..f5cba26993 100644 --- a/plugin-maven/README.md +++ b/plugin-maven/README.md @@ -261,11 +261,50 @@ any other maven phase (i.e. compile) then it can be configured as below; ```xml - 4.26 - ${project.basedir}/eclipse-formatter.xml + + 4.26 + + ${project.basedir}/eclipse-formatter.xml ``` +#### Sort Members + +Not only can you format your code with Eclipse JDT, but you can also sort the members as you know it from Eclipse IDE. +This ensures that the methods are always in sorted order (and thus reduces the likelihood of collisions in a version +control system). It is turned off by default, but you might want to consider enabling it when setting coding standards +for a project. + +The format to specify the sort order follows the `outlinesortoption` and `org.eclipse.jdt.ui.visibility.order` +properties that can be found in the workspace folder of your Eclipse IDE. You can look at the +file `.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs` in your workspace directory. + +###### Define Sort Members settings on project level + +```xml + + + true + + SF,SI,SM,F,I,C,M,T + + false + + true + + B,R,D,V + +``` + +###### Overwrite Sort Members settings on file level + +You can enable/disable the globally defined sort properties on file level by adding the following comments: +- `// @SortMembers:enabled=false` - disable the Sort Members feature for this file +- `// @SortMembers:doNotSortFields=true` - disable the sorting of static and instance fields +- `// @SortMembers:sortByVisibility=false` - don't sort members by its visibility modifier + ### formatAnnotations Type annotations should be on the same line as the type that they qualify. diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/Eclipse.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/Eclipse.java index 9e3b41dbc0..5e795ac93f 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/Eclipse.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/Eclipse.java @@ -24,7 +24,6 @@ import org.eclipse.aether.RepositorySystemSession; import com.diffplug.spotless.FormatterStep; -import com.diffplug.spotless.extra.EquoBasedStepBuilder; import com.diffplug.spotless.extra.P2Mirror; import com.diffplug.spotless.extra.java.EclipseJdtFormatterStep; import com.diffplug.spotless.maven.FormatterStepConfig; @@ -41,11 +40,26 @@ public class Eclipse implements FormatterStepFactory { @Parameter private List p2Mirrors = new ArrayList<>(); + @Parameter + private Boolean sortMembersDoNotSortFields = true; + + @Parameter + private Boolean sortMembersEnabled = false; + + @Parameter + private String sortMembersOrder; + + @Parameter + private String sortMembersVisibilityOrder; + + @Parameter + private Boolean sortMembersVisibilityOrderEnabled = false; + private File cacheDirectory; @Override public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { - EquoBasedStepBuilder eclipseConfig = EclipseJdtFormatterStep.createBuilder(stepConfig.getProvisioner()); + EclipseJdtFormatterStep.Builder eclipseConfig = EclipseJdtFormatterStep.createBuilder(stepConfig.getProvisioner()); eclipseConfig.setVersion(version == null ? EclipseJdtFormatterStep.defaultVersion() : version); if (null != file) { File settingsFile = stepConfig.getFileLocator().locateFile(file); @@ -55,6 +69,24 @@ public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { if (null != cacheDirectory) { eclipseConfig.setCacheDirectory(cacheDirectory); } + if (null != sortMembersEnabled) { + eclipseConfig.sortMembersEnabled(sortMembersEnabled); + } + if (null != sortMembersOrder) { + eclipseConfig.sortMembersOrder(sortMembersOrder); + } + if (null != sortMembersDoNotSortFields) { + eclipseConfig.sortMembersDoNotSortFields(sortMembersDoNotSortFields); + } + if (null != sortMembersVisibilityOrder) { + eclipseConfig.sortMembersVisibilityOrder(sortMembersVisibilityOrder); + } + if (null != sortMembersVisibilityOrderEnabled) { + eclipseConfig.sortMembersVisibilityOrderEnabled(sortMembersVisibilityOrderEnabled); + } + if (null != cacheDirectory) { + eclipseConfig.setCacheDirectory(cacheDirectory); + } return eclipseConfig.build(); } From f1a974958a3e30d4ecd3cd7472ed67eda6dd626f Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Sun, 3 Nov 2024 10:28:02 -0800 Subject: [PATCH 20/20] Remove `h6` headers in the docs. --- plugin-gradle/README.md | 4 ---- plugin-maven/README.md | 4 ---- 2 files changed, 8 deletions(-) diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index 2bfc70ecba..30dccb9fad 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -278,8 +278,6 @@ The format to specify the sort order follows the `outlinesortoption` and `org.ec properties that can be found in the workspace folder of your Eclipse IDE. You can look at the file `.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs` in your workspace directory. -###### Define Sort Members settings on project level - ```gradle spotless { java { @@ -298,8 +296,6 @@ spotless { .sortMembersVisibilityOrder("B,R,D,V") ``` -###### Overwrite Sort Members settings on file level - You can enable/disable the globally defined sort properties on file level by adding the following comments: - `// @SortMembers:enabled=false` - disable the Sort Members feature for this file - `// @SortMembers:doNotSortFields=true` - disable the sorting of static and instance fields diff --git a/plugin-maven/README.md b/plugin-maven/README.md index f5cba26993..3f27aa1085 100644 --- a/plugin-maven/README.md +++ b/plugin-maven/README.md @@ -279,8 +279,6 @@ The format to specify the sort order follows the `outlinesortoption` and `org.ec properties that can be found in the workspace folder of your Eclipse IDE. You can look at the file `.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs` in your workspace directory. -###### Define Sort Members settings on project level - ```xml @@ -298,8 +296,6 @@ file `.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs` in y ``` -###### Overwrite Sort Members settings on file level - You can enable/disable the globally defined sort properties on file level by adding the following comments: - `// @SortMembers:enabled=false` - disable the Sort Members feature for this file - `// @SortMembers:doNotSortFields=true` - disable the sorting of static and instance fields