blob: 2f53bdec3282da41534f8c69d9b8ce314f6a2076 [file] [log] [blame]
/*
* Copyright 2000-2009 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.
*/
/*
* @author max
*/
package com.intellij.lang.java;
import com.intellij.formatting.Block;
import com.intellij.formatting.FormattingModel;
import com.intellij.formatting.FormattingModelBuilder;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiFile;
import com.intellij.psi.TokenType;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.codeStyle.JavaCodeStyleSettings;
import com.intellij.psi.formatter.FormatterUtil;
import com.intellij.psi.formatter.FormattingDocumentModelImpl;
import com.intellij.psi.formatter.java.AbstractJavaBlock;
import com.intellij.psi.impl.source.SourceTreeToPsiMap;
import com.intellij.psi.impl.source.codeStyle.PsiBasedFormatterModelWithShiftIndentInside;
import com.intellij.psi.impl.source.tree.FileElement;
import com.intellij.psi.impl.source.tree.TreeElement;
import com.intellij.psi.impl.source.tree.TreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class JavaFormattingModelBuilder implements FormattingModelBuilder {
private static final Logger LOG = Logger.getInstance("#com.intellij.lang.java.JavaFormattingModelBuilder");
@Override
@NotNull
public FormattingModel createModel(final PsiElement element, final CodeStyleSettings settings) {
final FileElement fileElement = TreeUtil.getFileElement((TreeElement)SourceTreeToPsiMap.psiElementToTree(element));
LOG.assertTrue(fileElement != null, "File element should not be null for " + element);
CommonCodeStyleSettings commonSettings = settings.getCommonSettings(JavaLanguage.INSTANCE);
JavaCodeStyleSettings customJavaSettings = settings.getCustomSettings(JavaCodeStyleSettings.class);
Block block = AbstractJavaBlock.createJavaBlock(fileElement, commonSettings, customJavaSettings);
FormattingDocumentModelImpl model = FormattingDocumentModelImpl.createOn(element.getContainingFile());
return new PsiBasedFormatterModelWithShiftIndentInside (element.getContainingFile(), block, model);
}
@Override
public TextRange getRangeAffectingIndent(PsiFile file, int offset, ASTNode elementAtOffset) {
return doGetRangeAffectingIndent(elementAtOffset);
}
@Nullable
public static TextRange doGetRangeAffectingIndent(final ASTNode elementAtOffset) {
ASTNode current = elementAtOffset;
current = findNearestExpressionParent(current);
if (current == null) {
if (elementAtOffset.getElementType() == TokenType.WHITE_SPACE) {
ASTNode prevElement = elementAtOffset.getTreePrev();
if (prevElement == null) {
return elementAtOffset.getTextRange();
}
else {
ASTNode prevExpressionParent = findNearestExpressionParent(prevElement);
if (prevExpressionParent == null) {
return elementAtOffset.getTextRange();
}
else {
return new TextRange(prevExpressionParent.getTextRange().getStartOffset(), elementAtOffset.getTextRange().getEndOffset());
}
}
}
else {
// Look at IDEA-65777 for example of situation when it's necessary to expand element range in case of invalid syntax.
return combineWithErrorElementIfPossible(elementAtOffset);
}
}
else {
return current.getTextRange();
}
}
/**
* Checks if previous non-white space leaf of the given node is error element and combines formatting range relevant for it
* with the range of the given node.
*
* @param node target node
* @return given node range if there is no error-element before it; combined range otherwise
*/
@Nullable
private static TextRange combineWithErrorElementIfPossible(@NotNull ASTNode node) {
if (node.getElementType() == TokenType.ERROR_ELEMENT) {
return node.getTextRange();
}
final ASTNode prevLeaf = FormatterUtil.getPreviousLeaf(node, TokenType.WHITE_SPACE);
if (prevLeaf == null || prevLeaf.getElementType() != TokenType.ERROR_ELEMENT) {
return node.getTextRange();
}
final TextRange range = doGetRangeAffectingIndent(prevLeaf);
if (range == null) {
return node.getTextRange();
}
else {
return new TextRange(range.getStartOffset(), node.getTextRange().getEndOffset());
}
}
@Nullable
private static ASTNode findNearestExpressionParent(final ASTNode current) {
ASTNode result = current;
while (result != null) {
PsiElement psi = result.getPsi();
if (psi instanceof PsiExpression && !(psi.getParent() instanceof PsiExpression)) {
return result;
}
result = result.getTreeParent();
}
return result;
}
}