def createMethodFromSettings()

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


  def createMethodFromSettings(settings: ScalaExtractMethodSettings): ScFunction = {
    val accessMod  = settings.visibility
    val methodName = settings.methodName
    val target     = settings.elements(0)

    val typeParamsText = typeParametersText(settings)

    def paramText(param: ExtractMethodParameter): String = {
      val ExtractMethodParameter(oldName, _, fromElement, tp, _) = param
      typedName(oldName, tp.canonicalCodeText(fromElement), param.isCallByNameParameter)(fromElement.getProject)
    }

    val parameters = settings.parameters.filter(_.passAsParameter).map(paramText)
    val paramsText = if (parameters.nonEmpty || settings.calcReturnTypeIsUnit) parameters.mkString("(", ", ", ")") else ""

    val project = target.getProject
    val codeStyleSettings = ScalaCodeStyleSettings.getInstance(project)

    val retType = {
      val typeBuilder = new mutable.StringBuilder()

      val appendType =
        settings.calcReturnTypeIsUnit && codeStyleSettings.TYPE_ANNOTATION_UNIT_TYPE ||
          settings.addReturnType == ScalaApplicationSettings.ReturnTypeLevel.ADD ||
          settings.addReturnType == ScalaApplicationSettings.ReturnTypeLevel.BY_CODE_STYLE && isTypeAnnotationRequiredFor(settings, settings.visibility)

      val appendEqualSign = appendType || // if we append type, we cannot use procedure syntax
        !settings.calcReturnTypeIsUnit ||
        codeStyleSettings.ENFORCE_FUNCTIONAL_SYNTAX_FOR_UNIT ||
        target.isInScala3File // since 3.0 procedure syntax is not supported

      if (appendType) typeBuilder.append(": ").append(settings.calcReturnTypeText)
      if (appendEqualSign) typeBuilder.append(" = ")

      typeBuilder.result()
    }

    val notPassedParams = settings.parameters.filter(p => !p.passAsParameter).map {
      case ExtractMethodParameter(oldName, _, fromElement, tp, _) =>
        val nameAndType = typedName(oldName, tp.canonicalCodeText(fromElement))(fromElement.getProject)
      s"val $nameAndType = ???\n"
    }
    val notPassedParamsText = notPassedParams.mkString

    val elementsToAdd: Iterator[PsiElement] = settings.elements.toSeq match {
      case Seq(x: ScBlockExpr) => x.children.toSeq.drop(1).dropRight(1).iterator // drop '{' and '}'
      case x => x.iterator
    }
    val elementsText = elementsToAdd.map(_.getText).mkString("")

    def byOutputsSize(ifZero: => String, ifOne: => String, ifMany: => String): String = {
      settings.outputs.length match {
        case 0 => ifZero
        case 1 => ifOne
        case _ => ifMany
      }
    }

    val returnText =
      if (settings.lastReturn) ""
      else {
        def params = settings.outputs.map(_.paramName).mkString("(", ", ", ")")
        def multipleReturnText = {
          val ics = settings.innerClassSettings
          if (!ics.needClass) params //tuple
          else if (ics.isCase) s"${ics.className}$params"
          else s"new ${ics.className}$params"
        }

        settings.returnType match {
          case Some(t) if t.isUnit => byOutputsSize(
            "\nfalse",
            s"\nSome$params",
            s"\nSome($multipleReturnText)")
          case Some(_) => byOutputsSize(
            "\nNone",
            s"\nRight$params",
            s"\nRight($multipleReturnText)")
          case _ => byOutputsSize(
            "",
            s"\n${settings.outputs(0).paramName}",
            s"\n$multipleReturnText")
        }
      }

    val firstPart = s"${accessMod}def $methodName$typeParamsText$paramsText$retType {\n$notPassedParamsText"
    val offset = firstPart.length
    val secondPart = s"$elementsText$returnText\n}"
    val method = createMethodFromText(firstPart + secondPart, target)(target)

    if (!settings.lastReturn) {
      val returnVisitor = new ScalaRecursiveElementVisitor {
        override def visitReturn(ret: ScReturn): Unit = {
          if (!ret.method.contains(method)) return
          val retExprText = ret.expr.map(_.getText).mkString
          val newText = settings.returnType match {
            case Some(t) if t.isUnit => byOutputsSize(
              "true",
              "None",
              "None"
            )
            case Some(_) => byOutputsSize(
              s"Some($retExprText)",
              s"Left($retExprText)",
              s"Left($retExprText)"
            )
            case None => "" //should not occur
          }
          val retElem = createExpressionFromText(s"return $newText", ret)(ret.getManager)
          ret.replace(retElem)
        }
      }
      returnVisitor.visitScalaElement(method: ScalaPsiElement)
    }

    val visitor = new ScalaRecursiveElementVisitor() {
      override def visitReference(ref: ScReference): Unit = {
        ref.bind() match {
          case Some(ScalaResolveResult(named: PsiNamedElement, _: ScSubstitutor)) =>
            if (named.getContainingFile == method.getContainingFile && named.getTextOffset < offset &&
              !named.name.startsWith("_")) {
              val oldName = named.name
              var break = false
              for (param <- settings.parameters if !break) {
                if (param.oldName == oldName) {
                  implicit val projectContext: ProjectContext = method.projectContext
                  def tail(): Unit = {
                    if (param.oldName != param.newName) {
                      val newRef = createExpressionFromText(param.newName, ref)
                      ref.getParent.getNode.replaceChild(ref.getNode, newRef.getNode)
                    }
                  }
                  ref.getParent match {
                    case sect: ScUnderscoreSection if param.isFunction =>
                      val newRef = createExpressionFromText(param.newName, ref)
                      sect.getParent.getNode.replaceChild(sect.getNode, newRef.getNode)
                    case _ if param.isEmptyParamFunction =>
                      ref.getParent match {
                        case ref: ScReference if ref.refName == "apply" => tail()
                        case _: ScMethodCall => tail()
                        case _ =>
                          ref.asInstanceOf[ScExpression].expectedType() match {
                            case Some(FunctionType(_, params)) if params.isEmpty => tail()
                            case _ =>
                              //we need to replace by method call
                              val newRef = createExpressionFromText(s"${param.newName}()", ref)
                              ref.getParent.getNode.replaceChild(ref.getNode, newRef.getNode)
                          }
                      }
                    case _ => tail()
                  }
                  break = true
                }
              }
            }
          case _ =>
        }
        super.visitReference(ref)
      }
    }
    visitor.visitScalaElement(method)

    val bindTo = new ArrayBuffer[(PsiNamedElement, String)]
    val newVisitor = new ScalaRecursiveElementVisitor() {
      override def visitScalaElement(element: ScalaPsiElement): Unit = {
        element match {
          case named: PsiNamedElement if named != method && named.getTextOffset < offset =>
            settings.parameters.find(p => p.oldName == named.name)
              .filter(p => p.oldName != p.newName)
              .foreach(p => bindTo += ((named, p.newName)))
          case _ =>
        }
        super.visitScalaElement(element)
      }
    }
    newVisitor.visitScalaElement(method)
    for ((named, newName) <- bindTo) {
      val id = named.asInstanceOf[ScNamedElement].nameId
      id.getParent.getNode.replaceChild(id.getNode, createIdentifier(newName)(id.getManager))
    }
    method
  }