| /* |
| * 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; |
| } |
| } |