PY-25356 Remove excess blank lines between line comments

The behavior of the formatter follows the way pycodestyle.py
warns about these extra empty lines. Namely, it allows at most 2 blank
lines after line comments that are directly at the top-level of a file
(syntactically, not scope-wise), but only one blank line in any other
position.
This commit is contained in:
Mikhail Golubev
2017-07-28 15:23:31 +03:00
parent 67ea7e9c03
commit e8d4286a58
6 changed files with 94 additions and 14 deletions
@@ -90,18 +90,18 @@ public class PyBlock implements ASTBlock {
private List<PyBlock> mySubBlocks = null;
private Map<ASTNode, PyBlock> mySubBlockByNode = null;
private final boolean myEmptySequence;
// Shared among multiple children sub-blocks
private Alignment myChildAlignment = null;
private Alignment myDictAlignment = null;
private Wrap myDictWrapping = null;
private Wrap myFromImportWrapping = null;
public PyBlock(@Nullable PyBlock parent,
@NotNull ASTNode node,
@Nullable Alignment alignment,
@NotNull Indent indent,
@Nullable Wrap wrap,
public PyBlock(@Nullable PyBlock parent,
@NotNull ASTNode node,
@Nullable Alignment alignment,
@NotNull Indent indent,
@Nullable Wrap wrap,
@NotNull PyBlockContext context) {
myParent = parent;
myAlignment = alignment;
@@ -189,7 +189,7 @@ public class PyBlock implements ASTBlock {
Wrap childWrap = null;
Indent childIndent = Indent.getNoneIndent();
Alignment childAlignment = null;
final PyCodeStyleSettings settings = myContext.getPySettings();
if (parentType == PyElementTypes.BINARY_EXPRESSION && !isInControlStatement()) {
@@ -260,7 +260,7 @@ public class PyBlock implements ASTBlock {
}
}
if (parentType == PyElementTypes.LIST_LITERAL_EXPRESSION || parentType == PyElementTypes.LIST_COMP_EXPRESSION) {
if ((childType == PyTokenTypes.RBRACKET && !settings.HANG_CLOSING_BRACKETS) || childType == PyTokenTypes.LBRACKET) {
childIndent = Indent.getNoneIndent();
@@ -617,7 +617,7 @@ public class PyBlock implements ASTBlock {
myContext.getMode() == FormattingMode.ADJUST_INDENT) {
return true;
}
return !hasHangingIndent(myNode.getPsi()) && !(myNode.getElementType() == PyElementTypes.DICT_LITERAL_EXPRESSION &&
return !hasHangingIndent(myNode.getPsi()) && !(myNode.getElementType() == PyElementTypes.DICT_LITERAL_EXPRESSION &&
myContext.getPySettings().DICT_NEW_LINE_AFTER_LEFT_BRACE);
}
if (myNode.getElementType() == PyElementTypes.ARGUMENT_LIST) {
@@ -726,6 +726,13 @@ public class PyBlock implements ASTBlock {
final PsiElement psi1 = node1.getPsi();
PsiElement psi2 = node2.getPsi();
// 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) {
return Spacing.createSpacing(0, 0, 1, true, 2);
}
// skip not inline comments to handles blank lines between various declarations
if (psi2 instanceof PsiComment && hasLineBreaksBeforeInSameParent(node2, 1)) {
final PsiElement nonCommentAfter = PyPsiUtils.getNextNonCommentSibling(psi2, true);
@@ -746,7 +753,7 @@ public class PyBlock implements ASTBlock {
return Spacing.createSpacing(1, 1, 0, settings.KEEP_LINE_BREAKS, settings.KEEP_BLANK_LINES_IN_CODE);
}
}
if (childType1 == PyTokenTypes.COLON && psi2 instanceof PyStatementList) {
if (needLineBreakInStatement()) {
return Spacing.createSpacing(0, 0, 1, true, settings.KEEP_BLANK_LINES_IN_CODE);
@@ -794,9 +801,6 @@ public class PyBlock implements ASTBlock {
}
}
if (psi2 instanceof PsiComment && !hasLineBreaksBeforeInSameParent(psi2.getNode(), 1) && pySettings.SPACE_BEFORE_NUMBER_SIGN) {
return Spacing.createSpacing(2, 0, 0, false, 0);
}
}
return myContext.getSpacingBuilder().getSpacing(this, child1, child2);
}
@@ -877,7 +881,7 @@ public class PyBlock implements ASTBlock {
// delegation sometimes causes NPEs in formatter core, so we calculate the
// correct indent manually.
if (statementListsBelow > 0) { // was 1... strange
@SuppressWarnings("ConstantConditions")
@SuppressWarnings("ConstantConditions")
final int indent = myContext.getSettings().getIndentOptions().INDENT_SIZE;
return new ChildAttributes(Indent.getSpaceIndent(indent * statementListsBelow), null);
}
@@ -162,6 +162,9 @@ public class PyLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSettin
indentOptions.INDENT_SIZE = 4;
defaultSettings.ALIGN_MULTILINE_PARAMETERS_IN_CALLS = true;
defaultSettings.KEEP_BLANK_LINES_IN_DECLARATIONS = 1;
// Don't set it to 2 -- this setting is used implicitly in a lot of methods related to spacing,
// e.g. in SpacingBuilder#blankLines(), and can lead to unexpected side-effects in formatter's
// behavior
defaultSettings.KEEP_BLANK_LINES_IN_CODE = 1;
return defaultSettings;
}
@@ -77,6 +77,8 @@ public class PythonFormattingModelBuilder implements FormattingModelBuilderEx, C
final CommonCodeStyleSettings commonSettings = settings.getCommonSettings(PythonLanguage.getInstance());
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)
@@ -0,0 +1,35 @@
# line comment 1
# line comment 2
# line comment 3
def f():
# inner line comment 1
# inner line comment 2
# inner line comment 3
x = 42 # trailing comment 1
# line comment 4
def g():
pass
if True:
# semi top-level comment 1
# semi top-level comment 2
# semi top-level comment 3
class C:
pass
@@ -0,0 +1,31 @@
# line comment 1
# line comment 2
# line comment 3
def f():
# inner line comment 1
# inner line comment 2
# inner line comment 3
x = 42 # trailing comment 1
# line comment 4
def g():
pass
if True:
# semi top-level comment 1
# semi top-level comment 2
# semi top-level comment 3
class C:
pass
@@ -830,6 +830,11 @@ public class PyFormatterTest extends PyTestCase {
doTest();
}
// PY-25356
public void testCommentsSpacing() {
doTest();
}
public void testVariableAnnotations() {
runWithLanguageLevel(LanguageLevel.PYTHON36, this::doTest);
}