mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Refactoring to make PushDown use the same engine as other refactorings (need to doc)
This commit is contained in:
@@ -66,7 +66,7 @@ public abstract class MemberInfoBase<T extends PsiElement> {
|
||||
}
|
||||
|
||||
public T getMember() {
|
||||
LOG.assertTrue(myMember.isValid());
|
||||
LOG.assertTrue(myMember.isValid(), myMember.getText() + " is invalid");
|
||||
return myMember;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@ import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* TODO: Document
|
||||
*/
|
||||
public interface UsageViewDescriptor {
|
||||
/**
|
||||
* @return an array of elements whose usages were searched or {@link com.intellij.psi.PsiElement#EMPTY_ARRAY} if not available
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.jetbrains.python.psi;
|
||||
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.intellij.codeInsight.FileModificationService;
|
||||
import com.intellij.codeInsight.completion.PrioritizedLookupElement;
|
||||
@@ -52,6 +53,7 @@ import com.intellij.util.PlatformIcons;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import com.jetbrains.NotNullPredicate;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
@@ -63,6 +65,7 @@ import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import com.jetbrains.python.psi.types.*;
|
||||
import com.jetbrains.python.refactoring.classes.extractSuperclass.PyExtractSuperclassHelper;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -104,19 +107,9 @@ public class PyUtil {
|
||||
return node != null && node.getElementType().equals(TokenType.WHITE_SPACE);
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public static Set<PsiElement> getComments(PsiElement start) {
|
||||
final Set<PsiElement> comments = new HashSet<PsiElement>();
|
||||
PsiElement seeker = start.getPrevSibling();
|
||||
if (seeker == null) seeker = start.getParent().getPrevSibling();
|
||||
while (seeker instanceof PsiWhiteSpace || seeker instanceof PsiComment) {
|
||||
if (seeker instanceof PsiComment) {
|
||||
comments.add(seeker);
|
||||
}
|
||||
seeker = seeker.getPrevSibling();
|
||||
}
|
||||
return comments;
|
||||
//TODO: Doctg
|
||||
public static boolean isInit(@NotNull final PyFunction function) {
|
||||
return PyNames.INIT.equals(function.getName());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -1406,4 +1399,46 @@ public class PyUtil {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private static boolean isObject(@NotNull final PyMemberInfo classMemberInfo) {
|
||||
final PyElement element = classMemberInfo.getMember();
|
||||
if ((element instanceof PyClass) && PyNames.OBJECT.equals(element.getName())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out {@link com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo}
|
||||
* that should not be displayed in this refactoring (like object)
|
||||
*
|
||||
* @param pyMemberInfos collection to sort
|
||||
* @return sorted collection
|
||||
*/
|
||||
@NotNull
|
||||
public static Collection<PyMemberInfo> filterOutObject(@NotNull final Collection<PyMemberInfo> pyMemberInfos) {
|
||||
return Collections2.filter(pyMemberInfos, new ObjectPredicate(false));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filters only pyclass object (new class)
|
||||
*/
|
||||
public static class ObjectPredicate extends NotNullPredicate<PyMemberInfo> {
|
||||
private final boolean myAllowObjects;
|
||||
|
||||
/**
|
||||
* @param allowObjects allows only objects if true. Allows all but objects otherwise.
|
||||
*/
|
||||
public ObjectPredicate(final boolean allowObjects) {
|
||||
myAllowObjects = allowObjects;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applyNotNull(@NotNull final PyMemberInfo input) {
|
||||
return myAllowObjects == isObject(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ import com.jetbrains.python.PythonFileType;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import com.jetbrains.python.psi.PyElementGenerator;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.PyUtil;
|
||||
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
@@ -15,19 +15,20 @@
|
||||
*/
|
||||
package com.jetbrains.python.refactoring.classes;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.intellij.lang.injection.InjectedLanguageManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtilBase;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.psi.util.QualifiedName;
|
||||
import com.jetbrains.NotNullPredicate;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.PythonFileType;
|
||||
import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
|
||||
import com.jetbrains.python.codeInsight.imports.AddImportHelper;
|
||||
import com.jetbrains.python.psi.*;
|
||||
@@ -53,160 +54,99 @@ public class PyClassRefactoringUtil {
|
||||
private static final Key<Boolean> ENCODED_USE_FROM_IMPORT = Key.create("PyEncodedUseFromImport");
|
||||
private static final Key<String> ENCODED_IMPORT_AS = Key.create("PyEncodedImportAs");
|
||||
|
||||
private static final InitFirst INIT_FIRST = new InitFirst();
|
||||
|
||||
private PyClassRefactoringUtil() {
|
||||
}
|
||||
|
||||
public static void moveSuperclasses(PyClass clazz, Set<String> superClasses, PyClass superClass) {
|
||||
if (superClasses.size() == 0) return;
|
||||
final Project project = clazz.getProject();
|
||||
final List<PyExpression> toAdd = removeAndGetSuperClasses(clazz, superClasses);
|
||||
addSuperclasses(project, superClass, toAdd, superClasses);
|
||||
}
|
||||
|
||||
public static void addSuperclasses(Project project,
|
||||
PyClass superClass,
|
||||
@Nullable Collection<PyExpression> superClassesAsPsi,
|
||||
Collection<String> superClassesAsStrings) {
|
||||
if (superClassesAsStrings.size() == 0) return;
|
||||
PyArgumentList argList = superClass.getSuperClassExpressionList();
|
||||
if (argList != null) {
|
||||
if (superClassesAsPsi != null) {
|
||||
for (PyExpression element : superClassesAsPsi) {
|
||||
argList.addArgument(element);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (String s : superClassesAsStrings) {
|
||||
argList.addArgument(PyElementGenerator.getInstance(project).createExpressionFromText(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
addSuperclasses(project, superClass, superClassesAsStrings);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes super classes by name and returns list of removed
|
||||
* TODO: Doc
|
||||
* Copies class field declarations to some other place
|
||||
*
|
||||
* @param clazz class to find super classes to remove
|
||||
* @param superClassesToRemove list of super class names
|
||||
* @return list of removed classes
|
||||
* @param assignmentStatement list of class fields
|
||||
*/
|
||||
@NotNull
|
||||
public static List<PyExpression> removeAndGetSuperClasses(@NotNull PyClass clazz, @NotNull Set<String> superClassesToRemove) {
|
||||
if (superClassesToRemove.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final List<PyExpression> result = new ArrayList<PyExpression>();
|
||||
for (final PyExpression superClassExpression : clazz.getSuperClassExpressions()) {
|
||||
if (superClassesToRemove.contains(superClassExpression.getText())) {
|
||||
result.add(superClassExpression);
|
||||
superClassExpression.delete();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void addSuperclasses(Project project, PyClass superClass, Collection<String> superClasses) {
|
||||
if (superClasses.size() == 0) return;
|
||||
final StringBuilder builder = new StringBuilder("(");
|
||||
boolean hasChanges = false;
|
||||
for (String element : superClasses) {
|
||||
if (builder.length() > 1) builder.append(",");
|
||||
if (!alreadyHasSuperClass(superClass, element)) {
|
||||
builder.append(element);
|
||||
hasChanges = true;
|
||||
}
|
||||
}
|
||||
builder.append(")");
|
||||
if (!hasChanges) return;
|
||||
|
||||
final PsiFile file =
|
||||
PsiFileFactory.getInstance(project).createFileFromText(superClass.getName() + "temp", PythonFileType.INSTANCE, builder.toString());
|
||||
final PsiElement expression = file.getFirstChild().getFirstChild();
|
||||
PsiElement colon = superClass.getFirstChild();
|
||||
while (colon != null && !colon.getText().equals(":")) {
|
||||
colon = colon.getNextSibling();
|
||||
}
|
||||
LOG.assertTrue(colon != null && expression != null);
|
||||
PyPsiUtils.addBeforeInParent(colon, expression);
|
||||
}
|
||||
|
||||
private static boolean alreadyHasSuperClass(PyClass superClass, String className) {
|
||||
for (PyClass aClass : superClass.getSuperClasses()) {
|
||||
if (Comparing.strEqual(aClass.getName(), className)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves class field declarations to some other place
|
||||
* @param expressions list of class fields
|
||||
* @param superClass where to move them
|
||||
*/
|
||||
public static void moveFieldDeclarationToStatement(@NotNull final Collection<PyTargetExpression> expressions,
|
||||
@NotNull final PyStatementList superClassStatement) {
|
||||
for (final PyTargetExpression expression : expressions) {
|
||||
final PyAssignmentStatement expAssignmentStatement = PsiTreeUtil.getParentOfType(expression, PyAssignmentStatement.class);
|
||||
assert expAssignmentStatement != null: "Target expression has no assignment statement";
|
||||
PyUtil.addElementToStatementList(expAssignmentStatement.copy(), superClassStatement, true);
|
||||
expAssignmentStatement.delete();
|
||||
public static List<PyAssignmentStatement> copyFieldDeclarationToStatement(@NotNull final Collection<PyAssignmentStatement> assignmentStatement,
|
||||
@NotNull final PyStatementList superClassStatement) {
|
||||
List<PyAssignmentStatement> declations = new ArrayList<PyAssignmentStatement>(assignmentStatement.size());
|
||||
for (final PyAssignmentStatement expression : assignmentStatement) {
|
||||
PyAssignmentStatement newDeclaration = (PyAssignmentStatement)expression.copy();
|
||||
declations.add(newDeclaration);
|
||||
declations.add((PyAssignmentStatement)PyUtil.addElementToStatementList(newDeclaration, superClassStatement, true));
|
||||
PyPsiUtils.removeRedundantPass(superClassStatement);
|
||||
}
|
||||
|
||||
return declations;
|
||||
}
|
||||
public static void moveMethods(Collection<PyFunction> methods, PyClass superClass) {
|
||||
if (methods.size() == 0) return;
|
||||
|
||||
public static List<PyFunction> copyMethods(Collection<PyFunction> methods, PyClass superClass) {
|
||||
if (methods.size() == 0) return null;
|
||||
for (PsiElement e : methods) {
|
||||
rememberNamedReferences(e);
|
||||
}
|
||||
final PyElement[] elements = methods.toArray(new PyElement[methods.size()]);
|
||||
addMethods(superClass, elements, true);
|
||||
removeMethodsWithComments(elements);
|
||||
final PyFunction[] elements = methods.toArray(new PyFunction[methods.size()]);
|
||||
return addMethods(superClass, elements);
|
||||
}
|
||||
|
||||
private static void removeMethodsWithComments(PyElement[] elements) {
|
||||
for (PyElement element : elements) {
|
||||
final Set<PsiElement> comments = PyUtil.getComments(element);
|
||||
if (comments.size() > 0) {
|
||||
PyPsiUtils.removeElements(PsiUtilCore.toPsiElementArray(comments));
|
||||
//TODO: Doct
|
||||
@NotNull
|
||||
public static List<PyFunction> addMethods(@NotNull PyClass destination, @NotNull PyFunction... methods) {
|
||||
List<PyFunction> methodsToAdd = new ArrayList<PyFunction>(Arrays.asList(methods));
|
||||
Collections.sort(methodsToAdd, INIT_FIRST);
|
||||
|
||||
PyStatementList destStatementList = destination.getStatementList();
|
||||
List<PyFunction> newlyCreatedMethods = new ArrayList<PyFunction>(methods.length);
|
||||
|
||||
for (PyFunction method : methodsToAdd) {
|
||||
|
||||
if (destination.findMethodByName(method.getName(), false) != null) {
|
||||
continue; //TODO: Doc why
|
||||
}
|
||||
|
||||
|
||||
PyFunction newMethod = insertMethodInProperPlace(destStatementList, method);
|
||||
newlyCreatedMethods.add(newMethod);
|
||||
restoreNamedReferences(newMethod);
|
||||
}
|
||||
|
||||
PyPsiUtils.removeRedundantPass(destStatementList);
|
||||
return newlyCreatedMethods;
|
||||
}
|
||||
|
||||
//TODO: Doc algo
|
||||
@NotNull
|
||||
private static PyFunction insertMethodInProperPlace(
|
||||
@NotNull PyStatementList destStatementList,
|
||||
@NotNull PyFunction method) {
|
||||
boolean methodIsInit = PyUtil.isInit(method);
|
||||
if (!methodIsInit) {
|
||||
//Not init method could be inserted in the bottom
|
||||
return (PyFunction)destStatementList.add(method);
|
||||
}
|
||||
|
||||
//We should find appropriate place for init
|
||||
for (PsiElement element : destStatementList.getChildren()) {
|
||||
boolean elementComment = element instanceof PyExpressionStatement;
|
||||
boolean elementClassField = element instanceof PyAssignmentStatement;
|
||||
|
||||
if ((!(elementComment || elementClassField))) {
|
||||
return (PyFunction)destStatementList.addBefore(method, element);
|
||||
}
|
||||
}
|
||||
PyPsiUtils.removeElements(elements);
|
||||
return (PyFunction)destStatementList.add(method);
|
||||
}
|
||||
|
||||
public static <T extends PyElement & PyStatementListContainer>void insertPassIfNeeded(@NotNull T element) {
|
||||
|
||||
public static <T extends PyElement & PyStatementListContainer> void insertPassIfNeeded(@NotNull T element) {
|
||||
final PyStatementList statements = element.getStatementList();
|
||||
if (statements.getStatements().length == 0) {
|
||||
statements.add(
|
||||
PyElementGenerator.getInstance(element.getProject()).createFromText(LanguageLevel.getDefault(), PyPassStatement.class, PyNames.PASS));
|
||||
PyElementGenerator.getInstance(element.getProject())
|
||||
.createFromText(LanguageLevel.getDefault(), PyPassStatement.class, PyNames.PASS)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static void addMethods(final PyClass superClass, final PyElement[] elements, final boolean up) {
|
||||
if (elements.length == 0) return;
|
||||
final PyStatementList statements = superClass.getStatementList();
|
||||
for (PyElement newStatement : elements) {
|
||||
if (up && newStatement instanceof PyFunction) {
|
||||
final String name = newStatement.getName();
|
||||
if (name != null && superClass.findMethodByName(name, false) != null) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (newStatement instanceof PyExpressionStatement && newStatement.getFirstChild() instanceof PyStringLiteralExpression) continue;
|
||||
final PsiElement anchor = statements.add(newStatement);
|
||||
restoreNamedReferences(anchor);
|
||||
final Set<PsiElement> comments = PyUtil.getComments(newStatement);
|
||||
for (PsiElement comment : comments) {
|
||||
statements.addBefore(comment, anchor);
|
||||
}
|
||||
}
|
||||
PyPsiUtils.removeRedundantPass(statements);
|
||||
}
|
||||
|
||||
public static void restoreNamedReferences(@NotNull PsiElement element) {
|
||||
restoreNamedReferences(element, null);
|
||||
}
|
||||
@@ -231,6 +171,7 @@ public class PyClassRefactoringUtil {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static void restoreReference(final PyReferenceExpression node) {
|
||||
PsiNamedElement target = node.getCopyableUserData(ENCODED_IMPORT);
|
||||
final String asName = node.getCopyableUserData(ENCODED_IMPORT_AS);
|
||||
@@ -443,9 +384,10 @@ public class PyClassRefactoringUtil {
|
||||
|
||||
/**
|
||||
* Creates class method
|
||||
* @param methodName name if new method (be sure to check {@link com.jetbrains.python.PyNames} for special methods)
|
||||
* @param pyClass class to add method
|
||||
* @param modifier if method static or class or simple instance method (null)>
|
||||
*
|
||||
* @param methodName name if new method (be sure to check {@link com.jetbrains.python.PyNames} for special methods)
|
||||
* @param pyClass class to add method
|
||||
* @param modifier if method static or class or simple instance method (null)>
|
||||
* @param parameterNames method parameters
|
||||
* @return newly created method
|
||||
*/
|
||||
@@ -473,8 +415,55 @@ public class PyClassRefactoringUtil {
|
||||
builder.parameter(parameterName);
|
||||
}
|
||||
|
||||
final PyFunction function = builder.addFunction(pyClass.getStatementList(), LanguageLevel.getDefault());
|
||||
addMethods(pyClass, new PyElement[]{function}, true);
|
||||
return function;
|
||||
final PyFunction function = builder.buildFunction(pyClass.getProject(), LanguageLevel.getDefault());
|
||||
return addMethods(pyClass, function).get(0);
|
||||
}
|
||||
|
||||
//TODO: Doc
|
||||
@NotNull
|
||||
public static void addSuperclasses(@NotNull final Project project,
|
||||
@NotNull final PyClass clazz,
|
||||
@NotNull final PyClass... superClasses) {
|
||||
|
||||
final List<String> superClassNames = new ArrayList<String>();
|
||||
|
||||
|
||||
for (final PyClass superClass : Collections2.filter(Arrays.asList(superClasses), NotNullPredicate.INSTANCE)) {
|
||||
if (superClass.getName() != null) {
|
||||
superClassNames.add(superClass.getName());
|
||||
PyClassRefactoringUtil.insertImport(clazz, superClass);
|
||||
}
|
||||
}
|
||||
|
||||
PyArgumentList superClassExpressionList = clazz.getSuperClassExpressionList();
|
||||
PyElementGenerator generator = PyElementGenerator.getInstance(project);
|
||||
|
||||
if (superClassExpressionList != null) {
|
||||
for (String superClassName : superClassNames) {
|
||||
superClassExpressionList.addArgument(generator.createExpressionFromText(superClassName));
|
||||
}
|
||||
}
|
||||
//TODO: Doc why we do it manually
|
||||
else {
|
||||
String superClassText = String.format("(%s)", StringUtil.join(superClassNames, ","));
|
||||
clazz.addAfter(generator.createExpressionFromText(superClassText),
|
||||
clazz.getNameNode().getPsi());
|
||||
}
|
||||
}
|
||||
|
||||
private static class NameExtractor implements Function<PyElement, String> {
|
||||
@SuppressWarnings("NullableProblems") //We sure collection has no null
|
||||
@Nullable
|
||||
@Override
|
||||
public String apply(@NotNull final PyElement input) {
|
||||
return input.getName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class InitFirst implements Comparator<PyFunction> {
|
||||
@Override
|
||||
public int compare(PyFunction o1, PyFunction o2) {
|
||||
return (PyUtil.isInit(o1) ? 1 : 0) - (PyUtil.isInit(o2) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -21,6 +21,7 @@ import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyUtil;
|
||||
import com.jetbrains.python.refactoring.classes.PyClassRefactoringHandler;
|
||||
import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
|
||||
import com.jetbrains.python.vp.Creator;
|
||||
@@ -40,7 +41,7 @@ public class PyExtractSuperclassHandler extends PyClassRefactoringHandler {
|
||||
@NotNull final PyMemberInfoStorage infoStorage,
|
||||
@NotNull final Editor editor) {
|
||||
//TODO: Move to presenter
|
||||
if (PyExtractSuperclassHelper.filterOutDeniedMembers(infoStorage.getClassMemberInfos(classUnderRefactoring)).isEmpty()) {
|
||||
if (PyUtil.filterOutObject(infoStorage.getClassMemberInfos(classUnderRefactoring)).isEmpty()) {
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, PyBundle
|
||||
.message("refactoring.extract.super.class.no.members.allowed"), RefactoringBundle.message("extract.superclass.elements.header"),
|
||||
null);
|
||||
|
||||
+4
-48
@@ -33,10 +33,7 @@ import com.intellij.util.PathUtil;
|
||||
import com.jetbrains.NotNullPredicate;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.PythonFileType;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.psi.PyElementGenerator;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.MembersManager;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
@@ -55,7 +52,7 @@ public final class PyExtractSuperclassHelper {
|
||||
/**
|
||||
* Accepts only those members whose element is PyClass object (new classes)
|
||||
*/
|
||||
private static final Predicate<PyMemberInfo> ALLOW_OBJECT = new ObjectPredicate(true);
|
||||
private static final Predicate<PyMemberInfo> ALLOW_OBJECT = new PyUtil.ObjectPredicate(true);
|
||||
|
||||
private PyExtractSuperclassHelper() {
|
||||
}
|
||||
@@ -81,8 +78,8 @@ public final class PyExtractSuperclassHelper {
|
||||
|
||||
newClass = placeNewClass(project, newClass, clazz, targetFile);
|
||||
|
||||
MembersManager.moveAllMembers(clazz, newClass, selectedMemberInfos);
|
||||
PyClassRefactoringUtil.addSuperclasses(project, clazz, null, Collections.singleton(superBaseName));
|
||||
MembersManager.moveAllMembers(selectedMemberInfos, clazz, newClass);
|
||||
PyClassRefactoringUtil.addSuperclasses(project, clazz, null, newClass);
|
||||
|
||||
}
|
||||
|
||||
@@ -226,45 +223,4 @@ public final class PyExtractSuperclassHelper {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
private static boolean isObject(@NotNull final PyMemberInfo classMemberInfo) {
|
||||
final PyElement element = classMemberInfo.getMember();
|
||||
if ((element instanceof PyClass) && PyNames.OBJECT.equals(element.getName())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out {@link com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo}
|
||||
* that should not be displayed in this refactoring (like object)
|
||||
*
|
||||
* @param pyMemberInfos collection to sort
|
||||
* @return sorted collection
|
||||
*/
|
||||
@NotNull
|
||||
static Collection<PyMemberInfo> filterOutDeniedMembers(@NotNull final Collection<PyMemberInfo> pyMemberInfos) {
|
||||
return Collections2.filter(pyMemberInfos, new ObjectPredicate(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters only pyclass object (new class)
|
||||
*/
|
||||
private static class ObjectPredicate extends NotNullPredicate<PyMemberInfo> {
|
||||
private final boolean myAllowObjects;
|
||||
|
||||
/**
|
||||
* @param allowObjects allows only objects if true. Allows all but objects otherwise.
|
||||
*/
|
||||
private ObjectPredicate(final boolean allowObjects) {
|
||||
myAllowObjects = allowObjects;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applyNotNull(@NotNull final PyMemberInfo input) {
|
||||
return myAllowObjects == isObject(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -13,6 +13,7 @@ import com.intellij.util.containers.MultiMap;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.PythonLanguage;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyUtil;
|
||||
import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.vp.BadDataException;
|
||||
@@ -75,7 +76,7 @@ class PyExtractSuperclassPresenterImpl extends MembersBasedPresenterNoPreviewImp
|
||||
final String defaultFilePath = FileUtil.toSystemDependentName(myClassUnderRefactoring.getContainingFile().getVirtualFile().getPath());
|
||||
final VirtualFile[] roots = ProjectRootManager.getInstance(myClassUnderRefactoring.getProject()).getContentRoots();
|
||||
final Collection<PyMemberInfo> pyMemberInfos =
|
||||
PyExtractSuperclassHelper.filterOutDeniedMembers(myStorage.getClassMemberInfos(myClassUnderRefactoring));
|
||||
PyUtil.filterOutObject(myStorage.getClassMemberInfos(myClassUnderRefactoring));
|
||||
myView.configure(
|
||||
new PyExtractSuperclassInitializationInfo(new PyExtractSuperclassInfoModel(myClassUnderRefactoring), pyMemberInfos, defaultFilePath,
|
||||
roots));
|
||||
|
||||
+10
-4
@@ -1,5 +1,6 @@
|
||||
package com.jetbrains.python.refactoring.classes.membersManager;
|
||||
|
||||
import com.jetbrains.python.psi.PyAssignmentStatement;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyTargetExpression;
|
||||
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
|
||||
@@ -21,10 +22,15 @@ class ClassFieldsManager extends FieldsManager {
|
||||
|
||||
|
||||
@Override
|
||||
protected void moveMembers(@NotNull final PyClass from,
|
||||
@NotNull final PyClass to,
|
||||
@NotNull final Collection<PyTargetExpression> members) {
|
||||
PyClassRefactoringUtil.moveFieldDeclarationToStatement(members, to.getStatementList());
|
||||
protected void moveAssignments(@NotNull PyClass from,
|
||||
@NotNull Collection<PyAssignmentStatement> statements,
|
||||
@NotNull PyClass... to) {
|
||||
//TODO: Copy/paste with InstanceFieldsManager. Move to parent?
|
||||
for (PyClass destClass : to) {
|
||||
PyClassRefactoringUtil.copyFieldDeclarationToStatement(statements, destClass.getStatementList());
|
||||
}
|
||||
deleteElements(statements);
|
||||
PyClassRefactoringUtil.insertPassIfNeeded(from);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.jetbrains.python.refactoring.classes.membersManager;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.NotNullPredicate;
|
||||
import com.jetbrains.python.psi.PyAssignmentStatement;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
@@ -11,6 +13,7 @@ import com.jetbrains.python.psi.PyTargetExpression;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -36,6 +39,18 @@ abstract class FieldsManager extends MembersManager<PyTargetExpression> {
|
||||
return Lists.<PyElement>newArrayList(Collections2.filter(getFieldsByClass(pyClass), SIMPLE_ASSIGNMENTS_ONLY));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected void moveMembers(@NotNull PyClass from,
|
||||
@NotNull Collection<PyTargetExpression> members,
|
||||
@NotNull PyClass... to) {
|
||||
moveAssignments(from, Collections2.transform(members, new AssignmentTransform()), to);
|
||||
}
|
||||
|
||||
protected abstract void moveAssignments(@NotNull PyClass from,
|
||||
@NotNull Collection<PyAssignmentStatement> statements,
|
||||
@NotNull PyClass... to);
|
||||
|
||||
/**
|
||||
* Checks if class has fields. Only child may know how to obtain field
|
||||
*
|
||||
@@ -84,4 +99,12 @@ abstract class FieldsManager extends MembersManager<PyTargetExpression> {
|
||||
return (parent != null) && PyAssignmentStatement.class.isAssignableFrom(parent.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class AssignmentTransform implements Function<PyTargetExpression, PyAssignmentStatement> {
|
||||
@Override
|
||||
public PyAssignmentStatement apply(PyTargetExpression input) {
|
||||
return PsiTreeUtil.getParentOfType(input, PyAssignmentStatement.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
-10
@@ -5,7 +5,9 @@ import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -18,22 +20,35 @@ class InstanceFieldsManager extends FieldsManager {
|
||||
|
||||
|
||||
@Override
|
||||
protected void moveMembers(@NotNull final PyClass from,
|
||||
@NotNull final PyClass to,
|
||||
@NotNull final Collection<PyTargetExpression> members) {
|
||||
protected void moveAssignments(@NotNull PyClass from,
|
||||
@NotNull Collection<PyAssignmentStatement> statements,
|
||||
@NotNull PyClass... to) {
|
||||
//TODO: Copy/paste with ClassFieldsManager. Move to parent?
|
||||
|
||||
for (PyClass destClass : to) {
|
||||
copyInstanceFields(statements, destClass);
|
||||
}
|
||||
|
||||
deleteElements(statements);
|
||||
|
||||
final PyFunction fromInitMethod = from.findMethodByName(PyNames.INIT, false);
|
||||
if (fromInitMethod != null) {
|
||||
//We can't leave class constructor with empty body
|
||||
PyClassRefactoringUtil.insertPassIfNeeded(fromInitMethod);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Doc
|
||||
@NotNull
|
||||
private static List<PyAssignmentStatement> copyInstanceFields(@NotNull final Collection<PyAssignmentStatement> members,
|
||||
@NotNull final PyClass to) {
|
||||
//We need __init__ method, and if there is no any -- we need to create it
|
||||
PyFunction toInitMethod = to.findMethodByName(PyNames.INIT, false);
|
||||
if (toInitMethod == null) {
|
||||
toInitMethod = PyClassRefactoringUtil.createMethod(PyNames.INIT, to, null);
|
||||
}
|
||||
final PyStatementList statementList = toInitMethod.getStatementList();
|
||||
PyClassRefactoringUtil.moveFieldDeclarationToStatement(members, statementList);
|
||||
|
||||
final PyFunction fromInitMethod = from.findMethodByName(PyNames.INIT, false);
|
||||
if (fromInitMethod !=null) {
|
||||
//We can't leave class constructor with empty body
|
||||
PyClassRefactoringUtil.insertPassIfNeeded(fromInitMethod);
|
||||
}
|
||||
return PyClassRefactoringUtil.copyFieldDeclarationToStatement(members, statementList);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+38
-21
@@ -6,23 +6,23 @@ import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiNamedElement;
|
||||
import com.jetbrains.NotNullPredicate;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.psi.PyUtil;
|
||||
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* TODO: Extract "from, to[], members, deleteFromOrigin" to separate class?
|
||||
* Moves members between classes via its plugins (managers).
|
||||
* To move members use {@link #getAllMembersCouldBeMoved(com.jetbrains.python.psi.PyClass)} and {@link #moveMembers(com.jetbrains.python.psi.PyClass, com.jetbrains.python.psi.PyClass, java.util.Collection)}
|
||||
* To move members use {@link #getAllMembersCouldBeMoved(com.jetbrains.python.psi.PyClass)} and {@link #moveMembers(java.util.Collection, com.jetbrains.python.psi.PyClass, com.jetbrains.python.psi.PyClass)}
|
||||
* To add new manager, extend this class and add it to {@link #MANAGERS}
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
@@ -68,15 +68,18 @@ public abstract class MembersManager<T extends PyElement> implements Function<T,
|
||||
|
||||
|
||||
/**
|
||||
* TODO: Doc deleteFromOrigin
|
||||
* Moves members from one class to another
|
||||
*
|
||||
* @param memberInfos members to move
|
||||
* @param from source
|
||||
* @param to destination
|
||||
* @param memberInfos members to move
|
||||
*/
|
||||
public static void moveAllMembers(@NotNull final PyClass from,
|
||||
@NotNull final PyClass to,
|
||||
@NotNull final Collection<PyMemberInfo> memberInfos) {
|
||||
public static void moveAllMembers(
|
||||
@NotNull final Collection<PyMemberInfo> memberInfos,
|
||||
@NotNull final PyClass from,
|
||||
@NotNull final PyClass... to
|
||||
) {
|
||||
final Multimap<MembersManager<?>, PyMemberInfo> managerToMember = ArrayListMultimap.create();
|
||||
//Collect map (manager)->(list_of_memebers)
|
||||
for (final PyMemberInfo memberInfo : memberInfos) {
|
||||
@@ -84,20 +87,23 @@ public abstract class MembersManager<T extends PyElement> implements Function<T,
|
||||
}
|
||||
//Move members via manager
|
||||
for (final MembersManager<?> membersManager : managerToMember.keySet()) {
|
||||
moveSafely(from, to, membersManager, Collections2.transform(managerToMember.get(membersManager), PY_MEMBER_EXTRACTOR));
|
||||
Collection<PyElement> elementsToMove = Collections2.transform(managerToMember.get(membersManager), PY_MEMBER_EXTRACTOR);
|
||||
moveSafely(from, membersManager, elementsToMove, to);
|
||||
}
|
||||
PyClassRefactoringUtil.insertPassIfNeeded(from);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"}) //We check classes at runtime
|
||||
private static void moveSafely(@NotNull final PyClass from,
|
||||
@NotNull final PyClass to,
|
||||
@NotNull final MembersManager<?> manager,
|
||||
@NotNull final Collection<PyElement> elementsToMove) {
|
||||
private static void moveSafely(
|
||||
@NotNull final PyClass from,
|
||||
@NotNull final MembersManager<?> manager,
|
||||
@NotNull final Collection<PyElement> elementsToMove,
|
||||
@NotNull final PyClass... to) {
|
||||
manager.checkElementTypes(elementsToMove);
|
||||
manager.moveMembers(from, to, (Collection)elementsToMove);
|
||||
manager.moveMembers(from, (Collection)elementsToMove, to);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks that all elements has allowed type for manager
|
||||
*
|
||||
@@ -171,13 +177,17 @@ public abstract class MembersManager<T extends PyElement> implements Function<T,
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves element from one class to another
|
||||
*
|
||||
* @param from source
|
||||
* @param to destination
|
||||
* @param members collection of memebrs to move
|
||||
* Moves element from one class to another. Returns newly instered elements
|
||||
* <p/>
|
||||
* TODO: Doc
|
||||
* TODO: Doc who and what should return (write about elemnents)
|
||||
*/
|
||||
protected abstract void moveMembers(@NotNull PyClass from, @NotNull PyClass to, @NotNull Collection<T> members);
|
||||
@NotNull
|
||||
protected abstract void moveMembers(
|
||||
@NotNull PyClass from,
|
||||
@NotNull Collection<T> members,
|
||||
@NotNull PyClass... to);
|
||||
|
||||
|
||||
/**
|
||||
* Creates {@link com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo} from {@link com.jetbrains.python.psi.PyElement}
|
||||
@@ -191,6 +201,13 @@ public abstract class MembersManager<T extends PyElement> implements Function<T,
|
||||
@Override
|
||||
public abstract PyMemberInfo apply(@NotNull T input);
|
||||
|
||||
//TODO: Doct
|
||||
protected static void deleteElements(@NotNull Collection<? extends PsiElement> pyElementsToDelete) {
|
||||
for (PsiElement element : pyElementsToDelete) {
|
||||
element.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private static class PyMemberExtractor implements Function<PyMemberInfo, PyElement> {
|
||||
@SuppressWarnings("NullableProblems") //IDEA-120100
|
||||
@Override
|
||||
|
||||
+17
-2
@@ -6,6 +6,7 @@ import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -28,8 +29,22 @@ class MethodsManager extends MembersManager<PyFunction> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void moveMembers(@NotNull final PyClass from, @NotNull final PyClass to, @NotNull final Collection<PyFunction> members) {
|
||||
PyClassRefactoringUtil.moveMethods(members, to);
|
||||
protected void moveMembers(@NotNull PyClass from,
|
||||
@NotNull Collection<PyFunction> members,
|
||||
@NotNull PyClass... to) {
|
||||
for (PyClass destClass : to) {
|
||||
//We move copies here becase we there may be several destinations
|
||||
List<PyFunction> copies = new ArrayList<PyFunction>(members.size());
|
||||
for (PyFunction member : members) {
|
||||
PyFunction newMethod = (PyFunction)member.copy();
|
||||
copies.add(newMethod);
|
||||
}
|
||||
|
||||
PyClassRefactoringUtil.copyMethods(copies, destClass);
|
||||
}
|
||||
deleteElements(members);
|
||||
|
||||
PyClassRefactoringUtil.insertPassIfNeeded(from);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+29
-16
@@ -1,5 +1,6 @@
|
||||
package com.jetbrains.python.refactoring.classes.membersManager;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.usageView.UsageViewDescriptor;
|
||||
@@ -11,6 +12,8 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* TODO: Doc (and why it extends 2 classes)
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public abstract class PyMembersRefactoringBaseProcessor extends BaseRefactoringProcessor implements UsageViewDescriptor {
|
||||
@@ -18,17 +21,19 @@ public abstract class PyMembersRefactoringBaseProcessor extends BaseRefactoringP
|
||||
@NotNull
|
||||
protected final Collection<PyMemberInfo> myMembersToMove;
|
||||
@NotNull
|
||||
protected final PyClass myTo;
|
||||
@NotNull
|
||||
protected final PyClass myFrom;
|
||||
@NotNull
|
||||
private final PyClass[] myTo;
|
||||
|
||||
protected PyMembersRefactoringBaseProcessor(@NotNull final PyClass from,
|
||||
@NotNull final PyClass to,
|
||||
@NotNull final Collection<PyMemberInfo> membersToMove) {
|
||||
//TODO: Doc
|
||||
protected PyMembersRefactoringBaseProcessor(
|
||||
@NotNull final Collection<PyMemberInfo> membersToMove,
|
||||
@NotNull final PyClass from,
|
||||
@NotNull final PyClass... to) {
|
||||
super(from.getProject());
|
||||
myFrom = from;
|
||||
myTo = to;
|
||||
myMembersToMove = new ArrayList<PyMemberInfo>(membersToMove);
|
||||
myTo = to.clone();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -39,24 +44,32 @@ public abstract class PyMembersRefactoringBaseProcessor extends BaseRefactoringP
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected UsageInfo[] findUsages() {
|
||||
final List<PyUsageInfo> result = new ArrayList<PyUsageInfo>(myMembersToMove.size());
|
||||
for (final PyMemberInfo pyMemberInfo : myMembersToMove) {
|
||||
result.add(new PyUsageInfo(pyMemberInfo));
|
||||
}
|
||||
return result.toArray(new UsageInfo[result.size()]);
|
||||
public PsiElement[] getElements() {
|
||||
return myTo.clone();
|
||||
}
|
||||
|
||||
//TODO: Doc
|
||||
@NotNull
|
||||
@Override
|
||||
protected void performRefactoring(final UsageInfo[] usages) {
|
||||
final Collection<PyMemberInfo> membersToMoveFromUsage = new ArrayList<PyMemberInfo>(usages.length);
|
||||
protected final PyUsageInfo[] findUsages() {
|
||||
final List<PyUsageInfo> result = new ArrayList<PyUsageInfo>(myTo.length);
|
||||
for (final PyClass pyDestinationClass : myTo) {
|
||||
result.add(new PyUsageInfo(pyDestinationClass));
|
||||
}
|
||||
return result.toArray(new PyUsageInfo[result.size()]);
|
||||
}
|
||||
|
||||
//TODO: Doc
|
||||
@Override
|
||||
protected final void performRefactoring(final UsageInfo[] usages) {
|
||||
final Collection<PyClass> destinations = new ArrayList<PyClass>(usages.length);
|
||||
for (final UsageInfo usage : usages) {
|
||||
if (!(usage instanceof PyUsageInfo)) {
|
||||
throw new IllegalArgumentException("Only PyUsageInfo is accepted here");
|
||||
}
|
||||
//TODO: Doc
|
||||
membersToMoveFromUsage.add(((PyUsageInfo)usage).getPyMemberInfo());
|
||||
destinations.add(((PyUsageInfo)usage).getTo());
|
||||
}
|
||||
MembersManager.moveAllMembers(myFrom, myTo, membersToMoveFromUsage);
|
||||
MembersManager.moveAllMembers(myMembersToMove, myFrom, destinations.toArray(new PyClass[destinations.size()]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
package com.jetbrains.python.refactoring.classes.membersManager;
|
||||
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* TODO: Make it generic to allow to reuse in another projects?
|
||||
* TODO: Document
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
class PyUsageInfo extends UsageInfo {
|
||||
@NotNull
|
||||
private final PyMemberInfo myPyMemberInfo;
|
||||
private final PyClass myTo;
|
||||
|
||||
PyUsageInfo(@NotNull final PyMemberInfo pyMemberInfo) {
|
||||
super(pyMemberInfo.getMember(), true);
|
||||
myPyMemberInfo = pyMemberInfo;
|
||||
PyUsageInfo(@NotNull PyClass to) {
|
||||
super(to, true); //TODO: Make super generic and get rid of field?
|
||||
this.myTo = to;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
PyMemberInfo getPyMemberInfo() {
|
||||
return myPyMemberInfo;
|
||||
public PyClass getTo() {
|
||||
return myTo;
|
||||
}
|
||||
}
|
||||
|
||||
+15
-25
@@ -1,19 +1,17 @@
|
||||
package com.jetbrains.python.refactoring.classes.membersManager;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.psi.PsiNamedElement;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.jetbrains.NotNullPredicate;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.psi.PyExpression;
|
||||
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
|
||||
import com.jetbrains.python.refactoring.classes.ui.PyClassCellRenderer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Plugin that moves superclasses from one class to another
|
||||
@@ -25,7 +23,6 @@ class SuperClassesManager extends MembersManager<PyClass> {
|
||||
super(PyClass.class);
|
||||
}
|
||||
|
||||
private static final NameExtractor NAME_EXTRACTOR = new NameExtractor();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
@@ -34,16 +31,18 @@ class SuperClassesManager extends MembersManager<PyClass> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void moveMembers(@NotNull final PyClass from, @NotNull final PyClass to, @NotNull final Collection<PyClass> members) {
|
||||
final Set<String> superClassesToMove =
|
||||
Sets.newHashSet(Collections2.filter(Collections2.transform(members, NAME_EXTRACTOR), NotNullPredicate.INSTANCE));
|
||||
|
||||
for (final PyElement member : members) {
|
||||
superClassesToMove.add(member.getName());
|
||||
protected void moveMembers(@NotNull PyClass from, @NotNull Collection<PyClass> members, @NotNull PyClass... to) {
|
||||
for (PyClass destClass : to) {
|
||||
PyClassRefactoringUtil.addSuperclasses(from.getProject(), destClass, members.toArray(new PyClass[members.size()]));
|
||||
}
|
||||
|
||||
PyClassRefactoringUtil.moveSuperclasses(from, superClassesToMove, to);
|
||||
PyClassRefactoringUtil.insertImport(to, new ArrayList<PsiNamedElement>(members));
|
||||
for (PyExpression expression : from.getSuperClassExpressions()) {
|
||||
for (PyClass member : members) {
|
||||
if (expression.getText().equals(member.getName())) {
|
||||
expression.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -53,13 +52,4 @@ class SuperClassesManager extends MembersManager<PyClass> {
|
||||
//TODO: Check for "overrides"
|
||||
return new PyMemberInfo(input, false, name, false, this);
|
||||
}
|
||||
|
||||
private static class NameExtractor implements Function<PyElement, String> {
|
||||
@SuppressWarnings("NullableProblems") //We sure collection has no null
|
||||
@Nullable
|
||||
@Override
|
||||
public String apply(@NotNull final PyElement input) {
|
||||
return input.getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Incapsulates knowledge about class members that could be moved to some other class.
|
||||
* To use (get list of members to move or actually move them) use {@link com.jetbrains.python.refactoring.classes.membersManager.MembersManager#getAllMembersCouldBeMoved(com.jetbrains.python.psi.PyClass)}
|
||||
* and {@link com.jetbrains.python.refactoring.classes.membersManager.MembersManager#moveAllMembers(com.jetbrains.python.psi.PyClass, com.jetbrains.python.psi.PyClass, java.util.Collection)}
|
||||
* and {@link com.jetbrains.python.refactoring.classes.membersManager.MembersManager#moveAllMembers(java.util.Collection, com.jetbrains.python.psi.PyClass, com.jetbrains.python.psi.PyClass)}
|
||||
*
|
||||
* This class delegates its behaviour to its managers (some kind of plugins). There is one for each member type (one for method, one for field etc).
|
||||
* You need to extend {@link com.jetbrains.python.refactoring.classes.membersManager.MembersManager} to add some. See its javadoc for more info.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.jetbrains.python.refactoring.classes.pullUp;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
@@ -11,18 +10,14 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* TODO: Doc
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
class PyPullUpProcessor extends PyMembersRefactoringBaseProcessor {
|
||||
|
||||
PyPullUpProcessor(@NotNull final PyClass from, @NotNull final PyClass to, @NotNull final Collection<PyMemberInfo> membersToMove) {
|
||||
super(from, to, membersToMove);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElement[] getElements() {
|
||||
return new PsiElement[]{myTo};
|
||||
super(membersToMove, from, to);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+3
-3
@@ -34,7 +34,7 @@ import java.util.List;
|
||||
/**
|
||||
* @author Dennis.Ushakov
|
||||
*/
|
||||
public class PyPushDownConflicts {
|
||||
class PyPushDownConflicts {
|
||||
private static final Logger LOG = Logger.getInstance(PyPushDownProcessor.class.getName());
|
||||
|
||||
private final PyClass myClass;
|
||||
@@ -47,11 +47,11 @@ public class PyPushDownConflicts {
|
||||
myConflicts = new MultiMap<PsiElement, String>();
|
||||
}
|
||||
|
||||
public MultiMap<PsiElement, String> getConflicts() {
|
||||
MultiMap<PsiElement, String> getConflicts() {
|
||||
return myConflicts;
|
||||
}
|
||||
|
||||
public void checkTargetClassConflicts(PyClass clazz) {
|
||||
void checkTargetClassConflicts(PyClass clazz) {
|
||||
checkPlacementConflicts(clazz);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* 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.refactoring.classes.pushDown;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.classMembers.MemberInfoChange;
|
||||
import com.intellij.refactoring.classMembers.MemberInfoModel;
|
||||
import com.intellij.refactoring.classMembers.UsedByDependencyMemberInfoModel;
|
||||
import com.intellij.refactoring.ui.RefactoringDialog;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
|
||||
import com.jetbrains.python.refactoring.classes.ui.PyMemberSelectionPanel;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Dennis.Ushakov
|
||||
*/
|
||||
public class PyPushDownDialog extends RefactoringDialog {
|
||||
private final List<PyMemberInfo> myMemberInfos;
|
||||
private final PyClass myClass;
|
||||
private MemberInfoModel<PyElement, PyMemberInfo> myMemberInfoModel;
|
||||
|
||||
public PyPushDownDialog(Project project, PyClass aClass, PyMemberInfoStorage memberInfos) {
|
||||
super(project, true);
|
||||
myMemberInfos = memberInfos.getClassMemberInfos(aClass);
|
||||
myClass = aClass;
|
||||
|
||||
setTitle(PyPushDownHandler.REFACTORING_NAME);
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
protected String getHelpId() {
|
||||
return "python.reference.pushMembersDown";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doAction() {
|
||||
if(!isOKActionEnabled()) return;
|
||||
|
||||
final PyPushDownProcessor processor = new PyPushDownProcessor(getProject(), myClass, getSelectedMemberInfos());
|
||||
invokeRefactoring(processor);
|
||||
}
|
||||
|
||||
protected JComponent createNorthPanel() {
|
||||
GridBagConstraints gbConstraints = new GridBagConstraints();
|
||||
|
||||
JPanel panel = new JPanel(new GridBagLayout());
|
||||
|
||||
gbConstraints.insets = new Insets(4, 0, 4, 8);
|
||||
gbConstraints.weighty = 1;
|
||||
gbConstraints.weightx = 1;
|
||||
gbConstraints.gridy = 0;
|
||||
gbConstraints.gridwidth = GridBagConstraints.REMAINDER;
|
||||
gbConstraints.fill = GridBagConstraints.BOTH;
|
||||
gbConstraints.anchor = GridBagConstraints.WEST;
|
||||
final String fqn = myClass.getName();
|
||||
panel.add(new JLabel(RefactoringBundle.message("push.members.from.0.down.label", fqn)), gbConstraints);
|
||||
return panel;
|
||||
}
|
||||
|
||||
protected JComponent createCenterPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
final PyMemberSelectionPanel memberSelectionPanel = new PyMemberSelectionPanel(
|
||||
RefactoringBundle.message("members.to.be.pushed.down.panel.title"),
|
||||
myMemberInfos, null);
|
||||
panel.add(memberSelectionPanel, BorderLayout.CENTER);
|
||||
|
||||
myMemberInfoModel = new UsedByDependencyMemberInfoModel<PyElement, PyClass, PyMemberInfo>(myClass);
|
||||
myMemberInfoModel.memberInfoChanged(new MemberInfoChange<PyElement, PyMemberInfo>(myMemberInfos));
|
||||
memberSelectionPanel.init(myMemberInfoModel, myMemberInfos);
|
||||
return panel;
|
||||
}
|
||||
|
||||
public Collection<PyMemberInfo> getSelectedMemberInfos() {
|
||||
ArrayList<PyMemberInfo> list = new ArrayList<PyMemberInfo>(myMemberInfos.size());
|
||||
for (PyMemberInfo info : myMemberInfos) {
|
||||
if (info.isChecked() && myMemberInfoModel.isMemberEnabled(info)) {
|
||||
list.add(info);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
+21
-6
@@ -20,10 +20,14 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||
import com.intellij.util.Query;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyUtil;
|
||||
import com.jetbrains.python.psi.search.PyClassInheritorsSearch;
|
||||
import com.jetbrains.python.refactoring.classes.PyClassRefactoringHandler;
|
||||
import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
|
||||
import com.jetbrains.python.vp.Creator;
|
||||
import com.jetbrains.python.vp.ViewPresenterUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -33,12 +37,12 @@ public class PyPushDownHandler extends PyClassRefactoringHandler {
|
||||
public static final String REFACTORING_NAME = RefactoringBundle.message("push.members.down.title");
|
||||
|
||||
@Override
|
||||
protected void doRefactorImpl(@NotNull Project project,
|
||||
@NotNull PyClass classUnderRefactoring,
|
||||
@NotNull PyMemberInfoStorage infoStorage,
|
||||
protected void doRefactorImpl(@NotNull final Project project,
|
||||
@NotNull final PyClass classUnderRefactoring,
|
||||
@NotNull final PyMemberInfoStorage infoStorage,
|
||||
@NotNull Editor editor) {
|
||||
|
||||
|
||||
//TODO: Move to presenter?
|
||||
final Query<PyClass> query = PyClassInheritorsSearch.search(classUnderRefactoring, false);
|
||||
if (query.findFirst() == null) {
|
||||
final String message = RefactoringBundle.message("class.0.does.not.have.inheritors", classUnderRefactoring.getName());
|
||||
@@ -46,8 +50,19 @@ public class PyPushDownHandler extends PyClassRefactoringHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
final PyPushDownDialog dialog = new PyPushDownDialog(project, classUnderRefactoring, infoStorage);
|
||||
dialog.show();
|
||||
ViewPresenterUtils.linkViewWithPresenterAndLaunch(PyPushDownPresenter.class, PyPushDownView.class, new Creator<PyPushDownView, PyPushDownPresenter>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public PyPushDownPresenter createPresenter(@NotNull PyPushDownView view) {
|
||||
return new PyPushDownPresenterImpl(view, classUnderRefactoring, infoStorage);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyPushDownView createView(@NotNull PyPushDownPresenter presenter) {
|
||||
return new PyPushDownViewSwingImpl(classUnderRefactoring, project, presenter);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.jetbrains.python.refactoring.classes.pushDown;
|
||||
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedPresenter;
|
||||
|
||||
/**
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public interface PyPushDownPresenter extends MembersBasedPresenter {
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.jetbrains.python.refactoring.classes.pushDown;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor;
|
||||
import com.intellij.refactoring.classMembers.UsedByDependencyMemberInfoModel;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.psi.PyUtil;
|
||||
import com.jetbrains.python.psi.search.PyClassInheritorsSearch;
|
||||
import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedPresenterWithPreviewImpl;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersViewInitializationInfo;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Created by Ilya.Kazakevich on 10.02.14.
|
||||
*/
|
||||
public class PyPushDownPresenterImpl extends MembersBasedPresenterWithPreviewImpl<PyPushDownView> implements PyPushDownPresenter {
|
||||
public PyPushDownPresenterImpl(@NotNull PyPushDownView view,
|
||||
@NotNull PyClass classUnderRefactoring,
|
||||
@NotNull PyMemberInfoStorage infoStorage) {
|
||||
super(view, classUnderRefactoring, infoStorage);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public BaseRefactoringProcessor createProcessor() {
|
||||
return new PyPushDownProcessor(myView.getSelectedMemberInfos(), myClassUnderRefactoring);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected MultiMap<PsiElement, String> getConflicts() {
|
||||
return new PyPushDownConflicts(myClassUnderRefactoring, myStorage.getClassMemberInfos(myClassUnderRefactoring)).getConflicts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launch() {
|
||||
UsedByDependencyMemberInfoModel<PyElement, PyClass, PyMemberInfo> model =
|
||||
new UsedByDependencyMemberInfoModel<PyElement, PyClass, PyMemberInfo>(myClassUnderRefactoring);
|
||||
myView.configure(new MembersViewInitializationInfo(model, PyUtil.filterOutObject(myStorage.getClassMemberInfos(myClassUnderRefactoring))));
|
||||
myView.initAndShow();
|
||||
}
|
||||
}
|
||||
+20
-93
@@ -15,120 +15,47 @@
|
||||
*/
|
||||
package com.jetbrains.python.refactoring.classes.pushDown;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiNamedElement;
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.usageView.UsageViewDescriptor;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.usageView.UsageViewBundle;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.psi.PyExpression;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import com.jetbrains.python.psi.search.PyClassInheritorsSearch;
|
||||
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMembersRefactoringBaseProcessor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author Dennis.Ushakov
|
||||
*/
|
||||
public class PyPushDownProcessor extends BaseRefactoringProcessor {
|
||||
private static final Logger LOG = Logger.getInstance(PyPushDownProcessor.class.getName());
|
||||
public class PyPushDownProcessor extends PyMembersRefactoringBaseProcessor {
|
||||
|
||||
private PyClass myClass;
|
||||
private final Collection<PyMemberInfo> mySelectedMemberInfos;
|
||||
private static final String HEADER = RefactoringBundle.message("push.down.members.elements.header");
|
||||
|
||||
public PyPushDownProcessor(Project project, PyClass clazz, Collection<PyMemberInfo> selectedMemberInfos) {
|
||||
super(project);
|
||||
myClass = clazz;
|
||||
mySelectedMemberInfos = selectedMemberInfos;
|
||||
public PyPushDownProcessor(@NotNull Collection<PyMemberInfo> membersToMove,
|
||||
@NotNull PyClass from) {
|
||||
super(membersToMove, from, getChildren(from));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) {
|
||||
return new PyPushDownUsageViewDescriptor(myClass);
|
||||
private static PyClass[] getChildren(@NotNull final PyClass from) {
|
||||
Collection<PyClass> all = PyClassInheritorsSearch.search(from, false).findAll();
|
||||
return all.toArray(new PyClass[all.size()]);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected UsageInfo[] findUsages() {
|
||||
final Collection<PyClass> subClasses = PyClassInheritorsSearch.search(myClass, false).findAll();
|
||||
final UsageInfo[] result = new UsageInfo[subClasses.size()];
|
||||
ContainerUtil.map2Array(subClasses, result, new Function<PyClass, UsageInfo>() {
|
||||
public UsageInfo fun(PyClass pyClass) {
|
||||
return new UsageInfo(pyClass);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
|
||||
public String getProcessedElementsHeader() {
|
||||
return HEADER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refreshElements(PsiElement[] elements) {
|
||||
if (elements.length == 1 && elements[0] instanceof PyClass) {
|
||||
myClass = (PyClass)elements[0];
|
||||
}
|
||||
public String getCodeReferencesText(int usagesCount, int filesCount) {
|
||||
return RefactoringBundle.message("classes.to.push.down.members.to", UsageViewBundle.getReferencesString(usagesCount, filesCount));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performRefactoring(UsageInfo[] usages) {
|
||||
final Set<String> superClasses = new HashSet<String>();
|
||||
final Set<PsiNamedElement> extractedClasses = new HashSet<PsiNamedElement>();
|
||||
final List<PyFunction> methods = new ArrayList<PyFunction>();
|
||||
for (PyMemberInfo member : mySelectedMemberInfos) {
|
||||
final PyElement element = member.getMember();
|
||||
if (element instanceof PyFunction) methods.add((PyFunction)element);
|
||||
else if (element instanceof PyClass) {
|
||||
superClasses.add(element.getName());
|
||||
extractedClasses.add((PyClass)element);
|
||||
}
|
||||
else LOG.error("unmatched member class " + element.getClass());
|
||||
}
|
||||
final PyElement[] elements = methods.toArray(new PyElement[methods.size()]);
|
||||
|
||||
final List<PyExpression> superClassesElements = PyClassRefactoringUtil.removeAndGetSuperClasses(myClass, superClasses);
|
||||
|
||||
for (UsageInfo usage : usages) {
|
||||
final PyClass targetClass = (PyClass)usage.getElement();
|
||||
PyClassRefactoringUtil.addMethods(targetClass, elements, false);
|
||||
PyClassRefactoringUtil.addSuperclasses(myClass.getProject(), targetClass, superClassesElements, superClasses);
|
||||
PyClassRefactoringUtil.insertImport(targetClass, extractedClasses);
|
||||
}
|
||||
|
||||
if (methods.size() != 0) {
|
||||
PyPsiUtils.removeElements(elements);
|
||||
PyClassRefactoringUtil.insertPassIfNeeded(myClass);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean preprocessUsages(Ref<UsageInfo[]> ref) {
|
||||
final UsageInfo[] usages = ref.get();
|
||||
final PyPushDownConflicts conflicts = new PyPushDownConflicts(myClass, mySelectedMemberInfos);
|
||||
conflicts.checkSourceClassConflicts();
|
||||
|
||||
if (usages.length == 0) {
|
||||
final String message = RefactoringBundle.message("class.0.does.not.have.inheritors", myClass.getName()) + "\nPushing members down will result in them being deleted";
|
||||
final int answer = Messages.showYesNoDialog(message, PyPushDownHandler.REFACTORING_NAME, Messages.getWarningIcon());
|
||||
if (answer != Messages.YES) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (UsageInfo usage : usages) {
|
||||
conflicts.checkTargetClassConflicts((PyClass)usage.getElement());
|
||||
}
|
||||
return showConflicts(conflicts.getConflicts(), usages);
|
||||
@Nullable
|
||||
public String getCommentReferencesText(int usagesCount, int filesCount) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* 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.refactoring.classes.pushDown;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.usageView.UsageViewBundle;
|
||||
import com.intellij.usageView.UsageViewDescriptor;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Dennis.Ushakov
|
||||
*/
|
||||
public class PyPushDownUsageViewDescriptor implements UsageViewDescriptor {
|
||||
private final PyClass myClass;
|
||||
private static final String HEADER = RefactoringBundle.message("push.down.members.elements.header");
|
||||
|
||||
public PyPushDownUsageViewDescriptor(final PyClass clazz) {
|
||||
myClass = clazz;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PsiElement[] getElements() {
|
||||
return new PsiElement[] {myClass};
|
||||
}
|
||||
|
||||
public String getProcessedElementsHeader() {
|
||||
return HEADER;
|
||||
}
|
||||
|
||||
public String getCodeReferencesText(int usagesCount, int filesCount) {
|
||||
return RefactoringBundle.message("classes.to.push.down.members.to", UsageViewBundle.getReferencesString(usagesCount, filesCount));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getCommentReferencesText(int usagesCount, int filesCount) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.jetbrains.python.refactoring.classes.pushDown;
|
||||
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedView;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersViewInitializationInfo;
|
||||
|
||||
/**
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public interface PyPushDownView extends MembersBasedView<MembersViewInitializationInfo> {
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.jetbrains.python.refactoring.classes.pushDown;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedViewSwingImpl;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersViewInitializationInfo;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* Created by Ilya.Kazakevich on 10.02.14.
|
||||
*/
|
||||
public class PyPushDownViewSwingImpl extends MembersBasedViewSwingImpl<PyPushDownPresenter, MembersViewInitializationInfo>
|
||||
implements PyPushDownView {
|
||||
public PyPushDownViewSwingImpl(
|
||||
@NotNull final PyClass classUnderRefactoring,
|
||||
@NotNull final Project project,
|
||||
@NotNull final PyPushDownPresenter presenter) {
|
||||
super(project, presenter, RefactoringBundle.message("push.members.from.0.down.label", classUnderRefactoring.getName()));
|
||||
|
||||
myCenterPanel.add(myPyMemberSelectionPanel, BorderLayout.CENTER);
|
||||
setTitle(PyPushDownHandler.REFACTORING_NAME);
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ package com.jetbrains.python.vp;
|
||||
public interface Presenter {
|
||||
/**
|
||||
* Launches dialog. Presenter should fetch data and start view.
|
||||
* TODO: Say you run initand show and launch
|
||||
*/
|
||||
void launch();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
class Parent(object):
|
||||
C = 12
|
||||
|
||||
def foo(self):
|
||||
pass
|
||||
|
||||
def __init__(self):
|
||||
self.foo = 12
|
||||
|
||||
def foo(self):
|
||||
pass
|
||||
|
||||
|
||||
class Child(Parent, object):
|
||||
def __init__(self): pass
|
||||
|
||||
@@ -2,8 +2,10 @@ class Foo:
|
||||
def foo(self):
|
||||
print("a")
|
||||
|
||||
# this is boo
|
||||
def boo(self):
|
||||
"""
|
||||
In python we use multi-line comments inside of method body
|
||||
"""
|
||||
print "rrrrr"
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ class Foo:
|
||||
print("a")
|
||||
|
||||
class Boo(Foo):
|
||||
# this is boo
|
||||
def boo(self):
|
||||
"""
|
||||
In python we use multi-line comments inside of method body
|
||||
"""
|
||||
print "rrrrr"
|
||||
@@ -1,5 +1,7 @@
|
||||
class Foo:
|
||||
pass
|
||||
|
||||
class Boo():
|
||||
|
||||
class Boo(Foo):
|
||||
def foo(self):
|
||||
print "rrrrr"
|
||||
@@ -0,0 +1,48 @@
|
||||
class Dummny(object):
|
||||
pass
|
||||
|
||||
|
||||
class Parent(object):
|
||||
"""
|
||||
This class is usefull.
|
||||
"""
|
||||
CLASS_VAR_2 = 2
|
||||
|
||||
def __init__(self): pass
|
||||
|
||||
|
||||
def method_2(self):
|
||||
i = 1
|
||||
|
||||
|
||||
class Child_1(Parent, Dummny):
|
||||
CLASS_VAR_1 = 1
|
||||
|
||||
def __init__(self):
|
||||
self.inst_var = 12
|
||||
self.bar = 64
|
||||
|
||||
def method_1(self):
|
||||
"""
|
||||
Some text
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Child_2(Parent, Dummny):
|
||||
"""
|
||||
This class implements most sophisticated algorithm
|
||||
"""
|
||||
CLASS_VAR_1 = 1
|
||||
|
||||
def __init__(self):
|
||||
self.inst_var = 12
|
||||
|
||||
def lala(self):
|
||||
pass
|
||||
|
||||
def method_1(self):
|
||||
"""
|
||||
Some text
|
||||
"""
|
||||
pass
|
||||
@@ -0,0 +1,37 @@
|
||||
class Dummny(object):
|
||||
pass
|
||||
|
||||
|
||||
class Parent(object, Dummny):
|
||||
"""
|
||||
This class is usefull.
|
||||
"""
|
||||
CLASS_VAR_1 = 1
|
||||
CLASS_VAR_2 = 2
|
||||
|
||||
def __init__(self):
|
||||
self.inst_var = 12
|
||||
|
||||
|
||||
def method_1(self):
|
||||
"""
|
||||
Some text
|
||||
"""
|
||||
pass
|
||||
|
||||
def method_2(self):
|
||||
i = 1
|
||||
|
||||
|
||||
class Child_1(Parent):
|
||||
def __init__(self):
|
||||
self.bar = 64
|
||||
pass
|
||||
|
||||
|
||||
class Child_2(Parent):
|
||||
"""
|
||||
This class implements most sophisticated algorithm
|
||||
"""
|
||||
def lala(self):
|
||||
pass
|
||||
@@ -15,18 +15,25 @@
|
||||
*/
|
||||
package com.jetbrains.python.refactoring.classes;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor;
|
||||
import com.jetbrains.python.fixtures.PyTestCase;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.PyTargetExpression;
|
||||
import com.jetbrains.python.psi.stubs.PyClassNameIndex;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.MembersManager;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Dennis.Ushakov
|
||||
@@ -76,4 +83,19 @@ public abstract class PyClassRefactoringTest extends PyTestCase {
|
||||
Assert.assertThat(String.format("Expected one class named %s", name), classes, Matchers.hasSize(1));
|
||||
return classes.iterator().next();
|
||||
}
|
||||
|
||||
|
||||
protected void moveViaProcessor(@NotNull Project project, @NotNull final BaseRefactoringProcessor processor) {
|
||||
CommandProcessor.getInstance().executeCommand(project, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
processor.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.jetbrains.python.refactoring.classes.extractSuperclass;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
@@ -30,6 +32,7 @@ import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -137,6 +140,7 @@ public class PyExtractSuperclassTest extends PyClassRefactoringTest {
|
||||
new WriteCommandAction.Simple(myFixture.getProject()) {
|
||||
@Override
|
||||
protected void run() throws Throwable {
|
||||
//TODO: Test via presenter
|
||||
//noinspection ConstantConditions
|
||||
final String path = base_dir.getPath() + "/a/b";
|
||||
PyExtractSuperclassHelper.extractSuperclass(clazz, members, superclassName, path + "/foo.py");
|
||||
|
||||
+5
-14
@@ -13,12 +13,11 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.refactoring.classes;
|
||||
package com.jetbrains.python.refactoring.classes.pullUp;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.refactoring.classes.PyClassRefactoringTest;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.MembersManager;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -66,6 +65,7 @@ public class PyPullUpTest extends PyClassRefactoringTest {
|
||||
public void testMoveInstanceAttributesNoInit() {
|
||||
doHelperTest("Child", "#instance_field", "Parent");
|
||||
}
|
||||
|
||||
public void testMoveInstanceAttributesLeaveEmptyInit() {
|
||||
doHelperTest("Child", "#foo", "Parent");
|
||||
}
|
||||
@@ -97,16 +97,7 @@ public class PyPullUpTest extends PyClassRefactoringTest {
|
||||
final PyClass clazz = findClass(className);
|
||||
final PyElement member = findMember(className, memberName);
|
||||
final PyClass superClass = findClass(superClassName);
|
||||
CommandProcessor.getInstance().executeCommand(clazz.getProject(), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
MembersManager.moveAllMembers(clazz, superClass, Collections.singleton(MembersManager.findMember(clazz, member)));
|
||||
}
|
||||
});
|
||||
}
|
||||
}, null, null);
|
||||
moveViaProcessor(clazz.getProject(),
|
||||
new PyPullUpProcessor(clazz, superClass, Collections.singleton(MembersManager.findMember(clazz, member))));
|
||||
}
|
||||
}
|
||||
+12
-11
@@ -13,16 +13,18 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.refactoring.classes;
|
||||
package com.jetbrains.python.refactoring.classes.pushDown;
|
||||
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.refactoring.classes.PyClassRefactoringTest;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.MembersManager;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
import com.jetbrains.python.refactoring.classes.pushDown.PyPushDownProcessor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -37,6 +39,10 @@ public class PyPushDownTest extends PyClassRefactoringTest {
|
||||
doProcessorTest("Zope", null, "Foo");
|
||||
}
|
||||
|
||||
public void testFull() throws Exception {
|
||||
doProcessorTest("Parent", null, "#CLASS_VAR_1", "#inst_var", ".method_1", "Dummny");
|
||||
}
|
||||
|
||||
public void testMultiple() throws Exception {
|
||||
doProcessorTest("Foo", null, ".foo");
|
||||
}
|
||||
@@ -49,25 +55,20 @@ public class PyPushDownTest extends PyClassRefactoringTest {
|
||||
doProcessorTest("Foo", "method <b><code>foo</code></b> is already overridden in class <b><code>Boo</code></b>. Method will not be pushed down to that class.", ".foo");
|
||||
}
|
||||
|
||||
private void doProcessorTest(final String className, final String expectedError, final String... membersName) throws Exception {
|
||||
private void doProcessorTest(final String className, final String expectedError, final String... memberNames) throws Exception {
|
||||
try {
|
||||
String baseName = "/refactoring/pushdown/" + getTestName(true);
|
||||
myFixture.configureByFile(baseName + ".before.py");
|
||||
final PyClass clazz = findClass(className);
|
||||
final List<PyMemberInfo> members = new ArrayList<PyMemberInfo>();
|
||||
for (String memberName : membersName) {
|
||||
for (String memberName : memberNames) {
|
||||
final PyElement member = findMember(className, memberName);
|
||||
members.add(MembersManager.findMember(clazz, member));
|
||||
}
|
||||
|
||||
final PyPushDownProcessor processor = new PyPushDownProcessor(myFixture.getProject(), clazz, members);
|
||||
new WriteCommandAction.Simple(myFixture.getProject()) {
|
||||
@Override
|
||||
protected void run() throws Throwable {
|
||||
processor.run();
|
||||
}
|
||||
}.execute().throwException();
|
||||
myFixture.checkResultByFile(baseName + ".after.py");
|
||||
final PyPushDownProcessor processor = new PyPushDownProcessor(members, clazz);
|
||||
moveViaProcessor(myFixture.getProject(), processor);
|
||||
myFixture.checkResultByFile(baseName + ".after.py");
|
||||
} catch (Exception e) {
|
||||
if (expectedError == null) throw e;
|
||||
assertTrue(e.getMessage(), e.getMessage().contains(expectedError));
|
||||
Reference in New Issue
Block a user