mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 21:11:28 +07:00
PY-19705 Add blanks lines around methods of a class as required by PEP 8
This commit is contained in:
@@ -719,6 +719,8 @@ public class PyBlock implements ASTBlock {
|
||||
@Override
|
||||
@Nullable
|
||||
public Spacing getSpacing(@Nullable Block child1, @NotNull Block child2) {
|
||||
final CommonCodeStyleSettings settings = myContext.getSettings();
|
||||
final PyCodeStyleSettings pySettings = myContext.getPySettings();
|
||||
if (child1 instanceof ASTBlock && child2 instanceof ASTBlock) {
|
||||
final ASTNode node1 = ((ASTBlock)child1).getNode();
|
||||
ASTNode node2 = ((ASTBlock)child2).getNode();
|
||||
@@ -727,6 +729,22 @@ public class PyBlock implements ASTBlock {
|
||||
|
||||
PsiElement psi2 = node2.getPsi();
|
||||
|
||||
if (psi2 instanceof PyStatementList) {
|
||||
// Quite odd getSpacing() doesn't get called with child1=null for the first statement
|
||||
// in the statement list of a class, yet it does get called for the preceding colon and
|
||||
// the statement list itself. Hence we have to handle blank lines around methods here in
|
||||
// addition to SpacingBuilder.
|
||||
if (myNode.getElementType() == PyElementTypes.CLASS_DECLARATION) {
|
||||
final PyStatement[] statements = ((PyStatementList)psi2).getStatements();
|
||||
if (statements.length > 0 && statements[0] instanceof PyFunction) {
|
||||
return getBlankLinesForOption(settings.BLANK_LINES_AROUND_METHOD);
|
||||
}
|
||||
}
|
||||
if (childType1 == PyTokenTypes.COLON && needLineBreakInStatement()) {
|
||||
return Spacing.createSpacing(0, 0, 1, true, settings.KEEP_BLANK_LINES_IN_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
// pycodestyle.py enforces at most 2 blank lines only between comments directly
|
||||
// at the top-level of a file, not inside if, try/except, etc.
|
||||
if (psi1 instanceof PsiComment && myNode.getPsi() instanceof PsiFile) {
|
||||
@@ -744,8 +762,6 @@ public class PyBlock implements ASTBlock {
|
||||
final IElementType childType2 = psi2.getNode().getElementType();
|
||||
//noinspection ConstantConditions
|
||||
child2 = getSubBlockByNode(node2);
|
||||
final CommonCodeStyleSettings settings = myContext.getSettings();
|
||||
final PyCodeStyleSettings pySettings = myContext.getPySettings();
|
||||
|
||||
if ((childType1 == PyTokenTypes.EQ || childType2 == PyTokenTypes.EQ)) {
|
||||
final PyNamedParameter namedParameter = as(myNode.getPsi(), PyNamedParameter.class);
|
||||
@@ -754,19 +770,13 @@ public class PyBlock implements ASTBlock {
|
||||
}
|
||||
}
|
||||
|
||||
if (childType1 == PyTokenTypes.COLON && psi2 instanceof PyStatementList) {
|
||||
if (needLineBreakInStatement()) {
|
||||
return Spacing.createSpacing(0, 0, 1, true, settings.KEEP_BLANK_LINES_IN_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
if (psi1 instanceof PyImportStatementBase) {
|
||||
if (psi2 instanceof PyImportStatementBase) {
|
||||
final Boolean leftImportIsGroupStart = psi1.getCopyableUserData(IMPORT_GROUP_BEGIN);
|
||||
final Boolean rightImportIsGroupStart = psi2.getCopyableUserData(IMPORT_GROUP_BEGIN);
|
||||
// Cleanup user data, it's no longer needed
|
||||
psi1.putCopyableUserData(IMPORT_GROUP_BEGIN, null);
|
||||
// Don't remove IMPORT_GROUP_BEGIN from the element psi2 yet, because spacing is constructed pairwise:
|
||||
// Don't remove IMPORT_GROUP_BEGIN from the element psi2 yet, because spacing is constructed pairwise:
|
||||
// it might be needed on the next iteration.
|
||||
//psi2.putCopyableUserData(IMPORT_GROUP_BEGIN, null);
|
||||
if (rightImportIsGroupStart != null) {
|
||||
@@ -962,7 +972,7 @@ public class PyBlock implements ASTBlock {
|
||||
}
|
||||
}
|
||||
else if (lastChild != null && PyElementTypes.LIST_LIKE_EXPRESSIONS.contains(lastChild.getElementType())) {
|
||||
// handle pressing enter at the end of a list literal when there's no closing paren or bracket
|
||||
// handle pressing enter at the end of a list literal when there's no closing paren or bracket
|
||||
final ASTNode lastLastChild = lastChild.getLastChildNode();
|
||||
if (lastLastChild != null && lastLastChild.getPsi() instanceof PsiErrorElement) {
|
||||
// we're at a place like this: [foo, ... bar, <caret>
|
||||
|
||||
@@ -79,13 +79,10 @@ public class PythonFormattingModelBuilder implements FormattingModelBuilderEx, C
|
||||
return new SpacingBuilder(commonSettings)
|
||||
.before(END_OF_LINE_COMMENT).spacing(2, 0, 0, commonSettings.KEEP_LINE_BREAKS, commonSettings.KEEP_BLANK_LINES_IN_CODE)
|
||||
.after(END_OF_LINE_COMMENT).spacing(0, 0, 1, commonSettings.KEEP_LINE_BREAKS, commonSettings.KEEP_BLANK_LINES_IN_CODE)
|
||||
.between(CLASS_DECLARATION, STATEMENT_OR_DECLARATION).blankLines(commonSettings.BLANK_LINES_AROUND_CLASS)
|
||||
.between(STATEMENT_OR_DECLARATION, CLASS_DECLARATION).blankLines(commonSettings.BLANK_LINES_AROUND_CLASS)
|
||||
.between(FUNCTION_DECLARATION, STATEMENT_OR_DECLARATION).blankLines(commonSettings.BLANK_LINES_AROUND_METHOD)
|
||||
.between(STATEMENT_OR_DECLARATION, FUNCTION_DECLARATION).blankLines(commonSettings.BLANK_LINES_AROUND_METHOD)
|
||||
.after(FUNCTION_DECLARATION).blankLines(commonSettings.BLANK_LINES_AROUND_METHOD)
|
||||
.after(CLASS_DECLARATION).blankLines(commonSettings.BLANK_LINES_AROUND_CLASS)
|
||||
// Remove excess blank lines between imports (at most one is allowed).
|
||||
// Top-level definitions are supposed to be handled in PyBlock#getSpacing
|
||||
.around(CLASS_DECLARATION).blankLines(commonSettings.BLANK_LINES_AROUND_CLASS)
|
||||
.around(FUNCTION_DECLARATION).blankLines(commonSettings.BLANK_LINES_AROUND_METHOD)
|
||||
// Remove excess blank lines between imports (at most one is allowed).
|
||||
// Note that ImportOptimizer gets rid of them anyway.
|
||||
// Empty lines between import groups are handles in PyBlock#getSpacing
|
||||
.between(IMPORT_STATEMENTS, IMPORT_STATEMENTS).spacing(0, Integer.MAX_VALUE, 1, false, 1)
|
||||
|
||||
@@ -2,6 +2,7 @@ from unittest import TestCase
|
||||
|
||||
|
||||
class Spam(TestCase):
|
||||
|
||||
def eggs(self):
|
||||
self.fail()
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class Checkpoints(webapp2.RequestHandler):
|
||||
|
||||
def get(self):
|
||||
self.response.write(json.dumps({"meta": {"code": 400,
|
||||
"errorType": "paramError",
|
||||
|
||||
@@ -2,5 +2,6 @@ from unittest import TestCase
|
||||
|
||||
|
||||
class MyTest(TestCase):
|
||||
|
||||
def test_pass(self):
|
||||
self.assertEqual(1 + 1, 2)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class Adjunct:
|
||||
|
||||
def apply(self, right, arg):
|
||||
pass
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class C:
|
||||
|
||||
def foo(self):
|
||||
pass
|
||||
|
||||
|
||||
65
python/testData/formatter/blankLinesAroundFirstMethod.py
Normal file
65
python/testData/formatter/blankLinesAroundFirstMethod.py
Normal file
@@ -0,0 +1,65 @@
|
||||
class C1:
|
||||
# comment 1
|
||||
|
||||
# comment 2
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class C2:
|
||||
# comment 2
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class C3:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class C4:
|
||||
"""Docstring."""
|
||||
# comment 1
|
||||
|
||||
# comment 2
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class C5:
|
||||
"""Docstring."""
|
||||
# comment 2
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class C6:
|
||||
"""Docstring."""
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class C7:
|
||||
attr = 42
|
||||
# comment 1
|
||||
|
||||
# comment 2
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class C8:
|
||||
attr = 42
|
||||
# comment 2
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class C9:
|
||||
attr = 42
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
class C10: # comment before statement list
|
||||
def __init__(self):
|
||||
pass
|
||||
@@ -0,0 +1,76 @@
|
||||
class C1:
|
||||
|
||||
# comment 1
|
||||
|
||||
# comment 2
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class C2:
|
||||
|
||||
# comment 2
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class C3:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class C4:
|
||||
"""Docstring."""
|
||||
|
||||
# comment 1
|
||||
|
||||
# comment 2
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class C5:
|
||||
"""Docstring."""
|
||||
|
||||
# comment 2
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class C6:
|
||||
"""Docstring."""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class C7:
|
||||
attr = 42
|
||||
|
||||
# comment 1
|
||||
|
||||
# comment 2
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class C8:
|
||||
attr = 42
|
||||
|
||||
# comment 2
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class C9:
|
||||
attr = 42
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class C10: # comment before statement list
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
@@ -1,4 +1,5 @@
|
||||
class T1(object):
|
||||
|
||||
def m1(self):
|
||||
pass
|
||||
|
||||
@@ -6,5 +7,6 @@ class T1(object):
|
||||
# comment about T2
|
||||
|
||||
class T2(object):
|
||||
|
||||
def m2(self):
|
||||
pass
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class Foo(object):
|
||||
|
||||
def wrong_blank_lines(self):
|
||||
pass
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class A(object):
|
||||
|
||||
def foo(self):
|
||||
pass
|
||||
|
||||
|
||||
@@ -2,5 +2,6 @@ class Dialog:
|
||||
def validate(self): pass
|
||||
|
||||
class B(Dialog):
|
||||
|
||||
def validate(self):
|
||||
<selection>Dialog.validate(self)</selection>
|
||||
@@ -1,4 +1,5 @@
|
||||
class A:
|
||||
|
||||
def y(self):
|
||||
pass
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ def f(x):
|
||||
|
||||
|
||||
class Child(Base):
|
||||
|
||||
def __init__(self):
|
||||
super(Child, self).__init__()
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class Ancestor(object):
|
||||
|
||||
def __init__(self, a, b):
|
||||
self.a = a
|
||||
self.b = b
|
||||
@@ -8,5 +9,6 @@ class Ancestor(object):
|
||||
|
||||
|
||||
class Basic(Ancestor):
|
||||
|
||||
def func2(self):
|
||||
return self.func1()
|
||||
@@ -3,6 +3,7 @@ from shared_module import module_function as my_function, ModuleClass
|
||||
|
||||
|
||||
class NewParent(object):
|
||||
|
||||
def do_useful_stuff(self):
|
||||
i = shared_module.MODULE_CONTANT
|
||||
my_function()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class Parent(object):
|
||||
|
||||
def __init__(self):
|
||||
self.eggs = 12
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ abstractmethod()
|
||||
|
||||
|
||||
class NewParent(metaclass=ABCMeta):
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def foo_method(cls):
|
||||
|
||||
@@ -3,5 +3,6 @@ A = 1
|
||||
|
||||
|
||||
class Suppa:
|
||||
|
||||
def foo(self):
|
||||
print "bar"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
class Suppa:
|
||||
|
||||
def foo(self):
|
||||
print "bar"
|
||||
|
||||
@@ -20,6 +20,7 @@ class ToClass(object):
|
||||
|
||||
|
||||
class FromClass(ToClass):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class Parent(object):
|
||||
|
||||
def spam(self):
|
||||
pass
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class Suppa:
|
||||
|
||||
def foo(self):
|
||||
print "bar"
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class Suppa(object):
|
||||
|
||||
def foo(self):
|
||||
print "bar"
|
||||
|
||||
|
||||
@@ -2,5 +2,6 @@ from SuperClass import SuperClass
|
||||
|
||||
|
||||
class AnyClass(SuperClass):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
@@ -835,6 +835,11 @@ public class PyFormatterTest extends PyTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-19705
|
||||
public void testBlankLinesAroundFirstMethod() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testVariableAnnotations() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON36, this::doTest);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user