def replaceWithMethodCall()

in scala/scala-impl/src/org/jetbrains/plugins/scala/lang/refactoring/extractMethod/ScalaExtractMethodUtils.scala [371:536]


  def replaceWithMethodCall(settings: ScalaExtractMethodSettings,
                            elements: Seq[PsiElement],
                            parameterText: ExtractMethodParameter => String,
                            outputName: ExtractMethodOutput => String,
                            typeAdjuster: TypeAdjuster): Unit = {
    implicit val projectContext: ProjectContext = settings.projectContext

    val target = elements.head
    val element = elements.findByType[ScalaPsiElement].getOrElse(return)
    val processor = new CompletionProcessor(StdKinds.refExprLastRef, element) {
      override val includePrefixImports: Boolean = false
    }
    PsiTreeUtil.treeWalkUp(processor, element, null, ScalaResolveState.empty)
    val allNames = new mutable.HashSet[String]()
    allNames ++= processor.candidatesS.map(rr => rr.element.name)
    def generateFreshName(s: String): String = {
      var freshName = s
      var count = 0
      while (allNames.contains(freshName)) {
        count += 1
        freshName = s + count
      }
      freshName
    }
    val mFreshName = generateFreshName(settings.methodName + "Result")

    val params = settings.parameters.filter(_.passAsParameter)
      .map(param => parameterText(param) + (if (param.isFunction) " _" else ""))

    val paramsText = if (params.nonEmpty || settings.calcReturnTypeIsUnit) params.mkString("(", ", ", ")") else ""
    val methodCallText = s"${settings.methodName}$paramsText"
    var needExtractorsFromMultipleReturn = false

    val outputTypedNames = settings.outputs.map { o =>
      val fromElement = o.fromElement
      ScalaExtractMethodUtils.typedName(outputName(o), o.returnType.canonicalCodeText(fromElement))(fromElement.getProject)
    }
    val ics = settings.innerClassSettings

    def patternForDeclaration: String = {
      if (ics.needClass) return s"$mFreshName: ${ics.className}"

      if (outputTypedNames.length == 0) ""
      else if (outputTypedNames.length == 1) outputTypedNames(0)
      else outputTypedNames.mkString("(", ", ", ")")
    }

    def insertCallStmt(): PsiElement = {
      def insertExpression(text: String): PsiElement = {
        val expr = createExpressionFromText(text, target)
        target.replace(expr)
      }
      if (settings.lastReturn) insertExpression(s"return $methodCallText")
      else if (settings.outputs.length == 0) {
        val exprText = settings.returnType match {
          case None => methodCallText
          case Some(t) if t.isUnit => s"if ($methodCallText) return"
          case Some(_) =>
            val arrow = ScalaPsiUtil.functionArrow
            s"""$methodCallText match {
               |  case Some(toReturn) $arrow return toReturn
               |  case None $arrow
               |}""".stripMargin.replace("\r", "")
        }
        insertExpression(exprText)
      }
      else {
        val (pattern, isVal) = settings.outputs match {
          case _ if ics.needClass =>
            needExtractorsFromMultipleReturn = true
            (patternForDeclaration, true)
          case outputs if outputs.forall(_.isVal) =>
            (patternForDeclaration, true)
          case outputs if outputs.forall(!_.isVal) =>
            (patternForDeclaration, false)
          case _ =>
            needExtractorsFromMultipleReturn = true
            val typeText = ScalaExtractMethodUtils.outputTypeText(settings)
            (s"$mFreshName: $typeText", true)
        }
        val arrow = ScalaPsiUtil.functionArrow
        val exprText = settings.returnType match {
          case None => methodCallText
          case Some(t) if t.isUnit =>
            s"""$methodCallText match {
               |  case Some(result) $arrow result
               |  case None $arrow return
               |}""".stripMargin.replace("\r", "")
          case Some(_) =>
            s"""$methodCallText match {
               |  case Left(toReturn) $arrow return toReturn
               |  case Right(result) $arrow result
               |}""".stripMargin.replace("\r", "")
        }
        val expr = createExpressionFromText(exprText, target)
        val declaration = createDeclaration(pattern, "", isVariable = !isVal, expr, target)
        val result = target.replace(declaration)
        result
      }
    }

    def insertAssignsFromMultipleReturn(element: PsiElement): Unit = {
      if (!needExtractorsFromMultipleReturn) return

      var lastElem: PsiElement = element
      implicit val tpc: TypePresentationContext = TypePresentationContext(lastElem.getParent)
      def addElement(elem: PsiElement) = {
        lastElem = lastElem.getParent.addAfter(elem, lastElem)
        lastElem.getParent.addBefore(createNewLine()(elem.getManager), lastElem)
        lastElem
      }

      def addAssignment(ret: ExtractMethodOutput, exprText: String): Unit = {
        val stmt =
          if (ret.needNewDefinition) createDeclaration(ret.returnType, ret.paramName, !ret.isVal, exprText, target)
          else createExpressionFromText(ret.paramName + " = " + exprText, target)

        addElement(stmt)
      }

      val allVals = settings.outputs.forall(_.isVal)
      val allVars = settings.outputs.forall(!_.isVal)

      def addExtractorsFromCaseClass(): Unit = {
        if (allVals || allVars) {
          val patternArgsText = outputTypedNames.mkString("(", ", ", ")")
          val patternText = ics.className + patternArgsText
          val expr = createExpressionFromText(mFreshName, target)
          val stmt = createDeclaration(patternText, "", isVariable = allVars, expr, target)
          addElement(stmt)
        } else {
          addExtractorsFromClass()
        }
      }

      def addExtractorsFromClass(): Unit = {
        for (ret <- settings.outputs) {
          val exprText = s"$mFreshName.${ret.paramName}"
          addAssignment(ret, exprText)
        }
      }

      if (!ics.needClass) {
        var count = 1
        for (ret <- settings.outputs) {
          val exprText = s"$mFreshName._$count"
          addAssignment(ret, exprText)
          count += 1
        }
      }
      else if (ics.isCase) addExtractorsFromCaseClass()
      else addExtractorsFromClass()
    }

    def removeReplacedElements(): Unit = {
      val valid = elements.dropWhile(!_.isValid)
      if (valid.isEmpty) return

      valid.head.getParent.deleteChildRange(valid.head, valid.last)
    }

    val stmt = insertCallStmt()
    insertAssignsFromMultipleReturn(stmt)
    removeReplacedElements()
    typeAdjuster.markToAdjust(stmt.getParent)
  }