assertion for IDEADEV-24394
[fedora-idea.git] / lang-impl / src / com / intellij / find / findUsages / FindUsagesManager.java
blobc6a12d3d2ea707e692ac1ebf1778c83c00d92347
1 package com.intellij.find.findUsages;
3 import com.intellij.codeInsight.hint.HintUtil;
4 import com.intellij.codeInsight.hint.HintManagerImpl;
5 import com.intellij.codeInsight.hint.HintManager;
6 import com.intellij.find.FindBundle;
7 import com.intellij.lang.findUsages.LanguageFindUsages;
8 import com.intellij.navigation.NavigationItem;
9 import com.intellij.openapi.actionSystem.ActionManager;
10 import com.intellij.openapi.actionSystem.AnAction;
11 import com.intellij.openapi.actionSystem.IdeActions;
12 import com.intellij.openapi.application.ApplicationManager;
13 import com.intellij.openapi.diagnostic.Logger;
14 import com.intellij.openapi.editor.Document;
15 import com.intellij.openapi.editor.Editor;
16 import com.intellij.openapi.extensions.Extensions;
17 import com.intellij.openapi.fileEditor.FileEditor;
18 import com.intellij.openapi.fileEditor.FileEditorLocation;
19 import com.intellij.openapi.fileEditor.TextEditor;
20 import com.intellij.openapi.keymap.KeymapUtil;
21 import com.intellij.openapi.progress.ProgressIndicator;
22 import com.intellij.openapi.progress.ProgressManager;
23 import com.intellij.openapi.progress.Task;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.openapi.ui.Messages;
26 import com.intellij.openapi.util.*;
27 import com.intellij.openapi.util.text.StringUtil;
28 import com.intellij.openapi.wm.StatusBar;
29 import com.intellij.openapi.wm.WindowManager;
30 import com.intellij.psi.*;
31 import com.intellij.psi.search.LocalSearchScope;
32 import com.intellij.psi.search.SearchScope;
33 import com.intellij.ui.LightweightHint;
34 import com.intellij.ui.content.Content;
35 import com.intellij.usageView.UsageInfo;
36 import com.intellij.usageView.UsageViewManager;
37 import com.intellij.usageView.UsageViewUtil;
38 import com.intellij.usages.*;
39 import com.intellij.usages.impl.UsageViewManagerImpl;
40 import com.intellij.util.Processor;
41 import org.jdom.Element;
42 import org.jetbrains.annotations.NonNls;
43 import org.jetbrains.annotations.NotNull;
44 import org.jetbrains.annotations.Nullable;
46 import javax.swing.*;
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.Collections;
50 import java.util.List;
51 import java.util.concurrent.CopyOnWriteArrayList;
53 public class FindUsagesManager implements JDOMExternalizable {
54 private static final Logger LOG = Logger.getInstance("#com.intellij.find.findParameterUsages.FindUsagesManager");
56 private enum FileSearchScope {
57 FROM_START,
58 FROM_END,
59 AFTER_CARET,
60 BEFORE_CARET
63 private static final Key<String> KEY_START_USAGE_AGAIN = Key.create("KEY_START_USAGE_AGAIN");
64 @NonNls private static final String VALUE_START_USAGE_AGAIN = "START_AGAIN";
65 private final Project myProject;
66 private final com.intellij.usages.UsageViewManager myAnotherManager;
67 private boolean myToOpenInNewTab = false;
68 private final List<FindUsagesHandlerFactory> myHandlers = new ArrayList<FindUsagesHandlerFactory>();
70 public static class SearchData {
71 public SmartPsiElementPointer[] myElements = null;
72 public FindUsagesOptions myOptions = null;
74 public boolean equals(final Object o) {
75 if (this == o) return true;
76 if (o == null || getClass() != o.getClass()) return false;
78 final SearchData that = (SearchData)o;
80 if (!Arrays.equals(myElements, that.myElements)) return false;
81 if (myOptions != null ? !myOptions.equals(that.myOptions) : that.myOptions != null) return false;
83 return true;
86 public int hashCode() {
87 return myElements != null ? Arrays.hashCode(myElements) : 0;
91 private SearchData myLastSearchInFileData = new SearchData();
92 private final CopyOnWriteArrayList<SearchData> myFindUsagesHistory = new CopyOnWriteArrayList<SearchData>();
94 public FindUsagesManager(final Project project, com.intellij.usages.UsageViewManager anotherManager) {
95 myProject = project;
96 myAnotherManager = anotherManager;
99 public void registerFindUsagesHandler(FindUsagesHandlerFactory handler) {
100 myHandlers.add(0, handler);
103 public boolean canFindUsages(@NotNull final PsiElement element) {
104 for (FindUsagesHandlerFactory factory : myHandlers) {
105 if (factory.canFindUsages(element)) {
106 return true;
109 for (FindUsagesHandlerFactory factory : Extensions.getExtensions(FindUsagesHandlerFactory.EP_NAME, myProject)) {
110 if (factory.canFindUsages(element)) {
111 return true;
114 return false;
117 public void clearFindingNextUsageInFile() {
118 myLastSearchInFileData.myOptions = null;
119 myLastSearchInFileData.myElements = null;
122 public boolean findNextUsageInFile(FileEditor editor) {
123 return findUsageInFile(editor, FileSearchScope.AFTER_CARET);
126 public boolean findPreviousUsageInFile(FileEditor editor) {
127 return findUsageInFile(editor, FileSearchScope.BEFORE_CARET);
130 public void readExternal(Element element) throws InvalidDataException {
131 myToOpenInNewTab = JDOMExternalizer.readBoolean(element, "OPEN_NEW_TAB");
134 public void writeExternal(Element element) throws WriteExternalException {
135 JDOMExternalizer.write(element, "OPEN_NEW_TAB", myToOpenInNewTab);
138 private boolean findUsageInFile(@NotNull FileEditor editor, FileSearchScope direction) {
139 PsiElement[] elements = restorePsiElements(myLastSearchInFileData, true);
140 if (elements == null) return false;
141 if (elements.length == 0) return true;//all elements have invalidated
143 UsageInfoToUsageConverter.TargetElementsDescriptor descriptor = new UsageInfoToUsageConverter.TargetElementsDescriptor(elements);
145 //todo
146 TextEditor textEditor = (TextEditor)editor;
147 Document document = textEditor.getEditor().getDocument();
148 PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(document);
149 if (psiFile == null) return false;
151 findUsagesInEditor(descriptor, getFindUsagesHandler(elements[0], false), psiFile, direction, myLastSearchInFileData.myOptions, textEditor);
152 return true;
155 // returns null if cannot find, empty Pair if all elements have been changed
156 @Nullable
157 private PsiElement[] restorePsiElements(SearchData searchData, final boolean showErrorMessage) {
158 if (searchData == null) return null;
159 SmartPsiElementPointer[] lastSearchElements = searchData.myElements;
160 if (lastSearchElements == null) return null;
161 List<PsiElement> elements = new ArrayList<PsiElement>();
162 for (SmartPsiElementPointer pointer : lastSearchElements) {
163 PsiElement element = pointer.getElement();
164 if (element != null) elements.add(element);
166 if (elements.isEmpty() && showErrorMessage) {
167 Messages.showMessageDialog(myProject, FindBundle.message("find.searched.elements.have.been.changed.error"),
168 FindBundle.message("cannot.search.for.usages.title"), Messages.getInformationIcon());
169 // SCR #10022
170 //clearFindingNextUsageInFile();
171 return PsiElement.EMPTY_ARRAY;
174 return elements.toArray(new PsiElement[elements.size()]);
177 private void initLastSearchElement(final FindUsagesOptions findUsagesOptions,
178 UsageInfoToUsageConverter.TargetElementsDescriptor descriptor) {
179 myLastSearchInFileData = createSearchData(descriptor.getAllElements(), findUsagesOptions);
182 private SearchData createSearchData(final List<? extends PsiElement> psiElements, final FindUsagesOptions findUsagesOptions) {
183 SearchData data = new SearchData();
185 data.myElements = new SmartPsiElementPointer[psiElements.size()];
186 int idx = 0;
187 for (PsiElement psiElement : psiElements) {
188 data.myElements[idx++] = SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(psiElement);
190 data.myOptions = findUsagesOptions;
191 return data;
194 @Nullable
195 public FindUsagesHandler getFindUsagesHandler(PsiElement element, final boolean forHighlightUsages) {
196 for (FindUsagesHandlerFactory factory : myHandlers) {
197 if (factory.canFindUsages(element)) {
198 final FindUsagesHandler handler = factory.createFindUsagesHandler(element, forHighlightUsages);
199 if (handler != null) {
200 return handler;
204 for (FindUsagesHandlerFactory factory : Extensions.getExtensions(FindUsagesHandlerFactory.EP_NAME, myProject)) {
205 if (factory.canFindUsages(element)) {
206 final FindUsagesHandler handler = factory.createFindUsagesHandler(element, forHighlightUsages);
207 if (handler != null) {
208 return handler;
212 return null;
215 public void findUsages(@NotNull PsiElement psiElement, final PsiFile scopeFile, final FileEditor editor) {
216 final FindUsagesHandler handler = getFindUsagesHandler(psiElement, false);
217 if (handler == null) return;
219 boolean singleFile = scopeFile != null;
220 final AbstractFindUsagesDialog dialog = handler.getFindUsagesDialog(singleFile, shouldOpenInNewTab(), mustOpenInNewTab());
221 if (!singleFile) {
222 dialog.show();
223 if (!dialog.isOK()) return;
226 setOpenInNewTab(dialog.isShowInSeparateWindow());
228 FindUsagesOptions findUsagesOptions = dialog.calcFindUsagesOptions();
230 clearFindingNextUsageInFile();
231 LOG.assertTrue(handler.getPsiElement().isValid());
232 final UsageInfoToUsageConverter.TargetElementsDescriptor descriptor =
233 new UsageInfoToUsageConverter.TargetElementsDescriptor(handler.getPrimaryElements(), handler.getSecondaryElements());
234 if (singleFile) {
235 findUsagesOptions = (FindUsagesOptions)findUsagesOptions.clone();
236 findUsagesOptions.isDerivedClasses = false;
237 findUsagesOptions.isDerivedInterfaces = false;
238 findUsagesOptions.isImplementingClasses = false;
239 editor.putUserData(KEY_START_USAGE_AGAIN, null);
240 findUsagesInEditor(descriptor, handler, scopeFile, FileSearchScope.FROM_START, findUsagesOptions, editor);
242 else {
243 findUsages(descriptor, handler, dialog.isSkipResultsWhenOneUsage(), dialog.isShowInSeparateWindow(), findUsagesOptions);
247 @Nullable
248 public static SearchScope getCurrentSearchScope(FindUsagesHandler handler) {
249 if (handler == null) return null;
250 FindUsagesOptions findUsagesOptions = handler.getFindUsagesOptions();
251 return findUsagesOptions.searchScope;
254 // return null on failure or cancel
255 @Nullable
256 public UsageViewPresentation processUsages(@NotNull PsiElement element, @NotNull final Processor<Usage> processor, FindUsagesHandler handler) {
257 if (handler == null) return null;
259 FindUsagesOptions findUsagesOptions = handler.getFindUsagesOptions();
261 LOG.assertTrue(handler.getPsiElement().isValid());
262 final UsageInfoToUsageConverter.TargetElementsDescriptor descriptor =
263 new UsageInfoToUsageConverter.TargetElementsDescriptor(handler.getPrimaryElements(), handler.getSecondaryElements());
265 UsageViewPresentation presentation = createPresentation(element, findUsagesOptions, myToOpenInNewTab);
266 final UsageSearcher usageSearcher = createUsageSearcher(descriptor, handler, findUsagesOptions, null);
267 final boolean[] canceled = {false};
268 final int[] usageCount = {0};
269 Task task = new Task.Modal(myProject, UsageViewManagerImpl.getProgressTitle(presentation), true) {
270 public void run(@NotNull final ProgressIndicator indicator) {
271 usageSearcher.generate(new Processor<Usage>() {
272 public boolean process(final Usage usage) {
273 usageCount[0]++;
274 return processor.process(usage);
279 @Nullable
280 public NotificationInfo getNotificationInfo() {
281 return new NotificationInfo("Find Usages", "Find Usages Finished", usageCount[0] + " Usage(s) Found");
284 public void onCancel() {
285 canceled[0] = true;
288 ProgressManager.getInstance().run(task);
289 if (canceled[0]) return null;
290 return presentation;
293 private void setOpenInNewTab(final boolean toOpenInNewTab) {
294 if (!mustOpenInNewTab()) {
295 myToOpenInNewTab = toOpenInNewTab;
299 private boolean shouldOpenInNewTab() {
300 return mustOpenInNewTab() || myToOpenInNewTab;
303 private boolean mustOpenInNewTab() {
304 Content selectedContent = UsageViewManager.getInstance(myProject).getSelectedContent(true);
305 return selectedContent != null && selectedContent.isPinned();
309 private static UsageSearcher createUsageSearcher(final UsageInfoToUsageConverter.TargetElementsDescriptor descriptor,
310 final FindUsagesHandler handler,
311 final FindUsagesOptions options,
312 final PsiFile scopeFile) {
314 return new UsageSearcher() {
315 public void generate(@NotNull final Processor<Usage> processor) {
316 if (scopeFile != null) {
317 options.searchScope = new LocalSearchScope(scopeFile);
319 final Processor<UsageInfo> usageInfoProcessorToUsageProcessorAdapter = new Processor<UsageInfo>() {
320 public boolean process(UsageInfo usageInfo) {
321 return processor.process(UsageInfoToUsageConverter.convert(descriptor, usageInfo));
324 List<? extends PsiElement> elements =
325 ApplicationManager.getApplication().runReadAction(new Computable<List<? extends PsiElement>>() {
326 public List<? extends PsiElement> compute() {
327 return descriptor.getAllElements();
330 for (final PsiElement element : elements) {
331 ApplicationManager.getApplication().runReadAction(new Runnable() {
332 public void run() {
333 LOG.assertTrue(element.isValid());
336 handler.processElementUsages(element, usageInfoProcessorToUsageProcessorAdapter, options);
342 private static PsiElement2UsageTargetAdapter[] convertToUsageTargets(final List<? extends PsiElement> elementsToSearch) {
343 final ArrayList<PsiElement2UsageTargetAdapter> targets = new ArrayList<PsiElement2UsageTargetAdapter>(elementsToSearch.size());
344 for (PsiElement element : elementsToSearch) {
345 convertToUsageTarget(targets, element);
347 return targets.toArray(new PsiElement2UsageTargetAdapter[targets.size()]);
350 private void findUsages(final UsageInfoToUsageConverter.TargetElementsDescriptor descriptor,
351 final FindUsagesHandler handler,
352 final boolean toSkipUsagePanelWhenOneUsage,
353 final boolean toOpenInNewTab,
354 final FindUsagesOptions findUsagesOptions) {
356 List<? extends PsiElement> elements = descriptor.getAllElements();
357 final UsageTarget[] targets = convertToUsageTargets(elements);
358 myAnotherManager.searchAndShowUsages(targets, new Factory<UsageSearcher>() {
359 public UsageSearcher create() {
360 return createUsageSearcher(descriptor, handler, findUsagesOptions, null);
362 }, !toSkipUsagePanelWhenOneUsage, true, createPresentation(elements.get(0), findUsagesOptions, toOpenInNewTab), null);
363 addToHistory(elements, findUsagesOptions);
366 private static UsageViewPresentation createPresentation(PsiElement psiElement,
367 final FindUsagesOptions findUsagesOptions,
368 boolean toOpenInNewTab) {
369 UsageViewPresentation presentation = new UsageViewPresentation();
370 String scopeString = findUsagesOptions.searchScope != null ? findUsagesOptions.searchScope.getDisplayName() : null;
371 presentation.setScopeText(scopeString);
372 String usagesString = generateUsagesString(findUsagesOptions);
373 presentation.setUsagesString(usagesString);
374 String title;
375 if (scopeString != null) {
376 title = FindBundle.message("find.usages.of.element.in.scope.panel.title", usagesString, UsageViewUtil.getLongName(psiElement), scopeString);
378 else {
379 title = FindBundle.message("find.usages.of.element.panel.title", usagesString, UsageViewUtil.getLongName(psiElement));
381 presentation.setTabText(title);
382 presentation.setTabName(FindBundle.message("find.usages.of.element.tab.name", usagesString, UsageViewUtil.getShortName(psiElement)));
383 presentation.setTargetsNodeText(StringUtil.capitalize(UsageViewUtil.getType(psiElement)));
384 presentation.setOpenInNewTab(toOpenInNewTab);
385 return presentation;
388 private void findUsagesInEditor(final UsageInfoToUsageConverter.TargetElementsDescriptor descriptor,
389 final FindUsagesHandler handler,
390 final PsiFile scopeFile,
391 final FileSearchScope direction,
392 final FindUsagesOptions findUsagesOptions,
393 @NotNull FileEditor fileEditor) {
394 initLastSearchElement(findUsagesOptions, descriptor);
396 clearStatusBar();
398 final FileEditorLocation currentLocation = fileEditor.getCurrentLocation();
400 final UsageSearcher usageSearcher = createUsageSearcher(descriptor, handler, findUsagesOptions, scopeFile);
401 final boolean[] usagesWereFound = {false};
403 Usage fUsage = findSiblingUsage(myProject, usageSearcher, direction, currentLocation, usagesWereFound, fileEditor);
405 if (fUsage != null) {
406 fUsage.navigate(true);
407 fUsage.selectInEditor();
409 else if (!usagesWereFound[0]) {
410 String message = getNoUsagesFoundMessage(descriptor.getPrimaryElements()[0]);
411 showHintOrStatusBarMessage(message, fileEditor);
413 else {
414 fileEditor.putUserData(KEY_START_USAGE_AGAIN, VALUE_START_USAGE_AGAIN);
415 showHintOrStatusBarMessage(getSearchAgainMessage(descriptor.getPrimaryElements()[0], direction), fileEditor);
419 private static String getNoUsagesFoundMessage(PsiElement psiElement) {
420 String elementType = UsageViewUtil.getType(psiElement);
421 String elementName = UsageViewUtil.getShortName(psiElement);
422 return FindBundle.message("find.usages.of.element_type.element_name.not.found.message", elementType, elementName);
425 private void clearStatusBar() {
426 StatusBar statusBar = WindowManager.getInstance().getStatusBar(myProject);
427 statusBar.setInfo("");
430 private static String getSearchAgainMessage(PsiElement element, final FileSearchScope direction) {
431 String message = getNoUsagesFoundMessage(element);
432 if (direction == FileSearchScope.AFTER_CARET) {
433 AnAction action = ActionManager.getInstance().getAction(IdeActions.ACTION_FIND_NEXT);
434 String shortcutsText = KeymapUtil.getFirstKeyboardShortcutText(action);
435 if (shortcutsText.length() > 0) {
436 message = FindBundle.message("find.search.again.from.top.hotkey.message", message, shortcutsText);
438 else {
439 message = FindBundle.message("find.search.again.from.top.action.message", message);
442 else {
443 String shortcutsText =
444 KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction(IdeActions.ACTION_FIND_PREVIOUS));
445 if (shortcutsText.length() > 0) {
446 message = FindBundle.message("find.search.again.from.bottom.hotkey.message", message, shortcutsText);
448 else {
449 message = FindBundle.message("find.search.again.from.bottom.action.message", message);
452 return message;
455 private void showHintOrStatusBarMessage(String message, FileEditor fileEditor) {
456 if (fileEditor instanceof TextEditor) {
457 TextEditor textEditor = (TextEditor)fileEditor;
458 showEditorHint(message, textEditor.getEditor());
460 else {
461 StatusBar statusBar = WindowManager.getInstance().getStatusBar(myProject);
462 statusBar.setInfo(message);
466 private static Usage findSiblingUsage(@NotNull final Project project,
467 @NotNull final UsageSearcher usageSearcher,
468 FileSearchScope dir,
469 final FileEditorLocation currentLocation,
470 @NotNull final boolean[] usagesWereFound,
471 @NotNull FileEditor fileEditor) {
472 if (fileEditor.getUserData(KEY_START_USAGE_AGAIN) != null) {
473 dir = dir == FileSearchScope.AFTER_CARET ? FileSearchScope.FROM_START : FileSearchScope.FROM_END;
476 final FileSearchScope direction = dir;
478 final com.intellij.usages.UsageViewManager usageViewManager = com.intellij.usages.UsageViewManager.getInstance(project);
479 usageViewManager.setCurrentSearchCancelled(false);
480 final Usage[] foundUsage = {null};
481 usageSearcher.generate(new Processor<Usage>() {
482 public boolean process(Usage usage) {
483 if (usageViewManager.searchHasBeenCancelled()) return false;
485 usagesWereFound[0] = true;
487 if (direction == FileSearchScope.FROM_START) {
488 foundUsage[0] = usage;
489 return false;
491 else if (direction == FileSearchScope.FROM_END) {
492 foundUsage[0] = usage;
494 else if (direction == FileSearchScope.AFTER_CARET) {
495 if (Comparing.compare(usage.getLocation(), currentLocation) > 0) {
496 foundUsage[0] = usage;
497 return false;
500 else if (direction == FileSearchScope.BEFORE_CARET) {
501 if (Comparing.compare(usage.getLocation(), currentLocation) < 0) {
502 if (foundUsage[0] != null) {
503 if (foundUsage[0].getLocation().compareTo(usage.getLocation()) < 0) {
504 foundUsage[0] = usage;
507 else {
508 foundUsage[0] = usage;
511 else {
512 return false;
516 return true;
520 fileEditor.putUserData(KEY_START_USAGE_AGAIN, null);
522 return foundUsage[0];
525 private static void convertToUsageTarget(final List<PsiElement2UsageTargetAdapter> targets, PsiElement elementToSearch) {
526 if (elementToSearch instanceof NavigationItem) {
527 targets.add(new PsiElement2UsageTargetAdapter(elementToSearch));
529 else {
530 throw new IllegalArgumentException("Wrong usage target:" + elementToSearch);
534 private static String generateUsagesString(final FindUsagesOptions selectedOptions) {
535 String suffix = " " + FindBundle.message("find.usages.panel.title.separator") + " ";
536 ArrayList<String> strings = new ArrayList<String>();
537 if (selectedOptions.isUsages
538 || selectedOptions.isClassesUsages ||
539 selectedOptions.isMethodsUsages ||
540 selectedOptions.isFieldsUsages) {
541 strings.add(FindBundle.message("find.usages.panel.title.usages"));
543 if (selectedOptions.isIncludeOverloadUsages) {
544 strings.add(FindBundle.message("find.usages.panel.title.overloaded.methods.usages"));
546 if (selectedOptions.isDerivedClasses) {
547 strings.add(FindBundle.message("find.usages.panel.title.derived.classes"));
549 if (selectedOptions.isDerivedInterfaces) {
550 strings.add(FindBundle.message("find.usages.panel.title.derived.interfaces"));
552 if (selectedOptions.isImplementingClasses) {
553 strings.add(FindBundle.message("find.usages.panel.title.implementing.classes"));
555 if (selectedOptions.isImplementingMethods) {
556 strings.add(FindBundle.message("find.usages.panel.title.implementing.methods"));
558 if (selectedOptions.isOverridingMethods) {
559 strings.add(FindBundle.message("find.usages.panel.title.overriding.methods"));
561 if (strings.isEmpty()) {
562 strings.add(FindBundle.message("find.usages.panel.title.usages"));
564 String usagesString = "";
565 for (int i = 0; i < strings.size(); i++) {
566 String s = strings.get(i);
567 usagesString += i == strings.size() - 1 ? s : s + suffix;
569 return usagesString;
572 private static void showEditorHint(String message, final Editor editor) {
573 JComponent component = HintUtil.createInformationLabel(message);
574 final LightweightHint hint = new LightweightHint(component);
575 HintManagerImpl.getInstanceImpl().showEditorHint(hint, editor, HintManager.UNDER,
576 HintManager.HIDE_BY_ANY_KEY | HintManager.HIDE_BY_TEXT_CHANGE | HintManager.HIDE_BY_SCROLLING, 0, false);
579 public static String getHelpID(PsiElement element) {
580 return LanguageFindUsages.INSTANCE.forLanguage(element.getLanguage()).getHelpId(element);
583 private void addToHistory(final List<? extends PsiElement> elements, final FindUsagesOptions findUsagesOptions) {
584 SearchData data = createSearchData(elements, findUsagesOptions);
585 myFindUsagesHistory.remove(data);
586 myFindUsagesHistory.add(data);
588 // todo configure history depth limit
589 if (myFindUsagesHistory.size() > 15) {
590 myFindUsagesHistory.remove(0);
594 public void rerunAndRecallFromHistory(SearchData searchData) {
595 myFindUsagesHistory.remove(searchData);
596 PsiElement[] elements = restorePsiElements(searchData, true);
597 if (elements == null || elements.length == 0) return;
598 UsageInfoToUsageConverter.TargetElementsDescriptor descriptor = new UsageInfoToUsageConverter.TargetElementsDescriptor(elements);
599 findUsages(descriptor, getFindUsagesHandler(elements[0], false), false, false, searchData.myOptions);
602 // most recent entry is at the end of the list
603 public List<SearchData> getFindUsageHistory() {
604 removeInvalidElementsFromHistory();
605 return Collections.unmodifiableList(myFindUsagesHistory);
608 private void removeInvalidElementsFromHistory() {
609 for (SearchData data : myFindUsagesHistory) {
610 PsiElement[] elements = restorePsiElements(data, false);
611 if (elements == null || elements.length == 0) myFindUsagesHistory.remove(data);