blob: 96cead0659b196b66cf3b26c87801643e8cb5f04 [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.formatting.templateLanguages;
import com.intellij.formatting.ASTBlock;
import com.intellij.formatting.Block;
import com.intellij.formatting.Indent;
import com.intellij.formatting.Spacing;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author Alexey Chmutov
* Date: Jul 3, 2009
* Time: 2:47:10 PM
*/
class BlockUtil {
private BlockUtil() {
}
public static List<DataLanguageBlockWrapper> buildChildWrappers(@NotNull final Block parent) {
assert !(parent instanceof DataLanguageBlockWrapper) : parent.getClass();
List<Block> children = parent.getSubBlocks();
if (children.size() == 0) return Collections.emptyList();
ArrayList<DataLanguageBlockWrapper> result = new ArrayList<DataLanguageBlockWrapper>(children.size());
DataLanguageBlockWrapper prevWrapper = null;
for (Block child : children) {
DataLanguageBlockWrapper currWrapper = createAndAddBlock(result, child, null);
if(currWrapper != null && prevWrapper != null) {
Spacing spacing = parent.getSpacing(prevWrapper.getOriginal(), currWrapper.getOriginal());
prevWrapper.setRightHandSpacing(currWrapper, spacing);
}
prevWrapper = currWrapper;
}
return result;
}
public static Pair<List<DataLanguageBlockWrapper>, List<DataLanguageBlockWrapper>> splitBlocksByRightBound(@NotNull Block parent, @NotNull TextRange bounds) {
final List<Block> subBlocks = parent.getSubBlocks();
if (subBlocks.size() == 0) return Pair
.create(Collections.<DataLanguageBlockWrapper>emptyList(), Collections.<DataLanguageBlockWrapper>emptyList());
final ArrayList<DataLanguageBlockWrapper> before = new ArrayList<DataLanguageBlockWrapper>(subBlocks.size() / 2);
final ArrayList<DataLanguageBlockWrapper> after = new ArrayList<DataLanguageBlockWrapper>(subBlocks.size() / 2);
splitByRightBoundAndCollectBlocks(subBlocks, before, after, bounds);
return new Pair<List<DataLanguageBlockWrapper>, List<DataLanguageBlockWrapper>>(before, after);
}
private static void splitByRightBoundAndCollectBlocks(@NotNull List<Block> blocks,
@NotNull List<DataLanguageBlockWrapper> before,
@NotNull List<DataLanguageBlockWrapper> after,
@NotNull TextRange bounds) {
for (Block block : blocks) {
final TextRange textRange = block.getTextRange();
if (bounds.contains(textRange)) {
createAndAddBlock(before, block, null);
}
else if (bounds.getEndOffset() <= textRange.getStartOffset()) {
createAndAddBlock(after, block, null);
}
else {
//assert block.getSubBlocks().size() != 0 : "Block " + block.getTextRange() + " doesn't contain subblocks!";
splitByRightBoundAndCollectBlocks(block.getSubBlocks(), before, after, bounds);
}
}
}
@Nullable
private static DataLanguageBlockWrapper createAndAddBlock(List<DataLanguageBlockWrapper> list, Block block, @Nullable final Indent indent) {
DataLanguageBlockWrapper wrapper = DataLanguageBlockWrapper.create(block, indent);
if (wrapper != null) {
list.add(wrapper);
}
return wrapper;
}
public static List<Block> mergeBlocks(@NotNull List<TemplateLanguageBlock> tlBlocks, @NotNull List<DataLanguageBlockWrapper> foreignBlocks) {
ArrayList<Block> result = new ArrayList<Block>(tlBlocks.size() + foreignBlocks.size());
int vInd = 0;
int fInd = 0;
while (vInd < tlBlocks.size() && fInd < foreignBlocks.size()) {
final TemplateLanguageBlock v = tlBlocks.get(vInd);
final DataLanguageBlockWrapper f = foreignBlocks.get(fInd);
final TextRange vRange = v.getTextRange();
final TextRange fRange = f.getTextRange();
if (vRange.getStartOffset() >= fRange.getEndOffset()) {
// add leading foreign blocks
result.add(f);
fInd++;
}
else if (vRange.getEndOffset() <= fRange.getStartOffset()) {
// add leading TL blocks
result.add(v);
vInd++;
}
else if (vRange.getStartOffset() < fRange.getStartOffset() ||
vRange.getStartOffset() == fRange.getStartOffset() && vRange.getEndOffset() >= fRange.getEndOffset()) {
// add including TL blocks and split intersecting foreign blocks
result.add(v);
while (fInd < foreignBlocks.size() && vRange.contains(foreignBlocks.get(fInd).getTextRange())) {
v.addForeignChild(foreignBlocks.get(fInd++));
}
if (fInd < foreignBlocks.size()) {
final DataLanguageBlockWrapper notContainedF = foreignBlocks.get(fInd);
if (vRange.intersectsStrict(notContainedF.getTextRange())) {
Pair<List<DataLanguageBlockWrapper>, List<DataLanguageBlockWrapper>> splitBlocks = splitBlocksByRightBound(notContainedF.getOriginal(), vRange);
v.addForeignChildren(splitBlocks.getFirst());
foreignBlocks.remove(fInd);
if (splitBlocks.getSecond().size() > 0) {
foreignBlocks.addAll(fInd, splitBlocks.getSecond());
}
}
}
vInd++;
}
else if (vRange.getStartOffset() > fRange.getStartOffset() ||
vRange.getStartOffset() == fRange.getStartOffset() && vRange.getEndOffset() < fRange.getEndOffset()) {
// add including foreign blocks or split them if needed
int lastContainedTlInd = vInd;
while (lastContainedTlInd < tlBlocks.size() && fRange.intersectsStrict(tlBlocks.get(lastContainedTlInd).getTextRange())) {
lastContainedTlInd++;
}
if (fRange.contains(tlBlocks.get(lastContainedTlInd - 1).getTextRange())) {
result.add(f);
fInd++;
while (vInd < lastContainedTlInd) {
f.addTlChild(tlBlocks.get(vInd++));
}
}
else {
foreignBlocks.remove(fInd);
foreignBlocks.addAll(fInd, buildChildWrappers(f.getOriginal()));
}
}
}
while (vInd < tlBlocks.size()) {
result.add(tlBlocks.get(vInd++));
}
while (fInd < foreignBlocks.size()) {
result.add(foreignBlocks.get(fInd++));
}
return result;
}
@NotNull
public static List<DataLanguageBlockWrapper> filterBlocksByRange(@NotNull List<DataLanguageBlockWrapper> list, @NotNull TextRange textRange) {
int i = 0;
while (i < list.size()) {
final DataLanguageBlockWrapper wrapper = list.get(i);
final TextRange range = wrapper.getTextRange();
if (textRange.contains(range)) {
i++;
}
else if (range.intersectsStrict(textRange)) {
list.remove(i);
list.addAll(i, buildChildWrappers(wrapper.getOriginal()));
}
else {
list.remove(i);
}
}
return list;
}
static List<Block> splitBlockIntoFragments(@NotNull Block block, @NotNull List<TemplateLanguageBlock> subBlocks) {
final List<Block> children = new ArrayList<Block>(5);
final TextRange range = block.getTextRange();
int childStartOffset = range.getStartOffset();
TemplateLanguageBlock lastTLBlock = null;
for (TemplateLanguageBlock tlBlock : subBlocks) {
final TextRange tlTextRange = tlBlock.getTextRange();
if (tlTextRange.getStartOffset() > childStartOffset) {
TextRange dataBlockTextRange = new TextRange(childStartOffset, tlTextRange.getStartOffset());
if (tlBlock.isRequiredRange(dataBlockTextRange)) {
children.add(new DataLanguageBlockFragmentWrapper(block, dataBlockTextRange));
}
}
children.add(tlBlock);
lastTLBlock = tlBlock;
childStartOffset = tlTextRange.getEndOffset();
}
if (range.getEndOffset() > childStartOffset) {
TextRange dataBlockTextRange = new TextRange(childStartOffset, range.getEndOffset());
if (lastTLBlock == null || lastTLBlock.isRequiredRange(dataBlockTextRange) ) {
children.add(new DataLanguageBlockFragmentWrapper(block, dataBlockTextRange));
}
}
return children;
}
static void printBlocks(@Nullable TextRange textRange, @NotNull List<Block> list) {
StringBuilder sb = new StringBuilder(String.valueOf(textRange)).append(": ");
for (Block block : list) {
ASTNode node = block instanceof ASTBlock ? ((ASTBlock)block).getNode() : null;
TextRange r = block.getTextRange();
sb.append(" [").append(node != null ? node.getElementType() : null)//.append(" ").append(((BlockWithParent)block).getParent() != null)
.append(r).append(block.getIndent()).append(block.getAlignment()).append("] ");
}
System.out.println(sb);
}
static List<Block> setParent(List<Block> children, BlockWithParent parent) {
for (Block block : children) {
if (block instanceof BlockWithParent) ((BlockWithParent)block).setParent(parent);
}
return children;
}
}