mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-12178 Support pull up / push down for properties (not ready yet: only properties excluded from other managers)
This commit is contained in:
@@ -32,6 +32,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents a class declaration in source.
|
||||
@@ -112,6 +113,13 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
|
||||
@NotNull
|
||||
PyFunction[] getMethods();
|
||||
|
||||
/**
|
||||
* Get class properties.
|
||||
* @return Map [property_name] = [{@link com.jetbrains.python.psi.Property}]
|
||||
*/
|
||||
@NotNull
|
||||
Map<String, Property> getProperties();
|
||||
|
||||
/**
|
||||
* Finds a method with given name.
|
||||
* @param name what to look for
|
||||
|
||||
@@ -380,6 +380,13 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
|
||||
return getClassChildren(PythonDialectsTokenSetProvider.INSTANCE.getFunctionDeclarationTokens(), PyFunction.ARRAY_FACTORY);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Map<String, Property> getProperties() {
|
||||
initProperties();
|
||||
return new HashMap<String, Property>(myPropertyCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PyClass[] getNestedClasses() {
|
||||
return getClassChildren(TokenSet.create(PyElementTypes.CLASS_DECLARATION), PyClass.ARRAY_FACTORY);
|
||||
@@ -607,9 +614,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
|
||||
|
||||
@Override
|
||||
public Property findPropertyByCallable(Callable callable) {
|
||||
if (myPropertyCache == null) {
|
||||
myPropertyCache = initializePropertyCache();
|
||||
}
|
||||
initProperties();
|
||||
for (Property property : myPropertyCache.values()) {
|
||||
if (property.getGetter().valueOrNull() == callable ||
|
||||
property.getSetter().valueOrNull() == callable ||
|
||||
@@ -621,10 +626,14 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
|
||||
}
|
||||
|
||||
private Property findLocalProperty(String name) {
|
||||
initProperties();
|
||||
return myPropertyCache.get(name);
|
||||
}
|
||||
|
||||
private synchronized void initProperties() {
|
||||
if (myPropertyCache == null) {
|
||||
myPropertyCache = initializePropertyCache();
|
||||
}
|
||||
return myPropertyCache.get(name);
|
||||
}
|
||||
|
||||
private Map<String, Property> initializePropertyCache() {
|
||||
|
||||
+39
-6
@@ -1,9 +1,9 @@
|
||||
package com.jetbrains.python.refactoring.classes.membersManager;
|
||||
|
||||
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.google.common.collect.FluentIterable;
|
||||
import com.jetbrains.NotNullPredicate;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -32,7 +32,7 @@ class ClassFieldsManager extends FieldsManager {
|
||||
@NotNull final Collection<PyAssignmentStatement> statements,
|
||||
@NotNull final PyClass... to) {
|
||||
//TODO: Copy/paste with InstanceFieldsManager. Move to parent?
|
||||
final List<PyElement> result = new ArrayList<PyElement>();
|
||||
final Collection<PyElement> result = new ArrayList<PyElement>();
|
||||
for (final PyClass destClass : to) {
|
||||
result.addAll(PyClassRefactoringUtil.copyFieldDeclarationToStatement(statements, destClass.getStatementList(), destClass));
|
||||
}
|
||||
@@ -49,6 +49,39 @@ class ClassFieldsManager extends FieldsManager {
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<PyTargetExpression> getFieldsByClass(@NotNull final PyClass pyClass) {
|
||||
return pyClass.getClassAttributes();
|
||||
return FluentIterable.from(pyClass.getClassAttributes()).filter(new NoMetaAndProperties(pyClass)).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclude "__metaclass__" field and properties (there should be separate managers for them)
|
||||
* TODO: Check type and filter out any builtin element instead?
|
||||
*/
|
||||
private static class NoMetaAndProperties extends NotNullPredicate<PyTargetExpression> {
|
||||
@NotNull
|
||||
private final PyClass myClass;
|
||||
|
||||
private NoMetaAndProperties(@NotNull final PyClass aClass) {
|
||||
myClass = aClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applyNotNull(@NotNull final PyTargetExpression input) {
|
||||
final String name = input.getName();
|
||||
if (name == null) {
|
||||
return false;
|
||||
}
|
||||
if (name.equals(PyNames.DUNDER_METACLASS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final PyExpression assignedValue = input.findAssignedValue();
|
||||
if (assignedValue instanceof PyCallExpression) {
|
||||
final PyExpression callee = ((PyCallExpression)assignedValue).getCallee();
|
||||
if ((callee != null) && PyNames.PROPERTY.equals(callee.getName()) && (myClass.findProperty(name, false) != null)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-24
@@ -84,7 +84,7 @@ public abstract class MembersManager<T extends PyElement> implements Function<T,
|
||||
@SuppressWarnings({"unchecked", "rawtypes"}) //We check type at runtime
|
||||
private static Collection<PyMemberInfo<PyElement>> transformSafely(@NotNull final PyClass pyClass,
|
||||
@NotNull final MembersManager<?> manager) {
|
||||
final List<PyElement> membersCouldBeMoved = manager.getMembersCouldBeMoved(pyClass);
|
||||
final List<? extends PyElement> membersCouldBeMoved = manager.getMembersCouldBeMoved(pyClass);
|
||||
manager.checkElementTypes((Iterable)membersCouldBeMoved);
|
||||
return (Collection<PyMemberInfo<PyElement>>)Collections2.transform(membersCouldBeMoved, (Function)manager);
|
||||
}
|
||||
@@ -195,24 +195,9 @@ public abstract class MembersManager<T extends PyElement> implements Function<T,
|
||||
* @return list of members
|
||||
*/
|
||||
@NotNull
|
||||
protected abstract List<PyElement> getMembersCouldBeMoved(@NotNull PyClass pyClass);
|
||||
protected abstract List<? extends 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>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of elements that may require reference storing aid from {@link com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil#rememberNamedReferences(com.intellij.psi.PsiElement, String...)}
|
||||
*
|
||||
@@ -357,13 +342,6 @@ public abstract class MembersManager<T extends PyElement> implements Function<T,
|
||||
}
|
||||
}
|
||||
|
||||
private static class NamelessFilter<T extends PyElement & PsiNamedElement> extends NotNullPredicate<T> {
|
||||
@Override
|
||||
public boolean applyNotNull(@NotNull final T input) {
|
||||
return input.getName() != null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class FindByElement extends NotNullPredicate<PyMemberInfo<PyElement>> {
|
||||
private final PyElement myPyElement;
|
||||
|
||||
|
||||
+15
-3
@@ -1,7 +1,8 @@
|
||||
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.google.common.collect.FluentIterable;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
@@ -35,6 +36,7 @@ class MethodsManager extends MembersManager<PyFunction> {
|
||||
{PyNames.PROPERTY, PyNames.CLASSMETHOD, PyNames.STATICMETHOD};
|
||||
|
||||
public static final String ABC_META_PACKAGE = "abc";
|
||||
private static final NoPropertiesPredicate NO_PROPERTIES = new NoPropertiesPredicate();
|
||||
|
||||
MethodsManager() {
|
||||
super(PyFunction.class);
|
||||
@@ -62,8 +64,8 @@ class MethodsManager extends MembersManager<PyFunction> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<PyElement> getMembersCouldBeMoved(@NotNull final PyClass pyClass) {
|
||||
return Lists.<PyElement>newArrayList(filterNameless(Arrays.asList(pyClass.getMethods())));
|
||||
protected List<? extends PyElement> getMembersCouldBeMoved(@NotNull final PyClass pyClass) {
|
||||
return FluentIterable.from(Arrays.asList(pyClass.getMethods())).filter(new NamelessFilter<PyFunction>()).filter(NO_PROPERTIES).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -281,4 +283,14 @@ class MethodsManager extends MembersManager<PyFunction> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter out property setters and getters
|
||||
*/
|
||||
private static class NoPropertiesPredicate implements Predicate<PyFunction> {
|
||||
@Override
|
||||
public boolean apply(@NotNull PyFunction input) {
|
||||
return input.getProperty() == null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.jetbrains.python.refactoring.classes.membersManager;
|
||||
|
||||
import com.intellij.psi.PsiNamedElement;
|
||||
import com.jetbrains.NotNullPredicate;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
class NamelessFilter<T extends PyElement & PsiNamedElement> extends NotNullPredicate<T> {
|
||||
|
||||
@Override
|
||||
public boolean applyNotNull(@NotNull final T input) {
|
||||
return input.getName() != null;
|
||||
}
|
||||
}
|
||||
@@ -30,12 +30,36 @@ class BadMro(MainParent, object, SubParent1, SubParent2):
|
||||
pass
|
||||
|
||||
class HugeChild(SubParent1, date): #SubParent1 is disabled
|
||||
__metaclass__ = None # Anyway, this field should be ignored and processed separately as "metaclass", not "class field"
|
||||
|
||||
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)
|
||||
|
||||
def _set(self, val): # Should not be treated as method (part of property)
|
||||
pass
|
||||
|
||||
def _get(self): # Should not be treated as method (part of property)
|
||||
return None
|
||||
|
||||
name = property(fget=_get, fset=_set)
|
||||
|
||||
|
||||
@property
|
||||
def some_property(self): # Should not be treated as method (part of property)
|
||||
return None
|
||||
|
||||
@some_property.setter
|
||||
def some_property(self, val): # Should not be treated as method (part of property)
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def foo(self): #should be disabled
|
||||
pass
|
||||
def bar(self):
|
||||
|
||||
Reference in New Issue
Block a user