f3a9c183874d86ce66b438568bcd35d4bec9dedc
[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.impl.source.resolve.FileContextUtil;
30 import com.intellij.psi.search.DelegatingGlobalSearchScope;
31 import com.intellij.psi.search.GlobalSearchScope;
32 import com.intellij.psi.search.SearchScope;
33 import com.intellij.util.containers.ConcurrentFactoryMap;
34 import org.jetbrains.annotations.NotNull;
35
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.Map;
39
40 public class ResolveScopeManagerImpl extends ResolveScopeManager {
41   private final Project myProject;
42   private final ProjectRootManager myProjectRootManager;
43   private final PsiManager myManager;
44
45   private final Map<VirtualFile, GlobalSearchScope> myDefaultResolveScopesCache = new ConcurrentFactoryMap<VirtualFile, GlobalSearchScope>() {
46     @Override
47     protected GlobalSearchScope create(@NotNull VirtualFile key) {
48       GlobalSearchScope scope = null;
49       for(ResolveScopeProvider resolveScopeProvider: ResolveScopeProvider.EP_NAME.getExtensions()) {
50         scope = resolveScopeProvider.getResolveScope(key, myProject);
51         if (scope != null) break;
52       }
53       if (scope == null) scope = getInherentResolveScope(key);
54       for (ResolveScopeEnlarger enlarger : ResolveScopeEnlarger.EP_NAME.getExtensions()) {
55         final SearchScope extra = enlarger.getAdditionalResolveScope(key, myProject);
56         if (extra != null) {
57           scope = scope.union(extra);
58         }
59       }
60
61       return scope;
62     }
63   };
64
65   public ResolveScopeManagerImpl(Project project, ProjectRootManager projectRootManager, PsiManager psiManager) {
66     myProject = project;
67     myProjectRootManager = projectRootManager;
68     myManager = psiManager;
69
70     ((PsiManagerImpl) psiManager).registerRunnableToRunOnChange(new Runnable() {
71       @Override
72       public void run() {
73         myDefaultResolveScopesCache.clear();
74       }
75     });
76
77   }
78
79   private GlobalSearchScope getResolveScopeFromProviders(@NotNull final VirtualFile vFile) {
80     return myDefaultResolveScopesCache.get(vFile);
81   }
82
83   private GlobalSearchScope getInherentResolveScope(VirtualFile vFile) {
84     ProjectFileIndex projectFileIndex = myProjectRootManager.getFileIndex();
85     Module module = projectFileIndex.getModuleForFile(vFile);
86     if (module != null) {
87       boolean includeTests = projectFileIndex.isInTestSourceContent(vFile);
88       return GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module, includeTests);
89     }
90     else {
91       // resolve references in libraries in context of all modules which contain it
92       List<Module> modulesLibraryUsedIn = new ArrayList<Module>();
93       List<OrderEntry> orderEntries = projectFileIndex.getOrderEntriesForFile(vFile);
94
95       LibraryOrderEntry lib = null;
96       for (OrderEntry entry : orderEntries) {
97         if (entry instanceof JdkOrderEntry) {
98           return LibraryScopeCache.getInstance(myProject).getScopeForSdk((JdkOrderEntry)entry);
99         }
100
101         if (entry instanceof LibraryOrderEntry) {
102           lib = (LibraryOrderEntry)entry;
103           modulesLibraryUsedIn.add(entry.getOwnerModule());
104         }
105         else if (entry instanceof ModuleOrderEntry) {
106           modulesLibraryUsedIn.add(entry.getOwnerModule());
107         }
108       }
109
110       GlobalSearchScope allCandidates = LibraryScopeCache.getInstance(myProject).getScopeForLibraryUsedIn(modulesLibraryUsedIn);
111       if (lib != null) {
112         final LibraryRuntimeClasspathScope preferred = new LibraryRuntimeClasspathScope(myProject, lib);
113         // prefer current library
114         return new DelegatingGlobalSearchScope(allCandidates, preferred) {
115           @Override
116           public int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) {
117             boolean c1 = preferred.contains(file1);
118             boolean c2 = preferred.contains(file2);
119             if (c1 && !c2) return 1;
120             if (c2 && !c1) return -1;
121
122             return super.compare(file1, file2);
123           }
124         };
125       }
126       return allCandidates;
127     }
128   }
129
130   @Override
131   @NotNull
132   public GlobalSearchScope getResolveScope(@NotNull PsiElement element) {
133     ProgressIndicatorProvider.checkCanceled();
134
135     VirtualFile vFile;
136     final PsiFile contextFile;
137     if (element instanceof PsiDirectory) {
138       vFile = ((PsiDirectory)element).getVirtualFile();
139       contextFile = null;
140     }
141     else {
142       final PsiFile containingFile = element.getContainingFile();
143       if (containingFile instanceof PsiCodeFragment) {
144         final GlobalSearchScope forcedScope = ((PsiCodeFragment)containingFile).getForcedResolveScope();
145         if (forcedScope != null) {
146           return forcedScope;
147         }
148         final PsiElement context = containingFile.getContext();
149         if (context == null) {
150           return GlobalSearchScope.allScope(myProject);
151         }
152         return getResolveScope(context);
153       }
154
155       contextFile = containingFile != null ? FileContextUtil.getContextFile(containingFile) : null;
156       if (contextFile == null) {
157         return GlobalSearchScope.allScope(myProject);
158       }
159       else if (contextFile instanceof FileResolveScopeProvider) {
160         return ((FileResolveScopeProvider) contextFile).getFileResolveScope();
161       }
162       vFile = contextFile.getOriginalFile().getVirtualFile();
163     }
164     if (vFile == null || contextFile == null) {
165       return GlobalSearchScope.allScope(myProject);
166     }
167
168     return getResolveScopeFromProviders(vFile);
169   }
170
171
172   @Override
173   public GlobalSearchScope getDefaultResolveScope(final VirtualFile vFile) {
174     final PsiFile psiFile = myManager.findFile(vFile);
175     assert psiFile != null;
176     return getResolveScopeFromProviders(vFile);
177   }
178
179
180   @Override
181   @NotNull
182   public GlobalSearchScope getUseScope(@NotNull PsiElement element) {
183     VirtualFile vDirectory;
184     final VirtualFile virtualFile;
185     final PsiFile containingFile;
186     final GlobalSearchScope allScope = GlobalSearchScope.allScope(myManager.getProject());
187     if (element instanceof PsiDirectory) {
188       vDirectory = ((PsiDirectory)element).getVirtualFile();
189       virtualFile = null;
190       containingFile = null;
191     }
192     else {
193       containingFile = element.getContainingFile();
194       if (containingFile == null) return allScope;
195       virtualFile = containingFile.getVirtualFile();
196       if (virtualFile == null) return allScope;
197       vDirectory = virtualFile.getParent();
198     }
199
200     if (vDirectory == null) return allScope;
201     final ProjectFileIndex projectFileIndex = myProjectRootManager.getFileIndex();
202     final Module module = projectFileIndex.getModuleForFile(vDirectory);
203     if (module == null) {
204       return containingFile == null || virtualFile.isDirectory() || allScope.contains(virtualFile)
205              ? allScope : GlobalSearchScope.fileScope(containingFile).uniteWith(allScope);
206     }
207     boolean isTest = projectFileIndex.isInTestSourceContent(vDirectory);
208     GlobalSearchScope scope = isTest
209                               ? GlobalSearchScope.moduleTestsWithDependentsScope(module)
210                               : GlobalSearchScope.moduleWithDependentsScope(module);
211     RefResolveService resolveService;
212     if (virtualFile instanceof VirtualFileWithId && RefResolveService.ENABLED && (resolveService = RefResolveService.getInstance(myProject)).isUpToDate()) {
213       return resolveService.restrictByBackwardIds(virtualFile, scope);
214     }
215     return scope;
216   }
217 }