@@ -212,18 +212,28 @@ public Void visitIdentifier(IdentifierTree node, Void aVoid) {
212
212
}
213
213
}
214
214
}
215
-
216
215
public static String removeUnusedImports (final String contents ) throws FormatterException {
217
216
Context context = new Context ();
218
217
JCCompilationUnit unit = parse (context , contents );
219
- if (unit == null ) {
220
- // error handling is done during formatting
221
- return contents ;
222
- }
223
218
UnusedImportScanner scanner = new UnusedImportScanner (JavacTrees .instance (context ));
224
219
scanner .scan (unit , null );
225
- return applyReplacements (
220
+ String s = applyReplacements (
226
221
contents , buildReplacements (contents , unit , scanner .usedNames , scanner .usedInJavadoc ));
222
+
223
+ // Normalize newlines while preserving important blank lines
224
+ String sep = Newlines .guessLineSeparator (contents );
225
+
226
+ // Ensure exactly one blank line after package declaration
227
+ s = s .replaceAll ("(?m)^package .+" + sep + "\\ s+" + sep , "package $1" + sep + sep );
228
+
229
+ // Ensure exactly one blank line between last import and class declaration
230
+ s = s .replaceAll ("(?m)import .+" + sep + "\\ s+" + sep + "(?=class|interface|enum|record)" ,
231
+ "import $1" + sep + sep );
232
+
233
+ // Remove multiple blank lines elsewhere in imports section
234
+ s = s .replaceAll ("(?m)^import .+" + sep + "\\ s+" + sep + "(?=import)" , "import $1" + sep );
235
+
236
+ return s ;
227
237
}
228
238
229
239
private static JCCompilationUnit parse (Context context , String javaInput )
@@ -233,8 +243,7 @@ private static JCCompilationUnit parse(Context context, String javaInput)
233
243
Options .instance (context ).put ("--enable-preview" , "true" );
234
244
Options .instance (context ).put ("allowStringFolding" , "false" );
235
245
JCCompilationUnit unit ;
236
- JavacFileManager fileManager = new JavacFileManager (context , true , UTF_8 );
237
- try {
246
+ try (JavacFileManager fileManager = new JavacFileManager (context , true , UTF_8 )){
238
247
fileManager .setLocation (StandardLocation .PLATFORM_CLASS_PATH , ImmutableList .of ());
239
248
} catch (IOException e ) {
240
249
// impossible
@@ -284,11 +293,21 @@ private static RangeMap<Integer, String> buildReplacements(
284
293
int endPosition = importTree .getEndPosition (unit .endPositions );
285
294
endPosition = max (CharMatcher .isNot (' ' ).indexIn (contents , endPosition ), endPosition );
286
295
String sep = Newlines .guessLineSeparator (contents );
296
+
297
+ // Check if there's an empty line after this import
298
+ boolean hasEmptyLineAfter = false ;
299
+ if (endPosition + sep .length () * 2 <= contents .length ()) {
300
+ String nextTwoLines = contents .substring (endPosition , endPosition + sep .length () * 2 );
301
+ hasEmptyLineAfter = nextTwoLines .equals (sep + sep );
302
+ }
303
+
287
304
if (endPosition + sep .length () < contents .length ()
288
305
&& contents .subSequence (endPosition , endPosition + sep .length ()).toString ().equals (sep )) {
289
306
endPosition += sep .length ();
290
307
}
291
- if (size == 1 || importTree != lastImport ) {
308
+
309
+ // If this isn't the last import and there's an empty line after, preserve it
310
+ if ((size == 1 || importTree != lastImport ) && !hasEmptyLineAfter ) {
292
311
while (endPosition + sep .length () <= contents .length ()
293
312
&& contents .regionMatches (endPosition , sep , 0 , sep .length ())) {
294
313
endPosition += sep .length ();
@@ -298,7 +317,6 @@ private static RangeMap<Integer, String> buildReplacements(
298
317
}
299
318
return replacements ;
300
319
}
301
-
302
320
private static String getSimpleName (JCTree importTree ) {
303
321
return getQualifiedIdentifier (importTree ).getIdentifier ().toString ();
304
322
}
0 commit comments