diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml index c9dd720108fe..801590de42f6 100644 --- a/python/src/META-INF/python-plugin-common.xml +++ b/python/src/META-INF/python-plugin-common.xml @@ -24,6 +24,7 @@ + diff --git a/python/src/com/jetbrains/python/editor/PyEnterBetweenBracketsHandler.java b/python/src/com/jetbrains/python/editor/PyEnterBetweenBracketsHandler.java new file mode 100644 index 000000000000..0ceb1e79eb2e --- /dev/null +++ b/python/src/com/jetbrains/python/editor/PyEnterBetweenBracketsHandler.java @@ -0,0 +1,13 @@ +package com.jetbrains.python.editor; + +import com.intellij.codeInsight.editorActions.enter.EnterBetweenBracesHandler; + +/** + * @author yole + */ +public class PyEnterBetweenBracketsHandler extends EnterBetweenBracesHandler { + @Override + protected boolean isBracePair(char c1, char c2) { + return c1 == '[' && c2 == ']'; + } +} diff --git a/python/src/com/jetbrains/python/editor/PythonEnterHandler.java b/python/src/com/jetbrains/python/editor/PythonEnterHandler.java index 079e0f40a707..e13480e7cfbe 100644 --- a/python/src/com/jetbrains/python/editor/PythonEnterHandler.java +++ b/python/src/com/jetbrains/python/editor/PythonEnterHandler.java @@ -7,6 +7,7 @@ import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.actionSystem.EditorActionHandler; +import com.intellij.openapi.editor.actions.SplitLineAction; import com.intellij.openapi.util.Ref; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; @@ -46,6 +47,10 @@ public class PythonEnterHandler implements EnterHandlerDelegate { if (!(file instanceof PyFile)) { return Result.Continue; } + final Boolean isSplitLine = DataManager.getInstance().loadFromDataContext(dataContext, SplitLineAction.SPLIT_LINE_KEY); + if (isSplitLine != null) { + return Result.Continue; + } Document doc = editor.getDocument(); PsiDocumentManager.getInstance(file.getProject()).commitDocument(doc); final int offset = caretOffset.get(); diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index 1bc4435d1a48..10e355f9abb4 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -129,7 +129,7 @@ public class PyBlock implements ASTBlock { !ourBrackets.contains(childType)) { wrap = Wrap.createWrap(WrapType.NORMAL, true); } - if (needListAlignment(child)) { + if (needListAlignment(child) && !isEmptyList(_node.getPsi())) { childAlignment = getAlignmentForChildren(); } } @@ -149,7 +149,7 @@ public class PyBlock implements ASTBlock { childIndent = Indent.getNoneIndent(); } else { - childIndent = Indent.getContinuationIndent(); + childIndent = Indent.getNormalIndent(); } } else if (parentType == PyElementTypes.DICT_LITERAL_EXPRESSION) { @@ -171,6 +171,12 @@ public class PyBlock implements ASTBlock { childAlignment = getAlignmentForChildren(); } } + else if (parentType == PyElementTypes.KEY_VALUE_EXPRESSION) { + PyKeyValueExpression keyValue = (PyKeyValueExpression) _node.getPsi(); + if (keyValue != null && child.getPsi() == keyValue.getValue()) { + childIndent = Indent.getNormalIndent(); + } + } if (isAfterStatementList(child) && !hasLineBreaksBefore(child, 2)) { // maybe enter was pressed and cut us from a previous (nested) statement list childIndent = Indent.getNormalIndent(); @@ -179,6 +185,16 @@ public class PyBlock implements ASTBlock { return new PyBlock(child, childAlignment, childIndent, wrap, mySettings); } + private static boolean isEmptyList(PsiElement psi) { + if (psi instanceof PyDictLiteralExpression) { + return ((PyDictLiteralExpression) psi).getElements().length == 0; + } + if (psi instanceof PySequenceExpression) { + return ((PySequenceExpression) psi).getElements().length == 0; + } + return false; + } + private static boolean isAfterStatementList(ASTNode child) { try { PsiElement prev = sure(child.getPsi().getPrevSibling()); @@ -193,17 +209,33 @@ public class PyBlock implements ASTBlock { } } - private static boolean needListAlignment(ASTNode child) { + private boolean needListAlignment(ASTNode child) { IElementType childType = child.getElementType(); if (PyTokenTypes.OPEN_BRACES.contains(childType)) { return false; } if (PyTokenTypes.CLOSE_BRACES.contains(childType)) { - return PsiTreeUtil.getParentOfType(child.getPsi(), PyArgumentList.class) != null; + ASTNode prevNonSpace = findPrevNonSpaceNode(child); + if (prevNonSpace != null && prevNonSpace.getElementType() == PyTokenTypes.COMMA) { + return true; + } + return false; + } + if (_node.getElementType() == PyElementTypes.ARGUMENT_LIST) { + PyArgumentList argList = (PyArgumentList) _node.getPsi(); + return argList != null && argList.getArguments().length > 1; } return true; } + @Nullable + private static ASTNode findPrevNonSpaceNode(ASTNode node) { + do { + node = node.getTreePrev(); + } while(node != null && (node.getElementType() == TokenType.WHITE_SPACE || PyTokenTypes.WHITESPACE.contains(node.getElementType()))); + return node; + } + private static boolean hasLineBreaksBefore(ASTNode child, int minCount) { return isWhitespaceWithLineBreaks(TreeUtil.findLastLeaf(child.getTreePrev()), minCount) || isWhitespaceWithLineBreaks(child.getFirstChildNode(), minCount); @@ -263,8 +295,17 @@ public class PyBlock implements ASTBlock { IElementType type2 = childNode2.getElementType(); if (type1 == PyElementTypes.CLASS_DECLARATION) { + if ((type2 == PyElementTypes.CLASS_DECLARATION || type2 == PyElementTypes.FUNCTION_DECLARATION) && + parentType instanceof PyFileElementType) { + return getBlankLinesForOption(getPySettings().BLANK_LINES_BETWEEN_TOP_LEVEL_CLASSES_FUNCTIONS); + } return getBlankLinesForOption(mySettings.BLANK_LINES_AROUND_CLASS); } + if (type1 == PyElementTypes.FUNCTION_DECLARATION && + (type2 == PyElementTypes.CLASS_DECLARATION || type2 == PyElementTypes.FUNCTION_DECLARATION) && + parentType instanceof PyFileElementType) { + return getBlankLinesForOption(getPySettings().BLANK_LINES_BETWEEN_TOP_LEVEL_CLASSES_FUNCTIONS); + } if (type1 == PyElementTypes.FUNCTION_DECLARATION || (type2 == PyElementTypes.FUNCTION_DECLARATION && isStatementOrDeclaration(type1))) { return getBlankLinesForOption(mySettings.BLANK_LINES_AROUND_METHOD); @@ -386,7 +427,7 @@ public class PyBlock implements ASTBlock { return null; } - private boolean isImportStatement(IElementType type1) { + private static boolean isImportStatement(IElementType type1) { return (type1 == PyElementTypes.IMPORT_STATEMENT || type1 == PyElementTypes.FROM_IMPORT_STATEMENT); } @@ -486,7 +527,9 @@ public class PyBlock implements ASTBlock { */ - return new ChildAttributes(getChildIndent(newChildIndex), getChildAlignment()); + Indent childIndent = getChildIndent(newChildIndex); + Alignment childAlignment = getChildAlignment(); + return new ChildAttributes(childIndent, childAlignment); } private static boolean dedentAfterLastStatement(PyStatementList statementList) { @@ -498,25 +541,31 @@ public class PyBlock implements ASTBlock { return last instanceof PyReturnStatement || last instanceof PyRaiseStatement || last instanceof PyPassStatement; } + @Nullable private Alignment getChildAlignment() { if (ourListElementTypes.contains(_node.getElementType())) { + if (_node.getPsi() instanceof PyDictLiteralExpression) { + PyKeyValueExpression[] elements = ((PyDictLiteralExpression)_node.getPsi()).getElements(); + if (elements.length == 0) { + return null; + } + PyKeyValueExpression last = elements[elements.length-1]; + if (last.getValue() == null) { // incomplete + return null; + } + } return getAlignmentForChildren(); } return null; } private Indent getChildIndent(int newChildIndex) { + ASTNode afterNode = getAfterNode(newChildIndex); ASTNode lastChild = getLastNonSpaceChild(_node, false); if (lastChild != null && lastChild.getElementType() == PyElementTypes.STATEMENT_LIST && _subBlocks.size() >= newChildIndex) { - if (newChildIndex == 0) { // block text contains backslash line wrappings, child block list not built + if (afterNode == null) { return Indent.getNoneIndent(); } - int prevIndex = newChildIndex - 1; - while (prevIndex > 0 && _subBlocks.get(prevIndex).getNode().getElementType() == PyTokenTypes.END_OF_LINE_COMMENT) { - prevIndex--; - } - PyBlock insertAfterBlock = _subBlocks.get(prevIndex); - ASTNode afterNode = insertAfterBlock.getNode(); // handle pressing Enter after colon and before first statement in // existing statement list @@ -570,6 +619,13 @@ public class PyBlock implements ASTBlock { } } + if (afterNode != null && afterNode.getElementType() == PyElementTypes.KEY_VALUE_EXPRESSION) { + PyKeyValueExpression keyValue = (PyKeyValueExpression) afterNode.getPsi(); + if (keyValue != null && keyValue.getValue() == null) { // incomplete + return Indent.getContinuationIndent(true); + } + } + // constructs that imply indent for their children if (ourListElementTypes.contains(_node.getElementType()) || _node.getPsi() instanceof PyStatementPart) { return Indent.getNormalIndent(); @@ -589,6 +645,19 @@ public class PyBlock implements ASTBlock { */ } + @Nullable + private ASTNode getAfterNode(int newChildIndex) { + if (newChildIndex == 0) { // block text contains backslash line wrappings, child block list not built + return null; + } + int prevIndex = newChildIndex - 1; + while (prevIndex > 0 && _subBlocks.get(prevIndex).getNode().getElementType() == PyTokenTypes.END_OF_LINE_COMMENT) { + prevIndex--; + } + PyBlock insertAfterBlock = _subBlocks.get(prevIndex); + return insertAfterBlock.getNode(); + } + private static ASTNode getLastNonSpaceChild(ASTNode node, boolean acceptError) { ASTNode lastChild = node.getLastChildNode(); while (lastChild != null && diff --git a/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java b/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java index 22a67e7053b8..f7232ade3245 100644 --- a/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java +++ b/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java @@ -14,6 +14,8 @@ public class PyCodeStyleSettings extends CustomCodeStyleSettings { public boolean SPACE_AROUND_EQ_IN_NAMED_PARAMETER = false; public boolean SPACE_AROUND_EQ_IN_KEYWORD_ARGUMENT = false; + public int BLANK_LINES_BETWEEN_TOP_LEVEL_CLASSES_FUNCTIONS = 2; + public PyCodeStyleSettings(CodeStyleSettings container) { super("Python", container); } diff --git a/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java b/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java index 7474cb6197f8..472d6eb224b8 100644 --- a/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java +++ b/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java @@ -57,6 +57,8 @@ public class PyLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSettin "BLANK_LINES_AFTER_IMPORTS", "KEEP_BLANK_LINES_IN_DECLARATIONS", "KEEP_BLANK_LINES_IN_CODE"); + consumer.showCustomOption(PyCodeStyleSettings.class, "BLANK_LINES_BETWEEN_TOP_LEVEL_CLASSES_FUNCTIONS", "Between top-level classes and functions", + BLANK_LINES); } else if (settingsType == SettingsType.WRAPPING_AND_BRACES_SETTINGS) { consumer.showAllStandardOptions(); diff --git a/python/src/com/jetbrains/python/parsing/ExpressionParsing.java b/python/src/com/jetbrains/python/parsing/ExpressionParsing.java index 3c4073bba423..8047264147be 100644 --- a/python/src/com/jetbrains/python/parsing/ExpressionParsing.java +++ b/python/src/com/jetbrains/python/parsing/ExpressionParsing.java @@ -188,7 +188,10 @@ public class ExpressionParsing extends Parsing { private void parseDictLiteralTail(PsiBuilder.Marker startMarker, PsiBuilder.Marker firstKeyValueMarker) { if (!parseSingleExpression(false)) { myBuilder.error("expression expected"); - firstKeyValueMarker.drop(); + firstKeyValueMarker.done(PyElementTypes.KEY_VALUE_EXPRESSION); + if (atToken(PyTokenTypes.RBRACE)) { + myBuilder.advanceLexer(); + } startMarker.done(PyElementTypes.DICT_LITERAL_EXPRESSION); return; } @@ -308,10 +311,14 @@ public class ExpressionParsing extends Parsing { parseSliceEnd(expr, sliceItemStart); } else { - parseExpressionOptional(); - if (myBuilder.getTokenType() == PyTokenTypes.COLON) { + parseSingleExpression(false); + if (atToken(PyTokenTypes.COLON)) { parseSliceEnd(expr, sliceItemStart); } + else if (atToken(PyTokenTypes.COMMA)) { + sliceItemStart.done(PyElementTypes.SLICE_ITEM); + parseSliceListTail(expr); + } else { sliceItemStart.drop(); checkMatches(PyTokenTypes.RBRACKET, message("PARSE.expected.rbracket")); @@ -373,9 +380,13 @@ public class ExpressionParsing extends Parsing { } } + parseSliceListTail(exprStart); + } + + private void parseSliceListTail(PsiBuilder.Marker exprStart) { while (atToken(PyTokenTypes.COMMA)) { nextToken(); - sliceItemStart = myBuilder.mark(); + PsiBuilder.Marker sliceItemStart = myBuilder.mark(); parseTestExpression(false, false); if (matchToken(PyTokenTypes.COLON)) { parseTestExpression(false, false); diff --git a/python/testData/formatter/blankLineBetweenMethods.py b/python/testData/formatter/blankLineBetweenMethods.py index 2f18aaa72111..1c072f1a451e 100644 --- a/python/testData/formatter/blankLineBetweenMethods.py +++ b/python/testData/formatter/blankLineBetweenMethods.py @@ -1,6 +1,5 @@ class C: - pass -def foo(): - pass -def bar(): - pass \ No newline at end of file + def foo(self): + pass + def bar(self): + pass \ No newline at end of file diff --git a/python/testData/formatter/blankLineBetweenMethods_after.py b/python/testData/formatter/blankLineBetweenMethods_after.py index 89a633f6165e..a6b9280d7dbf 100644 --- a/python/testData/formatter/blankLineBetweenMethods_after.py +++ b/python/testData/formatter/blankLineBetweenMethods_after.py @@ -1,8 +1,6 @@ class C: - pass + def foo(self): + pass -def foo(): - pass - -def bar(): - pass \ No newline at end of file + def bar(self): + pass \ No newline at end of file diff --git a/python/testData/formatter/specialSlice.py b/python/testData/formatter/specialSlice.py new file mode 100644 index 000000000000..ba8e7fad0df1 --- /dev/null +++ b/python/testData/formatter/specialSlice.py @@ -0,0 +1 @@ +a[b1,:] \ No newline at end of file diff --git a/python/testData/formatter/specialSlice_after.py b/python/testData/formatter/specialSlice_after.py new file mode 100644 index 000000000000..98b951a3c1f9 --- /dev/null +++ b/python/testData/formatter/specialSlice_after.py @@ -0,0 +1 @@ +a[b1, :] \ No newline at end of file diff --git a/python/testData/formatter/twoLinesBetweenTopLevelClasses.py b/python/testData/formatter/twoLinesBetweenTopLevelClasses.py new file mode 100644 index 000000000000..c2e1ee19ffd3 --- /dev/null +++ b/python/testData/formatter/twoLinesBetweenTopLevelClasses.py @@ -0,0 +1,5 @@ +class C: + pass + +class D: + pass \ No newline at end of file diff --git a/python/testData/formatter/twoLinesBetweenTopLevelClasses_after.py b/python/testData/formatter/twoLinesBetweenTopLevelClasses_after.py new file mode 100644 index 000000000000..55ec52a5afaf --- /dev/null +++ b/python/testData/formatter/twoLinesBetweenTopLevelClasses_after.py @@ -0,0 +1,6 @@ +class C: + pass + + +class D: + pass \ No newline at end of file diff --git a/python/testData/formatter/twoLinesBetweenTopLevelFunctions.py b/python/testData/formatter/twoLinesBetweenTopLevelFunctions.py new file mode 100644 index 000000000000..3a6fd143c7fb --- /dev/null +++ b/python/testData/formatter/twoLinesBetweenTopLevelFunctions.py @@ -0,0 +1,11 @@ +class D: + pass + +def foo(): + pass + +def bar(): + pass + +class C: + pass diff --git a/python/testData/formatter/twoLinesBetweenTopLevelFunctions_after.py b/python/testData/formatter/twoLinesBetweenTopLevelFunctions_after.py new file mode 100644 index 000000000000..99227dec7391 --- /dev/null +++ b/python/testData/formatter/twoLinesBetweenTopLevelFunctions_after.py @@ -0,0 +1,14 @@ +class D: + pass + + +def foo(): + pass + + +def bar(): + pass + + +class C: + pass diff --git a/python/testData/inspections/AddClass_after.py b/python/testData/inspections/AddClass_after.py index 2e177c5b5b46..cecadfbd15f2 100644 --- a/python/testData/inspections/AddClass_after.py +++ b/python/testData/inspections/AddClass_after.py @@ -1,6 +1,7 @@ class Xyzzy(object): pass + class XyzzyTest: def testSimple(self): c = Xyzzy() diff --git a/python/testData/psi/IncompleteDict.py b/python/testData/psi/IncompleteDict.py new file mode 100644 index 000000000000..f047ba139c62 --- /dev/null +++ b/python/testData/psi/IncompleteDict.py @@ -0,0 +1 @@ +some_dict = { 'key': } \ No newline at end of file diff --git a/python/testData/psi/IncompleteDict.txt b/python/testData/psi/IncompleteDict.txt new file mode 100644 index 000000000000..e493cb0219c5 --- /dev/null +++ b/python/testData/psi/IncompleteDict.txt @@ -0,0 +1,18 @@ +PyFile:IncompleteDict.py + PyAssignmentStatement + PyTargetExpression: some_dict + PsiElement(Py:IDENTIFIER)('some_dict') + PsiWhiteSpace(' ') + PsiElement(Py:EQ)('=') + PsiWhiteSpace(' ') + PyDictLiteralExpression + PsiElement(Py:LBRACE)('{') + PsiWhiteSpace(' ') + PyKeyValueExpression + PyStringLiteralExpression: key + PsiElement(Py:STRING_LITERAL)(''key'') + PsiElement(Py:COLON)(':') + PsiErrorElement:expression expected + + PsiWhiteSpace(' ') + PsiElement(Py:RBRACE)('}') \ No newline at end of file diff --git a/python/testData/psi/SliceList.py b/python/testData/psi/SliceList.py new file mode 100644 index 000000000000..ba8e7fad0df1 --- /dev/null +++ b/python/testData/psi/SliceList.py @@ -0,0 +1 @@ +a[b1,:] \ No newline at end of file diff --git a/python/testData/psi/SliceList.txt b/python/testData/psi/SliceList.txt new file mode 100644 index 000000000000..c0c01a09a89c --- /dev/null +++ b/python/testData/psi/SliceList.txt @@ -0,0 +1,13 @@ +PyFile:SliceList.py + PyExpressionStatement + PySliceExpression + PyReferenceExpression: a + PsiElement(Py:IDENTIFIER)('a') + PsiElement(Py:LBRACKET)('[') + PySliceItem + PyReferenceExpression: b1 + PsiElement(Py:IDENTIFIER)('b1') + PsiElement(Py:COMMA)(',') + PySliceItem + PsiElement(Py:COLON)(':') + PsiElement(Py:RBRACKET)(']') \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/comment.after.py b/python/testData/refactoring/extractmethod/comment.after.py index 534163362db3..f4760c22c752 100644 --- a/python/testData/refactoring/extractmethod/comment.after.py +++ b/python/testData/refactoring/extractmethod/comment.after.py @@ -2,5 +2,6 @@ def bar(): print("Hello") + def foo(): bar() diff --git a/python/testData/refactoring/extractmethod/file.after.py b/python/testData/refactoring/extractmethod/file.after.py index 74d8ca43a208..5a313ba35fef 100644 --- a/python/testData/refactoring/extractmethod/file.after.py +++ b/python/testData/refactoring/extractmethod/file.after.py @@ -2,6 +2,7 @@ def bar(base_new, self_new): try: base_new.__init__(self_new) except AttributeError: pass + def __init__(self): for base in self__class__.__bases__: bar(base, self) diff --git a/python/testData/refactoring/extractmethod/return_tuple.after.py b/python/testData/refactoring/extractmethod/return_tuple.after.py index dc924a7a7074..f4ff8f774e74 100644 --- a/python/testData/refactoring/extractmethod/return_tuple.after.py +++ b/python/testData/refactoring/extractmethod/return_tuple.after.py @@ -1,5 +1,6 @@ def bar(p_name_new, params_new): return p_name_new + '(' + ', '.join(params_new) + ')' + def x(p_name, params): return bar(p_name, params), None \ No newline at end of file diff --git a/python/testData/refactoring/extractsuperclass/simple.after.py b/python/testData/refactoring/extractsuperclass/simple.after.py index 937128f2bd30..38248501dc4a 100644 --- a/python/testData/refactoring/extractsuperclass/simple.after.py +++ b/python/testData/refactoring/extractsuperclass/simple.after.py @@ -2,6 +2,7 @@ class Suppa: def foo(self): print "bar" + class Foo(Suppa): def bar(self): print "foo" diff --git a/python/testData/refactoring/extractsuperclass/withSuper.after.py b/python/testData/refactoring/extractsuperclass/withSuper.after.py index 76488d3f4b0f..5f346b590cad 100644 --- a/python/testData/refactoring/extractsuperclass/withSuper.after.py +++ b/python/testData/refactoring/extractsuperclass/withSuper.after.py @@ -2,6 +2,7 @@ class Suppa(object): def foo(self): print "bar" + class Foo(Suppa): def bar(self): print "foo" diff --git a/python/testData/refactoring/pullup/simple.after.py b/python/testData/refactoring/pullup/simple.after.py index 20c544899de8..d8c6cb7a0c2e 100644 --- a/python/testData/refactoring/pullup/simple.after.py +++ b/python/testData/refactoring/pullup/simple.after.py @@ -5,5 +5,6 @@ class Foo: def boo(self): print "rrrrr" + class Boo(Foo): pass \ No newline at end of file diff --git a/python/testData/refactoring/pullup/withComments.after.py b/python/testData/refactoring/pullup/withComments.after.py index 5ddd8d4241f3..ce91117e5413 100644 --- a/python/testData/refactoring/pullup/withComments.after.py +++ b/python/testData/refactoring/pullup/withComments.after.py @@ -6,5 +6,6 @@ class Foo: def boo(self): print "rrrrr" + class Boo(Foo): pass \ No newline at end of file diff --git a/python/testData/refactoring/pullup/withMultilineComments.after.py b/python/testData/refactoring/pullup/withMultilineComments.after.py index 3780dec0eb33..b7c0179871d9 100644 --- a/python/testData/refactoring/pullup/withMultilineComments.after.py +++ b/python/testData/refactoring/pullup/withMultilineComments.after.py @@ -5,6 +5,7 @@ class Foo: def boo(self): print "rrrrr" + class Boo(Foo): ''' this is boo diff --git a/python/testData/refactoring/pushdown/multiple.after.py b/python/testData/refactoring/pushdown/multiple.after.py index 1cdbbe7d84f7..fc67b30f16de 100644 --- a/python/testData/refactoring/pushdown/multiple.after.py +++ b/python/testData/refactoring/pushdown/multiple.after.py @@ -1,6 +1,7 @@ class Foo: pass + class Zope(Foo): def _mine(self): print "zope" @@ -8,6 +9,7 @@ class Zope(Foo): def foo(self): print("a") + class Boo(Foo): def boo(self): print "rrrrr" diff --git a/python/testData/refactoring/pushdown/py346.after.py b/python/testData/refactoring/pushdown/py346.after.py index b60d169e4c9e..278ab4607823 100644 --- a/python/testData/refactoring/pushdown/py346.after.py +++ b/python/testData/refactoring/pushdown/py346.after.py @@ -1,6 +1,7 @@ class A: pass + class B(A): def meth_b1(self): pass @@ -13,6 +14,7 @@ class B(A): def meth_a2(self): pass + class D(A): def meth_d1(self): pass diff --git a/python/testData/refactoring/pushdown/simple.after.py b/python/testData/refactoring/pushdown/simple.after.py index 926f8cf64c19..3d36fa53d6a6 100644 --- a/python/testData/refactoring/pushdown/simple.after.py +++ b/python/testData/refactoring/pushdown/simple.after.py @@ -1,6 +1,7 @@ class Foo: pass + class Boo(Foo): def boo(self): print "rrrrr" diff --git a/python/testSrc/com/jetbrains/python/PyFormatterTest.java b/python/testSrc/com/jetbrains/python/PyFormatterTest.java index 8ed532b07ff1..ecb58ad554ae 100644 --- a/python/testSrc/com/jetbrains/python/PyFormatterTest.java +++ b/python/testSrc/com/jetbrains/python/PyFormatterTest.java @@ -92,6 +92,18 @@ public class PyFormatterTest extends PyLightFixtureTestCase { doTest(); } + public void testTwoLinesBetweenTopLevelClasses() { // PY-2765 + doTest(); + } + + public void testTwoLinesBetweenTopLevelFunctions() { // PY-2765 + doTest(); + } + + public void testSpecialSlice() { // PY-1928 + doTest(); + } + private void doTest() { myFixture.configureByFile("formatter/" + getTestName(true) + ".py"); ApplicationManager.getApplication().runWriteAction(new Runnable() { diff --git a/python/testSrc/com/jetbrains/python/PyIndentTest.java b/python/testSrc/com/jetbrains/python/PyIndentTest.java index 31a195891490..5cd39c0783d6 100644 --- a/python/testSrc/com/jetbrains/python/PyIndentTest.java +++ b/python/testSrc/com/jetbrains/python/PyIndentTest.java @@ -71,12 +71,49 @@ public class PyIndentTest extends PyLightFixtureTestCase { doTest("__all__ = [a for", "__all__ = [a for\n" + " "); } + public void testAlignInListOnceMore() { // PY-2407 + doTest("for id in [\"SEARCH_RESULT_ATTRIBUTES\", \n" + + " \"WRITE_SEARCH_RESULT_ATTRIBUTES\", \n" + + " \"IDENTIFIER_UNDER_CARET_ATTRIBUTES\",]:", + "for id in [\"SEARCH_RESULT_ATTRIBUTES\", \n" + + " \"WRITE_SEARCH_RESULT_ATTRIBUTES\", \n" + + " \"IDENTIFIER_UNDER_CARET_ATTRIBUTES\",\n" + + " ]:"); + } + public void testAlignInDict() { doTest("some_call({'aaa': 'v1',})", "some_call({'aaa': 'v1',\n" + " })"); } + public void testAlignInDictInParams() { // PY-1947 + doTest("foobar({})", + "foobar({\n" + + " \n" + + "})"); + } + + public void testIndentDictMissingValue() { // PY-1469 + doTest("some_dict = {\n" + + " 'key': \n" + + "}", + "some_dict = {\n" + + " 'key': \n" + + " \n" + + "}"); + } + + public void testIndentDictStringValue() { // PY-1469 + doTest("some_dict = {\n" + + " 'key': ''\n" + + "}", + "some_dict = {\n" + + " 'key': \n" + + " ''\n" + + "}"); + } + public void testClass() { doTest("class A:\n" + " print a", "class A:\n" + " print a\n" + " "); } @@ -128,7 +165,7 @@ public class PyIndentTest extends PyLightFixtureTestCase { } public void testEnterInEmptyList() { - doTest("[]", "[\n]"); + doTest("[]", "[\n \n]"); } public void testEnterInEmptyDict() { @@ -149,6 +186,12 @@ public class PyIndentTest extends PyLightFixtureTestCase { ")"); } + public void testEnterInNonEmptyArgList() { // PY-1947 + doTest("Task(params=1)", + "Task(\n" + + " params=1)"); + } + public void testIndentAfterComment() { // PY-641 doTest("def foo():\n #some_call()\n another_call()", "def foo():\n #some_call()\n \n another_call()"); } diff --git a/python/testSrc/com/jetbrains/python/PythonParsingTest.java b/python/testSrc/com/jetbrains/python/PythonParsingTest.java index b3d8485f9995..3cd473572f7e 100644 --- a/python/testSrc/com/jetbrains/python/PythonParsingTest.java +++ b/python/testSrc/com/jetbrains/python/PythonParsingTest.java @@ -239,6 +239,14 @@ public class PythonParsingTest extends ParsingTestCase { doTest(); } + public void testIncompleteDict() { + doTest(); + } + + public void testSliceList() { // PY-1928 + doTest(); + } + public void doTest() { doTest(LanguageLevel.PYTHON25); }