Merge branch 'master' of git.labs.intellij.net:idea/ultimate

This commit is contained in:
Dmitry Cheryasov
2010-07-13 22:20:01 +03:00
28 changed files with 372 additions and 147 deletions
@@ -20,6 +20,9 @@ public class PyNames {
@NonNls public static final String GETATTR = "__getattr__";
@NonNls public static final String GETATTRIBUTE = "__getattribute__";
@NonNls public static final String CLASS = "__class__";
@NonNls public static final String METACLASS = "__metaclass__";
@NonNls public static final String SUPER = "super";
@NonNls public static final String OBJECT = "object";
@NonNls public static final String NONE = "None";
@@ -3,23 +3,29 @@ package com.jetbrains.python.codeInsight;
import com.intellij.codeInsight.completion.*;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.navigation.NavigationItem;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Iconable;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.ProjectScope;
import com.intellij.psi.stubs.StubIndex;
import com.intellij.psi.stubs.StubIndexKey;
import com.intellij.util.ProcessingContext;
import com.jetbrains.python.actions.AddImportHelper;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
import com.jetbrains.python.psi.stubs.PyClassNameIndex;
import com.jetbrains.python.psi.stubs.PyFunctionNameIndex;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
@@ -37,46 +43,79 @@ public class PyClassNameCompletionContributor extends CompletionContributor {
protected void addCompletions(@NotNull final CompletionParameters parameters,
ProcessingContext context,
@NotNull final CompletionResultSet resultSet) {
final Project project = parameters.getOriginalFile().getProject();
final GlobalSearchScope scope = ProjectScope.getAllScope(project);
final Collection<String> allKeys = ApplicationManager.getApplication().runReadAction(new Computable<Collection<String>>() {
public Collection<String> compute() {
return PyClassNameIndex.allKeys(project);
}
});
for (final String className : allKeys) {
if (resultSet.getPrefixMatcher().prefixMatches(className)) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
final Collection<PyClass> classes = PyClassNameIndex.find(className, project, scope);
for (PyClass aClass : classes) {
resultSet.addElement(LookupElementBuilder.create(aClass)
.setIcon(aClass.getIcon(Iconable.ICON_FLAG_CLOSED))
.setTailText(" " + aClass.getPresentation().getLocationString(), true)
.setInsertHandler(INSERT_HANDLER));
}
}
});
}
}
addVariantsFromIndex(resultSet, parameters.getOriginalFile(), PyClassNameIndex.KEY, CLASS_INSERT_HANDLER, Condition.TRUE);
addVariantsFromIndex(resultSet, parameters.getOriginalFile(), PyFunctionNameIndex.KEY, FUNCTION_INSERT_HANDLER, TOPLEVEL_FUNCTION);
}
});
}
private static class PyClassNameInsertHandler implements InsertHandler<LookupElement> {
public void handleInsert(final InsertionContext context, final LookupElement item) {
final PsiReference ref = context.getFile().findReferenceAt(context.getTailOffset() - 1);
if (ref == null || ref.resolve() == item.getObject()) {
// no import statement needed
return;
}
new WriteCommandAction(context.getProject(), context.getFile()) {
@Override
protected void run(Result result) throws Throwable {
addImport((PsiNamedElement) item.getObject(), context.getFile(), (PyElement) ref.getElement());
}
}.execute();
private static Condition<PyFunction> TOPLEVEL_FUNCTION = new Condition<PyFunction>() {
@Override
public boolean value(PyFunction pyFunction) {
return pyFunction.isTopLevel();
}
};
private static <T extends PsiNamedElement> void addVariantsFromIndex(final CompletionResultSet resultSet,
final PsiFile targetFile,
final StubIndexKey<String, T> key,
final InsertHandler<LookupElement> insertHandler,
final Condition<T> condition) {
final Project project = targetFile.getProject();
final GlobalSearchScope scope = ProjectScope.getAllScope(project);
final Collection<String> allNames = ApplicationManager.getApplication().runReadAction(new Computable<Collection<String>>() {
public Collection<String> compute() {
return StubIndex.getInstance().getAllKeys(key, project);
}
});
for (final String elementName : allNames) {
if (resultSet.getPrefixMatcher().prefixMatches(elementName)) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
final Collection<T> elements = StubIndex.getInstance().get(key, elementName, project, scope);
for (T element : elements) {
if (condition.value(element)) {
resultSet.addElement(LookupElementBuilder.create(element)
.setIcon(element.getIcon(Iconable.ICON_FLAG_CLOSED))
.setTailText(" " + ((NavigationItem)element).getPresentation().getLocationString(), true)
.setInsertHandler(insertHandler));
}
}
}
});
}
}
}
private static final InsertHandler<LookupElement> CLASS_INSERT_HANDLER = new InsertHandler<LookupElement>() {
public void handleInsert(final InsertionContext context, final LookupElement item) {
addImportForLookupElement(context, item, context.getTailOffset() - 1);
}
};
private static final InsertHandler<LookupElement> FUNCTION_INSERT_HANDLER = new PyFunctionInsertHandler() {
public void handleInsert(final InsertionContext context, final LookupElement item) {
int tailOffset = context.getTailOffset()-1;
super.handleInsert(context, item); // adds parentheses, modifies tail offset
final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(context.getProject());
documentManager.commitDocument(documentManager.getDocument(context.getFile()));
addImportForLookupElement(context, item, tailOffset);
}
};
private static void addImportForLookupElement(final InsertionContext context, final LookupElement item, final int tailOffset) {
final PsiReference ref = context.getFile().findReferenceAt(tailOffset);
if (ref == null || ref.resolve() == item.getObject()) {
// no import statement needed
return;
}
new WriteCommandAction(context.getProject(), context.getFile()) {
@Override
protected void run(Result result) throws Throwable {
addImport((PsiNamedElement) item.getObject(), context.getFile(), (PyElement) ref.getElement());
}
}.execute();
}
private static void addImport(final PsiNamedElement target, final PsiFile file, final PyElement element) {
@@ -101,5 +140,4 @@ public class PyClassNameCompletionContributor extends CompletionContributor {
}
}
private static final PyClassNameInsertHandler INSERT_HANDLER = new PyClassNameInsertHandler();
}
@@ -16,8 +16,9 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyFunctionBuilder;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -30,6 +31,9 @@ import java.util.*;
public class PyOverrideImplementUtil {
private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.codeInsight.override.PyOverrideImplementUtil");
private PyOverrideImplementUtil() {
}
@Nullable
public static PyClass getContextClass(@NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile file) {
PsiDocumentManager.getInstance(project).commitAllDocuments();
@@ -90,22 +94,19 @@ public class PyOverrideImplementUtil {
overrideMethods(editor, pyClass, membersToOverride);
}
public static void overrideMethods(final Editor editor, final PyClass pyClass, List<PyMethodMember> membersToOverride) {
final List<String> newMembers = generateCode(membersToOverride);
if (newMembers.isEmpty()) {
public static void overrideMethods(final Editor editor, final PyClass pyClass, final List<PyMethodMember> membersToOverride) {
if (membersToOverride == null) {
return;
}
new WriteCommandAction(pyClass.getProject(), pyClass.getContainingFile()) {
protected void run(final Result result) throws Throwable {
write(pyClass, newMembers, pyClass.getProject(), editor);
write(pyClass, membersToOverride, editor);
}
}.execute();
}
private static void write(@NotNull final PyClass pyClass,
@NotNull final List<String> newMembers,
@NotNull final Project project,
@NotNull final List<PyMethodMember> newMembers,
@NotNull final Editor editor) {
final PyStatementList statementList = pyClass.getStatementList();
final int offset = editor.getCaretModel().getOffset();
@@ -117,15 +118,11 @@ public class PyOverrideImplementUtil {
}
PyFunction element = null;
for (String newMember : newMembers) {
element = PyElementGenerator.getInstance(project).createFromText(PyFunction.class, newMember + "\n pass");
try {
element = (PyFunction)statementList.addAfter(element, anchor);
element = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(element);
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
for (PyMethodMember newMember : newMembers) {
PyFunction baseFunction = (PyFunction) newMember.getPsiElement();
final PyFunctionBuilder builder = buildOverriddenFunction(pyClass, baseFunction);
PyFunction function = builder.addFunctionAfter(statementList, anchor);
element = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(function);
}
PyPsiUtils.removeRedundantPass(statementList);
@@ -135,31 +132,43 @@ public class PyOverrideImplementUtil {
editor.getSelectionModel().setSelection(start, element.getTextRange().getEndOffset());
}
private static List<String> generateCode(final List<PyMethodMember> members) {
if (members == null) {
return Collections.emptyList();
private static PyFunctionBuilder buildOverriddenFunction(PyClass pyClass, PyFunction baseFunction) {
PyFunctionBuilder pyFunctionBuilder = new PyFunctionBuilder(baseFunction.getName());
final PyDecoratorList decorators = baseFunction.getDecoratorList();
if (decorators != null && decorators.findDecorator(PyNames.CLASSMETHOD) != null) {
pyFunctionBuilder.decorate(PyNames.CLASSMETHOD);
}
List<String> newMembers = new ArrayList<String>();
for (PyMethodMember member : members) {
newMembers.add(generateNewMethod(member.getPsiElement()));
final PyParameter[] baseParams = baseFunction.getParameterList().getParameters();
for (PyParameter parameter : baseParams) {
pyFunctionBuilder.parameter(parameter.getText());
}
return newMembers;
}
@NotNull
private static String generateNewMethod(@NotNull final PsiElement element) {
assert (element instanceof PyFunction);
final PyFunction function = (PyFunction)element;
final StringBuilder newMethodText = new StringBuilder();
final PyDecoratorList decoratorList = function.getDecoratorList();
if (decoratorList != null) {
for (PyDecorator decorator: decoratorList.getDecorators()) {
if ("classmethod".equals(decorator.getCallee().getText())) {
newMethodText.append("@classmethod\n");
}
PyClass baseClass = baseFunction.getContainingClass();
assert baseClass != null;
StringBuilder statementBody = new StringBuilder();
if (baseClass.isNewStyleClass()) {
statementBody.append(PyNames.SUPER);
statementBody.append("(");
final LanguageLevel langLevel = ((PyFile)pyClass.getContainingFile()).getLanguageLevel();
if (!langLevel.isPy3K()) {
statementBody.append(pyClass.getName()).append(", self");
}
statementBody.append(").").append(baseFunction.getName()).append("(");
for (int i = 1; i < baseParams.length; i++) {
statementBody.append(baseParams [i].getText());
}
statementBody.append(")");
}
return newMethodText.append("def ").append(function.getName()).append(function.getParameterList().getText()).append(":").toString();
else {
statementBody.append(baseClass.getName()).append(".").append(baseFunction.getName()).append("(");
for (PyParameter param : baseParams) {
statementBody.append(param.getText());
}
statementBody.append(")");
}
pyFunctionBuilder.statement(statementBody.toString());
return pyFunctionBuilder;
}
@NotNull
@@ -173,7 +182,4 @@ public class PyOverrideImplementUtil {
}
return superFunctions.values();
}
private PyOverrideImplementUtil() {
}
}
@@ -1,10 +1,10 @@
package com.jetbrains.python.inspections;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.PyCallExpression;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyExpression;
@@ -37,7 +37,7 @@ public class PySuperArgumentsInspection extends PyInspection {
@Override
public void visitPyCallExpression(PyCallExpression node) {
if ("super".equals(node.getCallee().getName())) {
if (PyNames.SUPER.equals(node.getCallee().getName())) {
PyExpression[] arguments = node.getArguments();
if (arguments.length == 2) {
if (arguments[0] instanceof PyReferenceExpression && arguments[1] instanceof PyReferenceExpression) {
@@ -26,6 +26,7 @@ import com.jetbrains.python.psi.impl.PyAugAssignmentStatementNavigator;
import com.jetbrains.python.psi.impl.PyForStatementNavigator;
import com.jetbrains.python.psi.impl.PyImportStatementNavigator;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import com.jetbrains.python.psi.search.PyOverridingMethodsSearch;
import com.jetbrains.python.psi.search.PySuperMethodsSearch;
import org.jetbrains.annotations.NotNull;
@@ -55,36 +56,20 @@ class PyUnusedLocalVariableInspectionVisitor extends PyInspectionVisitor {
processScope(PsiTreeUtil.getParentOfType(node, ScopeOwner.class), node);
}
class DontPerformException extends RuntimeException {}
static class DontPerformException extends RuntimeException {}
private void processScope(final ScopeOwner owner, final PyElement node) {
if (owner.getContainingFile() instanceof PyExpressionCodeFragment || PydevConsoleRunner.isInPydevConsole(owner)){
return;
}
// Check for locals() call
try {
owner.acceptChildren(new PyRecursiveElementVisitor(){
@Override
public void visitPyCallExpression(final PyCallExpression node) {
if ("locals".equals(node.getCallee().getText())){
throw new DontPerformException();
}
}
@Override
public void visitPyFunction(final PyFunction node) {
// stop here
}
});
}
catch (DontPerformException e) {
return;
}
if (callsLocals(owner)) return;
// If method overrides others do not mark parameters as unused if they are
boolean parametersCanBeUnused = false;
// If method overrides others or is overridden, do not mark parameters as unused if they are
boolean parametersCanBeUnused = true;
if (owner instanceof PyFunction) {
parametersCanBeUnused = PySuperMethodsSearch.search(((PyFunction)owner)).findFirst() != null;
parametersCanBeUnused = PySuperMethodsSearch.search(((PyFunction)owner)).findFirst() == null &&
PyOverridingMethodsSearch.search((PyFunction) owner, true).findFirst() == null;
}
final Scope scope = owner.getScope();
@@ -115,7 +100,7 @@ class PyUnusedLocalVariableInspectionVisitor extends PyInspectionVisitor {
}
final ReadWriteInstruction.ACCESS access = ((ReadWriteInstruction)instruction).getAccess();
// WriteAccess
if (access.isWriteAccess() && (parametersCanBeUnused || !(element != null && element.getParent() instanceof PyNamedParameter))) {
if (access.isWriteAccess() && (parametersCanBeUnused || !isParameter(element))) {
if (!myUsedElements.contains(element)){
myUnusedElements.add(element);
}
@@ -185,6 +170,32 @@ class PyUnusedLocalVariableInspectionVisitor extends PyInspectionVisitor {
}
}
private static boolean callsLocals(ScopeOwner owner) {
try {
owner.acceptChildren(new PyRecursiveElementVisitor(){
@Override
public void visitPyCallExpression(final PyCallExpression node) {
if ("locals".equals(node.getCallee().getText())){
throw new DontPerformException();
}
}
@Override
public void visitPyFunction(final PyFunction node) {
// stop here
}
});
}
catch (DontPerformException e) {
return true;
}
return false;
}
private static boolean isParameter(PsiElement element) {
return element != null && (element instanceof PyNamedParameter || element.getParent() instanceof PyNamedParameter);
}
void registerProblems() {
// Register problems
for (PsiElement element : myUnusedElements) {
@@ -2,19 +2,20 @@ package com.jetbrains.python.psi;
import com.intellij.psi.StubBasedPsiElement;
import com.jetbrains.python.psi.stubs.PyDecoratorListStub;
import com.jetbrains.python.psi.PyDecorator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A list of function decorators.
* User: dcheryasov
* Date: Sep 28, 2008
* @author dcheryasov
*/
public interface PyDecoratorList extends PyElement, StubBasedPsiElement<PyDecoratorListStub> {
/**
* @return decorators of function, in order of declaration (outermost first).
*/
@NotNull
PyDecorator[] getDecorators();
@Nullable
PyDecorator findDecorator(String name);
}
@@ -72,7 +72,7 @@ public class PyCallExpressionImpl extends PyElementImpl implements PyCallExpress
PyExpression callee = getCallee();
if (callee instanceof PyReferenceExpression) {
// hardwired special cases
if ("super".equals(callee.getText())) {
if (PyNames.SUPER.equals(callee.getText())) {
final PyType superCallType = getSuperCallType(callee, context);
if (superCallType != null) {
return superCallType;
@@ -135,7 +135,7 @@ public class PyCallExpressionImpl extends PyElementImpl implements PyCallExpress
PsiElement must_be_super_init = ((PyReferenceExpression)callee).getReference().resolve();
if (must_be_super_init instanceof PyFunction) {
PyClass must_be_super = ((PyFunction)must_be_super_init).getContainingClass();
if (must_be_super == PyBuiltinCache.getInstance(this).getClass("super")) {
if (must_be_super == PyBuiltinCache.getInstance(this).getClass(PyNames.SUPER)) {
PyArgumentList arglist = getArgumentList();
if (arglist != null) {
PyExpression[] args = arglist.getArguments();
@@ -708,6 +708,9 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
}
private boolean calculateNewStyleClass() {
if (((PyFile) getContainingFile()).getLanguageLevel().isPy3K()) {
return true;
}
PyClass objclass = PyBuiltinCache.getInstance(this).getClass("object");
if (this == objclass) return true; // a rare but possible case
if (hasNewStyleMetaClass(this)) return true;
@@ -723,7 +726,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
private static boolean hasNewStyleMetaClass(PyClass pyClass) {
final PsiFile containingFile = pyClass.getContainingFile();
if (containingFile instanceof PyFile) {
final PsiElement element = ((PyFile)containingFile).findExportedName("__metaclass__");
final PsiElement element = ((PyFile)containingFile).findExportedName(PyNames.METACLASS);
if (element instanceof PyTargetExpression) {
final PyExpression assignedValue = ((PyTargetExpression)element).findAssignedValue();
if (assignedValue != null && assignedValue.getText().equals("type")) {
@@ -32,4 +32,15 @@ public class PyDecoratorListImpl extends PyBaseElementImpl<PyDecoratorListStub>
return getStubOrPsiChildren(PyElementTypes.DECORATOR_CALL, decoarray);
//return decoarray;
}
@Override
public PyDecorator findDecorator(String name) {
final PyDecorator[] decorators = getDecorators();
for (PyDecorator decorator : decorators) {
if (name.equals(decorator.getCallee().getText())) {
return decorator;
}
}
return null;
}
}
@@ -6,7 +6,6 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.jetbrains.python.PythonFileType;
import com.jetbrains.python.PythonLanguage;
import com.jetbrains.python.psi.PyElementGenerator;
import com.jetbrains.python.psi.PyFunction;
@@ -21,6 +20,7 @@ public class PyFunctionBuilder {
private final String myName;
private final List<String> myParameters = new ArrayList<String>();
private final List<String> myStatements = new ArrayList<String>();
private final List<String> myDecorators = new ArrayList<String>();
public PyFunctionBuilder(String name) {
myName = name;
@@ -46,6 +46,10 @@ public class PyFunctionBuilder {
return (PyFunction) target.add(buildFunction(target.getProject()));
}
public PyFunction addFunctionAfter(PsiElement target, PsiElement anchor) {
return (PyFunction) target.addAfter(buildFunction(target.getProject()), anchor);
}
public PyFunction buildFunction(Project project) {
String text = buildText(project);
PyElementGenerator generator = PyElementGenerator.getInstance(project);
@@ -53,7 +57,11 @@ public class PyFunctionBuilder {
}
private String buildText(Project project) {
StringBuilder builder = new StringBuilder("def ");
StringBuilder builder = new StringBuilder();
for (String decorator : myDecorators) {
builder.append(decorator).append("\n");
}
builder.append("def ");
builder.append(myName).append("(");
builder.append(StringUtil.join(myParameters, ", "));
builder.append("):");
@@ -66,4 +74,8 @@ public class PyFunctionBuilder {
}
return builder.toString();
}
public void decorate(String decoratorName) {
myDecorators.add("@" + decoratorName);
}
}
@@ -13,14 +13,20 @@ public class PySuperMethodsSearch extends ExtensibleQueryFactory<PsiElement, PyS
public static class SearchParameters {
private final PyFunction myDerivedMethod;
private final boolean myDeepSearch;
public SearchParameters(final PyFunction derivedMethod) {
public SearchParameters(final PyFunction derivedMethod, boolean deepSearch) {
myDerivedMethod = derivedMethod;
myDeepSearch = deepSearch;
}
public PyFunction getDerivedMethod() {
return myDerivedMethod;
}
public boolean isDeepSearch() {
return myDeepSearch;
}
}
private PySuperMethodsSearch() {
@@ -28,7 +34,12 @@ public class PySuperMethodsSearch extends ExtensibleQueryFactory<PsiElement, PyS
}
public static Query<PsiElement> search(final PyFunction derivedMethod) {
final SearchParameters parameters = new SearchParameters(derivedMethod);
final SearchParameters parameters = new SearchParameters(derivedMethod, false);
return INSTANCE.createUniqueResultsQuery(parameters);
}
public static Query<PsiElement> search(final PyFunction derivedMethod, boolean deepSearch) {
final SearchParameters parameters = new SearchParameters(derivedMethod, deepSearch);
return INSTANCE.createUniqueResultsQuery(parameters);
}
}
@@ -6,6 +6,9 @@ import com.intellij.util.QueryExecutor;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyFunction;
import java.util.HashSet;
import java.util.Set;
/**
* @author yole
*/
@@ -14,14 +17,24 @@ public class PySuperMethodsSearchExecutor implements QueryExecutor<PsiElement, P
PyFunction func = queryParameters.getDerivedMethod();
String name = func.getName();
PyClass containingClass = func.getContainingClass();
Set<PyClass> foundMethodContainingClasses = new HashSet<PyClass>();
if (name != null && containingClass != null) {
PyClass[] superClasses = containingClass.getSuperClasses();
if (superClasses != null) {
for(PyClass superClass: superClasses) {
PyFunction superMethod = superClass.findMethodByName(name, false);
if (superMethod != null) {
if (!consumer.process(superMethod)) return false;
for (PyClass superClass : containingClass.iterateAncestors()) {
if (!queryParameters.isDeepSearch()) {
boolean isAlreadyFound = false;
for (PyClass alreadyFound : foundMethodContainingClasses) {
if (alreadyFound.isSubclass(superClass)) {
isAlreadyFound = true;
}
}
if (isAlreadyFound) {
continue;
}
}
PyFunction superMethod = superClass.findMethodByName(name, false);
if (superMethod != null) {
foundMethodContainingClasses.add(superClass);
if (!consumer.process(superMethod)) return false;
}
}
}
@@ -1,9 +1,19 @@
package com.jetbrains.python.refactoring.rename;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.ui.Messages;
import com.intellij.psi.PsiElement;
import com.intellij.refactoring.rename.RenamePsiElementProcessor;
import com.intellij.util.Processor;
import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.search.PyOverridingMethodsSearch;
import com.jetbrains.python.psi.search.PySuperMethodsSearch;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author yole
@@ -38,4 +48,41 @@ public class RenamePyFunctionProcessor extends RenamePsiElementProcessor {
public void setToSearchForTextOccurrences(PsiElement element, boolean enabled) {
PyCodeInsightSettings.getInstance().RENAME_SEARCH_NON_CODE_FOR_FUNCTION = enabled;
}
@Override
public PsiElement substituteElementToRename(PsiElement element, Editor editor) {
PyFunction function = (PyFunction) element;
final PyClass containingClass = function.getContainingClass();
if (containingClass == null) {
return function;
}
final List<PsiElement> superMethods = new ArrayList<PsiElement>(PySuperMethodsSearch.search(function, true).findAll());
if (superMethods.size() > 0) {
// TODO this is not exactly right for multiple inheritance
final PyFunction deepestSuperMethod = (PyFunction) superMethods.get(superMethods.size()-1);
String message = "Method " + function.getName() + " of class " + containingClass.getQualifiedName() + "\noverrides method of class "
+ deepestSuperMethod.getContainingClass().getQualifiedName() + ".\nDo you want to rename the base method?";
int rc = Messages.showYesNoCancelDialog(element.getProject(), message, "Rename", Messages.getQuestionIcon());
if (rc == 0) {
return deepestSuperMethod;
}
if (rc == 1) {
return function;
}
return null;
}
return function;
}
@Override
public void prepareRenaming(PsiElement element, final String newName, final Map<PsiElement, String> allRenames) {
PyFunction function = (PyFunction) element;
PyOverridingMethodsSearch.search(function, true).forEach(new Processor<PyFunction>() {
@Override
public boolean process(PyFunction pyFunction) {
allRenames.put(pyFunction, newName);
return true;
}
});
}
}
@@ -5,6 +5,7 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.util.containers.HashSet;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.codeInsight.intentions.*;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyQualifiedName;
@@ -133,7 +134,7 @@ public class UnsupportedFeatures extends PyAnnotator {
final PsiElement firstChild = node.getFirstChild();
if (firstChild != null) {
final String name = firstChild.getText();
if ("super".equals(name)) {
if (PyNames.SUPER.equals(name)) {
final PyArgumentList argumentList = node.getArgumentList();
if (argumentList != null && argumentList.getArguments().length == 0) {
getHolder().createWarningAnnotation(node, "super() should have arguments in Python 2");
@@ -0,0 +1,3 @@
from mypackage import shazam
shazam()
@@ -0,0 +1 @@
shaz<caret>
@@ -0,0 +1,6 @@
def shazam():
pass
class MyClass:
def shazzy(self): # should not be offered as completion variant
pass
@@ -6,4 +6,4 @@ class A:
class B(A):
@classmethod
def foo(cls):
<selection>pass</selection>
<selection>A.foo(cls)</selection>
+6
View File
@@ -0,0 +1,6 @@
class A(object):
def m(self):
pass
class B(A):
<caret>pass
@@ -0,0 +1,8 @@
class A(object):
def m(self):
pass
class B(A):
def m(self):
<selection>super(B, self).m()</selection>
+6
View File
@@ -0,0 +1,6 @@
class A:
def m(self):
pass
class B(A):
<caret>pass
+8
View File
@@ -0,0 +1,8 @@
class A:
def m(self):
pass
class B(A):
def m(self):
<selection>super().m()</selection>
+1 -1
View File
@@ -3,7 +3,7 @@ class A:
class B(A):
def doStuff(self):
<selection>pass</selection>
<selection>A.doStuff(self)</selection>
def otherMethod(self, foo, bar):
print foo, bar
@@ -0,0 +1,5 @@
class A:
def f<caret>oo(self): pass
class B(A):
def foo(self): pass
@@ -0,0 +1,5 @@
class A:
def qu(self): pass
class B(A):
def qu(self): pass
@@ -11,11 +11,6 @@ import com.jetbrains.python.fixtures.PyLightFixtureTestCase;
public class PyClassNameCompletionTest extends PyLightFixtureTestCase {
private boolean myOldAutocompleteValue;
@Override
protected String getTestDataPath() {
return PythonTestUtil.getTestDataPath() + "/completion/className/";
}
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -30,15 +25,15 @@ public class PyClassNameCompletionTest extends PyLightFixtureTestCase {
super.tearDown();
}
public void testSimple() throws Exception {
public void testSimple() {
doTest();
}
public void testReuseExisting() throws Exception {
public void testReuseExisting() {
doTest();
}
public void testQualified() throws Exception {
public void testQualified() {
final PyCodeInsightSettings settings = PyCodeInsightSettings.getInstance();
boolean oldValue = settings.PREFER_FROM_IMPORT;
settings.PREFER_FROM_IMPORT = false;
@@ -50,10 +45,15 @@ public class PyClassNameCompletionTest extends PyLightFixtureTestCase {
}
}
private void doTest() throws Exception {
myFixture.copyDirectoryToProject(getTestName(true), "");
public void testFunction() {
doTest();
}
private void doTest() {
final String path = "/completion/className/" + getTestName(true);
myFixture.copyDirectoryToProject(path, "");
myFixture.configureFromTempProjectFile(getTestName(true) + ".py");
myFixture.complete(CompletionType.CLASS_NAME);
myFixture.checkResultByFile(getTestName(true) + "/" + getTestName(true) + ".after.py");
myFixture.checkResultByFile(path + "/" + getTestName(true) + ".after.py");
}
}
@@ -3,9 +3,11 @@ package com.jetbrains.python;
import com.jetbrains.python.codeInsight.override.PyMethodMember;
import com.jetbrains.python.codeInsight.override.PyOverrideImplementUtil;
import com.jetbrains.python.fixtures.PyLightFixtureTestCase;
import com.jetbrains.python.psi.LanguageLevel;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyFile;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.impl.PythonLanguageLevelPusher;
import java.util.Collections;
import java.util.List;
@@ -14,21 +16,35 @@ import java.util.List;
* @author yole
*/
public class PyOverrideTest extends PyLightFixtureTestCase {
private void doTest() throws Exception {
private void doTest() {
myFixture.configureByFile("override/" + getTestName(true) + ".py");
PyFile file = (PyFile) myFixture.getFile();
List<PyClass> classes = file.getTopLevelClasses();
PyFunction toOverride = classes.get(0).getMethods() [0];
PyOverrideImplementUtil.overrideMethods(myFixture.getEditor(), classes.get(1),
Collections.singletonList(new PyMethodMember(toOverride)));
myFixture.checkResultByFile("override/" + getTestName(true) + "_after.py");
myFixture.checkResultByFile("override/" + getTestName(true) + "_after.py", true);
}
public void testSimple() throws Exception {
public void testSimple() {
doTest();
}
public void testClassmethod() throws Exception {
public void testClassmethod() {
doTest();
}
public void testNewStyle() {
doTest();
}
public void testPy3k() {
PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), LanguageLevel.PYTHON31);
try {
doTest();
}
finally {
PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), null);
}
}
}
@@ -8,13 +8,11 @@ import com.jetbrains.python.fixtures.PyLightFixtureTestCase;
* @author yole
*/
public class PyRenameTest extends PyLightFixtureTestCase {
public void testRenameField() throws Exception { // PY-457
myFixture.configureByFile("refactoring/rename/" + getTestName(true) + ".py");
myFixture.renameElementAtCaret("qu");
myFixture.checkResultByFile("refactoring/rename/" + getTestName(true) + "_after.py");
public void testRenameField() { // PY-457
doTest("qu");
}
public void testSearchInStrings() throws Exception { // PY-670
public void testSearchInStrings() { // PY-670
myFixture.configureByFile("refactoring/rename/" + getTestName(true) + ".py");
final PsiElement element = TargetElementUtilBase.findTargetElement(myFixture.getEditor(), TargetElementUtilBase.REFERENCED_ELEMENT_ACCEPTED |
TargetElementUtilBase.ELEMENT_NAME_ACCEPTED);
@@ -23,15 +21,21 @@ public class PyRenameTest extends PyLightFixtureTestCase {
myFixture.checkResultByFile("refactoring/rename/" + getTestName(true) + "_after.py");
}
public void testRenameParameter() throws Exception { // PY-385
myFixture.configureByFile("refactoring/rename/" + getTestName(true) + ".py");
myFixture.renameElementAtCaret("qu");
myFixture.checkResultByFile("refactoring/rename/" + getTestName(true) + "_after.py");
public void testRenameParameter() { // PY-385
doTest("qu");
}
public void testRenameMultipleDefinitionsLocal() throws Exception { // PY-727
public void testRenameMultipleDefinitionsLocal() { // PY-727
doTest("qu");
}
public void testRenameInheritors() {
doTest("qu");
}
private void doTest(final String newName) {
myFixture.configureByFile("refactoring/rename/" + getTestName(true) + ".py");
myFixture.renameElementAtCaret("qu");
myFixture.renameElementAtCaret(newName);
myFixture.checkResultByFile("refactoring/rename/" + getTestName(true) + "_after.py");
}
}