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