From df979257a64d70ae36de1c05bf234f8b2e77dbd6 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Fri, 19 Feb 2021 13:36:57 +0300 Subject: [PATCH] PY-47281 Re-use QualifiedNameBasedScope in PyQualifiedNameCompletionMatcher GitOrigin-RevId: f8407f86e083bc36f1be80b6fa43348a350511f2 --- .../PyQualifiedNameCompletionMatcher.java | 75 ++++++------------- 1 file changed, 23 insertions(+), 52 deletions(-) diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/stubs/PyQualifiedNameCompletionMatcher.java b/python/python-psi-impl/src/com/jetbrains/python/psi/stubs/PyQualifiedNameCompletionMatcher.java index 95a8020f5d57..d9f999d3af74 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/stubs/PyQualifiedNameCompletionMatcher.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/stubs/PyQualifiedNameCompletionMatcher.java @@ -6,12 +6,9 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.ProjectFileIndex; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; -import com.intellij.psi.search.DelegatingGlobalSearchScope; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.stubs.StubIndex; import com.intellij.psi.util.QualifiedName; @@ -26,7 +23,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.HashSet; -import java.util.List; import java.util.Objects; import java.util.Set; @@ -46,7 +42,7 @@ public class PyQualifiedNameCompletionMatcher { StubIndex stubIndex = StubIndex.getInstance(); Project project = Objects.requireNonNull(scope.getProject()); - GlobalSearchScope moduleMatchingScope = new ModuleQualifiedNameMatchingScope(scope, matcher, project); + GlobalSearchScope moduleMatchingScope = scope.intersectWith(new ModuleQualifiedNameMatchingScope(matcher, project)); Set alreadySuggestedAttributes = new HashSet<>(); IndexLookupStats stats = new IndexLookupStats(); try { @@ -60,7 +56,7 @@ public class PyQualifiedNameCompletionMatcher { attributeName, project, moduleMatchingScope, idFilter, PyElement.class, element -> { ProgressManager.checkCanceled(); VirtualFile vFile = element.getContainingFile().getVirtualFile(); - QualifiedName moduleQualifiedName = ModuleQualifiedNameMatchingScope.restoreModuleQualifiedName(vFile, project); + QualifiedName moduleQualifiedName = findQualifiedNameInClosestRoot(vFile, project); assert moduleQualifiedName != null : vFile; QualifiedName canonicalImportPath = findCanonicalImportPath(element, moduleQualifiedName, currentFile); QualifiedName importPath; @@ -196,57 +192,32 @@ public class PyQualifiedNameCompletionMatcher { } } - private static class ModuleQualifiedNameMatchingScope extends DelegatingGlobalSearchScope { + private static class ModuleQualifiedNameMatchingScope extends QualifiedNameFinder.QualifiedNameBasedScope { private final QualifiedNameMatcher myQualifiedNameMatcher; - private final Project myProject; - ModuleQualifiedNameMatchingScope(@NotNull GlobalSearchScope baseScope, - @NotNull QualifiedNameMatcher qualifiedNameMatcher, - @NotNull Project project) { - super(baseScope); + ModuleQualifiedNameMatchingScope(@NotNull QualifiedNameMatcher qualifiedNameMatcher, @NotNull Project project) { + super(project); myQualifiedNameMatcher = qualifiedNameMatcher; - myProject = project; } @Override - public boolean contains(@NotNull VirtualFile file) { - if (!super.contains(file)) return false; - QualifiedName qualifiedName = restoreModuleQualifiedName(file, myProject); - if (qualifiedName == null) return false; - return myQualifiedNameMatcher.qualifierMatches(qualifiedName); - } - - @Nullable - private static QualifiedName restoreModuleQualifiedName(@NotNull VirtualFile vFile, @NotNull Project project) { - ProjectFileIndex projectFileIndex = ProjectFileIndex.SERVICE.getInstance(project); - String fileName = vFile.getName(); - VirtualFile nameAnchor = fileName.equals(PyNames.INIT_DOT_PY) || fileName.equals(PyNames.INIT_DOT_PYI) ? vFile.getParent() : vFile; - VirtualFile closestRoot = findClosestRoot(nameAnchor, projectFileIndex); - if (closestRoot == null) return null; - String relativePath = VfsUtilCore.getRelativePath(nameAnchor, closestRoot); - // A relative path can be empty in case of __init__.py directly inside a root. - if (relativePath == null || relativePath.isEmpty()) return null; - return convertPathToImportableQualifiedName(relativePath); - } - - @Nullable - private static QualifiedName convertPathToImportableQualifiedName(@NotNull String relativePath) { - List parts = StringUtil.split(relativePath, VfsUtilCore.VFS_SEPARATOR); - String fileName = parts.get(parts.size() - 1); - parts.set(parts.size() - 1, StringUtil.substringBeforeLast(fileName, ".")); - if (ContainerUtil.exists(parts, p -> !PyNames.isIdentifier(p))) return null; - return QualifiedName.fromComponents(parts); - } - - @Nullable - private static VirtualFile findClosestRoot(@NotNull VirtualFile vFile, @NotNull ProjectFileIndex projectFileIndex) { - VirtualFile sourceRoot = projectFileIndex.getSourceRootForFile(vFile); - if (sourceRoot != null) return sourceRoot; - VirtualFile contentRoot = projectFileIndex.getContentRootForFile(vFile); - if (contentRoot != null) return contentRoot; - VirtualFile libraryRoot = projectFileIndex.getClassRootForFile(vFile); - if (libraryRoot != null) return libraryRoot; - return null; + protected boolean containsQualifiedNameInRoot(@NotNull VirtualFile root, @NotNull QualifiedName qName) { + return ContainerUtil.all(qName.getComponents(), PyNames::isIdentifier) && myQualifiedNameMatcher.qualifierMatches(qName); } } + + @Nullable + private static QualifiedName findQualifiedNameInClosestRoot(@NotNull VirtualFile file, @NotNull Project project) { + // TODO Come up with a better API for exposing these internals + Ref result = Ref.create(); + //noinspection ResultOfMethodCallIgnored + new QualifiedNameFinder.QualifiedNameBasedScope(project) { + @Override + protected boolean containsQualifiedNameInRoot(@NotNull VirtualFile root, @NotNull QualifiedName qName) { + result.set(qName); + return true; + } + }.contains(file); + return result.get(); + } }