J2K: AddFunctionParametersFix.java

This commit is contained in:
Alexey Sedunov
2015-09-11 00:06:30 +03:00
parent 3c48882c4f
commit 670bb1a27d
2 changed files with 94 additions and 153 deletions
@@ -60,10 +60,7 @@ change.function.parameter.type=Change parameter ''{0}'' type of function ''{1}''
change.primary.constructor.parameter.type=Change parameter ''{0}'' type of primary constructor of class ''{1}'' to ''{2}'' change.primary.constructor.parameter.type=Change parameter ''{0}'' type of primary constructor of class ''{1}'' to ''{2}''
change.type=Change type from ''{0}'' to ''{1}'' change.type=Change type from ''{0}'' to ''{1}''
change.type.family=Change Type change.type.family=Change Type
add.parameters.to.function=Add parameter{0} to function ''{1}''
add.parameters.to.constructor=Add parameter{0} to constructor ''{1}''
change.function.signature=Change the signature of function ''{0}'' change.function.signature=Change the signature of function ''{0}''
change.constructor.signature=Change the signature of constructor ''{0}''
change.function.literal.signature=Change the signature of function literal change.function.literal.signature=Change the signature of function literal
remove.parameter=Remove parameter ''{0}'' remove.parameter=Remove parameter ''{0}''
change.signature.family=Change signature of function/constructor change.signature.family=Change signature of function/constructor
@@ -14,183 +14,127 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.quickfix; package org.jetbrains.kotlin.idea.quickfix
import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFile
import com.intellij.psi.PsiReference; import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.psi.search.searches.ReferencesSearch; import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import kotlin.Unit; import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
import kotlin.jvm.functions.Function1; import org.jetbrains.kotlin.idea.core.CollectingNameValidator
import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeSignatureConfiguration
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor; import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetMethodDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor; import org.jetbrains.kotlin.idea.refactoring.changeSignature.modify
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor; import org.jetbrains.kotlin.idea.refactoring.changeSignature.runChangeSignature
import org.jetbrains.kotlin.idea.JetBundle; import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils; import org.jetbrains.kotlin.psi.JetCallElement
import org.jetbrains.kotlin.idea.core.CollectingNameValidator; import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.idea.refactoring.changeSignature.*; import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers; import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.psi.JetCallElement; import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.psi.JetExpression; import org.jetbrains.kotlin.types.checker.JetTypeChecker
import org.jetbrains.kotlin.psi.JetFile; import java.util.*
import org.jetbrains.kotlin.psi.ValueArgument;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.types.JetType;
import org.jetbrains.kotlin.types.checker.JetTypeChecker;
import java.util.ArrayList; public class AddFunctionParametersFix(
import java.util.Collection; private val callElement: JetCallElement,
import java.util.List; functionDescriptor: FunctionDescriptor,
private val hasTypeMismatches: Boolean) : ChangeFunctionSignatureFix(callElement, functionDescriptor) {
private val typesToShorten = ArrayList<JetType>()
import static org.jetbrains.kotlin.idea.refactoring.changeSignature.ChangeSignaturePackage.runChangeSignature; override fun getText(): String {
val parameters = functionDescriptor.valueParameters
val arguments = callElement.valueArguments
val newParametersCnt = arguments.size() - parameters.size()
assert(newParametersCnt > 0)
public class AddFunctionParametersFix extends ChangeFunctionSignatureFix { val subjectSuffix = if (newParametersCnt > 1) "s" else ""
private final JetCallElement callElement;
private final boolean hasTypeMismatches;
private final List<JetType> typesToShorten = new ArrayList<JetType>();
public AddFunctionParametersFix( val callableDescription = if (isConstructor()) {
@NotNull JetCallElement callElement, val className = functionDescriptor.containingDeclaration.name.asString()
@NotNull FunctionDescriptor functionDescriptor, "constructor '$className'"
boolean hasTypeMismatches
) {
super(callElement, functionDescriptor);
this.callElement = callElement;
this.hasTypeMismatches = hasTypeMismatches;
}
@NotNull
@Override
public String getText() {
List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
List<? extends ValueArgument> arguments = callElement.getValueArguments();
int newParametersCnt = arguments.size() - parameters.size();
assert newParametersCnt > 0;
String subjectSuffix = newParametersCnt > 1 ? "s" : "";
if (isConstructor()) {
String className = functionDescriptor.getContainingDeclaration().getName().asString();
if (hasTypeMismatches)
return JetBundle.message("change.constructor.signature", className);
else
return JetBundle.message("add.parameters.to.constructor", subjectSuffix, className);
} }
else { else {
String functionName = functionDescriptor.getName().asString(); val functionName = functionDescriptor.name.asString()
"function '$functionName'"
}
if (hasTypeMismatches) return if (hasTypeMismatches)
return JetBundle.message("change.function.signature", functionName); "Change the signature of $callableDescription"
else else
return JetBundle.message("add.parameters.to.function", subjectSuffix, functionName); "Add parameter$subjectSuffix to $callableDescription"
}
} }
@Override override fun isAvailable(project: Project, editor: Editor, file: PsiFile): Boolean {
public boolean isAvailable( if (!super.isAvailable(project, editor, file)) return false
@NotNull Project project, Editor editor, PsiFile file
) { // newParametersCnt <= 0: psi for this quickfix is no longer valid
if (!super.isAvailable(project, editor, file)) { val newParametersCnt = callElement.valueArguments.size() - functionDescriptor.valueParameters.size()
return false; return newParametersCnt > 0
} }
int newParametersCnt = callElement.getValueArguments().size() - functionDescriptor.getValueParameters().size(); override fun startInWriteAction() = true
if (newParametersCnt <= 0) {
// psi for this quickfix is no longer valid override fun invoke(project: Project, editor: Editor?, file: JetFile) {
return false; runChangeSignature(project, functionDescriptor, addParameterConfiguration(), callElement.analyzeFully(), callElement, text)
}
return true;
} }
@Override private fun addParameterConfiguration(): JetChangeSignatureConfiguration {
public boolean startInWriteAction() { return object : JetChangeSignatureConfiguration {
return true; override fun configure(originalDescriptor: JetMethodDescriptor, bindingContext: BindingContext): JetMethodDescriptor {
} return originalDescriptor.modify {
val parameters = functionDescriptor.valueParameters
val arguments = callElement.valueArguments
val validator = CollectingNameValidator()
@Override for (i in arguments.indices) {
protected void invoke(@NotNull Project project, Editor editor, JetFile file) { val argument = arguments.get(i)
BindingContext bindingContext = ResolutionUtils.analyzeFully((JetFile) callElement.getContainingFile()); val expression = argument.getArgumentExpression()
runChangeSignature(project, functionDescriptor, addParameterConfiguration(), bindingContext, callElement, getText());
}
private JetChangeSignatureConfiguration addParameterConfiguration() {
return new JetChangeSignatureConfiguration() {
@NotNull
@Override
public JetMethodDescriptor configure(@NotNull final JetMethodDescriptor originalDescriptor, @NotNull final BindingContext bindingContext) {
return ChangeSignaturePackage.modify(
originalDescriptor,
new Function1<JetMutableMethodDescriptor, Unit>() {
@Override
public Unit invoke(JetMutableMethodDescriptor descriptor) {
List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
List<? extends ValueArgument> arguments = callElement.getValueArguments();
CollectingNameValidator validator = new CollectingNameValidator();
for (int i = 0; i < arguments.size(); i ++) {
ValueArgument argument = arguments.get(i);
JetExpression expression = argument.getArgumentExpression();
if (i < parameters.size()) { if (i < parameters.size()) {
validator.addName(parameters.get(i).getName().asString()); validator.addName(parameters.get(i).name.asString())
JetType argumentType = expression != null ? bindingContext.getType(expression) : null; val argumentType = expression?.let { bindingContext.getType(it) }
JetType parameterType = parameters.get(i).getType(); val parameterType = parameters.get(i).type
if (argumentType != null && !JetTypeChecker.DEFAULT.isSubtypeOf(argumentType, parameterType)) { if (argumentType != null && !JetTypeChecker.DEFAULT.isSubtypeOf(argumentType, parameterType)) {
descriptor.getParameters().get(i).setCurrentTypeText(IdeDescriptorRenderers.SOURCE_CODE.renderType(argumentType)); it.parameters.get(i).currentTypeText = IdeDescriptorRenderers.SOURCE_CODE.renderType(argumentType)
typesToShorten.add(argumentType); typesToShorten.add(argumentType)
} }
} }
else { else {
JetParameterInfo parameterInfo = val parameterInfo = ChangeFunctionSignatureFix.getNewParameterInfo(
getNewParameterInfo((FunctionDescriptor) originalDescriptor.getBaseDescriptor(), bindingContext, argument, validator); originalDescriptor.baseDescriptor as FunctionDescriptor,
typesToShorten.add(parameterInfo.getOriginalType()); bindingContext,
argument,
validator
)
parameterInfo.originalType?.let { typesToShorten.add(it) }
if (expression != null) { if (expression != null) {
parameterInfo.setDefaultValueForCall(expression); parameterInfo.defaultValueForCall = expression
} }
descriptor.addParameter(parameterInfo); it.addParameter(parameterInfo)
} }
} }
return null;
}
}
);
}
@Override
public boolean performSilently(@NotNull Collection<? extends PsiElement> affectedFunctions) {
if (affectedFunctions.size() != 1) {
return false;
}
PsiElement onlyFunction = affectedFunctions.iterator().next();
return !hasTypeMismatches && !isConstructor() && !hasOtherUsages(onlyFunction);
}
@Override
public boolean forcePerformForSelectedFunctionOnly() {
return false;
}
};
}
private boolean hasOtherUsages(@NotNull PsiElement function) {
for (PsiReference reference : ReferencesSearch.search(function)) {
JetCallElement call = PsiTreeUtil.getParentOfType(reference.getElement(), JetCallElement.class);
if (call != null && !callElement.equals(call)) {
return true;
} }
} }
return false; override fun performSilently(affectedFunctions: Collection<PsiElement>): Boolean {
val onlyFunction = affectedFunctions.singleOrNull() ?: return false
return !hasTypeMismatches && !isConstructor() && !hasOtherUsages(onlyFunction)
}
}
} }
private boolean isConstructor() { private fun hasOtherUsages(function: PsiElement): Boolean {
return functionDescriptor instanceof ConstructorDescriptor; return ReferencesSearch.search(function).any {
val call = it.element.getParentOfType<JetCallElement>(false)
call != null && callElement != call
} }
}
private fun isConstructor() = functionDescriptor is ConstructorDescriptor
} }