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
}