Merge branch 'no-reformat-dialog-on-reformat-code-action'
[idea/community.git] / platform / lang-impl / src / com / intellij / codeInsight / actions / ReformatCodeProcessor.java
1 /*
2  * Copyright 2000-2011 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.formatting.FormattingProgressTask;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.editor.Document;
23 import com.intellij.openapi.editor.SelectionModel;
24 import com.intellij.openapi.module.Module;
25 import com.intellij.openapi.project.Project;
26 import com.intellij.openapi.util.TextRange;
27 import com.intellij.psi.PsiDirectory;
28 import com.intellij.psi.PsiDocumentManager;
29 import com.intellij.psi.PsiFile;
30 import com.intellij.psi.codeStyle.CodeStyleManager;
31 import com.intellij.util.IncorrectOperationException;
32 import com.intellij.util.containers.ContainerUtil;
33 import com.intellij.util.diff.FilesTooBigForDiffException;
34 import org.jetbrains.annotations.NotNull;
35 import org.jetbrains.annotations.Nullable;
36
37 import java.util.ArrayList;
38 import java.util.Collection;
39 import java.util.concurrent.Callable;
40 import java.util.concurrent.FutureTask;
41
42 public class ReformatCodeProcessor extends AbstractLayoutCodeProcessor {
43   
44   public static final String COMMAND_NAME = CodeInsightBundle.message("process.reformat.code");
45   
46   private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.actions.ReformatCodeProcessor");
47
48   private static final String PROGRESS_TEXT = CodeInsightBundle.message("reformat.progress.common.text");
49   private final Collection<TextRange> myRanges = new ArrayList<TextRange>();
50   private SelectionModel mySelectionModel;
51
52   public ReformatCodeProcessor(Project project, boolean processChangedTextOnly) {
53     super(project, COMMAND_NAME, PROGRESS_TEXT, processChangedTextOnly);
54   }
55
56   public ReformatCodeProcessor(@NotNull PsiFile file, @NotNull SelectionModel selectionModel) {
57     super(file.getProject(), file, COMMAND_NAME, PROGRESS_TEXT, false);
58     mySelectionModel = selectionModel;
59   }
60
61   public ReformatCodeProcessor(AbstractLayoutCodeProcessor processor, @NotNull SelectionModel selectionModel) {
62     super(processor, COMMAND_NAME, PROGRESS_TEXT);
63     mySelectionModel = selectionModel;
64   }
65
66   public ReformatCodeProcessor(AbstractLayoutCodeProcessor processor, boolean processChangedTextOnly) {
67     super(processor, COMMAND_NAME, PROGRESS_TEXT);
68     setProcessChangedTextOnly(processChangedTextOnly);
69   }
70
71   public ReformatCodeProcessor(Project project, Module module, boolean processChangedTextOnly) {
72     super(project, module, COMMAND_NAME, PROGRESS_TEXT, processChangedTextOnly);
73   }
74
75   public ReformatCodeProcessor(Project project, PsiDirectory directory, boolean includeSubdirs, boolean processChangedTextOnly) {
76     super(project, directory, includeSubdirs, PROGRESS_TEXT, COMMAND_NAME, processChangedTextOnly);
77   }
78
79   public ReformatCodeProcessor(Project project, PsiFile file, @Nullable TextRange range, boolean processChangedTextOnly) {
80     super(project, file, PROGRESS_TEXT, COMMAND_NAME, processChangedTextOnly);
81     if (range != null) {
82       myRanges.add(range);
83     }
84   }
85
86   public ReformatCodeProcessor(@NotNull PsiFile file, boolean processChangedTextOnly) {
87     super(file.getProject(), file, PROGRESS_TEXT, COMMAND_NAME, processChangedTextOnly);
88   }
89
90   public ReformatCodeProcessor(Project project, PsiFile[] files, @Nullable Runnable postRunnable, boolean processChangedTextOnly) {
91     this(project, files, COMMAND_NAME, postRunnable, processChangedTextOnly);
92   }
93
94   public ReformatCodeProcessor(Project project,
95                                PsiFile[] files,
96                                String commandName,
97                                @Nullable Runnable postRunnable,
98                                boolean processChangedTextOnly)
99   {
100     super(project, files, PROGRESS_TEXT, commandName, postRunnable, processChangedTextOnly);
101   }
102
103   @Override
104   @NotNull
105   protected FutureTask<Boolean> prepareTask(@NotNull final PsiFile file, final boolean processChangedTextOnly)
106     throws IncorrectOperationException
107   {
108     return new FutureTask<Boolean>(new Callable<Boolean>() {
109       private Document myDocument;
110
111       @Override
112       public Boolean call() throws Exception {
113         FormattingProgressTask.FORMATTING_CANCELLED_FLAG.set(false);
114         try {
115           Collection<TextRange> ranges = getRangesToFormat(processChangedTextOnly, file);
116
117           CharSequence before = null;
118           if (getInfoCollector() != null) {
119             myDocument = PsiDocumentManager.getInstance(myProject).getDocument(file);
120             LOG.assertTrue(myDocument != null);
121             before = myDocument.getImmutableCharSequence();
122           }
123
124           CodeStyleManager.getInstance(myProject).reformatText(file, ranges);
125
126           if (before != null) {
127             prepareUserNotificationMessage(myDocument, before);
128           }
129
130           return !FormattingProgressTask.FORMATTING_CANCELLED_FLAG.get();
131         }
132         catch (FilesTooBigForDiffException e) {
133           handleFileTooBigException(LOG, e, file);
134           return false;
135         } 
136         catch (IncorrectOperationException e) {
137           LOG.error(e);
138           return false;
139         }
140         finally {
141           myRanges.clear();
142         }
143       }
144     });
145   }
146
147   private void prepareUserNotificationMessage(@NotNull Document document, @NotNull CharSequence before) {
148     LOG.assertTrue(getInfoCollector() != null);
149     int number = FormatChangedTextUtil.calculateChangedLinesNumber(document, before);
150     if (number > 0) {
151       String message = "formatted " + number + " line" + (number > 1 ? "s" : "");
152       getInfoCollector().setReformatCodeNotification(message);
153     }
154   }
155
156   @NotNull
157   private Collection<TextRange> getRangesToFormat(boolean processChangedTextOnly, PsiFile file) throws FilesTooBigForDiffException {
158     if (mySelectionModel != null) {
159       return getSelectedRanges(mySelectionModel);
160     }
161
162     if (processChangedTextOnly) {
163       return FormatChangedTextUtil.getChangedTextRanges(myProject, file);
164     }
165
166     return !myRanges.isEmpty() ? myRanges : ContainerUtil.newArrayList(file.getTextRange());
167   }
168 }