e65dc0ebfc8bfe5392cd5031f89cf756eda59d55
[idea/community.git] / platform / indexing-impl / src / com / intellij / psi / impl / file / impl / ResolveScopeManagerImpl.java
1 // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.psi.impl.file.impl;
3
4 import com.intellij.injected.editor.VirtualFileWindow;
5 import com.intellij.notebook.editor.BackedVirtualFile;
6 import com.intellij.openapi.module.Module;
7 import com.intellij.openapi.progress.ProgressIndicatorProvider;
8 import com.intellij.openapi.project.Project;
9 import com.intellij.openapi.roots.*;
10 import com.intellij.openapi.roots.impl.LibraryScopeCache;
11 import com.intellij.openapi.vfs.VirtualFile;
12 import com.intellij.openapi.vfs.VirtualFileWithId;
13 import com.intellij.psi.*;
14 import com.intellij.psi.impl.PsiManagerImpl;
15 import com.intellij.psi.impl.ResolveScopeManager;
16 import com.intellij.psi.search.GlobalSearchScope;
17 import com.intellij.psi.search.PsiSearchScopeUtil;
18 import com.intellij.psi.search.SearchScope;
19 import com.intellij.util.containers.ConcurrentFactoryMap;
20 import com.intellij.util.containers.ContainerUtil;
21 import com.intellij.util.indexing.AdditionalIndexableFileSet;
22 import org.jetbrains.annotations.NotNull;
23
24 import java.util.List;
25 import java.util.Map;
26
27 public class ResolveScopeManagerImpl extends ResolveScopeManager {
28   private final Project myProject;
29   private final ProjectRootManager myProjectRootManager;
30   private final PsiManager myManager;
31
32   private final Map<VirtualFile, GlobalSearchScope> myDefaultResolveScopesCache;
33   private final AdditionalIndexableFileSet myAdditionalIndexableFileSet;
34
35   public ResolveScopeManagerImpl(Project project, ProjectRootManager projectRootManager, PsiManager psiManager) {
36     myProject = project;
37     myProjectRootManager = projectRootManager;
38     myManager = psiManager;
39     myAdditionalIndexableFileSet = new AdditionalIndexableFileSet(project);
40
41     myDefaultResolveScopesCache = ConcurrentFactoryMap.create(
42       key -> {
43         GlobalSearchScope scope = null;
44         for (ResolveScopeProvider resolveScopeProvider : ResolveScopeProvider.EP_NAME.getExtensionList()) {
45           scope = resolveScopeProvider.getResolveScope(key, myProject);
46           if (scope != null) break;
47         }
48         if (scope == null) scope = getInherentResolveScope(key);
49         for (ResolveScopeEnlarger enlarger : ResolveScopeEnlarger.EP_NAME.getExtensions()) {
50           SearchScope extra = enlarger.getAdditionalResolveScope(key, myProject);
51           if (extra != null) {
52             scope = scope.union(extra);
53           }
54         }
55         return scope;
56       },
57       ContainerUtil::createConcurrentWeakKeySoftValueMap);
58
59     ((PsiManagerImpl)psiManager).registerRunnableToRunOnChange(myDefaultResolveScopesCache::clear);
60   }
61
62   private GlobalSearchScope getResolveScopeFromProviders(@NotNull final VirtualFile vFile) {
63     return myDefaultResolveScopesCache.get(vFile);
64   }
65
66   private GlobalSearchScope getInherentResolveScope(VirtualFile vFile) {
67     ProjectFileIndex projectFileIndex = myProjectRootManager.getFileIndex();
68     Module module = projectFileIndex.getModuleForFile(vFile);
69     if (module != null) {
70       boolean includeTests = TestSourcesFilter.isTestSources(vFile, myProject);
71       return GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module, includeTests);
72     }
73
74     if (!projectFileIndex.isInLibrary(vFile)) {
75       GlobalSearchScope allScope = GlobalSearchScope.allScope(myProject);
76       if (!allScope.contains(vFile)) {
77         return GlobalSearchScope.fileScope(myProject, vFile).uniteWith(allScope);
78       }
79       return allScope;
80     }
81
82     return LibraryScopeCache.getInstance(myProject).getLibraryScope(projectFileIndex.getOrderEntriesForFile(vFile));
83   }
84
85   @Override
86   @NotNull
87   public GlobalSearchScope getResolveScope(@NotNull PsiElement element) {
88     ProgressIndicatorProvider.checkCanceled();
89
90     if (element instanceof PsiDirectory) {
91       return getResolveScopeFromProviders(((PsiDirectory)element).getVirtualFile());
92     }
93
94     PsiFile containingFile = element.getContainingFile();
95     if (containingFile instanceof PsiCodeFragment) {
96       GlobalSearchScope forcedScope = ((PsiCodeFragment)containingFile).getForcedResolveScope();
97       if (forcedScope != null) {
98         return forcedScope;
99       }
100     }
101
102     if (containingFile != null) {
103       PsiElement context = containingFile.getContext();
104       if (context != null) {
105         return withFile(containingFile, getResolveScope(context));
106       }
107     }
108
109     if (containingFile == null) {
110       return GlobalSearchScope.allScope(myProject);
111     }
112     if (containingFile instanceof FileResolveScopeProvider) {
113       return ((FileResolveScopeProvider)containingFile).getFileResolveScope();
114     }
115     VirtualFile vFile = containingFile.getOriginalFile().getVirtualFile();
116     if (vFile == null) {
117       return withFile(containingFile, GlobalSearchScope.allScope(myProject));
118     }
119     return getResolveScopeFromProviders(vFile);
120   }
121
122   private GlobalSearchScope withFile(PsiFile containingFile, GlobalSearchScope scope) {
123     return PsiSearchScopeUtil.isInScope(scope, containingFile)
124            ? scope
125            : scope.uniteWith(GlobalSearchScope.fileScope(myProject, containingFile.getViewProvider().getVirtualFile()));
126   }
127
128
129   @NotNull
130   @Override
131   public GlobalSearchScope getDefaultResolveScope(@NotNull final VirtualFile vFile) {
132     final PsiFile psiFile = myManager.findFile(vFile);
133     assert psiFile != null : "directory=" + vFile.isDirectory() + "; " + myProject;
134     return getResolveScopeFromProviders(vFile);
135   }
136
137
138   @Override
139   @NotNull
140   public GlobalSearchScope getUseScope(@NotNull PsiElement element) {
141     VirtualFile vDirectory;
142     final VirtualFile virtualFile;
143     final PsiFile containingFile;
144     final GlobalSearchScope allScope = GlobalSearchScope.allScope(myManager.getProject());
145     if (element instanceof PsiDirectory) {
146       vDirectory = ((PsiDirectory)element).getVirtualFile();
147       virtualFile = null;
148       containingFile = null;
149     }
150     else {
151       containingFile = element.getContainingFile();
152       if (containingFile == null) return allScope;
153       virtualFile = containingFile.getVirtualFile();
154       if (virtualFile == null) return allScope;
155       if (virtualFile instanceof VirtualFileWindow) {
156         return GlobalSearchScope.fileScope(myProject, ((VirtualFileWindow)virtualFile).getDelegate());
157       }
158       if (virtualFile instanceof BackedVirtualFile) {
159         return GlobalSearchScope.fileScope(myProject, ((BackedVirtualFile)virtualFile).getOriginFile());
160       }
161       if ("Scratch".equals(virtualFile.getFileType().getName())) {
162         return GlobalSearchScope.fileScope(myProject, virtualFile);
163       }
164       vDirectory = virtualFile.getParent();
165     }
166
167     if (vDirectory == null) return allScope;
168     final ProjectFileIndex projectFileIndex = myProjectRootManager.getFileIndex();
169     final Module module = projectFileIndex.getModuleForFile(vDirectory);
170     if (module == null) {
171       VirtualFile notNullVFile = virtualFile != null ? virtualFile : vDirectory;
172       final List<OrderEntry> entries = projectFileIndex.getOrderEntriesForFile(notNullVFile);
173       if (entries.isEmpty() && (myAdditionalIndexableFileSet.isInSet(notNullVFile) || isFromAdditionalLibraries(notNullVFile))) {
174         return allScope;
175       }
176
177       GlobalSearchScope result = LibraryScopeCache.getInstance(myProject).getLibraryUseScope(entries);
178       return containingFile == null || virtualFile.isDirectory() || result.contains(virtualFile)
179              ? result : GlobalSearchScope.fileScope(containingFile).uniteWith(result);
180     }
181     boolean isTest = TestSourcesFilter.isTestSources(vDirectory, myProject);
182     GlobalSearchScope scope = isTest
183                               ? GlobalSearchScope.moduleTestsWithDependentsScope(module)
184                               : GlobalSearchScope.moduleWithDependentsScope(module);
185     RefResolveService resolveService;
186     if (virtualFile instanceof VirtualFileWithId && RefResolveService.ENABLED && (resolveService = RefResolveService.getInstance(myProject)).isUpToDate()) {
187       return resolveService.restrictByBackwardIds(virtualFile, scope);
188     }
189     return scope;
190   }
191
192   private boolean isFromAdditionalLibraries(@NotNull final VirtualFile file) {
193     for (final AdditionalLibraryRootsProvider provider : AdditionalLibraryRootsProvider.EP_NAME.getExtensionList()) {
194       for (final SyntheticLibrary library : provider.getAdditionalProjectLibraries(myProject)) {
195         if (library.contains(file)) {
196           return true;
197         }
198       }
199     }
200     return false;
201   }
202 }