ask each intermediate context file for a forced resolve scope (IDEA-136304)
[idea/community.git] / platform / indexing-impl / src / com / intellij / psi / impl / file / impl / ResolveScopeManagerImpl.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.psi.impl.file.impl;
17
18 import com.intellij.openapi.module.Module;
19 import com.intellij.openapi.module.impl.scopes.LibraryRuntimeClasspathScope;
20 import com.intellij.openapi.progress.ProgressIndicatorProvider;
21 import com.intellij.openapi.project.Project;
22 import com.intellij.openapi.roots.*;
23 import com.intellij.openapi.roots.impl.LibraryScopeCache;
24 import com.intellij.openapi.vfs.VirtualFile;
25 import com.intellij.openapi.vfs.VirtualFileWithId;
26 import com.intellij.psi.*;
27 import com.intellij.psi.impl.PsiManagerImpl;
28 import com.intellij.psi.impl.ResolveScopeManager;
29 import com.intellij.psi.search.DelegatingGlobalSearchScope;
30 import com.intellij.psi.search.GlobalSearchScope;
31 import com.intellij.psi.search.SearchScope;
32 import com.intellij.util.containers.ConcurrentFactoryMap;
33 import org.jetbrains.annotations.NotNull;
34
35 import java.util.ArrayList;
36 import java.util.List;
37 import java.util.Map;
38
39 public class ResolveScopeManagerImpl extends ResolveScopeManager {
40   private final Project myProject;
41   private final ProjectRootManager myProjectRootManager;
42   private final PsiManager myManager;
43
44   private final Map<VirtualFile, GlobalSearchScope> myDefaultResolveScopesCache = new ConcurrentFactoryMap<VirtualFile, GlobalSearchScope>() {
45     @Override
46     protected GlobalSearchScope create(@NotNull VirtualFile key) {
47       GlobalSearchScope scope = null;
48       for(ResolveScopeProvider resolveScopeProvider: ResolveScopeProvider.EP_NAME.getExtensions()) {
49         scope = resolveScopeProvider.getResolveScope(key, myProject);
50         if (scope != null) break;
51       }
52       if (scope == null) scope = getInherentResolveScope(key);
53       for (ResolveScopeEnlarger enlarger : ResolveScopeEnlarger.EP_NAME.getExtensions()) {
54         final SearchScope extra = enlarger.getAdditionalResolveScope(key, myProject);
55         if (extra != null) {
56           scope = scope.union(extra);
57         }
58       }
59
60       return scope;
61     }
62   };
63
64   public ResolveScopeManagerImpl(Project project, ProjectRootManager projectRootManager, PsiManager psiManager) {
65     myProject = project;
66     myProjectRootManager = projectRootManager;
67     myManager = psiManager;
68
69     ((PsiManagerImpl) psiManager).registerRunnableToRunOnChange(new Runnable() {
70       @Override
71       public void run() {
72         myDefaultResolveScopesCache.clear();
73       }
74     });
75
76   }
77
78   private GlobalSearchScope getResolveScopeFromProviders(@NotNull final VirtualFile vFile) {
79     return myDefaultResolveScopesCache.get(vFile);
80   }
81
82   private GlobalSearchScope getInherentResolveScope(VirtualFile vFile) {
83     ProjectFileIndex projectFileIndex = myProjectRootManager.getFileIndex();
84     Module module = projectFileIndex.getModuleForFile(vFile);
85     if (module != null) {
86       boolean includeTests = projectFileIndex.isInTestSourceContent(vFile);
87       return GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module, includeTests);
88     }
89     else {
90       // resolve references in libraries in context of all modules which contain it
91       List<Module> modulesLibraryUsedIn = new ArrayList<Module>();
92       List<OrderEntry> orderEntries = projectFileIndex.getOrderEntriesForFile(vFile);
93
94       LibraryOrderEntry lib = null;
95       for (OrderEntry entry : orderEntries) {
96         if (entry instanceof JdkOrderEntry) {
97           return LibraryScopeCache.getInstance(myProject).getScopeForSdk((JdkOrderEntry)entry);
98         }
99
100         if (entry instanceof LibraryOrderEntry) {
101           lib = (LibraryOrderEntry)entry;
102           modulesLibraryUsedIn.add(entry.getOwnerModule());
103         }
104         else if (entry instanceof ModuleOrderEntry) {
105           modulesLibraryUsedIn.add(entry.getOwnerModule());
106         }
107       }
108
109       GlobalSearchScope allCandidates = LibraryScopeCache.getInstance(myProject).getScopeForLibraryUsedIn(modulesLibraryUsedIn);
110       if (lib != null) {
111         final LibraryRuntimeClasspathScope preferred = new LibraryRuntimeClasspathScope(myProject, lib);
112         // prefer current library
113         return new DelegatingGlobalSearchScope(allCandidates, preferred) {
114           @Override
115           public int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) {
116             boolean c1 = preferred.contains(file1);
117             boolean c2 = preferred.contains(file2);
118             if (c1 && !c2) return 1;
119             if (c2 && !c1) return -1;
120
121             return super.compare(file1, file2);
122           }
123         };
124       }
125       return allCandidates;
126     }
127   }
128
129   @Override
130   @NotNull
131   public GlobalSearchScope getResolveScope(@NotNull PsiElement element) {
132     ProgressIndicatorProvider.checkCanceled();
133
134     VirtualFile vFile;
135     final PsiFile contextFile;
136     if (element instanceof PsiDirectory) {
137       vFile = ((PsiDirectory)element).getVirtualFile();
138       contextFile = null;
139     }
140     else {
141       final PsiFile containingFile = element.getContainingFile();
142       if (containingFile instanceof PsiCodeFragment) {
143         final GlobalSearchScope forcedScope = ((PsiCodeFragment)containingFile).getForcedResolveScope();
144         if (forcedScope != null) {
145           return forcedScope;
146         }
147       }
148
149       if (containingFile != null) {
150         PsiElement context = containingFile.getContext();
151         if (context != null) {
152           return getResolveScope(context);
153         }
154       }
155
156       contextFile = containingFile;
157       if (containingFile == null) {
158         return GlobalSearchScope.allScope(myProject);
159       }
160       else if (contextFile instanceof FileResolveScopeProvider) {
161         return ((FileResolveScopeProvider) contextFile).getFileResolveScope();
162       }
163       vFile = contextFile.getOriginalFile().getVirtualFile();
164     }
165     if (vFile == null || contextFile == null) {
166       return GlobalSearchScope.allScope(myProject);
167     }
168
169     return getResolveScopeFromProviders(vFile);
170   }
171
172
173   @Override
174   public GlobalSearchScope getDefaultResolveScope(final VirtualFile vFile) {
175     final PsiFile psiFile = myManager.findFile(vFile);
176     assert psiFile != null;
177     return getResolveScopeFromProviders(vFile);
178   }
179
180
181   @Override
182   @NotNull
183   public GlobalSearchScope getUseScope(@NotNull PsiElement element) {
184     VirtualFile vDirectory;
185     final VirtualFile virtualFile;
186     final PsiFile containingFile;
187     final GlobalSearchScope allScope = GlobalSearchScope.allScope(myManager.getProject());
188     if (element instanceof PsiDirectory) {
189       vDirectory = ((PsiDirectory)element).getVirtualFile();
190       virtualFile = null;
191       containingFile = null;
192     }
193     else {
194       containingFile = element.getContainingFile();
195       if (containingFile == null) return allScope;
196       virtualFile = containingFile.getVirtualFile();
197       if (virtualFile == null) return allScope;
198       vDirectory = virtualFile.getParent();
199     }
200
201     if (vDirectory == null) return allScope;
202     final ProjectFileIndex projectFileIndex = myProjectRootManager.getFileIndex();
203     final Module module = projectFileIndex.getModuleForFile(vDirectory);
204     if (module == null) {
205       return containingFile == null || virtualFile.isDirectory() || allScope.contains(virtualFile)
206              ? allScope : GlobalSearchScope.fileScope(containingFile).uniteWith(allScope);
207     }
208     boolean isTest = projectFileIndex.isInTestSourceContent(vDirectory);
209     GlobalSearchScope scope = isTest
210                               ? GlobalSearchScope.moduleTestsWithDependentsScope(module)
211                               : GlobalSearchScope.moduleWithDependentsScope(module);
212     RefResolveService resolveService;
213     if (virtualFile instanceof VirtualFileWithId && RefResolveService.ENABLED && (resolveService = RefResolveService.getInstance(myProject)).isUpToDate()) {
214       return resolveService.restrictByBackwardIds(virtualFile, scope);
215     }
216     return scope;
217   }
218 }