in scala/scala-impl/src/org/jetbrains/plugins/scala/lang/folding/ScalaFoldingBuilder.scala [46:181]
private def appendDescriptors(node: ASTNode,
document: Document,
descriptors: java.util.List[FoldingDescriptor],
processedComments: mutable.HashSet[PsiElement],
processedRegions: mutable.HashSet[PsiElement]): Unit = {
val nodeTextRange = node.getTextRange
if (nodeTextRange.getStartOffset + 1 >= nodeTextRange.getEndOffset) return
val psi = node.getPsi
if (isMultiline(node) || isMultilineImport(node) || isNonEmptyExtensionBodyOnNewLine(node)) {
node.getElementType match {
case ScalaTokenTypes.tBLOCK_COMMENT | ScalaDocElementTypes.SCALA_DOC_COMMENT =>
descriptors add new FoldingDescriptor(node, nodeTextRange)
case EXTENSION_BODY =>
// extensions template body do not support `:` in the beginning,
// we should capture new line before
val isExtensionsTemplateBody = node.getTreeParent.getElementType == ScalaElementType.EXTENSION
val range = if (isExtensionsTemplateBody) captureWhitespaceBefore(node, nodeTextRange) else nodeTextRange
descriptors.add(new FoldingDescriptor(node, range))
case TEMPLATE_BODY => descriptors.add(new FoldingDescriptor(node, nodeTextRange))
case ImportStatement if isGoodImport(node) =>
descriptors add new FoldingDescriptor(node,
new TextRange(nodeTextRange.getStartOffset + IMPORT_KEYWORD.length + 1, getImportEnd(node)))
case MatchExprOrMatchType() =>
val infoOpt = multilineBodyInMatch(node)
infoOpt.foreach { info =>
descriptors add new FoldingDescriptor(node, info.range)
}
case _ =>
}
psi match {
case p: ScPackaging =>
p.findExplicitMarker match {
case Some(marker) => // `{` or `:`
val start = nodeTextRange.getStartOffset + marker.getStartOffsetInParent
val end = nodeTextRange.getEndOffset
val range = new TextRange(start, end)
descriptors.add(new FoldingDescriptor(node, range))
case _ =>
}
case p: ScStringLiteral if p.isMultiLineString =>
descriptors add new FoldingDescriptor(node, nodeTextRange)
case args: ScArgumentExprList if args.isArgsInParens =>
descriptors add new FoldingDescriptor(node, nodeTextRange)
case definition: ScDefinitionWithAssignment =>
val bodyOpt = definitionBody(definition)
bodyOpt.foreach { body =>
val start =
if (body.startsFromNewLine()) definition.assignment.map(_.endOffset).getOrElse(body.startOffset)
else body.startOffset
val end = definition.endOffset // will automatically include end marker
val rangeNew = TextRange.create(start, end)
// we generally do not expect bodies empty, but adding this `isEmpty` check just in case
if (!rangeNew.isEmpty) {
descriptors.add(new FoldingDescriptor(definition, rangeNew))
}
}
case _ =>
}
val treeParent: ASTNode = node.getTreeParent
if (treeParent != null) {
psi match {
case block: ScBlockExpr =>
// definition with assignment block is attached to the definition itself and is already handled
if (foldingSettings.isFoldingForAllBlocks && !treeParent.getPsi.is[ScDefinitionWithAssignment]) {
val rangeNew = elementRangeWithEndMarkerAttached(block, nodeTextRange)
descriptors.add(new FoldingDescriptor(node, rangeNew))
}
else treeParent.getPsi match {
case _: ScArgumentExprList | _: ScFor | _: ScWhile | _: ScDo | _: ScIf =>
val rangeNew = elementRangeWithEndMarkerAttached(block, nodeTextRange)
descriptors.add(new FoldingDescriptor(node, rangeNew))
case inf: ScInfixExpr if inf.right == node.getPsi => // SCL-3464
descriptors.add(new FoldingDescriptor(node, nodeTextRange))
case _ =>
}
case _: ScBlock =>
treeParent.getPsi match {
// NOTE: it's actually the only possible left variant: case clause,
// it will be merged with the pattern match above after ScBlockImpl is removed
case _: ScCaseClause =>
descriptors.add(new FoldingDescriptor(node, nodeTextRange))
case _ =>
}
case _ =>
}
}
} else if (node.getElementType == TYPE_PROJECTION) {
node.getPsi match {
case TypeLambda(typeName, typeParamClause, aliasedType) =>
val group = FoldingGroup.newGroup("typelambda")
val range1 = new TextRange(nodeTextRange.getStartOffset, typeParamClause.getTextRange.getStartOffset)
val d1 = new FoldingDescriptor(node, range1, group) {
override def getPlaceholderText: String = typeName
}
val range2 = new TextRange(aliasedType.getTextRange.getEndOffset, nodeTextRange.getEndOffset)
val d2 = new FoldingDescriptor(aliasedType.getNode, range2, group) {
override def getPlaceholderText = ""
}
descriptors addAll Seq(d1, d2).asJavaCollection
case _ =>
}
} else if (node.getElementType == ScalaTokenTypes.tLINE_COMMENT) {
if (!CustomFoldingBuilder.isCustomRegionElement(node.getPsi)) {
addCommentFolds(node.getPsi.asInstanceOf[PsiComment], processedComments, descriptors)
}
} else if (node.getElementType == SIMPLE_TYPE && node.getText == "Unit" &&
node.getPsi.getParent.is[ScFunctionDefinition] &&
ScalaCodeStyleSettings.getInstance(node.getPsi.getProject).ENFORCE_FUNCTIONAL_SYNTAX_FOR_UNIT && foldingSettings.isCollapseCustomRegions) {
node.getPsi match {
case sc: ScalaPsiElement =>
(sc.getPrevSiblingNotWhitespace, sc.getNextSiblingNotWhitespace) match {
case (a1: PsiElement, a2: PsiElement)
if a1.getNode.getElementType == ScalaTokenTypes.tCOLON && a2.getNode.getElementType == ScalaTokenTypes.tASSIGN =>
val startElement =
if (a1.getPrevSibling.is[PsiWhiteSpace]) a1.getPrevSibling
else a1
val endElement =
if (a2.getNextSibling.is[PsiWhiteSpace]) a2.getNextSibling
else a2
descriptors add new FoldingDescriptor(node,
new TextRange(startElement.getTextRange.getStartOffset, endElement.getTextRange.getEndOffset))
return
case _ =>
}
case _ =>
}
}
for (child <- node.getChildren(null)) {
appendDescriptors(child, document, descriptors, processedComments, processedRegions)
}
}