show "Current File" scope always (WEB-20644)
[idea/community.git] / platform / lang-impl / src / com / intellij / psi / search / PredefinedSearchScopeProviderImpl.java
1 /*
2  * Copyright 2000-2016 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.search;
17
18 import com.intellij.ide.IdeBundle;
19 import com.intellij.ide.favoritesTreeView.FavoritesManager;
20 import com.intellij.ide.hierarchy.HierarchyBrowserBase;
21 import com.intellij.ide.projectView.impl.AbstractUrl;
22 import com.intellij.openapi.actionSystem.CommonDataKeys;
23 import com.intellij.openapi.actionSystem.DataContext;
24 import com.intellij.openapi.actionSystem.LangDataKeys;
25 import com.intellij.openapi.editor.Editor;
26 import com.intellij.openapi.editor.SelectionModel;
27 import com.intellij.openapi.fileEditor.FileEditorManager;
28 import com.intellij.openapi.module.*;
29 import com.intellij.openapi.project.Project;
30 import com.intellij.openapi.util.Condition;
31 import com.intellij.openapi.util.Pair;
32 import com.intellij.openapi.util.TextRange;
33 import com.intellij.openapi.vfs.VirtualFile;
34 import com.intellij.openapi.wm.ToolWindow;
35 import com.intellij.openapi.wm.ToolWindowId;
36 import com.intellij.openapi.wm.ToolWindowManager;
37 import com.intellij.psi.PsiDocumentManager;
38 import com.intellij.psi.PsiElement;
39 import com.intellij.psi.PsiFile;
40 import com.intellij.psi.PsiWhiteSpace;
41 import com.intellij.psi.util.PsiTreeUtil;
42 import com.intellij.psi.util.PsiUtilCore;
43 import com.intellij.ui.content.Content;
44 import com.intellij.ui.content.ContentManager;
45 import com.intellij.usages.Usage;
46 import com.intellij.usages.UsageView;
47 import com.intellij.usages.UsageViewManager;
48 import com.intellij.usages.rules.PsiElementUsage;
49 import com.intellij.util.PlatformUtils;
50 import com.intellij.util.TreeItem;
51 import com.intellij.util.containers.ContainerUtil;
52 import org.jetbrains.annotations.NotNull;
53 import org.jetbrains.annotations.Nullable;
54 import org.jetbrains.jps.model.java.JavaSourceRootType;
55
56 import javax.swing.*;
57 import java.util.*;
58
59 public class PredefinedSearchScopeProviderImpl extends PredefinedSearchScopeProvider {
60   @NotNull
61   @Override
62   public List<SearchScope> getPredefinedScopes(@NotNull final Project project,
63                                                @Nullable final DataContext dataContext,
64                                                boolean suggestSearchInLibs,
65                                                boolean prevSearchFiles,
66                                                boolean currentSelection,
67                                                boolean usageView) {
68     Collection<SearchScope> result = ContainerUtil.newLinkedHashSet();
69     result.add(GlobalSearchScope.projectScope(project));
70     if (suggestSearchInLibs) {
71       result.add(GlobalSearchScope.allScope(project));
72     }
73
74     if (ModuleUtil.isSupportedRootType(project, JavaSourceRootType.TEST_SOURCE)) {
75       result.add(GlobalSearchScopesCore.projectProductionScope(project));
76       result.add(GlobalSearchScopesCore.projectTestScope(project));
77     }
78
79     result.add(GlobalSearchScopes.openFilesScope(project));
80
81     if (dataContext != null) {
82       PsiElement dataContextElement = CommonDataKeys.PSI_FILE.getData(dataContext);
83       if (dataContextElement == null) {
84         dataContextElement = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
85       }
86
87       if (dataContextElement == null) {
88         final Editor selectedTextEditor = FileEditorManager.getInstance(project).getSelectedTextEditor();
89         if (selectedTextEditor != null) {
90           dataContextElement = PsiDocumentManager.getInstance(project).getPsiFile(selectedTextEditor.getDocument());
91         }
92       }
93
94       if (dataContextElement != null) {
95         if (!PlatformUtils.isCidr()) { // TODO: have an API to disable module scopes.
96           Module module = ModuleUtilCore.findModuleForPsiElement(dataContextElement);
97           if (module == null) {
98             module = LangDataKeys.MODULE.getData(dataContext);
99           }
100           if (module != null && !(ModuleType.get(module) instanceof InternalModuleType)) {
101             result.add(module.getModuleScope());
102           }
103         }
104       }
105       PsiElement[] currentFileScope = PsiElement.EMPTY_ARRAY;
106       if (dataContextElement != null && dataContextElement.getContainingFile() != null) {
107         currentFileScope = new PsiElement[]{dataContextElement};
108       }
109       result.add(new LocalSearchScope(currentFileScope, IdeBundle.message("scope.current.file")));
110     }
111
112     if (currentSelection) {
113       FileEditorManager fileEditorManager = FileEditorManager.getInstance(project);
114       final Editor selectedTextEditor = fileEditorManager.getSelectedTextEditor();
115       if (selectedTextEditor != null) {
116         final PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(selectedTextEditor.getDocument());
117         if (psiFile != null) {
118           SelectionModel selectionModel = selectedTextEditor.getSelectionModel();
119           if (selectionModel.hasSelection()) {
120             int start = selectionModel.getSelectionStart();
121             final PsiElement startElement = psiFile.findElementAt(start);
122             if (startElement != null) {
123               int end = selectionModel.getSelectionEnd();
124               final PsiElement endElement = psiFile.findElementAt(end);
125               if (endElement != null) {
126                 final PsiElement parent = PsiTreeUtil.findCommonParent(startElement, endElement);
127                 if (parent != null) {
128                   final List<PsiElement> elements = new ArrayList<PsiElement>();
129                   final PsiElement[] children = parent.getChildren();
130                   TextRange selection = new TextRange(start, end);
131                   for (PsiElement child : children) {
132                     if (!(child instanceof PsiWhiteSpace) &&
133                         child.getContainingFile() != null &&
134                         selection.contains(child.getTextOffset())) {
135                       elements.add(child);
136                     }
137                   }
138                   if (!elements.isEmpty()) {
139                     SearchScope local = new LocalSearchScope(PsiUtilCore.toPsiElementArray(elements), IdeBundle.message("scope.selection"));
140                     result.add(local);
141                   }
142                 }
143               }
144             }
145           }
146         }
147       }
148     }
149
150     if (usageView) {
151       if (prevSearchFiles) {
152         addHierarchyScope(project, result);
153       }
154       UsageView selectedUsageView = UsageViewManager.getInstance(project).getSelectedUsageView();
155       if (selectedUsageView != null && !selectedUsageView.isSearchInProgress()) {
156         final Set<Usage> usages = selectedUsageView.getUsages();
157         final List<PsiElement> results = new ArrayList<PsiElement>(usages.size());
158
159         if (prevSearchFiles) {
160           final Set<VirtualFile> files = collectFiles(usages, true);
161           if (!files.isEmpty()) {
162             GlobalSearchScope prev = new GlobalSearchScope(project) {
163               private Set<VirtualFile> myFiles = null;
164
165               @NotNull
166               @Override
167               public String getDisplayName() {
168                 return IdeBundle.message("scope.files.in.previous.search.result");
169               }
170
171               @Override
172               public synchronized boolean contains(@NotNull VirtualFile file) {
173                 if (myFiles == null) {
174                   myFiles = collectFiles(usages, false);
175                 }
176                 return myFiles.contains(file);
177               }
178
179               @Override
180               public int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) {
181                 return 0;
182               }
183
184               @Override
185               public boolean isSearchInModuleContent(@NotNull Module aModule) {
186                 return true;
187               }
188
189               @Override
190               public boolean isSearchInLibraries() {
191                 return true;
192               }
193             };
194             result.add(prev);
195           }
196         }
197         else {
198           for (Usage usage : usages) {
199             if (usage instanceof PsiElementUsage) {
200               final PsiElement element = ((PsiElementUsage)usage).getElement();
201               if (element != null && element.isValid() && element.getContainingFile() != null) {
202                 results.add(element);
203               }
204             }
205           }
206
207           if (!results.isEmpty()) {
208             result.add(new LocalSearchScope(PsiUtilCore.toPsiElementArray(results), IdeBundle.message("scope.previous.search.results")));
209           }
210         }
211       }
212     }
213
214     final FavoritesManager favoritesManager = FavoritesManager.getInstance(project);
215     if (favoritesManager != null) {
216       for (final String favorite : favoritesManager.getAvailableFavoritesListNames()) {
217         final Collection<TreeItem<Pair<AbstractUrl, String>>> rootUrls = favoritesManager.getFavoritesListRootUrls(favorite);
218         if (rootUrls.isEmpty()) continue;  // ignore unused root
219         result.add(new GlobalSearchScope(project) {
220           @NotNull
221           @Override
222           public String getDisplayName() {
223             return "Favorite \'" + favorite + "\'";
224           }
225
226           @Override
227           public boolean contains(@NotNull final VirtualFile file) {
228             return favoritesManager.contains(favorite, file);
229           }
230
231           @Override
232           public int compare(@NotNull final VirtualFile file1, @NotNull final VirtualFile file2) {
233             return 0;
234           }
235
236           @Override
237           public boolean isSearchInModuleContent(@NotNull final Module aModule) {
238             return true;
239           }
240
241           @Override
242           public boolean isSearchInLibraries() {
243             return true;
244           }
245         });
246       }
247     }
248
249     ContainerUtil.addIfNotNull(result, getSelectedFilesScope(project, dataContext));
250
251     return ContainerUtil.newArrayList(result);
252   }
253
254   private static void addHierarchyScope(@NotNull Project project, Collection<SearchScope> result) {
255     final ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.HIERARCHY);
256     if (toolWindow == null) {
257       return;
258     }
259     final ContentManager contentManager = toolWindow.getContentManager();
260     final Content content = contentManager.getSelectedContent();
261     if (content == null) {
262       return;
263     }
264     final String name = content.getDisplayName();
265     final JComponent component = content.getComponent();
266     if (!(component instanceof HierarchyBrowserBase)) {
267       return;
268     }
269     final HierarchyBrowserBase hierarchyBrowserBase = (HierarchyBrowserBase)component;
270     final PsiElement[] elements = hierarchyBrowserBase.getAvailableElements();
271     if (elements.length > 0) {
272       result.add(new LocalSearchScope(elements, "Hierarchy '" + name + "' (visible nodes only)"));
273     }
274   }
275
276   @Nullable
277   private static SearchScope getSelectedFilesScope(final Project project, @Nullable DataContext dataContext) {
278     final VirtualFile[] filesOrDirs = dataContext == null ? null : CommonDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext);
279     if (filesOrDirs != null) {
280       final List<VirtualFile> selectedFiles = ContainerUtil.filter(filesOrDirs, new Condition<VirtualFile>() {
281         @Override
282         public boolean value(VirtualFile file) {
283           return !file.isDirectory();
284         }
285       });
286       if (!selectedFiles.isEmpty()) {
287         return GlobalSearchScope.filesScope(project, selectedFiles, "Selected Files");
288       }
289     }
290     return null;
291   }
292
293   protected static Set<VirtualFile> collectFiles(Set<Usage> usages, boolean findFirst) {
294     final Set<VirtualFile> files = new HashSet<VirtualFile>();
295     for (Usage usage : usages) {
296       if (usage instanceof PsiElementUsage) {
297         PsiElement psiElement = ((PsiElementUsage)usage).getElement();
298         if (psiElement != null && psiElement.isValid()) {
299           PsiFile psiFile = psiElement.getContainingFile();
300           if (psiFile != null) {
301             VirtualFile file = psiFile.getVirtualFile();
302             if (file != null) {
303               files.add(file);
304               if (findFirst) return files;
305             }
306           }
307         }
308       }
309     }
310     return files;
311   }
312 }