blob: d3978780e53572d0589de8eea5c6559b2db39066 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.find.findUsages;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.codeInsight.hint.HintManagerImpl;
import com.intellij.codeInsight.hint.HintUtil;
import com.intellij.find.FindBundle;
import com.intellij.find.FindSettings;
import com.intellij.lang.findUsages.LanguageFindUsages;
import com.intellij.navigation.NavigationItem;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorLocation;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.util.ProgressIndicatorBase;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.StatusBar;
import com.intellij.openapi.wm.ex.ProgressIndicatorEx;
import com.intellij.psi.*;
import com.intellij.psi.search.*;
import com.intellij.ui.LightweightHint;
import com.intellij.ui.content.Content;
import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageViewManager;
import com.intellij.usageView.UsageViewUtil;
import com.intellij.usages.*;
import com.intellij.util.CommonProcessors;
import com.intellij.util.Function;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.pico.ConstructorInjectionComponentAdapter;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
/**
* see {@link com.intellij.find.impl.FindManagerImpl#getFindUsagesManager()}
*/
public class FindUsagesManager implements JDOMExternalizable {
private static final Logger LOG = Logger.getInstance("#com.intellij.find.findParameterUsages.FindUsagesManager");
private enum FileSearchScope {
FROM_START,
FROM_END,
AFTER_CARET,
BEFORE_CARET
}
private static final Key<String> KEY_START_USAGE_AGAIN = Key.create("KEY_START_USAGE_AGAIN");
@NonNls private static final String VALUE_START_USAGE_AGAIN = "START_AGAIN";
private final Project myProject;
private final com.intellij.usages.UsageViewManager myAnotherManager;
private boolean myToOpenInNewTab = true;
private PsiElement2UsageTargetComposite myLastSearchInFileData; // EDT only
private final UsageHistory myHistory = new UsageHistory();
public FindUsagesManager(@NotNull Project project, @NotNull com.intellij.usages.UsageViewManager anotherManager) {
myProject = project;
myAnotherManager = anotherManager;
}
public boolean canFindUsages(@NotNull final PsiElement element) {
for (FindUsagesHandlerFactory factory : Extensions.getExtensions(FindUsagesHandlerFactory.EP_NAME, myProject)) {
try {
if (factory.canFindUsages(element)) {
return true;
}
}
catch (IndexNotReadyException e) {
throw e;
}
catch (Exception e) {
LOG.error(e);
}
}
return false;
}
public void clearFindingNextUsageInFile() {
ApplicationManager.getApplication().assertIsDispatchThread();
myLastSearchInFileData = null;
}
public boolean findNextUsageInFile(@NotNull FileEditor editor) {
return findUsageInFile(editor, FileSearchScope.AFTER_CARET);
}
public boolean findPreviousUsageInFile(@NotNull FileEditor editor) {
return findUsageInFile(editor, FileSearchScope.BEFORE_CARET);
}
@Override
public void readExternal(Element element) throws InvalidDataException {
myToOpenInNewTab = JDOMExternalizer.readBoolean(element, "OPEN_NEW_TAB");
}
@Override
public void writeExternal(Element element) throws WriteExternalException {
JDOMExternalizer.write(element, "OPEN_NEW_TAB", myToOpenInNewTab);
}
private boolean findUsageInFile(@NotNull FileEditor editor, @NotNull FileSearchScope direction) {
ApplicationManager.getApplication().assertIsDispatchThread();
if (myLastSearchInFileData == null) return false;
PsiElement[] primaryElements = myLastSearchInFileData.getPrimaryElements();
PsiElement[] secondaryElements = myLastSearchInFileData.getSecondaryElements();
if (primaryElements.length == 0) {//all elements have been invalidated
Messages.showMessageDialog(myProject, FindBundle.message("find.searched.elements.have.been.changed.error"),
FindBundle.message("cannot.search.for.usages.title"), Messages.getInformationIcon());
// SCR #10022
//clearFindingNextUsageInFile();
return false;
}
//todo
TextEditor textEditor = (TextEditor)editor;
Document document = textEditor.getEditor().getDocument();
PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(document);
if (psiFile == null) return false;
final FindUsagesHandler handler = getFindUsagesHandler(primaryElements[0], false);
if (handler == null) return false;
findUsagesInEditor(primaryElements, secondaryElements, handler, psiFile, direction, myLastSearchInFileData.myOptions, textEditor);
return true;
}
private void initLastSearchElement(@NotNull FindUsagesOptions findUsagesOptions,
@NotNull PsiElement[] primaryElements,
@NotNull PsiElement[] secondaryElements) {
ApplicationManager.getApplication().assertIsDispatchThread();
myLastSearchInFileData = new PsiElement2UsageTargetComposite(primaryElements, secondaryElements, findUsagesOptions);
}
@Nullable
public FindUsagesHandler getFindUsagesHandler(@NotNull PsiElement element, final boolean forHighlightUsages) {
for (FindUsagesHandlerFactory factory : Extensions.getExtensions(FindUsagesHandlerFactory.EP_NAME, myProject)) {
if (factory.canFindUsages(element)) {
final FindUsagesHandler handler = factory.createFindUsagesHandler(element, forHighlightUsages);
if (handler == FindUsagesHandler.NULL_HANDLER) return null;
if (handler != null) {
return handler;
}
}
}
return null;
}
@Nullable
public FindUsagesHandler getNewFindUsagesHandler(@NotNull PsiElement element, final boolean forHighlightUsages) {
for (FindUsagesHandlerFactory factory : Extensions.getExtensions(FindUsagesHandlerFactory.EP_NAME, myProject)) {
if (factory.canFindUsages(element)) {
Class<? extends FindUsagesHandlerFactory> aClass = factory.getClass();
FindUsagesHandlerFactory copy = (FindUsagesHandlerFactory)new ConstructorInjectionComponentAdapter(aClass.getName(), aClass)
.getComponentInstance(myProject.getPicoContainer());
final FindUsagesHandler handler = copy.createFindUsagesHandler(element, forHighlightUsages);
if (handler == FindUsagesHandler.NULL_HANDLER) return null;
if (handler != null) {
return handler;
}
}
}
return null;
}
public void findUsages(@NotNull PsiElement psiElement, final PsiFile scopeFile, final FileEditor editor, boolean showDialog, @Nullable("null means default (stored in options)") SearchScope searchScope) {
FindUsagesHandler handler = getFindUsagesHandler(psiElement, false);
if (handler == null) return;
boolean singleFile = scopeFile != null;
AbstractFindUsagesDialog dialog = handler.getFindUsagesDialog(singleFile, shouldOpenInNewTab(), mustOpenInNewTab());
if (showDialog) {
dialog.show();
if (!dialog.isOK()) return;
}
else {
dialog.close(DialogWrapper.OK_EXIT_CODE);
}
setOpenInNewTab(dialog.isShowInSeparateWindow());
FindUsagesOptions findUsagesOptions = dialog.calcFindUsagesOptions();
if (searchScope != null) {
findUsagesOptions.searchScope = searchScope;
}
clearFindingNextUsageInFile();
startFindUsages(findUsagesOptions, handler, scopeFile, editor);
}
void startFindUsages(@NotNull PsiElement psiElement,
@NotNull FindUsagesOptions findUsagesOptions,
PsiFile scopeFile,
FileEditor editor) {
FindUsagesHandler handler = getFindUsagesHandler(psiElement, false);
if (handler == null) return;
startFindUsages(findUsagesOptions, handler, scopeFile, editor);
}
private void startFindUsages(@NotNull FindUsagesOptions findUsagesOptions,
@NotNull FindUsagesHandler handler,
PsiFile scopeFile,
FileEditor editor) {
boolean singleFile = scopeFile != null;
clearFindingNextUsageInFile();
LOG.assertTrue(handler.getPsiElement().isValid());
PsiElement[] primaryElements = handler.getPrimaryElements();
checkNotNull(primaryElements, handler, "getPrimaryElements()");
PsiElement[] secondaryElements = handler.getSecondaryElements();
checkNotNull(secondaryElements, handler, "getSecondaryElements()");
if (singleFile) {
editor.putUserData(KEY_START_USAGE_AGAIN, null);
findUsagesInEditor(primaryElements, secondaryElements, handler, scopeFile, FileSearchScope.FROM_START, findUsagesOptions.clone(), editor);
}
else {
boolean skipResultsWithOneUsage = FindSettings.getInstance().isSkipResultsWithOneUsage();
findUsages(primaryElements, secondaryElements, handler, findUsagesOptions, skipResultsWithOneUsage);
}
}
public static void showSettingsAndFindUsages(@NotNull NavigationItem[] targets) {
if (targets.length == 0) return;
NavigationItem target = targets[0];
if (!(target instanceof ConfigurableUsageTarget)) return;
((ConfigurableUsageTarget)target).showSettings();
}
private static void checkNotNull(@NotNull PsiElement[] elements,
@NotNull FindUsagesHandler handler,
@NonNls @NotNull String methodName) {
for (PsiElement element : elements) {
if (element == null) {
LOG.error(handler + "." + methodName + " has returned array with null elements: " + Arrays.asList(elements));
}
}
}
@NotNull
public static ProgressIndicator startProcessUsages(@NotNull final FindUsagesHandler handler,
@NotNull final PsiElement[] primaryElements,
@NotNull final PsiElement[] secondaryElements,
@NotNull final Processor<Usage> processor,
@NotNull final FindUsagesOptions findUsagesOptions,
@NotNull final Runnable onComplete) {
final ProgressIndicatorBase indicator = new ProgressIndicatorBase();
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
try {
ProgressManager.getInstance().runProcess(new Runnable() {
@Override
public void run() {
final UsageSearcher usageSearcher = createUsageSearcher(primaryElements, secondaryElements, handler, findUsagesOptions, null);
usageSearcher.generate(processor);
}
}, indicator);
}
finally {
onComplete.run();
}
}
});
return indicator;
}
@NotNull
public UsageViewPresentation createPresentation(@NotNull FindUsagesHandler handler, @NotNull FindUsagesOptions findUsagesOptions) {
PsiElement element = handler.getPsiElement();
LOG.assertTrue(element.isValid());
return createPresentation(element, findUsagesOptions, myToOpenInNewTab);
}
private void setOpenInNewTab(final boolean toOpenInNewTab) {
if (!mustOpenInNewTab()) {
myToOpenInNewTab = toOpenInNewTab;
}
}
private boolean shouldOpenInNewTab() {
return mustOpenInNewTab() || myToOpenInNewTab;
}
private boolean mustOpenInNewTab() {
Content selectedContent = UsageViewManager.getInstance(myProject).getSelectedContent(true);
return selectedContent != null && selectedContent.isPinned();
}
@NotNull
private static UsageSearcher createUsageSearcher(@NotNull final PsiElement[] primaryElements,
@NotNull final PsiElement[] secondaryElements,
@NotNull final FindUsagesHandler handler,
@NotNull FindUsagesOptions options,
final PsiFile scopeFile) {
final FindUsagesOptions optionsClone = options.clone();
return new UsageSearcher() {
@Override
public void generate(@NotNull final Processor<Usage> processor) {
Project project = ApplicationManager.getApplication().runReadAction(new Computable<Project>() {
@Override
public Project compute() {
return scopeFile != null ? scopeFile.getProject() : primaryElements[0].getProject();
}
});
dropResolveCacheRegularly(ProgressManager.getInstance().getProgressIndicator(), project);
if (scopeFile != null) {
optionsClone.searchScope = new LocalSearchScope(scopeFile);
}
final Processor<UsageInfo> usageInfoProcessor = new CommonProcessors.UniqueProcessor<UsageInfo>(new Processor<UsageInfo>() {
@Override
public boolean process(final UsageInfo usageInfo) {
Usage usage = ApplicationManager.getApplication().runReadAction(new Computable<Usage>() {
@Override
public Usage compute() {
return UsageInfoToUsageConverter.convert(primaryElements, usageInfo);
}
});
return processor.process(usage);
}
});
final Iterable<PsiElement> elements = ContainerUtil.concat(primaryElements, secondaryElements);
optionsClone.fastTrack = new SearchRequestCollector(new SearchSession());
if (optionsClone.searchScope instanceof GlobalSearchScope) {
// we will search in project scope always but warn if some usage is out of scope
optionsClone.searchScope = optionsClone.searchScope.union(GlobalSearchScope.projectScope(project));
}
try {
for (final PsiElement element : elements) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
LOG.assertTrue(element.isValid());
}
});
handler.processElementUsages(element, usageInfoProcessor, optionsClone);
for (CustomUsageSearcher searcher : Extensions.getExtensions(CustomUsageSearcher.EP_NAME)) {
try {
searcher.processElementUsages(element, processor, optionsClone);
}
catch (IndexNotReadyException e) {
DumbService.getInstance(element.getProject()).showDumbModeNotification("Find usages is not available during indexing");
}
catch (Exception e) {
LOG.error(e);
}
}
}
PsiSearchHelper.SERVICE.getInstance(project)
.processRequests(optionsClone.fastTrack, new Processor<PsiReference>() {
@Override
public boolean process(final PsiReference ref) {
UsageInfo info = ApplicationManager.getApplication().runReadAction(new Computable<UsageInfo>() {
@Override
public UsageInfo compute() {
if (!ref.getElement().isValid()) return null;
return new UsageInfo(ref);
}
});
return info == null || usageInfoProcessor.process(info);
}
});
}
finally {
optionsClone.fastTrack = null;
}
}
};
}
@NotNull
private static PsiElement2UsageTargetAdapter[] convertToUsageTargets(@NotNull Iterable<PsiElement> elementsToSearch,
@NotNull final FindUsagesOptions findUsagesOptions) {
final List<PsiElement2UsageTargetAdapter> targets = ContainerUtil.map(elementsToSearch,
new Function<PsiElement, PsiElement2UsageTargetAdapter>() {
@Override
public PsiElement2UsageTargetAdapter fun(PsiElement element) {
return convertToUsageTarget(element, findUsagesOptions);
}
});
return targets.toArray(new PsiElement2UsageTargetAdapter[targets.size()]);
}
public void findUsages(@NotNull final PsiElement[] primaryElements,
@NotNull final PsiElement[] secondaryElements,
@NotNull final FindUsagesHandler handler,
@NotNull final FindUsagesOptions findUsagesOptions,
final boolean toSkipUsagePanelWhenOneUsage) {
if (primaryElements.length == 0) {
throw new AssertionError(handler + " " + findUsagesOptions);
}
Iterable<PsiElement> allElements = ContainerUtil.concat(primaryElements, secondaryElements);
final PsiElement2UsageTargetAdapter[] targets = convertToUsageTargets(allElements, findUsagesOptions);
myAnotherManager.searchAndShowUsages(targets, new Factory<UsageSearcher>() {
@Override
public UsageSearcher create() {
return createUsageSearcher(primaryElements, secondaryElements, handler, findUsagesOptions, null);
}
}, !toSkipUsagePanelWhenOneUsage, true, createPresentation(primaryElements[0], findUsagesOptions, shouldOpenInNewTab()), null);
myHistory.add(targets[0]);
}
private static void dropResolveCacheRegularly(ProgressIndicator indicator, @NotNull final Project project) {
if (indicator instanceof ProgressIndicatorEx) {
((ProgressIndicatorEx)indicator).addStateDelegate(new ProgressIndicatorBase() {
volatile long lastCleared = System.currentTimeMillis();
@Override
public void setFraction(double fraction) {
super.setFraction(fraction);
long current = System.currentTimeMillis();
if (current - lastCleared >= 500) {
lastCleared = current;
// fraction is changed when each file is processed =>
// resolve caches used when searching in that file are likely to be not needed anymore
PsiManager.getInstance(project).dropResolveCaches();
}
}
});
}
}
@NotNull
private static UsageViewPresentation createPresentation(@NotNull PsiElement psiElement,
@NotNull FindUsagesOptions options,
boolean toOpenInNewTab) {
UsageViewPresentation presentation = new UsageViewPresentation();
String scopeString = options.searchScope.getDisplayName();
presentation.setScopeText(scopeString);
String usagesString = generateUsagesString(options);
presentation.setUsagesString(usagesString);
String title = FindBundle.message("find.usages.of.element.in.scope.panel.title", usagesString, UsageViewUtil.getLongName(psiElement),
scopeString);
presentation.setTabText(title);
presentation.setTabName(FindBundle.message("find.usages.of.element.tab.name", usagesString, UsageViewUtil.getShortName(psiElement)));
presentation.setTargetsNodeText(StringUtil.capitalize(UsageViewUtil.getType(psiElement)));
presentation.setOpenInNewTab(toOpenInNewTab);
return presentation;
}
private void findUsagesInEditor(@NotNull final PsiElement[] primaryElements,
@NotNull final PsiElement[] secondaryElements,
@NotNull FindUsagesHandler handler,
@NotNull PsiFile scopeFile,
@NotNull FileSearchScope direction,
@NotNull final FindUsagesOptions findUsagesOptions,
@NotNull FileEditor fileEditor) {
initLastSearchElement(findUsagesOptions, primaryElements, secondaryElements);
clearStatusBar();
final FileEditorLocation currentLocation = fileEditor.getCurrentLocation();
final UsageSearcher usageSearcher = createUsageSearcher(primaryElements, secondaryElements, handler, findUsagesOptions, scopeFile);
AtomicBoolean usagesWereFound = new AtomicBoolean();
Usage fUsage = findSiblingUsage(usageSearcher, direction, currentLocation, usagesWereFound, fileEditor);
if (fUsage != null) {
fUsage.navigate(true);
fUsage.selectInEditor();
}
else if (!usagesWereFound.get()) {
String message = getNoUsagesFoundMessage(primaryElements[0]) + " in " + scopeFile.getName();
showHintOrStatusBarMessage(message, fileEditor);
}
else {
fileEditor.putUserData(KEY_START_USAGE_AGAIN, VALUE_START_USAGE_AGAIN);
showHintOrStatusBarMessage(getSearchAgainMessage(primaryElements[0], direction), fileEditor);
}
}
private static String getNoUsagesFoundMessage(PsiElement psiElement) {
String elementType = UsageViewUtil.getType(psiElement);
String elementName = UsageViewUtil.getShortName(psiElement);
return FindBundle.message("find.usages.of.element_type.element_name.not.found.message", elementType, elementName);
}
private void clearStatusBar() {
StatusBar.Info.set("", myProject);
}
private static String getSearchAgainMessage(PsiElement element, final FileSearchScope direction) {
String message = getNoUsagesFoundMessage(element);
if (direction == FileSearchScope.AFTER_CARET) {
AnAction action = ActionManager.getInstance().getAction(IdeActions.ACTION_FIND_NEXT);
String shortcutsText = KeymapUtil.getFirstKeyboardShortcutText(action);
if (shortcutsText.isEmpty()) {
message = FindBundle.message("find.search.again.from.top.action.message", message);
}
else {
message = FindBundle.message("find.search.again.from.top.hotkey.message", message, shortcutsText);
}
}
else {
String shortcutsText =
KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction(IdeActions.ACTION_FIND_PREVIOUS));
if (shortcutsText.isEmpty()) {
message = FindBundle.message("find.search.again.from.bottom.action.message", message);
}
else {
message = FindBundle.message("find.search.again.from.bottom.hotkey.message", message, shortcutsText);
}
}
return message;
}
private void showHintOrStatusBarMessage(String message, FileEditor fileEditor) {
if (fileEditor instanceof TextEditor) {
TextEditor textEditor = (TextEditor)fileEditor;
showEditorHint(message, textEditor.getEditor());
}
else {
StatusBar.Info.set(message, myProject);
}
}
private static Usage findSiblingUsage(@NotNull final UsageSearcher usageSearcher,
@NotNull FileSearchScope dir,
final FileEditorLocation currentLocation,
@NotNull final AtomicBoolean usagesWereFound,
@NotNull FileEditor fileEditor) {
if (fileEditor.getUserData(KEY_START_USAGE_AGAIN) != null) {
dir = dir == FileSearchScope.AFTER_CARET ? FileSearchScope.FROM_START : FileSearchScope.FROM_END;
}
final FileSearchScope direction = dir;
final AtomicReference<Usage> foundUsage = new AtomicReference<Usage>();
usageSearcher.generate(new Processor<Usage>() {
@Override
public boolean process(Usage usage) {
usagesWereFound.set(true);
if (direction == FileSearchScope.FROM_START) {
foundUsage.compareAndSet(null, usage);
return false;
}
if (direction == FileSearchScope.FROM_END) {
foundUsage.set(usage);
}
else if (direction == FileSearchScope.AFTER_CARET) {
if (Comparing.compare(usage.getLocation(), currentLocation) > 0) {
foundUsage.set(usage);
return false;
}
}
else if (direction == FileSearchScope.BEFORE_CARET) {
if (Comparing.compare(usage.getLocation(), currentLocation) >= 0) {
return false;
}
while (true) {
Usage found = foundUsage.get();
if (found == null) {
if (foundUsage.compareAndSet(null, usage)) break;
}
else {
if (Comparing.compare(found.getLocation(), usage.getLocation()) < 0 && foundUsage.compareAndSet(found, usage)) break;
}
}
}
return true;
}
});
fileEditor.putUserData(KEY_START_USAGE_AGAIN, null);
return foundUsage.get();
}
private static PsiElement2UsageTargetAdapter convertToUsageTarget(@NotNull PsiElement elementToSearch,
@NotNull FindUsagesOptions findUsagesOptions) {
if (elementToSearch instanceof NavigationItem) {
return new PsiElement2UsageTargetAdapter(elementToSearch,findUsagesOptions);
}
throw new IllegalArgumentException("Wrong usage target:" + elementToSearch + "; " + elementToSearch.getClass());
}
@NotNull
private static String generateUsagesString(@NotNull FindUsagesOptions selectedOptions) {
return selectedOptions.generateUsagesString();
}
private static void showEditorHint(String message, final Editor editor) {
JComponent component = HintUtil.createInformationLabel(message);
final LightweightHint hint = new LightweightHint(component);
HintManagerImpl.getInstanceImpl().showEditorHint(hint, editor, HintManager.UNDER,
HintManager.HIDE_BY_ANY_KEY |
HintManager.HIDE_BY_TEXT_CHANGE |
HintManager.HIDE_BY_SCROLLING, 0, false);
}
public static String getHelpID(PsiElement element) {
return LanguageFindUsages.INSTANCE.forLanguage(element.getLanguage()).getHelpId(element);
}
public void rerunAndRecallFromHistory(@NotNull ConfigurableUsageTarget usageTarget) {
usageTarget.findUsages();
addToHistory(usageTarget);
}
public void addToHistory(@NotNull ConfigurableUsageTarget usageTarget) {
myHistory.add(usageTarget);
}
@NotNull
public UsageHistory getHistory() {
return myHistory;
}
@NotNull
public static GlobalSearchScope getMaximalScope(@NotNull FindUsagesHandler handler) {
PsiElement element = handler.getPsiElement();
Project project = element.getProject();
PsiFile file = element.getContainingFile();
if (file != null && ProjectFileIndex.SERVICE.getInstance(project).isInContent(file.getViewProvider().getVirtualFile())) {
return GlobalSearchScope.projectScope(project);
}
return GlobalSearchScope.allScope(project);
}
}