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/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/codeInsight/controlflow/InstructionTypeCallback.java b/python/src/com/jetbrains/python/codeInsight/controlflow/InstructionTypeCallback.java new file mode 100644 index 000000000000..36c4cfd689fb --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/InstructionTypeCallback.java @@ -0,0 +1,14 @@ +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 { + @Nullable + 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..57f1d64f3593 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/ReadWriteInstruction.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/ReadWriteInstruction.java @@ -3,19 +3,28 @@ 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.impl.PyTargetExpressionImpl; import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.psi.types.TypeEvalContext; 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() { + @Nullable @Override - public PyType fun(TypeEvalContext context) { - return myElement instanceof PyExpression ? context.getType((PyExpression)myElement) : null; + public PyType getType(TypeEvalContext context, @Nullable PsiElement anchor) { + if (myElement instanceof PyTargetExpressionImpl) { + return ((PyTargetExpressionImpl) myElement).getTypeWithAnchor(context, anchor); + } + if (myElement instanceof PyExpression) { + return context.getType((PyExpression)myElement); + } + else { + return null; + } } }; @@ -50,7 +59,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 +72,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 +109,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/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/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/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index dc6956d7691f..89dbfb394f9e 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; @@ -29,12 +28,12 @@ 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; 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; @@ -56,18 +55,18 @@ 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 CommonCodeStyleSettings settings, - SpacingBuilder spacingBuilder) { + final PyBlockContext context) { + myParent = parent; _alignment = alignment; _indent = indent; _node = node; _wrap = wrap; - mySettings = settings; - mySpacingBuilder = spacingBuilder; + myContext = context; } @NotNull @@ -130,7 +129,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())) { @@ -138,9 +137,15 @@ 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) { if (childType == PyTokenTypes.RBRACKET || childType == PyTokenTypes.LBRACKET) { childIndent = Indent.getNoneIndent(); @@ -160,7 +165,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 { @@ -193,7 +198,7 @@ public class PyBlock implements ASTBlock { childIndent = Indent.getNormalIndent(); } - return new PyBlock(child, childAlignment, childIndent, wrap, mySettings, mySpacingBuilder); + return new PyBlock(this, child, childAlignment, childIndent, wrap, myContext); } private static boolean isEmptyList(PsiElement psi) { @@ -224,29 +229,39 @@ 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) { + if (prevNonSpace != null && prevNonSpace.getElementType() == PyTokenTypes.COMMA && myContext.getMode() == FormattingMode.ADJUST_INDENT) { return true; } 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) { 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; } if (_node.getElementType() == PyElementTypes.PARAMETER_LIST) { - return mySettings.ALIGN_MULTILINE_PARAMETERS; + return myContext.getSettings().ALIGN_MULTILINE_PARAMETERS; } if (child.getElementType() == PyTokenTypes.COMMA) { return false; @@ -315,7 +330,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 @@ -376,7 +391,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); } @@ -405,7 +420,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..24a2156dd216 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(null, 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); diff --git a/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java index 6da658fb4a36..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(); @@ -310,9 +312,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; } @@ -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 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"); } diff --git a/python/testSrc/com/jetbrains/python/PyFormatterTest.java b/python/testSrc/com/jetbrains/python/PyFormatterTest.java index d579e7cc7693..324b7d666b82 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,24 @@ public class PyFormatterTest extends PyTestCase { doTest(); } + public void testSetLiteralInArgList() { // PY-6672 + settings().ALIGN_MULTILINE_PARAMETERS_IN_CALLS = true; + doTest(); + } + + public void testLiterals() { // PY-6751 + doTest(); + } + + public void testTupleInArgList() { + settings().ALIGN_MULTILINE_PARAMETERS_IN_CALLS = true; + doTest(); + } + + public void testAlignInBinaryExpressions() { + doTest(); + } + public void testPsiFormatting() { // IDEA-69724 String initial = "def method_name(\n" + @@ -190,4 +209,8 @@ public class PyFormatterTest extends PyTestCase { }); myFixture.checkResultByFile("formatter/" + getTestName(true) + "_after.py"); } + + private CodeStyleSettings settings() { + return CodeStyleSettingsManager.getInstance().getSettings(myFixture.getProject()); + } } 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"); + } }