2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com
.intellij
.codeInsight
.folding
.impl
;
18 import com
.intellij
.codeInsight
.folding
.JavaCodeFoldingSettings
;
19 import com
.intellij
.lang
.ASTNode
;
20 import com
.intellij
.lang
.folding
.FoldingBuilderEx
;
21 import com
.intellij
.lang
.folding
.FoldingDescriptor
;
22 import com
.intellij
.openapi
.diagnostic
.Logger
;
23 import com
.intellij
.openapi
.editor
.Document
;
24 import com
.intellij
.openapi
.editor
.FoldingGroup
;
25 import com
.intellij
.openapi
.progress
.ProgressManager
;
26 import com
.intellij
.openapi
.project
.DumbAware
;
27 import com
.intellij
.openapi
.util
.TextRange
;
28 import com
.intellij
.openapi
.util
.text
.StringUtil
;
29 import com
.intellij
.psi
.*;
30 import com
.intellij
.psi
.codeStyle
.CodeStyleSettings
;
31 import com
.intellij
.psi
.codeStyle
.CodeStyleSettingsManager
;
32 import com
.intellij
.psi
.impl
.source
.PsiClassReferenceType
;
33 import com
.intellij
.psi
.impl
.source
.SourceTreeToPsiMap
;
34 import com
.intellij
.psi
.impl
.source
.jsp
.jspJava
.JspHolderMethod
;
35 import com
.intellij
.psi
.javadoc
.PsiDocComment
;
36 import com
.intellij
.psi
.util
.PropertyUtil
;
37 import com
.intellij
.psi
.util
.PsiTreeUtil
;
38 import com
.intellij
.psi
.util
.PsiUtil
;
39 import com
.intellij
.util
.Function
;
40 import com
.intellij
.util
.text
.CharArrayUtil
;
41 import org
.jetbrains
.annotations
.NonNls
;
42 import org
.jetbrains
.annotations
.NotNull
;
43 import org
.jetbrains
.annotations
.Nullable
;
45 import java
.util
.ArrayList
;
46 import java
.util
.Arrays
;
47 import java
.util
.List
;
49 public class JavaFoldingBuilder
extends FoldingBuilderEx
implements DumbAware
{
50 private static final Logger LOG
= Logger
.getInstance("#com.intellij.codeInsight.folding.impl.JavaFoldingBuilder");
51 private static final String SMILEY
= "<~>";
54 public FoldingDescriptor
[] buildFoldRegions(@NotNull PsiElement element
, @NotNull Document document
, boolean quick
) {
55 if (!(element
instanceof PsiJavaFile
)) {
56 return FoldingDescriptor
.EMPTY
;
58 PsiJavaFile file
= (PsiJavaFile
) element
;
60 List
<FoldingDescriptor
> result
= new ArrayList
<FoldingDescriptor
>();
61 PsiImportList importList
= file
.getImportList();
62 if (importList
!= null) {
63 PsiImportStatementBase
[] statements
= importList
.getAllImportStatements();
64 if (statements
.length
> 1) {
65 final TextRange rangeToFold
= getRangeToFold(importList
);
66 if (rangeToFold
!= null && rangeToFold
.getLength() > 1) {
67 result
.add(new FoldingDescriptor(importList
, rangeToFold
));
72 PsiClass
[] classes
= file
.getClasses();
73 for (PsiClass aClass
: classes
) {
74 ProgressManager
.checkCanceled();
75 addElementsToFold(result
, aClass
, document
, true, quick
);
78 TextRange range
= getFileHeader(file
);
79 if (range
!= null && range
.getLength() > 1 && document
.getLineNumber(range
.getEndOffset()) > document
.getLineNumber(range
.getStartOffset())) {
80 result
.add(new FoldingDescriptor(file
, range
));
83 return result
.toArray(new FoldingDescriptor
[result
.size()]);
86 private void addElementsToFold(List
<FoldingDescriptor
> list
, PsiClass aClass
, Document document
, boolean foldJavaDocs
, boolean quick
) {
87 if (!(aClass
.getParent() instanceof PsiJavaFile
) || ((PsiJavaFile
)aClass
.getParent()).getClasses().length
> 1) {
88 addToFold(list
, aClass
, document
, true);
91 PsiDocComment docComment
;
93 docComment
= aClass
.getDocComment();
94 if (docComment
!= null) {
95 addToFold(list
, docComment
, document
, true);
98 addAnnotationsToFold(aClass
.getModifierList(), list
, document
);
100 PsiElement
[] children
= aClass
.getChildren();
101 for (PsiElement child
: children
) {
102 ProgressManager
.checkCanceled();
104 if (child
instanceof PsiMethod
) {
105 PsiMethod method
= (PsiMethod
)child
;
106 addToFold(list
, method
, document
, true);
107 addAnnotationsToFold(method
.getModifierList(), list
, document
);
110 docComment
= method
.getDocComment();
111 if (docComment
!= null) {
112 addToFold(list
, docComment
, document
, true);
116 PsiCodeBlock body
= method
.getBody();
118 addCodeBlockFolds(body
, list
, document
, quick
);
121 else if (child
instanceof PsiField
) {
122 PsiField field
= (PsiField
)child
;
124 docComment
= field
.getDocComment();
125 if (docComment
!= null) {
126 addToFold(list
, docComment
, document
, true);
129 addAnnotationsToFold(field
.getModifierList(), list
, document
);
130 PsiExpression initializer
= field
.getInitializer();
131 if (initializer
!= null) {
132 addCodeBlockFolds(initializer
, list
, document
, quick
);
133 } else if (field
instanceof PsiEnumConstant
) {
134 addCodeBlockFolds(field
, list
, document
, quick
);
137 else if (child
instanceof PsiClassInitializer
) {
138 PsiClassInitializer initializer
= (PsiClassInitializer
)child
;
139 addToFold(list
, initializer
, document
, true);
140 addCodeBlockFolds(initializer
, list
, document
, quick
);
142 else if (child
instanceof PsiClass
) {
143 addElementsToFold(list
, (PsiClass
)child
, document
, true, quick
);
149 public String
getPlaceholderText(@NotNull final ASTNode node
) {
150 return getPlaceholderText(SourceTreeToPsiMap
.treeElementToPsi(node
));
153 private static String
getPlaceholderText(PsiElement element
) {
154 if (element
instanceof PsiImportList
) {
157 else if (element
instanceof PsiMethod
|| element
instanceof PsiClassInitializer
|| element
instanceof PsiClass
) {
160 else if (element
instanceof PsiDocComment
) {
163 else if (element
instanceof PsiFile
) {
166 else if (element
instanceof PsiAnnotation
) {
169 if (element
instanceof PsiReferenceParameterList
) {
175 public boolean isCollapsedByDefault(@NotNull final ASTNode node
) {
176 final PsiElement element
= SourceTreeToPsiMap
.treeElementToPsi(node
);
177 JavaCodeFoldingSettings settings
= JavaCodeFoldingSettings
.getInstance();
178 if (element
instanceof PsiNewExpression
|| element
instanceof PsiJavaToken
) {
179 return settings
.isCollapseLambdas();
181 if (element
instanceof PsiReferenceParameterList
) {
182 return settings
.isCollapseConstructorGenericParameters();
185 if (element
instanceof PsiImportList
) {
186 return settings
.isCollapseImports();
188 else if (element
instanceof PsiMethod
|| element
instanceof PsiClassInitializer
|| element
instanceof PsiCodeBlock
) {
189 if (!settings
.isCollapseAccessors() && !settings
.isCollapseMethods()) {
192 if (element
instanceof PsiMethod
&& isSimplePropertyAccessor((PsiMethod
)element
)) {
193 return settings
.isCollapseAccessors();
195 return settings
.isCollapseMethods();
197 else if (element
instanceof PsiAnonymousClass
) {
198 return settings
.isCollapseAnonymousClasses();
200 else if (element
instanceof PsiClass
) {
201 return !(element
.getParent() instanceof PsiFile
) && settings
.isCollapseInnerClasses();
203 else if (element
instanceof PsiDocComment
) {
204 return settings
.isCollapseJavadocs();
206 else if (element
instanceof PsiJavaFile
) {
207 return settings
.isCollapseFileHeader();
209 else if (element
instanceof PsiAnnotation
) {
210 return settings
.isCollapseAnnotations();
213 LOG
.error("Unknown element:" + element
);
218 private static boolean isSimplePropertyAccessor(PsiMethod method
) {
219 PsiCodeBlock body
= method
.getBody();
220 if (body
== null) return false;
221 PsiStatement
[] statements
= body
.getStatements();
222 if (statements
.length
!= 1) return false;
223 PsiStatement statement
= statements
[0];
224 if (PropertyUtil
.isSimplePropertyGetter(method
)) {
225 if (statement
instanceof PsiReturnStatement
) {
226 PsiExpression returnValue
= ((PsiReturnStatement
)statement
).getReturnValue();
227 if (returnValue
instanceof PsiReferenceExpression
) {
228 return ((PsiReferenceExpression
)returnValue
).resolve() instanceof PsiField
;
232 else if (PropertyUtil
.isSimplePropertySetter(method
)) {
233 if (statement
instanceof PsiExpressionStatement
) {
234 PsiExpression expr
= ((PsiExpressionStatement
)statement
).getExpression();
235 if (expr
instanceof PsiAssignmentExpression
) {
236 PsiExpression lhs
= ((PsiAssignmentExpression
)expr
).getLExpression();
237 PsiExpression rhs
= ((PsiAssignmentExpression
)expr
).getRExpression();
238 if (lhs
instanceof PsiReferenceExpression
&& rhs
instanceof PsiReferenceExpression
) {
239 return ((PsiReferenceExpression
)lhs
).resolve() instanceof PsiField
&&
240 ((PsiReferenceExpression
)rhs
).resolve() instanceof PsiParameter
;
249 public static TextRange
getRangeToFold(PsiElement element
) {
250 if (element
instanceof PsiMethod
) {
251 if (element
instanceof JspHolderMethod
) return null;
252 PsiCodeBlock body
= ((PsiMethod
)element
).getBody();
253 if (body
== null) return null;
254 return body
.getTextRange();
256 if (element
instanceof PsiClassInitializer
) {
257 return ((PsiClassInitializer
)element
).getBody().getTextRange();
259 if (element
instanceof PsiClass
) {
260 PsiClass aClass
= (PsiClass
)element
;
261 PsiJavaToken lBrace
= aClass
.getLBrace();
262 if (lBrace
== null) return null;
263 PsiJavaToken rBrace
= aClass
.getRBrace();
264 if (rBrace
== null) return null;
265 return new TextRange(lBrace
.getTextOffset(), rBrace
.getTextOffset() + 1);
267 if (element
instanceof PsiJavaFile
) {
268 return getFileHeader((PsiJavaFile
)element
);
270 if (element
instanceof PsiImportList
) {
271 PsiImportList list
= (PsiImportList
)element
;
272 PsiImportStatementBase
[] statements
= list
.getAllImportStatements();
273 if (statements
.length
== 0) return null;
274 final PsiElement importKeyword
= statements
[0].getFirstChild();
275 if (importKeyword
== null) return null;
276 int startOffset
= importKeyword
.getTextRange().getEndOffset() + 1;
277 int endOffset
= statements
[statements
.length
- 1].getTextRange().getEndOffset();
278 return new TextRange(startOffset
, endOffset
);
280 if (element
instanceof PsiDocComment
) {
281 return element
.getTextRange();
283 if (element
instanceof PsiAnnotation
) {
284 int startOffset
= element
.getTextRange().getStartOffset();
285 PsiElement last
= element
;
286 while (element
instanceof PsiAnnotation
) {
288 element
= PsiTreeUtil
.skipSiblingsForward(element
, PsiWhiteSpace
.class, PsiComment
.class);
291 return new TextRange(startOffset
, last
.getTextRange().getEndOffset());
297 private static TextRange
getFileHeader(PsiJavaFile file
) {
298 PsiElement first
= file
.getFirstChild();
299 if (first
instanceof PsiWhiteSpace
) first
= first
.getNextSibling();
300 PsiElement element
= first
;
301 while (element
instanceof PsiComment
) {
302 element
= element
.getNextSibling();
303 if (element
instanceof PsiWhiteSpace
) {
304 element
= element
.getNextSibling();
310 if (element
== null) return null;
311 if (element
.getPrevSibling() instanceof PsiWhiteSpace
) element
= element
.getPrevSibling();
312 if (element
== null || element
.equals(first
)) return null;
313 return new TextRange(first
.getTextOffset(), element
.getTextOffset());
316 private static void addAnnotationsToFold(PsiModifierList modifierList
, List
<FoldingDescriptor
> foldElements
, Document document
) {
317 if (modifierList
== null) return;
318 PsiElement
[] children
= modifierList
.getChildren();
319 for (int i
= 0; i
< children
.length
; i
++) {
320 PsiElement child
= children
[i
];
321 if (child
instanceof PsiAnnotation
) {
322 addToFold(foldElements
, child
, document
, false);
324 for (j
= i
+ 1; j
< children
.length
; j
++) {
325 PsiElement nextChild
= children
[j
];
326 if (nextChild
instanceof PsiModifier
) break;
329 //noinspection AssignmentToForLoopParameter
335 private void addCodeBlockFolds(PsiElement scope
, final List
<FoldingDescriptor
> foldElements
, final Document document
, final boolean quick
) {
336 scope
.accept(new JavaRecursiveElementWalkingVisitor() {
337 @Override public void visitClass(PsiClass aClass
) {
338 if (!addClosureFolding(aClass
, document
, foldElements
, quick
)) {
339 addToFold(foldElements
, aClass
, document
, true);
340 addElementsToFold(foldElements
, aClass
, document
, false, quick
);
345 public void visitMethodCallExpression(PsiMethodCallExpression expression
) {
346 addMethodGenericParametersFolding(expression
, foldElements
, document
, quick
);
348 super.visitMethodCallExpression(expression
);
352 public void visitNewExpression(PsiNewExpression expression
) {
353 addGenericParametersFolding(expression
, foldElements
, document
, quick
);
355 super.visitNewExpression(expression
);
360 private static void addMethodGenericParametersFolding(PsiMethodCallExpression expression
, List
<FoldingDescriptor
> foldElements
, Document document
, boolean quick
) {
361 final PsiReferenceExpression methodExpression
= expression
.getMethodExpression();
362 final PsiReferenceParameterList list
= methodExpression
.getParameterList();
363 if (list
== null || list
.getTextLength() <= 5) {
367 PsiMethodCallExpression element
= expression
;
369 if (!quick
&& !resolvesCorrectly(element
.getMethodExpression())) return;
370 final PsiElement parent
= element
.getParent();
371 if (!(parent
instanceof PsiExpressionList
) || !(parent
.getParent() instanceof PsiMethodCallExpression
)) break;
372 element
= (PsiMethodCallExpression
)parent
.getParent();
375 addTypeParametersFolding(foldElements
, document
, list
, 3, quick
);
378 private static boolean resolvesCorrectly(PsiReferenceExpression expression
) {
379 for (final JavaResolveResult result
: expression
.multiResolve(true)) {
380 if (!result
.isValidResult()) {
387 private static void addGenericParametersFolding(PsiNewExpression expression
, List
<FoldingDescriptor
> foldElements
, Document document
, boolean quick
) {
388 final PsiElement parent
= expression
.getParent();
389 if (!(parent
instanceof PsiVariable
)) {
393 final PsiType declType
= ((PsiVariable
)parent
).getType();
394 if (!(declType
instanceof PsiClassReferenceType
)) {
398 final PsiType
[] parameters
= ((PsiClassType
)declType
).getParameters();
399 if (parameters
.length
== 0) {
403 PsiJavaCodeReferenceElement classReference
= expression
.getClassReference();
404 if (classReference
== null) {
405 final PsiAnonymousClass anonymousClass
= expression
.getAnonymousClass();
406 if (anonymousClass
!= null) {
407 classReference
= anonymousClass
.getBaseClassReference();
409 if (quick
|| seemsLikeLambda(anonymousClass
.getSuperClass())) {
415 if (classReference
!= null) {
416 final PsiReferenceParameterList list
= classReference
.getParameterList();
419 final PsiJavaCodeReferenceElement declReference
= ((PsiClassReferenceType
)declType
).getReference();
420 final PsiReferenceParameterList declList
= declReference
.getParameterList();
421 if (declList
== null || !list
.getText().equals(declList
.getText())) {
425 if (!Arrays
.equals(list
.getTypeArguments(), parameters
)) {
430 addTypeParametersFolding(foldElements
, document
, list
, 5, quick
);
435 private static void addTypeParametersFolding(List
<FoldingDescriptor
> foldElements
, Document document
, PsiReferenceParameterList list
,
436 final int ifLongerThan
, boolean quick
) {
438 for (final PsiType type
: list
.getTypeArguments()) {
439 if (!type
.isValid()) {
442 if (type
instanceof PsiClassType
|| type
instanceof PsiArrayType
) {
443 if (PsiUtil
.resolveClassInType(type
) == null) {
450 final String text
= list
.getText();
451 if (text
.startsWith("<") && text
.endsWith(">") && text
.length() > ifLongerThan
) {
452 final TextRange range
= list
.getTextRange();
453 addFoldRegion(foldElements
, list
, document
, true, new TextRange(range
.getStartOffset(), range
.getEndOffset()));
457 private static boolean hasOnlyOneMethod(@NotNull PsiAnonymousClass anonymousClass
) {
458 if (anonymousClass
.getFields().length
!= 0) {
461 if (anonymousClass
.getInitializers().length
!= 0) {
464 if (anonymousClass
.getInnerClasses().length
!= 0) {
468 return anonymousClass
.getMethods().length
== 1;
471 private boolean addClosureFolding(final PsiClass aClass
, final Document document
, final List
<FoldingDescriptor
> foldElements
, final boolean quick
) {
472 if (!JavaCodeFoldingSettings
.getInstance().isCollapseLambdas()) {
476 boolean isClosure
= false;
477 if (aClass
instanceof PsiAnonymousClass
) {
478 final PsiAnonymousClass anonymousClass
= (PsiAnonymousClass
)aClass
;
479 final PsiElement element
= anonymousClass
.getParent();
480 if (element
instanceof PsiNewExpression
) {
481 final PsiNewExpression expression
= (PsiNewExpression
)element
;
482 final PsiExpressionList argumentList
= expression
.getArgumentList();
483 if (argumentList
!= null && argumentList
.getExpressions().length
== 0) {
484 final PsiMethod
[] methods
= anonymousClass
.getMethods();
485 if (hasOnlyOneMethod(anonymousClass
) && (quick
|| seemsLikeLambda(anonymousClass
.getBaseClassType().resolve()))) {
486 final PsiMethod method
= methods
[0];
487 final PsiCodeBlock body
= method
.getBody();
490 int rangeStart
= body
.getTextRange().getStartOffset();
491 int rangeEnd
= body
.getTextRange().getEndOffset();
492 final PsiJavaToken lbrace
= body
.getLBrace();
493 if (lbrace
!= null) rangeStart
= lbrace
.getTextRange().getEndOffset();
494 final PsiJavaToken rbrace
= body
.getRBrace();
495 if (rbrace
!= null) rangeEnd
= rbrace
.getTextRange().getStartOffset();
497 final CharSequence seq
= document
.getCharsSequence();
498 final PsiJavaToken classRBrace
= anonymousClass
.getRBrace();
499 if (classRBrace
!= null && rbrace
!= null) {
500 final int methodEndLine
= document
.getLineNumber(rangeEnd
);
501 final int methodEndLineStart
= document
.getLineStartOffset(methodEndLine
);
502 if ("}".equals(seq
.subSequence(methodEndLineStart
, document
.getLineEndOffset(methodEndLine
)).toString().trim())) {
503 int classEndStart
= classRBrace
.getTextRange().getStartOffset();
504 int classEndCol
= classEndStart
- document
.getLineStartOffset(document
.getLineNumber(classEndStart
));
505 rangeEnd
= classEndCol
+ methodEndLineStart
;
509 final CodeStyleSettings settings
= CodeStyleSettingsManager
.getSettings(aClass
.getProject());
510 int firstLineStart
= CharArrayUtil
.shiftForward(seq
, rangeStart
, " \t");
511 if (firstLineStart
< seq
.length() - 1 && seq
.charAt(firstLineStart
) == '\n') firstLineStart
++;
513 int lastLineEnd
= CharArrayUtil
.shiftBackward(seq
, rangeEnd
- 1, " \t");
514 if (lastLineEnd
> 0 && seq
.charAt(lastLineEnd
) == '\n') lastLineEnd
--;
515 if (lastLineEnd
< firstLineStart
) return false;
517 final String baseClassName
= quick ? anonymousClass
.getBaseClassReference().getReferenceName() : anonymousClass
.getBaseClassType().resolve().getName();
518 if (lastLineEnd
>= seq
.length() || firstLineStart
>= seq
.length() || firstLineStart
< 0) {
519 LOG
.assertTrue(false, "llE=" +
527 "; class=" + baseClassName
);
530 final String params
= StringUtil
.join(method
.getParameterList().getParameters(), new Function
<PsiParameter
, String
>() {
531 public String
fun(final PsiParameter psiParameter
) {
534 typeName
= psiParameter
.getTypeElement().getText();
537 typeName
= psiParameter
.getType().getPresentableText();
539 int genStart
= typeName
.indexOf('<');
540 int genEnd
= typeName
.lastIndexOf('>');
541 if (genStart
> 0 && genEnd
> 0) {
542 typeName
= typeName
.substring(0, genStart
) + typeName
.substring(genEnd
+ 1);
544 return typeName
+ " " + psiParameter
.getName();
547 @NonNls final String lambdas
= baseClassName
+ "(" + params
+ ") {";
549 final int closureStart
= expression
.getTextRange().getStartOffset();
550 final int closureEnd
= expression
.getTextRange().getEndOffset();
551 boolean oneLine
= false;
552 String contents
= seq
.subSequence(firstLineStart
, lastLineEnd
).toString();
553 if (contents
.indexOf('\n') < 0) {
554 final int beforeLength
= closureStart
- document
.getLineStartOffset(document
.getLineNumber(closureStart
));
555 final int afterLength
= document
.getLineEndOffset(document
.getLineNumber(closureEnd
)) - closureEnd
;
556 final int resultLineLength
= beforeLength
+ lambdas
.length() + contents
.length() + 5 + afterLength
;
558 if (resultLineLength
<= settings
.RIGHT_MARGIN
) {
559 rangeStart
= CharArrayUtil
.shiftForward(seq
, rangeStart
, " \n\t");
560 rangeEnd
= CharArrayUtil
.shiftBackward(seq
, rangeEnd
- 1, " \n\t") + 1;
565 if (rangeStart
>= rangeEnd
) return false;
567 FoldingGroup group
= FoldingGroup
.newGroup("lambda");
569 final String prettySpace
= oneLine ?
" " : "";
572 new FoldingDescriptor(expression
.getNode(), new TextRange(closureStart
, rangeStart
), group
) {
574 public String
getPlaceholderText() {
575 return lambdas
+ prettySpace
;
579 if (rbrace
!= null && rangeEnd
+ 1 < closureEnd
) {
581 .add(new FoldingDescriptor(rbrace
.getNode(), new TextRange(rangeEnd
, closureEnd
), group
) {
583 public String
getPlaceholderText() {
584 return prettySpace
+ "}";
588 addCodeBlockFolds(body
, foldElements
, document
, quick
);
597 private static boolean seemsLikeLambda(@Nullable final PsiClass baseClass
) {
598 if (baseClass
== null) return false;
600 if (!baseClass
.hasModifierProperty(PsiModifier
.ABSTRACT
)) return false;
602 final PsiMethod
[] constructors
= baseClass
.getConstructors();
603 boolean hasEmptyConstructor
= constructors
.length
== 0;
604 for (final PsiMethod method
: constructors
) {
605 if (method
.getParameterList().getParametersCount() == 0) {
606 hasEmptyConstructor
= true;
611 if (!hasEmptyConstructor
) return false;
613 for (final PsiMethod method
: baseClass
.getMethods()) {
614 if (method
.hasModifierProperty(PsiModifier
.ABSTRACT
)) {
622 private static boolean addToFold(List
<FoldingDescriptor
> list
, PsiElement elementToFold
, Document document
, boolean allowOneLiners
) {
623 LOG
.assertTrue(elementToFold
.isValid());
624 TextRange range
= getRangeToFold(elementToFold
);
625 if (range
== null) return false;
626 return addFoldRegion(list
, elementToFold
, document
, allowOneLiners
, range
);
629 private static boolean addFoldRegion(final List
<FoldingDescriptor
> list
, final PsiElement elementToFold
, final Document document
,
630 final boolean allowOneLiners
,
631 final TextRange range
) {
632 final TextRange fileRange
= elementToFold
.getContainingFile().getTextRange();
633 if (range
.equals(fileRange
)) return false;
635 LOG
.assertTrue(range
.getStartOffset() >= 0 && range
.getEndOffset() <= fileRange
.getEndOffset());
636 // PSI element text ranges may be invalid because of reparse exception (see, for example, IDEA-10617)
637 if (range
.getStartOffset() < 0 || range
.getEndOffset() > fileRange
.getEndOffset()) {
640 if (!allowOneLiners
) {
641 int startLine
= document
.getLineNumber(range
.getStartOffset());
642 int endLine
= document
.getLineNumber(range
.getEndOffset() - 1);
643 if (startLine
< endLine
&& range
.getLength() > 1) {
644 list
.add(new FoldingDescriptor(elementToFold
, range
));
650 if (range
.getLength() > getPlaceholderText(elementToFold
).length()) {
651 list
.add(new FoldingDescriptor(elementToFold
, range
));