public static Spacing getSpacing(
      GroovyBlock child1, GroovyBlock child2, CommonCodeStyleSettings settings) {

    ASTNode leftNode = child1.getNode();
    ASTNode rightNode = child2.getNode();
    final PsiElement left = leftNode.getPsi();
    final PsiElement right = rightNode.getPsi();

    IElementType leftType = leftNode.getElementType();
    IElementType rightType = rightNode.getElementType();

    // Braces Placement
    // For multi-line strings
    if (!mirrorsAst(child1) || !mirrorsAst(child2)) {
      return NO_SPACING;
    }

    if (leftType == mGDOC_COMMENT_START && rightType == mGDOC_COMMENT_DATA
        || leftType == mGDOC_COMMENT_DATA && rightType == mGDOC_COMMENT_END) {
      return LAZY_SPACING;
    }

    // For type parameters
    if (mLT == leftType && rightNode.getPsi() instanceof GrTypeParameter
        || mGT == rightType && leftNode.getPsi() instanceof GrTypeParameter
        || mIDENT == leftType && rightNode.getPsi() instanceof GrTypeParameterList) {
      return NO_SPACING;
    }

    // For left parentheses in method declarations or calls
    if (mLPAREN.equals(rightType)
        && rightNode.getPsi().getParent().getNode() != null
        && METHOD_DEFS.contains(rightNode.getPsi().getParent().getNode().getElementType())) {
      return NO_SPACING;
    }

    if (ARGUMENTS.equals(rightType)) {
      return NO_SPACING;
    }
    // For left square bracket in array declarations and selections by index
    if ((mLBRACK.equals(rightType)
            && rightNode.getPsi().getParent().getNode() != null
            && INDEX_OR_ARRAY.contains(rightNode.getPsi().getParent().getNode().getElementType()))
        || ARRAY_DECLARATOR.equals(rightType)) {
      return NO_SPACING;
    }

    if (METHOD_DEFS.contains(leftType)) {
      if (rightType == mSEMI) {
        return NO_SPACING;
      }
      return Spacing.createSpacing(
          0, 0, settings.BLANK_LINES_AROUND_METHOD + 1, settings.KEEP_LINE_BREAKS, 100);
    }

    if (METHOD_DEFS.contains(rightType)) {
      if (leftNode.getElementType() == GROOVY_DOC_COMMENT) {
        return Spacing.createSpacing(
            0, 0, settings.BLANK_LINES_AROUND_METHOD, settings.KEEP_LINE_BREAKS, 0);
      }
      return Spacing.createSpacing(
          0, 0, settings.BLANK_LINES_AROUND_METHOD + 1, settings.KEEP_LINE_BREAKS, 100);
    }

    if (leftType == mLCURLY && rightType == PARAMETERS_LIST) { // closure
      return LAZY_SPACING;
    }

    // For parentheses in arguments and typecasts
    if (LEFT_BRACES.contains(leftType) || RIGHT_BRACES.contains(rightType)) {
      PsiElement parent = (LEFT_BRACES.contains(leftType) ? left : right).getParent();
      boolean shouldHaveSpace =
          parent instanceof GrTypeCastExpression && settings.SPACE_WITHIN_CAST_PARENTHESES;
      return shouldHaveSpace ? COMMON_SPACING : NO_SPACING_WITH_NEWLINE;
    }
    // For type parameters
    if ((mLT.equals(leftType) || mGT.equals(rightType))
        && leftNode.getPsi().getParent() != null
        && leftNode.getPsi().getParent() instanceof GrTypeArgumentList) {
      return NO_SPACING_WITH_NEWLINE;
    }

    if (rightNode.getPsi() != null && rightNode.getPsi() instanceof GrTypeArgumentList) {
      return NO_SPACING_WITH_NEWLINE;
    }

    if (rightType == mCOLON
        && left instanceof GrParameter
        && left.getParent() instanceof GrForInClause) {
      return COMMON_SPACING;
    }

    /** ******** punctuation marks *********** */
    // For dots, commas etc.
    if ((PUNCTUATION_SIGNS.contains(rightType))
        || (mCOLON.equals(rightType)
            && !(rightNode.getPsi().getParent() instanceof GrConditionalExpression))) {
      return NO_SPACING_WITH_NEWLINE;
    }

    if (DOTS.contains(leftType)) {
      return NO_SPACING_WITH_NEWLINE;
    }

    /** ******** imports *********** */
    if (IMPORT_STATEMENT.equals(leftType) && IMPORT_STATEMENT.equals(rightType)) {
      return IMPORT_BETWEEN_SPACING;
    }
    if ((IMPORT_STATEMENT.equals(leftType)
            && (!IMPORT_STATEMENT.equals(rightType) && !mSEMI.equals(rightType)))
        || ((!IMPORT_STATEMENT.equals(leftType) && !mSEMI.equals(leftType))
            && IMPORT_STATEMENT.equals(rightType))) {
      return IMPORT_OTHER_SPACING;
    }

    // todo:check it for multiple assignments
    if ((VARIABLE_DEFINITION.equals(leftType) || VARIABLE_DEFINITION.equals(rightType))
        && !(leftNode.getTreeNext() instanceof PsiErrorElement)) {
      return Spacing.createSpacing(0, 0, 1, false, 100);
    }

    /** ******** exclusions *********** */
    // For << and >> ...
    if ((mLT.equals(leftType) && mLT.equals(rightType))
        || (mGT.equals(leftType) && mGT.equals(rightType))) {
      return NO_SPACING_WITH_NEWLINE;
    }

    // Unary and postfix expressions
    if (PREFIXES.contains(leftType)
        || POSTFIXES.contains(rightType)
        || (PREFIXES_OPTIONAL.contains(leftType)
            && leftNode.getPsi().getParent() instanceof GrUnaryExpression)) {
      return NO_SPACING_WITH_NEWLINE;
    }

    if (RANGES.contains(leftType) || RANGES.contains(rightType)) {
      return NO_SPACING_WITH_NEWLINE;
    }

    // For Gstrings and regexes
    if (left.getParent() != null
        && left.getParent().equals(right.getParent())
        && (left.getParent() instanceof GrString
            || leftNode.getTreeParent().getElementType() == mREGEX_LITERAL
            || leftNode.getTreeParent().getElementType() == mDOLLAR_SLASH_REGEX_LITERAL)) {
      return NO_SPACING;
    }
    if (isDollarInGStringInjection(leftNode) || isDollarInGStringInjection(rightNode)) {
      return NO_SPACING;
    }
    if (leftNode.getPsi().getParent() instanceof GrStringInjection
        && rightNode.getPsi().getParent() instanceof GrString
        && rightNode.getPsi().getParent().equals(leftNode.getPsi().getParent().getParent())) {
      return NO_SPACING;
    }

    if (mGDOC_ASTERISKS == leftType && mGDOC_COMMENT_DATA == rightType) {
      String text = rightNode.getText();
      if (text.length() > 0 && !text.startsWith(" ")) {
        return COMMON_SPACING;
      }
      return NO_SPACING;
    }

    if (leftType == mGDOC_TAG_VALUE_TOKEN && rightType == mGDOC_COMMENT_DATA) {
      return LAZY_SPACING;
    }

    if (left instanceof GrStatement
        && right instanceof GrStatement
        && left.getParent() instanceof GrStatementOwner
        && right.getParent() instanceof GrStatementOwner) {
      return COMMON_SPACING_WITH_NL;
    }

    if (rightType == mGDOC_INLINE_TAG_END
        || leftType == mGDOC_INLINE_TAG_START
        || rightType == mGDOC_INLINE_TAG_START
        || leftType == mGDOC_INLINE_TAG_END) {
      return NO_SPACING;
    }

    if ((leftType == GDOC_INLINED_TAG && rightType == mGDOC_COMMENT_DATA)
        || (leftType == mGDOC_COMMENT_DATA && rightType == GDOC_INLINED_TAG)) {
      // Keep formatting between groovy doc text and groovy doc reference tag as is.
      return NO_SPACING;
    }

    if (leftType == CLASS_TYPE_ELEMENT && rightType == mTRIPLE_DOT) {
      return NO_SPACING;
    }

    // diamonds
    if (rightType == mLT || rightType == mGT) {
      if (right.getParent() instanceof GrCodeReferenceElement) {
        PsiElement p = right.getParent().getParent();
        if (p instanceof GrNewExpression || p instanceof GrAnonymousClassDefinition) {
          return NO_SPACING;
        }
      }
    }

    if (leftType == mRPAREN && left.getParent() instanceof GrTypeCastExpression) {
      return settings.SPACE_AFTER_TYPE_CAST ? COMMON_SPACING : NO_SPACING;
    }

    return COMMON_SPACING;
  }
  /**
   * Calculates indent, based on code style, between parent block and child node
   *
   * @param parent parent block
   * @param child child node
   * @return indent
   */
  @NotNull
  public static Indent getChildIndent(
      @NotNull final GroovyBlock parent, @NotNull final ASTNode child) {
    ASTNode astNode = parent.getNode();
    final PsiElement psiParent = astNode.getPsi();

    // For Groovy file
    if (psiParent instanceof GroovyFileBase) {
      return Indent.getNoneIndent();
    }

    if (psiParent instanceof GrMethod && child.getPsi() instanceof GrParameterList) {
      return Indent.getContinuationIndent();
    }

    if (GSTRING_TOKENS_INNER.contains(child.getElementType())
        && mGSTRING_BEGIN != child.getElementType()) {
      return Indent.getAbsoluteNoneIndent();
    }

    if (psiParent instanceof GrListOrMap) {
      if (mLBRACK.equals(child.getElementType()) || mRBRACK.equals(child.getElementType())) {
        return Indent.getNoneIndent();
      } else {
        return Indent.getContinuationWithoutFirstIndent();
      }
    }

    // For common code block
    if (BLOCK_SET.contains(astNode.getElementType())
        && !BLOCK_STATEMENT.equals(astNode.getElementType())) {
      return indentForBlock(psiParent, child);
    }

    if (CASE_SECTION.equals(astNode.getElementType())) {
      return indentForCaseSection(child);
    }

    if (SWITCH_STATEMENT.equals(astNode.getElementType())) {
      return indentForSwitchStatement(psiParent, child);
    }

    if (psiParent instanceof GrLabeledStatement) {
      if (child.getPsi() instanceof GrLabel) {
        CommonCodeStyleSettings.IndentOptions indentOptions =
            parent.getSettings().getIndentOptions();
        if (indentOptions.LABEL_INDENT_ABSOLUTE) {
          return Indent.getAbsoluteLabelIndent();
        }
        return Indent.getLabelIndent();
      }
    }

    // for control structures
    if (psiParent instanceof GrControlStatement) {
      return getControlIndent(psiParent, child);
    }

    if (psiParent instanceof GrExpression) {
      return getExpressionIndent(psiParent, child);
    }
    if (psiParent instanceof GrVariable
        && child.getPsi() == ((GrVariable) psiParent).getInitializerGroovy()) {
      return Indent.getNormalIndent();
    }

    // For parameter lists
    if (psiParent instanceof GrParameterList
        || psiParent instanceof GrExtendsClause
        || psiParent instanceof GrThrowsClause) {
      if (parent.getIndent() != null) {
        return Indent.getContinuationWithoutFirstIndent();
      }
      return Indent.getNoneIndent();
    }

    // For arguments
    if (psiParent instanceof GrArgumentList) {
      if (child.getElementType() != mLPAREN
          && child.getElementType() != mRPAREN /*&& child.getElementType() != mCOMMA*/) {
        return Indent.getContinuationWithoutFirstIndent();
      }
    }

    if ((psiParent instanceof GrDocComment && child.getElementType() != mGDOC_COMMENT_START)
        || psiParent instanceof GrDocTag && child.getElementType() != mGDOC_TAG_NAME) {
      return Indent.getSpaceIndent(GDOC_COMMENT_INDENT);
    }

    if (psiParent instanceof GrNamedArgument
        && child.getPsi() == ((GrNamedArgument) psiParent).getExpression()) {
      return Indent.getContinuationIndent();
    }

    if (child.getPsi() instanceof GrVariable && psiParent instanceof GrVariableDeclaration) {
      return Indent.getContinuationWithoutFirstIndent();
    }

    return Indent.getNoneIndent();
  }
 private static boolean mirrorsAst(GroovyBlock block) {
   return block.getNode().getTextRange().equals(block.getTextRange())
       || block instanceof MethodCallWithoutQualifierBlock;
 }