AddNameToArgumentFix to use up-to-date names + checking preconditions in isAvailable
#EA-72651 Fixed
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user