PY-48009 Support formatting of PEP-634 match statements

GitOrigin-RevId: d4755af5ca19efef70db6c4a043135182bc0da04
This commit is contained in:
Mikhail Golubev
2021-06-16 11:53:25 +03:00
committed by intellij-monorepo-bot
parent da7f965b5a
commit ccf574bad9
52 changed files with 553 additions and 16 deletions

View File

@@ -45,7 +45,10 @@ public class PyBlock implements ASTBlock {
PyElementTypes.PARENTHESIZED_EXPRESSION,
PyElementTypes.SLICE_EXPRESSION,
PyElementTypes.SUBSCRIPTION_EXPRESSION,
PyElementTypes.GENERATOR_EXPRESSION);
PyElementTypes.GENERATOR_EXPRESSION,
PyElementTypes.SEQUENCE_PATTERN,
PyElementTypes.MAPPING_PATTERN,
PyElementTypes.PATTERN_ARGUMENT_LIST);
private static final TokenSet ourCollectionLiteralTypes = TokenSet.create(PyElementTypes.LIST_LITERAL_EXPRESSION,
PyElementTypes.LIST_COMP_EXPRESSION,
@@ -67,7 +70,10 @@ public class PyBlock implements ASTBlock {
PyElementTypes.GENERATOR_EXPRESSION,
PyElementTypes.FUNCTION_DECLARATION,
PyElementTypes.CALL_EXPRESSION,
PyElementTypes.FROM_IMPORT_STATEMENT);
PyElementTypes.FROM_IMPORT_STATEMENT,
PyElementTypes.SEQUENCE_PATTERN,
PyElementTypes.MAPPING_PATTERN,
PyElementTypes.PATTERN_ARGUMENT_LIST);
private static final boolean ALIGN_CONDITIONS_WITHOUT_PARENTHESES = false;
@@ -274,21 +280,28 @@ public class PyBlock implements ASTBlock {
childIndent = parenthesised ? Indent.getContinuationIndent() : Indent.getContinuationWithoutFirstIndent();
}
}
else if (parentType == PyElementTypes.LIST_LITERAL_EXPRESSION || parentType == PyElementTypes.LIST_COMP_EXPRESSION) {
if ((childType == PyTokenTypes.RBRACKET && !settings.HANG_CLOSING_BRACKETS) || childType == PyTokenTypes.LBRACKET) {
else if (parentType == PyElementTypes.OR_PATTERN) {
childAlignment = getAlignmentForChildren();
}
else if (parentType == PyElementTypes.SEQUENCE_PATTERN || parentType == PyElementTypes.MAPPING_PATTERN) {
if (PyTokenTypes.CLOSE_BRACES.contains(childType) && !settings.HANG_CLOSING_BRACKETS ||
PyTokenTypes.OPEN_BRACES.contains(childType)) {
childIndent = Indent.getNoneIndent();
}
else {
childIndent = settings.USE_CONTINUATION_INDENT_FOR_COLLECTION_AND_COMPREHENSIONS ? Indent.getContinuationIndent() : Indent.getNormalIndent();
childIndent = Indent.getNormalIndent();
}
}
else if (parentType == PyElementTypes.DICT_LITERAL_EXPRESSION || parentType == PyElementTypes.SET_LITERAL_EXPRESSION ||
parentType == PyElementTypes.SET_COMP_EXPRESSION || parentType == PyElementTypes.DICT_COMP_EXPRESSION) {
if ((childType == PyTokenTypes.RBRACE && !settings.HANG_CLOSING_BRACKETS) || childType == PyTokenTypes.LBRACE) {
else if (ourCollectionLiteralTypes.contains(parentType)) {
if ((PyTokenTypes.CLOSE_BRACES.contains(childType) && !settings.HANG_CLOSING_BRACKETS) ||
PyTokenTypes.OPEN_BRACES.contains(childType)) {
childIndent = Indent.getNoneIndent();
}
else if (settings.USE_CONTINUATION_INDENT_FOR_COLLECTION_AND_COMPREHENSIONS) {
childIndent = Indent.getContinuationIndent();
}
else {
childIndent = settings.USE_CONTINUATION_INDENT_FOR_COLLECTION_AND_COMPREHENSIONS ? Indent.getContinuationIndent() : Indent.getNormalIndent();
childIndent = Indent.getNormalIndent();
}
}
else if (parentType == PyElementTypes.STRING_LITERAL_EXPRESSION) {
@@ -361,7 +374,9 @@ public class PyBlock implements ASTBlock {
childIndent = useWiderIndent ? Indent.getContinuationIndent() : Indent.getNormalIndent();
}
}
else if (parentType == PyElementTypes.ARGUMENT_LIST || parentType == PyElementTypes.PARAMETER_LIST) {
else if (parentType == PyElementTypes.ARGUMENT_LIST ||
parentType == PyElementTypes.PATTERN_ARGUMENT_LIST ||
parentType == PyElementTypes.PARAMETER_LIST) {
if (childType == PyTokenTypes.RPAR && !settings.HANG_CLOSING_BRACKETS) {
childIndent = Indent.getNoneIndent();
}

View File

@@ -21,6 +21,8 @@ import static com.jetbrains.python.PyTokenTypes.*;
public class PythonFormattingModelBuilder implements FormattingModelBuilder, CustomFormattingModelBuilder {
private static final boolean DUMP_FORMATTING_AST = false;
static final TokenSet STATEMENT_OR_DECLARATION = PythonDialectsTokenSetProvider.getInstance().getStatementTokens();
private static final TokenSet STAR_PATTERNS = TokenSet.create(SINGLE_STAR_PATTERN, DOUBLE_STAR_PATTERN);
private static final TokenSet EXPRESSIONS_WITH_COLON = TokenSet.create(KEY_VALUE_EXPRESSION, KEY_VALUE_PATTERN, LAMBDA_EXPRESSION);
@Override
public @NotNull FormattingModel createModel(@NotNull FormattingContext formattingContext) {
@@ -56,7 +58,7 @@ public class PythonFormattingModelBuilder implements FormattingModelBuilder, Cus
.between(STATEMENT_OR_DECLARATION, STATEMENT_OR_DECLARATION).spacing(0, Integer.MAX_VALUE, 1, false, 1)
.between(COLON, STATEMENT_LIST).spacing(1, Integer.MAX_VALUE, 0, true, 0)
.afterInside(COLON, TokenSet.create(KEY_VALUE_EXPRESSION, LAMBDA_EXPRESSION)).spaceIf(pySettings.SPACE_AFTER_PY_COLON)
.afterInside(COLON, EXPRESSIONS_WITH_COLON).spaceIf(pySettings.SPACE_AFTER_PY_COLON)
.afterInside(GT, ANNOTATION).spaces(1)
.betweenInside(MINUS, GT, ANNOTATION).none()
@@ -107,11 +109,15 @@ public class PythonFormattingModelBuilder implements FormattingModelBuilder, Cus
.beforeInside(RPAR, PARAMETER_LIST)
.spaceIf(commonSettings.SPACE_WITHIN_METHOD_PARENTHESES, commonSettings.METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE)
.betweenInside(LPAR, RPAR, PATTERN_ARGUMENT_LIST).spaceIf(commonSettings.SPACE_WITHIN_EMPTY_METHOD_CALL_PARENTHESES)
.withinPairInside(LPAR, RPAR, PATTERN_ARGUMENT_LIST).spaceIf(commonSettings.SPACE_WITHIN_METHOD_CALL_PARENTHESES)
.withinPairInside(LPAR, RPAR, GENERATOR_EXPRESSION).spaces(0)
.withinPairInside(LPAR, RPAR, PARENTHESIZED_EXPRESSION).spaces(0)
.before(LBRACKET).spaceIf(pySettings.SPACE_BEFORE_LBRACKET)
.before(ARGUMENT_LIST).spaceIf(commonSettings.SPACE_BEFORE_METHOD_CALL_PARENTHESES)
.before(PATTERN_ARGUMENT_LIST).spaceIf(commonSettings.SPACE_BEFORE_METHOD_CALL_PARENTHESES)
.around(DECORATOR_CALL).spacing(1, Integer.MAX_VALUE, 0, true, 0)
.after(DECORATOR_LIST).spacing(1, Integer.MAX_VALUE, 0, true, 0)
@@ -123,6 +129,7 @@ public class PythonFormattingModelBuilder implements FormattingModelBuilder, Cus
.around(AUG_ASSIGN_OPERATIONS).spaceIf(commonSettings.SPACE_AROUND_ASSIGNMENT_OPERATORS)
.aroundInside(ADDITIVE_OPERATIONS, BINARY_EXPRESSION).spaceIf(commonSettings.SPACE_AROUND_ADDITIVE_OPERATORS)
.aroundInside(STAR_OPERATORS, STAR_PARAMETERS).none()
.aroundInside(STAR_OPERATORS, STAR_PATTERNS).none()
.around(MULTIPLICATIVE_OPERATIONS).spaceIf(commonSettings.SPACE_AROUND_MULTIPLICATIVE_OPERATORS)
.around(EXP).spaceIf(pySettings.SPACE_AROUND_POWER_OPERATOR)
.around(SHIFT_OPERATIONS).spaceIf(commonSettings.SPACE_AROUND_SHIFT_OPERATORS)
@@ -137,8 +144,9 @@ public class PythonFormattingModelBuilder implements FormattingModelBuilder, Cus
IF_KEYWORD, ELIF_KEYWORD, ELSE_KEYWORD,
FOR_KEYWORD, RETURN_KEYWORD, RAISE_KEYWORD,
ASSERT_KEYWORD, CLASS_KEYWORD, DEF_KEYWORD, DEL_KEYWORD,
EXEC_KEYWORD, GLOBAL_KEYWORD, NONLOCAL_KEYWORD, IMPORT_KEYWORD, LAMBDA_KEYWORD,
NOT_KEYWORD, WHILE_KEYWORD, YIELD_KEYWORD);
EXEC_KEYWORD, GLOBAL_KEYWORD, NONLOCAL_KEYWORD, IMPORT_KEYWORD,
LAMBDA_KEYWORD, NOT_KEYWORD, WHILE_KEYWORD, YIELD_KEYWORD,
AS_KEYWORD, MATCH_KEYWORD, CASE_KEYWORD);
private static TokenSet allButLambda() {
final PythonLanguage pythonLanguage = PythonLanguage.getInstance();

View File

@@ -1,5 +1,5 @@
match x:
case Class(
<caret>
<caret>
):
pass

View File

@@ -1,4 +1,4 @@
match x:
case {'foo': 1,
<caret>'bar': 2}
<caret>'bar': 2}
pass

View File

@@ -1,4 +1,4 @@
match x:
case [1,
<caret>2]:
<caret>2]:
pass

View File

@@ -0,0 +1,17 @@
match x:
case 1 \
| 2 \
| 3:
pass
case 1 | \
2 | \
3:
pass
case (1
| 2
| 3):
pass
case (1 |
2 |
3):
pass

View File

@@ -0,0 +1,12 @@
match x:
case (1 |
2
| 3,
[1
| 2,
3,],
Class(1
| 2,
3)
):
pass

View File

@@ -0,0 +1,12 @@
match x:
case (1 |
2
| 3,
[1
| 2,
3, ],
Class(1
| 2,
3)
):
pass

View File

@@ -0,0 +1,17 @@
match x:
case 1 \
| 2 \
| 3:
pass
case 1 | \
2 | \
3:
pass
case (1
| 2
| 3):
pass
case (1 |
2 |
3):
pass

View File

@@ -0,0 +1,5 @@
match x:
case Class(1,
foo=2,
bar=3):
pass

View File

@@ -0,0 +1,5 @@
match x:
case Class(1,
foo=2,
bar=3):
pass

View File

@@ -0,0 +1,7 @@
match x:
case Class(
1,
foo=2,
bar=3
):
pass

View File

@@ -0,0 +1,7 @@
match x:
case Class(
1,
foo=2,
bar=3
):
pass

View File

@@ -0,0 +1,7 @@
match x:
case Class(
1,
foo=2,
bar=3
):
pass

View File

@@ -0,0 +1,7 @@
match x:
case Class(
1,
foo=2,
bar=3
):
pass

View File

@@ -0,0 +1,7 @@
match x:
case {
'foo': 1,
'bar': 2,
'baz': 3
}:
pass

View File

@@ -0,0 +1,7 @@
match x:
case {
'foo': 1,
'bar': 2,
'baz': 3
}:
pass

View File

@@ -0,0 +1,13 @@
match x:
case [
1,
2,
3
]:
pass
case (
1,
2,
3
):
pass

View File

@@ -0,0 +1,13 @@
match x:
case [
1,
2,
3
]:
pass
case (
1,
2,
3
):
pass

View File

@@ -0,0 +1,5 @@
match x:
case {'foo': 1,
'bar': 2,
'baz': 3}:
pass

View File

@@ -0,0 +1,5 @@
match x:
case {'foo': 1,
'bar': 2,
'baz': 3}:
pass

View File

@@ -0,0 +1,6 @@
match x:
case [(1,
2),
[3,
4]
]

View File

@@ -0,0 +1,6 @@
match x:
case [(1,
2),
[3,
4]
]

View File

@@ -0,0 +1,9 @@
match x:
case [1,
2,
3]:
pass
case (1,
2,
3):
pass

View File

@@ -0,0 +1,9 @@
match x:
case [1,
2,
3]:
pass
case (1,
2,
3):
pass

View File

@@ -0,0 +1,7 @@
match x:
case {
'foo': 1,
'bar': 2,
'baz': 3
}:
pass

View File

@@ -0,0 +1,7 @@
match x:
case {
'foo': 1,
'bar': 2,
'baz': 3
}:
pass

View File

@@ -0,0 +1,13 @@
match x:
case [
1,
2,
3
]:
pass
case (
1,
2,
3
):
pass

View File

@@ -0,0 +1,13 @@
match x:
case [
1,
2,
3
]:
pass
case (
1,
2,
3
):
pass

View File

@@ -0,0 +1,7 @@
match x:
case (1, * xs):
pass
case [1, * _]:
pass
case {'foo': 1, ** others}:
pass

View File

@@ -0,0 +1,7 @@
match x:
case (1, *xs):
pass
case [1, *_]:
pass
case {'foo': 1, **others}:
pass

View File

@@ -0,0 +1,3 @@
match x:
case 42:
pass

View File

@@ -0,0 +1,3 @@
match x:
case 42:
pass

View File

@@ -0,0 +1,3 @@
match x:
case 42 as y:
pass

View File

@@ -0,0 +1,3 @@
match x:
case 42 as y:
pass

View File

@@ -0,0 +1,3 @@
match x:
case Class(foo = 1, bar=2):
pass

View File

@@ -0,0 +1,3 @@
match x:
case Class(foo=1, bar=2):
pass

View File

@@ -0,0 +1,3 @@
match x:
case {'foo': 1, 'bar': 2}:
pass

View File

@@ -0,0 +1,3 @@
match x :
case {'foo' :1, 'bar' :2} :
pass

View File

@@ -0,0 +1,9 @@
match x:
case [1, 2,]:
pass
case (1, 2,):
pass
case {'foo': 1, 'bar': 2,}:
pass
case Class(foo=1, bar=2,):
pass

View File

@@ -0,0 +1,9 @@
match x:
case [1 ,2 ,]:
pass
case (1 ,2 ,):
pass
case {'foo': 1 ,'bar': 2 ,}:
pass
case Class(foo=1 ,bar=2 ,):
pass

View File

@@ -0,0 +1,9 @@
match x:
case Class():
pass
case Class(foo=1):
pass
case Class(foo=1, bar=2):
pass
case Class(foo=1, bar=2,):
pass

View File

@@ -0,0 +1,9 @@
match x:
case Class ():
pass
case Class (foo=1):
pass
case Class (foo=1, bar=2):
pass
case Class (foo=1, bar=2, ):
pass

View File

@@ -0,0 +1,10 @@
match x:
case {}:
pass
case {'foo': 1}:
pass
case {'foo': 1, 'bar': 2}:
pass
case {'foo': 1, 'bar': 2,}:
pass

View File

@@ -0,0 +1,9 @@
match x:
case { }:
pass
case { 'foo': 1 }:
pass
case { 'foo': 1, 'bar': 2 }:
pass
case { 'foo': 1, 'bar': 2, }:
pass

View File

@@ -0,0 +1,17 @@
match x:
case []:
pass
case [1]:
pass
case [1, 2]:
pass
case [1, 2,]:
pass
case ():
pass
case (1,):
pass
case (1, 2):
pass
case (1, 2,):
pass

View File

@@ -0,0 +1,17 @@
match x:
case [ ]:
pass
case [ 1 ]:
pass
case [ 1, 2 ]:
pass
case [ 1, 2, ]:
pass
case ():
pass
case (1, ):
pass
case (1, 2):
pass
case (1, 2, ):
pass

View File

@@ -0,0 +1,9 @@
match x:
case Class():
pass
case Class(foo=1):
pass
case Class(foo=1, bar=2):
pass
case Class(foo=1, bar=2,):
pass

View File

@@ -0,0 +1,9 @@
match x:
case Class( ):
pass
case Class( foo=1 ):
pass
case Class( foo=1, bar=2 ):
pass
case Class( foo=1, bar=2, ):
pass

View File

@@ -0,0 +1,13 @@
match x:
case ('foo'
'bar'):
pass
case ('foo'
'bar',):
pass
case ['foo'
'bar']:
pass
case Class('foo'
'bar'):
pass

View File

@@ -0,0 +1,13 @@
match x:
case ('foo'
'bar'):
pass
case ('foo'
'bar', ):
pass
case ['foo'
'bar']:
pass
case Class('foo'
'bar'):
pass

View File

@@ -1083,4 +1083,131 @@ public class PyFormatterTest extends PyTestCase {
public void testIndentOfCommentsInsideMatchStatement() {
doTest();
}
// PY-49167
public void testNoSpacesInsideStarPatterns() {
doTest();
}
// PY-48009
public void testSpacesWithinBracketsInSequencePatterns() {
getCommonCodeStyleSettings().SPACE_WITHIN_BRACKETS = true;
doTest();
}
// PY-48009
public void testSpacesWithinBracesInMappingPatterns() {
getPythonCodeStyleSettings().SPACE_WITHIN_BRACES = true;
doTest();
}
// PY-48009
public void testSpacesWithinParenthesesInClassPatterns() {
getCommonCodeStyleSettings().SPACE_WITHIN_EMPTY_METHOD_CALL_PARENTHESES = true;
getCommonCodeStyleSettings().SPACE_WITHIN_METHOD_CALL_PARENTHESES = true;
doTest();
}
// PY-48009
public void testSpacesBeforeParenthesesInClassPatterns() {
getCommonCodeStyleSettings().SPACE_BEFORE_METHOD_CALL_PARENTHESES = true;
doTest();
}
// PY-48009
public void testSpacesBeforeAndAfterCommasInPatterns() {
getCommonCodeStyleSettings().SPACE_BEFORE_COMMA = true;
getCommonCodeStyleSettings().SPACE_AFTER_COMMA = false;
doTest();
}
// PY-48009
public void testSpacesBeforeAndAfterColonsInPatterns() {
getPythonCodeStyleSettings().SPACE_BEFORE_PY_COLON = true;
getPythonCodeStyleSettings().SPACE_AFTER_PY_COLON = false;
doTest();
}
// PY-48009
public void testItemAlignmentInSequencePatterns() {
doTest();
}
// PY-48009
public void testItemAlignmentInNestedSequencePatterns() {
doTest();
}
// PY-48009
public void testItemIndentInSequencePatterns() {
doTest();
}
// PY-48009
public void testHangingClosingBracketInSequencePatterns() {
getPythonCodeStyleSettings().HANG_CLOSING_BRACKETS = true;
doTest();
}
// PY-48009
public void testItemAlignmentInMappingPatterns() {
doTest();
}
// PY-48009
public void testItemIndentInMappingPatterns() {
doTest();
}
// PY-48009
public void testHangingClosingBracketInMappingPatterns() {
getPythonCodeStyleSettings().HANG_CLOSING_BRACKETS = true;
doTest();
}
// PY-48009
public void testAttributeAlignmentInClassPatterns() {
doTest();
}
// PY-48009
public void testAttributeIndentInClassPatterns() {
doTest();
}
// PY-48009
public void testHangingClosingBracketInClassPatterns() {
getPythonCodeStyleSettings().HANG_CLOSING_BRACKETS = true;
doTest();
}
// PY-48009
public void testStringElementAlignmentInLiteralPatterns() {
doTest();
}
// PY-48009
public void testSpacesAroundAsKeywordInPatterns() {
doTest();
}
// PY-48009
public void testSpacesAfterMatchAndCaseKeywords() {
doTest();
}
// PY-48009
public void testAlternativesAlignmentInOrPatterns() {
doTest();
}
// PY-48009
public void testAlternativesAlignmentInOrPatternsInsideSequenceLikePattern() {
doTest();
}
// PY-48009
public void testSpacesAroundEqualSignsInKeywordPatterns() {
doTest();
}
}