diff --git a/python/src/com/jetbrains/python/PyTokenTypes.java b/python/src/com/jetbrains/python/PyTokenTypes.java index c25d33085146..7df4b49c4807 100644 --- a/python/src/com/jetbrains/python/PyTokenTypes.java +++ b/python/src/com/jetbrains/python/PyTokenTypes.java @@ -137,6 +137,9 @@ public class PyTokenTypes { public static final TokenSet ADDITIVE_OPERATIONS = TokenSet.create(PLUS, MINUS); public static final TokenSet MULTIPLICATIVE_OPERATIONS = TokenSet.create(MULT, FLOORDIV, DIV, PERC); 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); + public static final TokenSet RELATIONAL_OPERATIONS = TokenSet.create(LT, GT, LE, GE); public static final TokenSet END_OF_STATEMENT = TokenSet.create(STATEMENT_BREAK, SEMICOLON); public static final TokenSet WHITESPACE = TokenSet.create(SPACE, TAB, FORMFEED); public static final TokenSet WHITESPACE_OR_LINEBREAK = TokenSet.create(SPACE, TAB, FORMFEED, LINE_BREAK); diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index 2a2c6d5f84e6..c39d7a911745 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -212,15 +212,71 @@ public class PyBlock implements ASTBlock { if (type1 == PyTokenTypes.COLON && type2 == PyElementTypes.STATEMENT_LIST) { return Spacing.createSpacing(1, Integer.MAX_VALUE, 0, true, 0); } + if (type2 == PyTokenTypes.COLON) { + return getSpacingForOption(getPySettings().SPACE_BEFORE_COLON); + } if (type1 == PyTokenTypes.COMMA) { return getSpacingForOption(mySettings.SPACE_AFTER_COMMA); } -/* - if (type1 == PyTokenTypes.COLON && type2 == PyElementTypes.STATEMENT_LIST) { - return Spacing.createSpacing(0, Integer.MAX_VALUE, 1, true, Integer.MAX_VALUE); - } -*/ + if (type2 == PyTokenTypes.COMMA) { + return getSpacingForOption(mySettings.SPACE_BEFORE_COMMA); + } + if (type2 == PyTokenTypes.SEMICOLON) { + return getSpacingForOption(mySettings.SPACE_BEFORE_SEMICOLON); + } + + if (type1 == PyTokenTypes.LPAR || type2 == PyTokenTypes.RPAR) { + if (parentType == PyElementTypes.ARGUMENT_LIST) { + return getSpacingForOption(mySettings.SPACE_WITHIN_METHOD_CALL_PARENTHESES); + } + } + if (type2 == PyTokenTypes.LBRACKET) { + return getSpacingForOption(getPySettings().SPACE_BEFORE_LBRACKET); + } + if (type1 == PyTokenTypes.LBRACKET || type2 == PyTokenTypes.RBRACKET) { + return getSpacingForOption(mySettings.SPACE_WITHIN_BRACKETS); + } + if (type1 == PyTokenTypes.LBRACE || type2 == PyTokenTypes.RBRACE) { + return getSpacingForOption(getPySettings().SPACE_WITHIN_BRACES); + } + if (type2 == PyElementTypes.ARGUMENT_LIST) { + return getSpacingForOption(mySettings.SPACE_BEFORE_METHOD_CALL_PARENTHESES); + } + + if (type1 == PyTokenTypes.EQ || type2 == PyTokenTypes.EQ) { + if (parentType == PyElementTypes.ASSIGNMENT_STATEMENT) { + return getSpacingForOption(mySettings.SPACE_AROUND_ASSIGNMENT_OPERATORS); + } + if (parentType == PyElementTypes.NAMED_PARAMETER) { + return getSpacingForOption(getPySettings().SPACE_AROUND_EQ_IN_NAMED_PARAMETER); + } + if (parentType == PyElementTypes.KEYWORD_ARGUMENT_EXPRESSION) { + return getSpacingForOption(getPySettings().SPACE_AROUND_EQ_IN_KEYWORD_ARGUMENT); + } + } + if (isAround(type1, type2, PyTokenTypes.AUG_ASSIGN_OPERATIONS)) { + return getSpacingForOption(mySettings.SPACE_AROUND_ASSIGNMENT_OPERATORS); + } + if (isAround(type1, type2, PyTokenTypes.ADDITIVE_OPERATIONS)) { + return getSpacingForOption(mySettings.SPACE_AROUND_ADDITIVE_OPERATORS); + } + if (isAround(type1, type2, PyTokenTypes.MULTIPLICATIVE_OPERATIONS)) { + return getSpacingForOption(mySettings.SPACE_AROUND_MULTIPLICATIVE_OPERATORS); + } + if (isAround(type1, type2, PyTokenTypes.SHIFT_OPERATIONS)) { + return getSpacingForOption(mySettings.SPACE_AROUND_SHIFT_OPERATORS); + } + if (isAround(type1, type2, PyTokenTypes.BITWISE_OPERATIONS)) { + return getSpacingForOption(mySettings.SPACE_AROUND_BITWISE_OPERATORS); + } + if (isAround(type1, type2, PyTokenTypes.EQUALITY_OPERATIONS)) { + return getSpacingForOption(mySettings.SPACE_AROUND_EQUALITY_OPERATORS); + } + if (isAround(type1, type2, PyTokenTypes.RELATIONAL_OPERATIONS)) { + return getSpacingForOption(mySettings.SPACE_AROUND_RELATIONAL_OPERATORS); + } + //if (parentType == PyElementTypes.ARGUMENT_LIST // || parentType == PyElementTypes.LIST_LITERAL_EXPRESSION) { @@ -240,6 +296,14 @@ public class PyBlock implements ASTBlock { return null; } + private static boolean isAround(IElementType type1, IElementType type2, final TokenSet tokenSet) { + return tokenSet.contains(type1) || tokenSet.contains(type2); + } + + private PyCodeStyleSettings getPySettings() { + return mySettings.getCustomSettings(PyCodeStyleSettings.class); + } + private Spacing getSpacingForOption(boolean isOptionSet) { int spaces = isOptionSet ? 1 : 0; return Spacing.createSpacing(spaces, spaces, 0, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); diff --git a/python/src/com/jetbrains/python/formatter/PyCodeStylePanel.form b/python/src/com/jetbrains/python/formatter/PyCodeStylePanel.form new file mode 100644 index 000000000000..d776fac9f890 --- /dev/null +++ b/python/src/com/jetbrains/python/formatter/PyCodeStylePanel.form @@ -0,0 +1,12 @@ + +
diff --git a/python/src/com/jetbrains/python/formatter/PyCodeStylePanel.java b/python/src/com/jetbrains/python/formatter/PyCodeStylePanel.java new file mode 100644 index 000000000000..00834f861eef --- /dev/null +++ b/python/src/com/jetbrains/python/formatter/PyCodeStylePanel.java @@ -0,0 +1,69 @@ +package com.jetbrains.python.formatter; + +import com.intellij.application.options.CodeStyleAbstractPanel; +import com.intellij.ide.highlighter.HighlighterFactory; +import com.intellij.openapi.editor.colors.EditorColorsScheme; +import com.intellij.openapi.editor.highlighter.EditorHighlighter; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.jetbrains.python.PyHighlighter; +import com.jetbrains.python.PythonFileType; +import com.jetbrains.python.psi.LanguageLevel; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +/** + * @author yole + */ +public class PyCodeStylePanel extends CodeStyleAbstractPanel { + private JPanel myPanel; + + protected PyCodeStylePanel(CodeStyleSettings settings) { + super(settings); + } + + @Override + protected EditorHighlighter createHighlighter(EditorColorsScheme scheme) { + return HighlighterFactory.createHighlighter(new PyHighlighter(LanguageLevel.PYTHON26), scheme); + } + + @Override + protected int getRightMargin() { + return 80; + } + + @Override + protected void prepareForReformat(PsiFile psiFile) { + } + + @NotNull + @Override + protected FileType getFileType() { + return PythonFileType.INSTANCE; + } + + @Override + protected String getPreviewText() { + return ""; + } + + @Override + protected void resetImpl(CodeStyleSettings settings) { + } + + @Override + public void apply(CodeStyleSettings settings) { + } + + @Override + public boolean isModified(CodeStyleSettings settings) { + return false; + } + + @Override + public JComponent getPanel() { + return myPanel; + } +} diff --git a/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java b/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java new file mode 100644 index 000000000000..2d77a950c261 --- /dev/null +++ b/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java @@ -0,0 +1,19 @@ +package com.jetbrains.python.formatter; + +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CustomCodeStyleSettings; + +/** + * @author yole + */ +public class PyCodeStyleSettings extends CustomCodeStyleSettings { + public boolean SPACE_WITHIN_BRACES = false; + public boolean SPACE_BEFORE_COLON = false; + public boolean SPACE_BEFORE_LBRACKET = false; + public boolean SPACE_AROUND_EQ_IN_NAMED_PARAMETER = false; + public boolean SPACE_AROUND_EQ_IN_KEYWORD_ARGUMENT = false; + + protected PyCodeStyleSettings(CodeStyleSettings container) { + super("Python", container); + } +} diff --git a/python/src/com/jetbrains/python/formatter/PyCodeStyleSettingsProvider.java b/python/src/com/jetbrains/python/formatter/PyCodeStyleSettingsProvider.java new file mode 100644 index 000000000000..ee3ddff246f6 --- /dev/null +++ b/python/src/com/jetbrains/python/formatter/PyCodeStyleSettingsProvider.java @@ -0,0 +1,38 @@ +package com.jetbrains.python.formatter; + +import com.intellij.application.options.CodeStyleAbstractConfigurable; +import com.intellij.application.options.CodeStyleAbstractPanel; +import com.intellij.openapi.options.Configurable; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CodeStyleSettingsProvider; +import com.intellij.psi.codeStyle.CustomCodeStyleSettings; +import org.jetbrains.annotations.NotNull; + +/** + * @author yole + */ +public class PyCodeStyleSettingsProvider extends CodeStyleSettingsProvider { + @Override + public CustomCodeStyleSettings createCustomSettings(CodeStyleSettings settings) { + return new PyCodeStyleSettings(settings); + } + + @NotNull + @Override + public Configurable createSettingsPage(CodeStyleSettings settings, CodeStyleSettings originalSettings) { + return new CodeStyleAbstractConfigurable(settings, originalSettings, "Ruby") { + protected CodeStyleAbstractPanel createPanel(final CodeStyleSettings settings) { + return new PyCodeStylePanel(settings); + } + + public String getHelpTopic() { + return null; + } + }; + } + + @Override + public String getConfigurableDisplayName() { + return "Python"; + } +} diff --git a/python/testData/formatter/pep8ExtraneousWhitespace.py b/python/testData/formatter/pep8ExtraneousWhitespace.py new file mode 100644 index 000000000000..3e8f49476605 --- /dev/null +++ b/python/testData/formatter/pep8ExtraneousWhitespace.py @@ -0,0 +1,4 @@ +spam( ham[ 1 ], { eggs: 2 } ) +if x == 4 : print x , y ; x , y = y , x +spam (1) +dict ['key'] = list [index] \ No newline at end of file diff --git a/python/testData/formatter/pep8ExtraneousWhitespace_after.py b/python/testData/formatter/pep8ExtraneousWhitespace_after.py new file mode 100644 index 000000000000..8e8435abf61e --- /dev/null +++ b/python/testData/formatter/pep8ExtraneousWhitespace_after.py @@ -0,0 +1,4 @@ +spam(ham[1], {eggs: 2}) +if x == 4: print x, y; x, y = y, x +spam(1) +dict['key'] = list[index] \ No newline at end of file diff --git a/python/testData/formatter/pep8KeywordArguments.py b/python/testData/formatter/pep8KeywordArguments.py new file mode 100644 index 000000000000..f64329217080 --- /dev/null +++ b/python/testData/formatter/pep8KeywordArguments.py @@ -0,0 +1,2 @@ +def complex(real, imag = 0.0): + return magic(r = real, i = imag) diff --git a/python/testData/formatter/pep8KeywordArguments_after.py b/python/testData/formatter/pep8KeywordArguments_after.py new file mode 100644 index 000000000000..c1b77345a065 --- /dev/null +++ b/python/testData/formatter/pep8KeywordArguments_after.py @@ -0,0 +1,2 @@ +def complex(real, imag=0.0): + return magic(r=real, i=imag) diff --git a/python/testData/formatter/pep8Operators.py b/python/testData/formatter/pep8Operators.py new file mode 100644 index 000000000000..6b175f27ef40 --- /dev/null +++ b/python/testData/formatter/pep8Operators.py @@ -0,0 +1,3 @@ +i=i+1 +submitted+=1 +x = x*2-1 diff --git a/python/testData/formatter/pep8Operators_after.py b/python/testData/formatter/pep8Operators_after.py new file mode 100644 index 000000000000..886392da79f6 --- /dev/null +++ b/python/testData/formatter/pep8Operators_after.py @@ -0,0 +1,3 @@ +i = i + 1 +submitted += 1 +x = x * 2 - 1 diff --git a/python/testSrc/com/jetbrains/python/PyFormatterTest.java b/python/testSrc/com/jetbrains/python/PyFormatterTest.java index d80e5f00710d..ff1185e012c1 100644 --- a/python/testSrc/com/jetbrains/python/PyFormatterTest.java +++ b/python/testSrc/com/jetbrains/python/PyFormatterTest.java @@ -20,6 +20,18 @@ public class PyFormatterTest extends PyLightFixtureTestCase { doTest(); } + public void testPep8ExtraneousWhitespace() throws Exception { + doTest(); + } + + public void testPep8Operators() throws Exception { + doTest(); + } + + public void testPep8KeywordArguments() throws Exception { + doTest(); + } + private void doTest() throws Exception { myFixture.configureByFile("formatter/" + getTestName(true) + ".py"); ApplicationManager.getApplication().runWriteAction(new Runnable() {