Reformat directory dialog: prettified a bit, added help id
[idea/community.git] / platform / lang-impl / src / com / intellij / codeInsight / actions / OptimizeImportsAction.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
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.intellij.codeInsight.actions;
18
19 import com.intellij.codeInsight.CodeInsightBundle;
20 import com.intellij.ide.util.PropertiesComponent;
21 import com.intellij.lang.LanguageImportStatements;
22 import com.intellij.openapi.actionSystem.*;
23 import com.intellij.openapi.application.ApplicationManager;
24 import com.intellij.openapi.editor.Editor;
25 import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
26 import com.intellij.openapi.module.Module;
27 import com.intellij.openapi.project.Project;
28 import com.intellij.openapi.ui.DialogWrapper;
29 import com.intellij.openapi.util.text.StringUtil;
30 import com.intellij.openapi.vfs.ReadonlyStatusHandler;
31 import com.intellij.openapi.vfs.VirtualFile;
32 import com.intellij.psi.*;
33 import org.jetbrains.annotations.NonNls;
34 import org.jetbrains.annotations.Nullable;
35
36 import javax.swing.*;
37 import javax.swing.border.EmptyBorder;
38
39 public class OptimizeImportsAction extends AnAction {
40   private static final @NonNls String HELP_ID = "editing.manageImports";
41   private static final String NO_IMPORTS_OPTIMIZED = "Unused imports not found";
42
43
44   @Override
45   public void actionPerformed(AnActionEvent event) {
46     actionPerformedImpl(event.getDataContext());
47   }
48
49   public static void actionPerformedImpl(final DataContext dataContext) {
50     final Project project = CommonDataKeys.PROJECT.getData(dataContext);
51     if (project == null) {
52       return;
53     }
54     PsiDocumentManager.getInstance(project).commitAllDocuments();
55     final Editor editor = BaseCodeInsightAction.getInjectedEditor(project, CommonDataKeys.EDITOR.getData(dataContext));
56
57     final VirtualFile[] files = CommonDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext);
58
59     PsiFile file = null;
60     PsiDirectory dir;
61
62     if (editor != null){
63       file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
64       if (file == null) return;
65       dir = file.getContainingDirectory();
66     }
67     else if (files != null && ReformatCodeAction.containsAtLeastOneFile(files)) {
68       final ReadonlyStatusHandler.OperationStatus operationStatus = ReadonlyStatusHandler.getInstance(project).ensureFilesWritable(files);
69       if (!operationStatus.hasReadonlyFiles()) {
70         new OptimizeImportsProcessor(project, ReformatCodeAction.convertToPsiFiles(files, project), null).run();
71       }
72       return;
73     }
74     else{
75       Project projectContext = PlatformDataKeys.PROJECT_CONTEXT.getData(dataContext);
76       Module moduleContext = LangDataKeys.MODULE_CONTEXT.getData(dataContext);
77
78       if (projectContext != null || moduleContext != null) {
79         final String text;
80         final boolean hasChanges;
81         if (moduleContext != null) {
82           text = CodeInsightBundle.message("process.scope.module", moduleContext.getName());
83           hasChanges = FormatChangedTextUtil.hasChanges(moduleContext);
84         }
85         else {
86           text = CodeInsightBundle.message("process.scope.project", projectContext.getPresentableUrl());
87           hasChanges = FormatChangedTextUtil.hasChanges(projectContext);
88         }
89         DialogWrapper dialog = new OptimizeImportsDialog(project, text, hasChanges);
90         if (!dialog.showAndGet()) {
91           return;
92         }
93         if (moduleContext != null) {
94           new OptimizeImportsProcessor(project, moduleContext).run();
95         }
96         else {
97           new OptimizeImportsProcessor(projectContext).run();
98         }
99         return;
100       }
101
102       PsiElement element = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
103       if (element == null) return;
104       if (element instanceof PsiDirectoryContainer) {
105         dir = ((PsiDirectoryContainer)element).getDirectories()[0];
106       }
107       else if (element instanceof PsiDirectory) {
108         dir = (PsiDirectory)element;
109       }
110       else{
111         file = element.getContainingFile();
112         if (file == null) return;
113         dir = file.getContainingDirectory();
114       }
115     }
116
117     boolean processDirectory = false;
118     boolean processOnlyVcsChangedFiles = false;
119     if (!ApplicationManager.getApplication().isUnitTestMode() && file == null && dir != null) {
120       String message = CodeInsightBundle.message("process.scope.directory", dir.getName());
121       OptimizeImportsDialog dialog = new OptimizeImportsDialog(project, message, FormatChangedTextUtil.hasChanges(dir));
122       dialog.show();
123       if (!dialog.isOK()) {
124         return;
125       }
126       processDirectory = true;
127       processOnlyVcsChangedFiles = dialog.isProcessOnlyVcsChangedFiles();
128     }
129
130     if (processDirectory){
131       new OptimizeImportsProcessor(project, dir, true, processOnlyVcsChangedFiles).run();
132     }
133     else{
134       final OptimizeImportsProcessor optimizer = new OptimizeImportsProcessor(project, file);
135       if (editor != null && EditorSettingsExternalizable.getInstance().getOptions().SHOW_NOTIFICATION_AFTER_OPTIMIZE_IMPORTS_ACTION) {
136         optimizer.setCollectInfo(true);
137         optimizer.setPostRunnable(new Runnable() {
138           @Override
139           public void run() {
140             LayoutCodeInfoCollector collector = optimizer.getInfoCollector();
141             if (collector != null) {
142               String info = collector.getOptimizeImportsNotification();
143               if (!editor.isDisposed() && editor.getComponent().isShowing()) {
144                 String message = info != null ? info : NO_IMPORTS_OPTIMIZED;
145                 FileInEditorProcessor.showHint(editor, StringUtil.capitalize(message));
146               }
147             }
148           }
149         });
150       }
151       optimizer.run();
152     }
153   }
154
155   @Override
156   public void update(AnActionEvent event){
157     if (!LanguageImportStatements.INSTANCE.hasAnyExtensions()) {
158       event.getPresentation().setVisible(false);
159       return;
160     }
161
162     Presentation presentation = event.getPresentation();
163     DataContext dataContext = event.getDataContext();
164     Project project = CommonDataKeys.PROJECT.getData(dataContext);
165     if (project == null){
166       presentation.setEnabled(false);
167       return;
168     }
169
170     final VirtualFile[] files = CommonDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext);
171
172     final Editor editor = BaseCodeInsightAction.getInjectedEditor(project, CommonDataKeys.EDITOR.getData(dataContext), false);
173     if (editor != null){
174       PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
175       if (file == null || !isOptimizeImportsAvailable(file)){
176         presentation.setEnabled(false);
177         return;
178       }
179     }
180     else if (files != null && ReformatCodeAction.containsAtLeastOneFile(files)) {
181       boolean anyHasOptimizeImports = false;
182       for (VirtualFile virtualFile : files) {
183         PsiFile file = PsiManager.getInstance(project).findFile(virtualFile);
184         if (file == null) {
185           presentation.setEnabled(false);
186           return;
187         }
188         if (isOptimizeImportsAvailable(file)) {
189           anyHasOptimizeImports = true;
190         }
191       }
192       if (!anyHasOptimizeImports) {
193         presentation.setEnabled(false);
194         return;
195       }
196     }
197     else if (files != null && files.length == 1) {
198       // skip. Both directories and single files are supported.
199     }
200     else if (LangDataKeys.MODULE_CONTEXT.getData(dataContext) == null &&
201              PlatformDataKeys.PROJECT_CONTEXT.getData(dataContext) == null) {
202       PsiElement element = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
203       if (element == null){
204         presentation.setEnabled(false);
205         return;
206       }
207
208       if (!(element instanceof PsiDirectory)){
209         PsiFile file = element.getContainingFile();
210         if (file == null || !isOptimizeImportsAvailable(file)){
211           presentation.setEnabled(false);
212           return;
213         }
214       }
215     }
216
217     presentation.setEnabled(true);
218   }
219
220   private static boolean isOptimizeImportsAvailable(final PsiFile file) {
221     return !LanguageImportStatements.INSTANCE.forFile(file).isEmpty();
222   }
223
224   private static class OptimizeImportsDialog extends DialogWrapper {
225     private final boolean myContextHasChanges;
226
227     private final String myText;
228     private JCheckBox myOnlyVcsCheckBox;
229     private final LastRunReformatCodeOptionsProvider myLastRunOptions;
230
231     OptimizeImportsDialog(Project project, String text, boolean hasChanges) {
232       super(project, false);
233       myText = text;
234       myContextHasChanges = hasChanges;
235       myLastRunOptions = new LastRunReformatCodeOptionsProvider(PropertiesComponent.getInstance());
236       setOKButtonText(CodeInsightBundle.message("reformat.code.accept.button.text"));
237       setTitle(CodeInsightBundle.message("process.optimize.imports"));
238       init();
239     }
240
241     public boolean isProcessOnlyVcsChangedFiles() {
242       return myOnlyVcsCheckBox.isSelected();
243     }
244
245     @Nullable
246     @Override
247     protected JComponent createCenterPanel() {
248       JPanel panel = new JPanel();
249       BoxLayout layout = new BoxLayout(panel, BoxLayout.Y_AXIS);
250       panel.setLayout(layout);
251
252       panel.add(new JLabel(myText));
253       myOnlyVcsCheckBox = new JCheckBox(CodeInsightBundle.message("process.scope.changed.files"));
254       boolean lastRunVcsChangedTextEnabled = myLastRunOptions.getLastTextRangeType() == TextRangeType.VCS_CHANGED_TEXT;
255
256       myOnlyVcsCheckBox.setEnabled(myContextHasChanges);
257       myOnlyVcsCheckBox.setSelected(myContextHasChanges && lastRunVcsChangedTextEnabled);
258       myOnlyVcsCheckBox.setBorder(new EmptyBorder(0, 10 , 0, 0));
259       panel.add(myOnlyVcsCheckBox);
260       return panel;
261     }
262
263     @Nullable
264     @Override
265     protected String getHelpId() {
266       return HELP_ID;
267     }
268   }
269 }