diff --git a/python/build/pycharm.exe4j b/python/build/pycharm.exe4j index b1e4eed185de..c44b2d0b3375 100644 --- a/python/build/pycharm.exe4j +++ b/python/build/pycharm.exe4j @@ -55,7 +55,9 @@ - + + + diff --git a/python/src/com/jetbrains/python/PythonBundledColorSchemeProvider.java b/python/src/com/jetbrains/python/PythonBundledColorSchemeProvider.java index 7b78adb700f3..728258a70c93 100644 --- a/python/src/com/jetbrains/python/PythonBundledColorSchemeProvider.java +++ b/python/src/com/jetbrains/python/PythonBundledColorSchemeProvider.java @@ -13,6 +13,8 @@ public class PythonBundledColorSchemeProvider implements BundledColorSchemesProv public static final String[] PATHS = { "/colorSchemes/WarmNeon", + "/colorSchemes/monokai", + "/colorSchemes/twilight", }; @Override diff --git a/python/src/com/jetbrains/python/actions/AddIgnoredIdentifierFix.java b/python/src/com/jetbrains/python/actions/AddIgnoredIdentifierFix.java index 1eb3b7bfb2bb..53cec5ae626e 100644 --- a/python/src/com/jetbrains/python/actions/AddIgnoredIdentifierFix.java +++ b/python/src/com/jetbrains/python/actions/AddIgnoredIdentifierFix.java @@ -1,5 +1,6 @@ package com.jetbrains.python.actions; +import com.intellij.codeInsight.intention.LowPriorityAction; import com.intellij.codeInspection.InspectionProfile; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; @@ -12,7 +13,7 @@ import org.jetbrains.annotations.NotNull; /** * @author yole */ -public class AddIgnoredIdentifierFix implements LocalQuickFix { +public class AddIgnoredIdentifierFix implements LocalQuickFix, LowPriorityAction { private final String myIdentifier; public AddIgnoredIdentifierFix(String identifier) { diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/PyConvertLambdaToFunctionIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/PyConvertLambdaToFunctionIntention.java index 921b09624481..2797a11f39fe 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/PyConvertLambdaToFunctionIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/PyConvertLambdaToFunctionIntention.java @@ -59,8 +59,6 @@ public class PyConvertLambdaToFunctionIntention extends BaseIntentionAction { functionBuilder.statement("return " + body.getText()); PyFunction function = functionBuilder.buildFunction(project); - - PyFunction parentFunction = PsiTreeUtil.getTopmostParentOfType(lambdaExpression, PyFunction.class); if (parentFunction != null ) { PyClass parentClass = PsiTreeUtil.getTopmostParentOfType(parentFunction, PyClass.class); diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/PyDictLiteralFormToConstructorIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/PyDictLiteralFormToConstructorIntention.java index b34b8aa87fb5..492b5fc995f3 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/PyDictLiteralFormToConstructorIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/PyDictLiteralFormToConstructorIntention.java @@ -41,9 +41,9 @@ public class PyDictLiteralFormToConstructorIntention extends BaseIntentionAction if (elements.length != 0) { for (PyKeyValueExpression element : elements) { PyExpression key = element.getKey(); - if (! (key instanceof PyStringLiteralExpression)) canConvert = false; + if (! (key instanceof PyStringLiteralExpression)) return false; String str = ((PyStringLiteralExpression)key).getStringValue(); - if(Character.isDigit(str.charAt(0))) canConvert = false; + if(Character.isDigit(str.charAt(0))) return false; try { Integer.parseInt(str) ; canConvert = false; diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/PyJoinIfIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/PyJoinIfIntention.java index 545c84d98621..0bf11d123288 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/PyJoinIfIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/PyJoinIfIntention.java @@ -3,6 +3,8 @@ package com.jetbrains.python.codeInsight.intentions; import com.intellij.codeInsight.intention.impl.BaseIntentionAction; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiComment; +import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; @@ -11,6 +13,8 @@ import com.jetbrains.python.psi.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; + /** * User: catherine * Intention to merge the if clauses in the case of nested ifs where only the inner if contains code (the outer if only contains the inner one) @@ -37,10 +41,13 @@ public class PyJoinIfIntention extends BaseIntentionAction { PyIfStatement expression = PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyIfStatement.class); + PyIfStatement ifStatement = getIfStatement(expression); PyStatement firstStatement = getFirstStatement(ifStatement); if (firstStatement != null) { + if (((PyIfStatement)firstStatement).getElifParts().length != 0) return false; + if (((PyIfStatement)firstStatement).getElsePart() != null) return false; PyStatementList stList = ((PyIfStatement)firstStatement).getIfPart().getStatementList(); if (stList != null) if (stList.getStatements().length != 0) @@ -59,11 +66,22 @@ public class PyJoinIfIntention extends BaseIntentionAction { if (firstStatement != null && firstStatement instanceof PyIfStatement) { PyExpression condition = ((PyIfStatement)firstStatement).getIfPart().getCondition(); PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); - PyExpression newCondition = elementGenerator.createExpressionFromText(ifStatement.getIfPart().getCondition().getText() + " and " + condition.getText()); + PyExpression newCondition = elementGenerator.createExpressionFromText( + ifStatement.getIfPart().getCondition().getText() + " and " + condition.getText()); ifStatement.getIfPart().getCondition().replace(newCondition); PyStatementList stList = ((PyIfStatement)firstStatement).getIfPart().getStatementList(); PyStatementList ifStatementList = ifStatement.getIfPart().getStatementList(); + + List comments = PsiTreeUtil.getChildrenOfTypeAsList(ifStatement.getIfPart(), PsiComment.class); + comments.addAll(PsiTreeUtil.getChildrenOfTypeAsList(((PyIfStatement)firstStatement).getIfPart(), PsiComment.class)); + comments.addAll(PsiTreeUtil.getChildrenOfTypeAsList(ifStatementList, PsiComment.class)); + comments.addAll(PsiTreeUtil.getChildrenOfTypeAsList(stList, PsiComment.class)); + + for (PsiElement comm : comments) { + ifStatement.getIfPart().addBefore(comm, ifStatementList); + comm.delete(); + } ifStatementList.replace(stList); } } diff --git a/python/src/com/jetbrains/python/inspections/PyAugmentAssignmentInspection.java b/python/src/com/jetbrains/python/inspections/PyAugmentAssignmentInspection.java index abb92ff419a8..5a7383a1d72e 100644 --- a/python/src/com/jetbrains/python/inspections/PyAugmentAssignmentInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyAugmentAssignmentInspection.java @@ -2,6 +2,7 @@ package com.jetbrains.python.inspections; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.tree.TokenSet; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.actions.AugmentedAssignmentQuickFix; @@ -43,17 +44,19 @@ public class PyAugmentAssignmentInspection extends PyInspection { PyExpression leftExpression = expression.getLeftExpression(); PyExpression rightExpression = expression.getRightExpression(); if (rightExpression != null) { + boolean changedParts = false; if (rightExpression.getText().equals(target.getText())) { PyExpression tmp = rightExpression; rightExpression = leftExpression; leftExpression = tmp; + changedParts = true; } PyElementType op = expression.getOperator(); - if (PyTokenTypes.ADDITIVE_OPERATIONS.contains(op) || - PyTokenTypes.MULTIPLICATIVE_OPERATIONS.contains(op) || - PyTokenTypes.SHIFT_OPERATIONS.contains(op) || - PyTokenTypes.BITWISE_OPERATIONS.contains(op) || - op == PyTokenTypes.EXP) { + final TokenSet operations = TokenSet.create(PyTokenTypes.PLUS, PyTokenTypes.MINUS, PyTokenTypes.MULT, + PyTokenTypes.FLOORDIV, PyTokenTypes.DIV, PyTokenTypes.PERC, PyTokenTypes.AND, PyTokenTypes.OR, + PyTokenTypes.XOR, PyTokenTypes.LTLT, PyTokenTypes.GTGT, PyTokenTypes.EXP); + final TokenSet commutativeOperations = TokenSet.create(PyTokenTypes.PLUS, PyTokenTypes.MULT); + if ((operations.contains(op) && !changedParts) || (changedParts && commutativeOperations.contains(op))) { if (leftExpression != null && (leftExpression instanceof PyReferenceExpression || leftExpression instanceof PySubscriptionExpression)) { if (leftExpression.getText().equals(target.getText())) { diff --git a/python/src/com/jetbrains/python/inspections/PyDictDuplicateKeysInspection.java b/python/src/com/jetbrains/python/inspections/PyDictDuplicateKeysInspection.java index 56e90deea0df..20c8f2edda28 100644 --- a/python/src/com/jetbrains/python/inspections/PyDictDuplicateKeysInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyDictDuplicateKeysInspection.java @@ -1,20 +1,18 @@ package com.jetbrains.python.inspections; import com.intellij.codeInspection.ProblemsHolder; -import com.intellij.openapi.util.Pair; -import com.intellij.psi.PsiElement; +import com.intellij.openapi.application.Application; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.psi.PsiElementVisitor; -import com.intellij.psi.PsiPolyVariantReference; -import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.HashSet; import com.jetbrains.python.PyBundle; -import com.jetbrains.python.actions.DictCreationQuickFix; import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.types.PyType; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; +import java.util.Set; /** * User: catherine @@ -57,5 +55,74 @@ public class PyDictDuplicateKeysInspection extends PyInspection { } } } + + @Override + public void visitPyCallExpression(PyCallExpression node) { + if (isDict(node)) { + HashSet set = new HashSet(); + PyExpression[] argumentList = node.getArgumentList().getArguments(); + for (PyExpression argument : argumentList) { + if (argument instanceof PyParenthesizedExpression) + argument = ((PyParenthesizedExpression)argument).getContainedExpression(); + if (argument instanceof PySequenceExpression) { + for (PyElement el : ((PySequenceExpression)argument).getElements()) { + String key = getKey(el); + checkKey(key, set, node); + } + } + else { + String key = getKey(argument); + checkKey(key, set, node); + } + } + } + } + + private void checkKey(String key, Set set, PyCallExpression node) { + if (key != null) { + if (set.contains(key)) { + registerProblem(node, "Dictionary contains duplicate keys " + key); + } + set.add(key); + } + } + + @Nullable + private String getKey(PyElement argument) { + if (argument instanceof PyParenthesizedExpression) { + PyExpression expr = ((PyParenthesizedExpression)argument).getContainedExpression(); + if (expr instanceof PyTupleExpression) { + PyElement key = ((PyTupleExpression)expr).getElements()[0]; + if (key instanceof PyStringLiteralExpression) { + return ((PyStringLiteralExpression)key).getStringValue(); + } + else { + return key.getText(); + } + } + } + if (argument instanceof PyKeywordArgument) { + return ((PyKeywordArgument)argument).getKeyword(); + } + return null; + } + + private boolean isDict(PyCallExpression expression) { + String name = expression.getCallee().getText(); + if ("dict".equals(name)) { + PyType type = expression.getType(myTypeEvalContext); + Application application = ApplicationManager.getApplication(); + if (application != null && application.isUnitTestMode()) { + return true; + } + if (type != null) { + if (type.isBuiltin()) { + return true; + } + } + } + return false; + } + } } diff --git a/python/testData/inspections/PyAugmentAssignmentInspection/test.py b/python/testData/inspections/PyAugmentAssignmentInspection/test.py index 50601231ca12..fc807600fb9d 100644 --- a/python/testData/inspections/PyAugmentAssignmentInspection/test.py +++ b/python/testData/inspections/PyAugmentAssignmentInspection/test.py @@ -25,3 +25,6 @@ var_7 += 2 x = x | 3 x = x & 3 x = x ^ 3 + +#PY-2514 +dy = 1 - dy diff --git a/python/testData/inspections/PyDictDuplicateKeysInspection/test.py b/python/testData/inspections/PyDictDuplicateKeysInspection/test.py index d8a5ace62bce..3fb6c0f73e84 100644 --- a/python/testData/inspections/PyDictDuplicateKeysInspection/test.py +++ b/python/testData/inspections/PyDictDuplicateKeysInspection/test.py @@ -7,4 +7,11 @@ import random def foo(): return random.random() -{foo(): 1, foo():2} +{foo(): 1, foo():2} + +# PY-2511 +dict = dict([('key', 666), ('key', 123)]) +dict = dict((('key', 666), ('key', 123))) +dict = dict((('key', 666), ('k', 123)), key=4) + +dict([('key', 666), ('ky', 123)])