c40c9477afd02d64bad6d72674f60cad5395ea5e
[idea/community.git] / python / src / com / jetbrains / python / refactoring / makeFunctionTopLevel / PyMakeFunctionTopLevelRefactoring.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.jetbrains.python.refactoring.makeFunctionTopLevel;
17
18 import com.intellij.openapi.actionSystem.CommonDataKeys;
19 import com.intellij.openapi.actionSystem.DataContext;
20 import com.intellij.openapi.application.ApplicationManager;
21 import com.intellij.openapi.editor.Editor;
22 import com.intellij.openapi.project.Project;
23 import com.intellij.psi.PsiElement;
24 import com.intellij.psi.PsiFile;
25 import com.intellij.psi.util.PsiTreeUtil;
26 import com.intellij.refactoring.RefactoringActionHandler;
27 import com.intellij.refactoring.RefactoringBundle;
28 import com.intellij.refactoring.util.CommonRefactoringUtil;
29 import com.intellij.util.IncorrectOperationException;
30 import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
31 import com.jetbrains.python.psi.PyFunction;
32 import com.jetbrains.python.psi.PyReferenceExpression;
33 import com.jetbrains.python.psi.PyUtil;
34 import com.jetbrains.python.refactoring.PyBaseRefactoringAction;
35 import org.jetbrains.annotations.NotNull;
36 import org.jetbrains.annotations.Nullable;
37
38 import static com.jetbrains.python.psi.PyUtil.as;
39
40 /**
41  * @author Mikhail Golubev
42  */
43 public class PyMakeFunctionTopLevelRefactoring extends PyBaseRefactoringAction {
44   public static final String ID = "py.make.function.top.level";
45
46   @Override
47   protected boolean isAvailableInEditorOnly() {
48     return true;
49   }
50
51   @Override
52   protected boolean isEnabledOnElementInsideEditor(@NotNull PsiElement element,
53                                                    @NotNull Editor editor,
54                                                    @NotNull PsiFile file,
55                                                    @NotNull DataContext context) {
56     return findTargetFunction(element) != null;
57   }
58
59   @Override
60   protected boolean isEnabledOnElementsOutsideEditor(@NotNull PsiElement[] elements) {
61     return false;
62   }
63
64   @Nullable
65   private static PyFunction findTargetFunction(@NotNull PsiElement element) {
66     if (isLocalFunction(element) || isInstanceMethod(element)) {
67       return (PyFunction)element;
68     }
69     final PyReferenceExpression refExpr = PsiTreeUtil.getParentOfType(element, PyReferenceExpression.class);
70     if (refExpr == null) {
71       return null;
72     }
73     final PsiElement resolved = refExpr.getReference().resolve();
74     if (isLocalFunction(resolved) || isInstanceMethod(resolved)) {
75       return (PyFunction)resolved;
76     }
77     return null;
78   }
79
80   private static boolean isInstanceMethod(@Nullable PsiElement element) {
81     final PyFunction function = as(element, PyFunction.class);
82     if (function == null) {
83       return false;
84     }
85     final PyUtil.MethodFlags flags = PyUtil.MethodFlags.of(function);
86     return flags != null && flags.isInstanceMethod();
87   }
88
89   private static boolean isLocalFunction(@Nullable PsiElement resolved) {
90     return resolved instanceof PyFunction && PsiTreeUtil.getParentOfType(resolved, ScopeOwner.class, true) instanceof PyFunction;
91   }
92
93   @Nullable
94   @Override
95   protected RefactoringActionHandler getHandler(@NotNull DataContext dataContext) {
96     return new RefactoringActionHandler() {
97       @Override
98       public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) {
99         final PsiElement element = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
100         if (element != null) {
101           final PyFunction function = findTargetFunction(element);
102           if (function != null) {
103             final PyBaseMakeFunctionTopLevelProcessor processor;
104             if (isInstanceMethod(function)) {
105               processor = new PyMakeMethodTopLevelProcessor(function, editor);
106             }
107             else {
108               processor = new PyMakeLocalFunctionTopLevelProcessor(function, editor);
109             }
110             try {
111               processor.run();
112             }
113             catch (IncorrectOperationException e) {
114               if (ApplicationManager.getApplication().isUnitTestMode()) {
115                 throw e;
116               }
117               CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("error.title"), e.getMessage(), ID, project);
118             }
119           }
120         }
121       }
122
123       @Override
124       public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) {
125         // should be called only from the editor
126       }
127     };
128   }
129 }