Refactored ReplaceExplicitFunctionLiteralParamWithItIntention

This commit is contained in:
Valentin Kipyatkov
2015-04-28 18:57:57 +03:00
parent 65d59467a8
commit c7937f181b
4 changed files with 53 additions and 60 deletions
@@ -296,8 +296,6 @@ replace.with.infix.function.call.intention=Replace with infix function call
replace.with.infix.function.call.intention.family=Replace with Infix Function Call
replace.with.infix.function.call.intention.error.resolution.failed=The element cannot be resolved
replace.with.infix.function.call.intention.error.package.call=Cannot be applied with a package as the receiver
replace.explicit.function.literal.param.with.it=Replace explicit parameter ''{0}'' with ''it''
replace.explicit.function.literal.param.with.it.family=Replace Explicit Parameter with 'it'
replace.it.with.explicit.function.literal.param=Replace 'it' with explicit parameter
replace.it.with.explicit.function.literal.param.family=Replace 'it' with Explicit Parameter
split.if=Split into 2 if's
@@ -21,93 +21,78 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.codeStyle.CodeStyleManager
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.refactoring.rename.RenameProcessor
import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
import org.jetbrains.kotlin.idea.JetBundle
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.references.JetReference
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.psi.JetFunctionLiteral
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
public class ReplaceExplicitFunctionLiteralParamWithItIntention() : PsiElementBaseIntentionAction() {
override fun invoke(project: Project, editor: Editor, element: PsiElement) {
val funcExpr = findFunctionLiteralToActOn(element)!!
val cursorWasOverParameterList = PsiTreeUtil.getParentOfType(element, javaClass<JetParameter>()) != null
ParamRenamingProcessor(editor, funcExpr, cursorWasOverParameterList).run()
}
override fun getFamilyName() = "Replace explicit lambda parameter with 'it'"
override fun isAvailable(project: Project, editor: Editor, element: PsiElement): Boolean {
val funcExpr = findFunctionLiteralToActOn(element)
if (funcExpr == null || funcExpr.getValueParameters().size() != 1) {
return false
}
val functionLiteral = targetFunctionLiteral(element, editor.getCaretModel().getOffset()) ?: return false
val parameter = funcExpr.getValueParameters().first()
if (parameter.getTypeReference() != null) {
return false
}
val parameter = functionLiteral.getValueParameters().singleOrNull() ?: return false
if (parameter.getTypeReference() != null) return false
setText(JetBundle.message("replace.explicit.function.literal.param.with.it", parameter.getName()))
setText("Replace explicit parameter '${parameter.getName()}' with 'it'")
return true
}
override fun getFamilyName(): String {
return JetBundle.message("replace.explicit.function.literal.param.with.it.family")
override fun invoke(project: Project, editor: Editor, element: PsiElement) {
val caretOffset = editor.getCaretModel().getOffset()
val functionLiteral = targetFunctionLiteral(element, editor.getCaretModel().getOffset())!!
val cursorInParameterList = functionLiteral.getValueParameterList()!!.getTextRange().containsOffset(caretOffset)
ParamRenamingProcessor(editor, functionLiteral, cursorInParameterList).run()
}
private fun findFunctionLiteralToActOn(element: PsiElement): JetFunctionLiteral? {
if (PsiTreeUtil.getParentOfType(element, javaClass<JetFunctionLiteralExpression>()) == null) {
return null
private fun targetFunctionLiteral(element: PsiElement, caretOffset: Int): JetFunctionLiteral? {
val innermostFunctionLiteral = element.getParentOfType<JetFunctionLiteral>(true) ?: return null
val expression = element.getParentOfType<JetSimpleNameExpression>(true)
if (expression != null) {
val reference = expression.getReference() as JetReference?
val target = reference?.resolveToDescriptors(expression.analyze())?.firstOrNull() as? ParameterDescriptor ?: return null
val functionDescriptor = target.getContainingDeclaration() as? AnonymousFunctionDescriptor ?: return null
return DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor) as? JetFunctionLiteral
}
val expression = PsiTreeUtil.getParentOfType(element, javaClass<JetSimpleNameExpression>(), javaClass<JetParameter>())
if (expression == null) {
return null
}
val arrow = innermostFunctionLiteral.getArrowNode() ?: return null
if (caretOffset > arrow.getTextRange().getEndOffset()) return null
when (expression) {
is JetParameter -> {
return PsiTreeUtil.skipParentsOfType(expression, javaClass<JetParameterList>()) as? JetFunctionLiteral
}
is JetSimpleNameExpression -> {
val reference = expression.getReference() as JetReference?
val variableDescriptor = reference?.resolveToDescriptors(expression.analyze(BodyResolveMode.PARTIAL))?.firstOrNull() as? VariableDescriptor?
if (variableDescriptor != null) {
val containingDescriptor = variableDescriptor.getContainingDeclaration()
if (containingDescriptor is AnonymousFunctionDescriptor) {
return DescriptorToSourceUtils.descriptorToDeclaration(containingDescriptor) as? JetFunctionLiteral
}
}
return null
}
else -> return null
}
return innermostFunctionLiteral
}
private class ParamRenamingProcessor(
val editor: Editor,
val funcLiteral: JetFunctionLiteral,
val cursorWasOverParameterList: Boolean) : RenameProcessor(editor.getProject(),
funcLiteral.getValueParameters().first(),
"it",
false,
false
val functionLiteral: JetFunctionLiteral,
val cursorWasInParameterList: Boolean
) : RenameProcessor(editor.getProject(),
functionLiteral.getValueParameters().single(),
"it",
false,
false
) {
public override fun performRefactoring(usages: Array<out UsageInfo>) {
super.performRefactoring(usages)
funcLiteral.deleteChildRange(funcLiteral.getValueParameterList(), funcLiteral.getArrowNode()!!.getPsi())
if (cursorWasOverParameterList) {
editor.getCaretModel().moveToOffset(funcLiteral.getBodyExpression()!!.getTextOffset())
functionLiteral.deleteChildRange(functionLiteral.getValueParameterList(), functionLiteral.getArrowNode()!!.getPsi())
if (cursorWasInParameterList) {
editor.getCaretModel().moveToOffset(functionLiteral.getBodyExpression()!!.getTextOffset())
}
CodeStyleManager.getInstance(editor.getProject()!!)!!.reformatText(funcLiteral.getContainingFile()!!,
funcLiteral.getTextRange()!!.getStartOffset(),
funcLiteral.getTextRange()!!.getEndOffset())
val range = functionLiteral.getTextRange()
CodeStyleManager.getInstance(functionLiteral.getProject()).reformatText(functionLiteral.getContainingFile(),
range.getStartOffset(),
range.getEndOffset())
}
}
@@ -0,0 +1,4 @@
// IS_APPLICABLE: false
fun applyTwice<A>(f: (A) -> A, x: A) = f(f(x))
val x = applyTwice({ p -> p + <caret>1 }, 40)
@@ -5176,6 +5176,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/notApplicable_parameterHasExplicitType.kt");
doTest(fileName);
}
@TestMetadata("notApplicable_wrongPosition.kt")
public void testNotApplicable_wrongPosition() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/notApplicable_wrongPosition.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam")