diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index d5abb38450ad..ab7db3c65134 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -149,6 +149,7 @@ refactoring.extract.method.error.cannot.perform.refactoring.no.corresponding.loo refactoring.extract.method.error.cannot.perform.refactoring.when.execution.flow.is.interrupted=Cannot perform refactoring when execution flow is interrupted refactoring.extract.method.error.cannot.perform.refactoring.when.from.import.inside=Cannot perform refactoring with from import statement inside code block refactoring.extract.method.error.cannot.perform.refactoring.using.selected.elements=Cannot perform extract method using selected element(s) +refactoring.extract.method.error.name.clash=Method name clashes with already existing method name ### Annotators ### ANN.deleting.none=deleting None diff --git a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java index 2b4bc0cee830..df24916f769e 100644 --- a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java +++ b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java @@ -20,10 +20,13 @@ import com.intellij.refactoring.extractMethod.AbstractVariableData; import com.intellij.refactoring.extractMethod.ExtractMethodDecorator; import com.intellij.refactoring.extractMethod.ExtractMethodValidator; import com.intellij.refactoring.util.CommonRefactoringUtil; +import com.intellij.util.Function; import com.intellij.util.containers.hash.HashMap; +import com.jetbrains.python.PyBundle; import com.jetbrains.python.PythonLanguage; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyPsiUtils; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; @@ -341,12 +344,16 @@ public class PyExtractMethodUtil { private static Pair getNameAndVariableData(final Project project, final CodeFragment fragment, final PsiElement element) { + final ExtractMethodValidator validator = new PyExtractMethodValidator(element, project); if (ApplicationManager.getApplication().isUnitTestMode()){ - String name = System.getProperty(NAME); if (name == null){ name = "foo"; } + final String result = validator.check(name); + if (result != null){ + throw new CommonRefactoringUtil.RefactoringErrorHintException(result); + } final List data = new ArrayList(); for (String in : fragment.getInputVariables()) { final AbstractVariableData d = new AbstractVariableData(); @@ -358,16 +365,6 @@ public class PyExtractMethodUtil { return Pair.create(name, data.toArray(new AbstractVariableData[data.size()])); } - final ExtractMethodValidator validator = new ExtractMethodValidator() { - public String check(final String name) { - // TODO[oleg] implement context for name clashes - return null; - } - - public boolean isValidName(final String name) { - return LanguageNamesValidation.INSTANCE.forLanguage(PythonLanguage.getInstance()).isIdentifier(name, project); - } - }; final boolean isMethod = PyPsiUtils.isMethodContext(element); final ExtractMethodDecorator decorator = new ExtractMethodDecorator() { public String createMethodPreview(final String methodName, final AbstractVariableData[] variableDatas) { @@ -402,5 +399,49 @@ public class PyExtractMethodUtil { return Pair.create(dialog.getMethodName(), dialog.getVariableData()); } + private static class PyExtractMethodValidator implements ExtractMethodValidator { + private final PsiElement myElement; + private final Project myProject; + private final Function myFunction; + + public PyExtractMethodValidator(final PsiElement element, final Project project) { + myElement = element; + myProject = project; + final PsiNamedElement parent = PsiTreeUtil.getParentOfType(myElement, PyFile.class, PyClass.class); + if (parent instanceof PyFile){ + final List functions = ((PyFile)parent).getTopLevelFunctions(); + myFunction = new Function() { + public Boolean fun(@NotNull final String s) { + for (PyFunction function : functions) { + if (s.equals(function.getName())){ + return false; + } + } + return true; + } + }; + } else + if (parent instanceof PyClass){ + myFunction = new Function() { + public Boolean fun(@NotNull final String s) { + return ((PyClass) parent).findMethodByName(s, true) == null; + } + }; + } else { + myFunction = null; + } + } + + public String check(final String name) { + if (myFunction != null && !myFunction.fun(name)){ + return PyBundle.message("refactoring.extract.method.error.name.clash"); + } + return null; + } + + public boolean isValidName(final String name) { + return LanguageNamesValidation.INSTANCE.forLanguage(PythonLanguage.getInstance()).isIdentifier(name, myProject); + } + } } diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java index 2f2285a3a6e4..33b87c32325b 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java @@ -69,18 +69,17 @@ public class PyExtractMethodTest extends LightMarkedTestCase { doTest("controlFlow/while_output.before.py", "bar", "controlFlow/while_output.after.py"); } - // TODO[oleg] implement me!!! - //public void testNameCollisionClass() throws Throwable { - // doTest("namesCollision/class.before.py", "hello", "Method name clashes with already existing method name"); - //} - // - //public void testNameCollisionFile() throws Throwable { - // doTest("namesCollision/file.before.py", "hello", "Method name clashes with already existing method name"); - //} - // - //public void testNameCollisionSuperClass() throws Throwable { - // doTest("namesCollision/superclass.before.py", "hello", "Method name clashes with already existing method name"); - //} + public void testNameCollisionClass() throws Throwable { + doTest("namesCollision/class.before.py", "hello", "Method name clashes with already existing method name"); + } + + public void testNameCollisionFile() throws Throwable { + doTest("namesCollision/file.before.py", "hello", "Method name clashes with already existing method name"); + } + + public void testNameCollisionSuperClass() throws Throwable { + doTest("namesCollision/superclass.before.py", "hello", "Method name clashes with already existing method name"); + } public void testOutNotEmptyStatements() throws Throwable { doTest("outNotEmpty/statements.before.py", "sum_squares", "outNotEmpty/statements.after.py");