def getChildIndent()

in scala/scala-impl/src/org/jetbrains/plugins/scala/lang/formatting/processors/ScalaIndentProcessor.scala [47:448]


  def getChildIndent(parent: ScalaBlock, child: ASTNode): Indent = {
    val settings = parent.commonSettings
    val scalaSettings = parent.settings.getCustomSettings(classOf[ScalaCodeStyleSettings])

    val parentNode = parent.getNode
    val parentElementType = parentNode.getElementType
    val parentParent = parentNode.getTreeParent
    val parentParentElementType = parentParent.nullSafe.map(_.getElementType).get
    val childElementType = child.getElementType
    val childPsi = child.getPsi

    val isBraceNextLineShifted1 = settings.BRACE_STYLE == NEXT_LINE_SHIFTED
    val isBraceNextLineShifted2 = settings.BRACE_STYLE == NEXT_LINE_SHIFTED2
    val isBraceNextLineShifted = isBraceNextLineShifted1 || isBraceNextLineShifted2

    val isMethodBraceNextLineShifted =
      settings.METHOD_BRACE_STYLE == NEXT_LINE_SHIFTED ||
        settings.METHOD_BRACE_STYLE == NEXT_LINE_SHIFTED2

    def processFunExpr(expr: ScFunctionExpr): Indent = expr.result match {
      case Some(e) if e == childPsi =>
        childPsi match {
          case _: ScBlockImpl => Indent.getNoneIndent
          case _: ScBlockExpr if isBraceNextLineShifted => Indent.getNormalIndent
          case _: ScBlockExpr => Indent.getNoneIndent
          case _: ScExpression => Indent.getNormalIndent
          case _ => Indent.getNoneIndent
        }
      case Some(_) if child.isInstanceOf[PsiComment] => Indent.getNormalIndent
      //the above case is a hack added to fix SCL-6803; probably will backfire with unintended indents
      case _ => Indent.getNoneIndent
    }

    def processMethodCall = childPsi match {
      case arg: ScArgumentExprList if arg.isBraceArgs =>
        if (isBraceNextLineShifted) Indent.getNormalIndent
        else Indent.getNoneIndent
      case _ =>
        Indent.getContinuationWithoutFirstIndent
    }

    @inline def blockIndent(b: ScBlock, bracesShifted: Boolean): Indent =
      if (bracesShifted && b.isEnclosedByBraces) Indent.getNormalIndent
      else Indent.getNoneIndent

    //TODO these are hack methods to facilitate indenting in cases when comment before def/val/var adds one more level of blocks
    def funIndent = childPsi match {
      case _: ScParameters if scalaSettings.INDENT_FIRST_PARAMETER_CLAUSE => Indent.getContinuationIndent
      case b: ScBlockExpr      => blockIndent(b, isMethodBraceNextLineShifted)
      case _: ScBlockStatement => Indent.getNormalIndent
      case c: PsiComment       => commentInBodyIndent(c)
      case _                   => Indent.getNoneIndent
    }
    //NOTE: it's not actually about `val` indent only, it's used in many different contexts
    def valIndent = childPsi match {
      case b: ScBlockExpr      => blockIndent(b, isBraceNextLineShifted)
      case _: ScBlockStatement => Indent.getNormalIndent
      case _: ScTypeElement    => Indent.getNormalIndent
      case c: PsiComment       => commentInBodyIndent(c)
      case _                   => Indent.getNoneIndent
    }
    def assignmentIndent = childPsi match {
      case b: ScBlockExpr      => blockIndent(b, isBraceNextLineShifted)
      case _: ScBlockStatement =>
        val isLeft = childPsi.getPrevSibling == null
        if (isLeft) Indent.getNoneIndent
        else Indent.getNormalIndent
      case _                   => Indent.getNoneIndent
    }
    def blockChildIndent: Indent = childPsi match {
      case b: ScBlock => blockIndent(b, isBraceNextLineShifted)
      case _          => Indent.getNormalIndent
    }

    childPsi match {
      case c: PsiComment if settings.KEEP_FIRST_COLUMN_COMMENT && Scala3IndentationBasedSyntaxUtils.isNotIndentedAtFirstColumn(c) =>
        return Indent.getNoneIndent
      case args: ScArgumentExprList if args.isInScala3File =>
        //TODO (minor) we ask `isInScala3File` for many elements, which is not optimal (it requires tree traversal to parent every time)
        // ideally we need to store information `isScala3` somewhere in global context when constructing blocks for entire scala file
        // (see other places using isInScala3File in formatter package)
        //indented arguments in parentheses in Scala 3 SCL-22238
        //NOTE: some special logic for scala 3 indented arguments also exists in ChainedMethodCallsBlockBuilder.collectChainedMethodCalls
        return Indent.getContinuationWithoutFirstIndent()
      case _ =>
    }

    childElementType match {
      case ScalaTokenType.EndKeyword | ScalaElementType.END_STMT =>
        return Indent.getNoneIndent
      case _ =>
    }

    if (childElementType == ScalaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS ||
      //handle DOC_COMMENT_DATA for the case when we don't have leading asterisk,
      //it can happen e.g. during pasting text to some broken scaladoc
      childElementType == ScalaDocTokenType.DOC_COMMENT_DATA ||
      childElementType == ScalaDocTokenType.DOC_COMMENT_END) {
      return Indent.getSpaceIndent(if (scalaSettings.USE_SCALADOC2_FORMATTING) 2 else 1)
    }
    if (IfOrElse.contains(parentElementType) && parent.lastNode != null) {
      return childPsi match {
        case _: ScBlockExpr if isBraceNextLineShifted => Indent.getNormalIndent(scalaSettings.ALIGN_IF_ELSE)
        case _: ScBlockExpr => Indent.getSpaceIndent(0, scalaSettings.ALIGN_IF_ELSE)
        case _: ScExpression => Indent.getNormalIndent(scalaSettings.ALIGN_IF_ELSE)
        case c: PsiComment => commentInBodyIndent(c)
        case _ => Indent.getSpaceIndent(0, scalaSettings.ALIGN_IF_ELSE)
      }
    }

    if (isYieldOrDo(parentElementType)) {
      childElementType match {
        case `parentElementType` => // skip
        case BlockExpression =>
          return if (isBraceNextLineShifted) Indent.getNormalIndent else Indent.getNoneIndent
        case _               =>
          return Indent.getNormalIndent
      }
    }

    //in chain method calls indentation will be added to outer block `.ref(...)` so seems like no need to add it to the dot
    if (childElementType == ScalaTokenTypes.tDOT) {
      return Indent.getNoneIndent
    }

    //the methodCall/functionExpr have dot block as optional, so cases with and without dot are considered
    if (parentElementType == ScalaTokenTypes.tDOT) {
      val nodeTreeParentParent = parentParent.nullSafe.map(_.getTreeParent).map(_.getPsi).get
      nodeTreeParentParent match {
        case expr: ScFunctionExpr =>
          return processFunExpr(expr)
        case _: ScMethodCall =>
          return processMethodCall
        case _ if parentParent.getPsi.is[ScReferenceExpression] =>
          //proper indentation for chained ref exprs
          return Indent.getContinuationWithoutFirstIndent
        case _ =>
      }
    }

    if (parentElementType == ScalaTokenTypes.tLBRACE && TryOrPackaging.contains(parentParentElementType)) {
      return if (ScalaTokenTypes.BRACES_TOKEN_SET.contains(childElementType)) Indent.getNoneIndent
      else if (isBraceNextLineShifted1) Indent.getNoneIndent
      else Indent.getNormalIndent
    }
    if (parentElementType == ScalaTokenTypes.tCOLON && parentParentElementType == ScalaElementType.PACKAGING) {
      val res = if (childElementType == ScalaTokenTypes.tCOLON)
        Indent.getNoneIndent
      else
        Indent.getNormalIndent
      return res
    }

    def matchChildIndent: Indent =
      childPsi match {
        case _: ScCaseClauses | _: ScMatchTypeCases if settings.INDENT_CASE_FROM_SWITCH => Indent.getNormalIndent
        case _: PsiComment => Indent.getNormalIndent
        case _ => Indent.getNoneIndent
      }

    parentNode.getPsi match {
      case expr: ScFunctionExpr =>
        processFunExpr(expr)
      case _: ScXmlElement =>
        childPsi match {
          case _: ScXmlStartTag | _: ScXmlEndTag | _: ScXmlEmptyTag => Indent.getNoneIndent
          case _ => Indent.getNormalIndent
        }
      case _: ScalaFile =>
        Indent.getNoneIndent
      case p: ScPackaging =>
        childElementType match {
          case ScalaTokenTypes.tLBRACE | ScalaTokenTypes.tRBRACE if p.isExplicit && isBraceNextLineShifted =>
            Indent.getNormalIndent
          case _ => Indent.getNoneIndent
        }
      case _: ScMatch | _: ScMatchTypeElement =>
        matchChildIndent
      case _ if parent.asOptionOf[ChainedMethodCallBlock].exists(_.isInMatchExpr)  =>
        //NOTE: right now match expressions with dot have different block structure then without dot
        //expr.match{...} is considered to be a part of method call chain
        //This might be reviesed, especially after SCL-21458 is fixed and we need to support
        //match chains without dots
        matchChildIndent
      case _: ScTry =>
        childElementType match {
          case ScalaTokenTypes.kTRY | ScalaElementType.CATCH_BLOCK | ScalaElementType.FINALLY_BLOCK => Indent.getNoneIndent
          case _ => blockChildIndent
        }
      case _: ScCatchBlock =>
        childElementType match {
          case ScalaTokenTypes.kCATCH        => Indent.getNoneIndent
          case ScalaElementType.CASE_CLAUSES => Indent.getNormalIndent
          case _                             => blockChildIndent
        }
      case _: ScThrow =>
        childElementType match {
          case ScalaTokenTypes.kTHROW => Indent.getNoneIndent
          case _                      => blockChildIndent
        }
      case _: ScEarlyDefinitions | _: ScTemplateBody | _: ScExtensionBody =>
        childElementType match {
          case ScalaTokenTypes.tLBRACE  => Indent.getNoneIndent
          case et if TokenSets.RBRACE_OR_END_STMT.contains(et) => Indent.getNoneIndent
          case _ if settings.CLASS_BRACE_STYLE == NEXT_LINE_SHIFTED => Indent.getNoneIndent
          case _ => Indent.getNormalIndent
        }
      case b: ScBlockExpr if b.getParent.is[ScFunction] =>
        childElementType match {
          case ScalaTokenTypes.tLBRACE | ScalaTokenTypes.tRBRACE => Indent.getNoneIndent
          case _ if settings.METHOD_BRACE_STYLE == NEXT_LINE_SHIFTED => Indent.getNoneIndent
          case _ => Indent.getNormalIndent
        }
      case _: ScRefinement | _: ScExistentialClause | _: ScBlockExpr  =>
        childElementType match {
          case ScalaTokenTypes.tLBRACE | ScalaTokenTypes.tRBRACE => Indent.getNoneIndent
          case _ if isBraceNextLineShifted1 => Indent.getNoneIndent
          case _ => Indent.getNormalIndent
        }
      case _: ScQuotedBlock =>
        childElementType match {
          case ScalaTokenType.QuoteStart | ScalaTokenTypes.tRBRACE => Indent.getNoneIndent
          case _ if isBraceNextLineShifted1 => Indent.getNoneIndent
          case _ => Indent.getNormalIndent
        }
      case _: ScSplicedBlock | _: ScSplicedPatternExpr =>
        childElementType match {
          case ScalaTokenType.SpliceStart | ScalaTokenTypes.tRBRACE => Indent.getNoneIndent
          case _ if isBraceNextLineShifted1 => Indent.getNoneIndent
          case _ => Indent.getNormalIndent
        }
      case _: ScFunction =>
        funIndent
      case _: ScAssignment =>
        assignmentIndent
      case _ if parentElementType == ScalaTokenTypes.kDEF ||
        TokenSets.FUNCTIONS.contains(parentParentElementType) &&
          ModifiersOrAnnotationOrLineComment.contains(parentElementType) =>
        funIndent
      case _: ScMethodCall =>
        processMethodCall
      case arg: ScArgumentExprList if arg.isBraceArgs =>
        if (scalaSettings.INDENT_BRACED_FUNCTION_ARGS &&
          arg.children.exists(c => ScalaTokenTypes.PARENTHESIS_TOKEN_SET.contains(c.elementType)) &&
          !ScalaTokenTypes.PARENTHESIS_TOKEN_SET.contains(childElementType)
        ) {
          Indent.getNormalIndent
        } else {
          Indent.getNoneIndent
        }
      case _: ScFor =>
        childElementType match {
          case _ if isYieldOrDo(childElementType) =>
            if (scalaSettings.INDENT_YIELD_AFTER_ONE_LINE_ENUMERATORS && isSimpleFor(child))
              Indent.getNormalIndent
            else
              Indent.getNoneIndent
          case ScalaTokenTypes.tLBRACE =>
            if (isBraceNextLineShifted)Indent.getNormalIndent
            else Indent.getNoneIndent
          case ScalaElementType.ENUMERATORS =>
            Indent.getNormalIndent
          case _ =>
            valIndent
        }
      case leaf: LeafPsiElement if leaf.getParent.is[ScFor] =>
        if (parentElementType == ScalaTokenTypes.tLBRACE && childElementType != ScalaTokenTypes.tLBRACE && childElementType != ScalaTokenTypes.tRBRACE) {
          if (isBraceNextLineShifted1) Indent.getNoneIndent
          else Indent.getNormalIndent
        }
        else if (parentElementType == ScalaTokenTypes.tLPARENTHESIS && childElementType != ScalaTokenTypes.tLPARENTHESIS&& childElementType != ScalaTokenTypes.tRPARENTHESIS)
          Indent.getNormalIndent
        else
          Indent.getNoneIndent
      case _: ScIf |
           _: ScWhile |
           _: ScDo  |
           _: ScFinallyBlock |
           _: ScCatchBlock |
           _: ScReturn |
           _: ScValue |
           _: ScVariable |
           _: ScTypeAlias =>
        valIndent
      case _ if childPsi.is[PsiComment] && (parentElementType == ScalaTokenTypes.kIF || parentElementType == ScalaTokenTypes.kELSE  || parentElementType == ScalaTokenType.ThenKeyword) =>
        commentInBodyIndent(childPsi.asInstanceOf[PsiComment])
      case _ if ScalaTokenTypes.VAL_VAR_TOKEN_SET.contains(parentElementType) ||
        TokenSets.PROPERTIES.contains(parentParentElementType) && parentElementType == ScalaElementType.MODIFIERS ||
        parentElementType == ScalaTokenTypes.kRETURN =>
        valIndent
      case _: ScCaseClause =>
        childElementType match {
          case ScalaTokenTypes.kCASE | ScalaTokenTypes.tFUNTYPE => Indent.getNoneIndent
          case _ =>
            childPsi match {
              case _: ScBlockImpl => Indent.getNoneIndent
              case _: ScBlockExpr if isBraceNextLineShifted => Indent.getNormalIndent
              case _: ScBlockExpr => Indent.getNoneIndent
              case _: ScGuard => Indent.getNormalIndent
              case _ => Indent.getNormalIndent
            }
        }
      case block: ScBlockImpl =>
        val blockParent = block.getParent
        blockParent match {
          case _: ScCaseClause | _: ScFunctionExpr =>
            childPsi match {
              case _: ScBlockExpr =>
                if(isBraceNextLineShifted || block.getChildren.length > 1) {
                  Indent.getNormalIndent
                } else {
                  Indent.getNoneIndent
                }
              case _ =>
                if (scalaSettings.DO_NOT_INDENT_CASE_CLAUSE_BODY && blockParent.startsFromNewLine()) Indent.getNoneIndent
                else Indent.getNormalIndent
            }
          case _ => Indent.getNoneIndent
        }
      case _: ScBlock =>
        Indent.getNoneIndent
      case _: ScEnumerators =>
        Indent.getContinuationWithoutFirstIndent(false)
      case _: ScEnumerator =>
        child match {
          case _: ScBlock =>
            Indent.getNoneIndent
          case _ =>
            Indent.getContinuationWithoutFirstIndent
        }
      case _: ScExtendsBlock if childElementType != ScalaElementType.TEMPLATE_BODY => Indent.getContinuationIndent
      case _: ScExtendsBlock if settings.CLASS_BRACE_STYLE == NEXT_LINE_SHIFTED || settings.CLASS_BRACE_STYLE == NEXT_LINE_SHIFTED2 =>
        Indent.getNormalIndent
      case _: ScExtendsBlock => Indent.getNoneIndent //Template body
      case _: ScParameterClause if ScalaTokenTypes.PARENTHESIS_TOKEN_SET.contains(childElementType) =>
        Indent.getNoneIndent
      case p: ScParameterClause if scalaSettings.USE_ALTERNATE_CONTINUATION_INDENT_FOR_PARAMS && isConstructorArgOrMemberFunctionParameter(p) =>
        Indent.getSpaceIndent(scalaSettings.ALTERNATE_CONTINUATION_INDENT_FOR_PARAMS, false)
      case _: ScParameterClause if  scalaSettings.NOT_CONTINUATION_INDENT_FOR_PARAMS =>
        val parentParentPsi = parentParent.nullSafe.map(_.getPsi).get
        val parentParentParentPsi = parentParent.nullSafe.map(_.getTreeParent).map(_.getPsi).get
        (parentParentPsi, parentParentParentPsi) match {
          case (_: ScParameters, _: ScFunctionExpr) => Indent.getNoneIndent
          case _ => Indent.getNormalIndent
        }
      case _: ScParenthesisedExpr | _: ScParenthesisedPattern | _: ScParenthesisedExpr =>
        Indent.getContinuationWithoutFirstIndent(settings.ALIGN_MULTILINE_PARENTHESIZED_EXPRESSION)
      case _: ScTuple | _: ScNamedTuple | _: ScNamedTupleTypeElement | _: ScUnitExpr =>
        if (scalaSettings.DO_NOT_INDENT_TUPLES_CLOSE_BRACE && childElementType == ScalaTokenTypes.tRPARENTHESIS) {
          Indent.getSpaceIndent(0, scalaSettings.ALIGN_TUPLE_ELEMENTS)
        } else {
          Indent.getContinuationWithoutFirstIndent(scalaSettings.ALIGN_TUPLE_ELEMENTS)
        }
      case _: ScNamedTupleExprComponent =>
        assignmentIndent
      case _: ScNamedTuplePatternComponent =>
        val isLeft = childPsi.getPrevSibling == null
        if (isLeft) Indent.getNoneIndent
        else Indent.getNormalIndent
      case _: ScTypeParamClause  if scalaSettings.INDENT_TYPE_PARAMETERS =>
        if (childElementType == ScalaTokenTypes.tRSQBRACKET) {
          Indent.getNoneIndent
        } else {
          Indent.getContinuationWithoutFirstIndent
        }
      case  _: ScTypeArgs  if scalaSettings.INDENT_TYPE_ARGUMENTS =>
        if (childElementType == ScalaTokenTypes.tRSQBRACKET) {
          Indent.getNoneIndent
        } else {
          Indent.getContinuationWithoutFirstIndent
        }

      case _: ScNamedTuplePattern | _: ScTuplePattern | _: ScPatternArgumentList=>
        // Closing `)` should not be aligned with the pattern components (SCL-24596)
        // Examples:
        // val (           |   val (           |    val MyCaseClass(
        //   a,            |     named1 = a,   |      a,
        //   b,            |     named2 = b,   |      b,
        // ) = (1, 2)      |   ) = (1, 2)      |    ) = instance
        // (It's same for `val` pattern definitions and patterns with `case`)
        val isRight = childPsi.getNextSibling == null
        if (isRight) Indent.getNoneIndent
        else Indent.getContinuationWithoutFirstIndent
      case _: ScPattern =>
        Indent.getContinuationWithoutFirstIndent
      case _: ScParameters | _: ScParameterClause | _: ScTemplateParents |
           _: ScExpression | _: ScTypeElement | _: ScTypes =>
        Indent.getContinuationWithoutFirstIndent
      case _: ScArgumentExprList =>
        if (ScalaTokenTypes.PARENTHESIS_TOKEN_SET.contains(childElementType)) Indent.getNoneIndent
        else Indent.getNormalIndent(false)
      case _: ScDocComment => Indent.getNoneIndent
      case _ if parentElementType == ScalaTokenTypes.kEXTENDS && childElementType != ScalaTokenTypes.kEXTENDS =>
        Indent.getContinuationIndent() //this is here to not break whatever processing there is before
      case _: ScImportSelectors if childElementType != ScalaTokenTypes.tRBRACE &&
        childElementType != ScalaTokenTypes.tLBRACE                    => Indent.getNormalIndent
      case Parent(_: ScReferenceExpression) if childElementType == ScalaElementType.TYPE_ARGS =>
        Indent.getContinuationWithoutFirstIndent
      case _ =>
        Indent.getNoneIndent
    }
  }