From 1835d329f0a55190661583287cc0399f532b8202 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 22 Aug 2012 10:32:05 +0200 Subject: [PATCH 01/12] allow specifying singleton option when creating a Python script run configuration --- .../python/run/PyRunConfigurationFactory.java | 10 +++++++++- .../python/run/PyRunConfigurationFactoryImpl.java | 3 ++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/python/openapi/src/com/jetbrains/python/run/PyRunConfigurationFactory.java b/python/openapi/src/com/jetbrains/python/run/PyRunConfigurationFactory.java index 97603c4ac6de..dd2f1ccf191b 100644 --- a/python/openapi/src/com/jetbrains/python/run/PyRunConfigurationFactory.java +++ b/python/openapi/src/com/jetbrains/python/run/PyRunConfigurationFactory.java @@ -28,7 +28,15 @@ public abstract class PyRunConfigurationFactory { return ServiceManager.getService(PyRunConfigurationFactory.class); } - public abstract PythonRunConfigurationParams createPythonScriptRunConfiguration(Module module, String scriptName); + /** + * Creates a run configuration to run a specified Python script. + * + * @param module the module in the context of which the script is run. + * @param scriptName the path to the script file. + * @param singleton if true, the "Check no other instances are running" option will be set for the run configuration. + * @return the settings of the created run configuration. + */ + public abstract PythonRunConfigurationParams createPythonScriptRunConfiguration(Module module, String scriptName, boolean singleton); public abstract RunnerAndConfigurationSettings createRunConfiguration(Module module, ConfigurationFactory factory); } diff --git a/python/src/com/jetbrains/python/run/PyRunConfigurationFactoryImpl.java b/python/src/com/jetbrains/python/run/PyRunConfigurationFactoryImpl.java index d7f64373fbec..559712533292 100644 --- a/python/src/com/jetbrains/python/run/PyRunConfigurationFactoryImpl.java +++ b/python/src/com/jetbrains/python/run/PyRunConfigurationFactoryImpl.java @@ -13,8 +13,9 @@ import org.jetbrains.annotations.NotNull; */ public class PyRunConfigurationFactoryImpl extends PyRunConfigurationFactory { @Override - public PythonRunConfigurationParams createPythonScriptRunConfiguration(Module module, String scriptName) { + public PythonRunConfigurationParams createPythonScriptRunConfiguration(Module module, String scriptName, boolean singleton) { RunnerAndConfigurationSettings settings = createRunConfiguration(module, PythonConfigurationType.getInstance().getFactory()); + settings.setSingleton(singleton); PythonRunConfigurationParams configuration = (PythonRunConfigurationParams)settings.getConfiguration(); configuration.setScriptName(scriptName); return configuration; From 5c7ef4d5215c0f7fddffaef5db19b5f65153f346 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 22 Aug 2012 11:45:13 +0200 Subject: [PATCH 02/12] less broken formatting for literals in argument list (PY-6672) --- .../com/jetbrains/python/formatter/PyBlock.java | 8 ++++++-- python/testData/formatter/setLiteralInArgList.py | 3 +++ .../formatter/setLiteralInArgList_after.py | 3 +++ .../com/jetbrains/python/PyFormatterTest.java | 16 +++++++++++++--- 4 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 python/testData/formatter/setLiteralInArgList.py create mode 100644 python/testData/formatter/setLiteralInArgList_after.py diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index dc6956d7691f..b377df3fa66d 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -160,7 +160,7 @@ public class PyBlock implements ASTBlock { } } else if (parentType == PyElementTypes.DICT_LITERAL_EXPRESSION || parentType == PyElementTypes.SET_LITERAL_EXPRESSION) { - if (childType == PyTokenTypes.RBRACE) { + if (childType == PyTokenTypes.RBRACE || !hasLineBreaksBefore(child, 1)) { childIndent = Indent.getNoneIndent(); } else { @@ -238,10 +238,14 @@ public class PyBlock implements ASTBlock { if (!mySettings.ALIGN_MULTILINE_PARAMETERS_IN_CALLS) { return false; } + if (child.getElementType() == PyTokenTypes.COMMA) { + return false; + } PyArgumentList argList = (PyArgumentList)_node.getPsi(); if (argList != null) { PyExpression[] arguments = argList.getArguments(); - return arguments.length > 1 || (arguments.length == 1 && PyPsiUtils.getNextComma(arguments[0].getNode()) != null); + return arguments.length > 1 || hasLineBreaksBefore(child, 1) || ( + arguments.length == 1 && PyPsiUtils.getNextComma(arguments[0].getNode()) != null); } return false; } diff --git a/python/testData/formatter/setLiteralInArgList.py b/python/testData/formatter/setLiteralInArgList.py new file mode 100644 index 000000000000..9c2f75d6dca6 --- /dev/null +++ b/python/testData/formatter/setLiteralInArgList.py @@ -0,0 +1,3 @@ +self.assertEqual( + {"000000000000", "111111111111"}, + foo['bar']['baz']) diff --git a/python/testData/formatter/setLiteralInArgList_after.py b/python/testData/formatter/setLiteralInArgList_after.py new file mode 100644 index 000000000000..9c2f75d6dca6 --- /dev/null +++ b/python/testData/formatter/setLiteralInArgList_after.py @@ -0,0 +1,3 @@ +self.assertEqual( + {"000000000000", "111111111111"}, + foo['bar']['baz']) diff --git a/python/testSrc/com/jetbrains/python/PyFormatterTest.java b/python/testSrc/com/jetbrains/python/PyFormatterTest.java index d579e7cc7693..8940604722b0 100644 --- a/python/testSrc/com/jetbrains/python/PyFormatterTest.java +++ b/python/testSrc/com/jetbrains/python/PyFormatterTest.java @@ -4,6 +4,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.jetbrains.python.fixtures.PyTestCase; import com.jetbrains.python.psi.LanguageLevel; @@ -118,12 +119,12 @@ public class PyFormatterTest extends PyTestCase { } public void testSpaceInMethodDeclaration() { // PY-4241 - CodeStyleSettingsManager.getInstance().getSettings(myFixture.getProject()).SPACE_BEFORE_METHOD_PARENTHESES = true; + settings().SPACE_BEFORE_METHOD_PARENTHESES = true; doTest(); } public void testOptionalAlignForMethodParameters() { // PY-3995 - CodeStyleSettingsManager.getInstance().getSettings(myFixture.getProject()).ALIGN_MULTILINE_PARAMETERS = false; + settings().ALIGN_MULTILINE_PARAMETERS = false; doTest(); } @@ -132,7 +133,7 @@ public class PyFormatterTest extends PyTestCase { } public void testAlignForMethodArguments() { // PY-3995 - CodeStyleSettingsManager.getInstance().getSettings(myFixture.getProject()).ALIGN_MULTILINE_PARAMETERS_IN_CALLS = true; + settings().ALIGN_MULTILINE_PARAMETERS_IN_CALLS = true; doTest(); } @@ -152,6 +153,11 @@ public class PyFormatterTest extends PyTestCase { doTest(); } + public void testSetLiteralInArgList() { // PY-6672 + settings().ALIGN_MULTILINE_PARAMETERS_IN_CALLS = true; + doTest(); + } + public void testPsiFormatting() { // IDEA-69724 String initial = "def method_name(\n" + @@ -190,4 +196,8 @@ public class PyFormatterTest extends PyTestCase { }); myFixture.checkResultByFile("formatter/" + getTestName(true) + "_after.py"); } + + private CodeStyleSettings settings() { + return CodeStyleSettingsManager.getInstance().getSettings(myFixture.getProject()); + } } From 355e26ae794dc2c034413794038c4c49f0585f59 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 22 Aug 2012 12:31:55 +0200 Subject: [PATCH 03/12] correct alignment for nested lists in argument list; pending test for PY-6751 --- python/src/com/jetbrains/python/formatter/PyBlock.java | 9 ++++++++- python/testData/formatter/literals.py | 7 +++++++ python/testData/formatter/literals_after.py | 7 +++++++ python/testData/formatter/tupleInArgList.py | 4 ++++ python/testData/formatter/tupleInArgList_after.py | 4 ++++ python/testSrc/com/jetbrains/python/PyFormatterTest.java | 9 +++++++++ 6 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 python/testData/formatter/literals.py create mode 100644 python/testData/formatter/literals_after.py create mode 100644 python/testData/formatter/tupleInArgList.py create mode 100644 python/testData/formatter/tupleInArgList_after.py diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index b377df3fa66d..f247dd05c195 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -141,6 +141,7 @@ public class PyBlock implements ASTBlock { PythonDialectsTokenSetProvider.INSTANCE.getExpressionTokens().contains(childType)) { childAlignment = getAlignmentForChildren(); } + if (parentType == PyElementTypes.LIST_LITERAL_EXPRESSION) { if (childType == PyTokenTypes.RBRACKET || childType == PyTokenTypes.LBRACKET) { childIndent = Indent.getNoneIndent(); @@ -224,9 +225,15 @@ public class PyBlock implements ASTBlock { IElementType childType = child.getElementType(); ASTNode firstGrandchild = child.getFirstChildNode(); IElementType firstGrandchildType = firstGrandchild == null ? null : firstGrandchild.getElementType(); - if (PyTokenTypes.OPEN_BRACES.contains(childType) || PyTokenTypes.OPEN_BRACES.contains(firstGrandchildType)) { + if (PyTokenTypes.OPEN_BRACES.contains(childType)) { return false; } + if (PyTokenTypes.OPEN_BRACES.contains(firstGrandchildType)) { + PsiElement psi = child.getPsi(); + if (psi instanceof PySequenceExpression && ((PySequenceExpression)psi).getElements().length == 0) { + return false; + } + } if (PyTokenTypes.CLOSE_BRACES.contains(childType)) { ASTNode prevNonSpace = findPrevNonSpaceNode(child); if (prevNonSpace != null && prevNonSpace.getElementType() == PyTokenTypes.COMMA) { diff --git a/python/testData/formatter/literals.py b/python/testData/formatter/literals.py new file mode 100644 index 000000000000..614706162efc --- /dev/null +++ b/python/testData/formatter/literals.py @@ -0,0 +1,7 @@ +d = { + 'success': 'OK', + 'tag_count': 2, + 'tags': [ + {'name': 'tag1', 'uses': '1', 'display': '1', 'security': 'private'}, + ], + } \ No newline at end of file diff --git a/python/testData/formatter/literals_after.py b/python/testData/formatter/literals_after.py new file mode 100644 index 000000000000..614706162efc --- /dev/null +++ b/python/testData/formatter/literals_after.py @@ -0,0 +1,7 @@ +d = { + 'success': 'OK', + 'tag_count': 2, + 'tags': [ + {'name': 'tag1', 'uses': '1', 'display': '1', 'security': 'private'}, + ], + } \ No newline at end of file diff --git a/python/testData/formatter/tupleInArgList.py b/python/testData/formatter/tupleInArgList.py new file mode 100644 index 000000000000..15fb29f80f62 --- /dev/null +++ b/python/testData/formatter/tupleInArgList.py @@ -0,0 +1,4 @@ +urlpatterns = patterns('', + (r'^admin/', include(admin.site.urls)), + url(r'^', include('cms.urls')), +) diff --git a/python/testData/formatter/tupleInArgList_after.py b/python/testData/formatter/tupleInArgList_after.py new file mode 100644 index 000000000000..72dce723faab --- /dev/null +++ b/python/testData/formatter/tupleInArgList_after.py @@ -0,0 +1,4 @@ +urlpatterns = patterns('', + (r'^admin/', include(admin.site.urls)), + url(r'^', include('cms.urls')), + ) diff --git a/python/testSrc/com/jetbrains/python/PyFormatterTest.java b/python/testSrc/com/jetbrains/python/PyFormatterTest.java index 8940604722b0..02796c90f882 100644 --- a/python/testSrc/com/jetbrains/python/PyFormatterTest.java +++ b/python/testSrc/com/jetbrains/python/PyFormatterTest.java @@ -158,6 +158,15 @@ public class PyFormatterTest extends PyTestCase { doTest(); } + public void _testLiterals() { // PY-6751 + doTest(); + } + + public void testTupleInArgList() { + settings().ALIGN_MULTILINE_PARAMETERS_IN_CALLS = true; + doTest(); + } + public void testPsiFormatting() { // IDEA-69724 String initial = "def method_name(\n" + From 98b245e56509fe76c5c09d575b9291c200980afd Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 22 Aug 2012 13:33:26 +0200 Subject: [PATCH 04/12] fix regression in resolve: PyKeywordArgument is not a declaration --- .../python/codeInsight/dataflow/scope/impl/ScopeImpl.java | 2 +- python/testData/resolve/KWArg.py | 8 ++++++++ python/testSrc/com/jetbrains/python/PyResolveTest.java | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 python/testData/resolve/KWArg.py diff --git a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java index 3b48330d3443..648587eeb1b7 100644 --- a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java +++ b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java @@ -179,7 +179,7 @@ public class ScopeImpl implements Scope { @Override public void visitPyElement(PyElement node) { - if (node instanceof PsiNamedElement) { + if (node instanceof PsiNamedElement && !(node instanceof PyKeywordArgument)) { namedElements.put(node.getName(), (PsiNamedElement)node); } // TODO: Cython-specific code diff --git a/python/testData/resolve/KWArg.py b/python/testData/resolve/KWArg.py new file mode 100644 index 000000000000..aaa73ba482dc --- /dev/null +++ b/python/testData/resolve/KWArg.py @@ -0,0 +1,8 @@ +from datetime import timedelta + +def foo(**kwargs): + pass + +def bar(): + foo(timedelta=timedelta) +# diff --git a/python/testSrc/com/jetbrains/python/PyResolveTest.java b/python/testSrc/com/jetbrains/python/PyResolveTest.java index ae4a4eb87245..3991c0d911c0 100644 --- a/python/testSrc/com/jetbrains/python/PyResolveTest.java +++ b/python/testSrc/com/jetbrains/python/PyResolveTest.java @@ -472,4 +472,8 @@ public class PyResolveTest extends PyResolveTestCase { public void testClassRedefinedField() { assertResolvesTo(PyClass.class, "Foo"); } + + public void testKWArg() { + assertResolvesTo(PyClass.class, "timedelta"); + } } From 5f15d990dc1bdfe9a2508ec8818a482ae204b444 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 22 Aug 2012 13:58:42 +0200 Subject: [PATCH 05/12] put the data which are shared between all PyBlocks in a formatting model into a single PyBlockContext class --- .../jetbrains/python/formatter/PyBlock.java | 22 ++++++------- .../python/formatter/PyBlockContext.java | 32 +++++++++++++++++++ .../PythonFormattingModelBuilder.java | 17 +++++++--- 3 files changed, 53 insertions(+), 18 deletions(-) create mode 100644 python/src/com/jetbrains/python/formatter/PyBlockContext.java diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index f247dd05c195..81b62b0067f8 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -5,7 +5,6 @@ import com.intellij.lang.ASTNode; import com.intellij.openapi.editor.Document; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; -import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.intellij.psi.impl.source.tree.TreeUtil; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; @@ -33,8 +32,7 @@ public class PyBlock implements ASTBlock { private final Indent _indent; private final ASTNode _node; private final Wrap _wrap; - private final CommonCodeStyleSettings mySettings; - private final SpacingBuilder mySpacingBuilder; + private final PyBlockContext myContext; private List _subBlocks = null; private Alignment myChildAlignment; private static final boolean DUMP_FORMATTING_BLOCKS = false; @@ -60,14 +58,12 @@ public class PyBlock implements ASTBlock { final Alignment alignment, final Indent indent, final Wrap wrap, - final CommonCodeStyleSettings settings, - SpacingBuilder spacingBuilder) { + final PyBlockContext context) { _alignment = alignment; _indent = indent; _node = node; _wrap = wrap; - mySettings = settings; - mySpacingBuilder = spacingBuilder; + myContext = context; } @NotNull @@ -194,7 +190,7 @@ public class PyBlock implements ASTBlock { childIndent = Indent.getNormalIndent(); } - return new PyBlock(child, childAlignment, childIndent, wrap, mySettings, mySpacingBuilder); + return new PyBlock(child, childAlignment, childIndent, wrap, myContext); } private static boolean isEmptyList(PsiElement psi) { @@ -242,7 +238,7 @@ public class PyBlock implements ASTBlock { return false; } if (_node.getElementType() == PyElementTypes.ARGUMENT_LIST) { - if (!mySettings.ALIGN_MULTILINE_PARAMETERS_IN_CALLS) { + if (!myContext.getSettings().ALIGN_MULTILINE_PARAMETERS_IN_CALLS) { return false; } if (child.getElementType() == PyTokenTypes.COMMA) { @@ -257,7 +253,7 @@ public class PyBlock implements ASTBlock { return false; } if (_node.getElementType() == PyElementTypes.PARAMETER_LIST) { - return mySettings.ALIGN_MULTILINE_PARAMETERS; + return myContext.getSettings().ALIGN_MULTILINE_PARAMETERS; } if (child.getElementType() == PyTokenTypes.COMMA) { return false; @@ -326,7 +322,7 @@ public class PyBlock implements ASTBlock { @Nullable public Spacing getSpacing(Block child1, Block child2) { - return mySpacingBuilder.getSpacing(this, child1, child2); + return myContext.getSpacingBuilder().getSpacing(this, child1, child2); } @NotNull @@ -387,7 +383,7 @@ public class PyBlock implements ASTBlock { // correct indent manually. if (statementListsBelow > 0) { // was 1... strange @SuppressWarnings("ConstantConditions") - int indent = mySettings.getIndentOptions().INDENT_SIZE; + int indent = myContext.getSettings().getIndentOptions().INDENT_SIZE; return new ChildAttributes(Indent.getSpaceIndent(indent * statementListsBelow), null); } @@ -416,7 +412,7 @@ public class PyBlock implements ASTBlock { @Nullable private Alignment getChildAlignment() { if (ourListElementTypes.contains(_node.getElementType())) { - if (_node.getPsi() instanceof PyParameterList && !mySettings.ALIGN_MULTILINE_PARAMETERS) { + if (_node.getPsi() instanceof PyParameterList && !myContext.getSettings().ALIGN_MULTILINE_PARAMETERS) { return null; } if (_node.getPsi() instanceof PyDictLiteralExpression) { diff --git a/python/src/com/jetbrains/python/formatter/PyBlockContext.java b/python/src/com/jetbrains/python/formatter/PyBlockContext.java new file mode 100644 index 000000000000..6a5aa1f84838 --- /dev/null +++ b/python/src/com/jetbrains/python/formatter/PyBlockContext.java @@ -0,0 +1,32 @@ +package com.jetbrains.python.formatter; + +import com.intellij.formatting.FormattingMode; +import com.intellij.formatting.SpacingBuilder; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; + +/** + * @author yole + */ +public class PyBlockContext { + private final CommonCodeStyleSettings mySettings; + private final SpacingBuilder mySpacingBuilder; + private final FormattingMode myMode; + + public PyBlockContext(CommonCodeStyleSettings settings, SpacingBuilder builder, FormattingMode mode) { + mySettings = settings; + mySpacingBuilder = builder; + myMode = mode; + } + + public CommonCodeStyleSettings getSettings() { + return mySettings; + } + + public SpacingBuilder getSpacingBuilder() { + return mySpacingBuilder; + } + + public FormattingMode getMode() { + return myMode; + } +} diff --git a/python/src/com/jetbrains/python/formatter/PythonFormattingModelBuilder.java b/python/src/com/jetbrains/python/formatter/PythonFormattingModelBuilder.java index 2cff072e78ed..c5981bad1fe5 100644 --- a/python/src/com/jetbrains/python/formatter/PythonFormattingModelBuilder.java +++ b/python/src/com/jetbrains/python/formatter/PythonFormattingModelBuilder.java @@ -20,26 +20,33 @@ import static com.jetbrains.python.PyTokenTypes.*; /** * @author yole */ -public class PythonFormattingModelBuilder implements FormattingModelBuilder, CustomFormattingModelBuilder { +public class PythonFormattingModelBuilder implements FormattingModelBuilderEx, CustomFormattingModelBuilder { private static final boolean DUMP_FORMATTING_AST = false; @NotNull - public FormattingModel createModel(final PsiElement element, final CodeStyleSettings settings) { + @Override + public FormattingModel createModel(@NotNull PsiElement element, + @NotNull CodeStyleSettings settings, + @NotNull FormattingMode mode) { if (DUMP_FORMATTING_AST) { ASTNode fileNode = element.getContainingFile().getNode(); System.out.println("AST tree for " + element.getContainingFile().getName() + ":"); printAST(fileNode, 0); } final CommonCodeStyleSettings codeStyleSettings = settings.getCommonSettings(PythonLanguage.getInstance()); - final PyBlock block = - new PyBlock(element.getNode(), null, Indent.getNoneIndent(), null, codeStyleSettings, - createSpacingBuilder(settings)); + final PyBlockContext context = new PyBlockContext(codeStyleSettings, createSpacingBuilder(settings), mode); + final PyBlock block = new PyBlock(element.getNode(), null, Indent.getNoneIndent(), null, context); if (DUMP_FORMATTING_AST) { FormattingModelDumper.dumpFormattingModel(block, 2, System.out); } return FormattingModelProvider.createFormattingModelForPsiFile(element.getContainingFile(), block, settings); } + @NotNull + public FormattingModel createModel(final PsiElement element, final CodeStyleSettings settings) { + return createModel(element, settings, FormattingMode.REFORMAT); + } + protected SpacingBuilder createSpacingBuilder(CodeStyleSettings settings) { final IFileElementType file = LanguageParserDefinitions.INSTANCE.forLanguage(PythonLanguage.getInstance()).getFileNodeType(); final PyCodeStyleSettings pySettings = settings.getCustomSettings(PyCodeStyleSettings.class); From 7a117f7a0bfeff60e5c8d0a39ab0860761727544 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 22 Aug 2012 14:13:16 +0200 Subject: [PATCH 06/12] indent brace after comma only in adjust line indent mode (PY-6751) --- python/src/com/jetbrains/python/formatter/PyBlock.java | 2 +- python/testData/formatter/literals_after.py | 4 ++-- python/testSrc/com/jetbrains/python/PyFormatterTest.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index 81b62b0067f8..75dd6bf3a95f 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -232,7 +232,7 @@ public class PyBlock implements ASTBlock { } if (PyTokenTypes.CLOSE_BRACES.contains(childType)) { ASTNode prevNonSpace = findPrevNonSpaceNode(child); - if (prevNonSpace != null && prevNonSpace.getElementType() == PyTokenTypes.COMMA) { + if (prevNonSpace != null && prevNonSpace.getElementType() == PyTokenTypes.COMMA && myContext.getMode() == FormattingMode.ADJUST_INDENT) { return true; } return false; diff --git a/python/testData/formatter/literals_after.py b/python/testData/formatter/literals_after.py index 614706162efc..11efea1fbeee 100644 --- a/python/testData/formatter/literals_after.py +++ b/python/testData/formatter/literals_after.py @@ -3,5 +3,5 @@ d = { 'tag_count': 2, 'tags': [ {'name': 'tag1', 'uses': '1', 'display': '1', 'security': 'private'}, - ], - } \ No newline at end of file + ], +} \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyFormatterTest.java b/python/testSrc/com/jetbrains/python/PyFormatterTest.java index 02796c90f882..9b49b08d2a50 100644 --- a/python/testSrc/com/jetbrains/python/PyFormatterTest.java +++ b/python/testSrc/com/jetbrains/python/PyFormatterTest.java @@ -158,7 +158,7 @@ public class PyFormatterTest extends PyTestCase { doTest(); } - public void _testLiterals() { // PY-6751 + public void testLiterals() { // PY-6751 doTest(); } From 2f5c1b17db0ea5ee3a151fffb7eb0353478ca684 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 22 Aug 2012 15:35:18 +0200 Subject: [PATCH 07/12] dedicated interface for type callbacks of instructions --- .../controlflow/InstructionTypeCallback.java | 13 ++++++++ .../controlflow/PyTypeAssertionEvaluator.java | 32 ++++++++++--------- .../controlflow/ReadWriteInstruction.java | 15 ++++----- .../psi/impl/PyReferenceExpressionImpl.java | 4 +-- 4 files changed, 39 insertions(+), 25 deletions(-) create mode 100644 python/src/com/jetbrains/python/codeInsight/controlflow/InstructionTypeCallback.java diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/InstructionTypeCallback.java b/python/src/com/jetbrains/python/codeInsight/controlflow/InstructionTypeCallback.java new file mode 100644 index 000000000000..d65637dcb2d8 --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/InstructionTypeCallback.java @@ -0,0 +1,13 @@ +package com.jetbrains.python.codeInsight.controlflow; + +import com.intellij.psi.PsiElement; +import com.jetbrains.python.psi.types.PyType; +import com.jetbrains.python.psi.types.TypeEvalContext; +import org.jetbrains.annotations.Nullable; + +/** + * @author yole + */ +public interface InstructionTypeCallback { + PyType getType(TypeEvalContext context, @Nullable PsiElement anchor); +} diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java b/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java index 4bcc297b6ec3..fd357d4335eb 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java @@ -1,11 +1,12 @@ package com.jetbrains.python.codeInsight.controlflow; -import com.intellij.util.Function; +import com.intellij.psi.PsiElement; import com.intellij.util.containers.CollectionFactory; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.types.*; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -43,9 +44,9 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor { final PyExpression typeElement = args[1]; final boolean positive = myPositive; if (!processTuple(target, typeElement)) { - pushAssertion(target, new Function() { + pushAssertion(target, new InstructionTypeCallback() { @Override - public PyType fun(TypeEvalContext context) { + public PyType getType(TypeEvalContext context, PsiElement anchor) { final List types = new ArrayList(); types.add(context.getType(typeElement)); return createAssertionType(context.getType(target), types, positive, context); @@ -59,9 +60,9 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor { if (args.length == 1 && args[0] instanceof PyReferenceExpression) { final PyReferenceExpression target = (PyReferenceExpression)args[0]; final boolean positive = myPositive; - pushAssertion(target, new Function() { + pushAssertion(target, new InstructionTypeCallback() { @Override - public PyType fun(TypeEvalContext context) { + public PyType getType(TypeEvalContext context, PsiElement anchor) { final List types = new ArrayList(); types.add(PyTypeParser.getTypeByName(target, PyNames.CALLABLE)); return createAssertionType(context.getType(target), types, positive, context); @@ -74,9 +75,9 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor { @Override public void visitPyReferenceExpression(final PyReferenceExpression node) { if (node.getParent() instanceof PyIfPart) { - pushAssertion(node, new Function() { + pushAssertion(node, new InstructionTypeCallback() { @Override - public PyType fun(TypeEvalContext context) { + public PyType getType(TypeEvalContext context, PsiElement anchor) { final List types = new ArrayList(); types.add(PyNoneType.INSTANCE); return createAssertionType(context.getType(node), types, false, context); @@ -95,9 +96,9 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor { if (lhs instanceof PyReferenceExpression && rhs instanceof PyReferenceExpression) { final PyReferenceExpression target = (PyReferenceExpression)lhs; if (PyNames.NONE.equals(rhs.getName())) { - pushAssertion(target, new Function() { + pushAssertion(target, new InstructionTypeCallback() { @Override - public PyType fun(TypeEvalContext context) { + public PyType getType(TypeEvalContext context, @Nullable PsiElement anchor) { final List types = new ArrayList(); types.add(PyNoneType.INSTANCE); return createAssertionType(context.getType(target), types, false, context); @@ -117,9 +118,9 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor { if (contained instanceof PyTupleExpression) { final PyTupleExpression tuple = (PyTupleExpression)contained; final boolean positive = myPositive; - pushAssertion(target, new Function() { + pushAssertion(target, new InstructionTypeCallback() { @Override - public PyType fun(TypeEvalContext context) { + public PyType getType(TypeEvalContext context, @Nullable PsiElement anchor) { final List types = new ArrayList(); for (PyExpression e : tuple.getElements()) { types.add(context.getType(e)); @@ -133,6 +134,7 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor { return pushed; } + @Nullable private static PyType createAssertionType(PyType initial, List types, boolean positive, TypeEvalContext context) { final List members = new ArrayList(); for (PyType t : types) { @@ -151,15 +153,15 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor { return initial; } - private void pushAssertion(PyReferenceExpression element, Function getType) { + private void pushAssertion(PyReferenceExpression element, InstructionTypeCallback getType) { myStack.push(new Assertion(element, getType)); } static class Assertion { private final PyReferenceExpression element; - private Function myFunction; + private InstructionTypeCallback myFunction; - Assertion(PyReferenceExpression element, Function getType) { + Assertion(PyReferenceExpression element, InstructionTypeCallback getType) { this.element = element; this.myFunction = getType; } @@ -168,7 +170,7 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor { return element; } - public Function getTypeEvalFunction() { + public InstructionTypeCallback getTypeEvalFunction() { return myFunction; } } diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/ReadWriteInstruction.java b/python/src/com/jetbrains/python/codeInsight/controlflow/ReadWriteInstruction.java index cbc25a83821a..a936acb73425 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/ReadWriteInstruction.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/ReadWriteInstruction.java @@ -3,7 +3,6 @@ package com.jetbrains.python.codeInsight.controlflow; import com.intellij.codeInsight.controlflow.ControlFlowBuilder; import com.intellij.codeInsight.controlflow.impl.InstructionImpl; import com.intellij.psi.PsiElement; -import com.intellij.util.Function; import com.jetbrains.python.psi.PyElement; import com.jetbrains.python.psi.PyExpression; import com.jetbrains.python.psi.types.PyType; @@ -12,9 +11,9 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; public class ReadWriteInstruction extends InstructionImpl { - final Function EXPR_TYPE = new Function() { + final InstructionTypeCallback EXPR_TYPE = new InstructionTypeCallback() { @Override - public PyType fun(TypeEvalContext context) { + public PyType getType(TypeEvalContext context, @Nullable PsiElement anchor) { return myElement instanceof PyExpression ? context.getType((PyExpression)myElement) : null; } }; @@ -50,7 +49,7 @@ public class ReadWriteInstruction extends InstructionImpl { private final String myName; private final ACCESS myAccess; - private final Function myGetType; + private final InstructionTypeCallback myGetType; private ReadWriteInstruction(final ControlFlowBuilder builder, final PsiElement element, @@ -63,7 +62,7 @@ public class ReadWriteInstruction extends InstructionImpl { final PsiElement element, final String name, final ACCESS access, - @Nullable final Function getType) { + @Nullable final InstructionTypeCallback getType) { super(builder, element); myName = name; myAccess = access; @@ -100,13 +99,13 @@ public class ReadWriteInstruction extends InstructionImpl { public static ReadWriteInstruction assertType(final ControlFlowBuilder builder, final PsiElement element, final String name, - final Function getType) { + final InstructionTypeCallback getType) { return new ReadWriteInstruction(builder, element, name, ACCESS.ASSERTTYPE, getType); } @Nullable - public PyType getType(TypeEvalContext context) { - return myGetType.fun(context); + public PyType getType(TypeEvalContext context, @Nullable PsiElement anchor) { + return myGetType.getType(context, anchor); } @NonNls diff --git a/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java index 6da658fb4a36..36a04c0471a9 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java @@ -310,9 +310,9 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere augAssignment != null ? augAssignment : anchor, true); if (!defs.isEmpty()) { - PyType type = defs.get(0).getType(context); + PyType type = defs.get(0).getType(context, anchor); for (int i = 1; i < defs.size(); i++) { - type = PyUnionType.union(type, defs.get(i).getType(context)); + type = PyUnionType.union(type, defs.get(i).getType(context, anchor)); } return type; } From cda3a7076540b7dfb5d795f7bb9508e03ca15370 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 22 Aug 2012 15:40:11 +0200 Subject: [PATCH 08/12] testdata --- python/testData/formatter/tupleInArgList_after.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/testData/formatter/tupleInArgList_after.py b/python/testData/formatter/tupleInArgList_after.py index 72dce723faab..ff31f6bd51cc 100644 --- a/python/testData/formatter/tupleInArgList_after.py +++ b/python/testData/formatter/tupleInArgList_after.py @@ -1,4 +1,4 @@ urlpatterns = patterns('', (r'^admin/', include(admin.site.urls)), url(r'^', include('cms.urls')), - ) +) From 3490280116e18b06d81ddcc68836ec375faad50c Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 22 Aug 2012 15:42:48 +0200 Subject: [PATCH 09/12] a PyTargetExpression will itself call PyTypeProvider.getReferenceType() when asked for its type (PY-7270) --- .../controlflow/InstructionTypeCallback.java | 1 + .../controlflow/ReadWriteInstruction.java | 12 +++++++++++- .../python/psi/impl/PyReferenceExpressionImpl.java | 14 +++++++++----- .../python/psi/impl/PyTargetExpressionImpl.java | 9 +++++++++ 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/InstructionTypeCallback.java b/python/src/com/jetbrains/python/codeInsight/controlflow/InstructionTypeCallback.java index d65637dcb2d8..36c4cfd689fb 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/InstructionTypeCallback.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/InstructionTypeCallback.java @@ -9,5 +9,6 @@ import org.jetbrains.annotations.Nullable; * @author yole */ public interface InstructionTypeCallback { + @Nullable PyType getType(TypeEvalContext context, @Nullable PsiElement anchor); } diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/ReadWriteInstruction.java b/python/src/com/jetbrains/python/codeInsight/controlflow/ReadWriteInstruction.java index a936acb73425..57f1d64f3593 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/ReadWriteInstruction.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/ReadWriteInstruction.java @@ -5,6 +5,7 @@ import com.intellij.codeInsight.controlflow.impl.InstructionImpl; import com.intellij.psi.PsiElement; import com.jetbrains.python.psi.PyElement; import com.jetbrains.python.psi.PyExpression; +import com.jetbrains.python.psi.impl.PyTargetExpressionImpl; import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NonNls; @@ -12,9 +13,18 @@ import org.jetbrains.annotations.Nullable; public class ReadWriteInstruction extends InstructionImpl { final InstructionTypeCallback EXPR_TYPE = new InstructionTypeCallback() { + @Nullable @Override public PyType getType(TypeEvalContext context, @Nullable PsiElement anchor) { - return myElement instanceof PyExpression ? context.getType((PyExpression)myElement) : null; + if (myElement instanceof PyTargetExpressionImpl) { + return ((PyTargetExpressionImpl) myElement).getTypeWithAnchor(context, anchor); + } + if (myElement instanceof PyExpression) { + return context.getType((PyExpression)myElement); + } + else { + return null; + } } }; diff --git a/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java index 36a04c0471a9..e1b56721082c 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java @@ -281,9 +281,11 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere public static PyType getTypeFromTarget(@NotNull final PsiElement target, final TypeEvalContext context, PyReferenceExpression anchor) { - final PyType pyType = getReferenceTypeFromProviders(target, context, anchor); - if (pyType != null) { - return pyType; + if (!(target instanceof PyTargetExpression)) { // PyTargetExpression will ask about its type itself + final PyType pyType = getReferenceTypeFromProviders(target, context, anchor); + if (pyType != null) { + return pyType; + } } if (target instanceof PyTargetExpression) { final String name = ((PyTargetExpression)target).getName(); @@ -329,7 +331,7 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere if (propertyDecorator != null) { return PyBuiltinCache.getInstance(target).getObjectType(PyNames.PROPERTY); } - for (PyDecorator decorator: decoratorList.getDecorators()) { + for (PyDecorator decorator : decoratorList.getDecorators()) { final PyQualifiedName qName = decorator.getQualifiedName(); if (qName != null && (qName.endsWith(PyNames.SETTER) || qName.endsWith(PyNames.DELETER))) { return PyBuiltinCache.getInstance(target).getObjectType(PyNames.PROPERTY); @@ -342,7 +344,9 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere } if (target instanceof PsiDirectory) { PsiFile file = ((PsiDirectory)target).findFile(PyNames.INIT_DOT_PY); - if (file != null) return getTypeFromTarget(file, context, anchor); + if (file != null) { + return getTypeFromTarget(file, context, anchor); + } } return null; } diff --git a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java index cb39aea1dd47..eafcb179fd1f 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java @@ -98,6 +98,11 @@ public class PyTargetExpressionImpl extends PyPresentableElementImpl Date: Wed, 22 Aug 2012 16:24:04 +0200 Subject: [PATCH 10/12] correctly check if we need to insert a backslash when wrapping (PY-7039) --- .../RemoveUnnecessaryBackslashQuickFix.java | 6 ++-- .../python/editor/PythonEnterHandler.java | 28 +++++++++++-------- .../com/jetbrains/python/PyEditingTest.java | 6 ++++ 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/python/src/com/jetbrains/python/actions/RemoveUnnecessaryBackslashQuickFix.java b/python/src/com/jetbrains/python/actions/RemoveUnnecessaryBackslashQuickFix.java index 813e070e3fdf..f2cf57da1883 100644 --- a/python/src/com/jetbrains/python/actions/RemoveUnnecessaryBackslashQuickFix.java +++ b/python/src/com/jetbrains/python/actions/RemoveUnnecessaryBackslashQuickFix.java @@ -8,6 +8,7 @@ import com.intellij.psi.PsiWhiteSpace; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.Stack; import com.jetbrains.python.PyBundle; +import com.jetbrains.python.editor.PythonEnterHandler; import com.jetbrains.python.psi.*; import org.jetbrains.annotations.NotNull; @@ -30,13 +31,12 @@ public class RemoveUnnecessaryBackslashQuickFix implements LocalQuickFix { public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { PsiElement problemElement = descriptor.getPsiElement(); if (problemElement != null) { - PyElement parent = PsiTreeUtil.getParentOfType(problemElement, PySequenceExpression.class, PyDictLiteralExpression.class, - PyParenthesizedExpression.class, PyArgumentList.class, PyParameterList.class); + PsiElement parent = PsiTreeUtil.getParentOfType(problemElement, PythonEnterHandler.IMPLICIT_WRAP_CLASSES); removeBackSlash(parent); } } - private static void removeBackSlash(PyElement parent) { + private static void removeBackSlash(PsiElement parent) { if (parent != null) { Stack stack = new Stack(); if (parent instanceof PyParenthesizedExpression) diff --git a/python/src/com/jetbrains/python/editor/PythonEnterHandler.java b/python/src/com/jetbrains/python/editor/PythonEnterHandler.java index 424218071db3..4521a6d0b31e 100644 --- a/python/src/com/jetbrains/python/editor/PythonEnterHandler.java +++ b/python/src/com/jetbrains/python/editor/PythonEnterHandler.java @@ -29,7 +29,15 @@ import org.jetbrains.annotations.Nullable; public class PythonEnterHandler extends EnterHandlerDelegateAdapter { private boolean needPostProcess = false; - private static final Class[] IMPLICIT_WRAP_CLASSES = new Class[]{ + public static final Class[] IMPLICIT_WRAP_CLASSES = new Class[] { + PySequenceExpression.class, + PyDictLiteralExpression.class, + PyParenthesizedExpression.class, + PyArgumentList.class, + PyParameterList.class + }; + + private static final Class[] WRAPPABLE_CLASSES = new Class[]{ PsiComment.class, PyParenthesizedExpression.class, PyListCompExpression.class, @@ -116,13 +124,11 @@ public class PythonEnterHandler extends EnterHandlerDelegateAdapter { final boolean isEscapedBackslash = "\\".equals(doc.getText(TextRange.create(offset-2, offset - 1))) && nextIsBackslash; if (nextIsBackslash && !isEscapedQuote && !isEscapedBackslash) return Result.Continue; - final PsiElement parent = string.getParent(); final StringBuilder replacementString = new StringBuilder(); needPostProcess = true; - if (parent instanceof PySequenceExpression || parent instanceof PyParenthesizedExpression || - parent instanceof PyBinaryExpression || parent instanceof PyKeyValueExpression || - parent instanceof PyNamedParameter || parent instanceof PyArgumentList) { - replacementString.append(quote + pref + quote); + + if (PsiTreeUtil.getParentOfType(string, IMPLICIT_WRAP_CLASSES) != null) { + replacementString.append(quote).append(pref).append(quote); doc.insertString(offset, replacementString); caretOffset.set(caretOffset.get() + 1); return Result.Continue; @@ -132,7 +138,7 @@ public class PythonEnterHandler extends EnterHandlerDelegateAdapter { replacementString.append(quote); caretOffset.set(caretOffset.get() + 1); } - replacementString.append(quote + " \\" + pref); + replacementString.append(quote).append(" \\").append(pref); if (!isEscapedQuote) replacementString.append(quote); doc.insertString(offset, replacementString.toString()); @@ -187,7 +193,7 @@ public class PythonEnterHandler extends EnterHandlerDelegateAdapter { PsiElement wrappableAfter = findWrappable(file, offset, false); if (!(wrappableBefore instanceof PsiComment)) { while (wrappableBefore != null) { - PsiElement next = PsiTreeUtil.getParentOfType(wrappableBefore, IMPLICIT_WRAP_CLASSES); + PsiElement next = PsiTreeUtil.getParentOfType(wrappableBefore, WRAPPABLE_CLASSES); if (next == null) { break; } @@ -196,7 +202,7 @@ public class PythonEnterHandler extends EnterHandlerDelegateAdapter { } if (!(wrappableAfter instanceof PsiComment)) { while (wrappableAfter != null) { - PsiElement next = PsiTreeUtil.getParentOfType(wrappableAfter, IMPLICIT_WRAP_CLASSES); + PsiElement next = PsiTreeUtil.getParentOfType(wrappableAfter, WRAPPABLE_CLASSES); if (next == null) { break; } @@ -234,8 +240,8 @@ public class PythonEnterHandler extends EnterHandlerDelegateAdapter { @Nullable private static PsiElement findWrappable(PsiFile file, int offset, boolean before) { PsiElement wrappable = before - ? findBeforeCaret(file, offset, IMPLICIT_WRAP_CLASSES) - : findAfterCaret(file, offset, IMPLICIT_WRAP_CLASSES); + ? findBeforeCaret(file, offset, WRAPPABLE_CLASSES) + : findAfterCaret(file, offset, WRAPPABLE_CLASSES); if (wrappable == null) { PsiElement emptyTuple = before ? findBeforeCaret(file, offset, PyTupleExpression.class) diff --git a/python/testSrc/com/jetbrains/python/PyEditingTest.java b/python/testSrc/com/jetbrains/python/PyEditingTest.java index 716a6ce1f71d..23403a3e55b9 100644 --- a/python/testSrc/com/jetbrains/python/PyEditingTest.java +++ b/python/testSrc/com/jetbrains/python/PyEditingTest.java @@ -111,6 +111,12 @@ public class PyEditingTest extends PyTestCase { " r'string'"); } + public void testEnterInStringFormatting() { // PY-7039 + doTestEnter("foo += \"foobar\" % foo\n", + "foo += \"fooba\" \\\n" + + " \"r\" % foo\n"); + } + public void testEnterInStatement() { doTestEnter("if a and b: pass", "if a \\\nand b: pass"); } From 21d6cb7512d5b0ed6d7adbc3ebd5fffe7058fc6d Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 22 Aug 2012 16:47:32 +0200 Subject: [PATCH 11/12] don't wrap before comma --- python/src/com/jetbrains/python/formatter/PyBlock.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index 75dd6bf3a95f..ba52b5aa8e00 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -126,7 +126,7 @@ public class PyBlock implements ASTBlock { if (ourListElementTypes.contains(parentType)) { // wrapping in non-parenthesized tuple expression is not allowed (PY-1792) if ((parentType != PyElementTypes.TUPLE_EXPRESSION || grandparentType == PyElementTypes.PARENTHESIZED_EXPRESSION) && - !ourBrackets.contains(childType)) { + !ourBrackets.contains(childType) && childType != PyTokenTypes.COMMA) { wrap = Wrap.createWrap(WrapType.NORMAL, true); } if (needListAlignment(child) && !isEmptyList(_node.getPsi())) { From 608ebd0168a9421038cd1bc3932ef6983149c8dc Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 22 Aug 2012 17:05:16 +0200 Subject: [PATCH 12/12] better alignment in nested binary expressions (PY-5710) --- .../com/jetbrains/python/formatter/PyBlock.java | 16 ++++++++++++---- .../formatter/PythonFormattingModelBuilder.java | 2 +- .../formatter/alignInBinaryExpressions.py | 3 +++ .../formatter/alignInBinaryExpressions_after.py | 3 +++ .../com/jetbrains/python/PyFormatterTest.java | 4 ++++ 5 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 python/testData/formatter/alignInBinaryExpressions.py create mode 100644 python/testData/formatter/alignInBinaryExpressions_after.py diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index ba52b5aa8e00..89dbfb394f9e 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -28,6 +28,7 @@ import static com.jetbrains.python.psi.PyUtil.sure; * @author yole */ public class PyBlock implements ASTBlock { + private final PyBlock myParent; private final Alignment _alignment; private final Indent _indent; private final ASTNode _node; @@ -54,11 +55,13 @@ public class PyBlock implements ASTBlock { PyTokenTypes.LBRACE, PyTokenTypes.RBRACE, PyTokenTypes.LBRACKET, PyTokenTypes.RBRACKET); - public PyBlock(final ASTNode node, + public PyBlock(final PyBlock parent, + final ASTNode node, final Alignment alignment, final Indent indent, final Wrap wrap, final PyBlockContext context) { + myParent = parent; _alignment = alignment; _indent = indent; _node = node; @@ -134,8 +137,13 @@ public class PyBlock implements ASTBlock { } } else if (parentType == PyElementTypes.BINARY_EXPRESSION && - PythonDialectsTokenSetProvider.INSTANCE.getExpressionTokens().contains(childType)) { - childAlignment = getAlignmentForChildren(); + (PythonDialectsTokenSetProvider.INSTANCE.getExpressionTokens().contains(childType) || PyTokenTypes.OPERATIONS.contains(childType))) { + if (grandparentType == PyElementTypes.BINARY_EXPRESSION && myParent != null) { + childAlignment = myParent.getAlignmentForChildren(); + } + else { + childAlignment = getAlignmentForChildren(); + } } if (parentType == PyElementTypes.LIST_LITERAL_EXPRESSION) { @@ -190,7 +198,7 @@ public class PyBlock implements ASTBlock { childIndent = Indent.getNormalIndent(); } - return new PyBlock(child, childAlignment, childIndent, wrap, myContext); + return new PyBlock(this, child, childAlignment, childIndent, wrap, myContext); } private static boolean isEmptyList(PsiElement psi) { diff --git a/python/src/com/jetbrains/python/formatter/PythonFormattingModelBuilder.java b/python/src/com/jetbrains/python/formatter/PythonFormattingModelBuilder.java index c5981bad1fe5..24a2156dd216 100644 --- a/python/src/com/jetbrains/python/formatter/PythonFormattingModelBuilder.java +++ b/python/src/com/jetbrains/python/formatter/PythonFormattingModelBuilder.java @@ -35,7 +35,7 @@ public class PythonFormattingModelBuilder implements FormattingModelBuilderEx, C } final CommonCodeStyleSettings codeStyleSettings = settings.getCommonSettings(PythonLanguage.getInstance()); final PyBlockContext context = new PyBlockContext(codeStyleSettings, createSpacingBuilder(settings), mode); - final PyBlock block = new PyBlock(element.getNode(), null, Indent.getNoneIndent(), null, context); + final PyBlock block = new PyBlock(null, element.getNode(), null, Indent.getNoneIndent(), null, context); if (DUMP_FORMATTING_AST) { FormattingModelDumper.dumpFormattingModel(block, 2, System.out); } diff --git a/python/testData/formatter/alignInBinaryExpressions.py b/python/testData/formatter/alignInBinaryExpressions.py new file mode 100644 index 000000000000..457cd4688350 --- /dev/null +++ b/python/testData/formatter/alignInBinaryExpressions.py @@ -0,0 +1,3 @@ +term1 = BNF.lpar + expr + BNF.rpar\ +| if_expr\ +| numeric_value diff --git a/python/testData/formatter/alignInBinaryExpressions_after.py b/python/testData/formatter/alignInBinaryExpressions_after.py new file mode 100644 index 000000000000..086d6282b8e4 --- /dev/null +++ b/python/testData/formatter/alignInBinaryExpressions_after.py @@ -0,0 +1,3 @@ +term1 = BNF.lpar + expr + BNF.rpar\ + | if_expr\ + | numeric_value diff --git a/python/testSrc/com/jetbrains/python/PyFormatterTest.java b/python/testSrc/com/jetbrains/python/PyFormatterTest.java index 9b49b08d2a50..324b7d666b82 100644 --- a/python/testSrc/com/jetbrains/python/PyFormatterTest.java +++ b/python/testSrc/com/jetbrains/python/PyFormatterTest.java @@ -167,6 +167,10 @@ public class PyFormatterTest extends PyTestCase { doTest(); } + public void testAlignInBinaryExpressions() { + doTest(); + } + public void testPsiFormatting() { // IDEA-69724 String initial = "def method_name(\n" +