IDEA-51919: reformatting for dirs fixed
[fedora-idea.git] / platform / lang-impl / src / com / intellij / codeInsight / actions / ReformatCodeAction.java
blob6bd5b3c562d914e834c7d24bfd8e60bd803f778f
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.intellij.codeInsight.actions;
19 import com.intellij.application.options.editor.EditorOptions;
20 import com.intellij.codeInsight.CodeInsightBundle;
21 import com.intellij.formatting.FormattingModelBuilder;
22 import com.intellij.lang.LanguageFormatting;
23 import com.intellij.notification.Notification;
24 import com.intellij.notification.NotificationListener;
25 import com.intellij.notification.NotificationType;
26 import com.intellij.notification.Notifications;
27 import com.intellij.openapi.actionSystem.*;
28 import com.intellij.openapi.editor.Editor;
29 import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
30 import com.intellij.openapi.module.Module;
31 import com.intellij.openapi.options.ShowSettingsUtil;
32 import com.intellij.openapi.project.DumbAware;
33 import com.intellij.openapi.project.DumbService;
34 import com.intellij.openapi.project.Project;
35 import com.intellij.openapi.util.TextRange;
36 import com.intellij.openapi.vfs.ReadonlyStatusHandler;
37 import com.intellij.openapi.vfs.VirtualFile;
38 import com.intellij.openapi.wm.IdeFrame;
39 import com.intellij.openapi.wm.ex.WindowManagerEx;
40 import com.intellij.psi.*;
41 import org.jetbrains.annotations.NonNls;
42 import org.jetbrains.annotations.NotNull;
44 import javax.swing.*;
45 import javax.swing.event.HyperlinkEvent;
46 import java.util.ArrayList;
48 public class ReformatCodeAction extends AnAction implements DumbAware {
49 private static final @NonNls String HELP_ID = "editing.codeReformatting";
51 public void actionPerformed(AnActionEvent event) {
52 DataContext dataContext = event.getDataContext();
53 final Project project = PlatformDataKeys.PROJECT.getData(dataContext);
54 if (project == null) {
55 return;
57 PsiDocumentManager.getInstance(project).commitAllDocuments();
58 final Editor editor = PlatformDataKeys.EDITOR.getData(dataContext);
59 final VirtualFile[] files = PlatformDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext);
60 if (files == null) {
61 return;
64 PsiFile file = null;
65 final PsiDirectory dir;
66 boolean hasSelection = false;
68 if (editor != null){
69 file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
70 if (file == null) return;
71 dir = file.getContainingDirectory();
72 hasSelection = editor.getSelectionModel().hasSelection();
74 else if (areFiles(files)) {
75 final ReadonlyStatusHandler.OperationStatus operationStatus = ReadonlyStatusHandler.getInstance(project).ensureFilesWritable(files);
76 if (!operationStatus.hasReadonlyFiles()) {
77 final ReformatFilesDialog reformatFilesDialog = new ReformatFilesDialog(project);
78 reformatFilesDialog.show();
79 if (!reformatFilesDialog.isOK()) return;
80 if (reformatFilesDialog.optimizeImports() && !DumbService.getInstance(project).isDumb()) {
81 new ReformatAndOptimizeImportsProcessor(project, convertToPsiFiles(files, project)).run();
83 else {
84 new ReformatCodeProcessor(project, convertToPsiFiles(files, project), null).run();
88 return;
90 else{
91 Project projectContext = PlatformDataKeys.PROJECT_CONTEXT.getData(dataContext);
92 Module moduleContext = LangDataKeys.MODULE_CONTEXT.getData(dataContext);
94 if (projectContext != null || moduleContext != null) {
95 final String text;
96 if (moduleContext != null) {
97 text = CodeInsightBundle.message("process.scope.module", moduleContext.getModuleFilePath());
99 else {
100 text = CodeInsightBundle.message("process.scope.project", projectContext.getPresentableUrl());
103 LayoutProjectCodeDialog dialog = new LayoutProjectCodeDialog(project, CodeInsightBundle.message("process.reformat.code"), text, true);
104 dialog.show();
105 if (!dialog.isOK()) return;
106 if (dialog.isOptimizeImports() && !DumbService.getInstance(project).isDumb()) {
107 if (moduleContext != null) {
108 new ReformatAndOptimizeImportsProcessor(project, moduleContext).run();
110 else {
111 new ReformatAndOptimizeImportsProcessor(projectContext).run();
114 else {
115 if (moduleContext != null) {
116 new ReformatCodeProcessor(project, moduleContext).run();
118 else {
119 new ReformatCodeProcessor(projectContext).run();
122 return;
125 PsiElement element = LangDataKeys.PSI_ELEMENT.getData(dataContext);
126 if (element == null) return;
127 if (element instanceof PsiDirectoryContainer) {
128 dir = ((PsiDirectoryContainer)element).getDirectories()[0];
130 else if (element instanceof PsiDirectory) {
131 dir = (PsiDirectory)element;
133 else {
134 file = element.getContainingFile();
135 if (file == null) return;
136 dir = file.getContainingDirectory();
140 boolean optimizeImports = ReformatFilesDialog.isOptmizeImportsOptionOn();
141 if (EditorSettingsExternalizable.getInstance().getOptions().SHOW_REFORMAT_DIALOG) {
142 final LayoutCodeDialog dialog = new LayoutCodeDialog(project, CodeInsightBundle.message("process.reformat.code"), file, dir,
143 hasSelection ? Boolean.TRUE : Boolean.FALSE, HELP_ID);
144 dialog.show();
145 if (!dialog.isOK()) return;
146 EditorSettingsExternalizable.getInstance().getOptions().SHOW_REFORMAT_DIALOG = !dialog.isDoNotAskMe();
147 updateShowDialogSetting(dialog, "\"Reformat Code\" dialog disabled");
148 optimizeImports = dialog.isOptimizeImports();
149 if (dialog.isProcessDirectory()){
150 if (optimizeImports) {
151 new ReformatAndOptimizeImportsProcessor(project, dir, dialog.isIncludeSubdirectories()).run();
153 else {
154 new ReformatCodeProcessor(project, dir, dialog.isIncludeSubdirectories()).run();
156 return;
160 final TextRange range;
161 if (editor != null && editor.getSelectionModel().hasSelection()){
162 range = new TextRange(editor.getSelectionModel().getSelectionStart(), editor.getSelectionModel().getSelectionEnd());
164 else{
165 range = null;
168 if (optimizeImports && range == null) {
169 new ReformatAndOptimizeImportsProcessor(project, file).run();
171 else {
172 new ReformatCodeProcessor(project, file, range).run();
176 public static void updateShowDialogSetting(LayoutCodeDialog dialog, String title) {
177 if (dialog.isDoNotAskMe()) {
178 Notifications.Bus.notify(new Notification("settings", title,
179 "<html>You can re-enable the dialog on the <a href=''>IDE Settings -> Editor</a> pane</html>",
180 NotificationType.INFORMATION, new NotificationListener() {
181 public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent e) {
182 if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
183 EditorOptions editorOptions = ShowSettingsUtil.getInstance().findApplicationConfigurable(EditorOptions.class);
184 IdeFrame ideFrame = WindowManagerEx.getInstanceEx().findFrameFor(null);
185 ShowSettingsUtil.getInstance().editConfigurable((JFrame)ideFrame, editorOptions);
188 }));
192 public static PsiFile[] convertToPsiFiles(final VirtualFile[] files,Project project) {
193 final PsiManager manager = PsiManager.getInstance(project);
194 final ArrayList<PsiFile> result = new ArrayList<PsiFile>();
195 for (VirtualFile virtualFile : files) {
196 final PsiFile psiFile = manager.findFile(virtualFile);
197 if (psiFile != null) result.add(psiFile);
199 return result.toArray(new PsiFile[result.size()]);
202 public void update(AnActionEvent event){
203 Presentation presentation = event.getPresentation();
204 DataContext dataContext = event.getDataContext();
205 Project project = PlatformDataKeys.PROJECT.getData(dataContext);
206 if (project == null){
207 presentation.setEnabled(false);
208 return;
211 Editor editor = PlatformDataKeys.EDITOR.getData(dataContext);
213 final VirtualFile[] files = PlatformDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext);
215 if (editor != null){
216 PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
217 if (file == null || file.getVirtualFile() == null) {
218 presentation.setEnabled(false);
219 return;
222 if (LanguageFormatting.INSTANCE.forContext(file) != null) {
223 presentation.setEnabled(true);
224 return;
227 else if (files!= null && areFiles(files)) {
228 boolean anyFormatters = false;
229 for (VirtualFile virtualFile : files) {
230 if (virtualFile.isDirectory()) {
231 presentation.setEnabled(false);
232 return;
234 final PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
235 if (psiFile == null) {
236 presentation.setEnabled(false);
237 return;
239 final FormattingModelBuilder builder = LanguageFormatting.INSTANCE.forContext(psiFile);
240 if (builder != null) {
241 anyFormatters = true;
244 if (!anyFormatters) {
245 presentation.setEnabled(false);
246 return;
249 else if (files != null && files.length == 1) {
250 // skip. Both directories and single files are supported.
252 else if (LangDataKeys.MODULE_CONTEXT.getData(dataContext) == null &&
253 PlatformDataKeys.PROJECT_CONTEXT.getData(dataContext) == null) {
254 PsiElement element = LangDataKeys.PSI_ELEMENT.getData(dataContext);
255 if (element == null) {
256 presentation.setEnabled(false);
257 return;
259 if (!(element instanceof PsiDirectory)) {
260 PsiFile file = element.getContainingFile();
261 if (file == null || LanguageFormatting.INSTANCE.forContext(file) == null) {
262 presentation.setEnabled(false);
263 return;
267 presentation.setEnabled(true);
270 public static boolean areFiles(final VirtualFile[] files) {
271 if (files == null) return false;
272 if (files.length < 2) return false;
273 for (VirtualFile virtualFile : files) {
274 if (virtualFile.isDirectory()) return false;
276 return true;