code processor: added tests on single file reformat and import optimization
[idea/community.git] / platform / lang-impl / src / com / intellij / codeInsight / actions / RearrangeCodeProcessor.java
1 /*
2  * Copyright 2000-2013 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 package com.intellij.codeInsight.actions;
17
18 import com.intellij.openapi.command.CommandProcessor;
19 import com.intellij.openapi.components.ServiceManager;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.editor.Document;
22 import com.intellij.openapi.editor.SelectionModel;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.openapi.util.TextRange;
25 import com.intellij.psi.PsiDocumentManager;
26 import com.intellij.psi.PsiFile;
27 import com.intellij.psi.codeStyle.arrangement.Rearranger;
28 import com.intellij.psi.codeStyle.arrangement.engine.ArrangementEngine;
29 import com.intellij.util.containers.ContainerUtil;
30 import com.intellij.util.diff.FilesTooBigForDiffException;
31 import org.jetbrains.annotations.NotNull;
32 import org.jetbrains.annotations.Nullable;
33
34 import java.util.Collection;
35 import java.util.concurrent.Callable;
36 import java.util.concurrent.FutureTask;
37
38 public class RearrangeCodeProcessor extends AbstractLayoutCodeProcessor {
39
40   public static final String COMMAND_NAME = "Rearrange code";
41   public static final String PROGRESS_TEXT = "Rearranging code...";
42
43   private static final Logger LOG = Logger.getInstance(RearrangeCodeProcessor.class);
44   private SelectionModel mySelectionModel;
45
46   public RearrangeCodeProcessor(@NotNull AbstractLayoutCodeProcessor previousProcessor) {
47     super(previousProcessor, COMMAND_NAME, PROGRESS_TEXT);
48   }
49
50   public RearrangeCodeProcessor(@NotNull AbstractLayoutCodeProcessor previousProcessor, @NotNull SelectionModel model) {
51     super(previousProcessor, COMMAND_NAME, PROGRESS_TEXT);
52     mySelectionModel = model;
53   }
54
55   public RearrangeCodeProcessor(@NotNull PsiFile file,
56                                 @NotNull SelectionModel selectionModel)
57   {
58     super(file.getProject(), file, PROGRESS_TEXT, COMMAND_NAME, false);
59     mySelectionModel = selectionModel;
60   }
61
62   public RearrangeCodeProcessor(@NotNull PsiFile file) {
63     super(file.getProject(), file, PROGRESS_TEXT, COMMAND_NAME, false);
64   }
65
66   public RearrangeCodeProcessor(@NotNull Project project,
67                                 @NotNull PsiFile[] files,
68                                 @NotNull String commandName,
69                                 @Nullable Runnable postRunnable) {
70     super(project, files, PROGRESS_TEXT, commandName, postRunnable, false);
71   }
72
73   @NotNull
74   @Override
75   protected FutureTask<Boolean> prepareTask(@NotNull final PsiFile file, final boolean processChangedTextOnly) {
76     return new FutureTask<Boolean>(new Callable<Boolean>() {
77       @Override
78       public Boolean call() throws Exception {
79         try {
80           Collection<TextRange> ranges = getRangesToFormat(file, processChangedTextOnly);
81           RearrangeCommand rearranger = new RearrangeCommand(myProject, file, COMMAND_NAME, ranges);
82           if (rearranger.couldRearrange()) {
83             rearranger.run();
84           }
85           return true;
86         }
87         catch (FilesTooBigForDiffException e) {
88           handleFileTooBigException(LOG, e, file);
89           return false;
90         }
91       }
92     });
93   }
94
95   public Collection<TextRange> getRangesToFormat(@NotNull PsiFile file, boolean processChangedTextOnly) throws FilesTooBigForDiffException {
96     if (mySelectionModel != null) {
97       return getSelectedRanges(mySelectionModel);
98     }
99
100     if (processChangedTextOnly) {
101       return FormatChangedTextUtil.getChangedTextRanges(myProject, file);
102     }
103
104     return ContainerUtil.newSmartList(file.getTextRange());
105   }
106 }
107
108
109 class RearrangeCommand {
110   @NotNull private PsiFile myFile;
111   @NotNull private String myCommandName;
112   @NotNull private Project myProject;
113   private Document myDocument;
114   private Runnable myCommand;
115   private final Collection<TextRange> myRanges;
116
117   RearrangeCommand(@NotNull Project project, @NotNull PsiFile file, @NotNull String commandName, @NotNull Collection<TextRange> ranges) {
118     myProject = project;
119     myFile = file;
120     myRanges = ranges;
121     myCommandName = commandName;
122     myDocument = PsiDocumentManager.getInstance(project).getDocument(file);
123   }
124
125   boolean couldRearrange() {
126     return myDocument != null && Rearranger.EXTENSION.forLanguage(myFile.getLanguage()) != null;
127   }
128
129   void run() {
130     assert myDocument != null;
131     prepare();
132     try {
133       CommandProcessor.getInstance().executeCommand(myProject, myCommand, myCommandName, null);
134     }
135     finally {
136       PsiDocumentManager.getInstance(myProject).commitDocument(myDocument);
137     }
138   }
139
140   private void prepare() {
141     final ArrangementEngine engine = ServiceManager.getService(myProject, ArrangementEngine.class);
142     myCommand = new Runnable() {
143       @Override
144       public void run() {
145         engine.arrange(myFile, myRanges);
146       }
147     };
148     PsiDocumentManager.getInstance(myProject).doPostponedOperationsAndUnblockDocument(myDocument);
149   }
150 }