From 75ed1902fcf83cade2117758e03dfc019f31dce6 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Tue, 5 Jul 2011 11:08:15 +0400 Subject: [PATCH 01/15] fixed PY-3849 Support for type detection when type specified in docstring as :class:`SomeClass` or :py:class:`SomeClass` sphinx role --- .../src/com/jetbrains/python/documentation/SphinxDocString.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/documentation/SphinxDocString.java b/python/src/com/jetbrains/python/documentation/SphinxDocString.java index 0839ea0cecae..f55b1dd009b5 100644 --- a/python/src/com/jetbrains/python/documentation/SphinxDocString.java +++ b/python/src/com/jetbrains/python/documentation/SphinxDocString.java @@ -85,7 +85,7 @@ public class SphinxDocString extends StructuredDocString { String tagValue = line.substring(tagEnd).trim(); tagValue = StringUtil.replace(tagValue, ":py:class:", ""); tagValue = StringUtil.replace(tagValue, ":class:", ""); - tagValue = tagValue.replaceAll("`(\\w+)`", "$1"); + tagValue = tagValue.replaceAll("`([^`]+)`", "$1"); int pos = tagValue.indexOf(':'); if (pos < 0) return index; String value = tagValue.substring(pos+1).trim(); From c9a264105935080ff1e79ee994f4d9e5f4b4cccb Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Tue, 5 Jul 2011 11:29:18 +0400 Subject: [PATCH 02/15] fixed PY-4084 Invalid manual variable types declaration syntax for rst --- .../com/jetbrains/python/documentation/StructuredDocString.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/documentation/StructuredDocString.java b/python/src/com/jetbrains/python/documentation/StructuredDocString.java index 6cec55415320..63c111de356b 100644 --- a/python/src/com/jetbrains/python/documentation/StructuredDocString.java +++ b/python/src/com/jetbrains/python/documentation/StructuredDocString.java @@ -23,7 +23,7 @@ public abstract class StructuredDocString { if (text == null) { return null; } - if (text.contains(":param ") || text.contains(":rtype") || text.contains(":type ")) { + if (text.contains(":param ") || text.contains(":rtype") || text.contains(":type")) { return new SphinxDocString(text); } return new EpydocString(text); From 760268e74438855b0774c9f8dd0b5ace0f39b33f Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 5 Jul 2011 13:10:10 +0200 Subject: [PATCH 03/15] help topics for plugin (PY-4087, PY-4088, PY-4089) --- .../python/buildout/BuildoutFacetConfiguration.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/buildout/BuildoutFacetConfiguration.java b/python/src/com/jetbrains/python/buildout/BuildoutFacetConfiguration.java index cfbaa1a46f0b..a97a987fb57e 100644 --- a/python/src/com/jetbrains/python/buildout/BuildoutFacetConfiguration.java +++ b/python/src/com/jetbrains/python/buildout/BuildoutFacetConfiguration.java @@ -109,6 +109,10 @@ public class BuildoutFacetConfiguration implements FacetConfiguration { @Override public void disposeUIResources() { } - } + @Override + public String getHelpTopic() { + return "reference-python-buildout"; + } + } } From 5a74d644d7bc194ec8807cdbd140b17ed6f2c65c Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 5 Jul 2011 13:17:35 +0200 Subject: [PATCH 04/15] search for ipy.exe in path (PY-4035) --- .../src/com/jetbrains/python/sdk/IronPythonSdkFlavor.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/python/src/com/jetbrains/python/sdk/IronPythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/IronPythonSdkFlavor.java index b6a1698a2054..6598c72b4907 100644 --- a/python/src/com/jetbrains/python/sdk/IronPythonSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/IronPythonSdkFlavor.java @@ -4,10 +4,10 @@ import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.openapi.util.text.StringUtil; import java.io.File; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.List; +import java.util.Set; +import java.util.TreeSet; /** * @author yole @@ -20,7 +20,7 @@ public class IronPythonSdkFlavor extends PythonSdkFlavor { @Override public Collection suggestHomePaths() { - List result = new ArrayList(); + Set result = new TreeSet(); String root = System.getenv("ProgramFiles(x86)"); if (root == null) { root = System.getenv("ProgramFiles"); @@ -36,6 +36,8 @@ public class IronPythonSdkFlavor extends PythonSdkFlavor { } } } + WinPythonSdkFlavor.findInPath(result, "ipy.exe"); + WinPythonSdkFlavor.findInPath(result, "ipy64.exe"); return result; } From cbd92451a19e82714555c262acb661bd48dac2b1 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 5 Jul 2011 14:28:55 +0200 Subject: [PATCH 05/15] correct skipChildProcessing check when switching from Python stubs to AST (PY-4056) --- .../psi/impl/stubs/PyFileStubBuilder.java | 17 ++++++++++++++--- python/testData/stubs/IfNameMain.py | 1 + .../com/jetbrains/python/PyStubsTest.java | 11 ++++++++--- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/impl/stubs/PyFileStubBuilder.java b/python/src/com/jetbrains/python/psi/impl/stubs/PyFileStubBuilder.java index dbbd8ce73b0a..1294d395497c 100644 --- a/python/src/com/jetbrains/python/psi/impl/stubs/PyFileStubBuilder.java +++ b/python/src/com/jetbrains/python/psi/impl/stubs/PyFileStubBuilder.java @@ -1,12 +1,15 @@ package com.jetbrains.python.psi.impl.stubs; +import com.intellij.lang.ASTNode; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.stubs.DefaultStubBuilder; import com.intellij.psi.stubs.StubElement; +import com.intellij.psi.tree.IElementType; import com.jetbrains.python.psi.PyFile; import com.jetbrains.python.psi.PyIfStatement; import com.jetbrains.python.psi.PyUtil; +import org.jetbrains.annotations.Nullable; /** * @author yole @@ -23,9 +26,17 @@ public class PyFileStubBuilder extends DefaultStubBuilder { @Override protected boolean skipChildProcessingWhenBuildingStubs(PsiElement element, PsiElement child) { - if (element instanceof PyIfStatement) { - return PyUtil.isIfNameEqualsMain((PyIfStatement)element); + return element instanceof PyIfStatement && PyUtil.isIfNameEqualsMain((PyIfStatement)element); + } + + @Override + public boolean skipChildProcessingWhenBuildingStubs(@Nullable ASTNode parent, IElementType childType) { + if (parent != null) { + final PsiElement psi = parent.getPsi(); + if (psi != null) { + return psi instanceof PyIfStatement && PyUtil.isIfNameEqualsMain((PyIfStatement)psi); + } } - return false; + return super.skipChildProcessingWhenBuildingStubs(parent, childType); } } diff --git a/python/testData/stubs/IfNameMain.py b/python/testData/stubs/IfNameMain.py index 8f5d89eecafa..eee945a80e54 100644 --- a/python/testData/stubs/IfNameMain.py +++ b/python/testData/stubs/IfNameMain.py @@ -1,2 +1,3 @@ if __name__ == '__main__': + import sys xyzzy = None diff --git a/python/testSrc/com/jetbrains/python/PyStubsTest.java b/python/testSrc/com/jetbrains/python/PyStubsTest.java index 6c022b8477a1..2fbe2d86d478 100644 --- a/python/testSrc/com/jetbrains/python/PyStubsTest.java +++ b/python/testSrc/com/jetbrains/python/PyStubsTest.java @@ -1,6 +1,3 @@ -/* - * @author max - */ package com.jetbrains.python; import com.intellij.openapi.application.Result; @@ -26,6 +23,10 @@ import com.jetbrains.python.toolbox.Maybe; import java.util.Collection; import java.util.List; +/** + * @author max + * @author yole + */ @TestDataPath("$CONTENT_ROOT/../testData/stubs/") public class PyStubsTest extends PyLightFixtureTestCase { @@ -316,7 +317,11 @@ public class PyStubsTest extends PyLightFixtureTestCase { } public void testIfNameMain() { // PY-4008 + final PyFileImpl file = (PyFileImpl) getTestFile(); ensureVariableNotInIndex("xyzzy"); + assertNotParsed(file); + file.acceptChildren(new PyRecursiveElementVisitor()); // assert no error on switching from stub to AST + assertNotNull(file.getTreeElement()); } public void testVariableInComprehension() { // PY-4029 From 05ec42064388689a256dd31304001908575ca02a Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 5 Jul 2011 15:02:52 +0200 Subject: [PATCH 06/15] fixed a case when formatter breaks code (PY-4034 comment) --- python/src/com/jetbrains/python/formatter/PyBlock.java | 5 +++-- python/testData/formatter/tupleAssignment.py | 8 ++++++++ python/testData/formatter/tupleAssignment_after.py | 8 ++++++++ python/testSrc/com/jetbrains/python/PyFormatterTest.java | 4 ++++ 4 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 python/testData/formatter/tupleAssignment.py create mode 100644 python/testData/formatter/tupleAssignment_after.py diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index 40169cac6b31..2b03588bd7b4 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -177,7 +177,7 @@ public class PyBlock implements ASTBlock { childIndent = Indent.getNormalIndent(); } } - else if (parentType == PyElementTypes.PARENTHESIZED_EXPRESSION) { + else if (parentType == PyElementTypes.PARENTHESIZED_EXPRESSION && hasLineBreaksBefore(child, 1)) { childIndent = Indent.getNormalIndent(); } @@ -242,7 +242,8 @@ public class PyBlock implements ASTBlock { } private static boolean hasLineBreaksBefore(ASTNode child, int minCount) { - return isWhitespaceWithLineBreaks(TreeUtil.findLastLeaf(child.getTreePrev()), minCount) || + final ASTNode treePrev = child.getTreePrev(); + return (treePrev != null && isWhitespaceWithLineBreaks(TreeUtil.findLastLeaf(treePrev), minCount)) || isWhitespaceWithLineBreaks(child.getFirstChildNode(), minCount); } diff --git a/python/testData/formatter/tupleAssignment.py b/python/testData/formatter/tupleAssignment.py new file mode 100644 index 000000000000..3218dc78e424 --- /dev/null +++ b/python/testData/formatter/tupleAssignment.py @@ -0,0 +1,8 @@ +def bad_autoformat_example(): + a = 5 + b = 10 + print a, b + (a, b) = b, a + print a, b + a, b = b, a + print a, b \ No newline at end of file diff --git a/python/testData/formatter/tupleAssignment_after.py b/python/testData/formatter/tupleAssignment_after.py new file mode 100644 index 000000000000..3218dc78e424 --- /dev/null +++ b/python/testData/formatter/tupleAssignment_after.py @@ -0,0 +1,8 @@ +def bad_autoformat_example(): + a = 5 + b = 10 + print a, b + (a, b) = b, a + print a, b + a, b = b, a + print a, b \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyFormatterTest.java b/python/testSrc/com/jetbrains/python/PyFormatterTest.java index 80e4413d11cd..3bb0f3af12ed 100644 --- a/python/testSrc/com/jetbrains/python/PyFormatterTest.java +++ b/python/testSrc/com/jetbrains/python/PyFormatterTest.java @@ -113,6 +113,10 @@ public class PyFormatterTest extends PyLightFixtureTestCase { doTest(); } + public void testTupleAssignment() { // PY-4034 comment + doTest(); + } + public void testPsiFormatting() { // IDEA-69724 String initial = "def method_name(\n" + From 6a51d99f8bb56bac0ede5da23bfad32e4d5812c0 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 5 Jul 2011 15:26:53 +0200 Subject: [PATCH 07/15] commas don't need to be aligned (PY-4034) --- .../com/jetbrains/python/formatter/PyBlock.java | 3 +++ .../testSrc/com/jetbrains/python/PyIndentTest.java | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index 2b03588bd7b4..dd61926a9bbc 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -230,6 +230,9 @@ public class PyBlock implements ASTBlock { PyArgumentList argList = (PyArgumentList) _node.getPsi(); return argList != null && argList.getArguments().length > 1; } + if (child.getElementType() == PyTokenTypes.COMMA) { + return false; + } return true; } diff --git a/python/testSrc/com/jetbrains/python/PyIndentTest.java b/python/testSrc/com/jetbrains/python/PyIndentTest.java index 40f924bdabb7..6149e2775e8f 100644 --- a/python/testSrc/com/jetbrains/python/PyIndentTest.java +++ b/python/testSrc/com/jetbrains/python/PyIndentTest.java @@ -271,6 +271,20 @@ public class PyIndentTest extends PyLightFixtureTestCase { ""); } + public void testNestedLists() { // PY-4034 + doTest("mat = [\n" + + " [1, 2, 3, 4, 4.5],\n" + + " [5, 6, 7, 8],\n" + + " [9, 10, 11, 12],\n" + + "]", + "mat = [\n" + + " [1, 2, 3, 4, 4.5],\n" + + " [5, 6, 7, 8],\n" + + " [9, 10, 11, 12],\n" + + " \n" + + "]"); + } + /* TODO: formatter core problem? public void testAlignListBeforeEquals() throws Exception { From 2edd245ddadb6b66466b05443f10e25bfc662ddd Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 5 Jul 2011 16:13:47 +0200 Subject: [PATCH 08/15] implement folding for docstrings and "collapse by default | method bodies" option (PY-2068) --- .../python/PythonFoldingBuilder.java | 92 +++++++++++++++---- python/testData/folding/docString.py | 7 ++ .../com/jetbrains/python/PyFoldingTest.java | 4 + 3 files changed, 84 insertions(+), 19 deletions(-) create mode 100644 python/testData/folding/docString.py diff --git a/python/src/com/jetbrains/python/PythonFoldingBuilder.java b/python/src/com/jetbrains/python/PythonFoldingBuilder.java index ada510dc311a..9b041fbc9912 100644 --- a/python/src/com/jetbrains/python/PythonFoldingBuilder.java +++ b/python/src/com/jetbrains/python/PythonFoldingBuilder.java @@ -7,10 +7,14 @@ import com.intellij.lang.folding.FoldingDescriptor; import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.LineTokenizer; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.TokenType; import com.intellij.psi.tree.IElementType; import com.jetbrains.python.psi.PyFileElementType; +import com.jetbrains.python.psi.PyStringLiteralExpression; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -49,25 +53,10 @@ public class PythonFoldingBuilder implements FoldingBuilder, DumbAware { } } else if (node.getElementType() == PyElementTypes.STATEMENT_LIST) { - IElementType elType = node.getTreeParent().getElementType(); - if (elType == PyElementTypes.FUNCTION_DECLARATION || elType == PyElementTypes.CLASS_DECLARATION) { - ASTNode colon = node.getTreeParent().findChildByType(PyTokenTypes.COLON); - if (colon != null && colon.getStartOffset() + 1 < node.getTextRange().getEndOffset() - 1) { - final CharSequence chars = node.getChars(); - int nodeStart = node.getTextRange().getStartOffset(); - int endOffset = node.getTextRange().getEndOffset(); - while(endOffset > colon.getStartOffset()+2 && endOffset > nodeStart && Character.isWhitespace(chars.charAt(endOffset - nodeStart - 1))) { - endOffset--; - } - descriptors.add(new FoldingDescriptor(node, new TextRange(colon.getStartOffset() + 1, endOffset))); - } - else { - TextRange range = node.getTextRange(); - if (range.getStartOffset() < range.getEndOffset() - 1) { // only for ranges at least 1 char wide - descriptors.add(new FoldingDescriptor(node, range)); - } - } - } + foldStatementList(node, descriptors); + } + else if (node.getElementType() == PyElementTypes.STRING_LITERAL_EXPRESSION) { + foldDocString(node, descriptors); } ASTNode child = node.getFirstChildNode(); @@ -77,6 +66,53 @@ public class PythonFoldingBuilder implements FoldingBuilder, DumbAware { } } + private static void foldStatementList(ASTNode node, List descriptors) { + IElementType elType = node.getTreeParent().getElementType(); + if (elType == PyElementTypes.FUNCTION_DECLARATION || elType == PyElementTypes.CLASS_DECLARATION) { + ASTNode colon = node.getTreeParent().findChildByType(PyTokenTypes.COLON); + if (colon != null && colon.getStartOffset() + 1 < node.getTextRange().getEndOffset() - 1) { + final CharSequence chars = node.getChars(); + int nodeStart = node.getTextRange().getStartOffset(); + int endOffset = node.getTextRange().getEndOffset(); + while(endOffset > colon.getStartOffset()+2 && endOffset > nodeStart && Character.isWhitespace(chars.charAt(endOffset - nodeStart - 1))) { + endOffset--; + } + descriptors.add(new FoldingDescriptor(node, new TextRange(colon.getStartOffset() + 1, endOffset))); + } + else { + TextRange range = node.getTextRange(); + if (range.getStartOffset() < range.getEndOffset() - 1) { // only for ranges at least 1 char wide + descriptors.add(new FoldingDescriptor(node, range)); + } + } + } + } + + private static void foldDocString(ASTNode node, List descriptors) { + if (getDocStringOwnerType(node) != null && StringUtil.countChars(node.getText(), '\n') > 1) { + descriptors.add(new FoldingDescriptor(node, node.getTextRange())); + } + } + + @Nullable + private static IElementType getDocStringOwnerType(ASTNode node) { + final ASTNode treeParent = node.getTreeParent(); + IElementType parentType = treeParent.getElementType(); + if (parentType == PyElementTypes.EXPRESSION_STATEMENT && treeParent.getTreeParent() != null) { + final ASTNode parent2 = treeParent.getTreeParent(); + if (parent2.getElementType() == PyElementTypes.STATEMENT_LIST && parent2.getTreeParent() != null && treeParent == parent2.getFirstChildNode()) { + final ASTNode parent3 = parent2.getTreeParent(); + if (parent3.getElementType() == PyElementTypes.FUNCTION_DECLARATION || parent3.getElementType() == PyElementTypes.CLASS_DECLARATION) { + return parent3.getElementType(); + } + } + else if (parent2.getElementType() instanceof PyFileElementType) { + return parent2.getElementType(); + } + } + return null; + } + private static boolean isImport(ASTNode node, boolean orWhitespace) { if (node == null) return false; IElementType elementType = node.getElementType(); @@ -90,6 +126,14 @@ public class PythonFoldingBuilder implements FoldingBuilder, DumbAware { if (isImport(node, false)) { return "import ..."; } + if (node.getElementType() == PyElementTypes.STRING_LITERAL_EXPRESSION) { + final String stringValue = ((PyStringLiteralExpression)node.getPsi()).getStringValue().trim(); + final String[] lines = LineTokenizer.tokenize(stringValue, true); + if (lines.length > 2 && lines[1].trim().length() == 0) { + return "\"\"\"" + lines [0].trim() + "...\"\"\""; + } + return "\"\"\"...\"\"\""; + } return "..."; } @@ -97,6 +141,16 @@ public class PythonFoldingBuilder implements FoldingBuilder, DumbAware { if (isImport(node, false)) { return CodeFoldingSettings.getInstance().COLLAPSE_IMPORTS; } + if (node.getElementType() == PyElementTypes.STRING_LITERAL_EXPRESSION) { + if (getDocStringOwnerType(node) == PyElementTypes.FUNCTION_DECLARATION && CodeFoldingSettings.getInstance().COLLAPSE_METHODS) { + // method will be collapsed, no need to also collapse docstring + return false; + } + return CodeFoldingSettings.getInstance().COLLAPSE_DOC_COMMENTS; + } + if (node.getElementType() == PyElementTypes.STATEMENT_LIST && node.getTreeParent().getElementType() == PyElementTypes.FUNCTION_DECLARATION) { + return CodeFoldingSettings.getInstance().COLLAPSE_METHODS; + } return false; } } diff --git a/python/testData/folding/docString.py b/python/testData/folding/docString.py new file mode 100644 index 000000000000..05cd8ca5e9c2 --- /dev/null +++ b/python/testData/folding/docString.py @@ -0,0 +1,7 @@ +def foo(): + """ + This is a docstring. + + It spans several lines. + """: + pass \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyFoldingTest.java b/python/testSrc/com/jetbrains/python/PyFoldingTest.java index 3d58fe97a467..fd7d2cab82e7 100644 --- a/python/testSrc/com/jetbrains/python/PyFoldingTest.java +++ b/python/testSrc/com/jetbrains/python/PyFoldingTest.java @@ -13,4 +13,8 @@ public class PyFoldingTest extends PyLightFixtureTestCase { public void testClassTrailingSpace() { // PY-2544 doTest(); } + + public void testDocString() { + doTest(); + } } From 718394a701f743e638b82a62f89734155b1681fd Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 5 Jul 2011 16:33:48 +0200 Subject: [PATCH 09/15] prepend system pythonpath to pycharm generated one (PY-3541) --- python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java index cd5417fdefbe..76b5a5678507 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java @@ -104,7 +104,7 @@ public abstract class PythonSdkFlavor { } public void initPythonPath(GeneralCommandLine cmd, Collection path) { - addToEnv(cmd, PYTHONPATH, StringUtil.join(path, File.pathSeparator)); + addToEnv(cmd, PYTHONPATH, appendSystemPythonPath(StringUtil.join(path, File.pathSeparator))); } public static void addToEnv(GeneralCommandLine cmd, final String key, String value) { From 75b78dc992c17b58117b1523d007daf9d74ddf8d Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 5 Jul 2011 18:00:34 +0200 Subject: [PATCH 10/15] help for Python extract method dialog (PY-4097) --- .../refactoring/extractmethod/PyExtractMethodUtil.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java index 4bf01f01988e..aebbc3700cd6 100644 --- a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java +++ b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java @@ -460,7 +460,12 @@ public class PyExtractMethodUtil { } }; - final AbstractExtractMethodDialog dialog = new AbstractExtractMethodDialog(project, "method_name", fragment, validator, decorator); + final AbstractExtractMethodDialog dialog = new AbstractExtractMethodDialog(project, "method_name", fragment, validator, decorator) { + @Override + protected String getHelpId() { + return "python.reference.extractMethod"; + } + }; dialog.show(); //return if don`t want to extract method From d5f7364d73319fca0154be26213ec0828b024a7a Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 5 Jul 2011 18:16:15 +0200 Subject: [PATCH 11/15] help ID (PY-4091) --- .../pluginSrc/com/jetbrains/python/module/PythonModuleType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/pluginSrc/com/jetbrains/python/module/PythonModuleType.java b/python/pluginSrc/com/jetbrains/python/module/PythonModuleType.java index 976985612984..98089001ec73 100644 --- a/python/pluginSrc/com/jetbrains/python/module/PythonModuleType.java +++ b/python/pluginSrc/com/jetbrains/python/module/PythonModuleType.java @@ -26,7 +26,7 @@ public class PythonModuleType extends PythonModuleTypeBase final PythonModuleBuilder moduleBuilder, final ModulesProvider modulesProvider) { ArrayList steps = new ArrayList(); - steps.add(new PythonSdkSelectStep(moduleBuilder, null, null, wizardContext.getProject())); + steps.add(new PythonSdkSelectStep(moduleBuilder, null, "reference.project.structure.sdk.python", wizardContext.getProject())); final List frameworkSupportProviderList = FrameworkSupportUtil.getProviders(getInstance()); if (!frameworkSupportProviderList.isEmpty()) { steps.add(new SupportForFrameworksStep(moduleBuilder, LibrariesContainerFactory.createContainer(wizardContext.getProject()))); From 487045f46036d03ba9b87e69d5739a9cb8ddef89 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 5 Jul 2011 19:02:58 +0200 Subject: [PATCH 12/15] rename correctly updates type names in epydoc docstrings (PY-4101) --- .../python/documentation/DocStringReferenceProvider.java | 8 ++++---- python/testData/refactoring/rename/epydocRenameType.py | 9 +++++++++ .../refactoring/rename/epydocRenameType_after.py | 9 +++++++++ .../com/jetbrains/python/refactoring/PyRenameTest.java | 4 ++++ 4 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 python/testData/refactoring/rename/epydocRenameType.py create mode 100644 python/testData/refactoring/rename/epydocRenameType_after.py diff --git a/python/src/com/jetbrains/python/documentation/DocStringReferenceProvider.java b/python/src/com/jetbrains/python/documentation/DocStringReferenceProvider.java index 1a8e9433ea25..ebdb94f8b706 100644 --- a/python/src/com/jetbrains/python/documentation/DocStringReferenceProvider.java +++ b/python/src/com/jetbrains/python/documentation/DocStringReferenceProvider.java @@ -53,7 +53,8 @@ public class DocStringReferenceProvider extends PsiReferenceProvider { @Override public boolean matches(char c) { return Character.isLetterOrDigit(c) || c == '_'; }}.negate(); - if (docString.substring(tagRange.getStartOffset(), tagRange.getEndOffset()).startsWith(":")) { + final String tagName = docString.substring(tagRange.getStartOffset(), tagRange.getEndOffset()); + if (tagName.startsWith(":")) { int ws = CharMatcher.anyOf(" \t*").indexIn(docString, pos+1); if (ws != -1) { int next = CharMatcher.anyOf(" \t*").negate().indexIn(docString, ws); @@ -69,10 +70,9 @@ public class DocStringReferenceProvider extends PsiReferenceProvider { if (endPos < 0) { endPos = docString.length(); } - result.add(new DocStringParameterReference(element, new TextRange(pos, endPos))); - if (docString.substring(tagRange.getStartOffset(), tagRange.getEndOffset()).equals(":type") || - docString.substring(tagRange.getStartOffset(), tagRange.getEndOffset()).equals(":rtype")) { + result.add(new DocStringParameterReference(element, new TextRange(pos, endPos))); + if (tagName.equals(":type") || tagName.equals(":rtype") || tagName.equals("@type") || tagName.equals("@rtype")) { pos = CharMatcher.anyOf(" \t*").negate().indexIn(docString, endPos+1); endPos = CharMatcher.anyOf("\n\r").indexIn(docString, pos+1); if (endPos == -1) diff --git a/python/testData/refactoring/rename/epydocRenameType.py b/python/testData/refactoring/rename/epydocRenameType.py new file mode 100644 index 000000000000..f65ea72b70ab --- /dev/null +++ b/python/testData/refactoring/rename/epydocRenameType.py @@ -0,0 +1,9 @@ +class Xyzzy: + pass + +def foo(p): + """ + @param p: the magic word + @type p: Xyzzy + @return: + """ \ No newline at end of file diff --git a/python/testData/refactoring/rename/epydocRenameType_after.py b/python/testData/refactoring/rename/epydocRenameType_after.py new file mode 100644 index 000000000000..cbe7dee49e0e --- /dev/null +++ b/python/testData/refactoring/rename/epydocRenameType_after.py @@ -0,0 +1,9 @@ +class Shazam: + pass + +def foo(p): + """ + @param p: the magic word + @type p: Shazam + @return: + """ \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyRenameTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyRenameTest.java index 9cc62c0bcb29..4f90304979c0 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyRenameTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyRenameTest.java @@ -66,6 +66,10 @@ public class PyRenameTest extends PyLightFixtureTestCase { doTest("bar"); } + public void testEpydocRenameType() { + doTest("Shazam"); + } + public void testRenameGlobalWithoutToplevel() { // PY-3547 doTest("bar"); } From 5aa0bfa0c686c5146f0e6b29a0b8c63fb662256b Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 5 Jul 2011 18:59:38 +0400 Subject: [PATCH 13/15] Show Python underscored variables in structure view as protected --- .../structureView/PyStructureViewElement.java | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java b/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java index a285357192c4..9ecb0eb5b8a3 100644 --- a/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java +++ b/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java @@ -32,6 +32,7 @@ public class PyStructureViewElement implements StructureViewTreeElement { NORMAL, // visible INVISIBLE, // not visible: e.g. local to function PRIVATE, // "__foo" in a class + PROTECTED, // "_foo" PREDEFINED // like "__init__"; only if really visible } @@ -132,12 +133,17 @@ public class PyStructureViewElement implements StructureViewTreeElement { } private static Visibility getVisibilityByName(@Nullable String name) { - if (name != null && name.startsWith("__")) { - if (PyNames.UnderscoredAttributes.contains(name)) { - return Visibility.PREDEFINED; + if (name != null) { + if (name.startsWith("__")) { + if (PyNames.UnderscoredAttributes.contains(name)) { + return Visibility.PREDEFINED; + } + else { + return Visibility.PRIVATE; + } } - else { - return Visibility.PRIVATE; + else if (name.startsWith("_")) { + return Visibility.PROTECTED; } } return Visibility.NORMAL; @@ -211,13 +217,15 @@ public class PyStructureViewElement implements StructureViewTreeElement { LayeredIcon icon = new LayeredIcon(2); icon.setIcon(normal_icon, 0); Icon overlay = null; - if (myVisibility == Visibility.PRIVATE) { + if (myVisibility == Visibility.PRIVATE || myVisibility == Visibility.PROTECTED) { overlay = PyIcons.PRIVATE; } else if (myVisibility == Visibility.PREDEFINED) { overlay = PyIcons.PREDEFINED; } - else if (myVisibility == Visibility.INVISIBLE) overlay = PyIcons.INVISIBLE; + else if (myVisibility == Visibility.INVISIBLE) { + overlay = PyIcons.INVISIBLE; + } if (overlay != null) { icon.setIcon(overlay, 1); } From 46a358dc15e5ac884f9e70e514cde468ef28fc92 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 5 Jul 2011 19:03:45 +0400 Subject: [PATCH 14/15] Show inherited members in structure view (PY-3936) --- .../PyInheritedMembersFilter.java | 50 ++++++++ .../structureView/PyStructureViewElement.java | 109 +++++++++++------- .../structureView/PyStructureViewModel.java | 9 ++ python/testData/structureView/inherited.py | 6 + .../jetbrains/python/PyStructureViewTest.java | 35 +++++- 5 files changed, 164 insertions(+), 45 deletions(-) create mode 100644 python/src/com/jetbrains/python/structureView/PyInheritedMembersFilter.java create mode 100644 python/testData/structureView/inherited.py diff --git a/python/src/com/jetbrains/python/structureView/PyInheritedMembersFilter.java b/python/src/com/jetbrains/python/structureView/PyInheritedMembersFilter.java new file mode 100644 index 000000000000..ae89e4c39316 --- /dev/null +++ b/python/src/com/jetbrains/python/structureView/PyInheritedMembersFilter.java @@ -0,0 +1,50 @@ +package com.jetbrains.python.structureView; + +import com.intellij.ide.IdeBundle; +import com.intellij.ide.util.treeView.smartTree.ActionPresentation; +import com.intellij.ide.util.treeView.smartTree.ActionPresentationData; +import com.intellij.ide.util.treeView.smartTree.Filter; +import com.intellij.ide.util.treeView.smartTree.TreeElement; +import com.intellij.openapi.util.IconLoader; +import com.jetbrains.python.psi.PyElement; +import org.jetbrains.annotations.NotNull; + +/** + * @author vlan + */ +public class PyInheritedMembersFilter implements Filter { + private static final String ID = "SHOW_INHERITED"; + + @Override + public boolean isReverted() { + return true; + } + + @Override + public boolean isVisible(TreeElement treeNode) { + if (treeNode instanceof PyStructureViewElement) { + final PyStructureViewElement sve = (PyStructureViewElement)treeNode; + return !sve.isInherited(); + } + return true; + } + + @NotNull + @Override + public String getName() { + return ID; + } + + @Override + public String toString() { + return getName(); + } + + @NotNull + @Override + public ActionPresentation getPresentation() { + return new ActionPresentationData(IdeBundle.message("action.structureview.show.inherited"), + null, + IconLoader.getIcon("/hierarchy/supertypes.png")); + } +} diff --git a/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java b/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java index 9ecb0eb5b8a3..fa7a06b61da2 100644 --- a/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java +++ b/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java @@ -2,18 +2,17 @@ package com.jetbrains.python.structureView; import com.intellij.ide.structureView.StructureViewTreeElement; import com.intellij.navigation.ItemPresentation; +import com.intellij.openapi.editor.colors.CodeInsightColors; import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.util.Iconable; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.ui.LayeredIcon; import com.intellij.util.Function; -import com.intellij.util.PlatformIcons; import com.jetbrains.python.PyIcons; import com.jetbrains.python.PyNames; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.ParamHelper; -import com.jetbrains.python.psi.impl.PyBuiltinCache; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -39,20 +38,26 @@ public class PyStructureViewElement implements StructureViewTreeElement { private PyElement myElement; private Visibility myVisibility; private Icon myIcon; + private boolean myInherited; - public PyStructureViewElement(PyElement element, Visibility vis) { + public PyStructureViewElement(PyElement element, Visibility vis, boolean inherited) { myElement = element; myVisibility = vis; + myInherited = inherited; } public PyStructureViewElement(PyElement element) { - this(element, Visibility.NORMAL); + this(element, Visibility.NORMAL, false); } public PyElement getValue() { return myElement; } + public boolean isInherited() { + return myInherited; + } + public void navigate(boolean requestFocus) { myElement.navigate(requestFocus); } @@ -69,46 +74,65 @@ public class PyStructureViewElement implements StructureViewTreeElement { myIcon = icon; } + @Override + public boolean equals(Object o) { + if (o instanceof StructureViewTreeElement) { + final Object value = ((StructureViewTreeElement)o).getValue(); + final String name = myElement.getName(); + if (value instanceof PyElement && name != null) { + return name.equals(((PyElement)value).getName()); + } + } + return false; + } + + @Override + public int hashCode() { + final String name = myElement.getName(); + return name != null ? name.hashCode() : 0; + } + public StructureViewTreeElement[] getChildren() { - final Set childrenElements = new LinkedHashSet(); - myElement.acceptChildren(new PyElementVisitor() { - @Override - public void visitElement(PsiElement element) { - if (element instanceof PyClass || element instanceof PyFunction || - (!(myElement instanceof PyClass) && isWorthyItem(element))) { - childrenElements.add((PyElement)element); - } - else { - element.acceptChildren(this); - } - } - }); - final Collection children = new ArrayList(); - for (PyElement element : childrenElements) { - final Visibility vis; - if (PsiTreeUtil.getParentOfType(element, PyFunction.class) != null) { - // whatever is defined inside a def, is hidden - vis = Visibility.INVISIBLE; - } - else { - vis = getVisibilityByName(element.getName()); - } - final PyStructureViewElement e = new PyStructureViewElement(element, vis); - children.add(e); - if (element instanceof PyClass && element.isValid()) { - PyClass the_exception = PyBuiltinCache.getInstance(element).getClass("Exception"); - final PyClass cls = (PyClass)element; - for (PyClass anc : cls.iterateAncestorClasses()) { - if (anc == the_exception) { - e.setIcon(PlatformIcons.EXCEPTION_CLASS_ICON); - break; - } + final Collection children = new LinkedHashSet(); + for (PyElement e : getElementChildren(myElement)) { + children.add(new PyStructureViewElement(e, getElementVisibility(e), false)); + } + if (myElement instanceof PyClass) { + for (PyClass c : ((PyClass)myElement).iterateAncestorClasses()) { + for (PyElement e: getElementChildren(c)) { + children.add(new PyStructureViewElement(e, getElementVisibility(e), true)); } } } + return children.toArray(new StructureViewTreeElement[children.size()]); + } + + private static Visibility getElementVisibility(PyElement element) { + if (!(element instanceof PyTargetExpression) && PsiTreeUtil.getParentOfType(element, PyFunction.class) != null) { + return Visibility.INVISIBLE; + } + else { + return getVisibilityByName(element.getName()); + } + } + + private static Collection getElementChildren(final PyElement element) { + final Collection children = new ArrayList(); + element.acceptChildren(new PyElementVisitor() { + @Override + public void visitElement(PsiElement e) { + if (e instanceof PyClass || e instanceof PyFunction || + (!(element instanceof PyClass) && isWorthyItem(e))) { + children.add((PyElement)e); + } + else { + e.acceptChildren(this); + } + } + }); final Collection attrs = new ArrayList(); - if (myElement instanceof PyClass) { - final PyClass c = (PyClass)myElement; + if (element instanceof PyClass) { + final PyClass c = (PyClass)element; final Comparator comparator = new Comparator() { @Override public int compare(PyTargetExpression e1, PyTargetExpression e2) { @@ -126,10 +150,10 @@ public class PyStructureViewElement implements StructureViewTreeElement { } for (PyTargetExpression e : attrs) { if (e.isValid()) { - children.add(new PyStructureViewElement(e, getVisibilityByName(e.getName()))); + children.add(e); } } - return children.toArray(new StructureViewTreeElement[children.size()]); + return children; } private static Visibility getVisibilityByName(@Nullable String name) { @@ -199,6 +223,9 @@ public class PyStructureViewElement implements StructureViewTreeElement { @Nullable public TextAttributesKey getTextAttributesKey() { + if (isInherited()) { + return CodeInsightColors.NOT_USED_ELEMENT_ATTRIBUTES; + } return null; } diff --git a/python/src/com/jetbrains/python/structureView/PyStructureViewModel.java b/python/src/com/jetbrains/python/structureView/PyStructureViewModel.java index 01d2970dbab7..2ca802c0ecf3 100644 --- a/python/src/com/jetbrains/python/structureView/PyStructureViewModel.java +++ b/python/src/com/jetbrains/python/structureView/PyStructureViewModel.java @@ -3,6 +3,7 @@ package com.jetbrains.python.structureView; import com.intellij.ide.structureView.StructureViewModel; import com.intellij.ide.structureView.StructureViewModelBase; import com.intellij.ide.structureView.StructureViewTreeElement; +import com.intellij.ide.util.treeView.smartTree.Filter; import com.intellij.ide.util.treeView.smartTree.Sorter; import com.intellij.psi.PsiFile; import com.jetbrains.python.psi.*; @@ -34,6 +35,14 @@ public class PyStructureViewModel extends StructureViewModelBase implements Stru return element instanceof PyClass; } + @NotNull + @Override + public Filter[] getFilters() { + return new Filter[] { + new PyInheritedMembersFilter(), + }; + } + @Override public boolean isAutoExpand(StructureViewTreeElement element) { return element.getValue() instanceof PsiFile; diff --git a/python/testData/structureView/inherited.py b/python/testData/structureView/inherited.py new file mode 100644 index 000000000000..4d456332117c --- /dev/null +++ b/python/testData/structureView/inherited.py @@ -0,0 +1,6 @@ +class C(object): + def f(self, x): + self.x = x + + def __str__(self): + return self.x \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyStructureViewTest.java b/python/testSrc/com/jetbrains/python/PyStructureViewTest.java index fac9f3eed1ee..f1c4292c0b51 100644 --- a/python/testSrc/com/jetbrains/python/PyStructureViewTest.java +++ b/python/testSrc/com/jetbrains/python/PyStructureViewTest.java @@ -24,10 +24,12 @@ public class PyStructureViewTest extends PyLightFixtureTestCase { " D1(C)\n" + " D2(C)\n" + " D3(lib1.C)\n" + - " D4(foo.bar.C)\n"); + " D4(foo.bar.C)\n", + false); } - public void testAttributes() { // PY-3371 + // PY-3371 + public void testAttributes() { myFixture.configureByFile(TEST_DIRECTORY + "attributes.py"); doTest("-attributes.py\n" + " -B(object)\n" + @@ -48,13 +50,38 @@ public class PyStructureViewTest extends PyLightFixtureTestCase { " i3\n" + " i4\n" + " i5\n" + - " g2\n"); + " g2\n", + false); } - private void doTest(final String expected) { + // PY-3936 + public void testInherited() { + myFixture.configureByFile(TEST_DIRECTORY + "inherited.py"); + doTest("-inherited.py\n" + + " -C(object)\n" + + " f(self, x)\n" + + " __str__(self)\n" + + " x\n" + + " __delattr__(self, name)\n" + + " __getattribute__(self, name)\n" + + " __hash__(self)\n" + + " __init__(self)\n" + + " __new__(cls, *more)\n" + + " __reduce_ex__(self, *args, **kwargs)\n" + + " __reduce__(self, *args, **kwargs)\n" + + " __repr__(self)\n" + + " __setattr__(self, name, value)\n" + + " __class__\n" + + " __dict__\n" + + " __doc__\n", + true); + } + + private void doTest(final String expected, final boolean inherited) { myFixture.testStructureView(new Consumer() { @Override public void consume(StructureViewComponent component) { + component.setActionActive("SHOW_INHERITED", !inherited); assertTreeEqual(component.getTree(), expected); } }); From 5583aa6d864566a2cb94246c0fe384522e0dfd57 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 6 Jul 2011 16:29:07 +0400 Subject: [PATCH 15/15] Added button to show fields in structure view (PY-3936) --- .../python/structureView/PyFieldsFilter.java | 47 +++++++++++++++++++ .../structureView/PyStructureViewElement.java | 18 +++++-- .../structureView/PyStructureViewModel.java | 1 + 3 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 python/src/com/jetbrains/python/structureView/PyFieldsFilter.java diff --git a/python/src/com/jetbrains/python/structureView/PyFieldsFilter.java b/python/src/com/jetbrains/python/structureView/PyFieldsFilter.java new file mode 100644 index 000000000000..05393d646ba5 --- /dev/null +++ b/python/src/com/jetbrains/python/structureView/PyFieldsFilter.java @@ -0,0 +1,47 @@ +package com.jetbrains.python.structureView; + +import com.intellij.ide.IdeBundle; +import com.intellij.ide.util.treeView.smartTree.ActionPresentation; +import com.intellij.ide.util.treeView.smartTree.ActionPresentationData; +import com.intellij.ide.util.treeView.smartTree.Filter; +import com.intellij.ide.util.treeView.smartTree.TreeElement; +import com.intellij.util.PlatformIcons; +import org.jetbrains.annotations.NotNull; + +/** + * @author vlan + */ +public class PyFieldsFilter implements Filter { + private static final String ID = "SHOW_FIELDS"; + + @Override + public boolean isReverted() { + return true; + } + + @Override + public boolean isVisible(TreeElement treeNode) { + if (treeNode instanceof PyStructureViewElement) { + final PyStructureViewElement sve = (PyStructureViewElement)treeNode; + return !sve.isField(); + } + return true; + } + + @NotNull + @Override + public String getName() { + return ID; + } + + @Override + public String toString() { + return getName(); + } + + @NotNull + @Override + public ActionPresentation getPresentation() { + return new ActionPresentationData(IdeBundle.message("action.structureview.show.fields"), null, PlatformIcons.FIELD_ICON); + } +} diff --git a/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java b/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java index fa7a06b61da2..98b873a81ddf 100644 --- a/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java +++ b/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java @@ -39,15 +39,17 @@ public class PyStructureViewElement implements StructureViewTreeElement { private Visibility myVisibility; private Icon myIcon; private boolean myInherited; + private boolean myField; - public PyStructureViewElement(PyElement element, Visibility vis, boolean inherited) { + public PyStructureViewElement(PyElement element, Visibility vis, boolean inherited, boolean field) { myElement = element; myVisibility = vis; myInherited = inherited; + myField = field; } public PyStructureViewElement(PyElement element) { - this(element, Visibility.NORMAL, false); + this(element, Visibility.NORMAL, false, false); } public PyElement getValue() { @@ -58,6 +60,10 @@ public class PyStructureViewElement implements StructureViewTreeElement { return myInherited; } + public boolean isField() { + return myField; + } + public void navigate(boolean requestFocus) { myElement.navigate(requestFocus); } @@ -95,18 +101,22 @@ public class PyStructureViewElement implements StructureViewTreeElement { public StructureViewTreeElement[] getChildren() { final Collection children = new LinkedHashSet(); for (PyElement e : getElementChildren(myElement)) { - children.add(new PyStructureViewElement(e, getElementVisibility(e), false)); + children.add(new PyStructureViewElement(e, getElementVisibility(e), false, elementIsField(e))); } if (myElement instanceof PyClass) { for (PyClass c : ((PyClass)myElement).iterateAncestorClasses()) { for (PyElement e: getElementChildren(c)) { - children.add(new PyStructureViewElement(e, getElementVisibility(e), true)); + children.add(new PyStructureViewElement(e, getElementVisibility(e), true, elementIsField(e))); } } } return children.toArray(new StructureViewTreeElement[children.size()]); } + private static boolean elementIsField(PyElement element) { + return element instanceof PyTargetExpression && PsiTreeUtil.getParentOfType(element, PyClass.class) != null; + } + private static Visibility getElementVisibility(PyElement element) { if (!(element instanceof PyTargetExpression) && PsiTreeUtil.getParentOfType(element, PyFunction.class) != null) { return Visibility.INVISIBLE; diff --git a/python/src/com/jetbrains/python/structureView/PyStructureViewModel.java b/python/src/com/jetbrains/python/structureView/PyStructureViewModel.java index 2ca802c0ecf3..d894abbb2a91 100644 --- a/python/src/com/jetbrains/python/structureView/PyStructureViewModel.java +++ b/python/src/com/jetbrains/python/structureView/PyStructureViewModel.java @@ -40,6 +40,7 @@ public class PyStructureViewModel extends StructureViewModelBase implements Stru public Filter[] getFilters() { return new Filter[] { new PyInheritedMembersFilter(), + new PyFieldsFilter(), }; }