PY-8174 Use provided arguments as default values for new parameters

This commit is contained in:
Mikhail Golubev
2017-06-05 21:20:45 +03:00
parent 05a195ba23
commit d8a1f8fd86
5 changed files with 62 additions and 25 deletions
@@ -165,6 +165,10 @@ public class PyTokenTypes {
public static final TokenSet WHITESPACE_OR_LINEBREAK = TokenSet.create(SPACE, TAB, FORMFEED, LINE_BREAK);
public static final TokenSet OPEN_BRACES = TokenSet.create(LBRACKET, LBRACE, LPAR);
public static final TokenSet CLOSE_BRACES = TokenSet.create(RBRACKET, RBRACE, RPAR);
public static final TokenSet NUMERIC_LITERALS = TokenSet.create(FLOAT_LITERAL, INTEGER_LITERAL, IMAGINARY_LITERAL);
public static final TokenSet BOOL_LITERALS = TokenSet.create(TRUE_KEYWORD, FALSE_KEYWORD);
public static final TokenSet SCALAR_LITERALS = TokenSet.orSet(STRING_NODES, BOOL_LITERALS, NUMERIC_LITERALS, TokenSet.create(NONE_KEYWORD));
public static final TokenSet AUG_ASSIGN_OPERATIONS = TokenSet.create(PLUSEQ, MINUSEQ, MULTEQ, ATEQ, DIVEQ,
PERCEQ, EXPEQ, GTGTEQ, LTLTEQ, ANDEQ, OREQ, XOREQ, FLOORDIVEQ);
@@ -18,6 +18,7 @@ package com.jetbrains.python.inspections.quickfix;
import com.google.common.collect.Iterators;
import com.google.common.collect.PeekingIterator;
import com.intellij.codeInspection.LocalQuickFixOnPsiElement;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
@@ -68,7 +69,16 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement {
final List<Pair<Integer, PyParameterInfo>> newParameters = new ArrayList<>();
for (PyExpression arg : mapping.getUnmappedArguments()) {
if (arg instanceof PyKeywordArgument) {
newParameters.add(Pair.create(parameters.length - 1, new PyParameterInfo(-1, ((PyKeywordArgument)arg).getKeyword(), "", false)));
final String defaultValueText;
final PyExpression value = ((PyKeywordArgument)arg).getValueExpression();
if (value != null && PyRefactoringUtil.isSimpleExpression(value) && !value.textContains('\n')) {
defaultValueText = value.getText();
}
else {
defaultValueText = ApplicationManager.getApplication().isUnitTestMode() ? "None" : "";
}
newParameters.add(Pair.create(parameters.length - 1,
new PyParameterInfo(-1, ((PyKeywordArgument)arg).getKeyword(), defaultValueText, true)));
}
else {
final TypeEvalContext context = TypeEvalContext.userInitiated(function.getProject(), callExpression.getContainingFile());
@@ -77,10 +87,10 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement {
final String paramName = PyRefactoringUtil.selectUniqueNameFromType(typeName, function.getStatementList());
newParameters.add(Pair.create(positionalParamAnchor, new PyParameterInfo(-1, paramName, "", false)));
}
}
}
return new PyChangeSignatureQuickFix(function, newParameters);
}
@NotNull
public static PyChangeSignatureQuickFix forMismatchingMethods(@NotNull PyFunction function, @NotNull PyFunction complementary) {
@@ -117,7 +127,7 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement {
}, ", ");
return "<html>" +
PyBundle.message("QFIX.change.signature.of", StringUtil.notNullize(function.getName()) + "(" + params + ")") +
"</html>";
"</html>";
}
@Nullable
@@ -127,36 +137,45 @@ public class PyChangeSignatureQuickFix extends LocalQuickFixOnPsiElement {
@Override
public void invoke(@NotNull Project project, @NotNull PsiFile file, @NotNull PsiElement startElement, @NotNull PsiElement endElement) {
final PyChangeSignatureDialog dialog = new PyChangeSignatureDialog(project, createMethodDescriptor(getFunction())) {
final PyFunction function = getFunction();
final PyMethodDescriptor descriptor = createMethodDescriptor(function);
final PyChangeSignatureDialog dialog = new PyChangeSignatureDialog(project, descriptor) {
// Similar to JavaChangeSignatureDialog.createAndPreselectNew()
@Override
protected int getSelectedIdx() {
return (int)StreamEx.of(getParameters()).indexOf(info -> info.getOldIndex() < 0).orElse(super.getSelectedIdx());
}
};
dialog.show();
if (ApplicationManager.getApplication().isUnitTestMode()) {
dialog.createRefactoringProcessor().run();
}
else {
dialog.show();
}
}
@NotNull
private PyMethodDescriptor createMethodDescriptor(final PyFunction function) {
return new PyMethodDescriptor(function) {
@Override
public List<PyParameterInfo> getParameters() {
final List<PyParameterInfo> result = new ArrayList<>();
final List<PyParameterInfo> originalParams = super.getParameters();
final PeekingIterator<Pair<Integer, PyParameterInfo>> extra = Iterators.peekingIterator(myExtraParameters.iterator());
while (extra.hasNext() && extra.peek().getFirst() < 0) {
@Override
public List<PyParameterInfo> getParameters() {
final List<PyParameterInfo> result = new ArrayList<>();
final List<PyParameterInfo> originalParams = super.getParameters();
final PeekingIterator<Pair<Integer, PyParameterInfo>> extra = Iterators.peekingIterator(myExtraParameters.iterator());
while (extra.hasNext() && extra.peek().getFirst() < 0) {
result.add(extra.next().getSecond());
}
for (int i = 0; i < originalParams.size(); i++) {
result.add(originalParams.get(i));
while (extra.hasNext() && extra.peek().getFirst() == i) {
result.add(extra.next().getSecond());
}
for (int i = 0; i < originalParams.size(); i++) {
result.add(originalParams.get(i));
while (extra.hasNext() && extra.peek().getFirst() == i) {
result.add(extra.next().getSecond());
}
}
return result;
}
};
return result;
}
};
}
@Nullable
@@ -17,6 +17,7 @@ package com.jetbrains.python.refactoring;
import com.intellij.codeInsight.PsiEquivalenceUtil;
import com.intellij.find.findUsages.FindUsagesHandler;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Pair;
@@ -28,6 +29,7 @@ import com.intellij.usageView.UsageInfo;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashSet;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.findUsages.PyFindUsagesHandlerFactory;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.refactoring.introduce.IntroduceValidator;
@@ -397,4 +399,16 @@ public class PyRefactoringUtil {
public static boolean isValidNewName(@NotNull String name, @NotNull PsiElement scopeAnchor) {
return !(IntroduceValidator.isDefinedInScope(name, scopeAnchor) || PyNames.isReserved(name));
}
public static boolean isSimpleExpression(@NotNull PyExpression value) {
if (value instanceof PyLiteralExpression) {
final ASTNode node = value.getNode();
// Check that string literal doesn't contain multiple glued nodes
return node.getChildren(null).length == 1 && PyTokenTypes.SCALAR_LITERALS.contains(node.getFirstChildNode().getElementType());
}
else if (value instanceof PyReferenceExpression) {
return PyUtil.isPy2ReservedWord((PyReferenceExpression)value);
}
return false;
}
}
@@ -89,7 +89,7 @@ public class PyChangeSignatureDialog extends
}
@Override
protected BaseRefactoringProcessor createRefactoringProcessor() {
public BaseRefactoringProcessor createRefactoringProcessor() {
final List<PyParameterInfo> parameters = getParameters();
return new PyChangeSignatureProcessor(myProject, myMethod.getMethod(), getMethodName(),
parameters.toArray(new PyParameterInfo[parameters.size()]));
@@ -28,10 +28,10 @@ import org.jetbrains.annotations.NotNull;
public class PyChangeSignatureProcessor extends ChangeSignatureProcessorBase {
protected PyChangeSignatureProcessor(Project project,
PyFunction method,
String newName,
PyParameterInfo[] parameterInfo) {
public PyChangeSignatureProcessor(Project project,
PyFunction method,
String newName,
PyParameterInfo[] parameterInfo) {
super(project, generateChangeInfo(method, newName, parameterInfo));
}