diff --git a/python/src/com/jetbrains/python/psi/PyArgumentList.java b/python/src/com/jetbrains/python/psi/PyArgumentList.java index fb7e3a9e62e5..c0effc354ad4 100644 --- a/python/src/com/jetbrains/python/psi/PyArgumentList.java +++ b/python/src/com/jetbrains/python/psi/PyArgumentList.java @@ -30,6 +30,7 @@ public interface PyArgumentList extends PyElement { * Tries to map the argument list to callee's idea of parameters. * @return a result object with mappings and diagnostic flags. */ + @NotNull AnalysisResult analyzeCall(); /** diff --git a/python/src/com/jetbrains/python/psi/impl/PyArgumentListImpl.java b/python/src/com/jetbrains/python/psi/impl/PyArgumentListImpl.java index 8de9a2bcfef2..144d652a5595 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyArgumentListImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyArgumentListImpl.java @@ -221,6 +221,7 @@ public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList super.deleteChildInternal(node); } + @NotNull public AnalysisResult analyzeCall() { final PyCallExpressionHelper.AnalysisResultImpl ret = new PyCallExpressionHelper.AnalysisResultImpl(this); PyExpression[] arguments = getArguments(); diff --git a/python/src/com/jetbrains/python/refactoring/NameSuggestorUtil.java b/python/src/com/jetbrains/python/refactoring/NameSuggestorUtil.java index c9aa4e40e96d..25f25a9c1d6d 100644 --- a/python/src/com/jetbrains/python/refactoring/NameSuggestorUtil.java +++ b/python/src/com/jetbrains/python/refactoring/NameSuggestorUtil.java @@ -9,10 +9,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * Created by IntelliJ IDEA. - * User: Alexey.Ivanov - * Date: Aug 24, 2009 - * Time: 6:49:13 PM + * @author Alexey.Ivanov */ public class NameSuggestorUtil { private NameSuggestorUtil() { diff --git a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java index 29d58d0d2d83..a3d6033c6878 100644 --- a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java +++ b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java @@ -18,9 +18,7 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.IntroduceTargetChooser; import com.intellij.refactoring.RefactoringActionHandler; import com.intellij.refactoring.util.CommonRefactoringUtil; -import com.intellij.util.ArrayUtil; import com.intellij.util.Function; -import com.intellij.util.containers.HashSet; import com.jetbrains.python.PyBundle; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyPsiUtils; @@ -31,10 +29,7 @@ import com.jetbrains.python.refactoring.PyRefactoringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.util.*; /** * @author Alexey.Ivanov @@ -72,28 +67,42 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) { } - protected String[] getSuggestedNames(@NotNull final PyExpression expression) { - Collection res = new HashSet(); + public Collection getSuggestedNames(@NotNull final PyExpression expression) { + Collection candidates = new HashSet(); String text = expression.getText(); if (text != null) { - for (String name : NameSuggestorUtil.generateNames(text)) { - if (myValidator.checkPossibleName(name, expression)) { - res.add(name); - } - } + candidates.addAll(NameSuggestorUtil.generateNames(text)); } PyType type = expression.getType(TypeEvalContext.fast()); if (type != null) { final String typeName = type.getName(); if (typeName != null) { - for (String name : NameSuggestorUtil.generateNamesByType(typeName)) { - if (myValidator.checkPossibleName(name, expression)) { - res.add(name); - } + candidates.addAll(NameSuggestorUtil.generateNamesByType(typeName)); + } + } + final PyKeywordArgument kwArg = PsiTreeUtil.getParentOfType(expression, PyKeywordArgument.class); + if (kwArg != null && kwArg.getValueExpression() == expression) { + candidates.add(kwArg.getKeyword()); + } + + final PyArgumentList argList = PsiTreeUtil.getParentOfType(expression, PyArgumentList.class); + if (argList != null) { + final PyArgumentList.AnalysisResult result = argList.analyzeCall(); + if (result.getMarkedCallee() != null && !result.isImplicitlyResolved()) { + final PyNamedParameter namedParameter = result.getPlainMappedParams().get(expression); + if (namedParameter != null) { + candidates.add(namedParameter.getName()); } } } - return ArrayUtil.toStringArray(res); + + Collection res = new HashSet(); + for (String name : candidates) { + if (myValidator.checkPossibleName(name, expression)) { + res.add(name); + } + } + return res; } public void performAction(@NotNull final Project project, Editor editor, PsiFile file, String name, boolean replaceAll, boolean hasConstructor, boolean isTestClass) { @@ -202,7 +211,7 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { else { occurrences = Collections.emptyList(); } - String[] possibleNames = getSuggestedNames(expression); + Collection possibleNames = getSuggestedNames(expression); replaceAll &= occurrences.size() > 0; InitPlace initInConstructor = InitPlace.SAME_METHOD; if (name == null) { diff --git a/python/src/com/jetbrains/python/refactoring/introduce/PyIntroduceDialog.java b/python/src/com/jetbrains/python/refactoring/introduce/PyIntroduceDialog.java index 4f80c4aae8dd..f9276fd47f6c 100644 --- a/python/src/com/jetbrains/python/refactoring/introduce/PyIntroduceDialog.java +++ b/python/src/com/jetbrains/python/refactoring/introduce/PyIntroduceDialog.java @@ -17,6 +17,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.event.*; +import java.util.Collection; /** * Created by IntelliJ IDEA. @@ -45,7 +46,7 @@ public class PyIntroduceDialog extends DialogWrapper implements PyIntroduceSetti @NotNull final String caption, @NotNull final IntroduceValidator validator, final int occurrencesCount, - final String[] possibleNames, + final Collection possibleNames, final String helpId, boolean hasConstructor, boolean isTestClass) { @@ -69,7 +70,7 @@ public class PyIntroduceDialog extends DialogWrapper implements PyIntroduceSetti return myHelpId; } - private void setUpNameComboBox(String[] possibleNames) { + private void setUpNameComboBox(Collection possibleNames) { final EditorComboBoxEditor comboEditor = new StringComboboxEditor(myProject, PythonFileType.INSTANCE, myNameComboBox); myNameComboBox.setEditor(comboEditor); diff --git a/python/src/com/jetbrains/python/refactoring/introduce/constant/ConstantIntroduceHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/constant/ConstantIntroduceHandler.java index c7ebb01e1329..e155cf025fa3 100644 --- a/python/src/com/jetbrains/python/refactoring/introduce/constant/ConstantIntroduceHandler.java +++ b/python/src/com/jetbrains/python/refactoring/introduce/constant/ConstantIntroduceHandler.java @@ -1,7 +1,6 @@ package com.jetbrains.python.refactoring.introduce.constant; import com.intellij.psi.PsiElement; -import com.intellij.util.ArrayUtil; import com.intellij.util.containers.HashSet; import com.jetbrains.python.PyBundle; import com.jetbrains.python.psi.PyExpression; @@ -13,10 +12,7 @@ import java.util.Collection; import java.util.List; /** - * Created by IntelliJ IDEA. - * User: Alexey.Ivanov - * Date: Aug 25, 2009 - * Time: 6:48:16 PM + * @author Alexey.Ivanov */ public class ConstantIntroduceHandler extends IntroduceHandler { public ConstantIntroduceHandler() { @@ -35,12 +31,12 @@ public class ConstantIntroduceHandler extends IntroduceHandler { return anchor.addBefore(declaration, ((PyFile)anchor).getStatements().get(0)); } - protected String[] getSuggestedNames(@NotNull final PyExpression expression) { + public Collection getSuggestedNames(@NotNull final PyExpression expression) { Collection names = new HashSet(); for (String name : super.getSuggestedNames(expression)) { names.add(name.toUpperCase()); } - return ArrayUtil.toStringArray(names); + return names; } @Override diff --git a/python/testData/refactoring/introduceVariable/suggestArgumentName.py b/python/testData/refactoring/introduceVariable/suggestArgumentName.py new file mode 100644 index 000000000000..80a5ff0ee381 --- /dev/null +++ b/python/testData/refactoring/introduceVariable/suggestArgumentName.py @@ -0,0 +1,4 @@ +def foo(extra_context): + pass + +foo({'a': 1}) diff --git a/python/testData/refactoring/introduceVariable/suggestKeywordArgumentName.py b/python/testData/refactoring/introduceVariable/suggestKeywordArgumentName.py new file mode 100644 index 000000000000..10023f4b9fce --- /dev/null +++ b/python/testData/refactoring/introduceVariable/suggestKeywordArgumentName.py @@ -0,0 +1,4 @@ +def foo(**kwargs): + pass + +foo(extra_context={'a': 1}) diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java index f5e9dad2d1c9..82605832fb1c 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java @@ -1,8 +1,12 @@ package com.jetbrains.python.refactoring; +import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.python.fixtures.PyLightFixtureTestCase; +import com.jetbrains.python.psi.PyExpression; import com.jetbrains.python.refactoring.introduce.variable.VariableIntroduceHandler; +import java.util.Collection; + /** * @author yole */ @@ -12,15 +16,31 @@ public class PyIntroduceVariableTest extends PyLightFixtureTestCase { return super.getTestDataPath() + "/refactoring/introduceVariable"; } - public void testSimple() throws Exception { + public void testSimple() { doTest(); } - public void testPy995() throws Exception { + public void testPy995() { doTest(); } - private void doTest() throws Exception { + public void testSuggestKeywordArgumentName() { // PY-1260 + myFixture.configureByFile(getTestName(true) + ".py"); + VariableIntroduceHandler handler = new VariableIntroduceHandler(); + PyExpression expr = PsiTreeUtil.getParentOfType(myFixture.getFile().findElementAt(myFixture.getEditor().getCaretModel().getOffset()), PyExpression.class); + final Collection names = handler.getSuggestedNames(expr); + assertTrue(names.contains("extra_context")); + } + + public void testSuggestArgumentName() { // PY-1260 + myFixture.configureByFile(getTestName(true) + ".py"); + VariableIntroduceHandler handler = new VariableIntroduceHandler(); + PyExpression expr = PsiTreeUtil.getParentOfType(myFixture.getFile().findElementAt(myFixture.getEditor().getCaretModel().getOffset()), PyExpression.class); + final Collection names = handler.getSuggestedNames(expr); + assertTrue(names.contains("extra_context")); + } + + private void doTest() { myFixture.configureByFile(getTestName(true) + ".py"); VariableIntroduceHandler handler = new VariableIntroduceHandler(); handler.performAction(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile(), "a", true, false, false);