From 02b1dc2759e3ace619da5640cd0b55bc1bf726a8 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Thu, 30 Aug 2012 14:40:17 +0400 Subject: [PATCH 01/13] fixed PY-7355 Specify type using annotation: quick-fix is not available for top-level statements --- .../SpecifyTypeInPy3AnnotationsIntention.java | 45 +++++++++---------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java index 69ff9506e981..e3ab71f0b1c8 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java @@ -63,36 +63,33 @@ public class SpecifyTypeInPy3AnnotationsIntention implements IntentionAction { } final PyType type = problemElement.getType(TypeEvalContext.slow()); if (type == null || type instanceof PyReturnTypeReference) { - PyFunction pyFunction = PsiTreeUtil.getParentOfType(problemElement, PyFunction.class); PsiReference reference = problemElement.getReference(); if (problemElement instanceof PyQualifiedExpression) { final PyExpression qualifier = ((PyQualifiedExpression)problemElement).getQualifier(); if (qualifier != null && !qualifier.getText().equals(PyNames.CANONICAL_SELF)) reference = qualifier.getReference(); } - if (pyFunction != null) { - PyParameter parameter = null; - final PsiElement resolvedReference = reference != null?reference.resolve() : null; - if (problemElement instanceof PyParameter) - parameter = (PyParameter)problemElement; - else if (resolvedReference instanceof PyParameter) - parameter = (PyParameter)resolvedReference; - if (parameter instanceof PyNamedParameter && (((PyNamedParameter)parameter).getAnnotation() != null || - parameter.getDefaultValue() != null)) return false; - if (parameter != null) - return true; - else { - if (resolvedReference instanceof PyTargetExpression) { - final PyExpression assignedValue = ((PyTargetExpression)resolvedReference).findAssignedValue(); - if (assignedValue instanceof PyCallExpression) { - final PyExpression callee = ((PyCallExpression)assignedValue).getCallee(); - if (callee != null) { - final PsiReference psiReference = callee.getReference(); - if (psiReference != null && psiReference.resolve() == null) return false; - } - final Callable callable = ((PyCallExpression)assignedValue).resolveCalleeFunction(PyResolveContext.defaultContext()); - - if (callable instanceof PyFunction && ((PyFunction)callable).getAnnotation() == null) return true; + PyParameter parameter = null; + final PsiElement resolvedReference = reference != null?reference.resolve() : null; + if (problemElement instanceof PyParameter) + parameter = (PyParameter)problemElement; + else if (resolvedReference instanceof PyParameter) + parameter = (PyParameter)resolvedReference; + if (parameter instanceof PyNamedParameter && (((PyNamedParameter)parameter).getAnnotation() != null || + parameter.getDefaultValue() != null)) return false; + if (parameter != null) + return true; + else { + if (resolvedReference instanceof PyTargetExpression) { + final PyExpression assignedValue = ((PyTargetExpression)resolvedReference).findAssignedValue(); + if (assignedValue instanceof PyCallExpression) { + final PyExpression callee = ((PyCallExpression)assignedValue).getCallee(); + if (callee != null) { + final PsiReference psiReference = callee.getReference(); + if (psiReference != null && psiReference.resolve() == null) return false; } + final Callable callable = ((PyCallExpression)assignedValue).resolveCalleeFunction(PyResolveContext.defaultContext()); + + if (callable instanceof PyFunction && ((PyFunction)callable).getAnnotation() == null) return true; } } } From e392e3ba568843a5318b11637a1d2f26b4b205b3 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 30 Aug 2012 15:45:48 +0400 Subject: [PATCH 02/13] Fixed path for Python 3.3 interpreter installed via official installer on Mac OS X (PY-7336) --- .../jetbrains/python/sdk/MacPythonSdkFlavor.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/python/src/com/jetbrains/python/sdk/MacPythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/MacPythonSdkFlavor.java index 416b22fef9cd..c233efd531ae 100644 --- a/python/src/com/jetbrains/python/sdk/MacPythonSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/MacPythonSdkFlavor.java @@ -16,6 +16,7 @@ public class MacPythonSdkFlavor extends CPythonSdkFlavor { } public static MacPythonSdkFlavor INSTANCE = new MacPythonSdkFlavor(); + private static final String[] POSSIBLE_BINARY_NAMES = {"python", "python2", "python3"}; @Override public Collection suggestHomePaths() { @@ -36,10 +37,15 @@ public class MacPythonSdkFlavor extends CPythonSdkFlavor { final String dir_name = dir.getName().toLowerCase(); if (dir.isDirectory()) { if ("Current".equals(dir_name) || dir_name.startsWith("2") || dir_name.startsWith("3")) { - VirtualFile bin_dir = dir.findChild("bin"); - if (bin_dir != null && bin_dir.isDirectory()) { - VirtualFile python_exe = bin_dir.findChild("python"); - if (python_exe != null) candidates.add(python_exe.getPath()); + final VirtualFile binDir = dir.findChild("bin"); + if (binDir != null && binDir.isDirectory()) { + for (String name : POSSIBLE_BINARY_NAMES) { + final VirtualFile child = binDir.findChild(name); + if (child != null) { + candidates.add(child.getPath()); + break; + } + } } } } From 5e314dbb48eee166a89894527dee2ce6ba797deb Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 30 Aug 2012 16:15:13 +0400 Subject: [PATCH 03/13] Added runWithLanguageLevel() for Python testes --- .../com/jetbrains/python/fixtures/PyTestCase.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java b/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java index f1142c26a00d..6697b0775462 100644 --- a/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java +++ b/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java @@ -27,6 +27,7 @@ import com.jetbrains.python.psi.LanguageLevel; import com.jetbrains.python.psi.PyFile; import com.jetbrains.python.psi.impl.PyFileImpl; import com.jetbrains.python.psi.impl.PythonLanguageLevelPusher; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -89,6 +90,16 @@ public abstract class PyTestCase extends UsefulTestCase { PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), languageLevel); } + protected void runWithLanguageLevel(@NotNull LanguageLevel languageLevel, @NotNull Runnable action) { + setLanguageLevel(languageLevel); + try { + action.run(); + } + finally { + setLanguageLevel(null); + } + } + protected static void assertNotParsed(PyFile file) { assertNull(PARSED_ERROR_MSG, ((PyFileImpl)file).getTreeElement()); } From 81672ebe207ec0d72711bfe912219c3bf75b6eb4 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 30 Aug 2012 16:58:53 +0400 Subject: [PATCH 04/13] Fixed unresolved __qualname__ for instances and functions in Python 3.3 (PY-6745) --- .../inspections/PyUnresolvedReferencesInspection.java | 5 +++++ .../qualNameAttribute.py | 10 ++++++++++ .../PyUnresolvedReferencesInspectionTest.java | 11 +++++++++++ 3 files changed, 26 insertions(+) create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/qualNameAttribute.py diff --git a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java index 03dab57430dc..35f927ce0e4a 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java @@ -449,6 +449,11 @@ public class PyUnresolvedReferencesInspection extends PyInspection { if (description == null) { boolean marked_qualified = false; if (element instanceof PyQualifiedExpression) { + // TODO: Add __qualname__ for Python 3.3 to the skeleton of , introduce a pseudo-class skeleton for + // + if ("__qualname__".equals(ref_text) && LanguageLevel.forElement(element).isAtLeast(LanguageLevel.PYTHON33)) { + return; + } final PyQualifiedExpression qexpr = (PyQualifiedExpression)element; if (PyNames.COMPARISON_OPERATORS.contains(qexpr.getReferencedName()) || refname == null) { return; diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/qualNameAttribute.py b/python/testData/inspections/PyUnresolvedReferencesInspection/qualNameAttribute.py new file mode 100644 index 000000000000..f1bb4a6e0d01 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/qualNameAttribute.py @@ -0,0 +1,10 @@ +class C: + pass + +def f(x): + x.__qualname__ #pass + +C.__qualname__ #pass +c = C() +c.__qualname__ #pass +f.__qualname__ #pass diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index a5bbed89e7b9..71642e4d361f 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -1,6 +1,7 @@ package com.jetbrains.python.inspections; import com.jetbrains.python.fixtures.PyTestCase; +import com.jetbrains.python.psi.LanguageLevel; import org.jetbrains.annotations.NotNull; /** @@ -190,6 +191,16 @@ public class PyUnresolvedReferencesInspectionTest extends PyTestCase { doTest(); } + // PY-6745 + public void testQualNameAttribute() { + runWithLanguageLevel(LanguageLevel.PYTHON33, new Runnable() { + @Override + public void run() { + doTest(); + } + }); + } + private void doTest() { myFixture.configureByFile(TEST_DIRECTORY + getTestName(true) + ".py"); myFixture.enableInspections(PyUnresolvedReferencesInspection.class); From 93d0b8058b45be6c776f320c703fb741b5fb6106 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Thu, 30 Aug 2012 17:30:21 +0400 Subject: [PATCH 05/13] fixed PY-7319 "Join lines" does not remove trailing backslash correctly --- .../python/editor/PyJoinLinesHandler.java | 46 ++++++++++++++----- .../joinLines/LongExpression-after.py | 1 + python/testData/joinLines/LongExpression.py | 2 + .../com/jetbrains/python/PyJoinLinesTest.java | 1 + 4 files changed, 39 insertions(+), 11 deletions(-) create mode 100644 python/testData/joinLines/LongExpression-after.py create mode 100644 python/testData/joinLines/LongExpression.py diff --git a/python/src/com/jetbrains/python/editor/PyJoinLinesHandler.java b/python/src/com/jetbrains/python/editor/PyJoinLinesHandler.java index 6afb2274409a..16dd0faa3127 100644 --- a/python/src/com/jetbrains/python/editor/PyJoinLinesHandler.java +++ b/python/src/com/jetbrains/python/editor/PyJoinLinesHandler.java @@ -3,6 +3,7 @@ package com.jetbrains.python.editor; import com.intellij.codeInsight.editorActions.JoinRawLinesHandlerDelegate; import com.intellij.openapi.editor.Document; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiComment; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; @@ -73,29 +74,52 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { } // single string case PY-4375 + final PyExpression leftExpression = request.leftExpr(); + final PyExpression rightExpression = request.rightExpr(); if (request.leftElem() == request.rightElem()) { IElementType type = request.leftElem().getNode().getElementType(); if (PyTokenTypes.SINGLE_QUOTED_STRING == type || PyTokenTypes.SINGLE_QUOTED_UNICODE == type) { - PyExpression element = request.leftExpr(); + PyExpression element = leftExpression; if (element == null) return CANNOT_JOIN; - String[] substrings = element.getText().split("\n"); - if (substrings.length != 1) { - StringBuilder replacement = new StringBuilder(); - for (String string : substrings) { - if (string.trim().endsWith("\\")) - replacement.append(string.substring(0, string.length()-1)); - else - replacement.append(string); - } - document.replaceString(element.getTextOffset(), element.getTextOffset()+element.getTextLength(), replacement); + if (removeBackSlash(document, element, false)) { return element.getTextOffset(); } } } + PsiElement expression = null; + if (leftExpression != null && rightExpression != null) { + if (PsiTreeUtil.isAncestor(leftExpression, rightExpression, false)) + expression = leftExpression; + else if (PsiTreeUtil.isAncestor(rightExpression, leftExpression, false)) + expression = rightExpression; + if (expression != null && !(expression instanceof PyStringLiteralExpression)) { + if (removeBackSlash(document, expression, true)) { + return expression.getTextOffset(); + } + } + } } return CANNOT_JOIN; } + private boolean removeBackSlash(Document document, PsiElement element, boolean trim) { + String[] substrings = element.getText().split("\n"); + if (substrings.length != 1) { + StringBuilder replacement = new StringBuilder(); + for (String string : substrings) { + if (trim) + string = StringUtil.trimLeading(string); + if (string.trim().endsWith("\\")) + replacement.append(string.substring(0, string.length()-1)); + else + replacement.append(string); + } + document.replaceString(element.getTextOffset(), element.getTextOffset()+element.getTextLength(), replacement); + return true; + } + return false; + } + // a dumb immutable result holder private static class Result { final String myInsert; diff --git a/python/testData/joinLines/LongExpression-after.py b/python/testData/joinLines/LongExpression-after.py new file mode 100644 index 000000000000..7fc1972a9b79 --- /dev/null +++ b/python/testData/joinLines/LongExpression-after.py @@ -0,0 +1 @@ +really_really_really_really_really_really_really_long_name = percentage_complete.as_integer_ratio() \ No newline at end of file diff --git a/python/testData/joinLines/LongExpression.py b/python/testData/joinLines/LongExpression.py new file mode 100644 index 000000000000..a58f8434142f --- /dev/null +++ b/python/testData/joinLines/LongExpression.py @@ -0,0 +1,2 @@ +really_really_really_really_really_really_really_long_name = percentage_complete\ + .as_integer_ratio() \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyJoinLinesTest.java b/python/testSrc/com/jetbrains/python/PyJoinLinesTest.java index 93234c071898..cfec42363c17 100644 --- a/python/testSrc/com/jetbrains/python/PyJoinLinesTest.java +++ b/python/testSrc/com/jetbrains/python/PyJoinLinesTest.java @@ -65,4 +65,5 @@ public class PyJoinLinesTest extends PyTestCase { public void testTwoStatements() { doTest(); } public void testStringWithSlash() { doTest(); } public void testListOfStrings() { doTest(); } + public void testLongExpression() { doTest(); } } From 5a0d7cdfe4cba92d9ffb79a784e3f1c0e98d96b5 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 30 Aug 2012 18:52:47 +0400 Subject: [PATCH 06/13] Fixed multiple compatibility messages for 'yield from' syntax (PY-7374) --- .../com/jetbrains/python/validation/CompatibilityVisitor.java | 1 + 1 file changed, 1 insertion(+) diff --git a/python/src/com/jetbrains/python/validation/CompatibilityVisitor.java b/python/src/com/jetbrains/python/validation/CompatibilityVisitor.java index 6af4d9a1821e..1214f3995f30 100644 --- a/python/src/com/jetbrains/python/validation/CompatibilityVisitor.java +++ b/python/src/com/jetbrains/python/validation/CompatibilityVisitor.java @@ -484,6 +484,7 @@ public abstract class CompatibilityVisitor extends PyAnnotator { if (level.isOlderThan(LanguageLevel.PYTHON33)) { registerProblem(node, "Python versions < 3.3 do not support this syntax. Delegating to a subgenerator is available since " + "Python 3.3; use explicit iteration over subgenerator instead."); + break; } } } From 5d5deeebfb0506384e963be5c2317777dfde4fba Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Thu, 30 Aug 2012 17:29:23 +0200 Subject: [PATCH 07/13] Fixed NFE (EA-38726). --- .../python/remote/PythonRemoteSdkAdditionalData.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/src/com/jetbrains/python/remote/PythonRemoteSdkAdditionalData.java b/python/src/com/jetbrains/python/remote/PythonRemoteSdkAdditionalData.java index 825a19ccdc5e..8710e902b5d3 100644 --- a/python/src/com/jetbrains/python/remote/PythonRemoteSdkAdditionalData.java +++ b/python/src/com/jetbrains/python/remote/PythonRemoteSdkAdditionalData.java @@ -295,14 +295,14 @@ public class PythonRemoteSdkAdditionalData extends PythonSdkAdditionalData imple if (element != null) { data.setHost(element.getAttributeValue(HOST)); - data.setPort(Integer.parseInt(element.getAttributeValue(PORT))); - data.setAnonymous(Boolean.parseBoolean(element.getAttributeValue(ANONYMOUS))); + data.setPort(StringUtil.parseInt(element.getAttributeValue(PORT), 22)); + data.setAnonymous(StringUtil.parseBoolean(element.getAttributeValue(ANONYMOUS), false)); data.setSerializedUserName(element.getAttributeValue(USERNAME)); data.setSerializedPassword(element.getAttributeValue(PASSWORD)); data.setPrivateKeyFile(StringUtil.nullize(element.getAttributeValue(PRIVATE_KEY_FILE))); data.setKnownHostsFile(StringUtil.nullize(element.getAttributeValue(KNOWN_HOSTS_FILE))); data.setSerializedPassphrase(element.getAttributeValue(PASSPHRASE)); - data.setUseKeyPair(Boolean.parseBoolean(element.getAttributeValue(USE_KEY_PAIR))); + data.setUseKeyPair(StringUtil.parseBoolean(element.getAttributeValue(USE_KEY_PAIR), false)); data.setInterpreterPath(StringUtil.nullize(element.getAttributeValue(INTERPRETER_PATH))); data.setPyCharmTempFilesPath(StringUtil.nullize(element.getAttributeValue(PYCHARM_HELPERS_PATH))); From 76ffc0f6211800735cd252477203da4e98ece378 Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Thu, 30 Aug 2012 16:08:24 +0400 Subject: [PATCH 08/13] Refer to originating class so IconLoader doesn't have to guess via stack frame reflection --- python/helpers/icons/PythonHelpersIcons.java | 8 ++++++-- python/rest/src/icons/RestIcons.java | 6 +++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/python/helpers/icons/PythonHelpersIcons.java b/python/helpers/icons/PythonHelpersIcons.java index c80728d8bc73..63f1581154ff 100644 --- a/python/helpers/icons/PythonHelpersIcons.java +++ b/python/helpers/icons/PythonHelpersIcons.java @@ -9,6 +9,10 @@ import javax.swing.*; * Don't repeat mistakes of others ;-) */ public class PythonHelpersIcons { - public static final Icon Keybd_closed = IconLoader.getIcon("/coverage/htmlfiles/keybd_closed.png"); // 35x12 - public static final Icon Keybd_open = IconLoader.getIcon("/coverage/htmlfiles/keybd_open.png"); // 35x12 + private static Icon load(String path) { + return IconLoader.getIcon(path, PythonHelpersIcons.class); + } + + public static final Icon Keybd_closed = load("/coverage/htmlfiles/keybd_closed.png"); // 35x12 + public static final Icon Keybd_open = load("/coverage/htmlfiles/keybd_open.png"); // 35x12 } diff --git a/python/rest/src/icons/RestIcons.java b/python/rest/src/icons/RestIcons.java index 37a8509d1aee..810d775edecd 100644 --- a/python/rest/src/icons/RestIcons.java +++ b/python/rest/src/icons/RestIcons.java @@ -9,5 +9,9 @@ import javax.swing.*; * Don't repeat mistakes of others ;-) */ public class RestIcons { - public static final Icon Rst = IconLoader.getIcon("/com/jetbrains/rest/res/rst.png"); // 16x16 + private static Icon load(String path) { + return IconLoader.getIcon(path, RestIcons.class); + } + + public static final Icon Rst = load("/com/jetbrains/rest/res/rst.png"); // 16x16 } From 0df6752999f6a5bb68b1f7d5fd3ddc5b304f588c Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Thu, 30 Aug 2012 19:26:50 +0400 Subject: [PATCH 09/13] Use plugin reflective icons --- .../com/jetbrains/python/facet/PythonFacetType.java | 4 ++-- python/rest/src/com/jetbrains/rest/RestFileType.java | 4 ++-- python/src/com/jetbrains/pyqt/QtTranslationsFileType.java | 4 ++-- python/src/com/jetbrains/pyqt/QtUIFileType.java | 4 ++-- python/src/com/jetbrains/python/PyIcons.java | 8 ++++---- python/src/com/jetbrains/python/PythonModuleTypeBase.java | 8 ++++---- .../com/jetbrains/python/buildout/BuildoutFacetType.java | 4 ++-- .../python/console/PythonDebugLanguageConsoleView.java | 4 ++-- .../jetbrains/python/console/RunPythonConsoleAction.java | 4 ++-- .../src/com/jetbrains/python/psi/impl/PyFunctionImpl.java | 8 ++++---- .../com/jetbrains/python/run/PythonConfigurationType.java | 4 ++-- .../src/com/jetbrains/python/sdk/IronPythonSdkFlavor.java | 4 ++-- python/src/com/jetbrains/python/sdk/JythonSdkFlavor.java | 4 ++-- python/src/com/jetbrains/python/sdk/PyPySdkFlavor.java | 4 ++-- python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java | 4 ++-- python/src/com/jetbrains/python/sdk/PythonSdkType.java | 4 ++-- .../src/com/jetbrains/python/sdk/VirtualEnvSdkFlavor.java | 4 ++-- .../python/testing/PythonTestConfigurationType.java | 4 ++-- 18 files changed, 42 insertions(+), 42 deletions(-) diff --git a/python/pluginSrc/com/jetbrains/python/facet/PythonFacetType.java b/python/pluginSrc/com/jetbrains/python/facet/PythonFacetType.java index a4b9c16d6cec..e71a767c58c8 100644 --- a/python/pluginSrc/com/jetbrains/python/facet/PythonFacetType.java +++ b/python/pluginSrc/com/jetbrains/python/facet/PythonFacetType.java @@ -9,12 +9,12 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleType; import com.intellij.openapi.projectRoots.ProjectJdkTable; import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.openapi.util.IconLoader; import com.intellij.patterns.ElementPattern; import com.intellij.util.indexing.FileContent; import com.jetbrains.python.PythonFileType; import com.jetbrains.python.module.PythonModuleType; import com.jetbrains.python.sdk.PythonSdkType; +import icons.PythonIcons; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -26,7 +26,7 @@ import java.util.List; * @author yole */ public class PythonFacetType extends FacetType { - public static final Icon ICON = IconLoader.getIcon("/com/jetbrains/python/icons/python.png"); + public static final Icon ICON = PythonIcons.Python.Icons.Python; @NonNls private static final String ID = "Python"; diff --git a/python/rest/src/com/jetbrains/rest/RestFileType.java b/python/rest/src/com/jetbrains/rest/RestFileType.java index 63bdf14b9b32..3142be8b63aa 100644 --- a/python/rest/src/com/jetbrains/rest/RestFileType.java +++ b/python/rest/src/com/jetbrains/rest/RestFileType.java @@ -1,7 +1,7 @@ package com.jetbrains.rest; import com.intellij.openapi.fileTypes.LanguageFileType; -import com.intellij.openapi.util.IconLoader; +import icons.RestIcons; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,7 +40,7 @@ public class RestFileType extends LanguageFileType { @Nullable public Icon getIcon() { - return IconLoader.getIcon("/com/jetbrains/rest/res/rst.png", RestFileType.class); + return RestIcons.Rst; } } diff --git a/python/src/com/jetbrains/pyqt/QtTranslationsFileType.java b/python/src/com/jetbrains/pyqt/QtTranslationsFileType.java index bf0a5e545fd6..0961948be93e 100644 --- a/python/src/com/jetbrains/pyqt/QtTranslationsFileType.java +++ b/python/src/com/jetbrains/pyqt/QtTranslationsFileType.java @@ -1,7 +1,7 @@ package com.jetbrains.pyqt; import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.util.IconLoader; +import icons.PythonIcons; /** * @author yole @@ -10,7 +10,7 @@ public class QtTranslationsFileType extends QtFileType implements FileType { public static QtTranslationsFileType INSTANCE = new QtTranslationsFileType(); protected QtTranslationsFileType() { - super("Qt translations file", "Qt Linguist translations files", "ts", IconLoader.getIcon("/com/jetbrains/pyqt/tsFile.png")); + super("Qt translations file", "Qt Linguist translations files", "ts", PythonIcons.Pyqt.TsFile); } protected String getToolName() { diff --git a/python/src/com/jetbrains/pyqt/QtUIFileType.java b/python/src/com/jetbrains/pyqt/QtUIFileType.java index 73985c82e681..7788662cdb51 100644 --- a/python/src/com/jetbrains/pyqt/QtUIFileType.java +++ b/python/src/com/jetbrains/pyqt/QtUIFileType.java @@ -1,7 +1,7 @@ package com.jetbrains.pyqt; import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.util.IconLoader; +import icons.PythonIcons; /** * @author yole @@ -10,7 +10,7 @@ public class QtUIFileType extends QtFileType implements FileType { public static QtUIFileType INSTANCE = new QtUIFileType(); protected QtUIFileType() { - super("Qt UI file", "Qt UI Designer form files", "ui", IconLoader.getIcon("/com/jetbrains/pyqt/uiForm.png")); + super("Qt UI file", "Qt UI Designer form files", "ui", PythonIcons.Pyqt.UiForm); } protected String getToolName() { diff --git a/python/src/com/jetbrains/python/PyIcons.java b/python/src/com/jetbrains/python/PyIcons.java index 7f93670b0bda..c4738e9e7228 100644 --- a/python/src/com/jetbrains/python/PyIcons.java +++ b/python/src/com/jetbrains/python/PyIcons.java @@ -1,6 +1,6 @@ package com.jetbrains.python; -import com.intellij.openapi.util.IconLoader; +import icons.PythonIcons; import org.jetbrains.annotations.NonNls; import javax.swing.*; @@ -13,7 +13,7 @@ import javax.swing.*; public interface PyIcons { @NonNls String DATA_ROOT = "icons/"; /*"/com/jetbrains/python/PyIcons";*/ - Icon PRIVATE = IconLoader.getIcon(DATA_ROOT + "nodes/lock.png"); - Icon PREDEFINED = IconLoader.getIcon(DATA_ROOT + "nodes/cyan-dot.png"); - Icon INVISIBLE = IconLoader.getIcon(DATA_ROOT + "nodes/red-inv-triangle.png"); + Icon PRIVATE = PythonIcons.Python.Icons.Nodes.Lock; + Icon PREDEFINED = PythonIcons.Python.Icons.Nodes.Cyan_dot; + Icon INVISIBLE = PythonIcons.Python.Icons.Nodes.Red_inv_triangle; } diff --git a/python/src/com/jetbrains/python/PythonModuleTypeBase.java b/python/src/com/jetbrains/python/PythonModuleTypeBase.java index 73115756b36f..7f9801eed6fa 100644 --- a/python/src/com/jetbrains/python/PythonModuleTypeBase.java +++ b/python/src/com/jetbrains/python/PythonModuleTypeBase.java @@ -2,7 +2,7 @@ package com.jetbrains.python; import com.intellij.ide.util.projectWizard.ModuleBuilder; import com.intellij.openapi.module.ModuleType; -import com.intellij.openapi.util.IconLoader; +import icons.PythonIcons; import org.jetbrains.annotations.NonNls; import javax.swing.*; @@ -12,9 +12,9 @@ import javax.swing.*; */ public abstract class PythonModuleTypeBase extends ModuleType { @NonNls public static final String PYTHON_MODULE = "PYTHON_MODULE"; - private final Icon myBigIcon = IconLoader.getIcon("/com/jetbrains/python/icons/python_24.png"); - private final Icon myOpenIcon = IconLoader.getIcon("/com/jetbrains/python/icons/pythonOpen.png"); - private final Icon myClosedIcon = IconLoader.getIcon("/com/jetbrains/python/icons/pythonClosed.png"); + private final Icon myBigIcon = PythonIcons.Python.Icons.Python_24; + private final Icon myOpenIcon = PythonIcons.Python.Icons.PythonOpen; + private final Icon myClosedIcon = PythonIcons.Python.Icons.PythonClosed; protected PythonModuleTypeBase() { super(PYTHON_MODULE); diff --git a/python/src/com/jetbrains/python/buildout/BuildoutFacetType.java b/python/src/com/jetbrains/python/buildout/BuildoutFacetType.java index d999e0fa539b..b8a0e0a6e9af 100644 --- a/python/src/com/jetbrains/python/buildout/BuildoutFacetType.java +++ b/python/src/com/jetbrains/python/buildout/BuildoutFacetType.java @@ -6,8 +6,8 @@ import com.intellij.facet.FacetTypeId; import com.intellij.facet.FacetTypeRegistry; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleType; -import com.intellij.openapi.util.IconLoader; import com.jetbrains.python.PythonModuleTypeBase; +import icons.PythonIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -48,7 +48,7 @@ public class BuildoutFacetType extends FacetType implements PyFunction { - private static final Icon PROPERTY_GETTER = IconLoader.findIcon("/com/jetbrains/python/icons/propertyGetter.png"); - private static final Icon PROPERTY_SETTER = IconLoader.findIcon("/com/jetbrains/python/icons/propertySetter.png"); - private static final Icon PROPERTY_DELETER = IconLoader.findIcon("/com/jetbrains/python/icons/propertyDeleter.png"); + private static final Icon PROPERTY_GETTER = PythonIcons.Python.Icons.PropertyGetter; + private static final Icon PROPERTY_SETTER = PythonIcons.Python.Icons.PropertySetter; + private static final Icon PROPERTY_DELETER = PythonIcons.Python.Icons.PropertyDeleter; public PyFunctionImpl(ASTNode astNode) { super(astNode); diff --git a/python/src/com/jetbrains/python/run/PythonConfigurationType.java b/python/src/com/jetbrains/python/run/PythonConfigurationType.java index 3098fa9d07a3..2abe39117d20 100644 --- a/python/src/com/jetbrains/python/run/PythonConfigurationType.java +++ b/python/src/com/jetbrains/python/run/PythonConfigurationType.java @@ -6,7 +6,7 @@ import com.intellij.execution.configurations.RunConfiguration; import com.intellij.execution.configurations.RunConfigurationModule; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.IconLoader; +import icons.PythonIcons; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -29,7 +29,7 @@ public class PythonConfigurationType implements ConfigurationType { return null; } - private final Icon _icon = IconLoader.getIcon("/com/jetbrains/python/icons/python.png"); + private final Icon _icon = PythonIcons.Python.Icons.Python; private static class PythonConfigurationFactory extends ConfigurationFactory { protected PythonConfigurationFactory(ConfigurationType configurationType) { diff --git a/python/src/com/jetbrains/python/sdk/IronPythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/IronPythonSdkFlavor.java index 1f556e56c539..11821f160903 100644 --- a/python/src/com/jetbrains/python/sdk/IronPythonSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/IronPythonSdkFlavor.java @@ -1,8 +1,8 @@ package com.jetbrains.python.sdk; import com.intellij.execution.configurations.GeneralCommandLine; -import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.text.StringUtil; +import icons.PythonIcons; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -84,6 +84,6 @@ public class IronPythonSdkFlavor extends PythonSdkFlavor { @Override public Icon getIcon() { - return IconLoader.getIcon("/com/jetbrains/python/icons/dotnet.png"); + return PythonIcons.Python.Icons.Dotnet; } } diff --git a/python/src/com/jetbrains/python/sdk/JythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/JythonSdkFlavor.java index 363f08a9e205..28fdb5b001cd 100644 --- a/python/src/com/jetbrains/python/sdk/JythonSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/JythonSdkFlavor.java @@ -2,10 +2,10 @@ package com.jetbrains.python.sdk; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.configurations.ParamsGroup; -import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.jetbrains.python.run.PythonCommandLineState; +import icons.PythonIcons; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -66,6 +66,6 @@ public class JythonSdkFlavor extends PythonSdkFlavor { @Override public Icon getIcon() { - return IconLoader.getIcon("/com/jetbrains/python/icons/jython.png"); + return PythonIcons.Python.Icons.Jython; } } diff --git a/python/src/com/jetbrains/python/sdk/PyPySdkFlavor.java b/python/src/com/jetbrains/python/sdk/PyPySdkFlavor.java index 20b34516cfa2..b948a9c95507 100644 --- a/python/src/com/jetbrains/python/sdk/PyPySdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/PyPySdkFlavor.java @@ -1,10 +1,10 @@ package com.jetbrains.python.sdk; import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.jetbrains.python.psi.LanguageLevel; +import icons.PythonIcons; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -77,6 +77,6 @@ public class PyPySdkFlavor extends PythonSdkFlavor { @Override public Icon getIcon() { - return IconLoader.getIcon("/com/jetbrains/python/icons/pypy.png"); + return PythonIcons.Python.Icons.Pypy; } } diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java index f80c9e12c803..d257402d02ea 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java @@ -6,12 +6,12 @@ import com.intellij.execution.process.ProcessOutput; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.SdkAdditionalData; -import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.encoding.EncodingManager; import com.jetbrains.python.psi.LanguageLevel; +import icons.PythonIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -226,6 +226,6 @@ public abstract class PythonSdkFlavor { } public Icon getIcon() { - return IconLoader.getIcon("/com/jetbrains/python/icons/python.png"); + return PythonIcons.Python.Icons.Python; } } diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkType.java b/python/src/com/jetbrains/python/sdk/PythonSdkType.java index 5df5247449e6..f6cefba7431f 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkType.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkType.java @@ -28,7 +28,6 @@ import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; @@ -46,6 +45,7 @@ import com.jetbrains.python.psi.LanguageLevel; import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.search.PyProjectScopeBuilder; import com.jetbrains.python.remote.PythonRemoteSdkAdditionalData; +import icons.PythonIcons; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -84,7 +84,7 @@ public class PythonSdkType extends SdkType { } public Icon getIcon() { - return IconLoader.getIcon("/com/jetbrains/python/icons/python.png"); + return PythonIcons.Python.Icons.Python; } @NotNull diff --git a/python/src/com/jetbrains/python/sdk/VirtualEnvSdkFlavor.java b/python/src/com/jetbrains/python/sdk/VirtualEnvSdkFlavor.java index c9ea879fbcfd..a59aac80ced0 100644 --- a/python/src/com/jetbrains/python/sdk/VirtualEnvSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/VirtualEnvSdkFlavor.java @@ -3,12 +3,12 @@ package com.jetbrains.python.sdk; import com.intellij.ide.DataManager; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.SystemProperties; +import icons.PythonIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -117,6 +117,6 @@ public class VirtualEnvSdkFlavor extends CPythonSdkFlavor { @Override public Icon getIcon() { - return IconLoader.getIcon("/com/jetbrains/python/icons/virtualenv.png"); + return PythonIcons.Python.Icons.Virtualenv; } } diff --git a/python/src/com/jetbrains/python/testing/PythonTestConfigurationType.java b/python/src/com/jetbrains/python/testing/PythonTestConfigurationType.java index 1fa6fbd8c428..28b069ae8fa4 100644 --- a/python/src/com/jetbrains/python/testing/PythonTestConfigurationType.java +++ b/python/src/com/jetbrains/python/testing/PythonTestConfigurationType.java @@ -2,13 +2,13 @@ package com.jetbrains.python.testing; import com.intellij.execution.configurations.*; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.IconLoader; import com.jetbrains.python.PyBundle; import com.jetbrains.python.testing.attest.PythonAtTestRunConfiguration; import com.jetbrains.python.testing.doctest.PythonDocTestRunConfiguration; import com.jetbrains.python.testing.nosetest.PythonNoseTestRunConfiguration; import com.jetbrains.python.testing.pytest.PyTestRunConfiguration; import com.jetbrains.python.testing.unittest.PythonUnitTestRunConfiguration; +import icons.PythonIcons; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -29,7 +29,7 @@ public class PythonTestConfigurationType implements ConfigurationType { return ConfigurationTypeUtil.findConfigurationType(PythonTestConfigurationType.class); } - private static final Icon ICON = IconLoader.getIcon("/com/jetbrains/python/icons/pythonTests.png"); + private static final Icon ICON = PythonIcons.Python.Icons.PythonTests; private static class PythonUnitTestConfigurationFactory extends ConfigurationFactory { protected PythonUnitTestConfigurationFactory(ConfigurationType configurationType) { From 868bae486e43b465e3b3df47f1ceb51fbabfbd0c Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Thu, 30 Aug 2012 19:45:38 +0400 Subject: [PATCH 10/13] fixed PY-7316 Wrong text ranges for type references that have qualified canonical names --- .../DocStringReferenceProvider.java | 16 +++++++++------- .../documentation/StructuredDocString.java | 5 +++++ python/testData/resolve/ReferenceInDocstring.py | 7 +++++++ .../com/jetbrains/python/PyResolveTest.java | 4 ++++ 4 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 python/testData/resolve/ReferenceInDocstring.py diff --git a/python/src/com/jetbrains/python/documentation/DocStringReferenceProvider.java b/python/src/com/jetbrains/python/documentation/DocStringReferenceProvider.java index 7210f6493bbc..0e1a26b73967 100644 --- a/python/src/com/jetbrains/python/documentation/DocStringReferenceProvider.java +++ b/python/src/com/jetbrains/python/documentation/DocStringReferenceProvider.java @@ -43,14 +43,14 @@ public class DocStringReferenceProvider extends PsiReferenceProvider { StructuredDocString docString = StructuredDocString.parse(text); if (docString != null) { result.addAll(referencesFromNames(element, offset, docString, - docString.getTagArguments(StructuredDocString.PARAM_TAGS), "parameter")); + docString.getTagArguments(StructuredDocString.PARAM_TAGS), StructuredDocString.PARAMETER)); result.addAll(referencesFromNames(element, offset, docString, - docString.getTagArguments(StructuredDocString.PARAM_TYPE_TAGS), "parameter_type")); + docString.getTagArguments(StructuredDocString.PARAM_TYPE_TAGS), StructuredDocString.PARAMETER_TYPE)); result.addAll(referencesFromNames(element, offset, docString, - docString.getKeywordArgumentSubstrings(), "keyword")); + docString.getKeywordArgumentSubstrings(), StructuredDocString.KEYWORD)); result.addAll(referencesFromNames(element, offset, docString, - docString.getTagArguments(StructuredDocString.VARIABLE_TAGS), "variable")); + docString.getTagArguments(StructuredDocString.VARIABLE_TAGS), StructuredDocString.VARIABLE)); } return result.toArray(new PsiReference[result.size()]); } @@ -69,9 +69,11 @@ public class DocStringReferenceProvider extends PsiReferenceProvider { if (PyNames.isIdentifier(s)) { result.add(new DocStringParameterReference(element, name.getTextRange().shiftRight(offset), refType)); } - final Substring type = docString.getParamTypeSubstring(s); - if (type != null) { - result.addAll(parseTypeReferences(element, type, offset)); + if (!refType.equals(StructuredDocString.PARAMETER_TYPE)) { + final Substring type = docString.getParamTypeSubstring(s); + if (type != null) { + result.addAll(parseTypeReferences(element, type, offset)); + } } } final Substring rtype = docString.getReturnTypeSubstring(); diff --git a/python/src/com/jetbrains/python/documentation/StructuredDocString.java b/python/src/com/jetbrains/python/documentation/StructuredDocString.java index d1fe556b9af4..de4b44d5f3c4 100644 --- a/python/src/com/jetbrains/python/documentation/StructuredDocString.java +++ b/python/src/com/jetbrains/python/documentation/StructuredDocString.java @@ -33,6 +33,11 @@ public abstract class StructuredDocString { public static String[] RAISES_TAGS = new String[] { "raises", "raise", "except", "exception" }; public static String[] RETURN_TAGS = new String[] { "return", "returns" }; + public static String PARAMETER = "parameter"; + public static String PARAMETER_TYPE = "parameter_type"; + public static String KEYWORD = "keyword"; + public static String VARIABLE = "variable"; + @Nullable public static StructuredDocString parse(String text) { if (text == null) { diff --git a/python/testData/resolve/ReferenceInDocstring.py b/python/testData/resolve/ReferenceInDocstring.py new file mode 100644 index 000000000000..d3a566e71135 --- /dev/null +++ b/python/testData/resolve/ReferenceInDocstring.py @@ -0,0 +1,7 @@ +from datetime import datetime + +def foo(p): + """Foo + :param datetime p: a datetime + + """ \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyResolveTest.java b/python/testSrc/com/jetbrains/python/PyResolveTest.java index 3991c0d911c0..ab3a496bf146 100644 --- a/python/testSrc/com/jetbrains/python/PyResolveTest.java +++ b/python/testSrc/com/jetbrains/python/PyResolveTest.java @@ -476,4 +476,8 @@ public class PyResolveTest extends PyResolveTestCase { public void testKWArg() { assertResolvesTo(PyClass.class, "timedelta"); } + + public void testReferenceInDocstring() { + assertResolvesTo(PyClass.class, "datetime"); + } } From 67be1e45e0b1a079a247ac46cb1f6524e6531be2 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 30 Aug 2012 21:06:58 +0400 Subject: [PATCH 11/13] Added intention for replacing explicit iteration with delegation to sub-generator for Python 3.3 (PY-7383) --- python/src/META-INF/python-plugin-common.xml | 5 + .../com/jetbrains/python/PyBundle.properties | 3 + .../intentions/PyYieldFromIntention.java | 108 ++++++++++++++++++ python/testData/intentions/afterYieldFrom.py | 4 + python/testData/intentions/beforeYieldFrom.py | 5 + .../com/jetbrains/python/PyIntentionTest.java | 5 + 6 files changed, 130 insertions(+) create mode 100644 python/src/com/jetbrains/python/codeInsight/intentions/PyYieldFromIntention.java create mode 100644 python/testData/intentions/afterYieldFrom.py create mode 100644 python/testData/intentions/beforeYieldFrom.py diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml index 3920ee02cd5c..de48e57c2e00 100644 --- a/python/src/META-INF/python-plugin-common.xml +++ b/python/src/META-INF/python-plugin-common.xml @@ -240,6 +240,11 @@ Python + + com.jetbrains.python.codeInsight.intentions.PyYieldFromIntention + Python + + diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index a747d89160b7..4f6458af2e8e 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -191,6 +191,9 @@ INTN.specify.type.in.annotation=Specify type for reference using annotation #TypeAssertionIntention INTN.insert.assertion=Insert type assertion +#PyYieldFromIntention +INTN.yield.from=Transform explicit iteration with 'yield' into 'yield from' expression + # Conflict checker CONFLICT.name.$0.obscured=Name ''{0}'' obscured by local definitions CONFLICT.name.$0.obscured.cannot.convert=Name ''{0}'' obscured. Cannot convert. diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/PyYieldFromIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/PyYieldFromIntention.java new file mode 100644 index 000000000000..52c7356a8dc0 --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/intentions/PyYieldFromIntention.java @@ -0,0 +1,108 @@ +package com.jetbrains.python.codeInsight.intentions; + +import com.intellij.codeInsight.intention.impl.BaseIntentionAction; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import com.jetbrains.python.PyBundle; +import com.jetbrains.python.psi.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author vlan + */ +public class PyYieldFromIntention extends BaseIntentionAction { + @NotNull + @Override + public String getFamilyName() { + return PyBundle.message("INTN.yield.from"); + } + + @NotNull + @Override + public String getText() { + return PyBundle.message("INTN.yield.from"); + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + if (LanguageLevel.forElement(file).isAtLeast(LanguageLevel.PYTHON33)) { + final PyForStatement forLoop = findForStatementAtCaret(editor, file); + if (forLoop != null) { + final PyTargetExpression forTarget = findSingleForLoopTarget(forLoop); + final PyReferenceExpression yieldValue = findSingleYieldValue(forLoop); + if (forTarget != null && yieldValue != null) { + final String targetName = forTarget.getName(); + if (targetName != null && targetName.equals(yieldValue.getName())) { + return true; + } + } + } + } + return false; + } + + @Override + public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + final PyForStatement forLoop = findForStatementAtCaret(editor, file); + if (forLoop != null) { + final PyExpression source = forLoop.getForPart().getSource(); + if (source != null) { + final PyElementGenerator generator = PyElementGenerator.getInstance(project); + final String text = "yield from foo"; + final PyExpressionStatement exprStmt = generator.createFromText(LanguageLevel.forElement(file), PyExpressionStatement.class, text); + final PyExpression expr = exprStmt.getExpression(); + if (expr instanceof PyYieldExpression) { + final PyExpression yieldValue = ((PyYieldExpression)expr).getExpression(); + if (yieldValue != null) { + yieldValue.replace(source); + forLoop.replace(exprStmt); + } + } + } + } + } + + @Nullable + private static PyForStatement findForStatementAtCaret(@NotNull Editor editor, @NotNull PsiFile file) { + final PsiElement elementAtCaret = file.findElementAt(editor.getCaretModel().getOffset()); + return PsiTreeUtil.getParentOfType(elementAtCaret, PyForStatement.class); + } + + @Nullable + private static PyTargetExpression findSingleForLoopTarget(@NotNull PyForStatement forLoop) { + final PyForPart forPart = forLoop.getForPart(); + final PyExpression forTarget = forPart.getTarget(); + if (forTarget instanceof PyTargetExpression) { + return (PyTargetExpression)forTarget; + } + return null; + } + + @Nullable + private static PyReferenceExpression findSingleYieldValue(@NotNull PyForStatement forLoop) { + final PyForPart forPart = forLoop.getForPart(); + final PyStatementList stmtList = forPart.getStatementList(); + if (stmtList != null && forLoop.getElsePart() == null) { + final PyStatement[] statements = stmtList.getStatements(); + if (statements.length == 1) { + final PyStatement firstStmt = statements[0]; + if (firstStmt instanceof PyExpressionStatement) { + final PyExpression firstExpr = ((PyExpressionStatement)firstStmt).getExpression(); + if (firstExpr instanceof PyYieldExpression) { + final PyYieldExpression yieldExpr = (PyYieldExpression)firstExpr; + final PyExpression yieldValue = yieldExpr.getExpression(); + if (yieldValue instanceof PyReferenceExpression) { + return (PyReferenceExpression)yieldValue; + } + } + } + } + } + return null; + } +} diff --git a/python/testData/intentions/afterYieldFrom.py b/python/testData/intentions/afterYieldFrom.py new file mode 100644 index 000000000000..7c246bec7a41 --- /dev/null +++ b/python/testData/intentions/afterYieldFrom.py @@ -0,0 +1,4 @@ +def f(g): + yield 'begin' + yield from g() + print('end') diff --git a/python/testData/intentions/beforeYieldFrom.py b/python/testData/intentions/beforeYieldFrom.py new file mode 100644 index 000000000000..b93f4b5550d7 --- /dev/null +++ b/python/testData/intentions/beforeYieldFrom.py @@ -0,0 +1,5 @@ +def f(g): + yield 'begin' + for x in g(): + yield x + print('end') diff --git a/python/testSrc/com/jetbrains/python/PyIntentionTest.java b/python/testSrc/com/jetbrains/python/PyIntentionTest.java index cb69abafe97f..9ef848193884 100644 --- a/python/testSrc/com/jetbrains/python/PyIntentionTest.java +++ b/python/testSrc/com/jetbrains/python/PyIntentionTest.java @@ -261,6 +261,11 @@ public class PyIntentionTest extends PyTestCase { doDocStubTest(); } + // PY-7383 + public void testYieldFrom() { + doTest(PyBundle.message("INTN.yield.from"), LanguageLevel.PYTHON33); + } + private void doDocStubTest() { CodeInsightSettings codeInsightSettings = CodeInsightSettings.getInstance(); codeInsightSettings.JAVADOC_STUB_ON_ENTER = true; From fb233cb90ca724997dcca209aa85dee38b7b167a Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 30 Aug 2012 21:09:49 +0400 Subject: [PATCH 12/13] Disabled extract method refactoring for code fragments that contain 'yield' (PY-7381) --- .../codeFragment/PyCodeFragment.java | 9 ++++++++- .../codeFragment/PyCodeFragmentUtil.java | 18 +++++++++++++++--- .../controlflow/PyControlFlowBuilder.java | 9 +++++++++ .../extractmethod/PyExtractMethodHandler.java | 3 +-- .../refactoring/extractmethod/Yield.before.py | 6 ++++++ .../refactoring/PyExtractMethodTest.java | 5 +++++ 6 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 python/testData/refactoring/extractmethod/Yield.before.py diff --git a/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragment.java b/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragment.java index dc01ddba6036..1c8af2692966 100644 --- a/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragment.java +++ b/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragment.java @@ -10,15 +10,18 @@ import java.util.Set; public class PyCodeFragment extends CodeFragment { private final Set myGlobalWrites; private final Set myNonlocalWrites; + private final boolean myYieldInside; public PyCodeFragment(final Set input, final Set output, final Set globalWrites, final Set nonlocalWrites, - final boolean returnInside) { + final boolean returnInside, + final boolean yieldInside) { super(input, output, returnInside); myGlobalWrites = globalWrites; myNonlocalWrites = nonlocalWrites; + myYieldInside = yieldInside; } public Set getGlobalWrites() { @@ -28,4 +31,8 @@ public class PyCodeFragment extends CodeFragment { public Set getNonlocalWrites() { return myNonlocalWrites; } + + public boolean isYieldInside() { + return myYieldInside; + } } diff --git a/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java b/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java index 6dd64b2d2d37..6e532215f50e 100644 --- a/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java +++ b/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java @@ -82,7 +82,13 @@ public class PyCodeFragmentUtil { } } - return new PyCodeFragment(inputNames, outputNames, globalWrites, nonlocalWrites, subGraphAnalysis.returns > 0); + + final boolean yieldsFound = subGraphAnalysis.yieldExpressions > 0; + if (yieldsFound && LanguageLevel.forElement(owner).isOlderThan(LanguageLevel.PYTHON33)) { + throw new CannotCreateCodeFragmentException("Cannot perform refactoring with 'yield' statement inside code block"); + } + + return new PyCodeFragment(inputNames, outputNames, globalWrites, nonlocalWrites, subGraphAnalysis.returns > 0, yieldsFound); } private static boolean resolvesToBoundMethodParameter(@NotNull PsiElement element) { @@ -164,13 +170,15 @@ public class PyCodeFragmentUtil { private final int regularExits; private final int returns; private final int outerLoopBreaks; + private final int yieldExpressions; - public AnalysisResult(int starImports, int targetInstructions, int returns, int regularExits, int outerLoopBreaks) { + public AnalysisResult(int starImports, int targetInstructions, int returns, int regularExits, int outerLoopBreaks, int yieldExpressions) { this.starImports = starImports; this.targetInstructions = targetInstructions; this.regularExits = regularExits; this.returns = returns; this.outerLoopBreaks = outerLoopBreaks; + this.yieldExpressions = yieldExpressions; } } @@ -181,6 +189,7 @@ public class PyCodeFragmentUtil { final Set targetInstructions = new HashSet(); int starImports = 0; int outerLoopBreaks = 0; + int yieldExpressions = 0; for (Pair edge : getOutgoingEdges(subGraph)) { final Instruction sourceInstruction = edge.getFirst(); @@ -218,9 +227,12 @@ public class PyCodeFragmentUtil { outerLoopBreaks++; } } + if (element instanceof PyYieldExpression) { + yieldExpressions++; + } } - return new AnalysisResult(starImports, targetInstructions.size(), returnSources, regularSources, outerLoopBreaks); + return new AnalysisResult(starImports, targetInstructions.size(), returnSources, regularSources, outerLoopBreaks, yieldExpressions); } @NotNull diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java b/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java index 1673b8f26977..3727f9936db8 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java @@ -425,6 +425,15 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor { myBuilder.flowAbrupted(); } + @Override + public void visitPyYieldExpression(PyYieldExpression node) { + myBuilder.startNode(node); + final PyExpression expression = node.getExpression(); + if (expression != null) { + expression.accept(this); + } + } + @Override public void visitPyRaiseStatement(final PyRaiseStatement node) { myBuilder.startNode(node); diff --git a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodHandler.java b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodHandler.java index 5d94c22c6004..13851055b779 100644 --- a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodHandler.java +++ b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodHandler.java @@ -1,7 +1,6 @@ package com.jetbrains.python.refactoring.extractmethod; import com.intellij.codeInsight.codeFragment.CannotCreateCodeFragmentException; -import com.intellij.codeInsight.codeFragment.CodeFragment; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.*; import com.intellij.openapi.project.Project; @@ -93,7 +92,7 @@ public class PyExtractMethodHandler implements RefactoringActionHandler { if (owner == null) { return; } - final CodeFragment fragment; + final PyCodeFragment fragment; try { fragment = PyCodeFragmentUtil.createCodeFragment(owner, element1, element2); } diff --git a/python/testData/refactoring/extractmethod/Yield.before.py b/python/testData/refactoring/extractmethod/Yield.before.py new file mode 100644 index 000000000000..38ff1855adad --- /dev/null +++ b/python/testData/refactoring/extractmethod/Yield.before.py @@ -0,0 +1,6 @@ +def f(xs): + found = False + for x in xs: + yield x + found = True + print(found) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java index fd71ca35dfbe..4223c9a4fcd0 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java @@ -235,4 +235,9 @@ public class PyExtractMethodTest extends LightMarkedTestCase { public void testNonlocal() { doTest("baz", LanguageLevel.PYTHON30); } + + // PY-7381 + public void testYield() { + doFail("bar", "Cannot perform refactoring with 'yield' statement inside code block"); + } } From 0e7bad6ede667dd9a361cb1c0cdfe50d064e5304 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 30 Aug 2012 21:13:28 +0400 Subject: [PATCH 13/13] Added ability to extract sub-generator in Python 3.3 for extract method refactoring (PY-7382) --- .../extractmethod/PyExtractMethodUtil.java | 11 ++++++++++- .../refactoring/extractmethod/Yield33.after.py | 11 +++++++++++ .../refactoring/extractmethod/Yield33.before.py | 6 ++++++ .../python/refactoring/PyExtractMethodTest.java | 5 +++++ 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 python/testData/refactoring/extractmethod/Yield33.after.py create mode 100644 python/testData/refactoring/extractmethod/Yield33.before.py diff --git a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java index ca9a2e987751..d40240a60415 100644 --- a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java +++ b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java @@ -110,6 +110,9 @@ public class PyExtractMethodUtil { if (fragment.isReturnInstructionInside()) { builder.append("return "); } + if (fragment.isYieldInside()) { + builder.append("yield from "); + } if (isMethod) { appendSelf(firstElement, builder, isStaticMethod); } @@ -158,6 +161,9 @@ public class PyExtractMethodUtil { // Generate call element builder.append(" = "); + if (fragment.isYieldInside()) { + builder.append("yield from "); + } if (isMethod){ appendSelf(elementsRange.get(0), builder, isStaticMethod); } @@ -234,7 +240,7 @@ public class PyExtractMethodUtil { public static void extractFromExpression(final Project project, final Editor editor, - final CodeFragment fragment, + final PyCodeFragment fragment, final PsiElement expression) { if (!fragment.getOutputVariables().isEmpty()){ CommonRefactoringUtil.showErrorHint(project, editor, @@ -281,6 +287,9 @@ public class PyExtractMethodUtil { // Generating call element final StringBuilder builder = new StringBuilder(); builder.append("return "); + if (fragment.isYieldInside()) { + builder.append("yield from "); + } if (isMethod){ appendSelf(expression, builder, isStaticMethod); } diff --git a/python/testData/refactoring/extractmethod/Yield33.after.py b/python/testData/refactoring/extractmethod/Yield33.after.py new file mode 100644 index 000000000000..edec94a609ea --- /dev/null +++ b/python/testData/refactoring/extractmethod/Yield33.after.py @@ -0,0 +1,11 @@ +def bar(found_new, xs_new): + for x in xs_new: + yield x + found_new = True + return found_new + + +def f(xs): + found = False + found = yield from bar(found, xs) + print(found) \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/Yield33.before.py b/python/testData/refactoring/extractmethod/Yield33.before.py new file mode 100644 index 000000000000..38ff1855adad --- /dev/null +++ b/python/testData/refactoring/extractmethod/Yield33.before.py @@ -0,0 +1,6 @@ +def f(xs): + found = False + for x in xs: + yield x + found = True + print(found) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java index 4223c9a4fcd0..e9be5974a0e9 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java @@ -240,4 +240,9 @@ public class PyExtractMethodTest extends LightMarkedTestCase { public void testYield() { doFail("bar", "Cannot perform refactoring with 'yield' statement inside code block"); } + + // PY-7382 + public void testYield33() { + doTest("bar", LanguageLevel.PYTHON33); + } }