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 JetBundle.message("change.function.signature", functionName);
else
return JetBundle.message("add.parameters.to.function", subjectSuffix, functionName);
}
}
@Override
public boolean isAvailable(
@NotNull Project project, Editor editor, PsiFile file
) {
if (!super.isAvailable(project, editor, file)) {
return false;
} }
int newParametersCnt = callElement.getValueArguments().size() - functionDescriptor.getValueParameters().size(); return if (hasTypeMismatches)
if (newParametersCnt <= 0) { "Change the signature of $callableDescription"
// psi for this quickfix is no longer valid else
return false; "Add parameter$subjectSuffix to $callableDescription"
}
return true;
} }
@Override override fun isAvailable(project: Project, editor: Editor, file: PsiFile): Boolean {
public boolean startInWriteAction() { if (!super.isAvailable(project, editor, file)) return false
return true;
// newParametersCnt <= 0: psi for this quickfix is no longer valid
val newParametersCnt = callElement.valueArguments.size() - functionDescriptor.valueParameters.size()
return newParametersCnt > 0
} }
@Override override fun startInWriteAction() = true
protected void invoke(@NotNull Project project, Editor editor, JetFile file) {
BindingContext bindingContext = ResolutionUtils.analyzeFully((JetFile) callElement.getContainingFile()); override fun invoke(project: Project, editor: Editor?, file: JetFile) {
runChangeSignature(project, functionDescriptor, addParameterConfiguration(), bindingContext, callElement, getText()); runChangeSignature(project, functionDescriptor, addParameterConfiguration(), callElement.analyzeFully(), callElement, text)
} }
private JetChangeSignatureConfiguration addParameterConfiguration() { private fun addParameterConfiguration(): JetChangeSignatureConfiguration {
return new JetChangeSignatureConfiguration() { return object : JetChangeSignatureConfiguration {
@NotNull override fun configure(originalDescriptor: JetMethodDescriptor, bindingContext: BindingContext): JetMethodDescriptor {
@Override return originalDescriptor.modify {
public JetMethodDescriptor configure(@NotNull final JetMethodDescriptor originalDescriptor, @NotNull final BindingContext bindingContext) { val parameters = functionDescriptor.valueParameters
return ChangeSignaturePackage.modify( val arguments = callElement.valueArguments
originalDescriptor, val validator = CollectingNameValidator()
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 ++) { for (i in arguments.indices) {
ValueArgument argument = arguments.get(i); val argument = arguments.get(i)
JetExpression expression = argument.getArgumentExpression(); val 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 {
JetParameterInfo parameterInfo =
getNewParameterInfo((FunctionDescriptor) originalDescriptor.getBaseDescriptor(), bindingContext, argument, validator);
typesToShorten.add(parameterInfo.getOriginalType());
if (expression != null) {
parameterInfo.setDefaultValueForCall(expression);
}
descriptor.addParameter(parameterInfo);
}
}
return null;
} }
} }
); else {
} val parameterInfo = ChangeFunctionSignatureFix.getNewParameterInfo(
originalDescriptor.baseDescriptor as FunctionDescriptor,
bindingContext,
argument,
validator
)
parameterInfo.originalType?.let { typesToShorten.add(it) }
@Override if (expression != null) {
public boolean performSilently(@NotNull Collection<? extends PsiElement> affectedFunctions) { parameterInfo.defaultValueForCall = expression
if (affectedFunctions.size() != 1) { }
return false;
it.addParameter(parameterInfo)
}
}
} }
PsiElement onlyFunction = affectedFunctions.iterator().next();
return !hasTypeMismatches && !isConstructor() && !hasOtherUsages(onlyFunction);
} }
@Override override fun performSilently(affectedFunctions: Collection<PsiElement>): Boolean {
public boolean forcePerformForSelectedFunctionOnly() { val onlyFunction = affectedFunctions.singleOrNull() ?: return false
return false; return !hasTypeMismatches && !isConstructor() && !hasOtherUsages(onlyFunction)
}
};
}
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;
} }
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
} }