PY-6637 First steps in the initial implementation
authorMikhail Golubev <mikhail.golubev@jetbrains.com>
Sun, 27 Sep 2015 22:11:21 +0000 (01:11 +0300)
committerMikhail Golubev <mikhail.golubev@jetbrains.com>
Mon, 5 Oct 2015 10:08:58 +0000 (13:08 +0300)
python/src/com/jetbrains/python/PyBundle.properties
python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java
python/src/com/jetbrains/python/codeInsight/intentions/PyConvertLocalFunctionToTopLevelFunction.java [new file with mode: 0644]

index ccda8a5b987e08869ff58902dda7ccc2b27f5bd3..892be747aebd26c4845fa4fa459f9d041c41e32b 100644 (file)
@@ -1,4 +1,4 @@
-f### Generic words ###
+f###=Generic words ###
 GNAME.function=function
 GNAME.class=class
 GNAME.var=variable
@@ -270,6 +270,8 @@ INTN.convert.static.method.to.function=Convert static method to function
 #PyConvertMethodToPropertyIntention
 INTN.convert.method.to.property=Convert method to property
 
+INTN.convert.local.function.to.top.level.function=Convert to top-level function
+
 # Conflict checker
 CONFLICT.name.$0.obscured=Name ''{0}'' obscured by local definitions
 CONFLICT.name.$0.obscured.cannot.convert=Name ''{0}'' obscured. Cannot convert.
index eb1f6a0f05e818a965f2d28f5bd44b1ddc0ad80a..25ca14531983100787e1d85f29a7a81aaa9b4939 100644 (file)
@@ -277,6 +277,7 @@ public class PyCodeFragmentUtil {
           for (PsiElement resolved : multiResolve(reference)) {
             if (!subGraphElements.contains(resolved)) {
               result.add(element);
+              break;
             }
           }
         }
diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/PyConvertLocalFunctionToTopLevelFunction.java b/python/src/com/jetbrains/python/codeInsight/intentions/PyConvertLocalFunctionToTopLevelFunction.java
new file mode 100644 (file)
index 0000000..a55f9eb
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2000-2015 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.controlflow.ControlFlow;
+import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
+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.PsiReference;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.util.IncorrectOperationException;
+import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
+import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
+import com.jetbrains.python.psi.PyFile;
+import com.jetbrains.python.psi.PyFunction;
+import com.jetbrains.python.psi.PyUtil;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Mikhail Golubev
+ */
+public class PyConvertLocalFunctionToTopLevelFunction extends BaseIntentionAction {
+  @Nls
+  @NotNull
+  @Override
+  public String getFamilyName() {
+    return PyBundle.message("INTN.convert.local.function.to.top.level.function");
+  }
+
+  @Override
+  public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
+    final PyFunction nestedFunction = findNestedFunctionUnderCaret(editor, file);
+    return nestedFunction != null;
+  }
+
+  @Nullable
+  private static PyFunction findNestedFunctionUnderCaret(Editor editor, PsiFile file) {
+    if (!(file instanceof PyFile)) return null;
+    final PsiElement element = PyUtil.findNonWhitespaceAtOffset(file, editor.getCaretModel().getOffset());
+    if (element == null) {
+      return null;
+    }
+    if (isLocalFunction(element.getParent()) && ((PyFunction)element.getParent()).getNameIdentifier() == element) {
+      return (PyFunction)element.getParent();
+    }
+    final PsiReference reference = element.getReference();
+    if (reference == null) {
+      return null;
+    }
+    final PsiElement resolved = reference.resolve();
+    if (isLocalFunction(resolved)) {
+      return (PyFunction)resolved;
+    }
+    return null;
+  }
+
+  private static boolean isLocalFunction(@Nullable PsiElement resolved) {
+    if (resolved instanceof PyFunction && PsiTreeUtil.getParentOfType(resolved, ScopeOwner.class, true) instanceof PyFunction) {
+      return true;
+    }
+    return false;
+  }
+
+  @Override
+  public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
+    final PyFunction function = findNestedFunctionUnderCaret(editor, file);
+    assert function != null;
+    final ControlFlow flow = ControlFlowCache.getControlFlow(function);
+  }
+}