revert "show "Current File" scope always (WEB-20644)" (following IDEA-CR-9242)
[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         if (dataContextElement.getContainingFile() != null) {
105           result.add(new LocalSearchScope(dataContextElement, IdeBundle.message("scope.current.file")));
106         }
107       }
108     }
109
110     if (currentSelection) {
111       FileEditorManager fileEditorManager = FileEditorManager.getInstance(project);
112       final Editor selectedTextEditor = fileEditorManager.getSelectedTextEditor();
113       if (selectedTextEditor != null) {
114         final PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(selectedTextEditor.getDocument());
115         if (psiFile != null) {
116           SelectionModel selectionModel = selectedTextEditor.getSelectionModel();
117           if (selectionModel.hasSelection()) {
118             int start = selectionModel.getSelectionStart();
119             final PsiElement startElement = psiFile.findElementAt(start);
120             if (startElement != null) {
121               int end = selectionModel.getSelectionEnd();
122               final PsiElement endElement = psiFile.findElementAt(end);
123               if (endElement != null) {
124                 final PsiElement parent = PsiTreeUtil.findCommonParent(startElement, endElement);
125                 if (parent != null) {
126                   final List<PsiElement> elements = new ArrayList<PsiElement>();
127                   final PsiElement[] children = parent.getChildren();
128                   TextRange selection = new TextRange(start, end);
129                   for (PsiElement child : children) {
130                     if (!(child instanceof PsiWhiteSpace) &&
131                         child.getContainingFile() != null &&
132                         selection.contains(child.getTextOffset())) {
133                       elements.add(child);
134                     }
135                   }
136                   if (!elements.isEmpty()) {
137                     SearchScope local = new LocalSearchScope(PsiUtilCore.toPsiElementArray(elements), IdeBundle.message("scope.selection"));
138                     result.add(local);
139                   }
140                 }
141               }
142             }
143           }
144         }
145       }
146     }
147
148     if (usageView) {
149       if (prevSearchFiles) {
150         addHierarchyScope(project, result);
151       }
152       UsageView selectedUsageView = UsageViewManager.getInstance(project).getSelectedUsageView();
153       if (selectedUsageView != null && !selectedUsageView.isSearchInProgress()) {
154         final Set<Usage> usages = selectedUsageView.getUsages();
155         final List<PsiElement> results = new ArrayList<PsiElement>(usages.size());
156
157         if (prevSearchFiles) {
158           final Set<VirtualFile> files = collectFiles(usages, true);
159           if (!files.isEmpty()) {
160             GlobalSearchScope prev = new GlobalSearchScope(project) {
161               private Set<VirtualFile> myFiles = null;
162
163               @NotNull
164               @Override
165               public String getDisplayName() {
166                 return IdeBundle.message("scope.files.in.previous.search.result");
167               }
168
169               @Override
170               public synchronized boolean contains(@NotNull VirtualFile file) {
171                 if (myFiles == null) {
172                   myFiles = collectFiles(usages, false);
173                 }
174                 return myFiles.contains(file);
175               }
176
177               @Override
178               public int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) {
179                 return 0;
180               }
181
182               @Override
183               public boolean isSearchInModuleContent(@NotNull Module aModule) {
184                 return true;
185               }
186
187               @Override
188               public boolean isSearchInLibraries() {
189                 return true;
190               }
191             };
192             result.add(prev);
193           }
194         }
195         else {
196           for (Usage usage : usages) {
197             if (usage instanceof PsiElementUsage) {
198               final PsiElement element = ((PsiElementUsage)usage).getElement();
199               if (element != null && element.isValid() && element.getContainingFile() != null) {
200                 results.add(element);
201               }
202             }
203           }
204
205           if (!results.isEmpty()) {
206             result.add(new LocalSearchScope(PsiUtilCore.toPsiElementArray(results), IdeBundle.message("scope.previous.search.results")));
207           }
208         }
209       }
210     }
211
212     final FavoritesManager favoritesManager = FavoritesManager.getInstance(project);
213     if (favoritesManager != null) {
214       for (final String favorite : favoritesManager.getAvailableFavoritesListNames()) {
215         final Collection<TreeItem<Pair<AbstractUrl, String>>> rootUrls = favoritesManager.getFavoritesListRootUrls(favorite);
216         if (rootUrls.isEmpty()) continue;  // ignore unused root
217         result.add(new GlobalSearchScope(project) {
218           @NotNull
219           @Override
220           public String getDisplayName() {
221             return "Favorite \'" + favorite + "\'";
222           }
223
224           @Override
225           public boolean contains(@NotNull final VirtualFile file) {
226             return favoritesManager.contains(favorite, file);
227           }
228
229           @Override
230           public int compare(@NotNull final VirtualFile file1, @NotNull final VirtualFile file2) {
231             return 0;
232           }
233
234           @Override
235           public boolean isSearchInModuleContent(@NotNull final Module aModule) {
236             return true;
237           }
238
239           @Override
240           public boolean isSearchInLibraries() {
241             return true;
242           }
243         });
244       }
245     }
246
247     ContainerUtil.addIfNotNull(result, getSelectedFilesScope(project, dataContext));
248
249     return ContainerUtil.newArrayList(result);
250   }
251
252   private static void addHierarchyScope(@NotNull Project project, Collection<SearchScope> result) {
253     final ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.HIERARCHY);
254     if (toolWindow == null) {
255       return;
256     }
257     final ContentManager contentManager = toolWindow.getContentManager();
258     final Content content = contentManager.getSelectedContent();
259     if (content == null) {
260       return;
261     }
262     final String name = content.getDisplayName();
263     final JComponent component = content.getComponent();
264     if (!(component instanceof HierarchyBrowserBase)) {
265       return;
266     }
267     final HierarchyBrowserBase hierarchyBrowserBase = (HierarchyBrowserBase)component;
268     final PsiElement[] elements = hierarchyBrowserBase.getAvailableElements();
269     if (elements.length > 0) {
270       result.add(new LocalSearchScope(elements, "Hierarchy '" + name + "' (visible nodes only)"));
271     }
272   }
273
274   @Nullable
275   private static SearchScope getSelectedFilesScope(final Project project, @Nullable DataContext dataContext) {
276     final VirtualFile[] filesOrDirs = dataContext == null ? null : CommonDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext);
277     if (filesOrDirs != null) {
278       final List<VirtualFile> selectedFiles = ContainerUtil.filter(filesOrDirs, new Condition<VirtualFile>() {
279         @Override
280         public boolean value(VirtualFile file) {
281           return !file.isDirectory();
282         }
283       });
284       if (!selectedFiles.isEmpty()) {
285         return GlobalSearchScope.filesScope(project, selectedFiles, "Selected Files");
286       }
287     }
288     return null;
289   }
290
291   protected static Set<VirtualFile> collectFiles(Set<Usage> usages, boolean findFirst) {
292     final Set<VirtualFile> files = new HashSet<VirtualFile>();
293     for (Usage usage : usages) {
294       if (usage instanceof PsiElementUsage) {
295         PsiElement psiElement = ((PsiElementUsage)usage).getElement();
296         if (psiElement != null && psiElement.isValid()) {
297           PsiFile psiFile = psiElement.getContainingFile();
298           if (psiFile != null) {
299             VirtualFile file = psiFile.getVirtualFile();
300             if (file != null) {
301               files.add(file);
302               if (findFirst) return files;
303             }
304           }
305         }
306       }
307     }
308     return files;
309   }
310 }