AddNameToArgumentFix to use up-to-date names + checking preconditions in isAvailable

#EA-72651 Fixed
This commit is contained in:
Valentin Kipyatkov
2015-09-04 19:43:07 +03:00
parent 527faec457
commit 6781abb3da
@@ -23,7 +23,7 @@ import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.openapi.ui.popup.ListPopupStep
import com.intellij.openapi.ui.popup.PopupStep
import com.intellij.openapi.ui.popup.util.BaseListPopupStep
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.JetIcons
import org.jetbrains.kotlin.idea.caches.resolve.analyze
@@ -38,23 +38,72 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
public class AddNameToArgumentFix(argument: JetValueArgument, private val possibleNames: List<Name>) : JetIntentionAction<JetValueArgument>(argument) {
public class AddNameToArgumentFix(argument: JetValueArgument) : JetIntentionAction<JetValueArgument>(argument) {
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean {
if (!super.isAvailable(project, editor, file)) return false
if (element.getArgumentExpression() == null) return false
return calculatePossibleArgumentNames().isNotEmpty()
}
override fun invoke(project: Project, editor: Editor?, file: JetFile) {
val possibleNames = calculatePossibleArgumentNames()
assert(possibleNames.isNotEmpty(), "isAvailable() should be checked before invoke()")
if (possibleNames.size() == 1 || editor == null || !editor.component.isShowing) {
addName(project, element, possibleNames.first())
}
else {
chooseNameAndAdd(project, editor)
chooseNameAndAdd(project, editor, possibleNames)
}
}
private fun chooseNameAndAdd(project: Project, editor: Editor) {
JBPopupFactory.getInstance().createListPopup(getNamePopup(project)).showInBestPositionFor(editor)
override fun getText(): String {
return calculatePossibleArgumentNames()
.singleOrNull()
?.let { "Add name to argument: '${createArgumentWithName(it).text}'" }
?: "Add name to argument..."
}
private fun getNamePopup(project: Project): ListPopupStep<Name> {
return object : BaseListPopupStep<Name>("Choose parameter name", possibleNames) {
override fun getFamilyName() = "Add name to argument"
private fun calculatePossibleArgumentNames(): List<Name> {
val callElement = element.getParentOfType<JetCallElement>(true) ?: return emptyList()
val context = element.analyze(BodyResolveMode.PARTIAL)
val resolvedCall = callElement.getResolvedCall(context) ?: return emptyList()
val argumentType = element.getArgumentExpression()?.let { context.getType(it) }
val usedParameters = resolvedCall.call.valueArguments
.map { resolvedCall.getArgumentMapping(it) }
.filterIsInstance<ArgumentMatch>()
.filter { argumentMatch -> argumentType == null || argumentType.isError || !argumentMatch.isError() }
.map { it.valueParameter }
.toSet()
return resolvedCall.resultingDescriptor.valueParameters
.filter { it !in usedParameters }
.map { it.name }
}
private fun addName(project: Project, argument: JetValueArgument, name: Name) {
project.executeWriteCommand("Add name to argument...") {
argument.replace(createArgumentWithName(name))
}
}
private fun createArgumentWithName(name: Name): JetValueArgument {
val argumentExpression = element.getArgumentExpression()!!
val newArgument = JetPsiFactory(element).createArgument(argumentExpression, name, element.getSpreadElement() != null)
return newArgument
}
private fun chooseNameAndAdd(project: Project, editor: Editor, names: List<Name>) {
JBPopupFactory.getInstance().createListPopup(getNamePopup(project, names)).showInBestPositionFor(editor)
}
private fun getNamePopup(project: Project, names: List<Name>): ListPopupStep<Name> {
return object : BaseListPopupStep<Name>("Choose parameter name", names) {
override fun onChosen(selectedValue: Name, finalChoice: Boolean): PopupStep<*>? {
if (finalChoice) {
addName(project, element, selectedValue)
@@ -64,60 +113,15 @@ public class AddNameToArgumentFix(argument: JetValueArgument, private val possib
override fun getIconFor(name: Name) = JetIcons.PARAMETER
override fun getTextFor(name: Name) = getParsedArgumentWithName(name, element).text
override fun getTextFor(name: Name) = createArgumentWithName(name).text
}
}
override fun getText(): String {
return possibleNames
.singleOrNull()
?.let { "Add name to argument: '${getParsedArgumentWithName(it, element).text}'" }
?: "Add name to argument..."
}
override fun getFamilyName() = "Add Name to Argument"
companion object : JetSingleIntentionActionFactory() {
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
val argument = diagnostic.psiElement.getParentOfType<JetValueArgument>(false) ?: return null
val possibleNames = generatePossibleNames(argument)
if (possibleNames.isEmpty()) return null
return AddNameToArgumentFix(argument, possibleNames)
return AddNameToArgumentFix(argument)
}
private fun generatePossibleNames(argument: JetValueArgument): List<Name> {
val callElement = argument.getParentOfType<JetCallElement>(true) ?: return emptyList()
val context = argument.analyze(BodyResolveMode.PARTIAL)
val resolvedCall = callElement.getResolvedCall(context) ?: return emptyList()
val argumentType = argument.getArgumentExpression()?.let { context.getType(it) }
val usedParameters = resolvedCall.call.valueArguments
.map { resolvedCall.getArgumentMapping(it) }
.filterIsInstance<ArgumentMatch>()
.filter { argumentMatch -> argumentType == null || argumentType.isError || !argumentMatch.isError() }
.map { it.valueParameter }
.toSet()
return resolvedCall.resultingDescriptor.valueParameters
.filter { it !in usedParameters }
.map { it.name }
}
private fun addName(project: Project, argument: JetValueArgument, name: Name) {
PsiDocumentManager.getInstance(project).commitAllDocuments()
project.executeWriteCommand("Add name to argument...") {
val newArgument = getParsedArgumentWithName(name, argument)
argument.replace(newArgument)
}
}
private fun getParsedArgumentWithName(name: Name, argument: JetValueArgument): JetValueArgument {
val argumentExpression = argument.getArgumentExpression()
?: error("Argument should be already parsed.")
return JetPsiFactory(argument).createArgument(argumentExpression, name, argument.getSpreadElement() != null)
}
}
}