mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Cleanup VariantsProcessor and CompletionVariantsProcessor
This commit is contained in:
@@ -465,9 +465,9 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
|
||||
@NotNull
|
||||
public Iterable<PyElement> iterateNames() {
|
||||
final List<PyElement> 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);
|
||||
|
||||
@@ -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<String, LookupElement> 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<PsiElement> nodeFilter,
|
||||
@Nullable Condition<String> nameFilter) {
|
||||
super(context, nodeFilter, nameFilter);
|
||||
mySuppressParentheses = false;
|
||||
}
|
||||
|
||||
public void suppressParentheses() {
|
||||
mySuppressParentheses = true;
|
||||
public CompletionVariantsProcessor(PsiElement context,
|
||||
@Nullable Condition<PsiElement> nodeFilter,
|
||||
@Nullable Condition<String> 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<LookupElement> variants = myVariants.values();
|
||||
return variants.toArray(new LookupElement[variants.size()]);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<LookupElement> 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)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<PsiElement> myNodeFilter;
|
||||
protected Condition<String> myNameFilter;
|
||||
|
||||
protected boolean myPlainNamesOnly = false; // if true, add insert handlers to known things like functions
|
||||
private List<String> myAllowedNames;
|
||||
private final List<String> mySeenNames = new ArrayList<>();
|
||||
protected final PsiElement myContext;
|
||||
|
||||
@Nullable
|
||||
protected final Condition<PsiElement> myNodeFilter;
|
||||
|
||||
@Nullable
|
||||
protected final Condition<String> myNameFilter;
|
||||
|
||||
protected final boolean myPlainNamesOnly; // if true, add insert handlers to known things like functions
|
||||
|
||||
@Nullable
|
||||
private Set<String> myAllowedNames;
|
||||
|
||||
@NotNull
|
||||
private final Set<String> mySeenNames = new HashSet<>();
|
||||
|
||||
public VariantsProcessor(PsiElement context) {
|
||||
// empty
|
||||
myContext = context;
|
||||
this(context, null, null, false);
|
||||
}
|
||||
|
||||
public VariantsProcessor(PsiElement context, @Nullable final Condition<PsiElement> nodeFilter, @Nullable final Condition<String> nameFilter) {
|
||||
public VariantsProcessor(PsiElement context, @Nullable Condition<PsiElement> nodeFilter, @Nullable Condition<String> nameFilter) {
|
||||
this(context, nodeFilter, nameFilter, false);
|
||||
}
|
||||
|
||||
public VariantsProcessor(PsiElement context,
|
||||
@Nullable Condition<PsiElement> nodeFilter,
|
||||
@Nullable Condition<String> 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<String> namesFilter) {
|
||||
myAllowedNames = namesFilter;
|
||||
public void setAllowedNames(@Nullable Collection<String> allowedNames) {
|
||||
if (allowedNames == null) {
|
||||
myAllowedNames = null;
|
||||
}
|
||||
else {
|
||||
myAllowedNames = new HashSet<>(allowedNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<PsiElement> 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
|
||||
|
||||
Reference in New Issue
Block a user