mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -279,7 +279,7 @@ public class TemplateBuilderImpl implements TemplateBuilder {
|
||||
public void run() {
|
||||
final Project project = myFile.getProject();
|
||||
VirtualFile file = myFile.getVirtualFile();
|
||||
assert file != null;
|
||||
assert file != null: "Virtual file is null for " + file;
|
||||
OpenFileDescriptor descriptor = new OpenFileDescriptor(project, file);
|
||||
final Editor editor = FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
|
||||
|
||||
|
||||
@@ -155,6 +155,7 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
|
||||
|
||||
List<PyTargetExpression> getClassAttributes();
|
||||
|
||||
@Nullable
|
||||
PyTargetExpression findClassAttribute(@NotNull String name, boolean inherited);
|
||||
|
||||
List<PyTargetExpression> getInstanceAttributes();
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import static com.jetbrains.python.psi.PyUtil.sure;
|
||||
|
||||
/**
|
||||
* TODO: Refactor and move to {@link com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil#createMethod(String, com.jetbrains.python.psi.PyClass, com.jetbrains.python.psi.PyFunction.Modifier, String...)}
|
||||
* Adds a method foo to class X if X.foo() is unresolved.
|
||||
* User: dcheryasov
|
||||
* Date: Apr 5, 2009 6:51:26 PM
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
|
||||
import com.jetbrains.python.codeInsight.imports.AddImportHelper;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.impl.PyFunctionBuilder;
|
||||
import com.jetbrains.python.psi.impl.PyImportedModule;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
|
||||
@@ -40,6 +41,9 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.jetbrains.python.psi.PyFunction.Modifier.CLASSMETHOD;
|
||||
import static com.jetbrains.python.psi.PyFunction.Modifier.STATICMETHOD;
|
||||
|
||||
/**
|
||||
* @author Dennis.Ushakov
|
||||
*/
|
||||
@@ -140,15 +144,15 @@ public class PyClassRefactoringUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves class field to another class
|
||||
* Moves class field declarations to some other place
|
||||
* @param expressions list of class fields
|
||||
* @param superClass where to move them
|
||||
*/
|
||||
public static void moveFields(@NotNull final Collection<PyTargetExpression> expressions, @NotNull final PyClass superClass) {
|
||||
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";
|
||||
final PyStatementList superClassStatement = superClass.getStatementList();
|
||||
PyUtil.addElementToStatementList(expAssignmentStatement.copy(), superClassStatement, true);
|
||||
expAssignmentStatement.delete();
|
||||
PyPsiUtils.removeRedundantPass(superClassStatement);
|
||||
@@ -437,4 +441,41 @@ public class PyClassRefactoringUtil {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 parameterNames method parameters
|
||||
* @return newly created method
|
||||
*/
|
||||
@NotNull
|
||||
public static PyFunction createMethod(@NotNull final String methodName,
|
||||
@NotNull final PyClass pyClass,
|
||||
@Nullable final PyFunction.Modifier modifier,
|
||||
@NotNull final String... parameterNames) {
|
||||
final PyFunctionBuilder builder = new PyFunctionBuilder(methodName);
|
||||
|
||||
|
||||
//TODO: Take names from codestyle?
|
||||
if (modifier == null) {
|
||||
builder.parameter(PyNames.CANONICAL_SELF);
|
||||
}
|
||||
else if (modifier == CLASSMETHOD) {
|
||||
builder.parameter(PyNames.CANONICAL_CLS);
|
||||
builder.decorate(PyNames.CLASSMETHOD);
|
||||
}
|
||||
else if (modifier == STATICMETHOD) {
|
||||
builder.decorate(PyNames.STATICMETHOD);
|
||||
}
|
||||
|
||||
for (final String parameterName : parameterNames) {
|
||||
builder.parameter(parameterName);
|
||||
}
|
||||
|
||||
final PyFunction function = builder.addFunction(pyClass.getStatementList(), LanguageLevel.getDefault());
|
||||
addMethods(pyClass, new PyElement[]{function}, true);
|
||||
return function;
|
||||
}
|
||||
}
|
||||
|
||||
+18
-35
@@ -1,57 +1,40 @@
|
||||
package com.jetbrains.python.refactoring.classes.membersManager;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.intellij.psi.PsiElement;
|
||||
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;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Moves class attributes up
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
class ClassFieldsManager extends MembersManager<PyTargetExpression> {
|
||||
class ClassFieldsManager extends FieldsManager {
|
||||
|
||||
ClassFieldsManager() {
|
||||
super(PyTargetExpression.class);
|
||||
super(true);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void moveMembers(@NotNull final PyClass from,
|
||||
@NotNull final PyClass to,
|
||||
@NotNull final Collection<PyTargetExpression> members) {
|
||||
PyClassRefactoringUtil.moveFieldDeclarationToStatement(members, to.getStatementList());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean classHasField(@NotNull final PyClass pyClass, @NotNull final String fieldName) {
|
||||
return pyClass.findClassAttribute(fieldName, true) != null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<PyElement> getMembersCouldBeMoved(@NotNull final PyClass pyClass) {
|
||||
return new ArrayList<PyElement>(Collections2.filter(pyClass.getClassAttributes(), new SimpleAssignmentsOnly()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void moveMembers(@NotNull final PyClass from, @NotNull final PyClass to, @NotNull final Collection<PyTargetExpression> members) {
|
||||
PyClassRefactoringUtil.moveFields(members, to);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyMemberInfo apply(@NotNull final PyElement input) {
|
||||
return new PyMemberInfo(input, true, input.getText(), false, this); //TODO: Check overrides
|
||||
}
|
||||
|
||||
private static class SimpleAssignmentsOnly implements Predicate<PyTargetExpression> {
|
||||
//Support only simplest cases like CLASS_VAR = 42.
|
||||
//Tuples (CLASS_VAR_1, CLASS_VAR_2) = "spam", "eggs" are not supported by now
|
||||
@Override
|
||||
public boolean apply(@Nullable final PyTargetExpression input) {
|
||||
if (input == null) {
|
||||
return false; //Filter out empties (which probably would never be here)
|
||||
}
|
||||
final PsiElement parent = input.getParent();
|
||||
return (parent != null) && PyAssignmentStatement.class.isAssignableFrom(parent.getClass());
|
||||
}
|
||||
protected List<PyTargetExpression> getFieldsByClass(@NotNull PyClass pyClass) {
|
||||
return pyClass.getClassAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.jetbrains.python.refactoring.classes.membersManager;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.psi.PsiElement;
|
||||
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;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Parent of all field-based plugins (like class fields, instance fields and so on)
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
abstract class FieldsManager extends MembersManager<PyTargetExpression> {
|
||||
private static final SimpleAssignmentsOnly SIMPLE_ASSIGNMENTS_ONLY = new SimpleAssignmentsOnly();
|
||||
private final boolean myStatic;
|
||||
|
||||
/**
|
||||
* @param isStatic is field static or not?
|
||||
*/
|
||||
protected FieldsManager(final boolean isStatic) {
|
||||
super(PyTargetExpression.class);
|
||||
myStatic = isStatic;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<PyElement> getMembersCouldBeMoved(@NotNull final PyClass pyClass) {
|
||||
return Lists.<PyElement>newArrayList(Collections2.filter(getFieldsByClass(pyClass), SIMPLE_ASSIGNMENTS_ONLY));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if class has fields. Only child may know how to obtain field
|
||||
* @param pyClass class to check
|
||||
* @param fieldName field name
|
||||
* @return true if has one
|
||||
*/
|
||||
protected abstract boolean classHasField(@NotNull PyClass pyClass, @NotNull String fieldName);
|
||||
|
||||
/**
|
||||
* Returns all fields by class. Only child may know how to obtain fields
|
||||
* @param pyClass class to check
|
||||
* @return list of fields in target expression (declaration) form
|
||||
*/
|
||||
@NotNull
|
||||
protected abstract List<PyTargetExpression> getFieldsByClass(@NotNull PyClass pyClass);
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyMemberInfo apply(@NotNull final PyElement input) {
|
||||
return new PyMemberInfo(input, myStatic, input.getText(), isOverrides((PyTargetExpression)input), this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Boolean isOverrides(@NotNull final PyTargetExpression input) {
|
||||
final PyClass aClass = input.getContainingClass();
|
||||
final String name = input.getName();
|
||||
if (name == null) {
|
||||
return null; //Field with out of name can't override something
|
||||
}
|
||||
|
||||
assert aClass != null : "Target expression declared outside of class:" + input;
|
||||
|
||||
return classHasField(aClass, name) ? true : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void moveMembers(@NotNull final PyClass from,
|
||||
@NotNull final PyClass to,
|
||||
@NotNull final Collection<PyTargetExpression> members) {
|
||||
PyClassRefactoringUtil.moveFieldDeclarationToStatement(members, to.getStatementList());
|
||||
}
|
||||
|
||||
|
||||
private static class SimpleAssignmentsOnly implements Predicate<PyTargetExpression> {
|
||||
//Support only simplest cases like CLASS_VAR = 42.
|
||||
//Tuples (CLASS_VAR_1, CLASS_VAR_2) = "spam", "eggs" are not supported by now
|
||||
@Override
|
||||
public boolean apply(@Nullable final PyTargetExpression input) {
|
||||
if (input == null) {
|
||||
return false; //Filter out empties (which probably would never be here)
|
||||
}
|
||||
final PsiElement parent = input.getParent();
|
||||
return (parent != null) && PyAssignmentStatement.class.isAssignableFrom(parent.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.jetbrains.python.refactoring.classes.membersManager;
|
||||
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
class InstanceFieldsManager extends FieldsManager {
|
||||
InstanceFieldsManager() {
|
||||
super(false);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void moveMembers(@NotNull final PyClass from,
|
||||
@NotNull final PyClass to,
|
||||
@NotNull final Collection<PyTargetExpression> members) {
|
||||
//We need __init__ method, and if there is no any -- we need to create it
|
||||
PyFunction initMethod = to.findMethodByName(PyNames.INIT, false);
|
||||
if (initMethod == null) {
|
||||
initMethod = PyClassRefactoringUtil.createMethod(PyNames.INIT, to, null);
|
||||
}
|
||||
final PyStatementList statementList = initMethod.getStatementList();
|
||||
if (statementList == null) {
|
||||
return; //TODO: Investigate how could it be
|
||||
}
|
||||
PyClassRefactoringUtil.moveFieldDeclarationToStatement(members, statementList);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean classHasField(@NotNull final PyClass pyClass, @NotNull final String fieldName) {
|
||||
return pyClass.findInstanceAttribute(fieldName, true) != null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<PyTargetExpression> getFieldsByClass(@NotNull final PyClass pyClass) {
|
||||
return pyClass.getInstanceAttributes();
|
||||
}
|
||||
}
|
||||
+33
-2
@@ -2,13 +2,16 @@ 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.PsiNamedElement;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
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;
|
||||
@@ -28,7 +31,7 @@ public abstract class MembersManager<T extends PyElement> implements Function<Py
|
||||
* List of managers. Class delegates all logic to them.
|
||||
*/
|
||||
private static final Collection<? extends MembersManager<?>> MANAGERS =
|
||||
Arrays.asList(new MethodsManager(), new SuperClassesManager(), new ClassFieldsManager());
|
||||
Arrays.asList(new MethodsManager(), new SuperClassesManager(), new ClassFieldsManager(), new InstanceFieldsManager());
|
||||
private static final PyMemberExtractor PY_MEMBER_EXTRACTOR = new PyMemberExtractor();
|
||||
|
||||
@NotNull
|
||||
@@ -114,6 +117,22 @@ public abstract class MembersManager<T extends PyElement> implements Function<Py
|
||||
@NotNull
|
||||
protected abstract List<PyElement> getMembersCouldBeMoved(@NotNull PyClass pyClass);
|
||||
|
||||
|
||||
/**
|
||||
* Filters out named elements (ones that subclasses {@link com.intellij.psi.PsiNamedElement}) and {@link com.jetbrains.python.psi.PyElement})
|
||||
* that are null or has null name.
|
||||
* You need it sometimes when code has errors (i.e. bad formatted code with annotation may treat annotation as method with null name.
|
||||
* note: we should probably throw exceptions in such cases and display "refactoring not available" window in handler)
|
||||
*
|
||||
* @param elementsToFilter collection of elements to filter
|
||||
* @param <T> element type
|
||||
* @return collection of T with out of nulls and elemens whos {@link com.intellij.psi.PsiNamedElement#getName()} returns null
|
||||
*/
|
||||
@NotNull
|
||||
protected static <T extends PsiNamedElement & PyElement> Collection<T> filterNameless(@NotNull final Collection<T> elementsToFilter) {
|
||||
return Collections2.filter(elementsToFilter, new NamelessFilter<T>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves element from one class to another
|
||||
*
|
||||
@@ -123,7 +142,12 @@ public abstract class MembersManager<T extends PyElement> implements Function<Py
|
||||
*/
|
||||
protected abstract void moveMembers(@NotNull PyClass from, @NotNull PyClass to, @NotNull Collection<T> members);
|
||||
|
||||
//TODO: Doc
|
||||
/**
|
||||
* Creates {@link com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo} from {@link com.jetbrains.python.psi.PyElement}
|
||||
* This process is plugin-specific and should be implemented in each plugin
|
||||
* @param input element
|
||||
* @return member info
|
||||
*/
|
||||
@SuppressWarnings("NullableProblems") //IDEA-120100
|
||||
@NotNull
|
||||
@Override
|
||||
@@ -136,6 +160,13 @@ public abstract class MembersManager<T extends PyElement> implements Function<Py
|
||||
return input.getMember();
|
||||
}
|
||||
}
|
||||
|
||||
private static class NamelessFilter<T extends PyElement & PsiNamedElement> implements Predicate<T> {
|
||||
@Override
|
||||
public boolean apply(@Nullable final T input) {
|
||||
return (input != null) && (input.getName() != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+6
-3
@@ -1,5 +1,6 @@
|
||||
package com.jetbrains.python.refactoring.classes.membersManager;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -23,7 +24,7 @@ class MethodsManager extends MembersManager<PyFunction> {
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<PyElement> getMembersCouldBeMoved(@NotNull final PyClass pyClass) {
|
||||
return Arrays.<PyElement>asList(pyClass.getMethods());
|
||||
return Lists.<PyElement>newArrayList(filterNameless(Arrays.asList(pyClass.getMethods())));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -36,8 +37,10 @@ class MethodsManager extends MembersManager<PyFunction> {
|
||||
public PyMemberInfo apply(@NotNull final PyElement input) {
|
||||
//TODO: Use generics to prevent casting in each subclass
|
||||
final PyFunction pyFunction = (PyFunction)input;
|
||||
//TODO: Support static and classmethod functions
|
||||
return new PyMemberInfo(input, false, buildDisplayMethodName(pyFunction), isOverrides(pyFunction), this);
|
||||
final PyUtil.MethodFlags flags = PyUtil.MethodFlags.of(pyFunction);
|
||||
assert flags != null : "No flags return while element is function " + pyFunction;
|
||||
final boolean isStatic = flags.isStaticMethod() || flags.isClassMethod();
|
||||
return new PyMemberInfo(input, isStatic, buildDisplayMethodName(pyFunction), isOverrides(pyFunction), this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
+3
-3
@@ -25,7 +25,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
*/
|
||||
public class PyMemberInfo extends MemberInfoBase<PyElement> {
|
||||
@NotNull
|
||||
private final MembersManager myMembersManager;
|
||||
private final MembersManager<?> myMembersManager;
|
||||
|
||||
/**
|
||||
* @param member element itself
|
||||
@@ -39,7 +39,7 @@ public class PyMemberInfo extends MemberInfoBase<PyElement> {
|
||||
final boolean isStatic,
|
||||
@NotNull final String displayName,
|
||||
@Nullable final Boolean overrides,
|
||||
@NotNull final MembersManager membersManager) {
|
||||
@NotNull final MembersManager<?> membersManager) {
|
||||
super(member);
|
||||
this.isStatic = isStatic;
|
||||
this.displayName = displayName;
|
||||
@@ -48,7 +48,7 @@ public class PyMemberInfo extends MemberInfoBase<PyElement> {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
MembersManager getMembersManager() {
|
||||
MembersManager<?> getMembersManager() {
|
||||
return myMembersManager;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ public class PyPullUpConflictsUtil {
|
||||
for (PyMemberInfo info : infos) {
|
||||
PsiElement member = info.getMember();
|
||||
boolean isConflict = false;
|
||||
//TODO: Delegate to MemeberManagers here
|
||||
if (member instanceof PyFunction) {
|
||||
final String name = ((PyFunction)member).getName();
|
||||
if (name == null) continue;
|
||||
|
||||
+3
-1
@@ -31,6 +31,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* Pull-up presenter implementation
|
||||
@@ -87,12 +88,13 @@ class PyPullUpPresenterImpl extends AbstractUsesDependencyMemberInfoModel<PyElem
|
||||
public boolean isMemberEnabled(PyMemberInfo member) {
|
||||
PyClass currentSuperClass = myView.getSelectedParent();
|
||||
if (member.getMember() instanceof PyClass) {
|
||||
//TODO: Delegate to Memebers Managers
|
||||
PyClass memberClass = (PyClass)member.getMember();
|
||||
if (memberClass.isSubclass(currentSuperClass) || currentSuperClass.isSubclass(memberClass)) {
|
||||
return false; //Class is already parent of superclass
|
||||
}
|
||||
}
|
||||
if (! PyPullUpConflictsUtil.checkConflicts(Arrays.asList(member), myView.getSelectedParent()).isEmpty()) {
|
||||
if (! PyPullUpConflictsUtil.checkConflicts(Collections.singletonList(member), myView.getSelectedParent()).isEmpty()) {
|
||||
return false; //Member has conflict
|
||||
}
|
||||
return (!myStorage.getDuplicatedMemberInfos(currentSuperClass).contains(member)) && member.getMember() != currentSuperClass;
|
||||
|
||||
@@ -59,6 +59,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* * TODO: Merge with {@link com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil#createMethod(String, com.jetbrains.python.psi.PyClass, com.jetbrains.python.psi.PyFunction.Modifier, String...)}
|
||||
* @author oleg
|
||||
*/
|
||||
public class PyExtractMethodUtil {
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
class Parent:
|
||||
def __init__(self):
|
||||
self.instance_field = "eggs"
|
||||
|
||||
|
||||
class Child(Parent):
|
||||
def __init__(self):
|
||||
Parent2.__init__(self)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
class Parent:
|
||||
pass
|
||||
|
||||
|
||||
class Child(Parent):
|
||||
def __init__(self):
|
||||
Parent2.__init__(self)
|
||||
self.instance_field = "eggs"
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
class Parent:
|
||||
def __init__(self):
|
||||
self.instance_field = "eggs"
|
||||
|
||||
|
||||
class Child(Parent):
|
||||
def __init__(self):
|
||||
Parent2.__init__(self)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
class Parent:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class Child(Parent):
|
||||
def __init__(self):
|
||||
Parent2.__init__(self)
|
||||
self.instance_field = "eggs"
|
||||
|
||||
@@ -29,10 +29,27 @@ class NoMembers(object):
|
||||
class BadMro(MainParent, object, SubParent1, SubParent2):
|
||||
pass
|
||||
|
||||
class SomeMembersDisabled(SubParent1, date): #SubParent1 is disabled
|
||||
class HugeChild(SubParent1, date): #SubParent1 is disabled
|
||||
def __init__(self):
|
||||
self.instance_field_1 = 42
|
||||
self.instance_field_2 = 100500
|
||||
|
||||
CLASS_FIELD = 42
|
||||
(CLASS_FIELD_A,CLASS_FIELD_B) = (42,100500) We do not support tuples in class assignments for now (see ClassFieldsManager)
|
||||
(CLASS_FIELD_A,CLASS_FIELD_B) = (42,100500) #We do not support tuples in class assignments for now (see ClassFieldsManager)
|
||||
def foo(self): #should be disabled
|
||||
pass
|
||||
def bar(self):
|
||||
pass
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def static_1(cls):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def static_2():
|
||||
pass
|
||||
|
||||
|
||||
@staticmethod
|
||||
def bad_method(): #Code has errors, so method should be not be marked as static
|
||||
pass
|
||||
@@ -20,6 +20,7 @@ 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 org.hamcrest.Matchers;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -56,7 +57,12 @@ public abstract class PyClassRefactoringTest extends PyTestCase {
|
||||
}
|
||||
|
||||
private PyElement findField(final String className, final String memberName) {
|
||||
return findClass(className).findClassAttribute(memberName, false);
|
||||
final PyClass aClass = findClass(className);
|
||||
final PyTargetExpression attribute = aClass.findClassAttribute(memberName, false);
|
||||
if (attribute != null) {
|
||||
return attribute;
|
||||
}
|
||||
return aClass.findInstanceAttribute(memberName, false);
|
||||
}
|
||||
|
||||
private PyFunction findMethod(final String className, final String name) {
|
||||
|
||||
@@ -57,6 +57,13 @@ public class PyPullUpTest extends PyClassRefactoringTest {
|
||||
doHelperTest("Child2", "#CLASS_VAR", "Parent2");
|
||||
}
|
||||
|
||||
public void testMoveInstanceAttributesSimple() {
|
||||
doHelperTest("Child", "#instance_field", "Parent");
|
||||
}
|
||||
public void testMoveInstanceAttributesNoInit() {
|
||||
doHelperTest("Child", "#instance_field", "Parent");
|
||||
}
|
||||
|
||||
public void testMultiFile() { // PY-2810
|
||||
doMultiFileTest();
|
||||
}
|
||||
|
||||
+68
-18
@@ -2,18 +2,18 @@ package com.jetbrains.python.refactoring.classes.pullUp;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.refactoring.classMembers.MemberInfoModel;
|
||||
import com.jetbrains.python.fixtures.PyTestCase;
|
||||
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.membersManager.PyMemberInfo;
|
||||
import org.easymock.Capture;
|
||||
import org.easymock.EasyMock;
|
||||
import org.easymock.internal.MocksControl;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.Assert;
|
||||
|
||||
@@ -70,12 +70,14 @@ public class PyPullUpPresenterTest extends PyTestCase {
|
||||
public void testNoParents() throws Exception {
|
||||
ensureNoMembers("NoParentsAllowed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that refactoring does not work for classes with out of members
|
||||
*/
|
||||
public void testNoMembers() throws Exception {
|
||||
ensureNoMembers("NoMembers");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that refactoring does not work when C3 MRO can't be calculated
|
||||
*/
|
||||
@@ -84,10 +86,10 @@ public class PyPullUpPresenterTest extends PyTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that some members are not allowed
|
||||
* Checks that some members are not allowed, while others are
|
||||
*/
|
||||
public void testDisabledMembers() throws Exception {
|
||||
PyPullUpPresenterImpl sut = configureByClass("SomeMembersDisabled");
|
||||
public void testMembers() throws Exception {
|
||||
PyPullUpPresenterImpl sut = configureByClass("HugeChild");
|
||||
EasyMock.expect(myView.getSelectedParent()).andReturn(getClassByName("SubParent1")).anyTimes();
|
||||
|
||||
myMocksControl.replay();
|
||||
@@ -96,16 +98,22 @@ public class PyPullUpPresenterTest extends PyTestCase {
|
||||
Assert.assertTrue("No members selected", myMemberInfos.hasCaptured());
|
||||
List<PyMemberInfo> members = myMemberInfos.getValue();
|
||||
Assert.assertFalse("No members selected", members.isEmpty());
|
||||
Collection<Pair<String, Boolean>> memberNamesAndStatus = Collections2.transform(members, new NameAndStatusTransformer(sut));
|
||||
final Collection<Entry> memberNamesAndStatus = Collections2.transform(members, new NameAndStatusTransformer(sut));
|
||||
|
||||
//Pair will return correct type
|
||||
@SuppressWarnings("unchecked") Matcher<Iterable<? extends Pair<String, Boolean>>> matcher = Matchers
|
||||
.containsInAnyOrder(
|
||||
Pair.create("date", true),
|
||||
Pair.create("CLASS_FIELD", true),
|
||||
Pair.create("SubParent1", false),
|
||||
Pair.create("foo", false),
|
||||
Pair.create("bar", true));
|
||||
final Matcher<Iterable<? extends Entry>> matcher = Matchers
|
||||
.containsInAnyOrder(new Entry("extends date", true, false),
|
||||
new Entry("CLASS_FIELD", true, true),
|
||||
new Entry("__init__(self)", true, false),
|
||||
new Entry("extends SubParent1", false, false),
|
||||
new Entry("foo(self)", false, false),
|
||||
new Entry("bar(self)", true, false),
|
||||
new Entry("static_1(cls)", true, true),
|
||||
new Entry("static_2()", true, true),
|
||||
new Entry("self.instance_field_1", true, false),
|
||||
new Entry("self.instance_field_2", true, false),
|
||||
new Entry("bad_method()", true, false)
|
||||
);
|
||||
Assert.assertThat("Wrong members or their states", memberNamesAndStatus, matcher);
|
||||
|
||||
|
||||
@@ -128,7 +136,6 @@ public class PyPullUpPresenterTest extends PyTestCase {
|
||||
}
|
||||
|
||||
|
||||
|
||||
private PyPullUpPresenterImpl configureByClass(String name) {
|
||||
PyClass childClass = getClassByName(name);
|
||||
PyMemberInfoStorage storage = new PyMemberInfoStorage(childClass);
|
||||
@@ -148,7 +155,7 @@ public class PyPullUpPresenterTest extends PyTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
private static class NameAndStatusTransformer implements Function<PyMemberInfo, Pair<String, Boolean>> {
|
||||
private static class NameAndStatusTransformer implements Function<PyMemberInfo, Entry> {
|
||||
private final PyPullUpPresenterImpl presenter;
|
||||
|
||||
private NameAndStatusTransformer(PyPullUpPresenterImpl presenter) {
|
||||
@@ -156,9 +163,52 @@ public class PyPullUpPresenterTest extends PyTestCase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<String, Boolean> apply(PyMemberInfo input) {
|
||||
PyElement member = input.getMember();
|
||||
return Pair.create(member.getName(), presenter.isMemberEnabled(input));
|
||||
public Entry apply(final PyMemberInfo input) {
|
||||
return new Entry(input.getDisplayName(), presenter.isMemberEnabled(input), input.isStatic());
|
||||
}
|
||||
}
|
||||
|
||||
private static class Entry {
|
||||
@NonNls @NotNull
|
||||
private final String myName;
|
||||
private final boolean myEnabled;
|
||||
private final boolean myStaticEntry;
|
||||
|
||||
private Entry(@NotNull final String name, final boolean enabled, final boolean staticEntry) {
|
||||
myName = name;
|
||||
myEnabled = enabled;
|
||||
myStaticEntry = staticEntry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Entry{" +
|
||||
"myName='" + myName + '\'' +
|
||||
", myEnabled=" + myEnabled +
|
||||
", myStaticEntry=" + myStaticEntry +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof Entry)) return false;
|
||||
|
||||
final Entry entry = (Entry)o;
|
||||
|
||||
if (myEnabled != entry.myEnabled) return false;
|
||||
if (myStaticEntry != entry.myStaticEntry) return false;
|
||||
if (!myName.equals(entry.myName)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myName.hashCode();
|
||||
result = 31 * result + (myEnabled ? 1 : 0);
|
||||
result = 31 * result + (myStaticEntry ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user