<commit_before>package org.jetbrains.plugins.scala
package lang
package formatting
package processors

import com.intellij.formatting.Spacing
import com.intellij.lang.ASTNode
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.TextRange
import com.intellij.psi.codeStyle.CommonCodeStyleSettings
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.javadoc.PsiDocComment
import com.intellij.psi.tree.TokenSet
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi._
import org.jetbrains.plugins.scala.extensions._
import org.jetbrains.plugins.scala.lang.formatting.settings.ScalaCodeStyleSettings
import org.jetbrains.plugins.scala.lang.lexer.{ScalaTokenTypes, ScalaTokenTypesEx, ScalaXmlTokenTypes}
import org.jetbrains.plugins.scala.lang.parser.ScalaElementTypes
import org.jetbrains.plugins.scala.lang.psi.ScalaPsiUtil
import org.jetbrains.plugins.scala.lang.psi.api.ScalaFile
import org.jetbrains.plugins.scala.lang.psi.api.base._
import org.jetbrains.plugins.scala.lang.psi.api.base.patterns._
import org.jetbrains.plugins.scala.lang.psi.api.base.types._
import org.jetbrains.plugins.scala.lang.psi.api.expr._
import org.jetbrains.plugins.scala.lang.psi.api.expr.xml.ScXmlPattern
import org.jetbrains.plugins.scala.lang.psi.api.statements._
import org.jetbrains.plugins.scala.lang.psi.api.statements.params._
import org.jetbrains.plugins.scala.lang.psi.api.toplevel.ScEarlyDefinitions
import org.jetbrains.plugins.scala.lang.psi.api.toplevel.imports.{ScImportSelectors, ScImportStmt}
import org.jetbrains.plugins.scala.lang.psi.api.toplevel.packaging._
import org.jetbrains.plugins.scala.lang.psi.api.toplevel.templates._
import org.jetbrains.plugins.scala.lang.psi.api.toplevel.typedef._
import org.jetbrains.plugins.scala.lang.refactoring.util.ScalaNamesUtil
import org.jetbrains.plugins.scala.lang.scaladoc.lexer.ScalaDocTokenType
import org.jetbrains.plugins.scala.lang.scaladoc.parser.ScalaDocElementTypes
import org.jetbrains.plugins.scala.lang.scaladoc.psi.api.ScDocComment
import org.jetbrains.plugins.scala.util.MultilineStringUtil

import scala.annotation.tailrec

object ScalaSpacingProcessor extends ScalaTokenTypes {
  private val LOG = Logger.getInstance("#org.jetbrains.plugins.scala.lang.formatting.processors.ScalaSpacingProcessor")

  val BLOCK_ELEMENT_TYPES = {
    import org.jetbrains.plugins.scala.lang.parser.ScalaElementTypes._
    TokenSet.create(BLOCK_EXPR, TEMPLATE_BODY, PACKAGING, TRY_BLOCK, MATCH_STMT, CATCH_BLOCK)
  }

  private def getText(node: ASTNode, fileText: String): String = {
    node.getTextRange.substring(fileText)
  }

  private def nextNotWithspace(elem: PsiElement): PsiElement = {
    var next = elem.getNextSibling
    while (next != null && (next.isInstanceOf[PsiWhiteSpace] ||
            next.getNode.getElementType == ScalaTokenTypes.tWHITE_SPACE_IN_LINE)) next = next.getNextSibling
    next
  }

  private def prevNotWithspace(elem: PsiElement): PsiElement = {
    var prev = elem.getPrevSibling
    while (prev != null && (prev.isInstanceOf[PsiWhiteSpace] ||
            prev.getNode.getElementType == ScalaTokenTypes.tWHITE_SPACE_IN_LINE)) prev = prev.getPrevSibling
    prev
  }

