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)
}