Merge branch 'master' of git.labs.intellij.net:idea/community
[idea/community.git] / platform / lang-impl / src / com / intellij / formatting / templateLanguages / BlockUtil.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.formatting.templateLanguages;
17
18 import com.intellij.formatting.ASTBlock;
19 import com.intellij.formatting.Block;
20 import com.intellij.formatting.Indent;
21 import com.intellij.formatting.Spacing;
22 import com.intellij.lang.ASTNode;
23 import com.intellij.openapi.util.Pair;
24 import com.intellij.openapi.util.TextRange;
25 import org.jetbrains.annotations.NotNull;
26 import org.jetbrains.annotations.Nullable;
27
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.List;
31
32 /**
33  * @author Alexey Chmutov
34  *         Date: Jul 3, 2009
35  *         Time: 2:47:10 PM
36  */
37 class BlockUtil {
38   private BlockUtil() {
39   }
40
41   public static List<DataLanguageBlockWrapper> buildChildWrappers(@NotNull final Block parent) {
42     assert !(parent instanceof DataLanguageBlockWrapper) : parent.getClass();
43     List<Block> children = parent.getSubBlocks();
44     if (children.size() == 0) return Collections.emptyList();
45     ArrayList<DataLanguageBlockWrapper> result = new ArrayList<DataLanguageBlockWrapper>(children.size());
46     DataLanguageBlockWrapper prevWrapper = null;
47     for (Block child : children) {
48       DataLanguageBlockWrapper currWrapper = createAndAddBlock(result, child, null);
49       if(currWrapper != null && prevWrapper != null) {
50         Spacing spacing = parent.getSpacing(prevWrapper.getOriginal(), currWrapper.getOriginal());
51         prevWrapper.setRightHandSpacing(currWrapper, spacing);
52       }
53       prevWrapper = currWrapper;
54     }
55     return result;
56   }
57
58   public static Pair<List<DataLanguageBlockWrapper>, List<DataLanguageBlockWrapper>> splitBlocksByRightBound(@NotNull Block parent, @NotNull TextRange bounds) {
59     final List<Block> subBlocks = parent.getSubBlocks();
60     if (subBlocks.size() == 0) return new Pair<List<DataLanguageBlockWrapper>, List<DataLanguageBlockWrapper>>(Collections.<DataLanguageBlockWrapper>emptyList(), Collections.<DataLanguageBlockWrapper>emptyList());
61     final ArrayList<DataLanguageBlockWrapper> before = new ArrayList<DataLanguageBlockWrapper>(subBlocks.size() / 2);
62     final ArrayList<DataLanguageBlockWrapper> after = new ArrayList<DataLanguageBlockWrapper>(subBlocks.size() / 2);
63     splitByRightBoundAndCollectBlocks(subBlocks, before, after, bounds);
64     return new Pair<List<DataLanguageBlockWrapper>, List<DataLanguageBlockWrapper>>(before, after);
65   }
66
67   private static void splitByRightBoundAndCollectBlocks(@NotNull List<Block> blocks,
68                                                         @NotNull List<DataLanguageBlockWrapper> before,
69                                                         @NotNull List<DataLanguageBlockWrapper> after,
70                                                         @NotNull TextRange bounds) {
71     for (Block block : blocks) {
72       final TextRange textRange = block.getTextRange();
73       if (bounds.contains(textRange)) {
74         createAndAddBlock(before, block, null);
75       }
76       else if (bounds.getEndOffset() <= textRange.getStartOffset()) {
77         createAndAddBlock(after, block, null);
78       }
79       else {
80         //assert block.getSubBlocks().size() != 0 : "Block " + block.getTextRange() + " doesn't contain subblocks!";
81         splitByRightBoundAndCollectBlocks(block.getSubBlocks(), before, after, bounds);
82       }
83     }
84   }
85
86   @Nullable
87   private static DataLanguageBlockWrapper createAndAddBlock(List<DataLanguageBlockWrapper> list, Block block, @Nullable final Indent indent) {
88     DataLanguageBlockWrapper wrapper = DataLanguageBlockWrapper.create(block, indent);
89     if (wrapper != null) {
90       list.add(wrapper);
91     }
92     return wrapper;
93   }
94
95
96   public static List<Block> mergeBlocks(@NotNull List<TemplateLanguageBlock> tlBlocks, @NotNull List<DataLanguageBlockWrapper> foreignBlocks) {
97     ArrayList<Block> result = new ArrayList<Block>(tlBlocks.size() + foreignBlocks.size());
98     int vInd = 0;
99     int fInd = 0;
100     while (vInd < tlBlocks.size() && fInd < foreignBlocks.size()) {
101       final TemplateLanguageBlock v = tlBlocks.get(vInd);
102       final DataLanguageBlockWrapper f = foreignBlocks.get(fInd);
103       final TextRange vRange = v.getTextRange();
104       final TextRange fRange = f.getTextRange();
105       if (vRange.getStartOffset() >= fRange.getEndOffset()) {
106         // add leading foreign blocks
107         result.add(f);
108         fInd++;
109       }
110       else if (vRange.getEndOffset() <= fRange.getStartOffset()) {
111         // add leading TL blocks
112         result.add(v);
113         vInd++;
114       }
115       else if (vRange.getStartOffset() < fRange.getStartOffset() ||
116                vRange.getStartOffset() == fRange.getStartOffset() && vRange.getEndOffset() >= fRange.getEndOffset()) {
117         // add including TL blocks and split intersecting foreign blocks
118         result.add(v);
119         while (fInd < foreignBlocks.size() && vRange.contains(foreignBlocks.get(fInd).getTextRange())) {
120           v.addForeignChild(foreignBlocks.get(fInd++));
121         }
122         if (fInd < foreignBlocks.size()) {
123           final DataLanguageBlockWrapper notContainedF = foreignBlocks.get(fInd);
124           if (vRange.intersectsStrict(notContainedF.getTextRange())) {
125             Pair<List<DataLanguageBlockWrapper>, List<DataLanguageBlockWrapper>> splitBlocks = splitBlocksByRightBound(notContainedF.getOriginal(), vRange);
126             v.addForeignChildren(splitBlocks.getFirst());
127             foreignBlocks.remove(fInd);
128             if (splitBlocks.getSecond().size() > 0) {
129               foreignBlocks.addAll(fInd, splitBlocks.getSecond());
130             }
131           }
132         }
133         vInd++;
134       }
135       else if (vRange.getStartOffset() > fRange.getStartOffset() ||
136                vRange.getStartOffset() == fRange.getStartOffset() && vRange.getEndOffset() < fRange.getEndOffset()) {
137         // add including foreign blocks or split them if needed
138         int lastContainedTlInd = vInd;
139         while (lastContainedTlInd < tlBlocks.size() && fRange.intersectsStrict(tlBlocks.get(lastContainedTlInd).getTextRange())) {
140           lastContainedTlInd++;
141         }
142         if (fRange.contains(tlBlocks.get(lastContainedTlInd - 1).getTextRange())) {
143           result.add(f);
144           fInd++;
145           while (vInd < lastContainedTlInd) {
146             f.addTlChild(tlBlocks.get(vInd++));
147           }
148         }
149         else {
150           foreignBlocks.remove(fInd);
151           foreignBlocks.addAll(fInd, buildChildWrappers(f.getOriginal()));
152         }
153       }
154     }
155     while (vInd < tlBlocks.size()) {
156       result.add(tlBlocks.get(vInd++));
157     }
158     while (fInd < foreignBlocks.size()) {
159       result.add(foreignBlocks.get(fInd++));
160     }
161     return result;
162   }
163
164   @NotNull
165   public static List<DataLanguageBlockWrapper> filterBlocksByRange(@NotNull List<DataLanguageBlockWrapper> list, @NotNull TextRange textRange) {
166     int i = 0;
167     while (i < list.size()) {
168       final DataLanguageBlockWrapper wrapper = list.get(i);
169       final TextRange range = wrapper.getTextRange();
170       if (textRange.contains(range)) {
171         i++;
172       }
173       else if (range.intersectsStrict(textRange)) {
174         list.remove(i);
175         list.addAll(i, buildChildWrappers(wrapper.getOriginal()));
176       }
177       else {
178         list.remove(i);
179       }
180     }
181     return list;
182   }
183
184   static List<Block> splitBlockIntoFragments(@NotNull Block block, @NotNull List<TemplateLanguageBlock> subBlocks) {
185     final List<Block> children = new ArrayList<Block>(5);
186     final TextRange range = block.getTextRange();
187     int childStartOffset = range.getStartOffset();
188     TemplateLanguageBlock lastTLBlock = null;
189     for (TemplateLanguageBlock tlBlock : subBlocks) {
190       final TextRange tlTextRange = tlBlock.getTextRange();
191       if (tlTextRange.getStartOffset() > childStartOffset) {
192         TextRange dataBlockTextRange = new TextRange(childStartOffset, tlTextRange.getStartOffset());
193         if (tlBlock.isRequiredRange(dataBlockTextRange)) {
194           children.add(new DataLanguageBlockFragmentWrapper(block, dataBlockTextRange));
195         }
196       }
197       children.add(tlBlock);
198       lastTLBlock = tlBlock;
199       childStartOffset = tlTextRange.getEndOffset();
200     }
201     if (range.getEndOffset() > childStartOffset) {
202       TextRange dataBlockTextRange = new TextRange(childStartOffset, range.getEndOffset());
203       if (lastTLBlock == null || lastTLBlock.isRequiredRange(dataBlockTextRange) ) {
204         children.add(new DataLanguageBlockFragmentWrapper(block, dataBlockTextRange));
205       }
206     }
207     return children;
208   }
209
210   static void printBlocks(@Nullable TextRange textRange, @NotNull List<Block> list) {
211     StringBuilder sb = new StringBuilder(String.valueOf(textRange)).append(": ");
212     for (Block block : list) {
213       ASTNode node = block instanceof ASTBlock ? ((ASTBlock)block).getNode() : null;
214       TextRange r = block.getTextRange();
215       sb.append(" [").append(node != null ? node.getElementType() : null)//.append(" ").append(((BlockWithParent)block).getParent() != null)
216           .append(r).append(block.getIndent()).append(block.getAlignment()).append("] ");
217     }
218     System.out.println(sb);
219   }
220
221   static List<Block> setParent(List<Block> children, BlockWithParent parent) {
222     for (Block block : children) {
223       if (block instanceof BlockWithParent) ((BlockWithParent)block).setParent(parent);
224     }
225     return children;
226   }
227 }