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)])