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.ListPopupStep
|
||||||
import com.intellij.openapi.ui.popup.PopupStep
|
import com.intellij.openapi.ui.popup.PopupStep
|
||||||
import com.intellij.openapi.ui.popup.util.BaseListPopupStep
|
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.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.idea.JetIcons
|
import org.jetbrains.kotlin.idea.JetIcons
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
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.calls.model.ArgumentMatch
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
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) {
|
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) {
|
if (possibleNames.size() == 1 || editor == null || !editor.component.isShowing) {
|
||||||
addName(project, element, possibleNames.first())
|
addName(project, element, possibleNames.first())
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
chooseNameAndAdd(project, editor)
|
chooseNameAndAdd(project, editor, possibleNames)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun chooseNameAndAdd(project: Project, editor: Editor) {
|
override fun getText(): String {
|
||||||
JBPopupFactory.getInstance().createListPopup(getNamePopup(project)).showInBestPositionFor(editor)
|
return calculatePossibleArgumentNames()
|
||||||
|
.singleOrNull()
|
||||||
|
?.let { "Add name to argument: '${createArgumentWithName(it).text}'" }
|
||||||
|
?: "Add name to argument..."
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getNamePopup(project: Project): ListPopupStep<Name> {
|
override fun getFamilyName() = "Add name to argument"
|
||||||
return object : BaseListPopupStep<Name>("Choose parameter name", possibleNames) {
|
|
||||||
|
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<*>? {
|
override fun onChosen(selectedValue: Name, finalChoice: Boolean): PopupStep<*>? {
|
||||||
if (finalChoice) {
|
if (finalChoice) {
|
||||||
addName(project, element, selectedValue)
|
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 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() {
|
companion object : JetSingleIntentionActionFactory() {
|
||||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||||
val argument = diagnostic.psiElement.getParentOfType<JetValueArgument>(false) ?: return null
|
val argument = diagnostic.psiElement.getParentOfType<JetValueArgument>(false) ?: return null
|
||||||
val possibleNames = generatePossibleNames(argument)
|
return AddNameToArgumentFix(argument)
|
||||||
if (possibleNames.isEmpty()) return null
|
|
||||||
return AddNameToArgumentFix(argument, possibleNames)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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