diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties index 3ff97a496c9..7b71fca33a6 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties @@ -200,10 +200,6 @@ change.function.signature.action.multiple=Change function signature... change.function.signature.family=Change function signature change.function.signature.chooser.title=Choose signature change.function.signature.action=Change function signature -add.name.to.argument.family=Add Name to Argument -add.name.to.argument.single=Add name to argument\: ''{0}'' -add.name.to.argument.multiple=Add name to argument... -add.name.to.argument.action=Add name to argument... add.name.to.parameter.name.chooser.title=Choose parameter name replace.java.class.argument=Replace javaClass() with T::class diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddNameToArgumentFix.java b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddNameToArgumentFix.java deleted file mode 100644 index 7312f01b2e5..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddNameToArgumentFix.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2010-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.kotlin.idea.quickfix; - -import com.google.common.collect.Lists; -import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.command.CommandProcessor; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; -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.util.PsiTreeUtil; -import kotlin.KotlinPackage; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.descriptors.CallableDescriptor; -import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor; -import org.jetbrains.kotlin.diagnostics.Diagnostic; -import org.jetbrains.kotlin.idea.JetBundle; -import org.jetbrains.kotlin.idea.JetIcons; -import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage; -import org.jetbrains.kotlin.idea.core.CorePackage; -import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil; -import org.jetbrains.kotlin.name.Name; -import org.jetbrains.kotlin.psi.JetCallElement; -import org.jetbrains.kotlin.psi.JetExpression; -import org.jetbrains.kotlin.psi.JetFile; -import org.jetbrains.kotlin.psi.JetValueArgument; -import org.jetbrains.kotlin.resolve.BindingContext; -import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage; -import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; -import org.jetbrains.kotlin.types.JetType; -import org.jetbrains.kotlin.types.checker.JetTypeChecker; - -import javax.swing.*; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory; - -public class AddNameToArgumentFix extends JetIntentionAction { - - @NotNull - private final List possibleNames; - - public AddNameToArgumentFix(@NotNull JetValueArgument argument, @NotNull List possibleNames) { - super(argument); - this.possibleNames = possibleNames; - } - - @NotNull - private static List generatePossibleNames(@NotNull JetValueArgument argument) { - JetCallElement callElement = PsiTreeUtil.getParentOfType(argument, JetCallElement.class); - assert callElement != null : "The argument has to be inside a function or constructor call"; - - BindingContext context = ResolvePackage.analyze(argument); - ResolvedCall resolvedCall = CallUtilPackage.getResolvedCall(callElement, context); - if (resolvedCall == null) return Collections.emptyList(); - - CallableDescriptor callableDescriptor = resolvedCall.getResultingDescriptor(); - JetExpression argExpression = argument.getArgumentExpression(); - JetType type = argExpression != null ? context.getType(argExpression) : null; - Set usedParameters = KotlinPackage.toSet( CorePackage.mapArgumentsToParameters(resolvedCall.getCall(), callableDescriptor).values()); - List names = Lists.newArrayList(); - for (ValueParameterDescriptor parameter: callableDescriptor.getValueParameters()) { - if (!usedParameters.contains(parameter) && (type == null || JetTypeChecker.DEFAULT.isSubtypeOf(type, parameter.getType()))) { - names.add(parameter.getName().asString()); - } - } - return names; - } - - @Override - protected void invoke(@NotNull Project project, Editor editor, JetFile file) { - if (possibleNames.size() == 1 || editor == null || !editor.getComponent().isShowing()) { - addName(project, element, possibleNames.get(0)); - } - else { - chooseNameAndAdd(project, editor); - } - } - - private void chooseNameAndAdd(@NotNull Project project, Editor editor) { - JBPopupFactory.getInstance().createListPopup(getNamePopup(project)).showInBestPositionFor(editor); - } - - private ListPopupStep getNamePopup(final @NotNull Project project) { - return new BaseListPopupStep( - JetBundle.message("add.name.to.parameter.name.chooser.title"), possibleNames) { - @Override - public PopupStep onChosen(String selectedName, boolean finalChoice) { - if (finalChoice) { - addName(project, element, selectedName); - } - return FINAL_CHOICE; - } - - @Override - public Icon getIconFor(String name) { - return JetIcons.PARAMETER; - } - - @NotNull - @Override - public String getTextFor(String name) { - return getParsedArgumentWithName(name, element).getText(); - } - }; - } - - private static void addName(@NotNull Project project, final @NotNull JetValueArgument argument, final @NotNull String name) { - PsiDocumentManager.getInstance(project).commitAllDocuments(); - - CommandProcessor.getInstance().executeCommand(project, new Runnable() { - @Override - public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - JetValueArgument newArgument = getParsedArgumentWithName(name, argument); - argument.replace(newArgument); - } - }); - } - }, JetBundle.message("add.name.to.argument.action"), null); - } - - @NotNull - private static JetValueArgument getParsedArgumentWithName(@NotNull String name, @NotNull JetValueArgument argument) { - JetExpression argumentExpression = argument.getArgumentExpression(); - assert argumentExpression != null : "Argument should be already parsed."; - return JetPsiFactory(argument).createArgument(argumentExpression, Name.identifier(name), argument.getSpreadElement() != null); - } - - @NotNull - @Override - public String getText() { - if (possibleNames.size() == 1) { - return JetBundle.message("add.name.to.argument.single", getParsedArgumentWithName(possibleNames.get(0), element).getText()); - } else { - return JetBundle.message("add.name.to.argument.multiple"); - } - } - - @NotNull - @Override - public String getFamilyName() { - return JetBundle.message("add.name.to.argument.family"); - } - @NotNull - public static JetIntentionActionsFactory createFactory() { - return new JetSingleIntentionActionFactory() { - @Nullable - @Override - public IntentionAction createAction(@NotNull Diagnostic diagnostic) { - JetValueArgument argument = QuickFixUtil.getParentElementOfType(diagnostic, JetValueArgument.class); - if (argument == null) return null; - List possibleNames = generatePossibleNames(argument); - return possibleNames.isEmpty() ? null : new AddNameToArgumentFix(argument, possibleNames); - } - }; - } -} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddNameToArgumentFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddNameToArgumentFix.kt new file mode 100644 index 00000000000..b006914934a --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddNameToArgumentFix.kt @@ -0,0 +1,141 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.quickfix + +import com.google.common.collect.Lists +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.command.CommandProcessor +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +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.util.PsiTreeUtil +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.JetBundle +import org.jetbrains.kotlin.idea.JetIcons +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.core.mapArgumentsToParameters +import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.JetCallElement +import org.jetbrains.kotlin.psi.JetFile +import org.jetbrains.kotlin.psi.JetPsiFactory +import org.jetbrains.kotlin.psi.JetValueArgument +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.types.checker.JetTypeChecker + +public class AddNameToArgumentFix(argument: JetValueArgument, private val possibleNames: List) : JetIntentionAction(argument) { + + override fun invoke(project: Project, editor: Editor?, file: JetFile) { + if (possibleNames.size() == 1 || editor == null || !editor.component.isShowing) { + addName(project, element, possibleNames.get(0)) + } + else { + chooseNameAndAdd(project, editor) + } + } + + private fun chooseNameAndAdd(project: Project, editor: Editor) { + JBPopupFactory.getInstance().createListPopup(getNamePopup(project)).showInBestPositionFor(editor) + } + + private fun getNamePopup(project: Project): ListPopupStep { + return object : BaseListPopupStep(JetBundle.message("add.name.to.parameter.name.chooser.title"), possibleNames) { + override fun onChosen(selectedName: String, finalChoice: Boolean): PopupStep { + if (finalChoice) { + addName(project, element, selectedName) + } + return PopupStep.FINAL_CHOICE + } + + override fun getIconFor(name: String) = JetIcons.PARAMETER + + override fun getTextFor(name: String) = getParsedArgumentWithName(name, element).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 { + private fun generatePossibleNames(argument: JetValueArgument): List { + val callElement = PsiTreeUtil.getParentOfType(argument, JetCallElement::class.java) + assert(callElement != null, "The argument has to be inside a function or constructor call") + + val context = argument.analyze() + val resolvedCall = callElement!!.getResolvedCall(context) ?: return emptyList() + + val callableDescriptor = resolvedCall.resultingDescriptor + val argExpression = argument.getArgumentExpression() + val type = if (argExpression != null) context.getType(argExpression) else null + val usedParameters = resolvedCall.call.mapArgumentsToParameters(callableDescriptor).values().toSet() + val names = Lists.newArrayList() + for (parameter in callableDescriptor.valueParameters) { + if (!usedParameters.contains(parameter) && (type == null || JetTypeChecker.DEFAULT.isSubtypeOf(type, parameter.type))) { + names.add(parameter.name.asString()) + } + } + return names + } + + private fun addName(project: Project, argument: JetValueArgument, name: String) { + PsiDocumentManager.getInstance(project).commitAllDocuments() + + CommandProcessor.getInstance().executeCommand( + project, + object : Runnable { + override fun run() { + ApplicationManager.getApplication().runWriteAction(object : Runnable { + override fun run() { + val newArgument = getParsedArgumentWithName(name, argument) + argument.replace(newArgument) + } + }) + } + }, + "Add name to argument...", + null) + } + + private fun getParsedArgumentWithName(name: String, argument: JetValueArgument): JetValueArgument { + val argumentExpression = argument.getArgumentExpression() + assert(argumentExpression != null, "Argument should be already parsed.") + return JetPsiFactory(argument).createArgument(argumentExpression!!, Name.identifier(name), argument.getSpreadElement() != null) + } + + public fun createFactory(): JetIntentionActionsFactory { + return object : JetSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic): IntentionAction? { + val argument = QuickFixUtil.getParentElementOfType(diagnostic, JetValueArgument::class.java) ?: return null + val possibleNames = generatePossibleNames(argument) + if (possibleNames.isEmpty()) return null + return AddNameToArgumentFix(argument, possibleNames) + } + } + } + } +}