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
;
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) {
57 PsiDocumentManager
.getInstance(project
).commitAllDocuments();
58 final Editor editor
= PlatformDataKeys
.EDITOR
.getData(dataContext
);
59 final VirtualFile
[] files
= PlatformDataKeys
.VIRTUAL_FILE_ARRAY
.getData(dataContext
);
65 final PsiDirectory dir
;
66 boolean hasSelection
= false;
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();
84 new ReformatCodeProcessor(project
, convertToPsiFiles(files
, project
), null).run();
91 Project projectContext
= PlatformDataKeys
.PROJECT_CONTEXT
.getData(dataContext
);
92 Module moduleContext
= LangDataKeys
.MODULE_CONTEXT
.getData(dataContext
);
94 if (projectContext
!= null || moduleContext
!= null) {
96 if (moduleContext
!= null) {
97 text
= CodeInsightBundle
.message("process.scope.module", moduleContext
.getModuleFilePath());
100 text
= CodeInsightBundle
.message("process.scope.project", projectContext
.getPresentableUrl());
103 LayoutProjectCodeDialog dialog
= new LayoutProjectCodeDialog(project
, CodeInsightBundle
.message("process.reformat.code"), text
, true);
105 if (!dialog
.isOK()) return;
106 if (dialog
.isOptimizeImports() && !DumbService
.getInstance(project
).isDumb()) {
107 if (moduleContext
!= null) {
108 new ReformatAndOptimizeImportsProcessor(project
, moduleContext
).run();
111 new ReformatAndOptimizeImportsProcessor(projectContext
).run();
115 if (moduleContext
!= null) {
116 new ReformatCodeProcessor(project
, moduleContext
).run();
119 new ReformatCodeProcessor(projectContext
).run();
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
;
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
);
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();
154 new ReformatCodeProcessor(project
, dir
, dialog
.isIncludeSubdirectories()).run();
160 final TextRange range
;
161 if (editor
!= null && editor
.getSelectionModel().hasSelection()){
162 range
= new TextRange(editor
.getSelectionModel().getSelectionStart(), editor
.getSelectionModel().getSelectionEnd());
168 if (optimizeImports
&& range
== null) {
169 new ReformatAndOptimizeImportsProcessor(project
, file
).run();
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
);
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);
211 Editor editor
= PlatformDataKeys
.EDITOR
.getData(dataContext
);
213 final VirtualFile
[] files
= PlatformDataKeys
.VIRTUAL_FILE_ARRAY
.getData(dataContext
);
216 PsiFile file
= PsiDocumentManager
.getInstance(project
).getPsiFile(editor
.getDocument());
217 if (file
== null || file
.getVirtualFile() == null) {
218 presentation
.setEnabled(false);
222 if (LanguageFormatting
.INSTANCE
.forContext(file
) != null) {
223 presentation
.setEnabled(true);
227 else if (files
!= null && areFiles(files
)) {
228 boolean anyFormatters
= false;
229 for (VirtualFile virtualFile
: files
) {
230 if (virtualFile
.isDirectory()) {
231 presentation
.setEnabled(false);
234 final PsiFile psiFile
= PsiManager
.getInstance(project
).findFile(virtualFile
);
235 if (psiFile
== null) {
236 presentation
.setEnabled(false);
239 final FormattingModelBuilder builder
= LanguageFormatting
.INSTANCE
.forContext(psiFile
);
240 if (builder
!= null) {
241 anyFormatters
= true;
244 if (!anyFormatters
) {
245 presentation
.setEnabled(false);
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);
259 if (!(element
instanceof PsiDirectory
)) {
260 PsiFile file
= element
.getContainingFile();
261 if (file
== null || LanguageFormatting
.INSTANCE
.forContext(file
) == null) {
262 presentation
.setEnabled(false);
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;