blob: 42dcb46c7ac0866eba5111622145cfc838289109 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.impl.source.codeStyle;
import com.intellij.formatting.*;
import com.intellij.injected.editor.DocumentWindow;
import com.intellij.lang.ASTNode;
import com.intellij.lang.LanguageFormatting;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.formatter.DocumentBasedFormattingModel;
import com.intellij.psi.impl.source.SourceTreeToPsiMap;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author nik
*/
abstract class CodeStyleManagerRunnable<T> {
protected CodeStyleSettings mySettings;
protected CommonCodeStyleSettings.IndentOptions myIndentOptions;
protected FormattingModel myModel;
protected TextRange mySignificantRange;
private final CodeStyleManagerImpl myCodeStyleManager;
@NotNull private final FormattingMode myMode;
CodeStyleManagerRunnable(CodeStyleManagerImpl codeStyleManager, @NotNull FormattingMode mode) {
myCodeStyleManager = codeStyleManager;
myMode = mode;
}
public T perform(PsiFile file, int offset, @Nullable TextRange range, T defaultValue) {
if (file instanceof PsiCompiledFile) {
file = ((PsiCompiledFile)file).getDecompiledPsiFile();
}
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myCodeStyleManager.getProject());
Document document = documentManager.getDocument(file);
if (document instanceof DocumentWindow) {
final DocumentWindow documentWindow = (DocumentWindow)document;
final PsiFile topLevelFile = InjectedLanguageManager.getInstance(file.getProject()).getTopLevelFile(file);
if (!file.equals(topLevelFile)) {
if (range != null) {
range = documentWindow.injectedToHost(range);
}
if (offset != -1) {
offset = documentWindow.injectedToHost(offset);
}
return adjustResultForInjected(perform(topLevelFile, offset, range, defaultValue), documentWindow);
}
}
final PsiFile templateFile = PsiUtilCore.getTemplateLanguageFile(file);
if (templateFile != null) {
file = templateFile;
document = documentManager.getDocument(templateFile);
}
PsiElement element = null;
if (offset != -1) {
element = CodeStyleManagerImpl.findElementInTreeWithFormatterEnabled(file, offset);
if (element == null && offset != file.getTextLength()) {
return defaultValue;
}
if (isInsidePlainComment(offset, element)) {
return computeValueInsidePlainComment(file, offset, defaultValue);
}
}
final FormattingModelBuilder builder = LanguageFormatting.INSTANCE.forContext(file);
FormattingModelBuilder elementBuilder = element != null ? LanguageFormatting.INSTANCE.forContext(element) : builder;
if (builder != null && elementBuilder != null) {
mySettings = CodeStyleSettingsManager.getSettings(myCodeStyleManager.getProject());
mySignificantRange = offset != -1 ? getSignificantRange(file, offset) : null;
if (builder instanceof FormattingModelBuilderEx) {
myIndentOptions = ((FormattingModelBuilderEx)builder).getIndentOptionsToUse(file, new FormatTextRanges(mySignificantRange, true), mySettings);
}
if (myIndentOptions == null) {
myIndentOptions = mySettings.getIndentOptions(file.getFileType());
}
myModel = CoreFormatterUtil.buildModel(builder, file, mySettings, myMode);
if (document != null && useDocumentBaseFormattingModel()) {
myModel = new DocumentBasedFormattingModel(myModel.getRootBlock(), document, myCodeStyleManager.getProject(), mySettings,
file.getFileType(), file);
}
final T result = doPerform(offset, range);
if (result != null) {
return result;
}
}
return defaultValue;
}
protected boolean useDocumentBaseFormattingModel() {
return true;
}
protected T adjustResultForInjected(T result, DocumentWindow documentWindow) {
return result;
}
protected T computeValueInsidePlainComment(PsiFile file, int offset, T defaultValue) {
return defaultValue;
}
@Nullable
protected abstract T doPerform(int offset, TextRange range);
private static boolean isInsidePlainComment(int offset, @Nullable PsiElement element) {
if (!(element instanceof PsiComment) || element instanceof PsiDocCommentBase || !element.getTextRange().contains(offset)) {
return false;
}
if (element instanceof PsiLanguageInjectionHost && InjectedLanguageUtil.hasInjections((PsiLanguageInjectionHost)element)) {
return false;
}
return true;
}
private static TextRange getSignificantRange(final PsiFile file, final int offset) {
final ASTNode elementAtOffset =
SourceTreeToPsiMap.psiElementToTree(CodeStyleManagerImpl.findElementInTreeWithFormatterEnabled(file, offset));
if (elementAtOffset == null) {
int significantRangeStart = CharArrayUtil.shiftBackward(file.getText(), offset - 1, "\r\t ");
return new TextRange(Math.max(significantRangeStart, 0), offset);
}
final FormattingModelBuilder builder = LanguageFormatting.INSTANCE.forContext(file);
final TextRange textRange = builder.getRangeAffectingIndent(file, offset, elementAtOffset);
if (textRange != null) {
return textRange;
}
return elementAtOffset.getTextRange();
}
}