| /* |
| * Copyright 2000-2013 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. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| 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; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.containers.Stack; |
| import com.jetbrains.python.PyBundle; |
| import com.jetbrains.python.PyNames; |
| import com.jetbrains.python.PythonStringUtil; |
| import com.jetbrains.python.psi.*; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * User: catherine |
| * |
| * Intention to convert variadic parameter(s) to normal |
| * For instance, |
| * |
| * from: |
| * def foo(**kwargs): |
| * doSomething(kwargs['foo']) |
| * |
| * to: |
| * def foo(foo, **kwargs): |
| * doSomething(foo) |
| * |
| */ |
| public class ConvertVariadicParamIntention extends BaseIntentionAction { |
| @NotNull |
| public String getText() { |
| return PyBundle.message("INTN.convert.variadic.param"); |
| } |
| |
| @NotNull |
| public String getFamilyName() { |
| return PyBundle.message("INTN.convert.variadic.param"); |
| } |
| |
| 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); |
| if (function != null) { |
| PyParameter[] parameterList = function.getParameterList().getParameters(); |
| for (PyParameter parameter : parameterList) { |
| if (parameter instanceof PyNamedParameter) { |
| if (((PyNamedParameter)parameter).isKeywordContainer()) { |
| List <PySubscriptionExpression> subscriptions = fillSubscriptions(function); |
| List <PyCallExpression> 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 (!PyNames.isIdentifierString(PythonStringUtil.getStringValue(expression))) |
| return false; |
| } |
| } |
| return true; |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| @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 null; |
| } |
| |
| 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); |
| } |
| |
| /** |
| * finds subscriptions of keyword container, adds them to mySubscriptions |
| * @param function |
| */ |
| private static List<PySubscriptionExpression> fillSubscriptions(PyFunction function) { |
| List<PySubscriptionExpression> subscriptions = new ArrayList<PySubscriptionExpression>(); |
| PyStatementList statementList = function.getStatementList(); |
| Stack<PsiElement> stack = new Stack<PsiElement>(); |
| PyParameter keywordContainer = getKeywordContainer(function); |
| if (keywordContainer != null) { |
| String keywordContainerName = keywordContainer.getName(); |
| for (PyStatement st : statementList.getStatements()) { |
| stack.push(st); |
| while (!stack.isEmpty()) { |
| PsiElement e = stack.pop(); |
| if (e instanceof PySubscriptionExpression) { |
| if (((PySubscriptionExpression)e).getOperand().getText().equals(keywordContainerName)) { |
| subscriptions.add((PySubscriptionExpression)e); |
| } |
| } |
| else { |
| for (PsiElement psiElement : e.getChildren()) { |
| stack.push(psiElement); |
| } |
| } |
| } |
| } |
| } |
| return subscriptions; |
| } |
| |
| 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 List<PyCallExpression> fillCallExpressions(PyFunction function) { |
| List<PyCallExpression> callElements = new ArrayList<PyCallExpression>(); |
| PyStatementList statementList = function.getStatementList(); |
| Stack<PsiElement> stack = new Stack<PsiElement>(); |
| PyParameter keywordContainer = getKeywordContainer(function); |
| if (keywordContainer != null) { |
| String keywordContainerName = keywordContainer.getName(); |
| for (PyStatement st : statementList.getStatements()) { |
| stack.push(st); |
| 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); |
| } |
| } |
| } |
| } |
| } |
| } |
| return callElements; |
| } |
| |
| private static void replaceSubscriptions(PyFunction function, Project project) { |
| PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); |
| List <PySubscriptionExpression> subscriptions = fillSubscriptions(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]); |
| } |
| else { |
| function.getParameterList().addBefore(p, function.getParameterList().getParameters()[1]); |
| function.getParameterList().addBefore((PsiElement)comma, function.getParameterList().getParameters()[1]); |
| } |
| subscriptionExpression.replace(p); |
| } |
| } |
| } |
| } |
| |
| private static void replaceCallElements(PyFunction function, Project project) { |
| PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); |
| List <PyCallExpression> callElements = fillCallExpressions(function); |
| |
| int size = callElements.size(); |
| for (int i = 0; i != size; ++i) { |
| PyCallExpression callExpression = callElements.get(i); |
| PyExpression indexExpression = callExpression.getArguments()[0]; |
| |
| 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"); |
| } |
| } |
| 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); |
| } |
| } |
| } |
| } |
| } |