  private def spacesToPreventNewIds(left: ScalaBlock, right: ScalaBlock, fileText: String, textRange: TextRange): Integer = {
    if (ScalaXmlTokenTypes.XML_ELEMENTS.contains(left.getNode.getElementType) ||
      ScalaXmlTokenTypes.XML_ELEMENTS.contains(right.getNode.getElementType)) return 0
    @tailrec
    def dfsChildren(currentNode: ASTNode, getChildren: ASTNode => List[ASTNode]): ASTNode = {
      getChildren(currentNode) find (_.getTextLength > 0) match {
        case Some(next) => dfsChildren(next, getChildren)
        case None => currentNode
      }
    }
    val leftNode = dfsChildren(left.myLastNode.getOrElse(left.getNode), _.getChildren(null).toList.reverse)
    val rightNode = dfsChildren(right.getNode, _.getChildren(null).toList)
    val concatString = if (textRange.contains(rightNode.getTextRange) && textRange.contains(leftNode.getTextRange)) {
      leftNode.getTextRange.substring(fileText) + rightNode.getTextRange.substring(fileText)
    } else return 0
    (leftNode.getTreeParent.getElementType, rightNode.getTreeParent.getElementType) match {
      case (ScalaElementTypes.INTERPOLATED_STRING_LITERAL, _) => 0
      case (_, ScalaElementTypes.INTERPOLATED_STRING_LITERAL) => 0
      case _ => if (ScalaNamesUtil.isIdentifier(concatString) || ScalaNamesUtil.isKeyword(concatString)) 1 else 0
    }
  }

