2 * Copyright 2000-2010 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.
16 package com.intellij.psi.impl.source.codeStyle;
18 import com.intellij.formatting.*;
19 import com.intellij.injected.editor.DocumentWindow;
20 import com.intellij.lang.ASTNode;
21 import com.intellij.lang.LanguageFormatting;
22 import com.intellij.lang.injection.InjectedLanguageManager;
23 import com.intellij.openapi.editor.Document;
24 import com.intellij.openapi.util.TextRange;
25 import com.intellij.psi.*;
26 import com.intellij.psi.codeStyle.CodeStyleSettings;
27 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
28 import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
29 import com.intellij.psi.formatter.DocumentBasedFormattingModel;
30 import com.intellij.psi.impl.source.SourceTreeToPsiMap;
31 import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
32 import com.intellij.psi.util.PsiUtilCore;
33 import com.intellij.util.text.CharArrayUtil;
34 import org.jetbrains.annotations.NotNull;
35 import org.jetbrains.annotations.Nullable;
40 abstract class CodeStyleManagerRunnable<T> {
41 protected CodeStyleSettings mySettings;
42 protected CommonCodeStyleSettings.IndentOptions myIndentOptions;
43 protected FormattingModel myModel;
44 protected TextRange mySignificantRange;
45 private final CodeStyleManagerImpl myCodeStyleManager;
46 @NotNull private final FormattingMode myMode;
48 CodeStyleManagerRunnable(CodeStyleManagerImpl codeStyleManager, @NotNull FormattingMode mode) {
49 myCodeStyleManager = codeStyleManager;
53 public T perform(PsiFile file, int offset, @Nullable TextRange range, T defaultValue) {
54 if (file instanceof PsiCompiledFile) {
55 file = ((PsiCompiledFile)file).getDecompiledPsiFile();
58 PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myCodeStyleManager.getProject());
59 Document document = documentManager.getDocument(file);
60 if (document instanceof DocumentWindow) {
61 final DocumentWindow documentWindow = (DocumentWindow)document;
62 final PsiFile topLevelFile = InjectedLanguageManager.getInstance(file.getProject()).getTopLevelFile(file);
63 if (!file.equals(topLevelFile)) {
65 range = documentWindow.injectedToHost(range);
68 offset = documentWindow.injectedToHost(offset);
70 return adjustResultForInjected(perform(topLevelFile, offset, range, defaultValue), documentWindow);
74 final PsiFile templateFile = PsiUtilCore.getTemplateLanguageFile(file);
75 if (templateFile != null) {
77 document = documentManager.getDocument(templateFile);
80 PsiElement element = null;
82 element = CodeStyleManagerImpl.findElementInTreeWithFormatterEnabled(file, offset);
83 if (element == null && offset != file.getTextLength()) {
86 if (isInsidePlainComment(offset, element)) {
87 return computeValueInsidePlainComment(file, offset, defaultValue);
91 final FormattingModelBuilder builder = LanguageFormatting.INSTANCE.forContext(file);
92 FormattingModelBuilder elementBuilder = element != null ? LanguageFormatting.INSTANCE.forContext(element) : builder;
93 if (builder != null && elementBuilder != null) {
94 mySettings = CodeStyleSettingsManager.getSettings(myCodeStyleManager.getProject());
96 mySignificantRange = offset != -1 ? getSignificantRange(file, offset) : null;
98 if (builder instanceof FormattingModelBuilderEx) {
99 myIndentOptions = ((FormattingModelBuilderEx)builder).getIndentOptionsToUse(file, new FormatTextRanges(mySignificantRange, true), mySettings);
101 if (myIndentOptions == null) {
102 myIndentOptions = mySettings.getIndentOptions(file.getFileType());
105 myModel = CoreFormatterUtil.buildModel(builder, file, mySettings, myMode);
107 if (document != null && useDocumentBaseFormattingModel()) {
108 myModel = new DocumentBasedFormattingModel(myModel.getRootBlock(), document, myCodeStyleManager.getProject(), mySettings,
109 file.getFileType(), file);
112 final T result = doPerform(offset, range);
113 if (result != null) {
120 protected boolean useDocumentBaseFormattingModel() {
124 protected T adjustResultForInjected(T result, DocumentWindow documentWindow) {
128 protected T computeValueInsidePlainComment(PsiFile file, int offset, T defaultValue) {
133 protected abstract T doPerform(int offset, TextRange range);
135 private static boolean isInsidePlainComment(int offset, @Nullable PsiElement element) {
136 if (!(element instanceof PsiComment) || !element.getTextRange().contains(offset)) {
140 if (element instanceof PsiLanguageInjectionHost && InjectedLanguageUtil.hasInjections((PsiLanguageInjectionHost)element)) {
147 private static TextRange getSignificantRange(final PsiFile file, final int offset) {
148 final ASTNode elementAtOffset =
149 SourceTreeToPsiMap.psiElementToTree(CodeStyleManagerImpl.findElementInTreeWithFormatterEnabled(file, offset));
150 if (elementAtOffset == null) {
151 int significantRangeStart = CharArrayUtil.shiftBackward(file.getText(), offset - 1, "\r\t ");
152 return new TextRange(significantRangeStart, offset);
155 final FormattingModelBuilder builder = LanguageFormatting.INSTANCE.forContext(file);
156 final TextRange textRange = builder.getRangeAffectingIndent(file, offset, elementAtOffset);
157 if (textRange != null) {
161 return elementAtOffset.getTextRange();