mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
pull out qualified name resolve logic from ResolveImportUtil into a separate QualifiedNameFinder class
This commit is contained in:
@@ -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));
|
||||
|
||||
+2
-2
@@ -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;
|
||||
}
|
||||
|
||||
+2
-2
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user