mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
resolve foreign imports in QualifiedNameResolver; simplified PyImportResolver API
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+1
-1
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user