• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 Google Inc. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.googlejavaformat.intellij;
18 
19 import com.google.common.util.concurrent.Runnables;
20 import com.google.googlejavaformat.java.FormatterException;
21 import com.google.googlejavaformat.java.ImportOrderer;
22 import com.google.googlejavaformat.java.JavaFormatterOptions;
23 import com.google.googlejavaformat.java.RemoveUnusedImports;
24 import com.intellij.ide.highlighter.JavaFileType;
25 import com.intellij.lang.ImportOptimizer;
26 import com.intellij.openapi.editor.Document;
27 import com.intellij.openapi.project.Project;
28 import com.intellij.psi.PsiDocumentManager;
29 import com.intellij.psi.PsiFile;
30 import org.jetbrains.annotations.NotNull;
31 
32 /** Uses {@code google-java-format} to optimize imports. */
33 public class GoogleJavaFormatImportOptimizer implements ImportOptimizer {
34 
35   @Override
supports(@otNull PsiFile file)36   public boolean supports(@NotNull PsiFile file) {
37     return JavaFileType.INSTANCE.equals(file.getFileType())
38         && GoogleJavaFormatSettings.getInstance(file.getProject()).isEnabled();
39   }
40 
41   @Override
processFile(@otNull PsiFile file)42   public @NotNull Runnable processFile(@NotNull PsiFile file) {
43     Project project = file.getProject();
44 
45     if (!JreConfigurationChecker.checkJreConfiguration(file.getProject())) {
46       return Runnables.doNothing();
47     }
48 
49     PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
50     Document document = documentManager.getDocument(file);
51 
52     if (document == null) {
53       return Runnables.doNothing();
54     }
55 
56     JavaFormatterOptions.Style style = GoogleJavaFormatSettings.getInstance(project).getStyle();
57 
58     final String origText = document.getText();
59     String text;
60     try {
61       text = ImportOrderer.reorderImports(RemoveUnusedImports.removeUnusedImports(origText), style);
62     } catch (FormatterException e) {
63       Notifications.displayParsingErrorNotification(project, file.getName());
64       return Runnables.doNothing();
65     }
66 
67     // pointless to change document text if it hasn't changed, plus this can interfere with
68     // e.g. GoogleJavaFormattingService's output, i.e. it can overwrite the results from the main
69     // formatter.
70     if (text.equals(origText)) {
71       return Runnables.doNothing();
72     }
73 
74     return () -> {
75       if (documentManager.isDocumentBlockedByPsi(document)) {
76         documentManager.doPostponedOperationsAndUnblockDocument(document);
77       }
78 
79       // similarly to above, don't overwrite new document text if it has changed - we use
80       // getCharsSequence() as we should have `writeAction()` (which I think means effectively a
81       // write-lock) and it saves calling getText(), which apparently is expensive.
82       CharSequence newText = document.getCharsSequence();
83       if (CharSequence.compare(origText, newText) != 0) {
84         return;
85       }
86 
87       document.setText(text);
88     };
89   }
90 }
91