diff --git a/python/build/pycharm_build.gant b/python/build/pycharm_build.gant index 3c668288cd34..e61775a07613 100644 --- a/python/build/pycharm_build.gant +++ b/python/build/pycharm_build.gant @@ -1,11 +1,11 @@ +import org.jetbrains.jps.Jps import org.jetbrains.jps.Module import static org.jetbrains.jps.idea.IdeaProjectLoader.guessHome -import org.jetbrains.jps.Jps includeTargets << new File("${guessHome(this as Script)}/community/build/scripts/utils.gant") includeTool << Jps -requireProperty("buildNumber", requireProperty("build.number", "102.SNAPSHOT")) +requireProperty("buildNumber", requireProperty("build.number", "104.SNAPSHOT")) setProperty("ch", "$home/community") setProperty("dryRun", false) diff --git a/python/helpers/pycharm/django_test_manage.py b/python/helpers/pycharm/django_test_manage.py index d15732efcbef..c1c2dbbee629 100644 --- a/python/helpers/pycharm/django_test_manage.py +++ b/python/helpers/pycharm/django_test_manage.py @@ -1,8 +1,14 @@ #!/usr/bin/env python from pycharm.fix_getpass import fixGetpass -from pycharm import django_test_settings +from pycharm import django_settings from django.core.management import execute_manager +import os +manage_file = os.getenv('PYCHARM_DJANGO_MANAGE_MODULE') +if not manage_file: + manage_file = 'manage' + if __name__ == "__main__": + __import__(manage_file) fixGetpass() execute_manager(django_settings) diff --git a/python/helpers/pycharm/tcmessages.py b/python/helpers/pycharm/tcmessages.py index 3d8d5a7d8fbf..f54e235252f0 100644 --- a/python/helpers/pycharm/tcmessages.py +++ b/python/helpers/pycharm/tcmessages.py @@ -36,7 +36,11 @@ class TeamcityServiceMessages: def testIgnored(self, testName, message=''): self.message('testIgnored', name=testName, message=message) - def testFailed(self, testName, message='', details=''): + def testFailed(self, testName, message='', details='', expected='', actual=''): + if expected and actual: + self.message('testFailed', type='comparisonFailure', name=testName, message=message, + details=details, expected=expected, actual=actual) + else: self.message('testFailed', name=testName, message=message, details=details) def testError(self, testName, message='', details=''): diff --git a/python/helpers/pycharm/tcunittest.py b/python/helpers/pycharm/tcunittest.py index f6e35ee9ea55..055401764e38 100644 --- a/python/helpers/pycharm/tcunittest.py +++ b/python/helpers/pycharm/tcunittest.py @@ -17,6 +17,26 @@ class TeamcityTestResult(TestResult): self.messages = TeamcityServiceMessages(self.output, prepend_linebreak=True) self.current_suite = None + def find_first(self, val): + quot = val[0] + count = 1 + quote_ind = val[count:].find(quot) + while val[count+quote_ind-1] == "\\": + count = count + quote_ind + 1 + quote_ind = val[count:].find(quot) + + return val[0:quote_ind+count+1] + + def find_second(self, val): + quot = val[-1] + count = 1 + quote_ind = val[:len(val)-count-1].rfind(quot) + while val[quote_ind-1] == "\\": + quote_ind = val[:quote_ind-1].rfind(quot) + + return val[quote_ind:] + + def formatErr(self, err): exctype, value, tb = err return ''.join(traceback.format_exception(exctype, value, tb)) @@ -46,10 +66,17 @@ class TeamcityTestResult(TestResult): def addFailure(self, test, err): TestResult.addFailure(self, test, err) + error_value = err[1][0] + + if error_value.startswith("'") or error_value.startswith('"'): + first = self.find_first(err[1][0]) + second = self.find_second(err[1][0]) + else: + first = second = "" err = self.formatErr(err) self.messages.testFailed(self.getTestName(test), - message='Failure', details=err) + message='Failure', details=err, expected=first, actual=second) def addSkip(self, test, reason): self.messages.testIgnored(self.getTestName(test), message=reason) diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml index fcdf8892823d..73a52fe9d45e 100644 --- a/python/src/META-INF/python-plugin-common.xml +++ b/python/src/META-INF/python-plugin-common.xml @@ -217,6 +217,10 @@ + + + + diff --git a/python/src/com/jetbrains/python/codeInsight/PyCodeInsightSettings.java b/python/src/com/jetbrains/python/codeInsight/PyCodeInsightSettings.java index 6f2b23ec5021..f13ae4b289f2 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyCodeInsightSettings.java +++ b/python/src/com/jetbrains/python/codeInsight/PyCodeInsightSettings.java @@ -33,7 +33,11 @@ public class PyCodeInsightSettings implements PersistentStateComponent usages) { + return new PyContainingFileRenamer((PyClass) element, newName); + } + + public static class PyContainingFileRenamer extends AutomaticRenamer { + private final PyClass myClass; + + public PyContainingFileRenamer(PyClass element, String newName) { + myClass = element; + myElements.add(element.getContainingFile()); + suggestAllNames(element.getName(), newName); + } + + @Override + public String getDialogTitle() { + return "Rename Containing File"; + } + + @Override + public String getDialogDescription() { + return "Rename containing file with the following name to: "; + } + + @Override + public String entityName() { + return "Containing File"; + } + + @Override + protected String nameToCanonicalName(@NonNls String name, PsiNamedElement element) { + return FileUtil.getNameWithoutExtension(name); + } + + @Override + protected String canonicalNameToName(@NonNls String canonicalName, PsiNamedElement element) { + return canonicalName + "." + FileUtil.getExtension(myClass.getContainingFile().getName()); + } + + @Override + public boolean isSelectedByDefault() { + return true; + } + } +} diff --git a/python/src/com/jetbrains/python/refactoring/rename/PyInheritorRenameFactory.java b/python/src/com/jetbrains/python/refactoring/rename/PyInheritorRenameFactory.java new file mode 100644 index 000000000000..84ce9ab3e2f3 --- /dev/null +++ b/python/src/com/jetbrains/python/refactoring/rename/PyInheritorRenameFactory.java @@ -0,0 +1,63 @@ +package com.jetbrains.python.refactoring.rename; + +import com.intellij.psi.PsiElement; +import com.intellij.refactoring.rename.naming.AutomaticRenamer; +import com.intellij.refactoring.rename.naming.AutomaticRenamerFactory; +import com.intellij.usageView.UsageInfo; +import com.jetbrains.python.codeInsight.PyCodeInsightSettings; +import com.jetbrains.python.psi.PyClass; +import com.jetbrains.python.psi.search.PyClassInheritorsSearch; + +import java.util.Collection; + +/** + * @author yole + */ +public class PyInheritorRenameFactory implements AutomaticRenamerFactory { + @Override + public boolean isApplicable(PsiElement element) { + return element instanceof PyClass; + } + + @Override + public String getOptionName() { + return "Rename inheritors"; + } + + @Override + public boolean isEnabled() { + return PyCodeInsightSettings.getInstance().RENAME_CLASS_INHERITORS; + } + + @Override + public void setEnabled(boolean enabled) { + PyCodeInsightSettings.getInstance().RENAME_CLASS_INHERITORS = enabled; + } + + @Override + public AutomaticRenamer createRenamer(PsiElement element, String newName, Collection usages) { + return new PyInheritorRenamer((PyClass) element, newName); + } + + public static class PyInheritorRenamer extends AutomaticRenamer { + public PyInheritorRenamer(PyClass element, String newName) { + myElements.addAll(PyClassInheritorsSearch.search(element, false).findAll()); + suggestAllNames(element.getName(), newName); + } + + @Override + public String getDialogTitle() { + return "Rename Inheritors"; + } + + @Override + public String getDialogDescription() { + return "Rename inheritor classes with the following names to:"; + } + + @Override + public String entityName() { + return "Inheritor Class"; + } + } +} diff --git a/python/src/com/jetbrains/python/refactoring/rename/PyParametersRenameFactory.java b/python/src/com/jetbrains/python/refactoring/rename/PyParametersRenameFactory.java new file mode 100644 index 000000000000..e38446a0baa0 --- /dev/null +++ b/python/src/com/jetbrains/python/refactoring/rename/PyParametersRenameFactory.java @@ -0,0 +1,91 @@ +package com.jetbrains.python.refactoring.rename; + +import com.intellij.openapi.util.Comparing; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.refactoring.rename.naming.AutomaticRenamer; +import com.intellij.refactoring.rename.naming.AutomaticRenamerFactory; +import com.intellij.usageView.UsageInfo; +import com.intellij.util.Processor; +import com.jetbrains.python.codeInsight.PyCodeInsightSettings; +import com.jetbrains.python.psi.PyFunction; +import com.jetbrains.python.psi.PyNamedParameter; +import com.jetbrains.python.psi.PyParameter; +import com.jetbrains.python.psi.search.PyOverridingMethodsSearch; + +import java.util.Collection; + +/** + * @author yole + */ +public class PyParametersRenameFactory implements AutomaticRenamerFactory { + @Override + public boolean isApplicable(PsiElement element) { + if (element instanceof PyParameter) { + PyFunction function = PsiTreeUtil.getParentOfType(element, PyFunction.class); + return function != null && function.getContainingClass() != null; + } + return false; + } + + @Override + public String getOptionName() { + return "Rename parameters in hierarchy"; + } + + @Override + public boolean isEnabled() { + return PyCodeInsightSettings.getInstance().RENAME_PARAMETERS_IN_HIERARCHY; + } + + @Override + public void setEnabled(boolean enabled) { + PyCodeInsightSettings.getInstance().RENAME_PARAMETERS_IN_HIERARCHY = enabled; + } + + @Override + public AutomaticRenamer createRenamer(PsiElement element, String newName, Collection usages) { + return new PyParametersRenamer((PyParameter)element, newName); + } + + public static class PyParametersRenamer extends AutomaticRenamer { + + public PyParametersRenamer(final PyParameter element, String newName) { + PyFunction function = PsiTreeUtil.getParentOfType(element, PyFunction.class); + PyOverridingMethodsSearch.search(function, true).forEach(new Processor() { + @Override + public boolean process(PyFunction pyFunction) { + PyParameter[] parameters = pyFunction.getParameterList().getParameters(); + for (PyParameter parameter : parameters) { + PyNamedParameter named = parameter.getAsNamed(); + if (named != null && Comparing.equal(named.getName(), element.getName())) { + myElements.add(named); + } + } + return true; + } + }); + suggestAllNames(element.getName(), newName); + } + + @Override + public String getDialogTitle() { + return "Rename parameters"; + } + + @Override + public String getDialogDescription() { + return "Rename parameter in hierarchy to:"; + } + + @Override + public String entityName() { + return "Parameter"; + } + + @Override + public boolean isSelectedByDefault() { + return true; + } + } +} diff --git a/python/testData/inspections/ReplaceNotEqOperator.py b/python/testData/inspections/ReplaceNotEqOperator.py index 59b313eb8773..3dc25820ba83 100644 --- a/python/testData/inspections/ReplaceNotEqOperator.py +++ b/python/testData/inspections/ReplaceNotEqOperator.py @@ -1 +1 @@ -print(a <> b) \ No newline at end of file +print(a <> b) \ No newline at end of file