  def getSpacing(left: ScalaBlock, right: ScalaBlock): Spacing = {
    val settings = right.getCommonSettings
    val keepBlankLinesInCode = settings.KEEP_BLANK_LINES_IN_CODE
    val keepLineBreaks = settings.KEEP_LINE_BREAKS
    val keepBlankLinesInDeclarations = settings.KEEP_BLANK_LINES_IN_DECLARATIONS
    val keepBlankLinesBeforeRBrace = settings.KEEP_BLANK_LINES_BEFORE_RBRACE
    def getSpacing(x: Int, y: Int, z: Int) = {
      if (keepLineBreaks) Spacing.createSpacing(y, y, z, true, x)
      else Spacing.createSpacing(y, y, z, false, 0)
    }
    if (left == null) {
      return getSpacing(keepBlankLinesInCode, 0, 0) //todo:
    }
    val scalaSettings: ScalaCodeStyleSettings =
      left.getSettings.getCustomSettings(classOf[ScalaCodeStyleSettings])
    def getDependentLFSpacing(x: Int, y: Int, range: TextRange) = {
      if (keepLineBreaks) Spacing.createDependentLFSpacing(y, y, range, true, x)
      else Spacing.createDependentLFSpacing(y, y, range, false, 0)
    }
    val leftNode = left.getNode
    val rightNode = right.getNode
    val fileText = PsiDocumentManager.getInstance(leftNode.getPsi.getProject).
      getDocument(leftNode.getPsi.getContainingFile).getText//leftNode.getPsi.getContainingFile.getText

    //new formatter spacing
    val leftElementType = leftNode.getElementType
    val rightElementType = rightNode.getElementType
    val leftPsi = leftNode.getPsi
    val rightPsi = rightNode.getPsi
    val fileTextRange = new TextRange(0, fileText.length())

    /**
     * This is not nodes text! This is blocks text, which can be different from node.
     */
    val (leftString, rightString) =
      if (!fileTextRange.contains(left.getTextRange) ||
        !fileTextRange.contains(right.getTextRange)) {
        LOG.error("File text: \n%s\n\nDoesn't contains nodes:\n(%s, %s)".
          format(fileText, leftPsi.getText, rightPsi.getText))
        (leftPsi.getText, rightPsi.getText)
      } else (left.getTextRange.substring(fileText),
            right.getTextRange.substring(fileText))

    val spacesMin: Integer = spacesToPreventNewIds(left, right, fileText, fileTextRange)
    val WITHOUT_SPACING = getSpacing(keepBlankLinesInCode, spacesMin, 0)
    val WITHOUT_SPACING_NO_KEEP = Spacing.createSpacing(spacesMin, spacesMin, 0, false, 0)
    val WITHOUT_SPACING_DEPENDENT = (range: TextRange) => getDependentLFSpacing(keepBlankLinesInCode, spacesMin, range)
    val WITH_SPACING = getSpacing(keepBlankLinesInCode, 1, 0)
    val WITH_SPACING_NO_KEEP = Spacing.createSpacing(1, 1, 0, false, 0)
    val WITH_SPACING_DEPENDENT = (range: TextRange) => getDependentLFSpacing(keepBlankLinesInCode, 1, range)<commit_msg>Reverted daf49fe due to critical NPE on new class creation.<commit_after>package org.jetbrains.plugins.scala
package lang
package formatting
package processors

import com.intellij.formatting.Spacing
import com.intellij.lang.ASTNode
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.TextRange
import com.intellij.psi.codeStyle.CommonCodeStyleSettings
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.javadoc.PsiDocComment
import com.intellij.psi.tree.TokenSet
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi._
import org.jetbrains.plugins.scala.extensions._
import org.jetbrains.plugins.scala.lang.formatting.settings.ScalaCodeStyleSettings
import org.jetbrains.plugins.scala.lang.lexer.{ScalaTokenTypes, ScalaTokenTypesEx, ScalaXmlTokenTypes}
import org.jetbrains.plugins.scala.lang.parser.ScalaElementTypes
import org.jetbrains.plugins.scala.lang.psi.ScalaPsiUtil
import org.jetbrains.plugins.scala.lang.psi.api.ScalaFile
import org.jetbrains.plugins.scala.lang.psi.api.base._
import org.jetbrains.plugins.scala.lang.psi.api.base.patterns._
import org.jetbrains.plugins.scala.lang.psi.api.base.types._
import org.jetbrains.plugins.scala.lang.psi.api.expr._
import org.jetbrains.plugins.scala.lang.psi.api.expr.xml.ScXmlPattern
import org.jetbrains.plugins.scala.lang.psi.api.statements._
import org.jetbrains.plugins.scala.lang.psi.api.statements.params._
import org.jetbrains.plugins.scala.lang.psi.api.toplevel.ScEarlyDefinitions
import org.jetbrains.plugins.scala.lang.psi.api.toplevel.imports.{ScImportSelectors, ScImportStmt}
import org.jetbrains.plugins.scala.lang.psi.api.toplevel.packaging._
import org.jetbrains.plugins.scala.lang.psi.api.toplevel.templates._
import org.jetbrains.plugins.scala.lang.psi.api.toplevel.typedef._
import org.jetbrains.plugins.scala.lang.refactoring.util.ScalaNamesUtil
import org.jetbrains.plugins.scala.lang.scaladoc.lexer.ScalaDocTokenType
import org.jetbrains.plugins.scala.lang.scaladoc.parser.ScalaDocElementTypes
import org.jetbrains.plugins.scala.lang.scaladoc.psi.api.ScDocComment
import org.jetbrains.plugins.scala.util.MultilineStringUtil

import scala.annotation.tailrec

object ScalaSpacingProcessor extends ScalaTokenTypes {
  private val LOG = Logger.getInstance("#org.jetbrains.plugins.scala.lang.formatting.processors.ScalaSpacingProcessor")

  val BLOCK_ELEMENT_TYPES = {
    import org.jetbrains.plugins.scala.lang.parser.ScalaElementTypes._
    TokenSet.create(BLOCK_EXPR, TEMPLATE_BODY, PACKAGING, TRY_BLOCK, MATCH_STMT, CATCH_BLOCK)
  }

  private def getText(node: ASTNode, fileText: String): String = {
    node.getTextRange.substring(fileText)
  }

  private def nextNotWithspace(elem: PsiElement): PsiElement = {
    var next = elem.getNextSibling
    while (next != null && (next.isInstanceOf[PsiWhiteSpace] ||
            next.getNode.getElementType == ScalaTokenTypes.tWHITE_SPACE_IN_LINE)) next = next.getNextSibling
    next
  }

  private def prevNotWithspace(elem: PsiElement): PsiElement = {
    var prev = elem.getPrevSibling
    while (prev != null && (prev.isInstanceOf[PsiWhiteSpace] ||
            prev.getNode.getElementType == ScalaTokenTypes.tWHITE_SPACE_IN_LINE)) prev = prev.getPrevSibling
    prev
  }

  private def spacesToPreventNewIds(left: ScalaBlock, right: ScalaBlock, fileText: String, textRange: TextRange): Integer = {
    if (ScalaXmlTokenTypes.XML_ELEMENTS.contains(left.getNode.getElementType) ||
      ScalaXmlTokenTypes.XML_ELEMENTS.contains(right.getNode.getElementType)) return 0
    @tailrec
    def dfsChildren(currentNode: ASTNode, getChildren: ASTNode => List[ASTNode]): ASTNode = {
      getChildren(currentNode) find (_.getTextLength > 0) match {
        case Some(next) => dfsChildren(next, getChildren)
        case None => currentNode
      }
    }
    val leftNode = dfsChildren(left.myLastNode.getOrElse(left.getNode), _.getChildren(null).toList.reverse)
    val rightNode = dfsChildren(right.getNode, _.getChildren(null).toList)
    val concatString = if (textRange.contains(rightNode.getTextRange) && textRange.contains(leftNode.getTextRange)) {
      leftNode.getTextRange.substring(fileText) + rightNode.getTextRange.substring(fileText)
    } else return 0
    (leftNode.getTreeParent.getElementType, rightNode.getTreeParent.getElementType) match {
      case (ScalaElementTypes.INTERPOLATED_STRING_LITERAL, _) => 0
      case (_, ScalaElementTypes.INTERPOLATED_STRING_LITERAL) => 0
      case _ => if (ScalaNamesUtil.isIdentifier(concatString) || ScalaNamesUtil.isKeyword(concatString)) 1 else 0
    }
  }

  def getSpacing(left: ScalaBlock, right: ScalaBlock): Spacing = {
    val settings = right.getCommonSettings
    val keepBlankLinesInCode = settings.KEEP_BLANK_LINES_IN_CODE
    val keepLineBreaks = settings.KEEP_LINE_BREAKS
    val keepBlankLinesInDeclarations = settings.KEEP_BLANK_LINES_IN_DECLARATIONS
    val keepBlankLinesBeforeRBrace = settings.KEEP_BLANK_LINES_BEFORE_RBRACE
    def getSpacing(x: Int, y: Int, z: Int) = {
      if (keepLineBreaks) Spacing.createSpacing(y, y, z, true, x)
      else Spacing.createSpacing(y, y, z, false, 0)
    }
    if (left == null) {
      return getSpacing(keepBlankLinesInCode, 0, 0) //todo:
    }
    val scalaSettings: ScalaCodeStyleSettings =
      left.getSettings.getCustomSettings(classOf[ScalaCodeStyleSettings])
    def getDependentLFSpacing(x: Int, y: Int, range: TextRange) = {
      if (keepLineBreaks) Spacing.createDependentLFSpacing(y, y, range, true, x)
      else Spacing.createDependentLFSpacing(y, y, range, false, 0)
    }
    val leftNode = left.getNode
    val rightNode = right.getNode
    val fileText = leftNode.getPsi.getContainingFile.getText

    //new formatter spacing
    val leftElementType = leftNode.getElementType
    val rightElementType = rightNode.getElementType
    val leftPsi = leftNode.getPsi
    val rightPsi = rightNode.getPsi
    val fileTextRange = new TextRange(0, fileText.length())

    /**
     * This is not nodes text! This is blocks text, which can be different from node.
     */
    val (leftString, rightString) =
      if (!fileTextRange.contains(left.getTextRange) ||
        !fileTextRange.contains(right.getTextRange)) {
        LOG.error("File text: \n%s\n\nDoesn't contains nodes:\n(%s, %s)".
          format(fileText, leftPsi.getText, rightPsi.getText))
        (leftPsi.getText, rightPsi.getText)
      } else (left.getTextRange.substring(fileText),
            right.getTextRange.substring(fileText))

    val spacesMin: Integer = spacesToPreventNewIds(left, right, fileText, fileTextRange)
    val WITHOUT_SPACING = getSpacing(keepBlankLinesInCode, spacesMin, 0)
    val WITHOUT_SPACING_NO_KEEP = Spacing.createSpacing(spacesMin, spacesMin, 0, false, 0)
    val WITHOUT_SPACING_DEPENDENT = (range: TextRange) => getDependentLFSpacing(keepBlankLinesInCode, spacesMin, range)
    val WITH_SPACING = getSpacing(keepBlankLinesInCode, 1, 0)
    val WITH_SPACING_NO_KEEP = Spacing.createSpacing(1, 1, 0, false, 0)
    val WITH_SPACING_DEPENDENT = (range: TextRange) => getDependentLFSpacing(keepBlankLinesInCode, 1, range)
{'fraction_non_alphanumeric': 0.10029292788394477, 'fraction_numerical': 0.0032082577765378713, 'mean_word_length': 4.742490989187025, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4640470', 'n_tokens_mistral': 4797, 'n_tokens_neox': 4598, 'n_words': 791}