resolve foreign imports in QualifiedNameResolver; simplified PyImportResolver API

This commit is contained in:
Dmitry Jemerov
2012-08-13 17:51:57 +02:00
parent 00aacada71
commit 85d753f866
11 changed files with 73 additions and 76 deletions
@@ -1,10 +1,11 @@
package com.jetbrains.python.psi.impl;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.psi.*;
import com.jetbrains.python.psi.PyElement;
import org.jetbrains.annotations.NotNull;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiPackage;
import com.jetbrains.python.psi.resolve.QualifiedNameResolveContext;
import org.jetbrains.annotations.Nullable;
/**
@@ -12,23 +13,18 @@ import org.jetbrains.annotations.Nullable;
*/
public class PyJavaImportResolver implements PyImportResolver {
@Nullable
public PsiElement resolveImportReference(@NotNull final PyElement importElement, @NotNull final PyQualifiedName importText,
@Nullable PyQualifiedName importFrom) {
String fqn = importText.toString();
if (importFrom != null) {
fqn = importFrom.toString() + "." + fqn;
}
final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(importElement.getProject());
public PsiElement resolveImportReference(PyQualifiedName name, QualifiedNameResolveContext context) {
String fqn = name.toString();
final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(context.getProject());
if (psiFacade == null) return null;
final PsiPackage aPackage = psiFacade.findPackage(fqn);
if (aPackage != null) {
return aPackage;
}
Module sourceModule = ModuleUtil.findModuleForPsiElement(importElement);
if (sourceModule != null) {
final PsiClass aClass = psiFacade.findClass(fqn, sourceModule.getModuleWithDependenciesAndLibrariesScope(false));
Module module = context.getModule();
if (module != null) {
final PsiClass aClass = psiFacade.findClass(fqn, module.getModuleWithDependenciesAndLibrariesScope(false));
if (aClass != null) return aClass;
}
return null;
@@ -2,8 +2,7 @@ package com.jetbrains.python.psi.impl;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.psi.PsiElement;
import com.jetbrains.python.psi.PyElement;
import org.jetbrains.annotations.NotNull;
import com.jetbrains.python.psi.resolve.QualifiedNameResolveContext;
import org.jetbrains.annotations.Nullable;
/**
@@ -13,6 +12,5 @@ public interface PyImportResolver {
ExtensionPointName<PyImportResolver> EP_NAME = ExtensionPointName.create("Pythonid.importResolver");
@Nullable
PsiElement resolveImportReference(@NotNull PyElement importElement, @NotNull PyQualifiedName importText,
@Nullable PyQualifiedName importFrom);
PsiElement resolveImportReference(PyQualifiedName name, QualifiedNameResolveContext context);
}
@@ -58,6 +58,13 @@ public class PyQualifiedName {
return result;
}
public PyQualifiedName append(PyQualifiedName qName) {
PyQualifiedName result = new PyQualifiedName(myComponents.size()+qName.getComponentCount());
result.myComponents.addAll(myComponents);
result.myComponents.addAll(result.getComponents());
return result;
}
@NotNull
public PyQualifiedName removeLastComponent() {
return removeTail(1);
@@ -74,6 +81,17 @@ public class PyQualifiedName {
return result;
}
@NotNull
public PyQualifiedName removeHead(int count) {
int size = myComponents.size();
PyQualifiedName result = new PyQualifiedName(size);
result.myComponents.addAll(myComponents);
for (int i = 0; i < count && result.myComponents.size() > 0; i++) {
result.myComponents.remove(0);
}
return result;
}
@NotNull
public List<String> getComponents() {
return myComponents;
@@ -65,6 +65,7 @@ public class QualifiedNameResolveContext {
return true;
}
@Nullable
public PsiFile getFootholdFile() {
return myFootholdFile;
}
@@ -74,6 +75,7 @@ public class QualifiedNameResolveContext {
return myPsiManager;
}
@NotNull
public Project getProject() {
return myPsiManager.getProject();
}
@@ -42,4 +42,6 @@ public interface QualifiedNameResolver {
<T extends PsiElement> T firstResultOfType(Class<T> clazz);
QualifiedNameResolver withContext(QualifiedNameResolveContext context);
QualifiedNameResolver withoutForeign();
}
+1
View File
@@ -15,6 +15,7 @@
<orderEntry type="module" module-name="jam-support-impl" scope="RUNTIME" />
<orderEntry type="module" module-name="python-plugin" scope="RUNTIME" />
<orderEntry type="module" module-name="yaml-plugin" scope="RUNTIME" />
<orderEntry type="module" module-name="coverage" scope="RUNTIME" />
</component>
</module>
@@ -136,7 +136,7 @@ public class PyFromImportStatementImpl extends PyBaseElementImpl<PyFromImportSta
return true;
}
if (isStarImport()) {
final List<PsiElement> targets = ResolveImportUtil.resolveFromOrForeignImport(this, getImportSourceQName());
final List<PsiElement> targets = ResolveImportUtil.resolveFromImportStatementSource(this, getImportSourceQName());
for (PsiElement target : targets) {
final PsiElement importedFile = PyUtil.turnDirIntoInit(target);
if (importedFile != null) {
@@ -205,6 +205,6 @@ public class PyFromImportStatementImpl extends PyBaseElementImpl<PyFromImportSta
return upper == null ? Collections.<PsiElement>emptyList() : Collections.<PsiElement>singletonList(upper);
}
}
return ResolveImportUtil.resolveFromOrForeignImportStatementSource(this, qName);
return ResolveImportUtil.resolveFromImportStatementSource(this, qName);
}
}
@@ -29,8 +29,8 @@ public class PyStarImportElementImpl extends PyElementImpl implements PyStarImpo
public Iterable<PyElement> iterateNames() {
if (getParent() instanceof PyFromImportStatement) {
PyFromImportStatement fromImportStatement = (PyFromImportStatement)getParent();
final List<PsiElement> importedFiles = ResolveImportUtil.resolveFromOrForeignImport(fromImportStatement,
fromImportStatement.getImportSourceQName());
final List<PsiElement> importedFiles =
ResolveImportUtil.resolveFromImportStatementSource(fromImportStatement, fromImportStatement.getImportSourceQName());
ChainIterable<PyElement> chain = new ChainIterable<PyElement>();
for (PsiElement importedFile : new HashSet<PsiElement>(importedFiles)) { // resolver gives lots of duplicates
final PsiElement source = PyUtil.turnDirIntoInit(importedFile);
@@ -50,8 +50,8 @@ public class PyStarImportElementImpl extends PyElementImpl implements PyStarImpo
}
if (getParent() instanceof PyFromImportStatement) {
PyFromImportStatement fromImportStatement = (PyFromImportStatement)getParent();
final List<PsiElement> importedFiles = ResolveImportUtil.resolveFromOrForeignImport(fromImportStatement,
fromImportStatement.getImportSourceQName());
final List<PsiElement> importedFiles =
ResolveImportUtil.resolveFromImportStatementSource(fromImportStatement, fromImportStatement.getImportSourceQName());
for (PsiElement importedFile : new HashSet<PsiElement>(importedFiles)) { // resolver gives lots of duplicates
final PsiElement source = PyUtil.turnDirIntoInit(importedFile);
if (source instanceof PyFile) {
@@ -29,7 +29,7 @@ public class PyFromImportSourceReference extends PyImportReference {
@NotNull
@Override
protected List<RatedResolveResult> resolveInner() {
List<PsiElement> targets = ResolveImportUtil.resolveFromOrForeignImport(myStatement, myElement.asQualifiedName());
List<PsiElement> targets = ResolveImportUtil.resolveFromImportStatementSource(myStatement, myElement.asQualifiedName());
return ResolveImportUtil.rateResults(targets);
}
}
@@ -3,6 +3,7 @@ package com.jetbrains.python.psi.resolve;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.intellij.facet.FacetManager;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
@@ -15,6 +16,7 @@ import com.intellij.psi.PsiFileSystemItem;
import com.jetbrains.django.facet.DjangoFacetType;
import com.jetbrains.python.console.PydevConsoleRunner;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
import com.jetbrains.python.psi.impl.PyImportResolver;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
@@ -38,6 +40,7 @@ public class QualifiedNameResolverImpl implements RootVisitor, QualifiedNameReso
private boolean myVisitAllModules = false;
private int myRelativeLevel = -1;
private boolean myWithoutRoots;
private boolean myWithoutForeign;
public QualifiedNameResolverImpl(@NotNull String qNameString) {
myQualifiedName = PyQualifiedName.fromDottedString(qNameString);
@@ -120,6 +123,12 @@ public class QualifiedNameResolverImpl implements RootVisitor, QualifiedNameReso
return this;
}
@Override
public QualifiedNameResolver withoutForeign() {
myWithoutForeign = true;
return this;
}
/**
* Specifies that we're looking for a file in a directory hierarchy, not a module in the Python package hierarchy
* (so we don't need to check for existence of __init__.py)
@@ -176,6 +185,15 @@ public class QualifiedNameResolverImpl implements RootVisitor, QualifiedNameReso
results.addAll(resolveInRoots());
}
if (!myWithoutForeign) {
for (PyImportResolver resolver : Extensions.getExtensions(PyImportResolver.EP_NAME)) {
PsiElement foreign = resolver.resolveImportReference(myQualifiedName, myContext);
if (foreign != null) {
results.add(foreign);
}
}
}
return Lists.newArrayList(results);
}
@@ -1,6 +1,5 @@
package com.jetbrains.python.psi.resolve;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.projectRoots.Sdk;
@@ -13,6 +12,8 @@ import com.intellij.util.containers.HashSet;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.*;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.TypeEvalContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -111,10 +112,7 @@ public class ResolveImportUtil {
if (modules.size() > 0) {
return rateResults(modules);
}
// in-python resolution failed
final PsiElement result = resolveForeignImport(importElement, qName, null);
return ResolveResultList.to(result);
return Collections.emptyList();
}
public static List<RatedResolveResult> resolveNameInFromImport(PyImportElement importElement, PyQualifiedName qName,
@@ -144,22 +142,8 @@ public class ResolveImportUtil {
}
if (!resultList.isEmpty()) {
return rateResults(resultList);
}
final PsiElement result = resolveForeignImport(importElement, qName, importStatement.getImportSourceQName());
return ResolveResultList.to(result);
}
@NotNull
public static List<PsiElement> resolveFromOrForeignImport(PyFromImportStatement fromImportStatement, PyQualifiedName qname) {
final List<PsiElement> results = resolveFromImportStatementSource(fromImportStatement, qname);
if (results.isEmpty() && qname != null && qname.getComponentCount() > 0) {
final PyQualifiedName importedQName = PyQualifiedName.fromComponents(qname.getLastComponent());
final PyQualifiedName containingQName = qname.removeLastComponent();
final PsiElement result = resolveForeignImport(fromImportStatement, importedQName, containingQName);
return result != null ? Collections.singletonList(result) : Collections.<PsiElement>emptyList();
}
return new ArrayList<PsiElement>(results);
return Collections.emptyList();
}
@NotNull
@@ -169,17 +153,6 @@ public class ResolveImportUtil {
return resolveModule(qName, file, absoluteImportEnabled, from_import_statement.getRelativeLevel());
}
@NotNull
public static List<PsiElement> resolveFromOrForeignImportStatementSource(@NotNull PyFromImportStatement fromImportStatement,
@Nullable PyQualifiedName qName) {
final List<PsiElement> results = resolveFromImportStatementSource(fromImportStatement, qName);
if (!results.isEmpty()) {
return results;
}
final PsiElement result = qName != null ? resolveForeignImport(fromImportStatement, qName, null) : null;
return result != null ? Collections.singletonList(result) : Collections.<PsiElement>emptyList();
}
/**
* Resolves a module reference in a general case.
*
@@ -272,19 +245,6 @@ public class ResolveImportUtil {
return cache;
}
@Nullable
private static PsiElement resolveForeignImport(@NotNull final PyElement importElement,
@NotNull final PyQualifiedName importText,
@Nullable final PyQualifiedName importFrom) {
for (PyImportResolver resolver : Extensions.getExtensions(PyImportResolver.EP_NAME)) {
PsiElement result = resolver.resolveImportReference(importElement, importText, importFrom);
if (result != null) {
return result;
}
}
return null;
}
/**
* Tries to find referencedName under the parent element. Used to resolve any names that look imported.
* Parent might happen to be a PyFile(__init__.py), then it is treated <i>both</i> as a file and as ist base dir.
@@ -328,12 +288,14 @@ public class ResolveImportUtil {
else if (parent instanceof PsiDirectory) {
dir = (PsiDirectory)parent;
}
else if (parent instanceof PsiDirectoryContainer) {
final PsiDirectoryContainer container = (PsiDirectoryContainer)parent;
for (PsiDirectory childDir : container.getDirectories()) {
final PsiElement result = resolveInDirectory(referencedName, containingFile, childDir, fileOnly, checkForPackage);
//if (fileOnly && ! (result instanceof PsiFile) && ! (result instanceof PsiDirectory)) return null;
if (result != null) return result;
else if (parent != null) {
PyType refType = PyReferenceExpressionImpl.getReferenceTypeFromProviders(parent, TypeEvalContext.fast(), null);
if (refType != null) {
final List<? extends RatedResolveResult> result = refType.resolveMember(referencedName, null, AccessDirection.READ,
PyResolveContext.defaultContext());
if (result != null && !result.isEmpty()) {
return result.get(0).getElement();
}
}
}
if (dir != null) {