diff --git a/python/psi-api/src/com/jetbrains/python/PyTokenTypes.java b/python/psi-api/src/com/jetbrains/python/PyTokenTypes.java index 3e5c2037de96..70f636351f3a 100644 --- a/python/psi-api/src/com/jetbrains/python/PyTokenTypes.java +++ b/python/psi-api/src/com/jetbrains/python/PyTokenTypes.java @@ -155,7 +155,7 @@ public class PyTokenTypes { public static final TokenSet SHIFT_OPERATIONS = TokenSet.create(LTLT, GTGT); public static final TokenSet ADDITIVE_OPERATIONS = TokenSet.create(PLUS, MINUS); public static final TokenSet MULTIPLICATIVE_OPERATIONS = TokenSet.create(MULT, AT, FLOORDIV, DIV, PERC); - public static final TokenSet MULTIPLICATIVE_OR_EXP = TokenSet.create(MULT, AT, FLOORDIV, DIV, PERC, EXP); + public static final TokenSet STAR_OPERATORS = TokenSet.create(MULT, EXP); public static final TokenSet UNARY_OPERATIONS = TokenSet.create(PLUS, MINUS, TILDE); public static final TokenSet BITWISE_OPERATIONS = TokenSet.create(AND, OR, XOR); public static final TokenSet EQUALITY_OPERATIONS = TokenSet.create(EQEQ, NE, NE_OLD); diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index 77d343d60ddd..da279f743a53 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -983,6 +983,8 @@ formatter.after.hash = After '#' formatter.around.top.level.imports = After top-level imports: formatter.after.local.imports=After local imports: 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 (**) formatter.single.clause.statements=Single-clause statements formatter.multi.clause.statements=Multi-clause statements formatter.force.new.line.after.colon=Force new line after colon diff --git a/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java b/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java index e87b16768cd5..4ae389a76f27 100644 --- a/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java +++ b/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java @@ -57,6 +57,7 @@ public class PyCodeStyleSettings extends CustomCodeStyleSettings { public boolean SPACE_BEFORE_LBRACKET = false; public boolean SPACE_AROUND_EQ_IN_NAMED_PARAMETER = false; public boolean SPACE_AROUND_EQ_IN_KEYWORD_ARGUMENT = false; + public boolean SPACE_AROUND_POWER_OPERATOR = true; public boolean SPACE_BEFORE_BACKSLASH = true; public int BLANK_LINES_AROUND_TOP_LEVEL_CLASSES_FUNCTIONS = 2; @@ -82,8 +83,8 @@ public class PyCodeStyleSettings extends CustomCodeStyleSettings { * Code style for most languages use continuation indent both for parameters in function definition and for arguments in function calls. * In Python continuation indent (assuming it's 8 spaces) for parameters is required by PEP 8, because otherwise they won't be visually * distinctive from function body. However for arguments (except several special cases) both normal and continuation indents are acceptable - * (as long as they're multiple of 4), though examples in PEP 8 itself use mostly normal indent. Nontheless some users prefer to have - * the same indetation level for arguments as for parameters. + * (as long as they're multiple of 4), though examples in PEP 8 itself use mostly normal indent. Nonetheless, some users prefer to have + * the same indentation level for arguments as for parameters. */ public boolean USE_CONTINUATION_INDENT_FOR_ARGUMENTS = false; diff --git a/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java b/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java index ce82d321465d..7a7d2181c8c9 100644 --- a/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java +++ b/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java @@ -69,6 +69,8 @@ public class PyLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSettin "SPACE_BEFORE_SEMICOLON"); consumer.showCustomOption(PyCodeStyleSettings.class, "SPACE_BEFORE_LBRACKET", PyBundle.message("formatter.left.bracket"), SPACES_BEFORE_PARENTHESES); + consumer.showCustomOption(PyCodeStyleSettings.class, "SPACE_AROUND_POWER_OPERATOR", + PyBundle.message("formatter.around.power.operator"), SPACES_AROUND_OPERATORS); consumer.showCustomOption(PyCodeStyleSettings.class, "SPACE_AROUND_EQ_IN_NAMED_PARAMETER", PyBundle.message("formatter.around.eq.in.named.parameter"), SPACES_AROUND_OPERATORS); consumer.showCustomOption(PyCodeStyleSettings.class, "SPACE_AROUND_EQ_IN_KEYWORD_ARGUMENT", @@ -84,6 +86,7 @@ public class PyLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSettin PyBundle.message("formatter.before.hash"), SPACES_OTHER); consumer.showCustomOption(PyCodeStyleSettings.class, "SPACE_AFTER_NUMBER_SIGN", PyBundle.message("formatter.after.hash"), SPACES_OTHER); + consumer.renameStandardOption("SPACE_AROUND_MULTIPLICATIVE_OPERATORS", PyBundle.message("formatter.around.multiplicative.operators")); } else if (settingsType == SettingsType.BLANK_LINES_SETTINGS) { consumer.showStandardOptions("BLANK_LINES_AROUND_CLASS", @@ -154,7 +157,7 @@ public class PyLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSettin private static String SPACING_SETTINGS_PREVIEW = "def settings_preview(argument, key=value):\n" + " dict = {1:'a', 2:'b', 3:'c'}\n" + " x = dict[1]\n" + - " expr = (1+2)*3 << 4 & 16\n" + + " expr = (1+2)*3 << 4**5 & 16\n" + " if expr == 0 or abs(expr) < 0: print('weird'); return\n" + " settings_preview(key=1)\n\n" + "foo =\\\n" + diff --git a/python/src/com/jetbrains/python/formatter/PythonFormattingModelBuilder.java b/python/src/com/jetbrains/python/formatter/PythonFormattingModelBuilder.java index 515c00ccbbfd..5fd36b55a774 100644 --- a/python/src/com/jetbrains/python/formatter/PythonFormattingModelBuilder.java +++ b/python/src/com/jetbrains/python/formatter/PythonFormattingModelBuilder.java @@ -131,8 +131,9 @@ public class PythonFormattingModelBuilder implements FormattingModelBuilderEx, C .around(AUG_ASSIGN_OPERATIONS).spaceIf(commonSettings.SPACE_AROUND_ASSIGNMENT_OPERATORS) .aroundInside(ADDITIVE_OPERATIONS, BINARY_EXPRESSION).spaceIf(commonSettings.SPACE_AROUND_ADDITIVE_OPERATORS) - .aroundInside(MULTIPLICATIVE_OR_EXP, STAR_PARAMETERS).none() - .around(MULTIPLICATIVE_OR_EXP).spaceIf(commonSettings.SPACE_AROUND_MULTIPLICATIVE_OPERATORS) + .aroundInside(STAR_OPERATORS, STAR_PARAMETERS).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) .around(BITWISE_OPERATIONS).spaceIf(commonSettings.SPACE_AROUND_BITWISE_OPERATORS) .around(EQUALITY_OPERATIONS).spaceIf(commonSettings.SPACE_AROUND_EQUALITY_OPERATORS) diff --git a/python/testData/formatter/noSpaceAroundPowerOperator.py b/python/testData/formatter/noSpaceAroundPowerOperator.py new file mode 100644 index 000000000000..66cfc795b7d2 --- /dev/null +++ b/python/testData/formatter/noSpaceAroundPowerOperator.py @@ -0,0 +1,5 @@ +import math + + +def circle_area(radius): + return math.PI * radius ** 2 diff --git a/python/testData/formatter/noSpaceAroundPowerOperator_after.py b/python/testData/formatter/noSpaceAroundPowerOperator_after.py new file mode 100644 index 000000000000..f1dee02c64e5 --- /dev/null +++ b/python/testData/formatter/noSpaceAroundPowerOperator_after.py @@ -0,0 +1,5 @@ +import math + + +def circle_area(radius): + return math.PI * radius**2 diff --git a/python/testSrc/com/jetbrains/python/PyFormatterTest.java b/python/testSrc/com/jetbrains/python/PyFormatterTest.java index 030542de8dca..cfa037e098a5 100644 --- a/python/testSrc/com/jetbrains/python/PyFormatterTest.java +++ b/python/testSrc/com/jetbrains/python/PyFormatterTest.java @@ -665,4 +665,10 @@ public class PyFormatterTest extends PyTestCase { getPythonCodeStyleSettings().USE_CONTINUATION_INDENT_FOR_ARGUMENTS = true; doTest(); } + + // PY-18265 + public void testNoSpaceAroundPowerOperator() { + getPythonCodeStyleSettings().SPACE_AROUND_POWER_OPERATOR = false; + doTest(); + } }