From 30c586b5a1f5ab6fdd46382115b8f9ac85eb7a68 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Thu, 31 Aug 2017 20:15:24 +0300 Subject: [PATCH] PY-19705 Add an option for blanks between class header and its first method On the whole, we don't touch these blank lines (as opposed to deleting them previously) unless they exceed the limit specified in settings (Keep maximum blank lines in declarations). If someone wants to always put a blank line before the first method, as in PY-1765, they can use this new option. --- python/src/com/jetbrains/python/PyBundle.properties | 1 + python/src/com/jetbrains/python/formatter/PyBlock.java | 8 ++++---- .../jetbrains/python/formatter/PyCodeStyleSettings.java | 1 + .../formatter/PyLanguageCodeStyleSettingsProvider.java | 2 ++ python/testData/create_tests/create_tst.expected.py | 1 - ...fClosingBraceInDictLiteralWhenNoHangingIndent_after.py | 1 - python/testData/formatter/beforeTopLevelClass_after.py | 1 - python/testData/formatter/blankLineAroundClasses_after.py | 1 - .../testData/formatter/blankLineBetweenMethods_after.py | 1 - python/testData/formatter/commentBetweenClasses_after.py | 2 -- .../testData/inspections/TransformClassicClass_after.py | 1 - .../PyAddMethodQuickFixTest/addMethodReplacePass_after.py | 1 - .../refactoring/extractsuperclass/fieldsNpe.after.py | 1 - .../importMultiFile/dest_module.after.py | 1 - .../extractsuperclass/instanceNotDeclaredInInit.after.py | 1 - .../dest_module.after.py | 1 - .../extractsuperclass/multifile/target.append.py | 1 - .../refactoring/extractsuperclass/multifile/target.new.py | 1 - .../extractsuperclass/py3ParentHasObject.after.py | 1 - .../refactoring/extractsuperclass/simple.after.py | 1 - .../refactoring/extractsuperclass/withSuper.after.py | 1 - python/testSrc/com/jetbrains/python/PyFormatterTest.java | 1 + 22 files changed, 9 insertions(+), 22 deletions(-) diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index 513c8385fc63..d01d3c94af16 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -1031,6 +1031,7 @@ formatter.before.hash = Before '#' formatter.after.hash = After '#' formatter.around.top.level.imports = After top-level imports: formatter.after.local.imports=After local imports: +formatter.before.first.method=Before the first method: formatter.around.top.level.classes.and.function=Around top-level classes and functions: formatter.around.multiplicative.operators=Multiplicative operators (*, @, /, %) formatter.around.power.operator=Power operator (**) diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index 8e70a14dfe21..a9b92f374717 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -737,7 +737,7 @@ public class PyBlock implements ASTBlock { 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); + return getBlankLinesForOption(pySettings.BLANK_LINES_BEFORE_FIRST_METHOD); } } if (childType1 == PyTokenTypes.COLON && needLineBreakInStatement()) { @@ -816,9 +816,9 @@ public class PyBlock implements ASTBlock { } @NotNull - private Spacing getBlankLinesForOption(int option) { - final int blankLines = option + 1; - return Spacing.createSpacing(0, 0, blankLines, + private Spacing getBlankLinesForOption(int minBlankLines) { + final int lineFeeds = minBlankLines + 1; + return Spacing.createSpacing(0, 0, lineFeeds, myContext.getSettings().KEEP_LINE_BREAKS, myContext.getSettings().KEEP_BLANK_LINES_IN_DECLARATIONS); } diff --git a/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java b/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java index a8d81a9de18d..d844e5f448c7 100644 --- a/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java +++ b/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java @@ -63,6 +63,7 @@ public class PyCodeStyleSettings extends CustomCodeStyleSettings { public boolean SPACE_BEFORE_BACKSLASH = true; public int BLANK_LINES_AROUND_TOP_LEVEL_CLASSES_FUNCTIONS = 2; + public int BLANK_LINES_BEFORE_FIRST_METHOD = 0; // TODO make boolean (it needs special editor in BlankLinesPanel) public boolean BLANK_LINE_AT_FILE_END = true; diff --git a/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java b/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java index 3f493e310055..ff006204ca62 100644 --- a/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java +++ b/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java @@ -102,6 +102,8 @@ public class PyLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSettin PyBundle.message("formatter.around.top.level.classes.and.function"), BLANK_LINES); consumer.showCustomOption(PyCodeStyleSettings.class, "BLANK_LINES_AFTER_LOCAL_IMPORTS", PyBundle.message("formatter.after.local.imports"), BLANK_LINES); + consumer.showCustomOption(PyCodeStyleSettings.class, "BLANK_LINES_BEFORE_FIRST_METHOD", + PyBundle.message("formatter.before.first.method"), BLANK_LINES); } else if (settingsType == SettingsType.WRAPPING_AND_BRACES_SETTINGS) { consumer.showStandardOptions("RIGHT_MARGIN", diff --git a/python/testData/create_tests/create_tst.expected.py b/python/testData/create_tests/create_tst.expected.py index 172a9a3c6ffb..b09ba847eef6 100644 --- a/python/testData/create_tests/create_tst.expected.py +++ b/python/testData/create_tests/create_tst.expected.py @@ -2,7 +2,6 @@ from unittest import TestCase class Spam(TestCase): - def eggs(self): self.fail() diff --git a/python/testData/formatter/alignmentOfClosingBraceInDictLiteralWhenNoHangingIndent_after.py b/python/testData/formatter/alignmentOfClosingBraceInDictLiteralWhenNoHangingIndent_after.py index 5c7cd915fe1d..044070106f87 100644 --- a/python/testData/formatter/alignmentOfClosingBraceInDictLiteralWhenNoHangingIndent_after.py +++ b/python/testData/formatter/alignmentOfClosingBraceInDictLiteralWhenNoHangingIndent_after.py @@ -1,5 +1,4 @@ class Checkpoints(webapp2.RequestHandler): - def get(self): self.response.write(json.dumps({"meta": {"code": 400, "errorType": "paramError", diff --git a/python/testData/formatter/beforeTopLevelClass_after.py b/python/testData/formatter/beforeTopLevelClass_after.py index 299757e2e9ba..6b331a7cd1b5 100644 --- a/python/testData/formatter/beforeTopLevelClass_after.py +++ b/python/testData/formatter/beforeTopLevelClass_after.py @@ -2,6 +2,5 @@ from unittest import TestCase class MyTest(TestCase): - def test_pass(self): self.assertEqual(1 + 1, 2) diff --git a/python/testData/formatter/blankLineAroundClasses_after.py b/python/testData/formatter/blankLineAroundClasses_after.py index b00290ebd5d5..d7f3fc0ca04a 100644 --- a/python/testData/formatter/blankLineAroundClasses_after.py +++ b/python/testData/formatter/blankLineAroundClasses_after.py @@ -1,5 +1,4 @@ class Adjunct: - def apply(self, right, arg): pass diff --git a/python/testData/formatter/blankLineBetweenMethods_after.py b/python/testData/formatter/blankLineBetweenMethods_after.py index 9cfa2e27b90d..54cae234d6c3 100644 --- a/python/testData/formatter/blankLineBetweenMethods_after.py +++ b/python/testData/formatter/blankLineBetweenMethods_after.py @@ -1,5 +1,4 @@ class C: - def foo(self): pass diff --git a/python/testData/formatter/commentBetweenClasses_after.py b/python/testData/formatter/commentBetweenClasses_after.py index 9a82642ffadc..e97c76b04590 100644 --- a/python/testData/formatter/commentBetweenClasses_after.py +++ b/python/testData/formatter/commentBetweenClasses_after.py @@ -1,5 +1,4 @@ class T1(object): - def m1(self): pass @@ -7,6 +6,5 @@ class T1(object): # comment about T2 class T2(object): - def m2(self): pass diff --git a/python/testData/inspections/TransformClassicClass_after.py b/python/testData/inspections/TransformClassicClass_after.py index 19a4bb306c8b..70402ca7f12c 100644 --- a/python/testData/inspections/TransformClassicClass_after.py +++ b/python/testData/inspections/TransformClassicClass_after.py @@ -1,5 +1,4 @@ class A(object): - def foo(self): pass diff --git a/python/testData/quickFixes/PyAddMethodQuickFixTest/addMethodReplacePass_after.py b/python/testData/quickFixes/PyAddMethodQuickFixTest/addMethodReplacePass_after.py index 595eb86ded71..3082267d4d02 100644 --- a/python/testData/quickFixes/PyAddMethodQuickFixTest/addMethodReplacePass_after.py +++ b/python/testData/quickFixes/PyAddMethodQuickFixTest/addMethodReplacePass_after.py @@ -1,5 +1,4 @@ class A: - def y(self): pass diff --git a/python/testData/refactoring/extractsuperclass/fieldsNpe.after.py b/python/testData/refactoring/extractsuperclass/fieldsNpe.after.py index 9a08604d360b..5944e81341bc 100644 --- a/python/testData/refactoring/extractsuperclass/fieldsNpe.after.py +++ b/python/testData/refactoring/extractsuperclass/fieldsNpe.after.py @@ -1,5 +1,4 @@ class Ancestor(object): - def __init__(self, a, b): self.a = a self.b = b diff --git a/python/testData/refactoring/extractsuperclass/importMultiFile/dest_module.after.py b/python/testData/refactoring/extractsuperclass/importMultiFile/dest_module.after.py index b717c28ecdcf..eba5f32d8456 100644 --- a/python/testData/refactoring/extractsuperclass/importMultiFile/dest_module.after.py +++ b/python/testData/refactoring/extractsuperclass/importMultiFile/dest_module.after.py @@ -3,7 +3,6 @@ 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() diff --git a/python/testData/refactoring/extractsuperclass/instanceNotDeclaredInInit.after.py b/python/testData/refactoring/extractsuperclass/instanceNotDeclaredInInit.after.py index 25fd7a3d8c68..ef3511dc8c66 100644 --- a/python/testData/refactoring/extractsuperclass/instanceNotDeclaredInInit.after.py +++ b/python/testData/refactoring/extractsuperclass/instanceNotDeclaredInInit.after.py @@ -1,5 +1,4 @@ class Parent(object): - def __init__(self): self.eggs = 12 diff --git a/python/testData/refactoring/extractsuperclass/moveAndMakeAbstractImportExistsPy3/dest_module.after.py b/python/testData/refactoring/extractsuperclass/moveAndMakeAbstractImportExistsPy3/dest_module.after.py index 946e299b39e6..ddce4a86100f 100644 --- a/python/testData/refactoring/extractsuperclass/moveAndMakeAbstractImportExistsPy3/dest_module.after.py +++ b/python/testData/refactoring/extractsuperclass/moveAndMakeAbstractImportExistsPy3/dest_module.after.py @@ -7,7 +7,6 @@ abstractmethod() class NewParent(metaclass=ABCMeta): - @classmethod @abstractmethod def foo_method(cls): diff --git a/python/testData/refactoring/extractsuperclass/multifile/target.append.py b/python/testData/refactoring/extractsuperclass/multifile/target.append.py index 681607f25d50..81722c98acac 100644 --- a/python/testData/refactoring/extractsuperclass/multifile/target.append.py +++ b/python/testData/refactoring/extractsuperclass/multifile/target.append.py @@ -3,6 +3,5 @@ A = 1 class Suppa: - def foo(self): print "bar" diff --git a/python/testData/refactoring/extractsuperclass/multifile/target.new.py b/python/testData/refactoring/extractsuperclass/multifile/target.new.py index 20c10a93ab7b..db6d3b7efa91 100644 --- a/python/testData/refactoring/extractsuperclass/multifile/target.new.py +++ b/python/testData/refactoring/extractsuperclass/multifile/target.new.py @@ -1,4 +1,3 @@ class Suppa: - def foo(self): print "bar" diff --git a/python/testData/refactoring/extractsuperclass/py3ParentHasObject.after.py b/python/testData/refactoring/extractsuperclass/py3ParentHasObject.after.py index 23b744155397..dd03d9a179c9 100644 --- a/python/testData/refactoring/extractsuperclass/py3ParentHasObject.after.py +++ b/python/testData/refactoring/extractsuperclass/py3ParentHasObject.after.py @@ -1,5 +1,4 @@ class Parent(object): - def spam(self): pass diff --git a/python/testData/refactoring/extractsuperclass/simple.after.py b/python/testData/refactoring/extractsuperclass/simple.after.py index 86b15163bcae..38248501dc4a 100644 --- a/python/testData/refactoring/extractsuperclass/simple.after.py +++ b/python/testData/refactoring/extractsuperclass/simple.after.py @@ -1,5 +1,4 @@ class Suppa: - def foo(self): print "bar" diff --git a/python/testData/refactoring/extractsuperclass/withSuper.after.py b/python/testData/refactoring/extractsuperclass/withSuper.after.py index 5831a7ab776a..5f346b590cad 100644 --- a/python/testData/refactoring/extractsuperclass/withSuper.after.py +++ b/python/testData/refactoring/extractsuperclass/withSuper.after.py @@ -1,5 +1,4 @@ class Suppa(object): - def foo(self): print "bar" diff --git a/python/testSrc/com/jetbrains/python/PyFormatterTest.java b/python/testSrc/com/jetbrains/python/PyFormatterTest.java index da4c9a612775..33bb36d3d69a 100644 --- a/python/testSrc/com/jetbrains/python/PyFormatterTest.java +++ b/python/testSrc/com/jetbrains/python/PyFormatterTest.java @@ -837,6 +837,7 @@ public class PyFormatterTest extends PyTestCase { // PY-19705 public void testBlankLinesAroundFirstMethod() { + getPythonCodeStyleSettings().BLANK_LINES_BEFORE_FIRST_METHOD = 1; doTest(); }