Merge branch 'master' into no-reformat-dialog-on-reformat-code-action
[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
60   public RearrangeCodeProcessor(@NotNull AbstractLayoutCodeProcessor previousProcessor, @NotNull SelectionModel selectionModel) {
61     super(previousProcessor, COMMAND_NAME, PROGRESS_TEXT);
62     mySelectionModel = selectionModel;
63   }
64   
65   public RearrangeCodeProcessor(@NotNull Project project,
66                                 @NotNull PsiFile file,
67                                 @Nullable SelectionModel selectionModel) {
68     super(project, file, PROGRESS_TEXT, COMMAND_NAME, false);
69     mySelectionModel = selectionModel;
70   }
71
72   public RearrangeCodeProcessor(@NotNull PsiFile file) {
73     super(file.getProject(), file, PROGRESS_TEXT, COMMAND_NAME, false);
74   }
75
76   public RearrangeCodeProcessor(@NotNull Project project,
77                                 @NotNull PsiFile[] files,
78                                 @NotNull String commandName,
79                                 @Nullable Runnable postRunnable) {
80     super(project, files, PROGRESS_TEXT, commandName, postRunnable, false);
81   }
82
83   @NotNull
84   @Override
85   protected FutureTask<Boolean> prepareTask(@NotNull final PsiFile file, final boolean processChangedTextOnly) {
86     return new FutureTask<Boolean>(new Callable<Boolean>() {
87       @Override
88       public Boolean call() throws Exception {
89         try {
90           Collection<TextRange> ranges = getRangesToFormat(file, processChangedTextOnly);
91           RearrangeCommand rearranger = new RearrangeCommand(myProject, file, COMMAND_NAME, ranges);
92           if (rearranger.couldRearrange()) {
93             rearranger.run();
94           }
95           return true;
96         }
97         catch (FilesTooBigForDiffException e) {
98           handleFileTooBigException(LOG, e, file);
99           return false;
100         }
101       }
102     });
103   }
104
105   public Collection<TextRange> getRangesToFormat(@NotNull PsiFile file, boolean processChangedTextOnly) throws FilesTooBigForDiffException {
106     if (mySelectionModel != null) {
107       return getSelectedRanges(mySelectionModel);
108     }
109
110     if (processChangedTextOnly) {
111       return FormatChangedTextUtil.getChangedTextRanges(myProject, file);
112     }
113
114     return ContainerUtil.newSmartList(file.getTextRange());
115   }
116 }
117
118
119 class RearrangeCommand {
120   @NotNull private PsiFile myFile;
121   @NotNull private String myCommandName;
122   @NotNull private Project myProject;
123   private Document myDocument;
124   private Runnable myCommand;
125   private final Collection<TextRange> myRanges;
126
127   RearrangeCommand(@NotNull Project project, @NotNull PsiFile file, @NotNull String commandName, @NotNull Collection<TextRange> ranges) {
128     myProject = project;
129     myFile = file;
130     myRanges = ranges;
131     myCommandName = commandName;
132     myDocument = PsiDocumentManager.getInstance(project).getDocument(file);
133   }
134
135   boolean couldRearrange() {
136     return myDocument != null && Rearranger.EXTENSION.forLanguage(myFile.getLanguage()) != null;
137   }
138
139   void run() {
140     assert myDocument != null;
141     prepare();
142     try {
143       CommandProcessor.getInstance().executeCommand(myProject, myCommand, myCommandName, null);
144     }
145     finally {
146       PsiDocumentManager.getInstance(myProject).commitDocument(myDocument);
147     }
148   }
149
150   private void prepare() {
151     final ArrangementEngine engine = ServiceManager.getService(myProject, ArrangementEngine.class);
152     myCommand = new Runnable() {
153       @Override
154       public void run() {
155         engine.arrange(myFile, myRanges);
156       }
157     };
158     PsiDocumentManager.getInstance(myProject).doPostponedOperationsAndUnblockDocument(myDocument);
159   }
160 }