/*
* Copyright 2000-2015 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.formatting.engine;
import com.intellij.formatting.*;
import com.intellij.formatting.FormatProcessor;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import org.jetbrains.annotations.Nullable;
public class IndentAdjuster {
private final AlignmentHelper myAlignmentHelper;
private final BlockIndentOptions myBlockIndentOptions;
public IndentAdjuster(BlockIndentOptions blockIndentOptions, AlignmentHelper alignmentHelper) {
myAlignmentHelper = alignmentHelper;
myBlockIndentOptions = blockIndentOptions;
}
/**
* Sometimes to align block we adjust whitespace of other block before.
* In such a case, we rollback to that block restarting formatting from there.
*
* @return new current block if we need to rollback, null otherwise
*/
public LeafBlockWrapper adjustIndent(LeafBlockWrapper block) {
AlignmentImpl alignment = CoreFormatterUtil.getAlignment(block);
WhiteSpace whiteSpace = block.getWhiteSpace();
if (alignment == null || myAlignmentHelper.shouldSkip(alignment)) {
if (whiteSpace.containsLineFeeds()) {
adjustSpacingByIndentOffset(block);
}
else {
whiteSpace.arrangeSpaces(block.getSpaceProperty());
}
return null;
}
return myAlignmentHelper.applyAlignment(alignment, block);
}
private void adjustSpacingByIndentOffset(LeafBlockWrapper block) {
CommonCodeStyleSettings.IndentOptions options = myBlockIndentOptions.getIndentOptions(block);
IndentData offset = block.calculateOffset(options);
block.getWhiteSpace().setSpaces(offset.getSpaces(), offset.getIndentSpaces());
}
public void adjustLineIndent(LeafBlockWrapper myCurrentBlock) {
IndentData alignOffset = getAlignOffset(myCurrentBlock);
if (alignOffset == null) {
adjustSpacingByIndentOffset(myCurrentBlock);
}
else {
myCurrentBlock.getWhiteSpace().setSpaces(alignOffset.getSpaces(), alignOffset.getIndentSpaces());
}
}
@Nullable
private static IndentData getAlignOffset(LeafBlockWrapper myCurrentBlock) {
AbstractBlockWrapper current = myCurrentBlock;
while (true) {
final AlignmentImpl alignment = current.getAlignment();
LeafBlockWrapper offsetResponsibleBlock;
if (alignment != null && (offsetResponsibleBlock = alignment.getOffsetRespBlockBefore(myCurrentBlock)) != null) {
final WhiteSpace whiteSpace = offsetResponsibleBlock.getWhiteSpace();
if (whiteSpace.containsLineFeeds()) {
return new IndentData(whiteSpace.getIndentSpaces(), whiteSpace.getSpaces());
}
else {
final int offsetBeforeBlock = CoreFormatterUtil.getStartColumn(offsetResponsibleBlock);
final AbstractBlockWrapper indentedParentBlock = CoreFormatterUtil.getIndentedParentBlock(myCurrentBlock);
if (indentedParentBlock == null) {
return new IndentData(0, offsetBeforeBlock);
}
else {
final int parentIndent = indentedParentBlock.getWhiteSpace().getIndentOffset();
if (parentIndent > offsetBeforeBlock) {
return new IndentData(0, offsetBeforeBlock);
}
else {
return new IndentData(parentIndent, offsetBeforeBlock - parentIndent);
}
}
}
}
else {
current = current.getParent();
if (current == null || current.getStartOffset() != myCurrentBlock.getStartOffset()) return null;
}
}
}
public IndentInfo adjustLineIndent(LeafBlockWrapper currentBlock, FormatProcessor.ChildAttributesInfo info) {
AbstractBlockWrapper parent = info.parent;
ChildAttributes childAttributes = info.attributes;
int index = info.index;
int alignOffset = getAlignOffsetBefore(childAttributes.getAlignment(), null);
if (alignOffset == -1) {
return parent.calculateChildOffset(myBlockIndentOptions.getIndentOptions(parent), childAttributes, index).createIndentInfo();
}
else {
AbstractBlockWrapper indentedParentBlock = CoreFormatterUtil.getIndentedParentBlock(currentBlock);
if (indentedParentBlock == null) {
return new IndentInfo(0, 0, alignOffset);
}
else {
int indentOffset = indentedParentBlock.getWhiteSpace().getIndentOffset();
if (indentOffset > alignOffset) {
return new IndentInfo(0, 0, alignOffset);
}
else {
return new IndentInfo(0, indentOffset, alignOffset - indentOffset);
}
}
}
}
private static int getAlignOffsetBefore(@Nullable final Alignment alignment, @Nullable final LeafBlockWrapper blockAfter) {
if (alignment == null) return -1;
final LeafBlockWrapper alignRespBlock = ((AlignmentImpl)alignment).getOffsetRespBlockBefore(blockAfter);
if (alignRespBlock != null) {
return CoreFormatterUtil.getStartColumn(alignRespBlock);
}
else {
return -1;
}
}
}