eb4ada17ecc7e9e8b5dd5e82da52914c0415715d
[idea/community.git] / platform / core-api / src / com / intellij / psi / codeStyle / CodeStyleManager.java
1 /*
2  * Copyright 2000-2017 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.psi.codeStyle;
17
18 import com.intellij.formatting.FormattingMode;
19 import com.intellij.lang.ASTNode;
20 import com.intellij.openapi.components.ServiceManager;
21 import com.intellij.openapi.editor.Document;
22 import com.intellij.openapi.fileTypes.FileType;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.openapi.util.Computable;
25 import com.intellij.openapi.util.TextRange;
26 import com.intellij.psi.PsiElement;
27 import com.intellij.psi.PsiFile;
28 import com.intellij.psi.PsiManager;
29 import com.intellij.util.IncorrectOperationException;
30 import com.intellij.util.ThrowableRunnable;
31 import com.intellij.util.containers.ContainerUtil;
32 import org.jetbrains.annotations.NotNull;
33 import org.jetbrains.annotations.Nullable;
34
35 import java.util.Collection;
36 import java.util.List;
37
38 /**
39  * Service for reformatting code fragments, getting names for elements
40  * according to the user's code style and working with import statements and full-qualified names.
41  */
42 public abstract class CodeStyleManager  {
43   /**
44    * Returns the code style manager for the specified project.
45    *
46    * @param project the project to get the code style manager for.
47    * @return the code style manager instance.
48    */
49   public static CodeStyleManager getInstance(@NotNull Project project) {
50     return ServiceManager.getService(project, CodeStyleManager.class);
51   }
52
53   /**
54    * Returns the code style manager for the project associated with the specified
55    * PSI manager.
56    *
57    * @param manager the PSI manager to get the code style manager for.
58    * @return the code style manager instance.
59    */
60   public static CodeStyleManager getInstance(@NotNull PsiManager manager) {
61     return getInstance(manager.getProject());
62   }
63
64   /**
65    * Gets the project with which the code style manager is associated.
66    *
67    * @return the project instance.
68    */
69   @NotNull public abstract Project getProject();
70
71   /**
72    * Reformats the contents of the specified PSI element, enforces braces and splits import
73    * statements according to the user's code style.
74    *
75    * @param element the element to reformat.
76    * @return the element in the PSI tree after the reformat operation corresponding to the
77    *         original element.
78    * @throws IncorrectOperationException if the file to reformat is read-only.
79    * @see #reformatText(PsiFile, int, int)
80    */
81   @NotNull public abstract PsiElement reformat(@NotNull PsiElement element) throws IncorrectOperationException;
82
83   /**
84    * Reformats the contents of the specified PSI element, and optionally enforces braces
85    * and splits import statements according to the user's code style.
86    *
87    * @param element                  the element to reformat.
88    * @param canChangeWhiteSpacesOnly if true, only reformatting is performed; if false,
89    *                                 braces and import statements also can be modified if necessary.
90    * @return the element in the PSI tree after the reformat operation corresponding to the
91    *         original element.
92    * @throws IncorrectOperationException if the file to reformat is read-only.
93    * @see #reformatText(PsiFile, int, int)
94    */
95   @NotNull public abstract PsiElement reformat(@NotNull PsiElement element, boolean canChangeWhiteSpacesOnly) throws IncorrectOperationException;
96
97   /**
98    * Reformats part of the contents of the specified PSI element, enforces braces
99    * and splits import statements according to the user's code style.
100    *
101    * @param element     the element to reformat.
102    * @param startOffset the start offset in the document of the text range to reformat.
103    * @param endOffset   the end offset in the document of the text range to reformat.
104    * @return the element in the PSI tree after the reformat operation corresponding to the
105    *         original element.
106    * @throws IncorrectOperationException if the file to reformat is read-only.
107    * @see #reformatText(PsiFile, int, int)
108    */
109   public abstract PsiElement reformatRange(@NotNull PsiElement element, int startOffset, int endOffset) throws IncorrectOperationException;
110
111   /**
112    * Reformats part of the contents of the specified PSI element, and optionally enforces braces
113    * and splits import statements according to the user's code style.
114    *
115    * @param element                  the element to reformat.
116    * @param startOffset              the start offset in the document of the text range to reformat.
117    * @param endOffset                the end offset in the document of the text range to reformat.
118    * @param canChangeWhiteSpacesOnly if true, only reformatting is performed; if false,
119    *                                 braces and import statements also can be modified if necessary.
120    * @return the element in the PSI tree after the reformat operation corresponding to the
121    *         original element.
122    * @throws IncorrectOperationException if the file to reformat is read-only.
123    * @see #reformatText(PsiFile, int, int)
124    */
125   public abstract PsiElement reformatRange(@NotNull PsiElement element,
126                                            int startOffset,
127                                            int endOffset,
128                                            boolean canChangeWhiteSpacesOnly) throws IncorrectOperationException;
129
130   /**
131    * Delegates to the {@link #reformatText(PsiFile, Collection)} with the single range defined by the given offsets.
132    *
133    * @param file     the file to reformat.
134    * @param startOffset the start of the text range to reformat.
135    * @param endOffset   the end of the text range to reformat.
136    * @throws IncorrectOperationException if the file to reformat is read-only.
137    */
138   public abstract void reformatText(@NotNull PsiFile file, int startOffset, int endOffset) throws IncorrectOperationException;
139
140   /**
141    * Re-formats a ranges of text in the specified file. This method works faster than
142    * {@link #reformatRange(PsiElement, int, int)} but invalidates the
143    * PSI structure for the file.
144    *
145    * @param file  the file to reformat
146    * @param ranges   ranges to process
147    * @throws IncorrectOperationException  if the file to reformat is read-only.
148    */
149   public abstract void reformatText(@NotNull PsiFile file, @NotNull Collection<TextRange> ranges) throws IncorrectOperationException;
150
151   public abstract void reformatTextWithContext(@NotNull PsiFile file, @NotNull ChangedRangesInfo info) throws IncorrectOperationException;
152   
153   public void reformatTextWithContext(@NotNull PsiFile file, @NotNull Collection<TextRange> ranges) throws IncorrectOperationException {
154     List<TextRange> rangesList = ContainerUtil.newArrayList(ranges);
155     reformatTextWithContext(file, new ChangedRangesInfo(rangesList, null));
156   }
157   
158   /**
159    * Re-formats the specified range of a file, modifying only line indents and leaving
160    * all other whitespace intact.
161    *
162    * @param file          the file to reformat.
163    * @param rangeToAdjust the range of text in which indents should be reformatted.
164    * @throws IncorrectOperationException if the file is read-only.
165    */
166   public abstract void adjustLineIndent(@NotNull PsiFile file, TextRange rangeToAdjust) throws IncorrectOperationException;
167
168   /**
169    * Reformats the line at the specified offset in the specified file, modifying only the line indent
170    * and leaving all other whitespace intact.
171    *
172    * @param file   the file to reformat.
173    * @param offset the offset the line at which should be reformatted.
174    * @throws IncorrectOperationException if the file is read-only.
175    */
176   public abstract int adjustLineIndent(@NotNull PsiFile file, int offset) throws IncorrectOperationException;
177
178   /**
179    * Reformats the line at the specified offset in the specified file, modifying only the line indent
180    * and leaving all other whitespace intact.
181    *
182    * @param document   the document to reformat.
183    * @param offset the offset the line at which should be reformatted.
184    * @throws IncorrectOperationException if the file is read-only.
185    */
186   public abstract int adjustLineIndent(@NotNull Document document, int offset);
187
188   /**
189    * @deprecated this method is not intended to be used by plugins.
190    */
191   public abstract boolean isLineToBeIndented(@NotNull PsiFile file, int offset);
192
193   /**
194    * Calculates the indent that should be used for the specified line in
195    * the specified file.
196    *
197    * @param file   the file for which the indent should be calculated.
198    * @param offset the offset for the line at which the indent should be calculated.
199    * @return the indent string (containing of tabs and/or whitespaces), or null if it
200    *         was not possible to calculate the indent.
201    */
202   @Nullable
203   public abstract String getLineIndent(@NotNull PsiFile file, int offset);
204
205   /**
206    * Calculates the indent that should be used for the current line in the specified
207    * editor.
208    *
209    * @param document for which the indent should be calculated.
210    * @return the indent string (containing of tabs and/or whitespaces), or null if it
211    *         was not possible to calculate the indent.
212    */
213   @Nullable
214   public abstract String getLineIndent(@NotNull Document document, int offset);
215
216   /**
217    * @deprecated
218    */
219   public abstract Indent getIndent(String text, FileType fileType);
220
221   /**
222    * @deprecated
223    */
224   public abstract String fillIndent(Indent indent, FileType fileType);
225
226   /**
227    * @deprecated
228    */
229   public abstract Indent zeroIndent();
230
231   /**
232    * Reformats line indents inside new element and reformats white spaces around it
233    * @param block - added element parent
234    * @param addedElement - new element
235    * @throws IncorrectOperationException if the operation fails for some reason (for example,
236    *                                     the file is read-only).
237    */
238   public abstract void reformatNewlyAddedElement(@NotNull final ASTNode block, @NotNull final ASTNode addedElement) throws IncorrectOperationException;
239
240   /**
241    * Formatting may be executed sequentially, i.e. the whole (re)formatting task is split into a number of smaller sub-tasks
242    * that are executed sequentially. That is done primarily for ability to show progress dialog during formatting (formatting
243    * is always performed from EDT, hence, the GUI freezes if we perform formatting as a single big iteration).
244    * <p/>
245    * However, there are situation when we don't want to use such an approach - for example, IntelliJ IDEA sometimes inserts dummy
246    * text into file in order to calculate formatting-specific data and removes it after that. We don't want to allow Swing events
247    * dispatching during that in order to not show that dummy text to the end-user.
248    * <p/>
249    * It's possible to configure that (implementation details are insignificant here) and current method serves as a read-only
250    * facade for obtaining information if 'sequential' processing is allowed at the moment.
251    *
252    * @return      {@code true} if 'sequential' formatting is allowed now; {@code false} otherwise
253    */
254   public abstract boolean isSequentialProcessingAllowed();
255
256   /**
257    * Disables automatic formatting of modified PSI elements, runs the specified operation
258    * and re-enables the formatting. Can be used to improve performance of PSI write
259    * operations.
260    *
261    * @param r the operation to run.
262    */
263   @SuppressWarnings("LambdaUnfriendlyMethodOverload")
264   public abstract void performActionWithFormatterDisabled(Runnable r);
265
266   @SuppressWarnings("LambdaUnfriendlyMethodOverload")
267   public abstract <T extends Throwable> void performActionWithFormatterDisabled(ThrowableRunnable<T> r) throws T;
268
269   public abstract <T> T performActionWithFormatterDisabled(Computable<T> r);
270
271   /**
272    * Calculates minimum spacing, allowed by formatting model (in columns) for a block starting at given offset,
273    * relative to its previous sibling block.
274    * Returns <code>-1</code>, if required block cannot be found at provided offset,
275    * or spacing cannot be calculated due to some other reason.
276    */
277   public int getSpacing(@NotNull PsiFile file, int offset) {
278     return -1;
279   }
280
281   /**
282    * Calculates minimum number of line feeds that should precede block starting at given offset, as dictated by formatting model.
283    * Returns <code>-1</code>, if required block cannot be found at provided offset,
284    * or spacing cannot be calculated due to some other reason.
285    */
286   public int getMinLineFeeds(@NotNull PsiFile file, int offset) {
287     return -1;
288   }
289
290   /**
291    * Retrieves the current formatting mode.
292    * 
293    * @param project The current project used to obtain {@code CodeStyleManager} instance.
294    * @return The current formatting mode.
295    * @see FormattingMode
296    */
297   public static FormattingMode getCurrentFormattingMode(@NotNull Project project) {
298     if (!project.isDisposed()) {
299       CodeStyleManager instance = getInstance(project);
300       if (instance instanceof FormattingModeAwareIndentAdjuster) {
301         return ((FormattingModeAwareIndentAdjuster)instance).getCurrentFormattingMode();
302       }
303     }
304     return FormattingMode.REFORMAT;
305   }
306 }