From 28156d540dbc643a5a605564bdc3ed1c8bd5a235 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 3 Aug 2016 15:32:06 +0300 Subject: [PATCH 1/8] Simplify ConvertVariadicParamIntention.isAvailable, annotate PyNames.isIdentifierString and link PyIntentionTest.testConvertVariadicParamEmptySubscription with issue --- .../src/com/jetbrains/python/PyNames.java | 2 +- .../ConvertVariadicParamIntention.java | 46 ++++++++++--------- .../python/intentions/PyIntentionTest.java | 1 + 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/python/psi-api/src/com/jetbrains/python/PyNames.java b/python/psi-api/src/com/jetbrains/python/PyNames.java index 25bf044e8796..623096171a8d 100644 --- a/python/psi-api/src/com/jetbrains/python/PyNames.java +++ b/python/psi-api/src/com/jetbrains/python/PyNames.java @@ -552,7 +552,7 @@ public class PyNames { return !isReserved(name) && isIdentifierString(name); } - public static boolean isIdentifierString(String name) { + public static boolean isIdentifierString(@NotNull @NonNls String name) { return IDENTIFIER_PATTERN.matcher(name).matches(); } diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java index 99bb88dc136e..d71a4c76159f 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.Stack; import com.jetbrains.python.PyBundle; @@ -60,38 +61,39 @@ public class ConvertVariadicParamIntention extends BaseIntentionAction { return PyBundle.message("INTN.convert.variadic.param"); } + @Override public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { if (!(file instanceof PyFile)) { return false; } - PyFunction function = - PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyFunction.class); + final PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); + final PyFunction function = PsiTreeUtil.getParentOfType(element, PyFunction.class); + if (function != null) { - PyParameter[] parameterList = function.getParameterList().getParameters(); - for (PyParameter parameter : parameterList) { - if (parameter instanceof PyNamedParameter) { - if (((PyNamedParameter)parameter).isKeywordContainer()) { - List subscriptions = fillSubscriptions(function); - List callElements = fillCallExpressions(function); - if ((subscriptions.size() + callElements.size()) != 0) { - for (PyCallExpression element : callElements) { - final PyExpression[] arguments = element.getArguments(); - if (arguments.length < 1) return false; - if (!PyNames.isIdentifierString(PythonStringUtil.getStringValue(arguments[0]))) - return false; - } - for (PySubscriptionExpression subscription : subscriptions) { - final PyExpression expression = subscription.getIndexExpression(); - if (expression == null || !PyNames.isIdentifierString(PythonStringUtil.getStringValue(expression))) - return false; - } + for (PyParameter parameter : function.getParameterList().getParameters()) { + if (parameter instanceof PyNamedParameter && ((PyNamedParameter)parameter).isKeywordContainer()) { + for (PyCallExpression call : fillCallExpressions(function)) { + final PyExpression firstArgument = ArrayUtil.getFirstElement(call.getArguments()); + final String firstArgumentValue = PythonStringUtil.getStringValue(firstArgument); + if (firstArgumentValue == null || !PyNames.isIdentifierString(firstArgumentValue)) { + return false; } - return true; } + + for (PySubscriptionExpression subscription : fillSubscriptions(function)) { + final PyExpression indexExpression = subscription.getIndexExpression(); + final String indexValue = PythonStringUtil.getStringValue(indexExpression); + if (indexValue == null || !PyNames.isIdentifierString(indexValue)) { + return false; + } + } + + return true; } } } + return false; } diff --git a/python/testSrc/com/jetbrains/python/intentions/PyIntentionTest.java b/python/testSrc/com/jetbrains/python/intentions/PyIntentionTest.java index 9cc021579039..53d2dc40c315 100644 --- a/python/testSrc/com/jetbrains/python/intentions/PyIntentionTest.java +++ b/python/testSrc/com/jetbrains/python/intentions/PyIntentionTest.java @@ -228,6 +228,7 @@ public class PyIntentionTest extends PyTestCase { doTest(PyBundle.message("INTN.convert.variadic.param")); } + // PY-20254 public void testConvertVariadicParamEmptySubscription() { doNegativeTest(PyBundle.message("INTN.convert.variadic.param")); } From 0bcf80147a6580ba0c865ff2ac3fb9016d81494a Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 3 Aug 2016 15:46:50 +0300 Subject: [PATCH 2/8] Simplify ConvertVariadicParamIntention.getKeywordContainer and use it in ConvertVariadicParamIntention.isAvailable --- .../ConvertVariadicParamIntention.java | 52 +++++++++---------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java index d71a4c76159f..caf7d622ff6c 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java @@ -33,6 +33,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -70,28 +71,24 @@ public class ConvertVariadicParamIntention extends BaseIntentionAction { final PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); final PyFunction function = PsiTreeUtil.getParentOfType(element, PyFunction.class); - if (function != null) { - for (PyParameter parameter : function.getParameterList().getParameters()) { - if (parameter instanceof PyNamedParameter && ((PyNamedParameter)parameter).isKeywordContainer()) { - for (PyCallExpression call : fillCallExpressions(function)) { - final PyExpression firstArgument = ArrayUtil.getFirstElement(call.getArguments()); - final String firstArgumentValue = PythonStringUtil.getStringValue(firstArgument); - if (firstArgumentValue == null || !PyNames.isIdentifierString(firstArgumentValue)) { - return false; - } - } - - for (PySubscriptionExpression subscription : fillSubscriptions(function)) { - final PyExpression indexExpression = subscription.getIndexExpression(); - final String indexValue = PythonStringUtil.getStringValue(indexExpression); - if (indexValue == null || !PyNames.isIdentifierString(indexValue)) { - return false; - } - } - - return true; + if (getKeywordContainer(function) != null) { + for (PyCallExpression call : fillCallExpressions(function)) { + final PyExpression firstArgument = ArrayUtil.getFirstElement(call.getArguments()); + final String firstArgumentValue = PythonStringUtil.getStringValue(firstArgument); + if (firstArgumentValue == null || !PyNames.isIdentifierString(firstArgumentValue)) { + return false; } } + + for (PySubscriptionExpression subscription : fillSubscriptions(function)) { + final PyExpression indexExpression = subscription.getIndexExpression(); + final String indexValue = PythonStringUtil.getStringValue(indexExpression); + if (indexValue == null || !PyNames.isIdentifierString(indexValue)) { + return false; + } + } + + return true; } return false; @@ -100,14 +97,13 @@ public class ConvertVariadicParamIntention extends BaseIntentionAction { @Nullable private static PyParameter getKeywordContainer(PyFunction function) { if (function != null) { - PyParameter[] parameterList = function.getParameterList().getParameters(); - for (PyParameter parameter : parameterList) { - if (parameter instanceof PyNamedParameter) { - if (((PyNamedParameter)parameter).isKeywordContainer()) { - return parameter; - } - } - } + return Arrays + .stream(function.getParameterList().getParameters()) + .filter(PyNamedParameter.class::isInstance) + .map(PyNamedParameter.class::cast) + .filter(PyNamedParameter::isKeywordContainer) + .findFirst() + .orElse(null); } return null; } From 8089c24f56b63dea7824cccc004a120b19f4ddc4 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 3 Aug 2016 17:27:05 +0300 Subject: [PATCH 3/8] ConvertVariadicParamIntention: annotate and update getText, getFamilyName, isAvailable, getKeywordContainer and invoke methods --- .../ConvertVariadicParamIntention.java | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java index caf7d622ff6c..b2cf5be7e07d 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java @@ -52,11 +52,14 @@ import java.util.List; * */ public class ConvertVariadicParamIntention extends BaseIntentionAction { + + @Override @NotNull public String getText() { return PyBundle.message("INTN.convert.variadic.param"); } + @Override @NotNull public String getFamilyName() { return PyBundle.message("INTN.convert.variadic.param"); @@ -71,7 +74,7 @@ public class ConvertVariadicParamIntention extends BaseIntentionAction { final PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); final PyFunction function = PsiTreeUtil.getParentOfType(element, PyFunction.class); - if (getKeywordContainer(function) != null) { + if (function != null) { for (PyCallExpression call : fillCallExpressions(function)) { final PyExpression firstArgument = ArrayUtil.getFirstElement(call.getArguments()); final String firstArgumentValue = PythonStringUtil.getStringValue(firstArgument); @@ -88,31 +91,32 @@ public class ConvertVariadicParamIntention extends BaseIntentionAction { } } - return true; + return getKeywordContainer(function) != null; } return false; } @Nullable - private static PyParameter getKeywordContainer(PyFunction function) { - if (function != null) { - return Arrays - .stream(function.getParameterList().getParameters()) - .filter(PyNamedParameter.class::isInstance) - .map(PyNamedParameter.class::cast) - .filter(PyNamedParameter::isKeywordContainer) - .findFirst() - .orElse(null); - } - return null; + private static PyParameter getKeywordContainer(@NotNull PyFunction function) { + return Arrays + .stream(function.getParameterList().getParameters()) + .filter(PyNamedParameter.class::isInstance) + .map(PyNamedParameter.class::cast) + .filter(PyNamedParameter::isKeywordContainer) + .findFirst() + .orElse(null); } + @Override public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - PyFunction function = - PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyFunction.class); - replaceSubscriptions(function, project); - replaceCallElements(function, project); + final PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); + final PyFunction function = PsiTreeUtil.getParentOfType(element, PyFunction.class); + + if (function != null) { + replaceSubscriptions(function, project); + replaceCallElements(function, project); + } } /** From 7756f9d0f73a613f5fa1e25332142047d0612e27 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 3 Aug 2016 17:31:29 +0300 Subject: [PATCH 4/8] ConvertVariadicParamIntention: annotate, rename and update fillSubscriptions method --- .../ConvertVariadicParamIntention.java | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java index b2cf5be7e07d..4d9862515dc4 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java @@ -34,6 +34,7 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; /** @@ -83,7 +84,7 @@ public class ConvertVariadicParamIntention extends BaseIntentionAction { } } - for (PySubscriptionExpression subscription : fillSubscriptions(function)) { + for (PySubscriptionExpression subscription : findKeywordContainerSubscriptions(function)) { final PyExpression indexExpression = subscription.getIndexExpression(); final String indexValue = PythonStringUtil.getStringValue(indexExpression); if (indexValue == null || !PyNames.isIdentifierString(indexValue)) { @@ -119,35 +120,40 @@ public class ConvertVariadicParamIntention extends BaseIntentionAction { } } - /** - * finds subscriptions of keyword container, adds them to mySubscriptions - * @param function - */ - private static List fillSubscriptions(PyFunction function) { - List subscriptions = new ArrayList<>(); - PyStatementList statementList = function.getStatementList(); - Stack stack = new Stack<>(); - PyParameter keywordContainer = getKeywordContainer(function); - if (keywordContainer != null) { - String keywordContainerName = keywordContainer.getName(); - for (PyStatement st : statementList.getStatements()) { - stack.push(st); + @NotNull + private static List findKeywordContainerSubscriptions(@NotNull PyFunction function) { + final PyParameter keywordContainer = getKeywordContainer(function); + final String keywordContainerName = keywordContainer == null ? null : keywordContainer.getName(); + + if (keywordContainerName != null) { + final List result = new ArrayList(); + final Stack stack = new Stack(); + + for (PyStatement statement : function.getStatementList().getStatements()) { + stack.push(statement); + while (!stack.isEmpty()) { - PsiElement e = stack.pop(); - if (e instanceof PySubscriptionExpression) { - if (((PySubscriptionExpression)e).getOperand().getText().equals(keywordContainerName)) { - subscriptions.add((PySubscriptionExpression)e); + final PsiElement element = stack.pop(); + + if (element instanceof PySubscriptionExpression) { + final PySubscriptionExpression subscription = (PySubscriptionExpression)element; + + if (subscription.getOperand().getText().equals(keywordContainerName)) { + result.add(subscription); } } else { - for (PsiElement psiElement : e.getChildren()) { - stack.push(psiElement); + for (PsiElement child : element.getChildren()) { + stack.push(child); } } } } + + return result; } - return subscriptions; + + return Collections.emptyList(); } private static boolean isCallElement(PyExpression callee, String keywordContainerName) { @@ -187,7 +193,7 @@ public class ConvertVariadicParamIntention extends BaseIntentionAction { private static void replaceSubscriptions(PyFunction function, Project project) { PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); - List subscriptions = fillSubscriptions(function); + List subscriptions = findKeywordContainerSubscriptions(function); int size = subscriptions.size(); for (int i = 0; i != size; ++i) { PySubscriptionExpression subscriptionExpression = subscriptions.get(i); From 36889826a1cb3448ff2515da99869f6af7f52702 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 3 Aug 2016 17:40:44 +0300 Subject: [PATCH 5/8] ConvertVariadicParamIntention: annotate, rename and update isCallElement and fillCallExpressions methods --- .../ConvertVariadicParamIntention.java | 60 ++++++++++--------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java index 4d9862515dc4..1f17913d84c9 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java @@ -76,7 +76,7 @@ public class ConvertVariadicParamIntention extends BaseIntentionAction { final PyFunction function = PsiTreeUtil.getParentOfType(element, PyFunction.class); if (function != null) { - for (PyCallExpression call : fillCallExpressions(function)) { + for (PyCallExpression call : findKeywordContainerCalls(function)) { final PyExpression firstArgument = ArrayUtil.getFirstElement(call.getArguments()); final String firstArgumentValue = PythonStringUtil.getStringValue(firstArgument); if (firstArgumentValue == null || !PyNames.isIdentifierString(firstArgumentValue)) { @@ -156,39 +156,45 @@ public class ConvertVariadicParamIntention extends BaseIntentionAction { return Collections.emptyList(); } - private static boolean isCallElement(PyExpression callee, String keywordContainerName) { - PyExpression qualifier = ((PyQualifiedExpression)callee).getQualifier(); - return (qualifier != null && qualifier.getText().equals(keywordContainerName) - && ("get".equals(((PyQualifiedExpression)callee).getReferencedName()) - || "__getitem__".equals(((PyQualifiedExpression)callee).getReferencedName()) )); + private static boolean isKeywordContainerCall(@NotNull PyQualifiedExpression callee, @NotNull String keywordContainerName) { + final PyExpression qualifier = callee.getQualifier(); + return qualifier != null && + qualifier.getText().equals(keywordContainerName) && + ArrayUtil.contains(callee.getReferencedName(), "get", PyNames.GETITEM); } - private static List fillCallExpressions(PyFunction function) { - List callElements = new ArrayList<>(); - PyStatementList statementList = function.getStatementList(); - Stack stack = new Stack<>(); - PyParameter keywordContainer = getKeywordContainer(function); - if (keywordContainer != null) { - String keywordContainerName = keywordContainer.getName(); - for (PyStatement st : statementList.getStatements()) { - stack.push(st); + @NotNull + private static List findKeywordContainerCalls(@NotNull PyFunction function) { + final PyParameter keywordContainer = getKeywordContainer(function); + final String keywordContainerName = keywordContainer == null ? null : keywordContainer.getName(); + + if (keywordContainerName != null) { + final List result = new ArrayList(); + final Stack stack = new Stack(); + + for (PyStatement statement : function.getStatementList().getStatements()) { + stack.push(statement); + while (!stack.isEmpty()) { - PsiElement e = stack.pop(); - if (!(e instanceof PySubscriptionExpression)) { - if (e instanceof PyCallExpression && ((PyCallExpression)e).getCallee() instanceof PyQualifiedExpression - && isCallElement(((PyCallExpression)e).getCallee(), keywordContainerName)) { - callElements.add((PyCallExpression)e); - } - else { - for (PsiElement psiElement : e.getChildren()) { - stack.push(psiElement); - } + final PsiElement element = stack.pop(); + + if (element instanceof PyCallExpression && + ((PyCallExpression)element).getCallee() instanceof PyQualifiedExpression && + isKeywordContainerCall((PyQualifiedExpression)((PyCallExpression)element).getCallee(), keywordContainerName)) { + result.add((PyCallExpression)element); + } + else { + for (PsiElement child : element.getChildren()) { + stack.push(child); } } } } + + return result; } - return callElements; + + return Collections.emptyList(); } private static void replaceSubscriptions(PyFunction function, Project project) { @@ -220,7 +226,7 @@ public class ConvertVariadicParamIntention extends BaseIntentionAction { private static void replaceCallElements(PyFunction function, Project project) { PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); - List callElements = fillCallExpressions(function); + List callElements = findKeywordContainerCalls(function); int size = callElements.size(); for (int i = 0; i != size; ++i) { From cdb1f85adc9932b7f33f6146d6aff0ffeace4939 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 3 Aug 2016 17:46:46 +0300 Subject: [PATCH 6/8] ConvertVariadicParamIntention: annotate, rename and update replaceSubscriptions method --- .../ConvertVariadicParamIntention.java | 44 +++++++++---------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java index 1f17913d84c9..f645e67683fe 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java @@ -115,7 +115,7 @@ public class ConvertVariadicParamIntention extends BaseIntentionAction { final PyFunction function = PsiTreeUtil.getParentOfType(element, PyFunction.class); if (function != null) { - replaceSubscriptions(function, project); + replaceKeywordContainerSubscriptions(function, project); replaceCallElements(function, project); } } @@ -197,30 +197,26 @@ public class ConvertVariadicParamIntention extends BaseIntentionAction { return Collections.emptyList(); } - private static void replaceSubscriptions(PyFunction function, Project project) { - PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); - List subscriptions = findKeywordContainerSubscriptions(function); - int size = subscriptions.size(); - for (int i = 0; i != size; ++i) { - PySubscriptionExpression subscriptionExpression = subscriptions.get(i); - PyExpression indexExpression = subscriptionExpression.getIndexExpression(); - if (indexExpression instanceof PyStringLiteralExpression) { - PyExpression p = elementGenerator.createExpressionFromText(LanguageLevel.forElement(function), - ((PyStringLiteralExpression)indexExpression).getStringValue()); - ASTNode comma = elementGenerator.createComma(); - PyClass containingClass = function.getContainingClass(); - if (p != null) { - if (containingClass == null) { - function.getParameterList().addBefore(p, function.getParameterList().getParameters()[0]); - function.getParameterList().addBefore((PsiElement)comma, function.getParameterList().getParameters()[0]); + private static void replaceKeywordContainerSubscriptions(@NotNull PyFunction function, @NotNull Project project) { + final PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); + + for (PySubscriptionExpression subscription : findKeywordContainerSubscriptions(function)) { + Optional + .ofNullable(subscription.getIndexExpression()) + .map(indexExpression -> PyUtil.as(indexExpression, PyStringLiteralExpression.class)) + .map(PyStringLiteralExpression::getStringValue) + .map(indexValue -> elementGenerator.createExpressionFromText(LanguageLevel.forElement(function), indexValue)) + .ifPresent( + parameter -> { + final int anchorIndex = function.getContainingClass() == null ? 0 : 1; + final PsiElement comma = (PsiElement)elementGenerator.createComma(); + + function.getParameterList().addBefore(parameter, function.getParameterList().getParameters()[anchorIndex]); + function.getParameterList().addBefore(comma, function.getParameterList().getParameters()[anchorIndex]); + + subscription.replace(parameter); } - else { - function.getParameterList().addBefore(p, function.getParameterList().getParameters()[1]); - function.getParameterList().addBefore((PsiElement)comma, function.getParameterList().getParameters()[1]); - } - subscriptionExpression.replace(p); - } - } + ); } } From bf8b9155c8141ae658a15ffee323edbff2277e33 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 3 Aug 2016 18:03:08 +0300 Subject: [PATCH 7/8] ConvertVariadicParamIntention: annotate, rename and update replaceCallElements method --- .../ConvertVariadicParamIntention.java | 86 ++++++++++--------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java index f645e67683fe..a50bd5627f04 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java @@ -16,7 +16,6 @@ package com.jetbrains.python.codeInsight.intentions; import com.intellij.codeInsight.intention.impl.BaseIntentionAction; -import com.intellij.lang.ASTNode; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; @@ -32,10 +31,7 @@ import com.jetbrains.python.psi.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import java.util.*; /** * User: catherine @@ -116,7 +112,7 @@ public class ConvertVariadicParamIntention extends BaseIntentionAction { if (function != null) { replaceKeywordContainerSubscriptions(function, project); - replaceCallElements(function, project); + replaceKeywordContainerCalls(function, project); } } @@ -220,45 +216,51 @@ public class ConvertVariadicParamIntention extends BaseIntentionAction { } } - private static void replaceCallElements(PyFunction function, Project project) { - PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); - List callElements = findKeywordContainerCalls(function); + private static void replaceKeywordContainerCalls(@NotNull PyFunction function, @NotNull Project project) { + final PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); - int size = callElements.size(); - for (int i = 0; i != size; ++i) { - PyCallExpression callExpression = callElements.get(i); - PyExpression indexExpression = callExpression.getArguments()[0]; + for (PyCallExpression call : findKeywordContainerCalls(function)) { + Optional + .of(call.getArguments()) + .map(ArrayUtil::getFirstElement) + .map(firstArgument -> PyUtil.as(firstArgument, PyStringLiteralExpression.class)) + .map(PyStringLiteralExpression::getStringValue) + .ifPresent( + indexValue -> { + final PyNamedParameter parameterWithDefaultValue = getParameterWithDefaultValue(elementGenerator, call, indexValue); + final PyExpression parameter = elementGenerator.createExpressionFromText(LanguageLevel.forElement(function), indexValue); + final PyParameter keywordContainer = getKeywordContainer(function); - if (indexExpression instanceof PyStringLiteralExpression) { - PyNamedParameter defaultValue = null; - if (callExpression.getArguments().length > 1) { - defaultValue = elementGenerator.createParameter( - ((PyStringLiteralExpression)indexExpression).getStringValue() - + "=" + callExpression.getArguments()[1].getText()); - } - if (defaultValue == null) { - PyExpression callee = callExpression.getCallee(); - if (callee instanceof PyQualifiedExpression && "get".equals(((PyQualifiedExpression)callee).getReferencedName())) { - defaultValue = elementGenerator.createParameter(((PyStringLiteralExpression)indexExpression).getStringValue() + "=None"); + if (parameter != null) { + if (parameterWithDefaultValue != null) { + function.getParameterList().addBefore(parameterWithDefaultValue, keywordContainer); + } + else { + function.getParameterList().addBefore(parameter, keywordContainer); + } + function.getParameterList().addBefore((PsiElement)elementGenerator.createComma(), keywordContainer); + + call.replace(parameter); + } } - } - PyExpression p = elementGenerator.createExpressionFromText(LanguageLevel.forElement(function), - ((PyStringLiteralExpression)indexExpression).getStringValue()); - ASTNode comma = elementGenerator.createComma(); - - PyParameter keywordContainer = getKeywordContainer(function); - - if (p != null) { - if (defaultValue != null) - function.getParameterList().addBefore(defaultValue, keywordContainer); - else - function.getParameterList().addBefore(p, keywordContainer); - - function.getParameterList().addBefore((PsiElement)comma, keywordContainer); - - callExpression.replace(p); - } - } + ); } } + + @Nullable + private static PyNamedParameter getParameterWithDefaultValue(@NotNull PyElementGenerator elementGenerator, + @NotNull PyCallExpression call, + @NotNull String parameterName) { + final PyExpression[] arguments = call.getArguments(); + if (arguments.length > 1) { + return elementGenerator.createParameter(parameterName + "=" + arguments[1].getText()); + } + + final PyQualifiedExpression callee = PyUtil.as(call.getCallee(), PyQualifiedExpression.class); + if (callee != null && "get".equals(callee.getReferencedName())) { + return elementGenerator.createParameter(parameterName + "=" + PyNames.NONE); + } + + return null; + } } From 690968ac93253fd2c024f78f02ab96af3c4d5fbe Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 3 Aug 2016 18:43:58 +0300 Subject: [PATCH 8/8] ConvertVariadicParamIntention: merge findKeywordContainerSubscriptions and findKeywordContainerCalls into one method with given predicate --- .../ConvertVariadicParamIntention.java | 131 +++++++++--------- 1 file changed, 62 insertions(+), 69 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java index a50bd5627f04..325b102fe267 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/ConvertVariadicParamIntention.java @@ -32,6 +32,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.function.BiPredicate; /** * User: catherine @@ -118,79 +119,12 @@ public class ConvertVariadicParamIntention extends BaseIntentionAction { @NotNull private static List findKeywordContainerSubscriptions(@NotNull PyFunction function) { - final PyParameter keywordContainer = getKeywordContainer(function); - final String keywordContainerName = keywordContainer == null ? null : keywordContainer.getName(); - - if (keywordContainerName != null) { - final List result = new ArrayList(); - final Stack stack = new Stack(); - - for (PyStatement statement : function.getStatementList().getStatements()) { - stack.push(statement); - - while (!stack.isEmpty()) { - final PsiElement element = stack.pop(); - - if (element instanceof PySubscriptionExpression) { - final PySubscriptionExpression subscription = (PySubscriptionExpression)element; - - if (subscription.getOperand().getText().equals(keywordContainerName)) { - result.add(subscription); - } - } - else { - for (PsiElement child : element.getChildren()) { - stack.push(child); - } - } - } - } - - return result; - } - - return Collections.emptyList(); - } - - private static boolean isKeywordContainerCall(@NotNull PyQualifiedExpression callee, @NotNull String keywordContainerName) { - final PyExpression qualifier = callee.getQualifier(); - return qualifier != null && - qualifier.getText().equals(keywordContainerName) && - ArrayUtil.contains(callee.getReferencedName(), "get", PyNames.GETITEM); + return findKeywordContainerUsages(function, ConvertVariadicParamIntention::isKeywordContainerSubscription); } @NotNull private static List findKeywordContainerCalls(@NotNull PyFunction function) { - final PyParameter keywordContainer = getKeywordContainer(function); - final String keywordContainerName = keywordContainer == null ? null : keywordContainer.getName(); - - if (keywordContainerName != null) { - final List result = new ArrayList(); - final Stack stack = new Stack(); - - for (PyStatement statement : function.getStatementList().getStatements()) { - stack.push(statement); - - while (!stack.isEmpty()) { - final PsiElement element = stack.pop(); - - if (element instanceof PyCallExpression && - ((PyCallExpression)element).getCallee() instanceof PyQualifiedExpression && - isKeywordContainerCall((PyQualifiedExpression)((PyCallExpression)element).getCallee(), keywordContainerName)) { - result.add((PyCallExpression)element); - } - else { - for (PsiElement child : element.getChildren()) { - stack.push(child); - } - } - } - } - - return result; - } - - return Collections.emptyList(); + return findKeywordContainerUsages(function, ConvertVariadicParamIntention::isKeywordContainerCall); } private static void replaceKeywordContainerSubscriptions(@NotNull PyFunction function, @NotNull Project project) { @@ -247,6 +181,65 @@ public class ConvertVariadicParamIntention extends BaseIntentionAction { } } + @NotNull + private static List findKeywordContainerUsages(@NotNull PyFunction function, + @NotNull BiPredicate usagePredicate) { + final PyParameter keywordContainer = getKeywordContainer(function); + final String keywordContainerName = keywordContainer == null ? null : keywordContainer.getName(); + + if (keywordContainerName != null) { + final List result = new ArrayList(); + final Stack stack = new Stack(); + + for (PyStatement statement : function.getStatementList().getStatements()) { + stack.push(statement); + + while (!stack.isEmpty()) { + final PsiElement element = stack.pop(); + + if (usagePredicate.test(element, keywordContainerName)) { + //noinspection unchecked + result.add((T)element); + } + else { + for (PsiElement child : element.getChildren()) { + stack.push(child); + } + } + } + } + + return result; + } + + return Collections.emptyList(); + } + + private static boolean isKeywordContainerSubscription(@Nullable PsiElement element, @NotNull String keywordContainerName) { + return Optional + .ofNullable(PyUtil.as(element, PySubscriptionExpression.class)) + .map(PySubscriptionExpression::getOperand) + .map(PyExpression::getText) + .filter(text -> text.equals(keywordContainerName)) + .isPresent(); + } + + private static boolean isKeywordContainerCall(@Nullable PsiElement element, @NotNull String keywordContainerName) { + return Optional + .ofNullable(PyUtil.as(element, PyCallExpression.class)) + .map(PyCallExpression::getCallee) + .map(callee -> PyUtil.as(callee, PyQualifiedExpression.class)) + .filter( + callee -> { + final PyExpression qualifier = callee.getQualifier(); + return qualifier != null && + qualifier.getText().equals(keywordContainerName) && + ArrayUtil.contains(callee.getReferencedName(), "get", PyNames.GETITEM); + } + ) + .isPresent(); + } + @Nullable private static PyNamedParameter getParameterWithDefaultValue(@NotNull PyElementGenerator elementGenerator, @NotNull PyCallExpression call,