diff --git a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java index 7748cf5f18fa..0e15bb657dde 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java @@ -465,9 +465,9 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression { @NotNull public Iterable iterateNames() { final List result = new ArrayList<>(); - VariantsProcessor processor = new VariantsProcessor(this) { + final VariantsProcessor processor = new VariantsProcessor(this) { @Override - protected void addElement(String name, PsiElement element) { + protected void addElement(@NotNull String name, @NotNull PsiElement element) { element = PyUtil.turnDirIntoInit(element); if (element instanceof PyElement) { result.add((PyElement)element); diff --git a/python/src/com/jetbrains/python/psi/resolve/CompletionVariantsProcessor.java b/python/src/com/jetbrains/python/psi/resolve/CompletionVariantsProcessor.java index 6b3b87f1c865..4be22038fff4 100644 --- a/python/src/com/jetbrains/python/psi/resolve/CompletionVariantsProcessor.java +++ b/python/src/com/jetbrains/python/psi/resolve/CompletionVariantsProcessor.java @@ -35,6 +35,7 @@ import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.impl.PyPsiUtils; import com.jetbrains.python.psi.types.PyCallableParameter; import com.jetbrains.python.psi.types.TypeEvalContext; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -44,24 +45,35 @@ import java.util.*; * @author yole */ public class CompletionVariantsProcessor extends VariantsProcessor { + + @NotNull private final Map myVariants = new HashMap<>(); - private boolean mySuppressParentheses = false; + + private final boolean mySuppressParentheses; public CompletionVariantsProcessor(PsiElement context) { super(context); + mySuppressParentheses = false; } public CompletionVariantsProcessor(PsiElement context, @Nullable Condition nodeFilter, @Nullable Condition nameFilter) { super(context, nodeFilter, nameFilter); + mySuppressParentheses = false; } - public void suppressParentheses() { - mySuppressParentheses = true; + public CompletionVariantsProcessor(PsiElement context, + @Nullable Condition nodeFilter, + @Nullable Condition nameFilter, + boolean plainNamesOnly, + boolean suppressParentheses) { + super(context, nodeFilter, nameFilter, plainNamesOnly); + mySuppressParentheses = suppressParentheses; } - private LookupElementBuilder setupItem(LookupElementBuilder item) { + @NotNull + private LookupElementBuilder setupItem(@NotNull LookupElementBuilder item) { final PsiElement element = item.getPsiElement(); if (!myPlainNamesOnly) { if (!mySuppressParentheses && @@ -115,9 +127,9 @@ public class CompletionVariantsProcessor extends VariantsProcessor { return item; } - private static boolean isSingleArgDecoratorCall(PsiElement elementInCall, PyFunction callee) { + private static boolean isSingleArgDecoratorCall(@Nullable PsiElement elementInCall, @NotNull PyFunction callee) { // special case hack to avoid the need of patching generator3.py - PyClass containingClass = callee.getContainingClass(); + final PyClass containingClass = callee.getContainingClass(); if (containingClass != null && PyNames.PROPERTY.equals(containingClass.getName()) && PyBuiltinCache.getInstance(elementInCall).isBuiltin(containingClass)) { return true; @@ -126,24 +138,26 @@ public class CompletionVariantsProcessor extends VariantsProcessor { if (callee.getParameterList().getParameters().length > 1) { return false; } - PyDecorator decorator = PsiTreeUtil.getParentOfType(elementInCall, PyDecorator.class); + final PyDecorator decorator = PsiTreeUtil.getParentOfType(elementInCall, PyDecorator.class); if (decorator == null) { return false; } return PsiTreeUtil.isAncestor(decorator.getCallee(), elementInCall, false); } + @NotNull public LookupElement[] getResult() { final Collection variants = myVariants.values(); return variants.toArray(new LookupElement[variants.size()]); } + @NotNull public List getResultList() { return new ArrayList<>(myVariants.values()); } @Override - protected void addElement(String name, PsiElement element) { + protected void addElement(@NotNull String name, @NotNull PsiElement element) { if (PyUtil.isClassPrivateName(name) && !PyUtil.inSameFile(element, myContext)) { return; } @@ -151,11 +165,10 @@ public class CompletionVariantsProcessor extends VariantsProcessor { } @Override - protected void addImportedElement(String referencedName, PyElement expr) { - Icon icon = expr.getIcon(0); + protected void addImportedElement(@NotNull String name, @NotNull PyElement element) { + Icon icon = element.getIcon(0); // things like PyTargetExpression cannot have a general icon, but here we only have variables if (icon == null) icon = PlatformIcons.VARIABLE_ICON; - LookupElementBuilder lookupItem = setupItem(LookupElementBuilder.createWithSmartPointer(referencedName, expr).withIcon(icon)); - myVariants.put(referencedName, lookupItem); + myVariants.put(name, setupItem(LookupElementBuilder.createWithSmartPointer(name, element).withIcon(icon))); } } diff --git a/python/src/com/jetbrains/python/psi/resolve/VariantsProcessor.java b/python/src/com/jetbrains/python/psi/resolve/VariantsProcessor.java index 38818f0f8182..9b04f118fbdb 100644 --- a/python/src/com/jetbrains/python/psi/resolve/VariantsProcessor.java +++ b/python/src/com/jetbrains/python/psi/resolve/VariantsProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,77 +23,80 @@ import com.intellij.psi.PsiInvalidElementAccessException; import com.intellij.psi.PsiNamedElement; import com.intellij.psi.ResolveState; import com.intellij.psi.scope.PsiScopeProcessor; -import com.jetbrains.python.psi.*; import com.intellij.psi.util.QualifiedName; +import com.jetbrains.python.psi.*; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; public abstract class VariantsProcessor implements PsiScopeProcessor { - protected final PsiElement myContext; - protected Condition myNodeFilter; - protected Condition myNameFilter; - protected boolean myPlainNamesOnly = false; // if true, add insert handlers to known things like functions - private List myAllowedNames; - private final List mySeenNames = new ArrayList<>(); + protected final PsiElement myContext; + + @Nullable + protected final Condition myNodeFilter; + + @Nullable + protected final Condition myNameFilter; + + protected final boolean myPlainNamesOnly; // if true, add insert handlers to known things like functions + + @Nullable + private Set myAllowedNames; + + @NotNull + private final Set mySeenNames = new HashSet<>(); public VariantsProcessor(PsiElement context) { - // empty - myContext = context; + this(context, null, null, false); } - public VariantsProcessor(PsiElement context, @Nullable final Condition nodeFilter, @Nullable final Condition nameFilter) { + public VariantsProcessor(PsiElement context, @Nullable Condition nodeFilter, @Nullable Condition nameFilter) { + this(context, nodeFilter, nameFilter, false); + } + + public VariantsProcessor(PsiElement context, + @Nullable Condition nodeFilter, + @Nullable Condition nameFilter, + boolean plainNamesOnly) { myContext = context; myNodeFilter = nodeFilter; myNameFilter = nameFilter; - } - - public boolean isPlainNamesOnly() { - return myPlainNamesOnly; - } - - public void setPlainNamesOnly(boolean plainNamesOnly) { myPlainNamesOnly = plainNamesOnly; } - @Override public boolean execute(@NotNull PsiElement element, @NotNull ResolveState substitutor) { if (myNodeFilter != null && !myNodeFilter.value(element)) return true; // skip whatever the filter rejects - // TODO: refactor to look saner; much code duplication if (element instanceof PsiNamedElement) { - final PsiNamedElement psiNamedElement = (PsiNamedElement)element; - final String name = PyUtil.getElementNameWithoutExtension(psiNamedElement); - if (name != null && nameIsAcceptable(name)) { - addElement(name, psiNamedElement); + final PsiNamedElement namedElement = (PsiNamedElement)element; + final String name = PyUtil.getElementNameWithoutExtension(namedElement); + if (nameIsAcceptable(name)) { + addElement(name, namedElement); } } else if (element instanceof PyReferenceExpression) { - PyReferenceExpression expr = (PyReferenceExpression)element; - String referencedName = expr.getReferencedName(); - if (nameIsAcceptable(referencedName)) { - addElement(referencedName, expr); + final PyReferenceExpression referenceExpression = (PyReferenceExpression)element; + final String name = referenceExpression.getReferencedName(); + if (nameIsAcceptable(name)) { + addElement(name, referenceExpression); } } else if (element instanceof PyImportedNameDefiner) { - boolean handledAsImported = false; - if (element instanceof PyImportElement) { - final PyImportElement importElement = (PyImportElement)element; - handledAsImported = handleImportElement(importElement); - } - if (! handledAsImported) { + if (!(element instanceof PyImportElement) || !handleImportElement((PyImportElement)element)) { final PyImportedNameDefiner definer = (PyImportedNameDefiner)element; for (PyElement expr : definer.iterateNames()) { if (expr != null && expr != myContext) { // NOTE: maybe rather have SingleIterables skip nulls outright? if (!expr.isValid()) { throw new PsiInvalidElementAccessException(expr, "Definer: " + definer); } - String referencedName = expr instanceof PyFile ? FileUtil.getNameWithoutExtension(((PyFile)expr).getName()) : expr.getName(); - if (referencedName != null && nameIsAcceptable(referencedName)) { - addImportedElement(referencedName, expr); + final String name = expr instanceof PyFile ? FileUtil.getNameWithoutExtension(((PyFile)expr).getName()) : expr.getName(); + if (nameIsAcceptable(name)) { + addImportedElement(name, expr); } } } @@ -103,11 +106,11 @@ public abstract class VariantsProcessor implements PsiScopeProcessor { return true; } - protected boolean handleImportElement(PyImportElement importElement) { + private boolean handleImportElement(@NotNull PyImportElement importElement) { final QualifiedName qName = importElement.getImportedQName(); if (qName != null && qName.getComponentCount() == 1) { - String name = importElement.getAsName() != null ? importElement.getAsName() : qName.getLastComponent(); - if (name != null && nameIsAcceptable(name)) { + final String name = importElement.getAsName() != null ? importElement.getAsName() : qName.getLastComponent(); + if (nameIsAcceptable(name)) { final PsiElement resolved = importElement.resolve(); if (resolved instanceof PsiNamedElement) { addElement(name, resolved); @@ -118,15 +121,16 @@ public abstract class VariantsProcessor implements PsiScopeProcessor { return false; } - protected void addElement(String name, PsiElement psiNamedElement) { + protected void addElement(@NotNull String name, @NotNull PsiElement element) { mySeenNames.add(name); } - protected void addImportedElement(String referencedName, PyElement expr) { - addElement(referencedName, expr); + protected void addImportedElement(@NotNull String name, @NotNull PyElement element) { + addElement(name, element); } - private boolean nameIsAcceptable(String name) { + @Contract("null -> false") + private boolean nameIsAcceptable(@Nullable String name) { if (name == null) { return false; } @@ -152,7 +156,12 @@ public abstract class VariantsProcessor implements PsiScopeProcessor { public void handleEvent(@NotNull Event event, Object associated) { } - public void setAllowedNames(List namesFilter) { - myAllowedNames = namesFilter; + public void setAllowedNames(@Nullable Collection allowedNames) { + if (allowedNames == null) { + myAllowedNames = null; + } + else { + myAllowedNames = new HashSet<>(allowedNames); + } } } diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index 995c1540703f..cafa57d1800a 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -720,11 +720,8 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { boolean withinOurClass = containingClass == getPyClass() || isInSuperCall(expressionHook); final CompletionVariantsProcessor processor = new CompletionVariantsProcessor( - expressionHook, new FilterNotInstance(myClass), null + expressionHook, new FilterNotInstance(myClass), null, false, suppressParentheses ); - if (suppressParentheses) { - processor.suppressParentheses(); - } myClass.processClassLevelDeclarations(processor); // We are here because of completion (see call stack), so we use code complete here diff --git a/python/src/com/jetbrains/python/psi/types/PyModuleType.java b/python/src/com/jetbrains/python/psi/types/PyModuleType.java index 249b89f271f9..4d77955a5fe2 100644 --- a/python/src/com/jetbrains/python/psi/types/PyModuleType.java +++ b/python/src/com/jetbrains/python/psi/types/PyModuleType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.roots.FileIndexFacade; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; @@ -367,14 +368,15 @@ public class PyModuleType implements PyType { // Modules don't descend from obje private static CompletionVariantsProcessor createCompletionVariantsProcessor(PsiElement location, boolean suppressParentheses, PointInImport point) { - final CompletionVariantsProcessor processor = new CompletionVariantsProcessor(location, - psiElement -> !(psiElement instanceof PyImportElement) || - PsiTreeUtil.getParentOfType(psiElement, PyImportStatementBase.class) instanceof PyFromImportStatement, null); - if (suppressParentheses) { - processor.suppressParentheses(); - } - processor.setPlainNamesOnly(point == PointInImport.AS_NAME); // no parens after imported function names - return processor; + final Condition nodeFilter = + psiElement -> !(psiElement instanceof PyImportElement) || + PsiTreeUtil.getParentOfType(psiElement, PyImportStatementBase.class) instanceof PyFromImportStatement; + + return new CompletionVariantsProcessor(location, + nodeFilter, + null, + point == PointInImport.AS_NAME, // no parens after imported function names + suppressParentheses); } @NotNull