pull out qualified name resolve logic from ResolveImportUtil into a separate QualifiedNameFinder class

This commit is contained in:
Dmitry Jemerov
2012-08-13 13:06:11 +02:00
parent 2afc743052
commit 0dd8ae909a
19 changed files with 204 additions and 182 deletions
@@ -12,6 +12,7 @@ import com.jetbrains.python.PythonDocStringFinder;
import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
@@ -214,7 +215,7 @@ public class AddImportHelper {
final boolean useQualified = !PyCodeInsightSettings.getInstance().PREFER_FROM_IMPORT;
final PsiFileSystemItem toImport = target instanceof PsiFileSystemItem ? ((PsiFileSystemItem)target).getParent() : target.getContainingFile();
final ImportPriority priority = getImportPriority(file, toImport);
final PyQualifiedName qName = ResolveImportUtil.findCanonicalImportPath(target, element);
final PyQualifiedName qName = QualifiedNameFinder.findCanonicalImportPath(target, element);
if (qName == null) return;
String path = qName.toString();
if (target instanceof PsiFileSystemItem) {
@@ -29,7 +29,7 @@ import com.jetbrains.python.psi.impl.PyFileImpl;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import com.jetbrains.python.psi.resolve.CollectProcessor;
import com.jetbrains.python.psi.resolve.PyResolveUtil;
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
import com.jetbrains.python.psi.search.PyProjectScopeBuilder;
import com.jetbrains.python.psi.stubs.PyClassNameIndex;
import com.jetbrains.python.psi.stubs.PyFunctionNameIndex;
@@ -158,7 +158,7 @@ public class PythonReferenceImporter implements ReferenceImporter {
if (srcfile != null && srcfile != existing_import_file && srcfile != node.getContainingFile() &&
(ImportFromExistingAction.isRoot(srcfile) || PyNames.isIdentifier(FileUtil.getNameWithoutExtension(srcfile.getName()))) &&
!isShadowedModule(srcfile)) {
PyQualifiedName import_path = ResolveImportUtil.findCanonicalImportPath(srcfile, node);
PyQualifiedName import_path = QualifiedNameFinder.findCanonicalImportPath(srcfile, node);
if (import_path != null && !seen_file_names.contains(import_path.toString())) {
// a new, valid hit
fix.addImport(symbol, srcfile, import_path, proposeAsName(node.getContainingFile(), refText, import_path));
@@ -15,7 +15,7 @@ import com.jetbrains.python.psi.PyFile;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
import com.jetbrains.python.sdk.PythonSdkType;
import java.util.List;
@@ -537,7 +537,7 @@ public class PyStdlibDocumentationLinkProvider implements PythonDocumentationLin
Sdk sdk = PyBuiltinCache.findSdkForFile(file);
VirtualFile vFile = file.getVirtualFile();
if (vFile != null && sdk != null && PythonSdkType.isStdLib(vFile, sdk)) {
PyQualifiedName qName = ResolveImportUtil.findCanonicalImportPath(element, originalElement);
PyQualifiedName qName = QualifiedNameFinder.findCanonicalImportPath(element, originalElement);
return getStdlibUrlFor(element, qName, sdk);
}
return null;
@@ -10,7 +10,7 @@ import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import com.jetbrains.python.psi.impl.PyTypeProvider;
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
import com.jetbrains.python.psi.types.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -216,7 +216,7 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase {
final PyClass c = f.getContainingClass();
final VirtualFile vfile = f.getContainingFile().getVirtualFile();
if (vfile != null) {
String module = ResolveImportUtil.findShortestImportableName(callSite != null ? callSite : f, vfile);
String module = QualifiedNameFinder.findShortestImportableName(callSite != null ? callSite : f, vfile);
if ("builtins".equals(module)) {
module = "__builtin__";
}
@@ -8,7 +8,7 @@ import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyImportedModule;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
import com.jetbrains.python.psi.types.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -37,7 +37,7 @@ public class DocStringTypeReference extends PsiReferenceBase<PsiElement> {
if (myElement instanceof PyStringLiteralExpression && element instanceof PyClass) {
final PyStringLiteralExpression e = (PyStringLiteralExpression)myElement;
final PyClass cls = (PyClass)element;
PyQualifiedName qname = ResolveImportUtil.findCanonicalImportPath(cls, element);
PyQualifiedName qname = QualifiedNameFinder.findCanonicalImportPath(cls, element);
if (qname != null) {
qname = qname.append(cls.getName());
ElementManipulator<PyStringLiteralExpression> manipulator = ElementManipulators.getManipulator(e);
@@ -25,7 +25,7 @@ import com.jetbrains.python.console.PydevDocumentationProvider;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
import com.jetbrains.python.psi.types.*;
import com.jetbrains.python.toolbox.ChainIterable;
import com.jetbrains.python.toolbox.FP;
@@ -348,7 +348,7 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i
if (sdk == null) {
return null;
}
PyQualifiedName qName = ResolveImportUtil.findCanonicalImportPath(element, originalElement);
PyQualifiedName qName = QualifiedNameFinder.findCanonicalImportPath(element, originalElement);
if (qName == null) {
return null;
}
@@ -432,7 +432,7 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i
final Project project = element.getProject();
final VirtualFile vFile = containingFile.getVirtualFile();
if (vFile != null && ProjectRootManager.getInstance(project).getFileIndex().isInLibraryClasses(vFile)) {
final PyQualifiedName qName = ResolveImportUtil.findCanonicalImportPath(element, element);
final PyQualifiedName qName = QualifiedNameFinder.findCanonicalImportPath(element, element);
if (qName != null && qName.getComponentCount() > 0) {
return true;
}
@@ -444,7 +444,7 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i
@Override
public void promptToConfigureDocumentation(PsiElement element) {
final Project project = element.getProject();
final PyQualifiedName qName = ResolveImportUtil.findCanonicalImportPath(element, element);
final PyQualifiedName qName = QualifiedNameFinder.findCanonicalImportPath(element, element);
if (qName != null && qName.getComponentCount() > 0) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
@@ -49,10 +49,7 @@ import com.jetbrains.python.psi.impl.PyImportedModule;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import com.jetbrains.python.psi.impl.references.PyImportReference;
import com.jetbrains.python.psi.impl.references.PyOperatorReference;
import com.jetbrains.python.psi.resolve.ImportedResolveResult;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.RatedResolveResult;
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
import com.jetbrains.python.psi.resolve.*;
import com.jetbrains.python.psi.types.*;
import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.Nls;
@@ -765,7 +762,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
dunderAll = ((PyFile)file).getDunderAll();
}
if (file != null && PyUtil.isPackage(file)) {
packageQName = ResolveImportUtil.findShortestImportableQName(file);
packageQName = QualifiedNameFinder.findShortestImportableQName(file);
}
}
PyImportStatementBase importStatement = PsiTreeUtil.getParentOfType(unusedImport, PyImportStatementBase.class);
@@ -809,7 +806,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
}
}
if (packageQName != null && importedElement instanceof PsiFileSystemItem) {
final PyQualifiedName importedQName = ResolveImportUtil.findShortestImportableQName(importedElement);
final PyQualifiedName importedQName = QualifiedNameFinder.findShortestImportableQName(importedElement);
if (importedQName != null && importedQName.matchesPrefix(packageQName)) {
continue;
}
@@ -20,6 +20,7 @@ import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.PyResolveUtil;
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
import com.jetbrains.python.psi.stubs.PropertyStubStorage;
import com.jetbrains.python.psi.stubs.PyClassStub;
@@ -213,7 +214,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
if (!psiFile.equals(builtins)) {
VirtualFile vFile = psiFile.getVirtualFile();
if (vFile != null) {
final String packageName = ResolveImportUtil.findShortestImportableName(this, vFile);
final String packageName = QualifiedNameFinder.findShortestImportableName(this, vFile);
return packageName + "." + name;
}
}
@@ -7,7 +7,7 @@ import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.stubs.IStubElementType;
import com.intellij.psi.stubs.StubElement;
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
import javax.swing.*;
@@ -48,7 +48,7 @@ public abstract class PyPresentableElementImpl<T extends StubElement> extends Py
final VirtualFile vFile = containingFile.getVirtualFile();
if (vFile != null) {
final String importableName = ResolveImportUtil.findShortestImportableName(containingFile, vFile);
final String importableName = QualifiedNameFinder.findShortestImportableName(containingFile, vFile);
if (importableName != null) {
return importableName;
}
@@ -5,9 +5,9 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyPsiFacade;
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
import com.jetbrains.python.psi.resolve.QualifiedNameResolver;
import com.jetbrains.python.psi.resolve.QualifiedNameResolverImpl;
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
import com.jetbrains.python.psi.stubs.PyClassNameIndex;
import com.jetbrains.python.psi.types.PyClassType;
import com.jetbrains.python.psi.types.PyClassTypeImpl;
@@ -47,6 +47,6 @@ public class PyPsiFacadeImpl extends PyPsiFacade {
@Nullable
@Override
public String findShortestImportableName(PsiElement importer, VirtualFile targetFile) {
return ResolveImportUtil.findShortestImportableName(importer, targetFile);
return QualifiedNameFinder.findShortestImportableName(importer, targetFile);
}
}
@@ -227,7 +227,7 @@ public class PyImportReference extends PyReferenceImpl {
if (myCurrentFile != null && (relativeLevel >= 0 || !ResolveImportUtil.isAbsoluteImportEnabledFor(myCurrentFile))) {
final PsiDirectory containingDirectory = myCurrentFile.getContainingDirectory();
if (containingDirectory != null) {
PyQualifiedName thisQName = ResolveImportUtil.findShortestImportableQName(containingDirectory);
PyQualifiedName thisQName = QualifiedNameFinder.findShortestImportableQName(containingDirectory);
if (thisQName == null || thisQName.getComponentCount() == relativeLevel) {
fillFromDir(ResolveImportUtil.stepBackFrom(myCurrentFile, relativeLevel), insertHandler);
}
@@ -174,7 +174,7 @@ public class PyQualifiedReference extends PyReferenceImpl {
if (ProjectScope.getProjectScope(myElement.getProject()).contains(vFile)) {
rate += 80;
}
final PyQualifiedName qName = ResolveImportUtil.findShortestImportableQName(myElement, vFile);
final PyQualifiedName qName = QualifiedNameFinder.findShortestImportableQName(myElement, vFile);
if (qName != null && imports.contains(qName)) {
rate += 70;
}
@@ -87,7 +87,7 @@ public class CompletionVariantsProcessor extends VariantsProcessor {
source = cls.getName();
}
else if (myContext == null || !PyUtil.inSameFile(myContext, element)) {
PyQualifiedName path = ResolveImportUtil.findCanonicalImportPath(element, null);
PyQualifiedName path = QualifiedNameFinder.findCanonicalImportPath(element, null);
if (path != null) {
if (element instanceof PyFile) {
path = path.removeLastComponent();
@@ -0,0 +1,167 @@
package com.jetbrains.python.psi.resolve;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyFile;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author yole
*/
public class QualifiedNameFinder {
/**
* Looks for a way to import given file.
*
* @param foothold an element in the file to import to (maybe the file itself); used to determine module, roots, etc.
* @param vfile file which importable name we want to find.
* @return a possibly qualified name under which the file may be imported, or null. If there's more than one way (overlapping roots),
* the name with fewest qualifiers is selected.
*/
@Nullable
public static String findShortestImportableName(PsiElement foothold, @NotNull VirtualFile vfile) {
final PyQualifiedName qName = findShortestImportableQName(foothold, vfile);
return qName == null ? null : qName.toString();
}
@Nullable
public static PyQualifiedName findShortestImportableQName(@Nullable PsiFileSystemItem fsItem) {
VirtualFile vFile = fsItem != null ? fsItem.getVirtualFile() : null;
return vFile != null ? findShortestImportableQName(fsItem, vFile) : null;
}
@Nullable
public static PyQualifiedName findShortestImportableQName(@NotNull PsiElement foothold, @NotNull VirtualFile vfile) {
final PythonPathCache cache = ResolveImportUtil.getPathCache(foothold);
final PyQualifiedName name = cache != null ? cache.getName(vfile) : null;
if (name != null) {
return name;
}
PathChoosingVisitor visitor = new PathChoosingVisitor(vfile);
RootVisitorHost.visitRoots(foothold, visitor);
final PyQualifiedName result = visitor.getResult();
if (cache != null) {
cache.putName(vfile, result);
}
return result;
}
@Nullable
public static String findShortestImportableName(Module module, @NotNull VirtualFile vfile) {
final PythonPathCache cache = PythonModulePathCache.getInstance(module);
final PyQualifiedName name = cache.getName(vfile);
if (name != null) {
return name.toString();
}
PathChoosingVisitor visitor = new PathChoosingVisitor(vfile);
RootVisitorHost.visitRoots(module, false, visitor);
final PyQualifiedName result = visitor.getResult();
cache.putName(vfile, result);
return result == null ? null : result.toString();
}
/**
* Returns the name through which the specified symbol should be imported. This can be different from the qualified name of the
* symbol (the place where a symbol is defined). For example, Python 2.7 unittest defines TestCase in unittest.case module
* but it should be imported directly from unittest.
*
* @param symbol the symbol to be imported
* @param foothold the location where the import statement would be added
* @return the qualified name, or null if it wasn't possible to calculate one
*/
@Nullable
public static PyQualifiedName findCanonicalImportPath(@NotNull PsiElement symbol, @Nullable PsiElement foothold) {
PsiFileSystemItem srcfile = symbol instanceof PsiFileSystemItem ? (PsiFileSystemItem)symbol : symbol.getContainingFile();
if (srcfile == null) {
return null;
}
VirtualFile virtualFile = srcfile.getVirtualFile();
if (virtualFile == null) {
return null;
}
if (srcfile instanceof PsiFile && symbol instanceof PsiNamedElement && !(symbol instanceof PsiFileSystemItem)) {
PsiElement toplevel = symbol;
if (symbol instanceof PyFunction) {
final PyClass containingClass = ((PyFunction)symbol).getContainingClass();
if (containingClass != null) {
toplevel = containingClass;
}
}
PsiDirectory dir = ((PsiFile)srcfile).getContainingDirectory();
while (dir != null) {
PsiFile initPy = dir.findFile(PyNames.INIT_DOT_PY);
if (initPy == null) {
break;
}
if (initPy instanceof PyFile && toplevel.equals(((PyFile)initPy).getElementNamed(((PsiNamedElement)toplevel).getName()))) {
virtualFile = dir.getVirtualFile();
}
dir = dir.getParentDirectory();
}
}
final PyQualifiedName qname = findShortestImportableQName(foothold != null ? foothold : symbol, virtualFile);
if (qname != null) {
for (PyCanonicalPathProvider provider : Extensions.getExtensions(PyCanonicalPathProvider.EP_NAME)) {
final PyQualifiedName restored = provider.getCanonicalPath(qname);
if (restored != null) {
return restored;
}
}
}
return qname;
}
/**
* Tries to find roots that contain given vfile, and among them the root that contains at the smallest depth.
*/
private static class PathChoosingVisitor implements RootVisitor {
private final VirtualFile myVFile;
private List<String> myResult;
private PathChoosingVisitor(VirtualFile file) {
if (!file.isDirectory() && file.getName().equals(PyNames.INIT_DOT_PY)) {
myVFile = file.getParent();
}
else {
myVFile = file;
}
}
public boolean visitRoot(VirtualFile root, Module module, Sdk sdk) {
final String relativePath = VfsUtilCore.getRelativePath(myVFile, root, '/');
if (relativePath != null) {
List<String> result = StringUtil.split(relativePath, "/");
if (myResult == null || result.size() < myResult.size()) {
if (result.size() > 0) {
result.set(result.size() - 1, FileUtil.getNameWithoutExtension(result.get(result.size() - 1)));
}
for (String component : result) {
if (!PyNames.isIdentifier(component)) {
return true;
}
}
myResult = result;
}
}
return myResult == null || myResult.size() > 0;
}
@Nullable
public PyQualifiedName getResult() {
return myResult != null ? PyQualifiedName.fromComponents(myResult) : null;
}
}
}
@@ -6,7 +6,6 @@ import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
@@ -240,7 +239,7 @@ public class ResolveImportUtil {
@NotNull PyQualifiedName qualifiedName) {
final PsiDirectory containingDirectory = foothold.getContainingDirectory();
if (containingDirectory != null) {
final PyQualifiedName containingPath = findCanonicalImportPath(containingDirectory, null);
final PyQualifiedName containingPath = QualifiedNameFinder.findCanonicalImportPath(containingDirectory, null);
if (containingPath != null && containingPath.getComponentCount() > 0) {
final PyQualifiedName absolutePath = containingPath.append(qualifiedName.toString());
final QualifiedNameResolver absoluteVisitor = new QualifiedNameResolverImpl(absolutePath).fromElement(foothold);
@@ -258,7 +257,7 @@ public class ResolveImportUtil {
}
@Nullable
private static PythonPathCache getPathCache(PsiElement foothold) {
static PythonPathCache getPathCache(PsiElement foothold) {
PythonPathCache cache = null;
final Module module = ModuleUtilCore.findModuleForPsiElement(foothold);
if (module != null) {
@@ -412,149 +411,6 @@ public class ResolveImportUtil {
return ret;
}
/**
* Tries to find roots that contain given vfile, and among them the root that contains at the smallest depth.
*/
private static class PathChoosingVisitor implements RootVisitor {
private final VirtualFile myVFile;
private List<String> myResult;
private PathChoosingVisitor(VirtualFile file) {
if (!file.isDirectory() && file.getName().equals(PyNames.INIT_DOT_PY)) {
myVFile = file.getParent();
}
else {
myVFile = file;
}
}
public boolean visitRoot(VirtualFile root, Module module, Sdk sdk) {
final String relativePath = VfsUtilCore.getRelativePath(myVFile, root, '/');
if (relativePath != null) {
List<String> result = StringUtil.split(relativePath, "/");
if (myResult == null || result.size() < myResult.size()) {
if (result.size() > 0) {
result.set(result.size() - 1, FileUtil.getNameWithoutExtension(result.get(result.size() - 1)));
}
for (String component : result) {
if (!PyNames.isIdentifier(component)) {
return true;
}
}
myResult = result;
}
}
return myResult == null || myResult.size() > 0;
}
@Nullable
public PyQualifiedName getResult() {
return myResult != null ? PyQualifiedName.fromComponents(myResult) : null;
}
}
/**
* Looks for a way to import given file.
*
* @param foothold an element in the file to import to (maybe the file itself); used to determine module, roots, etc.
* @param vfile file which importable name we want to find.
* @return a possibly qualified name under which the file may be imported, or null. If there's more than one way (overlapping roots),
* the name with fewest qualifiers is selected.
*/
@Nullable
public static String findShortestImportableName(PsiElement foothold, @NotNull VirtualFile vfile) {
final PyQualifiedName qName = findShortestImportableQName(foothold, vfile);
return qName == null ? null : qName.toString();
}
@Nullable
public static PyQualifiedName findShortestImportableQName(@Nullable PsiFileSystemItem fsItem) {
VirtualFile vFile = fsItem != null ? fsItem.getVirtualFile() : null;
return vFile != null ? findShortestImportableQName(fsItem, vFile) : null;
}
@Nullable
public static PyQualifiedName findShortestImportableQName(@NotNull PsiElement foothold, @NotNull VirtualFile vfile) {
final PythonPathCache cache = getPathCache(foothold);
final PyQualifiedName name = cache != null ? cache.getName(vfile) : null;
if (name != null) {
return name;
}
PathChoosingVisitor visitor = new PathChoosingVisitor(vfile);
RootVisitorHost.visitRoots(foothold, visitor);
final PyQualifiedName result = visitor.getResult();
if (cache != null) {
cache.putName(vfile, result);
}
return result;
}
@Nullable
public static String findShortestImportableName(Module module, @NotNull VirtualFile vfile) {
final PythonPathCache cache = PythonModulePathCache.getInstance(module);
final PyQualifiedName name = cache.getName(vfile);
if (name != null) {
return name.toString();
}
PathChoosingVisitor visitor = new PathChoosingVisitor(vfile);
RootVisitorHost.visitRoots(module, false, visitor);
final PyQualifiedName result = visitor.getResult();
cache.putName(vfile, result);
return result == null ? null : result.toString();
}
/**
* Returns the name through which the specified symbol should be imported. This can be different from the qualified name of the
* symbol (the place where a symbol is defined). For example, Python 2.7 unittest defines TestCase in unittest.case module
* but it should be imported directly from unittest.
*
* @param symbol the symbol to be imported
* @param foothold the location where the import statement would be added
* @return the qualified name, or null if it wasn't possible to calculate one
*/
@Nullable
public static PyQualifiedName findCanonicalImportPath(@NotNull PsiElement symbol, @Nullable PsiElement foothold) {
PsiFileSystemItem srcfile = symbol instanceof PsiFileSystemItem ? (PsiFileSystemItem)symbol : symbol.getContainingFile();
if (srcfile == null) {
return null;
}
VirtualFile virtualFile = srcfile.getVirtualFile();
if (virtualFile == null) {
return null;
}
if (srcfile instanceof PsiFile && symbol instanceof PsiNamedElement && !(symbol instanceof PsiFileSystemItem)) {
PsiElement toplevel = symbol;
if (symbol instanceof PyFunction) {
final PyClass containingClass = ((PyFunction)symbol).getContainingClass();
if (containingClass != null) {
toplevel = containingClass;
}
}
PsiDirectory dir = ((PsiFile)srcfile).getContainingDirectory();
while (dir != null) {
PsiFile initPy = dir.findFile(PyNames.INIT_DOT_PY);
if (initPy == null) {
break;
}
if (initPy instanceof PyFile && toplevel.equals(((PyFile)initPy).getElementNamed(((PsiNamedElement)toplevel).getName()))) {
virtualFile = dir.getVirtualFile();
}
dir = dir.getParentDirectory();
}
}
final PyQualifiedName qname = findShortestImportableQName(foothold != null ? foothold : symbol, virtualFile);
if (qname != null) {
for (PyCanonicalPathProvider provider : Extensions.getExtensions(PyCanonicalPathProvider.EP_NAME)) {
final PyQualifiedName restored = provider.getCanonicalPath(qname);
if (restored != null) {
return restored;
}
}
}
return qname;
}
/**
* @param element what we test (identifier, reference, import element, etc)
* @return the how the element relates to an enclosing import statement, if any
@@ -99,7 +99,7 @@ public class PyModuleType implements PyType { // Modules don't descend from obje
@Nullable
private List<? extends RatedResolveResult> resolveImplicitPackageMember(@NotNull String name,
@NotNull List<PyImportElement> importElements) {
final PyQualifiedName packageQName = ResolveImportUtil.findCanonicalImportPath(myModule, null);
final PyQualifiedName packageQName = QualifiedNameFinder.findCanonicalImportPath(myModule, null);
if (packageQName != null) {
final PyQualifiedName resolvingQName = packageQName.append(name);
for (PyImportElement importElement : importElements) {
@@ -146,7 +146,7 @@ public class PyModuleType implements PyType { // Modules don't descend from obje
if (file != null) {
file = file.getOriginalFile();
}
final PyQualifiedName absoluteQName = ResolveImportUtil.findShortestImportableQName(file);
final PyQualifiedName absoluteQName = QualifiedNameFinder.findShortestImportableQName(file);
if (file != null && absoluteQName != null) {
final PyQualifiedName prefixQName = PyUtil.isPackage(file) ? absoluteQName : absoluteQName.removeLastComponent();
if (prefixQName.getComponentCount() > 0) {
@@ -18,7 +18,7 @@ import com.jetbrains.python.psi.impl.PyBuiltinCache;
import com.jetbrains.python.psi.impl.PyImportedModule;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -244,7 +244,7 @@ public class PyClassRefactoringUtil {
final PsiFile newFile = element.getContainingFile();
final PsiFile file = anchor.getContainingFile();
if (newFile == file) return false;
final PyQualifiedName qname = ResolveImportUtil.findCanonicalImportPath(element, anchor);
final PyQualifiedName qname = QualifiedNameFinder.findCanonicalImportPath(element, anchor);
if (qname == null || !isValidQualifiedName(qname)) {
return false;
}
@@ -21,7 +21,7 @@ import com.jetbrains.python.codeInsight.imports.PyImportOptimizer;
import com.jetbrains.python.documentation.DocStringTypeReference;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
import com.jetbrains.python.refactoring.PyRefactoringUtil;
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
import org.jetbrains.annotations.NotNull;
@@ -228,7 +228,7 @@ public class PyMoveClassOrFunctionProcessor extends BaseRefactoringProcessor {
}
private static void checkValidImportableFile(PsiElement anchor, VirtualFile file) {
final PyQualifiedName qName = ResolveImportUtil.findShortestImportableQName(anchor, file);
final PyQualifiedName qName = QualifiedNameFinder.findShortestImportableQName(anchor, file);
if (!PyClassRefactoringUtil.isValidQualifiedName(qName)) {
throw new IncorrectOperationException(PyBundle.message("refactoring.move.class.or.function.error.cannot.use.module.name.$0", qName));
}
@@ -12,7 +12,7 @@ import com.jetbrains.python.actions.CreatePackageAction;
import com.jetbrains.python.codeInsight.imports.PyImportOptimizer;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
import com.jetbrains.python.refactoring.PyRefactoringUtil;
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
import org.jetbrains.annotations.NotNull;
@@ -80,13 +80,13 @@ public class PyMoveFileHandler extends MoveFileHandler {
if (importStmt instanceof PyFromImportStatement && PsiTreeUtil.getParentOfType(element, PyImportElement.class) != null) {
continue;
}
final PyQualifiedName newElementName = ResolveImportUtil.findCanonicalImportPath(newElement, element);
final PyQualifiedName newElementName = QualifiedNameFinder.findCanonicalImportPath(newElement, element);
replaceWithQualifiedExpression(element, newElementName);
}
else if (element instanceof PyReferenceExpression) {
updatedFiles.add(file);
if (((PyReferenceExpression)element).getQualifier() != null) {
final PyQualifiedName newQualifiedName = ResolveImportUtil.findCanonicalImportPath(newElement, element);
final PyQualifiedName newQualifiedName = QualifiedNameFinder.findCanonicalImportPath(newElement, element);
replaceWithQualifiedExpression(element, newQualifiedName);
} else {
final PyQualifiedName newName = PyQualifiedName.fromComponents(PyClassRefactoringUtil.getOriginalName(newElement));