mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-12213 When moving, members should be sorted according to their dependencies
This commit is contained in:
+5
-1
@@ -28,7 +28,7 @@ import java.util.Set;
|
||||
|
||||
public class MemberDependenciesStorage<T extends NavigatablePsiElement, C extends PsiElement> {
|
||||
protected final C myClass;
|
||||
private final C mySuperClass;
|
||||
private C mySuperClass;
|
||||
private final Map<T, Set<T>> myDependencyGraph;
|
||||
|
||||
public MemberDependenciesStorage(C aClass, C superClass) {
|
||||
@@ -37,6 +37,10 @@ public class MemberDependenciesStorage<T extends NavigatablePsiElement, C extend
|
||||
myDependencyGraph = new HashMap<T, Set<T>>();
|
||||
}
|
||||
|
||||
public void setSuperClass(C superClass) {
|
||||
mySuperClass = superClass;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Set<T> getMemberDependencies(T member) {
|
||||
Set<T> result = myDependencyGraph.get(member);
|
||||
|
||||
@@ -63,6 +63,7 @@ import com.jetbrains.python.codeInsight.stdlib.PyNamedTupleType;
|
||||
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.PyDependenciesComparator;
|
||||
import com.jetbrains.python.refactoring.classes.extractSuperclass.PyExtractSuperclassHelper;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -1285,6 +1286,36 @@ public class PyUtil {
|
||||
return instanceOf(element, toSkip) ? null : element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds element to statement list to the correct place according to its dependencies.
|
||||
* @param element to insert
|
||||
* @param statementList where element should be inserted
|
||||
* @return inserted element
|
||||
*/
|
||||
public static <T extends PyElement>T addElementToStatementList(@NotNull final T element,
|
||||
@NotNull final PyStatementList statementList) {
|
||||
PsiElement before = null;
|
||||
PsiElement after = null;
|
||||
for (final PyStatement statement : statementList.getStatements()) {
|
||||
if (PyDependenciesComparator.depends(element, statement)) {
|
||||
after = statement;
|
||||
}else if (PyDependenciesComparator.depends(statement, element)) {
|
||||
before = statement;
|
||||
}
|
||||
}
|
||||
final PsiElement result;
|
||||
if (after != null) {
|
||||
|
||||
result = statementList.addAfter(element, after);
|
||||
}else if (before != null) {
|
||||
result = statementList.addBefore(element, before);
|
||||
} else {
|
||||
result = addElementToStatementList(element, statementList, true);
|
||||
}
|
||||
@SuppressWarnings("unchecked") // Inserted element can't have different type
|
||||
final T resultCasted = (T)result;
|
||||
return resultCasted;
|
||||
}
|
||||
|
||||
public static PsiElement addElementToStatementList(@NotNull PsiElement element,
|
||||
@NotNull PyStatementList statementList,
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.jetbrains.python.refactoring.classes;
|
||||
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiPolyVariantReference;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Searches for element in another element's usages.
|
||||
* Parametrize it with needle and make stack to accept it.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
class DependencyVisitor extends PyRecursiveElementVisitor {
|
||||
|
||||
@NotNull
|
||||
private final PyElement myElementToFind;
|
||||
private boolean myDependencyFound;
|
||||
|
||||
/**
|
||||
* @param elementToFind what to find
|
||||
*/
|
||||
DependencyVisitor(@NotNull final PyElement elementToFind) {
|
||||
myElementToFind = elementToFind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyCallExpression(@NotNull final PyCallExpression node) {
|
||||
final PyExpression callee = node.getCallee();
|
||||
if (callee != null) {
|
||||
final PsiReference calleeReference = callee.getReference();
|
||||
if ((calleeReference != null) && calleeReference.isReferenceTo(myElementToFind)) {
|
||||
myDependencyFound = true;
|
||||
return;
|
||||
}
|
||||
final String calleeName = callee.getName();
|
||||
|
||||
if ((calleeName != null) && calleeName.equals(myElementToFind.getName())) { // Check by name also
|
||||
myDependencyFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyReferenceExpression(final PyReferenceExpression node) {
|
||||
|
||||
final PsiPolyVariantReference reference = node.getReference();
|
||||
if (reference.isReferenceTo(myElementToFind)) {
|
||||
myDependencyFound = true;
|
||||
return;
|
||||
}
|
||||
// TODO: This step is member-type specific. Move to MemberManagers?
|
||||
if (myElementToFind instanceof PyAssignmentStatement) {
|
||||
final PyExpression[] targets = ((PyAssignmentStatement)myElementToFind).getTargets();
|
||||
|
||||
if (targets.length != 1) {
|
||||
return;
|
||||
}
|
||||
final PyExpression expression = targets[0];
|
||||
|
||||
if (reference.isReferenceTo(expression)) {
|
||||
myDependencyFound = true;
|
||||
return;
|
||||
}
|
||||
if (node.getText().equals(expression.getText())) { // Check by name also
|
||||
myDependencyFound = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
final PsiElement declaration = reference.resolve();
|
||||
myDependencyFound = PsiTreeUtil.findFirstParent(declaration, new PsiElementCondition()) != null;
|
||||
}
|
||||
|
||||
public boolean isDependencyFound() {
|
||||
return myDependencyFound;
|
||||
}
|
||||
|
||||
private class PsiElementCondition implements Condition<PsiElement> {
|
||||
@Override
|
||||
public boolean value(final PsiElement psiElement) {
|
||||
return psiElement.equals(myElementToFind);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,19 +60,68 @@ public final class PyClassRefactoringUtil {
|
||||
/**
|
||||
* Copies class field declarations to some other place
|
||||
*
|
||||
* @param assignmentStatement list of class fields
|
||||
* @param assignmentStatements list of class fields
|
||||
* @param dequalifyIfDeclaredInClass If not null method will check if field declared in this class.
|
||||
* If declared -- qualifier will be removed.
|
||||
* For example: MyClass.Foo will become Foo it this param is MyClass.
|
||||
* @return new (copied) fields
|
||||
*/
|
||||
@NotNull
|
||||
public static List<PyAssignmentStatement> copyFieldDeclarationToStatement(@NotNull final Collection<PyAssignmentStatement> assignmentStatement,
|
||||
@NotNull final PyStatementList superClassStatement) {
|
||||
final List<PyAssignmentStatement> declations = new ArrayList<PyAssignmentStatement>(assignmentStatement.size());
|
||||
for (final PyAssignmentStatement expression : assignmentStatement) {
|
||||
final PyAssignmentStatement newDeclaration = (PyAssignmentStatement)expression.copy();
|
||||
declations.add((PyAssignmentStatement)PyUtil.addElementToStatementList(newDeclaration, superClassStatement, true));
|
||||
public static List<PyAssignmentStatement> copyFieldDeclarationToStatement(@NotNull final Collection<PyAssignmentStatement> assignmentStatements,
|
||||
@NotNull final PyStatementList superClassStatement,
|
||||
@Nullable final PyClass dequalifyIfDeclaredInClass) {
|
||||
final List<PyAssignmentStatement> declarations = new ArrayList<PyAssignmentStatement>(assignmentStatements.size());
|
||||
Collections.sort(declarations, PyDependenciesComparator.INSTANCE);
|
||||
|
||||
|
||||
for (final PyAssignmentStatement pyAssignmentStatement : assignmentStatements) {
|
||||
final PyElement value = pyAssignmentStatement.getAssignedValue();
|
||||
final PyAssignmentStatement newDeclaration = (PyAssignmentStatement)pyAssignmentStatement.copy();
|
||||
|
||||
if (value instanceof PyReferenceExpression && dequalifyIfDeclaredInClass != null) {
|
||||
final String newValue = getNewValueToAssign((PyReferenceExpression)value, dequalifyIfDeclaredInClass);
|
||||
|
||||
setNewAssigneeValue(newDeclaration, newValue);
|
||||
|
||||
}
|
||||
|
||||
declarations.add(PyUtil.addElementToStatementList(newDeclaration, superClassStatement));
|
||||
PyPsiUtils.removeRedundantPass(superClassStatement);
|
||||
}
|
||||
return declations;
|
||||
return declarations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets new value to assignment statement.
|
||||
* @param assignmentStatement statement to change
|
||||
* @param newValue new value
|
||||
*/
|
||||
private static void setNewAssigneeValue(@NotNull final PyAssignmentStatement assignmentStatement, @NotNull final String newValue) {
|
||||
final PyExpression oldValue = assignmentStatement.getAssignedValue();
|
||||
final PyExpression newExpression =
|
||||
PyElementGenerator.getInstance(assignmentStatement.getProject()).createExpressionFromText(LanguageLevel.forElement(assignmentStatement), newValue);
|
||||
if (oldValue != null) {
|
||||
oldValue.replace(newExpression);
|
||||
} else {
|
||||
assignmentStatement.add(newExpression);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current value declared in provided class and removes class qualifier if true
|
||||
* @param currentValue current value
|
||||
* @param dequalifyIfDeclaredInClass class to check
|
||||
* @return value as string
|
||||
*/
|
||||
@NotNull
|
||||
private static String getNewValueToAssign(@NotNull final PyReferenceExpression currentValue, @NotNull final PyClass dequalifyIfDeclaredInClass) {
|
||||
final PyExpression qualifier = currentValue.getQualifier();
|
||||
if ((qualifier instanceof PyReferenceExpression) &&
|
||||
((PyReferenceExpression)qualifier).getReference().isReferenceTo(dequalifyIfDeclaredInClass)) {
|
||||
final String name = currentValue.getName();
|
||||
return ((name != null) ? name : currentValue.getText());
|
||||
}
|
||||
return currentValue.getText();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.jetbrains.python.refactoring.classes;
|
||||
|
||||
import com.jetbrains.python.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Compares elements by their dependencies.
|
||||
* If A depends on B, then A < B
|
||||
*/
|
||||
public class PyDependenciesComparator implements Comparator<PyElement>, Serializable {
|
||||
|
||||
/**
|
||||
* Singleton comparator instance
|
||||
*/
|
||||
public static final PyDependenciesComparator INSTANCE = new PyDependenciesComparator();
|
||||
|
||||
private PyDependenciesComparator() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(@NotNull final PyElement o1, @NotNull final PyElement o2) {
|
||||
if (depends(o1, o2)) {
|
||||
return 1;
|
||||
}
|
||||
if (depends(o2, o1)) {
|
||||
return -1;
|
||||
}
|
||||
return getBlockType(o1).compareTo(getBlockType(o2));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static BlockType getBlockType(@NotNull final PyElement statement) {
|
||||
for (BlockType type : BlockType.values()) {
|
||||
if (type.myClass.isAssignableFrom(statement.getClass())) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return BlockType.OTHER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if first param depends on second.
|
||||
*/
|
||||
public static boolean depends(@NotNull final PyElement o1, @NotNull final PyElement o2) {
|
||||
final DependencyVisitor visitor = new DependencyVisitor(o2);
|
||||
o1.accept(visitor);
|
||||
return visitor.isDependencyFound();
|
||||
}
|
||||
|
||||
/**
|
||||
* Types of class members in order, they should appear
|
||||
*/
|
||||
private enum BlockType {
|
||||
DOC(PyExpressionStatement.class),
|
||||
DECLARATION(PyAssignmentStatement.class),
|
||||
METHOD(PyFunction.class),
|
||||
OTHER(PyElement.class);
|
||||
|
||||
@NotNull
|
||||
private final Class<? extends PyElement> myClass;
|
||||
|
||||
BlockType(@NotNull final Class<? extends PyElement> aClass) {
|
||||
myClass = aClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -8,6 +8,7 @@ import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.classMembers.MemberInfoModel;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.PythonLanguage;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
@@ -27,7 +28,8 @@ import java.util.Collections;
|
||||
/**
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
class PyExtractSuperclassPresenterImpl extends MembersBasedPresenterNoPreviewImpl<PyExtractSuperclassView>
|
||||
class PyExtractSuperclassPresenterImpl extends MembersBasedPresenterNoPreviewImpl<PyExtractSuperclassView,
|
||||
MemberInfoModel<PyElement, PyMemberInfo<PyElement>>>
|
||||
implements PyExtractSuperclassPresenter {
|
||||
private final NamesValidator myNamesValidator = LanguageNamesValidation.INSTANCE.forLanguage(PythonLanguage.getInstance());
|
||||
|
||||
|
||||
+5
-2
@@ -1,6 +1,9 @@
|
||||
package com.jetbrains.python.refactoring.classes.membersManager;
|
||||
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.PyAssignmentStatement;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.psi.PyTargetExpression;
|
||||
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -31,7 +34,7 @@ class ClassFieldsManager extends FieldsManager {
|
||||
//TODO: Copy/paste with InstanceFieldsManager. Move to parent?
|
||||
final List<PyElement> result = new ArrayList<PyElement>();
|
||||
for (final PyClass destClass : to) {
|
||||
result.addAll(PyClassRefactoringUtil.copyFieldDeclarationToStatement(statements, destClass.getStatementList()));
|
||||
result.addAll(PyClassRefactoringUtil.copyFieldDeclarationToStatement(statements, destClass.getStatementList(), destClass));
|
||||
}
|
||||
deleteElements(statements);
|
||||
PyClassRefactoringUtil.insertPassIfNeeded(from);
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ class InstanceFieldsManager extends FieldsManager {
|
||||
toInitMethod = createInitMethod(to);
|
||||
}
|
||||
final PyStatementList statementList = toInitMethod.getStatementList();
|
||||
return PyClassRefactoringUtil.copyFieldDeclarationToStatement(members, statementList);
|
||||
return PyClassRefactoringUtil.copyFieldDeclarationToStatement(members, statementList, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+16
-3
@@ -18,9 +18,7 @@ package com.jetbrains.python.refactoring.classes.membersManager;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Preconditions;
|
||||
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.intellij.util.ArrayUtil;
|
||||
@@ -29,6 +27,7 @@ import com.jetbrains.NotNullPredicate;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
|
||||
import com.jetbrains.python.refactoring.classes.PyDependenciesComparator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -103,6 +102,20 @@ public abstract class MembersManager<T extends PyElement> implements Function<T,
|
||||
@NotNull final PyClass from,
|
||||
@NotNull final PyClass... to
|
||||
) {
|
||||
List<PyMemberInfo<PyElement>> memberInfosSorted = new ArrayList<PyMemberInfo<PyElement>>(memberInfos);
|
||||
Collections.sort(memberInfosSorted, new Comparator<PyMemberInfo<PyElement>>() {
|
||||
@Override
|
||||
public int compare(PyMemberInfo<PyElement> o1, PyMemberInfo<PyElement> o2) {
|
||||
return PyDependenciesComparator.INSTANCE.compare(o1.getMember(), o2.getMember());
|
||||
}
|
||||
});
|
||||
|
||||
for (PyMemberInfo<PyElement> info : memberInfosSorted) {
|
||||
TypeSafeMovingStrategy.moveCheckingTypesAtRunTime(from, info.getMembersManager(), Collections.singleton(info), to);
|
||||
}
|
||||
|
||||
|
||||
/*//Move at once, sort
|
||||
final Multimap<MembersManager<PyElement>, PyMemberInfo<PyElement>> managerToMember = ArrayListMultimap.create();
|
||||
//Collect map (manager)->(list_of_memebers)
|
||||
for (final PyMemberInfo<PyElement> memberInfo : memberInfos) {
|
||||
@@ -112,7 +125,7 @@ public abstract class MembersManager<T extends PyElement> implements Function<T,
|
||||
for (final MembersManager<PyElement> membersManager : managerToMember.keySet()) {
|
||||
final Collection<PyMemberInfo<PyElement>> members = managerToMember.get(membersManager);
|
||||
TypeSafeMovingStrategy.moveCheckingTypesAtRunTime(from, membersManager, members, to);
|
||||
}
|
||||
}*/
|
||||
PyClassRefactoringUtil.insertPassIfNeeded(from);
|
||||
}
|
||||
|
||||
|
||||
+6
-4
@@ -19,9 +19,11 @@ import java.util.Collection;
|
||||
* or {@link com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedPresenterWithPreviewImpl} instead
|
||||
*
|
||||
* @param <T> view for that presenter
|
||||
* @param <M> Type of model {@link #myModel}
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
abstract class MembersBasedPresenterImpl<T extends MembersBasedView<?>> implements MembersBasedPresenter {
|
||||
abstract class MembersBasedPresenterImpl<T extends MembersBasedView<?>,
|
||||
M extends MemberInfoModel<PyElement, PyMemberInfo<PyElement>>> implements MembersBasedPresenter {
|
||||
@NotNull
|
||||
protected final T myView;
|
||||
@NotNull
|
||||
@@ -32,18 +34,18 @@ abstract class MembersBasedPresenterImpl<T extends MembersBasedView<?>> implemen
|
||||
* Member model
|
||||
*/
|
||||
@NotNull
|
||||
protected final MemberInfoModel<PyElement, PyMemberInfo<PyElement>> myModel;
|
||||
protected final M myModel;
|
||||
|
||||
/**
|
||||
* @param view View for presenter
|
||||
* @param classUnderRefactoring class to be refactored
|
||||
* @param infoStorage info storage
|
||||
* @param model Member model (to be used for dependencies checking)
|
||||
* @param model Member model (to be used for dependencies checking)
|
||||
*/
|
||||
MembersBasedPresenterImpl(@NotNull final T view,
|
||||
@NotNull final PyClass classUnderRefactoring,
|
||||
@NotNull final PyMemberInfoStorage infoStorage,
|
||||
@NotNull final MemberInfoModel<PyElement, PyMemberInfo<PyElement>> model) {
|
||||
@NotNull final M model) {
|
||||
myView = view;
|
||||
myClassUnderRefactoring = classUnderRefactoring;
|
||||
myStorage = infoStorage;
|
||||
|
||||
+4
-2
@@ -14,10 +14,12 @@ import org.jetbrains.annotations.NotNull;
|
||||
* To "preview" button would be displayed
|
||||
*
|
||||
* @param <T> view for this presenter
|
||||
* @param <M> Type of model
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
|
||||
public abstract class MembersBasedPresenterNoPreviewImpl<T extends MembersBasedView<?>> extends MembersBasedPresenterImpl<T> {
|
||||
public abstract class MembersBasedPresenterNoPreviewImpl<T extends MembersBasedView<?>,
|
||||
M extends MemberInfoModel<PyElement, PyMemberInfo<PyElement>>> extends MembersBasedPresenterImpl<T, M> {
|
||||
/**
|
||||
* @param view view for this presenter
|
||||
* @param classUnderRefactoring class to refactor
|
||||
@@ -27,7 +29,7 @@ public abstract class MembersBasedPresenterNoPreviewImpl<T extends MembersBasedV
|
||||
protected MembersBasedPresenterNoPreviewImpl(@NotNull final T view,
|
||||
@NotNull final PyClass classUnderRefactoring,
|
||||
@NotNull final PyMemberInfoStorage infoStorage,
|
||||
@NotNull final MemberInfoModel<PyElement, PyMemberInfo<PyElement>> model) {
|
||||
@NotNull final M model) {
|
||||
super(view, classUnderRefactoring, infoStorage, model);
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -14,9 +14,11 @@ import org.jetbrains.annotations.NotNull;
|
||||
* "Preview" button would be displayed.
|
||||
*
|
||||
* @param <T> view for this presenter
|
||||
* @param <M> Type of model
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public abstract class MembersBasedPresenterWithPreviewImpl<T extends MembersBasedView<?>> extends MembersBasedPresenterImpl<T> {
|
||||
public abstract class MembersBasedPresenterWithPreviewImpl<T extends MembersBasedView<?>,
|
||||
M extends MemberInfoModel<PyElement, PyMemberInfo<PyElement>>> extends MembersBasedPresenterImpl<T, M> {
|
||||
|
||||
|
||||
/**
|
||||
@@ -28,7 +30,7 @@ public abstract class MembersBasedPresenterWithPreviewImpl<T extends MembersBase
|
||||
protected MembersBasedPresenterWithPreviewImpl(@NotNull final T view,
|
||||
@NotNull final PyClass classUnderRefactoring,
|
||||
@NotNull final PyMemberInfoStorage infoStorage,
|
||||
@NotNull final MemberInfoModel<PyElement, PyMemberInfo<PyElement>> model) {
|
||||
@NotNull final M model) {
|
||||
super(view, classUnderRefactoring, infoStorage, model);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,4 +27,8 @@ import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedPr
|
||||
*/
|
||||
public interface PyPullUpPresenter extends MembersBasedPresenter {
|
||||
|
||||
/**
|
||||
* To be called when user changed parent
|
||||
*/
|
||||
void parentChanged();
|
||||
}
|
||||
|
||||
+5
-1
@@ -35,7 +35,7 @@ import java.util.Collections;
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
class PyPullUpPresenterImpl extends MembersBasedPresenterWithPreviewImpl<PyPullUpView> implements PyPullUpPresenter {
|
||||
class PyPullUpPresenterImpl extends MembersBasedPresenterWithPreviewImpl<PyPullUpView, PyPullUpInfoModel> implements PyPullUpPresenter {
|
||||
@NotNull
|
||||
private final Collection<PyClass> myParents;
|
||||
|
||||
@@ -89,6 +89,10 @@ class PyPullUpPresenterImpl extends MembersBasedPresenterWithPreviewImpl<PyPullU
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parentChanged() {
|
||||
myModel.setSuperClass(myView.getSelectedParent());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
|
||||
@@ -99,6 +99,7 @@ class PyPullUpViewSwingImpl extends MembersBasedViewSwingImpl<PyPullUpPresenter,
|
||||
for (final PyClass parent : configInfo.getParents()) {
|
||||
myParentsComboBoxModel.addElement(parent);
|
||||
}
|
||||
myPresenter.parentChanged();
|
||||
myParentsCombo.addItemListener(this);
|
||||
}
|
||||
|
||||
@@ -106,6 +107,7 @@ class PyPullUpViewSwingImpl extends MembersBasedViewSwingImpl<PyPullUpPresenter,
|
||||
public void itemStateChanged(final ItemEvent e) {
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
myPyMemberSelectionPanel.redraw();
|
||||
myPresenter.parentChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ package com.jetbrains.python.refactoring.classes.pushDown;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor;
|
||||
import com.intellij.refactoring.classMembers.MemberInfoModel;
|
||||
import com.intellij.refactoring.classMembers.UsedByDependencyMemberInfoModel;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
@@ -15,7 +16,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
/**
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public class PyPushDownPresenterImpl extends MembersBasedPresenterWithPreviewImpl<PyPushDownView> implements PyPushDownPresenter {
|
||||
public class PyPushDownPresenterImpl extends MembersBasedPresenterWithPreviewImpl<PyPushDownView, MemberInfoModel<PyElement, PyMemberInfo<PyElement>>> implements PyPushDownPresenter {
|
||||
@NotNull
|
||||
private final Project myProject;
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
class Foo:
|
||||
"""
|
||||
Some doc
|
||||
"""
|
||||
|
||||
def my_method(self):
|
||||
pass
|
||||
|
||||
CLASS_FIELD = 42
|
||||
@@ -0,0 +1,30 @@
|
||||
class DataHolder:
|
||||
VAR = 1
|
||||
|
||||
class Parent:
|
||||
A_FIELD = DataHolder.VAR
|
||||
CLASS_FIELD = 42
|
||||
ANOTHER_CLASS_FIELD = CLASS_FIELD
|
||||
BOO = 12
|
||||
FIELD = BOO
|
||||
|
||||
def __init__(self):
|
||||
self.d = Parent.BOO
|
||||
self.c = 1
|
||||
self.b = self.c
|
||||
|
||||
@staticmethod
|
||||
def foo():
|
||||
return "A"
|
||||
|
||||
SOME_VAR = foo()
|
||||
|
||||
|
||||
class Child(Parent): # Try to pull members up
|
||||
|
||||
|
||||
|
||||
def __init__(self):
|
||||
super(Child, self).__init__()
|
||||
self.a = 12
|
||||
i = 1
|
||||
@@ -0,0 +1,28 @@
|
||||
class DataHolder:
|
||||
VAR = 1
|
||||
|
||||
class Parent:
|
||||
BOO = 12
|
||||
|
||||
def __init__(self):
|
||||
self.c = 1
|
||||
|
||||
|
||||
class Child(Parent): # Try to pull members up
|
||||
CLASS_FIELD = 42
|
||||
ANOTHER_CLASS_FIELD = CLASS_FIELD
|
||||
FIELD = Parent.BOO
|
||||
A_FIELD = DataHolder.VAR
|
||||
|
||||
@staticmethod
|
||||
def foo():
|
||||
return "A"
|
||||
|
||||
SOME_VAR = foo()
|
||||
|
||||
def __init__(self):
|
||||
super(Child, self).__init__()
|
||||
self.a = 12
|
||||
self.b = self.c
|
||||
self.d = Parent.BOO
|
||||
i = 1
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.jetbrains.python.refactoring.classes;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.text.Matcher;
|
||||
import com.jetbrains.python.fixtures.PyTestCase;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public class PyDependenciesComparatorTest extends PyTestCase {
|
||||
|
||||
public void test() {
|
||||
myFixture.configureByFile("/refactoring/dependenciesTest.py");
|
||||
final PyClass clazz = getClassByName("Foo");
|
||||
|
||||
@SuppressWarnings("ConstantConditions") // Can't be null (class has docstring)
|
||||
PsiElement docStringExpression = clazz.getDocStringExpression().getParent();
|
||||
PyFunction method = clazz.getMethods()[0];
|
||||
PsiElement classField = clazz.getClassAttributes().get(0).getParent();
|
||||
|
||||
final List<PyStatement> elementList = new ArrayList<PyStatement>();
|
||||
elementList.addAll(Arrays.asList(clazz.getStatementList().getStatements()));
|
||||
Collections.sort(elementList, PyDependenciesComparator.INSTANCE);
|
||||
|
||||
Assert.assertThat("Members returned in wrong order", elementList, Matchers.contains(
|
||||
docStringExpression, classField, method
|
||||
));
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -84,6 +84,11 @@ public class PyPullUpTest extends PyClassRefactoringTest {
|
||||
doHelperTest("Child", "#foo", "Parent");
|
||||
}
|
||||
|
||||
// Ensures than all fields are moved in correct order
|
||||
public void testDependenciesOrder() {
|
||||
doHelperTestSeveralMembers("Child", "Parent", "#CLASS_FIELD", "#ANOTHER_CLASS_FIELD", "#FIELD", ".foo", "#SOME_VAR", "#b", "#d", "#A_FIELD");
|
||||
}
|
||||
|
||||
public void testMultiFile() { // PY-2810
|
||||
doMultiFileTest();
|
||||
}
|
||||
@@ -142,8 +147,12 @@ public class PyPullUpTest extends PyClassRefactoringTest {
|
||||
|
||||
|
||||
private void doHelperTest(final String className, final String memberName, final String superClassName) {
|
||||
doHelperTestSeveralMembers(className, superClassName, memberName);
|
||||
}
|
||||
|
||||
private void doHelperTestSeveralMembers(@NotNull final String className, @NotNull final String superClassName, @NotNull final String... memberNames) {
|
||||
myFixture.configureByFile(getMultiFileBaseName() + ".py");
|
||||
doPullUp(className, superClassName, memberName);
|
||||
doPullUp(className, superClassName, false, memberNames);
|
||||
myFixture.checkResultByFile(getMultiFileBaseName() + ".after.py");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user